Merge pull request #264 from Jbleezy/main

Various soundbank fixes
This commit is contained in:
Jan 2024-09-19 08:14:53 +02:00 committed by GitHub
commit 53cde27d7f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 321 additions and 217 deletions

View File

@ -6182,7 +6182,43 @@ namespace T6
SA_LOADED = 0x1, SA_LOADED = 0x1,
SA_STREAMED = 0x2, SA_STREAMED = 0x2,
SA_PRIMED = 0x3, 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 struct SndAliasFlags
@ -6248,16 +6284,16 @@ namespace T6
int16_t fadeIn; int16_t fadeIn;
int16_t fadeOut; int16_t fadeOut;
int16_t dopplerScale; int16_t dopplerScale;
char minPriorityThreshold; uint8_t minPriorityThreshold;
char maxPriorityThreshold; uint8_t maxPriorityThreshold;
char probability; uint8_t probability;
char occlusionLevel; uint8_t occlusionLevel;
char minPriority; uint8_t minPriority;
char maxPriority; uint8_t maxPriority;
char pan; uint8_t pan;
char limitCount; uint8_t limitCount;
char entityLimitCount; uint8_t entityLimitCount;
char duckGroup; uint8_t duckGroup;
}; };
#ifndef __zonecodegenerator #ifndef __zonecodegenerator

View File

