Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions src/core/extras/gta3/menu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ class TheMenu : public AbstractFrontend
// Writes temporary @text into the label @tmplabel and returns it's entry
TextEntry TempTextLabel(const std::string& text, const char* tmplabel)
{
fxt.set(tmplabel, text.data());
fxt.set(tmplabel, text.data(), text.size());
return TextLabel(tmplabel);
}

Expand Down Expand Up @@ -215,7 +215,7 @@ void TheMenu::LoadText()
return ParseFXT(fxt, lang.data());
};

this->fxt.add("ML__SM", ""); // Typed search text
this->fxt.add("ML__SM", "", 0); // Typed search text

// Try to load a fxt with the OS locale
auto locale = GetUserDefaultLCID();
Expand Down Expand Up @@ -343,10 +343,11 @@ bool TheMenu::BuildCurrentModsPage(int inc)
{
auto& modinfo = this->mMods[mCurrentModsPage * NumModsPerPage + i].get();
mCurrentPageMods.emplace_back(modinfo);
fxt.set(entry->m_szName, modinfo.GetName().c_str());
const std::string& modName = modinfo.GetName();
fxt.set(entry->m_szName, modName.c_str(), modName.size());
}
else
fxt.set(entry->m_szName, "");
fxt.set(entry->m_szName, "", 0);

}
return true;
Expand All @@ -359,7 +360,7 @@ bool TheMenu::BuildCurrentModsPage(int inc)
{
auto& entry = mPageMods.GetEntry(i)->pEntry;
entry->m_nActionType = MENU_ACTION_SKIP;
fxt.set(entry->m_szName, "");
fxt.set(entry->m_szName, "", 0);
}

return false;
Expand Down Expand Up @@ -679,7 +680,8 @@ void TheMenu::ModPageEvents()
title.resize(max_title_size - 3);
title.append("...");
}
fxt.set("ML_FYHH", modloader::toupper(title).data());
const std::string& titleUpper = modloader::toupper(title);
fxt.set("ML_FYHH", titleUpper.c_str(), titleUpper.size());
};

// Mod page builder for each entry in the Mods page
Expand Down
145 changes: 143 additions & 2 deletions src/plugins/gta3/std.asi/ModuleInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,126 @@
#include "asi.h"
#include "args_translator/translator.hpp"
#include <tlhelp32.h>
#include <winternl.h> // for NTSTATUS

using namespace modloader;


template<class F>
static bool ModulesWalk(uint32_t pid, F functor);

/*
* DLL Load Notification functions and structs from ntdll.dll
* They are documented as "may be changed or removed without further notice" and we can only import them dynamically
*/
typedef struct _LDR_DLL_LOADED_NOTIFICATION_DATA
{
ULONG Flags;
PUNICODE_STRING FullDllName;
PUNICODE_STRING BaseDllName;
PVOID DllBase;
ULONG SizeOfImage;
} LDR_DLL_LOADED_NOTIFICATION_DATA, *PLDR_DLL_LOADED_NOTIFICATION_DATA;

typedef struct _LDR_DLL_UNLOADED_NOTIFICATION_DATA
{
ULONG Flags;
PCUNICODE_STRING FullDllName;
PCUNICODE_STRING BaseDllName;
PVOID DllBase;
ULONG SizeOfImage;
} LDR_DLL_UNLOADED_NOTIFICATION_DATA, *PLDR_DLL_UNLOADED_NOTIFICATION_DATA;

typedef union _LDR_DLL_NOTIFICATION_DATA
{
LDR_DLL_LOADED_NOTIFICATION_DATA Loaded;
LDR_DLL_UNLOADED_NOTIFICATION_DATA Unloaded;
} LDR_DLL_NOTIFICATION_DATA, *PLDR_DLL_NOTIFICATION_DATA;

typedef const _LDR_DLL_NOTIFICATION_DATA * PCLDR_DLL_NOTIFICATION_DATA;

typedef VOID (CALLBACK * PLDR_DLL_NOTIFICATION_FUNCTION)(
_In_ ULONG NotificationReason,
_In_ PCLDR_DLL_NOTIFICATION_DATA NotificationData,
_In_opt_ PVOID Context
);

typedef _Function_class_(LDR_DLL_NOTIFICATION_FUNCTION)
VOID NTAPI LDR_DLL_NOTIFICATION_FUNCTION(
_In_ ULONG NotificationReason,
_In_ PCLDR_DLL_NOTIFICATION_DATA NotificationData,
_In_opt_ PVOID Context
);

