Dump SoundBank asset data files

This commit is contained in:
Jan
2021-04-05 18:50:42 +02:00
parent 05303313be
commit 3cda71d1e7
10 changed files with 670 additions and 2 deletions

View File

@ -1,5 +1,7 @@
#include "ObjLoaderT6.h"
#include <sstream>
#include "Game/T6/GameT6.h"
#include "Game/T6/GameAssetPoolT6.h"
#include "ObjContainer/IPak/IPak.h"
@ -92,6 +94,115 @@ namespace T6
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)
{
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

View File

@ -2,11 +2,15 @@
#include <unordered_map>
#include <memory>
#include <set>
#include <string>
#include <stack>
#include "IObjLoader.h"
#include "AssetLoading/IAssetLoader.h"
#include "SearchPath/ISearchPath.h"
#include "Game/T6/T6.h"
#include "ObjContainer/SoundBank/SoundBank.h"
namespace T6
{
@ -17,6 +21,12 @@ namespace T6
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 LoadImageFromIwi(GfxImage* image, ISearchPath* searchPath, Zone* zone);

View 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();
}

View 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;
};

View 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;
};