summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorNikolay Korolev <nickvnuk@gmail.com>2020-04-25 11:25:40 +0200
committerNikolay Korolev <nickvnuk@gmail.com>2020-04-25 11:25:40 +0200
commit2be2899034a5eb0001b3fb89ea29c7c5480f1fbc (patch)
tree7b1586badbaea9a7d455efc6648e6ecada82f47e /src
parentlibrw update (diff)
parentMerge pull request #490 from aap/debugmenu (diff)
downloadre3-2be2899034a5eb0001b3fb89ea29c7c5480f1fbc.tar
re3-2be2899034a5eb0001b3fb89ea29c7c5480f1fbc.tar.gz
re3-2be2899034a5eb0001b3fb89ea29c7c5480f1fbc.tar.bz2
re3-2be2899034a5eb0001b3fb89ea29c7c5480f1fbc.tar.lz
re3-2be2899034a5eb0001b3fb89ea29c7c5480f1fbc.tar.xz
re3-2be2899034a5eb0001b3fb89ea29c7c5480f1fbc.tar.zst
re3-2be2899034a5eb0001b3fb89ea29c7c5480f1fbc.zip
Diffstat (limited to '')
-rw-r--r--src/core/AnimViewer.cpp2
-rw-r--r--src/core/Game.cpp2
-rw-r--r--src/core/debugmenu_public.h154
-rw-r--r--src/core/main.cpp5
-rw-r--r--src/core/re3.cpp40
-rw-r--r--src/extras/arrow.inc16
-rw-r--r--src/extras/cursor.inc16
-rw-r--r--src/extras/debugmenu.cpp1268
-rw-r--r--src/extras/debugmenu.h80
-rw-r--r--src/extras/inttypes.h216
-rw-r--r--src/fakerw/fake.cpp4
-rw-r--r--src/rw/RwHelper.cpp11
-rw-r--r--src/rw/RwHelper.h1
13 files changed, 1617 insertions, 198 deletions
diff --git a/src/core/AnimViewer.cpp b/src/core/AnimViewer.cpp
index cf3ec5bf..36ea20b7 100644
--- a/src/core/AnimViewer.cpp
+++ b/src/core/AnimViewer.cpp
@@ -37,6 +37,7 @@
#include "Shadows.h"
#include "Radar.h"
#include "Hud.h"
+#include "debugmenu.h"
int CAnimViewer::animTxdSlot = 0;
CEntity *CAnimViewer::pTarget = nil;
@@ -208,7 +209,6 @@ PlayAnimation(RpClump *clump, AssocGroupId animGroup, AnimationId anim)
animAssoc->SetRun();
}
-extern void (*DebugMenuProcess)(void);
void
CAnimViewer::Update(void)
{
diff --git a/src/core/Game.cpp b/src/core/Game.cpp
index bfe6eee3..f72d342f 100644
--- a/src/core/Game.cpp
+++ b/src/core/Game.cpp
@@ -84,6 +84,7 @@
#include "World.h"
#include "ZoneCull.h"
#include "Zones.h"
+#include "debugmenu.h"
@@ -618,7 +619,6 @@ void CGame::InitialiseWhenRestarting(void)
DMAudio.ChangeMusicMode(MUSICMODE_GAME);
}
-extern void (*DebugMenuProcess)(void);
void CGame::Process(void)
{
CPad::UpdatePads();
diff --git a/src/core/debugmenu_public.h b/src/core/debugmenu_public.h
deleted file mode 100644
index 778e7afe..00000000
--- a/src/core/debugmenu_public.h
+++ /dev/null
@@ -1,154 +0,0 @@
-
-extern "C" {
-
-typedef void (*TriggerFunc)(void);
-
-struct DebugMenuEntry;
-
-typedef DebugMenuEntry *(*DebugMenuAddInt8_TYPE)(const char *path, const char *name, int8_t *ptr, TriggerFunc triggerFunc, int8_t step, int8_t lowerBound, int8_t upperBound, const char **strings);
-typedef DebugMenuEntry *(*DebugMenuAddInt16_TYPE)(const char *path, const char *name, int16_t *ptr, TriggerFunc triggerFunc, int16_t step, int16_t lowerBound, int16_t upperBound, const char **strings);
-typedef DebugMenuEntry *(*DebugMenuAddInt32_TYPE)(const char *path, const char *name, int32_t *ptr, TriggerFunc triggerFunc, int32_t step, int32_t lowerBound, int32_t upperBound, const char **strings);
-typedef DebugMenuEntry *(*DebugMenuAddInt64_TYPE)(const char *path, const char *name, int64_t *ptr, TriggerFunc triggerFunc, int64_t step, int64_t lowerBound, int64_t upperBound, const char **strings);
-typedef DebugMenuEntry *(*DebugMenuAddUInt8_TYPE)(const char *path, const char *name, uint8_t *ptr, TriggerFunc triggerFunc, uint8_t step, uint8_t lowerBound, uint8_t upperBound, const char **strings);
-typedef DebugMenuEntry *(*DebugMenuAddUInt16_TYPE)(const char *path, const char *name, uint16_t *ptr, TriggerFunc triggerFunc, uint16_t step, uint16_t lowerBound, uint16_t upperBound, const char **strings);
-typedef DebugMenuEntry *(*DebugMenuAddUInt32_TYPE)(const char *path, const char *name, uint32_t *ptr, TriggerFunc triggerFunc, uint32_t step, uint32_t lowerBound, uint32_t upperBound, const char **strings);
-typedef DebugMenuEntry *(*DebugMenuAddUInt64_TYPE)(const char *path, const char *name, uint64_t *ptr, TriggerFunc triggerFunc, uint64_t step, uint64_t lowerBound, uint64_t upperBound, const char **strings);
-typedef DebugMenuEntry *(*DebugMenuAddFloat32_TYPE)(const char *path, const char *name, float *ptr, TriggerFunc triggerFunc, float step, float lowerBound, float upperBound);
-typedef DebugMenuEntry *(*DebugMenuAddFloat64_TYPE)(const char *path, const char *name, double *ptr, TriggerFunc triggerFunc, double step, double lowerBound, double upperBound);
-typedef DebugMenuEntry *(*DebugMenuAddCmd_TYPE)(const char *path, const char *name, TriggerFunc triggerFunc);
-typedef void (*DebugMenuEntrySetWrap_TYPE)(DebugMenuEntry *e, bool wrap);
-typedef void (*DebugMenuEntrySetStrings_TYPE)(DebugMenuEntry *e, const char **strings);
-typedef void (*DebugMenuEntrySetAddress_TYPE)(DebugMenuEntry *e, void *addr);
-
-struct DebugMenuAPI
-{
- bool isLoaded;
- HMODULE module;
- DebugMenuAddInt8_TYPE addint8;
- DebugMenuAddInt16_TYPE addint16;
- DebugMenuAddInt32_TYPE addint32;
- DebugMenuAddInt64_TYPE addint64;
- DebugMenuAddUInt8_TYPE adduint8;
- DebugMenuAddUInt16_TYPE adduint16;
- DebugMenuAddUInt32_TYPE adduint32;
- DebugMenuAddUInt64_TYPE adduint64;
- DebugMenuAddFloat32_TYPE addfloat32;
- DebugMenuAddFloat64_TYPE addfloat64;
- DebugMenuAddCmd_TYPE addcmd;
- DebugMenuEntrySetWrap_TYPE setwrap;
- DebugMenuEntrySetStrings_TYPE setstrings;
- DebugMenuEntrySetAddress_TYPE setaddress;
-};
-extern DebugMenuAPI gDebugMenuAPI;
-
-inline DebugMenuEntry *DebugMenuAddInt8(const char *path, const char *name, int8_t *ptr, TriggerFunc triggerFunc, int8_t step, int8_t lowerBound, int8_t upperBound, const char **strings)
-{ return gDebugMenuAPI.addint8(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
-inline DebugMenuEntry *DebugMenuAddInt16(const char *path, const char *name, int16_t *ptr, TriggerFunc triggerFunc, int16_t step, int16_t lowerBound, int16_t upperBound, const char **strings)
-{ return gDebugMenuAPI.addint16(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
-inline DebugMenuEntry *DebugMenuAddInt32(const char *path, const char *name, int32_t *ptr, TriggerFunc triggerFunc, int32_t step, int32_t lowerBound, int32_t upperBound, const char **strings)
-{ return gDebugMenuAPI.addint32(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
-inline DebugMenuEntry *DebugMenuAddInt64(const char *path, const char *name, int64_t *ptr, TriggerFunc triggerFunc, int64_t step, int64_t lowerBound, int64_t upperBound, const char **strings)
-{ return gDebugMenuAPI.addint64(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
-inline DebugMenuEntry *DebugMenuAddUInt8(const char *path, const char *name, uint8_t *ptr, TriggerFunc triggerFunc, uint8_t step, uint8_t lowerBound, uint8_t upperBound, const char **strings)
-{ return gDebugMenuAPI.adduint8(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
-inline DebugMenuEntry *DebugMenuAddUInt16(const char *path, const char *name, uint16_t *ptr, TriggerFunc triggerFunc, uint16_t step, uint16_t lowerBound, uint16_t upperBound, const char **strings)
-{ return gDebugMenuAPI.adduint16(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
-inline DebugMenuEntry *DebugMenuAddUInt32(const char *path, const char *name, uint32_t *ptr, TriggerFunc triggerFunc, uint32_t step, uint32_t lowerBound, uint32_t upperBound, const char **strings)
-{ return gDebugMenuAPI.adduint32(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
-inline DebugMenuEntry *DebugMenuAddUInt64(const char *path, const char *name, uint64_t *ptr, TriggerFunc triggerFunc, uint64_t step, uint64_t lowerBound, uint64_t upperBound, const char **strings)
-{ return gDebugMenuAPI.adduint64(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
-inline DebugMenuEntry *DebugMenuAddFloat32(const char *path, const char *name, float *ptr, TriggerFunc triggerFunc, float step, float lowerBound, float upperBound)
-{ return gDebugMenuAPI.addfloat32(path, name, ptr, triggerFunc, step, lowerBound, upperBound); }
-inline DebugMenuEntry *DebugMenuAddFloat64(const char *path, const char *name, double *ptr, TriggerFunc triggerFunc, double step, double lowerBound, double upperBound)
-{ return gDebugMenuAPI.addfloat64(path, name, ptr, triggerFunc, step, lowerBound, upperBound); }
-inline DebugMenuEntry *DebugMenuAddCmd(const char *path, const char *name, TriggerFunc triggerFunc)
-{ return gDebugMenuAPI.addcmd(path, name, triggerFunc); }
-inline void DebugMenuEntrySetWrap(DebugMenuEntry *e, bool wrap)
-{ gDebugMenuAPI.setwrap(e, wrap); }
-inline void DebugMenuEntrySetStrings(DebugMenuEntry *e, const char **strings)
-{ gDebugMenuAPI.setstrings(e, strings); }
-inline void DebugMenuEntrySetAddress(DebugMenuEntry *e, void *addr)
-{ gDebugMenuAPI.setaddress(e, addr); }
-
-inline bool DebugMenuLoad(void)
-{
- if(gDebugMenuAPI.isLoaded)
- return true;
- HMODULE mod = LoadLibraryA("debugmenu");
- if(mod == nil){
- char modulePath[MAX_PATH];
- HMODULE dllModule;
- GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCTSTR)&gDebugMenuAPI, &dllModule);
- GetModuleFileNameA(dllModule, modulePath, MAX_PATH);
- char *p = strchr(modulePath, '\\');
- if(p) p[1] = '\0';
- strcat(modulePath, "debugmenu");
- mod = LoadLibraryA(modulePath);
- }
- if(mod == nil)
- return false;
- gDebugMenuAPI.addint8 = (DebugMenuAddInt8_TYPE)GetProcAddress(mod, "DebugMenuAddInt8");
- gDebugMenuAPI.addint16 = (DebugMenuAddInt16_TYPE)GetProcAddress(mod, "DebugMenuAddInt16");
- gDebugMenuAPI.addint32 = (DebugMenuAddInt32_TYPE)GetProcAddress(mod, "DebugMenuAddInt32");
- gDebugMenuAPI.addint64 = (DebugMenuAddInt64_TYPE)GetProcAddress(mod, "DebugMenuAddInt64");
- gDebugMenuAPI.adduint8 = (DebugMenuAddUInt8_TYPE)GetProcAddress(mod, "DebugMenuAddUInt8");
- gDebugMenuAPI.adduint16 = (DebugMenuAddUInt16_TYPE)GetProcAddress(mod, "DebugMenuAddUInt16");
- gDebugMenuAPI.adduint32 = (DebugMenuAddUInt32_TYPE)GetProcAddress(mod, "DebugMenuAddUInt32");
- gDebugMenuAPI.adduint64 = (DebugMenuAddUInt64_TYPE)GetProcAddress(mod, "DebugMenuAddUInt64");
- gDebugMenuAPI.addfloat32 = (DebugMenuAddFloat32_TYPE)GetProcAddress(mod, "DebugMenuAddFloat32");
- gDebugMenuAPI.addfloat64 = (DebugMenuAddFloat64_TYPE)GetProcAddress(mod, "DebugMenuAddFloat64");
- gDebugMenuAPI.addcmd = (DebugMenuAddCmd_TYPE)GetProcAddress(mod, "DebugMenuAddCmd");
- gDebugMenuAPI.setwrap = (DebugMenuEntrySetWrap_TYPE)GetProcAddress(mod, "DebugMenuEntrySetWrap");
- gDebugMenuAPI.setstrings = (DebugMenuEntrySetStrings_TYPE)GetProcAddress(mod, "DebugMenuEntrySetStrings");
- gDebugMenuAPI.setaddress = (DebugMenuEntrySetAddress_TYPE)GetProcAddress(mod, "DebugMenuEntrySetAddress");
- gDebugMenuAPI.isLoaded = true;
- gDebugMenuAPI.module = mod;
- return true;
-}
-
-}
-
-// Also overload them for simplicity
-
-inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, int8_t *ptr, TriggerFunc triggerFunc, int8_t step, int8_t lowerBound, int8_t upperBound, const char **strings)
-{ return gDebugMenuAPI.addint8(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
-inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, int16_t *ptr, TriggerFunc triggerFunc, int16_t step, int16_t lowerBound, int16_t upperBound, const char **strings)
-{ return gDebugMenuAPI.addint16(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
-inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, int32_t *ptr, TriggerFunc triggerFunc, int32_t step, int32_t lowerBound, int32_t upperBound, const char **strings)
-{ return gDebugMenuAPI.addint32(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
-inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, int64_t *ptr, TriggerFunc triggerFunc, int64_t step, int64_t lowerBound, int64_t upperBound, const char **strings)
-{ return gDebugMenuAPI.addint64(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
-inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, uint8_t *ptr, TriggerFunc triggerFunc, uint8_t step, uint8_t lowerBound, uint8_t upperBound, const char **strings)
-{ return gDebugMenuAPI.adduint8(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
-inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, uint16_t *ptr, TriggerFunc triggerFunc, uint16_t step, uint16_t lowerBound, uint16_t upperBound, const char **strings)
-{ return gDebugMenuAPI.adduint16(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
-inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, uint32_t *ptr, TriggerFunc triggerFunc, uint32_t step, uint32_t lowerBound, uint32_t upperBound, const char **strings)
-{ return gDebugMenuAPI.adduint32(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
-inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, uint64_t *ptr, TriggerFunc triggerFunc, uint64_t step, uint64_t lowerBound, uint64_t upperBound, const char **strings)
-{ return gDebugMenuAPI.adduint64(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
-inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, float *ptr, TriggerFunc triggerFunc, float step, float lowerBound, float upperBound)
-{ return gDebugMenuAPI.addfloat32(path, name, ptr, triggerFunc, step, lowerBound, upperBound); }
-inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, double *ptr, TriggerFunc triggerFunc, double step, double lowerBound, double upperBound)
-{ return gDebugMenuAPI.addfloat64(path, name, ptr, triggerFunc, step, lowerBound, upperBound); }
-
-inline DebugMenuEntry *DebugMenuAddVarBool32(const char *path, const char *name, int32_t *ptr, TriggerFunc triggerFunc)
-{
- static const char *boolstr[] = { "Off", "On" };
- DebugMenuEntry *e = DebugMenuAddVar(path, name, ptr, triggerFunc, 1, 0, 1, boolstr);
- DebugMenuEntrySetWrap(e, true);
- return e;
-}
-inline DebugMenuEntry *DebugMenuAddVarBool16(const char *path, const char *name, int16_t *ptr, TriggerFunc triggerFunc)
-{
- static const char *boolstr[] = { "Off", "On" };
- DebugMenuEntry *e = DebugMenuAddVar(path, name, ptr, triggerFunc, 1, 0, 1, boolstr);
- DebugMenuEntrySetWrap(e, true);
- return e;
-}
-inline DebugMenuEntry *DebugMenuAddVarBool8(const char *path, const char *name, int8_t *ptr, TriggerFunc triggerFunc)
-{
- static const char *boolstr[] = { "Off", "On" };
- DebugMenuEntry *e = DebugMenuAddVar(path, name, ptr, triggerFunc, 1, 0, 1, boolstr);
- DebugMenuEntrySetWrap(e, true);
- return e;
-}
diff --git a/src/core/main.cpp b/src/core/main.cpp
index 7013b10f..7c1f7938 100644
--- a/src/core/main.cpp
+++ b/src/core/main.cpp
@@ -59,6 +59,7 @@
#include "timebars.h"
#include "GenericGameStorage.h"
#include "SceneEdit.h"
+#include "debugmenu.h"
GlobalScene Scene;
@@ -90,9 +91,7 @@ void GameInit(void);
void SystemInit(void);
void TheGame(void);
-extern void (*DebugMenuProcess)(void);
-extern void (*DebugMenuRender)(void);
-void DebugMenuInit(void);
+void DebugMenuInit(void) {}
void DebugMenuPopulate(void);
diff --git a/src/core/re3.cpp b/src/core/re3.cpp
index 4cec5773..d4d2c208 100644
--- a/src/core/re3.cpp
+++ b/src/core/re3.cpp
@@ -17,7 +17,6 @@
#include "Heli.h"
#include "Automobile.h"
#include "Ped.h"
-#include "debugmenu_public.h"
#include "Particle.h"
#include "Console.h"
#include "Debug.h"
@@ -26,6 +25,7 @@
#include "Pad.h"
#include "PlayerPed.h"
#include "Radar.h"
+#include "debugmenu.h"
#include <list>
@@ -33,8 +33,6 @@
extern "C" int vsprintf(char* const _Buffer, char const* const _Format, va_list _ArgList);
#endif
-DebugMenuAPI gDebugMenuAPI;
-
#ifdef USE_PS2_RAND
unsigned __int64 myrand_seed = 1;
@@ -62,24 +60,6 @@ mysrand(unsigned int seed)
myrand_seed = seed;
}
-void (*DebugMenuProcess)(void);
-void (*DebugMenuRender)(void);
-static void stub(void) { }
-
-void
-DebugMenuInit(void)
-{
- if(DebugMenuLoad()){
- DebugMenuProcess = (void(*)(void))GetProcAddress(gDebugMenuAPI.module, "DebugMenuProcess");
- DebugMenuRender = (void(*)(void))GetProcAddress(gDebugMenuAPI.module, "DebugMenuRender");
- }
- if(DebugMenuProcess == nil || DebugMenuRender == nil){
- DebugMenuProcess = stub;
- DebugMenuRender = stub;
- }
-
-}
-
void WeaponCheat();
void HealthCheat();
void TankCheat();
@@ -269,7 +249,7 @@ TWEAKSWITCH(CWeather::NewWeatherType, 0, 3, wt, NULL);
void
DebugMenuPopulate(void)
{
- if(DebugMenuLoad()){
+ if(1){
static const char *weathers[] = {
"Sunny", "Cloudy", "Rainy", "Foggy"
};
@@ -284,7 +264,7 @@ DebugMenuPopulate(void)
e = DebugMenuAddVar("Time & Weather", "New Weather", (int16*)&CWeather::NewWeatherType, nil, 1, 0, 3, weathers);
DebugMenuEntrySetWrap(e, true);
DebugMenuAddVar("Time & Weather", "Wind", (float*)&CWeather::Wind, nil, 0.1f, 0.0f, 1.0f);
- DebugMenuAddVar("Time & Weather", "Time scale", (float*)0x8F2C20, nil, 0.1f, 0.0f, 10.0f);
+ DebugMenuAddVar("Time & Weather", "Time scale", (float*)&CTimer::GetTimeScale(), nil, 0.1f, 0.0f, 10.0f);
DebugMenuAddCmd("Cheats", "Weapons", WeaponCheat);
DebugMenuAddCmd("Cheats", "Money", MoneyCheat);
@@ -357,7 +337,7 @@ DebugMenuPopulate(void)
DebugMenuAddCmd("Debug", "Catalina Fly By", CHeli::StartCatalinaFlyBy);
DebugMenuAddCmd("Debug", "Catalina Take Off", CHeli::CatalinaTakeOff);
DebugMenuAddCmd("Debug", "Catalina Fly Away", CHeli::MakeCatalinaHeliFlyAway);
- DebugMenuAddVarBool8("Debug", "Script Heli On", (int8*)0x95CD43, nil);
+ DebugMenuAddVarBool8("Debug", "Script Heli On", (int8*)&CHeli::ScriptHeliOn, nil);
DebugMenuAddVarBool8("Debug", "Show Ped Paths", (int8*)&gbShowPedPaths, nil);
DebugMenuAddVarBool8("Debug", "Show Car Paths", (int8*)&gbShowCarPaths, nil);
@@ -397,18 +377,6 @@ DebugMenuPopulate(void)
}
}
-/*
-int (*RsEventHandler_orig)(int a, int b);
-int
-delayedPatches10(int a, int b)
-{
- DebugMenuInit();
- DebugMenuPopulate();
-
- return RsEventHandler_orig(a, b);
-}
-*/
-
const int re3_buffsize = 1024;
static char re3_buff[re3_buffsize];
diff --git a/src/extras/arrow.inc b/src/extras/arrow.inc
new file mode 100644
index 00000000..8ea78283
--- /dev/null
+++ b/src/extras/arrow.inc
@@ -0,0 +1,16 @@
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
diff --git a/src/extras/cursor.inc b/src/extras/cursor.inc
new file mode 100644
index 00000000..e8afd394
--- /dev/null
+++ b/src/extras/cursor.inc
@@ -0,0 +1,16 @@
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255,
+255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0,
+255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0,
+255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0,
+255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0,
+255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0,
+255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0,
+255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0,
+255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255,
+255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0,
+255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0,
+255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0,
+255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 255, 255, 255, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0,
+255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0,
+255, 255, 255, 255, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 255, 255, 255, 255, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0, 119, 119, 119, 0,
diff --git a/src/extras/debugmenu.cpp b/src/extras/debugmenu.cpp
new file mode 100644
index 00000000..d6ee18ab
--- /dev/null
+++ b/src/extras/debugmenu.cpp
@@ -0,0 +1,1268 @@
+#include "common.h"
+#include "RwHelper.h"
+#include "Pad.h"
+#include "ControllerConfig.h"
+#include "Timer.h"
+#include "rtcharse.h"
+#include "inttypes.h"
+#include "debugmenu.h"
+
+#define snprintf _snprintf
+
+#define strdup _strdup
+
+
+
+// Font stuff
+struct Pt
+{
+ int x, y;
+};
+
+enum MenuFontStyle
+{
+ MENUFONT_NORMAL,
+ MENUFONT_SEL_ACTIVE,
+ MENUFONT_SEL_INACTIVE,
+ MENUFONT_MOUSE
+};
+
+RtCharset *fontStyles[4];
+RtCharsetDesc fontDesc;
+int fontscale = 1; // not supported right now
+
+Pt
+fontGetStringSize(const char *s)
+{
+ Pt sz = { 0, 0 };
+ int x;
+ char c;
+ sz.y = fontDesc.height*fontscale; // always assume one line;
+ x = 0;
+ while(c = *s++){
+ if(c == '\n'){
+ sz.y += fontDesc.height*fontscale;
+ if(x > sz.x)
+ sz.x = x;
+ x = 0;
+ }else
+ x += fontDesc.width*fontscale;
+ }
+ if(x > sz.x)
+ sz.x = x;
+ return sz;
+}
+
+Pt
+fontPrint(const char *s, float x, float y, int style)
+{
+ RtCharsetPrintBuffered(fontStyles[style], s, x, y, false);
+ return fontGetStringSize(s);
+}
+
+int
+fontGetLen(int len)
+{
+ return len*fontDesc.width*fontscale;
+}
+
+
+void
+createMenuFont(void)
+{
+ OpenCharsetSafe();
+
+ RwRGBA fg_normal = { 255, 255, 255, 255 };
+ RwRGBA bg_normal = { 255, 255, 255, 0 };
+ fontStyles[MENUFONT_NORMAL] = RtCharsetCreate(&fg_normal, &bg_normal);
+ assert(fontStyles[MENUFONT_NORMAL]);
+
+ RwRGBA fg_sel_active = { 200, 200, 200, 255 };
+ RwRGBA bg_sel_active = { 132, 132, 132, 255 };
+ fontStyles[MENUFONT_SEL_ACTIVE] = RtCharsetCreate(&fg_sel_active, &bg_sel_active);
+ assert(fontStyles[MENUFONT_SEL_ACTIVE]);
+
+ RwRGBA fg_sel_inactive = { 200, 200, 200, 255 };
+ RwRGBA bg_sel_inactive = { 200, 200, 200, 0 };
+ fontStyles[MENUFONT_SEL_INACTIVE] = RtCharsetCreate(&fg_sel_inactive, &bg_sel_inactive);
+ assert(fontStyles[MENUFONT_SEL_INACTIVE]);
+
+ RwRGBA fg_mouse = { 255, 255, 255, 255 };
+ RwRGBA bg_mouse = { 132, 132, 132, 255 };
+ fontStyles[MENUFONT_MOUSE] = RtCharsetCreate(&fg_mouse, &bg_mouse);
+ assert(fontStyles[MENUFONT_MOUSE]);
+
+ RtCharsetGetDesc(fontStyles[MENUFONT_NORMAL], &fontDesc);
+}
+
+
+
+
+
+
+
+enum EntryType
+{
+ MENUEMPTY = 0,
+ MENUSUB,
+ MENUVAR,
+
+ MENUVAR_INT,
+ MENUVAR_FLOAT,
+ MENUVAR_CMD,
+
+ MENUSCROLL // dummy
+};
+
+struct Menu
+{
+ Menu *parent;
+ RwRect r;
+ MenuEntry *entries;
+ int numEntries;
+ int maxNameWidth, maxValWidth;
+
+ MenuEntry *findEntry(const char *entryname);
+ void insertEntrySorted(MenuEntry *entry);
+ void appendEntry(MenuEntry *entry);
+
+ bool isScrollingUp, isScrollingDown;
+ int scrollStart;
+ int numVisible;
+ RwRect scrollUpR, scrollDownR;
+ void scroll(int off);
+
+ int selection;
+ MenuEntry *selectedEntry; // updated by update
+ void changeSelection(int newsel);
+ void changeSelection(MenuEntry *e);
+
+ void update(void);
+ void draw(void);
+ Menu(void){ memset(this, 0, sizeof(Menu)); }
+};
+extern Menu toplevel;
+
+struct MenuEntry_Sub : MenuEntry
+{
+ Menu *submenu;
+
+ MenuEntry_Sub(const char *name, Menu *menu);
+};
+
+struct MenuEntry_Var : MenuEntry
+{
+ int maxvallen;
+ int vartype;
+ bool wrapAround;
+
+ virtual void processInput(bool mouseOver, bool selected) = 0;
+ int getValWidth(void) { return maxvallen; }
+ virtual void getValStr(char *str, int len) = 0;
+ MenuEntry_Var(const char *name, int type);
+};
+
+struct MenuEntry_Int : MenuEntry_Var
+{
+ virtual void setStrings(const char **strings) = 0;
+ virtual int findStringLen(void) = 0;
+ MenuEntry_Int(const char *name);
+};
+
+#define INTTYPES \
+ X(Int8, int8, 4, "%4" PRId8) \
+ X(Int16, int16, 6, "%6" PRId16) \
+ X(Int32, int32, 11, "%11" PRId32) \
+ X(Int64, int64, 21, "%21" PRId64) \
+ X(UInt8, uint8, 4, "%4" PRIu8) \
+ X(UInt16, uint16, 6, "%6" PRIu16) \
+ X(UInt32, uint32, 11, "%11" PRIu32) \
+ X(UInt64, uint64, 21, "%21" PRIu64)
+#define FLOATTYPES \
+ X(Float32, float, 11, "%11.3f") \
+ X(Float64, double, 11, "%11.3lf")
+
+#define X(NAME, TYPE, MAXLEN, FMT) \
+struct MenuEntry_##NAME : MenuEntry_Int \
+{ \
+ TYPE *variable; \
+ TYPE lowerBound, upperBound; \
+ TYPE step; \
+ TriggerFunc triggerFunc; \
+ const char *fmt; \
+ const char **strings; \
+ \
+ void processInput(bool mouseOver, bool selected); \
+ void getValStr(char *str, int len); \
+ \
+ void setStrings(const char **strings); \
+ int findStringLen(void); \
+ MenuEntry_##NAME(const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound, const char **strings); \
+};
+INTTYPES
+#undef X
+
+#define X(NAME, TYPE, MAXLEN, FMT) \
+struct MenuEntry_##NAME : MenuEntry_Var \
+{ \
+ TYPE *variable; \
+ TYPE lowerBound, upperBound; \
+ TYPE step; \
+ TriggerFunc triggerFunc; \
+ const char *fmt; \
+ \
+ void processInput(bool mouseOver, bool selected); \
+ void getValStr(char *str, int len); \
+ \
+ MenuEntry_##NAME(const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound); \
+};
+FLOATTYPES
+#undef X
+
+struct MenuEntry_Cmd : MenuEntry_Var
+{
+ TriggerFunc triggerFunc;
+
+ void processInput(bool mouseOver, bool selected);
+ void getValStr(char *str, int len);
+
+ MenuEntry_Cmd(const char *name, TriggerFunc triggerFunc);
+};
+
+
+Menu *findMenu(const char *name);
+
+
+
+#define MUHKEYS \
+ X(leftjustdown, rsLEFT) \
+ X(rightjustdown, rsRIGHT) \
+ X(upjustdown, rsUP) \
+ X(downjustdown, rsDOWN) \
+ X(pgupjustdown, rsPGUP) \
+ X(pgdnjustdown, rsPGDN)
+
+#define MUHBUTTONS \
+ X(button1justdown, 1) \
+ X(button2justdown, 2) \
+ X(button3justdown, 3)
+
+#define REPEATDELAY 700
+#define REPEATINTERVAL 50
+#define X(var, keycode) static int var;
+MUHKEYS
+#undef X
+static int downtime;
+static int repeattime;
+static int lastkeydown;
+static int *keyptr;
+
+static int buttondown[3];
+static int lastbuttondown;
+static int *buttonptr;
+static int button1justdown, button2justdown, button3justdown;
+static float mouseX, mouseY;
+
+static int menuOn;
+static int menuInitialized;
+static int screenWidth, screenHeight;
+static RwRaster *cursor, *arrow;
+
+static int firstBorder = 10;
+static int topBorder = 40; //10;
+static int leading = 4;
+static int gap = 10;
+static int minwidth = 100;
+
+void drawMouse(void);
+void drawArrow(RwRect r, int direction, int style);
+
+Menu toplevel;
+Menu *activeMenu = &toplevel;
+Menu *deepestMenu = &toplevel;
+Menu *mouseOverMenu;
+MenuEntry *mouseOverEntry;
+MenuEntry scrollUpEntry("SCROLLUP"), scrollDownEntry("SCROLLDOWN"); // dummies
+
+
+#define KEYJUSTDOWN(k) ControlsManager.GetIsKeyboardKeyJustDown((RsKeyCodes)k)
+#define KEYDOWN(k) ControlsManager.GetIsKeyboardKeyDown((RsKeyCodes)k)
+#define CTRLJUSTDOWN(key) \
+ ((KEYDOWN(rsLCTRL) || KEYDOWN(rsRCTRL)) && KEYJUSTDOWN((RsKeyCodes)key) || \
+ (KEYJUSTDOWN(rsLCTRL) || KEYJUSTDOWN(rsRCTRL)) && KEYDOWN((RsKeyCodes)key))
+#define CTRLDOWN(key) ((KEYDOWN(rsLCTRL) || KEYDOWN(rsRCTRL)) && KEYDOWN((RsKeyCodes)key))
+
+
+bool
+isMouseInRect(RwRect r)
+{
+ return (mouseX >= r.x && mouseX < r.x+r.w &&
+ mouseY >= r.y && mouseY < r.y+r.h);
+}
+
+/*
+ * MenuEntry
+ */
+
+MenuEntry::MenuEntry(const char *name)
+{
+ this->type = MENUEMPTY;
+ this->name = strdup(name);
+ this->next = nil;
+ this->menu = nil;
+}
+
+MenuEntry_Sub::MenuEntry_Sub(const char *name, Menu *menu)
+: MenuEntry(name)
+{
+ this->type = MENUSUB;
+ this->submenu = menu;
+}
+
+MenuEntry_Var::MenuEntry_Var(const char *name, int vartype)
+: MenuEntry(name)
+{
+ this->type = MENUVAR;
+ this->vartype = vartype;
+ this->maxvallen = 0;
+ this->wrapAround = false;
+}
+
+/*
+ * *****************************
+ * MenuEntry_Int
+ * *****************************
+ */
+
+MenuEntry_Int::MenuEntry_Int(const char *name)
+: MenuEntry_Var(name, MENUVAR_INT)
+{
+}
+
+#define X(NAME, TYPE, MAXLEN, FMT) \
+int \
+MenuEntry_##NAME::findStringLen(void){ \
+ TYPE i; \
+ int len, maxlen = 0; \
+ for(i = this->lowerBound; i <= this->upperBound; i++){ \
+ len = strlen(this->strings[i-this->lowerBound]); \
+ if(len > maxlen) \
+ maxlen = len; \
+ } \
+ return maxlen; \
+} \
+void \
+MenuEntry_##NAME::processInput(bool mouseOver, bool selected) \
+{ \
+ TYPE v, oldv; \
+ int overflow = 0; \
+ int underflow = 0; \
+ \
+ v = *this->variable; \
+ oldv = v; \
+ \
+ if((selected && leftjustdown) || (mouseOver && button3justdown)){ \
+ v -= this->step; \
+ if(v > oldv) \
+ underflow = 1; \
+ } \
+ if((selected && rightjustdown) || (mouseOver && button1justdown)){ \
+ v += this->step; \
+ if(v < oldv) \
+ overflow = 1; \
+ } \
+ if(this->wrapAround){ \
+ if(v > this->upperBound || overflow) v = this->lowerBound; \
+ if(v < this->lowerBound || underflow) v = this->upperBound; \
+ }else{ \
+ if(v > this->upperBound || overflow) v = this->upperBound; \
+ if(v < this->lowerBound || underflow) v = this->lowerBound; \
+ } \
+ \
+ *this->variable = v; \
+ if(oldv != v && this->triggerFunc) \
+ this->triggerFunc(); \
+} \
+void \
+MenuEntry_##NAME::getValStr(char *str, int len) \
+{ \
+ static char tmp[20]; \
+ if(this->strings){ \
+ snprintf(tmp, 20, "%%%ds", this->maxvallen); \
+ if(*this->variable < this->lowerBound || *this->variable > this->upperBound){ \
+ snprintf(str, len, "ERROR"); \
+ return; \
+ } \
+ snprintf(str, len, tmp, this->strings[*this->variable-this->lowerBound]); \
+ }else \
+ snprintf(str, len, this->fmt, *this->variable); \
+} \
+void \
+MenuEntry_##NAME::setStrings(const char **strings) \
+{ \
+ this->strings = strings; \
+ if(this->strings) \
+ this->maxvallen = findStringLen(); \
+} \
+MenuEntry_##NAME::MenuEntry_##NAME(const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound, const char **strings) \
+: MenuEntry_Int(name) \
+{ \
+ this->variable = ptr; \
+ this->step = step; \
+ this->lowerBound = lowerBound; \
+ this->upperBound = upperBound; \
+ this->triggerFunc = triggerFunc; \
+ this->maxvallen = MAXLEN; \
+ this->fmt = FMT; \
+ this->setStrings(strings); \
+}
+INTTYPES
+#undef X
+
+/*
+ * *****************************
+ * MenuEntry_Float
+ * *****************************
+ */
+
+#define X(NAME, TYPE, MAXLEN, FMT) \
+MenuEntry_##NAME::MenuEntry_##NAME(const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound) \
+: MenuEntry_Var(name, MENUVAR_FLOAT) \
+{ \
+ this->variable = ptr; \
+ this->step = step; \
+ this->lowerBound = lowerBound; \
+ this->upperBound = upperBound; \
+ this->triggerFunc = triggerFunc; \
+ this->maxvallen = MAXLEN; \
+ this->fmt = FMT; \
+} \
+void \
+MenuEntry_##NAME::getValStr(char *str, int len) \
+{ \
+ snprintf(str, len, this->fmt, *this->variable); \
+} \
+void \
+MenuEntry_##NAME::processInput(bool mouseOver, bool selected) \
+{ \
+ float v, oldv; \
+ int overflow = 0; \
+ int underflow = 0; \
+ \
+ v = *this->variable; \
+ oldv = v; \
+ \
+ if((selected && leftjustdown) || (mouseOver && button3justdown)){ \
+ v -= this->step; \
+ if(v > oldv) \
+ underflow = 1; \
+ } \
+ if((selected && rightjustdown) || (mouseOver && button1justdown)){ \
+ v += this->step; \
+ if(v < oldv) \
+ overflow = 1; \
+ } \
+ if(this->wrapAround){ \
+ if(v > this->upperBound || overflow) v = this->lowerBound; \
+ if(v < this->lowerBound || underflow) v = this->upperBound; \
+ }else{ \
+ if(v > this->upperBound || overflow) v = this->upperBound; \
+ if(v < this->lowerBound || underflow) v = this->lowerBound; \
+ } \
+ \
+ *this->variable = v; \
+ if(oldv != v && this->triggerFunc) \
+ this->triggerFunc(); \
+}
+
+FLOATTYPES
+#undef X
+
+/*
+ * *****************************
+ * MenuEntry_Cmd
+ * *****************************
+ */
+
+void
+MenuEntry_Cmd::processInput(bool mouseOver, bool selected)
+{
+ // Don't execute on button3
+ if(this->triggerFunc && (selected && (leftjustdown || rightjustdown) || (mouseOver && button1justdown)))
+ this->triggerFunc();
+}
+
+void
+MenuEntry_Cmd::getValStr(char *str, int len)
+{
+ strncpy(str, "<", len);
+}
+
+MenuEntry_Cmd::MenuEntry_Cmd(const char *name, TriggerFunc triggerFunc)
+: MenuEntry_Var(name, MENUVAR_CMD)
+{
+ this->maxvallen = 1;
+ this->triggerFunc = triggerFunc;
+}
+
+
+/*
+ * *****************************
+ * Menu
+ * *****************************
+ */
+
+void
+Menu::scroll(int off) {
+ if(isScrollingUp && off < 0)
+ scrollStart += off;
+ if(isScrollingDown && off > 0)
+ scrollStart += off;
+ if(scrollStart < 0) scrollStart = 0;
+ if(scrollStart > numEntries-numVisible) scrollStart = numEntries-numVisible;
+}
+
+void
+Menu::changeSelection(int newsel){
+ selection = newsel;
+ if(selection < 0) selection = 0;
+ if(selection >= numEntries) selection = numEntries-1;
+ if(selection < scrollStart) scrollStart = selection;
+ if(selection >= scrollStart+numVisible) scrollStart = selection-numVisible+1;
+}
+
+void
+Menu::changeSelection(MenuEntry *sel)
+{
+ MenuEntry *e;
+ int i = 0;
+ for(e = this->entries; e; e = e->next){
+ if(e == sel){
+ this->selection = i;
+ this->selectedEntry = sel;
+ break;
+ }
+ i++;
+ }
+}
+
+
+
+MenuEntry*
+Menu::findEntry(const char *entryname)
+{
+ MenuEntry *m;
+ for(m = this->entries; m; m = m->next)
+ if(strcmp(entryname, m->name) == 0)
+ return m;
+ return nil;
+}
+
+void
+Menu::insertEntrySorted(MenuEntry *entry)
+{
+ MenuEntry **mp;
+ int cmp;
+ for(mp = &this->entries; *mp; mp = &(*mp)->next){
+ cmp = strcmp(entry->name, (*mp)->name);
+ if(cmp == 0)
+ return;
+ if(cmp < 0)
+ break;
+ }
+ entry->next = *mp;
+ *mp = entry;
+ entry->menu = this;
+ this->numEntries++;
+}
+
+void
+Menu::appendEntry(MenuEntry *entry)
+{
+ MenuEntry **mp;
+ for(mp = &this->entries; *mp; mp = &(*mp)->next);
+ entry->next = *mp;
+ *mp = entry;
+ entry->menu = this;
+ this->numEntries++;
+}
+
+void
+Menu::update(void)
+{
+ int i;
+ int x, y;
+ Pt sz;
+ MenuEntry *e;
+ int onscreen;
+ x = this->r.x;
+ y = this->r.y + 18;
+ int end = this->r.y+this->r.h - 18;
+ this->numVisible = 0;
+
+ deepestMenu = this;
+
+ int bottomy = end;
+ onscreen = 1;
+ i = 0;
+ this->maxNameWidth = 0;
+ this->maxValWidth = 0;
+ this->isScrollingUp = this->scrollStart > 0;
+ this->isScrollingDown = false;
+ this->selectedEntry = nil;
+ for(e = this->entries; e; e = e->next){
+ sz = fontGetStringSize(e->name);
+ e->r.x = x;
+ e->r.y = y;
+ e->r.w = sz.x;
+ e->r.h = sz.y;
+
+ if(i == this->selection)
+ this->selectedEntry = e;
+
+ if(i >= this->scrollStart)
+ y += sz.y + leading*fontscale;
+ if(y >= end){
+ this->isScrollingDown = true;
+ onscreen = 0;
+ }else
+ bottomy = y;
+ if(i >= this->scrollStart && onscreen)
+ this->numVisible++;
+
+ if(e->type == MENUVAR){
+ int valwidth = fontGetLen(((MenuEntry_Var*)e)->getValWidth());
+ if(valwidth > maxValWidth)
+ maxValWidth = valwidth;
+ }
+ if(e->r.w > maxNameWidth)
+ maxNameWidth = e->r.w;
+ i++;
+ }
+ if(this->r.w < maxNameWidth + maxValWidth + gap*fontscale)
+ this->r.w = maxNameWidth + maxValWidth + gap*fontscale;
+
+ this->scrollUpR = this->r;
+ this->scrollUpR.h = 16;
+ this->scrollDownR = this->scrollUpR;
+ this->scrollDownR.y = bottomy;
+
+ // Update active submenu
+ if(this->selectedEntry && this->selectedEntry->type == MENUSUB){
+ Menu *submenu = ((MenuEntry_Sub*)this->selectedEntry)->submenu;
+ submenu->r.x = this->r.x+this->r.w + 10;
+ submenu->r.y = this->r.y;
+ submenu->r.w = minwidth; // update menu will expand
+ submenu->r.h = this->r.h;
+ submenu->update();
+ }
+}
+
+void
+Menu::draw(void)
+{
+ static char val[100];
+ int i;
+ MenuEntry *e;
+ i = 0;
+ for(e = this->entries; e; e = e->next){
+ if(i >= this->scrollStart+this->numVisible)
+ break;
+ if(i >= this->scrollStart){
+ int style = MENUFONT_NORMAL;
+ if(i == this->selection)
+ style = this == activeMenu ? MENUFONT_SEL_ACTIVE : MENUFONT_SEL_INACTIVE;
+ if(style != MENUFONT_SEL_ACTIVE && e == mouseOverEntry)
+ style = MENUFONT_MOUSE;
+ fontPrint(e->name, e->r.x, e->r.y, style);
+ if(e->type == MENUVAR){
+ int valw = fontGetLen(((MenuEntry_Var*)e)->getValWidth());
+ ((MenuEntry_Var*)e)->getValStr(val, 100);
+ fontPrint(val, e->r.x+this->r.w-valw, e->r.y, style);
+ }
+ }
+ i++;
+ }
+
+ if(this->isScrollingUp)
+ drawArrow(this->scrollUpR, -1, isMouseInRect(this->scrollUpR));
+ if(this->isScrollingDown)
+ drawArrow(this->scrollDownR, 1, isMouseInRect(this->scrollDownR));
+
+ if(this->selectedEntry && this->selectedEntry->type == MENUSUB)
+ ((MenuEntry_Sub*)this->selectedEntry)->submenu->draw();
+}
+
+Menu*
+findMenu(const char *name)
+{
+ Menu *m;
+ MenuEntry *e;
+ char *tmppath = strdup(name);
+ char *next, *curname;
+
+ curname = tmppath;
+ next = curname;
+
+ m = &toplevel;
+ while(*next){
+ curname = next;
+ while(*next){
+ if(*next == '|'){
+ *next++ = '\0';
+ break;
+ }
+ next++;
+ }
+ e = m->findEntry(curname);
+ if(e){
+ // return an error if the entry exists but isn't a menu
+ if(e->type != MENUSUB){
+ free(tmppath);
+ return nil;
+ }
+ m = ((MenuEntry_Sub*)e)->submenu;
+ }else{
+ // Create submenus that don't exist yet
+ Menu *submenu = new Menu();
+ submenu->parent = m;
+ MenuEntry *me = new MenuEntry_Sub(curname, submenu);
+ // Don't sort submenus outside the toplevel menu
+ if(m == &toplevel)
+ m->insertEntrySorted(me);
+ else
+ m->appendEntry(me);
+ m = submenu;
+ }
+ }
+
+ free(tmppath);
+ return m;
+}
+
+/*
+ * ****************
+ * debug menu
+ * ****************
+ */
+
+static uint8 cursorPx[] = {
+#include "cursor.inc"
+};
+
+static uint8 arrowPx[] = {
+#include "arrow.inc"
+};
+
+void
+initDebug(void)
+{
+ createMenuFont();
+
+ RwInt32 w, h, d, flags;
+ RwImage *img = RwImageCreate(16, 16, 32);
+ assert(img);
+ RwImageSetPixels(img, cursorPx);
+ RwImageSetStride(img, RwImageGetWidth(img)*4);
+ RwImageFindRasterFormat(img, rwRASTERTYPETEXTURE, &w, &h, &d, &flags);
+ cursor = RwRasterCreate(w, h, d, flags);
+ cursor = RwRasterSetFromImage(cursor, img);
+ assert(cursor);
+ RwImageDestroy(img);
+
+ img = RwImageCreate(32, 16, 32);
+ assert(img);
+ RwImageSetPixels(img, arrowPx);
+ RwImageSetStride(img, RwImageGetWidth(img)*4);
+ RwImageFindRasterFormat(img, rwRASTERTYPETEXTURE, &w, &h, &d, &flags);
+ arrow = RwRasterCreate(w, h, d, flags);
+ arrow = RwRasterSetFromImage(arrow, img);
+ assert(arrow);
+ RwImageDestroy(img);
+}
+
+void
+processInput(void)
+{
+ int shift = KEYDOWN(rsRSHIFT) || KEYDOWN(rsLSHIFT);
+#define X(var, keycode) var = KEYJUSTDOWN(keycode);
+ MUHKEYS
+#undef X
+
+ // Implement auto-repeat
+#define X(var, keycode) \
+ if(var){ \
+ repeattime = downtime = CTimer::GetTimeInMilliseconds(); \
+ lastkeydown = keycode; \
+ keyptr = &var; \
+ }
+ MUHKEYS
+#undef X
+ if(lastkeydown){
+ if(KEYDOWN(lastkeydown)){
+ int curtime = CTimer::GetTimeInMilliseconds();
+ if(curtime - downtime > REPEATDELAY){
+ if(curtime - repeattime > REPEATINTERVAL){
+ repeattime = curtime;
+ *keyptr = 1;
+ }
+ }
+ }else{
+ lastkeydown = 0;
+ }
+ }
+
+ // Also for mouse buttons
+#define X(var, num) \
+ if(var){ \
+ repeattime = downtime = CTimer::GetTimeInMilliseconds(); \
+ lastbuttondown = num; \
+ buttonptr = &var; \
+ }
+ MUHBUTTONS
+#undef X
+ if(lastbuttondown){
+ if(buttondown[lastbuttondown-1]){
+ int curtime = CTimer::GetTimeInMilliseconds();
+ if(curtime - downtime > REPEATDELAY){
+ if(curtime - repeattime > REPEATINTERVAL){
+ repeattime = curtime;
+ *buttonptr = 1;
+ }
+ }
+ }else{
+ lastbuttondown = 0;
+ }
+ }
+
+ // Walk through all visible menus and figure out which one the mouse is over
+ mouseOverMenu = nil;
+ mouseOverEntry = nil;
+ Menu *menu;
+ for(menu = deepestMenu; menu; menu = menu->parent)
+ if(isMouseInRect(menu->r)){
+ mouseOverMenu = menu;
+ break;
+ }
+ if(mouseOverMenu){
+ // Walk all visibile entries and figure out which one the mouse is over
+ MenuEntry *e;
+ int i = 0;
+ for(e = mouseOverMenu->entries; e; e = e->next){
+ if(i >= mouseOverMenu->scrollStart+mouseOverMenu->numVisible)
+ break;
+ if(i >= mouseOverMenu->scrollStart){
+ RwRect r = e->r;
+ r.w = mouseOverMenu->r.w; // span the whole menu
+ if(isMouseInRect(r)){
+ mouseOverEntry = e;
+ break;
+ }
+ }
+ i++;
+ }
+ if(mouseOverMenu->isScrollingUp && isMouseInRect(mouseOverMenu->scrollUpR)){
+ mouseOverEntry = &scrollUpEntry;
+ mouseOverEntry->r = mouseOverMenu->scrollUpR;
+ mouseOverEntry->menu = mouseOverMenu;
+ mouseOverEntry->type = MENUSCROLL;
+ }
+ if(mouseOverMenu->isScrollingDown && isMouseInRect(mouseOverMenu->scrollDownR)){
+ mouseOverEntry = &scrollDownEntry;
+ mouseOverEntry->r = mouseOverMenu->scrollDownR;
+ mouseOverEntry->menu = mouseOverMenu;
+ mouseOverEntry->type = MENUSCROLL;
+ }
+ }
+
+ if(pgupjustdown)
+ activeMenu->scroll(shift ? -5 : -1);
+ if(pgdnjustdown)
+ activeMenu->scroll(shift ? 5 : 1);
+ if(downjustdown)
+ activeMenu->changeSelection(activeMenu->selection + (shift ? 5 : 1));
+ if(upjustdown)
+ activeMenu->changeSelection(activeMenu->selection - (shift ? 5 : 1));
+
+ if(CPad::NewMouseControllerState.WHEELUP){
+ if(mouseOverMenu)
+ activeMenu = mouseOverMenu;
+ activeMenu->scroll(shift ? -5 : -1);
+ }
+ if(CPad::NewMouseControllerState.WHEELDN){
+ if(mouseOverMenu)
+ activeMenu = mouseOverMenu;
+ activeMenu->scroll(shift ? 5 : 1);
+ }
+
+ if(mouseOverEntry == &scrollUpEntry){
+ if(button1justdown){
+ activeMenu = mouseOverEntry->menu;
+ activeMenu->scroll(shift ? -5 : -1);
+ }
+ }
+ if(mouseOverEntry == &scrollDownEntry){
+ if(button1justdown){
+ activeMenu = mouseOverEntry->menu;
+ activeMenu->scroll(shift ? 5 : 1);
+ }
+ }
+
+ // Have to call this before processInput below because menu entry can change
+ if((button1justdown || button3justdown) && mouseOverEntry){
+ activeMenu = mouseOverEntry->menu;
+ activeMenu->changeSelection(mouseOverEntry);
+ }
+ if(KEYJUSTDOWN(rsENTER)){
+ if(activeMenu->selectedEntry && activeMenu->selectedEntry->type == MENUSUB)
+ activeMenu = ((MenuEntry_Sub*)activeMenu->selectedEntry)->submenu;
+ }else if(KEYJUSTDOWN(rsBACKSP)){
+ if(activeMenu->parent)
+ activeMenu = activeMenu->parent;
+ }else{
+ if(mouseOverEntry && mouseOverEntry->type == MENUVAR)
+ ((MenuEntry_Var*)mouseOverEntry)->processInput(true, mouseOverEntry == activeMenu->selectedEntry);
+ if(activeMenu->selectedEntry && activeMenu->selectedEntry->type == MENUVAR &&
+ mouseOverEntry != activeMenu->selectedEntry)
+ ((MenuEntry_Var*)activeMenu->selectedEntry)->processInput(false, true);
+ }
+}
+
+void
+updateMouse(void)
+{
+ CPad *pad = CPad::GetPad(0);
+ int dirX = 1;
+ int dirY = 1;
+
+ if(MousePointerStateHelper.bInvertHorizontally) dirX = -1;
+ if(MousePointerStateHelper.bInvertVertically) dirY = -1;
+
+ mouseX += pad->NewMouseControllerState.x*dirX;
+ mouseY += pad->NewMouseControllerState.y*dirY;
+
+ if(mouseX < 0.0f) mouseX = 0.0f;
+ if(mouseY < 0.0f) mouseY = 0.0f;
+ if(mouseX >= screenWidth) mouseX = screenWidth;
+ if(mouseY >= screenHeight) mouseY = screenHeight;
+
+ button1justdown = pad->NewMouseControllerState.LMB && !pad->OldMouseControllerState.LMB;
+ button2justdown = pad->NewMouseControllerState.MMB && !pad->OldMouseControllerState.MMB;
+ button3justdown = pad->NewMouseControllerState.RMB && !pad->OldMouseControllerState.RMB;
+ buttondown[0] = pad->NewMouseControllerState.LMB;
+ buttondown[1] = pad->NewMouseControllerState.MMB;
+ buttondown[2] = pad->NewMouseControllerState.RMB;
+
+ // Zero the mouse position so the camera won't move
+ pad->NewMouseControllerState.x = 0.0f;
+ pad->NewMouseControllerState.y = 0.0f;
+}
+
+void
+DebugMenuProcess(void)
+{
+ // We only process some input here
+
+ CPad *pad = CPad::GetPad(0);
+ if(CTRLJUSTDOWN('M'))
+ menuOn = !menuOn;
+ if(!menuOn)
+ return;
+
+ pad->DisablePlayerControls = 1;
+ // TODO: this could happen earlier
+ if(!menuInitialized){
+ initDebug();
+ menuInitialized = 1;
+ }
+ updateMouse();
+
+}
+
+#ifdef LIBRW
+#define CURRENTCAM (rw::engine->currentCamera)
+#else
+#define CURRENTCAM ((RwCamera*)RWSRCGLOBAL(curCamera))
+#endif
+
+void
+DebugMenuRender(void)
+{
+ if(!menuOn)
+ return;
+
+ RwRenderStateSet(rwRENDERSTATEZTESTENABLE, 0);
+ RwRenderStateSet(rwRENDERSTATEZWRITEENABLE, 0);
+ RwRenderStateSet(rwRENDERSTATEVERTEXALPHAENABLE, (void*)TRUE);
+ RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)rwBLENDSRCALPHA);
+ RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)rwBLENDINVSRCALPHA);
+ RwRenderStateSet(rwRENDERSTATEFOGENABLE, 0);
+ RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)rwCULLMODECULLNONE);
+
+ RwCamera *cam = CURRENTCAM;
+ screenWidth = RwRasterGetWidth(RwCameraGetRaster(cam));
+ screenHeight = RwRasterGetHeight(RwCameraGetRaster(cam));
+
+// if(screenHeight > 1080)
+// fontscale = 2;
+// else
+ fontscale = 1;
+
+ Pt sz;
+ sz = fontPrint("Debug Menu", firstBorder*fontscale+30, topBorder, 0);
+
+ toplevel.r.x = firstBorder*fontscale;
+ toplevel.r.y = topBorder + sz.y + 10;
+ toplevel.r.w = minwidth; // update menu will expand
+ toplevel.r.h = screenHeight - 10 - toplevel.r.y;
+ toplevel.update();
+ toplevel.draw();
+ processInput();
+ RtCharsetBufferFlush();
+
+ drawMouse();
+}
+
+
+
+void
+drawArrow(RwRect r, int direction, int style)
+{
+ static RwImVertexIndex indices[] = { 0, 1, 2, 2, 1, 3 };
+ static RwIm2DVertex arrowVerts[4];
+
+ RwCamera *cam = CURRENTCAM;
+ float recipz = 1.0f/RwCameraGetNearClipPlane(cam);
+
+ int width = RwRasterGetWidth(arrow);
+ int height = RwRasterGetHeight(arrow);
+
+ int left = r.x + (r.w - width)/2;
+ int right = left + width;
+ int top = r.y;
+ int bottom = r.y+r.h;
+
+ float umin = 0.5f / width;
+ float vmin = 0.5f / height;
+ float umax = (width + 0.5f) / width;
+ float vmax = (height + 0.5f) / height;
+ if(direction < 0){
+ vmin = (height - 0.5f) / height;
+ vmax = -0.5f / height;
+ }
+
+ if(style){
+ RwIm2DVertexSetScreenX(&arrowVerts[0], r.x);
+ RwIm2DVertexSetScreenY(&arrowVerts[0], r.y-1);
+ RwIm2DVertexSetScreenZ(&arrowVerts[0], RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(&arrowVerts[0], RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(&arrowVerts[0], recipz);
+ RwIm2DVertexSetIntRGBA(&arrowVerts[0], 132, 132, 132, 255);
+
+ RwIm2DVertexSetScreenX(&arrowVerts[1], r.x+r.w);
+ RwIm2DVertexSetScreenY(&arrowVerts[1], r.y-1);
+ RwIm2DVertexSetScreenZ(&arrowVerts[1], RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(&arrowVerts[1], RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(&arrowVerts[1], recipz);
+ RwIm2DVertexSetIntRGBA(&arrowVerts[1], 132, 132, 132, 255);
+
+ RwIm2DVertexSetScreenX(&arrowVerts[2], r.x);
+ RwIm2DVertexSetScreenY(&arrowVerts[2], r.y+r.h+1);
+ RwIm2DVertexSetScreenZ(&arrowVerts[2], RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(&arrowVerts[2], RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(&arrowVerts[2], recipz);
+ RwIm2DVertexSetIntRGBA(&arrowVerts[2], 132, 132, 132, 255);
+
+ RwIm2DVertexSetScreenX(&arrowVerts[3], r.x+r.w);
+ RwIm2DVertexSetScreenY(&arrowVerts[3], r.y+r.h+1);
+ RwIm2DVertexSetScreenZ(&arrowVerts[3], RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(&arrowVerts[3], RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(&arrowVerts[3], recipz);
+ RwIm2DVertexSetIntRGBA(&arrowVerts[3], 132, 132, 132, 255);
+
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, nil);
+ RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR);
+ RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, arrowVerts, 4, indices, 6);
+ }
+
+
+ RwIm2DVertexSetScreenX(&arrowVerts[0], left);
+ RwIm2DVertexSetScreenY(&arrowVerts[0], top);
+ RwIm2DVertexSetScreenZ(&arrowVerts[0], RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(&arrowVerts[0], RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(&arrowVerts[0], recipz);
+ RwIm2DVertexSetIntRGBA(&arrowVerts[0], 255, 255, 255, 255);
+ RwIm2DVertexSetU(&arrowVerts[0], umin, recipz);
+ RwIm2DVertexSetV(&arrowVerts[0], vmin, recipz);
+
+ RwIm2DVertexSetScreenX(&arrowVerts[1], right);
+ RwIm2DVertexSetScreenY(&arrowVerts[1], top);
+ RwIm2DVertexSetScreenZ(&arrowVerts[1], RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(&arrowVerts[1], RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(&arrowVerts[1], recipz);
+ RwIm2DVertexSetIntRGBA(&arrowVerts[1], 255, 255, 255, 255);
+ RwIm2DVertexSetU(&arrowVerts[1], umax, recipz);
+ RwIm2DVertexSetV(&arrowVerts[1], vmin, recipz);
+
+ RwIm2DVertexSetScreenX(&arrowVerts[2], left);
+ RwIm2DVertexSetScreenY(&arrowVerts[2], bottom);
+ RwIm2DVertexSetScreenZ(&arrowVerts[2], RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(&arrowVerts[2], RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(&arrowVerts[2], recipz);
+ RwIm2DVertexSetIntRGBA(&arrowVerts[2], 255, 255, 255, 255);
+ RwIm2DVertexSetU(&arrowVerts[2], umin, recipz);
+ RwIm2DVertexSetV(&arrowVerts[2], vmax, recipz);
+
+ RwIm2DVertexSetScreenX(&arrowVerts[3], right);
+ RwIm2DVertexSetScreenY(&arrowVerts[3], bottom);
+ RwIm2DVertexSetScreenZ(&arrowVerts[3], RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(&arrowVerts[3], RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(&arrowVerts[3], recipz);
+ RwIm2DVertexSetIntRGBA(&arrowVerts[3], 255, 255, 255, 255);
+ RwIm2DVertexSetU(&arrowVerts[3], umax, recipz);
+ RwIm2DVertexSetV(&arrowVerts[3], vmax, recipz);
+
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, arrow);
+ RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR);
+ RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, arrowVerts, 4, indices, 6);
+}
+
+void
+drawMouse(void)
+{
+ static RwImVertexIndex indices[] = { 0, 1, 2, 2, 1, 3 };
+ static RwIm2DVertex vertices[4];
+ RwIm2DVertex *vert;
+ RwCamera *cam;
+ cam = CURRENTCAM;
+ float x = mouseX;
+ float y = mouseY;
+ float w = RwRasterGetWidth(cursor);
+ float h = RwRasterGetHeight(cursor);
+ float recipz = 1.0f/RwCameraGetNearClipPlane(cam);
+
+ float umin = 0.5f / w;
+ float vmin = 0.5f / h;
+ float umax = (w + 0.5f) / w;
+ float vmax = (h + 0.5f) / h;
+
+ vert = vertices;
+ RwIm2DVertexSetScreenX(vert, x);
+ RwIm2DVertexSetScreenY(vert, y);
+ RwIm2DVertexSetScreenZ(vert, RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(vert, RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(vert, recipz);
+ RwIm2DVertexSetIntRGBA(vert, 255, 255, 255, 255);
+ RwIm2DVertexSetU(vert, umin, recipz);
+ RwIm2DVertexSetV(vert, vmin, recipz);
+ vert++;
+
+ RwIm2DVertexSetScreenX(vert, x+w);
+ RwIm2DVertexSetScreenY(vert, y);
+ RwIm2DVertexSetScreenZ(vert, RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(vert, RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(vert, recipz);
+ RwIm2DVertexSetIntRGBA(vert, 255, 255, 255, 255);
+ RwIm2DVertexSetU(vert, umax, recipz);
+ RwIm2DVertexSetV(vert, vmin, recipz);
+ vert++;
+
+ RwIm2DVertexSetScreenX(vert, x);
+ RwIm2DVertexSetScreenY(vert, y+h);
+ RwIm2DVertexSetScreenZ(vert, RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(vert, RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(vert, recipz);
+ RwIm2DVertexSetIntRGBA(vert, 255, 255, 255, 255);
+ RwIm2DVertexSetU(vert, umin, recipz);
+ RwIm2DVertexSetV(vert, vmax, recipz);
+ vert++;
+
+ RwIm2DVertexSetScreenX(vert, x+w);
+ RwIm2DVertexSetScreenY(vert, y+h);
+ RwIm2DVertexSetScreenZ(vert, RwIm2DGetNearScreenZ());
+ RwIm2DVertexSetCameraZ(vert, RwCameraGetNearClipPlane(cam));
+ RwIm2DVertexSetRecipCameraZ(vert, recipz);
+ RwIm2DVertexSetIntRGBA(vert, 255, 255, 255, 255);
+ RwIm2DVertexSetU(vert, umax, recipz);
+ RwIm2DVertexSetV(vert, vmax, recipz);
+ vert++;
+
+ RwRenderStateSet(rwRENDERSTATETEXTURERASTER, cursor);
+ RwRenderStateSet(rwRENDERSTATETEXTUREFILTER, (void*)rwFILTERLINEAR);
+ RwIm2DRenderIndexedPrimitive(rwPRIMTYPETRILIST, vertices, 4, indices, 6);
+}
+
+
+
+
+/*
+ * Generate interfaces
+ */
+
+
+#define X(NAME, TYPE, unused1, unused2) \
+MenuEntry* \
+DebugMenuAdd##NAME(const char *path, const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound, const char **strings) \
+{ \
+ Menu *m = findMenu(path); \
+ if(m == nil) \
+ return nil; \
+ MenuEntry *e = new MenuEntry_##NAME(name, ptr, triggerFunc, step, lowerBound, upperBound, strings); \
+ m->appendEntry(e); \
+ return e; \
+}
+INTTYPES
+#undef X
+
+#define X(NAME, TYPE, unused1, unused2) \
+MenuEntry* \
+DebugMenuAdd##NAME(const char *path, const char *name, TYPE *ptr, TriggerFunc triggerFunc, TYPE step, TYPE lowerBound, TYPE upperBound) \
+{ \
+ Menu *m = findMenu(path); \
+ if(m == nil) \
+ return nil; \
+ MenuEntry *e = new MenuEntry_##NAME(name, ptr, triggerFunc, step, lowerBound, upperBound); \
+ m->appendEntry(e); \
+ return e; \
+}
+FLOATTYPES
+#undef X
+
+MenuEntry* \
+DebugMenuAddCmd(const char *path, const char *name, TriggerFunc triggerFunc)
+{
+ Menu *m = findMenu(path);
+ if(m == nil)
+ return nil;
+ MenuEntry *e = new MenuEntry_Cmd(name, triggerFunc);
+ m->appendEntry(e);
+ return e;
+}
+
+void
+DebugMenuEntrySetWrap(MenuEntry *e, bool wrap)
+{
+ if(e && e->type == MENUVAR)
+ ((MenuEntry_Var*)e)->wrapAround = wrap;
+}
+
+void
+DebugMenuEntrySetStrings(MenuEntry *e, const char **strings)
+{
+ if(e && e->type == MENUVAR_INT)
+ ((MenuEntry_Int*)e)->setStrings(strings);
+}
+
+void
+DebugMenuEntrySetAddress(MenuEntry *e, void *addr)
+{
+ if(e && e->type == MENUVAR){
+ MenuEntry_Var *ev = (MenuEntry_Var*)e;
+ // HACK - we know the variable field is at the same address
+ // for all int/float classes. let's hope it stays that way.
+ if(ev->vartype = MENUVAR_INT)
+ ((MenuEntry_Int32*)e)->variable = (int32*)addr;
+ else if(ev->vartype = MENUVAR_FLOAT)
+ ((MenuEntry_Float32*)e)->variable = (float*)addr;
+ }
+}
diff --git a/src/extras/debugmenu.h b/src/extras/debugmenu.h
new file mode 100644
index 00000000..29bbd972
--- /dev/null
+++ b/src/extras/debugmenu.h
@@ -0,0 +1,80 @@
+#pragma once
+
+typedef void (*TriggerFunc)(void);
+
+struct Menu;
+
+struct MenuEntry
+{
+ int type;
+ const char *name;
+ MenuEntry *next;
+ RwRect r;
+ Menu *menu;
+
+ MenuEntry(const char *name);
+};
+
+typedef MenuEntry DebugMenuEntry;
+
+MenuEntry *DebugMenuAddInt8(const char *path, const char *name, int8 *ptr, TriggerFunc triggerFunc, int8 step, int8 lowerBound, int8 upperBound, const char **strings);
+MenuEntry *DebugMenuAddInt16(const char *path, const char *name, int16 *ptr, TriggerFunc triggerFunc, int16 step, int16 lowerBound, int16 upperBound, const char **strings);
+MenuEntry *DebugMenuAddInt32(const char *path, const char *name, int32 *ptr, TriggerFunc triggerFunc, int32 step, int32 lowerBound, int32 upperBound, const char **strings);
+MenuEntry *DebugMenuAddInt64(const char *path, const char *name, int64 *ptr, TriggerFunc triggerFunc, int64 step, int64 lowerBound, int64 upperBound, const char **strings);
+MenuEntry *DebugMenuAddUInt8(const char *path, const char *name, uint8 *ptr, TriggerFunc triggerFunc, uint8 step, uint8 lowerBound, uint8 upperBound, const char **strings);
+MenuEntry *DebugMenuAddUInt16(const char *path, const char *name, uint16 *ptr, TriggerFunc triggerFunc, uint16 step, uint16 lowerBound, uint16 upperBound, const char **strings);
+MenuEntry *DebugMenuAddUInt32(const char *path, const char *name, uint32 *ptr, TriggerFunc triggerFunc, uint32 step, uint32 lowerBound, uint32 upperBound, const char **strings);
+MenuEntry *DebugMenuAddUInt64(const char *path, const char *name, uint64 *ptr, TriggerFunc triggerFunc, uint64 step, uint64 lowerBound, uint64 upperBound, const char **strings);
+MenuEntry *DebugMenuAddFloat32(const char *path, const char *name, float *ptr, TriggerFunc triggerFunc, float step, float lowerBound, float upperBound);
+MenuEntry *DebugMenuAddFloat64(const char *path, const char *name, double *ptr, TriggerFunc triggerFunc, double step, double lowerBound, double upperBound);
+MenuEntry *DebugMenuAddCmd(const char *path, const char *name, TriggerFunc triggerFunc);
+void DebugMenuEntrySetWrap(MenuEntry *e, bool wrap);
+void DebugMenuEntrySetStrings(MenuEntry *e, const char **strings);
+void DebugMenuEntrySetAddress(MenuEntry *e, void *addr);
+void DebugMenuProcess(void);
+void DebugMenuRender(void);
+
+
+// Some overloads for simplicity
+inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, int8_t *ptr, TriggerFunc triggerFunc, int8_t step, int8_t lowerBound, int8_t upperBound, const char **strings)
+{ return DebugMenuAddInt8(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
+inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, int16_t *ptr, TriggerFunc triggerFunc, int16_t step, int16_t lowerBound, int16_t upperBound, const char **strings)
+{ return DebugMenuAddInt16(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
+inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, int32_t *ptr, TriggerFunc triggerFunc, int32_t step, int32_t lowerBound, int32_t upperBound, const char **strings)
+{ return DebugMenuAddInt32(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
+inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, int64_t *ptr, TriggerFunc triggerFunc, int64_t step, int64_t lowerBound, int64_t upperBound, const char **strings)
+{ return DebugMenuAddInt64(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
+inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, uint8_t *ptr, TriggerFunc triggerFunc, uint8_t step, uint8_t lowerBound, uint8_t upperBound, const char **strings)
+{ return DebugMenuAddUInt8(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
+inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, uint16_t *ptr, TriggerFunc triggerFunc, uint16_t step, uint16_t lowerBound, uint16_t upperBound, const char **strings)
+{ return DebugMenuAddUInt16(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
+inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, uint32_t *ptr, TriggerFunc triggerFunc, uint32_t step, uint32_t lowerBound, uint32_t upperBound, const char **strings)
+{ return DebugMenuAddUInt32(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
+inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, uint64_t *ptr, TriggerFunc triggerFunc, uint64_t step, uint64_t lowerBound, uint64_t upperBound, const char **strings)
+{ return DebugMenuAddUInt64(path, name, ptr, triggerFunc, step, lowerBound, upperBound, strings); }
+inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, float *ptr, TriggerFunc triggerFunc, float step, float lowerBound, float upperBound)
+{ return DebugMenuAddFloat32(path, name, ptr, triggerFunc, step, lowerBound, upperBound); }
+inline DebugMenuEntry *DebugMenuAddVar(const char *path, const char *name, double *ptr, TriggerFunc triggerFunc, double step, double lowerBound, double upperBound)
+{ return DebugMenuAddFloat64(path, name, ptr, triggerFunc, step, lowerBound, upperBound); }
+
+inline DebugMenuEntry *DebugMenuAddVarBool32(const char *path, const char *name, int32_t *ptr, TriggerFunc triggerFunc)
+{
+ static const char *boolstr[] = { "Off", "On" };
+ DebugMenuEntry *e = DebugMenuAddVar(path, name, ptr, triggerFunc, 1, 0, 1, boolstr);
+ DebugMenuEntrySetWrap(e, true);
+ return e;
+}
+inline DebugMenuEntry *DebugMenuAddVarBool16(const char *path, const char *name, int16_t *ptr, TriggerFunc triggerFunc)
+{
+ static const char *boolstr[] = { "Off", "On" };
+ DebugMenuEntry *e = DebugMenuAddVar(path, name, ptr, triggerFunc, 1, 0, 1, boolstr);
+ DebugMenuEntrySetWrap(e, true);
+ return e;
+}
+inline DebugMenuEntry *DebugMenuAddVarBool8(const char *path, const char *name, int8_t *ptr, TriggerFunc triggerFunc)
+{
+ static const char *boolstr[] = { "Off", "On" };
+ DebugMenuEntry *e = DebugMenuAddVar(path, name, ptr, triggerFunc, 1, 0, 1, boolstr);
+ DebugMenuEntrySetWrap(e, true);
+ return e;
+}
diff --git a/src/extras/inttypes.h b/src/extras/inttypes.h
new file mode 100644
index 00000000..bf0c53e2
--- /dev/null
+++ b/src/extras/inttypes.h
@@ -0,0 +1,216 @@
+#define PRId8 "hhd"
+#define PRId16 "hd"
+#define PRId32 "ld"
+#define PRId64 "lld"
+
+#define PRIdFAST8 "hhd"
+#define PRIdFAST16 "hd"
+#define PRIdFAST32 "ld"
+#define PRIdFAST64 "lld"
+
+#define PRIdLEAST8 "hhd"
+#define PRIdLEAST16 "hd"
+#define PRIdLEAST32 "ld"
+#define PRIdLEAST64 "lld"
+
+#define PRIdMAX "lld"
+#define PRIdPTR "lld"
+
+#define PRIi8 "hhi"
+#define PRIi16 "hi"
+#define PRIi32 "li"
+#define PRIi64 "lli"
+
+#define PRIiFAST8 "hhi"
+#define PRIiFAST16 "hi"
+#define PRIiFAST32 "li"
+#define PRIiFAST64 "lli"
+
+#define PRIiLEAST8 "hhi"
+#define PRIiLEAST16 "hi"
+#define PRIiLEAST32 "li"
+#define PRIiLEAST64 "lli"
+
+#define PRIiMAX "lli"
+#define PRIiPTR "lli"
+
+#define PRIo8 "hho"
+#define PRIo16 "ho"
+#define PRIo32 "lo"
+#define PRIo64 "llo"
+
+#define PRIoFAST8 "hho"
+#define PRIoFAST16 "ho"
+#define PRIoFAST32 "lo"
+#define PRIoFAST64 "llo"
+
+#define PRIoLEAST8 "hho"
+#define PRIoLEAST16 "ho"
+#define PRIoLEAST32 "lo"
+#define PRIoLEAST64 "llo"
+
+#define PRIoMAX "llo"
+#define PRIoPTR "llo"
+
+#define PRIu8 "hhu"
+#define PRIu16 "hu"
+#define PRIu32 "lu"
+#define PRIu64 "llu"
+
+#define PRIuFAST8 "hhu"
+#define PRIuFAST16 "hu"
+#define PRIuFAST32 "lu"
+#define PRIuFAST64 "llu"
+
+#define PRIuLEAST8 "hhu"
+#define PRIuLEAST16 "hu"
+#define PRIuLEAST32 "lu"
+#define PRIuLEAST64 "llu"
+
+#define PRIuMAX "llu"
+#define PRIuPTR "llu"
+
+#define PRIx8 "hhx"
+#define PRIx16 "hx"
+#define PRIx32 "lx"
+#define PRIx64 "llx"
+
+#define PRIxFAST8 "hhx"
+#define PRIxFAST16 "hx"
+#define PRIxFAST32 "lx"
+#define PRIxFAST64 "llx"
+
+#define PRIxLEAST8 "hhx"
+#define PRIxLEAST16 "hx"
+#define PRIxLEAST32 "lx"
+#define PRIxLEAST64 "llx"
+
+#define PRIxMAX "llx"
+#define PRIxPTR "llx"
+
+#define PRIX8 "hhX"
+#define PRIX16 "hX"
+#define PRIX32 "lX"
+#define PRIX64 "llX"
+
+#define PRIXFAST8 "hhX"
+#define PRIXFAST16 "hX"
+#define PRIXFAST32 "lX"
+#define PRIXFAST64 "llX"
+
+#define PRIXLEAST8 "hhX"
+#define PRIXLEAST16 "hX"
+#define PRIXLEAST32 "lX"
+#define PRIXLEAST64 "llX"
+
+#define PRIXMAX "llX"
+#define PRIXPTR "llX"
+
+ /* SCAN FORMAT MACROS */
+#define SCNd8 "hhd"
+#define SCNd16 "hd"
+#define SCNd32 "ld"
+#define SCNd64 "lld"
+
+#define SCNdFAST8 "hhd"
+#define SCNdFAST16 "hd"
+#define SCNdFAST32 "ld"
+#define SCNdFAST64 "lld"
+
+#define SCNdLEAST8 "hhd"
+#define SCNdLEAST16 "hd"
+#define SCNdLEAST32 "ld"
+#define SCNdLEAST64 "lld"
+
+#define SCNdMAX "lld"
+#define SCNdPTR "lld"
+
+#define SCNi8 "hhi"
+#define SCNi16 "hi"
+#define SCNi32 "li"
+#define SCNi64 "lli"
+
+#define SCNiFAST8 "hhi"
+#define SCNiFAST16 "hi"
+#define SCNiFAST32 "li"
+#define SCNiFAST64 "lli"
+
+#define SCNiLEAST8 "hhi"
+#define SCNiLEAST16 "hi"
+#define SCNiLEAST32 "li"
+#define SCNiLEAST64 "lli"
+
+#define SCNiMAX "lli"
+#define SCNiPTR "lli"
+
+#define SCNo8 "hho"
+#define SCNo16 "ho"
+#define SCNo32 "lo"
+#define SCNo64 "llo"
+
+#define SCNoFAST8 "hho"
+#define SCNoFAST16 "ho"
+#define SCNoFAST32 "lo"
+#define SCNoFAST64 "llo"
+
+#define SCNoLEAST8 "hho"
+#define SCNoLEAST16 "ho"
+#define SCNoLEAST32 "lo"
+#define SCNoLEAST64 "llo"
+
+#define SCNoMAX "llo"
+#define SCNoPTR "llo"
+
+#define SCNu8 "hhu"
+#define SCNu16 "hu"
+#define SCNu32 "lu"
+#define SCNu64 "llu"
+
+#define SCNuFAST8 "hhu"
+#define SCNuFAST16 "hu"
+#define SCNuFAST32 "lu"
+#define SCNuFAST64 "llu"
+
+#define SCNuLEAST8 "hhu"
+#define SCNuLEAST16 "hu"
+#define SCNuLEAST32 "lu"
+#define SCNuLEAST64 "llu"
+
+#define SCNuMAX "llu"
+#define SCNuPTR "llu"
+
+#define SCNx8 "hhx"
+#define SCNx16 "hx"
+#define SCNx32 "lx"
+#define SCNx64 "llx"
+
+#define SCNxFAST8 "hhx"
+#define SCNxFAST16 "hx"
+#define SCNxFAST32 "lx"
+#define SCNxFAST64 "llx"
+
+#define SCNxLEAST8 "hhx"
+#define SCNxLEAST16 "hx"
+#define SCNxLEAST32 "lx"
+#define SCNxLEAST64 "llx"
+
+#define SCNxMAX "llx"
+#define SCNxPTR "llx"
+
+#define SCNX8 "hhX"
+#define SCNX16 "hX"
+#define SCNX32 "lX"
+#define SCNX64 "llX"
+
+#define SCNXFAST8 "hhX"
+#define SCNXFAST16 "hX"
+#define SCNXFAST32 "lX"
+#define SCNXFAST64 "llX"
+
+#define SCNXLEAST8 "hhX"
+#define SCNXLEAST16 "hX"
+#define SCNXLEAST32 "lX"
+#define SCNXLEAST64 "llX"
+
+#define SCNXMAX "llX"
+#define SCNXPTR "llX" \ No newline at end of file
diff --git a/src/fakerw/fake.cpp b/src/fakerw/fake.cpp
index 59c01c91..b86e9214 100644
--- a/src/fakerw/fake.cpp
+++ b/src/fakerw/fake.cpp
@@ -176,8 +176,8 @@ const RwChar *RwImageSetPath(const RwChar * path) { Image::setSearchPath(path);
RwImage *RwImageSetStride(RwImage * image, RwInt32 stride) { image->stride = stride; return image; }
RwImage *RwImageSetPixels(RwImage * image, RwUInt8 * pixels) { image->pixels = pixels; return image; }
RwImage *RwImageSetPalette(RwImage * image, RwRGBA * palette) { image->palette = (uint8*)palette; return image; }
-RwInt32 RwImageGetWidth(const RwImage * image);
-RwInt32 RwImageGetHeight(const RwImage * image);
+RwInt32 RwImageGetWidth(const RwImage * image) { return image->width; }
+RwInt32 RwImageGetHeight(const RwImage * image) { return image->height; }
RwInt32 RwImageGetDepth(const RwImage * image);
RwInt32 RwImageGetStride(const RwImage * image);
RwUInt8 *RwImageGetPixels(const RwImage * image);
diff --git a/src/rw/RwHelper.cpp b/src/rw/RwHelper.cpp
index 5aa4475f..02a07558 100644
--- a/src/rw/RwHelper.cpp
+++ b/src/rw/RwHelper.cpp
@@ -9,12 +9,20 @@
RtCharset *debugCharset;
#endif
+static bool charsetOpen;
+void OpenCharsetSafe()
+{
+ if(!charsetOpen)
+ RtCharsetOpen();
+ charsetOpen = true;
+}
+
void CreateDebugFont()
{
#ifndef FINAL
RwRGBA color = { 255, 255, 128, 255 };
RwRGBA colorbg = { 0, 0, 0, 0 };
- RtCharsetOpen();
+ OpenCharsetSafe();
debugCharset = RtCharsetCreate(&color, &colorbg);
#endif
}
@@ -24,6 +32,7 @@ void DestroyDebugFont()
#ifndef FINAL
RtCharsetDestroy(debugCharset);
RtCharsetClose();
+ charsetOpen = false;
#endif
}
diff --git a/src/rw/RwHelper.h b/src/rw/RwHelper.h
index 9f178ec2..e6740031 100644
--- a/src/rw/RwHelper.h
+++ b/src/rw/RwHelper.h
@@ -3,6 +3,7 @@
void *RwMallocAlign(RwUInt32 size, RwUInt32 align);
void RwFreeAlign(void *mem);
+void OpenCharsetSafe();
void CreateDebugFont();
void DestroyDebugFont();
void ObrsPrintfString(const char *str, short x, short y);