OSDN Git Service

4a1aad4f1ae94d93492f3206ebdda204ee7fcb1e
[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 "inventory/inventory-slot-types.h"
20 #include "io/files-util.h"
21 #include "io/report.h"
22 #include "io/uid-checker.h"
23 #include "locale/character-encoding.h"
24 #include "monster/monster-compaction.h"
25 #include "monster/monster-status.h"
26 #include "player/player-status.h"
27 #include "save/floor-writer.h"
28 #include "save/info-writer.h"
29 #include "save/item-writer.h"
30 #include "save/monster-writer.h"
31 #include "save/player-writer.h"
32 #include "save/save-util.h"
33 #include "store/store-owners.h"
34 #include "store/store-util.h"
35 #include "system/angband-system.h"
36 #include "system/artifact-type-definition.h"
37 #include "system/baseitem-info.h"
38 #include "system/item-entity.h"
39 #include "system/monster-race-info.h"
40 #include "system/player-type-definition.h"
41 #include "util/angband-files.h"
42 #include "util/enum-converter.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)
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     auto &world = AngbandWorld::get_instance();
62     world.sf_system = 0L;
63     world.sf_when = now;
64     world.sf_saves++;
65
66     save_xor_byte = 0;
67     auto variant_length = VARIANT_NAME.length();
68     wr_byte(static_cast<byte>(variant_length));
69     for (auto i = 0U; i < variant_length; i++) {
70         save_xor_byte = 0;
71         wr_byte(VARIANT_NAME[i]);
72     }
73
74     save_xor_byte = 0;
75     wr_byte(H_VER_MAJOR);
76     wr_byte(H_VER_MINOR);
77     wr_byte(H_VER_PATCH);
78     wr_byte(H_VER_EXTRA);
79
80     byte tmp8u = (byte)Rand_external(256);
81     wr_byte(tmp8u);
82     v_stamp = 0L;
83     x_stamp = 0L;
84
85     wr_u32b(world.sf_system);
86     wr_u32b(world.sf_when);
87     wr_u16b(world.sf_lives);
88     wr_u16b(world.sf_saves);
89
90     wr_u32b(SAVEFILE_VERSION);
91     wr_u16b(0);
92     wr_byte(0);
93
94 #ifdef JP
95 #ifdef EUC
96     wr_byte(enum2i(CharacterEncoding::EUC_JP));
97 #endif
98 #ifdef SJIS
99     wr_byte(enum2i(CharacterEncoding::SHIFT_JIS));
100 #endif
101 #else
102     wr_byte(enum2i(CharacterEncoding::US_ASCII));
103 #endif
104
105     wr_randomizer();
106     wr_options();
107     wr_message_history();
108
109     uint16_t tmp16u = static_cast<uint16_t>(monraces_info.size());
110     wr_u16b(tmp16u);
111     for (auto r_idx = 0; r_idx < tmp16u; r_idx++) {
112         wr_lore(i2enum<MonsterRaceId>(r_idx));
113     }
114
115     tmp16u = static_cast<uint16_t>(BaseitemList::get_instance().size());
116     wr_u16b(tmp16u);
117     for (short bi_id = 0; bi_id < tmp16u; bi_id++) {
118         wr_perception(bi_id);
119     }
120
121     tmp16u = static_cast<uint16_t>(towns_info.size());
122     wr_u16b(tmp16u);
123
124     const auto &quests = QuestList::get_instance();
125     tmp16u = static_cast<uint16_t>(quests.size());
126     wr_u16b(tmp16u);
127
128     tmp8u = MAX_RANDOM_QUEST - MIN_RANDOM_QUEST;
129     wr_byte(tmp8u);
130
131     for (const auto &[quest_id, quest] : quests) {
132         wr_s16b(enum2i(quest_id));
133         wr_s16b(enum2i(quest.status));
134         wr_s16b((int16_t)quest.level);
135         wr_byte((byte)quest.complev);
136         wr_u32b(quest.comptime);
137
138         auto is_quest_running = quest.status == QuestStatusType::TAKEN;
139         is_quest_running |= quest.status == QuestStatusType::COMPLETED;
140         is_quest_running |= !QuestType::is_fixed(quest_id);
141         if (!is_quest_running) {
142             continue;
143         }
144
145         wr_s16b((int16_t)quest.cur_num);
146         wr_s16b((int16_t)quest.max_num);
147         wr_s16b(enum2i(quest.type));
148         wr_s16b(enum2i(quest.r_idx));
149         wr_s16b(enum2i(quest.reward_fa_id));
150         wr_byte((byte)quest.flags);
151         wr_byte((byte)quest.dungeon);
152     }
153
154     wr_s32b(player_ptr->wilderness_x);
155     wr_s32b(player_ptr->wilderness_y);
156     wr_bool(player_ptr->wild_mode);
157     wr_bool(player_ptr->ambush_flag);
158     wr_s32b(world.max_wild_x);
159     wr_s32b(world.max_wild_y);
160     for (int i = 0; i < world.max_wild_x; i++) {
161         for (int j = 0; j < world.max_wild_y; j++) {
162             wr_u32b(wilderness[j][i].seed);
163         }
164     }
165
166     const auto &artifacts = ArtifactList::get_instance();
167     auto max_a_num = enum2i(artifacts.rbegin()->first);
168     tmp16u = max_a_num + 1;
169     wr_u16b(tmp16u);
170     for (auto i = 0U; i < tmp16u; i++) {
171         const auto a_idx = i2enum<FixedArtifactId>(i);
172         const auto &artifact = artifacts.get_artifact(a_idx);
173         wr_bool(artifact.is_generated);
174         wr_s16b(artifact.floor_id);
175     }
176
177     wr_u32b(world.sf_play_time);
178     wr_FlagGroup(world.sf_winner, wr_byte);
179     wr_FlagGroup(world.sf_retired, wr_byte);
180
181     wr_player(player_ptr);
182     tmp16u = PY_MAX_LEVEL;
183     wr_u16b(tmp16u);
184     for (int i = 0; i < tmp16u; i++) {
185         wr_s16b((int16_t)player_ptr->player_hp[i]);
186     }
187
188     wr_u32b(player_ptr->spell_learned1);
189     wr_u32b(player_ptr->spell_learned2);
190     wr_u32b(player_ptr->spell_worked1);
191     wr_u32b(player_ptr->spell_worked2);
192     wr_u32b(player_ptr->spell_forgotten1);
193     wr_u32b(player_ptr->spell_forgotten2);
194     wr_s16b(player_ptr->learned_spells);
195     wr_s16b(player_ptr->add_spells);
196     for (int i = 0; i < 64; i++) {
197         wr_byte((byte)player_ptr->spell_order[i]);
198     }
199
200     for (int i = 0; i < INVEN_TOTAL; i++) {
201         const auto &item = player_ptr->inventory_list[i];
202         if (!item.is_valid()) {
203             continue;
204         }
205
206         wr_u16b((uint16_t)i);
207         wr_item(item);
208     }
209
210     wr_u16b(0xFFFF);
211     tmp16u = static_cast<uint16_t>(towns_info.size());
212     wr_u16b(tmp16u);
213
214     tmp16u = MAX_STORES;
215     wr_u16b(tmp16u);
216     for (size_t i = 1; i < towns_info.size(); i++) {
217         for (auto sst : STORE_SALE_TYPE_LIST) {
218             wr_store(&towns_info[i].stores[sst]);
219         }
220     }
221
222     wr_s16b(player_ptr->pet_follow_distance);
223     wr_s16b(player_ptr->pet_extra_flags);
224     if (screen_dump && (player_ptr->wait_report_score || !player_ptr->is_dead)) {
225         wr_string(screen_dump);
226     } else {
227         wr_string("");
228     }
229
230     if (!player_ptr->is_dead) {
231         if (!wr_dungeon(player_ptr)) {
232             return false;
233         }
234
235         wr_ghost();
236         wr_s32b(0);
237     }
238
239     wr_u32b(v_stamp);
240     wr_u32b(x_stamp);
241     return !ferror(saving_savefile) && (fflush(saving_savefile) != EOF);
242 }
243
244 /*!
245  * @brief セーブデータ書き込みのサブルーチン
246  * @param player_ptr プレイヤーへの参照ポインタ
247  * @param path セーブデータのフルパス
248  * @param type セーブ後の処理種別
249  * @return セーブの成功可否
250  */
251 static bool save_player_aux(PlayerType *player_ptr, const std::filesystem::path &path)
252 {
253     safe_setuid_grab();
254     auto fd = fd_make(path);
255     safe_setuid_drop();
256
257     bool is_save_successful = false;
258     saving_savefile = nullptr;
259     if (fd >= 0) {
260         (void)fd_close(fd);
261         safe_setuid_grab();
262         saving_savefile = angband_fopen(path, FileOpenMode::WRITE, true,
263             FileOpenType::SAVE);
264         safe_setuid_drop();
265         if (saving_savefile) {
266             if (wr_savefile_new(player_ptr)) {
267                 is_save_successful = true;
268             }
269
270             if (angband_fclose(saving_savefile)) {
271                 is_save_successful = false;
272             }
273         }
274
275         safe_setuid_grab();
276         if (!is_save_successful) {
277             (void)fd_kill(path);
278         }
279
280         safe_setuid_drop();
281     }
282
283     if (!is_save_successful) {
284         return false;
285     }
286
287     auto &world = AngbandWorld::get_instance();
288     counts_write(player_ptr, 0, world.play_time);
289     world.character_saved = true;
290     return true;
291 }
292
293 /*!
294  * @brief セーブデータ書き込みのメインルーチン
295  * @param player_ptr プレイヤーへの参照ポインタ
296  * @return 成功すればtrue
297  * @details 以下の順番で書き込みを実行する.
298  * 1. hoge.new にセーブデータを書き込む
299  * 2. hoge をhoge.old にリネームする
300  * 3. hoge.new をhoge にリネームする
301  * 4. hoge.old を削除する
302  */
303 bool save_player(PlayerType *player_ptr, SaveType type)
304 {
305     std::stringstream ss_new;
306     ss_new << savefile.string() << ".new";
307     auto savefile_new = ss_new.str();
308     safe_setuid_grab();
309     fd_kill(savefile_new);
310
311     safe_setuid_drop();
312     auto &world = AngbandWorld::get_instance();
313     world.update_playtime();
314     auto result = false;
315     if (save_player_aux(player_ptr, savefile_new.data())) {
316         std::stringstream ss_old;
317         ss_old << savefile.string() << ".old";
318         auto savefile_old = ss_old.str();
319         safe_setuid_grab();
320         fd_kill(savefile_old);
321         fd_move(savefile, savefile_old);
322         fd_move(savefile_new, savefile);
323         fd_kill(savefile_old);
324         safe_setuid_drop();
325         world.character_loaded = true;
326         result = true;
327     }
328
329     if (type != SaveType::CLOSE_GAME) {
330         world.is_loading_now = false;
331         update_creature(player_ptr);
332         mproc_init(player_ptr->current_floor_ptr);
333         world.is_loading_now = true;
334     }
335
336     return result;
337 }