OSDN Git Service

Merge pull request #3532 from sikabane-works/release/3.0.0.87-alpha
[hengbandforosx/hengbandosx.git] / src / main / info-initializer.cpp
1 /*!
2  * @file info-initializer.cpp
3  * @brief 変愚蛮怒のゲームデータ解析処理定義
4  */
5
6 #include "main/info-initializer.h"
7 #include "floor/wild.h"
8 #include "info-reader/artifact-reader.h"
9 #include "info-reader/baseitem-reader.h"
10 #include "info-reader/dungeon-reader.h"
11 #include "info-reader/ego-reader.h"
12 #include "info-reader/feature-reader.h"
13 #include "info-reader/fixed-map-parser.h"
14 #include "info-reader/general-parser.h"
15 #include "info-reader/info-reader-util.h"
16 #include "info-reader/magic-reader.h"
17 #include "info-reader/race-reader.h"
18 #include "info-reader/skill-reader.h"
19 #include "info-reader/vault-reader.h"
20 #include "io/files-util.h"
21 #include "io/uid-checker.h"
22 #include "main/angband-headers.h"
23 #include "main/init-error-messages-table.h"
24 #include "monster-race/monster-race.h"
25 #include "object-enchant/object-ego.h"
26 #include "player-info/class-info.h"
27 #include "player/player-skill.h"
28 #include "room/rooms-vault.h"
29 #include "system/angband-version.h"
30 #include "system/artifact-type-definition.h"
31 #include "system/baseitem-info.h"
32 #include "system/dungeon-info.h"
33 #include "system/monster-race-info.h"
34 #include "system/player-type-definition.h"
35 #include "system/terrain-type-definition.h"
36 #include "util/angband-files.h"
37 #include "util/string-processor.h"
38 #include "view/display-messages.h"
39 #include "world/world.h"
40 #include <fstream>
41 #include <string>
42 #include <string_view>
43 #include <sys/stat.h>
44 #ifndef WINDOWS
45 #include <sys/types.h>
46 #endif
47
48 namespace {
49
50 using Retoucher = void (*)(angband_header *);
51
52 template <typename>
53 struct is_vector : std::false_type {
54 };
55
56 template <typename T, typename Alloc>
57 struct is_vector<std::vector<T, Alloc>> : std::true_type {
58 };
59
60 /*!
61  * @brief 与えられた型 T が std::vector 型かどうか調べる
62  * T の型が std::vector<SomeType> に一致する時、is_vector_v<T> == true
63  * 一致しない時、is_vector_v<T> == false となる
64  * @tparam T 調べる型
65  */
66 template <typename T>
67 constexpr bool is_vector_v = is_vector<T>::value;
68
69 }
70
71 /*!
72  * @brief ヘッダ構造体の更新
73  * Initialize the header of an *_info.raw file.
74  * @param head rawファイルのヘッダ
75  * @param num データ数
76  * @param len データの長さ
77  * @return エラーコード
78  */
79 static void init_header(angband_header *head, IDX num = 0)
80 {
81     head->digest = {};
82     head->info_num = (IDX)num;
83 }
84
85 /*!
86  * @brief 各種設定データをlib/edit/のテキストから読み込み
87  * Initialize the "*_info" array
88  * @param filename ファイル名(拡張子txt)
89  * @param head 処理に用いるヘッダ構造体
90  * @param info データ保管先の構造体ポインタ
91  * @return エラーコード
92  * @note
93  * Note that we let each entry have a unique "name" and "text" string,
94  * even if the string happens to be empty (everyone has a unique '\0').
95  */
96 template <typename InfoType>
97 static errr init_info(std::string_view filename, angband_header &head, InfoType &info, Parser parser, Retoucher retouch = nullptr)
98 {
99     const auto &path = path_build(ANGBAND_DIR_EDIT, filename);
100     auto *fp = angband_fopen(path, FileOpenMode::READ);
101     if (!fp) {
102         quit_fmt(_("'%s'ファイルをオープンできません。", "Cannot open '%s' file."), filename.data());
103     }
104
105     constexpr auto info_is_vector = is_vector_v<InfoType>;
106     if constexpr (info_is_vector) {
107         using value_type = typename InfoType::value_type;
108         info.assign(head.info_num, value_type{});
109     }
110
111     char buf[1024]{};
112     const auto err = init_info_txt(fp, buf, &head, parser);
113     angband_fclose(fp);
114     if (err) {
115         const auto oops = (((err > 0) && (err < PARSE_ERROR_MAX)) ? err_str[err] : _("未知の", "unknown"));
116 #ifdef JP
117         msg_format("'%s'ファイルの %d 行目にエラー。", filename.data(), error_line);
118 #else
119         msg_format("Error %d at line %d of '%s'.", err, error_line, filename.data());
120 #endif
121         msg_format(_("レコード %d は '%s' エラーがあります。", "Record %d contains a '%s' error."), error_idx, oops);
122         msg_format(_("構文 '%s'。", "Parsing '%s'."), buf);
123         msg_print(nullptr);
124         quit_fmt(_("'%s'ファイルにエラー", "Error in '%s' file."), filename.data());
125     }
126
127     if constexpr (info_is_vector) {
128         info.shrink_to_fit();
129     }
130
131     head.info_num = static_cast<uint16_t>(info.size());
132     if (retouch) {
133         (*retouch)(&head);
134     }
135
136     return 0;
137 }
138
139 /*!
140  * @brief 固定アーティファクト情報読み込みのメインルーチン
141  * @return エラーコード
142  */
143 errr init_artifacts_info()
144 {
145     init_header(&artifacts_header);
146     return init_info("ArtifactDefinitions.txt", artifacts_header, artifacts_info, parse_artifacts_info);
147 }
148
149 /*!
150  * @brief ベースアイテム情報読み込みのメインルーチン
151  * @return エラーコード
152  */
153 errr init_baseitems_info()
154 {
155     init_header(&baseitems_header);
156     return init_info("BaseitemDefinitions.txt", baseitems_header, baseitems_info, parse_baseitems_info);
157 }
158
159 /*!
160  * @brief 職業魔法情報読み込みのメインルーチン
161  * @return エラーコード
162  */
163 errr init_class_magics_info()
164 {
165     init_header(&class_magics_header, PLAYER_CLASS_TYPE_MAX);
166     auto *parser = parse_class_magics_info;
167     return init_info("ClassMagicDefinitions.txt", class_magics_header, class_magics_info, parser);
168 }
169
170 /*!
171  * @brief 職業技能情報読み込みのメインルーチン
172  * @return エラーコード
173  */
174 errr init_class_skills_info()
175 {
176     init_header(&class_skills_header, PLAYER_CLASS_TYPE_MAX);
177     return init_info("ClassSkillDefinitions.txt", class_skills_header, class_skills_info, parse_class_skills_info);
178 }
179 /*!
180  * @brief ダンジョン情報読み込みのメインルーチン
181  * @return エラーコード
182  */
183 errr init_dungeons_info()
184 {
185     init_header(&dungeons_header);
186     return init_info("DungeonDefinitions.txt", dungeons_header, dungeons_info, parse_dungeons_info);
187 }
188
189 /*!
190  * @brief エゴ情報読み込みのメインルーチン
191  * @return エラーコード
192  */
193 errr init_egos_info()
194 {
195     init_header(&egos_header);
196     return init_info("EgoDefinitions.txt", egos_header, egos_info, parse_egos_info);
197 }
198
199 /*!
200  * @brief 地形情報読み込みのメインルーチン
201  * @return エラーコード
202  */
203 errr init_terrains_info()
204 {
205     init_header(&terrains_header);
206     auto *parser = parse_terrains_info;
207     auto *retoucher = retouch_terrains_info;
208     return init_info("TerrainDefinitions.txt", terrains_header, terrains_info, parser, retoucher);
209 }
210
211 /*!
212  * @brief モンスター種族情報読み込みのメインルーチン
213  * @return エラーコード
214  */
215 errr init_monster_race_definitions()
216 {
217     init_header(&monraces_header);
218     return init_info("MonsterRaceDefinitions.txt", monraces_header, monraces_info, parse_monraces_info);
219 }
220
221 /*!
222  * @brief Vault情報読み込みのメインルーチン
223  * @return エラーコード
224  * @note
225  * Note that we let each entry have a unique "name" and "text" string,
226  * even if the string happens to be empty (everyone has a unique '\0').
227  */
228 errr init_vaults_info()
229 {
230     init_header(&vaults_header);
231     return init_info("VaultDefinitions.txt", vaults_header, vaults_info, parse_vaults_info);
232 }
233
234 static bool read_wilderness_definition(std::ifstream &ifs)
235 {
236     std::string line;
237     while (!ifs.eof()) {
238         if (!std::getline(ifs, line)) {
239             return false;
240         }
241
242         if (line.empty() || line.starts_with('#')) {
243             continue;
244         }
245
246         const auto &splits = str_split(line, ':');
247         if ((splits.size() != 3) || (splits[0] != "M")) {
248             continue;
249         }
250
251         if (splits[1] == "WX") {
252             w_ptr->max_wild_x = std::stoi(splits[2]);
253         } else if (splits[1] == "WY") {
254             w_ptr->max_wild_y = std::stoi(splits[2]);
255         } else {
256             return false;
257         }
258
259         if ((w_ptr->max_wild_x > 0) && (w_ptr->max_wild_y > 0)) {
260             wilderness.assign(w_ptr->max_wild_y, std::vector<wilderness_type>(w_ptr->max_wild_x));
261             init_wilderness_encounter();
262             return true;
263         }
264     }
265
266     return false;
267 }
268
269 /*!
270  * @brief 荒野情報読み込み処理
271  * @return 読み込みに成功したか
272  */
273 bool init_wilderness()
274 {
275     const auto &path = path_build(ANGBAND_DIR_EDIT, WILDERNESS_DEFINITION);
276     std::ifstream ifs(path);
277     if (!ifs) {
278         return false;
279     }
280
281     return read_wilderness_definition(ifs);
282 }