From eaae55b668c493c103982d84edf2f603ae75f7d3 Mon Sep 17 00:00:00 2001 From: A1mDev <33463136+A1mDev@users.noreply.github.com> Date: Sat, 4 Oct 2025 15:07:57 +0700 Subject: [PATCH 1/5] Add check for redundant patch bytes and log warning - Added std::equal comparison to detect when patch bytes already match the binary. - Logs a clear message to the user indicating the patch is redundant, suggesting to verify patch data or check for conflicts with other plugins. --- types/mempatch.cpp | 21 ++++++++++++++++++++- userconf/mempatches.cpp | 4 ++++ userconf/mempatches.h | 1 + 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/types/mempatch.cpp b/types/mempatch.cpp index 5e7c5f7..658284c 100644 --- a/types/mempatch.cpp +++ b/types/mempatch.cpp @@ -29,15 +29,18 @@ class MemoryPatch { for (auto bit : info.vecPatch) { this->vecPatch.push_back(bit); } + for (auto bit : info.vecVerify) { this->vecVerify.push_back(bit); } + for (auto bit : info.vecPreserve) { this->vecPreserve.push_back(bit); } // ignore offset if address is bad this->pAddress = pAddress ? pAddress + (info.offset) : 0; + strPatchName = info.patchName; } bool Enable() { @@ -49,6 +52,19 @@ class MemoryPatch { if (!this->Verify()) { return false; } + + if (std::equal(vecPatch.begin(), vecPatch.end(), (uint8_t*) pAddress)) { + // The bytes in the binary file already match the patch, + // we inform the user that his patch is redundant, + // we tell them to check the patch. + // In this case, as a rule, section `verify` is missing. + smutils->LogError(myself, + "Patch '%s' is redundant. Binary already contains the patch bytes. " + "Check patch data or perhaps it was applied by a different plugin?", + strPatchName.c_str() + ); + } + ByteVectorRead(vecRestore, (uint8_t*) pAddress, vecPatch.size()); SourceHook::SetMemAccess((void*) this->pAddress, vecPatch.size() * sizeof(uint8_t), @@ -71,6 +87,7 @@ class MemoryPatch { // no memory to restore, fug return; } + ByteVectorWrite(vecRestore, (uint8_t*) pAddress); vecRestore.clear(); } @@ -94,10 +111,12 @@ class MemoryPatch { } return true; } + ~MemoryPatch() { this->Disable(); } - + + std::string strPatchName; // For error messages uintptr_t pAddress; ByteVector vecPatch, vecRestore, vecVerify, vecPreserve; }; diff --git a/userconf/mempatches.cpp b/userconf/mempatches.cpp index 1a2a451..99c207d 100644 --- a/userconf/mempatches.cpp +++ b/userconf/mempatches.cpp @@ -235,6 +235,10 @@ SMCResult MemPatchGameConfig::ReadSMC_LeavingSection(const SMCStates *states) { case PState_Runtime: // pop section info g_ParseState = PState_Root; + + // save data for error messages + g_CurrentPatchInfo->patchName = g_CurrentSection; + m_MemPatchInfoMap.insert(g_CurrentSection.c_str(), g_CurrentPatchInfo); g_CurrentPatchInfo = nullptr; diff --git a/userconf/mempatches.h b/userconf/mempatches.h index cee1e4a..1cd00e7 100644 --- a/userconf/mempatches.h +++ b/userconf/mempatches.h @@ -27,6 +27,7 @@ class MemPatchGameConfig : public ITextListener_SMC { offset = 0; } + std::string patchName; std::string signature; size_t offset; ByteVector vecPatch, vecVerify, vecPreserve; From 85aaba9dc5e507e2fb4fc5c09bec59f1954ecd32 Mon Sep 17 00:00:00 2001 From: A1mDev <33463136+A1mDev@users.noreply.github.com> Date: Sat, 4 Oct 2025 16:31:52 +0700 Subject: [PATCH 2/5] Code refactoring --- types/mempatch.cpp | 63 ++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/types/mempatch.cpp b/types/mempatch.cpp index 658284c..e6702d7 100644 --- a/types/mempatch.cpp +++ b/types/mempatch.cpp @@ -37,61 +37,48 @@ class MemoryPatch { for (auto bit : info.vecPreserve) { this->vecPreserve.push_back(bit); } - + // ignore offset if address is bad this->pAddress = pAddress ? pAddress + (info.offset) : 0; - strPatchName = info.patchName; } - + bool Enable() { if (vecRestore.size() > 0) { // already patched, disregard return false; } - + if (!this->Verify()) { return false; } - if (std::equal(vecPatch.begin(), vecPatch.end(), (uint8_t*) pAddress)) { - // The bytes in the binary file already match the patch, - // we inform the user that his patch is redundant, - // we tell them to check the patch. - // In this case, as a rule, section `verify` is missing. - smutils->LogError(myself, - "Patch '%s' is redundant. Binary already contains the patch bytes. " - "Check patch data or perhaps it was applied by a different plugin?", - strPatchName.c_str() - ); - } + ByteVectorRead(vecRestore, (uint8_t*)pAddress, vecPatch.size()); + + SourceHook::SetMemAccess((void*)this->pAddress, vecPatch.size() * sizeof(uint8_t), + SH_MEM_READ | SH_MEM_WRITE | SH_MEM_EXEC); + ByteVectorWrite(vecPatch, (uint8_t*)pAddress); - ByteVectorRead(vecRestore, (uint8_t*) pAddress, vecPatch.size()); - - SourceHook::SetMemAccess((void*) this->pAddress, vecPatch.size() * sizeof(uint8_t), - SH_MEM_READ | SH_MEM_WRITE | SH_MEM_EXEC); - ByteVectorWrite(vecPatch, (uint8_t*) pAddress); - for (size_t i = 0; i < vecPatch.size(); i++) { uint8_t preserveBits = 0; if (i < vecPreserve.size()) { preserveBits = vecPreserve[i]; } - *((uint8_t*) pAddress + i) = (vecPatch[i] & ~preserveBits) | (vecRestore[i] & preserveBits); + *((uint8_t*)pAddress + i) = (vecPatch[i] & ~preserveBits) | (vecRestore[i] & preserveBits); } - + return true; } - + void Disable() { if (vecRestore.size() == 0) { // no memory to restore, fug return; } - ByteVectorWrite(vecRestore, (uint8_t*) pAddress); + ByteVectorWrite(vecRestore, (uint8_t*)pAddress); vecRestore.clear(); } - + bool Verify() { if (!pAddress) { return false; @@ -116,7 +103,6 @@ class MemoryPatch { this->Disable(); } - std::string strPatchName; // For error messages uintptr_t pAddress; ByteVector vecPatch, vecRestore, vecVerify, vecPreserve; }; @@ -150,13 +136,30 @@ cell_t sm_MemoryPatchLoadFromConfig(IPluginContext *pContext, const cell_t *para if (!pConfig) { return pContext->ThrowNativeError("Invalid game config handle %x (error %d)", hndl, err); } + void* addr; - if (!pConfig->GetMemSig(info.signature.c_str(), &addr)) { + if (!pConfig->GetMemSig(info.signature.c_str(), &addr) || addr == NULL) { return pContext->ThrowNativeError("Failed to locate signature for '%s' (mempatch '%s')", info.signature.c_str(), name); } - + + if (info.vecPatch.empty()) { + return pContext->ThrowNativeError("Invalid patch configuration: section `patch` is empty for patch '%s'", name); + } + + if (std::equal(info.vecPatch.begin(), info.vecPatch.end(), (uint8_t*)addr)) { + // The bytes in the binary file already match the patch, + // we inform the user that his patch is redundant, + // we tell them to check the patch. + // In this case, as a rule, section `verify` is missing. + smutils->LogError(myself, + "Patch '%s' is redundant. Binary already contains the patch bytes. " + "Check patch data or perhaps it was applied by a different plugin?", + name + ); + } + MemoryPatch *pMemoryPatch = new MemoryPatch((uintptr_t) addr, info); - + return g_pHandleSys->CreateHandle(g_MemoryPatchType, pMemoryPatch, pContext->GetIdentity(), myself->GetIdentity(), NULL); } From 6de28a7128ad075f5f4bb2129158f2faee29172c Mon Sep 17 00:00:00 2001 From: A1mDev <33463136+A1mDev@users.noreply.github.com> Date: Sat, 4 Oct 2025 16:33:58 +0700 Subject: [PATCH 3/5] Remove unused code --- userconf/mempatches.cpp | 5 +---- userconf/mempatches.h | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/userconf/mempatches.cpp b/userconf/mempatches.cpp index 99c207d..e2f5fce 100644 --- a/userconf/mempatches.cpp +++ b/userconf/mempatches.cpp @@ -235,10 +235,7 @@ SMCResult MemPatchGameConfig::ReadSMC_LeavingSection(const SMCStates *states) { case PState_Runtime: // pop section info g_ParseState = PState_Root; - - // save data for error messages - g_CurrentPatchInfo->patchName = g_CurrentSection; - + m_MemPatchInfoMap.insert(g_CurrentSection.c_str(), g_CurrentPatchInfo); g_CurrentPatchInfo = nullptr; diff --git a/userconf/mempatches.h b/userconf/mempatches.h index 1cd00e7..cee1e4a 100644 --- a/userconf/mempatches.h +++ b/userconf/mempatches.h @@ -27,7 +27,6 @@ class MemPatchGameConfig : public ITextListener_SMC { offset = 0; } - std::string patchName; std::string signature; size_t offset; ByteVector vecPatch, vecVerify, vecPreserve; From c485f7bb167a76be1c8cf99eeeb863a4006d646d Mon Sep 17 00:00:00 2001 From: A1mDev <33463136+A1mDev@users.noreply.github.com> Date: Sat, 4 Oct 2025 16:40:50 +0700 Subject: [PATCH 4/5] Update mempatch.cpp --- types/mempatch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/mempatch.cpp b/types/mempatch.cpp index e6702d7..73e8d56 100644 --- a/types/mempatch.cpp +++ b/types/mempatch.cpp @@ -143,7 +143,7 @@ cell_t sm_MemoryPatchLoadFromConfig(IPluginContext *pContext, const cell_t *para } if (info.vecPatch.empty()) { - return pContext->ThrowNativeError("Invalid patch configuration: section `patch` is empty for patch '%s'", name); + return pContext->ThrowNativeError("Invalid patch configuration: section 'patch' is empty for patch '%s'", name); } if (std::equal(info.vecPatch.begin(), info.vecPatch.end(), (uint8_t*)addr)) { From cf4fdb13152c216be92c951a7a69bcda31199357 Mon Sep 17 00:00:00 2001 From: A1mDev <33463136+A1mDev@users.noreply.github.com> Date: Sat, 4 Oct 2025 16:52:21 +0700 Subject: [PATCH 5/5] Bump the version due to previous updates --- smsdk_config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smsdk_config.h b/smsdk_config.h index 690c0e0..1d3a9b1 100644 --- a/smsdk_config.h +++ b/smsdk_config.h @@ -40,7 +40,7 @@ /* Basic information exposed publicly */ #define SMEXT_CONF_NAME "Source Scramble" #define SMEXT_CONF_DESCRIPTION "Tools for working with memory." -#define SMEXT_CONF_VERSION "0.8.1" +#define SMEXT_CONF_VERSION "0.8.3" #define SMEXT_CONF_AUTHOR "nosoop" #define SMEXT_CONF_URL "https://github.com/nosoop/SMExt-SourceScramble" #define SMEXT_CONF_LOGTAG "sscramble"