Skip to content

Commit

Permalink
Updated OnDrawingFinished hook to not be overwritten by other mods. (#…
Browse files Browse the repository at this point in the history
…201)

* Updated OnDrawingFinished hook to not be overwritten by other mods.

* Added extra error checking.

* Replaced m_bStarted and m_bLateStarted with single variable.

* fixup! Replaced m_bStarted and m_bLateStarted with single variable.

* Switched hooked function to work with SkyGFX

* fixup! Switched hooked function to work with SkyGFX

* fixup! Switched hooked function to work with SkyGFX

* Simplified DebugDisplayTextBuffer hooking.

* fixup! Simplified DebugDisplayTextBuffer hooking.

* fixup! Simplified DebugDisplayTextBuffer hooking.

* fixup! Simplified DebugDisplayTextBuffer hooking.

* fixup! Simplified DebugDisplayTextBuffer hooking.

* Handled JMPSHORT hooking.

* fixup! Handled JMPSHORT hooking.
  • Loading branch information
MiranDMC authored Sep 21, 2024
1 parent 244207f commit ba6d942
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 62 deletions.
22 changes: 16 additions & 6 deletions source/CCodeInjector.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,27 @@ namespace CLEO
void CloseReadWriteAccess();

template<typename T>
void ReplaceFunction(T *funcPtr, memory_pointer Position, T** origFuncPtr = nullptr)
void ReplaceFunction(T *funcPtr, memory_pointer position, T** origFuncPtr = nullptr)
{
TRACE("Replacing call: 0x%08X", (DWORD)Position);
MemCall((size_t)Position, (size_t)funcPtr, (size_t*)origFuncPtr); // *whistle*
MemCall((size_t)position, (size_t)funcPtr, (size_t*)origFuncPtr);

if (origFuncPtr == nullptr) { TRACE("Replaced call at: 0x%08X", (DWORD)position); }
else { TRACE("Replaced call at: 0x%08X, original function was: 0x%08X", (DWORD)position, (DWORD)*origFuncPtr); }
}

void ReplaceJump(memory_pointer newJumpDst, memory_pointer position, memory_pointer* origJumpDest = nullptr)
{
MemJump((size_t)position, (size_t)newJumpDst, (size_t*)origJumpDest);

if (origJumpDest == nullptr) { TRACE("Replaced jump at: 0x%08X", (DWORD)position); }
else { TRACE("Replaced jump at: 0x%08X, original destination was: 0x%08X", (DWORD)position, (DWORD)origJumpDest->address); }
}

template<typename T>
void InjectFunction(T *funcPtr, memory_pointer Position)
void InjectFunction(T *funcPtr, memory_pointer position)
{
TRACE("Injecting function at: 0x%08X", (DWORD)Position);
MemJump((size_t)Position, (size_t)funcPtr);
TRACE("Injecting function at: 0x%08X", (DWORD)position);
MemJump((size_t)position, (size_t)funcPtr);
}

void Nop(memory_pointer addr, size_t size)
Expand Down
2 changes: 2 additions & 0 deletions source/CGameMenu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ namespace CLEO

