Skip to content
This repository has been archived by the owner on Jan 6, 2023. It is now read-only.

Commit

Permalink
code cleanup, add custom device
Browse files Browse the repository at this point in the history
  • Loading branch information
Vektor committed Jan 31, 2022
1 parent 65f4493 commit b7a1bcd
Show file tree
Hide file tree
Showing 10 changed files with 325 additions and 156 deletions.
21 changes: 21 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# ClosedIV

This repository contains the source code for ClosedIV.asi, an open source alternative to OpenIV.asi for GTA5

## Benefits over OpenIV.asi

- open source
- supports all features of OpenIV.asi
- supports multiple different versions of ASI Loaders (Recommended: https://github.com/ThirteenAG/Ultimate-ASI-Loader)

## How to build

CMake 3.17.x and Visual Studio is needed to build the project. You can run release.bat to build it quickly or use generate_*.bat to create project files for a specific VS version.

## Goals
- [x] Initial release
- [ ] Making the code look better
- [ ] Adding log files
- [ ] Adding logs to debug mods created by people, so they know what is wrong with their broken stuff
- [ ] Adding support to replace files without copying the entire RPF
- [ ] Support RPFs as directories
17 changes: 17 additions & 0 deletions src/gtav/rage/fiDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,21 @@ void rage::fiDevice::SetPath(const char* path, bool allowRoot, rage::fiDevice* p
.as<void(*)(void*, const char*, bool, rage::fiDevice*)>();

func(this, path, allowRoot, parent);
}

rage::fiDeviceRelative::fiDeviceRelative()
{
static auto relativeDeviceVMT = memory::scan("48 8D 05 ? ? ? ? 48 89 03 EB ? 33 DB 48 8D 15 ? ? ? ? 45 33 C9").add(3).rip().as<void*>();
VMT = relativeDeviceVMT;
pad[0xF8] = 0;
}

bool rage::fiDeviceRelative::Mount(const char* mountPoint)
{
return reinterpret_cast<rage::fiDevice*>(this)->Mount(mountPoint);
}

void rage::fiDeviceRelative::SetPath(const char* path, bool allowRoot, rage::fiDevice* parent)
{
reinterpret_cast<rage::fiDevice*>(this)->SetPath(path, allowRoot, parent);
}
11 changes: 11 additions & 0 deletions src/gtav/rage/fiDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,15 @@ namespace rage
bool Mount(const char* mountPoint);
void SetPath(const char* path, bool allowRoot, rage::fiDevice* parent);
};

class __declspec(novtable) fiDeviceRelative
{
void* VMT;
char pad[0x108];
public:
fiDeviceRelative();

bool Mount(const char* mountPoint);
void SetPath(const char* path, bool allowRoot, rage::fiDevice* parent);
};
}
60 changes: 60 additions & 0 deletions src/hooks/CustomDevice.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#include "main.h"

/*
List of stuff you can replace with this: https://gist.github.com/martonp96/59f731446c7f17db3f400c2be458c4a4
How to do it:
Find something from the list, eg. common:/data/dlclist.xml
Copy the original dlclist.xml from the files and place it in [your GTA5 folder]/newmods/common/data/dlclist.xml
Edit the file, add a new entry like <Item>dlcpacks:/mrpd/</Item>
Place the single player dlc.rpf like [your GTA5 folder]/newmods/dlcpacks/mrpd/dlc.rpf
Start the game and the modded stuff should be loaded automatically.
I used this for the example: https://hu.gta5-mods.com/maps/community-mission-row-pd
*/

void(*InitialMountOrig)();
void InitialMountHook()
{
InitialMountOrig();

std::filesystem::path cwd = std::filesystem::current_path() / "newmods/";
cwd.make_preferred();

//logger::info("path: %s", cwd.string().c_str());

rage::fiDeviceRelative* rootDevice = new rage::fiDeviceRelative();
rootDevice->SetPath(cwd.string().c_str(), true, nullptr);

if (rootDevice->Mount("mods:/"))
logger::info("Root device mounted!");

rage::fiDeviceRelative* platformDevice = new rage::fiDeviceRelative();
rage::fiDeviceRelative* platformDeviceCRC = new rage::fiDeviceRelative();
rage::fiDeviceRelative* commonDevice = new rage::fiDeviceRelative();
rage::fiDeviceRelative* commonDeviceCRC = new rage::fiDeviceRelative();
rage::fiDeviceRelative* dlcDevice = new rage::fiDeviceRelative();

platformDevice->SetPath("mods:/platform/", true, nullptr);
platformDevice->Mount("platform:/");

platformDeviceCRC->SetPath("mods:/platform/", true, nullptr);
platformDeviceCRC->Mount("platformcrc:/");

commonDevice->SetPath("mods:/common/", true, nullptr);
commonDevice->Mount("common:/");

commonDeviceCRC->SetPath("mods:/common/", true, nullptr);
commonDeviceCRC->Mount("commoncrc:/");

dlcDevice->SetPath("mods:/dlcpacks/", true, nullptr);
dlcDevice->Mount("dlcpacks:/");
}

