collection.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694
  1. #include "collection.h"
  2. #include "common.h"
  3. #include "vpk.h"
  4. #include "zip.h"
  5. #ifndef _WIN32
  6. #include <alloca.h>
  7. #else
  8. #define alloca _alloca
  9. #endif
  10. enum CollectionOpenResult collectionChainOpen(struct ICollection *collection,
  11. const char *name, enum FileType type, struct IFile **out_file) {
  12. while (collection) {
  13. enum CollectionOpenResult result = collection->open(collection, name, type, out_file);
  14. if (result == CollectionOpen_Success) return result;
  15. if (result == CollectionOpen_NotFound) {
  16. collection = collection->next;
  17. } else {
  18. /* TODO print error */
  19. return result;
  20. }
  21. }
  22. return CollectionOpen_NotFound;
  23. }
  24. struct FilesystemCollectionFile {
  25. struct IFile head;
  26. struct AFile file;
  27. struct Stack *temp;
  28. };
  29. struct FilesystemCollection {
  30. struct ICollection head;
  31. struct Memories mem;
  32. char *prefix;
  33. };
  34. static size_t filesystemCollectionFile_Read(struct IFile *file, size_t offset, size_t size, void *buffer) {
  35. struct FilesystemCollectionFile *f = (void*)file;
  36. const size_t result = aFileReadAtOffset(&f->file, offset, size, buffer);
  37. return result != AFileError ? result : 0;
  38. }
  39. static void filesystemCollectionFile_Close(struct IFile *file) {
  40. struct FilesystemCollectionFile *f = (void*)file;
  41. aFileClose(&f->file);
  42. stackFreeUpToPosition(f->temp, f);
  43. }
  44. static void filesystemCollectionClose(struct ICollection *collection) {
  45. (void)(collection);
  46. /* TODO ensure all files are closed */
  47. /* TODO free memory */
  48. }
  49. static char *makeResourceFilename(struct Stack *temp, const char *prefix, const char *name, enum FileType type) {
  50. const char *subdir = NULL;
  51. const char *suffix = NULL;
  52. switch (type) {
  53. case File_Map: subdir = "maps/"; suffix = ".bsp"; break;
  54. case File_Material: subdir = "materials/"; suffix = ".vmt"; break;
  55. case File_Texture: subdir = "materials/"; suffix = ".vtf"; break;
  56. case File_Model: subdir = "models/"; suffix = ".mdl"; break;
  57. }
  58. const int prefix_len = prefix ? strlen(prefix) : 0;
  59. const int subdir_len = strlen(subdir);
  60. const int name_len = strlen(name);
  61. const int suffix_len = strlen(suffix);
  62. const int name_length = prefix_len + subdir_len + name_len + suffix_len + 1;
  63. char *output = stackAlloc(temp, name_length);
  64. if (!output) return NULL;
  65. char *c = output;
  66. if (prefix)
  67. for (int i = 0; i < prefix_len; ++i)
  68. *c++ = prefix[i];
  69. for (int i = 0; i < subdir_len; ++i)
  70. *c++ = subdir[i];
  71. for (int i = 0; i < name_len; ++i) {
  72. char C = tolower(name[i]);
  73. *c++ = (C == '\\') ? '/' : C;
  74. }
  75. for (int i = 0; i < suffix_len; ++i)
  76. *c++ = suffix[i];
  77. *c = '\0';
  78. return output;
  79. }
  80. static enum CollectionOpenResult filesystemCollectionOpen(struct ICollection *collection,
  81. const char *name, enum FileType type, struct IFile **out_file) {
  82. struct FilesystemCollection *fsc = (struct FilesystemCollection*)collection;
  83. *out_file = NULL;
  84. struct FilesystemCollectionFile *file = stackAlloc(fsc->mem.temp, sizeof(*file));
  85. char *filename = makeResourceFilename(fsc->mem.temp, fsc->prefix, name, type);
  86. if (!file || !filename) {
  87. PRINTF("Not enough memory for file %s", name);
  88. return CollectionOpen_NotEnoughMemory;
  89. }
  90. if (aFileOpen(&file->file, filename) != AFile_Success) {
  91. if (type == File_Map)
  92. PRINTF("Cannot open map %s", filename);
  93. stackFreeUpToPosition(fsc->mem.temp, file);
  94. return CollectionOpen_NotFound;
  95. }
  96. file->head.size = file->file.size;
  97. file->head.read = filesystemCollectionFile_Read;
  98. file->head.close = filesystemCollectionFile_Close;
  99. file->temp = fsc->mem.temp;
  100. *out_file = &file->head;
  101. stackFreeUpToPosition(fsc->mem.temp, filename);
  102. return CollectionOpen_Success;
  103. }
  104. struct ICollection *collectionCreateFilesystem(struct Memories *mem, const char *dir) {
  105. int dir_len = strlen(dir);
  106. struct FilesystemCollection *collection = stackAlloc(mem->persistent, sizeof(*collection) + dir_len + 2);
  107. if (!collection)
  108. return NULL;
  109. memset(collection, 0, sizeof *collection);
  110. collection->mem = *mem;
  111. collection->prefix = (char*)(collection + 1);
  112. memcpy(collection->prefix, dir, dir_len);
  113. collection->prefix[dir_len] = '/';
  114. collection->prefix[dir_len+1] = '\0';
  115. collection->head.open = filesystemCollectionOpen;
  116. collection->head.close = filesystemCollectionClose;
  117. return &collection->head;
  118. }
  119. struct StringView {
  120. const char *s;
  121. int len;
  122. };
  123. #define PRI_SV "%.*s"
  124. #define PASS_SV(sv) sv.len, sv.s
  125. #define PASS_PSV(sv) sv->len, sv->s
  126. static struct StringView readString(const char **c, const char *end) {
  127. struct StringView ret = { *c, 0 };
  128. while (*c < end && **c != '\0') ++(*c);
  129. ret.len = *c - ret.s;
  130. ++(*c);
  131. return ret;
  132. }
  133. struct VPKFileMetadata {
  134. struct StringView filename;
  135. int archive;
  136. struct {
  137. size_t off, size;
  138. } dir, arc;
  139. };
  140. #define MAX_VPK_ARCHIVES 32
  141. struct VPKCollection {
  142. struct ICollection head;
  143. struct Memories mem;
  144. struct {
  145. const char *data;
  146. int size;
  147. } dir;
  148. struct AFile archives[MAX_VPK_ARCHIVES];
  149. struct VPKFileMetadata *files;
  150. int files_count;
  151. };
  152. struct VPKCollectionFile {
  153. struct IFile head;
  154. const struct VPKFileMetadata *metadata;
  155. struct VPKCollection *collection;
  156. };
  157. static void vpkCollectionClose(struct ICollection *collection) {
  158. (void)(collection);
  159. /* FIXME close handles and free memory */
  160. }
  161. static size_t vpkCollectionFileRead(struct IFile *file, size_t offset, size_t size, void *buffer) {
  162. struct VPKCollectionFile *f = (struct VPKCollectionFile*)file;
  163. const struct VPKFileMetadata *meta = f->metadata;
  164. size_t size_read = 0;
  165. if (offset < meta->dir.size) {
  166. const void *begin = ((char*)f->collection->dir.data) + offset + meta->dir.off;
  167. const size_t dir_size_left = meta->dir.size - offset;
  168. if (size < dir_size_left) {
  169. memcpy(buffer, begin, size);
  170. return size;
  171. }
  172. const size_t size_to_read = size - dir_size_left;
  173. memcpy(buffer, begin, size_to_read);
  174. buffer = ((char*)buffer) + size_to_read;
  175. offset += size_to_read;
  176. size -= size_to_read;
  177. size_read += size_to_read;
  178. }
  179. offset -= meta->dir.size;
  180. if (offset < meta->arc.size)
  181. size_read += aFileReadAtOffset(&f->collection->archives[meta->archive], meta->arc.off + offset, size, buffer);
  182. return size_read;
  183. }
  184. static void vpkCollectionFileClose(struct IFile *file) {
  185. struct VPKCollectionFile *f = (void*)file;
  186. stackFreeUpToPosition(f->collection->mem.temp, f);
  187. }
  188. static enum CollectionOpenResult vpkCollectionFileOpen(struct ICollection *collection,
  189. const char *name, enum FileType type, struct IFile **out_file) {
  190. struct VPKCollection *vpkc = (struct VPKCollection*)collection;
  191. *out_file = NULL;
  192. struct VPKCollectionFile *file = stackAlloc(vpkc->mem.temp, sizeof(*file));
  193. char *filename = makeResourceFilename(vpkc->mem.temp, NULL, name, type);
  194. if (!file || !filename) {
  195. PRINTF("Not enough memory for file %s", name);
  196. return CollectionOpen_NotEnoughMemory;
  197. }
  198. // binary search
  199. {
  200. const struct VPKFileMetadata *begin = vpkc->files;
  201. int count = vpkc->files_count;
  202. while (count > 0) {
  203. int item = count / 2;
  204. const struct VPKFileMetadata *meta = begin + item;
  205. const int comparison = strcmp(filename, meta->filename.s);
  206. if (comparison == 0) {
  207. file->metadata = meta;
  208. file->collection = vpkc;
  209. file->head.size = meta->arc.size + meta->dir.size;
  210. file->head.read = vpkCollectionFileRead;
  211. file->head.close = vpkCollectionFileClose;
  212. *out_file = &file->head;
  213. stackFreeUpToPosition(vpkc->mem.temp, filename);
  214. return CollectionOpen_Success;
  215. }
  216. if (comparison < 0) {
  217. count = item;
  218. } else {
  219. count = count - item - 1;
  220. begin += item + 1;
  221. }
  222. }
  223. }
  224. stackFreeUpToPosition(vpkc->mem.temp, file);
  225. return CollectionOpen_NotFound;
  226. }
  227. static int vpkMetadataCompare(const void *a, const void *b) {
  228. const struct VPKFileMetadata *am = a, *bm = b;
  229. return strcmp(am->filename.s, bm->filename.s);
  230. }
  231. struct ICollection *collectionCreateVPK(struct Memories *mem, const char *dir_filename) {
  232. PRINTF("Opening collection %s", dir_filename);
  233. struct VPKCollection *collection = stackAlloc(mem->persistent, sizeof(*collection));
  234. if (!collection)
  235. return NULL;
  236. memset(collection, 0, sizeof *collection);
  237. collection->mem = *mem;
  238. void *const temp_stack_top = stackGetCursor(mem->temp);
  239. {
  240. struct AFile dir_file;
  241. if (AFile_Success != aFileOpen(&dir_file, dir_filename)) {
  242. PRINTF("Cannot open %s", dir_filename);
  243. exit(-1);
  244. }
  245. char *data = stackAlloc(mem->persistent, dir_file.size);
  246. if (!data) {
  247. PRINTF("Cannot allocate %zd bytes of persistent memory", dir_file.size);
  248. exit(-1);
  249. }
  250. if (aFileReadAtOffset(&dir_file, 0, dir_file.size, data) != dir_file.size) {
  251. PRINTF("Cannot read entire directory of %zd bytes", dir_file.size);
  252. exit(-1);
  253. }
  254. collection->dir.data = data;
  255. collection->dir.size = dir_file.size;
  256. aFileClose(&dir_file);
  257. }
  258. const char *dir = collection->dir.data;
  259. const size_t size = collection->dir.size;
  260. if (size <= sizeof(struct VPK2Header)) {
  261. PRINT("VPK header is too small");
  262. exit(-1);
  263. }
  264. const struct VPK2Header *header = (void*)collection->dir.data;
  265. if (header->signature != VPK_SIGNATURE) {
  266. PRINTF("Wrong VPK signature %08x", header->signature);
  267. exit(-1);
  268. }
  269. if (header->version != 2) {
  270. PRINTF("VPK version %d is not supported", header->version);
  271. exit(-1);
  272. }
  273. struct VPKFileMetadata *files_begin = stackGetCursor(mem->persistent), *files_end = files_begin;
  274. int max_archives = -1;
  275. const char *const end = dir + size;
  276. const char *c = dir + sizeof(struct VPK2Header);
  277. for (;;) {
  278. // read extension
  279. const struct StringView ext = readString(&c, end);
  280. if (ext.len == 0)
  281. break;
  282. for (;;) {
  283. // read path
  284. const struct StringView path = readString(&c, end);
  285. if (path.len == 0)
  286. break;
  287. for (;;) {
  288. // read filename
  289. const struct StringView filename = readString(&c, end);
  290. if (filename.len == 0)
  291. break;
  292. if ((unsigned long)(end - c) < sizeof(struct VPKTreeEntry)) {
  293. PRINT("Incomplete VPKTreeEntry struct");
  294. exit(-1);
  295. }
  296. const struct VPKTreeEntry *const entry = (const struct VPKTreeEntry*)c;
  297. c += sizeof(struct VPKTreeEntry);
  298. if (entry->terminator != VPK_TERMINATOR) {
  299. PRINTF("Wrong terminator: %04x", entry->terminator);
  300. exit(-1);
  301. }
  302. const int filename_len = 3 + path.len + filename.len + ext.len;
  303. char *filename_temp = stackAlloc(mem->temp, filename_len);
  304. if (!filename_temp) {
  305. PRINT("Not enough temp memory");
  306. exit(-1);
  307. }
  308. memcpy(filename_temp, path.s, path.len);
  309. filename_temp[path.len] = '/';
  310. memcpy(filename_temp + path.len + 1, filename.s, filename.len);
  311. filename_temp[path.len + 1 + filename.len] = '.';
  312. memcpy(filename_temp + path.len + 1 + filename.len + 1, ext.s, ext.len);
  313. filename_temp[filename_len-1] = '\0';
  314. /*
  315. PRINTF("%s crc=%08x pre=%d arc=%d(%04x) off=%d len=%d",
  316. filename_temp,
  317. entry->crc,
  318. entry->preloadBytes, entry->archive, entry->archive,
  319. entry->archiveOffset, entry->archiveLength);
  320. */
  321. struct VPKFileMetadata *file = stackAlloc(mem->persistent, sizeof(struct VPKFileMetadata));
  322. if (!file) {
  323. PRINT("Not enough persistent memory");
  324. exit(-1);
  325. }
  326. memset(file, 0, sizeof(*file));
  327. file->filename.s = filename_temp;
  328. file->filename.len = filename_len - 1;
  329. if (entry->preloadBytes) {
  330. file->dir.off = c - (char*)dir;
  331. file->dir.size = entry->preloadBytes;
  332. }
  333. if (entry->archiveLength) {
  334. file->archive = entry->archive != 0x7fff ? entry->archive : -1;
  335. file->arc.off = entry->archiveOffset;
  336. file->arc.size = entry->archiveLength;
  337. }
  338. if (file->archive > max_archives)
  339. max_archives = file->archive;
  340. files_end = file + 1;
  341. ++collection->files_count;
  342. c += entry->preloadBytes;
  343. } // for filenames
  344. } // for paths
  345. } // for extensions
  346. // sort
  347. qsort(files_begin, files_end - files_begin, sizeof(*files_begin), vpkMetadataCompare);
  348. // store filenames in persistent memory
  349. for (struct VPKFileMetadata *file = files_begin; file != files_end; ++file) {
  350. char *string = stackAlloc(mem->persistent, file->filename.len + 1);
  351. if (!string) {
  352. PRINT("Not enough persistent memory");
  353. exit(-1);
  354. }
  355. memcpy(string, file->filename.s, file->filename.len + 1);
  356. file->filename.s = string;
  357. }
  358. // open archives
  359. if (max_archives >= MAX_VPK_ARCHIVES) {
  360. PRINTF("Too many archives: %d", max_archives);
  361. exit(-1);
  362. }
  363. const int dirfile_len = strlen(dir_filename) + 1;
  364. char *arcname = alloca(dirfile_len);
  365. if (!arcname || dirfile_len < 8) {
  366. PRINT("WTF");
  367. exit(-1);
  368. }
  369. memcpy(arcname, dir_filename, dirfile_len);
  370. for (int i = 0; i <= max_archives; ++i) {
  371. sprintf(arcname + dirfile_len - 8, "%03d.vpk", i);
  372. if (AFile_Success != aFileOpen(collection->archives+i, arcname)) {
  373. PRINTF("Cannot open archive %s", arcname);
  374. exit(-1);
  375. }
  376. }
  377. stackFreeUpToPosition(mem->temp, temp_stack_top);
  378. collection->head.open = vpkCollectionFileOpen;
  379. collection->head.close = vpkCollectionClose;
  380. collection->files = files_begin;
  381. return &collection->head;
  382. }
  383. struct PakfileFileMetadata {
  384. struct StringView filename;
  385. const void *data;
  386. uint32_t size;
  387. };
  388. struct PakfileCollection {
  389. struct ICollection head;
  390. struct Memories mem;
  391. struct PakfileFileMetadata *files;
  392. int files_count;
  393. };
  394. struct PakfileCollectionFile {
  395. struct IFile head;
  396. const struct PakfileFileMetadata *metadata;
  397. struct Stack *stack;
  398. };
  399. static void pakfileCollectionClose(struct ICollection *collection) {
  400. struct PakfileCollection *pc = (void*)collection;
  401. stackFreeUpToPosition(pc->mem.persistent, pc->files);
  402. }
  403. static size_t pakfileCollectionFileRead(struct IFile *file, size_t offset, size_t size, void *buffer) {
  404. struct PakfileCollectionFile *f = (struct PakfileCollectionFile*)file;
  405. const struct PakfileFileMetadata *meta = f->metadata;
  406. if (offset > meta->size)
  407. return 0;
  408. if (offset + size > meta->size)
  409. size = meta->size - offset;
  410. memcpy(buffer, (const char*)meta->data + offset, size);
  411. return size;
  412. }
  413. static void pakfileCollectionFileClose(struct IFile *file) {
  414. struct PakfileCollectionFile *f = (void*)file;
  415. stackFreeUpToPosition(f->stack, f);
  416. }
  417. static enum CollectionOpenResult pakfileCollectionFileOpen(struct ICollection *collection,
  418. const char *name, enum FileType type, struct IFile **out_file) {
  419. struct PakfileCollection *pakfilec = (struct PakfileCollection*)collection;
  420. *out_file = NULL;
  421. struct PakfileCollectionFile *file = stackAlloc(pakfilec->mem.temp, sizeof(*file));
  422. char *filename = makeResourceFilename(pakfilec->mem.temp, NULL, name, type);
  423. if (!file || !filename) {
  424. PRINTF("Not enough memory for file %s", name);
  425. return CollectionOpen_NotEnoughMemory;
  426. }
  427. // binary search
  428. {
  429. const struct PakfileFileMetadata *begin = pakfilec->files;
  430. int count = pakfilec->files_count;
  431. while (count > 0) {
  432. int item = count / 2;
  433. const struct PakfileFileMetadata *meta = begin + item;
  434. const int comparison = strncmp(filename, meta->filename.s, meta->filename.len);
  435. if (comparison == 0) {
  436. file->metadata = meta;
  437. file->stack = pakfilec->mem.temp;
  438. file->head.size = meta->size;
  439. file->head.read = pakfileCollectionFileRead;
  440. file->head.close = pakfileCollectionFileClose;
  441. *out_file = &file->head;
  442. stackFreeUpToPosition(pakfilec->mem.temp, filename);
  443. return CollectionOpen_Success;
  444. }
  445. if (comparison < 0) {
  446. count = item;
  447. } else {
  448. count = count - item - 1;
  449. begin += item + 1;
  450. }
  451. }
  452. }
  453. stackFreeUpToPosition(pakfilec->mem.temp, file);
  454. return CollectionOpen_NotFound;
  455. }
  456. static int pakfileMetadataCompare(const void *a, const void *b) {
  457. const struct PakfileFileMetadata *am = a, *bm = b;
  458. return strncmp(am->filename.s, bm->filename.s,
  459. am->filename.len < bm->filename.len ?
  460. am->filename.len : bm->filename.len);
  461. }
  462. struct ICollection *collectionCreatePakfile(struct Memories *mem, const void *pakfile, uint32_t size) {
  463. (void)mem;
  464. // 1. need to find zip end of directory
  465. if (size < (int)sizeof(struct ZipEndOfDirectory)) {
  466. PRINT("Invalid pakfile size");
  467. return NULL;
  468. }
  469. int eod_offset = size - sizeof(struct ZipEndOfDirectory);
  470. const struct ZipEndOfDirectory *eod;
  471. for (;;) {
  472. eod = (void*)((const char*)pakfile + eod_offset);
  473. // TODO what if comment contain signature?
  474. if (eod->signature == ZipEndOfDirectory_Signature)
  475. break;
  476. --eod_offset;
  477. }
  478. if (!eod) {
  479. PRINT("End-of-directory not found");
  480. return NULL;
  481. }
  482. if (eod->dir_offset > size || eod->dir_size > size || eod->dir_size + eod->dir_offset > size) {
  483. PRINTF("Wrong pakfile directory sizes; size=%d, dir_offset=%d, dir_size=%d",
  484. size, eod->dir_offset, eod->dir_size);
  485. return NULL;
  486. }
  487. struct PakfileFileMetadata *metadata_start = stackGetCursor(mem->persistent);
  488. int files_count = 0;
  489. const char *dir = (const char*)pakfile + eod->dir_offset, *dir_end = dir + eod->dir_size;
  490. for (;;) {
  491. if (dir == dir_end)
  492. break;
  493. if (dir_end - dir < (long)sizeof(struct ZipFileHeader)) {
  494. PRINT("Corrupted directory");
  495. break;
  496. }
  497. const struct ZipFileHeader *fileheader = (void*)dir;
  498. if (fileheader->signature != ZipFileHeader_Signature) {
  499. PRINTF("Wrong file header signature: %08x", fileheader->signature);
  500. break;
  501. }
  502. // TODO overflow
  503. const char *next_dir = dir + sizeof(struct ZipFileHeader) + fileheader->filename_length + fileheader->extra_field_length + fileheader->file_comment_length;
  504. if (dir > dir_end) {
  505. PRINT("Corrupted directory");
  506. break;
  507. }
  508. if (fileheader->compression != 0 || fileheader->uncompressed_size != fileheader->compressed_size) {
  509. PRINTF("Compression method %d is not supported", fileheader->compression);
  510. } else {
  511. const char *filename = dir + sizeof(struct ZipFileHeader);
  512. const char *local = (const char*)pakfile + fileheader->local_offset;
  513. if (size - (local - (const char*)pakfile) < (long)sizeof(struct ZipLocalFileHeader)) {
  514. PRINT("Local file header OOB");
  515. break;
  516. }
  517. const struct ZipLocalFileHeader *localheader = (void*)local;
  518. if (localheader->signature != ZipLocalFileHeader_Signature) {
  519. PRINTF("Invalid local file header signature %08x", localheader->signature);
  520. break;
  521. }
  522. // TODO overflow
  523. local += sizeof(*localheader) + localheader->filename_length + localheader->extra_field_length;
  524. if ((local - (const char*)pakfile) + fileheader->compressed_size > size) {
  525. PRINT("File data OOB");
  526. break;
  527. }
  528. struct PakfileFileMetadata *metadata = stackAlloc(mem->persistent, sizeof(*metadata));
  529. if (!metadata) {
  530. PRINT("Not enough memory");
  531. exit(-1);
  532. }
  533. metadata->data = local;
  534. metadata->size = fileheader->uncompressed_size;
  535. metadata->filename.s = filename;
  536. metadata->filename.len = fileheader->filename_length;
  537. ++files_count;
  538. /*
  539. PRINTF("FILE \""PRI_SV"\" size=%d offset=%d",
  540. PASS_SV(metadata->filename), fileheader->uncompressed_size, fileheader->local_offset);
  541. */
  542. }
  543. dir = next_dir;
  544. }
  545. if (dir != dir_end) {
  546. PRINT("Something went wrong");
  547. exit(-1);
  548. }
  549. // sort
  550. qsort(metadata_start, files_count, sizeof(*metadata_start), pakfileMetadataCompare);
  551. struct PakfileCollection *collection = stackAlloc(mem->persistent, sizeof(*collection));
  552. collection->mem = *mem;
  553. collection->files = metadata_start;
  554. collection->files_count = files_count;
  555. collection->head.open = pakfileCollectionFileOpen;
  556. collection->head.close = pakfileCollectionClose;
  557. return &collection->head;
  558. }