Skip to main content
The save data system provides a robust API for managing game saves with support for save lists, icons, metadata, and automatic error handling.

Overview

The save data API (sysutil/save.h) supports:
  • Multiple save slots with user selection
  • Save data icons and screenshots
  • Metadata (title, subtitle, description)
  • Automatic space checking
  • Corruption detection
  • Protected files

Save System Architecture

The save system uses a callback-based architecture with three main callbacks:
  1. List Callback - Display and select save slots
  2. Status Callback - Configure save metadata and check status
  3. File Callback - Read/write files
All save operations run asynchronously. You must continue calling sysUtilCheckCallback() during save operations.

Quick Start

1

Create Memory Container

Allocate memory for save operations (typically 5MB):
#include <sys/memory.h>
#include <sysutil/save.h>

sys_mem_container_t container;
s32 ret = sysMemContainerCreate(&container, 5 * 1024 * 1024);
if (ret != 0) {
    printf("Failed to create container: %d\n", ret);
    return -1;
}
2

Configure List Settings

Define how saves are sorted and filtered:
sysSaveListSettings listSettings;
memset(&listSettings, 0, sizeof(sysSaveListSettings));
listSettings.sortType = SYS_SAVE_SORT_TYPE_TIMESTAMP;
listSettings.sortOrder = SYS_SAVE_SORT_ORDER_DESCENDING;
listSettings.pathPrefix = "MYGAME-SAVE"; // Save directory prefix
listSettings.reserved = NULL;
3

Configure Buffer Settings

Allocate buffers for directory and file lists:
#define MAX_DIRECTORIES 100
#define MAX_FILES 3
#define BUFFER_SIZE (MAX_DIRECTORIES * sizeof(sysSaveDirectoryList))

sysSaveBufferSettings bufferSettings;
memset(&bufferSettings, 0, sizeof(sysSaveBufferSettings));
bufferSettings.maxDirectories = MAX_DIRECTORIES;
bufferSettings.maxFiles = MAX_FILES;
bufferSettings.bufferSize = BUFFER_SIZE;
bufferSettings.buffer = malloc(BUFFER_SIZE);
4

Implement Callbacks

See the callback examples below for detailed implementations.
5

Call Save/Load Function

// To save
ret = sysSaveListSave2(
    SYS_SAVE_CURRENT_VERSION,
    &listSettings,
    &bufferSettings,
    list_callback,
    status_callback,
    file_callback,
    container,
    NULL  // user_data
);

// To load
ret = sysSaveListLoad2(
    SYS_SAVE_CURRENT_VERSION,
    &listSettings,
    &bufferSettings,
    list_callback,
    status_callback,
    file_callback,
    container,
    NULL  // user_data
);

if (ret == SYS_SAVE_RETURN_DONE) {
    printf("Save/Load completed successfully\n");
} else if (ret == SYS_SAVE_RETURN_CANCELED) {
    printf("Operation canceled by user\n");
}
6

Cleanup

free(bufferSettings.buffer);
sysMemContainerDestroy(container);

List Callback

The list callback displays available saves and allows creating new saves:
void save_list_callback(
    sysSaveCallbackResult *result,
    sysSaveListIn *in,
    sysSaveListOut *out
) {
    printf("Found %d save directories\n", in->maxDirectories);
    
    // List all saves
    for (int i = 0; i < in->numDirectories; i++) {
        printf("  Save: %s [%s]\n",
            in->directoryList[i].directoryName,
            in->directoryList[i].listParameter);
    }
    
    // Set up output
    memset(out, 0, sizeof(sysSaveListOut));
    out->focus = SYS_SAVE_FOCUS_POSITION_LIST_HEAD;
    out->numDirectories = in->numDirectories;
    out->directoryList = in->directoryList;
    
    // If saving and not at max, allow creating new save
    if (is_saving && out->numDirectories < MAX_DIRECTORIES) {
        // Find unused directory name
        char *dir = malloc(SYS_SAVE_MAX_DIRECTORY_NAME);
        for (int i = 0; i < 100; i++) {
            snprintf(dir, SYS_SAVE_MAX_DIRECTORY_NAME, "MYGAME-SAVE%d", i);
            
            // Check if name is already used
            int found = 0;
            for (int j = 0; j < in->numDirectories; j++) {
                if (strcmp(dir, in->directoryList[j].directoryName) == 0) {
                    found = 1;
                    break;
                }
            }
            
            if (!found) {
                // Set up new save option
                sysSaveNewSaveGame *newSave = &new_save_data;
                sysSaveNewSaveGameIcon *newIcon = &new_save_icon;
                
                memset(newSave, 0, sizeof(sysSaveNewSaveGame));
                memset(newIcon, 0, sizeof(sysSaveNewSaveGameIcon));
                
                newSave->position = SYS_SAVE_NEW_SAVE_POSITION_TOP;
                newSave->directoryName = dir;
                
                if (icon_data && icon_size > 0) {
                    newSave->icon = newIcon;
                    newIcon->iconBufferSize = icon_size;
                    newIcon->iconBuffer = icon_data;
                }
                
                out->newSaveGame = newSave;
                break;
            }
        }
    }
    
    result->result = SYS_SAVE_CALLBACK_RESULT_CONTINUE;
}

