From 32919ca13d740fc36464160593544bf6490e4349 Mon Sep 17 00:00:00 2001 From: NishiOwO Date: Mon, 12 Jan 2026 11:48:26 +0900 Subject: [PATCH] update pak format --- engine/include/GearSrc/CRC.h | 17 +++ engine/include/GearSrc/File.h | 1 + engine/include/GearSrc/Foundation.h | 1 + engine/include/GearSrc/Log.h | 3 +- engine/src/crc.c | 25 ++++ engine/src/file.c | 8 +- engine/src/log.c | 4 + engine/src/resource.c | 162 ++++++++++++++++++++----- engine/tools/gs_makepak/main.c | 176 +++++++++++++++++++++------- 9 files changed, 324 insertions(+), 73 deletions(-) create mode 100644 engine/include/GearSrc/CRC.h create mode 100644 engine/src/crc.c diff --git a/engine/include/GearSrc/CRC.h b/engine/include/GearSrc/CRC.h new file mode 100644 index 0000000..af00f16 --- /dev/null +++ b/engine/include/GearSrc/CRC.h @@ -0,0 +1,17 @@ +#ifndef __GEARSRC_CRC_H__ +#define __GEARSRC_CRC_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +GSDECL GSU32 GSCRC32(void* input, unsigned int len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/engine/include/GearSrc/File.h b/engine/include/GearSrc/File.h index f02fa02..189c755 100644 --- a/engine/include/GearSrc/File.h +++ b/engine/include/GearSrc/File.h @@ -12,6 +12,7 @@ extern "C" { GSDECL GSFile GSFileOpen(GSEngine engine, const char* path); GSDECL int GSFileRead(GSFile file, void* out, int size); GSDECL void GSFileSeek(GSFile file, int pos); +GSDECL void GSFileSeekFromCurrent(GSFile file, int pos); GSDECL unsigned int GSFileSize(GSFile file); GSDECL void GSFileClose(GSFile file); diff --git a/engine/include/GearSrc/Foundation.h b/engine/include/GearSrc/Foundation.h index 1f690b4..94d232e 100644 --- a/engine/include/GearSrc/Foundation.h +++ b/engine/include/GearSrc/Foundation.h @@ -15,5 +15,6 @@ #include #include #include +#include #endif diff --git a/engine/include/GearSrc/Log.h b/engine/include/GearSrc/Log.h index f6c6e44..ab2e505 100644 --- a/engine/include/GearSrc/Log.h +++ b/engine/include/GearSrc/Log.h @@ -11,7 +11,8 @@ extern "C" { enum GSLogLevel { GSLogInfo = 0, GSLogWarn, - GSLogError + GSLogError, + GSLogDebug }; GSDECL void GSLog(int level, const char* fmt, ...); diff --git a/engine/src/crc.c b/engine/src/crc.c new file mode 100644 index 0000000..c8e7468 --- /dev/null +++ b/engine/src/crc.c @@ -0,0 +1,25 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-1998 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include + +GSU32 GSCRC32(void* input, unsigned int len) { + int i, j; + GSU32 byte, crc, mask; + unsigned char* message = input; + + i = 0; + crc = 0xFFFFFFFF; + while(i < len) { + byte = message[i]; // Get next byte. + crc = crc ^ byte; + for(j = 7; j >= 0; j--) { // Do eight times. + mask = -(crc & 1); + crc = (crc >> 1) ^ (0xEDB88320 & mask); + } + i = i + 1; + } + return ~crc; +} diff --git a/engine/src/file.c b/engine/src/file.c index 09bdd87..2282f7d 100644 --- a/engine/src/file.c +++ b/engine/src/file.c @@ -30,8 +30,7 @@ GSFile GSFileOpen(GSEngine engine, const char* path) { int i; nam[0] = 0; - nam[1] = 0; - nam += 2; + nam += 1; for(i = 0; i < shlen(engine->resource); i++) { if(strcmp(engine->resource[i].key, s) == 0) { @@ -78,6 +77,11 @@ void GSFileSeek(GSFile file, int pos) { file->seek = pos; } +void GSFileSeekFromCurrent(GSFile file, int pos) { + if(file->fp != NULL) fseek(file->fp, pos, SEEK_CUR); + file->seek += pos; +} + unsigned int GSFileSize(GSFile file) { return file->size; } diff --git a/engine/src/log.c b/engine/src/log.c index a978573..d0b260d 100644 --- a/engine/src/log.c +++ b/engine/src/log.c @@ -13,6 +13,8 @@ void GSLog(int level, const char* fmt, ...) { fprintf(out, "WARN "); } else if(level == GSLogError) { fprintf(out, "ERROR"); + } else if(level == GSLogDebug) { + fprintf(out, "DEBUG"); } #else fprintf(out, "[\x1b[mGearSrc\x1b[m|"); @@ -23,6 +25,8 @@ void GSLog(int level, const char* fmt, ...) { fprintf(out, "\x1b[1m\x1b[33mWARN\x1b[m "); } else if(level == GSLogError) { fprintf(out, "\x1b[1m\x1b[31mERROR\x1b[m"); + } else if(level == GSLogDebug) { + fprintf(out, "\x1b[1m\x1b[34mDEBUG\x1b[m"); } #endif diff --git a/engine/src/resource.c b/engine/src/resource.c index 52956a2..a79c8c0 100644 --- a/engine/src/resource.c +++ b/engine/src/resource.c @@ -3,9 +3,28 @@ #include #include #include +#include +#include #include +/** + * signature: 0x7F PAK + * + * entry: + * 1 byte: type + * 4 bytes: name crc32 + * 1 byte: name len + * n bytes: name + * if file { + * 4 bytes: actual size + * 4 bytes: compressed size + * 4 bytes: seek pos + * } else { + * 2 bytes: number of entries + * } + */ + static unsigned char res_sig[4] = { 0x7f, 'P', @@ -37,45 +56,134 @@ GSResource GSResourceOpen(GSEngine engine, const char* path) { return resource; } -void* GSResourceGet(GSResource resource, const char* name, unsigned int* size) { - char fn[129]; +int find_entry(GSFile f, const char* name, int max, GSU32* totsz, GSU32* actsz, GSU32* cmpsz, GSU32* seekpos) { + GSU32 crc = GSEndianSwapU32BE(GSCRC32((void*)name, strlen(name))); + int i; - fn[128] = 0; + for(i = 0; i < max; i++) { + unsigned char dir; + unsigned char len; + char* fn; + GSU32 t; + + if(GSFileRead(f, &dir, 1) < 1) return -1; + if(GSFileRead(f, &t, 4) < 4) return -1; + if(GSFileRead(f, &len, 1) < 1) return -1; + + fn = malloc(len + 1); + if(len > 0) { + if(GSFileRead(f, fn, len) < len) { + free(fn); + return -1; + } + } + fn[len] = 0; + + if(dir) { + GSU16 n; + GSU32 tsz; + + if(GSFileRead(f, &n, 2) < 2) { + free(fn); + return -1; + } + + if(GSFileRead(f, &tsz, 4) < 4) { + free(fn); + return -1; + } + + n = GSEndianSwapU16BE(n); + tsz = GSEndianSwapU32BE(tsz); + + if(totsz != NULL) *totsz = tsz; + + if(crc == t && strcmp(fn, name) == 0) { + GSLog(GSLogDebug, "Directory %s/ found", fn); + free(fn); + return n; + } + + GSFileSeekFromCurrent(f, tsz); + } else { + if(GSFileRead(f, actsz, 4) < 4) { + free(fn); + return -1; + } + + if(GSFileRead(f, cmpsz, 4) < 4) { + free(fn); + return -1; + } + + if(GSFileRead(f, seekpos, 4) < 4) { + free(fn); + return -1; + } + + *actsz = GSEndianSwapU32BE(*actsz); + *cmpsz = GSEndianSwapU32BE(*cmpsz); + *seekpos = GSEndianSwapU32BE(*seekpos); + + if(crc == t && strcmp(fn, name) == 0) { + GSLog(GSLogDebug, "File %s found", fn); + free(fn); + return -2; + } + } + + free(fn); + } + + return -1; +} + +void* GSResourceGet(GSResource resource, const char* name, unsigned int* size) { + char* p = GSStringDuplicate(name); + char* s = p; + char* f = s; + int m = 1; + GSU32 totsz; + GSU32 actsz; + GSU32 cmpsz; + GSU32 seekpos; + unsigned char* d = NULL; GSFileSeek(resource->file, 4); - while(!(GSFileRead(resource->file, fn, 128) < 128 || fn[0] == 0)) { - GSU32 actsz = 0; - GSU32 cmpsz = 0; - int i; - void* d; + while(1) { + s = strchr(s, '/'); - if(GSFileRead(resource->file, &actsz, 4) < 4) return NULL; - if(GSFileRead(resource->file, &cmpsz, 4) < 4) return NULL; + if(s != NULL) s[0] = 0; - actsz = GSEndianSwapU32BE(actsz); - cmpsz = GSEndianSwapU32BE(cmpsz); + m = find_entry(resource->file, f, m, strlen(f) == 0 ? &totsz : NULL, &actsz, &cmpsz, &seekpos); + if(m == -1) break; + if(m == -2) { + unsigned char* cmp = malloc(cmpsz); - *size = actsz; + if(actsz > cmpsz * 255) actsz = cmpsz * 255; - d = malloc(cmpsz); - if(GSFileRead(resource->file, d, cmpsz) < cmpsz) { - free(d); - return NULL; + d = malloc(actsz); + + GSFileSeek(resource->file, 4 + 1 + 4 + 1 + 2 + 4 + totsz + seekpos); + GSFileRead(resource->file, cmp, cmpsz); + + LZ4_decompress_safe(cmp, d, cmpsz, actsz); + + free(cmp); + + *size = actsz; + break; } - if(strcmp(fn, name) == 0) { - unsigned char* r = malloc(actsz); - LZ4_decompress_safe(d, r, cmpsz, actsz); - free(d); - - return r; - } else { - free(d); - } + if(s == NULL) break; + s++; + f = s; } - return NULL; + free(p); + + return d; } void GSResourceClose(GSResource resource) { diff --git a/engine/tools/gs_makepak/main.c b/engine/tools/gs_makepak/main.c index f541c6a..47b6f6a 100644 --- a/engine/tools/gs_makepak/main.c +++ b/engine/tools/gs_makepak/main.c @@ -8,18 +8,49 @@ #endif #include -typedef struct kv { - char* key; +#include +#include +#include -} kv_t; +typedef struct file file_t; + +struct file { + char* name; + int dir; + int before; + int after; + unsigned char* data; + file_t* parent; + file_t** files; +}; static FILE* out; static unsigned int total = 0; static unsigned int totalact = 0, totalcmp = 0; +static file_t* root; +static GSU32 seek = 0; -static void scan(const char* path, const char* pre); +static file_t* new_file(file_t* parent, const char* name) { + file_t* n = malloc(sizeof(*n)); -static void pack(const char* fullpath, const char* path) { + n->name = GSStringDuplicate(name); + n->dir = 0; + n->before = 0; + n->after = 0; + n->data = NULL; + n->parent = parent; + n->files = NULL; + + if(parent != NULL) { + arrput(parent->files, n); + } + + return n; +} + +static void scan(file_t* parent, const char* path, const char* pre); + +static void pack(file_t* parent, const char* fullpath, const char* path, const char* name) { int m; FILE* f = fopen(fullpath, "rb"); int insz; @@ -44,40 +75,22 @@ static void pack(const char* fullpath, const char* path) { outsz = LZ4_compress_default(input, output, insz, m); if(outsz > 0) { - unsigned int sz; - int i; - char fn[128]; - - memset(fn, 0, 128); - memcpy(fn, path, strlen(path)); - - fwrite(fn, 1, 128, out); - - sz = insz; - for(i = 0; i < 4; i++) { - unsigned char n = (sz >> 24) & 0xff; - - fwrite(&n, 1, 1, out); - - sz = sz << 8; - } - - sz = outsz; - for(i = 0; i < 4; i++) { - unsigned char n = (sz >> 24) & 0xff; - - fwrite(&n, 1, 1, out); - - sz = sz << 8; - } - - fwrite(output, 1, outsz, out); + file_t* n = new_file(parent, name); printf("done (%8d -> %8d)\n", insz, outsz); total++; totalact += insz; totalcmp += outsz; + + free(input); + + n->before = insz; + n->after = outsz; + n->data = output; + + fclose(f); + return; } else { printf("failed\n"); } @@ -104,7 +117,7 @@ static int is_dir(const char* path) { #endif } -static void found(const char* path, const char* pre, const char* input) { +static void found(file_t* parent, const char* path, const char* pre, const char* input) { char* p = malloc(strlen(pre) + strlen(input) + 1 + 1); char* fp = malloc(strlen(path) + 1 + strlen(input) + 1); @@ -118,11 +131,15 @@ static void found(const char* path, const char* pre, const char* input) { strcat(fp, input); if(is_dir(fp)) { + file_t* d = new_file(parent, input); + + d->dir = 1; + strcat(p, "/"); - scan(fp, p); + scan(d, fp, p); } else { - pack(fp, p); + pack(parent, fp, p, input); } free(fp); @@ -130,7 +147,7 @@ static void found(const char* path, const char* pre, const char* input) { free(p); } -static void scan(const char* path, const char* pre) { +static void scan(file_t* parent, const char* path, const char* pre) { #ifdef _WIN32 WIN32_FIND_DATA ffd; HANDLE hFind; @@ -147,7 +164,7 @@ static void scan(const char* path, const char* pre) { do { if(strcmp(ffd.cFileName, ".") != 0 && strcmp(ffd.cFileName, "..") != 0) { - found(path, pre, ffd.cFileName); + found(parent, path, pre, ffd.cFileName); } } while(FindNextFile(hFind, &ffd) != 0); FindClose(hFind); @@ -157,7 +174,7 @@ static void scan(const char* path, const char* pre) { struct dirent* d; while((d = readdir(dir)) != NULL) { if(strcmp(d->d_name, "..") != 0 && strcmp(d->d_name, ".") != 0) { - found(path, pre, d->d_name); + found(parent, path, pre, d->d_name); } } @@ -166,6 +183,76 @@ static void scan(const char* path, const char* pre) { #endif } +static void recursive_write(file_t* parent) { + int i; + + if(parent->dir) { + for(i = 0; i < arrlen(parent->files); i++) { + recursive_write(parent->files[i]); + } + } else if(parent->data != NULL && parent->after > 0) { + fwrite(parent->data, 1, parent->after, out); + } +} + +static int total_size(file_t* parent, int level) { + int sz = level == 0 ? 0 : (1 + 4 + 1 + strlen(parent->name) + 2 + 4); + int i; + + for(i = 0; i < arrlen(parent->files); i++) { + if(parent->files[i]->dir) { + sz += total_size(parent->files[i], level + 1); + } else { + sz += 1 + 4 + 1 + strlen(parent->files[i]->name) + 4 + 4 + 4; + } + } + + return sz; +} + +static void recursive(file_t* parent) { + int i; + unsigned char c; + GSU16 u16 = 0; + GSU32 u32 = 0; + GSU32 crc; + + crc = GSCRC32(parent->name, strlen(parent->name)); + + c = parent->dir ? 1 : 0; + fwrite(&c, 1, 1, out); + + u32 = GSEndianSwapU32BE(crc); + fwrite(&u32, 1, 4, out); + + c = strlen(parent->name); + fwrite(&c, 1, 1, out); + fwrite(parent->name, 1, c, out); + + if(parent->dir) { + u16 = GSEndianSwapU16BE(arrlen(parent->files)); + fwrite(&u16, 1, 2, out); + + u32 = GSEndianSwapU32BE(total_size(parent, 0)); + fwrite(&u32, 1, 4, out); + + for(i = 0; i < arrlen(parent->files); i++) { + recursive(parent->files[i]); + } + } else { + u32 = GSEndianSwapU32BE(parent->before); + fwrite(&u32, 1, 4, out); + + u32 = GSEndianSwapU32BE(parent->after); + fwrite(&u32, 1, 4, out); + + u32 = GSEndianSwapU32BE(seek); + fwrite(&u32, 1, 4, out); + + seek += parent->after; + } +} + int main(int argc, char** argv) { unsigned char dat[128]; @@ -179,21 +266,24 @@ int main(int argc, char** argv) { dat[3] = 'K'; fwrite(dat, 1, 4, out); + + root = new_file(NULL, ""); + root->dir = 1; } if(argc == 2) { - scan(".", ""); + scan(root, ".", ""); } else if(argc > 2) { int i; - for(i = 1; i < argc; i++) scan(argv[i], ""); + for(i = 1; i < argc; i++) scan(root, argv[i], ""); } else { printf("Usage: %s output [input]\n", argv[0]); return 1; } - memset(dat, 0, 128); - fwrite(dat, 1, 128, out); + recursive(root); + recursive_write(root); printf("total %u files added, compressed %u -> %u bytes\n", total, totalact, totalcmp);