#include "pak.h" #include "aliases.h" #include "darr.h" #include "io.h" #include #include #include #include #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]); } }