@ -1,9 +1,14 @@
#pragma once #pragma once
#include <string>
#include "Game/T6/T6.h"
#include <type_traits>
namespace T6 namespace T6
{ {
inline const std::string SOUND_GROUPS[]{ // From SndDriverGlobals
inline constexpr const char* SOUND_GROUPS[]{
// clang-format off
"grp_reference", "grp_reference",
"grp_master", "grp_master",
"grp_wpn_lfe", "grp_wpn_lfe",
@ -30,10 +35,11 @@ namespace T6
"grp_air", "grp_air",
"grp_bink", "grp_bink",
"grp_announcer", "grp_announcer",
"", // clang-format on
}; };
inline const std::string SOUND_CURVES[]{ // From SndDriverGlobals
inline constexpr const char* SOUND_CURVES[]{
"default", "default",
"defaultmin", "defaultmin",
"allon", "allon",
@ -51,10 +57,10 @@ namespace T6
"cos", "cos",
"rev60", "rev60",
"rev65", "rev65",
"",
}; };
inline const std::string SOUND_DUCK_GROUPS[]{ // From SndDriverGlobals
inline constexpr const char* SOUND_DUCK_GROUPS[]{
"snp_alerts_gameplay", "snp_alerts_gameplay",
"snp_ambience", "snp_ambience",
"snp_claw", "snp_claw",
@ -89,14 +95,16 @@ namespace T6
"snp_x3", "snp_x3",
}; };
inline const std::string SOUND_LIMIT_TYPES[]{ inline constexpr const char* SOUND_LIMIT_TYPES[]{
"none", "none",
"oldest", "oldest",
"reject", "reject",
"priority", "priority",
}; };
static_assert(std::extent_v<decltype(SOUND_LIMIT_TYPES)> == SND_LIMIT_COUNT);
inline const std::string SOUND_MOVE_TYPES[]{ // From executable
inline constexpr const char* SOUND_MOVE_TYPES[]{
"none", "none",
"left_player", "left_player",
"center_player", "center_player",
@ -107,31 +115,32 @@ namespace T6
"right_shot", "right_shot",
}; };
inline const std::string SOUND_LOAD_TYPES[]{ inline constexpr const char* SOUND_LOAD_TYPES[]{
"unknown", "unknown",
"loaded", "loaded",
"streamed", "streamed",
"primed", "primed",
}; };
static_assert(std::extent_v<decltype(SOUND_LOAD_TYPES)> == SA_COUNT);
inline const std::string SOUND_BUS_IDS[]{ inline constexpr const char* SOUND_BUS_IDS[]{
"bus_reverb", "bus_reverb",
"bus_fx", "bus_fx",
"bus_voice", "bus_voice",
"bus_pfutz", "bus_pfutz",
"bus_hdrfx", "bus_hdrfx",
"bus_ui", "bus_ui",
"bus_reference",
"bus_music", "bus_music",
"bus_movie", "bus_movie",
"bus_reference", "bus_reference",
"",
}; };
static_assert(std::extent_v<decltype(SOUND_BUS_IDS)> == SND_BUS_COUNT);
inline const std::string SOUND_RANDOMIZE_TYPES[]{ // From executable
inline constexpr const char* SOUND_RANDOMIZE_TYPES[]{
"",
"volume", "volume",
"pitch", "pitch",
"variant", "variant",
"",
}; };
} // namespace T6 } // namespace T6

View File

@ -72,7 +72,7 @@ bool AssetLoaderSoundBank::CanLoadFromRaw() const
return true; 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()) if (value.empty())
return 0; return 0;
@ -141,33 +141,33 @@ bool LoadSoundAlias(MemoryManager* memory, SndAlias* alias, const ParsedCsvRow&
alias->duck = Common::SND_HashName(row.GetValue("duck").data()); alias->duck = Common::SND_HashName(row.GetValue("duck").data());
alias->volMin = row.GetValueInt<uint16_t>("vol_min"); alias->volMin = row.GetValueInt<decltype(alias->volMin)>("vol_min");
alias->volMax = row.GetValueInt<uint16_t>("vol_max"); alias->volMax = row.GetValueInt<decltype(alias->volMax)>("vol_max");
alias->distMin = row.GetValueInt<uint16_t>("dist_min"); alias->distMin = row.GetValueInt<decltype(alias->distMin)>("dist_min");
alias->distMax = row.GetValueInt<uint16_t>("dist_max"); alias->distMax = row.GetValueInt<decltype(alias->distMax)>("dist_max");
alias->distReverbMax = row.GetValueInt<uint16_t>("dist_reverb_max"); alias->distReverbMax = row.GetValueInt<decltype(alias->distReverbMax)>("dist_reverb_max");
alias->limitCount = row.GetValueInt<uint8_t>("limit_count"); alias->limitCount = row.GetValueInt<decltype(alias->limitCount)>("limit_count");
alias->entityLimitCount = row.GetValueInt<uint8_t>("entity_limit_count"); alias->entityLimitCount = row.GetValueInt<decltype(alias->entityLimitCount)>("entity_limit_count");
alias->pitchMin = row.GetValueInt<uint16_t>("pitch_min"); alias->pitchMin = row.GetValueInt<decltype(alias->pitchMin)>("pitch_min");
alias->pitchMax = row.GetValueInt<uint16_t>("pitch_max"); alias->pitchMax = row.GetValueInt<decltype(alias->pitchMax)>("pitch_max");
alias->minPriority = row.GetValueInt<uint8_t>("min_priority"); alias->minPriority = row.GetValueInt<decltype(alias->minPriority)>("min_priority");
alias->maxPriority = row.GetValueInt<uint8_t>("max_priority"); alias->maxPriority = row.GetValueInt<decltype(alias->maxPriority)>("max_priority");
alias->minPriorityThreshold = row.GetValueInt<uint8_t>("min_priority_threshold"); alias->minPriorityThreshold = row.GetValueInt<decltype(alias->minPriorityThreshold)>("min_priority_threshold");
alias->maxPriorityThreshold = row.GetValueInt<uint8_t>("max_priority_threshold"); alias->maxPriorityThreshold = row.GetValueInt<decltype(alias->maxPriorityThreshold)>("max_priority_threshold");
alias->probability = row.GetValueInt<uint8_t>("probability"); alias->probability = row.GetValueInt<decltype(alias->probability)>("probability");
alias->startDelay = row.GetValueInt<uint16_t>("start_delay"); alias->startDelay = row.GetValueInt<decltype(alias->startDelay)>("start_delay");
alias->reverbSend = row.GetValueInt<uint16_t>("reverb_send"); alias->reverbSend = row.GetValueInt<decltype(alias->reverbSend)>("reverb_send");
alias->centerSend = row.GetValueInt<uint16_t>("center_send"); alias->centerSend = row.GetValueInt<decltype(alias->centerSend)>("center_send");
alias->envelopMin = row.GetValueInt<uint16_t>("envelop_min"); alias->envelopMin = row.GetValueInt<decltype(alias->envelopMin)>("envelop_min");
alias->envelopMax = row.GetValueInt<uint16_t>("envelop_max"); alias->envelopMax = row.GetValueInt<decltype(alias->envelopMax)>("envelop_max");
alias->envelopPercentage = row.GetValueInt<uint16_t>("envelop_percentage"); alias->envelopPercentage = row.GetValueInt<decltype(alias->envelopPercentage)>("envelop_percentage");
alias->occlusionLevel = row.GetValueInt<uint8_t>("occlusion_level"); alias->occlusionLevel = row.GetValueInt<decltype(alias->occlusionLevel)>("occlusion_level");
alias->fluxTime = row.GetValueInt<uint16_t>("move_time"); alias->fluxTime = row.GetValueInt<decltype(alias->fluxTime)>("move_time");
alias->futzPatch = row.GetValueInt<unsigned int>("futz"); alias->futzPatch = row.GetValueInt<decltype(alias->futzPatch)>("futz");
alias->contextType = row.GetValueInt<unsigned int>("context_type"); alias->contextType = row.GetValueInt<decltype(alias->contextType)>("context_type");
alias->contextValue = row.GetValueInt<unsigned int>("context_value"); alias->contextValue = row.GetValueInt<decltype(alias->contextValue)>("context_value");
alias->fadeIn = row.GetValueInt<int16_t>("fade_in"); alias->fadeIn = row.GetValueInt<decltype(alias->fadeIn)>("fade_in");
alias->fadeOut = row.GetValueInt<int16_t>("fade_out"); alias->fadeOut = row.GetValueInt<decltype(alias->fadeOut)>("fade_out");
alias->flags.looping = row.GetValue("loop") == "looping"; alias->flags.looping = row.GetValue("loop") == "looping";
alias->flags.panType = row.GetValue("pan") == "3d"; 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.pauseable = row.GetValue("pause") == "yes";
alias->flags.stopOnDeath = row.GetValue("stop_on_death") == "yes"; alias->flags.stopOnDeath = row.GetValue("stop_on_death") == "yes";
alias->duckGroup = static_cast<char>(GetValueIndex(row.GetValue("duck_group"), SOUND_DUCK_GROUPS, std::extent_v<decltype(SOUND_DUCK_GROUPS)>)); alias->duckGroup =
static_cast<decltype(alias->duckGroup)>(GetValueIndex(row.GetValue("duck_group"), SOUND_DUCK_GROUPS, std::extent_v<decltype(SOUND_DUCK_GROUPS)>));
alias->flags.volumeGroup = GetValueIndex(row.GetValue("group"), SOUND_GROUPS, std::extent_v<decltype(SOUND_GROUPS)>); alias->flags.volumeGroup = GetValueIndex(row.GetValue("group"), SOUND_GROUPS, std::extent_v<decltype(SOUND_GROUPS)>);
alias->flags.fluxType = GetValueIndex(row.GetValue("move_type"), SOUND_MOVE_TYPES, std::extent_v<decltype(SOUND_MOVE_TYPES)>); alias->flags.fluxType = GetValueIndex(row.GetValue("move_type"), SOUND_MOVE_TYPES, std::extent_v<decltype(SOUND_MOVE_TYPES)>);
alias->flags.loadType = GetValueIndex(row.GetValue("type"), SOUND_LOAD_TYPES, std::extent_v<decltype(SOUND_LOAD_TYPES)>); alias->flags.loadType = GetValueIndex(row.GetValue("type"), SOUND_LOAD_TYPES, std::extent_v<decltype(SOUND_LOAD_TYPES)>);

View File

@ -5,12 +5,16 @@
#include "Sound/FlacDecoder.h" #include "Sound/FlacDecoder.h"
#include "Sound/WavTypes.h" #include "Sound/WavTypes.h"
#include "Utils/FileUtils.h" #include "Utils/FileUtils.h"
#include "Utils/StringUtils.h"
#include <cstring> #include <cstring>
#include <filesystem> #include <filesystem>
#include <format>
#include <iostream> #include <iostream>
#include <unordered_map> #include <unordered_map>
namespace fs = std::filesystem;
std::unordered_map<unsigned int, unsigned char> INDEX_FOR_FRAMERATE{ std::unordered_map<unsigned int, unsigned char> INDEX_FOR_FRAMERATE{
{8000, 0}, {8000, 0},
{12000, 1}, {12000, 1},
@ -32,6 +36,30 @@ class SoundBankWriterImpl : public SoundBankWriter
inline static const std::string PAD_DATA = std::string(16, '\x00'); 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: public:
explicit SoundBankWriterImpl(std::string fileName, std::ostream& stream, ISearchPath* assetSearchPath) explicit SoundBankWriterImpl(std::string fileName, std::ostream& stream, ISearchPath* assetSearchPath)
: m_file_name(std::move(fileName)), : m_file_name(std::move(fileName)),
@ -118,31 +146,17 @@ public:
Write(&header, sizeof(header)); Write(&header, sizeof(header));
} }
bool WriteEntries() bool LoadWavFile(const SearchPathOpenFile& file, const SoundBankEntryInfo& sound, std::unique_ptr<char[]>& soundData, size_t& soundSize)
{
GoTo(DATA_OFFSET);
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<char[]> 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{}; WavHeader header{};
wavFile.m_stream->read(reinterpret_cast<char*>(&header), sizeof(WavHeader)); file.m_stream->read(reinterpret_cast<char*>(&header), sizeof(WavHeader));
soundSize = static_cast<size_t>(wavFile.m_length - sizeof(WavHeader)); soundSize = static_cast<size_t>(file.m_length - sizeof(WavHeader));
const auto frameCount = soundSize / (header.formatChunk.nChannels * (header.formatChunk.wBitsPerSample / 8)); const auto frameCount = soundSize / (header.formatChunk.nChannels * (header.formatChunk.wBitsPerSample / 8));
const auto frameRateIndex = INDEX_FOR_FRAMERATE[header.formatChunk.nSamplesPerSec]; const auto frameRateIndex = INDEX_FOR_FRAMERATE[header.formatChunk.nSamplesPerSec];
SoundAssetBankEntry entry{ SoundAssetBankEntry entry{
soundId, sound.m_sound_id,
soundSize, soundSize,
static_cast<size_t>(m_current_offset), static_cast<size_t>(m_current_offset),
frameCount, frameCount,
@ -155,25 +169,25 @@ public:
m_entries.push_back(entry); m_entries.push_back(entry);
soundData = std::make_unique<char[]>(soundSize); soundData = std::make_unique<char[]>(soundSize);
wavFile.m_stream->read(soundData.get(), soundSize); file.m_stream->read(soundData.get(), soundSize);
return true;
} }
else
bool LoadFlacFile(
const SearchPathOpenFile& file, const std::string& filePath, const SoundBankEntryInfo& sound, std::unique_ptr<char[]>& soundData, size_t& soundSize)
{ {
// if there is no wav file, try flac file soundSize = static_cast<size_t>(file.m_length);
const auto flacFile = m_asset_search_path->Open(soundFilePath + ".flac");
if (flacFile.IsOpen())
{
soundSize = static_cast<size_t>(flacFile.m_length);
soundData = std::make_unique<char[]>(soundSize); soundData = std::make_unique<char[]>(soundSize);
flacFile.m_stream->read(soundData.get(), soundSize); file.m_stream->read(soundData.get(), soundSize);
flac::FlacMetaData metaData; flac::FlacMetaData metaData;
if (flac::GetFlacMetaData(soundData.get(), soundSize, metaData)) if (flac::GetFlacMetaData(soundData.get(), soundSize, metaData))
{ {
const auto frameRateIndex = INDEX_FOR_FRAMERATE[metaData.m_sample_rate]; const auto frameRateIndex = INDEX_FOR_FRAMERATE[metaData.m_sample_rate];
SoundAssetBankEntry entry{ SoundAssetBankEntry entry{
soundId, sound.m_sound_id,
soundSize, soundSize,
static_cast<size_t>(m_current_offset), static_cast<size_t>(m_current_offset),
static_cast<unsigned>(metaData.m_total_samples), static_cast<unsigned>(metaData.m_total_samples),
@ -184,28 +198,72 @@ public:
}; };
m_entries.push_back(entry); m_entries.push_back(entry);
return true;
} }
else
{ std::cerr << std::format("Unable to decode .flac file for sound {}\n", filePath);
std::cerr << "Unable to decode .flac file for sound " << soundFilePath << "\n";
return false; return false;
} }
}
else bool LoadFileByExtension(const std::string& filePath, const SoundBankEntryInfo& sound, std::unique_ptr<char[]>& soundData, size_t& soundSize)
{ {
std::cerr << "Unable to find a compatible file for sound " << soundFilePath << "\n"; 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; return false;
} }
bool GuessFilenameAndLoadFile(const std::string& filePath, const SoundBankEntryInfo& sound, std::unique_ptr<char[]>& 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);
for (auto& sound : m_sounds)
{
const auto& soundFilePath = sound.m_file_path;
size_t soundSize;
std::unique_ptr<char[]> soundData;
if (!LoadFileByExtension(soundFilePath, sound, soundData, soundSize) && !GuessFilenameAndLoadFile(soundFilePath, sound, soundData, soundSize))
{
std::cerr << std::format("Unable to find a compatible file for sound {}\n", soundFilePath);
return false;
} }
const auto lastEntry = m_entries.rbegin(); const auto lastEntry = m_entries.rbegin();
if (!sound.m_streamed && lastEntry->frameRateIndex != 6) if (!sound.m_streamed && lastEntry->frameRateIndex != 6)
{ {
std::cout << "WARNING: Loaded sound \"" << soundFilePath 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",
<< "\" should have a framerate of 48000 but doesn't. This sound may not work on all games!\n"; soundFilePath);
} }
// calculate checksum
SoundAssetBankChecksum checksum{}; SoundAssetBankChecksum checksum{};
const auto md5Crypt = Crypto::CreateMD5(); const auto md5Crypt = Crypto::CreateMD5();
@ -278,30 +336,6 @@ public:
} }
private: 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::string m_file_name;
std::ostream& m_stream; std::ostream& m_stream;
ISearchPath* m_asset_search_path; ISearchPath* m_asset_search_path;

