Save file update (incomplete)
This commit is contained in:
@@ -20,5 +20,6 @@ add_subdirectory(log)
|
||||
add_subdirectory(display)
|
||||
add_subdirectory(input)
|
||||
add_subdirectory(network)
|
||||
add_subdirectory(save)
|
||||
add_subdirectory(system)
|
||||
add_subdirectory(time)
|
||||
@@ -0,0 +1,11 @@
|
||||
# Copyright (c) 2026 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
# Sources
|
||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
savedolphin.c
|
||||
savestreamdolphin.c
|
||||
)
|
||||
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "save/save.h"
|
||||
#include "util/string.h"
|
||||
|
||||
static void _saveGetFileName(const uint8_t slot, char_t *out, const size_t max) {
|
||||
snprintf(out, max, "%s_%u", SAVE_DOLPHIN_GAME_CODE, (uint32_t)slot);
|
||||
}
|
||||
|
||||
errorret_t saveInitDolphin(void) {
|
||||
SAVE.platform.mounted = false;
|
||||
|
||||
int32_t result = CARD_Mount(
|
||||
SAVE_DOLPHIN_CHANNEL,
|
||||
SAVE.platform.cardBuffer,
|
||||
NULL
|
||||
);
|
||||
|
||||
if(result < 0) {
|
||||
errorThrow("Failed to mount memory card (error %d)", result);
|
||||
}
|
||||
|
||||
SAVE.platform.mounted = true;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveDisposeDolphin(void) {
|
||||
if(SAVE.platform.mounted) {
|
||||
CARD_Unmount(SAVE_DOLPHIN_CHANNEL);
|
||||
SAVE.platform.mounted = false;
|
||||
}
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveLoadDolphin(const uint8_t slot, savefile_t *file) {
|
||||
char_t fileName[SAVE_DOLPHIN_FILE_NAME_MAX];
|
||||
_saveGetFileName(slot, fileName, SAVE_DOLPHIN_FILE_NAME_MAX);
|
||||
|
||||
int32_t result = CARD_Open(SAVE_DOLPHIN_CHANNEL, fileName, &SAVE.platform.cardFile);
|
||||
if(result == CARD_ERROR_NOFILE) {
|
||||
file->exists = false;
|
||||
errorOk();
|
||||
}
|
||||
if(result < 0) {
|
||||
file->exists = false;
|
||||
errorThrow("Failed to open memory card file for slot %u (error %d)",
|
||||
(uint32_t)slot, result
|
||||
);
|
||||
}
|
||||
|
||||
void *buffer = memalign(32, SAVE_DOLPHIN_SECTOR_SIZE);
|
||||
if(!buffer) {
|
||||
CARD_Close(&SAVE.platform.cardFile);
|
||||
errorThrow("Failed to allocate memory card read buffer");
|
||||
}
|
||||
|
||||
result = CARD_Read(&SAVE.platform.cardFile, buffer, SAVE_DOLPHIN_SECTOR_SIZE, 0);
|
||||
CARD_Close(&SAVE.platform.cardFile);
|
||||
|
||||
if(result < 0) {
|
||||
free(buffer);
|
||||
file->exists = false;
|
||||
errorThrow("Failed to read memory card data for slot %u (error %d)",
|
||||
(uint32_t)slot, result
|
||||
);
|
||||
}
|
||||
|
||||
memoryCopy(file, buffer, sizeof(savefile_t));
|
||||
free(buffer);
|
||||
|
||||
file->exists = true;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveWriteDolphin(const uint8_t slot, const savefile_t *file) {
|
||||
char_t fileName[SAVE_DOLPHIN_FILE_NAME_MAX];
|
||||
_saveGetFileName(slot, fileName, SAVE_DOLPHIN_FILE_NAME_MAX);
|
||||
|
||||
void *buffer = memalign(32, SAVE_DOLPHIN_SECTOR_SIZE);
|
||||
if(!buffer) {
|
||||
errorThrow("Failed to allocate memory card write buffer");
|
||||
}
|
||||
memset(buffer, 0, SAVE_DOLPHIN_SECTOR_SIZE);
|
||||
memoryCopy(buffer, file, sizeof(savefile_t));
|
||||
|
||||
// Try open existing file first; create if absent.
|
||||
int32_t result = CARD_Open(SAVE_DOLPHIN_CHANNEL, fileName, &SAVE.platform.cardFile);
|
||||
if(result == CARD_ERROR_NOFILE) {
|
||||
result = CARD_Create(
|
||||
SAVE_DOLPHIN_CHANNEL,
|
||||
fileName,
|
||||
SAVE_DOLPHIN_SECTOR_SIZE,
|
||||
&SAVE.platform.cardFile
|
||||
);
|
||||
}
|
||||
|
||||
if(result < 0) {
|
||||
free(buffer);
|
||||
errorThrow("Failed to open/create memory card file for slot %u (error %d)",
|
||||
(uint32_t)slot, result
|
||||
);
|
||||
}
|
||||
|
||||
result = CARD_Write(&SAVE.platform.cardFile, buffer, SAVE_DOLPHIN_SECTOR_SIZE, 0);
|
||||
CARD_Close(&SAVE.platform.cardFile);
|
||||
free(buffer);
|
||||
|
||||
if(result < 0) {
|
||||
errorThrow("Failed to write memory card data for slot %u (error %d)",
|
||||
(uint32_t)slot, result
|
||||
);
|
||||
}
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveDeleteDolphin(const uint8_t slot) {
|
||||
char_t fileName[SAVE_DOLPHIN_FILE_NAME_MAX];
|
||||
_saveGetFileName(slot, fileName, SAVE_DOLPHIN_FILE_NAME_MAX);
|
||||
|
||||
int32_t result = CARD_Delete(SAVE_DOLPHIN_CHANNEL, fileName);
|
||||
if(result < 0 && result != CARD_ERROR_NOFILE) {
|
||||
errorThrow("Failed to delete memory card file for slot %u (error %d)",
|
||||
(uint32_t)slot, result
|
||||
);
|
||||
}
|
||||
|
||||
errorOk();
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* 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 "save/savefile.h"
|
||||
#include <gccore.h>
|
||||
|
||||
#define SAVE_DOLPHIN_FILE_NAME_MAX 32
|
||||
#define SAVE_DOLPHIN_SECTOR_SIZE 8192
|
||||
|
||||
#ifndef SAVE_DOLPHIN_GAME_CODE
|
||||
#define SAVE_DOLPHIN_GAME_CODE "DUSK"
|
||||
#endif
|
||||
|
||||
#ifndef SAVE_DOLPHIN_CHANNEL
|
||||
#define SAVE_DOLPHIN_CHANNEL CARD_SLOTA
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
card_file cardFile;
|
||||
uint8_t cardBuffer[CARD_WORKAREA] __attribute__((aligned(32)));
|
||||
bool_t mounted;
|
||||
} savedolphin_t;
|
||||
|
||||
/**
|
||||
* Initializes the save system on GameCube (memory card slot A by default).
|
||||
*
|
||||
* @return An error code if initialization fails.
|
||||
*/
|
||||
errorret_t saveInitDolphin(void);
|
||||
|
||||
/**
|
||||
* Disposes of the save system on GameCube.
|
||||
*
|
||||
* @return An error code if disposal fails.
|
||||
*/
|
||||
errorret_t saveDisposeDolphin(void);
|
||||
|
||||
/**
|
||||
* Loads a save file from the memory card for the given slot.
|
||||
*
|
||||
* @param slot The save slot index.
|
||||
* @param file Output save file data.
|
||||
* @return An error code if the load fails.
|
||||
*/
|
||||
errorret_t saveLoadDolphin(const uint8_t slot, savefile_t *file);
|
||||
|
||||
/**
|
||||
* Writes a save file to the memory card for the given slot.
|
||||
*
|
||||
* @param slot The save slot index.
|
||||
* @param file Save file data to write.
|
||||
* @return An error code if the write fails.
|
||||
*/
|
||||
errorret_t saveWriteDolphin(const uint8_t slot, const savefile_t *file);
|
||||
|
||||
/**
|
||||
* Deletes the save file for the given slot from the memory card.
|
||||
*
|
||||
* @param slot The save slot index.
|
||||
* @return An error code if the delete fails.
|
||||
*/
|
||||
errorret_t saveDeleteDolphin(const uint8_t slot);
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "save/savedolphin.h"
|
||||
#include "save/savestreamdolphin.h"
|
||||
|
||||
typedef savedolphin_t saveplatform_t;
|
||||
typedef savestreamdolphin_t saveplatformstream_t;
|
||||
|
||||
#define saveInitPlatform saveInitDolphin
|
||||
#define saveDisposePlatform saveDisposeDolphin
|
||||
#define saveDeletePlatform saveDeleteDolphin
|
||||
|
||||
#define saveStreamOpenReadPlatform(stream, slot) \
|
||||
saveStreamOpenReadDolphin(&(stream)->platform, &(stream)->found, slot)
|
||||
#define saveStreamOpenWritePlatform(stream, slot) \
|
||||
saveStreamOpenWriteDolphin(&(stream)->platform, slot)
|
||||
#define saveStreamClosePlatform(stream) \
|
||||
saveStreamCloseDolphin(&(stream)->platform)
|
||||
#define saveStreamReadBytesPlatform(stream, buf, len) \
|
||||
saveStreamReadBytesDolphin(&(stream)->platform, buf, len)
|
||||
#define saveStreamWriteBytesPlatform(stream, buf, len) \
|
||||
saveStreamWriteBytesDolphin(&(stream)->platform, buf, len)
|
||||
#define saveStreamSeekPlatform(stream, pos) \
|
||||
saveStreamSeekDolphin(&(stream)->platform, pos)
|
||||
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "save/save.h"
|
||||
#include "save/savestreamdolphin.h"
|
||||
#include "util/memory.h"
|
||||
#include "util/string.h"
|
||||
|
||||
static void _saveStreamGetFileName(
|
||||
char_t *out, const size_t max, const uint8_t slot
|
||||
) {
|
||||
snprintf(out, max, "%s_%u", SAVE_DOLPHIN_GAME_CODE, (uint32_t)slot);
|
||||
}
|
||||
|
||||
errorret_t saveStreamOpenReadDolphin(
|
||||
savestreamdolphin_t *p, bool_t *found, const uint8_t slot
|
||||
) {
|
||||
char_t fileName[SAVE_DOLPHIN_FILE_NAME_MAX];
|
||||
_saveStreamGetFileName(fileName, SAVE_DOLPHIN_FILE_NAME_MAX, slot);
|
||||
|
||||
int32_t result = CARD_Open(
|
||||
SAVE_DOLPHIN_CHANNEL, fileName, &p->cardFile
|
||||
);
|
||||
if(result == CARD_ERROR_NOFILE) {
|
||||
*found = false;
|
||||
p->position = 0;
|
||||
p->writing = false;
|
||||
errorOk();
|
||||
}
|
||||
if(result < 0) {
|
||||
*found = false;
|
||||
errorThrow("Failed to open memory card file for slot %u (error %d)",
|
||||
(uint32_t)slot, result
|
||||
);
|
||||
}
|
||||
|
||||
result = CARD_Read(&p->cardFile, p->buffer, SAVE_DOLPHIN_SECTOR_SIZE, 0);
|
||||
CARD_Close(&p->cardFile);
|
||||
if(result < 0) {
|
||||
*found = false;
|
||||
errorThrow("Failed to read memory card data for slot %u (error %d)",
|
||||
(uint32_t)slot, result
|
||||
);
|
||||
}
|
||||
|
||||
*found = true;
|
||||
p->position = 0;
|
||||
p->writing = false;
|
||||
p->slot = slot;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveStreamOpenWriteDolphin(
|
||||
savestreamdolphin_t *p, const uint8_t slot
|
||||
) {
|
||||
memoryZero(p->buffer, SAVE_DOLPHIN_SECTOR_SIZE);
|
||||
p->position = 0;
|
||||
p->writing = true;
|
||||
p->slot = slot;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
void saveStreamCloseDolphin(savestreamdolphin_t *p) {
|
||||
if(!p->writing) return;
|
||||
|
||||
char_t fileName[SAVE_DOLPHIN_FILE_NAME_MAX];
|
||||
_saveStreamGetFileName(fileName, SAVE_DOLPHIN_FILE_NAME_MAX, p->slot);
|
||||
|
||||
int32_t result = CARD_Open(SAVE_DOLPHIN_CHANNEL, fileName, &p->cardFile);
|
||||
if(result == CARD_ERROR_NOFILE) {
|
||||
CARD_Create(
|
||||
SAVE_DOLPHIN_CHANNEL, fileName, SAVE_DOLPHIN_SECTOR_SIZE, &p->cardFile
|
||||
);
|
||||
}
|
||||
|
||||
CARD_Write(&p->cardFile, p->buffer, SAVE_DOLPHIN_SECTOR_SIZE, 0);
|
||||
CARD_Close(&p->cardFile);
|
||||
}
|
||||
|
||||
errorret_t saveStreamReadBytesDolphin(
|
||||
savestreamdolphin_t *p, void *buf, const size_t len
|
||||
) {
|
||||
if(p->position + len > SAVE_DOLPHIN_SECTOR_SIZE) {
|
||||
errorThrow("Save stream read exceeds sector size");
|
||||
}
|
||||
memoryCopy(buf, p->buffer + p->position, len);
|
||||
p->position += len;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveStreamWriteBytesDolphin(
|
||||
savestreamdolphin_t *p, const void *buf, const size_t len
|
||||
) {
|
||||
if(p->position + len > SAVE_DOLPHIN_SECTOR_SIZE) {
|
||||
errorThrow("Save stream write exceeds sector size");
|
||||
}
|
||||
memoryCopy(p->buffer + p->position, buf, len);
|
||||
p->position += len;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveStreamSeekDolphin(savestreamdolphin_t *p, const size_t pos) {
|
||||
if(pos >= SAVE_DOLPHIN_SECTOR_SIZE) {
|
||||
errorThrow("Save stream seek out of range");
|
||||
}
|
||||
p->position = pos;
|
||||
errorOk();
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* 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 "save/savedolphin.h"
|
||||
#include <gccore.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct {
|
||||
/** libogc memory card file handle. */
|
||||
card_file cardFile;
|
||||
/** In-memory sector buffer; all reads and writes operate on this. */
|
||||
uint8_t buffer[SAVE_DOLPHIN_SECTOR_SIZE] __attribute__((aligned(32)));
|
||||
/** Current read/write position within buffer. */
|
||||
size_t position;
|
||||
/** True when opened for writing; flushes buffer to card on close. */
|
||||
bool_t writing;
|
||||
/** Slot index stored at open time so Close can derive the filename. */
|
||||
uint8_t slot;
|
||||
} savestreamdolphin_t;
|
||||
|
||||
/**
|
||||
* Opens a memory card slot for reading by loading its sector into buffer.
|
||||
*
|
||||
* @param p Stream to initialize.
|
||||
* @param found Set to true if the file exists, false if it does not.
|
||||
* @param slot Save slot index.
|
||||
* @return An error if reading the card fails for a reason other than
|
||||
* missing file.
|
||||
*/
|
||||
errorret_t saveStreamOpenReadDolphin(
|
||||
savestreamdolphin_t *p, bool_t *found, const uint8_t slot
|
||||
);
|
||||
|
||||
/**
|
||||
* Opens a memory card slot for writing by zeroing the sector buffer.
|
||||
* The buffer is flushed to the card when savestreamCloseDolphin is called.
|
||||
*
|
||||
* @param p Stream to initialize.
|
||||
* @param slot Save slot index.
|
||||
* @return An error if initialization fails.
|
||||
*/
|
||||
errorret_t saveStreamOpenWriteDolphin(
|
||||
savestreamdolphin_t *p, const uint8_t slot
|
||||
);
|
||||
|
||||
/**
|
||||
* Flushes the sector buffer to the memory card (write mode only) and
|
||||
* releases the card file handle.
|
||||
*
|
||||
* @param p Stream to close.
|
||||
*/
|
||||
void saveStreamCloseDolphin(savestreamdolphin_t *p);
|
||||
|
||||
/**
|
||||
* Copies len bytes from the sector buffer at the current position into buf.
|
||||
*
|
||||
* @param p Active stream.
|
||||
* @param buf Destination buffer.
|
||||
* @param len Number of bytes to read.
|
||||
* @return An error if the read would exceed the sector size.
|
||||
*/
|
||||
errorret_t saveStreamReadBytesDolphin(
|
||||
savestreamdolphin_t *p, void *buf, const size_t len
|
||||
);
|
||||
|
||||
/**
|
||||
* Copies len bytes from buf into the sector buffer at the current position.
|
||||
*
|
||||
* @param p Active stream.
|
||||
* @param buf Source buffer.
|
||||
* @param len Number of bytes to write.
|
||||
* @return An error if the write would exceed the sector size.
|
||||
*/
|
||||
errorret_t saveStreamWriteBytesDolphin(
|
||||
savestreamdolphin_t *p, const void *buf, const size_t len
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets the current read/write position within the sector buffer.
|
||||
*
|
||||
* @param p Active stream.
|
||||
* @param pos Target byte offset from the start of the sector.
|
||||
* @return An error if pos is out of range.
|
||||
*/
|
||||
errorret_t saveStreamSeekDolphin(savestreamdolphin_t *p, const size_t pos);
|
||||
Reference in New Issue
Block a user