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 {