collection.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  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 999
  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(VPK1Header)) {
  261. PRINT("VPK header is too small");
  262. exit(-1);
  263. }
  264. const VPK1Header *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 < 1 || 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 + ((header->version == 1) ? sizeof(VPK1Header) : sizeof(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. #define DUMP_VPK_CONTENTS 0
  315. #if DUMP_VPK_CONTENTS
  316. PRINTF("%s crc=%08x pre=%d arc=%d(%04x) off=%d len=%d",
  317. filename_temp,
  318. entry->crc,
  319. entry->preloadBytes, entry->archive, entry->archive,
  320. entry->archiveOffset, entry->archiveLength);
  321. #endif
  322. struct VPKFileMetadata *file = stackAlloc(mem->persistent, sizeof(struct VPKFileMetadata));
  323. if (!file) {
  324. PRINT("Not enough persistent memory");
  325. exit(-1);
  326. }
  327. memset(file, 0, sizeof(*file));
  328. file->filename.s = filename_temp;
  329. file->filename.len = filename_len - 1;
  330. if (entry->preloadBytes) {
  331. file->dir.off = c - (char*)dir;
  332. file->dir.size = entry->preloadBytes;
  333. }
  334. if (entry->archiveLength) {
  335. file->archive = entry->archive != 0x7fff ? entry->archive : -1;
  336. file->arc.off = entry->archiveOffset;
  337. file->arc.size = entry->archiveLength;
  338. }
  339. if (file->archive > max_archives)
  340. max_archives = file->archive;
  341. files_end = file + 1;
  342. ++collection->files_count;
  343. c += entry->preloadBytes;
  344. } // for filenames
  345. } // for paths
  346. } // for extensions
  347. // sort
  348. qsort(files_begin, files_end - files_begin, sizeof(*files_begin), vpkMetadataCompare);
  349. // store filenames in persistent memory
  350. for (struct VPKFileMetadata *file = files_begin; file != files_end; ++file) {
  351. char *string = stackAlloc(mem->persistent, file->filename.len + 1);
  352. if (!string) {
  353. PRINT("Not enough persistent memory");
  354. exit(-1);
  355. }
  356. memcpy(string, file->filename.s, file->filename.len + 1);
  357. file->filename.s = string;
  358. }
  359. // open archives
  360. if (max_archives >= MAX_VPK_ARCHIVES) {
  361. PRINTF("Too many archives: %d", max_archives);
  362. exit(-1);
  363. }
  364. const int dirfile_len = strlen(dir_filename) + 1;
  365. char *arcname = alloca(dirfile_len);
  366. if (!arcname || dirfile_len < 8) {
  367. PRINT("WTF");
  368. exit(-1);
  369. }
  370. memcpy(arcname, dir_filename, dirfile_len);
  371. for (int i = 0; i <= max_archives; ++i) {
  372. sprintf(arcname + dirfile_len - 8, "%03d.vpk", i);
  373. if (AFile_Success != aFileOpen(collection->archives+i, arcname)) {
  374. PRINTF("Cannot open archive %s", arcname);
  375. exit(-1);
  376. }
  377. }
  378. stackFreeUpToPosition(mem->temp, temp_stack_top);
  379. collection->head.open = vpkCollectionFileOpen;
  380. collection->head.close = vpkCollectionClose;
  381. collection->files = files_begin;
  382. return &collection->head;
  383. }
  384. struct PakfileFileMetadata {
  385. struct StringView filename;
  386. const void *data;
  387. uint32_t size;
  388. };
  389. struct PakfileCollection {
  390. struct ICollection head;
  391. struct Memories mem;
  392. struct PakfileFileMetadata *files;
  393. int files_count;
  394. };
  395. struct PakfileCollectionFile {
  396. struct IFile head;
  397. const struct PakfileFileMetadata *metadata;
  398. struct Stack *stack;
  399. };
  400. static void pakfileCollectionClose(struct ICollection *collection) {
  401. struct PakfileCollection *pc = (void*)collection;
  402. stackFreeUpToPosition(pc->mem.persistent, pc->files);
  403. }
  404. static size_t pakfileCollectionFileRead(struct IFile *file, size_t offset, size_t size, void *buffer) {
  405. struct PakfileCollectionFile *f = (struct PakfileCollectionFile*)file;
  406. const struct PakfileFileMetadata *meta = f->metadata;
  407. if (offset > meta->size)
  408. return 0;
  409. if (offset + size > meta->size)
  410. size = meta->size - offset;
  411. memcpy(buffer, (const char*)meta->data + offset, size);
  412. return size;
  413. }
  414. static void pakfileCollectionFileClose(struct IFile *file) {
  415. struct PakfileCollectionFile *f = (void*)file;
  416. stackFreeUpToPosition(f->stack, f);
  417. }
  418. static enum CollectionOpenResult pakfileCollectionFileOpen(struct ICollection *collection,
  419. const char *name, enum FileType type, struct IFile **out_file) {
  420. struct PakfileCollection *pakfilec = (struct PakfileCollection*)collection;
  421. *out_file = NULL;
  422. struct PakfileCollectionFile *file = stackAlloc(pakfilec->mem.temp, sizeof(*file));
  423. char *filename = makeResourceFilename(pakfilec->mem.temp, NULL, name, type);
  424. if (!file || !filename) {
  425. PRINTF("Not enough memory for file %s", name);
  426. return CollectionOpen_NotEnoughMemory;
  427. }
  428. // binary search
  429. {
  430. const struct PakfileFileMetadata *begin = pakfilec->files;
  431. int count = pakfilec->files_count;
  432. while (count > 0) {
  433. int item = count / 2;
  434. const struct PakfileFileMetadata *meta = begin + item;
  435. const int comparison = strncmp(filename, meta->filename.s, meta->filename.len);
  436. if (comparison == 0) {
  437. file->metadata = meta;
  438. file->stack = pakfilec->mem.temp;
  439. file->head.size = meta->size;
  440. file->head.read = pakfileCollectionFileRead;
  441. file->head.close = pakfileCollectionFileClose;
  442. *out_file = &file->head;
  443. stackFreeUpToPosition(pakfilec->mem.temp, filename);
  444. return CollectionOpen_Success;
  445. }
  446. if (comparison < 0) {
  447. count = item;
  448. } else {
  449. count = count - item - 1;
  450. begin += item + 1;
  451. }
  452. }
  453. }
  454. stackFreeUpToPosition(pakfilec->mem.temp, file);
  455. return CollectionOpen_NotFound;
  456. }
  457. static int pakfileMetadataCompare(const void *a, const void *b) {
  458. const struct PakfileFileMetadata *am = a, *bm = b;
  459. return strncmp(am->filename.s, bm->filename.s,
  460. am->filename.len < bm->filename.len ?
  461. am->filename.len : bm->filename.len);
  462. }
  463. struct ICollection *collectionCreatePakfile(struct Memories *mem, const void *pakfile, uint32_t size) {
  464. (void)mem;
  465. // 1. need to find zip end of directory
  466. if (size < (int)sizeof(struct ZipEndOfDirectory)) {
  467. PRINT("Invalid pakfile size");
  468. return NULL;
  469. }
  470. int eod_offset = size - sizeof(struct ZipEndOfDirectory);
  471. const struct ZipEndOfDirectory *eod;
  472. for (;;) {
  473. eod = (const void*)((const char*)pakfile + eod_offset);
  474. // TODO what if comment contain signature?
  475. if (eod->signature == ZipEndOfDirectory_Signature)
  476. break;
  477. --eod_offset;
  478. }
  479. if (!eod) {
  480. PRINT("End-of-directory not found");
  481. return NULL;
  482. }
  483. if (eod->dir_offset > size || eod->dir_size > size || eod->dir_size + eod->dir_offset > size) {
  484. PRINTF("Wrong pakfile directory sizes; size=%d, dir_offset=%d, dir_size=%d",
  485. size, eod->dir_offset, eod->dir_size);
  486. return NULL;
  487. }
  488. struct PakfileFileMetadata *metadata_start = stackGetCursor(mem->persistent);
  489. int files_count = 0;
  490. const char *dir = (const char*)pakfile + eod->dir_offset, *dir_end = dir + eod->dir_size;
  491. for (;;) {
  492. if (dir == dir_end)
  493. break;
  494. if (dir_end - dir < (long)sizeof(struct ZipFileHeader)) {
  495. PRINT("Corrupted directory");
  496. break;
  497. }
  498. const struct ZipFileHeader *fileheader = (void*)dir;
  499. if (fileheader->signature != ZipFileHeader_Signature) {
  500. PRINTF("Wrong file header signature: %08x", fileheader->signature);
  501. break;
  502. }
  503. // TODO overflow
  504. const char *next_dir = dir + sizeof(struct ZipFileHeader) + fileheader->filename_length + fileheader->extra_field_length + fileheader->file_comment_length;
  505. if (dir > dir_end) {
  506. PRINT("Corrupted directory");
  507. break;
  508. }
  509. if (fileheader->compression != 0 || fileheader->uncompressed_size != fileheader->compressed_size) {
  510. PRINTF("Compression method %d is not supported", fileheader->compression);
  511. } else {
  512. const char *filename = dir + sizeof(struct ZipFileHeader);
  513. const char *local = (const char*)pakfile + fileheader->local_offset;
  514. if (size - (local - (const char*)pakfile) < (long)sizeof(struct ZipLocalFileHeader)) {
  515. PRINT("Local file header OOB");
  516. break;
  517. }
  518. const struct ZipLocalFileHeader *localheader = (void*)local;
  519. if (localheader->signature != ZipLocalFileHeader_Signature) {
  520. PRINTF("Invalid local file header signature %08x", localheader->signature);
  521. break;
  522. }
  523. // TODO overflow
  524. local += sizeof(*localheader) + localheader->filename_length + localheader->extra_field_length;
  525. if ((local - (const char*)pakfile) + fileheader->compressed_size > size) {
  526. PRINT("File data OOB");
  527. break;
  528. }
  529. struct PakfileFileMetadata *metadata = stackAlloc(mem->persistent, sizeof(*metadata));
  530. if (!metadata) {
  531. PRINT("Not enough memory");
  532. exit(-1);
  533. }
  534. metadata->data = local;
  535. metadata->size = fileheader->uncompressed_size;
  536. metadata->filename.s = filename;
  537. metadata->filename.len = fileheader->filename_length;
  538. ++files_count;
  539. /*
  540. PRINTF("FILE \""PRI_SV"\" size=%d offset=%d",
  541. PASS_SV(metadata->filename), fileheader->uncompressed_size, fileheader->local_offset);
  542. */
  543. }
  544. dir = next_dir;
  545. }
  546. if (dir != dir_end) {
  547. PRINT("Something went wrong");
  548. exit(-1);
  549. }
  550. // sort
  551. qsort(metadata_start, files_count, sizeof(*metadata_start), pakfileMetadataCompare);
  552. struct PakfileCollection *collection = stackAlloc(mem->persistent, sizeof(*collection));
  553. collection->mem = *mem;
  554. collection->files = metadata_start;
  555. collection->files_count = files_count;
  556. collection->head.open = pakfileCollectionFileOpen;
  557. collection->head.close = pakfileCollectionClose;
  558. return &collection->head;
  559. }