From 727de03eb505e2f5244635e3ee26383b86ff5305 Mon Sep 17 00:00:00 2001 From: Jannik Vogel Date: Mon, 30 May 2016 23:06:28 +0200 Subject: [PATCH] Introduce LoaderSDT for SDT/RAW audio files --- rwlib/CMakeLists.txt | 2 + rwlib/source/loaders/LoaderSDT.cpp | 156 +++++++++++++++++++++++++++++ rwlib/source/loaders/LoaderSDT.hpp | 67 +++++++++++++ 3 files changed, 225 insertions(+) create mode 100644 rwlib/source/loaders/LoaderSDT.cpp create mode 100644 rwlib/source/loaders/LoaderSDT.hpp diff --git a/rwlib/CMakeLists.txt b/rwlib/CMakeLists.txt index 5ce3e96e..1bf2559e 100644 --- a/rwlib/CMakeLists.txt +++ b/rwlib/CMakeLists.txt @@ -29,6 +29,8 @@ SET(RWLIB_SOURCES "source/loaders/RWBinaryStream.hpp" "source/loaders/LoaderDFF.hpp" "source/loaders/LoaderDFF.cpp" + "source/loaders/LoaderSDT.hpp" + "source/loaders/LoaderSDT.cpp" "source/loaders/LoaderTXD.hpp" "source/loaders/LoaderTXD.cpp" diff --git a/rwlib/source/loaders/LoaderSDT.cpp b/rwlib/source/loaders/LoaderSDT.cpp new file mode 100644 index 00000000..22e23c31 --- /dev/null +++ b/rwlib/source/loaders/LoaderSDT.cpp @@ -0,0 +1,156 @@ +#include + +#include + +LoaderSDT::LoaderSDT() +: m_version(GTAIIIVC) +, m_assetCount(0) +{ + + } + +typedef struct { + char chunkId[4]; + uint32_t chunkSize; + char format[4]; + struct { + char id[4]; + uint32_t size; + uint16_t audioFormat; + uint16_t numChannels; + uint32_t sampleRate; + uint32_t byteRate; + uint16_t blockAlign; + uint16_t bitsPerSample; + } fmt; + struct { + char id[4]; + uint32_t size; + } data; +} WaveHeader; + +bool LoaderSDT::load(const std::string& filename) +{ + auto baseName = filename; + auto sdtName = baseName + ".SDT"; + auto rawName = baseName + ".RAW"; + + FILE* fp = fopen(sdtName.c_str(), "rb"); + if (fp) { + fseek(fp, 0, SEEK_END); + unsigned long fileSize = ftell(fp); + fseek(fp, 0, SEEK_SET); + + m_assetCount = fileSize / 20; + m_assets.resize(m_assetCount); + + if ((m_assetCount = fread(&m_assets[0], sizeof(LoaderSDTFile), m_assetCount, fp)) != fileSize / 20) { + m_assets.resize(m_assetCount); + std::cout << "Error reading records in SDT archive" << std::endl; + } + + fclose(fp); + m_archive = rawName; + return true; + } else { + return false; + } +} + +/// Get the information of a asset in the examining archive +bool LoaderSDT::findAssetInfo(size_t index, LoaderSDTFile& out) +{ + if (index < m_assets.size()) { + out = m_assets[index]; + return true; + } + return false; +} + +char* LoaderSDT::loadToMemory(size_t index, bool asWave) +{ + LoaderSDTFile assetInfo; + bool found = findAssetInfo(index, assetInfo); + + if(!found) { + std::cerr << "Asset " << std::to_string(index) << " not found!" << std::endl; + return nullptr; + } + + std::string rawName = m_archive; + + FILE* fp = fopen(rawName.c_str(), "rb"); + if (fp) { + char* raw_data; + char* sample_data; + if (asWave) { + raw_data = new char[sizeof(WaveHeader) + assetInfo.size]; + + WaveHeader* header = reinterpret_cast(raw_data); + memcpy(header->chunkId, "RIFF", 4); + header->chunkSize = sizeof(WaveHeader) - 8 + assetInfo.size; + memcpy(header->format, "WAVE", 4); + memcpy(header->fmt.id, "fmt ", 4); + header->fmt.size = sizeof(WaveHeader::fmt) - 8; + header->fmt.audioFormat = 1; // PCM + header->fmt.numChannels = 1; // Mono + header->fmt.sampleRate = assetInfo.sampleRate; + header->fmt.byteRate = assetInfo.sampleRate * 2; + header->fmt.blockAlign = 2; + header->fmt.bitsPerSample = 16; + memcpy(header->data.id, "data", 4); + header->data.size = assetInfo.size; + + sample_data = raw_data + sizeof(WaveHeader); + } else { + raw_data = new char[assetInfo.size]; + sample_data = raw_data; + } + + fseek(fp, assetInfo.offset, SEEK_SET); + if (fread(sample_data, 1, assetInfo.size, fp) != assetInfo.size) { + std::cerr << "Error reading asset " << std::to_string(index) << std::endl; + } + + fclose(fp); + return raw_data; + } + else + return 0; +} + +/// Writes the contents of assetname to filename +bool LoaderSDT::saveAsset(size_t index, const std::string& filename, bool asWave) +{ + char* raw_data = loadToMemory(index, asWave); + if(!raw_data) + return false; + + FILE* dumpFile = fopen(filename.c_str(), "wb"); + if(dumpFile) { + LoaderSDTFile asset; + if(findAssetInfo(index, asset)) { + fwrite(raw_data, 1, asset.size + (asWave ? sizeof(WaveHeader) : 0), dumpFile); + printf("=> SDT: Saved %zu to disk with filename %s\n", index, filename.c_str()); + } + fclose(dumpFile); + + delete[] raw_data; + return true; + } else { + delete[] raw_data; + return false; + } +} + +/// Get the information of an asset by its index +const LoaderSDTFile &LoaderSDT::getAssetInfoByIndex(size_t index) const +{ + return m_assets[index]; +} + + +uint32_t LoaderSDT::getAssetCount() const +{ + return m_assetCount; +} diff --git a/rwlib/source/loaders/LoaderSDT.hpp b/rwlib/source/loaders/LoaderSDT.hpp new file mode 100644 index 00000000..61afbf98 --- /dev/null +++ b/rwlib/source/loaders/LoaderSDT.hpp @@ -0,0 +1,67 @@ +#pragma once +#ifndef _LOADERSDT_HPP_ +#define _LOADERSDT_HPP_ + +#include +#include +#include + +/// \brief Points to one file within the archive +class LoaderSDTFile +{ +public: + uint32_t offset; // offset of audio file in sfx.raw + uint32_t size; // size of audio file in bytes + uint32_t sampleRate; // the speed of audio + uint32_t loopStart; /// loop start, where looping would begin relative to audio file's position, 0 for beginning of audio file + uint32_t loopEnd; /// where looping would end relative to audio file's position, -1 for end of audio file +}; + +/** + \class LoaderSDT + \brief Parses the structure of GTA .SDT archives and loads the files in it +*/ +class LoaderSDT +{ +public: + /// Multiple versions of .SDT files + enum Versions + { + GTA2, + GTAIIIVC ///< GTA III and GTA VC archives -- only this one is implemented + }; + + /// Construct + LoaderSDT(); + + /// Load the structure of the archive + /// Omit the extension in filename + bool load(const std::string& filename); + + /// Load a file from the archive to memory and pass a pointer to it + /// Warning: Please delete[] the memory in the end. + /// Warning: Returns NULL (0) if by any reason it can't load the file + char* loadToMemory(size_t index, bool asWave = true); + + /// Writes the contents of index to filename + bool saveAsset(size_t index, const std::string& filename, bool asWave = true); + + /// Get the information of an asset in the examining archive + bool findAssetInfo(size_t index, LoaderSDTFile& out); + + /// Get the information of an asset by its index + const LoaderSDTFile &getAssetInfoByIndex(size_t index) const; + + /// Returns the number of asset files in the archive + uint32_t getAssetCount() const; + +private: + Versions m_version; ///< Version of this SDT archive + uint32_t m_assetCount; ///< Number of assets in the current archive + std::string m_archive; ///< Path to the archive being used (no extension) + + std::vector m_assets; ///< Asset info of the archive +}; + + +#endif // LoaderSDT_h__