From b5dd6df1cd85edc4cb9c2faa87355414202b7c00 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 12 Jan 2024 16:38:53 -0500 Subject: [PATCH 01/25] begin working on SndBank loading from raw --- src/ObjCommon/Game/T6/ObjConstantsT6.cpp | 137 ++++++++++ src/ObjCommon/Game/T6/ObjConstantsT6.h | 11 + .../T6/AssetLoaders/AssetLoaderSoundBank.cpp | 254 ++++++++++++++++++ .../T6/AssetLoaders/AssetLoaderSoundBank.h | 17 ++ src/ObjLoading/Game/T6/ObjLoaderT6.cpp | 3 +- .../T6/AssetDumpers/AssetDumperSndBank.cpp | 165 ++---------- src/Utils/Utils/StringUtils.cpp | 15 ++ src/Utils/Utils/StringUtils.h | 3 + 8 files changed, 456 insertions(+), 149 deletions(-) create mode 100644 src/ObjCommon/Game/T6/ObjConstantsT6.cpp create mode 100644 src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp create mode 100644 src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.h diff --git a/src/ObjCommon/Game/T6/ObjConstantsT6.cpp b/src/ObjCommon/Game/T6/ObjConstantsT6.cpp new file mode 100644 index 000000000..3ac97809d --- /dev/null +++ b/src/ObjCommon/Game/T6/ObjConstantsT6.cpp @@ -0,0 +1,137 @@ +#include "ObjConstantsT6.h" + +namespace T6 +{ + const std::array ObjConstants::SOUND_GROUPS{ + "grp_reference", + "grp_master", + "grp_wpn_lfe", + "grp_lfe", + "grp_hdrfx", + "grp_music", + "grp_voice", + "grp_set_piece", + "grp_igc", + "grp_mp_game", + "grp_explosion", + "grp_player_impacts", + "grp_scripted_moment", + "grp_menu", + "grp_whizby", + "grp_weapon", + "grp_vehicle", + "grp_impacts", + "grp_foley", + "grp_destructible", + "grp_physics", + "grp_ambience", + "grp_alerts", + "grp_air", + "grp_bink", + "grp_announcer", + "", + }; + + const std::array ObjConstants::SOUND_CURVES{ + "default", + "defaultmin", + "allon", + "alloff", + "rcurve0", + "rcurve1", + "rcurve2", + "rcurve3", + "rcurve4", + "rcurve5", + "steep", + "sindelay", + "cosdelay", + "sin", + "cos", + "rev60", + "rev65", + "", + }; + + const std::array ObjConstants::SOUND_DUCK_GROUPS{ + "snp_alerts_gameplay", + "snp_ambience", + "snp_claw", + "snp_destructible", + "snp_dying", + "snp_dying_ice", + "snp_evt_2d", + "snp_explosion", + "snp_foley", + "snp_grenade", + "snp_hdrfx", + "snp_igc", + "snp_impacts", + "snp_menu", + "snp_movie", + "snp_music", + "snp_never_duck", + "snp_player_dead", + "snp_player_impacts", + "snp_scripted_moment", + "snp_set_piece", + "snp_special", + "snp_vehicle", + "snp_vehicle_interior", + "snp_voice", + "snp_weapon_decay_1p", + "snp_whizby", + "snp_wpn_1p", + "snp_wpn_3p", + "snp_wpn_turret", + "snp_x2", + "snp_x3", + }; + + const std::array ObjConstants::SOUND_LIMIT_TYPES{ + "none", + "oldest", + "reject", + "priority", + }; + + const std::array ObjConstants::SOUND_MOVE_TYPES{ + "none", + "left_player", + "center_player", + "right_player", + "random", + "left_shot", + "center_shot", + "right_shot", + }; + + const std::array ObjConstants::SOUND_LOAD_TYPES{ + "unknown", + "loaded", + "streamed", + "primed", + }; + + const std::array ObjConstants::SOUND_BUS_IDS{ + "bus_reverb", + "bus_fx", + "bus_voice", + "bus_pfutz", + "bus_hdrfx", + "bus_ui", + "bus_reference", + "bus_music", + "bus_movie", + "bus_reference", + "", + }; + + const std::array ObjConstants::SOUND_RANDOMIZE_TYPES{ + "volume", + "pitch", + "variant", + "", + }; + +} // namespace T6 diff --git a/src/ObjCommon/Game/T6/ObjConstantsT6.h b/src/ObjCommon/Game/T6/ObjConstantsT6.h index 73f1cad30..d5991d9ab 100644 --- a/src/ObjCommon/Game/T6/ObjConstantsT6.h +++ b/src/ObjCommon/Game/T6/ObjConstantsT6.h @@ -1,4 +1,6 @@ #pragma once +#include +#include namespace T6 { @@ -24,5 +26,14 @@ namespace T6 static constexpr const char* GDF_FILENAME_WEAPON_ATTACHMENT = "attachment.gdf"; static constexpr const char* GDF_FILENAME_WEAPON_ATTACHMENT_UNIQUE = "attachmentunique.gdf"; static constexpr const char* GDF_FILENAME_ZBARRIER = "zbarrier.gdf"; + + static const std::array SOUND_GROUPS; + static const std::array SOUND_CURVES; + static const std::array SOUND_DUCK_GROUPS; + static const std::array SOUND_LIMIT_TYPES; + static const std::array SOUND_MOVE_TYPES; + static const std::array SOUND_LOAD_TYPES; + static const std::array SOUND_BUS_IDS; + static const std::array SOUND_RANDOMIZE_TYPES; }; } // namespace T6 diff --git a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp new file mode 100644 index 000000000..d74aa12d9 --- /dev/null +++ b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp @@ -0,0 +1,254 @@ +#include "AssetLoaderSoundBank.h" + +#include "Csv/CsvStream.h" +#include "Game/T6/CommonT6.h" +#include "Game/T6/ObjConstantsT6.h" +#include "Game/T6/T6.h" +#include "Pool/GlobalAssetPool.h" + +#include +#include +#include + +using namespace T6; +std::unordered_map AliasHeaders{}; + +void* AssetLoaderSoundBank::CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) +{ + auto* soundBank = memory->Create(); + memset(soundBank, 0, sizeof(SndBank)); + soundBank->name = memory->Dup(assetName.c_str()); + return soundBank; +} + +bool AssetLoaderSoundBank::CanLoadFromRaw() const +{ + return true; +} + +void LoadSoundAliasHeader(const std::vector& values) +{ + for (auto i = 0u; i < values.size(); i++) + { + AliasHeaders[values[i]] = i; + } +} + +const char* GetSoundAliasValue(const std::string& header, const std::vector& values, bool required = false) +{ + if (AliasHeaders.find(header) == AliasHeaders.end()) + { + if (required) + std::cerr << "ERROR: Required column \"" << header << "\" was not found"; + else + std::cerr << "WARNING: Expected column \"" << header << "\" was not found"; + return nullptr; + } + + auto value = values.at(AliasHeaders[header]).c_str(); + if (required && (!value || !*value)) + { + std::cerr << "ERROR: Required column \"" << header << "\" does not have a value"; + return nullptr; + } + + return value; +} + +char* GetSoundAliasValueString(const std::string& header, const std::vector& values, MemoryManager* memory, bool required = false) +{ + const auto* value = GetSoundAliasValue(header, values, required); + return value ? memory->Dup(value) : nullptr; +} + +int32_t GetSoundAliasValueInt(const std::string& header, const std::vector& values, bool required = false) +{ + const auto* value = GetSoundAliasValue(header, values, required); + if (value && *value) + return std::stoi(value); + return 0; +} + +size_t GetSoundAliasValueIndex( + const std::string& header, const std::vector& values, const std::string* lookupTable, size_t len, bool required = false) +{ + const auto* value = GetSoundAliasValue(header, values, required); + if (!value || !*value) + return 0; + + for (auto i = 0u; i < len; i++) + { + if (lookupTable[i] == value) + return i; + } + + return 0; +} + +bool GetSoundAliasValueBool(const std::string& header, const std::vector& values, const std::string& comparison, bool required = false) +{ + const auto* value = GetSoundAliasValue(header, values, required); + if (value && *value) + return comparison == value; + return false; +} + +bool LoadSoundAlias(MemoryManager* memory, SndAlias* alias, const std::vector& values) +{ + const auto* name = GetSoundAliasValue("name", values, true); + if (name == nullptr) + return false; + + const auto* aliasFileName = GetSoundAliasValue("file", values, true); + if (aliasFileName == nullptr) + return false; + + alias->name = memory->Dup(name); + alias->id = Common::SND_HashName(name); + alias->assetFileName = memory->Dup(aliasFileName); + alias->assetId = Common::SND_HashName(aliasFileName); + alias->secondaryname = GetSoundAliasValueString("secondary", values, memory); + alias->subtitle = GetSoundAliasValueString("subtitle", values, memory); + + alias->duck = Common::SND_HashName(GetSoundAliasValue("duck", values)); + + alias->volMin = GetSoundAliasValueInt("vol_min", values); + alias->volMax = GetSoundAliasValueInt("vol_max", values); + alias->distMin = GetSoundAliasValueInt("dist_min", values); + alias->distMax = GetSoundAliasValueInt("dist_max", values); + alias->distReverbMax = GetSoundAliasValueInt("dist_reverb_max", values); + alias->limitCount = GetSoundAliasValueInt("limit_count", values); + alias->entityLimitCount = GetSoundAliasValueInt("entity_limit_count", values); + alias->pitchMin = GetSoundAliasValueInt("pitch_min", values); + alias->pitchMax = GetSoundAliasValueInt("pitch_max", values); + alias->minPriority = GetSoundAliasValueInt("min_priority", values); + alias->maxPriority = GetSoundAliasValueInt("max_priority", values); + alias->minPriorityThreshold = GetSoundAliasValueInt("min_priority_threshold", values); + alias->maxPriorityThreshold = GetSoundAliasValueInt("max_priority_threshold", values); + alias->probability = GetSoundAliasValueInt("probability", values); + alias->startDelay = GetSoundAliasValueInt("start_delay", values); + alias->reverbSend = GetSoundAliasValueInt("reverb_send", values); + alias->centerSend = GetSoundAliasValueInt("center_send", values); + alias->envelopMin = GetSoundAliasValueInt("envelop_min", values); + alias->envelopMax = GetSoundAliasValueInt("envelop_max", values); + alias->envelopPercentage = GetSoundAliasValueInt("envelop_percentage", values); + alias->occlusionLevel = GetSoundAliasValueInt("occlusion_level", values); + alias->fluxTime = GetSoundAliasValueInt("move_time", values); + alias->futzPatch = GetSoundAliasValueInt("futz", values); + alias->contextType = GetSoundAliasValueInt("context_type", values); + alias->contextValue = GetSoundAliasValueInt("context_value", values); + alias->fadeIn = GetSoundAliasValueInt("fade_in", values); + alias->fadeOut = GetSoundAliasValueInt("fade_out", values); + + alias->flags.looping = GetSoundAliasValueBool("loop", values, "looping"); + alias->flags.panType = GetSoundAliasValueBool("pan", values, "3d"); + alias->flags.isBig = GetSoundAliasValueBool("is_big", values, "yes"); + alias->flags.distanceLpf = GetSoundAliasValueBool("distance_lpf", values, "yes"); + alias->flags.doppler = GetSoundAliasValueBool("doppler", values, "yes"); + alias->flags.timescale = GetSoundAliasValueBool("timescale", values, "yes"); + alias->flags.isMusic = GetSoundAliasValueBool("music", values, "yes"); + alias->flags.pauseable = GetSoundAliasValueBool("pause", values, "yes"); + alias->flags.stopOnDeath = GetSoundAliasValueBool("stop_on_death", values, "yes"); + + alias->flags.volumeGroup = GetSoundAliasValueIndex("group", values, ObjConstants::SOUND_GROUPS.data(), ObjConstants::SOUND_GROUPS.size()); + alias->flags.fluxType = GetSoundAliasValueIndex("move_type", values, ObjConstants::SOUND_MOVE_TYPES.data(), ObjConstants::SOUND_MOVE_TYPES.size()); + alias->flags.loadType = GetSoundAliasValueIndex("type", values, ObjConstants::SOUND_LOAD_TYPES.data(), ObjConstants::SOUND_LOAD_TYPES.size()); + alias->flags.busType = GetSoundAliasValueIndex("bus", values, ObjConstants::SOUND_BUS_IDS.data(), ObjConstants::SOUND_BUS_IDS.size()); + alias->flags.limitType = GetSoundAliasValueIndex("limit_type", values, ObjConstants::SOUND_LIMIT_TYPES.data(), ObjConstants::SOUND_LIMIT_TYPES.size()); + alias->flags.entityLimitType = + GetSoundAliasValueIndex("entity_limit_type", values, ObjConstants::SOUND_LIMIT_TYPES.data(), ObjConstants::SOUND_LIMIT_TYPES.size()); + alias->flags.volumeFalloffCurve = + GetSoundAliasValueIndex("volume_falloff_curve", values, ObjConstants::SOUND_CURVES.data(), ObjConstants::SOUND_CURVES.size()); + alias->flags.reverbFalloffCurve = + GetSoundAliasValueIndex("reverb_falloff_curve", values, ObjConstants::SOUND_CURVES.data(), ObjConstants::SOUND_CURVES.size()); + alias->flags.volumeMinFalloffCurve = + GetSoundAliasValueIndex("volume_min_falloff_curve", values, ObjConstants::SOUND_CURVES.data(), ObjConstants::SOUND_CURVES.size()); + alias->flags.reverbMinFalloffCurve = + GetSoundAliasValueIndex("reverb_min_falloff_curve", values, ObjConstants::SOUND_CURVES.data(), ObjConstants::SOUND_CURVES.size()); + alias->flags.randomizeType = + GetSoundAliasValueIndex("type", values, ObjConstants::SOUND_RANDOMIZE_TYPES.data(), ObjConstants::SOUND_RANDOMIZE_TYPES.size()); + + return true; +} + +bool LoadSoundAliasList(MemoryManager* memory, SndBank* sndBank, const SearchPathOpenFile& file) +{ + const CsvInputStream aliasCsv(*file.m_stream); + std::vector> csvLines; + std::vector currentLine; + auto maxCols = 0u; + + while (aliasCsv.NextRow(currentLine)) + { + if (currentLine.size() > maxCols) + maxCols = currentLine.size(); + csvLines.emplace_back(std::move(currentLine)); + currentLine = std::vector(); + } + + // Ensure there is at least one entry in the csv after the headers + if (maxCols * csvLines.size() > maxCols) + { + sndBank->aliasCount = 1; + sndBank->alias = static_cast(memory->Alloc(sizeof(SndAliasList))); + + sndBank->alias->count = csvLines.size() - 1; + sndBank->alias->head = static_cast(memory->Alloc(sizeof(SndAlias) * sndBank->alias->count)); + sndBank->alias->sequence = 0; + + LoadSoundAliasHeader(csvLines[0]); + + for (auto row = 1u; row < csvLines.size(); row++) + { + const auto& aliasValues = csvLines[row]; + if (!LoadSoundAlias(memory, &sndBank->alias->head[row - 1], aliasValues)) + return false; + } + + sndBank->alias->id = sndBank->alias->head[0].id; + sndBank->alias->name = sndBank->alias->head[0].name; + } + + return true; +} + +bool AssetLoaderSoundBank::LoadFromRaw( + const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const +{ + if (assetName.find('.') == std::string::npos) + { + std::cerr << "A language must be specific in the soundbank asset name! (Ex: mpl_common.all)" << std::endl; + return false; + } + + // open the soundbank aliases + const auto aliasFile = searchPath->Open("soundbank/" + assetName + ".aliases.csv"); + if (!aliasFile.IsOpen()) + return false; + + // set the defaults + auto* sndBank = memory->Create(); + sndBank->name = memory->Dup(assetName.c_str()); + + auto sndBankLocalization = utils::StringSplit(assetName, '.'); + const auto* zoneName = memory->Dup(sndBankLocalization.at(0).c_str()); + const auto* languageName = memory->Dup(sndBankLocalization.at(1).c_str()); + + sndBank->streamAssetBank.zone = zoneName; + sndBank->streamAssetBank.language = languageName; + sndBank->loadAssetBank.zone = zoneName; + sndBank->loadAssetBank.language = languageName; + sndBank->loadedAssets.zone = zoneName; + sndBank->loadedAssets.language = languageName; + sndBank->loadedAssets.loadedCount = 0; + + if (!LoadSoundAliasList(memory, sndBank, aliasFile)) + return false; + + // open the soundbank reverbs + + // open the soundbank ducks + + manager->AddAsset(ASSET_TYPE_SOUND, assetName, sndBank); + return true; +} diff --git a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.h b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.h new file mode 100644 index 000000000..f7f8154db --- /dev/null +++ b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.h @@ -0,0 +1,17 @@ +#pragma once +#include "AssetLoading/BasicAssetLoader.h" +#include "AssetLoading/IAssetLoadingManager.h" +#include "Game/T6/T6.h" +#include "SearchPath/ISearchPath.h" + +namespace T6 +{ + class AssetLoaderSoundBank final : public BasicAssetLoader + { + 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; + }; +} // namespace T6 diff --git a/src/ObjLoading/Game/T6/ObjLoaderT6.cpp b/src/ObjLoading/Game/T6/ObjLoaderT6.cpp index 431a3ca42..dcff00e55 100644 --- a/src/ObjLoading/Game/T6/ObjLoaderT6.cpp +++ b/src/ObjLoading/Game/T6/ObjLoaderT6.cpp @@ -9,6 +9,7 @@ #include "AssetLoaders/AssetLoaderRawFile.h" #include "AssetLoaders/AssetLoaderScriptParseTree.h" #include "AssetLoaders/AssetLoaderSlug.h" +#include "AssetLoaders/AssetLoaderSoundBank.h" #include "AssetLoaders/AssetLoaderStringTable.h" #include "AssetLoaders/AssetLoaderTracer.h" #include "AssetLoaders/AssetLoaderVehicle.h" @@ -51,7 +52,7 @@ namespace T6 REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_MATERIAL, Material)) REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_TECHNIQUE_SET, MaterialTechniqueSet)) REGISTER_ASSET_LOADER(AssetLoaderGfxImage) - REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_SOUND, SndBank)) + REGISTER_ASSET_LOADER(AssetLoaderSoundBank) REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_SOUND_PATCH, SndPatch)) REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_CLIPMAP, clipMap_t)) REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_CLIPMAP_PVS, clipMap_t)) diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp index b36a4f3c9..c01cc4e35 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp @@ -2,6 +2,7 @@ #include "Csv/CsvStream.h" #include "Game/T6/CommonT6.h" +#include "Game/T6/ObjConstantsT6.h" #include "ObjContainer/SoundBank/SoundBank.h" #include "Sound/WavWriter.h" #include "nlohmann/json.hpp" @@ -118,147 +119,15 @@ namespace 192000, }; - const std::string GROUPS_ENUM[]{ - "grp_reference", - "grp_master", - "grp_wpn_lfe", - "grp_lfe", - "grp_hdrfx", - "grp_music", - "grp_voice", - "grp_set_piece", - "grp_igc", - "grp_mp_game", - "grp_explosion", - "grp_player_impacts", - "grp_scripted_moment", - "grp_menu", - "grp_whizby", - "grp_weapon", - "grp_vehicle", - "grp_impacts", - "grp_foley", - "grp_destructible", - "grp_physics", - "grp_ambience", - "grp_alerts", - "grp_air", - "grp_bink", - "grp_announcer", - "", - }; - - const std::string CURVES_ENUM[]{ - "default", - "defaultmin", - "allon", - "alloff", - "rcurve0", - "rcurve1", - "rcurve2", - "rcurve3", - "rcurve4", - "rcurve5", - "steep", - "sindelay", - "cosdelay", - "sin", - "cos", - "rev60", - "rev65", - "", - }; - std::unordered_map CreateCurvesMap() { std::unordered_map result; - for (auto i = 0u; i < std::extent_v; i++) - result.emplace(T6::Common::SND_HashName(CURVES_ENUM[i].data()), CURVES_ENUM[i]); + for (auto i = 0u; i < ObjConstants::SOUND_CURVES.size(); i++) + result.emplace(T6::Common::SND_HashName(ObjConstants::SOUND_CURVES[i].data()), ObjConstants::SOUND_CURVES[i]); return result; } const std::unordered_map CURVES_MAP = CreateCurvesMap(); - - const std::string DUCK_GROUPS_ENUM[]{ - "snp_alerts_gameplay", - "snp_ambience", - "snp_claw", - "snp_destructible", - "snp_dying", - "snp_dying_ice", - "snp_evt_2d", - "snp_explosion", - "snp_foley", - "snp_grenade", - "snp_hdrfx", - "snp_igc", - "snp_impacts", - "snp_menu", - "snp_movie", - "snp_music", - "snp_never_duck", - "snp_player_dead", - "snp_player_impacts", - "snp_scripted_moment", - "snp_set_piece", - "snp_special", - "snp_vehicle", - "snp_vehicle_interior", - "snp_voice", - "snp_weapon_decay_1p", - "snp_whizby", - "snp_wpn_1p", - "snp_wpn_3p", - "snp_wpn_turret", - "snp_x2", - "snp_x3", - }; - - const std::string LIMIT_TYPES_ENUM[]{ - "none", - "oldest", - "reject", - "priority", - }; - - const std::string MOVE_TYPES_ENUM[]{ - "none", - "left_player", - "center_player", - "right_player", - "random", - "left_shot", - "center_shot", - "right_shot", - }; - - const std::string LOAD_TYPES_ENUM[]{ - "unknown", - "loaded", - "streamed", - "primed", - }; - - const std::string BUS_IDS_ENUM[]{ - "bus_reverb", - "bus_fx", - "bus_voice", - "bus_pfutz", - "bus_hdrfx", - "bus_ui", - "bus_reference", - "bus_music", - "bus_movie", - "bus_reference", - "", - }; - - const std::string RANDOMIZE_TYPES_ENUM[]{ - "volume", - "pitch", - "variant", - "", - }; } // namespace class AssetDumperSndBank::Internal @@ -356,7 +225,7 @@ class AssetDumperSndBank::Internal stream.WriteColumn((alias->secondaryname && *alias->secondaryname) ? alias->secondaryname : ""); // group - stream.WriteColumn(GROUPS_ENUM[alias->flags.volumeGroup]); + stream.WriteColumn(ObjConstants::SOUND_GROUPS[alias->flags.volumeGroup]); // vol_min stream.WriteColumn(std::to_string(alias->volMin)); @@ -377,28 +246,28 @@ class AssetDumperSndBank::Internal stream.WriteColumn(std::to_string(alias->distReverbMax)); // volume_falloff_curve - stream.WriteColumn(CURVES_ENUM[alias->flags.volumeFalloffCurve]); + stream.WriteColumn(ObjConstants::SOUND_CURVES[alias->flags.volumeFalloffCurve]); // reverb_falloff_curve - stream.WriteColumn(CURVES_ENUM[alias->flags.reverbFalloffCurve]); + stream.WriteColumn(ObjConstants::SOUND_CURVES[alias->flags.reverbFalloffCurve]); // volume_min_falloff_curve - stream.WriteColumn(CURVES_ENUM[alias->flags.volumeMinFalloffCurve]); + stream.WriteColumn(ObjConstants::SOUND_CURVES[alias->flags.volumeMinFalloffCurve]); // reverb_min_falloff_curve" - stream.WriteColumn(CURVES_ENUM[alias->flags.reverbMinFalloffCurve]); + stream.WriteColumn(ObjConstants::SOUND_CURVES[alias->flags.reverbMinFalloffCurve]); // limit_count stream.WriteColumn(std::to_string(alias->limitCount)); // limit_type - stream.WriteColumn(LIMIT_TYPES_ENUM[alias->flags.limitType]); + stream.WriteColumn(ObjConstants::SOUND_LIMIT_TYPES[alias->flags.limitType]); // entity_limit_count stream.WriteColumn(std::to_string(alias->entityLimitCount)); // entity_limit_type - stream.WriteColumn(LIMIT_TYPES_ENUM[alias->flags.entityLimitType]); + stream.WriteColumn(ObjConstants::SOUND_LIMIT_TYPES[alias->flags.entityLimitType]); // pitch_min stream.WriteColumn(std::to_string(alias->pitchMin)); @@ -425,13 +294,13 @@ class AssetDumperSndBank::Internal stream.WriteColumn(""); // type - stream.WriteColumn(LOAD_TYPES_ENUM[alias->flags.loadType]); + stream.WriteColumn(ObjConstants::SOUND_LOAD_TYPES[alias->flags.loadType]); // loop stream.WriteColumn(alias->flags.looping == T6::SA_NON_LOOPING ? "nonlooping" : "looping"); // randomize_type - stream.WriteColumn(RANDOMIZE_TYPES_ENUM[alias->flags.randomizeType]); + stream.WriteColumn(ObjConstants::SOUND_RANDOMIZE_TYPES[std::min(alias->flags.randomizeType, 3u)]); // probability", stream.WriteColumn(std::to_string(alias->probability)); @@ -473,7 +342,7 @@ class AssetDumperSndBank::Internal stream.WriteColumn(alias->flags.distanceLpf ? "yes" : "no"); // move_type", - stream.WriteColumn(MOVE_TYPES_ENUM[alias->flags.fluxType]); + stream.WriteColumn(ObjConstants::SOUND_MOVE_TYPES[alias->flags.fluxType]); // move_time", stream.WriteColumn(std::to_string(alias->fluxTime)); @@ -524,7 +393,7 @@ class AssetDumperSndBank::Internal stream.WriteColumn(alias->flags.stopOnDeath ? "yes" : "no"); // bus", - stream.WriteColumn(BUS_IDS_ENUM[alias->flags.busType]); + stream.WriteColumn(ObjConstants::SOUND_BUS_IDS[alias->flags.busType]); // snapshot", stream.WriteColumn(""); @@ -757,9 +626,9 @@ class AssetDumperSndBank::Internal for (auto i = 0u; i < 32u; i++) { values.push_back({ - {"duckGroup", DUCK_GROUPS_ENUM[i]}, - {"attenuation", duck.attenuation[i]}, - {"filter", duck.filter[i] } + {"duckGroup", ObjConstants::SOUND_DUCK_GROUPS[i]}, + {"attenuation", duck.attenuation[i] }, + {"filter", duck.filter[i] } }); } diff --git a/src/Utils/Utils/StringUtils.cpp b/src/Utils/Utils/StringUtils.cpp index bf8904da9..31368acd3 100644 --- a/src/Utils/Utils/StringUtils.cpp +++ b/src/Utils/Utils/StringUtils.cpp @@ -1,6 +1,7 @@ #include "StringUtils.h" #include +#include namespace utils { @@ -100,4 +101,18 @@ namespace utils for (auto& c : str) c = static_cast(toupper(static_cast(c))); } + + std::vector StringSplit(const std::string& str, const char delim) + { + std::vector strings{}; + std::istringstream stream(str); + + std::string s; + while (std::getline(stream, s, delim)) + { + strings.push_back(s); + } + + return strings; + } } // namespace utils diff --git a/src/Utils/Utils/StringUtils.h b/src/Utils/Utils/StringUtils.h index ca3f771d8..4104c4f10 100644 --- a/src/Utils/Utils/StringUtils.h +++ b/src/Utils/Utils/StringUtils.h @@ -1,5 +1,6 @@ #pragma once #include +#include namespace utils { @@ -14,4 +15,6 @@ namespace utils void MakeStringLowerCase(std::string& str); void MakeStringUpperCase(std::string& str); + + std::vector StringSplit(const std::string& str, const char delim); } // namespace utils From b10c752391784bb4edaba1891530775bb6e44eb3 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 15 Jan 2024 12:58:14 -0500 Subject: [PATCH 02/25] populate sound alias sub lists correctly. allocate memory for loadedAssets count --- .../T6/AssetLoaders/AssetLoaderSoundBank.cpp | 117 ++++++++++++++---- 1 file changed, 95 insertions(+), 22 deletions(-) diff --git a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp index d74aa12d9..afb5a729a 100644 --- a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp +++ b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp @@ -95,6 +95,8 @@ bool GetSoundAliasValueBool(const std::string& header, const std::vector& values) { + memset(alias, 0, sizeof(SndAlias)); + const auto* name = GetSoundAliasValue("name", values, true); if (name == nullptr) return false; @@ -171,7 +173,34 @@ bool LoadSoundAlias(MemoryManager* memory, SndAlias* alias, const std::vector>& csvLines) +{ + auto count = 1u; + + const auto* name = GetSoundAliasValue("name", csvLines[startRow], true); + if (!name || !*name) + return 0; + + while (true) + { + if (startRow + count >= csvLines.size()) + break; + + const auto* testName = GetSoundAliasValue("name", csvLines[startRow + count], true); + if (!name || !*name) + break; + + // if the name of the next entry does not match the first entry checked, it is not part of the sub list + if (strcmp(name, testName) != 0) + break; + + count++; + } + + return count; +} + +bool LoadSoundAliasList(MemoryManager* memory, SndBank* sndBank, const SearchPathOpenFile& file, unsigned int* loadedEntryCount) { const CsvInputStream aliasCsv(*file.m_stream); std::vector> csvLines; @@ -189,24 +218,55 @@ bool LoadSoundAliasList(MemoryManager* memory, SndBank* sndBank, const SearchPat // Ensure there is at least one entry in the csv after the headers if (maxCols * csvLines.size() > maxCols) { - sndBank->aliasCount = 1; - sndBank->alias = static_cast(memory->Alloc(sizeof(SndAliasList))); + // should be the total number of assets + sndBank->aliasCount = csvLines.size() - 1; + sndBank->alias = static_cast(memory->Alloc(sizeof(SndAliasList) * sndBank->aliasCount)); + memset(sndBank->alias, 0, sizeof(SndAliasList) * sndBank->aliasCount); - sndBank->alias->count = csvLines.size() - 1; - sndBank->alias->head = static_cast(memory->Alloc(sizeof(SndAlias) * sndBank->alias->count)); - sndBank->alias->sequence = 0; + // contains a list of all the alias ids in the sound bank + sndBank->aliasIndex = static_cast(memory->Alloc(sizeof(SndIndexEntry) * sndBank->aliasCount)); + memset(sndBank->aliasIndex, 0, sizeof(SndIndexEntry) * sndBank->aliasCount); LoadSoundAliasHeader(csvLines[0]); - for (auto row = 1u; row < csvLines.size(); row++) + auto row = 1u; + auto listIndex = 0u; + while (row < csvLines.size()) { - const auto& aliasValues = csvLines[row]; - if (!LoadSoundAlias(memory, &sndBank->alias->head[row - 1], aliasValues)) + // count how many of the next rows should be in the sound alias sub-list. Aliases are part of the same sub list if they have the same name for a different file + auto subListCount = GetAliasSubListCount(row, csvLines); + if (subListCount < 1) return false; - } - sndBank->alias->id = sndBank->alias->head[0].id; - sndBank->alias->name = sndBank->alias->head[0].name; + // allocate the sub list + sndBank->alias[listIndex].count = subListCount; + sndBank->alias[listIndex].head = static_cast(memory->Alloc(sizeof(SndAlias) * subListCount)); + sndBank->alias[listIndex].sequence = 0; + + // populate the sublist with the next X number of aliases in the file. Note: this will only work correctly if the aliases that are a part of a sub list are next to each other in the file + for (auto i = 0; i < subListCount; i++) + { + const auto& aliasValues = csvLines[row]; + if (!LoadSoundAlias(memory, &sndBank->alias[listIndex].head[i], aliasValues)) + return false; + + // if this asset is loaded instead of stream, increment the loaded count for later + if (sndBank->alias[listIndex].head[i].flags.loadType == T6::SA_LOADED) + (*loadedEntryCount)++; + + // populate the aliasIndex array + sndBank->aliasIndex[row - 1].value = sndBank->alias[listIndex].head[i].id; + sndBank->aliasIndex[row - 1].next = 0xFFFF; + + row++; + } + + // the main alias list id and name should match that of the entries in the sub list (since they all have the same name, all sub entries will be the same) + sndBank->alias[listIndex].id = sndBank->alias[listIndex].head[0].id; + sndBank->alias[listIndex].name = sndBank->alias[listIndex].head[0].name; + + listIndex++; + } } return true; @@ -228,26 +288,39 @@ bool AssetLoaderSoundBank::LoadFromRaw( // set the defaults auto* sndBank = memory->Create(); + memset(sndBank, 0, sizeof(SndBank)); + sndBank->name = memory->Dup(assetName.c_str()); auto sndBankLocalization = utils::StringSplit(assetName, '.'); - const auto* zoneName = memory->Dup(sndBankLocalization.at(0).c_str()); - const auto* languageName = memory->Dup(sndBankLocalization.at(1).c_str()); - - sndBank->streamAssetBank.zone = zoneName; - sndBank->streamAssetBank.language = languageName; - sndBank->loadAssetBank.zone = zoneName; - sndBank->loadAssetBank.language = languageName; - sndBank->loadedAssets.zone = zoneName; - sndBank->loadedAssets.language = languageName; + sndBank->streamAssetBank.zone = memory->Dup(sndBankLocalization.at(0).c_str()); + sndBank->streamAssetBank.language = memory->Dup(sndBankLocalization.at(1).c_str()); + memset(sndBank->streamAssetBank.linkTimeChecksum, 0xCC, 16); + + sndBank->loadAssetBank.zone = memory->Dup(sndBankLocalization.at(0).c_str()); + sndBank->loadAssetBank.language = memory->Dup(sndBankLocalization.at(1).c_str()); + memset(sndBank->loadAssetBank.linkTimeChecksum, 0xCC, 16); + + sndBank->loadedAssets.zone = memory->Dup(sndBankLocalization.at(0).c_str()); + sndBank->loadedAssets.language = memory->Dup(sndBankLocalization.at(1).c_str()); sndBank->loadedAssets.loadedCount = 0; - if (!LoadSoundAliasList(memory, sndBank, aliasFile)) + unsigned int loadedEntryCount = 0u; + if (!LoadSoundAliasList(memory, sndBank, aliasFile, &loadedEntryCount)) return false; + // allocate memory for the number of non-streamed assets + sndBank->loadedAssets.entryCount = loadedEntryCount; + sndBank->loadedAssets.entries = static_cast(memory->Alloc(sizeof(SndAssetBankEntry) * loadedEntryCount)); + memset(sndBank->loadedAssets.entries, 0, sizeof(SndAssetBankEntry) * loadedEntryCount); + // open the soundbank reverbs + sndBank->radverbs = nullptr; + sndBank->radverbCount = 0; // open the soundbank ducks + sndBank->ducks = nullptr; + sndBank->duckCount = 0; manager->AddAsset(ASSET_TYPE_SOUND, assetName, sndBank); return true; From 2323b322ca0c91afc91645f2960a4f20c5c10f72 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 24 Jan 2024 01:13:40 -0500 Subject: [PATCH 03/25] finish SndAliasList loading --- .../T6/AssetLoaders/AssetLoaderSoundBank.cpp | 143 ++++++++++++++---- .../T6/AssetDumpers/AssetDumperSndBank.cpp | 4 + src/ObjWriting/Game/T6/ZoneDumperT6.cpp | 2 +- 3 files changed, 117 insertions(+), 32 deletions(-) diff --git a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp index afb5a729a..49e5124ab 100644 --- a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp +++ b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp @@ -61,11 +61,11 @@ char* GetSoundAliasValueString(const std::string& header, const std::vectorDup(value) : nullptr; } -int32_t GetSoundAliasValueInt(const std::string& header, const std::vector& values, bool required = false) +long long GetSoundAliasValueInt(const std::string& header, const std::vector& values, bool required = false) { const auto* value = GetSoundAliasValue(header, values, required); if (value && *value) - return std::stoi(value); + return std::stoll(value); return 0; } @@ -152,6 +152,7 @@ bool LoadSoundAlias(MemoryManager* memory, SndAlias* alias, const std::vectorflags.pauseable = GetSoundAliasValueBool("pause", values, "yes"); alias->flags.stopOnDeath = GetSoundAliasValueBool("stop_on_death", values, "yes"); + alias->duckGroup = GetSoundAliasValueIndex("duck_group", values, ObjConstants::SOUND_DUCK_GROUPS.data(), ObjConstants::SOUND_DUCK_GROUPS.size()); alias->flags.volumeGroup = GetSoundAliasValueIndex("group", values, ObjConstants::SOUND_GROUPS.data(), ObjConstants::SOUND_GROUPS.size()); alias->flags.fluxType = GetSoundAliasValueIndex("move_type", values, ObjConstants::SOUND_MOVE_TYPES.data(), ObjConstants::SOUND_MOVE_TYPES.size()); alias->flags.loadType = GetSoundAliasValueIndex("type", values, ObjConstants::SOUND_LOAD_TYPES.data(), ObjConstants::SOUND_LOAD_TYPES.size()); @@ -168,7 +169,7 @@ bool LoadSoundAlias(MemoryManager* memory, SndAlias* alias, const std::vectorflags.reverbMinFalloffCurve = GetSoundAliasValueIndex("reverb_min_falloff_curve", values, ObjConstants::SOUND_CURVES.data(), ObjConstants::SOUND_CURVES.size()); alias->flags.randomizeType = - GetSoundAliasValueIndex("type", values, ObjConstants::SOUND_RANDOMIZE_TYPES.data(), ObjConstants::SOUND_RANDOMIZE_TYPES.size()); + GetSoundAliasValueIndex("randomize_type", values, ObjConstants::SOUND_RANDOMIZE_TYPES.data(), ObjConstants::SOUND_RANDOMIZE_TYPES.size()); return true; } @@ -200,7 +201,74 @@ unsigned int GetAliasSubListCount(unsigned int startRow, std::vectoraliasIndex = static_cast(memory->Alloc(sizeof(SndIndexEntry) * sndBank->aliasCount)); + memset(sndBank->aliasIndex, 0xFF, sizeof(SndIndexEntry) * sndBank->aliasCount); + + bool* setAliasIndexList = new bool[sndBank->aliasCount]; + memset(setAliasIndexList, false, sndBank->aliasCount); + + for (auto i = 0; i < sndBank->aliasCount; i++) + { + auto idx = sndBank->alias[i].id % sndBank->aliasCount; + if (sndBank->aliasIndex[idx].value == USHRT_MAX) + { + sndBank->aliasIndex[idx].value = i; + sndBank->aliasIndex[idx].next = USHRT_MAX; + setAliasIndexList[i] = true; + } + } + + for (auto i = 0; i < sndBank->aliasCount; i++) + { + if (setAliasIndexList[i]) + continue; + + auto idx = sndBank->alias[i].id % sndBank->aliasCount; + while (sndBank->aliasIndex[idx].next != USHRT_MAX) + { + idx = sndBank->aliasIndex[idx].next; + } + + auto offset = 1; + auto freeIdx = USHRT_MAX; + while (true) + { + freeIdx = (idx + offset) % sndBank->aliasCount; + if (sndBank->aliasIndex[freeIdx].value == USHRT_MAX) + break; + + freeIdx = (idx + sndBank->aliasCount - offset) % sndBank->aliasCount; + if (sndBank->aliasIndex[freeIdx].value == USHRT_MAX) + break; + + offset++; + freeIdx = USHRT_MAX; + + if (offset >= sndBank->aliasCount) + break; + } + + if (freeIdx == USHRT_MAX) + { + std::cerr << "Unable to allocate sound bank alias index list" << std::endl; + delete[] setAliasIndexList; + return false; + } + + sndBank->aliasIndex[idx].next = freeIdx; + sndBank->aliasIndex[freeIdx].value = i; + sndBank->aliasIndex[freeIdx].next = USHRT_MAX; + setAliasIndexList[i] = true; + } + + delete[] setAliasIndexList; + return true; +} + +bool LoadSoundAliasList(MemoryManager* memory, SndBank* sndBank, const SearchPathOpenFile& file, unsigned int* loadedEntryCount, unsigned int* streamedEntryCount) { const CsvInputStream aliasCsv(*file.m_stream); std::vector> csvLines; @@ -223,10 +291,6 @@ bool LoadSoundAliasList(MemoryManager* memory, SndBank* sndBank, const SearchPat sndBank->alias = static_cast(memory->Alloc(sizeof(SndAliasList) * sndBank->aliasCount)); memset(sndBank->alias, 0, sizeof(SndAliasList) * sndBank->aliasCount); - // contains a list of all the alias ids in the sound bank - sndBank->aliasIndex = static_cast(memory->Alloc(sizeof(SndIndexEntry) * sndBank->aliasCount)); - memset(sndBank->aliasIndex, 0, sizeof(SndIndexEntry) * sndBank->aliasCount); - LoadSoundAliasHeader(csvLines[0]); auto row = 1u; @@ -253,10 +317,8 @@ bool LoadSoundAliasList(MemoryManager* memory, SndBank* sndBank, const SearchPat // if this asset is loaded instead of stream, increment the loaded count for later if (sndBank->alias[listIndex].head[i].flags.loadType == T6::SA_LOADED) (*loadedEntryCount)++; - - // populate the aliasIndex array - sndBank->aliasIndex[row - 1].value = sndBank->alias[listIndex].head[i].id; - sndBank->aliasIndex[row - 1].next = 0xFFFF; + else + (*streamedEntryCount)++; row++; } @@ -267,6 +329,21 @@ bool LoadSoundAliasList(MemoryManager* memory, SndBank* sndBank, const SearchPat listIndex++; } + + // re-allocate the alias list and count if necessary. We don't know the true aliasCount until after parsing all the aliases in the file + if (listIndex != sndBank->aliasCount) + { + auto* oldAliases = sndBank->alias; + + sndBank->aliasCount = listIndex; + sndBank->alias = static_cast(memory->Alloc(sizeof(SndAliasList) * sndBank->aliasCount)); + memcpy(sndBank->alias, oldAliases, sizeof(SndAliasList) * sndBank->aliasCount); + + memory->Free(oldAliases); + } + + if (!LoadSoundAliasIndexList(memory, sndBank)) + return false; } return true; @@ -291,29 +368,12 @@ bool AssetLoaderSoundBank::LoadFromRaw( memset(sndBank, 0, sizeof(SndBank)); sndBank->name = memory->Dup(assetName.c_str()); - auto sndBankLocalization = utils::StringSplit(assetName, '.'); - sndBank->streamAssetBank.zone = memory->Dup(sndBankLocalization.at(0).c_str()); - sndBank->streamAssetBank.language = memory->Dup(sndBankLocalization.at(1).c_str()); - memset(sndBank->streamAssetBank.linkTimeChecksum, 0xCC, 16); - - sndBank->loadAssetBank.zone = memory->Dup(sndBankLocalization.at(0).c_str()); - sndBank->loadAssetBank.language = memory->Dup(sndBankLocalization.at(1).c_str()); - memset(sndBank->loadAssetBank.linkTimeChecksum, 0xCC, 16); - - sndBank->loadedAssets.zone = memory->Dup(sndBankLocalization.at(0).c_str()); - sndBank->loadedAssets.language = memory->Dup(sndBankLocalization.at(1).c_str()); - sndBank->loadedAssets.loadedCount = 0; - unsigned int loadedEntryCount = 0u; - if (!LoadSoundAliasList(memory, sndBank, aliasFile, &loadedEntryCount)) + unsigned int loadedEntryCount = 0u, streamedEntryCount = 0u; + if (!LoadSoundAliasList(memory, sndBank, aliasFile, &loadedEntryCount, &streamedEntryCount)) return false; - // allocate memory for the number of non-streamed assets - sndBank->loadedAssets.entryCount = loadedEntryCount; - sndBank->loadedAssets.entries = static_cast(memory->Alloc(sizeof(SndAssetBankEntry) * loadedEntryCount)); - memset(sndBank->loadedAssets.entries, 0, sizeof(SndAssetBankEntry) * loadedEntryCount); - // open the soundbank reverbs sndBank->radverbs = nullptr; sndBank->radverbCount = 0; @@ -322,6 +382,27 @@ bool AssetLoaderSoundBank::LoadFromRaw( sndBank->ducks = nullptr; sndBank->duckCount = 0; + if (loadedEntryCount > 0) + { + sndBank->loadAssetBank.zone = memory->Dup(sndBankLocalization.at(0).c_str()); + sndBank->loadAssetBank.language = memory->Dup(sndBankLocalization.at(1).c_str()); + memset(sndBank->loadAssetBank.linkTimeChecksum, 0xCC, 16); + + sndBank->loadedAssets.loadedCount = 0; + sndBank->loadedAssets.zone = memory->Dup(sndBankLocalization.at(0).c_str()); + sndBank->loadedAssets.language = memory->Dup(sndBankLocalization.at(1).c_str()); + sndBank->loadedAssets.entryCount = loadedEntryCount; + sndBank->loadedAssets.entries = static_cast(memory->Alloc(sizeof(SndAssetBankEntry) * loadedEntryCount)); + memset(sndBank->loadedAssets.entries, 0, sizeof(SndAssetBankEntry) * loadedEntryCount); + } + + if (streamedEntryCount > 0) + { + sndBank->streamAssetBank.zone = memory->Dup(sndBankLocalization.at(0).c_str()); + sndBank->streamAssetBank.language = memory->Dup(sndBankLocalization.at(1).c_str()); + memset(sndBank->streamAssetBank.linkTimeChecksum, 0xCC, 16); + } + manager->AddAsset(ASSET_TYPE_SOUND, assetName, sndBank); return true; } diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp index c01cc4e35..33bf94c17 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp @@ -52,6 +52,7 @@ namespace "start_delay", "reverb_send", "duck", + "duck_group", "pan", "center_send", "envelop_min", @@ -314,6 +315,9 @@ class AssetDumperSndBank::Internal // duck", stream.WriteColumn(FindNameForDuck(alias->duck, bank)); + // duck_group", + stream.WriteColumn(ObjConstants::SOUND_DUCK_GROUPS[alias->duckGroup]); + // pan", stream.WriteColumn(alias->flags.panType == SA_PAN_2D ? "2d" : "3d"); diff --git a/src/ObjWriting/Game/T6/ZoneDumperT6.cpp b/src/ObjWriting/Game/T6/ZoneDumperT6.cpp index ffb4113fd..30de7a3ee 100644 --- a/src/ObjWriting/Game/T6/ZoneDumperT6.cpp +++ b/src/ObjWriting/Game/T6/ZoneDumperT6.cpp @@ -45,7 +45,7 @@ bool ZoneDumper::DumpZone(AssetDumpingContext& context) const DUMP_ASSET_POOL(AssetDumperPhysConstraints, m_phys_constraints, ASSET_TYPE_PHYSCONSTRAINTS) // DUMP_ASSET_POOL(AssetDumperDestructibleDef, m_destructible_def, ASSET_TYPE_DESTRUCTIBLEDEF) // DUMP_ASSET_POOL(AssetDumperXAnimParts, m_xanim_parts, ASSET_TYPE_XANIMPARTS) - DUMP_ASSET_POOL(AssetDumperXModel, m_xmodel, ASSET_TYPE_XMODEL) + // DUMP_ASSET_POOL(AssetDumperXModel, m_xmodel, ASSET_TYPE_XMODEL) // DUMP_ASSET_POOL(AssetDumperMaterial, m_material, ASSET_TYPE_MATERIAL) // DUMP_ASSET_POOL(AssetDumperTechniqueSet, m_technique_set, ASSET_TYPE_TECHNIQUE_SET) DUMP_ASSET_POOL(AssetDumperGfxImage, m_image, ASSET_TYPE_IMAGE) From a93cb6f05d39e29ef17162721d87eb72df05637a Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 24 Jan 2024 01:14:20 -0500 Subject: [PATCH 04/25] dont comment that --- src/ObjWriting/Game/T6/ZoneDumperT6.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ObjWriting/Game/T6/ZoneDumperT6.cpp b/src/ObjWriting/Game/T6/ZoneDumperT6.cpp index 30de7a3ee..ffb4113fd 100644 --- a/src/ObjWriting/Game/T6/ZoneDumperT6.cpp +++ b/src/ObjWriting/Game/T6/ZoneDumperT6.cpp @@ -45,7 +45,7 @@ bool ZoneDumper::DumpZone(AssetDumpingContext& context) const DUMP_ASSET_POOL(AssetDumperPhysConstraints, m_phys_constraints, ASSET_TYPE_PHYSCONSTRAINTS) // DUMP_ASSET_POOL(AssetDumperDestructibleDef, m_destructible_def, ASSET_TYPE_DESTRUCTIBLEDEF) // DUMP_ASSET_POOL(AssetDumperXAnimParts, m_xanim_parts, ASSET_TYPE_XANIMPARTS) - // DUMP_ASSET_POOL(AssetDumperXModel, m_xmodel, ASSET_TYPE_XMODEL) + DUMP_ASSET_POOL(AssetDumperXModel, m_xmodel, ASSET_TYPE_XMODEL) // DUMP_ASSET_POOL(AssetDumperMaterial, m_material, ASSET_TYPE_MATERIAL) // DUMP_ASSET_POOL(AssetDumperTechniqueSet, m_technique_set, ASSET_TYPE_TECHNIQUE_SET) DUMP_ASSET_POOL(AssetDumperGfxImage, m_image, ASSET_TYPE_IMAGE) From acd9fa27fca6411e62df466f3f303846bc16fef4 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 24 Jan 2024 22:01:47 -0500 Subject: [PATCH 05/25] reverb and duck loading. cleaned up asset loading with custom csv parser --- src/ObjCommon/Csv/ParsedCsv.cpp | 65 +++ src/ObjCommon/Csv/ParsedCsv.h | 41 ++ src/ObjLoading.lua | 1 + .../T6/AssetLoaders/AssetLoaderSoundBank.cpp | 424 ++++++++++-------- 4 files changed, 349 insertions(+), 182 deletions(-) create mode 100644 src/ObjCommon/Csv/ParsedCsv.cpp create mode 100644 src/ObjCommon/Csv/ParsedCsv.h diff --git a/src/ObjCommon/Csv/ParsedCsv.cpp b/src/ObjCommon/Csv/ParsedCsv.cpp new file mode 100644 index 000000000..77ef87ee0 --- /dev/null +++ b/src/ObjCommon/Csv/ParsedCsv.cpp @@ -0,0 +1,65 @@ +#include "Csv/ParsedCsv.h" + +ParsedCsvRow::ParsedCsvRow(std::unordered_map& headers, std::vector& row) + : headers(headers), + values(row) +{ +} + +const std::string& ParsedCsvRow::GetValue(const std::string& header, bool required) const +{ + if (this->headers.find(header) == this->headers.end()) + { + if (required) + std::cerr << "ERROR: Required column \"" << header << "\" was not found"; + else + std::cerr << "WARNING: Expected column \"" << header << "\" was not found"; + return nullptr; + } + + auto& value = this->values.at(this->headers[header]); + if (required && value.empty()) + { + std::cerr << "ERROR: Required column \"" << header << "\" does not have a value"; + return nullptr; + } + + return value; +} + +ParsedCsv::ParsedCsv(const CsvInputStream& inputStream, bool hasHeaders) +{ + std::vector> csvLines; + std::vector currentLine; + + while (inputStream.NextRow(currentLine)) + { + csvLines.emplace_back(std::move(currentLine)); + currentLine = std::vector(); + } + + if (hasHeaders) + { + auto& headersRow = csvLines[0]; + for (auto i = 0u; i < headersRow.size(); i++) + { + this->headers[headersRow[i]] = i; + } + } + + for (auto i = hasHeaders ? 1u : 0u; i < csvLines.size(); i++) + { + auto& rowValues = csvLines[i]; + this->rows.push_back(ParsedCsvRow(this->headers, rowValues)); + } +} + +size_t ParsedCsv::Size() const +{ + return this->rows.size(); +} + +ParsedCsvRow ParsedCsv::operator[](size_t index) const +{ + return this->rows.at(index); +} diff --git a/src/ObjCommon/Csv/ParsedCsv.h b/src/ObjCommon/Csv/ParsedCsv.h new file mode 100644 index 000000000..01df30fbd --- /dev/null +++ b/src/ObjCommon/Csv/ParsedCsv.h @@ -0,0 +1,41 @@ +#pragma once +#include +#include +#include + +class ParsedCsvRow +{ + std::unordered_map& headers; + std::vector values; + +public: + explicit ParsedCsvRow(std::unordered_map& headers, std::vector& row); + const std::string& GetValue(const std::string& header, bool required = false) const; + + template T GetValueAs(const std::string& header, bool required = false) const + { + const auto& value = this->GetValue(header, required); + if (!value.empty()) + { + std::istringstream ss(value); + T out{}; + ss >> out; + return out; + } + + return {}; + } +}; + +class ParsedCsv +{ + std::unordered_map headers; + std::vector rows; + +public: + explicit ParsedCsv(const CsvInputStream& inputStream, bool hasHeaders = true); + + size_t Size() const; + + ParsedCsvRow operator[](size_t index) const; +}; \ No newline at end of file diff --git a/src/ObjLoading.lua b/src/ObjLoading.lua index c9109ae55..30605cdb3 100644 --- a/src/ObjLoading.lua +++ b/src/ObjLoading.lua @@ -55,4 +55,5 @@ function ObjLoading:project() minilzo:include(includes) minizip:include(includes) zlib:include(includes) + json:include(includes) end diff --git a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp index 49e5124ab..ffa9263dd 100644 --- a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp +++ b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp @@ -1,6 +1,8 @@ #include "AssetLoaderSoundBank.h" -#include "Csv/CsvStream.h" +#include "Csv/ParsedCsv.h" +#include "nlohmann/json.hpp" + #include "Game/T6/CommonT6.h" #include "Game/T6/ObjConstantsT6.h" #include "Game/T6/T6.h" @@ -11,7 +13,6 @@ #include using namespace T6; -std::unordered_map AliasHeaders{}; void* AssetLoaderSoundBank::CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) { @@ -26,54 +27,9 @@ bool AssetLoaderSoundBank::CanLoadFromRaw() const return true; } -void LoadSoundAliasHeader(const std::vector& values) -{ - for (auto i = 0u; i < values.size(); i++) - { - AliasHeaders[values[i]] = i; - } -} - -const char* GetSoundAliasValue(const std::string& header, const std::vector& values, bool required = false) -{ - if (AliasHeaders.find(header) == AliasHeaders.end()) - { - if (required) - std::cerr << "ERROR: Required column \"" << header << "\" was not found"; - else - std::cerr << "WARNING: Expected column \"" << header << "\" was not found"; - return nullptr; - } - - auto value = values.at(AliasHeaders[header]).c_str(); - if (required && (!value || !*value)) - { - std::cerr << "ERROR: Required column \"" << header << "\" does not have a value"; - return nullptr; - } - - return value; -} - -char* GetSoundAliasValueString(const std::string& header, const std::vector& values, MemoryManager* memory, bool required = false) -{ - const auto* value = GetSoundAliasValue(header, values, required); - return value ? memory->Dup(value) : nullptr; -} - -long long GetSoundAliasValueInt(const std::string& header, const std::vector& values, bool required = false) -{ - const auto* value = GetSoundAliasValue(header, values, required); - if (value && *value) - return std::stoll(value); - return 0; -} - -size_t GetSoundAliasValueIndex( - const std::string& header, const std::vector& values, const std::string* lookupTable, size_t len, bool required = false) +size_t GetValueIndex(const std::string& value, const std::string* lookupTable, size_t len) { - const auto* value = GetSoundAliasValue(header, values, required); - if (!value || !*value) + if (value.empty()) return 0; for (auto i = 0u; i < len; i++) @@ -85,114 +41,25 @@ size_t GetSoundAliasValueIndex( return 0; } -bool GetSoundAliasValueBool(const std::string& header, const std::vector& values, const std::string& comparison, bool required = false) -{ - const auto* value = GetSoundAliasValue(header, values, required); - if (value && *value) - return comparison == value; - return false; -} - -bool LoadSoundAlias(MemoryManager* memory, SndAlias* alias, const std::vector& values) -{ - memset(alias, 0, sizeof(SndAlias)); - - const auto* name = GetSoundAliasValue("name", values, true); - if (name == nullptr) - return false; - - const auto* aliasFileName = GetSoundAliasValue("file", values, true); - if (aliasFileName == nullptr) - return false; - - alias->name = memory->Dup(name); - alias->id = Common::SND_HashName(name); - alias->assetFileName = memory->Dup(aliasFileName); - alias->assetId = Common::SND_HashName(aliasFileName); - alias->secondaryname = GetSoundAliasValueString("secondary", values, memory); - alias->subtitle = GetSoundAliasValueString("subtitle", values, memory); - - alias->duck = Common::SND_HashName(GetSoundAliasValue("duck", values)); - - alias->volMin = GetSoundAliasValueInt("vol_min", values); - alias->volMax = GetSoundAliasValueInt("vol_max", values); - alias->distMin = GetSoundAliasValueInt("dist_min", values); - alias->distMax = GetSoundAliasValueInt("dist_max", values); - alias->distReverbMax = GetSoundAliasValueInt("dist_reverb_max", values); - alias->limitCount = GetSoundAliasValueInt("limit_count", values); - alias->entityLimitCount = GetSoundAliasValueInt("entity_limit_count", values); - alias->pitchMin = GetSoundAliasValueInt("pitch_min", values); - alias->pitchMax = GetSoundAliasValueInt("pitch_max", values); - alias->minPriority = GetSoundAliasValueInt("min_priority", values); - alias->maxPriority = GetSoundAliasValueInt("max_priority", values); - alias->minPriorityThreshold = GetSoundAliasValueInt("min_priority_threshold", values); - alias->maxPriorityThreshold = GetSoundAliasValueInt("max_priority_threshold", values); - alias->probability = GetSoundAliasValueInt("probability", values); - alias->startDelay = GetSoundAliasValueInt("start_delay", values); - alias->reverbSend = GetSoundAliasValueInt("reverb_send", values); - alias->centerSend = GetSoundAliasValueInt("center_send", values); - alias->envelopMin = GetSoundAliasValueInt("envelop_min", values); - alias->envelopMax = GetSoundAliasValueInt("envelop_max", values); - alias->envelopPercentage = GetSoundAliasValueInt("envelop_percentage", values); - alias->occlusionLevel = GetSoundAliasValueInt("occlusion_level", values); - alias->fluxTime = GetSoundAliasValueInt("move_time", values); - alias->futzPatch = GetSoundAliasValueInt("futz", values); - alias->contextType = GetSoundAliasValueInt("context_type", values); - alias->contextValue = GetSoundAliasValueInt("context_value", values); - alias->fadeIn = GetSoundAliasValueInt("fade_in", values); - alias->fadeOut = GetSoundAliasValueInt("fade_out", values); - - alias->flags.looping = GetSoundAliasValueBool("loop", values, "looping"); - alias->flags.panType = GetSoundAliasValueBool("pan", values, "3d"); - alias->flags.isBig = GetSoundAliasValueBool("is_big", values, "yes"); - alias->flags.distanceLpf = GetSoundAliasValueBool("distance_lpf", values, "yes"); - alias->flags.doppler = GetSoundAliasValueBool("doppler", values, "yes"); - alias->flags.timescale = GetSoundAliasValueBool("timescale", values, "yes"); - alias->flags.isMusic = GetSoundAliasValueBool("music", values, "yes"); - alias->flags.pauseable = GetSoundAliasValueBool("pause", values, "yes"); - alias->flags.stopOnDeath = GetSoundAliasValueBool("stop_on_death", values, "yes"); - - alias->duckGroup = GetSoundAliasValueIndex("duck_group", values, ObjConstants::SOUND_DUCK_GROUPS.data(), ObjConstants::SOUND_DUCK_GROUPS.size()); - alias->flags.volumeGroup = GetSoundAliasValueIndex("group", values, ObjConstants::SOUND_GROUPS.data(), ObjConstants::SOUND_GROUPS.size()); - alias->flags.fluxType = GetSoundAliasValueIndex("move_type", values, ObjConstants::SOUND_MOVE_TYPES.data(), ObjConstants::SOUND_MOVE_TYPES.size()); - alias->flags.loadType = GetSoundAliasValueIndex("type", values, ObjConstants::SOUND_LOAD_TYPES.data(), ObjConstants::SOUND_LOAD_TYPES.size()); - alias->flags.busType = GetSoundAliasValueIndex("bus", values, ObjConstants::SOUND_BUS_IDS.data(), ObjConstants::SOUND_BUS_IDS.size()); - alias->flags.limitType = GetSoundAliasValueIndex("limit_type", values, ObjConstants::SOUND_LIMIT_TYPES.data(), ObjConstants::SOUND_LIMIT_TYPES.size()); - alias->flags.entityLimitType = - GetSoundAliasValueIndex("entity_limit_type", values, ObjConstants::SOUND_LIMIT_TYPES.data(), ObjConstants::SOUND_LIMIT_TYPES.size()); - alias->flags.volumeFalloffCurve = - GetSoundAliasValueIndex("volume_falloff_curve", values, ObjConstants::SOUND_CURVES.data(), ObjConstants::SOUND_CURVES.size()); - alias->flags.reverbFalloffCurve = - GetSoundAliasValueIndex("reverb_falloff_curve", values, ObjConstants::SOUND_CURVES.data(), ObjConstants::SOUND_CURVES.size()); - alias->flags.volumeMinFalloffCurve = - GetSoundAliasValueIndex("volume_min_falloff_curve", values, ObjConstants::SOUND_CURVES.data(), ObjConstants::SOUND_CURVES.size()); - alias->flags.reverbMinFalloffCurve = - GetSoundAliasValueIndex("reverb_min_falloff_curve", values, ObjConstants::SOUND_CURVES.data(), ObjConstants::SOUND_CURVES.size()); - alias->flags.randomizeType = - GetSoundAliasValueIndex("randomize_type", values, ObjConstants::SOUND_RANDOMIZE_TYPES.data(), ObjConstants::SOUND_RANDOMIZE_TYPES.size()); - - return true; -} - -unsigned int GetAliasSubListCount(unsigned int startRow, std::vector>& csvLines) +unsigned int GetAliasSubListCount(unsigned int startRow, const ParsedCsv& csv) { auto count = 1u; - const auto* name = GetSoundAliasValue("name", csvLines[startRow], true); - if (!name || !*name) + const auto name = csv[startRow].GetValue("name", true); + if (name.empty()) return 0; while (true) { - if (startRow + count >= csvLines.size()) + if (startRow + count >= csv.Size()) break; - const auto* testName = GetSoundAliasValue("name", csvLines[startRow + count], true); - if (!name || !*name) + const auto testName = csv[startRow + count].GetValue("name", true); + if (testName.empty()) break; // if the name of the next entry does not match the first entry checked, it is not part of the sub list - if (strcmp(name, testName) != 0) + if (name != testName) break; count++; @@ -201,6 +68,87 @@ unsigned int GetAliasSubListCount(unsigned int startRow, std::vectorname = memory->Dup(name.data()); + alias->id = Common::SND_HashName(name.data()); + alias->assetFileName = memory->Dup(aliasFileName.data()); + alias->assetId = Common::SND_HashName(aliasFileName.data()); + alias->secondaryname = memory->Dup(row.GetValue("secondary").data()); + alias->subtitle = memory->Dup(row.GetValue("subtitle").data()); + + alias->duck = Common::SND_HashName(row.GetValue("duck").data()); + + alias->volMin = row.GetValueAs("vol_min"); + alias->volMax = row.GetValueAs("vol_max"); + alias->distMin = row.GetValueAs("dist_min"); + alias->distMax = row.GetValueAs("dist_max"); + alias->distReverbMax = row.GetValueAs("dist_reverb_max"); + alias->limitCount = row.GetValueAs("limit_count"); + alias->entityLimitCount = row.GetValueAs("entity_limit_count"); + alias->pitchMin = row.GetValueAs("pitch_min"); + alias->pitchMax = row.GetValueAs("pitch_max"); + alias->minPriority = row.GetValueAs("min_priority"); + alias->maxPriority = row.GetValueAs("max_priority"); + alias->minPriorityThreshold = row.GetValueAs("min_priority_threshold"); + alias->maxPriorityThreshold = row.GetValueAs("max_priority_threshold"); + alias->probability = row.GetValueAs("probability"); + alias->startDelay = row.GetValueAs("start_delay"); + alias->reverbSend = row.GetValueAs("reverb_send"); + alias->centerSend = row.GetValueAs("center_send"); + alias->envelopMin = row.GetValueAs("envelop_min"); + alias->envelopMax = row.GetValueAs("envelop_max"); + alias->envelopPercentage = row.GetValueAs("envelop_percentage"); + alias->occlusionLevel = row.GetValueAs("occlusion_level"); + alias->fluxTime = row.GetValueAs("move_time"); + alias->futzPatch = row.GetValueAs("futz"); + alias->contextType = row.GetValueAs("context_type"); + alias->contextValue = row.GetValueAs("context_value"); + alias->fadeIn = row.GetValueAs("fade_in"); + alias->fadeOut = row.GetValueAs("fade_out"); + + alias->flags.looping = row.GetValue("loop") == "looping"; + alias->flags.panType = row.GetValue("pan") == "3d"; + alias->flags.isBig = row.GetValue("is_big") == "yes"; + alias->flags.distanceLpf = row.GetValue("distance_lpf") == "yes"; + alias->flags.doppler = row.GetValue("doppler") == "yes"; + alias->flags.timescale = row.GetValue("timescale") == "yes"; + alias->flags.isMusic = row.GetValue("music") == "yes"; + alias->flags.pauseable = row.GetValue("pause") == "yes"; + alias->flags.stopOnDeath = row.GetValue("stop_on_death") == "yes"; + + alias->duckGroup = + static_cast(GetValueIndex(row.GetValue("duck_group"), ObjConstants::SOUND_DUCK_GROUPS.data(), ObjConstants::SOUND_DUCK_GROUPS.size())); + alias->flags.volumeGroup = GetValueIndex(row.GetValue("group"), ObjConstants::SOUND_GROUPS.data(), ObjConstants::SOUND_GROUPS.size()); + alias->flags.fluxType = GetValueIndex(row.GetValue("move_type"), ObjConstants::SOUND_MOVE_TYPES.data(), ObjConstants::SOUND_MOVE_TYPES.size()); + alias->flags.loadType = GetValueIndex(row.GetValue("type"), ObjConstants::SOUND_LOAD_TYPES.data(), ObjConstants::SOUND_LOAD_TYPES.size()); + alias->flags.busType = GetValueIndex(row.GetValue("bus"), ObjConstants::SOUND_BUS_IDS.data(), ObjConstants::SOUND_BUS_IDS.size()); + alias->flags.limitType = GetValueIndex(row.GetValue("limit_type"), ObjConstants::SOUND_LIMIT_TYPES.data(), ObjConstants::SOUND_LIMIT_TYPES.size()); + alias->flags.volumeFalloffCurve = GetValueIndex(row.GetValue("volume_falloff_curve"), ObjConstants::SOUND_CURVES.data(), ObjConstants::SOUND_CURVES.size()); + alias->flags.reverbFalloffCurve = GetValueIndex(row.GetValue("reverb_falloff_curve"), ObjConstants::SOUND_CURVES.data(), ObjConstants::SOUND_CURVES.size()); + + alias->flags.entityLimitType = + GetValueIndex(row.GetValue("entity_limit_type"), ObjConstants::SOUND_LIMIT_TYPES.data(), ObjConstants::SOUND_LIMIT_TYPES.size()); + alias->flags.volumeMinFalloffCurve = + GetValueIndex(row.GetValue("volume_min_falloff_curve"), ObjConstants::SOUND_CURVES.data(), ObjConstants::SOUND_CURVES.size()); + alias->flags.reverbMinFalloffCurve = + GetValueIndex(row.GetValue("reverb_min_falloff_curve"), ObjConstants::SOUND_CURVES.data(), ObjConstants::SOUND_CURVES.size()); + alias->flags.randomizeType = + GetValueIndex(row.GetValue("randomize_type"), ObjConstants::SOUND_RANDOMIZE_TYPES.data(), ObjConstants::SOUND_RANDOMIZE_TYPES.size()); + + return true; +} + bool LoadSoundAliasIndexList(MemoryManager* memory, SndBank* sndBank) { // contains a list of all the alias ids in the sound bank @@ -210,7 +158,7 @@ bool LoadSoundAliasIndexList(MemoryManager* memory, SndBank* sndBank) bool* setAliasIndexList = new bool[sndBank->aliasCount]; memset(setAliasIndexList, false, sndBank->aliasCount); - for (auto i = 0; i < sndBank->aliasCount; i++) + for (auto i = 0u; i < sndBank->aliasCount; i++) { auto idx = sndBank->alias[i].id % sndBank->aliasCount; if (sndBank->aliasIndex[idx].value == USHRT_MAX) @@ -221,7 +169,7 @@ bool LoadSoundAliasIndexList(MemoryManager* memory, SndBank* sndBank) } } - for (auto i = 0; i < sndBank->aliasCount; i++) + for (auto i = 0u; i < sndBank->aliasCount; i++) { if (setAliasIndexList[i]) continue; @@ -232,15 +180,15 @@ bool LoadSoundAliasIndexList(MemoryManager* memory, SndBank* sndBank) idx = sndBank->aliasIndex[idx].next; } - auto offset = 1; + auto offset = 1u; auto freeIdx = USHRT_MAX; while (true) { freeIdx = (idx + offset) % sndBank->aliasCount; if (sndBank->aliasIndex[freeIdx].value == USHRT_MAX) break; - - freeIdx = (idx + sndBank->aliasCount - offset) % sndBank->aliasCount; + + freeIdx = (idx + sndBank->aliasCount - offset) % sndBank->aliasCount; if (sndBank->aliasIndex[freeIdx].value == USHRT_MAX) break; @@ -268,37 +216,27 @@ bool LoadSoundAliasIndexList(MemoryManager* memory, SndBank* sndBank) return true; } -bool LoadSoundAliasList(MemoryManager* memory, SndBank* sndBank, const SearchPathOpenFile& file, unsigned int* loadedEntryCount, unsigned int* streamedEntryCount) +bool LoadSoundAliasList( + MemoryManager* memory, SndBank* sndBank, const SearchPathOpenFile& file, unsigned int* loadedEntryCount, unsigned int* streamedEntryCount) { - const CsvInputStream aliasCsv(*file.m_stream); - std::vector> csvLines; - std::vector currentLine; - auto maxCols = 0u; - - while (aliasCsv.NextRow(currentLine)) - { - if (currentLine.size() > maxCols) - maxCols = currentLine.size(); - csvLines.emplace_back(std::move(currentLine)); - currentLine = std::vector(); - } + const CsvInputStream aliasCsvStream(*file.m_stream); + const ParsedCsv aliasCsv(aliasCsvStream, true); // Ensure there is at least one entry in the csv after the headers - if (maxCols * csvLines.size() > maxCols) + if (aliasCsv.Size() > 0) { // should be the total number of assets - sndBank->aliasCount = csvLines.size() - 1; + sndBank->aliasCount = aliasCsv.Size(); sndBank->alias = static_cast(memory->Alloc(sizeof(SndAliasList) * sndBank->aliasCount)); memset(sndBank->alias, 0, sizeof(SndAliasList) * sndBank->aliasCount); - LoadSoundAliasHeader(csvLines[0]); - - auto row = 1u; + auto row = 0u; auto listIndex = 0u; - while (row < csvLines.size()) + while (row < sndBank->aliasCount) { - // count how many of the next rows should be in the sound alias sub-list. Aliases are part of the same sub list if they have the same name for a different file - auto subListCount = GetAliasSubListCount(row, csvLines); + // count how many of the next rows should be in the sound alias sub-list. Aliases are part of the same sub list if they have the same name for a + // different file + auto subListCount = GetAliasSubListCount(row, aliasCsv); if (subListCount < 1) return false; @@ -307,11 +245,11 @@ bool LoadSoundAliasList(MemoryManager* memory, SndBank* sndBank, const SearchPat sndBank->alias[listIndex].head = static_cast(memory->Alloc(sizeof(SndAlias) * subListCount)); sndBank->alias[listIndex].sequence = 0; - // populate the sublist with the next X number of aliases in the file. Note: this will only work correctly if the aliases that are a part of a sub list are next to each other in the file - for (auto i = 0; i < subListCount; i++) + // populate the sublist with the next X number of aliases in the file. Note: this will only work correctly if the aliases that are a part of a sub + // list are next to each other in the file + for (auto i = 0u; i < subListCount; i++) { - const auto& aliasValues = csvLines[row]; - if (!LoadSoundAlias(memory, &sndBank->alias[listIndex].head[i], aliasValues)) + if (!LoadSoundAlias(memory, &sndBank->alias[listIndex].head[i], aliasCsv[row])) return false; // if this asset is loaded instead of stream, increment the loaded count for later @@ -323,7 +261,8 @@ bool LoadSoundAliasList(MemoryManager* memory, SndBank* sndBank, const SearchPat row++; } - // the main alias list id and name should match that of the entries in the sub list (since they all have the same name, all sub entries will be the same) + // the main alias list id and name should match that of the entries in the sub list (since they all have the same name, all sub entries will be the + // same) sndBank->alias[listIndex].id = sndBank->alias[listIndex].head[0].id; sndBank->alias[listIndex].name = sndBank->alias[listIndex].head[0].name; @@ -349,6 +288,113 @@ bool LoadSoundAliasList(MemoryManager* memory, SndBank* sndBank, const SearchPat return true; } +bool LoadSoundRadverbs(MemoryManager* memory, SndBank* sndBank, const SearchPathOpenFile& file) +{ + const CsvInputStream radverbCsvStream(*file.m_stream); + const ParsedCsv radverbCsv(radverbCsvStream, true); + + if (radverbCsv.Size() > 0) + { + sndBank->radverbCount = radverbCsv.Size(); + sndBank->radverbs = static_cast(memory->Alloc(sizeof(SndRadverb) * sndBank->radverbCount)); + memset(sndBank->radverbs, 0, sizeof(SndRadverb) * sndBank->radverbCount); + + for (auto i = 0u; i < sndBank->radverbCount; i++) + { + auto& row = radverbCsv[i]; + + auto& name = row.GetValue("name", true); + if (name.empty()) + return false; + + strncpy_s(sndBank->radverbs[i].name, name.data(), 32); + sndBank->radverbs[i].id = Common::SND_HashName(name.data()); + sndBank->radverbs[i].smoothing = row.GetValueAs("smoothing"); + sndBank->radverbs[i].earlyTime = row.GetValueAs("earlyTime"); + sndBank->radverbs[i].lateTime = row.GetValueAs("lateTime"); + sndBank->radverbs[i].earlyGain = row.GetValueAs("earlyGain"); + sndBank->radverbs[i].lateGain = row.GetValueAs("lateGain"); + sndBank->radverbs[i].returnGain = row.GetValueAs("returnGain"); + sndBank->radverbs[i].earlyLpf = row.GetValueAs("earlyLpf"); + sndBank->radverbs[i].lateLpf = row.GetValueAs("lateLpf"); + sndBank->radverbs[i].inputLpf = row.GetValueAs("inputLpf"); + sndBank->radverbs[i].dampLpf = row.GetValueAs("dampLpf"); + sndBank->radverbs[i].wallReflect = row.GetValueAs("wallReflect"); + sndBank->radverbs[i].dryGain = row.GetValueAs("dryGain"); + sndBank->radverbs[i].earlySize = row.GetValueAs("earlySize"); + sndBank->radverbs[i].lateSize = row.GetValueAs("lateSize"); + sndBank->radverbs[i].diffusion = row.GetValueAs("diffusion"); + sndBank->radverbs[i].returnHighpass = row.GetValueAs("returnHighpass"); + } + } + + return true; +} + +bool LoadSoundDuckList(ISearchPath* searchPath, MemoryManager* memory, SndBank* sndBank, const SearchPathOpenFile& file) +{ + const CsvInputStream duckListCsvStream(*file.m_stream); + const ParsedCsv duckListCsv(duckListCsvStream, true); + + if (duckListCsv.Size() > 0) + { + sndBank->duckCount = duckListCsv.Size(); + sndBank->ducks = static_cast(memory->Alloc(sizeof(SndDuck) * sndBank->duckCount)); + memset(sndBank->ducks, 0, sizeof(SndDuck) * sndBank->duckCount); + + for (auto i = 0u; i < sndBank->duckCount; i++) + { + auto* duck = &sndBank->ducks[i]; + auto& row = duckListCsv[i]; + + const auto name = row.GetValue("name", true); + if (name.empty()) + return false; + + const auto duckFile = searchPath->Open("soundbank/ducks/" + name + ".duk"); + if (!duckFile.IsOpen()) + { + std::cerr << "Unable to find .duk file for " << name << " in ducklist for sound bank " << sndBank->name << std::endl; + return false; + } + + strncpy_s(duck->name, name.data(), 32); + duck->id = Common::SND_HashName(name.data()); + + auto duckJson = nlohmann::json::parse(*duckFile.m_stream); + duck->fadeIn = duckJson["fadeIn"].get(); + duck->fadeOut = duckJson["fadeOut"].get(); + duck->startDelay = duckJson["startDelay"].get(); + duck->distance = duckJson["distance"].get(); + duck->length = duckJson["length"].get(); + duck->updateWhilePaused = duckJson["updateWhilePaused"].get(); + + duck->fadeInCurve = duckJson["fadeInCurveId"].get(); + duck->fadeOutCurve = duckJson["fadeOutCurveId"].get(); + + if (duckJson.contains("fadeInCurve")) + duck->fadeInCurve = Common::SND_HashName(duckJson["fadeInCurve"].get().data()); + + if (duckJson.contains("fadeOutCurve")) + duck->fadeOutCurve = Common::SND_HashName(duckJson["fadeOutCurve"].get().data()); + + duck->attenuation = static_cast(memory->Alloc(sizeof(SndFloatAlign16) * 32)); + duck->filter = static_cast(memory->Alloc(sizeof(SndFloatAlign16) * 32)); + + for (auto& valueJson : duckJson["values"]) + { + auto index = + GetValueIndex(valueJson["duckGroup"].get(), ObjConstants::SOUND_DUCK_GROUPS.data(), ObjConstants::SOUND_DUCK_GROUPS.size()); + + duck->attenuation[index] = valueJson["attenuation"].get(); + duck->filter[index] = valueJson["filter"].get(); + } + } + } + + return true; +} + bool AssetLoaderSoundBank::LoadFromRaw( const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const { @@ -374,13 +420,27 @@ bool AssetLoaderSoundBank::LoadFromRaw( if (!LoadSoundAliasList(memory, sndBank, aliasFile, &loadedEntryCount, &streamedEntryCount)) return false; - // open the soundbank reverbs - sndBank->radverbs = nullptr; - sndBank->radverbCount = 0; + // load the soundbank reverbs + const auto radverbFile = searchPath->Open("soundbank/" + assetName + ".reverbs.csv"); + if (radverbFile.IsOpen()) + { + if (!LoadSoundRadverbs(memory, sndBank, radverbFile)) + { + std::cerr << "Sound Bank reverbs file for " << assetName << " is invalid" << std::endl; + return false; + } + } - // open the soundbank ducks - sndBank->ducks = nullptr; - sndBank->duckCount = 0; + // load the soundbank ducks + const auto duckListFile = searchPath->Open("soundbank/" + assetName + ".ducklist.csv"); + if (duckListFile.IsOpen()) + { + if (!LoadSoundDuckList(searchPath, memory, sndBank, duckListFile)) + { + std::cerr << "Sound Bank ducklist file for " << assetName << " is invalid" << std::endl; + return false; + } + } if (loadedEntryCount > 0) { From a020de6f808ce96a907cc6659d522d05bc0d03fb Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 26 Jan 2024 12:14:47 -0500 Subject: [PATCH 06/25] SoundBankWriter code --- src/Crypto/Crypto.cpp | 6 + src/Crypto/Crypto.h | 2 + src/Crypto/Impl/AlgorithmMD5.cpp | 64 +++++ src/Crypto/Impl/AlgorithmMD5.h | 20 ++ src/Linker/Linker.cpp | 3 + src/ObjCommon/Csv/ParsedCsv.cpp | 21 +- src/ObjCommon/Csv/ParsedCsv.h | 9 +- .../ObjContainer/SoundBank/SoundBankTypes.h | 0 src/ObjCommon/Sound/WavTypes.h | 17 ++ .../T6/AssetLoaders/AssetLoaderSoundBank.cpp | 186 ++++++++++--- .../SoundBank/SoundBankWriter.cpp | 261 ++++++++++++++++++ .../ObjContainer/SoundBank/SoundBankWriter.h | 25 ++ src/ObjWriting.lua | 3 +- src/ObjWriting/Sound/WavWriter.cpp | 2 - src/ObjWriting/Sound/WavWriter.h | 8 +- 15 files changed, 565 insertions(+), 62 deletions(-) create mode 100644 src/Crypto/Impl/AlgorithmMD5.cpp create mode 100644 src/Crypto/Impl/AlgorithmMD5.h rename src/{ObjLoading => ObjCommon}/ObjContainer/SoundBank/SoundBankTypes.h (100%) create mode 100644 src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp create mode 100644 src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.h diff --git a/src/Crypto/Crypto.cpp b/src/Crypto/Crypto.cpp index 4ee69ce40..3dc07fd7a 100644 --- a/src/Crypto/Crypto.cpp +++ b/src/Crypto/Crypto.cpp @@ -1,10 +1,16 @@ #include "Crypto.h" +#include "Impl/AlgorithmMD5.h" #include "Impl/AlgorithmRSA.h" #include "Impl/AlgorithmSHA1.h" #include "Impl/AlgorithmSHA256.h" #include "Impl/AlgorithmSalsa20.h" +std::unique_ptr Crypto::CreateMD5() +{ + return std::make_unique(); +} + std::unique_ptr Crypto::CreateSHA1() { return std::make_unique(); diff --git a/src/Crypto/Crypto.h b/src/Crypto/Crypto.h index 40f997528..e7e5edb8e 100644 --- a/src/Crypto/Crypto.h +++ b/src/Crypto/Crypto.h @@ -16,6 +16,8 @@ class Crypto RSA_PADDING_PSS, }; + static std::unique_ptr CreateMD5(); + static std::unique_ptr CreateSHA1(); static std::unique_ptr CreateSHA256(); diff --git a/src/Crypto/Impl/AlgorithmMD5.cpp b/src/Crypto/Impl/AlgorithmMD5.cpp new file mode 100644 index 000000000..215583839 --- /dev/null +++ b/src/Crypto/Impl/AlgorithmMD5.cpp @@ -0,0 +1,64 @@ +#include "AlgorithmSHA1.h" + +#include "CryptoLibrary.h" + +#include + +class AlgorithmSHA1::AlgorithmSHA1Impl +{ + hash_state m_state{}; + +public: + AlgorithmSHA1Impl() + { + CryptoLibrary::Init(); + + Init(); + } + + void Init() + { + sha1_init(&m_state); + } + + void Process(const void* input, const size_t inputSize) + { + sha1_process(&m_state, static_cast(input), inputSize); + } + + void Finish(void* hashBuffer) + { + sha1_done(&m_state, static_cast(hashBuffer)); + } +}; + +AlgorithmSHA1::AlgorithmSHA1() +{ + m_impl = new AlgorithmSHA1Impl(); +} + +AlgorithmSHA1::~AlgorithmSHA1() +{ + delete m_impl; + m_impl = nullptr; +} + +size_t AlgorithmSHA1::GetHashSize() +{ + return HASH_SIZE; +} + +void AlgorithmSHA1::Init() +{ + m_impl->Init(); +} + +void AlgorithmSHA1::Process(const void* input, const size_t inputSize) +{ + m_impl->Process(input, inputSize); +} + +void AlgorithmSHA1::Finish(void* hashBuffer) +{ + m_impl->Finish(hashBuffer); +} diff --git a/src/Crypto/Impl/AlgorithmMD5.h b/src/Crypto/Impl/AlgorithmMD5.h new file mode 100644 index 000000000..f9ae05143 --- /dev/null +++ b/src/Crypto/Impl/AlgorithmMD5.h @@ -0,0 +1,20 @@ +#pragma once +#include "IHashFunction.h" + +class AlgorithmSHA1 : public IHashFunction +{ + class AlgorithmSHA1Impl; + AlgorithmSHA1Impl* m_impl; + +public: + static const int HASH_SIZE = 20; + + AlgorithmSHA1(); + ~AlgorithmSHA1() override; + + size_t GetHashSize() override; + + void Init() override; + void Process(const void* input, size_t inputSize) override; + void Finish(void* hashBuffer) override; +}; diff --git a/src/Linker/Linker.cpp b/src/Linker/Linker.cpp index c6d2940e9..613aee22b 100644 --- a/src/Linker/Linker.cpp +++ b/src/Linker/Linker.cpp @@ -9,6 +9,7 @@ #include "LinkerSearchPaths.h" #include "ObjContainer/IPak/IPakWriter.h" #include "ObjContainer/IWD/IWD.h" +#include "ObjContainer/SoundBank/SoundBankWriter.h" #include "ObjLoading.h" #include "ObjWriting.h" #include "SearchPath/SearchPaths.h" @@ -419,6 +420,8 @@ class LinkerImpl final : public Linker SearchPaths& gdtSearchPaths, SearchPaths& sourceSearchPaths) const { + SoundBankWriter::OutputPath = fs::path(m_args.GetOutputFolderPathForProject(projectName)); + const auto zone = CreateZoneForDefinition(targetName, zoneDefinition, &assetSearchPaths, &gdtSearchPaths, &sourceSearchPaths); auto result = zone != nullptr; if (zone) diff --git a/src/ObjCommon/Csv/ParsedCsv.cpp b/src/ObjCommon/Csv/ParsedCsv.cpp index 77ef87ee0..3ac962291 100644 --- a/src/ObjCommon/Csv/ParsedCsv.cpp +++ b/src/ObjCommon/Csv/ParsedCsv.cpp @@ -6,7 +6,7 @@ ParsedCsvRow::ParsedCsvRow(std::unordered_map& headers, std { } -const std::string& ParsedCsvRow::GetValue(const std::string& header, bool required) const +const std::string ParsedCsvRow::GetValue(const std::string& header, bool required) const { if (this->headers.find(header) == this->headers.end()) { @@ -14,19 +14,34 @@ const std::string& ParsedCsvRow::GetValue(const std::string& header, bool requir std::cerr << "ERROR: Required column \"" << header << "\" was not found"; else std::cerr << "WARNING: Expected column \"" << header << "\" was not found"; - return nullptr; + + return {}; } auto& value = this->values.at(this->headers[header]); if (required && value.empty()) { std::cerr << "ERROR: Required column \"" << header << "\" does not have a value"; - return nullptr; + return {}; } return value; } +const float ParsedCsvRow::GetValueFloat(const std::string& header, bool required) const +{ + const auto& value = this->GetValue(header, required); + if (!value.empty()) + { + std::istringstream ss(value); + float out; + ss >> out; + return out; + } + + return {}; +} + ParsedCsv::ParsedCsv(const CsvInputStream& inputStream, bool hasHeaders) { std::vector> csvLines; diff --git a/src/ObjCommon/Csv/ParsedCsv.h b/src/ObjCommon/Csv/ParsedCsv.h index 01df30fbd..90973761b 100644 --- a/src/ObjCommon/Csv/ParsedCsv.h +++ b/src/ObjCommon/Csv/ParsedCsv.h @@ -10,17 +10,18 @@ class ParsedCsvRow public: explicit ParsedCsvRow(std::unordered_map& headers, std::vector& row); - const std::string& GetValue(const std::string& header, bool required = false) const; + const std::string GetValue(const std::string& header, bool required = false) const; + const float GetValueFloat(const std::string& header, bool required = false) const; - template T GetValueAs(const std::string& header, bool required = false) const + template T GetValueInt(const std::string& header, bool required = false) const { const auto& value = this->GetValue(header, required); if (!value.empty()) { std::istringstream ss(value); - T out{}; + long long out; ss >> out; - return out; + return static_cast(out); } return {}; diff --git a/src/ObjLoading/ObjContainer/SoundBank/SoundBankTypes.h b/src/ObjCommon/ObjContainer/SoundBank/SoundBankTypes.h similarity index 100% rename from src/ObjLoading/ObjContainer/SoundBank/SoundBankTypes.h rename to src/ObjCommon/ObjContainer/SoundBank/SoundBankTypes.h diff --git a/src/ObjCommon/Sound/WavTypes.h b/src/ObjCommon/Sound/WavTypes.h index 18dc14b9b..d14f34ed9 100644 --- a/src/ObjCommon/Sound/WavTypes.h +++ b/src/ObjCommon/Sound/WavTypes.h @@ -28,3 +28,20 @@ struct WavFormatChunkPcm uint16_t nBlockAlign; uint16_t wBitsPerSample; }; + +struct WavMetaData +{ + unsigned channelCount; + unsigned samplesPerSec; + unsigned bitsPerSample; +}; + +struct WavHeader +{ + unsigned int chunkIdRiff; + unsigned int chunkIdSize; + unsigned int format; + WavChunkHeader chunkHeader; + WavFormatChunkPcm formatChunk; + WavChunkHeader subChunkHeader; +}; \ No newline at end of file diff --git a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp index ffa9263dd..8f97dbdc1 100644 --- a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp +++ b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp @@ -1,6 +1,7 @@ #include "AssetLoaderSoundBank.h" #include "Csv/ParsedCsv.h" +#include "ObjContainer/SoundBank/SoundBankWriter.h" #include "nlohmann/json.hpp" #include "Game/T6/CommonT6.h" @@ -11,8 +12,54 @@ #include #include #include +#include using namespace T6; +namespace fs = std::filesystem; + +namespace +{ + const std::string PREFIXES_TO_DROP[] { + "raw/", + "devraw/", + }; + + _NODISCARD std::string GetSoundFilePath(SndAlias* sndAlias) + { + std::string soundFilePath(sndAlias->assetFileName); + + std::replace(soundFilePath.begin(), soundFilePath.end(), '\\', '/'); + for (const auto& droppedPrefix : PREFIXES_TO_DROP) + { + if (soundFilePath.rfind(droppedPrefix, 0) != std::string::npos) + { + soundFilePath.erase(0, droppedPrefix.size()); + break; + } + } + + return soundFilePath; + } + + _NODISCARD std::unique_ptr OpenSoundBankOutputFile(const std::string& bankName) + { + fs::path assetPath = SoundBankWriter::OutputPath / bankName; + + auto assetDir(assetPath); + assetDir.remove_filename(); + + create_directories(assetDir); + + auto outputStream = std::make_unique(assetPath, std::ios_base::out | std::ios_base::binary); + + if (outputStream->is_open()) + { + return std::move(outputStream); + } + + return nullptr; + } +} void* AssetLoaderSoundBank::CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) { @@ -84,38 +131,44 @@ bool LoadSoundAlias(MemoryManager* memory, SndAlias* alias, const ParsedCsvRow& alias->id = Common::SND_HashName(name.data()); alias->assetFileName = memory->Dup(aliasFileName.data()); alias->assetId = Common::SND_HashName(aliasFileName.data()); - alias->secondaryname = memory->Dup(row.GetValue("secondary").data()); - alias->subtitle = memory->Dup(row.GetValue("subtitle").data()); + + auto secondaryName = row.GetValue("secondary"); + if (!secondaryName.empty()) + alias->secondaryname = memory->Dup(secondaryName.data()); + + auto subtitle = row.GetValue("subtitle"); + if (!subtitle.empty()) + alias->subtitle = memory->Dup(subtitle.data()); alias->duck = Common::SND_HashName(row.GetValue("duck").data()); - alias->volMin = row.GetValueAs("vol_min"); - alias->volMax = row.GetValueAs("vol_max"); - alias->distMin = row.GetValueAs("dist_min"); - alias->distMax = row.GetValueAs("dist_max"); - alias->distReverbMax = row.GetValueAs("dist_reverb_max"); - alias->limitCount = row.GetValueAs("limit_count"); - alias->entityLimitCount = row.GetValueAs("entity_limit_count"); - alias->pitchMin = row.GetValueAs("pitch_min"); - alias->pitchMax = row.GetValueAs("pitch_max"); - alias->minPriority = row.GetValueAs("min_priority"); - alias->maxPriority = row.GetValueAs("max_priority"); - alias->minPriorityThreshold = row.GetValueAs("min_priority_threshold"); - alias->maxPriorityThreshold = row.GetValueAs("max_priority_threshold"); - alias->probability = row.GetValueAs("probability"); - alias->startDelay = row.GetValueAs("start_delay"); - alias->reverbSend = row.GetValueAs("reverb_send"); - alias->centerSend = row.GetValueAs("center_send"); - alias->envelopMin = row.GetValueAs("envelop_min"); - alias->envelopMax = row.GetValueAs("envelop_max"); - alias->envelopPercentage = row.GetValueAs("envelop_percentage"); - alias->occlusionLevel = row.GetValueAs("occlusion_level"); - alias->fluxTime = row.GetValueAs("move_time"); - alias->futzPatch = row.GetValueAs("futz"); - alias->contextType = row.GetValueAs("context_type"); - alias->contextValue = row.GetValueAs("context_value"); - alias->fadeIn = row.GetValueAs("fade_in"); - alias->fadeOut = row.GetValueAs("fade_out"); + alias->volMin = row.GetValueInt("vol_min"); + alias->volMax = row.GetValueInt("vol_max"); + alias->distMin = row.GetValueInt("dist_min"); + alias->distMax = row.GetValueInt("dist_max"); + alias->distReverbMax = row.GetValueInt("dist_reverb_max"); + alias->limitCount = row.GetValueInt("limit_count"); + alias->entityLimitCount = row.GetValueInt("entity_limit_count"); + alias->pitchMin = row.GetValueInt("pitch_min"); + alias->pitchMax = row.GetValueInt("pitch_max"); + alias->minPriority = row.GetValueInt("min_priority"); + alias->maxPriority = row.GetValueInt("max_priority"); + alias->minPriorityThreshold = row.GetValueInt("min_priority_threshold"); + alias->maxPriorityThreshold = row.GetValueInt("max_priority_threshold"); + alias->probability = row.GetValueInt("probability"); + alias->startDelay = row.GetValueInt("start_delay"); + alias->reverbSend = row.GetValueInt("reverb_send"); + alias->centerSend = row.GetValueInt("center_send"); + alias->envelopMin = row.GetValueInt("envelop_min"); + alias->envelopMax = row.GetValueInt("envelop_max"); + alias->envelopPercentage = row.GetValueInt("envelop_percentage"); + alias->occlusionLevel = row.GetValueInt("occlusion_level"); + alias->fluxTime = row.GetValueInt("move_time"); + alias->futzPatch = row.GetValueInt("futz"); + alias->contextType = row.GetValueInt("context_type"); + alias->contextValue = row.GetValueInt("context_value"); + alias->fadeIn = row.GetValueInt("fade_in"); + alias->fadeOut = row.GetValueInt("fade_out"); alias->flags.looping = row.GetValue("loop") == "looping"; alias->flags.panType = row.GetValue("pan") == "3d"; @@ -309,22 +362,22 @@ bool LoadSoundRadverbs(MemoryManager* memory, SndBank* sndBank, const SearchPath strncpy_s(sndBank->radverbs[i].name, name.data(), 32); sndBank->radverbs[i].id = Common::SND_HashName(name.data()); - sndBank->radverbs[i].smoothing = row.GetValueAs("smoothing"); - sndBank->radverbs[i].earlyTime = row.GetValueAs("earlyTime"); - sndBank->radverbs[i].lateTime = row.GetValueAs("lateTime"); - sndBank->radverbs[i].earlyGain = row.GetValueAs("earlyGain"); - sndBank->radverbs[i].lateGain = row.GetValueAs("lateGain"); - sndBank->radverbs[i].returnGain = row.GetValueAs("returnGain"); - sndBank->radverbs[i].earlyLpf = row.GetValueAs("earlyLpf"); - sndBank->radverbs[i].lateLpf = row.GetValueAs("lateLpf"); - sndBank->radverbs[i].inputLpf = row.GetValueAs("inputLpf"); - sndBank->radverbs[i].dampLpf = row.GetValueAs("dampLpf"); - sndBank->radverbs[i].wallReflect = row.GetValueAs("wallReflect"); - sndBank->radverbs[i].dryGain = row.GetValueAs("dryGain"); - sndBank->radverbs[i].earlySize = row.GetValueAs("earlySize"); - sndBank->radverbs[i].lateSize = row.GetValueAs("lateSize"); - sndBank->radverbs[i].diffusion = row.GetValueAs("diffusion"); - sndBank->radverbs[i].returnHighpass = row.GetValueAs("returnHighpass"); + sndBank->radverbs[i].smoothing = row.GetValueFloat("smoothing"); + sndBank->radverbs[i].earlyTime = row.GetValueFloat("earlyTime"); + sndBank->radverbs[i].lateTime = row.GetValueFloat("lateTime"); + sndBank->radverbs[i].earlyGain = row.GetValueFloat("earlyGain"); + sndBank->radverbs[i].lateGain = row.GetValueFloat("lateGain"); + sndBank->radverbs[i].returnGain = row.GetValueFloat("returnGain"); + sndBank->radverbs[i].earlyLpf = row.GetValueFloat("earlyLpf"); + sndBank->radverbs[i].lateLpf = row.GetValueFloat("lateLpf"); + sndBank->radverbs[i].inputLpf = row.GetValueFloat("inputLpf"); + sndBank->radverbs[i].dampLpf = row.GetValueFloat("dampLpf"); + sndBank->radverbs[i].wallReflect = row.GetValueFloat("wallReflect"); + sndBank->radverbs[i].dryGain = row.GetValueFloat("dryGain"); + sndBank->radverbs[i].earlySize = row.GetValueFloat("earlySize"); + sndBank->radverbs[i].lateSize = row.GetValueFloat("lateSize"); + sndBank->radverbs[i].diffusion = row.GetValueFloat("diffusion"); + sndBank->radverbs[i].returnHighpass = row.GetValueFloat("returnHighpass"); } } @@ -416,6 +469,7 @@ bool AssetLoaderSoundBank::LoadFromRaw( sndBank->name = memory->Dup(assetName.c_str()); auto sndBankLocalization = utils::StringSplit(assetName, '.'); + // load the soundbank aliases unsigned int loadedEntryCount = 0u, streamedEntryCount = 0u; if (!LoadSoundAliasList(memory, sndBank, aliasFile, &loadedEntryCount, &streamedEntryCount)) return false; @@ -442,6 +496,9 @@ bool AssetLoaderSoundBank::LoadFromRaw( } } + std::unique_ptr sablStream, sabsStream; + std::unique_ptr sablWriter, sabsWriter; + if (loadedEntryCount > 0) { sndBank->loadAssetBank.zone = memory->Dup(sndBankLocalization.at(0).c_str()); @@ -454,6 +511,11 @@ bool AssetLoaderSoundBank::LoadFromRaw( sndBank->loadedAssets.entryCount = loadedEntryCount; sndBank->loadedAssets.entries = static_cast(memory->Alloc(sizeof(SndAssetBankEntry) * loadedEntryCount)); memset(sndBank->loadedAssets.entries, 0, sizeof(SndAssetBankEntry) * loadedEntryCount); + + const auto sablName = assetName + ".sabl"; + sablStream = OpenSoundBankOutputFile(sablName); + if (sablStream) + sablWriter = SoundBankWriter::Create(sablName, *sablStream, searchPath); } if (streamedEntryCount > 0) @@ -461,6 +523,40 @@ bool AssetLoaderSoundBank::LoadFromRaw( sndBank->streamAssetBank.zone = memory->Dup(sndBankLocalization.at(0).c_str()); sndBank->streamAssetBank.language = memory->Dup(sndBankLocalization.at(1).c_str()); memset(sndBank->streamAssetBank.linkTimeChecksum, 0xCC, 16); + + const auto sabsName = assetName + ".sabs"; + sabsStream = OpenSoundBankOutputFile(sabsName); + if (sabsStream) + sablWriter = SoundBankWriter::Create(sabsName, *sabsStream, searchPath); + } + + // add aliases to the correct sound bank writer + for (auto i = 0u; i < sndBank->aliasCount; i++) + { + auto* aliasList = &sndBank->alias[i]; + for (auto j = 0; j < aliasList->count; j++) + { + auto* alias = &aliasList->head[j]; + + if (sabsWriter && alias->flags.loadType == T6::SA_STREAMED) + sabsWriter->AddSound(GetSoundFilePath(alias), alias->assetId); + else if (sablWriter) + sablWriter->AddSound(GetSoundFilePath(alias), alias->assetId); + } + } + + // write the output linked sound bank + if (sablWriter) + { + sablWriter->Write(); + sablStream->close(); + } + + // write the output streamed sound bank + if (sabsWriter) + { + sabsWriter->Write(); + sabsStream->close(); } manager->AddAsset(ASSET_TYPE_SOUND, assetName, sndBank); diff --git a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp new file mode 100644 index 000000000..b371dee98 --- /dev/null +++ b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp @@ -0,0 +1,261 @@ +#include "SoundBankWriter.h" + +#include "Crypto.h" +#include "ObjContainer/SoundBank/SoundBankTypes.h" +#include "Sound/WavTypes.h" +#include "Utils/Alignment.h" +#include "Utils/FileUtils.h" + +#include +#include +#include + +std::unordered_map INDEX_FOR_FRAMERATE{ + {8000, 0}, + {12000, 1}, + {16000, 2}, + {24000, 3}, + {32000, 4}, + {44100, 5}, + {48000, 6}, + {96000, 7}, + {192000, 8}, +}; + +class SoundBankWriterImpl : public SoundBankWriter +{ + static constexpr char BRANDING[] = "Created with OAT - OpenAssetTools"; + static constexpr int64_t DATA_OFFSET = 0x800; + static constexpr uint32_t MAGIC = FileUtils::MakeMagic32('2', 'U', 'X', '#'); + static constexpr uint32_t VERSION = 14u; + + inline static const std::string PAD_DATA = std::string(16, '\x00'); + +public: + explicit SoundBankWriterImpl::SoundBankWriterImpl(const std::string& fileName, std::ostream& stream, ISearchPath* assetSearchPath) + : m_file_name(fileName), + m_stream(stream), + m_asset_search_path(assetSearchPath), + m_sounds(), + m_current_offset(0), + m_total_size(0), + m_entry_section_offset(0), + m_checksum_section_offset(0) + { + } + + void AddSound(const std::string& soundFilePath, unsigned int soundId) override + { + this->m_sounds.push_back(std::make_pair(soundFilePath, soundId)); + } + + void GoTo(const int64_t offset) + { + m_stream.seekp(offset, std::ios::beg); + m_current_offset = offset; + } + + void Write(const void* data, const size_t dataSize) + { + m_stream.write(static_cast(data), dataSize); + m_current_offset += dataSize; + } + + void Pad(const size_t paddingSize) + { + auto paddingSizeLeft = paddingSize; + while (paddingSizeLeft > 0) + { + const auto writeSize = std::min(paddingSizeLeft, PAD_DATA.size()); + Write(PAD_DATA.data(), writeSize); + + paddingSizeLeft -= writeSize; + } + } + + void AlignToChunk() + { + if ((m_current_offset & 0xF) != 0) + Pad(0x10 - (m_current_offset & 0xF)); + } + + void WriteHeader() + { + GoTo(0); + + SoundAssetBankChecksum checksum{}; + memset(&checksum, 0xCC, sizeof(SoundAssetBankChecksum)); + + SoundAssetBankHeader header{MAGIC, + VERSION, + sizeof(SoundAssetBankEntry), + sizeof(SoundAssetBankChecksum), + 0x40, + m_entries.size(), + 0, + 0, + m_total_size, + m_entry_section_offset, + m_checksum_section_offset, + checksum}; + + strncpy(header.dependencies, m_file_name.data(), header.dependencySize); + + Write(&header, sizeof(header)); + } + + bool WriteEntries() + { + GoTo(DATA_OFFSET); + + for (auto& sound : m_sounds) + { + const auto soundFilePath = sound.first; + const auto soundId = sound.second; + + size_t soundSize = -1; + std::unique_ptr soundData; + + // try to find a wav file for the sound path + const auto wavFile = m_asset_search_path->Open(soundFilePath + ".wav"); + if (wavFile.IsOpen()) + { + WavHeader header{}; + wavFile.m_stream->read(reinterpret_cast(&header), sizeof(WavHeader)); + + soundSize = static_cast(wavFile.m_length - sizeof(WavHeader)); + auto frameCount = soundSize / (header.formatChunk.nChannels * (header.formatChunk.wBitsPerSample / 8)); + auto frameRateIndex = INDEX_FOR_FRAMERATE[header.formatChunk.nSamplesPerSec]; + + SoundAssetBankEntry entry{ + soundId, + soundSize, + static_cast(m_current_offset), + frameCount, + frameRateIndex, + static_cast(header.formatChunk.nChannels), + 0, + 0, + }; + + m_entries.push_back(entry); + + soundData = std::make_unique(soundSize); + wavFile.m_stream->read(soundData.get(), soundSize); + } + else + { + // if there is no wav file, try flac file + const auto flacFile = m_asset_search_path->Open(soundFilePath + ".wav"); + if (flacFile.IsOpen()) + { + soundSize = static_cast(flacFile.m_length); + + SoundAssetBankEntry entry{ + soundId, + soundSize, + static_cast(m_current_offset), + 0, + 0, + 0, + 0, + 8, + }; + + m_entries.push_back(entry); + + soundData = std::make_unique(soundSize); + flacFile.m_stream->read(soundData.get(), soundSize); + } + else + { + std::cerr << "Unable to find a compatible file for sound " << soundFilePath << std::endl; + return false; + } + } + + // calculate checksum + SoundAssetBankChecksum checksum{}; + + const auto md5Crypt = Crypto::CreateMD5(); + md5Crypt->Process(soundData.get(), soundSize); + md5Crypt->Finish(checksum.checksumBytes); + + m_checksums.push_back(checksum); + + // write data + Write(soundData.get(), soundSize); + } + + return true; + } + + void WriteEntryList() + { + AlignToChunk(); + + m_entry_section_offset = m_current_offset; + + for (auto& entry : m_entries) + { + Write(&entry, sizeof(SoundAssetBankEntry)); + } + } + + void WriteChecksumList() + { + m_checksum_section_offset = m_current_offset; + + for (auto& checksum : m_checksums) + { + Write(&checksum, sizeof(SoundAssetBankChecksum)); + } + } + + void WriteBranding() + { + AlignToChunk(); + Write(BRANDING, sizeof(BRANDING)); + AlignToChunk(); + } + + bool Write() override + { + WriteEntries(); + WriteEntryList(); + WriteChecksumList(); + WriteBranding(); + + m_total_size = m_current_offset; + + WriteHeader(); + + if (m_current_offset > UINT32_MAX) + { + std::cerr << "Sound bank files must be under 4GB. Please reduce the number of sounds being written!" << std::endl; + return false; + } + + return true; + } + +private: + std::string m_file_name; + std::ostream& m_stream; + ISearchPath* m_asset_search_path; + std::vector> m_sounds; + + int64_t m_current_offset; + std::vector m_entries; + std::vector m_checksums; + int64_t m_total_size; + int64_t m_entry_section_offset; + int64_t m_checksum_section_offset; +}; + +std::filesystem::path SoundBankWriter::OutputPath; + +std::unique_ptr SoundBankWriter::Create(const std::string& fileName, std::ostream& stream, ISearchPath* assetSearchPath) +{ + return std::make_unique(fileName, stream, assetSearchPath); +} diff --git a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.h b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.h new file mode 100644 index 000000000..a9f7f623e --- /dev/null +++ b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.h @@ -0,0 +1,25 @@ +#pragma once +#include "SearchPath/ISearchPath.h" + +#include +#include +#include + +class SoundBankWriter +{ +public: + SoundBankWriter() = default; + virtual ~SoundBankWriter() = default; + + SoundBankWriter(const SoundBankWriter& other) = default; + SoundBankWriter(SoundBankWriter&& other) noexcept = default; + SoundBankWriter& operator=(const SoundBankWriter& other) = default; + SoundBankWriter& operator=(SoundBankWriter&& other) noexcept = default; + + virtual void AddSound(const std::string& soundFilePath, unsigned int soundId) = 0; + virtual bool Write() = 0; + + static std::unique_ptr Create(const std::string& fileName, std::ostream& stream, ISearchPath* assetSearchPath); + + static std::filesystem::path OutputPath; +}; \ No newline at end of file diff --git a/src/ObjWriting.lua b/src/ObjWriting.lua index cf0ef6686..8505cd8b6 100644 --- a/src/ObjWriting.lua +++ b/src/ObjWriting.lua @@ -52,7 +52,8 @@ function ObjWriting:project() self:include(includes) Utils:include(includes) - json:include(includes) minilzo:include(includes) minizip:include(includes) + json:include(includes) + end diff --git a/src/ObjWriting/Sound/WavWriter.cpp b/src/ObjWriting/Sound/WavWriter.cpp index 6d0a5ae71..32b3e489f 100644 --- a/src/ObjWriting/Sound/WavWriter.cpp +++ b/src/ObjWriting/Sound/WavWriter.cpp @@ -1,7 +1,5 @@ #include "WavWriter.h" -#include "Sound/WavTypes.h" - WavWriter::WavWriter(std::ostream& stream) : m_stream(stream) { diff --git a/src/ObjWriting/Sound/WavWriter.h b/src/ObjWriting/Sound/WavWriter.h index 4e7c27843..8ee153e5e 100644 --- a/src/ObjWriting/Sound/WavWriter.h +++ b/src/ObjWriting/Sound/WavWriter.h @@ -1,12 +1,6 @@ #pragma once #include - -struct WavMetaData -{ - unsigned channelCount; - unsigned samplesPerSec; - unsigned bitsPerSample; -}; +#include class WavWriter { From f53c895d49feac2e4ffdaebe7304db63317bdd71 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 26 Jan 2024 12:30:39 -0500 Subject: [PATCH 07/25] fix MD5 crypto --- src/Crypto/Impl/AlgorithmMD5.cpp | 26 +++++++++++++------------- src/Crypto/Impl/AlgorithmMD5.h | 10 +++++----- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Crypto/Impl/AlgorithmMD5.cpp b/src/Crypto/Impl/AlgorithmMD5.cpp index 215583839..3e14e74b0 100644 --- a/src/Crypto/Impl/AlgorithmMD5.cpp +++ b/src/Crypto/Impl/AlgorithmMD5.cpp @@ -1,15 +1,15 @@ -#include "AlgorithmSHA1.h" +#include "AlgorithmMD5.h" #include "CryptoLibrary.h" #include -class AlgorithmSHA1::AlgorithmSHA1Impl +class AlgorithmMD5::AlgorithmMD5Impl { hash_state m_state{}; public: - AlgorithmSHA1Impl() + AlgorithmMD5Impl() { CryptoLibrary::Init(); @@ -18,47 +18,47 @@ class AlgorithmSHA1::AlgorithmSHA1Impl void Init() { - sha1_init(&m_state); + md5_init(&m_state); } void Process(const void* input, const size_t inputSize) { - sha1_process(&m_state, static_cast(input), inputSize); + md5_process(&m_state, static_cast(input), inputSize); } void Finish(void* hashBuffer) { - sha1_done(&m_state, static_cast(hashBuffer)); + md5_done(&m_state, static_cast(hashBuffer)); } }; -AlgorithmSHA1::AlgorithmSHA1() +AlgorithmMD5::AlgorithmMD5() { - m_impl = new AlgorithmSHA1Impl(); + m_impl = new AlgorithmMD5Impl(); } -AlgorithmSHA1::~AlgorithmSHA1() +AlgorithmMD5::~AlgorithmMD5() { delete m_impl; m_impl = nullptr; } -size_t AlgorithmSHA1::GetHashSize() +size_t AlgorithmMD5::GetHashSize() { return HASH_SIZE; } -void AlgorithmSHA1::Init() +void AlgorithmMD5::Init() { m_impl->Init(); } -void AlgorithmSHA1::Process(const void* input, const size_t inputSize) +void AlgorithmMD5::Process(const void* input, const size_t inputSize) { m_impl->Process(input, inputSize); } -void AlgorithmSHA1::Finish(void* hashBuffer) +void AlgorithmMD5::Finish(void* hashBuffer) { m_impl->Finish(hashBuffer); } diff --git a/src/Crypto/Impl/AlgorithmMD5.h b/src/Crypto/Impl/AlgorithmMD5.h index f9ae05143..25024f082 100644 --- a/src/Crypto/Impl/AlgorithmMD5.h +++ b/src/Crypto/Impl/AlgorithmMD5.h @@ -1,16 +1,16 @@ #pragma once #include "IHashFunction.h" -class AlgorithmSHA1 : public IHashFunction +class AlgorithmMD5 : public IHashFunction { - class AlgorithmSHA1Impl; - AlgorithmSHA1Impl* m_impl; + class AlgorithmMD5Impl; + AlgorithmMD5Impl* m_impl; public: static const int HASH_SIZE = 20; - AlgorithmSHA1(); - ~AlgorithmSHA1() override; + AlgorithmMD5(); + ~AlgorithmMD5() override; size_t GetHashSize() override; From 4e8d335c262c9556766276faecbb851be2e6fa14 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 26 Jan 2024 12:46:37 -0500 Subject: [PATCH 08/25] add climits for linux builds --- src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp index 8f97dbdc1..f8391925d 100644 --- a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp +++ b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp @@ -13,6 +13,7 @@ #include #include #include +#include using namespace T6; namespace fs = std::filesystem; From 365502d81957a0adda332a4913385c14837c5636 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 26 Jan 2024 12:58:31 -0500 Subject: [PATCH 09/25] more linux build errors --- .../T6/AssetLoaders/AssetLoaderSoundBank.cpp | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp index f8391925d..90fad2bd1 100644 --- a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp +++ b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp @@ -1,26 +1,25 @@ #include "AssetLoaderSoundBank.h" #include "Csv/ParsedCsv.h" -#include "ObjContainer/SoundBank/SoundBankWriter.h" -#include "nlohmann/json.hpp" - #include "Game/T6/CommonT6.h" #include "Game/T6/ObjConstantsT6.h" #include "Game/T6/T6.h" +#include "ObjContainer/SoundBank/SoundBankWriter.h" #include "Pool/GlobalAssetPool.h" +#include "nlohmann/json.hpp" #include +#include #include -#include #include -#include +#include using namespace T6; namespace fs = std::filesystem; namespace { - const std::string PREFIXES_TO_DROP[] { + const std::string PREFIXES_TO_DROP[]{ "raw/", "devraw/", }; @@ -60,7 +59,7 @@ namespace return nullptr; } -} +} // namespace void* AssetLoaderSoundBank::CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) { @@ -355,13 +354,13 @@ bool LoadSoundRadverbs(MemoryManager* memory, SndBank* sndBank, const SearchPath for (auto i = 0u; i < sndBank->radverbCount; i++) { - auto& row = radverbCsv[i]; + auto row = radverbCsv[i]; auto& name = row.GetValue("name", true); if (name.empty()) return false; - strncpy_s(sndBank->radverbs[i].name, name.data(), 32); + strncpy(sndBank->radverbs[i].name, name.data(), 32); sndBank->radverbs[i].id = Common::SND_HashName(name.data()); sndBank->radverbs[i].smoothing = row.GetValueFloat("smoothing"); sndBank->radverbs[i].earlyTime = row.GetValueFloat("earlyTime"); @@ -398,9 +397,8 @@ bool LoadSoundDuckList(ISearchPath* searchPath, MemoryManager* memory, SndBank* for (auto i = 0u; i < sndBank->duckCount; i++) { - auto* duck = &sndBank->ducks[i]; - auto& row = duckListCsv[i]; - + auto row = duckListCsv[i]; + const auto name = row.GetValue("name", true); if (name.empty()) return false; @@ -412,7 +410,8 @@ bool LoadSoundDuckList(ISearchPath* searchPath, MemoryManager* memory, SndBank* return false; } - strncpy_s(duck->name, name.data(), 32); + auto* duck = &sndBank->ducks[i]; + strncpy(duck->name, name.data(), 32); duck->id = Common::SND_HashName(name.data()); auto duckJson = nlohmann::json::parse(*duckFile.m_stream); @@ -545,7 +544,7 @@ bool AssetLoaderSoundBank::LoadFromRaw( sablWriter->AddSound(GetSoundFilePath(alias), alias->assetId); } } - + // write the output linked sound bank if (sablWriter) { From e2f8892278dde6008b7aa92d2e0b82d80e96192d Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 26 Jan 2024 13:05:07 -0500 Subject: [PATCH 10/25] clang formatting --- src/Linker/Linker.cpp | 2 +- src/ObjCommon/Csv/ParsedCsv.cpp | 2 +- src/ObjCommon/Csv/ParsedCsv.h | 6 +++--- src/ObjCommon/Sound/WavTypes.h | 2 +- src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp | 2 +- src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.h | 4 ++-- src/ObjWriting/Sound/WavWriter.h | 2 +- src/Utils/Utils/StringUtils.cpp | 4 ++-- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Linker/Linker.cpp b/src/Linker/Linker.cpp index 01a094e88..e9587c111 100644 --- a/src/Linker/Linker.cpp +++ b/src/Linker/Linker.cpp @@ -421,7 +421,7 @@ class LinkerImpl final : public Linker SearchPaths& sourceSearchPaths) const { SoundBankWriter::OutputPath = fs::path(m_args.GetOutputFolderPathForProject(projectName)); - + const auto zone = CreateZoneForDefinition(targetName, zoneDefinition, &assetSearchPaths, &gdtSearchPaths, &sourceSearchPaths); auto result = zone != nullptr; if (zone) diff --git a/src/ObjCommon/Csv/ParsedCsv.cpp b/src/ObjCommon/Csv/ParsedCsv.cpp index 3ac962291..d566eac86 100644 --- a/src/ObjCommon/Csv/ParsedCsv.cpp +++ b/src/ObjCommon/Csv/ParsedCsv.cpp @@ -14,7 +14,7 @@ const std::string ParsedCsvRow::GetValue(const std::string& header, bool require std::cerr << "ERROR: Required column \"" << header << "\" was not found"; else std::cerr << "WARNING: Expected column \"" << header << "\" was not found"; - + return {}; } diff --git a/src/ObjCommon/Csv/ParsedCsv.h b/src/ObjCommon/Csv/ParsedCsv.h index 90973761b..22d4e5be8 100644 --- a/src/ObjCommon/Csv/ParsedCsv.h +++ b/src/ObjCommon/Csv/ParsedCsv.h @@ -12,7 +12,7 @@ class ParsedCsvRow explicit ParsedCsvRow(std::unordered_map& headers, std::vector& row); const std::string GetValue(const std::string& header, bool required = false) const; const float GetValueFloat(const std::string& header, bool required = false) const; - + template T GetValueInt(const std::string& header, bool required = false) const { const auto& value = this->GetValue(header, required); @@ -35,8 +35,8 @@ class ParsedCsv public: explicit ParsedCsv(const CsvInputStream& inputStream, bool hasHeaders = true); - + size_t Size() const; ParsedCsvRow operator[](size_t index) const; -}; \ No newline at end of file +}; diff --git a/src/ObjCommon/Sound/WavTypes.h b/src/ObjCommon/Sound/WavTypes.h index d14f34ed9..1a9978163 100644 --- a/src/ObjCommon/Sound/WavTypes.h +++ b/src/ObjCommon/Sound/WavTypes.h @@ -44,4 +44,4 @@ struct WavHeader WavChunkHeader chunkHeader; WavFormatChunkPcm formatChunk; WavChunkHeader subChunkHeader; -}; \ No newline at end of file +}; diff --git a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp index b371dee98..57fc29d5a 100644 --- a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp +++ b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp @@ -19,7 +19,7 @@ std::unordered_map INDEX_FOR_FRAMERATE{ {44100, 5}, {48000, 6}, {96000, 7}, - {192000, 8}, + {192000, 8}, }; class SoundBankWriterImpl : public SoundBankWriter diff --git a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.h b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.h index a9f7f623e..88153b773 100644 --- a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.h +++ b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.h @@ -1,9 +1,9 @@ #pragma once #include "SearchPath/ISearchPath.h" +#include #include #include -#include class SoundBankWriter { @@ -22,4 +22,4 @@ class SoundBankWriter static std::unique_ptr Create(const std::string& fileName, std::ostream& stream, ISearchPath* assetSearchPath); static std::filesystem::path OutputPath; -}; \ No newline at end of file +}; diff --git a/src/ObjWriting/Sound/WavWriter.h b/src/ObjWriting/Sound/WavWriter.h index 8ee153e5e..eb0174a9a 100644 --- a/src/ObjWriting/Sound/WavWriter.h +++ b/src/ObjWriting/Sound/WavWriter.h @@ -1,6 +1,6 @@ #pragma once -#include #include +#include class WavWriter { diff --git a/src/Utils/Utils/StringUtils.cpp b/src/Utils/Utils/StringUtils.cpp index 31368acd3..cb5ede9c1 100644 --- a/src/Utils/Utils/StringUtils.cpp +++ b/src/Utils/Utils/StringUtils.cpp @@ -1,7 +1,7 @@ #include "StringUtils.h" -#include #include +#include namespace utils { @@ -106,7 +106,7 @@ namespace utils { std::vector strings{}; std::istringstream stream(str); - + std::string s; while (std::getline(stream, s, delim)) { From 7af802ff24e64b1e13d40b75737220a830328c13 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 26 Jan 2024 13:07:50 -0500 Subject: [PATCH 11/25] more linux build errors --- src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp index 57fc29d5a..3946f14c6 100644 --- a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp +++ b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp @@ -6,6 +6,7 @@ #include "Utils/Alignment.h" #include "Utils/FileUtils.h" +#include #include #include #include @@ -32,7 +33,7 @@ class SoundBankWriterImpl : public SoundBankWriter inline static const std::string PAD_DATA = std::string(16, '\x00'); public: - explicit SoundBankWriterImpl::SoundBankWriterImpl(const std::string& fileName, std::ostream& stream, ISearchPath* assetSearchPath) + explicit SoundBankWriterImpl(const std::string& fileName, std::ostream& stream, ISearchPath* assetSearchPath) : m_file_name(fileName), m_stream(stream), m_asset_search_path(assetSearchPath), From bc1ebfc3bc14218472607f77f8a532b4c43f1c2d Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 26 Jan 2024 13:31:36 -0500 Subject: [PATCH 12/25] .flac extension --- src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp index 3946f14c6..e9a728a5e 100644 --- a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp +++ b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp @@ -147,7 +147,7 @@ class SoundBankWriterImpl : public SoundBankWriter else { // if there is no wav file, try flac file - const auto flacFile = m_asset_search_path->Open(soundFilePath + ".wav"); + const auto flacFile = m_asset_search_path->Open(soundFilePath + ".flac"); if (flacFile.IsOpen()) { soundSize = static_cast(flacFile.m_length); From c3590a911c39362589985860a6af8550d1da4920 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 2 Feb 2024 11:28:21 -0500 Subject: [PATCH 13/25] dont build sabl if unable to find sound files --- src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp index e9a728a5e..449fe0e96 100644 --- a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp +++ b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp @@ -222,7 +222,12 @@ class SoundBankWriterImpl : public SoundBankWriter bool Write() override { - WriteEntries(); + if (!WriteEntries()) + { + std::cerr << "An error occurred writing the sound bank entires. Please check output." << std::endl; + return false; + } + WriteEntryList(); WriteChecksumList(); WriteBranding(); From 386f941d61aed8d6a8a5bc198d4a1a3d3236f469 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 2 Feb 2024 11:29:15 -0500 Subject: [PATCH 14/25] add comment --- src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp index 449fe0e96..0fc4fa6bb 100644 --- a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp +++ b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp @@ -84,6 +84,8 @@ class SoundBankWriterImpl : public SoundBankWriter { GoTo(0); + // The checksum here is supposed to be a MD5 of the entire data portion of the file. However T6 does not validate this. + // As long as the values here match the values in the SndBank asset, everything loads fine SoundAssetBankChecksum checksum{}; memset(&checksum, 0xCC, sizeof(SoundAssetBankChecksum)); From 9254e25b22d71b2d4a0c5e44b6dcd26f6e7871e4 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 2 Feb 2024 16:48:23 -0500 Subject: [PATCH 15/25] fix loaded sound banks and generating streamed sound banks --- .../T6/AssetLoaders/AssetLoaderSoundBank.cpp | 30 +++++++++++++--- .../SoundBank/SoundBankWriter.cpp | 36 +++++++++++++------ .../ObjContainer/SoundBank/SoundBankWriter.h | 4 +-- 3 files changed, 53 insertions(+), 17 deletions(-) diff --git a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp index 90fad2bd1..5a19f6e1f 100644 --- a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp +++ b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp @@ -512,6 +512,8 @@ bool AssetLoaderSoundBank::LoadFromRaw( sndBank->loadedAssets.entries = static_cast(memory->Alloc(sizeof(SndAssetBankEntry) * loadedEntryCount)); memset(sndBank->loadedAssets.entries, 0, sizeof(SndAssetBankEntry) * loadedEntryCount); + sndBank->runtimeAssetLoad = true; + const auto sablName = assetName + ".sabl"; sablStream = OpenSoundBankOutputFile(sablName); if (sablStream) @@ -527,7 +529,7 @@ bool AssetLoaderSoundBank::LoadFromRaw( const auto sabsName = assetName + ".sabs"; sabsStream = OpenSoundBankOutputFile(sabsName); if (sabsStream) - sablWriter = SoundBankWriter::Create(sabsName, *sabsStream, searchPath); + sabsWriter = SoundBankWriter::Create(sabsName, *sabsStream, searchPath); } // add aliases to the correct sound bank writer @@ -539,24 +541,42 @@ bool AssetLoaderSoundBank::LoadFromRaw( auto* alias = &aliasList->head[j]; if (sabsWriter && alias->flags.loadType == T6::SA_STREAMED) - sabsWriter->AddSound(GetSoundFilePath(alias), alias->assetId); + sabsWriter->AddSound(GetSoundFilePath(alias), alias->assetId, alias->flags.looping, true); else if (sablWriter) - sablWriter->AddSound(GetSoundFilePath(alias), alias->assetId); + sablWriter->AddSound(GetSoundFilePath(alias), alias->assetId, alias->flags.looping); } } // write the output linked sound bank if (sablWriter) { - sablWriter->Write(); + auto size = static_cast(sablWriter->Write()); sablStream->close(); + + if (size != UINT32_MAX) + { + sndBank->loadedAssets.dataSize = size; + sndBank->loadedAssets.data = static_cast(memory->Alloc(size)); + memset(sndBank->loadedAssets.data, 0, size); + } + else + { + std::cerr << "Loaded Sound Bank for " << assetName << " failed to generate. Please check your build files." << std::endl; + return false; + } } // write the output streamed sound bank if (sabsWriter) { - sabsWriter->Write(); + auto size = static_cast(sabsWriter->Write()); sabsStream->close(); + + if (size == UINT32_MAX) + { + std::cerr << "Streamed Sound Bank for " << assetName << " failed to generate. Please check your build files." << std::endl; + return false; + } } manager->AddAsset(ASSET_TYPE_SOUND, assetName, sndBank); diff --git a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp index 0fc4fa6bb..d44035da1 100644 --- a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp +++ b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp @@ -45,9 +45,9 @@ class SoundBankWriterImpl : public SoundBankWriter { } - void AddSound(const std::string& soundFilePath, unsigned int soundId) override + void AddSound(const std::string& soundFilePath, unsigned int soundId, bool looping, bool streamed) override { - this->m_sounds.push_back(std::make_pair(soundFilePath, soundId)); + this->m_sounds.push_back({soundFilePath, soundId, looping, streamed}); } void GoTo(const int64_t offset) @@ -113,8 +113,8 @@ class SoundBankWriterImpl : public SoundBankWriter for (auto& sound : m_sounds) { - const auto soundFilePath = sound.first; - const auto soundId = sound.second; + const auto& soundFilePath = sound.filePath; + const auto soundId = sound.soundId; size_t soundSize = -1; std::unique_ptr soundData; @@ -128,6 +128,13 @@ class SoundBankWriterImpl : public SoundBankWriter soundSize = static_cast(wavFile.m_length - sizeof(WavHeader)); auto frameCount = soundSize / (header.formatChunk.nChannels * (header.formatChunk.wBitsPerSample / 8)); + + if (!sound.streamed && header.formatChunk.nSamplesPerSec != 48000) + { + std::cout << "WARNING: \"" << soundFilePath << "\" has a framerate of " << header.formatChunk.nSamplesPerSec + << ". Loaded sounds are recommended to have a framerate of 48000. This sound may not work on all games!" << std::endl; + } + auto frameRateIndex = INDEX_FOR_FRAMERATE[header.formatChunk.nSamplesPerSec]; SoundAssetBankEntry entry{ @@ -137,7 +144,7 @@ class SoundBankWriterImpl : public SoundBankWriter frameCount, frameRateIndex, static_cast(header.formatChunk.nChannels), - 0, + sound.looping, 0, }; @@ -222,12 +229,12 @@ class SoundBankWriterImpl : public SoundBankWriter AlignToChunk(); } - bool Write() override + std::int64_t Write() override { if (!WriteEntries()) { std::cerr << "An error occurred writing the sound bank entires. Please check output." << std::endl; - return false; + return -1; } WriteEntryList(); @@ -241,17 +248,26 @@ class SoundBankWriterImpl : public SoundBankWriter if (m_current_offset > UINT32_MAX) { std::cerr << "Sound bank files must be under 4GB. Please reduce the number of sounds being written!" << std::endl; - return false; + return -1; } - return true; + // return the total size for the sound asset data + return m_entry_section_offset - DATA_OFFSET; } private: + struct SoundBankEntryInfo + { + std::string filePath; + unsigned int soundId; + bool looping; + bool streamed; + }; + std::string m_file_name; std::ostream& m_stream; ISearchPath* m_asset_search_path; - std::vector> m_sounds; + std::vector m_sounds; int64_t m_current_offset; std::vector m_entries; diff --git a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.h b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.h index 88153b773..cb0fb07a5 100644 --- a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.h +++ b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.h @@ -16,8 +16,8 @@ class SoundBankWriter SoundBankWriter& operator=(const SoundBankWriter& other) = default; SoundBankWriter& operator=(SoundBankWriter&& other) noexcept = default; - virtual void AddSound(const std::string& soundFilePath, unsigned int soundId) = 0; - virtual bool Write() = 0; + virtual void AddSound(const std::string& soundFilePath, unsigned int soundId, bool looping = false, bool streamed = false) = 0; + virtual std::int64_t Write() = 0; static std::unique_ptr Create(const std::string& fileName, std::ostream& stream, ISearchPath* assetSearchPath); From 42c4068d2a746300aedb4a3305394305412b3ce8 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 9 Feb 2024 10:49:55 -0500 Subject: [PATCH 16/25] add flac decoding to correctly add flac sounds to sound banks --- .gitmodules | 3 + premake5.lua | 2 + src/ObjCommon.lua | 2 + src/ObjCommon/Sound/FlacDecoder.cpp | 77 +++++++++++++++++++ src/ObjCommon/Sound/FlacDecoder.h | 21 +++++ .../SoundBank/SoundBankWriter.cpp | 51 +++++++----- thirdparty/flac | 1 + thirdparty/flac.lua | 47 +++++++++++ 8 files changed, 184 insertions(+), 20 deletions(-) create mode 100644 src/ObjCommon/Sound/FlacDecoder.cpp create mode 100644 src/ObjCommon/Sound/FlacDecoder.h create mode 160000 thirdparty/flac create mode 100644 thirdparty/flac.lua diff --git a/.gitmodules b/.gitmodules index 28b1e92ea..241c59ce6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "thirdparty/json"] path = thirdparty/json url = https://github.com/nlohmann/json.git +[submodule "thirdparty/flac"] + path = thirdparty/flac + url = https://github.com/astoeckel/libfoxenflac.git diff --git a/premake5.lua b/premake5.lua index 6d9b19f78..2eb1d6d7d 100644 --- a/premake5.lua +++ b/premake5.lua @@ -83,6 +83,7 @@ workspace "OpenAssetTools" -- ThirdParty -- ======================== include "thirdparty/catch2.lua" +include "thirdparty/flac.lua" include "thirdparty/libtomcrypt.lua" include "thirdparty/libtommath.lua" include "thirdparty/json.lua" @@ -94,6 +95,7 @@ include "thirdparty/zlib.lua" -- ThirdParty group: All projects that are external dependencies group "ThirdParty" catch2:project() + flac:project() libtommath:project() libtomcrypt:project() json:project() diff --git a/src/ObjCommon.lua b/src/ObjCommon.lua index 2d591bbef..19ae27e86 100644 --- a/src/ObjCommon.lua +++ b/src/ObjCommon.lua @@ -15,6 +15,7 @@ function ObjCommon:link(links) links:linkto(Utils) links:linkto(Common) links:linkto(minizip) + links:linkto(flac) end function ObjCommon:use() @@ -48,4 +49,5 @@ function ObjCommon:project() self:include(includes) Utils:include(includes) + flac:include(includes) end diff --git a/src/ObjCommon/Sound/FlacDecoder.cpp b/src/ObjCommon/Sound/FlacDecoder.cpp new file mode 100644 index 000000000..ccaad5b01 --- /dev/null +++ b/src/ObjCommon/Sound/FlacDecoder.cpp @@ -0,0 +1,77 @@ +#include +#include "FlacDecoder.h" + +class fx_flac_raii +{ +public: + fx_flac_raii() + { + ptr = FX_FLAC_ALLOC_DEFAULT(); + } + + ~fx_flac_raii() + { + free(ptr); + } + + operator fx_flac_t* () + { + return ptr; + } + +private: + fx_flac_t* ptr; +}; + +class FlacDecoderImpl : public FlacDecoder +{ +private: + void* m_data; + size_t m_length; + std::unique_ptr m_flac; + +public: + explicit FlacDecoderImpl(void* data, size_t length) + : m_data(data), + m_length(length) + { + + } + + unsigned int GetFrameCount() override + { + return static_cast(fx_flac_get_streaminfo(*m_flac.get(), FLAC_KEY_N_SAMPLES)); + } + + unsigned int GetFrameRate() override + { + return static_cast(fx_flac_get_streaminfo(*m_flac.get(), FLAC_KEY_SAMPLE_RATE)); + } + + unsigned int GetNumChannels() override + { + return static_cast(fx_flac_get_streaminfo(*m_flac.get(), FLAC_KEY_N_CHANNELS)); + } + + bool Decode() override + { + m_flac = std::make_unique(); + + while (true) + { + auto res = fx_flac_process(*m_flac.get(), reinterpret_cast(m_data), &m_length, nullptr, nullptr); + if (res == FLAC_ERR) + return false; + + if (res == FLAC_END_OF_METADATA) + break; + } + + return true; + } +}; + +std::unique_ptr FlacDecoder::Create(void* data, size_t length) +{ + return std::make_unique(data, length); +} diff --git a/src/ObjCommon/Sound/FlacDecoder.h b/src/ObjCommon/Sound/FlacDecoder.h new file mode 100644 index 000000000..1d6a46d02 --- /dev/null +++ b/src/ObjCommon/Sound/FlacDecoder.h @@ -0,0 +1,21 @@ +#pragma once +#include + +class FlacDecoder +{ +public: + FlacDecoder() = default; + virtual ~FlacDecoder() = default; + + FlacDecoder(const FlacDecoder& other) = default; + FlacDecoder(FlacDecoder&& other) noexcept = default; + FlacDecoder& operator=(const FlacDecoder& other) = default; + FlacDecoder& operator=(FlacDecoder&& other) noexcept = default; + + virtual bool Decode() = 0; + virtual unsigned int GetFrameCount() = 0; + virtual unsigned int GetFrameRate() = 0; + virtual unsigned int GetNumChannels() = 0; + + static std::unique_ptr Create(void* data, size_t length); +}; \ No newline at end of file diff --git a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp index d44035da1..1ea926617 100644 --- a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp +++ b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp @@ -3,9 +3,11 @@ #include "Crypto.h" #include "ObjContainer/SoundBank/SoundBankTypes.h" #include "Sound/WavTypes.h" +#include "Sound/FlacDecoder.h" #include "Utils/Alignment.h" #include "Utils/FileUtils.h" + #include #include #include @@ -128,13 +130,6 @@ class SoundBankWriterImpl : public SoundBankWriter soundSize = static_cast(wavFile.m_length - sizeof(WavHeader)); auto frameCount = soundSize / (header.formatChunk.nChannels * (header.formatChunk.wBitsPerSample / 8)); - - if (!sound.streamed && header.formatChunk.nSamplesPerSec != 48000) - { - std::cout << "WARNING: \"" << soundFilePath << "\" has a framerate of " << header.formatChunk.nSamplesPerSec - << ". Loaded sounds are recommended to have a framerate of 48000. This sound may not work on all games!" << std::endl; - } - auto frameRateIndex = INDEX_FOR_FRAMERATE[header.formatChunk.nSamplesPerSec]; SoundAssetBankEntry entry{ @@ -161,21 +156,31 @@ class SoundBankWriterImpl : public SoundBankWriter { soundSize = static_cast(flacFile.m_length); - SoundAssetBankEntry entry{ - soundId, - soundSize, - static_cast(m_current_offset), - 0, - 0, - 0, - 0, - 8, - }; - - m_entries.push_back(entry); - soundData = std::make_unique(soundSize); flacFile.m_stream->read(soundData.get(), soundSize); + + auto decoder = FlacDecoder::Create(soundData.get(), soundSize); + if (decoder->Decode()) + { + auto frameRateIndex = INDEX_FOR_FRAMERATE[decoder->GetFrameRate()]; + SoundAssetBankEntry entry{ + soundId, + soundSize, + static_cast(m_current_offset), + decoder->GetFrameCount(), + frameRateIndex, + decoder->GetNumChannels(), + sound.looping, + 8, + }; + + m_entries.push_back(entry); + } + else + { + std::cerr << "Unable to decode .flac file for sound " << soundFilePath << std::endl; + return false; + } } else { @@ -184,6 +189,12 @@ class SoundBankWriterImpl : public SoundBankWriter } } + auto lastEntry = m_entries.rbegin(); + if (!sound.streamed && lastEntry->frameRateIndex != 6) + { + std::cout << "WARNING: Loaded sound \"" << soundFilePath << "\" should have a framerate of 48000 but doesn't. This sound may not work on all games!" << std::endl; + } + // calculate checksum SoundAssetBankChecksum checksum{}; diff --git a/thirdparty/flac b/thirdparty/flac new file mode 160000 index 000000000..1b04fafb5 --- /dev/null +++ b/thirdparty/flac @@ -0,0 +1 @@ +Subproject commit 1b04fafb51aac0c3b7f0118a7bf7c93f6a60d824 diff --git a/thirdparty/flac.lua b/thirdparty/flac.lua new file mode 100644 index 000000000..b840aa9dd --- /dev/null +++ b/thirdparty/flac.lua @@ -0,0 +1,47 @@ +flac = {} + +function flac:include(includes) + if includes:handle(self:name()) then + includedirs { + path.join(ThirdPartyFolder(), "flac") + } + end +end + +function flac:link(links) + links:add(self:name()) +end + +function flac:use() + +end + +function flac:name() + return "flac" +end + +function flac:project() + local folder = ThirdPartyFolder() + local includes = Includes:create() + + project(self:name()) + targetdir(TargetDirectoryLib) + location "%{wks.location}/thirdparty/%{prj.name}" + kind "StaticLib" + language "C" + + files { + path.join(folder, "flac/foxen/*.h"), + path.join(folder, "flac/foxen/*.c") + } + + defines { + "_CRT_SECURE_NO_WARNINGS", + "_CRT_NONSTDC_NO_DEPRECATE" + } + + self:include(includes) + + -- Disable warnings. They do not have any value to us since it is not our code. + warnings "off" +end From 2478a1355f34c7f8bcc4562ea1ff8052bc743392 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 9 Feb 2024 11:39:51 -0500 Subject: [PATCH 17/25] code review changes --- src/Crypto/Impl/AlgorithmMD5.h | 2 +- src/ObjCommon/Csv/ParsedCsv.cpp | 16 ++-- src/ObjCommon/Csv/ParsedCsv.h | 8 +- src/ObjCommon/Game/T6/ObjConstantsT6.h | 9 --- ...{ObjConstantsT6.cpp => SoundConstantsT6.h} | 20 ++--- .../T6/AssetLoaders/AssetLoaderSoundBank.cpp | 74 +++++++++---------- .../SoundBank/SoundBankWriter.cpp | 49 ++++++------ .../ObjContainer/SoundBank/SoundBankWriter.h | 2 +- .../T6/AssetDumpers/AssetDumperSndBank.cpp | 36 ++++----- src/ObjWriting/Sound/WavWriter.h | 2 +- src/Utils/Utils/StringUtils.cpp | 4 +- 11 files changed, 104 insertions(+), 118 deletions(-) rename src/ObjCommon/Game/T6/{ObjConstantsT6.cpp => SoundConstantsT6.h} (80%) diff --git a/src/Crypto/Impl/AlgorithmMD5.h b/src/Crypto/Impl/AlgorithmMD5.h index 25024f082..4687572aa 100644 --- a/src/Crypto/Impl/AlgorithmMD5.h +++ b/src/Crypto/Impl/AlgorithmMD5.h @@ -7,7 +7,7 @@ class AlgorithmMD5 : public IHashFunction AlgorithmMD5Impl* m_impl; public: - static const int HASH_SIZE = 20; + static const int HASH_SIZE = 16; AlgorithmMD5(); ~AlgorithmMD5() override; diff --git a/src/ObjCommon/Csv/ParsedCsv.cpp b/src/ObjCommon/Csv/ParsedCsv.cpp index d566eac86..9ddb3e5a0 100644 --- a/src/ObjCommon/Csv/ParsedCsv.cpp +++ b/src/ObjCommon/Csv/ParsedCsv.cpp @@ -1,19 +1,19 @@ #include "Csv/ParsedCsv.h" -ParsedCsvRow::ParsedCsvRow(std::unordered_map& headers, std::vector& row) +ParsedCsvRow::ParsedCsvRow(std::unordered_map& headers, std::vector row) : headers(headers), - values(row) + values(std::move(row)) { } -const std::string ParsedCsvRow::GetValue(const std::string& header, bool required) const +std::string ParsedCsvRow::GetValue(const std::string& header, bool required) const { if (this->headers.find(header) == this->headers.end()) { if (required) - std::cerr << "ERROR: Required column \"" << header << "\" was not found"; + std::cerr << "ERROR: Required column \"" << header << "\" was not found" << std::endl; else - std::cerr << "WARNING: Expected column \"" << header << "\" was not found"; + std::cerr << "WARNING: Expected column \"" << header << "\" was not found" << std::endl; return {}; } @@ -21,14 +21,14 @@ const std::string ParsedCsvRow::GetValue(const std::string& header, bool require auto& value = this->values.at(this->headers[header]); if (required && value.empty()) { - std::cerr << "ERROR: Required column \"" << header << "\" does not have a value"; + std::cerr << "ERROR: Required column \"" << header << "\" does not have a value" << std::endl; return {}; } return value; } -const float ParsedCsvRow::GetValueFloat(const std::string& header, bool required) const +float ParsedCsvRow::GetValueFloat(const std::string& header, bool required) const { const auto& value = this->GetValue(header, required); if (!value.empty()) @@ -65,7 +65,7 @@ ParsedCsv::ParsedCsv(const CsvInputStream& inputStream, bool hasHeaders) for (auto i = hasHeaders ? 1u : 0u; i < csvLines.size(); i++) { auto& rowValues = csvLines[i]; - this->rows.push_back(ParsedCsvRow(this->headers, rowValues)); + this->rows.emplace_back(this->headers, std::move(rowValues)); } } diff --git a/src/ObjCommon/Csv/ParsedCsv.h b/src/ObjCommon/Csv/ParsedCsv.h index 22d4e5be8..ff2030d3a 100644 --- a/src/ObjCommon/Csv/ParsedCsv.h +++ b/src/ObjCommon/Csv/ParsedCsv.h @@ -1,7 +1,7 @@ #pragma once -#include #include #include +#include "Csv/CsvStream.h" class ParsedCsvRow { @@ -9,9 +9,9 @@ class ParsedCsvRow std::vector values; public: - explicit ParsedCsvRow(std::unordered_map& headers, std::vector& row); - const std::string GetValue(const std::string& header, bool required = false) const; - const float GetValueFloat(const std::string& header, bool required = false) const; + explicit ParsedCsvRow(std::unordered_map& headers, std::vector row); + std::string GetValue(const std::string& header, bool required = false) const; + float GetValueFloat(const std::string& header, bool required = false) const; template T GetValueInt(const std::string& header, bool required = false) const { diff --git a/src/ObjCommon/Game/T6/ObjConstantsT6.h b/src/ObjCommon/Game/T6/ObjConstantsT6.h index d5991d9ab..392102fa5 100644 --- a/src/ObjCommon/Game/T6/ObjConstantsT6.h +++ b/src/ObjCommon/Game/T6/ObjConstantsT6.h @@ -26,14 +26,5 @@ namespace T6 static constexpr const char* GDF_FILENAME_WEAPON_ATTACHMENT = "attachment.gdf"; static constexpr const char* GDF_FILENAME_WEAPON_ATTACHMENT_UNIQUE = "attachmentunique.gdf"; static constexpr const char* GDF_FILENAME_ZBARRIER = "zbarrier.gdf"; - - static const std::array SOUND_GROUPS; - static const std::array SOUND_CURVES; - static const std::array SOUND_DUCK_GROUPS; - static const std::array SOUND_LIMIT_TYPES; - static const std::array SOUND_MOVE_TYPES; - static const std::array SOUND_LOAD_TYPES; - static const std::array SOUND_BUS_IDS; - static const std::array SOUND_RANDOMIZE_TYPES; }; } // namespace T6 diff --git a/src/ObjCommon/Game/T6/ObjConstantsT6.cpp b/src/ObjCommon/Game/T6/SoundConstantsT6.h similarity index 80% rename from src/ObjCommon/Game/T6/ObjConstantsT6.cpp rename to src/ObjCommon/Game/T6/SoundConstantsT6.h index 3ac97809d..feb7cff6a 100644 --- a/src/ObjCommon/Game/T6/ObjConstantsT6.cpp +++ b/src/ObjCommon/Game/T6/SoundConstantsT6.h @@ -1,8 +1,9 @@ -#include "ObjConstantsT6.h" +#pragma once +#include namespace T6 { - const std::array ObjConstants::SOUND_GROUPS{ + inline const std::string SOUND_GROUPS[]{ "grp_reference", "grp_master", "grp_wpn_lfe", @@ -32,7 +33,7 @@ namespace T6 "", }; - const std::array ObjConstants::SOUND_CURVES{ + inline const std::string SOUND_CURVES[]{ "default", "defaultmin", "allon", @@ -53,7 +54,7 @@ namespace T6 "", }; - const std::array ObjConstants::SOUND_DUCK_GROUPS{ + inline const std::string SOUND_DUCK_GROUPS[]{ "snp_alerts_gameplay", "snp_ambience", "snp_claw", @@ -88,14 +89,14 @@ namespace T6 "snp_x3", }; - const std::array ObjConstants::SOUND_LIMIT_TYPES{ + inline const std::string SOUND_LIMIT_TYPES[]{ "none", "oldest", "reject", "priority", }; - const std::array ObjConstants::SOUND_MOVE_TYPES{ + inline const std::string SOUND_MOVE_TYPES[]{ "none", "left_player", "center_player", @@ -106,14 +107,14 @@ namespace T6 "right_shot", }; - const std::array ObjConstants::SOUND_LOAD_TYPES{ + inline const std::string SOUND_LOAD_TYPES[]{ "unknown", "loaded", "streamed", "primed", }; - const std::array ObjConstants::SOUND_BUS_IDS{ + inline const std::string SOUND_BUS_IDS[]{ "bus_reverb", "bus_fx", "bus_voice", @@ -127,11 +128,10 @@ namespace T6 "", }; - const std::array ObjConstants::SOUND_RANDOMIZE_TYPES{ + inline const std::string SOUND_RANDOMIZE_TYPES[]{ "volume", "pitch", "variant", "", }; - } // namespace T6 diff --git a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp index 5a19f6e1f..4b6d0775a 100644 --- a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp +++ b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp @@ -2,7 +2,7 @@ #include "Csv/ParsedCsv.h" #include "Game/T6/CommonT6.h" -#include "Game/T6/ObjConstantsT6.h" +#include "Game/T6/SoundConstantsT6.h" #include "Game/T6/T6.h" #include "ObjContainer/SoundBank/SoundBankWriter.h" #include "Pool/GlobalAssetPool.h" @@ -180,24 +180,18 @@ bool LoadSoundAlias(MemoryManager* memory, SndAlias* alias, const ParsedCsvRow& alias->flags.pauseable = row.GetValue("pause") == "yes"; alias->flags.stopOnDeath = row.GetValue("stop_on_death") == "yes"; - alias->duckGroup = - static_cast(GetValueIndex(row.GetValue("duck_group"), ObjConstants::SOUND_DUCK_GROUPS.data(), ObjConstants::SOUND_DUCK_GROUPS.size())); - alias->flags.volumeGroup = GetValueIndex(row.GetValue("group"), ObjConstants::SOUND_GROUPS.data(), ObjConstants::SOUND_GROUPS.size()); - alias->flags.fluxType = GetValueIndex(row.GetValue("move_type"), ObjConstants::SOUND_MOVE_TYPES.data(), ObjConstants::SOUND_MOVE_TYPES.size()); - alias->flags.loadType = GetValueIndex(row.GetValue("type"), ObjConstants::SOUND_LOAD_TYPES.data(), ObjConstants::SOUND_LOAD_TYPES.size()); - alias->flags.busType = GetValueIndex(row.GetValue("bus"), ObjConstants::SOUND_BUS_IDS.data(), ObjConstants::SOUND_BUS_IDS.size()); - alias->flags.limitType = GetValueIndex(row.GetValue("limit_type"), ObjConstants::SOUND_LIMIT_TYPES.data(), ObjConstants::SOUND_LIMIT_TYPES.size()); - alias->flags.volumeFalloffCurve = GetValueIndex(row.GetValue("volume_falloff_curve"), ObjConstants::SOUND_CURVES.data(), ObjConstants::SOUND_CURVES.size()); - alias->flags.reverbFalloffCurve = GetValueIndex(row.GetValue("reverb_falloff_curve"), ObjConstants::SOUND_CURVES.data(), ObjConstants::SOUND_CURVES.size()); - - alias->flags.entityLimitType = - GetValueIndex(row.GetValue("entity_limit_type"), ObjConstants::SOUND_LIMIT_TYPES.data(), ObjConstants::SOUND_LIMIT_TYPES.size()); - alias->flags.volumeMinFalloffCurve = - GetValueIndex(row.GetValue("volume_min_falloff_curve"), ObjConstants::SOUND_CURVES.data(), ObjConstants::SOUND_CURVES.size()); - alias->flags.reverbMinFalloffCurve = - GetValueIndex(row.GetValue("reverb_min_falloff_curve"), ObjConstants::SOUND_CURVES.data(), ObjConstants::SOUND_CURVES.size()); - alias->flags.randomizeType = - GetValueIndex(row.GetValue("randomize_type"), ObjConstants::SOUND_RANDOMIZE_TYPES.data(), ObjConstants::SOUND_RANDOMIZE_TYPES.size()); + alias->duckGroup = static_cast(GetValueIndex(row.GetValue("duck_group"), SOUND_DUCK_GROUPS, std::extent_v)); + alias->flags.volumeGroup = GetValueIndex(row.GetValue("group"), SOUND_GROUPS, std::extent_v); + alias->flags.fluxType = GetValueIndex(row.GetValue("move_type"), SOUND_MOVE_TYPES, std::extent_v); + alias->flags.loadType = GetValueIndex(row.GetValue("type"), SOUND_LOAD_TYPES, std::extent_v); + alias->flags.busType = GetValueIndex(row.GetValue("bus"), SOUND_BUS_IDS, std::extent_v); + alias->flags.limitType = GetValueIndex(row.GetValue("limit_type"), SOUND_LIMIT_TYPES, std::extent_v); + alias->flags.volumeFalloffCurve = GetValueIndex(row.GetValue("volume_falloff_curve"), SOUND_CURVES, std::extent_v); + alias->flags.reverbFalloffCurve = GetValueIndex(row.GetValue("reverb_falloff_curve"), SOUND_CURVES, std::extent_v); + alias->flags.entityLimitType = GetValueIndex(row.GetValue("entity_limit_type"), SOUND_LIMIT_TYPES, std::extent_v); + alias->flags.volumeMinFalloffCurve = GetValueIndex(row.GetValue("volume_min_falloff_curve"), SOUND_CURVES, std::extent_v); + alias->flags.reverbMinFalloffCurve = GetValueIndex(row.GetValue("reverb_min_falloff_curve"), SOUND_CURVES, std::extent_v); + alias->flags.randomizeType = GetValueIndex(row.GetValue("randomize_type"), SOUND_RANDOMIZE_TYPES, std::extent_v); return true; } @@ -208,16 +202,15 @@ bool LoadSoundAliasIndexList(MemoryManager* memory, SndBank* sndBank) sndBank->aliasIndex = static_cast(memory->Alloc(sizeof(SndIndexEntry) * sndBank->aliasCount)); memset(sndBank->aliasIndex, 0xFF, sizeof(SndIndexEntry) * sndBank->aliasCount); - bool* setAliasIndexList = new bool[sndBank->aliasCount]; - memset(setAliasIndexList, false, sndBank->aliasCount); + const auto setAliasIndexList = std::make_unique(sndBank->aliasCount); for (auto i = 0u; i < sndBank->aliasCount; i++) { auto idx = sndBank->alias[i].id % sndBank->aliasCount; - if (sndBank->aliasIndex[idx].value == USHRT_MAX) + if (sndBank->aliasIndex[idx].value == std::numeric_limits::max()) { sndBank->aliasIndex[idx].value = i; - sndBank->aliasIndex[idx].next = USHRT_MAX; + sndBank->aliasIndex[idx].next = std::numeric_limits::max(); setAliasIndexList[i] = true; } } @@ -228,44 +221,42 @@ bool LoadSoundAliasIndexList(MemoryManager* memory, SndBank* sndBank) continue; auto idx = sndBank->alias[i].id % sndBank->aliasCount; - while (sndBank->aliasIndex[idx].next != USHRT_MAX) + while (sndBank->aliasIndex[idx].next != std::numeric_limits::max()) { idx = sndBank->aliasIndex[idx].next; } auto offset = 1u; - auto freeIdx = USHRT_MAX; + auto freeIdx = std::numeric_limits::max(); while (true) { freeIdx = (idx + offset) % sndBank->aliasCount; - if (sndBank->aliasIndex[freeIdx].value == USHRT_MAX) + if (sndBank->aliasIndex[freeIdx].value == std::numeric_limits::max()) break; freeIdx = (idx + sndBank->aliasCount - offset) % sndBank->aliasCount; - if (sndBank->aliasIndex[freeIdx].value == USHRT_MAX) + if (sndBank->aliasIndex[freeIdx].value == std::numeric_limits::max()) break; offset++; - freeIdx = USHRT_MAX; + freeIdx = std::numeric_limits::max(); if (offset >= sndBank->aliasCount) break; } - if (freeIdx == USHRT_MAX) + if (freeIdx == std::numeric_limits::max()) { std::cerr << "Unable to allocate sound bank alias index list" << std::endl; - delete[] setAliasIndexList; return false; } sndBank->aliasIndex[idx].next = freeIdx; sndBank->aliasIndex[freeIdx].value = i; - sndBank->aliasIndex[freeIdx].next = USHRT_MAX; + sndBank->aliasIndex[freeIdx].next = std::numeric_limits::max(); setAliasIndexList[i] = true; } - delete[] setAliasIndexList; return true; } @@ -436,8 +427,7 @@ bool LoadSoundDuckList(ISearchPath* searchPath, MemoryManager* memory, SndBank* for (auto& valueJson : duckJson["values"]) { - auto index = - GetValueIndex(valueJson["duckGroup"].get(), ObjConstants::SOUND_DUCK_GROUPS.data(), ObjConstants::SOUND_DUCK_GROUPS.size()); + auto index = GetValueIndex(valueJson["duckGroup"].get(), SOUND_DUCK_GROUPS, std::extent_v); duck->attenuation[index] = valueJson["attenuation"].get(); duck->filter[index] = valueJson["filter"].get(); @@ -550,14 +540,15 @@ bool AssetLoaderSoundBank::LoadFromRaw( // write the output linked sound bank if (sablWriter) { - auto size = static_cast(sablWriter->Write()); + size_t dataSize = 0u; + auto result = sablWriter->Write(dataSize); sablStream->close(); - if (size != UINT32_MAX) + if (result) { - sndBank->loadedAssets.dataSize = size; - sndBank->loadedAssets.data = static_cast(memory->Alloc(size)); - memset(sndBank->loadedAssets.data, 0, size); + sndBank->loadedAssets.dataSize = dataSize; + sndBank->loadedAssets.data = static_cast(memory->Alloc(dataSize)); + memset(sndBank->loadedAssets.data, 0, dataSize); } else { @@ -569,10 +560,11 @@ bool AssetLoaderSoundBank::LoadFromRaw( // write the output streamed sound bank if (sabsWriter) { - auto size = static_cast(sabsWriter->Write()); + size_t dataSize = 0u; + auto result = sabsWriter->Write(dataSize); sabsStream->close(); - if (size == UINT32_MAX) + if (!result) { std::cerr << "Streamed Sound Bank for " << assetName << " failed to generate. Please check your build files." << std::endl; return false; diff --git a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp index 1ea926617..bab982d41 100644 --- a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp +++ b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp @@ -2,12 +2,11 @@ #include "Crypto.h" #include "ObjContainer/SoundBank/SoundBankTypes.h" -#include "Sound/WavTypes.h" #include "Sound/FlacDecoder.h" +#include "Sound/WavTypes.h" #include "Utils/Alignment.h" #include "Utils/FileUtils.h" - #include #include #include @@ -78,8 +77,8 @@ class SoundBankWriterImpl : public SoundBankWriter void AlignToChunk() { - if ((m_current_offset & 0xF) != 0) - Pad(0x10 - (m_current_offset & 0xF)); + if (m_current_offset % 16 != 0) + Pad(16 - (m_current_offset % 16)); } void WriteHeader() @@ -91,18 +90,20 @@ class SoundBankWriterImpl : public SoundBankWriter SoundAssetBankChecksum checksum{}; memset(&checksum, 0xCC, sizeof(SoundAssetBankChecksum)); - SoundAssetBankHeader header{MAGIC, - VERSION, - sizeof(SoundAssetBankEntry), - sizeof(SoundAssetBankChecksum), - 0x40, - m_entries.size(), - 0, - 0, - m_total_size, - m_entry_section_offset, - m_checksum_section_offset, - checksum}; + SoundAssetBankHeader header{ + MAGIC, + VERSION, + sizeof(SoundAssetBankEntry), + sizeof(SoundAssetBankChecksum), + 0x40, + m_entries.size(), + 0, + 0, + m_total_size, + m_entry_section_offset, + m_checksum_section_offset, + checksum, + }; strncpy(header.dependencies, m_file_name.data(), header.dependencySize); @@ -169,7 +170,7 @@ class SoundBankWriterImpl : public SoundBankWriter static_cast(m_current_offset), decoder->GetFrameCount(), frameRateIndex, - decoder->GetNumChannels(), + static_cast(decoder->GetNumChannels()), sound.looping, 8, }; @@ -192,7 +193,8 @@ class SoundBankWriterImpl : public SoundBankWriter auto lastEntry = m_entries.rbegin(); if (!sound.streamed && lastEntry->frameRateIndex != 6) { - std::cout << "WARNING: Loaded sound \"" << soundFilePath << "\" should have a framerate of 48000 but doesn't. This sound may not work on all games!" << std::endl; + std::cout << "WARNING: Loaded sound \"" << soundFilePath + << "\" should have a framerate of 48000 but doesn't. This sound may not work on all games!" << std::endl; } // calculate checksum @@ -240,12 +242,12 @@ class SoundBankWriterImpl : public SoundBankWriter AlignToChunk(); } - std::int64_t Write() override + bool Write(size_t& dataSize) override { if (!WriteEntries()) { std::cerr << "An error occurred writing the sound bank entires. Please check output." << std::endl; - return -1; + return false; } WriteEntryList(); @@ -259,11 +261,12 @@ class SoundBankWriterImpl : public SoundBankWriter if (m_current_offset > UINT32_MAX) { std::cerr << "Sound bank files must be under 4GB. Please reduce the number of sounds being written!" << std::endl; - return -1; + return false; } - // return the total size for the sound asset data - return m_entry_section_offset - DATA_OFFSET; + // output the total size for the sound asset data + dataSize = static_cast(m_entry_section_offset - DATA_OFFSET); + return true; } private: diff --git a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.h b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.h index cb0fb07a5..ccd338b3b 100644 --- a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.h +++ b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.h @@ -17,7 +17,7 @@ class SoundBankWriter SoundBankWriter& operator=(SoundBankWriter&& other) noexcept = default; virtual void AddSound(const std::string& soundFilePath, unsigned int soundId, bool looping = false, bool streamed = false) = 0; - virtual std::int64_t Write() = 0; + virtual bool Write(size_t& dataSize) = 0; static std::unique_ptr Create(const std::string& fileName, std::ostream& stream, ISearchPath* assetSearchPath); diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp index 33bf94c17..e25b5a99a 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp @@ -2,7 +2,7 @@ #include "Csv/CsvStream.h" #include "Game/T6/CommonT6.h" -#include "Game/T6/ObjConstantsT6.h" +#include "Game/T6/SoundConstantsT6.h" #include "ObjContainer/SoundBank/SoundBank.h" #include "Sound/WavWriter.h" #include "nlohmann/json.hpp" @@ -123,8 +123,8 @@ namespace std::unordered_map CreateCurvesMap() { std::unordered_map result; - for (auto i = 0u; i < ObjConstants::SOUND_CURVES.size(); i++) - result.emplace(T6::Common::SND_HashName(ObjConstants::SOUND_CURVES[i].data()), ObjConstants::SOUND_CURVES[i]); + for (auto i = 0u; i < std::extent_v; i++) + result.emplace(T6::Common::SND_HashName(SOUND_CURVES[i].data()), SOUND_CURVES[i]); return result; } @@ -226,7 +226,7 @@ class AssetDumperSndBank::Internal stream.WriteColumn((alias->secondaryname && *alias->secondaryname) ? alias->secondaryname : ""); // group - stream.WriteColumn(ObjConstants::SOUND_GROUPS[alias->flags.volumeGroup]); + stream.WriteColumn(SOUND_GROUPS[alias->flags.volumeGroup]); // vol_min stream.WriteColumn(std::to_string(alias->volMin)); @@ -247,28 +247,28 @@ class AssetDumperSndBank::Internal stream.WriteColumn(std::to_string(alias->distReverbMax)); // volume_falloff_curve - stream.WriteColumn(ObjConstants::SOUND_CURVES[alias->flags.volumeFalloffCurve]); + stream.WriteColumn(SOUND_CURVES[alias->flags.volumeFalloffCurve]); // reverb_falloff_curve - stream.WriteColumn(ObjConstants::SOUND_CURVES[alias->flags.reverbFalloffCurve]); + stream.WriteColumn(SOUND_CURVES[alias->flags.reverbFalloffCurve]); // volume_min_falloff_curve - stream.WriteColumn(ObjConstants::SOUND_CURVES[alias->flags.volumeMinFalloffCurve]); + stream.WriteColumn(SOUND_CURVES[alias->flags.volumeMinFalloffCurve]); // reverb_min_falloff_curve" - stream.WriteColumn(ObjConstants::SOUND_CURVES[alias->flags.reverbMinFalloffCurve]); + stream.WriteColumn(SOUND_CURVES[alias->flags.reverbMinFalloffCurve]); // limit_count stream.WriteColumn(std::to_string(alias->limitCount)); // limit_type - stream.WriteColumn(ObjConstants::SOUND_LIMIT_TYPES[alias->flags.limitType]); + stream.WriteColumn(SOUND_LIMIT_TYPES[alias->flags.limitType]); // entity_limit_count stream.WriteColumn(std::to_string(alias->entityLimitCount)); // entity_limit_type - stream.WriteColumn(ObjConstants::SOUND_LIMIT_TYPES[alias->flags.entityLimitType]); + stream.WriteColumn(SOUND_LIMIT_TYPES[alias->flags.entityLimitType]); // pitch_min stream.WriteColumn(std::to_string(alias->pitchMin)); @@ -295,13 +295,13 @@ class AssetDumperSndBank::Internal stream.WriteColumn(""); // type - stream.WriteColumn(ObjConstants::SOUND_LOAD_TYPES[alias->flags.loadType]); + stream.WriteColumn(SOUND_LOAD_TYPES[alias->flags.loadType]); // loop stream.WriteColumn(alias->flags.looping == T6::SA_NON_LOOPING ? "nonlooping" : "looping"); // randomize_type - stream.WriteColumn(ObjConstants::SOUND_RANDOMIZE_TYPES[std::min(alias->flags.randomizeType, 3u)]); + stream.WriteColumn(SOUND_RANDOMIZE_TYPES[std::min(alias->flags.randomizeType, 3u)]); // probability", stream.WriteColumn(std::to_string(alias->probability)); @@ -316,7 +316,7 @@ class AssetDumperSndBank::Internal stream.WriteColumn(FindNameForDuck(alias->duck, bank)); // duck_group", - stream.WriteColumn(ObjConstants::SOUND_DUCK_GROUPS[alias->duckGroup]); + stream.WriteColumn(SOUND_DUCK_GROUPS[alias->duckGroup]); // pan", stream.WriteColumn(alias->flags.panType == SA_PAN_2D ? "2d" : "3d"); @@ -346,7 +346,7 @@ class AssetDumperSndBank::Internal stream.WriteColumn(alias->flags.distanceLpf ? "yes" : "no"); // move_type", - stream.WriteColumn(ObjConstants::SOUND_MOVE_TYPES[alias->flags.fluxType]); + stream.WriteColumn(SOUND_MOVE_TYPES[alias->flags.fluxType]); // move_time", stream.WriteColumn(std::to_string(alias->fluxTime)); @@ -397,7 +397,7 @@ class AssetDumperSndBank::Internal stream.WriteColumn(alias->flags.stopOnDeath ? "yes" : "no"); // bus", - stream.WriteColumn(ObjConstants::SOUND_BUS_IDS[alias->flags.busType]); + stream.WriteColumn(SOUND_BUS_IDS[alias->flags.busType]); // snapshot", stream.WriteColumn(""); @@ -630,9 +630,9 @@ class AssetDumperSndBank::Internal for (auto i = 0u; i < 32u; i++) { values.push_back({ - {"duckGroup", ObjConstants::SOUND_DUCK_GROUPS[i]}, - {"attenuation", duck.attenuation[i] }, - {"filter", duck.filter[i] } + {"duckGroup", SOUND_DUCK_GROUPS[i]}, + {"attenuation", duck.attenuation[i] }, + {"filter", duck.filter[i] } }); } diff --git a/src/ObjWriting/Sound/WavWriter.h b/src/ObjWriting/Sound/WavWriter.h index eb0174a9a..4ce2bbc53 100644 --- a/src/ObjWriting/Sound/WavWriter.h +++ b/src/ObjWriting/Sound/WavWriter.h @@ -1,6 +1,6 @@ #pragma once -#include #include +#include "Sound/WavTypes.h" class WavWriter { diff --git a/src/Utils/Utils/StringUtils.cpp b/src/Utils/Utils/StringUtils.cpp index cb5ede9c1..d582fa1f6 100644 --- a/src/Utils/Utils/StringUtils.cpp +++ b/src/Utils/Utils/StringUtils.cpp @@ -104,13 +104,13 @@ namespace utils std::vector StringSplit(const std::string& str, const char delim) { - std::vector strings{}; + std::vector strings; std::istringstream stream(str); std::string s; while (std::getline(stream, s, delim)) { - strings.push_back(s); + strings.emplace_back(std::move(s)); } return strings; From bfed3d5e683d75858ad313ac29c3a7fbb9b3c241 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 9 Feb 2024 11:43:02 -0500 Subject: [PATCH 18/25] clang format --- src/ObjCommon/Csv/ParsedCsv.h | 3 ++- src/ObjCommon/Sound/FlacDecoder.cpp | 10 +++++----- src/ObjCommon/Sound/FlacDecoder.h | 2 +- src/ObjWriting/Sound/WavWriter.h | 3 ++- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/ObjCommon/Csv/ParsedCsv.h b/src/ObjCommon/Csv/ParsedCsv.h index ff2030d3a..358a4ee8f 100644 --- a/src/ObjCommon/Csv/ParsedCsv.h +++ b/src/ObjCommon/Csv/ParsedCsv.h @@ -1,7 +1,8 @@ #pragma once +#include "Csv/CsvStream.h" + #include #include -#include "Csv/CsvStream.h" class ParsedCsvRow { diff --git a/src/ObjCommon/Sound/FlacDecoder.cpp b/src/ObjCommon/Sound/FlacDecoder.cpp index ccaad5b01..335a8b707 100644 --- a/src/ObjCommon/Sound/FlacDecoder.cpp +++ b/src/ObjCommon/Sound/FlacDecoder.cpp @@ -1,6 +1,7 @@ -#include #include "FlacDecoder.h" +#include + class fx_flac_raii { public: @@ -14,12 +15,12 @@ class fx_flac_raii free(ptr); } - operator fx_flac_t* () + operator fx_flac_t*() { return ptr; } -private: +private: fx_flac_t* ptr; }; @@ -35,7 +36,6 @@ class FlacDecoderImpl : public FlacDecoder : m_data(data), m_length(length) { - } unsigned int GetFrameCount() override @@ -48,7 +48,7 @@ class FlacDecoderImpl : public FlacDecoder return static_cast(fx_flac_get_streaminfo(*m_flac.get(), FLAC_KEY_SAMPLE_RATE)); } - unsigned int GetNumChannels() override + unsigned int GetNumChannels() override { return static_cast(fx_flac_get_streaminfo(*m_flac.get(), FLAC_KEY_N_CHANNELS)); } diff --git a/src/ObjCommon/Sound/FlacDecoder.h b/src/ObjCommon/Sound/FlacDecoder.h index 1d6a46d02..136fc68ff 100644 --- a/src/ObjCommon/Sound/FlacDecoder.h +++ b/src/ObjCommon/Sound/FlacDecoder.h @@ -18,4 +18,4 @@ class FlacDecoder virtual unsigned int GetNumChannels() = 0; static std::unique_ptr Create(void* data, size_t length); -}; \ No newline at end of file +}; diff --git a/src/ObjWriting/Sound/WavWriter.h b/src/ObjWriting/Sound/WavWriter.h index 4ce2bbc53..c3bda89f1 100644 --- a/src/ObjWriting/Sound/WavWriter.h +++ b/src/ObjWriting/Sound/WavWriter.h @@ -1,7 +1,8 @@ #pragma once -#include #include "Sound/WavTypes.h" +#include + class WavWriter { public: From 54278a593a428de1a646289ef936ecafcbfac334 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 9 Feb 2024 11:55:02 -0500 Subject: [PATCH 19/25] include memory for linux build --- src/ObjCommon/Sound/FlacDecoder.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ObjCommon/Sound/FlacDecoder.h b/src/ObjCommon/Sound/FlacDecoder.h index 136fc68ff..0b659b234 100644 --- a/src/ObjCommon/Sound/FlacDecoder.h +++ b/src/ObjCommon/Sound/FlacDecoder.h @@ -1,5 +1,6 @@ #pragma once #include +#include class FlacDecoder { From f7ece105eda8cac72f9457aa575c5655e6fecfce Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 9 Feb 2024 12:04:41 -0500 Subject: [PATCH 20/25] more linux build fails --- src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp index 4b6d0775a..f4590542c 100644 --- a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp +++ b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp @@ -347,7 +347,7 @@ bool LoadSoundRadverbs(MemoryManager* memory, SndBank* sndBank, const SearchPath { auto row = radverbCsv[i]; - auto& name = row.GetValue("name", true); + auto name = row.GetValue("name", true); if (name.empty()) return false; From 45689a29a145c269633f71b6fe189f2efec4b5b9 Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 10 Feb 2024 10:01:55 +0100 Subject: [PATCH 21/25] chore: make SoundBankEntryInfo a class without constructors --- .../SoundBank/SoundBankWriter.cpp | 39 +++++++++++++------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp index bab982d41..ca39e057a 100644 --- a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp +++ b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp @@ -38,7 +38,6 @@ class SoundBankWriterImpl : public SoundBankWriter : m_file_name(fileName), m_stream(stream), m_asset_search_path(assetSearchPath), - m_sounds(), m_current_offset(0), m_total_size(0), m_entry_section_offset(0), @@ -48,7 +47,7 @@ class SoundBankWriterImpl : public SoundBankWriter void AddSound(const std::string& soundFilePath, unsigned int soundId, bool looping, bool streamed) override { - this->m_sounds.push_back({soundFilePath, soundId, looping, streamed}); + this->m_sounds.emplace_back(soundFilePath, soundId, looping, streamed); } void GoTo(const int64_t offset) @@ -116,8 +115,8 @@ class SoundBankWriterImpl : public SoundBankWriter for (auto& sound : m_sounds) { - const auto& soundFilePath = sound.filePath; - const auto soundId = sound.soundId; + const auto& soundFilePath = sound.m_file_path; + const auto soundId = sound.m_sound_id; size_t soundSize = -1; std::unique_ptr soundData; @@ -140,7 +139,7 @@ class SoundBankWriterImpl : public SoundBankWriter frameCount, frameRateIndex, static_cast(header.formatChunk.nChannels), - sound.looping, + sound.m_looping, 0, }; @@ -171,7 +170,7 @@ class SoundBankWriterImpl : public SoundBankWriter decoder->GetFrameCount(), frameRateIndex, static_cast(decoder->GetNumChannels()), - sound.looping, + sound.m_looping, 8, }; @@ -191,7 +190,7 @@ class SoundBankWriterImpl : public SoundBankWriter } auto lastEntry = m_entries.rbegin(); - if (!sound.streamed && lastEntry->frameRateIndex != 6) + if (!sound.m_streamed && lastEntry->frameRateIndex != 6) { std::cout << "WARNING: Loaded sound \"" << soundFilePath << "\" should have a framerate of 48000 but doesn't. This sound may not work on all games!" << std::endl; @@ -270,12 +269,28 @@ class SoundBankWriterImpl : public SoundBankWriter } private: - struct SoundBankEntryInfo + class SoundBankEntryInfo { - std::string filePath; - unsigned int soundId; - bool looping; - bool streamed; + public: + SoundBankEntryInfo() + : m_sound_id(0u), + m_looping(false), + m_streamed(false) + { + } + + SoundBankEntryInfo(std::string filePath, const unsigned int soundId, const bool looping, const bool streamed) + : m_file_path(std::move(filePath)), + m_sound_id(soundId), + m_looping(looping), + m_streamed(streamed) + { + } + + std::string m_file_path; + unsigned int m_sound_id; + bool m_looping; + bool m_streamed; }; std::string m_file_name; From 05ce73049d92dbfb2a9453d6af0318bbf3d8265f Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 10 Feb 2024 10:52:02 +0100 Subject: [PATCH 22/25] chore: small code cleanups --- src/ObjCommon/Csv/ParsedCsv.cpp | 10 +++--- src/ObjCommon/Csv/ParsedCsv.h | 10 +++--- src/ObjCommon/Game/T6/ObjConstantsT6.h | 2 -- .../T6/AssetLoaders/AssetLoaderSoundBank.cpp | 31 +++++++++---------- .../SoundBank/SoundBankWriter.cpp | 20 ++++++------ 5 files changed, 36 insertions(+), 37 deletions(-) diff --git a/src/ObjCommon/Csv/ParsedCsv.cpp b/src/ObjCommon/Csv/ParsedCsv.cpp index 9ddb3e5a0..c54ce5a5e 100644 --- a/src/ObjCommon/Csv/ParsedCsv.cpp +++ b/src/ObjCommon/Csv/ParsedCsv.cpp @@ -6,7 +6,7 @@ ParsedCsvRow::ParsedCsvRow(std::unordered_map& headers, std { } -std::string ParsedCsvRow::GetValue(const std::string& header, bool required) const +std::string ParsedCsvRow::GetValue(const std::string& header, const bool required) const { if (this->headers.find(header) == this->headers.end()) { @@ -28,7 +28,7 @@ std::string ParsedCsvRow::GetValue(const std::string& header, bool required) con return value; } -float ParsedCsvRow::GetValueFloat(const std::string& header, bool required) const +float ParsedCsvRow::GetValueFloat(const std::string& header, const bool required) const { const auto& value = this->GetValue(header, required); if (!value.empty()) @@ -42,7 +42,7 @@ float ParsedCsvRow::GetValueFloat(const std::string& header, bool required) cons return {}; } -ParsedCsv::ParsedCsv(const CsvInputStream& inputStream, bool hasHeaders) +ParsedCsv::ParsedCsv(const CsvInputStream& inputStream, const bool hasHeaders) { std::vector> csvLines; std::vector currentLine; @@ -55,7 +55,7 @@ ParsedCsv::ParsedCsv(const CsvInputStream& inputStream, bool hasHeaders) if (hasHeaders) { - auto& headersRow = csvLines[0]; + const auto& headersRow = csvLines[0]; for (auto i = 0u; i < headersRow.size(); i++) { this->headers[headersRow[i]] = i; @@ -74,7 +74,7 @@ size_t ParsedCsv::Size() const return this->rows.size(); } -ParsedCsvRow ParsedCsv::operator[](size_t index) const +ParsedCsvRow ParsedCsv::operator[](const size_t index) const { return this->rows.at(index); } diff --git a/src/ObjCommon/Csv/ParsedCsv.h b/src/ObjCommon/Csv/ParsedCsv.h index 358a4ee8f..6913c80c4 100644 --- a/src/ObjCommon/Csv/ParsedCsv.h +++ b/src/ObjCommon/Csv/ParsedCsv.h @@ -1,5 +1,7 @@ #pragma once + #include "Csv/CsvStream.h" +#include "Utils/ClassUtils.h" #include #include @@ -11,10 +13,10 @@ class ParsedCsvRow public: explicit ParsedCsvRow(std::unordered_map& headers, std::vector row); - std::string GetValue(const std::string& header, bool required = false) const; - float GetValueFloat(const std::string& header, bool required = false) const; + _NODISCARD std::string GetValue(const std::string& header, bool required = false) const; + _NODISCARD float GetValueFloat(const std::string& header, bool required = false) const; - template T GetValueInt(const std::string& header, bool required = false) const + template T GetValueInt(const std::string& header, const bool required = false) const { const auto& value = this->GetValue(header, required); if (!value.empty()) @@ -37,7 +39,7 @@ class ParsedCsv public: explicit ParsedCsv(const CsvInputStream& inputStream, bool hasHeaders = true); - size_t Size() const; + _NODISCARD size_t Size() const; ParsedCsvRow operator[](size_t index) const; }; diff --git a/src/ObjCommon/Game/T6/ObjConstantsT6.h b/src/ObjCommon/Game/T6/ObjConstantsT6.h index 392102fa5..73f1cad30 100644 --- a/src/ObjCommon/Game/T6/ObjConstantsT6.h +++ b/src/ObjCommon/Game/T6/ObjConstantsT6.h @@ -1,6 +1,4 @@ #pragma once -#include -#include namespace T6 { diff --git a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp index f4590542c..c0b12fb4d 100644 --- a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp +++ b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp @@ -6,13 +6,12 @@ #include "Game/T6/T6.h" #include "ObjContainer/SoundBank/SoundBankWriter.h" #include "Pool/GlobalAssetPool.h" -#include "nlohmann/json.hpp" +#include "Utils/StringUtils.h" -#include -#include #include #include #include +#include using namespace T6; namespace fs = std::filesystem; @@ -24,7 +23,7 @@ namespace "devraw/", }; - _NODISCARD std::string GetSoundFilePath(SndAlias* sndAlias) + _NODISCARD std::string GetSoundFilePath(const SndAlias* sndAlias) { std::string soundFilePath(sndAlias->assetFileName); @@ -88,7 +87,7 @@ size_t GetValueIndex(const std::string& value, const std::string* lookupTable, s return 0; } -unsigned int GetAliasSubListCount(unsigned int startRow, const ParsedCsv& csv) +unsigned int GetAliasSubListCount(const unsigned int startRow, const ParsedCsv& csv) { auto count = 1u; @@ -132,11 +131,11 @@ bool LoadSoundAlias(MemoryManager* memory, SndAlias* alias, const ParsedCsvRow& alias->assetFileName = memory->Dup(aliasFileName.data()); alias->assetId = Common::SND_HashName(aliasFileName.data()); - auto secondaryName = row.GetValue("secondary"); + const auto secondaryName = row.GetValue("secondary"); if (!secondaryName.empty()) alias->secondaryname = memory->Dup(secondaryName.data()); - auto subtitle = row.GetValue("subtitle"); + const auto subtitle = row.GetValue("subtitle"); if (!subtitle.empty()) alias->subtitle = memory->Dup(subtitle.data()); @@ -206,7 +205,7 @@ bool LoadSoundAliasIndexList(MemoryManager* memory, SndBank* sndBank) for (auto i = 0u; i < sndBank->aliasCount; i++) { - auto idx = sndBank->alias[i].id % sndBank->aliasCount; + const auto idx = sndBank->alias[i].id % sndBank->aliasCount; if (sndBank->aliasIndex[idx].value == std::numeric_limits::max()) { sndBank->aliasIndex[idx].value = i; @@ -280,7 +279,7 @@ bool LoadSoundAliasList( { // count how many of the next rows should be in the sound alias sub-list. Aliases are part of the same sub list if they have the same name for a // different file - auto subListCount = GetAliasSubListCount(row, aliasCsv); + const auto subListCount = GetAliasSubListCount(row, aliasCsv); if (subListCount < 1) return false; @@ -297,7 +296,7 @@ bool LoadSoundAliasList( return false; // if this asset is loaded instead of stream, increment the loaded count for later - if (sndBank->alias[listIndex].head[i].flags.loadType == T6::SA_LOADED) + if (sndBank->alias[listIndex].head[i].flags.loadType == SA_LOADED) (*loadedEntryCount)++; else (*streamedEntryCount)++; @@ -457,7 +456,7 @@ bool AssetLoaderSoundBank::LoadFromRaw( memset(sndBank, 0, sizeof(SndBank)); sndBank->name = memory->Dup(assetName.c_str()); - auto sndBankLocalization = utils::StringSplit(assetName, '.'); + const auto sndBankLocalization = utils::StringSplit(assetName, '.'); // load the soundbank aliases unsigned int loadedEntryCount = 0u, streamedEntryCount = 0u; @@ -525,12 +524,12 @@ bool AssetLoaderSoundBank::LoadFromRaw( // add aliases to the correct sound bank writer for (auto i = 0u; i < sndBank->aliasCount; i++) { - auto* aliasList = &sndBank->alias[i]; + const auto* aliasList = &sndBank->alias[i]; for (auto j = 0; j < aliasList->count; j++) { - auto* alias = &aliasList->head[j]; + const auto* alias = &aliasList->head[j]; - if (sabsWriter && alias->flags.loadType == T6::SA_STREAMED) + if (sabsWriter && alias->flags.loadType == SA_STREAMED) sabsWriter->AddSound(GetSoundFilePath(alias), alias->assetId, alias->flags.looping, true); else if (sablWriter) sablWriter->AddSound(GetSoundFilePath(alias), alias->assetId, alias->flags.looping); @@ -541,7 +540,7 @@ bool AssetLoaderSoundBank::LoadFromRaw( if (sablWriter) { size_t dataSize = 0u; - auto result = sablWriter->Write(dataSize); + const auto result = sablWriter->Write(dataSize); sablStream->close(); if (result) @@ -561,7 +560,7 @@ bool AssetLoaderSoundBank::LoadFromRaw( if (sabsWriter) { size_t dataSize = 0u; - auto result = sabsWriter->Write(dataSize); + const auto result = sabsWriter->Write(dataSize); sabsStream->close(); if (!result) diff --git a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp index ca39e057a..77a23362a 100644 --- a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp +++ b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp @@ -4,7 +4,6 @@ #include "ObjContainer/SoundBank/SoundBankTypes.h" #include "Sound/FlacDecoder.h" #include "Sound/WavTypes.h" -#include "Utils/Alignment.h" #include "Utils/FileUtils.h" #include @@ -34,8 +33,8 @@ class SoundBankWriterImpl : public SoundBankWriter inline static const std::string PAD_DATA = std::string(16, '\x00'); public: - explicit SoundBankWriterImpl(const std::string& fileName, std::ostream& stream, ISearchPath* assetSearchPath) - : m_file_name(fileName), + explicit SoundBankWriterImpl(std::string fileName, std::ostream& stream, ISearchPath* assetSearchPath) + : m_file_name(std::move(fileName)), m_stream(stream), m_asset_search_path(assetSearchPath), m_current_offset(0), @@ -102,6 +101,7 @@ class SoundBankWriterImpl : public SoundBankWriter m_entry_section_offset, m_checksum_section_offset, checksum, + {}, }; strncpy(header.dependencies, m_file_name.data(), header.dependencySize); @@ -118,7 +118,7 @@ class SoundBankWriterImpl : public SoundBankWriter const auto& soundFilePath = sound.m_file_path; const auto soundId = sound.m_sound_id; - size_t soundSize = -1; + size_t soundSize; std::unique_ptr soundData; // try to find a wav file for the sound path @@ -129,8 +129,8 @@ class SoundBankWriterImpl : public SoundBankWriter wavFile.m_stream->read(reinterpret_cast(&header), sizeof(WavHeader)); soundSize = static_cast(wavFile.m_length - sizeof(WavHeader)); - auto frameCount = soundSize / (header.formatChunk.nChannels * (header.formatChunk.wBitsPerSample / 8)); - auto frameRateIndex = INDEX_FOR_FRAMERATE[header.formatChunk.nSamplesPerSec]; + const auto frameCount = soundSize / (header.formatChunk.nChannels * (header.formatChunk.wBitsPerSample / 8)); + const auto frameRateIndex = INDEX_FOR_FRAMERATE[header.formatChunk.nSamplesPerSec]; SoundAssetBankEntry entry{ soundId, @@ -159,10 +159,10 @@ class SoundBankWriterImpl : public SoundBankWriter soundData = std::make_unique(soundSize); flacFile.m_stream->read(soundData.get(), soundSize); - auto decoder = FlacDecoder::Create(soundData.get(), soundSize); + const auto decoder = FlacDecoder::Create(soundData.get(), soundSize); if (decoder->Decode()) { - auto frameRateIndex = INDEX_FOR_FRAMERATE[decoder->GetFrameRate()]; + const auto frameRateIndex = INDEX_FOR_FRAMERATE[decoder->GetFrameRate()]; SoundAssetBankEntry entry{ soundId, soundSize, @@ -189,7 +189,7 @@ class SoundBankWriterImpl : public SoundBankWriter } } - auto lastEntry = m_entries.rbegin(); + const auto lastEntry = m_entries.rbegin(); if (!sound.m_streamed && lastEntry->frameRateIndex != 6) { std::cout << "WARNING: Loaded sound \"" << soundFilePath @@ -245,7 +245,7 @@ class SoundBankWriterImpl : public SoundBankWriter { if (!WriteEntries()) { - std::cerr << "An error occurred writing the sound bank entires. Please check output." << std::endl; + std::cerr << "An error occurred writing the sound bank entries. Please check output." << std::endl; return false; } From 4591787989fdf1c68ebc320d77d1a421b0f2a663 Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 10 Feb 2024 16:33:26 +0100 Subject: [PATCH 23/25] chore: drop third party lib for reading flac header --- .gitmodules | 3 - premake5.lua | 2 - src/ObjCommon.lua | 2 - src/ObjCommon/Sound/FlacDecoder.cpp | 268 ++++++++++++++---- src/ObjCommon/Sound/FlacDecoder.h | 36 +-- .../SoundBank/SoundBankWriter.cpp | 10 +- test/ObjCommonTests/Sound/FlacDecoderTest.cpp | 39 +++ thirdparty/flac | 1 - thirdparty/flac.lua | 47 --- 9 files changed, 282 insertions(+), 126 deletions(-) create mode 100644 test/ObjCommonTests/Sound/FlacDecoderTest.cpp delete mode 160000 thirdparty/flac delete mode 100644 thirdparty/flac.lua diff --git a/.gitmodules b/.gitmodules index 241c59ce6..28b1e92ea 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,6 +13,3 @@ [submodule "thirdparty/json"] path = thirdparty/json url = https://github.com/nlohmann/json.git -[submodule "thirdparty/flac"] - path = thirdparty/flac - url = https://github.com/astoeckel/libfoxenflac.git diff --git a/premake5.lua b/premake5.lua index 2eb1d6d7d..6d9b19f78 100644 --- a/premake5.lua +++ b/premake5.lua @@ -83,7 +83,6 @@ workspace "OpenAssetTools" -- ThirdParty -- ======================== include "thirdparty/catch2.lua" -include "thirdparty/flac.lua" include "thirdparty/libtomcrypt.lua" include "thirdparty/libtommath.lua" include "thirdparty/json.lua" @@ -95,7 +94,6 @@ include "thirdparty/zlib.lua" -- ThirdParty group: All projects that are external dependencies group "ThirdParty" catch2:project() - flac:project() libtommath:project() libtomcrypt:project() json:project() diff --git a/src/ObjCommon.lua b/src/ObjCommon.lua index 19ae27e86..2d591bbef 100644 --- a/src/ObjCommon.lua +++ b/src/ObjCommon.lua @@ -15,7 +15,6 @@ function ObjCommon:link(links) links:linkto(Utils) links:linkto(Common) links:linkto(minizip) - links:linkto(flac) end function ObjCommon:use() @@ -49,5 +48,4 @@ function ObjCommon:project() self:include(includes) Utils:include(includes) - flac:include(includes) end diff --git a/src/ObjCommon/Sound/FlacDecoder.cpp b/src/ObjCommon/Sound/FlacDecoder.cpp index 335a8b707..cff3072f5 100644 --- a/src/ObjCommon/Sound/FlacDecoder.cpp +++ b/src/ObjCommon/Sound/FlacDecoder.cpp @@ -1,77 +1,245 @@ #include "FlacDecoder.h" -#include +#include "Utils/Alignment.h" +#include "Utils/Endianness.h" +#include "Utils/FileUtils.h" -class fx_flac_raii +#include +#include +#include + +namespace { -public: - fx_flac_raii() - { - ptr = FX_FLAC_ALLOC_DEFAULT(); - } + constexpr auto FLAC_MAGIC = FileUtils::MakeMagic32('f', 'L', 'a', 'C'); - ~fx_flac_raii() + enum class MetaDataBlockType : unsigned { - free(ptr); - } + STREAMINFO = 0, + PADDING = 1, + APPLICATION = 2, + SEEKTABLE = 3, + VORBIS_COMMENT = 4, + CUESHEET = 5, + PICTURE = 6 + }; - operator fx_flac_t*() + struct MetaDataBlockHeader { - return ptr; - } + uint8_t isLastMetaDataBlock; + MetaDataBlockType blockType; + uint32_t blockLength; + }; -private: - fx_flac_t* ptr; -}; + constexpr auto STREAM_INFO_BLOCK_SIZE = 34; -class FlacDecoderImpl : public FlacDecoder -{ -private: - void* m_data; - size_t m_length; - std::unique_ptr m_flac; - -public: - explicit FlacDecoderImpl(void* data, size_t length) - : m_data(data), - m_length(length) + class FlacReadingException final : public std::exception { - } + public: + explicit FlacReadingException(std::string message) + : m_message(std::move(message)) + { + } + + _NODISCARD char const* what() const noexcept override + { + return m_message.c_str(); + } + + private: + std::string m_message; + }; - unsigned int GetFrameCount() override + class FlacBitReader { - return static_cast(fx_flac_get_streaminfo(*m_flac.get(), FLAC_KEY_N_SAMPLES)); - } + public: + explicit FlacBitReader(std::istream& stream) + : m_stream(stream), + m_last_byte(0u), + m_remaining_bits_last_byte(0u) + { + } + + template T ReadBits(const size_t bitCount) + { + union + { + uint8_t buffer[sizeof(T)]; + T result; + } data{}; + + const auto byteCount = utils::Align(bitCount, 8u) / 8u; + assert(byteCount <= sizeof(T)); + + const auto shiftCount = (8u - bitCount % 8) % 8; + + auto remainingBits = bitCount; + +#if HOST_ENDIANNESS == LITTLE_ENDIAN_ENDIANNESS + auto offset = byteCount - 1; +#else + auto offset = 0u; +#endif + + while (remainingBits > 0) + { + const auto curBits = static_cast(std::min(remainingBits, 8u)); + + if (m_remaining_bits_last_byte > 0) + { + if (m_remaining_bits_last_byte < curBits) + { + const auto bitsFromFirstByte = m_remaining_bits_last_byte; + data.buffer[offset] = static_cast(m_last_byte << (8u - bitsFromFirstByte)); + + m_stream.read(reinterpret_cast(&m_last_byte), sizeof(m_last_byte)); + if (m_stream.gcount() != sizeof(m_last_byte)) + throw FlacReadingException("Unexpected eof"); + + const auto bitsFromSecondByte = static_cast(curBits - m_remaining_bits_last_byte); + m_remaining_bits_last_byte = 8u - bitsFromSecondByte; + const auto maskForSecondByte = static_cast(0xFF << (8u - bitsFromSecondByte)); + data.buffer[offset] |= (m_last_byte & maskForSecondByte) >> bitsFromFirstByte; + } + else if (m_remaining_bits_last_byte == curBits) + { + data.buffer[offset] = static_cast(m_last_byte << (8u - curBits)); + m_remaining_bits_last_byte = 0u; + } + else // m_remaining_bits_last_byte > curBits + { + const auto maskForCurBits = 0xFF >> (8u - curBits); + const auto maskForCurBitsInRemainingBits = static_cast(maskForCurBits << (m_remaining_bits_last_byte - curBits)); + const auto selectedData = static_cast(m_last_byte & maskForCurBitsInRemainingBits); + data.buffer[offset] = static_cast(selectedData << (8u - m_remaining_bits_last_byte)); + m_remaining_bits_last_byte -= curBits; + } + } + else if (curBits >= 8u) + { + m_stream.read(reinterpret_cast(&data.buffer[offset]), sizeof(uint8_t)); + if (m_stream.gcount() != sizeof(uint8_t)) + throw FlacReadingException("Unexpected eof"); + } + else + { + m_stream.read(reinterpret_cast(&m_last_byte), sizeof(m_last_byte)); + if (m_stream.gcount() != sizeof(m_last_byte)) + throw FlacReadingException("Unexpected eof"); - unsigned int GetFrameRate() override + data.buffer[offset] = m_last_byte & (0xFF << (8u - curBits)); + m_remaining_bits_last_byte = static_cast(8u - curBits); + } + + remainingBits -= curBits; +#if HOST_ENDIANNESS == LITTLE_ENDIAN_ENDIANNESS + --offset; +#else + ++offset; +#endif + } + + data.result >>= shiftCount; + return data.result; + } + + void ReadBuffer(void* buffer, const size_t bitCount) + { + assert(m_remaining_bits_last_byte == 0); + assert(bitCount % 8 == 0); + + m_remaining_bits_last_byte = 0; + m_stream.read(static_cast(buffer), bitCount / 8); + } + + void Seek(const size_t offset) + { + assert(m_remaining_bits_last_byte == 0); + m_remaining_bits_last_byte = 0; + m_stream.seekg(offset, std::ios::cur); + } + + private: + std::istream& m_stream; + uint8_t m_last_byte; + uint8_t m_remaining_bits_last_byte; + }; +} // namespace + +namespace flac +{ + FlacMetaData::FlacMetaData() + : m_minimum_block_size(), + m_maximum_block_size(), + m_minimum_frame_size(), + m_maximum_frame_size(), + m_sample_rate(), + m_number_of_channels(), + m_bits_per_sample(), + m_total_samples(), + m_md5_signature{} { - return static_cast(fx_flac_get_streaminfo(*m_flac.get(), FLAC_KEY_SAMPLE_RATE)); } - unsigned int GetNumChannels() override + void FlacReadStreamInfo(FlacBitReader& reader, FlacMetaData& metaData) { - return static_cast(fx_flac_get_streaminfo(*m_flac.get(), FLAC_KEY_N_CHANNELS)); + metaData.m_minimum_block_size = reader.ReadBits(16); + metaData.m_maximum_block_size = reader.ReadBits(16); + metaData.m_minimum_frame_size = reader.ReadBits(24); + metaData.m_maximum_frame_size = reader.ReadBits(24); + metaData.m_sample_rate = reader.ReadBits(20); + metaData.m_number_of_channels = static_cast(reader.ReadBits(3) + 1); + metaData.m_bits_per_sample = static_cast(reader.ReadBits(5) + 1); + metaData.m_total_samples = reader.ReadBits(36); + reader.ReadBuffer(metaData.m_md5_signature, 128); } - bool Decode() override + bool GetFlacMetaData(std::istream& stream, FlacMetaData& metaData) { - m_flac = std::make_unique(); - - while (true) + try { - auto res = fx_flac_process(*m_flac.get(), reinterpret_cast(m_data), &m_length, nullptr, nullptr); - if (res == FLAC_ERR) - return false; + uint32_t readMagic; + stream.read(reinterpret_cast(&readMagic), sizeof(readMagic)); + if (stream.gcount() != sizeof(readMagic) || readMagic != FLAC_MAGIC) + throw FlacReadingException("Invalid flac magic"); + + FlacBitReader reader(stream); + while (true) + { + const MetaDataBlockHeader header{ + + reader.ReadBits(1), + static_cast(reader.ReadBits(7)), + reader.ReadBits(24), + }; + + if (header.blockType == MetaDataBlockType::STREAMINFO) + { + if (header.blockLength != STREAM_INFO_BLOCK_SIZE) + throw FlacReadingException("Flac stream info block size invalid"); + + FlacReadStreamInfo(reader, metaData); + return true; + } + + reader.Seek(header.blockLength * 8u); - if (res == FLAC_END_OF_METADATA) - break; + if (header.isLastMetaDataBlock) + break; + } + + throw FlacReadingException("Missing flac stream info block"); + } + catch (const FlacReadingException& e) + { + std::cerr << e.what() << "\n"; } - return true; + return false; } -}; -std::unique_ptr FlacDecoder::Create(void* data, size_t length) -{ - return std::make_unique(data, length); -} + bool GetFlacMetaData(const void* data, const size_t dataSize, FlacMetaData& metaData) + { + std::istringstream ss(std::string(static_cast(data), dataSize)); + return GetFlacMetaData(ss, metaData); + } +} // namespace flac diff --git a/src/ObjCommon/Sound/FlacDecoder.h b/src/ObjCommon/Sound/FlacDecoder.h index 0b659b234..cec719756 100644 --- a/src/ObjCommon/Sound/FlacDecoder.h +++ b/src/ObjCommon/Sound/FlacDecoder.h @@ -1,22 +1,26 @@ #pragma once + +#include #include -#include -class FlacDecoder +namespace flac { -public: - FlacDecoder() = default; - virtual ~FlacDecoder() = default; - - FlacDecoder(const FlacDecoder& other) = default; - FlacDecoder(FlacDecoder&& other) noexcept = default; - FlacDecoder& operator=(const FlacDecoder& other) = default; - FlacDecoder& operator=(FlacDecoder&& other) noexcept = default; + class FlacMetaData + { + public: + uint16_t m_minimum_block_size; + uint16_t m_maximum_block_size; + uint32_t m_minimum_frame_size; + uint32_t m_maximum_frame_size; + uint32_t m_sample_rate; + uint8_t m_number_of_channels; + uint8_t m_bits_per_sample; + uint64_t m_total_samples; + uint8_t m_md5_signature[16]; - virtual bool Decode() = 0; - virtual unsigned int GetFrameCount() = 0; - virtual unsigned int GetFrameRate() = 0; - virtual unsigned int GetNumChannels() = 0; + FlacMetaData(); + }; - static std::unique_ptr Create(void* data, size_t length); -}; + bool GetFlacMetaData(std::istream& stream, FlacMetaData& metaData); + bool GetFlacMetaData(const void* data, size_t dataSize, FlacMetaData& metaData); +} // namespace flac diff --git a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp index 77a23362a..153a75bab 100644 --- a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp +++ b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp @@ -159,17 +159,17 @@ class SoundBankWriterImpl : public SoundBankWriter soundData = std::make_unique(soundSize); flacFile.m_stream->read(soundData.get(), soundSize); - const auto decoder = FlacDecoder::Create(soundData.get(), soundSize); - if (decoder->Decode()) + flac::FlacMetaData metaData; + if (flac::GetFlacMetaData(soundData.get(), soundSize, metaData)) { - const auto frameRateIndex = INDEX_FOR_FRAMERATE[decoder->GetFrameRate()]; + const auto frameRateIndex = INDEX_FOR_FRAMERATE[metaData.m_sample_rate]; SoundAssetBankEntry entry{ soundId, soundSize, static_cast(m_current_offset), - decoder->GetFrameCount(), + static_cast(metaData.m_total_samples), frameRateIndex, - static_cast(decoder->GetNumChannels()), + metaData.m_number_of_channels, sound.m_looping, 8, }; diff --git a/test/ObjCommonTests/Sound/FlacDecoderTest.cpp b/test/ObjCommonTests/Sound/FlacDecoderTest.cpp new file mode 100644 index 000000000..b7e6ecd45 --- /dev/null +++ b/test/ObjCommonTests/Sound/FlacDecoderTest.cpp @@ -0,0 +1,39 @@ +#include "Sound/FlacDecoder.h" + +#include +#include + +namespace flac +{ + TEST_CASE("FlacDecoder: Ensure properly decodes flac stream info", "[sound][flac]") + { + // clang-format off + constexpr uint8_t testData[] + { + // Magic + 'f', 'L', 'a', 'C', + + // Block header + 0x00, 0x00, 0x00, 0x22, + + // StreamInfo block + 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0xB8, 0x02, 0xF0, 0x00, + 0x02, 0xF9, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + }; + // clang-format on + + FlacMetaData metaData; + const auto result = GetFlacMetaData(testData, sizeof(testData), metaData); + + REQUIRE(result == true); + REQUIRE(metaData.m_minimum_block_size == 1024); + REQUIRE(metaData.m_maximum_block_size == 1024); + REQUIRE(metaData.m_minimum_frame_size == 0); + REQUIRE(metaData.m_maximum_frame_size == 0); + REQUIRE(metaData.m_sample_rate == 48000); + REQUIRE(metaData.m_number_of_channels == 2); + REQUIRE(metaData.m_bits_per_sample == 16); + REQUIRE(metaData.m_total_samples == 194870); + } +} // namespace flac diff --git a/thirdparty/flac b/thirdparty/flac deleted file mode 160000 index 1b04fafb5..000000000 --- a/thirdparty/flac +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1b04fafb51aac0c3b7f0118a7bf7c93f6a60d824 diff --git a/thirdparty/flac.lua b/thirdparty/flac.lua deleted file mode 100644 index b840aa9dd..000000000 --- a/thirdparty/flac.lua +++ /dev/null @@ -1,47 +0,0 @@ -flac = {} - -function flac:include(includes) - if includes:handle(self:name()) then - includedirs { - path.join(ThirdPartyFolder(), "flac") - } - end -end - -function flac:link(links) - links:add(self:name()) -end - -function flac:use() - -end - -function flac:name() - return "flac" -end - -function flac:project() - local folder = ThirdPartyFolder() - local includes = Includes:create() - - project(self:name()) - targetdir(TargetDirectoryLib) - location "%{wks.location}/thirdparty/%{prj.name}" - kind "StaticLib" - language "C" - - files { - path.join(folder, "flac/foxen/*.h"), - path.join(folder, "flac/foxen/*.c") - } - - defines { - "_CRT_SECURE_NO_WARNINGS", - "_CRT_NONSTDC_NO_DEPRECATE" - } - - self:include(includes) - - -- Disable warnings. They do not have any value to us since it is not our code. - warnings "off" -end From 4914eb552f4e7edd23c5111a834f077737974f2f Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 10 Feb 2024 17:04:32 +0100 Subject: [PATCH 24/25] chore: fix linux build --- src/ObjCommon/Sound/FlacDecoder.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ObjCommon/Sound/FlacDecoder.cpp b/src/ObjCommon/Sound/FlacDecoder.cpp index cff3072f5..b709029fb 100644 --- a/src/ObjCommon/Sound/FlacDecoder.cpp +++ b/src/ObjCommon/Sound/FlacDecoder.cpp @@ -1,6 +1,7 @@ #include "FlacDecoder.h" #include "Utils/Alignment.h" +#include "Utils/ClassUtils.h" #include "Utils/Endianness.h" #include "Utils/FileUtils.h" From f6ee4790faffe3b054c95cfcc273fa659e6739c4 Mon Sep 17 00:00:00 2001 From: Jan Date: Sun, 11 Feb 2024 11:32:47 +0100 Subject: [PATCH 25/25] fix: not dumping all sound aliases in aliases csv --- .../Game/T6/AssetDumpers/AssetDumperSndBank.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp index e25b5a99a..c2ae698b3 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp @@ -214,7 +214,7 @@ class AssetDumperSndBank::Internal stream.WriteColumn(alias->name); // file - stream.WriteColumn(alias->assetFileName); + stream.WriteColumn(alias->assetFileName ? alias->assetFileName : ""); // template stream.WriteColumn(""); @@ -520,13 +520,14 @@ class AssetDumperSndBank::Internal for (auto j = 0; j < aliasList.count; j++) { const auto& alias = aliasList.head[j]; + + WriteAliasToFile(csvStream, &alias, sndBank); + csvStream.NextRow(); + if (alias.assetId && alias.assetFileName && dumpedAssets.find(alias.assetId) == dumpedAssets.end()) { DumpSndAlias(alias); dumpedAssets.emplace(alias.assetId); - - WriteAliasToFile(csvStream, &alias, sndBank); - csvStream.NextRow(); } } }