OpenGothic
Open source reimplementation of Gothic I and II
Loading...
Searching...
No Matches
directmemory.cpp
Go to the documentation of this file.
1#include "directmemory.h"
2
3#include <zenkit/DaedalusScript.hh>
4#include <Tempest/Log>
5
6#include <cassert>
7#include <charconv>
8
9#include "world/objects/npc.h"
10#include "gothic.h"
11
12using namespace Tempest;
13using namespace Compatibility;
14
15static uint32_t nextPot(uint32_t x) {
16 x--;
17 x |= x >> 1;
18 x |= x >> 2;
19 x |= x >> 4;
20 x |= x >> 8;
21 x |= x >> 16;
22 x++;
23 return x;
24 }
25
26static int32_t floatBitsToInt(float f) {
27 int32_t i = 0;
28 std::memcpy(&i, &f, sizeof(i));
29 return i;
30 }
31
32static float intBitsToFloat(int32_t i) {
33 float f = 0;
34 std::memcpy(&f, &i, sizeof(f));
35 return f;
36 }
37
38void DirectMemory::memory_instance::set_int(const zenkit::DaedalusSymbol& sym, uint16_t index, int32_t value) {
39 if(sym.name()=="ZCARRAY.NUMINARRAY" && value>1024)
40 Log::d("");
41 ptr32_t addr = address + ptr32_t(sym.offset_as_member()) + ptr32_t(index*sym.class_size());
42 owner.mem32.writeInt(addr, value);
43 }
44
45int32_t DirectMemory::memory_instance::get_int(const zenkit::DaedalusSymbol& sym, uint16_t index) const {
46 ptr32_t addr = address + ptr32_t(sym.offset_as_member()) + ptr32_t(index * sym.class_size());
47 int32_t v = owner.mem32.readInt(addr);
48 return v;
49 }
50
51void DirectMemory::memory_instance::set_float(const zenkit::DaedalusSymbol& sym, uint16_t index, float value) {
52 ptr32_t addr = address + ptr32_t(sym.offset_as_member()) + ptr32_t(index*sym.class_size());
53 owner.mem32.writeInt(addr, *reinterpret_cast<const int32_t*>(&value));
54 }
55
56float DirectMemory::memory_instance::get_float(const zenkit::DaedalusSymbol& sym, uint16_t index) const {
57 ptr32_t addr = address + ptr32_t(sym.offset_as_member()) + ptr32_t(index * sym.class_size());
58 int32_t v = owner.mem32.readInt(addr);
59
60 float f = 0;
61 std::memcpy(&f, &v, 4);
62 return f;
63 }
64
65void DirectMemory::memory_instance::set_string(const zenkit::DaedalusSymbol& sym, uint16_t index, std::string_view value) {
66 ptr32_t addr = address + ptr32_t(sym.offset_as_member()) + ptr32_t(index*sym.class_size());
67 if(auto* s = owner.mem32.deref<zString>(addr))
68 owner.memAssignString(*s, value);
69 }
70
71const std::string& DirectMemory::memory_instance::get_string(const zenkit::DaedalusSymbol& sym, uint16_t index) const {
72 ptr32_t addr = address + ptr32_t(sym.offset_as_member()) + ptr32_t(index*sym.class_size());
73
74 if(auto zs = owner.mem32.deref<const zString>(addr)) {
75 static std::string ret;
76 owner.memFromString(ret, *zs);
77 return ret;
78 }
79
80 Log::d("TODO: memory_instance::get_string ", sym.name());
81 (void)addr;
82 static std::string empty;
83 return empty;
84 }
85
86
87DirectMemory::DirectMemory(GameScript& owner, zenkit::DaedalusVm& vm) : gameScript(owner), vm(vm), cpu(*this, mem32) {
88 if(auto v = vm.find_symbol_by_name("Ikarus_Version")) {
89 const int version = v->type()==zenkit::DaedalusDataType::INT ? v->get_int() : 0;
90 Log::i("DMA mod detected: Ikarus v", version);
91 }
92 if(auto v = vm.find_symbol_by_name("LeGo_Version")) {
93 const char* version = v->type()==zenkit::DaedalusDataType::STRING ? v->get_string().c_str() : "LeGo";
94 Log::i("DMA mod detected: ", version);
95 }
96
97 setupFunctionTable();
98 setupIkarusLoops();
99 setupEngineText();
100 setupEngineMemory();
101
102 // Inline ASM
103 vm.override_function("ASMINT_Init", [this]() { ASMINT_Init(); });
104 vm.override_function("ASMINT_MyExternal", [](){});
105 vm.override_function("ASMINT_CallMyExternal", [this]() { ASMINT_CallMyExternal(); });
106
107 // Pointers
108 setupMemoryFunctions();
109
110 // Dynamic calls by id's
111 setupDirectFunctions();
112
113 setupWinapiFunctions();
114
115 setupUtilityFunctions();
116
117 // LeGo hooks
118 setupHookEngine();
119
120 setupMathFunctions();
121 setupStringsFunctions();
122
123 setupzCParserFunctions();
124
125 setupInitFileFunctions();
126 setupUiFunctions();
127 setupFontFunctions();
128 setupNpcFunctions();
129 setupWorldFunctions();
130
131 // various
132 vm.override_function("MEM_PrintStackTrace", [this](){ memPrintstacktraceImplementation(); });
133 vm.override_function("MEM_SendToSpy", [this](int cat, std::string_view msg){ return memSendToSpy(cat,msg); });
134 vm.override_function("MEM_ReplaceFunc", [this](zenkit::DaedalusFunction dest, zenkit::DaedalusFunction func){ memReplaceFunc(dest, func); });
135 //
136 vm.override_function("MEMINT_SetupExceptionHandler", [](){ });
137 vm.override_function("MEMINT_ReplaceSlowFunctions", [](){ });
138 //vm.override_function("MEMINT_ReplaceLoggingFunctions", [](){ });
139 vm.override_function("MEM_InitStatArrs", [](){ });
140 vm.override_function("MEM_InitRepeat", [](){ });
141 vm.override_function("MEM_GetCommandLine", [](){ return std::string(""); });
142
143 auto safeOverrideFunction = [&](std::string_view name, auto hook) {
144 auto f = vm.find_symbol_by_name(name);
145 if(f==nullptr || f->type()!=zenkit::DaedalusDataType::FUNCTION)
146 return;
147 vm.override_function(name, hook);
148 };
149
150 safeOverrideFunction("_RENDER_INIT", []() {
151 // unclear how exactly it should behave - need to find testing sample
152 Log::e("not implemented call [_RENDER_INIT]");
153 });
154 safeOverrideFunction("PRINT_FIXPS", [](){
155 // function patches asm code of zCView::PrintTimed* to fix text coloring - we can ignore it
156 });
157
158 // override for now: need to expose way-net system and provide dma-access for npc
159 safeOverrideFunction("TELEPORTNPCTOWP", [this](int npcId, std::string_view wpName){
160 auto wp = gameScript.world().findPoint(wpName,false);
161 if(wp==nullptr) {
162 Log::d("TeleportNpcToWP: invalid waypoint: ", wpName);
163 return;
164 }
165 auto npcRef = this->vm.find_symbol_by_index(uint32_t(npcId))->get_instance();
166 if(npcRef==nullptr || npcRef->user_ptr==nullptr)
167 return;
168 auto& npc = *reinterpret_cast<Npc*>(npcRef->user_ptr);
169 npc.setPosition (wp->position());
170 npc.setDirection(wp->direction());
171 });
172 //
173 safeOverrideFunction("LOG_MOVETOTOP", [](std::string_view topic) {
174 // need to have a projection of 'OCLOGMANAGER_PTR' into mem32
175 Log::e("not implemented call [LOG_MOVETOTOP]");
176 });
177
178 // Disable some high-level functions, until basic stuff is done
179 auto dummyfy = [&](std::string_view name, auto hook) {
180 auto f = vm.find_symbol_by_name(name);
181 if(f==nullptr || f->type()!=zenkit::DaedalusDataType::FUNCTION)
182 return;
183 vm.override_function(name, hook);
184 };
185 dummyfy("WRITENOP", [](int,int){}); // hook-related mess
186 dummyfy("MEM_SETKEYS", [](std::string_view,int,int){});
187 dummyfy("INIT_QUIVERS_ALWAYS", [](){}); // requires sorted npc list
188 }
189
190bool DirectMemory::isRequired(zenkit::DaedalusScript& vm) {
191 return
192 vm.find_symbol_by_name("Ikarus_Version") != nullptr &&
193 vm.find_symbol_by_name("MEM_InitAll") != nullptr &&
194 vm.find_symbol_by_name("MEM_ReadInt") != nullptr &&
195 vm.find_symbol_by_name("MEM_WriteInt") != nullptr &&
196 vm.find_symbol_by_name("_@") != nullptr &&
197 vm.find_symbol_by_name("_^") != nullptr;
198 }
199
200void DirectMemory::tick(uint64_t dt) {
201 //TODO: propper hook-engine
202 if(auto* sym = vm.find_symbol_by_name("_FF_Hook")) {
203 vm.call_function(sym);
204 }
205
206 tickUi(dt);
207 }
208
209void DirectMemory::eventPlayAni(std::string_view ani) {
210 // oCNPC__EV_PlayAni -> _AI_FUNCTION_EVENT
211 //TODO: propper hook-engine
212 if(ani.find("CALL ")!=0)
213 return;
214 //Log::d("LeGo::eventPlayAni: ", ani);
215#if 0
216
217 if(auto* sym = vm.find_symbol_by_name("_AI_FUNCTION_EVENT")) {
218 vm.push_string(ani);
219 vm.call_function(sym);
220 // vm.unsafe_jump(sym->address()+0x1b); // jump over asm-related stuff
221 return;
222 }
223 return;
224#endif
225
226 auto toInt = [](std::string_view ss) {
227 int32_t i = 0;
228 auto err = std::from_chars(ss.data(), ss.data()+ss.size(), i).ec;
229 if(err!=std::errc())
230 return 0;
231 return i;
232 };
233
234 auto toStr = [](std::string_view ss) {
235 std::string ret;
236 ret.reserve(ss.size());
237 for(size_t i=0; i<ss.size(); ++i) {
238 if(ss[i]=='\\' && i+1<ss.size()) {
239 ++i;
240 if(ss[i]=='_') {
241 ret.push_back(' ');
242 continue;
243 }
244 }
245 ret.push_back(ss[i]);
246 }
247 return ret;
248 };
249
250 auto v = ani.substr(5);
251 auto tok = v.substr(0, v.find(' '));
252 v = v.substr(v.find(' ')+1);
253
254 std::string_view args[10];
255 for(int i=0; i<10; ++i) {
256 size_t n = v.find(' ');
257 args[i] = v.substr(0, n);
258 if(n==std::string_view::npos)
259 break;
260 v = v.substr(n+1);
261 }
262
263 if(tok.size()>0 && std::isdigit(tok[0])) {
264 auto fncID = toInt(args[0]);
265 if(auto* sym = vm.find_symbol_by_index(uint32_t(fncID))) {
266 vm.call_function(sym);
267 }
268 return;
269 }
270 if(tok=="I") {
271 auto fncID = toInt(args[1]);
272 if(auto* sym = vm.find_symbol_by_index(uint32_t(fncID))) {
273 vm.call_function(sym, toInt(args[0]));
274 }
275 return;
276 }
277 if(tok=="S") {
278 auto fncID = toInt(args[1]);
279 if(auto* sym = vm.find_symbol_by_index(uint32_t(fncID))) {
280 //NOTE: need support in ZenKit, to use call_function with string&&
281 std::string arg0 = toStr(args[0]);
282 vm.call_function(sym, std::string_view(arg0));
283 }
284 return;
285 }
286 if(tok=="SS") {
287 auto fncID = toInt(args[2]);
288 if(auto* sym = vm.find_symbol_by_index(uint32_t(fncID))) {
289 std::string arg0 = toStr(args[0]);
290 std::string arg1 = toStr(args[1]);
291 vm.call_function(sym, std::string_view(arg0), std::string_view(arg1));
292 }
293 return;
294 }
295 if(tok=="NSII") {
296 auto fncID = toInt(args[4]);
297 auto npcID = toInt(args[0]);
298 auto npcS = vm.find_symbol_by_index(uint32_t(npcID));
299 auto inst = (npcS!=nullptr && npcS->type()==zenkit::DaedalusDataType::INSTANCE) ? npcS->get_instance() : nullptr;
300 if(auto* sym = vm.find_symbol_by_index(uint32_t(fncID))) {
301 (void)sym;
302 //NOTE: need support in ZenKit, to use call_function for externals
303 // vm.call_function(sym, inst, args[1], float(toInt(args[2])), float(toInt(args[3])));
304 }
305 return;
306 }
307
308 Log::d("Ikarus: skip ai-call: ", ani);
309 }
310
311void DirectMemory::setupFunctionTable() {
312 for(auto& i:vm.symbols()) {
313 if(i.type()!=zenkit::DaedalusDataType::FUNCTION)
314 continue;
315 if(i.address()==0)
316 continue;
317 symbolsByAddress.push_back(&i);
318 }
319
320 std::sort(symbolsByAddress.begin(), symbolsByAddress.end(), [](const zenkit::DaedalusSymbol* l, const zenkit::DaedalusSymbol* r){
321 return l->address() < r->address();
322 });
323 }
324
325void DirectMemory::setupIkarusLoops() {
326 // ## Traps
327 if(auto end = vm.find_symbol_by_name("END")) {
328 end->set_access_trap_enable(true);
329 }
330 if(auto break_ = vm.find_symbol_by_name("BREAK")) {
331 break_->set_access_trap_enable(true);
332 }
333 if(auto continue_ = vm.find_symbol_by_name("CONTINUE")) {
334 continue_->set_access_trap_enable(true);
335 }
336
337 auto end = vm.find_symbol_by_name("END");
338 auto rep = vm.find_symbol_by_name("REPEAT");
339 auto whl = vm.find_symbol_by_name("WHILE");
340 auto continue_ = vm.find_symbol_by_name("CONTINUE");
341
342 uint32_t endIdx = (end!=nullptr ? end->index() : uint32_t(-1));
343 uint32_t cntIdx = (continue_!=nullptr ? continue_->index() : uint32_t(-1));
344 uint32_t repAddr = (rep!=nullptr ? rep->address() : uint32_t(-1));
345 uint32_t whlAddr = (whl!=nullptr ? whl->address() : uint32_t(-1));
346
347 // auto cont = vm.find_symbol_by_name("CONTINUE");
348
349 // ## Control-flow ##
350 vm.override_function("repeat", [this](zenkit::DaedalusVm& vm) { return repeat(vm); });
351 vm.override_function("while", [this](zenkit::DaedalusVm& vm) { return while_(vm); });
352 vm.override_function("mem_goto", [this](zenkit::DaedalusVm& vm) { return mem_goto(vm); });
353 vm.register_access_trap([this](zenkit::DaedalusSymbol& i) { return loop_trap(&i); });
354
355 for(auto& i:vm.symbols()) {
356 if(i.type()!=zenkit::DaedalusDataType::FUNCTION)
357 continue;
358 }
359
360 struct LoopDecr {
361 uint32_t pc = 0;
362 uint32_t backJmp = 0;
363 };
364
365 std::vector<LoopDecr> loopStk;
366 for(uint32_t i=0; i<vm.size();) {
367 const auto pc = i;
368 auto instr = vm.instruction_at(i);
369 i += instr.size;
370
371 if(instr.op==zenkit::DaedalusOpcode::PUSHV && instr.symbol==endIdx) {
372 auto loop = loopStk.back();
373 loopBacktrack[loop.pc] = i; // loop exit
374 loopBacktrack[pc] = loop.backJmp;
375 loopStk.pop_back();
376 }
377 else if(instr.op==zenkit::DaedalusOpcode::PUSHV && instr.symbol==cntIdx) {
378 if(!loopStk.empty()) {
379 auto func = findSymbolByAddress(pc); (void)func;
380 auto loop = loopStk.back();
381 loopBacktrack[pc] = loop.pc;
382 }
383 }
384 else if(instr.op==zenkit::DaedalusOpcode::BL && (instr.address==repAddr || instr.address==whlAddr)) {
385 LoopDecr d;
386 if(instr.address==whlAddr) {
387 d.backJmp = traceBackExpression(vm, pc);
388 d.pc = pc;
389 }
390 else if(instr.address==repAddr) {
391 d.pc = pc;
392 d.backJmp = pc;
393 }
394 loopStk.push_back(d);
395 }
396 }
397 }
398
399void DirectMemory::setupEngineMemory() {
400 mem32.setCallbackR(Mem32::Type::zCParser, [this](zCParser& p, uint32_t){ memoryCallback(p, std::memory_order::acquire); });
401 mem32.setCallbackW(Mem32::Type::zCParser, [this](zCParser& p, uint32_t){ memoryCallback(p, std::memory_order::release); });
402
403 mem32.setCallbackR(Mem32::Type::zCParser_variables, [this](ScriptVar& v, uint32_t id) {
404 memoryCallback(v, id, std::memory_order::acquire);
405 });
406 mem32.setCallbackW(Mem32::Type::zCParser_variables, [this](ScriptVar& v, uint32_t id) {
407 memoryCallback(v, id, std::memory_order::release);
408 });
409 mem32.setCallbackR(Mem32::Type::zCPar_Symbol, [this](zCPar_Symbol& p, uint32_t id) {
410 memoryCallback(p, id, std::memory_order::acquire);
411 });
412 mem32.setCallbackW(Mem32::Type::zCPar_Symbol, [this](zCPar_Symbol& p, uint32_t id) {
413 memoryCallback(p, id, std::memory_order::release);
414 });
415
416 const ptr32_t BAD_BUILTIN_PTR = 0xBAD40000;
417 const ptr32_t GothicFirstInstructionAddress = 4198400;
418 const ptr32_t MEMINT_oGame_Pointer_Address = 11208836; //0xAB0884
419 const ptr32_t MEMINT_zTimer_Address = 10073044; //0x99B3D4
420 const ptr32_t MEMINT_gameMan_Pointer_Address = 9185624; //0x8C2958
421 const ptr32_t ZFACTORY = 9276912;
422 const ptr32_t ContentParserAddress = 11223232;
423 const ptr32_t ZFONTMAN = 11221460;
424 const ptr32_t OCNPCFOCUS__FOCUSLIST_G2 = 11208440;
425
426 mem32.pin(&versionHint, GothicFirstInstructionAddress, 4, "MEMINT_ReportVersionCheck");
427 mem32.pin(&oGame_Pointer, MEMINT_oGame_Pointer_Address, 4, "oGame*");
428 mem32.pin(&gameman_Ptr, MEMINT_gameMan_Pointer_Address, 4, "GameMan*");
429 mem32.pin(&zTimer, MEMINT_zTimer_Address, sizeof(zTimer), "zTimer");
430 mem32.pin(&zFactory_Ptr, ZFACTORY, 4, "zFactory*");
431 mem32.pin(&fontMan_Ptr, ZFONTMAN, 4, "zCFontMan*");
432 mem32.pin(ContentParserAddress, sizeof(zCParser), Mem32::Type::zCParser);
433 mem32.pin(&focusList, OCNPCFOCUS__FOCUSLIST_G2, sizeof(focusList), "OCNPCFOCUS__FOCUSLIST_G2");
434
435 const ptr32_t INGAME_MENU_INSTANCE = 8980576;
436 mem32.pin(menuName, INGAME_MENU_INSTANCE, sizeof(menuName)-1, "MENU_NAME");
437 std::strncpy(menuName, ::menuMain.data(), std::min(sizeof(menuName), ::menuMain.size()));
438
439 const ptr32_t ZERRPTR = 9231568;
440 mem32.alloc(ZERRPTR, sizeof(zError));
441
442 const ptr32_t LODENABLED = 8596020;
443 mem32.alloc(LODENABLED, 4, "LODENABLED"); // can always ignore that
444
445 const ptr32_t AMBIENTVOBSENABLED = 9079488;
446 mem32.alloc(AMBIENTVOBSENABLED, 4, "AMBIENTVOBSENABLED");
447
448 const ptr32_t GAME_HOLDTIME_ADDRESS = 11208840;
449 mem32.alloc(GAME_HOLDTIME_ADDRESS, 4, "GAME_HOLDTIME_ADDRESS"); // need to investigate how exactly it affects game
450
451 if(auto p = mem32.deref<std::array<ptr32_t,6>>(OCNPCFOCUS__FOCUSLIST_G2)) {
452 gameScript.focusMage();
453 (*p)[5] = BAD_BUILTIN_PTR; //TODO: pick-lock spells
454 }
455
456 // Trialog: need to implement reinterpret_cast to avoid in script-exception
457 const ptr32_t SPAWN_INSERTRANGE = 9153744;
458 mem32.pin(&spawnRange, SPAWN_INSERTRANGE, sizeof(spawnRange), "spawnRange");
459
460 // Storage for local variables, so script may address them thru pointers
461 scriptVariables = mem32.alloc(uint32_t(vm.symbols().size() * sizeof(ScriptVar)), Mem32::Type::zCParser_variables);
462 scriptSymbols = mem32.alloc(uint32_t(vm.symbols().size() * sizeof(zCPar_Symbol)), Mem32::Type::zCPar_Symbol);
463
464 // Built-in data without assumed address
465 oGame_Pointer = mem32.pin(&memGame, sizeof(memGame), "oGame");
466 memGame._ZCSESSION_WORLD = mem32.alloc(sizeof(oWorld));
467 memGame.WLDTIMER = BAD_BUILTIN_PTR;
468 memGame.TIMESTEP = 1; // used as boolean in anim8
469
470 auto& mem_world = *mem32.deref<oWorld>(memGame._ZCSESSION_WORLD);
471 mem_world.WAYNET = BAD_BUILTIN_PTR; //TODO: add implement some proxy to waynet
472 mem_world.VOBLIST_NPCS = 0; //BAD_BUILTIN_PTR; // zCListSort*
473
474 gameman_Ptr = mem32.alloc(sizeof(GameMgr));
475 if(auto v = vm.find_symbol_by_name("CurrSymbolTableLength")) {
476 v->set_int(int(vm.symbols().size()));
477 }
478
479 fontMan_Ptr = mem32.alloc(sizeof(zCFontMan));
480
481 // ## UI data
482 memGame.HPBAR = mem32.alloc(sizeof(oCViewStatusBar));
483 memGame.MANABAR = mem32.alloc(sizeof(oCViewStatusBar));
484 memGame.FOCUSBAR = mem32.alloc(sizeof(oCViewStatusBar));
485
486 memGame._ZCSESSION_VIEWPORT = mem32.alloc(sizeof(zCView));
487 memGame.ARRAY_VIEW[0] = mem32.alloc(sizeof(zCView));
488
489 mem32.deref<oCViewStatusBar>(memGame.HPBAR) ->RANGE_BAR = mem32.alloc(sizeof(zCView));
490 mem32.deref<oCViewStatusBar>(memGame.HPBAR) ->VALUE_BAR = mem32.alloc(sizeof(zCView));
491 mem32.deref<oCViewStatusBar>(memGame.FOCUSBAR)->RANGE_BAR = mem32.alloc(sizeof(zCView));
492 mem32.deref<oCViewStatusBar>(memGame.FOCUSBAR)->VALUE_BAR = mem32.alloc(sizeof(zCView));
493 }
494
495void DirectMemory::setupEngineText() {
496 // pin functions - CoM and some other mods rewriting assembly for them
497 const uint32_t OCNPC__ENABLE_EQUIPBESTWEAPONS = 7626662;
498 mem32.alloc(OCNPC__ENABLE_EQUIPBESTWEAPONS, 18, "OCNPC__ENABLE_EQUIPBESTWEAPONS");
499
500 const uint32_t OCNPC__GETNEXTENEMY = 7556941;
501 mem32.alloc(OCNPC__GETNEXTENEMY, 48, "OCNPC__GETNEXTENEMY");
502
503 const uint32_t OCITEMCONTAINER__CHECKSELECTEDITEM_ISACTIVE = 7378665;
504 mem32.alloc(OCITEMCONTAINER__CHECKSELECTEDITEM_ISACTIVE, 5, ".text");
505
506 const uint32_t OCITEMCONTAINER__CHECKSELECTEDITEM_ISACTIVEP = 7378700;
507 mem32.alloc(OCITEMCONTAINER__CHECKSELECTEDITEM_ISACTIVEP, 5, ".text");
508
509 const int OCSTEALCONTAINER__CREATELIST_ISARMOR_SP18 = 7384908;
510 mem32.alloc(OCSTEALCONTAINER__CREATELIST_ISARMOR_SP18, 8, ".text");
511
512 const int OCNPCCONTAINER__CREATELIST_ISARMOR_SP18 = 7386812;
513 mem32.alloc(OCNPCCONTAINER__CREATELIST_ISARMOR_SP18, 8, ".text");
514
515 const int OCSTEALCONTAINER__CREATELIST_ISARMOR = 7384900;
516 mem32.alloc(OCSTEALCONTAINER__CREATELIST_ISARMOR, 8, ".text");
517
518 const int OCNPCCONTAINER__CREATELIST_ISARMOR = 7386805;
519 mem32.alloc(OCNPCCONTAINER__CREATELIST_ISARMOR, 5, ".text");
520
521 const int OCNPCCONTAINER__HANDLEEVENT_ISEMPTY = 7387581;
522 mem32.alloc(OCNPCCONTAINER__HANDLEEVENT_ISEMPTY, 5, ".text");
523
524 const int OCNPCINVENTORY__HANDLEEVENT_KEYWEAPONJZ = 7402077;
525 mem32.alloc(OCNPCINVENTORY__HANDLEEVENT_KEYWEAPONJZ, 4, ".text");
526
527 const int OCNPCINVENTORY__HANDLEEVENT_KEYWEAPON = 7402065;
528 mem32.alloc(OCNPCINVENTORY__HANDLEEVENT_KEYWEAPON, 8, ".text");
529
530 const int OCAIHUMAN__CHANGECAMMODEBYSITUATION_SWITCHMOBCAM = 6935573;
531 mem32.alloc(OCAIHUMAN__CHANGECAMMODEBYSITUATION_SWITCHMOBCAM, 8, ".text");
532
533 // unknown, code segment used by INIT_RESTOREMOBFIRESTATES in CoM
534 const int INIT_RESTOREMOBFIRESTATES_P0 = 7482368;
535 const int INIT_RESTOREMOBFIRESTATES_P1 = 7482688;
536 mem32.alloc(INIT_RESTOREMOBFIRESTATES_P0, 4, ".text");
537 mem32.alloc(INIT_RESTOREMOBFIRESTATES_P1, 4, ".text");
538 }
539
540zenkit::DaedalusNakedCall DirectMemory::repeat(zenkit::DaedalusVm& vm) {
541 const int len = vm.pop_int();
542 zenkit::DaedalusSymbol* i = std::get<zenkit::DaedalusSymbol*>(vm.pop_reference());
543
544 auto rp = vm.instruction_at(vm.pc());
545 if(len==0 || i==nullptr) {
546 auto jmp = loopBacktrack[vm.pc()];
547 vm.unsafe_jump(jmp-rp.size);
548 return zenkit::DaedalusNakedCall();
549 }
550 // Log::i("repeat: ", i->get_int(), " < ", len);
551 auto& pl = loopPayload[vm.pc()];
552 pl.i = i;
553 pl.len = len;
554 i->set_int(0);
555 if(len>1024)
556 Log::d("");
557 return zenkit::DaedalusNakedCall();
558 }
559
560zenkit::DaedalusNakedCall DirectMemory::while_(zenkit::DaedalusVm& vm) {
561 const int cond = vm.pop_int();
562 // Log::i("while: ", cond);
563 if(cond!=0) {
564 return zenkit::DaedalusNakedCall();
565 }
566 auto rp = vm.instruction_at(vm.pc());
567 auto jmp = loopBacktrack[vm.pc()];
568 vm.unsafe_jump(jmp-rp.size);
569 return zenkit::DaedalusNakedCall();
570 }
571
572zenkit::DaedalusNakedCall DirectMemory::mem_goto(zenkit::DaedalusVm& vm) {
573 const int idx = vm.pop_int();
574
575 auto at = vm.pc();
576 auto func = findSymbolByAddress(at);
577 if(func==nullptr) {
578 Log::e("Ikarus: invalid vm state");
579 return zenkit::DaedalusNakedCall();
580 }
581
582 auto rp = vm.instruction_at(at);
583 auto label = vm.find_symbol_by_name("mem_label");
584 uint32_t lblAddr = (label!=nullptr ? label->address() : uint32_t(-1));
585 uint32_t pc = func->address();
586
587 for(uint32_t i=0, prev=0; i<at; ++i) {
588 auto instr = vm.instruction_at(pc);
589 if(instr.op==zenkit::DaedalusOpcode::BL && instr.address==lblAddr) {
590 auto lId = vm.instruction_at(prev);
591 if(lId.immediate==idx) {
592 vm.unsafe_jump(pc + instr.size - rp.size);
593 return zenkit::DaedalusNakedCall();
594 }
595 }
596 prev = pc;
597 pc += instr.size;
598 }
599
600 Log::e("Ikarus: invalid goto");
601 return zenkit::DaedalusNakedCall();
602 }
603
604void DirectMemory::loop_trap(zenkit::DaedalusSymbol* i) {
605 auto instr = vm.instruction_at(vm.pc());
606 if(instr.op != zenkit::DaedalusOpcode::PUSHV)
607 return; // Ikarus keywords are always use pushv
608
609 auto end = vm.find_symbol_by_name("END");
610 if(end!=nullptr && instr.symbol==end->index()) {
611 auto bt = loopBacktrack[vm.pc()];
612
613 if(loopPayload.find(bt)==loopPayload.end()) {
614 // while
615 vm.unsafe_jump(bt-instr.size);
616 return;
617 }
618
619 auto& pl = loopPayload[bt];
620 if(pl.i==nullptr) {
621 Log::e("bad loop end");
622 assert(0);
623 return;
624 }
625 const int32_t i = pl.i->get_int();
626 pl.i->set_int(i+1);
627 if(i+1 < pl.len) {
628 const uint32_t BLsize = 5;
629 vm.unsafe_jump(bt-instr.size+BLsize);
630 } else {
631 loopPayload.erase(bt);
632 }
633 return;
634 }
635
636 if(loopBacktrack.find(vm.pc())!=loopBacktrack.end()) {
637 Log::e("bad loop trap");
638 assert(0);
639 }
640 }
641
642uint32_t DirectMemory::traceBackExpression(zenkit::DaedalusVm& vm, uint32_t pc) {
643 /*
644 NOTE: can be expression based 'while';
645 while(var){}
646 PUSHV @var
647 BL @while
648
649 while(i < l) {}
650 PUSHV @l
651 PUSHV @i
652 LT
653 BL
654
655 handles by MEMINT_TraceParameter in ikarus
656 */
657
658 auto func = findSymbolByAddress(pc);
659 if(func==nullptr)
660 return uint32_t(-1); //error
661
662 // if(func->name()=="LIST_END")
663 // Log::d("");
664
665 std::vector<zenkit::DaedalusInstruction> icodes;
666 for(uint32_t i=func->address(); i<pc && i<vm.size();) {
667 auto instr = vm.instruction_at(i);
668 i += instr.size;
669 icodes.push_back(instr);
670 }
671
672 if(icodes.empty())
673 return uint32_t(-1); //error
674
675 int paramsNeeded = 1;
676 int instancesNeed = 0;
677 size_t at = icodes.size();
678 while((paramsNeeded>0 || instancesNeed>0) && at>0) {
679 --at;
680 const auto instr = icodes[at];
681 if(zenkit::DaedalusOpcode::ADD <= instr.op && instr.op <= zenkit::DaedalusOpcode::DIVMOVI)
682 paramsNeeded += 1;
683 else if(instr.op==zenkit::DaedalusOpcode::PUSHI) {
684 paramsNeeded -= 1;
685 }
686 else if(instr.op==zenkit::DaedalusOpcode::PUSHV || instr.op==zenkit::DaedalusOpcode::PUSHVI) {
687 auto* sym = vm.find_symbol_by_index(instr.symbol);
688 if(sym!=nullptr && sym->is_member())
689 instancesNeed = 1; // need instance to be set
690 paramsNeeded -= 1;
691 }
692 else if(instr.op==zenkit::DaedalusOpcode::GMOVI) {
693 instancesNeed = 0;
694 }
695 else if(instr.op==zenkit::DaedalusOpcode::BL) {
696 auto sym = vm.find_symbol_by_address(instr.address);
697 if(sym==nullptr)
698 return uint32_t(-1); //error
699
700 paramsNeeded += sym->count();
701 if(sym->has_return())
702 paramsNeeded -= 1;
703 }
704 else {
705 // non handled
706 return uint32_t(-1); //error
707 }
708 }
709
710 if(paramsNeeded!=0)
711 return uint32_t(-1); //error
712
713 pc = func->address();
714 for(uint32_t i=0; i<at; ++i) {
715 auto instr = vm.instruction_at(pc);
716 pc += instr.size;
717 }
718
719 return pc;
720 }
721
722const zenkit::DaedalusSymbol* DirectMemory::findSymbolByAddress(uint32_t addr) {
723 auto fn = std::lower_bound(symbolsByAddress.begin(), symbolsByAddress.end(), addr, [](const zenkit::DaedalusSymbol* l, uint32_t r){
724 return l->address()<r;
725 });
726 //auto id = std::distance(symbolsByAddress.begin(), fn); (void)id;
727 if(fn==symbolsByAddress.end())
728 return symbolsByAddress.back();
729 if((*fn)->address()==addr)
730 return *fn;
731 if(fn==symbolsByAddress.begin())
732 return nullptr;
733 --fn;
734 return *fn;
735 }
736
737std::string_view DirectMemory::demangleAddress(uint32_t addr){
738 for(auto& i:vm.symbols()) {
739 if(!i.is_const() || i.is_member())
740 continue;
741 if(i.type()!=zenkit::DaedalusDataType::INT)// && i.type() != zenkit::DaedalusDataType::FUNCTION)
742 continue;
743 if(ptr32_t(i.get_int())!=addr)
744 continue;
745 return i.name();
746 }
747 return "";
748 }
749
750std::string_view DirectMemory::menuMain() const {
751 return menuName;
752 }
753
754void DirectMemory::memoryCallback(zCParser& p, std::memory_order ord) {
755 if(ord==std::memory_order::acquire) {
756 if(p.symtab_table.ptr==0) {
757 // NOTE: initialize once
758 // currSymbolTableAddress / currSymbolTableLength
759 p.symtab_table.numInArray = int32_t(vm.symbols().size());
760 p.symtab_table.numAlloc = int32_t(nextPot(uint32_t(p.symtab_table.numInArray)));
761 p.symtab_table.ptr = mem32.realloc(p.symtab_table.ptr, uint32_t(p.symtab_table.numAlloc)*uint32_t(sizeof(uint32_t)));
762 auto v = mem32.deref<ptr32_t>(p.symtab_table.ptr);
763 for(int32_t i=0; i<p.symtab_table.numInArray; ++i) {
764 v[i] = scriptSymbols + uint32_t(i)*uint32_t(sizeof(zCPar_Symbol));
765 }
766
767 // TODO: currSortedSymbolTableAddress
768 // TODO: currParserStackAddress
769 }
770
771 p.stack_stack = 0; //TODO: map insteructions into mem32?
772
773 auto trap = vm.instruction_at(vm.pc());
774 p.stack_stackptr = vm.pc() + trap.size;
775 }
776 else {
777 auto instr = vm.instruction_at(vm.pc());
778
779 auto src = findSymbolByAddress(vm.pc() - instr.size);
780 auto dst = findSymbolByAddress(p.stack_stackptr);
781
782 if(src!=dst || dst==nullptr) {
783 if(src!=nullptr && dst!=nullptr)
784 Log::e("FIXME: unsafe jump: `\"", src->name(), "\" -> \"", dst->name(), "\""); else
785 Log::e("FIXME: unsafe jump!");
786 return;
787 }
788 vm.unsafe_jump(p.stack_stackptr - instr.size);
789 }
790 }
791
792void DirectMemory::memoryCallback(ScriptVar& v, uint32_t index, std::memory_order ord) {
793 if(index>=vm.symbols().size()) {
794 // should never happend
795 Log::d("ikarus: symbol table is corrupted");
796 assert(false);
797 return;
798 }
799
800 auto& str = v.data;
801 auto& sym = *vm.find_symbol_by_index(index);
802 if(sym.is_member()) {
803 Log::e("Ikarus: accessing member symbol (\"", sym.name(), "\")");
804 return;
805 }
806
807 switch (sym.type()) {
808 case zenkit::DaedalusDataType::FLOAT: {
809 if(ord==std::memory_order::release) {
810 //TODO
811 Log::e("Ikarus: unable to write to mapped symbol (\"", sym.name(), "\")");
812 } else {
813 auto val = sym.get_float();
814 str._VTBL = floatBitsToInt(val); // first 4 bytes
815 Log::d("VAR: ", sym.name(), " -> ", val);
816 }
817 break;
818 }
819 case zenkit::DaedalusDataType::INT: {
820 if(ord==std::memory_order::release) {
821 sym.set_int(str._VTBL);
822 } else {
823 auto val = sym.get_int();
824 str._VTBL = val; // first 4 bytes
825 }
826 break;
827 }
828 case zenkit::DaedalusDataType::STRING: {
829 if(ord==std::memory_order::release) {
830 //NOTE: since string is composite object, mixing reads and writes can cause bugs
831 /*
832 std::string dst;
833 memFromString(dst, str);
834 sym.set_string(dst);
835 Log::d("VAR: ", sym.name(), " {", str.ptr, "} <- ", dst);
836 */
837 Log::e("Ikarus: unable to write to mapped symbol (\"", sym.name(), "\")");
838 } else {
839 auto& cstr = sym.get_string();
840 memAssignString(str, cstr);
841 // Log::d("VAR: ", sym.name(), " {", str.ptr, "} -> ", chr);
842 }
843 break;
844 }
845 default:
846 Log::e("Ikarus: unable to map symbol (\"", sym.name(), "\") to virtual memory");
847 break;
848 }
849 }
850
851void DirectMemory::memoryCallback(zCPar_Symbol& s, uint32_t index, std::memory_order ord) {
852 if(index>=vm.symbols().size()) {
853 // should never happend
854 Log::e("ikarus: symbol table is corrupted");
855 assert(false);
856 return;
857 }
858
859 if(ord!=std::memory_order::acquire) {
860 auto& sym = *vm.find_symbol_by_index(index);
861 Log::e("ikarus: write to symbol ", sym.name()," table is not implemented");
862 return;
863 }
864
865 auto symBitfield = [](zenkit::DaedalusSymbol& sym) {
866 int32_t flags = 0;
867 if(sym.is_const())
868 flags |= zenkit::DaedalusSymbolFlag::CONST;
869 if(sym.has_return())
870 flags |= zenkit::DaedalusSymbolFlag::RETURN;
871 if(sym.is_member())
872 flags |= zenkit::DaedalusSymbolFlag::MEMBER;
873 if(sym.is_external())
874 flags |= zenkit::DaedalusSymbolFlag::EXTERNAL;
875 if(sym.is_merged())
876 flags |= zenkit::DaedalusSymbolFlag::MERGED;
877
878 int32_t bitfield = 0;
879 bitfield |= (sym.count() & 0xFFF); // 12 bits
880 bitfield |= (int32_t(sym.type()) << 12); // 4 bits
881 bitfield |= (flags << 16); // 6 bits
882 return bitfield;
883 };
884
885 auto symOffset = [](zenkit::DaedalusSymbol& sym) {
886 if(sym.is_member()) {
887 return int32_t(sym.offset_as_member());
888 }
889 else if(sym.type()==zenkit::DaedalusDataType::CLASS) {
890 // see 'create' in LeGo
891 return int32_t(sym.class_size());
892 }
893 else if(sym.type()==zenkit::DaedalusDataType::FUNCTION) {
894 // return int32_t(sym.address());
895 return int32_t(sym.rtype());
896 }
897 // TODO: pointer to value
898 return 0;
899 };
900
901 auto symContent = [](zenkit::DaedalusSymbol& sym) {
902 switch (sym.type()) {
903 case zenkit::DaedalusDataType::VOID:
904 return 0;
905 case zenkit::DaedalusDataType::FLOAT:
906 return floatBitsToInt(sym.get_float());
907 case zenkit::DaedalusDataType::INT:
908 return int32_t(sym.get_int());
909 case zenkit::DaedalusDataType::FUNCTION:
910 if(sym.is_const())
911 return int32_t(sym.address());
912 return int32_t(sym.get_int());
913 case zenkit::DaedalusDataType::STRING:
914 case zenkit::DaedalusDataType::CLASS:
915 case zenkit::DaedalusDataType::PROTOTYPE:
916 case zenkit::DaedalusDataType::INSTANCE:
917 //TODO
918 return 0;
919 }
920 return 0;
921 };
922
923 auto& sym = *vm.find_symbol_by_index(index);
924 memAssignString(s.name, sym.name());
925 s.next = scriptSymbols + uint32_t(index+1)*uint32_t(sizeof(zCPar_Symbol)); // ???
926 //---
927 s.content = symContent(sym);
928 s.offset = symOffset(sym);
929 //---
930 s.bitfield = symBitfield(sym);
931 s.filenr = int32_t(sym.file_index());
932 s.line = int32_t(sym.line_start());
933 s.line_anz = int32_t(sym.line_count());
934 s.pos_beg = int32_t(sym.char_start());
935 s.pos_anz = int32_t(sym.char_count());
936 //---
937 s.parent = scriptSymbols + uint32_t(sym.parent())*uint32_t(sizeof(zCPar_Symbol));
938
939 // other flags are internal
940 // Log::d("Ikarus: accessing symbol (\"", sym.name(), "\")");
941 }
942
943void DirectMemory::memAssignString(zString& str, std::string_view cstr) {
944 str.ptr = mem32.realloc(str.ptr, uint32_t(cstr.size()+1));
945 str.len = int32_t(cstr.size());
946
947 auto* chr = reinterpret_cast<char*>(mem32.derefv(str.ptr, uint32_t(str.len)));
948 std::memcpy(chr, cstr.data(), cstr.length());
949 chr[str.len] = '\0';
950 }
951
952void DirectMemory::memFromString(std::string& dst, const zString& str) {
953 if(str.len<=0) {
954 dst.clear();
955 return;
956 }
957
958 if(const char* chr = reinterpret_cast<const char*>(mem32.deref(str.ptr, uint32_t(str.len)))) {
959 dst.resize(size_t(str.len));
960 std::memcpy(dst.data(), chr, dst.size());
961 }
962 }
963
964
965void DirectMemory::memPrintstacktraceImplementation() {
966 static bool enable = true;
967 if(!enable)
968 return;
969 Log::e("[start of stacktrace]");
970 vm.print_stack_trace();
971 Log::e("[end of stacktrace]");
972 }
973
974void DirectMemory::memSendToSpy(int cat, std::string_view msg) {
975 Log::d("[zpy]: ", msg);
976 }
977
978void DirectMemory::memReplaceFunc(zenkit::DaedalusFunction dest, zenkit::DaedalusFunction func) {
979 auto* sf = func.value;
980 auto* sd = dest.value;
981 if(sd->name()=="MEM_SENDTOSPY")
982 return;
983 if(sd->name()=="MEM_PRINTSTACKTRACE")
984 return;
985 Log::d("mem_replacefunc: ",sd->name()," -> ",sf->name());
986 }
987
988void DirectMemory::ASMINT_Init() {
989 const int ASMINT_InternalStackSize = 1024;
990
991 if(!ASMINT_InternalStack) {
992 ASMINT_InternalStack = mem32.alloc(4 * ASMINT_InternalStackSize);
993 }
994 if(auto sym = vm.find_symbol_by_name("ASMINT_InternalStack")) {
995 if(sym->type()==zenkit::DaedalusDataType::INT)
996 sym->set_int(int32_t(ASMINT_InternalStack));
997 }
998
999 auto p = mem32.pin(&ASMINT_CallTarget, sizeof(ASMINT_CallTarget), "ASMINT_CallTarget");
1000 if(auto sym = vm.find_symbol_by_name("ASMINT_CallTarget")) {
1001 if(sym->type()==zenkit::DaedalusDataType::INT)
1002 sym->set_int(int32_t(p));
1003 }
1004 }
1005
1006void DirectMemory::ASMINT_CallMyExternal() {
1007 auto mem = mem32.deref(ASMINT_CallTarget);
1008 auto ins = reinterpret_cast<const uint8_t*>(std::get<0>(mem));
1009 auto len = std::get<1>(mem);
1010
1011 cpu.exec(ASMINT_CallTarget, ins, len);
1012 }
1013
1014void DirectMemory::setupMemoryFunctions() {
1015 vm.override_function("MEM_GetAddress_Init", [ ](){ });
1016
1017 vm.override_function("MEM_PtrToInst", [this](int address) { return mem_ptrtoinst(ptr32_t(address)); });
1018 vm.override_function("_^", [this](int address) { return mem_ptrtoinst(ptr32_t(address)); });
1019 vm.override_function("MEM_InstToPtr", [this](int index) { return mem_insttoptr(index); });
1020 vm.override_function("MEM_GetIntAddress", [this](zenkit::DaedalusVm& vm){ return _takeref(vm); });
1021 vm.override_function("_@", [this](zenkit::DaedalusVm& vm){ return _takeref(vm); });
1022 vm.override_function("MEM_GetStringAddress", [this](zenkit::DaedalusVm& vm){ return _takeref(vm); });
1023 vm.override_function("_@s", [this](zenkit::DaedalusVm& vm){ return _takeref(vm); });
1024 vm.override_function("MEM_GetFloatAddress", [this](zenkit::DaedalusVm& vm){ return _takeref(vm); });
1025 vm.override_function("_@f", [this](zenkit::DaedalusVm& vm){ return _takeref(vm); });
1026
1027 vm.override_function("MEM_ReadInt", [this](int address){ return mem_readint(address); });
1028 vm.override_function("MEM_WriteInt", [this](int address, int val){ mem_writeint(address, val); });
1029 vm.override_function("MEM_CopyBytes", [this](int src, int dst, int size){ mem_copybytes(src, dst, size); });
1030 vm.override_function("MEM_ReadString", [this](int sym){ return mem_readstring(sym); });
1031 vm.override_function("MEM_ReadStatArr", [this](zenkit::DaedalusVm& vm){ return mem_readstatarr(vm); });
1032 vm.override_function("MEM_WriteStatArr", [this](zenkit::DaedalusVm& vm){ return mem_writestatarr(vm); });
1033
1034 // ## MEM_Alloc and MEM_Free ##
1035 vm.override_function("MEM_Alloc", [this](int amount ) { return mem_alloc(amount); });
1036 vm.override_function("MEM_Free", [this](int address) { mem_free(address); });
1037 vm.override_function("MEM_Realloc", [this](int address, int oldsz, int size) { return mem_realloc(address,oldsz,size); });
1038 }
1039
1040auto DirectMemory::_takeref(zenkit::DaedalusVm& vm) -> zenkit::DaedalusNakedCall {
1041 if(vm.top_is_reference()) {
1042 auto [ref, idx, context] = vm.pop_reference();
1043 if(idx!=0) {
1044 Log::e("Ikarus: _takeref - unable take reference to array element");
1045 vm.push_int(int32_t(0xBAD10000));
1046 return zenkit::DaedalusNakedCall();
1047 }
1048
1049 if(ref->is_member()) {
1050 if(auto d = dynamic_cast<memory_instance*>(context.get())) {
1051 const ptr32_t ptr = d->address;
1052 vm.push_int(int32_t(ptr + ref->offset_as_member()));
1053 return zenkit::DaedalusNakedCall();
1054 }
1055 Log::e("Ikarus: _takeref - unable take reference to struct element");
1056 vm.push_int(int32_t(0xBAD10000));
1057 return zenkit::DaedalusNakedCall();
1058 }
1059
1060 const uint32_t id = ref->index();
1061 const ptr32_t ptr = scriptVariables + id*ptr32_t(sizeof(ScriptVar));
1062 if(false && ref->type()==zenkit::DaedalusDataType::STRING) {
1063 if(auto* s = mem32.deref<zString>(ptr))
1064 memAssignString(*s, ref->get_string(0));
1065 ref->set_instance(std::make_shared<memory_instance>(*this, ptr));
1066 }
1067 vm.push_int(int32_t(ptr));
1068 return zenkit::DaedalusNakedCall();
1069 }
1070
1071 auto symbol = vm.pop_int();
1072 auto sym = vm.find_symbol_by_index(uint32_t(symbol));
1073 if(sym==nullptr) {
1074 Log::e("Ikarus: _takeref - unable to resolve symbol");
1075 vm.push_int(int32_t(0xBAD10000));
1076 return zenkit::DaedalusNakedCall();
1077 }
1078
1079 if(sym->type()==zenkit::DaedalusDataType::INSTANCE) {
1080 auto inst = sym->get_instance().get();
1081 if(auto d = dynamic_cast<memory_instance*>(inst)) {
1082 const ptr32_t ptr = d->address;
1083 vm.push_int(int32_t(ptr));
1084 return zenkit::DaedalusNakedCall();
1085 }
1086 else if(inst!=nullptr) {
1087 // HACK: ScriptVar doesn't really have any backing of data
1088 const ptr32_t ptr = scriptVariables + inst->symbol_index()*ptr32_t(sizeof(ScriptVar));
1089 vm.push_int(int32_t(ptr));
1090 return zenkit::DaedalusNakedCall();
1091 }
1092 }
1093
1094 if(sym->type()==zenkit::DaedalusDataType::STRING) {
1095 Log::e("Ikarus: _takeref - not a memory-instance: ", sym->name());
1096 vm.push_int(int32_t(0xBAD20000));
1097 return zenkit::DaedalusNakedCall();
1098 }
1099
1100 Log::e("Ikarus: _takeref - not a memory-instance: ", sym->name());
1101 vm.push_int(int32_t(0xBAD20000));
1102 return zenkit::DaedalusNakedCall();
1103 }
1104
1105std::shared_ptr<zenkit::DaedalusInstance> DirectMemory::mem_ptrtoinst(ptr32_t address) {
1106 if(address==0)
1107 Log::d("mem_ptrtoinst: address is null");
1108 if(scriptVariables<=address && address<scriptVariables + ptr32_t(vm.symbols().size())*ptr32_t(sizeof(ScriptVar))) {
1109 // HACK: need to be consistent with _takeref
1110 uint32_t sId = (address-scriptVariables)/ptr32_t(sizeof(ScriptVar));
1111 auto sym = vm.find_symbol_by_index(sId);
1112 if(sym!=nullptr && sym->type()==zenkit::DaedalusDataType::INSTANCE)
1113 return sym->get_instance();
1114 }
1115 return std::make_shared<memory_instance>(*this, address);
1116 }
1117
1118int DirectMemory::mem_insttoptr(int index) {
1119 auto* sym = vm.find_symbol_by_index(uint32_t(index));
1120 if(sym==nullptr || sym->type() != zenkit::DaedalusDataType::INSTANCE) {
1121 Log::e("MEM_InstToPtr: Invalid instance: ", index);
1122 return 0;
1123 }
1124
1125 const std::shared_ptr<zenkit::DaedalusInstance>& inst = sym->get_instance();
1126 if(auto mem = dynamic_cast<memory_instance*>(inst.get())) {
1127 return int32_t(mem->address);
1128 }
1129
1130 // Log::d("MEM_InstToPtr: not a memory instance");
1131 // HACK: ScriptVar doesn't really have any backing of data
1132 const ptr32_t ptr = scriptVariables + inst->symbol_index()*ptr32_t(sizeof(ScriptVar));
1133 return int32_t(ptr);
1134 }
1135
1136int DirectMemory::mem_readint(int address) {
1137 return mem32.readInt(ptr32_t(address));
1138 }
1139
1140void DirectMemory::mem_writeint(int address, int val) {
1141 mem32.writeInt(uint32_t(address),val);
1142 }
1143
1144void DirectMemory::mem_copybytes(int src, int dst, int size) {
1145 mem32.copyBytes(ptr32_t(src),ptr32_t(dst),ptr32_t(size));
1146 }
1147
1148std::string DirectMemory::mem_readstring(int address) {
1149 if(auto s = mem32.deref<const zString>(ptr32_t(address))) {
1150 std::string str;
1151 memFromString(str, *s);
1152 return str;
1153 }
1154 return "";
1155 }
1156
1157zenkit::DaedalusNakedCall DirectMemory::mem_readstatarr(zenkit::DaedalusVm& vm) {
1158 const int index = vm.pop_int();
1159 auto [ref, idx, context] = vm.pop_reference();
1160
1161 const int ret = vm.get_int(context, ref, uint16_t(idx+index));
1162
1163 vm.push_int(ret);
1164 return zenkit::DaedalusNakedCall();
1165 }
1166
1167auto DirectMemory::mem_writestatarr(zenkit::DaedalusVm& vm) -> zenkit::DaedalusNakedCall {
1168 const int value = vm.pop_int();
1169 const int index = vm.pop_int();
1170 auto [ref, idx, context] = vm.pop_reference();
1171
1172 vm.set_int(context, ref, uint16_t(idx+index), value);
1173 return zenkit::DaedalusNakedCall();
1174 }
1175
1176int DirectMemory::mem_alloc(int amount) {
1177 if(amount==0) {
1178 Log::d("alocation zero bytes");
1179 return 0;
1180 }
1181 auto ptr = mem32.alloc(uint32_t(amount));
1182 return int32_t(ptr);
1183 }
1184
1185int DirectMemory::mem_alloc(int amount, const char* comment) {
1186 if(amount==0) {
1187 Log::d("alocation zero bytes");
1188 return 0;
1189 }
1190 auto ptr = mem32.alloc(uint32_t(amount), comment);
1191 return int32_t(ptr);
1192 }
1193
1194void DirectMemory::mem_free(int ptr) {
1195 mem32.free(Mem32::ptr32_t(ptr));
1196 }
1197
1198int DirectMemory::mem_realloc(int address, int oldsz, int size) {
1199 auto ptr = mem32.realloc(Mem32::ptr32_t(address), uint32_t(size));
1200 return int32_t(ptr);
1201 }
1202
1203
1204void DirectMemory::setupDirectFunctions() {
1205 vm.override_function("MEM_GetFuncIdByOffset", [this](int off) { return mem_getfuncidbyoffset(off); });
1206 vm.override_function("MEM_AssignInst", [this](int index, int ptr) { mem_assigninst(index, ptr); });
1207
1208 vm.override_function("MEM_CallByID", [this](zenkit::DaedalusVm& vm) { return mem_callbyid(vm); });
1209 vm.override_function("MEM_CallByPtr", [this](zenkit::DaedalusVm& vm) { return mem_callbyptr(vm); });
1210
1211 vm.override_function("memint_stackpushint", [this](zenkit::DaedalusVm& vm) { return memint_stackpushint (vm); });
1212 vm.override_function("memint_stackpushinst", [this](zenkit::DaedalusVm& vm) { return memint_stackpushinst(vm); });
1213 vm.override_function("memint_stackpushvar", [this](zenkit::DaedalusVm& vm) { return memint_stackpushvar (vm); });
1214
1215 vm.override_function("memint_popstring", [this](zenkit::DaedalusVm& vm) { return memint_popstring(vm); });
1216
1217 vm.override_function("mem_popintresult", [this](zenkit::DaedalusVm& vm) { return mem_popintresult(vm); });
1218 vm.override_function("mem_popstringresult", [this](zenkit::DaedalusVm& vm) { return mem_popstringresult(vm); });
1219 vm.override_function("mem_popinstresult", [this](zenkit::DaedalusVm& vm) { return mem_popinstresult(vm); });
1220 }
1221
1222int DirectMemory::mem_getfuncidbyoffset(int off) {
1223 if(off==0) {
1224 // MEM_INITALL
1225 return 0;
1226 }
1227 Log::e("TODO: mem_getfuncidbyoffset ", off);
1228 return 0;
1229 }
1230
1231void DirectMemory::mem_assigninst(int index, int ptr) {
1232 auto* sym = vm.find_symbol_by_index(uint32_t(index));
1233 if(sym==nullptr) {
1234 Log::e("MEM_AssignInst: Invalid instance: ",index);
1235 return;
1236 }
1237 sym->set_instance(std::make_shared<memory_instance>(*this, ptr32_t(ptr)));
1238 }
1239
1240void DirectMemory::directCall(zenkit::DaedalusVm& vm, zenkit::DaedalusSymbol& func) {
1241 if(func.type()!=zenkit::DaedalusDataType::FUNCTION) {
1242 Log::e("Bad unsafe function call");
1243 return;
1244 }
1245
1246 std::span<zenkit::DaedalusSymbol> params = vm.find_parameters_for_function(&func);
1247 if(params.size()>0)
1248 Log::d("");
1249 //vm.call_function(sym);
1250 //zenkit::StackGuard guard {&vm, func.rtype()};
1251 vm.unsafe_call(&func);
1252 }
1253
1254zenkit::DaedalusNakedCall DirectMemory::mem_callbyid(zenkit::DaedalusVm& vm) {
1255 const uint32_t symbId = uint32_t(vm.pop_int());
1256 auto* sym = vm.find_symbol_by_index(symbId);
1257 if(sym==nullptr || sym->type()!=zenkit::DaedalusDataType::FUNCTION) {
1258 Log::e("MEM_CallByID: Provided symbol is not callable (not function, prototype or instance): ", symbId);
1259 return zenkit::DaedalusNakedCall();
1260 }
1261 directCall(vm, *sym);
1262 return zenkit::DaedalusNakedCall();
1263 }
1264
1265zenkit::DaedalusNakedCall DirectMemory::mem_callbyptr(zenkit::DaedalusVm& vm) {
1266 //FIXME: map function into memory for real!
1267 const auto address = uint32_t(vm.pop_int());
1268 auto sym = vm.find_symbol_by_address(address);
1269 if(sym==nullptr || sym->type()!=zenkit::DaedalusDataType::FUNCTION) {
1270 char buf[16] = {};
1271 std::snprintf(buf, sizeof(buf), "%x", address);
1272 Log::e("MEM_CallByPtr: Provided symbol is not callable (not function, prototype or instance): 0x", buf);
1273 return zenkit::DaedalusNakedCall();
1274 }
1275 directCall(vm, *sym);
1276 return zenkit::DaedalusNakedCall();
1277 }
1278
1279auto DirectMemory::memint_stackpushint(zenkit::DaedalusVm& vm) -> zenkit::DaedalusNakedCall {
1280 return zenkit::DaedalusNakedCall();
1281 }
1282
1283auto DirectMemory::memint_stackpushinst(zenkit::DaedalusVm& vm) -> zenkit::DaedalusNakedCall {
1284 int id = vm.pop_int();
1285 auto sym = vm.find_symbol_by_index(uint32_t(id));
1286 if(sym!=nullptr && sym->type()==zenkit::DaedalusDataType::INSTANCE) {
1287 vm.push_instance(sym->get_instance());
1288 return zenkit::DaedalusNakedCall();
1289 }
1290 vm.push_instance(nullptr);
1291 return zenkit::DaedalusNakedCall();
1292 }
1293
1294auto DirectMemory::memint_stackpushvar(zenkit::DaedalusVm& vm) -> zenkit::DaedalusNakedCall {
1295 //NOTE: works by accident with _AI_FUNCTION_EVENT for now
1296 const int ptr = vm.pop_int(); (void)ptr;
1297 Log::d("TODO: memint_stackpushvar");
1298 return zenkit::DaedalusNakedCall();
1299 }
1300
1301auto DirectMemory::memint_popstring(zenkit::DaedalusVm& vm) -> zenkit::DaedalusNakedCall {
1302 return zenkit::DaedalusNakedCall();
1303 }
1304
1305auto DirectMemory::mem_popintresult(zenkit::DaedalusVm& vm) -> zenkit::DaedalusNakedCall {
1306 return zenkit::DaedalusNakedCall();
1307 }
1308
1309auto DirectMemory::mem_popstringresult(zenkit::DaedalusVm& vm) -> zenkit::DaedalusNakedCall {
1310 return zenkit::DaedalusNakedCall();
1311 }
1312
1313auto DirectMemory::mem_popinstresult(zenkit::DaedalusVm& vm) -> zenkit::DaedalusNakedCall {
1314 return zenkit::DaedalusNakedCall();
1315 }
1316
1317void DirectMemory::setupMathFunctions() {
1318 vm.override_function("MKF", [](int i) { return mkf(i); });
1319 vm.override_function("TRUNCF", [](int i) { return truncf(i); });
1320 vm.override_function("ROUNDF", [](int i) { return roundf(i); });
1321 vm.override_function("ADDF", [](int a, int b) { return addf(a,b); });
1322 vm.override_function("SUBF", [](int a, int b) { return subf(a,b); });
1323 vm.override_function("MULF", [](int a, int b) { return mulf(a,b); });
1324 vm.override_function("DIVF", [](int a, int b) { return divf(a,b); });
1325 }
1326
1327int DirectMemory::mkf(int v) {
1328 return floatBitsToInt(float(v));
1329 }
1330
1331int DirectMemory::truncf(int v) {
1332 float ret = intBitsToFloat(v);
1333 ret = std::truncf(ret);
1334 return int(ret); //floatBitsToInt(ret);
1335 }
1336
1337int DirectMemory::roundf(int v) {
1338 float ret = intBitsToFloat(v);
1339 ret = std::roundf(ret);
1340 return int(ret); //floatBitsToInt(ret);
1341 }
1342
1343int DirectMemory::addf(int ia, int ib) {
1344 float a = intBitsToFloat(ia);
1345 float b = intBitsToFloat(ib);
1346 return floatBitsToInt(a+b);
1347 }
1348
1349int DirectMemory::subf(int ia, int ib) {
1350 float a = intBitsToFloat(ia);
1351 float b = intBitsToFloat(ib);
1352 return floatBitsToInt(a-b);
1353 }
1354
1355int DirectMemory::mulf(int ia, int ib) {
1356 float a = intBitsToFloat(ia);
1357 float b = intBitsToFloat(ib);
1358 return floatBitsToInt(a*b);
1359 }
1360
1361int DirectMemory::divf(int ia, int ib) {
1362 float a = intBitsToFloat(ia);
1363 float b = intBitsToFloat(ib);
1364 return floatBitsToInt(a/b);
1365 }
1366
1367void DirectMemory::setupStringsFunctions() {
1368 // TODO: allow writes to mapped memory
1369 vm.override_function("STR_SubStr", [](std::string_view str, int start, int count) -> std::string {
1370 if(start < 0 || (count < 0)) {
1371 Log::e("STR_SubStr: start and count may not be negative.");
1372 return "";
1373 };
1374
1375 if(size_t(start)>=str.size()) {
1376 Log::e("STR_SubStr: The desired start of the substring lies beyond the end of the string.");
1377 return "";
1378 };
1379
1380 return std::string(str.substr(size_t(start), size_t(count)));
1381 });
1382
1383 //NOTE: native implementation requires coherency protocol
1384 // const ptr32_t ZSTRING__UPPER_G2 = 4631296;
1385 vm.override_function("STR_Upper", [](std::string_view str){
1386 std::string s = std::string(str);
1387 for(auto& c:s)
1388 c = char(std::toupper(c));
1389 return s;
1390 });
1391
1392 // LeGo immplementation requires 'rw' access to 'callback memory'
1393 vm.override_function("SB_TOSTRING", [this]() -> std::string {
1394 struct StringBuilder {
1395 ptr32_t ptr;
1396 int cln;
1397 int CAL;
1398 };
1399 const auto _SB_CURRENT = this->vm.find_symbol_by_name("_SB_CURRENT");
1400 if(_SB_CURRENT==nullptr || _SB_CURRENT->type()!=zenkit::DaedalusDataType::INT)
1401 return "";
1402
1403 auto text = mem32.deref<StringBuilder>(ptr32_t(_SB_CURRENT->get_int()));
1404 if(text->cln<=0 || text->ptr==0)
1405 return "";
1406 auto cstr = reinterpret_cast<const char*>(mem32.deref(text->ptr, uint32_t(text->cln)));
1407 return std::string(cstr, size_t(text->cln));
1408 });
1409
1410 const ptr32_t STR_FROMCHAR = 4198592;
1411 cpu.register_thiscall(STR_FROMCHAR, [this](ptr32_t self, ptr32_t pchr) {
1412 if(self<scriptVariables)
1413 return; // error
1414
1415 const uint32_t id = (self - scriptVariables)/ptr32_t(sizeof(ScriptVar));
1416 const auto sx = vm.find_symbol_by_index(id);
1417 if(sx==nullptr || sx->type()!=zenkit::DaedalusDataType::STRING)
1418 return;
1419
1420 auto [ptr, size] = mem32.deref(pchr);
1421 const char* chr = reinterpret_cast<const char*>(ptr);
1422 if(chr==nullptr || size==0) {
1423 sx->set_string("");
1424 return;
1425 }
1426 size_t strsz = 0;
1427 while(chr[strsz] && strsz<size)
1428 ++strsz;
1429 sx->set_string(std::string(chr, strsz));
1430 });
1431 }
1432
1433void DirectMemory::setupWinapiFunctions() {
1434 const ptr32_t WINAPI__LOADLIBRARY_ptr = 8577604;
1435 const ptr32_t WINAPI__GETPROCADDRESS_ptr = 8577688;
1436 mem32.alloc(WINAPI__LOADLIBRARY_ptr, 4, "WINAPI__LOADLIBRARY*");
1437 mem32.alloc(WINAPI__GETPROCADDRESS_ptr, 4, "WINAPI__GETPROCADDRESS*");
1438
1439 const ptr32_t WINAPI__LOADLIBRARY = mem32.alloc(4);
1440 mem32.writeInt(WINAPI__LOADLIBRARY_ptr, int32_t(WINAPI__LOADLIBRARY));
1441
1442 const ptr32_t WINAPI__GETPROCADDRESS = mem32.alloc(4);
1443 mem32.writeInt(WINAPI__GETPROCADDRESS_ptr, int32_t(WINAPI__GETPROCADDRESS));
1444
1445 cpu.register_stdcall(WINAPI__LOADLIBRARY, [this](ptr32_t pname) {
1446 auto [ptr, size] = mem32.deref(pname);
1447 auto name = std::string_view(reinterpret_cast<const char*>(ptr), reinterpret_cast<const char*>(ptr)+size);
1448 Log::d("suppress LoadLibrary: ", name);
1449 return 0x1;
1450 });
1451
1452 cpu.register_stdcall(WINAPI__GETPROCADDRESS, [this](ptr32_t module, ptr32_t pname) {
1453 auto [ptr, size] = mem32.deref(pname);
1454 auto name = std::string_view(reinterpret_cast<const char*>(ptr), reinterpret_cast<const char*>(ptr)+size);
1455 Log::d("suppress GetProcAddress: ",name);
1456 return 0x1;
1457 });
1458
1459 const ptr32_t GETUSERNAMEA = 8080162;
1460 cpu.register_stdcall(GETUSERNAMEA, [this](ptr32_t lpBuffer, ptr32_t pcbBuffer){
1461 std::string_view uname = "OpenGothic";
1462
1463 const uint32_t max = pcbBuffer ? uint32_t(mem32.readInt(pcbBuffer)) : 0;
1464 if(auto ptr = reinterpret_cast<char*>(mem32.derefv(lpBuffer, max))) {
1465 if(max>=uname.size()) {
1466 std::strncpy(reinterpret_cast<char*>(ptr), "OpenGothic", max);
1467 ptr[uname.size()] = '\0';
1468 return 1;
1469 }
1470 }
1471 // buffer is too small
1472 mem32.writeInt(pcbBuffer, int32_t(uname.size()+1));
1473 return -1;
1474 });
1475
1476 const ptr32_t GETLOCALTIME = 8079184;
1477 cpu.register_stdcall(GETLOCALTIME, [this](ptr32_t lpSystemTime) {
1478 struct SystemTime {
1479 uint16_t wYear;
1480 uint16_t wMonth;
1481 uint16_t wDayOfWeek;
1482 uint16_t wDay;
1483 uint16_t wHour;
1484 uint16_t wMinute;
1485 uint16_t wSecond;
1486 uint16_t wMilliseconds;
1487 };
1488
1489 if(auto ptr = mem32.deref<SystemTime>(lpSystemTime)) {
1490 const auto timePoint = std::chrono::system_clock::now();
1491 const std::time_t time = std::chrono::system_clock::to_time_t(timePoint);
1492 const auto t = std::localtime(&time);
1493 const auto ms = std::chrono::time_point_cast<std::chrono::milliseconds>(timePoint);
1494
1495 ptr->wYear = uint16_t(t->tm_year + 1900);
1496 ptr->wMonth = uint16_t(t->tm_mon + 1);
1497 ptr->wDayOfWeek = uint16_t(t->tm_wday);
1498 ptr->wDay = uint16_t(t->tm_mday);
1499 ptr->wHour = uint16_t(t->tm_hour);
1500 ptr->wMinute = uint16_t(t->tm_min);
1501 ptr->wSecond = uint16_t(t->tm_sec);
1502 ptr->wMilliseconds = uint16_t(ms.time_since_epoch().count()%1000);
1503 }
1504 });
1505 }
1506
1507void DirectMemory::setupUtilityFunctions() {
1508 static auto generateCrcTable = []() {
1509 std::array<uint32_t, 256> table;
1510 uint32_t polynomial = 0xEDB88320;
1511 for (uint32_t i = 0; i < 256; i++) {
1512 uint32_t c = i;
1513 for (size_t j = 0; j < 8; j++) {
1514 if(c & 1) {
1515 c = polynomial ^ (c >> 1);
1516 } else {
1517 c >>= 1;
1518 }
1519 }
1520 table[i] = c;
1521 }
1522 return table;
1523 };
1524
1525 const ptr32_t GETBUFFERCRC32_G2 = 6265360;
1526 cpu.register_cdecl(GETBUFFERCRC32_G2, [this](ptr32_t buf, int bufLen, int unused) {
1527 if(buf==0 || bufLen<=0)
1528 return 0;
1529
1530 const auto ptr = reinterpret_cast<const uint8_t*>(mem32.deref(buf,uint32_t(bufLen)));
1531 static auto table = generateCrcTable();
1532
1533 uint32_t initial = 0;
1534 uint32_t c = initial ^ 0xFFFFFFFF;
1535 for(int i = 0; i < bufLen; ++i) {
1536 c = table[(c ^ ptr[i]) & 0xFF] ^ (c >> 8);
1537 }
1538 return int32_t(c ^ 0xFFFFFFFF);
1539 });
1540
1541 const ptr32_t QSORT_G2 = 8195951;
1542 cpu.register_thiscall(QSORT_G2, [](ptr32_t base, int numElt, int eltSize, ptr32_t pCmp) {
1543 Log::e("LeGo: TODO: QSORT_G2(...)");
1544 });
1545
1546 const ptr32_t ZERROR__SETTARGET = 4513616;
1547 cpu.register_thiscall(ZERROR__SETTARGET, [](ptr32_t, int) {
1548 // nop
1549 });
1550
1551 const int SYSGETTIMEPTR_G2 = 5264000;
1552 cpu.register_cdecl(SYSGETTIMEPTR_G2, [this](){
1553 return uint32_t(gameScript.tickCount());
1554 });
1555 }
1556
1557void DirectMemory::setupHookEngine() {
1558 vm.override_function("HookEngineI", [](int address, int oldInstr, zenkit::DaedalusFunction function){
1559 auto sym = function.value;
1560 auto name = sym==nullptr ? "" : sym->name().c_str();
1561 Log::e("not implemented call [HookEngineI] (", reinterpret_cast<void*>(uint64_t(address)),
1562 " -> ", name, ")");
1563 });
1564
1565 const ptr32_t ZCCONSOLE__REGISTER = 7875296;
1566 cpu.register_thiscall(ZCCONSOLE__REGISTER, [](ptr32_t, ptr32_t pcmd, ptr32_t pdescr) {
1567 (void)pcmd;
1568 (void)pdescr;
1569 // auto sym = func.value;
1570 // auto name = sym==nullptr ? "" : sym->name().c_str();
1571 // Log::e("not implemented call [ZCCONSOLE__REGISTER] (", prefix, " -> ", name, ")");
1572 });
1573
1574 // PermMem
1575 vm.override_function("LOCALS", [this](zenkit::DaedalusVm& vm) -> zenkit::DaedalusNakedCall {
1576 auto sym = findSymbolByAddress(vm.pc());
1577 if(sym!=nullptr) {
1578 auto sx = vm.find_symbol_by_index(sym->index());
1579 sx->set_local_variables_enable(true);
1580 }
1581 return zenkit::DaedalusNakedCall();
1582 });
1583 vm.override_function("FINAL", []() {
1584 Log::e("LeGo: 'final' is not implemented");
1585 return 0;
1586 });
1587 }
1588
1589
1590void DirectMemory::setupzCParserFunctions() {
1591 const ptr32_t ZCPARSER__GETINDEX_G2 = 7943280;
1592 cpu.register_thiscall(ZCPARSER__GETINDEX_G2, [this](ptr32_t, std::string sym) {
1593 if(auto s = vm.find_symbol_by_name(sym))
1594 return int32_t(s->index());
1595 return -1;
1596 });
1597
1598 const ptr32_t ZCPARSER__CREATEINSTANCE = 7942048;
1599 cpu.register_thiscall(ZCPARSER__CREATEINSTANCE, [this](ptr32_t, int32_t instId, ptr32_t ptr){
1600 auto *sym = vm.find_symbol_by_index(uint32_t(instId));
1601 auto *cls = sym;
1602 if(sym != nullptr && sym->type() == zenkit::DaedalusDataType::INSTANCE) {
1603 cls = vm.find_symbol_by_index(sym->parent());
1604 }
1605
1606 if(sym==nullptr || cls == nullptr) {
1607 Log::e("LeGo::createInstance invalid symbold id (", instId, ")");
1608 return 0;
1609 }
1610
1611 auto inst = std::make_shared<memory_instance>(*this, ptr);
1612
1613 auto self = vm.find_symbol_by_name("SELF");
1614 auto prevSelf = self != nullptr ? self->get_instance() : nullptr;
1615 auto prevGi = vm.unsafe_get_gi();
1616 if(self!=nullptr)
1617 self->set_instance(inst);
1618
1619 vm.unsafe_set_gi(inst);
1620 vm.unsafe_call(sym);
1621
1622 vm.unsafe_set_gi(prevGi);
1623 if(self!=nullptr)
1624 self->set_instance(prevSelf);
1625
1626 sym->set_instance(inst);
1627 return int(ptr); // no idea, what return-value suppose to be - unused in LeGo
1628 });
1629 }
1630
1631void DirectMemory::setupInitFileFunctions() {
1632 vm.override_function("MEM_GetGothOpt", [](std::string_view sec, std::string_view opt) {
1633 return std::string(Gothic::inst().settingsGetS(sec, opt));
1634 });
1635 vm.override_function("MEM_GetModOpt", [](std::string_view sec, std::string_view opt) {
1636 Log::e("TODO: mem_getmodopt(", sec, ", ", opt, ")");
1637 return std::string("");
1638 });
1639 vm.override_function("MEM_GothOptSectionExists", [](std::string_view sec) {
1640 Log::e("TODO: mem_gothoptaectionexists(", sec, ")");
1641 return false;
1642 });
1643 vm.override_function("MEM_GothOptExists", [](std::string_view sec, std::string_view opt) {
1644 if(sec=="INTERNAL" && opt=="UnionActivated") {
1645 // Fake Union, so ikarus won't setup crazy workarounds for unpatched G2.
1646 return true;
1647 }
1648 Log::e("TODO: mem_gothoptexists(", sec, ", ", opt, ")");
1649 return false;
1650 });
1651 vm.override_function("MEM_ModOptSectionExists", [](std::string_view sec) {
1652 Log::e("TODO: mem_modoptsectionexists(", sec, ")");
1653 return false;
1654 });
1655 vm.override_function("MEM_ModOptExists", [](std::string_view sec, std::string_view opt) {
1656 Log::e("TODO: mem_modoptexists(", sec, ", ", opt, ")");
1657 return false;
1658 });
1659 vm.override_function("MEM_SetGothOpt", [](std::string_view sec, std::string_view opt, std::string_view v) {
1660 Log::e("TODO: mem_setgothopt(", sec, ", ", opt, ", ", v, ")");
1661 });
1662 }
1663
1664void DirectMemory::setupUiFunctions() {
1665 // https://github.com/Lehona/LeGo/blob/dev/View.d
1666 const int ZCVIEW__ZCVIEW = 8017664;
1667 const int ZCVIEW__OPEN = 8023040;
1668 const int ZCVIEW__CLOSE = 8023600;
1669 const int ZCVIEW_TOP = 8021904;
1670 const int ZCVIEW__SETSIZE = 8026016;
1671 const int zCVIEW__MOVE = 8025824;
1672 const int ZCVIEW__INSERTBACK = 8020272;
1673 cpu.register_thiscall(ZCVIEW__ZCVIEW, [this](ptr32_t ptr, int x1, int y1, int x2, int y2, int arg) {
1674 auto view = mem32.deref<zCView>(ptr);
1675 if(view==nullptr) {
1676 Log::e("LeGo: zCView__zCView - unable to resolve address");
1677 return;
1678 }
1679
1680 view->VPOSX = x1;
1681 view->VPOSY = y1;
1682 view->VSIZEX = x2-x1;
1683 view->VSIZEY = y2-y1;
1684 });
1685
1686 cpu.register_thiscall(ZCVIEW__OPEN, [this](ptr32_t ptr) {
1687 auto view = mem32.deref<zCView>(ptr);
1688 if(view==nullptr) {
1689 Log::e("LeGo: zCView__Open - unable to resolve address");
1690 return;
1691 }
1692 Log::e("LeGo: zCView__Open");
1693 });
1694
1695 cpu.register_thiscall(ZCVIEW__CLOSE, [this](ptr32_t ptr) {
1696 auto view = mem32.deref<zCView>(ptr);
1697 if(view==nullptr) {
1698 Log::e("LeGo: zCView__Close - unable to resolve address");
1699 return;
1700 }
1701 });
1702
1703 cpu.register_thiscall(ZCVIEW_TOP, [this](ptr32_t ptr) {
1704 auto view = mem32.deref<zCView>(ptr);
1705 if(view==nullptr) {
1706 Log::e("LeGo: zCView__Top - unable to resolve address");
1707 return;
1708 }
1709 });
1710
1711 cpu.register_thiscall(ZCVIEW__SETSIZE, [this](ptr32_t ptr, int x, int y) {
1712 auto view = mem32.deref<zCView>(ptr);
1713 if(view==nullptr) {
1714 Log::e("LeGo: zCView__SetSize - unable to resolve address");
1715 return;
1716 }
1717
1718 view->VSIZEX = x;
1719 view->VSIZEY = y;
1720 });
1721
1722 cpu.register_thiscall(zCVIEW__MOVE, [this](ptr32_t ptr, int x, int y) {
1723 auto view = mem32.deref<zCView>(ptr);
1724 if(view==nullptr) {
1725 Log::e("LeGo: zCView__Move - unable to resolve address");
1726 return;
1727 }
1728 view->VPOSX = x;
1729 view->VPOSY = y;
1730 });
1731
1732 cpu.register_thiscall(ZCVIEW__INSERTBACK, [this](ptr32_t ptr, std::string img) {
1733 auto view = mem32.deref<zCView>(ptr);
1734 if(view==nullptr) {
1735 Log::e("LeGo: zCView__InsertBack - unable to resolve address");
1736 return;
1737 }
1738 Log::e("LeGo: zCView__InsertBack: ", img);
1739 });
1740
1741 // ## Textures
1742 const int ZCTEXTURE__LOAD = 6239904;
1743 cpu.register_stdcall(ZCTEXTURE__LOAD, [](std::string img, int flag) {
1744 Log::e("LeGo: zCTexture__Load: ", img);
1745 return 0;
1746 });
1747 }
1748
1749void DirectMemory::setupFontFunctions() {
1750 const ptr32_t ZCFONTMAN__LOAD = 7897808;
1751 const ptr32_t ZCFONTMAN__GETFONT = 7898288;
1752 cpu.register_thiscall(ZCFONTMAN__LOAD, [](ptr32_t ptr, std::string font) {
1753 Log::e("LeGo: zCFontMan__Load");
1754 return 1234;
1755 });
1756 cpu.register_thiscall(ZCFONTMAN__GETFONT, [](ptr32_t ptr, int handle) {
1757 Log::e("LeGo: zCFontMan__GetFont");
1758 return handle;
1759 });
1760 }
1761
1762void DirectMemory::tickUi(uint64_t dt) {
1763 if(auto* vScreen = mem32.deref<zCView>(memGame._ZCSESSION_VIEWPORT)) {
1764 //TODO: report correct screen size
1765 vScreen->PSIZEX = 800;
1766 vScreen->PSIZEY = 600;
1767 }
1768
1769 //NOTE: PRINT_EXT
1770 if(auto* vPrint = mem32.deref<const zCView>(memGame.ARRAY_VIEW[0])) {
1771 auto* vList = vPrint->TEXTLINES_NEXT!=0 ? mem32.deref<const zCList>(vPrint->TEXTLINES_NEXT) : nullptr;
1772 while(vList!=nullptr) {
1773 if(auto* textView = mem32.deref<const zCViewText>(vList->data)) {
1774 std::string dst; //TODO: avoid reallocations
1775 memFromString(dst, textView->text);
1776 // Log::i("zCViewText: ", dst);
1777 }
1778 vList = vList->next!=0 ? mem32.deref<const zCList>(vList->next) : 0;
1779 }
1780 }
1781 }
1782
1783void DirectMemory::setupNpcFunctions() {
1784 const ptr32_t OCNPC__GETSLOTITEM = 7544720;
1785 cpu.register_thiscall(OCNPC__GETSLOTITEM, [](ptr32_t pHero, std::string slot) {
1786 Log::e("LeGo: OCNPC__GETSLOTITEM(", slot, ")");
1787 return 0x0;
1788 });
1789
1790 const ptr32_t OCNPC__EQUIPWEAPON = 7577648;
1791 cpu.register_thiscall(OCNPC__EQUIPWEAPON, [](ptr32_t pHero, ptr32_t pItem) {
1792 Log::e("LeGo: OCNPC__EQUIPWEAPON(", pItem, ")");
1793 });
1794 }
1795
1796void DirectMemory::setupWorldFunctions() {
1797 const ptr32_t OCWORLD__SEARCHVOBBYNAME_G2 = 7865872;
1798 cpu.register_thiscall(OCWORLD__SEARCHVOBBYNAME_G2, [](ptr32_t pWorld, std::string vob) {
1799 Log::e("LeGo: OCWORLD__SEARCHVOBBYNAME_G2(", vob, ")");
1800 });
1801
1802 const ptr32_t OCWORLD__SEARCHVOBLISTBYNAME_G2 = 7866048;
1803 cpu.register_thiscall(OCWORLD__SEARCHVOBLISTBYNAME_G2, [](ptr32_t pWorld, std::string vob, ptr32_t pOutArr) {
1804 Log::e("LeGo: OCWORLD__SEARCHVOBLISTBYNAME_G2(", vob, ")");
1805 });
1806 }
void exec(const ptr32_t basePc, const uint8_t *code, size_t len)
Definition cpu32.cpp:16
void register_cdecl(ptr32_t addr, const std::function< R(Args...)> &callback)
Definition cpu32.h:38
void register_stdcall(ptr32_t addr, const std::function< R(Args...)> &callback)
Definition cpu32.h:23
void register_thiscall(ptr32_t addr, const std::function< R(Args...)> &callback)
Definition cpu32.h:30
DirectMemory(GameScript &owner, zenkit::DaedalusVm &vm)
static bool isRequired(zenkit::DaedalusScript &vm)
void eventPlayAni(std::string_view ani)
void tick(uint64_t dt)
auto demangleAddress(uint32_t addr) -> std::string_view
auto menuMain() const -> std::string_view
const zenkit::IFocus & focusMage() const
Definition gamescript.h:106
uint64_t tickCount() const
const World & world() const
static Gothic & inst()
Definition gothic.cpp:249
Compatibility::ptr32_t ptr32_t
Definition mem32.h:27
void setCallbackW(Type t, const T &fn)
Definition mem32.h:34
int32_t readInt(ptr32_t address)
Definition mem32.cpp:205
void * derefv(ptr32_t address, uint32_t size)
Definition mem32.cpp:144
ptr32_t alloc(uint32_t size, const char *comment=nullptr)
Definition mem32.cpp:89
ptr32_t realloc(ptr32_t address, uint32_t size)
Definition mem32.cpp:277
ptr32_t pin(void *mem, ptr32_t address, uint32_t size, const char *comment=nullptr)
Definition mem32.cpp:41
void setCallbackR(Type t, const T &fn)
Definition mem32.h:31
void copyBytes(ptr32_t src, ptr32_t dst, uint32_t size)
Definition mem32.cpp:222
auto deref(ptr32_t address) -> std::tuple< void *, uint32_t >
Definition mem32.cpp:163
void free(ptr32_t at)
Definition mem32.cpp:113
void writeInt(ptr32_t address, int32_t v)
Definition mem32.cpp:191
Definition npc.h:25
bool setPosition(float x, float y, float z)
Definition npc.cpp:388
static float intBitsToFloat(int32_t i)
static int32_t floatBitsToInt(float f)
static uint32_t nextPot(uint32_t x)
uint32_t ptr32_t