didiextract.c 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084
  1. /* This file is part of DiDiExtract.
  2. *
  3. * DiDiExtract is free software: you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation, either version 3 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * DiDiExtract is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  15. */
  16. #include <stdbool.h>
  17. #include <stdio.h>
  18. #include <stdint.h>
  19. #include <string.h>
  20. #include <time.h>
  21. #include <cerrno>
  22. #include <getopt.h>
  23. #include <libgen.h> // dirname
  24. #include <sys/stat.h> // mkdir
  25. // PATH_MAX, NAME_MAX
  26. #ifdef __linux__
  27. #include <linux/limits.h>
  28. #else
  29. #include <limits.h> // *BSD and MSYS2
  30. #endif
  31. #include <assert.h>
  32. #include <zlib.h>
  33. #include "pefile.h"
  34. #include "print.h"
  35. #include "reader.h"
  36. #include "errors.h"
  37. #include "version.h"
  38. #define INFLATE_BUF_SIZE 0x4000
  39. #define SIZE_KiB 1024
  40. #define SIZE_MiB 1048576 // 1024^2
  41. #define SIZE_GiB 1073741824 // 1024^3
  42. #define WIN_PATH_MAX 260
  43. #define MAX_OUTPUT_PATH (PATH_MAX - WIN_PATH_MAX) - 2
  44. static char OutputPath[MAX_OUTPUT_PATH]; // should be absolute and end with a '/'
  45. #define DIDI_MAX_CHUNK_COUNT 1024
  46. // https://www.jazz2online.com/wiki/zlib/
  47. // https://www.rfc-editor.org/rfc/rfc1950
  48. // https://www.rfc-editor.org/rfc/rfc1951
  49. enum Operation {
  50. OP_NONE = 0,
  51. OP_EXTRACT = 1,
  52. OP_EXTRACT_DOOFUS = 2,
  53. OP_LIST = 3,
  54. OP_VERIFY = 4,
  55. OP_HELP = 5
  56. };
  57. // This is the header found at the overlay offset. DiDi probably stands for
  58. // Digital Dimensions, the creator of the installer.
  59. struct DiDiHeader {
  60. unsigned char magic[4]; // 0 DiDi
  61. uint32_t signature; // 4 0xDEADBABE
  62. uint32_t unknown_1; // 8
  63. uint32_t unknown_2; // 12
  64. uint32_t filesize; // 16
  65. uint32_t unknown_3; // 20
  66. uint32_t uncompressed_size; // 24
  67. uint32_t unknown_4; // 28 either a size or offset
  68. uint32_t filecount; // 32
  69. uint32_t doofus_offset; // 36 this + overlayoffset = offset to -DOOFUS-
  70. };
  71. // Contains deflated data for the installer, like translated strings and icons.
  72. // NOTE: Did not found a matching CRC32
  73. struct DoofusHeader {
  74. unsigned char magic[8]; // 0 -DOOFUS-
  75. uint32_t uncompressed_size; // 8
  76. uint32_t data_size; // 12
  77. uint32_t crc32; // 16 TODO it doesn't match
  78. };
  79. // File header
  80. struct DiDiFile {
  81. unsigned char magic[4]; // 0 FILE
  82. uint32_t uncompressed_size; // 4 uncompressed size
  83. uint32_t data_size; // 8 compressed size
  84. unsigned char datedata[7]; // 12
  85. unsigned char unknown_8[8]; // 19
  86. uint8_t group; // -- set from 'unknown_8' for now
  87. uint8_t language; // 27
  88. uint8_t filename_size; // 28
  89. unsigned char padding; // 29 padding/separator
  90. // Size of the filename depends on 'filename_size', it is not \0 terminated.
  91. // The maximum size is 255 since it is limited by a uint8_t, we add +1 so
  92. // it becomes 256 for the \0 terminator.
  93. unsigned char filename[256];
  94. };
  95. DIDIEErr readDiDiHeader(FILE * fp, DiDiHeader * dest) {
  96. DIDIEErr status;
  97. status = readBytesInto(fp, dest->magic, 4);
  98. if (status != DIDIE_OK) {
  99. return status;
  100. }
  101. // Verify that the magic is 'DiDi'
  102. if (*((uint32_t*)dest->magic) != 1766091076) {
  103. printError("Expected 'DiDi' magic.\n");
  104. return DIDIE_ERR_DIDI;
  105. }
  106. status = readUInt32(fp, &dest->signature);
  107. if (status != DIDIE_OK) {
  108. return status;
  109. }
  110. // Verify that the signature is 0xDEADBABE
  111. if (dest->signature != 0xDEADBABE) {
  112. printError("Expected 0xDEADBABE signature.\n");
  113. return DIDIE_ERR_DIDI;
  114. }
  115. status = readUInt32(fp, &dest->unknown_1);
  116. if (status != DIDIE_OK) {
  117. return status;
  118. }
  119. status = readUInt32(fp, &dest->unknown_2);
  120. if (status != DIDIE_OK) {
  121. return status;
  122. }
  123. status = readUInt32(fp, &dest->filesize);
  124. if (status != DIDIE_OK) {
  125. return status;
  126. }
  127. status = readUInt32(fp, &dest->unknown_3);
  128. if (status != DIDIE_OK) {
  129. return status;
  130. }
  131. status = readUInt32(fp, &dest->uncompressed_size);
  132. if (status != DIDIE_OK) {
  133. return status;
  134. }
  135. status = readUInt32(fp, &dest->unknown_4);
  136. if (status != DIDIE_OK) {
  137. return status;
  138. }
  139. status = readUInt32(fp, &dest->filecount);
  140. if (status != DIDIE_OK) {
  141. return status;
  142. }
  143. status = readUInt32(fp, &dest->doofus_offset);
  144. if (status != DIDIE_OK) {
  145. return status;
  146. }
  147. return DIDIE_OK;
  148. }
  149. DIDIEErr readDoofusHeader(FILE * fp, DoofusHeader * dest) {
  150. DIDIEErr status;
  151. status = readBytesInto(fp, dest->magic, 8);
  152. if (status != DIDIE_OK) {
  153. return status;
  154. }
  155. // Verify that the magic is '-DOOFUS-'
  156. if (strncmp((const char*)dest->magic, "-DOOFUS-", 8) != 0) {
  157. printError("Expected '-DOOFUS-' magic.\n");
  158. return DIDIE_ERR_DIDI;
  159. }
  160. status = readUInt32(fp, &dest->uncompressed_size);
  161. if (status != DIDIE_OK) {
  162. return status;
  163. }
  164. status = readUInt32(fp, &dest->data_size);
  165. if (status != DIDIE_OK) {
  166. return status;
  167. }
  168. status = readUInt32(fp, &dest->crc32);
  169. if (status != DIDIE_OK) {
  170. return status;
  171. }
  172. return DIDIE_OK;
  173. }
  174. DIDIEErr readDiDiFile(FILE * fp, DiDiFile * dest) {
  175. DIDIEErr status;
  176. // init struct
  177. memset(dest->filename, 0, 256);
  178. // Read magic
  179. status = readBytesInto(fp, dest->magic, 4);
  180. if (status != DIDIE_OK) {
  181. return status;
  182. }
  183. // Verify that the magic is 'FILE'
  184. if (*((uint32_t*)dest->magic) != 1162627398) {
  185. printError("Expected 'FILE' magic.\n");
  186. return DIDIE_ERR_DIDI;
  187. }
  188. // Read unknown
  189. status = readUInt32(fp, &dest->uncompressed_size);
  190. if (status != DIDIE_OK) {
  191. return status;
  192. }
  193. // Read data_size
  194. status = readUInt32(fp, &dest->data_size);
  195. if (status != DIDIE_OK) {
  196. return status;
  197. }
  198. // read date bytes
  199. status = readBytesInto(fp, dest->datedata, 7);
  200. if (status != DIDIE_OK) {
  201. return status;
  202. }
  203. // Read unknown 8 bytes
  204. status = readBytesInto(fp, dest->unknown_8, 8);
  205. if (status != DIDIE_OK) {
  206. return status;
  207. }
  208. dest->group = dest->unknown_8[5];
  209. status = readBytesInto(fp, &dest->language, 1);
  210. if (status != DIDIE_OK) {
  211. return status;
  212. }
  213. // HACKS
  214. // There might be a extra byte before the filename-size, we don't know.
  215. // What we know is that after the filename-size there should be a zero byte,
  216. // probably padding or something. So we use that to check if we should skip
  217. // a byte or not. TODO detect this once and not per file!
  218. status = readBytesInto(fp, &dest->filename_size, 1);
  219. if (status != DIDIE_OK) {
  220. return status;
  221. }
  222. status = readBytesInto(fp, &dest->padding, 1);
  223. if (status != DIDIE_OK) {
  224. return status;
  225. }
  226. if (dest->padding != 0) {
  227. //printf("Padding != 0!\n");
  228. dest->filename_size = dest->padding;
  229. status = readBytesInto(fp, &dest->padding, 1);
  230. if (status != DIDIE_OK) {
  231. return status;
  232. }
  233. }
  234. // Read filename
  235. status = readBytesInto(fp, dest->filename, dest->filename_size);
  236. if (status != DIDIE_OK) {
  237. return status;
  238. }
  239. return DIDIE_OK;
  240. }
  241. // Returns DIDIE_OK on success
  242. // Returns DIDIE_ERR_CRC32 on CRC32 mismatch
  243. // Returns DIDIE_ERR_INFLATE when something else gone wrong.
  244. DIDIEErr inflateFile(FILE *in_file, FILE *out_file, uint32_t available, z_stream *strm, uint32_t filecrc) {
  245. int ret;
  246. unsigned long newcrc;
  247. unsigned int have;
  248. unsigned char in_buf[INFLATE_BUF_SIZE];
  249. unsigned char out_buf[INFLATE_BUF_SIZE];
  250. newcrc = crc32(0, NULL, 0);
  251. uint32_t done = 0;
  252. uint32_t next_available = INFLATE_BUF_SIZE;
  253. if (next_available > available) {
  254. next_available = available;
  255. }
  256. do {
  257. // Set how many bytes we want to read next
  258. if ((done + next_available) > available) {
  259. next_available = available - done;
  260. }
  261. // Reached the end of what we wanted to read.
  262. if (!next_available) {
  263. break;
  264. }
  265. // Read data
  266. strm->avail_in = fread(in_buf, 1, next_available, in_file);
  267. if (ferror(in_file)) {
  268. printError("Failed to read from input file.\n");
  269. return DIDIE_ERR_INFLATE;
  270. }
  271. // This should never happen because the above check on '!next_available',
  272. // but just in case.
  273. if (strm->avail_in == 0) {
  274. printError("Failed to read, got 0 bytes, this should never happen.\n");
  275. return DIDIE_ERR_INFLATE;
  276. }
  277. strm->next_in = in_buf;
  278. /* run inflate() on input until output buffer not full */
  279. do {
  280. // Inflate
  281. strm->avail_out = INFLATE_BUF_SIZE;
  282. strm->next_out = out_buf;
  283. ret = inflate(strm, Z_NO_FLUSH);
  284. assert(ret != Z_STREAM_ERROR); /* state not clobbered */
  285. // Inflate failed
  286. if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) {
  287. if (strm->msg != NULL) {
  288. printError("zlib: %s\n", strm->msg);
  289. }
  290. else {
  291. printError("zlib error: %d\n", ret);
  292. }
  293. return DIDIE_ERR_INFLATE;
  294. }
  295. have = INFLATE_BUF_SIZE - strm->avail_out;
  296. // Write to output file
  297. if (out_file != NULL) {
  298. if (fwrite(out_buf, 1, have, out_file) != have || ferror(out_file)) {
  299. printError("Failed to write, have: %d, strm.avail_out: %d, "
  300. "next_available: %d\n", have, strm->avail_out,
  301. next_available);
  302. return DIDIE_ERR_INFLATE;
  303. }
  304. }
  305. // Update the CRC32
  306. if (have) {
  307. newcrc = crc32(newcrc, out_buf, have);
  308. }
  309. } while (strm->avail_out == 0);
  310. done += next_available;
  311. } while (ret != Z_STREAM_END);
  312. if (filecrc != newcrc) {
  313. printError("CRC Mismatch! Expected: %08X Got: %08X\n", filecrc, newcrc);
  314. return DIDIE_ERR_CRC32;
  315. }
  316. return DIDIE_OK;
  317. }
  318. void printHelp(void) {
  319. printf("==============================================================\n");
  320. printf(" Welcome to DiDi Extract version %s\n", DIDIE_VERSION_STR);
  321. printf("==============================================================\n\n");
  322. printf(" Usage: didiextract [OPERATION] [OPTIONS] INPUT_FILE\n\n");
  323. printf(" OPERATIONS\n");
  324. printf(" -x --extract OUTPUT_PATH Extract files.\n");
  325. printf(" -y --extract-doofus OUTPUT_PATH Extract doofus.bin, which "
  326. "contains, installer-strings, "
  327. "installer-icons/images and "
  328. "probably some instructions for "
  329. "the installer.\n");
  330. printf(" -l --list List files.\n");
  331. printf(" -V --verify Run extract without actually "
  332. "outputting files, crc32s will be "
  333. "checked.\n");
  334. printf(" -v --version Print version and exit.\n");
  335. printf(" -h --help Display this HELP.\n");
  336. printf("\n");
  337. printf(" OPTIONS\n");
  338. printf(" -g --group GROUP_NO List, verify or extract files "
  339. "inside this group-number. Use "
  340. "-l --list to see all files with "
  341. "their group numbers.\n");
  342. printf(" -d --debug Print debug info.\n");
  343. printf(" -s --silent Be silent, don't print anything.\n");
  344. printf("\n");
  345. printf(" NOTES\n");
  346. printf(" - Path to directory OUTPUT_PATH should exist and be writable.\n");
  347. printf(" - All files DiDi Extract does output will be overwritten when "
  348. "they exist!\n");
  349. }
  350. void printPrettySize(size_t size) {
  351. if (size > SIZE_GiB) {
  352. printInfo("%.2f GiB", (float)size / SIZE_GiB);
  353. }
  354. else
  355. if (size > SIZE_MiB) {
  356. printInfo("%.2f MiB", (float)size / SIZE_MiB);
  357. }
  358. else
  359. if (size > SIZE_KiB) {
  360. printInfo("%.2f KiB", (float)size / SIZE_KiB);
  361. }
  362. else {
  363. printInfo("%zu bytes", size);
  364. }
  365. }
  366. bool setPath(const char * optarg, char * dest) {
  367. // Resolve absolute path
  368. char * outputPath = realpath(optarg, dest);
  369. if (outputPath == NULL) {
  370. printError("Invalid PATH given, could not resolve absolute path for "
  371. "'%s'. Errno: %s\n", optarg, strerror(errno));
  372. return false;
  373. }
  374. size_t outputPathLen = strlen(outputPath);
  375. // -2 for the potential '/' we may add
  376. if (outputPathLen >= (MAX_OUTPUT_PATH - 1)) {
  377. printError("Absolute path of PATH is to large.\n");
  378. return false;
  379. }
  380. // Make sure the path ends with a '/'
  381. if (dest[outputPathLen - 1] != '/') {
  382. strcat(dest, "/");
  383. }
  384. // Make sure the path exists
  385. if (access(dest, F_OK) != 0) {
  386. // dest exists but is not a directory
  387. if (errno == ENOTDIR) {
  388. printError("'%s' is not a directory.\n", dest);
  389. return false;
  390. }
  391. }
  392. return true;
  393. }
  394. // Must be a valid pointer to a \0 terminated string.
  395. char * parsePath(char *path, char *dest) {
  396. char * pathCopy;
  397. char * section;
  398. char * returnPath;
  399. uint32_t strSize = 0;
  400. // Basic verification that this string may be a valid path
  401. do {
  402. unsigned char ch = path[strSize];
  403. if (ch == 0x00) {
  404. break;
  405. }
  406. // It contains a illegal character
  407. if (ch < 0x20 || ch > 0x7E || ch == '/') {
  408. printError("parsePath: path contains an illegal character "
  409. "0x%02X\n", ch);
  410. return NULL;
  411. }
  412. strSize++;
  413. // Path is to long
  414. if (strSize > WIN_PATH_MAX) {
  415. printError("parsePath: path is larger then WIN_PATH_MAX\n");
  416. return NULL;
  417. }
  418. } while (1);
  419. // Duplicate the path for the use with strtok
  420. pathCopy = strdup(path);
  421. if (pathCopy == NULL) {
  422. printError("parsePath: errno: %s\n", strerror(errno));
  423. return NULL;
  424. }
  425. dest[0] = 0x00;
  426. section = strtok(pathCopy, "\\");
  427. do {
  428. size_t sectionLen = strlen(section);
  429. if (sectionLen > NAME_MAX) {
  430. printError("parsePath: path component name exceeds NAME_MAX\n");
  431. free(pathCopy);
  432. return NULL;
  433. }
  434. // Don't allow a path section to start with '..'
  435. if (sectionLen >= 2) {
  436. if (section[0] == '.' && section[1] == '.') {
  437. printError("parsePath: path component starts with '..'."
  438. " Symbolic paths are not allowed! Path: '%s'\n", path);
  439. free(pathCopy);
  440. return NULL;
  441. }
  442. }
  443. strcat(dest, "/");
  444. strcat(dest, section);
  445. } while ((section = strtok(NULL, "\\")));
  446. free(pathCopy);
  447. returnPath = strdup(dest + 1); // +1 to remove the first character '/'
  448. if (returnPath == NULL) {
  449. printError("parsePath: errno: %s\n", strerror(errno));
  450. return NULL;
  451. }
  452. return returnPath;
  453. }
  454. bool preparePath(char * basePath, char * subPath, char * dest) {
  455. // Join paths
  456. if ((strlen(basePath) + strlen(subPath) + 1) > PATH_MAX) {
  457. printError("Overflow of final path > PATH_MAX\n");
  458. return false;
  459. }
  460. strcpy(dest, basePath);
  461. strcat(dest, subPath);
  462. // Try to create directories as needed
  463. char * outputFilePath;
  464. char * currentSubPath;
  465. char * separator;
  466. // make a copy which strchr may manipulate.
  467. outputFilePath = strdup(dest);
  468. if (outputFilePath == NULL) {
  469. printError("Errno: %s\n", strerror(errno));
  470. return false;
  471. }
  472. // get the path without filename
  473. currentSubPath = dirname(outputFilePath);
  474. // get the path its root (string until first '/')
  475. separator = strchr(currentSubPath, '/');
  476. // This should not happen because the given path by the user should exist.
  477. if (separator == NULL) {
  478. printError("This should not happen, please report if it does! (1)\n");
  479. return false;
  480. }
  481. // iterate through all sub-directories from root
  482. while (separator != NULL) {
  483. // terminate the dirName string on next occurance of '/'
  484. separator[0] = 0x00;
  485. // do not create root
  486. if (currentSubPath[0] != 0x00) {
  487. // stat currentSubPath
  488. if (access(currentSubPath, F_OK) != 0) {
  489. // currentSubPath exists but is not a directory
  490. if (errno == ENOTDIR) {
  491. printError("Extract subpath '%s' exists but is not a directory!\n",
  492. currentSubPath);
  493. free(outputFilePath);
  494. return false;
  495. }
  496. // currentSubPath does not exist, try to create a new directory
  497. if (errno == ENOENT) {
  498. errno = 0;
  499. if (mkdir(currentSubPath, 0777) != 0) {
  500. printError("Failed to create subpath (1): '%s'\n", currentSubPath);
  501. printError("Errno: %s\n", strerror(errno));
  502. free(outputFilePath);
  503. return false;
  504. }
  505. }
  506. }
  507. }
  508. // reset the previous set terminator
  509. separator[0] = '/';
  510. // set separator to next occurrence of '/' (will be set to NULL when
  511. // there are no more occurrences of '/'.
  512. separator = strchr(separator + 1, '/');
  513. }
  514. // last subdir
  515. if (access(currentSubPath, F_OK) != 0) {
  516. // currentSubPath exists but is not a directory
  517. if (errno == ENOTDIR) {
  518. printError("Extract path '%s' exists but is not a directory!\n",
  519. currentSubPath);
  520. free(outputFilePath);
  521. return false;
  522. }
  523. // currentSubPath does not exist, try to create a new directory
  524. if (errno == ENOENT) {
  525. if (mkdir(currentSubPath, 0777) != 0) {
  526. printError("Failed to create subpath (2): '%s'\n", currentSubPath);
  527. printError("Errno: %s\n", strerror(errno));
  528. free(outputFilePath);
  529. return false;
  530. }
  531. }
  532. }
  533. // cleanup
  534. free(outputFilePath);
  535. return true;
  536. }
  537. DIDIEErr listFiles(FILE *fp, uint32_t count, uint8_t group) {
  538. DiDiFile didiFile;
  539. DIDIEErr status;
  540. size_t totalsize = 0;
  541. uint32_t totalfiles = 0;
  542. printInfo("GRP SIZE NAME\n");
  543. printInfo("--- ---------- ------------\n");
  544. for (uint32_t i = 0; i < count; i++) {
  545. status = readDiDiFile(fp, &didiFile);
  546. if (status != DIDIE_OK) {
  547. fclose(fp);
  548. printError("Failed to read DiDiFile\n");
  549. return status;
  550. }
  551. fseek(fp, didiFile.data_size, SEEK_CUR);
  552. // skip (filtered out by given argument)
  553. if (group != 0xff && group != didiFile.group) {
  554. continue;
  555. }
  556. totalsize += didiFile.uncompressed_size;
  557. totalfiles++;
  558. printInfo("%3d %10u %s\n", didiFile.group, didiFile.uncompressed_size, didiFile.filename);
  559. }
  560. printInfo("---------------------------\n");
  561. printInfo("%d files (%ul bytes aka ", totalfiles, totalsize);
  562. printPrettySize(totalsize);
  563. printInfo(")\n");
  564. return DIDIE_OK;
  565. }
  566. DIDIEErr extractFiles(FILE *fp, uint32_t count, uint8_t group, bool onlycheck) {
  567. DiDiFile didiFile;
  568. uint8_t curgroup; // Current file group
  569. DIDIEErr status = DIDIE_OK;
  570. int zstatus;
  571. z_stream strm;
  572. char filepath[WIN_PATH_MAX];
  573. char finalFilepath[PATH_MAX];
  574. // Largest group number I've seen is 10 (0x0A), so we reserve 0xff for
  575. // when the group is unset by us.
  576. curgroup = 0xff;
  577. for (uint32_t i = 0; i < count; i++) {
  578. status = readDiDiFile(fp, &didiFile);
  579. if (status != DIDIE_OK) {
  580. printError("Failed to read DiDiFile\n");
  581. break;
  582. }
  583. // handle file groups
  584. if (didiFile.group != curgroup) {
  585. (void)inflateEnd(&strm);
  586. // allocate inflate state
  587. strm.zalloc = Z_NULL;
  588. strm.zfree = Z_NULL;
  589. strm.opaque = Z_NULL;
  590. strm.avail_in = 0;
  591. strm.next_in = Z_NULL;
  592. zstatus = inflateInit(&strm);
  593. if (zstatus != Z_OK) {
  594. status = DIDIE_ERR_OMEM;
  595. break;
  596. }
  597. curgroup = didiFile.group;
  598. }
  599. // skip (filtered out by given arguments)
  600. if (group != 0xff && group != didiFile.group) {
  601. fseek(fp, didiFile.data_size, SEEK_CUR);
  602. continue;
  603. }
  604. printInfo("%s\n", didiFile.filename);
  605. // When outputFile == NULL, it will not output any files.
  606. FILE *outputFile = NULL;
  607. // Do output files
  608. if (!onlycheck) {
  609. // Parse WIN32 filepath
  610. if (parsePath((char *)didiFile.filename, filepath) == NULL) {
  611. printError("Failed to parse win32 filepath.\n");
  612. status = DIDIE_ERR_PATH;
  613. break;
  614. }
  615. // Create 'finalFilepath' and make sure directories for that exist,
  616. // create them when needed to.
  617. if (!preparePath(OutputPath, filepath, finalFilepath)) {
  618. status = DIDIE_ERR_PATH;
  619. break;
  620. }
  621. outputFile = fopen(finalFilepath, "w");
  622. }
  623. // Read data chunk sizes
  624. uint16_t chunkcount = 0;
  625. uint32_t chunkstotal = 0;
  626. uint16_t chunksizes[DIDI_MAX_CHUNK_COUNT];
  627. while (chunkstotal < didiFile.data_size) {
  628. status = readUInt16(fp, &chunksizes[chunkcount]);
  629. if (status != DIDIE_OK) {
  630. break;
  631. }
  632. chunkstotal += chunksizes[chunkcount] + 2;
  633. chunkcount++;
  634. if (chunkcount > DIDI_MAX_CHUNK_COUNT) {
  635. printError("Overflow: chunkcount > %d\n", DIDI_MAX_CHUNK_COUNT);
  636. status = DIDIE_ERR_DIDI;
  637. break;
  638. }
  639. }
  640. // Reading data chunk sizes failed.
  641. if (status != DIDIE_OK) {
  642. break;
  643. }
  644. for (uint32_t i = 0; i < chunkcount; ++i) {
  645. // read CRC32
  646. uint32_t filecrc32;
  647. status = readUInt32(fp, &filecrc32);
  648. if (status != DIDIE_OK) {
  649. break;
  650. }
  651. // Inflate
  652. // NOTE: -4 for the CRC32
  653. status = inflateFile(fp, outputFile, chunksizes[i] - 4, &strm, filecrc32);
  654. if (status != DIDIE_OK) {
  655. // Errors should be printed.
  656. break;
  657. }
  658. }
  659. // Close output file
  660. if (outputFile != NULL) {
  661. fclose(outputFile);
  662. }
  663. // Inflate failed or CRC32 mismatch.
  664. if (status != DIDIE_OK) {
  665. break;
  666. }
  667. }
  668. if (curgroup != 0xff) {
  669. (void)inflateEnd(&strm);
  670. }
  671. return status;
  672. }
  673. DIDIEErr extractDoofus(FILE *fp) {
  674. DoofusHeader doofus;
  675. DIDIEErr status;
  676. FILE *outputFile;
  677. int zstatus;
  678. z_stream strm;
  679. char finalFilepath[PATH_MAX];
  680. status = readDoofusHeader(fp, &doofus);
  681. if (status != DIDIE_OK) {
  682. printError("Failed to read DoofusHeader\n");
  683. return DIDIE_ERR_DIDI;
  684. }
  685. printDebug("-DOOFUS- Header\n");
  686. printDebug("---------------------------------------------------\n");
  687. printDebug("Magic : %.8s\n", doofus.magic);
  688. printDebug("uncompressed size: %d\n", doofus.uncompressed_size);
  689. printDebug("data_size : %d\n", doofus.data_size);
  690. printDebug("CRC32 : %08X\n", doofus.crc32);
  691. printDebug("CRC32 : %08X\n", ~doofus.crc32);
  692. // allocate inflate state
  693. strm.zalloc = Z_NULL;
  694. strm.zfree = Z_NULL;
  695. strm.opaque = Z_NULL;
  696. strm.avail_in = 0;
  697. strm.next_in = Z_NULL;
  698. zstatus = inflateInit(&strm);
  699. if (zstatus != Z_OK) {
  700. return DIDIE_ERR_OMEM;
  701. }
  702. if (!preparePath(OutputPath, (char*)"doofus.bin", finalFilepath)) {
  703. return DIDIE_ERR_PATH;
  704. }
  705. outputFile = fopen(finalFilepath, "w");
  706. status = inflateFile(fp, outputFile, doofus.data_size, &strm, doofus.crc32);
  707. fclose(outputFile);
  708. return status;
  709. }
  710. int main(int argc, char *argv[]) {
  711. char inputFile[PATH_MAX];
  712. long overlayOffset;
  713. FILE * fp;
  714. uint8_t group = 0xff;
  715. enum Operation operation = OP_NONE;
  716. inputFile[0] = 0x00;
  717. // https://www.gnu.org/software/libc/manual/html_node/Getopt-Long-Options.html
  718. // https://www.gnu.org/software/libc/manual/html_node/Getopt-Long-Option-Example.html
  719. struct option long_options[] = {
  720. // OPERATIONS
  721. {"extract" , required_argument, NULL, 'x'},
  722. {"extract-doofus", required_argument, NULL, 'y'},
  723. {"list" , no_argument , NULL, 'l'},
  724. {"verify" , no_argument , NULL, 'V'},
  725. {"version" , no_argument , NULL, 'v'},
  726. {"help" , no_argument , NULL, 'h'},
  727. // OPTIONS
  728. {"group" , required_argument, NULL, 'g'},
  729. {"debug" , no_argument , NULL, 'd'},
  730. {"silent" , no_argument , NULL, 's'},
  731. {NULL , 0 , NULL, 0}
  732. };
  733. int option_index = 0;
  734. for (;;) {
  735. int opt = getopt_long(argc, argv, "x:y:g:lhdsVv",
  736. long_options, &option_index);
  737. if (opt == -1) {
  738. break;
  739. }
  740. switch (opt) {
  741. // OPERATIONS
  742. case 'x':
  743. {
  744. if (operation != OP_NONE) {
  745. printError("More then one operation is set! Do set only one.\n");
  746. return 1;
  747. }
  748. operation = OP_EXTRACT;
  749. if (setPath(optarg, OutputPath) == false) {
  750. return 1;
  751. }
  752. }
  753. break;
  754. case 'y':
  755. if (operation != OP_NONE) {
  756. printError("More then one operation is set! Do set only one.\n");
  757. return 1;
  758. }
  759. operation = OP_EXTRACT_DOOFUS;
  760. if (setPath(optarg, OutputPath) == false) {
  761. return 1;
  762. }
  763. break;
  764. case 'l':
  765. if (operation != OP_NONE) {
  766. printError("More then one operation is set! Do set only one.\n");
  767. return 1;
  768. }
  769. operation = OP_LIST;
  770. break;
  771. case 'V':
  772. if (operation != OP_NONE) {
  773. printError("More then one operation is set! Do set only one.\n");
  774. return 1;
  775. }
  776. operation = OP_VERIFY;
  777. break;
  778. case 'v':
  779. printf("DiDi Extract v%s\n", DIDIE_VERSION_STR);
  780. return 0;
  781. case 'h':
  782. printHelp();
  783. return 0;
  784. // OPTIONS
  785. case 'g':
  786. {
  787. char *str;
  788. unsigned long val = strtoul(optarg, &str, 10);
  789. if (val > 0xfe) {
  790. printError("Invalid group: number to large\n");
  791. return 1;
  792. }
  793. if (strlen(str)) {
  794. printError("Invalid group: string not allowed\n");
  795. return 1;
  796. }
  797. group = (uint8_t)val;
  798. }
  799. break;
  800. case 'd':
  801. setPrintFlag(PRINT_DEBUG);
  802. break;
  803. case 's':
  804. setPrintFlags(PRINT_SILENT);
  805. break;
  806. case '?':
  807. // invalid option
  808. printError("Invalid operation or option\n");
  809. return 1;
  810. default:
  811. printError("default\n");
  812. break;
  813. }
  814. }
  815. if ((argc - 1 ) < optind) {
  816. printError("Please supply a input file\n");
  817. return 1;
  818. }
  819. if ((argc - 1 ) > optind) {
  820. printError("Please supply only one input file\n");
  821. return 1;
  822. }
  823. if (strlen(argv[optind]) > (PATH_MAX - 1)) {
  824. printError("What are you trying to do? INPUT_FILE is larger then PATH_MAX\n");
  825. return 1;
  826. }
  827. strcpy(inputFile, argv[optind]);
  828. if (operation == OP_NONE) {
  829. printError("Please specify a operation.\n");
  830. return 1;
  831. }
  832. // Check if input file exists
  833. if (access(inputFile, F_OK) != 0) {
  834. printError("InputFile '%s' not found. Errno: %s\n", inputFile,
  835. strerror(errno));
  836. return 1;
  837. }
  838. // Get offset to overlay data
  839. overlayOffset = pefileGetOverlayOffset(inputFile);
  840. if (overlayOffset == -1) {
  841. printError("Failed to find overlay offset.\n", inputFile);
  842. return 1;
  843. }
  844. // Open inputFile
  845. fp = fopen(inputFile, "rb");
  846. if (fp == NULL) {
  847. printError("Failed to open inputFile '%s'\n", inputFile);
  848. printError("Errno: %s\n", strerror(errno));
  849. return 1;
  850. };
  851. // Seek to overlayData
  852. if (fseek(fp, overlayOffset, SEEK_SET) != 0) {
  853. printError("Failed to seek to overlayData. Offset: 0x%08X\n", overlayOffset);
  854. printError("Errno: %s\n", strerror(errno));
  855. fclose(fp);
  856. return 1;
  857. }
  858. DiDiHeader didiHeader;
  859. DIDIEErr status;
  860. status = readDiDiHeader(fp, &didiHeader);
  861. if (status != DIDIE_OK) {
  862. fclose(fp);
  863. printError("Failed to read DiDiHeader\n");
  864. return status;
  865. }
  866. printDebug("Overlay offset : %d\n", overlayOffset);
  867. printDebug("DIDI Header\n");
  868. printDebug("---------------------------------------------------\n");
  869. printDebug("Magic : %.4s\n", didiHeader.magic);
  870. printDebug("Signature : 0x%08X\n", didiHeader.signature);
  871. printDebug("Filecount : %d\n", didiHeader.filecount);
  872. printDebug("Unknown 1 : %08X %d\n", didiHeader.unknown_1, didiHeader.unknown_1);
  873. printDebug("Unknown 2 : %08X %d\n", didiHeader.unknown_2, didiHeader.unknown_2);
  874. printDebug("filesize : %08X %d\n", didiHeader.filesize, didiHeader.filesize);
  875. printDebug("Unknown 3 : %08X %d\n", didiHeader.unknown_3, didiHeader.unknown_3);
  876. printDebug("Uncompressed size: %08X %d\n", didiHeader.uncompressed_size, didiHeader.uncompressed_size);
  877. printDebug("Unknown 4 : %08X %d\n", didiHeader.unknown_4, didiHeader.unknown_4);
  878. printDebug("DOOFUS offset : %08X %d\n", didiHeader.doofus_offset, didiHeader.doofus_offset);
  879. if (operation == OP_LIST) {
  880. status = listFiles(fp, didiHeader.filecount, group);
  881. }
  882. else
  883. if (operation == OP_EXTRACT) {
  884. status = extractFiles(fp, didiHeader.filecount, group, false);
  885. }
  886. else
  887. if (operation == OP_VERIFY) {
  888. status = extractFiles(fp, didiHeader.filecount, group, true);
  889. }
  890. else
  891. if (operation == OP_EXTRACT_DOOFUS) {
  892. fseek(fp, overlayOffset + didiHeader.doofus_offset, SEEK_SET);
  893. status = extractDoofus(fp);
  894. }
  895. fclose(fp);
  896. return status;
  897. }