emoose / DQXIS-SDK

Wrapper DLL & SDK for Dragon Quest XI S

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Debug menu access

emoose opened this issue · comments

There seems to be a lot of leftovers from the games debug menu in the data files, very likely it can be accessed, but something seems to be blocking it?

UJackUMGManager::OpenMenu(EJackUMGMenuId__MenuDebugMenu) should make it show up, but doesn't for some reason. AFAIK it does activate something, since the pause menu isn't accessible after running this (as if another menu is in front of it), pulling up map window and closing it re-enables pause menu.

Also there's UJackCheatManager::DebugMenuEnabled(bool) & DebugMenu() functions, neither seem to have any effect though.

A field AJackGameModeBase::DebugMenu is a pointer to a AJackDebugMenu, doesn't seem to ever be initialised though, maybe some code needs to be added to create an AJackDebugMenu instance and fill that field, for the game to make use of it? (the dev-console needs almost the exact same thing - ViewportConsole field just needed to be filled with a UConsole instance, and that was all it needed for console to be enabled)

There is a UJackUMGManager::IsDebugMode() function which always returns false, could some code/blueprint be using that to check if debug menu is allowed? (I tried patching the function to make it return 1 instead, didn't seem to change much though, but if native EXE code was using it then it's likely the function was inlined or something instead of calling the execIsDebugMode function)

E: Looking into debug menu stuff further, it seems a lot of it has been stripped out.. eg. pretty much all the UJackGameplayStatics functions related to debug menu (STATIC_AddDebugMenuItem etc) end up calling nullsubs that do nothing, I think the debug menu blueprints might be relying on these functions to populate the menu, so it's no wonder that nothing appears when trying to load it :/

The classes that those UJackGameplayStatics functions would create & return still exist though, maybe with enough work they could be remade somehow...

I did want to ask (perhaps this should be a separate "issue", although really more of a "feature request"):

Is Triple.UTripleCheatManager inhibited in the same way? I can see that the class is there, but the commands (methods) can't be accessed by the console. Perhaps it has to be instantiated and a pointer set? (although offhand, I wouldn't know where that pointer would be, either in JackGame, Triple, or Engine)

Edit: I see a TArray<FString> Engine.AWorldSettings.ConsoleCommands, which (if I had to guess) pulls from Engine.UCheatManager and JackGame.UJackCheatManager. Will keep looking

I did want to ask (perhaps this should be a separate "issue", although really more of a "feature request"):

Is Triple.UTripleCheatManager inhibited in the same way? I can see that the class is there, but the commands (methods) can't be accessed by the console. Perhaps it has to be instantiated and a pointer set? (although offhand, I wouldn't know where that pointer would be, either in JackGame, Triple, or Engine)

Edit: I see a TArray Engine.AWorldSettings.ConsoleCommands, which (if I had to guess) pulls from Engine.UCheatManager and JackGame.UJackCheatManager. Will keep looking

Some additional research on this: https://benui.ca/unreal/cheatmanager/
I see that the TriplePlayerController class has pointers for UClass* CheatClass and UCheatManager* CheatManager (inherited from APlayerController). Currently trying to implement a 2D mode "dash" feature if I can get this to work (thanks to the latest changes in CustomActions.cpp, binds should work in 2D mode).

It seems like there's a TripleCheatManager instance at runtime, but (assuming so) I haven't managed to get it to "attach" to the 2D mode instance (I assume TriplePlayerController).

Strange that TripleCheatManager doesn't seem to inherit UCheatManager at all, tried setting CheatClass/CheatManager to it but I think because it's not UCheatManager it fails, not sure where that's meant to be set up really.

AFAIK for Exec functions to be called (like the cheat cmds inside TripleCheatManager), it either has to be setup as CheatClass/CheatManager, or custom code has to be added that runs UObject::ProcessConsoleExec on the instance of it.

You can see this in Player.cpp, in there the UPlayer::Exec is what calls into CheatManager->ProcessConsoleExec. It's possible they might have added custom code that calls TripleCheatManager->ProcessConsoleExec there, or it could be in one of the other ::Exec handlers (IIRC GameEngine has one, which calls into ULocalPlayer::Exec, which calls UPlayer::Exec, could be wrong though)

