161 lines
5.6 KiB
C
161 lines
5.6 KiB
C
/**
|
|
* Copyright (c) 2026 Dominic Masters
|
|
*
|
|
* This software is released under the MIT License.
|
|
* https://opensource.org/licenses/MIT
|
|
*/
|
|
|
|
#pragma once
|
|
#include "error/error.h"
|
|
#include <gccore.h>
|
|
|
|
// GameCube always uses this device (slot A + slot B). On Wii it's also used
|
|
// when DUSK_SAVE_WII_METHOD_CARD is selected - libogc's CARD API
|
|
// transparently redirects to Wii's NAND-based GameCube memory card
|
|
// emulation, so the exact same code path works on both.
|
|
#ifndef SAVE_DEVICE_DOLPHIN_GAME_CODE
|
|
#define SAVE_DEVICE_DOLPHIN_GAME_CODE "DUSK"
|
|
#endif
|
|
|
|
// Memory cards have no rename primitive and files can't be resized once
|
|
// created, so a single "current" file can't be replaced atomically the way
|
|
// the other backends do. Instead saveDeviceDolphinCardDataWrite() ping-pongs
|
|
// between these two fixed files - every store targets whichever one is NOT
|
|
// the currently valid+newest copy (by saveDeviceRawIsValid()'s generation
|
|
// counter), so a crash mid-write can only ever corrupt the copy nobody
|
|
// would load from; the other one stays intact as the fallback.
|
|
#ifndef SAVE_DEVICE_DOLPHIN_CARD_FILENAME_0
|
|
#define SAVE_DEVICE_DOLPHIN_CARD_FILENAME_0 "DUSKSAVE0"
|
|
#endif
|
|
#ifndef SAVE_DEVICE_DOLPHIN_CARD_FILENAME_1
|
|
#define SAVE_DEVICE_DOLPHIN_CARD_FILENAME_1 "DUSKSAVE1"
|
|
#endif
|
|
|
|
typedef struct {
|
|
// Set from this device's index into SAVE.devices - device 0 is
|
|
// CARD_SLOTA, device 1 is CARD_SLOTB.
|
|
int32_t channel;
|
|
uint8_t cardBuffer[CARD_WORKAREA] __attribute__((aligned(32)));
|
|
bool_t mounted;
|
|
} savedeviceplatform_t;
|
|
|
|
typedef struct savedevice_s savedevice_t;
|
|
|
|
/**
|
|
* Initializes the save device platform.
|
|
*
|
|
* @param device The save device platform to initialize.
|
|
* @return Error state if any.
|
|
*/
|
|
errorret_t saveDeviceDolphinCardInit(savedevice_t *device);
|
|
|
|
/**
|
|
* Updates the save device platform.
|
|
*
|
|
* @param device The save device platform to update.
|
|
* @return Error state if any.
|
|
*/
|
|
errorret_t saveDeviceDolphinCardUpdate(savedevice_t *device);
|
|
|
|
/**
|
|
* Requests the device to check its availability, this will call the callback
|
|
* whence completed. Mounts the memory card in this device's slot and
|
|
* confirms there's at least one free block and one free file entry left to
|
|
* write a save into.
|
|
*
|
|
* @param device The save device platform to check availability.
|
|
*/
|
|
void saveDeviceDolphinCardCheckAvailability(savedevice_t *device);
|
|
|
|
/**
|
|
* Maps a libogc CARD_ERROR_* result (from CARD_Probe or CARD_Mount) to the
|
|
* matching reasonKey.
|
|
*
|
|
* @param result The card_errors result code to map.
|
|
* @return The matching reasonKey, or a generic fallback if unrecognized.
|
|
*/
|
|
const char_t *saveDeviceDolphinCardErrorReasonKey(const int32_t result);
|
|
|
|
/**
|
|
* Returns whether the mounted memory card in the given slot has at least
|
|
* one block of free space and one free file entry remaining, by summing
|
|
* the block usage of every file already on the card (CARD_GetDirectory)
|
|
* against the card's total block count (CARD_GetBlockCount) - libogc
|
|
* doesn't expose a single "free blocks remaining" call.
|
|
*
|
|
* @param channel The CARD slot (CARD_SLOTA or CARD_SLOTB) to check. Must
|
|
* already be mounted.
|
|
* @return True if there's room to create at least one more save file.
|
|
*/
|
|
bool_t saveDeviceDolphinCardHasFreeSpace(const int32_t channel);
|
|
|
|
/**
|
|
* Disposes of the save device platform.
|
|
*
|
|
* @param device The save device platform to dispose.
|
|
* @return Error state if any.
|
|
*/
|
|
errorret_t saveDeviceDolphinCardDispose(savedevice_t *device);
|
|
|
|
// One probe result for one of the two ping-pong slots.
|
|
typedef struct {
|
|
bool_t valid;
|
|
uint32_t generation;
|
|
uint8_t *buffer;// only set if keepBuffer was requested, else NULL
|
|
uint32_t len;
|
|
} savedevicedolphincardslot_t;
|
|
|
|
/**
|
|
* Opens the given card file (if it exists) and validates it as a raw save
|
|
* blob via saveDeviceRawIsValid(). CARD_Read requires a 32-byte aligned
|
|
* buffer, so the read (and any buffer kept for the caller) uses memoryAlign
|
|
* rather than memoryAllocate. If keepBuffer is false, or the file doesn't
|
|
* exist/validate, no buffer is left allocated.
|
|
*
|
|
* @param channel The CARD slot (CARD_SLOTA or CARD_SLOTB) to probe.
|
|
* @param filename The card file name to open.
|
|
* @param keepBuffer Whether to keep the read buffer allocated on success.
|
|
* @return The probe result - valid is false if the file doesn't exist or
|
|
* doesn't validate as a raw save blob.
|
|
*/
|
|
savedevicedolphincardslot_t saveDeviceDolphinCardProbeSlot(
|
|
const int32_t channel,
|
|
const char_t *filename,
|
|
const bool_t keepBuffer
|
|
);
|
|
|
|
/**
|
|
* Writes the combined save data blob out to whichever of the two fixed
|
|
* card files (see SAVE_DEVICE_DOLPHIN_CARD_FILENAME_0/1 above) is not
|
|
* currently the valid+newest copy - ping-pong in place of the atomic
|
|
* rename the other backends use, since the CARD API has neither a rename
|
|
* nor a way to resize an existing file.
|
|
*
|
|
* @param device The save device to write to.
|
|
* @param buffer The raw bytes to write.
|
|
* @param size The number of bytes to write.
|
|
* @return Error state if any.
|
|
*/
|
|
errorret_t saveDeviceDolphinCardDataWrite(
|
|
savedevice_t *device,
|
|
const uint8_t *buffer,
|
|
size_t size
|
|
);
|
|
|
|
/**
|
|
* Reads whichever of the two fixed card files is valid and has the higher
|
|
* generation counter. If neither file exists or validates, this is not an
|
|
* error - *outBuffer is set to NULL and *outSize to 0.
|
|
*
|
|
* @param device The save device to read from.
|
|
* @param outBuffer Receives a memoryAlign'd (32-byte) buffer the caller
|
|
* must free with memoryFree, or NULL if nothing has been saved yet.
|
|
* @param outSize Receives the number of bytes in *outBuffer.
|
|
* @return Error state if any.
|
|
*/
|
|
errorret_t saveDeviceDolphinCardDataRead(
|
|
savedevice_t *device,
|
|
uint8_t **outBuffer,
|
|
size_t *outSize
|
|
);
|