libpokemegb/include/RomReader.h
Philippe Symons a61b727023 Add support for the gen II localizations
Note: for Korean games, specifically, I did not implement the Korean character set
(nor do I plan to)

The reason is that it is entirely different and more complex to implement than the other
character sets. I'm not personally invested nor interested enough to go through the hassle.

This affects the pokémon names that will be displayed when using libpokemegb. Instead as
a fallback, it will return "poke-<number>" for these roms.

Feel free to contribute proper Korean character set (and pokémon names) support!
2024-11-20 23:46:27 +01:00

205 lines
6.4 KiB
C++

#ifndef ROMREADER_H
#define ROMREADER_H
#include "common.h"
#include <cstdint>
class IRomReader
{
public:
virtual ~IRomReader();
/**
* @brief This function reads a byte, returns it and advances the internal pointer by 1 byte
*
* @return uint8_t
*/
virtual bool readByte(uint8_t& outByte) = 0;
/**
* @brief This function reads multiple bytes into the specified output buffer
*
* @return whether the operation was successful
*/
virtual bool read(uint8_t* outBuffer, uint32_t bytesToRead) = 0;
/**
* @brief This function keeps reading until either maxRead number of bytes have been read or the terminator has been encountered.
*/
virtual uint32_t readUntil(uint8_t* outBuffer, uint8_t terminator, uint32_t maxRead) = 0;
/**
* @brief Same as above but with multiple possible terminator characters
*/
virtual uint32_t readUntil(uint8_t* outBuffer, const uint8_t* terminatorList, uint8_t numTerminators, uint32_t maxRead) = 0;
/**
* @brief Reads a 16 bit unsigned integer from the rom at the current position.
* The Endianness enum indicates whether the field we're trying to read is actually stored in little endian or big endian form.
* Turns out there's a mix of them in the roms even though most fields are in little endian.
*
* We need to use this value to determine whether we should do a byteswap for the CPU we're running this program on.
*/
virtual bool readUint16(uint16_t& outByte, Endianness fieldEndianness = Endianness::LITTLE) = 0;
/**
* @brief See @readUint16. This is the same but then for uint32_t
*/
virtual bool readUint32(uint32_t& outByte, Endianness fieldEndianness = Endianness::LITTLE) = 0;
/**
* @brief This function reads the current byte without advancing the internal pointer by 1 byte
*
* @return uint8_t
*/
virtual uint8_t peek() = 0;
/**
* @brief This function advances the internal pointer by the specified numBytes.
* This can be considered a relative seek
*
*/
virtual bool advance(uint32_t numBytes = 1) = 0;
/**
* @brief This function seeks to the specified absolute rom offset
*
* @param absoluteOffset
*/
virtual bool seek(uint32_t absoluteOffset) = 0;
/**
* @brief Returns the index of the current bank
*/
virtual uint8_t getCurrentBankIndex() const = 0;
/**
* @brief This function seeks to the start of the specified bank index
*
* @param bankIndex
* @return true
* @return false
*/
virtual bool seekToBank(uint8_t bankIndex) = 0;
/**
* @brief This function seeks to the specified rom pointer. Note this should be an unprocessed pointer as stored in the rom (except for endianness conversions done by readUint16)
* The reason is that the pointers stored in the rom assume the memory address of the rom location AFTER it has been mounted/mapped in the gameboy memory.
* Therefore this function will subtract 0x4000 from it (because 0x0000 - 0x4000 in gameboy address space is ALWAYS occupied by bank 0)
*
* If bankIndex is not specified (= set to 0xFF), we will seek to the pointer in the current bank
*/
virtual bool seekToRomPointer(uint16_t pointer, uint8_t bankIndex = 0xFF) = 0;
/**
* @brief This function searches for a sequence of bytes (the needle) in the buffer starting from
* the current internal position
* @return true we found the sequence of bytes and we've seeked toward this point
* @return false we didn't find the sequence of bytes anywhere
*/
virtual bool searchFor(const uint8_t* needle, uint32_t needleLength) = 0;
protected:
private:
};
class BaseRomReader : public IRomReader
{
public:
BaseRomReader();
virtual ~BaseRomReader();
bool readUint16(uint16_t& outByte, Endianness fieldEndianness = Endianness::LITTLE) override;
bool readUint32(uint32_t& outByte, Endianness fieldEndianness = Endianness::LITTLE) override;
bool seekToBank(uint8_t bankIndex) override;
bool seekToRomPointer(uint16_t pointer, uint8_t bankIndex = 0xFF) override;
uint32_t readUntil(uint8_t* outBuffer, uint8_t terminator, uint32_t maxRead) override;
uint32_t readUntil(uint8_t* outBuffer, const uint8_t* terminatorList, uint8_t numTerminators, uint32_t maxRead) override;
protected:
private:
const bool cpuLittleEndian_;
};
class BufferBasedRomReader : public BaseRomReader
{
public:
BufferBasedRomReader(const uint8_t* buffer, uint32_t bufferSize);
virtual ~BufferBasedRomReader();
/**
* @brief This function reads a byte, returns it and advances the internal pointer by 1 byte
*
* @return uint8_t
*/
bool readByte(uint8_t& outByte) override;
/**
* @brief This function reads multiple bytes into the specified output buffer
*
* @return whether the operation was successful
*/
bool read(uint8_t* outBuffer, uint32_t bytesToRead) override;
/**
* @brief This function reads the current byte without advancing the internal pointer by 1 byte
*
* @return uint8_t
*/
uint8_t peek() override;
/**
* @brief This function advances the internal pointer by 1 byte
*
*/
bool advance(uint32_t numBytes = 1) override;
/**
* @brief This function seeks to the specified absolute rom offset
*
* @param absoluteOffset
*/
bool seek(uint32_t absoluteOffset) override;
/**
* @brief This function searches for a sequence of bytes (the needle) in the buffer starting from
* the current internal position
* @return true we found the sequence of bytes and we've seeked toward this point
* @return false we didn't find the sequence of bytes anywhere
*/
bool searchFor(const uint8_t* needle, uint32_t needleLength) override;
uint8_t getCurrentBankIndex() const override;
protected:
private:
const uint8_t* const buffer_;
const uint8_t* const end_;
const uint8_t* cur_;
};
class BitReader
{
public:
BitReader(IRomReader& romReader);
/**
* @brief Read the specified number of bits
*
* @param numBits
* @return uint8_t
*/
uint8_t read(uint8_t numBits);
bool seek(uint32_t byteOffset, uint8_t bitOffset = 0);
protected:
private:
IRomReader& romReader_;
uint8_t bitIndex_;
};
bool readGameboyCartridgeHeader(IRomReader& romReader, GameboyCartridgeHeader& cartridgeHeader);
#endif