OSDN Git Service

44398328e6aadc560f11093ecdec454c14caf138
[hengbandforosx/hengbandosx.git] / src / save / floor-writer.cpp
1 #include "save/floor-writer.h"
2 #include "core/object-compressor.h"
3 #include "floor/floor-events.h"
4 #include "floor/floor-save-util.h"
5 #include "floor/floor-save.h"
6 #include "grid/grid.h"
7 #include "io/files-util.h"
8 #include "io/uid-checker.h"
9 #include "load/floor-loader.h"
10 #include "monster-floor/monster-lite.h"
11 #include "monster/monster-compaction.h"
12 #include "save/item-writer.h"
13 #include "save/monster-writer.h"
14 #include "save/save-util.h"
15 #include "system/floor-type-definition.h"
16 #include "system/grid-type-definition.h"
17 #include "system/item-entity.h"
18 #include "system/redrawing-flags-updater.h"
19 #include "term/z-form.h"
20 #include "util/angband-files.h"
21 #include "util/sort.h"
22
23 /*!
24  * @brief 保存フロアの書き込み / Actually write a saved floor data using effectively compressed format.
25  * @param sf_ptr 保存したいフロアの参照ポインタ
26  */
27 void wr_saved_floor(PlayerType *player_ptr, saved_floor_type *sf_ptr)
28 {
29     auto *floor_ptr = player_ptr->current_floor_ptr;
30     if (!sf_ptr) {
31         wr_s16b((int16_t)floor_ptr->dun_level);
32     } else {
33         wr_s16b(sf_ptr->floor_id);
34         wr_byte((byte)sf_ptr->savefile_id);
35         wr_s16b((int16_t)sf_ptr->dun_level);
36         wr_s32b(sf_ptr->last_visit);
37         wr_u32b(sf_ptr->visit_mark);
38         wr_s16b(sf_ptr->upper_floor_id);
39         wr_s16b(sf_ptr->lower_floor_id);
40     }
41
42     wr_u16b((uint16_t)floor_ptr->base_level);
43     wr_u16b((int16_t)player_ptr->current_floor_ptr->num_repro);
44     wr_u16b((uint16_t)player_ptr->y);
45     wr_u16b((uint16_t)player_ptr->x);
46     wr_u16b((uint16_t)floor_ptr->height);
47     wr_u16b((uint16_t)floor_ptr->width);
48     wr_byte(player_ptr->feeling);
49
50     /*
51      * Usually number of templates are fewer than 255.  Even if
52      * more than 254 are exist, the occurrence of each template
53      * with larger ID is very small when we sort templates by
54      * occurrence.  So we will use two (or more) bytes for
55      * templete ID larger than 254.
56      *
57      * Ex: 256 will be "0xff" "0x01".
58      *     515 will be "0xff" "0xff" "0x03"
59      */
60
61     std::vector<grid_template_type> templates;
62     for (int y = 0; y < floor_ptr->height; y++) {
63         for (int x = 0; x < floor_ptr->width; x++) {
64             auto *g_ptr = &floor_ptr->grid_array[y][x];
65             uint i;
66             for (i = 0; i < templates.size(); i++) {
67                 if (templates[i].info == g_ptr->info && templates[i].feat == g_ptr->feat && templates[i].mimic == g_ptr->mimic && templates[i].special == g_ptr->special) {
68                     templates[i].occurrence++;
69                     break;
70                 }
71             }
72
73             if (i < templates.size()) {
74                 continue;
75             }
76
77             templates.push_back({ g_ptr->info, g_ptr->feat, g_ptr->mimic, g_ptr->special, 1 });
78         }
79     }
80
81     int dummy_why;
82     ang_sort(player_ptr, templates.data(), &dummy_why, templates.size(), ang_sort_comp_cave_temp, ang_sort_swap_cave_temp);
83
84     /*** Dump templates ***/
85     wr_u16b(static_cast<uint16_t>(templates.size()));
86     for (const auto &ct_ref : templates) {
87         wr_u16b(static_cast<uint16_t>(ct_ref.info));
88         wr_s16b(ct_ref.feat);
89         wr_s16b(ct_ref.mimic);
90         wr_s16b(ct_ref.special);
91     }
92
93     byte count = 0;
94     uint16_t prev_u16b = 0;
95     for (int y = 0; y < floor_ptr->height; y++) {
96         for (int x = 0; x < floor_ptr->width; x++) {
97             auto *g_ptr = &floor_ptr->grid_array[y][x];
98             uint i;
99             for (i = 0; i < templates.size(); i++) {
100                 if (templates[i].info == g_ptr->info && templates[i].feat == g_ptr->feat && templates[i].mimic == g_ptr->mimic && templates[i].special == g_ptr->special) {
101                     break;
102                 }
103             }
104
105             uint16_t tmp16u = (uint16_t)i;
106             if ((tmp16u == prev_u16b) && (count != MAX_UCHAR)) {
107                 count++;
108                 continue;
109             }
110
111             wr_byte((byte)count);
112             while (prev_u16b >= MAX_UCHAR) {
113                 wr_byte(MAX_UCHAR);
114                 prev_u16b -= MAX_UCHAR;
115             }
116
117             wr_byte((byte)prev_u16b);
118             prev_u16b = tmp16u;
119             count = 1;
120         }
121     }
122
123     if (count > 0) {
124         wr_byte((byte)count);
125         while (prev_u16b >= MAX_UCHAR) {
126             wr_byte(MAX_UCHAR);
127             prev_u16b -= MAX_UCHAR;
128         }
129
130         wr_byte((byte)prev_u16b);
131     }
132
133     /*** Dump objects ***/
134     wr_u16b(floor_ptr->o_max);
135     for (int i = 1; i < floor_ptr->o_max; i++) {
136         auto *o_ptr = &floor_ptr->o_list[i];
137         wr_item(o_ptr);
138     }
139
140     /*** Dump the monsters ***/
141     wr_u16b(floor_ptr->m_max);
142     for (int i = 1; i < floor_ptr->m_max; i++) {
143         auto *m_ptr = &floor_ptr->m_list[i];
144         wr_monster(m_ptr);
145     }
146 }
147
148 /*!
149  * @brief 現在フロアの書き込み /
150  * Write the current dungeon (new method)
151  * @player_ptr プレイヤーへの参照ポインタ
152  * @return 保存に成功したらTRUE
153  */
154 bool wr_dungeon(PlayerType *player_ptr)
155 {
156     forget_lite(player_ptr->current_floor_ptr);
157     forget_view(player_ptr->current_floor_ptr);
158     clear_mon_lite(player_ptr->current_floor_ptr);
159     static constexpr auto flags = {
160         StatusRecalculatingFlag::VIEW,
161         StatusRecalculatingFlag::LITE,
162         StatusRecalculatingFlag::MONSTER_LITE,
163         StatusRecalculatingFlag::MONSTER_STATUSES,
164         StatusRecalculatingFlag::DISTANCE,
165         StatusRecalculatingFlag::FLOW,
166     };
167     RedrawingFlagsUpdater::get_instance().set_flags(flags);
168     wr_s16b(max_floor_id);
169     wr_byte((byte)player_ptr->current_floor_ptr->dungeon_idx);
170     if (!player_ptr->floor_id) {
171         /* No array elements */
172         wr_byte(0);
173         wr_saved_floor(player_ptr, nullptr);
174         return true;
175     }
176
177     /*** In the dungeon ***/
178     wr_byte(MAX_SAVED_FLOORS);
179     for (int i = 0; i < MAX_SAVED_FLOORS; i++) {
180         saved_floor_type *sf_ptr = &saved_floors[i];
181         wr_s16b(sf_ptr->floor_id);
182         wr_byte((byte)sf_ptr->savefile_id);
183         wr_s16b((int16_t)sf_ptr->dun_level);
184         wr_s32b(sf_ptr->last_visit);
185         wr_u32b(sf_ptr->visit_mark);
186         wr_s16b(sf_ptr->upper_floor_id);
187         wr_s16b(sf_ptr->lower_floor_id);
188     }
189
190     saved_floor_type *cur_sf_ptr;
191     cur_sf_ptr = get_sf_ptr(player_ptr->floor_id);
192     if (!save_floor(player_ptr, cur_sf_ptr, SLF_SECOND)) {
193         return false;
194     }
195
196     for (int i = 0; i < MAX_SAVED_FLOORS; i++) {
197         saved_floor_type *sf_ptr = &saved_floors[i];
198         if (!sf_ptr->floor_id) {
199             continue;
200         }
201         if (!load_floor(player_ptr, sf_ptr, (SLF_SECOND | SLF_NO_KILL))) {
202             wr_byte(1);
203             continue;
204         }
205
206         wr_byte(0);
207         wr_saved_floor(player_ptr, sf_ptr);
208     }
209
210     return load_floor(player_ptr, cur_sf_ptr, (SLF_SECOND));
211 }
212
213 /*!
214  * @brief ゲームプレイ中のフロア一時保存出力処理サブルーチン / Actually write a temporary saved floor file
215  * @param player_ptr プレイヤーへの参照ポインタ
216  * @param sf_ptr 保存フロア参照ポインタ
217  */
218 static bool save_floor_aux(PlayerType *player_ptr, saved_floor_type *sf_ptr)
219 {
220     compact_objects(player_ptr, 0);
221     compact_monsters(player_ptr, 0);
222
223     byte tmp8u = (byte)randint0(256);
224     save_xor_byte = 0;
225     wr_byte(tmp8u);
226
227     /* Reset the checksum */
228     v_stamp = 0L;
229     x_stamp = 0L;
230     wr_u32b(saved_floor_file_sign);
231     wr_saved_floor(player_ptr, sf_ptr);
232     wr_u32b(v_stamp);
233     wr_u32b(x_stamp);
234
235     return !ferror(saving_savefile) && (fflush(saving_savefile) != EOF);
236 }
237 /*!
238  * @brief ゲームプレイ中のフロア一時保存出力処理メインルーチン / Attempt to save the temporarily saved-floor data
239  * @param player_ptr プレイヤーへの参照ポインタ
240  * @param sf_ptr 保存フロア参照ポインタ
241  * @param mode 保存オプション
242  */
243 bool save_floor(PlayerType *player_ptr, saved_floor_type *sf_ptr, BIT_FLAGS mode)
244 {
245     FILE *old_fff = nullptr;
246     byte old_xor_byte = 0;
247     uint32_t old_v_stamp = 0;
248     uint32_t old_x_stamp = 0;
249
250     if ((mode & SLF_SECOND) != 0) {
251         old_fff = saving_savefile;
252         old_xor_byte = save_xor_byte;
253         old_v_stamp = v_stamp;
254         old_x_stamp = x_stamp;
255     }
256
257     auto floor_savefile = savefile.string();
258     char ext[32];
259     strnfmt(ext, sizeof(ext), ".F%02d", (int)sf_ptr->savefile_id);
260     floor_savefile.append(ext);
261     safe_setuid_grab();
262     fd_kill(floor_savefile);
263     safe_setuid_drop();
264     saving_savefile = nullptr;
265     safe_setuid_grab();
266
267     auto fd = fd_make(floor_savefile);
268     safe_setuid_drop();
269     bool is_save_successful = false;
270     if (fd >= 0) {
271         (void)fd_close(fd);
272         safe_setuid_grab();
273         saving_savefile = angband_fopen(floor_savefile, FileOpenMode::WRITE, true);
274         safe_setuid_drop();
275         if (saving_savefile) {
276             if (save_floor_aux(player_ptr, sf_ptr)) {
277                 is_save_successful = true;
278             }
279
280             if (angband_fclose(saving_savefile)) {
281                 is_save_successful = false;
282             }
283         }
284
285         if (!is_save_successful) {
286             safe_setuid_grab();
287             (void)fd_kill(floor_savefile);
288             safe_setuid_drop();
289         }
290     }
291
292     if ((mode & SLF_SECOND) != 0) {
293         saving_savefile = old_fff;
294         save_xor_byte = old_xor_byte;
295         v_stamp = old_v_stamp;
296         x_stamp = old_x_stamp;
297     }
298
299     return is_save_successful;
300 }