OSDN Git Service

Merge branch 'develop' into macos-develop
[hengbandforosx/hengbandosx.git] / src / save / save.cpp
1 /*!
2  * @file save.c
3  * @brief セーブファイル書き込み処理 / Purpose: interact with savefiles
4  * @date 2014/07/12
5  * @author
6  * <pre>
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.
11  * </pre>
12  */
13
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"
45 #include <algorithm>
46 #include <sstream>
47 #include <string>
48
49 /*!
50  * @brief セーブデータの書き込み /
51  * Actually write a save-file
52  * @param player_ptr プレイヤーへの参照ポインタ
53  * @return 成功すればtrue
54  */
55 static bool wr_savefile_new(PlayerType *player_ptr, SaveType type)
56 {
57     compact_objects(player_ptr, 0);
58     compact_monsters(player_ptr, 0);
59
60     uint32_t now = (uint32_t)time((time_t *)0);
61     w_ptr->sf_system = 0L;
62     w_ptr->sf_when = now;
63     w_ptr->sf_saves++;
64
65     save_xor_byte = 0;
66     auto variant_length = VARIANT_NAME.length();
67     wr_byte(static_cast<byte>(variant_length));
68     for (auto i = 0U; i < variant_length; i++) {
69         save_xor_byte = 0;
70         wr_byte(VARIANT_NAME[i]);
71     }
72
73     save_xor_byte = 0;
74     wr_byte(H_VER_MAJOR);
75     wr_byte(H_VER_MINOR);
76     wr_byte(H_VER_PATCH);
77     wr_byte(H_VER_EXTRA);
78
79     byte tmp8u = (byte)Rand_external(256);
80     wr_byte(tmp8u);
81     v_stamp = 0L;
82     x_stamp = 0L;
83
84     wr_u32b(w_ptr->sf_system);
85     wr_u32b(w_ptr->sf_when);
86     wr_u16b(w_ptr->sf_lives);
87     wr_u16b(w_ptr->sf_saves);
88
89     wr_u32b(SAVEFILE_VERSION);
90     wr_u16b(0);
91     wr_byte(0);
92
93 #ifdef JP
94 #ifdef EUC
95     wr_byte(2);
96 #endif
97 #ifdef SJIS
98     wr_byte(3);
99 #endif
100 #else
101     wr_byte(1);
102 #endif
103
104     wr_randomizer();
105     wr_options(type);
106     uint32_t tmp32u = message_num();
107     if ((compress_savefile || (type == SaveType::DEBUG)) && (tmp32u > 40)) {
108         tmp32u = 40;
109     }
110
111     wr_u32b(tmp32u);
112     for (int i = tmp32u - 1; i >= 0; i--) {
113         wr_string(*message_str(i));
114     }
115
116     uint16_t tmp16u = static_cast<uint16_t>(monraces_info.size());
117     wr_u16b(tmp16u);
118     for (auto r_idx = 0; r_idx < tmp16u; r_idx++) {
119         wr_lore(i2enum<MonsterRaceId>(r_idx));
120     }
121
122     tmp16u = static_cast<uint16_t>(baseitems_info.size());
123     wr_u16b(tmp16u);
124     for (short bi_id = 0; bi_id < tmp16u; bi_id++) {
125         wr_perception(bi_id);
126     }
127
128     tmp16u = static_cast<uint16_t>(towns_info.size());
129     wr_u16b(tmp16u);
130
131     const auto &quest_list = QuestList::get_instance();
132     tmp16u = static_cast<uint16_t>(quest_list.size());
133     wr_u16b(tmp16u);
134
135     tmp8u = MAX_RANDOM_QUEST - MIN_RANDOM_QUEST;
136     wr_byte(tmp8u);
137
138     for (const auto &[q_idx, quest] : quest_list) {
139         wr_s16b(enum2i(q_idx));
140         wr_s16b(enum2i(quest.status));
141         wr_s16b((int16_t)quest.level);
142         wr_byte((byte)quest.complev);
143         wr_u32b(quest.comptime);
144
145         auto is_quest_running = quest.status == QuestStatusType::TAKEN;
146         is_quest_running |= quest.status == QuestStatusType::COMPLETED;
147         is_quest_running |= !QuestType::is_fixed(q_idx);
148         if (!is_quest_running) {
149             continue;
150         }
151
152         wr_s16b((int16_t)quest.cur_num);
153         wr_s16b((int16_t)quest.max_num);
154         wr_s16b(enum2i(quest.type));
155         wr_s16b(enum2i(quest.r_idx));
156         wr_s16b(enum2i(quest.reward_artifact_idx));
157         wr_byte((byte)quest.flags);
158         wr_byte((byte)quest.dungeon);
159     }
160
161     wr_s32b(player_ptr->wilderness_x);
162     wr_s32b(player_ptr->wilderness_y);
163     wr_bool(player_ptr->wild_mode);
164     wr_bool(player_ptr->ambush_flag);
165     wr_s32b(w_ptr->max_wild_x);
166     wr_s32b(w_ptr->max_wild_y);
167     for (int i = 0; i < w_ptr->max_wild_x; i++) {
168         for (int j = 0; j < w_ptr->max_wild_y; j++) {
169             wr_u32b(wilderness[j][i].seed);
170         }
171     }
172
173     auto max_a_num = enum2i(artifacts_info.rbegin()->first);
174     tmp16u = max_a_num + 1;
175     wr_u16b(tmp16u);
176     for (auto i = 0U; i < tmp16u; i++) {
177         const auto a_idx = i2enum<FixedArtifactId>(i);
178         const auto &artifact = ArtifactsInfo::get_instance().get_artifact(a_idx);
179         wr_bool(artifact.is_generated);
180         wr_s16b(artifact.floor_id);
181     }
182
183     wr_u32b(w_ptr->sf_play_time);
184     wr_FlagGroup(w_ptr->sf_winner, wr_byte);
185     wr_FlagGroup(w_ptr->sf_retired, wr_byte);
186
187     wr_player(player_ptr);
188     tmp16u = PY_MAX_LEVEL;
189     wr_u16b(tmp16u);
190     for (int i = 0; i < tmp16u; i++) {
191         wr_s16b((int16_t)player_ptr->player_hp[i]);
192     }
193
194     wr_u32b(player_ptr->spell_learned1);
195     wr_u32b(player_ptr->spell_learned2);
196     wr_u32b(player_ptr->spell_worked1);
197     wr_u32b(player_ptr->spell_worked2);
198     wr_u32b(player_ptr->spell_forgotten1);
199     wr_u32b(player_ptr->spell_forgotten2);
200     wr_s16b(player_ptr->learned_spells);
201     wr_s16b(player_ptr->add_spells);
202     for (int i = 0; i < 64; i++) {
203         wr_byte((byte)player_ptr->spell_order[i]);
204     }
205
206     for (int i = 0; i < INVEN_TOTAL; i++) {
207         auto *o_ptr = &player_ptr->inventory_list[i];
208         if (!o_ptr->is_valid()) {
209             continue;
210         }
211
212         wr_u16b((uint16_t)i);
213         wr_item(o_ptr);
214     }
215
216     wr_u16b(0xFFFF);
217     tmp16u = static_cast<uint16_t>(towns_info.size());
218     wr_u16b(tmp16u);
219
220     tmp16u = MAX_STORES;
221     wr_u16b(tmp16u);
222     for (size_t i = 1; i < towns_info.size(); i++) {
223         for (auto sst : STORE_SALE_TYPE_LIST) {
224             wr_store(&towns_info[i].stores[sst]);
225         }
226     }
227
228     wr_s16b(player_ptr->pet_follow_distance);
229     wr_s16b(player_ptr->pet_extra_flags);
230     if (screen_dump && (player_ptr->wait_report_score || !player_ptr->is_dead)) {
231         wr_string(screen_dump);
232     } else {
233         wr_string("");
234     }
235
236     if (!player_ptr->is_dead) {
237         if (!wr_dungeon(player_ptr)) {
238             return false;
239         }
240
241         wr_ghost();
242         wr_s32b(0);
243     }
244
245     wr_u32b(v_stamp);
246     wr_u32b(x_stamp);
247     return !ferror(saving_savefile) && (fflush(saving_savefile) != EOF);
248 }
249
250 /*!
251  * @brief セーブデータ書き込みのサブルーチン
252  * @param player_ptr プレイヤーへの参照ポインタ
253  * @param path セーブデータのフルパス
254  * @param type セーブ後の処理種別
255  * @return セーブの成功可否
256  */
257 static bool save_player_aux(PlayerType *player_ptr, const std::filesystem::path &path, SaveType type)
258 {
259     safe_setuid_grab();
260     auto fd = fd_make(path);
261     safe_setuid_drop();
262
263     bool is_save_successful = false;
264     saving_savefile = nullptr;
265     if (fd >= 0) {
266         (void)fd_close(fd);
267         safe_setuid_grab();
268         saving_savefile = angband_fopen(path, FileOpenMode::WRITE, true,
269             FileOpenType::SAVE);
270         safe_setuid_drop();
271         if (saving_savefile) {
272             if (wr_savefile_new(player_ptr, type)) {
273                 is_save_successful = true;
274             }
275
276             if (angband_fclose(saving_savefile)) {
277                 is_save_successful = false;
278             }
279         }
280
281         safe_setuid_grab();
282         if (!is_save_successful) {
283             (void)fd_kill(path);
284         }
285
286         safe_setuid_drop();
287     }
288
289     if (!is_save_successful) {
290         return false;
291     }
292
293     counts_write(player_ptr, 0, w_ptr->play_time);
294     w_ptr->character_saved = true;
295     return true;
296 }
297
298 /*!
299  * @brief セーブデータ書き込みのメインルーチン
300  * @param player_ptr プレイヤーへの参照ポインタ
301  * @return 成功すればtrue
302  * @details 以下の順番で書き込みを実行する.
303  * 1. hoge.new にセーブデータを書き込む
304  * 2. hoge をhoge.old にリネームする
305  * 3. hoge.new をhoge にリネームする
306  * 4. hoge.old を削除する
307  */
308 bool save_player(PlayerType *player_ptr, SaveType type)
309 {
310     std::stringstream ss_new;
311     ss_new << savefile.string() << ".new";
312     auto savefile_new = ss_new.str();
313     safe_setuid_grab();
314     fd_kill(savefile_new);
315     if (type == SaveType::DEBUG) {
316         const auto debug_save_dir = std::filesystem::path(debug_savefile).remove_filename();
317         std::error_code ec;
318         std::filesystem::create_directory(debug_save_dir, ec);
319     }
320     safe_setuid_drop();
321     update_playtime();
322     bool result = false;
323     if (save_player_aux(player_ptr, savefile_new.data(), type)) {
324         std::stringstream ss_old;
325         ss_old << savefile.string() << ".old";
326         auto savefile_old = ss_old.str();
327         safe_setuid_grab();
328         fd_kill(savefile_old);
329         const auto &path = type == SaveType::DEBUG ? debug_savefile : savefile;
330         fd_move(path, savefile_old);
331         fd_move(savefile_new, path);
332         fd_kill(savefile_old);
333         safe_setuid_drop();
334         w_ptr->character_loaded = true;
335         result = true;
336     }
337
338     if (type != SaveType::CLOSE_GAME) {
339         w_ptr->is_loading_now = false;
340         update_creature(player_ptr);
341         mproc_init(player_ptr->current_floor_ptr);
342         w_ptr->is_loading_now = true;
343     }
344
345     return result;
346 }