OSDN Git Service

Merge branch 'develop' into macos-develop
[hengbandforosx/hengbandosx.git] / src / save / save.cpp
index 574604d..5e5a05e 100644 (file)
@@ -1,4 +1,4 @@
-/*!
+/*!
  * @file save.c
  * @brief セーブファイル書き込み処理 / Purpose: interact with savefiles
  * @date 2014/07/12
@@ -43,6 +43,8 @@
 #include "view/display-messages.h"
 #include "world/world.h"
 #include <algorithm>
+#include <sstream>
+#include <string>
 
 /*!
  * @brief セーブデータの書き込み /
@@ -108,7 +110,7 @@ static bool wr_savefile_new(PlayerType *player_ptr, SaveType type)
 
     wr_u32b(tmp32u);
     for (int i = tmp32u - 1; i >= 0; i--) {
-        wr_string(message_str(i));
+        wr_string(*message_str(i));
     }
 
     uint16_t tmp16u = static_cast<uint16_t>(monraces_info.size());
@@ -123,7 +125,7 @@ static bool wr_savefile_new(PlayerType *player_ptr, SaveType type)
         wr_perception(bi_id);
     }
 
-    tmp16u = towns_info.size();
+    tmp16u = static_cast<uint16_t>(towns_info.size());
     wr_u16b(tmp16u);
 
     const auto &quest_list = QuestList::get_instance();
@@ -133,27 +135,27 @@ static bool wr_savefile_new(PlayerType *player_ptr, SaveType type)
     tmp8u = MAX_RANDOM_QUEST - MIN_RANDOM_QUEST;
     wr_byte(tmp8u);
 
-    for (const auto &[q_idx, q_ref] : quest_list) {
+    for (const auto &[q_idx, quest] : quest_list) {
         wr_s16b(enum2i(q_idx));
-        wr_s16b(enum2i(q_ref.status));
-        wr_s16b((int16_t)q_ref.level);
-        wr_byte((byte)q_ref.complev);
-        wr_u32b(q_ref.comptime);
-
-        auto is_quest_running = q_ref.status == QuestStatusType::TAKEN;
-        is_quest_running |= q_ref.status == QuestStatusType::COMPLETED;
-        is_quest_running |= !quest_type::is_fixed(q_idx);
+        wr_s16b(enum2i(quest.status));
+        wr_s16b((int16_t)quest.level);
+        wr_byte((byte)quest.complev);
+        wr_u32b(quest.comptime);
+
+        auto is_quest_running = quest.status == QuestStatusType::TAKEN;
+        is_quest_running |= quest.status == QuestStatusType::COMPLETED;
+        is_quest_running |= !QuestType::is_fixed(q_idx);
         if (!is_quest_running) {
             continue;
         }
 
-        wr_s16b((int16_t)q_ref.cur_num);
-        wr_s16b((int16_t)q_ref.max_num);
-        wr_s16b(enum2i(q_ref.type));
-        wr_s16b(enum2i(q_ref.r_idx));
-        wr_s16b(enum2i(q_ref.reward_artifact_idx));
-        wr_byte((byte)q_ref.flags);
-        wr_byte((byte)q_ref.dungeon);
+        wr_s16b((int16_t)quest.cur_num);
+        wr_s16b((int16_t)quest.max_num);
+        wr_s16b(enum2i(quest.type));
+        wr_s16b(enum2i(quest.r_idx));
+        wr_s16b(enum2i(quest.reward_artifact_idx));
+        wr_byte((byte)quest.flags);
+        wr_byte((byte)quest.dungeon);
     }
 
     wr_s32b(player_ptr->wilderness_x);
@@ -171,13 +173,11 @@ static bool wr_savefile_new(PlayerType *player_ptr, SaveType type)
     auto max_a_num = enum2i(artifacts_info.rbegin()->first);
     tmp16u = max_a_num + 1;
     wr_u16b(tmp16u);
-    ArtifactType dummy;
     for (auto i = 0U; i < tmp16u; i++) {
         const auto a_idx = i2enum<FixedArtifactId>(i);
-        const auto it = artifacts_info.find(a_idx);
-        const auto &a_ref = it != artifacts_info.end() ? it->second : dummy;
-        wr_bool(a_ref.is_generated);
-        wr_s16b(a_ref.floor_id);
+        const auto &artifact = ArtifactsInfo::get_instance().get_artifact(a_idx);
+        wr_bool(artifact.is_generated);
+        wr_s16b(artifact.floor_id);
     }
 
     wr_u32b(w_ptr->sf_play_time);
@@ -205,7 +205,7 @@ static bool wr_savefile_new(PlayerType *player_ptr, SaveType type)
 
     for (int i = 0; i < INVEN_TOTAL; i++) {
         auto *o_ptr = &player_ptr->inventory_list[i];
-        if (!o_ptr->bi_id) {
+        if (!o_ptr->is_valid()) {
             continue;
         }
 
@@ -214,14 +214,14 @@ static bool wr_savefile_new(PlayerType *player_ptr, SaveType type)
     }
 
     wr_u16b(0xFFFF);
-    tmp16u = towns_info.size();
+    tmp16u = static_cast<uint16_t>(towns_info.size());
     wr_u16b(tmp16u);
 
     tmp16u = MAX_STORES;
     wr_u16b(tmp16u);
-    for (int i = 1; i < towns_info.size(); i++) {
-        for (int j = 0; j < MAX_STORES; j++) {
-            wr_store(&towns_info[i].store[j]);
+    for (size_t i = 1; i < towns_info.size(); i++) {
+        for (auto sst : STORE_SALE_TYPE_LIST) {
+            wr_store(&towns_info[i].stores[sst]);
         }
     }
 
@@ -248,26 +248,25 @@ static bool wr_savefile_new(PlayerType *player_ptr, SaveType type)
 }
 
 /*!
- * @brief セーブデータ書き込みのサブルーチン /
- * Medium level player saver
+ * @brief セーブデータ書き込みのサブルーチン
  * @param player_ptr プレイヤーへの参照ポインタ
- * @return 成功すればtrue
- * @details
- * Angband 2.8.0 will use "fd" instead of "fff" if possible
+ * @param path セーブデータのフルパス
+ * @param type セーブ後の処理種別
+ * @return セーブの成功可否
  */
