Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Destiny Patch support #3131

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 160 additions & 0 deletions src/destiny.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/*
Copy link
Member

@Ghabry Ghabry Oct 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imo this is a good start. Just a little nit-pick:

Can you rename it to Game_Destiny similiar to how Ineluki patch is implemented (which is Game_Ineluki).

Then make it a class and put the instance variable in main_data.h/cpp and instantiate it in ResetGameObjects.

Also please update Makefile.am and CMakeLists.txt by adding the new files to it (put them at the correct location in alphabetical order).


Yes I know that DynRPG does it in a different way but I plan to rewrite this as less global state makes the cleanup easier when switching the game.

Copy link
Author

@drxgb drxgb Oct 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, no problem. I thought that I must create a singleton, but if already exists a container so it's much better.
I'll make this fixes. =D
Thanks for the tips.

* 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 <http://www.gnu.org/licenses/>.
*/

// Headers
#include "destiny.h"

#include <vector>

#ifndef EMSCRIPTEN
#include "exe_reader.h"
#endif // !EMSCRIPTEN
#include "output.h"


using lcf::ToString;
using lcf::rpg::EventCommand;


// Definitions
using destiny_dword_list = std::vector<int>;
using destiny_float_list = std::vector<double>;
using destiny_string_list = std::vector<std::string>;


namespace Destiny {
// Destiny containers
static destiny_dword_list dwords;
static destiny_float_list floats;
static destiny_string_list strings;

// Destiny data
static Version dllVersion;
static Version gameVersion;
static Language language;
static uint32_t extra;

// Settings
static bool decimalComma;
}


// Implementations
void Destiny::Load()
{
// Do not load Destiny whether player cannot find "Destiny.dll"
if (!FileFinder::Game().Exists(DESTINY_DLL))
return;

uint32_t dllVersion = 0;
uint32_t language = 0;
uint32_t gameVersion = 0;
uint32_t extra = 0;
uint32_t dwordSize = 0;
uint32_t floatSize = 0;
uint32_t stringSize = 0;

#ifndef EMSCRIPTEN
Filesystem_Stream::InputStream exe = FileFinder::Game().OpenFile(EXE_NAME);

if (exe) {
exe.seekg(0x00030689, std::ios_base::beg);
exe.read(reinterpret_cast<char*>(&stringSize), sizeof(uint32_t));
exe.seekg(1, std::ios_base::cur);
exe.read(reinterpret_cast<char*>(&floatSize), sizeof(uint32_t));
exe.seekg(1, std::ios_base::cur);
exe.read(reinterpret_cast<char*>(&dwordSize), sizeof(uint32_t));
exe.seekg(1, std::ios_base::cur);
exe.read(reinterpret_cast<char*>(&extra), sizeof(uint32_t));
exe.seekg(1, std::ios_base::cur);
exe.read(reinterpret_cast<char*>(&gameVersion), sizeof(uint32_t));
exe.seekg(1, std::ios_base::cur);
exe.read(reinterpret_cast<char*>(&language), sizeof(uint32_t));
exe.seekg(1, std::ios_base::cur);
exe.read(reinterpret_cast<char*>(&dllVersion), sizeof(uint32_t));
exe.seekg(1, std::ios_base::cur);
}
#else
// TODO [Dr.XGB]: Find to manage Destiny initialization parameters on Emscripten
dllVersion = 0x20000;
language = ENGLISH;
gameVersion = 0x20000107;
extra = 0x01;
dwordSize = floatSize = stringSize = 0x64;
#endif // !EMSCRIPTEN

Initialize(dllVersion, language, gameVersion, extra, dwordSize, floatSize, stringSize);
}

void Destiny::Initialize(uint32_t _dllVersion, uint32_t _language, uint32_t _gameVersion, uint32_t _extra, uint32_t _dwordSize, uint32_t _floatSize, uint32_t _stringSize)
{
dllVersion = Version(_dllVersion);
gameVersion = Version(_gameVersion);
language = static_cast<Language>(_language);
extra = _extra;
decimalComma = language == ENGLISH;

// Init containers
dwords.resize(_dwordSize);
floats.resize(_floatSize);
strings.resize(_stringSize);

// TODO: Init File container
// TODO: Init ClientSocket container

// Debug
Output::Debug("Destiny Initialized");
Output::Debug("Language: {}", decimalComma ? "English" : "Deutsch");
Output::Debug("DLL Version: {}", dllVersion.toString());
Output::Debug("Dwords: {}", _dwordSize);
Output::Debug("Floats: {}", _floatSize);
Output::Debug("Strings: {}", _stringSize);
}

void Destiny::Terminate()
{
dwords.clear();
floats.clear();
strings.clear();

// TODO: Clear File container
// TODO: Clear ClientSocket container
}

std::string Destiny::MakeString(lcf::rpg::SaveEventExecFrame& scriptData)
{
std::string code;

int32_t& current = scriptData.current_command;
const std::vector<EventCommand>& cmdList = scriptData.commands;
std::vector<EventCommand>::const_iterator it = cmdList.begin() + current++;

code = ToString((*it++).string);
while (it != cmdList.cend() && it->code == static_cast<int32_t>(EventCommand::Code::Comment_2)) {
code += '\n';
code += ToString((*it++).string);
++current;
}

return code;
}

bool Destiny::Interpret(const std::string& code)
{
// TODO [Dr.XGB]: DestinyScript Interpret
return true;
}
99 changes: 99 additions & 0 deletions src/destiny.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/

#ifndef EP_DESTINY_H
#define EP_DESTINY_H

#include <cstdint>
#include <sstream>

