From 73454199d39b68827c759a21516d660743140f90 Mon Sep 17 00:00:00 2001 From: 0KepOnline <78259495+0KepOnline@users.noreply.github.com> Date: Mon, 6 Jul 2026 14:08:57 +0300 Subject: [PATCH 1/5] Add `StreamCompressionZLib.h` include to `Spore/IO.h` --- Spore ModAPI/Spore/IO.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Spore ModAPI/Spore/IO.h b/Spore ModAPI/Spore/IO.h index 471f0a8c..35339a0a 100644 --- a/Spore ModAPI/Spore/IO.h +++ b/Spore ModAPI/Spore/IO.h @@ -43,6 +43,7 @@ #include #include #include +#include #include /// @namespace IO From 755e929d1744a7fb571c9dfe8f223ac2bd0ed389 Mon Sep 17 00:00:00 2001 From: 0KepOnline <78259495+0KepOnline@users.noreply.github.com> Date: Tue, 7 Jul 2026 00:39:49 +0300 Subject: [PATCH 2/5] Rename `Stream(De)CompressionZLib::Dispose` to `_dtor` Also fix a "missing" include which broke `ASSERT_SIZE` for both while generating fully valid objects in runtime (triple-checked in x32dbg). --- Spore ModAPI/SourceCode/DLL/AddressesIO.cpp | 4 ++-- Spore ModAPI/SourceCode/IO/StreamDefinitions.cpp | 8 ++++---- Spore ModAPI/Spore/IO/StreamCompressionZLib.h | 11 ++++++----- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Spore ModAPI/SourceCode/DLL/AddressesIO.cpp b/Spore ModAPI/SourceCode/DLL/AddressesIO.cpp index 93eac6cc..652c64db 100644 --- a/Spore ModAPI/SourceCode/DLL/AddressesIO.cpp +++ b/Spore ModAPI/SourceCode/DLL/AddressesIO.cpp @@ -243,7 +243,7 @@ namespace IO DefineAddress(SetCompressionHint, SelectAddress(0x672830, 0x67D2A0)); DefineAddress(Open, SelectAddress(0x672AA0, 0x67D510)); // destructor; private for ModAPI - DefineAddress(Dispose, SelectAddress(0x67DC30, 0x67DAD0)); + DefineAddress(_dtor, SelectAddress(0x67DC30, 0x67DAD0)); } namespace Addresses(StreamDecompressionZLib) @@ -266,7 +266,7 @@ namespace IO DefineAddress(SetBufferSize, SelectAddress(0x672960, 0x67D3E0)); DefineAddress(Open, SelectAddress(0x672DC0, 0x67D850)); // destructor; private for ModAPI - DefineAddress(Dispose, SelectAddress(0x67DCE0, 0x67DB80)); + DefineAddress(_dtor, SelectAddress(0x67DCE0, 0x67DB80)); } } #endif diff --git a/Spore ModAPI/SourceCode/IO/StreamDefinitions.cpp b/Spore ModAPI/SourceCode/IO/StreamDefinitions.cpp index 620c59ca..96ee169c 100644 --- a/Spore ModAPI/SourceCode/IO/StreamDefinitions.cpp +++ b/Spore ModAPI/SourceCode/IO/StreamDefinitions.cpp @@ -418,7 +418,7 @@ namespace IO StreamCompressionZLib::~StreamCompressionZLib() { - Dispose(); + _dtor(); } auto_METHOD_(StreamCompressionZLib, int, AddRef); @@ -446,7 +446,7 @@ namespace IO auto_METHOD(StreamCompressionZLib, bool, Open, Args(IStream* pOutputStream, int nHint), Args(pOutputStream, nHint)); // destructor, private for ModAPI - auto_METHOD_VOID_(StreamCompressionZLib, Dispose); + auto_METHOD_VOID_(StreamCompressionZLib, _dtor); StreamDecompressionZLib::StreamDecompressionZLib(IStream* pInputStream) : mpInputStream(NULL), @@ -461,7 +461,7 @@ namespace IO StreamDecompressionZLib::~StreamDecompressionZLib() { - Dispose(); + _dtor(); } auto_METHOD_(StreamDecompressionZLib, int, AddRef); @@ -488,7 +488,7 @@ namespace IO auto_METHOD(StreamDecompressionZLib, bool, Open, Args(IStream* pInputStream), Args(pInputStream)); // destructor, private for ModAPI - auto_METHOD_VOID_(StreamDecompressionZLib, Dispose); + auto_METHOD_VOID_(StreamDecompressionZLib, _dtor); ///////////////////////////////// diff --git a/Spore ModAPI/Spore/IO/StreamCompressionZLib.h b/Spore ModAPI/Spore/IO/StreamCompressionZLib.h index 4a4d6b33..10aed91d 100644 --- a/Spore ModAPI/Spore/IO/StreamCompressionZLib.h +++ b/Spore ModAPI/Spore/IO/StreamCompressionZLib.h @@ -1,6 +1,7 @@ #pragma once -#include +#include "IStream.h" +#include #define StreamCompressionZLibPtr eastl::intrusive_ptr #define StreamDecompressionZLibPtr eastl::intrusive_ptr @@ -68,7 +69,7 @@ namespace IO /* 2Ch */ size_t mInputSize; // destructor, private for ModAPI private: - virtual void Dispose(); + virtual void _dtor(); }; ASSERT_SIZE(StreamCompressionZLib, 0x30); @@ -93,7 +94,7 @@ namespace IO DeclareAddress(SetCompressionHint); DeclareAddress(Open); // destructor, private for ModAPI - DeclareAddress(Dispose); + DeclareAddress(_dtor); } class StreamDecompressionZLib : public IStream, public RefCountTemplateAtomic @@ -140,7 +141,7 @@ namespace IO /* 20h */ size_t mnInputBufferSize; // destructor, private for ModAPI private: - virtual void Dispose(); + virtual void _dtor(); }; ASSERT_SIZE(StreamDecompressionZLib, 0x24); @@ -164,6 +165,6 @@ namespace IO DeclareAddress(SetBufferSize); DeclareAddress(Open); // destructor, private for ModAPI - DeclareAddress(Dispose); + DeclareAddress(_dtor); } } \ No newline at end of file From 540353ebf638c7e8034c902e93081b933177f0d7 Mon Sep 17 00:00:00 2001 From: 0KepOnline <78259495+0KepOnline@users.noreply.github.com> Date: Tue, 7 Jul 2026 20:40:46 +0300 Subject: [PATCH 3/5] Add `IO::IniFile` stub --- Spore ModAPI/SourceCode/DLL/AddressesIO.cpp | 31 +++++++ Spore ModAPI/SourceCode/IO/IniFile.cpp | 71 +++++++++++++++ Spore ModAPI/Spore ModAPI.vcxproj | 2 + Spore ModAPI/Spore ModAPI.vcxproj.filters | 6 ++ Spore ModAPI/Spore/IO/IniFile.h | 96 +++++++++++++++++++++ 5 files changed, 206 insertions(+) create mode 100644 Spore ModAPI/SourceCode/IO/IniFile.cpp create mode 100644 Spore ModAPI/Spore/IO/IniFile.h diff --git a/Spore ModAPI/SourceCode/DLL/AddressesIO.cpp b/Spore ModAPI/SourceCode/DLL/AddressesIO.cpp index 652c64db..0b506209 100644 --- a/Spore ModAPI/SourceCode/DLL/AddressesIO.cpp +++ b/Spore ModAPI/SourceCode/DLL/AddressesIO.cpp @@ -12,6 +12,7 @@ #include #include #include +#include namespace Addresses(IO) { @@ -268,5 +269,35 @@ namespace IO // destructor; private for ModAPI DefineAddress(_dtor, SelectAddress(0x67DCE0, 0x67DB80)); } + + namespace Addresses(IniFile) + { + DefineAddress(GetOption, SelectAddress(0x90c410, 0x932fc0)); + DefineAddress(SetOption, SelectAddress(0x90c430, 0x932fe0)); + DefineAddress(GetPath, SelectAddress(0x959f70, 0x6e6560)); + DefineAddress(SetPath, SelectAddress(0x90c460, 0x933010)); + DefineAddress(GetStream, SelectAddress(0x90c4d0, 0x933080)); + DefineAddress(SetStream, SelectAddress(0x90c4e0, 0x933090)); + DefineAddress(Close, SelectAddress(0x90e5a0, 0x934ff0)); + DefineAddress(ReadEntry, SelectAddress(0x90cc10, 0x933820)); + DefineAddress(ReadEntryToBuffer, SelectAddress(0x90d410, 0x933eb0)); + DefineAddress(ReadEntryFormatted, SelectAddress(0x90cca0, 0x9338b0)); + DefineAddress(WriteEntry, SelectAddress(0x90d9d0, 0x934420)); + DefineAddress(WriteEntryFormatted, SelectAddress(0x90c810, 0x9333c0)); + DefineAddress(ReadBinary, SelectAddress(0x90c8f0, 0x9334a0)); + DefineAddress(WriteBinary, SelectAddress(0x90ca60, 0x933610)); + DefineAddress(EnumSections, SelectAddress(0x90e220, 0x934c70)); + DefineAddress(EnumEntries, SelectAddress(0x90ea80, 0x9354d0)); + DefineAddress(SectionExists, SelectAddress(0x90d6d0, 0x934170)); + DefineAddress(Open, SelectAddress(0x90c540, 0x9330f0)); + DefineAddress(GetEncoding, SelectAddress(0x90c670, 0x933220)); + DefineAddress(LoadSectionNames, SelectAddress(0x90e610, 0x935060)); + DefineAddress(GetFileLine8To8, SelectAddress(0x90cfb0, 0x933b40)); + DefineAddress(GetFileLine16To16, SelectAddress(0x90d0c0, 0x933c50)); + DefineAddress(GetFileLine, SelectAddress(0x90cdc0, 0x933950)); + DefineAddress(ConvertAndWriteStream, SelectAddress(0x90cb20, 0x9336d0)); + // stub + DefineAddress(_vftable, SelectAddress(0x1407fc8, 0x143e980)); + } } #endif diff --git a/Spore ModAPI/SourceCode/IO/IniFile.cpp b/Spore ModAPI/SourceCode/IO/IniFile.cpp new file mode 100644 index 00000000..fa27ca1d --- /dev/null +++ b/Spore ModAPI/SourceCode/IO/IniFile.cpp @@ -0,0 +1,71 @@ +#ifndef MODAPI_DLL_EXPORT + +#include + +namespace IO +{ + // Used to replace methods with varargs with their original addresses, as ModAPI doesn't + // support those (and likely won't). + static void _stub(IniFile* pThis) + { + *(void**)pThis = (void*)GetAddress(IniFile, _vftable); + } + + IniFile::IniFile(const wchar_t* pPath, int optionFlags) + : mFileStream((char*)nullptr) + , mpStream(nullptr) + , mnEncodingSrc(8) + , mbFileIsOpenForWriting(false) + , mbReadEntryCacheReady(false) + , mbLeaveFileOpenBetweenOperations(optionFlags & 1) + , mSectionPositionMap() + , mSectionNameMap() + { + _stub(this); + mPath[0] = L'\0'; + IniFile::SetPath(pPath); + } + + IniFile::IniFile(IStream* pStream) + : mFileStream((char*)nullptr) + , mnEncodingSrc(8) + , mbFileIsOpenForWriting(false) + , mbReadEntryCacheReady(false) + , mbLeaveFileOpenBetweenOperations(true) + , mSectionPositionMap() + , mSectionNameMap() + , mpStream(pStream) + { + _stub(this); + mPath[0] = L'\0'; + } + + auto_METHOD_VIRTUAL_const(IniFile, IniFile, int, GetOption, Args(Options option), Args(option)); + auto_METHOD_VIRTUAL_VOID(IniFile, IniFile, SetOption, Args(Options option, int value), Args(option, value)); + auto_METHOD_VIRTUAL_const_(IniFile, IniFile, wchar_t const*, GetPath); + auto_METHOD_VIRTUAL(IniFile, IniFile, bool, SetPath, Args(const wchar_t* pPath), Args(pPath)); + auto_METHOD_VIRTUAL_const_(IniFile, IniFile, IStream*, GetStream); + auto_METHOD_VIRTUAL(IniFile, IniFile, bool, SetStream, Args(IStream* pStream), Args(pStream)); + auto_METHOD_VIRTUAL_(IniFile, IniFile, bool, Close); + auto_METHOD_VIRTUAL(IniFile, IniFile, int, ReadEntry, Args(const wchar_t* pSection, const wchar_t* pKey, eastl::basic_string& sValue), Args(pSection, pKey, sValue)); + auto_METHOD_VIRTUAL(IniFile, IniFile, int, ReadEntryToBuffer, Args(const wchar_t* pSection, const wchar_t* pKey, wchar_t* pValue, uint32_t nValueLength), Args(pSection, pKey, pValue, nValueLength)); + // Stub, do not detour. + int __cdecl IniFile::ReadEntryFormatted(const wchar_t* pSection, const wchar_t* pKey, const wchar_t* pValueFormat, ...) { return 0; } + auto_METHOD_VIRTUAL(IniFile, IniFile, bool, WriteEntry, Args(const wchar_t* pSection, const wchar_t* pKey, const wchar_t* pValue), Args(pSection, pKey, pValue)); + // Stub, do not detour. + bool __cdecl IniFile::WriteEntryFormatted(const wchar_t* pSection, const wchar_t* pKey, const wchar_t* kValueFormat, ...) { return 0; } + auto_METHOD_VIRTUAL(IniFile, IniFile, int, ReadBinary, Args(const wchar_t* pSection, const wchar_t* pKey, void* pData, uint32_t nDataLength), Args(pSection, pKey, pData, nDataLength)); + auto_METHOD_VIRTUAL(IniFile, IniFile, bool, WriteBinary, Args(const wchar_t* pSection, const wchar_t* pKey, const void* pData, uint32_t nDataLength), Args(pSection, pKey, pData, nDataLength)); + auto_METHOD_VIRTUAL(IniFile, IniFile, int, EnumSections, Args(EnumCallback_t pCallback, void* pContext), Args(pCallback, pContext)); + auto_METHOD_VIRTUAL(IniFile, IniFile, int, EnumEntries, Args(const wchar_t* pSection, EnumCallback_t pCallback, void* pContext), Args(pSection, pCallback, pContext)); + auto_METHOD_VIRTUAL(IniFile, IniFile, bool, SectionExists, Args(const wchar_t* pSection), Args(pSection)); + auto_METHOD_VIRTUAL(IniFile, IniFile, bool, Open, Args(int nAccessFlags), Args(nAccessFlags)); + auto_METHOD_VIRTUAL_(IniFile, IniFile, int, GetEncoding); + auto_METHOD_VIRTUAL(IniFile, IniFile, bool, LoadSectionNames, Args(int nAccessFlags), Args(nAccessFlags)); + auto_METHOD_VIRTUAL(IniFile, IniFile, bool, GetFileLine8To8, Args(eastl::basic_string& sLine), Args(sLine)); + auto_METHOD_VIRTUAL(IniFile, IniFile, bool, GetFileLine16To16, Args(eastl::basic_string& sLine), Args(sLine)); + auto_METHOD_VIRTUAL(IniFile, IniFile, bool, GetFileLine, Args(eastl::basic_string& sLine), Args(sLine)); + auto_METHOD_VIRTUAL(IniFile, IniFile, bool, ConvertAndWriteStream, Args(const wchar_t* pchar, uint32_t count), Args(pchar, count)); +} + +#endif diff --git a/Spore ModAPI/Spore ModAPI.vcxproj b/Spore ModAPI/Spore ModAPI.vcxproj index e98ebcc6..7ee2371d 100644 --- a/Spore ModAPI/Spore ModAPI.vcxproj +++ b/Spore ModAPI/Spore ModAPI.vcxproj @@ -330,6 +330,7 @@ + @@ -1014,6 +1015,7 @@ + diff --git a/Spore ModAPI/Spore ModAPI.vcxproj.filters b/Spore ModAPI/Spore ModAPI.vcxproj.filters index 42bd3567..e157babe 100644 --- a/Spore ModAPI/Spore ModAPI.vcxproj.filters +++ b/Spore ModAPI/Spore ModAPI.vcxproj.filters @@ -2289,6 +2289,9 @@ Header Files + + Header Files + @@ -2783,6 +2786,9 @@ Source Files + + Source Files + diff --git a/Spore ModAPI/Spore/IO/IniFile.h b/Spore ModAPI/Spore/IO/IniFile.h new file mode 100644 index 00000000..406949da --- /dev/null +++ b/Spore ModAPI/Spore/IO/IniFile.h @@ -0,0 +1,96 @@ +#pragma once + +#include +#include "FileStream.h" + +namespace IO +{ + /// + /// A class that is used to read, parse, and write INI configuration files. + /// Stub, do not detour. + /// + class IniFile + { + public: + enum Options + { + kOptionNone, + kOptionLeaveFileOpen + }; + + typedef bool(*EnumCallback_t)(wchar_t*, wchar_t*, void*); + + IniFile(const wchar_t* pPath, int optionFlags); + IniFile(IStream* pStream); + + /* 00h */ virtual ~IniFile() final = default; + /* 04h */ virtual int GetOption(Options option) const; + /* 08h */ virtual void SetOption(Options option, int value); + /* 0Ch */ virtual wchar_t const* GetPath() const; + /* 10h */ virtual bool SetPath(const wchar_t* pPath); + /* 14h */ virtual class IStream* GetStream() const; + /* 18h */ virtual bool SetStream(IStream* pStream); + /* 1Ch */ virtual bool Close(); + /* 20h */ virtual int ReadEntry(const wchar_t* pSection, const wchar_t* pKey, class eastl::basic_string& sValue); + /* 24h */ virtual int ReadEntryToBuffer(const wchar_t* pSection, const wchar_t* pKey, wchar_t* pValue, uint32_t nValueLength); + // Stub, do not detour. + /* 28h */ virtual int ReadEntryFormatted(const wchar_t* pSection, const wchar_t* pKey, const wchar_t* pValueFormat, ...); + /* 2Ch */ virtual bool WriteEntry(const wchar_t* pSection, const wchar_t* pKey, const wchar_t* pValue); + // Stub, do not detour. + /* 30h */ virtual bool WriteEntryFormatted(const wchar_t* pSection, const wchar_t* pKey, const wchar_t* kValueFormat, ...); + /* 34h */ virtual int ReadBinary(const wchar_t* pSection, const wchar_t* pKey, void* pData, uint32_t nDataLength); + /* 38h */ virtual bool WriteBinary(const wchar_t* pSection, const wchar_t* pKey, const void* pData, uint32_t nDataLength); + /* 3Ch */ virtual int EnumSections(EnumCallback_t pCallback, void* pContext); + /* 40h */ virtual int EnumEntries(const wchar_t* pSection, EnumCallback_t pCallback, void* pContext); + /* 44h */ virtual bool SectionExists(const wchar_t* pSection); + protected: + /* 48h */ virtual bool Open(int nAccessFlags); + /* 4Ch */ virtual int GetEncoding(); + /* 50h */ virtual bool LoadSectionNames(int nAccessFlags); + /* 54h */ virtual bool GetFileLine8To8(class eastl::basic_string& sLine); + /* 58h */ virtual bool GetFileLine16To16(class eastl::basic_string& sLine); + /* 5Ch */ virtual bool GetFileLine(class eastl::basic_string& sLine); + /* 60h */ virtual bool ConvertAndWriteStream(const wchar_t* pchar, uint32_t count); + + /* 04h */ wchar_t mPath[260]; + /* 20Ch */ FileStream mFileStream; + /* 438h */ IStream* mpStream; + /* 43Ch */ int mnEncodingSrc; + /* 440h */ bool mbFileIsOpenForWriting; + /* 441h */ bool mbLeaveFileOpenBetweenOperations; + /* 442h */ bool mbReadEntryCacheReady; + /* 444h */ eastl::map, long, eastl::less>> mSectionPositionMap; + /* 460h */ eastl::map, eastl::basic_string, eastl::less>> mSectionNameMap; + }; + ASSERT_SIZE(IniFile, 0x47C); + + namespace Addresses(IniFile) + { + DeclareAddress(GetOption); + DeclareAddress(SetOption); + DeclareAddress(GetPath); + DeclareAddress(SetPath); + DeclareAddress(GetStream); + DeclareAddress(SetStream); + DeclareAddress(Close); + DeclareAddress(ReadEntry); + DeclareAddress(ReadEntryToBuffer); + DeclareAddress(ReadEntryFormatted); + DeclareAddress(WriteEntry); + DeclareAddress(WriteEntryFormatted); + DeclareAddress(ReadBinary); + DeclareAddress(WriteBinary); + DeclareAddress(EnumSections); + DeclareAddress(EnumEntries); + DeclareAddress(SectionExists); + DeclareAddress(Open); + DeclareAddress(GetEncoding); + DeclareAddress(LoadSectionNames); + DeclareAddress(GetFileLine8To8); + DeclareAddress(GetFileLine16To16); + DeclareAddress(GetFileLine); + DeclareAddress(ConvertAndWriteStream); + // stub + DeclareAddress(_vftable); + } +} \ No newline at end of file From 87cd07466fa7bfbb0a71f7f70043e41e77ac519c Mon Sep 17 00:00:00 2001 From: 0KepOnline <78259495+0KepOnline@users.noreply.github.com> Date: Tue, 7 Jul 2026 20:43:36 +0300 Subject: [PATCH 4/5] Add missing aliases --- Spore ModAPI/SourceCode/DLL/AddressesIO.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Spore ModAPI/SourceCode/DLL/AddressesIO.cpp b/Spore ModAPI/SourceCode/DLL/AddressesIO.cpp index 0b506209..f80b7591 100644 --- a/Spore ModAPI/SourceCode/DLL/AddressesIO.cpp +++ b/Spore ModAPI/SourceCode/DLL/AddressesIO.cpp @@ -245,6 +245,7 @@ namespace IO DefineAddress(Open, SelectAddress(0x672AA0, 0x67D510)); // destructor; private for ModAPI DefineAddress(_dtor, SelectAddress(0x67DC30, 0x67DAD0)); + DefineAddressAlias(Dispose, _dtor); } namespace Addresses(StreamDecompressionZLib) @@ -268,6 +269,7 @@ namespace IO DefineAddress(Open, SelectAddress(0x672DC0, 0x67D850)); // destructor; private for ModAPI DefineAddress(_dtor, SelectAddress(0x67DCE0, 0x67DB80)); + DefineAddressAlias(Dispose, _dtor); } namespace Addresses(IniFile) From 15c9b7a078eee0540d2244f3d774cbf7c8d3f176 Mon Sep 17 00:00:00 2001 From: 0KepOnline <78259495+0KepOnline@users.noreply.github.com> Date: Tue, 7 Jul 2026 21:49:04 +0300 Subject: [PATCH 5/5] Remove `IO::_stub` method, a leftover from earlier tests --- Spore ModAPI/SourceCode/IO/IniFile.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Spore ModAPI/SourceCode/IO/IniFile.cpp b/Spore ModAPI/SourceCode/IO/IniFile.cpp index fa27ca1d..4bdc84da 100644 --- a/Spore ModAPI/SourceCode/IO/IniFile.cpp +++ b/Spore ModAPI/SourceCode/IO/IniFile.cpp @@ -4,13 +4,6 @@ namespace IO { - // Used to replace methods with varargs with their original addresses, as ModAPI doesn't - // support those (and likely won't). - static void _stub(IniFile* pThis) - { - *(void**)pThis = (void*)GetAddress(IniFile, _vftable); - } - IniFile::IniFile(const wchar_t* pPath, int optionFlags) : mFileStream((char*)nullptr) , mpStream(nullptr) @@ -21,7 +14,7 @@ namespace IO , mSectionPositionMap() , mSectionNameMap() { - _stub(this); + *(void**)this = (void*)GetAddress(IniFile, _vftable); mPath[0] = L'\0'; IniFile::SetPath(pPath); } @@ -36,7 +29,7 @@ namespace IO , mSectionNameMap() , mpStream(pStream) { - _stub(this); + *(void**)this = (void*)GetAddress(IniFile, _vftable); mPath[0] = L'\0'; }