physfs_archiver_lec3d.c 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. /*
  2. * LucasArts 1990s-era shooters container files
  3. * GOB (Dark Forces 1994, Jedi Knight 1997)
  4. * LAB (Outlaws 1996)
  5. * LFD (XWing, Tie Fighter, Dark Forces, probably others)
  6. *
  7. * Written by Manuel Lauss <manuel.lauss@gmail.com>
  8. */
  9. #define __PHYSICSFS_INTERNAL__
  10. #include "physfs_internal.h"
  11. #if PHYSFS_SUPPORTS_LECARCHIVES
  12. /* GOB1: WAD-Style */
  13. static int gob1LoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 offset, void *arc)
  14. {
  15. PHYSFS_uint32 i, entries, dofs, dlen;
  16. char name[13];
  17. if (!io->seek(io, offset))
  18. return 0;
  19. BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &entries, 4), 0);
  20. entries = PHYSFS_swapULE32(entries);
  21. for (i = 0; i < entries; i++)
  22. {
  23. BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &dofs, 4), 0);
  24. BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &dlen, 4), 0);
  25. BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 13), 0);
  26. name[12] = '\0';
  27. dofs = PHYSFS_swapULE32(dofs);
  28. dlen = PHYSFS_swapULE32(dlen);
  29. BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, dofs, dlen), 0);
  30. }
  31. return 1;
  32. }
  33. /* GOB2: GOB1 with 127 byte filepath hierarchy */
  34. static int gob2LoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 offset, void *arc)
  35. {
  36. PHYSFS_uint32 i, entries, dofs, dlen;
  37. char name[128], *c;
  38. if (!io->seek(io, offset))
  39. return 0;
  40. BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &entries, 4), 0);
  41. entries = PHYSFS_swapULE32(entries);
  42. for (i = 0; i < entries; i++)
  43. {
  44. BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &dofs, 4), 0);
  45. BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &dlen, 4), 0);
  46. BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 128), 0);
  47. name[127] = '\0';
  48. /* replace backslashes */
  49. c = name;
  50. while (*c)
  51. {
  52. if (*c == '\\')
  53. *c = '/';
  54. ++c;
  55. }
  56. dofs = PHYSFS_swapULE32(dofs);
  57. dlen = PHYSFS_swapULE32(dlen);
  58. BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, dofs, dlen), 0);
  59. }
  60. return 1;
  61. }
  62. static void *GOB_openArchive(PHYSFS_Io *io, const char *name,
  63. int forWriting, int *claimed)
  64. {
  65. PHYSFS_uint8 buf[12];
  66. PHYSFS_uint32 catofs = 0;
  67. void *unpkarc = NULL;
  68. int ret, gob;
  69. assert(io != NULL); /* shouldn't ever happen. */
  70. BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
  71. BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, buf, 4), NULL);
  72. if (memcmp(buf, "GOB\x0a", 4) == 0)
  73. {
  74. gob = 1;
  75. }
  76. else if (memcmp(buf, "GOB\x20", 4) == 0)
  77. {
  78. /* Version */
  79. BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &catofs, 4), NULL);
  80. catofs = PHYSFS_swapULE32(catofs);
  81. if (catofs != 0x14)
  82. BAIL(PHYSFS_ERR_UNSUPPORTED, NULL);
  83. gob = 2;
  84. }
  85. else
  86. {
  87. BAIL(PHYSFS_ERR_UNSUPPORTED, NULL);
  88. gob = 0;
  89. }
  90. BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &catofs, 4), NULL);
  91. catofs = PHYSFS_swapULE32(catofs);
  92. *claimed = 1;
  93. unpkarc = UNPK_openArchive(io, 0, 1);
  94. BAIL_IF_ERRPASS(!unpkarc, NULL);
  95. switch (gob)
  96. {
  97. case 1: ret = gob1LoadEntries(io, catofs, unpkarc); break;
  98. case 2: ret = gob2LoadEntries(io, catofs, unpkarc); break;
  99. default: ret = 0;
  100. }
  101. if (ret == 0)
  102. {
  103. UNPK_abandonArchive(unpkarc);
  104. return NULL;
  105. }
  106. return unpkarc;
  107. }
  108. const PHYSFS_Archiver __PHYSFS_Archiver_GOB =
  109. {
  110. CURRENT_PHYSFS_ARCHIVER_API_VERSION,
  111. {
  112. "GOB",
  113. "LucasArts GOB container",
  114. "Manuel Lauss <manuel.lauss@gmail.com>",
  115. "https://icculus.org/physfs/",
  116. 0, /* supportsSymlinks */
  117. },
  118. GOB_openArchive,
  119. UNPK_enumerate,
  120. UNPK_openRead,
  121. UNPK_openWrite,
  122. UNPK_openAppend,
  123. UNPK_remove,
  124. UNPK_mkdir,
  125. UNPK_stat,
  126. UNPK_closeArchive
  127. };
  128. /** LFD **/
  129. static int lfdLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 skip, void *arc)
  130. {
  131. char ext[5], finalname[16];
  132. PHYSFS_uint32 dlen, pos;
  133. PHYSFS_sint64 len;
  134. int i;
  135. len = io->length(io);
  136. if (len < 4)
  137. {
  138. PHYSFS_setErrorCode(PHYSFS_ERR_PAST_EOF);
  139. return 0;
  140. }
  141. pos = skip;
  142. while (pos < len)
  143. {
  144. if (!io->seek(io, pos))
  145. return 0;
  146. memset(finalname, 0, 16);
  147. BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ext, 4), 0);
  148. BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, finalname, 8), 0);
  149. BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &dlen, 4), 0);
  150. dlen = PHYSFS_swapULE32(dlen);
  151. ext[4] = 0;
  152. i = strlen(finalname);
  153. finalname[i] = '.';
  154. strcpy(finalname + i + 1, ext);
  155. pos += 16; /* step over file meta info */
  156. BAIL_IF_ERRPASS(!UNPK_addEntry(arc, finalname, 0, -1, -1, pos, dlen), 0);
  157. pos += dlen; /* step over file contents */
  158. }
  159. return 1;
  160. }
  161. static void *LFD_openArchive(PHYSFS_Io *io, const char *name,
  162. int forWriting, int *claimed)
  163. {
  164. PHYSFS_uint8 buf[16];
  165. PHYSFS_uint32 catsize;
  166. void *unpkarc = NULL;
  167. assert(io != NULL); /* shouldn't ever happen. */
  168. BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
  169. BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, buf, 12), NULL);
  170. if (memcmp(buf, "RMAPresource", 12) == 0)
  171. {
  172. /* has a content directory */
  173. BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &catsize, 4), NULL);
  174. catsize = PHYSFS_swapULE32(catsize);
  175. if ((catsize & 15) || (catsize == 0))
  176. BAIL(PHYSFS_ERR_UNSUPPORTED, NULL);
  177. catsize += 16; /* include header */
  178. }
  179. else
  180. {
  181. catsize = 0;
  182. }
  183. if (!io->seek(io, 0))
  184. return NULL;
  185. *claimed = 1;
  186. unpkarc = UNPK_openArchive(io, 0, 1);
  187. BAIL_IF_ERRPASS(!unpkarc, NULL);
  188. if (!lfdLoadEntries(io, catsize, unpkarc))
  189. {
  190. UNPK_abandonArchive(unpkarc);
  191. return NULL;
  192. }
  193. return unpkarc;
  194. }
  195. const PHYSFS_Archiver __PHYSFS_Archiver_LFD =
  196. {
  197. CURRENT_PHYSFS_ARCHIVER_API_VERSION,
  198. {
  199. "LFD",
  200. "LucasArts LFD container",
  201. "Manuel Lauss <manuel.lauss@gmail.com>",
  202. "https://icculus.org/physfs/",
  203. 0, /* supportsSymlinks */
  204. },
  205. LFD_openArchive,
  206. UNPK_enumerate,
  207. UNPK_openRead,
  208. UNPK_openWrite,
  209. UNPK_openAppend,
  210. UNPK_remove,
  211. UNPK_mkdir,
  212. UNPK_stat,
  213. UNPK_closeArchive
  214. };
  215. /** LAB **/
  216. /* read characters until \0 is found */
  217. static PHYSFS_sint32 readstring(PHYSFS_Io *io, char *dest, unsigned max)
  218. {
  219. PHYSFS_uint32 bytes = 0;
  220. PHYSFS_sint64 ret;
  221. char c;
  222. while (bytes < max)
  223. {
  224. ret = io->read(io, &c, 1);
  225. if (ret == 0)
  226. return 0; /* file ended prematurely */
  227. else if (ret < 0)
  228. return -1;
  229. else if (c == 0)
  230. return bytes;
  231. *dest++ = c;
  232. bytes++;
  233. }
  234. return bytes;
  235. }
  236. static int labLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 cnt, void *arc)
  237. {
  238. const PHYSFS_uint32 lab_name_table_start = 16 + (cnt * 16);
  239. PHYSFS_uint32 nofs, dofs, dlen, fcc;
  240. PHYSFS_sint32 readlen;
  241. PHYSFS_sint64 savepos;
  242. char fn[32];
  243. PHYSFS_uint32 i;
  244. for (i = 0; i < cnt; i++)
  245. {
  246. BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &nofs, 4), 0);
  247. BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &dofs, 4), 0);
  248. BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &dlen, 4), 0);
  249. BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &fcc, 4), 0);
  250. nofs = PHYSFS_swapULE32(nofs);
  251. dofs = PHYSFS_swapULE32(dofs);
  252. dlen = PHYSFS_swapULE32(dlen);
  253. fcc = PHYSFS_swapULE32(fcc);
  254. /* remember where we parked */
  255. savepos = io->tell(io);
  256. if (savepos < 0)
  257. return 0;
  258. /* go to the filename table entry and read it */
  259. if (!io->seek(io, lab_name_table_start + nofs))
  260. return 0;
  261. memset(fn, 0, 32);
  262. readlen = readstring(io, fn, 31);
  263. if (readlen > 0)
  264. {
  265. BAIL_IF_ERRPASS(!UNPK_addEntry(arc, fn, 0, -1, -1, dofs, dlen), 0);
  266. }
  267. else
  268. {
  269. if (readlen == 0)
  270. PHYSFS_setErrorCode(PHYSFS_ERR_PAST_EOF);
  271. return 0;
  272. }
  273. /* ah that's the spot */
  274. if (!io->seek(io, savepos))
  275. return 0;
  276. }
  277. return 1;
  278. }
  279. static void *LAB_openArchive(PHYSFS_Io *io, const char *name,
  280. int forWriting, int *claimed)
  281. {
  282. PHYSFS_uint8 buf[16];
  283. PHYSFS_uint32 catsize, entries, i;
  284. void *unpkarc = NULL;
  285. assert(io != NULL); /* shouldn't ever happen. */
  286. BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL);
  287. BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, buf, 4), NULL);
  288. if (memcmp(buf, "LABN", 4) != 0)
  289. BAIL(PHYSFS_ERR_UNSUPPORTED, NULL);
  290. /* Container Format version, always 0x00010000 */
  291. BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &i, 4), NULL);
  292. i = PHYSFS_swapULE32(i);
  293. if (i != 0x00010000)
  294. BAIL(PHYSFS_ERR_UNSUPPORTED, NULL);
  295. BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &entries, 4), NULL);
  296. BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &catsize, 4), NULL);
  297. entries = PHYSFS_swapULE32(entries);
  298. catsize = PHYSFS_swapULE32(catsize);
  299. *claimed = 1;
  300. unpkarc = UNPK_openArchive(io, 0, 1);
  301. BAIL_IF_ERRPASS(!unpkarc, NULL);
  302. if (!labLoadEntries(io, entries, unpkarc))
  303. {
  304. UNPK_abandonArchive(unpkarc);
  305. return NULL;
  306. }
  307. return unpkarc;
  308. }
  309. const PHYSFS_Archiver __PHYSFS_Archiver_LAB =
  310. {
  311. CURRENT_PHYSFS_ARCHIVER_API_VERSION,
  312. {
  313. "LAB",
  314. "LucasArts LAB container",
  315. "Manuel Lauss <manuel.lauss@gmail.com>",
  316. "https://icculus.org/physfs/",
  317. 0, /* supportsSymlinks */
  318. },
  319. LAB_openArchive,
  320. UNPK_enumerate,
  321. UNPK_openRead,
  322. UNPK_openWrite,
  323. UNPK_openAppend,
  324. UNPK_remove,
  325. UNPK_mkdir,
  326. UNPK_stat,
  327. UNPK_closeArchive
  328. };
  329. #endif