legacy_world.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014
  1. /* MegaZeux
  2. *
  3. * Copyright (C) 1996 Greg Janson
  4. * Copyright (C) 1999 Charles Goetzman
  5. * Copyright (C) 2004 Gilead Kutnick <exophase@adelphia.net>
  6. * Copyright (C) 2017 Alice Rowan <petrifiedrowan@gmail.com>
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License as
  10. * published by the Free Software Foundation; either version 2 of
  11. * the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  21. */
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25. #include <sys/stat.h>
  26. #include "legacy_world.h"
  27. #include "legacy_board.h"
  28. #include "legacy_robot.h"
  29. #include "const.h"
  30. #include "counter.h"
  31. #include "error.h"
  32. #include "extmem.h"
  33. #include "fsafeopen.h"
  34. #include "graphics.h"
  35. #include "idput.h"
  36. #include "robot.h"
  37. #include "sprite.h"
  38. #include "str.h"
  39. #include "window.h"
  40. #include "world.h"
  41. #include "util.h"
  42. #include "audio/sfx.h"
  43. #ifndef CONFIG_LOADSAVE_METER
  44. static inline void meter_update_screen(int *curr, int target) {}
  45. static inline void meter_restore_screen(void) {}
  46. static inline void meter_initial_draw(int curr, int target, const char *title) {}
  47. #endif //!CONFIG_LOADSAVE_METER
  48. static char name_buffer[ROBOT_MAX_TR];
  49. static inline boolean legacy_load_counter(struct world *mzx_world,
  50. FILE *fp, struct counter_list *counter_list, int index)
  51. {
  52. int value = fgetd(fp);
  53. int name_length = fgetd(fp);
  54. name_buffer[0] = 0;
  55. if(name_length && !fread(name_buffer, name_length, 1, fp))
  56. return false;
  57. // Stupid legacy hacks
  58. if(!strncasecmp(name_buffer, "mzx_speed", name_length))
  59. {
  60. mzx_world->mzx_speed = value;
  61. return false;
  62. }
  63. if(!strncasecmp(name_buffer, "_____lock_speed", name_length))
  64. {
  65. mzx_world->lock_speed = value;
  66. return false;
  67. }
  68. load_new_counter(counter_list, index, name_buffer, name_length, value);
  69. return true;
  70. }
  71. static inline void legacy_load_string(FILE *fp,
  72. struct string_list *string_list, int index)
  73. {
  74. int name_length = fgetd(fp);
  75. int str_length = fgetd(fp);
  76. struct string *src_string;
  77. name_buffer[0] = 0;
  78. if(name_length && !fread(name_buffer, name_length, 1, fp))
  79. return;
  80. src_string = load_new_string(string_list, index,
  81. name_buffer, name_length, str_length);
  82. if(str_length && !fread(src_string->value, str_length, 1, fp))
  83. return;
  84. }
  85. static const char magic_code[16] =
  86. "\xE6\x52\xEB\xF2\x6D\x4D\x4A\xB7\x87\xB2\x92\x88\xDE\x91\x24";
  87. #define MAX_PASSWORD_LENGTH 15
  88. static int get_pw_xor_code(char *password, int pro_method)
  89. {
  90. int work = 85; // Start with 85... (01010101)
  91. size_t i;
  92. // Clear pw after first null
  93. for(i = strlen(password); i < MAX_PASSWORD_LENGTH; i++)
  94. {
  95. password[i] = 0;
  96. }
  97. for(i = 0; i < MAX_PASSWORD_LENGTH; i++)
  98. {
  99. //For each byte, roll once to the left and xor in pw byte if it
  100. //is an odd character, or add in pw byte if it is an even character.
  101. work <<= 1;
  102. if(work > 255)
  103. work ^= 257; // Wraparound from roll
  104. if(i & 1)
  105. {
  106. work += (signed char)password[i]; // Add (even byte)
  107. if(work > 255)
  108. work ^= 257; // Wraparound from add
  109. }
  110. else
  111. {
  112. work ^= (signed char)password[i]; // XOR (odd byte);
  113. }
  114. }
  115. // To factor in protection method, add it in and roll one last time
  116. work += pro_method;
  117. if(work > 255)
  118. work ^= 257;
  119. work <<= 1;
  120. if(work > 255)
  121. work ^= 257;
  122. // Can't be 0-
  123. if(work == 0)
  124. work = 86; // (01010110)
  125. // Done!
  126. return work;
  127. }
  128. static void decrypt(const char *file_name)
  129. {
  130. FILE *source;
  131. FILE *backup;
  132. FILE *dest;
  133. int file_length;
  134. int pro_method;
  135. int i;
  136. int len;
  137. char num_boards;
  138. char offset_low_byte;
  139. char xor_val;
  140. char password[MAX_PASSWORD_LENGTH + 1];
  141. char *file_buffer;
  142. char *src_ptr;
  143. char backup_name[MAX_PATH];
  144. int count;
  145. int meter_target, meter_curr = 0;
  146. source = fopen_unsafe(file_name, "rb");
  147. file_length = ftell_and_rewind(source);
  148. meter_target = file_length + (file_length - 15) + 4;
  149. meter_initial_draw(meter_curr, meter_target, "Decrypting...");
  150. file_buffer = cmalloc(file_length);
  151. src_ptr = file_buffer;
  152. count = fread(file_buffer, file_length, 1, source);
  153. fclose(source);
  154. if(!count)
  155. goto err_free;
  156. meter_curr = file_length - 1;
  157. meter_update_screen(&meter_curr, meter_target);
  158. src_ptr += 25;
  159. strncpy(backup_name, file_name, MAX_PATH - 8);
  160. strcat(backup_name, ".locked");
  161. backup = fopen_unsafe(backup_name, "wb");
  162. count = fwrite(file_buffer, file_length, 1, backup);
  163. fclose(backup);
  164. if(!count)
  165. goto err_free;
  166. dest = fopen_unsafe(file_name, "wb");
  167. if(!dest)
  168. {
  169. error_message(E_WORLD_DECRYPT_WRITE_PROTECTED, 0, NULL);
  170. return;
  171. }
  172. pro_method = *src_ptr;
  173. src_ptr++;
  174. // Get password
  175. memcpy(password, src_ptr, MAX_PASSWORD_LENGTH);
  176. password[MAX_PASSWORD_LENGTH] = '\0';
  177. src_ptr += 18;
  178. // First, normalize password...
  179. for(i = 0; i < MAX_PASSWORD_LENGTH; i++)
  180. {
  181. password[i] ^= magic_code[i];
  182. password[i] -= 0x12 + pro_method;
  183. password[i] ^= 0x8D;
  184. }
  185. // Xor code
  186. xor_val = get_pw_xor_code(password, pro_method);
  187. // Copy title
  188. if(!fwrite(file_buffer, 25, 1, dest))
  189. goto err_close;
  190. fputc(0, dest);
  191. fputs("M\x02\x11", dest);
  192. meter_curr += 25 + 1 + 3 - 1;
  193. meter_update_screen(&meter_curr, meter_target);
  194. len = file_length - 44;
  195. for(; len > 0; len--)
  196. {
  197. fputc((*src_ptr) ^ xor_val, dest);
  198. src_ptr++;
  199. if((len % 1000) == 0)
  200. {
  201. meter_curr += 999;
  202. meter_update_screen(&meter_curr, meter_target);
  203. }
  204. }
  205. meter_curr = file_length + (file_length - MAX_PASSWORD_LENGTH) - 1;
  206. meter_update_screen(&meter_curr, meter_target);
  207. // Must fix all the absolute file positions so that they're MAX_PASSWORD_LENGTH
  208. // less now
  209. src_ptr = file_buffer + 4245;
  210. fseek(dest, 4230, SEEK_SET);
  211. offset_low_byte = src_ptr[0] ^ xor_val;
  212. fputc(offset_low_byte - MAX_PASSWORD_LENGTH, dest);
  213. if(offset_low_byte < MAX_PASSWORD_LENGTH)
  214. {
  215. fputc((src_ptr[1] ^ xor_val) - 1, dest);
  216. }
  217. else
  218. {
  219. fputc(src_ptr[1] ^ xor_val, dest);
  220. }
  221. fputc(src_ptr[2] ^ xor_val, dest);
  222. fputc(src_ptr[3] ^ xor_val, dest);
  223. meter_curr += 4 - 1;
  224. meter_update_screen(&meter_curr, meter_target);
  225. src_ptr += 4;
  226. num_boards = ((*src_ptr) ^ xor_val);
  227. src_ptr++;
  228. // If custom SFX is there, run through and skip it
  229. if(!num_boards)
  230. {
  231. int sfx_length = (char)(src_ptr[0] ^ xor_val);
  232. sfx_length |= ((char)(src_ptr[1] ^ xor_val)) << 8;
  233. src_ptr += sfx_length + 2;
  234. num_boards = (*src_ptr) ^ xor_val;
  235. src_ptr++;
  236. }
  237. meter_target += num_boards * 4;
  238. meter_curr--;
  239. meter_update_screen(&meter_curr, meter_target);
  240. // Skip titles
  241. src_ptr += (25 * num_boards);
  242. // Synchronize source and dest positions
  243. fseek(dest, (long)(src_ptr - file_buffer - MAX_PASSWORD_LENGTH), SEEK_SET);
  244. // Offset boards
  245. for(i = 0; i < num_boards; i++)
  246. {
  247. // Skip length
  248. src_ptr += 4;
  249. fseek(dest, 4, SEEK_CUR);
  250. // Get offset
  251. offset_low_byte = src_ptr[0] ^ xor_val;
  252. fputc(offset_low_byte - MAX_PASSWORD_LENGTH, dest);
  253. if(offset_low_byte < MAX_PASSWORD_LENGTH)
  254. {
  255. fputc((src_ptr[1] ^ xor_val) - 1, dest);
  256. }
  257. else
  258. {
  259. fputc(src_ptr[1] ^ xor_val, dest);
  260. }
  261. fputc(src_ptr[2] ^ xor_val, dest);
  262. fputc(src_ptr[3] ^ xor_val, dest);
  263. src_ptr += 4;
  264. meter_target += 4 - 1;
  265. meter_update_screen(&meter_curr, meter_target);
  266. }
  267. err_close:
  268. fclose(dest);
  269. err_free:
  270. free(file_buffer);
  271. meter_restore_screen();
  272. }
  273. #define WORLD_GLOBAL_OFFSET_OFFSET 4230
  274. #define WORLD_BLOCK_1_SIZE 4129
  275. #define WORLD_BLOCK_2_SIZE 72
  276. /* This is a lot like try_load_world but much more thorough, and doesn't
  277. * pass data through or leave a file open. This needs to be done before
  278. * any data is ever loaded, so that Megazeux can cleanly abort if there
  279. * is an issue.
  280. *
  281. * There are a few redundant checks here with try_load_world, but that's ok.
  282. */
  283. static enum val_result __validate_legacy_world_file(const char *file,
  284. boolean savegame)
  285. {
  286. enum val_result result = VAL_SUCCESS;
  287. struct stat stat_result;
  288. int stat_op_result;
  289. FILE *f;
  290. char magic[15];
  291. int num_boards;
  292. int board_name_offset;
  293. int v, i;
  294. /* TEST 1: make sure it's even a file */
  295. stat_op_result = stat(file, &stat_result);
  296. if(stat_op_result ||
  297. !S_ISREG(stat_result.st_mode) ||
  298. !(f = fopen_unsafe(file, "rb")))
  299. {
  300. error_message(E_FILE_DOES_NOT_EXIST, 0, NULL);
  301. result = VAL_MISSING;
  302. goto err_out;
  303. }
  304. /* TEST 2: Is it a save file? */
  305. if(savegame)
  306. {
  307. int screen_mode, num_counters, num_strings, len;
  308. if(fread(magic, 5, 1, f) != 1)
  309. goto err_invalid;
  310. v = save_magic(magic);
  311. if(!v)
  312. goto err_invalid;
  313. else if(v > MZX_LEGACY_FORMAT_VERSION)
  314. {
  315. error_message(E_SAVE_VERSION_TOO_RECENT, v, NULL);
  316. result = VAL_VERSION;
  317. goto err_close;
  318. }
  319. // This enables 2.84 save loading.
  320. // If we ever want to remove this, change this check.
  321. //else if (v < MZX_VERSION)
  322. else if(v < MZX_LEGACY_FORMAT_VERSION)
  323. {
  324. error_message(E_SAVE_VERSION_OLD, v, NULL);
  325. result = VAL_VERSION;
  326. goto err_close;
  327. }
  328. /* TEST 3: Check for truncation, savegame style, hope this
  329. * doesn't explode :erm:
  330. */
  331. if(
  332. fseek(f, 8, SEEK_SET) ||
  333. fseek(f, WORLD_BLOCK_1_SIZE, SEEK_CUR) ||
  334. fseek(f, 71, SEEK_CUR) ||
  335. fseek(f, (len = fgetw(f)), SEEK_CUR) ||
  336. (len < 0) ||
  337. fseek(f, WORLD_BLOCK_2_SIZE, SEEK_CUR) ||
  338. fseek(f, 24, SEEK_CUR))
  339. {
  340. debug("pre-counters\n");
  341. goto err_invalid;
  342. }
  343. //do counters - vvvvnnnn(name)
  344. num_counters = fgetd(f);
  345. if(num_counters < 0)
  346. {
  347. debug("counter num\n");
  348. goto err_invalid;
  349. }
  350. for(i = 0; i < num_counters; i++)
  351. {
  352. if(
  353. fseek(f, 4, SEEK_CUR) || //value
  354. fseek(f, (len = fgetd(f)), SEEK_CUR) ||
  355. (len < 0))
  356. {
  357. debug("counters\n");
  358. goto err_invalid;
  359. }
  360. }
  361. //do strings- nnnnllll(name)(value)
  362. num_strings = fgetd(f);
  363. if(num_strings < 0)
  364. {
  365. debug("string num\n");
  366. goto err_invalid;
  367. }
  368. for(i = 0; i < num_strings; i++)
  369. {
  370. int name_length = fgetd(f);
  371. int value_length = fgetd(f);
  372. if(
  373. (name_length < 0) ||
  374. (value_length < 0) ||
  375. fseek(f, name_length, SEEK_CUR) ||
  376. fseek(f, value_length, SEEK_CUR))
  377. {
  378. debug("strings\n");
  379. goto err_invalid;
  380. }
  381. }
  382. if(
  383. fseek(f, 4612, SEEK_CUR) || //sprites
  384. fseek(f, 12, SEEK_CUR) || //misc
  385. fseek(f, fgetw(f), SEEK_CUR) || //fread_open
  386. fseek(f, 4, SEEK_CUR) || //fread_pos
  387. fseek(f, fgetw(f), SEEK_CUR) || //fwrite_open
  388. fseek(f, 4, SEEK_CUR)) //fwrite_pos
  389. {
  390. debug("post strings\n");
  391. goto err_invalid;
  392. }
  393. screen_mode = fgetw(f);
  394. if((screen_mode > 3) || (screen_mode > 1 &&
  395. fseek(f, 768, SEEK_CUR))) //smzx palette
  396. {
  397. debug("smzx palette\n");
  398. goto err_invalid;
  399. }
  400. if(
  401. fseek(f, 4, SEEK_CUR) || //commands
  402. ((len = fgetd(f)) < 0) || //vlayer size
  403. fseek(f, 4, SEEK_CUR) || // width & height
  404. fseek(f, len, SEEK_CUR) || //chars
  405. fseek(f, len, SEEK_CUR)) //colors
  406. {
  407. debug("vlayer\n");
  408. goto err_invalid;
  409. }
  410. /* now we should be at the global robot pointer! */
  411. }
  412. else /* !savegame */
  413. {
  414. int protection_method;
  415. /* TEST 3: Check for truncation */
  416. if(fseek(f, WORLD_GLOBAL_OFFSET_OFFSET, SEEK_SET))
  417. goto err_invalid;
  418. fseek(f, BOARD_NAME_SIZE, SEEK_SET);
  419. /* TEST 4: If we think it's locked, try to decrypt it. */
  420. protection_method = fgetc(f);
  421. if(protection_method > 0)
  422. {
  423. if(protection_method > 3)
  424. goto err_invalid;
  425. result = VAL_PROTECTED;
  426. goto err_close;
  427. }
  428. /* TEST 5: Test the magic */
  429. if(!fread(magic, 3, 1, f))
  430. goto err_invalid;
  431. v = world_magic(magic);
  432. if(v == 0)
  433. goto err_invalid;
  434. else if(v < V251)
  435. {
  436. error_message(E_WORLD_FILE_VERSION_OLD, v, NULL);
  437. result = VAL_VERSION;
  438. goto err_close;
  439. }
  440. else if(v > MZX_LEGACY_FORMAT_VERSION)
  441. {
  442. error_message(E_WORLD_FILE_VERSION_TOO_RECENT, v, NULL);
  443. result = VAL_VERSION;
  444. goto err_close;
  445. }
  446. /* TEST 6: Attempt to eliminate invalid files by
  447. * testing the palette for impossible values.
  448. */
  449. fseek(f, WORLD_GLOBAL_OFFSET_OFFSET - 48, SEEK_SET);
  450. for(i = 0; i<48; i++)
  451. {
  452. int val = fgetc(f);
  453. if((val < 0) || (val > 63))
  454. goto err_invalid;
  455. }
  456. /* now we should be at the global robot pointer! */
  457. }
  458. /* TEST 7: Either branch should be at the global robot pointer now.
  459. * Test for valid SFX structure, if applicable, and board information.
  460. */
  461. fseek(f, 4, SEEK_CUR);
  462. // Do the sfx
  463. num_boards = fgetc(f);
  464. if(num_boards == 0)
  465. {
  466. int sfx_size = fgetw(f);
  467. int sfx_off = ftell(f);
  468. for(i = 0; i < NUM_SFX; i++)
  469. {
  470. if(fseek(f, fgetc(f), SEEK_CUR))
  471. break;
  472. }
  473. if((i != NUM_SFX) || ((ftell(f) - sfx_off) != sfx_size))
  474. goto err_invalid;
  475. num_boards = fgetc(f);
  476. }
  477. if(num_boards == 0)
  478. goto err_invalid;
  479. board_name_offset = ftell(f);
  480. //Make sure board name and pointer data exists
  481. if(
  482. fseek(f, num_boards * BOARD_NAME_SIZE, SEEK_CUR) ||
  483. fseek(f, num_boards * 8, SEEK_CUR) ||
  484. ((ftell(f) - board_name_offset) != num_boards * (BOARD_NAME_SIZE + 8)))
  485. goto err_invalid;
  486. //todo: maybe have a complete fail when N number of pointers fail?
  487. goto err_close;
  488. err_invalid:
  489. result = VAL_INVALID;
  490. if(savegame)
  491. error_message(E_SAVE_FILE_INVALID, 0, NULL);
  492. else
  493. error_message(E_WORLD_FILE_INVALID, 0, NULL);
  494. err_close:
  495. fclose(f);
  496. err_out:
  497. return result;
  498. }
  499. enum val_result validate_legacy_world_file(struct world *mzx_world,
  500. const char *file, boolean savegame)
  501. {
  502. enum val_result res = __validate_legacy_world_file(file, savegame);
  503. if(res == VAL_PROTECTED)
  504. {
  505. if(!has_video_initialized() ||
  506. !confirm(mzx_world, "This world may be password protected. Decrypt it?"))
  507. {
  508. // Decrypt and try again
  509. decrypt(file);
  510. res = __validate_legacy_world_file(file, savegame);
  511. // If the world is still protected, abort.
  512. if(res != VAL_PROTECTED)
  513. return res;
  514. }
  515. error_message(E_WORLD_LOCKED, 0, NULL);
  516. return VAL_ABORTED;
  517. }
  518. return res;
  519. }
  520. void legacy_load_world(struct world *mzx_world, FILE *fp, const char *file,
  521. boolean savegame, int file_version, char *name, boolean *faded)
  522. {
  523. int i;
  524. int num_boards;
  525. int gl_rob, last_pos;
  526. unsigned char *charset_mem;
  527. unsigned char r, g, b;
  528. struct counter_list *counter_list;
  529. struct string_list *string_list;
  530. struct board *cur_board;
  531. int meter_target = 2, meter_curr = 0;
  532. if(savegame)
  533. {
  534. fseek(fp, 5, SEEK_SET);
  535. mzx_world->version = fgetw(fp);
  536. mzx_world->current_board_id = fgetc(fp);
  537. }
  538. else
  539. {
  540. fseek(fp, 29, SEEK_SET);
  541. strcpy(mzx_world->name, name);
  542. mzx_world->version = file_version;
  543. mzx_world->current_board_id = 0;
  544. }
  545. meter_initial_draw(meter_curr, meter_target, "Loading...");
  546. charset_mem = cmalloc(CHAR_SIZE * CHARSET_SIZE);
  547. if(!fread(charset_mem, CHAR_SIZE * CHARSET_SIZE, 1, fp))
  548. goto err_close;
  549. ec_clear_set();
  550. ec_mem_load_set(charset_mem, CHAR_SIZE * CHARSET_SIZE);
  551. free(charset_mem);
  552. // Idchars array...
  553. memset(id_chars, 0, ID_CHARS_SIZE);
  554. memset(id_dmg, 0, ID_DMG_SIZE);
  555. memset(bullet_color, 0, ID_BULLET_COLOR_SIZE);
  556. if(!fread(id_chars, LEGACY_ID_CHARS_SIZE, 1, fp))
  557. goto err_close;
  558. missile_color = fgetc(fp);
  559. if(!fread(bullet_color, LEGACY_ID_BULLET_COLOR_SIZE, 1, fp))
  560. goto err_close;
  561. if(!fread(id_dmg, LEGACY_ID_DMG_SIZE, 1, fp))
  562. goto err_close;
  563. // Status counters...
  564. if(fread((char *)mzx_world->status_counters_shown, COUNTER_NAME_SIZE,
  565. NUM_STATUS_COUNTERS, fp) != NUM_STATUS_COUNTERS)
  566. goto err_close;
  567. if(savegame)
  568. {
  569. if(!fread(mzx_world->keys, NUM_KEYS, 1, fp))
  570. goto err_close;
  571. mzx_world->blind_dur = fgetc(fp);
  572. mzx_world->firewalker_dur = fgetc(fp);
  573. mzx_world->freeze_time_dur = fgetc(fp);
  574. mzx_world->slow_time_dur = fgetc(fp);
  575. mzx_world->wind_dur = fgetc(fp);
  576. for(i = 0; i < 8; i++)
  577. {
  578. mzx_world->pl_saved_x[i] = fgetw(fp);
  579. }
  580. for(i = 0; i < 8; i++)
  581. {
  582. mzx_world->pl_saved_y[i] = fgetw(fp);
  583. }
  584. if(!fread(mzx_world->pl_saved_board, 8, 1, fp))
  585. goto err_close;
  586. mzx_world->saved_pl_color = fgetc(fp);
  587. mzx_world->under_player_id = fgetc(fp);
  588. mzx_world->under_player_color = fgetc(fp);
  589. mzx_world->under_player_param = fgetc(fp);
  590. mzx_world->mesg_edges = fgetc(fp);
  591. mzx_world->scroll_base_color = fgetc(fp);
  592. mzx_world->scroll_corner_color = fgetc(fp);
  593. mzx_world->scroll_pointer_color = fgetc(fp);
  594. mzx_world->scroll_title_color = fgetc(fp);
  595. mzx_world->scroll_arrow_color = fgetc(fp);
  596. {
  597. size_t len = fgetw(fp);
  598. if(len >= MAX_PATH)
  599. len = MAX_PATH - 1;
  600. if(len && !fread(mzx_world->real_mod_playing, len, 1, fp))
  601. goto err_close;
  602. mzx_world->real_mod_playing[len] = 0;
  603. }
  604. }
  605. mzx_world->edge_color = fgetc(fp);
  606. mzx_world->first_board = fgetc(fp);
  607. mzx_world->endgame_board = fgetc(fp);
  608. mzx_world->death_board = fgetc(fp);
  609. mzx_world->endgame_x = fgetw(fp);
  610. mzx_world->endgame_y = fgetw(fp);
  611. mzx_world->game_over_sfx = fgetc(fp);
  612. mzx_world->death_x = fgetw(fp);
  613. mzx_world->death_y = fgetw(fp);
  614. mzx_world->starting_lives = fgetw(fp);
  615. mzx_world->lives_limit = fgetw(fp);
  616. mzx_world->starting_health = fgetw(fp);
  617. mzx_world->health_limit = fgetw(fp);
  618. mzx_world->enemy_hurt_enemy = fgetc(fp);
  619. mzx_world->clear_on_exit = fgetc(fp);
  620. mzx_world->only_from_swap = fgetc(fp);
  621. // Palette...
  622. for(i = 0; i < 16; i++)
  623. {
  624. r = fgetc(fp);
  625. g = fgetc(fp);
  626. b = fgetc(fp);
  627. set_rgb(i, r, g, b);
  628. }
  629. if(savegame)
  630. {
  631. int vlayer_size;
  632. int num_counters, num_strings;
  633. int screen_mode;
  634. int j;
  635. for(i = 0; i < 16; i++)
  636. {
  637. set_color_intensity(i, fgetc(fp));
  638. }
  639. *faded = fgetc(fp);
  640. mzx_world->player_restart_x = fgetw(fp);
  641. mzx_world->player_restart_y = fgetw(fp);
  642. mzx_world->under_player_id = fgetc(fp);
  643. mzx_world->under_player_color = fgetc(fp);
  644. mzx_world->under_player_param = fgetc(fp);
  645. // Read counters
  646. num_counters = fgetd(fp);
  647. counter_list = &(mzx_world->counter_list);
  648. counter_list->num_counters = num_counters;
  649. counter_list->num_counters_allocated = num_counters;
  650. counter_list->counters = ccalloc(num_counters, sizeof(struct counter *));
  651. for(i = 0, j = 0; i < num_counters; i++)
  652. {
  653. boolean counter = legacy_load_counter(mzx_world, fp, counter_list, j);
  654. /* We loaded a special counter, this doesn't need to be
  655. * loaded into the regular list.
  656. */
  657. if(!counter)
  658. {
  659. counter_list->num_counters--;
  660. continue;
  661. }
  662. j++;
  663. }
  664. // Read strings
  665. num_strings = fgetd(fp);
  666. string_list = &(mzx_world->string_list);
  667. string_list->num_strings = num_strings;
  668. string_list->num_strings_allocated = num_strings;
  669. string_list->strings = ccalloc(num_strings, sizeof(struct string *));
  670. for(i = 0; i < num_strings; i++)
  671. {
  672. legacy_load_string(fp, string_list, i);
  673. }
  674. #ifndef CONFIG_KHASH
  675. // Versions without the hash table require these to be sorted at all times
  676. sort_counter_list(counter_list);
  677. sort_string_list(string_list);
  678. #endif
  679. // Sprite data
  680. for(i = 0; i < MAX_SPRITES; i++)
  681. {
  682. (mzx_world->sprite_list[i])->x = fgetw(fp);
  683. (mzx_world->sprite_list[i])->y = fgetw(fp);
  684. (mzx_world->sprite_list[i])->ref_x = fgetw(fp);
  685. (mzx_world->sprite_list[i])->ref_y = fgetw(fp);
  686. (mzx_world->sprite_list[i])->color = fgetc(fp);
  687. (mzx_world->sprite_list[i])->flags = fgetc(fp);
  688. (mzx_world->sprite_list[i])->width = fgetc(fp);
  689. (mzx_world->sprite_list[i])->height = fgetc(fp);
  690. (mzx_world->sprite_list[i])->col_x = fgetc(fp);
  691. (mzx_world->sprite_list[i])->col_y = fgetc(fp);
  692. (mzx_world->sprite_list[i])->col_width = fgetc(fp);
  693. (mzx_world->sprite_list[i])->col_height = fgetc(fp);
  694. }
  695. // total sprites
  696. mzx_world->active_sprites = fgetc(fp);
  697. // y order flag
  698. mzx_world->sprite_y_order = fgetc(fp);
  699. // collision info
  700. mzx_world->collision_count = fgetw(fp);
  701. for(i = 0; i < MAX_SPRITES; i++)
  702. {
  703. mzx_world->collision_list[i] = fgetw(fp);
  704. }
  705. // Multiplier
  706. mzx_world->multiplier = fgetw(fp);
  707. // Divider
  708. mzx_world->divider = fgetw(fp);
  709. // Circle divisions
  710. mzx_world->c_divisions = fgetw(fp);
  711. // String FREAD and FWRITE Delimiters
  712. mzx_world->fread_delimiter = fgetw(fp);
  713. mzx_world->fwrite_delimiter = fgetw(fp);
  714. // Builtin shooting/message status
  715. mzx_world->bi_shoot_status = fgetc(fp);
  716. mzx_world->bi_mesg_status = fgetc(fp);
  717. // Load input file name, open later
  718. {
  719. size_t len = fgetw(fp);
  720. if(len >= MAX_PATH)
  721. len = MAX_PATH - 1;
  722. if(len && !fread(mzx_world->input_file_name, len, 1, fp))
  723. goto err_close;
  724. mzx_world->input_file_name[len] = 0;
  725. }
  726. mzx_world->temp_input_pos = fgetd(fp);
  727. // Load output file name, open later
  728. {
  729. size_t len = fgetw(fp);
  730. if(len >= MAX_PATH)
  731. len = MAX_PATH - 1;
  732. if(len && !fread(mzx_world->output_file_name, len, 1, fp))
  733. goto err_close;
  734. mzx_world->output_file_name[len] = 0;
  735. }
  736. mzx_world->temp_output_pos = fgetd(fp);
  737. screen_mode = fgetw(fp);
  738. // If it's at SMZX mode 2, set default palette as loaded
  739. // so the .sav one doesn't get overwritten
  740. if(screen_mode == 2)
  741. {
  742. smzx_palette_loaded(1);
  743. }
  744. set_screen_mode(screen_mode);
  745. // Also get the palette
  746. if(screen_mode > 1)
  747. {
  748. for(i = 0; i < 256; i++)
  749. {
  750. r = fgetc(fp);
  751. g = fgetc(fp);
  752. b = fgetc(fp);
  753. set_rgb(i, r, g, b);
  754. }
  755. }
  756. mzx_world->commands = fgetd(fp);
  757. vlayer_size = fgetd(fp);
  758. mzx_world->vlayer_width = fgetw(fp);
  759. mzx_world->vlayer_height = fgetw(fp);
  760. mzx_world->vlayer_size = vlayer_size;
  761. mzx_world->vlayer_chars = cmalloc(vlayer_size);
  762. mzx_world->vlayer_colors = cmalloc(vlayer_size);
  763. if(vlayer_size &&
  764. (!fread(mzx_world->vlayer_chars, vlayer_size, 1, fp) ||
  765. !fread(mzx_world->vlayer_colors, vlayer_size, 1, fp)))
  766. goto err_close;
  767. }
  768. // Get position of global robot...
  769. gl_rob = fgetd(fp);
  770. // Get number of boards
  771. num_boards = fgetc(fp);
  772. if(num_boards == 0)
  773. {
  774. int sfx_size;
  775. char *sfx_offset = mzx_world->custom_sfx;
  776. // Sfx
  777. mzx_world->custom_sfx_on = 1;
  778. fseek(fp, 2, SEEK_CUR); // Skip word size
  779. //Read sfx
  780. for(i = 0; i < NUM_SFX; i++, sfx_offset += LEGACY_SFX_SIZE)
  781. {
  782. sfx_size = fgetc(fp);
  783. if(sfx_size && !fread(sfx_offset, sfx_size, 1, fp))
  784. goto err_close;
  785. }
  786. num_boards = fgetc(fp);
  787. }
  788. else
  789. {
  790. mzx_world->custom_sfx_on = 0;
  791. }
  792. meter_target += num_boards;
  793. meter_update_screen(&meter_curr, meter_target);
  794. mzx_world->num_boards = num_boards;
  795. mzx_world->num_boards_allocated = num_boards;
  796. mzx_world->board_list = cmalloc(sizeof(struct board *) * num_boards);
  797. // Skip the names for now
  798. // Gonna wanna come back to here
  799. last_pos = ftell(fp);
  800. fseek(fp, num_boards * BOARD_NAME_SIZE, SEEK_CUR);
  801. for(i = 0; i < num_boards; i++)
  802. {
  803. mzx_world->board_list[i] =
  804. legacy_load_board_allocate(mzx_world, fp, savegame, file_version);
  805. store_board_to_extram(mzx_world->board_list[i]);
  806. meter_update_screen(&meter_curr, meter_target);
  807. }
  808. // Read global robot
  809. fseek(fp, gl_rob, SEEK_SET); //don't worry if this fails
  810. legacy_load_robot(mzx_world, &mzx_world->global_robot, fp, savegame,
  811. file_version);
  812. // Some old worlds have the global_robot marked unused. Always mark it used.
  813. mzx_world->global_robot.used = 1;
  814. // Go back to where the names are
  815. fseek(fp, last_pos, SEEK_SET);
  816. for(i = 0; i < num_boards; i++)
  817. {
  818. cur_board = mzx_world->board_list[i];
  819. // Look at the name, width, and height of the just loaded board
  820. if(cur_board)
  821. {
  822. if(!fread(cur_board->board_name, BOARD_NAME_SIZE, 1, fp))
  823. cur_board->board_name[0] = 0;
  824. // Also patch a pointer to the global robot
  825. if(cur_board->robot_list)
  826. (mzx_world->board_list[i])->robot_list[0] = &mzx_world->global_robot;
  827. // Also optimize out null objects
  828. retrieve_board_from_extram(mzx_world->board_list[i]);
  829. optimize_null_objects(mzx_world->board_list[i]);
  830. store_board_to_extram(mzx_world->board_list[i]);
  831. }
  832. else
  833. {
  834. fseek(fp, BOARD_NAME_SIZE, SEEK_CUR);
  835. }
  836. }
  837. meter_update_screen(&meter_curr, meter_target);
  838. meter_restore_screen();
  839. fclose(fp);
  840. return;
  841. err_close:
  842. // Note that this file had already been successfully validated for length
  843. // and opened with no issue before this error occurred, and that the only
  844. // way to reach this error is a failed fread before any board/robot data
  845. // was loaded. Something seriously went wrong somewhere.
  846. error_message(E_IO_READ, 0, NULL);
  847. meter_restore_screen();
  848. fclose(fp);
  849. }