diff --git a/sfse/GameChargen.cpp b/sfse/GameChargen.cpp new file mode 100644 index 0000000..95a3115 --- /dev/null +++ b/sfse/GameChargen.cpp @@ -0,0 +1 @@ +#include "sfse/GameChargen.h" \ No newline at end of file diff --git a/sfse/GameChargen.h b/sfse/GameChargen.h new file mode 100644 index 0000000..0441f41 --- /dev/null +++ b/sfse/GameChargen.h @@ -0,0 +1,25 @@ +#pragma once + +#include "sfse/GameUI.h" +#include "sfse/GameSingleton.h" +#include "sfse_common/Relocation.h" +#include "sfse_common/Utilities.h" + +namespace TESNPCData +{ +class ChargenDataModel : public IDataModel, public BSTSingletonSDM +{ +public: + virtual ~ChargenDataModel(); + + static ChargenDataModel* GetSingleton() + { + RelocPtr singleton(0x58F7EF8); + return *singleton; + } + + // Contains main UI data model wrappers, decode these later + // This function will pull data from the TESNPC into this wrapper + DEFINE_MEMBER_FN_2(Update, void, 0x01890E98, TESNPC*, void* unk2); // Unk2 looks like somekind of restore point, is usually CharGenMenu+0x2D0 +}; +} \ No newline at end of file diff --git a/sfse/GameConsole.cpp b/sfse/GameConsole.cpp index 3d9590f..3f85778 100644 --- a/sfse/GameConsole.cpp +++ b/sfse/GameConsole.cpp @@ -11,7 +11,7 @@ void Console_Print(const char* fmt, ...) va_list args; va_start(args, fmt); - CALL_MEMBER_FN(mgr, VPrint)(fmt, args); + mgr->VPrint(fmt, args); va_end(args); } diff --git a/sfse/GameConsole.h b/sfse/GameConsole.h index a3c1610..f90391e 100644 --- a/sfse/GameConsole.h +++ b/sfse/GameConsole.h @@ -6,8 +6,7 @@ class ConsoleLog { public: - MEMBER_FN_PREFIX(ConsoleLog); - DEFINE_MEMBER_FN(VPrint, void, 0x02883978, const char* fmt, va_list args); + DEFINE_MEMBER_FN_2(VPrint, void, 0x02883978, const char* fmt, va_list args); }; extern RelocPtr g_console; diff --git a/sfse/GameObjects.h b/sfse/GameObjects.h index 3ae037b..35c9132 100644 --- a/sfse/GameObjects.h +++ b/sfse/GameObjects.h @@ -184,5 +184,7 @@ class TESNPC : u64 unk478; // 478 u8 unk480; // 480 u8 pad481[7]; // 481 + + DEFINE_MEMBER_FN_1(DeriveGeneticParentAppearance, void, 0x1B284E8, TESNPC* source); }; static_assert(sizeof(TESNPC) == 0x488); \ No newline at end of file diff --git a/sfse/GameReferences.h b/sfse/GameReferences.h index 54179b4..8495e52 100644 --- a/sfse/GameReferences.h +++ b/sfse/GameReferences.h @@ -253,10 +253,9 @@ class TESObjectREFR : public TESForm u8 unk10A; // 10A u8 unk10B; // 10B - MEMBER_FN_PREFIX(TESObjectREFR); - DEFINE_MEMBER_FN(IsInSpaceship, bool, 0x02B3A714) - DEFINE_MEMBER_FN(IsInSpace, bool, 0x01A0E1C8) - DEFINE_MEMBER_FN(HasKeyword, bool, 0x0139EDB8, BGSKeyword*); + DEFINE_MEMBER_FN_0(IsInSpaceship, bool, 0x02B3A714) + DEFINE_MEMBER_FN_0(IsInSpace, bool, 0x01A0E1C8) + DEFINE_MEMBER_FN_1(HasKeyword, bool, 0x0139EDB8, BGSKeyword*); }; static_assert(sizeof(TESObjectREFR) == 0x110); @@ -382,6 +381,17 @@ class Actor : public TESObjectREFR virtual void Unk_1A1(); // 1A1 // More... + DEFINE_MEMBER_FN_0(UpdateChargenAppearance, void, 0x02B3A714); // Only seems to work in CharGenMenu? + + // This function is very slow, do not use for realtime updates, mainly used for "entire character changed" + // The fields represent which subsets of chargen to update, usually you want (false, 0x28, false) + DEFINE_MEMBER_FN_3(UpdateAppearance, void, 0x24A93EC, bool unk1, u32 flags, bool unk3); +}; + +class MenuActor : public Actor +{ +public: + virtual ~MenuActor(); }; //inline RelocPtr g_playerCharacter(0x05595BA8); diff --git a/sfse/GameSettings.cpp b/sfse/GameSettings.cpp index 9589b59..a9f6d26 100644 --- a/sfse/GameSettings.cpp +++ b/sfse/GameSettings.cpp @@ -3,6 +3,7 @@ decltype(SettingT::pCollection) SettingT::pCollection(0x05913B98); decltype(SettingT::pCollection) SettingT::pCollection(0x05913BB8); decltype(SettingT::pCollection) SettingT::pCollection(0x059127A8); +decltype(SettingT::pCollection) SettingT::pCollection(0x058F50B8); u32 Setting::GetType(void) const { diff --git a/sfse/GameSettings.h b/sfse/GameSettings.h index 801a9ff..4ab596d 100644 --- a/sfse/GameSettings.h +++ b/sfse/GameSettings.h @@ -2,6 +2,7 @@ #include "sfse_common/Types.h" #include "sfse_common/Relocation.h" +#include "sfse_common/Utilities.h" #include "sfse/GameTypes.h" class Setting @@ -60,16 +61,33 @@ class SettingCollection char pSettingFile[260]; // 008 u32 pad10C; // 10C void* pHandle; // 110 +}; + +template +class SettingCollectionList : public SettingCollection +{ +public: + virtual ~SettingCollectionList(); + BSSimpleList SettingsA; // 118 u64 unk128; // 128 u64 unk130; // 130 }; template -class SettingCollectionList : public SettingCollection +class SettingCollectionMap : public SettingCollection { public: - virtual ~SettingCollectionList(); + virtual ~SettingCollectionMap(); + + u64 unk118; // BSTBTree ? Doesnt match F4 exactly + u64 unk120; + u64 unk128; + u64 unk130; + u64 unk138; + u64 unk140; + u64 unk148; + u64 unk150; }; class INISettingCollection : public SettingCollectionList @@ -90,6 +108,15 @@ class RegSettingCollection : public SettingCollectionList virtual ~RegSettingCollection(); }; +class GameSettingCollection : public SettingCollectionMap +{ +public: + virtual ~GameSettingCollection(); + + DEFINE_MEMBER_FN_1(GetSetting, Setting*, 0x01586734, const char*); +}; +static_assert(sizeof(GameSettingCollection) == 0x158); + template class SettingT { diff --git a/sfse/GameSingleton.h b/sfse/GameSingleton.h new file mode 100644 index 0000000..ad899c3 --- /dev/null +++ b/sfse/GameSingleton.h @@ -0,0 +1,45 @@ +#pragma once + +template +class BSTSingletonImplicit +{ +public: + using value_type = T; +}; + +template +class BSTSingletonExplicit +{ +public: + using value_type = T; +}; + +template +struct BSTSingletonSDMOpStaticBuffer +{ +public: + using value_type = T; +}; + +template +struct BSTSingletonSDMBase : + public Traits, + public BSTSingletonSDMOpStaticBuffer +{ +public: +}; + +template +struct BSTSDMTraits +{ +public: + using value_type = T; +}; + +template class Buffer = BSTSingletonSDMOpStaticBuffer> +struct BSTSingletonSDM : + public BSTSingletonSDMBase>> +{ +public: + using value_type = T; +}; diff --git a/sfse/GameUI.cpp b/sfse/GameUI.cpp new file mode 100644 index 0000000..1752735 --- /dev/null +++ b/sfse/GameUI.cpp @@ -0,0 +1 @@ +#include "sfse/GameUI.h" \ No newline at end of file diff --git a/sfse/GameUI.h b/sfse/GameUI.h new file mode 100644 index 0000000..9fdd15c --- /dev/null +++ b/sfse/GameUI.h @@ -0,0 +1,11 @@ +#pragma once + +class IDataModel +{ +public: + virtual ~IDataModel(); + + virtual void Unk_01(); + virtual void Unk_02(); + virtual void Unk_03(); +}; \ No newline at end of file diff --git a/sfse/Hooks_Scaleform.cpp b/sfse/Hooks_Scaleform.cpp new file mode 100644 index 0000000..ebd5590 --- /dev/null +++ b/sfse/Hooks_Scaleform.cpp @@ -0,0 +1,29 @@ +#include "Hooks_Scaleform.h" +#include "sfse_common/Relocation.h" +#include "sfse_common/Types.h" +#include "sfse_common/Log.h" +#include "sfse_common/SafeWrite.h" + +#include +#ifdef _DEBUG +#include +#endif + +// Scaleform::Log vtable +RelocAddr kHook_Scaleform_Log_Offset(0x04077C48 + 0x08); + +void Scaleform_LogMessageVarg(void*/*Scaleform::GFx::Log::State* */ logger, u32 messageType, const char* fmt, va_list args) +{ + DebugLog::log(DebugLog::kLevel_Message, fmt, args); +#ifdef _DEBUG + char szBuff[1024]; + vsnprintf_s(szBuff, sizeof(szBuff), fmt, args); + strcat_s(szBuff, "\n"); + OutputDebugString(szBuff); +#endif +} + +void Hooks_Scaleform_Apply() +{ + safeWrite64(kHook_Scaleform_Log_Offset, (uintptr_t)Scaleform_LogMessageVarg); +} \ No newline at end of file diff --git a/sfse/Hooks_Scaleform.h b/sfse/Hooks_Scaleform.h new file mode 100644 index 0000000..95d95ec --- /dev/null +++ b/sfse/Hooks_Scaleform.h @@ -0,0 +1,3 @@ +#pragma once + +void Hooks_Scaleform_Apply(); \ No newline at end of file diff --git a/sfse/Hooks_Script.cpp b/sfse/Hooks_Script.cpp index b5c3836..fbd1637 100644 --- a/sfse/Hooks_Script.cpp +++ b/sfse/Hooks_Script.cpp @@ -9,6 +9,39 @@ #include "sfse_common/Log.h" #include "xbyak/xbyak.h" +#ifdef _DEBUG +#include "sfse/GameObjects.h" +#include "sfse/GameChargen.h" +#include "sfse/GameSettings.h" + +bool Test_Execute(const SCRIPT_PARAMETER* paramInfo, const char*, TESObjectREFR* thisObj, TESObjectREFR* containingObj, Script* script, ScriptLocals* locals, float* result, u32* opcodeOffsetPtr) +{ + if (thisObj) { + dumpClass(thisObj, 0x1100 >> 3/*0x110 >> 3*/); + TESNPC* npc = (TESNPC*)thisObj->data.objectReference; + _MESSAGE("Name: %s", npc->strFullName.c_str()); + dumpClass(npc, 0x488 >> 3); + dumpClass(TESNPCData::ChargenDataModel::GetSingleton(), 0x700 >> 3); + + npc->MorphWeight.x = 1.0f; + npc->MorphWeight.y = 1.0f; + npc->MorphWeight.z = 1.0f; + bool unk1 = false; + u32 unk2 = 0x28; + bool unk3 = false; + static_cast(thisObj)->UpdateAppearance(unk1, unk2, unk3); + } + else + { + auto& gameSettings = (*SettingT::pCollection); + auto setting = gameSettings->GetSetting("sSkinToneDisplayName"); + Console_Print("Game Settings: %s", setting->name); + } + Console_Print("Dump Complete"); + return true; +} +#endif + bool GetSFSEVersion_Execute(const SCRIPT_PARAMETER* paramInfo, const char*, TESObjectREFR* thisObj, TESObjectREFR* containingObj, Script* script, ScriptLocals* locals, float* result, u32* opcodeOffsetPtr) { _MESSAGE("GetSFSEVersion_Execute"); @@ -43,6 +76,21 @@ void ConsoleCommandInit_Hook(void* unk1) cmd.bInvalidatesCellList = 0; break; } +#ifdef _DEBUG + else if (!strcmp(iter->pFunctionName, "GameComment")) + { + Script::SCRIPT_FUNCTION& cmd = *iter; + cmd.pFunctionName = "test"; + cmd.pShortName = ""; + cmd.pHelpString = ""; + cmd.bReferenceFunction = 0; + cmd.sParamCount = 0; + cmd.pExecuteFunction = Test_Execute; + cmd.bEditorFilter = 0; + cmd.bInvalidatesCellList = 0; + break; + } +#endif } } diff --git a/sfse/sfse.cpp b/sfse/sfse.cpp index b8f30fd..b70ba4f 100644 --- a/sfse/sfse.cpp +++ b/sfse/sfse.cpp @@ -10,6 +10,7 @@ #include "Hooks_Version.h" #include "Hooks_Script.h" +#include "Hooks_Scaleform.h" HINSTANCE g_moduleHandle = nullptr; @@ -141,6 +142,7 @@ void SFSE_Initialize() Hooks_Version_Apply(); Hooks_Script_Apply(); + Hooks_Scaleform_Apply(); FlushInstructionCache(GetCurrentProcess(), NULL, 0);