-static bool save_player_aux(PlayerType *player_ptr, char *name, SaveType type)
+static bool save_player_aux(PlayerType *player_ptr, const std::filesystem::path &path, SaveType type)
 {
-    safe_setuid_grab(player_ptr);
-    int file_permission = 0644;
-    int fd = fd_make(name, file_permission);
+    safe_setuid_grab();
+    auto fd = fd_make(path);
     safe_setuid_drop();
 
     bool is_save_successful = false;
     saving_savefile = nullptr;
     if (fd >= 0) {
         (void)fd_close(fd);
-        safe_setuid_grab(player_ptr);
-        saving_savefile = angband_fopen(name, "wb");
+        safe_setuid_grab();
+        saving_savefile = angband_fopen(path, FileOpenMode::WRITE, true,
+            FileOpenType::SAVE);
         safe_setuid_drop();
         if (saving_savefile) {
             if (wr_savefile_new(player_ptr, type)) {
@@ -279,9 +278,9 @@ static bool save_player_aux(PlayerType *player_ptr, char *name, SaveType type)
             }
         }
 
-        safe_setuid_grab(player_ptr);
+        safe_setuid_grab();
         if (!is_save_successful) {
-            (void)fd_kill(name);
+            (void)fd_kill(path);
         }
 
         safe_setuid_drop();
@@ -297,39 +296,40 @@ static bool save_player_aux(PlayerType *player_ptr, char *name, SaveType type)
 }
 
 /*!
- * @brief セーブデータ書き込みのメインルーチン /
- * Attempt to save the player in a savefile
+ * @brief セーブデータ書き込みのメインルーチン
  * @param player_ptr プレイヤーへの参照ポインタ
  * @return 成功すればtrue
+ * @details 以下の順番で書き込みを実行する.
+ * 1. hoge.new にセーブデータを書き込む
+ * 2. hoge をhoge.old にリネームする
+ * 3. hoge.new をhoge にリネームする
+ * 4. hoge.old を削除する
  */
 bool save_player(PlayerType *player_ptr, SaveType type)
 {
-    char safe[1024];
-    strcpy(safe, savefile);
-    strcat(safe, ".new");
-    safe_setuid_grab(player_ptr);
-    fd_kill(safe);
+    std::stringstream ss_new;
+    ss_new << savefile.string() << ".new";
+    auto savefile_new = ss_new.str();
+    safe_setuid_grab();
+    fd_kill(savefile_new);
+    if (type == SaveType::DEBUG) {
+        const auto debug_save_dir = std::filesystem::path(debug_savefile).remove_filename();
+        std::error_code ec;
+        std::filesystem::create_directory(debug_save_dir, ec);
+    }
     safe_setuid_drop();
     update_playtime();
     bool result = false;
-    if (save_player_aux(player_ptr, safe, type)) {
-        char temp[1024];
-        char filename[1024];
-        strcpy(temp, savefile);
-        strcat(temp, ".old");
-        safe_setuid_grab(player_ptr);
-        fd_kill(temp);
-
-        if (type == SaveType::DEBUG) {
-            strcpy(filename, debug_savefile);
-        }
-        if (type != SaveType::DEBUG) {
-            strcpy(filename, savefile);
-        }
-
-        fd_move(filename, temp);
-        fd_move(safe, filename);
-        fd_kill(temp);
+    if (save_player_aux(player_ptr, savefile_new.data(), type)) {
+        std::stringstream ss_old;
+        ss_old << savefile.string() << ".old";
+        auto savefile_old = ss_old.str();
+        safe_setuid_grab();
+        fd_kill(savefile_old);
+        const auto &path = type == SaveType::DEBUG ? debug_savefile : savefile;
+        fd_move(path, savefile_old);
+        fd_move(savefile_new, path);
+        fd_kill(savefile_old);
         safe_setuid_drop();
         w_ptr->character_loaded = true;
         result = true;