/* * ChunkReader.h * ------------- * Purpose: An extended FileReader to read Iff-like chunk-based file structures. * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #pragma once #include "BuildSettings.h" #include "../common/FileReader.h" #include OPENMPT_NAMESPACE_BEGIN class ChunkReader : public FileReader { public: template ChunkReader(mpt::span bytedata) : FileReader(bytedata) { } ChunkReader(const FileReader &other) : FileReader(other) { } ChunkReader(FileReader &&other) : FileReader(std::move(other)) { } template class Item { private: T chunkHeader; FileReader chunkData; public: Item(const T &header, FileReader &&data) : chunkHeader(header), chunkData(std::move(data)) { } Item(const Item &) = default; Item(Item &&) noexcept = default; const T &GetHeader() const { return chunkHeader; } const FileReader &GetData() const { return chunkData; } }; template class ChunkList : public std::vector> { public: typedef decltype(T().GetID()) id_type; // Check if the list contains a given chunk. bool ChunkExists(id_type id) const { return std::find_if(this->cbegin(), this->cend(), [&id](const Item &item) { return item.GetHeader().GetID() == id; }) != this->cend(); } // Retrieve the first chunk with a given ID. FileReader GetChunk(id_type id) const { auto item = std::find_if(this->cbegin(), this->cend(), [&id](const Item &item) { return item.GetHeader().GetID() == id; }); if(item != this->cend()) return item->GetData(); return FileReader(); } // Retrieve all chunks with a given ID. std::vector GetAllChunks(id_type id) const { std::vector result; for(const auto &item : *this) { if(item.GetHeader().GetID() == id) { result.push_back(item.GetData()); } } return result; } }; // Read a single "T" chunk. // T is required to have the methods GetID() and GetLength(). // GetLength() must return the chunk size in bytes, and GetID() the chunk ID. template Item GetNextChunk(off_t padding) { T chunkHeader; off_t dataSize = 0; if(Read(chunkHeader)) { dataSize = chunkHeader.GetLength(); } Item resultItem(chunkHeader, ReadChunk(dataSize)); // Skip padding bytes if(padding != 0 && dataSize % padding != 0) { Skip(padding - (dataSize % padding)); } return resultItem; } // Read a series of "T" chunks until the end of file is reached. // T is required to have the methods GetID() and GetLength(). // GetLength() must return the chunk size in bytes, and GetID() the chunk ID. template ChunkList ReadChunks(off_t padding) { ChunkList result; while(CanRead(sizeof(T))) { result.push_back(GetNextChunk(padding)); } return result; } // Read a series of "T" chunks until a given chunk ID is found. // T is required to have the methods GetID() and GetLength(). // GetLength() must return the chunk size in bytes, and GetID() the chunk ID. template ChunkList ReadChunksUntil(off_t padding, decltype(T().GetID()) stopAtID) { ChunkList result; while(CanRead(sizeof(T))) { result.push_back(GetNextChunk(padding)); if(result.back().GetHeader().GetID() == stopAtID) { break; } } return result; } }; OPENMPT_NAMESPACE_END