static NTSTATUS (NTAPI *pLdrRegisterDllNotification)(_In_ ULONG Flags, _In_ PLDR_DLL_NOTIFICATION_FUNCTION NotificationFunction,
_In_opt_ PVOID Context, _Out_ PVOID *Cookie);

static NTSTATUS (NTAPI *pLdrUnregisterDllNotification)(_In_ PVOID Cookie);

/*
* RAII for the DLL Load Notifications
*/
struct scoped_LdrLoadNotification
{
scoped_LdrLoadNotification(ThePlugin::ModuleInfo* module, const std::string& path)
: module(module)
{
if (pLdrRegisterDllNotification != nullptr && pLdrRegisterDllNotification != nullptr)
{
MultiByteToWideChar(CP_ACP, 0, path.c_str(), static_cast<int>(path.size()), contextPath, MAX_PATH);

if (pLdrRegisterDllNotification(0, LdrDllNotification, this, &notificationCookie) == 0)
{
plugin_ptr->cast<ThePlugin>().bLoadingModulesNow = true;
}
else
{
notificationCookie = nullptr;
}
}
}

~scoped_LdrLoadNotification()
{
if (notificationCookie != nullptr)
{
plugin_ptr->cast<ThePlugin>().bLoadingModulesNow = false;
pLdrUnregisterDllNotification(notificationCookie);
}
}

bool ImportsIntercepted() const { return bImportsIntercepted; }

private:
static void CALLBACK LdrDllNotification(ULONG NotificationReason, PCLDR_DLL_NOTIFICATION_DATA NotificationData, PVOID Context)
{
static constexpr ULONG LDR_DLL_NOTIFICATION_REASON_LOADED = 1;

if (NotificationReason == LDR_DLL_NOTIFICATION_REASON_LOADED)
{
// We only care about a load of the module we are currently loading, not any transient dependencies
const LDR_DLL_LOADED_NOTIFICATION_DATA& notification = NotificationData->Loaded;

scoped_LdrLoadNotification* ctx = static_cast<scoped_LdrLoadNotification*>(Context);
const size_t dllPathLength = notification.FullDllName->Length / sizeof(notification.FullDllName->Buffer[0]);
if (wcsncmp(ctx->contextPath, notification.FullDllName->Buffer, dllPathLength) == 0)
{
// This will be overwritten later anyway, but we need the module registered early
// for the sake of path redirections from DllMain
ctx->module->module = static_cast<HMODULE>(notification.DllBase);
ctx->module->PatchImports();
ctx->bImportsIntercepted = true;
}
}
}

private:
wchar_t contextPath[MAX_PATH];
ThePlugin::ModuleInfo* module;

PVOID notificationCookie = nullptr;
bool bImportsIntercepted = false;
};


/*
Expand Down Expand Up @@ -194,6 +307,20 @@ void ThePlugin::LocateCleo()
}


/*
* Try to import the DLL Load Notification functions
*/
void ThePlugin::LocateDllNotificationFuncs()
{
HMODULE ntdll = LoadLibrary(TEXT("ntdll"));
if (ntdll != nullptr)
{
pLdrRegisterDllNotification = reinterpret_cast<decltype(pLdrRegisterDllNotification)>(GetProcAddress(ntdll, "LdrRegisterDllNotification"));
pLdrUnregisterDllNotification = reinterpret_cast<decltype(pLdrUnregisterDllNotification)>(GetProcAddress(ntdll, "LdrUnregisterDllNotification"));
}
}


/*
* Loads the module assigned to our field path
*/
Expand All @@ -218,11 +345,25 @@ bool ThePlugin::ModuleInfo::Load()
}

// Load the library module into our module field
scoped_LdrLoadNotification xldr(this, file->fullpath());

SetLastError(0);
this->module = bIsMainExecutable? GetModuleHandleA(0) : LoadLibraryA(file->fullpath().c_str());
this->module = bIsMainExecutable? GetModuleHandle(nullptr) : LoadLibraryA(file->fullpath().c_str());

// Patch the module imports to pass throught args translation.
if(this->module) this->PatchImports();
// But do it only if the DLL Load Notification didn't do it already.
if(this->module)
{
if(!xldr.ImportsIntercepted())
{
this->PatchImports();
plugin_ptr->Log("File \"%s\" imports patched via the Legacy method", file->filepath());
}
else
{
plugin_ptr->Log("File \"%s\" imports patched via the DLL Load Notification", file->filepath());
}
}
}
return this->module != 0;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,11 @@ inline void path_translator_base::CallInfo::TranslatePathForASI(const T*& arg, c
bDoTranslation = true;
}
else
bDoTranslation = true;
{
// If relative, only translate if we're NOT in the process of loading modules,
// as we're already chdir'd to inside the mod directory otherwise.
bDoTranslation = !plugin_ptr->cast<ThePlugin>().bLoadingModulesNow;
}

