Skip to content

Commit 7704c0c

Browse files
authored
Merge pull request #3407 from florianessl/compat/StatDelimiter
Support for changed game constants: StatDelimiter & WhiteDragon patches
2 parents f757eb1 + eaf645c commit 7704c0c

27 files changed

Lines changed: 505 additions & 108 deletions

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,8 @@ add_library(${PROJECT_NAME} OBJECT
170170
src/game_config.h
171171
src/game_config_game.cpp
172172
src/game_config_game.h
173+
src/game_constants.h
174+
src/game_constants.cpp
173175
src/game_destiny.cpp
174176
src/game_destiny.h
175177
src/game_dynrpg.cpp

src/exe_reader.cpp

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,17 @@
1616
*/
1717

1818
// All of this code is unused on EMSCRIPTEN. *Do not use it*!
19+
1920
#ifndef EMSCRIPTEN
2021

2122
#include "exe_reader.h"
2223
#include "image_bmp.h"
2324
#include "output.h"
24-
#include "utils.h"
25+
#include "player.h"
2526
#include <algorithm>
2627
#include <iostream>
27-
#include <fstream>
2828
#include <zlib.h>
29+
#include <unordered_map>
2930

3031
namespace {
3132
// hashes of known RPG_RT startup logos
@@ -122,6 +123,7 @@ EXEReader::EXEReader(Filesystem_Stream::InputStream core) : corefile(std::move(c
122123
uint32_t sectionsOfs = optional_header + GetU16(ofs + 0x14); // skip opt header
123124
uint32_t data_directory_ofs = (format_pe32 ? 0x60 : 0x70);
124125
resource_rva = GetU32(optional_header + data_directory_ofs + 16);
126+
125127
if (!resource_rva) {
126128
// Is some kind of encrypted EXE -> Give up
127129
return;
@@ -138,6 +140,7 @@ EXEReader::EXEReader(Filesystem_Stream::InputStream core) : corefile(std::move(c
138140

139141
if (secName == 0x45444F43) { // CODE
140142
file_info.code_size = sectVs;
143+
file_info.code_ofs = GetU32(sectionsOfs + 0x14);
141144
} else if (secName == 0x52454843) { // CHER(RY)
142145
file_info.cherry_size = sectVs;
143146
} else if (secName == 0x50454547) { // GEEP
@@ -563,4 +566,52 @@ int EXEReader::FileInfo::GetEngineType(int& mp_version) const {
563566
return Player::EngineNone;
564567
}
565568

569+
std::unordered_map<Game_Constants::ConstantType, int32_t> EXEReader::GetOverriddenGameConstants() {
570+
std::unordered_map<Game_Constants::ConstantType, int32_t> game_constants;
571+
572+
auto apply_known_config = [&](Game_Constants::KnownPatchConfigurations conf) {
573+
Output::Debug("Assuming known patch config '{}'", Game_Constants::kKnownPatchConfigurations.tag(static_cast<int>(conf)));
574+
auto it_conf = Game_Constants::known_patch_configurations.find(conf);
575+
assert(it_conf != Game_Constants::known_patch_configurations.end());
576+
577+
for (auto it = it_conf->second.begin(); it != it_conf->second.end(); ++it) {
578+
game_constants[it->first] = it->second;
579+
}
580+
};
581+
582+
auto check_for_string = [&](uint32_t offset, const char* p) {
583+
while (*p) {
584+
if (GetU8(offset++) != *p++)
585+
return false;
586+
}
587+
return true;
588+
};
589+
590+
switch (file_info.code_size) {
591+
case 0x9CC00: // RM2K 1.62
592+
if (check_for_string(file_info.code_ofs + 0x07DAA6, "XXX" /* 3x "POP EAX" */)) {
593+
apply_known_config(Game_Constants::KnownPatchConfigurations::StatDelimiter);
594+
}
595+
break;
596+
case 0xC8E00: // RM2K3 1.0.8.0
597+
// For all known Italian translations, the "WhiteDragon" patch seems to be the only one
598+
// to translate this string in RPG_RT. So this segment can be used to reliably detect
599+
// the patch without having to read all individual constant values from the EXE
600+
if (check_for_string(file_info.code_ofs + 0x08EBE0, "NoTitolo")) {
601+
apply_known_config(Game_Constants::KnownPatchConfigurations::Rm2k3_Italian_WD_108);
602+
}
603+
if (check_for_string(file_info.code_ofs + 0x09D279, "XXX" /* 3x "POP EAX" */)) {
604+
apply_known_config(Game_Constants::KnownPatchConfigurations::StatDelimiter);
605+
}
606+
break;
607+
case 0xC9000: // RM2K3 1.0.9.1
608+
if (check_for_string(file_info.code_ofs + 0x09C5AD, "XXX" /* 3x "POP EAX" */)) {
609+
apply_known_config(Game_Constants::KnownPatchConfigurations::StatDelimiter);
610+
}
611+
break;
612+
}
613+
614+
return game_constants;
615+
}
616+
566617
#endif

src/exe_reader.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@
1818
#ifndef EP_EXE_READER_H
1919
#define EP_EXE_READER_H
2020

21+
#include "filesystem_stream.h"
22+
#include "game_constants.h"
2123
#include <cstdint>
24+
#include <lcf/enum_tags.h>
2225
#include <string>
23-
#include <istream>
2426
#include <vector>
25-
#include "bitmap.h"
26-
#include "player.h"
2727

2828
/**
2929
* Extracts resources from an EXE.
@@ -65,13 +65,16 @@ class EXEReader {
6565
MachineType machine_type = MachineType::Unknown;
6666
bool is_easyrpg_player = false;
6767
int maniac_patch_version = 0;
68+
uint32_t code_ofs = 0;
6869

6970
int GetEngineType(int& mp_version) const;
7071
void Print() const;
7172
};
7273

7374
const FileInfo& GetFileInfo();
7475

76+
std::unordered_map<Game_Constants::ConstantType, int32_t> GetOverriddenGameConstants();
77+
7578
private:
7679
// Bounds-checked unaligned reader primitives.
7780
// In case of out-of-bounds, returns 0 - this will usually result in a harmless error at some other level,

src/font.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
#include "cache.h"
5656
#include "player.h"
5757
#include "compiler.h"
58+
#include "game_clock.h"
5859

5960
// Static variables.
6061
namespace {

src/game_actor.cpp

Lines changed: 7 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@
1717

1818
// Headers
1919
#include <algorithm>
20-
#include <sstream>
2120
#include <iterator>
2221
#include "game_actor.h"
2322
#include "game_battle.h"
23+
#include "game_constants.h"
2424
#include "game_party.h"
2525
#include "sprite_actor.h"
2626
#include "main_data.h"
@@ -36,47 +36,24 @@
3636
#include "algo.h"
3737
#include "game_message_terms.h"
3838

39-
constexpr int max_level_2k = 50;
40-
constexpr int max_level_2k3 = 99;
41-
4239
int Game_Actor::MaxHpValue() const {
43-
auto& val = lcf::Data::system.easyrpg_max_actor_hp;
44-
if (val == -1) {
45-
return Player::IsRPG2k() ? 999 : 9999;
46-
}
47-
return val;
40+
return Main_Data::game_constants->MaxActorHpValue();
4841
}
4942

5043
int Game_Actor::MaxSpValue() const {
51-
auto& val = lcf::Data::system.easyrpg_max_actor_sp;
52-
if (val == -1) {
53-
return 999;
54-
}
55-
return val;
44+
return Main_Data::game_constants->MaxActorSpValue();
5645
}
5746

5847
int Game_Actor::MaxStatBattleValue() const {
59-
auto& val = lcf::Data::system.easyrpg_max_stat_battle_value;
60-
if (val == -1) {
61-
return 9999;
62-
}
63-
return val;
48+
return Main_Data::game_constants->MaxStatBattleValue();
6449
}
6550

6651
int Game_Actor::MaxStatBaseValue() const {
67-
auto& val = lcf::Data::system.easyrpg_max_stat_base_value;
68-
if (val == -1) {
69-
return 999;
70-
}
71-
return val;
52+
return Main_Data::game_constants->MaxStatBaseValue();
7253
}
7354

7455
int Game_Actor::MaxExpValue() const {
75-
auto& val = lcf::Data::system.easyrpg_max_exp;
76-
if (val == -1) {
77-
return Player::IsRPG2k() ? 999999 : 9999999;
78-
}
79-
return val;
56+
return Main_Data::game_constants->MaxExpValue();
8057
}
8158

8259
Game_Actor::Game_Actor(int actor_id) {
@@ -745,11 +722,7 @@ int Game_Actor::GetAccessoryId() const {
745722
}
746723

747724
int Game_Actor::GetMaxLevel() const {
748-
int max_level = Player::IsRPG2k() ? max_level_2k : max_level_2k3;
749-
if (lcf::Data::system.easyrpg_max_level > -1) {
750-
max_level = lcf::Data::system.easyrpg_max_level;
751-
}
752-
return Utils::Clamp<int32_t>(max_level, 1, dbActor->final_level);
725+
return Utils::Clamp<int32_t>(Main_Data::game_constants->MaxLevel(), 1, dbActor->final_level);
753726
}
754727

755728
void Game_Actor::SetExp(int _exp) {

src/game_actor.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,7 @@ class Game_Actor final : public Game_Battler {
425425

426426
/**
427427
* Sets exp of actor.
428-
* The value is adjusted to the boundary 0 up 999999.
428+
* The value is adjusted to the boundary 0 up to a maximum (dependent on engine type & patch).
429429
* Other actor attributes are not altered. Use ChangeExp to do a proper
430430
* experience change.
431431
*

src/game_actors.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,15 @@
1717

1818
// Headers
1919
#include "system.h"
20+
#include <cassert>
2021
#include <vector>
2122
#include "game_actors.h"
2223
#include "main_data.h"
2324
#include "output.h"
2425

2526
Game_Actors::Game_Actors() {
27+
assert(Main_Data::game_constants != nullptr && "Game Constants must be initialized");
28+
2629
data.reserve(lcf::Data::actors.size());
2730
for (size_t i = 0; i < lcf::Data::actors.size(); i++) {
2831
data.emplace_back(Game_Actor(i + 1));

src/game_battlealgorithm.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,20 @@
1717
*/
1818

1919
#include <cassert>
20-
#include <cmath>
2120
#include <cstdlib>
2221
#include <algorithm>
23-
#include <sstream>
2422
#include "game_actor.h"
2523
#include "game_battle.h"
2624
#include "game_battlealgorithm.h"
2725
#include "game_battler.h"
26+
#include "game_constants.h"
2827
#include "game_enemy.h"
29-
#include "game_enemyparty.h"
3028
#include "game_party.h"
3129
#include "game_party_base.h"
3230
#include "game_switches.h"
3331
#include "game_system.h"
3432
#include "main_data.h"
3533
#include "game_message_terms.h"
36-
#include "output.h"
3734
#include "player.h"
3835
#include <lcf/reader_util.h>
3936
#include <lcf/rpg/animation.h>
@@ -52,7 +49,7 @@
5249
#include "feature.h"
5350

5451
static inline int MaxDamageValue() {
55-
return lcf::Data::system.easyrpg_max_damage == -1 ? (Player::IsRPG2k() ? 999 : 9999) : lcf::Data::system.easyrpg_max_damage;
52+
return Main_Data::game_constants->MaxDamageValue();
5653
}
5754

5855
Game_BattleAlgorithm::AlgorithmBase::AlgorithmBase(Type ty, Game_Battler* source, Game_Battler* target) :

src/game_config_game.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,16 @@ void Game_ConfigGame::LoadFromArgs(CmdlineParser& cp) {
7777
}
7878
continue;
7979
}
80+
if (cp.ParseNext(arg, 1, "--engine-path")) {
81+
if (arg.NumValues() > 0) {
82+
std::string path = arg.Value(0);
83+
path = FileFinder::MakeCanonical(path, 0);
84+
if (!path.empty()) {
85+
engine_path.Set(path);
86+
}
87+
}
88+
continue;
89+
}
8090
if (cp.ParseNext(arg, 0, "--no-patch")) {
8191
patch_support.Set(false);
8292
patch_dynrpg.Lock(false);
@@ -203,6 +213,7 @@ void Game_ConfigGame::LoadFromStream(Filesystem_Stream::InputStream& is) {
203213

204214
new_game.FromIni(ini);
205215
engine_str.FromIni(ini);
216+
engine_path.FromIni(ini);
206217
fake_resolution.FromIni(ini);
207218

208219
if (patch_easyrpg.FromIni(ini)) {

src/game_config_game.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ struct Game_ConfigGame {
3737

3838
BoolConfigParam new_game{ "Start new game", "Skips the title screen and starts a new game directly", "Game", "NewGame", false };
3939
StringConfigParam engine_str{ "Engine", "", "Game", "Engine", std::string() };
40+
StringConfigParam engine_path{ "Engine Path", "Sets the executable to be used by the engine auto-detection", "Game", "EnginePath", std::string() };
4041
BoolConfigParam fake_resolution{ "Fake Metrics", "Makes games run on higher resolutions (with some success)", "Game", "FakeResolution", false };
4142
BoolConfigParam patch_easyrpg{ "EasyRPG", "EasyRPG Engine Extensions", "Patch", "EasyRPG", false };
4243
BoolConfigParam patch_destiny{ "Destiny Patch", "", "Patch", "Destiny", false };

0 commit comments

Comments
 (0)