diff options
Diffstat (limited to 'src/loadsave.cc')
-rw-r--r-- | src/loadsave.cc | 2630 |
1 files changed, 2630 insertions, 0 deletions
diff --git a/src/loadsave.cc b/src/loadsave.cc new file mode 100644 index 00000000..3c843a36 --- /dev/null +++ b/src/loadsave.cc @@ -0,0 +1,2630 @@ +#include "loadsave.hpp" +#include "loadsave.h" + +#include "artifact_type.hpp" +#include "birth.hpp" +#include "cave_type.hpp" +#include "dungeon_info_type.hpp" +#include "ego_item_type.hpp" +#include "game.hpp" +#include "init1.hpp" +#include "init2.hpp" +#include "levels.hpp" +#include "messages.hpp" +#include "modules.hpp" +#include "monster2.hpp" +#include "monster_race.hpp" +#include "monster_type.hpp" +#include "object1.hpp" +#include "object2.hpp" +#include "object_kind.hpp" +#include "options.hpp" +#include "player_class.hpp" +#include "player_level_flag.hpp" +#include "player_race.hpp" +#include "player_race_mod.hpp" +#include "player_type.hpp" +#include "hooks.hpp" +#include "skill_type.hpp" +#include "store_type.hpp" +#include "tables.hpp" +#include "timer_type.hpp" +#include "town_type.hpp" +#include "util.hpp" +#include "util.h" +#include "wilderness_map.hpp" +#include "variable.h" +#include "variable.hpp" +#include "xtra2.hpp" +#include "z-rand.hpp" + +#include <boost/filesystem.hpp> +#include <cassert> +#include <fmt/format.h> +#include <memory> + +static u32b vernum; /* Version flag */ +static FILE *fff; /* Local savefile ptr */ + +/* + * Show information on the screen, one line at a time. + * + * Avoid the top two lines, to avoid interference with "msg_print()". + */ +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(); +} + +/** + * Load/save flag + */ +enum class ls_flag_t { + LOAD = 3, + SAVE = 7 +}; + +/** + * Structure for loading/saving option values + */ +namespace { + +struct option_value { + std::string name; + bool_ value; +}; + +} // namespace (anonymous) + + +/* + * Basic byte-level reading from savefile. + */ +static byte sf_get() +{ + byte c; + + /* Get a character, decode the value */ + c = getc(fff) & 0xFF; + + /* Return the value */ + return (c); +} + + +static void sf_put(byte v) +{ + putc((int)v, fff); +} + +/* + * Size-aware read/write routines for the savefile, do all their + * work through sf_get and sf_put. + */ +static void do_byte(byte *v, ls_flag_t flag) +{ + switch (flag) + { + case ls_flag_t::LOAD: + { + *v = sf_get(); + return; + } + case ls_flag_t::SAVE: + { + byte val = *v; + sf_put(val); + return; + } + } +} + +static void do_char(char *c, ls_flag_t flag) +{ + do_byte((byte *) c, flag); +} + +static void do_bool(bool_ *f, ls_flag_t flag) +{ + byte b = *f; + do_byte(&b, flag); + if (flag == ls_flag_t::LOAD) + { + *f = b; + } +} + +static void do_std_bool(bool *x, ls_flag_t flag) +{ + switch (flag) + { + case ls_flag_t::LOAD: + { + *x = (sf_get() != 0); + return; + } + case ls_flag_t::SAVE: + { + byte val = (*x) ? 1 : 0; + sf_put(val); + return; + } + } +} + +static void do_u16b(u16b *v, ls_flag_t flag) +{ + switch (flag) + { + case ls_flag_t::LOAD: + { + (*v) = sf_get(); + (*v) |= ((u16b)(sf_get()) << 8); + return; + } + case ls_flag_t::SAVE: + { + u16b val; + val = *v; + sf_put((byte)(val & 0xFF)); + sf_put((byte)((val >> 8) & 0xFF)); + return; + } + } +} + +static void do_s16b(s16b *ip, ls_flag_t flag) +{ + do_u16b((u16b *)ip, flag); +} + +static void do_u32b(u32b *ip, ls_flag_t flag) +{ + switch(flag) + { + case ls_flag_t::LOAD: + { + (*ip) = sf_get(); + (*ip) |= ((u32b)(sf_get()) << 8); + (*ip) |= ((u32b)(sf_get()) << 16); + (*ip) |= ((u32b)(sf_get()) << 24); + return; + } + case ls_flag_t::SAVE: + { + u32b val = *ip; + sf_put((byte)(val & 0xFF)); + sf_put((byte)((val >> 8) & 0xFF)); + sf_put((byte)((val >> 16) & 0xFF)); + sf_put((byte)((val >> 24) & 0xFF)); + return; + } + } +} + +static void do_s32b(s32b *ip, ls_flag_t flag) +{ + do_u32b((u32b *)ip, flag); +} + +static void save_std_string(std::string const *s) +{ + // Length prefix. + u32b saved_size = s->size(); + do_u32b(&saved_size, ls_flag_t::SAVE); + // Save each character + for (auto c: *s) + { + sf_put(c); + } +} + +static std::string load_std_string() +{ + // Length prefix. + u32b saved_size; + do_u32b(&saved_size, ls_flag_t::LOAD); + // Convert to size_t + std::size_t n = saved_size; + // Make sure we reserve space rather than resizing as we go. + std::string s; + s.reserve(n); + // Read each character + for (std::size_t i = 0; i < n; i++) + { + s += sf_get(); + } + // Done + return s; +} + + +static void do_std_string(std::string &s, ls_flag_t flag) +{ + switch (flag) + { + case ls_flag_t::LOAD: + s = load_std_string(); + break; + case ls_flag_t::SAVE: + save_std_string(&s); + break; + } +} + +static void do_option_value(option_value *option_value, ls_flag_t flag) +{ + do_std_string(option_value->name, flag); + do_bool(&option_value->value, flag); +} + + +namespace { + +/** + * Load/save flag set + */ +template<std::size_t Tiers> void do_flag_set(flag_set<Tiers> *flags, ls_flag_t flag) +{ + for (std::size_t i = 0; i < flags->size(); i++) + { + do_u32b(&(*flags)[i], flag); + } +} + +template<typename T, typename F> void do_vector(ls_flag_t flag, std::vector<T> &v, F f) +{ + u32b n = v.size(); + + do_u32b(&n, flag); + + if (flag == ls_flag_t::LOAD) + { + v.clear(); // Make sure it's empty + v.reserve(n); + std::fill_n(std::back_inserter(v), n, T()); + } + + for (std::size_t i = 0; i < n; i++) + { + f(&v[i], flag); + } +} + +template<typename A, typename F> void do_array(std::string const &what, ls_flag_t flag, A &array, std::size_t size, F f) +{ + // Save/load size. + u32b n = size; + do_u32b(&n, flag); + + // Check that we don't overflow the array. + if (flag == ls_flag_t::LOAD) + { + if (n > size) + { + note(fmt::format("Too many {:s}: {:d} > {:d}! Game may act strangely or crash.", what, n, size).c_str()); + } + } + + // Load/save the contents of the array. + for (std::size_t i = 0; i < n; i++) + { + f(&array[i], flag); + } +} + +static void do_bytes(ls_flag_t flag, std::uint8_t *buf, std::size_t n) +{ + for (std::size_t i = 0; i < n; i++) + { + do_byte(&buf[i], flag); + } +}; + +static void do_seed(seed_t *seed, ls_flag_t flag) +{ + uint8_t buf[seed_t::n_bytes]; + + if (flag == ls_flag_t::SAVE) + { + seed->to_bytes(buf); + } + + do_bytes(flag, buf, sizeof(buf)); + + if (flag == ls_flag_t::LOAD) + { + *seed = seed_t::from_bytes(buf); + } +} + +} // namespace (anonymous) + + +/* + * Load/Save quick start data + */ +static void do_quick_start(ls_flag_t flag, birther &previous_char) +{ + do_s16b(&previous_char.race, flag); + do_s16b(&previous_char.rmod, flag); + do_s16b(&previous_char.pclass, flag); + do_s16b(&previous_char.spec, flag); + do_byte(&previous_char.quests, flag); + do_byte(&previous_char.god, flag); + do_s32b(&previous_char.grace, flag); + do_s32b(&previous_char.au, flag); + + for (std::size_t i = 0; i < 6; i++) + { + do_s16b(&(previous_char.stat[i]), flag); + } + do_s16b(&previous_char.luck, flag); + + do_bool(&previous_char.quick_ok, flag); +} + +static void do_skill_modifier(skill_modifier *s, ls_flag_t flag) +{ + do_char(&s->basem, flag); + do_u32b(&s->base, flag); + do_char(&s->modm, flag); + do_s16b(&s->mod, flag); +} + +static void do_skill_modifiers(skill_modifiers *skill_modifiers, ls_flag_t flag) +{ + do_vector(flag, skill_modifiers->modifiers, do_skill_modifier); +} + +static void do_player_level_flag(player_level_flag *lflag, ls_flag_t flag) +{ + do_flag_set(&lflag->oflags, flag); + do_s16b(&lflag->pval, flag); +} + +/* + * The special saved subrace + */ +static void do_subrace(ls_flag_t flag) +{ + auto &race_mod_info = game->edit_data.race_mod_info; + + player_race_mod *sr_ptr = &race_mod_info[SUBRACE_SAVE]; + int i; + + do_std_string(sr_ptr->title, flag); + do_std_string(sr_ptr->description, flag); + + do_bool(&sr_ptr->place, flag); + + for (i = 0; i < 6; i++) + { + do_s16b(&sr_ptr->ps.adj[i], flag); + } + + do_char(&sr_ptr->luck, flag); + do_s16b(&sr_ptr->mana, flag); + + do_s16b(&sr_ptr->ps.mhp, flag); + do_s16b(&sr_ptr->ps.exp, flag); + + do_char(&sr_ptr->infra, flag); + + do_vector(flag, sr_ptr->ps.powers, do_s16b); + + for (i = 0; i < BODY_MAX; i++) + { + do_char(&sr_ptr->body_parts[i], flag); + } + + do_flag_set(&sr_ptr->flags, flag); + + for (i = 0; i < PY_MAX_LEVEL + 1; i++) + { + do_player_level_flag(&sr_ptr->lflags[i], flag); + } + + do_byte(&sr_ptr->g_attr, flag); + do_char(&sr_ptr->g_char, flag); + + do_skill_modifiers(&sr_ptr->skill_modifiers, flag); +} + + +static void do_random_spell(random_spell *s_ptr, ls_flag_t flag) +{ + do_s16b(&s_ptr->mana, flag); + do_s16b(&s_ptr->fail, flag); + do_u32b(&s_ptr->proj_flags, flag); + do_byte(&s_ptr->GF, flag); + do_byte(&s_ptr->radius, flag); + do_byte(&s_ptr->dam_sides, flag); + do_byte(&s_ptr->dam_dice, flag); + do_byte(&s_ptr->level, flag); + do_std_bool(&s_ptr->untried, flag); +} + +static void do_level_marker(level_marker *marker, ls_flag_t flag) +{ + std::string v; + + if (flag == ls_flag_t::SAVE) + { + v = level_marker_values().stringify(*marker); + } + + do_std_string(v, flag); + + if (flag == ls_flag_t::LOAD) + { + bool valid = level_marker_values().parse(v.c_str(), marker); + if (!valid) + { + note(fmt::format("Bad level marker: {}!", v).c_str()); + abort(); + } + } +} + +/* + * Misc. other data + */ +static bool_ do_extra(ls_flag_t flag) +{ + auto const &d_info = game->edit_data.d_info; + auto &s_info = game->s_info; + + do_std_string(game->player_name, flag); + + do_std_string(game->died_from, flag); + + /* Handle the special levels info */ + { + byte tmp8u = d_info.size(); + u16b tmp16u = MAX_DUNGEON_DEPTH; + + do_byte(&tmp8u, flag); + + if (flag == ls_flag_t::LOAD) + { + if (tmp8u > d_info.size()) + { + note(format("Too many dungeon types!", static_cast<int>(tmp8u))); + } + } + + do_u16b(&tmp16u, flag); + + if (flag == ls_flag_t::LOAD) + { + if (tmp16u > MAX_DUNGEON_DEPTH) + { + note(format("Too many (%d) max level by dungeon type!", static_cast<int>(tmp16u))); + } + } + + /* Load the special levels history */ + for (std::size_t i = 0; i < tmp8u; i++) + { + for (std::size_t j = 0; j < tmp16u; j++) + { + do_level_marker(&game->level_markers[j][i], flag); + } + } + } + + do_std_bool(&game->generate_special_feeling, flag); + + /* Load the quick start data */ + do_quick_start(flag, game->previous_char); + + /* Load/save the special subrace */ + do_subrace(flag); + + /* Race/Class/Gender/Spells */ + do_s32b(&p_ptr->lives, flag); + do_byte(&p_ptr->prace, flag); + do_byte(&p_ptr->pracem, flag); + do_byte(&p_ptr->pclass, flag); + do_byte(&p_ptr->pspec, flag); + do_byte(&p_ptr->mimic_form, flag); + do_s16b(&p_ptr->mimic_level, flag); + + do_byte(&p_ptr->hitdie, flag); + do_u16b(&p_ptr->expfact, flag); + + /* Dump the stats (maximum and current) */ + for (std::size_t i = 0; i < 6; ++i) do_s16b(&p_ptr->stat_max[i], flag); + for (std::size_t i = 0; i < 6; ++i) do_s16b(&p_ptr->stat_cur[i], flag); + for (std::size_t i = 0; i < 6; ++i) do_s16b(&p_ptr->stat_cnt[i], flag); + for (std::size_t i = 0; i < 6; ++i) do_s16b(&p_ptr->stat_los[i], flag); + + // Skills + { + do_s16b(&p_ptr->skill_points, flag); + do_s16b(&p_ptr->skill_last_level, flag); + do_s16b(&p_ptr->melee_style, flag); + do_s16b(&p_ptr->use_piercing_shots, flag); + + do_array("skills", flag, s_info, s_info.size(), + [](auto skill, auto flag) -> void { + do_s32b(&skill->value, flag); + do_s32b(&skill->mod, flag); + do_std_bool(&skill->dev, flag); + do_std_bool(&skill->hidden, flag); + } + ); + } + + // Abilities + do_vector(flag, p_ptr->abilities, do_u16b); + + // Miscellaneous + do_s16b(&p_ptr->luck_base, flag); + do_s16b(&p_ptr->luck_max, flag); + + do_s32b(&p_ptr->au, flag); + + do_s32b(&p_ptr->max_exp, flag); + do_s32b(&p_ptr->exp, flag); + do_u16b(&p_ptr->exp_frac, flag); + do_s16b(&p_ptr->lev, flag); + + do_s16b(&p_ptr->town_num, flag); /* -KMW- */ + + /* Write arena and rewards information -KMW- */ + do_s16b(&p_ptr->inside_quest, flag); + + + /* Save/load spellbinder */ + do_byte(&p_ptr->spellbinder.trigger, flag); + do_vector(flag, p_ptr->spellbinder.spell_idxs, do_u32b); + + // Quest status + do_array("plot quests", flag, plots, MAX_PLOTS, + [](auto plot, auto flag) -> void { + do_s16b(plot, flag); + } + ); + + // Random quests + do_array("random quests", flag, random_quests, MAX_RANDOM_QUEST, + [](auto q, auto flag) -> void { + do_byte(&q->type, flag); + do_s16b(&q->r_idx, flag); + do_std_bool(&q->done, flag); + } + ); + + do_s16b(&p_ptr->oldpx, flag); + do_s16b(&p_ptr->oldpy, flag); + + do_s16b(&p_ptr->mhp, flag); + do_s16b(&p_ptr->chp, flag); + do_u16b(&p_ptr->chp_frac, flag); + do_s16b(&p_ptr->hp_mod, flag); + + do_s16b(&p_ptr->msane, flag); + do_s16b(&p_ptr->csane, flag); + do_u16b(&p_ptr->csane_frac, flag); + + do_s16b(&p_ptr->msp, flag); + do_s16b(&p_ptr->csp, flag); + do_u16b(&p_ptr->csp_frac, flag); + + /* Gods */ + do_s32b(&p_ptr->grace, flag); + do_s32b(&p_ptr->grace_delay, flag); + do_bool(&p_ptr->praying, flag); + do_s16b(&p_ptr->melkor_sacrifice, flag); + do_byte(&p_ptr->pgod, flag); + + do_s16b(&p_ptr->max_plv, flag); + + // Max dungeon levels + { + byte tmp8u = d_info.size(); + + do_byte(&tmp8u, flag); + + for (std::size_t i = 0; i < tmp8u; i++) + { + s16b tmp16s = max_dlv[i]; + + do_s16b(&tmp16s, flag); + + if ((flag == ls_flag_t::LOAD) && (i <= d_info.size())) + { + max_dlv[i] = tmp16s; + } + } + } + + /* Repair max player level??? */ + if ((flag == ls_flag_t::LOAD) && (p_ptr->max_plv < p_ptr->lev)) + { + p_ptr->max_plv = p_ptr->lev; + } + + /* Help */ + do_std_bool(&p_ptr->help.enabled, flag); + for (std::size_t i = 0; i < HELP_MAX; i++) + { + do_std_bool(&p_ptr->help.activated[i], flag); + } + + /* More info */ + do_s16b(&p_ptr->blind, flag); + do_s16b(&p_ptr->paralyzed, flag); + do_s16b(&p_ptr->confused, flag); + do_s16b(&p_ptr->food, flag); + do_s32b(&p_ptr->energy, flag); + do_s16b(&p_ptr->fast, flag); + do_s16b(&p_ptr->speed_factor, flag); + do_s16b(&p_ptr->slow, flag); + do_s16b(&p_ptr->afraid, flag); + do_s16b(&p_ptr->cut, flag); + do_s16b(&p_ptr->stun, flag); + do_s16b(&p_ptr->poisoned, flag); + do_s16b(&p_ptr->image, flag); + do_s16b(&p_ptr->protevil, flag); + do_s16b(&p_ptr->invuln, flag); + do_s16b(&p_ptr->hero, flag); + do_s16b(&p_ptr->shero, flag); + do_s16b(&p_ptr->shield, flag); + do_s16b(&p_ptr->shield_power, flag); + do_s16b(&p_ptr->shield_power_opt, flag); + do_s16b(&p_ptr->shield_power_opt2, flag); + do_s16b(&p_ptr->shield_opt, flag); + do_s16b(&p_ptr->blessed, flag); + do_s16b(&p_ptr->control, flag); + do_byte(&p_ptr->control_dir, flag); + do_s16b(&p_ptr->tim_precognition, flag); + do_s16b(&p_ptr->tim_thunder, flag); + do_s16b(&p_ptr->tim_thunder_p1, flag); + do_s16b(&p_ptr->tim_thunder_p2, flag); + do_s16b(&p_ptr->tim_project, flag); + do_s16b(&p_ptr->tim_project_dam, flag); + do_s16b(&p_ptr->tim_project_gf, flag); + do_s16b(&p_ptr->tim_project_rad, flag); + do_s16b(&p_ptr->tim_project_flag, flag); + + do_s16b(&p_ptr->tim_magic_breath, flag); + do_s16b(&p_ptr->tim_water_breath, flag); + + do_s16b(&p_ptr->tim_roots, flag); + do_s16b(&p_ptr->tim_roots_ac, flag); + do_s16b(&p_ptr->tim_roots_dam, flag); + + do_s16b(&p_ptr->tim_invis, flag); + do_s16b(&p_ptr->word_recall, flag); + do_byte(&p_ptr->recall_dungeon, flag); + do_s16b(&p_ptr->see_infra, flag); + do_s16b(&p_ptr->tim_infra, flag); + do_s16b(&p_ptr->oppose_fire, flag); + do_s16b(&p_ptr->oppose_cold, flag); + do_s16b(&p_ptr->oppose_acid, flag); + do_s16b(&p_ptr->oppose_elec, flag); + do_s16b(&p_ptr->oppose_pois, flag); + do_s16b(&p_ptr->oppose_cc, flag); + + do_s16b(&p_ptr->tim_esp, flag); + do_s16b(&p_ptr->tim_wraith, flag); + do_s16b(&p_ptr->tim_ffall, flag); + do_s16b(&p_ptr->tim_fly, flag); + do_s16b(&p_ptr->tim_poison, flag); + do_s16b(&p_ptr->tim_invisible, flag); + do_s16b(&p_ptr->tim_inv_pow, flag); + do_s16b(&p_ptr->tim_mimic, flag); + do_s16b(&p_ptr->lightspeed, flag); + do_s16b(&p_ptr->tim_lite, flag); + do_s16b(&p_ptr->tim_regen, flag); + do_s16b(&p_ptr->tim_regen_pow, flag); + do_s16b(&p_ptr->holy, flag); + do_s16b(&p_ptr->immov_cntr, flag); + do_s16b(&p_ptr->strike, flag); + do_s16b(&p_ptr->tim_reflect, flag); + do_s16b(&p_ptr->tim_deadly, flag); + do_s16b(&p_ptr->prob_travel, flag); + do_s16b(&p_ptr->disrupt_shield, flag); + do_s16b(&p_ptr->parasite, flag); + do_s16b(&p_ptr->parasite_r_idx, flag); + do_s16b(&p_ptr->absorb_soul, flag); + do_s32b(&p_ptr->inertia_controlled_spell, flag); + do_s16b(&p_ptr->last_rewarded_level, flag); + + do_array("corruptions", flag, p_ptr->corruptions, CORRUPTIONS_MAX, + [](auto corruption, auto flag) -> void { + do_bool(corruption, flag); + } + ); + + do_bool(&p_ptr->corrupt_anti_teleport_stopped, flag); + + do_byte(&p_ptr->confusing, flag); + do_bool(&p_ptr->black_breath, flag); + do_bool(&fate_flag, flag); + do_bool(&ambush_flag, flag); + do_byte(&p_ptr->allow_one_death, flag); + + do_s16b(&no_breeds, flag); + + /* Auxilliary variables */ + do_u32b(&p_ptr->mimic_extra, flag); + do_u32b(&p_ptr->antimagic_extra, flag); + do_u32b(&p_ptr->music_extra, flag); + do_u32b(&p_ptr->necro_extra, flag); + do_u32b(&p_ptr->necro_extra2, flag); + + do_u16b(&p_ptr->body_monster, flag); + do_bool(&p_ptr->disembodied, flag); + + /* Are we in astral mode? */ + do_bool(&p_ptr->astral, flag); + + // Powers + do_array("powers", flag, p_ptr->powers_mod, POWER_MAX, + [](auto pwr, auto flag) -> void { + do_bool(pwr, flag); + } + ); + + /* The tactic */ + do_char(&p_ptr->tactic, flag); + + /* The movement */ + do_char(&p_ptr->movement, flag); + + /* The comapnions killed */ + do_s16b(&p_ptr->companion_killed, flag); + + /* The fate */ + do_bool(&p_ptr->no_mortal, flag); + + /* Random spells */ + do_vector(flag, p_ptr->random_spells, do_random_spell); + + /* Random seed for object flavors. */ + do_seed(&seed_flavor(), flag); + + /* Special stuff */ + do_u16b(&total_winner, flag); + do_u16b(&has_won, flag); + do_u16b(&noscore, flag); + + /* Write death */ + do_bool(&death, flag); + + /* Level feeling */ + do_s16b(&feeling, flag); + + /* Turn of last "feeling" */ + do_s32b(&old_turn, flag); + + /* Current turn */ + do_s32b(&turn, flag); + + return TRUE; +} + + +/* + * Read a monster + */ +static void do_monster(monster_type *m_ptr, ls_flag_t flag) +{ + /* Read the monster race */ + do_s16b(&m_ptr->r_idx, flag); + + do_u16b(&m_ptr->ego, flag); + + /* Read the other information */ + do_byte(&m_ptr->fy, flag); + do_byte(&m_ptr->fx, flag); + + do_s32b(&m_ptr->hp, flag); + do_s32b(&m_ptr->maxhp, flag); + + do_s16b(&m_ptr->csleep, flag); + do_byte(&m_ptr->mspeed, flag); + do_byte(&m_ptr->energy, flag); + do_byte(&m_ptr->stunned, flag); + do_byte(&m_ptr->confused, flag); + do_byte(&m_ptr->monfear, flag); + do_u32b(&m_ptr->smart, flag); + do_s16b(&m_ptr->status, flag); + do_s16b(&m_ptr->possessor, flag); + do_byte(&m_ptr->speed, flag); + do_byte(&m_ptr->level, flag); + do_s16b(&m_ptr->ac, flag); + do_s32b(&m_ptr->exp, flag); + do_s16b(&m_ptr->target, flag); + + do_s16b(&m_ptr->bleeding, flag); + do_s16b(&m_ptr->poisoned, flag); + + do_s32b(&m_ptr->mflag, flag); + + if (flag == ls_flag_t::LOAD) + { + m_ptr->mflag &= PERM_MFLAG_MASK; + } + + /* Attacks */ + do_array("attacks", flag, m_ptr->blow, 4, + [](auto blow, auto flag) { + do_byte(&blow->method, flag); + do_byte(&blow->effect, flag); + do_byte(&blow->d_dice, flag); + do_byte(&blow->d_side, flag); + } + ); +} + + + +/* + * Determine if an item can be wielded/worn (e.g. helmet, sword, bow, arrow) + */ +static bool_ wearable_p(object_type *o_ptr) +{ + /* Valid "tval" codes */ + switch (o_ptr->tval) + { + case TV_WAND: + case TV_STAFF: + case TV_ROD: + case TV_ROD_MAIN: + case TV_SHOT: + case TV_ARROW: + case TV_BOLT: + case TV_BOOMERANG: + case TV_BOW: + case TV_DIGGING: + case TV_HAFTED: + case TV_POLEARM: + case TV_MSTAFF: + case TV_SWORD: + case TV_AXE: + 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_SCROLL: + case TV_LITE: + case TV_POTION: + case TV_POTION2: + case TV_AMULET: + case TV_RING: + case TV_HYPNOS: + case TV_INSTRUMENT: + case TV_DAEMON_BOOK: + case TV_TOOL: + { + return (TRUE); + } + } + + /* Nope */ + return (FALSE); +} + + +/* + * rd/wr an object + */ +static void do_item(object_type *o_ptr, ls_flag_t flag) +{ + auto &k_info = game->edit_data.k_info; + auto &a_info = game->edit_data.a_info; + auto &e_info = game->edit_data.e_info; + + byte old_dd; + byte old_ds; + + /* Kind */ + do_s16b(&o_ptr->k_idx, flag); + + /* Location */ + do_byte(&o_ptr->iy, flag); + do_byte(&o_ptr->ix, flag); + + /* Type/Subtype */ + do_byte(&o_ptr->tval, flag); + do_byte(&o_ptr->sval, flag); + + /* Special pval */ + do_s32b(&o_ptr->pval, flag); + + /* Special pval */ + do_s16b(&o_ptr->pval2, flag); + + /* Special pval */ + do_s32b(&o_ptr->pval3, flag); + + do_byte(&o_ptr->discount, flag); + do_byte(&o_ptr->number, flag); + do_s32b(&o_ptr->weight, flag); + + do_byte(&o_ptr->name1, flag); + do_s16b(&o_ptr->name2, flag); + do_s16b(&o_ptr->name2b, flag); + do_s16b(&o_ptr->timeout, flag); + + do_s16b(&o_ptr->to_h, flag); + do_s16b(&o_ptr->to_d, flag); + do_s16b(&o_ptr->to_a, flag); + + do_s16b(&o_ptr->ac, flag); + + /* We do special processing of this flag when reading */ + if (flag == ls_flag_t::LOAD) + { + do_byte(&old_dd, ls_flag_t::LOAD); + do_byte(&old_ds, ls_flag_t::LOAD); + } + if (flag == ls_flag_t::SAVE) + { + do_byte(&o_ptr->dd, ls_flag_t::SAVE); + do_byte(&o_ptr->ds, ls_flag_t::SAVE); + } + + do_byte(&o_ptr->ident, flag); + + do_byte(&o_ptr->marked, flag); + + /* flags */ + do_flag_set(&o_ptr->art_flags, flag); + + /* obvious flags */ + do_flag_set(&o_ptr->art_oflags, flag); + + /* Monster holding object */ + do_s16b(&o_ptr->held_m_idx, flag); + + /* Special powers */ + do_byte(&o_ptr->xtra1, flag); + do_s16b(&o_ptr->xtra2, flag); + + do_byte(&o_ptr->elevel, flag); + do_s32b(&o_ptr->exp, flag); + + /* Read the pseudo-id */ + do_byte(&o_ptr->sense, flag); + + /* Read the found info */ + do_byte(&o_ptr->found, flag); + do_s16b(&o_ptr->found_aux1, flag); + do_s16b(&o_ptr->found_aux2, flag); + do_s16b(&o_ptr->found_aux3, flag); + do_s16b(&o_ptr->found_aux4, flag); + + // Inscription + do_std_string(o_ptr->inscription, flag); + + // Artifact name + do_std_string(o_ptr->artifact_name, flag); + + /* Stick any more shared code before this. The rest + of this function is reserved for ls_flag_t::LOAD's + cleanup functions */ + + if (flag == ls_flag_t::SAVE) return; + + /*********** END OF ls_flag_t::SAVE ***************/ + + /* Obtain the "kind" template */ + object_kind *k_ptr = &k_info[o_ptr->k_idx]; + + /* Obtain tval/sval from k_info */ + o_ptr->tval = k_ptr->tval; + if (o_ptr->tval != TV_RANDART) o_ptr->sval = k_ptr->sval; + + + /* Repair non "wearable" items */ + if (!wearable_p(o_ptr)) + { + /* Acquire correct fields */ + o_ptr->to_h = k_ptr->to_h; + o_ptr->to_d = k_ptr->to_d; + o_ptr->to_a = k_ptr->to_a; + + /* Acquire correct fields */ + o_ptr->ac = k_ptr->ac; + o_ptr->dd = k_ptr->dd; + o_ptr->ds = k_ptr->ds; + + /* All done */ + return; + } + + + /* Paranoia */ + if (o_ptr->name1) + { + /* Obtain the artifact info */ + auto a_ptr = &a_info[o_ptr->name1]; + + /* Verify that artifact */ + if (!a_ptr->name) o_ptr->name1 = 0; + } + + /* Paranoia */ + if (o_ptr->name2) + { + ego_item_type *e_ptr; + + /* Obtain the ego-item info */ + e_ptr = &e_info[o_ptr->name2]; + + /* Verify that ego-item */ + if (!e_ptr->name) o_ptr->name2 = 0; + } + + + /* Acquire standard fields */ + o_ptr->ac = k_ptr->ac; + o_ptr->dd = k_ptr->dd; + o_ptr->ds = k_ptr->ds; + + /* Artifacts */ + if (o_ptr->name1) + { + artifact_type *a_ptr; + + /* Obtain the artifact info */ + a_ptr = &a_info[o_ptr->name1]; + + /* Acquire new artifact fields */ + o_ptr->ac = a_ptr->ac; + o_ptr->dd = a_ptr->dd; + o_ptr->ds = a_ptr->ds; + + /* Acquire new artifact weight */ + o_ptr->weight = a_ptr->weight; + } + + /* Ego items */ + if (o_ptr->name2) + { + o_ptr->dd = old_dd; + o_ptr->ds = old_ds; + } + + if (!o_ptr->artifact_name.empty()) /* A random artifact */ + { + o_ptr->dd = old_dd; + o_ptr->ds = old_ds; + } +} + +static void do_cave_type(cave_type *c_ptr, ls_flag_t flag) +{ + do_u16b(&c_ptr->info, flag); + do_byte(&c_ptr->feat, flag); + do_byte(&c_ptr->mimic, flag); + do_s16b(&c_ptr->special, flag); + do_s16b(&c_ptr->special2, flag); + do_s16b(&c_ptr->inscription, flag); + do_byte(&c_ptr->mana, flag); + do_s16b(&c_ptr->effect, flag); +} + +static void do_grid(ls_flag_t flag) +{ + for (int y = 0; y < cur_hgt; y++) + { + for (int x = 0; x < cur_wid; x++) + { + do_cave_type(&cave[y][x], flag); + } + } +} + +static bool do_objects(ls_flag_t flag, bool no_companions) +{ + if (flag == ls_flag_t::SAVE) + { + // Compact everything before saving + compact_objects(0); + compact_monsters(0); + } + + u16b n_objects = o_max; + + if (flag == ls_flag_t::SAVE) + { + u16b tmp16u = n_objects; + + if (no_companions) + { + for (int i = 1; i < o_max; i++) + { + object_type *o_ptr = &o_list[i]; + + if (o_ptr->held_m_idx && (m_list[o_ptr->held_m_idx].status == MSTATUS_COMPANION)) + { + tmp16u--; + } + } + } + + do_u16b(&tmp16u, flag); + } + else + { + do_u16b(&n_objects, flag); + } + + /* Verify maximum */ + if ((flag == ls_flag_t::LOAD) && (n_objects > max_o_idx)) + { + note("Too many object entries!"); + return false; + } + + /* Dungeon items */ + if (flag == ls_flag_t::SAVE) + { + for (std::size_t i = 1; i < n_objects; i++) + { + auto o_ptr = &o_list[i]; + // Skip objects held by companions when no_companions is set + if (no_companions && o_ptr->held_m_idx && (m_list[o_ptr->held_m_idx].status == MSTATUS_COMPANION)) + { + continue; + } + + do_item(o_ptr, ls_flag_t::SAVE); + } + } + else if (flag == ls_flag_t::LOAD) + { + for (int i = 1; i < n_objects; i++) + { + /* Get a new record */ + int o_idx = o_pop(); + + /* Oops */ + if (i != o_idx) + { + note(format("Object allocation error (%d <> %d)", i, o_idx)); + return false; + } + + /* Acquire place */ + auto o_ptr = &o_list[o_idx]; + + /* Read the item */ + do_item(o_ptr, ls_flag_t::LOAD); + + /* Monster */ + if (o_ptr->held_m_idx) + { + /* Monster */ + monster_type *m_ptr = &m_list[o_ptr->held_m_idx]; + + /* Place the object */ + m_ptr->hold_o_idxs.push_back(o_idx); + } + + /* Dungeon */ + else + { + /* Access the item location */ + auto c_ptr = &cave[o_ptr->iy][o_ptr->ix]; + + /* Place the object */ + c_ptr->o_idxs.push_back(o_idx); + } + } + } + + return true; +} + + +static bool do_monsters(ls_flag_t flag, bool no_companions) +{ + auto &r_info = game->edit_data.r_info; + + u16b n_monsters = m_max; + + if (flag == ls_flag_t::SAVE) + { + u16b tmp16u = m_max; + + if (no_companions) + { + for (int i = 1; i < m_max; i++) + { + monster_type *m_ptr = &m_list[i]; + + if (m_ptr->status == MSTATUS_COMPANION) + { + tmp16u--; + } + } + } + + do_u16b(&tmp16u, flag); + } + else + { + do_u16b(&n_monsters, flag); + } + + /* Validate */ + if ((flag == ls_flag_t::LOAD) && (n_monsters > max_m_idx)) + { + note("Too many monster entries!"); + return false; + } + + /* Load/save the monsters */ + + if (flag == ls_flag_t::SAVE) + { + for (std::size_t i = 1; i < n_monsters; i++) + { + auto m_ptr = &m_list[i]; + + // Skip companions when no_companions is set + if (no_companions && m_ptr->status == MSTATUS_COMPANION) + { + continue; + } + + do_monster(m_ptr, ls_flag_t::SAVE); + } + } + else if (flag == ls_flag_t::LOAD) + { + for (int i = 1; i < n_monsters; i++) + { + /* Get a new record */ + int m_idx = m_pop(); + + /* Oops */ + if (i != m_idx) + { + note(format("Monster allocation error (%d <> %d)", i, m_idx)); + return false; + } + + /* Acquire monster */ + auto m_ptr = &m_list[m_idx]; + + /* Read the monster */ + do_monster(m_ptr, ls_flag_t::LOAD); + + /* Place in grid */ + auto c_ptr = &cave[m_ptr->fy][m_ptr->fx]; + c_ptr->m_idx = m_idx; + + /* Controlled? */ + if (m_ptr->mflag & MFLAG_CONTROL) + { + p_ptr->control = m_idx; + } + + /* Count as an alive member of race */ + auto r_ptr = &r_info[m_ptr->r_idx]; + r_ptr->cur_num++; + } + } + + /* Save/load pets */ + { + const std::size_t sz = + (flag == ls_flag_t::SAVE && !no_companions) ? max_m_idx : 0; + + do_array("pets", flag, km_list, sz, + [](auto m_ptr, auto flag) { + do_monster(m_ptr, flag); + } + ); + } + + return true; +} + +/* + * Handle dungeon + * + * The monsters/objects must be loaded in the same order + * that they were stored, since the actual indexes matter. + */ +static bool_ do_dungeon(ls_flag_t flag, bool no_companions) +{ + /* Header info */ + do_s16b(&dun_level, flag); + do_byte(&dungeon_type, flag); + do_s16b(&num_repro, flag); + do_s16b(&p_ptr->py, flag); + do_s16b(&p_ptr->px, flag); + do_s16b(&cur_hgt, flag); + do_s16b(&cur_wid, flag); + do_s16b(&max_panel_rows, flag); + do_s16b(&max_panel_cols, flag); + + do_flag_set(&dungeon_flags, flag); + + /* Last teleportation */ + do_s16b(&last_teleportation_y, flag); + do_s16b(&last_teleportation_y, flag); + + /* Spell effects */ + do_array("spell effects", flag, effects, MAX_EFFECTS, + [](auto effect, auto flag) -> void { + do_s16b(&effect->type, flag); + do_s16b(&effect->dam, flag); + do_s16b(&effect->time, flag); + do_u32b(&effect->flags, flag); + do_s16b(&effect->cx, flag); + do_s16b(&effect->cy, flag); + do_s16b(&effect->rad, flag); + } + ); + + /* To prevent bugs with evolving dungeons */ + for (std::size_t i = 0; i < 100; i++) + { + do_s16b(&floor_type[i], flag); + do_s16b(&fill_type[i], flag); + } + + if ((flag == ls_flag_t::LOAD) && (!dun_level && !p_ptr->inside_quest)) + { + int xstart = 0; + int ystart = 0; + /* Init the wilderness */ + process_dungeon_file("w_info.txt", &ystart, &xstart, cur_hgt, cur_wid, + TRUE, FALSE); + + /* Init the town */ + xstart = 0; + ystart = 0; + init_flags = 0; + process_dungeon_file("t_info.txt", &ystart, &xstart, cur_hgt, cur_wid, + TRUE, FALSE); + } + + do_grid(flag); + + /*** Objects ***/ + if (!do_objects(flag, no_companions)) + { + return FALSE; + } + + /*** Monsters ***/ + if (!do_monsters(flag, no_companions)) + { + return FALSE; + } + + /*** Success ***/ + + /* The dungeon is ready */ + if (flag == ls_flag_t::LOAD) character_dungeon = TRUE; + + /* Success */ + return (TRUE); +} + +/* Save the current persistent dungeon -SC- */ +void save_dungeon() +{ + char tmp[16]; + char name[1024], buf[5]; + + /* Save only persistent dungeons */ + if (!get_dungeon_save(buf) || (!dun_level)) return; + + /* Construct filename */ + sprintf(tmp, "%s.%s", game->player_base.c_str(), buf); + path_build(name, 1024, ANGBAND_DIR_SAVE, tmp); + + /* Open the file */ + fff = my_fopen(name, "wb"); + + /* Save the dungeon */ + do_dungeon(ls_flag_t::SAVE, true); + + my_fclose(fff); +} + + +/* + * Read a store + */ +static void do_store(store_type *str, ls_flag_t flag) +{ + // Store state + do_s32b(&str->store_open, flag); + do_u16b(&str->owner, flag); + do_s32b(&str->last_visit, flag); + + // Items in store + { + byte num = str->stock.size(); + + do_byte(&num, flag); + + if (flag == ls_flag_t::SAVE) + { + for (std::size_t i = 0; i < num; i++) + { + do_item(&str->stock[i], ls_flag_t::SAVE); + } + } + else if (flag == ls_flag_t::LOAD) + { + for (std::size_t i = 0; i < num; i++) + { + object_type forge; + object_wipe(&forge); + do_item(&forge, ls_flag_t::LOAD); + + if ((str->stock.size() < STORE_INVEN_MAX) && (str->stock.size() < str->stock_size)) + { + object_type stock_obj; + object_copy(&stock_obj, &forge); + str->stock.push_back(stock_obj); + } + } + } + } +} + +/* + * RNG state + */ +static void do_randomizer(ls_flag_t flag) +{ + std::string state; + + if (flag == ls_flag_t::SAVE) + { + state = get_complex_rng_state(); + } + + do_std_string(state, flag); + + if (flag == ls_flag_t::LOAD) + { + set_complex_rng_state(state); + } + + /* Accept */ + if (flag == ls_flag_t::LOAD) + { + set_complex_rng(); + } +} + +/* + * Handle options + * + * Normal options are stored as a set of 256 bit flags, + * plus a set of 256 bit masks to indicate which bit flags were defined + * at the time the savefile was created. This will allow new options + * to be added, and old options to be removed, at any time, without + * hurting old savefiles. + * + * The window options are stored in the same way, but note that each + * window gets 32 options, and their order is fixed by certain defines. + */ +static void do_options(ls_flag_t flag) +{ + int i, n; + + u32b oflag[8]; + u32b mask[8]; + + /*** Special info */ + + /* Read "delay_factor" */ + do_byte(&options->delay_factor, flag); + + /* Read "hitpoint_warn" */ + do_byte(&options->hitpoint_warn, flag); + + /*** Cheating options ***/ + do_bool(&wizard, flag); + do_bool(&options->cheat_peek, flag); + do_bool(&options->cheat_hear, flag); + do_bool(&options->cheat_room, flag); + do_bool(&options->cheat_xtra, flag); + do_bool(&options->cheat_live, flag); + + /*** Autosave options */ + do_bool(&options->autosave_l, flag); + do_bool(&options->autosave_t, flag); + do_s16b(&options->autosave_freq, flag); + + // Standard options + { + std::vector<option_value> option_values; + + // If we're saving we need to map to a vector of key-value pairs. + if (flag == ls_flag_t::SAVE) + { + for (auto const &option: options->standard_options) + { + option_values.emplace_back( + option_value { + option.o_text, + *option.o_var + } + ); + } + } + + // Read/write the option values + do_vector(flag, option_values, do_option_value); + + // If we're loading we need to set options based of the key-value pairs. + if (flag == ls_flag_t::LOAD) + { + // Go through all the options that were loaded. + for (auto const &option_value: option_values) + { + // We need to search through all the options + // that are actually in the game; we'll ignore + // saved options that are now gone. + const option_type *found_option; + for (auto const &option: options->standard_options) + { + if (option_value.name == option.o_text) + { + found_option = &option; + break; + } + } + + // If we found the option, we'll set the value. + if (found_option) + { + *(*found_option).o_var = option_value.value; + } + } + } + + } + + if (flag == ls_flag_t::LOAD) + { + /*** Window Options ***/ + + /* Read the window flags */ + for (n = 0; n < ANGBAND_TERM_MAX; n++) + { + do_u32b(&oflag[n], flag); + } + + /* Read the window masks */ + for (n = 0; n < ANGBAND_TERM_MAX; n++) + { + do_u32b(&mask[n], flag); + } + + /* Analyze the options */ + for (n = 0; n < ANGBAND_TERM_MAX; n++) + { + /* Analyze the options */ + for (i = 0; i < 32; i++) + { + /* Process valid flags */ + if (mask[n] & (1L << i)) + { + /* Process valid flags */ + if (window_mask[n] & (1L << i)) + { + /* Set */ + if (oflag[n] & (1L << i)) + { + /* Set */ + window_flag[n] |= (1L << i); + } + + /* Clear */ + else + { + /* Clear */ + window_flag[n] &= ~(1L << i); + } + } + } + } + } + } + + if (flag == ls_flag_t::SAVE) + { + /*** Window options ***/ + + /* Dump the flags */ + for (i = 0; i < ANGBAND_TERM_MAX; i++) + { + do_u32b(&window_flag[i], flag); + } + + /* Dump the masks */ + for (i = 0; i < ANGBAND_TERM_MAX; i++) + { + do_u32b(&window_mask[i], flag); + } + } +} + + +/* + * Handle player inventory. Note that the inventory is + * "re-sorted" later by "dungeon()". + */ +static bool_ do_inventory(ls_flag_t flag) +{ + auto const &a_info = game->edit_data.a_info; + + if (flag == ls_flag_t::LOAD) + { + int slot = 0; + + object_type forge; + object_type *q_ptr; + + /* No items */ + inven_cnt = 0; + equip_cnt = 0; + + /* Read until done */ + while (1) + { + u16b n; + + /* Get the next item index */ + do_u16b(&n, ls_flag_t::LOAD); + + /* Nope, we reached the end */ + if (n == 0xFFFF) break; + + /* Get local object */ + q_ptr = &forge; + + /* Wipe the object */ + object_wipe(q_ptr); + + /* Read the item */ + do_item(q_ptr, ls_flag_t::LOAD); + + /* Hack -- verify item */ + if (!q_ptr->k_idx) return (FALSE); + + /* Wield equipment */ + if (n >= INVEN_WIELD) + { + /* Copy object */ + object_copy(&p_ptr->inventory[n], q_ptr); + + /* Take care of item sets */ + if (q_ptr->name1) + { + wield_set(q_ptr->name1, a_info[q_ptr->name1].set, TRUE); + } + + /* 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 (FALSE); + } + + /* Carry inventory */ + else + { + /* Get a slot */ + n = slot++; + + /* Copy object */ + object_copy(&p_ptr->inventory[n], q_ptr); + + /* One more item */ + inven_cnt++; + } + } + } + if (flag == ls_flag_t::SAVE) + { + for (u16b i = 0; i < INVEN_TOTAL; i++) + { + object_type *o_ptr = &p_ptr->inventory[i]; + if (!o_ptr->k_idx) continue; + do_u16b(&i, flag); + do_item(o_ptr, flag); + } + // Sentinel + u16b sent = 0xFFFF; + do_u16b(&sent, ls_flag_t::SAVE); + } + /* Success */ + return (TRUE); +} + + +static void do_message(message &msg, ls_flag_t flag) +{ + do_std_string(msg.text, flag); + do_u32b(&msg.count, flag); + do_byte(&msg.color, flag); +} + + +/* + * Read the saved messages + */ +static void do_messages(ls_flag_t flag) +{ + auto &messages = game->messages; + + /* Save/load number of messages */ + s16b num = messages.size(); + do_s16b(&num, flag); + + /* Read the messages */ + for (int i = 0; i < num; i++) + { + message message; + + if (flag == ls_flag_t::SAVE) + { + message = messages.at(i); + } + + do_message(message, flag); + + if (flag == ls_flag_t::LOAD) + { + messages.add(message); + } + } +} + +/* Returns TRUE if we successfully load the dungeon */ +bool_ load_dungeon(char *ext) +{ + char tmp[16]; + char name[1024]; + byte old_dungeon_type = dungeon_type; + s16b old_dun = dun_level; + + /* Construct name */ + sprintf(tmp, "%s.%s", game->player_base.c_str(), ext); + path_build(name, 1024, ANGBAND_DIR_SAVE, tmp); + + /* Open the file */ + fff = my_fopen(name, "rb"); + + if (fff == NULL) + { + dun_level = old_dun; + dungeon_type = old_dungeon_type; + + my_fclose(fff); + return (FALSE); + } + + /* Read the dungeon */ + if (!do_dungeon(ls_flag_t::LOAD, false)) + { + dun_level = old_dun; + dungeon_type = old_dungeon_type; + + my_fclose(fff); + return (FALSE); + } + + dun_level = old_dun; + dungeon_type = old_dungeon_type; + + /* Done */ + my_fclose(fff); + return (TRUE); +} + +/* + * Load/save timers. + */ +static void do_timers(ls_flag_t flag) +{ + for (auto &&t_ptr: game->timers) + { + do_std_bool(&t_ptr->m_enabled, flag); + do_s32b(&t_ptr->m_delay, flag); + do_s32b(&t_ptr->m_countdown, flag); + } +} + +/* + * Load/save stores. + */ +static void do_stores(ls_flag_t flag) +{ + auto const &st_info = game->edit_data.st_info; + + // Indexes for "real" towns. + std::vector<byte> reals; + reals.reserve(max_towns); + + // Fill in the "real" towns if necessary. + if (flag == ls_flag_t::SAVE) + { + for (int i = 1; i < max_towns; i++) + { + if (!(town_info[i].flags & TOWN_REAL)) + { + continue; + } + reals.emplace_back(i); + } + } + + // Load/save + do_vector(flag, reals, do_byte); + + /* Read the stores */ + u16b n_stores = st_info.size(); + do_u16b(&n_stores, flag); + assert(n_stores <= st_info.size()); + + for (auto const z: reals) + { + if (!town_info[z].stocked) + { + create_stores_stock(z); + } + + for (int j = 0; j < n_stores; j++) + { + do_store(&town_info[z].store[j], flag); + } + } +} + +/* + * Monster memory + */ +static bool do_monster_lore(ls_flag_t flag) +{ + auto &r_info = game->edit_data.r_info; + + do_array("monster races", flag, r_info, r_info.size(), + [](auto r_ptr, auto flag) -> void { + do_s16b(&r_ptr->r_pkills, flag); + do_s16b(&r_ptr->max_num, flag); + do_bool(&r_ptr->on_saved, flag); + } + ); + + return true; +} + + +/* + * Object memory + */ +static bool do_object_lore(ls_flag_t flag) +{ + auto &k_info = game->edit_data.k_info; + + do_array("object kinds", flag, k_info, k_info.size(), + [](auto k_ptr, auto flag) -> void { + do_bool(&k_ptr->aware, flag); + do_bool(&k_ptr->tried, flag); + do_bool(&k_ptr->artifact, flag); + } + ); + + return true; +} + + +static bool do_towns(ls_flag_t flag) +{ + auto &d_info = game->edit_data.d_info; + + u16b max_towns_ldsv = max_towns; + + do_u16b(&max_towns_ldsv, flag); + + if ((flag == ls_flag_t::LOAD) && (max_towns_ldsv > max_towns)) + { + note("Too many towns!"); + return false; + } + + if (flag == ls_flag_t::SAVE) + { + max_towns_ldsv = TOWN_RANDOM; + } + + do_u16b(&max_towns_ldsv, flag); + + if ((flag == ls_flag_t::LOAD) && (max_towns_ldsv != TOWN_RANDOM)) + { + note("Different random towns base!"); + return false; + } + + for (std::size_t i = 0; i < max_towns; i++) + { + auto town = &town_info[i]; + + do_bool(&town->destroyed, flag); + + if (i >= TOWN_RANDOM) + { + do_seed(&town->seed, flag); + do_byte(&town->flags, flag); + + // Create stock if necessary + if ((town_info->flags & TOWN_REAL) && (flag == ls_flag_t::LOAD)) + { + create_stores_stock(i); + } + } + } + + if (flag == ls_flag_t::SAVE) + { + max_towns_ldsv = d_info.size(); + } + + do_u16b(&max_towns_ldsv, flag); + + if ((flag == ls_flag_t::LOAD) && (max_towns_ldsv > d_info.size())) + { + note("Too many dungeon types!"); + return false; + } + + // Town quest entrances + u16b max_quests_ldsv = TOWN_DUNGEON; + do_u16b(&max_quests_ldsv, flag); + + if ((flag == ls_flag_t::LOAD) && (max_quests_ldsv > TOWN_DUNGEON)) + { + note("Too many town per dungeons!"); + return false; + } + + for (std::size_t i = 0; i < max_towns_ldsv; i++) + { + for (std::size_t j = 0; j < max_quests_ldsv; j++) + { + do_s16b(&(d_info[i].t_idx[j]), flag); + do_s16b(&(d_info[i].t_level[j]), flag); + } + do_s16b(&(d_info[i].t_num), flag); + } + + return true; +} + +static bool do_quests(ls_flag_t flag) +{ + do_array("quests", flag, quest, MAX_Q_IDX, + [](auto q, auto flag) { + // Load/save the data + do_s16b(&q->status, flag); + for (auto &quest_data : q->data) + { + do_s32b(&quest_data, flag); + } + // Initialize if necessary + if ((flag == ls_flag_t::LOAD) && (q->init != NULL)) + { + q->init(); + } + } + ); + + return true; +} + +static bool do_wilderness(ls_flag_t flag) +{ + auto &wilderness = game->wilderness; + + // Player position and "mode" wrt. wilderness + do_s32b(&p_ptr->wilderness_x, flag); + do_s32b(&p_ptr->wilderness_y, flag); + do_bool(&p_ptr->wild_mode, flag); + do_bool(&p_ptr->old_wild_mode, flag); + + // Size of the wilderness + u16b wild_x_size = wilderness.width(); + u16b wild_y_size = wilderness.height(); + do_u16b(&wild_x_size, flag); + do_u16b(&wild_y_size, flag); + + if (flag == ls_flag_t::LOAD) + { + if ((wild_x_size > wilderness.width()) || (wild_y_size > wilderness.height())) + { + note("Wilderness is too big!"); + return false; + } + } + + // Save/load wilderness tile state + for (std::size_t x = 0; x < wild_x_size; x++) + { + for (std::size_t y = 0; y < wild_y_size; y++) + { + auto w = &wilderness(x, y); + do_seed(&w->seed, flag); + do_u16b(&w->entrance, flag); + do_bool(&w->known, flag); + } + } + + return true; +} + +static void do_randart(random_artifact *ra_ptr, ls_flag_t flag) +{ + do_std_string(ra_ptr->name_full, flag); + do_std_string(ra_ptr->name_short, flag); + do_byte(&ra_ptr->level, flag); + do_byte(&ra_ptr->attr, flag); + do_u32b(&ra_ptr->cost, flag); + do_byte(&ra_ptr->activation, flag); + do_byte(&ra_ptr->generated, flag); +} + +static bool do_randarts(ls_flag_t flag) +{ + do_vector(flag, game->random_artifacts, do_randart); + return true; +} + +static bool do_artifacts(ls_flag_t flag) +{ + auto &a_info = game->edit_data.a_info; + + do_array("artifacts", flag, a_info, a_info.size(), + [](auto a_ptr, auto flag) { + do_byte(&a_ptr->cur_num, flag); + } + ); + + return true; +} + +static bool do_fates(ls_flag_t flag) +{ + do_array("fates", flag, fates, MAX_FATES, + [](auto fate, auto flag) { + do_byte(&fate->fate, flag); + do_byte(&fate->level, flag); + do_byte(&fate->serious, flag); + do_s16b(&fate->o_idx, flag); + do_s16b(&fate->e_idx, flag); + do_s16b(&fate->a_idx, flag); + do_s16b(&fate->v_idx, flag); + do_s16b(&fate->r_idx, flag); + do_s16b(&fate->count, flag); + do_s16b(&fate->time, flag); + do_bool(&fate->know, flag); + } + ); + + return true; +} + +static bool do_floor_inscriptions(ls_flag_t flag) +{ + do_array("inscriptions", flag, p_ptr->inscriptions, MAX_INSCRIPTIONS, + [](auto i_ptr, auto flag) { + do_std_bool(i_ptr, flag); + } + ); + + return true; +} + +static bool do_player_hd(ls_flag_t flag) +{ + auto &player_hp = game->player_hp; + + do_array("hit dice entries", flag, player_hp, PY_MAX_LEVEL, + [](auto hp_ptr, auto flag) { + do_s16b(hp_ptr, flag); + } + ); + + return true; +} + + +/* + * Actually read the savefile + */ +static bool_ do_savefile_aux(ls_flag_t flag) +{ + auto &class_info = game->edit_data.class_info; + auto const &race_info = game->edit_data.race_info; + auto const &race_mod_info = game->edit_data.race_mod_info; + + /* Mention the savefile version */ + if (flag == ls_flag_t::LOAD) + { + if (vernum != SAVEFILE_VERSION) + { + note("Incompatible save file version"); + return FALSE; + } + } + + /* Handle version bytes */ + if (flag == ls_flag_t::LOAD) + { + /* Discard all this, we've already read it */ + u32b mt32b; + do_u32b(&mt32b, flag); + } + if (flag == ls_flag_t::SAVE) + { + u32b saver; + saver = SAVEFILE_VERSION; + do_u32b(&saver, flag); + } + + /* Game module */ + { + std::string loaded_game_module; + + if (flag == ls_flag_t::SAVE) + { + loaded_game_module = game_module; + } + do_std_string(loaded_game_module, flag); + + // Check for incompatible module + if (flag == ls_flag_t::LOAD) + { + if (!module_savefile_loadable(loaded_game_module)) + { + note(fmt::format("Bad game module. Savefile was saved with module '{:s}' but game is '{:s}'.", + loaded_game_module, + game_module).c_str()); + return FALSE; + } + } + } + + /* Timers */ + do_timers(flag); + + /* Read RNG state */ + do_randomizer(flag); + + /* Automatizer state */ + do_bool(&automatizer_enabled, flag); + + /* Then the options */ + do_options(flag); + + /* Then the "messages" */ + do_messages(flag); + + if (!do_monster_lore(flag)) + { + return FALSE; + } + + if (!do_object_lore(flag)) + { + return FALSE; + } + + if (!do_towns(flag)) + { + return FALSE; + } + + if (!do_quests(flag)) + { + return FALSE; + } + + if (!do_wilderness(flag)) + { + return FALSE; + } + + if (!do_randarts(flag)) + { + return FALSE; + } + + if (!do_artifacts(flag)) + { + return FALSE; + } + + if (!do_fates(flag)) + { + return FALSE; + } + + if (!do_floor_inscriptions(flag)) + { + return FALSE; + } + + if (!do_extra(flag)) + { + return FALSE; + } + + if (!do_player_hd(flag)) + { + return FALSE; + } + + if (flag == ls_flag_t::LOAD) + { + // Make sure that the auxiliary pointers for player + // class, race, etc. point to the right places. + rp_ptr = &race_info[p_ptr->prace]; + rmp_ptr = &race_mod_info[p_ptr->pracem]; + cp_ptr = &class_info[p_ptr->pclass]; + spp_ptr = &class_info[p_ptr->pclass].spec[p_ptr->pspec]; + } + + /* Read the pet command settings */ + do_byte(&p_ptr->pet_follow_distance, flag); + do_byte(&p_ptr->pet_open_doors, flag); + do_byte(&p_ptr->pet_pickup_items, flag); + + /* Dripping Tread */ + do_s16b(&p_ptr->dripping_tread, flag); + + /* Read the inventory */ + if (!do_inventory(flag)) + { + if (flag == ls_flag_t::LOAD) + { + note("Unable to read inventory"); + return FALSE; + } + } + + /* Stores */ + do_stores(flag); + + /* I'm not dead yet... */ + if (!death) + { + /* Dead players have no dungeon */ + if (flag == ls_flag_t::LOAD) + { + note("Restoring Dungeon..."); + } + + if ((flag == ls_flag_t::LOAD) && (!do_dungeon(ls_flag_t::LOAD, false))) + { + note("Error reading dungeon data"); + return FALSE; + } + + if (flag == ls_flag_t::SAVE) + { + do_dungeon(ls_flag_t::SAVE, false); + } + } + + /* Success */ + return TRUE; +} + + + +/* + * Actually read the savefile + */ +static errr rd_savefile() +{ + errr err = 0; + + /* The savefile is a binary file */ + fff = my_fopen(savefile, "rb"); + + /* Paranoia */ + if (!fff) return ( -1); + + /* Call the sub-function */ + err = !do_savefile_aux(ls_flag_t::LOAD); + + /* Check for errors */ + if (ferror(fff)) err = -1; + + /* Close the file */ + my_fclose(fff); + + /* Result */ + return (err); +} + + +/* + * Attempt to Load a "savefile" + * + * On multi-user systems, you may only "read" a savefile if you will be + * allowed to "write" it later, this prevents painful situations in which + * the player loads a savefile belonging to someone else, and then is not + * allowed to save his game when he quits. + * + * We return "TRUE" if the savefile was usable, and we set the global + * flag "character_loaded" if a real, living, character was loaded. + * + * Note that we always try to load the "current" savefile, even if + * there is no such file, so we must check for "empty" savefile names. + */ +bool_ load_player() +{ + errr err = 0; + + cptr what = "generic"; + + /* Paranoia */ + turn = 0; + + /* Paranoia */ + death = FALSE; + + + /* Allow empty savefile name */ + if (!savefile[0]) return (TRUE); + + + /* XXX XXX XXX Fix this */ + + /* Verify the existance of the savefile */ + if (!boost::filesystem::exists(savefile)) + { + /* Give a message */ + msg_format("Savefile does not exist: %s", savefile); + msg_print(NULL); + + /* Allow this */ + return (TRUE); + } + + /* Okay */ + if (!err) + { + /* Open the savefile */ + int fd = fd_open(savefile, O_RDONLY); + + /* No file */ + if (fd < 0) err = -1; + + /* Message (below) */ + if (err) what = "Cannot open savefile"; + + /* Close the file */ + if (!err) fd_close(fd); + } + + /* Process file */ + if (!err) + { + /* Open the file XXX XXX XXX XXX Should use Angband file interface */ + fff = my_fopen(savefile, "rb"); + + /* Read the first four bytes */ + do_u32b(&vernum, ls_flag_t::LOAD); + + my_fclose(fff); + + } + + /* Process file */ + if (!err) + { + /* Clear screen */ + Term_clear(); + + /* Attempt to load */ + err = rd_savefile(); + + /* Message (below) */ + if (err) what = "Cannot parse savefile"; + } + + /* Paranoia */ + if (!err) + { + /* Invalid turn */ + if (!turn) err = -1; + + /* Message (below) */ + if (err) what = "Broken savefile"; + } + + + /* Okay */ + if (!err) + { + /* Player is dead */ + if (death) + { + /* Player is no longer "dead" */ + death = FALSE; + + /* Cheat death (unless the character retired) */ + if (arg_wizard && !total_winner) + { + /* A character was loaded */ + character_loaded = TRUE; + + /* Done */ + return (TRUE); + } + + /* Forget turns */ + turn = old_turn = 0; + + /* Done */ + return (TRUE); + } + + /* A character was loaded */ + character_loaded = TRUE; + + /* Still alive */ + if (p_ptr->chp >= 0) + { + /* Reset cause of death */ + game->died_from = "(alive and well)"; + } + + /* Success */ + return (TRUE); + } + + + /* Message */ + msg_format("Error (%s) reading savefile (version " FMTu32b ").", + what, vernum); + msg_print(NULL); + + /* Oops */ + return (FALSE); +} + + + +/* + * Medium level player saver + */ +static bool_ save_player_aux(char *name) +{ + bool_ ok = FALSE; + int fd = -1; + int mode = 0644; + + /* No file yet */ + fff = NULL; + + /* Create the savefile */ + fd = fd_make(name, mode); + + /* File is okay */ + if (fd >= 0) + { + /* Close the "fd" */ + fd_close(fd); + + /* Open the savefile */ + fff = my_fopen(name, "wb"); + + /* Successful open */ + if (fff) + { + /* Write the savefile */ + if (do_savefile_aux(ls_flag_t::SAVE)) ok = TRUE; + + /* Attempt to close it */ + if (my_fclose(fff)) ok = FALSE; + } + + /* "broken" savefile */ + if (!ok) + { + /* Remove "broken" files */ + fd_kill(name); + } + } + + /* Failure */ + if (!ok) return (FALSE); + + /* Success */ + return (TRUE); +} + +/* + * Attempt to save the player in a savefile + */ +bool_ save_player() +{ + int result = FALSE; + char safe[1024]; + + /* New savefile */ + strcpy(safe, savefile); + strcat(safe, ".new"); + + /* Remove it */ + fd_kill(safe); + + /* Attempt to save the player */ + if (save_player_aux(safe)) + { + char temp[1024]; + + /* Old savefile */ + strcpy(temp, savefile); + strcat(temp, ".old"); + + /* Remove it */ + fd_kill(temp); + + /* Preserve old savefile */ + fd_move(savefile, temp); + + /* Activate new savefile */ + fd_move(safe, savefile); + + /* Remove preserved savefile */ + fd_kill(temp); + + /* Hack -- Pretend the character was loaded */ + character_loaded = TRUE; + + /* Success */ + result = TRUE; + } + + save_savefile_names(); + + /* Return the result */ + return (result); +} |