static memory::InitFuncs CustomDevice([] {
auto mem = memory::scan("E8 ? ? ? ? E8 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 48 8B D8 48 85 C0 74 14 48 8B C8 E8 ? ? ? ? 48 8D 05 ? ? ? ? 48 89 03 EB 02 33 DB 48 8D 15");
InitialMountOrig = mem.add(1).rip().as<decltype(InitialMountOrig)>();
mem.set_call(InitialMountHook);
});
91 changes: 91 additions & 0 deletions src/hooks/FileReadHooks.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#include "main.h"

HANDLE OpenBulkHook(void* device, const char* path, __int64* a3)
{
HANDLE FileW;
WCHAR WideCharStr[256];

*a3 = 0;

MultiByteToWideChar(0xFDE9u, 0, path, -1, WideCharStr, 256);

FileW = CreateFileW((L"mods/" + std::wstring(WideCharStr)).c_str(), 0x80000000, 1u, 0, 3, 0x80, 0);

if (FileW == (HANDLE)-1)
FileW = CreateFileW(WideCharStr, 0x80000000, 1u, 0, 3, 0x80, 0);
else
{
logger::info("[%s] Found mods/%s", __FUNCTION__, path);
}

return FileW;
}

FILETIME GetFileTimeHook(void* device, const char* path)
{
WCHAR WideCharStr[256];
WIN32_FILE_ATTRIBUTE_DATA FileInformation;

MultiByteToWideChar(0xFDE9u, 0, path, -1, WideCharStr, 256);

if (GetFileAttributesExW((L"mods/" + std::wstring(WideCharStr)).c_str(), GetFileExInfoStandard, &FileInformation))
{
logger::info("[%s] Found mods/%s", __FUNCTION__, path);
return FileInformation.ftLastWriteTime;
}
else if (GetFileAttributesExW(WideCharStr, GetFileExInfoStandard, &FileInformation))
return FileInformation.ftLastWriteTime;
else
return (FILETIME)0;
}

uint64_t GetFileSizeHook(void* device, const char* path)
{
WCHAR WideCharStr[256];
WIN32_FILE_ATTRIBUTE_DATA FileInformation;

MultiByteToWideChar(0xFDE9u, 0, path, -1, WideCharStr, 256);

if (GetFileAttributesExW((L"mods/" + std::wstring(WideCharStr)).c_str(), GetFileExInfoStandard, &FileInformation))
{
logger::info("[%s] Found mods/%s", __FUNCTION__, path);
return FileInformation.nFileSizeLow | (static_cast<size_t>(FileInformation.nFileSizeHigh) << 32);
}
else if (GetFileAttributesExW(WideCharStr, GetFileExInfoStandard, &FileInformation))
return FileInformation.nFileSizeLow | (static_cast<size_t>(FileInformation.nFileSizeHigh) << 32);
else
return 0;
}

uint64_t GetAttributesHook(void* device, const char* path)
{
WCHAR WideCharStr[256];
DWORD FileAttributesW;

MultiByteToWideChar(0xFDE9u, 0, path, -1, WideCharStr, 256);

FileAttributesW = GetFileAttributesW((L"mods/" + std::wstring(WideCharStr)).c_str());

if (FileAttributesW == -1)
FileAttributesW = GetFileAttributesW(WideCharStr);
else
{
logger::info("[%s] Found mods/%s", __FUNCTION__, path);
}
return FileAttributesW;
}

static memory::InitFuncs FileReadHooks([] {
//hooks for reading files
memory::scan("40 53 48 81 EC ? ? ? ? 49 8B D8 4C 8B C2 48 8D 4C 24 ? BA ? ? ? ? E8 ? ? ? ? 48 83 64 24")
.make_jmp_ret(OpenBulkHook);

memory::scan("48 81 EC ? ? ? ? 4C 8B C2 48 8D 4C 24 ? BA ? ? ? ? E8 ? ? ? ? 4C 8D 44 24 ? 33 D2 48 8B C8 FF 15 ? ? ? ? 85 C0 75 04 33 C0 EB 0F 8B 44 24 38 8B 4C 24 34 48 C1 E0 20 48 0B C1 48 81 C4")
.make_jmp_ret(GetFileTimeHook);

memory::scan("48 81 EC ? ? ? ? 4C 8B C2 48 8D 4C 24 ? BA ? ? ? ? E8 ? ? ? ? 4C 8D 44 24 ? 33 D2 48 8B C8 FF 15 ? ? ? ? 85 C0 75 04 33 C0 EB 0F 8B 44 24 3C 8B 4C 24 40 48 C1 E0 20 48 0B C1 48 81 C4")
.make_jmp_ret(GetFileSizeHook);

memory::scan("48 89 5C 24 ? 57 48 81 EC ? ? ? ? 4C 8B C2 48 8D 4C 24 ? BA ? ? ? ? E8 ? ? ? ? 48 8B C8 FF 15 ? ? ? ? 83 CF FF 8B D8 3B C7 74 0F 48 8D 4C 24")
.make_jmp_ret(GetAttributesHook);
});
25 changes: 25 additions & 0 deletions src/hooks/MPDLCMapHooks.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include "main.h"

