Fixed save crash
Backported from branch ac2 (commit 85b61097) - CARD_Mount was being called
without CARD_Init first, leaving per-channel control blocks and the DSP
unlock sequence unset. On real Dolphin/hardware this surfaced as a hard
MMIO crash instead of a clean CARD_ERROR_* failure.
Co-Authored-By: Dominic Masters <dominic@domsplace.com>
This commit is contained in:
@@ -9,23 +9,48 @@
|
|||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include "util/string.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) {
|
errorret_t saveInitDolphin(void) {
|
||||||
SAVE.platform.mounted = false;
|
SAVE.platform.mounted = false;
|
||||||
|
|
||||||
int32_t result = CARD_Mount(
|
// Must run once before any other CARD_* call: sets up card_inited,
|
||||||
SAVE_DOLPHIN_CHANNEL,
|
// the per-channel control blocks (wait queues, alarms) CARD_Mount reads,
|
||||||
SAVE.platform.cardBuffer,
|
// and initializes the DSP (needed for the card unlock sequence).
|
||||||
NULL
|
// Skipping this leaves those structures unset, so CARD_Mount ends up
|
||||||
);
|
// touching hardware state that was never brought up -- e.g. Dolphin's
|
||||||
|
// "Trying to read 32 bits from an invalid MMIO" error -- rather than
|
||||||
|
// failing cleanly with a CARD_ERROR_* code.
|
||||||
|
int32_t result = CARD_Init(SAVE_DOLPHIN_GAME_CODE, NULL);
|
||||||
|
if(result < 0) {
|
||||||
|
errorThrow("Failed to initialize memory card subsystem: %s (%d)",
|
||||||
|
saveCardErrorStringDolphin(result), result
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
result = CARD_Mount(
|
||||||
|
SAVE_DOLPHIN_CHANNEL,
|
||||||
|
SAVE.platform.cardBuffer,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
} while(result == CARD_ERROR_BUSY);
|
||||||
|
|
||||||
|
// Special-case the failures a player can actually act on; everything
|
||||||
|
// else falls through to the generic, fully-enumerated message below.
|
||||||
|
switch(result) {
|
||||||
|
case CARD_ERROR_NOCARD:
|
||||||
|
errorThrow("No memory card inserted in the slot");
|
||||||
|
case CARD_ERROR_WRONGDEVICE:
|
||||||
|
errorThrow("Unsupported device inserted in the memory card slot");
|
||||||
|
case CARD_ERROR_BROKEN:
|
||||||
|
errorThrow("Memory card is damaged or unformatted");
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if(result < 0) {
|
if(result < 0) {
|
||||||
errorThrow("Failed to mount memory card (error %d)", result);
|
errorThrow("Failed to mount memory card: %s (%d)",
|
||||||
|
saveCardErrorStringDolphin(result), result
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
SAVE.platform.mounted = true;
|
SAVE.platform.mounted = true;
|
||||||
@@ -42,19 +67,22 @@ errorret_t saveDisposeDolphin(void) {
|
|||||||
|
|
||||||
errorret_t saveLoadDolphin(const uint8_t slot, savefile_t *file) {
|
errorret_t saveLoadDolphin(const uint8_t slot, savefile_t *file) {
|
||||||
char_t fileName[SAVE_DOLPHIN_FILE_NAME_MAX];
|
char_t fileName[SAVE_DOLPHIN_FILE_NAME_MAX];
|
||||||
_saveGetFileName(slot, fileName, SAVE_DOLPHIN_FILE_NAME_MAX);
|
saveGetFileNameDolphin(slot, fileName, SAVE_DOLPHIN_FILE_NAME_MAX);
|
||||||
|
|
||||||
int32_t result = CARD_Open(
|
int32_t result;
|
||||||
SAVE_DOLPHIN_CHANNEL, fileName, &SAVE.platform.cardFile
|
do {
|
||||||
);
|
result = CARD_Open(
|
||||||
|
SAVE_DOLPHIN_CHANNEL, fileName, &SAVE.platform.cardFile
|
||||||
|
);
|
||||||
|
} while(result == CARD_ERROR_BUSY);
|
||||||
if(result == CARD_ERROR_NOFILE) {
|
if(result == CARD_ERROR_NOFILE) {
|
||||||
file->exists = false;
|
file->exists = false;
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
if(result < 0) {
|
if(result < 0) {
|
||||||
file->exists = false;
|
file->exists = false;
|
||||||
errorThrow("Failed to open memory card file for slot %u (error %d)",
|
errorThrow("Failed to open memory card file for slot %u: %s (%d)",
|
||||||
(uint32_t)slot, result
|
(uint32_t)slot, saveCardErrorStringDolphin(result), result
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,16 +92,18 @@ errorret_t saveLoadDolphin(const uint8_t slot, savefile_t *file) {
|
|||||||
errorThrow("Failed to allocate memory card read buffer");
|
errorThrow("Failed to allocate memory card read buffer");
|
||||||
}
|
}
|
||||||
|
|
||||||
result = CARD_Read(
|
do {
|
||||||
&SAVE.platform.cardFile, buffer, SAVE_DOLPHIN_SECTOR_SIZE, 0
|
result = CARD_Read(
|
||||||
);
|
&SAVE.platform.cardFile, buffer, SAVE_DOLPHIN_SECTOR_SIZE, 0
|
||||||
|
);
|
||||||
|
} while(result == CARD_ERROR_BUSY);
|
||||||
CARD_Close(&SAVE.platform.cardFile);
|
CARD_Close(&SAVE.platform.cardFile);
|
||||||
|
|
||||||
if(result < 0) {
|
if(result < 0) {
|
||||||
memoryFree(buffer);
|
memoryFree(buffer);
|
||||||
file->exists = false;
|
file->exists = false;
|
||||||
errorThrow("Failed to read memory card data for slot %u (error %d)",
|
errorThrow("Failed to read memory card data for slot %u: %s (%d)",
|
||||||
(uint32_t)slot, result
|
(uint32_t)slot, saveCardErrorStringDolphin(result), result
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +116,7 @@ errorret_t saveLoadDolphin(const uint8_t slot, savefile_t *file) {
|
|||||||
|
|
||||||
errorret_t saveWriteDolphin(const uint8_t slot, const savefile_t *file) {
|
errorret_t saveWriteDolphin(const uint8_t slot, const savefile_t *file) {
|
||||||
char_t fileName[SAVE_DOLPHIN_FILE_NAME_MAX];
|
char_t fileName[SAVE_DOLPHIN_FILE_NAME_MAX];
|
||||||
_saveGetFileName(slot, fileName, SAVE_DOLPHIN_FILE_NAME_MAX);
|
saveGetFileNameDolphin(slot, fileName, SAVE_DOLPHIN_FILE_NAME_MAX);
|
||||||
|
|
||||||
void *buffer = memoryAlign(32, SAVE_DOLPHIN_SECTOR_SIZE);
|
void *buffer = memoryAlign(32, SAVE_DOLPHIN_SECTOR_SIZE);
|
||||||
if(!buffer) {
|
if(!buffer) {
|
||||||
@@ -96,34 +126,41 @@ errorret_t saveWriteDolphin(const uint8_t slot, const savefile_t *file) {
|
|||||||
memoryCopy(buffer, file, sizeof(savefile_t));
|
memoryCopy(buffer, file, sizeof(savefile_t));
|
||||||
|
|
||||||
// Try open existing file first; create if absent.
|
// Try open existing file first; create if absent.
|
||||||
int32_t result = CARD_Open(
|
int32_t result;
|
||||||
SAVE_DOLPHIN_CHANNEL, fileName, &SAVE.platform.cardFile
|
do {
|
||||||
);
|
result = CARD_Open(
|
||||||
if(result == CARD_ERROR_NOFILE) {
|
SAVE_DOLPHIN_CHANNEL, fileName, &SAVE.platform.cardFile
|
||||||
result = CARD_Create(
|
|
||||||
SAVE_DOLPHIN_CHANNEL,
|
|
||||||
fileName,
|
|
||||||
SAVE_DOLPHIN_SECTOR_SIZE,
|
|
||||||
&SAVE.platform.cardFile
|
|
||||||
);
|
);
|
||||||
|
} while(result == CARD_ERROR_BUSY);
|
||||||
|
if(result == CARD_ERROR_NOFILE) {
|
||||||
|
do {
|
||||||
|
result = CARD_Create(
|
||||||
|
SAVE_DOLPHIN_CHANNEL,
|
||||||
|
fileName,
|
||||||
|
SAVE_DOLPHIN_SECTOR_SIZE,
|
||||||
|
&SAVE.platform.cardFile
|
||||||
|
);
|
||||||
|
} while(result == CARD_ERROR_BUSY);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(result < 0) {
|
if(result < 0) {
|
||||||
memoryFree(buffer);
|
memoryFree(buffer);
|
||||||
errorThrow("Failed to open/create memory card file for slot %u (error %d)",
|
errorThrow("Failed to open/create memory card file for slot %u: %s (%d)",
|
||||||
(uint32_t)slot, result
|
(uint32_t)slot, saveCardErrorStringDolphin(result), result
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
result = CARD_Write(
|
do {
|
||||||
&SAVE.platform.cardFile, buffer, SAVE_DOLPHIN_SECTOR_SIZE, 0
|
result = CARD_Write(
|
||||||
);
|
&SAVE.platform.cardFile, buffer, SAVE_DOLPHIN_SECTOR_SIZE, 0
|
||||||
|
);
|
||||||
|
} while(result == CARD_ERROR_BUSY);
|
||||||
CARD_Close(&SAVE.platform.cardFile);
|
CARD_Close(&SAVE.platform.cardFile);
|
||||||
memoryFree(buffer);
|
memoryFree(buffer);
|
||||||
|
|
||||||
if(result < 0) {
|
if(result < 0) {
|
||||||
errorThrow("Failed to write memory card data for slot %u (error %d)",
|
errorThrow("Failed to write memory card data for slot %u: %s (%d)",
|
||||||
(uint32_t)slot, result
|
(uint32_t)slot, saveCardErrorStringDolphin(result), result
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,14 +169,52 @@ errorret_t saveWriteDolphin(const uint8_t slot, const savefile_t *file) {
|
|||||||
|
|
||||||
errorret_t saveDeleteDolphin(const uint8_t slot) {
|
errorret_t saveDeleteDolphin(const uint8_t slot) {
|
||||||
char_t fileName[SAVE_DOLPHIN_FILE_NAME_MAX];
|
char_t fileName[SAVE_DOLPHIN_FILE_NAME_MAX];
|
||||||
_saveGetFileName(slot, fileName, SAVE_DOLPHIN_FILE_NAME_MAX);
|
saveGetFileNameDolphin(slot, fileName, SAVE_DOLPHIN_FILE_NAME_MAX);
|
||||||
|
|
||||||
int32_t result = CARD_Delete(SAVE_DOLPHIN_CHANNEL, fileName);
|
int32_t result;
|
||||||
|
do {
|
||||||
|
result = CARD_Delete(SAVE_DOLPHIN_CHANNEL, fileName);
|
||||||
|
} while(result == CARD_ERROR_BUSY);
|
||||||
if(result < 0 && result != CARD_ERROR_NOFILE) {
|
if(result < 0 && result != CARD_ERROR_NOFILE) {
|
||||||
errorThrow("Failed to delete memory card file for slot %u (error %d)",
|
errorThrow("Failed to delete memory card file for slot %u: %s (%d)",
|
||||||
(uint32_t)slot, result
|
(uint32_t)slot, saveCardErrorStringDolphin(result), result
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void saveGetFileNameDolphin(
|
||||||
|
const uint8_t slot, char_t *out, const size_t max
|
||||||
|
) {
|
||||||
|
snprintf(out, max, "%s_%u", SAVE_DOLPHIN_GAME_CODE, (uint32_t)slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char_t *saveCardErrorStringDolphin(const int32_t result) {
|
||||||
|
switch(result) {
|
||||||
|
case CARD_ERROR_READY: return "card is ready";
|
||||||
|
case CARD_ERROR_UNLOCKED:
|
||||||
|
return "card is being unlocked or already unlocked";
|
||||||
|
case CARD_ERROR_BUSY: return "card is busy";
|
||||||
|
case CARD_ERROR_WRONGDEVICE: return "wrong device connected in slot";
|
||||||
|
case CARD_ERROR_NOCARD: return "no memory card in slot";
|
||||||
|
case CARD_ERROR_NOFILE: return "specified file not found";
|
||||||
|
case CARD_ERROR_IOERROR: return "internal EXI I/O error";
|
||||||
|
case CARD_ERROR_BROKEN:
|
||||||
|
return "directory structure or file entry broken";
|
||||||
|
case CARD_ERROR_EXIST:
|
||||||
|
return "file already exists with the specified parameters";
|
||||||
|
case CARD_ERROR_NOENT:
|
||||||
|
return "no empty block available to create the file";
|
||||||
|
case CARD_ERROR_INSSPACE:
|
||||||
|
return "not enough space to write file to memory card";
|
||||||
|
case CARD_ERROR_NOPERM:
|
||||||
|
return "not enough permissions to operate on the file";
|
||||||
|
case CARD_ERROR_LIMIT: return "card size limit reached";
|
||||||
|
case CARD_ERROR_NAMETOOLONG: return "filename too long";
|
||||||
|
case CARD_ERROR_ENCODING: return "font encoding PAL/SJIS mismatch";
|
||||||
|
case CARD_ERROR_CANCELED: return "card operation canceled";
|
||||||
|
case CARD_ERROR_FATAL_ERROR: return "fatal error, non-recoverable";
|
||||||
|
default: return "unknown card error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -66,3 +66,26 @@ errorret_t saveWriteDolphin(const uint8_t slot, const savefile_t *file);
|
|||||||
* @return An error code if the delete fails.
|
* @return An error code if the delete fails.
|
||||||
*/
|
*/
|
||||||
errorret_t saveDeleteDolphin(const uint8_t slot);
|
errorret_t saveDeleteDolphin(const uint8_t slot);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the memory card file name for a given save slot, from
|
||||||
|
* SAVE_DOLPHIN_GAME_CODE and the slot index.
|
||||||
|
*
|
||||||
|
* @param slot The save slot index.
|
||||||
|
* @param out Destination buffer for the file name.
|
||||||
|
* @param max Size of out, in bytes.
|
||||||
|
*/
|
||||||
|
void saveGetFileNameDolphin(
|
||||||
|
const uint8_t slot, char_t *out, const size_t max
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes a libogc CARD_ERROR_* result code (see
|
||||||
|
* https://libogc.devkitpro.org/group__card__errors.html), for logging
|
||||||
|
* alongside the raw numeric code.
|
||||||
|
*
|
||||||
|
* @param result The result code returned by a CARD_* libogc call.
|
||||||
|
* @return A human-readable description of the result code, or
|
||||||
|
* "unknown card error" if result doesn't match a known CARD_ERROR_* code.
|
||||||
|
*/
|
||||||
|
const char_t *saveCardErrorStringDolphin(const int32_t result);
|
||||||
|
|||||||
Reference in New Issue
Block a user