pak

pak.git
git clone git://git.lenczewski.org/pak.git
Log | Files | Refs | README | LICENSE

pak.c (20943B)


      1 #define HEADER_IMPL
      2 
      3 #include "pak.h"
      4 
      5 #include <string.h>
      6 #include <unistd.h>
      7 #include <inttypes.h>
      8 
      9 /* mem stream helper
     10  * ===========================================================================
     11  */
     12 
     13 struct pak_mem_stream {
     14 	uint8_t *buf, *end, *cur;
     15 };
     16 
     17 static inline size_t
     18 pak_mem_stream_bytes_remaining(struct pak_mem_stream const *stream)
     19 {
     20 	assert(stream->cur <= stream->end);
     21 	return stream->end - stream->cur;
     22 }
     23 
     24 static inline size_t
     25 pak_mem_stream_bytes_consumed(struct pak_mem_stream const *stream)
     26 {
     27 	assert(stream->buf <= stream->cur);
     28 	return stream->cur - stream->buf;
     29 }
     30 
     31 static inline void
     32 pak_mem_stream_reset(struct pak_mem_stream *stream)
     33 {
     34 	stream->cur = stream->buf;
     35 }
     36 
     37 static inline void *
     38 pak_mem_stream_skip(struct pak_mem_stream *stream, size_t bytes)
     39 {
     40 	assert(stream->cur + bytes <= stream->end);
     41 	void *ptr = stream->cur;
     42 	stream->cur += bytes;
     43 	return ptr;
     44 }
     45 
     46 static inline void
     47 pak_mem_stream_readu8(struct pak_mem_stream *stream, uint8_t *out)
     48 {
     49 	assert(stream->cur + sizeof(uint8_t) <= stream->end);
     50 	uint8_t res = *(stream->cur++);
     51 	*out = res;
     52 	stream->cur += sizeof *out;
     53 }
     54 
     55 static inline void
     56 pak_mem_stream_readu16(struct pak_mem_stream *stream, uint16_t *out)
     57 {
     58 	assert(stream->cur + sizeof(uint16_t) <= stream->end);
     59 	uint16_t res = *((uint16_t *) stream->cur);
     60 	*out = res;
     61 	stream->cur += sizeof *out;
     62 }
     63 
     64 static inline void
     65 pak_mem_stream_readu32(struct pak_mem_stream *stream, uint32_t *out)
     66 {
     67 	assert(stream->cur + sizeof(uint32_t) <= stream->end);
     68 	uint32_t res = *((uint32_t *) stream->cur);
     69 	*out = res;
     70 	stream->cur += sizeof *out;
     71 }
     72 
     73 static inline void
     74 pak_mem_stream_readu64(struct pak_mem_stream *stream, uint64_t *out)
     75 {
     76 	assert(stream->cur + sizeof(uint64_t) <= stream->end);
     77 	uint64_t res = *((uint64_t *) stream->cur);
     78 	*out = res;
     79 	stream->cur += sizeof *out;
     80 }
     81 
     82 static inline void
     83 pak_mem_stream_readbuf(struct pak_mem_stream *stream, void *buf, size_t len)
     84 {
     85 	assert(stream->cur + len <= stream->end);
     86 	memcpy(buf, stream->cur, len);
     87 	stream->cur += len;
     88 }
     89 
     90 #define PAK_MEM_STREAM_READ(stream, var) \
     91 	_Generic((var), \
     92 		 uint8_t:  pak_mem_stream_readu8, \
     93 		 uint16_t: pak_mem_stream_readu16, \
     94 		 uint32_t: pak_mem_stream_readu32, \
     95 		 uint64_t: pak_mem_stream_readu64 \
     96 		)((stream), &(var))
     97 
     98 static inline void
     99 pak_mem_stream_writeu8(struct pak_mem_stream *stream, uint8_t val)
    100 {
    101 	assert(stream->cur + sizeof(uint8_t) <= stream->end);
    102 	*(stream->cur++) = val;
    103 	stream->cur += sizeof val;
    104 }
    105 
    106 static inline void
    107 pak_mem_stream_writeu16(struct pak_mem_stream *stream, uint16_t val)
    108 {
    109 	assert(stream->cur + sizeof(uint16_t) <= stream->end);
    110 	*((uint16_t *) stream->cur) = val;
    111 	stream->cur += sizeof val;
    112 }
    113 
    114 static inline void
    115 pak_mem_stream_writeu32(struct pak_mem_stream *stream, uint32_t val)
    116 {
    117 	assert(stream->cur + sizeof(uint32_t) <= stream->end);
    118 	*((uint32_t *) stream->cur) = val;
    119 	stream->cur += sizeof val;
    120 }
    121 
    122 static inline void
    123 pak_mem_stream_writeu64(struct pak_mem_stream *stream, uint64_t val)
    124 {
    125 	assert(stream->cur + sizeof(uint64_t) <= stream->end);
    126 	*((uint64_t *) stream->cur) = val;
    127 	stream->cur += sizeof val;
    128 }
    129 
    130 static inline void
    131 pak_mem_stream_writebuf(struct pak_mem_stream *stream, void const *buf, size_t len)
    132 {
    133 	assert(stream->cur + len <= stream->end);
    134 	memcpy(stream->cur, buf, len);
    135 	stream->cur += len;
    136 }
    137 
    138 #define PAK_MEM_STREAM_WRITE(stream, var) \
    139 	_Generic((var), \
    140 		 uint8_t:  pak_mem_stream_writeu8, \
    141 		 uint16_t: pak_mem_stream_writeu16, \
    142 		 uint32_t: pak_mem_stream_writeu32, \
    143 		 uint64_t: pak_mem_stream_writeu64 \
    144 		)((stream), (var))
    145 
    146 /* ===========================================================================
    147  */
    148 
    149 static int
    150 pak_write_mem_header(struct pak_mem_stream *stream, struct pakfmt_header const *hdr)
    151 {
    152 	if (pak_mem_stream_bytes_remaining(stream) < PAK_HEADER_SIZE)
    153 		return-1;
    154 
    155 	pak_mem_stream_writebuf(stream, PAK_MAGIC, PAK_MAGIC_LEN);
    156 	PAK_MEM_STREAM_WRITE(stream, hdr->header_length);
    157 
    158 	PAK_MEM_STREAM_WRITE(stream, hdr->index_file_offset);
    159 	PAK_MEM_STREAM_WRITE(stream, hdr->index_compression);
    160 	PAK_MEM_STREAM_WRITE(stream, hdr->index_compressed_length);
    161 	PAK_MEM_STREAM_WRITE(stream, hdr->index_uncompressed_length);
    162 	PAK_MEM_STREAM_WRITE(stream, hdr->index_uncompressed_crc32c);
    163 
    164 	PAK_MEM_STREAM_WRITE(stream, hdr->data_file_offset);
    165 	PAK_MEM_STREAM_WRITE(stream, hdr->data_compressed_length);
    166 
    167 	return 0;
    168 }
    169 
    170 static int
    171 pak_write_mem_tag(struct pak_mem_stream *stream, struct pak_tag const *tag)
    172 {
    173 	if (pak_mem_stream_bytes_remaining(stream) < PAK_TAG_SIZE)
    174 		return -1;
    175 
    176 	pak_mem_stream_writebuf(stream, tag->header.name, sizeof tag->header.name);
    177 	PAK_MEM_STREAM_WRITE(stream, tag->header.type);
    178 	PAK_MEM_STREAM_WRITE(stream, tag->header.length);
    179 
    180 	if (pak_mem_stream_bytes_remaining(stream) < tag->header.length)
    181 		return -1;
    182 
    183 	pak_mem_stream_writebuf(stream, tag->data, tag->header.length);
    184 
    185 	return 0;
    186 }
    187 
    188 static int
    189 pak_write_mem_archive(struct pak_mem_stream *stream, struct pak_archive const *archive)
    190 {
    191 	if (pak_mem_stream_bytes_remaining(stream) < PAK_ARCHIVE_SIZE)
    192 		return -1;
    193 
    194 	pak_mem_stream_writebuf(stream, archive->header.name, sizeof archive->header.name);
    195 	PAK_MEM_STREAM_WRITE(stream, archive->header.type);
    196 	PAK_MEM_STREAM_WRITE(stream, archive->header.length);
    197 
    198 	size_t archive_bytes_remaining = archive->header.length - PAK_ARCHIVE_PRELUDE_SIZE;
    199 	if (pak_mem_stream_bytes_remaining(stream) < archive_bytes_remaining)
    200 		return -1;
    201 
    202 	PAK_MEM_STREAM_WRITE(stream, archive->header.data_offset);
    203 	PAK_MEM_STREAM_WRITE(stream, archive->header.data_compressed_length);
    204 	PAK_MEM_STREAM_WRITE(stream, archive->header.data_uncompressed_length);
    205 	PAK_MEM_STREAM_WRITE(stream, archive->header.data_uncompressed_crc32c);
    206 	PAK_MEM_STREAM_WRITE(stream, archive->header.data_compression);
    207 
    208 	PAK_MEM_STREAM_WRITE(stream, archive->header.reserved);
    209 	PAK_MEM_STREAM_WRITE(stream, archive->header.tag_count);
    210 
    211 	struct pak_tag *tag;
    212 	LIST_ENTRY_ITER(&archive->tags, tag, list_node) {
    213 		if (pak_write_mem_tag(stream, tag) < 0)
    214 			return -1;
    215 	}
    216 
    217 	return 0;
    218 }
    219 
    220 static int
    221 pak_write_mem_archive_data(struct pak_mem_stream *stream, struct pak_archive const *archive)
    222 {
    223 	if (pak_mem_stream_bytes_remaining(stream) < archive->header.data_offset)
    224 		return -1;
    225 
    226 	pak_mem_stream_skip(stream, archive->header.data_offset);
    227 
    228 	assert(archive->header.data_compression == PAK_COMPRESSION_NONE);
    229 	assert(archive->header.data_compressed_length == archive->header.data_uncompressed_length);
    230 
    231 	if (pak_mem_stream_bytes_remaining(stream) < archive->header.data_compressed_length)
    232 		return -1;
    233 
    234 	pak_mem_stream_writebuf(stream, archive->data, archive->header.data_compressed_length);
    235 
    236 	return 0;
    237 }
    238 
    239 static int
    240 pak_write_mem_index(struct pak_mem_stream *stream, struct list_node const *index)
    241 {
    242 	struct pak_archive *it;
    243 	LIST_ENTRY_ITER(index, it, list_node) {
    244 		if (pak_write_mem_archive(stream, it) < 0)
    245 			return -1;
    246 	}
    247 
    248 	return 0;
    249 }
    250 
    251 static int
    252 pak_read_mem_header(struct pak_mem_stream *stream, struct pakfmt_header *hdr)
    253 {
    254 	if (pak_mem_stream_bytes_remaining(stream) < PAK_HEADER_SIZE)
    255 		return -1;
    256 
    257 	pak_mem_stream_readbuf(stream, hdr->magic, sizeof hdr->magic);
    258 	if (strncmp(PAK_MAGIC, hdr->magic, sizeof hdr->magic))
    259 		return -1;
    260 
    261 	PAK_MEM_STREAM_READ(stream, hdr->header_length);
    262 	if (hdr->header_length != PAK_HEADER_SIZE)
    263 		return -1;
    264 
    265 	PAK_MEM_STREAM_READ(stream, hdr->index_file_offset);
    266 	PAK_MEM_STREAM_READ(stream, hdr->index_compression);
    267 	PAK_MEM_STREAM_READ(stream, hdr->index_compressed_length);
    268 	PAK_MEM_STREAM_READ(stream, hdr->index_uncompressed_length);
    269 	PAK_MEM_STREAM_READ(stream, hdr->index_uncompressed_crc32c);
    270 
    271 	PAK_MEM_STREAM_READ(stream, hdr->data_file_offset);
    272 	PAK_MEM_STREAM_READ(stream, hdr->data_compressed_length);
    273 
    274 	return 0;
    275 }
    276 
    277 static struct pak_tag *
    278 pak_read_mem_tag(struct pak_mem_stream *stream, struct arena *arena)
    279 {
    280 	struct pak_tag *res = ARENA_ALLOC_SIZED(arena, struct pak_tag);
    281 	if (!res) return NULL;
    282 
    283 	memset(res, 0, sizeof *res);
    284 
    285 	if (pak_mem_stream_bytes_remaining(stream) < PAK_TAG_SIZE)
    286 		return NULL;
    287 
    288 	pak_mem_stream_readbuf(stream, res->header.name, sizeof res->header.name);
    289 	PAK_MEM_STREAM_READ(stream, res->header.type);
    290 	PAK_MEM_STREAM_READ(stream, res->header.length);
    291 
    292 	if (pak_mem_stream_bytes_remaining(stream) < res->header.length)
    293 		return NULL;
    294 
    295 	res->data = pak_mem_stream_skip(stream, res->header.length);
    296 
    297 	return res;
    298 }
    299 
    300 static struct pak_archive *
    301 pak_read_mem_archive(struct pak_mem_stream *stream, struct arena *arena)
    302 {
    303 	struct pak_archive *res = ARENA_ALLOC_SIZED(arena, struct pak_archive);
    304 	if (!res) return NULL;
    305 
    306 	memset(res, 0, sizeof *res);
    307 
    308 	if (pak_mem_stream_bytes_remaining(stream) < PAK_ARCHIVE_SIZE)
    309 		return NULL;
    310 
    311 	pak_mem_stream_readbuf(stream, res->header.name, sizeof res->header.name);
    312 	PAK_MEM_STREAM_READ(stream, res->header.type);
    313 	PAK_MEM_STREAM_READ(stream, res->header.length);
    314 
    315 	size_t archive_bytes_remaining = res->header.length - PAK_ARCHIVE_PRELUDE_SIZE;
    316 	if (pak_mem_stream_bytes_remaining(stream) < archive_bytes_remaining)
    317 		return NULL;
    318 
    319 	PAK_MEM_STREAM_READ(stream, res->header.data_offset);
    320 	PAK_MEM_STREAM_READ(stream, res->header.data_compressed_length);
    321 	PAK_MEM_STREAM_READ(stream, res->header.data_uncompressed_length);
    322 	PAK_MEM_STREAM_READ(stream, res->header.data_uncompressed_crc32c);
    323 	PAK_MEM_STREAM_READ(stream, res->header.data_compression);
    324 
    325 	PAK_MEM_STREAM_READ(stream, res->header.reserved);
    326 	PAK_MEM_STREAM_READ(stream, res->header.tag_count);
    327 
    328 	res->tags = LIST_INIT(res->tags);
    329 	for (uint32_t i = 0; i < res->header.tag_count; i++) {
    330 		struct pak_tag *ent = pak_read_mem_tag(stream, arena);
    331 		if (!ent) return NULL;
    332 
    333 		list_push_tail(&res->tags, &ent->list_node);
    334 	}
    335 
    336 	return res;
    337 }
    338 
    339 static int
    340 pak_read_mem_archive_data(struct pak_mem_stream *stream, struct pak_archive *archive)
    341 {
    342 	if (pak_mem_stream_bytes_remaining(stream) < archive->header.data_offset)
    343 		return -1;
    344 
    345 	pak_mem_stream_skip(stream, archive->header.data_offset);
    346 
    347 	if (pak_mem_stream_bytes_remaining(stream) < archive->header.data_compressed_length)
    348 		return -1;
    349 
    350 	archive->data = pak_mem_stream_skip(stream, archive->header.data_compressed_length);
    351 
    352 	return 0;
    353 }
    354 
    355 static int
    356 pak_read_mem_index(struct pak_mem_stream *stream, struct arena *arena, struct list_node *index)
    357 {
    358 	while (pak_mem_stream_bytes_remaining(stream)) {
    359 		struct pak_archive *ent = pak_read_mem_archive(stream, arena);
    360 		if (!ent) return -1;
    361 
    362 		list_push_tail(index, &ent->list_node);
    363 	}
    364 
    365 	return 0;
    366 }
    367 
    368 /* ===========================================================================
    369  */
    370 
    371 int
    372 pak_read_header(int fd, struct pakfmt_header *out)
    373 {
    374 	if (lseek(fd, 0, SEEK_SET) < 0)
    375 		return -1;
    376 
    377 	ssize_t bytes_read = 0;
    378 	if ((bytes_read = read(fd, out, sizeof *out)) < 0)
    379 		return -1;
    380 
    381 	if ((size_t) bytes_read < sizeof *out)
    382 		return -1;
    383 
    384 	return 0;
    385 }
    386 
    387 int
    388 pak_read_index(int fd, struct arena *arena, struct pakfmt_header const *hdr,
    389 	       uint8_t *buf, size_t len, struct list_node *index)
    390 {
    391 	/* TODO: this assumes we can always do in-place decompression.
    392 	 *       is this the case?
    393 	 */
    394 	assert(hdr->index_uncompressed_length <= len);
    395 
    396 	int res = -1;
    397 
    398 	if (lseek(fd, hdr->index_file_offset, SEEK_SET) < 0)
    399 		goto error;
    400 
    401 	ssize_t bytes_read = 0;
    402 	if ((bytes_read = read(fd, buf, hdr->index_compressed_length)) < 0)
    403 		goto error;
    404 
    405 	if ((size_t) bytes_read < hdr->index_compressed_length)
    406 		goto error;
    407 
    408 	/* TODO: implement decompression */
    409 	assert(hdr->index_compression == PAK_COMPRESSION_NONE);
    410 	assert(hdr->index_compressed_length == hdr->index_uncompressed_length);
    411 
    412 	struct pak_mem_stream stream = {
    413 		.buf = buf, .end = buf + hdr->index_uncompressed_length, .cur = buf,
    414 	};
    415 
    416 	res = pak_read_mem_index(&stream, arena, index);
    417 
    418 error:
    419 	return res;
    420 }
    421 
    422 int
    423 pak_read_archive(int fd, struct arena *arena, struct pakfmt_header const *hdr,
    424 		 char name[static PAK_ARCHIVE_NAME_LEN], uint32_t type,
    425 		 struct pak_archive **out)
    426 {
    427 	uint8_t prelude[PAK_ARCHIVE_PRELUDE_SIZE];
    428 
    429 	int res = -1;
    430 
    431 	if (lseek(fd, hdr->index_file_offset, SEEK_SET) < 0)
    432 		goto error;
    433 
    434 	do {
    435 		ssize_t bytes_read = 0;
    436 		if ((bytes_read = read(fd, prelude, sizeof prelude)) < 0)
    437 			goto error;
    438 
    439 		if ((size_t) bytes_read < sizeof prelude)
    440 			goto error;
    441 
    442 		// we want to simply access the first few fields in the
    443 		// archive (the "prelude"), and so casting is fine as long
    444 		// as we don't overrun our prelude buffer
    445 		struct pakfmt_archive *archive = (struct pakfmt_archive *) prelude;
    446 		size_t archive_bytes_remaining = archive->length - PAK_ARCHIVE_PRELUDE_SIZE;
    447 
    448 		if (strncmp(name, archive->name, sizeof archive->name))
    449 			goto skip;
    450 
    451 		if (archive->type != type)
    452 			goto skip;
    453 
    454 		// read rest of archive
    455 		uint8_t *buf = arena_alloc(arena, archive->length, alignof(struct pak_archive));
    456 		if (!buf)
    457 			goto error;
    458 
    459 		lseek(fd, -PAK_ARCHIVE_PRELUDE_SIZE, SEEK_CUR);
    460 
    461 		if ((bytes_read = read(fd, buf, archive->length)) < 0)
    462 			goto error;
    463 
    464 		if ((size_t) bytes_read < archive->length)
    465 			goto error;
    466 
    467 		struct pak_mem_stream stream = {
    468 			.buf = buf, .end = buf + archive->length, .cur = buf,
    469 		};
    470 
    471 		*out = pak_read_mem_archive(&stream, arena);
    472 		if (!*out)
    473 			goto error;
    474 
    475 		res = 0;
    476 
    477 		break;
    478 
    479 skip:
    480 		if (lseek(fd, archive_bytes_remaining, SEEK_CUR) < 0)
    481 			goto error;
    482 	} while (1);
    483 
    484 error:
    485 	return res;
    486 }
    487 
    488 int
    489 pak_read_archive_data(int fd, struct pakfmt_header const *hdr,
    490 		      struct pakfmt_archive const *archive,
    491 		      uint8_t *buf, size_t len)
    492 {
    493 	assert(archive->data_compressed_length <= len);
    494 
    495 	if (lseek(fd, hdr->data_file_offset + archive->data_offset, SEEK_SET) < 0)
    496 		return -1;
    497 
    498 	ssize_t bytes_read = 0;
    499 	if ((bytes_read = read(fd, buf, archive->data_compressed_length)) < 0)
    500 		return -1;
    501 
    502 	if ((size_t) bytes_read < archive->data_compressed_length)
    503 		return -1;
    504 
    505 	return 0;
    506 }
    507 
    508 int
    509 pak_write(int fd, struct pak const *pak)
    510 {
    511 	int res = -1;
    512 	void *buf = NULL;
    513 
    514 	size_t serialised_size = pak_file_size(pak);
    515 	buf = malloc(serialised_size);
    516 	if (!buf)
    517 		goto error;
    518 
    519 	if (pak_write_mem(buf, serialised_size, pak) < 0)
    520 		goto error;
    521 
    522 	size_t written = write(fd, buf, serialised_size);
    523 	if (written < serialised_size)
    524 		goto error;
    525 
    526 	res = 0;
    527 
    528 error:
    529 	free(buf);
    530 
    531 	return res;
    532 }
    533 
    534 int
    535 pak_write_mem(uint8_t *buf, size_t len, struct pak const *pak)
    536 {
    537 	struct pak_mem_stream stream = {
    538 		.buf = buf, .end = buf + len, .cur = buf,
    539 	};
    540 
    541 	if (pak_write_mem_header(&stream, &pak->header) < 0)
    542 		return -1;
    543 
    544 	uint8_t *index_buf = buf + pak->header.index_file_offset;
    545 	uint8_t *index_buf_end = index_buf + pak->header.index_compressed_length;
    546 	uint8_t *data_buf = buf + pak->header.data_file_offset;
    547 	uint8_t *data_buf_end = data_buf + pak->header.data_compressed_length;
    548 
    549 	struct pak_mem_stream index_stream = {
    550 		.buf = index_buf, .end = index_buf_end, .cur = index_buf,
    551 	};
    552 
    553 	if (pak_write_mem_index(&index_stream, &pak->index) < 0)
    554 		return -1;
    555 
    556 	struct pak_mem_stream data_stream = {
    557 		.buf = data_buf, .end = data_buf_end, .cur = data_buf,
    558 	};
    559 
    560 	struct pak_archive *it;
    561 	LIST_ENTRY_ITER(&pak->index, it, list_node) {
    562 		pak_mem_stream_reset(&data_stream);
    563 		if (pak_write_mem_archive_data(&data_stream, it) < 0)
    564 			return -1;
    565 	}
    566 
    567 	return 0;
    568 }
    569 
    570 int
    571 pak_read_mem(uint8_t *buf, size_t len, struct arena *arena, struct pak *pak)
    572 {
    573 	struct pak_mem_stream stream = {
    574 		.buf = buf, .end = buf + len, .cur = buf,
    575 	};
    576 
    577 	if (pak_read_mem_header(&stream, &pak->header) < 0)
    578 		return -1;
    579 
    580 	uint8_t *buf_end = buf + len;
    581 	uint8_t *index_buf = buf + pak->header.index_file_offset;
    582 	uint8_t *index_buf_end = index_buf + pak->header.index_compressed_length;
    583 	uint8_t *data_buf = buf + pak->header.data_file_offset;
    584 	uint8_t *data_buf_end = data_buf + pak->header.data_compressed_length;
    585 
    586 	if (index_buf_end > buf_end || data_buf_end > buf_end)
    587 		return -1;
    588 
    589 	pak->index = LIST_INIT(pak->index);
    590 
    591 	struct pak_mem_stream index_stream = {
    592 		.buf = index_buf, .end = index_buf_end, .cur = index_buf,
    593 	};
    594 
    595 	if (pak_read_mem_index(&index_stream, arena, &pak->index) < 0)
    596 		return -1;
    597 
    598 	struct pak_mem_stream data_stream = {
    599 		.buf = data_buf, .end = data_buf_end, .cur = data_buf,
    600 	};
    601 
    602 	struct pak_archive *it;
    603 	LIST_ENTRY_ITER(&pak->index, it, list_node) {
    604 		pak_mem_stream_reset(&data_stream);
    605 		if (pak_read_mem_archive_data(&data_stream, it))
    606 			return -1;
    607 	}
    608 
    609 	return 0;
    610 }
    611 
    612 void
    613 pak_open(struct pak *pak)
    614 {
    615 	memcpy(pak->header.magic, PAK_MAGIC, sizeof pak->header.magic);
    616 	pak->header.header_length = sizeof pak->header;
    617 
    618 	pak->header.index_file_offset = 0;
    619 	pak->header.index_compression = PAK_COMPRESSION_NONE;
    620 	pak->header.index_compressed_length = 0;
    621 	pak->header.index_uncompressed_length = 0;
    622 	pak->header.index_uncompressed_crc32c = 0;
    623 
    624 	pak->header.data_file_offset = 0;
    625 	pak->header.data_compressed_length = 0;
    626 
    627 	pak->index = LIST_INIT(pak->index);
    628 }
    629 
    630 struct pak_archive *
    631 pak_add_archive(struct pak *pak, struct arena *arena,
    632 		char name[static PAK_ARCHIVE_NAME_LEN], uint32_t type,
    633 		enum pak_compression_type compression,
    634 		struct pak_tag *tags, size_t tags_count,
    635 		void *data, size_t compressed_length,
    636 		size_t uncompressed_length, uint32_t uncompressed_crc32c)
    637 {
    638 	struct pak_archive *archive = ARENA_ALLOC_SIZED(arena, struct pak_archive);
    639 	if (!archive) return NULL;
    640 
    641 	memset(archive, 0, sizeof *archive);
    642 
    643 	memcpy(archive->header.name, name, sizeof archive->header.name);
    644 	archive->header.type = type;
    645 	archive->header.length = PAK_ARCHIVE_SIZE;
    646 
    647 	archive->header.data_compressed_length = compressed_length;
    648 	archive->header.data_uncompressed_length = uncompressed_length;
    649 	archive->header.data_uncompressed_crc32c = uncompressed_crc32c;
    650 	archive->header.data_compression = compression;
    651 
    652 	archive->header.tag_count = tags_count;
    653 
    654 	archive->tags = LIST_INIT(archive->tags);
    655 	for (size_t i = 0; i < tags_count; i++) {
    656 		struct pak_tag *tag = ARENA_ALLOC_SIZED(arena, struct pak_tag);
    657 		if (!tag) return NULL;
    658 
    659 		memset(tag, 0, sizeof *tag);
    660 
    661 		memcpy(tag->header.name, tags[i].header.name, sizeof tag->header.name);
    662 		tag->header.type = tags[i].header.type;
    663 		tag->header.length = tags[i].header.length;
    664 
    665 		tag->data = tags[i].data;
    666 
    667 		list_push_tail(&archive->tags, &tag->list_node);
    668 
    669 		archive->header.length += PAK_TAG_SIZE + tag->header.length;
    670 	}
    671 
    672 	archive->data = data;
    673 
    674 	list_push_tail(&pak->index, &archive->list_node);
    675 
    676 	return archive;
    677 }
    678 
    679 void
    680 pak_close(struct pak *pak)
    681 {
    682 	pak->header.index_file_offset = pak->header.header_length;
    683 
    684 	pak->header.index_compressed_length = 0;
    685 	pak->header.data_compressed_length = 0;
    686 
    687 	struct pak_archive *it;
    688 	LIST_ENTRY_ITER(&pak->index, it, list_node) {
    689 		pak->header.index_uncompressed_length += it->header.length;
    690 		pak->header.index_uncompressed_crc32c = 0; /* TODO: calculate crc32c */
    691 
    692 		it->header.data_offset = pak->header.data_compressed_length;
    693 		pak->header.data_compressed_length += it->header.data_compressed_length;
    694 	}
    695 
    696 	assert(pak->header.index_compression == PAK_COMPRESSION_NONE); /* TODO: index compression */
    697 	pak->header.index_compressed_length = pak->header.index_uncompressed_length;
    698 	pak->header.data_file_offset = pak->header.index_file_offset + pak->header.index_compressed_length;
    699 }
    700 
    701 void
    702 pak_debug_dump(struct pak const *pak)
    703 {
    704 	printf("pak header:\n");
    705 	printf("\tlength: %" PRIu32 "\n", pak->header.header_length);
    706 	printf("\tindex file offset: %" PRIu64 "\n", pak->header.index_file_offset);
    707 	printf("\tindex compression: %" PRIu32 "\n", pak->header.index_compression);
    708 	printf("\tindex compressed length: %" PRIu32 "\n", pak->header.index_compressed_length);
    709 	printf("\tindex uncompressed length: %" PRIu32 "\n", pak->header.index_uncompressed_length);
    710 	printf("\tindex uncompressed CRC32c: %" PRIu32 "\n", pak->header.index_uncompressed_crc32c);
    711 	printf("\tdata file offset: %" PRIu64 "\n", pak->header.data_file_offset);
    712 	printf("\tdata compressed length: %" PRIu64 "\n", pak->header.data_compressed_length);
    713 
    714 	printf("pak index:\n");
    715 
    716 	size_t i = 0;
    717 
    718 	struct pak_archive *it;
    719 	LIST_ENTRY_ITER(&pak->index, it, list_node) {
    720 		printf("\tarchive %zu:\n", i++);
    721 		printf("\t\tname: \"%.*s\"\n", (int) sizeof it->header.name, it->header.name);
    722 		printf("\t\tarchive type: %" PRIu32 "\n", it->header.type);
    723 		printf("\t\tarchive length: %" PRIu32 "\n", it->header.length);
    724 		printf("\t\tdata offset: %" PRIu64 "\n", it->header.data_offset);
    725 		printf("\t\tdata compressed length: %" PRIu64 "\n", it->header.data_compressed_length);
    726 		printf("\t\tdata uncompressed length: %" PRIu64 "\n", it->header.data_uncompressed_length);
    727 		printf("\t\tdata uncompressed CRC32c: %" PRIu32 "\n", it->header.data_uncompressed_crc32c);
    728 		printf("\t\tdata compression: %" PRIu32 "\n", it->header.data_compression);
    729 		printf("\t\ttag count: %" PRIu32 "\n", it->header.tag_count);
    730 
    731 		printf("\t\tarchive tags:\n");
    732 
    733 		size_t j = 0;
    734 
    735 		struct pak_tag *tag;
    736 		LIST_ENTRY_ITER(&it->tags, tag, list_node) {
    737 			printf("\t\t\ttag %zu:\n", j++);
    738 			printf("\t\t\t\ttag name: \"%.*s\"\n", (int) sizeof tag->header.name, tag->header.name);
    739 			printf("\t\t\t\ttag type: %" PRIu16 "\n", tag->header.type);
    740 			printf("\t\t\t\ttag length: %" PRIu16 "\n", tag->header.length);
    741 		}
    742 
    743 		printf("\t\tarchive data: %p\n", it->data);
    744 	}
    745 }