pckr/src/pak.c

329 lines
6.7 KiB
C

#include "pak.h"
#include "aliases.h"
#include "darr.h"
#include "io.h"
#include <linux/limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PAK_MAGIC 0x4B41502054535341
#define PAK_MAJOR 1
#define PAK_MINOR 0
#define PAK_PATCH 0
#define PAK_EXT ".pak"
struct asset_pack {
pak_t header;
FILE *fp;
u8 *toc;
toc_entry_t **toc_entries;
u64 assets_length;
u64 assets_offset;
};
toc_entry_t *create_toc_entry(const char *name, u64 offset);
void write_toc_entry(toc_entry_t *entry, FILE *fp);
void write_toc_entries(const toc_t *toc, toc_entry_t **entries, FILE *fp);
pak_entry_t *create_pak_entry(u64 data_length);
pak_entry_t *create_pak_entry_from_file(FILE *fp);
void write_pak_entry(const pak_entry_t *entry, FILE *fp);
void write_pak(const pak_t *pak, toc_entry_t **toc_entries,
pak_entry_t **pak_entries, FILE *fp);
bool create_asset_pack(const char *dirpath, const char *output) {
bool created = false;
char dst[PATH_MAX + 1] = {0};
strcpy(dst, output);
strcat(dst, PAK_EXT);
FILE *fp = fopen(dst, "wb");
if (!fp) {
goto RETURN_CREATE_ASSET_PAK;
}
darr_t *toc_entries_darr = NULL;
darr_t *pak_entries_darr = NULL;
bool initialiased =
darr_init(&toc_entries_darr) && darr_init(&pak_entries_darr);
if (!initialiased) {
goto FREE_ARRAYS_CREATE_ASSET_PAK;
}
pak_t pak = {
.magic = PAK_MAGIC,
.version =
(pak_ver_t){
.major = PAK_MAJOR,
.minor = PAK_MINOR,
.patch = PAK_PATCH,
},
.toc =
(toc_t){
.count = 0,
.size = 0,
},
};
dirwalk_t *dw = NULL;
dirwalk_result_t result = {0};
result = walk_dir(&dw, dirpath);
if (!(result.fp)) {
goto FREE_ARRAYS_CREATE_ASSET_PAK;
}
u64 offset = 0;
while (result.reading) {
if (result.fp) {
++(pak.toc.count);
toc_entry_t *toc_entry = create_toc_entry(result.name, offset);
pak_entry_t *pak_entry = create_pak_entry_from_file(result.fp);
if (!toc_entry || !pak_entry) {
goto FREE_ARRAYS_CREATE_ASSET_PAK;
}
pak.toc.size += sizeof(toc_entry_t) + toc_entry->length;
offset += sizeof(pak_entry_t) + pak_entry->size;
darr_add(&toc_entries_darr, (void *)toc_entry);
darr_add(&pak_entries_darr, (void *)pak_entry);
}
result = walk_dir(&dw, NULL);
}
toc_entry_t **toc_entries = (toc_entry_t **)darr_get_items(toc_entries_darr);
pak_entry_t **pak_entries = (pak_entry_t **)darr_get_items(pak_entries_darr);
write_pak(&pak, toc_entries, pak_entries, fp);
created = true;
FREE_ARRAYS_CREATE_ASSET_PAK:
darr_free(&pak_entries_darr);
darr_free(&toc_entries_darr);
fclose(fp);
RETURN_CREATE_ASSET_PAK:
return created;
}
asset_pack_t *load_asset_pack(const char *filepath) {
char full_path[PATH_MAX] = {0};
realpath(filepath, full_path);
asset_pack_t *pack = (asset_pack_t *)malloc(sizeof(asset_pack_t));
if (!pack) {
return NULL;
}
pack->fp = fopen(full_path, "rb");
if (!(pack->fp)) {
close_asset_pack(&pack);
return NULL;
}
u64 file_size = get_file_length(pack->fp);
fread((void *)(&(pack->header)), sizeof(pak_t), 1, pack->fp);
u64 toc_size = pack->header.toc.size;
pack->toc = (u8 *)malloc(toc_size);
if (!(pack->toc)) {
close_asset_pack(&pack);
return NULL;
}
fread((void *)(pack->toc), toc_size, 1, pack->fp);
u64 entries_size = sizeof(toc_entry_t *) * pack->header.toc.count;
pack->toc_entries = (toc_entry_t **)malloc(entries_size);
if (!(pack->toc_entries)) {
close_asset_pack(&pack);
return NULL;
}
toc_entry_t *next = (toc_entry_t *)pack->toc;
for (u64 i = 0; i < pack->header.toc.count; ++i) {
pack->toc_entries[i] = next;
next = (toc_entry_t *)((u8 *)next + sizeof(toc_entry_t) + next->length);
}
pack->assets_offset = ftell(pack->fp);
pack->assets_length = file_size - pack->assets_offset;
return pack;
}
buf_t *read_file_from_pack(asset_pack_t *pack, const char *filename) {
toc_entry_t *entry = NULL;
buf_t *output = NULL;
for (u64 i = 0; i < pack->header.toc.count; ++i) {
entry = pack->toc_entries[i];
if (STRNEQ(filename, entry->name, entry->length)) {
fseek(pack->fp, pack->assets_offset + entry->offset, SEEK_SET);
u64 size = 0;
fread(&size, sizeof(u64), 1, pack->fp);
output = (buf_t *)malloc(sizeof(buf_t) + size);
if (!output) {
break;
}
output->size = size;
fread(&(output->data), size, 1, pack->fp);
break;
}
}
return output;
}
void close_pack_file(buf_t **buf) {
buf_t *b = *buf;
if (!b || b->size == 0) {
return;
}
free(*buf);
*buf = NULL;
}
void close_asset_pack(asset_pack_t **pack) {
if (!(*pack)) {
return;
}
if ((*pack)->toc_entries) {
free((*pack)->toc_entries);
}
if ((*pack)->toc) {
free((*pack)->toc);
}
if ((*pack)->fp) {
fclose((*pack)->fp);
}
free(*pack);
*pack = NULL;
}
toc_entry_t *create_toc_entry(const char *name, u64 offset) {
u64 length = strlen(name);
u64 size = sizeof(toc_entry_t) + length + 1;
toc_entry_t *entry = (toc_entry_t *)malloc(size);
if (!entry) {
return NULL;
}
memset((void *)entry, 0, size);
entry->offset = offset;
entry->length = length + 1;
strcpy(entry->name, name);
return entry;
}
void write_toc_entry(toc_entry_t *entry, FILE *fp) {
fwrite((void *)entry, sizeof(toc_entry_t) + entry->length, 1, fp);
}
void write_toc_entries(const toc_t *toc, toc_entry_t **entries, FILE *fp) {
if (!fp || !entries) {
return;
}
for (u64 i = 0; i < toc->count; ++i) {
write_toc_entry(entries[i], fp);
if (!entries[i]) {
continue;
}
free(entries[i]);
entries[i] = NULL;
}
}
pak_entry_t *create_pak_entry(u64 data_length) {
pak_entry_t *entry = (pak_entry_t *)malloc(sizeof(pak_entry_t) + data_length);
if (!entry) {
return NULL;
}
memset(entry, 0, data_length);
entry->size = data_length;
return entry;
}
pak_entry_t *create_pak_entry_from_file(FILE *fp) {
pak_entry_t *entry = NULL;
if (!fp) {
goto RETURN_PAK_ENTRY;
}
u64 length = get_file_length(fp) + 1;
entry = create_pak_entry(length);
if (!entry) {
goto RETURN_PAK_ENTRY;
}
read_entire_file((void *)(entry->data), fp);
RETURN_PAK_ENTRY:
return entry;
}
void write_pak_entry(const pak_entry_t *entry, FILE *fp) {
fwrite((void *)entry, sizeof(pak_entry_t) + entry->size, 1, fp);
}
void write_pak(const pak_t *pak, toc_entry_t **toc_entries,
pak_entry_t **pak_entries, FILE *fp) {
fwrite(pak, sizeof(pak_t), 1, fp);
write_toc_entries(&(pak->toc), toc_entries, fp);
for (u64 i = 0; i < pak->toc.count; ++i) {
write_pak_entry(pak_entries[i], fp);
free(pak_entries[i]);
}
}