diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e4c6b0..96c7fdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,4 +40,10 @@ Update records: - 经测试能够正常工作。 - Initial organization of RawFileClass_Impl completed. -- Tested and confirmed to work correctly. \ No newline at end of file +- Tested and confirmed to work correctly. + +## Debug_Impl +#### 2025-10-17 IronHammer_Std +- 恢复了调试日志功能。 + +- Restored the debug logging functionality. \ No newline at end of file diff --git a/Patch/Debug.h b/Patch/Debug.h index 2d8b3c2..9ff7dd2 100644 --- a/Patch/Debug.h +++ b/Patch/Debug.h @@ -2,6 +2,7 @@ #include #include #include +#include namespace Debug { @@ -14,27 +15,30 @@ namespace Debug void LogAndMessageString(const char* pStr); void LogAndMessageString(const std::string& Str); - void LogFormat(const std::string& fmt, auto&&... args) { - std::string msg = std::format(fmt, std::forward(args)...); + template + inline void LogFormat(const std::format_string<_Types...> fmt, _Types&&... args) { + std::string msg = std::vformat(fmt.get(), std::make_format_args(args...)); LogString(msg); } - void LogVFormat(const std::string& fmt, std::format_args args) { + inline void LogVFormat(std::string_view fmt, std::format_args args) { std::string msg = std::vformat(fmt, args); LogString(msg); } - void MessageFormat(const std::string& fmt, auto&&... args) { - std::string msg = std::format(fmt, std::forward(args)...); + template + inline void MessageFormat(const std::format_string<_Types...> fmt, _Types&&... args) { + std::string msg = std::vformat(fmt.get(), std::make_format_args(args...)); MessageString(msg); } - void MessageVFormat(const std::string& fmt, std::format_args args) { + inline void MessageVFormat(const std::string& fmt, std::format_args args) { std::string msg = std::vformat(fmt, args); MessageString(msg); } - void LogFormatAndMessage(const std::string& fmt, auto&&... args) { - std::string msg = std::format(fmt, std::forward(args)...); + template + inline void LogFormatAndMessage(const std::format_string<_Types...> fmt, _Types&&... args) { + std::string msg = std::vformat(fmt.get(), std::make_format_args(args...)); LogAndMessageString(msg); } - void LogVFormatAndMessage(const std::string& fmt, std::format_args args) { + inline void LogVFormatAndMessage(const std::string& fmt, std::format_args args) { std::string msg = std::vformat(fmt, args); LogAndMessageString(msg); } diff --git a/Patch/DynamicPatch.cpp b/Patch/DynamicPatch.cpp index 4578dd5..9e2600b 100644 --- a/Patch/DynamicPatch.cpp +++ b/Patch/DynamicPatch.cpp @@ -1,10 +1,10 @@ -#include "DynamicPatch.h" +#include "DynamicPatch.h" #include #include #pragma comment(lib, "imagehlp.lib") -// ɨ蹤 +// 节区扫描工具 bool LocateExecutableSection(const char* sectionName, SectionInfo* result) { HMODULE moduleBase = GetModuleHandle(nullptr); @@ -44,7 +44,7 @@ bool LocateExecutableSection(const char* sectionName, SectionInfo* result) return false; } -// ִд洢IJ +// 执行所有存储的补丁 void CodeModifier::ExecuteAllStored() { SectionInfo section; @@ -59,22 +59,22 @@ void CodeModifier::ExecuteAllStored() { const CodeModifier* modifier = reinterpret_cast(current); - // ֹƫ + // 终止条件:空偏移量 if (modifier->m_targetOffset == 0) { break; } - // ֤Ч + // 验证数据有效性 if (modifier->m_dataSize > 0 && modifier->m_patchData != nullptr) { modifier->Execute(); } - // ƶһ + // 移动到下一个补丁 current += sizeof(CodeModifier); } } -// ת͵òʵ +// 跳转和调用补丁实现 void CodeModifier::InsertFarJump(DWORD offset, DWORD target) { FarJumpInstruction instruction(offset, target); diff --git a/Patch/DynamicPatch.h b/Patch/DynamicPatch.h index 0627ea0..6b883ff 100644 --- a/Patch/DynamicPatch.h +++ b/Patch/DynamicPatch.h @@ -1,19 +1,19 @@ -#pragma once +#pragma once #include #include #include #include -// ʹøصĽƣͻ +// 使用更独特的节区名称,避免冲突 #define CODE_PATCH_SECTION ".codepatch" #pragma section(CODE_PATCH_SECTION, read, execute) -// ָṹ +// 指令结构体 #pragma pack(push, 1) struct FarJumpInstruction { BYTE opcode; // 0xE9 for near jump, 0xEA for far jump - DWORD offset; // תƫ + DWORD offset; // 跳转偏移量 FarJumpInstruction(DWORD from, DWORD to) { @@ -25,7 +25,7 @@ struct FarJumpInstruction struct NearCallInstruction { BYTE opcode; // 0xE8 - DWORD offset; // ƫ + DWORD offset; // 调用偏移量 NearCallInstruction(DWORD from, DWORD to) { @@ -36,18 +36,18 @@ struct NearCallInstruction struct ExtendedCallInstruction { - WORD prefix; // ǰ׺ - DWORD offset; // ƫ + WORD prefix; // 特殊调用前缀 + DWORD offset; // 调用偏移量 ExtendedCallInstruction(DWORD from, DWORD to) { - prefix = 0x15FF; // FF15 + prefix = 0x15FF; // FF15 操作码 offset = to; } }; #pragma pack(pop) -// ڴ汣װ +// 内存保护封装类 class MemoryGuard { private: @@ -73,19 +73,19 @@ class MemoryGuard } } - // ø + // 禁用复制 MemoryGuard(const MemoryGuard&) = delete; MemoryGuard& operator=(const MemoryGuard&) = delete; }; -// Ϣṹ +// 节区信息结构 struct SectionInfo { void* virtualAddress; size_t virtualSize; }; -// +// 主补丁类 #pragma pack(push, 1) class CodeModifier { @@ -95,14 +95,14 @@ class CodeModifier const BYTE* m_patchData; public: - // ĬϹ캯 + // 默认构造函数 CodeModifier() : m_targetOffset(0), m_dataSize(0), m_patchData(nullptr) {} - // 캯 + // 参数化构造函数 CodeModifier(DWORD offset, DWORD size, const BYTE* data) : m_targetOffset(offset), m_dataSize(size), m_patchData(data) {} - // Ӧòڴ + // 应用补丁到内存 void Execute() const { if (m_targetOffset == 0 || m_dataSize == 0 || m_patchData == nullptr) { @@ -111,17 +111,17 @@ class CodeModifier void* targetAddress = reinterpret_cast(m_targetOffset); - // ʹRAIIģʽڴ汣 + // 使用RAII模式管理内存保护 MemoryGuard guard(targetAddress, m_dataSize); - // ȫظ + // 安全地复制数据 std::memcpy(targetAddress, m_patchData, m_dataSize); } - // ̬ + // 静态方法集 static void ExecuteAllStored(); - // ͻݲ + // 类型化数据补丁 template static void ModifyWithData(DWORD offset, std::initializer_list dataPattern) { @@ -133,7 +133,7 @@ class CodeModifier modifier.Execute(); } - // ԭʼֽڲ汾 + // 原始字节补丁(数组版本) template static void ModifyRawBytes(DWORD offset, const BYTE(&byteArray)[ArraySize]) { @@ -141,41 +141,41 @@ class CodeModifier modifier.Execute(); } - // ԭʼֽڲʼб汾 + // 原始字节补丁(初始化列表版本) static void ModifyRawBytes(DWORD offset, std::initializer_list byteData) { ModifyWithData(offset, byteData); } - // ԭʼֽڲָ+Ȱ汾 + // 原始字节补丁(指针+长度版本) static void ModifyRawBytes(DWORD offset, const BYTE* dataPtr, size_t dataLength) { CodeModifier modifier(offset, static_cast(dataLength), dataPtr); modifier.Execute(); } - // Զת + // 远跳转补丁 static void InsertFarJump(DWORD offset, DWORD jumpTarget); static void InsertFarJump(DWORD offset, void* jumpTarget) { InsertFarJump(offset, reinterpret_cast(jumpTarget)); } - // ò + // 近调用补丁 static void InsertNearCall(DWORD offset, DWORD callTarget); static void InsertNearCall(DWORD offset, void* callTarget) { InsertNearCall(offset, reinterpret_cast(callTarget)); } - // 6ֽڵòʽ + // 6字节调用补丁(特殊格式) static void InsertExtendedCall(DWORD offset, DWORD callTarget); static void InsertExtendedCall(DWORD offset, void* callTarget) { InsertExtendedCall(offset, reinterpret_cast(callTarget)); } - // 麯޲ + // 虚函数表修补 static void UpdateVTableEntry(DWORD vtableOffset, DWORD newFunction); static void UpdateVTableEntry(DWORD vtableOffset, void* newFunction) { @@ -200,7 +200,7 @@ class CodeModifier UpdateVTableEntry(vtableOffset, GetMemberFunctionAddress(memberFunc)); } - // ƫ޲ + // 偏移量修补(别名) static void ModifyPointer(DWORD offset, DWORD newValue) { UpdateVTableEntry(offset, newValue); @@ -212,5 +212,5 @@ class CodeModifier }; #pragma pack(pop) -// ɨ躯 +// 节区扫描函数声明 bool LocateExecutableSection(const char* sectionName, SectionInfo* result); \ No newline at end of file diff --git a/README.md b/README.md index ff0b903..55b1ed0 100644 --- a/README.md +++ b/README.md @@ -104,12 +104,13 @@ The content of this project is open source and follows the MIT license. 不考虑提供任何的新增或增强逻辑。 允许也欢迎添加与调试等相关的辅助功能。 +所有源文件和说明文件应统一编码为 UTF8 带BOM格式。 + 所有人注意不要直接提交到main分支。 可以申请加入,并在dev分支上直接开发与提交; 也可以fork本项目,并在自己的分支当中添加内容。 以上两种方式都应通过Pull Request的方式合并到main分支。 - Please note that this project does not aim to fully reverse engineer YR. Instead, it focuses on documenting critical functions and implementations. If you are interested, you can try to supplement more functions. @@ -123,6 +124,9 @@ Ares/Phobos, etc. hooked and what changes they made. No new or enhanced logic is considered. Adding auxiliary features related to debugging and similar tasks is allowed and encouraged. +All source files and documentation files +should be uniformly encoded in **UTF-8 with BOM** format. + Everyone, please avoid committing directly to the main branch. You can apply to join and develop and commit directly on the dev branch; or you can fork this project and add content in your own branch. diff --git a/YRDict.vcxproj b/YRDict.vcxproj index 908aa77..3ab5a85 100644 --- a/YRDict.vcxproj +++ b/YRDict.vcxproj @@ -89,9 +89,14 @@ + + + + + @@ -230,6 +235,7 @@ + @@ -364,6 +370,7 @@ + @@ -399,6 +406,7 @@ + @@ -424,10 +432,13 @@ + + + diff --git a/YRpp b/YRpp index ccf1cad..119136e 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit ccf1cad9f505f86b4bf36bf49ac744cc6c1500d9 +Subproject commit 119136ea15638eb8f6fcc96fe67330b94029679a diff --git a/dllmain.cpp b/dllmain.cpp index 060fd8c..768d9cc 100644 --- a/dllmain.cpp +++ b/dllmain.cpp @@ -1,12 +1,36 @@  #define WIN32_LEAN_AND_MEAN #include +#include "src/InitBase.h" + BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { + if (ul_reason_for_call == DLL_PROCESS_ATTACH) + { + /* + + NOTE: + Call Sequence: + + CTOR of global/static objects in YRDict.dll + DllMain(DLL_PROCESS_ATTACH) of YRDict.dll + EntryPoint of GameMD.exe + InitBeforeEveryGameMDFunction + CTOR of global/static objects in GameMD.exe + WinMain of GameMD.exe + ... + Exit of GameMD.exe + DTOR of global/static objects in GameMD.exe + UninitAfterEveryGameMDFunction + ExitProcess + + */ + InitBeforeEveryGameMDFunction_Register(); + } return TRUE; } diff --git a/src/CopyProtection.cpp b/src/CopyProtection.cpp index 9c6268b..b974f04 100644 --- a/src/CopyProtection.cpp +++ b/src/CopyProtection.cpp @@ -1,6 +1,6 @@ #include "Syringe.h" -DEFINE_HOOK(0x4A80D0, CD_AlwaysFindYR, 6) +DEFINE_HOOK(0xc, CD_AlwaysFindYR, 6) { R->EAX(2); return 0x4A8265; @@ -18,26 +18,42 @@ DEFINE_HOOK(0x479110, CD_NeverAsk, 5) return 0x4791EA; } +/* +Implemented in Launch_Impl.cpp + DEFINE_HOOK(0x49F5C0, CopyProtection_IsLauncherRunning, 8) { R->AL(1); return 0x49F61A; } +*/ + +/* +Implemented in Launch_Impl.cpp + DEFINE_HOOK(0x49F620, CopyProtection_NotifyLauncher, 5) { R->AL(1); return 0x49F733; } +*/ + +/* +Implemented in Launch_Impl.cpp + DEFINE_HOOK(0x49F7A0, CopyProtection_CheckProtectedData, 8) { R->AL(1); return 0x49F8A7; } +*/ + // this douchebag blows your base up when it thinks you're cheating DEFINE_HOOK(0x55CFDF, BlowMeUp, 0) { return 0x55D059; } + diff --git a/src/Debug_Impl.cpp b/src/Debug_Impl.cpp new file mode 100644 index 0000000..a413c65 --- /dev/null +++ b/src/Debug_Impl.cpp @@ -0,0 +1,129 @@ +#include "Debug_Impl.h" +#include "VanillaImpl.h" +#include +#include + +namespace Debug_Impl +{ + FILE* LogFile = nullptr; + FILE* TempLogFile = nullptr; + std::filesystem::path LogFilePath{}; + std::filesystem::path TempLogFilePath{}; + + bool IsLogFileOpen() + { + return LogFile != nullptr; + } + + void MakeLogFile() + { + if constexpr (!UseVanillaImpl_Supplementary) + { + static bool made = 0; + if (!made) { + wchar_t path[MAX_PATH]; + + SYSTEMTIME time; + + GetLocalTime(&time); + GetCurrentDirectoryW(MAX_PATH, path); + + LogFilePath = path; + LogFilePath /= L"Debug"; + + CreateDirectoryW(LogFilePath.c_str(), nullptr); + + wchar_t subpath[64]; + swprintf(subpath, 64, L"debug.%04u%02u%02u-%02u%02u%02u.log", + time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond); + + TempLogFilePath = LogFilePath / L"debug.log"; + LogFilePath /= subpath; + + made = 1; + } + } + } + + void OpenLogFile() + { + if constexpr (!UseVanillaImpl_Supplementary) + { + + MakeLogFile(); + CloseLogFile(nullptr); + + LogFile = _wfsopen(LogFilePath.c_str(), L"w", _SH_DENYWR); + if (!LogFile) { + wchar_t msg[100] = L"\0"; + LogFile = nullptr; + wsprintfW(msg, L"Log file failed to open. Error code = %X(%hs)", errno, strerror(errno)); + MessageBoxW(Game::hWnd, LogFilePath.c_str(), msg, MB_OK | MB_ICONEXCLAMATION); + ExitProcess(1); + } + TempLogFile = _wfsopen(TempLogFilePath.c_str(), L"w", _SH_DENYWR); + if (!TempLogFile) { + wchar_t msg[100] = L"\0"; + TempLogFile = nullptr; + wsprintfW(msg, L"Temp log file failed to open. Error code = %X(%hs)", errno, strerror(errno)); + MessageBoxW(Game::hWnd, TempLogFilePath.c_str(), msg, MB_OK | MB_ICONEXCLAMATION); + ExitProcess(1); + } + } + } + + void CloseLogFile(const char* CloseState) + { + if constexpr (!UseVanillaImpl_Supplementary) + { + if (IsLogFileOpen()) + { + if (CloseState != nullptr) + Debug::LogString(CloseState); + else + Debug::LogString("YRDict : Closing Log File...\n"); + fclose(LogFile); + fclose(TempLogFile); + LogFile = nullptr; + TempLogFile = nullptr; + } + } + } + + void FlushLogFile() + { + if constexpr (!UseVanillaImpl_Supplementary) + { + if (IsLogFileOpen()) + { + fflush(LogFile); + fflush(TempLogFile); + } + } + } + + void LogWithVArgsUnflushed(const char* pFormat, va_list args) + { + if constexpr (!UseVanillaImpl_Supplementary) + { + vfprintf(LogFile, pFormat, args); + vfprintf(TempLogFile, pFormat, args); + } + } + + void __cdecl Log(const char* pFormat, ...) + { + if constexpr (!UseVanillaImpl_Supplementary) + { + if (!IsLogFileOpen()) + OpenLogFile(); + + va_list args; + va_start(args, pFormat); + LogWithVArgsUnflushed(pFormat, args); + va_end(args); + + FlushLogFile(); + } + } +} \ No newline at end of file diff --git a/src/Debug_Impl.h b/src/Debug_Impl.h new file mode 100644 index 0000000..6577de2 --- /dev/null +++ b/src/Debug_Impl.h @@ -0,0 +1,25 @@ +#pragma once +#include +#include +#include "ImplBase.h" +#include "VanillaImpl.h" + +namespace Debug_Impl +{ + extern FILE* LogFile; + extern FILE* TempLogFile; + extern std::filesystem::path LogFilePath; + extern std::filesystem::path TempLogFilePath; + + bool IsLogFileOpen(); + void OpenLogFile(); + + //nullptr if no CloseState is needed + void CloseLogFile(const char* CloseState); + void FlushLogFile(); + void LogWithVArgsUnflushed(const char* pFormat, va_list args); + + void __cdecl Log(const char* pFormat, ...); + FUNCTION_IMPLEMENT(0x4A4AC0, Log); + FUNCTION_IMPLEMENT(0x4068E0, Log); +} \ No newline at end of file diff --git a/src/ImplBase.h b/src/ImplBase.h index 5417680..fd335f0 100644 --- a/src/ImplBase.h +++ b/src/ImplBase.h @@ -3,6 +3,14 @@ #include #include +/* + +实现类的定义。 +所有实现类都不可构造。 +Impl Base Classes Definition. +All Impl Classes are Non-constructible. + +*/ class NonConstructible { public: NonConstructible() = delete; @@ -43,7 +51,6 @@ class NOVTABLE ImplBase : NonConstructible return reinterpret_cast(this); } }; -#define This (this->_This()) //For Virtual Classes using vtable_t = size_t; @@ -70,12 +77,35 @@ class NOVTABLE VirtualImplBase : public ImplBase } }; +/* + +在实现类的成员函数当中,使用This宏访问实际类型。 +For Member Functions in Impl Classes, +Use MACRO This to have access to the actual class. + +*/ +#define This (this->_This()) + +/* + +重置虚表指针到T类型的虚表。 +Reset VTable pointer to VTable of type T. + +*/ template void reset_vtable(V* ptr) noexcept { reinterpret_cast(ptr)->reset_vtable(); } +/* + +转换成员函数等指针到void*等 +不受任何约束限制。 +Cast Member Function Pointers to void* etc. +This function is unrestricted. + +*/ template dst_type constexpr union_cast(src_type src) { @@ -87,6 +117,38 @@ dst_type constexpr union_cast(src_type src) return u.d; } +/* +辅助类型,不直接使用 +Helper Class, NO Direct Usage. +*/ +struct FunctionImplHelper +{ + FunctionImplHelper() = delete; + FunctionImplHelper(size_t Addr, void* Func) + { + CodeModifier::InsertFarJump(Addr, Func); + } + ~FunctionImplHelper() = default; +}; + +/* +辅助类型,不直接使用 +Helper Class, NO Direct Usage. +*/ +struct FunctionAuxHelper +{ + FunctionAuxHelper() = delete; + FunctionAuxHelper(size_t CallAddr, void* AuxFunc) + { + CodeModifier::InsertNearCall(CallAddr, AuxFunc); + } + ~FunctionAuxHelper() = default; +}; + +/* +辅助宏,不直接使用 +Helper Macro, NO Direct Usage. +*/ #define IMPL_TYPE_DEFINE(_ClassName) \ namespace _ClassName##_Impl_Namespace { \ inline struct _ClassName##_Implement_Caller { \ @@ -96,21 +158,106 @@ namespace _ClassName##_Impl_Namespace { \ } _ClassName##_implement_caller; \ } +/* + +定义实现类的实现替换函数。 +Defining Implementation Replacing Functions. + +示例/Example: + +(in class definition) +DEFINE_IMPLEMENT +{ + IMPLEMENT(Addr1, Method1) + ... +} + +详见示例 FileClass_Impl.h +See details in FileClass_Impl.h + +*/ #define IMPLEMENT(Addr, Method) \ CodeModifier::InsertFarJump(Addr, union_cast(&Method)); #define DEFINE_IMPLEMENT \ inline static void Implement() + +/* + +定义实现类的开头与结尾。 +Defining Implementation Class Begin/End. + +用法/Usage: + +T是被实现的类。 +T is the class to be implemented. + +非虚类/Non-Virtual: + +DEFINE_IMPL(T) +{ + ... +}; +DEFINE_IMPL_END(T) + +非虚类/Virtual: + +DEFINE_VIRTUAL_IMPL(T, VTableAddress) +{ + ... +}; +DEFINE_IMPL_END(T) + +详见示例 FileClass_Impl.h +See details in FileClass_Impl.h + +*/ #define DEFINE_VIRTUAL_IMPL(_ClassName, _VTable) \ class NOVTABLE _ClassName ## _Impl : public VirtualImplBase<_ClassName, _VTable>, public _ClassName ## _Data - #define DEFINE_IMPL(_ClassName) \ class NOVTABLE _ClassName ## _Impl : public ImplBase<_ClassName> - #define DEFINE_IMPL_END(_ClassName) \ IMPL_TYPE_DEFINE(_ClassName ## _Impl) \ static_assert(sizeof(_ClassName ## _Impl) == sizeof(_ClassName), #_ClassName " is different from its Impl in size!"); +/* + +为实现类配置成员变量。 +Defining Member Variables for Impl Classes. + +用法/Usage: + +若被实现的类是T,其基类为B,则放置下列代码于 +实现类定义的DEFINE_IMPL/DEFINE_VIRTUAL_IMPL前面: +If the class being implemented is T, whose base class is B, +it is advised to write the following code before +DEFINE_IMPL/DEFINE_VIRTUAL_IMPL of Impl Classes. + +DEFINE_DATA_TYPE(T) : BASE_DATA(B) +{ + ... +}; + +详见示例 FileClass_Impl.h +See details in FileClass_Impl.h + +*/ +#define DEFINE_DATA_TYPE(_ClassName) \ + struct _ClassName ## _Data +#define BASE_DATA(_Type) \ + public _Type ## _Data + +/* + +自动生成静态与动态析构函数。 +要求需要类似析构函数的"void DTOR();"函数 +Auto Generate Static & Dynamic Destructors. +Require Destructor-like function "void DTOR();" + +详见示例 FileClass_Impl.h +See details in FileClass_Impl.h + +*/ #define MAKE_DTOR \ void StaticDTOR() { this->DTOR(); }\ real_type* DynamicDTOR(bool _Delete) \ @@ -120,10 +267,63 @@ namespace _ClassName##_Impl_Namespace { \ return _This(); \ } + +/* + +调用基类的析构函数,用在继承链上逐个析构。 +Calling base class's destructor. +For destroying on the inheritance chain. + +若被实现的类的基类是B,则在实现类的DTOR()中可以使用BASE_CLASS_DTOR(T) +if B is the base of the class being implemented, +BASE_CLASS_DTOR(T) is available in DTOR() of the impl class. + +详见示例 FileClass_Impl.h +See details in FileClass_Impl.h + +*/ #define BASE_CLASS_DTOR(_Type) \ (DestroyBase<_Type ## _Impl>()) -#define DEFINE_DATA_TYPE(_ClassName) \ - struct _ClassName ## _Data -#define BASE_DATA(_Type) \ - public _Type ## _Data \ No newline at end of file +/* + +为普通函数生成实现补丁,放在函数的实现后面。 +Generate Implementation Patch for Non-member Functions. +For use after the function's implementation. + +详见示例 Launch_Impl.h +See details in Launch_Impl.h + +*/ +#define FUNCTION_IMPLEMENT(Addr, Func) \ + inline FunctionImplHelper _ImplHelper_ ## Addr ## Func {Addr, union_cast(Func)}; + +/* + +为尚未实现的函数生成跳转到原函数的补丁,放在函数的声明后面。 +Generate Jump-to-Original Patch for Not-Yet-Implemented Functions. +For use after the function's declaration. + +详见示例 Launch_Impl.h +See details in Launch_Impl.h + +*/ +#define FUNCTION_NOT_YET_IMPLEMENTED(Addr) \ + { JMP_STD((Addr)) } + + +/* + +为特定位置的函数调用生成调用替换补丁,放在函数的实现后面。 +用于替换特定位置调用的函数,要求新的函数与原函数有相同的调用约定与参数。 +Generate Auxillary Call Replacement Patch for Specific Call Sites. +For use after the function's implementation. +Used to replace function calls at specific locations, +requiring the new function to have the same calling convention and parameters as the original. + +详见示例 WinMain_Impl.h +See details in WinMain_Impl.h + +*/ +#define FUNCTION_CALL_AUX(CallAddr, AuxFunc) \ + inline FunctionAuxHelper _AuxHelper_ ## CallAddr ## AuxFunc {CallAddr, union_cast(AuxFunc)}; \ No newline at end of file diff --git a/src/InitBase.h b/src/InitBase.h new file mode 100644 index 0000000..f32514d --- /dev/null +++ b/src/InitBase.h @@ -0,0 +1,79 @@ +#pragma once +#include +#include +#include + +/* +辅助变量,不直接使用 +Helper Variable, NO Direct Usage. +*/ +inline std::vector __InitBeforeEveryGameMDFunctions{}; +inline std::vector __UninitAfterEveryGameMDFunctions{}; + +/* +辅助类型,不直接使用 +Helper Class, NO Direct Usage. +*/ +inline void __cdecl UninitAfterEveryGameMDFunction() +{ + for (auto& func : __UninitAfterEveryGameMDFunctions)func(); +} +inline void InitBeforeEveryGameMDFunction() +{ + for (auto& func : __InitBeforeEveryGameMDFunctions)func(); + CRT::atexit(UninitAfterEveryGameMDFunction); +} +inline void InitBeforeEveryGameMDFunction_Register() +{ + CodeModifier::ModifyPointer(0x812000, InitBeforeEveryGameMDFunction); +} +struct InitBeforeEveryGameMDFunctionHelper +{ + InitBeforeEveryGameMDFunctionHelper() = delete; + InitBeforeEveryGameMDFunctionHelper(void(*func)()) + { + __InitBeforeEveryGameMDFunctions.push_back(func); + } + ~InitBeforeEveryGameMDFunctionHelper() = default; +}; +struct UninitAfterEveryGameMDFunctionHelper +{ + UninitAfterEveryGameMDFunctionHelper() = delete; + UninitAfterEveryGameMDFunctionHelper(void(*func)()) + { + __UninitAfterEveryGameMDFunctions.push_back(func); + } + ~UninitAfterEveryGameMDFunctionHelper() = default; +}; + + +/* + +NOTE: +Call Sequence: + +CTOR of global/static objects in YRDict.dll +DllMain(DLL_PROCESS_ATTACH) of YRDict.dll +EntryPoint of GameMD.exe +InitBeforeEveryGameMDFunction +CTOR of global/static objects in GameMD.exe +WinMain of GameMD.exe +... +Exit of GameMD.exe +DTOR of global/static objects in GameMD.exe +UninitAfterEveryGameMDFunction +ExitProcess + +*/ +/* + +在每次GameMD.exe启动前调用的函数 +与此同时,在GameMD.exe退出后调用对应的反初始化函数 +Functions to be called before every GameMD.exe launch +and corresponding uninitialization functions after GameMD.exe exit. + +*/ +#define INIT_BEFORE_EVERY_GAMEMD_FUNCTION(func) \ + inline InitBeforeEveryGameMDFunctionHelper __InitBeforeEveryGameMDFunctionHelper_##func(func); +#define UNINIT_AFTER_EVERY_GAMEMD_FUNCTION(func) \ + inline UninitAfterEveryGameMDFunctionHelper __UninitAfterEveryGameMDFunctionHelper_##func(func); diff --git a/src/Launch_Impl.cpp b/src/Launch_Impl.cpp new file mode 100644 index 0000000..4e1fffc --- /dev/null +++ b/src/Launch_Impl.cpp @@ -0,0 +1,189 @@ +#include "Launch_Impl.h" +#include "Windows.h" +#include + + +namespace CopyProtection_Impl +{ + + bool IsLauncherRunning() + { + if constexpr (UseVanillaImpl_Critical) + { + HANDLE hMutex; + + Debug::LogString("COPYPROTECTION - Checking if launcher is running\n"); + + hMutex = CreateMutexA(0, 0, MutexGUID); + bool HasLauncher = (GetLastError() == ERROR_ALREADY_EXISTS); + if (hMutex)CloseHandle(hMutex); + + Debug::Log("result was %d\n", HasLauncher); + return HasLauncher; + } + else + { + return true; + } + } + + bool NotifyLauncher() + { + if constexpr (UseVanillaImpl_Critical) + { + HANDLE hEvent; + DWORD NextCheckTime; + MSG Msg; + + const DWORD MillisecondsOf1Minute = 60000; + const DWORD MillisecondsOf10Seconds = 10000; + + Debug::LogString("COPYPROTECTION - Notify launcher\n"); + + PeekMessageA(&Msg, 0, WM_USER, WM_USER, PM_NOREMOVE); + DWORD NotifyWaitEndTime = timeGetTime() + MillisecondsOf1Minute; + + if (timeGetTime() < NotifyWaitEndTime) + { + + while (1) + { + hEvent = OpenEventA(EVENT_MODIFY_STATE, TRUE, NotifySignalGUID); + if (hEvent)break; + Sleep(0); + if (timeGetTime() >= NotifyWaitEndTime) + { + Debug::LogString("***** Failed to notify launcher!\n"); + return false; + } + } + + SetEvent(hEvent); + CloseHandle(hEvent); + Debug::LogString("Launcher notified.\n"); + Debug::LogString("Waiting for message from launcher.\n"); + NextCheckTime = timeGetTime() + MillisecondsOf10Seconds; + if (timeGetTime() <= NextCheckTime) + { + while (!PeekMessageA( + &Msg, 0, + Launcher_HereIAm_MessageID, + Launcher_HereIAm_MessageID, + PM_REMOVE) || + Msg.message != Launcher_HereIAm_MessageID) + { + Sleep(0); + if (timeGetTime() > NextCheckTime) + { + Debug::LogString("***** Failed to notify launcher!\n"); + return false; + } + } + _mProtectedData = (char*)MapViewOfFileEx((HANDLE)Msg.lParam, FILE_MAP_ALL_ACCESS, 0, 0, 0, 0); + if (_mProtectedData) + return true; + } + } + Debug::LogString("***** Failed to notify launcher!\n"); + return false; + } + else + { + return true; + } + } + + bool CheckVersion() + { + if constexpr (UseVanillaImpl_Critical) + { + LSTATUS RegQueryResult; + HKEY hKey; + DWORD RegKeycbData; + DWORD RegKeyType; + DWORD VersionFromRegister; + + if (RegOpenKeyExA(HKEY_CLASSES_ROOT, Launcher_RegKey, 0, KEY_READ, &hKey)) + return 0; + RegKeycbData = 4; + RegQueryResult = RegQueryValueExA(hKey, "ID", 0, &RegKeyType, (LPBYTE)&VersionFromRegister, &RegKeycbData); + RegCloseKey(hKey); + if (RegQueryResult || RegKeyType != REG_DWORD) + return 0; + return VersionFromRegister == VersionClass::Instance.Version_Number(); + } + else + { + return true; + } + } + + bool CheckProtectedData() + { + if constexpr (UseVanillaImpl_Critical) + { + const char* ProtectedStr; + + Debug::LogString("COPYPROTECTION - Validating\n"); + + ProtectedStr = _mProtectedData; + if (!_mProtectedData) + ProtectedStr = mProtectData_Failed; + Debug::Log("_mProtectedData = %d (%s)\n", _mProtectedData, ProtectedStr); + if (_mProtectedData) + return strcmp(_mProtectedData, mProtectData_Success) == 0; + + //Refolded Inline Function Calls + return CheckVersion(); + } + else + { + return true; + } + } + + void __fastcall DispatchLauncherMessage(MSG* pMsg) + { + if constexpr (UseVanillaImpl_Critical) + { + if (pMsg->message == Launcher_HereIAm_Message) + { + Debug::LogString("COPYPROTECTION - Received message from launcher.\n"); + _mProtectedData = (char*)MapViewOfFileEx((HANDLE)pMsg->lParam, FILE_MAP_ALL_ACCESS, 0, 0, 0, 0); + if (_mProtectedData) + Debug::Log("The message says: %s\n", _mProtectedData); + else + Debug::LogString("***** MapViewOfFileEx() Failed!\n"); + } + } + else return; + } + + LSTATUS ShutDown() + { + if constexpr (UseVanillaImpl_Critical) + { + LSTATUS RegOpenResult; + HKEY phkResult; + + Debug::LogString("COPYPROTECTION - Shutdown\n"); + if (_mProtectedData) + { + UnmapViewOfFile(_mProtectedData); + _mProtectedData = nullptr; + } + RegOpenResult = RegOpenKeyExA(HKEY_CLASSES_ROOT, Launcher_RegKey, 0, KEY_ALL_ACCESS, &phkResult); + if (!RegOpenResult) + { + RegDeleteValueA(phkResult, "ID"); + RegOpenResult = RegCloseKey(phkResult); + } + return RegOpenResult; + } + else + { + return ERROR_SUCCESS; + } + } +} + diff --git a/src/Launch_Impl.h b/src/Launch_Impl.h new file mode 100644 index 0000000..332b00f --- /dev/null +++ b/src/Launch_Impl.h @@ -0,0 +1,35 @@ +#pragma once +#include "ImplBase.h" +#include "VanillaImpl.h" +#include + +namespace CopyProtection_Impl +{ + inline const char* MutexGUID = "48BC11BD-C4D7-466b-8A31-C6ABBAD47B3E"; + inline const char* NotifySignalGUID = "D6E7FC97-64F9-4d28-B52C-754EDF721C6F"; + inline const char* Launcher_RegKey = "WChat\\SysID"; + inline const UINT Launcher_HereIAm_MessageID = 0xBEEFu; + inline const UINT Launcher_HereIAm_Message = 48879; + + bool IsLauncherRunning(); + FUNCTION_IMPLEMENT(0x49F5C0, IsLauncherRunning); + + bool NotifyLauncher(); + FUNCTION_IMPLEMENT(0x49F620, NotifyLauncher); + + bool CheckVersion(); + FUNCTION_IMPLEMENT(0x49F920, CheckVersion); + + bool CheckProtectedData(); + FUNCTION_IMPLEMENT(0x49F7A0, CheckProtectedData); + + void __fastcall DispatchLauncherMessage(MSG* pMsg); + FUNCTION_IMPLEMENT(0x49F740, DispatchLauncherMessage); + + LSTATUS ShutDown(); + FUNCTION_IMPLEMENT(0x49F8B0, ShutDown); + + inline const char* mProtectData_Failed = "EEK!"; + inline const char* mProtectData_Success = "UIDATA,3DDATA,MAPS"; + DEFINE_REFERENCE(char*, _mProtectedData, 0x89F75C) +} \ No newline at end of file diff --git a/src/VanillaImpl.h b/src/VanillaImpl.h new file mode 100644 index 0000000..5aa45cf --- /dev/null +++ b/src/VanillaImpl.h @@ -0,0 +1,52 @@ +#pragma once + +/* +以下的开关均通过 if constexpr 在编译期决定。 +The following switches are all decided at compile time via if constexpr. +*/ + +/* +使用默认实现。 +默认为true来尽量使用原版实现。 +切换为false以禁用原版实现。 + +Default - Use Vanilla Implementation +Default to true to use vanilla implementations where possible. +Switch to false to disable vanilla implementations +*/ +inline const bool UseVanillaImpl = true; + +/* +在Bug修复上使用默认实现 +默认为false来修复Bug。 +切换为true来使用有Bug的原版实现。 +注意:Bug修复应与对应的原版实现同时发布。 + +Bugfix - Use Vanilla Implementation for Bugfixes +Default to false to fix bugs. +Switch to true to use vanilla implementations even if they have bugs. +NOTE: Bugfixes should be released along with the Vanilla Implementation . +*/ +inline const bool UseVanillaImpl_Bugfix = false; + +/* +在补充功能上使用默认实现 +默认为false以启用日志等功能。 +切换为true来停用这些功能。 + +Supplementary - Use Vanilla Implementation for Supplementary Functions +Default to false to enable supplementary functions (Logging etc.) +Switch to true to turn off Supplementary functions. +*/ +inline const bool UseVanillaImpl_Supplementary = false; + +/* +在关键位置使用默认实现。 +默认为false来重写关键功能, +否则游戏可能无法正常启动。 + +Critical - Use Vanilla Implementation for Critical Functions +Default to false since some critical functions have to be rewritten, +or the game cannot be launched properly. +*/ +inline const bool UseVanillaImpl_Critical = false; \ No newline at end of file diff --git a/src/VersionMark.cpp b/src/VersionMark.cpp index 38f7548..d1fbe08 100644 --- a/src/VersionMark.cpp +++ b/src/VersionMark.cpp @@ -1,5 +1,6 @@ #include "Syringe.h" #include "Version.h" +#include const char* DisplayStr = "YRDict " MARK_VERSION; const char* InternalStr = "YRDict " PRODUCT_VERSION; @@ -17,3 +18,36 @@ DEFINE_HOOK(0x74FDC0, GetModuleVersion, 5) return 0x74FEEF; } + +DEFINE_HOOK(0x532017, DlgProc_MainMenu_Version, 5) +{ + GET(HWND, hWnd, ESI); + + // account for longer version numbers + const int MinimumWidth = 168; + + RECT Rect; + if (GetWindowRect(hWnd, &Rect)) { + int width = Rect.right - Rect.left; + + if (width < MinimumWidth) { + // extend to the left by the difference + Rect.left -= (MinimumWidth - width); + + // if moved out of screen, move right by this amount + if (Rect.left < 0) { + Rect.right += -Rect.left; + Rect.left = 0; + } + + MoveWindow(hWnd, Rect.left, Rect.top, Rect.right - Rect.left, Rect.bottom - Rect.top, FALSE); + } + } + + return 0; +} + +DEFINE_HOOK(0x52CA37, InitGame_Delay, 5) +{ + return 0x52CA65; +} diff --git a/src/WinMain_Impl.cpp b/src/WinMain_Impl.cpp new file mode 100644 index 0000000..4f3fee3 --- /dev/null +++ b/src/WinMain_Impl.cpp @@ -0,0 +1,28 @@ +#include "WinMain_Impl.h" +#include "Debug_Impl.h" +#include +#include + +namespace WinMain_Impl +{ + void UninitLast_CloseLog() + { + Debug_Impl::CloseLogFile("YRDict : Closing Log File at exit...\n"); + } + + + int __stdcall WinMain_Aux(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) + { + if constexpr (!UseVanillaImpl_Supplementary) + { + if (!Debug_Impl::IsLogFileOpen()) + Debug_Impl::OpenLogFile(); + } + int Result = System::WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); + if constexpr (!UseVanillaImpl_Supplementary) + { + Debug_Impl::FlushLogFile(); + } + return Result; + } +} \ No newline at end of file diff --git a/src/WinMain_Impl.h b/src/WinMain_Impl.h new file mode 100644 index 0000000..5e137fd --- /dev/null +++ b/src/WinMain_Impl.h @@ -0,0 +1,15 @@ +#pragma once +#include "ImplBase.h" +#include "VanillaImpl.h" +#include "InitBase.h" + + +namespace WinMain_Impl +{ + void UninitLast_CloseLog(); + UNINIT_AFTER_EVERY_GAMEMD_FUNCTION(UninitLast_CloseLog) + + //Replace the original WinMain call so as to insert our own initialization code + int __stdcall WinMain_Aux(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow); + FUNCTION_CALL_AUX(0x7CD8EA, WinMain_Aux) +} \ No newline at end of file