mirror of
https://github.com/GearsProgress/Poke_Transporter_GB.git
synced 2026-10-23 07:53:18 -05:00
137 lines
5.2 KiB
C++
137 lines
5.2 KiB
C++
#ifndef _FILECONTAINERREADER_H
|
|
#define _FILECONTAINERREADER_H
|
|
|
|
#include "typeDefs.h"
|
|
|
|
#define FILE_NAME_LENGTH 16
|
|
#define DEFAULT_CHUNK_SIZE 2048
|
|
|
|
/**
|
|
* @brief This class provides functionality to read files from the file container format
|
|
* created by the make-file-container tool.
|
|
*
|
|
* This format consists of multiple compressed chunks.
|
|
*
|
|
* It is done this way to keep the decompression buffer small (because we can't afford to consume
|
|
* too much IWRAM) but still allow for large file containers and bundling files together. (which you
|
|
* might want to do to improve compression ratios) The chunk size can be configured when creating
|
|
* the file container, and the FileContainerReader will read and decompress chunks on demand when
|
|
* seeking or reading data.
|
|
*/
|
|
class FileContainerReader
|
|
{
|
|
public:
|
|
FileContainerReader(const u8 **chunkList, u32 chunkCount, u32 chunkSize = DEFAULT_CHUNK_SIZE);
|
|
~FileContainerReader();
|
|
|
|
/**
|
|
* @brief This function initializes the FileContainerReader. It should be called before any
|
|
* other function is used. The reason for this initialization step is to give you control over
|
|
* the moment when the first chunk is decompressed. This can be useful if you want to perform
|
|
* some setup or checks before the decompression process begins. By calling init() explicitly,
|
|
* you can ensure that the FileContainerReader is ready to handle file reading operations at the
|
|
* appropriate time in your application.
|
|
*
|
|
* Note: because of this, we also only pass the decompression buffer here
|
|
* If the decompression buffer is not large enough to hold the chunks,
|
|
* the function will return false and the FileContainerReader will not be initialized. In this
|
|
* case, you should provide a larger decompression buffer and call init() again.
|
|
*
|
|
* If it DOES return true, please note that the FileContainerReader will keep using the
|
|
* decompressionBuffer_ until it gets destroyed.
|
|
*/
|
|
bool init(u8 *decompressionBuffer, u32 decompressionBufferSize);
|
|
|
|
/**
|
|
* @brief Returns the number of files stored in the file container.
|
|
*/
|
|
u32 getNumberOfFiles() const;
|
|
|
|
/**
|
|
* @brief IF the file container stores the file names,
|
|
* this function returns a pointer to the file name of the file at the given index.
|
|
* Otherwise, it returns nullptr.
|
|
*/
|
|
const char *getFileName(u32 fileIndex);
|
|
|
|
/**
|
|
* @brief This function returns the size of the file at the given index.
|
|
*/
|
|
u32 getFileSize(u32 fileIndex) const;
|
|
|
|
/**
|
|
* @brief This function seeks to the beginning of the file at the given index.
|
|
*/
|
|
void seekToFile(u32 fileIndex);
|
|
|
|
/**
|
|
* @brief Read data from the current position in the file container into the provided buffer.
|
|
*/
|
|
void read(u8 *buffer, u32 size);
|
|
|
|
/**
|
|
* @brief Gives you a direct pointer to the specified file in the decompression buffer.
|
|
* (unsafe!)
|
|
*
|
|
* This is useful to use data directly from the decompression buffer without having to allocate
|
|
* and copy another buffer. This is essential in high memory pressure scenarios, such as
|
|
* mystery_gift_builder. We can't afford to keep multiple linebuffers in memory in addition to
|
|
* the decompression buffer and all the other variables there.
|
|
*
|
|
* WARNING: this is only safe if you know the file is stored fully in the current chunk, because
|
|
* files may span multiple chunks. Or if the container only consists of a single chunk. Pointers
|
|
* acquired this way will become stale whenever a seek is done to a position in a different
|
|
* chunk, as that will cause the decompression buffer to be overwritten with the new chunk's
|
|
* data.
|
|
*/
|
|
u8 *getPointerToFileInDecompressionBuffer(u32 fileIndex);
|
|
|
|
/**
|
|
* @brief Combines seekToFile and read().
|
|
* Just a convenience function to make our code shorter :-)
|
|
*/
|
|
void seekAndRead(u32 fileIndex, u8 *buffer, u32 size);
|
|
|
|
/**
|
|
* @brief Even shorter variant of seekAndRead that reads the entire file at once.
|
|
* But it assumes you have provided a large enough buffer to hold the entire file.
|
|
*
|
|
* (useful for text table reading)
|
|
*/
|
|
void readFile(u32 fileIndex, u8 *buffer);
|
|
|
|
protected:
|
|
private:
|
|
/**
|
|
* @brief This function calculates the file offset for the specified file index
|
|
* The file offset is determined by reading the file index (which contains the file entry sizes)
|
|
* and summing up the sizes of all previous entries + header size + optional name table size
|
|
* (if names are stored).
|
|
*/
|
|
u32 getFileOffset(u32 fileIndex) const;
|
|
|
|
/**
|
|
* @brief This function seeks to the specified absolute offset in the file container data.
|
|
* Absolute offset refers to an offset across the file container chunks.
|
|
*/
|
|
void seek(u32 offset);
|
|
|
|
/**
|
|
* @brief This function just uncompresses the chunk at the specified index
|
|
* into decompressionBuffer_.
|
|
*/
|
|
void uncompressChunk(u32 chunkIndex);
|
|
|
|
char fileNameBuffer_[FILE_NAME_LENGTH + 1];
|
|
u8 *decompressionBuffer_;
|
|
const u8 **chunkList_;
|
|
u32 chunkCount_;
|
|
u32 chunkSize_;
|
|
u16 *fileIndex_;
|
|
u32 curChunkIndex_;
|
|
u32 curPos_;
|
|
u32 fileCount_;
|
|
bool hasNames_;
|
|
};
|
|
|
|
#endif |