#include "lcf/rpg/saveeventexecframe.h"


namespace Destiny {
// Constants
constexpr const char* DESTINY_DLL = "Destiny.dll";


// Enums
enum Language {
DEUTSCH = 0,
ENGLISH,
};


// Structs
struct Version {
uint16_t major;
uint16_t minor;

Version()
: major(0), minor(0) {}
Version(uint32_t version) {
major = version >> 0x10;
minor = version & 0xFFFF;
}

std::string toString() {
std::stringstream ss;

ss << major << '.' << minor;
return ss.str();
}
};


// Functions
/**
* Load the Destiny module
*/
void Load();

/**
* Initialize and apply the patch to the game interpreter
*/
void Initialize(
uint32_t _dllVersion,
uint32_t _language,
uint32_t _gameVersion,
uint32_t _extra,
uint32_t _dwordSize,
uint32_t _floatSize,
uint32_t _stringSize
);

/**
* Clear Destiny patch before close
*/
void Terminate();


// Interpret functions

/*
* Make the DestinyScript code exctracting from the event script's comment command
*/
std::string MakeString(lcf::rpg::SaveEventExecFrame& scriptData);


/*
* Evaluate DestinyScript code
*/
bool Interpret(const std::string& code);

}
#endif // !EP_DESTINY_H
1 change: 1 addition & 0 deletions src/game_config_game.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,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() };
BoolConfigParam fake_resolution{ "Fake Metrics", "Makes games run on higher resolutions (with some success)", "Game", "FakeResolution", false };
BoolConfigParam patch_destiny{ "Destiny Patch", "", "Patch", "Destiny", false };
BoolConfigParam patch_dynrpg{ "DynRPG", "", "Patch", "DynRPG", false };
BoolConfigParam patch_maniac{ "Maniac Patch", "", "Patch", "Maniac", false };
BoolConfigParam patch_common_this_event{ "Common This Event", "Support \"This Event\" in Common Events", "Patch", "CommonThisEvent", false };
Expand Down
16 changes: 16 additions & 0 deletions src/game_interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <cassert>
#include "game_interpreter.h"
#include "audio.h"
#include "destiny.h"
#include "dynrpg.h"
#include "filefinder.h"
#include "game_map.h"
Expand Down Expand Up @@ -2093,6 +2094,7 @@ bool Game_Interpreter::CommandEndEventProcessing(lcf::rpg::EventCommand const& /
}

bool Game_Interpreter::CommandComment(const lcf::rpg::EventCommand &com) {
// DynRpg command
if (Player::IsPatchDynRpg()) {
if (com.string.empty() || com.string[0] != '@') {
// Not a DynRPG command
Expand All @@ -2117,6 +2119,20 @@ bool Game_Interpreter::CommandComment(const lcf::rpg::EventCommand &com) {

return DynRpg::Invoke(command);
}


// DestinyScript
if (Player::IsPatchDestiny()) {
if (com.string.empty() || com.string[0] != '$') {
// Not a DestinyScript
return true;
}

const std::string& code = Destiny::MakeString(GetFrame());
Output::Debug("DestinyScript Code:\n{}", code);
return Destiny::Interpret(code);
}

return true;
}

Expand Down
13 changes: 11 additions & 2 deletions src/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "cache.h"
#include "rand.h"
#include "cmdline_parser.h"
#include "destiny.h"
#include "dynrpg.h"
#include "filefinder.h"
#include "filefinder_rtp.h"
Expand Down Expand Up @@ -832,10 +833,14 @@ void Player::CreateGameObjects() {
if (!FileFinder::Game().FindFile("accord.dll").empty()) {
game_config.patch_maniac.Set(true);
}

if (!FileFinder::Game().FindFile(Destiny::DESTINY_DLL).empty()) {
game_config.patch_destiny.Set(true);
}
}

Output::Debug("Patch configuration: dynrpg={} maniac={} key-patch={} common-this={} pic-unlock={} 2k3-commands={}",
Player::IsPatchDynRpg(), Player::IsPatchManiac(), Player::IsPatchKeyPatch(), game_config.patch_common_this_event.Get(), game_config.patch_unlock_pics.Get(), game_config.patch_rpg2k3_commands.Get());
Output::Debug("Patch configuration: dynrpg={} maniac={} key-patch={} common-this={} pic-unlock={} 2k3-commands={} destiny={}",
Player::IsPatchDynRpg(), Player::IsPatchManiac(), Player::IsPatchKeyPatch(), game_config.patch_common_this_event.Get(), game_config.patch_unlock_pics.Get(), game_config.patch_rpg2k3_commands.Get(), Player::IsPatchDestiny());

ResetGameObjects();

Expand All @@ -844,6 +849,10 @@ void Player::CreateGameObjects() {
if (Player::IsPatchKeyPatch()) {
Main_Data::game_ineluki->ExecuteScriptList(FileFinder::Game().FindFile("autorun.script"));
}

if (Player::IsPatchDestiny()) {
Destiny::Load();
}
}

bool Player::ChangeResolution(int width, int height) {
Expand Down
9 changes: 9 additions & 0 deletions src/player.h
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,11 @@ namespace Player {
*/
bool IsPatchKeyPatch();

/**
* @return True when Destiny Patch is active
*/
bool IsPatchDestiny();

/**
* @return Running engine version. 2000 for RPG2k and 2003 for RPG2k3
*/
Expand Down Expand Up @@ -480,4 +485,8 @@ inline bool Player::IsPatchKeyPatch() {
return game_config.patch_key_patch.Get();
}

inline bool Player::IsPatchDestiny() {
return game_config.patch_destiny.Get();
}

#endif