Skip to content
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ add_library(${PROJECT_NAME} OBJECT
src/game_config.h
src/game_config_game.cpp
src/game_config_game.h
src/game_constants.h
src/game_constants.cpp
src/game_destiny.cpp
src/game_destiny.h
src/game_dynrpg.cpp
Expand Down
55 changes: 53 additions & 2 deletions src/exe_reader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,17 @@
*/

// All of this code is unused on EMSCRIPTEN. *Do not use it*!

#ifndef EMSCRIPTEN

#include "exe_reader.h"
#include "image_bmp.h"
#include "output.h"
#include "utils.h"
#include "player.h"
#include <algorithm>
#include <iostream>
#include <fstream>
#include <zlib.h>
#include <unordered_map>

namespace {
// hashes of known RPG_RT startup logos
Expand Down Expand Up @@ -122,6 +123,7 @@ 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);

if (!resource_rva) {
// Is some kind of encrypted EXE -> Give up
return;
Expand All @@ -138,6 +140,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
Expand Down Expand Up @@ -563,4 +566,52 @@ int EXEReader::FileInfo::GetEngineType(int& mp_version) const {
return Player::EngineNone;
}

std::unordered_map<Game_Constants::ConstantType, int32_t> EXEReader::GetOverriddenGameConstants() {
std::unordered_map<Game_Constants::ConstantType, int32_t> game_constants;

auto apply_known_config = [&](Game_Constants::KnownPatchConfigurations conf) {
Output::Debug("Assuming known patch config '{}'", Game_Constants::kKnownPatchConfigurations.tag(static_cast<int>(conf)));
auto it_conf = Game_Constants::known_patch_configurations.find(conf);
assert(it_conf != Game_Constants::known_patch_configurations.end());

for (auto it = it_conf->second.begin(); it != it_conf->second.end(); ++it) {
game_constants[it->first] = it->second;
}
};

auto check_for_string = [&](uint32_t offset, const char* p) {
while (*p) {
if (GetU8(offset++) != *p++)
return false;
}
return true;
};

switch (file_info.code_size) {
case 0x9CC00: // RM2K 1.62
if (check_for_string(file_info.code_ofs + 0x07DAA6, "XXX" /* 3x "POP EAX" */)) {
apply_known_config(Game_Constants::KnownPatchConfigurations::StatDelimiter);
}
break;
case 0xC8E00: // RM2K3 1.0.8.0
// For all known Italian translations, the "WhiteDragon" patch seems to be the only one
// to translate this string in RPG_RT. So this segment can be used to reliably detect
// the patch without having to read all individual constant values from the EXE
if (check_for_string(file_info.code_ofs + 0x08EBE0, "NoTitolo")) {
apply_known_config(Game_Constants::KnownPatchConfigurations::Rm2k3_Italian_WD_108);
}
if (check_for_string(file_info.code_ofs + 0x09D279, "XXX" /* 3x "POP EAX" */)) {
apply_known_config(Game_Constants::KnownPatchConfigurations::StatDelimiter);
}
break;
case 0xC9000: // RM2K3 1.0.9.1
if (check_for_string(file_info.code_ofs + 0x09C5AD, "XXX" /* 3x "POP EAX" */)) {
apply_known_config(Game_Constants::KnownPatchConfigurations::StatDelimiter);
}
break;
}

return game_constants;
}

#endif
9 changes: 6 additions & 3 deletions src/exe_reader.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@
#ifndef EP_EXE_READER_H
#define EP_EXE_READER_H

#include "filesystem_stream.h"
#include "game_constants.h"
#include <cstdint>
#include <lcf/enum_tags.h>
#include <string>
#include <istream>
#include <vector>
#include "bitmap.h"
#include "player.h"

/**
* Extracts resources from an EXE.
Expand Down Expand Up @@ -65,13 +65,16 @@ class EXEReader {
MachineType machine_type = MachineType::Unknown;
bool is_easyrpg_player = false;
int maniac_patch_version = 0;
uint32_t code_ofs = 0;

int GetEngineType(int& mp_version) const;
void Print() const;
};

const FileInfo& GetFileInfo();

std::unordered_map<Game_Constants::ConstantType, int32_t> GetOverriddenGameConstants();

private:
// Bounds-checked unaligned reader primitives.
// In case of out-of-bounds, returns 0 - this will usually result in a harmless error at some other level,
Expand Down
1 change: 1 addition & 0 deletions src/font.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
#include "cache.h"
#include "player.h"
#include "compiler.h"
#include "game_clock.h"

// Static variables.
namespace {
Expand Down
41 changes: 7 additions & 34 deletions src/game_actor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@

// Headers
#include <algorithm>
#include <sstream>
#include <iterator>
#include "game_actor.h"
#include "game_battle.h"
#include "game_constants.h"
#include "game_party.h"
#include "sprite_actor.h"
#include "main_data.h"
Expand All @@ -36,47 +36,24 @@
#include "algo.h"
#include "game_message_terms.h"

constexpr int max_level_2k = 50;
constexpr int max_level_2k3 = 99;

int Game_Actor::MaxHpValue() const {
auto& val = lcf::Data::system.easyrpg_max_actor_hp;
if (val == -1) {
return Player::IsRPG2k() ? 999 : 9999;
}
return val;
return Main_Data::game_constants->MaxActorHpValue();
}

int Game_Actor::MaxSpValue() const {
auto& val = lcf::Data::system.easyrpg_max_actor_sp;
if (val == -1) {
return 999;
}
return val;
return Main_Data::game_constants->MaxActorSpValue();
}

int Game_Actor::MaxStatBattleValue() const {
auto& val = lcf::Data::system.easyrpg_max_stat_battle_value;
if (val == -1) {
return 9999;
}
return val;
return Main_Data::game_constants->MaxStatBattleValue();
}

int Game_Actor::MaxStatBaseValue() const {
auto& val = lcf::Data::system.easyrpg_max_stat_base_value;
if (val == -1) {
return 999;
}
return val;
return Main_Data::game_constants->MaxStatBaseValue();
}

int Game_Actor::MaxExpValue() const {
auto& val = lcf::Data::system.easyrpg_max_exp;
if (val == -1) {
return Player::IsRPG2k() ? 999999 : 9999999;
}
return val;
return Main_Data::game_constants->MaxExpValue();
}

Game_Actor::Game_Actor(int actor_id) {
Expand Down Expand Up @@ -745,11 +722,7 @@ int Game_Actor::GetAccessoryId() const {
}

int Game_Actor::GetMaxLevel() const {
int max_level = Player::IsRPG2k() ? max_level_2k : max_level_2k3;
if (lcf::Data::system.easyrpg_max_level > -1) {
max_level = lcf::Data::system.easyrpg_max_level;
}
return Utils::Clamp<int32_t>(max_level, 1, dbActor->final_level);
return Utils::Clamp<int32_t>(Main_Data::game_constants->MaxLevel(), 1, dbActor->final_level);
}

void Game_Actor::SetExp(int _exp) {
Expand Down
2 changes: 1 addition & 1 deletion src/game_actor.h
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ class Game_Actor final : public Game_Battler {

/**
* Sets exp of actor.
* The value is adjusted to the boundary 0 up 999999.
* The value is adjusted to the boundary 0 up to a maximum (dependent on engine type & patch).
* Other actor attributes are not altered. Use ChangeExp to do a proper
* experience change.
*
Expand Down
3 changes: 3 additions & 0 deletions src/game_actors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@

// Headers
#include "system.h"
#include <cassert>
#include <vector>
#include "game_actors.h"
#include "main_data.h"
#include "output.h"

Game_Actors::Game_Actors() {
assert(Main_Data::game_constants != nullptr && "Game Constants must be initialized");

data.reserve(lcf::Data::actors.size());
for (size_t i = 0; i < lcf::Data::actors.size(); i++) {
data.emplace_back(Game_Actor(i + 1));
Expand Down
7 changes: 2 additions & 5 deletions src/game_battlealgorithm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,20 @@
*/

#include <cassert>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <sstream>
#include "game_actor.h"
#include "game_battle.h"
#include "game_battlealgorithm.h"
#include "game_battler.h"
#include "game_constants.h"
#include "game_enemy.h"
#include "game_enemyparty.h"
#include "game_party.h"
#include "game_party_base.h"
#include "game_switches.h"
#include "game_system.h"
#include "main_data.h"
#include "game_message_terms.h"
#include "output.h"
#include "player.h"
#include <lcf/reader_util.h>
#include <lcf/rpg/animation.h>
Expand All @@ -52,7 +49,7 @@
#include "feature.h"

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

Game_BattleAlgorithm::AlgorithmBase::AlgorithmBase(Type ty, Game_Battler* source, Game_Battler* target) :
Expand Down
11 changes: 11 additions & 0 deletions src/game_config_game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,16 @@ void Game_ConfigGame::LoadFromArgs(CmdlineParser& cp) {
}
continue;
}
if (cp.ParseNext(arg, 1, "--engine-path")) {
if (arg.NumValues() > 0) {
std::string path = arg.Value(0);
path = FileFinder::MakeCanonical(path, 0);
if (!path.empty()) {
engine_path.Set(path);
}
}
continue;
}
if (cp.ParseNext(arg, 0, "--no-patch")) {
patch_support.Set(false);
patch_dynrpg.Lock(false);
Expand Down Expand Up @@ -197,6 +207,7 @@ void Game_ConfigGame::LoadFromStream(Filesystem_Stream::InputStream& is) {

new_game.FromIni(ini);
engine_str.FromIni(ini);
engine_path.FromIni(ini);
fake_resolution.FromIni(ini);

if (patch_easyrpg.FromIni(ini)) {
Expand Down
1 change: 1 addition & 0 deletions src/game_config_game.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ struct Game_ConfigGame {

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