Status Callback

The status callback configures save metadata and checks available space:
void save_status_callback(
    sysSaveCallbackResult *result,
    sysSaveStatusIn *in,
    sysSaveStatusOut *out
) {
    printf("Save status callback\n");
    printf("  Free space: %d KB\n", in->freeSpaceKB);
    printf("  Is new: %d\n", in->isNew);
    printf("  Directory: %s\n", in->directoryStatus.directoryName);
    
    // Set up output
    result->result = SYS_SAVE_CALLBACK_RESULT_CONTINUE;
    out->setParam = &in->getParam;
    out->recreateMode = SYS_SAVE_RECREATE_MODE_DELETE;
    
    // Check free space (assume we need 1MB)
    int needed_kb = 1024 + in->systemSizeKB;
    int available_kb = in->freeSpaceKB + in->sizeKB;
    
    if (available_kb < needed_kb) {
        result->result = SYS_SAVE_CALLBACK_RESULT_NO_SPACE_LEFT;
        result->missingSpaceKB = needed_kb - available_kb;
        return;
    }
    
    // Set metadata
    strncpy(in->getParam.title, "My Game Save", SYS_SAVE_MAX_TITLE);
    strncpy(in->getParam.subtitle, "Level 5", SYS_SAVE_MAX_SUBTITLE);
    strncpy(in->getParam.detail,
            "Character: Hero\n"
            "HP: 100\n"
            "Level: 5\n",
            SYS_SAVE_MAX_DETAIL);
    
    save_mode = SAVE_MODE_ICON; // Start with icon
}

File Callback

The file callback handles reading and writing individual files:
enum SaveMode {
    SAVE_MODE_ICON,
    SAVE_MODE_SCREENSHOT,
    SAVE_MODE_DATA,
    SAVE_MODE_DONE
};

void save_file_callback(
    sysSaveCallbackResult *result,
    sysSaveFileIn *in,
    sysSaveFileOut *out
) {
    printf("File callback - mode: %d\n", save_mode);
    printf("  Last operation size: %d bytes\n", in->previousOperationResultSize);
    
    memset(out, 0, sizeof(sysSaveFileOut));
    
    switch (save_mode) {
        case SAVE_MODE_ICON:
            printf("Saving icon\n");
            out->fileOperation = SYS_SAVE_FILE_OPERATION_WRITE;
            out->fileType = SYS_SAVE_FILETYPE_CONTENT_ICON0;
            out->size = icon_size;
            out->bufferSize = icon_size;
            out->buffer = icon_data;
            
            result->result = SYS_SAVE_CALLBACK_RESULT_CONTINUE;
            result->incrementProgress = 30;
            save_mode = SAVE_MODE_SCREENSHOT;
            break;
            
        case SAVE_MODE_SCREENSHOT:
            printf("Saving screenshot\n");
            out->fileOperation = SYS_SAVE_FILE_OPERATION_WRITE;
            out->fileType = SYS_SAVE_FILETYPE_CONTENT_PIC1;
            out->size = screenshot_size;
            out->bufferSize = screenshot_size;
            out->buffer = screenshot_data;
            
            result->result = SYS_SAVE_CALLBACK_RESULT_CONTINUE;
            result->incrementProgress = 30;
            save_mode = SAVE_MODE_DATA;
            break;
            
        case SAVE_MODE_DATA:
            if (is_saving) {
                printf("Writing save data\n");
                out->fileOperation = SYS_SAVE_FILE_OPERATION_WRITE;
            } else {
                printf("Reading save data\n");
                out->fileOperation = SYS_SAVE_FILE_OPERATION_READ;
            }
            
            out->filename = "GAMEDATA";
            out->fileType = SYS_SAVE_FILETYPE_STANDARD_FILE;
            out->size = save_data_size;
            out->bufferSize = save_data_size;
            out->buffer = save_data_buffer;
            
            result->result = SYS_SAVE_CALLBACK_RESULT_CONTINUE;
            result->incrementProgress = 100;
            save_mode = SAVE_MODE_DONE;
            break;
            
        case SAVE_MODE_DONE:
        default:
            result->result = SYS_SAVE_CALLBACK_RESULT_DONE;
            
            // Verify loaded data
            if (!is_saving && in->previousOperationResultSize != save_data_size) {
                result->result = SYS_SAVE_CALLBACK_RESULT_CORRUPTED;
            }
            break;
    }
}

