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 00000000..3ac97809 --- /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 73f1cad3..d5991d9a 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 00000000..d74aa12d --- /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 00000000..f7f8154d --- /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 431a3ca4..dcff00e5 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 b36a4f3c..c01cc4e3 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 bf8904da..31368acd 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 ca3f771d..4104c4f1 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 | 115 ++++++++++++++---- 1 file changed, 94 insertions(+), 21 deletions(-) diff --git a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp index d74aa12d..afb5a729 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 = memory->Dup(sndBankLocalization.at(0).c_str()); + sndBank->streamAssetBank.language = memory->Dup(sndBankLocalization.at(1).c_str()); + memset(sndBank->streamAssetBank.linkTimeChecksum, 0xCC, 16); - 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->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 afb5a729..49e5124a 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 c01cc4e3..33bf94c1 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 ffb4113f..30de7a3e 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 30de7a3e..ffb4113f 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 00000000..77ef87ee --- /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 00000000..01df30fb --- /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 c9109ae5..30605cdb 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 49e5124a..ffa9263d 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) +size_t GetValueIndex(const std::string& value, const std::string* lookupTable, size_t len) { - 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) -{ - 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 4ee69ce4..3dc07fd7 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 40f99752..e7e5edb8 100644 --- a/src/Crypto/Crypto.h +++ b/src/Crypto/Crypto.h @@ -16,6 +16,8 @@ public: 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 00000000..21558383 --- /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 00000000..f9ae0514 --- /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 c6d2940e..613aee22 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 77ef87ee..3ac96229 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 01df30fb..90973761 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 18dc14b9..d14f34ed 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 ffa9263d..8f97dbdc 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 00000000..b371dee9 --- /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 00000000..a9f7f623 --- /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 cf0ef668..8505cd8b 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 6d0a5ae7..32b3e489 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 4e7c2784..8ee153e5 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 21558383..3e14e74b 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 @@ public: 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 f9ae0514..25024f08 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 8f97dbdc..f8391925 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 | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp index f8391925..90fad2bd 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 +#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 01a094e8..e9587c11 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 3ac96229..d566eac8 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 90973761..22d4e5be 100644 --- a/src/ObjCommon/Csv/ParsedCsv.h +++ b/src/ObjCommon/Csv/ParsedCsv.h @@ -12,7 +12,7 @@ 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; - + 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 d14f34ed..1a997816 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 b371dee9..57fc29d5 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 a9f7f623..88153b77 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 @@ public: 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 8ee153e5..eb0174a9 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 31368acd..cb5ede9c 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 57fc29d5..3946f14c 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 3946f14c..e9a728a5 100644 --- a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp +++ b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp @@ -147,7 +147,7 @@ public: 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 e9a728a5..449fe0e9 100644 --- a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp +++ b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp @@ -222,7 +222,12 @@ public: 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 449fe0e9..0fc4fa6b 100644 --- a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp +++ b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp @@ -84,6 +84,8 @@ public: { 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 90fad2bd..5a19f6e1 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 0fc4fa6b..d44035da 100644 --- a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp +++ b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp @@ -45,9 +45,9 @@ public: { } - 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 @@ public: 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 @@ public: 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 @@ public: frameCount, frameRateIndex, static_cast(header.formatChunk.nChannels), - 0, + sound.looping, 0, }; @@ -222,12 +229,12 @@ public: 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 @@ public: 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 88153b77..cb0fb07a 100644 --- a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.h +++ b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.h @@ -16,8 +16,8 @@ public: 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 28b1e92e..241c59ce 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 6d9b19f7..2eb1d6d7 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 2d591bbe..19ae27e8 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 00000000..ccaad5b0 --- /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 00000000..1d6a46d0 --- /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 d44035da..1ea92661 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 @@ public: 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 @@ public: { 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 @@ public: } } + 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 00000000..1b04fafb --- /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 00000000..b840aa9d --- /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 25024f08..4687572a 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 d566eac8..9ddb3e5a 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 22d4e5be..ff2030d3 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 d5991d9a..392102fa 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 3ac97809..feb7cff6 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 5a19f6e1..4b6d0775 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 1ea92661..bab982d4 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 @@ public: 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 @@ public: 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 @@ public: static_cast(m_current_offset), decoder->GetFrameCount(), frameRateIndex, - decoder->GetNumChannels(), + static_cast(decoder->GetNumChannels()), sound.looping, 8, }; @@ -192,7 +193,8 @@ public: 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 @@ public: 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 @@ public: 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 cb0fb07a..ccd338b3 100644 --- a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.h +++ b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.h @@ -17,7 +17,7 @@ public: 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 33bf94c1..e25b5a99 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 eb0174a9..4ce2bbc5 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 cb5ede9c..d582fa1f 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 ff2030d3..358a4ee8 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 ccaad5b0..335a8b70 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 @@ public: free(ptr); } - operator fx_flac_t* () + operator fx_flac_t*() { return ptr; } -private: +private: fx_flac_t* ptr; }; @@ -35,7 +36,6 @@ public: : m_data(data), m_length(length) { - } unsigned int GetFrameCount() override @@ -48,7 +48,7 @@ public: 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 1d6a46d0..136fc68f 100644 --- a/src/ObjCommon/Sound/FlacDecoder.h +++ b/src/ObjCommon/Sound/FlacDecoder.h @@ -18,4 +18,4 @@ public: 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 4ce2bbc5..c3bda89f 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 136fc68f..0b659b23 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 4b6d0775..f4590542 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 bab982d4..ca39e057 100644 --- a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp +++ b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp @@ -38,7 +38,6 @@ public: : 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 @@ public: 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 @@ public: 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 @@ public: frameCount, frameRateIndex, static_cast(header.formatChunk.nChannels), - sound.looping, + sound.m_looping, 0, }; @@ -171,7 +170,7 @@ public: decoder->GetFrameCount(), frameRateIndex, static_cast(decoder->GetNumChannels()), - sound.looping, + sound.m_looping, 8, }; @@ -191,7 +190,7 @@ public: } 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 @@ public: } 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 9ddb3e5a..c54ce5a5 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 358a4ee8..6913c80c 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 392102fa..73f1cad3 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 f4590542..c0b12fb4 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 ca39e057..77a23362 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 @@ public: m_entry_section_offset, m_checksum_section_offset, checksum, + {}, }; strncpy(header.dependencies, m_file_name.data(), header.dependencySize); @@ -118,7 +118,7 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: } } - 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 @@ public: { 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 | 294 ++++++++++++++---- 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, 295 insertions(+), 139 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 241c59ce..28b1e92e 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 2eb1d6d7..6d9b19f7 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 19ae27e8..2d591bbe 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 335a8b70..cff3072f 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() + constexpr auto FLAC_MAGIC = FileUtils::MakeMagic32('f', 'L', 'a', 'C'); + + enum class MetaDataBlockType : unsigned { - ptr = FX_FLAC_ALLOC_DEFAULT(); - } + STREAMINFO = 0, + PADDING = 1, + APPLICATION = 2, + SEEKTABLE = 3, + VORBIS_COMMENT = 4, + CUESHEET = 5, + PICTURE = 6 + }; - ~fx_flac_raii() + struct MetaDataBlockHeader { - free(ptr); - } + uint8_t isLastMetaDataBlock; + MetaDataBlockType blockType; + uint32_t blockLength; + }; - operator fx_flac_t*() + constexpr auto STREAM_INFO_BLOCK_SIZE = 34; + + class FlacReadingException final : public std::exception { - 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) + public: + explicit FlacReadingException(std::string message) + : m_message(std::move(message)) { - 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; - } -}; + _NODISCARD char const* what() const noexcept override + { + return m_message.c_str(); + } -std::unique_ptr FlacDecoder::Create(void* data, size_t length) + private: + std::string m_message; + }; + + class FlacBitReader + { + 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"); + + 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 { - return std::make_unique(data, length); -} + 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{} + { + } + + void FlacReadStreamInfo(FlacBitReader& reader, FlacMetaData& metaData) + { + 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 GetFlacMetaData(std::istream& stream, FlacMetaData& metaData) + { + try + { + 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 (header.isLastMetaDataBlock) + break; + } + + throw FlacReadingException("Missing flac stream info block"); + } + catch (const FlacReadingException& e) + { + std::cerr << e.what() << "\n"; + } + + return false; + } + + 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 0b659b23..cec71975 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; + 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]; - FlacDecoder(const FlacDecoder& other) = default; - FlacDecoder(FlacDecoder&& other) noexcept = default; - FlacDecoder& operator=(const FlacDecoder& other) = default; - FlacDecoder& operator=(FlacDecoder&& other) noexcept = default; + FlacMetaData(); + }; - 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); -}; + 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 77a23362..153a75ba 100644 --- a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp +++ b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp @@ -159,17 +159,17 @@ public: 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 00000000..b7e6ecd4 --- /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 1b04fafb..00000000 --- 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 b840aa9d..00000000 --- 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 cff3072f..b709029f 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 e25b5a99..c2ae698b 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(); } } }