diff --git a/CMakeLists.txt b/CMakeLists.txt
index 87859f18f0..69c6e6f115 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -109,6 +109,11 @@ add_library(${PROJECT_NAME} OBJECT
src/dynrpg_textplugin.h
src/enemyai.cpp
src/enemyai.h
+ src/exe_buildinfo.h
+ src/exe_constants.h
+ src/exe_patches.h
+ src/exe_shared.h
+ src/exe_strings.h
src/exe_reader.cpp
src/exe_reader.h
src/exfont.h
diff --git a/Makefile.am b/Makefile.am
index 02992d0f91..e95b677527 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -89,6 +89,11 @@ libeasyrpg_player_a_SOURCES = \
src/dynrpg_textplugin.cpp \
src/enemyai.cpp \
src/enemyai.h \
+ src/exe_buildinfo.h \
+ src/exe_constants.h \
+ src/exe_patches.h \
+ src/exe_shared.h \
+ src/exe_strings.h \
src/exe_reader.cpp \
src/exe_reader.h \
src/exfont.h \
diff --git a/src/battle_message.cpp b/src/battle_message.cpp
index 1b89b53f85..0994cbcbac 100644
--- a/src/battle_message.cpp
+++ b/src/battle_message.cpp
@@ -150,6 +150,15 @@ static std::string GetHpSpRecoveredMessage(const Game_Battler& target, int value
Utils::MakeSvArray(target.GetName(), std::to_string(value), points)
);
}
+
+ StringView template_text;
+ if (Player::Constants::HasEmbeddedTemplateString(EXE::Shared::EmbeddedStringTypes::Battle_HpSpRecovery, template_text)) {
+ return Utils::ReplacePlaceholders(
+ template_text,
+ Utils::MakeArray('S', 'U', 'V', 'T'),
+ Utils::MakeSvArray(target.GetName(), points, std::to_string(value), lcf::Data::terms.hp_recovery)
+ );
+ }
std::stringstream ss;
std::string particle, particle2, space = "";
@@ -188,6 +197,20 @@ std::string GetParameterAbsorbedMessage(const Game_Battler& source, const Game_B
Utils::MakeSvArray(source.GetName(), target.GetName(), std::to_string(value), points)
);
}
+
+ auto embedded_str_type = target_is_ally
+ ? EXE::Shared::EmbeddedStringTypes::Battle_AbsorbAlly
+ : EXE::Shared::EmbeddedStringTypes::Battle_AbsorbEnemy;
+
+ StringView template_text;
+ if (Player::Constants::HasEmbeddedTemplateString(embedded_str_type, template_text)) {
+ return Utils::ReplacePlaceholders(
+ template_text,
+ Utils::MakeArray('O', 'U', 'V', 'T'),
+ Utils::MakeSvArray(target.GetName(), points, std::to_string(value), message)
+ );
+ }
+
std::stringstream ss;
std::string particle, particle2, space = "";
@@ -243,6 +266,20 @@ std::string GetDamagedMessage(const Game_Battler& target, int value) {
Utils::MakeSvArray(target.GetName(), std::to_string(value), lcf::Data::terms.health_points)
);
}
+
+ auto embedded_str_type = target_is_ally
+ ? EXE::Shared::EmbeddedStringTypes::Battle_DamageToAlly
+ : EXE::Shared::EmbeddedStringTypes::Battle_DamageToEnemy;
+
+ StringView template_text;
+ if (Player::Constants::HasEmbeddedTemplateString(embedded_str_type, template_text)) {
+ return Utils::ReplacePlaceholders(
+ template_text,
+ Utils::MakeArray('S', 'V', 'T'),
+ Utils::MakeSvArray(target.GetName(), std::to_string(value), message)
+ );
+ }
+
std::stringstream ss;
std::string particle, space = "";
ss << target.GetName();
@@ -276,6 +313,20 @@ std::string GetParameterChangeMessage(const Game_Battler& target, int value, Str
Utils::MakeSvArray(target.GetName(), std::to_string(value), points)
);
}
+
+ auto embedded_str_type = is_positive
+ ? EXE::Shared::EmbeddedStringTypes::Battle_StatIncrease
+ : EXE::Shared::EmbeddedStringTypes::Battle_StatDecrease;
+
+ StringView template_text;
+ if (Player::Constants::HasEmbeddedTemplateString(embedded_str_type, template_text)) {
+ return Utils::ReplacePlaceholders(
+ template_text,
+ Utils::MakeArray('S', 'U', 'V', 'T'),
+ Utils::MakeSvArray(target.GetName(), points, std::to_string(value), message)
+ );
+ }
+
std::stringstream ss;
std::string particle, particle2, space = "";
ss << target.GetName();
@@ -437,6 +488,16 @@ std::string GetItemStartMessage2k(const Game_Battler& source, const lcf::rpg::It
Utils::MakeSvArray(source.GetName(), item.name)
);
}
+
+ //StringView template_text;
+ //if (Player::Constants::HasEmbeddedTemplateString(EXE::Shared::EmbeddedStringTypes::Battle_UseItem, template_text)) {
+ // return Utils::ReplacePlaceholders(
+ // template_text,
+ // Utils::MakeArray('S', 'O', 'T'),
+ // Utils::MakeSvArray(source.GetName(), item.name, ToString(lcf::Data::terms.use_item))
+ // );
+ //}
+
std::string particle;
if (Player::IsCP932())
particle = "は";
diff --git a/src/exe_buildinfo.h b/src/exe_buildinfo.h
new file mode 100644
index 0000000000..fc6aa932f3
--- /dev/null
+++ b/src/exe_buildinfo.h
@@ -0,0 +1,242 @@
+/*
+ * 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 .
+ */
+
+#ifndef EP_EXE_BUILDINFO_H
+#define EP_EXE_BUILDINFO_H
+
+#include
+#include
+#include "utils.h"
+
+namespace EXE::BuildInfo {
+
+ enum class EngineType {
+ UnknownEngine = 0,
+ RPG2000,
+ RPG2003,
+ RM2k3_MP,
+ EasyRPG
+ };
+
+ static constexpr auto kEngineTypes = lcf::makeEnumTags(
+ "Unknown",
+ "RPG2000",
+ "RPG2003",
+ "RM2k3_MP",
+ "EasyRPG"
+ );
+
+ enum KnownEngineBuildVersions {
+ UnknownBuild = 0,
+
+ RM2K_20000306,
+ RM2K_2000XXXX_UNK,
+ RM2K_20000507,
+ RM2K_20000619,
+ RM2K_20000711,
+ RM2K_20001113,
+ RM2K_20001115,
+ RM2K_20001227,
+ RM2K_20010505,
+ RM2K_20030327,
+ RM2K_20030625,
+ RM2KE_160,
+ RM2KE_161,
+ RM2KE_162,
+
+ RM2K3_100,
+ RM2K3_UNK_1,
+ RM2K3_UNK_2,
+ RM2K3_1021_1021,
+ RM2K3_1030_1030_1,
+ RM2K3_1030_1030_2,
+ RM2K3_1030_1040,
+ RM2K3_1050_1050_1,
+ RM2K3_1050_1050_2,
+ RM2K3_1060_1060,
+ RM2K3_1070_1070,
+ RM2K3_1080_1080,
+ RM2K3_1091_1091,
+
+ LAST_RM2K = RM2KE_162,
+ LAST_RM2K3 = RM2K3_1091_1091,
+ LAST
+ };
+
+ static constexpr auto kKnownEngineBuildDescriptions = lcf::makeEnumTags(
+ "UnknownBuild",
+
+ "1.00",
+ "2000-Unk",
+ "2000-05-07",
+ "2000-06-19",
+ "2000-07-11",
+ "2000-11-13",
+ "2000-11-15",
+ "1.07",
+ "1.10",
+ "1.50 (VALUE!)",
+ "1.51 (VALUE!)",
+ "1.60 (English)",
+ "1.61 (English)",
+ "1.62 (English)",
+
+ "1.0.0",
+ "1.0.1_UNK1",
+ "1.0.1_UNK2",
+ "1.0.2.1",
+ "1.0.3.1",
+ "1.0.3.2",
+ "1.0.4.0",
+ "1.0.5.1",
+ "1.0.5.2",
+ "1.0.6",
+ "1.0.7",
+ "1.0.8",
+ "1.0.9.1"
+ );
+
+ static_assert(kKnownEngineBuildDescriptions.size() == static_cast(KnownEngineBuildVersions::LAST));
+
+ constexpr size_t count_known_engine_builds = KnownEngineBuildVersions::LAST;
+ constexpr size_t count_known_rm2k_builds = 1 + KnownEngineBuildVersions::LAST_RM2K - KnownEngineBuildVersions::RM2K_20000306;
+ constexpr size_t count_known_rm2k3_builds = 1 + KnownEngineBuildVersions::LAST_RM2K3 - KnownEngineBuildVersions::RM2K3_100;
+
+ struct EngineBuildInfo {
+ EngineType engine_type;
+ int32_t code_size;
+ int32_t entrypoint;
+
+ constexpr EngineBuildInfo() :
+ engine_type(EngineType::UnknownEngine), code_size(0), entrypoint(0) {
+ }
+
+ constexpr EngineBuildInfo(EngineType engine_type, int32_t code_size, int32_t entrypoint) :
+ engine_type(engine_type), code_size(code_size), entrypoint(entrypoint) {
+ }
+ };
+
+ using engine_buildinfo_map = std::array, count_known_engine_builds>;
+
+ constexpr engine_buildinfo_map known_engine_builds = {{
+ { UnknownBuild, { EngineType::UnknownEngine, 0, 0 } },
+
+ { RM2K_20000306, { EngineType::RPG2000, 0x96400, 0x097220 } },
+ { RM2K_2000XXXX_UNK, { EngineType::RPG2000, 0x96600, 0x0972C4 } },
+ { RM2K_20000507, { EngineType::RPG2000, 0x96600, 0x0972D8 } },
+ { RM2K_20000619, { EngineType::RPG2000, 0x96C00, 0x097940 } },
+ { RM2K_20000711, { EngineType::RPG2000, 0x96E00, 0x097B50 } },
+ { RM2K_20001113, { EngineType::RPG2000, 0x96800, 0x0975E4 } },
+ { RM2K_20001115, { EngineType::RPG2000, 0x96800, 0x0975EC } },
+ { RM2K_20001227, { EngineType::RPG2000, 0x96A00, 0x097744 } },
+ { RM2K_20010505, { EngineType::RPG2000, 0x96A00, 0x097710 } },
+ { RM2K_20030327, { EngineType::RPG2000, 0x9BC00, 0x09CA80 } },
+ { RM2K_20030625, { EngineType::RPG2000, 0x9BE00, 0x09CC30 } },
+ { RM2KE_160, { EngineType::RPG2000, 0x9C000, 0x09CC78 } },
+ { RM2KE_161, { EngineType::RPG2000, 0x9CA00, 0x09D708 } },
+ { RM2KE_162, { EngineType::RPG2000, 0x9CC00, 0x09D930 } },
+
+ { RM2K3_100, { EngineType::RPG2003, 0xBD600, 0x0BE3F4 } },
+ { RM2K3_UNK_1, { EngineType::RPG2003, 0xBF200, 0x0BFF50 } },
+ { RM2K3_UNK_2, { EngineType::RPG2003, 0xBE800, 0x0BF6A8 } },
+ { RM2K3_1021_1021, { EngineType::RPG2003, 0xBF400, 0x0C00CC } },
+ { RM2K3_1030_1030_1, { EngineType::RPG2003, 0xBFE00, 0x0C0B90 } },
+ { RM2K3_1030_1030_2, { EngineType::RPG2003, 0xC0A00, 0x0C1878 } },
+ { RM2K3_1030_1040, { EngineType::RPG2003, 0xC0800, 0x0C14D8 } },
+ { RM2K3_1050_1050_1, { EngineType::RPG2003, 0xC7400, 0x0C81C4 } },
+ { RM2K3_1050_1050_2, { EngineType::RPG2003, 0xC7400, 0x0C81F0 } },
+ { RM2K3_1060_1060, { EngineType::RPG2003, 0xC8A00, 0x0C9804 } },
+ { RM2K3_1070_1070, { EngineType::RPG2003, 0xC8C00, 0x0C9984 } },
+ { RM2K3_1080_1080, { EngineType::RPG2003, 0xC8E00, 0x0C9C1C } },
+ { RM2K3_1091_1091, { EngineType::RPG2003, 0xC9000, 0x0C9D24 } }
+ }};
+
+ template
+ class fixed_size_byte_array {
+ public:
+ template
+ constexpr fixed_size_byte_array(Args... args)
+ : _size(sizeof...(args)), _data(init_fixedsize_array({ args... })) {
+ }
+ constexpr size_t size() const { return _size; }
+
+ constexpr const uint8_t operator[](const size_t off) const noexcept {
+ return _data[off];
+ }
+ private:
+ template
+ static constexpr std::array init_fixedsize_array(const std::array input) {
+ std::array result{};
+ for (size_t i = 0; i < N; ++i) {
+ result[i] = input[i];
+ }
+ return result;
+ }
+
+ size_t _size;
+ std::array _data;
+ };
+
+ constexpr size_t MAX_SIZE_CHK_PRE = 4;
+
+ using validate_const_data = fixed_size_byte_array;
+
+ class CodeAddressInfo {
+ public:
+ int32_t default_val;
+ uint8_t size_val;
+ size_t code_offset;
+ validate_const_data pre_data;
+
+ template
+ constexpr CodeAddressInfo(int32_t default_val, uint8_t size_val, size_t code_offset, Args... args) :
+ default_val(default_val), size_val(size_val), code_offset(code_offset), pre_data(validate_const_data{ static_cast(args)... }) {
+ }
+ };
+
+ class CodeAddressStringInfo {
+ public:
+ size_t code_offset;
+ uint32_t crc_jp, crc_en;
+ validate_const_data pre_data;
+
+ template
+ constexpr CodeAddressStringInfo(size_t code_offset, uint32_t crc_jp, uint32_t crc_en, Args... args) :
+ code_offset(code_offset), crc_jp(crc_jp), crc_en(crc_en), pre_data(validate_const_data{ static_cast(args)... }) {
+ }
+ };
+
+ constexpr size_t MAX_SIZE_CHK_PATCH_SEGMENT = 8;
+
+ using patch_segment_data = fixed_size_byte_array;
+
+ class PatchDetectionInfo {
+ public:
+ size_t chk_segment_offset;
+ patch_segment_data chk_segment_data;
+ size_t extract_var_offset;
+
+ constexpr PatchDetectionInfo(size_t chk_segment_offset, patch_segment_data chk_segment_data) :
+ chk_segment_offset(chk_segment_offset), chk_segment_data(chk_segment_data), extract_var_offset(0) {
+ }
+ constexpr PatchDetectionInfo(size_t chk_segment_offset, patch_segment_data chk_segment_data, size_t extract_var_offset) :
+ chk_segment_offset(chk_segment_offset), chk_segment_data(chk_segment_data), extract_var_offset(extract_var_offset) {
+ }
+ };
+}
+
+#endif
diff --git a/src/exe_constants.h b/src/exe_constants.h
new file mode 100644
index 0000000000..53f110325c
--- /dev/null
+++ b/src/exe_constants.h
@@ -0,0 +1,436 @@
+/*
+ * 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 .
+ */
+
+#ifndef EP_EXE_CONSTANTS_H
+#define EP_EXE_CONSTANTS_H
+
+#include "exe_buildinfo.h"
+#include "exe_shared.h"
+
+namespace EXE::Constants {
+ using namespace BuildInfo;
+ using GameConstantType = EXE::Shared::GameConstantType;
+
+ enum class KnownPatchConfigurations {
+ Rm2k3_Italian_WD_108, // Italian "WhiteDragon" patch
+ QP_StatDelimiter,
+ LAST
+ };
+
+ static constexpr auto kKnownPatchConfigurations = lcf::makeEnumTags(
+ "Rm2k3 Italian 1.08",
+ "QuickPatch StatDelimiter"
+ );
+
+ static_assert(kKnownPatchConfigurations.size() == static_cast(KnownPatchConfigurations::LAST));
+
+ using patch_config = std::map;
+ using T = GameConstantType;
+
+ const std::map known_patch_configurations = {
+ {
+ KnownPatchConfigurations::Rm2k3_Italian_WD_108, {
+ { T::MinVarLimit, -999999999 },
+ { T::MaxVarLimit, 999999999 },
+ { T::MaxEnemyHP, 999999999 },
+ { T::MaxEnemySP, 999999999 },
+ { T::MaxActorHP, 99999 },
+ { T::MaxActorSP, 9999 },
+ { T::MaxAtkBaseValue, 9999 },
+ { T::MaxDefBaseValue, 9999 },
+ { T::MaxSpiBaseValue, 9999 },
+ { T::MaxAgiBaseValue, 9999 },
+ { T::MaxDamageValue, 99999 },
+ { T::MaxGoldValue, 9999999 }
+ }},{
+ KnownPatchConfigurations::QP_StatDelimiter, {
+ { T::MaxActorHP, 9999999 },
+ { T::MaxActorSP, 9999999 },
+ { T::MaxAtkBaseValue, 999999 },
+ { T::MaxDefBaseValue, 999999 },
+ { T::MaxSpiBaseValue, 999999 },
+ { T::MaxAgiBaseValue, 999999 },
+ { T::MaxAtkBattleValue, 999999 },
+ { T::MaxDefBattleValue, 999999 },
+ { T::MaxSpiBattleValue, 999999 },
+ { T::MaxAgiBattleValue, 999999 }
+ }}
+ };
+
+ using code_address = std::pair;
+ using code_address_map = std::array(GameConstantType::LAST)>;
+
+#define ADD_EAX_ESI 0x03, 0xC6
+#define ADD_EDX_ESI 0x03, 0xD6
+#define MOV_EAX 0xB8
+#define MOV_ECX 0xB9
+#define MOV_EDX 0xBA
+#define SUB_EDX_EBX 0x2B, 0xD3
+#define CMP_DWORD_ESP 0x81, 0x7C, 0x24
+#define CMP_ESI 0x81, 0xFE
+#define CMP_EAX_BYTE 0x83, 0xF8
+#define CMP_EBX_BYTE 0x83, 0xFB
+
+//#define DEPENDS_ON_PREVIOUS { 0xFF, 0xFF, 0x00, 0xFF }
+//
+// constexpr auto magic_prev = std::array(DEPENDS_ON_PREVIOUS);
+
+ template
+ constexpr code_address map(T default_val, size_t code_offset, Args&&... args) {
+ return { C, CodeAddressInfo(default_val, sizeof(T), code_offset, std::forward(args)...) };
+ }
+
+ template
+ constexpr code_address not_def() {
+ return { C, CodeAddressInfo(0, 0, 0) };
+ }
+
+ using engine_code_adresses_rm2k = std::array, static_cast(count_known_rm2k_builds)>;
+ using engine_code_adresses_rm2k3 = std::array, static_cast(count_known_rm2k3_builds)>;
+
+ constexpr code_address_map empty_code_map = {{
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def()
+ }};
+
+ constexpr engine_code_adresses_rm2k known_engine_builds_rm2k = {{
+ {
+ RM2K_20000306,
+ empty_code_map
+ }, {
+ RM2K_2000XXXX_UNK,
+ {{
+ map ( -999999, 0x085A0C, CMP_DWORD_ESP, 0x10),
+ map ( 999999, 0x085A36, CMP_DWORD_ESP, 0x10),
+
+ map ( 160, 0x06D039, MOV_EDX),
+ map ( 148, 0x06D040, SUB_EDX_EBX, MOV_ECX),
+ map ( 160, 0x06D05B, MOV_EDX),
+ map ( 88, 0x06D062, SUB_EDX_EBX, MOV_ECX),
+
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def()
+ }}
+ }, {
+ RM2K_20000507,
+ empty_code_map
+ },{
+ RM2K_20000619,
+ empty_code_map
+ },{
+ RM2K_20000711,
+ {{
+ map ( -999999, 0x0846A8, CMP_DWORD_ESP, 0x10),
+ map ( 999999, 0x0846D2, CMP_DWORD_ESP, 0x10),
+
+ map ( 160, 0x06E491, MOV_EDX),
+ map ( 148, 0x06E498, SUB_EDX_EBX, MOV_ECX),
+ map ( 160, 0x06E4B3, MOV_EDX),
+ map ( 88, 0x06E4BA, SUB_EDX_EBX, MOV_ECX),
+
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def()
+ }}
+ },{
+ RM2K_20001113,
+ empty_code_map
+ },{
+ RM2K_20001115,
+ empty_code_map
+ },{
+ RM2K_20001227,
+ {{
+ map ( -999999, 0x085D78, CMP_DWORD_ESP, 0x10),
+ map ( 999999, 0x085DA2, CMP_DWORD_ESP, 0x10),
+
+ map ( 160, 0x06D5B9, MOV_EDX),
+ map ( 148, 0x06D5C0, SUB_EDX_EBX, MOV_ECX),
+ map ( 160, 0x06D5DB, MOV_EDX),
+ map ( 88, 0x06D5E2, SUB_EDX_EBX, MOV_ECX),
+
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def()
+ }}
+ },{
+ RM2K_20010505,
+ empty_code_map
+ },{
+ RM2K_20030327,
+ empty_code_map
+ },{
+ RM2K_20030625,
+ empty_code_map
+ },{
+ RM2KE_160,
+ empty_code_map
+ },{
+ RM2KE_161,
+ empty_code_map
+ },{
+ RM2KE_162,
+ empty_code_map
+ }
+ }};
+
+ constexpr engine_code_adresses_rm2k3 known_engine_builds_rm2k3 = {{
+ {
+ RM2K3_100,
+ empty_code_map
+ }, {
+ RM2K3_UNK_1,
+ empty_code_map
+ },{
+ RM2K3_UNK_2,
+ empty_code_map
+ }, {
+ RM2K3_1021_1021,
+ empty_code_map
+ },{
+ RM2K3_1030_1030_1,
+ empty_code_map
+ }, {
+ RM2K3_1030_1030_2,
+ empty_code_map
+ },{
+ RM2K3_1030_1040,
+ {{
+ map (-9999999, 0x0A60B3, CMP_DWORD_ESP, 0x10),
+ map ( 9999999, 0x0A60DD, CMP_DWORD_ESP, 0x10),
+
+ map ( 160, 0x08AC49, MOV_EDX),
+ map ( 148, 0x08AC50, SUB_EDX_EBX, MOV_ECX),
+ map ( 160, 0x08AC6B, MOV_EDX),
+ map ( 88, 0x08AC72, SUB_EDX_EBX, MOV_ECX),
+
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def()
+ }}
+ }, {
+ RM2K3_1050_1050_1,
+ empty_code_map
+ },{
+ RM2K3_1050_1050_2,
+ empty_code_map
+ }, {
+ RM2K3_1060_1060,
+ {{
+ map (-9999999, 0x0AC4F7, CMP_DWORD_ESP, 0x10),
+ map ( 9999999, 0x0AC521, CMP_DWORD_ESP, 0x10),
+
+ map ( 160, 0x08FB6D, MOV_EDX),
+ map ( 148, 0x08FB74, SUB_EDX_EBX, MOV_ECX),
+ map ( 160, 0x08FB8F, MOV_EDX),
+ map ( 88, 0x08FB96, SUB_EDX_EBX, MOV_ECX),
+
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def(),
+ not_def()
+ }}
+ },{
+ RM2K3_1070_1070,
+ empty_code_map
+ },{
+ RM2K3_1080_1080,
+ {{
+ map (-9999999, 0x0AC76B, CMP_DWORD_ESP, 0x10),
+ map ( 9999999, 0x0AC795, CMP_DWORD_ESP, 0x10),
+
+ map ( 160, 0x08FC21, MOV_EDX),
+ map ( 148, 0x08FC28, SUB_EDX_EBX, MOV_ECX),
+ map ( 160, 0x08FC43, MOV_EDX),
+ map ( 88, 0x08FC4A, SUB_EDX_EBX, MOV_ECX),
+
+ map ( 9999, 0x0B652B, MOV_ECX), /* 0x0B8590 - 0x0B858B */
+ map ( 999, 0x0B659D, MOV_ECX), /* 0x0B85B2 - 0x0B85AD */
+ not_def(),
+ not_def(),
+
+ map ( 999, 0x0B6636, MOV_ECX), /* 0x0B85D1 - 0xB85CC */
+ map ( 999, 0x0B689C, MOV_ECX), /* 0x0B85F0 - 0xB85EB */
+ map ( 999, 0x0B694C, MOV_ECX), /* 0x0B860F - 0xB860A */
+ map ( 999, 0x0B69F2, MOV_ECX), /* 0x0B862E - 0xB8629 */
+
+ map ( 9999, 0x0BEF3C, MOV_ECX),
+ map ( 9999, 0x0BF008, MOV_ECX),
+ map ( 9999, 0x0BF0D1, MOV_ECX),
+ map ( 9999, 0x0BF16D, MOV_ECX),
+
+ map ( 9999, 0x09C43C, MOV_EAX),
+ map ( 9999999, 0x0B60C3, CMP_ESI),
+ map ( 999999, 0x0A5B54, ADD_EDX_ESI, MOV_EAX),
+ map ( 99, 0x092399, CMP_EAX_BYTE),
+ map ( 16, 0x08FB34, CMP_EBX_BYTE),
+ not_def(),
+ }}
+ },{
+ RM2K3_1091_1091,
+ {{
+ map (-9999999, 0x0B5103, CMP_DWORD_ESP, 0x10),
+ map ( 9999999, 0x0B512D, CMP_DWORD_ESP, 0x10),
+
+ map ( 160, 0x08EE15, MOV_EDX),
+ map ( 148, 0x08EE1C, SUB_EDX_EBX, MOV_ECX),
+ map ( 160, 0x08EE37, MOV_EDX),
+ map ( 88, 0x08EE3E, SUB_EDX_EBX, MOV_ECX),
+
+ map ( 9999, 0x0AD543, MOV_ECX),
+ map ( 999, 0x0AD5B5, MOV_ECX),
+ not_def(),
+ not_def(),
+
+ map ( 999, 0x0AD64E, MOV_ECX),
+ map ( 999, 0x0AD8B4, MOV_ECX),
+ map ( 999, 0x0AD964, MOV_ECX),
+ map ( 999, 0x0ADA0A, MOV_ECX),
+
+ map ( 9999, 0x0A92B8, MOV_ECX),
+ map ( 9999, 0x0A9384, MOV_ECX),
+ map ( 9999, 0x0A944D, MOV_ECX),
+ map ( 9999, 0x0A94E9, MOV_ECX),
+
+ map ( 9999, 0x09B770, MOV_EAX),
+ map ( 9999999, 0x0AD0DB, CMP_ESI),
+ map ( 999999, 0x0A3EDC, ADD_EDX_ESI, MOV_EAX),
+ map ( 99, 0x09158D, CMP_EAX_BYTE),
+ map ( 16, 0x08ED28, CMP_EBX_BYTE),
+ not_def(),
+ }}
+ }
+ }};
+}
+
+#endif
diff --git a/src/exe_patches.h b/src/exe_patches.h
new file mode 100644
index 0000000000..551d01f833
--- /dev/null
+++ b/src/exe_patches.h
@@ -0,0 +1,104 @@
+/*
+ * 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 .
+ */
+
+#ifndef EP_EXE_PATCHES_H
+#define EP_EXE_PATCHES_H
+
+#include "exe_buildinfo.h"
+#include "exe_shared.h"
+
+namespace EXE::Patches {
+ using namespace BuildInfo;
+ using KnownPatches = EXE::Shared::KnownPatches;
+
+ template
+ constexpr patch_segment_data patch_segment(Args... args) {
+ return patch_segment_data{ static_cast(args)... };
+ }
+
+ using patch_detection = std::pair;
+ template
+ using patch_detection_map = std::array;
+
+ constexpr patch_detection_map<8> patches_RM2K_107 = {{
+ { KnownPatches::UnlockPics, { 0x082B4D, patch_segment(0x90, 0x90, 0x90, 0x90, 0x90) } },
+ { KnownPatches::CommonThisEvent, { 0x084F4C, patch_segment(0xE9, 0xAF, 0xBC, 0xFA, 0xFF) } },
+ { KnownPatches::BreakLoopFix, { 0x08A62A, patch_segment(0xE8, 0x85, 0xE8, 0xFF, 0xFF) } },
+ { KnownPatches::AutoEnterPatch, { 0x06D420, patch_segment(0x75, 0x75) } },
+ { KnownPatches::BetterAEP, { 0x06D75F, patch_segment(0xEB, 0x07), 0x096CA4 } },
+ { KnownPatches::PicPointer, { 0x087BE6, patch_segment(0xE8, 0xE9, 0xBF, 0xF9, 0xFF) } },
+ { KnownPatches::PicPointer_R, { 0x087BE6, patch_segment(0xE8, 0x51, 0xC0, 0xF9, 0xFF) } },
+ { KnownPatches::DirectMenu, { 0x078FC8, patch_segment(0xE9, 0x13, 0x92, 0xFB, 0xFF), 0x0321B5 } }
+ }};
+
+ constexpr patch_detection_map<4> patches_RM2K_110 = {{
+ { KnownPatches::CommonThisEvent, { 0x084E5C, patch_segment(0xE9, 0x33, 0xBD, 0xFA, 0xFF) } },
+ { KnownPatches::BreakLoopFix, { 0x08A73A, patch_segment(0xE8, 0x85, 0xE8, 0xFF, 0xFF) } },
+ { KnownPatches::PicPointer_R, { 0x023B28, patch_segment(0xE8, 0xD5, 0xC0, 0xF9, 0xFF) } },
+ { KnownPatches::DirectMenu, { 0x078ED8, patch_segment(0xE9, 0x93, 0x92, 0xFB, 0xFF), 0x032145 } }
+ }};
+
+ constexpr patch_detection_map<3> patches_RM2K_150 = {{
+ { KnownPatches::CommonThisEvent, { 0x089EAC, patch_segment(0xE9, 0xDF, 0x6D, 0xFA, 0xFF) } },
+ { KnownPatches::BreakLoopFix, { 0x08F72A, patch_segment(0xE8, 0x8D, 0xE8, 0xFF, 0xFF) } },
+ { KnownPatches::DirectMenu, { 0x07DE30, patch_segment(0xE9, 0x37, 0x44, 0xFB, 0xFF), 0x032241 } }
+ }};
+
+ constexpr patch_detection_map<4> patches_RM2K_151 = {{
+ { KnownPatches::CommonThisEvent, { 0x089FC8, patch_segment(0xE9, 0x07, 0x6D, 0xFA, 0xFF) } },
+ { KnownPatches::BreakLoopFix, { 0x08F846, patch_segment(0xE88DE8FFFF) } },
+ { KnownPatches::PicPointer_R, { 0x08CD82, patch_segment(0xE8, 0x89, 0x6F, 0xF9, 0xFF) } },
+ { KnownPatches::DirectMenu, { 0x07DF4C, patch_segment(0xE9, 0x5F, 0x43, 0xFB, 0xFF), 0x032285 } }
+ }};
+
+ constexpr patch_detection_map<2> patches_RM2K_160 = {{
+ { KnownPatches::CommonThisEvent, { 0x088838, patch_segment(0xE9, 0xEB, 0x85, 0xFA, 0xFF) } },
+ { KnownPatches::BreakLoopFix, { 0x08E098, patch_segment(0xE8, 0xAF, 0xE8, 0xFF, 0xFF) } }
+ }};
+
+ constexpr patch_detection_map<2> patches_RM2K_161 = {{
+ { KnownPatches::CommonThisEvent, { 0x089208, patch_segment(0xE9, 0x2B, 0x80, 0xFA, 0xFF) } },
+ { KnownPatches::BreakLoopFix, { 0x08EAA4, patch_segment(0xE8, 0xAF, 0xE8, 0xFE, 0xFF) } }
+ }};
+
+ constexpr patch_detection_map<4> patches_RM2K_162 = {{
+ { KnownPatches::CommonThisEvent, { 0x08B8D0, patch_segment(0xE9, 0x63, 0x59, 0xFA, 0xFF) } },
+ { KnownPatches::BreakLoopFix, { 0x09116C, patch_segment(0xE8, 0xAF, 0xE8, 0xFF, 0xFF) } },
+ { KnownPatches::PicPointer_R, { 0x08E6CA, patch_segment(0xE8, 0xA5, 0x5B, 0xF9, 0xFF) } },
+ { KnownPatches::DirectMenu, { 0x07F848, patch_segment(0xE9, 0xC7, 0x2F, 0xFB, 0xFF), 0x0327E9 } }
+ }};
+
+ constexpr patch_detection_map<8> patches_RM2K3_1080 = {{
+ { KnownPatches::UnlockPics, { 0x0B12FA, patch_segment(0x90, 0x90, 0x90, 0x90, 0x90) } },
+ { KnownPatches::CommonThisEvent, { 0x0AB670, patch_segment(0xE9, 0x8B, 0x76, 0xF9, 0xFF) } },
+ { KnownPatches::BreakLoopFix, { 0x0B1E4B, patch_segment(0xE8, 0x04, 0xE2, 0xFF, 0xFF) } },
+ { KnownPatches::AutoEnterPatch, { 0x08FAC0, patch_segment(0x75, 0x3D) } },
+ { KnownPatches::BetterAEP, { 0x08FDA7, patch_segment(0xEB, 0x07), 0x0C91A4 } },
+ { KnownPatches::PicPointer, { 0x0AEB1E, patch_segment(0xE8, 0x01, 0x5F, 0xF8, 0xFF) } },
+ { KnownPatches::PicPointer_R, { 0x0AEB1E, patch_segment(0xE8, 0x65, 0x5F, 0xF8, 0xFF) } },
+ { KnownPatches::DirectMenu, { 0x0A0422, patch_segment(0xE9, 0xE2, 0x5E, 0xFA, 0xFF), 0x00462DE } }
+ }};
+
+ constexpr patch_detection_map<4> patches_RM2K3_1091 = {{
+ { KnownPatches::CommonThisEvent, { 0x0B4008, patch_segment(0xE9, 0xF3, 0xEC, 0xF8, 0xFF) } },
+ { KnownPatches::BreakLoopFix, { 0x0BA7E3, patch_segment(0xE8, 0x04, 0xE2, 0xFF, 0xFF) } },
+ { KnownPatches::PicPointer_R, { 0x0B74B6, patch_segment(0xE8, 0xCD, 0xD5, 0xF7, 0xFF) } },
+ { KnownPatches::DirectMenu, { 0x09F756, patch_segment(0xE9, 0xAE, 0x6B, 0xFA, 0xFF), 0x00462DE } }
+ }};
+}
+
+#endif
diff --git a/src/exe_reader.cpp b/src/exe_reader.cpp
index 4785f021a8..ae8a9921ae 100644
--- a/src/exe_reader.cpp
+++ b/src/exe_reader.cpp
@@ -25,6 +25,7 @@
#include
#include
#include
+#include
namespace {
// hashes of known RPG_RT startup logos
@@ -74,6 +75,8 @@ EXEReader::EXEReader(Filesystem_Stream::InputStream core) : corefile(std::move(c
uint32_t sectionsOfs = optional_header + GetU16(ofs + 0x14); // skip opt header
uint32_t data_directory_ofs = (format_pe32 ? 0x60 : 0x70);
resource_rva = GetU32(optional_header + data_directory_ofs + 16);
+ file_info.entrypoint = format_pe32 ? GetU32(optional_header + 0x10) : 0;
+
if (!resource_rva) {
// Is some kind of encrypted EXE -> Give up
return;
@@ -90,6 +93,7 @@ EXEReader::EXEReader(Filesystem_Stream::InputStream core) : corefile(std::move(c
if (secName == 0x45444F43) { // CODE
file_info.code_size = sectVs;
+ file_info.code_ofs = GetU32(sectionsOfs + 0x14);
} else if (secName == 0x52454843) { // CHER(RY)
file_info.cherry_size = sectVs;
} else if (secName == 0x50454547) { // GEEP
@@ -406,7 +410,7 @@ bool EXEReader::ResNameCheck(uint32_t i, const char* p) {
}
void EXEReader::FileInfo::Print() const {
- Output::Debug("RPG_RT information: version={} logos={} code={:#x} cherry={:#x} geep={:#x} arch={} easyrpg={}", version_str, logos, code_size, cherry_size, geep_size, kMachineTypes[machine_type], is_easyrpg_player);
+ Output::Debug("RPG_RT information: version={} logos={} code={:#x} entrypoint={:#x} cherry={:#x} geep={:#x} arch={} easyrpg={}", version_str, logos, code_size, entrypoint, cherry_size, geep_size, kMachineTypes[machine_type], is_easyrpg_player);
}
int EXEReader::FileInfo::GetEngineType(bool& is_maniac_patch) const {
@@ -489,4 +493,325 @@ int EXEReader::FileInfo::GetEngineType(bool& is_maniac_patch) const {
return Player::EngineNone;
}
+std::map EXEReader::GetOverridenGameConstants() {
+ constexpr bool debug_const_extraction = true;
+
+ std::map game_constants;
+ int code_offset = file_info.code_ofs - 0x400;
+
+ auto match_surrounding_data = [&](const EXE::BuildInfo::CodeAddressInfo& info, const uint32_t const_ofs) {
+ for (int i = 0; i < info.pre_data.size(); i++) {
+ if (info.pre_data[i] != GetU8(const_ofs - info.pre_data.size() + i))
+ return false;
+ }
+ /*for (int i = 0; i < info.post_data.size(); i++) {
+ if (info.post_data[i] != GetU8(const_ofs + sizeof(uint32_t) + i))
+ return false;
+ }*/
+ //Is a hit -> constant value can be extracted
+ return true;
+ };
+
+ auto check_address_map = [&](const EXE::Constants::code_address_map& map) {
+ uint32_t const_ofs;
+ bool extract_success = false;
+
+ for (auto it = map.begin(); it != map.end();++it) {
+ auto const_type = it->first;
+ auto& addr_info = it->second;
+
+ if (addr_info.code_offset == 0) {
+ // constant is not defined in this map
+ continue;
+ }
+
+ const_ofs = code_offset + addr_info.code_offset;
+
+ bool extract_constant = false;
+ /*if (addr_info.pre_data == ExeConstants::magic_prev && extract_success) {
+ extract_constant = true;
+ } else*/
+ if (match_surrounding_data(addr_info, const_ofs)) {
+ extract_constant = true;
+ }
+
+ if (extract_constant) {
+ int32_t value;
+ switch (addr_info.size_val) {
+ case 4:
+ value = GetU32(const_ofs);
+ break;
+ case 2:
+ value = GetU16(const_ofs);
+ break;
+ case 1:
+ value = GetU8(const_ofs);
+ break;
+ default:
+ continue;
+ }
+
+ auto it = game_constants.find(const_type);
+ if (it != game_constants.end() && it->second == value) {
+ // Constant override has already been applied through some other means
+ continue;
+ }
+
+ if (value != addr_info.default_val || it != game_constants.end()) {
+ game_constants[const_type] = value;
+ Output::Debug("Read constant '{}': {} (default: {})", EXE::Shared::kGameConstantType.tag(const_type), value, addr_info.default_val);
+ } else if (debug_const_extraction) {
+ Output::Debug("Constant '{}' unchanged: {}", EXE::Shared::kGameConstantType.tag(const_type), value);
+ }
+ extract_success = true;
+ } else {
+ Output::Debug("Could not read constant '{}'", EXE::Shared::kGameConstantType.tag(const_type));
+ extract_success = false;
+ }
+ }
+ };
+
+ auto apply_known_config = [&](EXE::Constants::KnownPatchConfigurations conf) {
+ Output::Debug("Assuming known patch config '{}'", EXE::Constants::kKnownPatchConfigurations.tag(static_cast(conf)));
+ auto it_conf = EXE::Constants::known_patch_configurations.find(conf);
+ assert(it_conf != EXE::Constants::known_patch_configurations.end());
+
+ for (auto it = it_conf->second.begin(); it != it_conf->second.end(); ++it) {
+ game_constants[it->first] = it->second;
+ }
+ };
+
+ for (auto it = EXE::BuildInfo::known_engine_builds.begin(); it != EXE::BuildInfo::known_engine_builds.end(); ++it) {
+ auto& curr_build_info = std::get(*it);
+
+ if (file_info.code_size != curr_build_info.code_size || file_info.entrypoint != curr_build_info.entrypoint) {
+ continue;
+ }
+ build_version = std::get(*it);
+ build_info = curr_build_info;
+ break;
+ }
+
+ if (build_version != EXE::BuildInfo::KnownEngineBuildVersions::UnknownBuild) {
+ Output::Debug("Assuming {} build '{}' for constant extraction",
+ EXE::BuildInfo::kEngineTypes.tag(build_info.engine_type),
+ EXE::BuildInfo::kKnownEngineBuildDescriptions.tag(build_version));
+
+ EXE::Constants::code_address_map const* constant_addresses = nullptr;
+
+ switch (build_info.engine_type) {
+ case EXE::BuildInfo::EngineType::RPG2000:
+ {
+ auto& builds = EXE::Constants::known_engine_builds_rm2k;
+ auto it = std::find_if(builds.begin(), builds.end(), [&](const auto& pair) {
+ return pair.first == build_version;
+ });
+ if (it != builds.end()) {
+ constant_addresses = &it->second;
+ }
+ }
+ break;
+ case EXE::BuildInfo::EngineType::RPG2003:
+ {
+ auto& builds = EXE::Constants::known_engine_builds_rm2k3;
+ auto it = std::find_if(builds.begin(), builds.end(), [&](const auto& pair) {
+ return pair.first == build_version;
+ });
+ if (it != builds.end()) {
+ constant_addresses = &it->second;
+ }
+ }
+ break;
+ }
+
+ switch (build_version) {
+ case EXE::BuildInfo::RM2KE_162:
+ if (CheckForString(0x07DEA6, "XXX" /* 3x "POP EAX" */)) {
+ apply_known_config(EXE::Constants::KnownPatchConfigurations::QP_StatDelimiter);
+ }
+ break;
+ case EXE::BuildInfo::RM2K3_1080_1080:
+ if (CheckForString(0x08EFE0, "NoTitolo")) {
+ apply_known_config(EXE::Constants::KnownPatchConfigurations::Rm2k3_Italian_WD_108);
+ }
+ if (CheckForString(0x09D679, "XXX" /* 3x "POP EAX" */)) {
+ apply_known_config(EXE::Constants::KnownPatchConfigurations::QP_StatDelimiter);
+ }
+ break;
+ case EXE::BuildInfo::RM2K3_1091_1091:
+ if (CheckForString(0x09C9AD, "XXX" /* 3x "POP EAX" */)) {
+ apply_known_config(EXE::Constants::KnownPatchConfigurations::QP_StatDelimiter);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (constant_addresses) {
+ check_address_map(*constant_addresses);
+ }
+ } else {
+ Output::Debug("Unknown build");
+ }
+ return game_constants;
+}
+
+std::map EXEReader::GetEmbeddedStrings(std::string encoding) {
+ constexpr int max_string_size = 32;
+ constexpr bool debug_string_extraction = true;
+
+ std::map embedded_strings;
+ int code_offset = file_info.code_ofs - 0x400;
+ std::array str_data;
+
+ auto match_surrounding_data = [&](const EXE::BuildInfo::CodeAddressStringInfo& info, const uint32_t const_ofs) {
+ for (int i = 0; i < info.pre_data.size(); i++) {
+ if (info.pre_data[i] != GetU8(const_ofs - info.pre_data.size() + i))
+ return false;
+ }
+ return true;
+ };
+
+ auto check_string_address_map = [&](const EXE::Strings::string_address_map& map) {
+ uint32_t const_ofs;
+ bool extract_success = false;
+
+ for (auto it = map.begin(); it != map.end(); ++it) {
+ auto const_type = it->first;
+ auto& addr_info = it->second;
+
+ if (addr_info.code_offset == 0) {
+ // string is not defined in this map
+ continue;
+ }
+
+ const_ofs = code_offset + addr_info.code_offset;
+
+ bool extract_string = false;
+ if (match_surrounding_data(addr_info, const_ofs)) {
+ extract_string = true;
+ }
+
+ if (extract_string) {
+ int32_t size_str = GetU32(const_ofs);
+ if (size_str > max_string_size) {
+ Output::Debug("Unexpected length for embedded string: {} ({})", EXE::Shared::kEmbeddedStringTypes.tag(const_type), size_str);
+ continue;
+ }
+ const_ofs += 4;
+ for (int i = 0; i < size_str; ++i) {
+ str_data[i] = GetU8(const_ofs + i);
+ }
+ auto crc = static_cast(crc32(0, reinterpret_cast(str_data.data()), size_str));
+
+ if ((crc != addr_info.crc_jp && crc != addr_info.crc_en) || debug_string_extraction) {
+ auto extracted_string = lcf::ReaderUtil::Recode(ToString(lcf::DBString(str_data.data(), static_cast(size_str))), encoding);
+
+ if (debug_string_extraction && crc == addr_info.crc_jp) {
+ Output::Debug("Embedded string for '{}' matches JP -> '{}'", EXE::Shared::kEmbeddedStringTypes.tag(const_type), extracted_string);
+ } else if (debug_string_extraction && crc == addr_info.crc_en) {
+ Output::Debug("Embedded string for '{}' matches EN -> '{}'", EXE::Shared::kEmbeddedStringTypes.tag(const_type), extracted_string);
+ } else {
+ Output::Debug("Read embedded string '{}' -> '{}'", EXE::Shared::kEmbeddedStringTypes.tag(const_type), extracted_string);
+
+ //TODO: add to map
+ }
+ }
+
+ extract_success = true;
+ } else {
+ Output::Debug("Could not read embedded string '{}'", EXE::Shared::kEmbeddedStringTypes.tag(const_type));
+ extract_success = false;
+ }
+ }
+ };
+
+ switch (build_version) {
+ case EXE::BuildInfo::RM2K_20030625:
+ check_string_address_map(EXE::Strings::string_addresses_rm2k_151);
+ break;
+ default:
+ break;
+ }
+
+ return embedded_strings;
+}
+
+std::vector EXEReader::CheckForPatches() {
+ std::vector patches;
+
+ int code_offset = file_info.code_ofs - 0x400;
+
+ auto check_for_patch_segment = [&](const EXE::BuildInfo::PatchDetectionInfo& patch_info) {
+ for (int i = 0; i < patch_info.chk_segment_data.size(); i++) {
+ if (patch_info.chk_segment_data[i] != GetU8(code_offset + patch_info.chk_segment_offset + i))
+ return false;
+ }
+ };
+
+ auto apply_patches = [&](const auto& patch_detection_map) {
+ for (auto it = patch_detection_map.begin(); it < patch_detection_map.end(); ++it) {
+ auto patch_type = it->first;
+ auto& patch_info = it->second;
+
+ if (!check_for_patch_segment(patch_info)) {
+ continue;
+ }
+
+ if (patch_info.extract_var_offset == 0) {
+ Output::Debug("Detected Patch: '{}'", EXE::Shared::kKnownPatches.tag(static_cast(patch_type)));
+ patches.emplace_back(EXE::Shared::PatchSetupInfo { patch_type, 0 });
+ } else {
+ int patch_var = GetU32(code_offset + patch_info.extract_var_offset);
+ if (patch_var > 0) {
+ Output::Debug("Detected Patch: '{}' (VarId: {})", EXE::Shared::kKnownPatches.tag(static_cast(patch_type)), patch_var);
+ }
+ patches.emplace_back(EXE::Shared::PatchSetupInfo{ patch_type, patch_var });
+ }
+ }
+ };
+
+ switch (build_version) {
+ case EXE::BuildInfo::RM2K_20001227:
+ apply_patches(EXE::Patches::patches_RM2K_107);
+ break;
+ case EXE::BuildInfo::RM2K_20010505:
+ apply_patches(EXE::Patches::patches_RM2K_110);
+ break;
+ case EXE::BuildInfo::RM2K_20030327:
+ apply_patches(EXE::Patches::patches_RM2K_150);
+ break;
+ case EXE::BuildInfo::RM2K_20030625:
+ apply_patches(EXE::Patches::patches_RM2K_151);
+ break;
+ case EXE::BuildInfo::RM2KE_160:
+ apply_patches(EXE::Patches::patches_RM2K_160);
+ break;
+ case EXE::BuildInfo::RM2KE_161:
+ apply_patches(EXE::Patches::patches_RM2K_161);
+ break;
+ case EXE::BuildInfo::RM2KE_162:
+ apply_patches(EXE::Patches::patches_RM2K_162);
+ break;
+
+ case EXE::BuildInfo::RM2K3_1080_1080:
+ apply_patches(EXE::Patches::patches_RM2K3_1080);
+ break;
+ case EXE::BuildInfo::RM2K3_1091_1091:
+ apply_patches(EXE::Patches::patches_RM2K3_1091);
+ break;
+ default:
+ break;
+ }
+
+ return patches;
+}
+
+bool EXEReader::CheckForString(uint32_t offset, const char* p) {
+ while (*p) {
+ if (GetU8(file_info.code_ofs - 0x400 + offset++) != *p++)
+ return false;
+ }
+ return true;
+}
#endif
diff --git a/src/exe_reader.h b/src/exe_reader.h
index 15abb5ded8..a9ec4ff5b4 100644
--- a/src/exe_reader.h
+++ b/src/exe_reader.h
@@ -23,6 +23,10 @@
#include
#include
#include "bitmap.h"
+#include "exe_buildinfo.h"
+#include "exe_constants.h"
+#include "exe_patches.h"
+#include "exe_strings.h"
#include "player.h"
/**
@@ -64,6 +68,8 @@ class EXEReader {
uint32_t geep_size = 0;
MachineType machine_type = MachineType::Unknown;
bool is_easyrpg_player = false;
+ uint32_t code_ofs = 0;
+ uint32_t entrypoint = 0;
int GetEngineType(bool& is_maniac_patch) const;
void Print() const;
@@ -71,6 +77,12 @@ class EXEReader {
const FileInfo& GetFileInfo();
+ std::map GetOverridenGameConstants();
+
+ std::map GetEmbeddedStrings(std::string encoding);
+
+ std::vector