#include "nw4r/snd/snd_NandSoundArchive.h" #include #include namespace nw4r { namespace snd { class NandSoundArchive::NandFileStream : public ut::NandFileStream { public: NandFileStream(const NANDFileInfo* pFileInfo, u32 offset, u32 size); NandFileStream(const char* pPath, u32 offset, u32 size); virtual s32 Read(void* pDst, u32 size); // at 0x14 virtual void Seek(s32 offset, u32 origin); // at 0x44 virtual u32 Tell() const { return ut::NandFileStream::Tell() - mOffset; } // at 0x58 virtual u32 GetSize() const { return mSize; } // at 0x40 private: s32 mOffset; // at 0x16C s32 mSize; // at 0x170 }; NandSoundArchive::NandSoundArchive() : mOpen(false) {} NandSoundArchive::~NandSoundArchive() { Close(); } bool NandSoundArchive::Open(const char* pPath) { if (mOpen) { Close(); } if (NANDOpen(pPath, &mFileInfo, NAND_ACCESS_READ)) { return false; } mOpen = true; if (!LoadFileHeader()) { return false; } char extRoot[FILE_PATH_MAX]; char *pExtRoot = extRoot; // UB: (?) Array index 256 is past the end of the array char *pEnd = &extRoot[FILE_PATH_MAX + 1]; if (pPath[0] != '/' && pPath[0] != '\\') { char currentDir[64]; if (NANDGetCurrentDir(currentDir) != NAND_RESULT_OK) { return false; } char *pCurrentDir = currentDir; while (*pCurrentDir != '\0') { if (pExtRoot >= pEnd) { return false; } *pExtRoot++ = *pCurrentDir++; } if (pExtRoot >= pEnd) { return false; } *pExtRoot++ = '/'; } const char *pPath2 = pPath; const char *p3 = pPath; while (*pPath2 != '\0') { if (*pPath2 == '/' || *pPath2 == '\\') { while (p3 < pPath2) { if (pExtRoot >= pEnd) { return false; } *pExtRoot++ = *p3++; } } pPath2++; } if (pExtRoot >= pEnd) { return false; } *pExtRoot = '\0'; SetExternalFileRoot(extRoot); return true; } void NandSoundArchive::Close() { if (mOpen) { NANDClose(&mFileInfo); mOpen = false; } Shutdown(); } ut::FileStream* NandSoundArchive::OpenStream(void* pBuffer, int size, u32 offset, u32 length) const { if (!mOpen) { return NULL; } if (size < sizeof(NandFileStream)) { return NULL; } return new (pBuffer) NandFileStream(&mFileInfo, offset, length); } ut::FileStream* NandSoundArchive::OpenExtStream(void* pBuffer, int size, const char* pExtPath, u32 offset, u32 length) const { if (!mOpen) { return NULL; } if (size < sizeof(NandFileStream)) { return NULL; } NandFileStream* pExtStream = new (pBuffer) NandFileStream(pExtPath, offset, length); if (!pExtStream->IsAvailable()) { pExtStream->~NandFileStream(); return NULL; } return pExtStream; } int NandSoundArchive::detail_GetRequiredStreamBufferSize() const { return sizeof(NandFileStream); } bool NandSoundArchive::LoadFileHeader() { u8 headerArea[detail::SoundArchiveFile::HEADER_AREA_SIZE]; static const u32 headerAlignSize = ut::RoundUp(sizeof(detail::SoundArchiveFile::Header), 32); void* pFile = ut::RoundUp(headerArea, 32); if (NANDSeek(&mFileInfo, 0, NAND_SEEK_BEG) != NAND_RESULT_OK) { return false; } s32 bytesRead = NANDRead(&mFileInfo, pFile, headerAlignSize); if (bytesRead != headerAlignSize) { return false; } mFileReader.Init(pFile); Setup(&mFileReader); return true; } bool NandSoundArchive::LoadHeader(void* pBuffer, u32 size) { u32 infoSize = mFileReader.GetInfoChunkSize(); u32 infoOffsetU = mFileReader.GetInfoChunkOffset(); s32 infoOffset = *reinterpret_cast(&infoOffsetU); if (size < infoSize) { return false; } s32 currOffset = NANDSeek(&mFileInfo, infoOffset, NAND_SEEK_BEG); if (currOffset != infoOffset) { return false; } s32 bytesRead = NANDRead(&mFileInfo, pBuffer, infoSize); if (bytesRead != infoSize) { return false; } mFileReader.SetInfoChunk(pBuffer, infoSize); return true; } bool NandSoundArchive::LoadLabelStringData(void* pBuffer, u32 size) { u32 labelSize = mFileReader.GetLabelStringChunkSize(); u32 labelOffsetU = mFileReader.GetLabelStringChunkOffset(); s32 labelOffset = *reinterpret_cast(&labelOffsetU); if (size < labelSize) { return false; } s32 currOffset = NANDSeek(&mFileInfo, labelOffset, NAND_SEEK_BEG); if (currOffset != labelOffset) { return false; } s32 bytesRead = NANDRead(&mFileInfo, pBuffer, labelSize); if (bytesRead != labelSize) { return false; } mFileReader.SetStringChunk(pBuffer, labelSize); return true; } NandSoundArchive::NandFileStream::NandFileStream(const NANDFileInfo* pFileInfo, u32 offset, u32 size) : ut::NandFileStream(pFileInfo, NAND_ACCESS_READ, false), mOffset(offset), mSize(size) { if (IsAvailable()) { ut::NandFileStream::Seek(mOffset, SEEKORG_BEG); } } NandSoundArchive::NandFileStream::NandFileStream(const char* pPath, u32 offset, u32 size) : ut::NandFileStream(pPath, NAND_ACCESS_READ), mOffset(offset), mSize(size) { if (IsAvailable()) { ut::NandFileStream::Seek(mOffset, SEEKORG_BEG); } } s32 NandSoundArchive::NandFileStream::Read(void* pDst, u32 size) { u32 endOffset = mOffset + mSize; u32 startOffset = ut::NandFileStream::Tell(); if (startOffset + size > endOffset) { size = ut::RoundUp(endOffset - ut::NandFileStream::Tell(), 32); } s32 bytesRead = ut::NandFileStream::Read(pDst, size); DCStoreRange(pDst, size); return bytesRead; } void NandSoundArchive::NandFileStream::Seek(s32 offset, u32 origin) { switch (origin) { case SEEKORG_BEG: { offset += mOffset; break; } case SEEKORG_CUR: { offset += ut::NandFileStream::Tell(); break; } case SEEKORG_END: { offset = mOffset + mSize - offset; break; } default: { return; } } if (offset < mOffset) { offset = mOffset; } else if (offset > mOffset + mSize) { offset = mOffset + mSize; } ut::NandFileStream::Seek(offset, SEEKORG_BEG); } }}