diff --git a/README.md b/README.md index d1ffc40..64a10cb 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,9 @@ funpack output.fpk [unpacked/] # dir defaults to "output" if omitted ```c #include -fpk_t *pak = fpk_open("assets.fpk"); -fpk_find(pak, "some/file.bin", &data, &size); +struct fpk *pak = fpk_open("assets.fpk"); +fpk_entry_t *entry = fpk_find(pak, "some/file.bin"); +void *data = fpk_read(pak, entry, &size); fpk_close(pak); ``` diff --git a/docs/format.md b/docs/format.md new file mode 100644 index 0000000..fa8cd67 --- /dev/null +++ b/docs/format.md @@ -0,0 +1,52 @@ +# FPK1 format + +``` +[ header ][ data blobs ][ string table ][ directory entries ] +``` + +## Header (28 bytes) + +| Field | Type | Size | Offset | +|-----------------|-----------|------|--------| +| `magic` | char[4] | 4 | 0 | +| `entry_count` | uint64_t | 8 | 4 | +| `dir_offset` | uint64_t | 8 | 12 | +| `strtab_offset` | uint64_t | 8 | 20 | + +`magic` = `"FPK1"`. + +`FPK_HEADER_SIZE` (28): field-by-field, not `sizeof()`. + +## Data blobs + +Raw bytes, back-to-back, no padding, writer add order. + +## String table + +Paths, `/`-separated, NUL-terminated, back-to-back, entry order. + +`strtab_offset` points here. + +## Directory (25 bytes per entry) + +| Field | Type | Size | Offset | +|---------------|----------|------|--------| +| `path_offset` | uint64_t | 8 | 0 | +| `data_offset` | uint64_t | 8 | 8 | +| `data_size` | uint64_t | 8 | 16 | +| `method` | uint8_t | 1 | 24 | + +`path_offset` relative to `strtab_offset`. `data_offset` absolute. + +`FPK_DIR_ENTRY_SIZE` (25): field-by-field, not `sizeof()`. + +`dir_offset` points here, last region. File size = `dir_offset + +entry_count * FPK_DIR_ENTRY_SIZE`. + +## Compression + +`method`: reserved, v1 = `FPK_METHOD_STORE` (0) only. + +## Endianness + +Little-endian on disk, always. Big-endian host swaps on read/write. diff --git a/include/fpk.h b/include/fpk.h new file mode 100644 index 0000000..2332454 --- /dev/null +++ b/include/fpk.h @@ -0,0 +1,63 @@ +#ifndef FPK_H +#define FPK_H + +#include +#include + +#define FPK_MAGIC "FPK1" +#define FPK_MAGIC_SIZE 4 +#define FPK_VERSION 1 + +#define FPK_HEADER_SIZE 28 +/* + * magic 4 + * entry_count 8 + * dir_offset 8 + * strtab_offset 8 + * -- + * 28 + */ +#define FPK_DIR_ENTRY_SIZE 25 +/* + * path_offset 8 + * data_offset 8 + * data_size 8 + * method 1 + * -- + * 25 + */ + +#define FPK_METHOD_STORE 0 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + const char *path; + uint64_t data_offset; + uint64_t data_size; + uint8_t method; +} fpk_entry_t; + +struct fpk; +struct fpk_writer; + +struct fpk *fpk_open(const char *path); +void fpk_close(struct fpk *fpk); +fpk_entry_t *fpk_find(struct fpk *fpk, const char *path); +void *fpk_read(struct fpk *fpk, const fpk_entry_t *entry, size_t *out_size); +uint64_t fpk_entry_count(const struct fpk *fpk); +const fpk_entry_t *fpk_entry_at(const struct fpk *fpk, uint64_t index); + +struct fpk_writer *fpk_writer_open(const char *path); +int fpk_writer_add(struct fpk_writer *w, const char *path, const void *data, + size_t size); +int fpk_writer_finalize(struct fpk_writer *w); +void fpk_writer_close(struct fpk_writer *w); + +#ifdef __cplusplus +} +#endif + +#endif /* FPK_H */ diff --git a/include/fpk_version.h b/include/fpk_version.h new file mode 100644 index 0000000..4deaac3 --- /dev/null +++ b/include/fpk_version.h @@ -0,0 +1,16 @@ +#ifndef FPK_VERSION_H +#define FPK_VERSION_H + +#define FPK_VERSION_MAJOR 0 +#define FPK_VERSION_MINOR 1 +#define FPK_VERSION_PATCH 0 + +#define FPK_STRINGIFY_HELPER(x) #x +#define FPK_STRINGIFY(x) FPK_STRINGIFY_HELPER(x) + +#define FPK_VERSION_STRING \ + FPK_STRINGIFY(FPK_VERSION_MAJOR) \ + "." FPK_STRINGIFY(FPK_VERSION_MINOR) "." FPK_STRINGIFY( \ + FPK_VERSION_PATCH) + +#endif /* FPK_VERSION_H */