This commit is contained in:
Redo
2025-10-03 19:38:24 -05:00
commit f396288889
8 changed files with 411 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
.*
!.gitignore

BIN
Blockland.exe Normal file

Binary file not shown.

BIN
RedBlocklandLoader.dll Normal file

Binary file not shown.

12
compile.bat Normal file
View File

@@ -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

12
readme.md Normal file
View File

@@ -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.

127
src/RedBlocklandLoader.cpp Normal file
View File

@@ -0,0 +1,127 @@
#include <windows.h>
#include <psapi.h>
#include <shlwapi.h>
#include <map>
#include <string>
#include "RedoBlHooks.cpp"
char moduleDir[MAX_PATH];
std::map<std::string, HMODULE> 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(){}

169
src/RedoBlHooks.cpp Normal file
View File

@@ -0,0 +1,169 @@
//////////////////////////////////////////////////
// RedoBlHooks Version 2.2
//////////////////////////////////////////////////
// Includes
#include <Windows.h>
#include <psapi.h>
#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<len; i++){
char c = text[i];
if(c=='?'){
uk = true;
}else if(c>='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<len; i++){
rbh_PatchByte(location+i, repl[i]);
}
}
void rbh_PatchInt(ADDR addr, int rval){
for(unsigned int i=0; i<4; i++){
BYTE repl=(rval >> (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<len; i++){
rbh_PatchByte(dest+i, *((BYTE*)(src+i)));
}
}
void rbh_HookFunction(ADDR victim, ADDR detour, BYTE* origbytes){
memcpy(origbytes, (BYTE*)victim, 6); //save old data
rbh_PatchByte(victim, 0xE9); //call
PatchCall(victim+1, detour); //detour func with call offset
rbh_PatchByte(victim+5, 0xC3); //retn
}
void rbh_UnhookFunction(ADDR victim, BYTE* origbytes){
PatchCopy(victim, (ADDR)origbytes, 6); //restore old data
}
int rbh_PatchAllMatches(unsigned int len, char* pattern, char* mask, char* replace, bool debugprint){
int numpatched = 0;
for(ADDR i=ImageBase; i<ImageBase+ImageSize-len; i++){
if(CompareData((BYTE*)i, (BYTE*)pattern, mask)){
if(debugprint) BlPrintf("RedoBlHooks: Patching call at %08x", i);
numpatched++;
for(ADDR c=0; c<len; c++){
rbh_PatchByte(i+c, replace[c]);
}
}
}
return numpatched;
}
//////////////////////////////////////////////////
// Init
BlFunctionDefIntern(rbh_BlPrintf);
bool rbh_InitInternal(){
InitScanner();
rbh_BlPrintf = (rbh_rbh_BlPrintfFnT)rbh_ScanFunctionHex((char*)"8D 44 24 08 33 D2 50 FF 74 24 08 33 C9 E8 ? ? ? ? 83 C4 08 C3");
if(!rbh_BlPrintf) return false;
return true;
}

89
src/RedoBlHooks.hpp Normal file
View File

@@ -0,0 +1,89 @@
//////////////////////////////////////////////////
// RedoBlHooks Version 2.2
#pragma once
typedef unsigned char BYTE;
typedef unsigned int ADDR;
#ifndef RBH_DEBUGPRINT
#define RBH_DEBUGPRINT true
#endif
#define BlFunctionDef(returnType, convention, name, ...) \
typedef returnType (convention *rbh_##name##FnT)(__VA_ARGS__); \
rbh_##name##FnT name;
#define BlFunctionDefExtern(returnType, convention, name, ...) \
typedef returnType (convention *rbh_##name##FnT)(__VA_ARGS__); \
extern rbh_##name##FnT name;
#define BlFunctionDefIntern(name) \
rbh_##name##FnT name;
#define BlScanFunctionCode(target, pattern, mask) \
target = (rbh_##target##FnT)rbh_ScanFunctionCode((char*)pattern, (char*)mask); \
if(!target){ \
rbh_BlPrintf("RedoBlHooks | Cannot find function "#target"!"); \
return false; \
}else{ \
if(RBH_DEBUGPRINT) rbh_BlPrintf("RedoBlHooks | Found function "#target" at %08x", (int)target); \
}
#define BlScanFunctionHex(target, text) \
target = (rbh_##target##FnT)rbh_ScanFunctionHex((char*)text); \
if(!target){ \
rbh_BlPrintf("RedoBlHooks | Cannot find function "#target"!"); \
return false; \
}else{ \
if(RBH_DEBUGPRINT) rbh_BlPrintf("RedoBlHooks | Found function "#target" at %08x", (int)target); \
}
#define BlScanCode(target, pattern, mask) \
target = rbh_ScanFunctionCode((char*)pattern, (char*)mask); \
if(!target){ \
rbh_BlPrintf("RedoBlHooks | Cannot find pattern "#target"!"); \
return false; \
}else{ \
if(RBH_DEBUGPRINT) rbh_BlPrintf("RedoBlHooks | Found "#target" at %08x", (int)target); \
}
#define BlScanHex(target, text) \
target = rbh_ScanFunctionHex((char*)text); \
if(!target){ \
rbh_BlPrintf("RedoBlHooks | Cannot find "#target"!"); \
return false; \
}else{ \
if(RBH_DEBUGPRINT) rbh_BlPrintf("RedoBlHooks | Found "#target" at %08x", (int)target); \
}
#define BlFunctionHookDef(func) \
BYTE rbh_BlFunctionHook##func##Data[6]; \
void func##HookOn(){ rbh_HookFunction((ADDR)func, (ADDR)func##Hook, rbh_BlFunctionHook##func##Data); } \
void func##HookOff(){ rbh_UnhookFunction((ADDR)func, rbh_BlFunctionHook##func##Data); }
#define BlPatchAllMatches(len, patt, mask, repl) \
rbh_PatchAllMatches(len, (char*)patt, (char*)mask, (char*)repl, RBH_DEBUGPRINT);
#define BlPatchByte(addr, byte) \
rbh_PatchByte((ADDR)addr, (BYTE)byte);
#define BlPatchBytes(len, addr, repl) \
rbh_PatchBytes(len, (ADDR)addr, (BYTE*)repl);
#define BlInit if(!rbh_InitInternal()) return false;
#define BlPrintf rbh_BlPrintf
bool rbh_InitInternal();
ADDR rbh_ScanFunctionCode(char*, char*);
ADDR rbh_ScanFunctionHex(char*);
void rbh_HookFunction(ADDR, ADDR, BYTE*);
void rbh_UnhookFunction(ADDR, BYTE*);
int rbh_PatchAllMatches(unsigned int, char*, char*, char*, bool);
void rbh_PatchByte(ADDR, BYTE);
void rbh_PatchBytes(unsigned int, ADDR, BYTE*);
void rbh_PatchInt(ADDR, int);
BlFunctionDefExtern(void, , rbh_BlPrintf, const char*, ...);