diff --git a/src/Common/Game/T6/T6_Assets.h b/src/Common/Game/T6/T6_Assets.h index 99cea761..5711f232 100644 --- a/src/Common/Game/T6/T6_Assets.h +++ b/src/Common/Game/T6/T6_Assets.h @@ -6182,7 +6182,43 @@ namespace T6 SA_LOADED = 0x1, SA_STREAMED = 0x2, SA_PRIMED = 0x3, - SA_COUNT = 0x4, + + SA_COUNT + }; + + enum SndLimitType + { + SND_LIMIT_NONE = 0x0, + SND_LIMIT_OLDEST = 0x1, + SND_LIMIT_REJECT = 0x2, + SND_LIMIT_PRIORITY = 0x3, + + SND_LIMIT_COUNT + }; + + enum SndBus + { + SND_BUS_REVERB = 0x0, + SND_BUS_FX = 0x1, + SND_BUS_VOICE = 0x2, + SND_BUS_PFUTZ = 0x3, + SND_BUS_HDRFX = 0x4, + SND_BUS_UI = 0x5, + SND_BUS_MUSIC = 0x6, + SND_BUS_MOVIE = 0x7, + SND_BUS_REFERENCE = 0x8, + + SND_BUS_COUNT + }; + + enum SndRandomizeType + { + SND_RANDOMIZE_INSTANCE = 0x0, + SND_RANDOMIZE_ENTITY_VOLUME = 0x1, + SND_RANDOMIZE_ENTITY_PITCH = 0x2, + SND_RANDOMIZE_ENTITY_VARIANT = 0x4, + + SND_RANDOMIZE_ENTITY_COUNT }; struct SndAliasFlags @@ -6248,16 +6284,16 @@ namespace T6 int16_t fadeIn; int16_t fadeOut; int16_t dopplerScale; - char minPriorityThreshold; - char maxPriorityThreshold; - char probability; - char occlusionLevel; - char minPriority; - char maxPriority; - char pan; - char limitCount; - char entityLimitCount; - char duckGroup; + uint8_t minPriorityThreshold; + uint8_t maxPriorityThreshold; + uint8_t probability; + uint8_t occlusionLevel; + uint8_t minPriority; + uint8_t maxPriority; + uint8_t pan; + uint8_t limitCount; + uint8_t entityLimitCount; + uint8_t duckGroup; }; #ifndef __zonecodegenerator diff --git a/src/ObjCommon/Game/T6/SoundConstantsT6.h b/src/ObjCommon/Game/T6/SoundConstantsT6.h index feb7cff6..878db002 100644 --- a/src/ObjCommon/Game/T6/SoundConstantsT6.h +++ b/src/ObjCommon/Game/T6/SoundConstantsT6.h @@ -1,9 +1,14 @@ #pragma once -#include + +#include "Game/T6/T6.h" + +#include namespace T6 { - inline const std::string SOUND_GROUPS[]{ + // From SndDriverGlobals + inline constexpr const char* SOUND_GROUPS[]{ + // clang-format off "grp_reference", "grp_master", "grp_wpn_lfe", @@ -30,10 +35,11 @@ namespace T6 "grp_air", "grp_bink", "grp_announcer", - "", + // clang-format on }; - inline const std::string SOUND_CURVES[]{ + // From SndDriverGlobals + inline constexpr const char* SOUND_CURVES[]{ "default", "defaultmin", "allon", @@ -51,10 +57,10 @@ namespace T6 "cos", "rev60", "rev65", - "", }; - inline const std::string SOUND_DUCK_GROUPS[]{ + // From SndDriverGlobals + inline constexpr const char* SOUND_DUCK_GROUPS[]{ "snp_alerts_gameplay", "snp_ambience", "snp_claw", @@ -89,14 +95,16 @@ namespace T6 "snp_x3", }; - inline const std::string SOUND_LIMIT_TYPES[]{ + inline constexpr const char* SOUND_LIMIT_TYPES[]{ "none", "oldest", "reject", "priority", }; + static_assert(std::extent_v == SND_LIMIT_COUNT); - inline const std::string SOUND_MOVE_TYPES[]{ + // From executable + inline constexpr const char* SOUND_MOVE_TYPES[]{ "none", "left_player", "center_player", @@ -107,31 +115,32 @@ namespace T6 "right_shot", }; - inline const std::string SOUND_LOAD_TYPES[]{ + inline constexpr const char* SOUND_LOAD_TYPES[]{ "unknown", "loaded", "streamed", "primed", }; + static_assert(std::extent_v == SA_COUNT); - inline const std::string SOUND_BUS_IDS[]{ + inline constexpr const char* SOUND_BUS_IDS[]{ "bus_reverb", "bus_fx", "bus_voice", "bus_pfutz", "bus_hdrfx", "bus_ui", - "bus_reference", "bus_music", "bus_movie", "bus_reference", - "", }; + static_assert(std::extent_v == SND_BUS_COUNT); - inline const std::string SOUND_RANDOMIZE_TYPES[]{ + // From executable + inline constexpr const char* 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 8269dc81..371187ab 100644 --- a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp +++ b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderSoundBank.cpp @@ -72,7 +72,7 @@ bool AssetLoaderSoundBank::CanLoadFromRaw() const return true; } -size_t GetValueIndex(const std::string& value, const std::string* lookupTable, size_t len) +size_t GetValueIndex(const std::string& value, const char* const* lookupTable, const size_t len) { if (value.empty()) return 0; @@ -141,33 +141,33 @@ bool LoadSoundAlias(MemoryManager* memory, SndAlias* alias, const ParsedCsvRow& alias->duck = Common::SND_HashName(row.GetValue("duck").data()); - 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->volMin = row.GetValueIntvolMin)>("vol_min"); + alias->volMax = row.GetValueIntvolMax)>("vol_max"); + alias->distMin = row.GetValueIntdistMin)>("dist_min"); + alias->distMax = row.GetValueIntdistMax)>("dist_max"); + alias->distReverbMax = row.GetValueIntdistReverbMax)>("dist_reverb_max"); + alias->limitCount = row.GetValueIntlimitCount)>("limit_count"); + alias->entityLimitCount = row.GetValueIntentityLimitCount)>("entity_limit_count"); + alias->pitchMin = row.GetValueIntpitchMin)>("pitch_min"); + alias->pitchMax = row.GetValueIntpitchMax)>("pitch_max"); + alias->minPriority = row.GetValueIntminPriority)>("min_priority"); + alias->maxPriority = row.GetValueIntmaxPriority)>("max_priority"); + alias->minPriorityThreshold = row.GetValueIntminPriorityThreshold)>("min_priority_threshold"); + alias->maxPriorityThreshold = row.GetValueIntmaxPriorityThreshold)>("max_priority_threshold"); + alias->probability = row.GetValueIntprobability)>("probability"); + alias->startDelay = row.GetValueIntstartDelay)>("start_delay"); + alias->reverbSend = row.GetValueIntreverbSend)>("reverb_send"); + alias->centerSend = row.GetValueIntcenterSend)>("center_send"); + alias->envelopMin = row.GetValueIntenvelopMin)>("envelop_min"); + alias->envelopMax = row.GetValueIntenvelopMax)>("envelop_max"); + alias->envelopPercentage = row.GetValueIntenvelopPercentage)>("envelop_percentage"); + alias->occlusionLevel = row.GetValueIntocclusionLevel)>("occlusion_level"); + alias->fluxTime = row.GetValueIntfluxTime)>("move_time"); + alias->futzPatch = row.GetValueIntfutzPatch)>("futz"); + alias->contextType = row.GetValueIntcontextType)>("context_type"); + alias->contextValue = row.GetValueIntcontextValue)>("context_value"); + alias->fadeIn = row.GetValueIntfadeIn)>("fade_in"); + alias->fadeOut = row.GetValueIntfadeOut)>("fade_out"); alias->flags.looping = row.GetValue("loop") == "looping"; alias->flags.panType = row.GetValue("pan") == "3d"; @@ -179,7 +179,8 @@ 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"), SOUND_DUCK_GROUPS, std::extent_v)); + alias->duckGroup = + static_castduckGroup)>(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); diff --git a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp index 7294ff81..0c60f554 100644 --- a/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp +++ b/src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp @@ -5,12 +5,16 @@ #include "Sound/FlacDecoder.h" #include "Sound/WavTypes.h" #include "Utils/FileUtils.h" +#include "Utils/StringUtils.h" #include #include +#include #include #include +namespace fs = std::filesystem; + std::unordered_map INDEX_FOR_FRAMERATE{ {8000, 0}, {12000, 1}, @@ -32,6 +36,30 @@ class SoundBankWriterImpl : public SoundBankWriter inline static const std::string PAD_DATA = std::string(16, '\x00'); + class SoundBankEntryInfo + { + 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; + }; + public: explicit SoundBankWriterImpl(std::string fileName, std::ostream& stream, ISearchPath* assetSearchPath) : m_file_name(std::move(fileName)), @@ -118,6 +146,100 @@ public: Write(&header, sizeof(header)); } + bool LoadWavFile(const SearchPathOpenFile& file, const SoundBankEntryInfo& sound, std::unique_ptr& soundData, size_t& soundSize) + { + WavHeader header{}; + file.m_stream->read(reinterpret_cast(&header), sizeof(WavHeader)); + + soundSize = static_cast(file.m_length - sizeof(WavHeader)); + const auto frameCount = soundSize / (header.formatChunk.nChannels * (header.formatChunk.wBitsPerSample / 8)); + const auto frameRateIndex = INDEX_FOR_FRAMERATE[header.formatChunk.nSamplesPerSec]; + + SoundAssetBankEntry entry{ + sound.m_sound_id, + soundSize, + static_cast(m_current_offset), + frameCount, + frameRateIndex, + static_cast(header.formatChunk.nChannels), + sound.m_looping, + 0, + }; + + m_entries.push_back(entry); + + soundData = std::make_unique(soundSize); + file.m_stream->read(soundData.get(), soundSize); + + return true; + } + + bool LoadFlacFile( + const SearchPathOpenFile& file, const std::string& filePath, const SoundBankEntryInfo& sound, std::unique_ptr& soundData, size_t& soundSize) + { + soundSize = static_cast(file.m_length); + + soundData = std::make_unique(soundSize); + file.m_stream->read(soundData.get(), soundSize); + + flac::FlacMetaData metaData; + if (flac::GetFlacMetaData(soundData.get(), soundSize, metaData)) + { + const auto frameRateIndex = INDEX_FOR_FRAMERATE[metaData.m_sample_rate]; + SoundAssetBankEntry entry{ + sound.m_sound_id, + soundSize, + static_cast(m_current_offset), + static_cast(metaData.m_total_samples), + frameRateIndex, + metaData.m_number_of_channels, + sound.m_looping, + 8, + }; + + m_entries.push_back(entry); + return true; + } + + std::cerr << std::format("Unable to decode .flac file for sound {}\n", filePath); + return false; + } + + bool LoadFileByExtension(const std::string& filePath, const SoundBankEntryInfo& sound, std::unique_ptr& soundData, size_t& soundSize) + { + auto extension = fs::path(filePath).extension().string(); + utils::MakeStringLowerCase(extension); + if (extension.empty()) + return false; + + const auto file = m_asset_search_path->Open(filePath); + if (!file.IsOpen()) + return false; + + if (extension == ".wav") + return LoadWavFile(file, sound, soundData, soundSize); + + if (extension == ".flac") + return LoadFlacFile(file, filePath, sound, soundData, soundSize); + + return false; + } + + bool GuessFilenameAndLoadFile(const std::string& filePath, const SoundBankEntryInfo& sound, std::unique_ptr& soundData, size_t& soundSize) + { + fs::path pathWithExtension = fs::path(filePath).replace_extension(".wav"); + auto file = m_asset_search_path->Open(pathWithExtension.string()); + if (file.IsOpen()) + return LoadWavFile(file, sound, soundData, soundSize); + + pathWithExtension = fs::path(filePath).replace_extension(".flac"); + file = m_asset_search_path->Open(pathWithExtension.string()); + if (file.IsOpen()) + return LoadFlacFile(file, pathWithExtension.string(), sound, soundData, soundSize); + + return false; + } + bool WriteEntries() { GoTo(DATA_OFFSET); @@ -125,87 +247,23 @@ public: for (auto& sound : m_sounds) { const auto& soundFilePath = sound.m_file_path; - const auto soundId = sound.m_sound_id; size_t soundSize; 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()) + if (!LoadFileByExtension(soundFilePath, sound, soundData, soundSize) && !GuessFilenameAndLoadFile(soundFilePath, sound, soundData, soundSize)) { - WavHeader header{}; - wavFile.m_stream->read(reinterpret_cast(&header), sizeof(WavHeader)); - - soundSize = static_cast(wavFile.m_length - sizeof(WavHeader)); - const auto frameCount = soundSize / (header.formatChunk.nChannels * (header.formatChunk.wBitsPerSample / 8)); - const auto frameRateIndex = INDEX_FOR_FRAMERATE[header.formatChunk.nSamplesPerSec]; - - SoundAssetBankEntry entry{ - soundId, - soundSize, - static_cast(m_current_offset), - frameCount, - frameRateIndex, - static_cast(header.formatChunk.nChannels), - sound.m_looping, - 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 + ".flac"); - if (flacFile.IsOpen()) - { - soundSize = static_cast(flacFile.m_length); - - soundData = std::make_unique(soundSize); - flacFile.m_stream->read(soundData.get(), soundSize); - - flac::FlacMetaData metaData; - if (flac::GetFlacMetaData(soundData.get(), soundSize, metaData)) - { - const auto frameRateIndex = INDEX_FOR_FRAMERATE[metaData.m_sample_rate]; - SoundAssetBankEntry entry{ - soundId, - soundSize, - static_cast(m_current_offset), - static_cast(metaData.m_total_samples), - frameRateIndex, - metaData.m_number_of_channels, - sound.m_looping, - 8, - }; - - m_entries.push_back(entry); - } - else - { - std::cerr << "Unable to decode .flac file for sound " << soundFilePath << "\n"; - return false; - } - } - else - { - std::cerr << "Unable to find a compatible file for sound " << soundFilePath << "\n"; - return false; - } + std::cerr << std::format("Unable to find a compatible file for sound {}\n", soundFilePath); + return false; } const auto lastEntry = m_entries.rbegin(); 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!\n"; + std::cout << std::format("WARNING: Loaded sound \"{}\" should have a framerate of 48000 but doesn't. This sound may not work on all games!\n", + soundFilePath); } - // calculate checksum SoundAssetBankChecksum checksum{}; const auto md5Crypt = Crypto::CreateMD5(); @@ -278,30 +336,6 @@ public: } private: - class SoundBankEntryInfo - { - 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; std::ostream& m_stream; ISearchPath* m_asset_search_path; diff --git a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp index 532cb3fa..d37b4905 100644 --- a/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp +++ b/src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp @@ -8,6 +8,7 @@ #include "nlohmann/json.hpp" #include +#include #include #include @@ -124,7 +125,7 @@ namespace { std::unordered_map result; for (auto i = 0u; i < std::extent_v; i++) - result.emplace(T6::Common::SND_HashName(SOUND_CURVES[i].data()), SOUND_CURVES[i]); + result.emplace(T6::Common::SND_HashName(SOUND_CURVES[i]), SOUND_CURVES[i]); return result; } @@ -195,7 +196,7 @@ class AssetDumperSndBank::Internal stream.NextRow(); } - static const char* FindNameForDuck(unsigned int id, const SndBank* bank) + static const char* FindNameForDuck(const unsigned int id, const SndBank* bank) { for (auto i = 0u; i < bank->duckCount; i++) { @@ -208,13 +209,30 @@ class AssetDumperSndBank::Internal return ""; } - static void WriteAliasToFile(CsvOutputStream& stream, const SndAlias* alias, const SndBank* bank) + static const char* ExtensionForSndFormat(const snd_asset_format format) + { + switch (format) + { + case SND_ASSET_FORMAT_PCMS16: + return ".wav"; + + case SND_ASSET_FORMAT_FLAC: + return ".flac"; + + default: + assert(false); + return ""; + } + } + + static void WriteAliasToFile(CsvOutputStream& stream, const SndAlias* alias, const std::optional maybeFormat, const SndBank* bank) { // name stream.WriteColumn(alias->name); // file - stream.WriteColumn(alias->assetFileName ? alias->assetFileName : ""); + const auto* extension = maybeFormat ? ExtensionForSndFormat(*maybeFormat) : ""; + stream.WriteColumn(alias->assetFileName ? std::format("{}{}", alias->assetFileName, extension) : ""); // template stream.WriteColumn(""); @@ -255,7 +273,7 @@ class AssetDumperSndBank::Internal // volume_min_falloff_curve stream.WriteColumn(SOUND_CURVES[alias->flags.volumeMinFalloffCurve]); - // reverb_min_falloff_curve" + // reverb_min_falloff_curve stream.WriteColumn(SOUND_CURVES[alias->flags.reverbMinFalloffCurve]); // limit_count @@ -303,103 +321,103 @@ class AssetDumperSndBank::Internal // randomize_type stream.WriteColumn(SOUND_RANDOMIZE_TYPES[std::min(alias->flags.randomizeType, 3u)]); - // probability", + // probability stream.WriteColumn(std::to_string(alias->probability)); - // start_delay", + // start_delay stream.WriteColumn(std::to_string(alias->startDelay)); - // reverb_send", + // reverb_send stream.WriteColumn(std::to_string(alias->reverbSend)); - // duck", + // duck stream.WriteColumn(FindNameForDuck(alias->duck, bank)); - // duck_group", + // duck_group stream.WriteColumn(SOUND_DUCK_GROUPS[alias->duckGroup]); - // pan", + // pan stream.WriteColumn(alias->flags.panType == SA_PAN_2D ? "2d" : "3d"); - // center_send", + // center_send stream.WriteColumn(std::to_string(alias->centerSend)); - // envelop_min", + // envelop_min stream.WriteColumn(std::to_string(alias->envelopMin)); - // envelop_max", + // envelop_max stream.WriteColumn(std::to_string(alias->envelopMax)); - // envelop_percentage", + // envelop_percentage stream.WriteColumn(std::to_string(alias->envelopPercentage)); - // occlusion_level", + // occlusion_level stream.WriteColumn(std::to_string(alias->occlusionLevel)); - // occlusion_wet_dry", + // occlusion_wet_dry stream.WriteColumn(""); - // is_big", + // is_big stream.WriteColumn(alias->flags.isBig ? "yes" : "no"); - // distance_lpf" + // distance_lpf stream.WriteColumn(alias->flags.distanceLpf ? "yes" : "no"); - // move_type", + // move_type stream.WriteColumn(SOUND_MOVE_TYPES[alias->flags.fluxType]); - // move_time", + // move_time stream.WriteColumn(std::to_string(alias->fluxTime)); - // real_delay", + // real_delay stream.WriteColumn(""); - // subtitle", + // subtitle stream.WriteColumn((alias->subtitle && *alias->subtitle) ? alias->subtitle : ""); - // mature", + // mature stream.WriteColumn(""); - // doppler", + // doppler stream.WriteColumn(alias->flags.doppler ? "yes" : "no"); - // futz", + // futz stream.WriteColumn(std::to_string(alias->futzPatch)); - // context_type", + // context_type stream.WriteColumn(std::to_string(alias->contextType)); - // context_value", + // context_value stream.WriteColumn(std::to_string(alias->contextValue)); - // compression", + // compression stream.WriteColumn(""); - // timescale", + // timescale stream.WriteColumn(alias->flags.timescale ? "yes" : "no"); - // music", + // music stream.WriteColumn(alias->flags.isMusic ? "yes" : "no"); - // fade_in", + // fade_in stream.WriteColumn(std::to_string(alias->fadeIn)); - // fade_out", + // fade_out stream.WriteColumn(std::to_string(alias->fadeOut)); - // pc_format", + // pc_format stream.WriteColumn(""); - // pause", + // pause stream.WriteColumn(alias->flags.pauseable ? "yes" : "no"); - // stop_on_death", + // stop_on_death stream.WriteColumn(alias->flags.stopOnDeath ? "yes" : "no"); - // bus", + // bus stream.WriteColumn(SOUND_BUS_IDS[alias->flags.busType]); - // snapshot", + // snapshot stream.WriteColumn(""); } @@ -420,7 +438,7 @@ class AssetDumperSndBank::Internal const auto outFile = OpenAssetOutputFile(assetFileName, ".wav"); if (!outFile) { - std::cerr << "Failed to open sound output file: \"" << assetFileName << "\"\n"; + std::cerr << std::format("Failed to open sound output file: \"{}\"\n", assetFileName); return; } @@ -447,7 +465,7 @@ class AssetDumperSndBank::Internal const auto outFile = OpenAssetOutputFile(assetFileName, extension); if (!outFile) { - std::cerr << "Failed to open sound output file: \"" << assetFileName << "\"\n"; + std::cerr << std::format("Failed to open sound output file: \"{}\"\n", assetFileName); return; } @@ -460,7 +478,7 @@ class AssetDumperSndBank::Internal } } - void DumpSndAlias(const SndAlias& alias) const + std::optional DumpSndAlias(const SndAlias& alias) const { const auto soundFile = FindSoundDataInSoundBanks(alias.assetId); if (soundFile.IsOpen()) @@ -484,29 +502,30 @@ class AssetDumperSndBank::Internal case SND_ASSET_FORMAT_WMA: case SND_ASSET_FORMAT_WIIUADPCM: case SND_ASSET_FORMAT_MPC: - std::cerr << "Cannot dump sound (Unsupported sound format " << format << "): \"" << alias.assetFileName << "\"\n"; + std::cerr << std::format("Cannot dump sound (Unknown sound format {}): \"{}\"\n", static_cast(format), alias.assetFileName); break; default: assert(false); - std::cerr << "Cannot dump sound (Unknown sound format " << format << "): \"" << alias.assetFileName << "\"\n"; + std::cerr << std::format("Cannot dump sound (Unknown sound format {}): \"{}\"\n", static_cast(format), alias.assetFileName); break; } + + return format; } - else - { - std::cerr << "Could not find data for sound \"" << alias.assetFileName << "\"\n"; - } + + std::cerr << std::format("Could not find data for sound \"{}\"\n", alias.assetFileName); + return {}; } void DumpSndBankAliases(const SndBank* sndBank) const { - std::unordered_set dumpedAssets; + std::unordered_map> dumpedAssets; - const auto outFile = OpenAssetOutputFile("soundbank/" + std::string(sndBank->name) + ".aliases", ".csv"); + const auto outFile = OpenAssetOutputFile(std::format("soundbank/{}.aliases", sndBank->name), ".csv"); if (!outFile) { - std::cerr << "Failed to open sound alias output file: \"" << sndBank->name << "\"\n"; + std::cerr << std::format("Failed to open sound alias output file: \"\"\n", sndBank->name); return; } @@ -520,15 +539,24 @@ class AssetDumperSndBank::Internal for (auto j = 0; j < aliasList.count; j++) { const auto& alias = aliasList.head[j]; + std::optional maybeFormat; - WriteAliasToFile(csvStream, &alias, sndBank); - csvStream.NextRow(); - - if (alias.assetId && alias.assetFileName && dumpedAssets.find(alias.assetId) == dumpedAssets.end()) + if (alias.assetId && alias.assetFileName) { - DumpSndAlias(alias); - dumpedAssets.emplace(alias.assetId); + const auto previouslyDeterminedFormat = dumpedAssets.find(alias.assetId); + if (previouslyDeterminedFormat == dumpedAssets.end()) + { + maybeFormat = DumpSndAlias(alias); + dumpedAssets[alias.assetId] = maybeFormat; + } + else + { + maybeFormat = previouslyDeterminedFormat->second; + } } + + WriteAliasToFile(csvStream, &alias, maybeFormat, sndBank); + csvStream.NextRow(); } } } @@ -536,14 +564,12 @@ class AssetDumperSndBank::Internal void DumpSoundRadverb(const SndBank* sndBank) const { if (sndBank->radverbCount <= 0) - { return; - } - const auto outFile = OpenAssetOutputFile("soundbank/" + std::string(sndBank->name) + ".reverbs", ".csv"); + const auto outFile = OpenAssetOutputFile(std::format("soundbank/{}.reverbs", sndBank->name), ".csv"); if (!outFile) { - std::cerr << "Failed to open sound reverb output file: \"" << sndBank->name << "\"\n"; + std::cerr << std::format("Failed to open sound reverb output file: \"{}\"\n", sndBank->name); return; } @@ -577,14 +603,12 @@ class AssetDumperSndBank::Internal void DumpSoundDucks(const SndBank* sndBank) const { if (sndBank->duckCount <= 0) - { return; - } - const auto outFile = OpenAssetOutputFile("soundbank/" + std::string(sndBank->name) + ".ducklist", ".csv"); + const auto outFile = OpenAssetOutputFile(std::format("soundbank/{}.ducklist", sndBank->name), ".csv"); if (!outFile) { - std::cerr << "Failed to open sound reverb output file: \"" << sndBank->name << "\"\n"; + std::cerr << std::format("Failed to open sound reverb output file: \"{}\"\n", sndBank->name); return; } @@ -598,10 +622,10 @@ class AssetDumperSndBank::Internal csvStream.WriteColumn(duck.name); csvStream.NextRow(); - const auto duckFile = OpenAssetOutputFile("soundbank/ducks/" + std::string(duck.name), ".duk"); + const auto duckFile = OpenAssetOutputFile(std::format("soundbank/ducks/{}", duck.name), ".duk"); if (!outFile) { - std::cerr << "Failed to open sound duck output file: \"" << duck.name << "\"\n"; + std::cerr << std::format("Failed to open sound duck output file: \"{}\"\n", duck.name); return; } @@ -628,12 +652,12 @@ class AssetDumperSndBank::Internal } auto values = std::vector{}; - for (auto i = 0u; i < 32u; i++) + for (auto j = 0u; j < 32u; j++) { values.push_back({ - {"duckGroup", SOUND_DUCK_GROUPS[i]}, - {"attenuation", duck.attenuation[i] }, - {"filter", duck.filter[i] } + {"duckGroup", SOUND_DUCK_GROUPS[j]}, + {"attenuation", duck.attenuation[j] }, + {"filter", duck.filter[j] } }); }