# Conflicts:
#	CubeModLoader/main.cpp
This commit is contained in:
yangzhi 2019-10-16 14:07:21 +08:00
commit 9b837e0a91
6 changed files with 169 additions and 102 deletions

View File

@ -3,20 +3,13 @@
#include <windows.h>
#include <vector>
#include "Process.h"
#include "crc.h"
#define CUBE_VERSION "1.0.0-1"
#define CUBE_PACKED_CRC 0xC7682619
#define CUBE_UNPACKED_CRC 0xBA092543
#define MODLOADER_CRC 0x39D18E98
char* CUBE_EXECUTABLE = "cubeworld.exe";
char* MODLOADER_DLL = "CubeModLoader.dll";
using namespace std;
bool FileExists(char* fileName) {
bool FileExists(const char* fileName) {
DWORD dwAttrib = GetFileAttributes(fileName);
return (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}
@ -28,13 +21,8 @@ int Bail(int result){
}
int main(int argc, char** argv) {
bool testMode = false;
if (argc >= 2 && !strcmp(argv[1], "test")) {
testMode = true;
printf("Test mode enabled. CRC checks will be bypassed.\n");
if (argc >= 3) {
CUBE_EXECUTABLE = argv[2];
}
if (argc >= 2) {
CUBE_EXECUTABLE = argv[1];
}
//Cube world is obviously required
@ -43,44 +31,12 @@ int main(int argc, char** argv) {
return Bail(1);
}
unsigned int checksum = crc32_file(CUBE_EXECUTABLE);
if (testMode) {
printf("%s CRC: %08X\n", CUBE_EXECUTABLE, checksum);
}
// Check if the game is still packed
if (checksum == CUBE_PACKED_CRC && !testMode) {
printf("Cube World was found, but it is not unpacked.\n"
"Use Steamless to unpack %s.\n", CUBE_EXECUTABLE);
return Bail(1);
}
if (checksum != CUBE_UNPACKED_CRC && !testMode) {
printf("Cube World was found, but it is not version %s.\n"
"(Found CRC %08X, expected %08X)\n"
"Please update your game.\n",
CUBE_VERSION, checksum, CUBE_UNPACKED_CRC);
return Bail(1);
}
//Inject our dll
if ( !FileExists(MODLOADER_DLL) ) {
printf("%s not found.\n", MODLOADER_DLL);
return Bail(1);
}
unsigned int loaderChecksum = crc32_file(MODLOADER_DLL);
if (loaderChecksum != MODLOADER_CRC && !testMode) {
printf("%s is the wrong version (%08X)\n", MODLOADER_DLL, loaderChecksum);
return Bail(1);
}
if (testMode) {
printf("%s CRC: %08X\n", MODLOADER_DLL, loaderChecksum);
}
Process process(CUBE_EXECUTABLE);
//Create game in suspended state

View File

@ -1,7 +1,7 @@
#ifndef MAIN_H_INCLUDED
#define MAIN_H_INCLUDED
bool FileExists(char* fileName);
bool FileExists(const char* fileName);
int Bail(int result);
int main(int argc, char** argv);

View File

@ -2,9 +2,16 @@
#include <windows.h>
#include <vector>
#include "DLL.h"
#include "crc.h"
#define MOD_MAJOR_VERSION 4
#define MOD_MINOR_VERSION 1
#define MOD_MINOR_VERSION 2
#define CUBE_VERSION "1.0.0-1"
#define CUBE_PACKED_CRC 0xC7682619
#define CUBE_UNPACKED_CRC 0xBA092543
#define MODLOADER_NAME "CubeModLoader"
#define no_optimize __attribute__((optimize("O0")))
@ -30,8 +37,12 @@ dllname->name = GetProcAddress(dllname->handle, #name);
using namespace std;
void* base;
vector <DLL*> modDLLs;
void* base; // Module base
vector <DLL*> modDLLs; // Every mod we've loaded
HMODULE hSelf; // A handle to ourself, to prevent being unloaded
void* initterm_e; // A pointer to a function which is run extremely soon after starting, or after being unpacked
const size_t BYTES_TO_MOVE = 14; // The size of a far jump
char initterm_e_remember[BYTES_TO_MOVE]; // We'll use this to store the code we overwrite in initterm_e, so we can put it back later.
void WriteFarJMP(void* source, void* destination) {
DWORD dwOldProtection;
@ -52,65 +63,69 @@ void WriteFarJMP(void* source, void* destination) {
#include "callbacks/ChatHandler.h"
#include "callbacks/P2PRequestHandler.h"
#include "callbacks/CheckInventoryFullHandler.h"
#include "callbacks/CheckMapIconVisibilityHandler.h"
void SetupHandlers() {
SetupChatHandler();
SetupP2PRequestHandler();
SetupCheckInventoryFullHandler();
SetupCheckMapIconVisibilityHandler();
}
void Popup(const char* title, char* msg ){
void Popup(const char* title, const char* msg ) {
MessageBoxA(0, msg, title, MB_OK | MB_ICONINFORMATION);
}
extern "C" __declspec(dllexport) BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
base = GetModuleHandle(NULL);
void PrintLoadedMods() {
std::string mods("Mods Loaded:\n");
for (DLL* dll : modDLLs) {
mods += dll->fileName;
mods += "\n";
}
Popup("Loaded Mods", mods.c_str());
}
SetupHandlers();
// Handles injecting callbacks and the mods
void StartMods() {
char msg[256] = {0};
//Find mods
HANDLE hFind;
WIN32_FIND_DATA data;
SetupHandlers();
CreateDirectory("Mods", NULL);
hFind = FindFirstFile("Mods\\*.dll", &data);
if (hFind != INVALID_HANDLE_VALUE) {
do {
// We should be loaded into the application's address space, so we can just LoadLibraryA
DLL* dll = new DLL(string("Mods\\") + data.cFileName);
dll->Load();
printf("Loaded %s\n", dll->fileName.c_str());
modDLLs.push_back(dll);
} while (FindNextFile(hFind, &data));
FindClose(hFind);
}
//Find mods
HANDLE hFind;
WIN32_FIND_DATA data;
// Find all the functions the mods may export
for (DLL* dll: modDLLs) {
MUST_IMPORT(dll, ModMajorVersion);
MUST_IMPORT(dll, ModMinorVersion);
MUST_IMPORT(dll, ModPreInitialize);
IMPORT(dll, ModInitialize);
IMPORT(dll, HandleChat);
IMPORT(dll, HandleP2PRequest);
IMPORT(dll, HandleCheckInventoryFull);
IMPORT(dll, HandleCheckMapIconVisibility);
}
CreateDirectory("Mods", NULL);
hFind = FindFirstFile("Mods\\*.dll", &data);
if (hFind != INVALID_HANDLE_VALUE) {
do {
// We should be loaded into the application's address space, so we can just LoadLibraryA
DLL* dll = new DLL(string("Mods\\") + data.cFileName);
dll->Load();
printf("Loaded %s\n", dll->fileName.c_str());
modDLLs.push_back(dll);
} while (FindNextFile(hFind, &data));
FindClose(hFind);
}
// Ensure version compatibility
char msg[512] = {0};
for (DLL* dll: modDLLs) {
int majorVersion = ((int(*)())dll->ModMajorVersion)();
int minorVersion = ((int(*)())dll->ModMinorVersion)();
if (majorVersion != MOD_MAJOR_VERSION) {
sprintf(msg, "%s has major version %d but requires %d.\n", dll->fileName.c_str(), majorVersion, MOD_MAJOR_VERSION);
Popup("Error", msg);
exit(1);
}
// Find all the functions the mods may export
for (DLL* dll: modDLLs) {
MUST_IMPORT(dll, ModMajorVersion);
MUST_IMPORT(dll, ModMinorVersion);
MUST_IMPORT(dll, ModPreInitialize);
IMPORT(dll, ModInitialize);
IMPORT(dll, HandleChat);
IMPORT(dll, HandleP2PRequest);
IMPORT(dll, HandleCheckInventoryFull);
IMPORT(dll, HandleCheckMapIconVisibility);
}
// Ensure version compatibility
for (DLL* dll: modDLLs) {
int majorVersion = ((int(*)())dll->ModMajorVersion)();
int minorVersion = ((int(*)())dll->ModMinorVersion)();
if (majorVersion != MOD_MAJOR_VERSION) {
sprintf(msg, "%s has major version %d but requires %d.\n", dll->fileName.c_str(), majorVersion, MOD_MAJOR_VERSION);
Popup("Error", msg);
exit(1);
if (minorVersion > MOD_MINOR_VERSION) {
sprintf(msg, "%s has minor version %d but requires %d or lower.\n", dll->fileName.c_str(), minorVersion, MOD_MINOR_VERSION);
@ -129,7 +144,108 @@ extern "C" __declspec(dllexport) BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD
((void(*)())dll->ModInitialize)();
}
}
}
if (hSelf) PrintLoadedMods();
return;
}
void* StartMods_ptr = (void*)&StartMods;
void no_optimize ASMStartMods() {
asm(PUSH_ALL
PREPARE_STACK
// Initialize mods and callbacks
"call [StartMods_ptr] \n"
// We can put initterm_e back how we found it.
"call [CopyInitializationBack_ptr] \n"
RESTORE_STACK
POP_ALL
// Run initterm_e properly this time.
"jmp [initterm_e] \n"
);
}
void PatchFreeImage(){
// Thanks to frognik for showing off this method!
DWORD oldProtect;
void* patchaddr = (void*)GetModuleHandleA("FreeImage.dll") + 0x1E8C12;
VirtualProtect((LPVOID)patchaddr, 8, PAGE_EXECUTE_READWRITE, &oldProtect);
*(uint64_t*)patchaddr = 0x909090000000A8E9;
}
void InitializationPatch() {
// Get pointer to initterm_e
initterm_e = *(void**)(base + 0x42CBD8);
// Store old code, we'll copy it back once we regain control.
memcpy(initterm_e_remember, initterm_e, BYTES_TO_MOVE);
// Write a jump to our code
WriteFarJMP(initterm_e, (void*)&ASMStartMods);
}
// This restores initterm_e to how it was before we hijacked it.
void CopyInitializationBack() {
DWORD dwOldProtection;
VirtualProtect(initterm_e, BYTES_TO_MOVE, PAGE_EXECUTE_READWRITE, &dwOldProtection);
memcpy(initterm_e, initterm_e_remember, BYTES_TO_MOVE);
VirtualProtect(initterm_e, BYTES_TO_MOVE, dwOldProtection, &dwOldProtection);
return;
}
void* CopyInitializationBack_ptr = (void*)&CopyInitializationBack;
bool already_ran = false;
extern "C" __declspec(dllexport) BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
base = GetModuleHandle(NULL);
// Don't allow this to run more than once
if (already_ran)
return true;
already_ran = true;
char msg[256] = {0};
// This serves to prevent ourself from being unloaded, if we are a .fip
hSelf = LoadLibrary(MODLOADER_NAME ".fip");
// The user could also inject this as a DLL, so if we can't find our .fip, we were probably launched with a launcher.
// Therefore, we don't need to prompt the user.
if (hSelf) {
if (MessageBoxA(NULL, "Would you like to run with mods?", "Cube World Mod Loader", MB_YESNO) != IDYES) {
PatchFreeImage();
return true;
}
}
base = GetModuleHandle(NULL);
// Figure out where the executable is and verify its checksum
char cubePath[_MAX_PATH+1];
GetModuleFileName(NULL, cubePath, _MAX_PATH);
uint32_t checksum = crc32_file(cubePath);
if (checksum == CUBE_PACKED_CRC || checksum == CUBE_UNPACKED_CRC) {
// Patch some code to run StartMods. This method makes it work with AND without SteamStub.
InitializationPatch();
} else {
sprintf(msg, "%s does not seem to be version %s. CRC %08X", cubePath, CUBE_VERSION, checksum);
Popup("Error", msg);
PatchFreeImage();
return true;
}
Sleep(250);
PatchFreeImage();
}
return true;
}

View File

@ -3,20 +3,15 @@
Supports injecting Cube World mods. DLLs must go in a folder called "Mods".
## Installing
Get the latest executable from Releases and place it in the same folder as cubeworld.exe
Get the latest .fib from Releases and place it in the same folder as cubeworld.exe
https://github.com/ChrisMiuchiz/Cube-World-Mod-Launcher/releases
## Installing Mods
A "Mods" folder should be created in the same folder as cubeworld.exe, and mods should be installed by moving them into the Mods folder.
## Preparing cubeworld.exe
Since Cube World is using SteamStub obfuscation, you need to remove the obfuscation using [Steamless](https://github.com/atom0s/Steamless).
## Building the launcher or mods
This project will only ever support GCC. This program will not even build using MSVC, not only because the inline assembly syntax is different, but because support for inline assembly was removed for x86-64.
This project currently only supports GCC, but will likely move to clang soon. This program will not even build using MSVC, not only because the inline assembly syntax is different, but because support for inline assembly was removed for x86-64.
All mods MUST include cwmods.h from [cwsdk](https://github.com/ChrisMiuchiz/CWSDK) to function.