2016-07-11 11:14:58 -04:00
|
|
|
#include "STDInclude.hpp"
|
|
|
|
|
|
|
|
namespace Components
|
|
|
|
{
|
|
|
|
std::vector<std::string> FastFiles::ZonePaths;
|
|
|
|
|
|
|
|
// This has to be called only once, when the game starts
|
|
|
|
void FastFiles::LoadInitialZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync)
|
|
|
|
{
|
|
|
|
std::vector<Game::XZoneInfo> data;
|
|
|
|
Utils::Merge(&data, zoneInfo, zoneCount);
|
|
|
|
|
|
|
|
if (FastFiles::Exists("iw4x_patch_mp"))
|
|
|
|
{
|
|
|
|
data.push_back({ "iw4x_patch_mp", 1, 0 });
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load custom weapons, if present (force that later on)
|
|
|
|
if (FastFiles::Exists("iw4x_weapons_mp"))
|
|
|
|
{
|
|
|
|
data.push_back({ "iw4x_weapons_mp", 1, 0 });
|
|
|
|
}
|
|
|
|
|
|
|
|
return FastFiles::LoadDLCUIZones(data.data(), data.size(), sync);
|
|
|
|
}
|
|
|
|
|
|
|
|
// This has to be called every time the cgame is reinitialized
|
|
|
|
void FastFiles::LoadDLCUIZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync)
|
|
|
|
{
|
|
|
|
std::vector<Game::XZoneInfo> data;
|
|
|
|
Utils::Merge(&data, zoneInfo, zoneCount);
|
|
|
|
|
|
|
|
Game::XZoneInfo info = { nullptr, 2, 0 };
|
|
|
|
|
|
|
|
// Custom ui stuff
|
|
|
|
if (FastFiles::Exists("iw4x_ui_mp"))
|
|
|
|
{
|
|
|
|
info.name = "iw4x_ui_mp";
|
|
|
|
data.push_back(info);
|
|
|
|
}
|
|
|
|
else // Fallback
|
|
|
|
{
|
|
|
|
info.name = "dlc1_ui_mp";
|
|
|
|
data.push_back(info);
|
|
|
|
|
|
|
|
info.name = "dlc2_ui_mp";
|
|
|
|
data.push_back(info);
|
|
|
|
}
|
|
|
|
|
|
|
|
return FastFiles::LoadLocalizeZones(data.data(), data.size(), sync);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FastFiles::LoadGfxZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync)
|
|
|
|
{
|
|
|
|
std::vector<Game::XZoneInfo> data;
|
|
|
|
Utils::Merge(&data, zoneInfo, zoneCount);
|
|
|
|
|
|
|
|
if (FastFiles::Exists("iw4x_code_post_gfx_mp"))
|
|
|
|
{
|
|
|
|
data.push_back({ "iw4x_code_post_gfx_mp", zoneInfo->allocFlags, zoneInfo->freeFlags });
|
|
|
|
}
|
|
|
|
|
|
|
|
Game::DB_LoadXAssets(data.data(), data.size(), sync);
|
|
|
|
}
|
|
|
|
|
|
|
|
// This has to be called every time fastfiles are loaded :D
|
|
|
|
void FastFiles::LoadLocalizeZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync)
|
|
|
|
{
|
|
|
|
std::vector<Game::XZoneInfo> data;
|
|
|
|
Utils::Merge(&data, zoneInfo, zoneCount);
|
|
|
|
|
|
|
|
Game::XZoneInfo info = { nullptr, 4, 0 };
|
|
|
|
|
|
|
|
// Not sure how they should be loaded :S
|
|
|
|
std::string langZone = fmt::sprintf("iw4x_localized_%s", Game::Win_GetLanguage());
|
|
|
|
|
|
|
|
if (FastFiles::Exists(langZone))
|
|
|
|
{
|
|
|
|
info.name = langZone.data();
|
|
|
|
}
|
|
|
|
else if (FastFiles::Exists("iw4x_localized_english")) // Fallback
|
|
|
|
{
|
|
|
|
info.name = "iw4x_localized_english";
|
|
|
|
}
|
|
|
|
|
|
|
|
data.push_back(info);
|
|
|
|
|
|
|
|
Game::DB_LoadXAssets(data.data(), data.size(), sync);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Name is a bit weird, due to FasFileS and ExistS :P
|
|
|
|
bool FastFiles::Exists(std::string file)
|
|
|
|
{
|
|
|
|
std::string path = FastFiles::GetZoneLocation(file.data());
|
|
|
|
path.append(file);
|
|
|
|
|
|
|
|
if (!Utils::String::EndsWith(path.data(), ".ff"))
|
|
|
|
{
|
|
|
|
path.append(".ff");
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::ifstream(path.data()).good();
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* FastFiles::GetZoneLocation(const char* file)
|
|
|
|
{
|
|
|
|
const char* dir = Dvar::Var("fs_basepath").Get<const char*>();
|
|
|
|
|
|
|
|
for (auto &path : FastFiles::ZonePaths)
|
|
|
|
{
|
|
|
|
std::string absoluteFile = fmt::sprintf("%s\\%s%s", dir, path.data(), file);
|
|
|
|
|
|
|
|
// No ".ff" appended, append it manually
|
|
|
|
if (!Utils::String::EndsWith(absoluteFile, ".ff"))
|
|
|
|
{
|
|
|
|
absoluteFile.append(".ff");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if FastFile exists
|
|
|
|
if (Utils::IO::FileExists(absoluteFile))
|
|
|
|
{
|
|
|
|
return Utils::String::VA("%s", path.data());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Utils::String::VA("zone\\%s\\", Game::Win_GetLanguage());
|
|
|
|
}
|
|
|
|
|
|
|
|
void FastFiles::AddZonePath(std::string path)
|
|
|
|
{
|
|
|
|
FastFiles::ZonePaths.push_back(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string FastFiles::Current()
|
|
|
|
{
|
|
|
|
const char* file = (Utils::Hook::Get<char*>(0x112A680) + 4);
|
|
|
|
|
|
|
|
if (file == reinterpret_cast<char*>(4))
|
|
|
|
{
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
return file;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FastFiles::ReadVersionStub(unsigned int* version, int size)
|
|
|
|
{
|
|
|
|
Game::DB_ReadXFileUncompressed(version, size);
|
|
|
|
|
2016-09-18 09:10:23 -04:00
|
|
|
// Allow loading of custom version
|
2016-07-11 11:14:58 -04:00
|
|
|
if (*version == XFILE_VERSION_IW4X)
|
|
|
|
{
|
|
|
|
*version = XFILE_VERSION;
|
|
|
|
}
|
2016-09-18 09:10:23 -04:00
|
|
|
|
|
|
|
Zones::InstallPatches(*version);
|
2016-09-18 12:08:30 -04:00
|
|
|
|
|
|
|
// TODO: Ugly hotpatch, beautify that!
|
|
|
|
// if (*version >= 316)
|
|
|
|
// {
|
|
|
|
// *version = XFILE_VERSION;
|
|
|
|
// }
|
2016-07-11 11:14:58 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
FastFiles::FastFiles()
|
|
|
|
{
|
|
|
|
Dvar::Register<bool>("ui_zoneDebug", false, Game::dvar_flag::DVAR_FLAG_SAVED, "Display current loaded zone.");
|
|
|
|
|
|
|
|
// Redirect zone paths
|
|
|
|
Utils::Hook(0x44DA90, FastFiles::GetZoneLocation, HOOK_JUMP).Install()->Quick();
|
|
|
|
|
|
|
|
// Allow loading 'newer' zones
|
|
|
|
Utils::Hook(0x4158E7, FastFiles::ReadVersionStub, HOOK_CALL).Install()->Quick();
|
|
|
|
|
|
|
|
// Allow custom zone loading
|
|
|
|
if (!ZoneBuilder::IsEnabled())
|
|
|
|
{
|
|
|
|
Utils::Hook(0x506BC7, FastFiles::LoadInitialZones, HOOK_CALL).Install()->Quick();
|
|
|
|
Utils::Hook(0x60B4AC, FastFiles::LoadDLCUIZones, HOOK_CALL).Install()->Quick();
|
|
|
|
Utils::Hook(0x506B25, FastFiles::LoadGfxZones, HOOK_CALL).Install()->Quick();
|
|
|
|
}
|
|
|
|
|
|
|
|
// basic checks (hash jumps, both normal and playlist)
|
|
|
|
Utils::Hook::Nop(0x5B97A3, 2);
|
|
|
|
Utils::Hook::Nop(0x5BA493, 2);
|
|
|
|
|
|
|
|
Utils::Hook::Nop(0x5B991C, 2);
|
|
|
|
Utils::Hook::Nop(0x5BA60C, 2);
|
|
|
|
|
|
|
|
Utils::Hook::Nop(0x5B97B4, 2);
|
|
|
|
Utils::Hook::Nop(0x5BA4A4, 2);
|
|
|
|
|
|
|
|
// allow loading of IWffu (unsigned) files
|
|
|
|
Utils::Hook::Set<BYTE>(0x4158D9, 0xEB); // main function
|
|
|
|
Utils::Hook::Nop(0x4A1D97, 2); // DB_AuthLoad_InflateInit
|
|
|
|
|
|
|
|
// some other, unknown, check
|
|
|
|
Utils::Hook::Set<BYTE>(0x5B9912, 0xB8);
|
|
|
|
Utils::Hook::Set<DWORD>(0x5B9913, 1);
|
|
|
|
|
|
|
|
Utils::Hook::Set<BYTE>(0x5BA602, 0xB8);
|
|
|
|
Utils::Hook::Set<DWORD>(0x5BA603, 1);
|
|
|
|
|
|
|
|
// Add custom zone paths
|
|
|
|
FastFiles::AddZonePath("zone\\patch\\");
|
|
|
|
FastFiles::AddZonePath("zone\\dlc\\");
|
|
|
|
|
|
|
|
Renderer::OnFrame([] ()
|
|
|
|
{
|
|
|
|
if (FastFiles::Current().empty() || !Dvar::Var("ui_zoneDebug").Get<bool>()) return;
|
|
|
|
|
|
|
|
Game::Font* font = Game::R_RegisterFont("fonts/consoleFont"); // Inlining that seems to skip xpos, no idea why xD
|
|
|
|
float color[4] = { 1.0f, 1.0f, 1.0f, (Game::CL_IsCgameInitialized() ? 0.3f : 1.0f) };
|
|
|
|
Game::R_AddCmdDrawText(Utils::String::VA("Loading FastFile: %s", FastFiles::Current().data()), 0x7FFFFFFF, font, 5.0f, static_cast<float>(Renderer::Height() - 5), 1.0f, 1.0f, 0.0f, color, Game::ITEM_TEXTSTYLE_NORMAL);
|
|
|
|
});
|
|
|
|
|
|
|
|
Command::Add("loadzone", [] (Command::Params params)
|
|
|
|
{
|
|
|
|
if (params.Length() < 2) return;
|
|
|
|
|
|
|
|
Game::XZoneInfo info;
|
|
|
|
info.name = params[1];
|
|
|
|
info.allocFlags = 1;//0x01000000;
|
|
|
|
info.freeFlags = 0;
|
|
|
|
|
|
|
|
Game::DB_LoadXAssets(&info, 1, true);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
FastFiles::~FastFiles()
|
|
|
|
{
|
|
|
|
FastFiles::ZonePaths.clear();
|
|
|
|
}
|
|
|
|
}
|