File Types

The save system supports different file types:
File TypeDescription
SYS_SAVE_FILETYPE_STANDARD_FILERegular save data files
SYS_SAVE_FILETYPE_PROTECTED_FILECopy-protected files
SYS_SAVE_FILETYPE_CONTENT_ICON0Save icon (PNG)
SYS_SAVE_FILETYPE_CONTENT_ICON1Alternative icon
SYS_SAVE_FILETYPE_CONTENT_PIC1Screenshot (PNG)
SYS_SAVE_FILETYPE_CONTENT_SND0Sound file
Icons and screenshots should be PNG format. The system automatically resizes them for display in the save menu.

Auto Save/Load

For simpler save operations without user selection:
ret = sysSaveAutoSave2(
    SYS_SAVE_CURRENT_VERSION,
    "MYGAME-AUTOSAVE",  // Directory name
    SYS_SAVE_ERROR_DIALOG_SHOW,
    &bufferSettings,
    status_callback,
    file_callback,
    container,
    NULL
);

Fixed Save/Load

Use fixed callbacks when you already know the save directory:
void fixed_callback(
    sysSaveCallbackResult *result,
    sysSaveListIn *in,
    sysSaveFixedOut *out
) {
    memset(out, 0, sizeof(sysSaveFixedOut));
    out->directoryName = "MYGAME-SAVE0";
    result->result = SYS_SAVE_CALLBACK_RESULT_CONTINUE;
}

ret = sysSaveFixedSave2(
    SYS_SAVE_CURRENT_VERSION,
    &listSettings,
    &bufferSettings,
    fixed_callback,
    status_callback,
    file_callback,
    container,
    NULL
);

Error Handling

Callback results control flow and error display:
ResultDescription
SYS_SAVE_CALLBACK_RESULT_CONTINUEContinue to next callback
SYS_SAVE_CALLBACK_RESULT_DONEOperation complete
SYS_SAVE_CALLBACK_RESULT_NO_SPACE_LEFTInsufficient space
SYS_SAVE_CALLBACK_RESULT_ERRORGeneric error
SYS_SAVE_CALLBACK_RESULT_CORRUPTEDSave data corrupted
SYS_SAVE_CALLBACK_RESULT_NOT_FOUNDSave not found
SYS_SAVE_CALLBACK_RESULT_ERROR_CUSTOMCustom error message

Custom Error Messages

if (custom_error_occurred) {
    result->result = SYS_SAVE_CALLBACK_RESULT_ERROR_CUSTOM;
    result->customErrorMessage = "Failed to save: Invalid game state";
}

Sort and Focus Options

listSettings.sortType = SYS_SAVE_SORT_TYPE_TIMESTAMP;
listSettings.sortOrder = SYS_SAVE_SORT_ORDER_DESCENDING; // Newest first

Return Values

The save/load functions return:
Return ValueDescription
SYS_SAVE_RETURN_DONESuccess
SYS_SAVE_RETURN_CANCELEDUser canceled
SYS_SAVE_RETURN_ERROR_CALLBACKCallback error
SYS_SAVE_RETURN_ERROR_HDD_ERRORHard drive error
SYS_SAVE_RETURN_ERROR_INTERNALInternal error
SYS_SAVE_RETURN_ERROR_INVALID_ARGInvalid argument
SYS_SAVE_RETURN_ERROR_NO_SPACE_LEFTInsufficient space
SYS_SAVE_RETURN_ERROR_CORRUPTEDCorrupted data
SYS_SAVE_RETURN_ERROR_ALREADY_IN_USESave in use

Complete Example

See the full implementation in samples/sys/save/source/main.c which demonstrates:
  • Creating save directories with icons
  • Saving multiple file types
  • Loading and verifying save data
  • Handling errors and user cancellation
  • Using threaded save operations

Best Practices

  • Always check free space before saving
  • Allocate sufficient memory container (5MB recommended)
  • Use unique directory name prefixes for your game
  • Set appropriate metadata for save selection UI
  • Handle all callback results properly
  • Continue calling sysUtilCheckCallback() during operations
  • Free allocated buffers after operations complete
Save Directory Names:
  • Must be uppercase
  • Maximum 32 characters
  • Should include your game’s title ID or unique prefix
  • Example: MYGAME-SAVE0, BLUS12345-AUTO
Icons and Screenshots:
  • Use PNG format
  • Recommended icon size: 320x176
  • Recommended screenshot size: 480x272
  • Store in save directory as ICON0.PNG and PIC1.PNG

Build docs developers (and LLMs) love