diff --git a/CHANGELOG.md b/CHANGELOG.md index 96c7fdd..80f7154 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,8 +42,32 @@ Update records: - Initial organization of RawFileClass_Impl completed. - Tested and confirmed to work correctly. +## Launch_Impl +#### 2025-10-16 IronHammer_Std +- 整理了CopyProtection部分的实现。 + +- Organized the implementation of CopyProtection. + +## InitCheck_Impl +#### 2025-10-18 IronHammer_Std +- 整理了GetFreeDiskSpaceKB的实现。 + +- Organized the implementation of GetFreeDiskSpaceKB. + ## Debug_Impl #### 2025-10-17 IronHammer_Std - 恢复了调试日志功能。 -- Restored the debug logging functionality. \ No newline at end of file +- Restored the debug logging functionality. +#### 2025-10-17 IronHammer_Std +- 更新文件操作为CRT的函数。 + +- Updated file operations to use CRT functions. + +## WinMain_Impl +#### 2025-10-17 IronHammer_Std +- 替换为了WinMain_Aux以增强日志相关功能。 +- 给出了WinMain的开头一小段的部分实现,并作为部分实现的样例。 + +- Replaced with WinMain_Aux to enhance logging-related features. +- Provided a small portion of the implementation at the beginning of WinMain as a sample of partial implementation. \ No newline at end of file diff --git a/Patch/Debug.cpp b/Patch/Debug.cpp index 1eda146..fa80950 100644 --- a/Patch/Debug.cpp +++ b/Patch/Debug.cpp @@ -1,12 +1,24 @@ #include "Debug.h" #include #include +#include "../src/VanillaImpl.h" +#include "../src/Debug_Impl.h" namespace Debug { void Log(const char* pFormat, ...) { - JMP_STD(0x4068E0); + if constexpr (!UseVanillaImpl_Supplementary) + { + va_list args; + va_start(args, pFormat); + Debug_Impl::LogWithVArgs(pFormat, args); + va_end(args); + } + else + { + JMP_STD(0x4068E0); + } } void LogAndMessage(const char* pFormat, ...) { @@ -20,11 +32,25 @@ namespace Debug } void LogString(const char* pStr) { - Log("%s", pStr); + if constexpr (!UseVanillaImpl_Supplementary) + { + Debug_Impl::LogString(pStr); + } + else + { + Log("%s", pStr); + } } void LogString(const std::string& Str) { - Log("%s", Str.c_str()); + if constexpr (!UseVanillaImpl_Supplementary) + { + Debug_Impl::LogString(Str.c_str()); + } + else + { + Log("%s", Str.c_str()); + } } void MessageString(const char* pStr) { diff --git a/README.md b/README.md index 55b1ed0..449e081 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,12 @@ YRDict 以函数为单位进行整理。 对单个类的实现,在YRpp当中记录下这个类的Type和具体内容, 同时,在实现当中以一个Type_Impl来容纳实现。 +具体的书写方式,可以参照示例代码和src/ImplBase.h等。 +函数的实现可以是完整的C++代码,也可以是IDA产出的伪代码。 +无论如何,请确保最终的代码能够成功编译通过。 +每个提交的函数按照ImplBase.h的约定标示是否可用。 +例如(PENDING)_IMPLEMENT/(PENDING)_FUNCTION_IMPLEMENT等。 +如果有任何不清楚的地方,欢迎提出Issue进行讨论。 YRDict is organized by functions. For each function, its interface and memory address are documented in projects like YRpp. @@ -66,6 +72,14 @@ For the implementation of a single class, record the Type and specific content of this class in YRpp, and at the same time, use a Type_Impl to contain the implementation. +Regarding the specific format, please see the example source files and src/ImplBase.h. +Implementations can be complete C++ code or IDA-generated pseudocode. +Anyway, make sure the final code can be built successfully. +Mark each function's status using the marker symbols defined in ImplBase.h +e.g., using (PENDING)_IMPLEMENT/(PENDING)_FUNCTION_IMPLEMENT. +For any questions, we welcome you to start a discussion by opening a new Issue. + + ## 使用/USAGE 把这个项目编译成DLL,或下载Release当中的DLL。 @@ -78,6 +92,7 @@ and at the same time, use a Type_Impl to contain the implementation. 当然,也可以仅仅拿来参考,或者用来作为自己扩展的基础。 这个项目的内容是开源的,遵循MIT许可证。 +欢迎基于YRDict开发引擎扩展。 Compile this project into a DLL, or download the DLL from the Release page. @@ -92,6 +107,7 @@ Loading it alongside other extensions may lead to unpredictable behavior. Naturally, you are also free to use this project solely for reference or as a foundation for your own extensions. The content of this project is open source and follows the MIT license. +You are welcome to develop game engine extensions based on YRDict. ## 注意/NOTE @@ -101,8 +117,9 @@ The content of this project is open source and follows the MIT license. 这个项目可以修正WW的原有BUG,但请注明WW原来是怎么写的。 如果有意愿的话,也可以标注出Ares/Phobos等钩了什么位置以及改变了什么。 -不考虑提供任何的新增或增强逻辑。 -允许也欢迎添加与调试等相关的辅助功能。 +不考虑提供任何的新增玩法或增强的游戏内逻辑。 +允许也欢迎添加与调试等相关的辅助功能和优化补丁。 + 所有源文件和说明文件应统一编码为 UTF8 带BOM格式。 @@ -110,6 +127,7 @@ The content of this project is open source and follows the MIT license. 可以申请加入,并在dev分支上直接开发与提交; 也可以fork本项目,并在自己的分支当中添加内容。 以上两种方式都应通过Pull Request的方式合并到main分支。 +注意只会合并通过所有自动检查的PR(如nightly build & changelog check) Please note that this project does not aim to fully reverse engineer YR. Instead, it focuses on documenting critical functions and implementations. @@ -121,8 +139,10 @@ This project can fix the original bugs of WW, but please indicate how WW was originally written. If you are willing, you can also indicate what positions 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. +No new or enhanced in-game logic is considered. +Adding auxiliary features related to debugging and similar tasks +as well as simple optimization patches is allowed and encouraged. + All source files and documentation files should be uniformly encoded in **UTF-8 with BOM** format. @@ -131,6 +151,8 @@ 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. Both methods should merge into the main branch via Pull Request. +Note that only PRs that pass all automated checks +(e.g., nightly build & changelog check) will be merged. ## 结语/CONCLUSION diff --git a/YRpp b/YRpp index 119136e..c34756e 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit 119136ea15638eb8f6fcc96fe67330b94029679a +Subproject commit c34756e952b1ea61a41287604e29643e9c9cc352 diff --git a/src/Debug_Impl.cpp b/src/Debug_Impl.cpp index a413c65..9a39a33 100644 --- a/src/Debug_Impl.cpp +++ b/src/Debug_Impl.cpp @@ -2,6 +2,7 @@ #include "VanillaImpl.h" #include #include +#include namespace Debug_Impl { @@ -53,7 +54,7 @@ namespace Debug_Impl MakeLogFile(); CloseLogFile(nullptr); - LogFile = _wfsopen(LogFilePath.c_str(), L"w", _SH_DENYWR); + LogFile = CRT::_fsopen(LogFilePath.string().c_str(), "w", _SH_DENYWR); if (!LogFile) { wchar_t msg[100] = L"\0"; LogFile = nullptr; @@ -61,7 +62,7 @@ namespace Debug_Impl MessageBoxW(Game::hWnd, LogFilePath.c_str(), msg, MB_OK | MB_ICONEXCLAMATION); ExitProcess(1); } - TempLogFile = _wfsopen(TempLogFilePath.c_str(), L"w", _SH_DENYWR); + TempLogFile = CRT::_fsopen(TempLogFilePath.string().c_str(), "w", _SH_DENYWR); if (!TempLogFile) { wchar_t msg[100] = L"\0"; TempLogFile = nullptr; @@ -82,8 +83,8 @@ namespace Debug_Impl Debug::LogString(CloseState); else Debug::LogString("YRDict : Closing Log File...\n"); - fclose(LogFile); - fclose(TempLogFile); + CRT::fclose(LogFile); + CRT::fclose(TempLogFile); LogFile = nullptr; TempLogFile = nullptr; } @@ -96,8 +97,8 @@ namespace Debug_Impl { if (IsLogFileOpen()) { - fflush(LogFile); - fflush(TempLogFile); + CRT::fflush(LogFile); + CRT::fflush(TempLogFile); } } } @@ -106,8 +107,17 @@ namespace Debug_Impl { if constexpr (!UseVanillaImpl_Supplementary) { - vfprintf(LogFile, pFormat, args); - vfprintf(TempLogFile, pFormat, args); + CRT::vfprintf(LogFile, pFormat, args); + CRT::vfprintf(TempLogFile, pFormat, args); + } + } + + void LogUnflushed(const char* Str) + { + if constexpr (!UseVanillaImpl_Supplementary) + { + CRT::fprintf(LogFile, "%s", Str); + CRT::fprintf(TempLogFile, "%s", Str); } } @@ -126,4 +136,30 @@ namespace Debug_Impl FlushLogFile(); } } + + void LogWithVArgs(const char* pFormat, va_list args) + { + if constexpr (!UseVanillaImpl_Supplementary) + { + if (!IsLogFileOpen()) + OpenLogFile(); + + LogWithVArgsUnflushed(pFormat, args); + + FlushLogFile(); + } + } + + void LogString(const char* Str) + { + if constexpr (!UseVanillaImpl_Supplementary) + { + if (!IsLogFileOpen()) + OpenLogFile(); + + LogUnflushed(Str); + + FlushLogFile(); + } + } } \ No newline at end of file diff --git a/src/Debug_Impl.h b/src/Debug_Impl.h index 6577de2..21c7105 100644 --- a/src/Debug_Impl.h +++ b/src/Debug_Impl.h @@ -18,8 +18,12 @@ namespace Debug_Impl void CloseLogFile(const char* CloseState); void FlushLogFile(); void LogWithVArgsUnflushed(const char* pFormat, va_list args); + void LogUnflushed(const char* Str); void __cdecl Log(const char* pFormat, ...); FUNCTION_IMPLEMENT(0x4A4AC0, Log); FUNCTION_IMPLEMENT(0x4068E0, Log); + + void LogWithVArgs(const char* pFormat, va_list args); + void LogString(const char* Str); } \ No newline at end of file diff --git a/src/FileClass_Impl.h b/src/FileClass_Impl.h index 588a211..0e1abac 100644 --- a/src/FileClass_Impl.h +++ b/src/FileClass_Impl.h @@ -84,7 +84,7 @@ DEFINE_VIRTUAL_IMPL(RawFileClass, 0x7F0904u) void Close(); DWORD GetFileTime(); bool SetFileTime(DWORD FileTime); - void CDCheck(DWORD ErrorCode, bool bUnk, const char* FileName) {} + void CDCheck(DWORD ErrorCode, bool canRetry, const char* FileName) {} //Non-virtual Function Implements void Bias(int offset = 0, int length = -1); diff --git a/src/ImplBase.h b/src/ImplBase.h index fd335f0..84f0e6f 100644 --- a/src/ImplBase.h +++ b/src/ImplBase.h @@ -181,6 +181,13 @@ See details in FileClass_Impl.h #define DEFINE_IMPLEMENT \ inline static void Implement() +/* + +什么也不会干,标记此处在完成实现之后应该换成IMPLEMENT +Does nothing; marks this location to be replaced with IMPLEMENT after the implementation is complete. + +*/ +#define PENDING_IMPLEMENT(Addr, Method) /* @@ -287,7 +294,7 @@ See details in FileClass_Impl.h /* -为普通函数生成实现补丁,放在函数的实现后面。 +为非成员函数生成实现补丁,放在函数的实现后面。 Generate Implementation Patch for Non-member Functions. For use after the function's implementation. @@ -300,8 +307,16 @@ See details in Launch_Impl.h /* -为尚未实现的函数生成跳转到原函数的补丁,放在函数的声明后面。 -Generate Jump-to-Original Patch for Not-Yet-Implemented Functions. +什么也不会干,标记此处在完成实现之后应该换成FUNCTION_IMPLEMENT +Does nothing; marks this location to be replaced with FUNCTION_IMPLEMENT after the implementation is complete. + +*/ +#define PENDING_FUNCTION_IMPLEMENT(Addr, Func) + +/* + +为尚未实现的非成员函数生成跳转到原函数的补丁,放在函数的声明后面。 +Generate Jump-to-Original Patch for Not-Yet-Implemented Non-member Functions. For use after the function's declaration. 详见示例 Launch_Impl.h diff --git a/src/Launch_Impl.cpp b/src/Launch_Impl.cpp index 4e1fffc..63e0ded 100644 --- a/src/Launch_Impl.cpp +++ b/src/Launch_Impl.cpp @@ -1,7 +1,7 @@ #include "Launch_Impl.h" #include "Windows.h" #include - +#include namespace CopyProtection_Impl { @@ -67,10 +67,10 @@ namespace CopyProtection_Impl { while (!PeekMessageA( &Msg, 0, - Launcher_HereIAm_MessageID, - Launcher_HereIAm_MessageID, + Launcher_HereIAm_Message, + Launcher_HereIAm_Message, PM_REMOVE) || - Msg.message != Launcher_HereIAm_MessageID) + Msg.message != Launcher_HereIAm_Message) { Sleep(0); if (timeGetTime() > NextCheckTime) @@ -187,3 +187,55 @@ namespace CopyProtection_Impl } } + +namespace InitCheck_Impl +{ + DWORD GetFreeDiskSpaceKB() + { + HMODULE hKernal32; + BOOL(__stdcall * GetDiskFreeSpaceExA)(LPCSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER); + unsigned int FreeDiskSpaceKB; + DWORD SectorsPerCluster; + DWORD BytesPerSector; + DWORD NumberOfFreeClusters; + DWORD TotalNumberOfClusters; + ULARGE_INTEGER FreeBytesAvailableToCaller; + ULARGE_INTEGER TotalNumberOfFreeBytes; + ULARGE_INTEGER TotalNumberOfBytes; + + Debug::LogString("Checking available disk space\n"); + hKernal32 = GetModuleHandleA("KERNEL32.DLL"); + if (hKernal32) + { + GetDiskFreeSpaceExA = reinterpret_cast + (GetProcAddress(hKernal32, "GetDiskFreeSpaceExA")); + if (GetDiskFreeSpaceExA) + { + Debug::LogString("Using GetDiskFreeSpaceEx\n"); + if (GetDiskFreeSpaceExA(NULL, &FreeBytesAvailableToCaller, &TotalNumberOfBytes, &TotalNumberOfFreeBytes)) + { + FreeDiskSpaceKB = Game::F2I64((FreeBytesAvailableToCaller.HighPart * 4294967296.0 + FreeBytesAvailableToCaller.LowPart) * 0.0009765625); + Debug::Log("Free disk space is %d Mb\n", FreeDiskSpaceKB >> 10); + return FreeDiskSpaceKB; + } + + //why there is 0 to %s ?! + Debug::Log("GetDiskFreeSpaceEx failed with error code %d - %s\n", GetLastError(), 0); + } + else + { + //why there is 0 to %s ?! + Debug::Log("GetProcAddress failed with error code %d - %s\n", GetLastError(), 0); + } + } + else + { + Debug::LogString("Failed to get module handle for KERNEL32.DLL\n"); + } + if (!GetDiskFreeSpaceA(0, &SectorsPerCluster, &BytesPerSector, &NumberOfFreeClusters, &TotalNumberOfClusters)) + return 0; + FreeDiskSpaceKB = NumberOfFreeClusters * ((BytesPerSector * SectorsPerCluster) >> 10); + Debug::Log("Free disk space is %d Mb\n", FreeDiskSpaceKB >> 10); + return FreeDiskSpaceKB; + } +} \ No newline at end of file diff --git a/src/Launch_Impl.h b/src/Launch_Impl.h index 332b00f..6b5e787 100644 --- a/src/Launch_Impl.h +++ b/src/Launch_Impl.h @@ -8,8 +8,7 @@ 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; + inline const UINT Launcher_HereIAm_Message = 0xBEEFu; bool IsLauncherRunning(); FUNCTION_IMPLEMENT(0x49F5C0, IsLauncherRunning); @@ -32,4 +31,10 @@ namespace CopyProtection_Impl inline const char* mProtectData_Failed = "EEK!"; inline const char* mProtectData_Success = "UIDATA,3DDATA,MAPS"; DEFINE_REFERENCE(char*, _mProtectedData, 0x89F75C) +}; + +namespace InitCheck_Impl +{ + DWORD GetFreeDiskSpaceKB(); + FUNCTION_IMPLEMENT(0x48DD50, GetFreeDiskSpaceKB); } \ No newline at end of file diff --git a/src/WinMain_Impl.cpp b/src/WinMain_Impl.cpp index 4f3fee3..cb1ef31 100644 --- a/src/WinMain_Impl.cpp +++ b/src/WinMain_Impl.cpp @@ -2,6 +2,9 @@ #include "Debug_Impl.h" #include #include +#include +#include +#include "Launch_Impl.h" namespace WinMain_Impl { @@ -9,7 +12,7 @@ namespace WinMain_Impl { Debug_Impl::CloseLogFile("YRDict : Closing Log File at exit...\n"); } - + int __stdcall WinMain_Aux(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { @@ -25,4 +28,107 @@ namespace WinMain_Impl } return Result; } + + + int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) + { + //Initialize system params + Game::hInstance = hInstance; + Game::MainThreadId = ::GetCurrentThreadId(); + + //Show that WW is genius + if constexpr (!UseVanillaImpl_Supplementary) + { + //WW is so genius that we cannot understand the idea behind these numbers + Debug::Log("%d-%d\n", 1660, 152); + Debug::Log("%d-%d\n", 1661, 76); + Debug::Log("%d-%d\n", 1662, 76); + Debug::Log("%d-%d\n", 1663, 19); + Debug::Log("%d-%d\n", 1664, 3608); + Debug::Log("%d-%d\n", 1665, 1752); + Debug::Log("%d-%d\n", 1666, 3600); + Debug::Log("%d-%d\n", 1667, 272); + Debug::Log("%d-%d\n", 1668, 456); + Debug::Log("%d-%d\n", 1669, 888); + Debug::Log("%d-%d\n", 1670, 120); + Debug::Log("%d-%d\n", 1671, 1824); + Debug::Log("%d-%d\n", 1672, 6040); + Debug::Log("%d-%d\n", 1673, 352); + Debug::Log("%d-%d\n", 1674, 760); + Debug::Log("%d-%d\n", 1675, 328); + Debug::Log("%d-%d\n", 1676, 116); + Debug::Log("%d-%d\n", 1677, 90296); + Debug::Log("%d-%d\n", 1678, 432); + Debug::Log("%d-%d\n", 1679, 1776); + Debug::Log("%d-%d\n", 1680, 3792); + Debug::Log("%d-%d\n", 1681, 24); + Debug::Log("%d-%d\n", 1682, 21868); + Debug::Log("%d-%d\n", 1683, 3608); + Debug::Log("%d-%d\n", 1684, 176); + Debug::Log("%d-%d\n", 1685, 700); + Debug::Log("%d-%d\n", 1686, 176); + Debug::Log("%d-%d\n", 1687, 676); + Debug::Log("%d-%d\n", 1688, 212); + Debug::Log("%d-%d\n", 1689, 160); + Debug::Log("%d-%d\n", 1690, 248); + Debug::Log("%d-%d\n", 1691, 224); + Debug::Log("%d-%d\n", 1692, 700); + Debug::Log("%d-%d\n", 1693, 72); + Debug::Log("%d-%d\n", 1694, 180); + Debug::Log("%d-%d\n", 1695, 164); + Debug::Log("%d-%d\n", 1696, 56); + Debug::Log("%d-%d\n", 1697, 2280); + Debug::Log("%d-%d\n", 1698, 3704); + Debug::Log("%d-%d\n", 1699, 14144); + Debug::Log("%d-%d\n", 1700, 256); + Debug::Log("%d-%d\n", 1701, 128); + Debug::Log("%d-%d\n", 1702, 432); + Debug::Log("%d-%d\n", 1703, 6336); + Debug::Log("%d-%d\n", 1704, 1024); + Debug::Log("%d-%d\n", 1705, 1); + Debug::Log("%d-%d\n", 1706, 1312); + Debug::Log("%d-%d\n", 1707, 240); + Debug::Log("%d-%d\n", 1708, 212); + Debug::Log("%d-%d\n", 1709, 8); + Debug::Log("%d-%d\n", 1710, 28); + Debug::Log("%d-%d\n", 1711, 4); + Debug::Log("%d-%d\n", 1712, 8); + Debug::Log("%d-%d\n", 1713, 16); + Debug::Log("%d-%d\n", 1714, 32); + Debug::Log("%d-%d\n", 1715, 24); + } + + // return true if Launcher is found + if (!CopyProtection_Impl::IsLauncherRunning()) + { + // CopyProtection -- Exit if Launcher isn't found + Debug::LogString("Launcher not running....Bail!\n"); + return 0; + } + + //Ensure that there is only 1 Instance + //including TS/FS/RA2/YR + Game::GameInstanceMutex = CreateMutexA(0, FALSE, GameInstanceMutex_UUID); + if (GetLastError() == ERROR_ALREADY_EXISTS) + { + HWND hWnd_FromMutex = FindWindowA(GameInstanceMutex_UUID, NULL); + if (hWnd_FromMutex) + { + SetForegroundWindow(hWnd_FromMutex); + ShowWindow(hWnd_FromMutex, SW_RESTORE); + } + if (Game::GameInstanceMutex) + { + CloseHandle(Game::GameInstanceMutex); + Game::GameInstanceMutex = NULL; + } + Debug::LogString("TibSun is already running...Bail!\n"); + // Exit -- Game is already running + return 0; + } + // Ensure that only one game instance is running + Debug::LogString("Create AppMutex okay.\n"); + + //To be Implemented + } } \ No newline at end of file diff --git a/src/WinMain_Impl.h b/src/WinMain_Impl.h index 5e137fd..82bed37 100644 --- a/src/WinMain_Impl.h +++ b/src/WinMain_Impl.h @@ -12,4 +12,15 @@ namespace WinMain_Impl //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) + + + inline const char* GameInstanceMutex_UUID = "29e3bb2a-2f36-11d3-a72c-0090272fa661"; + + /* + * + * As an example of partial implementation + * + */ + int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow); + PENDING_FUNCTION_IMPLEMENT(0x6BB9A0, WinMain) } \ No newline at end of file