mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-04-20 00:02:55 +00:00
SoundBankWriter code
This commit is contained in:
parent
acd9fa27fc
commit
a020de6f80
@ -1,10 +1,16 @@
|
|||||||
#include "Crypto.h"
|
#include "Crypto.h"
|
||||||
|
|
||||||
|
#include "Impl/AlgorithmMD5.h"
|
||||||
#include "Impl/AlgorithmRSA.h"
|
#include "Impl/AlgorithmRSA.h"
|
||||||
#include "Impl/AlgorithmSHA1.h"
|
#include "Impl/AlgorithmSHA1.h"
|
||||||
#include "Impl/AlgorithmSHA256.h"
|
#include "Impl/AlgorithmSHA256.h"
|
||||||
#include "Impl/AlgorithmSalsa20.h"
|
#include "Impl/AlgorithmSalsa20.h"
|
||||||
|
|
||||||
|
std::unique_ptr<IHashFunction> Crypto::CreateMD5()
|
||||||
|
{
|
||||||
|
return std::make_unique<AlgorithmMD5>();
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<IHashFunction> Crypto::CreateSHA1()
|
std::unique_ptr<IHashFunction> Crypto::CreateSHA1()
|
||||||
{
|
{
|
||||||
return std::make_unique<AlgorithmSHA1>();
|
return std::make_unique<AlgorithmSHA1>();
|
||||||
|
@ -16,6 +16,8 @@ public:
|
|||||||
RSA_PADDING_PSS,
|
RSA_PADDING_PSS,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static std::unique_ptr<IHashFunction> CreateMD5();
|
||||||
|
|
||||||
static std::unique_ptr<IHashFunction> CreateSHA1();
|
static std::unique_ptr<IHashFunction> CreateSHA1();
|
||||||
static std::unique_ptr<IHashFunction> CreateSHA256();
|
static std::unique_ptr<IHashFunction> CreateSHA256();
|
||||||
|
|
||||||
|
64
src/Crypto/Impl/AlgorithmMD5.cpp
Normal file
64
src/Crypto/Impl/AlgorithmMD5.cpp
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#include "AlgorithmSHA1.h"
|
||||||
|
|
||||||
|
#include "CryptoLibrary.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
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<const uint8_t*>(input), inputSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Finish(void* hashBuffer)
|
||||||
|
{
|
||||||
|
sha1_done(&m_state, static_cast<uint8_t*>(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);
|
||||||
|
}
|
20
src/Crypto/Impl/AlgorithmMD5.h
Normal file
20
src/Crypto/Impl/AlgorithmMD5.h
Normal file
@ -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;
|
||||||
|
};
|
@ -9,6 +9,7 @@
|
|||||||
#include "LinkerSearchPaths.h"
|
#include "LinkerSearchPaths.h"
|
||||||
#include "ObjContainer/IPak/IPakWriter.h"
|
#include "ObjContainer/IPak/IPakWriter.h"
|
||||||
#include "ObjContainer/IWD/IWD.h"
|
#include "ObjContainer/IWD/IWD.h"
|
||||||
|
#include "ObjContainer/SoundBank/SoundBankWriter.h"
|
||||||
#include "ObjLoading.h"
|
#include "ObjLoading.h"
|
||||||
#include "ObjWriting.h"
|
#include "ObjWriting.h"
|
||||||
#include "SearchPath/SearchPaths.h"
|
#include "SearchPath/SearchPaths.h"
|
||||||
@ -419,6 +420,8 @@ class LinkerImpl final : public Linker
|
|||||||
SearchPaths& gdtSearchPaths,
|
SearchPaths& gdtSearchPaths,
|
||||||
SearchPaths& sourceSearchPaths) const
|
SearchPaths& sourceSearchPaths) const
|
||||||
{
|
{
|
||||||
|
SoundBankWriter::OutputPath = fs::path(m_args.GetOutputFolderPathForProject(projectName));
|
||||||
|
|
||||||
const auto zone = CreateZoneForDefinition(targetName, zoneDefinition, &assetSearchPaths, &gdtSearchPaths, &sourceSearchPaths);
|
const auto zone = CreateZoneForDefinition(targetName, zoneDefinition, &assetSearchPaths, &gdtSearchPaths, &sourceSearchPaths);
|
||||||
auto result = zone != nullptr;
|
auto result = zone != nullptr;
|
||||||
if (zone)
|
if (zone)
|
||||||
|
@ -6,7 +6,7 @@ ParsedCsvRow::ParsedCsvRow(std::unordered_map<std::string, size_t>& 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())
|
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";
|
std::cerr << "ERROR: Required column \"" << header << "\" was not found";
|
||||||
else
|
else
|
||||||
std::cerr << "WARNING: Expected column \"" << header << "\" was not found";
|
std::cerr << "WARNING: Expected column \"" << header << "\" was not found";
|
||||||
return nullptr;
|
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& value = this->values.at(this->headers[header]);
|
auto& value = this->values.at(this->headers[header]);
|
||||||
if (required && value.empty())
|
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";
|
||||||
return nullptr;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
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)
|
ParsedCsv::ParsedCsv(const CsvInputStream& inputStream, bool hasHeaders)
|
||||||
{
|
{
|
||||||
std::vector<std::vector<std::string>> csvLines;
|
std::vector<std::vector<std::string>> csvLines;
|
||||||
|
@ -10,17 +10,18 @@ class ParsedCsvRow
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ParsedCsvRow(std::unordered_map<std::string, size_t>& headers, std::vector<std::string>& row);
|
explicit ParsedCsvRow(std::unordered_map<std::string, size_t>& headers, std::vector<std::string>& 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<typename T> T GetValueAs(const std::string& header, bool required = false) const
|
template<typename T> T GetValueInt(const std::string& header, bool required = false) const
|
||||||
{
|
{
|
||||||
const auto& value = this->GetValue(header, required);
|
const auto& value = this->GetValue(header, required);
|
||||||
if (!value.empty())
|
if (!value.empty())
|
||||||
{
|
{
|
||||||
std::istringstream ss(value);
|
std::istringstream ss(value);
|
||||||
T out{};
|
long long out;
|
||||||
ss >> out;
|
ss >> out;
|
||||||
return out;
|
return static_cast<T>(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
@ -28,3 +28,20 @@ struct WavFormatChunkPcm
|
|||||||
uint16_t nBlockAlign;
|
uint16_t nBlockAlign;
|
||||||
uint16_t wBitsPerSample;
|
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;
|
||||||
|
};
|
@ -1,6 +1,7 @@
|
|||||||
#include "AssetLoaderSoundBank.h"
|
#include "AssetLoaderSoundBank.h"
|
||||||
|
|
||||||
#include "Csv/ParsedCsv.h"
|
#include "Csv/ParsedCsv.h"
|
||||||
|
#include "ObjContainer/SoundBank/SoundBankWriter.h"
|
||||||
#include "nlohmann/json.hpp"
|
#include "nlohmann/json.hpp"
|
||||||
|
|
||||||
#include "Game/T6/CommonT6.h"
|
#include "Game/T6/CommonT6.h"
|
||||||
@ -11,8 +12,54 @@
|
|||||||
#include <Utils/StringUtils.h>
|
#include <Utils/StringUtils.h>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
using namespace T6;
|
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<std::ofstream> 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<std::ofstream>(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)
|
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->id = Common::SND_HashName(name.data());
|
||||||
alias->assetFileName = memory->Dup(aliasFileName.data());
|
alias->assetFileName = memory->Dup(aliasFileName.data());
|
||||||
alias->assetId = Common::SND_HashName(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->duck = Common::SND_HashName(row.GetValue("duck").data());
|
||||||
|
|
||||||
alias->volMin = row.GetValueAs<uint16_t>("vol_min");
|
alias->volMin = row.GetValueInt<uint16_t>("vol_min");
|
||||||
alias->volMax = row.GetValueAs<uint16_t>("vol_max");
|
alias->volMax = row.GetValueInt<uint16_t>("vol_max");
|
||||||
alias->distMin = row.GetValueAs<uint16_t>("dist_min");
|
alias->distMin = row.GetValueInt<uint16_t>("dist_min");
|
||||||
alias->distMax = row.GetValueAs<uint16_t>("dist_max");
|
alias->distMax = row.GetValueInt<uint16_t>("dist_max");
|
||||||
alias->distReverbMax = row.GetValueAs<uint16_t>("dist_reverb_max");
|
alias->distReverbMax = row.GetValueInt<uint16_t>("dist_reverb_max");
|
||||||
alias->limitCount = row.GetValueAs<char>("limit_count");
|
alias->limitCount = row.GetValueInt<uint8_t>("limit_count");
|
||||||
alias->entityLimitCount = row.GetValueAs<char>("entity_limit_count");
|
alias->entityLimitCount = row.GetValueInt<uint8_t>("entity_limit_count");
|
||||||
alias->pitchMin = row.GetValueAs<uint16_t>("pitch_min");
|
alias->pitchMin = row.GetValueInt<uint16_t>("pitch_min");
|
||||||
alias->pitchMax = row.GetValueAs<uint16_t>("pitch_max");
|
alias->pitchMax = row.GetValueInt<uint16_t>("pitch_max");
|
||||||
alias->minPriority = row.GetValueAs<char>("min_priority");
|
alias->minPriority = row.GetValueInt<uint8_t>("min_priority");
|
||||||
alias->maxPriority = row.GetValueAs<char>("max_priority");
|
alias->maxPriority = row.GetValueInt<uint8_t>("max_priority");
|
||||||
alias->minPriorityThreshold = row.GetValueAs<char>("min_priority_threshold");
|
alias->minPriorityThreshold = row.GetValueInt<uint8_t>("min_priority_threshold");
|
||||||
alias->maxPriorityThreshold = row.GetValueAs<char>("max_priority_threshold");
|
alias->maxPriorityThreshold = row.GetValueInt<uint8_t>("max_priority_threshold");
|
||||||
alias->probability = row.GetValueAs<char>("probability");
|
alias->probability = row.GetValueInt<uint8_t>("probability");
|
||||||
alias->startDelay = row.GetValueAs<uint16_t>("start_delay");
|
alias->startDelay = row.GetValueInt<uint16_t>("start_delay");
|
||||||
alias->reverbSend = row.GetValueAs<uint16_t>("reverb_send");
|
alias->reverbSend = row.GetValueInt<uint16_t>("reverb_send");
|
||||||
alias->centerSend = row.GetValueAs<uint16_t>("center_send");
|
alias->centerSend = row.GetValueInt<uint16_t>("center_send");
|
||||||
alias->envelopMin = row.GetValueAs<uint16_t>("envelop_min");
|
alias->envelopMin = row.GetValueInt<uint16_t>("envelop_min");
|
||||||
alias->envelopMax = row.GetValueAs<uint16_t>("envelop_max");
|
alias->envelopMax = row.GetValueInt<uint16_t>("envelop_max");
|
||||||
alias->envelopPercentage = row.GetValueAs<uint16_t>("envelop_percentage");
|
alias->envelopPercentage = row.GetValueInt<uint16_t>("envelop_percentage");
|
||||||
alias->occlusionLevel = row.GetValueAs<char>("occlusion_level");
|
alias->occlusionLevel = row.GetValueInt<uint8_t>("occlusion_level");
|
||||||
alias->fluxTime = row.GetValueAs<uint16_t>("move_time");
|
alias->fluxTime = row.GetValueInt<uint16_t>("move_time");
|
||||||
alias->futzPatch = row.GetValueAs<unsigned int>("futz");
|
alias->futzPatch = row.GetValueInt<unsigned int>("futz");
|
||||||
alias->contextType = row.GetValueAs<unsigned int>("context_type");
|
alias->contextType = row.GetValueInt<unsigned int>("context_type");
|
||||||
alias->contextValue = row.GetValueAs<unsigned int>("context_value");
|
alias->contextValue = row.GetValueInt<unsigned int>("context_value");
|
||||||
alias->fadeIn = row.GetValueAs<int16_t>("fade_in");
|
alias->fadeIn = row.GetValueInt<int16_t>("fade_in");
|
||||||
alias->fadeOut = row.GetValueAs<int16_t>("fade_out");
|
alias->fadeOut = row.GetValueInt<int16_t>("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";
|
||||||
@ -309,22 +362,22 @@ bool LoadSoundRadverbs(MemoryManager* memory, SndBank* sndBank, const SearchPath
|
|||||||
|
|
||||||
strncpy_s(sndBank->radverbs[i].name, name.data(), 32);
|
strncpy_s(sndBank->radverbs[i].name, name.data(), 32);
|
||||||
sndBank->radverbs[i].id = Common::SND_HashName(name.data());
|
sndBank->radverbs[i].id = Common::SND_HashName(name.data());
|
||||||
sndBank->radverbs[i].smoothing = row.GetValueAs<float>("smoothing");
|
sndBank->radverbs[i].smoothing = row.GetValueFloat("smoothing");
|
||||||
sndBank->radverbs[i].earlyTime = row.GetValueAs<float>("earlyTime");
|
sndBank->radverbs[i].earlyTime = row.GetValueFloat("earlyTime");
|
||||||
sndBank->radverbs[i].lateTime = row.GetValueAs<float>("lateTime");
|
sndBank->radverbs[i].lateTime = row.GetValueFloat("lateTime");
|
||||||
sndBank->radverbs[i].earlyGain = row.GetValueAs<float>("earlyGain");
|
sndBank->radverbs[i].earlyGain = row.GetValueFloat("earlyGain");
|
||||||
sndBank->radverbs[i].lateGain = row.GetValueAs<float>("lateGain");
|
sndBank->radverbs[i].lateGain = row.GetValueFloat("lateGain");
|
||||||
sndBank->radverbs[i].returnGain = row.GetValueAs<float>("returnGain");
|
sndBank->radverbs[i].returnGain = row.GetValueFloat("returnGain");
|
||||||
sndBank->radverbs[i].earlyLpf = row.GetValueAs<float>("earlyLpf");
|
sndBank->radverbs[i].earlyLpf = row.GetValueFloat("earlyLpf");
|
||||||
sndBank->radverbs[i].lateLpf = row.GetValueAs<float>("lateLpf");
|
sndBank->radverbs[i].lateLpf = row.GetValueFloat("lateLpf");
|
||||||
sndBank->radverbs[i].inputLpf = row.GetValueAs<float>("inputLpf");
|
sndBank->radverbs[i].inputLpf = row.GetValueFloat("inputLpf");
|
||||||
sndBank->radverbs[i].dampLpf = row.GetValueAs<float>("dampLpf");
|
sndBank->radverbs[i].dampLpf = row.GetValueFloat("dampLpf");
|
||||||
sndBank->radverbs[i].wallReflect = row.GetValueAs<float>("wallReflect");
|
sndBank->radverbs[i].wallReflect = row.GetValueFloat("wallReflect");
|
||||||
sndBank->radverbs[i].dryGain = row.GetValueAs<float>("dryGain");
|
sndBank->radverbs[i].dryGain = row.GetValueFloat("dryGain");
|
||||||
sndBank->radverbs[i].earlySize = row.GetValueAs<float>("earlySize");
|
sndBank->radverbs[i].earlySize = row.GetValueFloat("earlySize");
|
||||||
sndBank->radverbs[i].lateSize = row.GetValueAs<float>("lateSize");
|
sndBank->radverbs[i].lateSize = row.GetValueFloat("lateSize");
|
||||||
sndBank->radverbs[i].diffusion = row.GetValueAs<float>("diffusion");
|
sndBank->radverbs[i].diffusion = row.GetValueFloat("diffusion");
|
||||||
sndBank->radverbs[i].returnHighpass = row.GetValueAs<float>("returnHighpass");
|
sndBank->radverbs[i].returnHighpass = row.GetValueFloat("returnHighpass");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,6 +469,7 @@ bool AssetLoaderSoundBank::LoadFromRaw(
|
|||||||
sndBank->name = memory->Dup(assetName.c_str());
|
sndBank->name = memory->Dup(assetName.c_str());
|
||||||
auto sndBankLocalization = utils::StringSplit(assetName, '.');
|
auto sndBankLocalization = utils::StringSplit(assetName, '.');
|
||||||
|
|
||||||
|
// load the soundbank aliases
|
||||||
unsigned int loadedEntryCount = 0u, streamedEntryCount = 0u;
|
unsigned int loadedEntryCount = 0u, streamedEntryCount = 0u;
|
||||||
if (!LoadSoundAliasList(memory, sndBank, aliasFile, &loadedEntryCount, &streamedEntryCount))
|
if (!LoadSoundAliasList(memory, sndBank, aliasFile, &loadedEntryCount, &streamedEntryCount))
|
||||||
return false;
|
return false;
|
||||||
@ -442,6 +496,9 @@ bool AssetLoaderSoundBank::LoadFromRaw(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<std::ofstream> sablStream, sabsStream;
|
||||||
|
std::unique_ptr<SoundBankWriter> sablWriter, sabsWriter;
|
||||||
|
|
||||||
if (loadedEntryCount > 0)
|
if (loadedEntryCount > 0)
|
||||||
{
|
{
|
||||||
sndBank->loadAssetBank.zone = memory->Dup(sndBankLocalization.at(0).c_str());
|
sndBank->loadAssetBank.zone = memory->Dup(sndBankLocalization.at(0).c_str());
|
||||||
@ -454,6 +511,11 @@ bool AssetLoaderSoundBank::LoadFromRaw(
|
|||||||
sndBank->loadedAssets.entryCount = loadedEntryCount;
|
sndBank->loadedAssets.entryCount = loadedEntryCount;
|
||||||
sndBank->loadedAssets.entries = static_cast<SndAssetBankEntry*>(memory->Alloc(sizeof(SndAssetBankEntry) * loadedEntryCount));
|
sndBank->loadedAssets.entries = static_cast<SndAssetBankEntry*>(memory->Alloc(sizeof(SndAssetBankEntry) * loadedEntryCount));
|
||||||
memset(sndBank->loadedAssets.entries, 0, 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)
|
if (streamedEntryCount > 0)
|
||||||
@ -461,6 +523,40 @@ bool AssetLoaderSoundBank::LoadFromRaw(
|
|||||||
sndBank->streamAssetBank.zone = memory->Dup(sndBankLocalization.at(0).c_str());
|
sndBank->streamAssetBank.zone = memory->Dup(sndBankLocalization.at(0).c_str());
|
||||||
sndBank->streamAssetBank.language = memory->Dup(sndBankLocalization.at(1).c_str());
|
sndBank->streamAssetBank.language = memory->Dup(sndBankLocalization.at(1).c_str());
|
||||||
memset(sndBank->streamAssetBank.linkTimeChecksum, 0xCC, 16);
|
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);
|
manager->AddAsset(ASSET_TYPE_SOUND, assetName, sndBank);
|
||||||
|
261
src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp
Normal file
261
src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.cpp
Normal file
@ -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 <filesystem>
|
||||||
|
#include <iostream>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
std::unordered_map<unsigned int, unsigned char> 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<const char*>(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<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{};
|
||||||
|
wavFile.m_stream->read(reinterpret_cast<char*>(&header), sizeof(WavHeader));
|
||||||
|
|
||||||
|
soundSize = static_cast<size_t>(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<size_t>(m_current_offset),
|
||||||
|
frameCount,
|
||||||
|
frameRateIndex,
|
||||||
|
static_cast<unsigned char>(header.formatChunk.nChannels),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
|
||||||
|
m_entries.push_back(entry);
|
||||||
|
|
||||||
|
soundData = std::make_unique<char[]>(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<size_t>(flacFile.m_length);
|
||||||
|
|
||||||
|
SoundAssetBankEntry entry{
|
||||||
|
soundId,
|
||||||
|
soundSize,
|
||||||
|
static_cast<size_t>(m_current_offset),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
8,
|
||||||
|
};
|
||||||
|
|
||||||
|
m_entries.push_back(entry);
|
||||||
|
|
||||||
|
soundData = std::make_unique<char[]>(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<std::pair<std::string, unsigned int>> m_sounds;
|
||||||
|
|
||||||
|
int64_t m_current_offset;
|
||||||
|
std::vector<SoundAssetBankEntry> m_entries;
|
||||||
|
std::vector<SoundAssetBankChecksum> 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> SoundBankWriter::Create(const std::string& fileName, std::ostream& stream, ISearchPath* assetSearchPath)
|
||||||
|
{
|
||||||
|
return std::make_unique<SoundBankWriterImpl>(fileName, stream, assetSearchPath);
|
||||||
|
}
|
25
src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.h
Normal file
25
src/ObjLoading/ObjContainer/SoundBank/SoundBankWriter.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "SearchPath/ISearchPath.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <ostream>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
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<SoundBankWriter> Create(const std::string& fileName, std::ostream& stream, ISearchPath* assetSearchPath);
|
||||||
|
|
||||||
|
static std::filesystem::path OutputPath;
|
||||||
|
};
|
@ -52,7 +52,8 @@ function ObjWriting:project()
|
|||||||
|
|
||||||
self:include(includes)
|
self:include(includes)
|
||||||
Utils:include(includes)
|
Utils:include(includes)
|
||||||
json:include(includes)
|
|
||||||
minilzo:include(includes)
|
minilzo:include(includes)
|
||||||
minizip:include(includes)
|
minizip:include(includes)
|
||||||
|
json:include(includes)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
#include "WavWriter.h"
|
#include "WavWriter.h"
|
||||||
|
|
||||||
#include "Sound/WavTypes.h"
|
|
||||||
|
|
||||||
WavWriter::WavWriter(std::ostream& stream)
|
WavWriter::WavWriter(std::ostream& stream)
|
||||||
: m_stream(stream)
|
: m_stream(stream)
|
||||||
{
|
{
|
||||||
|
@ -1,12 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
#include <Sound/WavTypes.h>
|
||||||
struct WavMetaData
|
|
||||||
{
|
|
||||||
unsigned channelCount;
|
|
||||||
unsigned samplesPerSec;
|
|
||||||
unsigned bitsPerSample;
|
|
||||||
};
|
|
||||||
|
|
||||||
class WavWriter
|
class WavWriter
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user