jjsave.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. /* This file is part of JJSave.
  2. *
  3. * JJSave 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. * JJSave 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 <stdio.h>
  17. #include <stdint.h>
  18. #include <string.h>
  19. #include <getopt.h>
  20. #include <stdlib.h> // strtoul
  21. #include <errno.h>
  22. #include "version.h"
  23. #define DATA_MULTIPLIER 0x0100 // 256 (16 * 16) | (8 * 32) 00100000000
  24. #define OFFSET_MULTIPLIER 0x07D0 // 2000 (16 * 125) | (8 * 250) 11111010000
  25. enum Operation {
  26. OP_NONE = 0,
  27. OP_READ = 1,
  28. OP_WRITE = 2
  29. };
  30. enum Difficulty {
  31. DIFFICULTY_EASY = 0,
  32. DIFFICULTY_MEDIUM = 1,
  33. DIFFICULTY_HARD = 2,
  34. DIFFICULTY_TURBO = 3
  35. };
  36. struct CipherState {
  37. uint16_t key1, key2;
  38. };
  39. // This will generate a new keys state, what the code original looked like is
  40. // hard to guess but this is doing the same as the original, it is translated
  41. // from assembly. I failed to recognize any existing cipher, but I'm also no
  42. // expert. (Did Borland products have compiler optimisations in 1994?)
  43. //
  44. // However the basic idea is to multiply the first key with a constant, do
  45. // some mangling/math on high parts and low parts to calculate the next keys
  46. // (random bytes). During the calculations bytes will overflow, this is part
  47. // of the process.
  48. void gen_new_keys(struct CipherState *result, uint16_t key1, uint16_t key2) {
  49. uint16_t val;
  50. uint32_t buf;
  51. #ifdef JJSAVE_DEBUG
  52. printf(" --> result->key1: %04X\n", result->key1);
  53. printf(" --> result->key2: %04X\n", result->key2);
  54. #endif
  55. buf = key1 * 0x8405;
  56. #ifdef JJSAVE_DEBUG
  57. printf(" buf : %08X\n", buf);
  58. #endif
  59. val = (uint16_t)(buf >> 16) + (key1 * 2056) + (key2 * 5);
  60. result->key1 = ((val >> 8) + ((uint8_t)key2 + (key2 << 5)) * 4) * 256 | (uint8_t)val;
  61. result->key2 = (uint16_t)buf + 1;
  62. #ifdef JJSAVE_DEBUG
  63. printf(" <-- result->key1: %04X\n", result->key1);
  64. printf(" <-- result->key2: %04X\n", result->key2);
  65. #endif
  66. return;
  67. }
  68. uint16_t finalize_key(uint16_t key, uint16_t multiplier) {
  69. uint32_t buf = key * multiplier;
  70. return (uint16_t)(buf >> 16);
  71. }
  72. struct SaveGame {
  73. unsigned char name[16];
  74. uint16_t level;
  75. uint16_t planet;
  76. uint16_t difficulty;
  77. uint16_t unknown_1;
  78. uint16_t unknown_2;
  79. uint16_t unknown_3;
  80. uint16_t unknown_4;
  81. uint16_t unknown_5;
  82. uint16_t unknown_6;
  83. };
  84. void savegame_init(struct SaveGame *sg) {
  85. // init name with all spaces
  86. memset(sg->name, 0x20, 16);
  87. sg->level = 0;
  88. sg->planet = 0;
  89. sg->difficulty = 0;
  90. sg->unknown_1 = 0;
  91. sg->unknown_2 = 0;
  92. sg->unknown_3 = 0;
  93. sg->unknown_4 = 0;
  94. sg->unknown_5 = 0;
  95. sg->unknown_6 = 0;
  96. }
  97. void savegame_print(struct SaveGame *sg) {
  98. printf("Name : \"%.16s\"\n", sg->name);
  99. printf("Planet : %5d\n", sg->planet);
  100. printf("Level : %5d\n", sg->level);
  101. printf("Difficulty: %5d\n", sg->difficulty);
  102. printf("Unknown 1 : %5d 0x%04X\n", sg->unknown_1, sg->unknown_1);
  103. printf("Unknown 2 : %5d 0x%04X\n", sg->unknown_2, sg->unknown_2);
  104. printf("Unknown 3 : %5d 0x%04X\n", sg->unknown_3, sg->unknown_3);
  105. printf("Unknown 4 : %5d 0x%04X\n", sg->unknown_4, sg->unknown_4);
  106. printf("Unknown 5 : %5d 0x%04X\n", sg->unknown_5, sg->unknown_5);
  107. printf("Unknown 6 : %5d 0x%04X\n", sg->unknown_6, sg->unknown_6);
  108. }
  109. // Reads next value from file and returns the deciphered value, updating the
  110. // keys state while doing so.
  111. uint16_t read_value(struct CipherState *keys, FILE *fp, uint16_t *to) {
  112. struct CipherState result;
  113. uint16_t enc_value;
  114. uint16_t key;
  115. gen_new_keys(&result, keys->key2, keys->key1);
  116. keys->key1 = result.key1;
  117. keys->key2 = result.key2;
  118. key = finalize_key(result.key1, DATA_MULTIPLIER);
  119. if (fread(&enc_value, sizeof(enc_value), 1, fp) != 1) {
  120. fclose(fp);
  121. return 1;
  122. }
  123. *to = enc_value ^ key;
  124. return 0;
  125. }
  126. // Read from SAVE file and decipher save game into struct SaveGame.
  127. // Returns:
  128. // 0 ok success
  129. // 1 on file errors
  130. // 2 on magic error
  131. uint8_t savegame_read(struct SaveGame *sg, const char *savefile) {
  132. uint8_t magic;
  133. uint16_t data_offset;
  134. uint16_t key;
  135. FILE *fp;
  136. struct CipherState next_keys;
  137. fp = fopen(savefile, "rb");
  138. if (!fp) {
  139. return 1;
  140. }
  141. // Read save name
  142. if (fread(sg->name, sizeof(unsigned char) * 16, 1, fp) != 1) {
  143. fclose(fp);
  144. return 1;
  145. }
  146. // Read and check magic
  147. if (fread(&magic, sizeof(magic), 1, fp) != 1) {
  148. fclose(fp);
  149. return 1;
  150. }
  151. if (magic != 0x1A) {
  152. fclose(fp);
  153. return 2;
  154. }
  155. // Read keys
  156. if (fread(&next_keys.key2, sizeof(next_keys.key2), 1, fp) != 1) {
  157. fclose(fp);
  158. return 1;
  159. }
  160. if (fread(&next_keys.key1, sizeof(next_keys.key1), 1, fp) != 1) {
  161. fclose(fp);
  162. return 1;
  163. }
  164. #ifdef JJSAVE_DEBUG
  165. printf("Keys : 0x%04X 0x%04X\n", next_keys.key1, next_keys.key2);
  166. #endif
  167. gen_new_keys(&next_keys, next_keys.key2, next_keys.key1);
  168. key = finalize_key(next_keys.key1, OFFSET_MULTIPLIER);
  169. data_offset = key + 21;
  170. #ifdef JJSAVE_DEBUG
  171. printf("Data offset: %d = 0x%04X\n", data_offset, data_offset);
  172. #endif
  173. // Seek to data offset
  174. if (fseek(fp, (long)data_offset, SEEK_SET) != 0) {
  175. fclose(fp);
  176. return 1;
  177. }
  178. // Read keys
  179. if (fread(&next_keys.key2, sizeof(next_keys.key2), 1, fp) != 1) {
  180. fclose(fp);
  181. return 1;
  182. }
  183. if (fread(&next_keys.key1, sizeof(next_keys.key1), 1, fp) != 1) {
  184. fclose(fp);
  185. return 1;
  186. }
  187. #ifdef JJSAVE_DEBUG
  188. printf("Keys : 0x%04X 0x%04X\n", next_keys.key1, next_keys.key2);
  189. #endif
  190. // NOTE: read_value() does call fclose(fp) on error.
  191. if (read_value(&next_keys, fp, &sg->level) != 0) {
  192. return 1;
  193. }
  194. if (read_value(&next_keys, fp, &sg->planet) != 0) {
  195. return 1;
  196. }
  197. if (read_value(&next_keys, fp, &sg->difficulty) != 0) {
  198. return 1;
  199. }
  200. if (read_value(&next_keys, fp, &sg->unknown_1) != 0) {
  201. return 1;
  202. }
  203. if (read_value(&next_keys, fp, &sg->unknown_2) != 0) {
  204. return 1;
  205. }
  206. if (read_value(&next_keys, fp, &sg->unknown_3) != 0) {
  207. return 1;
  208. }
  209. if (read_value(&next_keys, fp, &sg->unknown_4) != 0) {
  210. return 1;
  211. }
  212. if (read_value(&next_keys, fp, &sg->unknown_5) != 0) {
  213. return 1;
  214. }
  215. if (read_value(&next_keys, fp, &sg->unknown_6) != 0) {
  216. return 1;
  217. }
  218. fclose(fp);
  219. return 0;
  220. }
  221. // Used for value generation based of current keys state.
  222. uint16_t read_next_value(struct CipherState *next_keys) {
  223. gen_new_keys(next_keys, next_keys->key2, next_keys->key1);
  224. return finalize_key(next_keys->key1, DATA_MULTIPLIER);
  225. }
  226. // Write SAVE file
  227. uint8_t savegame_write(struct SaveGame *sg, uint32_t systime, const char *savefile) {
  228. FILE *fp;
  229. uint16_t value;
  230. uint16_t key;
  231. struct CipherState next_keys;
  232. next_keys.key2 = (uint16_t)(systime >> 16);
  233. next_keys.key1 = (uint16_t)systime;
  234. fp = fopen(savefile, "wb");
  235. if (!fp) {
  236. return 1;
  237. }
  238. // write savegame name
  239. if (fwrite(sg->name, 16, 1, fp) != 1) {
  240. fclose(fp);
  241. return 1;
  242. }
  243. // write magic
  244. if (putc(0x1A, fp) != 0x1A) {
  245. fclose(fp);
  246. return 1;
  247. }
  248. // write system time (the secret start keys)
  249. if (fwrite(&next_keys.key2, sizeof(next_keys.key2), 1, fp) != 1) {
  250. fclose(fp);
  251. return 1;
  252. }
  253. if (fwrite(&next_keys.key1, sizeof(next_keys.key1), 1, fp) != 1) {
  254. fclose(fp);
  255. return 1;
  256. }
  257. // generate the number of bytes to skip (garbage bytes)
  258. gen_new_keys(&next_keys, next_keys.key2, next_keys.key1);
  259. key = finalize_key(next_keys.key1, OFFSET_MULTIPLIER);
  260. #ifdef JJSAVE_DEBUG
  261. printf("Garbage bytes: %d %04X\n", key, key);
  262. #endif
  263. uint16_t nogarbage = key;
  264. // generate/write garbage bytes
  265. for (uint16_t i=0; i<nogarbage; ++i) {
  266. gen_new_keys(&next_keys, next_keys.key2, next_keys.key1);
  267. key = finalize_key(next_keys.key1, DATA_MULTIPLIER);
  268. value = (uint8_t)(key >> 8);
  269. if (fwrite(&value, sizeof(uint8_t), 1, fp) != 1) {
  270. fclose(fp);
  271. return 1;
  272. }
  273. }
  274. #ifdef JJSAVE_DEBUG
  275. printf("----------------------------\n");
  276. printf("New keys: %04X %04X\n", next_keys.key2, next_keys.key1);
  277. #endif
  278. // write keys (so on read we can skip the garbage and start of with the
  279. // values we left with)
  280. if (fwrite(&next_keys.key2, sizeof(next_keys.key2), 1, fp) != 1) {
  281. fclose(fp);
  282. return 1;
  283. }
  284. if (fwrite(&next_keys.key1, sizeof(next_keys.key1), 1, fp) != 1) {
  285. fclose(fp);
  286. return 1;
  287. }
  288. // level
  289. value = read_next_value(&next_keys)^ sg->level;
  290. if (fwrite(&value, sizeof(value), 1, fp) != 1) {
  291. fclose(fp);
  292. return 1;
  293. }
  294. // planet
  295. value = read_next_value(&next_keys)^ sg->planet;
  296. if (fwrite(&value, sizeof(value), 1, fp) != 1) {
  297. fclose(fp);
  298. return 1;
  299. }
  300. // difficulty
  301. value = read_next_value(&next_keys)^ sg->difficulty;
  302. if (fwrite(&value, sizeof(value), 1, fp) != 1) {
  303. fclose(fp);
  304. return 1;
  305. }
  306. // unknown 1
  307. value = read_next_value(&next_keys)^ sg->unknown_1;
  308. if (fwrite(&value, sizeof(value), 1, fp) != 1) {
  309. fclose(fp);
  310. return 1;
  311. }
  312. // unknown 2
  313. value = read_next_value(&next_keys)^ sg->unknown_2;
  314. if (fwrite(&value, sizeof(value), 1, fp) != 1) {
  315. fclose(fp);
  316. return 1;
  317. }
  318. // unknown 3
  319. value = read_next_value(&next_keys)^ sg->unknown_3;
  320. if (fwrite(&value, sizeof(value), 1, fp) != 1) {
  321. fclose(fp);
  322. return 1;
  323. }
  324. // unknown 4
  325. value = read_next_value(&next_keys)^ sg->unknown_4;
  326. if (fwrite(&value, sizeof(value), 1, fp) != 1) {
  327. fclose(fp);
  328. return 1;
  329. }
  330. // unknown 5
  331. value = read_next_value(&next_keys)^ sg->unknown_5;
  332. if (fwrite(&value, sizeof(value), 1, fp) != 1) {
  333. fclose(fp);
  334. return 1;
  335. }
  336. // unknown 6
  337. value = read_next_value(&next_keys)^ sg->unknown_6;
  338. if (fwrite(&value, sizeof(value), 1, fp) != 1) {
  339. fclose(fp);
  340. return 1;
  341. }
  342. fclose(fp);
  343. return 0;
  344. }
  345. void print_help(void) {
  346. printf("==============================================================\n");
  347. printf(" Welcome to JJ Save version %s\n", JJSAVE_VERSION_STR);
  348. printf("==============================================================\n\n");
  349. printf(" Usage: jjsave [OPERATION]\n\n");
  350. printf(" OPERATIONS\n\n");
  351. printf(" -r --read INPUT_FILE Read SAVE file. (default behaviour)\n\n");
  352. printf(" -w --write Create new SAVE file.\n");
  353. printf(" NAME Save game name, max 16 characters.\n");
  354. printf(" PLANET Planet ID.\n");
  355. printf(" LEVEL Level number.\n");
  356. printf(" DIFFICULTY Difficulty (0-3)\n");
  357. printf(" OUTPUT_FILE File to write the output to.\n\n");
  358. printf(" -v --version Print version and exit.\n");
  359. printf(" -h --help Display this HELP.\n");
  360. printf("\n");
  361. }
  362. int main(int argc, char *argv[]) {
  363. enum Operation operation = OP_READ;
  364. struct SaveGame sg;
  365. savegame_init(&sg);
  366. // https://www.gnu.org/software/libc/manual/html_node/Getopt-Long-Options.html
  367. // https://www.gnu.org/software/libc/manual/html_node/Getopt-Long-Option-Example.html
  368. struct option long_options[] = {
  369. // OPERATIONS
  370. {"write" , no_argument , NULL, 'w'},
  371. {"version", no_argument , NULL, 'v'},
  372. {"help" , no_argument , NULL, 'h'},
  373. {NULL , 0 , NULL, 0}
  374. };
  375. int option_index = 0;
  376. for (;;) {
  377. int opt = getopt_long(argc, argv, "rwhv",
  378. long_options, &option_index);
  379. if (opt == -1) {
  380. break;
  381. }
  382. switch (opt) {
  383. // OPERATIONS
  384. /*case 'r':
  385. operation = OP_READ;
  386. break;*/
  387. case 'w':
  388. operation = OP_WRITE;
  389. break;
  390. case 'v':
  391. printf("JJ Save v%s\n", JJSAVE_VERSION_STR);
  392. return 0;
  393. case 'h':
  394. print_help();
  395. return 0;
  396. case '?':
  397. // invalid option
  398. printf("Invalid operation or option\n");
  399. return 1;
  400. default:
  401. printf("default\n");
  402. break;
  403. }
  404. }
  405. // Read
  406. if (operation == OP_READ) {
  407. if ((argc - 1 ) < optind) {
  408. fprintf(stderr, "Please supply an input file\n");
  409. return 1;
  410. }
  411. if ((argc - 1 ) > optind) {
  412. fprintf(stderr, "Please supply only one input file\n");
  413. return 1;
  414. }
  415. // 0 ok success
  416. // 1 on file errors
  417. // 2 on magic error
  418. uint8_t result = savegame_read(&sg, (const char *)argv[optind]);
  419. if (result == 1) {
  420. perror("Error reading file");
  421. return 1;
  422. }
  423. else
  424. if (result == 2) {
  425. fprintf(stderr, "Not a valid SAVE file (magic is not 0x1A)\n");
  426. return 2;
  427. }
  428. savegame_print(&sg);
  429. }
  430. // Write
  431. else
  432. if (operation == OP_WRITE) {
  433. if ((argc - 5 ) < optind) {
  434. printf("Please supply NAME PLANET LEVEL DIFFICULTY OUTPUT_FILE\n");
  435. return 1;
  436. }
  437. if ((argc - 5 ) > optind) {
  438. printf("Invalid arguments\n");
  439. return 1;
  440. }
  441. // name
  442. size_t namelen = strlen(argv[optind]);
  443. if (namelen > 16) {
  444. printf("Given save name is to long, it may one by 16 characters long.\n");
  445. return 1;
  446. }
  447. memcpy(sg.name, argv[optind], namelen);
  448. char *str;
  449. unsigned long val;
  450. // planet
  451. val = strtoul(argv[optind + 1], &str, 10);
  452. if (val > 0xffff) {
  453. printf("Invalid planet: number to large\n");
  454. return 1;
  455. }
  456. if (strlen(str)) {
  457. printf("Invalid planet: string not allowed\n");
  458. return 1;
  459. }
  460. sg.planet = (uint16_t)val;
  461. // level
  462. val = strtoul(argv[optind + 2], &str, 10);
  463. if (val > 0xffff) {
  464. printf("Invalid level: number to large\n");
  465. return 1;
  466. }
  467. if (strlen(str)) {
  468. printf("Invalid level: string not allowed\n");
  469. return 1;
  470. }
  471. sg.level = (uint16_t)val;
  472. // level
  473. val = strtoul(argv[optind + 3], &str, 10);
  474. if (val > 3) {
  475. printf("Invalid difficulty: number to large\n");
  476. return 1;
  477. }
  478. if (strlen(str)) {
  479. printf("Invalid difficulty: string not allowed\n");
  480. return 1;
  481. }
  482. sg.difficulty = (uint16_t)val;
  483. savegame_print(&sg);
  484. if (savegame_write(&sg, 0x00000000, argv[optind + 4]) != 0) {
  485. fprintf(stderr, "Failed to write save game to \"%s\"\n", argv[optind + 4]);
  486. return 1;
  487. }
  488. printf("Wrote to %s\n", argv[optind + 4]);
  489. }
  490. return 0;
  491. }