diff --git a/src/Syroot.Worms.sln b/src/Syroot.Worms.sln index d707d86..ad68862 100644 --- a/src/Syroot.Worms.sln +++ b/src/Syroot.Worms.sln @@ -49,13 +49,17 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = " Solution Items", " Solut EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Syroot.Worms.Worms2.GameServer", "tool\Syroot.Worms.Worms2.GameServer\Syroot.Worms.Worms2.GameServer.csproj", "{13ABF717-5809-441D-A5D8-66E1EE75A390}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fkNetcode", "tool\fkNetcode\fkNetcode.vcxproj", "{C4138811-7CFA-4826-A3DD-AF2B618EAFC1}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Module Projects", "Module Projects", "{2A8C6AAA-BA1F-4FB6-A598-114930217B6C}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FrontendKitWS", "tool\FrontendKitWS\FrontendKitWS.vcxproj", "{E391EA12-B929-466C-932F-DEF72B8CEB5D}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WormKitTools", "module\WormKitTools\WormKitTools.vcxproj", "{068A8647-0A66-4E39-983B-43ACEAC5C937}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wkUnlimiter", "tool\wkUnlimiter\wkUnlimiter.vcxproj", "{CDED4B7C-91DF-45D3-9704-DB8750BDAF5B}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FrontendKitWS", "module\FrontendKitWS\FrontendKitWS.vcxproj", "{E391EA12-B929-466C-932F-DEF72B8CEB5D}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WormKitTools", "tool\WormKitTools\FrontendKitLib.vcxproj", "{068A8647-0A66-4E39-983B-43ACEAC5C937}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fkDesPatch", "module\fkDesPatch\fkDesPatch.vcxproj", "{6B4D57EA-4642-440A-AB62-2E011D7B64E1}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fkNetcode", "module\fkNetcode\fkNetcode.vcxproj", "{C4138811-7CFA-4826-A3DD-AF2B618EAFC1}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wkUnlimiter", "module\wkUnlimiter\wkUnlimiter.vcxproj", "{CDED4B7C-91DF-45D3-9704-DB8750BDAF5B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -131,22 +135,26 @@ Global {13ABF717-5809-441D-A5D8-66E1EE75A390}.Debug|Any CPU.Build.0 = Debug|Any CPU {13ABF717-5809-441D-A5D8-66E1EE75A390}.Release|Any CPU.ActiveCfg = Release|Any CPU {13ABF717-5809-441D-A5D8-66E1EE75A390}.Release|Any CPU.Build.0 = Release|Any CPU - {C4138811-7CFA-4826-A3DD-AF2B618EAFC1}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {C4138811-7CFA-4826-A3DD-AF2B618EAFC1}.Debug|Any CPU.Build.0 = Debug|Win32 - {C4138811-7CFA-4826-A3DD-AF2B618EAFC1}.Release|Any CPU.ActiveCfg = Release|Win32 - {C4138811-7CFA-4826-A3DD-AF2B618EAFC1}.Release|Any CPU.Build.0 = Release|Win32 - {E391EA12-B929-466C-932F-DEF72B8CEB5D}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {E391EA12-B929-466C-932F-DEF72B8CEB5D}.Debug|Any CPU.Build.0 = Debug|Win32 - {E391EA12-B929-466C-932F-DEF72B8CEB5D}.Release|Any CPU.ActiveCfg = Release|Win32 - {E391EA12-B929-466C-932F-DEF72B8CEB5D}.Release|Any CPU.Build.0 = Release|Win32 - {CDED4B7C-91DF-45D3-9704-DB8750BDAF5B}.Debug|Any CPU.ActiveCfg = Debug|Win32 - {CDED4B7C-91DF-45D3-9704-DB8750BDAF5B}.Debug|Any CPU.Build.0 = Debug|Win32 - {CDED4B7C-91DF-45D3-9704-DB8750BDAF5B}.Release|Any CPU.ActiveCfg = Release|Win32 - {CDED4B7C-91DF-45D3-9704-DB8750BDAF5B}.Release|Any CPU.Build.0 = Release|Win32 {068A8647-0A66-4E39-983B-43ACEAC5C937}.Debug|Any CPU.ActiveCfg = Debug|Win32 {068A8647-0A66-4E39-983B-43ACEAC5C937}.Debug|Any CPU.Build.0 = Debug|Win32 {068A8647-0A66-4E39-983B-43ACEAC5C937}.Release|Any CPU.ActiveCfg = Release|Win32 {068A8647-0A66-4E39-983B-43ACEAC5C937}.Release|Any CPU.Build.0 = Release|Win32 + {E391EA12-B929-466C-932F-DEF72B8CEB5D}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {E391EA12-B929-466C-932F-DEF72B8CEB5D}.Debug|Any CPU.Build.0 = Debug|Win32 + {E391EA12-B929-466C-932F-DEF72B8CEB5D}.Release|Any CPU.ActiveCfg = Release|Win32 + {E391EA12-B929-466C-932F-DEF72B8CEB5D}.Release|Any CPU.Build.0 = Release|Win32 + {6B4D57EA-4642-440A-AB62-2E011D7B64E1}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {6B4D57EA-4642-440A-AB62-2E011D7B64E1}.Debug|Any CPU.Build.0 = Debug|Win32 + {6B4D57EA-4642-440A-AB62-2E011D7B64E1}.Release|Any CPU.ActiveCfg = Release|Win32 + {6B4D57EA-4642-440A-AB62-2E011D7B64E1}.Release|Any CPU.Build.0 = Release|Win32 + {C4138811-7CFA-4826-A3DD-AF2B618EAFC1}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {C4138811-7CFA-4826-A3DD-AF2B618EAFC1}.Debug|Any CPU.Build.0 = Debug|Win32 + {C4138811-7CFA-4826-A3DD-AF2B618EAFC1}.Release|Any CPU.ActiveCfg = Release|Win32 + {C4138811-7CFA-4826-A3DD-AF2B618EAFC1}.Release|Any CPU.Build.0 = Release|Win32 + {CDED4B7C-91DF-45D3-9704-DB8750BDAF5B}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {CDED4B7C-91DF-45D3-9704-DB8750BDAF5B}.Debug|Any CPU.Build.0 = Debug|Win32 + {CDED4B7C-91DF-45D3-9704-DB8750BDAF5B}.Release|Any CPU.ActiveCfg = Release|Win32 + {CDED4B7C-91DF-45D3-9704-DB8750BDAF5B}.Release|Any CPU.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -169,10 +177,11 @@ Global {212F8090-9775-4098-BD44-9ABC01FBE553} = {99E56312-A064-4AD3-8443-0B56A5F76E6B} {1FAB6B9F-2585-46DC-81C0-579DC808C389} = {0B9B0B74-3EB1-46A4-BCCC-F2D6AE59A9EE} {13ABF717-5809-441D-A5D8-66E1EE75A390} = {0B9B0B74-3EB1-46A4-BCCC-F2D6AE59A9EE} - {C4138811-7CFA-4826-A3DD-AF2B618EAFC1} = {0B9B0B74-3EB1-46A4-BCCC-F2D6AE59A9EE} - {E391EA12-B929-466C-932F-DEF72B8CEB5D} = {0B9B0B74-3EB1-46A4-BCCC-F2D6AE59A9EE} - {CDED4B7C-91DF-45D3-9704-DB8750BDAF5B} = {0B9B0B74-3EB1-46A4-BCCC-F2D6AE59A9EE} - {068A8647-0A66-4E39-983B-43ACEAC5C937} = {0B9B0B74-3EB1-46A4-BCCC-F2D6AE59A9EE} + {068A8647-0A66-4E39-983B-43ACEAC5C937} = {2A8C6AAA-BA1F-4FB6-A598-114930217B6C} + {E391EA12-B929-466C-932F-DEF72B8CEB5D} = {2A8C6AAA-BA1F-4FB6-A598-114930217B6C} + {6B4D57EA-4642-440A-AB62-2E011D7B64E1} = {2A8C6AAA-BA1F-4FB6-A598-114930217B6C} + {C4138811-7CFA-4826-A3DD-AF2B618EAFC1} = {2A8C6AAA-BA1F-4FB6-A598-114930217B6C} + {CDED4B7C-91DF-45D3-9704-DB8750BDAF5B} = {2A8C6AAA-BA1F-4FB6-A598-114930217B6C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {1CD4EDE2-A5FB-4A58-A850-3506AB7E7B69} diff --git a/src/tool/FrontendKitWS/FrontendKitWS.rc b/src/module/FrontendKitWS/FrontendKitWS.rc similarity index 100% rename from src/tool/FrontendKitWS/FrontendKitWS.rc rename to src/module/FrontendKitWS/FrontendKitWS.rc diff --git a/src/tool/FrontendKitWS/FrontendKitWS.vcxproj b/src/module/FrontendKitWS/FrontendKitWS.vcxproj similarity index 100% rename from src/tool/FrontendKitWS/FrontendKitWS.vcxproj rename to src/module/FrontendKitWS/FrontendKitWS.vcxproj diff --git a/src/tool/FrontendKitWS/fk.h b/src/module/FrontendKitWS/fk.h similarity index 100% rename from src/tool/FrontendKitWS/fk.h rename to src/module/FrontendKitWS/fk.h diff --git a/src/tool/FrontendKitWS/main.cpp b/src/module/FrontendKitWS/main.cpp similarity index 100% rename from src/tool/FrontendKitWS/main.cpp rename to src/module/FrontendKitWS/main.cpp diff --git a/src/tool/FrontendKitWS/main.def b/src/module/FrontendKitWS/main.def similarity index 100% rename from src/tool/FrontendKitWS/main.def rename to src/module/FrontendKitWS/main.def diff --git a/src/tool/FrontendKitWS/resource.h b/src/module/FrontendKitWS/resource.h similarity index 100% rename from src/tool/FrontendKitWS/resource.h rename to src/module/FrontendKitWS/resource.h diff --git a/src/tool/WormKitTools/FrontendKitLib.vcxproj b/src/module/WormKitTools/WormKitTools.vcxproj similarity index 95% rename from src/tool/WormKitTools/FrontendKitLib.vcxproj rename to src/module/WormKitTools/WormKitTools.vcxproj index 81f1a0f..941ebec 100644 --- a/src/tool/WormKitTools/FrontendKitLib.vcxproj +++ b/src/module/WormKitTools/WormKitTools.vcxproj @@ -93,7 +93,7 @@ - + @@ -103,13 +103,14 @@ - + + diff --git a/src/tool/WormKitTools/FrontendKitLib.vcxproj.filters b/src/module/WormKitTools/WormKitTools.vcxproj.filters similarity index 89% rename from src/tool/WormKitTools/FrontendKitLib.vcxproj.filters rename to src/module/WormKitTools/WormKitTools.vcxproj.filters index bcecdc6..6ef6fd8 100644 --- a/src/tool/WormKitTools/FrontendKitLib.vcxproj.filters +++ b/src/module/WormKitTools/WormKitTools.vcxproj.filters @@ -18,9 +18,6 @@ Source Files - - Source Files - Source Files @@ -30,14 +27,14 @@ Source Files + + Source Files + Header Files - - Header Files - Header Files @@ -47,10 +44,16 @@ Header Files + + Header Files + Header Files + + Header Files + \ No newline at end of file diff --git a/src/module/WormKitTools/include/wkExe.h b/src/module/WormKitTools/include/wkExe.h new file mode 100644 index 0000000..6d9cf32 --- /dev/null +++ b/src/module/WormKitTools/include/wkExe.h @@ -0,0 +1,88 @@ +#pragma once +#include +#include +#include +#include "wkPatch.h" + +namespace wk +{ + enum JumpType + { + JT_JMP, // jmp (0xE9) + JT_CALL, // call (0xE8) + JT_FARJMP, // farjump (0xEA) + JT_FARCALL, // farcall (0x9A) + JT_PUSHRET, // pushret + }; + + struct Exe + { + public: + // ---- FIELDS ------------------------------------------------------------------------------------------------- + + HANDLE Handle; + PIMAGE_DOS_HEADER DosHeader; + PIMAGE_NT_HEADERS NtHeader; + PIMAGE_FILE_HEADER FileHeader; + PIMAGE_OPTIONAL_HEADER OptHeader; + + // ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------ + + Exe(HMODULE hModule = NULL); + + // ---- METHODS ------------------------------------------------------------------------------------------------ + + /// + /// Returns the offset of the first match of the given signature. It must take the form "12 FF ?? EB 34", where + /// two question marks represent a wildcard. + /// + /// The signature to find. + /// The offset of the first match of the signature. + ULONG_PTR find(std::string signature); + + /// + /// Returns the offset of the first match of the given signature. It is an array of bytes, where all values + /// being negative or bigger than 0xFF represent a wildcard. + /// + /// The signature to find. + /// The offset of the first match of the signature. + ULONG_PTR find(int16_t signature[], size_t signatureSize); // any non-uint8_t value is a wildcard + + /// + /// Writes the given value to the specified offset. + /// + /// The type of the value to write. + /// The relative address at which to write the value. + /// The value to write. + template void set(ULONG_PTR offset, T value); + + /// + /// Replaces the given import with the specified hook, which must have the same signature. + /// + /// The imported method to replace. + /// The method to replace the import with. + void setImp(LPVOID import, LPVOID hook); + + /// + /// Writes a jump to the given callee at the specified offset. + /// + /// The relative address at which to write the value. + /// The number of bytes overwritten for the jump opcode. + /// The called method. + /// The type of the jump to insert. + void setJmp(ULONG_PTR offset, SIZE_T size, LPVOID callee, DWORD jumpType); + + /// + /// Nops out the given number of bytes at the specified offset. + /// + /// The relative address at which to nop out code. + /// The number of bytes to nop out. + void setNop(ULONG_PTR offset, SIZE_T size); + + private: + LPVOID getAddress(ULONG_PTR offset); + PIMAGE_IMPORT_DESCRIPTOR getImports(); + }; +} + +#include "wkExe.inl" \ No newline at end of file diff --git a/src/module/WormKitTools/include/wkExe.inl b/src/module/WormKitTools/include/wkExe.inl new file mode 100644 index 0000000..4f1602e --- /dev/null +++ b/src/module/WormKitTools/include/wkExe.inl @@ -0,0 +1,9 @@ +namespace wk +{ + template + void Exe::set(ULONG_PTR offset, T newValue) + { + Patch patch(getAddress(offset), sizeof(T)); + patch.write(newValue); + } +} \ No newline at end of file diff --git a/src/tool/WormKitTools/include/wkConfig.h b/src/module/WormKitTools/include/wkIni.h similarity index 88% rename from src/tool/WormKitTools/include/wkConfig.h rename to src/module/WormKitTools/include/wkIni.h index d754698..cf8f184 100644 --- a/src/tool/WormKitTools/include/wkConfig.h +++ b/src/module/WormKitTools/include/wkIni.h @@ -3,10 +3,10 @@ namespace wk { - class Config + class Ini { public: - Config(LPCSTR fileName); + Ini(LPCSTR fileName); void get(LPCSTR category, LPCSTR key, BOOL& result, UINT fallback) const; void get(LPCSTR category, LPCSTR key, UINT& result, UINT fallback) const; diff --git a/src/module/WormKitTools/include/wkPatch.h b/src/module/WormKitTools/include/wkPatch.h new file mode 100644 index 0000000..4b7c2b5 --- /dev/null +++ b/src/module/WormKitTools/include/wkPatch.h @@ -0,0 +1,31 @@ +#pragma once +#include + +namespace wk +{ + struct Patch + { + public: + // ---- FIELDS ------------------------------------------------------------------------------------------------- + + ULONG_PTR position; + + // ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------ + + Patch(LPVOID address, SIZE_T size); + ~Patch(); + + // ---- METHODS ------------------------------------------------------------------------------------------------ + + void close() const; + template void write(const T& value); + + private: + // ---- FIELDS ------------------------------------------------------------------------------------------------- + LPBYTE _address; + SIZE_T _size; + DWORD _oldProtect; + }; +} + +#include "wkPatch.inl" diff --git a/src/tool/WormKitTools/include/wkPatch.inl b/src/module/WormKitTools/include/wkPatch.inl similarity index 100% rename from src/tool/WormKitTools/include/wkPatch.inl rename to src/module/WormKitTools/include/wkPatch.inl diff --git a/src/tool/WormKitTools/include/wkUtils.h b/src/module/WormKitTools/include/wkUtils.h similarity index 59% rename from src/tool/WormKitTools/include/wkUtils.h rename to src/module/WormKitTools/include/wkUtils.h index c9d3397..0529e1c 100644 --- a/src/tool/WormKitTools/include/wkUtils.h +++ b/src/module/WormKitTools/include/wkUtils.h @@ -14,21 +14,28 @@ namespace wk GAMEID_W2_1_05 = GAMEID_W2 | 0x010500, GAMEID_W2_1_05_BR = GAMEID_W2_1_05 | 0x10, // Worms2 1.05 Br GAMEID_W2_1_05_EN = GAMEID_W2_1_05 | 0x20, // Worms2 1.05 Du, En, Fr, It, Po, Sp, Sw - GAMEID_W2_1_05_GE = GAMEID_W2_1_05 | 0x30, // Worms2 1.05 - GAMEID_W2_1_05_NA = GAMEID_W2_1_05 | 0x40, // Worms2 1.05 - GAMEID_W2_1_05_SA = GAMEID_W2_1_05 | 0x50, // Worms2 1.05 + GAMEID_W2_1_05_GE = GAMEID_W2_1_05 | 0x30, // Worms2 1.05 De + GAMEID_W2_1_05_NA = GAMEID_W2_1_05 | 0x40, // Worms2 1.05 NA + GAMEID_W2_1_05_SA = GAMEID_W2_1_05 | 0x50, // Worms2 1.05 SA GAMEID_W2_1_07 = GAMEID_W2 | 0x010700, GAMEID_W2_1_07_TRY = GAMEID_W2_1_07 | 0x60, // Worms2 1.07 Trymedia + GAMEID_W2_LAST = GAMEID_W2_1_07_TRY, GAMEID_WA = 0x30000000, + GAMEID_WA_3_6_31 = GAMEID_WA | 0x030630, // Worms Armageddon 3.6.31 + GAMEID_WA_3_7_2_1 = GAMEID_WA | 0x030721, // Worms Armageddon 3.7.2.1 GAMEID_WA_3_8 = GAMEID_WA | 0x030800, GAMEID_WA_3_8_CD = GAMEID_WA_3_8 | 0x10, // Worms Armageddon 3.8 CD + GAMEID_WA_LAST = GAMEID_WA_3_8_CD, GAMEID_WWP = 0x40000000, + GAMEID_WWP_LAST = GAMEID_WWP, GAMEID_OW = 0x50000000, + GAMEID_OW_LAST = GAMEID_OW, GAMEID_WWPA = 0x60000000, + GAMEID_WWPA_LAST = GAMEID_WWPA, }; int getGameID(DWORD timeDateStamp); diff --git a/src/tool/WormKitTools/src/pch.cpp b/src/module/WormKitTools/src/pch.cpp similarity index 100% rename from src/tool/WormKitTools/src/pch.cpp rename to src/module/WormKitTools/src/pch.cpp diff --git a/src/tool/WormKitTools/src/pch.h b/src/module/WormKitTools/src/pch.h similarity index 100% rename from src/tool/WormKitTools/src/pch.h rename to src/module/WormKitTools/src/pch.h diff --git a/src/module/WormKitTools/src/wkExe.cpp b/src/module/WormKitTools/src/wkExe.cpp new file mode 100644 index 0000000..b62320d --- /dev/null +++ b/src/module/WormKitTools/src/wkExe.cpp @@ -0,0 +1,160 @@ +#include "wkExe.h" +#include +#include +#include +#include + +namespace wk +{ + // ---- CONSTRUCTORS & DESTRUCTOR ---------------------------------------------------------------------------------- + + Exe::Exe(HMODULE hModule) + { + Handle = hModule == NULL ? GetModuleHandleA(NULL) : hModule; + DosHeader = (PIMAGE_DOS_HEADER)Handle; + NtHeader = (PIMAGE_NT_HEADERS)((DWORD)DosHeader + DosHeader->e_lfanew); + FileHeader = (PIMAGE_FILE_HEADER)&NtHeader->FileHeader; + OptHeader = (PIMAGE_OPTIONAL_HEADER)&NtHeader->OptionalHeader; + } + + // ---- METHODS (PUBLIC) ------------------------------------------------------------------------------------------- + + ULONG_PTR Exe::find(std::string signature) + { + std::istringstream iss(signature); + std::istream_iterator begin(iss), end; + std::vector tokens(begin, end); + + std::vector sig; + for (auto& token : tokens) + sig.push_back(token == "??" ? -1 : std::stoi(token, 0, 16)); + + return find(&sig[0], sig.size()); + } + + ULONG_PTR Exe::find(int16_t signature[], size_t signatureSize) + { + PIMAGE_SECTION_HEADER sections = IMAGE_FIRST_SECTION(NtHeader); + for (int index = 0; index < FileHeader->NumberOfSections; index++) + { + if (sections[index].Characteristics != (IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ)) + continue; + LPBYTE code = (LPBYTE)(ULONG_PTR)Handle + sections[index].VirtualAddress; + LPBYTE last = &code[sections[index].SizeOfRawData - signatureSize]; + for (; code < last; code++) + { + bool found = true; + for (size_t i = 0; i < signatureSize; i++) + { + if (signature[i] < 0x00 || signature[i] > 0xFF || code[i] == (BYTE)signature[i]) + { + continue; + } + else + { + found = false; + break; + } + } + if (found) + return (ULONG_PTR)code - (ULONG_PTR)Handle; + } + } + throw std::exception("Could not find signature."); + } + + void Exe::setImp(LPVOID import, LPVOID hook) + { + bool success = false; + PIMAGE_IMPORT_DESCRIPTOR imports = getImports(); + if (!imports) + throw std::exception("Import patching failed, import table not found."); + + while (imports->Characteristics) + { + PIMAGE_THUNK_DATA thunk = (PIMAGE_THUNK_DATA)((ULONG_PTR)Handle + imports->OriginalFirstThunk); + PIMAGE_THUNK_DATA table = (PIMAGE_THUNK_DATA)((ULONG_PTR)Handle + imports->FirstThunk); + while (thunk->u1.Ordinal) + { + if (table->u1.Function == (ULONG_PTR)import) + { + Patch patch(&table->u1.Function, sizeof(ULONG_PTR)); + patch.write(hook); + success = true; + } + thunk++; + table++; + } + imports++; + } + + if (!success) + throw std::exception("Import patching failed, import not found."); + } + + void Exe::setJmp(ULONG_PTR offset, SIZE_T size, LPVOID callee, DWORD jumpType) + { + Patch patch(getAddress(offset), size); + + if (size >= 5 && offset) + { + BYTE opSize, opCode; + switch (jumpType) + { + case JT_PUSHRET: opSize = 6; opCode = 0x68; break; + case JT_FARJMP: opSize = 7; opCode = 0xEA; break; + case JT_FARCALL: opSize = 7; opCode = 0x9A; break; + case JT_CALL: opSize = 5; opCode = 0xE8; break; + default: opSize = 5; opCode = 0xE9; break; + } + + if (size < opSize) + throw std::exception("Not enough space to patch opcode."); + + patch.write(opCode); + switch (opSize) + { + case 7: + patch.write((ULONG)callee); + patch.write(0x23); + break; + case 6: + patch.write((ULONG)callee); + patch.write(0xC3); + break; + default: + patch.write((ULONG)callee - (ULONG_PTR)offset - 5); + break; + } + for (DWORD i = opSize; i < size; i++) + patch.write((uint8_t)0x90); + } + } + + void Exe::setNop(ULONG_PTR offset, SIZE_T size) + { + Patch patch(getAddress(offset), size); + while (size--) + patch.write((uint8_t)0x90); + } + + // ---- METHODS (PRIVATE) ------------------------------------------------------------------------------------------ + + LPVOID Exe::getAddress(ULONG_PTR offset) + { + return (LPVOID)((ULONG_PTR)Handle + offset); + } + + PIMAGE_IMPORT_DESCRIPTOR Exe::getImports() + { + if ((FileHeader->SizeOfOptionalHeader + >= FIELD_OFFSET(IMAGE_OPTIONAL_HEADER, DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT + 1])) + && OptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + && OptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size) + { + return (PIMAGE_IMPORT_DESCRIPTOR)((ULONG_PTR)Handle + + OptHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); + } + return NULL; + } +} diff --git a/src/tool/WormKitTools/src/wkConfig.cpp b/src/module/WormKitTools/src/wkIni.cpp similarity index 56% rename from src/tool/WormKitTools/src/wkConfig.cpp rename to src/module/WormKitTools/src/wkIni.cpp index b09888d..26b3388 100644 --- a/src/tool/WormKitTools/src/wkConfig.cpp +++ b/src/module/WormKitTools/src/wkIni.cpp @@ -1,38 +1,38 @@ -#include "wkConfig.h" +#include "wkIni.h" #include namespace wk { - Config::Config(LPCSTR fileName) + Ini::Ini(LPCSTR fileName) { GetModuleFileName(NULL, _filePath, MAX_PATH); char* sepIdx = strrchr(_filePath, '\\') + 1; strcpy_s(sepIdx, MAX_PATH - (int)(sepIdx - _filePath), fileName); } - void Config::get(LPCSTR category, LPCSTR key, BOOL& result, UINT fallback) const + void Ini::get(LPCSTR category, LPCSTR key, BOOL& result, UINT fallback) const { result = GetPrivateProfileInt(category, key, fallback, _filePath); } - void Config::get(LPCSTR category, LPCSTR key, UINT& result, UINT fallback) const + void Ini::get(LPCSTR category, LPCSTR key, UINT& result, UINT fallback) const { result = GetPrivateProfileInt(category, key, fallback, _filePath); } - void Config::get(LPCSTR category, LPCSTR key, LPSTR result, INT resultLength, LPCSTR fallback) const + void Ini::get(LPCSTR category, LPCSTR key, LPSTR result, INT resultLength, LPCSTR fallback) const { GetPrivateProfileString(category, key, fallback, result, resultLength, _filePath); } - void Config::set(LPCSTR category, LPCSTR key, UINT value) const + void Ini::set(LPCSTR category, LPCSTR key, UINT value) const { CHAR buffer[32]; sprintf_s(buffer, "%d", value); WritePrivateProfileString(category, key, buffer, _filePath); } - void Config::set(LPCSTR category, LPCSTR key, LPCSTR value) const + void Ini::set(LPCSTR category, LPCSTR key, LPCSTR value) const { WritePrivateProfileString(category, key, value, _filePath); } diff --git a/src/module/WormKitTools/src/wkPatch.cpp b/src/module/WormKitTools/src/wkPatch.cpp new file mode 100644 index 0000000..4aff3fd --- /dev/null +++ b/src/module/WormKitTools/src/wkPatch.cpp @@ -0,0 +1,32 @@ +#include "wkPatch.h" +#include + +namespace wk +{ + // ---- CONSTRUCTORS & DESTRUCTOR ---------------------------------------------------------------------------------- + + Patch::Patch(LPVOID address, SIZE_T size) + : _address((LPBYTE)address) + , _size(size) + , position(0) + { + if (!_address || !_size) + throw std::invalid_argument("Address and size must not be 0."); + if (!VirtualProtect(_address, _size, PAGE_EXECUTE_READWRITE, &_oldProtect)) + throw std::exception("VirtualProtect failed, call GetLastError for more info."); + } + + Patch::~Patch() + { + close(); + } + + // ---- METHODS (PUBLIC) ------------------------------------------------------------------------------------------- + + void Patch::close() const + { + DWORD oldProtect; + if (!VirtualProtect(_address, _size, _oldProtect, &oldProtect)) + throw std::exception("VirtualProtect failed, call GetLastError for more info."); + } +}; diff --git a/src/tool/WormKitTools/src/wkUtils.cpp b/src/module/WormKitTools/src/wkUtils.cpp similarity index 88% rename from src/tool/WormKitTools/src/wkUtils.cpp rename to src/module/WormKitTools/src/wkUtils.cpp index 44036ee..5efd6cc 100644 --- a/src/tool/WormKitTools/src/wkUtils.cpp +++ b/src/module/WormKitTools/src/wkUtils.cpp @@ -14,6 +14,8 @@ namespace wk case 0x3528DA98: return GAMEID_W2_1_05_NA; case 0x3528DBDA: return GAMEID_W2_1_05_SA; case 0x3587BE19: return GAMEID_W2_1_07_TRY; + case 0x4CE25091: return GAMEID_WA_3_6_31; + case 0x513D83BC: return GAMEID_WA_3_7_2_1; case 0x5EF04515: return GAMEID_WA_3_8_CD; default: return GAMEID_NONE; } diff --git a/src/module/fkDesPatch/fkDesPatch.vcxproj b/src/module/fkDesPatch/fkDesPatch.vcxproj new file mode 100644 index 0000000..c1dc506 --- /dev/null +++ b/src/module/fkDesPatch/fkDesPatch.vcxproj @@ -0,0 +1,99 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + 16.0 + Win32Proj + {6b4d57ea-4642-440a-ab62-2e011d7b64e1} + fkDesPatch + 10.0 + + + + DynamicLibrary + true + v142 + MultiByte + + + DynamicLibrary + false + v142 + true + MultiByte + + + + + + + + + + + + + + + true + bin\$(Configuration)\ + obj\$(Configuration)\ + + + false + bin\$(Configuration)\ + obj\$(Configuration)\ + + + + Level3 + true + WIN32;_DEBUG;FKDESPATCH_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + $(ProjectDir)..\WormKitTools\include\;%(AdditionalIncludeDirectories) + + + Windows + true + false + + + + + Level3 + true + true + true + WIN32;NDEBUG;FKDESPATCH_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + $(ProjectDir)..\WormKitTools\include\;%(AdditionalIncludeDirectories) + + + Windows + true + true + true + false + + + + + + + + {068a8647-0a66-4e39-983b-43aceac5c937} + + + + + + \ No newline at end of file diff --git a/src/module/fkDesPatch/main.cpp b/src/module/fkDesPatch/main.cpp new file mode 100644 index 0000000..038c848 --- /dev/null +++ b/src/module/fkDesPatch/main.cpp @@ -0,0 +1,84 @@ +#define WIN32_LEAN_AND_MEAN +#include +#include +#include "wkExe.h" +#include "wkIni.h" +#include "wkUtils.h" + +// ---- Initialization ---- + +BOOL iniAutoOssett; + +void init() +{ + wk::Ini ini("fkDesPatch.ini"); + + // Load INI settings. + ini.get("Frontend", "AutoOssett", iniAutoOssett, FALSE); + + // Ensure INI file has been created with default setting. + ini.set("Frontend", "AutoOssett", iniAutoOssett); +} + +// ---- Patch ---- + +void patch(wk::Exe& exe, int gameVersion) +{ + if (gameVersion == wk::GAMEID_W2_1_07_TRY) + { + + } + else + { + // getAddress file to code 0x400C02 + if (iniAutoOssett) + exe.set(0x000446A2, (uint8_t)0xEB); + } +} + +// ---- Main ---- + +int getVersion(DWORD timeDateStamp) +{ + int id = wk::getGameID(timeDateStamp); + switch (id) + { + case wk::GAMEID_W2_1_05_BR: + case wk::GAMEID_W2_1_05_EN: + case wk::GAMEID_W2_1_05_GE: + case wk::GAMEID_W2_1_05_NA: + case wk::GAMEID_W2_1_05_SA: + case wk::GAMEID_W2_1_07_TRY: + return id; + default: + return 0; + } +} + +BOOL WINAPI DllMain(HMODULE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + { + wk::Exe exe; + int version = getVersion(exe.FileHeader->TimeDateStamp); + if (version) + { + init(); + patch(exe, version); + } + else + { + MessageBox(NULL, "fkDesPatch is incompatible with your game version. Please run the 1.05 patch or 1.07 " + "release of Worms 2. Otherwise, you can delete the module to remove this warning.", "fkDesPatch", + MB_ICONWARNING); + } + } + break; + + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} diff --git a/src/tool/fkNetcode/fkNetcode.rc b/src/module/fkNetcode/fkNetcode.rc similarity index 100% rename from src/tool/fkNetcode/fkNetcode.rc rename to src/module/fkNetcode/fkNetcode.rc diff --git a/src/tool/fkNetcode/fkNetcode.vcxproj b/src/module/fkNetcode/fkNetcode.vcxproj similarity index 96% rename from src/tool/fkNetcode/fkNetcode.vcxproj rename to src/module/fkNetcode/fkNetcode.vcxproj index 19ae316..792f9ae 100644 --- a/src/tool/fkNetcode/fkNetcode.vcxproj +++ b/src/module/fkNetcode/fkNetcode.vcxproj @@ -98,7 +98,7 @@ - + {068a8647-0a66-4e39-983b-43aceac5c937} diff --git a/src/tool/fkNetcode/main.cpp b/src/module/fkNetcode/main.cpp similarity index 67% rename from src/tool/fkNetcode/main.cpp rename to src/module/fkNetcode/main.cpp index ecd949d..e5e004f 100644 --- a/src/tool/fkNetcode/main.cpp +++ b/src/module/fkNetcode/main.cpp @@ -2,36 +2,36 @@ #include #include #include -#include "wkConfig.h" +#include "wkIni.h" #include "wkExe.h" #include "wkPatch.h" #include "wkUtils.h" -// ---- Configuration ---- +// ---- Initialization ---- -CHAR cfgFallbackIP[16]; -CHAR cfgServiceUrl[MAX_PATH]; -BOOL cfgShowErrors; +CHAR iniFallbackIP[16]; +CHAR iniServiceUrl[MAX_PATH]; +BOOL iniShowErrors; -void configure() +void init() { - wk::Config config("fkNetcode.ini"); + wk::Ini ini("fkNetcode.ini"); // Load INI settings. - config.get("AddressResolval", "FallbackIP", cfgFallbackIP, 16); - config.get("AddressResolval", "ServiceUrl", cfgServiceUrl, MAX_PATH, "http://ip.syroot.com"); - config.get("AddressResolval", "ShowErrors", cfgShowErrors, TRUE); + ini.get("AddressResolval", "FallbackIP", iniFallbackIP, 16); + ini.get("AddressResolval", "ServiceUrl", iniServiceUrl, MAX_PATH, "http://ip.syroot.com"); + ini.get("AddressResolval", "ShowErrors", iniShowErrors, TRUE); // Ensure INI file has been created with default setting. - config.set("AddressResolval", "FallbackIP", cfgFallbackIP); - config.set("AddressResolval", "ServiceUrl", cfgServiceUrl); - config.set("AddressResolval", "ShowErrors", cfgShowErrors); + ini.set("AddressResolval", "FallbackIP", iniFallbackIP); + ini.set("AddressResolval", "ServiceUrl", iniServiceUrl); + ini.set("AddressResolval", "ShowErrors", iniShowErrors); // Validate fallback IP. BYTE b; - if (*cfgFallbackIP && sscanf_s(cfgFallbackIP, "%hhu.%hhu.%hhu.%hhu", &b, &b, &b, &b) != 4) + if (*iniFallbackIP && sscanf_s(iniFallbackIP, "%hhu.%hhu.%hhu.%hhu", &b, &b, &b, &b) != 4) { - *cfgFallbackIP = NULL; + *iniFallbackIP = NULL; MessageBox(NULL, "Invalid fallback IP setting in fkNetcode.ini has been ignored.", "fkNetcode", MB_ICONWARNING); } } @@ -50,14 +50,14 @@ bool resolveIPCached(LPSTR buffer) bool resolveIPExternal(LPSTR buffer) { - if (!*cfgServiceUrl) + if (!*iniServiceUrl) return false; // Query a web service which replies with the IP in plain text. HINTERNET hInternet = 0, hFile = 0; if (hInternet = InternetOpen(NULL, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0)) { - if (hFile = InternetOpenUrl(hInternet, cfgServiceUrl, NULL, 0, + if (hFile = InternetOpenUrl(hInternet, iniServiceUrl, NULL, 0, INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_RELOAD, NULL)) { DWORD responseLength = 0; @@ -84,7 +84,7 @@ bool resolveIPExternal(LPSTR buffer) DWORD error = GetLastError(); if (hFile) InternetCloseHandle(hFile); if (hInternet) InternetCloseHandle(hInternet); - if (error && cfgShowErrors) + if (error && iniShowErrors) { CHAR msg[512]; sprintf_s(msg, "Could not resolve your IP through the web service. %s", wk::getErrorMessage(error).c_str()); @@ -95,9 +95,9 @@ bool resolveIPExternal(LPSTR buffer) bool resolveIPFallback(LPSTR buffer) { - if (!*cfgFallbackIP) + if (!*iniFallbackIP) return false; - lstrcpy(buffer, cfgFallbackIP); + lstrcpy(buffer, iniFallbackIP); return true; } @@ -136,19 +136,19 @@ bool __stdcall patchResolveIP(LPSTR buffer, int bufferLength) void patch(wk::Exe& exe, int gameVersion) { - wk::Patch::jump(exe.Offset(0x00001799), 5, &patchResolveIP, wk::IJ_JUMP); // replace IP resolve with web service + exe.setJmp(0x00001799, 5, &patchResolveIP, wk::JT_JMP); // replace IP resolve with web service if (gameVersion == wk::GAMEID_W2_1_07_TRY) { - wk::Patch::nops(exe.Offset(0x00053B96), 5); // prevent overriding IP with user name - wk::Patch::nops(exe.Offset(0x00054693), 5); // prevent overriding IP with NAT IP - wk::Patch::nops(exe.Offset(0x00054635), 11); // useless sleep when connecting to server + exe.setNop(0x00053B96, 5); // prevent overriding IP with user name + exe.setNop(0x00054693, 5); // prevent overriding IP with NAT IP + exe.setNop(0x00054635, 11); // useless sleep when connecting to server } else { - wk::Patch::nops(exe.Offset(0x00053E96), 5); // prevent overriding IP with user name - wk::Patch::nops(exe.Offset(0x00054935), 11); // useless sleep when connecting to server - wk::Patch::nops(exe.Offset(0x00054993), 5); // prevent overriding IP with NAT IP + exe.setNop(0x00053E96, 5); // prevent overriding IP with user name + exe.setNop(0x00054935, 11); // useless sleep when connecting to server + exe.setNop(0x00054993, 5); // prevent overriding IP with NAT IP } } @@ -181,7 +181,7 @@ BOOL WINAPI DllMain(HMODULE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) int version = getVersion(exe.FileHeader->TimeDateStamp); if (version) { - configure(); + init(); patch(exe, version); } else diff --git a/src/tool/fkNetcode/resource.h b/src/module/fkNetcode/resource.h similarity index 100% rename from src/tool/fkNetcode/resource.h rename to src/module/fkNetcode/resource.h diff --git a/src/module/wkUnlimiter/main.cpp b/src/module/wkUnlimiter/main.cpp new file mode 100644 index 0000000..dd41cf4 --- /dev/null +++ b/src/module/wkUnlimiter/main.cpp @@ -0,0 +1,132 @@ +#define WIN32_LEAN_AND_MEAN +#include +#include +#include "wkConfig.h" +#include "wkExe.h" +#include "wkUtils.h" + +// ---- Initialization ---- + +BOOL iniAllowMultiInstance; +BOOL iniEnableAllControls; +BOOL iniShowAllControls; +BOOL iniUnlockCamera; +BOOL iniUnlockCursor; + +void init(int gameID) +{ + wk::Ini ini("wkUnlimiter.ini"); + + // Load INI settings. + ini.get("Common", "AllowMultiInstance", iniAllowMultiInstance, TRUE); + ini.get("Frontend", "EnableAllControls", iniEnableAllControls, TRUE); + ini.get("Frontend", "ShowAllControls", iniShowAllControls, FALSE); + if (gameID == wk::GAMEID_WA_3_8_CD) + { + ini.get("Game", "UnlockCamera", iniUnlockCamera, TRUE); + ini.get("Game", "UnlockCursor", iniUnlockCursor, TRUE); + } + + // Ensure INI file has been created with default setting. + ini.set("Common", "AllowMultiInstance", iniAllowMultiInstance); + ini.set("Frontend", "EnableAllControls", iniEnableAllControls); + ini.set("Frontend", "ShowAllControls", iniShowAllControls); + if (gameID == wk::GAMEID_WA_3_8_CD) + { + ini.set("Game", "UnlockCamera", iniUnlockCamera); + ini.set("Game", "UnlockCursor", iniUnlockCursor); + } +} + +// ---- Patch ---- + +HANDLE WINAPI Kernel32_CreateSemaphoreA(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, + LONG lMaximumCount, LPCSTR lpName) +{ + HANDLE handle = CreateSemaphoreA(lpSemaphoreAttributes, lInitialCount, lMaximumCount, lpName); + SetLastError(ERROR_SUCCESS); + return handle; +} + +void patch(wk::Exe& exe, int gameID) +{ + if (iniAllowMultiInstance) + { + // Requires WormKitDS as game loads modules after this + exe.setImp(CreateSemaphoreA, Kernel32_CreateSemaphoreA); + } + if (iniEnableAllControls) + { + // CWnd::EnableWindow(bEnable) -> CWnd::EnableWindow(TRUE) + exe.set(exe.find("C2 04 00 8B 49 50 8B 01 FF A0 A8") - 13, 0x9090016A); + } + if (iniShowAllControls) + { + // CWnd::ShowWindow(bShow) -> CWnd::ShowWindow(TRUE) + exe.set(exe.find("C2 04 00 8B 49 50 8B 01 FF A0 A0") - 13, 0x9090016A); + } + if (iniUnlockCamera) + { + exe.setNop(exe.find("89 13 EB 06 3B C7 7E 02"), 2); // X- axis + exe.setNop(exe.find("89 3B 8B 43 04 3B C1 7D 09 5F 5E"), 2); // X+ axis + exe.setNop(exe.find("89 4B 04 5B C2 14 00 3B C6 7E 03"), 3); // Y- axis + exe.setNop(exe.find("89 73 04 5F 5E 5B C2 14 00"), 3); // Y+ axis + } + if (iniUnlockCursor) + { + exe.setNop(0x00159A72, 6); // X- axis 1 + exe.setNop(0x00159A8F, 6); // X+ axis 1 + exe.setNop(0x00159AE5, 6); // Y+ axis 1 + exe.setNop(0x00159B00, 10); // Y- axis 1 cave + exe.setNop(0x00159B19, 6); // Y- axis 1 island + exe.setNop(0x00159EAD, 6); // X- axis 2 + exe.setNop(0x00159ECA, 6); // X+ axis 2 + exe.setNop(0x00159EE7, 6); // Y+ axis 2 + exe.setNop(0x00159F02, 10); // Y- axis 2 cave + exe.setNop(0x00159F1B, 6); // Y- axis 2 island + } +} + +// ---- Main ---- + +int getVersion(DWORD timeDateStamp) +{ + int id = wk::getGameID(timeDateStamp); + switch (id) + { + case wk::GAMEID_WA_3_6_31: + case wk::GAMEID_WA_3_7_2_1: + case wk::GAMEID_WA_3_8_CD: + return id; + default: + return 0; + } +} + +BOOL WINAPI DllMain(HMODULE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + { + wk::Exe exe; + int version = getVersion(exe.FileHeader->TimeDateStamp); + if (version) + { + init(version); + patch(exe, version); + } + else + { + MessageBox(NULL, "wkUnlimiter is incompatible with your game version. Please run the 3.6.31.0, " + "3.7.2.1, or 3.8 CD release of Worms Armageddon. Otherwise, you can delete the module to remove " + "this warning.", "wkUnlimiter", MB_ICONWARNING); + } + } + break; + + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} diff --git a/src/tool/wkUnlimiter/wkConfig.cpp b/src/module/wkUnlimiter/wkConfig.cpp similarity index 58% rename from src/tool/wkUnlimiter/wkConfig.cpp rename to src/module/wkUnlimiter/wkConfig.cpp index b09888d..522ce1b 100644 --- a/src/tool/wkUnlimiter/wkConfig.cpp +++ b/src/module/wkUnlimiter/wkConfig.cpp @@ -3,36 +3,36 @@ namespace wk { - Config::Config(LPCSTR fileName) + Ini::Ini(LPCSTR fileName) { GetModuleFileName(NULL, _filePath, MAX_PATH); char* sepIdx = strrchr(_filePath, '\\') + 1; strcpy_s(sepIdx, MAX_PATH - (int)(sepIdx - _filePath), fileName); } - void Config::get(LPCSTR category, LPCSTR key, BOOL& result, UINT fallback) const + void Ini::get(LPCSTR category, LPCSTR key, BOOL& result, UINT fallback) const { result = GetPrivateProfileInt(category, key, fallback, _filePath); } - void Config::get(LPCSTR category, LPCSTR key, UINT& result, UINT fallback) const + void Ini::get(LPCSTR category, LPCSTR key, UINT& result, UINT fallback) const { result = GetPrivateProfileInt(category, key, fallback, _filePath); } - void Config::get(LPCSTR category, LPCSTR key, LPSTR result, INT resultLength, LPCSTR fallback) const + void Ini::get(LPCSTR category, LPCSTR key, LPSTR result, INT resultLength, LPCSTR fallback) const { GetPrivateProfileString(category, key, fallback, result, resultLength, _filePath); } - void Config::set(LPCSTR category, LPCSTR key, UINT value) const + void Ini::set(LPCSTR category, LPCSTR key, UINT value) const { CHAR buffer[32]; sprintf_s(buffer, "%d", value); WritePrivateProfileString(category, key, buffer, _filePath); } - void Config::set(LPCSTR category, LPCSTR key, LPCSTR value) const + void Ini::set(LPCSTR category, LPCSTR key, LPCSTR value) const { WritePrivateProfileString(category, key, value, _filePath); } diff --git a/src/tool/wkUnlimiter/wkConfig.h b/src/module/wkUnlimiter/wkConfig.h similarity index 88% rename from src/tool/wkUnlimiter/wkConfig.h rename to src/module/wkUnlimiter/wkConfig.h index d754698..cf8f184 100644 --- a/src/tool/wkUnlimiter/wkConfig.h +++ b/src/module/wkUnlimiter/wkConfig.h @@ -3,10 +3,10 @@ namespace wk { - class Config + class Ini { public: - Config(LPCSTR fileName); + Ini(LPCSTR fileName); void get(LPCSTR category, LPCSTR key, BOOL& result, UINT fallback) const; void get(LPCSTR category, LPCSTR key, UINT& result, UINT fallback) const; diff --git a/src/tool/wkUnlimiter/wkUnlimiter.vcxproj b/src/module/wkUnlimiter/wkUnlimiter.vcxproj similarity index 96% rename from src/tool/wkUnlimiter/wkUnlimiter.vcxproj rename to src/module/wkUnlimiter/wkUnlimiter.vcxproj index 5e6f35e..db3e9b6 100644 --- a/src/tool/wkUnlimiter/wkUnlimiter.vcxproj +++ b/src/module/wkUnlimiter/wkUnlimiter.vcxproj @@ -93,7 +93,7 @@ - + {068a8647-0a66-4e39-983b-43aceac5c937} diff --git a/src/tool/wkUnlimiter/wkUnlimiter.vcxproj.filters b/src/module/wkUnlimiter/wkUnlimiter.vcxproj.filters similarity index 100% rename from src/tool/wkUnlimiter/wkUnlimiter.vcxproj.filters rename to src/module/wkUnlimiter/wkUnlimiter.vcxproj.filters diff --git a/src/tool/WormKitTools/include/wkExe.h b/src/tool/WormKitTools/include/wkExe.h deleted file mode 100644 index 8da2e3c..0000000 --- a/src/tool/WormKitTools/include/wkExe.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include - -namespace wk -{ - struct Exe - { - public: - HANDLE Handle; - IMAGE_DOS_HEADER* DosHeader; - IMAGE_NT_HEADERS* NtHeader; - IMAGE_FILE_HEADER* FileHeader; - IMAGE_OPTIONAL_HEADER* OptionalHeader; - - Exe(HMODULE hModule = NULL); - - ULONG_PTR Offset(ULONG_PTR off); - BOOL IsCode(LPVOID ptr); - BOOL IsData(LPVOID ptr); - }; -} diff --git a/src/tool/WormKitTools/include/wkPatch.h b/src/tool/WormKitTools/include/wkPatch.h deleted file mode 100644 index 64ee0bb..0000000 --- a/src/tool/WormKitTools/include/wkPatch.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once -#include - -namespace wk -{ - enum InsertJump - { - IJ_JUMP, // Insert a jump (0xE9) with patchJump - IJ_CALL, // Insert a call (0xE8) with patchJump - IJ_FARJUMP, // Insert a farjump (0xEA) with patchJump - IJ_FARCALL, // Insert a farcall (0x9A) with patchJump - IJ_PUSHRET, // Insert a pushret with patchJump - }; - - struct Patch - { - public: - ULONG_PTR position; - - Patch(ULONG_PTR address, SIZE_T size); - ~Patch(); - - void close() const; - template void write(const T& value); - - static void byte(ULONG_PTR address, BYTE newValue); - static void nops(ULONG_PTR address, SIZE_T size); - static void jump(ULONG_PTR address, SIZE_T size, PVOID callee, DWORD jumpType); - - private: - LPBYTE _address; - SIZE_T _size; - DWORD _oldProtect; - }; -} - -#include "wkPatch.inl" diff --git a/src/tool/WormKitTools/src/wkExe.cpp b/src/tool/WormKitTools/src/wkExe.cpp deleted file mode 100644 index cadd545..0000000 --- a/src/tool/WormKitTools/src/wkExe.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "wkExe.h" - -namespace wk -{ - Exe::Exe(HMODULE hModule) - { - Handle = hModule == 0 ? GetModuleHandleA(NULL) : hModule; - DosHeader = (IMAGE_DOS_HEADER*)Handle; - NtHeader = (IMAGE_NT_HEADERS*)((DWORD)DosHeader + DosHeader->e_lfanew); - FileHeader = (IMAGE_FILE_HEADER*)&NtHeader->FileHeader; - OptionalHeader = (IMAGE_OPTIONAL_HEADER*)&NtHeader->OptionalHeader; - } - - ULONG_PTR Exe::Offset(ULONG_PTR off) - { - return (DWORD)Handle + off; - } - - BOOL Exe::IsCode(LPVOID ptr) - { - return DWORD(ptr) >= Offset(OptionalHeader->BaseOfCode) - && DWORD(ptr) < Offset(OptionalHeader->BaseOfCode) + OptionalHeader->SizeOfCode; - } - - BOOL Exe::IsData(LPVOID ptr) - { - return DWORD(ptr) >= Offset(OptionalHeader->BaseOfData) - && DWORD(ptr) < Offset(OptionalHeader->BaseOfData) + OptionalHeader->SizeOfInitializedData + OptionalHeader->SizeOfUninitializedData; - } -} diff --git a/src/tool/WormKitTools/src/wkPatch.cpp b/src/tool/WormKitTools/src/wkPatch.cpp deleted file mode 100644 index da67642..0000000 --- a/src/tool/WormKitTools/src/wkPatch.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include "wkPatch.h" -#include - -namespace wk -{ - Patch::Patch(ULONG_PTR address, SIZE_T size) - : _address(reinterpret_cast(address)) - , _size(size) - , position(0) - { - if (!_address || !_size) - throw std::invalid_argument("Address and size must not be 0."); - if (!VirtualProtect(_address, _size, PAGE_EXECUTE_READWRITE, &_oldProtect)) - throw std::exception("VirtualProtect failed, call GetLastError for more info."); - } - - Patch::~Patch() - { - close(); - } - - void Patch::close() const - { - DWORD oldProtect; - if (!VirtualProtect(_address, _size, _oldProtect, &oldProtect)) - throw std::exception("VirtualProtect failed, call GetLastError for more info."); - } - - void Patch::byte(ULONG_PTR address, BYTE newValue) - { - wk::Patch patch(address, 1); - patch.write(newValue); - } - - void Patch::nops(ULONG_PTR address, SIZE_T size) - { - wk::Patch patch(address, size); - while (size--) - patch.write(0x90); - } - - void Patch::jump(ULONG_PTR address, SIZE_T size, PVOID callee, DWORD jumpType) - { - wk::Patch patch(address, size); - - if (size >= 5 && address) - { - BYTE opSize, opCode; - switch (jumpType) - { - case IJ_PUSHRET: opSize = 6; opCode = 0x68; break; - case IJ_FARJUMP: opSize = 7; opCode = 0xEA; break; - case IJ_FARCALL: opSize = 7; opCode = 0x9A; break; - case IJ_CALL: opSize = 5; opCode = 0xE8; break; - default: opSize = 5; opCode = 0xE9; break; - } - - if (size < opSize) - throw std::exception("Not enough space to patch opcode."); - - patch.write(opCode); - switch (opSize) - { - case 7: - patch.write((ULONG)callee); - patch.write(0x23); - break; - case 6: - patch.write((ULONG)callee); - patch.write(0xC3); - break; - default: - patch.write((ULONG)callee - address - 5); - break; - } - for (DWORD i = opSize; i < size; i++) - patch.write(0x90); - } - } -}; diff --git a/src/tool/wkUnlimiter/main.cpp b/src/tool/wkUnlimiter/main.cpp deleted file mode 100644 index f826c5c..0000000 --- a/src/tool/wkUnlimiter/main.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#define WIN32_LEAN_AND_MEAN -#include -#include "wkConfig.h" -#include "wkExe.h" -#include "wkPatch.h" -#include "wkUtils.h" - -// ---- Configuration ---- - -BOOL cfgAllowMultiInstance; -BOOL cfgEnableAllControls; -BOOL cfgUnlockCamera; -BOOL cfgUnlockCursor; - -void configure() -{ - wk::Config config("wkUnlimiter.ini"); - - // Load INI settings. - config.get("Common", "AllowMultiInstance", cfgAllowMultiInstance, TRUE); - config.get("Frontend", "EnableAllControls", cfgEnableAllControls, TRUE); - config.get("Game", "UnlockCamera", cfgUnlockCamera, TRUE); - config.get("Game", "UnlockCursor", cfgUnlockCursor, TRUE); - - // Ensure INI file has been created with default setting. - config.set("Common", "AllowMultiInstance", cfgAllowMultiInstance); - config.set("Frontend", "EnableAllControls", cfgEnableAllControls); - config.set("Game", "UnlockCamera", cfgUnlockCamera); - config.set("Game", "UnlockCursor", cfgUnlockCursor); -} - -// ---- Patch ---- - -void patch(wk::Exe& exe, int gameVersion) -{ - if (cfgAllowMultiInstance) - { - wk::Patch patch(exe.Offset(0x000E7AD1), 1); - patch.write((char)0xEB); // Requires WormKitDS as game executable loads modules after this patched out check. - } - if (cfgEnableAllControls) - { - wk::Patch patch(exe.Offset(0x001C5D26), 4); - patch.write(0x9090016A); // CWnd::EnableWindow(bEnable) -> CWnd::EnableWindow(TRUE) - } - if (cfgUnlockCamera) - { - wk::Patch::nops(exe.Offset(0x00142A89), 2); // X- axis - wk::Patch::nops(exe.Offset(0x00142A91), 2); // X+ axis - wk::Patch::nops(exe.Offset(0x00142AA7), 3); // Y- axis - wk::Patch::nops(exe.Offset(0x00142A9C), 3); // Y+ axis - } - if (cfgUnlockCursor) - { - wk::Patch::nops(exe.Offset(0x00159EAD), 6); // X- axis 1 - wk::Patch::nops(exe.Offset(0x00159A72), 6); // X- axis 2 - wk::Patch::nops(exe.Offset(0x00159ECA), 6); // X+ axis 1 - wk::Patch::nops(exe.Offset(0x00159A8F), 6); // X+ axis 2 - wk::Patch::nops(exe.Offset(0x00159B00), 10); // Y- axis 1 cave - wk::Patch::nops(exe.Offset(0x00159F02), 10); // Y- axis 2 cave - wk::Patch::nops(exe.Offset(0x00159B19), 6); // Y- axis 1 island - wk::Patch::nops(exe.Offset(0x00159F1B), 6); // Y- axis 2 island - wk::Patch::nops(exe.Offset(0x00159AE5), 6); // Y+ axis 1 - wk::Patch::nops(exe.Offset(0x00159EE7), 6); // Y+ axis 2 - } -} - -// ---- Main ---- - -int getVersion(DWORD timeDateStamp) -{ - int id = wk::getGameID(timeDateStamp); - switch (id) - { - case wk::GAMEID_WA_3_8_CD: - return id; - default: - return 0; - } -} - -BOOL WINAPI DllMain(HMODULE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) -{ - switch (fdwReason) - { - case DLL_PROCESS_ATTACH: - { - wk::Exe exe; - int version = getVersion(exe.FileHeader->TimeDateStamp); - if (version) - { - configure(); - patch(exe, version); - } - else - { - MessageBox(NULL, "wkUnlimiter is incompatible with your game version. Please run the 3.8 CD release of " - "Worms Armageddon. Otherwise, you can delete the module to remove this warning.", "wkUnlimiter", - MB_ICONWARNING); - } - } - break; - - case DLL_PROCESS_DETACH: - break; - } - return TRUE; -}