void(*LoadMpDlc)();
void(*EnableMpDlcMaps)(bool);

bool(*GameStateChangeOrig)(int);
bool GameStateChangeHook(int gameState)
{
if (gameState == 0) //GAME_STATE_PLAYING
{
LoadMpDlc();
EnableMpDlcMaps(true);
}
return GameStateChangeOrig(gameState);
}

static memory::InitFuncs EnableMpDlcMapsHooks([] {
//get game state to enable mp dlc maps
auto mem = memory::scan("E8 ? ? ? ? 84 C0 74 ? E8 ? ? ? ? 0F B6 0D");
GameStateChangeOrig = mem.add(1).rip().as<decltype(GameStateChangeOrig)>();
mem.set_call(GameStateChangeHook);

LoadMpDlc = memory::scan("C6 05 ? ? ? ? 00 E8 ? ? ? ? 48 8B 0D ? ? ? ? BA E2 99 8F 57").add(-0xB).as<decltype(LoadMpDlc)>();
EnableMpDlcMaps = memory::scan("40 53 48 83 EC 20 8B D9 89 0D").as<decltype(EnableMpDlcMaps)>();
});
71 changes: 71 additions & 0 deletions src/hooks/PackfileEncryptionHooks.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#include "main.h"

uint32_t currentEncryption;
bool FindEncryptionHook(uint32_t encryption)
{
currentEncryption = encryption;
return (encryption & 0xFF00000) == 0xFE00000;
}

void(*DecryptHeaderOrig)(uint32_t, char*, int);
void DecryptHeaderHook(uint32_t salt, char* entryTable, int size)
{
if (currentEncryption == 0x4E45504F) //OPEN
{
logger::info("not encrypted RPF found");
return;
}
DecryptHeaderOrig(salt, entryTable, size);
}

void(*DecryptHeader2Orig)(uint32_t, uint32_t, char*, int);
void DecryptHeader2Hook(uint32_t encryption, uint32_t salt, char* header, int nameTableLen)
{
if (encryption == 0x4E45504F) //OPEN
{
logger::info("not encrypted RPF found");
return;
}
DecryptHeader2Orig(encryption, salt, header, nameTableLen);
}

bool(*ParseHeaderOrig)(rage::fiPackfile*, const char*, bool, void*);
bool ParseHeaderHook(rage::fiPackfile* a1, const char* name, bool readHeader, void* customHeader)
{
logger::info("Parsing header for %s", name);

bool ret = ParseHeaderOrig(a1, name, readHeader, customHeader);
if (ret)
{
for (int i = 0; i < a1->filesCount; ++i)
{
Entry* v21 = (Entry*)(a1->entryTable + 16 * i);
if (v21->IsBinary() && v21->bin.nameOffset > 0 && v21->bin.isEncrypted)
{
if (currentEncryption == 0x4E45504F) //OPEN
v21->bin.isEncrypted = 0xFEFFFFF;
}
}

if (currentEncryption == 0x4E45504F) //OPEN
a1->currentFileOffset = 0xFEFFFFF;
}
return ret;
}

static memory::InitFuncs PackfileEncryptionHooks([] {
//allow unencrypted RPFs
memory::scan("E8 ? ? ? ? 48 8B 53 20 44 8B C7 41 8B CE E8").set_call(FindEncryptionHook);

auto mem = memory::scan("E8 ? ? ? ? 41 8B D4 44 39 63 28 76 3F 41 B9");
DecryptHeaderOrig = mem.add(1).rip().as<decltype(DecryptHeaderOrig)>();
mem.set_call(DecryptHeaderHook);

mem = memory::scan("E8 ? ? ? ? 8B 55 F8 48 8B 43 10 48 03 D0 48 8B CB 48 89 53 18 66 44 89 22 33 D2 E8");
DecryptHeader2Orig = mem.add(1).rip().as<decltype(DecryptHeader2Orig)>();
mem.set_call(DecryptHeader2Hook);

mem = memory::scan("44 88 BB ? ? ? ? 89 43 58 E8 ? ? ? ? 4C 8D 9C 24 ? ? ? ? 49 8B 5B 38 49 8B 73 40 49 8B 7B 48 49 8B E3 41 5F 41 5E 41 5D 41 5C 5D C3").add(10);
ParseHeaderOrig = mem.add(1).rip().as<decltype(ParseHeaderOrig)>();
mem.set_call(ParseHeaderHook);
});
Loading

0 comments on commit b7a1bcd

Please sign in to comment.