iw4x-client/src/Components/Modules/FileSystem.cpp

385 lines
9.5 KiB
C++
Raw Normal View History

2022-02-27 07:53:44 -05:00
#include <STDInclude.hpp>
2017-01-19 16:23:59 -05:00
namespace Components
{
std::mutex FileSystem::Mutex;
std::recursive_mutex FileSystem::FSMutex;
Utils::Memory::Allocator FileSystem::MemAllocator;
void FileSystem::File::read(Game::FsThread thread)
2017-01-19 16:23:59 -05:00
{
2022-12-31 09:09:11 -05:00
std::lock_guard _(FSMutex);
2017-01-19 16:23:59 -05:00
assert(!filePath.empty());
int handle;
const auto len = Game::FS_FOpenFileReadForThread(filePath.data(), &handle, thread);
2017-01-19 16:23:59 -05:00
2022-08-11 06:31:19 -04:00
if (!handle)
2017-01-19 16:23:59 -05:00
{
2022-08-11 06:31:19 -04:00
return;
}
2022-08-11 06:31:19 -04:00
auto* buf = AllocateFile(len + 1);
2022-08-11 06:31:19 -04:00
[[maybe_unused]] auto bytesRead = Game::FS_Read(buf, len, handle);
2022-08-11 06:31:19 -04:00
assert(bytesRead == len);
2022-08-11 06:31:19 -04:00
buf[len] = '\0';
2022-08-11 06:31:19 -04:00
Game::FS_FCloseFile(handle);
this->buffer.append(buf, len);
FreeFile(buf);
2017-01-19 16:23:59 -05:00
}
void FileSystem::RawFile::read()
{
this->buffer.clear();
2022-12-31 09:09:11 -05:00
auto* rawfile = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_RAWFILE, this->filePath.data()).rawfile;
2017-01-19 16:23:59 -05:00
if (!rawfile || Game::DB_IsXAssetDefault(Game::XAssetType::ASSET_TYPE_RAWFILE, this->filePath.data())) return;
this->buffer.resize(Game::DB_GetRawFileLen(rawfile));
2022-04-16 04:01:25 -04:00
Game::DB_GetRawBuffer(rawfile, this->buffer.data(), static_cast<int>(this->buffer.size()));
2017-01-19 16:23:59 -05:00
}
2022-12-31 09:09:11 -05:00
FileSystem::FileReader::FileReader(std::string file) : handle(0), name(std::move(file))
2017-01-19 16:23:59 -05:00
{
this->size = Game::FS_FOpenFileReadCurrentThread(this->name.data(), &this->handle);
}
FileSystem::FileReader::~FileReader()
{
if (this->exists() && this->handle)
{
Game::FS_FCloseFile(this->handle);
}
}
2022-12-31 09:09:11 -05:00
bool FileSystem::FileReader::exists() const noexcept
2017-01-19 16:23:59 -05:00
{
return (this->size >= 0 && this->handle);
}
2022-12-31 09:09:11 -05:00
std::string FileSystem::FileReader::getName() const
2017-01-19 16:23:59 -05:00
{
return this->name;
}
2022-12-31 09:09:11 -05:00
int FileSystem::FileReader::getSize() const noexcept
2017-01-19 16:23:59 -05:00
{
return this->size;
}
2022-12-31 09:09:11 -05:00
std::string FileSystem::FileReader::getBuffer() const
2017-01-19 16:23:59 -05:00
{
Utils::Memory::Allocator allocator;
2022-12-31 09:09:11 -05:00
if (!this->exists()) return {};
2017-01-19 16:23:59 -05:00
const auto position = Game::FS_FTell(this->handle);
2018-05-09 08:33:52 -04:00
this->seek(0, Game::FS_SEEK_SET);
2017-01-19 16:23:59 -05:00
char* buffer = allocator.allocateArray<char>(this->size);
if (!this->read(buffer, this->size))
{
2018-05-09 08:33:52 -04:00
this->seek(position, Game::FS_SEEK_SET);
return {};
2017-01-19 16:23:59 -05:00
}
2018-05-09 08:33:52 -04:00
this->seek(position, Game::FS_SEEK_SET);
2017-01-19 16:23:59 -05:00
return {buffer, static_cast<std::size_t>(this->size)};
2017-01-19 16:23:59 -05:00
}
2022-12-31 09:09:11 -05:00
bool FileSystem::FileReader::read(void* buffer, std::size_t _size) const noexcept
2017-01-19 16:23:59 -05:00
{
2022-12-31 09:09:11 -05:00
if (!this->exists() || static_cast<std::size_t>(this->size) < _size || Game::FS_Read(buffer, static_cast<int>(_size), this->handle) != static_cast<int>(_size))
2017-01-19 16:23:59 -05:00
{
return false;
}
return true;
}
2022-12-31 09:09:11 -05:00
void FileSystem::FileReader::seek(int offset, int origin) const
2017-01-19 16:23:59 -05:00
{
if (this->exists())
{
Game::FS_Seek(this->handle, offset, origin);
}
}
2022-12-31 09:09:11 -05:00
void FileSystem::FileWriter::write(const std::string& data) const
2017-01-19 16:23:59 -05:00
{
if (this->handle)
{
2022-12-31 09:09:11 -05:00
Game::FS_Write(data.data(), static_cast<int>(data.size()), this->handle);
2017-01-19 16:23:59 -05:00
}
}
void FileSystem::FileWriter::open(bool append)
{
if (append)
{
this->handle = Game::FS_FOpenFileAppend(this->filePath.data());
}
else
{
this->handle = Game::FS_FOpenFileWrite(this->filePath.data());
}
}
void FileSystem::FileWriter::close()
{
if (this->handle)
{
Game::FS_FCloseFile(this->handle);
this->handle = 0;
}
}
std::filesystem::path FileSystem::GetAppdataPath()
{
PWSTR path;
if (!SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &path)))
{
throw std::runtime_error("Failed to read APPDATA path!");
}
auto _0 = gsl::finally([&path]
{
CoTaskMemFree(path);
});
return std::filesystem::path(path) / "xlabs";
}
2018-12-17 08:29:18 -05:00
std::vector<std::string> FileSystem::GetFileList(const std::string& path, const std::string& extension)
2017-01-19 16:23:59 -05:00
{
std::vector<std::string> fileList;
auto numFiles = 0;
const auto** files = Game::FS_ListFiles(path.data(), extension.data(), Game::FS_LIST_PURE_ONLY, &numFiles, 10);
2017-01-19 16:23:59 -05:00
if (files)
{
for (int i = 0; i < numFiles; ++i)
{
if (files[i])
{
fileList.emplace_back(files[i]);
2017-01-19 16:23:59 -05:00
}
}
Game::FS_FreeFileList(files, 10);
2017-01-19 16:23:59 -05:00
}
return fileList;
}
2018-12-17 08:29:18 -05:00
std::vector<std::string> FileSystem::GetSysFileList(const std::string& path, const std::string& extension, bool folders)
2017-01-19 16:23:59 -05:00
{
std::vector<std::string> fileList;
auto numFiles = 0;
const auto** files = Game::Sys_ListFiles(path.data(), extension.data(), nullptr, &numFiles, folders);
2017-01-19 16:23:59 -05:00
if (files)
{
for (int i = 0; i < numFiles; ++i)
{
if (files[i])
{
fileList.emplace_back(files[i]);
2017-01-19 16:23:59 -05:00
}
}
Game::Sys_FreeFileList(files);
}
return fileList;
}
2022-08-11 06:44:03 -04:00
bool FileSystem::_DeleteFile(const std::string& folder, const std::string& file)
2017-01-19 16:23:59 -05:00
{
2022-12-31 09:09:11 -05:00
char path[MAX_PATH]{};
2023-01-05 04:59:09 -05:00
Game::FS_BuildPathToFile((*Game::fs_basepath)->current.string, reinterpret_cast<char*>(0x63D0BB8), Utils::String::VA("%s/%s", folder.data(), file.data()), reinterpret_cast<char**>(&path));
return Game::FS_Remove(path);
2017-01-19 16:23:59 -05:00
}
int FileSystem::ReadFile(const char* path, char** buffer)
{
if (!buffer) return -1;
if (!path) return -1;
2022-12-31 09:09:11 -05:00
std::lock_guard _(Mutex);
FileReader reader(path);
2017-01-19 16:23:59 -05:00
int size = reader.getSize();
if (reader.exists() && size >= 0)
{
2022-12-31 09:09:11 -05:00
*buffer = AllocateFile(size + 1);
2017-01-19 16:23:59 -05:00
if (reader.read(*buffer, size)) return size;
2022-12-31 09:09:11 -05:00
FreeFile(*buffer);
2017-01-19 16:23:59 -05:00
*buffer = nullptr;
}
return -1;
}
char* FileSystem::AllocateFile(int size)
{
2022-12-31 09:09:11 -05:00
return MemAllocator.allocateArray<char>(size);
2017-01-19 16:23:59 -05:00
}
void FileSystem::FreeFile(void* buffer)
{
2022-12-31 09:09:11 -05:00
MemAllocator.free(buffer);
2017-01-19 16:23:59 -05:00
}
void FileSystem::RegisterFolder(const char* folder)
{
2023-01-05 04:59:09 -05:00
const std::string fs_cdpath = (*Game::fs_cdpath)->current.string;
const std::string fs_basepath = (*Game::fs_basepath)->current.string;
const std::string fs_homepath = (*Game::fs_homepath)->current.string;
2017-01-19 16:23:59 -05:00
if (!fs_cdpath.empty()) Game::FS_AddLocalizedGameDirectory(fs_cdpath.data(), folder);
if (!fs_basepath.empty()) Game::FS_AddLocalizedGameDirectory(fs_basepath.data(), folder);
if (!fs_homepath.empty()) Game::FS_AddLocalizedGameDirectory(fs_homepath.data(), folder);
}
void FileSystem::RegisterFolders()
{
if (ZoneBuilder::IsEnabled())
{
2022-12-31 09:09:11 -05:00
RegisterFolder("zonedata");
2017-01-19 16:23:59 -05:00
}
2022-12-31 09:09:11 -05:00
RegisterFolder("userraw");
2017-01-19 16:23:59 -05:00
}
__declspec(naked) void FileSystem::StartupStub()
{
__asm
{
pushad
push esi
call FileSystem::RegisterFolders
pop esi
popad
mov edx, ds:63D0CC0h
push 48264Dh
retn
}
}
int FileSystem::ExecIsFSStub(const char* execFilename)
{
2017-06-29 18:41:15 -04:00
return !File(execFilename).exists();
2017-01-19 16:23:59 -05:00
}
void FileSystem::FsStartupSync(const char* a1)
{
2022-12-31 09:09:11 -05:00
std::lock_guard _(FSMutex);
2017-01-19 16:23:59 -05:00
return Utils::Hook::Call<void(const char*)>(0x4823A0)(a1); // FS_Startup
}
2022-12-31 09:09:11 -05:00
void FileSystem::FsRestartSync(int localClientNum, int checksumFeed)
2017-01-19 16:23:59 -05:00
{
2022-12-31 09:09:11 -05:00
std::lock_guard _(FSMutex);
2019-12-26 07:24:48 -05:00
Maps::GetUserMap()->freeIwd();
2022-12-31 09:09:11 -05:00
Utils::Hook::Call<void(int, int)>(0x461A50)(localClientNum, checksumFeed); // FS_Restart
2017-04-06 16:22:30 -04:00
Maps::GetUserMap()->reloadIwd();
2017-01-19 16:23:59 -05:00
}
2022-12-31 09:09:11 -05:00
void FileSystem::FsShutdownSync(int closemfp)
2019-12-26 07:24:48 -05:00
{
2022-12-31 09:09:11 -05:00
std::lock_guard _(FSMutex);
2019-12-26 07:24:48 -05:00
Maps::GetUserMap()->freeIwd();
2022-12-31 09:09:11 -05:00
Utils::Hook::Call<void(int)>(0x4A46C0)(closemfp); // FS_Shutdown
2019-12-26 07:24:48 -05:00
}
2017-01-19 16:23:59 -05:00
void FileSystem::DelayLoadImagesSync()
{
2022-12-31 09:09:11 -05:00
std::lock_guard _(FSMutex);
2017-01-19 16:23:59 -05:00
return Utils::Hook::Call<void()>(0x494060)(); // DB_LoadDelayedImages
}
2022-12-31 09:09:11 -05:00
int FileSystem::LoadTextureSync(Game::GfxImageLoadDef** loadDef, Game::GfxImage* image)
2017-01-19 16:23:59 -05:00
{
2022-12-31 09:09:11 -05:00
std::lock_guard _(FSMutex);
2017-01-19 16:23:59 -05:00
return Game::Load_Texture(loadDef, image);
}
2017-04-06 16:22:30 -04:00
void FileSystem::IwdFreeStub(Game::iwd_t* iwd)
{
Maps::GetUserMap()->handlePackfile(iwd);
Utils::Hook::Call<void(void*)>(0x4291A0)(iwd);
}
const char* FileSystem::Sys_DefaultInstallPath_Hk()
{
static auto current_path = std::filesystem::current_path().string();
return current_path.data();
}
2017-01-19 16:23:59 -05:00
FileSystem::FileSystem()
{
// Thread safe file system interaction
2022-12-31 09:09:11 -05:00
Utils::Hook(0x4F4BFF, AllocateFile, HOOK_CALL).install()->quick();
Utils::Hook(Game::FS_FreeFile, FreeFile, HOOK_JUMP).install()->quick();
2017-01-19 16:23:59 -05:00
// Filesystem config checks
2023-01-01 06:47:50 -05:00
Utils::Hook(0x6098FD, ExecIsFSStub, HOOK_CALL).install()->quick();
2017-01-19 16:23:59 -05:00
// Don't strip the folders from the config name (otherwise our ExecIsFSStub fails)
Utils::Hook::Nop(0x6098F2, 5);
2017-01-19 16:23:59 -05:00
// Register additional folders
2022-12-31 09:09:11 -05:00
Utils::Hook(0x482647, StartupStub, HOOK_JUMP).install()->quick();
2017-01-19 16:23:59 -05:00
2022-12-31 09:09:11 -05:00
// exec whitelist removal
2017-01-19 16:23:59 -05:00
Utils::Hook::Nop(0x609685, 5);
Utils::Hook::Nop(0x60968C, 2);
// ignore 'no iwd files found in main'
Utils::Hook::Nop(0x642A4B, 5);
// Ignore bad magic, when trying to free hunk when it's already cleared
2022-12-31 09:09:11 -05:00
Utils::Hook::Set<std::uint16_t>(0x49AACE, 0xC35E);
2017-01-19 16:23:59 -05:00
// Synchronize filesystem starts
2022-12-31 09:09:11 -05:00
Utils::Hook(0x4290C6, FsStartupSync, HOOK_CALL).install()->quick(); // FS_InitFilesystem
Utils::Hook(0x461A88, FsStartupSync, HOOK_CALL).install()->quick(); // FS_Restart
2017-01-19 16:23:59 -05:00
// Synchronize filesystem restarts
2022-12-31 09:09:11 -05:00
Utils::Hook(0x4A745B, FsRestartSync, HOOK_CALL).install()->quick(); // SV_SpawnServer
Utils::Hook(0x4C8609, FsRestartSync, HOOK_CALL).install()->quick(); // FS_ConditionalRestart
Utils::Hook(0x5AC68E, FsRestartSync, HOOK_CALL).install()->quick(); // CL_ParseServerMessage
2017-01-19 16:23:59 -05:00
2019-12-26 07:24:48 -05:00
// Synchronize filesystem stops
2022-12-31 09:09:11 -05:00
Utils::Hook(0x461A55, FsShutdownSync, HOOK_CALL).install()->quick(); // FS_Restart
Utils::Hook(0x4D40DB, FsShutdownSync, HOOK_CALL).install()->quick(); // Com_Quitf
2019-12-26 07:24:48 -05:00
2017-01-19 16:23:59 -05:00
// Synchronize db image loading
2022-12-31 09:09:11 -05:00
Utils::Hook(0x415AB8, DelayLoadImagesSync, HOOK_CALL).install()->quick();
Utils::Hook(0x4D32BC, LoadTextureSync, HOOK_CALL).install()->quick();
2017-04-06 16:22:30 -04:00
// Handle IWD freeing
2022-12-31 09:09:11 -05:00
Utils::Hook(0x642F60, IwdFreeStub, HOOK_CALL).install()->quick();
// Set the working dir based on info from the Xlabs launcher
2022-12-31 09:09:11 -05:00
Utils::Hook(0x4326E0, Sys_DefaultInstallPath_Hk, HOOK_JUMP).install()->quick();
2017-01-19 16:23:59 -05:00
}
FileSystem::~FileSystem()
{
assert(FileSystem::MemAllocator.empty());
}
}