3 * @brief セーブファイル書き込み処理 / Purpose: interact with savefiles
7 * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke
8 * This software may be copied and distributed for educational, research,
9 * and not for profit purposes provided that this copyright and statement
10 * are included in all such copies. Other copyrights may also apply.
14 #include "save/save.h"
15 #include "core/object-compressor.h"
16 #include "dungeon/quest.h"
17 #include "floor/floor-town.h"
18 #include "floor/wild.h"
19 #include "game-option/text-display-options.h"
20 #include "inventory/inventory-slot-types.h"
21 #include "io/files-util.h"
22 #include "io/report.h"
23 #include "io/uid-checker.h"
24 #include "monster-race/monster-race.h"
25 #include "monster/monster-compaction.h"
26 #include "monster/monster-status.h"
27 #include "player/player-status.h"
28 #include "save/floor-writer.h"
29 #include "save/info-writer.h"
30 #include "save/item-writer.h"
31 #include "save/monster-writer.h"
32 #include "save/player-writer.h"
33 #include "save/save-util.h"
34 #include "store/store-owners.h"
35 #include "store/store-util.h"
36 #include "system/angband-version.h"
37 #include "system/artifact-type-definition.h"
38 #include "system/baseitem-info.h"
39 #include "system/item-entity.h"
40 #include "system/monster-race-info.h"
41 #include "system/player-type-definition.h"
42 #include "util/angband-files.h"
43 #include "view/display-messages.h"
44 #include "world/world.h"
48 * @brief セーブデータの書き込み /
49 * Actually write a save-file
50 * @param player_ptr プレイヤーへの参照ポインタ
53 static bool wr_savefile_new(PlayerType *player_ptr, SaveType type)
55 compact_objects(player_ptr, 0);
56 compact_monsters(player_ptr, 0);
58 uint32_t now = (uint32_t)time((time_t *)0);
59 w_ptr->sf_system = 0L;
64 auto variant_length = VARIANT_NAME.length();
65 wr_byte(static_cast<byte>(variant_length));
66 for (auto i = 0U; i < variant_length; i++) {
68 wr_byte(VARIANT_NAME[i]);
77 byte tmp8u = (byte)Rand_external(256);
82 wr_u32b(w_ptr->sf_system);
83 wr_u32b(w_ptr->sf_when);
84 wr_u16b(w_ptr->sf_lives);
85 wr_u16b(w_ptr->sf_saves);
87 wr_u32b(SAVEFILE_VERSION);
104 uint32_t tmp32u = message_num();
105 if ((compress_savefile || (type == SaveType::DEBUG)) && (tmp32u > 40)) {
110 for (int i = tmp32u - 1; i >= 0; i--) {
111 wr_string(message_str(i));
114 uint16_t tmp16u = static_cast<uint16_t>(monraces_info.size());
116 for (auto r_idx = 0; r_idx < tmp16u; r_idx++) {
117 wr_lore(i2enum<MonsterRaceId>(r_idx));
120 tmp16u = static_cast<uint16_t>(baseitems_info.size());
122 for (short bi_id = 0; bi_id < tmp16u; bi_id++) {
123 wr_perception(bi_id);
126 tmp16u = static_cast<uint16_t>(towns_info.size());
129 const auto &quest_list = QuestList::get_instance();
130 tmp16u = static_cast<uint16_t>(quest_list.size());
133 tmp8u = MAX_RANDOM_QUEST - MIN_RANDOM_QUEST;
136 for (const auto &[q_idx, quest] : quest_list) {
137 wr_s16b(enum2i(q_idx));
138 wr_s16b(enum2i(quest.status));
139 wr_s16b((int16_t)quest.level);
140 wr_byte((byte)quest.complev);
141 wr_u32b(quest.comptime);
143 auto is_quest_running = quest.status == QuestStatusType::TAKEN;
144 is_quest_running |= quest.status == QuestStatusType::COMPLETED;
145 is_quest_running |= !QuestType::is_fixed(q_idx);
146 if (!is_quest_running) {
150 wr_s16b((int16_t)quest.cur_num);
151 wr_s16b((int16_t)quest.max_num);
152 wr_s16b(enum2i(quest.type));
153 wr_s16b(enum2i(quest.r_idx));
154 wr_s16b(enum2i(quest.reward_artifact_idx));
155 wr_byte((byte)quest.flags);
156 wr_byte((byte)quest.dungeon);
159 wr_s32b(player_ptr->wilderness_x);
160 wr_s32b(player_ptr->wilderness_y);
161 wr_bool(player_ptr->wild_mode);
162 wr_bool(player_ptr->ambush_flag);
163 wr_s32b(w_ptr->max_wild_x);
164 wr_s32b(w_ptr->max_wild_y);
165 for (int i = 0; i < w_ptr->max_wild_x; i++) {
166 for (int j = 0; j < w_ptr->max_wild_y; j++) {
167 wr_u32b(wilderness[j][i].seed);
171 auto max_a_num = enum2i(artifacts_info.rbegin()->first);
172 tmp16u = max_a_num + 1;
174 for (auto i = 0U; i < tmp16u; i++) {
175 const auto a_idx = i2enum<FixedArtifactId>(i);
176 const auto &artifact = ArtifactsInfo::get_instance().get_artifact(a_idx);
177 wr_bool(artifact.is_generated);
178 wr_s16b(artifact.floor_id);
181 wr_u32b(w_ptr->sf_play_time);
182 wr_FlagGroup(w_ptr->sf_winner, wr_byte);
183 wr_FlagGroup(w_ptr->sf_retired, wr_byte);
185 wr_player(player_ptr);
186 tmp16u = PY_MAX_LEVEL;
188 for (int i = 0; i < tmp16u; i++) {
189 wr_s16b((int16_t)player_ptr->player_hp[i]);
192 wr_u32b(player_ptr->spell_learned1);
193 wr_u32b(player_ptr->spell_learned2);
194 wr_u32b(player_ptr->spell_worked1);
195 wr_u32b(player_ptr->spell_worked2);
196 wr_u32b(player_ptr->spell_forgotten1);
197 wr_u32b(player_ptr->spell_forgotten2);
198 wr_s16b(player_ptr->learned_spells);
199 wr_s16b(player_ptr->add_spells);
200 for (int i = 0; i < 64; i++) {
201 wr_byte((byte)player_ptr->spell_order[i]);
204 for (int i = 0; i < INVEN_TOTAL; i++) {
205 auto *o_ptr = &player_ptr->inventory_list[i];
206 if (!o_ptr->is_valid()) {
210 wr_u16b((uint16_t)i);
215 tmp16u = static_cast<uint16_t>(towns_info.size());
220 for (size_t i = 1; i < towns_info.size(); i++) {
221 for (auto j = 0; j < MAX_STORES; j++) {
222 wr_store(&towns_info[i].store[j]);
226 wr_s16b(player_ptr->pet_follow_distance);
227 wr_s16b(player_ptr->pet_extra_flags);
228 if (screen_dump && (player_ptr->wait_report_score || !player_ptr->is_dead)) {
229 wr_string(screen_dump);
234 if (!player_ptr->is_dead) {
235 if (!wr_dungeon(player_ptr)) {
245 return !ferror(saving_savefile) && (fflush(saving_savefile) != EOF);
249 * @brief セーブデータ書き込みのサブルーチン /
250 * Medium level player saver
251 * @param player_ptr プレイヤーへの参照ポインタ
254 * Angband 2.8.0 will use "fd" instead of "fff" if possible
256 static bool save_player_aux(PlayerType *player_ptr, char *name, SaveType type)
258 safe_setuid_grab(player_ptr);
259 auto fd = fd_make(name);
262 bool is_save_successful = false;
263 saving_savefile = nullptr;
266 safe_setuid_grab(player_ptr);
267 saving_savefile = angband_fopen(name, FileOpenMode::WRITE, true);
269 if (saving_savefile) {
270 if (wr_savefile_new(player_ptr, type)) {
271 is_save_successful = true;
274 if (angband_fclose(saving_savefile)) {
275 is_save_successful = false;
279 safe_setuid_grab(player_ptr);
280 if (!is_save_successful) {
287 if (!is_save_successful) {
291 counts_write(player_ptr, 0, w_ptr->play_time);
292 w_ptr->character_saved = true;
297 * @brief セーブデータ書き込みのメインルーチン /
298 * Attempt to save the player in a savefile
299 * @param player_ptr プレイヤーへの参照ポインタ
302 bool save_player(PlayerType *player_ptr, SaveType type)
305 strcpy(safe, savefile);
306 strcat(safe, ".new");
307 safe_setuid_grab(player_ptr);
312 if (save_player_aux(player_ptr, safe, type)) {
315 strcpy(temp, savefile);
316 strcat(temp, ".old");
317 safe_setuid_grab(player_ptr);
320 if (type == SaveType::DEBUG) {
321 strcpy(filename, debug_savefile);
323 if (type != SaveType::DEBUG) {
324 strcpy(filename, savefile);
327 fd_move(filename, temp);
328 fd_move(safe, filename);
331 w_ptr->character_loaded = true;
335 if (type != SaveType::CLOSE_GAME) {
336 w_ptr->is_loading_now = false;
337 update_creature(player_ptr);
338 mproc_init(player_ptr->current_floor_ptr);
339 w_ptr->is_loading_now = true;