From 6c03e4e29327e6ad1e5ac0779cdd5619793ed1ae Mon Sep 17 00:00:00 2001
From: Alexander Batalov <alex.batalov@gmail.com>
Date: Wed, 9 Nov 2022 14:35:07 +0300
Subject: [PATCH] Fix storing pointers in map global variables

---
 src/interpreter_extra.cc | 10 +++++++---
 src/map.cc               | 33 ++++++++++++++++++++++++++++-----
 src/map.h                |  4 ++--
 3 files changed, 37 insertions(+), 10 deletions(-)

diff --git a/src/interpreter_extra.cc b/src/interpreter_extra.cc
index da59779..8eac59d 100644
--- a/src/interpreter_extra.cc
+++ b/src/interpreter_extra.cc
@@ -1220,16 +1220,20 @@ static void opGetMapVar(Program* program)
 {
     int data = programStackPopInteger(program);
 
-    int value = mapGetGlobalVar(data);
+    ProgramValue value;
+    if (mapGetGlobalVar(data, value) == -1) {
+        value.opcode = VALUE_TYPE_INT;
+        value.integerValue = -1;
+    }
 
-    programStackPushInteger(program, value);
+    programStackPushValue(program, value);
 }
 
 // set_map_var
 // 0x4558C8
 static void opSetMapVar(Program* program)
 {
-    int value = programStackPopInteger(program);
+    ProgramValue value = programStackPopValue(program);
     int variable = programStackPopInteger(program);
 
     mapSetGlobalVar(variable, value);
diff --git a/src/map.cc b/src/map.cc
index 87990e1..ca4d214 100644
--- a/src/map.cc
+++ b/src/map.cc
@@ -164,6 +164,11 @@ static char _scratchStr[40];
 // 0x631E78
 static char _map_path[COMPAT_MAX_PATH];
 
+// CE: Basically the same problem described in |gMapLocalPointers|, but this
+// time Olympus folks use global map variables to store objects (looks like
+// only `self_obj`).
+static std::vector<void*> gMapGlobalPointers;
+
 // CE: There is a bug in the user-space scripting where they want to store
 // pointers to |Object| instances in local vars. This is obviously wrong as it's
 // meaningless to save these pointers in file. As a workaround use second array
@@ -390,27 +395,41 @@ int mapSetElevation(int elevation)
 }
 
 // 0x482220
-int mapSetGlobalVar(int var, int value)
+int mapSetGlobalVar(int var, ProgramValue& value)
 {
     if (var < 0 || var >= gMapGlobalVarsLength) {
         debugPrint("ERROR: attempt to reference map var out of range: %d", var);
         return -1;
     }
 
-    gMapGlobalVars[var] = value;
+    if (value.opcode == VALUE_TYPE_PTR) {
+        gMapGlobalVars[var] = 0;
+        gMapGlobalPointers[var] = value.pointerValue;
+    } else {
+        gMapGlobalVars[var] = value.integerValue;
+        gMapGlobalPointers[var] = nullptr;
+    }
 
     return 0;
 }
 
 // 0x482250
-int mapGetGlobalVar(int var)
+int mapGetGlobalVar(int var, ProgramValue& value)
 {
     if (var < 0 || var >= gMapGlobalVarsLength) {
         debugPrint("ERROR: attempt to reference map var out of range: %d", var);
-        return 0;
+        return -1;
     }
 
-    return gMapGlobalVars[var];
+    if (gMapGlobalPointers[var] != nullptr) {
+        value.opcode = VALUE_TYPE_PTR;
+        value.pointerValue = gMapGlobalPointers[var];
+    } else {
+        value.opcode = VALUE_TYPE_INT;
+        value.integerValue = gMapGlobalVars[var];
+    }
+
+    return 0;
 }
 
 // 0x482280
@@ -1521,6 +1540,8 @@ static int mapGlobalVariablesInit(int count)
         if (gMapGlobalVars == NULL) {
             return -1;
         }
+
+        gMapGlobalPointers.resize(count);
     }
 
     gMapGlobalVarsLength = count;
@@ -1536,6 +1557,8 @@ static void mapGlobalVariablesFree()
         gMapGlobalVars = NULL;
         gMapGlobalVarsLength = 0;
     }
+
+    gMapGlobalPointers.clear();
 }
 
 // NOTE: Inlined.
diff --git a/src/map.h b/src/map.h
index 4b95b89..0cdebb1 100644
--- a/src/map.h
+++ b/src/map.h
@@ -86,8 +86,8 @@ void isoEnable();
 bool isoDisable();
 bool isoIsDisabled();
 int mapSetElevation(int elevation);
-int mapSetGlobalVar(int var, int value);
-int mapGetGlobalVar(int var);
+int mapSetGlobalVar(int var, ProgramValue& value);
+int mapGetGlobalVar(int var, ProgramValue& value);
 int mapSetLocalVar(int var, ProgramValue& value);
 int mapGetLocalVar(int var, ProgramValue& value);
 int _map_malloc_local_var(int a1);