From df3f2d7bbaf120ad79aacb281f807895a2735215 Mon Sep 17 00:00:00 2001 From: UnknownException <25252012+UnknownException@users.noreply.github.com> Date: Sat, 30 May 2026 11:26:07 +0200 Subject: [PATCH 1/2] Proton support, compatibility and rendering improvements Fixed black screens under Proton and various Windows configurations by using a separate overlay window. Fixed video playback by adding 32-bit blitting support and removing the previous workaround. Improved OpenGL renderer and software renderer performance. Allow custom width/height settings for DirectDraw. Added experimental ASLR/DEP support. Added a safeguard against executing unimplemented functions. Added minimal support for the Technical Demo. --- .github/workflows/action-development.yml | 3 +- .github/workflows/action-release.yml | 3 +- Debug/subtitans.ini | 10 +- PROTON-v1_0-v1_1.md | 12 + README.md | 90 ++-- Release/subtitans.ini | 10 +- d3drm/d3drm.cpp | 19 +- d3drm/d3drm.vcxproj | 5 + d3drm/injector.cpp | 39 +- game/subtitans.ini | 10 +- shared/file.cpp | 39 +- shared/file.h | 3 +- shared/gameversion.h | 21 +- subtitans/clipper.cpp | 22 +- subtitans/ddrawreplacementpatch.cpp | 217 +++++++--- subtitans/ddrawreplacementpatch.h | 5 + subtitans/demopatcher.cpp | 16 +- subtitans/demopatcher.h | 6 +- subtitans/device.cpp | 98 +++-- subtitans/gogpatcher.cpp | 170 ++++---- subtitans/gogpatcher.h | 5 +- subtitans/iddrawpalette.h | 4 +- subtitans/input.cpp | 18 +- subtitans/keyboarddevice.cpp | 56 +-- subtitans/mousedevice.cpp | 56 +-- subtitans/movieheapcorruptionpatch.cpp | 9 +- subtitans/nativeresolutionpatch.cpp | 18 - subtitans/nativeresolutionpatch.h | 3 - subtitans/openglrenderer.cpp | 472 +++++++++++++-------- subtitans/openglrenderer.h | 1 + subtitans/palette.cpp | 52 +-- subtitans/palette.h | 8 +- subtitans/patcher.cpp | 46 ++- subtitans/patcher.h | 3 + subtitans/pescalpel.cpp | 198 +++++++++ subtitans/pescalpel.h | 7 + subtitans/registrypatch.cpp | 57 +++ subtitans/registrypatch.h | 14 + subtitans/softwarerenderer.cpp | 94 +++-- subtitans/steampatchedpatcher.cpp | 161 ++++---- subtitans/steampatchedpatcher.h | 4 +- subtitans/steampatcher.cpp | 23 +- subtitans/steampatcher.h | 5 +- subtitans/subtitans.cpp | 213 ++++++++-- subtitans/subtitans.h | 16 +- subtitans/subtitans.vcxproj | 11 + subtitans/subtitans.vcxproj.filters | 21 + subtitans/surface.cpp | 503 ++++++++++++++--------- subtitans/surface.h | 25 +- subtitans/techdemopatcher.cpp | 73 ++++ subtitans/techdemopatcher.h | 12 + 51 files changed, 2061 insertions(+), 925 deletions(-) create mode 100644 PROTON-v1_0-v1_1.md create mode 100644 subtitans/pescalpel.cpp create mode 100644 subtitans/pescalpel.h create mode 100644 subtitans/registrypatch.cpp create mode 100644 subtitans/registrypatch.h create mode 100644 subtitans/techdemopatcher.cpp create mode 100644 subtitans/techdemopatcher.h diff --git a/.github/workflows/action-development.yml b/.github/workflows/action-development.yml index 92d7f98..8f0ea61 100644 --- a/.github/workflows/action-development.yml +++ b/.github/workflows/action-development.yml @@ -29,9 +29,10 @@ jobs: shell: powershell run: | Rename-Item -Path README.md -NewName readme.txt + Rename-Item -Path PROTON-v1_0-v1_1.md -NewName PROTON-v1_0-v1_1.txt Rename-Item -Path LICENSE -NewName license.txt $compress = @{ - Path = "Release/*.dll", "readme.txt", "license.txt", "Release/*.ini" + Path = "Release/*.dll", "readme.txt", "PROTON-v1_0-v1_1.txt", "license.txt", "Release/*.ini" DestinationPath = "SubTitans.zip" Force = $True } diff --git a/.github/workflows/action-release.yml b/.github/workflows/action-release.yml index e5d36e0..0d18dab 100644 --- a/.github/workflows/action-release.yml +++ b/.github/workflows/action-release.yml @@ -27,9 +27,10 @@ jobs: shell: powershell run: | Rename-Item -Path README.md -NewName readme.txt + Rename-Item -Path PROTON-v1_0-v1_1.md -NewName PROTON-v1_0-v1_1.txt Rename-Item -Path LICENSE -NewName license.txt $compress = @{ - Path = "Release/*.dll", "readme.txt", "license.txt", "Release/*.ini" + Path = "Release/*.dll", "readme.txt", "PROTON-v1_0-v1_1.txt", "license.txt", "Release/*.ini" DestinationPath = "SubTitans.zip" Force = $True } diff --git a/Debug/subtitans.ini b/Debug/subtitans.ini index 7f7ec79..3000ad4 100644 --- a/Debug/subtitans.ini +++ b/Debug/subtitans.ini @@ -26,4 +26,12 @@ MovieHeap=true ; Replaces the default FPS limiter with a better implementation. ImprovedFPSLimiter=true ; Improves the smoothness of scrolling in-game. -SmoothScroll=true \ No newline at end of file +SmoothScroll=true + +[SECURITY] +; Stop the execution of the game when a path of SubTitans has been hit that is not implemented. +; Setting this flag to false could result in undefined behavior, advised is to keep this as-is. +PreventExecutingUnimplemented=true +; EXPERIMENTAL! Patches the executable to enable ASLR and DEP, these are modern security mitigations. +; Could crash the game as it was never designed to run with ASLR or DEP enabled. +EnableASLRAndDEP=false \ No newline at end of file diff --git a/PROTON-v1_0-v1_1.md b/PROTON-v1_0-v1_1.md new file mode 100644 index 0000000..e50fc3a --- /dev/null +++ b/PROTON-v1_0-v1_1.md @@ -0,0 +1,12 @@ +# Linux & Proton (Steam) -> Upgrade from v1.0 to v1.1 +If you are running the game on Linux via Steam/Proton, the upgrade to v1.1 requires a workaround. + +### How to Run the Patch via Steam/Proton +Because the patcher needs to run within your game's specific Wine context, use this renaming workaround to launch it through Steam: + +0. **Download:** Ignore the guide and just download the patch file from https://steamcommunity.com/sharedfiles/filedetails/?id=2129291420 +1. **Register fix:** Before attempting to patch, **launch the game at least once** with `subtitans.dll` already placed in your game folder. This will fix the version registry key. +2. **Create a Backup:** Navigate to your game folder and find `STEditor.exe`. Create a backup copy of it. +3. **Rename the Patcher:** Copy the patch file `st1_mj0to0_1_usa.exe` to the game folder and rename it to `STEditor.exe`. +4. **Launch the Patcher:** Launch the game and select the **"Game Editor"** option in Steam. This tricks Proton into executing the patcher with the correct context. +5. **Restore Original Files:** Once patching is finished you must delete the patch file `STEditor.exe`. Don't forget to restore your backup copy of `STEditor.exe` diff --git a/README.md b/README.md index b4664ac..fbabfd0 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,21 @@ -# SubTitans -Unofficial Submarine Titans patch \ -![GitHub all releases](https://img.shields.io/github/downloads/UnknownException/SubTitans/total) +# SubTitans ![GitHub all releases](https://img.shields.io/github/downloads/UnknownException/SubTitans/total) +## Unofficial patch for Submarine Titans -**Requirements** -* Windows 7, Windows 10, Windows 11 or Wine (+ Ubuntu) -* Supports Steam/Retail v1.0, Retail v1.1 and GOG v1.1 +### Requirements +* Windows 11 or a Linux distribution with Wine or Proton +* Retail v1.1 or GOG v1.1 -**Features** +> Support for v1.0, Demo, and the Technology Demo is limited. +> These versions are not actively tested, and some features may be missing. + +### Instructions +1. Copy & paste d3drm.dll, subtitans.dll and subtitans.ini into your Submarine Titans folder. +2. Open STConfig.exe and select 1280x1024. +3. Run the game. + +> **For Steam users**: At least start the game once before applying this patch. steam_installscript.vdf will not be applied if you skip this step. + +### Features * Support for any resolution in-game (tested up to 3840x2160) * OpenGL and Software rendering replacement for DirectDraw to improve compatibility and performance * DInput replacement @@ -14,36 +23,45 @@ Unofficial Submarine Titans patch \ * Added mission skip cheat (Retail/GOG v1.1; workaround for progression bugs) * Fixes various internal errors * Fixes alt-tab crashes +* Fixes video issues * Support for display scaling -* Windows 7 palette color fix +* Palette color fix -**Instructions** -1. Copy & paste d3drm.dll, subtitans.dll and subtitans.ini into your Submarine Titans folder. -2. Open STConfig.exe and select 1280x1024. -3. Run the game. - -**Q&A** \ -Q: How do I use the mission skip cheat? \ -A: Load the mission you want to skip and write 'orbiton' without quotes in the chatbox. \ -Exit to the menu and select a random campaign to start the next mission. \ - \ -Q: Help, I'm getting MSVCP140.dll errors! \ -A: Install vc_redist.x86.exe ( https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads ). \ - \ -Q: How to update Submarine Titans to version 1.1? \ -A: https://steamcommunity.com/sharedfiles/filedetails/?id=2129291420 \ - \ -Q: I've got a problem in-game with my mouse and/or keyboard. \ -A: Open SubTitans.ini and set CustomInput to false, this should revert the input handling changes. \ - \ -Q: I've got a problem with the OpenGL/Software renderer and want to use a custom DDraw wrapper. \ -A: Open SubTitans.ini and set Renderer to 1. \ - \ -Q: The Submarine Titans demo crashes with or without this patch. \ -A: The support for the demo version of Submarine Titans is bare minimum. Please don't report issues regarding the demo, it will be ignored. - -**Known bugs** +### Known bugs * Regions next to the in-game command panel aren't selectable/clickable. -**Uninstall** -* Deleting SubTitans.dll will disable the in-game patches. \ No newline at end of file +## Question and Answers +### How to update Submarine Titans to version 1.1? +> Window: https://steamcommunity.com/sharedfiles/filedetails/?id=2129291420 \ +> Linux: See [PROTON-v1_0-v_1_1.md](PROTON-v1_0-v1_1.md) + +### Why am I getting MSVCP140.dll errors? +> You're missing some libraries on your computer, you'll need to install the Visual C++ redistributable provided by Microsoft. \ +> vc_redist.x86.exe ( https://aka.ms/vc14/vc_redist.x86.exe ) + +### I've got a problem in-game with my mouse and/or keyboard. +> Open SubTitans.ini and set CustomInput to *false*. \ +> This will turn off the DInput reimplementation. + +### How can I use a custom or GOG's DDraw wrapper? +> Open SubTitans.ini and set Renderer to **1**. +> +> Only the OpenGL renderer and Software renderer are tested while developing this patch. \ +> Custom wrappers might introduce new issues, please validate if issues you observe also occur under the OpenGL/Software renderer. + +### The Submarine Titans (technology) demo crashes with or without this patch. +> The support for the demo versions of Submarine Titans is bare minimum. Please don't report issues regarding the demo, it will be ignored. + +### Why would I want to enable the experimental ASLR/DEP option? +> Many (older) applications have inherent vulnerabilities, and exposing them over the internet (such as in multiplayer modes) increases the attack vector by a huge margin. +> +> By enabling **ASLR (Address Space Layout Randomization)** and **DEP (Data Execution Prevention)**, you tighten security by making the application's memory space less predictable. While this doesn't patch the vulnerabilities within the game itself, it makes them significantly harder for an attacker to successfully exploit + +### How do I use the mission skip cheat? +> Load the mission you want to skip and write 'orbiton' without quotes in the chatbox. \ +> Exit to the menu and select a random campaign to start the next mission. \ + +### How do I uninstall the patch? +> Deleting SubTitans.dll will disable the in-game patches. +> +> Do **NOT** delete d3drm.dll, the game will stop working without it. diff --git a/Release/subtitans.ini b/Release/subtitans.ini index 7f7ec79..3000ad4 100644 --- a/Release/subtitans.ini +++ b/Release/subtitans.ini @@ -26,4 +26,12 @@ MovieHeap=true ; Replaces the default FPS limiter with a better implementation. ImprovedFPSLimiter=true ; Improves the smoothness of scrolling in-game. -SmoothScroll=true \ No newline at end of file +SmoothScroll=true + +[SECURITY] +; Stop the execution of the game when a path of SubTitans has been hit that is not implemented. +; Setting this flag to false could result in undefined behavior, advised is to keep this as-is. +PreventExecutingUnimplemented=true +; EXPERIMENTAL! Patches the executable to enable ASLR and DEP, these are modern security mitigations. +; Could crash the game as it was never designed to run with ASLR or DEP enabled. +EnableASLRAndDEP=false \ No newline at end of file diff --git a/d3drm/d3drm.cpp b/d3drm/d3drm.cpp index 0daae99..83f3763 100644 --- a/d3drm/d3drm.cpp +++ b/d3drm/d3drm.cpp @@ -6,12 +6,6 @@ #include "../shared/file.h" #include "injector.h" -#ifdef _DEBUG - #pragma comment(lib, "../Debug/shared.lib") -#else - #pragma comment(lib, "../Release/shared.lib") -#endif - #pragma comment(linker, "/EXPORT:D3DRMVectorModulus=_D3DRMVectorModulus@4") extern "C" float __stdcall D3DRMVectorModulus(D3DVector3* vector) { @@ -26,14 +20,17 @@ uint32_t GetSubTitansVersion() WCHAR applicationPath[MAX_PATH]; GetModuleFileName(NULL, applicationPath, MAX_PATH); - uint32_t checkSum = File::CalculateChecksum(applicationPath); + uint32_t checkSum = File::CalculateChecksumW(applicationPath); switch (checkSum) { - case Shared::ST_GAMEVERSION_RETAIL_UNPATCHED: - case Shared::ST_GAMEVERSION_RETAIL_PATCHED: - case Shared::ST_GAMEVERSION_GOG_MODIFIED: - case Shared::ST_GAMEVERSION_DEMO: + case Shared::ST_GAMEVERSION_0_0_6: + case Shared::ST_GAMEVERSION_0_1_6: + case Shared::ST_GAMEVERSION_1_0_0: + case Shared::ST_GAMEVERSION_1_1_0: + case Shared::ST_GAMEVERSION_1_1_0_ASLR_DEP: + case Shared::ST_GAMEVERSION_1_1_0_GOG: + case Shared::ST_GAMEVERSION_1_1_0_GOG_ASLR_DEP: return checkSum; default: return 0; diff --git a/d3drm/d3drm.vcxproj b/d3drm/d3drm.vcxproj index 37da9cb..5dbfbaa 100644 --- a/d3drm/d3drm.vcxproj +++ b/d3drm/d3drm.vcxproj @@ -84,6 +84,11 @@ + + + {5f2c7921-4b31-4712-8722-c99eff304ec4} + + diff --git a/d3drm/injector.cpp b/d3drm/injector.cpp index 0483876..274cbb1 100644 --- a/d3drm/injector.cpp +++ b/d3drm/injector.cpp @@ -125,7 +125,27 @@ namespace Injector { GameVersion = gameVersion; switch (GameVersion) { - case Shared::ST_GAMEVERSION_RETAIL_UNPATCHED: + case Shared::ST_GAMEVERSION_0_0_6: + { + LoadModule_JmpFrom = 0x005CE3CE; + LoadModule_JmpBack = LoadModule_JmpFrom + LoadModule_DetourSize; + + UnloadModule_JmpFrom = 0x005CE3D3; + UnloadModule_JmpBack = UnloadModule_JmpFrom + UnloadModule_DetourSize; + + StartApplicationAddress = 0x00401ACD; + } break; + case Shared::ST_GAMEVERSION_0_1_6: + { + LoadModule_JmpFrom = 0x0071A25E; + LoadModule_JmpBack = LoadModule_JmpFrom + LoadModule_DetourSize; + + UnloadModule_JmpFrom = 0x0071A263; + UnloadModule_JmpBack = UnloadModule_JmpFrom + UnloadModule_DetourSize; + + StartApplicationAddress = 0x00401FF0; + } break; + case Shared::ST_GAMEVERSION_1_0_0: { LoadModule_JmpFrom = 0x00734B6E; LoadModule_JmpBack = LoadModule_JmpFrom + LoadModule_DetourSize; @@ -135,8 +155,8 @@ namespace Injector { StartApplicationAddress = 0x00401FEB; } break; - case Shared::ST_GAMEVERSION_RETAIL_PATCHED: - case Shared::ST_GAMEVERSION_GOG_MODIFIED: + case Shared::ST_GAMEVERSION_1_1_0: + case Shared::ST_GAMEVERSION_1_1_0_GOG: { LoadModule_JmpFrom = 0x007337FE; LoadModule_JmpBack = LoadModule_JmpFrom + LoadModule_DetourSize; @@ -146,15 +166,20 @@ namespace Injector { StartApplicationAddress = 0x00401FF5; } break; - case Shared::ST_GAMEVERSION_DEMO: + case Shared::ST_GAMEVERSION_1_1_0_ASLR_DEP: + case Shared::ST_GAMEVERSION_1_1_0_GOG_ASLR_DEP: { - LoadModule_JmpFrom = 0x0071A25E; + const unsigned int originalBaseAddress = Shared::IMAGE_BASE; + const unsigned int baseAddress = (unsigned int)GetModuleHandle(NULL); + + LoadModule_JmpFrom = 0x007337FE - originalBaseAddress + baseAddress; LoadModule_JmpBack = LoadModule_JmpFrom + LoadModule_DetourSize; - UnloadModule_JmpFrom = 0x0071A263; + UnloadModule_JmpFrom = 0x00733803 - originalBaseAddress + baseAddress; UnloadModule_JmpBack = UnloadModule_JmpFrom + UnloadModule_DetourSize; - StartApplicationAddress = 0x00401FF0; + StartApplicationAddress = 0x00401FF5 - originalBaseAddress + baseAddress; + } break; default: return false; diff --git a/game/subtitans.ini b/game/subtitans.ini index 7f7ec79..3000ad4 100644 --- a/game/subtitans.ini +++ b/game/subtitans.ini @@ -26,4 +26,12 @@ MovieHeap=true ; Replaces the default FPS limiter with a better implementation. ImprovedFPSLimiter=true ; Improves the smoothness of scrolling in-game. -SmoothScroll=true \ No newline at end of file +SmoothScroll=true + +[SECURITY] +; Stop the execution of the game when a path of SubTitans has been hit that is not implemented. +; Setting this flag to false could result in undefined behavior, advised is to keep this as-is. +PreventExecutingUnimplemented=true +; EXPERIMENTAL! Patches the executable to enable ASLR and DEP, these are modern security mitigations. +; Could crash the game as it was never designed to run with ASLR or DEP enabled. +EnableASLRAndDEP=false \ No newline at end of file diff --git a/shared/file.cpp b/shared/file.cpp index a05632d..037f869 100644 --- a/shared/file.cpp +++ b/shared/file.cpp @@ -15,9 +15,8 @@ bool File::Exists(const wchar_t* path) // Only Kernel32 dependent constexpr uint32_t CRC32_POLYNOMIAL = 0xEDB88320; -unsigned int File::CalculateChecksum(const wchar_t* path) +unsigned int CalculateChecksum(HANDLE fileHandle) { - HANDLE fileHandle = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (fileHandle == INVALID_HANDLE_VALUE) return 0; @@ -25,29 +24,19 @@ unsigned int File::CalculateChecksum(const wchar_t* path) HANDLE processHeap = GetProcessHeap(); if (processHeap == NULL) - { - CloseHandle(fileHandle); return 0; - } void* allocatedMemory = HeapAlloc(processHeap, HEAP_ZERO_MEMORY, allocatedSize); if (allocatedMemory == nullptr) - { - CloseHandle(fileHandle); return 0; - } DWORD bytesRead = 0; if (!ReadFile(fileHandle, allocatedMemory, allocatedSize, &bytesRead, 0) || bytesRead != allocatedSize) { - CloseHandle(fileHandle); HeapFree(processHeap, 0, allocatedMemory); - return 0; } - CloseHandle(fileHandle); - uint32_t checkSumTable[256]; for (uint32_t tableIt = 0; tableIt < sizeof(checkSumTable) / sizeof(uint32_t); ++tableIt) { @@ -69,4 +58,28 @@ unsigned int File::CalculateChecksum(const wchar_t* path) HeapFree(processHeap, 0, allocatedMemory); return ~checkSumResult; -} \ No newline at end of file +} + +uint32_t File::CalculateChecksumA(const char* path) +{ + HANDLE fileHandle = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (fileHandle == INVALID_HANDLE_VALUE) + return 0; + + uint32_t checksum = CalculateChecksum(fileHandle); + + CloseHandle(fileHandle); + return checksum; +} + +uint32_t File::CalculateChecksumW(const wchar_t* path) +{ + HANDLE fileHandle = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (fileHandle == INVALID_HANDLE_VALUE) + return 0; + + uint32_t checksum = CalculateChecksum(fileHandle); + + CloseHandle(fileHandle); + return checksum; +} diff --git a/shared/file.h b/shared/file.h index c81816b..43a8874 100644 --- a/shared/file.h +++ b/shared/file.h @@ -3,5 +3,6 @@ namespace File { bool Exists(const wchar_t* path); // Only Kernel32 dependent - uint32_t CalculateChecksum(const wchar_t* path); // Only Kernel32 dependent + uint32_t CalculateChecksumA(const char* path); // Only Kernel32 dependent + uint32_t CalculateChecksumW(const wchar_t* path); // Only Kernel32 dependent } \ No newline at end of file diff --git a/shared/gameversion.h b/shared/gameversion.h index f484807..30b23fd 100644 --- a/shared/gameversion.h +++ b/shared/gameversion.h @@ -1,12 +1,25 @@ #pragma once namespace Shared { - constexpr unsigned long ST_GAMEVERSION_RETAIL_UNPATCHED = 0x826D8625; // 1.0 from steam store/retail - constexpr unsigned long ST_GAMEVERSION_RETAIL_PATCHED = 0xB5BD8D15; // 1.1 retail patch - constexpr unsigned long ST_GAMEVERSION_GOG_MODIFIED = 0x18C26D0A; // 1.1 from gog store - constexpr unsigned long ST_GAMEVERSION_DEMO = 0x6B08028C; // Demo + // Technology Demo + constexpr unsigned long ST_GAMEVERSION_0_0_6 = 0x15C56BB2; + // Demo + constexpr unsigned long ST_GAMEVERSION_0_1_6 = 0x6B08028C; + // Retail, Steam + constexpr unsigned long ST_GAMEVERSION_1_0_0 = 0x826D8625; + // Retail + constexpr unsigned long ST_GAMEVERSION_1_1_0 = 0xB5BD8D15; + // GOG + constexpr unsigned long ST_GAMEVERSION_1_1_0_GOG = 0x18C26D0A; + // Retail - DYNAMICBASE (ASLR) and NXCOMPAT (DEP) flags enabled + constexpr unsigned long ST_GAMEVERSION_1_1_0_ASLR_DEP = 0x0191DC88; + // GOG - DYNAMICBASE (ASLR) and NXCOMPAT (DEP) flags enabled + constexpr unsigned long ST_GAMEVERSION_1_1_0_GOG_ASLR_DEP = 0xACEE3C97; + // Who cares? perhaps I should remove this constexpr unsigned long ST_LANGUAGE_ENGLISH_UNPATCHED = 0xCE56F1FC; // 1.0 English constexpr unsigned long ST_LANGUAGE_ENGLISH_PATCHED = 0x0EB5715F; // 1.1 English constexpr unsigned long ST_LANGUAGE_ENGLISH_DEMO = 0xE73A5959; // Demo English + + constexpr unsigned long IMAGE_BASE = 0x00400000; } \ No newline at end of file diff --git a/subtitans/clipper.cpp b/subtitans/clipper.cpp index 0b361e9..eda9264 100644 --- a/subtitans/clipper.cpp +++ b/subtitans/clipper.cpp @@ -5,16 +5,16 @@ using namespace DDraw; Clipper::Clipper() { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); referenceCount = 0; } -Clipper::~Clipper() { GetLogger()->Trace("%s\n", __FUNCTION__); } +Clipper::~Clipper() { TRACELOG("%s\n", __FUNCTION__); } // IUnknown uint32_t __stdcall Clipper::QueryInterface(GUID* guid, void** result) { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); GetLogger()->Error("%s %s\n", __FUNCTION__, "unknown interface"); *result = nullptr; @@ -23,7 +23,7 @@ uint32_t __stdcall Clipper::QueryInterface(GUID* guid, void** result) uint32_t __stdcall Clipper::AddRef() { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); referenceCount++; @@ -32,7 +32,7 @@ uint32_t __stdcall Clipper::AddRef() uint32_t __stdcall Clipper::Release() { - GetLogger()->Trace("%s (Remaining references %i)\n", __FUNCTION__, referenceCount); + TRACELOG("%s (Remaining references %i)\n", __FUNCTION__, referenceCount); if (--referenceCount == 0) delete this; @@ -43,17 +43,17 @@ uint32_t __stdcall Clipper::Release() // clipper uint32_t __stdcall Clipper::GetClipList(void*, void*, void*) { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); return ResultCode::NoClipList; } -uint32_t __stdcall Clipper::GetHWnd(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Clipper::Initialize(void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Clipper::IsClipListChanged(uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Clipper::SetClipList(void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } +uint32_t __stdcall Clipper::GetHWnd(void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Clipper::Initialize(void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Clipper::IsClipListChanged(uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Clipper::SetClipList(void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } uint32_t __stdcall Clipper::SetHWnd(uint32_t, void*) { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); return ResultCode::Ok; } \ No newline at end of file diff --git a/subtitans/ddrawreplacementpatch.cpp b/subtitans/ddrawreplacementpatch.cpp index 86035d1..065d23b 100644 --- a/subtitans/ddrawreplacementpatch.cpp +++ b/subtitans/ddrawreplacementpatch.cpp @@ -11,7 +11,7 @@ namespace DDrawCreateDetour { static unsigned long JmpFromAddress = 0; static unsigned long JmpBackAddress = 0; - __declspec(naked) void Implementation() + static __declspec(naked) void Implementation() { __asm call [DirectDrawCreate]; __asm jmp [JmpBackAddress]; @@ -24,7 +24,7 @@ namespace DInputCreateDetour { static unsigned long JmpFromAddress = 0; static unsigned long JmpBackAddress = 0; - __declspec(naked) void Implementation() + static __declspec(naked) void Implementation() { __asm call [DirectInputCreate]; __asm jmp [JmpBackAddress]; @@ -42,42 +42,14 @@ namespace WindowRegisterClassDetour { static uint8_t KeyTranslationTable[256] = { 0, }; - LRESULT CALLBACK WndProcDetour(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) + static LRESULT CALLBACK WndProcDetour(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_WINDOWPOSCHANGING: { - GetLogger()->Debug("Handling WM_WINDOWPOSCHANGING\n"); - WINDOWPOS* windowPos = (WINDOWPOS*)lParam; - if (windowPos->cx != Global::MonitorWidth || windowPos->cy != Global::MonitorHeight) - { - windowPos->cx = Global::MonitorWidth; - windowPos->cy = Global::MonitorHeight; - - if (Global::GameWindow) - { - HDC deviceContext = GetDC(Global::GameWindow); - - RECT rect; - rect.left = 0; - rect.top = 0; - rect.right = Global::MonitorWidth; - rect.bottom = Global::MonitorHeight; - - HBRUSH brush = CreateSolidBrush(RGB(0, 0, 0)); - FillRect(deviceContext, &rect, brush); - DeleteObject(brush); - - ReleaseDC(Global::GameWindow, deviceContext); - } - } - - RECT rect; - rect.left = 0; - rect.top = 0; - rect.right = Global::InternalWidth; - rect.bottom = Global::InternalHeight; + TRACELOG("%s %s\n", __FUNCTION__, "WM_WINDOWPOSCHANGING"); + RECT rect { 0, 0 , Global::InternalWidth, Global::InternalHeight }; ClipCursor(&rect); } break; case WM_MOUSEMOVE: @@ -112,7 +84,7 @@ namespace WindowRegisterClassDetour { Global::MouseInformation.mPressed = Global::KeyPressedFlag; break; case WM_XBUTTONUP: - // Filter on key? + // The game doesn't differentiate between button 1 and 2 Global::MouseInformation.xPressed = Global::KeyReleasedFlag; break; case WM_XBUTTONDOWN: @@ -165,11 +137,7 @@ namespace WindowRegisterClassDetour { if (wParam == 1) { - RECT rect; - rect.left = 0; - rect.top = 0; - rect.right = Global::InternalWidth; - rect.bottom = Global::InternalHeight; + RECT rect{ 0, 0 , Global::InternalWidth, Global::InternalHeight }; ClipCursor(&rect); } else @@ -184,14 +152,14 @@ namespace WindowRegisterClassDetour { return WndProc(hWnd, message, wParam, lParam); } - ATOM __stdcall RegisterClassDetour(WNDCLASSA* wndClass) + static ATOM __stdcall RegisterClassDetour(WNDCLASSA* wndClass) { WndProc = wndClass->lpfnWndProc; wndClass->lpfnWndProc = WndProcDetour; return RegisterClassA(wndClass); } - __declspec(naked) void Implementation() + static __declspec(naked) void Implementation() { __asm call [RegisterClassDetour]; __asm jmp [JmpBackAddress]; @@ -207,8 +175,50 @@ namespace WindowCreateDetour { // Function variables static bool ForceSoftwareRendering = false; - void __stdcall CreateRenderer() + static LRESULT CALLBACK OverlayWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch (uMsg) + { + case WM_MOUSEACTIVATE: + return MA_NOACTIVATE; + case WM_NCHITTEST: + return HTTRANSPARENT; + default: + return DefWindowProc(hwnd, uMsg, wParam, lParam); + } + } + + static void CreateRenderWindow() + { + WNDCLASS wc = {}; + wc.lpfnWndProc = OverlayWndProc; + wc.hInstance = GetModuleHandle(NULL); + wc.lpszClassName = L"SubTitansOverlay"; + wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); + RegisterClass(&wc); + + HWND hwndOverlay = CreateWindowEx( + WS_EX_NOACTIVATE | WS_EX_NOPARENTNOTIFY, + L"SubTitansOverlay", + L"SubTitans Overlay", + WS_POPUP, + 0, 0, + Global::MonitorWidth, + Global::MonitorHeight, + Global::GameWindow, // Parent + NULL, GetModuleHandle(NULL), NULL + ); + + ShowWindow(hwndOverlay, SW_SHOWNOACTIVATE); + UpdateWindow(hwndOverlay); + + Global::RenderWindow = hwndOverlay; + } + + static void __stdcall CreateRenderer() { + CreateRenderWindow(); + IRenderer* renderer = nullptr; if (!ForceSoftwareRendering) { @@ -228,13 +238,17 @@ namespace WindowCreateDetour { { GetLogger()->Informational("Using the software renderer\n"); renderer = new SoftwareRenderer(); - renderer->Create(); + if (!renderer->Create()) + { + GetLogger()->Critical("Failed to create the software renderer\n"); + Exit(); + } } Global::Backend = renderer; } - __declspec(naked) void Implementation() + static __declspec(naked) void Implementation() { __asm call [CreateWindowExA]; __asm mov [Global::GameWindow], eax; @@ -252,7 +266,7 @@ namespace DInputAbsolutePositioning { static unsigned long JmpFromAddress = 0; static unsigned long JmpBackAddress = 0; - __declspec(naked) void Implementation() + static __declspec(naked) void Implementation() { __asm mov edi, [Global::MouseInformation.x]; __asm mov edx, [Global::MouseInformation.y]; @@ -260,6 +274,94 @@ namespace DInputAbsolutePositioning { } } +namespace VideoFormatCheckDetour { + // This is a remnant of a previous attempt to fix the video playback. + // It's handy for (my) debugging / tracing purposes :) + + // Detour variables + constexpr unsigned long DetourSize = 9; + static unsigned long JmpFromAddress = 0; + static unsigned long JmpBackAddress = 0; + + static void __stdcall PeekVideoName(const char* videoPath) + { + uint32_t length = strlen(videoPath); + if (length == 0) return; + + uint32_t start = 0; + for (uint32_t i = length - 1; i > 0; --i) + { + if (videoPath[i] == '\\') + { + start = i + 1; + break; + } + } + + // Final char \ check & I don't expect the filename to start at the first character + if (start != length && start != 0) + { + Global::VideoRequested = true; + GetLogger()->Informational("Video '%s' is being prepared to play.\n", std::string(videoPath + start, length - start).c_str()); + } + else + { + GetLogger()->Informational("Failed to grab video name\n"); + } + } + + static __declspec(naked) void Implementation() + { + // Copy of the prologue + __asm push ebp; + __asm mov ebp, esp; + __asm push esi; + __asm push edi; + + // Restore throw away register + __asm push ecx; + + // Custom + __asm mov eax, dword ptr ss : [ebp + 0x08]; + __asm push eax; + __asm call [PeekVideoName]; + + // Restore eax + __asm mov eax, dword ptr ss : [ebp + 0x08]; + + __asm jmp [JmpBackAddress]; + } +} + +namespace VideoScalingDetour { + // The game doesn't expect the window to be a different dimensions than 640x480, 800x600, 1024x768 or 1280x1024. + // When the video scaling/positioning function is called with certain flags set, it'll center the video based on the window dimensions. + // This results in the video being (partly) blit outside of the surface buffer, as the buffer matches the expected dimensions and not window dimensions. + + constexpr unsigned long DetourSize = 6; + static unsigned long JmpFromAddress = 0; + static unsigned long JmpBackAddress = 0; + + // Use the internal width/height instead of the window dimensions + static BOOL __stdcall GetClientRectImpl(HWND* hWnd, RECT* rect) + { + rect->left = 0; + rect->top = 0; + rect->right = Global::InternalWidth; + rect->bottom = Global::InternalHeight; + + GetLogger()->Debug("GetClientRect override: %ix%i\n", rect->right, rect->bottom); + + return TRUE; + } + + static __declspec(naked) void Implementation() + { + __asm call [GetClientRectImpl]; + __asm jmp [JmpBackAddress]; + } +} + DDrawReplacementPatch::DDrawReplacementPatch() { GetLogger()->Informational("Constructing %s\n", __func__); @@ -268,6 +370,8 @@ DDrawReplacementPatch::DDrawReplacementPatch() DInputDetourAddress = 0; WindowRegisterClassDetourAddress = 0; WindowCreateDetourAddress = 0; + VideoFormatCheckDetourAddress = 0; + VideoScalingAddress = 0; DInputAbsolutePositioningDetourAddress = 0; ForceSoftwareRendering = false; DInputReplacement = false; @@ -283,12 +387,16 @@ DDrawReplacementPatch::~DDrawReplacementPatch() bool DDrawReplacementPatch::Validate() { - return DDrawDetourAddress && DInputDetourAddress - && WindowRegisterClassDetourAddress && WindowCreateDetourAddress + return DDrawDetourAddress + && DInputDetourAddress + && WindowRegisterClassDetourAddress + && WindowCreateDetourAddress +// && VideoFormatCheckDetourAddress // Optional; The detoured function does not exist in the demo :( + && VideoScalingAddress && DInputAbsolutePositioningDetourAddress; } -void BuildKeyLookupTable() +static void BuildKeyLookupTable() { auto t = &WindowRegisterClassDetour::KeyTranslationTable; @@ -418,6 +526,19 @@ bool DDrawReplacementPatch::Apply() if (!Detour::Create(WindowCreateDetour::JmpFromAddress, WindowCreateDetour::DetourSize, (unsigned long)WindowCreateDetour::Implementation)) return false; + if (VideoFormatCheckDetourAddress != 0) + { + VideoFormatCheckDetour::JmpFromAddress = VideoFormatCheckDetourAddress; + VideoFormatCheckDetour::JmpBackAddress = VideoFormatCheckDetour::JmpFromAddress + VideoFormatCheckDetour::DetourSize; + if (!Detour::Create(VideoFormatCheckDetour::JmpFromAddress, VideoFormatCheckDetour::DetourSize, (unsigned long)VideoFormatCheckDetour::Implementation)) + return false; + } + + VideoScalingDetour::JmpFromAddress = VideoScalingAddress; + VideoScalingDetour::JmpBackAddress = VideoScalingDetour::JmpFromAddress + VideoScalingDetour::DetourSize; + if (!Detour::Create(VideoScalingDetour::JmpFromAddress, VideoScalingDetour::DetourSize, (unsigned long)VideoScalingDetour::Implementation)) + return false; + if (DInputReplacement) { DInputCreateDetour::JmpFromAddress = DInputDetourAddress; diff --git a/subtitans/ddrawreplacementpatch.h b/subtitans/ddrawreplacementpatch.h index 223316e..d6f695f 100644 --- a/subtitans/ddrawreplacementpatch.h +++ b/subtitans/ddrawreplacementpatch.h @@ -14,6 +14,11 @@ class DDrawReplacementPatch : public Patch { unsigned long DInputDetourAddress; unsigned long WindowRegisterClassDetourAddress; unsigned long WindowCreateDetourAddress; + unsigned long VideoScalingAddress; + /// + /// Optional; log which video is being prepared to play + /// + unsigned long VideoFormatCheckDetourAddress; uint32_t DInputAbsolutePositioningDetourAddress; bool ForceSoftwareRendering; bool DInputReplacement; diff --git a/subtitans/demopatcher.cpp b/subtitans/demopatcher.cpp index 92310e4..f730e72 100644 --- a/subtitans/demopatcher.cpp +++ b/subtitans/demopatcher.cpp @@ -1,7 +1,5 @@ #include "subtitans.h" -#include #include "sleepwellpatch.h" -#include "movieheapcorruptionpatch.h" #include "ddrawreplacementpatch.h" #include "demopatcher.h" @@ -13,7 +11,7 @@ DemoPatcher::DemoPatcher() { - + _registerGameVersion = 0x00100000; } DemoPatcher::~DemoPatcher() @@ -21,6 +19,12 @@ DemoPatcher::~DemoPatcher() } +bool DemoPatcher::Initialize() +{ + bool restartRequired = RestoreVersion(_registerGameVersion); + return !restartRequired; +} + void DemoPatcher::Configure() { auto sleepWellPatch = new SleepWellPatch(); @@ -37,7 +41,6 @@ void DemoPatcher::Configure() if (Global::RenderHeight == 0) Global::RenderHeight = Global::MonitorHeight; - bool isWindows7 = IsWindows7OrGreater() && !IsWindows8OrGreater(); auto renderingBackend = GetConfiguration()->GetInt32(L"FEATURE", L"Renderer", Global::RenderingBackend::Automatic); auto ddrawReplacementPatch = new DDrawReplacementPatch(); @@ -45,9 +48,10 @@ void DemoPatcher::Configure() ddrawReplacementPatch->DInputDetourAddress = 0x00701C9E; ddrawReplacementPatch->WindowRegisterClassDetourAddress = 0x00561847; ddrawReplacementPatch->WindowCreateDetourAddress = 0x00561893; + ddrawReplacementPatch->VideoFormatCheckDetourAddress = 0; + ddrawReplacementPatch->VideoScalingAddress = 0x006B0497; ddrawReplacementPatch->DInputAbsolutePositioningDetourAddress = 0x007020F0; - ddrawReplacementPatch->ForceSoftwareRendering = renderingBackend == Global::RenderingBackend::Software - || (isWindows7 && renderingBackend == Global::RenderingBackend::Automatic); // Prefer software rendering on windows 7 + ddrawReplacementPatch->ForceSoftwareRendering = renderingBackend == Global::RenderingBackend::Software; ddrawReplacementPatch->DInputReplacement = false; // Doesn't work that well with the demo _patches.push_back(ddrawReplacementPatch); diff --git a/subtitans/demopatcher.h b/subtitans/demopatcher.h index b966c12..54b2fc5 100644 --- a/subtitans/demopatcher.h +++ b/subtitans/demopatcher.h @@ -1,10 +1,14 @@ +#pragma once #include "patcher.h" class DemoPatcher : public Patcher { + uint32_t _registerGameVersion = 0; + public: DemoPatcher(); virtual ~DemoPatcher(); protected: - void Configure() override; + virtual bool Initialize() override; + virtual void Configure() override; }; \ No newline at end of file diff --git a/subtitans/device.cpp b/subtitans/device.cpp index dc11a31..2efca37 100644 --- a/subtitans/device.cpp +++ b/subtitans/device.cpp @@ -9,20 +9,21 @@ using namespace DDraw; Device::Device() { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); referenceCount = 0; } Device::~Device() { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); } // IUnknown uint32_t __stdcall Device::QueryInterface(GUID* guid, void** result) { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); + // IID_IDirectDraw4 if (guid->Data1 == 0x9C59509A && guid->Data2 == 0x39BD && guid->Data3 == 0x11D1 && @@ -42,7 +43,7 @@ uint32_t __stdcall Device::QueryInterface(GUID* guid, void** result) uint32_t __stdcall Device::AddRef() { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); referenceCount++; @@ -51,7 +52,7 @@ uint32_t __stdcall Device::AddRef() uint32_t __stdcall Device::Release() { - GetLogger()->Trace("%s (Remaining references %i)\n", __FUNCTION__, referenceCount); + TRACELOG("%s (Remaining references %i)\n", __FUNCTION__, referenceCount); if(--referenceCount == 0) delete this; @@ -60,11 +61,11 @@ uint32_t __stdcall Device::Release() } // Direct Draw -uint32_t __stdcall Device::Compact() { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } +uint32_t __stdcall Device::Compact() { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } uint32_t __stdcall Device::CreateClipper(uint32_t, IDDrawClipper** result, void*) { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); *result = new Clipper(); (*result)->AddRef(); @@ -73,26 +74,27 @@ uint32_t __stdcall Device::CreateClipper(uint32_t, IDDrawClipper** result, void* uint32_t __stdcall Device::CreatePalette(uint32_t flags, void* palette, IDDrawPalette** result, void* unused) { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); *result = new Palette(); (*result)->AddRef(); - return ((Palette*)*result)->CreatePallete(flags, (uint8_t*)palette) ? ResultCode::Ok : ResultCode::Unimplemented; + return ((Palette*)*result)->CreatePallete(flags, (uint32_t*)palette) ? ResultCode::Ok : ResultCode::Unimplemented; } uint32_t __stdcall Device::CreateSurface(DDraw::SurfaceDescription* surfaceDescription, IDDrawSurface4** result, void* unused) { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); *result = new Surface(surfaceDescription); (*result)->AddRef(); return ResultCode::Ok; } -uint32_t __stdcall Device::DuplicateSurface(void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } +uint32_t __stdcall Device::DuplicateSurface(void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } + uint32_t __stdcall Device::EnumDisplayModes(uint32_t flags, void* surfaceDescription, void* appDef, EnumDisplayModesCallBack callback) { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); if (flags != 0) { @@ -118,7 +120,7 @@ uint32_t __stdcall Device::EnumDisplayModes(uint32_t flags, void* surfaceDescrip return ResultCode::InvalidArgument; } - constexpr uint32_t bitsPerPixel[] = { 8,/* 16,*/ 32 }; + constexpr uint32_t bitsPerPixel[] = { 8, 32 }; const std::vector> displayModes = { std::make_pair(640, 480), std::make_pair(800, 600), @@ -139,7 +141,7 @@ uint32_t __stdcall Device::EnumDisplayModes(uint32_t flags, void* surfaceDescrip | SurfaceDescriptionFlag::RefreshRate | SurfaceDescriptionFlag::PixelFormat; resultSurfaceDescription.width = displayMode.first; resultSurfaceDescription.height = displayMode.second; - resultSurfaceDescription.pitch = ((displayMode.first * bbp + 31) & ~31) >> 3; //displayMode.first * (bbp / 8); + resultSurfaceDescription.pitch = ((displayMode.first * bbp + 31) & ~31) >> 3; resultSurfaceDescription.refreshRate = 0; resultSurfaceDescription.pixelFormat.size = sizeof(PixelFormat); resultSurfaceDescription.pixelFormat.flags = PixelFormatFlag::RGB; @@ -149,13 +151,16 @@ uint32_t __stdcall Device::EnumDisplayModes(uint32_t flags, void* surfaceDescrip { case 8: resultSurfaceDescription.pixelFormat.flags |= PixelFormatFlag::PalettedIndexed8; -// case 16: + break; case 32: - default: resultSurfaceDescription.pixelFormat.rBitMask = 0xFF0000; resultSurfaceDescription.pixelFormat.gBitMask = 0xFF00; resultSurfaceDescription.pixelFormat.bBitMask = 0xFF; break; + default: + GetLogger()->Critical("%s %ibbp not implemented!\n", __FUNCTION__, bbp); + UNIMPLEMENTED_EXIT(); + break; } GetLogger()->Debug("%s calling callback for %ix%i %ibpp\n", __FUNCTION__, displayMode.first, displayMode.second, bbp); @@ -170,17 +175,17 @@ uint32_t __stdcall Device::EnumDisplayModes(uint32_t flags, void* surfaceDescrip } } - GetLogger()->Trace("%s finished\n", __FUNCTION__); + TRACELOG("%s finished\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Device::EnumSurfaces(uint32_t, void*, void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Device::FlipToGDISurface() { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } +uint32_t __stdcall Device::EnumSurfaces(uint32_t, void*, void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Device::FlipToGDISurface() { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } uint32_t __stdcall Device::GetCaps(Caps* caps, Caps* helCaps) { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); memset(caps, 0, sizeof(Caps)); caps->size = sizeof(Caps); @@ -191,48 +196,63 @@ uint32_t __stdcall Device::GetCaps(Caps* caps, Caps* helCaps) return ResultCode::Ok; } -uint32_t __stdcall Device::GetDisplayMode(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Device::GetFourCCCodes(void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Device::GetGDISurface(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Device::GetMonitoryFrequency(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Device::GetScanLine(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Device::GetVerticalBlankStatus(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Device::Initialize(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } +uint32_t __stdcall Device::GetDisplayMode(void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Device::GetFourCCCodes(void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Device::GetGDISurface(void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Device::GetMonitoryFrequency(void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Device::GetScanLine(void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Device::GetVerticalBlankStatus(void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Device::Initialize(void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } uint32_t __stdcall Device::RestoreDisplayMode() { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); return ResultCode::Ok; } uint32_t __stdcall Device::SetCooperativeLevel(HWND hwnd, uint32_t flags) { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); return ResultCode::Ok; } uint32_t __stdcall Device::SetDisplayMode(uint32_t width, uint32_t height, uint32_t bitsPerPixel, uint32_t refreshRate, uint32_t flags) { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); if (Global::Backend) Global::Backend->OnDestroyPrimarySurface(); + if (Global::VideoRequested) + GetLogger()->Debug("Video %ix%ix%ibpp\n", width, height, bitsPerPixel); + else + GetLogger()->Debug("Game %ix%ix%ibpp\n", width, height, bitsPerPixel); + + Global::VideoRequested = false; + Global::InternalWidth = width; Global::InternalHeight = height; - Global::BitsPerPixel = bitsPerPixel; - SetWindowPos(Global::GameWindow, NULL, 0, 0, Global::MonitorWidth, Global::MonitorHeight, 0); + RECT rect; + if (GetWindowRect(Global::GameWindow, &rect)) + { + int32_t currWidth = rect.right - rect.left; + int32_t currHeight = rect.bottom - rect.top; + + if (currWidth != Global::MonitorWidth || currHeight != Global::MonitorHeight) + SetWindowPos(Global::GameWindow, nullptr, 0, 0, Global::MonitorWidth, Global::MonitorHeight, SWP_NOMOVE); + } - Global::VideoWorkaround = bitsPerPixel != 8 && width == 800 && height == 600; + // RenderWindow must act as a click through overlay over the game window. + ShowWindow(Global::RenderWindow, SW_SHOWNOACTIVATE); return ResultCode::Ok; } uint32_t __stdcall Device::WaitForVerticalBlank(uint32_t flag, void*) { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); if (flag == 1) WaitForSingleObject(Global::VerticalBlankEvent, 65); @@ -242,11 +262,11 @@ uint32_t __stdcall Device::WaitForVerticalBlank(uint32_t flag, void*) return ResultCode::Ok; } -uint32_t __stdcall Device::GetAvailableVidMem(void*, void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Device::GetSurfaceFromDC(void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Device::RestoreAllSurfaces() { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Device::TestCooperativeLevel() { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Device::GetDeviceIdentifier(void*,uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } +uint32_t __stdcall Device::GetAvailableVidMem(void*, void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Device::GetSurfaceFromDC(void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Device::RestoreAllSurfaces() { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Device::TestCooperativeLevel() { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Device::GetDeviceIdentifier(void*,uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } uint32_t __stdcall DirectDrawCreate(void*, IDDraw4** result, void*) { diff --git a/subtitans/gogpatcher.cpp b/subtitans/gogpatcher.cpp index 9757d15..9a56667 100644 --- a/subtitans/gogpatcher.cpp +++ b/subtitans/gogpatcher.cpp @@ -1,15 +1,15 @@ #include "subtitans.h" #include "nativeresolutionpatch.h" #include "sleepwellpatch.h" +#include "ddrawreplacementpatch.h" #include "movieheapcorruptionpatch.h" #include "scrollpatch.h" #include "missionskippatch.h" -#include "ddrawreplacementpatch.h" #include "gogpatcher.h" -GOGPatcher::GOGPatcher() +GOGPatcher::GOGPatcher(uint32_t baseOffset) { - + _baseOffset = baseOffset; } GOGPatcher::~GOGPatcher() @@ -19,38 +19,46 @@ GOGPatcher::~GOGPatcher() void GOGPatcher::Configure() { +#define RELOC_ADDR(originalAddress) originalAddress - Shared::IMAGE_BASE + _baseOffset + + Global::RenderWidth = GetConfiguration()->GetInt32(L"SETTING", L"Width", 0); + if (Global::RenderWidth == 0) + Global::RenderWidth = Global::MonitorWidth; + + Global::RenderHeight = GetConfiguration()->GetInt32(L"SETTING", L"Height", 0); + if (Global::RenderHeight == 0) + Global::RenderHeight = Global::MonitorHeight; + auto renderingBackend = GetConfiguration()->GetInt32(L"FEATURE", L"Renderer", Global::RenderingBackend::Automatic); - bool usingGOGRenderer = renderingBackend == Global::RenderingBackend::DirectDraw || renderingBackend == Global::RenderingBackend::Automatic; // Always enable the improved frames per second limiter when DirectDraw isn't used // The alternative renderers are listening to the events published by SleepWellPatch - if (!usingGOGRenderer || GetConfiguration()->GetBoolean(L"FIX", L"ImprovedFPSLimiter", true)) + if (renderingBackend != Global::RenderingBackend::DirectDraw || + GetConfiguration()->GetBoolean(L"FIX", L"ImprovedFPSLimiter", true)) { auto sleepWellPatch = new SleepWellPatch(); - sleepWellPatch->DetourAddress = 0x006E5064; - sleepWellPatch->FrameLimitMemoryAddress = 0x00807654; - sleepWellPatch->DisableOriginalLimiterSleepAddress = 0x006E508B; + sleepWellPatch->DetourAddress = RELOC_ADDR(0x006E5064); + sleepWellPatch->FrameLimitMemoryAddress = RELOC_ADDR(0x00807654); + sleepWellPatch->DisableOriginalLimiterSleepAddress = RELOC_ADDR(0x006E508B); _patches.push_back(sleepWellPatch); } - if(!usingGOGRenderer) + if (renderingBackend == Global::RenderingBackend::DirectDraw) + { + GetLogger()->Informational("Using DirectDraw\n"); + } + else { GetLogger()->Informational("Using a DirectDraw replacement\n"); - Global::RenderWidth = GetConfiguration()->GetInt32(L"SETTING", L"Width", 0); - if (Global::RenderWidth == 0) - Global::RenderWidth = Global::MonitorWidth; - - Global::RenderHeight = GetConfiguration()->GetInt32(L"SETTING", L"Height", 0); - if (Global::RenderHeight == 0) - Global::RenderHeight = Global::MonitorHeight; - auto ddrawReplacementPatch = new DDrawReplacementPatch(); - ddrawReplacementPatch->DDrawDetourAddress = 0x006B9981; - ddrawReplacementPatch->DInputDetourAddress = 0x0071B2BE; - ddrawReplacementPatch->WindowRegisterClassDetourAddress = 0x0056AEC7; - ddrawReplacementPatch->WindowCreateDetourAddress = 0x0056AF13; - ddrawReplacementPatch->DInputAbsolutePositioningDetourAddress = 0x0071B6CC; + ddrawReplacementPatch->DDrawDetourAddress = RELOC_ADDR(0x006B9981); + ddrawReplacementPatch->DInputDetourAddress = RELOC_ADDR(0x0071B2BE); + ddrawReplacementPatch->WindowRegisterClassDetourAddress = RELOC_ADDR(0x0056AEC7); + ddrawReplacementPatch->WindowCreateDetourAddress = RELOC_ADDR(0x0056AF13); + ddrawReplacementPatch->VideoFormatCheckDetourAddress = RELOC_ADDR(0x006C2A00); + ddrawReplacementPatch->VideoScalingAddress = RELOC_ADDR(0x006C3D97); + ddrawReplacementPatch->DInputAbsolutePositioningDetourAddress = RELOC_ADDR(0x0071B6CC); ddrawReplacementPatch->ForceSoftwareRendering = renderingBackend == Global::RenderingBackend::Software; ddrawReplacementPatch->DInputReplacement = GetConfiguration()->GetBoolean(L"FEATURE", L"CustomInput", true); _patches.push_back(ddrawReplacementPatch); @@ -61,83 +69,83 @@ void GOGPatcher::Configure() if (GetConfiguration()->GetBoolean(L"FEATURE", L"NativeResolution", true)) { auto nativeResolutionPatch = new NativeResolutionPatch(); - nativeResolutionPatch->GuiRescalerAddress = 0x004F19E4; - nativeResolutionPatch->QueueScreenAddress = 0x004F845B; - nativeResolutionPatch->ScreenInitialResizeWidthAddress = 0x005307EB; - nativeResolutionPatch->ScreenInitialResizeHeightAddress = 0x005307F2; - nativeResolutionPatch->ScreenResizeWidthCompareAddress = 0x0056D78A; - nativeResolutionPatch->ScreenResizeWidthAddress = 0x0056D7F5; - nativeResolutionPatch->ScreenResizeHeightAddress = 0x0056D7FF; - nativeResolutionPatch->GamefieldPresetWidthAddress = 0x0056B1CC; - nativeResolutionPatch->GamefieldPresetHeightAddress = 0x0056B1D6; - nativeResolutionPatch->GamefieldHeightReducingAddress = 0x004F7057; - nativeResolutionPatch->GamefieldHeightRestorationAddress = 0x004FB487; - nativeResolutionPatch->MovieWidthAddress = 0x00570515; - nativeResolutionPatch->MovieHeightAddress = 0x0057051C; - nativeResolutionPatch->RepositionBottomMenuDetourAddress = 0x004F6814; - nativeResolutionPatch->RenameSettingsDetourAddress = 0x0052F190; - nativeResolutionPatch->RenameSettingsFunctionAddress = 0x00711B70; - nativeResolutionPatch->RedesignFrameDetourAddress = 0x00543032; - nativeResolutionPatch->RedesignFrameTeamIdMemoryAddress = 0x0080874E; - nativeResolutionPatch->RedesignFrameDrawFunctionAddress = 0x00403738; - nativeResolutionPatch->RepositionBriefingDetourAddress = 0x004F6AB2; - nativeResolutionPatch->CurrentScreenWidthAddress = 0x00806730; + nativeResolutionPatch->GuiRescalerAddress = RELOC_ADDR(0x004F19E4); + nativeResolutionPatch->QueueScreenAddress = RELOC_ADDR(0x004F845B); + nativeResolutionPatch->ScreenInitialResizeWidthAddress = RELOC_ADDR(0x005307EB); + nativeResolutionPatch->ScreenInitialResizeHeightAddress = RELOC_ADDR(0x005307F2); + nativeResolutionPatch->ScreenResizeWidthCompareAddress = RELOC_ADDR(0x0056D78A); + nativeResolutionPatch->ScreenResizeWidthAddress = RELOC_ADDR(0x0056D7F5); + nativeResolutionPatch->ScreenResizeHeightAddress = RELOC_ADDR(0x0056D7FF); + nativeResolutionPatch->GamefieldPresetWidthAddress = RELOC_ADDR(0x0056B1CC); + nativeResolutionPatch->GamefieldPresetHeightAddress = RELOC_ADDR(0x0056B1D6); + nativeResolutionPatch->GamefieldHeightReducingAddress = RELOC_ADDR(0x004F7057); + nativeResolutionPatch->GamefieldHeightRestorationAddress = RELOC_ADDR(0x004FB487); + nativeResolutionPatch->RepositionBottomMenuDetourAddress = RELOC_ADDR(0x004F6814); + nativeResolutionPatch->RenameSettingsDetourAddress = RELOC_ADDR(0x0052F190); + nativeResolutionPatch->RenameSettingsFunctionAddress = RELOC_ADDR(0x00711B70); + nativeResolutionPatch->RedesignFrameDetourAddress = RELOC_ADDR(0x00543032); + nativeResolutionPatch->RedesignFrameTeamIdMemoryAddress = RELOC_ADDR(0x0080874E); + nativeResolutionPatch->RedesignFrameDrawFunctionAddress = RELOC_ADDR(0x00403738); + nativeResolutionPatch->RepositionBriefingDetourAddress = RELOC_ADDR(0x004F6AB2); + nativeResolutionPatch->CurrentScreenWidthAddress = RELOC_ADDR(0x00806730); _patches.push_back(nativeResolutionPatch); } if (GetConfiguration()->GetBoolean(L"FIX", L"MovieHeap", true)) { auto movieHeapCorruptionPatch = new MovieHeapCorruptionPatch(); - movieHeapCorruptionPatch->AllocatedMemoryOffset = 0x00856900; - movieHeapCorruptionPatch->StructurePointer = 0x006D5923 + 3; - movieHeapCorruptionPatch->StructureOffsets[0] = 0x006D59B0 + 1; - movieHeapCorruptionPatch->StructureOffsets[1] = 0x006D5A0C + 1; - movieHeapCorruptionPatch->StructureOffsets[2] = 0x006D5A11 + 1; - movieHeapCorruptionPatch->StructureOffsets[3] = 0x006D5A21 + 2; - movieHeapCorruptionPatch->StructureOffsets[4] = 0x006D5A2D + 2; - movieHeapCorruptionPatch->StructureOffsets[5] = 0x006D5A33 + 1; - movieHeapCorruptionPatch->StructureOffsets[6] = 0x006D5A38 + 1; - movieHeapCorruptionPatch->StructureOffsets[7] = 0x006D5A43 + 2; - movieHeapCorruptionPatch->StructureOffsets[8] = 0x006D5A4F + 2; - movieHeapCorruptionPatch->StructureOffsets[9] = 0x006D5A55 + 2; - movieHeapCorruptionPatch->StructureOffsets[10] = 0x006D5A65 + 2; - movieHeapCorruptionPatch->StructureOffsets[11] = 0x006D5A73 + 2; - movieHeapCorruptionPatch->StructureOffsets[12] = 0x006D5A79 + 3; - movieHeapCorruptionPatch->StructureOffsets[13] = 0x006D5A84 + 1; - movieHeapCorruptionPatch->StructureOffsets[14] = 0x006D5A89 + 3; - movieHeapCorruptionPatch->StructureOffsets[15] = 0x006D5A90 + 1; - movieHeapCorruptionPatch->StructureOffsets[16] = 0x006D5AB4 + 1; - movieHeapCorruptionPatch->StructureOffsets[17] = 0x006D5ABE + 2; - movieHeapCorruptionPatch->StructureOffsets[18] = 0x006D5AD8 + 1; - movieHeapCorruptionPatch->StructureOffsets[19] = 0x006D5AE2 + 1; - movieHeapCorruptionPatch->StructureOffsets[20] = 0x006D5AE7 + 2; - movieHeapCorruptionPatch->StructureOffsets[21] = 0x006D5B1D + 1; - movieHeapCorruptionPatch->StructureOffsets[22] = 0x006D5B2B + 2; - movieHeapCorruptionPatch->StructureOffsets[23] = 0x006D5B37 + 2; - movieHeapCorruptionPatch->StructureOffsets[24] = 0x006D5B41 + 2; - movieHeapCorruptionPatch->StructureOffsets[25] = 0x006D5BDE + 1; - movieHeapCorruptionPatch->DetourAddress = 0x006D599C; + movieHeapCorruptionPatch->AllocatedMemoryOffset = RELOC_ADDR(0x00856900); + movieHeapCorruptionPatch->StructurePointer = RELOC_ADDR(0x006D5923 + 3); + movieHeapCorruptionPatch->StructureOffsets[0] = RELOC_ADDR(0x006D59B0 + 1); + movieHeapCorruptionPatch->StructureOffsets[1] = RELOC_ADDR(0x006D5A0C + 1); + movieHeapCorruptionPatch->StructureOffsets[2] = RELOC_ADDR(0x006D5A11 + 1); + movieHeapCorruptionPatch->StructureOffsets[3] = RELOC_ADDR(0x006D5A21 + 2); + movieHeapCorruptionPatch->StructureOffsets[4] = RELOC_ADDR(0x006D5A2D + 2); + movieHeapCorruptionPatch->StructureOffsets[5] = RELOC_ADDR(0x006D5A33 + 1); + movieHeapCorruptionPatch->StructureOffsets[6] = RELOC_ADDR(0x006D5A38 + 1); + movieHeapCorruptionPatch->StructureOffsets[7] = RELOC_ADDR(0x006D5A43 + 2); + movieHeapCorruptionPatch->StructureOffsets[8] = RELOC_ADDR(0x006D5A4F + 2); + movieHeapCorruptionPatch->StructureOffsets[9] = RELOC_ADDR(0x006D5A55 + 2); + movieHeapCorruptionPatch->StructureOffsets[10] = RELOC_ADDR(0x006D5A65 + 2); + movieHeapCorruptionPatch->StructureOffsets[11] = RELOC_ADDR(0x006D5A73 + 2); + movieHeapCorruptionPatch->StructureOffsets[12] = RELOC_ADDR(0x006D5A79 + 3); + movieHeapCorruptionPatch->StructureOffsets[13] = RELOC_ADDR(0x006D5A84 + 1); + movieHeapCorruptionPatch->StructureOffsets[14] = RELOC_ADDR(0x006D5A89 + 3); + movieHeapCorruptionPatch->StructureOffsets[15] = RELOC_ADDR(0x006D5A90 + 1); + movieHeapCorruptionPatch->StructureOffsets[16] = RELOC_ADDR(0x006D5AB4 + 1); + movieHeapCorruptionPatch->StructureOffsets[17] = RELOC_ADDR(0x006D5ABE + 2); + movieHeapCorruptionPatch->StructureOffsets[18] = RELOC_ADDR(0x006D5AD8 + 1); + movieHeapCorruptionPatch->StructureOffsets[19] = RELOC_ADDR(0x006D5AE2 + 1); + movieHeapCorruptionPatch->StructureOffsets[20] = RELOC_ADDR(0x006D5AE7 + 2); + movieHeapCorruptionPatch->StructureOffsets[21] = RELOC_ADDR(0x006D5B1D + 1); + movieHeapCorruptionPatch->StructureOffsets[22] = RELOC_ADDR(0x006D5B2B + 2); + movieHeapCorruptionPatch->StructureOffsets[23] = RELOC_ADDR(0x006D5B37 + 2); + movieHeapCorruptionPatch->StructureOffsets[24] = RELOC_ADDR(0x006D5B41 + 2); + movieHeapCorruptionPatch->StructureOffsets[25] = RELOC_ADDR(0x006D5BDE + 1); + movieHeapCorruptionPatch->DetourAddress = RELOC_ADDR(0x006D599C); _patches.push_back(movieHeapCorruptionPatch); } if (GetConfiguration()->GetBoolean(L"FIX", L"SmoothScroll", true)) { auto scrollPatch = new ScrollPatch(); - scrollPatch->UpdateRateAddress = 0x004AB083 + 2; - scrollPatch->DetourAddress = 0x004AB0EF; - scrollPatch->OriginalSpeedModifiersAddress = 0x007AC584; + scrollPatch->UpdateRateAddress = RELOC_ADDR(0x004AB083 + 2); + scrollPatch->DetourAddress = RELOC_ADDR(0x004AB0EF); + scrollPatch->OriginalSpeedModifiersAddress = RELOC_ADDR(0x007AC584); _patches.push_back(scrollPatch); } if (GetConfiguration()->GetBoolean(L"FEATURE", L"MissionSkipCheat", true)) { auto missionSkipPatch = new MissionSkipPatch(); - missionSkipPatch->AddCheatCodeDetourAddress = 0x00522B66; - missionSkipPatch->AddCheatCodeAlternativeReturnAddress = 0x00522B85; - missionSkipPatch->FullMapPathDetourAddress = 0x00593442; - missionSkipPatch->RelativeMapPathDetourAddress = 0x00593476; - missionSkipPatch->CurrentMapNameVariable = 0x0080EF1E; - missionSkipPatch->CheatValidationFunctionAddress = 0x0072E6F0; + missionSkipPatch->AddCheatCodeDetourAddress = RELOC_ADDR(0x00522B66); + missionSkipPatch->AddCheatCodeAlternativeReturnAddress = RELOC_ADDR(0x00522B85); + missionSkipPatch->FullMapPathDetourAddress = RELOC_ADDR(0x00593442); + missionSkipPatch->RelativeMapPathDetourAddress = RELOC_ADDR(0x00593476); + missionSkipPatch->CurrentMapNameVariable = RELOC_ADDR(0x0080EF1E); + missionSkipPatch->CheatValidationFunctionAddress = RELOC_ADDR(0x0072E6F0); _patches.push_back(missionSkipPatch); } + +#undef RELOC_ADDR } \ No newline at end of file diff --git a/subtitans/gogpatcher.h b/subtitans/gogpatcher.h index 0cce26e..21a023f 100644 --- a/subtitans/gogpatcher.h +++ b/subtitans/gogpatcher.h @@ -1,8 +1,11 @@ +#pragma once #include "patcher.h" class GOGPatcher : public Patcher { + uint32_t _baseOffset; + public: - GOGPatcher(); + GOGPatcher(uint32_t baseOffset); virtual ~GOGPatcher(); protected: diff --git a/subtitans/iddrawpalette.h b/subtitans/iddrawpalette.h index b0cc6b3..690877d 100644 --- a/subtitans/iddrawpalette.h +++ b/subtitans/iddrawpalette.h @@ -23,8 +23,8 @@ namespace DDraw { // Direct Draw Palette virtual uint32_t __stdcall GetCaps(void*) = 0; - virtual uint32_t __stdcall GetEntries(uint32_t, uint32_t, uint32_t, uint8_t*) = 0; + virtual uint32_t __stdcall GetEntries(uint32_t, uint32_t, uint32_t, uint32_t*) = 0; virtual uint32_t __stdcall Initialize(void*, uint32_t, void*) = 0; - virtual uint32_t __stdcall SetEntries(uint32_t, uint32_t, uint32_t, uint8_t*) = 0; + virtual uint32_t __stdcall SetEntries(uint32_t, uint32_t, uint32_t, uint32_t*) = 0; }; } \ No newline at end of file diff --git a/subtitans/input.cpp b/subtitans/input.cpp index 7416219..19a9afe 100644 --- a/subtitans/input.cpp +++ b/subtitans/input.cpp @@ -23,7 +23,7 @@ uint32_t __stdcall Input::QueryInterface(GUID* guid, void** result) uint32_t __stdcall Input::AddRef() { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); ReferenceCount++; @@ -32,7 +32,7 @@ uint32_t __stdcall Input::AddRef() uint32_t __stdcall Input::Release() { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); if (--ReferenceCount == 0) delete this; @@ -42,7 +42,7 @@ uint32_t __stdcall Input::Release() uint32_t __stdcall Input::CreateDevice(GUID* guid, DInput::IDInputDevice7** result, void*) { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); GUID mouseGuid; mouseGuid.Data1 = 0x6F1D2B60; @@ -79,12 +79,12 @@ uint32_t __stdcall Input::CreateDevice(GUID* guid, DInput::IDInputDevice7** resu return ResultCode::Ok; } -uint32_t __stdcall Input::EnumDevices(void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Input::GetDeviceStatus(GUID* guid) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Input::RunControlPanel(HWND, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Input::Initialize(HINSTANCE hInstance, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Input::FindDevice(GUID* guid, void* str, void* guid2) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Input::CreateDeviceEx(GUID*, GUID, void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } +uint32_t __stdcall Input::EnumDevices(void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Input::GetDeviceStatus(GUID* guid) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Input::RunControlPanel(HWND, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Input::Initialize(HINSTANCE hInstance, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Input::FindDevice(GUID* guid, void* str, void* guid2) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Input::CreateDeviceEx(GUID*, GUID, void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } uint32_t __stdcall DirectInputCreate(HINSTANCE hInstance, uint32_t version, DInput::IDInput7** result, void*) { diff --git a/subtitans/keyboarddevice.cpp b/subtitans/keyboarddevice.cpp index ef1cda1..1019e95 100644 --- a/subtitans/keyboarddevice.cpp +++ b/subtitans/keyboarddevice.cpp @@ -22,7 +22,7 @@ uint32_t __stdcall KeyboardDevice::QueryInterface(GUID* guid, void** result) uint32_t __stdcall KeyboardDevice::AddRef() { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); ReferenceCount++; @@ -31,7 +31,7 @@ uint32_t __stdcall KeyboardDevice::AddRef() uint32_t __stdcall KeyboardDevice::Release() { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); if (--ReferenceCount == 0) delete this; @@ -39,26 +39,26 @@ uint32_t __stdcall KeyboardDevice::Release() return ResultCode::Ok; } -uint32_t __stdcall KeyboardDevice::GetCapabilities(DInput::Caps* caps) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall KeyboardDevice::EnumObjects(void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall KeyboardDevice::GetProperty(GUID& guid, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall KeyboardDevice::SetProperty(GUID& guid, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } +uint32_t __stdcall KeyboardDevice::GetCapabilities(DInput::Caps* caps) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall KeyboardDevice::EnumObjects(void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall KeyboardDevice::GetProperty(GUID& guid, void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall KeyboardDevice::SetProperty(GUID& guid, void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } uint32_t __stdcall KeyboardDevice::Acquire() { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); return ResultCode::Ok; } uint32_t __stdcall KeyboardDevice::Unacquire() { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); return ResultCode::Ok; } uint32_t __stdcall KeyboardDevice::GetDeviceState(uint32_t bufferSize, void* buffer) { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); if (bufferSize != sizeof(Global::_KeyboardInformation)) { @@ -74,11 +74,11 @@ uint32_t __stdcall KeyboardDevice::GetDeviceState(uint32_t bufferSize, void* buf return ResultCode::Ok; } -uint32_t __stdcall KeyboardDevice::GetDeviceData(uint32_t, void*, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } +uint32_t __stdcall KeyboardDevice::GetDeviceData(uint32_t, void*, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } uint32_t __stdcall KeyboardDevice::SetDataFormat(DInput::DataFormat* dataFormat) { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); Initialized = false; if (dataFormat->size != sizeof(DInput::DataFormat)) @@ -104,26 +104,26 @@ uint32_t __stdcall KeyboardDevice::SetDataFormat(DInput::DataFormat* dataFormat) return ResultCode::Ok; } -uint32_t __stdcall KeyboardDevice::SetEventNotification(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } +uint32_t __stdcall KeyboardDevice::SetEventNotification(void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } uint32_t __stdcall KeyboardDevice::SetCooperativeLevel(void*, uint32_t) { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall KeyboardDevice::GetObjectInfo(void*, uint32_t, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall KeyboardDevice::GetDeviceInfo(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall KeyboardDevice::RunControlPanel(void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall KeyboardDevice::Initialize(void*, int32_t, GUID&) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall KeyboardDevice::CreateEffect(GUID&, void*, void**, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall KeyboardDevice::EnumEffects(void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall KeyboardDevice::GetEffectInfo(void*, GUID&) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall KeyboardDevice::GetForceFeedbackState(uint32_t*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall KeyboardDevice::SendForceFeedbackCommand(uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall KeyboardDevice::EnumCreatedEffectObjects(void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall KeyboardDevice::Escape(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall KeyboardDevice::Poll() { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall KeyboardDevice::SendDeviceData(void*, void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall KeyboardDevice::EnumEffectsInFile(void*, void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall KeyboardDevice::WriteEffectsToFile(void*, uint32_t, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } +uint32_t __stdcall KeyboardDevice::GetObjectInfo(void*, uint32_t, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall KeyboardDevice::GetDeviceInfo(void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall KeyboardDevice::RunControlPanel(void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall KeyboardDevice::Initialize(void*, int32_t, GUID&) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall KeyboardDevice::CreateEffect(GUID&, void*, void**, void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall KeyboardDevice::EnumEffects(void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall KeyboardDevice::GetEffectInfo(void*, GUID&) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall KeyboardDevice::GetForceFeedbackState(uint32_t*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall KeyboardDevice::SendForceFeedbackCommand(uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall KeyboardDevice::EnumCreatedEffectObjects(void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall KeyboardDevice::Escape(void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall KeyboardDevice::Poll() { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall KeyboardDevice::SendDeviceData(void*, void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall KeyboardDevice::EnumEffectsInFile(void*, void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall KeyboardDevice::WriteEffectsToFile(void*, uint32_t, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } diff --git a/subtitans/mousedevice.cpp b/subtitans/mousedevice.cpp index 160226a..b7e7de1 100644 --- a/subtitans/mousedevice.cpp +++ b/subtitans/mousedevice.cpp @@ -22,7 +22,7 @@ uint32_t __stdcall MouseDevice::QueryInterface(GUID* guid, void** result) uint32_t __stdcall MouseDevice::AddRef() { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); ReferenceCount++; @@ -31,7 +31,7 @@ uint32_t __stdcall MouseDevice::AddRef() uint32_t __stdcall MouseDevice::Release() { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); if (--ReferenceCount == 0) delete this; @@ -41,7 +41,7 @@ uint32_t __stdcall MouseDevice::Release() uint32_t __stdcall MouseDevice::GetCapabilities(DInput::Caps* caps) { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); if (caps->size != sizeof(DInput::Caps)) { @@ -57,13 +57,13 @@ uint32_t __stdcall MouseDevice::GetCapabilities(DInput::Caps* caps) return ResultCode::Ok; } -uint32_t __stdcall MouseDevice::EnumObjects(void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall MouseDevice::GetProperty(GUID& guid, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall MouseDevice::SetProperty(GUID& guid, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } +uint32_t __stdcall MouseDevice::EnumObjects(void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall MouseDevice::GetProperty(GUID& guid, void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall MouseDevice::SetProperty(GUID& guid, void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } uint32_t __stdcall MouseDevice::Acquire() { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); if (!Initialized) { @@ -76,13 +76,13 @@ uint32_t __stdcall MouseDevice::Acquire() uint32_t __stdcall MouseDevice::Unacquire() { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); return ResultCode::Ok; } uint32_t __stdcall MouseDevice::GetDeviceState(uint32_t bufferSize, void* buffer) { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); if (!Initialized) { @@ -105,11 +105,11 @@ uint32_t __stdcall MouseDevice::GetDeviceState(uint32_t bufferSize, void* buffer return ResultCode::Ok; } -uint32_t __stdcall MouseDevice::GetDeviceData(uint32_t, void*, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } +uint32_t __stdcall MouseDevice::GetDeviceData(uint32_t, void*, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } uint32_t __stdcall MouseDevice::SetDataFormat(DInput::DataFormat* dataFormat) { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); Initialized = false; if (dataFormat->size != sizeof(DInput::DataFormat)) @@ -135,26 +135,26 @@ uint32_t __stdcall MouseDevice::SetDataFormat(DInput::DataFormat* dataFormat) return ResultCode::Ok; } -uint32_t __stdcall MouseDevice::SetEventNotification(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } +uint32_t __stdcall MouseDevice::SetEventNotification(void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } uint32_t __stdcall MouseDevice::SetCooperativeLevel(void*, uint32_t) { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall MouseDevice::GetObjectInfo(void*, uint32_t, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall MouseDevice::GetDeviceInfo(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall MouseDevice::RunControlPanel(void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall MouseDevice::Initialize(void*, int32_t, GUID&) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall MouseDevice::CreateEffect(GUID&, void*, void**, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall MouseDevice::EnumEffects(void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall MouseDevice::GetEffectInfo(void*, GUID&) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall MouseDevice::GetForceFeedbackState(uint32_t*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall MouseDevice::SendForceFeedbackCommand(uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall MouseDevice::EnumCreatedEffectObjects(void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall MouseDevice::Escape(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall MouseDevice::Poll() { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall MouseDevice::SendDeviceData(void*, void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall MouseDevice::EnumEffectsInFile(void*, void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall MouseDevice::WriteEffectsToFile(void*, uint32_t, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } \ No newline at end of file +uint32_t __stdcall MouseDevice::GetObjectInfo(void*, uint32_t, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall MouseDevice::GetDeviceInfo(void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall MouseDevice::RunControlPanel(void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall MouseDevice::Initialize(void*, int32_t, GUID&) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall MouseDevice::CreateEffect(GUID&, void*, void**, void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall MouseDevice::EnumEffects(void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall MouseDevice::GetEffectInfo(void*, GUID&) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall MouseDevice::GetForceFeedbackState(uint32_t*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall MouseDevice::SendForceFeedbackCommand(uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall MouseDevice::EnumCreatedEffectObjects(void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall MouseDevice::Escape(void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall MouseDevice::Poll() { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall MouseDevice::SendDeviceData(void*, void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall MouseDevice::EnumEffectsInFile(void*, void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall MouseDevice::WriteEffectsToFile(void*, uint32_t, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } \ No newline at end of file diff --git a/subtitans/movieheapcorruptionpatch.cpp b/subtitans/movieheapcorruptionpatch.cpp index 66f6b74..9f76840 100644 --- a/subtitans/movieheapcorruptionpatch.cpp +++ b/subtitans/movieheapcorruptionpatch.cpp @@ -26,7 +26,7 @@ namespace MovieHeapCorruption { { GetLogger()->Critical("Failed to update an offset for the movie player! (Address 0x%08x)\n", address); MessageBox(NULL, L"Unexpected failure while patching the movie player", L"SubTitans", MB_ICONERROR); - ExitProcess(-1); + return Exit(); } } @@ -35,6 +35,13 @@ namespace MovieHeapCorruption { void __stdcall AllocateMemoryAndRedirectReferences() { NewAllocatedMemoryAddress = CoTaskMemAlloc(0x11A * 4); + if (NewAllocatedMemoryAddress == nullptr) + { + GetLogger()->Critical("Failed to allocate memory for the video player patch\n"); + MessageBox(NULL, L"Unexpected failure while patching the movie player", L"SubTitans", MB_ICONERROR); + return Exit(); + } + memset(NewAllocatedMemoryAddress, 0, 0x11A * 4); for (int i = 0; i < sizeof(StructurePatches) / sizeof(unsigned long); ++i) diff --git a/subtitans/nativeresolutionpatch.cpp b/subtitans/nativeresolutionpatch.cpp index 1a25132..ce1641e 100644 --- a/subtitans/nativeresolutionpatch.cpp +++ b/subtitans/nativeresolutionpatch.cpp @@ -336,9 +336,6 @@ NativeResolutionPatch::NativeResolutionPatch() GamefieldHeightReducingAddress = 0; GamefieldHeightRestorationAddress = 0; - MovieWidthAddress = 0; - MovieHeightAddress = 0; - RepositionBottomMenuDetourAddress = 0; RenameSettingsDetourAddress = 0; RenameSettingsFunctionAddress = 0; @@ -371,8 +368,6 @@ bool NativeResolutionPatch::Validate() !GamefieldPresetHeightAddress || !GamefieldHeightReducingAddress || !GamefieldHeightRestorationAddress || - !MovieWidthAddress || - !MovieHeightAddress || !RepositionBottomMenuDetourAddress || !RenameSettingsDetourAddress || !RenameSettingsFunctionAddress || @@ -451,19 +446,6 @@ bool NativeResolutionPatch::Apply() if (!MemoryWriter::Write(GamefieldHeightRestorationAddress, nopGamefieldResizeArray, 5)) return false; - - // The movie resolution patch does NOT scale the movie - // Movie resolution patch (Width) - constexpr int movieWidth = 800; - memcpy(buffer, &movieWidth, sizeof(int)); - if (!MemoryWriter::Write(MovieWidthAddress, buffer, sizeof(int))) - return false; - - // Movie resolution patch (Height) - constexpr int movieHeight = 600; - memcpy(buffer, &movieHeight, sizeof(int)); - if (!MemoryWriter::Write(MovieHeightAddress, buffer, sizeof(int))) - return false; // Patch variables NativeResolution::ControlPanelMarginLeft = (screenWidth - 1280) / 2; diff --git a/subtitans/nativeresolutionpatch.h b/subtitans/nativeresolutionpatch.h index 824c2e2..60d1106 100644 --- a/subtitans/nativeresolutionpatch.h +++ b/subtitans/nativeresolutionpatch.h @@ -27,9 +27,6 @@ class NativeResolutionPatch : public Patch { unsigned long GamefieldHeightReducingAddress; unsigned long GamefieldHeightRestorationAddress; - unsigned long MovieWidthAddress; - unsigned long MovieHeightAddress; - unsigned long RepositionBottomMenuDetourAddress; unsigned long RenameSettingsDetourAddress; unsigned long RenameSettingsFunctionAddress; diff --git a/subtitans/openglrenderer.cpp b/subtitans/openglrenderer.cpp index 8f0fc51..793d2e8 100644 --- a/subtitans/openglrenderer.cpp +++ b/subtitans/openglrenderer.cpp @@ -51,7 +51,10 @@ bool SetPixelFormat(HDC deviceContext) bool IsOpenGL3Compatible(HDC deviceContext) { if (!SetPixelFormat(deviceContext)) + { + GetLogger()->Error("%s %s\n", __FUNCTION__, "failed to set the pixel format"); return false; + } HGLRC openGLContext = wglCreateContext(deviceContext); if (!wglMakeCurrent(deviceContext, openGLContext)) @@ -79,9 +82,9 @@ bool IsOpenGL3Compatible(HDC deviceContext) bool OpenGLRenderer::Create() { - if (!Global::GameWindow) + if (!Global::RenderWindow) { - GetLogger()->Error("%s %s\n", __FUNCTION__, "GameWindow hasn't been set"); + GetLogger()->Error("%s %s\n", __FUNCTION__, "RenderWindow hasn't been set"); return false; } @@ -91,9 +94,9 @@ bool OpenGLRenderer::Create() return false; } - HDC deviceContext = GetDC(nullptr); + HDC deviceContext = GetDC(Global::RenderWindow); bool isCompatible = IsOpenGL3Compatible(deviceContext); - ReleaseDC(nullptr, deviceContext); + ReleaseDC(Global::RenderWindow, deviceContext); if (isCompatible) { @@ -125,112 +128,129 @@ void OpenGLRenderer::OnDestroyPrimarySurface() Mutex.unlock(); } -const char* vertexShader = "#version 330 core\n \ -layout (location = 0) in vec3 pos; \ -layout (location = 1) in vec2 uv; \ - \ -out vec2 surfaceUv; \ - \ -void main() \ -{ \ - gl_Position.xyz = pos; \ - gl_Position.w = 1.0; \ - \ - surfaceUv = uv; \ -} \ -"; - -const char* fragmentShader = "#version 330 core\n \ -uniform usampler2D surface; \ -uniform sampler2D palette; \ - \ -in vec2 surfaceUv; \ -out vec4 renderColor; \ - \ -void main() \ -{ \ - float idx = float(texture(surface, surfaceUv).r); \ - vec2 uv = (vec2(idx, 0.f) + 0.5f) / 256.f; \ - renderColor = vec4(texture(palette, uv).rgb, 1.f); \ -} \ -"; - -const char* fragmentShaderRetro = "#version 330 core\n \ -uniform usampler2D surface; \ -uniform sampler2D palette; \ - \ -in vec2 surfaceUv; \ -out vec4 renderColor; \ - \ -vec3 getDestColor(float x, float y) \ -{ \ - float idx = texelFetch(surface, ivec2(x, y), 0).r; \ - vec2 uv = (vec2(idx, 0.f) + 0.5f) / 256.f; \ - return texture(palette, uv).rgb; \ -} \ - \ -vec3 getMostIntense(vec3 left, vec3 right) \ -{ \ - if(left.r + left.g + left.b >= right.r + right.g + right.b) \ - { \ - return left; \ - } \ - \ - return right; \ -} \ - \ -void main() \ -{ \ - float idx = float(texture(surface, surfaceUv).r); \ - vec2 uv = (vec2(idx, 0.f) + 0.5f) / 256.f; \ - renderColor = vec4(texture(palette, uv).rgb, 1.f); \ - \ - vec2 size = textureSize(surface, 0); \ - float x = surfaceUv.x * size.x; \ - float y = surfaceUv.y * size.y; \ - \ - /* Color blending based on intensity (+ shape) */ \ - /* Top */ \ - float pos = y; \ - if(pos - 1 >= 0) { pos -= 1;} \ - vec3 topColor = getMostIntense(renderColor.rgb, getDestColor(x, pos)); \ - \ - /* Left */ \ - pos = x; \ - if(pos - 1 >= 0) { pos -= 1;} \ - vec3 leftColor = getMostIntense(renderColor.rgb, getDestColor(pos, y)); \ - \ - /* Bottom */ \ - pos = y; \ - if(pos + 1 < size.y) { pos += 1; } \ - vec3 bottomColor = getMostIntense(renderColor.rgb, getDestColor(x, pos)); \ - \ - /* Right */ \ - pos = x; \ - if(pos + 1 < size.x) { pos += 1; } \ - vec3 rightColor = getMostIntense(renderColor.rgb, getDestColor(pos, y)); \ - \ - renderColor.rgb = (renderColor.rgb * 2 + topColor + leftColor + bottomColor + rightColor) / 6.f; \ - \ - /* Scan line approximation */ \ - if(mod(floor(gl_FragCoord.y), 2) != 0) \ - { \ - float multiplier = 0.7f; \ - \ - if(topColor.r + topColor.g + topColor.b > 2.f) \ - { \ - multiplier += (topColor.r + topColor.g + topColor.b - 2.f) * 0.1f; \ - } \ - \ - if(bottomColor.r + bottomColor.g + bottomColor.b > 2) \ - { \ - multiplier += (bottomColor.r + bottomColor.g + bottomColor.b - 2.f) * 0.1f; \ - } \ - \ - renderColor.rgb *= multiplier; \ - } \ -} \ -"; +const char* vertexShader = R"( +#version 330 core +layout (location = 0) in vec3 pos; +layout (location = 1) in vec2 uv; + +out vec2 surfaceUv; + +void main() +{ + gl_Position.xyz = pos; + gl_Position.w = 1.0; + + surfaceUv = uv; +} +)"; + +const char* fragmentShader8Bit = R"( +#version 330 core +uniform usampler2D surface; +uniform sampler2D palette; + +in vec2 surfaceUv; +out vec4 renderColor; + +void main() +{ + float idx = float(texture(surface, surfaceUv).r); + vec2 uv = (vec2(idx, 0.f) + 0.5f) / 256.f; + renderColor = vec4(texture(palette, uv).rgb, 1.f); +} +)"; + +const char* fragmentShader32Bit = R"( +#version 330 core +uniform sampler2D surface; + +in vec2 surfaceUv; +out vec4 renderColor; + +void main() +{ + renderColor = vec4(texture(surface, surfaceUv).rgb, 1.f); +} +)"; + + +const char* fragmentShaderRetro8Bit = R"( +#version 330 core +uniform usampler2D surface; +uniform sampler2D palette; + +in vec2 surfaceUv; +out vec4 renderColor; + +vec3 getDestColor(float x, float y) +{ + float idx = texelFetch(surface, ivec2(x, y), 0).r; + vec2 uv = (vec2(idx, 0.f) + 0.5f) / 256.f; + return texture(palette, uv).rgb; +} + +vec3 getMostIntense(vec3 left, vec3 right) +{ + if(left.r + left.g + left.b >= right.r + right.g + right.b) + { + return left; + } + + return right; +} + +void main() +{ + float idx = float(texture(surface, surfaceUv).r); + vec2 uv = (vec2(idx, 0.f) + 0.5f) / 256.f; + renderColor = vec4(texture(palette, uv).rgb, 1.f); + + vec2 size = textureSize(surface, 0); + float x = surfaceUv.x * size.x; + float y = surfaceUv.y * size.y; + + /* Color blending based on intensity (+ shape) */ + /* Top */ + float pos = y; + if(pos - 1 >= 0) { pos -= 1;} + vec3 topColor = getMostIntense(renderColor.rgb, getDestColor(x, pos)); + + /* Left */ + pos = x; + if(pos - 1 >= 0) { pos -= 1;} + vec3 leftColor = getMostIntense(renderColor.rgb, getDestColor(pos, y)); + + /* Bottom */ + pos = y; + if(pos + 1 < size.y) { pos += 1; } + vec3 bottomColor = getMostIntense(renderColor.rgb, getDestColor(x, pos)); + + /* Right */ + pos = x; + if(pos + 1 < size.x) { pos += 1; } + vec3 rightColor = getMostIntense(renderColor.rgb, getDestColor(pos, y)); + + renderColor.rgb = (renderColor.rgb * 2 + topColor + leftColor + bottomColor + rightColor) / 6.f; + + /* Scan line approximation */ + if(mod(floor(gl_FragCoord.y), 2) != 0) + { + float multiplier = 0.7f; + + if(topColor.r + topColor.g + topColor.b > 2.f) + { + multiplier += (topColor.r + topColor.g + topColor.b - 2.f) * 0.1f; + } + + if(bottomColor.r + bottomColor.g + bottomColor.b > 2) + { + multiplier += (bottomColor.r + bottomColor.g + bottomColor.b - 2.f) * 0.1f; + } + + renderColor.rgb *= multiplier; + } +} +)"; constexpr float _squareVertexData[]{ // POS @@ -289,7 +309,7 @@ HGLRC CreateOpenGLContext(HDC deviceContext) } -uint32_t CreateShader() +uint32_t CreateShader(const char* fragmentShader) { uint32_t vertexShaderId = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertexShaderId, 1, &vertexShader, NULL); @@ -308,7 +328,7 @@ uint32_t CreateShader() } uint32_t fragmentShaderId = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(fragmentShaderId, 1, Global::RetroShader ? &fragmentShaderRetro : &fragmentShader, NULL); + glShaderSource(fragmentShaderId, 1, &fragmentShader, NULL); glCompileShader(fragmentShaderId); glGetShaderiv(fragmentShaderId, GL_COMPILE_STATUS, &compileSucceeded); if (!compileSucceeded) @@ -364,36 +384,64 @@ void InitImGui() #endif } +void OpenGLRenderer::RecalculateGLSurface() +{ + float* recalculatedSurface = new float[sizeof(_squareVertexData) / sizeof(float)]; + memcpy(recalculatedSurface, _squareVertexData, sizeof(_squareVertexData)); + + if (RenderInformation.Padding != 0) + { + recalculatedSurface[0] += (float)RenderInformation.Padding * 2 / RenderInformation.MonitorWidth; + recalculatedSurface[3] += (float)RenderInformation.Padding * 2 / RenderInformation.MonitorWidth; + recalculatedSurface[6] -= (float)RenderInformation.Padding * 2 / RenderInformation.MonitorWidth; + recalculatedSurface[9] -= (float)RenderInformation.Padding * 2 / RenderInformation.MonitorWidth; + } + + glBufferData(GL_ARRAY_BUFFER, sizeof(_squareVertexData), recalculatedSurface, GL_STATIC_DRAW); + delete[] recalculatedSurface; +} + void OpenGLRenderer::Run() { - HDC deviceContext = GetDC(nullptr); + HDC deviceContext = GetDC(Global::RenderWindow); HGLRC openGLContext = CreateOpenGLContext(deviceContext); if (openGLContext == nullptr) { GetLogger()->Error("%s %s\n", __FUNCTION__, "failed to create OpenGL context"); - ReleaseDC(nullptr, deviceContext); + ReleaseDC(Global::RenderWindow, deviceContext); + return; + } + + uint32_t shaderProgram8BitId = CreateShader(Global::RetroShader ? fragmentShaderRetro8Bit : fragmentShader8Bit); + if (shaderProgram8BitId == 0) + { + wglMakeCurrent(nullptr, nullptr); + wglDeleteContext(openGLContext); + ReleaseDC(Global::RenderWindow, deviceContext); return; } - uint32_t shaderProgramId = CreateShader(); - if (shaderProgramId == 0) + uint32_t shaderProgram32BitId = CreateShader(fragmentShader32Bit); + if (shaderProgram32BitId == 0) { + glDeleteProgram(shaderProgram8BitId); + wglMakeCurrent(nullptr, nullptr); wglDeleteContext(openGLContext); - ReleaseDC(nullptr, deviceContext); + ReleaseDC(Global::RenderWindow, deviceContext); return; } - glUseProgram(shaderProgramId); + glUseProgram(shaderProgram8BitId); + + glUniform1i(glGetUniformLocation(shaderProgram8BitId, "surface"), 0); + glUniform1i(glGetUniformLocation(shaderProgram8BitId, "palette"), 1); /* Create images */ uint32_t surfaceTextureId = 0; uint32_t paletteTextureId = 0; - glUniform1i(glGetUniformLocation(shaderProgramId, "surface"), 0); - glUniform1i(glGetUniformLocation(shaderProgramId, "palette"), 1); - glGenTextures(1, &surfaceTextureId); glGenTextures(1, &paletteTextureId); @@ -415,13 +463,82 @@ void OpenGLRenderer::Run() InitImGui(); + RenderInformation = Global::GetRenderInformation(); + RecalculateGLSurface(); + + uint8_t* surfaceBuffer = nullptr; + uint32_t surfaceBufferSize = 0; + uint8_t* paletteBuffer = new uint8_t[1024]; + memset(paletteBuffer, 0, 1024); + + int32_t currentWidth = 0; + int32_t currentHeight = 0; + int32_t currentBitsPerPixel = 0; + Mutex.lock(); while (IsThreadRunning) { Mutex.unlock(); + uint32_t drawTime = timeGetTime(); + + // Clear + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + if (surfaceBuffer) + { + // Set images + if (currentBitsPerPixel == 8 && paletteBuffer) + { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, surfaceTextureId); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_R8UI, currentWidth, currentHeight, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, surfaceBuffer); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, paletteTextureId); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 1, 0, GL_BGRA, GL_UNSIGNED_BYTE, paletteBuffer); + } + else if (currentBitsPerPixel == 32) + { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, surfaceTextureId); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, currentWidth, currentHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, surfaceBuffer); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, 0); + } + + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (void*)(12 * sizeof(float))); + + glBindBuffer(GL_ARRAY_BUFFER, vertexBufferId); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } + +#ifdef _IMGUI_ENABLED + if (Global::ImGuiEnabled) + { + ImGui_ImplOpenGL3_NewFrame(); + ImGui::NewFrame(); + + ImGui::ShowDemoWindow(); + ImGui::Render(); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + } +#endif + + SwapBuffers(deviceContext); + + drawTime = timeGetTime() - drawTime; + // Render if event has been dispatched OR if the rendering will drop under 25fps as some screens like the loading screen do not dispatch the event - WaitForSingleObject(Global::RenderEvent, 40); + WaitForSingleObject(Global::RenderEvent, 40 - min(drawTime, 40)); Mutex.lock(); @@ -430,7 +547,8 @@ void OpenGLRenderer::Run() continue; // Stop rendering if the game isn't being focussed on - if (GetForegroundWindow() != Global::GameWindow) + HWND foregroundWindow = GetForegroundWindow(); + if (foregroundWindow != Global::GameWindow && foregroundWindow != Global::RenderWindow) continue; // Prevent flickering caused by palette changes @@ -438,70 +556,69 @@ void OpenGLRenderer::Run() continue; // Can't render without palette - if (!PrimarySurface->AttachedPalette) + if (!PrimarySurface->AttachedPalette && PrimarySurface->BitsPerPixel == 8) continue; - // Clear - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + if (!surfaceBuffer) + { + const uint32_t requestedBufferSize = PrimarySurface->Stride * PrimarySurface->Height; + surfaceBuffer = new uint8_t[requestedBufferSize]; + surfaceBufferSize = requestedBufferSize; + + RecalculateGLSurface(); + RecalculateSurface = false; + } + else if (RecalculateSurface) + { + const uint32_t requestedBufferSize = PrimarySurface->Stride * PrimarySurface->Height; + if (requestedBufferSize > surfaceBufferSize) + { + delete[] surfaceBuffer; + surfaceBuffer = new uint8_t[requestedBufferSize]; + surfaceBufferSize = requestedBufferSize; + } - // Set images - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, surfaceTextureId); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + RecalculateGLSurface(); + RecalculateSurface = false; + } PrimarySurface->PrimaryDrawMutex.lock(); - glTexImage2D(GL_TEXTURE_2D, 0, GL_R8UI, PrimarySurface->Stride, PrimarySurface->Height, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, PrimarySurface->SurfaceBuffer); - PrimarySurface->PrimaryDrawMutex.unlock(); - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, paletteTextureId); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + if (PrimarySurface->BitsPerPixel == 8) + { + PrimarySurface->AttachedPalette->Mutex.lock(); + memcpy(paletteBuffer, PrimarySurface->AttachedPalette->RawPalette, sizeof(PrimarySurface->AttachedPalette->RawPalette)); + PrimarySurface->AttachedPalette->Mutex.unlock(); + } - PrimarySurface->AttachedPalette->Mutex.lock(); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 1, 0, GL_BGRA, GL_UNSIGNED_BYTE, PrimarySurface->AttachedPalette->RawPalette); - PrimarySurface->AttachedPalette->Mutex.unlock(); + memcpy(surfaceBuffer, PrimarySurface->SurfaceBuffer, PrimarySurface->Stride * PrimarySurface->Height); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (void*)(12 * sizeof(float))); + SetEvent(Global::VerticalBlankEvent); - glBindBuffer(GL_ARRAY_BUFFER, vertexBufferId); - if (RecalculateSurface) - { - float* recalculatedSurface = new float[sizeof(_squareVertexData)/sizeof(float)]; - memcpy(recalculatedSurface, _squareVertexData, sizeof(_squareVertexData)); - - if (RenderInformation.Padding != 0) - { - recalculatedSurface[0] += (float)RenderInformation.Padding * 2 / RenderInformation.MonitorWidth; - recalculatedSurface[3] += (float)RenderInformation.Padding * 2 / RenderInformation.MonitorWidth; - recalculatedSurface[6] -= (float)RenderInformation.Padding * 2 / RenderInformation.MonitorWidth; - recalculatedSurface[9] -= (float)RenderInformation.Padding * 2 / RenderInformation.MonitorWidth; - } + PrimarySurface->PrimaryDrawMutex.unlock(); - glBufferData(GL_ARRAY_BUFFER, sizeof(_squareVertexData), recalculatedSurface, GL_STATIC_DRAW); - delete[] recalculatedSurface; - RecalculateSurface = false; - } - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + currentWidth = PrimarySurface->Width; + currentHeight = PrimarySurface->Height; -#ifdef _IMGUI_ENABLED - if (Global::ImGuiEnabled) + if (currentBitsPerPixel != PrimarySurface->BitsPerPixel) { - ImGui_ImplOpenGL3_NewFrame(); - ImGui::NewFrame(); + // BBP switched + if (PrimarySurface->BitsPerPixel == 8) + { + glUseProgram(shaderProgram8BitId); - ImGui::ShowDemoWindow(); - ImGui::Render(); - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + glUniform1i(glGetUniformLocation(shaderProgram8BitId, "surface"), 0); + glUniform1i(glGetUniformLocation(shaderProgram8BitId, "palette"), 1); + } + else if (PrimarySurface->BitsPerPixel == 32) + { + glUseProgram(shaderProgram32BitId); + + glUniform1i(glGetUniformLocation(shaderProgram32BitId, "surface"), 0); + } } -#endif - - // Swap - SwapBuffers(deviceContext); - SetEvent(Global::VerticalBlankEvent); + currentBitsPerPixel = PrimarySurface->BitsPerPixel; } Mutex.unlock(); @@ -512,14 +629,21 @@ void OpenGLRenderer::Run() ImGui_ImplOpenGL3_Shutdown(); ImGui::DestroyContext(); #endif + + if (paletteBuffer) + delete[] paletteBuffer; + + if (surfaceBuffer) + delete[] surfaceBuffer; glDeleteBuffers(1, &vertexBufferId); glDeleteVertexArrays(1, &vboId); - glDeleteProgram(shaderProgramId); + glDeleteProgram(shaderProgram32BitId); + glDeleteProgram(shaderProgram8BitId); wglMakeCurrent(nullptr, nullptr); wglDeleteContext(openGLContext); - ReleaseDC(nullptr, deviceContext); + ReleaseDC(Global::RenderWindow, deviceContext); GetLogger()->Informational("%s %s\n", __FUNCTION__, "has been cleaned up"); } \ No newline at end of file diff --git a/subtitans/openglrenderer.h b/subtitans/openglrenderer.h index 28d69a6..f5d4b51 100644 --- a/subtitans/openglrenderer.h +++ b/subtitans/openglrenderer.h @@ -11,5 +11,6 @@ class OpenGLRenderer : public IRenderer { virtual void OnDestroyPrimarySurface() override; protected: + void RecalculateGLSurface(); virtual void Run() override; }; \ No newline at end of file diff --git a/subtitans/palette.cpp b/subtitans/palette.cpp index 8c5d479..81fb9eb 100644 --- a/subtitans/palette.cpp +++ b/subtitans/palette.cpp @@ -5,19 +5,19 @@ using namespace DDraw; Palette::Palette() { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); ReferenceCount = 0; memset(RawPalette, 0, sizeof(RawPalette)); } -Palette::~Palette() { GetLogger()->Trace("%s\n", __FUNCTION__); } +Palette::~Palette() { TRACELOG("%s\n", __FUNCTION__); } // IUnknown uint32_t __stdcall Palette::QueryInterface(GUID* guid, void** result) { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); GetLogger()->Error("%s %s\n", __FUNCTION__, "unknown interface"); *result = nullptr; @@ -25,7 +25,7 @@ uint32_t __stdcall Palette::QueryInterface(GUID* guid, void** result) } uint32_t __stdcall Palette::AddRef() { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); ReferenceCount++; @@ -34,7 +34,7 @@ uint32_t __stdcall Palette::AddRef() uint32_t __stdcall Palette::Release() { - GetLogger()->Trace("%s (Remaining references %i)\n", __FUNCTION__, ReferenceCount); + TRACELOG("%s (Remaining references %i)\n", __FUNCTION__, ReferenceCount); if(--ReferenceCount == 0) delete this; @@ -45,9 +45,9 @@ uint32_t __stdcall Palette::Release() // Palette uint32_t __stdcall Palette::GetCaps(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Palette::GetEntries(uint32_t shouldBeZero, uint32_t index, uint32_t count, uint8_t* result) +uint32_t __stdcall Palette::GetEntries(uint32_t shouldBeZero, uint32_t index, uint32_t count, uint32_t* result) { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); if (shouldBeZero != 0) { @@ -58,12 +58,12 @@ uint32_t __stdcall Palette::GetEntries(uint32_t shouldBeZero, uint32_t index, ui Mutex.lock(); // BGR -> RGB - for (uint32_t i = 0; i < count * 4; i += 4) + for (uint32_t i = 0; i < count; ++i) { - result[i] = RawPalette[index * 4 + i + 2]; - result[i + 1] = RawPalette[index * 4 + i + 1]; - result[i + 2] = RawPalette[index * 4 + i]; - result[i + 3] = RawPalette[index * 4 + i + 3]; + const uint32_t pixel = RawPalette[i]; + result[i] = (pixel & 0xFF00FF00) + | ((pixel << 16) & 0x00FF0000) + | ((pixel >> 16) & 0x000000FF); } Mutex.unlock(); @@ -73,9 +73,9 @@ uint32_t __stdcall Palette::GetEntries(uint32_t shouldBeZero, uint32_t index, ui uint32_t __stdcall Palette::Initialize(void*, uint32_t, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Palette::SetEntries(uint32_t shouldBeZero, uint32_t index, uint32_t count, uint8_t* input) +uint32_t __stdcall Palette::SetEntries(uint32_t shouldBeZero, uint32_t index, uint32_t count, uint32_t* input) { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); if (shouldBeZero != 0) { @@ -86,12 +86,12 @@ uint32_t __stdcall Palette::SetEntries(uint32_t shouldBeZero, uint32_t index, ui Mutex.lock(); // RGB -> BGR - for (uint32_t i = 0; i < count * 4; i += 4) + for (uint32_t i = 0; i < count; ++i) { - RawPalette[index * 4 + i] = input[i + 2]; - RawPalette[index * 4 + i + 1] = input[i + 1]; - RawPalette[index * 4 + i + 2] = input[i]; - RawPalette[index * 4 + i + 3] = input[i + 3]; + const uint32_t pixel = input[i]; + RawPalette[index + i] = (pixel & 0xFF00FF00) + | ((pixel << 16) & 0x00FF0000) + | ((pixel >> 16) & 0x000000FF); } Mutex.unlock(); @@ -99,9 +99,9 @@ uint32_t __stdcall Palette::SetEntries(uint32_t shouldBeZero, uint32_t index, ui return ResultCode::Ok; } -bool Palette::CreatePallete(uint32_t flags, uint8_t* palette) +bool Palette::CreatePallete(uint32_t flags, uint32_t* palette) { - GetLogger()->Trace("%s\n", __FUNCTION__); + TRACELOG("%s\n", __FUNCTION__); // Not implemented if (flags & PaletteCapabilityFlags::Bits_1) { GetLogger()->Error("%s %s\n", __FUNCTION__, "Bits_1"); return false; } @@ -118,12 +118,12 @@ bool Palette::CreatePallete(uint32_t flags, uint8_t* palette) Mutex.lock(); // RGB -> BGR - for (int i = 0; i < 1024; i += 4) + for (uint32_t i = 0; i < 256; ++i) { - RawPalette[i] = palette[i+2]; - RawPalette[i+1] = palette[i+1]; - RawPalette[i+2] = palette[i]; - RawPalette[i+3] = palette[i+3]; + const uint32_t pixel = palette[i]; + RawPalette[i] = (pixel & 0xFF00FF00) + | ((pixel << 16) & 0x00FF0000) + | ((pixel >> 16) & 0x000000FF); } Mutex.unlock(); diff --git a/subtitans/palette.h b/subtitans/palette.h index a943cae..be98777 100644 --- a/subtitans/palette.h +++ b/subtitans/palette.h @@ -13,17 +13,17 @@ class Palette : public DDraw::IDDrawPalette { // Direct Draw Palette virtual uint32_t __stdcall GetCaps(void*) override; - virtual uint32_t __stdcall GetEntries(uint32_t, uint32_t, uint32_t, uint8_t*) override; + virtual uint32_t __stdcall GetEntries(uint32_t, uint32_t, uint32_t, uint32_t*) override; virtual uint32_t __stdcall Initialize(void*, uint32_t, void*) override; - virtual uint32_t __stdcall SetEntries(uint32_t, uint32_t, uint32_t, uint8_t*) override; + virtual uint32_t __stdcall SetEntries(uint32_t, uint32_t, uint32_t, uint32_t*) override; // Custom private: uint32_t ReferenceCount; public: - bool CreatePallete(uint32_t flags, uint8_t* palette); + bool CreatePallete(uint32_t flags, uint32_t* palette); std::mutex Mutex; - uint8_t RawPalette[1024]; + uint32_t RawPalette[256]; }; \ No newline at end of file diff --git a/subtitans/patcher.cpp b/subtitans/patcher.cpp index 3420c5d..397bb7d 100644 --- a/subtitans/patcher.cpp +++ b/subtitans/patcher.cpp @@ -17,13 +17,14 @@ bool Patcher::Apply() { for (auto it = _patches.begin(); it != _patches.end(); ++it) { - if (!(*it)->Validate()) + auto patch = *it; + if (!patch->Validate()) { MessageBox(NULL, (*it)->ErrorMessage(), L"Patch validation error", MB_ICONERROR); return false; } - if (!(*it)->Apply()) + if (!patch->Apply()) { MessageBox(NULL, (*it)->ErrorMessage(), L"Patching error", MB_ICONERROR); return false; @@ -31,4 +32,43 @@ bool Patcher::Apply() } return true; -} \ No newline at end of file +} + +bool Patcher::RestoreVersion(uint32_t expectedVersionValue) +{ + const std::wstring registryKeyName(L"SOFTWARE\\Ellipse Studios\\Submarine Titans\\Version"); + const std::wstring versionValueName(L"version"); + + HKEY registryKey; + LSTATUS resultCode = RegOpenKeyEx(HKEY_LOCAL_MACHINE, registryKeyName.c_str(), 0, KEY_QUERY_VALUE | KEY_SET_VALUE, ®istryKey); + if (resultCode != ERROR_SUCCESS) + { + GetLogger()->Warning("Insufficient rights to read or write to the version registry key. Result: %i\n", resultCode); + return false; + } + + DWORD versionValue = 0; + DWORD dwSize = sizeof(versionValue); + DWORD dwType; + + resultCode = RegQueryValueEx(registryKey, versionValueName.c_str(), NULL, &dwType, (LPBYTE)&versionValue, &dwSize); + if (resultCode != ERROR_SUCCESS) + { + GetLogger()->Warning("Failed to query the version registry value. Result: %i\n", resultCode); + return false; + } + + bool registryModified = false; + if (versionValue != expectedVersionValue) + { + resultCode = RegSetValueEx(registryKey, versionValueName.c_str(), 0, dwType, (const BYTE*)&expectedVersionValue, dwSize); + + registryModified = resultCode == ERROR_SUCCESS; + if (!registryModified) + GetLogger()->Warning("Failed to update the version registry key. Result: %i\n", resultCode); + } + + + RegCloseKey(registryKey); + return registryModified; +} diff --git a/subtitans/patcher.h b/subtitans/patcher.h index 37232a5..0409209 100644 --- a/subtitans/patcher.h +++ b/subtitans/patcher.h @@ -2,6 +2,7 @@ #include "patch.h" class Patcher { + public: Patcher(); virtual ~Patcher(); @@ -14,4 +15,6 @@ class Patcher { protected: std::vector _patches; + + static bool RestoreVersion(uint32_t expectedVersionValue); }; diff --git a/subtitans/pescalpel.cpp b/subtitans/pescalpel.cpp new file mode 100644 index 0000000..e2c1b3a --- /dev/null +++ b/subtitans/pescalpel.cpp @@ -0,0 +1,198 @@ +#include "subtitans.h" +#include "pescalpel.h" +#include + +// I'd rather apply these modifications to this PE when it's not running, +// but I don't want to modify and distribute the game executable so there's that. + +// Grab the reloc section, we're in luck as the game hasn't been stripped of it +static IMAGE_SECTION_HEADER* FindRelocSection(IMAGE_NT_HEADERS* nt) +{ + IMAGE_SECTION_HEADER* sections = IMAGE_FIRST_SECTION(nt); + + for (int i = 0; i < nt->FileHeader.NumberOfSections; ++i) + { + char name[9] = {}; + memcpy(name, sections[i].Name, 8); + + if (strcmp(name, ".reloc") == 0) + return §ions[i]; + } + + return nullptr; +} + +// Godspeed, little executable. +static bool SwitchDynamicBaseAndNXCompatFlags(BYTE* base, bool enable) +{ + IMAGE_DOS_HEADER* dos = (IMAGE_DOS_HEADER*)base; + if (dos->e_magic != IMAGE_DOS_SIGNATURE) + { + GetLogger()->Error("What are we trying to open here?\n"); + return false; + } + + IMAGE_NT_HEADERS* nt = (IMAGE_NT_HEADERS*)(base + dos->e_lfanew); + if (nt->Signature != IMAGE_NT_SIGNATURE) + { + GetLogger()->Error("We're lacking new technology.\n"); + return false; + } + + auto& relocDir = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; + if (enable) + { + nt->OptionalHeader.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE; + nt->OptionalHeader.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_NX_COMPAT; + } + else + { + nt->OptionalHeader.DllCharacteristics &= ~IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE; + nt->OptionalHeader.DllCharacteristics &= ~IMAGE_DLLCHARACTERISTICS_NX_COMPAT; + } + + return true; +} + +static bool UpdateChecksum(const wchar_t* applicationPath, BYTE* base) +{ + DWORD headerSum; + DWORD checkSum; + + if (MapFileAndCheckSumW(applicationPath, &headerSum, &checkSum) != 0) + { + GetLogger()->Error("Failed to calculate checksum\n"); + return false; + } + + GetLogger()->Debug("Header sum: %i, Check sum: %i\n", &headerSum, &checkSum); + + IMAGE_DOS_HEADER* dos = (IMAGE_DOS_HEADER*)base; + if (dos->e_magic != IMAGE_DOS_SIGNATURE) + { + GetLogger()->Error("What are we trying to open here?\n"); + return false; + } + + IMAGE_NT_HEADERS* nt = (IMAGE_NT_HEADERS*)(base + dos->e_lfanew); + if (nt->Signature != IMAGE_NT_SIGNATURE) + { + GetLogger()->Error("We're lacking new technology.\n"); + return false; + } + + GetLogger()->Debug("PE header check sum: %i\n", nt->OptionalHeader.CheckSum); + + // Original PE contains a value of 0, GOG seems to contain an incorrect value? + // For now we'll just skip updating the check sum... + +// nt->OptionalHeader.CheckSum = newChecksum; + + return true; +} + +static bool ApplyChanges(wchar_t* applicationPath, bool enable) +{ + const wchar_t* bakExecutable = L"submarinetitans.bak"; + const wchar_t* tmpExecutable = L"submarinetitans.tmp"; + bool fileCopied = CopyFileW(applicationPath, tmpExecutable, false); + if (!fileCopied) + { + GetLogger()->Error("Failed to create submarinetitans.tmp\n"); + return false; + } + + // Step 1: Set the flags in the header, write to disk + // Step 2: Calculate Checksum and write to header + for (int i = 0; i < 2; ++i) + { + HANDLE hFile = CreateFileW( + tmpExecutable, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr + ); + + if (hFile == INVALID_HANDLE_VALUE) + return false; + + HANDLE hMap = CreateFileMappingW(hFile, nullptr, PAGE_READWRITE, 0, 0, nullptr); + if (!hMap) + { + CloseHandle(hFile); + return false; + } + + BYTE* base = (BYTE*)MapViewOfFile(hMap, FILE_MAP_WRITE, 0, 0, 0); + if (!base) + { + CloseHandle(hMap); + CloseHandle(hFile); + return false; + } + + bool result = false; + if (i == 0) + result = SwitchDynamicBaseAndNXCompatFlags(base, enable); + else + result = UpdateChecksum(tmpExecutable, base); + + if (result) + FlushViewOfFile(base, 0); + + UnmapViewOfFile(base); + + CloseHandle(hMap); + CloseHandle(hFile); + + if (!result) + return false; + } + + // Move the current running process + if (!MoveFileExW(applicationPath, bakExecutable, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)) + { + GetLogger()->Error("Failed to backup executable\n"); + return false; + } + + // Move the modified exe in place + if (!MoveFileExW(tmpExecutable, applicationPath, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)) + { + // Try to restore the running process + MoveFileExW(bakExecutable, applicationPath, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH); + + GetLogger()->Error("Failed to apply update\n"); + return false; + } + + // Delete the original PE later + MoveFileExW(bakExecutable, nullptr, MOVEFILE_REPLACE_EXISTING | MOVEFILE_DELAY_UNTIL_REBOOT); + + return true; +} + +bool PE::Scalpel::EnableDynamicBaseAndNXCompat() +{ + wchar_t applicationPath[MAX_PATH] = {}; + + auto hModule = GetModuleHandle(NULL); + if (GetModuleFileNameW(hModule, applicationPath, MAX_PATH) == 0) + return false; + + return ApplyChanges(applicationPath, true); +} + +bool PE::Scalpel::DisableDynamicBaseAndNXCompat() +{ + wchar_t applicationPath[MAX_PATH] = {}; + + auto hModule = GetModuleHandle(NULL); + if (GetModuleFileNameW(hModule, applicationPath, MAX_PATH) == 0) + return false; + + return ApplyChanges(applicationPath, false); +} diff --git a/subtitans/pescalpel.h b/subtitans/pescalpel.h new file mode 100644 index 0000000..6a97620 --- /dev/null +++ b/subtitans/pescalpel.h @@ -0,0 +1,7 @@ +#pragma once +namespace PE { + namespace Scalpel { + bool EnableDynamicBaseAndNXCompat(); + bool DisableDynamicBaseAndNXCompat(); + } +} diff --git a/subtitans/registrypatch.cpp b/subtitans/registrypatch.cpp new file mode 100644 index 0000000..27f390a --- /dev/null +++ b/subtitans/registrypatch.cpp @@ -0,0 +1,57 @@ +#include "subtitans.h" +#include "registrypatch.h" + +/* + The Technology Demo has multiple registry issues, especially on modern operating systems. + This is a lazy workaround. +*/ + +namespace RegistryDetour { + // Detour variables + constexpr unsigned long DetourSize = 6; + static unsigned long JmpFromAddress = 0; + static unsigned long JmpBackAddress = 0; + + LSTATUS __stdcall RegQueryValueExADetour(HKEY key, LPCSTR value, LPDWORD reserved, LPDWORD type, LPBYTE data, LPDWORD sizePointer) + { + if (_stricmp(value, "CDAudioDrive") == 0 && *type == REG_SZ) + { + strcpy_s((char*)data, *sizePointer, "NONE"); + return ERROR_SUCCESS; + } + + return RegQueryValueExA(key, value, reserved, type, data, sizePointer); + } + + __declspec(naked) void Implementation() + { + __asm mov esi, [RegQueryValueExADetour]; + __asm jmp [JmpBackAddress]; + } +} + +RegistryPatch::RegistryPatch() +{ + GetLogger()->Informational("Constructing %s\n", __func__); + + DetourAddress = 0; +} + +RegistryPatch::~RegistryPatch() +{ + GetLogger()->Informational("Destructing %s\n", __func__); +} + +bool RegistryPatch::Validate() +{ + return DetourAddress != 0; +} + +bool RegistryPatch::Apply() +{ + GetLogger()->Informational("%s\n", __FUNCTION__); + + RegistryDetour::JmpFromAddress = DetourAddress; + RegistryDetour::JmpBackAddress = RegistryDetour::JmpFromAddress + RegistryDetour::DetourSize; + return Detour::Create(RegistryDetour::JmpFromAddress, RegistryDetour::DetourSize, (uint32_t)RegistryDetour::Implementation); +} \ No newline at end of file diff --git a/subtitans/registrypatch.h b/subtitans/registrypatch.h new file mode 100644 index 0000000..ee737ca --- /dev/null +++ b/subtitans/registrypatch.h @@ -0,0 +1,14 @@ +#pragma once +#include "patch.h" + +class RegistryPatch : public Patch { +public: + RegistryPatch(); + virtual ~RegistryPatch(); + + bool Validate() override; + bool Apply() override; + const wchar_t* ErrorMessage() override { return L"Failed to apply the registry patch"; } + + unsigned long DetourAddress; +}; \ No newline at end of file diff --git a/subtitans/softwarerenderer.cpp b/subtitans/softwarerenderer.cpp index d77974e..fbd59cb 100644 --- a/subtitans/softwarerenderer.cpp +++ b/subtitans/softwarerenderer.cpp @@ -54,15 +54,46 @@ void SoftwareRenderer::OnDestroyPrimarySurface() void SoftwareRenderer::Run() { BITMAPINFO* primaryBitmapInfo = (BITMAPINFO*)new char[sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 256]; + memset(primaryBitmapInfo, 0, sizeof(BITMAPINFOHEADER)); + primaryBitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + primaryBitmapInfo->bmiHeader.biPlanes = 1; + primaryBitmapInfo->bmiHeader.biCompression = BI_RGB; + primaryBitmapInfo->bmiHeader.biBitCount = 8; + primaryBitmapInfo->bmiHeader.biClrUsed = 256; + primaryBitmapInfo->bmiHeader.biSizeImage = 0; + uint8_t* surfaceBuffer = nullptr; Mutex.lock(); + + Global::RenderInformation currentRenderInformation; + memset(¤tRenderInformation, 0, sizeof(Global::RenderInformation)); + + int32_t currentWidth = 0; + int32_t currentHeight = 0; + while (IsThreadRunning) { Mutex.unlock(); + uint32_t drawTime = timeGetTime(); + + if (surfaceBuffer) + { + auto dc = GetDC(Global::RenderWindow); + + if (currentRenderInformation.InternalWidth == currentRenderInformation.MonitorWidth && currentRenderInformation.InternalHeight == currentRenderInformation.MonitorHeight) + SetDIBitsToDevice(dc, 0, 0, currentWidth, currentHeight, 0, 0, 0, currentHeight, surfaceBuffer, primaryBitmapInfo, DIB_RGB_COLORS); + else + StretchDIBits(dc, currentRenderInformation.Padding, 0, currentRenderInformation.AspectRatioCompensatedWidth, currentRenderInformation.MonitorHeight, 0, 0, currentWidth, currentHeight, surfaceBuffer, primaryBitmapInfo, DIB_RGB_COLORS, SRCCOPY); + + ReleaseDC(Global::RenderWindow, dc); + } + + drawTime = timeGetTime() - drawTime; + // Render if event has been dispatched OR if the rendering will drop under 25fps as some screens like the loading screen do not dispatch the event - WaitForSingleObject(Global::RenderEvent, 40); + WaitForSingleObject(Global::RenderEvent, 40 - min(drawTime, 40)); Mutex.lock(); @@ -71,55 +102,66 @@ void SoftwareRenderer::Run() continue; // Stop rendering if the game isn't being focussed on - if (GetForegroundWindow() != Global::GameWindow) + HWND foregroundWindow = GetForegroundWindow(); + if (foregroundWindow != Global::GameWindow && foregroundWindow != Global::RenderWindow) continue; // Prevent flickering caused by palette changes if (PrimarySurface->PrimaryInvalid) continue; - memset(primaryBitmapInfo, 0, sizeof(BITMAPINFOHEADER)); - primaryBitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - primaryBitmapInfo->bmiHeader.biWidth = PrimarySurface->Stride; // Read only value - primaryBitmapInfo->bmiHeader.biHeight = -PrimarySurface->Height; // Read only value - primaryBitmapInfo->bmiHeader.biPlanes = 1; - primaryBitmapInfo->bmiHeader.biCompression = BI_RGB; - primaryBitmapInfo->bmiHeader.biBitCount = 8; - primaryBitmapInfo->bmiHeader.biClrUsed = 256; - primaryBitmapInfo->bmiHeader.biSizeImage = 0; - - PrimarySurface->PrimaryDrawMutex.lock(); + if (PrimarySurface->BitsPerPixel == 8) + { + // Can't render without palette + if (!PrimarySurface->AttachedPalette) + continue; - if (PrimarySurface->AttachedPalette) + primaryBitmapInfo->bmiHeader.biBitCount = 8; + primaryBitmapInfo->bmiHeader.biClrUsed = 256; + } + else { - PrimarySurface->AttachedPalette->Mutex.lock(); - memcpy(primaryBitmapInfo->bmiColors, PrimarySurface->AttachedPalette->RawPalette, sizeof(PrimarySurface->AttachedPalette->RawPalette)); - PrimarySurface->AttachedPalette->Mutex.unlock(); + primaryBitmapInfo->bmiHeader.biBitCount = 32; + primaryBitmapInfo->bmiHeader.biClrUsed = 0; } + primaryBitmapInfo->bmiHeader.biWidth = PrimarySurface->Width; + primaryBitmapInfo->bmiHeader.biHeight = -PrimarySurface->Height; + if (!surfaceBuffer || RecalculateSurface) { if (surfaceBuffer) + { delete[] surfaceBuffer; + auto dc = GetDC(Global::RenderWindow); + PatBlt(dc, 0, 0, Global::MonitorWidth, Global::MonitorHeight, BLACKNESS); + ReleaseDC(Global::RenderWindow, dc); + } + surfaceBuffer = new uint8_t[PrimarySurface->Stride * PrimarySurface->Height]; RecalculateSurface = false; + + memcpy(¤tRenderInformation, &RenderInformation, sizeof(Global::RenderInformation)); } - memcpy(surfaceBuffer, PrimarySurface->SurfaceBuffer, PrimarySurface->Stride * PrimarySurface->Height); + PrimarySurface->PrimaryDrawMutex.lock(); - PrimarySurface->PrimaryDrawMutex.unlock(); + if (PrimarySurface->BitsPerPixel == 8) + { + PrimarySurface->AttachedPalette->Mutex.lock(); + memcpy(primaryBitmapInfo->bmiColors, PrimarySurface->AttachedPalette->RawPalette, sizeof(PrimarySurface->AttachedPalette->RawPalette)); + PrimarySurface->AttachedPalette->Mutex.unlock(); + } - auto dc = GetDC(nullptr); + memcpy(surfaceBuffer, PrimarySurface->SurfaceBuffer, PrimarySurface->Stride * PrimarySurface->Height); - if (RenderInformation.InternalWidth == RenderInformation.MonitorWidth && RenderInformation.InternalHeight == RenderInformation.MonitorHeight) - SetDIBitsToDevice(dc, 0, 0, PrimarySurface->Width, PrimarySurface->Height, 0, 0, 0, PrimarySurface->Height, surfaceBuffer, primaryBitmapInfo, DIB_RGB_COLORS); - else - StretchDIBits(dc, RenderInformation.Padding, 0, RenderInformation.AspectRatioCompensatedWidth, RenderInformation.MonitorHeight, 0, 0, PrimarySurface->Width, PrimarySurface->Height, surfaceBuffer, primaryBitmapInfo, DIB_RGB_COLORS, SRCCOPY); + SetEvent(Global::VerticalBlankEvent); - ReleaseDC(nullptr, dc); + PrimarySurface->PrimaryDrawMutex.unlock(); - SetEvent(Global::VerticalBlankEvent); + currentWidth = PrimarySurface->Width; + currentHeight = PrimarySurface->Height; } Mutex.unlock(); diff --git a/subtitans/steampatchedpatcher.cpp b/subtitans/steampatchedpatcher.cpp index c4eef86..710340a 100644 --- a/subtitans/steampatchedpatcher.cpp +++ b/subtitans/steampatchedpatcher.cpp @@ -7,8 +7,10 @@ #include "missionskippatch.h" #include "steampatchedpatcher.h" -SteamPatchedPatcher::SteamPatchedPatcher() +SteamPatchedPatcher::SteamPatchedPatcher(uint32_t baseOffset) { + _baseOffset = baseOffset; + _registerGameVersion = 0x01010000; } SteamPatchedPatcher::~SteamPatchedPatcher() @@ -18,7 +20,15 @@ SteamPatchedPatcher::~SteamPatchedPatcher() void SteamPatchedPatcher::Configure() { - bool isWindows7 = IsWindows7OrGreater() && !IsWindows8OrGreater(); +#define RELOC_ADDR(originalAddress) originalAddress - Shared::IMAGE_BASE + _baseOffset + + Global::RenderWidth = GetConfiguration()->GetInt32(L"SETTING", L"Width", 0); + if (Global::RenderWidth == 0) + Global::RenderWidth = Global::MonitorWidth; + + Global::RenderHeight = GetConfiguration()->GetInt32(L"SETTING", L"Height", 0); + if (Global::RenderHeight == 0) + Global::RenderHeight = Global::MonitorHeight; auto renderingBackend = GetConfiguration()->GetInt32(L"FEATURE", L"Renderer", Global::RenderingBackend::Automatic); @@ -28,9 +38,9 @@ void SteamPatchedPatcher::Configure() GetConfiguration()->GetBoolean(L"FIX", L"ImprovedFPSLimiter", true)) { auto sleepWellPatch = new SleepWellPatch(); - sleepWellPatch->DetourAddress = 0x006E5064; - sleepWellPatch->FrameLimitMemoryAddress = 0x00807654; - sleepWellPatch->DisableOriginalLimiterSleepAddress = 0x006E508B; + sleepWellPatch->DetourAddress = RELOC_ADDR(0x006E5064); + sleepWellPatch->FrameLimitMemoryAddress = RELOC_ADDR(0x00807654); + sleepWellPatch->DisableOriginalLimiterSleepAddress = RELOC_ADDR(0x006E508B); _patches.push_back(sleepWellPatch); } @@ -42,22 +52,15 @@ void SteamPatchedPatcher::Configure() { GetLogger()->Informational("Using a DirectDraw replacement\n"); - Global::RenderWidth = GetConfiguration()->GetInt32(L"SETTING", L"Width", 0); - if (Global::RenderWidth == 0) - Global::RenderWidth = Global::MonitorWidth; - - Global::RenderHeight = GetConfiguration()->GetInt32(L"SETTING", L"Height", 0); - if (Global::RenderHeight == 0) - Global::RenderHeight = Global::MonitorHeight; - auto ddrawReplacementPatch = new DDrawReplacementPatch(); - ddrawReplacementPatch->DDrawDetourAddress = 0x006B9981; - ddrawReplacementPatch->DInputDetourAddress = 0x0071B2BE; - ddrawReplacementPatch->WindowRegisterClassDetourAddress = 0x0056AEC7; - ddrawReplacementPatch->WindowCreateDetourAddress = 0x0056AF13; - ddrawReplacementPatch->DInputAbsolutePositioningDetourAddress = 0x0071B6CC; - ddrawReplacementPatch->ForceSoftwareRendering = renderingBackend == Global::RenderingBackend::Software - || (isWindows7 && renderingBackend == Global::RenderingBackend::Automatic); // Prefer software rendering on windows 7 + ddrawReplacementPatch->DDrawDetourAddress = RELOC_ADDR(0x006B9981); + ddrawReplacementPatch->DInputDetourAddress = RELOC_ADDR(0x0071B2BE); + ddrawReplacementPatch->WindowRegisterClassDetourAddress = RELOC_ADDR(0x0056AEC7); + ddrawReplacementPatch->WindowCreateDetourAddress = RELOC_ADDR(0x0056AF13); + ddrawReplacementPatch->VideoFormatCheckDetourAddress = RELOC_ADDR(0x006C2A00); + ddrawReplacementPatch->VideoScalingAddress = RELOC_ADDR(0x006C3D97); + ddrawReplacementPatch->DInputAbsolutePositioningDetourAddress = RELOC_ADDR(0x0071B6CC); + ddrawReplacementPatch->ForceSoftwareRendering = renderingBackend == Global::RenderingBackend::Software; ddrawReplacementPatch->DInputReplacement = GetConfiguration()->GetBoolean(L"FEATURE", L"CustomInput", true); _patches.push_back(ddrawReplacementPatch); @@ -67,83 +70,83 @@ void SteamPatchedPatcher::Configure() if (GetConfiguration()->GetBoolean(L"FEATURE", L"NativeResolution", true)) { auto nativeResolutionPatch = new NativeResolutionPatch(); - nativeResolutionPatch->GuiRescalerAddress = 0x004F19E4; - nativeResolutionPatch->QueueScreenAddress = 0x004F845B; - nativeResolutionPatch->ScreenInitialResizeWidthAddress = 0x005307EB; - nativeResolutionPatch->ScreenInitialResizeHeightAddress = 0x005307F2; - nativeResolutionPatch->ScreenResizeWidthCompareAddress = 0x0056D78A; - nativeResolutionPatch->ScreenResizeWidthAddress = 0x0056D7F5; - nativeResolutionPatch->ScreenResizeHeightAddress = 0x0056D7FF; - nativeResolutionPatch->GamefieldPresetWidthAddress = 0x0056B1CC; - nativeResolutionPatch->GamefieldPresetHeightAddress = 0x0056B1D6; - nativeResolutionPatch->GamefieldHeightReducingAddress = 0x004F7057; - nativeResolutionPatch->GamefieldHeightRestorationAddress = 0x004FB487; - nativeResolutionPatch->MovieWidthAddress = 0x00570515; - nativeResolutionPatch->MovieHeightAddress = 0x0057051C; - nativeResolutionPatch->RepositionBottomMenuDetourAddress = 0x004F6814; - nativeResolutionPatch->RenameSettingsDetourAddress = 0x0052F190; - nativeResolutionPatch->RenameSettingsFunctionAddress = 0x00711B70; - nativeResolutionPatch->RedesignFrameDetourAddress = 0x00543032; - nativeResolutionPatch->RedesignFrameTeamIdMemoryAddress = 0x0080874E; - nativeResolutionPatch->RedesignFrameDrawFunctionAddress = 0x00403738; - nativeResolutionPatch->RepositionBriefingDetourAddress = 0x004F6AB2; - nativeResolutionPatch->CurrentScreenWidthAddress = 0x00806730; + nativeResolutionPatch->GuiRescalerAddress = RELOC_ADDR(0x004F19E4); + nativeResolutionPatch->QueueScreenAddress = RELOC_ADDR(0x004F845B); + nativeResolutionPatch->ScreenInitialResizeWidthAddress = RELOC_ADDR(0x005307EB); + nativeResolutionPatch->ScreenInitialResizeHeightAddress = RELOC_ADDR(0x005307F2); + nativeResolutionPatch->ScreenResizeWidthCompareAddress = RELOC_ADDR(0x0056D78A); + nativeResolutionPatch->ScreenResizeWidthAddress = RELOC_ADDR(0x0056D7F5); + nativeResolutionPatch->ScreenResizeHeightAddress = RELOC_ADDR(0x0056D7FF); + nativeResolutionPatch->GamefieldPresetWidthAddress = RELOC_ADDR(0x0056B1CC); + nativeResolutionPatch->GamefieldPresetHeightAddress = RELOC_ADDR(0x0056B1D6); + nativeResolutionPatch->GamefieldHeightReducingAddress = RELOC_ADDR(0x004F7057); + nativeResolutionPatch->GamefieldHeightRestorationAddress = RELOC_ADDR(0x004FB487); + nativeResolutionPatch->RepositionBottomMenuDetourAddress = RELOC_ADDR(0x004F6814); + nativeResolutionPatch->RenameSettingsDetourAddress = RELOC_ADDR(0x0052F190); + nativeResolutionPatch->RenameSettingsFunctionAddress = RELOC_ADDR(0x00711B70); + nativeResolutionPatch->RedesignFrameDetourAddress = RELOC_ADDR(0x00543032); + nativeResolutionPatch->RedesignFrameTeamIdMemoryAddress = RELOC_ADDR(0x0080874E); + nativeResolutionPatch->RedesignFrameDrawFunctionAddress = RELOC_ADDR(0x00403738); + nativeResolutionPatch->RepositionBriefingDetourAddress = RELOC_ADDR(0x004F6AB2); + nativeResolutionPatch->CurrentScreenWidthAddress = RELOC_ADDR(0x00806730); _patches.push_back(nativeResolutionPatch); } if (GetConfiguration()->GetBoolean(L"FIX", L"MovieHeap", true)) { auto movieHeapCorruptionPatch = new MovieHeapCorruptionPatch(); - movieHeapCorruptionPatch->AllocatedMemoryOffset = 0x00856900; - movieHeapCorruptionPatch->StructurePointer = 0x006D5923 + 3; - movieHeapCorruptionPatch->StructureOffsets[0] = 0x006D59B0 + 1; - movieHeapCorruptionPatch->StructureOffsets[1] = 0x006D5A0C + 1; - movieHeapCorruptionPatch->StructureOffsets[2] = 0x006D5A11 + 1; - movieHeapCorruptionPatch->StructureOffsets[3] = 0x006D5A21 + 2; - movieHeapCorruptionPatch->StructureOffsets[4] = 0x006D5A2D + 2; - movieHeapCorruptionPatch->StructureOffsets[5] = 0x006D5A33 + 1; - movieHeapCorruptionPatch->StructureOffsets[6] = 0x006D5A38 + 1; - movieHeapCorruptionPatch->StructureOffsets[7] = 0x006D5A43 + 2; - movieHeapCorruptionPatch->StructureOffsets[8] = 0x006D5A4F + 2; - movieHeapCorruptionPatch->StructureOffsets[9] = 0x006D5A55 + 2; - movieHeapCorruptionPatch->StructureOffsets[10] = 0x006D5A65 + 2; - movieHeapCorruptionPatch->StructureOffsets[11] = 0x006D5A73 + 2; - movieHeapCorruptionPatch->StructureOffsets[12] = 0x006D5A79 + 3; - movieHeapCorruptionPatch->StructureOffsets[13] = 0x006D5A84 + 1; - movieHeapCorruptionPatch->StructureOffsets[14] = 0x006D5A89 + 3; - movieHeapCorruptionPatch->StructureOffsets[15] = 0x006D5A90 + 1; - movieHeapCorruptionPatch->StructureOffsets[16] = 0x006D5AB4 + 1; - movieHeapCorruptionPatch->StructureOffsets[17] = 0x006D5ABE + 2; - movieHeapCorruptionPatch->StructureOffsets[18] = 0x006D5AD8 + 1; - movieHeapCorruptionPatch->StructureOffsets[19] = 0x006D5AE2 + 1; - movieHeapCorruptionPatch->StructureOffsets[20] = 0x006D5AE7 + 2; - movieHeapCorruptionPatch->StructureOffsets[21] = 0x006D5B1D + 1; - movieHeapCorruptionPatch->StructureOffsets[22] = 0x006D5B2B + 2; - movieHeapCorruptionPatch->StructureOffsets[23] = 0x006D5B37 + 2; - movieHeapCorruptionPatch->StructureOffsets[24] = 0x006D5B41 + 2; - movieHeapCorruptionPatch->StructureOffsets[25] = 0x006D5BDE + 1; - movieHeapCorruptionPatch->DetourAddress = 0x006D599C; + movieHeapCorruptionPatch->AllocatedMemoryOffset = RELOC_ADDR(0x00856900); + movieHeapCorruptionPatch->StructurePointer = RELOC_ADDR(0x006D5923 + 3); + movieHeapCorruptionPatch->StructureOffsets[0] = RELOC_ADDR(0x006D59B0 + 1); + movieHeapCorruptionPatch->StructureOffsets[1] = RELOC_ADDR(0x006D5A0C + 1); + movieHeapCorruptionPatch->StructureOffsets[2] = RELOC_ADDR(0x006D5A11 + 1); + movieHeapCorruptionPatch->StructureOffsets[3] = RELOC_ADDR(0x006D5A21 + 2); + movieHeapCorruptionPatch->StructureOffsets[4] = RELOC_ADDR(0x006D5A2D + 2); + movieHeapCorruptionPatch->StructureOffsets[5] = RELOC_ADDR(0x006D5A33 + 1); + movieHeapCorruptionPatch->StructureOffsets[6] = RELOC_ADDR(0x006D5A38 + 1); + movieHeapCorruptionPatch->StructureOffsets[7] = RELOC_ADDR(0x006D5A43 + 2); + movieHeapCorruptionPatch->StructureOffsets[8] = RELOC_ADDR(0x006D5A4F + 2); + movieHeapCorruptionPatch->StructureOffsets[9] = RELOC_ADDR(0x006D5A55 + 2); + movieHeapCorruptionPatch->StructureOffsets[10] = RELOC_ADDR(0x006D5A65 + 2); + movieHeapCorruptionPatch->StructureOffsets[11] = RELOC_ADDR(0x006D5A73 + 2); + movieHeapCorruptionPatch->StructureOffsets[12] = RELOC_ADDR(0x006D5A79 + 3); + movieHeapCorruptionPatch->StructureOffsets[13] = RELOC_ADDR(0x006D5A84 + 1); + movieHeapCorruptionPatch->StructureOffsets[14] = RELOC_ADDR(0x006D5A89 + 3); + movieHeapCorruptionPatch->StructureOffsets[15] = RELOC_ADDR(0x006D5A90 + 1); + movieHeapCorruptionPatch->StructureOffsets[16] = RELOC_ADDR(0x006D5AB4 + 1); + movieHeapCorruptionPatch->StructureOffsets[17] = RELOC_ADDR(0x006D5ABE + 2); + movieHeapCorruptionPatch->StructureOffsets[18] = RELOC_ADDR(0x006D5AD8 + 1); + movieHeapCorruptionPatch->StructureOffsets[19] = RELOC_ADDR(0x006D5AE2 + 1); + movieHeapCorruptionPatch->StructureOffsets[20] = RELOC_ADDR(0x006D5AE7 + 2); + movieHeapCorruptionPatch->StructureOffsets[21] = RELOC_ADDR(0x006D5B1D + 1); + movieHeapCorruptionPatch->StructureOffsets[22] = RELOC_ADDR(0x006D5B2B + 2); + movieHeapCorruptionPatch->StructureOffsets[23] = RELOC_ADDR(0x006D5B37 + 2); + movieHeapCorruptionPatch->StructureOffsets[24] = RELOC_ADDR(0x006D5B41 + 2); + movieHeapCorruptionPatch->StructureOffsets[25] = RELOC_ADDR(0x006D5BDE + 1); + movieHeapCorruptionPatch->DetourAddress = RELOC_ADDR(0x006D599C); _patches.push_back(movieHeapCorruptionPatch); } if (GetConfiguration()->GetBoolean(L"FIX", L"SmoothScroll", true)) { auto scrollPatch = new ScrollPatch(); - scrollPatch->UpdateRateAddress = 0x004AB083 + 2; - scrollPatch->DetourAddress = 0x004AB0EF; - scrollPatch->OriginalSpeedModifiersAddress = 0x007AC584; + scrollPatch->UpdateRateAddress = RELOC_ADDR(0x004AB083 + 2); + scrollPatch->DetourAddress = RELOC_ADDR(0x004AB0EF); + scrollPatch->OriginalSpeedModifiersAddress = RELOC_ADDR(0x007AC584); _patches.push_back(scrollPatch); } if (GetConfiguration()->GetBoolean(L"FEATURE", L"MissionSkipCheat", true)) { auto missionSkipPatch = new MissionSkipPatch(); - missionSkipPatch->AddCheatCodeDetourAddress = 0x00522B66; - missionSkipPatch->AddCheatCodeAlternativeReturnAddress = 0x00522B85; - missionSkipPatch->FullMapPathDetourAddress = 0x00593442; - missionSkipPatch->RelativeMapPathDetourAddress = 0x00593476; - missionSkipPatch->CurrentMapNameVariable = 0x0080EF1E; - missionSkipPatch->CheatValidationFunctionAddress = 0x0072E6F0; + missionSkipPatch->AddCheatCodeDetourAddress = RELOC_ADDR(0x00522B66); + missionSkipPatch->AddCheatCodeAlternativeReturnAddress = RELOC_ADDR(0x00522B85); + missionSkipPatch->FullMapPathDetourAddress = RELOC_ADDR(0x00593442); + missionSkipPatch->RelativeMapPathDetourAddress = RELOC_ADDR(0x00593476); + missionSkipPatch->CurrentMapNameVariable = RELOC_ADDR(0x0080EF1E); + missionSkipPatch->CheatValidationFunctionAddress = RELOC_ADDR(0x0072E6F0); _patches.push_back(missionSkipPatch); } + +#undef RELOC_ADDR } \ No newline at end of file diff --git a/subtitans/steampatchedpatcher.h b/subtitans/steampatchedpatcher.h index b127b30..b424e52 100644 --- a/subtitans/steampatchedpatcher.h +++ b/subtitans/steampatchedpatcher.h @@ -2,8 +2,10 @@ #include "steampatcher.h" class SteamPatchedPatcher : public SteamPatcher { + uint32_t _baseOffset; + public: - SteamPatchedPatcher(); + SteamPatchedPatcher(uint32_t baseOffset); virtual ~SteamPatchedPatcher(); protected: diff --git a/subtitans/steampatcher.cpp b/subtitans/steampatcher.cpp index 9c5b8d4..a8b1023 100644 --- a/subtitans/steampatcher.cpp +++ b/subtitans/steampatcher.cpp @@ -61,6 +61,7 @@ namespace Steam { SteamPatcher::SteamPatcher() { + _registerGameVersion = 0x01000000; } SteamPatcher::~SteamPatcher() @@ -75,14 +76,22 @@ bool SteamPatcher::Initialize() restartRequired |= Steam::RemoveFile(L"dplayx.dll"); restartRequired |= Steam::RemoveFile(L"steam_installscript.vdf"); restartRequired |= Steam::DisableCompatibilityMode(); + restartRequired |= RestoreVersion(_registerGameVersion); return !restartRequired; } void SteamPatcher::Configure() { - bool isWindows7 = IsWindows7OrGreater() && !IsWindows8OrGreater(); + Global::RenderWidth = GetConfiguration()->GetInt32(L"SETTING", L"Width", 0); + if (Global::RenderWidth == 0) + Global::RenderWidth = Global::MonitorWidth; + + Global::RenderHeight = GetConfiguration()->GetInt32(L"SETTING", L"Height", 0); + if (Global::RenderHeight == 0) + Global::RenderHeight = Global::MonitorHeight; + bool isWindows7 = IsWindows7OrGreater() && !IsWindows8OrGreater(); auto renderingBackend = GetConfiguration()->GetInt32(L"FEATURE", L"Renderer", Global::RenderingBackend::Automatic); // Always enable the improved frames per second limiter when DirectDraw isn't used @@ -105,19 +114,13 @@ void SteamPatcher::Configure() { GetLogger()->Informational("Using a DirectDraw replacement\n"); - Global::RenderWidth = GetConfiguration()->GetInt32(L"SETTING", L"Width", 0); - if (Global::RenderWidth == 0) - Global::RenderWidth = Global::MonitorWidth; - - Global::RenderHeight = GetConfiguration()->GetInt32(L"SETTING", L"Height", 0); - if (Global::RenderHeight == 0) - Global::RenderHeight = Global::MonitorHeight; - auto ddrawReplacementPatch = new DDrawReplacementPatch(); ddrawReplacementPatch->DDrawDetourAddress = 0x006BAC31; ddrawReplacementPatch->DInputDetourAddress = 0x0071C56E; ddrawReplacementPatch->WindowRegisterClassDetourAddress = 0x0056C707; ddrawReplacementPatch->WindowCreateDetourAddress = 0x0056C753; + ddrawReplacementPatch->VideoFormatCheckDetourAddress = 0x006C3CB0; + ddrawReplacementPatch->VideoScalingAddress = 0x006C5047; ddrawReplacementPatch->DInputAbsolutePositioningDetourAddress = 0x0071C9C0; ddrawReplacementPatch->ForceSoftwareRendering = renderingBackend == Global::RenderingBackend::Software || (isWindows7 && renderingBackend == Global::RenderingBackend::Automatic); // Prefer software rendering on windows 7 @@ -141,8 +144,6 @@ void SteamPatcher::Configure() nativeResolutionPatch->GamefieldPresetHeightAddress = 0x0056CA16; nativeResolutionPatch->GamefieldHeightReducingAddress = 0x004F88C7; nativeResolutionPatch->GamefieldHeightRestorationAddress = 0x004FCCF7; - nativeResolutionPatch->MovieWidthAddress = 0x00571D55; - nativeResolutionPatch->MovieHeightAddress = 0x00571D5C; nativeResolutionPatch->RepositionBottomMenuDetourAddress = 0x004F8084; nativeResolutionPatch->RenameSettingsDetourAddress = 0x005309D0; nativeResolutionPatch->RenameSettingsFunctionAddress = 0x00712E20; diff --git a/subtitans/steampatcher.h b/subtitans/steampatcher.h index 493920b..0f2d85c 100644 --- a/subtitans/steampatcher.h +++ b/subtitans/steampatcher.h @@ -2,11 +2,14 @@ #include "patcher.h" class SteamPatcher : public Patcher { +protected: + uint32_t _registerGameVersion = 0; + public: SteamPatcher(); virtual ~SteamPatcher(); protected: - bool Initialize() override; + virtual bool Initialize() override; virtual void Configure() override; }; \ No newline at end of file diff --git a/subtitans/subtitans.cpp b/subtitans/subtitans.cpp index 002e150..fbadd54 100644 --- a/subtitans/subtitans.cpp +++ b/subtitans/subtitans.cpp @@ -3,6 +3,8 @@ #include "steampatchedpatcher.h" #include "gogpatcher.h" #include "demopatcher.h" +#include "techdemopatcher.h" +#include "pescalpel.h" static Logger* g_Logger = nullptr; Logger* GetLogger() @@ -28,6 +30,91 @@ Configuration* GetConfiguration() return g_Configuration; } +static void StackTrace() +{ + uint32_t* ebpRegister = nullptr; + __asm mov [ebpRegister], ebp; + + ULONG_PTR stackHighLimit = 0; + ULONG_PTR stackLowLimit = 0; + + GetCurrentThreadStackLimits(&stackLowLimit, &stackHighLimit); + + uint32_t returnAddress = 0; + int depth = 0; + + std::vector> modulePaths; + + GetLogger()->Informational("Stack trace:\n"); + + while (ebpRegister != nullptr) + { + if ((uint32_t)ebpRegister >= (uint32_t)stackHighLimit || (uint32_t)ebpRegister < (uint32_t)stackLowLimit) + { + GetLogger()->Informational("Address out of range!\n"); + break; + } + + if ((uint32_t)ebpRegister % 4 != 0) + { + GetLogger()->Informational("Invalid address\n"); + break; + } + + returnAddress = *(ebpRegister + 1); // ebp + 0x04 + if (returnAddress == 0) + break; + + HMODULE hMod = nullptr; + if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR)returnAddress, &hMod)) + { + char modulePath[_MAX_PATH]; + char name[_MAX_FNAME]; + char ext[_MAX_EXT]; + + if (GetModuleFileNameA(hMod, modulePath, _MAX_PATH) != NULL + && _splitpath_s(modulePath, nullptr, 0, nullptr, 0, name, _MAX_FNAME, ext, _MAX_EXT) == 0) + { + modulePaths.push_back(std::make_pair(std::string(name) + ext, modulePath)); + GetLogger()->Informational("[%i] Absolute 0x%04X Relative 0x%04X (%s%s)\n", depth++, returnAddress, returnAddress - (uint32_t)hMod, name, ext); + } + else + { + GetLogger()->Informational("[%i] Absolute 0x%04X Relative 0x%04X\n", depth++, returnAddress, returnAddress - (uint32_t)hMod); + } + } + else + { + GetLogger()->Informational("[%i] Address 0x%04X\n", depth++, returnAddress); + } + + ebpRegister = (uint32_t*)(*ebpRegister); + if (ebpRegister == nullptr) + break; + } + + GetLogger()->Informational("End of stack trace\n"); + + for (const auto& modulePath : modulePaths) + { + uint32_t checksum = File::CalculateChecksumA(modulePath.second.c_str()); + GetLogger()->Informational("%s CRC32 %04X\n", modulePath.first.c_str(), checksum); + } +} + +void Exit() +{ + __try { + StackTrace(); + } + __except(EXCEPTION_EXECUTE_HANDLER) { + GetLogger()->Critical("Stack trace crashed!"); + } + + MessageBox(NULL, L"SubTitans has been stopped to prevent undefined behavior.\nPlease check subtitans.log and submit a ticket at Github.", L"Application has been terminated", MB_ICONERROR); + ExitProcess(-1); +} + namespace Global { int32_t InternalWidth = 0; @@ -39,9 +126,10 @@ namespace Global int32_t BitsPerPixel = 0; - bool VideoWorkaround = false; + bool VideoRequested = false; HWND GameWindow = nullptr; + HWND RenderWindow = nullptr; HANDLE RenderEvent = nullptr; HANDLE VerticalBlankEvent = nullptr; @@ -54,7 +142,7 @@ namespace Global _MouseInformation MouseInformation; _KeyboardInformation KeyboardInformation; - void Init() + static void Init() { InternalWidth = 800; InternalHeight = 600; @@ -73,12 +161,12 @@ namespace Global } } -const char* GetApplicationLanguage() +static const char* GetApplicationLanguage() { const std::wstring STStringDll = L"ST_String.dll"; if (File::Exists(STStringDll.c_str())) { - uint32_t checksum = File::CalculateChecksum(STStringDll.c_str()); + uint32_t checksum = File::CalculateChecksumW(STStringDll.c_str()); switch (checksum) { case Shared::ST_LANGUAGE_ENGLISH_UNPATCHED: @@ -95,33 +183,44 @@ const char* GetApplicationLanguage() } } -Patcher* CreateVersionSpecificGamePatcher(unsigned long gameVersion) +static Patcher* CreateVersionSpecificGamePatcher(unsigned long gameVersion) { Patcher* patcher = nullptr; GetLogger()->Informational("Detected Version: "); switch (gameVersion) { - case Shared::ST_GAMEVERSION_RETAIL_UNPATCHED: + case Shared::ST_GAMEVERSION_0_0_6: + patcher = new TechDemoPatcher(); + GetLogger()->Informational("Technology Demo - %s\n", GetApplicationLanguage()); + break; + case Shared::ST_GAMEVERSION_0_1_6: + patcher = new DemoPatcher(); + GetLogger()->Informational("Demo - %s\n", GetApplicationLanguage()); + break; + case Shared::ST_GAMEVERSION_1_0_0: patcher = new SteamPatcher(); GetLogger()->Informational("Retail 1.0 - %s\n", GetApplicationLanguage()); GetLogger()->Warning("DEPRECATED!!! Consider updating to 1.1\n"); MessageBox(NULL, L"This version of Submarine Titans is outdated, please update to v1.1.", L"SubTitans Patch", MB_ICONWARNING); break; - case Shared::ST_GAMEVERSION_RETAIL_PATCHED: - patcher = new SteamPatchedPatcher(); + case Shared::ST_GAMEVERSION_1_1_0: + patcher = new SteamPatchedPatcher(Shared::IMAGE_BASE); GetLogger()->Informational("Retail 1.1 - %s\n", GetApplicationLanguage()); break; - case Shared::ST_GAMEVERSION_GOG_MODIFIED: - patcher = new GOGPatcher(); + case Shared::ST_GAMEVERSION_1_1_0_GOG: + patcher = new GOGPatcher(Shared::IMAGE_BASE); GetLogger()->Informational("GOG 1.1 - %s\n", GetApplicationLanguage()); break; - case Shared::ST_GAMEVERSION_DEMO: - patcher = new DemoPatcher(); - GetLogger()->Informational("Demo - %s\n", GetApplicationLanguage()); + case Shared::ST_GAMEVERSION_1_1_0_ASLR_DEP: + patcher = new SteamPatchedPatcher((uint32_t)GetModuleHandle(NULL)); + GetLogger()->Informational("Retail 1.1 with ASLR and DEP enabled (experimental) - %s\n", GetApplicationLanguage()); + break; + case Shared::ST_GAMEVERSION_1_1_0_GOG_ASLR_DEP: + patcher = new GOGPatcher((uint32_t)GetModuleHandle(NULL)); + GetLogger()->Informational("GOG 1.1 with ASLR and DEP enabled (experimental) - %s\n", GetApplicationLanguage()); break; default: - GetLogger()->Informational("Unknown\n"); GetLogger()->Critical("Incompatible application\n"); MessageBox(NULL, L"Incompatible application", L"SubTitans Patch", MB_ICONERROR); break; @@ -130,7 +229,7 @@ Patcher* CreateVersionSpecificGamePatcher(unsigned long gameVersion) return patcher; } -void LogOperatingSystem() +static void LogOperatingSystem() { GetLogger()->Informational("Operating System: "); @@ -139,7 +238,7 @@ void LogOperatingSystem() else if (IsWindowsVistaOrGreater() && !IsWindows7OrGreater()) GetLogger()->Informational("Windows Vista (unsupported)"); else if (IsWindows7OrGreater() && !IsWindows8OrGreater()) - GetLogger()->Informational("Windows 7 (supported)"); + GetLogger()->Informational("Windows 7 (unsupported)"); // BUG: ST.exe has no manifest targeting Windows 8.1 or 10 so it will always report as Windows 8 else if (IsWindows8OrGreater()) GetLogger()->Informational("Windows 8 or newer (supported)"); @@ -153,7 +252,7 @@ void LogOperatingSystem() GetLogger()->Informational("\n"); } -void LogHardwareInformation() +static void LogHardwareInformation() { // Architecture GetLogger()->Informational("CPU Architecture: "); @@ -192,7 +291,7 @@ void LogHardwareInformation() } } -uint32_t GetTimerResolution() +static uint32_t GetTimerResolution() { uint32_t resolution = 0; @@ -207,6 +306,65 @@ uint32_t GetTimerResolution() return resolution; } +void RestartRequired() +{ + GetLogger()->Warning("Game restart required because settings have been modified.\n"); + MessageBox(NULL, L"The game configuration has been changed, please restart the game.", L"SubTitans Patch", MB_ICONINFORMATION); + ExitProcess(0); +} + +static bool HandleASLRAndDEPConfiguration(uint32_t gameVersion) +{ + bool aslrDepEnabled = GetConfiguration()->GetBoolean(L"SECURITY", L"EnableASLRAndDEP", false); + if (aslrDepEnabled && (gameVersion == Shared::ST_GAMEVERSION_1_1_0 || gameVersion == Shared::ST_GAMEVERSION_1_1_0_GOG)) + { + int result = MessageBoxW(nullptr, L"Do you want to enable ASLR and DEP?\n\n" + "This operation modifies the executable, which may cause unexpected side effects.\n" + "Enabling this for Submarine Titans is experimental and hasn't been thoroughly tested.\n" + , L"SubTitans ASLR/DEP", MB_YESNO | MB_ICONQUESTION); + if (result == IDYES) + { + GetLogger()->Informational("ALSR and DEP enable requested, PE surgery required.\n"); + if (!PE::Scalpel::EnableDynamicBaseAndNXCompat()) + Exit(); + + return true; + } + } + else if (!aslrDepEnabled && (gameVersion == Shared::ST_GAMEVERSION_1_1_0_ASLR_DEP || gameVersion == Shared::ST_GAMEVERSION_1_1_0_GOG_ASLR_DEP)) + { + GetLogger()->Informational("ALSR and DEP disable requested, PE surgery required.\n"); + if (!PE::Scalpel::DisableDynamicBaseAndNXCompat()) + Exit(); + + return true; + } + + GetLogger()->Informational("ASLR and DEP: %s\n", aslrDepEnabled ? "enabled" : "disabled"); + + return false; +} + +static void TerminateOnHeapCorruption(uint32_t gameVersion) +{ + // This is automatically turned on for 'modern' executables with a subsystem of 6 and higher. + // The game is ancient and has a subsystem version 4.0 defined in the PE header, so manual enabling it is. + // https://devblogs.microsoft.com/oldnewthing/20131227-00/?p=2243 + + BOOL result = false; + switch (gameVersion) + { + case Shared::ST_GAMEVERSION_1_1_0: + case Shared::ST_GAMEVERSION_1_1_0_GOG: + case Shared::ST_GAMEVERSION_1_1_0_ASLR_DEP: + case Shared::ST_GAMEVERSION_1_1_0_GOG_ASLR_DEP: + result = HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); + break; + } + + GetLogger()->Informational("Terminate on heap corruption: %s\n", result ? "enabled" : "disabled"); +} + static Patcher* g_GamePatcher = nullptr; static UINT g_TimerResolution = 0; @@ -217,9 +375,14 @@ extern "C" void __stdcall InitializeLibrary(unsigned long gameVersion) GetLogger()->Informational("Compilation Date: %s\n", __TIMESTAMP__); + if (HandleASLRAndDEPConfiguration(gameVersion)) + return RestartRequired(); + + TerminateOnHeapCorruption(gameVersion); + g_GamePatcher = CreateVersionSpecificGamePatcher(gameVersion); - if(!g_GamePatcher) - ExitProcess(-1); + if (!g_GamePatcher) + return Exit(); LogOperatingSystem(); LogHardwareInformation(); @@ -228,11 +391,7 @@ extern "C" void __stdcall InitializeLibrary(unsigned long gameVersion) GetLogger()->Informational("\nStarting SubTitans\n"); if (!g_GamePatcher->Initialize()) - { - GetLogger()->Warning("Game restart required because GamePatcher::Initialize returned false\n"); - MessageBox(NULL, L"Please restart the game.", L"SubTitans Patch", MB_ICONINFORMATION); - ExitProcess(0); - } + return RestartRequired(); GetLogger()->Informational("Configuring patches\n"); g_GamePatcher->Configure(); @@ -241,13 +400,15 @@ extern "C" void __stdcall InitializeLibrary(unsigned long gameVersion) if (!g_GamePatcher->Apply()) { GetLogger()->Critical("Failed to apply patches\n"); - ExitProcess(-1); + return Exit(); } // Set timer resolution g_TimerResolution = timerResolution; if(g_TimerResolution != 0) timeBeginPeriod(g_TimerResolution); + + GetLogger()->Informational("Initialized SubTitans\n"); } #pragma comment(linker, "/EXPORT:ReleaseLibrary=_ReleaseLibrary@0") diff --git a/subtitans/subtitans.h b/subtitans/subtitans.h index 80f54d2..85e7094 100644 --- a/subtitans/subtitans.h +++ b/subtitans/subtitans.h @@ -11,6 +11,7 @@ #include #pragma comment(lib, "winmm.lib") +#pragma comment(lib, "Imagehlp.lib") // PEScalpel #include "../shared/gameversion.h" #include "../shared/detour.h" @@ -19,15 +20,17 @@ #include "../shared/configuration.h" #include "../shared/file.h" +Logger* GetLogger(); // Singleton +Configuration* GetConfiguration(); // Singleton +void Exit(); +#define UNIMPLEMENTED_EXIT() if(GetConfiguration()->GetBoolean(L"SECURITY", L"PreventExecutingUnimplemented", true)) { Exit(); } + #ifdef _DEBUG - #pragma comment(lib, "../Debug/shared.lib") + #define TRACELOG(format, ...) GetLogger()->Trace(format, ##__VA_ARGS__) #else - #pragma comment(lib, "../Release/shared.lib") + #define TRACELOG(format, ...) ((void)0) #endif -Logger* GetLogger(); // Singleton -Configuration* GetConfiguration(); // Singleton - namespace ResultCode { constexpr uint32_t Ok = 0x00000000; constexpr uint32_t InvalidArgument = 0x80070057; // DDraw invalid parameter, DInput invalid parameter @@ -60,9 +63,10 @@ namespace Global { extern int32_t BitsPerPixel; // Variable - extern bool VideoWorkaround; // Variable + extern bool VideoRequested; // Variable extern HWND GameWindow; // Don't use this with GetDC/ReleaseDC on different threads + extern HWND RenderWindow; extern HANDLE RenderEvent; extern HANDLE VerticalBlankEvent; diff --git a/subtitans/subtitans.vcxproj b/subtitans/subtitans.vcxproj index d561243..ebaa8a2 100644 --- a/subtitans/subtitans.vcxproj +++ b/subtitans/subtitans.vcxproj @@ -99,6 +99,8 @@ + + @@ -109,6 +111,7 @@ + @@ -140,6 +143,8 @@ + + @@ -150,6 +155,12 @@ + + + + + {5f2c7921-4b31-4712-8722-c99eff304ec4} + diff --git a/subtitans/subtitans.vcxproj.filters b/subtitans/subtitans.vcxproj.filters index 1f168c8..38254d6 100644 --- a/subtitans/subtitans.vcxproj.filters +++ b/subtitans/subtitans.vcxproj.filters @@ -83,6 +83,15 @@ patcher + + patcher + + + patches + + + pe + @@ -200,6 +209,15 @@ patcher + + patcher + + + patches + + + pe + @@ -223,5 +241,8 @@ {7b3b4aa3-26da-4048-aee1-be945cf29d93} + + {5f0f4339-857a-4f35-b55e-aa25b353262a} + \ No newline at end of file diff --git a/subtitans/surface.cpp b/subtitans/surface.cpp index 5dd7c3a..80deb39 100644 --- a/subtitans/surface.cpp +++ b/subtitans/surface.cpp @@ -6,13 +6,60 @@ using namespace DDraw; static int s_surfaceCounter = 0; static Surface* s_primarySurface = nullptr; // For palettes -Surface::Surface(SurfaceDescription* description) -{ - Identifier = ++s_surfaceCounter; +static SurfaceDescription CopyDescription(SurfaceDescription* description) +{ + SurfaceDescription desc; + memcpy(&desc, description, sizeof(SurfaceDescription)); + return desc; +} + +static int32_t ParseWidth(const SurfaceDescription& description, bool isPrimary, uint32_t identifier) +{ + if (isPrimary) return Global::InternalWidth; + else if (description.flags & SurfaceDescriptionFlag::Width) return description.width; + + GetLogger()->Error("%s %s (%i)\n", __FUNCTION__, "unexpected non set width", identifier); + return Global::InternalWidth; +} + +static int32_t ParseHeight(const SurfaceDescription& description, bool isPrimary, uint32_t identifier) +{ + if (isPrimary) return Global::InternalHeight; + else if (description.flags & SurfaceDescriptionFlag::Height) return description.height; + + GetLogger()->Error("%s %s (%i)\n", __FUNCTION__, "unexpected non set height", identifier); + return Global::InternalHeight; + +} + +static int32_t ParseBitsPerPixel(const SurfaceDescription& description, bool isPrimary, uint32_t identifier) +{ + if (isPrimary) return Global::BitsPerPixel; + + if (description.flags & SurfaceDescriptionFlag::PixelFormat) + { + if (description.pixelFormat.rgbBitCount == 32) return 32; + else if (description.pixelFormat.rgbBitCount == 8) return 8; + else + { + GetLogger()->Error("%s %s %i (%i)\n", __FUNCTION__, "unexpected bitcount", description.pixelFormat.rgbBitCount, identifier); + UNIMPLEMENTED_EXIT(); + } + } - GetLogger()->Trace("%s (%i)\n", __FUNCTION__, Identifier); + return Global::BitsPerPixel; +} - memcpy(&Description, description, sizeof(SurfaceDescription)); +Surface::Surface(SurfaceDescription* description) + : Identifier(++s_surfaceCounter) + , Description(CopyDescription(description)) + , Width(ParseWidth(Description, IsPrimary(), Identifier)) + , Height(ParseHeight(Description, IsPrimary(), Identifier)) + , BitsPerPixel(ParseBitsPerPixel(Description, IsPrimary(), Identifier)) + , BytesPerPixel(BitsPerPixel / 8) + , Stride(((Width* BitsPerPixel + 31) & ~31) >> 3) +{ + TRACELOG("%s (%i)\n", __FUNCTION__, Identifier); PrimaryInvalid = false; MemoryDeviceContext = std::make_pair(nullptr, nullptr); @@ -20,24 +67,8 @@ Surface::Surface(SurfaceDescription* description) if (IsPrimary()) { GetLogger()->Debug("%i is the primary surface\n", Identifier); - Width = Global::InternalWidth; - Height = Global::InternalHeight; - s_primarySurface = this; } - else if(Description.flags & SurfaceDescriptionFlag::Height | SurfaceDescriptionFlag::Width) - { - Width = Description.width; - Height = Description.height; - } - else - { - GetLogger()->Error("%s %s (%i)\n", __FUNCTION__, "unexpected non set width / height", Identifier); - Width = Global::InternalWidth; - Height = Global::InternalHeight; - } - - Stride = ((Width * 8 + 31) & ~31) >> 3; SurfaceBuffer = new uint8_t[Stride * Height]; memset(SurfaceBuffer, 0, Stride * Height); @@ -47,13 +78,15 @@ Surface::Surface(SurfaceDescription* description) ReferenceCount = 0; + MemoryDeviceBuffer = nullptr; + if (IsPrimary() && Global::Backend) Global::Backend->OnCreatePrimarySurface(this); } Surface::~Surface() { - GetLogger()->Trace("%s (%i)\n", __FUNCTION__, Identifier); + TRACELOG("%s (%i)\n", __FUNCTION__, Identifier); if (IsPrimary()) { @@ -82,7 +115,7 @@ Surface::~Surface() // IUnknown uint32_t __stdcall Surface::QueryInterface(GUID* guid, void** result) { - GetLogger()->Trace("%s (%i)\n", __FUNCTION__, Identifier); + TRACELOG("%s (%i)\n", __FUNCTION__, Identifier); GetLogger()->Error("%s %s\n", __FUNCTION__, "unknown interface"); *result = nullptr; @@ -91,7 +124,7 @@ uint32_t __stdcall Surface::QueryInterface(GUID* guid, void** result) uint32_t __stdcall Surface::AddRef() { - GetLogger()->Trace("%s (%i)\n", __FUNCTION__, Identifier); + TRACELOG("%s (%i)\n", __FUNCTION__, Identifier); ReferenceCount++; @@ -100,7 +133,7 @@ uint32_t __stdcall Surface::AddRef() uint32_t __stdcall Surface::Release() { - GetLogger()->Trace("%s (%i) (Remaining references %i)\n", __FUNCTION__, Identifier, ReferenceCount); + TRACELOG("%s (%i) (Remaining references %i)\n", __FUNCTION__, Identifier, ReferenceCount); if(--ReferenceCount == 0) delete this; @@ -111,225 +144,292 @@ uint32_t __stdcall Surface::Release() // Direct Draw uint32_t __stdcall Surface::AddAttachedSurface(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } uint32_t __stdcall Surface::AddOverlayDirtyRect(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } + +uint32_t FillColorBlt(Surface* surface, const RECT* destinationRect, const uint32_t& fillColor) +{ + RECT destinationRectangle; + if (!destinationRect) + { + destinationRectangle.top = 0; + destinationRectangle.left = 0; + destinationRectangle.bottom = surface->Height; + destinationRectangle.right = surface->Width; + + destinationRect = &destinationRectangle; + } + + const int32_t destinationWidth = destinationRect->right - destinationRect->left; + + if (surface->IsPrimary()) + surface->PrimaryDrawMutex.lock(); + + if (surface->BitsPerPixel == 8 || fillColor == 0) + { + // Quick copy (Only if full width and starts at the upper left corner) + if (destinationRect->top == 0 && destinationWidth == surface->Width) + { + uint32_t size = destinationRect->bottom * surface->Stride; + memset(surface->SurfaceBuffer, (uint8_t)fillColor, size); + } + else + { + const uint32_t destinationWidthBits = destinationWidth * surface->BytesPerPixel; // TODO uint <-> int + const int32_t destinationHeight = destinationRect->bottom - destinationRect->top; + + uint8_t* destination = surface->SurfaceBuffer + (destinationRect->left * surface->BytesPerPixel) + surface->Stride * destinationRect->top; + for (int i = 0; i < destinationHeight; ++i) + { + memset(destination, (uint8_t)fillColor, destinationWidthBits); + destination += surface->Stride; + } + } + } +#if _DEBUG + else + { + // It should be sufficient as-is for normal gameplay and the videos + GetLogger()->Debug("%s %s %ibbp %i\n", __FUNCTION__, "unable to blt fill color", surface->BitsPerPixel, fillColor); + } +#endif + + if (surface->IsPrimary()) + { + surface->PrimaryDrawMutex.unlock(); + + if (surface->PrimaryInvalid) + surface->PrimaryInvalid = false; + } + + return ResultCode::Ok; +} + uint32_t __stdcall Surface::Blt(RECT* destinationRect, IDDrawSurface4* sourceDDSurface, RECT* sourceRect, uint32_t flags, void* bltFx) { - GetLogger()->Trace("%s (%i)\n", __FUNCTION__, Identifier); + TRACELOG("%s (%i)\n", __FUNCTION__, Identifier); + constexpr unsigned long BltFxFillColorOffset = 0x50; constexpr unsigned long BltFlagColorFill = 0x400; - bool colorFill = flags & BltFlagColorFill; - unsigned long fillColor = 0; + const bool colorFill = flags & BltFlagColorFill; + if (colorFill) + return FillColorBlt(this, destinationRect, *((uint32_t*)((uint8_t*)bltFx + BltFxFillColorOffset))); - Surface* sourceSurface = (Surface*)sourceDDSurface; - if (sourceSurface == nullptr && !colorFill) // Unexpected + const Surface* sourceSurface = (Surface*)sourceDDSurface; + if (sourceSurface == nullptr) + { + GetLogger()->Error("%s %s\n", __FUNCTION__, "unable to blt without source surface"); return ResultCode::Ok; + } - if (colorFill) + if (BitsPerPixel != sourceSurface->BitsPerPixel) { - sourceSurface = this; - sourceRect = destinationRect; - - memcpy(&fillColor, (uint8_t*)bltFx + BltFxFillColorOffset, sizeof(unsigned long)); + GetLogger()->Error("%s %s %i -> %i\n", __FUNCTION__, "unable to blt different bbp", sourceSurface->BitsPerPixel, BitsPerPixel); + return ResultCode::Ok; } RECT sourceRectangle; - if (sourceRect) - sourceRectangle = *sourceRect; - else + if (!sourceRect) { sourceRectangle.top = 0; sourceRectangle.left = 0; sourceRectangle.bottom = sourceSurface->Height; sourceRectangle.right = sourceSurface->Width; + + sourceRect = &sourceRectangle; } RECT destinationRectangle; - if (destinationRect) - destinationRectangle = *destinationRect; - else + if (!destinationRect) { destinationRectangle.top = 0; destinationRectangle.left = 0; destinationRectangle.bottom = Height; destinationRectangle.right = Width; + + destinationRect = &destinationRectangle; } - int32_t sourceWidth = sourceRectangle.right - sourceRectangle.left; - int32_t sourceHeight = sourceRectangle.bottom - sourceRectangle.top; - int32_t destinationWidth = destinationRectangle.right - destinationRectangle.left; - int32_t destinationHeight = destinationRectangle.bottom - destinationRectangle.top; + if (Width < destinationRect->right || Height < destinationRect->bottom + || (!colorFill && (sourceSurface->Width < sourceRect->right || Height < sourceRect->bottom))) + { + GetLogger()->Error("%s %s (%i)\n", __FUNCTION__, "attempted to write outside of surface boundaries", Identifier); +#if _DEBUG + GetLogger()->Informational("Src rect: l:%i t:%i r:%i b:%i (%ix%i@%ibpp)\nDst rect: l:%i t:%i r:%i b:%i (%ix%i@%ibpp)\n" + , sourceRect->left, sourceRect->top, sourceRect->right, sourceRect->bottom + , colorFill ? -1 : sourceSurface->Width + , colorFill ? -1 : sourceSurface->Height + , colorFill ? BitsPerPixel : sourceSurface->BitsPerPixel + , destinationRect->left, destinationRect->top, destinationRect->right, destinationRect->bottom, Width, Height, BitsPerPixel + ); +#endif - if (sourceWidth == destinationWidth && sourceHeight == destinationHeight) + return ResultCode::Ok; + } + + const int32_t sourceWidth = sourceRect->right - sourceRect->left; + const int32_t sourceHeight = sourceRect->bottom - sourceRect->top; + const int32_t destinationWidth = destinationRect->right - destinationRect->left; + const int32_t destinationHeight = destinationRect->bottom - destinationRect->top; + if (sourceWidth != destinationWidth || sourceHeight != destinationHeight) { + GetLogger()->Error("%s %s (%i, %i) -> (%i, %i)\n", __FUNCTION__, "unable to stretch blt", sourceWidth, sourceHeight, destinationWidth, destinationHeight); + return ResultCode::Ok; + } + + if (IsPrimary()) PrimaryDrawMutex.lock(); - // Quick copy (Only if full width and starts at the upper left corner) - if (sourceRectangle.top == 0 && destinationRectangle.top == 0 - && sourceRectangle.left == 0 && destinationRectangle.left == 0 - && destinationWidth == Width) - { - if (colorFill) - { - memset(SurfaceBuffer, (uint8_t)fillColor, sourceRectangle.bottom * Stride); - } - else - { - if (sourceSurface != this) - { - memcpy(SurfaceBuffer, sourceSurface->SurfaceBuffer, sourceRectangle.bottom * Stride); - } - else - { - memmove(SurfaceBuffer, sourceSurface->SurfaceBuffer, sourceRectangle.bottom * Stride); - } - } - } + // Quick copy (Only if full width and starts at the upper left corner) + if (sourceRect->top == 0 && destinationRect->top == 0 + && sourceRect->left == 0 && destinationRect->left == 0 + && destinationWidth == Width) + { + uint32_t size = sourceRect->bottom * Stride; // TODO uint <-> int + if (sourceSurface != this) + memcpy(SurfaceBuffer, sourceSurface->SurfaceBuffer, size); else + memmove(SurfaceBuffer, sourceSurface->SurfaceBuffer, size); + } + else + { + const uint32_t destinationWidthBits = destinationWidth * BytesPerPixel; + uint8_t* destination = SurfaceBuffer + (destinationRect->left * BytesPerPixel) + Stride * destinationRect->top; + uint8_t* source = sourceSurface->SurfaceBuffer + (sourceRect->left * BytesPerPixel) + sourceSurface->Stride * sourceRect->top; + + const auto copyMethod = sourceSurface == this ? memmove : memcpy; + + for (int32_t i = 0; i < destinationHeight; ++i) { - for (int i = 0; i < destinationHeight; ++i) - { - if (colorFill) - { - memset((char*)SurfaceBuffer + Stride * (i + destinationRectangle.top) + destinationRectangle.left, - (uint8_t)fillColor, - destinationWidth); - } - else - { - if (sourceSurface != this) - { - memcpy((char*)SurfaceBuffer + Stride * (i + destinationRectangle.top) + destinationRectangle.left, - (char*)sourceSurface->SurfaceBuffer + sourceSurface->Stride * (i + sourceRectangle.top) + sourceRectangle.left, - destinationWidth); - } - else - { - memmove((char*)SurfaceBuffer + Stride * (i + destinationRectangle.top) + destinationRectangle.left, - (char*)sourceSurface->SurfaceBuffer + sourceSurface->Stride * (i + sourceRectangle.top) + sourceRectangle.left, - destinationWidth); - } - } - } + copyMethod(destination, source, destinationWidthBits); + source += sourceSurface->Stride; + destination += Stride; } + } + if (IsPrimary()) + { PrimaryDrawMutex.unlock(); if (PrimaryInvalid) PrimaryInvalid = false; } - else - { - GetLogger()->Error("%s %s (%i, %i) -> (%i, %i)\n", __FUNCTION__, "unable to stretch blt", sourceWidth, sourceHeight, destinationWidth, destinationHeight); - } return ResultCode::Ok; } -uint32_t __stdcall Surface::BltBatch(void*, uint32_t, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Surface::BltFast(uint32_t, uint32_t, void*, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Surface::DeleteAttachedSurface(uint32_t, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Surface::EnumAttachedSurfaces(void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Surface::EnumOverlayZOrders(uint32_t, void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Surface::Flip(void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Surface::GetAttachedSurface(void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Surface::GetBltStatus(uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } +uint32_t __stdcall Surface::BltBatch(void*, uint32_t, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Surface::BltFast(uint32_t, uint32_t, void*, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Surface::DeleteAttachedSurface(uint32_t, void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Surface::EnumAttachedSurfaces(void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Surface::EnumOverlayZOrders(uint32_t, void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Surface::Flip(void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Surface::GetAttachedSurface(void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Surface::GetBltStatus(uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } uint32_t __stdcall Surface::GetCaps(SurfaceCaps* surfaceCaps) { - GetLogger()->Trace("%s (%i)\n", __FUNCTION__, Identifier); + TRACELOG("%s (%i)\n", __FUNCTION__, Identifier); memcpy(surfaceCaps, &Description.caps, sizeof(SurfaceCaps)); return ResultCode::Ok; } -uint32_t __stdcall Surface::GetClipper(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Surface::GetColorKey(uint32_t, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } +uint32_t __stdcall Surface::GetClipper(void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Surface::GetColorKey(uint32_t, void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } uint32_t __stdcall Surface::GetDeviceContext(HDC* param1) { - GetLogger()->Trace("%s (%i)\n", __FUNCTION__, Identifier); - - BITMAPINFO* primaryBitmapInfo = (BITMAPINFO*)new char[sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 256]; - memset(primaryBitmapInfo, 0, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 256); - primaryBitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - primaryBitmapInfo->bmiHeader.biWidth = Global::VideoWorkaround ? Global::MonitorWidth : Stride; - primaryBitmapInfo->bmiHeader.biHeight = Global::VideoWorkaround ? -Global::MonitorHeight : -Height; - primaryBitmapInfo->bmiHeader.biPlanes = 1; - primaryBitmapInfo->bmiHeader.biCompression = BI_RGB; - primaryBitmapInfo->bmiHeader.biBitCount = Global::BitsPerPixel; - primaryBitmapInfo->bmiHeader.biClrUsed = Global::BitsPerPixel == 8 ? 256 : 0; - primaryBitmapInfo->bmiHeader.biSizeImage = 0; + TRACELOG("%s (%i)\n", __FUNCTION__, Identifier); - if (Global::BitsPerPixel == 8) - { - if (AttachedPalette) - memcpy(primaryBitmapInfo->bmiColors, AttachedPalette->RawPalette, sizeof(AttachedPalette->RawPalette)); - else if (s_primarySurface && s_primarySurface->AttachedPalette) - memcpy(primaryBitmapInfo->bmiColors, s_primarySurface->AttachedPalette->RawPalette, sizeof(s_primarySurface->AttachedPalette->RawPalette)); - else - GetLogger()->Error("%s %s\n", __FUNCTION__, "no attached palette found"); - } + auto createBitmapInfo = [this]() { + BITMAPINFO* primaryBitmapInfo = (BITMAPINFO*)new uint8_t[sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 256]; + memset(primaryBitmapInfo, 0, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 256); + primaryBitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + primaryBitmapInfo->bmiHeader.biWidth = Width; + primaryBitmapInfo->bmiHeader.biHeight = -Height; + primaryBitmapInfo->bmiHeader.biPlanes = 1; + primaryBitmapInfo->bmiHeader.biCompression = BI_RGB; + primaryBitmapInfo->bmiHeader.biBitCount = BitsPerPixel; + primaryBitmapInfo->bmiHeader.biClrUsed = BitsPerPixel == 8 ? 256 : 0; + primaryBitmapInfo->bmiHeader.biSizeImage = 0; + + if (BitsPerPixel == 8) + { + if (AttachedPalette) + memcpy(primaryBitmapInfo->bmiColors, AttachedPalette->RawPalette, sizeof(AttachedPalette->RawPalette)); + else if (s_primarySurface && s_primarySurface->AttachedPalette) + memcpy(primaryBitmapInfo->bmiColors, s_primarySurface->AttachedPalette->RawPalette, sizeof(s_primarySurface->AttachedPalette->RawPalette)); + else + GetLogger()->Error("%s %s\n", __FUNCTION__, "no attached palette found"); + } + + return primaryBitmapInfo; + }; - auto copyBufferToDIB = [this](HDC deviceContext, BITMAPINFO* bitmapInfo) { - void* BitmapPointer = nullptr; - auto bitmap = CreateDIBSection(deviceContext, bitmapInfo, DIB_RGB_COLORS, &BitmapPointer, NULL, 0); + auto createDIBSection = [this](HDC deviceContext, BITMAPINFO* bitmapInfo) { + auto bitmap = CreateDIBSection(deviceContext, bitmapInfo, DIB_RGB_COLORS, (void**)&MemoryDeviceBuffer, NULL, 0); if (!bitmap) { - GetLogger()->Error("%s %s\n", __FUNCTION__, "CreateDIBSection call has failed"); + GetLogger()->Critical("%s %s\n", __FUNCTION__, "CreateDIBSection call has failed"); + Exit(); + return (HGDIOBJ)nullptr; } - - if (Global::VideoWorkaround) - memset(BitmapPointer, 0, Global::MonitorWidth * Global::MonitorHeight); - else - memcpy(BitmapPointer, SurfaceBuffer, Stride * Height); - + return SelectObject(deviceContext, bitmap); }; - + if (!MemoryDeviceContext.first) { - auto deviceContext = GetDC(nullptr); + auto deviceContext = GetDC(Global::GameWindow); auto memoryDeviceContext = CreateCompatibleDC(deviceContext); - ReleaseDC(nullptr, deviceContext); + ReleaseDC(Global::GameWindow, deviceContext); + + auto primaryBitmapInfo = createBitmapInfo(); + auto oldBitmap = createDIBSection(memoryDeviceContext, primaryBitmapInfo); + delete[] (uint8_t*)primaryBitmapInfo; - auto oldBitmap = copyBufferToDIB(memoryDeviceContext, primaryBitmapInfo); MemoryDeviceContext = std::make_pair(memoryDeviceContext, oldBitmap); } - else - { - auto oldBitmap = copyBufferToDIB(MemoryDeviceContext.first, primaryBitmapInfo); - DeleteObject(oldBitmap); - } - - - delete[] (char*)primaryBitmapInfo; + memcpy(MemoryDeviceBuffer, SurfaceBuffer, Stride * Height); *param1 = MemoryDeviceContext.first; return ResultCode::Ok; } -uint32_t __stdcall Surface::GetFlipStatus(uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Surface::GetOverlayPosition(void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Surface::GetPallete(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } +uint32_t __stdcall Surface::GetFlipStatus(uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Surface::GetOverlayPosition(void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Surface::GetPallete(void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } uint32_t __stdcall Surface::GetPixelFormat(PixelFormat* result) { - GetLogger()->Trace("%s (%i)\n", __FUNCTION__, Identifier); + TRACELOG("%s (%i)\n", __FUNCTION__, Identifier); memset(result, 0, sizeof(PixelFormat)); result->size = sizeof(PixelFormat); - result->flags = PixelFormatFlag::RGB | PixelFormatFlag::PalettedIndexed8; - result->rgbBitCount = 8; + result->flags = PixelFormatFlag::RGB; + if (BitsPerPixel == 8) + { + result->flags |= PixelFormatFlag::PalettedIndexed8; + result->rgbBitCount = 8; + } + else + { + result->rgbBitCount = 32; + result->rBitMask = 0xFF0000; + result->gBitMask = 0xFF00; + result->bBitMask = 0xFF; + } return ResultCode::Ok; } uint32_t __stdcall Surface::GetSurfaceDesc(DDraw::SurfaceDescription* desc) { - GetLogger()->Trace("%s (%i)\n", __FUNCTION__, Identifier); + TRACELOG("%s (%i)\n", __FUNCTION__, Identifier); + if (desc->size != sizeof(DDraw::SurfaceDescription)) { GetLogger()->Error("%s %s %i != %i\n", __FUNCTION__, "unexpected size of description object", desc->size, sizeof(DDraw::SurfaceDescription)); @@ -338,68 +438,55 @@ uint32_t __stdcall Surface::GetSurfaceDesc(DDraw::SurfaceDescription* desc) memset(desc, 0, sizeof(DDraw::SurfaceDescription)); desc->size = sizeof(DDraw::SurfaceDescription); - desc->flags = SurfaceDescriptionFlag::Width | SurfaceDescriptionFlag::Height | SurfaceDescriptionFlag::Pitch - | SurfaceDescriptionFlag::SurfacePointer | SurfaceDescriptionFlag::PixelFormat; + desc->flags = SurfaceDescriptionFlag::Width | SurfaceDescriptionFlag::Height | SurfaceDescriptionFlag::PixelFormat; desc->width = Width; desc->height = Height; - desc->pitch = Stride; - desc->surface = SurfaceBuffer; return GetPixelFormat(&desc->pixelFormat); } -uint32_t __stdcall Surface::Initialize(void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Surface::IsLost() { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } +uint32_t __stdcall Surface::Initialize(void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Surface::IsLost() { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } uint32_t __stdcall Surface::Lock(RECT* rect, DDraw::SurfaceDescription* desc, uint32_t flags, void* unused) { - GetLogger()->Trace("%s (%i)\n", __FUNCTION__, Identifier); + TRACELOG("%s (%i)\n", __FUNCTION__, Identifier); uint32_t result = GetSurfaceDesc(desc); + if (IsPrimary()) + PrimaryDrawMutex.lock(); + + desc->flags |= SurfaceDescriptionFlag::SurfacePointer | SurfaceDescriptionFlag::Pitch; + desc->pitch = Stride; + if (rect != nullptr) - desc->surface = SurfaceBuffer + rect->top * Stride + rect->left; + desc->surface = SurfaceBuffer + rect->top * Stride + rect->left * BytesPerPixel; + else + desc->surface = SurfaceBuffer; return result; } uint32_t __stdcall Surface::ReleaseDeviceContext(HDC deviceContext) { - GetLogger()->Trace("%s (%i)\n", __FUNCTION__, Identifier); + TRACELOG("%s (%i)\n", __FUNCTION__, Identifier); if (!deviceContext) return ResultCode::Ok; - if (deviceContext == MemoryDeviceContext.first) - { - if (Global::VideoWorkaround) - { - auto dc = GetDC(Global::GameWindow); - - int32_t x = Global::MonitorWidth / 2 - Width / 2; - int32_t y = Global::MonitorHeight / 2 - Height / 2; - - StretchBlt(dc, Global::GetPadding(), 0, Global::GetAspectRatioCompensatedWidth(), Global::MonitorHeight, MemoryDeviceContext.first, x, y, Width, Height, SRCCOPY); - - ReleaseDC(Global::GameWindow, dc); - } - - // Ignore any other modifications to this DC - } - else - { + // Ignore any other modifications to this DC + if (deviceContext != MemoryDeviceContext.first) GetLogger()->Error("%s %s\n", __FUNCTION__, "trying to release (possibly invalid) device context"); - } - return ResultCode::Ok; } -uint32_t __stdcall Surface::Restore() { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } +uint32_t __stdcall Surface::Restore() { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } uint32_t __stdcall Surface::SetClipper(IDDrawClipper* clipper) { - GetLogger()->Trace("%s (%i)\n", __FUNCTION__, Identifier); + TRACELOG("%s (%i)\n", __FUNCTION__, Identifier); if (AttachedClipper) AttachedClipper->Release(); @@ -410,14 +497,15 @@ uint32_t __stdcall Surface::SetClipper(IDDrawClipper* clipper) return ResultCode::Ok; } -uint32_t __stdcall Surface::SetColorKey(uint32_t, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Surface::SetOverlayPosition(uint32_t, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } +uint32_t __stdcall Surface::SetColorKey(uint32_t, void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Surface::SetOverlayPosition(uint32_t, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } uint32_t __stdcall Surface::SetPalette(IDDrawPalette* palette) { - GetLogger()->Trace("%s (%i)\n", __FUNCTION__, Identifier); + TRACELOG("%s (%i)\n", __FUNCTION__, Identifier); - PrimaryDrawMutex.lock(); + if (IsPrimary()) + PrimaryDrawMutex.lock(); if (AttachedPalette) AttachedPalette->Release(); @@ -425,7 +513,8 @@ uint32_t __stdcall Surface::SetPalette(IDDrawPalette* palette) AttachedPalette = (Palette*)palette; AttachedPalette->AddRef(); - PrimaryDrawMutex.unlock(); + if (IsPrimary()) + PrimaryDrawMutex.unlock(); PrimaryInvalid = true; @@ -434,19 +523,23 @@ uint32_t __stdcall Surface::SetPalette(IDDrawPalette* palette) uint32_t __stdcall Surface::Unlock(RECT* rect) { - GetLogger()->Trace("%s (%i)\n", __FUNCTION__, Identifier); + TRACELOG("%s (%i)\n", __FUNCTION__, Identifier); + + if (IsPrimary()) + PrimaryDrawMutex.unlock(); + return ResultCode::Ok; } -uint32_t __stdcall Surface::UpdateOverlay(void*, void*, void*, uint32_t, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Surface::UpdateOverlayDisplay(uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Surface::UpdateOverlayZOrder(uint32_t, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Surface::GetDDInterface(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Surface::PageLock(uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Surface::PageUnlock(uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Surface::SetSurfaceDesc(void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Surface::SetPrivateData(void*, void*, uint32_t, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Surface::GetPrivateData(void*, void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Surface::FreePrivateData(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Surface::GetUniquenessValue(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -uint32_t __stdcall Surface::ChangeUniquenessValue() { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } \ No newline at end of file +uint32_t __stdcall Surface::UpdateOverlay(void*, void*, void*, uint32_t, void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Surface::UpdateOverlayDisplay(uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Surface::UpdateOverlayZOrder(uint32_t, void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Surface::GetDDInterface(void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Surface::PageLock(uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Surface::PageUnlock(uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Surface::SetSurfaceDesc(void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Surface::SetPrivateData(void*, void*, uint32_t, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Surface::GetPrivateData(void*, void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Surface::FreePrivateData(void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Surface::GetUniquenessValue(void*) { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } +uint32_t __stdcall Surface::ChangeUniquenessValue() { GetLogger()->Error("%s\n", __FUNCTION__); UNIMPLEMENTED_EXIT(); return ResultCode::Ok; } \ No newline at end of file diff --git a/subtitans/surface.h b/subtitans/surface.h index 8c877a3..d68385e 100644 --- a/subtitans/surface.h +++ b/subtitans/surface.h @@ -58,14 +58,24 @@ class Surface : public DDraw::IDDrawSurface4 { virtual uint32_t __stdcall ChangeUniquenessValue() override; // Custom +private: + const uint32_t Identifier; + const DDraw::SurfaceDescription Description; +public: + const int32_t Width; + const int32_t Height; + const int32_t BitsPerPixel; + const int32_t BytesPerPixel; + const int32_t Stride; + private: uint32_t ReferenceCount; - uint32_t Identifier; + Clipper* AttachedClipper; + + std::pair MemoryDeviceContext; + uint8_t* MemoryDeviceBuffer; public: - int32_t Width; // Read only - int32_t Height; // Read only - int32_t Stride; // Read only uint8_t* SurfaceBuffer; std::mutex PrimaryDrawMutex; @@ -73,10 +83,5 @@ class Surface : public DDraw::IDDrawSurface4 { Palette* AttachedPalette; -private: - DDraw::SurfaceDescription Description; - Clipper* AttachedClipper; - - bool IsPrimary() { return (Description.flags & DDraw::SurfaceDescriptionFlag::Caps) && (Description.caps.caps[0] & DDraw::SurfaceCapsFlag::Primary); } - std::pair MemoryDeviceContext; + bool IsPrimary() const { return (Description.flags & DDraw::SurfaceDescriptionFlag::Caps) && (Description.caps.caps[0] & DDraw::SurfaceCapsFlag::Primary); } }; \ No newline at end of file diff --git a/subtitans/techdemopatcher.cpp b/subtitans/techdemopatcher.cpp new file mode 100644 index 0000000..2cd38b1 --- /dev/null +++ b/subtitans/techdemopatcher.cpp @@ -0,0 +1,73 @@ +#include "subtitans.h" +#include "sleepwellpatch.h" +#include "ddrawreplacementpatch.h" +#include "registrypatch.h" +#include "shlobj.h" +#include "techdemopatcher.h" + +// The bare minimum to get the Submarine Titans technology demo running. +// DirectDraw support is dropped, OpenGL or Software rendering is forced. +// No support for custom resolutions/widescreen is provided. +// If the game keeps crashing you must delete the savegame folder and create a profile in-game. +// Disabling the intro video through stconfig.exe might help improve stability. +// Doesn't work well without Administrator privileges. + +TechDemoPatcher::TechDemoPatcher() +{ +} + +TechDemoPatcher::~TechDemoPatcher() +{ + +} + +bool TechDemoPatcher::Initialize() +{ + if (!IsUserAnAdmin()) + { + MessageBox(NULL + , L"The Technology Demo has quite a few compatibility issues\n\n" + L"Without Administrator privileges you'll most likely encounter registry errors." + , L"Compatibility warning" + , MB_OK | MB_ICONWARNING); + } + + return true; +} + +void TechDemoPatcher::Configure() +{ + auto sleepWellPatch = new SleepWellPatch(); + sleepWellPatch->DetourAddress = 0x00592FDA; + sleepWellPatch->FrameLimitMemoryAddress = 0x006B92EC; + sleepWellPatch->DisableOriginalLimiterSleepAddress = 0x00593001; + _patches.push_back(sleepWellPatch); + + Global::RenderWidth = GetConfiguration()->GetInt32(L"SETTING", L"Width", 0); + if (Global::RenderWidth == 0) + Global::RenderWidth = Global::MonitorWidth; + + Global::RenderHeight = GetConfiguration()->GetInt32(L"SETTING", L"Height", 0); + if (Global::RenderHeight == 0) + Global::RenderHeight = Global::MonitorHeight; + + auto renderingBackend = GetConfiguration()->GetInt32(L"FEATURE", L"Renderer", Global::RenderingBackend::Automatic); + + auto ddrawReplacementPatch = new DDrawReplacementPatch(); + ddrawReplacementPatch->DDrawDetourAddress = 0x005778A1; + ddrawReplacementPatch->DInputDetourAddress = 0x005B254E; + ddrawReplacementPatch->WindowRegisterClassDetourAddress = 0x00561895; + ddrawReplacementPatch->WindowCreateDetourAddress = 0x005618E1; + ddrawReplacementPatch->VideoFormatCheckDetourAddress = 0; + ddrawReplacementPatch->VideoScalingAddress = 0x005842B7; + ddrawReplacementPatch->DInputAbsolutePositioningDetourAddress = 0x005B29A3; + ddrawReplacementPatch->ForceSoftwareRendering = renderingBackend == Global::RenderingBackend::Software; + ddrawReplacementPatch->DInputReplacement = false; // Doesn't work that well with the demo + _patches.push_back(ddrawReplacementPatch); + + auto registryPatch = new RegistryPatch(); + registryPatch->DetourAddress = 0x00565E1E; + _patches.push_back(registryPatch); + + Global::RetroShader = GetConfiguration()->GetBoolean(L"FEATURE", L"RetroShader", false); +} \ No newline at end of file diff --git a/subtitans/techdemopatcher.h b/subtitans/techdemopatcher.h new file mode 100644 index 0000000..863113f --- /dev/null +++ b/subtitans/techdemopatcher.h @@ -0,0 +1,12 @@ +#pragma once +#include "patcher.h" + +class TechDemoPatcher : public Patcher { +public: + TechDemoPatcher(); + virtual ~TechDemoPatcher(); + +protected: + virtual bool Initialize() override; + virtual void Configure() override; +}; \ No newline at end of file From c6f305f744f94633c90af989912cd613033cc89b Mon Sep 17 00:00:00 2001 From: UnknownException <25252012+UnknownException@users.noreply.github.com> Date: Sat, 30 May 2026 12:22:33 +0200 Subject: [PATCH 2/2] Update build runner image to windows-2022 --- .github/workflows/action-development.yml | 4 ++-- .github/workflows/action-release.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/action-development.yml b/.github/workflows/action-development.yml index 8f0ea61..8510cf5 100644 --- a/.github/workflows/action-development.yml +++ b/.github/workflows/action-development.yml @@ -5,7 +5,7 @@ on: jobs: build: name: Build SubTitans - runs-on: windows-2019 + runs-on: windows-2022 steps: - name: Create Timestamp id: create_timestamp @@ -23,7 +23,7 @@ jobs: - name: Compile shell: cmd - run: msbuild SubTitans.sln -p:Configuration=Release -p:Platform=x86 + run: msbuild SubTitans.sln -p:Configuration=Release -p:Platform=x86 -p:PlatformToolset=v142 - name: Create Zip shell: powershell diff --git a/.github/workflows/action-release.yml b/.github/workflows/action-release.yml index 0d18dab..7cf4e4f 100644 --- a/.github/workflows/action-release.yml +++ b/.github/workflows/action-release.yml @@ -5,7 +5,7 @@ on: jobs: build: name: Build SubTitans - runs-on: windows-2019 + runs-on: windows-2022 steps: - name: Create Timestamp id: create_timestamp @@ -21,7 +21,7 @@ jobs: - name: Compile shell: cmd - run: msbuild SubTitans.sln -p:Configuration=Release -p:Platform=x86 + run: msbuild SubTitans.sln -p:Configuration=Release -p:Platform=x86 -p:PlatformToolset=v142 - name: Create Zip shell: powershell