diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 0b61a59070..90b0f11895 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -3613,9 +3613,9 @@ bool Game_Interpreter::CommandConditionalBranch(lcf::rpg::EventCommand const& co int op = com.parameters[4] & 3; int ignoreCase = com.parameters[4] >> 8 & 1; - std::string str_param = static_cast(com.string); - std::string str_l = Main_Data::game_strings->GetWithMode(str_param, com.parameters[2], modes[0]+1); - std::string str_r = Main_Data::game_strings->GetWithMode(str_param, com.parameters[3], modes[1]); + std::string str_param = ToString(com.string); + StringView str_l = Main_Data::game_strings->GetWithMode(str_param, modes[0]+1, com.parameters[2], *Main_Data::game_variables); + StringView str_r = Main_Data::game_strings->GetWithMode(str_param, modes[1], com.parameters[3], *Main_Data::game_variables); result = ManiacPatch::CheckString(str_l, str_r, op, ignoreCase); } break; @@ -4315,11 +4315,12 @@ bool Game_Interpreter::CommandManiacShowStringPicture(lcf::rpg::EventCommand con } if (com.parameters.size() >= 23) { - text.text = Main_Data::game_strings->GetWithMode( + text.text = ToString(Main_Data::game_strings->GetWithMode( components[1], + delims[0] - 1, com.parameters[22], - delims[0] - 1 - ); + *Main_Data::game_variables + )); } else { text.text = components[1]; } @@ -4718,8 +4719,8 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& string_id_1 = Main_Data::game_variables->Get(string_id_1); } - int op = (com.parameters[3] >> 0) & 255; - int fn = (com.parameters[3] >> 8) & 255; + int op = (com.parameters[3] >> 0) & 255; + int fn = (com.parameters[3] >> 8) & 255; int flags = (com.parameters[3] >> 16) & 255; int hex_flag = (flags >> 1) & 1; @@ -4743,8 +4744,8 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& (com.parameters[0] >> 16) & 15 }; - Game_Strings::Str_t str_param = static_cast(com.string); - Game_Strings::Str_t result = ""; + std::string str_param = ToString(com.string); + std::string result; Game_Strings::Str_Params str_params = { string_id_0, hex_flag, @@ -4758,18 +4759,18 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& switch (fn) { case 0: //String - result = Main_Data::game_strings->GetWithMode(str_param, args[0], modes[0]); + result = ToString(Main_Data::game_strings->GetWithMode(str_param, modes[0], args[0], *Main_Data::game_variables)); args[1] = ValueOrVariable(modes[1], args[1]); // min_size - result = Main_Data::game_strings->PrependMin(result, args[1], ' '); + result = Game_Strings::PrependMin(result, args[1], ' '); break; case 1: //Number args[0] = ValueOrVariable(modes[0], args[0]); args[1] = ValueOrVariable(modes[1], args[1]); result = std::to_string(args[0]); - result = Main_Data::game_strings->PrependMin(result, args[1], '0'); + result = Game_Strings::PrependMin(result, args[1], '0'); break; case 2: //Switch { @@ -4781,25 +4782,25 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& } else { result = "OFF"; } - result = Main_Data::game_strings->PrependMin(result, args[1], ' '); + result = Game_Strings::PrependMin(result, args[1], ' '); break; } case 3: //Database Names args[1] = ValueOrVariable(modes[1], args[1]); - result = ManiacPatch::GetLcfName(args[0], args[1], (bool)args[2]); + result = ToString(ManiacPatch::GetLcfName(args[0], args[1], (bool)args[2])); break; case 4: //Database Descriptions args[1] = ValueOrVariable(modes[1], args[1]); - result = ManiacPatch::GetLcfDescription(args[0], args[1], (bool)args[2]); + result = ToString(ManiacPatch::GetLcfDescription(args[0], args[1], (bool)args[2])); break; case 6: //Concatenate (cat) { int pos = 0; std::string op_string; for (int i = 0; i < 3; i++) { - op_string += Main_Data::game_strings->GetWithModeAndPos(str_param, args[i], modes[i], &pos); + op_string += ToString(Main_Data::game_strings->GetWithModeAndPos(str_param, modes[i], args[i], &pos, *Main_Data::game_variables)); } - result = (Game_Strings::Str_t)op_string; + result = std::move(op_string); break; } case 7: //Insert (ins) @@ -4808,10 +4809,10 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& std::string base, insert; args[1] = ValueOrVariable(modes[1], args[1]); - base = Main_Data::game_strings->GetWithModeAndPos(str_param, args[0], modes[0], &pos); - insert = Main_Data::game_strings->GetWithModeAndPos(str_param, args[2], modes[2], &pos); + base = ToString(Main_Data::game_strings->GetWithModeAndPos(str_param, modes[0], args[0], &pos, *Main_Data::game_variables)); + insert = ToString(Main_Data::game_strings->GetWithModeAndPos(str_param, modes[2], args[2], &pos, *Main_Data::game_variables)); - result = (Game_Strings::Str_t)base.insert(args[1], insert); + result = base.insert(args[1], insert); break; } case 8: //Replace (rep) @@ -4819,9 +4820,9 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& int pos = 0; std::string base, search, replacement; - base = Main_Data::game_strings->GetWithModeAndPos(str_param, args[0], modes[0], &pos); - search = Main_Data::game_strings->GetWithModeAndPos(str_param, args[1], modes[1], &pos); - replacement = Main_Data::game_strings->GetWithModeAndPos(str_param, args[2], modes[2], &pos); + base = ToString(Main_Data::game_strings->GetWithModeAndPos(str_param, modes[0], args[0], &pos, *Main_Data::game_variables)); + search = ToString(Main_Data::game_strings->GetWithModeAndPos(str_param, modes[1], args[1], &pos, *Main_Data::game_variables)); + replacement = ToString(Main_Data::game_strings->GetWithModeAndPos(str_param, modes[2], args[2], &pos, *Main_Data::game_variables)); std::size_t index = base.find(search); while (index != std::string::npos) { @@ -4829,18 +4830,18 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& index = base.find(search, index + replacement.length()); } - result = static_cast(base); + result = std::move(base); break; } case 9: //Substring (subs) args[1] = ValueOrVariable(modes[1], args[1]); args[2] = ValueOrVariable(modes[2], args[2]); - result = Main_Data::game_strings->GetWithMode(str_param, args[0], modes[0]).substr(args[1], args[2]); + result = ToString(Main_Data::game_strings->GetWithMode(str_param, modes[0], args[0], *Main_Data::game_variables).substr(args[1], args[2])); break; case 10: //Join (join) { - std::string op_string = ""; - std::string delimiter = (std::string)Main_Data::game_strings->GetWithMode(str_param, args[0], modes[0]); + std::string op_string; + std::string delimiter = ToString(Main_Data::game_strings->GetWithMode(str_param, modes[0], args[0], *Main_Data::game_variables)); // args[1] & mode[1] relates to starting ID for strings to join // mode 0 = id literal, 1 = direct var, 2 = var literal, 3 = direct var @@ -4849,7 +4850,7 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& while (args[2] > 0) { if (modes[1] < 2) { - op_string += Main_Data::game_strings->Get(args[1]++); + op_string += ToString(Main_Data::game_strings->Get(args[1]++)); } else { op_string += std::to_string(Main_Data::game_variables->Get(args[1]++)); } @@ -4863,7 +4864,7 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& case 12: //File (file) { // maniacs does not like a file extension - Game_Strings::Str_t filename = Main_Data::game_strings->GetWithMode(str_param, args[0], modes[0]); + StringView filename = Main_Data::game_strings->GetWithMode(str_param, modes[0], args[0], *Main_Data::game_variables); // args[1] is the encoding... 0 for ansi, 1 for utf8 bool do_yield; result = Game_Strings::FromFile(filename, args[1], do_yield); @@ -4879,16 +4880,17 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& case 13: //Remove (rem) args[1] = ValueOrVariable(modes[1], args[1]); args[2] = ValueOrVariable(modes[2], args[2]); - result = Main_Data::game_strings->GetWithMode(str_param, args[0], modes[0]).erase(args[1], args[2]); + result = ToString(Main_Data::game_strings->GetWithMode(str_param, modes[0], args[0], *Main_Data::game_variables)); + result = result.erase(args[1], args[2]); break; case 14: //Replace Ex (exRep) , edge case: the arg "first" is at ((flags >> 19) & 1). Wtf BingShan { int pos = 0; std::string base, search, replacement; - base = Main_Data::game_strings->GetWithModeAndPos(str_param, args[0], modes[0], &pos); - search = Main_Data::game_strings->GetWithModeAndPos(str_param, args[1], modes[1], &pos); - replacement = Main_Data::game_strings->GetWithModeAndPos(str_param, args[2], modes[2], &pos); + base = ToString(Main_Data::game_strings->GetWithModeAndPos(str_param, modes[0], args[0], &pos, *Main_Data::game_variables)); + search = ToString(Main_Data::game_strings->GetWithModeAndPos(str_param, modes[1], args[1], &pos, *Main_Data::game_variables)); + replacement = ToString(Main_Data::game_strings->GetWithModeAndPos(str_param, modes[2], args[2], &pos, *Main_Data::game_variables)); std::regex rexp(search); @@ -4900,7 +4902,7 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& Output::Warning("Unknown or unimplemented string sub-operation {}", op); break; } - if (is_range) Main_Data::game_strings->RangeOp(str_params, string_id_1, result, op); + if (is_range) Main_Data::game_strings->RangeOp(str_params, string_id_1, result, op, nullptr, *Main_Data::game_variables); else { if (op == 0) Main_Data::game_strings->Asg(str_params, result); if (op == 1) Main_Data::game_strings->Cat(str_params, result); @@ -4908,38 +4910,41 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& break; case 2: //toNum takes hex case 3: //getLen - if (is_range) Main_Data::game_strings->RangeOp(str_params, string_id_1, result, op, args); + if (is_range) Main_Data::game_strings->RangeOp(str_params, string_id_1, result, op, args, *Main_Data::game_variables); else { - if (op == 2) Main_Data::game_strings->ToNum(str_params, args[0]); - if (op == 3) Main_Data::game_strings->GetLen(str_params, args[0]); + if (op == 2) { + int num = Main_Data::game_strings->ToNum(str_params, args[0], *Main_Data::game_variables); + Main_Data::game_variables->Set(args[0], num); + } + if (op == 3) Main_Data::game_strings->GetLen(str_params, args[0], *Main_Data::game_variables); } break; case 4: //inStr FIXME: takes hex? { - Game_Strings::Str_t search = Main_Data::game_strings->GetWithMode(str_param, args[0], modes[0]); + StringView search = Main_Data::game_strings->GetWithMode(str_param, modes[0], args[0], *Main_Data::game_variables); args[1] = ValueOrVariable(modes[1], args[1]); // not sure this is necessary but better safe args[2] = ValueOrVariable(modes[2], args[2]); - if (is_range) Main_Data::game_strings->RangeOp(str_params, string_id_1, search, op, args); - else Main_Data::game_strings->InStr(str_params, search, args[1], args[2]); + if (is_range) Main_Data::game_strings->RangeOp(str_params, string_id_1, ToString(search), op, args, *Main_Data::game_variables); + else Main_Data::game_strings->InStr(str_params, ToString(search), args[1], args[2], *Main_Data::game_variables); break; } case 5: //split takes hex { - Game_Strings::Str_t delimiter = Main_Data::game_strings->GetWithMode(str_param, args[0], modes[0]); + StringView delimiter = Main_Data::game_strings->GetWithMode(str_param, modes[0], args[0], *Main_Data::game_variables); args[1] = ValueOrVariable(modes[1], args[1]); args[2] = ValueOrVariable(modes[2], args[2]); - if (is_range) Main_Data::game_strings->RangeOp(str_params, string_id_1, delimiter, op, args); - else Main_Data::game_strings->Split(str_params, delimiter, args[1], args[2]); + if (is_range) Main_Data::game_strings->RangeOp(str_params, string_id_1, ToString(delimiter), op, args, *Main_Data::game_variables); + else Main_Data::game_strings->Split(str_params, ToString(delimiter), args[1], args[2], *Main_Data::game_variables); break; } case 7: //toFile takes hex { - std::string filename = Main_Data::game_strings->GetWithMode(str_param, args[0], modes[0]); + StringView filename = Main_Data::game_strings->GetWithMode(str_param, modes[0], args[0], *Main_Data::game_variables); args[1] = ValueOrVariable(modes[1], args[1]); - Main_Data::game_strings->ToFile(str_params, filename, args[1]); + Main_Data::game_strings->ToFile(str_params, ToString(filename), args[1]); break; } case 8: //popLine takes hex @@ -4956,16 +4961,16 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& case 10: //exMatch , edge case: the only command that generates 8 parameters instead of 7 { // takes hex - std::string expr = Main_Data::game_strings->GetWithMode(str_param, args[0], modes[0]); + std::string expr = ToString(Main_Data::game_strings->GetWithMode(str_param, modes[0], args[0], *Main_Data::game_variables)); args[1] = ValueOrVariable(modes[1], args[1]); // output var args[2] = ValueOrVariable(modes[2], args[2]); // beginning pos if (is_range) { - Main_Data::game_strings->RangeOp(str_params, string_id_1, expr, op, args); + Main_Data::game_strings->RangeOp(str_params, string_id_1, expr, op, args, *Main_Data::game_variables); } else { - if (op == 9) Main_Data::game_strings->ExMatch(str_params, expr, args[1], args[2]); - else Main_Data::game_strings->ExMatch(str_params, expr, args[1], args[2], args[3]); + if (op == 9) Main_Data::game_strings->ExMatch(str_params, expr, args[1], args[2], -1, *Main_Data::game_variables); + else Main_Data::game_strings->ExMatch(str_params, expr, args[1], args[2], args[3], *Main_Data::game_variables); } break; } diff --git a/src/game_message.cpp b/src/game_message.cpp index 95b697f7a9..cfb9edd653 100644 --- a/src/game_message.cpp +++ b/src/game_message.cpp @@ -161,7 +161,7 @@ static std::optional CommandCodeInserterNoRecurse(char ch, const ch *iter = parse_ret.next; int value = parse_ret.value; - std::string str = Main_Data::game_strings->Get(value); + std::string str = ToString(Main_Data::game_strings->Get(value)); // \t[] is evaluated but command codes inside it are not evaluated again return PendingMessage::ApplyTextInsertingCommands(str, escape_char, PendingMessage::DefaultCommandInserter); @@ -176,7 +176,7 @@ std::optional Game_Message::CommandCodeInserter(char ch, const char *iter = parse_ret.next; int value = parse_ret.value; - std::string str = Main_Data::game_strings->Get(value); + std::string str = ToString(Main_Data::game_strings->Get(value)); // Command codes in \t[] are evaluated once. return PendingMessage::ApplyTextInsertingCommands(str, escape_char, CommandCodeInserterNoRecurse); diff --git a/src/game_strings.cpp b/src/game_strings.cpp index 913a596a65..7f2d1e87dc 100644 --- a/src/game_strings.cpp +++ b/src/game_strings.cpp @@ -19,6 +19,7 @@ #include #include #include "async_handler.h" +#include "game_map.h" #include "game_message.h" #include "game_strings.h" #include "game_switches.h" @@ -30,67 +31,85 @@ void Game_Strings::WarnGet(int id) const { --_warnings; } -Game_Strings::Str_t Game_Strings::Asg(Str_Params params, Str_t string) { - return Set(params, string); +StringView Game_Strings::Asg(Str_Params params, StringView string) { + Set(params, string); + return Get(params.string_id); } -Game_Strings::Str_t Game_Strings::Cat(Str_Params params, Str_t string) { - if (!ResizeWithId(params.string_id)) return ""; +StringView Game_Strings::Cat(Str_Params params, StringView string) { + if (params.string_id <= 0) { + return {}; + } - Str_t s = Get(params.string_id); - std::string op_string = s; - op_string.append(string); - Set(params, op_string); - return s; + auto it = _strings.find(params.string_id); + if (it == _strings.end()) { + Set(params, string); + return Get(params.string_id); + } + it->second += ToString(string); + return it->second; } -int Game_Strings::ToNum(Str_Params params, int var_id) { - if (!ResizeWithId(params.string_id)) return -1; - std::string str = Get(params.string_id); +int Game_Strings::ToNum(Str_Params params, int var_id, Game_Variables& variables) { + if (params.string_id <= 0) { + return -1; + } + + auto it = _strings.find(params.string_id); + if (it == _strings.end()) { + return 0; + } int num; if (params.hex) - num = static_cast(std::strtol(str.c_str(), nullptr, 16)); + num = static_cast(std::strtol(it->second.c_str(), nullptr, 16)); else - num = static_cast(std::strtol(str.c_str(), nullptr, 0)); + num = static_cast(std::strtol(it->second.c_str(), nullptr, 0)); - Main_Data::game_variables->Set(var_id, num); + variables.Set(var_id, num); + Game_Map::SetNeedRefresh(true); return num; } -int Game_Strings::GetLen(Str_Params params, int var_id) { +int Game_Strings::GetLen(Str_Params params, int var_id, Game_Variables& variables) const { // Note: The length differs between Maniac and EasyRPG due to different internal encoding (utf-8 vs. ansi) - if (!ResizeWithId(params.string_id)) return -1; + if (params.string_id <= 0) { + return -1; + } int len = Get(params.string_id).length(); - Main_Data::game_variables->Set(var_id, len); + variables.Set(var_id, len); + Game_Map::SetNeedRefresh(true); return len; } -int Game_Strings::InStr(Str_Params params, std::string search, int var_id, int begin) { - if (!ResizeWithId(params.string_id)) return -1; +int Game_Strings::InStr(Str_Params params, std::string search, int var_id, int begin, Game_Variables& variables) const { + if (params.string_id <= 0) { + return -1; + } if (params.extract) { search = Extract(search, params.hex); } - std::string str = Get(params.string_id); - int index = Get(params.string_id).find(search, begin); - Main_Data::game_variables->Set(var_id, index); + variables.Set(var_id, index); + Game_Map::SetNeedRefresh(true); return index; } -int Game_Strings::Split(Str_Params params, std::string delimiter, int string_out_id, int var_id) { - if (!ResizeWithId(params.string_id)) return -1; +int Game_Strings::Split(Str_Params params, const std::string& delimiter, int string_out_id, int var_id, Game_Variables& variables) { + if (params.string_id <= 0) { + return -1; + } size_t index; std::string token; // always returns at least 1 int splits = 1; - std::string str = Get(params.string_id); + std::string str = ToString(Get(params.string_id)); params.string_id = string_out_id; @@ -104,11 +123,11 @@ int Game_Strings::Split(Str_Params params, std::string delimiter, int string_out // set the remaining string Set(params, str); - Main_Data::game_variables->Set(var_id, splits); + variables.Set(var_id, splits); return splits; } -Game_Strings::Str_t Game_Strings::FromFile(StringView filename, int encoding, bool& do_yield) { +std::string Game_Strings::FromFile(StringView filename, int encoding, bool& do_yield) { do_yield = false; Filesystem_Stream::InputStream is = FileFinder::OpenText(filename); @@ -123,7 +142,7 @@ Game_Strings::Str_t Game_Strings::FromFile(StringView filename, int encoding, bo } auto vec = Utils::ReadStream(is); - Str_t file_content(vec.begin(), vec.end()); + std::string file_content(vec.begin(), vec.end()); if (encoding == 0) { lcf::Encoder enc(Player::encoding); @@ -133,8 +152,8 @@ Game_Strings::Str_t Game_Strings::FromFile(StringView filename, int encoding, bo return file_content; } -Game_Strings::Str_t Game_Strings::ToFile(Str_Params params, std::string filename, int encoding) { - std::string str = Get(params.string_id); +StringView Game_Strings::ToFile(Str_Params params, std::string filename, int encoding) { + std::string str = ToString(Get(params.string_id)); if (params.extract) { filename = Extract(filename, params.hex); @@ -171,41 +190,46 @@ Game_Strings::Str_t Game_Strings::ToFile(Str_Params params, std::string filename return str; } -Game_Strings::Str_t Game_Strings::PopLine(Str_Params params, int offset, int string_out_id) { +StringView Game_Strings::PopLine(Str_Params params, int offset, int string_out_id) { // FIXME: consideration needed around encoding -- what mode are files read in? - if (!ResizeWithId(params.string_id)) return ""; + if (params.string_id <= 0) { + return {}; + } int index; std::string result; - std::string str = Get(params.string_id); + StringView str = Get(params.string_id); - std::stringstream ss(str); + std::stringstream ss(ToString(str)); while (offset >= 0 && Utils::ReadLine(ss, result)) { offset--; } offset = ss.rdbuf()->in_avail(); Set(params, ss.str().substr(str.length() - offset)); + params.string_id = string_out_id; - return Set(params, result); + Set(params, result); + return Get(params.string_id); } -Game_Strings::Str_t Game_Strings::ExMatch(Str_Params params, std::string expr, int var_id, int begin, int string_out_id) { +StringView Game_Strings::ExMatch(Str_Params params, std::string expr, int var_id, int begin, int string_out_id, Game_Variables& variables) { int var_result; - Str_t str_result; + std::string str_result; std::smatch match; if (params.extract) { expr = Extract(expr, params.hex); } - std::string base = Get(params.string_id).erase(0, begin); + std::string base = ToString(Get(params.string_id)).erase(0, begin); std::regex r(expr); std::regex_search(base, match, r); var_result = match.position() + begin; - Main_Data::game_variables->Set(var_id, var_result); + variables.Set(var_id, var_result); + Game_Map::SetNeedRefresh(true); str_result = match.str(); if (string_out_id > 0) { @@ -215,7 +239,7 @@ Game_Strings::Str_t Game_Strings::ExMatch(Str_Params params, std::string expr, i return str_result; } -const Game_Strings::Strings_t& Game_Strings::RangeOp(Str_Params params, int string_id_1, Str_t string, int op, int args[]) { +const Game_Strings::Strings_t& Game_Strings::RangeOp(Str_Params params, int string_id_1, std::string string, int op, int args[], Game_Variables& variables) { if (EP_UNLIKELY(ShouldWarn(params.string_id))) { WarnGet(params.string_id); } @@ -233,36 +257,31 @@ const Game_Strings::Strings_t& Game_Strings::RangeOp(Str_Params params, int stri std::swap(params.string_id, string_id_1); } - if (EP_UNLIKELY(string_id_1 > static_cast(_strings.size()))) { - _strings.resize(string_id_1, ""); - } - for (int start = params.string_id; params.string_id <= string_id_1; params.string_id++) { switch (op) { case 0: Asg(params, string); break; case 1: Cat(params, string); break; - case 2: ToNum(params, args[0] + (params.string_id - start)); break; - case 3: GetLen(params, args[0] + (params.string_id - start)); break; - case 4: InStr(params, string, args[1], args[2]); break; - case 5: params.string_id += Split(params, string, args[1], args[2]); break; + case 2: ToNum(params, args[0] + (params.string_id - start), variables); break; + case 3: GetLen(params, args[0] + (params.string_id - start), variables); break; + case 4: InStr(params, string, args[1], args[2], variables); break; + case 5: params.string_id += Split(params, string, args[1], args[2], variables); break; case 8: break; // range case not applicable for popLine; see case in game_interpreter.cpp - case 9: ExMatch(params, string, args[1] + (params.string_id - start), args[2]); break; - case 10: ExMatch(params, string, args[1] + (params.string_id - start), args[2], args[3]); break; + case 9: ExMatch(params, string, args[1] + (params.string_id - start), args[2], -1, variables); break; + case 10: ExMatch(params, string, args[1] + (params.string_id - start), args[2], args[3], variables); break; } } return GetData(); } -Game_Strings::Str_t Game_Strings::PrependMin(Str_t string, int min_size, char c) { +std::string Game_Strings::PrependMin(StringView string, int min_size, char c) { if (string.size() < min_size) { int s = min_size - string.size(); - std::string res = std::string(s, c) + (std::string)string; - return res; + return std::string(s, c) + ToString(string); } - return string; + return ToString(string); } -Game_Strings::Str_t Game_Strings::Extract(Str_t string, bool as_hex) { +std::string Game_Strings::Extract(StringView string, bool as_hex) { PendingMessage::CommandInserter cmd_fn; if (as_hex) { @@ -271,7 +290,7 @@ Game_Strings::Str_t Game_Strings::Extract(Str_t string, bool as_hex) { cmd_fn = ManiacsCommandInserter; } - return static_cast(PendingMessage::ApplyTextInsertingCommands(string, Player::escape_char, cmd_fn)); + return PendingMessage::ApplyTextInsertingCommands(ToString(string), Player::escape_char, cmd_fn); } std::optional Game_Strings::ManiacsCommandInserter(char ch, const char** iter, const char* end, uint32_t escape_char) { @@ -289,7 +308,7 @@ std::optional Game_Strings::ManiacsCommandInserter(char ch, const c int value = parse_ret.value; // Contrary to Messages, the content of \t[]-strings is not evaluated - return Main_Data::game_strings->Get(value); + return ToString(Main_Data::game_strings->Get(value)); } return Game_Message::CommandCodeInserter(ch, iter, end, escape_char); diff --git a/src/game_strings.h b/src/game_strings.h index 06af837e98..38a20c6532 100644 --- a/src/game_strings.h +++ b/src/game_strings.h @@ -32,143 +32,154 @@ */ class Game_Strings { public: - using Str_t = std::string; - using Strings_t = std::vector; + using Strings_t = std::unordered_map; - // Warnings disabled for now as there is no way to predefine text strings in the database - static constexpr int max_warnings = 0; + // currently only warns when ID <= 0 + static constexpr int max_warnings = 10; struct Str_Params { - int string_id, hex, extract; + int string_id = 0, hex = 0, extract = 0; }; Game_Strings() = default; void SetData(Strings_t s); - void SetData(std::vector s); + void SetData(const std::vector& s); const Strings_t& GetData() const; - std::vector GetLcfData(); + std::vector GetLcfData() const; - Str_t Get(int id) const; - Str_t GetIndirect(int id) const; - Str_t GetWithMode(Str_t str_data, int mode, int arg) const; - Str_t GetWithModeAndPos(Str_t str_data, int mode, int arg, int* pos); + StringView Get(int id) const; + StringView GetIndirect(int id, const Game_Variables& variables) const; + StringView GetWithMode(StringView str_data, int mode, int arg, const Game_Variables& variables) const; + StringView GetWithModeAndPos(StringView str_data, int mode, int arg, int* pos, const Game_Variables& variables); - Str_t Asg(Str_Params params, Str_t string); - Str_t Cat(Str_Params params, Str_t string); - int ToNum(Str_Params params, int var_id); - int GetLen(Str_Params params, int var_id); - int InStr(Str_Params params, std::string search, int var_id, int begin = 0); - int Split(Str_Params params, std::string delimiter, int string_out_id, int var_id); - static Str_t FromFile(StringView filename, int encoding, bool& do_yield); - Str_t ToFile(Str_Params params, std::string filename, int encoding); - Str_t PopLine(Str_Params params, int offset, int string_out_id); - Str_t ExMatch(Str_Params params, std::string expr, int var_id, int begin, int string_out_id = -1); + StringView Asg(Str_Params params, StringView string); + StringView Cat(Str_Params params, StringView string); + int ToNum(Str_Params params, int var_id, Game_Variables& variables); + int GetLen(Str_Params params, int var_id, Game_Variables& variables) const; + int InStr(Str_Params params, std::string search, int var_id, int begin, Game_Variables& variables) const; + int Split(Str_Params params, const std::string& delimiter, int string_out_id, int var_id, Game_Variables& variables); + static std::string FromFile(StringView filename, int encoding, bool& do_yield); + StringView ToFile(Str_Params params, std::string filename, int encoding); + StringView PopLine(Str_Params params, int offset, int string_out_id); + StringView ExMatch(Str_Params params, std::string expr, int var_id, int begin, int string_out_id, Game_Variables& variables); - const Strings_t& RangeOp(Str_Params params, int string_id_1, Str_t string, int op, int args[] = nullptr); + const Strings_t& RangeOp(Str_Params params, int string_id_1, std::string string, int op, int args[], Game_Variables& variables); - Str_t PrependMin(Str_t string, int min_size, char c); - Str_t Extract(Str_t string, bool as_hex); + static std::string PrependMin(StringView string, int min_size, char c); + static std::string Extract(StringView string, bool as_hex); static std::optional ManiacsCommandInserter(char ch, const char** iter, const char* end, uint32_t escape_char); static std::optional ManiacsCommandInserterHex(char ch, const char** iter, const char* end, uint32_t escape_char); private: - Str_t Set(Str_Params params, Str_t string); - bool ResizeWithId(int id); + void Set(Str_Params params, StringView string); bool ShouldWarn(int id) const; void WarnGet(int id) const; -private: Strings_t _strings; mutable int _warnings = max_warnings; }; -inline Game_Strings::Str_t Game_Strings::Set(Str_Params params, Str_t string) { - if (!ResizeWithId(params.string_id)) return ""; +inline void Game_Strings::Set(Str_Params params, StringView string) { + if (params.string_id <= 0) { + return; + } - auto& s = _strings[params.string_id - 1]; - s = string; + std::string ins_string = ToString(string); if (params.extract) { - s = Extract(s, params.hex); + ins_string = Extract(ins_string, params.hex); + } + + auto it = _strings.find(params.string_id); + if (it == _strings.end()) { + if (ins_string.empty()) { + return; + } else { + _strings[params.string_id] = ins_string; + } + } else { + it->second = ins_string; } - return s; } inline void Game_Strings::SetData(Strings_t s) { _strings = std::move(s); } -inline void Game_Strings::SetData(std::vector s) { - _strings = std::vector(s.begin(), s.end()); +inline void Game_Strings::SetData(const std::vector& s) { + int i = 1; + for (const auto& string: s) { + if (!s.empty()) { + _strings[i] = ToString(string); + } + ++i; + } } inline const Game_Strings::Strings_t& Game_Strings::GetData() const { return _strings; } -inline std::vector Game_Strings::GetLcfData() { - return std::vector(_strings.begin(), _strings.end()); +inline std::vector Game_Strings::GetLcfData() const { + std::vector lcf_data; + + for (auto& [index, value]: _strings) { + assert(index > 0); + if (index >= lcf_data.size()) { + lcf_data.resize(index + 1); + } + lcf_data[index - 1] = lcf::DBString(value); + } + + return lcf_data; } inline bool Game_Strings::ShouldWarn(int id) const { return id <= 0 && _warnings > 0; } -inline Game_Strings::Str_t Game_Strings::Get(int id) const { +inline StringView Game_Strings::Get(int id) const { if (EP_UNLIKELY(ShouldWarn(id))) { WarnGet(id); } - if (id <= 0 || id > static_cast(_strings.size())) { - return ""; + auto it = _strings.find(id); + if (it == _strings.end()) { + return {}; } - return _strings[id - 1]; + return it->second; } -inline bool Game_Strings::ResizeWithId(int id) { - if (EP_UNLIKELY(ShouldWarn(id))) { - WarnGet(id); - } - if (id <= 0) { - return false; - } - if (EP_UNLIKELY(id > static_cast(_strings.size()))) { - _strings.resize(id, ""); - } - return true; -} - -inline Game_Strings::Str_t Game_Strings::GetIndirect(int id) const { - auto val_indirect = Main_Data::game_variables->Get(id); +inline StringView Game_Strings::GetIndirect(int id, const Game_Variables& variables) const { + auto val_indirect = variables.Get(id); return Get(static_cast(val_indirect)); } -inline Game_Strings::Str_t Game_Strings::GetWithMode(Str_t str_data, int arg, int mode) const { +inline StringView Game_Strings::GetWithMode(StringView str_data, int mode, int arg, const Game_Variables& variables) const { switch (mode) { case 1: // direct string reference return Get(arg); - break; case 2: // indirect string reference - return GetIndirect(arg); - break; + return GetIndirect(arg, variables); + default: + return str_data; } - return str_data; } -inline Game_Strings::Str_t Game_Strings::GetWithModeAndPos(Str_t str_data, int arg, int mode, int* pos) { - Str_t ret; +inline StringView Game_Strings::GetWithModeAndPos(StringView str_data, int mode, int arg, int* pos, const Game_Variables& variables) { + StringView ret; switch (mode) { case 0: + assert(pos); ret = str_data.substr(*pos, arg); *pos += arg; - break; + return ret; case 1: // direct string reference - ret = Get(arg); - break; + return Get(arg); case 2: // indirect string reference - ret = GetIndirect(arg); - break; + return GetIndirect(arg, variables); + default: + return ret; } - return ret; } diff --git a/src/game_windows.cpp b/src/game_windows.cpp index 4f80a20b06..4ca722385a 100644 --- a/src/game_windows.cpp +++ b/src/game_windows.cpp @@ -34,7 +34,7 @@ static std::optional CommandCodeInserter(char ch, const char **iter int value = parse_ret.value; // Contrary to Messages, the content of \t[]-strings is not evaluated - return Main_Data::game_strings->Get(value); + return ToString(Main_Data::game_strings->Get(value)); } return PendingMessage::DefaultCommandInserter(ch, iter, end, escape_char); diff --git a/src/maniac_patch.cpp b/src/maniac_patch.cpp index b4ed45d436..7e60fb3d4c 100644 --- a/src/maniac_patch.cpp +++ b/src/maniac_patch.cpp @@ -610,34 +610,39 @@ bool ManiacPatch::GetKeyState(uint32_t key_id) { } -bool ManiacPatch::CheckString(std::string str_l, std::string str_r, int op, bool ignore_case) { +bool ManiacPatch::CheckString(StringView str_l, StringView str_r, int op, bool ignore_case) { + auto check = [op](const auto& l, const auto& r) { + switch (op) { + case 0: // eq + return l == r; + case 2: // contains (l contains r) + return l.find(r) != std::string::npos; + case 1: // neq + return l != r; + case 3: // notContains (l does not contain r) + return l.find(r) == std::string::npos; + default: + return false; + } + }; + if (ignore_case) { - str_l = Utils::LowerCase(str_l); - str_r = Utils::LowerCase(str_r); + std::string str_l_lower = Utils::LowerCase(str_l); + std::string str_r_lower = Utils::LowerCase(str_r); + return check(str_l_lower, str_r_lower); } - switch (op) { - case 0: // eq - return str_l == str_r; - case 2: // contains (l contains r) - return str_l.find(str_r) != std::string::npos; - case 1: // neq - return str_l != str_r; - case 3: // notContains (l does not contain r) - return str_l.find(str_r) == std::string::npos; - default: - return false; - } + return check(str_l, str_r); } -Game_Strings::Str_t ManiacPatch::GetLcfName(int data_type, int id, bool is_dynamic) { - auto get_name = [id](StringView type, const auto& vec) -> std::string { +StringView ManiacPatch::GetLcfName(int data_type, int id, bool is_dynamic) { + auto get_name = [id](StringView type, const auto& vec) -> StringView { auto* data = lcf::ReaderUtil::GetElement(vec, id); if (!data) { Output::Warning("Unable to read {} name: {}", type, id); return {}; } - return ToString(data->name); + return data->name; }; switch (data_type) @@ -646,7 +651,7 @@ Game_Strings::Str_t ManiacPatch::GetLcfName(int data_type, int id, bool is_dynam if (is_dynamic) { auto actor = Main_Data::game_actors->GetActor(id); if (actor != nullptr) { - return static_cast(actor->GetName()); + return actor->GetName(); } } else { @@ -662,25 +667,25 @@ Game_Strings::Str_t ManiacPatch::GetLcfName(int data_type, int id, bool is_dynam case 7: return get_name("State", lcf::Data::states); //.state[a].name case 8: return get_name("Animation", lcf::Data::animations); //.anim[a].name case 9: return get_name("Chipset", lcf::Data::chipsets); //.tileset[a].name - case 10: return static_cast(Main_Data::game_switches->GetName(id)); //.s[a].name - case 11: return static_cast(Main_Data::game_variables->GetName(id)); //.v[a].name + case 10: return Main_Data::game_switches->GetName(id); //.s[a].name + case 11: return Main_Data::game_variables->GetName(id); //.v[a].name case 12: return {}; // FIXME: .t[a].name -- not sure how to get this for now case 13: //.cev[a].name { // assuming the vector of common events here is ordered by common event ID if (Game_Map::GetCommonEvents().size() >= id) { - return (Game_Strings::Str_t)Game_Map::GetCommonEvents()[id - 1].GetName(); + return Game_Map::GetCommonEvents()[id - 1].GetName(); } break; } case 14: return get_name("Class", lcf::Data::classes); //.class[a].name case 15: return get_name("BattlerAnimation", lcf::Data::battleranimations); //.anim2[a].name - case 16: return static_cast(Game_Map::GetMapName(id)); //.map[a].name + case 16: return Game_Map::GetMapName(id); //.map[a].name case 17: //.mev[a].name { auto map = Game_Map::GetEvent(id); if (map != nullptr) { - return static_cast(map->GetName()); + return map->GetName(); } break; } @@ -689,7 +694,7 @@ Game_Strings::Str_t ManiacPatch::GetLcfName(int data_type, int id, bool is_dynam auto actor = Main_Data::game_party->GetActor(id); if (actor != nullptr) { if (is_dynamic) { - return static_cast(actor->GetName()); + return actor->GetName(); } else { id = actor->GetId(); @@ -704,17 +709,17 @@ Game_Strings::Str_t ManiacPatch::GetLcfName(int data_type, int id, bool is_dynam return {}; } -Game_Strings::Str_t ManiacPatch::GetLcfDescription(int data_type, int id, bool is_dynamic) { - auto get_desc = [id](StringView type, const auto& vec) -> std::string { +StringView ManiacPatch::GetLcfDescription(int data_type, int id, bool is_dynamic) { + auto get_desc = [id](StringView type, const auto& vec) -> StringView { auto* data = lcf::ReaderUtil::GetElement(vec, id); if (!data) { Output::Warning("Unable to read {} description: {}", type, id); return {}; } if constexpr (std::is_same_v::value_type, lcf::rpg::Actor>) { - return ToString(data->title); + return data->title; } else { - return ToString(data->description); + return data->description; } }; @@ -724,7 +729,7 @@ Game_Strings::Str_t ManiacPatch::GetLcfDescription(int data_type, int id, bool i if (is_dynamic) { auto actor = Main_Data::game_actors->GetActor(id); if (actor != nullptr) { - return static_cast(actor->GetTitle()); + return actor->GetTitle(); } } else { @@ -738,7 +743,7 @@ Game_Strings::Str_t ManiacPatch::GetLcfDescription(int data_type, int id, bool i auto actor = Main_Data::game_party->GetActor(id); if (actor != nullptr) { if (is_dynamic) { - return static_cast(actor->GetTitle()); + return actor->GetTitle(); } else { id = actor->GetId(); @@ -750,5 +755,5 @@ Game_Strings::Str_t ManiacPatch::GetLcfDescription(int data_type, int id, bool i } Output::Warning("GetLcfDescription: Unsupported data_type {} {}", data_type, id); - return ""; + return {}; } diff --git a/src/maniac_patch.h b/src/maniac_patch.h index 12f78df169..179b19c28f 100644 --- a/src/maniac_patch.h +++ b/src/maniac_patch.h @@ -34,10 +34,10 @@ namespace ManiacPatch { bool GetKeyState(uint32_t key_id); - bool CheckString(std::string str_l, std::string str_r, int op, bool ignore_case); + bool CheckString(StringView str_l, StringView str_r, int op, bool ignore_case); - Game_Strings::Str_t GetLcfName(int data_type, int id, bool is_dynamic); - Game_Strings::Str_t GetLcfDescription(int data_type, int id, bool is_dynamic); + StringView GetLcfName(int data_type, int id, bool is_dynamic); + StringView GetLcfDescription(int data_type, int id, bool is_dynamic); } #endif