commit f396288889b2e282c602673157d97d1388a49c83 Author: Redo Date: Fri Oct 3 19:38:24 2025 -0500 initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9ea0f13 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.* +!.gitignore diff --git a/Blockland.exe b/Blockland.exe new file mode 100644 index 0000000..99a8644 Binary files /dev/null and b/Blockland.exe differ diff --git a/RedBlocklandLoader.dll b/RedBlocklandLoader.dll new file mode 100644 index 0000000..7d97b05 Binary files /dev/null and b/RedBlocklandLoader.dll differ diff --git a/compile.bat b/compile.bat new file mode 100644 index 0000000..388c620 --- /dev/null +++ b/compile.bat @@ -0,0 +1,12 @@ +@echo off +cd /d %~dp0 + +set buildargs=-Wall -Werror -m32 -shared -Isrc -static -lpsapi -lshlwapi -static-libgcc -static-libstdc++ + +echo on +g++ src/RedBlocklandLoader.cpp %buildargs% -o RedBlocklandLoader.dll +@echo off + +rem objdump -d RedBlocklandLoader.dll > RedBlocklandLoader.dll.dump.txt + +pause diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..3b2952d --- /dev/null +++ b/readme.md @@ -0,0 +1,12 @@ + +# RedBlocklandLoader + +DLL mod loader for Blockland +Tested working with Blockland v21 r2033 + +## How to Install +- Copy `RedBlocklandLoader.dll` into your Blockland install folder, next to `Blockland.exe` +- Copy `Blockland.exe` into your Blockland folder, replacing the old `Blockland.exe` +- Make `Blockland.exe` read-only (rightclick -> Properties -> Attributes -> Read-only) so the launcher can't overwrite it +- Create a folder called `modules` the Blockland folder. This is where you will install DLL mods. +If all goes well, you should see "RedBlocklandLoader Init" in the console near the beginning of loading, followed by the loading of any installed DLL mods. diff --git a/src/RedBlocklandLoader.cpp b/src/RedBlocklandLoader.cpp new file mode 100644 index 0000000..4bfa4ed --- /dev/null +++ b/src/RedBlocklandLoader.cpp @@ -0,0 +1,127 @@ + +#include +#include +#include + +#include +#include + +#include "RedoBlHooks.cpp" + +char moduleDir[MAX_PATH]; + +std::map moduleTable; + +bool rblDetachModule(std::string name){ + HMODULE module = moduleTable[name]; + if(!module) return false; + + BlPrintf(" " "Detaching module \"%s\"", name.c_str()); + + if(!FreeLibrary(module)){ + BlPrintf(" \x03" "FreeLibrary failed to detach module. Error: %i", GetLastError()); + return false; + } + + moduleTable[name] = NULL; + return true; +} + +HMODULE rblAttachModule(std::string name){ + rblDetachModule(name); + + BlPrintf(" " "Attaching module \"%s\"", name.c_str()); + + char filename[MAX_PATH]; + strcpy_s(filename, moduleDir); + PathAppend(filename, name.c_str()); + + HMODULE module = LoadLibraryA(filename); + if (module == NULL) + BlPrintf(" \x03" "LoadLibrary failed to attach module. Error: %i", GetLastError()); + else + moduleTable[name] = module; + + return module; +} + +void rblLoad(){ + BlPrintf("RedBlocklandLoader Init:"); + + if(!GetModuleFileNameA(NULL, moduleDir, MAX_PATH)){ + BlPrintf(" \x03" "Failed to determine game file location. Error: %i", GetLastError()); + return; + } + PathRemoveFileSpec(moduleDir); + PathAppend(moduleDir, "modules"); + + bool foundAny = false; + + char moduleSearch[MAX_PATH]; + strcpy_s(moduleSearch, moduleDir); + PathAppend(moduleSearch, "*.dll"); + + BlPrintf(" " "Searching for modules in: \"%s\"", moduleSearch); + + WIN32_FIND_DATAA finddata; + HANDLE hFind = FindFirstFileA(moduleSearch, &finddata); + + if(hFind!=INVALID_HANDLE_VALUE){ + do{ + if(finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + continue; + + foundAny = true; + rblAttachModule(finddata.cFileName); + + } while(FindNextFileA(hFind, &finddata)); + } + + if(!foundAny){ + BlPrintf(" \x03" "No modules found"); + } + + BlPrintf(" "); +} + +BlFunctionDef(void, __cdecl, InitGame); +void __cdecl InitGameHook(); +BlFunctionHookDef(InitGame); + +void __cdecl InitGameHook(){ + rblLoad(); + + InitGameHookOff(); + InitGame(); + InitGameHookOn(); +} + +bool rblInit(){ + BlInit; + + BlScanFunctionHex(InitGame, "55 8B EC 6A FF 68 ? ? ? ? 64 A1 ? ? ? ? 50 83 EC 08 56 57 A1 ? ? ? ? 33 C5 50 8D 45 F4 64 A3 ? ? ? ? C7 05 ? ? ? ? ? ? ? ? C7 05 ? ? ? ? ? ? ? ? C7 05 ? ? ? ? ? ? ? ? C7 05 ? ? ? ? ? ? ? ? E8 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 8B F0 89 75 F0 85 F6 74 29 8B CE E8 ? ? ? ? 8D 46 34 C7 06 ? ? ? ? 89 45 F0 C7 40 ? ? ? ? ? C7 00 ? ? ? ? C7 40 ? ? ? ? ? EB 02"); + + InitGameHookOn(); + + return true; +} + +bool rblDenit(){ + InitGameHookOff(); + BlPrintf("RedBlocklandLoader: Successfully removed"); + + return true; +} + +bool __stdcall DllMain(HINSTANCE hinstance, unsigned int reason, void* reserved){ + switch(reason){ + case DLL_PROCESS_ATTACH: + return rblInit(); + case DLL_PROCESS_DETACH: + return rblDenit(); + default: + return true; + } +} + +extern "C" void __declspec(dllexport) __cdecl RedBlocklandLoader(){} diff --git a/src/RedoBlHooks.cpp b/src/RedoBlHooks.cpp new file mode 100644 index 0000000..d58c557 --- /dev/null +++ b/src/RedoBlHooks.cpp @@ -0,0 +1,169 @@ + +////////////////////////////////////////////////// +// RedoBlHooks Version 2.2 + +////////////////////////////////////////////////// +// Includes + +#include +#include + +#include "RedoBlHooks.hpp" + +////////////////////////////////////////////////// +// Sig Scanning + +ADDR ImageBase; +ADDR ImageSize; + +void InitScanner(){ + HMODULE module = GetModuleHandle(NULL); + if (module){ + MODULEINFO info; + GetModuleInformation(GetCurrentProcess(), module, &info, sizeof(MODULEINFO)); + ImageBase = (ADDR)info.lpBaseOfDll; + ImageSize = info.SizeOfImage; + } +} + +bool CompareData(BYTE *data, BYTE *pattern, char* mask){ + for (; *mask; ++data, ++pattern, ++mask){ + if (*mask=='x' && *data!=*pattern) + return false; + } + return (*mask)==0; +} + +ADDR FindPattern(ADDR imageBase, ADDR imageSize, BYTE *pattern, char *mask){ + for (ADDR i=imageBase; i < imageBase+imageSize; i++){ + if(CompareData((PBYTE)i, pattern, mask)){ + return i; + } + } + return 0; +} + +// Public + +ADDR rbh_ScanFunctionCode(char* pattern, char* mask){ + return FindPattern(ImageBase, ImageSize-strlen(mask), (BYTE*)pattern, mask); +} + +ADDR rbh_ScanFunctionHex(char* text){ + unsigned int len = strlen(text); + char* patt = (char*)malloc(len); + char* mask = (char*)malloc(len); + + int outidx = 0; + int val = 0; + bool uk = false; + for(unsigned int i=0; i='0' && c<='9'){ + val = (val<<4) + (c-'0'); + }else if(c>='A' && c<='F'){ + val = (val<<4) + (c-'A'+10); + }else if(c>='a' && c<='f'){ + val = (val<<4) + (c-'a'+10); + }else if(c==' '){ + patt[outidx] = uk ? 0 : val; + mask[outidx] = uk ? '?' : 'x'; + val = 0; + uk = false; + outidx++; + } + } + + patt[outidx] = uk ? 0 : val; + mask[outidx] = uk ? '?' : 'x'; + outidx++; + patt[outidx] = 0; + mask[outidx] = 0; + + ADDR res = rbh_ScanFunctionCode(patt, mask); + + free(patt); + free(mask); + + return res; +} + +////////////////////////////////////////////////// +// Call Patching and Hooking + +void rbh_PatchByte(ADDR location, BYTE value){ + DWORD oldProtection; + VirtualProtect((void*)location, 1, PAGE_EXECUTE_READWRITE, &oldProtection); + *((BYTE*)location) = value; + VirtualProtect((void*)location, 1, oldProtection, &oldProtection); +} + +void rbh_PatchBytes(unsigned int len, ADDR location, BYTE* repl){ + for(unsigned int i=0; i> (i*8)) & 0xFF; + rbh_PatchByte(addr+i,repl); + } +} + +int CallOffset(ADDR instr, ADDR func){ + return func - (instr+4); +} + +void PatchCall(ADDR instr, ADDR target){ + rbh_PatchInt(instr, CallOffset(instr, target)); +} + +void PatchCopy(ADDR dest, ADDR src, unsigned int len){ + for(unsigned int i=0; i