//----
if(bDoTranslation)
Expand Down
5 changes: 4 additions & 1 deletion src/plugins/gta3/std.asi/asi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,11 @@ const ThePlugin::info& ThePlugin::GetInfo()
*/
bool ThePlugin::OnStartup()
{
// Try to import the DLL Load Notification functions
this->LocateDllNotificationFuncs();

// Register GTA module for some arg translation
this->asiList.emplace_front("gta", nullptr, GetModuleHandleA(0));
this->asiList.emplace_front("gta", nullptr, GetModuleHandle(nullptr));
this->asiList.front().PatchImports();

// Find CLEO.asi
Expand Down
8 changes: 7 additions & 1 deletion src/plugins/gta3/std.asi/asi.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,16 +124,22 @@ class ThePlugin : public modloader::basic_plugin
// CLEO.ASI version
int iCleoVersion;
bool bHasNoCleoFolder;

// Set to true when loading - do not translate relative paths, as we're chdir'd into modloader already
// Set by the DLL Load Notification
bool bLoadingModulesNow = false;

// List of asi files need to load (or loaded)
ModuleInfoList asiList; // It's called asiList but it's not limited to .asi files!

// List of CLEO scripts (.cs, .cs3, .cs4, .cs5, .cm)
CsInfoList csList; // It's called cs but it's not limited to .cs files (e.g. cm files works)

// Find all cleo plugins already loaded and push them into asi list
void LocateCleo();

// Try to import the DLL Load Notification functions
void LocateDllNotificationFuncs();

};

