From 888b33a9c6363467f781d37172d5d084b474f42e Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 27 Mar 2021 00:01:33 +0100 Subject: [PATCH] Add AssetLoader for attachments --- src/Common/Game/T6/T6_Assets.h | 25 ++ .../T6/AssetLoaders/AssetLoaderWeapon.cpp | 108 ++++++++ .../Game/T6/AssetLoaders/AssetLoaderWeapon.h | 8 + .../AssetLoaderWeaponAttachment.cpp | 138 ++++++++++ .../AssetLoaderWeaponAttachment.h | 18 ++ .../AssetLoaderWeaponAttachmentUnique.cpp | 247 ++++++++++++++++++ .../AssetLoaderWeaponAttachmentUnique.h | 21 ++ src/ObjLoading/Game/T6/ObjLoaderT6.cpp | 6 +- 8 files changed, 569 insertions(+), 2 deletions(-) create mode 100644 src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderWeaponAttachment.cpp create mode 100644 src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderWeaponAttachment.h create mode 100644 src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderWeaponAttachmentUnique.cpp create mode 100644 src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderWeaponAttachmentUnique.h diff --git a/src/Common/Game/T6/T6_Assets.h b/src/Common/Game/T6/T6_Assets.h index 2ea8fffa..f2912804 100644 --- a/src/Common/Game/T6/T6_Assets.h +++ b/src/Common/Game/T6/T6_Assets.h @@ -1547,6 +1547,31 @@ namespace T6 WEAPON_FIRETYPECOUNT = 0xA, }; + enum eAttachmentOverrideSounds + { + ATTACHMENT_OVERRIDE_SOUND_FIRE, + ATTACHMENT_OVERRIDE_SOUND_FIRE_PLAYER, + ATTACHMENT_OVERRIDE_SOUND_FIRE_LOOP, + ATTACHMENT_OVERRIDE_SOUND_FIRE_LOOP_PLAYER, + ATTACHMENT_OVERRIDE_SOUND_FIRE_LOOP_END, + ATTACHMENT_OVERRIDE_SOUND_FIRE_LOOP_END_PLAYER, + ATTACHMENT_OVERRIDE_SOUND_FIRE_START, + ATTACHMENT_OVERRIDE_SOUND_FIRE_STOP, + ATTACHMENT_OVERRIDE_SOUND_FIRE_START_PLAYER, + ATTACHMENT_OVERRIDE_SOUND_FIRE_STOP_PLAYER, + ATTACHMENT_OVERRIDE_SOUND_FIRE_LAST, + ATTACHMENT_OVERRIDE_SOUND_FIRE_LAST_PLAYER, + + NUM_ATTACHMENT_OVERRIDE_SOUNDS + }; + + enum eAttachmentOverrideEffects + { + ATTACHMENT_OVERRIDE_EFFECT_VIEW_FLASH, + ATTACHMENT_OVERRIDE_EFFECT_WORLD_FLASH, + + NUM_ATTACHMENT_OVERRIDE_EFFECTS + }; struct WeaponAttachment { diff --git a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderWeapon.cpp b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderWeapon.cpp index 7ce3f44a..f2bd0291 100644 --- a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderWeapon.cpp +++ b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderWeapon.cpp @@ -4,6 +4,8 @@ #include #include + +#include "AssetLoaderWeaponAttachmentUnique.h" #include "Utils/ClassUtils.h" #include "Game/T6/ObjConstantsT6.h" #include "Game/T6/T6.h" @@ -399,6 +401,111 @@ void AssetLoaderWeapon::CalculateWeaponFields(WeaponFullDef* weapon) weapon->weapVariantDef.fOOPosAnimLength[1] = 1.0f / static_cast(weapon->weapVariantDef.iAdsTransOutTime); } +bool AssetLoaderWeapon::IsStringOverride(const char* str1, const char* str2) +{ + if ((str1 == nullptr) != (str2 == nullptr)) + return true; + + if (str1 == nullptr) + return false; + + return strcmp(str1, str2) != 0; +} + +bool AssetLoaderWeapon::IsFxOverride(FxEffectDef* effect1, FxEffectDef* effect2) +{ + if ((effect1 == nullptr) != (effect2 == nullptr)) + return true; + + if (effect1 == nullptr) + return false; + + return strcmp(effect1->name, effect2->name) != 0; +} + +void AssetLoaderWeapon::HandleSoundOverride(WeaponAttachmentUnique* attachmentUnique, const char* snd1, const char* snd2, const eAttachmentOverrideSounds sndOverrideIndex) +{ + if (IsStringOverride(snd1, snd2)) + attachmentUnique->soundOverrides |= 1 << static_cast(sndOverrideIndex); +} + +void AssetLoaderWeapon::HandleFxOverride(WeaponAttachmentUnique* attachmentUnique, FxEffectDef* effect1, FxEffectDef* effect2, const eAttachmentOverrideEffects fxOverrideIndex) +{ + if (IsFxOverride(effect1, effect2)) + attachmentUnique->effectOverrides |= 1 << static_cast(fxOverrideIndex); +} + +void AssetLoaderWeapon::CalculateAttachmentFields(WeaponFullDef* weapon, unsigned attachmentIndex, WeaponAttachmentUnique* attachmentUnique) +{ + for (auto& val : attachmentUnique->animationOverrides) + val = 0; + + for (auto animIndex = 0u; animIndex < std::extent::value; animIndex++) + { + if (IsStringOverride(weapon->szXAnims[animIndex], attachmentUnique->szXAnims[animIndex])) + attachmentUnique->animationOverrides[animIndex / 32] |= 1 << (animIndex % 32); + } + + attachmentUnique->soundOverrides = 0; + HandleSoundOverride(attachmentUnique, weapon->weapDef.fireSound, attachmentUnique->fireSound, ATTACHMENT_OVERRIDE_SOUND_FIRE); + HandleSoundOverride(attachmentUnique, weapon->weapDef.fireSoundPlayer, attachmentUnique->fireSoundPlayer, ATTACHMENT_OVERRIDE_SOUND_FIRE_PLAYER); + HandleSoundOverride(attachmentUnique, weapon->weapDef.fireLoopSound, attachmentUnique->fireLoopSound, ATTACHMENT_OVERRIDE_SOUND_FIRE_LOOP); + HandleSoundOverride(attachmentUnique, weapon->weapDef.fireLoopSoundPlayer, attachmentUnique->fireLoopSoundPlayer, ATTACHMENT_OVERRIDE_SOUND_FIRE_LOOP_PLAYER); + HandleSoundOverride(attachmentUnique, weapon->weapDef.fireLoopEndSound, attachmentUnique->fireLoopEndSound, ATTACHMENT_OVERRIDE_SOUND_FIRE_LOOP_END); + HandleSoundOverride(attachmentUnique, weapon->weapDef.fireLoopEndSoundPlayer, attachmentUnique->fireLoopEndSoundPlayer, ATTACHMENT_OVERRIDE_SOUND_FIRE_LOOP_END_PLAYER); + HandleSoundOverride(attachmentUnique, weapon->weapDef.fireStartSound, attachmentUnique->fireStartSound, ATTACHMENT_OVERRIDE_SOUND_FIRE_START); + HandleSoundOverride(attachmentUnique, weapon->weapDef.fireStopSound, attachmentUnique->fireStopSound, ATTACHMENT_OVERRIDE_SOUND_FIRE_STOP); + HandleSoundOverride(attachmentUnique, weapon->weapDef.fireStartSoundPlayer, attachmentUnique->fireStartSoundPlayer, ATTACHMENT_OVERRIDE_SOUND_FIRE_START_PLAYER); + HandleSoundOverride(attachmentUnique, weapon->weapDef.fireStopSoundPlayer, attachmentUnique->fireStopSoundPlayer, ATTACHMENT_OVERRIDE_SOUND_FIRE_STOP_PLAYER); + HandleSoundOverride(attachmentUnique, weapon->weapDef.fireLastSound, attachmentUnique->fireLastSound, ATTACHMENT_OVERRIDE_SOUND_FIRE_LAST); + HandleSoundOverride(attachmentUnique, weapon->weapDef.fireLastSoundPlayer, attachmentUnique->fireLastSoundPlayer, ATTACHMENT_OVERRIDE_SOUND_FIRE_LAST_PLAYER); + + attachmentUnique->effectOverrides = 0; + HandleFxOverride(attachmentUnique, weapon->weapDef.viewFlashEffect, attachmentUnique->viewFlashEffect, ATTACHMENT_OVERRIDE_EFFECT_VIEW_FLASH); + HandleFxOverride(attachmentUnique, weapon->weapDef.worldFlashEffect, attachmentUnique->worldFlashEffect, ATTACHMENT_OVERRIDE_EFFECT_WORLD_FLASH); + + attachmentUnique->childLink = 0; + if (attachmentUnique->combinedAttachmentTypeMask == 0) + { + WeaponAttachmentUnique* lastSibling = nullptr; + for (auto attachmentUniqueIndex = std::extent::value; attachmentUniqueIndex < std::extent::value; + attachmentUniqueIndex++) + { + if (weapon->attachmentUniques[attachmentUniqueIndex] != nullptr + && weapon->attachmentUniques[attachmentUniqueIndex]->combinedAttachmentTypeMask & (1 << static_cast(attachmentUnique->attachmentType)) + && weapon->attachmentUniques[attachmentUniqueIndex]->attachmentType != attachmentUnique->attachmentType) + { + std::vector attachments; + if(AssetLoaderWeaponAttachmentUnique::ExtractAttachmentsFromAssetName(weapon->attachmentUniques[attachmentUniqueIndex]->szInternalName, attachments) + && attachments.front() == attachmentUnique->attachmentType) + { + if (lastSibling == nullptr) + { + attachmentUnique->childLink = attachmentUniqueIndex; + lastSibling = weapon->attachmentUniques[attachmentUniqueIndex]; + } + else + { + lastSibling->siblingLink = attachmentUniqueIndex; + lastSibling = weapon->attachmentUniques[attachmentUniqueIndex]; + } + } + } + } + } +} + +void AssetLoaderWeapon::CalculateAttachmentFields(WeaponFullDef* weapon) +{ + for (auto attachmentUniqueIndex = 0u; attachmentUniqueIndex < std::extent::value; attachmentUniqueIndex++) + { + if (weapon->attachmentUniques[attachmentUniqueIndex] == nullptr) + continue; + + CalculateAttachmentFields(weapon, attachmentUniqueIndex, weapon->attachmentUniques[attachmentUniqueIndex]); + } +} + void* AssetLoaderWeapon::CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) { auto* weaponFullDef = memory->Create(); @@ -443,6 +550,7 @@ bool AssetLoaderWeapon::LoadFromRaw(const std::string& assetName, ISearchPath* s // TODO: Load accuracy graph and flametable CalculateWeaponFields(weaponFullDef); + CalculateAttachmentFields(weaponFullDef); manager->AddAsset(ASSET_TYPE_WEAPON, assetName, &weaponFullDef->weapVariantDef, converter.GetDependencies(), converter.GetUsedScriptStrings()); diff --git a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderWeapon.h b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderWeapon.h index 398ad63a..080bf937 100644 --- a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderWeapon.h +++ b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderWeapon.h @@ -9,7 +9,15 @@ namespace T6 class AssetLoaderWeapon final : public BasicAssetLoader { static void LinkWeaponFullDefSubStructs(WeaponFullDef* weapon); + + static bool IsStringOverride(const char* str1, const char* str2); + static bool IsFxOverride(FxEffectDef* effect1, FxEffectDef* effect2); + static void HandleSoundOverride(WeaponAttachmentUnique* attachmentUnique, const char* snd1, const char* snd2, eAttachmentOverrideSounds sndOverrideIndex); + static void HandleFxOverride(WeaponAttachmentUnique* attachmentUnique, FxEffectDef* effect1, FxEffectDef* effect2, eAttachmentOverrideEffects fxOverrideIndex); + static void CalculateWeaponFields(WeaponFullDef* weapon); + static void CalculateAttachmentFields(WeaponFullDef* weapon, unsigned attachmentIndex, WeaponAttachmentUnique* attachmentUnique); + static void CalculateAttachmentFields(WeaponFullDef* weapon); public: _NODISCARD void* CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) override; diff --git a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderWeaponAttachment.cpp b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderWeaponAttachment.cpp new file mode 100644 index 00000000..c4533fcf --- /dev/null +++ b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderWeaponAttachment.cpp @@ -0,0 +1,138 @@ +#include "AssetLoaderWeaponAttachment.h" + +#include +#include +#include + +#include "Game/T6/ObjConstantsT6.h" +#include "Game/T6/T6.h" +#include "Game/T6/InfoString/EnumStrings.h" +#include "Game/T6/InfoString/InfoStringToStructConverter.h" +#include "Game/T6/InfoString/WeaponAttachmentFields.h" +#include "InfoString/InfoString.h" + +using namespace T6; + +namespace T6 +{ + eAttachmentPoint attachmentPointByAttachmentTable[] + { + ATTACHMENT_POINT_NONE, // none + ATTACHMENT_POINT_TOP, // acog + ATTACHMENT_POINT_TRIGGER, // dualclip + ATTACHMENT_POINT_TOP, // dualoptic + ATTACHMENT_POINT_BOTTOM, // dw + ATTACHMENT_POINT_MUZZLE, // extbarrel + ATTACHMENT_POINT_TRIGGER, // extclip + ATTACHMENT_POINT_TRIGGER, // extramags + ATTACHMENT_POINT_GUNPERK, // fastads + ATTACHMENT_POINT_TOP, // fastreload + ATTACHMENT_POINT_TRIGGER, // fmj + ATTACHMENT_POINT_BOTTOM, // gl + ATTACHMENT_POINT_BOTTOM, // grip + ATTACHMENT_POINT_TOP, // holo + ATTACHMENT_POINT_BOTTOM, // ir + ATTACHMENT_POINT_BOTTOM, // is + ATTACHMENT_POINT_GUNPERK, // longbreath + ATTACHMENT_POINT_BOTTOM, // mk + ATTACHMENT_POINT_TOP, // mms + ATTACHMENT_POINT_TOP, // rangefinder + ATTACHMENT_POINT_TOP, // reflex + ATTACHMENT_POINT_MUZZLE, // rf + ATTACHMENT_POINT_BOTTOM, // sf + ATTACHMENT_POINT_MUZZLE, // silencer + ATTACHMENT_POINT_TRIGGER, // stackfire + ATTACHMENT_POINT_GUNPERK, // stalker + ATTACHMENT_POINT_GUNPERK, // steadyaim + ATTACHMENT_POINT_GUNPERK, // swayreduc + ATTACHMENT_POINT_TOP, // tacknife + ATTACHMENT_POINT_TOP, // vzoom + }; + + static_assert(std::extent::value == ATTACHMENT_TYPE_COUNT); + + class InfoStringToWeaponAttachmentConverter final : public InfoStringToStructConverter + { + protected: + bool ConvertExtensionField(const cspField_t& field, const std::string& value) override + { + switch (static_cast(field.iFieldType)) + { + case AFT_ATTACHMENTTYPE: + return ConvertEnumInt(value, field.iOffset, szAttachmentTypeNames, std::extent::value); + + case AFT_PENETRATE_TYPE: + return ConvertEnumInt(value, field.iOffset, penetrateTypeNames, std::extent::value); + + case AFT_FIRETYPE: + return ConvertEnumInt(value, field.iOffset, szWeapFireTypeNames, std::extent::value); + + default: + assert(false); + return false; + } + } + + public: + InfoStringToWeaponAttachmentConverter(const InfoString& infoString, WeaponAttachment* weaponAttachment, ZoneScriptStrings& zoneScriptStrings, MemoryManager* memory, IAssetLoadingManager* manager, + const cspField_t* fields, const size_t fieldCount) + : InfoStringToStructConverter(infoString, weaponAttachment, zoneScriptStrings, memory, manager, fields, fieldCount) + { + } + }; +} + +void AssetLoaderWeaponAttachment::CalculateAttachmentFields(WeaponAttachment* attachment) +{ + // attachmentPoint + if(static_cast(attachment->attachmentType) < ATTACHMENT_TYPE_COUNT) + { + attachment->attachmentPoint = attachmentPointByAttachmentTable[attachment->attachmentType]; + } +} + +void* AssetLoaderWeaponAttachment::CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) +{ + auto* attachment = memory->Create(); + memset(attachment, 0, sizeof(WeaponAttachment)); + CalculateAttachmentFields(attachment); + attachment->szInternalName = memory->Dup(assetName.c_str()); + return attachment; +} + +bool AssetLoaderWeaponAttachment::CanLoadFromRaw() const +{ + return true; +} + +bool AssetLoaderWeaponAttachment::LoadFromRaw(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const +{ + const auto fileName = "attachment/" + assetName; + const auto file = searchPath->Open(fileName); + if (!file.IsOpen()) + return false; + + InfoString infoString; + if (!infoString.FromStream(ObjConstants::INFO_STRING_PREFIX_WEAPON_ATTACHMENT, *file.m_stream)) + { + std::cout << "Failed to read attachment raw file: \"" << fileName << "\"" << std::endl; + return true; + } + + auto* attachment = memory->Create(); + memset(attachment, 0, sizeof(WeaponAttachment)); + + InfoStringToWeaponAttachmentConverter converter(infoString, attachment, zone->m_script_strings, memory, manager, attachment_fields, std::extent::value); + if (!converter.Convert()) + { + std::cout << "Failed to parse attachment raw file: \"" << fileName << "\"" << std::endl; + return true; + } + + CalculateAttachmentFields(attachment); + attachment->szInternalName = memory->Dup(assetName.c_str()); + + manager->AddAsset(ASSET_TYPE_ATTACHMENT, assetName, attachment, converter.GetDependencies(), converter.GetUsedScriptStrings()); + + return true; +} diff --git a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderWeaponAttachment.h b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderWeaponAttachment.h new file mode 100644 index 00000000..e6d96350 --- /dev/null +++ b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderWeaponAttachment.h @@ -0,0 +1,18 @@ +#pragma once +#include "Game/T6/T6.h" +#include "AssetLoading/BasicAssetLoader.h" +#include "AssetLoading/IAssetLoadingManager.h" +#include "SearchPath/ISearchPath.h" + +namespace T6 +{ + class AssetLoaderWeaponAttachment final : public BasicAssetLoader + { + static void CalculateAttachmentFields(WeaponAttachment* attachment); + + public: + _NODISCARD void* CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) override; + _NODISCARD bool CanLoadFromRaw() const override; + bool LoadFromRaw(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const override; + }; +} diff --git a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderWeaponAttachmentUnique.cpp b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderWeaponAttachmentUnique.cpp new file mode 100644 index 00000000..6a889a0f --- /dev/null +++ b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderWeaponAttachmentUnique.cpp @@ -0,0 +1,247 @@ +#include "AssetLoaderWeaponAttachmentUnique.h" + +#include +#include +#include + +#include "Utils/ClassUtils.h" +#include "Game/T6/ObjConstantsT6.h" +#include "Game/T6/T6.h" +#include "Game/T6/InfoString/EnumStrings.h" +#include "Game/T6/InfoString/InfoStringToStructConverter.h" +#include "Game/T6/InfoString/WeaponAttachmentUniqueFields.h" +#include "InfoString/InfoString.h" + +using namespace T6; + +namespace T6 +{ + class InfoStringToWeaponAttachmentUniqueConverter final : public InfoStringToStructConverter + { + bool ConvertHideTags(const cspField_t& field, const std::string& value) + { + std::vector valueArray; + if (!ParseAsArray(value, valueArray)) + { + std::cout << "Failed to parse hide tags as array" << std::endl; + return false; + } + + if (valueArray.size() > std::extent::value) + { + std::cout << "Cannot have more than " << std::extent::value << " hide tags!" << std::endl; + return false; + } + + auto* hideTags = reinterpret_cast(reinterpret_cast(m_structure) + field.iOffset); + auto currentHideTag = 0u; + + if (valueArray.size() < std::extent::value) + { + m_used_script_string_list.emplace(m_zone_script_strings.AddOrGetScriptString("")); + } + + for (; currentHideTag < valueArray.size(); currentHideTag++) + { + const auto scrString = m_zone_script_strings.AddOrGetScriptString(valueArray[currentHideTag]); + hideTags[currentHideTag] = scrString; + m_used_script_string_list.emplace(scrString); + } + + for (; currentHideTag < std::extent::value; currentHideTag++) + { + hideTags[currentHideTag] = m_zone_script_strings.GetScriptString(""); + } + + return true; + } + + _NODISCARD bool ConvertWeaponCamo(const cspField_t& field, const std::string& value) + { + if (value.empty()) + { + *reinterpret_cast(reinterpret_cast(m_structure) + field.iOffset) = nullptr; + return true; + } + + auto* camo = m_loading_manager->LoadDependency(ASSET_TYPE_WEAPON_CAMO, value); + + if (camo == nullptr) + { + std::cout << "Failed to load camo asset \"" << value << "\"" << std::endl; + return false; + } + + m_dependencies.emplace(camo); + *reinterpret_cast(reinterpret_cast(m_structure) + field.iOffset) = camo->m_ptr; + + return true; + } + + protected: + bool ConvertExtensionField(const cspField_t& field, const std::string& value) override + { + switch (static_cast(field.iFieldType)) + { + case AUFT_ATTACHMENTTYPE: + return ConvertEnumInt(value, field.iOffset, szAttachmentTypeNames, std::extent::value); + + case AUFT_HIDETAGS: + return ConvertHideTags(field, value); + + case AUFT_OVERLAYRETICLE: + return ConvertEnumInt(value, field.iOffset, szWeapOverlayReticleNames, std::extent::value); + + case AUFT_CAMO: + return ConvertWeaponCamo(field, value); + + default: + assert(false); + return false; + } + } + + public: + InfoStringToWeaponAttachmentUniqueConverter(const InfoString& infoString, WeaponAttachmentUniqueFull* attachmentUniqueFull, ZoneScriptStrings& zoneScriptStrings, MemoryManager* memory, IAssetLoadingManager* manager, + const cspField_t* fields, const size_t fieldCount) + : InfoStringToStructConverter(infoString, attachmentUniqueFull, zoneScriptStrings, memory, manager, fields, fieldCount) + { + } + }; +} + +bool AssetLoaderWeaponAttachmentUnique::ExtractAttachmentsFromAssetName(const std::string& assetName, std::vector& attachmentList) +{ + std::vector parts; + + auto attachCount = 1u; + auto partStart = 0u; + for(auto ci = 0u; ci < assetName.size(); ci++) + { + if(assetName[ci] == '_') + { + parts.emplace_back(assetName, partStart, ci - partStart); + partStart = ci + 1; + } + else if(assetName[ci] == '+') + { + attachCount++; + parts.emplace_back(assetName, partStart, ci - partStart); + partStart = ci + 1; + } + } + + if(partStart < assetName.size()) + parts.emplace_back(assetName, partStart, assetName.size() - partStart); + + for(auto attachPartOffset = parts.size() - attachCount; attachPartOffset < parts.size(); attachPartOffset++) + { + auto& specifiedAttachName = parts[attachPartOffset]; + + for (auto& c : specifiedAttachName) + c = static_cast(tolower(c)); + + auto foundAttachment = false; + for(auto attachIndex = 0u; attachIndex < std::extent::value; attachIndex++) + { + if(specifiedAttachName == szAttachmentTypeNames[attachIndex]) + { + attachmentList.push_back(static_cast(attachIndex)); + foundAttachment = true; + break; + } + } + + if(!foundAttachment) + return false; + } + + return true; +} + +void AssetLoaderWeaponAttachmentUnique::LinkAttachmentUniqueFullSubStructs(WeaponAttachmentUniqueFull* attachmentUnique) +{ + attachmentUnique->attachment.hideTags = attachmentUnique->hideTags; + attachmentUnique->attachment.szXAnims = attachmentUnique->szXAnims; + attachmentUnique->attachment.locationDamageMultipliers = attachmentUnique->locationDamageMultipliers; +} + +bool AssetLoaderWeaponAttachmentUnique::CalculateAttachmentUniqueFields(const std::string& assetName, WeaponAttachmentUniqueFull* attachmentUnique) +{ + // combinedAttachmentTypeMask + std::vector attachmentsFromName; + if(!ExtractAttachmentsFromAssetName(assetName, attachmentsFromName)) + { + std::cout << "Failed to determine attachments from attachment unique name \"" << assetName << "\"" << std::endl; + return false; + } + + if (attachmentsFromName.size() > 1) + { + for(auto attachment : attachmentsFromName) + { + attachmentUnique->attachment.combinedAttachmentTypeMask |= 1 << attachment; + } + } + + // animationOverrides + // siblingLink + // childLink + // soundOverrides + // effectOverrides + + return true; +} + +void* AssetLoaderWeaponAttachmentUnique::CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) +{ + auto* attachmentUniqueFull = memory->Create(); + memset(attachmentUniqueFull, 0, sizeof(WeaponAttachmentUniqueFull)); + LinkAttachmentUniqueFullSubStructs(attachmentUniqueFull); + CalculateAttachmentUniqueFields(assetName, attachmentUniqueFull); + attachmentUniqueFull->attachment.szInternalName = memory->Dup(assetName.c_str()); + return attachmentUniqueFull; +} + +bool AssetLoaderWeaponAttachmentUnique::CanLoadFromRaw() const +{ + return true; +} + +bool AssetLoaderWeaponAttachmentUnique::LoadFromRaw(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const +{ + const auto fileName = "attachmentunique/" + assetName; + const auto file = searchPath->Open(fileName); + if (!file.IsOpen()) + return false; + + InfoString infoString; + if (!infoString.FromStream(ObjConstants::INFO_STRING_PREFIX_WEAPON_ATTACHMENT_UNIQUE, *file.m_stream)) + { + std::cout << "Failed to read attachment unique raw file: \"" << fileName << "\"" << std::endl; + return true; + } + + auto* attachmentUniqueFull = memory->Create(); + memset(attachmentUniqueFull, 0, sizeof(WeaponAttachmentUniqueFull)); + LinkAttachmentUniqueFullSubStructs(attachmentUniqueFull); + + InfoStringToWeaponAttachmentUniqueConverter converter(infoString, attachmentUniqueFull, zone->m_script_strings, memory, manager, attachment_unique_fields, std::extent::value); + if (!converter.Convert()) + { + std::cout << "Failed to parse attachment unique raw file: \"" << fileName << "\"" << std::endl; + return true; + } + + if (!CalculateAttachmentUniqueFields(assetName, attachmentUniqueFull)) + return true; + + attachmentUniqueFull->attachment.szInternalName = memory->Dup(assetName.c_str()); + + auto* assetInfo = GlobalAssetPool::GetAssetByName(assetName); + auto* asset = assetInfo ? assetInfo->Asset() : nullptr; + + manager->AddAsset(ASSET_TYPE_ATTACHMENT_UNIQUE, assetName, &attachmentUniqueFull->attachment, converter.GetDependencies(), converter.GetUsedScriptStrings()); + + return true; +} diff --git a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderWeaponAttachmentUnique.h b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderWeaponAttachmentUnique.h new file mode 100644 index 00000000..8ce7a7e4 --- /dev/null +++ b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderWeaponAttachmentUnique.h @@ -0,0 +1,21 @@ +#pragma once +#include "Game/T6/T6.h" +#include "AssetLoading/BasicAssetLoader.h" +#include "AssetLoading/IAssetLoadingManager.h" +#include "SearchPath/ISearchPath.h" + +namespace T6 +{ + class AssetLoaderWeaponAttachmentUnique final : public BasicAssetLoader + { + static void LinkAttachmentUniqueFullSubStructs(WeaponAttachmentUniqueFull* attachmentUnique); + static bool CalculateAttachmentUniqueFields(const std::string& assetName, WeaponAttachmentUniqueFull* attachmentUnique); + + public: + static bool ExtractAttachmentsFromAssetName(const std::string& assetName, std::vector& attachmentList); + + _NODISCARD void* CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) override; + _NODISCARD bool CanLoadFromRaw() const override; + bool LoadFromRaw(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const override; + }; +} diff --git a/src/ObjLoading/Game/T6/ObjLoaderT6.cpp b/src/ObjLoading/Game/T6/ObjLoaderT6.cpp index 411c1b46..c7b85959 100644 --- a/src/ObjLoading/Game/T6/ObjLoaderT6.cpp +++ b/src/ObjLoading/Game/T6/ObjLoaderT6.cpp @@ -13,6 +13,8 @@ #include "AssetLoaders/AssetLoaderStringTable.h" #include "AssetLoaders/AssetLoaderVehicle.h" #include "AssetLoaders/AssetLoaderWeapon.h" +#include "AssetLoaders/AssetLoaderWeaponAttachment.h" +#include "AssetLoaders/AssetLoaderWeaponAttachmentUnique.h" #include "AssetLoading/AssetLoadingManager.h" #include "Image/Texture.h" #include "Image/IwiLoader.h" @@ -52,8 +54,8 @@ namespace T6 REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_MENU, menuDef_t)) REGISTER_ASSET_LOADER(AssetLoaderLocalizeEntry) REGISTER_ASSET_LOADER(AssetLoaderWeapon) - REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_ATTACHMENT, WeaponAttachment)) - REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_ATTACHMENT_UNIQUE, WeaponAttachmentUnique)) + REGISTER_ASSET_LOADER(AssetLoaderWeaponAttachment) + REGISTER_ASSET_LOADER(AssetLoaderWeaponAttachmentUnique) REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_WEAPON_CAMO, WeaponCamo)) REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_SNDDRIVER_GLOBALS, SndDriverGlobals)) REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_FX, FxEffectDef))