diff --git a/src/client/component/fastfiles.cpp b/src/client/component/fastfiles.cpp index f6f34b19..0421f083 100644 --- a/src/client/component/fastfiles.cpp +++ b/src/client/component/fastfiles.cpp @@ -73,6 +73,56 @@ namespace fastfiles localized_strings::override(str, str); } } + + utils::hook::detour db_read_stream_file_hook; + void db_read_stream_file_stub(int a1, int a2) + { + // always use lz4 compressor type when reading stream files + *game::g_compressor = 4; + return db_read_stream_file_hook.invoke(a1, a2); + } + + bool exists(const std::string& zone) + { + const auto is_localized = game::DB_IsLocalized(zone.data()); + const auto db_fs = game::DB_FSInitialize(); + + auto handle = db_fs->vftbl->OpenFile(db_fs, + (is_localized ? game::SF_ZONE_LOC : game::SF_ZONE), utils::string::va("%s.ff", zone.data())); + const auto _0 = gsl::finally([&] + { + if (handle != nullptr) + { + db_fs->vftbl->Close(db_fs, handle); + } + + }); + + return handle != nullptr; + } + + void skip_extra_zones_stub(utils::hook::assembler& a) + { + const auto skip = a.newLabel(); + const auto original = a.newLabel(); + + a.pushad64(); + a.test(r15d, game::DB_ZONE_CUSTOM); // allocFlags + a.jnz(skip); + + a.bind(original); + a.popad64(); + a.mov(r8d, 9); + a.mov(rdx, 0x140933528); + a.jmp(0x140415E09); + + a.bind(skip); + a.popad64(); + a.mov(r14d, game::DB_ZONE_CUSTOM); + a.not_(r14d); + a.and_(r15d, r14d); + a.jmp(0x140415E29); + } } void enum_assets(const game::XAssetType type, const std::function& callback, const bool includeOverride) @@ -103,6 +153,16 @@ namespace fastfiles add_missing_localized_strings(); + // Allow loading of mixed compressor types + utils::hook::nop(0x1403E66A7, 2); + + // Fix compressor type on streamed file load + db_read_stream_file_hook.create(0x14041D710, db_read_stream_file_stub); + + // Don't load extra zones with loadzone + utils::hook::nop(0x140415DFC, 13); + utils::hook::jump(0x140415DFC, utils::hook::assemble(skip_extra_zones_stub), true); + command::add("loadzone", [](const command::params& params) { if (params.size() < 2) @@ -111,9 +171,17 @@ namespace fastfiles return; } + const auto name = params.get(1); + + if (!fastfiles::exists(name)) + { + console::warn("loadzone: zone \"%s\" could not be found!\n", name); + return; + } + game::XZoneInfo info{}; - info.name = params.get(1); - info.allocFlags = 1; + info.name = name; + info.allocFlags = game::DB_ZONE_GAME | game::DB_ZONE_CUSTOM; info.freeFlags = 0; game::DB_LoadXAssets(&info, 1u, game::DBSyncMode::DB_LOAD_SYNC); }); diff --git a/src/client/component/mapents.cpp b/src/client/component/mapents.cpp index c597e6e2..3954d4e8 100644 --- a/src/client/component/mapents.cpp +++ b/src/client/component/mapents.cpp @@ -276,6 +276,12 @@ namespace mapents scripting::token_map[name] = id; } + void add_field(const std::string& name, game::scriptType_e type, unsigned int id) + { + custom_fields[id] = type; + scripting::token_map[name] = id; + } + utils::hook::detour scr_find_field_hook; unsigned int scr_find_field_stub(unsigned int name, game::scriptType_e* type) { @@ -382,7 +388,7 @@ namespace mapents utils::hook::call(0x14058BD6B, should_load_addon_mapents); utils::hook::call(0x1406B3384, cm_trigger_model_bounds_stub); - add_field("script_specialops", game::SCRIPT_INTEGER); + add_field("script_specialops", game::SCRIPT_INTEGER, 0x20000); } }; } diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp index 7fb35b20..731b3455 100644 --- a/src/client/game/structs.hpp +++ b/src/client/game/structs.hpp @@ -995,6 +995,20 @@ namespace game DB_LOAD_SYNC_SKIP_ALWAYS_LOADED = 0x5, }; + enum DBAllocFlags : std::int32_t + { + DB_ZONE_NONE = 0x0, + DB_ZONE_COMMON = 0x1, + DB_ZONE_UI = 0x2, + DB_ZONE_GAME = 0x4, + DB_ZONE_LOAD = 0x8, + DB_ZONE_DEV = 0x10, + DB_ZONE_BASEMAP = 0x20, + DB_ZONE_TRANSIENT_POOL = 0x40, + DB_ZONE_TRANSIENT_MASK = 0x40, + DB_ZONE_CUSTOM = 0x1000 // added for custom zone loading + }; + struct XZoneInfo { const char* name; diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index d6c51ebf..b7dff7f6 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -44,7 +44,8 @@ namespace game WEAK symbol DB_GetRawFileLen{0x140413D80}; WEAK symbol DB_GetRawBuffer{0x140413C40}; WEAK symbol DB_LinkXAssetEntry1{0x140414900}; - + WEAK symbol DB_IsLocalized{0x1404141E0}; + WEAK symbol Dvar_FindVar{0x140618F90}; WEAK symbol Dvar_FindMalleableVar{0x140618F00}; WEAK symbol Dvar_GetCombinedString{0x1405A75D0}; @@ -192,6 +193,7 @@ namespace game WEAK symbol g_assetNames{0x140BEF280}; + WEAK symbol g_compressor{0x142065E80}; WEAK symbol g_poolSize{0x140BF2E40}; WEAK symbol g_entities{0x1452DDDA0};