From 86d64076fe3cb180c8dccea5d36a98fab11a4e39 Mon Sep 17 00:00:00 2001 From: Jorge Augusto Date: Wed, 26 Apr 2023 00:21:30 -0300 Subject: [PATCH 01/47] StringVar: Basic implementation -Simple assign and concatenation operations already work -Added the escape \t[x] for messages -Complete documentation of the inner workings of the command made by BingShan --- CMakeLists.txt | 2 + Makefile.am | 2 + src/game_interpreter.cpp | 142 +++++++++++++++++++++++++++++++++++++++ src/game_interpreter.h | 1 + src/game_message.cpp | 4 ++ src/game_message.h | 12 ++++ src/game_strings.cpp | 61 +++++++++++++++++ src/game_strings.h | 83 +++++++++++++++++++++++ src/main_data.cpp | 2 + src/main_data.h | 2 + src/pending_message.cpp | 10 +++ src/player.cpp | 4 ++ src/scene_save.cpp | 3 +- 13 files changed, 327 insertions(+), 1 deletion(-) create mode 100644 src/game_strings.cpp create mode 100644 src/game_strings.h diff --git a/CMakeLists.txt b/CMakeLists.txt index cac05d3c57..edb60d5de5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -190,6 +190,8 @@ add_library(${PROJECT_NAME} OBJECT src/game_quit.h src/game_screen.cpp src/game_screen.h + src/game_strings.cpp + src/game_strings.h src/game_switches.cpp src/game_switches.h src/game_system.cpp diff --git a/Makefile.am b/Makefile.am index 0edc1f0704..2c2c339015 100644 --- a/Makefile.am +++ b/Makefile.am @@ -169,6 +169,8 @@ libeasyrpg_player_a_SOURCES = \ src/game_player.h \ src/game_screen.cpp \ src/game_screen.h \ + src/game_strings.cpp \ + src/game_strings.h \ src/game_switches.cpp \ src/game_switches.h \ src/game_system.cpp \ diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 353d66fa9f..e4a8573be6 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -34,6 +34,7 @@ #include "game_targets.h" #include "game_switches.h" #include "game_variables.h" +#include "game_strings.h" #include "game_party.h" #include "game_actors.h" #include "game_system.h" @@ -819,6 +820,8 @@ bool Game_Interpreter::ExecuteCommand(lcf::rpg::EventCommand const& com) { return CommandManiacChangePictureId(com); case Cmd::Maniac_SetGameOption: return CommandManiacSetGameOption(com); + case 3020: //Cmd::Maniac_ControlStrings + return CommandManiacControlStrings(com); case Cmd::Maniac_CallCommand: return CommandManiacCallCommand(com); default: @@ -4634,6 +4637,145 @@ bool Game_Interpreter::CommandManiacSetGameOption(lcf::rpg::EventCommand const&) return true; } +bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& com) { + if (!Player::IsPatchManiac()) { + return true; + } + //*parameter 0 - Modifier of the operation members + // -bits 0..3: + // Refers to the String mode: t[x], t[x..y], t[v[x]], t[v[x]..v[y]] + // -bits 4..7: + // Refers to the first argument, which can change depending on the type of operation: x, v[x], v[v[x]] | string, t[x], t[v[x]] + // -bits 8..11: + // Refers to the second argument, which can change depending on the type of operation: x, v[x], v[v[x]] | string, t[x], t[v[x]] + // -bits 12..15: + // Refers to the third argument, which can change depending on the type of operation: x, v[x], v[v[x]] | string, t[x], t[v[x]] + // -bits 16..19: + // Refers to the fourth argument, which can change depending on the type of operation: x, v[x], v[v[x]] | string, t[x], t[v[x]] (edge case for exMatch) + // + //*parameter 1 - String index 0 + // + //*parameter 2 - String index 1 (optional for ranges) + // + //*parameter 3 - general flags + // -byte0: + // Refers to the type of operation: asg, cat, toNum... + // -byte1: + // It is a flag that indicates sub-operation: actor[x].name, .actor[x].desc, ins, rep, subs, join... Used only in asg and cat operations + // -byte2: + // Flags, such as: extract, hex... There is also an edge case where the last argument of exRep is here + // + //*parameters 4..n - arguments + int string_mode = com.parameters[0] & 15; + int string_id_0 = com.parameters[1]; + int string_id_1 = com.parameters[2]; //for ranges + + int is_range = string_mode & 1; + + if (string_mode >= 2) { + string_id_0 = Main_Data::game_variables->Get(string_id_0); + } + if (string_mode == 3) { + 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 flags = (com.parameters[3] >> 16) & 255; + + int hex_flag = (flags >> 17) & 1; + int extract_flag = (flags >> 18) & 1; + + int args[] = { + com.parameters[4], + com.parameters[5], + com.parameters[6], + 0 + }; + if (com.parameters.size() > 7) { + args[3] = com.parameters[7]; //The exMatch command is a edge case that uses 4 arguments + } + int modes[] = { + (com.parameters[0] >> 4) & 15, + (com.parameters[0] >> 8) & 15, + (com.parameters[0] >> 12) & 15, + (com.parameters[0] >> 16) & 15 + }; + + Game_Strings::Str_t result = ""; + + switch (op) + { + case 0: //asg + case 1: //cat + switch (fn) + { + case 0: //Base + switch (modes[0]) + { + case 0: result = (Game_Strings::Str_t)com.string; break; + case 1: result = Main_Data::game_strings->Get(args[0]); break; + case 2: result = Main_Data::game_strings->GetIndirect(args[0]); break; + } + switch (modes[1]) + { + case 0: break; + case 1: args[1] = Main_Data::game_variables->Get(args[1]); break; + case 2: args[1] = Main_Data::game_variables->GetIndirect(args[1]); break; + } + break; + case 3: //Database Names + break; + case 4: //Database Descriptions + break; + case 7: //Insert (ins) + break; + case 8: //Replace (rep) + break; + case 9: //Substring (subs) + break; + case 10: //Join (join) + break; + case 12: //File (file) + break; + case 13: //Remove (rem) + break; + case 14: //Replace Ex (exRep) , edge case: the arg "first" is at ((flags >> 19) & 1). Wtf BingShan + break; + default: + break; + } + if (op == 0) { + Main_Data::game_strings->Asg(string_id_0, result); + } + else { + Main_Data::game_strings->Cat(string_id_0, result); + } + Output::Debug("t[{}]: {}", string_id_0, Main_Data::game_strings->Get(string_id_0)); + break; + case 2: //toNum + break; + case 3: //getLen + break; + case 4: //inStr + break; + case 5: //split + break; + case 7: //toFile + break; + case 8: //popLine + break; + case 9: //exInStr + break; + case 10: //exMatch , edge case: the only command that generates 8 parameters instead of 7 + break; + default: + Output::Warning("Unknown or unimplemented string operation {}", op); + break; + } + return true; +} + bool Game_Interpreter::CommandManiacCallCommand(lcf::rpg::EventCommand const&) { if (!Player::IsPatchManiac()) { return true; diff --git a/src/game_interpreter.h b/src/game_interpreter.h index a436cbfaed..b77679b966 100644 --- a/src/game_interpreter.h +++ b/src/game_interpreter.h @@ -283,6 +283,7 @@ class Game_Interpreter bool CommandManiacControlGlobalSave(lcf::rpg::EventCommand const& com); bool CommandManiacChangePictureId(lcf::rpg::EventCommand const& com); bool CommandManiacSetGameOption(lcf::rpg::EventCommand const& com); + bool CommandManiacControlStrings(lcf::rpg::EventCommand const& com); bool CommandManiacCallCommand(lcf::rpg::EventCommand const& com); int DecodeInt(lcf::DBArray::const_iterator& it); diff --git a/src/game_message.cpp b/src/game_message.cpp index d2a0245236..849d4cf22c 100644 --- a/src/game_message.cpp +++ b/src/game_message.cpp @@ -323,6 +323,10 @@ Game_Message::ParseParamResult Game_Message::ParseVariable(const char* iter, con return ParseParam('V', 'v', iter, end, escape_char, skip_prefix, max_recursion - 1); } +Game_Message::ParseParamResult Game_Message::ParseString(const char* iter, const char* end, uint32_t escape_char, bool skip_prefix, int max_recursion) { + return ParseParamImpl('T', 't', iter, end, escape_char, skip_prefix, max_recursion); +} + Game_Message::ParseParamResult Game_Message::ParseColor(const char* iter, const char* end, uint32_t escape_char, bool skip_prefix, int max_recursion) { return ParseParam('C', 'c', iter, end, escape_char, skip_prefix, max_recursion); } diff --git a/src/game_message.h b/src/game_message.h index 8c7ad39a1d..83b90041eb 100644 --- a/src/game_message.h +++ b/src/game_message.h @@ -131,6 +131,18 @@ namespace Game_Message { */ ParseParamResult ParseVariable(const char* iter, const char* end, uint32_t escape_char, bool skip_prefix = false, int max_recursion = default_max_recursion); + /** Parse a \t[] variable string + * + * @param iter start of utf8 string + * @param end end of utf8 string + * @param escape_char the escape character to use + * @param skip_prefix if true, assume prefix was already parsed and iter starts at the first left bracket. + * @param max_recursion How many times to allow recursive variable lookups. + * + * @return \refer ParseParamResult + */ + ParseParamResult ParseString(const char* iter, const char* end, uint32_t escape_char, bool skip_prefix = false, int max_recursion = default_max_recursion); + /** Parse a \c[] color string * * @param iter start of utf8 string diff --git a/src/game_strings.cpp b/src/game_strings.cpp new file mode 100644 index 0000000000..ae7c0d5aa7 --- /dev/null +++ b/src/game_strings.cpp @@ -0,0 +1,61 @@ +/* + * This file is part of EasyRPG Player. + * + * EasyRPG Player is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * EasyRPG Player is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with EasyRPG Player. If not, see . + */ + + // Headers +#include "game_strings.h" +#include "game_variables.h" +#include "output.h" + +Game_Strings::Game_Strings() +{} + +void Game_Strings::WarnGet(int id) const { + Output::Debug("Invalid read strvar[{}]!", id); + --_warnings; +} + +Game_Strings::Str_t Game_Strings::Asg(int string_id, Str_t string) { + if (EP_UNLIKELY(ShouldWarn(string_id))) { + WarnGet(string_id); + } + if (string_id <= 0) { + return ""; + } + if (EP_UNLIKELY(string_id > static_cast(_strings.size()))) { + _strings.resize(string_id, ""); + } + auto& s = _strings[string_id - 1]; + s = string; + return s; +} + +Game_Strings::Str_t Game_Strings::Cat(int string_id, Str_t string) { + if (EP_UNLIKELY(ShouldWarn(string_id))) { + WarnGet(string_id); + } + if (string_id <= 0) { + return ""; + } + if (EP_UNLIKELY(string_id > static_cast(_strings.size()))) { + _strings.resize(string_id, ""); + } + auto& s = _strings[string_id - 1]; + std::string op_string = (std::string)s; + op_string.append((std::string)string); + s = (Str_t)op_string; + return s; +} diff --git a/src/game_strings.h b/src/game_strings.h new file mode 100644 index 0000000000..68714829cf --- /dev/null +++ b/src/game_strings.h @@ -0,0 +1,83 @@ +/* + * This file is part of EasyRPG Player. + * + * EasyRPG Player is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * EasyRPG Player is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with EasyRPG Player. If not, see . + */ + + // Headers +#include +#include "compiler.h" +#include "string_view.h" +#include +#include +#include "output.h" +#include "main_data.h" +#include "game_variables.h" + +/** + * Game_Strings class. + */ +class Game_Strings { +public: + using Str_t = lcf::DBString; + using Strings_t = std::vector; + + static constexpr int max_warnings = 10; + + Game_Strings(); + + void SetData(Strings_t s); + const Strings_t& GetData() const; + + Str_t Get(int id) const; + Str_t GetIndirect(int id) const; + + Str_t Asg(int string_id, Str_t string); + Str_t Cat(int string_id, Str_t string); + +private: + bool ShouldWarn(int id) const; + void WarnGet(int id) const; +private: + Strings_t _strings; + mutable int _warnings = max_warnings; +}; + +inline void Game_Strings::SetData(Strings_t s) { + _strings = std::move(s); +} + +inline const Game_Strings::Strings_t& Game_Strings::GetData() const { + return _strings; +} + +inline bool Game_Strings::ShouldWarn(int id) const { + return id <= 0 && _warnings > 0; +} + +inline Game_Strings::Str_t Game_Strings::Get(int id) const { + if (EP_UNLIKELY(ShouldWarn(id))) { + WarnGet(id); + } + if (id <= 0 || id > static_cast(_strings.size())) { + return ""; + } + return _strings[id - 1]; +} + +inline Game_Strings::Str_t Game_Strings::GetIndirect(int id) const { + auto val_indirect = Main_Data::game_variables->Get(id); + return Get(static_cast(val_indirect)); +} + diff --git a/src/main_data.cpp b/src/main_data.cpp index 45ed05effb..8a57dd79ff 100644 --- a/src/main_data.cpp +++ b/src/main_data.cpp @@ -31,6 +31,7 @@ #include "game_pictures.h" #include "game_map.h" #include "game_variables.h" +#include "game_strings.h" #include "game_switches.h" #include "game_targets.h" #include "game_quit.h" @@ -59,6 +60,7 @@ namespace Main_Data { std::unique_ptr game_system; std::unique_ptr game_switches; std::unique_ptr game_variables; + std::unique_ptr game_strings; std::unique_ptr game_screen; std::unique_ptr game_pictures; std::unique_ptr game_windows; diff --git a/src/main_data.h b/src/main_data.h index ffc2326883..e2d199eb96 100644 --- a/src/main_data.h +++ b/src/main_data.h @@ -36,6 +36,7 @@ class Game_Party; class Game_EnemyParty; class Game_Switches; class Game_Variables; +class Game_Strings; class Game_Targets; class Game_Quit; class Game_Ineluki; @@ -46,6 +47,7 @@ namespace Main_Data { extern std::unique_ptr game_system; extern std::unique_ptr game_switches; extern std::unique_ptr game_variables; + extern std::unique_ptr game_strings; extern std::unique_ptr game_screen; extern std::unique_ptr game_pictures; extern std::unique_ptr game_windows; diff --git a/src/pending_message.cpp b/src/pending_message.cpp index bd3dc38971..e3e3968478 100644 --- a/src/pending_message.cpp +++ b/src/pending_message.cpp @@ -17,6 +17,7 @@ #include "pending_message.h" #include "game_variables.h" +#include "game_strings.h" #include "game_actors.h" #include "game_message.h" #include @@ -123,6 +124,15 @@ std::string PendingMessage::ApplyTextInsertingCommands(std::string input, uint32 auto fn_res = cmd_fn(ch, &iter, end, escape_char); if (fn_res) { output.append(*fn_res); + start_copy = iter; + } else if (ch == 'T' || ch == 't') { + auto parse_ret = Game_Message::ParseString(iter, end, escape_char, true); + iter = parse_ret.next; + int value = parse_ret.value; + + Game_Strings::Str_t string = Main_Data::game_strings->Get(value); + output.append((std::string)string); + start_copy = iter; } } diff --git a/src/player.cpp b/src/player.cpp index 6aa0ed70c1..162af8e8f6 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -55,6 +55,7 @@ #include "game_pictures.h" #include "game_system.h" #include "game_variables.h" +#include "game_strings.h" #include "game_targets.h" #include "game_windows.h" #include "graphics.h" @@ -903,6 +904,8 @@ void Player::ResetGameObjects() { Main_Data::game_variables = std::make_unique(min_var, max_var); Main_Data::game_variables->SetLowerLimit(lcf::Data::variables.size()); + Main_Data::game_strings = std::make_unique(); + // Prevent a crash when Game_Map wants to reset the screen content // because Setup() modified pictures array Main_Data::game_screen = std::make_unique(); @@ -1161,6 +1164,7 @@ void Player::LoadSavegame(const std::string& save_name, int save_id) { Main_Data::game_switches->SetData(std::move(save->system.switches)); Main_Data::game_variables->SetLowerLimit(lcf::Data::variables.size()); Main_Data::game_variables->SetData(std::move(save->system.variables)); + Main_Data::game_strings->SetData(std::move(save->system.maniac_strings)); Main_Data::game_system->SetupFromSave(std::move(save->system)); Main_Data::game_actors->SetSaveData(std::move(save->actors)); Main_Data::game_party->SetupFromSave(std::move(save->inventory)); diff --git a/src/scene_save.cpp b/src/scene_save.cpp index 1ad4043bcb..7a1e5eae91 100644 --- a/src/scene_save.cpp +++ b/src/scene_save.cpp @@ -30,6 +30,7 @@ #include "game_party.h" #include "game_switches.h" #include "game_variables.h" +#include "game_strings.h" #include "game_party.h" #include "game_actors.h" #include "game_system.h" @@ -137,9 +138,9 @@ bool Scene_Save::Save(std::ostream& os, int slot_id, bool prepare_save) { save.system = Main_Data::game_system->GetSaveData(); save.system.switches = Main_Data::game_switches->GetData(); save.system.variables = Main_Data::game_variables->GetData(); + save.system.maniac_strings = Main_Data::game_strings->GetData(); save.inventory = Main_Data::game_party->GetSaveData(); save.actors = Main_Data::game_actors->GetSaveData(); - save.screen = Main_Data::game_screen->GetSaveData(); save.pictures = Main_Data::game_pictures->GetSaveData(); save.easyrpg_data.windows = Main_Data::game_windows->GetSaveData(); From 409a4edb9ad9de160155a8c39f865291e8cadaaa Mon Sep 17 00:00:00 2001 From: Jorge Augusto Date: Wed, 26 Apr 2023 01:19:25 -0300 Subject: [PATCH 02/47] StringVar: :Game_Strings: minor update --- src/game_interpreter.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index e4a8573be6..362cdfdeff 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -4710,7 +4710,7 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& case 1: //cat switch (fn) { - case 0: //Base + case 0: //String switch (modes[0]) { case 0: result = (Game_Strings::Str_t)com.string; break; @@ -4724,6 +4724,8 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& case 2: args[1] = Main_Data::game_variables->GetIndirect(args[1]); break; } break; + case 1: //Number + break; case 3: //Database Names break; case 4: //Database Descriptions @@ -4743,6 +4745,7 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& case 14: //Replace Ex (exRep) , edge case: the arg "first" is at ((flags >> 19) & 1). Wtf BingShan break; default: + Output::Warning("Unknown or unimplemented string sub-operation {}", op); break; } if (op == 0) { From 87ae11192d7c5d7f79f75a4a0ea230d417e43e16 Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Tue, 27 Jun 2023 14:38:23 -0600 Subject: [PATCH 03/47] StringVar: add min and range support for string asg and cat --- src/game_interpreter.cpp | 14 +++++++++++--- src/game_strings.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ src/game_strings.h | 5 ++++- 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 362cdfdeff..3bc205d982 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -4723,6 +4723,9 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& case 1: args[1] = Main_Data::game_variables->Get(args[1]); break; case 2: args[1] = Main_Data::game_variables->GetIndirect(args[1]); break; } + + // min_size + result = Main_Data::game_strings->PrependMin(result, args[1]); break; case 1: //Number break; @@ -4748,11 +4751,16 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& Output::Warning("Unknown or unimplemented string sub-operation {}", op); break; } - if (op == 0) { - Main_Data::game_strings->Asg(string_id_0, result); + if (is_range) { + Main_Data::game_strings->RangeOp(string_id_0, string_id_1, result, op); } else { - Main_Data::game_strings->Cat(string_id_0, result); + if (op == 0) { + Main_Data::game_strings->Asg(string_id_0, result); + } + else { + Main_Data::game_strings->Cat(string_id_0, result); + } } Output::Debug("t[{}]: {}", string_id_0, Main_Data::game_strings->Get(string_id_0)); break; diff --git a/src/game_strings.cpp b/src/game_strings.cpp index ae7c0d5aa7..8fe31e1060 100644 --- a/src/game_strings.cpp +++ b/src/game_strings.cpp @@ -59,3 +59,43 @@ Game_Strings::Str_t Game_Strings::Cat(int string_id, Str_t string) { s = (Str_t)op_string; return s; } + +const Game_Strings::Strings_t& Game_Strings::RangeOp(int string_id_0, int string_id_1, Str_t string, int op) { + if (EP_UNLIKELY(ShouldWarn(string_id_0))) { + WarnGet(string_id_0); + } + if (EP_UNLIKELY(ShouldWarn(string_id_1))) { + WarnGet(string_id_1); + } + + if (string_id_0 <= 0) { string_id_0 = 1; } + if (string_id_1 <= 0) { string_id_1 = 1; } + + // swap so that id_0 is < id_1 + if (string_id_0 > string_id_1) { + string_id_0 = string_id_0 ^ string_id_1; + string_id_1 = string_id_1 ^ string_id_0; + string_id_0 = string_id_0 ^ string_id_1; + } + + if (EP_UNLIKELY(string_id_1 > static_cast(_strings.size()))) { + _strings.resize(string_id_1, ""); + } + + for (int i = string_id_0; i <= string_id_1; i++) { + switch (op) { + case 0: Asg(i, string); break; + case 1: Cat(i, string); break; + } + } + return GetData(); +} + +Game_Strings::Str_t Game_Strings::PrependMin(Str_t string, int min_size) { + if (string.size() < min_size) { + int s = min_size - string.size(); + std::string res = std::string(s, ' ') + (std::string)string; + return (Str_t)res; + } + return string; +} diff --git a/src/game_strings.h b/src/game_strings.h index 68714829cf..2e6d16d981 100644 --- a/src/game_strings.h +++ b/src/game_strings.h @@ -46,9 +46,13 @@ class Game_Strings { Str_t Asg(int string_id, Str_t string); Str_t Cat(int string_id, Str_t string); + const Strings_t& RangeOp(int string_id_0, int string_id_1, Str_t string, int op); + + Str_t PrependMin(Str_t string, int min_size); private: bool ShouldWarn(int id) const; void WarnGet(int id) const; + private: Strings_t _strings; mutable int _warnings = max_warnings; @@ -80,4 +84,3 @@ inline Game_Strings::Str_t Game_Strings::GetIndirect(int id) const { auto val_indirect = Main_Data::game_variables->Get(id); return Get(static_cast(val_indirect)); } - From f95ee789ab5b41f263ea203d48683235d4a458b5 Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Tue, 27 Jun 2023 15:13:50 -0600 Subject: [PATCH 04/47] StringVar: add num asg & cat --- src/game_interpreter.cpp | 16 +++++++++++++++- src/game_strings.cpp | 4 ++-- src/game_strings.h | 2 +- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 3bc205d982..b7d049f146 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -4725,9 +4725,23 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& } // min_size - result = Main_Data::game_strings->PrependMin(result, args[1]); + result = Main_Data::game_strings->PrependMin(result, args[1], ' '); break; case 1: //Number + switch (modes[0]) + { + case 0: break; + case 1: args[0] = Main_Data::game_variables->Get(args[0]); break; + case 2: args[0] = Main_Data::game_variables->GetIndirect(args[0]); break; + } + switch (modes[1]) + { + case 0: break; + case 1: args[1] = Main_Data::game_variables->Get(args[1]); break; + case 2: args[1] = Main_Data::game_variables->GetIndirect(args[1]); break; + } + result = (Game_Strings::Str_t)std::to_string(args[0]); + result = Main_Data::game_strings->PrependMin(result, args[1], '0'); break; case 3: //Database Names break; diff --git a/src/game_strings.cpp b/src/game_strings.cpp index 8fe31e1060..cab028dc35 100644 --- a/src/game_strings.cpp +++ b/src/game_strings.cpp @@ -91,10 +91,10 @@ const Game_Strings::Strings_t& Game_Strings::RangeOp(int string_id_0, int string return GetData(); } -Game_Strings::Str_t Game_Strings::PrependMin(Str_t string, int min_size) { +Game_Strings::Str_t Game_Strings::PrependMin(Str_t string, int min_size, char c) { if (string.size() < min_size) { int s = min_size - string.size(); - std::string res = std::string(s, ' ') + (std::string)string; + std::string res = std::string(s, c) + (std::string)string; return (Str_t)res; } return string; diff --git a/src/game_strings.h b/src/game_strings.h index 2e6d16d981..f08a3fd8a5 100644 --- a/src/game_strings.h +++ b/src/game_strings.h @@ -48,7 +48,7 @@ class Game_Strings { const Strings_t& RangeOp(int string_id_0, int string_id_1, Str_t string, int op); - Str_t PrependMin(Str_t string, int min_size); + Str_t PrependMin(Str_t string, int min_size, char c); private: bool ShouldWarn(int id) const; void WarnGet(int id) const; From e1613c33407fb0cc8c9f31e58376ff2b8acd8edd Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Tue, 27 Jun 2023 21:39:34 -0600 Subject: [PATCH 05/47] StringVar: add some string name assigns --- src/game_interpreter.cpp | 32 +++++++++++++++++++++++++++++++- src/game_strings.cpp | 2 ++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index b7d049f146..19bab55fa5 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -4743,7 +4743,37 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& result = (Game_Strings::Str_t)std::to_string(args[0]); result = Main_Data::game_strings->PrependMin(result, args[1], '0'); break; - case 3: //Database Names + case 3: //Database Names + switch (args[0]) + { + case 0: result = (Game_Strings::Str_t)Main_Data::game_actors->GetActor(args[1])->GetName(); break; //.actor[a].name + case 1: break; //.skill[a].name + case 2: break; //.item[a].name + case 3: break; //.enemy[a].name + case 4: break; //.troop[a].name + case 5: break; //.terrain[a].name + case 6: break; //.element[a].name + case 7: break; //.state[a].name + case 8: break; //.anim[a].name + case 9: break; //.tileset[a].name + case 10: result = (Game_Strings::Str_t)Main_Data::game_switches->GetName(args[1]); break; //.s[a].name + case 11: result = (Game_Strings::Str_t)Main_Data::game_variables->GetName(args[1]); break; //.v[a].name + case 12: break; //.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() >= args[1]) { + result = (Game_Strings::Str_t)Game_Map::GetCommonEvents()[args[1]-1].GetName(); + } + break; + case 14: break; //.class[a].name + case 15: break; //.anim2[a].name + case 16: result = (Game_Strings::Str_t)Game_Map::GetMapName(args[1]); break; //.map[a].name + case 17: result = (Game_Strings::Str_t)Game_Map::GetEvent(args[1])->GetName(); break; //.mev[a].name + case 18: result = (Game_Strings::Str_t)Main_Data::game_party->GetActor(args[1])->GetName(); break; //.member[a].name + } + Output::Debug("com.string: {}", com.string); + Output::Debug("args[]: {} {} {} {}", args[0], args[1], args[2], args[3]); + Output::Debug("flags: {}", flags); break; case 4: //Database Descriptions break; diff --git a/src/game_strings.cpp b/src/game_strings.cpp index cab028dc35..d5d8bbc609 100644 --- a/src/game_strings.cpp +++ b/src/game_strings.cpp @@ -67,7 +67,9 @@ const Game_Strings::Strings_t& Game_Strings::RangeOp(int string_id_0, int string if (EP_UNLIKELY(ShouldWarn(string_id_1))) { WarnGet(string_id_1); } + if (string_id_0 <= 0 && string_id_1 <= 0) { return GetData(); } + // maniacs just ignores if only one of the params is <= 0 if (string_id_0 <= 0) { string_id_0 = 1; } if (string_id_1 <= 0) { string_id_1 = 1; } From 63f850d5b5859c7223c2d84e824d107e7c075227 Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Wed, 28 Jun 2023 12:02:59 -0600 Subject: [PATCH 06/47] StringVar: add concat subcommand to string assignment --- src/game_interpreter.cpp | 64 ++++++++++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 16 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 19bab55fa5..a0cc2966fc 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include "game_interpreter.h" @@ -4704,6 +4705,12 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& Game_Strings::Str_t result = ""; + Output::Debug("com.string: {}", com.string); + Output::Debug("op: {}, fn: {}", op, fn); + Output::Debug("args[]: {} {} {} {}", args[0], args[1], args[2], args[3]); + Output::Debug("modes[]: {} {} {} {}", modes[0], modes[1], modes[2], modes[3]); + Output::Debug("flags: {}", flags); + switch (op) { case 0: //asg @@ -4746,37 +4753,62 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& case 3: //Database Names switch (args[0]) { - case 0: result = (Game_Strings::Str_t)Main_Data::game_actors->GetActor(args[1])->GetName(); break; //.actor[a].name - case 1: break; //.skill[a].name - case 2: break; //.item[a].name - case 3: break; //.enemy[a].name - case 4: break; //.troop[a].name - case 5: break; //.terrain[a].name - case 6: break; //.element[a].name - case 7: break; //.state[a].name - case 8: break; //.anim[a].name - case 9: break; //.tileset[a].name + case 0: //.actor[a].name + if (args[2]) { + result = (Game_Strings::Str_t)Main_Data::game_actors->GetActor(args[1])->GetName(); + } + else { + result = (Game_Strings::Str_t)lcf::ReaderUtil::GetElement(lcf::Data::actors, args[1])->name; + } + break; + case 1: result = (Game_Strings::Str_t)lcf::ReaderUtil::GetElement(lcf::Data::skills, args[1])->name; break; //.skill[a].name + case 2: result = (Game_Strings::Str_t)lcf::ReaderUtil::GetElement(lcf::Data::items, args[1])->name; break; //.item[a].name + case 3: result = (Game_Strings::Str_t)lcf::ReaderUtil::GetElement(lcf::Data::enemies, args[1])->name; break; //.enemy[a].name + case 4: result = (Game_Strings::Str_t)lcf::ReaderUtil::GetElement(lcf::Data::troops, args[1])->name; break; //.troop[a].name + case 5: result = (Game_Strings::Str_t)lcf::ReaderUtil::GetElement(lcf::Data::terrains, args[1])->name; break; //.terrain[a].name + case 6: result = (Game_Strings::Str_t)lcf::ReaderUtil::GetElement(lcf::Data::attributes, args[1])->name; break; //.element[a].name + case 7: result = (Game_Strings::Str_t)lcf::ReaderUtil::GetElement(lcf::Data::states, args[1])->name; break; //.state[a].name + case 8: result = (Game_Strings::Str_t)lcf::ReaderUtil::GetElement(lcf::Data::animations, args[1])->name; break; //.anim[a].name + case 9: result = (Game_Strings::Str_t)lcf::ReaderUtil::GetElement(lcf::Data::chipsets, args[1])->name; break; //.tileset[a].name case 10: result = (Game_Strings::Str_t)Main_Data::game_switches->GetName(args[1]); break; //.s[a].name case 11: result = (Game_Strings::Str_t)Main_Data::game_variables->GetName(args[1]); break; //.v[a].name - case 12: break; //.t[a].name -- not sure how to get this for now + case 12: break; //.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() >= args[1]) { result = (Game_Strings::Str_t)Game_Map::GetCommonEvents()[args[1]-1].GetName(); } break; - case 14: break; //.class[a].name - case 15: break; //.anim2[a].name + case 14: result = (Game_Strings::Str_t)lcf::ReaderUtil::GetElement(lcf::Data::classes, args[1])->name; break; //.class[a].name + case 15: result = (Game_Strings::Str_t)lcf::ReaderUtil::GetElement(lcf::Data::battleranimations, args[1])->name; break; //.anim2[a].name case 16: result = (Game_Strings::Str_t)Game_Map::GetMapName(args[1]); break; //.map[a].name case 17: result = (Game_Strings::Str_t)Game_Map::GetEvent(args[1])->GetName(); break; //.mev[a].name case 18: result = (Game_Strings::Str_t)Main_Data::game_party->GetActor(args[1])->GetName(); break; //.member[a].name } - Output::Debug("com.string: {}", com.string); - Output::Debug("args[]: {} {} {} {}", args[0], args[1], args[2], args[3]); - Output::Debug("flags: {}", flags); break; case 4: //Database Descriptions break; + case 6: //Concatenate (cat) + { + int it = 0; + std::string op_string = ""; + for (int i = 0; i < 3; i++) { + switch (modes[i]) { + case 0: // part of the raw command string + op_string += ((std::string)com.string).substr(it, args[i]); + it += args[i]; + break; + case 1: // direct string reference + op_string += (std::string)Main_Data::game_strings->Get(args[i]); + break; + case 2: // indirect string reference + op_string += (std::string)Main_Data::game_strings->GetIndirect(args[i]); + break; + } + } + result = (Game_Strings::Str_t)op_string; + break; + } case 7: //Insert (ins) break; case 8: //Replace (rep) From 5d0150f04f0750f8059902b126cedbcf26ddcd1b Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Wed, 28 Jun 2023 14:33:15 -0600 Subject: [PATCH 07/47] StringVar: refactors, add insert and replace subcommands --- src/game_interpreter.cpp | 72 +++++++++++++++++++--------------------- src/game_strings.h | 31 +++++++++++++++++ src/game_variables.h | 12 +++++++ 3 files changed, 77 insertions(+), 38 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index a0cc2966fc..be846a365b 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -4718,39 +4718,22 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& switch (fn) { case 0: //String - switch (modes[0]) - { - case 0: result = (Game_Strings::Str_t)com.string; break; - case 1: result = Main_Data::game_strings->Get(args[0]); break; - case 2: result = Main_Data::game_strings->GetIndirect(args[0]); break; - } - switch (modes[1]) - { - case 0: break; - case 1: args[1] = Main_Data::game_variables->Get(args[1]); break; - case 2: args[1] = Main_Data::game_variables->GetIndirect(args[1]); break; - } + result = Main_Data::game_strings->GetWithMode((Game_Strings::Str_t)com.string, args[0], modes[0]); + args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); // min_size result = Main_Data::game_strings->PrependMin(result, args[1], ' '); break; case 1: //Number - switch (modes[0]) - { - case 0: break; - case 1: args[0] = Main_Data::game_variables->Get(args[0]); break; - case 2: args[0] = Main_Data::game_variables->GetIndirect(args[0]); break; - } - switch (modes[1]) - { - case 0: break; - case 1: args[1] = Main_Data::game_variables->Get(args[1]); break; - case 2: args[1] = Main_Data::game_variables->GetIndirect(args[1]); break; - } + args[0] = Main_Data::game_variables->GetWithMode(args[0], modes[0]); + args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); + result = (Game_Strings::Str_t)std::to_string(args[0]); result = Main_Data::game_strings->PrependMin(result, args[1], '0'); break; case 3: //Database Names + args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); + switch (args[0]) { case 0: //.actor[a].name @@ -4790,29 +4773,42 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& break; case 6: //Concatenate (cat) { - int it = 0; - std::string op_string = ""; + int pos = 0; + std::string op_string; for (int i = 0; i < 3; i++) { - switch (modes[i]) { - case 0: // part of the raw command string - op_string += ((std::string)com.string).substr(it, args[i]); - it += args[i]; - break; - case 1: // direct string reference - op_string += (std::string)Main_Data::game_strings->Get(args[i]); - break; - case 2: // indirect string reference - op_string += (std::string)Main_Data::game_strings->GetIndirect(args[i]); - break; - } + op_string += (std::string)Main_Data::game_strings->GetWithModeAndPos(com.string, args[i], modes[i], &pos); } result = (Game_Strings::Str_t)op_string; break; } case 7: //Insert (ins) + { + int pos = 0; + std::string base; + std::string insert; + + args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); + break; + } case 8: //Replace (rep) + { + int pos = 0; + std::string base, search, replacement; + + base = (std::string)Main_Data::game_strings->GetWithModeAndPos(com.string, args[0], modes[0], &pos); + search = (std::string)Main_Data::game_strings->GetWithModeAndPos(com.string, args[1], modes[1], &pos); + replacement = (std::string)Main_Data::game_strings->GetWithModeAndPos(com.string, args[2], modes[2], &pos); + + std::size_t index = base.find(search); + while (index != std::string::npos) { + base.replace(index, search.length(), replacement); + index = base.find(search, index + replacement.length()); + } + + result = (Game_Strings::Str_t)base; break; + } case 9: //Substring (subs) break; case 10: //Join (join) diff --git a/src/game_strings.h b/src/game_strings.h index f08a3fd8a5..8a018832be 100644 --- a/src/game_strings.h +++ b/src/game_strings.h @@ -42,6 +42,8 @@ class Game_Strings { 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); Str_t Asg(int string_id, Str_t string); Str_t Cat(int string_id, Str_t string); @@ -84,3 +86,32 @@ inline Game_Strings::Str_t Game_Strings::GetIndirect(int id) const { auto val_indirect = Main_Data::game_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 { + switch (mode) { + case 1: // direct string reference + return Get(arg); + break; + case 2: // indirect string reference + return GetIndirect(arg); + break; + } + return str_data; +} + +inline Game_Strings::Str_t Game_Strings::GetWithModeAndPos(Str_t str_data, int arg, int mode, int* pos) { + Str_t ret; + switch (mode) { + case 0: + ret = (Str_t)((std::string)str_data).substr(*pos, arg); + *pos += arg; + break; + case 1: // direct string reference + ret = Get(arg); + break; + case 2: // indirect string reference + ret = GetIndirect(arg); + break; + } + return ret; +} diff --git a/src/game_variables.h b/src/game_variables.h index 333e7e5768..6be5c08cb5 100644 --- a/src/game_variables.h +++ b/src/game_variables.h @@ -48,6 +48,7 @@ class Game_Variables { Var_t Get(int variable_id) const; Var_t GetIndirect(int variable_id) const; + Var_t GetWithMode(int id, int mode) const; Var_t Set(int variable_id, Var_t value); Var_t Add(int variable_id, Var_t value); @@ -205,6 +206,17 @@ inline Game_Variables::Var_t Game_Variables::GetIndirect(int variable_id) const return Get(static_cast(val_indirect)); } +// mode 0: pass through id value +// mode 1: get by id +// mode 2: get indirect by id +inline Game_Variables::Var_t Game_Variables::GetWithMode(int id, int mode) const { + switch (mode) { + case 1: return Get(id); break; + case 2: return GetIndirect(id); break; + } + return id; +} + inline void Game_Variables::SetWarning(int w) { _warnings = w; } From 1174d5476a0cceaa63687dbb7a28938db12cf8bd Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Wed, 28 Jun 2023 14:53:00 -0600 Subject: [PATCH 08/47] StringVar: add substring support --- src/game_interpreter.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index be846a365b..b0183fa7ae 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -4810,6 +4810,9 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& break; } case 9: //Substring (subs) + args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); + args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[2]); + result = (Game_Strings::Str_t)((std::string)Main_Data::game_strings->GetWithMode(com.string, args[0], modes[0])).substr(args[1], args[2]); break; case 10: //Join (join) break; From 12d14d1c48dcbf9fe2e9d06034f52e09d84fd582 Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Wed, 28 Jun 2023 16:08:36 -0600 Subject: [PATCH 09/47] StringVar: add join, rem, exrep subcommands, fix insert i had deleted some code from the insert subcommand previously somehow --- src/game_interpreter.cpp | 44 +++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index b0183fa7ae..5a77dfb9da 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -17,12 +17,13 @@ // Headers #include +#include #include #include +#include #include #include #include -#include #include "game_interpreter.h" #include "audio.h" #include "dynrpg.h" @@ -4684,6 +4685,7 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& int fn = (com.parameters[3] >> 8) & 255; int flags = (com.parameters[3] >> 16) & 255; + int first_flag = (flags >> 3) & 1; int hex_flag = (flags >> 17) & 1; int extract_flag = (flags >> 18) & 1; @@ -4784,11 +4786,13 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& case 7: //Insert (ins) { int pos = 0; - std::string base; - std::string insert; + std::string base, insert; args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); - + base = (std::string)Main_Data::game_strings->GetWithModeAndPos(com.string, args[0], modes[0], &pos); + insert = (std::string)Main_Data::game_strings->GetWithModeAndPos(com.string, args[2], modes[2], &pos); + + result = (Game_Strings::Str_t)base.insert(args[1], insert); break; } case 8: //Replace (rep) @@ -4814,14 +4818,44 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[2]); result = (Game_Strings::Str_t)((std::string)Main_Data::game_strings->GetWithMode(com.string, args[0], modes[0])).substr(args[1], args[2]); break; - case 10: //Join (join) + case 10: //Join Variables (join) + { + std::string op_string = ""; + std::string delimiter = (std::string)Main_Data::game_strings->GetWithMode(com.string, args[0], modes[0]); + // mode of arg[1] is weird, seems offset by +2, not sure what the intent is + args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1] - 2); + args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[2]); + + while (args[2] > 0) { + op_string += std::to_string(Main_Data::game_variables->Get(args[1]++)); + if (--args[2] > 0) op_string += delimiter; + } + + result = (Game_Strings::Str_t)op_string; break; + } case 12: //File (file) break; case 13: //Remove (rem) + args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); + args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[2]); + result = (Game_Strings::Str_t)((std::string)Main_Data::game_strings->GetWithMode(com.string, args[0], modes[0])).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 = (std::string)Main_Data::game_strings->GetWithModeAndPos(com.string, args[0], modes[0], &pos); + search = (std::string)Main_Data::game_strings->GetWithModeAndPos(com.string, args[1], modes[1], &pos); + replacement = (std::string)Main_Data::game_strings->GetWithModeAndPos(com.string, args[2], modes[2], &pos); + + std::regex rexp(search); + + if (first_flag) result = (Game_Strings::Str_t)std::regex_replace(base, rexp, replacement, std::regex_constants::format_first_only); + else result = (Game_Strings::Str_t)std::regex_replace(base, rexp, replacement); break; + } default: Output::Warning("Unknown or unimplemented string sub-operation {}", op); break; From 164e909013508901cec405a8de2f0f17a8d9f92e Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Wed, 28 Jun 2023 16:47:49 -0600 Subject: [PATCH 10/47] StringVar: convert casts to static_cast --- src/game_interpreter.cpp | 66 ++++++++++++++++++++-------------------- src/game_strings.h | 2 +- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 5a77dfb9da..852ae9ee68 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -4720,7 +4720,7 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& switch (fn) { case 0: //String - result = Main_Data::game_strings->GetWithMode((Game_Strings::Str_t)com.string, args[0], modes[0]); + result = Main_Data::game_strings->GetWithMode(static_cast(com.string), args[0], modes[0]); args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); // min_size @@ -4730,7 +4730,7 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& args[0] = Main_Data::game_variables->GetWithMode(args[0], modes[0]); args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); - result = (Game_Strings::Str_t)std::to_string(args[0]); + result = static_cast(std::to_string(args[0])); result = Main_Data::game_strings->PrependMin(result, args[1], '0'); break; case 3: //Database Names @@ -4746,17 +4746,17 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& result = (Game_Strings::Str_t)lcf::ReaderUtil::GetElement(lcf::Data::actors, args[1])->name; } break; - case 1: result = (Game_Strings::Str_t)lcf::ReaderUtil::GetElement(lcf::Data::skills, args[1])->name; break; //.skill[a].name - case 2: result = (Game_Strings::Str_t)lcf::ReaderUtil::GetElement(lcf::Data::items, args[1])->name; break; //.item[a].name - case 3: result = (Game_Strings::Str_t)lcf::ReaderUtil::GetElement(lcf::Data::enemies, args[1])->name; break; //.enemy[a].name - case 4: result = (Game_Strings::Str_t)lcf::ReaderUtil::GetElement(lcf::Data::troops, args[1])->name; break; //.troop[a].name - case 5: result = (Game_Strings::Str_t)lcf::ReaderUtil::GetElement(lcf::Data::terrains, args[1])->name; break; //.terrain[a].name - case 6: result = (Game_Strings::Str_t)lcf::ReaderUtil::GetElement(lcf::Data::attributes, args[1])->name; break; //.element[a].name - case 7: result = (Game_Strings::Str_t)lcf::ReaderUtil::GetElement(lcf::Data::states, args[1])->name; break; //.state[a].name - case 8: result = (Game_Strings::Str_t)lcf::ReaderUtil::GetElement(lcf::Data::animations, args[1])->name; break; //.anim[a].name - case 9: result = (Game_Strings::Str_t)lcf::ReaderUtil::GetElement(lcf::Data::chipsets, args[1])->name; break; //.tileset[a].name - case 10: result = (Game_Strings::Str_t)Main_Data::game_switches->GetName(args[1]); break; //.s[a].name - case 11: result = (Game_Strings::Str_t)Main_Data::game_variables->GetName(args[1]); break; //.v[a].name + case 1: result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::skills, args[1])->name); break; //.skill[a].name + case 2: result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::items, args[1])->name); break; //.item[a].name + case 3: result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::enemies, args[1])->name); break; //.enemy[a].name + case 4: result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::troops, args[1])->name); break; //.troop[a].name + case 5: result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::terrains, args[1])->name); break; //.terrain[a].name + case 6: result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::attributes, args[1])->name); break; //.element[a].name + case 7: result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::states, args[1])->name); break; //.state[a].name + case 8: result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::animations, args[1])->name); break; //.anim[a].name + case 9: result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::chipsets, args[1])->name); break; //.tileset[a].name + case 10: result = static_cast(Main_Data::game_switches->GetName(args[1])); break; //.s[a].name + case 11: result = static_cast(Main_Data::game_variables->GetName(args[1])); break; //.v[a].name case 12: break; //.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 @@ -4764,11 +4764,11 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& result = (Game_Strings::Str_t)Game_Map::GetCommonEvents()[args[1]-1].GetName(); } break; - case 14: result = (Game_Strings::Str_t)lcf::ReaderUtil::GetElement(lcf::Data::classes, args[1])->name; break; //.class[a].name - case 15: result = (Game_Strings::Str_t)lcf::ReaderUtil::GetElement(lcf::Data::battleranimations, args[1])->name; break; //.anim2[a].name - case 16: result = (Game_Strings::Str_t)Game_Map::GetMapName(args[1]); break; //.map[a].name - case 17: result = (Game_Strings::Str_t)Game_Map::GetEvent(args[1])->GetName(); break; //.mev[a].name - case 18: result = (Game_Strings::Str_t)Main_Data::game_party->GetActor(args[1])->GetName(); break; //.member[a].name + case 14: result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::classes, args[1])->name); break; //.class[a].name + case 15: result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::battleranimations, args[1])->name); break; //.anim2[a].name + case 16: result = static_cast(Game_Map::GetMapName(args[1])); break; //.map[a].name + case 17: result = static_cast(Game_Map::GetEvent(args[1])->GetName()); break; //.mev[a].name + case 18: result = static_cast(Main_Data::game_party->GetActor(args[1])->GetName()); break; //.member[a].name } break; case 4: //Database Descriptions @@ -4778,7 +4778,7 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& int pos = 0; std::string op_string; for (int i = 0; i < 3; i++) { - op_string += (std::string)Main_Data::game_strings->GetWithModeAndPos(com.string, args[i], modes[i], &pos); + op_string += static_cast(Main_Data::game_strings->GetWithModeAndPos(com.string, args[i], modes[i], &pos)); } result = (Game_Strings::Str_t)op_string; break; @@ -4789,8 +4789,8 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& std::string base, insert; args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); - base = (std::string)Main_Data::game_strings->GetWithModeAndPos(com.string, args[0], modes[0], &pos); - insert = (std::string)Main_Data::game_strings->GetWithModeAndPos(com.string, args[2], modes[2], &pos); + base = static_cast(Main_Data::game_strings->GetWithModeAndPos(com.string, args[0], modes[0], &pos)); + insert = static_cast(Main_Data::game_strings->GetWithModeAndPos(com.string, args[2], modes[2], &pos)); result = (Game_Strings::Str_t)base.insert(args[1], insert); break; @@ -4800,9 +4800,9 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& int pos = 0; std::string base, search, replacement; - base = (std::string)Main_Data::game_strings->GetWithModeAndPos(com.string, args[0], modes[0], &pos); - search = (std::string)Main_Data::game_strings->GetWithModeAndPos(com.string, args[1], modes[1], &pos); - replacement = (std::string)Main_Data::game_strings->GetWithModeAndPos(com.string, args[2], modes[2], &pos); + base = static_cast(Main_Data::game_strings->GetWithModeAndPos(com.string, args[0], modes[0], &pos)); + search = static_cast(Main_Data::game_strings->GetWithModeAndPos(com.string, args[1], modes[1], &pos)); + replacement = static_cast(Main_Data::game_strings->GetWithModeAndPos(com.string, args[2], modes[2], &pos)); std::size_t index = base.find(search); while (index != std::string::npos) { @@ -4810,13 +4810,13 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& index = base.find(search, index + replacement.length()); } - result = (Game_Strings::Str_t)base; + result = static_cast(base); break; } case 9: //Substring (subs) args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[2]); - result = (Game_Strings::Str_t)((std::string)Main_Data::game_strings->GetWithMode(com.string, args[0], modes[0])).substr(args[1], args[2]); + result = static_cast(static_cast(Main_Data::game_strings->GetWithMode(com.string, args[0], modes[0])).substr(args[1], args[2])); break; case 10: //Join Variables (join) { @@ -4831,7 +4831,7 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& if (--args[2] > 0) op_string += delimiter; } - result = (Game_Strings::Str_t)op_string; + result = static_cast(op_string); break; } case 12: //File (file) @@ -4839,21 +4839,21 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& case 13: //Remove (rem) args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[2]); - result = (Game_Strings::Str_t)((std::string)Main_Data::game_strings->GetWithMode(com.string, args[0], modes[0])).erase(args[1], args[2]); + result = static_cast(static_cast(Main_Data::game_strings->GetWithMode(com.string, args[0], modes[0])).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 = (std::string)Main_Data::game_strings->GetWithModeAndPos(com.string, args[0], modes[0], &pos); - search = (std::string)Main_Data::game_strings->GetWithModeAndPos(com.string, args[1], modes[1], &pos); - replacement = (std::string)Main_Data::game_strings->GetWithModeAndPos(com.string, args[2], modes[2], &pos); + base = static_cast(Main_Data::game_strings->GetWithModeAndPos(com.string, args[0], modes[0], &pos)); + search = static_cast(Main_Data::game_strings->GetWithModeAndPos(com.string, args[1], modes[1], &pos)); + replacement = static_cast(Main_Data::game_strings->GetWithModeAndPos(com.string, args[2], modes[2], &pos)); std::regex rexp(search); - if (first_flag) result = (Game_Strings::Str_t)std::regex_replace(base, rexp, replacement, std::regex_constants::format_first_only); - else result = (Game_Strings::Str_t)std::regex_replace(base, rexp, replacement); + if (first_flag) result = static_cast(std::regex_replace(base, rexp, replacement, std::regex_constants::format_first_only)); + else result = static_cast(std::regex_replace(base, rexp, replacement)); break; } default: diff --git a/src/game_strings.h b/src/game_strings.h index 8a018832be..8fe8b8ba0e 100644 --- a/src/game_strings.h +++ b/src/game_strings.h @@ -103,7 +103,7 @@ inline Game_Strings::Str_t Game_Strings::GetWithModeAndPos(Str_t str_data, int a Str_t ret; switch (mode) { case 0: - ret = (Str_t)((std::string)str_data).substr(*pos, arg); + ret = static_cast(static_cast(str_data).substr(*pos, arg)); *pos += arg; break; case 1: // direct string reference From 0279892088a36897b6b35a27903efa166660d38c Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Wed, 28 Jun 2023 20:56:58 -0600 Subject: [PATCH 11/47] StringVar: add toNum function --- src/game_interpreter.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 852ae9ee68..e7a835d0ca 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -4671,8 +4671,9 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& int string_mode = com.parameters[0] & 15; int string_id_0 = com.parameters[1]; int string_id_1 = com.parameters[2]; //for ranges + Output::Debug("string_mode: {:b}, id1: {}, id2: {}", string_mode, string_id_0, string_id_1); - int is_range = string_mode & 1; + int is_range = string_mode & 1; if (string_mode >= 2) { string_id_0 = Main_Data::game_variables->Get(string_id_0); @@ -4874,7 +4875,19 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& Output::Debug("t[{}]: {}", string_id_0, Main_Data::game_strings->Get(string_id_0)); break; case 2: //toNum + { + if (is_range) { + for (int i = string_id_0; i <= string_id_1; i++) { + int num = std::stoi(static_cast(Main_Data::game_strings->Get(i))); + Main_Data::game_variables->Set(args[0] + (i - string_id_0), num); + } + } + else { + int num = std::stoi(static_cast(Main_Data::game_strings->Get(string_id_0))); + Main_Data::game_variables->Set(args[0], num); + } break; + } case 3: //getLen break; case 4: //inStr From 6de91d44b367f3ebc366963eaadd849f7855bf00 Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Wed, 28 Jun 2023 22:07:21 -0600 Subject: [PATCH 12/47] StringVar: add getLen and inStr functions --- src/game_interpreter.cpp | 37 ++++++++++++---------------- src/game_strings.cpp | 52 +++++++++++++++++++++++++++++++++++++++- src/game_strings.h | 5 +++- 3 files changed, 70 insertions(+), 24 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index e7a835d0ca..893f8b36aa 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -4861,37 +4861,30 @@ 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(string_id_0, string_id_1, result, op); - } + if (is_range) Main_Data::game_strings->RangeOp(string_id_0, string_id_1, result, op); else { - if (op == 0) { - Main_Data::game_strings->Asg(string_id_0, result); - } - else { - Main_Data::game_strings->Cat(string_id_0, result); - } + if (op == 0) Main_Data::game_strings->Asg(string_id_0, result); + if (op == 1) Main_Data::game_strings->Cat(string_id_0, result); } - Output::Debug("t[{}]: {}", string_id_0, Main_Data::game_strings->Get(string_id_0)); break; case 2: //toNum - { - if (is_range) { - for (int i = string_id_0; i <= string_id_1; i++) { - int num = std::stoi(static_cast(Main_Data::game_strings->Get(i))); - Main_Data::game_variables->Set(args[0] + (i - string_id_0), num); - } - } + case 3: //getLen + if (is_range) Main_Data::game_strings->RangeOp(string_id_0, string_id_1, result, op, args); else { - int num = std::stoi(static_cast(Main_Data::game_strings->Get(string_id_0))); - Main_Data::game_variables->Set(args[0], num); + if (op == 2) Main_Data::game_strings->ToNum(string_id_0, args[0]); + if (op == 3) Main_Data::game_strings->GetLen(string_id_0, args[0]); } break; - } - case 3: //getLen - break; case 4: //inStr + { + std::string search = static_cast(Main_Data::game_strings->GetWithMode(com.string, args[0], modes[0])); + args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); // not sure this is necessary but better safe + args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[2]); + + if (is_range) Main_Data::game_strings->RangeOp(string_id_0, string_id_1, static_cast(search), op, args); + else Main_Data::game_strings->InStr(string_id_0, search, args[1], args[2]); break; + } case 5: //split break; case 7: //toFile diff --git a/src/game_strings.cpp b/src/game_strings.cpp index d5d8bbc609..e55718719d 100644 --- a/src/game_strings.cpp +++ b/src/game_strings.cpp @@ -60,7 +60,54 @@ Game_Strings::Str_t Game_Strings::Cat(int string_id, Str_t string) { return s; } -const Game_Strings::Strings_t& Game_Strings::RangeOp(int string_id_0, int string_id_1, Str_t string, int op) { +int Game_Strings::ToNum(int string_id, int var_id) { + if (EP_UNLIKELY(ShouldWarn(string_id))) { + WarnGet(string_id); + } + if (string_id <= 0) { + return -1; + } + if (EP_UNLIKELY(string_id > static_cast(_strings.size()))) { + _strings.resize(string_id, ""); + } + + int num = std::stoi(static_cast(_strings[string_id])); + Main_Data::game_variables->Set(var_id, num); + return num; +} + +int Game_Strings::GetLen(int string_id, int var_id) { + if (EP_UNLIKELY(ShouldWarn(string_id))) { + WarnGet(string_id); + } + if (string_id <= 0) { + return -1; + } + if (EP_UNLIKELY(string_id > static_cast(_strings.size()))) { + _strings.resize(string_id, ""); + } + + int len = static_cast(_strings[string_id-1]).length(); + Main_Data::game_variables->Set(var_id, len); + return len; +} + +int Game_Strings::InStr(int string_id, std::string search, int var_id, int begin) { + if (EP_UNLIKELY(ShouldWarn(string_id))) { + WarnGet(string_id); + } + if (string_id <= 0) { + return -1; + } + if (EP_UNLIKELY(string_id > static_cast(_strings.size()))) { + _strings.resize(string_id, ""); + } + + int index = static_cast(_strings[string_id - 1]).find(search, begin); + Main_Data::game_variables->Set(var_id, index); +} + +const Game_Strings::Strings_t& Game_Strings::RangeOp(int string_id_0, int string_id_1, Str_t string, int op, int args[]) { if (EP_UNLIKELY(ShouldWarn(string_id_0))) { WarnGet(string_id_0); } @@ -88,6 +135,9 @@ const Game_Strings::Strings_t& Game_Strings::RangeOp(int string_id_0, int string switch (op) { case 0: Asg(i, string); break; case 1: Cat(i, string); break; + case 2: ToNum(i, args[0] + (i - string_id_0)); break; + case 3: GetLen(i, args[0] + (i - string_id_0)); break; + case 4: InStr(i, static_cast(string), args[1], args[2]); break; } } return GetData(); diff --git a/src/game_strings.h b/src/game_strings.h index 8fe8b8ba0e..7a2c0f93ac 100644 --- a/src/game_strings.h +++ b/src/game_strings.h @@ -47,8 +47,11 @@ class Game_Strings { Str_t Asg(int string_id, Str_t string); Str_t Cat(int string_id, Str_t string); + int ToNum(int string_id, int var_id); + int GetLen(int string_id, int var_id); + int InStr(int string_id, std::string search, int var_id, int begin = 0); - const Strings_t& RangeOp(int string_id_0, int string_id_1, Str_t string, int op); + const Strings_t& RangeOp(int string_id_0, int string_id_1, Str_t string, int op, int args[] = nullptr); Str_t PrependMin(Str_t string, int min_size, char c); private: From 17dab1830d8b7397fe0fca0bfd3b1f22a95153fd Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Thu, 29 Jun 2023 08:54:52 -0600 Subject: [PATCH 13/47] StringVar: more refactors and add split function --- src/game_interpreter.cpp | 14 +++++-- src/game_strings.cpp | 79 +++++++++++++++------------------------- src/game_strings.h | 24 ++++++++++++ 3 files changed, 65 insertions(+), 52 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 893f8b36aa..14ca5dd5cd 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -4877,16 +4877,24 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& break; case 4: //inStr { - std::string search = static_cast(Main_Data::game_strings->GetWithMode(com.string, args[0], modes[0])); + Game_Strings::Str_t search = Main_Data::game_strings->GetWithMode(com.string, args[0], modes[0]); args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); // not sure this is necessary but better safe args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[2]); - if (is_range) Main_Data::game_strings->RangeOp(string_id_0, string_id_1, static_cast(search), op, args); - else Main_Data::game_strings->InStr(string_id_0, search, args[1], args[2]); + if (is_range) Main_Data::game_strings->RangeOp(string_id_0, string_id_1, search, op, args); + else Main_Data::game_strings->InStr(string_id_0, static_cast(search), args[1], args[2]); break; } case 5: //split + { + Game_Strings::Str_t delimiter = Main_Data::game_strings->GetWithMode(com.string, args[0], modes[0]); + args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); + args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[2]); + + if (is_range) Main_Data::game_strings->RangeOp(string_id_0, string_id_1, delimiter, op, args); + else Main_Data::game_strings->Split(string_id_0, static_cast(delimiter), args[1], args[2]); break; + } case 7: //toFile break; case 8: //popLine diff --git a/src/game_strings.cpp b/src/game_strings.cpp index e55718719d..33330361be 100644 --- a/src/game_strings.cpp +++ b/src/game_strings.cpp @@ -29,30 +29,12 @@ void Game_Strings::WarnGet(int id) const { } Game_Strings::Str_t Game_Strings::Asg(int string_id, Str_t string) { - if (EP_UNLIKELY(ShouldWarn(string_id))) { - WarnGet(string_id); - } - if (string_id <= 0) { - return ""; - } - if (EP_UNLIKELY(string_id > static_cast(_strings.size()))) { - _strings.resize(string_id, ""); - } - auto& s = _strings[string_id - 1]; - s = string; - return s; + return Set(string, string_id); } Game_Strings::Str_t Game_Strings::Cat(int string_id, Str_t string) { - if (EP_UNLIKELY(ShouldWarn(string_id))) { - WarnGet(string_id); - } - if (string_id <= 0) { - return ""; - } - if (EP_UNLIKELY(string_id > static_cast(_strings.size()))) { - _strings.resize(string_id, ""); - } + if (!ResizeWithId(string_id)) return ""; + auto& s = _strings[string_id - 1]; std::string op_string = (std::string)s; op_string.append((std::string)string); @@ -61,31 +43,15 @@ Game_Strings::Str_t Game_Strings::Cat(int string_id, Str_t string) { } int Game_Strings::ToNum(int string_id, int var_id) { - if (EP_UNLIKELY(ShouldWarn(string_id))) { - WarnGet(string_id); - } - if (string_id <= 0) { - return -1; - } - if (EP_UNLIKELY(string_id > static_cast(_strings.size()))) { - _strings.resize(string_id, ""); - } + if (!ResizeWithId(string_id)) return -1; - int num = std::stoi(static_cast(_strings[string_id])); + int num = std::stoi(static_cast(_strings[string_id-1])); Main_Data::game_variables->Set(var_id, num); return num; } int Game_Strings::GetLen(int string_id, int var_id) { - if (EP_UNLIKELY(ShouldWarn(string_id))) { - WarnGet(string_id); - } - if (string_id <= 0) { - return -1; - } - if (EP_UNLIKELY(string_id > static_cast(_strings.size()))) { - _strings.resize(string_id, ""); - } + if (!ResizeWithId(string_id)) return -1; int len = static_cast(_strings[string_id-1]).length(); Main_Data::game_variables->Set(var_id, len); @@ -93,20 +59,34 @@ int Game_Strings::GetLen(int string_id, int var_id) { } int Game_Strings::InStr(int string_id, std::string search, int var_id, int begin) { - if (EP_UNLIKELY(ShouldWarn(string_id))) { - WarnGet(string_id); - } - if (string_id <= 0) { - return -1; - } - if (EP_UNLIKELY(string_id > static_cast(_strings.size()))) { - _strings.resize(string_id, ""); - } + if (!ResizeWithId(string_id)) return -1; int index = static_cast(_strings[string_id - 1]).find(search, begin); Main_Data::game_variables->Set(var_id, index); } +int Game_Strings::Split(int string_id, std::string delimiter, int string_out_id, int var_id) { + if (!ResizeWithId(string_id)) return -1; + + // always returns at least 1 + int splits = 1; + size_t index = 0; + std::string str = static_cast(_strings[string_id - 1]); + std::string token; + + while (index = str.find(delimiter) != std::string::npos) { + token = str.substr(0, index); + Set(static_cast(token), string_out_id++); + splits++; + str.erase(0, index + delimiter.length()); + } + + // set the remaining string + Set(static_cast(str), string_out_id); + Main_Data::game_variables->Set(var_id, splits); + return splits; +} + const Game_Strings::Strings_t& Game_Strings::RangeOp(int string_id_0, int string_id_1, Str_t string, int op, int args[]) { if (EP_UNLIKELY(ShouldWarn(string_id_0))) { WarnGet(string_id_0); @@ -138,6 +118,7 @@ const Game_Strings::Strings_t& Game_Strings::RangeOp(int string_id_0, int string case 2: ToNum(i, args[0] + (i - string_id_0)); break; case 3: GetLen(i, args[0] + (i - string_id_0)); break; case 4: InStr(i, static_cast(string), args[1], args[2]); break; + case 5: i += Split(i, static_cast(string), args[1], args[2]); break; } } return GetData(); diff --git a/src/game_strings.h b/src/game_strings.h index 7a2c0f93ac..3e681102c1 100644 --- a/src/game_strings.h +++ b/src/game_strings.h @@ -50,11 +50,14 @@ class Game_Strings { int ToNum(int string_id, int var_id); int GetLen(int string_id, int var_id); int InStr(int string_id, std::string search, int var_id, int begin = 0); + int Split(int string_id, std::string delimiter, int string_out_id, int var_id); const Strings_t& RangeOp(int string_id_0, int string_id_1, Str_t string, int op, int args[] = nullptr); Str_t PrependMin(Str_t string, int min_size, char c); private: + Str_t Set(Str_t string, int string_id); + bool ResizeWithId(int id); bool ShouldWarn(int id) const; void WarnGet(int id) const; @@ -63,6 +66,14 @@ class Game_Strings { mutable int _warnings = max_warnings; }; +inline Game_Strings::Str_t Game_Strings::Set(Str_t string, int string_id) { + if (!ResizeWithId(string_id)) return ""; + + auto& s = _strings[string_id - 1]; + s = string; + return s; +} + inline void Game_Strings::SetData(Strings_t s) { _strings = std::move(s); } @@ -85,6 +96,19 @@ inline Game_Strings::Str_t Game_Strings::Get(int id) const { return _strings[id - 1]; } +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); return Get(static_cast(val_indirect)); From fb1af42e61a8c55475f8a00eedc3a7f7515be29f Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Thu, 29 Jun 2023 11:46:48 -0600 Subject: [PATCH 14/47] StringVar: add popline function --- src/game_interpreter.cpp | 13 ++++++++++++- src/game_strings.cpp | 40 +++++++++++++++++++++++++++++----------- src/game_strings.h | 5 +++-- 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 14ca5dd5cd..67a6e1cc45 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -4897,8 +4897,19 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& } case 7: //toFile break; - case 8: //popLine + case 8: //popLine + { + // a range parameter with popLine doesn't affect multiple strings; + // it instead alters the behavior. + // given a range t[a..b], it will pop the first (b-a)+1 lines, + // and store the last popped line into the output string. + args[1] = Main_Data::game_variables->GetWithMode(args[0], modes[0]); + if (is_range) + Main_Data::game_strings->PopLine(string_id_0, string_id_1 - string_id_0, args[0]); + else + Main_Data::game_strings->PopLine(string_id_0, 0, args[0]); break; + } case 9: //exInStr break; case 10: //exMatch , edge case: the only command that generates 8 parameters instead of 7 diff --git a/src/game_strings.cpp b/src/game_strings.cpp index 33330361be..7d8dcc04bd 100644 --- a/src/game_strings.cpp +++ b/src/game_strings.cpp @@ -29,23 +29,23 @@ void Game_Strings::WarnGet(int id) const { } Game_Strings::Str_t Game_Strings::Asg(int string_id, Str_t string) { - return Set(string, string_id); + return Set(string_id, string); } Game_Strings::Str_t Game_Strings::Cat(int string_id, Str_t string) { if (!ResizeWithId(string_id)) return ""; - auto& s = _strings[string_id - 1]; - std::string op_string = (std::string)s; - op_string.append((std::string)string); - s = (Str_t)op_string; + Str_t s = Get(string_id); + std::string op_string = static_cast(s); + op_string.append(static_cast(string)); + Set(string_id, static_cast(op_string)); return s; } int Game_Strings::ToNum(int string_id, int var_id) { if (!ResizeWithId(string_id)) return -1; - int num = std::stoi(static_cast(_strings[string_id-1])); + int num = std::stoi(static_cast(Get(string_id))); Main_Data::game_variables->Set(var_id, num); return num; } @@ -53,7 +53,7 @@ int Game_Strings::ToNum(int string_id, int var_id) { int Game_Strings::GetLen(int string_id, int var_id) { if (!ResizeWithId(string_id)) return -1; - int len = static_cast(_strings[string_id-1]).length(); + int len = static_cast(Get(string_id)).length(); Main_Data::game_variables->Set(var_id, len); return len; } @@ -61,7 +61,7 @@ int Game_Strings::GetLen(int string_id, int var_id) { int Game_Strings::InStr(int string_id, std::string search, int var_id, int begin) { if (!ResizeWithId(string_id)) return -1; - int index = static_cast(_strings[string_id - 1]).find(search, begin); + int index = static_cast(Get(string_id)).find(search, begin); Main_Data::game_variables->Set(var_id, index); } @@ -71,22 +71,39 @@ int Game_Strings::Split(int string_id, std::string delimiter, int string_out_id, // always returns at least 1 int splits = 1; size_t index = 0; - std::string str = static_cast(_strings[string_id - 1]); + std::string str = static_cast(Get(string_id)); std::string token; while (index = str.find(delimiter) != std::string::npos) { token = str.substr(0, index); - Set(static_cast(token), string_out_id++); + Set(string_out_id++, static_cast(token)); splits++; str.erase(0, index + delimiter.length()); } // set the remaining string - Set(static_cast(str), string_out_id); + Set(string_out_id, static_cast(str)); Main_Data::game_variables->Set(var_id, splits); return splits; } +Game_Strings::Str_t Game_Strings::PopLine(int string_id, int offset, int string_out_id) { + if (!ResizeWithId(string_id)) return ""; + + int index; + std::string result; + std::string str = static_cast(Get(string_id)); + + std::stringstream ss(str); + + while (offset >= 0 && std::getline(ss, result)) { offset--; } + + offset = ss.rdbuf()->in_avail(); + + Set(string_id, static_cast(ss.str().substr(str.length() - offset))); + return Set(string_out_id, static_cast(result)); +} + const Game_Strings::Strings_t& Game_Strings::RangeOp(int string_id_0, int string_id_1, Str_t string, int op, int args[]) { if (EP_UNLIKELY(ShouldWarn(string_id_0))) { WarnGet(string_id_0); @@ -119,6 +136,7 @@ const Game_Strings::Strings_t& Game_Strings::RangeOp(int string_id_0, int string case 3: GetLen(i, args[0] + (i - string_id_0)); break; case 4: InStr(i, static_cast(string), args[1], args[2]); break; case 5: i += Split(i, static_cast(string), args[1], args[2]); break; + case 8: break; // range case not applicable for popLine; see case in game_interpreter.cpp } } return GetData(); diff --git a/src/game_strings.h b/src/game_strings.h index 3e681102c1..36ea5375f1 100644 --- a/src/game_strings.h +++ b/src/game_strings.h @@ -51,12 +51,13 @@ class Game_Strings { int GetLen(int string_id, int var_id); int InStr(int string_id, std::string search, int var_id, int begin = 0); int Split(int string_id, std::string delimiter, int string_out_id, int var_id); + Str_t PopLine(int string_id, int offset, int string_out_id); const Strings_t& RangeOp(int string_id_0, int string_id_1, Str_t string, int op, int args[] = nullptr); Str_t PrependMin(Str_t string, int min_size, char c); private: - Str_t Set(Str_t string, int string_id); + Str_t Set(int string_id, Str_t string); bool ResizeWithId(int id); bool ShouldWarn(int id) const; void WarnGet(int id) const; @@ -66,7 +67,7 @@ class Game_Strings { mutable int _warnings = max_warnings; }; -inline Game_Strings::Str_t Game_Strings::Set(Str_t string, int string_id) { +inline Game_Strings::Str_t Game_Strings::Set(int string_id, Str_t string) { if (!ResizeWithId(string_id)) return ""; auto& s = _strings[string_id - 1]; From c8fcc85abdd9c8bdc04176755777e9b5530b6395 Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Thu, 29 Jun 2023 13:53:13 -0600 Subject: [PATCH 15/47] StringVar: add regex find and replace functions --- src/game_interpreter.cpp | 49 ++++++---- src/game_strings.cpp | 202 ++++++++++++++++++++++----------------- src/game_strings.h | 1 + 3 files changed, 143 insertions(+), 109 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 67a6e1cc45..2f3b990931 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -4671,7 +4671,7 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& int string_mode = com.parameters[0] & 15; int string_id_0 = com.parameters[1]; int string_id_1 = com.parameters[2]; //for ranges - Output::Debug("string_mode: {:b}, id1: {}, id2: {}", string_mode, string_id_0, string_id_1); + // Output::Debug("string_mode: {:b}, id1: {}, id2: {}", string_mode, string_id_0, string_id_1); int is_range = string_mode & 1; @@ -4708,11 +4708,11 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& Game_Strings::Str_t result = ""; - Output::Debug("com.string: {}", com.string); - Output::Debug("op: {}, fn: {}", op, fn); - Output::Debug("args[]: {} {} {} {}", args[0], args[1], args[2], args[3]); - Output::Debug("modes[]: {} {} {} {}", modes[0], modes[1], modes[2], modes[3]); - Output::Debug("flags: {}", flags); + // Output::Debug("com.string: {}", com.string); + // Output::Debug("op: {}, fn: {}", op, fn); + // Output::Debug("args[]: {} {} {} {}", args[0], args[1], args[2], args[3]); + // Output::Debug("modes[]: {} {} {} {}", modes[0], modes[1], modes[2], modes[3]); + // Output::Debug("flags: {}", flags); switch (op) { @@ -4790,8 +4790,8 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& std::string base, insert; args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); - base = static_cast(Main_Data::game_strings->GetWithModeAndPos(com.string, args[0], modes[0], &pos)); - insert = static_cast(Main_Data::game_strings->GetWithModeAndPos(com.string, args[2], modes[2], &pos)); + base = static_cast(Main_Data::game_strings->GetWithModeAndPos(com.string, args[0], modes[0], &pos)); + insert = static_cast(Main_Data::game_strings->GetWithModeAndPos(com.string, args[2], modes[2], &pos)); result = (Game_Strings::Str_t)base.insert(args[1], insert); break; @@ -4847,14 +4847,14 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& int pos = 0; std::string base, search, replacement; - base = static_cast(Main_Data::game_strings->GetWithModeAndPos(com.string, args[0], modes[0], &pos)); - search = static_cast(Main_Data::game_strings->GetWithModeAndPos(com.string, args[1], modes[1], &pos)); + base = static_cast(Main_Data::game_strings->GetWithModeAndPos(com.string, args[0], modes[0], &pos)); + search = static_cast(Main_Data::game_strings->GetWithModeAndPos(com.string, args[1], modes[1], &pos)); replacement = static_cast(Main_Data::game_strings->GetWithModeAndPos(com.string, args[2], modes[2], &pos)); std::regex rexp(search); if (first_flag) result = static_cast(std::regex_replace(base, rexp, replacement, std::regex_constants::format_first_only)); - else result = static_cast(std::regex_replace(base, rexp, replacement)); + else result = static_cast(std::regex_replace(base, rexp, replacement)); break; } default: @@ -4882,7 +4882,7 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[2]); if (is_range) Main_Data::game_strings->RangeOp(string_id_0, string_id_1, search, op, args); - else Main_Data::game_strings->InStr(string_id_0, static_cast(search), args[1], args[2]); + else Main_Data::game_strings->InStr(string_id_0, static_cast(search), args[1], args[2]); break; } case 5: //split @@ -4892,28 +4892,37 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[2]); if (is_range) Main_Data::game_strings->RangeOp(string_id_0, string_id_1, delimiter, op, args); - else Main_Data::game_strings->Split(string_id_0, static_cast(delimiter), args[1], args[2]); + else Main_Data::game_strings->Split(string_id_0, static_cast(delimiter), args[1], args[2]); break; } case 7: //toFile break; case 8: //popLine - { // a range parameter with popLine doesn't affect multiple strings; // it instead alters the behavior. // given a range t[a..b], it will pop the first (b-a)+1 lines, // and store the last popped line into the output string. args[1] = Main_Data::game_variables->GetWithMode(args[0], modes[0]); - if (is_range) - Main_Data::game_strings->PopLine(string_id_0, string_id_1 - string_id_0, args[0]); - else - Main_Data::game_strings->PopLine(string_id_0, 0, args[0]); + + if (is_range) Main_Data::game_strings->PopLine(string_id_0, string_id_1 - string_id_0, args[0]); + else Main_Data::game_strings->PopLine(string_id_0, 0, args[0]); break; - } case 9: //exInStr - break; case 10: //exMatch , edge case: the only command that generates 8 parameters instead of 7 + { + std::string expr = static_cast(Main_Data::game_strings->GetWithMode(com.string, args[0], modes[0])); + args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); // output var + args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[2]); // beginning pos + + if (is_range) { + Main_Data::game_strings->RangeOp(string_id_0, string_id_1, static_cast(expr), op, args); + } + else { + if (op == 9) Main_Data::game_strings->ExMatch(string_id_0, expr, args[1], args[2]); + else Main_Data::game_strings->ExMatch(string_id_0, expr, args[1], args[2], args[3]); + } break; + } default: Output::Warning("Unknown or unimplemented string operation {}", op); break; diff --git a/src/game_strings.cpp b/src/game_strings.cpp index 7d8dcc04bd..c44225cffd 100644 --- a/src/game_strings.cpp +++ b/src/game_strings.cpp @@ -16,6 +16,7 @@ */ // Headers +#include #include "game_strings.h" #include "game_variables.h" #include "output.h" @@ -24,129 +25,152 @@ Game_Strings::Game_Strings() {} void Game_Strings::WarnGet(int id) const { - Output::Debug("Invalid read strvar[{}]!", id); - --_warnings; + Output::Debug("Invalid read strvar[{}]!", id); + --_warnings; } Game_Strings::Str_t Game_Strings::Asg(int string_id, Str_t string) { - return Set(string_id, string); + return Set(string_id, string); } Game_Strings::Str_t Game_Strings::Cat(int string_id, Str_t string) { - if (!ResizeWithId(string_id)) return ""; + if (!ResizeWithId(string_id)) return ""; - Str_t s = Get(string_id); - std::string op_string = static_cast(s); - op_string.append(static_cast(string)); - Set(string_id, static_cast(op_string)); - return s; + Str_t s = Get(string_id); + std::string op_string = static_cast(s); + op_string.append(static_cast(string)); + Set(string_id, static_cast(op_string)); + return s; } int Game_Strings::ToNum(int string_id, int var_id) { - if (!ResizeWithId(string_id)) return -1; + if (!ResizeWithId(string_id)) return -1; - int num = std::stoi(static_cast(Get(string_id))); - Main_Data::game_variables->Set(var_id, num); - return num; + int num = std::stoi(static_cast(Get(string_id))); + Main_Data::game_variables->Set(var_id, num); + return num; } int Game_Strings::GetLen(int string_id, int var_id) { - if (!ResizeWithId(string_id)) return -1; + if (!ResizeWithId(string_id)) return -1; - int len = static_cast(Get(string_id)).length(); - Main_Data::game_variables->Set(var_id, len); - return len; + int len = static_cast(Get(string_id)).length(); + Main_Data::game_variables->Set(var_id, len); + return len; } int Game_Strings::InStr(int string_id, std::string search, int var_id, int begin) { - if (!ResizeWithId(string_id)) return -1; + if (!ResizeWithId(string_id)) return -1; - int index = static_cast(Get(string_id)).find(search, begin); - Main_Data::game_variables->Set(var_id, index); + int index = static_cast(Get(string_id)).find(search, begin); + Main_Data::game_variables->Set(var_id, index); + return index; } int Game_Strings::Split(int string_id, std::string delimiter, int string_out_id, int var_id) { - if (!ResizeWithId(string_id)) return -1; - - // always returns at least 1 - int splits = 1; - size_t index = 0; - std::string str = static_cast(Get(string_id)); - std::string token; - - while (index = str.find(delimiter) != std::string::npos) { - token = str.substr(0, index); - Set(string_out_id++, static_cast(token)); - splits++; - str.erase(0, index + delimiter.length()); - } - - // set the remaining string - Set(string_out_id, static_cast(str)); - Main_Data::game_variables->Set(var_id, splits); - return splits; + if (!ResizeWithId(string_id)) return -1; + + // always returns at least 1 + int splits = 1; + size_t index = 0; + std::string str = static_cast(Get(string_id)); + std::string token; + + while (index = str.find(delimiter) != std::string::npos) { + token = str.substr(0, index); + Set(string_out_id++, static_cast(token)); + splits++; + str.erase(0, index + delimiter.length()); + } + + // set the remaining string + Set(string_out_id, static_cast(str)); + Main_Data::game_variables->Set(var_id, splits); + return splits; } Game_Strings::Str_t Game_Strings::PopLine(int string_id, int offset, int string_out_id) { - if (!ResizeWithId(string_id)) return ""; + if (!ResizeWithId(string_id)) return ""; - int index; - std::string result; - std::string str = static_cast(Get(string_id)); + int index; + std::string result; + std::string str = static_cast(Get(string_id)); - std::stringstream ss(str); + std::stringstream ss(str); - while (offset >= 0 && std::getline(ss, result)) { offset--; } + while (offset >= 0 && std::getline(ss, result)) { offset--; } - offset = ss.rdbuf()->in_avail(); + offset = ss.rdbuf()->in_avail(); - Set(string_id, static_cast(ss.str().substr(str.length() - offset))); - return Set(string_out_id, static_cast(result)); + Set(string_id, static_cast(ss.str().substr(str.length() - offset))); + return Set(string_out_id, static_cast(result)); +} + +Game_Strings::Str_t Game_Strings::ExMatch(int string_id, std::string expr, int var_id, int begin, int string_out_id) { + int var_result; + Str_t str_result; + std::smatch match; + + std::string base = static_cast(Get(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); + + str_result = static_cast(match.str()); + if (string_out_id > 0) { + Set(string_out_id, str_result); + } + return str_result; } const Game_Strings::Strings_t& Game_Strings::RangeOp(int string_id_0, int string_id_1, Str_t string, int op, int args[]) { - if (EP_UNLIKELY(ShouldWarn(string_id_0))) { - WarnGet(string_id_0); - } - if (EP_UNLIKELY(ShouldWarn(string_id_1))) { - WarnGet(string_id_1); - } - if (string_id_0 <= 0 && string_id_1 <= 0) { return GetData(); } - - // maniacs just ignores if only one of the params is <= 0 - if (string_id_0 <= 0) { string_id_0 = 1; } - if (string_id_1 <= 0) { string_id_1 = 1; } - - // swap so that id_0 is < id_1 - if (string_id_0 > string_id_1) { - string_id_0 = string_id_0 ^ string_id_1; - string_id_1 = string_id_1 ^ string_id_0; - string_id_0 = string_id_0 ^ string_id_1; - } - - if (EP_UNLIKELY(string_id_1 > static_cast(_strings.size()))) { - _strings.resize(string_id_1, ""); - } - - for (int i = string_id_0; i <= string_id_1; i++) { - switch (op) { - case 0: Asg(i, string); break; - case 1: Cat(i, string); break; - case 2: ToNum(i, args[0] + (i - string_id_0)); break; - case 3: GetLen(i, args[0] + (i - string_id_0)); break; - case 4: InStr(i, static_cast(string), args[1], args[2]); break; - case 5: i += Split(i, static_cast(string), args[1], args[2]); break; - case 8: break; // range case not applicable for popLine; see case in game_interpreter.cpp - } - } - return GetData(); + if (EP_UNLIKELY(ShouldWarn(string_id_0))) { + WarnGet(string_id_0); + } + if (EP_UNLIKELY(ShouldWarn(string_id_1))) { + WarnGet(string_id_1); + } + if (string_id_0 <= 0 && string_id_1 <= 0) { return GetData(); } + + // maniacs just ignores if only one of the params is <= 0 + if (string_id_0 <= 0) { string_id_0 = 1; } + if (string_id_1 <= 0) { string_id_1 = 1; } + + // swap so that id_0 is < id_1 + if (string_id_0 > string_id_1) { + string_id_0 = string_id_0 ^ string_id_1; + string_id_1 = string_id_1 ^ string_id_0; + string_id_0 = string_id_0 ^ string_id_1; + } + + if (EP_UNLIKELY(string_id_1 > static_cast(_strings.size()))) { + _strings.resize(string_id_1, ""); + } + + for (int i = string_id_0; i <= string_id_1; i++) { + switch (op) { + case 0: Asg(i, string); break; + case 1: Cat(i, string); break; + case 2: ToNum(i, args[0] + (i - string_id_0)); break; + case 3: GetLen(i, args[0] + (i - string_id_0)); break; + case 4: InStr(i, static_cast(string), args[1], args[2]); break; + case 5: i += Split(i, static_cast(string), args[1], args[2]); break; + case 8: break; // range case not applicable for popLine; see case in game_interpreter.cpp + case 9: ExMatch(i, static_cast(string), args[1] + (i - string_id_0), args[2]); break; + case 10: ExMatch(i, static_cast(string), args[1] + (i - string_id_0), args[2], args[3]); break; + } + } + return GetData(); } Game_Strings::Str_t Game_Strings::PrependMin(Str_t 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 (Str_t)res; - } - return string; + if (string.size() < min_size) { + int s = min_size - string.size(); + std::string res = std::string(s, c) + (std::string)string; + return (Str_t)res; + } + return string; } diff --git a/src/game_strings.h b/src/game_strings.h index 36ea5375f1..7dcd3f9236 100644 --- a/src/game_strings.h +++ b/src/game_strings.h @@ -52,6 +52,7 @@ class Game_Strings { int InStr(int string_id, std::string search, int var_id, int begin = 0); int Split(int string_id, std::string delimiter, int string_out_id, int var_id); Str_t PopLine(int string_id, int offset, int string_out_id); + Str_t ExMatch(int string_id, std::string expr, int var_id, int begin, int string_out_id = -1); const Strings_t& RangeOp(int string_id_0, int string_id_1, Str_t string, int op, int args[] = nullptr); From 77e304a62c3ec3acb96c64ecbeea8da44ac3a053 Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Thu, 29 Jun 2023 16:59:21 -0600 Subject: [PATCH 16/47] StringVar: first pass at extract functionality --- src/game_interpreter.cpp | 46 ++++---- src/game_strings.cpp | 237 ++++++++++++++++++++------------------- src/game_strings.h | 47 +++++--- src/pending_message.cpp | 15 ++- src/pending_message.h | 5 +- 5 files changed, 189 insertions(+), 161 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 2f3b990931..a4117f5072 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -4671,7 +4671,6 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& int string_mode = com.parameters[0] & 15; int string_id_0 = com.parameters[1]; int string_id_1 = com.parameters[2]; //for ranges - // Output::Debug("string_mode: {:b}, id1: {}, id2: {}", string_mode, string_id_0, string_id_1); int is_range = string_mode & 1; @@ -4686,9 +4685,9 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& int fn = (com.parameters[3] >> 8) & 255; int flags = (com.parameters[3] >> 16) & 255; + int hex_flag = (flags >> 1) & 1; + int extract_flag = (flags >> 2) & 1; int first_flag = (flags >> 3) & 1; - int hex_flag = (flags >> 17) & 1; - int extract_flag = (flags >> 18) & 1; int args[] = { com.parameters[4], @@ -4707,12 +4706,11 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& }; Game_Strings::Str_t result = ""; - - // Output::Debug("com.string: {}", com.string); - // Output::Debug("op: {}, fn: {}", op, fn); - // Output::Debug("args[]: {} {} {} {}", args[0], args[1], args[2], args[3]); - // Output::Debug("modes[]: {} {} {} {}", modes[0], modes[1], modes[2], modes[3]); - // Output::Debug("flags: {}", flags); + Game_Strings::Str_Params str_params = { + string_id_0, + hex_flag, + extract_flag, + }; switch (op) { @@ -4861,18 +4859,18 @@ 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(string_id_0, string_id_1, result, op); + if (is_range) Main_Data::game_strings->RangeOp(str_params, string_id_1, result, op); else { - if (op == 0) Main_Data::game_strings->Asg(string_id_0, result); - if (op == 1) Main_Data::game_strings->Cat(string_id_0, result); + if (op == 0) Main_Data::game_strings->Asg(str_params, result); + if (op == 1) Main_Data::game_strings->Cat(str_params, result); } break; case 2: //toNum case 3: //getLen - if (is_range) Main_Data::game_strings->RangeOp(string_id_0, string_id_1, result, op, args); + if (is_range) Main_Data::game_strings->RangeOp(str_params, string_id_1, result, op, args); else { - if (op == 2) Main_Data::game_strings->ToNum(string_id_0, args[0]); - if (op == 3) Main_Data::game_strings->GetLen(string_id_0, args[0]); + if (op == 2) Main_Data::game_strings->ToNum(str_params, args[0]); + if (op == 3) Main_Data::game_strings->GetLen(str_params, args[0]); } break; case 4: //inStr @@ -4881,8 +4879,8 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); // not sure this is necessary but better safe args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[2]); - if (is_range) Main_Data::game_strings->RangeOp(string_id_0, string_id_1, search, op, args); - else Main_Data::game_strings->InStr(string_id_0, static_cast(search), args[1], 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, static_cast(search), args[1], args[2]); break; } case 5: //split @@ -4891,8 +4889,8 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[2]); - if (is_range) Main_Data::game_strings->RangeOp(string_id_0, string_id_1, delimiter, op, args); - else Main_Data::game_strings->Split(string_id_0, static_cast(delimiter), args[1], 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, static_cast(delimiter), args[1], args[2]); break; } case 7: //toFile @@ -4904,8 +4902,8 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& // and store the last popped line into the output string. args[1] = Main_Data::game_variables->GetWithMode(args[0], modes[0]); - if (is_range) Main_Data::game_strings->PopLine(string_id_0, string_id_1 - string_id_0, args[0]); - else Main_Data::game_strings->PopLine(string_id_0, 0, args[0]); + if (is_range) Main_Data::game_strings->PopLine(str_params, string_id_1 - string_id_0, args[0]); + else Main_Data::game_strings->PopLine(str_params, 0, args[0]); break; case 9: //exInStr case 10: //exMatch , edge case: the only command that generates 8 parameters instead of 7 @@ -4915,11 +4913,11 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[2]); // beginning pos if (is_range) { - Main_Data::game_strings->RangeOp(string_id_0, string_id_1, static_cast(expr), op, args); + Main_Data::game_strings->RangeOp(str_params, string_id_1, static_cast(expr), op, args); } else { - if (op == 9) Main_Data::game_strings->ExMatch(string_id_0, expr, args[1], args[2]); - else Main_Data::game_strings->ExMatch(string_id_0, expr, args[1], args[2], args[3]); + 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]); } break; } diff --git a/src/game_strings.cpp b/src/game_strings.cpp index c44225cffd..11499ae490 100644 --- a/src/game_strings.cpp +++ b/src/game_strings.cpp @@ -25,152 +25,161 @@ Game_Strings::Game_Strings() {} void Game_Strings::WarnGet(int id) const { - Output::Debug("Invalid read strvar[{}]!", id); - --_warnings; + Output::Debug("Invalid read strvar[{}]!", id); + --_warnings; } -Game_Strings::Str_t Game_Strings::Asg(int string_id, Str_t string) { - return Set(string_id, string); +Game_Strings::Str_t Game_Strings::Asg(Str_Params params, Str_t string) { + return Set(params, string); } -Game_Strings::Str_t Game_Strings::Cat(int string_id, Str_t string) { - if (!ResizeWithId(string_id)) return ""; +Game_Strings::Str_t Game_Strings::Cat(Str_Params params, Str_t string) { + if (!ResizeWithId(params.string_id)) return ""; - Str_t s = Get(string_id); - std::string op_string = static_cast(s); - op_string.append(static_cast(string)); - Set(string_id, static_cast(op_string)); - return s; + Str_t s = Get(params.string_id); + std::string op_string = static_cast(s); + op_string.append(static_cast(string)); + Set(params, static_cast(op_string)); + return s; } -int Game_Strings::ToNum(int string_id, int var_id) { - if (!ResizeWithId(string_id)) return -1; +int Game_Strings::ToNum(Str_Params params, int var_id) { + if (!ResizeWithId(params.string_id)) return -1; - int num = std::stoi(static_cast(Get(string_id))); - Main_Data::game_variables->Set(var_id, num); - return num; + int num = std::stoi(static_cast(Get(params.string_id))); + Main_Data::game_variables->Set(var_id, num); + return num; } -int Game_Strings::GetLen(int string_id, int var_id) { - if (!ResizeWithId(string_id)) return -1; +int Game_Strings::GetLen(Str_Params params, int var_id) { + if (!ResizeWithId(params.string_id)) return -1; - int len = static_cast(Get(string_id)).length(); - Main_Data::game_variables->Set(var_id, len); - return len; + int len = static_cast(Get(params.string_id)).length(); + Main_Data::game_variables->Set(var_id, len); + return len; } -int Game_Strings::InStr(int string_id, std::string search, int var_id, int begin) { - if (!ResizeWithId(string_id)) return -1; +int Game_Strings::InStr(Str_Params params, std::string search, int var_id, int begin) { + if (!ResizeWithId(params.string_id)) return -1; - int index = static_cast(Get(string_id)).find(search, begin); - Main_Data::game_variables->Set(var_id, index); - return index; + int index = static_cast(Get(params.string_id)).find(search, begin); + Main_Data::game_variables->Set(var_id, index); + return index; } -int Game_Strings::Split(int string_id, std::string delimiter, int string_out_id, int var_id) { - if (!ResizeWithId(string_id)) return -1; - - // always returns at least 1 - int splits = 1; - size_t index = 0; - std::string str = static_cast(Get(string_id)); - std::string token; - - while (index = str.find(delimiter) != std::string::npos) { - token = str.substr(0, index); - Set(string_out_id++, static_cast(token)); - splits++; - str.erase(0, index + delimiter.length()); - } - - // set the remaining string - Set(string_out_id, static_cast(str)); - Main_Data::game_variables->Set(var_id, splits); - return splits; +int Game_Strings::Split(Str_Params params, std::string delimiter, int string_out_id, int var_id) { + if (!ResizeWithId(params.string_id)) return -1; + + // always returns at least 1 + int splits = 1; + size_t index = 0; + std::string str = static_cast(Get(params.string_id)); + std::string token; + + params.string_id = string_out_id; + + while (index = str.find(delimiter) != std::string::npos) { + token = str.substr(0, index); + Set(params, static_cast(token)); + params.string_id++; + splits++; + str.erase(0, index + delimiter.length()); + } + + // set the remaining string + Set(params, static_cast(str)); + Main_Data::game_variables->Set(var_id, splits); + return splits; } -Game_Strings::Str_t Game_Strings::PopLine(int string_id, int offset, int string_out_id) { - if (!ResizeWithId(string_id)) return ""; +Game_Strings::Str_t Game_Strings::PopLine(Str_Params params, int offset, int string_out_id) { + if (!ResizeWithId(params.string_id)) return ""; - int index; - std::string result; - std::string str = static_cast(Get(string_id)); + int index; + std::string result; + std::string str = static_cast(Get(params.string_id)); - std::stringstream ss(str); + std::stringstream ss(str); - while (offset >= 0 && std::getline(ss, result)) { offset--; } + while (offset >= 0 && std::getline(ss, result)) { offset--; } - offset = ss.rdbuf()->in_avail(); + offset = ss.rdbuf()->in_avail(); - Set(string_id, static_cast(ss.str().substr(str.length() - offset))); - return Set(string_out_id, static_cast(result)); + Set(params, static_cast(ss.str().substr(str.length() - offset))); + params.string_id = string_out_id; + return Set(params, static_cast(result)); } -Game_Strings::Str_t Game_Strings::ExMatch(int string_id, std::string expr, int var_id, int begin, int string_out_id) { - int var_result; - Str_t str_result; - std::smatch match; +Game_Strings::Str_t Game_Strings::ExMatch(Str_Params params, std::string expr, int var_id, int begin, int string_out_id) { + int var_result; + Str_t str_result; + std::smatch match; - std::string base = static_cast(Get(string_id)).erase(0, begin); - std::regex r(expr); + std::string base = static_cast(Get(params.string_id)).erase(0, begin); + std::regex r(expr); - std::regex_search(base, match, r); + std::regex_search(base, match, r); - var_result = match.position() + begin; - Main_Data::game_variables->Set(var_id, var_result); + var_result = match.position() + begin; + Main_Data::game_variables->Set(var_id, var_result); - str_result = static_cast(match.str()); - if (string_out_id > 0) { - Set(string_out_id, str_result); - } - return str_result; + str_result = static_cast(match.str()); + if (string_out_id > 0) { + params.string_id = string_out_id; + Set(params, str_result); + } + return str_result; } -const Game_Strings::Strings_t& Game_Strings::RangeOp(int string_id_0, int string_id_1, Str_t string, int op, int args[]) { - if (EP_UNLIKELY(ShouldWarn(string_id_0))) { - WarnGet(string_id_0); - } - if (EP_UNLIKELY(ShouldWarn(string_id_1))) { - WarnGet(string_id_1); - } - if (string_id_0 <= 0 && string_id_1 <= 0) { return GetData(); } - - // maniacs just ignores if only one of the params is <= 0 - if (string_id_0 <= 0) { string_id_0 = 1; } - if (string_id_1 <= 0) { string_id_1 = 1; } - - // swap so that id_0 is < id_1 - if (string_id_0 > string_id_1) { - string_id_0 = string_id_0 ^ string_id_1; - string_id_1 = string_id_1 ^ string_id_0; - string_id_0 = string_id_0 ^ string_id_1; - } - - if (EP_UNLIKELY(string_id_1 > static_cast(_strings.size()))) { - _strings.resize(string_id_1, ""); - } - - for (int i = string_id_0; i <= string_id_1; i++) { - switch (op) { - case 0: Asg(i, string); break; - case 1: Cat(i, string); break; - case 2: ToNum(i, args[0] + (i - string_id_0)); break; - case 3: GetLen(i, args[0] + (i - string_id_0)); break; - case 4: InStr(i, static_cast(string), args[1], args[2]); break; - case 5: i += Split(i, static_cast(string), args[1], args[2]); break; - case 8: break; // range case not applicable for popLine; see case in game_interpreter.cpp - case 9: ExMatch(i, static_cast(string), args[1] + (i - string_id_0), args[2]); break; - case 10: ExMatch(i, static_cast(string), args[1] + (i - string_id_0), args[2], args[3]); break; - } - } - return GetData(); +const Game_Strings::Strings_t& Game_Strings::RangeOp(Str_Params params, int string_id_1, Str_t string, int op, int args[]) { + if (EP_UNLIKELY(ShouldWarn(params.string_id))) { + WarnGet(params.string_id); + } + if (EP_UNLIKELY(ShouldWarn(string_id_1))) { + WarnGet(string_id_1); + } + if (params.string_id <= 0 && string_id_1 <= 0) { return GetData(); } + + // maniacs just ignores if only one of the params is <= 0 + if (params.string_id <= 0) { params.string_id = 1; } + if (string_id_1 <= 0) { string_id_1 = 1; } + + // swap so that id_0 is < id_1 + if (params.string_id > string_id_1) { + params.string_id = params.string_id ^ string_id_1; + string_id_1 = string_id_1 ^ params.string_id; + params.string_id = 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, static_cast(string), args[1], args[2]); break; + case 5: params.string_id += Split(params, static_cast(string), args[1], args[2]); break; + case 8: break; // range case not applicable for popLine; see case in game_interpreter.cpp + case 9: ExMatch(params, static_cast(string), args[1] + (params.string_id - start), args[2]); break; + case 10: ExMatch(params, static_cast(string), args[1] + (params.string_id - start), args[2], args[3]); break; + } + } + return GetData(); } Game_Strings::Str_t Game_Strings::PrependMin(Str_t 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 (Str_t)res; - } - return string; + if (string.size() < min_size) { + int s = min_size - string.size(); + std::string res = std::string(s, c) + (std::string)string; + return (Str_t)res; + } + return string; +} + +inline Game_Strings::Str_t Game_Strings::Extract(Str_t string) { + return static_cast(PendingMessage::ApplyTextInsertingCommands(static_cast(string), Player::escape_char, true)); } diff --git a/src/game_strings.h b/src/game_strings.h index 7dcd3f9236..d89cd11e49 100644 --- a/src/game_strings.h +++ b/src/game_strings.h @@ -16,14 +16,16 @@ */ // Headers -#include -#include "compiler.h" -#include "string_view.h" #include #include -#include "output.h" -#include "main_data.h" +#include +#include "compiler.h" #include "game_variables.h" +#include "main_data.h" +#include "output.h" +#include "pending_message.h" +#include "player.h" +#include "string_view.h" /** * Game_Strings class. @@ -35,6 +37,10 @@ class Game_Strings { static constexpr int max_warnings = 10; + struct Str_Params { + int string_id, hex, extract; + }; + Game_Strings(); void SetData(Strings_t s); @@ -45,34 +51,39 @@ class Game_Strings { 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); - Str_t Asg(int string_id, Str_t string); - Str_t Cat(int string_id, Str_t string); - int ToNum(int string_id, int var_id); - int GetLen(int string_id, int var_id); - int InStr(int string_id, std::string search, int var_id, int begin = 0); - int Split(int string_id, std::string delimiter, int string_out_id, int var_id); - Str_t PopLine(int string_id, int offset, int string_out_id); - Str_t ExMatch(int string_id, std::string expr, int var_id, int begin, int string_out_id = -1); + 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); + 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); - const Strings_t& RangeOp(int string_id_0, int string_id_1, Str_t string, int op, int args[] = nullptr); + const Strings_t& RangeOp(Str_Params params, int string_id_1, Str_t string, int op, int args[] = nullptr); Str_t PrependMin(Str_t string, int min_size, char c); + private: - Str_t Set(int string_id, Str_t string); + Str_t Set(Str_Params params, Str_t string); bool ResizeWithId(int id); bool ShouldWarn(int id) const; void WarnGet(int id) const; + Str_t Extract(Str_t string); private: Strings_t _strings; mutable int _warnings = max_warnings; }; -inline Game_Strings::Str_t Game_Strings::Set(int string_id, Str_t string) { - if (!ResizeWithId(string_id)) return ""; - auto& s = _strings[string_id - 1]; +inline Game_Strings::Str_t Game_Strings::Set(Str_Params params, Str_t string) { + if (!ResizeWithId(params.string_id)) return ""; + + auto& s = _strings[params.string_id - 1]; s = string; + if (params.extract) + s = Extract(s); return s; } diff --git a/src/pending_message.cpp b/src/pending_message.cpp index e3e3968478..04e57f8087 100644 --- a/src/pending_message.cpp +++ b/src/pending_message.cpp @@ -20,6 +20,7 @@ #include "game_strings.h" #include "game_actors.h" #include "game_message.h" +#include "game_switches.h" #include #include "output.h" #include "utils.h" @@ -87,7 +88,8 @@ void PendingMessage::SetChoiceResetColors(bool value) { choice_reset_color = value; } -std::string PendingMessage::ApplyTextInsertingCommands(std::string input, uint32_t escape_char, CommandInserter cmd_fn) { + +std::string PendingMessage::ApplyTextInsertingCommands(std::string input, uint32_t escape_char, CommandInserter cmd_fn, bool maniacs_parsing) { if (input.empty()) { return input; } @@ -133,6 +135,17 @@ std::string PendingMessage::ApplyTextInsertingCommands(std::string input, uint32 Game_Strings::Str_t string = Main_Data::game_strings->Get(value); output.append((std::string)string); + start_copy = iter; + } else if (maniacs_parsing && (ch == 'S' || ch == 's')) { + // parsing a switch within an extracted string var command will parse \s[N] as a switch (ON/OFF) + auto parse_ret = Game_Message::ParseSpeed(iter, end, escape_char, true); + iter = parse_ret.next; + int value = parse_ret.value; + + bool sw = Main_Data::game_switches->Get(value); + if (sw) output.append("ON"); + else output.append("OFF"); + start_copy = iter; } } diff --git a/src/pending_message.h b/src/pending_message.h index b34b3681ad..33f4862ad3 100644 --- a/src/pending_message.h +++ b/src/pending_message.h @@ -67,13 +67,10 @@ class PendingMessage { void SetIsEventMessage(bool value) { is_event_message = value; } bool IsEventMessage() const { return is_event_message; } + static std::string ApplyTextInsertingCommands(std::string input, uint32_t escape_char, CommandInserter cmd_fn, bool maniacs_parsing = false); private: int PushLineImpl(std::string msg, CommandInserter cmd_fn); - - std::string ApplyTextInsertingCommands(std::string input, uint32_t escape_char, CommandInserter cmd_fn); - - private: ChoiceContinuation choice_continuation; std::vector texts; int choice_start = -1; From 89027524056dafb4b549ce40b374eb2b02464b59 Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Thu, 29 Jun 2023 21:10:05 -0600 Subject: [PATCH 17/47] StringVar: support entity description assignment/concat --- src/game_interpreter.cpp | 44 +++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index a4117f5072..e78a1b0815 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -4712,6 +4712,12 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& extract_flag, }; + Output::Debug("string_mode {} string_id_0 {}", string_mode, string_id_0); + Output::Debug("op {} fn {} flags {}", op, fn, flags); + Output::Debug("hex {} extractt {} first {}", hex_flag, extract_flag, first_flag); + Output::Debug("args {} {} {} {}", args[0], args[1], args[2], args[3]); + Output::Debug("modes {} {} {} {}", modes[0], modes[1], modes[2], modes[3]); + switch (op) { case 0: //asg @@ -4738,12 +4744,8 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& switch (args[0]) { case 0: //.actor[a].name - if (args[2]) { - result = (Game_Strings::Str_t)Main_Data::game_actors->GetActor(args[1])->GetName(); - } - else { - result = (Game_Strings::Str_t)lcf::ReaderUtil::GetElement(lcf::Data::actors, args[1])->name; - } + if (args[2]) result = static_cast(Main_Data::game_actors->GetActor(args[1])->GetName()); + else result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::actors, args[1])->name); break; case 1: result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::skills, args[1])->name); break; //.skill[a].name case 2: result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::items, args[1])->name); break; //.item[a].name @@ -4767,10 +4769,38 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& case 15: result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::battleranimations, args[1])->name); break; //.anim2[a].name case 16: result = static_cast(Game_Map::GetMapName(args[1])); break; //.map[a].name case 17: result = static_cast(Game_Map::GetEvent(args[1])->GetName()); break; //.mev[a].name - case 18: result = static_cast(Main_Data::game_party->GetActor(args[1])->GetName()); break; //.member[a].name + case 18: //.member[a].name + if (args[2]) { + result = static_cast(Main_Data::game_party->GetActor(args[1])->GetName()); + } + else { + args[1] = Main_Data::game_party->GetActor(args[1])->GetId(); + result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::actors, args[1])->name); + } + break; } break; case 4: //Database Descriptions + args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); + + switch (args[0]) + { + case 0: //.actor[a].desc + if (args[2]) result = static_cast(Main_Data::game_actors->GetActor(args[1])->GetTitle()); + else result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::actors, args[1])->title); + break; + case 1: result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::skills, args[1])->description); break; //.skill[a].desc + case 2: result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::items, args[1])->description); break; //.item[a].desc + case 18: //.member[a].desc + if (args[2]) { + result = static_cast(Main_Data::game_party->GetActor(args[1])->GetTitle()); + } + else { + args[1] = Main_Data::game_party->GetActor(args[1])->GetId(); + result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::actors, args[1])->title); + } + break; + } break; case 6: //Concatenate (cat) { From c2147398c1f01080e35730e5afd9081c064c834b Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Fri, 30 Jun 2023 16:21:17 -0600 Subject: [PATCH 18/47] StringVar: add read file to string --- src/filefinder.cpp | 11 +++++++++++ src/filefinder.h | 27 ++++++++++++++++++++++----- src/game_interpreter.cpp | 15 +++++++++++++++ 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/src/filefinder.cpp b/src/filefinder.cpp index 1836f70bda..50ba96300e 100644 --- a/src/filefinder.cpp +++ b/src/filefinder.cpp @@ -68,6 +68,7 @@ namespace { constexpr const auto SOUND_TYPES = Utils::MakeSvArray( ".opus", ".oga", ".ogg", ".wav", ".mp3", ".wma"); constexpr const auto FONTS_TYPES = Utils::MakeSvArray(".fon", ".fnt", ".bdf", ".ttf", ".ttc", ".otf", ".woff2", ".woff"); + constexpr const auto TEXT_TYPES = Utils::MakeSvArray(".txt", ".csv"); } FilesystemView FileFinder::Game() { @@ -358,6 +359,11 @@ std::string FileFinder::FindFont(StringView name) { return find_generic(args); } +std::string FileFinder::FindText(StringView name) { + DirectoryTree::Args args = { MakePath("Text", name), TEXT_TYPES, 1, true }; + return find_generic(args); +} + Filesystem_Stream::InputStream open_generic(StringView dir, StringView name, DirectoryTree::Args& args) { if (!Tr::GetCurrentTranslationId().empty()) { auto tr_fs = Tr::GetCurrentTranslationFilesystem(); @@ -397,6 +403,11 @@ Filesystem_Stream::InputStream FileFinder::OpenFont(StringView name) { return open_generic("Font", name, args); } +Filesystem_Stream::InputStream FileFinder::OpenText(StringView name) { + DirectoryTree::Args args = { MakePath("Text", name), TEXT_TYPES, 1, false }; + return open_generic("Text", name, args); +} + bool FileFinder::IsMajorUpdatedTree() { auto fs = Game(); assert(fs); diff --git a/src/filefinder.h b/src/filefinder.h index 6411e373ad..b4b400fa54 100644 --- a/src/filefinder.h +++ b/src/filefinder.h @@ -97,6 +97,23 @@ namespace FileFinder { */ std::string FindSound(StringView name); + /** + * Finds a font file. + * Searches through the current RPG Maker game and the RTP directories. + * + * @param name the font name. + * @return path to file. + */ + std::string FindFont(StringView name); + + /** + * Finds a text file in the current RPG Maker game. + * + * @param name the text path and name. + * @return path to file. + */ + std::string FindText(StringView name); + /** * Finds an image file and opens a file handle to it. * Searches through the current RPG Maker game and the RTP directories. @@ -137,13 +154,13 @@ namespace FileFinder { Filesystem_Stream::InputStream OpenFont(StringView name); /** - * Finds a font file. - * Searches through the current RPG Maker game and the RTP directories. + * Finds a textt file and opens a file handle to it. + * Searches through the Text folder of the current RPG Maker game. * - * @param name the font name. - * @return path to file. + * @param name the text path and name. + * @return read handle on success or invalid handle if not found */ - std::string FindFont(StringView name); + Filesystem_Stream::InputStream OpenText(StringView name); /** * Appends name to directory. diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index e78a1b0815..8abc20421c 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -4712,6 +4712,7 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& extract_flag, }; + Output::Debug("com.string: {}", com.string); Output::Debug("string_mode {} string_id_0 {}", string_mode, string_id_0); Output::Debug("op {} fn {} flags {}", op, fn, flags); Output::Debug("hex {} extractt {} first {}", hex_flag, extract_flag, first_flag); @@ -4864,7 +4865,20 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& break; } case 12: //File (file) + { + // maniacs does not like a file extension + Game_Strings::Str_t filename = Main_Data::game_strings->GetWithMode(com.string, args[0], modes[0]); + // args[1] is the encoding... 0 for sjis, 1 for utf8 + + Filesystem_Stream::InputStream is = FileFinder::OpenText(StringView(filename)); + if (is.good()) { + std::stringstream buffer; + buffer << is.rdbuf(); + result = static_cast(buffer.str()); + } + is.Close(); break; + } case 13: //Remove (rem) args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[2]); @@ -4924,6 +4938,7 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& break; } case 7: //toFile + break; case 8: //popLine // a range parameter with popLine doesn't affect multiple strings; From fac88cad714ab4dd035e5f84ba461e92f141a277 Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Mon, 3 Jul 2023 13:48:25 -0600 Subject: [PATCH 19/47] StringVar: first pass at file writing support --- src/filefinder.cpp | 10 ++++++++++ src/filefinder.h | 13 ++++++++++++- src/game_interpreter.cpp | 7 ++++++- src/game_strings.cpp | 19 +++++++++++++++++++ src/game_strings.h | 1 + 5 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/filefinder.cpp b/src/filefinder.cpp index 50ba96300e..1e69768142 100644 --- a/src/filefinder.cpp +++ b/src/filefinder.cpp @@ -383,6 +383,16 @@ Filesystem_Stream::InputStream open_generic(StringView dir, StringView name, Dir return is; } +Filesystem_Stream::InputStream open_generic_with_fallback(StringView dir, StringView name, DirectoryTree::Args& args) { + auto is = open_generic(dir, name, args); + if (!is) { is = FileFinder::Save().OpenFile(args); } + if (!is) { + Output::Debug("Unable to find in either Game or Save: {}/{}", dir, name); + } + + return is; +} + Filesystem_Stream::InputStream FileFinder::OpenImage(StringView dir, StringView name) { DirectoryTree::Args args = { MakePath(dir, name), IMG_TYPES, 1, false }; return open_generic(dir, name, args); diff --git a/src/filefinder.h b/src/filefinder.h index b4b400fa54..8f6be1ed68 100644 --- a/src/filefinder.h +++ b/src/filefinder.h @@ -154,14 +154,25 @@ namespace FileFinder { Filesystem_Stream::InputStream OpenFont(StringView name); /** - * Finds a textt file and opens a file handle to it. + * Finds a text file and opens a file handle to it. * Searches through the Text folder of the current RPG Maker game. + * Will also search through the directory save files are written to as a fallback, + * as it needs to account for files written by the game as well. * * @param name the text path and name. * @return read handle on success or invalid handle if not found */ Filesystem_Stream::InputStream OpenText(StringView name); + /** + * Writes data to a txt file. + * If the file exists, it will be overwritten. + * + * @param name the text file path and name + * @param data the content of the text file to be written + */ + void WriteText(StringView name, StringView data); + /** * Appends name to directory. * diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 8abc20421c..bb663eebbd 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -822,7 +822,7 @@ bool Game_Interpreter::ExecuteCommand(lcf::rpg::EventCommand const& com) { return CommandManiacChangePictureId(com); case Cmd::Maniac_SetGameOption: return CommandManiacSetGameOption(com); - case 3020: //Cmd::Maniac_ControlStrings + case Cmd::Maniac_ControlStrings: return CommandManiacControlStrings(com); case Cmd::Maniac_CallCommand: return CommandManiacCallCommand(com); @@ -4938,8 +4938,13 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& break; } case 7: //toFile + { + std::string filename = static_cast(Main_Data::game_strings->GetWithMode(com.string, args[0], modes[0])); + args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); + Main_Data::game_strings->ToFile(str_params, filename, args[1]); break; + } case 8: //popLine // a range parameter with popLine doesn't affect multiple strings; // it instead alters the behavior. diff --git a/src/game_strings.cpp b/src/game_strings.cpp index 11499ae490..1c8f6f109f 100644 --- a/src/game_strings.cpp +++ b/src/game_strings.cpp @@ -92,6 +92,25 @@ int Game_Strings::Split(Str_Params params, std::string delimiter, int string_out return splits; } +Game_Strings::Str_t Game_Strings::ToFile(Str_Params params, std::string filename, int encoding) { + std::string str = static_cast(Get(params.string_id)); + + // this sucks but it is what maniacs does + filename = "Text/" + filename + ".txt"; + + auto txt_out = FileFinder::Game().OpenOutputStream(filename); + if (!txt_out) { txt_out = FileFinder::Save().OpenOutputStream(filename); } + if (!txt_out) { + Output::Warning("Maniac String Op toFile failed!"); + return ""; + } + + txt_out << str; + txt_out.Close(); + + return static_cast(str); +} + Game_Strings::Str_t Game_Strings::PopLine(Str_Params params, int offset, int string_out_id) { if (!ResizeWithId(params.string_id)) return ""; diff --git a/src/game_strings.h b/src/game_strings.h index d89cd11e49..33e1fbe362 100644 --- a/src/game_strings.h +++ b/src/game_strings.h @@ -57,6 +57,7 @@ class Game_Strings { 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); + 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); From 82ee999969cba2bdfe9f2955020ffe5b6923ad2a Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Mon, 3 Jul 2023 16:34:37 -0600 Subject: [PATCH 20/47] StringVar: add support for string conditionals --- src/game_interpreter.cpp | 52 +++++++++++++++++++++++++++++++++++----- src/game_interpreter.h | 1 + 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index bb663eebbd..35598ada32 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -3603,6 +3603,24 @@ bool Game_Interpreter::CommandConditionalBranch(lcf::rpg::EventCommand const& co result = CheckOperator(value1, value2, com.parameters[4]); } break; + case 15: + //Maniac: string comparison + if (Player::IsPatchManiac()) { + int modes[] = { + (com.parameters[1] ) & 15, //str_l mode: 0 = direct, 1 = indirect + (com.parameters[1] >> 4) & 15, //str_r mode: 0 = literal, 1 = direct, 2 = indirect + }; + + int op = com.parameters[4] & 3; + int ignoreCase = com.parameters[4] >> 8 & 1; + + //Output::Debug("conditional string {}", com.string); + //Output::Debug("params {} {} {} {} {}", com.parameters[0], com.parameters[1], com.parameters[2], com.parameters[3], com.parameters[4]); + std::string str_l = static_cast(Main_Data::game_strings->GetWithMode(com.string, com.parameters[2], modes[0]+1)); + std::string str_r = static_cast(Main_Data::game_strings->GetWithMode(com.string, com.parameters[3], modes[1])); + result = ManiacCheckString(str_l, str_r, op, ignoreCase); + } + break; default: Output::Warning("ConditionalBranch: Branch {} unsupported", com.parameters[0]); } @@ -4702,6 +4720,7 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& (com.parameters[0] >> 4) & 15, (com.parameters[0] >> 8) & 15, (com.parameters[0] >> 12) & 15, + (com.parameters[0] >> 16) & 15 }; @@ -4712,12 +4731,12 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& extract_flag, }; - Output::Debug("com.string: {}", com.string); - Output::Debug("string_mode {} string_id_0 {}", string_mode, string_id_0); - Output::Debug("op {} fn {} flags {}", op, fn, flags); - Output::Debug("hex {} extractt {} first {}", hex_flag, extract_flag, first_flag); - Output::Debug("args {} {} {} {}", args[0], args[1], args[2], args[3]); - Output::Debug("modes {} {} {} {}", modes[0], modes[1], modes[2], modes[3]); + //Output::Debug("com.string: {}", com.string); + //Output::Debug("string_mode {} string_id_0 {}", string_mode, string_id_0); + //Output::Debug("op {} fn {} flags {}", op, fn, flags); + //Output::Debug("hex {} extractt {} first {}", hex_flag, extract_flag, first_flag); + //Output::Debug("args {} {} {} {}", args[0], args[1], args[2], args[3]); + //Output::Debug("modes {} {} {} {}", modes[0], modes[1], modes[2], modes[3]); switch (op) { @@ -5032,3 +5051,24 @@ bool Game_Interpreter::ManiacCheckContinueLoop(int val, int val2, int type, int return false; } } + +bool Game_Interpreter::ManiacCheckString(std::string str_l, std::string str_r, int op, bool ignore_case) const { + std::regex specialChars { R"([-[\]{}()*+?.,\^$|#\s])" }; + std::string sanitized_r = std::regex_replace(str_r, specialChars, R"(\$&)"); + if (op > 1) { sanitized_r = ".*" + sanitized_r + ".*"; } + + std::regex search; + if (ignore_case) { search = std::regex(sanitized_r, std::regex_constants::icase); } + else { search = std::regex(sanitized_r); } + + switch (op) { + case 0: // eq + case 2: // contains (l contains r) + return std::regex_match(str_l, search); + case 1: // neq + case 3: // notContains (l does not contain r) + return !std::regex_match(str_l, search); + default: + return false; + } +} diff --git a/src/game_interpreter.h b/src/game_interpreter.h index b77679b966..a24c51e9dd 100644 --- a/src/game_interpreter.h +++ b/src/game_interpreter.h @@ -331,6 +331,7 @@ class Game_Interpreter bool CheckOperator(int val, int val2, int op) const; bool ManiacCheckContinueLoop(int val, int val2, int type, int op) const; + bool ManiacCheckString(std::string str_l, std::string str_r, int op, bool ignore_case) const; lcf::rpg::SaveEventExecState _state; KeyInputState _keyinput; From ad3ebdcac2215b8d1ce592af2c7e379af00e8e37 Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Tue, 4 Jul 2023 14:08:49 -0600 Subject: [PATCH 21/47] StringVar: add support for hex conversion in extract operators --- src/game_interpreter.cpp | 25 +++++++------- src/game_strings.cpp | 19 ++++++++--- src/game_strings.h | 9 +++-- src/pending_message.cpp | 72 ++++++++++++++++++++++++++++------------ src/pending_message.h | 5 ++- 5 files changed, 89 insertions(+), 41 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 35598ada32..9d20e59d62 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -4731,12 +4731,12 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& extract_flag, }; - //Output::Debug("com.string: {}", com.string); - //Output::Debug("string_mode {} string_id_0 {}", string_mode, string_id_0); - //Output::Debug("op {} fn {} flags {}", op, fn, flags); - //Output::Debug("hex {} extractt {} first {}", hex_flag, extract_flag, first_flag); - //Output::Debug("args {} {} {} {}", args[0], args[1], args[2], args[3]); - //Output::Debug("modes {} {} {} {}", modes[0], modes[1], modes[2], modes[3]); + Output::Debug("com.string: {}", com.string); + Output::Debug("string_mode {} string_id_0 {}", string_mode, string_id_0); + Output::Debug("op {} fn {} flags {}", op, fn, flags); + Output::Debug("hex {} extractt {} first {}", hex_flag, extract_flag, first_flag); + Output::Debug("args {} {} {} {}", args[0], args[1], args[2], args[3]); + Output::Debug("modes {} {} {} {}", modes[0], modes[1], modes[2], modes[3]); switch (op) { @@ -4928,7 +4928,7 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& if (op == 1) Main_Data::game_strings->Cat(str_params, result); } break; - case 2: //toNum + case 2: //toNum takes hex case 3: //getLen if (is_range) Main_Data::game_strings->RangeOp(str_params, string_id_1, result, op, args); else { @@ -4936,7 +4936,7 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& if (op == 3) Main_Data::game_strings->GetLen(str_params, args[0]); } break; - case 4: //inStr + case 4: //inStr takes hex?????? { Game_Strings::Str_t search = Main_Data::game_strings->GetWithMode(com.string, args[0], modes[0]); args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); // not sure this is necessary but better safe @@ -4946,7 +4946,7 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& else Main_Data::game_strings->InStr(str_params, static_cast(search), args[1], args[2]); break; } - case 5: //split + case 5: //split takes hex { Game_Strings::Str_t delimiter = Main_Data::game_strings->GetWithMode(com.string, args[0], modes[0]); args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); @@ -4956,7 +4956,7 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& else Main_Data::game_strings->Split(str_params, static_cast(delimiter), args[1], args[2]); break; } - case 7: //toFile + case 7: //toFile takes hex { std::string filename = static_cast(Main_Data::game_strings->GetWithMode(com.string, args[0], modes[0])); args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); @@ -4964,7 +4964,7 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& Main_Data::game_strings->ToFile(str_params, filename, args[1]); break; } - case 8: //popLine + case 8: //popLine takes hex // a range parameter with popLine doesn't affect multiple strings; // it instead alters the behavior. // given a range t[a..b], it will pop the first (b-a)+1 lines, @@ -4974,9 +4974,10 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& if (is_range) Main_Data::game_strings->PopLine(str_params, string_id_1 - string_id_0, args[0]); else Main_Data::game_strings->PopLine(str_params, 0, args[0]); break; - case 9: //exInStr + case 9: //exInStr case 10: //exMatch , edge case: the only command that generates 8 parameters instead of 7 { + // takes hex std::string expr = static_cast(Main_Data::game_strings->GetWithMode(com.string, args[0], modes[0])); args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); // output var args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[2]); // beginning pos diff --git a/src/game_strings.cpp b/src/game_strings.cpp index 1c8f6f109f..8bc3e4130f 100644 --- a/src/game_strings.cpp +++ b/src/game_strings.cpp @@ -45,8 +45,14 @@ Game_Strings::Str_t Game_Strings::Cat(Str_Params params, Str_t string) { int Game_Strings::ToNum(Str_Params params, int var_id) { if (!ResizeWithId(params.string_id)) return -1; + std::string str = static_cast(Get(params.string_id)); + + int num; + if (params.hex) + num = std::stoi(str, 0, 16); + else + num = std::stoi(str); - int num = std::stoi(static_cast(Get(params.string_id))); Main_Data::game_variables->Set(var_id, num); return num; } @@ -62,6 +68,14 @@ int Game_Strings::GetLen(Str_Params params, int var_id) { int Game_Strings::InStr(Str_Params params, std::string search, int var_id, int begin) { if (!ResizeWithId(params.string_id)) return -1; + if (params.extract) { + search = static_cast(Extract(static_cast(search), params.hex)); + } + + std::string str = static_cast(Get(params.string_id)); + + Output::Debug("Searching for {} in {}", search, str); + int index = static_cast(Get(params.string_id)).find(search, begin); Main_Data::game_variables->Set(var_id, index); return index; @@ -199,6 +213,3 @@ Game_Strings::Str_t Game_Strings::PrependMin(Str_t string, int min_size, char c) return string; } -inline Game_Strings::Str_t Game_Strings::Extract(Str_t string) { - return static_cast(PendingMessage::ApplyTextInsertingCommands(static_cast(string), Player::escape_char, true)); -} diff --git a/src/game_strings.h b/src/game_strings.h index 33e1fbe362..eb097edaa5 100644 --- a/src/game_strings.h +++ b/src/game_strings.h @@ -70,7 +70,7 @@ class Game_Strings { bool ResizeWithId(int id); bool ShouldWarn(int id) const; void WarnGet(int id) const; - Str_t Extract(Str_t string); + Str_t Extract(Str_t string, bool as_hex); private: Strings_t _strings; @@ -84,7 +84,7 @@ inline Game_Strings::Str_t Game_Strings::Set(Str_Params params, Str_t string) { auto& s = _strings[params.string_id - 1]; s = string; if (params.extract) - s = Extract(s); + s = Extract(s, params.hex); return s; } @@ -156,3 +156,8 @@ inline Game_Strings::Str_t Game_Strings::GetWithModeAndPos(Str_t str_data, int a } return ret; } + +inline Game_Strings::Str_t Game_Strings::Extract(Str_t string, bool as_hex) { + PendingMessage::CommandInserter cmd_fn = PendingMessage::BuildManiacsCommandInserter(as_hex); + return static_cast(PendingMessage::ApplyTextInsertingCommands(static_cast(string), Player::escape_char, cmd_fn)); +} diff --git a/src/pending_message.cpp b/src/pending_message.cpp index 04e57f8087..c30b5a55bb 100644 --- a/src/pending_message.cpp +++ b/src/pending_message.cpp @@ -88,8 +88,8 @@ void PendingMessage::SetChoiceResetColors(bool value) { choice_reset_color = value; } +std::string PendingMessage::ApplyTextInsertingCommands(std::string input, uint32_t escape_char, CommandInserter cmd_fn) { -std::string PendingMessage::ApplyTextInsertingCommands(std::string input, uint32_t escape_char, CommandInserter cmd_fn, bool maniacs_parsing) { if (input.empty()) { return input; } @@ -127,27 +127,7 @@ std::string PendingMessage::ApplyTextInsertingCommands(std::string input, uint32 if (fn_res) { output.append(*fn_res); start_copy = iter; - } else if (ch == 'T' || ch == 't') { - auto parse_ret = Game_Message::ParseString(iter, end, escape_char, true); - iter = parse_ret.next; - int value = parse_ret.value; - - Game_Strings::Str_t string = Main_Data::game_strings->Get(value); - output.append((std::string)string); - - start_copy = iter; - } else if (maniacs_parsing && (ch == 'S' || ch == 's')) { - // parsing a switch within an extracted string var command will parse \s[N] as a switch (ON/OFF) - auto parse_ret = Game_Message::ParseSpeed(iter, end, escape_char, true); - iter = parse_ret.next; - int value = parse_ret.value; - - bool sw = Main_Data::game_switches->Get(value); - if (sw) output.append("ON"); - else output.append("OFF"); - - start_copy = iter; - } + } } if (start_copy == input.data()) { @@ -184,3 +164,51 @@ std::optional PendingMessage::DefaultCommandInserter(char ch, const return std::nullopt; }; + +std::optional PendingMessage::ManiacsCommandInserter(char ch, const char** iter, const char* end, uint32_t escape_char) { + if (ch == 'T' || ch == 't') { + auto parse_ret = Game_Message::ParseString(*iter, end, escape_char, true); + *iter = parse_ret.next; + int value = parse_ret.value; + + Game_Strings::Str_t string = Main_Data::game_strings->Get(value); + return (std::string)string; + } else if (ch == 'S' || ch == 's') { + // parsing a switch within an extracted string var command will parse \s[N] as a switch (ON/OFF) + auto parse_ret = Game_Message::ParseSpeed(*iter, end, escape_char, true); + *iter = parse_ret.next; + int value = parse_ret.value; + + bool sw = Main_Data::game_switches->Get(value); + if (sw) { + return "ON"; + } + else { + return "OFF"; + } + } + + return DefaultCommandInserter(ch, iter, end, escape_char); +}; + +std::optional PendingMessage::ManiacsCommandInserterHex(char ch, const char** iter, const char* end, uint32_t escape_char) { + if (ch == 'V' || ch == 'v') { + auto parse_ret = Game_Message::ParseVariable(*iter, end, escape_char, true); + *iter = parse_ret.next; + int value = parse_ret.value; + + int variable_value = Main_Data::game_variables->Get(value); + std::ostringstream ss; + ss << std::hex << variable_value; + return ss.str(); + } + + return ManiacsCommandInserter(ch, iter, end, escape_char); +}; + +PendingMessage::CommandInserter PendingMessage::BuildManiacsCommandInserter(bool hex_nums) { + if (hex_nums) { + return PendingMessage::ManiacsCommandInserterHex; + } + return PendingMessage::ManiacsCommandInserter; +}; diff --git a/src/pending_message.h b/src/pending_message.h index 33f4862ad3..78e1ce4181 100644 --- a/src/pending_message.h +++ b/src/pending_message.h @@ -30,6 +30,9 @@ class PendingMessage { using ChoiceContinuation = std::function; using CommandInserter = std::function(char,const char**,const char*,uint32_t)>; static std::optional DefaultCommandInserter(char ch, const char** iter, const char* end, uint32_t escape_char); + 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); + static CommandInserter BuildManiacsCommandInserter(bool hex_nums); int PushLine(std::string msg, CommandInserter cmd_fn = DefaultCommandInserter); int PushChoice(std::string msg, bool enabled = true, CommandInserter cmd_fn = DefaultCommandInserter); @@ -67,7 +70,7 @@ class PendingMessage { void SetIsEventMessage(bool value) { is_event_message = value; } bool IsEventMessage() const { return is_event_message; } - static std::string ApplyTextInsertingCommands(std::string input, uint32_t escape_char, CommandInserter cmd_fn, bool maniacs_parsing = false); + static std::string ApplyTextInsertingCommands(std::string input, uint32_t escape_char, CommandInserter cmd_fn); private: int PushLineImpl(std::string msg, CommandInserter cmd_fn); From 88c8934090dceedc12bb35529d2b58629ec79474 Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Tue, 4 Jul 2023 21:04:31 -0600 Subject: [PATCH 22/47] StringVar: more hex support in string functions --- src/game_strings.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/game_strings.cpp b/src/game_strings.cpp index 8bc3e4130f..4ad71f516b 100644 --- a/src/game_strings.cpp +++ b/src/game_strings.cpp @@ -109,6 +109,10 @@ int Game_Strings::Split(Str_Params params, std::string delimiter, int string_out Game_Strings::Str_t Game_Strings::ToFile(Str_Params params, std::string filename, int encoding) { std::string str = static_cast(Get(params.string_id)); + if (params.extract) { + filename = static_cast(Extract(static_cast(filename), params.hex)); + } + // this sucks but it is what maniacs does filename = "Text/" + filename + ".txt"; @@ -148,6 +152,10 @@ Game_Strings::Str_t Game_Strings::ExMatch(Str_Params params, std::string expr, i Str_t str_result; std::smatch match; + if (params.extract) { + expr = static_cast(Extract(static_cast(expr), params.hex)); + } + std::string base = static_cast(Get(params.string_id)).erase(0, begin); std::regex r(expr); From 7c3e2efff947afa4369fff758bdc5480e471f56c Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Wed, 5 Jul 2023 13:40:36 -0600 Subject: [PATCH 23/47] StringVar: properly use fallback for opening files --- src/filefinder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/filefinder.cpp b/src/filefinder.cpp index 1e69768142..e7002019b5 100644 --- a/src/filefinder.cpp +++ b/src/filefinder.cpp @@ -415,7 +415,7 @@ Filesystem_Stream::InputStream FileFinder::OpenFont(StringView name) { Filesystem_Stream::InputStream FileFinder::OpenText(StringView name) { DirectoryTree::Args args = { MakePath("Text", name), TEXT_TYPES, 1, false }; - return open_generic("Text", name, args); + return open_generic_with_fallback("Text", name, args); } bool FileFinder::IsMajorUpdatedTree() { From a0d976f0d16a7b982ad1051e8dd18b76be7d4209 Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Wed, 5 Jul 2023 13:50:10 -0600 Subject: [PATCH 24/47] StringVar: add find generic fallback function for FindText --- src/filefinder.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/filefinder.cpp b/src/filefinder.cpp index e7002019b5..23e320755a 100644 --- a/src/filefinder.cpp +++ b/src/filefinder.cpp @@ -338,6 +338,15 @@ std::string find_generic(const DirectoryTree::Args& args) { return FileFinder::Game().FindFile(args); } +std::string find_generic_with_fallback(DirectoryTree::Args& args) { + std::string found = find_generic(args); + if (found.empty()) { + return FileFinder::Save().FindFile(args); + } + + return found; +} + std::string FileFinder::FindImage(StringView dir, StringView name) { DirectoryTree::Args args = { MakePath(dir, name), IMG_TYPES, 1, false }; return find_generic(args); @@ -361,7 +370,7 @@ std::string FileFinder::FindFont(StringView name) { std::string FileFinder::FindText(StringView name) { DirectoryTree::Args args = { MakePath("Text", name), TEXT_TYPES, 1, true }; - return find_generic(args); + return find_generic_with_fallback(args); } Filesystem_Stream::InputStream open_generic(StringView dir, StringView name, DirectoryTree::Args& args) { @@ -387,7 +396,7 @@ Filesystem_Stream::InputStream open_generic_with_fallback(StringView dir, String auto is = open_generic(dir, name, args); if (!is) { is = FileFinder::Save().OpenFile(args); } if (!is) { - Output::Debug("Unable to find in either Game or Save: {}/{}", dir, name); + Output::Debug("Unable to open in either Game or Save: {}/{}", dir, name); } return is; From 6aa50b3f69b6b3b0d52bcea31f203a3cdd9cd184 Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Wed, 5 Jul 2023 14:32:20 -0600 Subject: [PATCH 25/47] StringVar: support more text types also remove debug statements --- src/filefinder.cpp | 2 +- src/game_interpreter.cpp | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/filefinder.cpp b/src/filefinder.cpp index 23e320755a..622c7451ff 100644 --- a/src/filefinder.cpp +++ b/src/filefinder.cpp @@ -68,7 +68,7 @@ namespace { constexpr const auto SOUND_TYPES = Utils::MakeSvArray( ".opus", ".oga", ".ogg", ".wav", ".mp3", ".wma"); constexpr const auto FONTS_TYPES = Utils::MakeSvArray(".fon", ".fnt", ".bdf", ".ttf", ".ttc", ".otf", ".woff2", ".woff"); - constexpr const auto TEXT_TYPES = Utils::MakeSvArray(".txt", ".csv"); + constexpr const auto TEXT_TYPES = Utils::MakeSvArray(".txt", ".csv", ".svg", ".xml", ".json", ".yml", ".yaml"); } FilesystemView FileFinder::Game() { diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 9d20e59d62..b2a3e3d52d 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -3614,8 +3614,6 @@ bool Game_Interpreter::CommandConditionalBranch(lcf::rpg::EventCommand const& co int op = com.parameters[4] & 3; int ignoreCase = com.parameters[4] >> 8 & 1; - //Output::Debug("conditional string {}", com.string); - //Output::Debug("params {} {} {} {} {}", com.parameters[0], com.parameters[1], com.parameters[2], com.parameters[3], com.parameters[4]); std::string str_l = static_cast(Main_Data::game_strings->GetWithMode(com.string, com.parameters[2], modes[0]+1)); std::string str_r = static_cast(Main_Data::game_strings->GetWithMode(com.string, com.parameters[3], modes[1])); result = ManiacCheckString(str_l, str_r, op, ignoreCase); @@ -4731,13 +4729,6 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& extract_flag, }; - Output::Debug("com.string: {}", com.string); - Output::Debug("string_mode {} string_id_0 {}", string_mode, string_id_0); - Output::Debug("op {} fn {} flags {}", op, fn, flags); - Output::Debug("hex {} extractt {} first {}", hex_flag, extract_flag, first_flag); - Output::Debug("args {} {} {} {}", args[0], args[1], args[2], args[3]); - Output::Debug("modes {} {} {} {}", modes[0], modes[1], modes[2], modes[3]); - switch (op) { case 0: //asg From e1c631214f742f9b0183860c0b855bf18ca2133a Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Wed, 5 Jul 2023 20:43:00 -0600 Subject: [PATCH 26/47] StringVars: parse string pic delimiters to support direct and indirect string references --- src/game_interpreter.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index b2a3e3d52d..bdcd9da330 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -4293,8 +4293,20 @@ bool Game_Interpreter::CommandManiacShowStringPicture(lcf::rpg::EventCommand con text.font_size = font_px; - auto components = Utils::Tokenize(com.string, [](char32_t ch) { - return ch == '\x01'; + // maniacs stores string parsing modes in the delimiters + // x01 -> literal string + // x02 -> direct reference + // x03 -> indirect reference + // for the displayed string, the id argument is in com.parameters[22] + // here we are capturing all the delimiters, but currently only need to support reading the first one + int i = 0; + int delims[3] = {}; + auto components = Utils::Tokenize(com.string, [p = &delims, &i](char32_t ch) { + if (ch == '\x01' || ch == '\x02' || ch == '\x03') { + (*p)[i++] = static_cast(ch); + return true; + } + return false; }); if (components.size() < 4) { @@ -4302,7 +4314,11 @@ bool Game_Interpreter::CommandManiacShowStringPicture(lcf::rpg::EventCommand con return true; } - text.text = components[1]; + text.text = static_cast(Main_Data::game_strings->GetWithMode( + static_cast(components[1]), + com.parameters[22], + delims[0]-1 + )); params.system_name = components[2]; text.font_name = components[3]; From ab783c8619ac6623f4913131a78ed1ea2b622bb1 Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Fri, 7 Jul 2023 08:49:53 -0600 Subject: [PATCH 27/47] StringVar: fix split function i guess i never actually tested it???? --- src/game_strings.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/game_strings.cpp b/src/game_strings.cpp index 4ad71f516b..0e3bc7715d 100644 --- a/src/game_strings.cpp +++ b/src/game_strings.cpp @@ -84,15 +84,16 @@ int Game_Strings::InStr(Str_Params params, std::string search, int var_id, int b int Game_Strings::Split(Str_Params params, std::string delimiter, int string_out_id, int var_id) { if (!ResizeWithId(params.string_id)) return -1; + size_t index; + std::string token; + // always returns at least 1 int splits = 1; - size_t index = 0; std::string str = static_cast(Get(params.string_id)); - std::string token; params.string_id = string_out_id; - while (index = str.find(delimiter) != std::string::npos) { + for (index = str.find(delimiter); index != std::string::npos; index = str.find(delimiter)) { token = str.substr(0, index); Set(params, static_cast(token)); params.string_id++; From 6ef09ab6d06cd1054e41de0e1eba8a4269742324 Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Fri, 7 Jul 2023 09:19:19 -0600 Subject: [PATCH 28/47] StringVar: fix join function i had only implemented the logic for joining vars, not joining strings --- src/game_interpreter.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index bdcd9da330..ce0725e305 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -4874,16 +4874,24 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[2]); result = static_cast(static_cast(Main_Data::game_strings->GetWithMode(com.string, args[0], modes[0])).substr(args[1], args[2])); break; - case 10: //Join Variables (join) + case 10: //Join (join) { std::string op_string = ""; std::string delimiter = (std::string)Main_Data::game_strings->GetWithMode(com.string, args[0], modes[0]); - // mode of arg[1] is weird, seems offset by +2, not sure what the intent is - args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1] - 2); + Output::Debug("args {} {} {}", args[0], args[1], args[2]); + Output::Debug("modes {} {} {}", modes[0], modes[1], modes[2]); + // 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 + args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1] % 2); args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[2]); while (args[2] > 0) { - op_string += std::to_string(Main_Data::game_variables->Get(args[1]++)); + if (modes[1] < 2) { + op_string += static_cast(Main_Data::game_strings->Get(args[1]++)); + } else { + op_string += std::to_string(Main_Data::game_variables->Get(args[1]++)); + } + if (--args[2] > 0) op_string += delimiter; } From 1c5b6f68880fc2c2bcec770150dc318c03761931 Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Fri, 7 Jul 2023 17:07:25 -0600 Subject: [PATCH 29/47] StringVar: crack at fixing line breaks in show string pic --- src/game_strings.h | 2 +- src/game_windows.cpp | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/game_strings.h b/src/game_strings.h index eb097edaa5..68a73dce09 100644 --- a/src/game_strings.h +++ b/src/game_strings.h @@ -64,13 +64,13 @@ class Game_Strings { const Strings_t& RangeOp(Str_Params params, int string_id_1, Str_t string, int op, int args[] = nullptr); Str_t PrependMin(Str_t string, int min_size, char c); + Str_t Extract(Str_t string, bool as_hex); private: Str_t Set(Str_Params params, Str_t string); bool ResizeWithId(int id); bool ShouldWarn(int id) const; void WarnGet(int id) const; - Str_t Extract(Str_t string, bool as_hex); private: Strings_t _strings; diff --git a/src/game_windows.cpp b/src/game_windows.cpp index 9acf80b534..b30cc8931e 100644 --- a/src/game_windows.cpp +++ b/src/game_windows.cpp @@ -17,6 +17,7 @@ #include "game_windows.h" #include "game_message.h" +#include "game_strings.h" #include "main_data.h" #include "compiler.h" #include "text.h" @@ -203,7 +204,8 @@ void Game_Windows::Window_User::Refresh(bool& async_wait) { fonts.emplace_back(font); - std::stringstream ss(ToString(text.text)); + std::string extracted_text = ToString(Main_Data::game_strings->Extract(text.text, false)); + std::stringstream ss(extracted_text); std::string out; PendingMessage pm; while (Utils::ReadLine(ss, out)) { From 329a2805c8fe375fab4695f96a5a5fd48749e470 Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Wed, 12 Jul 2023 11:51:13 -0600 Subject: [PATCH 30/47] StringVar: add safety to parsing string pic delimiters --- src/game_interpreter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index ce0725e305..a9880e5a18 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -4300,10 +4300,10 @@ bool Game_Interpreter::CommandManiacShowStringPicture(lcf::rpg::EventCommand con // for the displayed string, the id argument is in com.parameters[22] // here we are capturing all the delimiters, but currently only need to support reading the first one int i = 0; - int delims[3] = {}; + std::vector delims; auto components = Utils::Tokenize(com.string, [p = &delims, &i](char32_t ch) { if (ch == '\x01' || ch == '\x02' || ch == '\x03') { - (*p)[i++] = static_cast(ch); + p->push_back(static_cast(ch)); return true; } return false; From 0ec5f0ce6eb5bea448af973b8b32b0e3f4978e05 Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Wed, 12 Jul 2023 17:03:16 -0600 Subject: [PATCH 31/47] StringVar: refactor from DBString to std string also includes safe checks around nullptr when retrieving names and descriptions --- src/game_interpreter.cpp | 252 +++++++++++++++++++++++++-------------- src/game_interpreter.h | 9 ++ src/game_strings.cpp | 50 ++++---- src/game_strings.h | 14 ++- src/game_windows.cpp | 2 +- src/scene_save.cpp | 2 +- 6 files changed, 213 insertions(+), 116 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index a9880e5a18..57635d2644 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -3614,8 +3614,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_l = static_cast(Main_Data::game_strings->GetWithMode(com.string, com.parameters[2], modes[0]+1)); - std::string str_r = static_cast(Main_Data::game_strings->GetWithMode(com.string, com.parameters[3], modes[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]); result = ManiacCheckString(str_l, str_r, op, ignoreCase); } break; @@ -4738,6 +4739,7 @@ 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 = ""; Game_Strings::Str_Params str_params = { string_id_0, @@ -4752,7 +4754,7 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& switch (fn) { case 0: //String - result = Main_Data::game_strings->GetWithMode(static_cast(com.string), args[0], modes[0]); + result = Main_Data::game_strings->GetWithMode(str_param, args[0], modes[0]); args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); // min_size @@ -4762,79 +4764,23 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& args[0] = Main_Data::game_variables->GetWithMode(args[0], modes[0]); args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); - result = static_cast(std::to_string(args[0])); + result = std::to_string(args[0]); result = Main_Data::game_strings->PrependMin(result, args[1], '0'); break; case 3: //Database Names args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); - - switch (args[0]) - { - case 0: //.actor[a].name - if (args[2]) result = static_cast(Main_Data::game_actors->GetActor(args[1])->GetName()); - else result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::actors, args[1])->name); - break; - case 1: result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::skills, args[1])->name); break; //.skill[a].name - case 2: result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::items, args[1])->name); break; //.item[a].name - case 3: result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::enemies, args[1])->name); break; //.enemy[a].name - case 4: result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::troops, args[1])->name); break; //.troop[a].name - case 5: result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::terrains, args[1])->name); break; //.terrain[a].name - case 6: result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::attributes, args[1])->name); break; //.element[a].name - case 7: result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::states, args[1])->name); break; //.state[a].name - case 8: result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::animations, args[1])->name); break; //.anim[a].name - case 9: result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::chipsets, args[1])->name); break; //.tileset[a].name - case 10: result = static_cast(Main_Data::game_switches->GetName(args[1])); break; //.s[a].name - case 11: result = static_cast(Main_Data::game_variables->GetName(args[1])); break; //.v[a].name - case 12: break; //.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() >= args[1]) { - result = (Game_Strings::Str_t)Game_Map::GetCommonEvents()[args[1]-1].GetName(); - } - break; - case 14: result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::classes, args[1])->name); break; //.class[a].name - case 15: result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::battleranimations, args[1])->name); break; //.anim2[a].name - case 16: result = static_cast(Game_Map::GetMapName(args[1])); break; //.map[a].name - case 17: result = static_cast(Game_Map::GetEvent(args[1])->GetName()); break; //.mev[a].name - case 18: //.member[a].name - if (args[2]) { - result = static_cast(Main_Data::game_party->GetActor(args[1])->GetName()); - } - else { - args[1] = Main_Data::game_party->GetActor(args[1])->GetId(); - result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::actors, args[1])->name); - } - break; - } + result = ManiacGetLcfDataName(args[0], args[1], (bool)args[2]); break; case 4: //Database Descriptions args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); - - switch (args[0]) - { - case 0: //.actor[a].desc - if (args[2]) result = static_cast(Main_Data::game_actors->GetActor(args[1])->GetTitle()); - else result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::actors, args[1])->title); - break; - case 1: result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::skills, args[1])->description); break; //.skill[a].desc - case 2: result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::items, args[1])->description); break; //.item[a].desc - case 18: //.member[a].desc - if (args[2]) { - result = static_cast(Main_Data::game_party->GetActor(args[1])->GetTitle()); - } - else { - args[1] = Main_Data::game_party->GetActor(args[1])->GetId(); - result = static_cast(lcf::ReaderUtil::GetElement(lcf::Data::actors, args[1])->title); - } - break; - } + result = ManiacGetLcfDataDescription(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 += static_cast(Main_Data::game_strings->GetWithModeAndPos(com.string, args[i], modes[i], &pos)); + op_string += Main_Data::game_strings->GetWithModeAndPos(str_param, args[i], modes[i], &pos); } result = (Game_Strings::Str_t)op_string; break; @@ -4845,8 +4791,8 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& std::string base, insert; args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); - base = static_cast(Main_Data::game_strings->GetWithModeAndPos(com.string, args[0], modes[0], &pos)); - insert = static_cast(Main_Data::game_strings->GetWithModeAndPos(com.string, args[2], modes[2], &pos)); + 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); result = (Game_Strings::Str_t)base.insert(args[1], insert); break; @@ -4856,9 +4802,9 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& int pos = 0; std::string base, search, replacement; - base = static_cast(Main_Data::game_strings->GetWithModeAndPos(com.string, args[0], modes[0], &pos)); - search = static_cast(Main_Data::game_strings->GetWithModeAndPos(com.string, args[1], modes[1], &pos)); - replacement = static_cast(Main_Data::game_strings->GetWithModeAndPos(com.string, args[2], modes[2], &pos)); + 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); std::size_t index = base.find(search); while (index != std::string::npos) { @@ -4872,12 +4818,12 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& case 9: //Substring (subs) args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[2]); - result = static_cast(static_cast(Main_Data::game_strings->GetWithMode(com.string, args[0], modes[0])).substr(args[1], args[2])); + result = Main_Data::game_strings->GetWithMode(str_param, args[0], modes[0]).substr(args[1], args[2]); break; case 10: //Join (join) { std::string op_string = ""; - std::string delimiter = (std::string)Main_Data::game_strings->GetWithMode(com.string, args[0], modes[0]); + std::string delimiter = (std::string)Main_Data::game_strings->GetWithMode(str_param, args[0], modes[0]); Output::Debug("args {} {} {}", args[0], args[1], args[2]); Output::Debug("modes {} {} {}", modes[0], modes[1], modes[2]); // args[1] & mode[1] relates to starting ID for strings to join @@ -4887,7 +4833,7 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& while (args[2] > 0) { if (modes[1] < 2) { - op_string += static_cast(Main_Data::game_strings->Get(args[1]++)); + op_string += Main_Data::game_strings->Get(args[1]++); } else { op_string += std::to_string(Main_Data::game_variables->Get(args[1]++)); } @@ -4895,20 +4841,20 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& if (--args[2] > 0) op_string += delimiter; } - result = static_cast(op_string); + result = op_string; break; } case 12: //File (file) { // maniacs does not like a file extension - Game_Strings::Str_t filename = Main_Data::game_strings->GetWithMode(com.string, args[0], modes[0]); + Game_Strings::Str_t filename = Main_Data::game_strings->GetWithMode(str_param, args[0], modes[0]); // args[1] is the encoding... 0 for sjis, 1 for utf8 Filesystem_Stream::InputStream is = FileFinder::OpenText(StringView(filename)); if (is.good()) { std::stringstream buffer; buffer << is.rdbuf(); - result = static_cast(buffer.str()); + result = buffer.str(); } is.Close(); break; @@ -4916,21 +4862,21 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& case 13: //Remove (rem) args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[2]); - result = static_cast(static_cast(Main_Data::game_strings->GetWithMode(com.string, args[0], modes[0])).erase(args[1], args[2])); + result = Main_Data::game_strings->GetWithMode(str_param, args[0], modes[0]).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 = static_cast(Main_Data::game_strings->GetWithModeAndPos(com.string, args[0], modes[0], &pos)); - search = static_cast(Main_Data::game_strings->GetWithModeAndPos(com.string, args[1], modes[1], &pos)); - replacement = static_cast(Main_Data::game_strings->GetWithModeAndPos(com.string, args[2], modes[2], &pos)); + 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); std::regex rexp(search); - if (first_flag) result = static_cast(std::regex_replace(base, rexp, replacement, std::regex_constants::format_first_only)); - else result = static_cast(std::regex_replace(base, rexp, replacement)); + if (first_flag) result = std::regex_replace(base, rexp, replacement, std::regex_constants::format_first_only); + else result = std::regex_replace(base, rexp, replacement); break; } default: @@ -4953,27 +4899,27 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& break; case 4: //inStr takes hex?????? { - Game_Strings::Str_t search = Main_Data::game_strings->GetWithMode(com.string, args[0], modes[0]); + Game_Strings::Str_t search = Main_Data::game_strings->GetWithMode(str_param, args[0], modes[0]); args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); // not sure this is necessary but better safe args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[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, static_cast(search), args[1], args[2]); + else Main_Data::game_strings->InStr(str_params, search, args[1], args[2]); break; } case 5: //split takes hex { - Game_Strings::Str_t delimiter = Main_Data::game_strings->GetWithMode(com.string, args[0], modes[0]); + Game_Strings::Str_t delimiter = Main_Data::game_strings->GetWithMode(str_param, args[0], modes[0]); args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[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, static_cast(delimiter), args[1], args[2]); + else Main_Data::game_strings->Split(str_params, delimiter, args[1], args[2]); break; } case 7: //toFile takes hex { - std::string filename = static_cast(Main_Data::game_strings->GetWithMode(com.string, args[0], modes[0])); + std::string filename = Main_Data::game_strings->GetWithMode(str_param, args[0], modes[0]); args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); Main_Data::game_strings->ToFile(str_params, filename, args[1]); @@ -4993,12 +4939,12 @@ 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 = static_cast(Main_Data::game_strings->GetWithMode(com.string, args[0], modes[0])); + std::string expr = Main_Data::game_strings->GetWithMode(str_param, args[0], modes[0]); args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); // output var args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[2]); // beginning pos if (is_range) { - Main_Data::game_strings->RangeOp(str_params, string_id_1, static_cast(expr), op, args); + Main_Data::game_strings->RangeOp(str_params, string_id_1, expr, op, args); } else { if (op == 9) Main_Data::game_strings->ExMatch(str_params, expr, args[1], args[2]); @@ -5088,3 +5034,135 @@ bool Game_Interpreter::ManiacCheckString(std::string str_l, std::string str_r, i return false; } } + +Game_Strings::Str_t Game_Interpreter::ManiacGetLcfDataName(int data_type, int id, bool is_dynamic) const { + switch (data_type) + { + case 0: //.actor[a].name + if (is_dynamic) { + auto actor = Main_Data::game_actors->GetActor(id); + if (actor != nullptr) { + return static_cast(actor->GetName()); + } + } else { + return Game_Interpreter::ManiacGetNameSafely(lcf::Data::actors, id); + } + break; + case 1: return Game_Interpreter::ManiacGetNameSafely(lcf::Data::skills, id); //.skill[a].name + case 2: return Game_Interpreter::ManiacGetNameSafely(lcf::Data::items, id); //.item[a].name + case 3: return Game_Interpreter::ManiacGetNameSafely(lcf::Data::enemies, id); //.enemy[a].name + case 4: return Game_Interpreter::ManiacGetNameSafely(lcf::Data::troops, id); //.troop[a].name + case 5: return Game_Interpreter::ManiacGetNameSafely(lcf::Data::terrains, id); //.terrain[a].name + case 6: return Game_Interpreter::ManiacGetNameSafely(lcf::Data::attributes, id); //.element[a].name + case 7: return Game_Interpreter::ManiacGetNameSafely(lcf::Data::states, id); //.state[a].name + case 8: return Game_Interpreter::ManiacGetNameSafely(lcf::Data::animations, id); //.anim[a].name + case 9: return Game_Interpreter::ManiacGetNameSafely(lcf::Data::chipsets, id); //.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 12: break; //.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(); + } + break; + } + case 14: return Game_Interpreter::ManiacGetNameSafely(lcf::Data::classes, id); //.class[a].name + case 15: return Game_Interpreter::ManiacGetNameSafely(lcf::Data::battleranimations, id); //.anim2[a].name + case 16: return static_cast(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()); + } + break; + } + case 18: //.member[a].name + { + auto actor = Main_Data::game_party->GetActor(id); + if (actor != nullptr) { + if (is_dynamic) { + return static_cast(actor->GetName()); + } + else { + id = actor->GetId(); + return Game_Interpreter::ManiacGetNameSafely(lcf::Data::actors, id); + } + } + break; + } + } + + Output::Warning("Unable to read name: {} {}", data_type, id); + return ""; +} + +Game_Strings::Str_t Game_Interpreter::ManiacGetLcfDataDescription(int data_type, int id, bool is_dynamic) const { + switch (data_type) + { + case 0: //.actor[a].desc + if (is_dynamic) { + auto actor = Main_Data::game_actors->GetActor(id); + if (actor != nullptr) { + return static_cast(actor->GetTitle()); + } + } else { + return Game_Interpreter::ManiacGetDescriptionSafely(lcf::Data::actors, id); + } + break; + case 1: return Game_Interpreter::ManiacGetDescriptionSafely(lcf::Data::skills, id); //.skill[a].desc + case 2: return Game_Interpreter::ManiacGetDescriptionSafely(lcf::Data::items, id); //.item[a].desc + case 18: //.member[a].desc + { + auto actor = Main_Data::game_party->GetActor(id); + if (actor != nullptr) { + if (is_dynamic) { + return static_cast(actor->GetTitle()); + } + else { + id = actor->GetId(); + return Game_Interpreter::ManiacGetDescriptionSafely(lcf::Data::actors, id); + } + } + break; + } + } + + Output::Warning("Unable to read description: {} {}", data_type, id); + return ""; +} + +template +Game_Strings::Str_t Game_Interpreter::ManiacGetNameSafely(std::vector vec, int id) const { + T* data = lcf::ReaderUtil::GetElement(vec, id); + if (data != nullptr) { + return static_cast(data->name); + } + + Output::Warning("Unable to read name: {}", id); + return ""; +} + +template +Game_Strings::Str_t Game_Interpreter::ManiacGetDescriptionSafely(std::vector vec, int id) const { + T* data = lcf::ReaderUtil::GetElement(vec, id); + if (data != nullptr) { + return static_cast(data->description); + } + + Output::Warning("Unable to read description: {}", id); + return ""; +} + +template <> +Game_Strings::Str_t Game_Interpreter::ManiacGetDescriptionSafely(std::vector vec, int id) const { + lcf::rpg::Actor* data = lcf::ReaderUtil::GetElement(vec, id); + if (data != nullptr) { + return static_cast(data->title); + } + + Output::Warning("Unable to read description: {}", id); + return ""; +} diff --git a/src/game_interpreter.h b/src/game_interpreter.h index a24c51e9dd..f7b582f43f 100644 --- a/src/game_interpreter.h +++ b/src/game_interpreter.h @@ -336,6 +336,15 @@ class Game_Interpreter lcf::rpg::SaveEventExecState _state; KeyInputState _keyinput; AsyncOp _async_op = {}; + + std::string ManiacGetLcfDataName(int data_type, int id, bool is_dynamic) const; + std::string ManiacGetLcfDataDescription(int data_type, int id, bool is_dynamic) const; + +private: + template std::string ManiacGetNameSafely(std::vector, int id) const; + template std::string ManiacGetDescriptionSafely(std::vector, int id) const; + template<> std::string ManiacGetDescriptionSafely(std::vector, int id) const; + }; inline const lcf::rpg::SaveEventExecFrame* Game_Interpreter::GetFramePtr() const { diff --git a/src/game_strings.cpp b/src/game_strings.cpp index 0e3bc7715d..8102d37e49 100644 --- a/src/game_strings.cpp +++ b/src/game_strings.cpp @@ -37,15 +37,15 @@ Game_Strings::Str_t Game_Strings::Cat(Str_Params params, Str_t string) { if (!ResizeWithId(params.string_id)) return ""; Str_t s = Get(params.string_id); - std::string op_string = static_cast(s); - op_string.append(static_cast(string)); - Set(params, static_cast(op_string)); + std::string op_string = s; + op_string.append(string); + Set(params, op_string); return s; } int Game_Strings::ToNum(Str_Params params, int var_id) { if (!ResizeWithId(params.string_id)) return -1; - std::string str = static_cast(Get(params.string_id)); + std::string str = Get(params.string_id); int num; if (params.hex) @@ -60,7 +60,7 @@ int Game_Strings::ToNum(Str_Params params, int var_id) { int Game_Strings::GetLen(Str_Params params, int var_id) { if (!ResizeWithId(params.string_id)) return -1; - int len = static_cast(Get(params.string_id)).length(); + int len = Get(params.string_id).length(); Main_Data::game_variables->Set(var_id, len); return len; } @@ -69,14 +69,14 @@ int Game_Strings::InStr(Str_Params params, std::string search, int var_id, int b if (!ResizeWithId(params.string_id)) return -1; if (params.extract) { - search = static_cast(Extract(static_cast(search), params.hex)); + search = Extract(search, params.hex); } - std::string str = static_cast(Get(params.string_id)); + std::string str = Get(params.string_id); Output::Debug("Searching for {} in {}", search, str); - int index = static_cast(Get(params.string_id)).find(search, begin); + int index = Get(params.string_id).find(search, begin); Main_Data::game_variables->Set(var_id, index); return index; } @@ -89,29 +89,29 @@ int Game_Strings::Split(Str_Params params, std::string delimiter, int string_out // always returns at least 1 int splits = 1; - std::string str = static_cast(Get(params.string_id)); + std::string str = Get(params.string_id); params.string_id = string_out_id; for (index = str.find(delimiter); index != std::string::npos; index = str.find(delimiter)) { token = str.substr(0, index); - Set(params, static_cast(token)); + Set(params, token); params.string_id++; splits++; str.erase(0, index + delimiter.length()); } // set the remaining string - Set(params, static_cast(str)); + Set(params, str); Main_Data::game_variables->Set(var_id, splits); return splits; } Game_Strings::Str_t Game_Strings::ToFile(Str_Params params, std::string filename, int encoding) { - std::string str = static_cast(Get(params.string_id)); + std::string str = Get(params.string_id); if (params.extract) { - filename = static_cast(Extract(static_cast(filename), params.hex)); + filename = Extract(filename, params.hex); } // this sucks but it is what maniacs does @@ -127,7 +127,7 @@ Game_Strings::Str_t Game_Strings::ToFile(Str_Params params, std::string filename txt_out << str; txt_out.Close(); - return static_cast(str); + return str; } Game_Strings::Str_t Game_Strings::PopLine(Str_Params params, int offset, int string_out_id) { @@ -135,7 +135,7 @@ Game_Strings::Str_t Game_Strings::PopLine(Str_Params params, int offset, int str int index; std::string result; - std::string str = static_cast(Get(params.string_id)); + std::string str = Get(params.string_id); std::stringstream ss(str); @@ -143,9 +143,9 @@ Game_Strings::Str_t Game_Strings::PopLine(Str_Params params, int offset, int str offset = ss.rdbuf()->in_avail(); - Set(params, static_cast(ss.str().substr(str.length() - offset))); + Set(params, ss.str().substr(str.length() - offset)); params.string_id = string_out_id; - return Set(params, static_cast(result)); + return Set(params, result); } Game_Strings::Str_t Game_Strings::ExMatch(Str_Params params, std::string expr, int var_id, int begin, int string_out_id) { @@ -154,10 +154,10 @@ Game_Strings::Str_t Game_Strings::ExMatch(Str_Params params, std::string expr, i std::smatch match; if (params.extract) { - expr = static_cast(Extract(static_cast(expr), params.hex)); + expr = Extract(expr, params.hex); } - std::string base = static_cast(Get(params.string_id)).erase(0, begin); + std::string base = Get(params.string_id).erase(0, begin); std::regex r(expr); std::regex_search(base, match, r); @@ -165,7 +165,7 @@ Game_Strings::Str_t Game_Strings::ExMatch(Str_Params params, std::string expr, i var_result = match.position() + begin; Main_Data::game_variables->Set(var_id, var_result); - str_result = static_cast(match.str()); + str_result = match.str(); if (string_out_id > 0) { params.string_id = string_out_id; Set(params, str_result); @@ -203,11 +203,11 @@ const Game_Strings::Strings_t& Game_Strings::RangeOp(Str_Params params, int stri 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, static_cast(string), args[1], args[2]); break; - case 5: params.string_id += Split(params, static_cast(string), args[1], args[2]); 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 8: break; // range case not applicable for popLine; see case in game_interpreter.cpp - case 9: ExMatch(params, static_cast(string), args[1] + (params.string_id - start), args[2]); break; - case 10: ExMatch(params, static_cast(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]); break; + case 10: ExMatch(params, string, args[1] + (params.string_id - start), args[2], args[3]); break; } } return GetData(); @@ -217,7 +217,7 @@ Game_Strings::Str_t Game_Strings::PrependMin(Str_t 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 (Str_t)res; + return res; } return string; } diff --git a/src/game_strings.h b/src/game_strings.h index 68a73dce09..c7b038df59 100644 --- a/src/game_strings.h +++ b/src/game_strings.h @@ -32,7 +32,7 @@ */ class Game_Strings { public: - using Str_t = lcf::DBString; + using Str_t = std::string; using Strings_t = std::vector; static constexpr int max_warnings = 10; @@ -44,7 +44,9 @@ class Game_Strings { Game_Strings(); void SetData(Strings_t s); + void SetData(std::vector s); const Strings_t& GetData() const; + std::vector GetLcfData(); Str_t Get(int id) const; Str_t GetIndirect(int id) const; @@ -92,10 +94,18 @@ 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 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 bool Game_Strings::ShouldWarn(int id) const { return id <= 0 && _warnings > 0; } @@ -144,7 +154,7 @@ inline Game_Strings::Str_t Game_Strings::GetWithModeAndPos(Str_t str_data, int a Str_t ret; switch (mode) { case 0: - ret = static_cast(static_cast(str_data).substr(*pos, arg)); + ret = str_data.substr(*pos, arg); *pos += arg; break; case 1: // direct string reference diff --git a/src/game_windows.cpp b/src/game_windows.cpp index b30cc8931e..bf4f7cabd7 100644 --- a/src/game_windows.cpp +++ b/src/game_windows.cpp @@ -204,7 +204,7 @@ void Game_Windows::Window_User::Refresh(bool& async_wait) { fonts.emplace_back(font); - std::string extracted_text = ToString(Main_Data::game_strings->Extract(text.text, false)); + std::string extracted_text = Main_Data::game_strings->Extract(ToString(text.text), false); std::stringstream ss(extracted_text); std::string out; PendingMessage pm; diff --git a/src/scene_save.cpp b/src/scene_save.cpp index 7a1e5eae91..c9ee59c4b4 100644 --- a/src/scene_save.cpp +++ b/src/scene_save.cpp @@ -138,7 +138,7 @@ bool Scene_Save::Save(std::ostream& os, int slot_id, bool prepare_save) { save.system = Main_Data::game_system->GetSaveData(); save.system.switches = Main_Data::game_switches->GetData(); save.system.variables = Main_Data::game_variables->GetData(); - save.system.maniac_strings = Main_Data::game_strings->GetData(); + save.system.maniac_strings = Main_Data::game_strings->GetLcfData(); save.inventory = Main_Data::game_party->GetSaveData(); save.actors = Main_Data::game_actors->GetSaveData(); save.screen = Main_Data::game_screen->GetSaveData(); From 95a90ebfffeee353cddb7d502ff2930ac847a08a Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Thu, 13 Jul 2023 08:57:33 -0600 Subject: [PATCH 32/47] refactor maniac helpers into maniac_patch module --- src/game_interpreter.cpp | 135 +----------------------------------- src/game_interpreter.h | 9 --- src/maniac_patch.cpp | 146 ++++++++++++++++++++++++++++++++++++++- src/maniac_patch.h | 9 +++ 4 files changed, 155 insertions(+), 144 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 57635d2644..d6fdd2022a 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -36,7 +36,6 @@ #include "game_targets.h" #include "game_switches.h" #include "game_variables.h" -#include "game_strings.h" #include "game_party.h" #include "game_actors.h" #include "game_system.h" @@ -4769,11 +4768,11 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& break; case 3: //Database Names args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); - result = ManiacGetLcfDataName(args[0], args[1], (bool)args[2]); + result = ManiacPatch::GetLcfName(args[0], args[1], (bool)args[2]); break; case 4: //Database Descriptions args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); - result = ManiacGetLcfDataDescription(args[0], args[1], (bool)args[2]); + result = ManiacPatch::GetLcfDescription(args[0], args[1], (bool)args[2]); break; case 6: //Concatenate (cat) { @@ -5035,134 +5034,4 @@ bool Game_Interpreter::ManiacCheckString(std::string str_l, std::string str_r, i } } -Game_Strings::Str_t Game_Interpreter::ManiacGetLcfDataName(int data_type, int id, bool is_dynamic) const { - switch (data_type) - { - case 0: //.actor[a].name - if (is_dynamic) { - auto actor = Main_Data::game_actors->GetActor(id); - if (actor != nullptr) { - return static_cast(actor->GetName()); - } - } else { - return Game_Interpreter::ManiacGetNameSafely(lcf::Data::actors, id); - } - break; - case 1: return Game_Interpreter::ManiacGetNameSafely(lcf::Data::skills, id); //.skill[a].name - case 2: return Game_Interpreter::ManiacGetNameSafely(lcf::Data::items, id); //.item[a].name - case 3: return Game_Interpreter::ManiacGetNameSafely(lcf::Data::enemies, id); //.enemy[a].name - case 4: return Game_Interpreter::ManiacGetNameSafely(lcf::Data::troops, id); //.troop[a].name - case 5: return Game_Interpreter::ManiacGetNameSafely(lcf::Data::terrains, id); //.terrain[a].name - case 6: return Game_Interpreter::ManiacGetNameSafely(lcf::Data::attributes, id); //.element[a].name - case 7: return Game_Interpreter::ManiacGetNameSafely(lcf::Data::states, id); //.state[a].name - case 8: return Game_Interpreter::ManiacGetNameSafely(lcf::Data::animations, id); //.anim[a].name - case 9: return Game_Interpreter::ManiacGetNameSafely(lcf::Data::chipsets, id); //.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 12: break; //.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(); - } - break; - } - case 14: return Game_Interpreter::ManiacGetNameSafely(lcf::Data::classes, id); //.class[a].name - case 15: return Game_Interpreter::ManiacGetNameSafely(lcf::Data::battleranimations, id); //.anim2[a].name - case 16: return static_cast(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()); - } - break; - } - case 18: //.member[a].name - { - auto actor = Main_Data::game_party->GetActor(id); - if (actor != nullptr) { - if (is_dynamic) { - return static_cast(actor->GetName()); - } - else { - id = actor->GetId(); - return Game_Interpreter::ManiacGetNameSafely(lcf::Data::actors, id); - } - } - break; - } - } - - Output::Warning("Unable to read name: {} {}", data_type, id); - return ""; -} - -Game_Strings::Str_t Game_Interpreter::ManiacGetLcfDataDescription(int data_type, int id, bool is_dynamic) const { - switch (data_type) - { - case 0: //.actor[a].desc - if (is_dynamic) { - auto actor = Main_Data::game_actors->GetActor(id); - if (actor != nullptr) { - return static_cast(actor->GetTitle()); - } - } else { - return Game_Interpreter::ManiacGetDescriptionSafely(lcf::Data::actors, id); - } - break; - case 1: return Game_Interpreter::ManiacGetDescriptionSafely(lcf::Data::skills, id); //.skill[a].desc - case 2: return Game_Interpreter::ManiacGetDescriptionSafely(lcf::Data::items, id); //.item[a].desc - case 18: //.member[a].desc - { - auto actor = Main_Data::game_party->GetActor(id); - if (actor != nullptr) { - if (is_dynamic) { - return static_cast(actor->GetTitle()); - } - else { - id = actor->GetId(); - return Game_Interpreter::ManiacGetDescriptionSafely(lcf::Data::actors, id); - } - } - break; - } - } - - Output::Warning("Unable to read description: {} {}", data_type, id); - return ""; -} - -template -Game_Strings::Str_t Game_Interpreter::ManiacGetNameSafely(std::vector vec, int id) const { - T* data = lcf::ReaderUtil::GetElement(vec, id); - if (data != nullptr) { - return static_cast(data->name); - } - - Output::Warning("Unable to read name: {}", id); - return ""; -} - -template -Game_Strings::Str_t Game_Interpreter::ManiacGetDescriptionSafely(std::vector vec, int id) const { - T* data = lcf::ReaderUtil::GetElement(vec, id); - if (data != nullptr) { - return static_cast(data->description); - } - - Output::Warning("Unable to read description: {}", id); - return ""; -} -template <> -Game_Strings::Str_t Game_Interpreter::ManiacGetDescriptionSafely(std::vector vec, int id) const { - lcf::rpg::Actor* data = lcf::ReaderUtil::GetElement(vec, id); - if (data != nullptr) { - return static_cast(data->title); - } - - Output::Warning("Unable to read description: {}", id); - return ""; -} diff --git a/src/game_interpreter.h b/src/game_interpreter.h index f7b582f43f..a24c51e9dd 100644 --- a/src/game_interpreter.h +++ b/src/game_interpreter.h @@ -336,15 +336,6 @@ class Game_Interpreter lcf::rpg::SaveEventExecState _state; KeyInputState _keyinput; AsyncOp _async_op = {}; - - std::string ManiacGetLcfDataName(int data_type, int id, bool is_dynamic) const; - std::string ManiacGetLcfDataDescription(int data_type, int id, bool is_dynamic) const; - -private: - template std::string ManiacGetNameSafely(std::vector, int id) const; - template std::string ManiacGetDescriptionSafely(std::vector, int id) const; - template<> std::string ManiacGetDescriptionSafely(std::vector, int id) const; - }; inline const lcf::rpg::SaveEventExecFrame* Game_Interpreter::GetFramePtr() const { diff --git a/src/maniac_patch.cpp b/src/maniac_patch.cpp index a03c607d07..325a9b099c 100644 --- a/src/maniac_patch.cpp +++ b/src/maniac_patch.cpp @@ -16,14 +16,19 @@ */ #include "maniac_patch.h" + +#include "input.h" +#include "game_actors.h" #include "game_interpreter_control_variables.h" -#include "main_data.h" +#include "game_map.h" #include "game_interpreter.h" +#include "game_party.h" #include "game_switches.h" #include "game_variables.h" +#include "main_data.h" #include "output.h" -#include "input.h" +#include #include /* @@ -600,4 +605,141 @@ bool ManiacPatch::GetKeyState(uint32_t key_id) { } return Input::IsRawKeyPressed(key); + + + +} + +Game_Strings::Str_t ManiacPatch::GetLcfName(int data_type, int id, bool is_dynamic) { + switch (data_type) + { + case 0: //.actor[a].name + if (is_dynamic) { + auto actor = Main_Data::game_actors->GetActor(id); + if (actor != nullptr) { + return static_cast(actor->GetName()); + } + } + else { + return GetNameSafely(lcf::Data::actors, id); + } + break; + case 1: return GetNameSafely(lcf::Data::skills, id); //.skill[a].name + case 2: return GetNameSafely(lcf::Data::items, id); //.item[a].name + case 3: return GetNameSafely(lcf::Data::enemies, id); //.enemy[a].name + case 4: return GetNameSafely(lcf::Data::troops, id); //.troop[a].name + case 5: return GetNameSafely(lcf::Data::terrains, id); //.terrain[a].name + case 6: return GetNameSafely(lcf::Data::attributes, id); //.element[a].name + case 7: return GetNameSafely(lcf::Data::states, id); //.state[a].name + case 8: return GetNameSafely(lcf::Data::animations, id); //.anim[a].name + case 9: return GetNameSafely(lcf::Data::chipsets, id); //.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 12: break; //.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(); + } + break; + } + case 14: return GetNameSafely(lcf::Data::classes, id); //.class[a].name + case 15: return GetNameSafely(lcf::Data::battleranimations, id); //.anim2[a].name + case 16: return static_cast(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()); + } + break; + } + case 18: //.member[a].name + { + auto actor = Main_Data::game_party->GetActor(id); + if (actor != nullptr) { + if (is_dynamic) { + return static_cast(actor->GetName()); + } + else { + id = actor->GetId(); + return GetNameSafely(lcf::Data::actors, id); + } + } + break; + } + } + + Output::Warning("Unable to read name: {} {}", data_type, id); + return ""; +} + +Game_Strings::Str_t ManiacPatch::GetLcfDescription(int data_type, int id, bool is_dynamic) { + switch (data_type) + { + case 0: //.actor[a].desc + if (is_dynamic) { + auto actor = Main_Data::game_actors->GetActor(id); + if (actor != nullptr) { + return static_cast(actor->GetTitle()); + } + } + else { + return GetDescriptionSafely(lcf::Data::actors, id); + } + break; + case 1: return GetDescriptionSafely(lcf::Data::skills, id); //.skill[a].desc + case 2: return GetDescriptionSafely(lcf::Data::items, id); //.item[a].desc + case 18: //.member[a].desc + { + auto actor = Main_Data::game_party->GetActor(id); + if (actor != nullptr) { + if (is_dynamic) { + return static_cast(actor->GetTitle()); + } + else { + id = actor->GetId(); + return GetDescriptionSafely(lcf::Data::actors, id); + } + } + break; + } + } + + Output::Warning("Unable to read description: {} {}", data_type, id); + return ""; +} + +template +Game_Strings::Str_t ManiacPatch::GetNameSafely(std::vector vec, int id) { + T* data = lcf::ReaderUtil::GetElement(vec, id); + if (data != nullptr) { + return static_cast(data->name); + } + + Output::Warning("Unable to read name: {}", id); + return ""; +} + +template +Game_Strings::Str_t ManiacPatch::GetDescriptionSafely(std::vector vec, int id) { + T* data = lcf::ReaderUtil::GetElement(vec, id); + if (data != nullptr) { + return static_cast(data->description); + } + + Output::Warning("Unable to read description: {}", id); + return ""; +} + +template <> +Game_Strings::Str_t ManiacPatch::GetDescriptionSafely(std::vector vec, int id) { + lcf::rpg::Actor* data = lcf::ReaderUtil::GetElement(vec, id); + if (data != nullptr) { + return static_cast(data->title); + } + + Output::Warning("Unable to read description: {}", id); + return ""; } diff --git a/src/maniac_patch.h b/src/maniac_patch.h index d86353b3ac..f17f72e5ef 100644 --- a/src/maniac_patch.h +++ b/src/maniac_patch.h @@ -23,6 +23,8 @@ #include #include "span.h" +#include "game_strings.h" + class Game_Interpreter; namespace ManiacPatch { @@ -31,6 +33,13 @@ namespace ManiacPatch { std::array GetKeyRange(); bool GetKeyState(uint32_t key_id); + + 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); + + template Game_Strings::Str_t GetNameSafely(std::vector vec, int id); + template Game_Strings::Str_t GetDescriptionSafely(std::vector vec, int id); + template <> Game_Strings::Str_t GetDescriptionSafely(std::vector vec, int id); } #endif From f540e2c64ba8823a8fb99ff0ef09071e9332bff3 Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Thu, 13 Jul 2023 12:36:12 -0600 Subject: [PATCH 33/47] StringVar: add switch to string conversion also some more refactors --- src/game_interpreter.cpp | 38 ++++++++++++++------------------------ src/maniac_patch.cpp | 22 ++++++++++++++++++++++ src/maniac_patch.h | 2 ++ 3 files changed, 38 insertions(+), 24 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index d6fdd2022a..3975441a63 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -3616,7 +3616,7 @@ bool Game_Interpreter::CommandConditionalBranch(lcf::rpg::EventCommand const& co 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]); - result = ManiacCheckString(str_l, str_r, op, ignoreCase); + result = ManiacPatch::CheckString(str_l, str_r, op, ignoreCase); } break; default: @@ -4766,6 +4766,19 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& result = std::to_string(args[0]); result = Main_Data::game_strings->PrependMin(result, args[1], '0'); break; + case 2: //Switch + { + if (modes[0] == 1) args[0] = Main_Data::game_variables->Get(args[0]); + args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); + + if (Main_Data::game_switches->Get(args[0])) { + result = "ON"; + } else { + result = "OFF"; + } + result = Main_Data::game_strings->PrependMin(result, args[1], ' '); + break; + } case 3: //Database Names args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); result = ManiacPatch::GetLcfName(args[0], args[1], (bool)args[2]); @@ -5012,26 +5025,3 @@ bool Game_Interpreter::ManiacCheckContinueLoop(int val, int val2, int type, int return false; } } - -bool Game_Interpreter::ManiacCheckString(std::string str_l, std::string str_r, int op, bool ignore_case) const { - std::regex specialChars { R"([-[\]{}()*+?.,\^$|#\s])" }; - std::string sanitized_r = std::regex_replace(str_r, specialChars, R"(\$&)"); - if (op > 1) { sanitized_r = ".*" + sanitized_r + ".*"; } - - std::regex search; - if (ignore_case) { search = std::regex(sanitized_r, std::regex_constants::icase); } - else { search = std::regex(sanitized_r); } - - switch (op) { - case 0: // eq - case 2: // contains (l contains r) - return std::regex_match(str_l, search); - case 1: // neq - case 3: // notContains (l does not contain r) - return !std::regex_match(str_l, search); - default: - return false; - } -} - - diff --git a/src/maniac_patch.cpp b/src/maniac_patch.cpp index 325a9b099c..db6d20ec62 100644 --- a/src/maniac_patch.cpp +++ b/src/maniac_patch.cpp @@ -29,6 +29,7 @@ #include "output.h" #include +#include #include /* @@ -610,6 +611,27 @@ bool ManiacPatch::GetKeyState(uint32_t key_id) { } +bool ManiacPatch::CheckString(std::string str_l, std::string str_r, int op, bool ignore_case) { + std::regex specialChars { R"([-[\]{}()*+?.,\^$|#\s])" }; + std::string sanitized_r = std::regex_replace(str_r, specialChars, R"(\$&)"); + if (op > 1) { sanitized_r = ".*" + sanitized_r + ".*"; } + + std::regex search; + if (ignore_case) { search = std::regex(sanitized_r, std::regex_constants::icase); } + else { search = std::regex(sanitized_r); } + + switch (op) { + case 0: // eq + case 2: // contains (l contains r) + return std::regex_match(str_l, search); + case 1: // neq + case 3: // notContains (l does not contain r) + return !std::regex_match(str_l, search); + default: + return false; + } +} + Game_Strings::Str_t ManiacPatch::GetLcfName(int data_type, int id, bool is_dynamic) { switch (data_type) { diff --git a/src/maniac_patch.h b/src/maniac_patch.h index f17f72e5ef..8b2fe23d01 100644 --- a/src/maniac_patch.h +++ b/src/maniac_patch.h @@ -34,6 +34,8 @@ namespace ManiacPatch { bool GetKeyState(uint32_t key_id); + bool CheckString(std::string str_l, std::string 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); From 48c87d5fc093c68f91ffe957f5c6a045776f0d22 Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Thu, 12 Oct 2023 12:48:04 -0600 Subject: [PATCH 34/47] StringVar: fix game_message string param parse result --- src/game_message.cpp | 2 +- src/pending_message.cpp | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/game_message.cpp b/src/game_message.cpp index 849d4cf22c..c64b0b52bd 100644 --- a/src/game_message.cpp +++ b/src/game_message.cpp @@ -324,7 +324,7 @@ Game_Message::ParseParamResult Game_Message::ParseVariable(const char* iter, con } Game_Message::ParseParamResult Game_Message::ParseString(const char* iter, const char* end, uint32_t escape_char, bool skip_prefix, int max_recursion) { - return ParseParamImpl('T', 't', iter, end, escape_char, skip_prefix, max_recursion); + return ParseParam('T', 't', iter, end, escape_char, skip_prefix, max_recursion); } Game_Message::ParseParamResult Game_Message::ParseColor(const char* iter, const char* end, uint32_t escape_char, bool skip_prefix, int max_recursion) { diff --git a/src/pending_message.cpp b/src/pending_message.cpp index c30b5a55bb..15bab8218a 100644 --- a/src/pending_message.cpp +++ b/src/pending_message.cpp @@ -160,20 +160,20 @@ std::optional PendingMessage::DefaultCommandInserter(char ch, const int variable_value = Main_Data::game_variables->Get(value); return std::to_string(variable_value); - } - - return std::nullopt; -}; - -std::optional PendingMessage::ManiacsCommandInserter(char ch, const char** iter, const char* end, uint32_t escape_char) { - if (ch == 'T' || ch == 't') { + } else if (ch == 'T' || ch == 't') { auto parse_ret = Game_Message::ParseString(*iter, end, escape_char, true); *iter = parse_ret.next; int value = parse_ret.value; Game_Strings::Str_t string = Main_Data::game_strings->Get(value); return (std::string)string; - } else if (ch == 'S' || ch == 's') { + } + + return std::nullopt; +}; + +std::optional PendingMessage::ManiacsCommandInserter(char ch, const char** iter, const char* end, uint32_t escape_char) { + if (ch == 'S' || ch == 's') { // parsing a switch within an extracted string var command will parse \s[N] as a switch (ON/OFF) auto parse_ret = Game_Message::ParseSpeed(*iter, end, escape_char, true); *iter = parse_ret.next; From c6349be9a73429c1687a85e9fcb745157b10729e Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Thu, 12 Oct 2023 16:36:56 -0600 Subject: [PATCH 35/47] StringVar: change string comparison strategy, remove regex --- src/maniac_patch.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/maniac_patch.cpp b/src/maniac_patch.cpp index db6d20ec62..5078253688 100644 --- a/src/maniac_patch.cpp +++ b/src/maniac_patch.cpp @@ -29,7 +29,6 @@ #include "output.h" #include -#include #include /* @@ -612,21 +611,20 @@ bool ManiacPatch::GetKeyState(uint32_t key_id) { } bool ManiacPatch::CheckString(std::string str_l, std::string str_r, int op, bool ignore_case) { - std::regex specialChars { R"([-[\]{}()*+?.,\^$|#\s])" }; - std::string sanitized_r = std::regex_replace(str_r, specialChars, R"(\$&)"); - if (op > 1) { sanitized_r = ".*" + sanitized_r + ".*"; } - - std::regex search; - if (ignore_case) { search = std::regex(sanitized_r, std::regex_constants::icase); } - else { search = std::regex(sanitized_r); } + if (ignore_case) { + str_l = Utils::LowerCase(str_l); + str_r = Utils::LowerCase(str_r); + } switch (op) { case 0: // eq + return strcmp(str_l.c_str(), str_r.c_str()) == 0; case 2: // contains (l contains r) - return std::regex_match(str_l, search); + return str_l.find(str_r) != std::string::npos; case 1: // neq + return strcmp(str_l.c_str(), str_r.c_str()) != 0; case 3: // notContains (l does not contain r) - return !std::regex_match(str_l, search); + return str_l.find(str_r) == std::string::npos; default: return false; } From 5a9ef0644364cd61c4e2f4111d7d51a6035207f7 Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Thu, 12 Oct 2023 16:56:54 -0600 Subject: [PATCH 36/47] StringVar: use Utils ReadLine instead of std getline --- src/game_interpreter.cpp | 1 + src/game_strings.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 3975441a63..80fef75986 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -4861,6 +4861,7 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& // maniacs does not like a file extension Game_Strings::Str_t filename = Main_Data::game_strings->GetWithMode(str_param, args[0], modes[0]); // args[1] is the encoding... 0 for sjis, 1 for utf8 + // FIXME: support multiple encodings Filesystem_Stream::InputStream is = FileFinder::OpenText(StringView(filename)); if (is.good()) { diff --git a/src/game_strings.cpp b/src/game_strings.cpp index 8102d37e49..ac2784a3a9 100644 --- a/src/game_strings.cpp +++ b/src/game_strings.cpp @@ -131,6 +131,7 @@ Game_Strings::Str_t Game_Strings::ToFile(Str_Params params, std::string filename } Game_Strings::Str_t 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 ""; int index; @@ -139,7 +140,7 @@ Game_Strings::Str_t Game_Strings::PopLine(Str_Params params, int offset, int str std::stringstream ss(str); - while (offset >= 0 && std::getline(ss, result)) { offset--; } + while (offset >= 0 && Utils::ReadLine(ss, result)) { offset--; } offset = ss.rdbuf()->in_avail(); From 8d32f7a9b3f83d241d709d74a912041acd94eeaf Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Thu, 12 Oct 2023 20:26:55 -0600 Subject: [PATCH 37/47] StringVar: use ValueOrVariable and remove redundant helper function --- src/game_interpreter.cpp | 42 ++++++++++++++++++++-------------------- src/game_variables.h | 11 ----------- 2 files changed, 21 insertions(+), 32 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 80fef75986..446d3abe7a 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -4754,14 +4754,14 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& { case 0: //String result = Main_Data::game_strings->GetWithMode(str_param, args[0], modes[0]); - args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); + args[1] = ValueOrVariable(modes[1], args[1]); // min_size result = Main_Data::game_strings->PrependMin(result, args[1], ' '); break; case 1: //Number - args[0] = Main_Data::game_variables->GetWithMode(args[0], modes[0]); - args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); + 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'); @@ -4769,7 +4769,7 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& case 2: //Switch { if (modes[0] == 1) args[0] = Main_Data::game_variables->Get(args[0]); - args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); + args[1] = ValueOrVariable(modes[1], args[1]); if (Main_Data::game_switches->Get(args[0])) { result = "ON"; @@ -4780,11 +4780,11 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& break; } case 3: //Database Names - args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); + args[1] = ValueOrVariable(modes[1], args[1]); result = ManiacPatch::GetLcfName(args[0], args[1], (bool)args[2]); break; case 4: //Database Descriptions - args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); + args[1] = ValueOrVariable(modes[1], args[1]); result = ManiacPatch::GetLcfDescription(args[0], args[1], (bool)args[2]); break; case 6: //Concatenate (cat) @@ -4802,7 +4802,7 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& int pos = 0; std::string base, insert; - args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); + 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); @@ -4828,8 +4828,8 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& break; } case 9: //Substring (subs) - args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); - args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[2]); + 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]); break; case 10: //Join (join) @@ -4840,8 +4840,8 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& Output::Debug("modes {} {} {}", modes[0], modes[1], modes[2]); // 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 - args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1] % 2); - args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[2]); + args[1] = ValueOrVariable(modes[1] % 2, args[1]); + args[2] = ValueOrVariable(modes[2], args[2]); while (args[2] > 0) { if (modes[1] < 2) { @@ -4873,8 +4873,8 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& break; } case 13: //Remove (rem) - args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); - args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[2]); + 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]); break; case 14: //Replace Ex (exRep) , edge case: the arg "first" is at ((flags >> 19) & 1). Wtf BingShan @@ -4913,8 +4913,8 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& case 4: //inStr takes hex?????? { Game_Strings::Str_t search = Main_Data::game_strings->GetWithMode(str_param, args[0], modes[0]); - args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); // not sure this is necessary but better safe - args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[2]); + 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]); @@ -4923,8 +4923,8 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& case 5: //split takes hex { Game_Strings::Str_t delimiter = Main_Data::game_strings->GetWithMode(str_param, args[0], modes[0]); - args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); - args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[2]); + 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]); @@ -4933,7 +4933,7 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& case 7: //toFile takes hex { std::string filename = Main_Data::game_strings->GetWithMode(str_param, args[0], modes[0]); - args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); + args[1] = ValueOrVariable(modes[1], args[1]); Main_Data::game_strings->ToFile(str_params, filename, args[1]); break; @@ -4943,7 +4943,7 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& // it instead alters the behavior. // given a range t[a..b], it will pop the first (b-a)+1 lines, // and store the last popped line into the output string. - args[1] = Main_Data::game_variables->GetWithMode(args[0], modes[0]); + args[1] = ValueOrVariable(modes[0], args[0]); if (is_range) Main_Data::game_strings->PopLine(str_params, string_id_1 - string_id_0, args[0]); else Main_Data::game_strings->PopLine(str_params, 0, args[0]); @@ -4953,8 +4953,8 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& { // takes hex std::string expr = Main_Data::game_strings->GetWithMode(str_param, args[0], modes[0]); - args[1] = Main_Data::game_variables->GetWithMode(args[1], modes[1]); // output var - args[2] = Main_Data::game_variables->GetWithMode(args[2], modes[2]); // beginning pos + 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); diff --git a/src/game_variables.h b/src/game_variables.h index 6be5c08cb5..0d6e9382e2 100644 --- a/src/game_variables.h +++ b/src/game_variables.h @@ -206,17 +206,6 @@ inline Game_Variables::Var_t Game_Variables::GetIndirect(int variable_id) const return Get(static_cast(val_indirect)); } -// mode 0: pass through id value -// mode 1: get by id -// mode 2: get indirect by id -inline Game_Variables::Var_t Game_Variables::GetWithMode(int id, int mode) const { - switch (mode) { - case 1: return Get(id); break; - case 2: return GetIndirect(id); break; - } - return id; -} - inline void Game_Variables::SetWarning(int w) { _warnings = w; } From c4f1ac352e0f8a322b892861a36aa19dbbbf679a Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Thu, 12 Oct 2023 20:37:43 -0600 Subject: [PATCH 38/47] updated to read files from Save first before game files --- src/filefinder.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/filefinder.cpp b/src/filefinder.cpp index 622c7451ff..463628f5db 100644 --- a/src/filefinder.cpp +++ b/src/filefinder.cpp @@ -339,9 +339,9 @@ std::string find_generic(const DirectoryTree::Args& args) { } std::string find_generic_with_fallback(DirectoryTree::Args& args) { - std::string found = find_generic(args); + std::string found = FileFinder::Save().FindFile(args); if (found.empty()) { - return FileFinder::Save().FindFile(args); + return find_generic(args); } return found; @@ -393,8 +393,8 @@ Filesystem_Stream::InputStream open_generic(StringView dir, StringView name, Dir } Filesystem_Stream::InputStream open_generic_with_fallback(StringView dir, StringView name, DirectoryTree::Args& args) { - auto is = open_generic(dir, name, args); - if (!is) { is = FileFinder::Save().OpenFile(args); } + auto is = FileFinder::Save().OpenFile(args); + if (!is) { is = open_generic(dir, name, args); } if (!is) { Output::Debug("Unable to open in either Game or Save: {}/{}", dir, name); } From 9e5aa8807f400add839115cee671a52751ef7f9f Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Thu, 12 Oct 2023 22:38:14 -0600 Subject: [PATCH 39/47] StringVar: remove string extract logic from game_windows some related code changed during the rebase and this no longer appears to be necessary --- src/game_strings.h | 3 ++- src/game_windows.cpp | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/game_strings.h b/src/game_strings.h index c7b038df59..10850eeb42 100644 --- a/src/game_strings.h +++ b/src/game_strings.h @@ -85,8 +85,9 @@ inline Game_Strings::Str_t Game_Strings::Set(Str_Params params, Str_t string) { auto& s = _strings[params.string_id - 1]; s = string; - if (params.extract) + if (params.extract) { s = Extract(s, params.hex); + } return s; } diff --git a/src/game_windows.cpp b/src/game_windows.cpp index bf4f7cabd7..4728f4636e 100644 --- a/src/game_windows.cpp +++ b/src/game_windows.cpp @@ -204,8 +204,7 @@ void Game_Windows::Window_User::Refresh(bool& async_wait) { fonts.emplace_back(font); - std::string extracted_text = Main_Data::game_strings->Extract(ToString(text.text), false); - std::stringstream ss(extracted_text); + std::stringstream ss(ToString(text.text)); std::string out; PendingMessage pm; while (Utils::ReadLine(ss, out)) { From 2bbf64de8fc21a51da21b500ef7e2f70bd0e0175 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Sun, 22 Oct 2023 14:16:44 +0200 Subject: [PATCH 40/47] StringVar: Support files with Ansi encoding Fix crash when older string var commands are used. --- src/filefinder.cpp | 2 +- src/game_interpreter.cpp | 29 +++++++++++------------- src/game_strings.cpp | 48 +++++++++++++++++++++++++++++++--------- src/game_strings.h | 6 +++-- 4 files changed, 56 insertions(+), 29 deletions(-) diff --git a/src/filefinder.cpp b/src/filefinder.cpp index 463628f5db..b7885e48a5 100644 --- a/src/filefinder.cpp +++ b/src/filefinder.cpp @@ -399,7 +399,7 @@ Filesystem_Stream::InputStream open_generic_with_fallback(StringView dir, String Output::Debug("Unable to open in either Game or Save: {}/{}", dir, name); } - return is; + return is; } Filesystem_Stream::InputStream FileFinder::OpenImage(StringView dir, StringView name) { diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 446d3abe7a..4f952494f0 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -4314,11 +4314,16 @@ bool Game_Interpreter::CommandManiacShowStringPicture(lcf::rpg::EventCommand con return true; } - text.text = static_cast(Main_Data::game_strings->GetWithMode( - static_cast(components[1]), - com.parameters[22], - delims[0]-1 - )); + if (com.parameters.size() >= 23) { + text.text = Main_Data::game_strings->GetWithMode( + components[1], + com.parameters[22], + delims[0] - 1 + ); + } else { + text.text = components[1]; + } + params.system_name = components[2]; text.font_name = components[3]; @@ -4860,16 +4865,8 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& { // maniacs does not like a file extension Game_Strings::Str_t filename = Main_Data::game_strings->GetWithMode(str_param, args[0], modes[0]); - // args[1] is the encoding... 0 for sjis, 1 for utf8 - // FIXME: support multiple encodings - - Filesystem_Stream::InputStream is = FileFinder::OpenText(StringView(filename)); - if (is.good()) { - std::stringstream buffer; - buffer << is.rdbuf(); - result = buffer.str(); - } - is.Close(); + // args[1] is the encoding... 0 for ansi, 1 for utf8 + result = Game_Strings::FromFile(filename, args[1]); break; } case 13: //Remove (rem) @@ -4910,7 +4907,7 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& if (op == 3) Main_Data::game_strings->GetLen(str_params, args[0]); } break; - case 4: //inStr takes hex?????? + case 4: //inStr FIXME: takes hex? { Game_Strings::Str_t search = Main_Data::game_strings->GetWithMode(str_param, args[0], modes[0]); args[1] = ValueOrVariable(modes[1], args[1]); // not sure this is necessary but better safe diff --git a/src/game_strings.cpp b/src/game_strings.cpp index ac2784a3a9..1edea035a6 100644 --- a/src/game_strings.cpp +++ b/src/game_strings.cpp @@ -17,13 +17,11 @@ // Headers #include +#include #include "game_strings.h" #include "game_variables.h" #include "output.h" -Game_Strings::Game_Strings() -{} - void Game_Strings::WarnGet(int id) const { Output::Debug("Invalid read strvar[{}]!", id); --_warnings; @@ -49,9 +47,9 @@ int Game_Strings::ToNum(Str_Params params, int var_id) { int num; if (params.hex) - num = std::stoi(str, 0, 16); + num = static_cast(std::strtol(str.c_str(), nullptr, 16)); else - num = std::stoi(str); + num = static_cast(std::strtol(str.c_str(), nullptr, 0)); Main_Data::game_variables->Set(var_id, num); return num; @@ -107,6 +105,23 @@ int Game_Strings::Split(Str_Params params, std::string delimiter, int string_out return splits; } +Game_Strings::Str_t Game_Strings::FromFile(StringView filename, int encoding) { + Filesystem_Stream::InputStream is = FileFinder::OpenText(filename); + if (!is) { + return {}; + } + + auto vec = Utils::ReadStream(is); + Str_t file_content(vec.begin(), vec.end()); + + if (encoding == 0) { + lcf::Encoder enc(Player::encoding); + enc.Decode(file_content); + } + + 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); @@ -114,14 +129,27 @@ Game_Strings::Str_t Game_Strings::ToFile(Str_Params params, std::string filename filename = Extract(filename, params.hex); } - // this sucks but it is what maniacs does + // Maniacs forces the File in Text/ folder with .txt extension + // TODO: Maybe relax this? filename = "Text/" + filename + ".txt"; - auto txt_out = FileFinder::Game().OpenOutputStream(filename); - if (!txt_out) { txt_out = FileFinder::Save().OpenOutputStream(filename); } + auto txt_out = FileFinder::Save().OpenOutputStream(filename); if (!txt_out) { - Output::Warning("Maniac String Op toFile failed!"); - return ""; + if (!FileFinder::Save().MakeDirectory("Text", false)) { + Output::Warning("Maniac String Op ToFile failed: Cannot create Text directory"); + return {}; + } + + txt_out = FileFinder::Save().OpenOutputStream(filename); + if (!txt_out) { + Output::Warning("Maniac String Op ToFile failed: Cannot write to {}", filename); + return {}; + } + } + + if (encoding == 0) { + lcf::Encoder enc(Player::encoding); + enc.Encode(str); } txt_out << str; diff --git a/src/game_strings.h b/src/game_strings.h index 10850eeb42..d1caf52ef5 100644 --- a/src/game_strings.h +++ b/src/game_strings.h @@ -35,13 +35,14 @@ class Game_Strings { using Str_t = std::string; using Strings_t = std::vector; - static constexpr int max_warnings = 10; + // Warnings disabled for now as there is no way to predefine text strings in the database + static constexpr int max_warnings = 0; struct Str_Params { int string_id, hex, extract; }; - Game_Strings(); + Game_Strings() = default; void SetData(Strings_t s); void SetData(std::vector s); @@ -59,6 +60,7 @@ class Game_Strings { 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); 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); From ed006339c6df8c98c33a4e39067bde4bd8cf4e91 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Sun, 22 Oct 2023 14:17:17 +0200 Subject: [PATCH 41/47] String Pic: Interpret Linebreaks (\n) --- src/game_windows.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/game_windows.cpp b/src/game_windows.cpp index 4728f4636e..19d899502d 100644 --- a/src/game_windows.cpp +++ b/src/game_windows.cpp @@ -254,6 +254,19 @@ void Game_Windows::Window_User::Refresh(bool& async_wait) { const auto ch = tret.ch; + if (ch == '\n') { + if (!line32.empty()) { + x += Text::GetSize(*font, Utils::EncodeUTF(line32)).width; + line32.clear(); + } + + x_max = std::max(x, x_max); + x = 0; + y += text.font_size + text.line_spacing; + + continue; + } + if (Utils::IsControlCharacter(ch)) { // control characters not handled continue; @@ -371,6 +384,17 @@ void Game_Windows::Window_User::Refresh(bool& async_wait) { const auto ch = tret.ch; + if (ch == '\n') { + if (!line32.empty()) { + Text::Draw(*window->GetContents(), x, y, *font, *system, text_color, Utils::EncodeUTF(line32)); + line32.clear(); + } + + x = 0; + y += text.font_size + text.line_spacing; + continue; + } + if (Utils::IsControlCharacter(ch)) { // control characters not handled continue; From 069828e993bfb2a730a5ae0ff9348d74cf4746c0 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Sun, 22 Oct 2023 14:26:05 +0200 Subject: [PATCH 42/47] String Var: Move Maniac Command Inserters to Game_Strings --- src/game_strings.cpp | 46 ++++++++++++++++++++++++++++++++++++++--- src/game_strings.h | 8 +++---- src/pending_message.cpp | 41 ------------------------------------ src/pending_message.h | 3 --- 4 files changed, 46 insertions(+), 52 deletions(-) diff --git a/src/game_strings.cpp b/src/game_strings.cpp index 1edea035a6..d16c78fd0c 100644 --- a/src/game_strings.cpp +++ b/src/game_strings.cpp @@ -18,7 +18,9 @@ // Headers #include #include +#include "game_message.h" #include "game_strings.h" +#include "game_switches.h" #include "game_variables.h" #include "output.h" @@ -217,9 +219,7 @@ const Game_Strings::Strings_t& Game_Strings::RangeOp(Str_Params params, int stri // swap so that id_0 is < id_1 if (params.string_id > string_id_1) { - params.string_id = params.string_id ^ string_id_1; - string_id_1 = string_id_1 ^ params.string_id; - params.string_id = params.string_id ^ string_id_1; + std::swap(params.string_id, string_id_1); } if (EP_UNLIKELY(string_id_1 > static_cast(_strings.size()))) { @@ -251,3 +251,43 @@ Game_Strings::Str_t Game_Strings::PrependMin(Str_t string, int min_size, char c) return string; } +Game_Strings::Str_t Game_Strings::Extract(Str_t string, bool as_hex) { + PendingMessage::CommandInserter cmd_fn; + + if (as_hex) { + cmd_fn = ManiacsCommandInserterHex; + } else { + cmd_fn = ManiacsCommandInserter; + } + + return static_cast(PendingMessage::ApplyTextInsertingCommands(string, Player::escape_char, cmd_fn)); +} + +std::optional Game_Strings::ManiacsCommandInserter(char ch, const char** iter, const char* end, uint32_t escape_char) { + if (ch == 'S' || ch == 's') { + // \s in a normal message is the speed modifier + // parsing a switch within an extracted string var command will parse \s[N] as a switch (ON/OFF) + auto parse_ret = Game_Message::ParseSpeed(*iter, end, escape_char, true); + *iter = parse_ret.next; + int value = parse_ret.value; + + return Main_Data::game_switches->Get(value) ? "ON" : "OFF"; + } + + return PendingMessage::DefaultCommandInserter(ch, iter, end, escape_char); +}; + +std::optional Game_Strings::ManiacsCommandInserterHex(char ch, const char** iter, const char* end, uint32_t escape_char) { + if (ch == 'V' || ch == 'v') { + auto parse_ret = Game_Message::ParseVariable(*iter, end, escape_char, true); + *iter = parse_ret.next; + int value = parse_ret.value; + + int variable_value = Main_Data::game_variables->Get(value); + std::ostringstream ss; + ss << std::hex << variable_value; + return ss.str(); + } + + return ManiacsCommandInserter(ch, iter, end, escape_char); +}; diff --git a/src/game_strings.h b/src/game_strings.h index d1caf52ef5..514b8e4b17 100644 --- a/src/game_strings.h +++ b/src/game_strings.h @@ -70,6 +70,9 @@ class Game_Strings { Str_t PrependMin(Str_t string, int min_size, char c); Str_t Extract(Str_t 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); @@ -169,8 +172,3 @@ inline Game_Strings::Str_t Game_Strings::GetWithModeAndPos(Str_t str_data, int a } return ret; } - -inline Game_Strings::Str_t Game_Strings::Extract(Str_t string, bool as_hex) { - PendingMessage::CommandInserter cmd_fn = PendingMessage::BuildManiacsCommandInserter(as_hex); - return static_cast(PendingMessage::ApplyTextInsertingCommands(static_cast(string), Player::escape_char, cmd_fn)); -} diff --git a/src/pending_message.cpp b/src/pending_message.cpp index 15bab8218a..427b5711ab 100644 --- a/src/pending_message.cpp +++ b/src/pending_message.cpp @@ -171,44 +171,3 @@ std::optional PendingMessage::DefaultCommandInserter(char ch, const return std::nullopt; }; - -std::optional PendingMessage::ManiacsCommandInserter(char ch, const char** iter, const char* end, uint32_t escape_char) { - if (ch == 'S' || ch == 's') { - // parsing a switch within an extracted string var command will parse \s[N] as a switch (ON/OFF) - auto parse_ret = Game_Message::ParseSpeed(*iter, end, escape_char, true); - *iter = parse_ret.next; - int value = parse_ret.value; - - bool sw = Main_Data::game_switches->Get(value); - if (sw) { - return "ON"; - } - else { - return "OFF"; - } - } - - return DefaultCommandInserter(ch, iter, end, escape_char); -}; - -std::optional PendingMessage::ManiacsCommandInserterHex(char ch, const char** iter, const char* end, uint32_t escape_char) { - if (ch == 'V' || ch == 'v') { - auto parse_ret = Game_Message::ParseVariable(*iter, end, escape_char, true); - *iter = parse_ret.next; - int value = parse_ret.value; - - int variable_value = Main_Data::game_variables->Get(value); - std::ostringstream ss; - ss << std::hex << variable_value; - return ss.str(); - } - - return ManiacsCommandInserter(ch, iter, end, escape_char); -}; - -PendingMessage::CommandInserter PendingMessage::BuildManiacsCommandInserter(bool hex_nums) { - if (hex_nums) { - return PendingMessage::ManiacsCommandInserterHex; - } - return PendingMessage::ManiacsCommandInserter; -}; diff --git a/src/pending_message.h b/src/pending_message.h index 78e1ce4181..1bbf51e94f 100644 --- a/src/pending_message.h +++ b/src/pending_message.h @@ -30,9 +30,6 @@ class PendingMessage { using ChoiceContinuation = std::function; using CommandInserter = std::function(char,const char**,const char*,uint32_t)>; static std::optional DefaultCommandInserter(char ch, const char** iter, const char* end, uint32_t escape_char); - 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); - static CommandInserter BuildManiacsCommandInserter(bool hex_nums); int PushLine(std::string msg, CommandInserter cmd_fn = DefaultCommandInserter); int PushChoice(std::string msg, bool enabled = true, CommandInserter cmd_fn = DefaultCommandInserter); From 6886f038ea419b4b0cb6c6e09a2a817ac2f05784 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Sun, 22 Oct 2023 14:50:12 +0200 Subject: [PATCH 43/47] String Var: Use lambdas in GetLcfName and GetLcfDesc to make the code more local --- src/maniac_patch.cpp | 101 +++++++++++++++++++------------------------ src/maniac_patch.h | 4 -- 2 files changed, 45 insertions(+), 60 deletions(-) diff --git a/src/maniac_patch.cpp b/src/maniac_patch.cpp index 5078253688..b4ed45d436 100644 --- a/src/maniac_patch.cpp +++ b/src/maniac_patch.cpp @@ -618,11 +618,11 @@ bool ManiacPatch::CheckString(std::string str_l, std::string str_r, int op, bool switch (op) { case 0: // eq - return strcmp(str_l.c_str(), str_r.c_str()) == 0; + return str_l == str_r; case 2: // contains (l contains r) return str_l.find(str_r) != std::string::npos; case 1: // neq - return strcmp(str_l.c_str(), str_r.c_str()) != 0; + return str_l != str_r; case 3: // notContains (l does not contain r) return str_l.find(str_r) == std::string::npos; default: @@ -631,6 +631,15 @@ bool ManiacPatch::CheckString(std::string str_l, std::string str_r, int op, bool } 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 { + auto* data = lcf::ReaderUtil::GetElement(vec, id); + if (!data) { + Output::Warning("Unable to read {} name: {}", type, id); + return {}; + } + return ToString(data->name); + }; + switch (data_type) { case 0: //.actor[a].name @@ -641,21 +650,21 @@ Game_Strings::Str_t ManiacPatch::GetLcfName(int data_type, int id, bool is_dynam } } else { - return GetNameSafely(lcf::Data::actors, id); + return get_name("Actor", lcf::Data::actors); } break; - case 1: return GetNameSafely(lcf::Data::skills, id); //.skill[a].name - case 2: return GetNameSafely(lcf::Data::items, id); //.item[a].name - case 3: return GetNameSafely(lcf::Data::enemies, id); //.enemy[a].name - case 4: return GetNameSafely(lcf::Data::troops, id); //.troop[a].name - case 5: return GetNameSafely(lcf::Data::terrains, id); //.terrain[a].name - case 6: return GetNameSafely(lcf::Data::attributes, id); //.element[a].name - case 7: return GetNameSafely(lcf::Data::states, id); //.state[a].name - case 8: return GetNameSafely(lcf::Data::animations, id); //.anim[a].name - case 9: return GetNameSafely(lcf::Data::chipsets, id); //.tileset[a].name + case 1: return get_name("Skill", lcf::Data::skills); //.skill[a].name + case 2: return get_name("Item", lcf::Data::items); //.item[a].name + case 3: return get_name("Enemy", lcf::Data::enemies); //.enemy[a].name + case 4: return get_name("Troop", lcf::Data::troops); //.troop[a].name + case 5: return get_name("Terrain", lcf::Data::terrains); //.terrain[a].name + case 6: return get_name("Attribute", lcf::Data::attributes); //.element[a].name + 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 12: break; //.t[a].name -- not sure how to get this for now + 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 @@ -664,8 +673,8 @@ Game_Strings::Str_t ManiacPatch::GetLcfName(int data_type, int id, bool is_dynam } break; } - case 14: return GetNameSafely(lcf::Data::classes, id); //.class[a].name - case 15: return GetNameSafely(lcf::Data::battleranimations, id); //.anim2[a].name + 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 17: //.mev[a].name { @@ -684,18 +693,31 @@ Game_Strings::Str_t ManiacPatch::GetLcfName(int data_type, int id, bool is_dynam } else { id = actor->GetId(); - return GetNameSafely(lcf::Data::actors, id); + return get_name("Actor", lcf::Data::actors); } } break; } } - Output::Warning("Unable to read name: {} {}", data_type, id); - return ""; + Output::Warning("GetLcfName: Unsupported data_type {} {}", data_type, id); + 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 { + 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); + } else { + return ToString(data->description); + } + }; + switch (data_type) { case 0: //.actor[a].desc @@ -706,11 +728,11 @@ Game_Strings::Str_t ManiacPatch::GetLcfDescription(int data_type, int id, bool i } } else { - return GetDescriptionSafely(lcf::Data::actors, id); + return get_desc("Actor", lcf::Data::actors); } break; - case 1: return GetDescriptionSafely(lcf::Data::skills, id); //.skill[a].desc - case 2: return GetDescriptionSafely(lcf::Data::items, id); //.item[a].desc + case 1: return get_desc("Skill", lcf::Data::skills); //.skill[a].desc + case 2: return get_desc("Item", lcf::Data::items); //.item[a].desc case 18: //.member[a].desc { auto actor = Main_Data::game_party->GetActor(id); @@ -720,46 +742,13 @@ Game_Strings::Str_t ManiacPatch::GetLcfDescription(int data_type, int id, bool i } else { id = actor->GetId(); - return GetDescriptionSafely(lcf::Data::actors, id); + return get_desc("Actor", lcf::Data::actors); } } break; } } - Output::Warning("Unable to read description: {} {}", data_type, id); - return ""; -} - -template -Game_Strings::Str_t ManiacPatch::GetNameSafely(std::vector vec, int id) { - T* data = lcf::ReaderUtil::GetElement(vec, id); - if (data != nullptr) { - return static_cast(data->name); - } - - Output::Warning("Unable to read name: {}", id); - return ""; -} - -template -Game_Strings::Str_t ManiacPatch::GetDescriptionSafely(std::vector vec, int id) { - T* data = lcf::ReaderUtil::GetElement(vec, id); - if (data != nullptr) { - return static_cast(data->description); - } - - Output::Warning("Unable to read description: {}", id); - return ""; -} - -template <> -Game_Strings::Str_t ManiacPatch::GetDescriptionSafely(std::vector vec, int id) { - lcf::rpg::Actor* data = lcf::ReaderUtil::GetElement(vec, id); - if (data != nullptr) { - return static_cast(data->title); - } - - Output::Warning("Unable to read description: {}", id); + Output::Warning("GetLcfDescription: Unsupported data_type {} {}", data_type, id); return ""; } diff --git a/src/maniac_patch.h b/src/maniac_patch.h index 8b2fe23d01..12f78df169 100644 --- a/src/maniac_patch.h +++ b/src/maniac_patch.h @@ -38,10 +38,6 @@ namespace ManiacPatch { 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); - - template Game_Strings::Str_t GetNameSafely(std::vector vec, int id); - template Game_Strings::Str_t GetDescriptionSafely(std::vector vec, int id); - template <> Game_Strings::Str_t GetDescriptionSafely(std::vector vec, int id); } #endif From e20c316e14be56ee5653a6fe7162c12b7e86273e Mon Sep 17 00:00:00 2001 From: Ghabry Date: Sun, 22 Oct 2023 15:57:05 +0200 Subject: [PATCH 44/47] String Var: Use correct extraction logic for \t[] In Messages commands in \t[] are evaluated once. In StringPics and Extract they are not. --- src/dynrpg_textplugin.cpp | 12 ++++++------ src/game_interpreter.cpp | 12 ++++++------ src/game_interpreter_map.cpp | 2 +- src/game_message.cpp | 31 +++++++++++++++++++++++++++++++ src/game_message.h | 3 +++ src/game_strings.cpp | 11 ++++++++++- src/game_windows.cpp | 15 ++++++++++++++- src/pending_message.cpp | 30 ++++++++++++++---------------- src/pending_message.h | 10 ++++++---- src/scene_battle_rpg2k.cpp | 4 ++-- src/scene_battle_rpg2k3.cpp | 4 ++-- src/window_message.cpp | 5 +++-- 12 files changed, 98 insertions(+), 41 deletions(-) diff --git a/src/dynrpg_textplugin.cpp b/src/dynrpg_textplugin.cpp index 8654cd164f..3d98ddb7f7 100644 --- a/src/dynrpg_textplugin.cpp +++ b/src/dynrpg_textplugin.cpp @@ -164,8 +164,8 @@ class DynRpgText : public Drawable { } static DynRpgText* GetTextHandle(const std::string& id, bool silent = false) { - PendingMessage pm; - pm.PushLine(id, DynRpgText::CommandCodeInserter); + PendingMessage pm(CommandCodeInserter); + pm.PushLine(id); std::string new_id = pm.GetLines().front(); auto it = graphics.find(new_id); @@ -230,8 +230,8 @@ class DynRpgText : public Drawable { const FontRef& font = Font::Default(); for (auto& t : texts) { - PendingMessage pm; - pm.PushLine(t, CommandCodeInserter); + PendingMessage pm(CommandCodeInserter); + pm.PushLine(t); t = pm.GetLines().front(); Rect r = Text::GetSize(*font, t); @@ -269,8 +269,8 @@ static bool WriteText(dyn_arg_list args) { if (!okay) return true; - PendingMessage pm; - pm.PushLine(id, DynRpgText::CommandCodeInserter); + PendingMessage pm(DynRpgText::CommandCodeInserter); + pm.PushLine(id); std::string new_id = pm.GetLines().front(); graphics[new_id] = std::make_unique(1, x, y, text); diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 4f952494f0..296cce1189 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -909,7 +909,7 @@ bool Game_Interpreter::CommandShowMessage(lcf::rpg::EventCommand const& com) { / return false; } - auto pm = PendingMessage(); + PendingMessage pm(Game_Message::CommandCodeInserter); pm.SetIsEventMessage(true); // Set first line @@ -1000,7 +1000,7 @@ bool Game_Interpreter::CommandShowChoices(lcf::rpg::EventCommand const& com) { / return false; } - auto pm = PendingMessage(); + PendingMessage pm(Game_Message::CommandCodeInserter); pm.SetIsEventMessage(true); // Choices setup @@ -1031,7 +1031,7 @@ bool Game_Interpreter::CommandInputNumber(lcf::rpg::EventCommand const& com) { / return false; } - auto pm = PendingMessage(); + PendingMessage pm(Game_Message::CommandCodeInserter); pm.SetIsEventMessage(true); int variable_id = com.parameters[1]; @@ -1696,7 +1696,7 @@ bool Game_Interpreter::CommandChangeExp(lcf::rpg::EventCommand const& com) { // com.parameters[4] ); - PendingMessage pm; + PendingMessage pm(Game_Message::CommandCodeInserter); pm.SetEnableFace(false); for (const auto& actor : GetActors(com.parameters[0], com.parameters[1])) { @@ -1726,7 +1726,7 @@ bool Game_Interpreter::CommandChangeLevel(lcf::rpg::EventCommand const& com) { / com.parameters[4] ); - PendingMessage pm; + PendingMessage pm(Game_Message::CommandCodeInserter); pm.SetEnableFace(false); for (const auto& actor : GetActors(com.parameters[0], com.parameters[1])) { @@ -3937,7 +3937,7 @@ bool Game_Interpreter::CommandChangeClass(lcf::rpg::EventCommand const& com) { / return false; } - PendingMessage pm; + PendingMessage pm(Game_Message::CommandCodeInserter); pm.SetEnableFace(false); const lcf::rpg::Class* cls = lcf::ReaderUtil::GetElement(lcf::Data::classes, class_id); diff --git a/src/game_interpreter_map.cpp b/src/game_interpreter_map.cpp index c8733d2dab..8d6a26537a 100644 --- a/src/game_interpreter_map.cpp +++ b/src/game_interpreter_map.cpp @@ -362,7 +362,7 @@ bool Game_Interpreter_Map::CommandShowInn(lcf::rpg::EventCommand const& com) { / return false; } - auto pm = PendingMessage(); + PendingMessage pm(Game_Message::CommandCodeInserter); StringView greeting_1, greeting_2, greeting_3, accept, cancel; diff --git a/src/game_message.cpp b/src/game_message.cpp index c64b0b52bd..95b697f7a9 100644 --- a/src/game_message.cpp +++ b/src/game_message.cpp @@ -22,6 +22,7 @@ #include "game_player.h" #include "game_battle.h" #include "game_system.h" +#include "game_strings.h" #include "main_data.h" #include "window_message.h" #include "font.h" @@ -154,6 +155,36 @@ bool Game_Message::CanShowMessage(bool foreground) { return window ? window->GetAllowNextMessage(foreground) : false; } +static std::optional CommandCodeInserterNoRecurse(char ch, const char** iter, const char* end, uint32_t escape_char) { + if ((ch == 'T' || ch == 't') && Player::IsPatchManiac()) { + auto parse_ret = Game_Message::ParseString(*iter, end, escape_char, true); + *iter = parse_ret.next; + int value = parse_ret.value; + + std::string str = 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); + } + + return PendingMessage::DefaultCommandInserter(ch, iter, end, escape_char); +} + +std::optional Game_Message::CommandCodeInserter(char ch, const char** iter, const char* end, uint32_t escape_char) { + if ((ch == 'T' || ch == 't') && Player::IsPatchManiac()) { + auto parse_ret = Game_Message::ParseString(*iter, end, escape_char, true); + *iter = parse_ret.next; + int value = parse_ret.value; + + std::string str = Main_Data::game_strings->Get(value); + + // Command codes in \t[] are evaluated once. + return PendingMessage::ApplyTextInsertingCommands(str, escape_char, CommandCodeInserterNoRecurse); + } + + return PendingMessage::DefaultCommandInserter(ch, iter, end, escape_char); +} + Game_Message::ParseParamResult Game_Message::ParseParam( char upper, char lower, diff --git a/src/game_message.h b/src/game_message.h index 83b90041eb..555c2bdd21 100644 --- a/src/game_message.h +++ b/src/game_message.h @@ -96,6 +96,9 @@ namespace Game_Message { /** @return true if message window is running */ bool IsMessageActive(); + /** The command code parser for the message box */ + std::optional CommandCodeInserter(char ch, const char** iter, const char* end, uint32_t escape_char); + // EasyRPG extension allowing more recursive variables \v[\v[...]] static constexpr int easyrpg_default_max_recursion = 8; // RPG_RT only allows 1 level of recursion. diff --git a/src/game_strings.cpp b/src/game_strings.cpp index d16c78fd0c..ea7cbbfb03 100644 --- a/src/game_strings.cpp +++ b/src/game_strings.cpp @@ -58,6 +58,8 @@ int Game_Strings::ToNum(Str_Params params, int var_id) { } int Game_Strings::GetLen(Str_Params params, int var_id) { + // Note: The length differs between Maniac and EasyRPG due to different internal encoding (utf-8 vs. ansi) + if (!ResizeWithId(params.string_id)) return -1; int len = Get(params.string_id).length(); @@ -272,9 +274,16 @@ std::optional Game_Strings::ManiacsCommandInserter(char ch, const c int value = parse_ret.value; return Main_Data::game_switches->Get(value) ? "ON" : "OFF"; + } else if (ch == 'T' || ch == 't') { + auto parse_ret = Game_Message::ParseString(*iter, end, escape_char, true); + *iter = parse_ret.next; + int value = parse_ret.value; + + // Contrary to Messages, the content of \t[]-strings is not evaluated + return Main_Data::game_strings->Get(value); } - return PendingMessage::DefaultCommandInserter(ch, iter, end, escape_char); + return Game_Message::CommandCodeInserter(ch, iter, end, escape_char); }; std::optional Game_Strings::ManiacsCommandInserterHex(char ch, const char** iter, const char* end, uint32_t escape_char) { diff --git a/src/game_windows.cpp b/src/game_windows.cpp index 19d899502d..4f80a20b06 100644 --- a/src/game_windows.cpp +++ b/src/game_windows.cpp @@ -27,6 +27,19 @@ #include "output.h" #include "player.h" +static std::optional CommandCodeInserter(char ch, const char **iter, const char *end, uint32_t escape_char) { + if ((ch == 'T' || ch == 't') && Player::IsPatchManiac()) { + auto parse_ret = Game_Message::ParseString(*iter, end, escape_char, true); + *iter = parse_ret.next; + int value = parse_ret.value; + + // Contrary to Messages, the content of \t[]-strings is not evaluated + return Main_Data::game_strings->Get(value); + } + + return PendingMessage::DefaultCommandInserter(ch, iter, end, escape_char); +} + Game_Windows::Window_User::Window_User(lcf::rpg::SaveEasyRpgWindow save) : data(std::move(save)) { @@ -206,7 +219,7 @@ void Game_Windows::Window_User::Refresh(bool& async_wait) { std::stringstream ss(ToString(text.text)); std::string out; - PendingMessage pm; + PendingMessage pm(CommandCodeInserter); while (Utils::ReadLine(ss, out)) { pm.PushLine(out); } diff --git a/src/pending_message.cpp b/src/pending_message.cpp index 427b5711ab..aea1fba180 100644 --- a/src/pending_message.cpp +++ b/src/pending_message.cpp @@ -29,6 +29,7 @@ #include #include #include +#include static void RemoveControlChars(std::string& s) { // RPG_RT ignores any control characters within messages. @@ -36,26 +37,31 @@ static void RemoveControlChars(std::string& s) { s.erase(iter, s.end()); } -int PendingMessage::PushLineImpl(std::string msg, CommandInserter cmd_fn) { +PendingMessage::PendingMessage(PendingMessage::CommandInserter cmd_fn) : + command_inserter(std::move(cmd_fn)) { + // no-op +}; + +int PendingMessage::PushLineImpl(std::string msg) { RemoveControlChars(msg); - msg = ApplyTextInsertingCommands(std::move(msg), Player::escape_char, cmd_fn); + msg = ApplyTextInsertingCommands(std::move(msg), Player::escape_char, command_inserter); texts.push_back(std::move(msg)); return texts.size(); } -int PendingMessage::PushLine(std::string msg, CommandInserter cmd_fn) { +int PendingMessage::PushLine(std::string msg) { assert(!HasChoices()); assert(!HasNumberInput()); - return PushLineImpl(std::move(msg), cmd_fn); + return PushLineImpl(std::move(msg)); } -int PendingMessage::PushChoice(std::string msg, bool enabled, CommandInserter cmd_fn) { +int PendingMessage::PushChoice(std::string msg, bool enabled) { assert(!HasNumberInput()); if (!HasChoices()) { choice_start = NumLines(); } choice_enabled[GetNumChoices()] = enabled; - return PushLineImpl(std::move(msg), cmd_fn); + return PushLineImpl(std::move(msg)); } int PendingMessage::PushNumInput(int variable_id, int num_digits) { @@ -88,8 +94,7 @@ void PendingMessage::SetChoiceResetColors(bool value) { choice_reset_color = value; } -std::string PendingMessage::ApplyTextInsertingCommands(std::string input, uint32_t escape_char, CommandInserter cmd_fn) { - +std::string PendingMessage::ApplyTextInsertingCommands(std::string input, uint32_t escape_char, const CommandInserter& cmd_fn) { if (input.empty()) { return input; } @@ -160,14 +165,7 @@ std::optional PendingMessage::DefaultCommandInserter(char ch, const int variable_value = Main_Data::game_variables->Get(value); return std::to_string(variable_value); - } else if (ch == 'T' || ch == 't') { - auto parse_ret = Game_Message::ParseString(*iter, end, escape_char, true); - *iter = parse_ret.next; - int value = parse_ret.value; - - Game_Strings::Str_t string = Main_Data::game_strings->Get(value); - return (std::string)string; } return std::nullopt; -}; +} diff --git a/src/pending_message.h b/src/pending_message.h index 1bbf51e94f..793d47e498 100644 --- a/src/pending_message.h +++ b/src/pending_message.h @@ -31,8 +31,9 @@ class PendingMessage { using CommandInserter = std::function(char,const char**,const char*,uint32_t)>; static std::optional DefaultCommandInserter(char ch, const char** iter, const char* end, uint32_t escape_char); - int PushLine(std::string msg, CommandInserter cmd_fn = DefaultCommandInserter); - int PushChoice(std::string msg, bool enabled = true, CommandInserter cmd_fn = DefaultCommandInserter); + PendingMessage(CommandInserter cmd_fn); + int PushLine(std::string msg); + int PushChoice(std::string msg, bool enabled = true); int PushNumInput(int variable_id, int num_digits); void PushPageEnd(); @@ -67,10 +68,11 @@ class PendingMessage { void SetIsEventMessage(bool value) { is_event_message = value; } bool IsEventMessage() const { return is_event_message; } - static std::string ApplyTextInsertingCommands(std::string input, uint32_t escape_char, CommandInserter cmd_fn); + static std::string ApplyTextInsertingCommands(std::string input, uint32_t escape_char, const CommandInserter& cmd_fn); private: - int PushLineImpl(std::string msg, CommandInserter cmd_fn); + int PushLineImpl(std::string msg); + CommandInserter command_inserter; ChoiceContinuation choice_continuation; std::vector texts; int choice_start = -1; diff --git a/src/scene_battle_rpg2k.cpp b/src/scene_battle_rpg2k.cpp index 6df695cea9..430ad18b31 100644 --- a/src/scene_battle_rpg2k.cpp +++ b/src/scene_battle_rpg2k.cpp @@ -844,7 +844,7 @@ Scene_Battle_Rpg2k::SceneActionReturn Scene_Battle_Rpg2k::ProcessSceneActionVict std::vector drops; Main_Data::game_enemyparty->GenerateDrops(drops); - auto pm = PendingMessage(); + PendingMessage pm(Game_Message::CommandCodeInserter); pm.SetEnableFace(false); pm.SetWordWrapped(Feature::HasPlaceholders()); @@ -901,7 +901,7 @@ Scene_Battle_Rpg2k::SceneActionReturn Scene_Battle_Rpg2k::ProcessSceneActionDefe Main_Data::game_system->SetMessagePosition(2); Main_Data::game_system->SetMessageTransparent(false); - auto pm = PendingMessage(); + PendingMessage pm(Game_Message::CommandCodeInserter); pm.SetEnableFace(false); pm.SetWordWrapped(Feature::HasPlaceholders()); diff --git a/src/scene_battle_rpg2k3.cpp b/src/scene_battle_rpg2k3.cpp index 9b4f00cb90..e2c2a54066 100644 --- a/src/scene_battle_rpg2k3.cpp +++ b/src/scene_battle_rpg2k3.cpp @@ -1811,7 +1811,7 @@ Scene_Battle_Rpg2k3::SceneActionReturn Scene_Battle_Rpg2k3::ProcessSceneActionVi std::vector drops; Main_Data::game_enemyparty->GenerateDrops(drops); - auto pm = PendingMessage(); + PendingMessage pm(Game_Message::CommandCodeInserter); pm.SetEnableFace(false); pm.PushLine(ToString(lcf::Data::terms.victory) + Player::escape_symbol + "|"); @@ -1911,7 +1911,7 @@ Scene_Battle_Rpg2k3::SceneActionReturn Scene_Battle_Rpg2k3::ProcessSceneActionDe Main_Data::game_system->SetMessagePosition(0); Main_Data::game_system->SetMessageTransparent(false); - auto pm = PendingMessage(); + PendingMessage pm(Game_Message::CommandCodeInserter); pm.SetEnableFace(false); pm.PushLine(ToString(lcf::Data::terms.defeat)); diff --git a/src/window_message.cpp b/src/window_message.cpp index 20a5936f07..4c00c6e918 100644 --- a/src/window_message.cpp +++ b/src/window_message.cpp @@ -91,7 +91,8 @@ void DebugLogText(const char*, Args&&...) { } Window_Message::Window_Message(int ix, int iy, int iwidth, int iheight) : Window_Selectable(ix, iy, iwidth, iheight), number_input_window(new Window_NumberInput(0, 0)), - gold_window(new Window_Gold(Player::screen_width - Player::menu_offset_x - gold_window_width, Player::menu_offset_y, gold_window_width, gold_window_height)) + gold_window(new Window_Gold(Player::screen_width - Player::menu_offset_x - gold_window_width, Player::menu_offset_y, gold_window_width, gold_window_height)), + pending_message(Game_Message::CommandCodeInserter) { SetContents(Bitmap::Create(width - 16, height - 16)); @@ -359,7 +360,7 @@ void Window_Message::FinishMessageProcessing() { line_char_counter = 0; SetIndex(-1); - pending_message = {}; + pending_message = PendingMessage(Game_Message::CommandCodeInserter); auto close_frames = Game_Battle::IsBattleRunning() ? 0 : message_animation_frames; From 26ff6b8f853af7e13802873d5cbc597c5a975638 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Sun, 22 Oct 2023 15:59:29 +0200 Subject: [PATCH 45/47] Add eneway aka narcodis as Author --- docs/AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/AUTHORS.md b/docs/AUTHORS.md index 697930bb41..22ad007758 100644 --- a/docs/AUTHORS.md +++ b/docs/AUTHORS.md @@ -7,6 +7,7 @@ EasyRPG Player authors * Carsten Teibes (carstene1ns) * Christian Breitwieser (ChrisBreiti) * Diego Pedraza (zegeri) +* Erich Newey (enewey) * Dmytro Kushnariov (rohkea) * Francisco de la Peña (fdelapena) * Gabriel Kind (Ghabry) From 64802138a08bf6c01dcba1586455cfaad7956cf9 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Sun, 22 Oct 2023 16:07:50 +0200 Subject: [PATCH 46/47] String Var: Fetch the text file for emscripten --- src/async_handler.cpp | 10 ++++++++++ src/async_handler.h | 6 ++++++ src/game_interpreter.cpp | 10 +++++++++- src/game_strings.cpp | 13 ++++++++++++- src/game_strings.h | 2 +- src/scene_save.cpp | 9 +-------- src/scene_settings.cpp | 8 +------- 7 files changed, 40 insertions(+), 18 deletions(-) diff --git a/src/async_handler.cpp b/src/async_handler.cpp index ab4f9189e2..ffd5e1a143 100644 --- a/src/async_handler.cpp +++ b/src/async_handler.cpp @@ -257,6 +257,16 @@ bool AsyncHandler::IsFilePending(bool important, bool graphic) { return false; } +void AsyncHandler::SaveFilesystem() { +#ifdef EMSCRIPTEN + // Save changed file system + EM_ASM({ + FS.syncfs(function(err) { + }); + }); +#endif +} + bool AsyncHandler::IsImportantFilePending() { return IsFilePending(true, false); } diff --git a/src/async_handler.h b/src/async_handler.h index 06be2cdc2c..3282cd5822 100644 --- a/src/async_handler.h +++ b/src/async_handler.h @@ -92,6 +92,12 @@ namespace AsyncHandler { * @return If any file with params is pending. */ bool IsFilePending(bool important, bool graphic); + + /** + * Saves the state of the Save filesystem. + * Only works on emscripten, noop on other platforms. + */ + void SaveFilesystem(); } using FileRequestBinding = std::shared_ptr; diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index 296cce1189..f31fe8aeb8 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -4866,7 +4866,15 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& // maniacs does not like a file extension Game_Strings::Str_t filename = Main_Data::game_strings->GetWithMode(str_param, args[0], modes[0]); // args[1] is the encoding... 0 for ansi, 1 for utf8 - result = Game_Strings::FromFile(filename, args[1]); + bool do_yield; + result = Game_Strings::FromFile(filename, args[1], do_yield); + + if (do_yield) { + // Wait for text file download and repeat + _async_op = AsyncOp::MakeYieldRepeat(); + return true; + } + break; } case 13: //Remove (rem) diff --git a/src/game_strings.cpp b/src/game_strings.cpp index ea7cbbfb03..96a82ef766 100644 --- a/src/game_strings.cpp +++ b/src/game_strings.cpp @@ -18,6 +18,7 @@ // Headers #include #include +#include "async_handler.h" #include "game_message.h" #include "game_strings.h" #include "game_switches.h" @@ -109,9 +110,17 @@ int Game_Strings::Split(Str_Params params, std::string delimiter, int string_out return splits; } -Game_Strings::Str_t Game_Strings::FromFile(StringView filename, int encoding) { +Game_Strings::Str_t Game_Strings::FromFile(StringView filename, int encoding, bool& do_yield) { + do_yield = false; + Filesystem_Stream::InputStream is = FileFinder::OpenText(filename); if (!is) { + // Emscripten: Try to async fetch the file + auto* request = AsyncHandler::RequestFile("Text", filename); + request->SetImportantFile(true); + request->Start(); + do_yield = !request->IsReady(); + return {}; } @@ -159,6 +168,8 @@ Game_Strings::Str_t Game_Strings::ToFile(Str_Params params, std::string filename txt_out << str; txt_out.Close(); + AsyncHandler::SaveFilesystem(); + return str; } diff --git a/src/game_strings.h b/src/game_strings.h index 514b8e4b17..06af837e98 100644 --- a/src/game_strings.h +++ b/src/game_strings.h @@ -60,7 +60,7 @@ class Game_Strings { 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); + 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); diff --git a/src/scene_save.cpp b/src/scene_save.cpp index c9ee59c4b4..d2aa7dfcf4 100644 --- a/src/scene_save.cpp +++ b/src/scene_save.cpp @@ -157,14 +157,7 @@ bool Scene_Save::Save(std::ostream& os, int slot_id, bool prepare_save) { bool res = lcf::LSD_Reader::Save(os, save, lcf_engine, Player::encoding); DynRpg::Save(slot_id); - -#ifdef EMSCRIPTEN - // Save changed file system - EM_ASM({ - FS.syncfs(function(err) { - }); - }); -#endif + AsyncHandler::SaveFilesystem(); return res; } diff --git a/src/scene_settings.cpp b/src/scene_settings.cpp index 0ce917c512..c50ca8123f 100644 --- a/src/scene_settings.cpp +++ b/src/scene_settings.cpp @@ -566,13 +566,7 @@ bool Scene_Settings::SaveConfig(bool silent) { cfg.WriteToStream(cfg_out); -#ifdef EMSCRIPTEN - // Save changed file system - EM_ASM({ - FS.syncfs(function(err) { - }); - }); -#endif + AsyncHandler::SaveFilesystem(); if (silent) { Output::Debug("Configuration saved to {}", cfg_out.GetName()); From 7221769f8dbb5a696b559aa89b1feacda462c100 Mon Sep 17 00:00:00 2001 From: Erich Newey Date: Sun, 22 Oct 2023 12:15:51 -0600 Subject: [PATCH 47/47] StringVars: remove debug statements --- src/game_interpreter.cpp | 3 +-- src/game_strings.cpp | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index f31fe8aeb8..0b61a59070 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -4841,8 +4841,7 @@ bool Game_Interpreter::CommandManiacControlStrings(lcf::rpg::EventCommand const& { std::string op_string = ""; std::string delimiter = (std::string)Main_Data::game_strings->GetWithMode(str_param, args[0], modes[0]); - Output::Debug("args {} {} {}", args[0], args[1], args[2]); - Output::Debug("modes {} {} {}", modes[0], modes[1], modes[2]); + // 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 args[1] = ValueOrVariable(modes[1] % 2, args[1]); diff --git a/src/game_strings.cpp b/src/game_strings.cpp index 96a82ef766..913a596a65 100644 --- a/src/game_strings.cpp +++ b/src/game_strings.cpp @@ -77,8 +77,6 @@ int Game_Strings::InStr(Str_Params params, std::string search, int var_id, int b std::string str = Get(params.string_id); - Output::Debug("Searching for {} in {}", search, str); - int index = Get(params.string_id).find(search, begin); Main_Data::game_variables->Set(var_id, index); return index;