Allow SVF to register model names, not just IDs

This commit is contained in:
Silent 2024-05-18 16:49:03 +02:00
parent ccf61157fb
commit 4169c9e867
No known key found for this signature in database
GPG key ID: AE53149BB0C45AF1
15 changed files with 263 additions and 23 deletions

View file

@ -5,6 +5,7 @@
#include "Utils/HookEach.hpp"
#include "StoredCar.h"
#include "SVF.h"
#include "ParseUtils.hpp"
#include "Utils/DelimStringReader.h"
@ -136,9 +137,11 @@ namespace ExtraCompSpecularity
GetPrivateProfileSectionW(L"ExtraCompSpecularityExceptions", reader.GetBuffer(), reader.GetSize(), pPath);
while (const wchar_t* str = reader.GetString())
{
int32_t toList = wcstol(str, nullptr, 0);
if ( toList > 0 )
SVF::RegisterFeature(toList, SVF::Feature::_INTERNAL_NO_SPECULARITY_ON_EXTRAS);
auto modelID = ParseUtils::TryParseInt(str);
if (modelID)
SVF::RegisterFeature(*modelID, SVF::Feature::_INTERNAL_NO_SPECULARITY_ON_EXTRAS);
else
SVF::RegisterFeature(ParseUtils::ParseString(str), SVF::Feature::_INTERNAL_NO_SPECULARITY_ON_EXTRAS);
}
}

View file

@ -0,0 +1,37 @@
#include "ParseUtils.hpp"
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
static std::string WcharToUTF8(std::wstring_view str)
{
std::string result;
const int count = WideCharToMultiByte(CP_UTF8, 0, str.data(), str.size(), nullptr, 0, nullptr, nullptr);
if (count != 0)
{
result.resize(count);
WideCharToMultiByte(CP_UTF8, 0, str.data(), str.size(), result.data(), count, nullptr, nullptr);
}
return result;
}
std::optional<int32_t> ParseUtils::TryParseInt(const wchar_t* str)
{
std::optional<int32_t> result;
wchar_t* end;
const int32_t val = wcstol(str, &end, 0);
if (*end == '\0')
{
result.emplace(val);
}
return result;
}
std::string ParseUtils::ParseString(const wchar_t* str)
{
return WcharToUTF8(str);
}

View file

@ -0,0 +1,11 @@
#pragma once
#include <cstdint>
#include <optional>
#include <string>
namespace ParseUtils
{
std::optional<int32_t> TryParseInt(const wchar_t* str);
std::string ParseString(const wchar_t* str);
};

View file

