mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-04-20 00:02:55 +00:00
Dump SoundBank asset data files
This commit is contained in:
parent
05303313be
commit
3cda71d1e7
@ -5755,6 +5755,14 @@ namespace T6
|
|||||||
MaterialArgumentDef u;
|
MaterialArgumentDef u;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum SndAliasType
|
||||||
|
{
|
||||||
|
SAT_UNKNOWN = 0x0,
|
||||||
|
SAT_LOADED = 0x1,
|
||||||
|
SAT_STREAMED = 0x2,
|
||||||
|
SAT_PRIMED = 0x3,
|
||||||
|
SAT_COUNT = 0x4,
|
||||||
|
};
|
||||||
|
|
||||||
struct SndAlias
|
struct SndAlias
|
||||||
{
|
{
|
||||||
@ -5764,7 +5772,7 @@ namespace T6
|
|||||||
const char* secondaryname;
|
const char* secondaryname;
|
||||||
unsigned int assetId;
|
unsigned int assetId;
|
||||||
const char* assetFileName;
|
const char* assetFileName;
|
||||||
unsigned int flags0;
|
unsigned int flags0; // Bits 15/16 are SndAliasType
|
||||||
unsigned int flags1;
|
unsigned int flags1;
|
||||||
unsigned int duck;
|
unsigned int duck;
|
||||||
unsigned int contextType;
|
unsigned int contextType;
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#include "ObjLoaderT6.h"
|
#include "ObjLoaderT6.h"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#include "Game/T6/GameT6.h"
|
#include "Game/T6/GameT6.h"
|
||||||
#include "Game/T6/GameAssetPoolT6.h"
|
#include "Game/T6/GameAssetPoolT6.h"
|
||||||
#include "ObjContainer/IPak/IPak.h"
|
#include "ObjContainer/IPak/IPak.h"
|
||||||
@ -92,6 +94,115 @@ namespace T6
|
|||||||
return zone->m_game == &g_GameT6;
|
return zone->m_game == &g_GameT6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ObjLoader::VerifySoundBankChecksum(const SoundBank* soundBank, const SndRuntimeAssetBank& sndRuntimeAssetBank)
|
||||||
|
{
|
||||||
|
SndAssetBankChecksum checksum{};
|
||||||
|
static_assert(sizeof(SndAssetBankChecksum::checksumBytes) == sizeof(SndRuntimeAssetBank::linkTimeChecksum));
|
||||||
|
for (auto i = 0u; i < sizeof(SndAssetBankChecksum::checksumBytes); i++)
|
||||||
|
checksum.checksumBytes[i] = sndRuntimeAssetBank.linkTimeChecksum[i];
|
||||||
|
|
||||||
|
return soundBank->VerifyChecksum(checksum);
|
||||||
|
}
|
||||||
|
|
||||||
|
SoundBank* ObjLoader::LoadSoundBankForZone(ISearchPath* searchPath, const std::string& soundBankFileName, Zone* zone)
|
||||||
|
{
|
||||||
|
if (ObjLoading::Configuration.Verbose)
|
||||||
|
std::cout << "Trying to load sound bank '" << soundBankFileName << "' for zone '" << zone->m_name << "'" << std::endl;
|
||||||
|
|
||||||
|
auto* existingSoundBank = SoundBank::Repository.GetContainerByName(soundBankFileName);
|
||||||
|
if (existingSoundBank != nullptr)
|
||||||
|
{
|
||||||
|
if (ObjLoading::Configuration.Verbose)
|
||||||
|
std::cout << "Referencing loaded sound bank '" << soundBankFileName << "'." << std::endl;
|
||||||
|
|
||||||
|
SoundBank::Repository.AddContainerReference(existingSoundBank, zone);
|
||||||
|
return existingSoundBank;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto file = searchPath->Open(soundBankFileName);
|
||||||
|
if (file.IsOpen())
|
||||||
|
{
|
||||||
|
auto sndBank = std::make_unique<SoundBank>(soundBankFileName, std::move(file.m_stream), file.m_length);
|
||||||
|
auto* sndBankPtr = sndBank.get();
|
||||||
|
|
||||||
|
if (!sndBank->Initialize())
|
||||||
|
{
|
||||||
|
std::cout << "Failed to load sound bank '" << soundBankFileName << "'" << std::endl;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
SoundBank::Repository.AddContainer(std::move(sndBank), zone);
|
||||||
|
|
||||||
|
if (ObjLoading::Configuration.Verbose)
|
||||||
|
std::cout << "Found and loaded sound bank '" << soundBankFileName << "'" << std::endl;
|
||||||
|
|
||||||
|
return sndBankPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Failed to load sound bank '" << soundBankFileName << "'" << std::endl;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjLoader::LoadSoundBankFromLinkedInfo(ISearchPath* searchPath, const std::string& soundBankFileName, const SndRuntimeAssetBank* sndBankLinkedInfo, Zone* zone, std::set<std::string>& loadedBanksForZone, std::stack<std::string>& dependenciesToLoad)
|
||||||
|
{
|
||||||
|
if (loadedBanksForZone.find(soundBankFileName) == loadedBanksForZone.end())
|
||||||
|
{
|
||||||
|
auto* soundBank = LoadSoundBankForZone(searchPath, soundBankFileName, zone);
|
||||||
|
|
||||||
|
if (soundBank)
|
||||||
|
{
|
||||||
|
if (!VerifySoundBankChecksum(soundBank, *sndBankLinkedInfo))
|
||||||
|
{
|
||||||
|
std::cout << "Checksum of sound bank does not match link time checksum for '" << soundBankFileName << "'" << std::endl;
|
||||||
|
}
|
||||||
|
loadedBanksForZone.emplace(soundBankFileName);
|
||||||
|
|
||||||
|
for (const auto& dependency : soundBank->GetDependencies())
|
||||||
|
{
|
||||||
|
dependenciesToLoad.emplace(dependency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjLoader::LoadSoundBanksFromAsset(ISearchPath* searchPath, const SndBank* sndBank, Zone* zone, std::set<std::string>& loadedBanksForZone)
|
||||||
|
{
|
||||||
|
std::stack<std::string> dependenciesToLoad;
|
||||||
|
|
||||||
|
if (sndBank->streamAssetBank.zone)
|
||||||
|
{
|
||||||
|
const auto soundBankFileName = SoundBank::GetFileNameForDefinition(true, sndBank->streamAssetBank.zone, sndBank->streamAssetBank.language);
|
||||||
|
LoadSoundBankFromLinkedInfo(searchPath, soundBankFileName, &sndBank->streamAssetBank, zone, loadedBanksForZone, dependenciesToLoad);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sndBank->runtimeAssetLoad && sndBank->loadAssetBank.zone)
|
||||||
|
{
|
||||||
|
const auto soundBankFileName = SoundBank::GetFileNameForDefinition(false, sndBank->loadAssetBank.zone, sndBank->loadAssetBank.language);
|
||||||
|
LoadSoundBankFromLinkedInfo(searchPath, soundBankFileName, &sndBank->loadAssetBank, zone, loadedBanksForZone, dependenciesToLoad);
|
||||||
|
}
|
||||||
|
|
||||||
|
while(!dependenciesToLoad.empty())
|
||||||
|
{
|
||||||
|
auto dependencyFileName = dependenciesToLoad.top();
|
||||||
|
dependenciesToLoad.pop();
|
||||||
|
|
||||||
|
if (loadedBanksForZone.find(dependencyFileName) == loadedBanksForZone.end())
|
||||||
|
{
|
||||||
|
auto* soundBank = LoadSoundBankForZone(searchPath, dependencyFileName, zone);
|
||||||
|
|
||||||
|
if (soundBank)
|
||||||
|
{
|
||||||
|
loadedBanksForZone.emplace(dependencyFileName);
|
||||||
|
|
||||||
|
for (const auto& dependency : soundBank->GetDependencies())
|
||||||
|
{
|
||||||
|
dependenciesToLoad.emplace(dependency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ObjLoader::LoadIPakForZone(ISearchPath* searchPath, const std::string& ipakName, Zone* zone)
|
void ObjLoader::LoadIPakForZone(ISearchPath* searchPath, const std::string& ipakName, Zone* zone)
|
||||||
{
|
{
|
||||||
if (ObjLoading::Configuration.Verbose)
|
if (ObjLoading::Configuration.Verbose)
|
||||||
@ -199,6 +310,16 @@ namespace T6
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (assetPoolT6->m_sound_bank != nullptr)
|
||||||
|
{
|
||||||
|
std::set<std::string> loadedSoundBanksForZone;
|
||||||
|
|
||||||
|
for (auto* sndBankAssetInfo : *assetPoolT6->m_sound_bank)
|
||||||
|
{
|
||||||
|
LoadSoundBanksFromAsset(searchPath, sndBankAssetInfo->Asset(), zone, loadedSoundBanksForZone);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjLoader::UnloadContainersOfZone(Zone* zone) const
|
void ObjLoader::UnloadContainersOfZone(Zone* zone) const
|
||||||
|
@ -2,11 +2,15 @@
|
|||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
#include <stack>
|
||||||
|
|
||||||
#include "IObjLoader.h"
|
#include "IObjLoader.h"
|
||||||
#include "AssetLoading/IAssetLoader.h"
|
#include "AssetLoading/IAssetLoader.h"
|
||||||
#include "SearchPath/ISearchPath.h"
|
#include "SearchPath/ISearchPath.h"
|
||||||
#include "Game/T6/T6.h"
|
#include "Game/T6/T6.h"
|
||||||
|
#include "ObjContainer/SoundBank/SoundBank.h"
|
||||||
|
|
||||||
namespace T6
|
namespace T6
|
||||||
{
|
{
|
||||||
@ -17,6 +21,12 @@ namespace T6
|
|||||||
|
|
||||||
std::unordered_map<asset_type_t, std::unique_ptr<IAssetLoader>> m_asset_loaders_by_type;
|
std::unordered_map<asset_type_t, std::unique_ptr<IAssetLoader>> m_asset_loaders_by_type;
|
||||||
|
|
||||||
|
static bool VerifySoundBankChecksum(const SoundBank* soundBank, const SndRuntimeAssetBank& sndRuntimeAssetBank);
|
||||||
|
static SoundBank* LoadSoundBankForZone(ISearchPath* searchPath, const std::string& soundBankFileName, Zone* zone);
|
||||||
|
static void LoadSoundBankFromLinkedInfo(ISearchPath* searchPath, const std::string& soundBankFileName, const SndRuntimeAssetBank* sndBankLinkedInfo, Zone* zone,
|
||||||
|
std::set<std::string>& loadedBanksForZone, std::stack<std::string>& dependenciesToLoad);
|
||||||
|
static void LoadSoundBanksFromAsset(ISearchPath* searchPath, const SndBank* sndBank, Zone* zone, std::set<std::string>& loadedBanksForZone);
|
||||||
|
|
||||||
static void LoadIPakForZone(ISearchPath* searchPath, const std::string& ipakName, Zone* zone);
|
static void LoadIPakForZone(ISearchPath* searchPath, const std::string& ipakName, Zone* zone);
|
||||||
|
|
||||||
static void LoadImageFromIwi(GfxImage* image, ISearchPath* searchPath, Zone* zone);
|
static void LoadImageFromIwi(GfxImage* image, ISearchPath* searchPath, Zone* zone);
|
||||||
|
299
src/ObjLoading/ObjContainer/SoundBank/SoundBank.cpp
Normal file
299
src/ObjLoading/ObjContainer/SoundBank/SoundBank.cpp
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
#include "SoundBank.h"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "zlib.h"
|
||||||
|
|
||||||
|
#include "Utils/FileUtils.h"
|
||||||
|
|
||||||
|
ObjContainerRepository<SoundBank, Zone> SoundBank::Repository;
|
||||||
|
|
||||||
|
class SoundBankInputBuffer final : public objbuf
|
||||||
|
{
|
||||||
|
std::istream& m_stream;
|
||||||
|
int64_t m_base_offset;
|
||||||
|
size_t m_size;
|
||||||
|
size_t m_offset;
|
||||||
|
bool m_open;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::streamsize showmanyc() override
|
||||||
|
{
|
||||||
|
return m_size - m_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
int_type underflow() override
|
||||||
|
{
|
||||||
|
if (m_offset >= m_size)
|
||||||
|
return EOF;
|
||||||
|
|
||||||
|
return m_stream.peek();
|
||||||
|
}
|
||||||
|
|
||||||
|
int_type uflow() override
|
||||||
|
{
|
||||||
|
if (m_offset >= m_size)
|
||||||
|
return EOF;
|
||||||
|
|
||||||
|
m_offset++;
|
||||||
|
return m_stream.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::streamsize xsgetn(char* ptr, std::streamsize count) override
|
||||||
|
{
|
||||||
|
if (m_offset + count > m_size)
|
||||||
|
count = m_size - m_offset;
|
||||||
|
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
m_stream.read(ptr, count);
|
||||||
|
|
||||||
|
const auto readSize = m_stream.gcount();
|
||||||
|
m_offset += readSize;
|
||||||
|
return readSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos_type seekoff(const off_type off, const std::ios_base::seekdir dir, const std::ios_base::openmode mode) override
|
||||||
|
{
|
||||||
|
if(dir == std::ios_base::beg)
|
||||||
|
{
|
||||||
|
return seekpos(off, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dir == std::ios_base::end)
|
||||||
|
{
|
||||||
|
if (off > m_size)
|
||||||
|
return pos_type(-1);
|
||||||
|
|
||||||
|
return seekpos(m_size - off, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return seekpos(m_offset + off, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
pos_type seekpos(const pos_type pos, std::ios_base::openmode mode) override
|
||||||
|
{
|
||||||
|
if (pos < 0 || pos >= m_size)
|
||||||
|
return pos_type(-1);
|
||||||
|
|
||||||
|
m_stream.seekg(m_base_offset + pos);
|
||||||
|
m_offset = static_cast<size_t>(pos);
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
SoundBankInputBuffer(std::istream& stream, const int64_t baseOffset, const size_t size)
|
||||||
|
: m_stream(stream),
|
||||||
|
m_base_offset(baseOffset),
|
||||||
|
m_size(size),
|
||||||
|
m_offset(0),
|
||||||
|
m_open(true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
_NODISCARD bool is_open() const override
|
||||||
|
{
|
||||||
|
return m_open;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool close() override
|
||||||
|
{
|
||||||
|
const auto result = m_open;
|
||||||
|
m_open = false;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool SoundBank::ReadHeader()
|
||||||
|
{
|
||||||
|
m_stream->read(reinterpret_cast<char*>(&m_header), sizeof(m_header));
|
||||||
|
if (m_stream->gcount() != sizeof(m_header))
|
||||||
|
{
|
||||||
|
printf("Unexpected eof when trying to load sndbank header.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_header.magic != MAGIC)
|
||||||
|
{
|
||||||
|
std::cout << "Invalid sndbank magic 0x" << std::hex << m_header.magic << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_header.version != VERSION)
|
||||||
|
{
|
||||||
|
std::cout << "Unsupported sndbank version " << m_header.version << " (should be " << VERSION << ")" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_header.entrySize != sizeof(SndAssetBankEntry))
|
||||||
|
{
|
||||||
|
std::cout << "Invalid sndbank entry size 0x" << std::hex << m_header.entrySize << " (should be 0x" << std::hex << sizeof(SndAssetBankEntry) << ")" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_header.fileSize != m_file_size)
|
||||||
|
{
|
||||||
|
std::cout << "Invalid sndbank " << m_file_size << " (header expects " << m_header.fileSize << ")" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_header.entryCount
|
||||||
|
&& (m_header.entryOffset <= 0 || m_header.entryOffset + sizeof(SndAssetBankEntry) * m_header.entryCount > m_file_size))
|
||||||
|
{
|
||||||
|
std::cout << "Invalid sndbank entry offset " << m_header.entryOffset << " (filesize is " << m_file_size << ")" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_header.checksumOffset <= 0 || m_header.checksumOffset + sizeof(SndAssetBankChecksum) * m_header.entryCount > m_file_size)
|
||||||
|
{
|
||||||
|
std::cout << "Invalid sndbank checksum offset " << m_header.checksumOffset << " (filesize is " << m_file_size << ")" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_header.dependencyCount * m_header.dependencySize > sizeof(SndAssetBankHeader::dependencies))
|
||||||
|
{
|
||||||
|
std::cout << "Invalid sndbank dependency sizes (count is " << m_header.dependencyCount << "; size is " << m_header.dependencySize << ")" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto i = 0u; i < m_header.dependencyCount; i++)
|
||||||
|
{
|
||||||
|
const auto dependencyLen = strnlen(&m_header.dependencies[i * m_header.dependencySize], m_header.dependencySize);
|
||||||
|
std::string dependencyName(&m_header.dependencies[i * m_header.dependencySize], dependencyLen);
|
||||||
|
|
||||||
|
if (dependencyName.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
m_dependencies.emplace_back(std::move(dependencyName));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SoundBank::ReadEntries()
|
||||||
|
{
|
||||||
|
m_stream->seekg(m_header.entryOffset);
|
||||||
|
|
||||||
|
for (auto i = 0u; i < m_header.entryCount; i++)
|
||||||
|
{
|
||||||
|
SndAssetBankEntry entry{};
|
||||||
|
m_stream->read(reinterpret_cast<char*>(&entry), sizeof(entry));
|
||||||
|
|
||||||
|
if (m_stream->gcount() != sizeof(entry))
|
||||||
|
{
|
||||||
|
std::cout << "Failed to read sound bank entry at index " << i << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.offset == 0 || entry.offset + entry.size >= m_file_size)
|
||||||
|
{
|
||||||
|
std::cout << "Invalid sound bank entry data offset " << entry.offset << " (filesize is " << m_header.fileSize << ")" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_entries.push_back(entry);
|
||||||
|
m_entries_by_id.emplace(std::make_pair(entry.id, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SoundBank::ReadChecksums()
|
||||||
|
{
|
||||||
|
m_stream->seekg(m_header.entryOffset);
|
||||||
|
|
||||||
|
for (auto i = 0u; i < m_header.entryCount; i++)
|
||||||
|
{
|
||||||
|
SndAssetBankChecksum checksum{};
|
||||||
|
m_stream->read(reinterpret_cast<char*>(&checksum), sizeof(checksum));
|
||||||
|
|
||||||
|
if (m_stream->gcount() != sizeof(checksum))
|
||||||
|
{
|
||||||
|
std::cout << "Failed to read sound bank checksum at index " << i << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_checksums.push_back(checksum);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SoundBank::GetFileNameForDefinition(const bool streamed, const char* zone, const char* language)
|
||||||
|
{
|
||||||
|
std::ostringstream str;
|
||||||
|
|
||||||
|
assert(zone != nullptr);
|
||||||
|
|
||||||
|
if (zone)
|
||||||
|
str << zone;
|
||||||
|
|
||||||
|
if (language)
|
||||||
|
str << "." << language;
|
||||||
|
|
||||||
|
if (streamed)
|
||||||
|
str << ".sabs";
|
||||||
|
else
|
||||||
|
str << ".sabl";
|
||||||
|
|
||||||
|
return str.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
SoundBank::SoundBank(std::string fileName, std::unique_ptr<std::istream> stream, const int64_t fileSize)
|
||||||
|
: m_file_name(std::move(fileName)),
|
||||||
|
m_stream(std::move(stream)),
|
||||||
|
m_file_size(fileSize),
|
||||||
|
m_initialized(false),
|
||||||
|
m_header{}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SoundBank::GetName()
|
||||||
|
{
|
||||||
|
return m_file_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SoundBank::Initialize()
|
||||||
|
{
|
||||||
|
if (m_initialized)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!ReadHeader()
|
||||||
|
|| !ReadEntries()
|
||||||
|
|| !ReadChecksums())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_initialized = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<std::string>& SoundBank::GetDependencies() const
|
||||||
|
{
|
||||||
|
return m_dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SoundBank::VerifyChecksum(const SndAssetBankChecksum& checksum) const
|
||||||
|
{
|
||||||
|
return m_initialized && memcmp(checksum.checksumBytes, m_header.checksumChecksum.checksumBytes, sizeof(SndAssetBankChecksum)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchPathOpenFile SoundBank::GetEntryStream(const unsigned id) const
|
||||||
|
{
|
||||||
|
const auto foundEntry = m_entries_by_id.find(id);
|
||||||
|
|
||||||
|
if (foundEntry != m_entries_by_id.end())
|
||||||
|
{
|
||||||
|
const auto& entry = m_entries[foundEntry->second];
|
||||||
|
|
||||||
|
m_stream->seekg(entry.offset);
|
||||||
|
|
||||||
|
return SearchPathOpenFile(std::make_unique<iobjstream>(std::make_unique<SoundBankInputBuffer>(*m_stream, entry.offset, entry.size)), entry.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SearchPathOpenFile();
|
||||||
|
}
|
52
src/ObjLoading/ObjContainer/SoundBank/SoundBank.h
Normal file
52
src/ObjLoading/ObjContainer/SoundBank/SoundBank.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <istream>
|
||||||
|
|
||||||
|
#include "Utils/ClassUtils.h"
|
||||||
|
#include "ObjContainer/ObjContainerReferenceable.h"
|
||||||
|
#include "ObjContainer/ObjContainerRepository.h"
|
||||||
|
#include "ObjContainer/SoundBank/SoundBankTypes.h"
|
||||||
|
#include "SearchPath/ISearchPath.h"
|
||||||
|
#include "Utils/FileUtils.h"
|
||||||
|
#include "Utils/ObjStream.h"
|
||||||
|
#include "Zone/Zone.h"
|
||||||
|
|
||||||
|
class SoundBank final : public ObjContainerReferenceable
|
||||||
|
{
|
||||||
|
static constexpr uint32_t MAGIC = FileUtils::MakeMagic32('2', 'U', 'X', '#');
|
||||||
|
static constexpr uint32_t VERSION = 14u;
|
||||||
|
|
||||||
|
std::string m_file_name;
|
||||||
|
std::unique_ptr<std::istream> m_stream;
|
||||||
|
int64_t m_file_size;
|
||||||
|
|
||||||
|
bool m_initialized;
|
||||||
|
SndAssetBankHeader m_header;
|
||||||
|
std::vector<std::string> m_dependencies;
|
||||||
|
std::vector<SndAssetBankEntry> m_entries;
|
||||||
|
std::vector<SndAssetBankChecksum> m_checksums;
|
||||||
|
std::unordered_map<unsigned int, size_t> m_entries_by_id;
|
||||||
|
|
||||||
|
bool ReadHeader();
|
||||||
|
bool ReadEntries();
|
||||||
|
bool ReadChecksums();
|
||||||
|
|
||||||
|
public:
|
||||||
|
static ObjContainerRepository<SoundBank, Zone> Repository;
|
||||||
|
|
||||||
|
static std::string GetFileNameForDefinition(bool streamed, const char* zone, const char* language);
|
||||||
|
|
||||||
|
SoundBank(std::string fileName, std::unique_ptr<std::istream> stream, int64_t fileSize);
|
||||||
|
SoundBank(const SoundBank& other) = delete;
|
||||||
|
SoundBank(SoundBank&& other) noexcept = default;
|
||||||
|
SoundBank& operator=(const SoundBank& other) = delete;
|
||||||
|
SoundBank& operator=(SoundBank&& other) noexcept = default;
|
||||||
|
|
||||||
|
std::string GetName() override;
|
||||||
|
|
||||||
|
bool Initialize();
|
||||||
|
_NODISCARD const std::vector<std::string>& GetDependencies() const;
|
||||||
|
|
||||||
|
_NODISCARD bool VerifyChecksum(const SndAssetBankChecksum& checksum) const;
|
||||||
|
_NODISCARD SearchPathOpenFile GetEntryStream(unsigned int id) const;
|
||||||
|
};
|
45
src/ObjLoading/ObjContainer/SoundBank/SoundBankTypes.h
Normal file
45
src/ObjLoading/ObjContainer/SoundBank/SoundBankTypes.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
class SoundBankConsts
|
||||||
|
{
|
||||||
|
SoundBankConsts() = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr unsigned OFFSET_DATA_START = 0x800;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SndAssetBankChecksum
|
||||||
|
{
|
||||||
|
char checksumBytes[16];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SndAssetBankHeader
|
||||||
|
{
|
||||||
|
unsigned int magic; // + 0x0
|
||||||
|
unsigned int version; // + 0x4
|
||||||
|
unsigned int entrySize; // + 0x8
|
||||||
|
unsigned int checksumSize; // + 0xC
|
||||||
|
unsigned int dependencySize; // + 0x10
|
||||||
|
unsigned int entryCount; // + 0x14
|
||||||
|
unsigned int dependencyCount; // + 0x18
|
||||||
|
unsigned int pad32; // + 0x1C
|
||||||
|
int64_t fileSize; // + 0x20
|
||||||
|
int64_t entryOffset; // + 0x28
|
||||||
|
int64_t checksumOffset; // + 0x30
|
||||||
|
SndAssetBankChecksum checksumChecksum; // + 0x38
|
||||||
|
char dependencies[512]; // + 0x48
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SndAssetBankEntry
|
||||||
|
{
|
||||||
|
unsigned int id;
|
||||||
|
unsigned int size;
|
||||||
|
unsigned int offset;
|
||||||
|
unsigned int frameCount;
|
||||||
|
char frameRateIndex;
|
||||||
|
char channelCount;
|
||||||
|
char looping;
|
||||||
|
char format;
|
||||||
|
};
|
@ -3,6 +3,7 @@ ObjWriting = {}
|
|||||||
function ObjWriting:include(includes)
|
function ObjWriting:include(includes)
|
||||||
if includes:handle(self:name()) then
|
if includes:handle(self:name()) then
|
||||||
ObjCommon:include(includes)
|
ObjCommon:include(includes)
|
||||||
|
ObjLoading:include(includes)
|
||||||
ZoneCommon:include(includes)
|
ZoneCommon:include(includes)
|
||||||
includedirs {
|
includedirs {
|
||||||
path.join(ProjectFolder(), "ObjWriting")
|
path.join(ProjectFolder(), "ObjWriting")
|
||||||
@ -14,6 +15,7 @@ function ObjWriting:link(links)
|
|||||||
links:add(self:name())
|
links:add(self:name())
|
||||||
links:linkto(Utils)
|
links:linkto(Utils)
|
||||||
links:linkto(ObjCommon)
|
links:linkto(ObjCommon)
|
||||||
|
links:linkto(ObjLoading)
|
||||||
links:linkto(ZoneCommon)
|
links:linkto(ZoneCommon)
|
||||||
links:linkto(minilzo)
|
links:linkto(minilzo)
|
||||||
links:linkto(minizip)
|
links:linkto(minizip)
|
||||||
|
111
src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp
Normal file
111
src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.cpp
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
#include "AssetDumperSndBank.h"
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
#include "Csv/CsvStream.h"
|
||||||
|
#include "ObjContainer/SoundBank/SoundBank.h"
|
||||||
|
|
||||||
|
using namespace T6;
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
std::unique_ptr<std::ostream> AssetDumperSndBank::OpenOutputFile(AssetDumpingContext& context, const std::string& outputFileName) const
|
||||||
|
{
|
||||||
|
fs::path assetPath(context.m_base_path);
|
||||||
|
|
||||||
|
fs::path assetName(outputFileName);
|
||||||
|
auto firstPart = true;
|
||||||
|
for(const auto& part : assetName)
|
||||||
|
{
|
||||||
|
if(firstPart)
|
||||||
|
{
|
||||||
|
firstPart = false;
|
||||||
|
if(part.string() == "raw"
|
||||||
|
|| part.string() == "devraw")
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
assetPath.append(part.string());
|
||||||
|
}
|
||||||
|
|
||||||
|
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 AssetDumperSndBank::DumpSndBank(AssetDumpingContext& context, const XAssetInfo<SndBank>* sndBankInfo)
|
||||||
|
{
|
||||||
|
const auto* sndBank = sndBankInfo->Asset();
|
||||||
|
|
||||||
|
// auto* streamAssetBank = SoundBank::Repository.GetContainerByName(SoundBank::GetFileNameForDefinition(true, sndBank->streamAssetBank.zone, sndBank->streamAssetBank.language));
|
||||||
|
// SoundBank* loadedAssetBank = nullptr;
|
||||||
|
// if (sndBank->runtimeAssetLoad && sndBank->loadAssetBank.zone)
|
||||||
|
// loadedAssetBank = SoundBank::Repository.GetContainerByName(SoundBank::GetFileNameForDefinition(false, sndBank->loadAssetBank.zone, sndBank->loadAssetBank.language));
|
||||||
|
|
||||||
|
std::map<unsigned int, std::string> aliasMappings;
|
||||||
|
for (auto i = 0u; i < sndBank->aliasCount; i++)
|
||||||
|
{
|
||||||
|
const auto& aliasList = sndBank->alias[i];
|
||||||
|
for (auto j = 0; j < aliasList.count; j++)
|
||||||
|
{
|
||||||
|
const auto& alias = aliasList.head[j];
|
||||||
|
if (alias.assetId && alias.assetFileName)
|
||||||
|
aliasMappings[alias.assetId] = alias.assetFileName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& [id, filename] : aliasMappings)
|
||||||
|
{
|
||||||
|
auto foundEntry = false;
|
||||||
|
|
||||||
|
for (const auto* soundBank : SoundBank::Repository)
|
||||||
|
{
|
||||||
|
auto soundFile = soundBank->GetEntryStream(id);
|
||||||
|
if (soundFile.IsOpen())
|
||||||
|
{
|
||||||
|
auto outFile = OpenOutputFile(context, filename);
|
||||||
|
if (!outFile)
|
||||||
|
{
|
||||||
|
std::cout << "Failed to open sound outputfile: \"" << filename << "\"" << std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
char buffer[2048];
|
||||||
|
while (!soundFile.m_stream->eof())
|
||||||
|
{
|
||||||
|
soundFile.m_stream->read(buffer, sizeof(buffer));
|
||||||
|
const auto readSize = soundFile.m_stream->gcount();
|
||||||
|
outFile->write(buffer, readSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
foundEntry = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!foundEntry)
|
||||||
|
{
|
||||||
|
std::cout << "Could not find data for sound \"" << filename << "\"" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssetDumperSndBank::DumpPool(AssetDumpingContext& context, AssetPool<SndBank>* pool)
|
||||||
|
{
|
||||||
|
for(const auto* assetInfo : *pool)
|
||||||
|
{
|
||||||
|
if (!assetInfo->m_name.empty() && assetInfo->m_name[0] == ',')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
DumpSndBank(context, assetInfo);
|
||||||
|
}
|
||||||
|
}
|
19
src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.h
Normal file
19
src/ObjWriting/Game/T6/AssetDumpers/AssetDumperSndBank.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
#include "Dumping/AbstractAssetDumper.h"
|
||||||
|
#include "Game/T6/T6.h"
|
||||||
|
|
||||||
|
namespace T6
|
||||||
|
{
|
||||||
|
class AssetDumperSndBank final : public IAssetDumper<SndBank>
|
||||||
|
{
|
||||||
|
std::unique_ptr<std::ostream> OpenOutputFile(AssetDumpingContext& context, const std::string& outputFileName) const;
|
||||||
|
void DumpSndBank(AssetDumpingContext& context, const XAssetInfo<SndBank>* sndBankInfo);
|
||||||
|
|
||||||
|
public:
|
||||||
|
void DumpPool(AssetDumpingContext& context, AssetPool<SndBank>* pool) override;
|
||||||
|
};
|
||||||
|
}
|
@ -13,6 +13,7 @@
|
|||||||
#include "AssetDumpers/AssetDumperFontIcon.h"
|
#include "AssetDumpers/AssetDumperFontIcon.h"
|
||||||
#include "AssetDumpers/AssetDumperPhysConstraints.h"
|
#include "AssetDumpers/AssetDumperPhysConstraints.h"
|
||||||
#include "AssetDumpers/AssetDumperPhysPreset.h"
|
#include "AssetDumpers/AssetDumperPhysPreset.h"
|
||||||
|
#include "AssetDumpers/AssetDumperSndBank.h"
|
||||||
#include "AssetDumpers/AssetDumperTracer.h"
|
#include "AssetDumpers/AssetDumperTracer.h"
|
||||||
#include "AssetDumpers/AssetDumperVehicle.h"
|
#include "AssetDumpers/AssetDumperVehicle.h"
|
||||||
#include "AssetDumpers/AssetDumperWeapon.h"
|
#include "AssetDumpers/AssetDumperWeapon.h"
|
||||||
@ -46,7 +47,7 @@ bool ZoneDumper::DumpZone(AssetDumpingContext& context) const
|
|||||||
// DUMP_ASSET_POOL(AssetDumperMaterial, m_material);
|
// DUMP_ASSET_POOL(AssetDumperMaterial, m_material);
|
||||||
// DUMP_ASSET_POOL(AssetDumperTechniqueSet, m_technique_set);
|
// DUMP_ASSET_POOL(AssetDumperTechniqueSet, m_technique_set);
|
||||||
DUMP_ASSET_POOL(AssetDumperGfxImage, m_image);
|
DUMP_ASSET_POOL(AssetDumperGfxImage, m_image);
|
||||||
// DUMP_ASSET_POOL(AssetDumperSndBank, m_sound_bank);
|
DUMP_ASSET_POOL(AssetDumperSndBank, m_sound_bank);
|
||||||
// DUMP_ASSET_POOL(AssetDumperSndPatch, m_sound_patch);
|
// DUMP_ASSET_POOL(AssetDumperSndPatch, m_sound_patch);
|
||||||
// DUMP_ASSET_POOL(AssetDumperClipMap, m_clip_map);
|
// DUMP_ASSET_POOL(AssetDumperClipMap, m_clip_map);
|
||||||
// DUMP_ASSET_POOL(AssetDumperComWorld, m_com_world);
|
// DUMP_ASSET_POOL(AssetDumperComWorld, m_com_world);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user