E: didn't see any changes to UPlayer::Exec compared to stock 4.18, I guess it's possible the stuff that calls into TripleCheatManager was ifdef'd out of shipping builds though..

E2: ooh interesting, AJackTriplePlayerController::ProcessConsoleExec has had some customisation done to it: https://i.imgur.com/ZoXhvRd.png
Left is DQXI, right is 4.18, hmm, don't really know much about these UE4 modules, but afaik TripleCheatManager is the only part of the Triple_classes file, so maybe this is trying to run ProcessConsoleExec on it somehow.

Seems that first call to v10 + 0x1D8 (0x14215FB10) always returns 0 for some reason, I'd guess that it's probably meant to return a TripleCheatManager instance for the call underneath to run ProcessConsoleExec on it, hm..

E: found a way to hook the StartupModule func for it, seems to work fine without needing to construct anything :)
Just hope it works fine without interfering with anything, not sure if maybe the field we call CheatManager is actually used for something else (didn't see any code that would set it, but I could have missed something)

Only problem I've seen so far is that console says "command not found" even though the command exists & was executed, afaik this is a bug in AJackTriplePlayerController::ProcessConsoleExec, the "result = v7 & ..." should just be "result = ...", luckily isn't too hard to patch that out.


Alright, got UTripleCheatManager working I think, it's a little hacky though since it's constructing a new UTripleCheatManager instance for every console command entered, I think it should be getting set as part of the IModuleInterface returned in that pic above, not sure how to go about that right now though.

typedef bool(*UObject__ProcessConsoleExec_Fn)(UObject* thisptr, void* a2, void* a3, void* a4);
UObject__ProcessConsoleExec_Fn AJackTriplePlayerController__ProcessConsoleExec_Orig;
bool AJackTriplePlayerController__ProcessConsoleExec_Hook(AJackTriplePlayerController* thisptr, void* a2, void* a3, void* a4)
{
  auto res = AJackTriplePlayerController__ProcessConsoleExec_Orig(thisptr, a2, a3, a4);
  if (res)
    return res;

  auto test = (UTripleCheatManager*)StaticConstructObject_Internal(UTripleCheatManager::StaticClass(), thisptr, 0, 0, 0, 0, 0, 0, 0);
  auto UTripleCheatManager__ProcessConsoleExec = (UObject__ProcessConsoleExec_Fn)test->Vtable[0x45]; // +0x228
  return UTripleCheatManager__ProcessConsoleExec(test, a2, a3, a4);
}

MH_CreateHook((LPVOID)(mBaseAddress + 0x912C30), AJackTriplePlayerController__ProcessConsoleExec_Hook, (LPVOID*)&AJackTriplePlayerController__ProcessConsoleExec_Orig);

(also needs DQXIHook.cpp StaticConstructObject_Internal to be marked extern, and typedef for it moved to pch.h moved that stuff in latest commit)

Haven't tested much, but TripleReturnTitle console command seems to work with this at least :D

If the IModuleInterface can be hooked so it creates a UTripleCheatManager as part of the constructor or something I think this hook could be removed, since there's already code there for calling into it etc.

"Incroyable! I do not believet! Young man, it must 'ave been fate zat brought you 'ere! Destiny! la prodence! I sense a great power within you! A power of maximal significance!"

This is huge! I've done a bit of testing with it, very exciting to see what we can find poking around with the 2D mode commands now. Alas, it doesn't look like TripleRunRate does anything in my testing, possibly there was something originally intended by the devs but was stripped out later in development.

RunRate in 3D mode, likewise, is a bit funky (it only activates if you have a menu pulled up with "Z" and deactivates upon EInputEvent::IE_Released with the dash bind), so maybe more testing is needed to figure out exactly how TripleRunRate works to begin with. (TestPlayerDash turned out to be a better option, as that only affected sprint speed and persisted)

One thing I noticed is that TripleCheatManager commands stop working upon exiting 2D mode to the main menu (disconnect) and loading back into 2D mode via file select. I'd probably consider this an edge case, but perhaps worth noting. (Haven't tried this via a normal save & quit however, so it's possibly just a side effect of using the disconnect command to return to the main menu).

(Edit: TripleReturnTitle does the same)

Glad I could help! Now I just wonder if the debug menu needs a similar thing, did try filling the AJackGameModeBase::DebugMenu field I mentioned before, but that was before we had UClass::ClassDefaultObject mapped out.. guess I'll have to play around with it some more now.

(E: fixed!) Only just realized that this StartupModule method probably won't let it get unlocked when injecting the DLL, since StartupModule gets ran pretty early on during game launch, maybe can fix that using a hook like the first one I posted though
(really I'm not even sure if the UWP stuff here even works, still haven't heard anything from anyone with the MS store ver, wouldn't be surprised if the MS store EXE I've been working with is older than the current one :/)

E: about TripleRunRate, seems it does set some variable but doesn't have any code to read it back, guessing that was ifdef'd out, 3D RunRate probably had a similar fate but I guess they forgot to ifdef some run-during-menu code or something.

There does seem to be some code that's for defaulting it though, var it copies default from does get read by a few other things, could just need those things to be changed to point to TripleRunRate var instead maybe, hm...

E2: aha yep, float at 145E86918 is definitely used as default walk speed, seems it's copied to some other var at certain points though (eg. when speech shows up), so changing it isn't immediate, guess I need to find where var it copies to is used...
Also gets reset at certain points too (entering camp sets to 4, leaving camp sets to 2...)

Oh it's actually simpler, 145EE66E0 is the speed, changing that updates it instantly, hm..
E3: 711c455 :)