@ -84,6 +84,26 @@ namespace SVF
#endif
};
static std::multimap<std::string, std::tuple<Feature, int32_t> > specialVehFeaturesByName;
static void* (*GetModelInfoCB)(const char* name, int* outIndex);
static bool bModelNamesReady = false;
void _resolveFeatureNamesInternal()
{
if (bModelNamesReady && GetModelInfoCB != nullptr && !specialVehFeaturesByName.empty())
{
for (const auto& feature : specialVehFeaturesByName)
{
int32_t index;
if (GetModelInfoCB(feature.first.c_str(), &index) != nullptr)
{
specialVehFeatures.emplace(index, feature.second);
}
}
specialVehFeaturesByName.clear();
}
}
int32_t RegisterFeature( int32_t modelID, Feature feature )
{
if ( feature == Feature::NO_FEATURE ) return -1;
@ -93,6 +113,15 @@ namespace SVF
return cookie;
}
int32_t RegisterFeature( std::string modelName, Feature feature )
{
if ( feature == Feature::NO_FEATURE ) return -1;
const int32_t cookie = _getCookie();
specialVehFeaturesByName.emplace( std::move(modelName), std::make_tuple(feature, cookie) );
return cookie;
}
void DeleteFeature( int32_t cookie )
{
for ( auto it = specialVehFeatures.begin(); it != specialVehFeatures.end(); ++it )
@ -103,6 +132,15 @@ namespace SVF
return;
}
}
for ( auto it = specialVehFeaturesByName.begin(); it != specialVehFeaturesByName.end(); ++it )
{
if ( std::get<int32_t>(it->second) == cookie )
{
specialVehFeaturesByName.erase( it );
return;
}
}
}
void DisableStockVehiclesForFeature( Feature feature )
@ -119,10 +157,23 @@ namespace SVF
++it;
}
}
for ( auto it = specialVehFeaturesByName.begin(); it != specialVehFeaturesByName.end(); )
{
if ( std::get<Feature>(it->second) == feature && std::get<int32_t>(it->second) <= highestStockCookie )
{
it = specialVehFeaturesByName.erase( it );
}
else
{
++it;
}
}
}
bool ModelHasFeature( int32_t modelID, Feature feature )
{
_resolveFeatureNamesInternal();
auto results = specialVehFeatures.equal_range( modelID );
return std::find_if( results.first, results.second, [feature] ( const auto& e ) {
return std::get<Feature>(e.second) == feature;
@ -131,6 +182,7 @@ namespace SVF
std::function<bool(Feature)> ForAllModelFeatures( int32_t modelID, std::function<bool(Feature)> pred )
{
_resolveFeatureNamesInternal();
auto results = specialVehFeatures.equal_range( modelID );
for ( auto it = results.first; it != results.second; ++it )
{
@ -141,6 +193,16 @@ namespace SVF
}
return std::move(pred);
}
void RegisterGetModelInfoCB(void*(*func)(const char*, int*))
{
GetModelInfoCB = func;
}
void MarkModelNamesReady()
{
bModelNamesReady = true;
}
}
// Returns "feature cookie" on success, -1 on failure
@ -152,6 +214,12 @@ __declspec(dllexport) int32_t RegisterSpecialVehicleFeature( int32_t modelID, co
return SVF::RegisterFeature( modelID, SVF::GetFeatureFromName(featureName) );
}
__declspec(dllexport) int32_t RegisterSpecialVehicleFeatureByName( const char* modelName, const char* featureName )
{
if ( featureName == nullptr || modelName == nullptr ) return -1;
return SVF::RegisterFeature( modelName, SVF::GetFeatureFromName(featureName) );
}
__declspec(dllexport) void DeleteSpecialVehicleFeature( int32_t cookie )
{
if ( cookie == -1 ) return;

View file

@ -1,6 +1,7 @@
#pragma once
#include <functional>
#include <string>
namespace SVF
{
@ -49,8 +50,12 @@ namespace SVF
};
int32_t RegisterFeature( int32_t modelID, Feature feature );
int32_t RegisterFeature( std::string modelName, Feature feature );
void DeleteFeature( int32_t cookie );
void DisableStockVehiclesForFeature( Feature feature );
bool ModelHasFeature( int32_t modelID, Feature feature );
std::function<bool(Feature)> ForAllModelFeatures( int32_t modelID, std::function<bool(Feature)> pred );
void RegisterGetModelInfoCB(void*(*func)(const char*, int*));
void MarkModelNamesReady();
};

View file

@ -839,6 +839,17 @@ namespace SitInBoat
}
namespace SVFReadyHook
{
static void (*orgInitialiseObjectData)(const char*);
static void InitialiseObjectData_ReadySVF(const char* path)
{
orgInitialiseObjectData(path);
SVF::MarkModelNamesReady();
}
}
void InjectDelayedPatches_III_Common( bool bHasDebugMenu, const wchar_t* wcModulePath )
{
using namespace Memory;
@ -995,6 +1006,21 @@ void InjectDelayedPatches_III_Common( bool bHasDebugMenu, const wchar_t* wcModul
}
TXN_CATCH();
// Register CBaseModelInfo::GetModelInfo for SVF so we can resolve model names
try
{
using namespace SVFReadyHook;
auto initialiseObjectData = get_pattern("E8 ? ? ? ? B3 01 59 8D 6D 04");
auto getModelInfo = (void*(*)(const char*, int*))get_pattern("31 FF 8D 84 20 00 00 00 00 8B 04 BD", -7);
InterceptCall(initialiseObjectData, orgInitialiseObjectData, InitialiseObjectData_ReadySVF);
SVF::RegisterGetModelInfoCB(getModelInfo);
}
TXN_CATCH();
FLAUtils::Init(moduleList);
}

View file

@ -30,6 +30,11 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Shipping|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\SilentPatch\ParseUtils.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Shipping|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\SilentPatch\RWGTA.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Shipping|Win32'">NotUsing</PrecompiledHeader>
@ -66,6 +71,7 @@
<ClInclude Include="..\SilentPatch\debugmenu_public.h" />
<ClInclude Include="..\SilentPatch\Desktop.h" />
<ClInclude Include="..\SilentPatch\Maths.h" />
<ClInclude Include="..\SilentPatch\ParseUtils.hpp" />
<ClInclude Include="..\SilentPatch\StdAfx.h" />
<ClInclude Include="..\SilentPatch\StoredCar.h" />
<ClInclude Include="..\SilentPatch\SVF.h" />

View file

@ -57,6 +57,9 @@
<ClCompile Include="..\SilentPatch\TheFLAUtils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\SilentPatch\ParseUtils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\SilentPatch\Timer.h">
@ -104,6 +107,9 @@
<ClInclude Include="..\SilentPatch\TheFLAUtils.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\SilentPatch\ParseUtils.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\SilentPatch\SilentPatch.rc">

View file

@ -29,6 +29,7 @@
#include "Utils/HookEach.hpp"
#include "Desktop.h"
#include "SVF.h"
#include "debugmenu_public.h"
#include "resource.h"
@ -2659,6 +2660,16 @@ static bool IgnoresWeaponPedsForPCFix()
}
namespace SVFReadyHook
{
static void (*orgMatchAllModelStrings)();
static void MatchAllModelStrings_ReadySVF()
{
orgMatchAllModelStrings();
SVF::MarkModelNamesReady();
}
}
#ifndef NDEBUG
// ============= QPC spoof for verifying high timer issues =============
@ -3894,6 +3905,16 @@ BOOL InjectDelayedPatches_10()
}
}
// Register CBaseModelInfo::GetModelInfo for SVF so we can resolve model names
{
using namespace SVFReadyHook;
auto func = (void*(*)(const char*, int*))0x4C5940;
InterceptCall(0x5B922F, orgMatchAllModelStrings, MatchAllModelStrings_ReadySVF);
SVF::RegisterGetModelInfoCB(func);
}
#ifndef NDEBUG
if ( const int QPCDays = GetPrivateProfileIntW(L"Debug", L"AddDaysToQPC", 0, wcModulePath); QPCDays != 0 )
{

View file

@ -207,6 +207,11 @@ copy /y "$(TargetPath)" "H:\Rockstar Games\Grand Theft Auto San Andreas\SilentPa
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Shipping|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\SilentPatch\ParseUtils.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Shipping|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\SilentPatch\SVF.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Shipping|Win32'">NotUsing</PrecompiledHeader>
@ -246,6 +251,7 @@ copy /y "$(TargetPath)" "H:\Rockstar Games\Grand Theft Auto San Andreas\SilentPa
<ClInclude Include="..\SilentPatch\debugmenu_public.h" />
<ClInclude Include="..\SilentPatch\Desktop.h" />
<ClInclude Include="..\SilentPatch\Maths.h" />
<ClInclude Include="..\SilentPatch\ParseUtils.hpp" />
<ClInclude Include="..\SilentPatch\resource1.h" />
<ClInclude Include="..\SilentPatch\RWUtils.hpp" />
<ClInclude Include="..\SilentPatch\SVF.h" />

View file

@ -81,6 +81,9 @@
<ClCompile Include="..\SilentPatch\Desktop.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\SilentPatch\ParseUtils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="StdAfxSA.h">
@ -164,6 +167,9 @@
<ClInclude Include="..\SilentPatch\RWUtils.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\SilentPatch\ParseUtils.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\SilentPatch\SilentPatch.rc">

View file

@ -6,6 +6,7 @@
#include "PedSA.h"
#include "Utils/DelimStringReader.h"
#include "PlayerInfoSA.h"
#include "ParseUtils.hpp"
#include "SVF.h"
@ -27,26 +28,29 @@ bool ReadDoubleRearWheels(const wchar_t* pPath)
GetPrivateProfileSectionW( L"DoubleRearWheels", reader.GetBuffer(), reader.GetSize(), pPath );
while ( const wchar_t* str = reader.GetString() )
{
wchar_t textLine[64];
wchar_t* context = nullptr;
wchar_t* token;
wchar_t textLine[128];
wcscpy_s(textLine, str);
wcscpy_s( textLine, str );
token = wcstok_s( textLine, L"=", &context );
wchar_t* context = nullptr;
wchar_t* model = wcstok_s(textLine, L"=", &context);
if (model == nullptr) continue;
int32_t toList = wcstol( token, nullptr, 0 );
if ( toList <= 0 ) continue;
wchar_t* val = wcstok_s(nullptr, L"=", &context);
if (val == nullptr) continue;
wchar_t* begin = wcstok_s( nullptr, L"=", &context );
if ( begin == nullptr ) continue;
auto value = ParseUtils::TryParseInt(val);
if (!value) continue;
wchar_t* end = nullptr;
bool value = wcstoul( begin, &end, 0 ) != 0;
if ( begin != end )
auto modelID = ParseUtils::TryParseInt(model);
if (modelID)
{
SVF::RegisterFeature( toList, value ? SVF::Feature::_INTERNAL_FORCE_DOUBLE_RWHEELS_ON : SVF::Feature::_INTERNAL_FORCE_DOUBLE_RWHEELS_OFF );
listedAny = true;
SVF::RegisterFeature(*modelID, *value ? SVF::Feature::_INTERNAL_FORCE_DOUBLE_RWHEELS_ON : SVF::Feature::_INTERNAL_FORCE_DOUBLE_RWHEELS_OFF);
}
else
{
SVF::RegisterFeature(ParseUtils::ParseString(model), *value ? SVF::Feature::_INTERNAL_FORCE_DOUBLE_RWHEELS_ON : SVF::Feature::_INTERNAL_FORCE_DOUBLE_RWHEELS_OFF);
}
listedAny = true;
}
return listedAny;
}
@ -202,9 +206,11 @@ void ReadRotorFixExceptions(const wchar_t* pPath)
GetPrivateProfileSectionW( L"RotorFixExceptions", reader.GetBuffer(), reader.GetSize(), pPath );
while ( const wchar_t* str = reader.GetString() )
{
int32_t toList = wcstol( str, nullptr, 0 );
if ( toList > 0 )
SVF::RegisterFeature( toList, SVF::Feature::_INTERNAL_NO_ROTOR_FADE );
auto ID = ParseUtils::TryParseInt(str);
if (ID)
SVF::RegisterFeature(*ID, SVF::Feature::_INTERNAL_NO_ROTOR_FADE);
else
SVF::RegisterFeature(ParseUtils::ParseString(str), SVF::Feature::_INTERNAL_NO_ROTOR_FADE);
}
}
@ -216,9 +222,11 @@ void ReadLightbeamFixExceptions(const wchar_t* pPath)
GetPrivateProfileSectionW( L"LightbeamFixExceptions", reader.GetBuffer(), reader.GetSize(), pPath );
while ( const wchar_t* str = reader.GetString() )
{
int32_t toList = wcstol( str, nullptr, 0 );
if ( toList > 0 )
SVF::RegisterFeature( toList, SVF::Feature::_INTERNAL_NO_LIGHTBEAM_BFC_FIX );
auto ID = ParseUtils::TryParseInt(str);
if (ID)
SVF::RegisterFeature(*ID, SVF::Feature::_INTERNAL_NO_LIGHTBEAM_BFC_FIX);
else
SVF::RegisterFeature(ParseUtils::ParseString(str), SVF::Feature::_INTERNAL_NO_LIGHTBEAM_BFC_FIX);
}
}

