Skip to content

Commit

Permalink
Debugger: Add hook to force enable debug info load
Browse files Browse the repository at this point in the history
  • Loading branch information
nikitalita committed Sep 1, 2023
1 parent dd0c240 commit c995630
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 20 deletions.
7 changes: 3 additions & 4 deletions src/DarkId.Papyrus.DebugServer/BreakpointManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,11 @@ namespace DarkId::Papyrus::DebugServer
if (!hasDebugInfo) {

#if FALLOUT
const std::string gameName = "Fallout4";
const std::string iniName = "fallout4.ini";
#else
const std::string gameName = "Skyrim"
const std::string iniName = "skyrim.ini";
#endif

return dap::Error("No debug data for script %s. Ensure that `bLoadDebugInformation=1` is set under `[Papyrus]` in %s.ini", scriptName, gameName);
return dap::Error("No debug data for script %s. Ensure that `bLoadDebugInformation=1` is set under `[Papyrus]` in %s", scriptName, iniName);
}

for (const auto& srcBreakpoint : srcBreakpoints)
Expand Down
69 changes: 69 additions & 0 deletions src/DarkId.Papyrus.DebugServer/ConfigHooks.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#pragma once
#include <xbyak/xbyak.h>
#include "GameInterfaces.h"
#include <REL/Relocation.h>

namespace DarkId::Papyrus::DebugServer
{
namespace Game_Offset {
#ifdef SKYRIM
// Where the bEnableLogging skyrim.ini setting is stored; this is overwritten when the ini is loaded
constexpr auto pPapyrusEnableLogging = RELOCATION_ID(510627, 383715); // 1.5.97: 141DF5AE8, 1.6.640: 141E88DC8
constexpr auto pPapyrusEnableTrace = RELOCATION_ID(510667, 383766); // 1.5.97: 141DF5C60, 1.6.640: 141E88F88

#else // FALLOUT
// Where the bEnableLogging fallout4.ini setting is stored; this is overwritten when the ini is loaded
constexpr auto pPapyrusEnableLogging = REL::ID(1272228); // 14380E140
// Where the bEnableTrace fallout4.ini setting is stored; overwritten on ini load
constexpr auto pPapyrusEnableTrace = REL::ID(218028); // 143818DC0
#endif
}
// Modified hook from PapyrusTweaks by NightFallStorm with permission
struct EnableLoadDebugInformation
{
#ifdef SKYRIM
using ScriptLogger = RE::SkyrimScript::Logger;
#else
using ScriptLogger = RE::GameScript::Logger;
#endif
// Hook the <Skyrim/Game>VM's constructor that constructs CompiledScriptLoader, to force enable debug information loading
// This thunk hook plays well with the doc string hook
static RE::BSScript::CompiledScriptLoader* thunk(RE::BSScript::CompiledScriptLoader* a_unmadeSelf, ScriptLogger* a_unmadeLogger, bool a_loadDebugInformation, bool a_loadDocStrings)
{
return func(a_unmadeSelf, a_unmadeLogger, true, a_loadDocStrings);
}

static inline REL::Relocation<decltype(thunk)> func;

// Hook target is SkyrimVM/GameVM's call to the CompiledScriptLoader constructor
static inline REL::Relocation<std::uintptr_t> getHookTarget() {
#ifdef SKYRIM
// Skyrim SE:
// SkyrimVM::SkyrimVM():
// - 1.5.97: offset at 0x0009204A0 (0x1409204A0)
// - 1.6.640: offset at 0x00095F6E0 (0x14095F6E0)
// Hooks at SkyrimVM() offset:
// - 1.5.97: 0x604 (0x140920AA4)
// - 1.6.640: 0x664 (0x14095FD44)
// The hook target is the same address as asm instruction `call <label_of_CompiledScriptLoader()>`
return REL::Relocation<std::uintptr_t>(RELOCATION_ID(53108, 53919), REL::VariantOffset(0x604, 0x664, 0x604));
#else
// Fallout 4:
// GameVM::GameVM(): offset at 0x001370D60 (0x141370D60)
// Hooks at GameVM() offset 0x1C4 (0x141370F24)
// The hook target is the same address as asm instruction `call <label_of_CompiledScriptLoader()>`
return REL::Relocation<std::uintptr_t>(REL::ID(35833), REL::Offset(0x1C4).offset());
#endif

}
// Install our hook at the specified address
static inline void Install()
{
REL::Relocation<std::uintptr_t> target = getHookTarget();
stl::write_thunk_call<EnableLoadDebugInformation>(target.address());
logger::info("EnableLoadDebugInformation hooked at address {:x}", target.address());
logger::info("EnableLoadDebugInformation hooked at offset {:x}", target.offset());
}

};
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,15 @@
<PostBuildEventUseInBuild>false</PostBuildEventUseInBuild>
<PreBuildEventUseInBuild>false</PreBuildEventUseInBuild>
<PreLinkEventUseInBuild>false</PreLinkEventUseInBuild>
<OutDir>$(ProjectDir)bin\$(ProjectName)\$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(ProjectDir)obj\$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
<OutDir>$(ProjectDir)bin\$(ProjectName)\$(Platform)\$(Configuration)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<PostBuildEventUseInBuild>false</PostBuildEventUseInBuild>
<PreBuildEventUseInBuild>false</PreBuildEventUseInBuild>
<PreLinkEventUseInBuild>false</PreLinkEventUseInBuild>
<OutDir>F:\Games\fallout_4_mods_folder\mods\Papyrus Debug Extension\F4SE\Plugins\</OutDir>
<OutDir>$(ProjectDir)bin\$(ProjectName)\$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(ProjectDir)obj\$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup>
Expand Down Expand Up @@ -233,6 +233,7 @@
<ClInclude Include="DebugServer.h" />
<ClInclude Include="FormMetadata.h" />
<ClInclude Include="GameInterfaces.h" />
<ClInclude Include="ConfigHooks.h" />
<ClInclude Include="IdHandleBase.h" />
<ClInclude Include="IdMap.h" />
<ClInclude Include="IdProvider.h" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,5 +128,6 @@
<ClInclude Include="Protocol\websocket_server.h">
<Filter>Protocol</Filter>
</ClInclude>
<ClInclude Include="ConfigHooks.h" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@
<ItemGroup>
<ClInclude Include="ArrayStateNode.h" />
<ClInclude Include="BreakpointManager.h" />
<ClInclude Include="ConfigHooks.h" />
<ClInclude Include="DebugExecutionManager.h" />
<ClInclude Include="DebugServer.h" />
<ClInclude Include="FormMetadata.h" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@
<ClInclude Include="StructStateNode.h" />
<ClInclude Include="Utilities.h" />
<ClInclude Include="version.h" />
<ClInclude Include="ConfigHooks.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="version.rc" />
Expand Down
12 changes: 7 additions & 5 deletions src/DarkId.Papyrus.DebugServer/Utilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,13 @@ namespace DarkId::Papyrus::DebugServer
}

