From 2b0529ab7da2172108175ae7c0855801789da817 Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 1 Apr 2021 15:11:39 +0200 Subject: [PATCH 01/37] Make sure the code is considered c++17 because using msvc c++20 breaks existing code and no c++20 features are actually being used --- premake5.lua | 5 +---- src/STDInclude.hpp | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/premake5.lua b/premake5.lua index 28eafda6..ea000824 100644 --- a/premake5.lua +++ b/premake5.lua @@ -456,10 +456,7 @@ workspace "iw4x" --iw4mvm.project() workspace "*" - buildoptions { - "/std:c++latest" - } - systemversion "latest" + cppdialect "C++17" defines { "_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS" } rule "ProtobufCompiler" diff --git a/src/STDInclude.hpp b/src/STDInclude.hpp index 872e40c3..38a4a204 100644 --- a/src/STDInclude.hpp +++ b/src/STDInclude.hpp @@ -5,8 +5,8 @@ #ifndef RC_INVOKED -#define _HAS_CXX17 1 -#define _HAS_CXX20 1 +//#define _HAS_CXX17 1 +//#define _HAS_CXX20 1 #define VC_EXTRALEAN #define WIN32_LEAN_AND_MEAN #define _CRT_SECURE_NO_WARNINGS From bb979639d63bc11996fe62447b33aa9a1212cc0e Mon Sep 17 00:00:00 2001 From: rackover Date: Fri, 2 Apr 2021 16:13:51 +0200 Subject: [PATCH 02/37] Hook triggers --- src/Components/Modules/Maps.cpp | 26 ++++++++++++++++++++++++++ src/Components/Modules/Maps.hpp | 2 ++ 2 files changed, 28 insertions(+) diff --git a/src/Components/Modules/Maps.cpp b/src/Components/Modules/Maps.cpp index 44c3a2ec..0d98db04 100644 --- a/src/Components/Modules/Maps.cpp +++ b/src/Components/Modules/Maps.cpp @@ -739,6 +739,24 @@ namespace Components Utils::Hook::Call(0x408910)(ent, unk, unk2); } + + bool Maps::SV_SetTriggerModelHook(Game::gentity_s* ent) { + + //Logger::Print("SETTRIGGERMODELHOOK %s\n", ent->target); + std::string classname = Game::SL_ConvertToString(ent->script_classname); + std::string targetname = Game::SL_ConvertToString(ent->targetname); + //std::string test = ""; + //if (ent->padding_short) { + // test = Game::SL_ConvertToString(ent->padding_short); + //} + + return Utils::Hook::Call(0x5050C0)(ent); + } + + int16 Maps::CM_TriggerModelBounds(int modelPointer, Game::Bounds* bounds) { + Game::MapEnts* ents = *reinterpret_cast(0x1AA651C); + return Utils::Hook::Call(0x4416C0)(modelPointer, bounds); + } Maps::Maps() { @@ -778,6 +796,14 @@ namespace Components // disable turrets on CoD:OL 448+ maps for now Utils::Hook(0x5EE577, Maps::G_SpawnTurretHook, HOOK_CALL).install()->quick(); Utils::Hook(0x44A4D5, Maps::G_SpawnTurretHook, HOOK_CALL).install()->quick(); + + // Check trigger models + Utils::Hook(0x5FC0F1, Maps::SV_SetTriggerModelHook, HOOK_CALL).install()->quick(); + Utils::Hook(0x5FC2671, Maps::SV_SetTriggerModelHook, HOOK_CALL).install()->quick(); + + Utils::Hook(0x5050D4, Maps::CM_TriggerModelBounds, HOOK_CALL).install()->quick(); + + // //#define SORT_SMODELS #if !defined(DEBUG) || !defined(SORT_SMODELS) diff --git a/src/Components/Modules/Maps.hpp b/src/Components/Modules/Maps.hpp index cbc4a282..75f9df7a 100644 --- a/src/Components/Modules/Maps.hpp +++ b/src/Components/Modules/Maps.hpp @@ -120,5 +120,7 @@ namespace Components static void SetSpecularStub1(); static void SetSpecularStub2(); static void G_SpawnTurretHook(Game::gentity_s* ent, int unk, int unk2); + static bool SV_SetTriggerModelHook(Game::gentity_s* ent); + static int16 CM_TriggerModelBounds(int brushModelPointer, Game::Bounds* bounds); }; } From d5c8f15f7bea1108bfbb7b15e7b3277dd7adabad Mon Sep 17 00:00:00 2001 From: rackover Date: Fri, 2 Apr 2021 16:14:58 +0200 Subject: [PATCH 03/37] Enhance gentity_s struct --- src/Game/Structs.hpp | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index 28e82bfe..faa7f84d 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -4477,15 +4477,30 @@ namespace Game typedef struct gentity_s { int number; - unsigned char pad[308]; // 4 + unsigned char pad[139]; // 4 + unsigned int brushModel; + unsigned char pad3[165]; float origin[3]; // 312 float angles[3]; // 324 char pad2[8]; gclient_t* client; // 344 - unsigned char pad3[28]; + void /*Turret*/ *turret; + //Vehicle *vehicle; + int physObjId; + unsigned __int16 model; + char physicsObject; + char takedamage; + char active; + char handler; + char team; + bool freeAfterEvent; + __int16 padding_short; short classname; - short pad4; - unsigned char pad5[248]; + unsigned __int16 script_classname; + unsigned __int16 script_linkName; + unsigned __int16 target; + unsigned __int16 targetname; + unsigned char pad5[242]; } gentity_t; #pragma pack(push, 1) From ff8e3218363228f5e350ece4c74715c5770ed639 Mon Sep 17 00:00:00 2001 From: rackover Date: Fri, 2 Apr 2021 16:16:11 +0200 Subject: [PATCH 04/37] Fix soundalias building for zonebuilder --- .../AssetInterfaces/Isnd_alias_list_t.cpp | 158 ++++++++++++++---- 1 file changed, 128 insertions(+), 30 deletions(-) diff --git a/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp b/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp index 2bbc6a8e..8bae003d 100644 --- a/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp +++ b/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp @@ -35,6 +35,8 @@ namespace Assets std::string errors; json11::Json infoData = json11::Json::parse(aliasFile.getBuffer(), errors); + json11::Json head = infoData["head"][0]; + if (!infoData.is_object()) { @@ -45,45 +47,141 @@ namespace Assets Game::snd_alias_t* alias = aliasList->head; // try and parse everything and if it fails then fail for the whole file - auto type = infoData["type"]; - auto subtitle = infoData["subtitle"]; - auto secondaryAliasName = infoData["secondaryAliasName"]; - auto chainAliasName = infoData["chainAliasName"]; - auto soundFile = infoData["soundFile"]; - auto sequence = infoData["sequence"]; - auto volMin = infoData["volMin"]; - auto volMax = infoData["volMax"]; - auto pitchMin = infoData["pitchMin"]; - auto pitchMax = infoData["pitchMax"]; - auto distMin = infoData["distMin"]; - auto distMax = infoData["distMax"]; - auto flags = infoData["flags"]; - auto slavePercentage = infoData["slavePercentage"]; - auto probability = infoData["probability"]; - auto lfePercentage = infoData["lfePercentage"]; - auto centerPercentage = infoData["centerPercentage"]; - auto startDelay = infoData["startDelay"]; - auto volumeFalloffCurve = infoData["volumeFalloffCurve"]; - auto envelopMin = infoData["envelopMin"]; - auto envelopMax = infoData["envelopMax"]; - auto envelopPercentage = infoData["envelopPercentage"]; - auto speakerMap = infoData["speakerMap"]; + auto type = head["type"]; + auto subtitle = head["subtitle"]; + auto secondaryAliasName = head["secondaryAliasName"]; + auto chainAliasName = head["chainAliasName"]; + auto soundFile = head["soundFile"]; + auto sequence = head["sequence"]; + auto volMin = head["volMin"]; + auto volMax = head["volMax"]; + auto pitchMin = head["pitchMin"]; + auto pitchMax = head["pitchMax"]; + auto distMin = head["distMin"]; + auto distMax = head["distMax"]; + auto flags = head["flags"]; + auto slavePercentage = head["slavePercentage"]; + auto probability = head["probability"]; + auto lfePercentage = head["lfePercentage"]; + auto centerPercentage = head["centerPercentage"]; + auto startDelay = head["startDelay"]; + auto volumeFalloffCurve = head["volumeFalloffCurve"]; + auto envelopMin = head["envelopMin"]; + auto envelopMax = head["envelopMax"]; + auto envelopPercentage = head["envelopPercentage"]; + auto speakerMap = head["speakerMap"]; + + // Fix casing + if (soundFile.is_null()) { + soundFile = head["soundfile"]; + + Components::Logger::Print("Fixed casing on %s\n", name.data()); + } if (type.is_null() || soundFile.is_null()) { - Components::Logger::Error("Failed to parse sound %s! Each alias must have at least a type and a soundFile", name.data()); + //auto p = fopen("test", "w"); + //fwrite(aliasFile.getBuffer().data(), aliasFile.getBuffer().length(), 1, p); + //fflush(p); + //fclose(p); + + //auto p2 = fopen("test2", "w"); + //fwrite(infoData.dump().data(), infoData.dump().length(), 1, p2); + //fflush(p2); + //fclose(p2); + + Components::Logger::Print("Type is %s\n", type.dump().data()); + Components::Logger::Print("SoundFile is %s\n", soundFile.dump().data()); + Components::Logger::Error("Failed to parse sound %s! Each alias must have at least a type and a soundFile\n", name.data()); return; } #define CHECK(x, type) (x.is_##type##() || x.is_null()) // TODO: actually support all of those properties - if (CHECK(type, string) && CHECK(subtitle, string) && CHECK(secondaryAliasName, string) && CHECK(chainAliasName, string) && + if (!CHECK(type, string)) { + Components::Logger::Print("%s is not string but %d (%s)\n", "type", type.type(), type.dump().data()); + } + + if (!CHECK(subtitle, string)) { + Components::Logger::Print("%s is not string but %d (%s)\n", "subtitle", subtitle.type(), subtitle.dump().data()); + } + + if (!CHECK(secondaryAliasName, string)) { + Components::Logger::Print("%s is not string but %d (%s)\n", "secondaryAliasName", secondaryAliasName.type(), secondaryAliasName.dump().data()); + } + + if (!CHECK(chainAliasName, string)) { + Components::Logger::Print("%s is not string but %d (%s)\n", "chainAliasName", chainAliasName.type(), chainAliasName.dump().data()); + } + + if (!CHECK(soundFile, string)) { + Components::Logger::Print("%s is not string but %d (%s)\n", "soundFile", soundFile.type(), soundFile.dump().data()); + } + + if (!CHECK(sequence, number)) { + Components::Logger::Print("%s is not number but %d (%s)\n", "sequence", sequence.type(), sequence.dump().data()); + } + + if (!CHECK(volMin, number)) { + Components::Logger::Print("%s is not number but %d (%s)\n", "volMin", volMin.type(), volMin.dump().data()); + } + + if (!CHECK(volMax, number)) { + Components::Logger::Print("%s is not number but %d (%s)\n", "volMax", volMax.type(), volMax.dump().data()); + } + + if (!CHECK(pitchMin, number)) { + Components::Logger::Print("%s is not number but %d (%s)\n", "pitchMin", pitchMin.type(), pitchMin.dump().data()); + } + + if (!CHECK(pitchMax, number)) { + Components::Logger::Print("%s is not number but %d (%s)\n", "pitchMax", pitchMax.type(), pitchMax.dump().data()); + } + + if (!CHECK(probability, number)) { + Components::Logger::Print("%s is not number but %d (%s)\n", "probability", probability.type(), probability.dump().data()); + } + + if (!CHECK(lfePercentage, number)) { + Components::Logger::Print("%s is not number but %d (%s)\n", "lfePercentage", lfePercentage.type(), lfePercentage.dump().data()); + } + + if (!CHECK(centerPercentage, number)) { + Components::Logger::Print("%s is not number but %d (%s)\n", "centerPercentage", centerPercentage.type(), centerPercentage.dump().data()); + } + + if (!CHECK(startDelay, number)) { + Components::Logger::Print("%s is not number but %d (%s)\n", "startDelay", startDelay.type(), startDelay.dump().data()); + } + + if (!CHECK(volumeFalloffCurve, string)) { + Components::Logger::Print("%s is not string but %d (%s)\n", "volumeFalloffCurve", volumeFalloffCurve.type(), volumeFalloffCurve.dump().data()); + } + + if (!CHECK(envelopMin, number)) { + Components::Logger::Print("%s is not number but %d (%s)\n", "envelopMin", envelopMin.type(), envelopMin.dump().data()); + } + + if (!CHECK(envelopMax, number)) { + Components::Logger::Print("%s is not number but %d (%s)\n", "envelopMax", envelopMax.type(), envelopMax.dump().data()); + } + + if (!CHECK(envelopPercentage, number)) { + Components::Logger::Print("%s is not number but %d (%s)\n", "envelopPercentage", envelopPercentage.type(), envelopPercentage.dump().data()); + } + + if (!CHECK(speakerMap, object)) { + Components::Logger::Print("%s is not object but %d (%s)\n", "speakerMap", speakerMap.type(), speakerMap.dump().data()); + } + + + if (CHECK(type, number) && CHECK(subtitle, string) && CHECK(secondaryAliasName, string) && CHECK(chainAliasName, string) && CHECK(soundFile, string) && CHECK(sequence, number) && CHECK(volMin, number) && CHECK(volMax, number) && CHECK(pitchMin, number) && CHECK(pitchMax, number) && CHECK(distMin, number) && CHECK(distMax, number) && CHECK(flags, number) && CHECK(slavePercentage, number) && CHECK(probability, number) && CHECK(lfePercentage, number) && CHECK(centerPercentage, number) && CHECK(startDelay, number) && CHECK(volumeFalloffCurve, string) && CHECK(envelopMin, number) && CHECK(envelopMax, number) && CHECK(envelopPercentage, number) && - CHECK(speakerMap, string)) + CHECK(speakerMap, object)) { alias->soundFile->exists = true; @@ -123,12 +221,12 @@ namespace Assets alias->volumeFalloffCurve = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, volumeFalloffCurve.string_value(), builder).sndCurve; } - if (type.string_value() == "loaded"s) + if (type.number_value() == 1) // Loaded { alias->soundFile->type = Game::SAT_LOADED; alias->soundFile->u.loadSnd = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, soundFile.string_value(), builder).loadSnd; } - else if (type.string_value() == "streamed"s) + else if (type.number_value() == 2) // Streamed { alias->soundFile->type = Game::SAT_STREAMED; std::string streamedFile = soundFile.string_value(); @@ -138,12 +236,12 @@ namespace Assets } else { - Components::Logger::Error("Failed to parse sound %s! Invalid sound type %s", name.data(), type.string_value().c_str()); + Components::Logger::Error("Failed to parse sound %s! Invalid sound type %s\n", name.data(), type.string_value().c_str()); } } else { - Components::Logger::Error("Failed to parse sound %s!", name.data()); + Components::Logger::Error("Failed to parse sound %s!\n", name.data()); return; } From b21e57ccc276bf8ea437fdf051e58c6932736a04 Mon Sep 17 00:00:00 2001 From: Jan Date: Fri, 2 Apr 2021 15:23:18 +0200 Subject: [PATCH 05/37] Add r_drawSceneModelBoundingBoxes debugging dvar to draw dobj and scene model bounding boxes --- src/Components/Modules/QuickPatch.cpp | 35 ++++ src/Game/Functions.cpp | 92 +++++++++ src/Game/Functions.hpp | 5 + src/Game/Structs.hpp | 272 ++++++++++++++++++++++++++ 4 files changed, 404 insertions(+) diff --git a/src/Components/Modules/QuickPatch.cpp b/src/Components/Modules/QuickPatch.cpp index b79bcdd0..34143914 100644 --- a/src/Components/Modules/QuickPatch.cpp +++ b/src/Components/Modules/QuickPatch.cpp @@ -973,6 +973,41 @@ namespace Components } }); + Dvar::OnInit([] + { + Dvar::Register("r_drawSceneModelBoundingBoxes", false, Game::DVAR_FLAG_CHEAT, "Draw scene model bounding boxes"); + }); + + Scheduler::OnFrame([]() + { + if (!Game::CL_IsCgameInitialized() || !Dvar::Var("r_drawSceneModelBoundingBoxes").get()) return; + + float red[4] = { 1.0f, 0.0f, 0.0f, 1.0f }; + float blue[4] = { 0.0f, 0.0f, 1.0f, 1.0f }; + + auto* scene = Game::scene; + + for(auto i = 0; i < scene->sceneModelCount; i++) + { + if(!scene->sceneModel[i].model) + continue; + + auto b = scene->sceneModel[i].model->bounds; + b.midPoint[0] += scene->sceneModel[i].placement.base.origin[0]; + b.midPoint[1] += scene->sceneModel[i].placement.base.origin[1]; + b.midPoint[2] += scene->sceneModel[i].placement.base.origin[2]; + b.halfSize[0] *= scene->sceneModel[i].placement.scale; + b.halfSize[1] *= scene->sceneModel[i].placement.scale; + b.halfSize[2] *= scene->sceneModel[i].placement.scale; + Game::R_AddDebugBounds(red, &b, &scene->sceneModel[i].placement.base.quat); + } + + for(auto i = 0; i < scene->sceneDObjCount; i++) + { + Game::R_AddDebugBounds(blue, &scene->sceneDObj[i].cull.bounds); + } + }); + // Dvars Dvar::Register("ui_streamFriendly", false, Game::DVAR_FLAG_SAVED, "Stream friendly UI"); diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 63e01a18..14af16f0 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -419,6 +419,8 @@ namespace Game clientstate_t* clcState = reinterpret_cast(0xB2C540); + GfxScene* scene = reinterpret_cast(0x6944914); + XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize) { int elSize = DB_GetXAssetSizeHandlers[type](); @@ -728,6 +730,29 @@ namespace Game std::memmove(&solution[0], &res[0], sizeof(res)); } + void QuatRot(vec3_t* vec, const vec4_t* quat) + { + vec4_t q{ (*quat)[3],(*quat)[0],(*quat)[1],(*quat)[2] }; + + vec4_t res{ 0, (*vec)[0], (*vec)[1], (*vec)[2] }; + vec4_t res2; + vec4_t quat_conj{ q[0], -q[1], -q[2], -q[3] }; + QuatMultiply(&q, &res, &res2); + QuatMultiply(&res2, &quat_conj, &res); + + (*vec)[0] = res[1]; + (*vec)[1] = res[2]; + (*vec)[2] = res[3]; + } + + void QuatMultiply(const vec4_t* q1, const vec4_t* q2, vec4_t* res) + { + (*res)[0] = (*q2)[0] * (*q1)[0] - (*q2)[1] * (*q1)[1] - (*q2)[2] * (*q1)[2] - (*q2)[3] * (*q1)[3]; + (*res)[1] = (*q2)[0] * (*q1)[1] + (*q2)[1] * (*q1)[0] - (*q2)[2] * (*q1)[3] + (*q2)[3] * (*q1)[2]; + (*res)[2] = (*q2)[0] * (*q1)[2] + (*q2)[1] * (*q1)[3] + (*q2)[2] * (*q1)[0] - (*q2)[3] * (*q1)[1]; + (*res)[3] = (*q2)[0] * (*q1)[3] - (*q2)[1] * (*q1)[2] + (*q2)[2] * (*q1)[1] + (*q2)[3] * (*q1)[0]; + } + void SortWorldSurfaces(GfxWorld* world) { DWORD* specular1 = reinterpret_cast(0x69F105C); @@ -806,6 +831,73 @@ namespace Game Game::R_AddDebugLine(color, v4, v8); } + void R_AddDebugBounds(float* color, Bounds* b, const float(*quat)[4]) + { + vec3_t v[8]; + auto* center = b->midPoint; + auto* halfSize = b->halfSize; + + v[0][0] = -halfSize[0]; + v[0][1] = -halfSize[1]; + v[0][2] = -halfSize[2]; + + v[1][0] = halfSize[0]; + v[1][1] = -halfSize[1]; + v[1][2] = -halfSize[2]; + + v[2][0] = -halfSize[0]; + v[2][1] = halfSize[1]; + v[2][2] = -halfSize[2]; + + v[3][0] = halfSize[0]; + v[3][1] = halfSize[1]; + v[3][2] = -halfSize[2]; + + v[4][0] = -halfSize[0]; + v[4][1] = -halfSize[1]; + v[4][2] = halfSize[2]; + + v[5][0] = halfSize[0]; + v[5][1] = -halfSize[1]; + v[5][2] = halfSize[2]; + + v[6][0] = -halfSize[0]; + v[6][1] = halfSize[1]; + v[6][2] = halfSize[2]; + + v[7][0] = halfSize[0]; + v[7][1] = halfSize[1]; + v[7][2] = halfSize[2]; + + for(auto& vec : v) + { + QuatRot(&vec, quat); + vec[0] += center[0]; + vec[1] += center[1]; + vec[2] += center[2]; + } + + // bottom + Game::R_AddDebugLine(color, v[0], v[1]); + Game::R_AddDebugLine(color, v[1], v[3]); + Game::R_AddDebugLine(color, v[3], v[2]); + Game::R_AddDebugLine(color, v[2], v[0]); + + // top + Game::R_AddDebugLine(color, v[4], v[5]); + Game::R_AddDebugLine(color, v[5], v[7]); + Game::R_AddDebugLine(color, v[7], v[6]); + Game::R_AddDebugLine(color, v[6], v[4]); + + // verticals + Game::R_AddDebugLine(color, v[0], v[4]); + Game::R_AddDebugLine(color, v[1], v[5]); + Game::R_AddDebugLine(color, v[2], v[6]); + Game::R_AddDebugLine(color, v[3], v[7]); + } + + + #pragma optimize("", off) __declspec(naked) float UI_GetScoreboardLeft(void* /*a1*/) { diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index 8b125c82..53d5b1f6 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -858,6 +858,8 @@ namespace Game extern clientstate_t* clcState; + extern GfxScene* scene; + XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize); void Menu_FreeItemMemory(Game::itemDef_s* item); const char* TableLookup(StringTable* stringtable, int row, int column); @@ -906,9 +908,12 @@ namespace Game void Vec3Normalize(vec3_t& vec); void Vec2UnpackTexCoords(const PackedTexCoords in, vec2_t* out); void MatrixVecMultiply(const float(&mulMat)[3][3], const vec3_t& mulVec, vec3_t& solution); + void QuatRot(vec3_t* vec, const vec4_t* quat); + void QuatMultiply(const vec4_t* q1, const vec4_t* q2, vec4_t* res); void SortWorldSurfaces(GfxWorld* world); void R_AddDebugLine(float* color, float* v1, float* v2); void R_AddDebugString(float *color, float *pos, float scale, const char *str); void R_AddDebugBounds(float* color, Bounds* b); + void R_AddDebugBounds(float* color, Bounds* b, const float(*quat)[4]); } diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index faa7f84d..bb17bf79 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -4844,6 +4844,278 @@ namespace Game GfxCmdBufState *state; }; + struct GfxDrawGroupSetupFields + { + unsigned __int16 materialSortedIndex : 15; + unsigned __int16 useHeroLighting : 1; + char sceneLightIndex; + char surfType; + }; + + union GfxDrawGroupSetup + { + GfxDrawGroupSetupFields fields; + unsigned int packed; + }; + + struct GfxMarkSurfLightingFields + { + char lmapIndex; + char reflectionProbeIndex; + unsigned __int16 modelIndex; + }; + + union GfxMarkSurfLighting + { + GfxMarkSurfLightingFields fields; + unsigned int packed; + }; + + struct GfxMarkSurf + { + GfxDrawGroupSetup drawGroup; + unsigned __int16* indices; + unsigned __int16 triCount; + char modelType; + char pad; + GfxMarkSurfLighting lighting; + }; + + struct GfxCodeSurf + { + GfxDrawGroupSetup drawGroup; + unsigned int triCount; + unsigned __int16* indices; + unsigned __int16 argOffset; + unsigned __int16 argCount; + }; + + struct __declspec(align(4)) GfxGlassSurf + { + GfxDrawGroupSetup drawGroup; + char pad; + char reflectionProbeIndex; + unsigned __int16 triCount; + unsigned __int16* indices; + unsigned __int16 lightingHandle; + }; + + struct GfxCloudSurfFields + { + unsigned __int16 materialSortedIndex; + char cloudDataIndex; + char surfType; + }; + + union GfxCloudSurf + { + GfxCloudSurfFields fields; + unsigned int packed; + }; + + struct GfxSparkSurfFields + { + unsigned __int16 materialSortedIndex; + unsigned __int16 sparkDataIndex; + }; + + union GfxSparkSurf + { + GfxSparkSurfFields fields; + unsigned int packed; + }; + + struct GfxSceneDef + { + int time; + float floatTime; + float viewOffset[3]; + GfxImage* sunShadowImage; + float sunShadowPixelAdjust[4]; + }; + + struct GfxLight + { + char type; + char canUseShadowMap; + char unused[2]; + float color[3]; + float dir[3]; + float origin[3]; + float radius; + float cosHalfFovOuter; + float cosHalfFovInner; + int exponent; + unsigned int spotShadowIndex; + GfxLightDef* def; + }; + + struct GfxVisibleLight + { + char pad[0x2004]; + }; + + struct GfxEntity + { + unsigned int renderFxFlags; + float materialTime; + }; + + struct GfxSkinnedXModelSurfs + { + void* firstSurf; + }; + + struct GfxSceneEntityCull + { + volatile unsigned int state; + Bounds bounds; + GfxSkinnedXModelSurfs skinnedSurfs; + }; + + union GfxSceneEntityInfo + { + void/*cpose_t*/* pose; + unsigned __int16* cachedLightingHandle; + }; + + struct GfxSceneEntity + { + float lightingOrigin[3]; + GfxPlacement placement; + GfxSceneEntityCull cull; + char lods[32]; + unsigned __int32 gfxEntIndex : 7; + unsigned __int32 entnum : 12; + unsigned __int32 renderFxFlags : 13; + void/*DObj*/* obj; + GfxSceneEntityInfo info; + char reflectionProbeIndex; + }; + + struct GfxScaledPlacement + { + GfxPlacement base; + float scale; + }; + + struct GfxSceneModel + { + XModelDrawInfo info; + XModel* model; + void/*DObj*/* obj; + GfxScaledPlacement placement; + unsigned __int32 gfxEntIndex : 7; + unsigned __int32 entnum : 12; + unsigned __int32 renderFxFlags : 13; + float radius; + unsigned __int16* cachedLightingHandle; + float lightingOrigin[3]; + char reflectionProbeIndex; + char lod; + }; + + struct __declspec(align(4)) GfxSceneBrush + { + BModelDrawInfo info; + unsigned __int16 entnum; + GfxBrushModel* bmodel; + GfxPlacement placement; + char reflectionProbeIndex; + }; + + union GfxSceneGlass + { + struct + { + bool rendered; + char reflectionProbeIndex; + unsigned __int16 lightingHandle; + }; + unsigned int packed; + }; + + union GfxEntCellRefInfo + { + float radius; + GfxBrushModel* bmodel; + }; + + struct GfxSceneDpvs + { + unsigned int localClientNum; + char* entVisData[7]; + unsigned __int16* sceneXModelIndex; + unsigned __int16* sceneDObjIndex; + GfxEntCellRefInfo* entInfo[4]; + }; + + struct __declspec(align(64)) GfxScene + { + GfxCodeSurf codeEmissiveSurfs[2048]; + GfxCodeSurf codeTransSurfs[640]; + GfxMarkSurf markSurfs[1536]; + GfxGlassSurf glassSurfs[768]; + GfxCloudSurf cloudSurfs[256]; + GfxDrawSurf drawSurfsDepthHack[32]; + GfxDrawSurf drawSurfsLitOpaque[8192]; + GfxDrawSurf drawSurfsLitTrans[2048]; + GfxDrawSurf drawSurfsEmissive[8192]; + GfxDrawSurf drawSurfsSunShadow0[4096]; + GfxDrawSurf drawSurfsSunShadow1[8192]; + GfxDrawSurf drawSurfsSpotShadow0[896]; + GfxDrawSurf drawSurfsSpotShadow1[896]; + GfxDrawSurf drawSurfsSpotShadow2[896]; + GfxDrawSurf drawSurfsSpotShadow3[896]; + unsigned int sceneLightIsUsed[32]; + unsigned int cachedSceneLightIsUsed[4][32]; + GfxSparkSurf sparkSurfs[64]; + unsigned int drawSurfLimit[10]; + volatile int drawSurfCount[10]; + GfxDrawSurf* drawSurfs[10]; + volatile int codeSurfUser[2]; + volatile int markMeshGuard; + unsigned int codeEmissiveSurfCount; + unsigned int codeTransSurfCount; + unsigned int markSurfCount; + unsigned int glassSurfCount; + GfxSceneDef def; + unsigned int addedLightCount; + GfxLight addedLight[32]; + bool isAddedLightCulled[32]; + float dynamicSpotLightNearPlaneOffset; + float dynamicSpotLightLength; + GfxVisibleLight visLight[4]; + GfxVisibleLight visLightShadow[1]; + unsigned int* entOverflowedDrawBuf; + volatile int gfxEntCount; + GfxEntity gfxEnts[128]; + int sceneDObjCount; + int preClientSceneDObjCount; + int sceneDObjCountAtMark; + GfxSceneEntity sceneDObj[520]; + char sceneDObjVisData[7][512]; + int sceneDObjMarkableViewmodelIndex; + unsigned int sceneDObjFirstViewmodelIndex; + unsigned int sceneDObjViewmodelCount; + volatile int sceneModelCount; + int sceneModelCountAtMark; + int sceneDObjModelCount; + GfxSceneModel sceneModel[1024]; + char sceneModelVisData[7][1024]; + volatile int sceneBrushCount; + int sceneBrushCountAtMark; + GfxSceneBrush sceneBrush[512]; + char sceneBrushVisData[3][512]; + GfxSceneGlass sceneGlass[1024]; + unsigned int sceneDynModelCount; + unsigned int sceneDynBrushCount; + int gfxEntCountAtMark; + GfxSceneDpvs dpvs; + int updateSound; + int allowAddDObj; + }; + #pragma endregion #ifndef IDA From f4838994f8809bf88692499ababa996874712635 Mon Sep 17 00:00:00 2001 From: rackover Date: Sat, 3 Apr 2021 21:59:37 +0200 Subject: [PATCH 06/37] Small improvement to clipmap trigger generation code --- .../Modules/AssetInterfaces/IclipMap_t.cpp | 51 +++++++++++++++---- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp b/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp index 3904ea74..f47fe8d7 100644 --- a/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp +++ b/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp @@ -886,28 +886,61 @@ namespace Assets clipMap->mapEnts = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MAP_ENTS, Utils::String::VA("maps/mp/%s.d3dbsp", name.data()), builder).mapEnts; // add triggers to mapEnts - /* - std::list slabs; - std::list hulls; - std::list models; - for (int i = 0; i < clipMap->numCModels; ++i) + std::vector slabs; + std::vector hulls; + std::vector models; + + clipMap->mapEnts->trigger.count = clipMap->numSubModels; + clipMap->mapEnts->trigger.hullCount = clipMap->numSubModels; + clipMap->mapEnts->trigger.slabCount = 0; + + for (int i = 0; i < clipMap->numSubModels; ++i) { - Game::cLeafBrushNode_t* node = &clipMap->cLeafBrushNodes[clipMap->cModels[i].leaf.leafBrushNode]; + Game::TriggerHull trigHull = {}; + trigHull.bounds = clipMap->cmodels[i].bounds; + trigHull.contents = clipMap->cmodels[i].leaf.brushContents | clipMap->cmodels[i].leaf.terrainContents;; + + Game::TriggerModel trigMod = {}; + trigMod.hullCount = 1; + trigMod.firstHull = 0; + trigMod.contents = clipMap->cmodels[i].leaf.brushContents | clipMap->cmodels[i].leaf.terrainContents;; + + auto* node = &clipMap->leafbrushNodes[clipMap->cmodels[i].leaf.leafBrushNode]; + if (!node->leafBrushCount) continue; // skip empty brushes int baseHull = hulls.size(); for (int j = 0; j < node->leafBrushCount; ++j) { - Game::cbrush_t* brush = &clipMap->cBrushes[node->data.leaf.brushes[j]]; - int baseSlab = slabs.size(); + auto* brush = &clipMap->brushes[node->data.leaf.brushes[j]]; + + auto baseSlab = slabs.size(); for (int k = 0; k < brush->numsides; ++k) { Game::TriggerSlab curSlab; + curSlab.dir[0] = brush->sides[k].plane->normal[0]; + curSlab.dir[1] = brush->sides[k].plane->normal[1]; + curSlab.dir[2] = brush->sides[k].plane->normal[2]; + curSlab.halfSize = brush->sides[k].plane->dist; + curSlab.midPoint = 0.0f; // ?? + + slabs.emplace_back(curSlab); + clipMap->mapEnts->trigger.slabCount++; } + + trigHull.firstSlab = baseSlab; + trigHull.slabCount = slabs.size() - baseSlab; } + + models.emplace_back(trigMod); + hulls.emplace_back(trigHull); } - */ + + clipMap->mapEnts->trigger.models = &models[0]; + clipMap->mapEnts->trigger.hulls = &hulls[0]; + clipMap->mapEnts->trigger.slabs = &slabs[0]; + // This mustn't be null and has to have at least 1 'valid' entry. if (!clipMap->smodelNodeCount || !clipMap->smodelNodes) From a2832d606ccc331845a8371aab02be27d66143a1 Mon Sep 17 00:00:00 2001 From: rackover Date: Sat, 3 Apr 2021 22:00:16 +0200 Subject: [PATCH 07/37] Added r_drawSceneModelCollisions --- src/Components/Modules/QuickPatch.cpp | 41 +++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/Components/Modules/QuickPatch.cpp b/src/Components/Modules/QuickPatch.cpp index 34143914..3dd345a0 100644 --- a/src/Components/Modules/QuickPatch.cpp +++ b/src/Components/Modules/QuickPatch.cpp @@ -976,6 +976,7 @@ namespace Components Dvar::OnInit([] { Dvar::Register("r_drawSceneModelBoundingBoxes", false, Game::DVAR_FLAG_CHEAT, "Draw scene model bounding boxes"); + Dvar::Register("r_drawSceneModelCollisions", false, Game::DVAR_FLAG_CHEAT, "Draw scene model collisions"); }); Scheduler::OnFrame([]() @@ -1004,10 +1005,50 @@ namespace Components for(auto i = 0; i < scene->sceneDObjCount; i++) { + scene->sceneDObj[i].cull.bounds.halfSize[0] = std::abs(scene->sceneDObj[i].cull.bounds.halfSize[0]); + scene->sceneDObj[i].cull.bounds.halfSize[1] = std::abs(scene->sceneDObj[i].cull.bounds.halfSize[1]); + scene->sceneDObj[i].cull.bounds.halfSize[2] = std::abs(scene->sceneDObj[i].cull.bounds.halfSize[2]); + + if (scene->sceneDObj[i].cull.bounds.halfSize[0] < 0 || + scene->sceneDObj[i].cull.bounds.halfSize[1] < 0 || + scene->sceneDObj[i].cull.bounds.halfSize[2] < 0) { + + Components::Logger::Print("WARNING: Negative half size for DOBJ %s, this will cause culling issues!", scene->sceneDObj[i].obj->models[0]->name); + } + Game::R_AddDebugBounds(blue, &scene->sceneDObj[i].cull.bounds); } }); + Scheduler::OnFrame([]() + { + if (!Game::CL_IsCgameInitialized()) return; + if (!Dvar::Var("r_drawSceneModelCollisions").get()) return; + + float green[4] = { 0.0f, 1.0f, 0.0f, 1.0f }; + + auto* scene = Game::scene; + + for (auto i = 0; i < scene->sceneModelCount; i++) + { + if (!scene->sceneModel[i].model) + continue; + + for (auto j = 0; j < scene->sceneModel[i].model->numCollSurfs; j++) { + auto b = scene->sceneModel[i].model->collSurfs[j].bounds; + b.midPoint[0] += scene->sceneModel[i].placement.base.origin[0]; + b.midPoint[1] += scene->sceneModel[i].placement.base.origin[1]; + b.midPoint[2] += scene->sceneModel[i].placement.base.origin[2]; + b.halfSize[0] *= scene->sceneModel[i].placement.scale; + b.halfSize[1] *= scene->sceneModel[i].placement.scale; + b.halfSize[2] *= scene->sceneModel[i].placement.scale; + + Game::R_AddDebugBounds(green, &b, &scene->sceneModel[i].placement.base.quat); + } + } + }); + + // Dvars Dvar::Register("ui_streamFriendly", false, Game::DVAR_FLAG_SAVED, "Stream friendly UI"); From b083021bbc655e4cd097e61a285f067b6ecd8d38 Mon Sep 17 00:00:00 2001 From: rackover Date: Sat, 3 Apr 2021 22:00:29 +0200 Subject: [PATCH 08/37] Bump submodules --- deps/json11 | 2 +- deps/libtomcrypt | 2 +- deps/libtommath | 2 +- deps/protobuf | 2 +- deps/zlib | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/deps/json11 b/deps/json11 index 2df9473f..e2e3a11e 160000 --- a/deps/json11 +++ b/deps/json11 @@ -1 +1 @@ -Subproject commit 2df9473fb3605980db55ecddf34392a2e832ad35 +Subproject commit e2e3a11e99672b018e0e0657867e6a3439e180cf diff --git a/deps/libtomcrypt b/deps/libtomcrypt index 910d6252..1937f412 160000 --- a/deps/libtomcrypt +++ b/deps/libtomcrypt @@ -1 +1 @@ -Subproject commit 910d6252770f1e517d9ed02dc0549a1d61dfe159 +Subproject commit 1937f412605e1b04ddb41ef9c2f2f0aab7e61548 diff --git a/deps/libtommath b/deps/libtommath index eda0bd6a..6ac0b0c1 160000 --- a/deps/libtommath +++ b/deps/libtommath @@ -1 +1 @@ -Subproject commit eda0bd6ae5705ab90b866dfb52c5f15c23687f81 +Subproject commit 6ac0b0c1b69b9a88e1b3b3002c2e3a9062ae99b4 diff --git a/deps/protobuf b/deps/protobuf index df2bce34..63cfdafa 160000 --- a/deps/protobuf +++ b/deps/protobuf @@ -1 +1 @@ -Subproject commit df2bce345d4bc8cdc3eba2a866e11e79e1fff4df +Subproject commit 63cfdafacba6141717a2df97fc123dc0c14ba7c4 diff --git a/deps/zlib b/deps/zlib index 53ce2713..d71dc66f 160000 --- a/deps/zlib +++ b/deps/zlib @@ -1 +1 @@ -Subproject commit 53ce2713117ef2a8ed682d77b944df991c499252 +Subproject commit d71dc66fa8a153fb6e7c626847095d9697a6cf42 From 2301892d1e080d365f17f3fd1e76be60e681ba44 Mon Sep 17 00:00:00 2001 From: rackover Date: Sun, 4 Apr 2021 12:05:38 +0200 Subject: [PATCH 09/37] More detailed sampler error message --- src/Components/Modules/Renderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Components/Modules/Renderer.cpp b/src/Components/Modules/Renderer.cpp index c5379905..eae970ee 100644 --- a/src/Components/Modules/Renderer.cpp +++ b/src/Components/Modules/Renderer.cpp @@ -108,7 +108,7 @@ namespace Components void Renderer::R_TextureFromCodeError(const char* sampler, Game::GfxCmdBufState* state) { - Game::Com_Error(0, "Tried to use '%s' when it isn't valid for material '%s' and technique '%s'", + Game::Com_Error(0, "Tried to use sampler '%s' when it isn't valid for material '%s' and technique '%s'", sampler, state->material->info.name, state->technique->name); } From fe8571a0c6e741539aeb9eeca49936dfbb680e44 Mon Sep 17 00:00:00 2001 From: rackover Date: Sun, 4 Apr 2021 12:06:24 +0200 Subject: [PATCH 10/37] Enhanced material matching for iw3 maps --- .../Modules/AssetInterfaces/IMaterial.cpp | 45 ++++++++++++++++++- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/src/Components/Modules/AssetInterfaces/IMaterial.cpp b/src/Components/Modules/AssetInterfaces/IMaterial.cpp index f401eccb..a9e1b387 100644 --- a/src/Components/Modules/AssetInterfaces/IMaterial.cpp +++ b/src/Components/Modules/AssetInterfaces/IMaterial.cpp @@ -29,6 +29,12 @@ namespace Assets "_add_lin_nofog", }; + std::map techSetCorrespondance = { + {"effect_zfeather_outdoor", "effect_zfeather_blend"}, + {"effect", "effect_blend"}, + {"effect_nofog", "effect_blend_nofog"} + }; + Components::FileSystem::File materialFile(Utils::String::VA("materials/%s.iw4xMaterial", name.data())); if (!materialFile.exists()) return; @@ -185,12 +191,13 @@ namespace Assets if (!t1->techniques[i] && !t2->techniques[i]) continue;; if (!t1->techniques[i] || !t2->techniques[i]) return false; - if (t1->techniques[i]->flags != t1->techniques[i]->flags) return false; + if (t1->techniques[i]->flags != t2->techniques[i]->flags) return false; } return true; }; + Game::DB_EnumXAssetEntries(Game::XAssetType::ASSET_TYPE_MATERIAL, [asset, techsetMatches](Game::XAssetEntry* entry) { if (!replacementFound) @@ -209,8 +216,42 @@ namespace Assets if (!replacementFound && asset->techniqueSet) { - Components::Logger::Print("No replacement found for material %s with techset %s\n", asset->info.name, asset->techniqueSet->name); + std::string techName = asset->techniqueSet->name; + if (techSetCorrespondance.find(techName) != techSetCorrespondance.end()) { + auto iw4TechSetName = techSetCorrespondance[techName]; + Game::XAssetEntry* entry = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, iw4TechSetName.data()); + + if (entry) { + asset->techniqueSet = entry->asset.header.techniqueSet; + + Game::DB_EnumXAssetEntries(Game::XAssetType::ASSET_TYPE_MATERIAL, [asset](Game::XAssetEntry* entry) + { + if (!replacementFound) + { + Game::XAssetHeader header = entry->asset.header; + + if (header.material->techniqueSet == asset->techniqueSet) + { + Components::Logger::Print("Material %s with techset %s has been mapped (last chance!) to %s\n", asset->info.name, asset->techniqueSet->name, header.material->techniqueSet->name); + asset->info.sortKey = header.material->info.sortKey; + replacementFound = true; + } + } + }, false, false); + + if (!replacementFound) { + Components::Logger::Print("Could not find any loaded material with techset %s, so I cannot set the sortkey for material %s\n", asset->techniqueSet->name, asset->info.name); + } + } + else { + Components::Logger::Print("Could not find any loaded techset with iw4 name %s for iw3 techset %s\n", iw4TechSetName, asset->techniqueSet->name); + } + } + else { + Components::Logger::Print("Could not match iw3 techset %s with any of the techsets I know! This is a critical error, the map will not be playable.\n", asset->techniqueSet->name); + } + } if (!reader.end()) From 06301f5f85d4ec589705b96239afbe5b14484c8d Mon Sep 17 00:00:00 2001 From: rackover Date: Sun, 4 Apr 2021 12:06:45 +0200 Subject: [PATCH 11/37] DOBJ struct and better gentity_s --- src/Game/Structs.hpp | 46 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index bb17bf79..aed56399 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -4474,12 +4474,50 @@ namespace Game char pad3[724]; } gclient_t; + + /* 1571 */ + struct DSkelPartBits + { + int anim[6]; + int control[6]; + int worldCtrl[6]; + int skel[6]; + }; + + /* 1572 */ + struct DSkel + { + DSkelPartBits partBits; + int timeStamp; + /*DObjAnimMat*/void *mat; + }; + + /* 1573 */ + struct DObj + { + /*XAnimTree_s*/ void* tree; + unsigned __int16 duplicateParts; + unsigned __int16 entnum; + char duplicatePartsSize; + char numModels; + char numBones; + char flags; + unsigned int ignoreCollision; + volatile int locked; + DSkel skel; + float radius; + unsigned int hidePartBits[6]; + XModel** models; + }; + typedef struct gentity_s { int number; unsigned char pad[139]; // 4 - unsigned int brushModel; - unsigned char pad3[165]; + unsigned int brushModelIndex; + unsigned char pad3[135]; + int brushModelContents; + unsigned char pad4[26]; float origin[3]; // 312 float angles[3]; // 324 char pad2[8]; @@ -4988,7 +5026,7 @@ namespace Game unsigned __int32 gfxEntIndex : 7; unsigned __int32 entnum : 12; unsigned __int32 renderFxFlags : 13; - void/*DObj*/* obj; + DObj* obj; GfxSceneEntityInfo info; char reflectionProbeIndex; }; @@ -5003,7 +5041,7 @@ namespace Game { XModelDrawInfo info; XModel* model; - void/*DObj*/* obj; + DObj* obj; GfxScaledPlacement placement; unsigned __int32 gfxEntIndex : 7; unsigned __int32 entnum : 12; From e9f78eedb71438d3e455a9e738e377042b4a94ba Mon Sep 17 00:00:00 2001 From: Jan Date: Sun, 4 Apr 2021 00:56:15 +0200 Subject: [PATCH 12/37] Add r_drawTriggers debugging dvar to draw triggers on the map --- src/Components/Modules/QuickPatch.cpp | 55 ++++++++++ src/Components/Modules/Script.cpp | 2 +- src/Game/Structs.hpp | 139 +++++++++++++++++++++++--- 3 files changed, 182 insertions(+), 14 deletions(-) diff --git a/src/Components/Modules/QuickPatch.cpp b/src/Components/Modules/QuickPatch.cpp index 3dd345a0..dc54b0c0 100644 --- a/src/Components/Modules/QuickPatch.cpp +++ b/src/Components/Modules/QuickPatch.cpp @@ -1049,6 +1049,61 @@ namespace Components }); + Dvar::OnInit([] + { + Dvar::Register("r_drawTriggers", false, Game::DVAR_FLAG_CHEAT, "Draw triggers"); + }); + + Scheduler::OnFrame([]() + { + if (!Game::CL_IsCgameInitialized() || !Dvar::Var("r_drawTriggers").get()) return; + + float hurt[4] = { 1.0f, 0.0f, 0.0f, 1.0f }; + float hurtTouch[4] = { 0.75f, 0.0f, 0.0f, 1.0f }; + float damage[4] = { 0.0f, 0.0f, 1.0f, 1.0f }; + float once[4] = { 0.0f, 1.0f, 1.0f, 1.0f }; + float multiple[4] = { 0.0f, 1.0f, 0.0f, 1.0f }; + + auto* entities = Game::g_entities; + for(auto i = 0u; i < 0x800; i++) + { + auto* ent = &entities[i]; + + if(ent->r.isInUse) + { + auto b = ent->r.box; + b.midPoint[0] += ent->r.currentOrigin[0]; + b.midPoint[1] += ent->r.currentOrigin[1]; + b.midPoint[2] += ent->r.currentOrigin[2]; + + switch(ent->handler) + { + case Game::ENT_HANDLER_TRIGGER_HURT: + Game::R_AddDebugBounds(hurt, &b); + break; + + case Game::ENT_HANDLER_TRIGGER_HURT_TOUCH: + Game::R_AddDebugBounds(hurtTouch, &b); + break; + + case Game::ENT_HANDLER_TRIGGER_DAMAGE: + Game::R_AddDebugBounds(damage, &b); + break; + + case Game::ENT_HANDLER_TRIGGER_MULTIPLE: + if(ent->spawnflags & 0x40) + Game::R_AddDebugBounds(once, &b); + else + Game::R_AddDebugBounds(multiple, &b); + break; + + default: + break; + } + } + } + }); + // Dvars Dvar::Register("ui_streamFriendly", false, Game::DVAR_FLAG_SAVED, "Stream friendly UI"); diff --git a/src/Components/Modules/Script.cpp b/src/Components/Modules/Script.cpp index 51f0e394..fc199dc0 100644 --- a/src/Components/Modules/Script.cpp +++ b/src/Components/Modules/Script.cpp @@ -411,7 +411,7 @@ namespace Components { Logger::Error(5, "Entity: %i is not a client", gentity); } - return &Game::svs_clients[gentity->number]; + return &Game::svs_clients[gentity->s.number]; } void Script::AddFunctions() diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index aed56399..dca0b1fa 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -4474,7 +4474,6 @@ namespace Game char pad3[724]; } gclient_t; - /* 1571 */ struct DSkelPartBits { @@ -4510,20 +4509,114 @@ namespace Game XModel** models; }; - typedef struct gentity_s + + struct LerpEntityState + { + char pad[0x70]; + }; + + struct clientLinkInfo_t + { + __int16 parentId; + char tagName; + char flags; + }; + + struct entityState_s { int number; - unsigned char pad[139]; // 4 - unsigned int brushModelIndex; - unsigned char pad3[135]; - int brushModelContents; - unsigned char pad4[26]; - float origin[3]; // 312 - float angles[3]; // 324 - char pad2[8]; + int eType; + LerpEntityState lerp; + int time2; + int otherEntityNum; + int attackerEntityNum; + int groundEntityNum; + int loopSound; + int surfType; + + union + { + int brushModel; + int triggerModel; + int item; + int xmodel; + int primaryLight; + } index; + + int clientNum; + int iHeadIcon; + int iHeadIconTeam; + int solid; + unsigned int eventParm; + int eventSequence; + int events[4]; + unsigned int eventParms[4]; + unsigned __int16 weapon; + int legsAnim; + int torsoAnim; + int un1; + int un2; + clientLinkInfo_t clientLinkInfo; + unsigned int partBits[6]; + int clientMask[1]; + }; + + struct EntHandle + { + unsigned __int16 number; + unsigned __int16 infoIndex; + }; + + struct entityShared_t + { + char isLinked; + char modelType; + char svFlags; + char isInUse; + Bounds box; + int contents; + Bounds absBox; + float currentOrigin[3]; + float currentAngles[3]; + EntHandle ownerNum; + int eventTime; + }; + + enum EntHandler + { + ENT_HANDLER_NULL = 0x0, + ENT_HANDLER_TRIGGER_MULTIPLE = 0x1, + ENT_HANDLER_TRIGGER_HURT = 0x2, + ENT_HANDLER_TRIGGER_HURT_TOUCH = 0x3, + ENT_HANDLER_TRIGGER_DAMAGE = 0x4, + ENT_HANDLER_SCRIPT_MOVER = 0x5, + ENT_HANDLER_SCRIPT_MODEL = 0x6, + ENT_HANDLER_GRENADE = 0x7, + ENT_HANDLER_TIMED_OBJECT = 0x8, + ENT_HANDLER_ROCKET = 0x9, + ENT_HANDLER_CLIENT = 0xA, + ENT_HANDLER_CLIENT_SPECTATOR = 0xB, + ENT_HANDLER_CLIENT_DEAD = 0xC, + ENT_HANDLER_PLAYER_CLONE = 0xD, + ENT_HANDLER_TURRET_INIT = 0xE, + ENT_HANDLER_TURRET = 0xF, + ENT_HANDLER_DROPPED_ITEM = 0x10, + ENT_HANDLER_ITEM_INIT = 0x11, + ENT_HANDLER_ITEM = 0x12, + ENT_HANDLER_PRIMARY_LIGHT = 0x13, + ENT_HANDLER_PLAYER_BLOCK = 0x14, + ENT_HANDLER_VEHICLE = 0x15, + + ENT_HANDLER_COUNT + }; + + typedef struct gentity_s + { + entityState_s s; + entityShared_t r; gclient_t* client; // 344 - void /*Turret*/ *turret; - //Vehicle *vehicle; + void /*Turret*/* turret; + void /*Vehicle*/* vehicle; int physObjId; unsigned __int16 model; char physicsObject; @@ -4538,7 +4631,27 @@ namespace Game unsigned __int16 script_linkName; unsigned __int16 target; unsigned __int16 targetname; - unsigned char pad5[242]; + unsigned int attachIgnoreCollision; + int spawnflags; + int flags; + int eventTime; + int clipmask; + int processedFrame; + EntHandle parent; + int nextthink; + int health; + int maxHealth; + int damage; + int count; + EntHandle missileTargetEnt; + EntHandle remoteControlledOwner; + gentity_s* tagChildren; + unsigned __int16 attachModelNames[19]; + unsigned __int16 attachTagNames[19]; + int useCount; + gentity_s* nextFree; + int birthTime; + char pad[100]; } gentity_t; #pragma pack(push, 1) From f851a0a3a929b8eedf1e3b36c5104ae124978439 Mon Sep 17 00:00:00 2001 From: rackover Date: Sun, 4 Apr 2021 12:25:48 +0200 Subject: [PATCH 13/37] Put DOBJ back --- src/Game/Structs.hpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index dca0b1fa..effa3f6a 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -5130,6 +5130,38 @@ namespace Game unsigned __int16* cachedLightingHandle; }; + struct DSkelPartBits + { + int anim[6]; + int control[6]; + int worldCtrl[6]; + int skel[6]; + }; + + struct DSkel + { + DSkelPartBits partBits; + int timeStamp; + /*DObjAnimMat*/void* mat; + }; + + struct DObj + { + /*XAnimTree_s*/ void* tree; + unsigned __int16 duplicateParts; + unsigned __int16 entnum; + char duplicatePartsSize; + char numModels; + char numBones; + char flags; + unsigned int ignoreCollision; + volatile int locked; + DSkel skel; + float radius; + unsigned int hidePartBits[6]; + XModel** models; + }; + struct GfxSceneEntity { float lightingOrigin[3]; From d610e8517609d46cc9e71c1e77f5a6aaeafbbdb1 Mon Sep 17 00:00:00 2001 From: rackover Date: Sun, 4 Apr 2021 18:04:00 +0200 Subject: [PATCH 14/37] C-Compliant clipmap trigger conversion --- .../Modules/AssetInterfaces/IclipMap_t.cpp | 41 +++++++++++-------- src/Components/Modules/QuickPatch.cpp | 9 +++- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp b/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp index f47fe8d7..f804bf60 100644 --- a/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp +++ b/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp @@ -587,12 +587,12 @@ namespace Assets Game::clipMap_t* orgClipMap = nullptr; Game::DB_EnumXAssets(Game::XAssetType::ASSET_TYPE_CLIPMAP_MP, [](Game::XAssetHeader header, void* clipMap) - { - if (!*reinterpret_cast(clipMap)) { - *reinterpret_cast(clipMap) = header.clipMap; - } - }, &orgClipMap, false); + if (!*reinterpret_cast(clipMap)) + { + *reinterpret_cast(clipMap) = header.clipMap; + } + }, &orgClipMap, false); if (orgClipMap) std::memcpy(clipMap, orgClipMap, sizeof Game::clipMap_t); @@ -887,13 +887,18 @@ namespace Assets // add triggers to mapEnts - std::vector slabs; - std::vector hulls; - std::vector models; + clipMap->mapEnts->trigger.count = clipMap->numSubModels; clipMap->mapEnts->trigger.hullCount = clipMap->numSubModels; - clipMap->mapEnts->trigger.slabCount = 0; + clipMap->mapEnts->trigger.slabCount = clipMap->numSubModels * 1000; + + Game::TriggerHull* hulls = builder->getAllocator()->allocateArray(clipMap->mapEnts->trigger.hullCount * sizeof(Game::TriggerHull)); + Game::TriggerModel* models = builder->getAllocator()->allocateArray(clipMap->mapEnts->trigger.count * sizeof(Game::TriggerModel)); + Game::TriggerSlab* slabs = builder->getAllocator()->allocateArray(clipMap->mapEnts->trigger.slabCount * sizeof(Game::TriggerSlab)); + + int hullCountSoFar = 0; + int slabCountSoFar = 0; for (int i = 0; i < clipMap->numSubModels; ++i) { @@ -910,12 +915,12 @@ namespace Assets if (!node->leafBrushCount) continue; // skip empty brushes - int baseHull = hulls.size(); + int baseHull = hullCountSoFar; for (int j = 0; j < node->leafBrushCount; ++j) { auto* brush = &clipMap->brushes[node->data.leaf.brushes[j]]; - auto baseSlab = slabs.size(); + auto baseSlab = slabCountSoFar; for (int k = 0; k < brush->numsides; ++k) { Game::TriggerSlab curSlab; @@ -925,23 +930,23 @@ namespace Assets curSlab.halfSize = brush->sides[k].plane->dist; curSlab.midPoint = 0.0f; // ?? - slabs.emplace_back(curSlab); - clipMap->mapEnts->trigger.slabCount++; + slabs[slabCountSoFar] = curSlab; + slabCountSoFar++; } trigHull.firstSlab = baseSlab; - trigHull.slabCount = slabs.size() - baseSlab; + trigHull.slabCount = slabCountSoFar - baseSlab; } - models.emplace_back(trigMod); - hulls.emplace_back(trigHull); + models[i] = trigMod; + hulls[i] = trigHull; + hullCountSoFar++; } - clipMap->mapEnts->trigger.models = &models[0]; + clipMap->mapEnts->trigger.models = &models[0];; clipMap->mapEnts->trigger.hulls = &hulls[0]; clipMap->mapEnts->trigger.slabs = &slabs[0]; - // This mustn't be null and has to have at least 1 'valid' entry. if (!clipMap->smodelNodeCount || !clipMap->smodelNodes) { diff --git a/src/Components/Modules/QuickPatch.cpp b/src/Components/Modules/QuickPatch.cpp index dc54b0c0..daaaef9c 100644 --- a/src/Components/Modules/QuickPatch.cpp +++ b/src/Components/Modules/QuickPatch.cpp @@ -1071,7 +1071,7 @@ namespace Components if(ent->r.isInUse) { - auto b = ent->r.box; + Game::Bounds b = ent->r.box; b.midPoint[0] += ent->r.currentOrigin[0]; b.midPoint[1] += ent->r.currentOrigin[1]; b.midPoint[2] += ent->r.currentOrigin[2]; @@ -1098,6 +1098,13 @@ namespace Components break; default: + float rv = std::min((float)ent->handler, (float)5) / 5; + float gv = std::clamp((float)ent->handler-5, (float)0, (float)5) / 5; + float bv = std::clamp((float)ent->handler - 10, (float)0, (float)5) / 5; + + float color[4] = { rv, gv, bv, 1.0f }; + + Game::R_AddDebugBounds(color, &b); break; } } From 9b8682571396e4acdf38b04487e9b3346f5c3b96 Mon Sep 17 00:00:00 2001 From: rackover Date: Sun, 4 Apr 2021 18:04:28 +0200 Subject: [PATCH 15/37] whoopsie (need algorithm for clamp) --- src/STDInclude.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/STDInclude.hpp b/src/STDInclude.hpp index 38a4a204..3b8ead33 100644 --- a/src/STDInclude.hpp +++ b/src/STDInclude.hpp @@ -38,6 +38,7 @@ #include #include #include +#include // Experimental C++17 features #include From 529e44ac51c263a38dd8f7b70f78d31c90376ed1 Mon Sep 17 00:00:00 2001 From: rackover Date: Mon, 5 Apr 2021 14:49:30 +0200 Subject: [PATCH 16/37] Fixed clipmap trigger brushmodel conversion (correct indexing) --- .../Modules/AssetInterfaces/IclipMap_t.cpp | 56 ++++++++++--------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp b/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp index f804bf60..a7d9edb5 100644 --- a/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp +++ b/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp @@ -887,8 +887,6 @@ namespace Assets // add triggers to mapEnts - - clipMap->mapEnts->trigger.count = clipMap->numSubModels; clipMap->mapEnts->trigger.hullCount = clipMap->numSubModels; clipMap->mapEnts->trigger.slabCount = clipMap->numSubModels * 1000; @@ -899,6 +897,11 @@ namespace Assets int hullCountSoFar = 0; int slabCountSoFar = 0; + //int skipped = 0; + + Utils::Entities memEnts(clipMap->mapEnts->entityString); + + std::vector radioTriggers = memEnts.getRadioTriggerModels(); for (int i = 0; i < clipMap->numSubModels; ++i) { @@ -908,42 +911,45 @@ namespace Assets Game::TriggerModel trigMod = {}; trigMod.hullCount = 1; - trigMod.firstHull = 0; + trigMod.firstHull = i /*- skipped*/; trigMod.contents = clipMap->cmodels[i].leaf.brushContents | clipMap->cmodels[i].leaf.terrainContents;; auto* node = &clipMap->leafbrushNodes[clipMap->cmodels[i].leaf.leafBrushNode]; - if (!node->leafBrushCount) continue; // skip empty brushes + if (node->leafBrushCount) { + ////skipped++; + //continue; // skip empty brushes - int baseHull = hullCountSoFar; - for (int j = 0; j < node->leafBrushCount; ++j) - { - auto* brush = &clipMap->brushes[node->data.leaf.brushes[j]]; - - auto baseSlab = slabCountSoFar; - for (int k = 0; k < brush->numsides; ++k) + int baseHull = hullCountSoFar; + for (int j = 0; j < node->leafBrushCount; ++j) { - Game::TriggerSlab curSlab; - curSlab.dir[0] = brush->sides[k].plane->normal[0]; - curSlab.dir[1] = brush->sides[k].plane->normal[1]; - curSlab.dir[2] = brush->sides[k].plane->normal[2]; - curSlab.halfSize = brush->sides[k].plane->dist; - curSlab.midPoint = 0.0f; // ?? + auto* brush = &clipMap->brushes[node->data.leaf.brushes[j]]; - slabs[slabCountSoFar] = curSlab; - slabCountSoFar++; + auto baseSlab = slabCountSoFar; + for (int k = 0; k < brush->numsides; ++k) + { + Game::TriggerSlab curSlab; + curSlab.dir[0] = brush->sides[k].plane->normal[0]; + curSlab.dir[1] = brush->sides[k].plane->normal[1]; + curSlab.dir[2] = brush->sides[k].plane->normal[2]; + curSlab.halfSize = brush->sides[k].plane->dist; + curSlab.midPoint = 0.0f; // ?? + + slabs[slabCountSoFar] = curSlab; + slabCountSoFar++; + } + + trigHull.firstSlab = baseSlab; + trigHull.slabCount = slabCountSoFar - baseSlab; } - - trigHull.firstSlab = baseSlab; - trigHull.slabCount = slabCountSoFar - baseSlab; } - models[i] = trigMod; - hulls[i] = trigHull; + models[i/* - skipped*/] = trigMod; + hulls[i/* - skipped*/] = trigHull; hullCountSoFar++; } - clipMap->mapEnts->trigger.models = &models[0];; + clipMap->mapEnts->trigger.models = &models[0]; clipMap->mapEnts->trigger.hulls = &hulls[0]; clipMap->mapEnts->trigger.slabs = &slabs[0]; From 22d63bf3b60e26bbed91d5136f4482055454a4a2 Mon Sep 17 00:00:00 2001 From: rackover Date: Mon, 5 Apr 2021 16:25:07 +0200 Subject: [PATCH 17/37] Group dvars to draw triggers/trees --- src/Components/Modules/QuickPatch.cpp | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/Components/Modules/QuickPatch.cpp b/src/Components/Modules/QuickPatch.cpp index daaaef9c..3c847941 100644 --- a/src/Components/Modules/QuickPatch.cpp +++ b/src/Components/Modules/QuickPatch.cpp @@ -946,11 +946,6 @@ namespace Components } }); - Dvar::OnInit([] - { - Dvar::Register("r_drawAabbTrees", false, Game::DVAR_FLAG_USERCREATED, "Draw aabb trees"); - }); - Scheduler::OnFrame([]() { if (!Game::CL_IsCgameInitialized() || !Dvar::Var("r_drawAabbTrees").get()) return; @@ -977,6 +972,8 @@ namespace Components { Dvar::Register("r_drawSceneModelBoundingBoxes", false, Game::DVAR_FLAG_CHEAT, "Draw scene model bounding boxes"); Dvar::Register("r_drawSceneModelCollisions", false, Game::DVAR_FLAG_CHEAT, "Draw scene model collisions"); + Dvar::Register("r_drawTriggers", false, Game::DVAR_FLAG_CHEAT, "Draw triggers"); + Dvar::Register("r_drawAabbTrees", false, Game::DVAR_FLAG_USERCREATED, "Draw aabb trees"); }); Scheduler::OnFrame([]() @@ -1048,12 +1045,6 @@ namespace Components } }); - - Dvar::OnInit([] - { - Dvar::Register("r_drawTriggers", false, Game::DVAR_FLAG_CHEAT, "Draw triggers"); - }); - Scheduler::OnFrame([]() { if (!Game::CL_IsCgameInitialized() || !Dvar::Var("r_drawTriggers").get()) return; From 953dcd205735e0f31d7cfea3aa39c6cfdd2186fb Mon Sep 17 00:00:00 2001 From: rackover Date: Mon, 5 Apr 2021 16:25:44 +0200 Subject: [PATCH 18/37] Material error formatting using const char* instead of string --- src/Components/Modules/AssetInterfaces/IMaterial.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Components/Modules/AssetInterfaces/IMaterial.cpp b/src/Components/Modules/AssetInterfaces/IMaterial.cpp index a9e1b387..4228e485 100644 --- a/src/Components/Modules/AssetInterfaces/IMaterial.cpp +++ b/src/Components/Modules/AssetInterfaces/IMaterial.cpp @@ -245,7 +245,7 @@ namespace Assets } } else { - Components::Logger::Print("Could not find any loaded techset with iw4 name %s for iw3 techset %s\n", iw4TechSetName, asset->techniqueSet->name); + Components::Logger::Print("Could not find any loaded techset with iw4 name %s for iw3 techset %s\n", iw4TechSetName.data(), asset->techniqueSet->name); } } else { From fe2e2f1b3c24c764719219e4d49584c0e6948f76 Mon Sep 17 00:00:00 2001 From: rackover Date: Mon, 5 Apr 2021 16:26:15 +0200 Subject: [PATCH 19/37] Read clipmap from clipmap file (already converted) instead of converting it on the fly --- .../Modules/AssetInterfaces/IclipMap_t.cpp | 87 +++++-------------- 1 file changed, 24 insertions(+), 63 deletions(-) diff --git a/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp b/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp index a7d9edb5..0a2cbeb3 100644 --- a/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp +++ b/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp @@ -1,6 +1,6 @@ #include "StdInclude.hpp" -#define IW4X_CLIPMAP_VERSION 1 +#define IW4X_CLIPMAP_VERSION 2 namespace Assets { @@ -605,7 +605,7 @@ namespace Assets } int version = reader.read(); - if (version != IW4X_CLIPMAP_VERSION) + if (version > IW4X_CLIPMAP_VERSION) { Components::Logger::Error(0, "Reading clipmap '%s' failed, expected version is %d, but it was %d!", name.data(), IW4X_CLIPMAP_VERSION, version); } @@ -881,77 +881,38 @@ namespace Assets clipMap->smodelNodeCount = reader.read(); clipMap->smodelNodes = reader.readArray(clipMap->smodelNodeCount); - clipMap->checksum = reader.read(); - clipMap->mapEnts = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MAP_ENTS, Utils::String::VA("maps/mp/%s.d3dbsp", name.data()), builder).mapEnts; // add triggers to mapEnts + if (version >= 2) { + clipMap->mapEnts->trigger.count = clipMap->numSubModels; + clipMap->mapEnts->trigger.hullCount = clipMap->numSubModels; - clipMap->mapEnts->trigger.count = clipMap->numSubModels; - clipMap->mapEnts->trigger.hullCount = clipMap->numSubModels; - clipMap->mapEnts->trigger.slabCount = clipMap->numSubModels * 1000; + Game::TriggerHull* hulls = builder->getAllocator()->allocateArray(clipMap->mapEnts->trigger.hullCount); + Game::TriggerModel* models = builder->getAllocator()->allocateArray(clipMap->mapEnts->trigger.count); - Game::TriggerHull* hulls = builder->getAllocator()->allocateArray(clipMap->mapEnts->trigger.hullCount * sizeof(Game::TriggerHull)); - Game::TriggerModel* models = builder->getAllocator()->allocateArray(clipMap->mapEnts->trigger.count * sizeof(Game::TriggerModel)); - Game::TriggerSlab* slabs = builder->getAllocator()->allocateArray(clipMap->mapEnts->trigger.slabCount * sizeof(Game::TriggerSlab)); + int slabCountSoFar = 0; - int hullCountSoFar = 0; - int slabCountSoFar = 0; - //int skipped = 0; - - Utils::Entities memEnts(clipMap->mapEnts->entityString); - - std::vector radioTriggers = memEnts.getRadioTriggerModels(); - - for (int i = 0; i < clipMap->numSubModels; ++i) - { - Game::TriggerHull trigHull = {}; - trigHull.bounds = clipMap->cmodels[i].bounds; - trigHull.contents = clipMap->cmodels[i].leaf.brushContents | clipMap->cmodels[i].leaf.terrainContents;; - - Game::TriggerModel trigMod = {}; - trigMod.hullCount = 1; - trigMod.firstHull = i /*- skipped*/; - trigMod.contents = clipMap->cmodels[i].leaf.brushContents | clipMap->cmodels[i].leaf.terrainContents;; - - auto* node = &clipMap->leafbrushNodes[clipMap->cmodels[i].leaf.leafBrushNode]; - - if (node->leafBrushCount) { - ////skipped++; - //continue; // skip empty brushes - - int baseHull = hullCountSoFar; - for (int j = 0; j < node->leafBrushCount; ++j) - { - auto* brush = &clipMap->brushes[node->data.leaf.brushes[j]]; - - auto baseSlab = slabCountSoFar; - for (int k = 0; k < brush->numsides; ++k) - { - Game::TriggerSlab curSlab; - curSlab.dir[0] = brush->sides[k].plane->normal[0]; - curSlab.dir[1] = brush->sides[k].plane->normal[1]; - curSlab.dir[2] = brush->sides[k].plane->normal[2]; - curSlab.halfSize = brush->sides[k].plane->dist; - curSlab.midPoint = 0.0f; // ?? - - slabs[slabCountSoFar] = curSlab; - slabCountSoFar++; - } - - trigHull.firstSlab = baseSlab; - trigHull.slabCount = slabCountSoFar - baseSlab; - } + for (int i = 0; i < clipMap->numSubModels; ++i) + { + models[i] = reader.read(); + hulls[i] = reader.read(); } - models[i/* - skipped*/] = trigMod; - hulls[i/* - skipped*/] = trigHull; - hullCountSoFar++; + size_t slabCount = reader.read(); + clipMap->mapEnts->trigger.slabCount = slabCount; + Game::TriggerSlab* slabs = builder->getAllocator()->allocateArray(clipMap->mapEnts->trigger.slabCount); + for (int i = 0; i < clipMap->mapEnts->trigger.slabCount; i++) { + slabs[i] = reader.read(); + } + + + clipMap->mapEnts->trigger.models = &models[0]; + clipMap->mapEnts->trigger.hulls = &hulls[0]; + clipMap->mapEnts->trigger.slabs = &slabs[0]; } - clipMap->mapEnts->trigger.models = &models[0]; - clipMap->mapEnts->trigger.hulls = &hulls[0]; - clipMap->mapEnts->trigger.slabs = &slabs[0]; + clipMap->checksum = reader.read(); // This mustn't be null and has to have at least 1 'valid' entry. if (!clipMap->smodelNodeCount || !clipMap->smodelNodes) From aa9f98b0e74cdc502c47d4fa628b7340c39324fc Mon Sep 17 00:00:00 2001 From: rackover Date: Mon, 5 Apr 2021 18:13:00 +0200 Subject: [PATCH 20/37] Fix warnings, fix post-rebase issues --- .../Modules/AssetInterfaces/IclipMap_t.cpp | 6 ++-- src/Components/Modules/Maps.cpp | 15 ++++---- src/Game/Structs.hpp | 36 ------------------- 3 files changed, 8 insertions(+), 49 deletions(-) diff --git a/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp b/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp index 0a2cbeb3..08e1e03f 100644 --- a/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp +++ b/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp @@ -891,9 +891,7 @@ namespace Assets Game::TriggerHull* hulls = builder->getAllocator()->allocateArray(clipMap->mapEnts->trigger.hullCount); Game::TriggerModel* models = builder->getAllocator()->allocateArray(clipMap->mapEnts->trigger.count); - int slabCountSoFar = 0; - - for (int i = 0; i < clipMap->numSubModels; ++i) + for (unsigned int i = 0; i < clipMap->numSubModels; ++i) { models[i] = reader.read(); hulls[i] = reader.read(); @@ -902,7 +900,7 @@ namespace Assets size_t slabCount = reader.read(); clipMap->mapEnts->trigger.slabCount = slabCount; Game::TriggerSlab* slabs = builder->getAllocator()->allocateArray(clipMap->mapEnts->trigger.slabCount); - for (int i = 0; i < clipMap->mapEnts->trigger.slabCount; i++) { + for (unsigned int i = 0; i < clipMap->mapEnts->trigger.slabCount; i++) { slabs[i] = reader.read(); } diff --git a/src/Components/Modules/Maps.cpp b/src/Components/Modules/Maps.cpp index 0d98db04..6b9b8ffb 100644 --- a/src/Components/Modules/Maps.cpp +++ b/src/Components/Modules/Maps.cpp @@ -742,19 +742,15 @@ namespace Components bool Maps::SV_SetTriggerModelHook(Game::gentity_s* ent) { - //Logger::Print("SETTRIGGERMODELHOOK %s\n", ent->target); - std::string classname = Game::SL_ConvertToString(ent->script_classname); - std::string targetname = Game::SL_ConvertToString(ent->targetname); - //std::string test = ""; - //if (ent->padding_short) { - // test = Game::SL_ConvertToString(ent->padding_short); - //} + // Use me for debugging + //std::string classname = Game::SL_ConvertToString(ent->script_classname); + //std::string targetname = Game::SL_ConvertToString(ent->targetname); return Utils::Hook::Call(0x5050C0)(ent); } int16 Maps::CM_TriggerModelBounds(int modelPointer, Game::Bounds* bounds) { - Game::MapEnts* ents = *reinterpret_cast(0x1AA651C); + //Game::MapEnts* ents = *reinterpret_cast(0x1AA651C); // Use me for debugging return Utils::Hook::Call(0x4416C0)(modelPointer, bounds); } @@ -797,11 +793,12 @@ namespace Components Utils::Hook(0x5EE577, Maps::G_SpawnTurretHook, HOOK_CALL).install()->quick(); Utils::Hook(0x44A4D5, Maps::G_SpawnTurretHook, HOOK_CALL).install()->quick(); +#ifdef DEBUG // Check trigger models Utils::Hook(0x5FC0F1, Maps::SV_SetTriggerModelHook, HOOK_CALL).install()->quick(); Utils::Hook(0x5FC2671, Maps::SV_SetTriggerModelHook, HOOK_CALL).install()->quick(); - Utils::Hook(0x5050D4, Maps::CM_TriggerModelBounds, HOOK_CALL).install()->quick(); +#endif // diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index effa3f6a..b7bfe710 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -4474,42 +4474,6 @@ namespace Game char pad3[724]; } gclient_t; - /* 1571 */ - struct DSkelPartBits - { - int anim[6]; - int control[6]; - int worldCtrl[6]; - int skel[6]; - }; - - /* 1572 */ - struct DSkel - { - DSkelPartBits partBits; - int timeStamp; - /*DObjAnimMat*/void *mat; - }; - - /* 1573 */ - struct DObj - { - /*XAnimTree_s*/ void* tree; - unsigned __int16 duplicateParts; - unsigned __int16 entnum; - char duplicatePartsSize; - char numModels; - char numBones; - char flags; - unsigned int ignoreCollision; - volatile int locked; - DSkel skel; - float radius; - unsigned int hidePartBits[6]; - XModel** models; - }; - - struct LerpEntityState { char pad[0x70]; From aa85b957403305ad30252b842543ad3ef11c1fe8 Mon Sep 17 00:00:00 2001 From: rackover Date: Mon, 5 Apr 2021 23:18:59 +0200 Subject: [PATCH 21/37] Added more techset matching --- .../Modules/AssetInterfaces/IMaterial.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Components/Modules/AssetInterfaces/IMaterial.cpp b/src/Components/Modules/AssetInterfaces/IMaterial.cpp index 4228e485..ccd12e4b 100644 --- a/src/Components/Modules/AssetInterfaces/IMaterial.cpp +++ b/src/Components/Modules/AssetInterfaces/IMaterial.cpp @@ -32,7 +32,22 @@ namespace Assets std::map techSetCorrespondance = { {"effect_zfeather_outdoor", "effect_zfeather_blend"}, {"effect", "effect_blend"}, - {"effect_nofog", "effect_blend_nofog"} + {"effect_nofog", "effect_blend_nofog"}, + {"wc_unlit_add", "wc_unlit_add_lin"}, + {"wc_unlit_multiply", "wc_unlit_multiply_lin"}, + {"wc_unlit_falloff_add", "wc_unlit_falloff_add_lin_ua"}, + {"mc_unlit_replace", "mc_unlit_replace_lin"}, + {"mc_unlit_nofog", "mc_unlit_blend_nofog_ua"}, + {"wc_unlit", "wc_unlit_add"}, + {"wc_unlit_multiply_lin", "wc_unlit_multiply_lin"}, + {"wc_unlit_blend", "wc_unlit_blend_lin"}, + {"mc_unlit", "mc_unlit_replace_lin"}/*, + {"", ""}, + {"", ""}, + {"", ""}, + {"", ""}, + {"", ""}, + {"", ""},*/ }; Components::FileSystem::File materialFile(Utils::String::VA("materials/%s.iw4xMaterial", name.data())); From ace08107fe68f12a04a61095ca03b0fa27716b92 Mon Sep 17 00:00:00 2001 From: rackover Date: Mon, 5 Apr 2021 23:19:40 +0200 Subject: [PATCH 22/37] Only read clipmap models if we have a clipmap model count --- .../Modules/AssetInterfaces/IclipMap_t.cpp | 42 ++++++++++--------- src/Components/Modules/ZoneBuilder.cpp | 8 +++- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp b/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp index 08e1e03f..6ccc39b0 100644 --- a/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp +++ b/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp @@ -885,29 +885,31 @@ namespace Assets // add triggers to mapEnts if (version >= 2) { - clipMap->mapEnts->trigger.count = clipMap->numSubModels; - clipMap->mapEnts->trigger.hullCount = clipMap->numSubModels; + if (clipMap->numSubModels > 0) { + clipMap->mapEnts->trigger.count = clipMap->numSubModels; + clipMap->mapEnts->trigger.hullCount = clipMap->numSubModels; - Game::TriggerHull* hulls = builder->getAllocator()->allocateArray(clipMap->mapEnts->trigger.hullCount); - Game::TriggerModel* models = builder->getAllocator()->allocateArray(clipMap->mapEnts->trigger.count); + Game::TriggerHull* hulls = builder->getAllocator()->allocateArray(clipMap->mapEnts->trigger.hullCount); + Game::TriggerModel* models = builder->getAllocator()->allocateArray(clipMap->mapEnts->trigger.count); - for (unsigned int i = 0; i < clipMap->numSubModels; ++i) - { - models[i] = reader.read(); - hulls[i] = reader.read(); + for (unsigned int i = 0; i < clipMap->numSubModels; ++i) + { + models[i] = reader.read(); + hulls[i] = reader.read(); + } + + size_t slabCount = reader.read(); + clipMap->mapEnts->trigger.slabCount = slabCount; + Game::TriggerSlab* slabs = builder->getAllocator()->allocateArray(clipMap->mapEnts->trigger.slabCount); + for (unsigned int i = 0; i < clipMap->mapEnts->trigger.slabCount; i++) { + slabs[i] = reader.read(); + } + + + clipMap->mapEnts->trigger.models = &models[0]; + clipMap->mapEnts->trigger.hulls = &hulls[0]; + clipMap->mapEnts->trigger.slabs = &slabs[0]; } - - size_t slabCount = reader.read(); - clipMap->mapEnts->trigger.slabCount = slabCount; - Game::TriggerSlab* slabs = builder->getAllocator()->allocateArray(clipMap->mapEnts->trigger.slabCount); - for (unsigned int i = 0; i < clipMap->mapEnts->trigger.slabCount; i++) { - slabs[i] = reader.read(); - } - - - clipMap->mapEnts->trigger.models = &models[0]; - clipMap->mapEnts->trigger.hulls = &hulls[0]; - clipMap->mapEnts->trigger.slabs = &slabs[0]; } clipMap->checksum = reader.read(); diff --git a/src/Components/Modules/ZoneBuilder.cpp b/src/Components/Modules/ZoneBuilder.cpp index 82477459..1c8329a6 100644 --- a/src/Components/Modules/ZoneBuilder.cpp +++ b/src/Components/Modules/ZoneBuilder.cpp @@ -222,8 +222,14 @@ namespace Components } Game::XAssetHeader assetHeader = AssetHandler::FindAssetForZone(type, name, this, isSubAsset); + + if (type == Game::XAssetType::ASSET_TYPE_LOADED_SOUND) { + Logger::Print("Loading loaded_sound '%s'\n", name.data()); + } + if (!assetHeader.data) - { Logger::Error("Error: Missing asset '%s' of type '%s'\n", name.data(), Game::DB_GetXAssetTypeName(type)); + { + Logger::Error("Error: Missing asset '%s' of type '%s'\n", name.data(), Game::DB_GetXAssetTypeName(type)); return false; } From a2c8705868f8e3f1f8c70539e0d25ebed7bf0621 Mon Sep 17 00:00:00 2001 From: rackover Date: Tue, 6 Apr 2021 11:33:14 +0200 Subject: [PATCH 23/37] Do not overwrite a techniqueset with another, the game really doesn't like taht --- src/Components/Modules/AssetInterfaces/IMaterial.cpp | 8 +++----- src/Components/Modules/Maps.cpp | 4 +++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Components/Modules/AssetInterfaces/IMaterial.cpp b/src/Components/Modules/AssetInterfaces/IMaterial.cpp index ccd12e4b..17e95947 100644 --- a/src/Components/Modules/AssetInterfaces/IMaterial.cpp +++ b/src/Components/Modules/AssetInterfaces/IMaterial.cpp @@ -206,7 +206,8 @@ namespace Assets if (!t1->techniques[i] && !t2->techniques[i]) continue;; if (!t1->techniques[i] || !t2->techniques[i]) return false; - if (t1->techniques[i]->flags != t2->techniques[i]->flags) return false; + // Apparently, this is really not that important + //if (t1->techniques[i]->flags != t2->techniques[i]->flags) return false; } return true; @@ -238,8 +239,6 @@ namespace Assets Game::XAssetEntry* entry = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, iw4TechSetName.data()); if (entry) { - asset->techniqueSet = entry->asset.header.techniqueSet; - Game::DB_EnumXAssetEntries(Game::XAssetType::ASSET_TYPE_MATERIAL, [asset](Game::XAssetEntry* entry) { if (!replacementFound) @@ -264,9 +263,8 @@ namespace Assets } } else { - Components::Logger::Print("Could not match iw3 techset %s with any of the techsets I know! This is a critical error, the map will not be playable.\n", asset->techniqueSet->name); + Components::Logger::Print("Could not match iw3 techset %s with any of the techsets I know! This is a critical error, there's a good chance the map will not be playable.\n", asset->techniqueSet->name); } - } if (!reader.end()) diff --git a/src/Components/Modules/Maps.cpp b/src/Components/Modules/Maps.cpp index 6b9b8ffb..daaac1bc 100644 --- a/src/Components/Modules/Maps.cpp +++ b/src/Components/Modules/Maps.cpp @@ -750,7 +750,9 @@ namespace Components } int16 Maps::CM_TriggerModelBounds(int modelPointer, Game::Bounds* bounds) { - //Game::MapEnts* ents = *reinterpret_cast(0x1AA651C); // Use me for debugging +#ifdef DEBUG + Game::MapEnts* ents = *reinterpret_cast(0x1AA651C); // Use me for debugging +#endif return Utils::Hook::Call(0x4416C0)(modelPointer, bounds); } From d6394f0c69ec2e581640f0b6d0574255ca447692 Mon Sep 17 00:00:00 2001 From: rackover Date: Tue, 6 Apr 2021 21:10:47 +0200 Subject: [PATCH 24/37] weapon zonebuilding fix? --- .../Modules/AssetInterfaces/IWeapon.cpp | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/Components/Modules/AssetInterfaces/IWeapon.cpp b/src/Components/Modules/AssetInterfaces/IWeapon.cpp index 92135700..c325e8e7 100644 --- a/src/Components/Modules/AssetInterfaces/IWeapon.cpp +++ b/src/Components/Modules/AssetInterfaces/IWeapon.cpp @@ -274,9 +274,9 @@ namespace Assets { buffer->align(Utils::Stream::ALIGN_4); int* ptrs = buffer->dest(); - buffer->saveMax(37 * sizeof(Game::snd_alias_list_t*)); + buffer->saveMax(31 * sizeof(Game::snd_alias_list_t*)); - for (int i = 0; i < 37; i++) + for (int i = 0; i < 31; i++) { if (!def->bounceSound[i]) { @@ -433,14 +433,16 @@ namespace Assets if (def->projExplosionSound) { - buffer->saveMax(4); + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveMax(sizeof(Game::snd_alias_list_t*)); buffer->saveString(def->projExplosionSound->aliasName); Utils::Stream::ClearPointer(&dest->projExplosionSound); } if (def->projDudSound) { - buffer->saveMax(4); + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveMax(sizeof(Game::snd_alias_list_t*)); buffer->saveString(def->projDudSound->aliasName); Utils::Stream::ClearPointer(&dest->projDudSound); } @@ -476,7 +478,8 @@ namespace Assets if (def->projIgnitionSound) { - buffer->saveMax(4); + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveMax(sizeof(Game::snd_alias_list_t*)); buffer->saveString(def->projIgnitionSound->aliasName); Utils::Stream::ClearPointer(&dest->projIgnitionSound); } @@ -551,7 +554,8 @@ namespace Assets if (def->turretOverheatSound) { - buffer->saveMax(4); + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveMax(sizeof(Game::snd_alias_list_t*)); buffer->saveString(def->turretOverheatSound->aliasName); Utils::Stream::ClearPointer(&dest->turretOverheatSound); } @@ -569,7 +573,8 @@ namespace Assets if (def->turretBarrelSpinMaxSnd) { - buffer->saveMax(4); + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveMax(sizeof(Game::snd_alias_list_t*)); buffer->saveString(def->turretBarrelSpinMaxSnd->aliasName); Utils::Stream::ClearPointer(&dest->turretBarrelSpinMaxSnd); } @@ -577,7 +582,8 @@ namespace Assets for (int i = 0; i < 4; i++) { if (!def->turretBarrelSpinUpSnd[i]) continue; - buffer->saveMax(4); + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveMax(sizeof(Game::snd_alias_list_t*)); buffer->saveString(def->turretBarrelSpinUpSnd[i]->aliasName); Utils::Stream::ClearPointer(&dest->turretBarrelSpinUpSnd[i]); } @@ -585,21 +591,24 @@ namespace Assets for (int i = 0; i < 4; i++) { if (!def->turretBarrelSpinDownSnd[i]) continue; - buffer->saveMax(4); + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveMax(sizeof(Game::snd_alias_list_t*)); buffer->saveString(def->turretBarrelSpinDownSnd[i]->aliasName); Utils::Stream::ClearPointer(&dest->turretBarrelSpinDownSnd[i]); } if (def->missileConeSoundAlias) { - buffer->saveMax(4); + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveMax(sizeof(Game::snd_alias_list_t*)); buffer->saveString(def->missileConeSoundAlias->aliasName); Utils::Stream::ClearPointer(&dest->missileConeSoundAlias); } if (def->missileConeSoundAliasAtBase) { - buffer->saveMax(4); + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveMax(sizeof(Game::snd_alias_list_t*)); buffer->saveString(def->missileConeSoundAliasAtBase->aliasName); Utils::Stream::ClearPointer(&dest->missileConeSoundAliasAtBase); } From 4db236c85293e0cc5feee80e67679e2358dba1a6 Mon Sep 17 00:00:00 2001 From: rackover Date: Thu, 8 Apr 2021 12:50:47 +0200 Subject: [PATCH 25/37] Additional techset matching --- src/Components/Modules/AssetInterfaces/IMaterial.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Components/Modules/AssetInterfaces/IMaterial.cpp b/src/Components/Modules/AssetInterfaces/IMaterial.cpp index 17e95947..e9b9d86e 100644 --- a/src/Components/Modules/AssetInterfaces/IMaterial.cpp +++ b/src/Components/Modules/AssetInterfaces/IMaterial.cpp @@ -41,8 +41,8 @@ namespace Assets {"wc_unlit", "wc_unlit_add"}, {"wc_unlit_multiply_lin", "wc_unlit_multiply_lin"}, {"wc_unlit_blend", "wc_unlit_blend_lin"}, - {"mc_unlit", "mc_unlit_replace_lin"}/*, - {"", ""}, + {"mc_unlit", "mc_unlit_replace_lin"}, + {"effect_zfeather", "effect_zfeather_blend"}/*, {"", ""}, {"", ""}, {"", ""}, From e19409f550c0d3a817884e13b04dc122a20936fc Mon Sep 17 00:00:00 2001 From: rackover Date: Thu, 8 Apr 2021 13:01:19 +0200 Subject: [PATCH 26/37] Zonebuilder builds sounds & loaded_sounds now --- .../Modules/AssetInterfaces/ILoadedSound.cpp | 2 +- .../AssetInterfaces/Isnd_alias_list_t.cpp | 476 ++++++++++-------- src/Components/Modules/ZoneBuilder.cpp | 4 - src/Game/Structs.hpp | 4 +- 4 files changed, 277 insertions(+), 209 deletions(-) diff --git a/src/Components/Modules/AssetInterfaces/ILoadedSound.cpp b/src/Components/Modules/AssetInterfaces/ILoadedSound.cpp index f2ddb8b0..376a21d9 100644 --- a/src/Components/Modules/AssetInterfaces/ILoadedSound.cpp +++ b/src/Components/Modules/AssetInterfaces/ILoadedSound.cpp @@ -4,7 +4,7 @@ namespace Assets { void ILoadedSound::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - Components::FileSystem::File soundFile(Utils::String::VA("sounds/%s", name.data())); + Components::FileSystem::File soundFile(Utils::String::VA("loaded_sound/%s", name.data())); if (!soundFile.exists()) { header->loadSnd = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).loadSnd; diff --git a/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp b/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp index 8bae003d..881355b4 100644 --- a/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp +++ b/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp @@ -4,10 +4,10 @@ namespace Assets { void Isnd_alias_list_t::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { - Components::FileSystem::File aliasFile(Utils::String::VA("sounds/%s", name.data())); + Components::FileSystem::File aliasFile(Utils::String::VA("sounds/%s", name.c_str())); if (!aliasFile.exists()) { - header->sound = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).sound; + header->sound = Components::AssetHandler::FindOriginalAsset(this->getType(), name.c_str()).sound; return; } @@ -18,232 +18,304 @@ namespace Assets return; } - aliasList->head = builder->getAllocator()->allocate(); - if (!aliasList->head) - { - Components::Logger::Print("Error allocating memory for sound alias structure!\n"); - return; - } - aliasList->head->soundFile = builder->getAllocator()->allocate(); - if (!aliasList->head->soundFile) - { - Components::Logger::Print("Error allocating memory for sound alias structure!\n"); - return; - } - - aliasList->count = 1; - std::string errors; json11::Json infoData = json11::Json::parse(aliasFile.getBuffer(), errors); - json11::Json head = infoData["head"][0]; + json11::Json aliasesContainer = infoData["head"]; + auto aliases = aliasesContainer.array_items(); - if (!infoData.is_object()) + aliasList->count = aliases.size(); + aliasList->aliasName = builder->getAllocator()->duplicateString(infoData["aliasName"].string_value().c_str()); + + for (size_t i = 0; i < aliasList->count; i++) { - Components::Logger::Error("Failed to load sound %s!", name.data()); - return; - } + json11::Json head = aliasesContainer[i]; - Game::snd_alias_t* alias = aliasList->head; + if (!infoData.is_object()) + { + Components::Logger::Error("Failed to load sound %s!", name.c_str()); + return; + } - // try and parse everything and if it fails then fail for the whole file - auto type = head["type"]; - auto subtitle = head["subtitle"]; - auto secondaryAliasName = head["secondaryAliasName"]; - auto chainAliasName = head["chainAliasName"]; - auto soundFile = head["soundFile"]; - auto sequence = head["sequence"]; - auto volMin = head["volMin"]; - auto volMax = head["volMax"]; - auto pitchMin = head["pitchMin"]; - auto pitchMax = head["pitchMax"]; - auto distMin = head["distMin"]; - auto distMax = head["distMax"]; - auto flags = head["flags"]; - auto slavePercentage = head["slavePercentage"]; - auto probability = head["probability"]; - auto lfePercentage = head["lfePercentage"]; - auto centerPercentage = head["centerPercentage"]; - auto startDelay = head["startDelay"]; - auto volumeFalloffCurve = head["volumeFalloffCurve"]; - auto envelopMin = head["envelopMin"]; - auto envelopMax = head["envelopMax"]; - auto envelopPercentage = head["envelopPercentage"]; - auto speakerMap = head["speakerMap"]; - - // Fix casing - if (soundFile.is_null()) { - soundFile = head["soundfile"]; + // Allocate + aliasList->head = builder->getAllocator()->allocate(); + if (!aliasList->head) + { + Components::Logger::Print("Error allocating memory for sound alias structure!\n"); + return; + } - Components::Logger::Print("Fixed casing on %s\n", name.data()); - } + aliasList->head->soundFile = builder->getAllocator()->allocate(); + if (!aliasList->head->soundFile) + { + Components::Logger::Print("Error allocating memory for sound alias structure!\n"); + return; + } - if (type.is_null() || soundFile.is_null()) - { - //auto p = fopen("test", "w"); - //fwrite(aliasFile.getBuffer().data(), aliasFile.getBuffer().length(), 1, p); - //fflush(p); - //fclose(p); + Game::snd_alias_t* alias = aliasList->head; - //auto p2 = fopen("test2", "w"); - //fwrite(infoData.dump().data(), infoData.dump().length(), 1, p2); - //fflush(p2); - //fclose(p2); + // try and parse everything and if it fails then fail for the whole file + auto type = head["type"]; + auto subtitle = head["subtitle"]; + auto secondaryAliasName = head["secondaryAliasName"]; + auto chainAliasName = head["chainAliasName"]; + auto soundFile = head["soundFile"]; + auto sequence = head["sequence"]; + auto volMin = head["volMin"]; + auto volMax = head["volMax"]; + auto pitchMin = head["pitchMin"]; + auto pitchMax = head["pitchMax"]; + auto distMin = head["distMin"]; + auto distMax = head["distMax"]; + auto flags = head["flags"]; + auto slavePercentage = head["slavePercentage"]; + auto probability = head["probability"]; + auto lfePercentage = head["lfePercentage"]; + auto centerPercentage = head["centerPercentage"]; + auto startDelay = head["startDelay"]; + auto volumeFalloffCurve = head["volumeFalloffCurve"]; + auto envelopMin = head["envelopMin"]; + auto envelopMax = head["envelopMax"]; + auto envelopPercentage = head["envelopPercentage"]; + auto speakerMap = head["speakerMap"]; - Components::Logger::Print("Type is %s\n", type.dump().data()); - Components::Logger::Print("SoundFile is %s\n", soundFile.dump().data()); - Components::Logger::Error("Failed to parse sound %s! Each alias must have at least a type and a soundFile\n", name.data()); - return; - } + // Fix casing + if (soundFile.is_null()) + { + soundFile = head["soundfile"]; + + Components::Logger::Print("Fixed casing on %s\n", name.c_str()); + } + + if (type.is_null() || soundFile.is_null()) + { + Components::Logger::Print("Type is %s\n", type.dump().c_str()); + Components::Logger::Print("SoundFile is %s\n", soundFile.dump().c_str()); + Components::Logger::Error("Failed to parse sound %s! Each alias must have at least a type and a soundFile\n", name.c_str()); + return; + } #define CHECK(x, type) (x.is_##type##() || x.is_null()) - // TODO: actually support all of those properties - if (!CHECK(type, string)) { - Components::Logger::Print("%s is not string but %d (%s)\n", "type", type.type(), type.dump().data()); - } - - if (!CHECK(subtitle, string)) { - Components::Logger::Print("%s is not string but %d (%s)\n", "subtitle", subtitle.type(), subtitle.dump().data()); - } - - if (!CHECK(secondaryAliasName, string)) { - Components::Logger::Print("%s is not string but %d (%s)\n", "secondaryAliasName", secondaryAliasName.type(), secondaryAliasName.dump().data()); - } - - if (!CHECK(chainAliasName, string)) { - Components::Logger::Print("%s is not string but %d (%s)\n", "chainAliasName", chainAliasName.type(), chainAliasName.dump().data()); - } - - if (!CHECK(soundFile, string)) { - Components::Logger::Print("%s is not string but %d (%s)\n", "soundFile", soundFile.type(), soundFile.dump().data()); - } - - if (!CHECK(sequence, number)) { - Components::Logger::Print("%s is not number but %d (%s)\n", "sequence", sequence.type(), sequence.dump().data()); - } - - if (!CHECK(volMin, number)) { - Components::Logger::Print("%s is not number but %d (%s)\n", "volMin", volMin.type(), volMin.dump().data()); - } - - if (!CHECK(volMax, number)) { - Components::Logger::Print("%s is not number but %d (%s)\n", "volMax", volMax.type(), volMax.dump().data()); - } - - if (!CHECK(pitchMin, number)) { - Components::Logger::Print("%s is not number but %d (%s)\n", "pitchMin", pitchMin.type(), pitchMin.dump().data()); - } - - if (!CHECK(pitchMax, number)) { - Components::Logger::Print("%s is not number but %d (%s)\n", "pitchMax", pitchMax.type(), pitchMax.dump().data()); - } - - if (!CHECK(probability, number)) { - Components::Logger::Print("%s is not number but %d (%s)\n", "probability", probability.type(), probability.dump().data()); - } - - if (!CHECK(lfePercentage, number)) { - Components::Logger::Print("%s is not number but %d (%s)\n", "lfePercentage", lfePercentage.type(), lfePercentage.dump().data()); - } - - if (!CHECK(centerPercentage, number)) { - Components::Logger::Print("%s is not number but %d (%s)\n", "centerPercentage", centerPercentage.type(), centerPercentage.dump().data()); - } - - if (!CHECK(startDelay, number)) { - Components::Logger::Print("%s is not number but %d (%s)\n", "startDelay", startDelay.type(), startDelay.dump().data()); - } - - if (!CHECK(volumeFalloffCurve, string)) { - Components::Logger::Print("%s is not string but %d (%s)\n", "volumeFalloffCurve", volumeFalloffCurve.type(), volumeFalloffCurve.dump().data()); - } - - if (!CHECK(envelopMin, number)) { - Components::Logger::Print("%s is not number but %d (%s)\n", "envelopMin", envelopMin.type(), envelopMin.dump().data()); - } - - if (!CHECK(envelopMax, number)) { - Components::Logger::Print("%s is not number but %d (%s)\n", "envelopMax", envelopMax.type(), envelopMax.dump().data()); - } - - if (!CHECK(envelopPercentage, number)) { - Components::Logger::Print("%s is not number but %d (%s)\n", "envelopPercentage", envelopPercentage.type(), envelopPercentage.dump().data()); - } - - if (!CHECK(speakerMap, object)) { - Components::Logger::Print("%s is not object but %d (%s)\n", "speakerMap", speakerMap.type(), speakerMap.dump().data()); - } - - - if (CHECK(type, number) && CHECK(subtitle, string) && CHECK(secondaryAliasName, string) && CHECK(chainAliasName, string) && - CHECK(soundFile, string) && CHECK(sequence, number) && CHECK(volMin, number) && CHECK(volMax, number) && CHECK(pitchMin, number) && - CHECK(pitchMax, number) && CHECK(distMin, number) && CHECK(distMax, number) && CHECK(flags, number) && CHECK(slavePercentage, number) && - CHECK(probability, number) && CHECK(lfePercentage, number) && CHECK(centerPercentage, number) && CHECK(startDelay, number) && - CHECK(volumeFalloffCurve, string) && CHECK(envelopMin, number) && CHECK(envelopMax, number) && CHECK(envelopPercentage, number) && - CHECK(speakerMap, object)) - { - - alias->soundFile->exists = true; - - if (subtitle.is_string()) + // TODO: actually support all of those properties + if (!CHECK(type, number)) { - alias->subtitle = subtitle.string_value().data(); - } - if (secondaryAliasName.is_string()) - { - alias->secondaryAliasName = secondaryAliasName.string_value().data(); - } - if (chainAliasName.is_string()) - { - alias->chainAliasName = chainAliasName.string_value().data(); + Components::Logger::Print("%s is not number but %d (%s)\n", "type", type.type(), type.dump().c_str()); } - alias->sequence = sequence.int_value(); - alias->volMin = float(volMin.number_value()); - alias->volMax = float(volMax.number_value()); - alias->pitchMin = float(pitchMin.number_value()); - alias->pitchMax = float(pitchMax.number_value()); - alias->distMin = float(distMin.number_value()); - alias->distMax = float(distMax.number_value()); - alias->flags = flags.int_value(); - alias->___u15.slavePercentage = float(slavePercentage.number_value()); - alias->probability = float(probability.number_value()); - alias->lfePercentage = float(lfePercentage.number_value()); - alias->centerPercentage = float(centerPercentage.number_value()); - alias->startDelay = startDelay.int_value(); - alias->envelopMin = float(envelopMin.number_value()); - alias->envelopMax = float(envelopMax.number_value()); - alias->envelopPercentage = float(envelopPercentage.number_value()); - - if (volumeFalloffCurve.is_string()) + if (!CHECK(subtitle, string)) { - alias->volumeFalloffCurve = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, volumeFalloffCurve.string_value(), builder).sndCurve; + Components::Logger::Print("%s is not string but %d (%s)\n", "subtitle", subtitle.type(), subtitle.dump().c_str()); } - if (type.number_value() == 1) // Loaded + if (!CHECK(secondaryAliasName, string)) { - alias->soundFile->type = Game::SAT_LOADED; - alias->soundFile->u.loadSnd = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, soundFile.string_value(), builder).loadSnd; + Components::Logger::Print("%s is not string but %d (%s)\n", "secondaryAliasName", secondaryAliasName.type(), secondaryAliasName.dump().c_str()); } - else if (type.number_value() == 2) // Streamed + + if (!CHECK(chainAliasName, string)) { - alias->soundFile->type = Game::SAT_STREAMED; - std::string streamedFile = soundFile.string_value(); - int split = streamedFile.find_last_of('/'); - alias->soundFile->u.streamSnd.filename.info.raw.dir = builder->getAllocator()->duplicateString(streamedFile.substr(0, split).c_str()); - alias->soundFile->u.streamSnd.filename.info.raw.name = builder->getAllocator()->duplicateString(streamedFile.substr(split).c_str()); + Components::Logger::Print("%s is not string but %d (%s)\n", "chainAliasName", chainAliasName.type(), chainAliasName.dump().c_str()); + } + + if (!CHECK(soundFile, string)) + { + Components::Logger::Print("%s is not string but %d (%s)\n", "soundFile", soundFile.type(), soundFile.dump().c_str()); + } + + if (!CHECK(sequence, number)) + { + Components::Logger::Print("%s is not number but %d (%s)\n", "sequence", sequence.type(), sequence.dump().c_str()); + } + + if (!CHECK(volMin, number)) + { + Components::Logger::Print("%s is not number but %d (%s)\n", "volMin", volMin.type(), volMin.dump().c_str()); + } + + if (!CHECK(volMax, number)) + { + Components::Logger::Print("%s is not number but %d (%s)\n", "volMax", volMax.type(), volMax.dump().c_str()); + } + + if (!CHECK(pitchMin, number)) + { + Components::Logger::Print("%s is not number but %d (%s)\n", "pitchMin", pitchMin.type(), pitchMin.dump().c_str()); + } + + if (!CHECK(pitchMax, number)) + { + Components::Logger::Print("%s is not number but %d (%s)\n", "pitchMax", pitchMax.type(), pitchMax.dump().c_str()); + } + + if (!CHECK(probability, number)) + { + Components::Logger::Print("%s is not number but %d (%s)\n", "probability", probability.type(), probability.dump().c_str()); + } + + if (!CHECK(lfePercentage, number)) + { + Components::Logger::Print("%s is not number but %d (%s)\n", "lfePercentage", lfePercentage.type(), lfePercentage.dump().c_str()); + } + + if (!CHECK(centerPercentage, number)) + { + Components::Logger::Print("%s is not number but %d (%s)\n", "centerPercentage", centerPercentage.type(), centerPercentage.dump().c_str()); + } + + if (!CHECK(startDelay, number)) + { + Components::Logger::Print("%s is not number but %d (%s)\n", "startDelay", startDelay.type(), startDelay.dump().c_str()); + } + + if (!CHECK(volumeFalloffCurve, string)) + { + Components::Logger::Print("%s is not string but %d (%s)\n", "volumeFalloffCurve", volumeFalloffCurve.type(), volumeFalloffCurve.dump().c_str()); + } + + if (!CHECK(envelopMin, number)) + { + Components::Logger::Print("%s is not number but %d (%s)\n", "envelopMin", envelopMin.type(), envelopMin.dump().c_str()); + } + + if (!CHECK(envelopMax, number)) + { + Components::Logger::Print("%s is not number but %d (%s)\n", "envelopMax", envelopMax.type(), envelopMax.dump().c_str()); + } + + if (!CHECK(envelopPercentage, number)) + { + Components::Logger::Print("%s is not number but %d (%s)\n", "envelopPercentage", envelopPercentage.type(), envelopPercentage.dump().c_str()); + } + + if (!CHECK(speakerMap, object)) + { + Components::Logger::Print("%s is not object but %d (%s)\n", "speakerMap", speakerMap.type(), speakerMap.dump().c_str()); + } + + + if (CHECK(type, number) && CHECK(subtitle, string) && CHECK(secondaryAliasName, string) && CHECK(chainAliasName, string) && + CHECK(soundFile, string) && CHECK(sequence, number) && CHECK(volMin, number) && CHECK(volMax, number) && CHECK(pitchMin, number) && + CHECK(pitchMax, number) && CHECK(distMin, number) && CHECK(distMax, number) && CHECK(flags, number) && CHECK(slavePercentage, number) && + CHECK(probability, number) && CHECK(lfePercentage, number) && CHECK(centerPercentage, number) && CHECK(startDelay, number) && + CHECK(volumeFalloffCurve, string) && CHECK(envelopMin, number) && CHECK(envelopMax, number) && CHECK(envelopPercentage, number) && + CHECK(speakerMap, object)) + { + + alias->soundFile->exists = true; + alias->aliasName = aliasList->aliasName; + + if (subtitle.is_string()) + { + alias->subtitle = builder->getAllocator()->duplicateString(subtitle.string_value().c_str()); + } + if (secondaryAliasName.is_string()) + { + alias->secondaryAliasName = builder->getAllocator()->duplicateString(secondaryAliasName.string_value().c_str()); + } + if (chainAliasName.is_string()) + { + alias->chainAliasName = builder->getAllocator()->duplicateString(chainAliasName.string_value().c_str()); + } + + alias->sequence = sequence.int_value(); + alias->volMin = float(volMin.number_value()); + alias->volMax = float(volMax.number_value()); + alias->pitchMin = float(pitchMin.number_value()); + alias->pitchMax = float(pitchMax.number_value()); + alias->distMin = float(distMin.number_value()); + alias->distMax = float(distMax.number_value()); + alias->flags = flags.int_value(); + alias->___u15.slavePercentage = float(slavePercentage.number_value()); + alias->probability = float(probability.number_value()); + alias->lfePercentage = float(lfePercentage.number_value()); + alias->centerPercentage = float(centerPercentage.number_value()); + alias->startDelay = startDelay.int_value(); + alias->envelopMin = float(envelopMin.number_value()); + alias->envelopMax = float(envelopMax.number_value()); + alias->envelopPercentage = float(envelopPercentage.number_value()); + + // Speaker map object + if (!speakerMap.is_null()) + { + alias->speakerMap = builder->getAllocator()->allocate(); + if (!alias->speakerMap) + { + Components::Logger::Print("Error allocating memory for speakermap in sound alias%s!\n", alias->aliasName); + return; + } + + alias->speakerMap->name = builder->getAllocator()->duplicateString(speakerMap["name"].string_value().c_str()); + alias->speakerMap->isDefault = speakerMap["isDefault"].bool_value(); + + if (speakerMap["channelMaps"].is_array()) + { + json11::Json::array channelMaps = speakerMap["channelMaps"].array_items(); + + assert(channelMaps.size() <= 4); + + // channelMapIndex should never exceed 1 + for (size_t channelMapIndex = 0; channelMapIndex < 2; channelMapIndex++) + { + // subChannelIndex should never exceed 1 + for (size_t subChannelIndex = 0; subChannelIndex < 2; subChannelIndex++) + { + json11::Json channelMap = channelMaps[channelMapIndex * 2 + subChannelIndex]; // 0-3 + + auto speakers = channelMap["speakers"].array_items(); + + alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakerCount = speakers.size(); + + for (size_t speakerIndex = 0; speakerIndex < alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakerCount; speakerIndex++) + { + auto speaker = speakers[speakerIndex]; + alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].levels[0] = static_cast(speaker["levels0"].number_value()); + alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].levels[1] = static_cast(speaker["levels1"].number_value()); + alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].numLevels = static_cast(speaker["numLevels"].number_value()); + alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].speaker = static_cast(speaker["speaker"].number_value()); + } + } + } + } + } + + if (volumeFalloffCurve.is_string()) + { + alias->volumeFalloffCurve = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, volumeFalloffCurve.string_value(), builder).sndCurve; + } + + if (type.number_value() == 1) // Loaded + { + alias->soundFile->type = Game::SAT_LOADED; + alias->soundFile->u.loadSnd = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, soundFile.string_value(), builder).loadSnd; + } + else if (type.number_value() == 2) // Streamed + { + alias->soundFile->type = Game::SAT_STREAMED; + std::string streamedFile = soundFile.string_value(); + int split = streamedFile.find_last_of('/'); + alias->soundFile->u.streamSnd.filename.info.raw.dir = builder->getAllocator()->duplicateString(streamedFile.substr(0, split).c_str()); + alias->soundFile->u.streamSnd.filename.info.raw.name = builder->getAllocator()->duplicateString(streamedFile.substr(split).c_str()); + } + else + { + Components::Logger::Error("Failed to parse sound %s! Invalid sound type %s\n", name.c_str(), type.string_value().c_str()); + return; + } + + if (i == 0) + { + aliasList->head = alias; + } } else { - Components::Logger::Error("Failed to parse sound %s! Invalid sound type %s\n", name.data(), type.string_value().c_str()); + Components::Logger::Error("Failed to parse sound %s!\n", name.c_str()); + return; } } - else - { - Components::Logger::Error("Failed to parse sound %s!\n", name.data()); - return; - } + + header->sound = aliasList; #undef CHECK @@ -253,7 +325,7 @@ namespace Assets { Game::snd_alias_list_t* asset = header.sound; - for (int i = 0; i < asset->count; ++i) + for (unsigned int i = 0; i < asset->count; ++i) { Game::snd_alias_t* alias = &asset->head[i]; @@ -302,7 +374,7 @@ namespace Assets Game::snd_alias_t* destHead = buffer->dest(); buffer->saveArray(asset->head, asset->count); - for (int i = 0; i < asset->count; ++i) + for (unsigned int i = 0; i < asset->count; ++i) { Game::snd_alias_t* destAlias = &destHead[i]; Game::snd_alias_t* alias = &asset->head[i]; diff --git a/src/Components/Modules/ZoneBuilder.cpp b/src/Components/Modules/ZoneBuilder.cpp index 1c8329a6..b03b3708 100644 --- a/src/Components/Modules/ZoneBuilder.cpp +++ b/src/Components/Modules/ZoneBuilder.cpp @@ -223,10 +223,6 @@ namespace Components Game::XAssetHeader assetHeader = AssetHandler::FindAssetForZone(type, name, this, isSubAsset); - if (type == Game::XAssetType::ASSET_TYPE_LOADED_SOUND) { - Logger::Print("Loading loaded_sound '%s'\n", name.data()); - } - if (!assetHeader.data) { Logger::Error("Error: Missing asset '%s' of type '%s'\n", name.data(), Game::DB_GetXAssetTypeName(type)); diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index b7bfe710..7fdf309a 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -906,7 +906,7 @@ namespace Game struct MSSChannelMap { - int speakerCount; + unsigned int speakerCount; MSSSpeakerLevels speakers[6]; }; @@ -950,7 +950,7 @@ namespace Game { const char *aliasName; snd_alias_t *head; - int count; + unsigned int count; }; struct cStaticModel_s From 7dbb258a4794e1421f29c2cea318f1c9b6234cc1 Mon Sep 17 00:00:00 2001 From: rackover Date: Fri, 9 Apr 2021 18:17:23 +0200 Subject: [PATCH 27/37] Correct array allocation for loaded sounds --- .../AssetInterfaces/Isnd_alias_list_t.cpp | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp b/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp index 881355b4..bacaf8c2 100644 --- a/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp +++ b/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp @@ -25,6 +25,15 @@ namespace Assets auto aliases = aliasesContainer.array_items(); aliasList->count = aliases.size(); + + // Allocate + aliasList->head = builder->getAllocator()->allocateArray(aliasList->count); + if (!aliasList->head) + { + Components::Logger::Print("Error allocating memory for sound alias structure!\n"); + return; + } + aliasList->aliasName = builder->getAllocator()->duplicateString(infoData["aliasName"].string_value().c_str()); for (size_t i = 0; i < aliasList->count; i++) @@ -37,14 +46,6 @@ namespace Assets return; } - // Allocate - aliasList->head = builder->getAllocator()->allocate(); - if (!aliasList->head) - { - Components::Logger::Print("Error allocating memory for sound alias structure!\n"); - return; - } - aliasList->head->soundFile = builder->getAllocator()->allocate(); if (!aliasList->head->soundFile) { @@ -281,21 +282,29 @@ namespace Assets if (volumeFalloffCurve.is_string()) { - alias->volumeFalloffCurve = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, volumeFalloffCurve.string_value(), builder).sndCurve; + alias->volumeFalloffCurve = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, "$default" /*volumeFalloffCurve.string_value().c_str()*/, builder).sndCurve; } - if (type.number_value() == 1) // Loaded + if (type.number_value() == Game::snd_alias_type_t::SAT_LOADED) // Loaded { alias->soundFile->type = Game::SAT_LOADED; - alias->soundFile->u.loadSnd = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, soundFile.string_value(), builder).loadSnd; + alias->soundFile->u.loadSnd = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, soundFile.string_value().c_str(), builder).loadSnd; } - else if (type.number_value() == 2) // Streamed + else if (type.number_value() == Game::snd_alias_type_t::SAT_STREAMED) // Streamed { alias->soundFile->type = Game::SAT_STREAMED; std::string streamedFile = soundFile.string_value(); + std::string directory = ""s; int split = streamedFile.find_last_of('/'); - alias->soundFile->u.streamSnd.filename.info.raw.dir = builder->getAllocator()->duplicateString(streamedFile.substr(0, split).c_str()); - alias->soundFile->u.streamSnd.filename.info.raw.name = builder->getAllocator()->duplicateString(streamedFile.substr(split).c_str()); + + if (split >= 0) + { + directory = streamedFile.substr(split); + streamedFile = streamedFile.substr(0, split); + } + + alias->soundFile->u.streamSnd.filename.info.raw.dir = builder->getAllocator()->duplicateString(directory.c_str()); + alias->soundFile->u.streamSnd.filename.info.raw.name = builder->getAllocator()->duplicateString(streamedFile.c_str()); } else { @@ -303,10 +312,7 @@ namespace Assets return; } - if (i == 0) - { - aliasList->head = alias; - } + aliasList->head[i] = *alias; } else { From eebc48ab7c7459d56f8149ef218a0d924c15b524 Mon Sep 17 00:00:00 2001 From: rackover Date: Sat, 10 Apr 2021 21:01:19 +0200 Subject: [PATCH 28/37] Set correct type flags when loading soundaliases (this should be in iw3xport, i'll move it later) --- .../AssetInterfaces/Isnd_alias_list_t.cpp | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp b/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp index bacaf8c2..0ffc665f 100644 --- a/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp +++ b/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp @@ -282,17 +282,38 @@ namespace Assets if (volumeFalloffCurve.is_string()) { - alias->volumeFalloffCurve = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, "$default" /*volumeFalloffCurve.string_value().c_str()*/, builder).sndCurve; + std::string fallOffCurve = volumeFalloffCurve.string_value(); + + if (fallOffCurve.size() == 0) + { + fallOffCurve = "$default"; + } + + auto curve = Components::AssetHandler::FindAssetForZone( + Game::XAssetType::ASSET_TYPE_SOUND_CURVE, + fallOffCurve.c_str(), + builder + ).sndCurve; + + alias->volumeFalloffCurve = curve; } + // Clear the flags from type + alias->flags &= ~(0b111 << 7); + if (type.number_value() == Game::snd_alias_type_t::SAT_LOADED) // Loaded { alias->soundFile->type = Game::SAT_LOADED; alias->soundFile->u.loadSnd = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, soundFile.string_value().c_str(), builder).loadSnd; + + // Set the type + alias->flags |= (Game::SAT_LOADED & 0b111) << 7; + } else if (type.number_value() == Game::snd_alias_type_t::SAT_STREAMED) // Streamed { alias->soundFile->type = Game::SAT_STREAMED; + alias->flags |= (Game::SAT_STREAMED & 0b111) << 7; std::string streamedFile = soundFile.string_value(); std::string directory = ""s; int split = streamedFile.find_last_of('/'); From eebd0ae1da40220cbf6afa4273e6791c2ecbd69f Mon Sep 17 00:00:00 2001 From: rackover Date: Mon, 12 Apr 2021 00:40:10 +0200 Subject: [PATCH 29/37] Cleanup & handle flag conversion in iw3xport --- .../Modules/AssetInterfaces/IFxEffectDef.cpp | 6 ++---- .../AssetInterfaces/Isnd_alias_list_t.cpp | 20 +++++++++---------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/Components/Modules/AssetInterfaces/IFxEffectDef.cpp b/src/Components/Modules/AssetInterfaces/IFxEffectDef.cpp index 7cd2c87d..38928466 100644 --- a/src/Components/Modules/AssetInterfaces/IFxEffectDef.cpp +++ b/src/Components/Modules/AssetInterfaces/IFxEffectDef.cpp @@ -7,12 +7,13 @@ namespace Assets void IFxEffectDef::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) { if (!header->data) this->loadEfx(header, name, builder); // Check if we have an editor fx - if (!header->data /*&& !builder->isPrimaryAsset()*/) this->loadNative(header, name, builder); // Check if there is a native one if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game + if (!header->data /*&& !builder->isPrimaryAsset()*/) this->loadNative(header, name, builder); // Check if there is a native one } void IFxEffectDef::loadFxElemVisuals(Game::FxElemVisuals* visuals, char elemType, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader) { + switch (elemType) { case Game::FX_ELEM_TYPE_MODEL: @@ -34,10 +35,7 @@ namespace Assets if (visuals->soundName) { visuals->soundName = reader->readCString(); - visuals->soundName = "null"; - Components::Logger::Print("Unable to load sounds yet!\n"); } - break; } diff --git a/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp b/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp index 0ffc665f..2626abe0 100644 --- a/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp +++ b/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp @@ -298,22 +298,15 @@ namespace Assets alias->volumeFalloffCurve = curve; } - // Clear the flags from type - alias->flags &= ~(0b111 << 7); - - if (type.number_value() == Game::snd_alias_type_t::SAT_LOADED) // Loaded + if (static_cast(type.number_value()) == Game::snd_alias_type_t::SAT_LOADED) // Loaded { alias->soundFile->type = Game::SAT_LOADED; alias->soundFile->u.loadSnd = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, soundFile.string_value().c_str(), builder).loadSnd; - - // Set the type - alias->flags |= (Game::SAT_LOADED & 0b111) << 7; - } - else if (type.number_value() == Game::snd_alias_type_t::SAT_STREAMED) // Streamed + else if (static_cast(type.number_value()) == Game::snd_alias_type_t::SAT_STREAMED) // Streamed { alias->soundFile->type = Game::SAT_STREAMED; - alias->flags |= (Game::SAT_STREAMED & 0b111) << 7; + std::string streamedFile = soundFile.string_value(); std::string directory = ""s; int split = streamedFile.find_last_of('/'); @@ -363,7 +356,12 @@ namespace Assets if (alias->volumeFalloffCurve) { - builder->loadAsset(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, alias->volumeFalloffCurve); + if (!builder->loadAsset(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, alias->volumeFalloffCurve)) + { + // (Should never happen, but just in case) + alias->volumeFalloffCurve->filename = "$default"; + builder->loadAsset(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, alias->volumeFalloffCurve); + } } } } From d98cfb55cbdf1022530b9ec34338694907090a71 Mon Sep 17 00:00:00 2001 From: rackover Date: Tue, 13 Apr 2021 00:48:01 +0200 Subject: [PATCH 30/37] Fix typos in WAV loading and streamed sound path --- src/Components/Modules/AssetInterfaces/ILoadedSound.cpp | 2 +- src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Components/Modules/AssetInterfaces/ILoadedSound.cpp b/src/Components/Modules/AssetInterfaces/ILoadedSound.cpp index 376a21d9..9dc06fb1 100644 --- a/src/Components/Modules/AssetInterfaces/ILoadedSound.cpp +++ b/src/Components/Modules/AssetInterfaces/ILoadedSound.cpp @@ -60,8 +60,8 @@ namespace Assets } sound->sound.info.channels = reader.read(); - sound->sound.info.samples = reader.read(); sound->sound.info.rate = reader.read(); + sound->sound.info.samples = reader.read(); sound->sound.info.block_size = reader.read(); sound->sound.info.bits = reader.read(); diff --git a/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp b/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp index 2626abe0..02966b04 100644 --- a/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp +++ b/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp @@ -313,8 +313,8 @@ namespace Assets if (split >= 0) { - directory = streamedFile.substr(split); - streamedFile = streamedFile.substr(0, split); + directory = streamedFile.substr(0, split); + streamedFile = streamedFile.substr(split+1); } alias->soundFile->u.streamSnd.filename.info.raw.dir = builder->getAllocator()->duplicateString(directory.c_str()); From 1fe241c7e5007dd2fa2f6a460fb1b4fb8f7c034c Mon Sep 17 00:00:00 2001 From: rackover Date: Tue, 13 Apr 2021 10:28:10 +0200 Subject: [PATCH 31/37] Load weapon sounds with zonebuilder --- .../Modules/AssetInterfaces/IWeapon.cpp | 78 +++++++++++++++++-- 1 file changed, 73 insertions(+), 5 deletions(-) diff --git a/src/Components/Modules/AssetInterfaces/IWeapon.cpp b/src/Components/Modules/AssetInterfaces/IWeapon.cpp index c325e8e7..cf98af0a 100644 --- a/src/Components/Modules/AssetInterfaces/IWeapon.cpp +++ b/src/Components/Modules/AssetInterfaces/IWeapon.cpp @@ -15,9 +15,9 @@ namespace Assets } } - void IWeapon::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) - { - Game::WeaponCompleteDef* asset = header.weapon; + void IWeapon::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) + { + Game::WeaponCompleteDef* asset = header.weapon; // convert all script strings if (asset->hideTags) @@ -64,7 +64,7 @@ namespace Assets builder->addScriptString(asset->weapDef->notetrackRumbleMapValues[i]); } } - + // now load all sub-assets properly if (asset->killIcon) builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->killIcon); @@ -119,7 +119,75 @@ namespace Assets if (asset->weapDef->projBeaconEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->projBeaconEffect); if (asset->weapDef->projIgnitionEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->projIgnitionEffect); if (asset->weapDef->turretOverheatEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->turretOverheatEffect); - } + +#define LoadWeapSound(sound) if (asset->weapDef->##sound##) builder->loadAsset(Game::XAssetType::ASSET_TYPE_SOUND, asset->weapDef->##sound##) + + LoadWeapSound(pickupSound); + LoadWeapSound(pickupSoundPlayer); + LoadWeapSound(ammoPickupSound); + LoadWeapSound(ammoPickupSoundPlayer); + LoadWeapSound(projectileSound); + LoadWeapSound(pullbackSound); + LoadWeapSound(pullbackSoundPlayer); + LoadWeapSound(fireSound); + LoadWeapSound(fireSoundPlayer); + LoadWeapSound(fireSoundPlayerAkimbo); + LoadWeapSound(fireLoopSound); + LoadWeapSound(fireLoopSoundPlayer); + LoadWeapSound(fireStopSound); + LoadWeapSound(fireStopSoundPlayer); + LoadWeapSound(fireLastSound); + LoadWeapSound(fireLastSoundPlayer); + LoadWeapSound(emptyFireSound); + LoadWeapSound(emptyFireSoundPlayer); + LoadWeapSound(meleeSwipeSound); + LoadWeapSound(meleeSwipeSoundPlayer); + LoadWeapSound(meleeHitSound); + LoadWeapSound(meleeMissSound); + LoadWeapSound(rechamberSound); + LoadWeapSound(rechamberSoundPlayer); + LoadWeapSound(reloadSound); + LoadWeapSound(reloadSoundPlayer); + LoadWeapSound(reloadEmptySound); + LoadWeapSound(reloadEmptySoundPlayer); + LoadWeapSound(reloadStartSound); + LoadWeapSound(reloadStartSoundPlayer); + LoadWeapSound(reloadEndSound); + LoadWeapSound(reloadEndSoundPlayer); + LoadWeapSound(detonateSound); + LoadWeapSound(detonateSoundPlayer); + LoadWeapSound(nightVisionWearSound); + LoadWeapSound(nightVisionWearSoundPlayer); + LoadWeapSound(nightVisionRemoveSound); + LoadWeapSound(nightVisionRemoveSoundPlayer); + LoadWeapSound(altSwitchSound); + LoadWeapSound(altSwitchSoundPlayer); + LoadWeapSound(raiseSound); + LoadWeapSound(raiseSoundPlayer); + LoadWeapSound(firstRaiseSound); + LoadWeapSound(firstRaiseSoundPlayer); + LoadWeapSound(putawaySound); + LoadWeapSound(putawaySoundPlayer); + LoadWeapSound(scanSound); + + // No idea how to handle this! + //if (asset->weapDef->bounceSound && *asset->weapDef->bounceSound) builder->loadAsset(Game::XAssetType::ASSET_TYPE_SOUND, *asset->weapDef->bounceSound); + + LoadWeapSound(projExplosionSound); + LoadWeapSound(projDudSound); + LoadWeapSound(projIgnitionSound); + LoadWeapSound(turretOverheatSound); + LoadWeapSound(turretBarrelSpinMaxSnd); + + for (size_t i = 0; i < 4; i++) + { + LoadWeapSound(turretBarrelSpinUpSnd[i]); + LoadWeapSound(turretBarrelSpinDownSnd[i]); + } + + LoadWeapSound(missileConeSoundAlias); + LoadWeapSound(missileConeSoundAliasAtBase); + } void IWeapon::writeWeaponDef(Game::WeaponDef* def, Components::ZoneBuilder::Zone* builder, Utils::Stream* buffer) { From 3e9b05d8cefe37943d0f411d77e0f1dc30ce5620 Mon Sep 17 00:00:00 2001 From: rackover Date: Tue, 13 Apr 2021 19:54:09 +0200 Subject: [PATCH 32/37] Load bounce sounds, better soundalias structure --- src/Components/Modules/AssetInterfaces/IWeapon.cpp | 9 +++++++-- .../Modules/AssetInterfaces/Isnd_alias_list_t.cpp | 12 +++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/Components/Modules/AssetInterfaces/IWeapon.cpp b/src/Components/Modules/AssetInterfaces/IWeapon.cpp index cf98af0a..350006d9 100644 --- a/src/Components/Modules/AssetInterfaces/IWeapon.cpp +++ b/src/Components/Modules/AssetInterfaces/IWeapon.cpp @@ -170,8 +170,13 @@ namespace Assets LoadWeapSound(putawaySoundPlayer); LoadWeapSound(scanSound); - // No idea how to handle this! - //if (asset->weapDef->bounceSound && *asset->weapDef->bounceSound) builder->loadAsset(Game::XAssetType::ASSET_TYPE_SOUND, *asset->weapDef->bounceSound); + if (asset->weapDef->bounceSound) + { + for (size_t i = 0; i < 31; i++) + { + LoadWeapSound(bounceSound[i]); + } + } LoadWeapSound(projExplosionSound); LoadWeapSound(projDudSound); diff --git a/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp b/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp index 02966b04..f14e00b5 100644 --- a/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp +++ b/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp @@ -34,7 +34,7 @@ namespace Assets return; } - aliasList->aliasName = builder->getAllocator()->duplicateString(infoData["aliasName"].string_value().c_str()); + aliasList->aliasName = builder->getAllocator()->duplicateString(name.c_str()); for (size_t i = 0; i < aliasList->count; i++) { @@ -79,6 +79,7 @@ namespace Assets auto envelopMax = head["envelopMax"]; auto envelopPercentage = head["envelopPercentage"]; auto speakerMap = head["speakerMap"]; + auto aliasName = head["aliasName"]; // Fix casing if (soundFile.is_null()) @@ -109,6 +110,11 @@ namespace Assets Components::Logger::Print("%s is not string but %d (%s)\n", "subtitle", subtitle.type(), subtitle.dump().c_str()); } + if (!CHECK(aliasName, string)) + { + Components::Logger::Print("%s is not string but %d (%s)\n", "aliasName", aliasName.type(), aliasName.dump().c_str()); + } + if (!CHECK(secondaryAliasName, string)) { Components::Logger::Print("%s is not string but %d (%s)\n", "secondaryAliasName", secondaryAliasName.type(), secondaryAliasName.dump().c_str()); @@ -195,7 +201,7 @@ namespace Assets } - if (CHECK(type, number) && CHECK(subtitle, string) && CHECK(secondaryAliasName, string) && CHECK(chainAliasName, string) && + if (CHECK(type, number) && CHECK(aliasName, string) && CHECK(subtitle, string) && CHECK(secondaryAliasName, string) && CHECK(chainAliasName, string) && CHECK(soundFile, string) && CHECK(sequence, number) && CHECK(volMin, number) && CHECK(volMax, number) && CHECK(pitchMin, number) && CHECK(pitchMax, number) && CHECK(distMin, number) && CHECK(distMax, number) && CHECK(flags, number) && CHECK(slavePercentage, number) && CHECK(probability, number) && CHECK(lfePercentage, number) && CHECK(centerPercentage, number) && CHECK(startDelay, number) && @@ -204,7 +210,7 @@ namespace Assets { alias->soundFile->exists = true; - alias->aliasName = aliasList->aliasName; + alias->aliasName = builder->getAllocator()->duplicateString(aliasName.string_value().c_str()); if (subtitle.is_string()) { From ee5f6a70c0488059d70cbadfbd56cbaefe6c1d46 Mon Sep 17 00:00:00 2001 From: rackover Date: Wed, 21 Apr 2021 23:41:06 +0200 Subject: [PATCH 33/37] Sorted techset match-ups, slightly more sane techset matching, still not perfect though! (mp_convoy won't work unless the createart.gsc is smashed) --- .../Modules/AssetInterfaces/IMaterial.cpp | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/Components/Modules/AssetInterfaces/IMaterial.cpp b/src/Components/Modules/AssetInterfaces/IMaterial.cpp index e9b9d86e..c782a2c8 100644 --- a/src/Components/Modules/AssetInterfaces/IMaterial.cpp +++ b/src/Components/Modules/AssetInterfaces/IMaterial.cpp @@ -30,19 +30,22 @@ namespace Assets }; std::map techSetCorrespondance = { - {"effect_zfeather_outdoor", "effect_zfeather_blend"}, + {"effect", "effect_blend"}, {"effect", "effect_blend"}, {"effect_nofog", "effect_blend_nofog"}, + {"effect_zfeather", "effect_zfeather_blend"}, + {"wc_unlit_add", "wc_unlit_add_lin"}, {"wc_unlit_multiply", "wc_unlit_multiply_lin"}, {"wc_unlit_falloff_add", "wc_unlit_falloff_add_lin_ua"}, - {"mc_unlit_replace", "mc_unlit_replace_lin"}, - {"mc_unlit_nofog", "mc_unlit_blend_nofog_ua"}, {"wc_unlit", "wc_unlit_add"}, {"wc_unlit_multiply_lin", "wc_unlit_multiply_lin"}, {"wc_unlit_blend", "wc_unlit_blend_lin"}, - {"mc_unlit", "mc_unlit_replace_lin"}, - {"effect_zfeather", "effect_zfeather_blend"}/*, + {"wc_unlit_replace", "wc_unlit_replace_lin"}, + + {"mc_unlit_replace", "mc_unlit_replace_lin"}, + {"mc_unlit_nofog", "mc_unlit_blend_nofog_ua"}, + {"mc_unlit", "mc_unlit_blend_lin"}/*, {"", ""}, {"", ""}, {"", ""}, @@ -236,26 +239,27 @@ namespace Assets std::string techName = asset->techniqueSet->name; if (techSetCorrespondance.find(techName) != techSetCorrespondance.end()) { auto iw4TechSetName = techSetCorrespondance[techName]; - Game::XAssetEntry* entry = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, iw4TechSetName.data()); + Game::XAssetEntry* iw4TechSet = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, iw4TechSetName.data()); - if (entry) { - Game::DB_EnumXAssetEntries(Game::XAssetType::ASSET_TYPE_MATERIAL, [asset](Game::XAssetEntry* entry) + if (iw4TechSet) { + Game::DB_EnumXAssetEntries(Game::XAssetType::ASSET_TYPE_MATERIAL, [asset, iw4TechSet](Game::XAssetEntry* entry) { if (!replacementFound) { Game::XAssetHeader header = entry->asset.header; - if (header.material->techniqueSet == asset->techniqueSet) + if (header.material->techniqueSet == iw4TechSet->asset.header.techniqueSet) { - Components::Logger::Print("Material %s with techset %s has been mapped (last chance!) to %s\n", asset->info.name, asset->techniqueSet->name, header.material->techniqueSet->name); + Components::Logger::Print("Material %s with techset %s has been mapped to %s (last chance!)\n", asset->info.name, asset->techniqueSet->name, header.material->techniqueSet->name); asset->info.sortKey = header.material->info.sortKey; + asset->techniqueSet = iw4TechSet->asset.header.techniqueSet; replacementFound = true; } } }, false, false); if (!replacementFound) { - Components::Logger::Print("Could not find any loaded material with techset %s, so I cannot set the sortkey for material %s\n", asset->techniqueSet->name, asset->info.name); + Components::Logger::Print("Could not find any loaded material with techset %s (in replacement of %s), so I cannot set the sortkey for material %s\n", iw4TechSetName.data(), asset->techniqueSet->name, asset->info.name); } } else { @@ -263,7 +267,7 @@ namespace Assets } } else { - Components::Logger::Print("Could not match iw3 techset %s with any of the techsets I know! This is a critical error, there's a good chance the map will not be playable.\n", asset->techniqueSet->name); + Components::Logger::Print("Could not match iw3 techset %s with any of the techsets I know! This is a critical error, there's a good chance the map will not be playable.\n", techName.c_str()); } } From 63ccf0e2bdc1367a4d5defa454e4f990101df7ce Mon Sep 17 00:00:00 2001 From: rackover Date: Mon, 26 Apr 2021 12:30:23 +0200 Subject: [PATCH 34/37] draw model names --- src/Components/Modules/QuickPatch.cpp | 41 +++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/Components/Modules/QuickPatch.cpp b/src/Components/Modules/QuickPatch.cpp index 3c847941..68b667df 100644 --- a/src/Components/Modules/QuickPatch.cpp +++ b/src/Components/Modules/QuickPatch.cpp @@ -973,9 +973,50 @@ namespace Components Dvar::Register("r_drawSceneModelBoundingBoxes", false, Game::DVAR_FLAG_CHEAT, "Draw scene model bounding boxes"); Dvar::Register("r_drawSceneModelCollisions", false, Game::DVAR_FLAG_CHEAT, "Draw scene model collisions"); Dvar::Register("r_drawTriggers", false, Game::DVAR_FLAG_CHEAT, "Draw triggers"); + Dvar::Register("r_drawModelNames", false, Game::DVAR_FLAG_CHEAT, "Draw all model names"); Dvar::Register("r_drawAabbTrees", false, Game::DVAR_FLAG_USERCREATED, "Draw aabb trees"); }); + Scheduler::OnFrame([]() + { + if (!Game::CL_IsCgameInitialized() || !Dvar::Var("r_drawModelNames").get()) return; + + float sceneModelsColor[4] = { 1.0f, 1.0f, 0.0f, 1.0f }; + float dobjsColor[4] = { 0.0f, 1.0f, 1.0f, 1.0f }; + float staticModelsColor[4] = { 1.0f, 0.0f, 1.0f, 1.0f }; + + auto mapName = Dvar::Var("mapname").get(); + auto* scene = Game::scene; + auto world = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_GFXWORLD, Utils::String::VA("maps/mp/%s.d3dbsp", mapName))->asset.header.gfxWorld; + + for (auto i = 0; i < scene->sceneModelCount; i++) + { + if (!scene->sceneModel[i].model) + continue; + + Game::R_AddDebugString(sceneModelsColor, scene->sceneModel[i].placement.base.origin, 1.0, scene->sceneModel[i].model->name); + } + + for (auto i = 0; i < scene->sceneDObjCount; i++) + { + if (scene->sceneDObj[i].obj) { + for (size_t j = 0; j < scene->sceneDObj[i].obj->numModels; j++) + { + Game::R_AddDebugString(dobjsColor, scene->sceneDObj[i].placement.origin, 1.0, scene->sceneDObj[i].obj->models[j]->name); + } + } + } + + // Static models + for (auto i = 0; i < world->dpvs.smodelCount; i++) + { + auto staticModel = world->dpvs.smodelDrawInsts[i]; + if (staticModel.model) { + Game::R_AddDebugString(staticModelsColor, staticModel.placement.origin, 1.0, staticModel.model->name); + } + } + }); + Scheduler::OnFrame([]() { if (!Game::CL_IsCgameInitialized() || !Dvar::Var("r_drawSceneModelBoundingBoxes").get()) return; From 791bd35c53ff187921f34f45c20e973e3a5dfa66 Mon Sep 17 00:00:00 2001 From: rackover Date: Mon, 26 Apr 2021 12:31:02 +0200 Subject: [PATCH 35/37] It's okay if we don't write every asset we had in memory --- src/Components/Modules/ZoneBuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Components/Modules/ZoneBuilder.cpp b/src/Components/Modules/ZoneBuilder.cpp index b03b3708..dd51c331 100644 --- a/src/Components/Modules/ZoneBuilder.cpp +++ b/src/Components/Modules/ZoneBuilder.cpp @@ -44,7 +44,7 @@ namespace Components if (!found) { - Logger::Error("Asset %s of type %s was loaded, but not written!", name.data(), Game::DB_GetXAssetTypeName(subAsset.type)); + Logger::Print("Asset %s of type %s was loaded, but not written!", name.data(), Game::DB_GetXAssetTypeName(subAsset.type)); } } From 368e339efcc4c2ff4212012f140800ced6c8324a Mon Sep 17 00:00:00 2001 From: rackover Date: Wed, 28 Apr 2021 18:49:17 +0200 Subject: [PATCH 36/37] Aggressive material replacement --- .../Modules/AssetInterfaces/IMaterial.cpp | 29 ++++++++++++++----- src/Components/Modules/QuickPatch.cpp | 2 +- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/Components/Modules/AssetInterfaces/IMaterial.cpp b/src/Components/Modules/AssetInterfaces/IMaterial.cpp index c782a2c8..d12974a4 100644 --- a/src/Components/Modules/AssetInterfaces/IMaterial.cpp +++ b/src/Components/Modules/AssetInterfaces/IMaterial.cpp @@ -38,14 +38,17 @@ namespace Assets {"wc_unlit_add", "wc_unlit_add_lin"}, {"wc_unlit_multiply", "wc_unlit_multiply_lin"}, {"wc_unlit_falloff_add", "wc_unlit_falloff_add_lin_ua"}, - {"wc_unlit", "wc_unlit_add"}, + {"wc_unlit", "wc_unlit_add_lin"}, + {"wc_unlit_alphatest", "wc_unlit_blend_lin_ua"}, {"wc_unlit_multiply_lin", "wc_unlit_multiply_lin"}, {"wc_unlit_blend", "wc_unlit_blend_lin"}, {"wc_unlit_replace", "wc_unlit_replace_lin"}, {"mc_unlit_replace", "mc_unlit_replace_lin"}, {"mc_unlit_nofog", "mc_unlit_blend_nofog_ua"}, - {"mc_unlit", "mc_unlit_blend_lin"}/*, + {"mc_unlit", "mc_unlit_blend_lin"}, + {"mc_unlit_alphatest", "mc_unlit_blend_lin"} + /*, {"", ""}, {"", ""}, {"", ""}, @@ -241,7 +244,8 @@ namespace Assets auto iw4TechSetName = techSetCorrespondance[techName]; Game::XAssetEntry* iw4TechSet = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, iw4TechSetName.data()); - if (iw4TechSet) { + if (iw4TechSet) + { Game::DB_EnumXAssetEntries(Game::XAssetType::ASSET_TYPE_MATERIAL, [asset, iw4TechSet](Game::XAssetEntry* entry) { if (!replacementFound) @@ -253,21 +257,32 @@ namespace Assets Components::Logger::Print("Material %s with techset %s has been mapped to %s (last chance!)\n", asset->info.name, asset->techniqueSet->name, header.material->techniqueSet->name); asset->info.sortKey = header.material->info.sortKey; asset->techniqueSet = iw4TechSet->asset.header.techniqueSet; + + // this is terrible! + asset->stateBitsCount = header.material->stateBitsCount; + asset->stateBitsTable = header.material->stateBitsTable; + std::memcpy(asset->stateBitsEntry, header.material->stateBitsEntry, 48); + asset->constantCount = header.material->constantCount; + asset->constantTable = header.material->constantTable; + replacementFound = true; } } }, false, false); - if (!replacementFound) { + if (!replacementFound) + { Components::Logger::Print("Could not find any loaded material with techset %s (in replacement of %s), so I cannot set the sortkey for material %s\n", iw4TechSetName.data(), asset->techniqueSet->name, asset->info.name); } } - else { + else + { Components::Logger::Print("Could not find any loaded techset with iw4 name %s for iw3 techset %s\n", iw4TechSetName.data(), asset->techniqueSet->name); } } - else { - Components::Logger::Print("Could not match iw3 techset %s with any of the techsets I know! This is a critical error, there's a good chance the map will not be playable.\n", techName.c_str()); + else + { + Components::Logger::Print("Could not match iw3 techset %s with any of the techsets I know! This is a critical error, there's a good chance the map will not be playable.\n", techName.data()); } } diff --git a/src/Components/Modules/QuickPatch.cpp b/src/Components/Modules/QuickPatch.cpp index 68b667df..7c8640b0 100644 --- a/src/Components/Modules/QuickPatch.cpp +++ b/src/Components/Modules/QuickPatch.cpp @@ -951,7 +951,7 @@ namespace Components if (!Game::CL_IsCgameInitialized() || !Dvar::Var("r_drawAabbTrees").get()) return; float cyan[4] = { 0.0f, 0.5f, 0.5f, 1.0f }; - float red[4] = { 1.0f, 0.0f, 0.0f, 1.0f }; + float red[4] = { 1.0f, 0.0f, 0.0f, 1.0f }; Game::clipMap_t* clipMap = *reinterpret_cast(0x7998E0); //Game::GfxWorld* gameWorld = *reinterpret_cast(0x66DEE94); From 462148a4b05e8f6fb841a7eade07da25ff0aab8d Mon Sep 17 00:00:00 2001 From: rackover Date: Wed, 28 Apr 2021 20:23:56 +0200 Subject: [PATCH 37/37] Fixed signed/unsigned masquerade --- src/Components/Modules/QuickPatch.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Components/Modules/QuickPatch.cpp b/src/Components/Modules/QuickPatch.cpp index 7c8640b0..4b0e78b3 100644 --- a/src/Components/Modules/QuickPatch.cpp +++ b/src/Components/Modules/QuickPatch.cpp @@ -1000,7 +1000,7 @@ namespace Components for (auto i = 0; i < scene->sceneDObjCount; i++) { if (scene->sceneDObj[i].obj) { - for (size_t j = 0; j < scene->sceneDObj[i].obj->numModels; j++) + for (int j = 0; j < scene->sceneDObj[i].obj->numModels; j++) { Game::R_AddDebugString(dobjsColor, scene->sceneDObj[i].placement.origin, 1.0, scene->sceneDObj[i].obj->models[j]->name); } @@ -1008,7 +1008,7 @@ namespace Components } // Static models - for (auto i = 0; i < world->dpvs.smodelCount; i++) + for (size_t i = 0; i < world->dpvs.smodelCount; i++) { auto staticModel = world->dpvs.smodelDrawInsts[i]; if (staticModel.model) {