OSDN Git Service

[Refactor] #3177 アーティファクトに欠番がなくなったのでfind() で探さずat() で直接取得するようにした
[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
47 /*!
48  * @brief セーブデータの書き込み /
49  * Actually write a save-file
50  * @param player_ptr プレイヤーへの参照ポインタ
51  * @return 成功すればtrue
52  */
53 static bool wr_savefile_new(PlayerType *player_ptr, SaveType type)
54 {
55     compact_objects(player_ptr, 0);
56     compact_monsters(player_ptr, 0);
57
58     uint32_t now = (uint32_t)time((time_t *)0);
59     w_ptr->sf_system = 0L;
60     w_ptr->sf_when = now;
61     w_ptr->sf_saves++;
62
63     save_xor_byte = 0;
64     auto variant_length = VARIANT_NAME.length();
65     wr_byte(static_cast<byte>(variant_length));
66     for (auto i = 0U; i < variant_length; i++) {
67         save_xor_byte = 0;
68         wr_byte(VARIANT_NAME[i]);
69     }
70
71     save_xor_byte = 0;
72     wr_byte(H_VER_MAJOR);
73     wr_byte(H_VER_MINOR);
74     wr_byte(H_VER_PATCH);
75     wr_byte(H_VER_EXTRA);
76
77     byte tmp8u = (byte)Rand_external(256);
78     wr_byte(tmp8u);
79     v_stamp = 0L;
80     x_stamp = 0L;
81
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);
86
87     wr_u32b(SAVEFILE_VERSION);
88     wr_u16b(0);
89     wr_byte(0);
90
91 #ifdef JP
92 #ifdef EUC
93     wr_byte(2);
94 #endif
95 #ifdef SJIS
96     wr_byte(3);
97 #endif
98 #else
99     wr_byte(1);
100 #endif
101
102     wr_randomizer();
103     wr_options(type);
104     uint32_t tmp32u = message_num();
105     if ((compress_savefile || (type == SaveType::DEBUG)) && (tmp32u > 40)) {
106         tmp32u = 40;
107     }
108
109     wr_u32b(tmp32u);
110     for (int i = tmp32u - 1; i >= 0; i--) {
111         wr_string(message_str(i));
112     }
113
114     uint16_t tmp16u = static_cast<uint16_t>(monraces_info.size());
115     wr_u16b(tmp16u);
116     for (auto r_idx = 0; r_idx < tmp16u; r_idx++) {
117         wr_lore(i2enum<MonsterRaceId>(r_idx));
118     }
119
120     tmp16u = static_cast<uint16_t>(baseitems_info.size());
121     wr_u16b(tmp16u);
122     for (short bi_id = 0; bi_id < tmp16u; bi_id++) {
123         wr_perception(bi_id);
124     }
125
126     tmp16u = static_cast<uint16_t>(towns_info.size());
127     wr_u16b(tmp16u);
128
129     const auto &quest_list = QuestList::get_instance();
130     tmp16u = static_cast<uint16_t>(quest_list.size());
131     wr_u16b(tmp16u);
132
133     tmp8u = MAX_RANDOM_QUEST - MIN_RANDOM_QUEST;
134     wr_byte(tmp8u);
135
136     for (const auto &[q_idx, q_ref] : quest_list) {
137         wr_s16b(enum2i(q_idx));
138         wr_s16b(enum2i(q_ref.status));
139         wr_s16b((int16_t)q_ref.level);
140         wr_byte((byte)q_ref.complev);
141         wr_u32b(q_ref.comptime);
142
143         auto is_quest_running = q_ref.status == QuestStatusType::TAKEN;
144         is_quest_running |= q_ref.status == QuestStatusType::COMPLETED;
145         is_quest_running |= !quest_type::is_fixed(q_idx);
146         if (!is_quest_running) {
147             continue;
148         }
149
150         wr_s16b((int16_t)q_ref.cur_num);
151         wr_s16b((int16_t)q_ref.max_num);
152         wr_s16b(enum2i(q_ref.type));
153         wr_s16b(enum2i(q_ref.r_idx));
154         wr_s16b(enum2i(q_ref.reward_artifact_idx));
155         wr_byte((byte)q_ref.flags);
156         wr_byte((byte)q_ref.dungeon);
157     }
158
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);
168         }
169     }
170
171     auto max_a_num = enum2i(artifacts_info.rbegin()->first);
172     tmp16u = max_a_num + 1;
173     wr_u16b(tmp16u);
174     ArtifactType dummy;
175     for (auto i = 0U; i < tmp16u; i++) {
176         const auto a_idx = i2enum<FixedArtifactId>(i);
177         const auto &artifact = (i > 0) ? artifacts_info.at(a_idx) : dummy;
178         wr_bool(artifact.is_generated);
179         wr_s16b(artifact.floor_id);
180     }
181
182     wr_u32b(w_ptr->sf_play_time);
183     wr_FlagGroup(w_ptr->sf_winner, wr_byte);
184     wr_FlagGroup(w_ptr->sf_retired, wr_byte);
185
186     wr_player(player_ptr);
187     tmp16u = PY_MAX_LEVEL;
188     wr_u16b(tmp16u);
189     for (int i = 0; i < tmp16u; i++) {
190         wr_s16b((int16_t)player_ptr->player_hp[i]);
191     }
192
193     wr_u32b(player_ptr->spell_learned1);
194     wr_u32b(player_ptr->spell_learned2);
195     wr_u32b(player_ptr->spell_worked1);
196     wr_u32b(player_ptr->spell_worked2);
197     wr_u32b(player_ptr->spell_forgotten1);
198     wr_u32b(player_ptr->spell_forgotten2);
199     wr_s16b(player_ptr->learned_spells);
200     wr_s16b(player_ptr->add_spells);
201     for (int i = 0; i < 64; i++) {
202         wr_byte((byte)player_ptr->spell_order[i]);
203     }
204
205     for (int i = 0; i < INVEN_TOTAL; i++) {
206         auto *o_ptr = &player_ptr->inventory_list[i];
207         if (!o_ptr->bi_id) {
208             continue;
209         }
210
211         wr_u16b((uint16_t)i);
212         wr_item(o_ptr);
213     }
214
215     wr_u16b(0xFFFF);
216     tmp16u = static_cast<uint16_t>(towns_info.size());
217     wr_u16b(tmp16u);
218
219     tmp16u = MAX_STORES;
220     wr_u16b(tmp16u);
221     for (size_t i = 1; i < towns_info.size(); i++) {
222         for (auto j = 0; j < MAX_STORES; j++) {
223             wr_store(&towns_info[i].store[j]);
224         }
225     }
226
227     wr_s16b(player_ptr->pet_follow_distance);
228     wr_s16b(player_ptr->pet_extra_flags);
229     if (screen_dump && (player_ptr->wait_report_score || !player_ptr->is_dead)) {
230         wr_string(screen_dump);
231     } else {
232         wr_string("");
233     }
234
235     if (!player_ptr->is_dead) {
236         if (!wr_dungeon(player_ptr)) {
237             return false;
238         }
239
240         wr_ghost();
241         wr_s32b(0);
242     }
243
244     wr_u32b(v_stamp);
245     wr_u32b(x_stamp);
246     return !ferror(saving_savefile) && (fflush(saving_savefile) != EOF);
247 }
248
249 /*!
250  * @brief セーブデータ書き込みのサブルーチン /
251  * Medium level player saver
252  * @param player_ptr プレイヤーへの参照ポインタ
253  * @return 成功すればtrue
254  * @details
255  * Angband 2.8.0 will use "fd" instead of "fff" if possible
256  */
257 static bool save_player_aux(PlayerType *player_ptr, char *name, SaveType type)
258 {
259     safe_setuid_grab(player_ptr);
260     int file_permission = 0644;
261     int fd = fd_make(name, file_permission);
262     safe_setuid_drop();
263
264     bool is_save_successful = false;
265     saving_savefile = nullptr;
266     if (fd >= 0) {
267         (void)fd_close(fd);
268         safe_setuid_grab(player_ptr);
269         saving_savefile = angband_fopen(name, "wb");
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(player_ptr);
282         if (!is_save_successful) {
283             (void)fd_kill(name);
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  * Attempt to save the player in a savefile
301  * @param player_ptr プレイヤーへの参照ポインタ
302  * @return 成功すればtrue
303  */
304 bool save_player(PlayerType *player_ptr, SaveType type)
305 {
306     char safe[1024];
307     strcpy(safe, savefile);
308     strcat(safe, ".new");
309     safe_setuid_grab(player_ptr);
310     fd_kill(safe);
311     safe_setuid_drop();
312     update_playtime();
313     bool result = false;
314     if (save_player_aux(player_ptr, safe, type)) {
315         char temp[1024];
316         char filename[1024];
317         strcpy(temp, savefile);
318         strcat(temp, ".old");
319         safe_setuid_grab(player_ptr);
320         fd_kill(temp);
321
322         if (type == SaveType::DEBUG) {
323             strcpy(filename, debug_savefile);
324         }
325         if (type != SaveType::DEBUG) {
326             strcpy(filename, savefile);
327         }
328
329         fd_move(filename, temp);
330         fd_move(safe, filename);
331         fd_kill(temp);
332         safe_setuid_drop();
333         w_ptr->character_loaded = true;
334         result = true;
335     }
336
337     if (type != SaveType::CLOSE_GAME) {
338         w_ptr->is_loading_now = false;
339         update_creature(player_ptr);
340         mproc_init(player_ptr->current_floor_ptr);
341         w_ptr->is_loading_now = true;
342     }
343
344     return result;
345 }