inline int GetSourceReference(const dap::Source& src) {
return src.sourceReference.value(
GetScriptReference(
src.name.value(
std::filesystem::path(src.path.value("")).filename().string() // default if name isn't set
)));
return src.sourceReference.value( // try Source's set sourceReference first
GetScriptReference( // if not present, run the hasher on the source's filename
src.name.value(
std::filesystem::path(
src.path.value("")
).filename().string() // empty string if src.path isn't set
))); // will end up returning 0 if path isn't set, which is what we want
}

inline std::string GetSourceModfiedTime(const dap::Source & src) {
Expand Down
20 changes: 15 additions & 5 deletions src/DarkId.Papyrus.DebugServer/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,25 @@ namespace XSE = F4SE;
#include "version.h" // VERSION_VERSTRING, VERSION_MAJOR

#include "DebugServer.h"
#include "ConfigHooks.h"
#include "RuntimeEvents.h"
using namespace DarkId::Papyrus::DebugServer;

DebugServer* g_debugServer;
using namespace std::literals;

#ifdef SKYRIM
constexpr auto gameLoaded = SKSE::MessagingInterface::kDataLoaded;
#else
constexpr auto gameLoaded = F4SE::MessagingInterface::kGameLoaded;
#endif


void MessageHandler(XSE::MessagingInterface::Message* msg)
{
switch (msg->type)
{
#if SKYRIM
case SKSE::MessagingInterface::kDataLoaded :
#elif FALLOUT
case F4SE::MessagingInterface::kGameLoaded :
#endif
case gameLoaded:
{
RuntimeEvents::Internal::CommitHooks();

Expand Down Expand Up @@ -147,6 +151,11 @@ extern "C"
logger::info("Initializing plugin...");
Init(a_xse);
logger::info("Plugin Initialized!");

// We install this hook before any scripts are loaded to ensure that any script loads are done with LoadDebugInformation enabled.
logger::info("Installing EnableLoadDebugInformation hook...");
EnableLoadDebugInformation::Install();
logger::info("Installing EnableLoadDebugInformation hook installed!");
#if SKYRIM
logger::info("Registering Listener...");
if (XSE::GetMessagingInterface()->RegisterListener("SKSE", MessageHandler)){
Expand All @@ -158,6 +167,7 @@ extern "C"
auto messaging = XSE::GetMessagingInterface();
messaging->RegisterListener(MessageHandler);
#endif
g_debugServer->Listen();
return true;
}
};
23 changes: 19 additions & 4 deletions src/DarkId.Papyrus.DebugServer/pdsPCH.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once
#if SKYRIM

#define SPDLOG_LEVEL_NAMES { "TRACE", "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL", "OFF" }
#if SKYRIM
#include <SKSE/Impl/PCH.h>
#undef GetObject // Have to do this because PCH pulls in spdlog->winbase.h->windows.h->wingdi.h, which redfines GetObject
#undef GetObjectA
Expand All @@ -16,7 +17,7 @@ namespace DarkId::Papyrus::DebugServer
namespace XSE = SKSE;
namespace logger = SKSE::log;
}
#define DLLEXPORT __declspec(dllexport)
namespace XSE = SKSE;

#elif FALLOUT

Expand All @@ -35,6 +36,20 @@ namespace DarkId::Papyrus::DebugServer
namespace XSE = F4SE;
namespace logger = F4SE::log;
}
#define DLLEXPORT __declspec(dllexport)
namespace XSE = F4SE;
#endif

#endif
namespace stl {
using namespace XSE::stl;

template <class T>
void write_thunk_call(std::uintptr_t a_src)
{
auto& trampoline = XSE::GetTrampoline();
XSE::AllocTrampoline(14);

T::func = trampoline.write_call<5>(a_src, T::thunk);
}
}

#define DLLEXPORT __declspec(dllexport)

0 comments on commit c995630

Please sign in to comment.