void __fastcall OnDrawMenuBackground(void *texture, int dummy, RwRect2D *rect, RwRGBA *color)
{
GetInstance().Start(CCleoInstance::InitStage::OnDraw); // late initialization

CTexture_DrawInRect(texture, rect, color); // call original

CFont::SetBackground(false, false);
Expand Down
1 change: 1 addition & 0 deletions source/CGameVersionManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ namespace CLEO
{ 0x0053C758, memory_und, memory_und, memory_und, memory_und }, // MA_CALL_GAME_RESTART_1 TODO: find for other versions
{ 0x00748E04, memory_und, memory_und, memory_und, memory_und }, // MA_CALL_GAME_RESTART_2 TODO: find for other versions
{ 0x00748E3E, memory_und, memory_und, memory_und, memory_und }, // MA_CALL_GAME_RESTART_3 TODO: find for other versions
{ 0x00532260, memory_und, memory_und, memory_und, memory_und }, // MA_DEBUG_DISPLAY_TEXT_BUFFER TODO: find for other versions

// GV_US10, GV_US11, GV_EU10, GV_EU11, GV_STEAM
{ 0x008A6168, memory_und, 0x008A6168, 0x008A7450, 0x00913C20 }, // MA_OPCODE_HANDLER,
Expand Down
1 change: 1 addition & 0 deletions source/CGameVersionManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ namespace CLEO
MA_CALL_GAME_RESTART_1,
MA_CALL_GAME_RESTART_2,
MA_CALL_GAME_RESTART_3,
MA_DEBUG_DISPLAY_TEXT_BUFFER, // empty function called after everything else is drawn

// CustomOpcodeSystem
MA_OPCODE_HANDLER,
Expand Down
97 changes: 57 additions & 40 deletions source/CleoBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,6 @@ namespace CLEO
CCleoInstance CleoInstance;
CCleoInstance& GetInstance() { return CleoInstance; }

inline CCleoInstance::CCleoInstance()
{
m_bStarted = false;
}

inline CCleoInstance::~CCleoInstance()
{
Stop();
Expand Down Expand Up @@ -111,52 +106,79 @@ namespace CLEO
_asm jmp oriFunc
}

void CCleoInstance::Start()
void __declspec(naked) CCleoInstance::OnDebugDisplayTextBuffer()
{
GetInstance().CallCallbacks(eCallbackId::DrawingFinished); // execute registered callbacks
static DWORD oriFunc;
oriFunc = (DWORD)(GetInstance().DebugDisplayTextBuffer);
if (oriFunc != (DWORD)nullptr)
_asm jmp oriFunc
else
_asm ret
}

void CCleoInstance::Start(InitStage stage)
{
if (m_bStarted) return; // already started
m_bStarted = true;
if (stage > InitStage::Done) return; // invalid argument

auto nextStage = InitStage(m_initStage + 1);
if (stage != nextStage) return;

FS::create_directory(Filepath_Cleo);
FS::create_directory(Filepath_Cleo + "\\cleo_modules");
FS::create_directory(Filepath_Cleo + "\\cleo_plugins");
FS::create_directory(Filepath_Cleo + "\\cleo_saves");
if (stage == InitStage::Initial)
{
TRACE("CLEO initialization: Phase 1");

OpcodeInfoDb.Load((Filepath_Cleo + "\\.config\\sa.json").c_str());
FS::create_directory(Filepath_Cleo);
FS::create_directory(Filepath_Cleo + "\\cleo_modules");
FS::create_directory(Filepath_Cleo + "\\cleo_plugins");
FS::create_directory(Filepath_Cleo + "\\cleo_saves");

CodeInjector.OpenReadWriteAccess(); // must do this earlier to ensure plugins write access on init
GameMenu.Inject(CodeInjector);
DmaFix.Inject(CodeInjector);
OpcodeSystem.Inject(CodeInjector);
ScriptEngine.Inject(CodeInjector);
OpcodeInfoDb.Load((Filepath_Cleo + "\\.config\\sa.json").c_str());

CodeInjector.ReplaceFunction(OnCreateMainWnd, VersionManager.TranslateMemoryAddress(MA_CALL_CREATE_MAIN_WINDOW), &CreateMainWnd_Orig);
CodeInjector.OpenReadWriteAccess(); // must do this earlier to ensure plugins write access on init
GameMenu.Inject(CodeInjector);
DmaFix.Inject(CodeInjector);
OpcodeSystem.Inject(CodeInjector);
ScriptEngine.Inject(CodeInjector);

CodeInjector.ReplaceFunction(OnUpdateGameLogics, VersionManager.TranslateMemoryAddress(MA_CALL_UPDATE_GAME_LOGICS), &UpdateGameLogics);
CodeInjector.ReplaceFunction(OnCreateMainWnd, VersionManager.TranslateMemoryAddress(MA_CALL_CREATE_MAIN_WINDOW), &CreateMainWnd_Orig);

CodeInjector.ReplaceFunction(OnScmInit1, VersionManager.TranslateMemoryAddress(MA_CALL_INIT_SCM1), &ScmInit1_Orig);
CodeInjector.ReplaceFunction(OnScmInit2, VersionManager.TranslateMemoryAddress(MA_CALL_INIT_SCM2), &ScmInit2_Orig);
CodeInjector.ReplaceFunction(OnScmInit3, VersionManager.TranslateMemoryAddress(MA_CALL_INIT_SCM3), &ScmInit3_Orig);
CodeInjector.ReplaceFunction(OnUpdateGameLogics, VersionManager.TranslateMemoryAddress(MA_CALL_UPDATE_GAME_LOGICS), &UpdateGameLogics);

CodeInjector.ReplaceFunction(OnGameShutdown, VersionManager.TranslateMemoryAddress(MA_CALL_GAME_SHUTDOWN), &GameShutdown);
CodeInjector.ReplaceFunction(OnScmInit1, VersionManager.TranslateMemoryAddress(MA_CALL_INIT_SCM1), &ScmInit1_Orig);
CodeInjector.ReplaceFunction(OnScmInit2, VersionManager.TranslateMemoryAddress(MA_CALL_INIT_SCM2), &ScmInit2_Orig);
CodeInjector.ReplaceFunction(OnScmInit3, VersionManager.TranslateMemoryAddress(MA_CALL_INIT_SCM3), &ScmInit3_Orig);

CodeInjector.ReplaceFunction(OnGameRestart1, VersionManager.TranslateMemoryAddress(MA_CALL_GAME_RESTART_1), &GameRestart1);
CodeInjector.ReplaceFunction(OnGameRestart2, VersionManager.TranslateMemoryAddress(MA_CALL_GAME_RESTART_2), &GameRestart2);
CodeInjector.ReplaceFunction(OnGameRestart3, VersionManager.TranslateMemoryAddress(MA_CALL_GAME_RESTART_3), &GameRestart3);
CodeInjector.ReplaceFunction(OnGameShutdown, VersionManager.TranslateMemoryAddress(MA_CALL_GAME_SHUTDOWN), &GameShutdown);

CodeInjector.ReplaceFunction(OnDrawingFinished, 0x00734640); // nullsub_63 - originally something like renderDebugStuff?
CodeInjector.ReplaceFunction(OnGameRestart1, VersionManager.TranslateMemoryAddress(MA_CALL_GAME_RESTART_1), &GameRestart1);
CodeInjector.ReplaceFunction(OnGameRestart2, VersionManager.TranslateMemoryAddress(MA_CALL_GAME_RESTART_2), &GameRestart2);
CodeInjector.ReplaceFunction(OnGameRestart3, VersionManager.TranslateMemoryAddress(MA_CALL_GAME_RESTART_3), &GameRestart3);

OpcodeSystem.Init();
PluginSystem.LoadPlugins();
OpcodeSystem.Init();
PluginSystem.LoadPlugins();
}

// delayed until menu background drawing
if (stage == InitStage::OnDraw)
{
TRACE("CLEO initialization: Phase 2");

CodeInjector.ReplaceJump(OnDebugDisplayTextBuffer, VersionManager.TranslateMemoryAddress(MA_DEBUG_DISPLAY_TEXT_BUFFER), &DebugDisplayTextBuffer);
}

m_initStage = stage;
}

void CCleoInstance::Stop()
{
if (!m_bStarted) return;
m_bStarted = false;

ScriptEngine.GameEnd();
if (m_initStage >= InitStage::Initial)
{
ScriptEngine.GameEnd();
PluginSystem.UnloadPlugins();
}

PluginSystem.UnloadPlugins();
m_initStage = InitStage::None;
}

void CCleoInstance::GameBegin()
Expand Down Expand Up @@ -230,11 +252,6 @@ namespace CLEO
GetInstance().RemoveCallback(id, func);
}