Expand Down
7 changes: 5 additions & 2 deletions src/plugins/gta3/std.data/data_traits/handling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -363,8 +363,11 @@ static auto MakeReadmeReader() -> std::function<maybe_readable<HandlingStoreType
// Handling Merger
static auto xinit = initializer([](DataPlugin* plugin_ptr)
{
// TODO instead of using 0xC2B9C8 read offset to handling data (III Airplane refreshing compatibility)
auto ReloadHandling = std::bind(injector::thiscall<void(void*)>::call<0x5BD830>, mem_ptr(0xC2B9C8).get<void>());
auto ReloadHandling = []
{
void* handlingData = *mem_ptr(0x5BFA95 + 1).get<void*>();
injector::thiscall<void(void*)>::call<0x5BD830>(handlingData);
};

// Handling Merger
plugin_ptr->AddMerger<handling_store>("handling.cfg", true, false, false, reinstall_since_start, gdir_refresh(ReloadHandling));
Expand Down
23 changes: 12 additions & 11 deletions src/shared/fxt_parser/fxt.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,41 +74,42 @@ namespace injector
/*
* Used on III/VC to get a wchar string
*/
static string_container to_wchar(const char* value)
static string_container to_wchar(const char* value, size_t len)
{
string_container str; str.reserve((strlen(value) + 1) * 2);
for(auto p = value; *p; ++p)
string_container str; str.reserve((len + 2) * 2);
for(size_t i = 0; i < len; ++i)
{
str.emplace_back(*p); str.emplace_back('\0'); // character
str.emplace_back(value[i]); str.emplace_back('\0'); // character
}
str.emplace_back('\0'); str.emplace_back('\0'); // null terminator
// GTA III and VC always over-read strings by one character, so give two null terminators
str.insert(str.end(), 4, '\0');
return str;
}

/*
* Used on SA to get a char string
*/
static string_container to_char(const char* value)
static string_container to_char(const char* value, size_t len)
{
string_container str(value, value + strlen(value) + 1); //+1 for terminator
string_container str(value, value + len + 1); //+1 for terminator
return str;
}

/*
* Adds a GXT @key - @value pair to the text map for use in our GxtHook
*/
static void add(const char* key, const char* value, hash_type table = 0)
static void add(const char* key, const char* value, size_t len, hash_type table = 0)
{
if(data().can_patch) patch();
data().tmap[table][GetHash(key)] = gvm.IsIII() || gvm.IsVC()? to_wchar(value) : to_char(value);
data().tmap[table][GetHash(key)] = gvm.IsIII() || gvm.IsVC()? to_wchar(value, len) : to_char(value, len);
}

/*
* Overrides the specified GXT @key
*/
static void set(const char* key, const char* value, hash_type table = 0)
static void set(const char* key, const char* value, size_t len, hash_type table = 0)
{
return add(key, value, table);
return add(key, value, len, table);
}

/*
Expand Down
14 changes: 13 additions & 1 deletion src/shared/fxt_parser/fxt_parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,19 @@ namespace injector
}

// Adds into the text map only if found both key and value
if(key && value) manager.add(key, value, table);
if(key && value)
{
// CLEO trims trailing whitespace from strings, we should do the same
char* lastNonWhitespace = value;
for(char* p = value; *p; ++p)
{
if(!(*p == 0x20 || (*p >= 0x09 && *p <= 0x0D))) // NOT whitespace?
{
lastNonWhitespace = p;
}
}
manager.add(key, value, lastNonWhitespace - value + 1, table);
}
}

// Done
Expand Down
3 changes: 2 additions & 1 deletion src/translator/gta3/3/10.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,8 @@ static void III_10(std::map<memory_pointer_raw, memory_pointer_raw>& map)
map[0x5B905E] = 0x4762C2; // call _ZN8CFileMgr8OpenFileEPKcS1_ ; @CFileLoader::LoadLevel
map[0x5BD830] = 0x546DB0; // _ZN16cHandlingDataMgr16LoadHandlingDataEv
map[0x5BD850] = 0x546DDE; // call _ZN8CFileMgr12LoadTextFileEPKcPhi ; @cHandlingDataMgr::LoadHandlingData
map[0xC2B9C8] = 0x728060; // mod_HandlingManager CHandlingData
//map[0xC2B9C8] = 0x728060; // mod_HandlingManager CHandlingData
map[0x5BFA95] = 0x48BD77; // mov ecx, offset mod_HandlingManager CHandlingData
map[0x5B8428] = 0x476AEB; // call _ZN8CFileMgr8OpenFileEPKcS1_ ; @CFileLoader::LoadObjectTypes
map[0x5B871A] = 0x478393; // call _ZN8CFileMgr8OpenFileEPKcS1_ ; @CFileLoader::LoadScene
map[0x5BF750] = 0x564EA0; // _ZN11CWeaponInfo10InitialiseEv
Expand Down
3 changes: 2 additions & 1 deletion src/translator/gta3/sa/10us.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,8 @@ static void sa_10us(std::map<memory_pointer_raw, memory_pointer_raw>& map)
map[0x5B905E] = 0x5B905E; // call _ZN8CFileMgr8OpenFileEPKcS1_ ; @CFileLoader::LoadLevel
map[0x5BD830] = 0x5BD830; // _ZN16cHandlingDataMgr16LoadHandlingDataEv
map[0x5BD850] = 0x5BD850; // call _ZN8CFileMgr8OpenFileEPKcS1_ ; @cHandlingDataMgr::LoadHandlingData
map[0xC2B9C8] = 0xC2B9C8; // mod_HandlingManager CHandlingData
//map[0xC2B9C8] = 0xC2B9C8; // mod_HandlingManager CHandlingData
map[0x5BFA95] = 0x5BFA95; // mov ecx, offset mod_HandlingManager CHandlingData
map[0x5B8428] = 0x5B8428; // call _ZN8CFileMgr8OpenFileEPKcS1_ ; @CFileLoader::LoadObjectTypes
map[0x5B871A] = 0x5B871A; // call _ZN8CFileMgr8OpenFileEPKcS1_ ; @CFileLoader::LoadScene
map[0x5DD780] = 0x5DD780; // _ZN9CPlantMgr12ReloadConfigEv
Expand Down
3 changes: 2 additions & 1 deletion src/translator/gta3/vc/10.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,8 @@ static void vc_10(std::map<memory_pointer_raw, memory_pointer_raw>& map)
map[0x5B905E] = 0x48D97C; // call _ZN8CFileMgr8OpenFileEPKcS1_ ; @CFileLoader::LoadLevel
map[0x5BD830] = 0x5AAE20; // _ZN16cHandlingDataMgr16LoadHandlingDataEv
map[0x5BD850] = 0x5AAE4E; // call _ZN8CFileMgr12LoadTextFileEPKcPhi ; @cHandlingDataMgr::LoadHandlingData
map[0xC2B9C8] = 0x978E58; // mod_HandlingManager CHandlingData
//map[0xC2B9C8] = 0x978E58; // mod_HandlingManager CHandlingData
map[0x5BFA95] = 0x4A5034; // mov ecx, offset mod_HandlingManager CHandlingData
map[0x5B8428] = 0x48C846; // call _ZN8CFileMgr8OpenFileEPKcS1_ ; @CFileLoader::LoadObjectTypes
map[0x5B871A] = 0x48B079; // call _ZN8CFileMgr8OpenFileEPKcS1_ ; @CFileLoader::LoadScene
map[0x5BF750] = 0x5D5750; // _ZN11CWeaponInfo10InitialiseEv
Expand Down