*/
void strip_name(char *buf, KIND_OBJECT_IDX k_idx)
{
- object_kind *k_ptr = &k_info[k_idx];
- concptr str = k_ptr->name.c_str();
- while ((*str == ' ') || (*str == '&') || (*str == '#'))
- str++;
+ auto k_ptr = &k_info[k_idx];
+ auto tok = str_split(k_ptr->name, ' ');
+ std::string name = "";
+ for (auto s : tok) {
+ if (s == "" || s == "~" || s == "&" || s == "#")
+ continue;
+
+ auto offset = 0;
+ auto endpos = s.size();
+ auto is_kanji = false;
- char *t;
- for (t = buf; *str; str++) {
+ if (s[0] == '~' || s[0] == '#')
+ offset++;
#ifdef JP
- if (iskanji(*str)) {
- *t++ = *str++;
- *t++ = *str;
- continue;
- }
+ if (s.size() > 2)
+ is_kanji = iskanji(s[endpos - 2]);
+
#endif
- if (*str != '~' && *str != '#')
- *t++ = *str;
+ if (!is_kanji && (s[endpos - 1] == '~' || s[endpos - 1] == '#'))
+ endpos--;
+
+ name += s.substr(offset, endpos);
}
- *t = '\0';
+ name += " ";
+ strcpy(buf, name.c_str());
}
FF_CAN_PASS = 55, /*!< 通過可能な地形である */
FF_CAN_DIG = 57, /*!< 掘削コマンドの対象となる地形である */
FF_TREE = 83, /*!< 木の生えた地形である */
+ FF_PLANT = 88, //!< 植物の生えた地形である
FF_SPECIAL = 96, /*!< クエストやダンジョンに関わる特別な地形である */
FF_HURT_DISI = 97, /*!< 分解属性の対象となる地形である */
FF_QUEST_ENTER = 98, /*!< クエストの入り口である */
#include "info-reader/artifact-reader.h"
+#include "info-reader/info-reader-util.h"
#include "info-reader/kind-info-tokens-table.h"
+#include "info-reader/parse-error-types.h"
#include "main/angband-headers.h"
#include "object-enchant/tr-types.h"
#include "system/artifact-type-definition.h"
#include "util/bit-flags-calculator.h"
#include "util/string-processor.h"
#include "view/display-messages.h"
-#include <string>
/*!
* @brief テキストトークンを走査してフラグを一つ得る(アーティファクト用) /
* Grab one activation index flag
* @param a_ptr 保管先のアーティファクト構造体参照ポインタ
* @param what 参照元の文字列ポインタ
- * @return エラーがあった場合1、エラーがない場合0を返す
+ * @return 見つかったらtrue
*/
-static errr grab_one_artifact_flag(artifact_type *a_ptr, concptr what)
+static bool grab_one_artifact_flag(artifact_type *a_ptr, std::string_view what)
{
- if (k_info_flags.find(what) != k_info_flags.end()) {
- add_flag(a_ptr->flags, k_info_flags[what]);
- return 0;
- }
+ if (info_grab_one_flag(a_ptr->flags, k_info_flags, what))
+ return true;
if (EnumClassFlagGroup<TRG>::grab_one_flag(a_ptr->gen_flags, k_info_gen_flags, what))
- return 0;
+ return true;
- msg_format(_("未知の伝説のアイテム・フラグ '%s'。", "Unknown artifact flag '%s'."), what);
- return 1;
+ msg_format(_("未知の伝説のアイテム・フラグ '%s'。", "Unknown artifact flag '%s'."), what.data());
+ return false;
}
/*!
* @param head ヘッダ構造体
* @return エラーコード
*/
-errr parse_a_info(char *buf, angband_header *head)
+errr parse_a_info(std::string_view buf, angband_header *head)
{
static artifact_type *a_ptr = NULL;
- char *s, *t;
- if (buf[0] == 'N') {
- s = angband_strchr(buf + 2, ':');
- if (!s)
- return 1;
+ const auto &tokens = str_split(buf, ':', false, 10);
- *s++ = '\0';
-#ifdef JP
- if (!*s)
- return 1;
-#endif
- int i = atoi(buf + 2);
+ if (tokens[0] == "N") {
+ // N:index:name_ja
+ if (tokens.size() < 3 || tokens[1].size() == 0)
+ return PARSE_ERROR_GENERIC;
+
+ auto i = std::stoi(tokens[1]);
if (i < error_idx)
- return 4;
+ return PARSE_ERROR_NON_SEQUENTIAL_RECORDS;
if (i >= head->info_num)
- return 2;
+ return PARSE_ERROR_OUT_OF_BOUNDS;
error_idx = i;
a_ptr = &a_info[i];
add_flag(a_ptr->flags, TR_IGNORE_ELEC);
add_flag(a_ptr->flags, TR_IGNORE_FIRE);
add_flag(a_ptr->flags, TR_IGNORE_COLD);
+
#ifdef JP
- a_ptr->name = std::string(s);
+ a_ptr->name = tokens[2];
#endif
- } else if (!a_ptr) {
- return 3;
- }
-#ifdef JP
- /* 英語名を読むルーチンを追加 */
- /* 'E' から始まる行は英語名としている */
- else if (buf[0] == 'E') {
- /* nothing to do */
- }
-#else
- else if (buf[0] == 'E') {
- s = buf + 2;
- a_ptr->name = std::string(s);
- }
+ } else if (!a_ptr)
+ return PARSE_ERROR_MISSING_RECORD_HEADER;
+ else if (tokens[0] == "E") {
+ // E:name_en
+#ifndef JP
+ if (tokens[1].size() == 0)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+ a_ptr->name = tokens[1];
#endif
- else if (buf[0] == 'D') {
+ } else if (tokens[0] == "D") {
+ // D:JapaneseText
+ // D:$EnglishText
+ if (tokens.size() < 2 || tokens[1].size() == 0)
+ return PARSE_ERROR_NON_SEQUENTIAL_RECORDS;
#ifdef JP
- if (buf[2] == '$')
- return 0;
- s = buf + 2;
+ if (tokens[1][0] == '$')
+ return PARSE_ERROR_NONE;
+ a_ptr->text.append(buf.substr(2));
#else
- if (buf[2] != '$')
- return 0;
- s = buf + 3;
+ if (tokens[1][0] != '$')
+ return PARSE_ERROR_NONE;
+ a_ptr->text.append(buf.substr(3));
#endif
- a_ptr->text.append(s);
- } else if (buf[0] == 'I') {
- int tval, sval, pval;
- if (3 != sscanf(buf + 2, "%d:%d:%d", &tval, &sval, &pval))
- return 1;
+ } else if (tokens[0] == "I") {
+ // I:tval:sval:pval
+ if (tokens.size() < 4)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
- a_ptr->tval = (tval_type)tval;
- a_ptr->sval = (OBJECT_SUBTYPE_VALUE)sval;
- a_ptr->pval = (PARAMETER_VALUE)pval;
- } else if (buf[0] == 'W') {
- int level, rarity, wgt;
- long cost;
- if (4 != sscanf(buf + 2, "%d:%d:%d:%ld", &level, &rarity, &wgt, &cost))
- return 1;
+ info_set_value(a_ptr->tval, tokens[1]);
+ info_set_value(a_ptr->sval, tokens[2]);
+ info_set_value(a_ptr->pval, tokens[3]);
+ } else if (tokens[0] == "W") {
+ // W:level:ratiry:weight:cost
+ if (tokens.size() < 5)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
- a_ptr->level = (DEPTH)level;
- a_ptr->rarity = (RARITY)rarity;
- a_ptr->weight = (WEIGHT)wgt;
- a_ptr->cost = (PRICE)cost;
- } else if (buf[0] == 'P') {
- int ac, hd1, hd2, th, td, ta;
- if (6 != sscanf(buf + 2, "%d:%dd%d:%d:%d:%d", &ac, &hd1, &hd2, &th, &td, &ta))
- return 1;
+ info_set_value(a_ptr->level, tokens[1]);
+ info_set_value(a_ptr->rarity, tokens[2]);
+ info_set_value(a_ptr->weight, tokens[3]);
+ info_set_value(a_ptr->cost, tokens[4]);
+ } else if (tokens[0] == "P") {
+ // P:ac:dd:ds:to_h:to_d:to_a
+ if (tokens.size() < 6)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
- a_ptr->ac = (ARMOUR_CLASS)ac;
- a_ptr->dd = (DICE_NUMBER)hd1;
- a_ptr->ds = (DICE_SID)hd2;
- a_ptr->to_h = (HIT_PROB)th;
- a_ptr->to_d = (HIT_POINT)td;
- a_ptr->to_a = (ARMOUR_CLASS)ta;
- } else if (buf[0] == 'U') {
- byte n;
- n = grab_one_activation_flag(buf + 2);
- if (n > 0) {
- a_ptr->act_idx = n;
- } else {
- return 5;
- }
- } else if (buf[0] == 'F') {
- for (s = buf + 2; *s;) {
- /* loop */
- for (t = s; *t && (*t != ' ') && (*t != '|'); ++t)
- ;
+ const auto &dice = str_split(tokens[2], 'd', false, 2);
+ if (dice.size() != 2)
+ return PARSE_ERROR_NON_SEQUENTIAL_RECORDS;
- if (*t) {
- *t++ = '\0';
- while ((*t == ' ') || (*t == '|'))
- t++;
- }
+ info_set_value(a_ptr->ac, tokens[1]);
+ info_set_value(a_ptr->dd, dice[0]);
+ info_set_value(a_ptr->ds, dice[1]);
+ info_set_value(a_ptr->to_h, tokens[3]);
+ info_set_value(a_ptr->to_d, tokens[4]);
+ info_set_value(a_ptr->to_a, tokens[5]);
+ } else if (tokens[0] == "U") {
+ // U:activation_flag
+ if (tokens.size() < 2 || tokens[1].size() == 0)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+ auto n = grab_one_activation_flag(tokens[1].c_str());
+ if (n <= 0)
+ return PARSE_ERROR_INVALID_FLAG;
- if (0 != grab_one_artifact_flag(a_ptr, s))
- return 5;
+ a_ptr->act_idx = (IDX)n;
+ } else if (tokens[0] == "F") {
+ // F:flags
+ if (tokens.size() < 2 || tokens[1].size() == 0)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
- s = t;
+ const auto &flags = str_split(tokens[1], '|', true, 10);
+ for (const auto &f : flags) {
+ if (f.size() == 0)
+ continue;
+ if (!grab_one_artifact_flag(a_ptr, f))
+ return PARSE_ERROR_INVALID_FLAG;
}
- } else {
- return 6;
- }
+ } else
+ return PARSE_ERROR_UNDEFINED_DIRECTIVE;
- return 0;
+ return PARSE_ERROR_NONE;
}
#pragma once
-#include "info-reader/info-reader-util.h"
#include "system/angband.h"
+#include <string_view>
-errr parse_a_info(char *buf, angband_header *head);
+struct angband_header;
+errr parse_a_info(std::string_view buf, angband_header *head);
#include "grid/feature.h"
#include "info-reader/dungeon-info-tokens-table.h"
#include "info-reader/feature-reader.h"
+#include "info-reader/info-reader-util.h"
#include "info-reader/parse-error-types.h"
#include "info-reader/race-info-tokens-table.h"
#include "io/tokenizer.h"
#include "main/angband-headers.h"
#include "util/string-processor.h"
#include "view/display-messages.h"
-#include <string>
/*!
* @brief テキストトークンを走査してフラグを一つ得る(ダンジョン用) /
* Grab one flag for a dungeon type from a textual string
* @param d_ptr 保管先のダンジョン構造体参照ポインタ
* @param what 参照元の文字列ポインタ
- * @return エラーコード
+ * @return 見つけたらtrue
*/
-static errr grab_one_dungeon_flag(dungeon_type *d_ptr, concptr what)
+static bool grab_one_dungeon_flag(dungeon_type *d_ptr, std::string_view what)
{
if (EnumClassFlagGroup<DF>::grab_one_flag(d_ptr->flags, d_info_flags, what))
- return 0;
+ return true;
- msg_format(_("未知のダンジョン・フラグ '%s'。", "Unknown dungeon type flag '%s'."), what);
- return 1;
+ msg_format(_("未知のダンジョン・フラグ '%s'。", "Unknown dungeon type flag '%s'."), what.data());
+ return false;
}
/*!
* Grab one (basic) flag in a monster_race from a textual string
* @param d_ptr 保管先のダンジョン構造体参照ポインタ
* @param what 参照元の文字列ポインタ
- * @return エラーコード
+ * @return 見つけたらtrue
*/
-static errr grab_one_basic_monster_flag(dungeon_type *d_ptr, concptr what)
+static bool grab_one_basic_monster_flag(dungeon_type *d_ptr, std::string_view what)
{
- if (grab_one_flag(&d_ptr->mflags1, r_info_flags1, what) == 0)
- return 0;
+ if (info_grab_one_flag(d_ptr->mflags1, r_info_flags1, what))
+ return true;
- if (grab_one_flag(&d_ptr->mflags2, r_info_flags2, what) == 0)
- return 0;
+ if (info_grab_one_flag(d_ptr->mflags2, r_info_flags2, what))
+ return true;
- if (grab_one_flag(&d_ptr->mflags3, r_info_flags3, what) == 0)
- return 0;
+ if (info_grab_one_flag(d_ptr->mflags3, r_info_flags3, what))
+ return true;
- if (grab_one_flag(&d_ptr->mflags7, r_info_flags7, what) == 0)
- return 0;
+ if (info_grab_one_flag(d_ptr->mflags7, r_info_flags7, what))
+ return true;
- if (grab_one_flag(&d_ptr->mflags8, r_info_flags8, what) == 0)
- return 0;
+ if (info_grab_one_flag(d_ptr->mflags8, r_info_flags8, what))
+ return true;
- if (grab_one_flag(&d_ptr->mflags9, r_info_flags9, what) == 0)
- return 0;
+ if (info_grab_one_flag(d_ptr->mflags9, r_info_flags9, what))
+ return true;
- if (grab_one_flag(&d_ptr->mflagsr, r_info_flagsr, what) == 0)
- return 0;
+ if (info_grab_one_flag(d_ptr->mflagsr, r_info_flagsr, what))
+ return true;
- msg_format(_("未知のモンスター・フラグ '%s'。", "Unknown monster flag '%s'."), what);
- return 1;
+ msg_format(_("未知のモンスター・フラグ '%s'。", "Unknown monster flag '%s'."), what.data());
+ return false;
}
/*!
* Grab one (spell) flag in a monster_race from a textual string
* @param d_ptr 保管先のダンジョン構造体参照ポインタ
* @param what 参照元の文字列ポインタ
- * @return エラーコード
+ * @return 見つけたらtrue
*/
-static errr grab_one_spell_monster_flag(dungeon_type *d_ptr, concptr what)
+static bool grab_one_spell_monster_flag(dungeon_type *d_ptr, std::string_view what)
{
if (EnumClassFlagGroup<RF_ABILITY>::grab_one_flag(d_ptr->m_ability_flags, r_info_ability_flags, what))
- return 0;
+ return true;
- msg_format(_("未知のモンスター・フラグ '%s'。", "Unknown monster flag '%s'."), what);
- return 1;
+ msg_format(_("未知のモンスター・フラグ '%s'。", "Unknown monster flag '%s'."), what.data());
+ return false;
}
/*!
* @param head ヘッダ構造体
* @return エラーコード
*/
-errr parse_d_info(char *buf, angband_header *head)
+errr parse_d_info(std::string_view buf, angband_header *head)
{
static dungeon_type *d_ptr = NULL;
- char *s, *t;
- if (buf[0] == 'N') {
- s = angband_strchr(buf + 2, ':');
- if (!s)
- return 1;
+ const auto &tokens = str_split(buf, ':', false);
- *s++ = '\0';
-#ifdef JP
- if (!*s)
- return 1;
-#endif
+ if (tokens[0] == "N") {
+ // N:index:name_ja
+ if (tokens.size() < 3 || tokens[1].size() == 0)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
- int i = atoi(buf + 2);
+ auto i = std::stoi(tokens[1]);
if (i < error_idx)
- return 4;
+ return PARSE_ERROR_NON_SEQUENTIAL_RECORDS;
if (i >= head->info_num)
- return 2;
+ return PARSE_ERROR_OUT_OF_BOUNDS;
error_idx = i;
d_ptr = &d_info[i];
#ifdef JP
- d_ptr->name = std::string(s);
+ d_ptr->name = tokens[2];
#endif
- }
-#ifdef JP
- else if (buf[0] == 'E')
- return 0;
-#else
- else if (buf[0] == 'E') {
- /* Acquire the Text */
- s = buf + 2;
-
- /* Store the name */
- d_ptr->name = std::string(s);
- }
+ } else if (!d_ptr)
+ return PARSE_ERROR_MISSING_RECORD_HEADER;
+ else if (tokens[0] == "E") {
+ // E:name_en
+#ifndef JP
+ if (tokens.size() < 2 || tokens[1].size() == 0)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+ d_ptr->name = tokens[1];
#endif
- else if (buf[0] == 'D') {
+ } else if (tokens[0] == "D") {
+ // D:text_ja
+ // D:$text_en
+ if (tokens.size() < 2 || tokens[1].size() == 0)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
#ifdef JP
- if (buf[2] == '$')
- return 0;
- s = buf + 2;
+ if (tokens[1][0] == '$')
+ return PARSE_ERROR_NONE;
+ d_ptr->text.append(buf.substr(2));
#else
- if (buf[2] != '$')
- return 0;
- s = buf + 3;
+ if (tokens[1][0] != '$')
+ return PARSE_ERROR_NONE;
+ d_ptr->text.append(buf.substr(3));
#endif
- d_ptr->text.append(s);
- } else if (buf[0] == 'W') {
- int min_lev, max_lev;
- int min_plev, mode;
- int min_alloc, max_chance;
- int obj_good, obj_great;
- int pit, nest;
-
- if (10
- != sscanf(buf + 2, "%d:%d:%d:%d:%d:%d:%d:%d:%x:%x", &min_lev, &max_lev, &min_plev, &mode, &min_alloc, &max_chance, &obj_good, &obj_great,
- (unsigned int *)&pit, (unsigned int *)&nest))
- return 1;
-
- d_ptr->mindepth = (DEPTH)min_lev;
- d_ptr->maxdepth = (DEPTH)max_lev;
- d_ptr->min_plev = (PLAYER_LEVEL)min_plev;
- d_ptr->mode = (BIT_FLAGS8)mode;
- d_ptr->min_m_alloc_level = min_alloc;
- d_ptr->max_m_alloc_chance = max_chance;
- d_ptr->obj_good = obj_good;
- d_ptr->obj_great = obj_great;
- d_ptr->pit = (BIT_FLAGS16)pit;
- d_ptr->nest = (BIT_FLAGS16)nest;
- } else if (buf[0] == 'P') {
- int dy, dx;
- if (2 != sscanf(buf + 2, "%d:%d", &dy, &dx))
- return 1;
-
- d_ptr->dy = dy;
- d_ptr->dx = dx;
- } else if (buf[0] == 'L') {
- char *zz[16];
- if (tokenize(buf + 2, DUNGEON_FEAT_PROB_NUM * 2 + 1, zz, 0) != (DUNGEON_FEAT_PROB_NUM * 2 + 1))
- return 1;
-
- for (int i = 0; i < DUNGEON_FEAT_PROB_NUM; i++) {
- d_ptr->floor[i].feat = f_tag_to_index(zz[i * 2]);
+ } else if (tokens[0] == "W") {
+ // W:min_level:max_level:(1):mode:(2):(3):(4):(5):prob_pit:prob_nest
+ // (1)minimum player level (unused)
+ // (2)minimum level of allocating monster
+ // (3)maximum probability of level boost of allocation monster
+ // (4)maximum probability of dropping good objects
+ // (5)maximum probability of dropping great objects
+ if (tokens.size() < 11)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+
+ info_set_value(d_ptr->mindepth, tokens[1]);
+ info_set_value(d_ptr->maxdepth, tokens[2]);
+ info_set_value(d_ptr->min_plev, tokens[3]);
+ info_set_value(d_ptr->mode, tokens[4]);
+ info_set_value(d_ptr->min_m_alloc_level, tokens[5]);
+ info_set_value(d_ptr->max_m_alloc_chance, tokens[6]);
+ info_set_value(d_ptr->obj_good, tokens[7]);
+ info_set_value(d_ptr->obj_great, tokens[8]);
+ info_set_value(d_ptr->pit, tokens[9]);
+ info_set_value(d_ptr->nest, tokens[10]);
+ } else if (tokens[0] == "P") {
+ // P:wild_y:wild_x
+ if (tokens.size() < 3)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+
+ info_set_value(d_ptr->dy, tokens[1]);
+ info_set_value(d_ptr->dx, tokens[2]);
+ } else if (tokens[0] == "L") {
+ // L:floor_1:prob_1:floor_2:prob_2:floor_3:prob_3:tunnel_prob
+ if (tokens.size() < DUNGEON_FEAT_PROB_NUM * 2 + 2)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+
+ for (size_t i = 0; i < DUNGEON_FEAT_PROB_NUM; i++) {
+ auto feat_idx = i * 2 + 1;
+ auto per_idx = feat_idx + 1;
+ d_ptr->floor[i].feat = f_tag_to_index(tokens[feat_idx]);
if (d_ptr->floor[i].feat < 0)
return PARSE_ERROR_UNDEFINED_TERRAIN_TAG;
- d_ptr->floor[i].percent = (PERCENTAGE)atoi(zz[i * 2 + 1]);
+ info_set_value(d_ptr->floor[i].percent, tokens[per_idx]);
}
- d_ptr->tunnel_percent = atoi(zz[DUNGEON_FEAT_PROB_NUM * 2]);
- } else if (buf[0] == 'A') {
- char *zz[16];
- if (tokenize(buf + 2, DUNGEON_FEAT_PROB_NUM * 2 + 4, zz, 0) != (DUNGEON_FEAT_PROB_NUM * 2 + 4))
- return 1;
+ auto tunnel_idx = DUNGEON_FEAT_PROB_NUM * 2 + 1;
+ info_set_value(d_ptr->tunnel_percent, tokens[tunnel_idx]);
+ } else if (tokens[0] == "A") {
+ // A:wall_1:prob_1:wall_2:prob_2:wall_3:prob_3:outer_wall:inner_wall:stream_1:stream_2
+ if (tokens.size() < DUNGEON_FEAT_PROB_NUM * 2 + 5)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
for (int i = 0; i < DUNGEON_FEAT_PROB_NUM; i++) {
- d_ptr->fill[i].feat = f_tag_to_index(zz[i * 2]);
+ auto feat_idx = i * 2 + 1;
+ auto prob_idx = feat_idx + 1;
+ d_ptr->fill[i].feat = f_tag_to_index(tokens[feat_idx]);
if (d_ptr->fill[i].feat < 0)
return PARSE_ERROR_UNDEFINED_TERRAIN_TAG;
- d_ptr->fill[i].percent = (PERCENTAGE)atoi(zz[i * 2 + 1]);
+ info_set_value(d_ptr->fill[i].percent, tokens[prob_idx]);
}
- d_ptr->outer_wall = f_tag_to_index(zz[DUNGEON_FEAT_PROB_NUM * 2]);
+ auto idx = DUNGEON_FEAT_PROB_NUM * 2 + 1;
+ d_ptr->outer_wall = f_tag_to_index(tokens[idx++]);
if (d_ptr->outer_wall < 0)
return PARSE_ERROR_UNDEFINED_TERRAIN_TAG;
- d_ptr->inner_wall = f_tag_to_index(zz[DUNGEON_FEAT_PROB_NUM * 2 + 1]);
+ d_ptr->inner_wall = f_tag_to_index(tokens[idx++]);
if (d_ptr->inner_wall < 0)
return PARSE_ERROR_UNDEFINED_TERRAIN_TAG;
- d_ptr->stream1 = f_tag_to_index(zz[DUNGEON_FEAT_PROB_NUM * 2 + 2]);
+ d_ptr->stream1 = f_tag_to_index(tokens[idx++]);
if (d_ptr->stream1 < 0)
return PARSE_ERROR_UNDEFINED_TERRAIN_TAG;
- d_ptr->stream2 = f_tag_to_index(zz[DUNGEON_FEAT_PROB_NUM * 2 + 3]);
+ d_ptr->stream2 = f_tag_to_index(tokens[idx]);
if (d_ptr->stream2 < 0)
return PARSE_ERROR_UNDEFINED_TERRAIN_TAG;
- } else if (buf[0] == 'F') {
- int artif = 0, monst = 0;
-
- for (s = buf + 2; *s;) {
- /* loop */
- for (t = s; *t && (*t != ' ') && (*t != '|'); ++t)
- ;
-
- if (*t) {
- *t++ = '\0';
- while (*t == ' ' || *t == '|')
- t++;
- }
-
- if (1 == sscanf(s, "FINAL_ARTIFACT_%d", &artif)) {
- d_ptr->final_artifact = (ARTIFACT_IDX)artif;
- s = t;
+ } else if (tokens[0] == "F") {
+ // F:flags
+ if (tokens.size() < 2)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+
+ const auto &flags = str_split(tokens[1], '|', true);
+ for (const auto &f : flags) {
+ if (f.size() == 0)
continue;
- }
- if (1 == sscanf(s, "FINAL_OBJECT_%d", &artif)) {
- d_ptr->final_object = (KIND_OBJECT_IDX)artif;
- s = t;
- continue;
- }
-
- if (1 == sscanf(s, "FINAL_GUARDIAN_%d", &monst)) {
- d_ptr->final_guardian = (MONRACE_IDX)monst;
- s = t;
- continue;
+ const auto &f_tokens = str_split(f, '_');
+ if (f_tokens.size() == 3) {
+ if (f_tokens[0] == "FINAL" && f_tokens[1] == "ARTIFACT") {
+ info_set_value(d_ptr->final_artifact, f_tokens[2]);
+ continue;
+ }
+ if (f_tokens[0] == "FINAL" && f_tokens[1] == "OBJECT") {
+ info_set_value(d_ptr->final_object, f_tokens[2]);
+ continue;
+ }
+ if (f_tokens[0] == "FINAL" && f_tokens[1] == "GUARDIAN") {
+ info_set_value(d_ptr->final_guardian, f_tokens[2]);
+ continue;
+ }
+ if (f_tokens[0] == "MONSTER" && f_tokens[1] == "DIV") {
+ info_set_value(d_ptr->special_div, f_tokens[2]);
+ continue;
+ }
}
- if (1 == sscanf(s, "MONSTER_DIV_%d", &monst)) {
- d_ptr->special_div = (PROB)monst;
- s = t;
+ if (!grab_one_dungeon_flag(d_ptr, f))
+ return PARSE_ERROR_INVALID_FLAG;
+ }
+ } else if (tokens[0] == "M") {
+ // M:monsterflags
+ if (tokens.size() < 2)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+
+ const auto &flags = str_split(tokens[1], '|', true);
+ for (const auto &f : flags) {
+ if (f.size() == 0)
continue;
- }
-
- if (0 != grab_one_dungeon_flag(d_ptr, s))
- return 5;
- s = t;
- }
- } else if (buf[0] == 'M') {
- for (s = buf + 2; *s;) {
- /* loop */
- for (t = s; *t && (*t != ' ') && (*t != '|'); ++t)
- ;
-
- if (*t) {
- *t++ = '\0';
- while (*t == ' ' || *t == '|')
- t++;
- }
+ const auto &m_tokens = str_split(f, '_');
+ if (m_tokens[0] == "R" && m_tokens[1] == "CHAR") {
+ if (m_tokens[2].size() > 4)
+ return PARSE_ERROR_GENERIC;
- if (!strncmp(s, "R_CHAR_", 7)) {
- s += 7;
- angband_strcpy(d_ptr->r_char, s, sizeof(d_ptr->r_char));
- s = t;
+ strcpy(d_ptr->r_char, m_tokens[2].c_str());
continue;
}
- if (0 != grab_one_basic_monster_flag(d_ptr, s))
- return 5;
-
- s = t;
+ if (!grab_one_basic_monster_flag(d_ptr, f))
+ return PARSE_ERROR_INVALID_FLAG;
}
- } else if (buf[0] == 'S') {
- for (s = buf + 2; *s;) {
- /* loop */
- for (t = s; *t && (*t != ' ') && (*t != '|'); ++t)
- ;
-
- if (*t) {
- *t++ = '\0';
- while ((*t == ' ') || (*t == '|'))
- t++;
- }
-
- int i;
- if (1 == sscanf(s, "1_IN_%d", &i)) {
- s = t;
+ } else if (tokens[0] == "S") {
+ // S: flags
+ if (tokens.size() < 2)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+
+ const auto &flags = str_split(tokens[1], '|', true);
+ for (const auto &f : flags) {
+ if (f.size() == 0)
continue;
- }
- if (0 != grab_one_spell_monster_flag(d_ptr, s))
- return 5;
+ const auto &s_tokens = str_split(f, '_');
+ if (s_tokens.size() == 3 && s_tokens[1] == "IN") {
+ if (s_tokens[0] != "1")
+ return PARSE_ERROR_GENERIC;
+ continue; //!< r_info.txtからのコピペ対策
+ }
- s = t;
+ if (!grab_one_spell_monster_flag(d_ptr, f))
+ return PARSE_ERROR_INVALID_FLAG;
}
- } else {
- return 6;
}
+ else
+ return PARSE_ERROR_UNDEFINED_DIRECTIVE;
return 0;
}
#pragma once
#include "system/angband.h"
-#include "info-reader/info-reader-util.h"
+#include <string_view>
-errr parse_d_info(char *buf, angband_header *head);
+struct angband_header;
+errr parse_d_info(std::string_view buf, angband_header *head);
#include "info-reader/ego-reader.h"
+#include "info-reader/info-reader-util.h"
#include "info-reader/kind-info-tokens-table.h"
+#include "info-reader/parse-error-types.h"
#include "main/angband-headers.h"
#include "object-enchant/object-ego.h"
#include "object-enchant/tr-types.h"
-#include "parse-error-types.h"
#include "util/bit-flags-calculator.h"
#include "util/string-processor.h"
#include "view/display-messages.h"
-#include <string>
-#include <utility>
/*!
* @brief テキストトークンを走査してフラグを一つ得る(エゴ用) /
* Grab one flag in a ego-item_type from a textual string
* @param e_ptr 保管先のエゴ構造体参照ポインタ
* @param what 参照元の文字列ポインタ
- * @return エラーがあった場合1、エラーがない場合0を返す
+ * @return 見つけたらtrue
*/
static bool grab_one_ego_item_flag(ego_item_type *e_ptr, std::string_view what)
{
- if (k_info_flags.find(what) != k_info_flags.end()) {
- add_flag(e_ptr->flags, k_info_flags[what]);
- return 0;
- }
+ if (info_grab_one_flag(e_ptr->flags, k_info_flags, what))
+ return true;
if (EnumClassFlagGroup<TRG>::grab_one_flag(e_ptr->gen_flags, k_info_gen_flags, what))
- return 0;
+ return true;
- msg_format(_("未知の名のあるアイテム・フラグ '%s'。", "Unknown ego-item flag '%s'."), what);
- return 1;
+ msg_format(_("未知の名のあるアイテム・フラグ '%s'。", "Unknown ego-item flag '%s'."), what.data());
+ return false;
}
+/*!
+ * @brief テキストトークンを走査して生成フラグを一つ得る(エゴ用) /
+ * Grab one genetation flag in a ego-item_type from a textual string
+ * @param e_ptr 保管先のエゴ構造体参照ポインタ
+ * @param what 参照元の文字列ポインタ
+ * @return 見つけたらtrue
+ */
static bool grab_ego_generate_flags(ego_generate_type &xtra, std::string_view what)
{
- if (k_info_flags.find(what) != k_info_flags.end()) {
- xtra.tr_flags.push_back(k_info_flags[what]);
- return 0;
+ if (auto it = k_info_flags.find(what); it != k_info_flags.end()) {
+ xtra.tr_flags.push_back(it->second);
+ return true;
}
- auto it = k_info_gen_flags.find(what);
- if (it != k_info_gen_flags.end()) {
+ if (auto it = k_info_gen_flags.find(what); it != k_info_gen_flags.end()) {
xtra.trg_flags.push_back(it->second);
- return false;
+ return true;
}
- return true;
+ return false;
}
-
/*!
* @brief アイテムエゴ情報(e_info)のパース関数 /
* Initialize the "e_info" array, by parsing an ascii "template" file
* @param head ヘッダ構造体
* @return エラーコード
*/
-errr parse_e_info(char *buf, angband_header *head)
+errr parse_e_info(std::string_view buf, angband_header *head)
{
static ego_item_type *e_ptr = NULL;
- auto line = std::string(buf);
- auto tok = str_split(line, ':', false);
+ const auto &tokens = str_split(buf, ':', false, 10);
- error_idx = 0;
+ error_idx = 0; //!< @note 順不同で登録しているため
- if (tok[0] == "N") {
+ if (tokens[0] == "N") {
// N:index:name_ja
- if (tok[1].size() == 0)
+ if (tokens.size() < 3 || tokens[1].size() == 0)
return PARSE_ERROR_GENERIC;
- auto i = std::stoi(tok[1]);
+ auto i = std::stoi(tokens[1]);
if (i < error_idx)
return PARSE_ERROR_NON_SEQUENTIAL_RECORDS;
if (i >= head->info_num)
error_idx = i;
e_ptr = &e_info[i];
#ifdef JP
- e_ptr->name = tok[2];
+ e_ptr->name = tokens[2];
#endif
} else if (!e_ptr)
return PARSE_ERROR_MISSING_RECORD_HEADER;
- else if (tok[0] == "E") {
+ else if (tokens[0] == "E") {
// E:name_en
#ifndef JP
- if (tok[1].size() == 0)
- return 1;
- e_ptr->name = tok[1];
+ if (tokens[1].size() == 0)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+ e_ptr->name = tokens[1];
#endif
- }
- else if (tok[0] == "X") {
+ } else if (tokens[0] == "X") {
// X:slot:rating
- if (tok.size() < 3)
+ if (tokens.size() < 3)
return PARSE_ERROR_TOO_FEW_ARGUMENTS;
- e_ptr->slot = (INVENTORY_IDX)std::stoi(tok[1]);
- e_ptr->rating = (PRICE)std::stoi(tok[2]);
- } else if (tok[0] == "W") {
+
+ info_set_value(e_ptr->slot, tokens[1]);
+ info_set_value(e_ptr->rating, tokens[2]);
+ } else if (tokens[0] == "W") {
// W:level:ratiry:xtra:cost
// xtra is not used
- if (tok.size() < 5)
+ if (tokens.size() < 5)
return PARSE_ERROR_TOO_FEW_ARGUMENTS;
- e_ptr->level = (DEPTH)std::stoi(tok[1]);
- e_ptr->rarity = (RARITY)std::stoi(tok[2]);
- e_ptr->cost = (PRICE)std::stoi(tok[4]);
- } else if (tok[0] == "B") {
+
+ info_set_value(e_ptr->level, tokens[1]);
+ info_set_value(e_ptr->rarity, tokens[2]);
+ info_set_value(e_ptr->cost, tokens[4]);
+ } else if (tokens[0] == "B") {
// Base bonuses
// B:to_hit:to_dam:to_ac
- if (tok.size() < 4)
- return 1;
- e_ptr->base_to_h = (HIT_PROB)std::stoi(tok[1]);
- e_ptr->base_to_d = (HIT_POINT)std::stoi(tok[2]);
- e_ptr->base_to_a = (ARMOUR_CLASS)std::stoi(tok[3]);
- } else if (tok[0] == "C") {
+ if (tokens.size() < 4)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+
+ info_set_value(e_ptr->base_to_h, tokens[1]);
+ info_set_value(e_ptr->base_to_d, tokens[2]);
+ info_set_value(e_ptr->base_to_a, tokens[3]);
+ } else if (tokens[0] == "C") {
// Creation bonuses (random plus)
// C:to_hit:to_dam:to_ac:pval
- if (tok.size() < 5)
+ if (tokens.size() < 5)
return PARSE_ERROR_TOO_FEW_ARGUMENTS;
- e_ptr->max_to_h = (HIT_PROB)std::stoi(tok[1]);
- e_ptr->max_to_d = (HIT_POINT)std::stoi(tok[2]);
- e_ptr->max_to_a = (ARMOUR_CLASS)std::stoi(tok[3]);
- e_ptr->max_pval = (PARAMETER_VALUE)std::stoi(tok[4]);
- } else if (tok[0] == "U") {
+
+ info_set_value(e_ptr->max_to_h, tokens[1]);
+ info_set_value(e_ptr->max_to_d, tokens[2]);
+ info_set_value(e_ptr->max_to_a, tokens[3]);
+ info_set_value(e_ptr->max_pval, tokens[4]);
+ } else if (tokens[0] == "U") {
// U:activation_flag
- if (tok.size() < 2 || tok[1].size() == 0)
+ if (tokens.size() < 2 || tokens[1].size() == 0)
return PARSE_ERROR_TOO_FEW_ARGUMENTS;
- auto n = grab_one_activation_flag(tok[1].c_str());
+
+ auto n = grab_one_activation_flag(tokens[1].c_str());
if (n <= 0)
return PARSE_ERROR_INVALID_FLAG;
+
e_ptr->act_idx = (IDX)n;
- } else if (tok[0] == "F") {
+ } else if (tokens[0] == "F") {
// F:flags
- if (tok.size() < 2 || tok[1].size() == 0)
- return PARSE_ERROR_UNDEFINED_TERRAIN_TAG;
+ if (tokens.size() < 2 || tokens[1].size() == 0)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
- const auto& flags = str_split(tok[1], '|', true);
+ const auto& flags = str_split(tokens[1], '|', true, 10);
for (const auto& f : flags) {
if (f.size() == 0)
continue;
- if (0 != grab_one_ego_item_flag(e_ptr, f))
+ if (!grab_one_ego_item_flag(e_ptr, f))
return PARSE_ERROR_INVALID_FLAG;
}
- } else if (tok[0] == "G") {
+ } else if (tokens[0] == "G") {
// G:mul/dev:flags
- if (tok.size() < 3)
- return PARSE_ERROR_UNDEFINED_TERRAIN_TAG;
+ if (tokens.size() < 3)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
- auto prob = str_split(tok[1], '/');
- if (prob.size() != 2 || tok[1].size() == 0 || tok[2].size() == 0)
+ const auto &prob = str_split(tokens[1], '/', false, 2);
+ if (prob.size() != 2 || tokens[1].size() == 0 || tokens[2].size() == 0)
return PARSE_ERROR_TOO_FEW_ARGUMENTS;
ego_generate_type xtra;
xtra.mul = std::stoi(prob[0]);
xtra.dev = std::stoi(prob[1]);
- const auto& flags = str_split(tok[2], '|', true);
+ const auto& flags = str_split(tokens[2], '|', true, 10);
for (const auto& f : flags) {
if (f.size() == 0)
continue;
- if (grab_ego_generate_flags(xtra, f))
+ if (!grab_ego_generate_flags(xtra, f))
return PARSE_ERROR_INVALID_FLAG;
}
#pragma once
#include "system/angband.h"
-#include "parse-error-types.h"
-#include "info-reader/info-reader-util.h"
+#include <string_view>
-errr parse_e_info(char *buf, angband_header *head);
+struct angband_header;
+errr parse_e_info(std::string_view buf, angband_header *head);
#include "info-reader/feature-info-tokens-table.h"
/*!
- * 地形属性トークンの定義 /
- * Feature info flags
+ * @brief 地形属性トークンの定義 / Feature info flags
*/
-concptr f_info_flags[NUM_F_FLAGS] = {
- "LOS",
- "PROJECT",
- "MOVE",
- "PLACE",
- "DROP",
- "SECRET",
- "NOTICE",
- "REMEMBER",
- "OPEN",
- "CLOSE",
- "BASH",
- "SPIKE",
- "DISARM",
- "STORE",
- "TUNNEL",
- "MAY_HAVE_GOLD",
- "HAS_GOLD",
- "HAS_ITEM",
- "DOOR",
- "TRAP",
- "STAIRS",
- "RUNE_PROTECTION",
- "LESS",
- "MORE",
- "AVOID_RUN",
- "FLOOR",
- "WALL",
- "PERMANENT",
- "XXX00",
- "XXX01",
- "XXX02",
- "HIT_TRAP",
+const std::unordered_map<std::string_view, feature_flag_type> f_info_flags = {
+ { "LOS", FF_LOS },
+ { "PROJECT", FF_PROJECT },
+ { "MOVE", FF_MOVE },
+ { "PLACE", FF_PLACE },
+ { "DROP", FF_DROP },
+ { "SECRET", FF_SECRET },
+ { "NOTICE", FF_NOTICE },
+ { "REMEMBER", FF_REMEMBER },
+ { "OPEN", FF_OPEN },
+ { "CLOSE", FF_CLOSE },
+ { "BASH", FF_BASH },
+ { "SPIKE", FF_SPIKE },
+ { "DISARM", FF_DISARM },
+ { "STORE", FF_STORE },
+ { "TUNNEL", FF_TUNNEL },
+ { "MAY_HAVE_GOLD", FF_MAY_HAVE_GOLD },
+ { "HAS_GOLD", FF_HAS_GOLD },
+ { "HAS_ITEM", FF_HAS_ITEM },
+ { "DOOR", FF_DOOR },
+ { "TRAP", FF_TRAP },
+ { "STAIRS", FF_STAIRS },
+ { "RUNE_PROTECTION", FF_RUNE_PROTECTION },
+ { "LESS", FF_LESS },
+ { "MORE", FF_MORE },
+ { "AVOID_RUN", FF_AVOID_RUN },
+ { "FLOOR", FF_FLOOR },
+ { "WALL", FF_WALL },
+ { "PERMANENT", FF_PERMANENT },
+ // { "XXX00", FF_XXX00 },
+ // { "XXX01", FF_XXX01 },
+ // { "XXX02", FF_XXX02 },
+ { "HIT_TRAP", FF_HIT_TRAP },
- "BRIDGE",
- "RIVER",
- "LAKE",
- "BRIDGED",
- "COVERED",
- "GLOW",
- "ENSECRET",
- "WATER",
- "LAVA",
- "SHALLOW",
- "DEEP",
- "POISON_PUDDLE",
- "HURT_ROCK",
- "HURT_FIRE",
- "HURT_COLD",
- "HURT_ACID",
- "COLD_PUDDLE",
- "ACID_PUDDLE",
- "OIL",
- "ELEC_PUDDLE",
- "CAN_CLIMB",
- "CAN_FLY",
- "CAN_SWIM",
- "CAN_PASS",
- "CAN_OOZE",
- "CAN_DIG",
- "HIDE_ITEM",
- "HIDE_SNEAK",
- "HIDE_SWIM",
- "HIDE_DIG",
- "KILL_HUGE",
- "KILL_MOVE",
+ // { "BRIDGE", FF_BRIDGE },
+ // { "RIVER", FF_RIVER },
+ // { "LAKE", FF_LAKE },
+ // { "BRIDGED", FF_BRIDGED },
+ // { "COVERED", FF_COVERED },
+ { "GLOW", FF_GLOW },
+ { "ENSECRET", FF_ENSECRET },
+ { "WATER", FF_WATER },
+ { "LAVA", FF_LAVA },
+ { "SHALLOW", FF_SHALLOW },
+ { "DEEP", FF_DEEP },
+ { "POISON_PUDDLE", FF_POISON_PUDDLE },
+ { "HURT_ROCK", FF_HURT_ROCK },
+ // { "HURT_FIRE", FF_HURT_FIRE },
+ // { "HURT_COLD", FF_HURT_COLD },
+ // { "HURT_ACID", FF_HURT_ACID },
+ { "COLD_PUDDLE", FF_COLD_PUDDLE },
+ { "ACID_PUDDLE", FF_ACID_PUDDLE },
+ // { "OIL", FF_OIL },
+ { "ELEC_PUDDLE", FF_ELEC_PUDDLE },
+ // { "CAN_CLIMB", FF_CAN_CLIMB },
+ { "CAN_FLY", FF_CAN_FLY },
+ { "CAN_SWIM", FF_CAN_SWIM },
+ { "CAN_PASS", FF_CAN_PASS },
+ // { "CAN_OOZE", FF_CAN_OOZE },
+ { "CAN_DIG", FF_CAN_DIG },
+ // { "HIDE_ITEM", FF_HIDE_ITEM },
+ // { "HIDE_SNEAK", FF_HIDE_SNEAK },
+ // { "HIDE_SWIM", FF_HIDE_SWIM },
+ // { "HIDE_DIG", FF_HIDE_DIG },
+ // { "KILL_HUGE", FF_KILL_HUGE },
+ // { "KILL_MOVE", FF_KILL_MOVE },
- "PICK_TRAP",
- "PICK_DOOR",
- "ALLOC",
- "CHEST",
- "DROP_1D2",
- "DROP_2D2",
- "DROP_GOOD",
- "DROP_GREAT",
- "HURT_POIS",
- "HURT_ELEC",
- "HURT_WATER",
- "HURT_BWATER",
- "USE_FEAT",
- "GET_FEAT",
- "GROUND",
- "OUTSIDE",
- "EASY_HIDE",
- "EASY_CLIMB",
- "MUST_CLIMB",
- "TREE",
- "NEED_TREE",
- "BLOOD",
- "DUST",
- "SLIME",
- "PLANT",
- "XXX2",
- "INSTANT",
- "EXPLODE",
- "TIMED",
- "ERUPT",
- "STRIKE",
- "SPREAD",
+ // { "PICK_TRAP", FF_PICK_TRAP },
+ // { "PICK_DOOR", FF_PICK_DOOR },
+ // { "ALLOC", FF_ALLOC },
+ // { "CHEST", FF_CHEST },
+ // { "DROP_1D2", FF_DROP_1D2 },
+ // { "DROP_2D2", FF_DROP_2D2 },
+ // { "DROP_GOOD", FF_DROP_GOOD },
+ // { "DROP_GREAT", FF_DROP_GREAT },
+ // { "HURT_POIS", FF_HURT_POIS },
+ // { "HURT_ELEC", FF_HURT_ELEC },
+ // { "HURT_WATER", FF_HURT_WATER },
+ // { "HURT_BWATER", FF_HURT_BWATER },
+ // { "USE_FEAT", FF_USE_FEAT },
+ // { "GET_FEAT", FF_GET_FEAT },
+ // { "GROUND", FF_GROUND },
+ // { "OUTSIDE", FF_OUTSIDE },
+ // { "EASY_HIDE", FF_EASY_HIDE },
+ // { "EASY_CLIMB", FF_EASY_CLIMB },
+ // { "MUST_CLIMB", FF_MUST_CLIMB },
+ { "TREE", FF_TREE },
+ // { "NEED_TREE", FF_NEED_TREE },
+ // { "BLOOD", FF_BLOOD },
+ // { "DUST", FF_DUST },
+ // { "SLIME", FF_SLIME },
+ { "PLANT", FF_PLANT },
+ // { "XXX2", FF_XXX2 },
+ // { "INSTANT", FF_INSTANT },
+ // { "EXPLODE", FF_EXPLODE },
+ // { "TIMED", FF_TIMED },
+ // { "ERUPT", FF_ERUPT },
+ // { "STRIKE", FF_STRIKE },
+ // { "SPREAD", FF_SPREAD },
- "SPECIAL",
- "HURT_DISI",
- "QUEST_ENTER",
- "QUEST_EXIT",
- "QUEST",
- "SHAFT",
- "MOUNTAIN",
- "BLDG",
- "RUNE_EXPLOSION",
- "PATTERN",
- "TOWN",
- "ENTRANCE",
- "MIRROR",
- "UNPERM",
- "TELEPORTABLE",
- "CONVERT",
- "GLASS",
+ { "SPECIAL", FF_SPECIAL },
+ { "HURT_DISI", FF_HURT_DISI },
+ { "QUEST_ENTER", FF_QUEST_ENTER },
+ { "QUEST_EXIT", FF_QUEST_EXIT },
+ { "QUEST", FF_QUEST },
+ { "SHAFT", FF_SHAFT },
+ { "MOUNTAIN", FF_MOUNTAIN },
+ { "BLDG", FF_BLDG },
+ { "RUNE_EXPLOSION", FF_RUNE_EXPLOSION },
+ { "PATTERN", FF_PATTERN },
+ { "TOWN", FF_TOWN },
+ { "ENTRANCE", FF_ENTRANCE },
+ { "MIRROR", FF_MIRROR },
+ { "UNPERM", FF_UNPERM },
+ { "TELEPORTABLE", FF_TELEPORTABLE },
+ { "CONVERT", FF_CONVERT },
+ { "GLASS", FF_GLASS },
};
#pragma once
#include "system/angband.h"
+#include "grid/feature-flag-types.h"
+#include <string_view>
+#include <unordered_map>
-#define NUM_F_FLAGS 113
-extern concptr f_info_flags[NUM_F_FLAGS];
+extern const std::unordered_map<std::string_view, feature_flag_type> f_info_flags;
#include "grid/grid.h"
#include "grid/trap.h"
#include "info-reader/feature-info-tokens-table.h"
+#include "info-reader/info-reader-util.h"
#include "info-reader/parse-error-types.h"
#include "main/angband-headers.h"
#include "room/door-definition.h"
#include "util/bit-flags-calculator.h"
#include "util/string-processor.h"
#include "view/display-messages.h"
-#include <string>
-#include <string_view>
-/*! 地形タグ情報から地形IDを得られなかった場合にTRUEを返す */
-static bool feat_tag_is_not_found = FALSE;
+/*! 地形タグ情報から地形IDを得られなかった場合にtrueを返す */
+static bool feat_tag_is_not_found = false;
/*!
* @brief テキストトークンを走査してフラグを一つ得る(地形情報向け) /
* Grab one flag in an feature_type from a textual string
* @param f_ptr 地形情報を保管する先の構造体参照ポインタ
* @param what 参照元の文字列ポインタ
- * @return エラーコード
+ * @return 見つけたらtrue
*/
-static errr grab_one_feat_flag(feature_type *f_ptr, concptr what)
+static bool grab_one_feat_flag(feature_type *f_ptr, std::string_view what)
{
- for (int i = 0; i < FF_FLAG_MAX; i++) {
- if (streq(what, f_info_flags[i])) {
- add_flag(f_ptr->flags, i);
- return 0;
- }
- }
+ if (info_grab_one_flag(f_ptr->flags, f_info_flags, what))
+ return true;
- msg_format(_("未知の地形フラグ '%s'。", "Unknown feature flag '%s'."), what);
- return PARSE_ERROR_GENERIC;
+ msg_format(_("未知の地形フラグ '%s'。", "Unknown feature flag '%s'."), what.data());
+ return false;
}
/*!
* @param f_ptr 地形情報を保管する先の構造体参照ポインタ
* @param what 参照元の文字列ポインタ
* @param count ステートの保存先ID
- * @return エラーコード
+ * @return 見つけたらtrue
*/
-static errr grab_one_feat_action(feature_type *f_ptr, concptr what, int count)
+static bool grab_one_feat_action(feature_type *f_ptr, std::string_view what, int count)
{
- for (FF_FLAGS_IDX i = 0; i < FF_FLAG_MAX; i++) {
- if (streq(what, f_info_flags[i])) {
- f_ptr->state[count].action = i;
- return 0;
- }
+ if (auto it = f_info_flags.find(what); it != f_info_flags.end()) {
+ f_ptr->state[count].action = static_cast<FF_FLAGS_IDX>(it->second);
+ return true;
}
- msg_format(_("未知の地形アクション '%s'。", "Unknown feature action '%s'."), what);
- return PARSE_ERROR_GENERIC;
+ msg_format(_("未知の地形アクション '%s'。", "Unknown feature action '%s'."), what.data());
+ return false;
}
/*!
* @param head ヘッダ構造体
* @return エラーコード
*/
-errr parse_f_info(char *buf, angband_header *head)
+errr parse_f_info(std::string_view buf, angband_header *head)
{
static feature_type *f_ptr = NULL;
- int i;
- char *s, *t;
- if (buf[0] == 'N') {
- s = angband_strchr(buf + 2, ':');
+ const auto &tokens = str_split(buf, ':', false, 10);
- if (s) {
- *s++ = '\0';
- }
+ if (tokens[0] == "N") {
+ // N:index:tag
+ if (tokens.size() < 3)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+
+ if (tokens[1].size() == 0 || tokens[2].size() == 0)
+ return PARSE_ERROR_GENERIC;
- i = atoi(buf + 2);
- if (i <= error_idx)
- return 4;
+ auto i = std::stoi(tokens[1]);
+ if (i < error_idx)
+ return PARSE_ERROR_NON_SEQUENTIAL_RECORDS;
if (i >= head->info_num)
- return 2;
+ return PARSE_ERROR_OUT_OF_BOUNDS;
error_idx = i;
f_ptr = &f_info[i];
- if (s) {
- f_ptr->tag = std::string(s);
- }
+ f_ptr->tag = tokens[2];
f_ptr->mimic = (FEAT_IDX)i;
f_ptr->destroyed = (FEAT_IDX)i;
for (i = 0; i < MAX_FEAT_STATES; i++)
f_ptr->state[i].action = FF_FLAG_MAX;
- } else if (!f_ptr) {
- return 3;
- }
-#ifdef JP
- else if (buf[0] == 'J') {
- f_ptr->name = std::string(buf + 2);
- } else if (buf[0] == 'E') {
- }
-#else
- else if (buf[0] == 'J') {
- } else if (buf[0] == 'E') {
- f_ptr->name = std::string(buf + 2);
- }
-#endif
- else if (buf[0] == 'M') {
- f_ptr->mimic_tag = std::string(buf + 2);
- } else if (buf[0] == 'G') {
- int j;
- byte s_attr;
- char char_tmp[F_LIT_MAX];
- if (buf[1] != ':')
- return 1;
- if (!buf[2])
- return 1;
- if (buf[3] != ':')
- return 1;
- if (!buf[4])
- return 1;
-
- char_tmp[F_LIT_STANDARD] = buf[2];
- s_attr = color_char_to_attr(buf[4]);
+
+ } else if (!f_ptr)
+ return PARSE_ERROR_MISSING_RECORD_HEADER;
+ else if (tokens[0] == _("J", "E")) {
+ // J:name_ja
+ // E:name_en
+ if (tokens.size() < 2 || tokens[1].size() == 0)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+ f_ptr->name = tokens[1];
+ } else if (tokens[0] == _("E", "J")) {
+ //pass
+ } else if (tokens[0] == "M") {
+ // M:mimic_tag
+ if (tokens.size() < 2 || tokens[1].size() == 0)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+ f_ptr->mimic_tag = tokens[1];
+ } else if (tokens[0] == "G") {
+ // G:symbol:color:lite:lite_symbol:lite_color:dark_symbol:dark_color
+ if (tokens.size() < 3)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+
+ size_t n;
+ char s_char;
+ if (tokens[1].size() == 1) {
+ s_char = tokens[1][0];
+ n = 2;
+ } else if (tokens[1].size() == 0 && tokens[2].size() == 0) {
+ if (tokens.size() < 4)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+
+ s_char = ':';
+ n = 3;
+ } else
+ return PARSE_ERROR_GENERIC;
+
+ auto s_attr = color_char_to_attr(tokens[n++][0]);
if (s_attr > 127)
- return 1;
+ return PARSE_ERROR_GENERIC;
+ f_ptr->d_char[F_LIT_STANDARD] = s_char;
f_ptr->d_attr[F_LIT_STANDARD] = s_attr;
- f_ptr->d_char[F_LIT_STANDARD] = char_tmp[F_LIT_STANDARD];
- if (buf[5] == ':') {
+ if (tokens.size() == n) {
+ for (int j = F_LIT_NS_BEGIN; j < F_LIT_MAX; j++) {
+ f_ptr->d_char[j] = s_char;
+ f_ptr->d_attr[j] = s_attr;
+ }
+ } else if (tokens[n++] == "LIT") {
apply_default_feat_lighting(f_ptr->d_attr, f_ptr->d_char);
- if (!streq(buf + 6, "LIT")) {
- char attr_lite_tmp[F_LIT_MAX - F_LIT_NS_BEGIN];
-
- if ((F_LIT_MAX - F_LIT_NS_BEGIN) * 2
- != sscanf(buf + 6, "%c:%c:%c:%c", &char_tmp[F_LIT_LITE], &attr_lite_tmp[F_LIT_LITE - F_LIT_NS_BEGIN], &char_tmp[F_LIT_DARK],
- &attr_lite_tmp[F_LIT_DARK - F_LIT_NS_BEGIN]))
- return 1;
- if (buf[F_LIT_MAX * 4 + 1])
- return 1;
-
- for (j = F_LIT_NS_BEGIN; j < F_LIT_MAX; j++) {
- switch (attr_lite_tmp[j - F_LIT_NS_BEGIN]) {
- case '*':
- /* Use default lighting */
- break;
- case '-':
- /* No lighting support */
- f_ptr->d_attr[j] = f_ptr->d_attr[F_LIT_STANDARD];
- break;
- default:
- /* Extract the color */
- f_ptr->d_attr[j] = color_char_to_attr(attr_lite_tmp[j - F_LIT_NS_BEGIN]);
- if (f_ptr->d_attr[j] > 127)
- return 1;
- break;
- }
- f_ptr->d_char[j] = char_tmp[j];
+
+ for (int j = F_LIT_NS_BEGIN; j < F_LIT_MAX; j++) {
+ auto c_idx = n + (j - F_LIT_NS_BEGIN) * 2;
+ auto a_idx = c_idx + 1;
+ if (tokens.size() <= (size_t)a_idx)
+ continue;
+ if (tokens[c_idx].size() != 1 || tokens[a_idx].size() != 1)
+ continue;
+
+ f_ptr->d_char[j] = tokens[c_idx][0];
+
+ if (tokens[a_idx] == "*") {
+ // pass
+ } else if (tokens[a_idx] == "-") {
+ f_ptr->d_attr[j] = s_attr;
+ } else {
+ f_ptr->d_attr[j] = color_char_to_attr(tokens[a_idx][0]);
+ if (f_ptr->d_attr[j] > 127)
+ return PARSE_ERROR_GENERIC;
}
}
- } else if (!buf[5]) {
- for (j = F_LIT_NS_BEGIN; j < F_LIT_MAX; j++) {
- f_ptr->d_attr[j] = s_attr;
- f_ptr->d_char[j] = char_tmp[F_LIT_STANDARD];
- }
} else
- return 1;
- } else if (buf[0] == 'F') {
- for (s = buf + 2; *s;) {
- /* loop */
- for (t = s; *t && (*t != ' ') && (*t != '|'); ++t)
- ;
-
- if (*t) {
- *t++ = '\0';
- while (*t == ' ' || *t == '|')
- t++;
- }
-
- if (1 == sscanf(s, "SUBTYPE_%d", &i)) {
- f_ptr->subtype = (FEAT_SUBTYPE)i;
- s = t;
-
+ return PARSE_ERROR_GENERIC;
+ } else if (tokens[0] == "F") {
+ // F:flags
+ if (tokens.size() < 2 || tokens[1].size() == 0)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+
+ const auto &flags = str_split(tokens[1], '|', true, 10);
+ for (const auto &f : flags) {
+ if (f.size() == 0)
continue;
- }
- if (1 == sscanf(s, "POWER_%d", &i)) {
- f_ptr->power = (FEAT_POWER)i;
- s = t;
- continue;
+ const auto &f_tokens = str_split(f, '_', false, 2);
+ if (f_tokens.size() == 2) {
+ if (f_tokens[0] == "SUBTYPE") {
+ info_set_value(f_ptr->subtype, f_tokens[1]);
+ continue;
+ } else if (f_tokens[0] == "POWER") {
+ info_set_value(f_ptr->power, f_tokens[1]);
+ continue;
+ }
}
- if (0 != grab_one_feat_flag(f_ptr, s))
- return (PARSE_ERROR_INVALID_FLAG);
-
- s = t;
+ if (!grab_one_feat_flag(f_ptr, f))
+ return PARSE_ERROR_INVALID_FLAG;
}
- } else if (buf[0] == 'W') {
- int priority;
- if (1 != sscanf(buf + 2, "%d", &priority))
- return (PARSE_ERROR_GENERIC);
- f_ptr->priority = (FEAT_PRIORITY)priority;
- } else if (buf[0] == 'K') {
- for (i = 0; i < MAX_FEAT_STATES; i++)
+ } else if (tokens[0] == "W") {
+ // W:priority
+ if (tokens.size() < 2 || tokens[1].size() == 0)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+ info_set_value(f_ptr->priority, tokens[1]);
+ } else if (tokens[0] == "K") {
+ // K:state:feat
+ if (tokens.size() < 3)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+ if (tokens[1].size() == 0 || tokens[2].size() == 0)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+
+ int i = 0;
+ for (; i < MAX_FEAT_STATES; i++) {
if (f_ptr->state[i].action == FF_FLAG_MAX)
break;
+ }
if (i == MAX_FEAT_STATES)
return PARSE_ERROR_GENERIC;
- /* loop */
- for (s = t = buf + 2; *t && (*t != ':'); t++)
- ;
-
- if (*t == ':')
- *t++ = '\0';
-
- if (streq(s, "DESTROYED")) {
- f_ptr->destroyed_tag = std::string(t);
- } else {
+ if (tokens[1] == "DESTROYED")
+ f_ptr->destroyed_tag = tokens[2];
+ else {
f_ptr->state[i].action = 0;
- if (0 != grab_one_feat_action(f_ptr, s, i))
+ if (!grab_one_feat_action(f_ptr, tokens[1], i))
return PARSE_ERROR_INVALID_FLAG;
- f_ptr->state[i].result_tag = std::string(t);
+ f_ptr->state[i].result_tag = tokens[2];
}
- } else {
- return 6;
- }
+ } else
+ return PARSE_ERROR_UNDEFINED_DIRECTIVE;
- return 0;
+ return PARSE_ERROR_NONE;
}
/*!
* @param str タグ文字列
* @return 地形ID
*/
-s16b f_tag_to_index(concptr str)
+FEAT_IDX f_tag_to_index(std::string_view str)
{
- for (u16b i = 0; i < f_head.info_num; i++) {
- if (streq(f_info[i].tag.c_str(), str)) {
- return (s16b)i;
- }
+ for (size_t i = 0; i < f_head.info_num; i++) {
+ if (f_info[i].tag == str)
+ return (FEAT_IDX)i;
}
return -1;
#pragma once
#include "system/angband.h"
-#include "info-reader/info-reader-util.h"
+#include <string_view>
-errr parse_f_info(char *buf, angband_header *head);
+struct angband_header;
+errr parse_f_info(std::string_view buf, angband_header *head);
errr init_feat_variables(void);
-s16b f_tag_to_index(concptr str);
+FEAT_IDX f_tag_to_index(std::string_view str);
s16b f_tag_to_index_in_init(concptr str);
void retouch_f_info(angband_header *head);
#include "dungeon/quest.h"
#include "grid/feature.h"
#include "info-reader/feature-reader.h"
+#include "info-reader/info-reader-util.h"
#include "info-reader/parse-error-types.h"
#include "info-reader/random-grid-effect-types.h"
#include "io/tokenizer.h"
#include "system/system-variables.h"
#include "util/angband-files.h"
#include "util/string-processor.h"
+#include <string>
dungeon_grid letter[255];
* @param parse_info_txt_line パース関数
* @return エラーコード
*/
-errr init_info_txt(FILE *fp, char *buf, angband_header *head, parse_info_txt_func parse_info_txt_line)
+errr init_info_txt(FILE *fp, char *buf, angband_header *head, std::function<errr(std::string_view, angband_header *)> parse_info_txt_line)
{
error_idx = -1;
error_line = 0;
}
}
- if ((err = (*parse_info_txt_line)(buf, head)) != 0)
+ if ((err = parse_info_txt_line(buf, head)) != 0)
return (err);
}
#pragma once
#include "system/angband.h"
+#include <string_view>
enum parse_error_type : int;
extern dungeon_grid letter[255];
-typedef struct angband_header angband_header;
-typedef struct floor_type floor_type;
-typedef errr (*parse_info_txt_func)(char *buf, angband_header *head);
-errr init_info_txt(FILE *fp, char *buf, angband_header *head, parse_info_txt_func parse_info_txt_line);
+struct angband_header;
+struct floor_type;
+
+errr init_info_txt(FILE *fp, char *buf, angband_header *head, std::function<errr(std::string_view, angband_header *)> parse_info_txt_line);
parse_error_type parse_line_feature(floor_type *floor_ptr, char *buf);
parse_error_type parse_line_building(char *buf);
int error_line; /*!< データ読み込み/初期化時に汎用的にエラー行数を保存するグローバル変数 */
/*!
- * @brief テキストトークンを走査してフラグを一つ得る(汎用) /
- * Grab one flag from a textual string
- * @param flags ビットフラグを追加する先の参照ポインタ
- * @param names トークン定義配列
- * @param what 参照元の文字列ポインタ
- * @return エラーコード
- */
-errr grab_one_flag(u32b *flags, concptr names[], concptr what)
-{
- for (int i = 0; i < 32; i++) {
- if (streq(what, names[i])) {
- *flags |= (1UL << i);
- return 0;
- }
- }
-
- return -1;
-}
-
-/*!
* @brief テキストトークンを走査してフラグを一つ得る(発動能力用) /
* Grab one activation index flag
* @param what 参照元の文字列ポインタ
#pragma once
#include "system/angband.h"
+#include "util/bit-flags-calculator.h"
+#include <string>
+#include <string_view>
+#include <unordered_map>
/*
* Size of memory reserved for initialization of some arrays
*/
-extern int error_idx;
-extern int error_line;
+extern int error_idx; //!< エラーが発生したinfo ID
+extern int error_line; //!< エラーが発生した行
-typedef struct angband_header angband_header;
-errr grab_one_flag(u32b *flags, concptr names[], concptr what);
byte grab_one_activation_flag(concptr what);
+
+using sview = std::string_view;
+
+/*!
+ * @brief infoフラグ文字列をフラグビットに変換する
+ * @param flags ビットフラグ変数
+ * @param names フラグ文字列変換表
+ * @param what フラグ文字列
+ * @return 見つけたらtrue
+ */
+template <typename T>
+bool info_grab_one_flag(u32b &flags, const std::unordered_map<sview, T> &names, sview what)
+{
+ if (auto it = names.find(what); it != names.end()) {
+ set_bits(flags, it->second);
+ return true;
+ }
+ return false;
+}
+
+/*!
+ * @brief infoフラグ文字列をフラグビットに変換する
+ * @param flags ビットフラグ配列
+ * @param names フラグ文字列変換表
+ * @param what フラグ文字列
+ * @return 見つけたらtrue
+ */
+template <typename T>
+bool info_grab_one_flag(u32b *flags, const std::unordered_map<sview, T> &names, sview what)
+{
+ if (auto it = names.find(what); it != names.end()) {
+ add_flag(flags, it->second);
+ return true;
+ }
+ return false;
+}
+
+/*!
+ * @brief infoパラメータに値をセットする
+ * @param パラメータ変数
+ * @val 値
+ */
+template <typename T>
+void info_set_value(T &arg, const std::string &val)
+{
+ arg = static_cast<T>(std::stoi(val));
+}
* オブジェクト基本特性トークンの定義 /
* Object flags
*/
-std::unordered_map<std::string_view, tr_type> k_info_flags = {
+const std::unordered_map<std::string_view, tr_type> k_info_flags = {
{ "STR", TR_STR },
{ "INT", TR_INT },
{ "WIS", TR_WIS },
* オブジェクト生成特性トークンの定義 /
* Object flags
*/
-std::unordered_map<std::string_view, TRG> k_info_gen_flags = {
+const std::unordered_map<std::string_view, TRG> k_info_gen_flags = {
{ "INSTA_ART", TRG::INSTA_ART },
{ "QUESTITEM", TRG::QUESTITEM },
{ "XTRA_POWER", TRG::XTRA_POWER },
#include "system/angband.h"
#include "object-enchant/tr-types.h"
#include "object-enchant/trg-types.h"
-#include <string>
+#include <string_view>
#include <unordered_map>
-extern std::unordered_map<std::string_view, tr_type> k_info_flags;
-extern std::unordered_map<std::string_view, TRG> k_info_gen_flags;
+extern const std::unordered_map<std::string_view, tr_type> k_info_flags;
+extern const std::unordered_map<std::string_view, TRG> k_info_gen_flags;
#include "info-reader/kind-reader.h"
+#include "info-reader/info-reader-util.h"
#include "info-reader/kind-info-tokens-table.h"
+#include "info-reader/parse-error-types.h"
#include "main/angband-headers.h"
#include "object-enchant/tr-types.h"
#include "object/object-kind.h"
#include "util/bit-flags-calculator.h"
#include "util/string-processor.h"
#include "view/display-messages.h"
-#include <string>
/*!
* @brief テキストトークンを走査してフラグを一つ得る(ベースアイテム用) /
* Grab one flag in an object_kind from a textual string
* @param k_ptr 保管先のベースアイテム構造体参照ポインタ
* @param what 参照元の文字列ポインタ
- * @return エラーコード
+ * @return 見つけたらtrue
*/
-static errr grab_one_kind_flag(object_kind *k_ptr, concptr what)
+static bool grab_one_kind_flag(object_kind *k_ptr, std::string_view what)
{
- if (k_info_flags.find(what) != k_info_flags.end()) {
- add_flag(k_ptr->flags, k_info_flags[what]);
- return 0;
- }
+ if (info_grab_one_flag(k_ptr->flags, k_info_flags, what))
+ return true;
if (EnumClassFlagGroup<TRG>::grab_one_flag(k_ptr->gen_flags, k_info_gen_flags, what))
- return 0;
+ return true;
- msg_format(_("未知のアイテム・フラグ '%s'。", "Unknown object flag '%s'."), what);
- return 1;
+ msg_format(_("未知のアイテム・フラグ '%s'。", "Unknown object flag '%s'."), what.data());
+ return false;
}
/*!
* @param head ヘッダ構造体
* @return エラーコード
*/
-errr parse_k_info(char *buf, angband_header *head)
+errr parse_k_info(std::string_view buf, angband_header *head)
{
static object_kind *k_ptr = NULL;
+ const auto &tokens = str_split(buf, ':', false, 10);
- char *s, *t;
- if (buf[0] == 'N') {
-#ifdef JP
- char *flavor;
-#endif
- s = angband_strchr(buf + 2, ':');
- if (!s)
- return 1;
-
- *s++ = '\0';
- int i = atoi(buf + 2);
+ if (tokens[0] == "N") {
+ // N:index:name_ja
+ if (tokens.size() < 3 || tokens[1].size() == 0)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
- if (i <= error_idx)
- return 4;
+ auto i = std::stoi(tokens[1]);
+ if (i < error_idx)
+ return PARSE_ERROR_NON_SEQUENTIAL_RECORDS;
if (i >= head->info_num)
- return 2;
+ return PARSE_ERROR_OUT_OF_BOUNDS;
error_idx = i;
k_ptr = &k_info[i];
-
#ifdef JP
- if (!*s)
- return 1;
-
- flavor = angband_strchr(s, ':');
- if (flavor) {
- *flavor++ = '\0';
- k_ptr->flavor_name = std::string(flavor);
- }
-
- k_ptr->name = std::string(s);
+ k_ptr->name = tokens[2];
#endif
- } else if (!k_ptr) {
- return 3;
- }
-#ifdef JP
- /* 英語名を読むルーチンを追加 */
- /* 'E' から始まる行は英語名としている */
- else if (buf[0] == 'E') {
- /* nothing to do */
- }
-#else
- else if (buf[0] == 'E') {
- char *flavor;
- s = buf + 2;
- flavor = angband_strchr(s, ':');
- if (flavor) {
- *flavor++ = '\0';
- k_ptr->flavor_name = std::string(flavor);
- }
-
- k_ptr->name = std::string(s);
- }
+ if (tokens.size() > 3)
+ k_ptr->flavor_name = tokens[3];
+
+ } else if (!k_ptr)
+ return PARSE_ERROR_MISSING_RECORD_HEADER;
+ else if (tokens[0] == "E") {
+ // E:name_en
+#ifndef JP
+ if (tokens.size() < 2 || tokens[1].size() == 0)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+ k_ptr->name = tokens[1];
+
+ if (tokens.size() > 2)
+ k_ptr->flavor_name = tokens[2];
#endif
- else if (buf[0] == 'D') {
+ } else if (tokens[0] == "D") {
+ // D:text_ja
+ // D:$text_en
+ if (tokens.size() < 2 || tokens[1].size() == 0)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
#ifdef JP
- if (buf[2] == '$')
- return 0;
- s = buf + 2;
+ if (tokens[1][0] == '$')
+ return PARSE_ERROR_NONE;
+ k_ptr->text.append(buf.substr(2));
#else
- if (buf[2] != '$')
- return 0;
- s = buf + 3;
+ if (tokens[1][0] != '$')
+ return PARSE_ERROR_NONE;
+ k_ptr->text.append(buf.substr(3));
#endif
- k_ptr->text.append(s);
- } else if (buf[0] == 'G') {
- char sym;
- byte tmp;
- if (buf[1] != ':')
- return 1;
- if (!buf[2])
- return 1;
- if (buf[3] != ':')
- return 1;
- if (!buf[4])
- return 1;
-
- sym = buf[2];
- tmp = color_char_to_attr(buf[4]);
- if (tmp > 127)
- return 1;
-
- k_ptr->d_attr = tmp;
- k_ptr->d_char = sym;
- } else if (buf[0] == 'I') {
- int tval, sval, pval;
- if (3 != sscanf(buf + 2, "%d:%d:%d", &tval, &sval, &pval))
- return 1;
-
- k_ptr->tval = (tval_type)tval;
- k_ptr->sval = (OBJECT_SUBTYPE_VALUE)sval;
- k_ptr->pval = (PARAMETER_VALUE)pval;
- } else if (buf[0] == 'W') {
- int level, extra, wgt;
- long cost;
- if (4 != sscanf(buf + 2, "%d:%d:%d:%ld", &level, &extra, &wgt, &cost))
- return 1;
-
- k_ptr->level = (DEPTH)level;
- k_ptr->extra = (BIT_FLAGS8)extra;
- k_ptr->weight = (WEIGHT)wgt;
- k_ptr->cost = (PRICE)cost;
- } else if (buf[0] == 'A') {
- int i = 0;
- for (s = buf + 1; s && (s[0] == ':') && s[1]; ++i) {
- k_ptr->chance[i] = 1;
- k_ptr->locale[i] = atoi(s + 1);
- t = angband_strchr(s + 1, '/');
- s = angband_strchr(s + 1, ':');
- if (t && (!s || t < s)) {
- int chance = atoi(t + 1);
- if (chance > 0)
- k_ptr->chance[i] = (PROB)chance;
- }
- }
- } else if (buf[0] == 'P') {
- int ac, hd1, hd2, th, td, ta;
- if (6 != sscanf(buf + 2, "%d:%dd%d:%d:%d:%d", &ac, &hd1, &hd2, &th, &td, &ta))
- return 1;
-
- k_ptr->ac = (ARMOUR_CLASS)ac;
- k_ptr->dd = (DICE_NUMBER)hd1;
- k_ptr->ds = (DICE_SID)hd2;
- k_ptr->to_h = (HIT_PROB)th;
- k_ptr->to_d = (HIT_POINT)td;
- k_ptr->to_a = (ARMOUR_CLASS)ta;
- } else if (buf[0] == 'U') {
- byte n;
- n = grab_one_activation_flag(buf + 2);
- if (n > 0) {
- k_ptr->act_idx = n;
- } else {
- return 5;
+ } else if (tokens[0] == "G") {
+ // G:color:symbol
+ if (tokens.size() < 3 || tokens[1].size() == 0 || tokens[2].size() == 0)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+
+ auto a = color_char_to_attr(tokens[2][0]);
+ if (a > 127)
+ return PARSE_ERROR_GENERIC;
+
+ k_ptr->d_attr = a;
+ k_ptr->d_char = tokens[1][0];
+ } else if (tokens[0] == "I") {
+ // I:tval:sval:pval
+ if (tokens.size() < 4)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+
+ info_set_value(k_ptr->tval, tokens[1]);
+ info_set_value(k_ptr->sval, tokens[2]);
+ info_set_value(k_ptr->pval, tokens[3]);
+ } else if (tokens[0] == "W") {
+ // W:level:extra:weight:cost
+ if (tokens.size() < 5)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+
+ info_set_value(k_ptr->level, tokens[1]);
+ info_set_value(k_ptr->extra, tokens[2]);
+ info_set_value(k_ptr->weight, tokens[3]);
+ info_set_value(k_ptr->cost, tokens[4]);
+ } else if (tokens[0] == "A") {
+ // A:level/chance(:level/chance:level/chance:level/chance)
+ if (tokens.size() < 2 || tokens.size() > 5)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+
+ auto i = 0;
+ for (auto t = tokens.begin() + 1; t != tokens.end(); t++) {
+ const auto &rarity = str_split(*t, '/', false, 2);
+ if (rarity.size() != 2 || rarity[0].size() == 0 || rarity[1].size() == 0)
+ return PARSE_ERROR_NON_SEQUENTIAL_RECORDS;
+ info_set_value(k_ptr->locale[i], rarity[0]);
+ info_set_value(k_ptr->chance[i], rarity[1]);
+ i++;
}
- } else if (buf[0] == 'F') {
- for (s = buf + 2; *s;) {
- /* loop */
- for (t = s; *t && (*t != ' ') && (*t != '|'); ++t)
- ;
-
- if (*t) {
- *t++ = '\0';
- while (*t == ' ' || *t == '|')
- t++;
- }
-
- if (0 != grab_one_kind_flag(k_ptr, s))
- return 5;
- s = t;
+ } else if (tokens[0] == "P") {
+ // P:ac:dd:ds:to_h:to_d:to_a
+ if (tokens.size() < 6)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+
+ const auto &dice = str_split(tokens[2], 'd', false, 2);
+ if (dice.size() != 2)
+ return PARSE_ERROR_NON_SEQUENTIAL_RECORDS;
+
+ info_set_value(k_ptr->ac, tokens[1]);
+ info_set_value(k_ptr->dd, dice[0]);
+ info_set_value(k_ptr->ds, dice[1]);
+ info_set_value(k_ptr->to_h, tokens[3]);
+ info_set_value(k_ptr->to_d, tokens[4]);
+ info_set_value(k_ptr->to_a, tokens[5]);
+ } else if (tokens[0] == "U") {
+ // U:activation_flag
+ if (tokens.size() < 2 || tokens[1].size() == 0)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+ auto n = grab_one_activation_flag(tokens[1].c_str());
+ if (n <= 0)
+ return PARSE_ERROR_INVALID_FLAG;
+
+ k_ptr->act_idx = (IDX)n;
+ } else if (tokens[0] == "F") {
+ // F:flags
+ if (tokens.size() < 2 || tokens[1].size() == 0)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+
+ const auto &flags = str_split(tokens[1], '|', true, 10);
+ for (const auto &f : flags) {
+ if (f.size() == 0)
+ continue;
+ if (!grab_one_kind_flag(k_ptr, f))
+ return PARSE_ERROR_INVALID_FLAG;
}
- } else {
- return 6;
- }
+ } else
+ return PARSE_ERROR_UNDEFINED_DIRECTIVE;
- return 0;
+ return PARSE_ERROR_NONE;
}
-#pragma once
+#pragma once
#include "system/angband.h"
-#include "info-reader/info-reader-util.h"
+#include <string_view>
-errr parse_k_info(char *buf, angband_header *head);
+struct angband_header;
+errr parse_k_info(std::string_view buf, angband_header *head);
#include "info-reader/magic-reader.h"
+#include "info-reader/info-reader-util.h"
+#include "info-reader/parse-error-types.h"
#include "main/angband-headers.h"
#include "player-ability/player-ability-types.h"
#include "player/player-class.h"
#include "util/string-processor.h"
+namespace {
+/*!
+ * @brief 魔法タイプ名とtvalの対応表
+ */
+const std::unordered_map<std::string_view, tval_type> name_to_tval = {
+ { "SORCERY", TV_SORCERY_BOOK },
+ { "LIFE", TV_LIFE_BOOK },
+ { "MUSIC", TV_MUSIC_BOOK },
+ { "HISSATSU", TV_HISSATSU_BOOK },
+ { "NONE", TV_NONE },
+};
+
+/*!
+ * @brief 魔法必須能力とenumの対応表
+ */
+const std::unordered_map<std::string_view, int> name_to_stat = {
+ { "STR", A_STR },
+ { "INT", A_INT },
+ { "WIS", A_WIS },
+ { "DEX", A_DEX },
+ { "CON", A_CON },
+ { "CHR", A_CHR },
+};
+}
+
/*!
* @brief 職業魔法情報(m_info)のパース関数 /
* Initialize the "m_info" array, by parsing an ascii "template" file
* @param head ヘッダ構造体
* @return エラーコード
*/
-errr parse_m_info(char *buf, angband_header *head)
+errr parse_m_info(std::string_view buf, angband_header *head)
{
static player_magic *m_ptr = NULL;
static int realm, magic_idx = 0, readable = 0;
+ const auto &tokens = str_split(buf, ':', false, 7);
- if (buf[0] == 'N') {
- int i = atoi(buf + 2);
+ if (tokens[0] == "N") {
+ // N:class-index
+ if (tokens.size() < 2 && tokens[1].size() == 0)
+ return PARSE_ERROR_GENERIC;
- if (i <= error_idx)
- return 4;
+ auto i = std::stoi(tokens[1]);
+ if (i < error_idx)
+ return PARSE_ERROR_NON_SEQUENTIAL_RECORDS;
if (i >= head->info_num)
- return 2;
+ return PARSE_ERROR_OUT_OF_BOUNDS;
error_idx = i;
m_ptr = &m_info[i];
- } else if (!m_ptr) {
- return 3;
- } else if (buf[0] == 'I') {
- char *book, *stat;
- int xtra, type, first, weight;
- char *s;
- s = angband_strchr(buf + 2, ':');
-
- /* Verify that colon */
- if (!s)
- return 1;
-
- /* Nuke the colon, advance to the name */
- *s++ = '\0';
-
- book = buf + 2;
-
- if (streq(book, "SORCERY"))
- m_ptr->spell_book = TV_SORCERY_BOOK;
- else if (streq(book, "LIFE"))
- m_ptr->spell_book = TV_LIFE_BOOK;
- else if (streq(book, "MUSIC"))
- m_ptr->spell_book = TV_MUSIC_BOOK;
- else if (streq(book, "HISSATSU"))
- m_ptr->spell_book = TV_HISSATSU_BOOK;
- else if (streq(book, "NONE"))
- m_ptr->spell_book = TV_NONE;
- else
- return 5;
-
- stat = s;
- s = angband_strchr(s, ':');
- if (!s)
- return 1;
- *s++ = '\0';
-
- if (streq(stat, "STR"))
- m_ptr->spell_stat = A_STR;
- else if (streq(stat, "INT"))
- m_ptr->spell_stat = A_INT;
- else if (streq(stat, "WIS"))
- m_ptr->spell_stat = A_WIS;
- else if (streq(stat, "DEX"))
- m_ptr->spell_stat = A_DEX;
- else if (streq(stat, "CON"))
- m_ptr->spell_stat = A_CON;
- else if (streq(stat, "CHR"))
- m_ptr->spell_stat = A_CHR;
- else
- return 5;
-
- if (4 != sscanf(s, "%x:%d:%d:%d", (uint *)&xtra, &type, &first, &weight))
- return 1;
-
- m_ptr->spell_xtra = xtra;
- m_ptr->spell_type = type;
- m_ptr->spell_first = first;
- m_ptr->spell_weight = weight;
- } else if (buf[0] == 'R') {
- if (2 != sscanf(buf + 2, "%d:%d", &realm, &readable))
- return 1;
-
+ } else if (!m_ptr)
+ return PARSE_ERROR_MISSING_RECORD_HEADER;
+ else if (tokens[0] == "I") {
+ if (tokens.size() < 7 || tokens[1].size() == 0)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+
+ const auto tval = name_to_tval.find(tokens[1]);
+ if (tval == name_to_tval.end())
+ return PARSE_ERROR_INVALID_FLAG;
+
+ m_ptr->spell_book = tval->second;
+
+ const auto stat = name_to_stat.find(tokens[2]);
+ if (stat == name_to_stat.end())
+ return PARSE_ERROR_INVALID_FLAG;
+
+ m_ptr->spell_stat = stat->second;
+
+ info_set_value(m_ptr->spell_xtra, tokens[3]);
+ info_set_value(m_ptr->spell_type, tokens[4]);
+ info_set_value(m_ptr->spell_first, tokens[5]);
+ info_set_value(m_ptr->spell_weight, tokens[6]);
+ } else if (tokens[0] == "R") {
+ if (tokens.size() < 3)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+
+ info_set_value(realm, tokens[1]);
+ info_set_value(readable, tokens[2]);
magic_idx = 0;
- } else if (buf[0] == 'T') {
- int level, mana, fail, exp;
-
+ } else if (tokens[0] == "T") {
if (!readable)
- return 1;
- if (4 != sscanf(buf + 2, "%d:%d:%d:%d", &level, &mana, &fail, &exp))
- return 1;
+ return PARSE_ERROR_GENERIC;
+
+ if (tokens.size() < 5)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
- m_ptr->info[realm][magic_idx].slevel = (PLAYER_LEVEL)level;
- m_ptr->info[realm][magic_idx].smana = (MANA_POINT)mana;
- m_ptr->info[realm][magic_idx].sfail = (PERCENTAGE)fail;
- m_ptr->info[realm][magic_idx].sexp = (EXP)exp;
+ auto &magic = m_ptr->info[realm][magic_idx];
+ info_set_value(magic.slevel, tokens[1]);
+ info_set_value(magic.smana, tokens[2]);
+ info_set_value(magic.sfail, tokens[3]);
+ info_set_value(magic.sexp, tokens[4]);
magic_idx++;
} else
- return 6;
+ return PARSE_ERROR_UNDEFINED_DIRECTIVE;
- return 0;
+ return PARSE_ERROR_NONE;
}
#pragma once
-#include "info-reader/info-reader-util.h"
#include "system/angband.h"
+#include <string_view>
-errr parse_m_info(char *buf, angband_header *head);
+struct angband_header;
+errr parse_m_info(std::string_view buf, angband_header *head);
* モンスターの打撃手段トークンの定義 /
* Monster Blow Methods
*/
-concptr r_info_blow_method[NB_RBM_TYPE + 1] = {
- "",
- "HIT",
- "TOUCH",
- "PUNCH",
- "KICK",
- "CLAW",
- "BITE",
- "STING",
- "SLASH",
- "BUTT",
- "CRUSH",
- "ENGULF",
- "CHARGE",
- "CRAWL",
- "DROOL",
- "SPIT",
- "EXPLODE",
- "GAZE",
- "WAIL",
- "SPORE",
- "XXX4",
- "BEG",
- "INSULT",
- "MOAN",
- "SHOW",
- "SHOOT",
- NULL
+const std::unordered_map<std::string_view, rbm_type> r_info_blow_method = {
+ { "HIT", RBM_HIT },
+ { "TOUCH", RBM_TOUCH },
+ { "PUNCH", RBM_PUNCH },
+ { "KICK", RBM_KICK },
+ { "CLAW", RBM_CLAW },
+ { "BITE", RBM_BITE },
+ { "STING", RBM_STING },
+ { "SLASH", RBM_SLASH },
+ { "BUTT", RBM_BUTT },
+ { "CRUSH", RBM_CRUSH },
+ { "ENGULF", RBM_ENGULF },
+ { "CHARGE", RBM_CHARGE },
+ { "CRAWL", RBM_CRAWL },
+ { "DROOL", RBM_DROOL },
+ { "SPIT", RBM_SPIT },
+ { "EXPLODE", RBM_EXPLODE },
+ { "GAZE", RBM_GAZE },
+ { "WAIL", RBM_WAIL },
+ { "SPORE", RBM_SPORE },
+ { "XXX4", RBM_XXX4 },
+ { "BEG", RBM_BEG },
+ { "INSULT", RBM_INSULT },
+ { "MOAN", RBM_MOAN },
+ { "SHOW", RBM_SHOW },
+ { "SHOOT", RBM_SHOOT },
};
/*!
* モンスターの打撃属性トークンの定義 /
* Monster Blow Effects
*/
-concptr r_info_blow_effect[NB_RBE_TYPE + 1] = {
- "",
- "HURT",
- "POISON",
- "UN_BONUS",
- "UN_POWER",
- "EAT_GOLD",
- "EAT_ITEM",
- "EAT_FOOD",
- "EAT_LITE",
- "ACID",
- "ELEC",
- "FIRE",
- "COLD",
- "BLIND",
- "CONFUSE",
- "TERRIFY",
- "PARALYZE",
- "LOSE_STR",
- "LOSE_INT",
- "LOSE_WIS",
- "LOSE_DEX",
- "LOSE_CON",
- "LOSE_CHR",
- "LOSE_ALL",
- "SHATTER",
- "EXP_10",
- "EXP_20",
- "EXP_40",
- "EXP_80",
- "DISEASE",
- "TIME",
- "EXP_VAMP",
- "DR_MANA",
- "SUPERHURT",
- "INERTIA",
- "STUN",
- "FLAVOR",
- NULL
+const std::unordered_map<std::string_view, rbe_type> r_info_blow_effect = {
+ { "HURT", RBE_HURT },
+ { "POISON", RBE_POISON },
+ { "UN_BONUS", RBE_UN_BONUS },
+ { "UN_POWER", RBE_UN_POWER },
+ { "EAT_GOLD", RBE_EAT_GOLD },
+ { "EAT_ITEM", RBE_EAT_ITEM },
+ { "EAT_FOOD", RBE_EAT_FOOD },
+ { "EAT_LITE", RBE_EAT_LITE },
+ { "ACID", RBE_ACID },
+ { "ELEC", RBE_ELEC },
+ { "FIRE", RBE_FIRE },
+ { "COLD", RBE_COLD },
+ { "BLIND", RBE_BLIND },
+ { "CONFUSE", RBE_CONFUSE },
+ { "TERRIFY", RBE_TERRIFY },
+ { "PARALYZE", RBE_PARALYZE },
+ { "LOSE_STR", RBE_LOSE_STR },
+ { "LOSE_INT", RBE_LOSE_INT },
+ { "LOSE_WIS", RBE_LOSE_WIS },
+ { "LOSE_DEX", RBE_LOSE_DEX },
+ { "LOSE_CON", RBE_LOSE_CON },
+ { "LOSE_CHR", RBE_LOSE_CHR },
+ { "LOSE_ALL", RBE_LOSE_ALL },
+ { "SHATTER", RBE_SHATTER },
+ { "EXP_10", RBE_EXP_10 },
+ { "EXP_20", RBE_EXP_20 },
+ { "EXP_40", RBE_EXP_40 },
+ { "EXP_80", RBE_EXP_80 },
+ { "DISEASE", RBE_DISEASE },
+ { "TIME", RBE_TIME },
+ { "EXP_VAMP", RBE_DR_LIFE },
+ { "DR_MANA", RBE_DR_MANA },
+ { "SUPERHURT", RBE_SUPERHURT },
+ { "INERTIA", RBE_INERTIA },
+ { "STUN", RBE_STUN },
+ { "FLAVOR", RBE_FLAVOR },
};
/*!
* モンスター特性トークンの定義1 /
* Monster race flags
*/
-concptr r_info_flags1[NUM_R_FLAGS_1] = {
- "UNIQUE",
- "QUESTOR",
- "MALE",
- "FEMALE",
- "CHAR_CLEAR",
- "SHAPECHANGER",
- "ATTR_CLEAR",
- "ATTR_MULTI",
- "FORCE_DEPTH",
- "FORCE_MAXHP",
- "PREVENT_SUDDEN_MAGIC",
- "FORCE_EXTRA",
- "ATTR_SEMIRAND",
- "FRIENDS",
- "ESCORT",
- "ESCORTS",
- "NEVER_BLOW",
- "NEVER_MOVE",
- "RAND_25",
- "RAND_50",
- "ONLY_GOLD",
- "ONLY_ITEM",
- "DROP_60",
- "DROP_90",
- "DROP_1D2",
- "DROP_2D2",
- "DROP_3D2",
- "DROP_4D2",
- "DROP_GOOD",
- "DROP_GREAT",
- "XXX2",
- "XXX3"
+const std::unordered_map<std::string_view, race_flags1> r_info_flags1 = {
+ { "UNIQUE", RF1_UNIQUE },
+ { "QUESTOR", RF1_QUESTOR },
+ { "MALE", RF1_MALE },
+ { "FEMALE", RF1_FEMALE },
+ { "CHAR_CLEAR", RF1_CHAR_CLEAR },
+ { "SHAPECHANGER", RF1_SHAPECHANGER },
+ { "ATTR_CLEAR", RF1_ATTR_CLEAR },
+ { "ATTR_MULTI", RF1_ATTR_MULTI },
+ { "FORCE_DEPTH", RF1_FORCE_DEPTH },
+ { "FORCE_MAXHP", RF1_FORCE_MAXHP },
+ { "PREVENT_SUDDEN_MAGIC", RF1_PREVENT_SUDDEN_MAGIC },
+ { "FORCE_EXTRA", RF1_FORCE_EXTRA },
+ { "ATTR_SEMIRAND", RF1_ATTR_SEMIRAND },
+ { "FRIENDS", RF1_FRIENDS },
+ { "ESCORT", RF1_ESCORT },
+ { "ESCORTS", RF1_ESCORTS },
+ { "NEVER_BLOW", RF1_NEVER_BLOW },
+ { "NEVER_MOVE", RF1_NEVER_MOVE },
+ { "RAND_25", RF1_RAND_25 },
+ { "RAND_50", RF1_RAND_50 },
+ { "ONLY_GOLD", RF1_ONLY_GOLD },
+ { "ONLY_ITEM", RF1_ONLY_ITEM },
+ { "DROP_60", RF1_DROP_60 },
+ { "DROP_90", RF1_DROP_90 },
+ { "DROP_1D2", RF1_DROP_1D2 },
+ { "DROP_2D2", RF1_DROP_2D2 },
+ { "DROP_3D2", RF1_DROP_3D2 },
+ { "DROP_4D2", RF1_DROP_4D2 },
+ { "DROP_GOOD", RF1_DROP_GOOD },
+ { "DROP_GREAT", RF1_DROP_GREAT },
};
/*!
* モンスター特性トークンの定義2 /
* Monster race flags
*/
-concptr r_info_flags2[NUM_R_FLAGS_2] = {
- "STUPID",
- "SMART",
- "CAN_SPEAK",
- "REFLECTING",
- "INVISIBLE",
- "COLD_BLOOD",
- "EMPTY_MIND",
- "WEIRD_MIND",
- "MULTIPLY",
- "REGENERATE",
- "CHAR_MULTI",
- "ATTR_ANY",
- "POWERFUL",
- "ELDRITCH_HORROR",
- "AURA_FIRE",
- "AURA_ELEC",
- "OPEN_DOOR",
- "BASH_DOOR",
- "PASS_WALL",
- "KILL_WALL",
- "MOVE_BODY",
- "KILL_BODY",
- "TAKE_ITEM",
- "KILL_ITEM",
- "XXX",
- "XXX",
- "XXX",
- "XXX",
- "XXX",
- "XXX",
- "HUMAN",
- "QUANTUM"
+const std::unordered_map<std::string_view, race_flags2> r_info_flags2 = {
+ { "STUPID", RF2_STUPID },
+ { "SMART", RF2_SMART },
+ { "CAN_SPEAK", RF2_CAN_SPEAK },
+ { "REFLECTING", RF2_REFLECTING },
+ { "INVISIBLE", RF2_INVISIBLE },
+ { "COLD_BLOOD", RF2_COLD_BLOOD },
+ { "EMPTY_MIND", RF2_EMPTY_MIND },
+ { "WEIRD_MIND", RF2_WEIRD_MIND },
+ { "MULTIPLY", RF2_MULTIPLY },
+ { "REGENERATE", RF2_REGENERATE },
+ { "CHAR_MULTI", RF2_CHAR_MULTI },
+ { "ATTR_ANY", RF2_ATTR_ANY },
+ { "POWERFUL", RF2_POWERFUL },
+ { "ELDRITCH_HORROR", RF2_ELDRITCH_HORROR },
+ { "AURA_FIRE", RF2_AURA_FIRE },
+ { "AURA_ELEC", RF2_AURA_ELEC },
+ { "OPEN_DOOR", RF2_OPEN_DOOR },
+ { "BASH_DOOR", RF2_BASH_DOOR },
+ { "PASS_WALL", RF2_PASS_WALL },
+ { "KILL_WALL", RF2_KILL_WALL },
+ { "MOVE_BODY", RF2_MOVE_BODY },
+ { "KILL_BODY", RF2_KILL_BODY },
+ { "TAKE_ITEM", RF2_TAKE_ITEM },
+ { "KILL_ITEM", RF2_KILL_ITEM },
+ { "HUMAN", RF2_HUMAN },
+ { "QUANTUM", RF2_QUANTUM }
};
/*!
* モンスター特性トークンの定義3 /
* Monster race flags
*/
-concptr r_info_flags3[NUM_R_FLAGS_3] = {
- "ORC",
- "TROLL",
- "GIANT",
- "DRAGON",
- "DEMON",
- "UNDEAD",
- "EVIL",
- "ANIMAL",
- "AMBERITE",
- "GOOD",
- "AURA_COLD",
- "NONLIVING",
- "HURT_LITE",
- "HURT_ROCK",
- "HURT_FIRE",
- "HURT_COLD",
- "ANGEL",
- "XXX",
- "XXX",
- "XXX",
- "XXX",
- "XXX",
- "XXX",
- "XXX",
- "XXX",
- "XXX",
- "XXX",
- "XXX",
- "NO_FEAR",
- "NO_STUN",
- "NO_CONF",
- "NO_SLEEP"
+const std::unordered_map<std::string_view, race_flags3> r_info_flags3 = {
+ { "ORC", RF3_ORC },
+ { "TROLL", RF3_TROLL },
+ { "GIANT", RF3_GIANT },
+ { "DRAGON", RF3_DRAGON },
+ { "DEMON", RF3_DEMON },
+ { "UNDEAD", RF3_UNDEAD },
+ { "EVIL", RF3_EVIL },
+ { "ANIMAL", RF3_ANIMAL },
+ { "AMBERITE", RF3_AMBERITE },
+ { "GOOD", RF3_GOOD },
+ { "AURA_COLD", RF3_AURA_COLD },
+ { "NONLIVING", RF3_NONLIVING },
+ { "HURT_LITE", RF3_HURT_LITE },
+ { "HURT_ROCK", RF3_HURT_ROCK },
+ { "HURT_FIRE", RF3_HURT_FIRE },
+ { "HURT_COLD", RF3_HURT_COLD },
+ { "ANGEL", RF3_ANGEL },
+ { "NO_FEAR", RF3_NO_FEAR },
+ { "NO_STUN", RF3_NO_STUN },
+ { "NO_CONF", RF3_NO_CONF },
+ { "NO_SLEEP", RF3_NO_SLEEP }
};
/*!
* Monster race flags
* "GUARDIAN" ... init.c d_infoの FINAL_GUARDIAN_* にて自動指定
*/
-concptr r_info_flags7[NUM_R_FLAGS_7] = {
- "AQUATIC",
- "CAN_SWIM",
- "CAN_FLY",
- "FRIENDLY",
- "NAZGUL",
- "UNIQUE2",
- "RIDING",
- "KAGE",
- "HAS_LITE_1",
- "SELF_LITE_1",
- "HAS_LITE_2",
- "SELF_LITE_2",
- "XXX7X12",
- "CHAMELEON",
- "XXXX4XXX",
- "TANUKI",
- "HAS_DARK_1",
- "SELF_DARK_1",
- "HAS_DARK_2",
- "SELF_DARK_2",
- "XXX7X20",
- "XXX7X21",
- "XXX7X22",
- "XXX7X23",
- "XXX7X24",
- "XXX7X25",
- "XXX7X26",
- "XXX7X27",
- "XXX7X28",
- "XXX7X29",
- "XXX7X30",
- "XXX7X31",
+const std::unordered_map<std::string_view, race_flags7> r_info_flags7 = {
+ { "AQUATIC", RF7_AQUATIC },
+ { "CAN_SWIM", RF7_CAN_SWIM },
+ { "CAN_FLY", RF7_CAN_FLY },
+ { "FRIENDLY", RF7_FRIENDLY },
+ { "NAZGUL", RF7_NAZGUL },
+ { "UNIQUE2", RF7_UNIQUE2 },
+ { "RIDING", RF7_RIDING },
+ { "KAGE", RF7_KAGE },
+ { "HAS_LITE_1", RF7_HAS_LITE_1 },
+ { "SELF_LITE_1", RF7_SELF_LITE_1 },
+ { "HAS_LITE_2", RF7_HAS_LITE_2 },
+ { "SELF_LITE_2", RF7_SELF_LITE_2 },
+ { "CHAMELEON", RF7_CHAMELEON },
+ { "TANUKI", RF7_TANUKI },
+ { "HAS_DARK_1", RF7_HAS_DARK_1 },
+ { "SELF_DARK_1", RF7_SELF_DARK_1 },
+ { "HAS_DARK_2", RF7_HAS_DARK_2 },
+ { "SELF_DARK_2", RF7_SELF_DARK_2 },
};
/*!
* モンスター特性トークンの定義8 /
* Monster race flags
*/
-concptr r_info_flags8[NUM_R_FLAGS_8] = {
- "WILD_ONLY",
- "WILD_TOWN",
- "XXX8X02",
- "WILD_SHORE",
- "WILD_OCEAN",
- "WILD_WASTE",
- "WILD_WOOD",
- "WILD_VOLCANO",
- "XXX8X08",
- "WILD_MOUNTAIN",
- "WILD_GRASS",
- "XXX8X11",
- "XXX8X12",
- "XXX8X13",
- "XXX8X14",
- "XXX8X15",
- "XXX8X16",
- "XXX8X17",
- "XXX8X18",
- "XXX8X19",
- "XXX8X20",
- "XXX8X21",
- "XXX8X22",
- "XXX8X23",
- "XXX8X24",
- "XXX8X25",
- "XXX8X26",
- "XXX8X27",
- "XXX8X28",
- "XXX8X29",
- "WILD_SWAMP", /* ToDo: Implement Swamp */
- "WILD_ALL",
+const std::unordered_map<std::string_view, race_flags8> r_info_flags8 = {
+ { "WILD_ONLY", RF8_WILD_ONLY },
+ { "WILD_TOWN", RF8_WILD_TOWN },
+ { "WILD_SHORE", RF8_WILD_SHORE },
+ { "WILD_OCEAN", RF8_WILD_OCEAN },
+ { "WILD_WASTE", RF8_WILD_WASTE },
+ { "WILD_WOOD", RF8_WILD_WOOD },
+ { "WILD_VOLCANO", RF8_WILD_VOLCANO },
+ { "WILD_MOUNTAIN", RF8_WILD_MOUNTAIN },
+ { "WILD_GRASS", RF8_WILD_GRASS },
+ { "WILD_SWAMP", RF8_WILD_SWAMP },
+ { "WILD_ALL", RF8_WILD_ALL },
};
/*!
* モンスター特性トークンの定義9 /
* Monster race flags
*/
-concptr r_info_flags9[NUM_R_FLAGS_9] = {
- "DROP_CORPSE",
- "DROP_SKELETON",
- "EAT_BLIND",
- "EAT_CONF",
- "EAT_MANA",
- "EAT_NEXUS",
- "EAT_BLINK",
- "EAT_SLEEP",
- "EAT_BERSERKER",
- "EAT_ACIDIC",
- "EAT_SPEED",
- "EAT_CURE",
- "EAT_FIRE_RES",
- "EAT_COLD_RES",
- "EAT_ACID_RES",
- "EAT_ELEC_RES",
- "EAT_POIS_RES",
- "EAT_INSANITY",
- "EAT_DRAIN_EXP",
- "EAT_POISONOUS",
- "EAT_GIVE_STR",
- "EAT_GIVE_INT",
- "EAT_GIVE_WIS",
- "EAT_GIVE_DEX",
- "EAT_GIVE_CON",
- "EAT_GIVE_CHR",
- "EAT_LOSE_STR",
- "EAT_LOSE_INT",
- "EAT_LOSE_WIS",
- "EAT_LOSE_DEX",
- "EAT_LOSE_CON",
- "EAT_LOSE_CHR",
- "EAT_DRAIN_MANA",
+const std::unordered_map<std::string_view, race_flags9> r_info_flags9 = {
+ { "DROP_CORPSE", RF9_DROP_CORPSE },
+ { "DROP_SKELETON", RF9_DROP_SKELETON },
+ { "EAT_BLIND", RF9_EAT_BLIND },
+ { "EAT_CONF", RF9_EAT_CONF },
+ { "EAT_MANA", RF9_EAT_MANA },
+ { "EAT_NEXUS", RF9_EAT_NEXUS },
+ // { "EAT_BLINK", RF9_EAT_BLINK }, //<! @note フラグ未定義
+ { "EAT_SLEEP", RF9_EAT_SLEEP },
+ { "EAT_BERSERKER", RF9_EAT_BERSERKER },
+ { "EAT_ACIDIC", RF9_EAT_ACIDIC },
+ { "EAT_SPEED", RF9_EAT_SPEED },
+ { "EAT_CURE", RF9_EAT_CURE },
+ { "EAT_FIRE_RES", RF9_EAT_FIRE_RES },
+ { "EAT_COLD_RES", RF9_EAT_COLD_RES },
+ { "EAT_ACID_RES", RF9_EAT_ACID_RES },
+ { "EAT_ELEC_RES", RF9_EAT_ELEC_RES },
+ { "EAT_POIS_RES", RF9_EAT_POIS_RES },
+ { "EAT_INSANITY", RF9_EAT_INSANITY },
+ { "EAT_DRAIN_EXP", RF9_EAT_DRAIN_EXP },
+ { "EAT_POISONOUS", RF9_EAT_POISONOUS },
+ { "EAT_GIVE_STR", RF9_EAT_GIVE_STR },
+ { "EAT_GIVE_INT", RF9_EAT_GIVE_INT },
+ { "EAT_GIVE_WIS", RF9_EAT_GIVE_WIS },
+ { "EAT_GIVE_DEX", RF9_EAT_GIVE_DEX },
+ { "EAT_GIVE_CON", RF9_EAT_GIVE_CON },
+ { "EAT_GIVE_CHR", RF9_EAT_GIVE_CHR },
+ { "EAT_LOSE_STR", RF9_EAT_LOSE_STR },
+ { "EAT_LOSE_INT", RF9_EAT_LOSE_INT },
+ { "EAT_LOSE_WIS", RF9_EAT_LOSE_WIS },
+ { "EAT_LOSE_DEX", RF9_EAT_LOSE_DEX },
+ { "EAT_LOSE_CON", RF9_EAT_LOSE_CON },
+ { "EAT_LOSE_CHR", RF9_EAT_LOSE_CHR },
+ { "EAT_DRAIN_MANA", RF9_EAT_DRAIN_MANA },
};
/*!
* モンスター特性トークンの定義R(耐性) /
* Monster race flags
*/
-concptr r_info_flagsr[NUM_R_FLAGS_R] = {
- "IM_ACID",
- "IM_ELEC",
- "IM_FIRE",
- "IM_COLD",
- "IM_POIS",
- "RES_LITE",
- "RES_DARK",
- "RES_NETH",
- "RES_WATE",
- "RES_PLAS",
- "RES_SHAR",
- "RES_SOUN",
- "RES_CHAO",
- "RES_NEXU",
- "RES_DISE",
- "RES_WALL",
- "RES_INER",
- "RES_TIME",
- "RES_GRAV",
- "RES_ALL",
- "RES_TELE",
- "XXX",
- "XXX",
- "XXX",
- "XXX",
- "XXX",
- "XXX",
- "XXX",
- "XXX",
- "XXX",
- "XXX",
- "XXX",
+const std::unordered_map<std::string_view, race_flags_resistance> r_info_flagsr = {
+ { "IM_ACID", RFR_IM_ACID },
+ { "IM_ELEC", RFR_IM_ELEC },
+ { "IM_FIRE", RFR_IM_FIRE },
+ { "IM_COLD", RFR_IM_COLD },
+ { "IM_POIS", RFR_IM_POIS },
+ { "RES_LITE", RFR_RES_LITE },
+ { "RES_DARK", RFR_RES_DARK },
+ { "RES_NETH", RFR_RES_NETH },
+ { "RES_WATE", RFR_RES_WATE },
+ { "RES_PLAS", RFR_RES_PLAS },
+ { "RES_SHAR", RFR_RES_SHAR },
+ { "RES_SOUN", RFR_RES_SOUN },
+ { "RES_CHAO", RFR_RES_CHAO },
+ { "RES_NEXU", RFR_RES_NEXU },
+ { "RES_DISE", RFR_RES_DISE },
+ { "RES_WALL", RFR_RES_WALL },
+ { "RES_INER", RFR_RES_INER },
+ { "RES_TIME", RFR_RES_TIME },
+ { "RES_GRAV", RFR_RES_GRAV },
+ { "RES_ALL", RFR_RES_ALL },
+ { "RES_TELE", RFR_RES_TELE },
};
#include "monster-attack/monster-attack-effect.h"
#include "monster-attack/monster-attack-types.h"
+#include "monster-race/race-ability-flags.h"
+#include "monster-race/race-flags1.h"
+#include "monster-race/race-flags2.h"
+#include "monster-race/race-flags3.h"
+#include "monster-race/race-flags7.h"
+#include "monster-race/race-flags8.h"
+#include "monster-race/race-flags9.h"
+#include "monster-race/race-flags-resistance.h"
#include "system/angband.h"
#include <string_view>
#include <unordered_map>
-#define NUM_R_FLAGS_1 32
-#define NUM_R_FLAGS_2 32
-#define NUM_R_FLAGS_3 32
-#define NUM_R_FLAGS_4 32
-#define NUM_R_ABILITY_FLAGS_1 32
-#define NUM_R_ABILITY_FLAGS_2 32
-#define NUM_R_FLAGS_7 32
-#define NUM_R_FLAGS_8 32
-#define NUM_R_FLAGS_9 33
-#define NUM_R_FLAGS_R 32
-
enum class RF_ABILITY;
-extern concptr r_info_blow_method[NB_RBM_TYPE + 1];
-extern concptr r_info_blow_effect[NB_RBE_TYPE + 1];
-extern concptr r_info_flags1[NUM_R_FLAGS_1];
-extern concptr r_info_flags2[NUM_R_FLAGS_2];
-extern concptr r_info_flags3[NUM_R_FLAGS_3];
+extern const std::unordered_map<std::string_view, rbm_type> r_info_blow_method;
+extern const std::unordered_map<std::string_view, rbe_type> r_info_blow_effect;
+extern const std::unordered_map<std::string_view, race_flags1> r_info_flags1;
+extern const std::unordered_map<std::string_view, race_flags2> r_info_flags2;
+extern const std::unordered_map<std::string_view, race_flags3> r_info_flags3;
extern const std::unordered_map<std::string_view, RF_ABILITY> r_info_ability_flags;
-extern concptr r_info_flags7[NUM_R_FLAGS_7];
-extern concptr r_info_flags8[NUM_R_FLAGS_8];
-extern concptr r_info_flags9[NUM_R_FLAGS_9];
-extern concptr r_info_flagsr[NUM_R_FLAGS_R];
+extern const std::unordered_map<std::string_view, race_flags7> r_info_flags7;
+extern const std::unordered_map<std::string_view, race_flags8> r_info_flags8;
+extern const std::unordered_map<std::string_view, race_flags9> r_info_flags9;
+extern const std::unordered_map<std::string_view, race_flags_resistance> r_info_flagsr;
#include "info-reader/race-reader.h"
+#include "info-reader/info-reader-util.h"
#include "info-reader/parse-error-types.h"
#include "info-reader/race-info-tokens-table.h"
#include "main/angband-headers.h"
* Grab one (basic) flag in a monster_race from a textual string
* @param r_ptr 保管先のモンスター種族構造体参照ポインタ
* @param what 参照元の文字列ポインタ
- * @return エラーコード
+ * @return 見つけたらtrue
*/
-static errr grab_one_basic_flag(monster_race *r_ptr, concptr what)
+static bool grab_one_basic_flag(monster_race *r_ptr, std::string_view what)
{
- if (grab_one_flag(&r_ptr->flags1, r_info_flags1, what) == 0)
- return PARSE_ERROR_NONE;
+ if (info_grab_one_flag(r_ptr->flags1, r_info_flags1, what))
+ return true;
- if (grab_one_flag(&r_ptr->flags2, r_info_flags2, what) == 0)
- return PARSE_ERROR_NONE;
+ if (info_grab_one_flag(r_ptr->flags2, r_info_flags2, what))
+ return true;
- if (grab_one_flag(&r_ptr->flags3, r_info_flags3, what) == 0)
- return PARSE_ERROR_NONE;
+ if (info_grab_one_flag(r_ptr->flags3, r_info_flags3, what))
+ return true;
- if (grab_one_flag(&r_ptr->flags7, r_info_flags7, what) == 0)
- return PARSE_ERROR_NONE;
+ if (info_grab_one_flag(r_ptr->flags7, r_info_flags7, what))
+ return true;
- if (grab_one_flag(&r_ptr->flags8, r_info_flags8, what) == 0)
- return PARSE_ERROR_NONE;
+ if (info_grab_one_flag(r_ptr->flags8, r_info_flags8, what))
+ return true;
- if (grab_one_flag(&r_ptr->flags9, r_info_flags9, what) == 0)
- return PARSE_ERROR_NONE;
+ if (info_grab_one_flag(r_ptr->flags9, r_info_flags9, what))
+ return true;
- if (grab_one_flag(&r_ptr->flagsr, r_info_flagsr, what) == 0)
- return PARSE_ERROR_NONE;
+ if (info_grab_one_flag(r_ptr->flagsr, r_info_flagsr, what))
+ return true;
- msg_format(_("未知のモンスター・フラグ '%s'。", "Unknown monster flag '%s'."), what);
- return PARSE_ERROR_GENERIC;
+ msg_format(_("未知のモンスター・フラグ '%s'。", "Unknown monster flag '%s'."), what.data());
+ return false;
}
/*!
* Grab one (spell) flag in a monster_race from a textual string
* @param r_ptr 保管先のモンスター種族構造体参照ポインタ
* @param what 参照元の文字列ポインタ
- * @return エラーコード
+ * @return 見つけたらtrue
*/
-static errr grab_one_spell_flag(monster_race *r_ptr, concptr what)
+static bool grab_one_spell_flag(monster_race *r_ptr, std::string_view what)
{
if (EnumClassFlagGroup<RF_ABILITY>::grab_one_flag(r_ptr->ability_flags, r_info_ability_flags, what))
- return PARSE_ERROR_NONE;
+ return true;
- msg_format(_("未知のモンスター・フラグ '%s'。", "Unknown monster flag '%s'."), what);
- return PARSE_ERROR_GENERIC;
+ msg_format(_("未知のモンスター・フラグ '%s'。", "Unknown monster flag '%s'."), what.data());
+ return false;
}
/*!
* @param head ヘッダ構造体
* @return エラーコード
*/
-errr parse_r_info(char *buf, angband_header *head)
+errr parse_r_info(std::string_view buf, angband_header *head)
{
static monster_race *r_ptr = NULL;
- char *s, *t;
- if (buf[0] == 'N') {
- s = angband_strchr(buf + 2, ':');
- if (!s)
- return PARSE_ERROR_GENERIC;
+ const auto &tokens = str_split(buf, ':', true, 10);
- *s++ = '\0';
-#ifdef JP
- if (!*s)
- return PARSE_ERROR_GENERIC;
-#endif
+ if (tokens[0] == "N") {
+ // N:index:name_ja
+ if (tokens.size() < 3 || tokens[1].size() == 0)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
- int i = atoi(buf + 2);
+ auto i = std::stoi(tokens[1]);
if (i < error_idx)
return PARSE_ERROR_NON_SEQUENTIAL_RECORDS;
-
if (i >= head->info_num)
- return PARSE_ERROR_OBSOLETE_FILE;
+ return PARSE_ERROR_OUT_OF_BOUNDS;
error_idx = i;
r_ptr = &r_info[i];
#ifdef JP
- r_ptr->name = std::string(s);
+ r_ptr->name = tokens[2];
#endif
- } else if (!r_ptr) {
+ } else if (!r_ptr)
return PARSE_ERROR_MISSING_RECORD_HEADER;
- }
-#ifdef JP
- /* 英語名を読むルーチンを追加 */
- /* 'E' から始まる行は英語名 */
- else if (buf[0] == 'E') {
- r_ptr->E_name = std::string(buf + 2);
- }
-#else
- else if (buf[0] == 'E') {
- r_ptr->name = std::string(buf + 2);
- }
+ else if (tokens[0] == "E") {
+ // E:name_en
+#ifndef JP
+ if (tokens.size() < 2 || tokens[1].size() == 0)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+ r_ptr->name = tokens[1];
#endif
- else if (buf[0] == 'D') {
+ } else if (tokens[0] == "D") {
+ // D:text_ja
+ // D:$text_en
+ if (tokens.size() < 2 || tokens[1].size() == 0)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
#ifdef JP
- if (buf[2] == '$')
+ if (tokens[1][0] == '$')
return PARSE_ERROR_NONE;
-
- s = buf + 2;
+ r_ptr->text.append(buf.substr(2));
#else
- if (buf[2] != '$')
+ if (tokens[1][0] != '$')
return PARSE_ERROR_NONE;
- s = buf + 3;
+ r_ptr->text.append(buf.substr(3));
#endif
- r_ptr->text.append(s);
- } else if (buf[0] == 'G') {
- if ((buf[1] != ':') || !buf[2] || (buf[3] != ':') || !buf[4])
- return PARSE_ERROR_GENERIC;
+ } else if (tokens[0] == "G") {
+ // G:color:symbol
+ if (tokens.size() < 3 || tokens[1].size() == 0 || tokens[2].size() == 0)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
- char sym = buf[2];
- byte tmp = color_char_to_attr(buf[4]);
- if (tmp > 127)
+ auto a = color_char_to_attr(tokens[2][0]);
+ if (a > 127)
return PARSE_ERROR_GENERIC;
- r_ptr->d_char = sym;
- r_ptr->d_attr = tmp;
- } else if (buf[0] == 'I') {
- int spd, hp1, hp2, aaf, ac, slp;
- if (sscanf(buf + 2, "%d:%dd%d:%d:%d:%d", &spd, &hp1, &hp2, &aaf, &ac, &slp) != 6)
- return PARSE_ERROR_GENERIC;
+ r_ptr->d_attr = a;
+ r_ptr->d_char = tokens[1][0];
+ } else if (tokens[0] == "I") {
+ // G:speed:hp_dice:affect_range:ac:sleep_degree
+ if (tokens.size() < 6)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
- r_ptr->speed = (SPEED)spd;
- r_ptr->hdice = (DICE_NUMBER)MAX(hp1, 1);
- r_ptr->hside = (DICE_SID)MAX(hp2, 1);
- r_ptr->aaf = (POSITION)aaf;
- r_ptr->ac = (ARMOUR_CLASS)ac;
- r_ptr->sleep = (SLEEP_DEGREE)slp;
- } else if (buf[0] == 'W') {
- int lev, rar, pad;
- long exp;
- long nextexp;
- int nextmon;
- if (sscanf(buf + 2, "%d:%d:%d:%ld:%ld:%d", &lev, &rar, &pad, &exp, &nextexp, &nextmon) != 6)
+ const auto &dice = str_split(tokens[2], 'd', false, 2);
+ if (dice.size() < 2)
return PARSE_ERROR_GENERIC;
- r_ptr->level = (DEPTH)lev;
- r_ptr->rarity = (RARITY)rar;
- r_ptr->extra = (BIT_FLAGS16)pad;
- r_ptr->mexp = (EXP)exp;
- r_ptr->next_exp = (EXP)nextexp;
- r_ptr->next_r_idx = (MONRACE_IDX)nextmon;
- } else if (buf[0] == 'R') {
- int id, ds, dd;
- int i = 0;
- for (; i < A_MAX; i++)
+ info_set_value(r_ptr->speed, tokens[1]);
+ info_set_value(r_ptr->hdice, dice[0]);
+ info_set_value(r_ptr->hside, dice.size() == 1 ? "1" : dice[1]);
+ info_set_value(r_ptr->aaf, tokens[3]);
+ info_set_value(r_ptr->ac, tokens[4]);
+ info_set_value(r_ptr->sleep, tokens[5]);
+ } else if (tokens[0] == "W") {
+ // W:level:ratity:extra:exp:next_exp:next_id
+ if (tokens.size() < 5 || tokens.size() == 6)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+
+ info_set_value(r_ptr->level, tokens[1]);
+ info_set_value(r_ptr->rarity, tokens[2]);
+ info_set_value(r_ptr->extra, tokens[3]);
+ info_set_value(r_ptr->mexp, tokens[4]);
+
+ if (tokens.size() < 6)
+ return PARSE_ERROR_NONE;
+
+ info_set_value(r_ptr->next_exp, tokens[5]);
+ info_set_value(r_ptr->next_r_idx, tokens[6]);
+ } else if (tokens[0] == "R") {
+ // R:reinforcer_idx:number_dice
+ size_t i = 0;
+ for (; i < A_MAX; i++) {
if (r_ptr->reinforce_id[i] == 0)
break;
+ }
- if ((i == 6) || (sscanf(buf + 2, "%d:%dd%d", &id, &dd, &ds) != 3))
+ if (i >= 6)
return PARSE_ERROR_GENERIC;
- r_ptr->reinforce_id[i] = (MONRACE_IDX)id;
- r_ptr->reinforce_dd[i] = (DICE_NUMBER)dd;
- r_ptr->reinforce_ds[i] = (DICE_SID)ds;
- } else if (buf[0] == 'B') {
- int n1, n2;
- int i = 0;
- for (i = 0; i < 4; i++)
+ if (tokens.size() < 3)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+ if (tokens[1].size() == 0 || tokens[2].size() == 0)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+
+ const auto &dice = str_split(tokens[2], 'd', false, 2);
+ info_set_value(r_ptr->reinforce_id[i], tokens[1]);
+ info_set_value(r_ptr->reinforce_dd[i], dice[0]);
+ info_set_value(r_ptr->reinforce_ds[i], dice[1]);
+ } else if (tokens[0] == "B") {
+ // B:blow_type:blow_effect:dice
+ size_t i = 0;
+ for (; i < 4; i++) {
if (!r_ptr->blow[i].method)
break;
-
- if (i == 4)
- return PARSE_ERROR_GENERIC;
-
- /* loop */
- for (s = t = buf + 2; *t && (*t != ':'); t++)
- ;
-
- if (*t == ':')
- *t++ = '\0';
-
- for (n1 = 0; r_info_blow_method[n1]; n1++) {
- if (streq(s, r_info_blow_method[n1]))
- break;
}
- if (!r_info_blow_method[n1])
+ if (i >= 4)
return PARSE_ERROR_GENERIC;
- /* loop */
- for (s = t; *t && (*t != ':'); t++)
- ;
+ if (tokens.size() < 3)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+ if (tokens[1].size() == 0 || tokens[2].size() == 0)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
- if (*t == ':')
- *t++ = '\0';
+ auto rbm = r_info_blow_method.find(tokens[1]);
+ if (rbm == r_info_blow_method.end())
+ return PARSE_ERROR_INVALID_FLAG;
- for (n2 = 0; r_info_blow_effect[n2]; n2++) {
- if (streq(s, r_info_blow_effect[n2]))
- break;
- }
+ auto rbe = r_info_blow_effect.find(tokens[2]);
+ if (rbe == r_info_blow_effect.end())
+ return PARSE_ERROR_INVALID_FLAG;
- if (!r_info_blow_effect[n2])
- return PARSE_ERROR_GENERIC;
+ r_ptr->blow[i].method = rbm->second;
+ r_ptr->blow[i].effect = rbe->second;
- /* loop */
- for (s = t; *t && (*t != 'd'); t++)
- ;
-
- if (*t == 'd')
- *t++ = '\0';
-
- r_ptr->blow[i].method = (rbm_type)n1;
- r_ptr->blow[i].effect = (rbe_type)n2;
- r_ptr->blow[i].d_dice = atoi(s);
- r_ptr->blow[i].d_side = atoi(t);
- } else if (buf[0] == 'F') {
- for (s = buf + 2; *s;) {
- /* loop */
- for (t = s; *t && (*t != ' ') && (*t != '|'); ++t)
- ;
-
- if (*t) {
- *t++ = '\0';
- while (*t == ' ' || *t == '|')
- t++;
- }
+ if (tokens.size() < 4)
+ return PARSE_ERROR_NONE;
- if (0 != grab_one_basic_flag(r_ptr, s))
- return PARSE_ERROR_INVALID_FLAG;
+ const auto &dice = str_split(tokens[3], 'd', false, 2);
+ info_set_value(r_ptr->blow[i].d_dice, dice[0]);
+ info_set_value(r_ptr->blow[i].d_side, dice[1]);
+ } else if (tokens[0] == "F") {
+ // F:flags
+ if (tokens.size() < 2 || tokens[1].size() == 0)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+
+ const auto &flags = str_split(tokens[1], '|', true, 10);
+ for (const auto &f : flags) {
+ if (f.size() == 0)
+ continue;
- s = t;
+ if (!grab_one_basic_flag(r_ptr, f))
+ return PARSE_ERROR_INVALID_FLAG;
}
- } else if (buf[0] == 'S') {
- for (s = buf + 2; *s;) {
-
- /* loop */
- for (t = s; *t && (*t != ' ') && (*t != '|'); ++t)
- ;
-
- if (*t) {
- *t++ = '\0';
- while ((*t == ' ') || (*t == '|'))
- t++;
- }
+ } else if (tokens[0] == "S") {
+ // S:flags
+ if (tokens.size() < 2 || tokens[1].size() == 0)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+
+ const auto &flags = str_split(tokens[1], '|', true, 10);
+ for (const auto &f : flags) {
+ if (f.size() == 0)
+ continue;
- int i;
- if (1 == sscanf(s, "1_IN_%d", &i)) {
+ const auto &s_tokens = str_split(f, '_', false, 3);
+ if (s_tokens.size() == 3 && s_tokens[1] == "IN") {
+ if (s_tokens[0] != "1")
+ return PARSE_ERROR_GENERIC;
+ RARITY i;
+ info_set_value(i, s_tokens[2]);
r_ptr->freq_spell = 100 / i;
- s = t;
- continue;
+ return PARSE_ERROR_NONE;
}
- if (grab_one_spell_flag(r_ptr, s) != PARSE_ERROR_NONE)
+ if (!grab_one_spell_flag(r_ptr, f))
return PARSE_ERROR_INVALID_FLAG;
-
- s = t;
}
- } else if (buf[0] == 'A') {
- int id, per, rarity;
- int i = 0;
- for (i = 0; i < 4; i++)
+
+ } else if (tokens[0] == "A") {
+ // A:artifact_idx:rarity:percent
+ size_t i = 0;
+ for (; i < 4; i++) {
if (!r_ptr->artifact_id[i])
break;
-
- if ((i == 4) || (sscanf(buf + 2, "%d:%d:%d", &id, &rarity, &per) != 3))
+ }
+ if (i >= 4)
return PARSE_ERROR_GENERIC;
- r_ptr->artifact_id[i] = (ARTIFACT_IDX)id;
- r_ptr->artifact_rarity[i] = (RARITY)rarity;
- r_ptr->artifact_percent[i] = (PERCENTAGE)per;
- } else if (buf[0] == 'V') {
- int val;
- if (sscanf(buf + 2, "%d", &val) != 3)
- return 1;
+ if (tokens.size() < 4)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+
+ info_set_value(r_ptr->artifact_id[i], tokens[1]);
+ info_set_value(r_ptr->artifact_rarity[i], tokens[2]);
+ info_set_value(r_ptr->artifact_percent[i], tokens[3]);
+ } else if (tokens[0] == "V") {
+ // V:arena_odds
+ if (tokens.size() < 2)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
- r_ptr->arena_ratio = (PERCENTAGE)val;
- } else {
+ info_set_value(r_ptr->arena_ratio, tokens[1]);
+ } else
return PARSE_ERROR_UNDEFINED_DIRECTIVE;
- }
return PARSE_ERROR_NONE;
}
#pragma once
-#include "info-reader/info-reader-util.h"
#include "system/angband.h"
+#include <string_view>
-errr parse_r_info(char *buf, angband_header *head);
+struct angband_header;
+errr parse_r_info(std::string_view buf, angband_header *head);
#include "info-reader/skill-reader.h"
+#include "info-reader/info-reader-util.h"
+#include "info-reader/parse-error-types.h"
#include "main/angband-headers.h"
+#include "object/tval-types.h"
#include "player/player-skill.h"
+#include "util/string-processor.h"
+
+namespace {
+const std::unordered_map<int, int> level_to_exp = {
+ { EXP_LEVEL_UNSKILLED, WEAPON_EXP_UNSKILLED },
+ { EXP_LEVEL_BEGINNER, WEAPON_EXP_BEGINNER },
+ { EXP_LEVEL_SKILLED, WEAPON_EXP_SKILLED },
+ { EXP_LEVEL_EXPERT, WEAPON_EXP_EXPERT },
+ { EXP_LEVEL_MASTER, WEAPON_EXP_MASTER },
+};
+}
/*!
* @brief 職業技能情報(s_info)のパース関数 /
* @param head ヘッダ構造体
* @return エラーコード
*/
-errr parse_s_info(char *buf, angband_header *head)
+errr parse_s_info(std::string_view buf, angband_header *head)
{
static skill_table *s_ptr = NULL;
- if (buf[0] == 'N') {
- int i = atoi(buf + 2);
- if (i <= error_idx)
- return 4;
+ const auto &tokens = str_split(buf, ':', false, 5);
+
+ if (tokens[0] == "N") {
+ // N:class-index
+ if (tokens.size() < 2 && tokens[1].size() == 0)
+ return PARSE_ERROR_GENERIC;
+
+ auto i = std::stoi(tokens[1]);
+ if (i < error_idx)
+ return PARSE_ERROR_NON_SEQUENTIAL_RECORDS;
if (i >= head->info_num)
- return 2;
+ return PARSE_ERROR_OUT_OF_BOUNDS;
error_idx = i;
s_ptr = &s_info[i];
- } else if (!s_ptr) {
- return 3;
- } else if (buf[0] == 'W') {
+ } else if (!s_ptr)
+ return PARSE_ERROR_MISSING_RECORD_HEADER;
+ else if(tokens[0] == "W") {
+ if (tokens.size() < 5)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+
int tval, sval, start, max;
- const s16b exp_conv_table[] = { WEAPON_EXP_UNSKILLED, WEAPON_EXP_BEGINNER, WEAPON_EXP_SKILLED, WEAPON_EXP_EXPERT, WEAPON_EXP_MASTER };
+ info_set_value(tval, tokens[1]);
+ info_set_value(sval, tokens[2]);
+ info_set_value(start, tokens[3]);
+ info_set_value(max, tokens[4]);
+
+ auto start_exp = level_to_exp.find(start);
+ if (start_exp == level_to_exp.end())
+ return PARSE_ERROR_INVALID_FLAG;
- if (4 != sscanf(buf + 2, "%d:%d:%d:%d", &tval, &sval, &start, &max))
- return 1;
+ auto max_exp = level_to_exp.find(max);
+ if (max_exp == level_to_exp.end())
+ return PARSE_ERROR_INVALID_FLAG;
- if (start < EXP_LEVEL_UNSKILLED || start > EXP_LEVEL_MASTER || max < EXP_LEVEL_UNSKILLED || max > EXP_LEVEL_MASTER)
- return 8;
+ s_ptr->w_start[tval][sval] = (SUB_EXP)start_exp->second;
+ s_ptr->w_max[tval][sval] = (SUB_EXP)max_exp->second;
+ } else if (tokens[0] == "S") {
+ if (tokens.size() < 4)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
- s_ptr->w_start[tval][sval] = exp_conv_table[start];
- s_ptr->w_max[tval][sval] = exp_conv_table[max];
- } else if (buf[0] == 'S') {
int num, start, max;
- if (3 != sscanf(buf + 2, "%d:%d:%d", &num, &start, &max))
- return 1;
+ info_set_value(num, tokens[1]);
+ info_set_value(start, tokens[2]);
+ info_set_value(max, tokens[3]);
- if (start < WEAPON_EXP_UNSKILLED || start > WEAPON_EXP_MASTER || max < WEAPON_EXP_UNSKILLED || max > WEAPON_EXP_MASTER)
- return 8;
+ if (start < WEAPON_EXP_UNSKILLED || start > WEAPON_EXP_MASTER || max < WEAPON_EXP_UNSKILLED || max > WEAPON_EXP_MASTER || start > max)
+ return PARSE_ERROR_INVALID_FLAG;
s_ptr->s_start[num] = (SUB_EXP)start;
s_ptr->s_max[num] = (SUB_EXP)max;
} else
- return 6;
+ return PARSE_ERROR_UNDEFINED_DIRECTIVE;
- return 0;
+ return PARSE_ERROR_NONE;
}
#pragma once
#include "system/angband.h"
-#include "info-reader/info-reader-util.h"
+#include <string_view>
-errr parse_s_info(char *buf, angband_header *head);
+struct angband_header;
+errr parse_s_info(std::string_view buf, angband_header *head);
#include "info-reader/vault-reader.h"
#include "main/angband-headers.h"
+#include "info-reader/info-reader-util.h"
+#include "info-reader/parse-error-types.h"
#include "room/rooms-vault.h"
#include "util/string-processor.h"
-#include <string>
/*!
* @brief Vault情報(v_info)のパース関数 /
* @param head ヘッダ構造体
* @return エラーコード
*/
-errr parse_v_info(char *buf, angband_header *head)
+errr parse_v_info(std::string_view buf, angband_header *head)
{
- char *s;
static vault_type *v_ptr = NULL;
+ const auto &tokens = str_split(buf, ':', false, 5);
- if (buf[0] == 'N') {
- s = angband_strchr(buf + 2, ':');
- if (!s)
- return 1;
+ if (tokens[0] == "N") {
+ // N:index:name
+ if (tokens.size() < 3)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+ if (tokens[1].size() == 0 || tokens[2].size() == 0)
+ return PARSE_ERROR_GENERIC;
- *s++ = '\0';
- if (!*s)
- return 1;
-
- int i = atoi(buf + 2);
- if (i <= error_idx)
- return 4;
+ auto i = std::stoi(tokens[1]);
+ if (i < error_idx)
+ return PARSE_ERROR_NON_SEQUENTIAL_RECORDS;
if (i >= head->info_num)
- return 2;
+ return PARSE_ERROR_OUT_OF_BOUNDS;
error_idx = i;
v_ptr = &v_info[i];
- v_ptr->name = std::string(s);
+ v_ptr->name = std::string(tokens[2]);
} else if (!v_ptr)
- return 3;
- else if (buf[0] == 'D') {
- v_ptr->text.append(buf + 2);
- } else if (buf[0] == 'X') {
- EFFECT_ID typ, rat, hgt, wid;
- if (4 != sscanf(buf + 2, "%d:%d:%d:%d", &typ, &rat, &hgt, &wid))
- return 1;
+ return PARSE_ERROR_MISSING_RECORD_HEADER;
+ else if (tokens[0] == "D") {
+ // D:MapText
+ if (tokens.size() < 2 || tokens[1].size() == 0)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
+
+ v_ptr->text.append(buf.substr(2));
+ } else if (tokens[0] == "X") {
+ // X:type:rate:height:width
+ if (tokens.size() < 5)
+ return PARSE_ERROR_TOO_FEW_ARGUMENTS;
- v_ptr->typ = (ROOM_IDX)typ;
- v_ptr->rat = (PROB)rat;
- v_ptr->hgt = (POSITION)hgt;
- v_ptr->wid = (POSITION)wid;
+ info_set_value(v_ptr->typ, tokens[1]);
+ info_set_value(v_ptr->rat, tokens[2]);
+ info_set_value(v_ptr->hgt, tokens[3]);
+ info_set_value(v_ptr->wid, tokens[4]);
} else
- return 6;
+ return PARSE_ERROR_UNDEFINED_DIRECTIVE;
- return 0;
+ return PARSE_ERROR_NONE;
}
#pragma once
#include "system/angband.h"
-#include "info-reader/info-reader-util.h"
+#include <string_view>
-errr parse_v_info(char *buf, angband_header *head);
+struct angband_header;
+errr parse_v_info(std::string_view buf, angband_header *head);
/*!
* @brief 各初期データ用ヘッダ構造体 / Template file header information (see "init.c").
*/
-typedef struct angband_header angband_header;
-typedef errr (*parse_info_txt_func)(char *buf, angband_header *head);
-
struct angband_header {
byte checksum; //!< Checksum of "info" records
u16b info_num; //!< このinfoのデータ数
#include "info-reader/feature-reader.h"
#include "info-reader/fixed-map-parser.h"
#include "info-reader/general-parser.h"
+#include "info-reader/info-reader-util.h"
#include "info-reader/kind-reader.h"
#include "info-reader/magic-reader.h"
#include "info-reader/race-reader.h"
#ifndef WINDOWS
#include <sys/types.h>
#endif
+#include <string_view>
/*!
* @brief 基本情報読み込みのメインルーチン /
* even if the string happens to be empty (everyone has a unique '\0').
*/
template <typename InfoType>
-static errr init_info(concptr filename, angband_header &head, std::vector<InfoType> &info, parse_info_txt_func parser, void (*retouch)(angband_header *head))
+static errr init_info(concptr filename, angband_header &head, std::vector<InfoType> &info, std::function<errr(std::string_view, angband_header *)> parser,
+ void (*retouch)(angband_header *head))
{
char buf[1024];
path_build(buf, sizeof(buf), ANGBAND_DIR_EDIT, format("%s.txt", filename));
RF8_XXX8X08 = 0x00000100,
RF8_WILD_MOUNTAIN = 0x00000200,
RF8_WILD_GRASS = 0x00000400,
+ RF8_WILD_SWAMP = 0x00000800, //!< 沼地に生息(未使用)
RF8_WILD_ALL = 0x80000000,
};