View file

@ -771,6 +771,17 @@ namespace CarPartsBackfaceCulling
}
namespace SVFReadyHook
{
static void (*orgInitialiseObjectData)(const char*);
static void InitialiseObjectData_ReadySVF(const char* path)
{
orgInitialiseObjectData(path);
SVF::MarkModelNamesReady();
}
}
void InjectDelayedPatches_VC_Common( bool bHasDebugMenu, const wchar_t* wcModulePath )
{
using namespace Memory;
@ -926,6 +937,20 @@ void InjectDelayedPatches_VC_Common( bool bHasDebugMenu, const wchar_t* wcModule
TXN_CATCH();
}
// Register CBaseModelInfo::GetModelInfo for SVF so we can resolve model names
try
{
using namespace SVFReadyHook;
auto initialiseObjectData = get_pattern("E8 ? ? ? ? 59 E8 ? ? ? ? E8 ? ? ? ? 31 DB");
auto getModelInfo = (void*(*)(const char*, int*))get_pattern("57 31 FF 55 8B 6C 24 14", -6);
InterceptCall(initialiseObjectData, orgInitialiseObjectData, InitialiseObjectData_ReadySVF);
SVF::RegisterGetModelInfoCB(getModelInfo);
}
TXN_CATCH();
FLAUtils::Init(moduleList);
}

View file

@ -170,6 +170,7 @@
<ClInclude Include="..\SilentPatch\debugmenu_public.h" />
<ClInclude Include="..\SilentPatch\Desktop.h" />
<ClInclude Include="..\SilentPatch\Maths.h" />
<ClInclude Include="..\SilentPatch\ParseUtils.hpp" />
<ClInclude Include="..\SilentPatch\RWUtils.hpp" />
<ClInclude Include="..\SilentPatch\StdAfx.h" />
<ClInclude Include="..\SilentPatch\StoredCar.h" />
@ -198,6 +199,11 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Shipping|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\SilentPatch\ParseUtils.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Shipping|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\SilentPatch\RWGTA.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Shipping|Win32'">NotUsing</PrecompiledHeader>

View file

@ -66,6 +66,9 @@
<ClInclude Include="..\SilentPatch\RWUtils.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\SilentPatch\ParseUtils.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\SilentPatch\Timer.cpp">
@ -107,6 +110,9 @@
<ClCompile Include="..\SilentPatch\TheFLAUtils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\SilentPatch\ParseUtils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\SilentPatch\SilentPatch.rc">