/* File: load.c */ /* Purpose: support for loading savefiles -BEN- */ #include "angband.h" /* * This file is responsible for loading all "2.7.X" savefiles * * Note that 2.7.0 - 2.7.2 savefiles are obsolete and will not work. * * We attempt to prevent corrupt savefiles from inducing memory errors. * * Note that Angband 2.7.9 encodes "terrain features" in the savefile * using the old 2.7.8 method. Angband 2.8.0 will use the same method * to read pre-2.8.0 savefiles, but will use a new method to save them, * which will only affect "save.c". * * Note that Angband 2.8.0 will use a VERY different savefile method, * which will use "blocks" of information which can be ignored or parsed, * and which will not use a silly "protection" scheme on the savefiles, * but which may still use some form of "checksums" to prevent the use * of "corrupt" savefiles, which might cause nasty weirdness. * * Note that this file should not use the random number generator, the * object flavors, the visual attr/char mappings, or anything else which * is initialized *after* or *during* the "load character" function. */ /* * Local "savefile" pointer */ static FILE *fff; /* * Hack -- old "encryption" byte */ static byte xor_byte; /* * Hack -- simple "checksum" on the actual values */ static u32b v_check = 0L; /* * Hack -- simple "checksum" on the encoded bytes */ static u32b x_check = 0L; /* * Show information on the screen, one line at a time. * Start at line 2, and wrap, if needed, back to line 2. */ static void note(cptr msg) { static int y = 2; /* Draw the message */ prt(msg, y, 0); /* Advance one line (wrap if needed) */ if (++y >= 24) y = 2; /* Flush it */ Term_fresh(); } /* * Hack -- determine if an item is "wearable" (or a missile) */ static bool wearable_p(object_type *i_ptr) { /* Valid "tval" codes */ switch (i_ptr->tval) { case TV_SHOT: case TV_ARROW: case TV_BOLT: case TV_BOW: case TV_DIGGING: case TV_HAFTED: case TV_POLEARM: case TV_SWORD: case TV_BOOTS: case TV_GLOVES: case TV_HELM: case TV_CROWN: case TV_SHIELD: case TV_CLOAK: case TV_SOFT_ARMOR: case TV_HARD_ARMOR: case TV_DRAG_ARMOR: case TV_LITE: case TV_AMULET: case TV_RING: return (TRUE); } /* Nope */ return (FALSE); } /* * The following functions are used to load the basic building blocks * of savefiles. They also maintain the "checksum" info for 2.7.0+ */ static byte sf_get(void) { byte c, v; /* Get a character, decode the value */ c = getc(fff) & 0xFF; v = c ^ xor_byte; xor_byte = c; /* Maintain the checksum info */ v_check += v; x_check += xor_byte; /* Return the value */ return (v); } static void rd_byte(byte *ip) { *ip = sf_get(); } /*static void rd_char(char *ip) { rd_byte((byte*)ip); }*/ static void rd_u16b(u16b *ip) { (*ip) = sf_get(); (*ip) |= ((u16b)(sf_get()) << 8); } static void rd_s16b(s16b *ip) { rd_u16b((u16b*)ip); } static void rd_u32b(u32b *ip) { (*ip) = sf_get(); (*ip) |= ((u32b)(sf_get()) << 8); (*ip) |= ((u32b)(sf_get()) << 16); (*ip) |= ((u32b)(sf_get()) << 24); } static void rd_s32b(s32b *ip) { rd_u32b((u32b*)ip); } /* * Hack -- read a string */ static void rd_string(char *str, int max) { int i; /* Read the string */ for (i = 0; TRUE; i++) { byte tmp8u; /* Read a byte */ rd_byte(&tmp8u); /* Collect string while legal */ if (i < max) str[i] = tmp8u; /* End of string */ if (!tmp8u) break; } /* Terminate */ str[max-1] = '\0'; } /* * Hack -- strip some bytes */ static void strip_bytes(int n) { int i; byte tmp8u; /* Strip the bytes */ for (i = 0; i < n; i++) rd_byte(&tmp8u); } /* * Read an item (2.7.0 or later) * * Note that savefiles from 2.7.0 and 2.7.1 are obsolete. * * Note that pre-2.7.9 savefiles (from Angband 2.5.1 onward anyway) * can be read using the code above. * * This function attempts to "repair" old savefiles, and to extract * the most up to date values for various object fields. * * Note that Angband 2.7.9 introduced a new method for object "flags" * in which the "flags" on an object are actually extracted when they * are needed from the object kind, artifact index, ego-item index, * and two special "xtra" fields which are used to encode any "extra" * power of certain ego-items. This had the side effect that items * imported from pre-2.7.9 savefiles will lose any "extra" powers they * may have had, and also, all "uncursed" items will become "cursed" * again, including Calris, even if it is being worn at the time. As * a complete hack, items which are inscribed with "uncursed" will be * "uncursed" when imported from pre-2.7.9 savefiles. */ static void rd_item(object_type *i_ptr) { byte old_dd; byte old_ds; u32b f1, f2, f3; s16b tmp_s16b; byte tmp_byte; u16b tmp16u; object_kind *k_ptr; char note[128]; /* Hack -- wipe */ WIPE(i_ptr, object_type); /* Kind */ rd_s16b(&tmp_s16b); i_ptr->set_k_idx(tmp_s16b); /* Location */ rd_byte(&tmp_byte); i_ptr->set_iy(tmp_byte); rd_byte(&tmp_byte); i_ptr->set_ix(tmp_byte); /* Special pval */ rd_s16b(&tmp_s16b); i_ptr->set_pval(tmp_s16b); /* New method */ rd_byte(&tmp_byte); i_ptr->set_discount(tmp_byte); rd_byte(&tmp_byte); i_ptr->set_number(tmp_byte); rd_byte(&tmp_byte); i_ptr->set_name1(tmp_byte); rd_byte(&tmp_byte); i_ptr->set_name2(tmp_byte); rd_s16b(&tmp_s16b); i_ptr->set_timeout(tmp_s16b); rd_s16b(&tmp_s16b); i_ptr->set_to_h(tmp_s16b); rd_s16b(&tmp_s16b); i_ptr->set_to_d(tmp_s16b); rd_s16b(&tmp_s16b); i_ptr->set_to_a(tmp_s16b); rd_s16b(&tmp_s16b); i_ptr->set_ac(tmp_s16b); rd_byte(&old_dd); rd_byte(&old_ds); rd_byte(&tmp_byte); i_ptr->set_ident(tmp_byte); rd_byte(&tmp_byte); i_ptr->set_marked(tmp_byte); /* Old flags */ strip_bytes(12); /* Unused */ rd_u16b(&tmp16u); /* Special powers */ rd_byte(&tmp_byte); i_ptr->set_xtra1(tmp_byte); rd_byte(&tmp_byte); i_ptr->set_xtra2(tmp_byte); /* Inscription */ rd_string(note, 128); /* Save the inscription */ if (note[0]) i_ptr->set_note(quark_add(note)); rd_s16b(&i_ptr->next_i_idx); /* Mega-Hack -- handle "dungeon objects" later */ if ((i_ptr->k_idx >= 445) && (i_ptr->k_idx <= 479)) return; /* Obtain the "kind" template */ k_ptr = i_ptr->k_ptr; /* Hack -- notice "broken" items */ if (k_ptr->cost <= 0) i_ptr->set_ident_flag(ID_BROKEN); /* Repair non "wearable" items */ if (!wearable_p(i_ptr)) { /* Acquire correct fields */ i_ptr->set_to_h(k_ptr->to_h); i_ptr->set_to_d(k_ptr->to_d); i_ptr->set_to_a(k_ptr->to_a); /* Acquire correct fields */ i_ptr->set_ac(k_ptr->ac); i_ptr->set_dd(k_ptr->dd); i_ptr->set_ds(k_ptr->ds); /* Paranoia */ i_ptr->set_name1(0); i_ptr->set_name2(0); /* All done */ return; } /* Extract the flags */ object_flags(i_ptr, &f1, &f2, &f3); /* Paranoia */ if (i_ptr->name1) { /* Obtain the artifact info */ artifact_type *a_ptr = &a_info[i_ptr->name1]; /* Verify that artifact */ if (!a_ptr->name) i_ptr->set_name1(0); } /* Paranoia */ if (i_ptr->name2) { /* Obtain the ego-item info */ ego_item_type *e_ptr = &e_info[i_ptr->name2]; /* Verify that ego-item */ if (!e_ptr->name) i_ptr->set_name2(0); } /* Acquire standard fields */ i_ptr->set_ac(k_ptr->ac); i_ptr->set_dd(k_ptr->dd); i_ptr->set_ds(k_ptr->ds); /* Hack -- extract the "broken" flag */ if (!i_ptr->pval < 0) i_ptr->set_ident_flag(ID_BROKEN); /* Artifacts */ if (i_ptr->name1) { /* Obtain the artifact info */ artifact_type *a_ptr = &a_info[i_ptr->name1]; /* Acquire new artifact "pval" */ i_ptr->set_pval(a_ptr->pval); /* Acquire new artifact fields */ i_ptr->set_ac(a_ptr->ac); i_ptr->set_dd(a_ptr->dd); i_ptr->set_ds(a_ptr->ds); /* Hack -- extract the "broken" flag */ if (!a_ptr->cost) i_ptr->set_ident_flag(ID_BROKEN); } /* Ego items */ if (i_ptr->name2) { /* Obtain the ego-item info */ ego_item_type *e_ptr = &e_info[i_ptr->name2]; /* Hack -- keep some old fields */ if ((i_ptr->dd < old_dd) && (i_ptr->ds == old_ds)) { /* Keep old boosted damage dice */ i_ptr->set_dd(old_dd); } /* Hack -- extract the "broken" flag */ if (!e_ptr->cost) i_ptr->set_ident_flag(ID_BROKEN); } } /* * Read a monster */ static void rd_monster(monster_type *m_ptr) { byte tmp8u; s16b tmp_s16b; byte tmp_byte; /* Hack -- wipe */ WIPE(m_ptr, monster_type); /* Read the monster race */ rd_s16b(&tmp_s16b); m_ptr->set_r_idx(tmp_s16b); /* Read the other information */ rd_byte(&tmp_byte); m_ptr->set_fy(tmp_byte); rd_byte(&tmp_byte); m_ptr->set_fx(tmp_byte); rd_s16b(&tmp_s16b); m_ptr->set_hp(tmp_s16b); rd_s16b(&tmp_s16b); m_ptr->set_maxhp(tmp_s16b); rd_s16b(&tmp_s16b); m_ptr->set_csleep(tmp_s16b); rd_byte(&tmp_byte); m_ptr->set_mspeed(tmp_byte); rd_byte(&tmp_byte); m_ptr->set_energy(tmp_byte); rd_byte(&tmp_byte); m_ptr->set_stunned(tmp_byte); rd_byte(&tmp_byte); m_ptr->set_confused(tmp_byte); rd_byte(&tmp_byte); m_ptr->set_monfear(tmp_byte); rd_byte(&tmp_byte); m_ptr->set_temp_speed(tmp_byte); rd_byte(&tmp_byte); m_ptr->set_temp_slow(tmp_byte); rd_byte(&tmp_byte); m_ptr->set_spawned(tmp_byte); rd_byte(&tmp8u); } /* * Read the monster lore */ static void rd_lore(int r_idx) { byte tmp8u; monster_race *r_ptr = &r_info[r_idx]; /* Count sights/deaths/kills */ rd_s16b(&r_ptr->r_sights); rd_s16b(&r_ptr->r_deaths); rd_s16b(&r_ptr->r_pkills); rd_s16b(&r_ptr->r_tkills); /* Count wakes and ignores */ rd_byte(&r_ptr->r_wake); rd_byte(&r_ptr->r_ignore); /* Extra stuff */ rd_byte(&r_ptr->r_xtra1); rd_byte(&r_ptr->r_xtra2); /* Count drops */ rd_byte(&r_ptr->r_drop_gold); rd_byte(&r_ptr->r_drop_item); /* Count spells */ rd_byte(&r_ptr->r_cast_inate); rd_byte(&r_ptr->r_cast_spell); /* Count blows of each type */ rd_byte(&r_ptr->r_blows[0]); rd_byte(&r_ptr->r_blows[1]); rd_byte(&r_ptr->r_blows[2]); rd_byte(&r_ptr->r_blows[3]); /* Memorize flags */ rd_u32b(&r_ptr->r_flags1); rd_u32b(&r_ptr->r_flags2); rd_u32b(&r_ptr->r_flags3); rd_u32b(&r_ptr->r_flags4); rd_u32b(&r_ptr->r_flags5); rd_u32b(&r_ptr->r_flags6); /* Read the "Racial" monster limit per level */ rd_byte(&r_ptr->max_num); /* Later (?) */ rd_byte(&tmp8u); rd_byte(&tmp8u); rd_byte(&tmp8u); /* Repair the lore flags */ r_ptr->r_flags1 &= r_ptr->flags1; r_ptr->r_flags2 &= r_ptr->flags2; r_ptr->r_flags3 &= r_ptr->flags3; r_ptr->r_flags4 &= r_ptr->flags4; r_ptr->r_flags5 &= r_ptr->flags5; r_ptr->r_flags6 &= r_ptr->flags6; } /* * Read a store */ static errr rd_store(int n) { store_type *st_ptr = &store[n]; int j; byte own, num; /* Read the basic info */ rd_s32b(&st_ptr->store_open); rd_s16b(&st_ptr->insult_cur); rd_byte(&own); rd_byte(&num); rd_s16b(&st_ptr->good_buy); rd_s16b(&st_ptr->bad_buy); /* Extract the owner (see above) */ st_ptr->owner = own; /* Read the items */ for (j = 0; j < num; j++) { object_type forge; /* Read the item */ rd_item(&forge); /* Acquire valid items */ if (st_ptr->stock_num < STORE_INVEN_MAX) { /* Acquire the item */ st_ptr->stock[st_ptr->stock_num++] = forge; } } /* Success */ return (0); } /* * Read options (ignore most pre-2.7.9 options) * * XXX XXX XXX Angband 2.8.0 will not only throw out all pre-2.8.0 * options, and will save at least 8*32 option flags, but will also * save an "option mask" indicating which option flags are legal. * This will allow easy "adapting" to older savefiles. * * We should also make the "cheating" options official flags, and * move the "byte" options to a different part of the code, perhaps * with a few more (for variety). * * We should perhaps attempt to parse a few of the pre-2.8.0 flags, * such as "rogue_like_commands" or "use_color". Or not... * * XXX XXX XXX Remember to rename all the options for 2.8.0 with * nicer names, and to reorganize them in the option flag sets in * some more or less intuitive order (perhaps simply in order?). * * Implement simple "savefile extenders" using some form of "sized" * chunks of bytes, with a {size,type,data} format, so everyone can * know the size, interested people can know the type, and the actual * data is available to the parsing routines that acknowledge the type. * * Consider changing the "globe of invulnerability" code so that it * takes some form of "maximum damage to protect from" in addition to * the existing "number of turns to protect for", and where each hit * by a monster will reduce the shield by that amount. */ static void rd_options(void) { int i; byte b; u16b c; u32b opt[4]; /*** Normal options ***/ /* Read the options */ rd_u32b(&opt[0]); rd_u32b(&opt[1]); rd_u32b(&opt[2]); rd_u32b(&opt[3]); /*** Special options ***/ /* Read "delay_spd" */ rd_byte(&b); delay_spd = b; /* Read "hitpoint_warn" */ rd_byte(&b); hitpoint_warn = b; /*** Cheating options ***/ rd_u16b(&c); if (c & 0x0002) wizard = TRUE; cheat_peek = (c & 0x0100) ? TRUE : FALSE; cheat_hear = (c & 0x0200) ? TRUE : FALSE; cheat_room = (c & 0x0400) ? TRUE : FALSE; cheat_xtra = (c & 0x0800) ? TRUE : FALSE; cheat_know = (c & 0x1000) ? TRUE : FALSE; cheat_live = (c & 0x2000) ? TRUE : FALSE; /*** Normal Options ***/ /* Analyze the options */ for (i = 0; options[i].o_desc; i++) { int os = options[i].o_set; int ob = options[i].o_bit; /* Extract a variable setting, if possible */ if (options[i].o_var && os) { (*options[i].o_var) = (opt[os-1] & (1L << ob)) ? TRUE : FALSE; } } } /* * Hack -- strip the "ghost" info * * XXX XXX XXX This is such a nasty hack it hurts. */ static void rd_ghost() { char buf[64]; /* Strip name */ rd_string(buf, 64); /* Strip old data */ strip_bytes(60); } /* * Read/Write the "extra" information */ static void rd_extra() { int i; byte tmp8u; rd_string(player_name, 32); rd_string(died_from, 80); for (i = 0; i < 4; i++) { rd_string(history[i], 60); } /* Class/Race/Gender/Spells */ rd_byte(&p_ptr->prace); rd_byte(&p_ptr->pclass); rd_byte(&p_ptr->male); rd_byte(&tmp8u); /* oops */ /* Special Race/Class info */ rd_byte(&p_ptr->hitdie); rd_byte(&p_ptr->expfact); /* Age/Height/Weight */ rd_s16b(&p_ptr->age); rd_s16b(&p_ptr->ht); rd_s16b(&p_ptr->wt); /* Read the stat info */ for (i = 0; i < 6; i++) rd_s16b(&p_ptr->stat_max[i]); for (i = 0; i < 6; i++) rd_s16b(&p_ptr->stat_cur[i]); strip_bytes(24); /* oops */ rd_s32b(&p_ptr->au); rd_s32b(&p_ptr->max_exp); rd_s32b(&p_ptr->exp); rd_u16b(&p_ptr->exp_frac); rd_s16b(&p_ptr->lev); rd_s16b(&p_ptr->mhp); rd_s16b(&p_ptr->chp); rd_u16b(&p_ptr->chp_frac); rd_s16b(&p_ptr->msp); rd_s16b(&p_ptr->csp); rd_u16b(&p_ptr->csp_frac); rd_s16b(&p_ptr->max_plv); rd_s16b(&p_ptr->max_dlv); /* More info */ strip_bytes(8); rd_s16b(&p_ptr->sc); strip_bytes(2); /* Read the flags */ strip_bytes(2); /* Old "rest" */ rd_s16b(&p_ptr->blind); rd_s16b(&p_ptr->paralyzed); rd_s16b(&p_ptr->confused); rd_s16b(&p_ptr->food); strip_bytes(4); /* Old "food_digested" / "protection" */ rd_s16b(&p_ptr->energy); rd_s16b(&p_ptr->fast); rd_s16b(&p_ptr->slow); rd_s16b(&p_ptr->afraid); rd_s16b(&p_ptr->cut); rd_s16b(&p_ptr->stun); rd_s16b(&p_ptr->poisoned); rd_s16b(&p_ptr->image); rd_s16b(&p_ptr->protevil); rd_s16b(&p_ptr->invuln); rd_s16b(&p_ptr->hero); rd_s16b(&p_ptr->shero); rd_s16b(&p_ptr->shield); rd_s16b(&p_ptr->blessed); rd_s16b(&p_ptr->tim_invis); rd_s16b(&p_ptr->word_recall); rd_s16b(&p_ptr->see_infra); rd_s16b(&p_ptr->tim_infra); rd_s16b(&p_ptr->oppose_fire); rd_s16b(&p_ptr->oppose_cold); rd_s16b(&p_ptr->oppose_acid); rd_s16b(&p_ptr->oppose_elec); rd_s16b(&p_ptr->oppose_pois); rd_byte(&p_ptr->confusing); rd_byte(&tmp8u); /* oops */ rd_byte(&tmp8u); /* oops */ rd_byte(&tmp8u); /* oops */ rd_byte(&p_ptr->searching); rd_byte(&p_ptr->maximize); rd_byte(&p_ptr->preserve); rd_byte(&tmp8u); /* Future use */ for (i = 0; i < 48; i++) rd_byte(&tmp8u); /* Skip the flags */ strip_bytes(12); /* Hack -- the two "special seeds" */ rd_u32b(&seed_flavor); rd_u32b(&seed_town); /* Special stuff */ rd_u16b(&panic_save); rd_u16b(&total_winner); rd_u16b(&noscore); /* Read "death" */ rd_byte(&tmp8u); death = tmp8u; /* Read "feeling" */ rd_byte(&tmp8u); feeling = tmp8u; /* Turn of last "feeling" */ rd_s32b(&old_turn); /* Current turn */ rd_s32b(&turn); } /* * Read the player inventory * * Note that the inventory changed in Angband 2.7.4. Two extra * pack slots were added and the equipment was rearranged. Note * that these two features combine when parsing old save-files, in * which items from the old "aux" slot are "carried", perhaps into * one of the two new "inventory" slots. * * Note that the inventory is "re-sorted" later by "dungeon()". */ static errr rd_inventory() { int slot = 0; object_type forge; /* No weight */ total_weight = 0; /* No items */ inven_cnt = 0; equip_cnt = 0; /* Read until done */ while (1) { u16b n; /* Get the next item index */ rd_u16b(&n); /* Nope, we reached the end */ if (n == 0xFFFF) break; /* Read the item */ rd_item(&forge); /* Hack -- verify item */ if (!forge.exists()) return 53; /* Wield equipment */ if (n >= INVEN_WIELD) { /* Structure copy */ inventory[n] = forge; /* Add the weight */ total_weight += forge.get_number() * forge.get_weight(); /* One more item */ equip_cnt++; } /* Warning -- backpack is full */ else if (inven_cnt == INVEN_PACK) { /* Oops */ note("Too many items in the inventory!"); /* Fail */ return (54); } /* Carry inventory */ else { /* Get a slot */ n = slot++; /* Structure copy */ inventory[n] = forge; /* Add the weight */ total_weight += forge.get_number() * forge.get_weight(); /* One more item */ inven_cnt++; } } /* Success */ return (0); } /* * Read the saved messages */ static void rd_messages() { int i; char buf[128]; s16b num; /* Hack -- old method used circular queue */ rd_s16b(&num); /* Read the messages */ for (i = 0; i < num; i++) { /* Read the message */ rd_string(buf, 128); /* Save the message */ message_add(buf); } } /* * New "cave grid" flags -- saved in savefile */ #define OLD_GRID_W_01 0x0001 /* Wall type (bit 1) */ #define OLD_GRID_W_02 0x0002 /* Wall type (bit 2) */ #define OLD_GRID_PERM 0x0004 /* Wall type is permanent */ #define OLD_GRID_QQQQ 0x0008 /* Unused */ #define OLD_GRID_MARK 0x0010 /* Grid is memorized */ #define OLD_GRID_GLOW 0x0020 /* Grid is illuminated */ #define OLD_GRID_ROOM 0x0040 /* Grid is part of a room */ #define OLD_GRID_ICKY 0x0080 /* Grid is anti-teleport */ /* * Masks for the new grid types */ #define OLD_GRID_WALL_MASK 0x0003 /* Wall type */ /* * Legal results of OLD_GRID_WALL_MASK */ #define OLD_GRID_WALL_NONE 0x0000 /* No wall */ #define OLD_GRID_WALL_MAGMA 0x0001 /* Magma vein */ #define OLD_GRID_WALL_QUARTZ 0x0002 /* Quartz vein */ #define OLD_GRID_WALL_GRANITE 0x0003 /* Granite wall */ /* * Read the dungeon (new method) * * XXX XXX XXX Angband 2.8.0 will totally change the dungeon info * * XXX XXX XXX Try to be more flexible about "too many monsters" * * XXX XXX XXX Mega-Hack -- attempt to convert pre-2.8.0 savefile * format into 2.8.0 internal format, by extracting the new cave * grid terrain feature flags. Note that we may have to move the * terrain feature extractors into the "rd_item()" function. */ static errr rd_dungeon() { int i, y, x; u16b start; u16b limit; cave_type *c_ptr; object_type *i_ptr; /* Header info */ rd_s16b(&dun_level); rd_s16b(&num_repro); rd_s16b(&py); rd_s16b(&px); rd_s16b(&cur_hgt); rd_s16b(&cur_wid); rd_s16b(&max_panel_rows); rd_s16b(&max_panel_cols); /* Read cave */ for (x = 0; x < cur_wid; x++) { for (y = 0; y < cur_hgt; y++) { /* Get the cave pointer */ c_ptr = &cave[y][x]; /* Read features/flags */ rd_byte(&c_ptr->feat); rd_byte(&c_ptr->info); } } /* Read the item count */ rd_u16b(&limit); /* Hack -- verify */ if (limit >= 512) { note(format("Too many (%d) object entries!", limit)); return (151); } /* Read the dungeon items */ for (i = 1; i < limit; i++) { int i_idx; object_type forge; /* Point at it */ i_ptr = &forge; /* Read the item */ rd_item(i_ptr); /* Access the item location */ c_ptr = i_ptr->c_ptr; /* Skip dead objects */ if (!i_ptr->exists()) continue; /* Get a new record */ i_idx = i_pop(); /* Oops */ if (!i_idx) { note(format("Too many (%d) objects!", i_max)); return (152); } /* Acquire place */ i_ptr = &i_list[i_idx]; /* Copy the item */ (*i_ptr) = forge; /* Mark the location */ c_ptr->i_idx = i_idx; } /* Extract index of first monster */ start = 1; /* Read the monster count */ rd_u16b(&limit); /* Hack -- verify */ if (limit >= 1024) { note(format("Too many (%d) monster entries!", limit)); return (161); } /* Read the monsters */ for (i = start; i < limit; i++) { int m_idx; monster_type *m_ptr; monster_race *r_ptr; monster_type forge; /* Forge */ m_ptr = &forge; /* Read the monster */ rd_monster(m_ptr); /* Access grid */ c_ptr = &cave[m_ptr->fy][m_ptr->fx]; /* Access race */ r_ptr = &r_info[m_ptr->r_idx]; /* Hack -- ignore "broken" monsters */ if (m_ptr->r_idx <= 0) continue; /* Hack -- ignore "player ghosts" */ if (m_ptr->r_idx >= MAX_R_IDX-1) continue; /* Get a new record */ m_idx = m_pop(); /* Oops */ if (!m_idx) { note(format("Too many (%d) monsters!", m_max)); return (162); } /* Acquire place */ m_ptr = &m_list[m_idx]; /* Copy the item */ (*m_ptr) = forge; /* Mark the location */ c_ptr->m_idx = m_idx; /* Count XXX XXX XXX */ r_ptr->cur_num++; } /* Hack -- clean up the dungeon */ for (y = 0; y < cur_hgt; y++) { for (x = 0; x < cur_wid; x++) { cave_type *c_ptr = &cave[y][x]; /* Hack -- convert nothing-ness into floors */ if (c_ptr->feat == 0x00) c_ptr->feat = 0x01; } } /* Read the dungeon */ character_dungeon = TRUE; /* Success */ return (0); } /* * Actually read the savefile * * Angband 2.8.0 will completely replace this code, see "save.c", * though this code will be kept to read pre-2.8.0 savefiles. */ static errr rd_savefile_new_aux(void) { int i; byte tmp8u; u16b tmp16u; u32b tmp32u; u32b n_x_check, n_v_check; u32b o_x_check, o_v_check; /* Mention the savefile version */ note(format("Loading a %d.%d.%d savefile...", sf_major, sf_minor, sf_patch)); /* Strip the version bytes */ strip_bytes(4); /* Hack -- decrypt */ xor_byte = sf_extra; /* Clear the checksums */ v_check = 0L; x_check = 0L; /* Operating system info */ rd_u32b(&sf_xtra); /* Time of savefile creation */ rd_u32b(&sf_when); /* Number of resurrections */ rd_u16b(&sf_lives); /* Number of times played */ rd_u16b(&sf_saves); /* Later use (always zero) */ rd_u32b(&tmp32u); /* Later use (always zero) */ rd_u32b(&tmp32u); /* Then the options */ rd_options(); if (arg_fiddle) note("Loaded Option Flags"); /* Then the "messages" */ rd_messages(); if (arg_fiddle) note("Loaded Messages"); /* Monster Memory */ rd_u16b(&tmp16u); /* Incompatible save files */ if (tmp16u > MAX_R_IDX) { note(format("Too many (%u) monster races!", tmp16u)); return (21); } /* Read the available records */ for (i = 0; i < tmp16u; i++) { monster_race *r_ptr; /* Read the lore */ rd_lore(i); /* Access that monster */ r_ptr = &r_info[i]; } if (arg_fiddle) note("Loaded Monster Memory"); /* Object Memory */ rd_u16b(&tmp16u); /* Incompatible save files */ if (tmp16u > MAX_K_IDX) { note(format("Too many (%u) object kinds!", tmp16u)); return (22); } /* Read the object memory */ for (i = 0; i < tmp16u; i++) { byte tmp8u; object_kind *k_ptr = &k_info[i]; rd_byte(&tmp8u); k_ptr->aware = (tmp8u & 0x01) ? TRUE: FALSE; k_ptr->tried = (tmp8u & 0x02) ? TRUE: FALSE; } if (arg_fiddle) note("Loaded Object Memory"); /* Load the Quests */ rd_u16b(&tmp16u); /* Incompatible save files */ if (tmp16u > 8) { note(format("Too many (%u) quests!", tmp16u)); return (23); } /* Load the Quests */ for (i = 0; i < tmp16u; i++) { rd_byte(&tmp8u); q_list[i].level = tmp8u; rd_byte(&tmp8u); rd_byte(&tmp8u); rd_byte(&tmp8u); } if (arg_fiddle) note("Loaded Quests"); /* Load the Artifacts */ rd_u16b(&tmp16u); /* Incompatible save files */ if (tmp16u > MAX_A_IDX) { note(format("Too many (%u) artifacts!", tmp16u)); return (24); } /* Read the artifact flags */ for (i = 0; i < tmp16u; i++) { rd_byte(&tmp8u); a_info[i].cur_num = tmp8u; rd_byte(&tmp8u); rd_byte(&tmp8u); rd_byte(&tmp8u); } if (arg_fiddle) note("Loaded Artifacts"); /* Read the extra stuff */ rd_extra(); if (arg_fiddle) note("Loaded extra information"); /* Read the player_hp array */ rd_u16b(&tmp16u); /* Incompatible save files */ if (tmp16u > PY_MAX_LEVEL) { note(format("Too many (%u) hitpoint entries!", tmp16u)); return (25); } /* Read the player_hp array */ for (i = 0; i < tmp16u; i++) { rd_s16b(&player_hp[i]); } /* Important -- Initialize the race/class */ rp_ptr = &race_info[p_ptr->prace]; cp_ptr = &class_info[p_ptr->pclass]; /* Important -- Choose the magic info */ mp_ptr = &magic_info[p_ptr->pclass]; /* Read spell info */ for (i = 0; i < 64; i++) { rd_byte(&spell_learned[i]); rd_byte(&spell_worked[i]); rd_byte(&spell_forgotten[i]); } for (i = 0; i < 64; i++) { rd_byte(&spell_order[i]); } /* Read the inventory */ if (rd_inventory()) { note("Unable to read inventory"); return (21); } /* Read the stores */ rd_u16b(&tmp16u); for (i = 0; i < tmp16u; i++) { if (rd_store(i)) return (22); } /* I'm not dead yet... */ if (!death) { /* Dead players have no dungeon */ note("Restoring Dungeon..."); if (rd_dungeon()) { note("Error reading dungeon data"); return (34); } /* Read the ghost info */ rd_ghost(); } /* Save the checksum */ n_v_check = v_check; /* Read the old checksum */ rd_u32b(&o_v_check); /* Verify */ if (o_v_check != n_v_check) { note("Invalid checksum"); return (11); } /* Save the encoded checksum */ n_x_check = x_check; /* Read the checksum */ rd_u32b(&o_x_check); /* Verify */ if (o_x_check != n_x_check) { note("Invalid encoded checksum"); return (11); } /* Hack -- no ghosts */ r_info[MAX_R_IDX-1].max_num = 0; /* Success */ return (0); } /* * Actually read the savefile * * Angband 2.8.0 will completely replace this code, see "save.c", * though this code will be kept to read pre-2.8.0 savefiles. */ errr rd_savefile_new(void) { errr err; /* The savefile is a binary file */ fff = my_fopen(savefile, "rb"); /* Paranoia */ if (!fff) return (-1); /* Call the sub-function */ err = rd_savefile_new_aux(); /* Check for errors */ if (ferror(fff)) err = -1; /* Close the file */ my_fclose(fff); /* Result */ return (err); }