View File

@ -8,6 +8,7 @@
#include "nlohmann/json.hpp" #include "nlohmann/json.hpp"
#include <filesystem> #include <filesystem>
#include <format>
#include <fstream> #include <fstream>
#include <unordered_set> #include <unordered_set>
@ -124,7 +125,7 @@ namespace
{ {
std::unordered_map<unsigned int, std::string> result; std::unordered_map<unsigned int, std::string> result;
for (auto i = 0u; i < std::extent_v<decltype(SOUND_CURVES)>; i++) for (auto i = 0u; i < std::extent_v<decltype(SOUND_CURVES)>; 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; return result;
} }
@ -195,7 +196,7 @@ class AssetDumperSndBank::Internal
stream.NextRow(); 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++) for (auto i = 0u; i < bank->duckCount; i++)
{ {
@ -208,13 +209,30 @@ class AssetDumperSndBank::Internal
return ""; 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<snd_asset_format> maybeFormat, const SndBank* bank)
{ {
// name // name
stream.WriteColumn(alias->name); stream.WriteColumn(alias->name);
// file // file
stream.WriteColumn(alias->assetFileName ? alias->assetFileName : ""); const auto* extension = maybeFormat ? ExtensionForSndFormat(*maybeFormat) : "";
stream.WriteColumn(alias->assetFileName ? std::format("{}{}", alias->assetFileName, extension) : "");
// template // template
stream.WriteColumn(""); stream.WriteColumn("");
@ -255,7 +273,7 @@ class AssetDumperSndBank::Internal
// volume_min_falloff_curve // volume_min_falloff_curve
stream.WriteColumn(SOUND_CURVES[alias->flags.volumeMinFalloffCurve]); stream.WriteColumn(SOUND_CURVES[alias->flags.volumeMinFalloffCurve]);
// reverb_min_falloff_curve" // reverb_min_falloff_curve
stream.WriteColumn(SOUND_CURVES[alias->flags.reverbMinFalloffCurve]); stream.WriteColumn(SOUND_CURVES[alias->flags.reverbMinFalloffCurve]);
// limit_count // limit_count
@ -303,103 +321,103 @@ class AssetDumperSndBank::Internal
// randomize_type // randomize_type
stream.WriteColumn(SOUND_RANDOMIZE_TYPES[std::min(alias->flags.randomizeType, 3u)]); stream.WriteColumn(SOUND_RANDOMIZE_TYPES[std::min(alias->flags.randomizeType, 3u)]);
// probability", // probability
stream.WriteColumn(std::to_string(alias->probability)); stream.WriteColumn(std::to_string(alias->probability));
// start_delay", // start_delay
stream.WriteColumn(std::to_string(alias->startDelay)); stream.WriteColumn(std::to_string(alias->startDelay));
// reverb_send", // reverb_send
stream.WriteColumn(std::to_string(alias->reverbSend)); stream.WriteColumn(std::to_string(alias->reverbSend));
// duck", // duck
stream.WriteColumn(FindNameForDuck(alias->duck, bank)); stream.WriteColumn(FindNameForDuck(alias->duck, bank));
// duck_group", // duck_group
stream.WriteColumn(SOUND_DUCK_GROUPS[alias->duckGroup]); stream.WriteColumn(SOUND_DUCK_GROUPS[alias->duckGroup]);
// pan", // pan
stream.WriteColumn(alias->flags.panType == SA_PAN_2D ? "2d" : "3d"); stream.WriteColumn(alias->flags.panType == SA_PAN_2D ? "2d" : "3d");
// center_send", // center_send
stream.WriteColumn(std::to_string(alias->centerSend)); stream.WriteColumn(std::to_string(alias->centerSend));
// envelop_min", // envelop_min
stream.WriteColumn(std::to_string(alias->envelopMin)); stream.WriteColumn(std::to_string(alias->envelopMin));
// envelop_max", // envelop_max
stream.WriteColumn(std::to_string(alias->envelopMax)); stream.WriteColumn(std::to_string(alias->envelopMax));
// envelop_percentage", // envelop_percentage
stream.WriteColumn(std::to_string(alias->envelopPercentage)); stream.WriteColumn(std::to_string(alias->envelopPercentage));
// occlusion_level", // occlusion_level
stream.WriteColumn(std::to_string(alias->occlusionLevel)); stream.WriteColumn(std::to_string(alias->occlusionLevel));
// occlusion_wet_dry", // occlusion_wet_dry
stream.WriteColumn(""); stream.WriteColumn("");
// is_big", // is_big
stream.WriteColumn(alias->flags.isBig ? "yes" : "no"); stream.WriteColumn(alias->flags.isBig ? "yes" : "no");
// distance_lpf" // distance_lpf
stream.WriteColumn(alias->flags.distanceLpf ? "yes" : "no"); stream.WriteColumn(alias->flags.distanceLpf ? "yes" : "no");
// move_type", // move_type
stream.WriteColumn(SOUND_MOVE_TYPES[alias->flags.fluxType]); stream.WriteColumn(SOUND_MOVE_TYPES[alias->flags.fluxType]);
// move_time", // move_time
stream.WriteColumn(std::to_string(alias->fluxTime)); stream.WriteColumn(std::to_string(alias->fluxTime));
// real_delay", // real_delay
stream.WriteColumn(""); stream.WriteColumn("");
// subtitle", // subtitle
stream.WriteColumn((alias->subtitle && *alias->subtitle) ? alias->subtitle : ""); stream.WriteColumn((alias->subtitle && *alias->subtitle) ? alias->subtitle : "");
// mature", // mature
stream.WriteColumn(""); stream.WriteColumn("");
// doppler", // doppler
stream.WriteColumn(alias->flags.doppler ? "yes" : "no"); stream.WriteColumn(alias->flags.doppler ? "yes" : "no");
// futz", // futz
stream.WriteColumn(std::to_string(alias->futzPatch)); stream.WriteColumn(std::to_string(alias->futzPatch));
// context_type", // context_type
stream.WriteColumn(std::to_string(alias->contextType)); stream.WriteColumn(std::to_string(alias->contextType));
// context_value", // context_value
stream.WriteColumn(std::to_string(alias->contextValue)); stream.WriteColumn(std::to_string(alias->contextValue));
// compression", // compression
stream.WriteColumn(""); stream.WriteColumn("");
// timescale", // timescale
stream.WriteColumn(alias->flags.timescale ? "yes" : "no"); stream.WriteColumn(alias->flags.timescale ? "yes" : "no");
// music", // music
stream.WriteColumn(alias->flags.isMusic ? "yes" : "no"); stream.WriteColumn(alias->flags.isMusic ? "yes" : "no");
// fade_in", // fade_in
stream.WriteColumn(std::to_string(alias->fadeIn)); stream.WriteColumn(std::to_string(alias->fadeIn));
// fade_out", // fade_out
stream.WriteColumn(std::to_string(alias->fadeOut)); stream.WriteColumn(std::to_string(alias->fadeOut));
// pc_format", // pc_format
stream.WriteColumn(""); stream.WriteColumn("");
// pause", // pause
stream.WriteColumn(alias->flags.pauseable ? "yes" : "no"); stream.WriteColumn(alias->flags.pauseable ? "yes" : "no");
// stop_on_death", // stop_on_death
stream.WriteColumn(alias->flags.stopOnDeath ? "yes" : "no"); stream.WriteColumn(alias->flags.stopOnDeath ? "yes" : "no");
// bus", // bus
stream.WriteColumn(SOUND_BUS_IDS[alias->flags.busType]); stream.WriteColumn(SOUND_BUS_IDS[alias->flags.busType]);
// snapshot", // snapshot
stream.WriteColumn(""); stream.WriteColumn("");
} }
@ -420,7 +438,7 @@ class AssetDumperSndBank::Internal
const auto outFile = OpenAssetOutputFile(assetFileName, ".wav"); const auto outFile = OpenAssetOutputFile(assetFileName, ".wav");
if (!outFile) 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; return;
} }
@ -447,7 +465,7 @@ class AssetDumperSndBank::Internal
const auto outFile = OpenAssetOutputFile(assetFileName, extension); const auto outFile = OpenAssetOutputFile(assetFileName, extension);
if (!outFile) 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; return;
} }
@ -460,7 +478,7 @@ class AssetDumperSndBank::Internal
} }
} }
void DumpSndAlias(const SndAlias& alias) const std::optional<snd_asset_format> DumpSndAlias(const SndAlias& alias) const
{ {
const auto soundFile = FindSoundDataInSoundBanks(alias.assetId); const auto soundFile = FindSoundDataInSoundBanks(alias.assetId);
if (soundFile.IsOpen()) if (soundFile.IsOpen())
@ -484,29 +502,30 @@ class AssetDumperSndBank::Internal
case SND_ASSET_FORMAT_WMA: case SND_ASSET_FORMAT_WMA:
case SND_ASSET_FORMAT_WIIUADPCM: case SND_ASSET_FORMAT_WIIUADPCM:
case SND_ASSET_FORMAT_MPC: 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<unsigned>(format), alias.assetFileName);
break; break;
default: default:
assert(false); 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<unsigned>(format), alias.assetFileName);
break; break;
} }
return format;
} }
else
{ std::cerr << std::format("Could not find data for sound \"{}\"\n", alias.assetFileName);
std::cerr << "Could not find data for sound \"" << alias.assetFileName << "\"\n"; return {};
}
} }
void DumpSndBankAliases(const SndBank* sndBank) const void DumpSndBankAliases(const SndBank* sndBank) const
{ {
std::unordered_set<unsigned> dumpedAssets; std::unordered_map<unsigned int, std::optional<snd_asset_format>> 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) 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; return;
} }
@ -520,15 +539,24 @@ class AssetDumperSndBank::Internal
for (auto j = 0; j < aliasList.count; j++) for (auto j = 0; j < aliasList.count; j++)
{ {
const auto& alias = aliasList.head[j]; const auto& alias = aliasList.head[j];
std::optional<snd_asset_format> maybeFormat;
WriteAliasToFile(csvStream, &alias, sndBank); if (alias.assetId && alias.assetFileName)
csvStream.NextRow();
if (alias.assetId && alias.assetFileName && dumpedAssets.find(alias.assetId) == dumpedAssets.end())
{ {
DumpSndAlias(alias); const auto previouslyDeterminedFormat = dumpedAssets.find(alias.assetId);
dumpedAssets.emplace(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 void DumpSoundRadverb(const SndBank* sndBank) const
{ {
if (sndBank->radverbCount <= 0) if (sndBank->radverbCount <= 0)
{
return; 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) 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; return;
} }
@ -577,14 +603,12 @@ class AssetDumperSndBank::Internal
void DumpSoundDucks(const SndBank* sndBank) const void DumpSoundDucks(const SndBank* sndBank) const
{ {
if (sndBank->duckCount <= 0) if (sndBank->duckCount <= 0)
{
return; 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) 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; return;
} }
@ -598,10 +622,10 @@ class AssetDumperSndBank::Internal
csvStream.WriteColumn(duck.name); csvStream.WriteColumn(duck.name);
csvStream.NextRow(); 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) 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; return;
} }
@ -628,12 +652,12 @@ class AssetDumperSndBank::Internal
} }
auto values = std::vector<nlohmann::json>{}; auto values = std::vector<nlohmann::json>{};
for (auto i = 0u; i < 32u; i++) for (auto j = 0u; j < 32u; j++)
{ {
values.push_back({ values.push_back({
{"duckGroup", SOUND_DUCK_GROUPS[i]}, {"duckGroup", SOUND_DUCK_GROUPS[j]},
{"attenuation", duck.attenuation[i] }, {"attenuation", duck.attenuation[j] },
{"filter", duck.filter[i] } {"filter", duck.filter[j] }
}); });
} }