Add save file validation when backing up or restoring a save

This commit is contained in:
Philippe Symons
2026-01-07 18:30:06 +01:00
parent d106863b50
commit 0ff264c13c
6 changed files with 1094 additions and 909 deletions

View File

@@ -1,55 +1,59 @@
#ifndef _DATACOPYSCENE_H
#define _DATACOPYSCENE_H
#include "scenes/SceneWithProgressBar.h"
#include "transferpak/TransferPakManager.h"
#include "transferpak/TransferPakRomReader.h"
#include "transferpak/TransferPakSaveManager.h"
#include "transferpak/TransferPakDataCopier.h"
enum class DataCopyOperation
{
BACKUP_SAVE,
BACKUP_ROM,
RESTORE_SAVE,
WIPE_SAVE
};
typedef struct DataCopySceneContext
{
DataCopyOperation operation;
ManagedString saveToRestorePath;
} DataCopySceneContext;
class DataCopyScene : public SceneWithProgressBar
{
public:
DataCopyScene(SceneDependencies& deps, void* context);
virtual ~DataCopyScene();
void init() override;
void destroy() override;
void processUserInput() override;
void render(RDPQGraphics& gfx, const Rectangle& sceneBounds) override;
void onDialogDone();
protected:
void setupDialog(DialogWidgetStyle& style) override;
void setupProgressBar(ProgressBarWidgetStyle& style) override;
private:
TransferPakRomReader romReader_;
TransferPakSaveManager saveManager_;
DataCopySceneContext* sceneContext_;
ITransferPakDataCopySource* copySource_;
ITransferPakDataCopyDestination* copyDestination_;
TransferPakDataCopier* copier_;
sprite_t* dialogWidgetSprite_;
sprite_t* progressBackgroundSprite_;
DialogData diag_;
uint32_t totalBytesToCopy_;
};
void deleteDataCopySceneContext(void* context);
#ifndef _DATACOPYSCENE_H
#define _DATACOPYSCENE_H
#include "scenes/SceneWithProgressBar.h"
#include "transferpak/TransferPakManager.h"
#include "transferpak/TransferPakRomReader.h"
#include "transferpak/TransferPakSaveManager.h"
#include "transferpak/TransferPakDataCopier.h"
enum class DataCopyOperation
{
BACKUP_SAVE,
BACKUP_ROM,
RESTORE_SAVE,
WIPE_SAVE
};
typedef struct DataCopySceneContext
{
DataCopyOperation operation;
ManagedString saveToRestorePath;
} DataCopySceneContext;
class DataCopyScene : public SceneWithProgressBar
{
public:
DataCopyScene(SceneDependencies& deps, void* context);
virtual ~DataCopyScene();
void init() override;
void destroy() override;
void processUserInput() override;
void render(RDPQGraphics& gfx, const Rectangle& sceneBounds) override;
void onDialogDone();
protected:
void setupDialog(DialogWidgetStyle& style) override;
void setupProgressBar(ProgressBarWidgetStyle& style) override;
private:
TransferPakRomReader romReader_;
TransferPakSaveManager saveManager_;
DataCopySceneContext* sceneContext_;
ITransferPakDataCopySource* copySource_;
ITransferPakDataCopyDestination* copyDestination_;
TransferPakDataCopier* copier_;
sprite_t* dialogWidgetSprite_;
sprite_t* progressBackgroundSprite_;
DialogData diag_;
uint32_t totalBytesToCopy_;
bool needsValidation_;
bool isValidating_;
bool shouldResetRTC_;
char savOutputPath_[4096];
};
void deleteDataCopySceneContext(void* context);
#endif

View File

