├── game ├── .placeholder └── subtitans.ini ├── d3drm ├── injector.h ├── types.h ├── d3drm.vcxproj.filters ├── d3drm.vcxproj.user ├── d3drm.cpp ├── d3drm.vcxproj └── injector.cpp ├── shared ├── detour.h ├── memorywriter.h ├── file.h ├── configuration.h ├── detour.cpp ├── logger.h ├── gameversion.h ├── shared.vcxproj.filters ├── memorywriter.cpp ├── configuration.cpp ├── file.cpp ├── logger.cpp └── shared.vcxproj ├── subtitans ├── gogpatcher.h ├── demopatcher.h ├── patch.h ├── steampatcher.h ├── steampatchedpatcher.h ├── patcher.h ├── openglrenderer.h ├── softwarerenderer.h ├── scrollpatch.h ├── sleepwellpatch.h ├── movieheapcorruptionpatch.h ├── missionskippatch.h ├── ddrawreplacementpatch.h ├── patcher.cpp ├── iddrawclipper.h ├── clipper.h ├── idinput7.h ├── palette.h ├── irenderer.h ├── input.h ├── imgui │ ├── LICENSE.txt │ ├── imgui_impl_opengl3.h │ └── imconfig.h ├── iddrawpalette.h ├── nativeresolutionpatch.h ├── clipper.cpp ├── scrollpatch.cpp ├── demopatcher.cpp ├── mousedevice.h ├── keyboarddevice.h ├── device.h ├── sleepwellpatch.cpp ├── input.cpp ├── subtitans.h ├── glew │ └── LICENSE.txt ├── movieheapcorruptionpatch.cpp ├── surface.h ├── softwarerenderer.cpp ├── iddraw4.h ├── palette.cpp ├── idinputdevice7.h ├── iddrawsurface4.h ├── keyboarddevice.cpp ├── mousedevice.cpp ├── gogpatcher.cpp ├── steampatchedpatcher.cpp ├── subtitans.vcxproj ├── subtitans.vcxproj.filters ├── missionskippatch.cpp ├── subtitans.cpp ├── steampatcher.cpp └── device.cpp ├── LICENSE ├── Debug └── subtitans.ini ├── Release └── subtitans.ini ├── README.md ├── .github └── workflows │ ├── action-release.yml │ └── action-development.yml ├── SubTitans.sln └── .gitignore /game/.placeholder: -------------------------------------------------------------------------------- 1 | Put a copy of Submarine Titans inside this folder to enable debugging -------------------------------------------------------------------------------- /d3drm/injector.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Injector { 4 | bool Apply(unsigned long gameVersion); 5 | } 6 | -------------------------------------------------------------------------------- /d3drm/types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #pragma pack(push, 1) 4 | struct D3DVector3 5 | { 6 | float x; 7 | float y; 8 | float z; 9 | }; 10 | #pragma pack(pop) 11 | -------------------------------------------------------------------------------- /shared/detour.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace Detour { 5 | bool Create(unsigned long origin, SIZE_T length, unsigned long destination); 6 | }; 7 | -------------------------------------------------------------------------------- /shared/memorywriter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace MemoryWriter { 5 | bool Write(unsigned long address, unsigned char* bytes, SIZE_T length, bool enforceNoIntersecting = true); 6 | }; 7 | -------------------------------------------------------------------------------- /subtitans/gogpatcher.h: -------------------------------------------------------------------------------- 1 | #include "patcher.h" 2 | 3 | class GOGPatcher : public Patcher { 4 | public: 5 | GOGPatcher(); 6 | virtual ~GOGPatcher(); 7 | 8 | protected: 9 | void Configure() override; 10 | }; -------------------------------------------------------------------------------- /subtitans/demopatcher.h: -------------------------------------------------------------------------------- 1 | #include "patcher.h" 2 | 3 | class DemoPatcher : public Patcher { 4 | public: 5 | DemoPatcher(); 6 | virtual ~DemoPatcher(); 7 | 8 | protected: 9 | void Configure() override; 10 | }; -------------------------------------------------------------------------------- /shared/file.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace File { 5 | bool Exists(const wchar_t* path); // Only Kernel32 dependent 6 | uint32_t CalculateChecksum(const wchar_t* path); // Only Kernel32 dependent 7 | } -------------------------------------------------------------------------------- /subtitans/patch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class Patch { 4 | public: 5 | Patch() {} 6 | virtual ~Patch() {} 7 | 8 | virtual bool Validate() = 0; 9 | virtual bool Apply() = 0; 10 | virtual const wchar_t* ErrorMessage() = 0; 11 | }; 12 | -------------------------------------------------------------------------------- /subtitans/steampatcher.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "patcher.h" 3 | 4 | class SteamPatcher : public Patcher { 5 | public: 6 | SteamPatcher(); 7 | virtual ~SteamPatcher(); 8 | 9 | protected: 10 | bool Initialize() override; 11 | virtual void Configure() override; 12 | }; -------------------------------------------------------------------------------- /subtitans/steampatchedpatcher.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "steampatcher.h" 3 | 4 | class SteamPatchedPatcher : public SteamPatcher { 5 | public: 6 | SteamPatchedPatcher(); 7 | virtual ~SteamPatchedPatcher(); 8 | 9 | protected: 10 | virtual void Configure() override; 11 | }; -------------------------------------------------------------------------------- /subtitans/patcher.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "patch.h" 3 | 4 | class Patcher { 5 | public: 6 | Patcher(); 7 | virtual ~Patcher(); 8 | 9 | // Optional 10 | virtual bool Initialize() { return true; } 11 | // Set addresses 12 | virtual void Configure() = 0; 13 | bool Apply(); 14 | 15 | protected: 16 | std::vector _patches; 17 | }; 18 | -------------------------------------------------------------------------------- /d3drm/d3drm.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /subtitans/openglrenderer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "irenderer.h" 3 | 4 | class OpenGLRenderer : public IRenderer { 5 | public: 6 | OpenGLRenderer(); 7 | virtual ~OpenGLRenderer(); 8 | 9 | virtual bool Create() override; 10 | virtual void OnCreatePrimarySurface(Surface* primarySurface) override; 11 | virtual void OnDestroyPrimarySurface() override; 12 | 13 | protected: 14 | virtual void Run() override; 15 | }; -------------------------------------------------------------------------------- /subtitans/softwarerenderer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "irenderer.h" 3 | 4 | class SoftwareRenderer : public IRenderer { 5 | public: 6 | SoftwareRenderer(); 7 | virtual ~SoftwareRenderer(); 8 | 9 | virtual bool Create() override; 10 | virtual void OnCreatePrimarySurface(Surface* primarySurface) override; 11 | virtual void OnDestroyPrimarySurface() override; 12 | 13 | protected: 14 | virtual void Run() override; 15 | }; -------------------------------------------------------------------------------- /subtitans/scrollpatch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "patch.h" 3 | 4 | class ScrollPatch : public Patch { 5 | public: 6 | ScrollPatch(); 7 | virtual ~ScrollPatch(); 8 | 9 | bool Validate() override; 10 | bool Apply() override; 11 | const wchar_t* ErrorMessage() override { return L"Failed to apply the scroll patch"; } 12 | 13 | unsigned long UpdateRateAddress; 14 | unsigned long DetourAddress; 15 | unsigned long OriginalSpeedModifiersAddress; 16 | }; -------------------------------------------------------------------------------- /subtitans/sleepwellpatch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "patch.h" 3 | 4 | class SleepWellPatch : public Patch { 5 | public: 6 | SleepWellPatch(); 7 | virtual ~SleepWellPatch(); 8 | 9 | bool Validate() override; 10 | bool Apply() override; 11 | const wchar_t* ErrorMessage() override { return L"Failed to apply Sleep Well patch"; } 12 | 13 | unsigned long DetourAddress; 14 | unsigned long FrameLimitMemoryAddress; 15 | unsigned long DisableOriginalLimiterSleepAddress; 16 | }; -------------------------------------------------------------------------------- /shared/configuration.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | class Configuration { 5 | std::wstring _configFilePath; 6 | 7 | public: 8 | Configuration(std::wstring configFileName); 9 | virtual ~Configuration(); 10 | 11 | bool GetBoolean(std::wstring category, std::wstring key, bool placeholder = false); 12 | std::wstring GetString(std::wstring category, std::wstring key, std::wstring placeholder = L""); 13 | int32_t GetInt32(std::wstring category, std::wstring key, int32_t placeholder = 0); 14 | }; 15 | -------------------------------------------------------------------------------- /subtitans/movieheapcorruptionpatch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "patch.h" 3 | 4 | class MovieHeapCorruptionPatch : public Patch { 5 | public: 6 | MovieHeapCorruptionPatch(); 7 | virtual ~MovieHeapCorruptionPatch(); 8 | 9 | bool Validate() override; 10 | bool Apply() override; 11 | const wchar_t* ErrorMessage() override { return L"Failed to apply Movie Heap Corruption patch"; } 12 | 13 | unsigned long AllocatedMemoryOffset; 14 | unsigned long StructurePointer; 15 | unsigned long StructureOffsets[26]; 16 | unsigned long DetourAddress; 17 | }; -------------------------------------------------------------------------------- /shared/detour.cpp: -------------------------------------------------------------------------------- 1 | #include "memorywriter.h" 2 | #include "detour.h" 3 | 4 | bool Detour::Create(unsigned long origin, SIZE_T length, unsigned long destination) 5 | { 6 | if (length < 5) 7 | return false; 8 | 9 | unsigned char* newBytes = new unsigned char[length]; 10 | memset(newBytes, 0x90, length); // Fill with nops 11 | newBytes[0] = 0xE9; // Jump instruction 12 | 13 | unsigned long jumpDistance = destination - origin - 5; 14 | memcpy(&newBytes[1], &jumpDistance, sizeof(unsigned long)); // Jump distance 15 | 16 | bool result = MemoryWriter::Write(origin, newBytes, length); 17 | 18 | delete[] newBytes; 19 | return result; 20 | } -------------------------------------------------------------------------------- /subtitans/missionskippatch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "patch.h" 3 | 4 | class MissionSkipPatch : public Patch { 5 | public: 6 | MissionSkipPatch(); 7 | virtual ~MissionSkipPatch(); 8 | 9 | bool Validate() override; 10 | bool Apply() override; 11 | const wchar_t* ErrorMessage() override { return L"Failed to apply the mission skip patch"; } 12 | 13 | unsigned long AddCheatCodeDetourAddress; 14 | unsigned long AddCheatCodeAlternativeReturnAddress; 15 | unsigned long FullMapPathDetourAddress; 16 | unsigned long RelativeMapPathDetourAddress; 17 | 18 | unsigned long CurrentMapNameVariable; 19 | unsigned long CheatValidationFunctionAddress; 20 | }; -------------------------------------------------------------------------------- /shared/logger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | class Logger { 7 | public: 8 | Logger(std::string logFilePath); 9 | virtual ~Logger(); 10 | 11 | void Clear(); 12 | 13 | void Critical(const char* format, ...); 14 | void Error(const char* format, ...); 15 | void Warning(const char* format, ...); 16 | void Informational(const char* format, ...); 17 | void Debug(const char* format, ...); 18 | void Trace(const char* format, ...); 19 | 20 | private: 21 | char* _logBuffer; 22 | std::string _logFilePath; 23 | void Log(const char* format, va_list arguments, const char* prefix); 24 | 25 | std::mutex _mutex; 26 | }; 27 | -------------------------------------------------------------------------------- /subtitans/ddrawreplacementpatch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "patch.h" 3 | 4 | class DDrawReplacementPatch : public Patch { 5 | public: 6 | DDrawReplacementPatch(); 7 | virtual ~DDrawReplacementPatch(); 8 | 9 | bool Validate() override; 10 | bool Apply() override; 11 | const wchar_t* ErrorMessage() override { return L"Failed to apply the directdraw replacement patch"; } 12 | 13 | unsigned long DDrawDetourAddress; 14 | unsigned long DInputDetourAddress; 15 | unsigned long WindowRegisterClassDetourAddress; 16 | unsigned long WindowCreateDetourAddress; 17 | uint32_t DInputAbsolutePositioningDetourAddress; 18 | bool ForceSoftwareRendering; 19 | bool DInputReplacement; 20 | }; -------------------------------------------------------------------------------- /shared/gameversion.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Shared { 4 | constexpr unsigned long ST_GAMEVERSION_RETAIL_UNPATCHED = 0x826D8625; // 1.0 from steam store/retail 5 | constexpr unsigned long ST_GAMEVERSION_RETAIL_PATCHED = 0xB5BD8D15; // 1.1 retail patch 6 | constexpr unsigned long ST_GAMEVERSION_GOG_MODIFIED = 0x18C26D0A; // 1.1 from gog store 7 | constexpr unsigned long ST_GAMEVERSION_DEMO = 0x6B08028C; // Demo 8 | 9 | constexpr unsigned long ST_LANGUAGE_ENGLISH_UNPATCHED = 0xCE56F1FC; // 1.0 English 10 | constexpr unsigned long ST_LANGUAGE_ENGLISH_PATCHED = 0x0EB5715F; // 1.1 English 11 | constexpr unsigned long ST_LANGUAGE_ENGLISH_DEMO = 0xE73A5959; // Demo English 12 | } -------------------------------------------------------------------------------- /subtitans/patcher.cpp: -------------------------------------------------------------------------------- 1 | #include "subtitans.h" 2 | #include "patcher.h" 3 | 4 | Patcher::Patcher() 5 | { 6 | } 7 | 8 | Patcher::~Patcher() 9 | { 10 | for (auto it = _patches.begin(); it != _patches.end(); ++it) 11 | { 12 | delete *it; 13 | } 14 | } 15 | 16 | bool Patcher::Apply() 17 | { 18 | for (auto it = _patches.begin(); it != _patches.end(); ++it) 19 | { 20 | if (!(*it)->Validate()) 21 | { 22 | MessageBox(NULL, (*it)->ErrorMessage(), L"Patch validation error", MB_ICONERROR); 23 | return false; 24 | } 25 | 26 | if (!(*it)->Apply()) 27 | { 28 | MessageBox(NULL, (*it)->ErrorMessage(), L"Patching error", MB_ICONERROR); 29 | return false; 30 | } 31 | } 32 | 33 | return true; 34 | } -------------------------------------------------------------------------------- /subtitans/iddrawclipper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace DDraw { 4 | class IDDrawClipper { 5 | public: 6 | // IUnknown 7 | virtual uint32_t __stdcall QueryInterface(GUID* guid, void** result) = 0; 8 | virtual uint32_t __stdcall AddRef() = 0; 9 | virtual uint32_t __stdcall Release() = 0; 10 | 11 | // Direct Draw Clipper 12 | virtual uint32_t __stdcall GetClipList(void*, void*, void*) = 0; 13 | virtual uint32_t __stdcall GetHWnd(void*) = 0; 14 | virtual uint32_t __stdcall Initialize(void*, uint32_t) = 0; 15 | virtual uint32_t __stdcall IsClipListChanged(uint32_t) = 0; 16 | virtual uint32_t __stdcall SetClipList(void*, uint32_t) = 0; 17 | virtual uint32_t __stdcall SetHWnd(uint32_t, void*) = 0; 18 | }; 19 | } -------------------------------------------------------------------------------- /subtitans/clipper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "iddraw4.h" 3 | 4 | class Clipper : public DDraw::IDDrawClipper { 5 | public: 6 | Clipper(); 7 | virtual ~Clipper(); 8 | 9 | // IUnknown 10 | virtual uint32_t __stdcall QueryInterface(GUID* guid, void** result) override; 11 | virtual uint32_t __stdcall AddRef() override; 12 | virtual uint32_t __stdcall Release() override; 13 | 14 | // Direct Draw Clipper 15 | virtual uint32_t __stdcall GetClipList(void*, void*, void*) override; 16 | virtual uint32_t __stdcall GetHWnd(void*) override; 17 | virtual uint32_t __stdcall Initialize(void*, uint32_t) override; 18 | virtual uint32_t __stdcall IsClipListChanged(uint32_t) override; 19 | virtual uint32_t __stdcall SetClipList(void*, uint32_t) override; 20 | virtual uint32_t __stdcall SetHWnd(uint32_t, void*) override; 21 | 22 | // Custom 23 | private: 24 | uint32_t referenceCount; 25 | }; -------------------------------------------------------------------------------- /subtitans/idinput7.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "idinputdevice7.h" 3 | 4 | namespace DInput { 5 | class IDInput7 { 6 | public: 7 | // IUnknown 8 | virtual uint32_t __stdcall QueryInterface(GUID* guid, void** result) = 0; 9 | virtual uint32_t __stdcall AddRef() = 0; 10 | virtual uint32_t __stdcall Release() = 0; 11 | 12 | // DInput7 13 | virtual uint32_t __stdcall CreateDevice(GUID* guid, IDInputDevice7** result, void*) = 0; 14 | virtual uint32_t __stdcall EnumDevices(void* callback, void*, uint32_t) = 0; 15 | virtual uint32_t __stdcall GetDeviceStatus(GUID* guid) = 0; 16 | virtual uint32_t __stdcall RunControlPanel(HWND, uint32_t) = 0; 17 | virtual uint32_t __stdcall Initialize(HINSTANCE hInstance, uint32_t) = 0; 18 | virtual uint32_t __stdcall FindDevice(GUID* guid, void* str, void* guid2) = 0; 19 | virtual uint32_t __stdcall CreateDeviceEx(GUID*, GUID, void*, void*) = 0; 20 | }; 21 | } -------------------------------------------------------------------------------- /subtitans/palette.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "iddraw4.h" 3 | 4 | class Palette : public DDraw::IDDrawPalette { 5 | public: 6 | Palette(); 7 | virtual ~Palette(); 8 | 9 | // IUnknown 10 | virtual uint32_t __stdcall QueryInterface(GUID* guid, void** result) override; 11 | virtual uint32_t __stdcall AddRef() override; 12 | virtual uint32_t __stdcall Release() override; 13 | 14 | // Direct Draw Palette 15 | virtual uint32_t __stdcall GetCaps(void*) override; 16 | virtual uint32_t __stdcall GetEntries(uint32_t, uint32_t, uint32_t, uint8_t*) override; 17 | virtual uint32_t __stdcall Initialize(void*, uint32_t, void*) override; 18 | virtual uint32_t __stdcall SetEntries(uint32_t, uint32_t, uint32_t, uint8_t*) override; 19 | 20 | // Custom 21 | private: 22 | uint32_t ReferenceCount; 23 | public: 24 | bool CreatePallete(uint32_t flags, uint8_t* palette); 25 | 26 | std::mutex Mutex; 27 | 28 | uint8_t RawPalette[1024]; 29 | }; -------------------------------------------------------------------------------- /d3drm/d3drm.vcxproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WindowsLocalDebugger 5 | $(SolutionDir)game\ST.exe 6 | $(OutputPath) 7 | NativeOnly 8 | 9 | 10 | WindowsLocalDebugger 11 | $(SolutionDir)game\ST.exe 12 | $(OutputPath) 13 | NativeOnly 14 | 15 | -------------------------------------------------------------------------------- /subtitans/irenderer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class Surface; 4 | class IRenderer { 5 | protected: 6 | std::thread* RenderThread; 7 | std::atomic_bool IsThreadRunning; 8 | std::mutex Mutex; 9 | 10 | Surface* PrimarySurface; 11 | bool RecalculateSurface; 12 | 13 | Global::RenderInformation RenderInformation; // Copied from Shared for thread safety 14 | 15 | public: 16 | IRenderer() { 17 | RenderThread = nullptr; 18 | IsThreadRunning = false; 19 | PrimarySurface = nullptr; 20 | RecalculateSurface = true; 21 | memset(&RenderInformation, 0, sizeof(Global::RenderInformation)); 22 | } 23 | virtual ~IRenderer() { 24 | if (RenderThread) { 25 | IsThreadRunning = false; 26 | RenderThread->join(); 27 | delete RenderThread; 28 | RenderThread = nullptr; 29 | } 30 | } 31 | 32 | virtual bool Create() = 0; 33 | virtual void OnCreatePrimarySurface(Surface* primarySurface) = 0; 34 | virtual void OnDestroyPrimarySurface() = 0; 35 | 36 | protected: 37 | virtual void Run() = 0; // Render thread 38 | }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Max Visser 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /subtitans/input.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "idinput7.h" 3 | 4 | class Input : public DInput::IDInput7 { 5 | public: 6 | Input(); 7 | virtual ~Input(); 8 | 9 | // IUnknown 10 | virtual uint32_t __stdcall QueryInterface(GUID* guid, void** result) override; 11 | virtual uint32_t __stdcall AddRef() override; 12 | virtual uint32_t __stdcall Release() override; 13 | 14 | // DInput 15 | virtual uint32_t __stdcall CreateDevice(GUID* guid, DInput::IDInputDevice7** result, void*) override; 16 | virtual uint32_t __stdcall EnumDevices(void* callback, void*, uint32_t) override; 17 | virtual uint32_t __stdcall GetDeviceStatus(GUID* guid) override; 18 | virtual uint32_t __stdcall RunControlPanel(HWND, uint32_t) override; 19 | virtual uint32_t __stdcall Initialize(HINSTANCE hInstance, uint32_t) override; 20 | virtual uint32_t __stdcall FindDevice(GUID* guid, void* str, void* guid2) override; 21 | virtual uint32_t __stdcall CreateDeviceEx(GUID*, GUID, void*, void*) override; 22 | 23 | // Custom 24 | uint32_t ReferenceCount; 25 | }; 26 | 27 | uint32_t __stdcall DirectInputCreate(HINSTANCE hInstance, uint32_t version, DInput::IDInput7** result, void*); 28 | -------------------------------------------------------------------------------- /subtitans/imgui/LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2022 Omar Cornut 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /subtitans/iddrawpalette.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace DDraw { 4 | namespace PaletteCapabilityFlags { 5 | constexpr uint32_t Bits_1 = 0x00000100; 6 | constexpr uint32_t Bits_2 = 0x00000200; 7 | constexpr uint32_t Bits_4 = 0x00000001; 8 | constexpr uint32_t Bits_8 = 0x00000004; 9 | constexpr uint32_t Use8BitIndex = 0x00000002; 10 | constexpr uint32_t PrimarySurface = 0x00000010; 11 | constexpr uint32_t PrimarySurfaceLeft = 0x00000020; 12 | constexpr uint32_t AllowFullPalette = 0x00000040; 13 | constexpr uint32_t VSync = 0x00000080; 14 | constexpr uint32_t UseAlpha = 0x00000400; 15 | }; 16 | 17 | class IDDrawPalette { 18 | public: 19 | // IUnknown 20 | virtual uint32_t __stdcall QueryInterface(GUID* guid, void** result) = 0; 21 | virtual uint32_t __stdcall AddRef() = 0; 22 | virtual uint32_t __stdcall Release() = 0; 23 | 24 | // Direct Draw Palette 25 | virtual uint32_t __stdcall GetCaps(void*) = 0; 26 | virtual uint32_t __stdcall GetEntries(uint32_t, uint32_t, uint32_t, uint8_t*) = 0; 27 | virtual uint32_t __stdcall Initialize(void*, uint32_t, void*) = 0; 28 | virtual uint32_t __stdcall SetEntries(uint32_t, uint32_t, uint32_t, uint8_t*) = 0; 29 | }; 30 | } -------------------------------------------------------------------------------- /Debug/subtitans.ini: -------------------------------------------------------------------------------- 1 | [SETTING] 2 | ; Override the detected screen width (0: Ignore). 3 | Width=0 4 | ; Override the detected screen height (0: Ignore). 5 | Height=0 6 | 7 | [FEATURE] 8 | ; Provides widescreen support with propper scaling of the GUI. 9 | ; This patch overrides the in-game resolution of 1280x1024. 10 | NativeResolution=true 11 | ; Provides an alternative for the obsolete DirectDraw renderer (0: Automatic, 1: DirectDraw, 2: OpenGL, 3: Software). 12 | ; Set the value to 1 when using a custom DirectDraw wrapper. 13 | Renderer=0 14 | ; Add the 'orbiton' cheat for mission skipping, see the readme for more information. 15 | MissionSkipCheat=true 16 | ; Provides an alternative for the obsolete DirectInput input handling. 17 | ; Set this variable to false if you're experiencing problems with mouse or keyboard input. 18 | ; This modification doesn't work in combination with the DirectDraw renderer. 19 | CustomInput=true 20 | ; Shader that tries to mimic CRT behavior. The OpenGL renderer is required for this effect. 21 | RetroShader=false 22 | 23 | [FIX] 24 | ; Fixes a crash caused by the video player. 25 | MovieHeap=true 26 | ; Replaces the default FPS limiter with a better implementation. 27 | ImprovedFPSLimiter=true 28 | ; Improves the smoothness of scrolling in-game. 29 | SmoothScroll=true -------------------------------------------------------------------------------- /game/subtitans.ini: -------------------------------------------------------------------------------- 1 | [SETTING] 2 | ; Override the detected screen width (0: Ignore). 3 | Width=0 4 | ; Override the detected screen height (0: Ignore). 5 | Height=0 6 | 7 | [FEATURE] 8 | ; Provides widescreen support with propper scaling of the GUI. 9 | ; This patch overrides the in-game resolution of 1280x1024. 10 | NativeResolution=true 11 | ; Provides an alternative for the obsolete DirectDraw renderer (0: Automatic, 1: DirectDraw, 2: OpenGL, 3: Software). 12 | ; Set the value to 1 when using a custom DirectDraw wrapper. 13 | Renderer=0 14 | ; Add the 'orbiton' cheat for mission skipping, see the readme for more information. 15 | MissionSkipCheat=true 16 | ; Provides an alternative for the obsolete DirectInput input handling. 17 | ; Set this variable to false if you're experiencing problems with mouse or keyboard input. 18 | ; This modification doesn't work in combination with the DirectDraw renderer. 19 | CustomInput=true 20 | ; Shader that tries to mimic CRT behavior. The OpenGL renderer is required for this effect. 21 | RetroShader=false 22 | 23 | [FIX] 24 | ; Fixes a crash caused by the video player. 25 | MovieHeap=true 26 | ; Replaces the default FPS limiter with a better implementation. 27 | ImprovedFPSLimiter=true 28 | ; Improves the smoothness of scrolling in-game. 29 | SmoothScroll=true -------------------------------------------------------------------------------- /Release/subtitans.ini: -------------------------------------------------------------------------------- 1 | [SETTING] 2 | ; Override the detected screen width (0: Ignore). 3 | Width=0 4 | ; Override the detected screen height (0: Ignore). 5 | Height=0 6 | 7 | [FEATURE] 8 | ; Provides widescreen support with propper scaling of the GUI. 9 | ; This patch overrides the in-game resolution of 1280x1024. 10 | NativeResolution=true 11 | ; Provides an alternative for the obsolete DirectDraw renderer (0: Automatic, 1: DirectDraw, 2: OpenGL, 3: Software). 12 | ; Set the value to 1 when using a custom DirectDraw wrapper. 13 | Renderer=0 14 | ; Add the 'orbiton' cheat for mission skipping, see the readme for more information. 15 | MissionSkipCheat=true 16 | ; Provides an alternative for the obsolete DirectInput input handling. 17 | ; Set this variable to false if you're experiencing problems with mouse or keyboard input. 18 | ; This modification doesn't work in combination with the DirectDraw renderer. 19 | CustomInput=true 20 | ; Shader that tries to mimic CRT behavior. The OpenGL renderer is required for this effect. 21 | RetroShader=false 22 | 23 | [FIX] 24 | ; Fixes a crash caused by the video player. 25 | MovieHeap=true 26 | ; Replaces the default FPS limiter with a better implementation. 27 | ImprovedFPSLimiter=true 28 | ; Improves the smoothness of scrolling in-game. 29 | SmoothScroll=true -------------------------------------------------------------------------------- /shared/shared.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | memory 7 | 8 | 9 | memory 10 | 11 | 12 | 13 | 14 | io 15 | 16 | 17 | 18 | 19 | {9b895b6d-8336-44b3-9ef4-43a43db59e08} 20 | 21 | 22 | {ebff36a6-0f63-4b4e-a2f5-e31dbaddb77a} 23 | 24 | 25 | 26 | 27 | memory 28 | 29 | 30 | memory 31 | 32 | 33 | 34 | 35 | io 36 | 37 | 38 | -------------------------------------------------------------------------------- /subtitans/nativeresolutionpatch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "patch.h" 3 | 4 | class NativeResolutionPatch : public Patch { 5 | public: 6 | NativeResolutionPatch(); 7 | virtual ~NativeResolutionPatch(); 8 | 9 | bool Validate() override; 10 | bool Apply() override; 11 | const wchar_t* ErrorMessage() override { return L"Failed to apply Native Resolution patch"; } 12 | 13 | unsigned long GuiRescalerAddress; 14 | 15 | unsigned long QueueScreenAddress; 16 | 17 | unsigned long ScreenInitialResizeWidthAddress; 18 | unsigned long ScreenInitialResizeHeightAddress; 19 | 20 | unsigned long ScreenResizeWidthCompareAddress; 21 | unsigned long ScreenResizeWidthAddress; 22 | unsigned long ScreenResizeHeightAddress; 23 | 24 | unsigned long GamefieldPresetWidthAddress; 25 | unsigned long GamefieldPresetHeightAddress; 26 | 27 | unsigned long GamefieldHeightReducingAddress; 28 | unsigned long GamefieldHeightRestorationAddress; 29 | 30 | unsigned long MovieWidthAddress; 31 | unsigned long MovieHeightAddress; 32 | 33 | unsigned long RepositionBottomMenuDetourAddress; 34 | unsigned long RenameSettingsDetourAddress; 35 | unsigned long RenameSettingsFunctionAddress; 36 | unsigned long RedesignFrameDetourAddress; 37 | unsigned long RedesignFrameTeamIdMemoryAddress; 38 | unsigned long RedesignFrameDrawFunctionAddress; 39 | unsigned long RepositionBriefingDetourAddress; 40 | 41 | unsigned long CurrentScreenWidthAddress; 42 | }; -------------------------------------------------------------------------------- /d3drm/d3drm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "types.h" 5 | #include "../shared/gameversion.h" 6 | #include "../shared/file.h" 7 | #include "injector.h" 8 | 9 | #ifdef _DEBUG 10 | #pragma comment(lib, "../Debug/shared.lib") 11 | #else 12 | #pragma comment(lib, "../Release/shared.lib") 13 | #endif 14 | 15 | #pragma comment(linker, "/EXPORT:D3DRMVectorModulus=_D3DRMVectorModulus@4") 16 | extern "C" float __stdcall D3DRMVectorModulus(D3DVector3* vector) 17 | { 18 | if (vector == nullptr) 19 | return 0.f; 20 | 21 | return static_cast(sqrt(vector->x * vector->x + vector->y * vector->y + vector->z * vector->z)); 22 | } 23 | 24 | uint32_t GetSubTitansVersion() 25 | { 26 | WCHAR applicationPath[MAX_PATH]; 27 | GetModuleFileName(NULL, applicationPath, MAX_PATH); 28 | 29 | uint32_t checkSum = File::CalculateChecksum(applicationPath); 30 | 31 | switch (checkSum) 32 | { 33 | case Shared::ST_GAMEVERSION_RETAIL_UNPATCHED: 34 | case Shared::ST_GAMEVERSION_RETAIL_PATCHED: 35 | case Shared::ST_GAMEVERSION_GOG_MODIFIED: 36 | case Shared::ST_GAMEVERSION_DEMO: 37 | return checkSum; 38 | default: 39 | return 0; 40 | } 41 | } 42 | 43 | // Use d3drm for injecting custom DLL 44 | BOOLEAN __stdcall DllMain(HINSTANCE handle, DWORD reason, LPVOID reserved) 45 | { 46 | // Warning: Only use functions available in Kernel32 47 | if (reason == DLL_PROCESS_ATTACH) 48 | { 49 | uint32_t gameVersion = GetSubTitansVersion(); 50 | if (gameVersion == 0) 51 | return FALSE; 52 | 53 | // Allow uninstalling patch by removing SubTitans.dll 54 | if (!File::Exists(L"SubTitans.dll")) 55 | return TRUE; 56 | 57 | return Injector::Apply(gameVersion); 58 | } 59 | 60 | return TRUE; 61 | } -------------------------------------------------------------------------------- /shared/memorywriter.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "memorywriter.h" 3 | 4 | namespace MemoryWriter { 5 | struct _reservedAddressSpace 6 | { 7 | unsigned long address; 8 | SIZE_T length; 9 | 10 | _reservedAddressSpace(unsigned long x, SIZE_T y) 11 | { 12 | address = x; 13 | length = y; 14 | } 15 | }; 16 | static std::vector<_reservedAddressSpace> _reservedAddressSpaces; 17 | 18 | bool AddressIntersects(unsigned long left, unsigned long leftSize) 19 | { 20 | for (auto it = _reservedAddressSpaces.begin(); it != _reservedAddressSpaces.end(); ++it) 21 | { 22 | unsigned long leftEnd = left + leftSize; 23 | unsigned long rightEnd = it->address + it->length; 24 | if (left < rightEnd && leftEnd > it->address /* right */) 25 | return true; 26 | } 27 | 28 | _reservedAddressSpaces.push_back(_reservedAddressSpace(left, leftSize)); 29 | return false; 30 | } 31 | 32 | bool Write(unsigned long address, unsigned char* bytes, SIZE_T length, bool enforceNoIntersecting) 33 | { 34 | // Prevent intersecting detours/overwrites 35 | if (enforceNoIntersecting && AddressIntersects(address, length)) 36 | return false; 37 | 38 | HANDLE currentProcess = GetCurrentProcess(); 39 | unsigned long* addressPointer = (unsigned long*)address; 40 | 41 | unsigned long previousProtection = 0; 42 | if (VirtualProtectEx(currentProcess, addressPointer, length, PAGE_EXECUTE_READWRITE, &previousProtection) == FALSE) 43 | return false; 44 | 45 | memcpy(addressPointer, bytes, length); 46 | 47 | FlushInstructionCache(currentProcess, addressPointer, length); // Failing is ok...ish 48 | 49 | return VirtualProtectEx(currentProcess, addressPointer, length, previousProtection, &previousProtection) == TRUE; 50 | } 51 | } -------------------------------------------------------------------------------- /subtitans/clipper.cpp: -------------------------------------------------------------------------------- 1 | #include "subtitans.h" 2 | #include "clipper.h" 3 | 4 | using namespace DDraw; 5 | 6 | Clipper::Clipper() 7 | { 8 | GetLogger()->Trace("%s\n", __FUNCTION__); 9 | referenceCount = 0; 10 | } 11 | 12 | Clipper::~Clipper() { GetLogger()->Trace("%s\n", __FUNCTION__); } 13 | 14 | // IUnknown 15 | uint32_t __stdcall Clipper::QueryInterface(GUID* guid, void** result) 16 | { 17 | GetLogger()->Trace("%s\n", __FUNCTION__); 18 | 19 | GetLogger()->Error("%s %s\n", __FUNCTION__, "unknown interface"); 20 | *result = nullptr; 21 | return ResultCode::NoInterface; 22 | } 23 | 24 | uint32_t __stdcall Clipper::AddRef() 25 | { 26 | GetLogger()->Trace("%s\n", __FUNCTION__); 27 | 28 | referenceCount++; 29 | 30 | return ResultCode::Ok; 31 | } 32 | 33 | uint32_t __stdcall Clipper::Release() 34 | { 35 | GetLogger()->Trace("%s (Remaining references %i)\n", __FUNCTION__, referenceCount); 36 | 37 | if (--referenceCount == 0) 38 | delete this; 39 | 40 | return ResultCode::Ok; 41 | } 42 | 43 | // clipper 44 | uint32_t __stdcall Clipper::GetClipList(void*, void*, void*) 45 | { 46 | GetLogger()->Trace("%s\n", __FUNCTION__); 47 | return ResultCode::NoClipList; 48 | } 49 | 50 | uint32_t __stdcall Clipper::GetHWnd(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 51 | uint32_t __stdcall Clipper::Initialize(void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 52 | uint32_t __stdcall Clipper::IsClipListChanged(uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 53 | uint32_t __stdcall Clipper::SetClipList(void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 54 | 55 | uint32_t __stdcall Clipper::SetHWnd(uint32_t, void*) 56 | { 57 | GetLogger()->Trace("%s\n", __FUNCTION__); 58 | return ResultCode::Ok; 59 | } -------------------------------------------------------------------------------- /shared/configuration.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "configuration.h" 3 | 4 | std::wstring PrependCurrentWorkingDirectory(std::wstring configFileName) 5 | { 6 | uint32_t length = GetCurrentDirectory(0, NULL); 7 | if (length == 0) 8 | return configFileName; 9 | 10 | WCHAR* applicationPath = new WCHAR[length + 1]; 11 | GetCurrentDirectory(length + 1, applicationPath); 12 | if (length == 0) 13 | { 14 | delete[] applicationPath; 15 | return configFileName; 16 | } 17 | 18 | std::wstring result = std::wstring(applicationPath) + L'/' + configFileName; 19 | 20 | delete[] applicationPath; 21 | return result; 22 | } 23 | 24 | Configuration::Configuration(std::wstring configFileName) 25 | { 26 | _configFilePath = PrependCurrentWorkingDirectory(configFileName); 27 | } 28 | 29 | Configuration::~Configuration() 30 | { 31 | 32 | } 33 | 34 | bool Configuration::GetBoolean(std::wstring category, std::wstring key, bool placeholder) 35 | { 36 | WCHAR buffer[6]; 37 | GetPrivateProfileString(category.c_str(), key.c_str(), placeholder ? L"true" : L"false", buffer, sizeof(buffer) / 2, _configFilePath.c_str()); 38 | 39 | bool result = _wcsicmp(L"true", buffer) == 0; 40 | result |= _wcsicmp(L"yes", buffer) == 0; 41 | result |= _wcsicmp(L"on", buffer) == 0; 42 | result |= _wcsicmp(L"1", buffer) == 0; 43 | 44 | return result; 45 | } 46 | 47 | std::wstring Configuration::GetString(std::wstring category, std::wstring key, std::wstring placeholder) 48 | { 49 | WCHAR buffer[64]; 50 | GetPrivateProfileString(category.c_str(), key.c_str(), placeholder.c_str(), buffer, sizeof(buffer) / 2, _configFilePath.c_str()); 51 | 52 | return std::wstring(buffer); 53 | } 54 | 55 | int32_t Configuration::GetInt32(std::wstring category, std::wstring key, int32_t placeholder) 56 | { 57 | return GetPrivateProfileInt(category.c_str(), key.c_str(), placeholder, _configFilePath.c_str()); 58 | } -------------------------------------------------------------------------------- /shared/file.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "file.h" 3 | 4 | // Only Kernel32 dependent 5 | bool File::Exists(const wchar_t* path) 6 | { 7 | WIN32_FIND_DATAW findData; 8 | HANDLE findHandle = FindFirstFileW(path, &findData); 9 | if (findHandle == INVALID_HANDLE_VALUE) 10 | return false; 11 | 12 | FindClose(findHandle); 13 | return true; 14 | } 15 | 16 | // Only Kernel32 dependent 17 | constexpr uint32_t CRC32_POLYNOMIAL = 0xEDB88320; 18 | unsigned int File::CalculateChecksum(const wchar_t* path) 19 | { 20 | HANDLE fileHandle = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 21 | if (fileHandle == INVALID_HANDLE_VALUE) 22 | return 0; 23 | 24 | uint32_t allocatedSize = GetFileSize(fileHandle, NULL); 25 | 26 | HANDLE processHeap = GetProcessHeap(); 27 | if (processHeap == NULL) 28 | { 29 | CloseHandle(fileHandle); 30 | return 0; 31 | } 32 | 33 | void* allocatedMemory = HeapAlloc(processHeap, HEAP_ZERO_MEMORY, allocatedSize); 34 | if (allocatedMemory == nullptr) 35 | { 36 | CloseHandle(fileHandle); 37 | return 0; 38 | } 39 | 40 | DWORD bytesRead = 0; 41 | if (!ReadFile(fileHandle, allocatedMemory, allocatedSize, &bytesRead, 0) || bytesRead != allocatedSize) 42 | { 43 | CloseHandle(fileHandle); 44 | HeapFree(processHeap, 0, allocatedMemory); 45 | 46 | return 0; 47 | } 48 | 49 | CloseHandle(fileHandle); 50 | 51 | uint32_t checkSumTable[256]; 52 | for (uint32_t tableIt = 0; tableIt < sizeof(checkSumTable) / sizeof(uint32_t); ++tableIt) 53 | { 54 | checkSumTable[tableIt] = tableIt; 55 | for (uint32_t i = 0; i < 8; ++i) 56 | { 57 | checkSumTable[tableIt] = checkSumTable[tableIt] & 1 ? 58 | checkSumTable[tableIt] >> 1 ^ CRC32_POLYNOMIAL 59 | : checkSumTable[tableIt] >> 1; 60 | } 61 | } 62 | 63 | uint32_t checkSumResult = ~0; 64 | for (uint32_t i = 0; i < allocatedSize; ++i) 65 | { 66 | checkSumResult = checkSumTable[checkSumResult & 0xFF ^ *((uint8_t*)allocatedMemory + i)] ^ checkSumResult >> 8; 67 | } 68 | 69 | HeapFree(processHeap, 0, allocatedMemory); 70 | 71 | return ~checkSumResult; 72 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SubTitans 2 | Unofficial Submarine Titans patch \ 3 | ![GitHub all releases](https://img.shields.io/github/downloads/UnknownException/SubTitans/total) 4 | 5 | **Requirements** 6 | * Windows 7, Windows 10, Windows 11 or Wine (+ Ubuntu) 7 | * Supports Steam/Retail v1.0, Retail v1.1 and GOG v1.1 8 | 9 | **Features** 10 | * Support for any resolution in-game (tested up to 3840x2160) 11 | * OpenGL and Software rendering replacement for DirectDraw to improve compatibility and performance 12 | * DInput replacement 13 | * Improved scrolling 14 | * Added mission skip cheat (Retail/GOG v1.1; workaround for progression bugs) 15 | * Fixes various internal errors 16 | * Fixes alt-tab crashes 17 | * Support for display scaling 18 | * Windows 7 palette color fix 19 | 20 | **Instructions** 21 | 1. Copy & paste d3drm.dll, subtitans.dll and subtitans.ini into your Submarine Titans folder. 22 | 2. Open STConfig.exe and select 1280x1024. 23 | 3. Run the game. 24 | 25 | **Q&A** \ 26 | Q: How do I use the mission skip cheat? \ 27 | A: Load the mission you want to skip and write 'orbiton' without quotes in the chatbox. \ 28 | Exit to the menu and select a random campaign to start the next mission. \ 29 | \ 30 | Q: Help, I'm getting MSVCP140.dll errors! \ 31 | A: Install vc_redist.x86.exe ( https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads ). \ 32 | \ 33 | Q: How to update Submarine Titans to version 1.1? \ 34 | A: https://steamcommunity.com/sharedfiles/filedetails/?id=2129291420 \ 35 | \ 36 | Q: I've got a problem in-game with my mouse and/or keyboard. \ 37 | A: Open SubTitans.ini and set CustomInput to false, this should revert the input handling changes. \ 38 | \ 39 | Q: I've got a problem with the OpenGL/Software renderer and want to use a custom DDraw wrapper. \ 40 | A: Open SubTitans.ini and set Renderer to 1. \ 41 | \ 42 | Q: The Submarine Titans demo crashes with or without this patch. \ 43 | 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. 44 | 45 | **Known bugs** 46 | * Regions next to the in-game command panel aren't selectable/clickable. 47 | 48 | **Uninstall** 49 | * Deleting SubTitans.dll will disable the in-game patches. -------------------------------------------------------------------------------- /subtitans/scrollpatch.cpp: -------------------------------------------------------------------------------- 1 | #include "subtitans.h" 2 | #include "scrollpatch.h" 3 | 4 | namespace ScrollSpeed { 5 | // Detour variables 6 | constexpr unsigned long DetourSize = 7; 7 | static unsigned long JmpFromAddress = 0; 8 | static unsigned long JmpBackAddress = 0; 9 | static unsigned long OriginalSpeedModifiers = 0; 10 | 11 | // Function specific variables 12 | const float SpeedMultiplier = 0.76f; 13 | const float SpeedModifiers[3] = { 4.5f * SpeedMultiplier, 3.0f * SpeedMultiplier, 1.5f * SpeedMultiplier }; 14 | __declspec(naked) void Implementation() 15 | { 16 | __asm pushfd; // store eflags previous cmp 17 | 18 | __asm cmp edx, 0x02; 19 | __asm jg UNEXPECTED_RANGE; 20 | 21 | __asm fmul dword ptr ds:[edx * 0x04 + SpeedModifiers]; 22 | 23 | __asm popfd; 24 | __asm jmp [JmpBackAddress]; 25 | 26 | UNEXPECTED_RANGE: 27 | __asm push eax; 28 | __asm mov eax, [OriginalSpeedModifiers]; 29 | __asm fmul dword ptr ds:[edx * 0x04 + eax]; 30 | __asm pop eax; 31 | 32 | __asm popfd; 33 | __asm jmp [JmpBackAddress]; 34 | } 35 | } 36 | 37 | ScrollPatch::ScrollPatch() 38 | { 39 | GetLogger()->Informational("Constructing %s\n", __func__); 40 | 41 | UpdateRateAddress = 0; 42 | DetourAddress = 0; 43 | OriginalSpeedModifiersAddress = 0; 44 | } 45 | 46 | ScrollPatch::~ScrollPatch() 47 | { 48 | GetLogger()->Informational("Destructing %s\n", __func__); 49 | } 50 | 51 | bool ScrollPatch::Validate() 52 | { 53 | return UpdateRateAddress != 0 && DetourAddress != 0 && OriginalSpeedModifiersAddress != 0; 54 | } 55 | 56 | bool ScrollPatch::Apply() 57 | { 58 | GetLogger()->Informational("%s\n", __FUNCTION__); 59 | 60 | // Update at least as fast as the normal game speed 61 | unsigned char updateRate[] = { 0x26 }; 62 | if (!MemoryWriter::Write(UpdateRateAddress, updateRate, sizeof(updateRate))) 63 | return false; 64 | 65 | // Revert movement speed by changing the fast/med/slow movement presets 66 | ScrollSpeed::JmpFromAddress = DetourAddress; 67 | ScrollSpeed::JmpBackAddress = ScrollSpeed::JmpFromAddress + ScrollSpeed::DetourSize; 68 | if (!Detour::Create(ScrollSpeed::JmpFromAddress, ScrollSpeed::DetourSize, (unsigned long)ScrollSpeed::Implementation)) 69 | return false; 70 | 71 | ScrollSpeed::OriginalSpeedModifiers = OriginalSpeedModifiersAddress; 72 | 73 | return true; 74 | } -------------------------------------------------------------------------------- /.github/workflows/action-release.yml: -------------------------------------------------------------------------------- 1 | name: SubTitans Release 2 | on: 3 | push: 4 | branches: [ master ] 5 | jobs: 6 | build: 7 | name: Build SubTitans 8 | runs-on: windows-2019 9 | steps: 10 | - name: Create Timestamp 11 | id: create_timestamp 12 | run: $buildTime = Get-Date -Format "yyyy-MM-dd_HH-mm-ss"; Write-Host "::set-output name=release_datestamp::$buildTime" 13 | 14 | #https://github.com/marketplace/actions/checkout 15 | - name: Check out 16 | uses: actions/checkout@v2 17 | 18 | #https://github.com/marketplace/actions/setup-msbuild 19 | - name: Add msbuild to PATH 20 | uses: microsoft/setup-msbuild@v1.0.2 21 | 22 | - name: Compile 23 | shell: cmd 24 | run: msbuild SubTitans.sln -p:Configuration=Release -p:Platform=x86 25 | 26 | - name: Create Zip 27 | shell: powershell 28 | run: | 29 | Rename-Item -Path README.md -NewName readme.txt 30 | Rename-Item -Path LICENSE -NewName license.txt 31 | $compress = @{ 32 | Path = "Release/*.dll", "readme.txt", "license.txt", "Release/*.ini" 33 | DestinationPath = "SubTitans.zip" 34 | Force = $True 35 | } 36 | Compress-Archive @compress 37 | 38 | #https://github.com/marketplace/actions/create-a-release 39 | - name: Create Release 40 | id: create_release 41 | uses: actions/create-release@v1 42 | env: 43 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 44 | with: 45 | tag_name: ${{ steps.create_timestamp.outputs.release_datestamp }} 46 | release_name: Release ${{ steps.create_timestamp.outputs.release_datestamp }} 47 | body: | 48 | Release from master branch 49 | See README.MD/readme.txt 50 | draft: false 51 | prerelease: false 52 | 53 | #https://github.com/marketplace/actions/upload-a-release-asset 54 | - name: Upload Release 55 | id: upload-release-asset 56 | uses: actions/upload-release-asset@v1 57 | env: 58 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 59 | with: 60 | upload_url: ${{ steps.create_release.outputs.upload_url }} 61 | asset_path: ./SubTitans.zip 62 | asset_name: SubTitans-${{ steps.create_timestamp.outputs.release_datestamp }}.zip 63 | asset_content_type: application/zip 64 | -------------------------------------------------------------------------------- /subtitans/demopatcher.cpp: -------------------------------------------------------------------------------- 1 | #include "subtitans.h" 2 | #include 3 | #include "sleepwellpatch.h" 4 | #include "movieheapcorruptionpatch.h" 5 | #include "ddrawreplacementpatch.h" 6 | #include "demopatcher.h" 7 | 8 | // The bare minimum to get the Submarine Titans demo running. 9 | // DirectDraw support is dropped, OpenGL or Software rendering is forced. 10 | // No support for custom resolutions/widescreen is provided. 11 | // If the game keeps crashing you must delete the savegame folder and create a profile in-game. 12 | // Disabling the intro video through stconfig.exe might help improve stability. 13 | 14 | DemoPatcher::DemoPatcher() 15 | { 16 | 17 | } 18 | 19 | DemoPatcher::~DemoPatcher() 20 | { 21 | 22 | } 23 | 24 | void DemoPatcher::Configure() 25 | { 26 | auto sleepWellPatch = new SleepWellPatch(); 27 | sleepWellPatch->DetourAddress = 0x006D1714; 28 | sleepWellPatch->FrameLimitMemoryAddress = 0x007EBF1C; 29 | sleepWellPatch->DisableOriginalLimiterSleepAddress = 0x006D173B; 30 | _patches.push_back(sleepWellPatch); 31 | 32 | Global::RenderWidth = GetConfiguration()->GetInt32(L"SETTING", L"Width", 0); 33 | if (Global::RenderWidth == 0) 34 | Global::RenderWidth = Global::MonitorWidth; 35 | 36 | Global::RenderHeight = GetConfiguration()->GetInt32(L"SETTING", L"Height", 0); 37 | if (Global::RenderHeight == 0) 38 | Global::RenderHeight = Global::MonitorHeight; 39 | 40 | bool isWindows7 = IsWindows7OrGreater() && !IsWindows8OrGreater(); 41 | auto renderingBackend = GetConfiguration()->GetInt32(L"FEATURE", L"Renderer", Global::RenderingBackend::Automatic); 42 | 43 | auto ddrawReplacementPatch = new DDrawReplacementPatch(); 44 | ddrawReplacementPatch->DDrawDetourAddress = 0x006A6B81; 45 | ddrawReplacementPatch->DInputDetourAddress = 0x00701C9E; 46 | ddrawReplacementPatch->WindowRegisterClassDetourAddress = 0x00561847; 47 | ddrawReplacementPatch->WindowCreateDetourAddress = 0x00561893; 48 | ddrawReplacementPatch->DInputAbsolutePositioningDetourAddress = 0x007020F0; 49 | ddrawReplacementPatch->ForceSoftwareRendering = renderingBackend == Global::RenderingBackend::Software 50 | || (isWindows7 && renderingBackend == Global::RenderingBackend::Automatic); // Prefer software rendering on windows 7 51 | ddrawReplacementPatch->DInputReplacement = false; // Doesn't work that well with the demo 52 | _patches.push_back(ddrawReplacementPatch); 53 | 54 | Global::RetroShader = GetConfiguration()->GetBoolean(L"FEATURE", L"RetroShader", false); 55 | } -------------------------------------------------------------------------------- /SubTitans.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30011.22 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "d3drm", "d3drm\d3drm.vcxproj", "{229FEA89-DB14-4AD2-BD12-878FE36496F0}" 7 | ProjectSection(ProjectDependencies) = postProject 8 | {5F2C7921-4B31-4712-8722-C99EFF304EC4} = {5F2C7921-4B31-4712-8722-C99EFF304EC4} 9 | {033CA3B5-4016-489A-943E-663953F6F89A} = {033CA3B5-4016-489A-943E-663953F6F89A} 10 | EndProjectSection 11 | EndProject 12 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "subtitans", "subtitans\subtitans.vcxproj", "{033CA3B5-4016-489A-943E-663953F6F89A}" 13 | ProjectSection(ProjectDependencies) = postProject 14 | {5F2C7921-4B31-4712-8722-C99EFF304EC4} = {5F2C7921-4B31-4712-8722-C99EFF304EC4} 15 | EndProjectSection 16 | EndProject 17 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shared", "shared\shared.vcxproj", "{5F2C7921-4B31-4712-8722-C99EFF304EC4}" 18 | EndProject 19 | Global 20 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 21 | Debug|x86 = Debug|x86 22 | Release|x86 = Release|x86 23 | EndGlobalSection 24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 25 | {229FEA89-DB14-4AD2-BD12-878FE36496F0}.Debug|x86.ActiveCfg = Debug|Win32 26 | {229FEA89-DB14-4AD2-BD12-878FE36496F0}.Debug|x86.Build.0 = Debug|Win32 27 | {229FEA89-DB14-4AD2-BD12-878FE36496F0}.Release|x86.ActiveCfg = Release|Win32 28 | {229FEA89-DB14-4AD2-BD12-878FE36496F0}.Release|x86.Build.0 = Release|Win32 29 | {033CA3B5-4016-489A-943E-663953F6F89A}.Debug|x86.ActiveCfg = Debug|Win32 30 | {033CA3B5-4016-489A-943E-663953F6F89A}.Debug|x86.Build.0 = Debug|Win32 31 | {033CA3B5-4016-489A-943E-663953F6F89A}.Release|x86.ActiveCfg = Release|Win32 32 | {033CA3B5-4016-489A-943E-663953F6F89A}.Release|x86.Build.0 = Release|Win32 33 | {5F2C7921-4B31-4712-8722-C99EFF304EC4}.Debug|x86.ActiveCfg = Debug|Win32 34 | {5F2C7921-4B31-4712-8722-C99EFF304EC4}.Debug|x86.Build.0 = Debug|Win32 35 | {5F2C7921-4B31-4712-8722-C99EFF304EC4}.Release|x86.ActiveCfg = Release|Win32 36 | {5F2C7921-4B31-4712-8722-C99EFF304EC4}.Release|x86.Build.0 = Release|Win32 37 | EndGlobalSection 38 | GlobalSection(SolutionProperties) = preSolution 39 | HideSolutionNode = FALSE 40 | EndGlobalSection 41 | GlobalSection(ExtensibilityGlobals) = postSolution 42 | SolutionGuid = {C2508059-5238-4A1D-963C-2BBB955B6EA7} 43 | EndGlobalSection 44 | EndGlobal 45 | -------------------------------------------------------------------------------- /subtitans/mousedevice.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "idinputdevice7.h" 3 | 4 | class MouseDevice : public DInput::IDInputDevice7 { 5 | public: 6 | MouseDevice(); 7 | virtual ~MouseDevice(); 8 | 9 | // IUnknown 10 | virtual uint32_t __stdcall QueryInterface(GUID* guid, void** result) override; 11 | virtual uint32_t __stdcall AddRef() override; 12 | virtual uint32_t __stdcall Release() override; 13 | 14 | // Inherited via IDInputDevice7 15 | virtual uint32_t __stdcall GetCapabilities(DInput::Caps* caps) override; 16 | virtual uint32_t __stdcall EnumObjects(void* callback, void*, uint32_t) override; 17 | virtual uint32_t __stdcall GetProperty(GUID& guid, void*) override; 18 | virtual uint32_t __stdcall SetProperty(GUID& guid, void*) override; 19 | virtual uint32_t __stdcall Acquire() override; 20 | virtual uint32_t __stdcall Unacquire() override; 21 | virtual uint32_t __stdcall GetDeviceState(uint32_t, void*) override; 22 | virtual uint32_t __stdcall GetDeviceData(uint32_t, void*, void*, uint32_t) override; 23 | virtual uint32_t __stdcall SetDataFormat(DInput::DataFormat*) override; 24 | virtual uint32_t __stdcall SetEventNotification(void*) override; 25 | virtual uint32_t __stdcall SetCooperativeLevel(void*, uint32_t) override; 26 | virtual uint32_t __stdcall GetObjectInfo(void*, uint32_t, uint32_t) override; 27 | virtual uint32_t __stdcall GetDeviceInfo(void*) override; 28 | virtual uint32_t __stdcall RunControlPanel(void*, uint32_t) override; 29 | virtual uint32_t __stdcall Initialize(void*, int32_t, GUID&) override; 30 | virtual uint32_t __stdcall CreateEffect(GUID&, void*, void**, void*) override; 31 | virtual uint32_t __stdcall EnumEffects(void* callback, void*, uint32_t) override; 32 | virtual uint32_t __stdcall GetEffectInfo(void*, GUID&) override; 33 | virtual uint32_t __stdcall GetForceFeedbackState(uint32_t*) override; 34 | virtual uint32_t __stdcall SendForceFeedbackCommand(uint32_t) override; 35 | virtual uint32_t __stdcall EnumCreatedEffectObjects(void* callback, void*, uint32_t) override; 36 | virtual uint32_t __stdcall Escape(void*) override; 37 | virtual uint32_t __stdcall Poll() override; 38 | virtual uint32_t __stdcall SendDeviceData(void*, void* callback, void*, uint32_t) override; 39 | virtual uint32_t __stdcall EnumEffectsInFile(void*, void* callback, void*, uint32_t) override; 40 | virtual uint32_t __stdcall WriteEffectsToFile(void*, uint32_t, void*, uint32_t) override; 41 | 42 | // Custom 43 | uint32_t ReferenceCount; 44 | 45 | bool Initialized; 46 | }; -------------------------------------------------------------------------------- /shared/logger.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #ifdef _DEBUG 3 | #include 4 | #endif 5 | #include "logger.h" 6 | 7 | //#define _TRACELOG 8 | 9 | constexpr int LogBufferSize = 1024; 10 | 11 | Logger::Logger(std::string logFilePath) 12 | { 13 | _logBuffer = new char[LogBufferSize]; 14 | _logFilePath = logFilePath; 15 | } 16 | 17 | Logger::~Logger() 18 | { 19 | delete[] _logBuffer; 20 | } 21 | 22 | void Logger::Clear() 23 | { 24 | _mutex.lock(); 25 | std::ofstream fileStream(_logFilePath, std::ofstream::out); 26 | fileStream.close(); 27 | _mutex.unlock(); 28 | } 29 | 30 | void Logger::Critical(const char* format, ...) 31 | { 32 | va_list arguments; 33 | va_start(arguments, format); 34 | Log(format, arguments, "[Critical] "); 35 | va_end(arguments); 36 | } 37 | 38 | void Logger::Error(const char* format, ...) 39 | { 40 | va_list arguments; 41 | va_start(arguments, format); 42 | Log(format, arguments, "[Error] "); 43 | va_end(arguments); 44 | } 45 | 46 | void Logger::Warning(const char* format, ...) 47 | { 48 | va_list arguments; 49 | va_start(arguments, format); 50 | Log(format, arguments, "[Warning] "); 51 | va_end(arguments); 52 | } 53 | 54 | void Logger::Informational(const char* format, ...) 55 | { 56 | va_list arguments; 57 | va_start(arguments, format); 58 | Log(format, arguments, ""); 59 | va_end(arguments); 60 | } 61 | 62 | void Logger::Debug(const char* format, ...) 63 | { 64 | #ifdef _DEBUG 65 | va_list arguments; 66 | va_start(arguments, format); 67 | Log(format, arguments, "[Debug] "); 68 | va_end(arguments); 69 | #endif 70 | } 71 | 72 | void Logger::Trace(const char* format, ...) 73 | { 74 | #ifdef _TRACELOG 75 | va_list arguments; 76 | va_start(arguments, format); 77 | Log(format, arguments, "[Trace] "); 78 | va_end(arguments); 79 | #endif 80 | } 81 | 82 | void Logger::Log(const char* format, va_list arguments, const char* prefix) 83 | { 84 | _mutex.lock(); 85 | 86 | vsnprintf(_logBuffer, LogBufferSize, format, arguments); 87 | 88 | #ifndef _TRACELOG 89 | std::ofstream fileStream(_logFilePath, std::ofstream::out | std::ofstream::app); 90 | fileStream.write(prefix, strlen(prefix)); 91 | fileStream.write(_logBuffer, strlen(_logBuffer)); 92 | fileStream.close(); 93 | #endif 94 | 95 | #ifdef _DEBUG 96 | std::string output = std::string(prefix) + std::string(_logBuffer); 97 | OutputDebugStringA(output.c_str()); 98 | #endif 99 | 100 | _mutex.unlock(); 101 | } -------------------------------------------------------------------------------- /subtitans/keyboarddevice.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "idinputdevice7.h" 3 | 4 | class KeyboardDevice : public DInput::IDInputDevice7 { 5 | public: 6 | KeyboardDevice(); 7 | virtual ~KeyboardDevice(); 8 | 9 | // IUnknown 10 | virtual uint32_t __stdcall QueryInterface(GUID* guid, void** result) override; 11 | virtual uint32_t __stdcall AddRef() override; 12 | virtual uint32_t __stdcall Release() override; 13 | 14 | // Inherited via IDInputDevice7 15 | virtual uint32_t __stdcall GetCapabilities(DInput::Caps* caps) override; 16 | virtual uint32_t __stdcall EnumObjects(void* callback, void*, uint32_t) override; 17 | virtual uint32_t __stdcall GetProperty(GUID& guid, void*) override; 18 | virtual uint32_t __stdcall SetProperty(GUID& guid, void*) override; 19 | virtual uint32_t __stdcall Acquire() override; 20 | virtual uint32_t __stdcall Unacquire() override; 21 | virtual uint32_t __stdcall GetDeviceState(uint32_t, void*) override; 22 | virtual uint32_t __stdcall GetDeviceData(uint32_t, void*, void*, uint32_t) override; 23 | virtual uint32_t __stdcall SetDataFormat(DInput::DataFormat*) override; 24 | virtual uint32_t __stdcall SetEventNotification(void*) override; 25 | virtual uint32_t __stdcall SetCooperativeLevel(void*, uint32_t) override; 26 | virtual uint32_t __stdcall GetObjectInfo(void*, uint32_t, uint32_t) override; 27 | virtual uint32_t __stdcall GetDeviceInfo(void*) override; 28 | virtual uint32_t __stdcall RunControlPanel(void*, uint32_t) override; 29 | virtual uint32_t __stdcall Initialize(void*, int32_t, GUID&) override; 30 | virtual uint32_t __stdcall CreateEffect(GUID&, void*, void**, void*) override; 31 | virtual uint32_t __stdcall EnumEffects(void* callback, void*, uint32_t) override; 32 | virtual uint32_t __stdcall GetEffectInfo(void*, GUID&) override; 33 | virtual uint32_t __stdcall GetForceFeedbackState(uint32_t*) override; 34 | virtual uint32_t __stdcall SendForceFeedbackCommand(uint32_t) override; 35 | virtual uint32_t __stdcall EnumCreatedEffectObjects(void* callback, void*, uint32_t) override; 36 | virtual uint32_t __stdcall Escape(void*) override; 37 | virtual uint32_t __stdcall Poll() override; 38 | virtual uint32_t __stdcall SendDeviceData(void*, void* callback, void*, uint32_t) override; 39 | virtual uint32_t __stdcall EnumEffectsInFile(void*, void* callback, void*, uint32_t) override; 40 | virtual uint32_t __stdcall WriteEffectsToFile(void*, uint32_t, void*, uint32_t) override; 41 | 42 | // Custom 43 | uint32_t ReferenceCount; 44 | 45 | bool Initialized; 46 | }; -------------------------------------------------------------------------------- /.github/workflows/action-development.yml: -------------------------------------------------------------------------------- 1 | name: SubTitans Development 2 | on: 3 | push: 4 | branches: [ development ] 5 | jobs: 6 | build: 7 | name: Build SubTitans 8 | runs-on: windows-2019 9 | steps: 10 | - name: Create Timestamp 11 | id: create_timestamp 12 | run: $buildTime = Get-Date -Format "yyyy-MM-dd_HH-mm-ss"; Write-Host "::set-output name=release_datestamp::$buildTime" 13 | 14 | #https://github.com/marketplace/actions/checkout 15 | - name: Check out 16 | uses: actions/checkout@v2 17 | with: 18 | ref: development 19 | 20 | #https://github.com/marketplace/actions/setup-msbuild 21 | - name: Add msbuild to PATH 22 | uses: microsoft/setup-msbuild@v1.0.2 23 | 24 | - name: Compile 25 | shell: cmd 26 | run: msbuild SubTitans.sln -p:Configuration=Release -p:Platform=x86 27 | 28 | - name: Create Zip 29 | shell: powershell 30 | run: | 31 | Rename-Item -Path README.md -NewName readme.txt 32 | Rename-Item -Path LICENSE -NewName license.txt 33 | $compress = @{ 34 | Path = "Release/*.dll", "readme.txt", "license.txt", "Release/*.ini" 35 | DestinationPath = "SubTitans.zip" 36 | Force = $True 37 | } 38 | Compress-Archive @compress 39 | 40 | #https://github.com/marketplace/actions/create-a-release 41 | - name: Create Release 42 | id: create_release 43 | uses: actions/create-release@v1 44 | env: 45 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 46 | with: 47 | tag_name: ${{ steps.create_timestamp.outputs.release_datestamp }} 48 | release_name: Development ${{ steps.create_timestamp.outputs.release_datestamp }} 49 | body: | 50 | Release from development branch 51 | See README.MD/readme.txt 52 | draft: false 53 | prerelease: true 54 | 55 | #https://github.com/marketplace/actions/upload-a-release-asset 56 | - name: Upload Release 57 | id: upload-release-asset 58 | uses: actions/upload-release-asset@v1 59 | env: 60 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 61 | with: 62 | upload_url: ${{ steps.create_release.outputs.upload_url }} 63 | asset_path: ./SubTitans.zip 64 | asset_name: Dev-SubTitans-${{ steps.create_timestamp.outputs.release_datestamp }}.zip 65 | asset_content_type: application/zip 66 | -------------------------------------------------------------------------------- /subtitans/device.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "iddraw4.h" 3 | 4 | class Device : public DDraw::IDDraw4{ 5 | public: 6 | Device(); 7 | virtual ~Device(); 8 | 9 | // IUnknown 10 | virtual uint32_t __stdcall QueryInterface(GUID* guid, void** result) override; 11 | virtual uint32_t __stdcall AddRef() override; 12 | virtual uint32_t __stdcall Release() override; 13 | 14 | // Direct Draw 15 | virtual uint32_t __stdcall Compact() override; 16 | virtual uint32_t __stdcall CreateClipper(uint32_t, DDraw::IDDrawClipper** result, void*) override; 17 | virtual uint32_t __stdcall CreatePalette(uint32_t, void*, DDraw::IDDrawPalette** result, void*) override; 18 | virtual uint32_t __stdcall CreateSurface(DDraw::SurfaceDescription* surfaceDescription, DDraw::IDDrawSurface4** result, void* unused) override; 19 | virtual uint32_t __stdcall DuplicateSurface(void*, void*) override; 20 | virtual uint32_t __stdcall EnumDisplayModes(uint32_t flags, void* surfaceDescription, void* appDef, DDraw::EnumDisplayModesCallBack callback) override; 21 | virtual uint32_t __stdcall EnumSurfaces(uint32_t, void*, void*, void*) override; 22 | virtual uint32_t __stdcall FlipToGDISurface() override; 23 | virtual uint32_t __stdcall GetCaps(DDraw::Caps* caps, DDraw::Caps* helCaps) override; 24 | virtual uint32_t __stdcall GetDisplayMode(void*) override; 25 | virtual uint32_t __stdcall GetFourCCCodes(void*, void*) override; 26 | virtual uint32_t __stdcall GetGDISurface(void*) override; 27 | virtual uint32_t __stdcall GetMonitoryFrequency(void*) override; 28 | virtual uint32_t __stdcall GetScanLine(void*) override; 29 | virtual uint32_t __stdcall GetVerticalBlankStatus(void*) override; 30 | virtual uint32_t __stdcall Initialize(void*) override; 31 | virtual uint32_t __stdcall RestoreDisplayMode() override; 32 | virtual uint32_t __stdcall SetCooperativeLevel(HWND hwnd, uint32_t flags) override; 33 | virtual uint32_t __stdcall SetDisplayMode(uint32_t width, uint32_t height, uint32_t bitsPerPixel, uint32_t refreshRate, uint32_t flags) override; 34 | virtual uint32_t __stdcall WaitForVerticalBlank(uint32_t, void*) override; 35 | virtual uint32_t __stdcall GetAvailableVidMem(void*, void*, void*) override; 36 | virtual uint32_t __stdcall GetSurfaceFromDC(void*, void*) override; 37 | virtual uint32_t __stdcall RestoreAllSurfaces() override; 38 | virtual uint32_t __stdcall TestCooperativeLevel() override; 39 | virtual uint32_t __stdcall GetDeviceIdentifier(void*, uint32_t) override; 40 | 41 | // Custom 42 | private: 43 | uint32_t referenceCount; 44 | }; 45 | 46 | uint32_t __stdcall DirectDrawCreate(void*, DDraw::IDDraw4** result, void*); -------------------------------------------------------------------------------- /subtitans/sleepwellpatch.cpp: -------------------------------------------------------------------------------- 1 | #include "subtitans.h" 2 | #include "sleepwellpatch.h" 3 | 4 | namespace SleepWell { 5 | // Detour variables 6 | constexpr unsigned long DetourSize = 5; 7 | static unsigned long JmpFromAddress = 0; 8 | static unsigned long JmpBackAddress = 0; 9 | static unsigned long FrameLimitMemoryAddress = 0; 10 | 11 | // Function specific variables 12 | static unsigned long CurrentTime = timeGetTime(); 13 | static unsigned long PreviousTime = timeGetTime(); 14 | static unsigned long DeltaTime = 0; 15 | static HANDLE* RenderEvent = nullptr; 16 | 17 | #define FrameLimit *(unsigned long*)FrameLimitMemoryAddress 18 | void __stdcall SleepHandler() 19 | { 20 | if (RenderEvent) 21 | SetEvent(*RenderEvent); 22 | 23 | CurrentTime = timeGetTime(); 24 | DeltaTime = CurrentTime - PreviousTime; 25 | 26 | // Sleep (inaccurate) to prevent rendering too many frames 27 | if (DeltaTime < FrameLimit) 28 | Sleep(FrameLimit - DeltaTime); 29 | // Reset if 5 frames behind schedule 30 | else if (DeltaTime > FrameLimit * 5) 31 | PreviousTime = CurrentTime - FrameLimit * 2; 32 | 33 | // Try to prevent negative effect of sleep inaccuracy 34 | PreviousTime += FrameLimit; 35 | } 36 | #undef FrameLimit 37 | 38 | __declspec(naked) void Implementation() 39 | { 40 | // Previous instruction is an __stdcall, expect ecx, ebx and flags to be trashed 41 | __asm call [SleepHandler]; 42 | 43 | // Restore original code 44 | __asm mov eax, dword ptr ds:[esi + 0x20]; 45 | __asm test eax, eax; 46 | 47 | __asm jmp [JmpBackAddress]; 48 | } 49 | } 50 | 51 | SleepWellPatch::SleepWellPatch() 52 | { 53 | GetLogger()->Informational("Constructing %s\n", __func__); 54 | 55 | DetourAddress = 0; 56 | FrameLimitMemoryAddress = 0; 57 | DisableOriginalLimiterSleepAddress = 0; 58 | } 59 | 60 | SleepWellPatch::~SleepWellPatch() 61 | { 62 | GetLogger()->Informational("Destructing %s\n", __func__); 63 | } 64 | 65 | bool SleepWellPatch::Validate() 66 | { 67 | return DetourAddress != 0 && FrameLimitMemoryAddress != 0 && DisableOriginalLimiterSleepAddress != 0; 68 | } 69 | 70 | bool SleepWellPatch::Apply() 71 | { 72 | GetLogger()->Informational("%s\n", __FUNCTION__); 73 | 74 | SleepWell::JmpFromAddress = DetourAddress; 75 | SleepWell::JmpBackAddress = SleepWell::JmpFromAddress + SleepWell::DetourSize; 76 | SleepWell::FrameLimitMemoryAddress = FrameLimitMemoryAddress; 77 | 78 | if (!Detour::Create(SleepWell::JmpFromAddress, SleepWell::DetourSize, (unsigned long)SleepWell::Implementation)) 79 | return false; 80 | 81 | unsigned char removeOriginalSleepCall[] = { 0x8B, 0xCE, 0x90, 0x90, 0x90, 0x90 }; 82 | if (!MemoryWriter::Write(DisableOriginalLimiterSleepAddress, removeOriginalSleepCall, sizeof(removeOriginalSleepCall))) 83 | return false; 84 | 85 | SleepWell::RenderEvent = &Global::RenderEvent; 86 | 87 | return true; 88 | } -------------------------------------------------------------------------------- /subtitans/imgui/imgui_impl_opengl3.h: -------------------------------------------------------------------------------- 1 | // dear imgui: Renderer Backend for modern OpenGL with shaders / programmatic pipeline 2 | // - Desktop GL: 2.x 3.x 4.x 3 | // - Embedded GL: ES 2.0 (WebGL 1.0), ES 3.0 (WebGL 2.0) 4 | // This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..) 5 | 6 | // Implemented features: 7 | // [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID! 8 | // [x] Renderer: Desktop GL only: Support for large meshes (64k+ vertices) with 16-bit indices. 9 | 10 | // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 11 | // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. 12 | // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. 13 | // Read online: https://github.com/ocornut/imgui/tree/master/docs 14 | 15 | // About GLSL version: 16 | // The 'glsl_version' initialization parameter should be NULL (default) or a "#version XXX" string. 17 | // On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es" 18 | // Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp. 19 | 20 | #pragma once 21 | #include "imgui.h" // IMGUI_IMPL_API 22 | 23 | // Backend API 24 | IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL); 25 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown(); 26 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame(); 27 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data); 28 | 29 | // (Optional) Called by Init/NewFrame/Shutdown 30 | IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture(); 31 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture(); 32 | IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects(); 33 | IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects(); 34 | 35 | // Specific OpenGL ES versions 36 | //#define IMGUI_IMPL_OPENGL_ES2 // Auto-detected on Emscripten 37 | //#define IMGUI_IMPL_OPENGL_ES3 // Auto-detected on iOS/Android 38 | 39 | // You can explicitly select GLES2 or GLES3 API by using one of the '#define IMGUI_IMPL_OPENGL_LOADER_XXX' in imconfig.h or compiler command-line. 40 | #if !defined(IMGUI_IMPL_OPENGL_ES2) \ 41 | && !defined(IMGUI_IMPL_OPENGL_ES3) 42 | 43 | // Try to detect GLES on matching platforms 44 | #if defined(__APPLE__) 45 | #include 46 | #endif 47 | #if (defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)) || (defined(__ANDROID__)) 48 | #define IMGUI_IMPL_OPENGL_ES3 // iOS, Android -> GL ES 3, "#version 300 es" 49 | #elif defined(__EMSCRIPTEN__) 50 | #define IMGUI_IMPL_OPENGL_ES2 // Emscripten -> GL ES 2, "#version 100" 51 | #else 52 | // Otherwise imgui_impl_opengl3_loader.h will be used. 53 | #endif 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /subtitans/input.cpp: -------------------------------------------------------------------------------- 1 | #include "subtitans.h" 2 | #include "mousedevice.h" 3 | #include "keyboarddevice.h" 4 | #include "input.h" 5 | 6 | Input::Input() 7 | { 8 | ReferenceCount = 0; 9 | } 10 | 11 | Input::~Input() 12 | { 13 | } 14 | 15 | uint32_t __stdcall Input::QueryInterface(GUID* guid, void** result) 16 | { 17 | unsigned long guid4[] = { 0, 0 }; 18 | memcpy(guid4, guid->Data4, sizeof(guid4)); 19 | GetLogger()->Error("Unknown interface %08X-%04X-%04X-%08X%08X\n", guid->Data1, guid->Data2, guid->Data3, guid4[0], guid4[1]); 20 | *result = nullptr; 21 | return ResultCode::NoInterface; 22 | } 23 | 24 | uint32_t __stdcall Input::AddRef() 25 | { 26 | GetLogger()->Trace("%s\n", __FUNCTION__); 27 | 28 | ReferenceCount++; 29 | 30 | return ResultCode::Ok; 31 | } 32 | 33 | uint32_t __stdcall Input::Release() 34 | { 35 | GetLogger()->Trace("%s\n", __FUNCTION__); 36 | 37 | if (--ReferenceCount == 0) 38 | delete this; 39 | 40 | return ResultCode::Ok; 41 | } 42 | 43 | uint32_t __stdcall Input::CreateDevice(GUID* guid, DInput::IDInputDevice7** result, void*) 44 | { 45 | GetLogger()->Trace("%s\n", __FUNCTION__); 46 | 47 | GUID mouseGuid; 48 | mouseGuid.Data1 = 0x6F1D2B60; 49 | mouseGuid.Data2 = 0xD5A0; 50 | mouseGuid.Data3 = 0x11CF; 51 | uint32_t value = 0x4544C7BF; 52 | memcpy(mouseGuid.Data4, &value, sizeof(uint32_t)); 53 | value = 0x00005453; 54 | memcpy(&mouseGuid.Data4[4], &value, sizeof(uint32_t)); 55 | 56 | GUID keyboardGuid; 57 | memcpy(&keyboardGuid, &mouseGuid, sizeof(GUID)); 58 | keyboardGuid.Data1 = 0x6F1D2B61; 59 | 60 | if (memcmp(guid, &mouseGuid, sizeof(GUID)) == 0) 61 | { 62 | GetLogger()->Informational("Mouse requested\n"); 63 | *result = new MouseDevice(); 64 | (*result)->AddRef(); 65 | } 66 | else if (memcmp(guid, &keyboardGuid, sizeof(GUID)) == 0) 67 | { 68 | GetLogger()->Informational("Keyboard requested\n"); 69 | *result = new KeyboardDevice(); 70 | (*result)->AddRef(); 71 | } 72 | else 73 | { 74 | GetLogger()->Error("Unsupported device (%08X)\n", guid->Data1); 75 | *result = nullptr; 76 | return ResultCode::InvalidArgument; 77 | } 78 | 79 | return ResultCode::Ok; 80 | } 81 | 82 | uint32_t __stdcall Input::EnumDevices(void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 83 | uint32_t __stdcall Input::GetDeviceStatus(GUID* guid) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 84 | uint32_t __stdcall Input::RunControlPanel(HWND, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 85 | uint32_t __stdcall Input::Initialize(HINSTANCE hInstance, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 86 | uint32_t __stdcall Input::FindDevice(GUID* guid, void* str, void* guid2) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 87 | uint32_t __stdcall Input::CreateDeviceEx(GUID*, GUID, void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 88 | 89 | uint32_t __stdcall DirectInputCreate(HINSTANCE hInstance, uint32_t version, DInput::IDInput7** result, void*) 90 | { 91 | GetLogger()->Debug("DInput version %08X\n", version); 92 | 93 | *result = new Input(); 94 | (*result)->AddRef(); 95 | 96 | return ResultCode::Ok; 97 | } 98 | -------------------------------------------------------------------------------- /subtitans/subtitans.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #pragma comment(lib, "winmm.lib") 14 | 15 | #include "../shared/gameversion.h" 16 | #include "../shared/detour.h" 17 | #include "../shared/memorywriter.h" 18 | #include "../shared/logger.h" 19 | #include "../shared/configuration.h" 20 | #include "../shared/file.h" 21 | 22 | #ifdef _DEBUG 23 | #pragma comment(lib, "../Debug/shared.lib") 24 | #else 25 | #pragma comment(lib, "../Release/shared.lib") 26 | #endif 27 | 28 | Logger* GetLogger(); // Singleton 29 | Configuration* GetConfiguration(); // Singleton 30 | 31 | namespace ResultCode { 32 | constexpr uint32_t Ok = 0x00000000; 33 | constexpr uint32_t InvalidArgument = 0x80070057; // DDraw invalid parameter, DInput invalid parameter 34 | constexpr uint32_t Unimplemented = 0x80004001; // DDraw unsupported 35 | constexpr uint32_t NoInterface = 0x80004002; 36 | 37 | // DDraw 38 | constexpr uint32_t NoClipList = 0x887600CD; 39 | constexpr uint32_t InvalidObject = 0x88760082; 40 | 41 | // DInput 42 | constexpr uint32_t NotInitialized = 0x80070015; 43 | } 44 | 45 | class IRenderer; 46 | namespace Global { 47 | enum RenderingBackend { 48 | Automatic = 0, // Same as using OpenGL 49 | DirectDraw, 50 | OpenGL, 51 | Software 52 | }; 53 | 54 | extern int32_t InternalWidth; // Variable 55 | extern int32_t InternalHeight; // Variable 56 | extern int32_t MonitorWidth; // Constant 57 | extern int32_t MonitorHeight; // Constant 58 | extern int32_t RenderWidth; // Constant 59 | extern int32_t RenderHeight; // Constant 60 | 61 | extern int32_t BitsPerPixel; // Variable 62 | 63 | extern bool VideoWorkaround; // Variable 64 | 65 | extern HWND GameWindow; // Don't use this with GetDC/ReleaseDC on different threads 66 | 67 | extern HANDLE RenderEvent; 68 | extern HANDLE VerticalBlankEvent; 69 | 70 | extern IRenderer* Backend; // Create/Delete called from ddrawreplacement 71 | 72 | extern bool RetroShader; // Constant 73 | extern std::atomic ImGuiEnabled; 74 | 75 | __forceinline float GetScaleFactor() { return (float)MonitorHeight / (float)InternalHeight; } 76 | __forceinline int32_t GetAspectRatioCompensatedWidth() { return (int32_t)(InternalWidth * GetScaleFactor()); } 77 | __forceinline int32_t GetPadding() { return (int32_t)((MonitorWidth - GetAspectRatioCompensatedWidth()) / 2.f); } 78 | 79 | struct RenderInformation { 80 | int32_t InternalWidth; 81 | int32_t InternalHeight; 82 | int32_t MonitorWidth; 83 | int32_t MonitorHeight; 84 | int32_t RenderWidth; 85 | int32_t RenderHeight; 86 | int32_t BitsPerPixel; 87 | int32_t AspectRatioCompensatedWidth; 88 | int32_t Padding; 89 | }; 90 | 91 | __forceinline RenderInformation GetRenderInformation() { 92 | RenderInformation ri; 93 | memset(&ri, 0, sizeof(RenderInformation)); 94 | 95 | ri.InternalWidth = InternalWidth; 96 | ri.InternalHeight = InternalHeight; 97 | ri.MonitorWidth = MonitorWidth; 98 | ri.MonitorHeight = MonitorHeight; 99 | ri.RenderWidth = RenderWidth; 100 | ri.RenderHeight = RenderHeight; 101 | ri.BitsPerPixel = BitsPerPixel; 102 | ri.AspectRatioCompensatedWidth = GetAspectRatioCompensatedWidth(); 103 | ri.Padding = GetPadding(); 104 | 105 | return ri; 106 | } 107 | 108 | #pragma pack(push, 1) 109 | struct _MouseInformation { 110 | int32_t x; 111 | int32_t y; 112 | int32_t z; 113 | uint8_t lPressed; 114 | uint8_t rPressed; 115 | uint8_t mPressed; 116 | uint8_t xPressed; 117 | }; 118 | 119 | struct _KeyboardInformation { 120 | uint8_t keyPressed[256]; 121 | }; 122 | #pragma pack(pop) 123 | 124 | extern _MouseInformation MouseInformation; 125 | extern _KeyboardInformation KeyboardInformation; 126 | 127 | constexpr uint8_t KeyPressedFlag = 0x80; 128 | constexpr uint8_t KeyReleasedFlag = 0x00; 129 | } -------------------------------------------------------------------------------- /subtitans/glew/LICENSE.txt: -------------------------------------------------------------------------------- 1 | The OpenGL Extension Wrangler Library 2 | Copyright (C) 2002-2007, Milan Ikits 3 | Copyright (C) 2002-2007, Marcelo E. Magallon 4 | Copyright (C) 2002, Lev Povalahev 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright notice, 11 | this list of conditions and the following disclaimer. 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | * The name of the author may be used to endorse or promote products 16 | derived from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 28 | THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | 31 | Mesa 3-D graphics library 32 | Version: 7.0 33 | 34 | Copyright (C) 1999-2007 Brian Paul All Rights Reserved. 35 | 36 | Permission is hereby granted, free of charge, to any person obtaining a 37 | copy of this software and associated documentation files (the "Software"), 38 | to deal in the Software without restriction, including without limitation 39 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 40 | and/or sell copies of the Software, and to permit persons to whom the 41 | Software is furnished to do so, subject to the following conditions: 42 | 43 | The above copyright notice and this permission notice shall be included 44 | in all copies or substantial portions of the Software. 45 | 46 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 47 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 48 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 49 | BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 50 | AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 51 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 52 | 53 | 54 | Copyright (c) 2007 The Khronos Group Inc. 55 | 56 | Permission is hereby granted, free of charge, to any person obtaining a 57 | copy of this software and/or associated documentation files (the 58 | "Materials"), to deal in the Materials without restriction, including 59 | without limitation the rights to use, copy, modify, merge, publish, 60 | distribute, sublicense, and/or sell copies of the Materials, and to 61 | permit persons to whom the Materials are furnished to do so, subject to 62 | the following conditions: 63 | 64 | The above copyright notice and this permission notice shall be included 65 | in all copies or substantial portions of the Materials. 66 | 67 | THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 68 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 69 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 70 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 71 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 72 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 73 | MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. 74 | -------------------------------------------------------------------------------- /subtitans/movieheapcorruptionpatch.cpp: -------------------------------------------------------------------------------- 1 | #include "subtitans.h" 2 | #include "movieheapcorruptionpatch.h" 3 | 4 | /* 5 | Issue: Trying to free (CoTaskMemFree) a static structure, which hasn't been created with a CoTaskMemAlloc call. 6 | Solution: The structure should be created with CoTaskMemAlloc 7 | */ 8 | 9 | namespace MovieHeapCorruption { 10 | // Function specific variables 11 | static unsigned long CurrentAllocatedMemoryAddress = 0; 12 | static void* NewAllocatedMemoryAddress = 0; 13 | 14 | void RecalculateStructPointer(unsigned long address) 15 | { 16 | unsigned long* addressPointer = (unsigned long*)address; 17 | unsigned long addition = 0; 18 | 19 | memcpy(&addition, addressPointer, sizeof(int)); 20 | addition -= CurrentAllocatedMemoryAddress; // Get relative position in structure 21 | 22 | unsigned long newAddress = (unsigned long)NewAllocatedMemoryAddress; 23 | newAddress += addition; 24 | 25 | if (!MemoryWriter::Write(address, (unsigned char*)&newAddress, sizeof(int), false)) 26 | { 27 | GetLogger()->Critical("Failed to update an offset for the movie player! (Address 0x%08x)\n", address); 28 | MessageBox(NULL, L"Unexpected failure while patching the movie player", L"SubTitans", MB_ICONERROR); 29 | ExitProcess(-1); 30 | } 31 | } 32 | 33 | // Functions specific variables 34 | static unsigned long StructurePatches[26]; 35 | void __stdcall AllocateMemoryAndRedirectReferences() 36 | { 37 | NewAllocatedMemoryAddress = CoTaskMemAlloc(0x11A * 4); 38 | memset(NewAllocatedMemoryAddress, 0, 0x11A * 4); 39 | 40 | for (int i = 0; i < sizeof(StructurePatches) / sizeof(unsigned long); ++i) 41 | { 42 | RecalculateStructPointer(StructurePatches[i]); 43 | } 44 | 45 | CurrentAllocatedMemoryAddress = (unsigned long)NewAllocatedMemoryAddress; 46 | } 47 | 48 | // Detour variables 49 | constexpr unsigned long DetourSize = 5; 50 | static unsigned long JmpFromAddress = 0; 51 | static unsigned long JmpBackAddress = 0; 52 | __declspec(naked) void Implementation() 53 | { 54 | __asm pushad; 55 | __asm pushfd; 56 | 57 | __asm call [AllocateMemoryAndRedirectReferences]; 58 | 59 | __asm popfd; 60 | __asm popad; 61 | 62 | __asm mov ecx, [NewAllocatedMemoryAddress]; 63 | __asm mov dword ptr ss:[ebp - 0x10], ecx; 64 | 65 | // Restore code 66 | __asm mov ecx, 0x12; 67 | 68 | __asm jmp [JmpBackAddress]; 69 | } 70 | } 71 | 72 | MovieHeapCorruptionPatch::MovieHeapCorruptionPatch() 73 | { 74 | GetLogger()->Informational("Constructing %s\n", __func__); 75 | 76 | AllocatedMemoryOffset = 0; 77 | StructurePointer = 0; 78 | memset(StructureOffsets, 0, sizeof(StructureOffsets)); 79 | DetourAddress = 0; 80 | } 81 | 82 | MovieHeapCorruptionPatch::~MovieHeapCorruptionPatch() 83 | { 84 | GetLogger()->Informational("Destructing %s\n", __func__); 85 | } 86 | 87 | bool MovieHeapCorruptionPatch::Validate() 88 | { 89 | // Sanity check 90 | if (sizeof(StructureOffsets) != sizeof(MovieHeapCorruption::StructurePatches)) 91 | return false; 92 | 93 | for (int i = 0; i < sizeof(StructureOffsets) / sizeof(unsigned long); ++i) 94 | { 95 | if (StructureOffsets[i] == 0) 96 | return false; 97 | } 98 | 99 | return AllocatedMemoryOffset != 0 && StructurePointer != 0 && DetourAddress != 0; 100 | } 101 | 102 | bool MovieHeapCorruptionPatch::Apply() 103 | { 104 | GetLogger()->Informational("%s\n", __FUNCTION__); 105 | 106 | unsigned char zeroArray[] = { 0x00, 0x00, 0x00, 0x00 }; 107 | if (!MemoryWriter::Write(StructurePointer, zeroArray, sizeof(zeroArray))) 108 | return false; 109 | 110 | MovieHeapCorruption::CurrentAllocatedMemoryAddress = AllocatedMemoryOffset; 111 | 112 | // Skip address at first position 113 | memcpy(MovieHeapCorruption::StructurePatches, StructureOffsets, sizeof(MovieHeapCorruption::StructurePatches)); 114 | 115 | MovieHeapCorruption::JmpFromAddress = DetourAddress; 116 | MovieHeapCorruption::JmpBackAddress = MovieHeapCorruption::JmpFromAddress + MovieHeapCorruption::DetourSize; 117 | if (!Detour::Create(MovieHeapCorruption::JmpFromAddress, MovieHeapCorruption::DetourSize, (unsigned long)MovieHeapCorruption::Implementation)) 118 | return false; 119 | 120 | return true; 121 | } -------------------------------------------------------------------------------- /subtitans/surface.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "iddraw4.h" 3 | #include "clipper.h" 4 | #include "palette.h" 5 | 6 | class Surface : public DDraw::IDDrawSurface4 { 7 | public: 8 | Surface(DDraw::SurfaceDescription* description); 9 | virtual ~Surface(); 10 | 11 | // IUnknown 12 | virtual uint32_t __stdcall QueryInterface(GUID* guid, void** result) override; 13 | virtual uint32_t __stdcall AddRef() override; 14 | virtual uint32_t __stdcall Release() override; 15 | 16 | // Direct Draw 17 | virtual uint32_t __stdcall AddAttachedSurface(void*) override; 18 | virtual uint32_t __stdcall AddOverlayDirtyRect(void*) override; 19 | virtual uint32_t __stdcall Blt(RECT* sourceRect, IDDrawSurface4* sourceSurface, RECT* destinationRect, uint32_t flags, void* unknown2) override; 20 | virtual uint32_t __stdcall BltBatch(void*, uint32_t, uint32_t) override; 21 | virtual uint32_t __stdcall BltFast(uint32_t, uint32_t, void*, void*, uint32_t) override; 22 | virtual uint32_t __stdcall DeleteAttachedSurface(uint32_t, void*) override; 23 | virtual uint32_t __stdcall EnumAttachedSurfaces(void*, void*) override; 24 | virtual uint32_t __stdcall EnumOverlayZOrders(uint32_t, void*, void*) override; 25 | virtual uint32_t __stdcall Flip(void*, uint32_t) override; 26 | virtual uint32_t __stdcall GetAttachedSurface(void*, void*) override; 27 | virtual uint32_t __stdcall GetBltStatus(uint32_t) override; 28 | virtual uint32_t __stdcall GetCaps(DDraw::SurfaceCaps* surfaceCaps) override; 29 | virtual uint32_t __stdcall GetClipper(void*) override; 30 | virtual uint32_t __stdcall GetColorKey(uint32_t, void*) override; 31 | virtual uint32_t __stdcall GetDeviceContext(HDC*) override; 32 | virtual uint32_t __stdcall GetFlipStatus(uint32_t) override; 33 | virtual uint32_t __stdcall GetOverlayPosition(void*, void*) override; 34 | virtual uint32_t __stdcall GetPallete(void*) override; 35 | virtual uint32_t __stdcall GetPixelFormat(DDraw::PixelFormat* result) override; 36 | virtual uint32_t __stdcall GetSurfaceDesc(DDraw::SurfaceDescription* desc) override; 37 | virtual uint32_t __stdcall Initialize(void*, void*) override; 38 | virtual uint32_t __stdcall IsLost() override; 39 | virtual uint32_t __stdcall Lock(RECT* rect, DDraw::SurfaceDescription* desc, uint32_t flags, void* unused) override; 40 | virtual uint32_t __stdcall ReleaseDeviceContext(HDC dc) override; 41 | virtual uint32_t __stdcall Restore() override; 42 | virtual uint32_t __stdcall SetClipper(DDraw::IDDrawClipper* clipper) override; 43 | virtual uint32_t __stdcall SetColorKey(uint32_t, void*) override; 44 | virtual uint32_t __stdcall SetOverlayPosition(uint32_t, uint32_t) override; 45 | virtual uint32_t __stdcall SetPalette(DDraw::IDDrawPalette* palette) override; 46 | virtual uint32_t __stdcall Unlock(RECT* rect) override; 47 | virtual uint32_t __stdcall UpdateOverlay(void*, void*, void*, uint32_t, void*) override; 48 | virtual uint32_t __stdcall UpdateOverlayDisplay(uint32_t) override; 49 | virtual uint32_t __stdcall UpdateOverlayZOrder(uint32_t, void*) override; 50 | virtual uint32_t __stdcall GetDDInterface(void*) override; 51 | virtual uint32_t __stdcall PageLock(uint32_t) override; 52 | virtual uint32_t __stdcall PageUnlock(uint32_t) override; 53 | virtual uint32_t __stdcall SetSurfaceDesc(void*, uint32_t) override; 54 | virtual uint32_t __stdcall SetPrivateData(void*, void*, uint32_t, uint32_t) override; 55 | virtual uint32_t __stdcall GetPrivateData(void*, void*, void*) override; 56 | virtual uint32_t __stdcall FreePrivateData(void*) override; 57 | virtual uint32_t __stdcall GetUniquenessValue(void*) override; 58 | virtual uint32_t __stdcall ChangeUniquenessValue() override; 59 | 60 | // Custom 61 | private: 62 | uint32_t ReferenceCount; 63 | uint32_t Identifier; 64 | 65 | public: 66 | int32_t Width; // Read only 67 | int32_t Height; // Read only 68 | int32_t Stride; // Read only 69 | uint8_t* SurfaceBuffer; 70 | 71 | std::mutex PrimaryDrawMutex; 72 | std::atomic_bool PrimaryInvalid; 73 | 74 | Palette* AttachedPalette; 75 | 76 | private: 77 | DDraw::SurfaceDescription Description; 78 | Clipper* AttachedClipper; 79 | 80 | bool IsPrimary() { return (Description.flags & DDraw::SurfaceDescriptionFlag::Caps) && (Description.caps.caps[0] & DDraw::SurfaceCapsFlag::Primary); } 81 | std::pair MemoryDeviceContext; 82 | }; -------------------------------------------------------------------------------- /d3drm/d3drm.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | 16.0 15 | {229FEA89-DB14-4AD2-BD12-878FE36496F0} 16 | d3drm 17 | 10.0 18 | 19 | 20 | 21 | DynamicLibrary 22 | true 23 | v142 24 | Unicode 25 | 26 | 27 | DynamicLibrary 28 | false 29 | v142 30 | true 31 | Unicode 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | true 47 | 48 | 49 | false 50 | 51 | 52 | 53 | Level3 54 | true 55 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 56 | true 57 | 58 | 59 | Console 60 | true 61 | 62 | 63 | 64 | 65 | Level3 66 | true 67 | true 68 | true 69 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 70 | true 71 | 72 | 73 | Console 74 | true 75 | true 76 | false 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /subtitans/softwarerenderer.cpp: -------------------------------------------------------------------------------- 1 | #include "subtitans.h" 2 | #include "surface.h" 3 | #include "softwarerenderer.h" 4 | 5 | SoftwareRenderer::SoftwareRenderer() 6 | { 7 | } 8 | 9 | SoftwareRenderer::~SoftwareRenderer() 10 | { 11 | } 12 | 13 | bool SoftwareRenderer::Create() 14 | { 15 | if (!Global::GameWindow) 16 | { 17 | GetLogger()->Error("%s %s\n", __FUNCTION__, "GameWindow hasn't been set"); 18 | return false; 19 | } 20 | 21 | if (RenderThread) 22 | { 23 | GetLogger()->Warning("%s %s\n", __FUNCTION__, "already created"); 24 | return false; 25 | } 26 | 27 | IsThreadRunning = true; 28 | RenderThread = new std::thread(&SoftwareRenderer::Run, this); 29 | 30 | return true; 31 | } 32 | 33 | void SoftwareRenderer::OnCreatePrimarySurface(Surface* primarySurface) 34 | { 35 | Mutex.lock(); 36 | Global::RenderInformation oldInformation; 37 | memcpy(&oldInformation, &RenderInformation, sizeof(Global::RenderInformation)); 38 | RenderInformation = Global::GetRenderInformation(); 39 | 40 | if (memcmp(&oldInformation, &RenderInformation, sizeof(Global::RenderInformation)) != 0) 41 | RecalculateSurface = true; 42 | 43 | PrimarySurface = primarySurface; 44 | Mutex.unlock(); 45 | } 46 | 47 | void SoftwareRenderer::OnDestroyPrimarySurface() 48 | { 49 | Mutex.lock(); 50 | PrimarySurface = nullptr; 51 | Mutex.unlock(); 52 | } 53 | 54 | void SoftwareRenderer::Run() 55 | { 56 | BITMAPINFO* primaryBitmapInfo = (BITMAPINFO*)new char[sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 256]; 57 | uint8_t* surfaceBuffer = nullptr; 58 | 59 | Mutex.lock(); 60 | while (IsThreadRunning) 61 | { 62 | Mutex.unlock(); 63 | 64 | // 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 65 | WaitForSingleObject(Global::RenderEvent, 40); 66 | 67 | Mutex.lock(); 68 | 69 | // Not initialized 70 | if (PrimarySurface == nullptr) 71 | continue; 72 | 73 | // Stop rendering if the game isn't being focussed on 74 | if (GetForegroundWindow() != Global::GameWindow) 75 | continue; 76 | 77 | // Prevent flickering caused by palette changes 78 | if (PrimarySurface->PrimaryInvalid) 79 | continue; 80 | 81 | memset(primaryBitmapInfo, 0, sizeof(BITMAPINFOHEADER)); 82 | primaryBitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 83 | primaryBitmapInfo->bmiHeader.biWidth = PrimarySurface->Stride; // Read only value 84 | primaryBitmapInfo->bmiHeader.biHeight = -PrimarySurface->Height; // Read only value 85 | primaryBitmapInfo->bmiHeader.biPlanes = 1; 86 | primaryBitmapInfo->bmiHeader.biCompression = BI_RGB; 87 | primaryBitmapInfo->bmiHeader.biBitCount = 8; 88 | primaryBitmapInfo->bmiHeader.biClrUsed = 256; 89 | primaryBitmapInfo->bmiHeader.biSizeImage = 0; 90 | 91 | PrimarySurface->PrimaryDrawMutex.lock(); 92 | 93 | if (PrimarySurface->AttachedPalette) 94 | { 95 | PrimarySurface->AttachedPalette->Mutex.lock(); 96 | memcpy(primaryBitmapInfo->bmiColors, PrimarySurface->AttachedPalette->RawPalette, sizeof(PrimarySurface->AttachedPalette->RawPalette)); 97 | PrimarySurface->AttachedPalette->Mutex.unlock(); 98 | } 99 | 100 | if (!surfaceBuffer || RecalculateSurface) 101 | { 102 | if (surfaceBuffer) 103 | delete[] surfaceBuffer; 104 | 105 | surfaceBuffer = new uint8_t[PrimarySurface->Stride * PrimarySurface->Height]; 106 | RecalculateSurface = false; 107 | } 108 | 109 | memcpy(surfaceBuffer, PrimarySurface->SurfaceBuffer, PrimarySurface->Stride * PrimarySurface->Height); 110 | 111 | PrimarySurface->PrimaryDrawMutex.unlock(); 112 | 113 | auto dc = GetDC(nullptr); 114 | 115 | if (RenderInformation.InternalWidth == RenderInformation.MonitorWidth && RenderInformation.InternalHeight == RenderInformation.MonitorHeight) 116 | SetDIBitsToDevice(dc, 0, 0, PrimarySurface->Width, PrimarySurface->Height, 0, 0, 0, PrimarySurface->Height, surfaceBuffer, primaryBitmapInfo, DIB_RGB_COLORS); 117 | else 118 | StretchDIBits(dc, RenderInformation.Padding, 0, RenderInformation.AspectRatioCompensatedWidth, RenderInformation.MonitorHeight, 0, 0, PrimarySurface->Width, PrimarySurface->Height, surfaceBuffer, primaryBitmapInfo, DIB_RGB_COLORS, SRCCOPY); 119 | 120 | ReleaseDC(nullptr, dc); 121 | 122 | SetEvent(Global::VerticalBlankEvent); 123 | } 124 | Mutex.unlock(); 125 | 126 | if(surfaceBuffer) 127 | delete[] surfaceBuffer; 128 | 129 | delete[] (char*)primaryBitmapInfo; 130 | } -------------------------------------------------------------------------------- /subtitans/iddraw4.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "iddrawsurface4.h" 3 | #include "iddrawclipper.h" 4 | #include "iddrawpalette.h" 5 | 6 | namespace DDraw { 7 | namespace PixelFormatFlag { 8 | constexpr uint32_t PalettedIndexed8 = 0x00000020; 9 | constexpr uint32_t RGB = 0x00000040; 10 | } 11 | 12 | typedef uint32_t(__stdcall* EnumDisplayModesCallBack)(void*, void*); 13 | 14 | #pragma pack(push, 1) 15 | struct _ExternalDeviceCaps { 16 | uint32_t caps; 17 | uint32_t colorKeycaps; 18 | uint32_t effectCaps; 19 | uint32_t rops[8]; 20 | }; 21 | 22 | struct _ExternalDeviceCapsExt { 23 | uint32_t caps; 24 | _ExternalDeviceCaps base; 25 | }; 26 | 27 | struct Caps { // 380 28 | uint32_t size; 29 | uint32_t caps[2]; 30 | uint32_t colorKeyCaps; 31 | uint32_t effectCaps; 32 | uint32_t effectAlphaCaps; 33 | uint32_t palleteCaps; 34 | uint32_t stereoVisionCaps; 35 | uint32_t alphaBltConstantBitDepth; 36 | uint32_t alphaBltPixelBitDepth; 37 | uint32_t alphaBltSurfaceBitDepth; 38 | uint32_t alphaOverlayConstantBitDepth; 39 | uint32_t alphaOverlayPixelBitDepth; 40 | uint32_t alphaOverlaySurfaceBitDepth; 41 | uint32_t zBufferBitDepth; 42 | uint32_t memoryTotal; 43 | uint32_t memoryFree; 44 | uint32_t maximumVisibleOverlays; 45 | uint32_t currentVisibleOverlays; 46 | uint32_t fourCCCodesCount; 47 | uint32_t alignBoundarySource; 48 | uint32_t alignSizeSource; 49 | uint32_t alignBoundaryDestination; 50 | uint32_t alignSizeDestination; 51 | uint32_t alignStride; 52 | uint32_t rops[8]; 53 | uint32_t legacyCaps; 54 | uint32_t minimumOverlayStretch; 55 | uint32_t maximumOverlayStretch; 56 | uint32_t minimumLiveVideoStretch; 57 | uint32_t maximumLiveVideoStretch; 58 | uint32_t minimumHardwareCodecStretch; 59 | uint32_t maximumHardwareCodecStretch; 60 | uint32_t reserved[3]; 61 | _ExternalDeviceCaps systemToVirtualMemoryCaps; 62 | _ExternalDeviceCaps virtualMemoryToSystemCaps; 63 | _ExternalDeviceCaps systemToSystemCaps; 64 | uint32_t maximumVideoPorts; 65 | uint32_t currentVideoPorts; 66 | uint32_t systemToVirtualMemoryCaps2; 67 | _ExternalDeviceCapsExt nonLocalToLocalCaps; 68 | SurfaceCaps surfaceCaps; 69 | }; 70 | #pragma pack(pop) 71 | 72 | class IDDraw4 { 73 | public: 74 | // IUnknown 75 | virtual uint32_t __stdcall QueryInterface(GUID* guid, void** result) = 0; 76 | virtual uint32_t __stdcall AddRef() = 0; 77 | virtual uint32_t __stdcall Release() = 0; 78 | 79 | // Direct Draw 80 | virtual uint32_t __stdcall Compact() = 0; 81 | virtual uint32_t __stdcall CreateClipper(uint32_t, IDDrawClipper** result, void*) = 0; 82 | virtual uint32_t __stdcall CreatePalette(uint32_t flags, void* palette, IDDrawPalette** result, void* unused) = 0; 83 | virtual uint32_t __stdcall CreateSurface(SurfaceDescription* surfaceDescription, IDDrawSurface4** result, void* unused) = 0; 84 | virtual uint32_t __stdcall DuplicateSurface(void*, void*) = 0; 85 | virtual uint32_t __stdcall EnumDisplayModes(uint32_t flags, void* surfaceDescription, void* appDef, EnumDisplayModesCallBack callback) = 0; 86 | virtual uint32_t __stdcall EnumSurfaces(uint32_t, void*, void*, void*) = 0; 87 | virtual uint32_t __stdcall FlipToGDISurface() = 0; 88 | virtual uint32_t __stdcall GetCaps(Caps* caps, Caps* helCaps) = 0; 89 | virtual uint32_t __stdcall GetDisplayMode(void*) = 0; 90 | virtual uint32_t __stdcall GetFourCCCodes(void*, void*) = 0; 91 | virtual uint32_t __stdcall GetGDISurface(void*) = 0; 92 | virtual uint32_t __stdcall GetMonitoryFrequency(void*) = 0; 93 | virtual uint32_t __stdcall GetScanLine(void*) = 0; 94 | virtual uint32_t __stdcall GetVerticalBlankStatus(void*) = 0; 95 | virtual uint32_t __stdcall Initialize(void*) = 0; 96 | virtual uint32_t __stdcall RestoreDisplayMode() = 0; 97 | virtual uint32_t __stdcall SetCooperativeLevel(HWND hwnd, uint32_t flags) = 0; 98 | virtual uint32_t __stdcall SetDisplayMode(uint32_t width, uint32_t height, uint32_t bitsPerPixel, uint32_t refreshRate, uint32_t flags) = 0; 99 | virtual uint32_t __stdcall WaitForVerticalBlank(uint32_t, void*) = 0; 100 | virtual uint32_t __stdcall GetAvailableVidMem(void*, void*, void*) = 0; 101 | virtual uint32_t __stdcall GetSurfaceFromDC(void*, void*) = 0; 102 | virtual uint32_t __stdcall RestoreAllSurfaces() = 0; 103 | virtual uint32_t __stdcall TestCooperativeLevel() = 0; 104 | virtual uint32_t __stdcall GetDeviceIdentifier(void*, uint32_t) = 0; 105 | }; 106 | } -------------------------------------------------------------------------------- /subtitans/palette.cpp: -------------------------------------------------------------------------------- 1 | #include "subtitans.h" 2 | #include "palette.h" 3 | 4 | using namespace DDraw; 5 | 6 | Palette::Palette() 7 | { 8 | GetLogger()->Trace("%s\n", __FUNCTION__); 9 | 10 | ReferenceCount = 0; 11 | 12 | memset(RawPalette, 0, sizeof(RawPalette)); 13 | } 14 | 15 | Palette::~Palette() { GetLogger()->Trace("%s\n", __FUNCTION__); } 16 | 17 | // IUnknown 18 | uint32_t __stdcall Palette::QueryInterface(GUID* guid, void** result) 19 | { 20 | GetLogger()->Trace("%s\n", __FUNCTION__); 21 | 22 | GetLogger()->Error("%s %s\n", __FUNCTION__, "unknown interface"); 23 | *result = nullptr; 24 | return ResultCode::NoInterface; 25 | } 26 | uint32_t __stdcall Palette::AddRef() 27 | { 28 | GetLogger()->Trace("%s\n", __FUNCTION__); 29 | 30 | ReferenceCount++; 31 | 32 | return ResultCode::Ok; 33 | } 34 | 35 | uint32_t __stdcall Palette::Release() 36 | { 37 | GetLogger()->Trace("%s (Remaining references %i)\n", __FUNCTION__, ReferenceCount); 38 | 39 | if(--ReferenceCount == 0) 40 | delete this; 41 | 42 | return ResultCode::Ok; 43 | } 44 | 45 | // Palette 46 | uint32_t __stdcall Palette::GetCaps(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 47 | 48 | uint32_t __stdcall Palette::GetEntries(uint32_t shouldBeZero, uint32_t index, uint32_t count, uint8_t* result) 49 | { 50 | GetLogger()->Trace("%s\n", __FUNCTION__); 51 | 52 | if (shouldBeZero != 0) 53 | { 54 | GetLogger()->Error("%s %s %i != 0\n", __FUNCTION__, "shouldBeZero", shouldBeZero); 55 | return ResultCode::InvalidArgument; 56 | } 57 | 58 | Mutex.lock(); 59 | 60 | // BGR -> RGB 61 | for (uint32_t i = 0; i < count * 4; i += 4) 62 | { 63 | result[i] = RawPalette[index * 4 + i + 2]; 64 | result[i + 1] = RawPalette[index * 4 + i + 1]; 65 | result[i + 2] = RawPalette[index * 4 + i]; 66 | result[i + 3] = RawPalette[index * 4 + i + 3]; 67 | } 68 | 69 | Mutex.unlock(); 70 | 71 | return ResultCode::Ok; 72 | } 73 | 74 | uint32_t __stdcall Palette::Initialize(void*, uint32_t, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 75 | 76 | uint32_t __stdcall Palette::SetEntries(uint32_t shouldBeZero, uint32_t index, uint32_t count, uint8_t* input) 77 | { 78 | GetLogger()->Trace("%s\n", __FUNCTION__); 79 | 80 | if (shouldBeZero != 0) 81 | { 82 | GetLogger()->Error("%s %s %i != 0\n", __FUNCTION__, "shouldBeZero", shouldBeZero); 83 | return ResultCode::InvalidArgument; 84 | } 85 | 86 | Mutex.lock(); 87 | 88 | // RGB -> BGR 89 | for (uint32_t i = 0; i < count * 4; i += 4) 90 | { 91 | RawPalette[index * 4 + i] = input[i + 2]; 92 | RawPalette[index * 4 + i + 1] = input[i + 1]; 93 | RawPalette[index * 4 + i + 2] = input[i]; 94 | RawPalette[index * 4 + i + 3] = input[i + 3]; 95 | } 96 | 97 | Mutex.unlock(); 98 | 99 | return ResultCode::Ok; 100 | } 101 | 102 | bool Palette::CreatePallete(uint32_t flags, uint8_t* palette) 103 | { 104 | GetLogger()->Trace("%s\n", __FUNCTION__); 105 | 106 | // Not implemented 107 | if (flags & PaletteCapabilityFlags::Bits_1) { GetLogger()->Error("%s %s\n", __FUNCTION__, "Bits_1"); return false; } 108 | if (flags & PaletteCapabilityFlags::Bits_2) { GetLogger()->Error("%s %s\n", __FUNCTION__, "Bits_2"); return false; } 109 | if (flags & PaletteCapabilityFlags::Bits_4) { GetLogger()->Error("%s %s\n", __FUNCTION__, "Bits_4"); return false; } 110 | //if (flags & PaletteCapabilityFlags::Bits_8) { GetLogger()->Debug("%s %s\n", __FUNCTION__, "Bits_8"); return false; } 111 | if (flags & PaletteCapabilityFlags::Use8BitIndex) { GetLogger()->Error("%s %s\n", __FUNCTION__, "Use8BitIndex"); return false; } 112 | if (flags & PaletteCapabilityFlags::PrimarySurface) { GetLogger()->Error("%s %s\n", __FUNCTION__, "PrimarySurface"); return false; } 113 | if (flags & PaletteCapabilityFlags::PrimarySurfaceLeft) { GetLogger()->Error("%s %s\n", __FUNCTION__, "PrimarySurfaceLeft"); return false; } 114 | if (flags & PaletteCapabilityFlags::AllowFullPalette) { GetLogger()->Error("%s %s\n", __FUNCTION__, "AllowFullPalette"); return false; } 115 | if (flags & PaletteCapabilityFlags::VSync) { GetLogger()->Error("%s %s\n", __FUNCTION__, "VSync"); return false; } 116 | if (flags & PaletteCapabilityFlags::UseAlpha) { GetLogger()->Error("%s %s\n", __FUNCTION__, "UseAlpha"); return false; } 117 | 118 | Mutex.lock(); 119 | 120 | // RGB -> BGR 121 | for (int i = 0; i < 1024; i += 4) 122 | { 123 | RawPalette[i] = palette[i+2]; 124 | RawPalette[i+1] = palette[i+1]; 125 | RawPalette[i+2] = palette[i]; 126 | RawPalette[i+3] = palette[i+3]; 127 | } 128 | 129 | Mutex.unlock(); 130 | 131 | return true; 132 | } -------------------------------------------------------------------------------- /shared/shared.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 16.0 30 | {5F2C7921-4B31-4712-8722-C99EFF304EC4} 31 | shared 32 | 10.0 33 | 34 | 35 | 36 | StaticLibrary 37 | true 38 | v142 39 | Unicode 40 | 41 | 42 | StaticLibrary 43 | false 44 | v142 45 | true 46 | Unicode 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | false 62 | 63 | 64 | true 65 | 66 | 67 | 68 | Level3 69 | true 70 | true 71 | true 72 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 73 | true 74 | 75 | 76 | Console 77 | true 78 | true 79 | true 80 | 81 | 82 | 83 | 84 | Level3 85 | true 86 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 87 | true 88 | 89 | 90 | Console 91 | true 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /d3drm/injector.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../shared/gameversion.h" 3 | #include "injector.h" 4 | 5 | namespace Injector { 6 | static unsigned long GameVersion = 0; 7 | 8 | static HMODULE SubTitansLibrary = nullptr; 9 | typedef void(__stdcall* SubTitansLibrary_InitializeLibrary)(unsigned long); 10 | typedef void(__stdcall* SubTitansLibrary_ReleaseLibrary)(); 11 | 12 | void __stdcall LoadModule() 13 | { 14 | SubTitansLibrary = LoadLibrary(L"subtitans.dll"); 15 | if (!SubTitansLibrary) 16 | { 17 | MessageBox(NULL, L"Failed to load subtitans.dll!", L"D3DRM (Custom)", MB_ICONERROR); 18 | ExitProcess(-1); 19 | } 20 | 21 | SubTitansLibrary_InitializeLibrary initializeLibrary = (SubTitansLibrary_InitializeLibrary)GetProcAddress(SubTitansLibrary, "InitializeLibrary"); 22 | if (!initializeLibrary) 23 | { 24 | MessageBox(NULL, L"Failed to retrieve InitializeLibrary from subtitans.dll!", L"D3DRM (Custom)", MB_ICONERROR); 25 | ExitProcess(-1); 26 | } 27 | 28 | initializeLibrary(GameVersion); 29 | } 30 | 31 | constexpr unsigned long LoadModule_DetourSize = 5; 32 | static unsigned long LoadModule_JmpFrom = 0; 33 | static unsigned long LoadModule_JmpBack = 0; 34 | static unsigned long StartApplicationAddress = 0; 35 | __declspec(naked) void LoadModule_Detour() 36 | { 37 | __asm pushad; 38 | __asm pushfd; 39 | 40 | __asm call [LoadModule]; 41 | 42 | __asm popfd; 43 | __asm popad; 44 | 45 | __asm call [StartApplicationAddress]; 46 | 47 | __asm jmp [LoadModule_JmpBack]; 48 | } 49 | 50 | void __stdcall UnloadModule() 51 | { 52 | if (!SubTitansLibrary) 53 | { 54 | MessageBox(NULL, L"Failed to unload subtitans.dll!", L"D3DRM (Custom)", MB_ICONERROR); 55 | ExitProcess(-1); 56 | } 57 | 58 | SubTitansLibrary_ReleaseLibrary releaseLibrary = (SubTitansLibrary_ReleaseLibrary)GetProcAddress(SubTitansLibrary, "ReleaseLibrary"); 59 | if (!releaseLibrary) 60 | { 61 | MessageBox(NULL, L"Failed to retrieve ReleaseLibrary from subtitans.dll!", L"D3DRM (Custom)", MB_ICONERROR); 62 | ExitProcess(-1); 63 | } 64 | 65 | releaseLibrary(); 66 | FreeLibrary(SubTitansLibrary); 67 | } 68 | 69 | constexpr unsigned long UnloadModule_DetourSize = 6; 70 | static unsigned long UnloadModule_JmpFrom = 0; 71 | static unsigned long UnloadModule_JmpBack = 0; 72 | __declspec(naked) void UnloadModule_Detour() 73 | { 74 | __asm pushad; 75 | __asm pushfd; 76 | 77 | __asm call [UnloadModule]; 78 | 79 | __asm popfd; 80 | __asm popad; 81 | 82 | __asm mov dword ptr ss:[ebp - 0x60], eax; 83 | __asm mov eax, dword ptr ss:[ebp - 0x60]; 84 | 85 | __asm jmp [UnloadModule_JmpBack]; 86 | } 87 | 88 | // Only Kernel32 dependent 89 | bool ApplyDetour(unsigned long origin, unsigned long length, unsigned long destination) 90 | { 91 | HANDLE currentProcess = GetCurrentProcess(); 92 | unsigned long* originPointer = (unsigned long*)origin; 93 | 94 | unsigned long previousProtection = 0; 95 | if (VirtualProtectEx(currentProcess, originPointer, length, PAGE_EXECUTE_READWRITE, &previousProtection) == FALSE) 96 | return false; 97 | 98 | // Hard length limit of 6... 99 | unsigned char bytesToCopy[] = { 0xE9, 0x90, 0x90, 0x90, 0x90, 0x90 }; 100 | if (sizeof(bytesToCopy) < length) 101 | return false; 102 | 103 | unsigned long jumpDistance = destination - origin - 5; 104 | 105 | for (int i = 0; i < 4; ++i) 106 | { 107 | unsigned char* destByte = (unsigned char*)&jumpDistance; 108 | bytesToCopy[i + 1] = *(destByte + i); 109 | } 110 | 111 | SIZE_T writtenBytes = 0; 112 | if (WriteProcessMemory(currentProcess, originPointer, bytesToCopy, length, &writtenBytes) == FALSE) 113 | return false; 114 | 115 | FlushInstructionCache(currentProcess, originPointer, length); 116 | 117 | if (VirtualProtectEx(currentProcess, originPointer, length, previousProtection, &previousProtection) == FALSE) 118 | return false; 119 | 120 | return true; 121 | } 122 | 123 | bool Apply(unsigned long gameVersion) 124 | { 125 | GameVersion = gameVersion; 126 | switch (GameVersion) 127 | { 128 | case Shared::ST_GAMEVERSION_RETAIL_UNPATCHED: 129 | { 130 | LoadModule_JmpFrom = 0x00734B6E; 131 | LoadModule_JmpBack = LoadModule_JmpFrom + LoadModule_DetourSize; 132 | 133 | UnloadModule_JmpFrom = 0x00734B73; 134 | UnloadModule_JmpBack = UnloadModule_JmpFrom + UnloadModule_DetourSize; 135 | 136 | StartApplicationAddress = 0x00401FEB; 137 | } break; 138 | case Shared::ST_GAMEVERSION_RETAIL_PATCHED: 139 | case Shared::ST_GAMEVERSION_GOG_MODIFIED: 140 | { 141 | LoadModule_JmpFrom = 0x007337FE; 142 | LoadModule_JmpBack = LoadModule_JmpFrom + LoadModule_DetourSize; 143 | 144 | UnloadModule_JmpFrom = 0x00733803; 145 | UnloadModule_JmpBack = UnloadModule_JmpFrom + UnloadModule_DetourSize; 146 | 147 | StartApplicationAddress = 0x00401FF5; 148 | } break; 149 | case Shared::ST_GAMEVERSION_DEMO: 150 | { 151 | LoadModule_JmpFrom = 0x0071A25E; 152 | LoadModule_JmpBack = LoadModule_JmpFrom + LoadModule_DetourSize; 153 | 154 | UnloadModule_JmpFrom = 0x0071A263; 155 | UnloadModule_JmpBack = UnloadModule_JmpFrom + UnloadModule_DetourSize; 156 | 157 | StartApplicationAddress = 0x00401FF0; 158 | } break; 159 | default: 160 | return false; 161 | } 162 | 163 | if (!ApplyDetour(LoadModule_JmpFrom, LoadModule_DetourSize, (unsigned long)LoadModule_Detour)) 164 | return false; 165 | 166 | if (!ApplyDetour(UnloadModule_JmpFrom, UnloadModule_DetourSize, (unsigned long)UnloadModule_Detour)) 167 | return false; 168 | 169 | return true; 170 | } 171 | } -------------------------------------------------------------------------------- /subtitans/idinputdevice7.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace DInput { 4 | enum KeyboardKey { 5 | KEY_ESCAPE = 1, // Esc 6 | KEY_1, // 1 7 | KEY_2, // 2 8 | KEY_3, // 3 9 | KEY_4, // 4 10 | KEY_5, // 5 11 | KEY_6, // 6 12 | KEY_7, // 7 13 | KEY_8, // 8 14 | KEY_9, // 9 15 | KEY_0, // 0 16 | KEY_DASH, // - 17 | KEY_EQUAL, // = 18 | KEY_BACKSPACE, // <- 19 | KEY_TAB, // |<- ->| 20 | KEY_Q, // Q 21 | KEY_W, // W 22 | KEY_E, // E 23 | KEY_R, // R 24 | KEY_T, // T 25 | KEY_Y, // Y 26 | KEY_U, // U 27 | KEY_I, // I 28 | KEY_O, // O 29 | KEY_P, // P 30 | KEY_LEFTBRACKET, // [ 31 | KEY_RIGHTBRACKET, // ] 32 | KEY_ENTER, // Enter 33 | KEY_LEFTCTRL, // Left Ctrl (Control) 34 | KEY_A, // A 35 | KEY_S, // S 36 | KEY_D, // D 37 | KEY_F, // F 38 | KEY_G, // G 39 | KEY_H, // H 40 | KEY_J, // J 41 | KEY_K, // K 42 | KEY_L, // L 43 | KEY_SEMICOLON, // ; 44 | KEY_APOSTROPHE, // ' 45 | KEY_TILDE, // ~ 46 | KEY_LEFTSHIFT, // Left Shift 47 | KEY_BACKSLASH, /* \ */ 48 | KEY_Z, // Z 49 | KEY_X, // X 50 | KEY_C, // C 51 | KEY_V, // V 52 | KEY_B, // B 53 | KEY_N, // N 54 | KEY_M, // M 55 | KEY_COMMA, // , 56 | KEY_PERIOD, // . 57 | KEY_FORWARDSLASH, // / 58 | KEY_RIGHTSHIFT, // Right Shift 59 | KEY_MULTIPLTY, // * 60 | KEY_LEFTALT, // Left Alt 61 | KEY_SPACE, // Space 62 | KEY_CAPSLOCK, // Caps Lock 63 | KEY_F1, // F1 64 | KEY_F2, // F2 65 | KEY_F3, // F3 66 | KEY_F4, // F4 67 | KEY_F5, // F5 68 | KEY_F6, // F6 69 | KEY_F7, // F7 70 | KEY_F8, // F8 71 | KEY_F9, // F9 72 | KEY_F10, // F10 73 | KEY_NUMLOCK, // Num Lock 74 | KEY_SCROLLLOCK, // Scroll Lock 75 | KEY_NUMPAD7, // Numpad 7 76 | KEY_NUMPAD8, // Numpad 8 77 | KEY_NUMPAD9, // Numpad 9 78 | KEY_SUBTRACT, // Numpad - 79 | KEY_NUMPAD4, // Numpad 4 80 | KEY_NUMPAD5, // Numpad 5 81 | KEY_NUMPAD6, // Numpad 6 82 | KEY_ADD, // Numpad + 83 | KEY_NUMPAD1, // Numpad 1 84 | KEY_NUMPAD2, // Numpad 2 85 | KEY_NUMPAD3, // Numpad 3 86 | KEY_NUMPAD0, // Numpad 0 87 | KEY_DECIMAL, // Numpad Del 88 | 89 | KEY_F11 = 87, // F11 90 | KEY_F12 = 88, // F12 91 | 92 | KEY_RIGHTCTRL = 157, // Right CTRL 93 | 94 | KEY_DIVIDE = 181, // Numpad / 95 | 96 | // KEY_PRINTSCRN = 183,// Printscreen 97 | KEY_RIGHTALT = 184, // Right alt 98 | 99 | // KEY_PAUSE = 197, // Pause/Break 100 | 101 | KEY_HOME = 199, // Home 102 | KEY_UP = 200, // Up 103 | KEY_PAGEUP = 201, // Page Up 104 | 105 | KEY_LEFT = 203, // Left 106 | 107 | KEY_RIGHT = 205, // Right 108 | 109 | KEY_END = 207, // End 110 | KEY_DOWN = 208, // Down 111 | KEY_PAGEDOWN = 209, // Page Down 112 | KEY_INSERT = 210, // Insert 113 | KEY_DELETE = 211, // Delete 114 | }; 115 | 116 | #pragma pack(push, 1) 117 | struct ObjectDataFormat { 118 | GUID* guid; 119 | uint32_t offset; 120 | uint32_t type; 121 | uint32_t flags; 122 | }; 123 | 124 | struct DataFormat { 125 | uint32_t size; // sizeof(DataFormat) 126 | uint32_t objectSize; // sizeof(ObjectDataFormat); 127 | uint32_t flags; 128 | uint32_t contentSize; // return object 129 | uint32_t objectCount; 130 | ObjectDataFormat* objects; 131 | }; 132 | 133 | struct Caps { 134 | uint32_t size; 135 | uint32_t flags; 136 | uint32_t devType; 137 | uint32_t numberOfAxes; 138 | uint32_t numberOfButtons; 139 | uint32_t pointOfViewControllers; 140 | uint32_t ffSamplePeriod; 141 | uint32_t ffMinimumTimeResolution; 142 | uint32_t firmwareRevision; 143 | uint32_t hardwareRevision; 144 | uint32_t ffDriverRevision; 145 | }; 146 | #pragma pack(pop) 147 | 148 | class IDInputDevice7 { 149 | public: 150 | // IUnknown 151 | virtual uint32_t __stdcall QueryInterface(GUID* guid, void** result) = 0; 152 | virtual uint32_t __stdcall AddRef() = 0; 153 | virtual uint32_t __stdcall Release() = 0; 154 | 155 | // DirectInput7 156 | virtual uint32_t __stdcall GetCapabilities(Caps* caps) = 0; 157 | virtual uint32_t __stdcall EnumObjects(void* callback, void*, uint32_t) = 0; 158 | virtual uint32_t __stdcall GetProperty(GUID& guid, void*) = 0; 159 | virtual uint32_t __stdcall SetProperty(GUID& guid, void*) = 0; 160 | virtual uint32_t __stdcall Acquire() = 0; 161 | virtual uint32_t __stdcall Unacquire() = 0; 162 | virtual uint32_t __stdcall GetDeviceState(uint32_t, void*) = 0; 163 | virtual uint32_t __stdcall GetDeviceData(uint32_t, void*, void*, uint32_t) = 0; 164 | virtual uint32_t __stdcall SetDataFormat(DataFormat*) = 0; 165 | virtual uint32_t __stdcall SetEventNotification(void*) = 0; 166 | virtual uint32_t __stdcall SetCooperativeLevel(void*, uint32_t) = 0; 167 | virtual uint32_t __stdcall GetObjectInfo(void*, uint32_t, uint32_t) = 0; 168 | virtual uint32_t __stdcall GetDeviceInfo(void*) = 0; 169 | virtual uint32_t __stdcall RunControlPanel(void*, uint32_t) = 0; 170 | virtual uint32_t __stdcall Initialize(void*, int32_t, GUID&) = 0; 171 | virtual uint32_t __stdcall CreateEffect(GUID&, void*, void**, void*) = 0; 172 | virtual uint32_t __stdcall EnumEffects(void* callback, void*, uint32_t) = 0; 173 | virtual uint32_t __stdcall GetEffectInfo(void*, GUID&) = 0; 174 | virtual uint32_t __stdcall GetForceFeedbackState(uint32_t*) = 0; 175 | virtual uint32_t __stdcall SendForceFeedbackCommand(uint32_t) = 0; 176 | virtual uint32_t __stdcall EnumCreatedEffectObjects(void* callback, void*, uint32_t) = 0; 177 | virtual uint32_t __stdcall Escape(void*) = 0; 178 | virtual uint32_t __stdcall Poll() = 0; 179 | virtual uint32_t __stdcall SendDeviceData(void*, void* callback, void*, uint32_t) = 0; 180 | virtual uint32_t __stdcall EnumEffectsInFile(void*, void* callback, void*, uint32_t) = 0; 181 | virtual uint32_t __stdcall WriteEffectsToFile(void*, uint32_t, void*, uint32_t) = 0; 182 | }; 183 | } -------------------------------------------------------------------------------- /subtitans/iddrawsurface4.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace DDraw { 4 | class IDDrawClipper; 5 | class IDDrawPalette; 6 | 7 | namespace SurfaceDescriptionFlag { 8 | constexpr uint32_t Caps = 0x00000001; 9 | constexpr uint32_t Height = 0x00000002; 10 | constexpr uint32_t Width = 0x00000004; 11 | constexpr uint32_t Pitch = 0x00000008; 12 | constexpr uint32_t SurfacePointer = 0x00000800; 13 | constexpr uint32_t PixelFormat = 0x00001000; 14 | constexpr uint32_t RefreshRate = 0x00040000; 15 | } 16 | 17 | namespace SurfaceCapsFlag { 18 | constexpr uint32_t Primary = 0x00000200; 19 | } 20 | 21 | #pragma pack(push, 1) 22 | struct SurfaceCaps { 23 | uint32_t caps[3]; 24 | union { 25 | uint32_t capsExt; 26 | uint32_t volumeDepth; 27 | }; 28 | }; 29 | 30 | struct ColorKey { 31 | uint32_t colorSpaceLow; 32 | uint32_t colorSpaceHigh; 33 | }; 34 | 35 | struct PixelFormat { 36 | uint32_t size; 37 | uint32_t flags; 38 | uint32_t fourCC; 39 | union { 40 | uint32_t rgbBitCount; 41 | uint32_t yuvBitCount; 42 | uint32_t zBufferBitDepth; 43 | uint32_t alphaBitDepth; 44 | uint32_t luminanceBitCount; 45 | uint32_t bumpBitCount; 46 | uint32_t privateFormatBitCount; 47 | }; 48 | union { 49 | uint32_t rBitMask; 50 | uint32_t yBitMask; 51 | uint32_t stencilBitDepth; 52 | uint32_t luminanceBitMask; 53 | uint32_t bumpDeltaUBitMask; 54 | uint32_t operations; 55 | }; 56 | union { 57 | uint32_t gBitMask; 58 | uint32_t uBitMask; 59 | uint32_t zBitMask; 60 | uint32_t bumpDeltaVBitMask; 61 | struct { 62 | uint16_t flipMultisampleTypes; 63 | uint16_t bltMultisampleTypes; 64 | }; 65 | }; 66 | union { 67 | uint32_t bBitMask; 68 | uint32_t vBitMask; 69 | uint32_t stencilBitMask; 70 | uint32_t bumpLuminanceBitMask; 71 | }; 72 | union { 73 | uint32_t rgbAlphaBitMask; 74 | uint32_t yuvAlphaBitMask; 75 | uint32_t luminanceAlphaBitMask; 76 | uint32_t rgbzBitMask; 77 | uint32_t yuvzBitMask; 78 | }; 79 | }; 80 | 81 | struct SurfaceDescription { // should be 124 size 82 | uint32_t size; 83 | uint32_t flags; 84 | uint32_t height; 85 | uint32_t width; 86 | union { 87 | int32_t pitch; 88 | uint32_t linearSize; 89 | }; 90 | union { 91 | uint32_t backBufferCount; 92 | uint32_t depth; 93 | }; 94 | union { 95 | uint32_t mipMapCount; 96 | uint32_t refreshRate; 97 | uint32_t srcVBHandle; 98 | }; 99 | uint32_t alphaBitDepth; 100 | uint32_t reserved; 101 | void* surface; 102 | union { 103 | ColorKey colorKeyDestOverlay; 104 | uint32_t emptyFaceColor; 105 | }; 106 | ColorKey colorKeyDestBlt; 107 | ColorKey colorKeySrcOverlay; 108 | ColorKey colorKeySrcBlt; 109 | union { 110 | PixelFormat pixelFormat; 111 | uint32_t fvf; 112 | }; 113 | SurfaceCaps caps; 114 | uint32_t textureStage; 115 | }; 116 | #pragma pack(pop) 117 | 118 | class IDDrawSurface4 { 119 | public: 120 | // IUnknown 121 | virtual uint32_t __stdcall QueryInterface(GUID* guid, void** result) = 0; 122 | virtual uint32_t __stdcall AddRef() = 0; 123 | virtual uint32_t __stdcall Release() = 0; 124 | 125 | // DirectDraw Surface 126 | virtual uint32_t __stdcall AddAttachedSurface(void*) = 0; 127 | virtual uint32_t __stdcall AddOverlayDirtyRect(void*) = 0; 128 | virtual uint32_t __stdcall Blt(RECT* destinationRect, IDDrawSurface4* sourceSurface, RECT* sourceRect, uint32_t flags, void* bltFx) = 0; 129 | virtual uint32_t __stdcall BltBatch(void*, uint32_t, uint32_t) = 0; 130 | virtual uint32_t __stdcall BltFast(uint32_t, uint32_t, void*, void*, uint32_t) = 0; 131 | virtual uint32_t __stdcall DeleteAttachedSurface(uint32_t, void*) = 0; 132 | virtual uint32_t __stdcall EnumAttachedSurfaces(void*, void*) = 0; 133 | virtual uint32_t __stdcall EnumOverlayZOrders(uint32_t, void*, void*) = 0; 134 | virtual uint32_t __stdcall Flip(void*, uint32_t) = 0; 135 | virtual uint32_t __stdcall GetAttachedSurface(void*, void*) = 0; 136 | virtual uint32_t __stdcall GetBltStatus(uint32_t) = 0; 137 | virtual uint32_t __stdcall GetCaps(SurfaceCaps* surfaceCaps) = 0; 138 | virtual uint32_t __stdcall GetClipper(void*) = 0; 139 | virtual uint32_t __stdcall GetColorKey(uint32_t, void*) = 0; 140 | virtual uint32_t __stdcall GetDeviceContext(HDC*) = 0; 141 | virtual uint32_t __stdcall GetFlipStatus(uint32_t) = 0; 142 | virtual uint32_t __stdcall GetOverlayPosition(void*, void*) = 0; 143 | virtual uint32_t __stdcall GetPallete(void*) = 0; 144 | virtual uint32_t __stdcall GetPixelFormat(PixelFormat* result) = 0; 145 | virtual uint32_t __stdcall GetSurfaceDesc(SurfaceDescription* desc) = 0; 146 | virtual uint32_t __stdcall Initialize(void*, void*) = 0; 147 | virtual uint32_t __stdcall IsLost() = 0; 148 | virtual uint32_t __stdcall Lock(RECT* rect, SurfaceDescription* desc, uint32_t flags , void* unused) = 0; 149 | virtual uint32_t __stdcall ReleaseDeviceContext(HDC dc) = 0; 150 | virtual uint32_t __stdcall Restore() = 0; 151 | virtual uint32_t __stdcall SetClipper(IDDrawClipper* clipper) = 0; 152 | virtual uint32_t __stdcall SetColorKey(uint32_t, void*) = 0; 153 | virtual uint32_t __stdcall SetOverlayPosition(uint32_t, uint32_t) = 0; 154 | virtual uint32_t __stdcall SetPalette(IDDrawPalette* palette) = 0; 155 | virtual uint32_t __stdcall Unlock(RECT* rect) = 0; 156 | virtual uint32_t __stdcall UpdateOverlay(void*, void*, void*, uint32_t, void*) = 0; 157 | virtual uint32_t __stdcall UpdateOverlayDisplay(uint32_t) = 0; 158 | virtual uint32_t __stdcall UpdateOverlayZOrder(uint32_t, void*) = 0; 159 | virtual uint32_t __stdcall GetDDInterface(void*) = 0; 160 | virtual uint32_t __stdcall PageLock(uint32_t) = 0; 161 | virtual uint32_t __stdcall PageUnlock(uint32_t) = 0; 162 | virtual uint32_t __stdcall SetSurfaceDesc(void*, uint32_t) = 0; 163 | virtual uint32_t __stdcall SetPrivateData(void*, void*, uint32_t, uint32_t) = 0; 164 | virtual uint32_t __stdcall GetPrivateData(void*, void*, void*) = 0; 165 | virtual uint32_t __stdcall FreePrivateData(void*) = 0; 166 | virtual uint32_t __stdcall GetUniquenessValue(void*) = 0; 167 | virtual uint32_t __stdcall ChangeUniquenessValue() = 0; 168 | }; 169 | } 170 | -------------------------------------------------------------------------------- /subtitans/keyboarddevice.cpp: -------------------------------------------------------------------------------- 1 | #include "subtitans.h" 2 | #include "keyboarddevice.h" 3 | 4 | KeyboardDevice::KeyboardDevice() 5 | { 6 | ReferenceCount = 0; 7 | Initialized = false; 8 | } 9 | 10 | KeyboardDevice::~KeyboardDevice() 11 | { 12 | } 13 | 14 | uint32_t __stdcall KeyboardDevice::QueryInterface(GUID* guid, void** result) 15 | { 16 | unsigned long guid4[] = { 0, 0 }; 17 | memcpy(guid4, guid->Data4, sizeof(guid4)); 18 | GetLogger()->Error("%s %s %08X-%04X-%04X-%08X%08X\n", __FUNCTION__, "unknown interface", guid->Data1, guid->Data2, guid->Data3, guid4[0], guid4[1]); 19 | *result = nullptr; 20 | return ResultCode::NoInterface; 21 | } 22 | 23 | uint32_t __stdcall KeyboardDevice::AddRef() 24 | { 25 | GetLogger()->Trace("%s\n", __FUNCTION__); 26 | 27 | ReferenceCount++; 28 | 29 | return ResultCode::Ok; 30 | } 31 | 32 | uint32_t __stdcall KeyboardDevice::Release() 33 | { 34 | GetLogger()->Trace("%s\n", __FUNCTION__); 35 | 36 | if (--ReferenceCount == 0) 37 | delete this; 38 | 39 | return ResultCode::Ok; 40 | } 41 | 42 | uint32_t __stdcall KeyboardDevice::GetCapabilities(DInput::Caps* caps) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 43 | uint32_t __stdcall KeyboardDevice::EnumObjects(void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 44 | uint32_t __stdcall KeyboardDevice::GetProperty(GUID& guid, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 45 | uint32_t __stdcall KeyboardDevice::SetProperty(GUID& guid, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 46 | 47 | uint32_t __stdcall KeyboardDevice::Acquire() 48 | { 49 | GetLogger()->Trace("%s\n", __FUNCTION__); 50 | return ResultCode::Ok; 51 | } 52 | 53 | uint32_t __stdcall KeyboardDevice::Unacquire() 54 | { 55 | GetLogger()->Trace("%s\n", __FUNCTION__); 56 | return ResultCode::Ok; 57 | } 58 | 59 | uint32_t __stdcall KeyboardDevice::GetDeviceState(uint32_t bufferSize, void* buffer) 60 | { 61 | GetLogger()->Trace("%s\n", __FUNCTION__); 62 | 63 | if (bufferSize != sizeof(Global::_KeyboardInformation)) 64 | { 65 | GetLogger()->Error("%s %s %i != %i\n", __FUNCTION__, "unexpected size", bufferSize, sizeof(Global::_KeyboardInformation)); 66 | return ResultCode::InvalidArgument; 67 | } 68 | 69 | if (Initialized) 70 | memcpy(buffer, &Global::KeyboardInformation, sizeof(Global::_KeyboardInformation)); 71 | else 72 | memset(buffer, 0, sizeof(Global::_KeyboardInformation)); 73 | 74 | return ResultCode::Ok; 75 | } 76 | 77 | uint32_t __stdcall KeyboardDevice::GetDeviceData(uint32_t, void*, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 78 | 79 | uint32_t __stdcall KeyboardDevice::SetDataFormat(DInput::DataFormat* dataFormat) 80 | { 81 | GetLogger()->Trace("%s\n", __FUNCTION__); 82 | Initialized = false; 83 | 84 | if (dataFormat->size != sizeof(DInput::DataFormat)) 85 | { 86 | GetLogger()->Error("%s %s %i != %i\n", __FUNCTION__, "data format has an unexpected size", dataFormat->size, sizeof(DInput::DataFormat)); 87 | return ResultCode::InvalidArgument; 88 | } 89 | 90 | if (dataFormat->objectSize != sizeof(DInput::ObjectDataFormat)) 91 | { 92 | GetLogger()->Error("%s %s %i != %i\n", __FUNCTION__, "object data format has an unexpected size", dataFormat->objectSize, sizeof(DInput::ObjectDataFormat)); 93 | return ResultCode::InvalidArgument; 94 | } 95 | 96 | if (dataFormat->contentSize != sizeof(Global::_KeyboardInformation)) 97 | { 98 | GetLogger()->Error("%s %s %i != %i\n", __FUNCTION__, "unexpected data buffer size", dataFormat->contentSize, sizeof(Global::_KeyboardInformation)); 99 | return ResultCode::InvalidArgument; 100 | } 101 | 102 | Initialized = true; 103 | 104 | return ResultCode::Ok; 105 | } 106 | 107 | uint32_t __stdcall KeyboardDevice::SetEventNotification(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 108 | 109 | uint32_t __stdcall KeyboardDevice::SetCooperativeLevel(void*, uint32_t) 110 | { 111 | GetLogger()->Trace("%s\n", __FUNCTION__); 112 | return ResultCode::Ok; 113 | } 114 | 115 | uint32_t __stdcall KeyboardDevice::GetObjectInfo(void*, uint32_t, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 116 | uint32_t __stdcall KeyboardDevice::GetDeviceInfo(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 117 | uint32_t __stdcall KeyboardDevice::RunControlPanel(void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 118 | uint32_t __stdcall KeyboardDevice::Initialize(void*, int32_t, GUID&) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 119 | uint32_t __stdcall KeyboardDevice::CreateEffect(GUID&, void*, void**, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 120 | uint32_t __stdcall KeyboardDevice::EnumEffects(void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 121 | uint32_t __stdcall KeyboardDevice::GetEffectInfo(void*, GUID&) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 122 | uint32_t __stdcall KeyboardDevice::GetForceFeedbackState(uint32_t*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 123 | uint32_t __stdcall KeyboardDevice::SendForceFeedbackCommand(uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 124 | uint32_t __stdcall KeyboardDevice::EnumCreatedEffectObjects(void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 125 | uint32_t __stdcall KeyboardDevice::Escape(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 126 | uint32_t __stdcall KeyboardDevice::Poll() { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 127 | uint32_t __stdcall KeyboardDevice::SendDeviceData(void*, void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 128 | uint32_t __stdcall KeyboardDevice::EnumEffectsInFile(void*, void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 129 | uint32_t __stdcall KeyboardDevice::WriteEffectsToFile(void*, uint32_t, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 130 | -------------------------------------------------------------------------------- /subtitans/mousedevice.cpp: -------------------------------------------------------------------------------- 1 | #include "subtitans.h" 2 | #include "mousedevice.h" 3 | 4 | MouseDevice::MouseDevice() 5 | { 6 | ReferenceCount = 0; 7 | Initialized = true; 8 | } 9 | 10 | MouseDevice::~MouseDevice() 11 | { 12 | } 13 | 14 | uint32_t __stdcall MouseDevice::QueryInterface(GUID* guid, void** result) 15 | { 16 | unsigned long guid4[] = { 0, 0 }; 17 | memcpy(guid4, guid->Data4, sizeof(guid4)); 18 | GetLogger()->Error("%s %s %08X-%04X-%04X-%08X%08X\n", __FUNCTION__, "unknown interface", guid->Data1, guid->Data2, guid->Data3, guid4[0], guid4[1]); 19 | *result = nullptr; 20 | return ResultCode::NoInterface; 21 | } 22 | 23 | uint32_t __stdcall MouseDevice::AddRef() 24 | { 25 | GetLogger()->Trace("%s\n", __FUNCTION__); 26 | 27 | ReferenceCount++; 28 | 29 | return ResultCode::Ok; 30 | } 31 | 32 | uint32_t __stdcall MouseDevice::Release() 33 | { 34 | GetLogger()->Trace("%s\n", __FUNCTION__); 35 | 36 | if (--ReferenceCount == 0) 37 | delete this; 38 | 39 | return ResultCode::Ok; 40 | } 41 | 42 | uint32_t __stdcall MouseDevice::GetCapabilities(DInput::Caps* caps) 43 | { 44 | GetLogger()->Trace("%s\n", __FUNCTION__); 45 | 46 | if (caps->size != sizeof(DInput::Caps)) 47 | { 48 | GetLogger()->Error("%s %s %i != %i\n", __FUNCTION__, "caps structure has an unexpected size", caps->size, sizeof(DInput::Caps)); 49 | return ResultCode::InvalidArgument; 50 | } 51 | 52 | caps->flags = 1; // Attached 53 | caps->devType = 2; // Mouse 54 | caps->numberOfAxes = 3; // X, Y and Z 55 | caps->numberOfButtons = 4; // L, R, M and X 56 | 57 | return ResultCode::Ok; 58 | } 59 | 60 | uint32_t __stdcall MouseDevice::EnumObjects(void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 61 | uint32_t __stdcall MouseDevice::GetProperty(GUID& guid, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 62 | uint32_t __stdcall MouseDevice::SetProperty(GUID& guid, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 63 | 64 | uint32_t __stdcall MouseDevice::Acquire() 65 | { 66 | GetLogger()->Trace("%s\n", __FUNCTION__); 67 | 68 | if (!Initialized) 69 | { 70 | GetLogger()->Error("%s %s\n", __FUNCTION__, "data format has to be set"); 71 | return ResultCode::NotInitialized; 72 | } 73 | 74 | return ResultCode::Ok; 75 | } 76 | 77 | uint32_t __stdcall MouseDevice::Unacquire() 78 | { 79 | GetLogger()->Trace("%s\n", __FUNCTION__); 80 | return ResultCode::Ok; 81 | } 82 | 83 | uint32_t __stdcall MouseDevice::GetDeviceState(uint32_t bufferSize, void* buffer) 84 | { 85 | GetLogger()->Trace("%s\n", __FUNCTION__); 86 | 87 | if (!Initialized) 88 | { 89 | GetLogger()->Error("%s %s\n", __FUNCTION__, "data format has to be set"); 90 | return ResultCode::NotInitialized; 91 | } 92 | 93 | if (bufferSize != sizeof(Global::_MouseInformation)) 94 | { 95 | GetLogger()->Error("%s %s %i != %i\n", __FUNCTION__, "received an unexpected buffer size", bufferSize, sizeof(Global::MouseInformation)); 96 | return ResultCode::InvalidArgument; 97 | } 98 | 99 | memcpy(buffer, &Global::MouseInformation, sizeof(Global::_MouseInformation)); 100 | // Don't care about relative X & Y positioning. 101 | // X & Y values are modified by a detour later on. 102 | 103 | Global::MouseInformation.z = 0; 104 | 105 | return ResultCode::Ok; 106 | } 107 | 108 | uint32_t __stdcall MouseDevice::GetDeviceData(uint32_t, void*, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 109 | 110 | uint32_t __stdcall MouseDevice::SetDataFormat(DInput::DataFormat* dataFormat) 111 | { 112 | GetLogger()->Trace("%s\n", __FUNCTION__); 113 | Initialized = false; 114 | 115 | if (dataFormat->size != sizeof(DInput::DataFormat)) 116 | { 117 | GetLogger()->Error("%s %s %i != %i\n", __FUNCTION__, "data format has an unexpected size", dataFormat->size, sizeof(DInput::DataFormat)); 118 | return ResultCode::InvalidArgument; 119 | } 120 | 121 | if (dataFormat->objectSize != sizeof(DInput::ObjectDataFormat)) 122 | { 123 | GetLogger()->Error("%s %s %i != %i\n", __FUNCTION__, "object data format has an unexpected size", dataFormat->objectSize, sizeof(DInput::ObjectDataFormat)); 124 | return ResultCode::InvalidArgument; 125 | } 126 | 127 | if (dataFormat->contentSize != sizeof(Global::_MouseInformation)) 128 | { 129 | GetLogger()->Error("%s %s %i != %i\n", __FUNCTION__, "unexpected data buffer size", dataFormat->contentSize, sizeof(Global::_MouseInformation)); 130 | return ResultCode::InvalidArgument; 131 | } 132 | 133 | Initialized = true; 134 | 135 | return ResultCode::Ok; 136 | } 137 | 138 | uint32_t __stdcall MouseDevice::SetEventNotification(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 139 | 140 | uint32_t __stdcall MouseDevice::SetCooperativeLevel(void*, uint32_t) 141 | { 142 | GetLogger()->Trace("%s\n", __FUNCTION__); 143 | return ResultCode::Ok; 144 | } 145 | 146 | uint32_t __stdcall MouseDevice::GetObjectInfo(void*, uint32_t, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 147 | uint32_t __stdcall MouseDevice::GetDeviceInfo(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 148 | uint32_t __stdcall MouseDevice::RunControlPanel(void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 149 | uint32_t __stdcall MouseDevice::Initialize(void*, int32_t, GUID&) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 150 | uint32_t __stdcall MouseDevice::CreateEffect(GUID&, void*, void**, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 151 | uint32_t __stdcall MouseDevice::EnumEffects(void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 152 | uint32_t __stdcall MouseDevice::GetEffectInfo(void*, GUID&) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 153 | uint32_t __stdcall MouseDevice::GetForceFeedbackState(uint32_t*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 154 | uint32_t __stdcall MouseDevice::SendForceFeedbackCommand(uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 155 | uint32_t __stdcall MouseDevice::EnumCreatedEffectObjects(void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 156 | uint32_t __stdcall MouseDevice::Escape(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 157 | uint32_t __stdcall MouseDevice::Poll() { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 158 | uint32_t __stdcall MouseDevice::SendDeviceData(void*, void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 159 | uint32_t __stdcall MouseDevice::EnumEffectsInFile(void*, void* callback, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 160 | uint32_t __stdcall MouseDevice::WriteEffectsToFile(void*, uint32_t, void*, uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } -------------------------------------------------------------------------------- /subtitans/gogpatcher.cpp: -------------------------------------------------------------------------------- 1 | #include "subtitans.h" 2 | #include "nativeresolutionpatch.h" 3 | #include "sleepwellpatch.h" 4 | #include "movieheapcorruptionpatch.h" 5 | #include "scrollpatch.h" 6 | #include "missionskippatch.h" 7 | #include "ddrawreplacementpatch.h" 8 | #include "gogpatcher.h" 9 | 10 | GOGPatcher::GOGPatcher() 11 | { 12 | 13 | } 14 | 15 | GOGPatcher::~GOGPatcher() 16 | { 17 | 18 | } 19 | 20 | void GOGPatcher::Configure() 21 | { 22 | auto renderingBackend = GetConfiguration()->GetInt32(L"FEATURE", L"Renderer", Global::RenderingBackend::Automatic); 23 | bool usingGOGRenderer = renderingBackend == Global::RenderingBackend::DirectDraw || renderingBackend == Global::RenderingBackend::Automatic; 24 | 25 | // Always enable the improved frames per second limiter when DirectDraw isn't used 26 | // The alternative renderers are listening to the events published by SleepWellPatch 27 | if (!usingGOGRenderer || GetConfiguration()->GetBoolean(L"FIX", L"ImprovedFPSLimiter", true)) 28 | { 29 | auto sleepWellPatch = new SleepWellPatch(); 30 | sleepWellPatch->DetourAddress = 0x006E5064; 31 | sleepWellPatch->FrameLimitMemoryAddress = 0x00807654; 32 | sleepWellPatch->DisableOriginalLimiterSleepAddress = 0x006E508B; 33 | _patches.push_back(sleepWellPatch); 34 | } 35 | 36 | if(!usingGOGRenderer) 37 | { 38 | GetLogger()->Informational("Using a DirectDraw replacement\n"); 39 | 40 | Global::RenderWidth = GetConfiguration()->GetInt32(L"SETTING", L"Width", 0); 41 | if (Global::RenderWidth == 0) 42 | Global::RenderWidth = Global::MonitorWidth; 43 | 44 | Global::RenderHeight = GetConfiguration()->GetInt32(L"SETTING", L"Height", 0); 45 | if (Global::RenderHeight == 0) 46 | Global::RenderHeight = Global::MonitorHeight; 47 | 48 | auto ddrawReplacementPatch = new DDrawReplacementPatch(); 49 | ddrawReplacementPatch->DDrawDetourAddress = 0x006B9981; 50 | ddrawReplacementPatch->DInputDetourAddress = 0x0071B2BE; 51 | ddrawReplacementPatch->WindowRegisterClassDetourAddress = 0x0056AEC7; 52 | ddrawReplacementPatch->WindowCreateDetourAddress = 0x0056AF13; 53 | ddrawReplacementPatch->DInputAbsolutePositioningDetourAddress = 0x0071B6CC; 54 | ddrawReplacementPatch->ForceSoftwareRendering = renderingBackend == Global::RenderingBackend::Software; 55 | ddrawReplacementPatch->DInputReplacement = GetConfiguration()->GetBoolean(L"FEATURE", L"CustomInput", true); 56 | _patches.push_back(ddrawReplacementPatch); 57 | 58 | Global::RetroShader = GetConfiguration()->GetBoolean(L"FEATURE", L"RetroShader", false); 59 | } 60 | 61 | if (GetConfiguration()->GetBoolean(L"FEATURE", L"NativeResolution", true)) 62 | { 63 | auto nativeResolutionPatch = new NativeResolutionPatch(); 64 | nativeResolutionPatch->GuiRescalerAddress = 0x004F19E4; 65 | nativeResolutionPatch->QueueScreenAddress = 0x004F845B; 66 | nativeResolutionPatch->ScreenInitialResizeWidthAddress = 0x005307EB; 67 | nativeResolutionPatch->ScreenInitialResizeHeightAddress = 0x005307F2; 68 | nativeResolutionPatch->ScreenResizeWidthCompareAddress = 0x0056D78A; 69 | nativeResolutionPatch->ScreenResizeWidthAddress = 0x0056D7F5; 70 | nativeResolutionPatch->ScreenResizeHeightAddress = 0x0056D7FF; 71 | nativeResolutionPatch->GamefieldPresetWidthAddress = 0x0056B1CC; 72 | nativeResolutionPatch->GamefieldPresetHeightAddress = 0x0056B1D6; 73 | nativeResolutionPatch->GamefieldHeightReducingAddress = 0x004F7057; 74 | nativeResolutionPatch->GamefieldHeightRestorationAddress = 0x004FB487; 75 | nativeResolutionPatch->MovieWidthAddress = 0x00570515; 76 | nativeResolutionPatch->MovieHeightAddress = 0x0057051C; 77 | nativeResolutionPatch->RepositionBottomMenuDetourAddress = 0x004F6814; 78 | nativeResolutionPatch->RenameSettingsDetourAddress = 0x0052F190; 79 | nativeResolutionPatch->RenameSettingsFunctionAddress = 0x00711B70; 80 | nativeResolutionPatch->RedesignFrameDetourAddress = 0x00543032; 81 | nativeResolutionPatch->RedesignFrameTeamIdMemoryAddress = 0x0080874E; 82 | nativeResolutionPatch->RedesignFrameDrawFunctionAddress = 0x00403738; 83 | nativeResolutionPatch->RepositionBriefingDetourAddress = 0x004F6AB2; 84 | nativeResolutionPatch->CurrentScreenWidthAddress = 0x00806730; 85 | _patches.push_back(nativeResolutionPatch); 86 | } 87 | 88 | if (GetConfiguration()->GetBoolean(L"FIX", L"MovieHeap", true)) 89 | { 90 | auto movieHeapCorruptionPatch = new MovieHeapCorruptionPatch(); 91 | movieHeapCorruptionPatch->AllocatedMemoryOffset = 0x00856900; 92 | movieHeapCorruptionPatch->StructurePointer = 0x006D5923 + 3; 93 | movieHeapCorruptionPatch->StructureOffsets[0] = 0x006D59B0 + 1; 94 | movieHeapCorruptionPatch->StructureOffsets[1] = 0x006D5A0C + 1; 95 | movieHeapCorruptionPatch->StructureOffsets[2] = 0x006D5A11 + 1; 96 | movieHeapCorruptionPatch->StructureOffsets[3] = 0x006D5A21 + 2; 97 | movieHeapCorruptionPatch->StructureOffsets[4] = 0x006D5A2D + 2; 98 | movieHeapCorruptionPatch->StructureOffsets[5] = 0x006D5A33 + 1; 99 | movieHeapCorruptionPatch->StructureOffsets[6] = 0x006D5A38 + 1; 100 | movieHeapCorruptionPatch->StructureOffsets[7] = 0x006D5A43 + 2; 101 | movieHeapCorruptionPatch->StructureOffsets[8] = 0x006D5A4F + 2; 102 | movieHeapCorruptionPatch->StructureOffsets[9] = 0x006D5A55 + 2; 103 | movieHeapCorruptionPatch->StructureOffsets[10] = 0x006D5A65 + 2; 104 | movieHeapCorruptionPatch->StructureOffsets[11] = 0x006D5A73 + 2; 105 | movieHeapCorruptionPatch->StructureOffsets[12] = 0x006D5A79 + 3; 106 | movieHeapCorruptionPatch->StructureOffsets[13] = 0x006D5A84 + 1; 107 | movieHeapCorruptionPatch->StructureOffsets[14] = 0x006D5A89 + 3; 108 | movieHeapCorruptionPatch->StructureOffsets[15] = 0x006D5A90 + 1; 109 | movieHeapCorruptionPatch->StructureOffsets[16] = 0x006D5AB4 + 1; 110 | movieHeapCorruptionPatch->StructureOffsets[17] = 0x006D5ABE + 2; 111 | movieHeapCorruptionPatch->StructureOffsets[18] = 0x006D5AD8 + 1; 112 | movieHeapCorruptionPatch->StructureOffsets[19] = 0x006D5AE2 + 1; 113 | movieHeapCorruptionPatch->StructureOffsets[20] = 0x006D5AE7 + 2; 114 | movieHeapCorruptionPatch->StructureOffsets[21] = 0x006D5B1D + 1; 115 | movieHeapCorruptionPatch->StructureOffsets[22] = 0x006D5B2B + 2; 116 | movieHeapCorruptionPatch->StructureOffsets[23] = 0x006D5B37 + 2; 117 | movieHeapCorruptionPatch->StructureOffsets[24] = 0x006D5B41 + 2; 118 | movieHeapCorruptionPatch->StructureOffsets[25] = 0x006D5BDE + 1; 119 | movieHeapCorruptionPatch->DetourAddress = 0x006D599C; 120 | _patches.push_back(movieHeapCorruptionPatch); 121 | } 122 | 123 | if (GetConfiguration()->GetBoolean(L"FIX", L"SmoothScroll", true)) 124 | { 125 | auto scrollPatch = new ScrollPatch(); 126 | scrollPatch->UpdateRateAddress = 0x004AB083 + 2; 127 | scrollPatch->DetourAddress = 0x004AB0EF; 128 | scrollPatch->OriginalSpeedModifiersAddress = 0x007AC584; 129 | _patches.push_back(scrollPatch); 130 | } 131 | 132 | if (GetConfiguration()->GetBoolean(L"FEATURE", L"MissionSkipCheat", true)) 133 | { 134 | auto missionSkipPatch = new MissionSkipPatch(); 135 | missionSkipPatch->AddCheatCodeDetourAddress = 0x00522B66; 136 | missionSkipPatch->AddCheatCodeAlternativeReturnAddress = 0x00522B85; 137 | missionSkipPatch->FullMapPathDetourAddress = 0x00593442; 138 | missionSkipPatch->RelativeMapPathDetourAddress = 0x00593476; 139 | missionSkipPatch->CurrentMapNameVariable = 0x0080EF1E; 140 | missionSkipPatch->CheatValidationFunctionAddress = 0x0072E6F0; 141 | _patches.push_back(missionSkipPatch); 142 | } 143 | } -------------------------------------------------------------------------------- /subtitans/steampatchedpatcher.cpp: -------------------------------------------------------------------------------- 1 | #include "subtitans.h" 2 | #include "nativeresolutionpatch.h" 3 | #include "sleepwellpatch.h" 4 | #include "ddrawreplacementpatch.h" 5 | #include "movieheapcorruptionpatch.h" 6 | #include "scrollpatch.h" 7 | #include "missionskippatch.h" 8 | #include "steampatchedpatcher.h" 9 | 10 | SteamPatchedPatcher::SteamPatchedPatcher() 11 | { 12 | } 13 | 14 | SteamPatchedPatcher::~SteamPatchedPatcher() 15 | { 16 | 17 | } 18 | 19 | void SteamPatchedPatcher::Configure() 20 | { 21 | bool isWindows7 = IsWindows7OrGreater() && !IsWindows8OrGreater(); 22 | 23 | auto renderingBackend = GetConfiguration()->GetInt32(L"FEATURE", L"Renderer", Global::RenderingBackend::Automatic); 24 | 25 | // Always enable the improved frames per second limiter when DirectDraw isn't used 26 | // The alternative renderers are listening to the events published by SleepWellPatch 27 | if (renderingBackend != Global::RenderingBackend::DirectDraw || 28 | GetConfiguration()->GetBoolean(L"FIX", L"ImprovedFPSLimiter", true)) 29 | { 30 | auto sleepWellPatch = new SleepWellPatch(); 31 | sleepWellPatch->DetourAddress = 0x006E5064; 32 | sleepWellPatch->FrameLimitMemoryAddress = 0x00807654; 33 | sleepWellPatch->DisableOriginalLimiterSleepAddress = 0x006E508B; 34 | _patches.push_back(sleepWellPatch); 35 | } 36 | 37 | if (renderingBackend == Global::RenderingBackend::DirectDraw) 38 | { 39 | GetLogger()->Informational("Using DirectDraw\n"); 40 | } 41 | else 42 | { 43 | GetLogger()->Informational("Using a DirectDraw replacement\n"); 44 | 45 | Global::RenderWidth = GetConfiguration()->GetInt32(L"SETTING", L"Width", 0); 46 | if (Global::RenderWidth == 0) 47 | Global::RenderWidth = Global::MonitorWidth; 48 | 49 | Global::RenderHeight = GetConfiguration()->GetInt32(L"SETTING", L"Height", 0); 50 | if (Global::RenderHeight == 0) 51 | Global::RenderHeight = Global::MonitorHeight; 52 | 53 | auto ddrawReplacementPatch = new DDrawReplacementPatch(); 54 | ddrawReplacementPatch->DDrawDetourAddress = 0x006B9981; 55 | ddrawReplacementPatch->DInputDetourAddress = 0x0071B2BE; 56 | ddrawReplacementPatch->WindowRegisterClassDetourAddress = 0x0056AEC7; 57 | ddrawReplacementPatch->WindowCreateDetourAddress = 0x0056AF13; 58 | ddrawReplacementPatch->DInputAbsolutePositioningDetourAddress = 0x0071B6CC; 59 | ddrawReplacementPatch->ForceSoftwareRendering = renderingBackend == Global::RenderingBackend::Software 60 | || (isWindows7 && renderingBackend == Global::RenderingBackend::Automatic); // Prefer software rendering on windows 7 61 | ddrawReplacementPatch->DInputReplacement = GetConfiguration()->GetBoolean(L"FEATURE", L"CustomInput", true); 62 | _patches.push_back(ddrawReplacementPatch); 63 | 64 | Global::RetroShader = GetConfiguration()->GetBoolean(L"FEATURE", L"RetroShader", false); 65 | } 66 | 67 | if (GetConfiguration()->GetBoolean(L"FEATURE", L"NativeResolution", true)) 68 | { 69 | auto nativeResolutionPatch = new NativeResolutionPatch(); 70 | nativeResolutionPatch->GuiRescalerAddress = 0x004F19E4; 71 | nativeResolutionPatch->QueueScreenAddress = 0x004F845B; 72 | nativeResolutionPatch->ScreenInitialResizeWidthAddress = 0x005307EB; 73 | nativeResolutionPatch->ScreenInitialResizeHeightAddress = 0x005307F2; 74 | nativeResolutionPatch->ScreenResizeWidthCompareAddress = 0x0056D78A; 75 | nativeResolutionPatch->ScreenResizeWidthAddress = 0x0056D7F5; 76 | nativeResolutionPatch->ScreenResizeHeightAddress = 0x0056D7FF; 77 | nativeResolutionPatch->GamefieldPresetWidthAddress = 0x0056B1CC; 78 | nativeResolutionPatch->GamefieldPresetHeightAddress = 0x0056B1D6; 79 | nativeResolutionPatch->GamefieldHeightReducingAddress = 0x004F7057; 80 | nativeResolutionPatch->GamefieldHeightRestorationAddress = 0x004FB487; 81 | nativeResolutionPatch->MovieWidthAddress = 0x00570515; 82 | nativeResolutionPatch->MovieHeightAddress = 0x0057051C; 83 | nativeResolutionPatch->RepositionBottomMenuDetourAddress = 0x004F6814; 84 | nativeResolutionPatch->RenameSettingsDetourAddress = 0x0052F190; 85 | nativeResolutionPatch->RenameSettingsFunctionAddress = 0x00711B70; 86 | nativeResolutionPatch->RedesignFrameDetourAddress = 0x00543032; 87 | nativeResolutionPatch->RedesignFrameTeamIdMemoryAddress = 0x0080874E; 88 | nativeResolutionPatch->RedesignFrameDrawFunctionAddress = 0x00403738; 89 | nativeResolutionPatch->RepositionBriefingDetourAddress = 0x004F6AB2; 90 | nativeResolutionPatch->CurrentScreenWidthAddress = 0x00806730; 91 | _patches.push_back(nativeResolutionPatch); 92 | } 93 | 94 | if (GetConfiguration()->GetBoolean(L"FIX", L"MovieHeap", true)) 95 | { 96 | auto movieHeapCorruptionPatch = new MovieHeapCorruptionPatch(); 97 | movieHeapCorruptionPatch->AllocatedMemoryOffset = 0x00856900; 98 | movieHeapCorruptionPatch->StructurePointer = 0x006D5923 + 3; 99 | movieHeapCorruptionPatch->StructureOffsets[0] = 0x006D59B0 + 1; 100 | movieHeapCorruptionPatch->StructureOffsets[1] = 0x006D5A0C + 1; 101 | movieHeapCorruptionPatch->StructureOffsets[2] = 0x006D5A11 + 1; 102 | movieHeapCorruptionPatch->StructureOffsets[3] = 0x006D5A21 + 2; 103 | movieHeapCorruptionPatch->StructureOffsets[4] = 0x006D5A2D + 2; 104 | movieHeapCorruptionPatch->StructureOffsets[5] = 0x006D5A33 + 1; 105 | movieHeapCorruptionPatch->StructureOffsets[6] = 0x006D5A38 + 1; 106 | movieHeapCorruptionPatch->StructureOffsets[7] = 0x006D5A43 + 2; 107 | movieHeapCorruptionPatch->StructureOffsets[8] = 0x006D5A4F + 2; 108 | movieHeapCorruptionPatch->StructureOffsets[9] = 0x006D5A55 + 2; 109 | movieHeapCorruptionPatch->StructureOffsets[10] = 0x006D5A65 + 2; 110 | movieHeapCorruptionPatch->StructureOffsets[11] = 0x006D5A73 + 2; 111 | movieHeapCorruptionPatch->StructureOffsets[12] = 0x006D5A79 + 3; 112 | movieHeapCorruptionPatch->StructureOffsets[13] = 0x006D5A84 + 1; 113 | movieHeapCorruptionPatch->StructureOffsets[14] = 0x006D5A89 + 3; 114 | movieHeapCorruptionPatch->StructureOffsets[15] = 0x006D5A90 + 1; 115 | movieHeapCorruptionPatch->StructureOffsets[16] = 0x006D5AB4 + 1; 116 | movieHeapCorruptionPatch->StructureOffsets[17] = 0x006D5ABE + 2; 117 | movieHeapCorruptionPatch->StructureOffsets[18] = 0x006D5AD8 + 1; 118 | movieHeapCorruptionPatch->StructureOffsets[19] = 0x006D5AE2 + 1; 119 | movieHeapCorruptionPatch->StructureOffsets[20] = 0x006D5AE7 + 2; 120 | movieHeapCorruptionPatch->StructureOffsets[21] = 0x006D5B1D + 1; 121 | movieHeapCorruptionPatch->StructureOffsets[22] = 0x006D5B2B + 2; 122 | movieHeapCorruptionPatch->StructureOffsets[23] = 0x006D5B37 + 2; 123 | movieHeapCorruptionPatch->StructureOffsets[24] = 0x006D5B41 + 2; 124 | movieHeapCorruptionPatch->StructureOffsets[25] = 0x006D5BDE + 1; 125 | movieHeapCorruptionPatch->DetourAddress = 0x006D599C; 126 | _patches.push_back(movieHeapCorruptionPatch); 127 | } 128 | 129 | if (GetConfiguration()->GetBoolean(L"FIX", L"SmoothScroll", true)) 130 | { 131 | auto scrollPatch = new ScrollPatch(); 132 | scrollPatch->UpdateRateAddress = 0x004AB083 + 2; 133 | scrollPatch->DetourAddress = 0x004AB0EF; 134 | scrollPatch->OriginalSpeedModifiersAddress = 0x007AC584; 135 | _patches.push_back(scrollPatch); 136 | } 137 | 138 | if (GetConfiguration()->GetBoolean(L"FEATURE", L"MissionSkipCheat", true)) 139 | { 140 | auto missionSkipPatch = new MissionSkipPatch(); 141 | missionSkipPatch->AddCheatCodeDetourAddress = 0x00522B66; 142 | missionSkipPatch->AddCheatCodeAlternativeReturnAddress = 0x00522B85; 143 | missionSkipPatch->FullMapPathDetourAddress = 0x00593442; 144 | missionSkipPatch->RelativeMapPathDetourAddress = 0x00593476; 145 | missionSkipPatch->CurrentMapNameVariable = 0x0080EF1E; 146 | missionSkipPatch->CheatValidationFunctionAddress = 0x0072E6F0; 147 | _patches.push_back(missionSkipPatch); 148 | } 149 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | 352 | # Submarine Titans 353 | *.dkd 354 | *.dkx 355 | *.bik 356 | *.vps 357 | *.avi 358 | *.dll 359 | *.exe 360 | *.rpt -------------------------------------------------------------------------------- /subtitans/subtitans.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | 16.0 15 | {033CA3B5-4016-489A-943E-663953F6F89A} 16 | subtitans 17 | 10.0 18 | 19 | 20 | 21 | DynamicLibrary 22 | true 23 | v142 24 | Unicode 25 | 26 | 27 | DynamicLibrary 28 | false 29 | v142 30 | true 31 | Unicode 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | false 47 | glew\include\;$(IncludePath) 48 | 49 | 50 | true 51 | glew\include\;$(IncludePath) 52 | 53 | 54 | 55 | Level3 56 | true 57 | true 58 | true 59 | NDEBUG;_CONSOLE;GLEW_STATIC;%(PreprocessorDefinitions) 60 | true 61 | 62 | 63 | Console 64 | true 65 | true 66 | false 67 | opengl32.lib;%(AdditionalDependencies) 68 | 69 | 70 | 71 | 72 | Level3 73 | true 74 | _DEBUG;_CONSOLE;GLEW_STATIC;%(PreprocessorDefinitions) 75 | true 76 | 77 | 78 | Console 79 | true 80 | opengl32.lib;%(AdditionalDependencies) 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /subtitans/subtitans.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | patcher 7 | 8 | 9 | patcher 10 | 11 | 12 | patches 13 | 14 | 15 | patches 16 | 17 | 18 | patcher 19 | 20 | 21 | patcher 22 | 23 | 24 | patches 25 | 26 | 27 | patches 28 | 29 | 30 | patches 31 | 32 | 33 | renderer 34 | 35 | 36 | renderer 37 | 38 | 39 | renderer 40 | 41 | 42 | renderer 43 | 44 | 45 | renderer 46 | 47 | 48 | renderer 49 | 50 | 51 | patches 52 | 53 | 54 | input 55 | 56 | 57 | input 58 | 59 | 60 | input 61 | 62 | 63 | external\glew 64 | 65 | 66 | external\imgui 67 | 68 | 69 | external\imgui 70 | 71 | 72 | external\imgui 73 | 74 | 75 | external\imgui 76 | 77 | 78 | external\imgui 79 | 80 | 81 | external\imgui 82 | 83 | 84 | patcher 85 | 86 | 87 | 88 | 89 | patcher 90 | 91 | 92 | patcher 93 | 94 | 95 | patches 96 | 97 | 98 | patches 99 | 100 | 101 | patches 102 | 103 | 104 | patcher 105 | 106 | 107 | 108 | patcher 109 | 110 | 111 | patches 112 | 113 | 114 | patches 115 | 116 | 117 | patches 118 | 119 | 120 | renderer 121 | 122 | 123 | renderer 124 | 125 | 126 | renderer 127 | 128 | 129 | renderer 130 | 131 | 132 | renderer 133 | 134 | 135 | renderer 136 | 137 | 138 | renderer 139 | 140 | 141 | renderer 142 | 143 | 144 | renderer 145 | 146 | 147 | renderer 148 | 149 | 150 | renderer 151 | 152 | 153 | patches 154 | 155 | 156 | input 157 | 158 | 159 | input 160 | 161 | 162 | input 163 | 164 | 165 | input 166 | 167 | 168 | input 169 | 170 | 171 | external\glew 172 | 173 | 174 | external\glew 175 | 176 | 177 | external\imgui 178 | 179 | 180 | external\imgui 181 | 182 | 183 | external\imgui 184 | 185 | 186 | external\imgui 187 | 188 | 189 | external\imgui 190 | 191 | 192 | external\imgui 193 | 194 | 195 | external\imgui 196 | 197 | 198 | external\imgui 199 | 200 | 201 | patcher 202 | 203 | 204 | 205 | 206 | {e3532ddf-e57d-41bd-a5df-615eb2473d18} 207 | 208 | 209 | {de28ff75-bb05-4d2f-a46c-e8e9928ad7a0} 210 | 211 | 212 | {96b7acbf-c395-4eaf-9e76-233bef363367} 213 | 214 | 215 | {e5c7e412-5885-47ed-835b-1c1e0f54b9d5} 216 | 217 | 218 | {7339be0a-9b12-4ffe-9890-19a395845437} 219 | 220 | 221 | {af7b9feb-4c75-463d-a3d5-322996067dd1} 222 | 223 | 224 | {7b3b4aa3-26da-4048-aee1-be945cf29d93} 225 | 226 | 227 | -------------------------------------------------------------------------------- /subtitans/missionskippatch.cpp: -------------------------------------------------------------------------------- 1 | #include "subtitans.h" 2 | #include "missionskippatch.h" 3 | 4 | /* 5 | Issue: Some missions don't always trigger the mission end event 6 | Solution: Add a cheat for mission skipping 7 | */ 8 | 9 | namespace MissionSkip { 10 | // Runtime variables 11 | static char* CurrentMapNamePointer = 0; 12 | 13 | // Function specific variables 14 | const char MissionSkipCheatString[] = { 'O', 'R', 'B', 'I', 'T', 'O', 'N', 0x00 }; 15 | const char MissionValidationString[] = { 'M', 'I', 'S', 'S', 'I', 'O', 'N', 'S', '\\', 'M', 'I', 'S', 'S', 0x00 }; 16 | 17 | // For Detours 18 | static char OrbitonMissionPathPattern[] = { '%', 's', '%', 's', '%', 's', '0', '0', '0', 0x00 }; 19 | static char DefaultMissionPathPattern[] = { '%', 's', '%', 's', '%', 's', '%', 'd', '0', '1', 0x00 }; 20 | static bool OrbitonActivated = false; 21 | 22 | void ActivateOrbiton() 23 | { 24 | GetLogger()->Informational("Orbiton is activated for %s\n", CurrentMapNamePointer); 25 | 26 | // Copy map name and convert to uppercase 27 | char cheatActivatedMapName[32] = { 0x00, }; 28 | strncpy_s(cheatActivatedMapName, sizeof(cheatActivatedMapName), CurrentMapNamePointer, strlen(CurrentMapNamePointer)); 29 | if (_strupr_s(cheatActivatedMapName, sizeof(cheatActivatedMapName)) != 0) 30 | { 31 | GetLogger()->Error("Failed to convert map name to uppercase\n"); 32 | GetLogger()->Informational("Orbiton has been disabled\n"); 33 | return; 34 | } 35 | 36 | constexpr int missionValidationStringLength = sizeof(MissionValidationString) - 1; 37 | constexpr int missionNumberStringLength = 3; 38 | 39 | // Validate if the string has the expected length 40 | if(strlen(cheatActivatedMapName) < missionValidationStringLength + missionNumberStringLength) 41 | { 42 | GetLogger()->Warning("Current map has an unexpected length, are you sure this is a mission?\n"); 43 | GetLogger()->Informational("Orbiton has been disabled\n"); 44 | return; 45 | } 46 | 47 | // Check if the current map is a mission 48 | for (unsigned int i = 0; i < missionValidationStringLength; ++i) 49 | { 50 | if (MissionValidationString[i] != cheatActivatedMapName[i]) 51 | { 52 | GetLogger()->Warning("You're only allowed to skip mission maps!\n"); 53 | GetLogger()->Informational("Orbiton has been disabled\n"); 54 | return; 55 | } 56 | } 57 | 58 | char missionNumberAsString[missionNumberStringLength + 1] = { 0x00, }; 59 | memcpy_s(missionNumberAsString, sizeof(missionNumberAsString), cheatActivatedMapName + missionValidationStringLength, missionNumberStringLength); 60 | int missionNumber = atoi(missionNumberAsString + 1); // Skip team 61 | if (missionNumber < 10) 62 | missionNumber++; 63 | 64 | sprintf_s(missionNumberAsString + 1, sizeof(missionNumberAsString) - 1, "%02d", missionNumber); 65 | memcpy_s(cheatActivatedMapName + missionValidationStringLength, missionNumberStringLength, missionNumberAsString, missionNumberStringLength); 66 | 67 | memcpy_s(OrbitonMissionPathPattern + (sizeof(OrbitonMissionPathPattern) - 1) - missionNumberStringLength, missionNumberStringLength, missionNumberAsString, missionNumberStringLength); 68 | 69 | GetLogger()->Informational("Next mission will be forced to: %s\nExit to main menu and start a new campaign.\n", cheatActivatedMapName); 70 | 71 | OrbitonActivated = true; 72 | } 73 | 74 | namespace AddCheatCode { 75 | // Detour variables 76 | constexpr unsigned long DetourSize = 7; 77 | static unsigned long JmpFromAddress = 0; // Inside of TECH cheat 78 | static unsigned long JmpBackAddress = 0; // Execute TECH cheat 79 | static unsigned long JmpBackAlternativeAddress = 0; // Jump to begin of FOW cheat 80 | static unsigned long CheatValidationFunctionAddress = 0; 81 | 82 | __declspec(naked) void Implementation() 83 | { 84 | // Restore code 85 | __asm add esp, 0x0C; 86 | __asm test eax, eax; 87 | __asm jnz missionSkipCheat; 88 | __asm jmp [JmpBackAddress]; 89 | 90 | missionSkipCheat: 91 | // New cheat 92 | __asm push 0x07; 93 | __asm push offset [MissionSkipCheatString]; 94 | __asm push ebx; 95 | __asm call [CheatValidationFunctionAddress]; 96 | __asm test eax, eax; 97 | __asm jnz missionSkipCheatEpilogue; 98 | 99 | __asm pushfd; 100 | __asm pushad; 101 | 102 | ActivateOrbiton(); 103 | 104 | __asm popad; 105 | __asm popfd; 106 | 107 | missionSkipCheatEpilogue: 108 | __asm jmp [JmpBackAlternativeAddress]; 109 | } 110 | } 111 | 112 | namespace FullPathOverride 113 | { 114 | // Detour variables 115 | constexpr unsigned long DetourSize = 5; 116 | static unsigned long JmpFromAddress = 0; 117 | static unsigned long JmpBackAddress = 0; 118 | 119 | __declspec(naked) void Implementation() 120 | { 121 | __asm cmp [OrbitonActivated], 0x00; 122 | __asm je fullPathOrbitonDeactivated; 123 | 124 | __asm cmp ecx, 0x01; 125 | __asm je disableOrbitonForTutorial; 126 | 127 | __asm push offset [OrbitonMissionPathPattern]; 128 | __asm jmp [JmpBackAddress]; 129 | 130 | disableOrbitonForTutorial: 131 | __asm mov [OrbitonActivated], 0x00; 132 | 133 | fullPathOrbitonDeactivated: 134 | __asm push offset [DefaultMissionPathPattern]; 135 | __asm jmp [JmpBackAddress]; 136 | } 137 | } 138 | 139 | namespace RelativePathOverride 140 | { 141 | // Detour variables 142 | constexpr unsigned long DetourSize = 5; 143 | static unsigned long JmpFromAddress = 0; 144 | static unsigned long JmpBackAddress = 0; 145 | 146 | __declspec(naked) void Implementation() 147 | { 148 | __asm cmp [OrbitonActivated], 0x00; 149 | __asm je fullPathOrbitonDeactivated; 150 | 151 | __asm push offset [OrbitonMissionPathPattern + 0x02]; 152 | __asm mov [OrbitonActivated], 0x00; 153 | __asm jmp [JmpBackAddress]; 154 | 155 | fullPathOrbitonDeactivated: 156 | __asm push offset [DefaultMissionPathPattern + 0x02]; 157 | __asm jmp [JmpBackAddress]; 158 | } 159 | } 160 | } 161 | 162 | MissionSkipPatch::MissionSkipPatch() 163 | { 164 | GetLogger()->Informational("Constructing %s\n", __func__); 165 | 166 | AddCheatCodeDetourAddress = 0; 167 | AddCheatCodeAlternativeReturnAddress = 0; 168 | FullMapPathDetourAddress = 0; 169 | RelativeMapPathDetourAddress = 0; 170 | 171 | CurrentMapNameVariable = 0; 172 | CheatValidationFunctionAddress = 0; 173 | } 174 | 175 | MissionSkipPatch::~MissionSkipPatch() 176 | { 177 | GetLogger()->Informational("Destructing %s\n", __func__); 178 | } 179 | 180 | bool MissionSkipPatch::Validate() 181 | { 182 | return AddCheatCodeDetourAddress && 183 | AddCheatCodeAlternativeReturnAddress && 184 | FullMapPathDetourAddress && 185 | RelativeMapPathDetourAddress && 186 | CurrentMapNameVariable && 187 | CheatValidationFunctionAddress; 188 | } 189 | 190 | bool MissionSkipPatch::Apply() 191 | { 192 | GetLogger()->Informational("%s\n", __FUNCTION__); 193 | 194 | MissionSkip::CurrentMapNamePointer = (char*)CurrentMapNameVariable; 195 | 196 | MissionSkip::AddCheatCode::JmpFromAddress = AddCheatCodeDetourAddress; 197 | MissionSkip::AddCheatCode::JmpBackAddress = MissionSkip::AddCheatCode::JmpFromAddress + MissionSkip::AddCheatCode::DetourSize; 198 | MissionSkip::AddCheatCode::JmpBackAlternativeAddress = AddCheatCodeAlternativeReturnAddress; 199 | MissionSkip::AddCheatCode::CheatValidationFunctionAddress = CheatValidationFunctionAddress; 200 | if (!Detour::Create(MissionSkip::AddCheatCode::JmpFromAddress, MissionSkip::AddCheatCode::DetourSize, (unsigned long)MissionSkip::AddCheatCode::Implementation)) 201 | return false; 202 | 203 | MissionSkip::FullPathOverride::JmpFromAddress = FullMapPathDetourAddress; 204 | MissionSkip::FullPathOverride::JmpBackAddress = MissionSkip::FullPathOverride::JmpFromAddress + MissionSkip::FullPathOverride::DetourSize; 205 | if (!Detour::Create(MissionSkip::FullPathOverride::JmpFromAddress, MissionSkip::FullPathOverride::DetourSize, (unsigned long)MissionSkip::FullPathOverride::Implementation)) 206 | return false; 207 | 208 | MissionSkip::RelativePathOverride::JmpFromAddress = RelativeMapPathDetourAddress; 209 | MissionSkip::RelativePathOverride::JmpBackAddress = MissionSkip::RelativePathOverride::JmpFromAddress + MissionSkip::RelativePathOverride::DetourSize; 210 | if (!Detour::Create(MissionSkip::RelativePathOverride::JmpFromAddress, MissionSkip::RelativePathOverride::DetourSize, (unsigned long)MissionSkip::RelativePathOverride::Implementation)) 211 | return false; 212 | 213 | return true; 214 | } 215 | -------------------------------------------------------------------------------- /subtitans/subtitans.cpp: -------------------------------------------------------------------------------- 1 | #include "subtitans.h" 2 | #include "steampatcher.h" 3 | #include "steampatchedpatcher.h" 4 | #include "gogpatcher.h" 5 | #include "demopatcher.h" 6 | 7 | static Logger* g_Logger = nullptr; 8 | Logger* GetLogger() 9 | { 10 | if (!g_Logger) 11 | { 12 | g_Logger = new Logger("subtitans.log"); 13 | g_Logger->Clear(); 14 | } 15 | 16 | return g_Logger; 17 | } 18 | 19 | static Configuration* g_Configuration = nullptr; 20 | Configuration* GetConfiguration() 21 | { 22 | if (!g_Configuration) 23 | { 24 | g_Configuration = new Configuration(L"subtitans.ini"); 25 | GetLogger()->Informational("Initialized configuration\n"); 26 | } 27 | 28 | return g_Configuration; 29 | } 30 | 31 | namespace Global 32 | { 33 | int32_t InternalWidth = 0; 34 | int32_t InternalHeight = 0; 35 | int32_t MonitorWidth = 0; 36 | int32_t MonitorHeight = 0; 37 | int32_t RenderWidth = 0; 38 | int32_t RenderHeight = 0; 39 | 40 | int32_t BitsPerPixel = 0; 41 | 42 | bool VideoWorkaround = false; 43 | 44 | HWND GameWindow = nullptr; 45 | 46 | HANDLE RenderEvent = nullptr; 47 | HANDLE VerticalBlankEvent = nullptr; 48 | 49 | IRenderer* Backend = nullptr; 50 | 51 | bool RetroShader = false; 52 | std::atomic ImGuiEnabled = false; 53 | 54 | _MouseInformation MouseInformation; 55 | _KeyboardInformation KeyboardInformation; 56 | 57 | void Init() 58 | { 59 | InternalWidth = 800; 60 | InternalHeight = 600; 61 | MonitorWidth = GetSystemMetrics(SM_CXSCREEN); 62 | MonitorHeight = GetSystemMetrics(SM_CYSCREEN); 63 | RenderWidth = MonitorWidth; 64 | RenderHeight = MonitorHeight; 65 | 66 | BitsPerPixel = 8; 67 | 68 | RenderEvent = CreateEvent(NULL, FALSE, FALSE, NULL); 69 | VerticalBlankEvent = CreateEvent(NULL, FALSE, FALSE, NULL); 70 | 71 | memset(&MouseInformation, 0, sizeof(_MouseInformation)); 72 | memset(&KeyboardInformation, 0, sizeof(_KeyboardInformation)); 73 | } 74 | } 75 | 76 | const char* GetApplicationLanguage() 77 | { 78 | const std::wstring STStringDll = L"ST_String.dll"; 79 | if (File::Exists(STStringDll.c_str())) 80 | { 81 | uint32_t checksum = File::CalculateChecksum(STStringDll.c_str()); 82 | switch (checksum) 83 | { 84 | case Shared::ST_LANGUAGE_ENGLISH_UNPATCHED: 85 | case Shared::ST_LANGUAGE_ENGLISH_PATCHED: 86 | case Shared::ST_LANGUAGE_ENGLISH_DEMO: 87 | return "English"; 88 | default: 89 | return "Unknown language"; 90 | } 91 | } 92 | else 93 | { 94 | return "ST_String.dll not found"; 95 | } 96 | } 97 | 98 | Patcher* CreateVersionSpecificGamePatcher(unsigned long gameVersion) 99 | { 100 | Patcher* patcher = nullptr; 101 | 102 | GetLogger()->Informational("Detected Version: "); 103 | switch (gameVersion) 104 | { 105 | case Shared::ST_GAMEVERSION_RETAIL_UNPATCHED: 106 | patcher = new SteamPatcher(); 107 | GetLogger()->Informational("Retail 1.0 - %s\n", GetApplicationLanguage()); 108 | GetLogger()->Warning("DEPRECATED!!! Consider updating to 1.1\n"); 109 | MessageBox(NULL, L"This version of Submarine Titans is outdated, please update to v1.1.", L"SubTitans Patch", MB_ICONWARNING); 110 | break; 111 | case Shared::ST_GAMEVERSION_RETAIL_PATCHED: 112 | patcher = new SteamPatchedPatcher(); 113 | GetLogger()->Informational("Retail 1.1 - %s\n", GetApplicationLanguage()); 114 | break; 115 | case Shared::ST_GAMEVERSION_GOG_MODIFIED: 116 | patcher = new GOGPatcher(); 117 | GetLogger()->Informational("GOG 1.1 - %s\n", GetApplicationLanguage()); 118 | break; 119 | case Shared::ST_GAMEVERSION_DEMO: 120 | patcher = new DemoPatcher(); 121 | GetLogger()->Informational("Demo - %s\n", GetApplicationLanguage()); 122 | break; 123 | default: 124 | GetLogger()->Informational("Unknown\n"); 125 | GetLogger()->Critical("Incompatible application\n"); 126 | MessageBox(NULL, L"Incompatible application", L"SubTitans Patch", MB_ICONERROR); 127 | break; 128 | } 129 | 130 | return patcher; 131 | } 132 | 133 | void LogOperatingSystem() 134 | { 135 | GetLogger()->Informational("Operating System: "); 136 | 137 | if (IsWindowsXPOrGreater() && !IsWindowsVistaOrGreater()) 138 | GetLogger()->Informational("Windows XP (unsupported)"); 139 | else if (IsWindowsVistaOrGreater() && !IsWindows7OrGreater()) 140 | GetLogger()->Informational("Windows Vista (unsupported)"); 141 | else if (IsWindows7OrGreater() && !IsWindows8OrGreater()) 142 | GetLogger()->Informational("Windows 7 (supported)"); 143 | // BUG: ST.exe has no manifest targeting Windows 8.1 or 10 so it will always report as Windows 8 144 | else if (IsWindows8OrGreater()) 145 | GetLogger()->Informational("Windows 8 or newer (supported)"); 146 | else 147 | GetLogger()->Informational("Unknown (unsupported)"); 148 | 149 | HMODULE hNTDLL = GetModuleHandle(L"ntdll.dll"); 150 | if (hNTDLL && GetProcAddress(hNTDLL, "wine_get_version") != NULL) 151 | GetLogger()->Informational(" - Running under Wine (supported)"); 152 | 153 | GetLogger()->Informational("\n"); 154 | } 155 | 156 | void LogHardwareInformation() 157 | { 158 | // Architecture 159 | GetLogger()->Informational("CPU Architecture: "); 160 | 161 | SYSTEM_INFO systemInfo; 162 | GetNativeSystemInfo(&systemInfo); 163 | switch (systemInfo.wProcessorArchitecture) 164 | { 165 | case PROCESSOR_ARCHITECTURE_AMD64: 166 | GetLogger()->Informational("x64\n"); 167 | break; 168 | case PROCESSOR_ARCHITECTURE_INTEL: 169 | GetLogger()->Informational("x32\n"); 170 | break; 171 | default: 172 | GetLogger()->Informational("Unknown (%i)\n", systemInfo.wProcessorArchitecture); 173 | break; 174 | } 175 | 176 | // Core count 177 | uint32_t coreCount = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS); 178 | GetLogger()->Informational("Total Processors: %i Cores\n", coreCount); 179 | 180 | // Memory 181 | MEMORYSTATUSEX memoryStatus; 182 | memoryStatus.dwLength = sizeof(MEMORYSTATUSEX); 183 | if (GlobalMemoryStatusEx(&memoryStatus) == 0) 184 | { 185 | GetLogger()->Warning("Failed to retrieve memory status\n"); 186 | } 187 | else 188 | { 189 | GetLogger()->Informational("Installed Memory: %I64dMiB\n", memoryStatus.ullTotalPhys / 1024 / 1024); 190 | GetLogger()->Informational("Available Memory: %I64dMiB\n", memoryStatus.ullAvailPhys / 1024 / 1024); 191 | GetLogger()->Informational("Committed Memory: %I64dMiB (%i%%)\n", (memoryStatus.ullTotalPhys - memoryStatus.ullAvailPhys) / 1024 / 1024, memoryStatus.dwMemoryLoad); 192 | } 193 | } 194 | 195 | uint32_t GetTimerResolution() 196 | { 197 | uint32_t resolution = 0; 198 | 199 | TIMECAPS timeCaps; 200 | if (timeGetDevCaps(&timeCaps, sizeof(TIMECAPS)) == TIMERR_NOERROR) 201 | resolution = min(max(timeCaps.wPeriodMin, 1), timeCaps.wPeriodMax); 202 | else 203 | GetLogger()->Error("Failed to get timer resolution\n"); 204 | 205 | GetLogger()->Informational("Timer Resolution: %ims\n", resolution); 206 | 207 | return resolution; 208 | } 209 | 210 | static Patcher* g_GamePatcher = nullptr; 211 | static UINT g_TimerResolution = 0; 212 | 213 | #pragma comment(linker, "/EXPORT:InitializeLibrary=_InitializeLibrary@4") 214 | extern "C" void __stdcall InitializeLibrary(unsigned long gameVersion) 215 | { 216 | Global::Init(); 217 | 218 | GetLogger()->Informational("Compilation Date: %s\n", __TIMESTAMP__); 219 | 220 | g_GamePatcher = CreateVersionSpecificGamePatcher(gameVersion); 221 | if(!g_GamePatcher) 222 | ExitProcess(-1); 223 | 224 | LogOperatingSystem(); 225 | LogHardwareInformation(); 226 | 227 | uint32_t timerResolution = GetTimerResolution(); 228 | 229 | GetLogger()->Informational("\nStarting SubTitans\n"); 230 | if (!g_GamePatcher->Initialize()) 231 | { 232 | GetLogger()->Warning("Game restart required because GamePatcher::Initialize returned false\n"); 233 | MessageBox(NULL, L"Please restart the game.", L"SubTitans Patch", MB_ICONINFORMATION); 234 | ExitProcess(0); 235 | } 236 | 237 | GetLogger()->Informational("Configuring patches\n"); 238 | g_GamePatcher->Configure(); 239 | 240 | GetLogger()->Informational("Applying patches\n"); 241 | if (!g_GamePatcher->Apply()) 242 | { 243 | GetLogger()->Critical("Failed to apply patches\n"); 244 | ExitProcess(-1); 245 | } 246 | 247 | // Set timer resolution 248 | g_TimerResolution = timerResolution; 249 | if(g_TimerResolution != 0) 250 | timeBeginPeriod(g_TimerResolution); 251 | } 252 | 253 | #pragma comment(linker, "/EXPORT:ReleaseLibrary=_ReleaseLibrary@0") 254 | extern "C" void __stdcall ReleaseLibrary() 255 | { 256 | GetLogger()->Informational("Shutting down\n"); 257 | ClipCursor(nullptr); // Free the cursor 258 | 259 | if (g_TimerResolution != 0) 260 | timeEndPeriod(g_TimerResolution); 261 | 262 | if (g_GamePatcher) 263 | delete g_GamePatcher; 264 | 265 | if (g_Configuration) 266 | delete g_Configuration; 267 | 268 | if (g_Logger) 269 | delete g_Logger; 270 | } 271 | 272 | BOOLEAN __stdcall DllMain(HINSTANCE handle, DWORD reason, LPVOID reserved) 273 | { 274 | return TRUE; 275 | } -------------------------------------------------------------------------------- /subtitans/steampatcher.cpp: -------------------------------------------------------------------------------- 1 | #include "subtitans.h" 2 | #include "nativeresolutionpatch.h" 3 | #include "sleepwellpatch.h" 4 | #include "ddrawreplacementpatch.h" 5 | #include "movieheapcorruptionpatch.h" 6 | #include "scrollpatch.h" 7 | #include "steampatcher.h" 8 | 9 | namespace Steam { 10 | bool DisableCompatibilityMode() 11 | { 12 | const std::wstring registryKeyName(L"Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers"); 13 | 14 | WCHAR applicationPath[MAX_PATH]; 15 | GetModuleFileName(NULL, applicationPath, MAX_PATH); 16 | 17 | DWORD keyBufferSize = 1024; 18 | WCHAR* keyBuffer = new WCHAR[keyBufferSize]; 19 | if (RegGetValue(HKEY_CURRENT_USER, registryKeyName.c_str(), applicationPath, RRF_RT_REG_SZ, NULL, keyBuffer, &keyBufferSize) != ERROR_SUCCESS) // Non existing or not enough rights? Don't bother for now. 20 | { 21 | delete[] keyBuffer; 22 | return false; 23 | } 24 | 25 | const std::wstring keyContent(keyBuffer); 26 | delete[] keyBuffer; 27 | 28 | if (keyContent.find(L"WINXPSP3") == std::wstring::npos) 29 | return false; 30 | 31 | // if (MessageBox(NULL, L"The WINXPSP3 compatibilty flag is known to cause conflicts with this game.\nDo you want to remove the key?", L"Conflicting key found", MB_YESNO | MB_ICONWARNING) != IDYES) 32 | // return false; 33 | 34 | HKEY registryKey; 35 | if (RegOpenKeyEx(HKEY_CURRENT_USER, registryKeyName.c_str(), 0, KEY_SET_VALUE, ®istryKey) != ERROR_SUCCESS) // Non existing or not enough rights? Don't bother for now. 36 | { 37 | MessageBox(NULL, L"Failed to remove the registry key", L"Error (Open Key)", MB_ICONERROR); 38 | return false; 39 | } 40 | 41 | bool keyDeleted = RegDeleteValue(registryKey, applicationPath) == ERROR_SUCCESS; 42 | if (!keyDeleted) 43 | MessageBox(NULL, L"Failed to remove the registry key", L"Error (Delete Key)", MB_ICONERROR); 44 | 45 | RegCloseKey(registryKey); 46 | return keyDeleted; 47 | } 48 | 49 | bool RemoveFile(std::wstring file) 50 | { 51 | DWORD findResult = GetFileAttributes(file.c_str()); 52 | if (findResult == INVALID_FILE_ATTRIBUTES || findResult & FILE_ATTRIBUTE_DIRECTORY) 53 | return false; 54 | 55 | std::wstring fileBackup = L"" + file + L".bak"; 56 | MoveFileEx(file.c_str(), fileBackup.c_str(), MOVEFILE_REPLACE_EXISTING); 57 | 58 | return true; 59 | } 60 | } 61 | 62 | SteamPatcher::SteamPatcher() 63 | { 64 | } 65 | 66 | SteamPatcher::~SteamPatcher() 67 | { 68 | } 69 | 70 | bool SteamPatcher::Initialize() 71 | { 72 | bool restartRequired = false; 73 | 74 | restartRequired |= Steam::RemoveFile(L"dplay.dll"); 75 | restartRequired |= Steam::RemoveFile(L"dplayx.dll"); 76 | restartRequired |= Steam::RemoveFile(L"steam_installscript.vdf"); 77 | restartRequired |= Steam::DisableCompatibilityMode(); 78 | 79 | return !restartRequired; 80 | } 81 | 82 | void SteamPatcher::Configure() 83 | { 84 | bool isWindows7 = IsWindows7OrGreater() && !IsWindows8OrGreater(); 85 | 86 | auto renderingBackend = GetConfiguration()->GetInt32(L"FEATURE", L"Renderer", Global::RenderingBackend::Automatic); 87 | 88 | // Always enable the improved frames per second limiter when DirectDraw isn't used 89 | // The alternative renderers are listening to the events published by SleepWellPatch 90 | if (renderingBackend != Global::RenderingBackend::DirectDraw || 91 | GetConfiguration()->GetBoolean(L"FIX", L"ImprovedFPSLimiter", true)) 92 | { 93 | auto sleepWellPatch = new SleepWellPatch(); 94 | sleepWellPatch->DetourAddress = 0x006E6314; 95 | sleepWellPatch->FrameLimitMemoryAddress = 0x00808024; 96 | sleepWellPatch->DisableOriginalLimiterSleepAddress = 0x006E633B; 97 | _patches.push_back(sleepWellPatch); 98 | } 99 | 100 | if (renderingBackend == Global::RenderingBackend::DirectDraw) 101 | { 102 | GetLogger()->Informational("Using DirectDraw\n"); 103 | } 104 | else 105 | { 106 | GetLogger()->Informational("Using a DirectDraw replacement\n"); 107 | 108 | Global::RenderWidth = GetConfiguration()->GetInt32(L"SETTING", L"Width", 0); 109 | if (Global::RenderWidth == 0) 110 | Global::RenderWidth = Global::MonitorWidth; 111 | 112 | Global::RenderHeight = GetConfiguration()->GetInt32(L"SETTING", L"Height", 0); 113 | if (Global::RenderHeight == 0) 114 | Global::RenderHeight = Global::MonitorHeight; 115 | 116 | auto ddrawReplacementPatch = new DDrawReplacementPatch(); 117 | ddrawReplacementPatch->DDrawDetourAddress = 0x006BAC31; 118 | ddrawReplacementPatch->DInputDetourAddress = 0x0071C56E; 119 | ddrawReplacementPatch->WindowRegisterClassDetourAddress = 0x0056C707; 120 | ddrawReplacementPatch->WindowCreateDetourAddress = 0x0056C753; 121 | ddrawReplacementPatch->DInputAbsolutePositioningDetourAddress = 0x0071C9C0; 122 | ddrawReplacementPatch->ForceSoftwareRendering = renderingBackend == Global::RenderingBackend::Software 123 | || (isWindows7 && renderingBackend == Global::RenderingBackend::Automatic); // Prefer software rendering on windows 7 124 | ddrawReplacementPatch->DInputReplacement = false; // Doesn't work that well with this version 125 | _patches.push_back(ddrawReplacementPatch); 126 | 127 | Global::RetroShader = GetConfiguration()->GetBoolean(L"FEATURE", L"RetroShader", false); 128 | } 129 | 130 | if (GetConfiguration()->GetBoolean(L"FEATURE", L"NativeResolution", true)) 131 | { 132 | auto nativeResolutionPatch = new NativeResolutionPatch(); 133 | nativeResolutionPatch->GuiRescalerAddress = 0x004F3254; 134 | nativeResolutionPatch->QueueScreenAddress = 0x004F9CCB; 135 | nativeResolutionPatch->ScreenInitialResizeWidthAddress = 0x0053202B; 136 | nativeResolutionPatch->ScreenInitialResizeHeightAddress = 0x00532032; 137 | nativeResolutionPatch->ScreenResizeWidthCompareAddress = 0x0056EFCA; 138 | nativeResolutionPatch->ScreenResizeWidthAddress = 0x0056F035; 139 | nativeResolutionPatch->ScreenResizeHeightAddress = 0x0056F03F; 140 | nativeResolutionPatch->GamefieldPresetWidthAddress = 0x0056CA0C; 141 | nativeResolutionPatch->GamefieldPresetHeightAddress = 0x0056CA16; 142 | nativeResolutionPatch->GamefieldHeightReducingAddress = 0x004F88C7; 143 | nativeResolutionPatch->GamefieldHeightRestorationAddress = 0x004FCCF7; 144 | nativeResolutionPatch->MovieWidthAddress = 0x00571D55; 145 | nativeResolutionPatch->MovieHeightAddress = 0x00571D5C; 146 | nativeResolutionPatch->RepositionBottomMenuDetourAddress = 0x004F8084; 147 | nativeResolutionPatch->RenameSettingsDetourAddress = 0x005309D0; 148 | nativeResolutionPatch->RenameSettingsFunctionAddress = 0x00712E20; 149 | nativeResolutionPatch->RedesignFrameDetourAddress = 0x00544872; 150 | nativeResolutionPatch->RedesignFrameTeamIdMemoryAddress = 0x0080911E; 151 | nativeResolutionPatch->RedesignFrameDrawFunctionAddress = 0x0040372E; 152 | nativeResolutionPatch->RepositionBriefingDetourAddress = 0x004F8322; 153 | nativeResolutionPatch->CurrentScreenWidthAddress = 0x00807100; 154 | _patches.push_back(nativeResolutionPatch); 155 | } 156 | 157 | if (GetConfiguration()->GetBoolean(L"FIX", L"MovieHeap", true)) 158 | { 159 | auto movieHeapCorruptionPatch = new MovieHeapCorruptionPatch(); 160 | movieHeapCorruptionPatch->AllocatedMemoryOffset = 0x008572D0; 161 | movieHeapCorruptionPatch->StructurePointer = 0x006D6BD3 + 3; 162 | movieHeapCorruptionPatch->StructureOffsets[0] = 0x006D6C60 + 1; 163 | movieHeapCorruptionPatch->StructureOffsets[1] = 0x006D6CBC + 1; 164 | movieHeapCorruptionPatch->StructureOffsets[2] = 0x006D6CC1 + 1; 165 | movieHeapCorruptionPatch->StructureOffsets[3] = 0x006D6CD1 + 2; 166 | movieHeapCorruptionPatch->StructureOffsets[4] = 0x006D6CDD + 2; 167 | movieHeapCorruptionPatch->StructureOffsets[5] = 0x006D6CE3 + 1; 168 | movieHeapCorruptionPatch->StructureOffsets[6] = 0x006D6CE8 + 1; 169 | movieHeapCorruptionPatch->StructureOffsets[7] = 0x006D6CF3 + 2; 170 | movieHeapCorruptionPatch->StructureOffsets[8] = 0x006D6CFF + 2; 171 | movieHeapCorruptionPatch->StructureOffsets[9] = 0x006D6D05 + 2; 172 | movieHeapCorruptionPatch->StructureOffsets[10] = 0x006D6D15 + 2; 173 | movieHeapCorruptionPatch->StructureOffsets[11] = 0x006D6D23 + 2; 174 | movieHeapCorruptionPatch->StructureOffsets[12] = 0x006D6D29 + 3; 175 | movieHeapCorruptionPatch->StructureOffsets[13] = 0x006D6D34 + 1; 176 | movieHeapCorruptionPatch->StructureOffsets[14] = 0x006D6D39 + 3; 177 | movieHeapCorruptionPatch->StructureOffsets[15] = 0x006D6D40 + 1; 178 | movieHeapCorruptionPatch->StructureOffsets[16] = 0x006D6D64 + 1; 179 | movieHeapCorruptionPatch->StructureOffsets[17] = 0x006D6D6E + 2; 180 | movieHeapCorruptionPatch->StructureOffsets[18] = 0x006D6D88 + 1; 181 | movieHeapCorruptionPatch->StructureOffsets[19] = 0x006D6D92 + 1; 182 | movieHeapCorruptionPatch->StructureOffsets[20] = 0x006D6D97 + 2; 183 | movieHeapCorruptionPatch->StructureOffsets[21] = 0x006D6DCD + 1; 184 | movieHeapCorruptionPatch->StructureOffsets[22] = 0x006D6DDB + 2; 185 | movieHeapCorruptionPatch->StructureOffsets[23] = 0x006D6DE7 + 2; 186 | movieHeapCorruptionPatch->StructureOffsets[24] = 0x006D6DF1 + 2; 187 | movieHeapCorruptionPatch->StructureOffsets[25] = 0x006D6E8E + 1; 188 | movieHeapCorruptionPatch->DetourAddress = 0x006D6C4C; 189 | _patches.push_back(movieHeapCorruptionPatch); 190 | } 191 | 192 | if (GetConfiguration()->GetBoolean(L"FIX", L"SmoothScroll", true)) 193 | { 194 | auto scrollPatch = new ScrollPatch(); 195 | scrollPatch->UpdateRateAddress = 0x004AC943 + 2; 196 | scrollPatch->DetourAddress = 0x004AC9AF; 197 | scrollPatch->OriginalSpeedModifiersAddress = 0x007ACF1C; 198 | _patches.push_back(scrollPatch); 199 | } 200 | } -------------------------------------------------------------------------------- /subtitans/device.cpp: -------------------------------------------------------------------------------- 1 | #include "subtitans.h" 2 | #include "surface.h" 3 | #include "clipper.h" 4 | #include "palette.h" 5 | #include "irenderer.h" 6 | #include "device.h" 7 | 8 | using namespace DDraw; 9 | 10 | Device::Device() 11 | { 12 | GetLogger()->Trace("%s\n", __FUNCTION__); 13 | 14 | referenceCount = 0; 15 | } 16 | Device::~Device() 17 | { 18 | GetLogger()->Trace("%s\n", __FUNCTION__); 19 | } 20 | 21 | // IUnknown 22 | uint32_t __stdcall Device::QueryInterface(GUID* guid, void** result) 23 | { 24 | GetLogger()->Trace("%s\n", __FUNCTION__); 25 | 26 | if (guid->Data1 == 0x9C59509A && 27 | guid->Data2 == 0x39BD && 28 | guid->Data3 == 0x11D1 && 29 | guid->Data4[0] == 0x8C && guid->Data4[1] == 0x4A && guid->Data4[2] == 0x00 && guid->Data4[3] == 0xC0 && 30 | guid->Data4[4] == 0x4F && guid->Data4[5] == 0xD9 && guid->Data4[6] == 0x30 && guid->Data4[7] == 0xC5) 31 | { 32 | *result = this; 33 | AddRef(); 34 | 35 | return ResultCode::Ok; 36 | } 37 | 38 | GetLogger()->Error("%s %s\n", __FUNCTION__, "unknown interface"); 39 | *result = nullptr; 40 | return ResultCode::NoInterface; 41 | } 42 | 43 | uint32_t __stdcall Device::AddRef() 44 | { 45 | GetLogger()->Trace("%s\n", __FUNCTION__); 46 | 47 | referenceCount++; 48 | 49 | return ResultCode::Ok; 50 | } 51 | 52 | uint32_t __stdcall Device::Release() 53 | { 54 | GetLogger()->Trace("%s (Remaining references %i)\n", __FUNCTION__, referenceCount); 55 | 56 | if(--referenceCount == 0) 57 | delete this; 58 | 59 | return ResultCode::Ok; 60 | } 61 | 62 | // Direct Draw 63 | uint32_t __stdcall Device::Compact() { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 64 | 65 | uint32_t __stdcall Device::CreateClipper(uint32_t, IDDrawClipper** result, void*) 66 | { 67 | GetLogger()->Trace("%s\n", __FUNCTION__); 68 | *result = new Clipper(); 69 | (*result)->AddRef(); 70 | 71 | return ResultCode::Ok; 72 | } 73 | 74 | uint32_t __stdcall Device::CreatePalette(uint32_t flags, void* palette, IDDrawPalette** result, void* unused) 75 | { 76 | GetLogger()->Trace("%s\n", __FUNCTION__); 77 | *result = new Palette(); 78 | (*result)->AddRef(); 79 | 80 | return ((Palette*)*result)->CreatePallete(flags, (uint8_t*)palette) ? ResultCode::Ok : ResultCode::Unimplemented; 81 | } 82 | 83 | uint32_t __stdcall Device::CreateSurface(DDraw::SurfaceDescription* surfaceDescription, IDDrawSurface4** result, void* unused) 84 | { 85 | GetLogger()->Trace("%s\n", __FUNCTION__); 86 | *result = new Surface(surfaceDescription); 87 | (*result)->AddRef(); 88 | 89 | return ResultCode::Ok; 90 | } 91 | 92 | uint32_t __stdcall Device::DuplicateSurface(void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 93 | uint32_t __stdcall Device::EnumDisplayModes(uint32_t flags, void* surfaceDescription, void* appDef, EnumDisplayModesCallBack callback) 94 | { 95 | GetLogger()->Trace("%s\n", __FUNCTION__); 96 | 97 | if (flags != 0) 98 | { 99 | GetLogger()->Error("%s %s\n", __FUNCTION__, "unexpected flags"); 100 | return ResultCode::InvalidArgument; 101 | } 102 | 103 | if (surfaceDescription) 104 | { 105 | GetLogger()->Warning("%s %s\n", __FUNCTION__, "unexpected surface description"); 106 | return ResultCode::InvalidArgument; 107 | } 108 | 109 | if (!appDef) 110 | { 111 | GetLogger()->Warning("%s %s\n", __FUNCTION__, "appDef is NULL"); 112 | return ResultCode::InvalidArgument; 113 | } 114 | 115 | if (!callback) 116 | { 117 | GetLogger()->Warning("%s %s\n", __FUNCTION__, "missing callback"); 118 | return ResultCode::InvalidArgument; 119 | } 120 | 121 | constexpr uint32_t bitsPerPixel[] = { 8,/* 16,*/ 32 }; 122 | const std::vector> displayModes = { 123 | std::make_pair(640, 480), 124 | std::make_pair(800, 600), 125 | std::make_pair(1024, 768), 126 | std::make_pair(1280, 1024), 127 | std::make_pair(Global::RenderWidth, Global::RenderHeight) 128 | }; 129 | 130 | SurfaceDescription resultSurfaceDescription; 131 | for (auto bbp : bitsPerPixel) 132 | { 133 | for (auto displayMode : displayModes) 134 | { 135 | memset(&resultSurfaceDescription, 0, sizeof(SurfaceDescription)); 136 | 137 | resultSurfaceDescription.size = sizeof(SurfaceDescription); 138 | resultSurfaceDescription.flags = SurfaceDescriptionFlag::Height | SurfaceDescriptionFlag::Width | SurfaceDescriptionFlag::Pitch 139 | | SurfaceDescriptionFlag::RefreshRate | SurfaceDescriptionFlag::PixelFormat; 140 | resultSurfaceDescription.width = displayMode.first; 141 | resultSurfaceDescription.height = displayMode.second; 142 | resultSurfaceDescription.pitch = ((displayMode.first * bbp + 31) & ~31) >> 3; //displayMode.first * (bbp / 8); 143 | resultSurfaceDescription.refreshRate = 0; 144 | resultSurfaceDescription.pixelFormat.size = sizeof(PixelFormat); 145 | resultSurfaceDescription.pixelFormat.flags = PixelFormatFlag::RGB; 146 | resultSurfaceDescription.pixelFormat.rgbBitCount = bbp; 147 | 148 | switch (bbp) 149 | { 150 | case 8: 151 | resultSurfaceDescription.pixelFormat.flags |= PixelFormatFlag::PalettedIndexed8; 152 | // case 16: 153 | case 32: 154 | default: 155 | resultSurfaceDescription.pixelFormat.rBitMask = 0xFF0000; 156 | resultSurfaceDescription.pixelFormat.gBitMask = 0xFF00; 157 | resultSurfaceDescription.pixelFormat.bBitMask = 0xFF; 158 | break; 159 | } 160 | 161 | GetLogger()->Debug("%s calling callback for %ix%i %ibpp\n", __FUNCTION__, displayMode.first, displayMode.second, bbp); 162 | 163 | // callback 0 = stop; 1 = continue 164 | if (callback(&resultSurfaceDescription, appDef) == 0) 165 | { 166 | GetLogger()->Debug("%s callback early quit\n", __FUNCTION__); 167 | 168 | return ResultCode::Ok; 169 | } 170 | } 171 | } 172 | 173 | GetLogger()->Trace("%s finished\n", __FUNCTION__); 174 | 175 | return ResultCode::Ok; 176 | } 177 | 178 | uint32_t __stdcall Device::EnumSurfaces(uint32_t, void*, void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 179 | uint32_t __stdcall Device::FlipToGDISurface() { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 180 | 181 | uint32_t __stdcall Device::GetCaps(Caps* caps, Caps* helCaps) 182 | { 183 | GetLogger()->Trace("%s\n", __FUNCTION__); 184 | 185 | memset(caps, 0, sizeof(Caps)); 186 | caps->size = sizeof(Caps); 187 | 188 | memset(helCaps, 0, sizeof(Caps)); 189 | helCaps->size = sizeof(Caps); 190 | 191 | return ResultCode::Ok; 192 | } 193 | 194 | uint32_t __stdcall Device::GetDisplayMode(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 195 | uint32_t __stdcall Device::GetFourCCCodes(void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 196 | uint32_t __stdcall Device::GetGDISurface(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 197 | uint32_t __stdcall Device::GetMonitoryFrequency(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 198 | uint32_t __stdcall Device::GetScanLine(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 199 | uint32_t __stdcall Device::GetVerticalBlankStatus(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 200 | uint32_t __stdcall Device::Initialize(void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 201 | 202 | uint32_t __stdcall Device::RestoreDisplayMode() 203 | { 204 | GetLogger()->Trace("%s\n", __FUNCTION__); 205 | return ResultCode::Ok; 206 | } 207 | 208 | uint32_t __stdcall Device::SetCooperativeLevel(HWND hwnd, uint32_t flags) 209 | { 210 | GetLogger()->Trace("%s\n", __FUNCTION__); 211 | return ResultCode::Ok; 212 | } 213 | 214 | uint32_t __stdcall Device::SetDisplayMode(uint32_t width, uint32_t height, uint32_t bitsPerPixel, uint32_t refreshRate, uint32_t flags) 215 | { 216 | GetLogger()->Trace("%s\n", __FUNCTION__); 217 | 218 | if (Global::Backend) 219 | Global::Backend->OnDestroyPrimarySurface(); 220 | 221 | Global::InternalWidth = width; 222 | Global::InternalHeight = height; 223 | 224 | Global::BitsPerPixel = bitsPerPixel; 225 | 226 | SetWindowPos(Global::GameWindow, NULL, 0, 0, Global::MonitorWidth, Global::MonitorHeight, 0); 227 | 228 | Global::VideoWorkaround = bitsPerPixel != 8 && width == 800 && height == 600; 229 | 230 | return ResultCode::Ok; 231 | } 232 | 233 | uint32_t __stdcall Device::WaitForVerticalBlank(uint32_t flag, void*) 234 | { 235 | GetLogger()->Trace("%s\n", __FUNCTION__); 236 | 237 | if (flag == 1) 238 | WaitForSingleObject(Global::VerticalBlankEvent, 65); 239 | else 240 | GetLogger()->Error("%s %s %08X\n", __FUNCTION__, "flag not implemented", flag); 241 | 242 | return ResultCode::Ok; 243 | } 244 | 245 | uint32_t __stdcall Device::GetAvailableVidMem(void*, void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 246 | uint32_t __stdcall Device::GetSurfaceFromDC(void*, void*) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 247 | uint32_t __stdcall Device::RestoreAllSurfaces() { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 248 | uint32_t __stdcall Device::TestCooperativeLevel() { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 249 | uint32_t __stdcall Device::GetDeviceIdentifier(void*,uint32_t) { GetLogger()->Error("%s\n", __FUNCTION__); return ResultCode::Ok; } 250 | 251 | uint32_t __stdcall DirectDrawCreate(void*, IDDraw4** result, void*) 252 | { 253 | *result = new Device(); 254 | (*result)->AddRef(); 255 | return ResultCode::Ok; 256 | } -------------------------------------------------------------------------------- /subtitans/imgui/imconfig.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // COMPILE-TIME OPTIONS FOR DEAR IMGUI 3 | // Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure. 4 | // You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions. 5 | //----------------------------------------------------------------------------- 6 | // A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it) 7 | // B) or '#define IMGUI_USER_CONFIG "my_imgui_config.h"' in your project and then add directives in your own file without touching this template. 8 | //----------------------------------------------------------------------------- 9 | // You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp 10 | // files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures. 11 | // Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts. 12 | // Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using. 13 | //----------------------------------------------------------------------------- 14 | 15 | #pragma once 16 | 17 | //---- Define assertion handler. Defaults to calling assert(). 18 | // If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement. 19 | //#define IM_ASSERT(_EXPR) MyAssert(_EXPR) 20 | //#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts 21 | 22 | //---- Define attributes of all API symbols declarations, e.g. for DLL under Windows 23 | // Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility. 24 | // DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions() 25 | // for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details. 26 | //#define IMGUI_API __declspec( dllexport ) 27 | //#define IMGUI_API __declspec( dllimport ) 28 | 29 | //---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names. 30 | //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS 31 | //#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87: disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This will be folded into IMGUI_DISABLE_OBSOLETE_FUNCTIONS in a few versions. 32 | 33 | //---- Disable all of Dear ImGui or don't implement standard windows. 34 | // It is very strongly recommended to NOT disable the demo windows during development. Please read comments in imgui_demo.cpp. 35 | //#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty. 36 | //#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. Not recommended. 37 | //#define IMGUI_DISABLE_METRICS_WINDOW // Disable metrics/debugger and other debug tools: ShowMetricsWindow() and ShowStackToolWindow() will be empty. 38 | 39 | //---- Don't implement some functions to reduce linkage requirements. 40 | //#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a) 41 | //#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW) 42 | //#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a) 43 | //#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime). 44 | //#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default). 45 | //#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf) 46 | //#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself. 47 | //#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies) 48 | //#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function. 49 | //#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions(). 50 | //#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available 51 | 52 | //---- Include imgui_user.h at the end of imgui.h as a convenience 53 | //#define IMGUI_INCLUDE_IMGUI_USER_H 54 | 55 | //---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another) 56 | //#define IMGUI_USE_BGRA_PACKED_COLOR 57 | 58 | //---- Use 32-bit for ImWchar (default is 16-bit) to support unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...) 59 | //#define IMGUI_USE_WCHAR32 60 | 61 | //---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version 62 | // By default the embedded implementations are declared static and not available outside of Dear ImGui sources files. 63 | //#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h" 64 | //#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h" 65 | //#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION 66 | //#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION 67 | 68 | //---- Use stb_printf's faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined) 69 | // Requires 'stb_sprintf.h' to be available in the include path. Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by STB sprintf. 70 | // #define IMGUI_USE_STB_SPRINTF 71 | 72 | //---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui) 73 | // Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided). 74 | // On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'. 75 | //#define IMGUI_ENABLE_FREETYPE 76 | 77 | //---- Use stb_truetype to build and rasterize the font atlas (default) 78 | // The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend. 79 | //#define IMGUI_ENABLE_STB_TRUETYPE 80 | 81 | //---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4. 82 | // This will be inlined as part of ImVec2 and ImVec4 class declarations. 83 | /* 84 | #define IM_VEC2_CLASS_EXTRA \ 85 | ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \ 86 | operator MyVec2() const { return MyVec2(x,y); } 87 | 88 | #define IM_VEC4_CLASS_EXTRA \ 89 | ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \ 90 | operator MyVec4() const { return MyVec4(x,y,z,w); } 91 | */ 92 | 93 | //---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices. 94 | // Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices). 95 | // Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer. 96 | // Read about ImGuiBackendFlags_RendererHasVtxOffset for details. 97 | //#define ImDrawIdx unsigned int 98 | 99 | //---- Override ImDrawCallback signature (will need to modify renderer backends accordingly) 100 | //struct ImDrawList; 101 | //struct ImDrawCmd; 102 | //typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data); 103 | //#define ImDrawCallback MyImDrawCallback 104 | 105 | //---- Debug Tools: Macro to break in Debugger 106 | // (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.) 107 | //#define IM_DEBUG_BREAK IM_ASSERT(0) 108 | //#define IM_DEBUG_BREAK __debugbreak() 109 | 110 | //---- Debug Tools: Have the Item Picker break in the ItemAdd() function instead of ItemHoverable(), 111 | // (which comes earlier in the code, will catch a few extra items, allow picking items other than Hovered one.) 112 | // This adds a small runtime cost which is why it is not enabled by default. 113 | //#define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX 114 | 115 | //---- Debug Tools: Enable slower asserts 116 | //#define IMGUI_DEBUG_PARANOID 117 | 118 | //---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files. 119 | /* 120 | namespace ImGui 121 | { 122 | void MyFunction(const char* name, const MyMatrix44& v); 123 | } 124 | */ 125 | --------------------------------------------------------------------------------