void __cdecl CCleoInstance::OnDrawingFinished()
{
GetInstance().CallCallbacks(eCallbackId::DrawingFinished); // execute registered callbacks
}

DWORD WINAPI CLEO_GetInternalAudioStream(CLEO::CRunningScript* thread, DWORD stream) // arg CAudioStream *
{
return stream; // CAudioStream::streamInternal offset is 0
Expand Down
37 changes: 25 additions & 12 deletions source/CleoBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@ namespace CLEO
{
class CCleoInstance
{
bool m_bStarted;
bool m_bGameInProgress;
std::map<eCallbackId, std::set<void*>> m_callbacks;

public:
// order here defines init and deinit and order!
enum InitStage : size_t
{
None,
Initial,
OnDraw,
Done = OnDraw
};

// order here defines init and deinit order!
CDmaFix DmaFix;
CGameMenu GameMenu;
CCodeInjector CodeInjector;
Expand All @@ -35,16 +39,16 @@ namespace CLEO

int saveSlot = -1; // -1 if not loaded from save

CCleoInstance();
CCleoInstance() = default;
virtual ~CCleoInstance();

void Start();
void Start(InitStage stage);
void Stop();

void GameBegin();
void GameEnd();

bool IsStarted() const { return m_bStarted; }
bool IsStarted() const { return m_initStage != InitStage::None; }

void AddCallback(eCallbackId id, void* func);
void RemoveCallback(eCallbackId id, void* func);
Expand Down Expand Up @@ -75,15 +79,24 @@ namespace CLEO

// call for Game::Shutdown
void(__cdecl* GameShutdown)() = nullptr;
static void __cdecl OnGameShutdown();
static void OnGameShutdown();

// calls for Game::ShutDownForRestart
void(__cdecl* GameRestart1)() = nullptr;
void(__cdecl* GameRestart2)() = nullptr;
void(__cdecl* GameRestart3)() = nullptr;
static void __cdecl OnGameRestart1();
static void __cdecl OnGameRestart2();
static void __cdecl OnGameRestart3();
static void OnGameRestart1();
static void OnGameRestart2();
static void OnGameRestart3();

// empty function called after everything else is drawn
memory_pointer DebugDisplayTextBuffer = nullptr;
static void OnDebugDisplayTextBuffer();

private:
InitStage m_initStage = InitStage::None;
bool m_bGameInProgress;
std::map<eCallbackId, std::set<void*>> m_callbacks;
};

CCleoInstance& GetInstance();
Expand Down
30 changes: 27 additions & 3 deletions source/Mem.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,45 @@ inline void MemCopy(U p, const T* v) { memcpy((void*)p, v, sizeof(T)); }
template<typename T, typename U>
inline void MemCopy(U p, const T* v, int n) { memcpy((void*)p, v, n); }

// Write a jump to v to the address at p and copy the replaced call address to r
// Write a jump to v to the address at p and copy the replaced jump address to r
template<typename T, typename U>
inline void MemJump(U p, const T v, T *r = nullptr)
{
if (r != nullptr)
{
switch (MemRead<BYTE>(p))
{
case OP_JMP:
*r = (T)(DWORD(p) + 5 + MemRead<signed int>(p + 1));
break;

case OP_JMPSHORT:
*r = (T)(DWORD(p) + 2 + MemRead<signed char>(p + 1));
break;

default:
*r = (T)nullptr;
break;
}
}

MemWrite<BYTE>(p++, OP_JMP);
if (r) *r = (T)(MemRead<DWORD>(p) + p + 4);
MemWrite<DWORD>(p, ((DWORD)v - (DWORD)p) - 4);
}

// Write a call to v to the address at p and copy the replaced call address to r
template<typename T, typename U>
inline void MemCall(U p, const T v, T *r = nullptr)
{
if (r != nullptr)
{
if (MemRead<BYTE>(p) == OP_CALL)
*r = (T)(DWORD(p) + 5 + MemRead<signed int>(p + 1));
else
*r = (T)nullptr;
}

MemWrite<BYTE>(p++, OP_CALL);
if (r) *r = (T)(MemRead<DWORD>(p) + p + 4);
MemWrite<DWORD>(p, (DWORD)v - (DWORD)p - 4);
}

Expand Down
2 changes: 1 addition & 1 deletion source/dllmain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class Starter
" 10) gta_sa.exe, decrypted 3.0 steam executable, 5 697 536 bytes."
);

CLEO::GetInstance().Start();
CLEO::GetInstance().Start(CLEO::CCleoInstance::InitStage::Initial);
}

~Starter()
Expand Down

0 comments on commit ba6d942

Please sign in to comment.