#pragma once #include "YBaseLib/ByteStream.h" #include "types.h" #include #include #include class HDDImage { public: using SectorIndex = u32; static constexpr u32 InvalidSectorNumber = UINT32_C(0xFFFFFFFF); static constexpr u32 DefaultSectorSize = 4096; static std::unique_ptr Create(const char* filename, u64 size_in_bytes, u32 sector_size = DefaultSectorSize); static std::unique_ptr Open(const char* filename, u32 sector_size = DefaultSectorSize); ~HDDImage(); const u64 GetImageSize() const { return m_image_size; } const u32 GetSectorSize() const { return m_sector_size; } const u32 GetSectorCount() const { return m_sector_count; } void Read(void* buffer, u64 offset, u32 size); void Write(const void* buffer, u64 offset, u32 size); /// Erases the current replay log, and replaces it with the log from the specified stream. bool LoadState(ByteStream* stream); /// Copies the current state of the replay log to the specified stream, so it can be restored later. bool SaveState(ByteStream* stream); /// Flushes any buffered sectors to the backing file/log. void Flush(); /// Commits all changes made in the replay log to the base image. void CommitLog(); /// Erases any changes made in the replay log, restoring the image to its base state. void RevertLog(); private: using LogSectorMap = std::vector; struct SectorBuffer { std::unique_ptr data; SectorIndex sector_number = InvalidSectorNumber; bool in_log = false; bool dirty = false; }; HDDImage(const std::string filename, ByteStream* base_stream, ByteStream* log_stream, u64 size, u32 sector_size, u32 sector_count, u32 version_number, LogSectorMap log_sector_map); static ByteStream* CreateLogFile(const char* filename, bool truncate_existing, bool atomic_update, u64 image_size, u32 sector_size, u32& num_sectors, u32 version_number, LogSectorMap& sector_map); static ByteStream* OpenLogFile(const char* filename, u64 image_size, u32& sector_size, u32& num_sectors, u32& version_number, LogSectorMap& sector_map); // Returns the offset in the image (either log or base) for the specified sector. u64 GetFileOffset(SectorIndex sector_index) const { return static_cast(sector_index) * static_cast(m_sector_size); } // Returns whether the specified sector is in the log (true), or in the base image (false). bool IsSectorInLog(SectorIndex sector_index) const { return (m_log_sector_map[sector_index] != InvalidSectorNumber); } // Currently, we only have one sector open. But we could change this in the future. SectorBuffer& GetSector(SectorIndex sector_index); void LoadSector(SectorBuffer& buf, SectorIndex sector_index); void LoadSectorFromImage(SectorBuffer& buf, SectorIndex sector_index); void LoadSectorFromLog(SectorBuffer& buf, SectorIndex sector_index); void WriteSectorToLog(SectorBuffer& buf); void ReleaseSector(SectorBuffer& buf); void ReleaseAllSectors(); std::string m_filename; ByteStream* m_base_stream; ByteStream* m_log_stream; u64 m_image_size; u32 m_sector_size; u32 m_sector_count; u32 m_version_number; LogSectorMap m_log_sector_map; SectorBuffer m_current_sector; };