diff --git a/.gitignore b/.gitignore index c2375b3c..43e45ed5 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,8 @@ .vscode __pycache__ Debug +!Spore ModAPI/Spore/Debug/ +!Spore ModAPI/SourceCode/Debug/ Release Projects/* !Projects/Example Projects/ diff --git a/Spore ModAPI/SourceCode/DLL/AddressesDebug.cpp b/Spore ModAPI/SourceCode/DLL/AddressesDebug.cpp new file mode 100644 index 00000000..3357ba41 --- /dev/null +++ b/Spore ModAPI/SourceCode/DLL/AddressesDebug.cpp @@ -0,0 +1,25 @@ +#ifdef MODAPI_DLL_EXPORT +#include "stdafx.h" +#include + +namespace Debug +{ + namespace Addresses(ReportWriter) + { + DefineAddress(GetFileNameExtension, SelectAddress(0x0091eee0, 0x0091ec20)); + DefineAddress(OpenReport, SelectAddress(0x0091ef10, 0x0091ec50)); + DefineAddress(CloseReport, SelectAddress(0x0091ef70, 0x0091ecb0)); + DefineAddress(BeginSection, SelectAddress(0x0091efa0, 0x0091ece0)); + DefineAddress(EndSection, SelectAddress(0x0091efd0, 0x0091ed10)); + DefineAddress(WriteText, SelectAddress(0x0091efe0, 0x0091ed20)); + DefineAddress(WriteFormatted, SelectAddress(0x0091f020, 0x0091ed60)); + DefineAddress(WriteKeyValue, SelectAddress(0x0091f070, 0x0091edb0)); + DefineAddress(WriteKeyValueFormatted, SelectAddress(0x0091f0f0, 0x0091ee30)); + DefineAddress(WriteBlankLine, SelectAddress(0x0091f140, 0x0091ee80)); + DefineAddress(Write, SelectAddress(0x0091f150, 0x0091ee90)); + DefineAddress(GetPosition, SelectAddress(0x0091f1b0, 0x0091eef0)); + DefineAddress(SetPosition, SelectAddress(0x0091f1d0, 0x0091ef10)); + }; +} + +#endif diff --git a/Spore ModAPI/SourceCode/DLL/DebugReportWriterOverride.cpp b/Spore ModAPI/SourceCode/DLL/DebugReportWriterOverride.cpp new file mode 100644 index 00000000..67fb7069 --- /dev/null +++ b/Spore ModAPI/SourceCode/DLL/DebugReportWriterOverride.cpp @@ -0,0 +1,184 @@ +#ifdef MODAPI_DLL_EXPORT +#include "stdafx.h" +#include "DebugReportWriterOverride.h" + +#include +#include + +typedef LONG(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); + +member_detour(WriteSystemInfoDetour, Debug::ReportWriter, bool(void)) +{ + bool detoured(void) + { + char buffer[256] = { 0 }; + DWORD bufferSize = sizeof(buffer); + + if (GetComputerNameExA(COMPUTER_NAME_FORMAT::ComputerNameDnsFullyQualified, buffer, &bufferSize)) + { + WriteKeyValue("Computer name", buffer); + } + + bufferSize = sizeof(buffer); + if (GetUserNameA(buffer, &bufferSize)) + { + WriteKeyValue("User name", buffer); + } + + WriteKeyValue("EA_PLATFORM", "Windows on X86"); + + // GetVersionExA() is deprecated and only reports upto Windows 8 + // unless a higher Windows version is specified in the executable + // manifest, so use RtlGetVersion() from ntdll.dll instead + HMODULE ntdllHandle = GetModuleHandleW(L"ntdll.dll"); + if (ntdllHandle != nullptr) + { + RtlGetVersionPtr rtlGetVersion = (RtlGetVersionPtr)GetProcAddress(ntdllHandle, "RtlGetVersion"); + if (rtlGetVersion != nullptr) + { + RTL_OSVERSIONINFOW versionInfo = { 0 }; + versionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOW); + + if (rtlGetVersion(&versionInfo) == 0) + { + if (versionInfo.dwMajorVersion == 5 && + versionInfo.dwMinorVersion == 1) + { + sprintf_s(buffer, sizeof(buffer), "Windows XP"); + } + else if (versionInfo.dwMajorVersion == 6 && + versionInfo.dwMinorVersion == 0) + { + sprintf_s(buffer, sizeof(buffer), "Windows Vista"); + } + else if (versionInfo.dwMajorVersion == 6 && + versionInfo.dwMinorVersion == 1) + { + sprintf_s(buffer, sizeof(buffer), "Windows 7"); + } + else if (versionInfo.dwMajorVersion == 6 && + versionInfo.dwMinorVersion == 2) + { + sprintf_s(buffer, sizeof(buffer), "Windows 8"); + } + else if (versionInfo.dwMajorVersion == 6 && + versionInfo.dwMinorVersion == 3) + { + sprintf_s(buffer, sizeof(buffer), "Windows 8.1"); + } + else if (versionInfo.dwMajorVersion == 10 && + versionInfo.dwMinorVersion == 0 && + versionInfo.dwBuildNumber < 22000) + { + sprintf_s(buffer, sizeof(buffer), "Windows 10"); + } + else if (versionInfo.dwMajorVersion >= 10 && + versionInfo.dwMinorVersion == 0 && + versionInfo.dwBuildNumber >= 22000) + { + sprintf_s(buffer, sizeof(buffer), "Windows 11"); + } + else + { + sprintf_s(buffer, sizeof(buffer), "Unknown"); + } + WriteKeyValue("OS name", buffer); + + sprintf_s(buffer, sizeof(buffer), "%u.%u.%u", versionInfo.dwMajorVersion, versionInfo.dwMinorVersion, versionInfo.dwBuildNumber); + WriteKeyValue("OS version number", buffer); + + if (versionInfo.szCSDVersion[0] != L'\0') + { + sprintf_s(buffer, sizeof(buffer), "%ls", versionInfo.szCSDVersion); + WriteKeyValue("OS service pack", buffer); + } + + } + } + } + + WriteKeyValue("Debugger present", (IsDebuggerPresent() ? "yes" : "no")); + + // GetSystemInfo() does not return correct information + // when we're running on a x86_64 CPU while in a x86 process, + // so use GetNativeSystemInfo() instead. + SYSTEM_INFO systemInfo = { 0 }; + GetNativeSystemInfo(&systemInfo); + + sprintf_s(buffer, sizeof(buffer), "%u", systemInfo.dwNumberOfProcessors); + WriteKeyValue("CPU count", buffer); + + if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) + { + sprintf_s(buffer, sizeof(buffer), "x86-64"); + } + else if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA32_ON_WIN64) + { + sprintf_s(buffer, sizeof(buffer), "x86 on x86-64"); + } + else + { + sprintf_s(buffer, sizeof(buffer), "x86"); + } + WriteKeyValue("Processor type", buffer); + + sprintf_s(buffer, sizeof(buffer), "%u", systemInfo.wProcessorLevel); + WriteKeyValue("Processor level", buffer); + + sprintf_s(buffer, sizeof(buffer), "%u", systemInfo.wProcessorRevision); + WriteKeyValue("Processor revision", buffer); + + + MEMORYSTATUSEX memoryStatus = { 0 }; + memoryStatus.dwLength = sizeof(MEMORYSTATUSEX); + if (GlobalMemoryStatusEx(&memoryStatus)) + { + sprintf_s(buffer, sizeof(buffer), "%d%%", memoryStatus.dwMemoryLoad); + WriteKeyValue("Memory load", buffer); + + sprintf_s(buffer, sizeof(buffer), "%I64d Mb", memoryStatus.ullTotalPhys / 1024 / 1024); + WriteKeyValue("Total physical memory", buffer); + + sprintf_s(buffer, sizeof(buffer), "%I64d Mb", memoryStatus.ullAvailPhys / 1024 / 1024); + WriteKeyValue("Available physical memory", buffer); + + sprintf_s(buffer, sizeof(buffer), "%I64d Mb", memoryStatus.ullTotalPageFile / 1024 / 1024); + WriteKeyValue("Total page file memory", buffer); + + sprintf_s(buffer, sizeof(buffer), "%I64d Mb", memoryStatus.ullAvailPageFile / 1024 / 1024); + WriteKeyValue("Available page file memory", buffer); + + sprintf_s(buffer, sizeof(buffer), "%I64d Mb", memoryStatus.ullTotalVirtual / 1024 / 1024); + WriteKeyValue("Total virtual memory", buffer); + + sprintf_s(buffer, sizeof(buffer), "%I64d Mb", memoryStatus.ullAvailVirtual / 1024 / 1024); + WriteKeyValue("Free virtual memory", buffer); + } + + return true; + } +}; + +member_detour(WriteApplicationInfoDetour, Debug::ReportWriter, bool(void)) +{ + bool detoured(void) + { + bool ret = original_function(this); + + char buffer[256] = { 0 }; + sprintf_s(buffer, sizeof(buffer), "%i.%i.%i", ModAPI::GetMajorVersion(), ModAPI::GetMinorVersion(), ModAPI::GetBuildVersion()); + WriteKeyValue("ModAPI Version", buffer); + + return ret; + } +}; + + +long DebugReportWriterOverride::AttachDetour() +{ + long result = 0; + result |= WriteSystemInfoDetour::attach(Address(ModAPI::ChooseAddress(0x0091f480, 0x0091f1c0))); + result |= WriteApplicationInfoDetour::attach(Address(ModAPI::ChooseAddress(0x0091f850, 0x0091f590))); + return result; +} +#endif diff --git a/Spore ModAPI/SourceCode/DLL/DebugReportWriterOverride.h b/Spore ModAPI/SourceCode/DLL/DebugReportWriterOverride.h new file mode 100644 index 00000000..6dab42ce --- /dev/null +++ b/Spore ModAPI/SourceCode/DLL/DebugReportWriterOverride.h @@ -0,0 +1,6 @@ +#pragma once + +namespace DebugReportWriterOverride +{ + long AttachDetour(); +} \ No newline at end of file diff --git a/Spore ModAPI/SourceCode/DLL/dllmain.cpp b/Spore ModAPI/SourceCode/DLL/dllmain.cpp index f5201622..e9d258b6 100644 --- a/Spore ModAPI/SourceCode/DLL/dllmain.cpp +++ b/Spore ModAPI/SourceCode/DLL/dllmain.cpp @@ -7,6 +7,7 @@ #include "Application.h" #include "TextureOverride.h" #include "SpaceToolIconOverride.h" +#include "DebugReportWriterOverride.h" uintptr_t baseAddress; ArgScript::PrintF_ptr ArgScript::PrintF; @@ -27,6 +28,7 @@ BOOL APIENTRY DllMain(HMODULE hModule, ModAPI::AttachDetour(); TextureOverride::AttachDetour(); SpaceToolIconOverride::AttachDetour(); + DebugReportWriterOverride::AttachDetour(); CommitDetours(); break; diff --git a/Spore ModAPI/SourceCode/Debug/ReportWriter.cpp b/Spore ModAPI/SourceCode/Debug/ReportWriter.cpp new file mode 100644 index 00000000..0f1dc21f --- /dev/null +++ b/Spore ModAPI/SourceCode/Debug/ReportWriter.cpp @@ -0,0 +1,40 @@ +//#ifndef MODAPI_DLL_EXPORT +/**************************************************************************** +* Copyright (C) 2026 Rosalie Wanders +* +* This file is part of Spore ModAPI. +* +* Spore ModAPI is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +****************************************************************************/ + +#include + +namespace Debug +{ + auto_METHOD(ReportWriter, void, GetFileNameExtension, Args(char* buffer, size_t size), Args(buffer, size)); + auto_METHOD(ReportWriter, bool, OpenReport, Args(char* param_1), Args(param_1)); + auto_METHOD_(ReportWriter, bool, CloseReport); + auto_METHOD(ReportWriter, bool, BeginSection, Args(const char* name), Args(name)); + auto_METHOD_(ReportWriter, void, EndSection); + auto_METHOD(ReportWriter, bool, WriteText, Args(const char* text), Args(text)); + auto_METHOD(ReportWriter, bool, WriteFormatted, Args(const char* text), Args(text)); + auto_METHOD(ReportWriter, bool, WriteKeyValue, Args(const char* key, const char* value), Args(key, value)); + auto_METHOD(ReportWriter, bool, WriteKeyValueFormatted, Args(const char* key, const char* value), Args(key, value)); + auto_METHOD_(ReportWriter, void, WriteBlankLine); + auto_METHOD(ReportWriter, bool, Write, Args(const char* text, size_t size), Args(text, size)); + auto_METHOD_(ReportWriter, long, GetPosition); + auto_METHOD(ReportWriter, void, SetPosition, Args(long position), Args(position)); +}; + +//#endif \ No newline at end of file diff --git a/Spore ModAPI/Spore ModAPI.vcxproj b/Spore ModAPI/Spore ModAPI.vcxproj index b8dc8c9f..792492ce 100644 --- a/Spore ModAPI/Spore ModAPI.vcxproj +++ b/Spore ModAPI/Spore ModAPI.vcxproj @@ -294,6 +294,7 @@ + @@ -327,6 +328,7 @@ + @@ -958,6 +960,8 @@ + + @@ -996,6 +1000,7 @@ + diff --git a/Spore ModAPI/Spore ModAPI.vcxproj.filters b/Spore ModAPI/Spore ModAPI.vcxproj.filters index 5cf0eeb0..4cc3733a 100644 --- a/Spore ModAPI/Spore ModAPI.vcxproj.filters +++ b/Spore ModAPI/Spore ModAPI.vcxproj.filters @@ -2319,6 +2319,12 @@ Header Files + + Header Files + + + Header Files + @@ -2825,6 +2831,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + diff --git a/Spore ModAPI/Spore/Debug/ReportWriter.h b/Spore ModAPI/Spore/Debug/ReportWriter.h new file mode 100644 index 00000000..8e6b7bb8 --- /dev/null +++ b/Spore ModAPI/Spore/Debug/ReportWriter.h @@ -0,0 +1,44 @@ +#pragma once + +#include + +namespace Debug +{ + class ReportWriter + { + public: + /* 00h */ // TODO ~ReportWriter(); + /* 04h */ void GetFileNameExtension(char* buffer, size_t size); + /* 08h */ bool OpenReport(char* param_1); // TODO + /* 0Ch */ bool CloseReport(void); + /* 10h */ // TODO + /* 14h */ // TODO + /* 18h */ bool BeginSection(const char* name); + /* 1Ch */ void EndSection(void); + /* 20h */ bool WriteText(const char* text); + /* 24h */ bool WriteFormatted(const char* text); + /* 28h */ bool WriteKeyValue(const char* key, const char* value); + /* 2Ch */ bool WriteKeyValueFormatted(const char* key, const char* value); + /* 30h */ void WriteBlankLine(void); + /* 34h */ bool Write(const char* text, size_t size); + /* 38h */ long GetPosition(void); + /* 3Ch */ void SetPosition(long position); + }; + + namespace Addresses(ReportWriter) + { + DeclareAddress(GetFileNameExtension); + DeclareAddress(OpenReport); + DeclareAddress(CloseReport); + DeclareAddress(BeginSection); + DeclareAddress(EndSection); + DeclareAddress(WriteText); + DeclareAddress(WriteFormatted); + DeclareAddress(WriteKeyValue); + DeclareAddress(WriteKeyValueFormatted); + DeclareAddress(WriteBlankLine); + DeclareAddress(Write); + DeclareAddress(GetPosition); + DeclareAddress(SetPosition); + } +}