Gooreat! Very excited that we could have a 2D mode dash working in short order! :D

(really I'm not even sure if the UWP stuff here even works, still haven't heard anything from anyone with the MS store ver, wouldn't be surprised if the MS store EXE I've been working with is older than the current one :/)

I'll try to contact the few folks we have in our modding Discord who have (or at least *had) the Gamepass version, see if they'd be willing to test the latest build.

TripleRunRate works excellently! So i'm looking at trying to add a bind for TripleRunRate using EInputEvent::IE_Pressed and EInputEvent::IE_Released, but I'm not sure how the hooked TripleCheatManager instance should be accessed.

I've tried g_TripleCheatManager = UObject::FindObject<UTripleCheatManager>(); similar to what worked for g_JackCheatManager (probably not the most elegant solution, to be fair), but ofc FindObject() returns nullptr

Thought I'd be able to manage at least this much buuut xD

I'll try to look into it some more later (i'm sure there's got to be a better way to access it anyhow).

Thanks so much for this! I know some folks are going to be ecstatic to see these features that have been eluding us for so long.

g_TripleCheatManager = (UTripleCheatManager*)UTripleCheatManager::StaticClass()->ClassDefaultObject may work hopefully, might need to check it for nullptr and call UTripleCheatManager::StaticClass()->CreateDefaultObject() if so (like in FTripleModule__GetCheatManager_Hook, I never saw it returning nullptr there though, but UE4 does this nullptr/CreateDefaultObject stuff in GetDefault<UObject>() so I figured it's worth using just in case)

::StaticClass() does an expensive FindObject call though, so you might want to cache the returned StaticClass() (shouldn't ever change) during CacheUFunctions(), and then access manager thru ClassDefaultObject (can change if module is unloaded I think, so probably shouldn't cache it), something like

auto cheatManager = (UTripleCheatManager*)g_TripleCheatManagerClass->ClassDefaultObject;
if (!cheatManager)
  cheatManager = g_TripleCheatManagerClass->CreateDefaultObject<UTripleCheatManager>();
cheatManager->TripleRunRate(50);

E: added a GetDefault function that can handle the ClassDefaultObject/CreateDefaultObject stuff, should be able to use this instead:

auto cheatManager = UTripleCheatManager::StaticClass()->GetDefault<UTripleCheatManager>();
cheatManager->TripleRunRate(50);

Also I think StaticClass might get cached automatically like the UFunctions, so maybe just call UTripleCheatManager::StaticClass() one time inside CacheUFunctions() so it caches itself first (along with any of the CheatManager funcs you're calling)