@@ -1,199 +1,222 @@
#ifndef _TRANSFERPAKDATACOPIER_H
#define _TRANSFERPAKDATACOPIER_H
#include <cstdio>
#include <cstdint>
class TransferPakRomReader;
class TransferPakSaveManager;
/**
* This interface is used to define a transfer pak datasource to copy from
*/
class ITransferPakDataCopySource
{
public:
virtual ~ITransferPakDataCopySource();
virtual bool readyForTransfer() const = 0;
virtual uint16_t getCurrentBankIndex() const = 0;
virtual uint32_t getNumberOfBytesRead() const = 0;
virtual uint32_t read(uint8_t *buffer, uint32_t bytesToRead) = 0;
protected:
private:
};
/**
* This class implements the ITransferPakDataCopySource interface with TransferPakRomReader
*/
class TransferPakRomReaderCopySource : public ITransferPakDataCopySource
{
public:
TransferPakRomReaderCopySource(TransferPakRomReader &romReader);
virtual ~TransferPakRomReaderCopySource();
bool readyForTransfer() const override;
uint16_t getCurrentBankIndex() const override;
uint32_t getNumberOfBytesRead() const override;
uint32_t read(uint8_t *buffer, uint32_t bytesToRead) override;
protected:
private:
TransferPakRomReader &romReader_;
uint32_t bytesRead_;
};
/**
* This class implements the ITransferPakDataCopySource interface with TransferPakRomReader
*/
class TransferPakSaveManagerCopySource : public ITransferPakDataCopySource
{
public:
TransferPakSaveManagerCopySource(TransferPakSaveManager &saveManager);
virtual ~TransferPakSaveManagerCopySource();
bool readyForTransfer() const override;
uint16_t getCurrentBankIndex() const override;
uint32_t getNumberOfBytesRead() const override;
uint32_t read(uint8_t *buffer, uint32_t bytesToRead) override;
protected:
private:
TransferPakSaveManager &saveManager_;
uint32_t bytesRead_;
};
class TransferPakFileCopySource : public ITransferPakDataCopySource
{
public:
TransferPakFileCopySource(const char *filePath);
virtual ~TransferPakFileCopySource();
bool readyForTransfer() const override;
uint16_t getCurrentBankIndex() const override;
uint32_t getNumberOfBytesRead() const override;
uint32_t read(uint8_t *buffer, uint32_t bytesToRead) override;
protected:
private:
FILE *inputFile_;
uint32_t bytesRead_;
};
class TransferPakNullCopySource : public ITransferPakDataCopySource
{
public:
TransferPakNullCopySource();
virtual ~TransferPakNullCopySource();
bool readyForTransfer() const override;
uint16_t getCurrentBankIndex() const override;
uint32_t getNumberOfBytesRead() const override;
uint32_t read(uint8_t *buffer, uint32_t bytesToRead) override;
protected:
private:
uint32_t bytesRead_;
};
/**
* @brief This interface is used to define a transfer pak data destination to copy to
*
*/
class ITransferPakDataCopyDestination
{
public:
virtual ~ITransferPakDataCopyDestination();
virtual bool readyForTransfer() const = 0;
virtual uint16_t getCurrentBankIndex() const = 0;
virtual uint32_t getNumberOfBytesWritten() const = 0;
virtual uint32_t write(uint8_t *buffer, uint32_t bytesToWrite) = 0;
virtual void close() = 0;
protected:
private:
};
class TransferPakSaveManagerDestination : public ITransferPakDataCopyDestination
{
public:
TransferPakSaveManagerDestination(TransferPakSaveManager& saveManager);
virtual ~TransferPakSaveManagerDestination();
bool readyForTransfer() const override;
uint16_t getCurrentBankIndex() const override;
uint32_t getNumberOfBytesWritten() const override;
uint32_t write(uint8_t *buffer, uint32_t bytesToWrite) override;
void close() override;
protected:
private:
TransferPakSaveManager& saveManager_;
uint32_t bytesWritten_;
};
class TransferPakFileCopyDestination : public ITransferPakDataCopyDestination
{
public:
TransferPakFileCopyDestination(const char *pathOnSDCard, bool resetRTC = false);
virtual ~TransferPakFileCopyDestination();
bool readyForTransfer() const override;
uint16_t getCurrentBankIndex() const override;
uint32_t getNumberOfBytesWritten() const override;
uint32_t write(uint8_t *buffer, uint32_t bytesToWrite) override;
void close() override;
protected:
private:
FILE *outputFile_;
uint32_t bytesWritten_;
bool resetRTC_;
};
/**
* This class directs the copy process from the source to the specified output file
*
* It exists to abstract the source (rom/SRAM) and control the copy flow/speed and to
* allow us to give UI feedback on the copy process.
*
* After all: I found that the transfer pak is able to read 32 bytes every 1,5-2milliseconds
* We don't want the UI to remain frozen during that time.
* (source: http://n64devkit.square7.ch/pro-man/pro26/26-07.htm)
*/
class TransferPakDataCopier
{
public:
TransferPakDataCopier(ITransferPakDataCopySource &source, ITransferPakDataCopyDestination &destination);
~TransferPakDataCopier();
uint16_t getCurrentBankIndex() const;
uint32_t getNumberOfBytesRead() const;
size_t copyChunk(uint32_t numBytesToCopy);
protected:
private:
ITransferPakDataCopySource &source_;
ITransferPakDataCopyDestination &destination_;
};
#ifndef _TRANSFERPAKDATACOPIER_H
#define _TRANSFERPAKDATACOPIER_H
#include <cstdio>
#include <cstdint>
class TransferPakRomReader;
class TransferPakSaveManager;
/**
* This interface is used to define a transfer pak datasource to copy from
*/
class ITransferPakDataCopySource
{
public:
virtual ~ITransferPakDataCopySource();
virtual bool readyForTransfer() const = 0;
virtual uint16_t getCurrentBankIndex() const = 0;
virtual uint32_t getNumberOfBytesRead() const = 0;
virtual uint32_t read(uint8_t *buffer, uint32_t bytesToRead) = 0;
protected:
private:
};
/**
* This class implements the ITransferPakDataCopySource interface with TransferPakRomReader
*/
class TransferPakRomReaderCopySource : public ITransferPakDataCopySource
{
public:
TransferPakRomReaderCopySource(TransferPakRomReader &romReader);
virtual ~TransferPakRomReaderCopySource();
bool readyForTransfer() const override;
uint16_t getCurrentBankIndex() const override;
uint32_t getNumberOfBytesRead() const override;
uint32_t read(uint8_t *buffer, uint32_t bytesToRead) override;
protected:
private:
TransferPakRomReader &romReader_;
uint32_t bytesRead_;
};
/**
* This class implements the ITransferPakDataCopySource interface with TransferPakRomReader
*/
class TransferPakSaveManagerCopySource : public ITransferPakDataCopySource
{
public:
TransferPakSaveManagerCopySource(TransferPakSaveManager &saveManager);
virtual ~TransferPakSaveManagerCopySource();
bool readyForTransfer() const override;
uint16_t getCurrentBankIndex() const override;
uint32_t getNumberOfBytesRead() const override;
uint32_t read(uint8_t *buffer, uint32_t bytesToRead) override;
protected:
private:
TransferPakSaveManager &saveManager_;
uint32_t bytesRead_;
};
class TransferPakFileCopySource : public ITransferPakDataCopySource
{
public:
TransferPakFileCopySource(const char *filePath);
virtual ~TransferPakFileCopySource();
bool readyForTransfer() const override;
uint16_t getCurrentBankIndex() const override;
uint32_t getNumberOfBytesRead() const override;
uint32_t read(uint8_t *buffer, uint32_t bytesToRead) override;
protected:
private:
FILE *inputFile_;
uint32_t bytesRead_;
};
class TransferPakNullCopySource : public ITransferPakDataCopySource
{
public:
TransferPakNullCopySource();
virtual ~TransferPakNullCopySource();
bool readyForTransfer() const override;
uint16_t getCurrentBankIndex() const override;
uint32_t getNumberOfBytesRead() const override;
uint32_t read(uint8_t *buffer, uint32_t bytesToRead) override;
protected:
private:
uint32_t bytesRead_;
};
/**
* @brief This interface is used to define a transfer pak data destination to copy to
*
*/
class ITransferPakDataCopyDestination
{
public:
virtual ~ITransferPakDataCopyDestination();
virtual bool readyForTransfer() const = 0;
virtual uint16_t getCurrentBankIndex() const = 0;
virtual uint32_t getNumberOfBytesWritten() const = 0;
virtual uint32_t write(uint8_t *buffer, uint32_t bytesToWrite) = 0;
virtual void close() = 0;
protected:
private:
};
class TransferPakSaveManagerDestination : public ITransferPakDataCopyDestination
{
public:
TransferPakSaveManagerDestination(TransferPakSaveManager& saveManager);
virtual ~TransferPakSaveManagerDestination();
bool readyForTransfer() const override;
uint16_t getCurrentBankIndex() const override;
uint32_t getNumberOfBytesWritten() const override;
uint32_t write(uint8_t *buffer, uint32_t bytesToWrite) override;
void close() override;
protected:
private:
TransferPakSaveManager& saveManager_;
uint32_t bytesWritten_;
};
class TransferPakFileCopyDestination : public ITransferPakDataCopyDestination
{
public:
TransferPakFileCopyDestination(const char *pathOnSDCard);
virtual ~TransferPakFileCopyDestination();
bool readyForTransfer() const override;
uint16_t getCurrentBankIndex() const override;
uint32_t getNumberOfBytesWritten() const override;
uint32_t write(uint8_t *buffer, uint32_t bytesToWrite) override;
void close() override;
protected:
private:
FILE *outputFile_;
uint32_t bytesWritten_;
};
/** This class is used to validate the copied data. */
class FileValidationCopyDestination : public ITransferPakDataCopyDestination
{
public:
FileValidationCopyDestination(const char *pathOnSDCard);
virtual ~FileValidationCopyDestination();
bool readyForTransfer() const override;
uint16_t getCurrentBankIndex() const override;
uint32_t getNumberOfBytesWritten() const override;
uint32_t write(uint8_t *buffer, uint32_t bytesToWrite) override;
void close() override;
bool isDataValid() const;
protected:
private:
FILE *inputFile_;
uint32_t bytesValidated_;
bool isValid_;
};
/**
* This class directs the copy process from the source to the specified output file
*
* It exists to abstract the source (rom/SRAM) and control the copy flow/speed and to
* allow us to give UI feedback on the copy process.
*
* After all: I found that the transfer pak is able to read 32 bytes every 1,5-2milliseconds
* We don't want the UI to remain frozen during that time.
* (source: http://n64devkit.square7.ch/pro-man/pro26/26-07.htm)
*/
class TransferPakDataCopier
{
public:
TransferPakDataCopier(ITransferPakDataCopySource &source, ITransferPakDataCopyDestination &destination);
~TransferPakDataCopier();
uint16_t getCurrentBankIndex() const;
uint32_t getNumberOfBytesRead() const;
size_t copyChunk(uint32_t numBytesToCopy);
protected:
private:
ITransferPakDataCopySource &source_;
ITransferPakDataCopyDestination &destination_;
};
#endif