diff --git a/game/game/compatibility/ikarus.cpp b/game/game/compatibility/ikarus.cpp index 6cc269f56..54d70b5f5 100644 --- a/game/game/compatibility/ikarus.cpp +++ b/game/game/compatibility/ikarus.cpp @@ -4,11 +4,35 @@ using namespace Tempest; +enum { + GothicFirstInstructionAddress = 4198400, // 0x401000 + ContentParserAddress = 11223232, // 0xAB40C0 + vfxParserPointerAddress = 9234156, // 0x8CE6EC + menuParserPointerAddress = 9248360, // 0x8D1E68 + pfxParserPointerAddress = 9278004, // 0x8D9234 + + MEMINT_oGame_Pointer_Address = 11208836, //0xAB0884 + MEMINT_zTimer_Address = 10073044, //0x99B3D4 + MEMINT_oCInformationManager_Address = 11191384, //0xAAC458 + MEMINT_gameMan_Pointer_address = 9185624, //0x8C2958 + }; + Ikarus::Ikarus(GameScript& /*owner*/, Daedalus::DaedalusVM& vm) /*:owner(owner)*/ { Log::i("DMA mod detected: Ikarus"); + // build-in data with assumed address + versionHint = 504628679; // G2 + allocator.pin(&versionHint, GothicFirstInstructionAddress, 4, "MEMINT_ReportVersionCheck"); + allocator.pin(&oGame_Pointer, MEMINT_oGame_Pointer_Address, 4, "oGame*"); + allocator.pin(&parserProxy, ContentParserAddress, sizeof(parserProxy), "zCParser proxy"); + + // build-in data without assumed address + oGame_Pointer = allocator.pin(&gameProxy, 0, sizeof(gameProxy), "oGame"); + // Note: no inline asm + vm.registerInternalFunction("ASMINT_Push", [](Daedalus::DaedalusVM&){}); + vm.registerInternalFunction("ASMINT_Pop", [](Daedalus::DaedalusVM&){}); vm.registerInternalFunction("ASMINT_MyExternal", [](Daedalus::DaedalusVM&){}); vm.registerInternalFunction("ASMINT_CallMyExternal", [](Daedalus::DaedalusVM&){}); vm.registerInternalFunction("ASMINT_Init", [](Daedalus::DaedalusVM&){}); @@ -18,21 +42,35 @@ Ikarus::Ikarus(GameScript& /*owner*/, Daedalus::DaedalusVM& vm) vm.registerInternalFunction("ASM_Run", [](Daedalus::DaedalusVM&){}); vm.registerInternalFunction("ASM_RunOnce", [](Daedalus::DaedalusVM&){}); - vm.registerInternalFunction("MEM_ReadInt", [this](Daedalus::DaedalusVM& vm){ mem_readint(vm); }); - vm.registerInternalFunction("MEM_WriteInt", [this](Daedalus::DaedalusVM& vm){ mem_writeint(vm); }); - vm.registerInternalFunction("MEM_SearchVobByName", [this](Daedalus::DaedalusVM& vm){ mem_searchvobbyname(vm); }); + vm.registerInternalFunction("MEMINT_SetupExceptionHandler", [this](Daedalus::DaedalusVM& vm){ mem_setupexceptionhandler(vm); }); + vm.registerInternalFunction("MEMINT_ReplaceSlowFunctions", [ ](Daedalus::DaedalusVM& ) { }); + vm.registerInternalFunction("MEM_GetAddress_Init", [this](Daedalus::DaedalusVM& vm){ mem_getaddress_init(vm); }); + vm.registerInternalFunction("MEM_PrintStackTrace", [this](Daedalus::DaedalusVM& vm){ mem_printstacktrace_implementation(vm); }); + vm.registerInternalFunction("MEM_GetFuncPtr", [this](Daedalus::DaedalusVM& vm){ mem_getfuncptr(vm); }); + vm.registerInternalFunction("MEM_ReplaceFunc", [this](Daedalus::DaedalusVM& vm){ mem_replacefunc(vm); }); + vm.registerInternalFunction("MEM_SearchVobByName", [this](Daedalus::DaedalusVM& vm){ mem_searchvobbyname(vm); }); + + // ## Basic Read Write ## + vm.registerInternalFunction("MEM_ReadInt", [this](Daedalus::DaedalusVM& vm){ mem_readint(vm); }); + vm.registerInternalFunction("MEM_WriteInt", [this](Daedalus::DaedalusVM& vm){ mem_writeint(vm); }); + vm.registerInternalFunction("MEM_CopyBytes", [this](Daedalus::DaedalusVM& vm){ mem_copybytes(vm); }); + vm.registerInternalFunction("MEM_GetCommandLine", [this](Daedalus::DaedalusVM& vm){ mem_getcommandline(vm); }); + + // ## Basic zCParser related functions ## + vm.registerInternalFunction("MEM_GetIntAddress", [this](Daedalus::DaedalusVM& vm){ _takeref(vm); }); + vm.registerInternalFunction("MEM_PtrToInst", [this](Daedalus::DaedalusVM& vm){ mem_ptrtoinst(vm); }); + vm.registerInternalFunction("_@", [this](Daedalus::DaedalusVM& vm){ _takeref(vm); }); + vm.registerInternalFunction("_@s", [this](Daedalus::DaedalusVM& vm){ _takeref_s(vm); }); + vm.registerInternalFunction("_@f", [this](Daedalus::DaedalusVM& vm){ _takeref_f(vm); }); - vm.registerInternalFunction("CALL__stdcall", [this](Daedalus::DaedalusVM& vm){ call__stdcall(vm); }); + // ## Preliminary MEM_Alloc and MEM_Free ## + vm.registerInternalFunction("MEM_Alloc", [this](Daedalus::DaedalusVM& vm){ mem_alloc(vm); }); + vm.registerInternalFunction("MEM_Free", [this](Daedalus::DaedalusVM& vm){ mem_free(vm); }); - // Get and set instance offsets - vm.registerInternalFunction("_^", [this](Daedalus::DaedalusVM& vm){ _deref(vm); }); - // Address Operator - vm.registerInternalFunction("MEM_GetIntAddress", [this](Daedalus::DaedalusVM& vm){ _takeref(vm); }); + vm.registerInternalFunction("CALL__stdcall", [this](Daedalus::DaedalusVM& vm){ call__stdcall(vm); }); - vm.registerInternalFunction("_@", [this](Daedalus::DaedalusVM& vm){ _takeref(vm); }); - vm.registerInternalFunction("_@s", [this](Daedalus::DaedalusVM& vm){ _takeref_s(vm); }); - vm.registerInternalFunction("_@f", [this](Daedalus::DaedalusVM& vm){ _takeref_f(vm); }); + // vm.disAsm(vm.getDATFile().getSymbolIndexByName("MEMINT_GetAddress_Init")); } bool Ikarus::isRequired(Daedalus::DaedalusVM& vm) { @@ -45,17 +83,74 @@ bool Ikarus::isRequired(Daedalus::DaedalusVM& vm) { dat.hasSymbolName("_^"); } +void Ikarus::mem_setupexceptionhandler(Daedalus::DaedalusVM&) { + // disallow any SEH handlers and similar - that is not a script business! + } + +void Ikarus::mem_getaddress_init(Daedalus::DaedalusVM&) { /* nop */ } + +void Ikarus::mem_replacefunc(Daedalus::DaedalusVM& vm) { + int func = vm.popInt(); + int dest = vm.popInt(); + auto& sf = vm.getDATFile().getSymbolByIndex(func); + auto& sd = vm.getDATFile().getSymbolByIndex(dest); + + if(sf.properties.elemProps.type!=Daedalus::EParType_Func) { + Log::e("mem_replacefunc: invalid function ptr"); + return; + } + if(sd.properties.elemProps.type!=Daedalus::EParType_Func) { + Log::e("mem_replacefunc: invalid function ptr"); + return; + } + + Log::d("mem_replacefunc: ",sd.name," -> ",sf.name); + //auto& bin = vm.getDATFile().rawCode(); + //bin[sd.address]->op = Daedalus::EParOp_Jump; + //bin[sd.address]->address = func; + } + +void Ikarus::mem_printstacktrace_implementation(Daedalus::DaedalusVM &vm) { + Log::e("[start of stacktrace]"); + auto stk = vm.getCallStack(); + for(auto& i:stk) + Log::e(i); + Log::e("[end of stacktrace]"); + } + +void Ikarus::mem_getfuncptr(Daedalus::DaedalusVM& vm) { + auto func = vm.popInt(); + auto& sym = vm.getDATFile().getSymbolByIndex(func); + if(sym.properties.elemProps.type!=Daedalus::EParType_Func) { + Log::e("mem_getfuncptr: invalid function ptr"); + vm.setReturn(0); + return; + } + vm.setReturn(0); + } + void Ikarus::mem_readint(Daedalus::DaedalusVM& vm) { - auto address = vm.popInt(); - (void)address; - vm.setReturn(0x1); + const auto address = vm.popInt(); + int32_t v = allocator.readInt(address); + vm.setReturn(v); } void Ikarus::mem_writeint(Daedalus::DaedalusVM& vm) { - auto val = vm.popInt(); - (void)val; - auto address = vm.popInt(); - (void)address; + auto val = vm.popInt(); + auto address = uint32_t(vm.popInt()); + allocator.writeInt(address,val); + } + +void Ikarus::mem_copybytes(Daedalus::DaedalusVM& vm) { + auto size = uint32_t(vm.popInt()); + auto dst = uint32_t(vm.popInt()); + auto src = uint32_t(vm.popInt()); + allocator.copyBytes(src,dst,size); + } + +void Ikarus::mem_getcommandline(Daedalus::DaedalusVM &vm) { + // TODO: return real commandline + vm.setReturn(""); } void Ikarus::mem_searchvobbyname(Daedalus::DaedalusVM& vm) { @@ -70,10 +165,10 @@ void Ikarus::call__stdcall(Daedalus::DaedalusVM& vm) { (void)address; } -void Ikarus::_deref(Daedalus::DaedalusVM& vm) { - auto val = vm.popInt(); - (void)val; - vm.setReturn(0x1); +void Ikarus::mem_ptrtoinst(Daedalus::DaedalusVM& vm) { + auto address = vm.popInt(); + // TODO: return an instance + vm.setReturn(address); } void Ikarus::_takeref(Daedalus::DaedalusVM& vm) { @@ -93,3 +188,14 @@ void Ikarus::_takeref_f(Daedalus::DaedalusVM& vm) { (void)val; vm.setReturn(0x1); } + +void Ikarus::mem_alloc(Daedalus::DaedalusVM& vm) { + auto amount = vm.popInt(); + auto ptr = allocator.alloc(amount); + vm.setReturn(int32_t(ptr)); + } + +void Ikarus::mem_free(Daedalus::DaedalusVM& vm) { + auto ptr = uint32_t(vm.popInt()); + allocator.free(Mem32::ptr32_t(ptr)); + } diff --git a/game/game/compatibility/ikarus.h b/game/game/compatibility/ikarus.h index 5e95a0fc5..f9bfb43e3 100644 --- a/game/game/compatibility/ikarus.h +++ b/game/game/compatibility/ikarus.h @@ -3,6 +3,7 @@ #include #include "scriptplugin.h" +#include "mem32.h" class GameScript; @@ -13,18 +14,54 @@ class Ikarus : public ScriptPlugin { static bool isRequired(Daedalus::DaedalusVM& vm); private: - void mem_readint (Daedalus::DaedalusVM &vm); - void mem_writeint (Daedalus::DaedalusVM &vm); - void mem_searchvobbyname(Daedalus::DaedalusVM &vm); + using ptr32_t = Mem32::ptr32_t; - void call__stdcall (Daedalus::DaedalusVM &vm); + struct oGame { + int data[16] = {}; + }; + + struct zCParser { + uint8_t padd0[24] = {}; + ptr32_t symtab_table_array = 0; // 24 + uint8_t padd1[8] = {}; + ptr32_t sorted_symtab_table_array = 0; // 36 + uint8_t padd2[32] = {}; + ptr32_t stack = 0; // 72 + ptr32_t stack_stackPtr = 0; // 76 + }; + + void mem_setupexceptionhandler (Daedalus::DaedalusVM &vm); + void mem_getaddress_init (Daedalus::DaedalusVM &vm); + void mem_printstacktrace_implementation(Daedalus::DaedalusVM &vm); + void mem_getfuncptr (Daedalus::DaedalusVM &vm); + void mem_replacefunc (Daedalus::DaedalusVM &vm); + void mem_searchvobbyname (Daedalus::DaedalusVM &vm); + + // ## Basic Read Write ## + void mem_readint (Daedalus::DaedalusVM &vm); + void mem_writeint (Daedalus::DaedalusVM &vm); + void mem_copybytes (Daedalus::DaedalusVM &vm); + void mem_getcommandline (Daedalus::DaedalusVM &vm); - void _deref (Daedalus::DaedalusVM &vm); + // pointers + void mem_ptrtoinst (Daedalus::DaedalusVM &vm); + // ## Basic zCParser related functions ## void _takeref (Daedalus::DaedalusVM &vm); void _takeref_s (Daedalus::DaedalusVM &vm); void _takeref_f (Daedalus::DaedalusVM &vm); + // ## Preliminary MEM_Alloc and MEM_Free ## + void mem_alloc (Daedalus::DaedalusVM &vm); + void mem_free (Daedalus::DaedalusVM &vm); + + void call__stdcall (Daedalus::DaedalusVM &vm); + + Mem32 allocator; + uint32_t versionHint = 0; + ptr32_t oGame_Pointer = 0; + oGame gameProxy; + zCParser parserProxy; // GameScript& owner; }; diff --git a/game/game/compatibility/lego.cpp b/game/game/compatibility/lego.cpp new file mode 100644 index 000000000..33eb346f1 --- /dev/null +++ b/game/game/compatibility/lego.cpp @@ -0,0 +1,18 @@ +#include "lego.h" +#include "ikarus.h" + +#include + +using namespace Tempest; + +LeGo::LeGo(GameScript& /*owner*/, Daedalus::DaedalusVM& /*vm*/) { + Log::i("DMA mod detected: LeGo"); + } + +bool LeGo::isRequired(Daedalus::DaedalusVM& vm) { + auto& dat = vm.getDATFile(); + return + dat.hasSymbolName("LeGo_InitFlags") && + dat.hasSymbolName("LeGo_Init") && + Ikarus::isRequired(vm); + } diff --git a/game/game/compatibility/lego.h b/game/game/compatibility/lego.h new file mode 100644 index 000000000..7e60cdd02 --- /dev/null +++ b/game/game/compatibility/lego.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#include "scriptplugin.h" + +class GameScript; + +class LeGo : public ScriptPlugin { + public: + LeGo(GameScript& owner, Daedalus::DaedalusVM& vm); + + static bool isRequired(Daedalus::DaedalusVM& vm); + }; + diff --git a/game/game/compatibility/mem32.cpp b/game/game/compatibility/mem32.cpp new file mode 100644 index 000000000..437bc8528 --- /dev/null +++ b/game/game/compatibility/mem32.cpp @@ -0,0 +1,183 @@ +#include "mem32.h" + +#include +#include +#include + +using namespace Tempest; + +Mem32::Mem32() { + /* + * [0x00001000 .. 0x80000000] - (2GB) user space + * [0x80000000 .. 0xc0000000] - (1GB) extra space(reserved for opengothic use; pinned memory) + * [0xc0000000 .. 0xffffffff] - (1GB) kernel space + */ + region.emplace_back(Region(0x1000,0x80000000)); + } + +Mem32::~Mem32() { + for(auto& rgn:region) { + if(rgn.status==S_Allocated && rgn.real!=nullptr) { + std::free(rgn.real); + rgn.real = nullptr; + } + } + } + +Mem32::ptr32_t Mem32::pin(void* mem, ptr32_t address, uint32_t size, const char* comment) { + for(size_t i=0; i(uint64_t(address))); + } + +void Mem32::compactage() { + for(size_t i=0; i+1status==S_Unused) { + Log::e("mem_writeint: address translation failure: ", reinterpret_cast(uint64_t(address))); + return; + } + address -= rgn->address; + auto ptr = reinterpret_cast(rgn->real)+address; + std::memcpy(ptr,&v,4); + } + +int32_t Mem32::readInt(ptr32_t address) { + auto rgn = translate(address); + if(rgn==nullptr || rgn->status==S_Unused) { + Log::e("mem_readint: address translation failure: ", reinterpret_cast(uint64_t(address))); + return 0; + } + address -= rgn->address; + auto ptr = reinterpret_cast(rgn->real)+address; + int32_t ret = 0; + std::memcpy(&ret,ptr,4); + return ret; + } + +void Mem32::copyBytes(ptr32_t psrc, ptr32_t pdst, uint32_t size) { + auto src = translate(psrc); + auto dst = translate(pdst); + if(src==nullptr || src->status==S_Unused) { + Log::e("mem_copybytes: address translation failure: ", reinterpret_cast(uint64_t(psrc))); + return; + } + if(dst==nullptr || dst->status==S_Unused) { + Log::e("mem_copybytes: address translation failure: ", reinterpret_cast(uint64_t(pdst))); + return; + } + + size_t sOff = psrc - src->address; + size_t dOff = pdst - dst->address; + size_t sz = size; + if(src->sizesize-sOff,sz); + } + if(dst->sizesize-dOff,sz); + } + std::memcpy(reinterpret_cast(dst->real)+sOff, + reinterpret_cast(src->real)+dOff, + sz); + } + +Mem32::Region* Mem32::translate(ptr32_t address) { + for(auto& rgn:region) { + if(rgn.address<=address && address +#include + +class Mem32 { + public: + Mem32(); + ~Mem32(); + + using ptr32_t = uint32_t; + static constexpr uint32_t memAlign = 8; + + ptr32_t pin (void* mem, ptr32_t address, uint32_t size, const char* comment = nullptr); + ptr32_t alloc(uint32_t size); + void free (ptr32_t at); + + void writeInt(ptr32_t address, int32_t v); + int32_t readInt (ptr32_t address); + void copyBytes(ptr32_t src, ptr32_t dst, uint32_t size); + + private: + enum Status:uint8_t { + S_Unused, + S_Allocated, + S_Pin, + }; + + struct Region { + Region(); + Region(ptr32_t b, uint32_t sz):address(b),size(sz){} + ptr32_t address = 0; + uint32_t size = 0; + void* real = nullptr; + const char* comment = nullptr; + Status status = S_Unused; + }; + + Region* translate(ptr32_t address); + void compactage(); + + std::vector region; + }; + diff --git a/game/game/gamescript.cpp b/game/game/gamescript.cpp index fbbe059f9..849dff0c1 100644 --- a/game/game/gamescript.cpp +++ b/game/game/gamescript.cpp @@ -10,6 +10,7 @@ #include "game/serialize.h" #include "game/globaleffects.h" #include "game/compatibility/ikarus.h" +#include "game/compatibility/lego.h" #include "world/objects/npc.h" #include "world/objects/item.h" #include "world/objects/interactive.h" @@ -408,6 +409,9 @@ void GameScript::initCommon() { if(Ikarus::isRequired(vm)) { plugins.emplace_back(std::make_unique(*this,vm)); } + if(LeGo::isRequired(vm)) { + plugins.emplace_back(std::make_unique(*this,vm)); + } } void GameScript::initDialogs() { diff --git a/lib/ZenLib b/lib/ZenLib index 4c6f35004..baef78d35 160000 --- a/lib/ZenLib +++ b/lib/ZenLib @@ -1 +1 @@ -Subproject commit 4c6f35004ab469fbaa3853eb0c8599663590c0be +Subproject commit baef78d35eaf41ab8888382be97e741aa2c00f1b