Unify save system into one save.h/.c; diverge storage format per platform
Renames savefile_t to saveslot_t and folds last session's standalone settings.h/.c module back in as savemeta_t, so there's one save system (SAVE.slots[] + SAVE.meta) instead of two parallel ones - while letting each platform pick its own physical format for the two concepts: - Linux now writes human-editable JSON (slot0.json, settings.json, ...) via yyjson's mutable writer API, so players can hand-fix a bad setting. - PSP folds meta into the same sceUtilitySavedata binary payload as its one save slot (SAVE_SLOT_COUNT_MAX=1 there - a future save picker will let players manage multiple named saves via the OS's own browser). - GameCube consolidates the 3 per-slot memory card files and the separate settings file into one combined card file. Also fixes two bugs surfaced while building this: the CRC finalize step seeked to a hardcoded offset (only safe for one section per file, breaks once meta+slots share a buffer), and save.c's async/sync dispatch left an unconditional fallback call that doesn't exist on PSP-only platforms. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
#include "input/input.h"
|
||||
#include "save/settings.h"
|
||||
#include "save/save.h"
|
||||
|
||||
// #define INPUT_PSP_GAMEPAD_BUTTON_ACCEPT INPUT_SDL2_GAMEPAD_BUTTON_CUSTOM
|
||||
// #define INPUT_PSP_GAMEPAD_BUTTON_CANCEL INPUT_SDL2_GAMEPAD_BUTTON_CUSTOM
|
||||
@@ -95,5 +95,5 @@ errorret_t inputInitPSP(void) {
|
||||
}
|
||||
|
||||
float_t inputGetDeadzoneSDL2(const inputbutton_t button) {
|
||||
return settingsGet()->deadzone;
|
||||
return saveGetMeta()->deadzone;
|
||||
}
|
||||
@@ -8,6 +8,12 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||
PUBLIC
|
||||
savepsp.c
|
||||
savestreampsp.c
|
||||
settingspsp.c
|
||||
settingsstreampsp.c
|
||||
)
|
||||
|
||||
# PSP only needs one Dusk-side save slot - a future main-menu save picker
|
||||
# will let players manage multiple named saves through the OS's own
|
||||
# sceUtilitySavedata browser instead of Dusk maintaining its own numbered
|
||||
# slots (see save/saveslot.h for the default used by every other platform).
|
||||
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
|
||||
SAVE_SLOT_COUNT_MAX=1
|
||||
)
|
||||
|
||||
@@ -14,7 +14,7 @@ typedef savestreampsp_t saveplatformstream_t;
|
||||
|
||||
#define saveInitPlatform saveInitPSP
|
||||
#define saveDisposePlatform saveDisposePSP
|
||||
#define saveDeletePlatform saveDeletePSP
|
||||
#define saveSlotDeletePlatform saveDeleteSlotPSP
|
||||
|
||||
#define saveStreamReadBytesPlatform(stream, buf, len) \
|
||||
saveStreamReadBytesPSP(&(stream)->platform, buf, len)
|
||||
@@ -22,16 +22,32 @@ typedef savestreampsp_t saveplatformstream_t;
|
||||
saveStreamWriteBytesPSP(&(stream)->platform, buf, len)
|
||||
#define saveStreamSeekPlatform(stream, pos) \
|
||||
saveStreamSeekPSP(&(stream)->platform, pos)
|
||||
#define saveStreamTellPlatform(stream, out) \
|
||||
saveStreamTellPSP(&(stream)->platform, out)
|
||||
|
||||
// Save/load go entirely through the native sceUtilitySavedata dialog
|
||||
// (savePSPBeginSave/Load), which spans multiple frames - these bypass
|
||||
// save.c's normal synchronous open/write-fields/close flow above (that's
|
||||
// still used internally, just against an in-memory buffer, from within
|
||||
// savePSPBeginSave/Load themselves) and are what save.c's saveWrite()/
|
||||
// saveLoad() actually call on this platform.
|
||||
#define saveAsyncWritePlatform(slot, onComplete, user) \
|
||||
// savePSPBeginSave/Load themselves) and are what save.c's saveWriteSlot()/
|
||||
// saveLoadSlot()/saveWriteMeta()/saveLoadMeta() actually call on this
|
||||
// platform. Meta and the (one) slot are serialized together into the same
|
||||
// buffer - there is no separate meta-only path on PSP - so the meta
|
||||
// variants just drive the same dialog against SAVE_ACTIVE_SLOT.
|
||||
#define saveSlotAsyncWritePlatform(slot, onComplete, user) \
|
||||
savePSPBeginSave(slot, onComplete, user)
|
||||
#define saveAsyncLoadPlatform(slot, onComplete, user) \
|
||||
#define saveSlotAsyncLoadPlatform(slot, onComplete, user) \
|
||||
savePSPBeginLoad(slot, onComplete, user)
|
||||
#define saveMetaAsyncWritePlatform(onComplete, user) \
|
||||
savePSPBeginSave(SAVE_ACTIVE_SLOT, onComplete, user)
|
||||
#define saveMetaAsyncLoadPlatform(onComplete, user) \
|
||||
savePSPBeginLoad(SAVE_ACTIVE_SLOT, onComplete, user)
|
||||
#define saveIsBusyPlatform() savePSPIsBusy()
|
||||
#define savePlatformUpdate() savePSPUpdate()
|
||||
|
||||
// Meta only reaches memory via the native dialog now (folded into the same
|
||||
// payload as the save slot) - running that multi-frame dialog on every
|
||||
// single boot just to eagerly populate SAVE.meta would reintroduce the
|
||||
// exact UX problem a lightweight settings-only file used to avoid, so
|
||||
// saveInit() skips eager loading entirely on this platform.
|
||||
#define saveSkipEagerLoadPlatform
|
||||
|
||||
+20
-19
@@ -45,7 +45,7 @@ errorret_t saveDisposePSP(void) {
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveDeletePSP(const uint8_t slot) {
|
||||
errorret_t saveDeleteSlotPSP(const uint8_t slot) {
|
||||
char_t path[SAVE_PSP_PATH_MAX];
|
||||
stringFormat(
|
||||
path, sizeof(path), SAVE_PSP_FILE_FORMAT, SAVE_PSP_GAME_NAME,
|
||||
@@ -54,7 +54,7 @@ errorret_t saveDeletePSP(const uint8_t slot) {
|
||||
|
||||
int32_t result = sceIoRemove(path);
|
||||
if(result < 0 && result != (int32_t)0x80010002) {
|
||||
errorThrow("Failed to delete save file for slot %u", (uint32_t)slot);
|
||||
errorThrow("Failed to delete save data for slot %u", (uint32_t)slot);
|
||||
}
|
||||
|
||||
char_t dir[SAVE_PSP_PATH_MAX];
|
||||
@@ -79,19 +79,19 @@ void savePSPBeginSave(
|
||||
assertNotNull(onComplete, "onComplete cannot be NULL");
|
||||
assertTrue(SAVE.platform.op == SAVE_PSP_OP_NONE, "Save already in progress");
|
||||
|
||||
savefile_t *file = &SAVE.files[slot];
|
||||
saveslot_t *slotData = &SAVE.slots[slot];
|
||||
|
||||
// Serialize into the buffer synchronously (plain memory writes, same
|
||||
// header/version/CRC framing as every other platform) before the dialog
|
||||
// ever starts - only the actual commit-to-storage step needs to wait on
|
||||
// the dialog.
|
||||
// Serialize meta then the slot into the buffer synchronously (plain
|
||||
// memory writes, same header/version/CRC framing as every other
|
||||
// platform) before the dialog ever starts - only the actual commit-to-
|
||||
// storage step needs to wait on the dialog.
|
||||
savestream_t stream;
|
||||
memoryZero(&stream, sizeof(savestream_t));
|
||||
stream.platform.buffer = SAVE.platform.dataBuffer;
|
||||
stream.platform.bufferSize = sizeof(SAVE.platform.dataBuffer);
|
||||
|
||||
errorret_t ret = saveFileWrite(&stream, file);
|
||||
if(errorIsOk(ret)) ret = saveStreamFinalizeWriteImpl(&stream);
|
||||
errorret_t ret = saveMetaSerializeWrite(&stream, &SAVE.meta);
|
||||
if(errorIsOk(ret)) ret = saveSlotSerializeWrite(&stream, slotData);
|
||||
if(errorIsNotOk(ret)) {
|
||||
onComplete(ret, user);
|
||||
return;
|
||||
@@ -121,7 +121,7 @@ void savePSPBeginSave(
|
||||
// save browser entry.
|
||||
stringCopy(param->sfoParam.title, "Dusk", sizeof(param->sfoParam.title));
|
||||
stringCopy(
|
||||
param->sfoParam.savedataTitle, file->playerName,
|
||||
param->sfoParam.savedataTitle, slotData->playerName,
|
||||
sizeof(param->sfoParam.savedataTitle)
|
||||
);
|
||||
stringCopy(
|
||||
@@ -157,10 +157,10 @@ void savePSPBeginLoad(
|
||||
|
||||
SceIoStat stat;
|
||||
if(sceIoGetstat(path, &stat) < 0) {
|
||||
// No save data for this slot yet - not an error (matches every other
|
||||
// platform's "nothing to load yet" behavior), and deliberately skips
|
||||
// showing the dialog at all rather than surfacing an empty "no data"
|
||||
// native screen for a slot the player has never saved to.
|
||||
// No save data yet - not an error (matches every other platform's
|
||||
// "nothing to load yet" behavior), and deliberately skips showing the
|
||||
// dialog at all rather than surfacing an empty "no data" native
|
||||
// screen for data the player has never saved.
|
||||
onComplete(errorOkImpl(), user);
|
||||
return;
|
||||
}
|
||||
@@ -264,19 +264,20 @@ errorret_t savePSPUpdate(void) {
|
||||
SAVE.available = true;
|
||||
|
||||
if(op == SAVE_PSP_OP_LOAD) {
|
||||
savefile_t *file = &SAVE.files[slot];
|
||||
savestream_t stream;
|
||||
memoryZero(&stream, sizeof(savestream_t));
|
||||
stream.platform.buffer = SAVE.platform.dataBuffer;
|
||||
stream.platform.bufferSize = sizeof(SAVE.platform.dataBuffer);
|
||||
stream.platform.length = SAVE.platform.param.dataSize;
|
||||
|
||||
errorret_t ret = saveFileLoad(&stream, file);
|
||||
if(errorIsOk(ret)) ret = saveStreamVerifyChecksumImpl(&stream, slot);
|
||||
file->exists = errorIsOk(ret);
|
||||
errorret_t ret = saveMetaSerializeRead(&stream, &SAVE.meta);
|
||||
if(errorIsOk(ret)) {
|
||||
ret = saveSlotSerializeRead(&stream, &SAVE.slots[slot]);
|
||||
}
|
||||
cb(ret, user);
|
||||
} else {
|
||||
SAVE.files[slot].exists = true;
|
||||
SAVE.meta.exists = true;
|
||||
SAVE.slots[slot].exists = true;
|
||||
cb(errorOkImpl(), user);
|
||||
}
|
||||
break;
|
||||
|
||||
+21
-15
@@ -7,7 +7,8 @@
|
||||
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
#include "save/savefile.h"
|
||||
#include "save/saveslot.h"
|
||||
#include "save/savemeta.h"
|
||||
#include <pspiofilemgr.h>
|
||||
#include <psputility.h>
|
||||
|
||||
@@ -30,8 +31,13 @@ typedef enum {
|
||||
typedef struct {
|
||||
SceUtilitySavedataParam param;
|
||||
// Raw buffer sceUtilitySavedata reads/writes the whole save into/from -
|
||||
// holds BOTH save meta and the (single) save slot back-to-back,
|
||||
// populated by our own savestream_t serialization (see savestreampsp.h)
|
||||
// before a save starts, and deserialized from after a load finishes.
|
||||
// Meta lives in here rather than its own lightweight file specifically
|
||||
// because a device-wide preference change is meant to feel like a real
|
||||
// save on this platform (a brief native icon flash), not need its own
|
||||
// separate storage mechanism.
|
||||
uint8_t dataBuffer[SAVE_PSP_DATA_BUFFER_SIZE] __attribute__((aligned(64)));
|
||||
size_t dataLength;
|
||||
|
||||
@@ -66,24 +72,24 @@ errorret_t saveInitPSP(void);
|
||||
errorret_t saveDisposePSP(void);
|
||||
|
||||
/**
|
||||
* Deletes the save data folder for the given slot from the memory stick.
|
||||
* Deletes the (one) save data folder from the memory stick.
|
||||
*
|
||||
* @param slot The save slot index.
|
||||
* @param slot The save slot index (always 0 on PSP - see
|
||||
* SAVE_SLOT_COUNT_MAX's override in this platform's CMakeLists.txt).
|
||||
* @return An error code if the delete fails.
|
||||
*/
|
||||
errorret_t saveDeletePSP(const uint8_t slot);
|
||||
errorret_t saveDeleteSlotPSP(const uint8_t slot);
|
||||
|
||||
/**
|
||||
* Starts a save via the native sceUtilitySavedata dialog (mode AUTOSAVE -
|
||||
* writes silently with just a brief icon flash, no confirm screen, since
|
||||
* SAVE mode shows one even for a slot with no existing data - but
|
||||
* PARAM.SFO/title/description are generated identically regardless of
|
||||
* mode, and the OS handles the save browser entry either way) for the
|
||||
* given slot. Serializes SAVE.files[slot] into SAVE.platform.dataBuffer
|
||||
* first, synchronously, then kicks off the dialog and returns - completion
|
||||
* is reported later via onComplete, driven by savePSPUpdate() each frame.
|
||||
* If no save data exists yet for this slot, sceUtilitySavedataInitStart()
|
||||
* creates it.
|
||||
* mode, and the OS handles the save browser entry either way). Serializes
|
||||
* SAVE.meta then SAVE.slots[slot] into SAVE.platform.dataBuffer first,
|
||||
* synchronously, then kicks off the dialog and returns - completion is
|
||||
* reported later via onComplete, driven by savePSPUpdate() each frame.
|
||||
* If no save data exists yet, sceUtilitySavedataInitStart() creates it.
|
||||
*
|
||||
* @param slot The save slot index.
|
||||
* @param onComplete Callback invoked once the dialog finishes.
|
||||
@@ -95,11 +101,11 @@ void savePSPBeginSave(
|
||||
|
||||
/**
|
||||
* Starts a load via the native sceUtilitySavedata dialog (mode AUTOLOAD -
|
||||
* see savePSPBeginSave() for why not the plain LOAD mode) for the given
|
||||
* slot, unless a quick sceIoGetstat check finds no save data for this slot
|
||||
* yet - in which case onComplete is invoked immediately with
|
||||
* SAVE.files[slot].exists left false, matching the other platforms'
|
||||
* "no file yet" semantics, and no dialog is shown at all.
|
||||
* see savePSPBeginSave() for why not the plain LOAD mode) unless a quick
|
||||
* sceIoGetstat check finds no save data yet - in which case onComplete is
|
||||
* invoked immediately with SAVE.meta/SAVE.slots[slot].exists left false,
|
||||
* matching the other platforms' "no file yet" semantics, and no dialog is
|
||||
* shown at all.
|
||||
*
|
||||
* @param slot The save slot index.
|
||||
* @param onComplete Callback invoked once the dialog (or immediate
|
||||
|
||||
@@ -39,3 +39,8 @@ errorret_t saveStreamSeekPSP(savestreampsp_t *p, const size_t pos) {
|
||||
p->position = pos;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t saveStreamTellPSP(savestreampsp_t *p, size_t *out) {
|
||||
*out = p->position;
|
||||
errorOk();
|
||||
}
|
||||
|
||||
@@ -53,3 +53,12 @@ errorret_t saveStreamWriteBytesPSP(
|
||||
* @return An error if pos is out of range.
|
||||
*/
|
||||
errorret_t saveStreamSeekPSP(savestreampsp_t *p, const size_t pos);
|
||||
|
||||
/**
|
||||
* Gets the current read/write position within the buffer.
|
||||
*
|
||||
* @param p Active stream.
|
||||
* @param out Receives the current position.
|
||||
* @return An error - always succeeds, matches saveStreamTellImpl's shape.
|
||||
*/
|
||||
errorret_t saveStreamTellPSP(savestreampsp_t *p, size_t *out);
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "save/settingspsp.h"
|
||||
#include "save/settingsstreampsp.h"
|
||||
|
||||
typedef settingspsp_t settingsplatform_t;
|
||||
typedef settingsstreampsp_t settingsplatformstream_t;
|
||||
|
||||
#define settingsInitPlatform settingsInitPSP
|
||||
#define settingsDisposePlatform settingsDisposePSP
|
||||
|
||||
#define settingsStreamOpenReadPlatform(stream) \
|
||||
settingsStreamOpenReadPSP(&(stream)->platform, &(stream)->found)
|
||||
#define settingsStreamOpenWritePlatform(stream) \
|
||||
settingsStreamOpenWritePSP(&(stream)->platform)
|
||||
#define settingsStreamClosePlatform(stream) \
|
||||
settingsStreamClosePSP(&(stream)->platform)
|
||||
#define settingsStreamReadBytesPlatform(stream, buf, len) \
|
||||
settingsStreamReadBytesPSP(&(stream)->platform, buf, len)
|
||||
#define settingsStreamWriteBytesPlatform(stream, buf, len) \
|
||||
settingsStreamWriteBytesPSP(&(stream)->platform, buf, len)
|
||||
#define settingsStreamSeekPlatform(stream, pos) \
|
||||
settingsStreamSeekPSP(&(stream)->platform, pos)
|
||||
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "save/settingspsp.h"
|
||||
|
||||
errorret_t settingsInitPSP(void) {
|
||||
SceIoStat stat;
|
||||
if(sceIoGetstat(SAVE_PSP_ROOT, &stat) < 0) {
|
||||
errorThrow("No memory stick detected");
|
||||
}
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t settingsDisposePSP(void) {
|
||||
errorOk();
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/**
|
||||
* 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/settingsfile.h"
|
||||
#include "save/savepsp.h"
|
||||
#include <pspiofilemgr.h>
|
||||
|
||||
#define SETTINGS_PSP_DIR_FORMAT "ms0:/PSP/SAVEDATA/%sCFG"
|
||||
#define SETTINGS_PSP_FILE_FORMAT SETTINGS_PSP_DIR_FORMAT "/settings.bin"
|
||||
#define SETTINGS_PSP_PATH_MAX 256
|
||||
|
||||
typedef struct {
|
||||
uint8_t reserved;
|
||||
} settingspsp_t;
|
||||
|
||||
/**
|
||||
* Initializes the settings system on PSP. Confirms the memory stick is
|
||||
* reachable (sceIoGetstat on SAVE_PSP_ROOT), same check as saveInitPSP().
|
||||
* Deliberately does not go through sceUtilitySavedata (see savepsp.h) -
|
||||
* that dialog is built for the "game save" browser/PARAM.SFO experience
|
||||
* and shows a visible native icon flash on every write, which is fine for
|
||||
* an explicit menu Save but not for a settings file that can be written
|
||||
* every time the player nudges a slider. Plain sceIo file I/O, in its own
|
||||
* directory outside the per-slot savedata folders, avoids that entirely.
|
||||
*
|
||||
* @return An error code if no memory stick is reachable.
|
||||
*/
|
||||
errorret_t settingsInitPSP(void);
|
||||
|
||||
/**
|
||||
* Disposes of the settings system on PSP.
|
||||
*
|
||||
* @return An error code if disposal fails.
|
||||
*/
|
||||
errorret_t settingsDisposePSP(void);
|
||||
@@ -1,72 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "save/settingspsp.h"
|
||||
#include "save/settingsstreampsp.h"
|
||||
#include "util/string.h"
|
||||
|
||||
errorret_t settingsStreamOpenReadPSP(settingsstreampsp_t *p, bool_t *found) {
|
||||
char_t path[SETTINGS_PSP_PATH_MAX];
|
||||
stringFormat(
|
||||
path, sizeof(path), SETTINGS_PSP_FILE_FORMAT, SAVE_PSP_GAME_NAME
|
||||
);
|
||||
|
||||
p->fd = sceIoOpen(path, PSP_O_RDONLY, 0);
|
||||
*found = (p->fd >= 0);
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t settingsStreamOpenWritePSP(settingsstreampsp_t *p) {
|
||||
char_t dir[SETTINGS_PSP_PATH_MAX];
|
||||
stringFormat(dir, sizeof(dir), SETTINGS_PSP_DIR_FORMAT, SAVE_PSP_GAME_NAME);
|
||||
sceIoMkdir(dir, 0777);
|
||||
|
||||
char_t path[SETTINGS_PSP_PATH_MAX];
|
||||
stringFormat(
|
||||
path, sizeof(path), SETTINGS_PSP_FILE_FORMAT, SAVE_PSP_GAME_NAME
|
||||
);
|
||||
|
||||
p->fd = sceIoOpen(path, PSP_O_WRONLY | PSP_O_CREAT | PSP_O_TRUNC, 0777);
|
||||
if(p->fd < 0) {
|
||||
errorThrow("Failed to open settings file for writing");
|
||||
}
|
||||
errorOk();
|
||||
}
|
||||
|
||||
void settingsStreamClosePSP(settingsstreampsp_t *p) {
|
||||
if(p->fd >= 0) {
|
||||
sceIoClose(p->fd);
|
||||
p->fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
errorret_t settingsStreamReadBytesPSP(
|
||||
settingsstreampsp_t *p, void *buf, const size_t len
|
||||
) {
|
||||
int32_t read = sceIoRead(p->fd, buf, len);
|
||||
if(read != (int32_t)len) {
|
||||
errorThrow("Unexpected end of settings file");
|
||||
}
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t settingsStreamWriteBytesPSP(
|
||||
settingsstreampsp_t *p, const void *buf, const size_t len
|
||||
) {
|
||||
int32_t written = sceIoWrite(p->fd, buf, len);
|
||||
if(written != (int32_t)len) {
|
||||
errorThrow("Failed to write settings data");
|
||||
}
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t settingsStreamSeekPSP(settingsstreampsp_t *p, const size_t pos) {
|
||||
if(sceIoLseek(p->fd, (SceOff)pos, PSP_SEEK_SET) < 0) {
|
||||
errorThrow("Failed to seek in settings file");
|
||||
}
|
||||
errorOk();
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/**
|
||||
* 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 <pspiofilemgr.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct {
|
||||
SceUID fd;
|
||||
} settingsstreampsp_t;
|
||||
|
||||
/**
|
||||
* Opens the settings file for reading via plain sceIo (not the
|
||||
* sceUtilitySavedata dialog - see settingspsp.h).
|
||||
*
|
||||
* @param p Stream to initialize.
|
||||
* @param found Set to true if the file exists, false if it does not.
|
||||
* @return An error if the open fails for a reason other than missing file.
|
||||
*/
|
||||
errorret_t settingsStreamOpenReadPSP(settingsstreampsp_t *p, bool_t *found);
|
||||
|
||||
/**
|
||||
* Opens the settings file for writing, creating its directory and
|
||||
* truncating the file if needed.
|
||||
*
|
||||
* @param p Stream to initialize.
|
||||
* @return An error if the file cannot be opened for writing.
|
||||
*/
|
||||
errorret_t settingsStreamOpenWritePSP(settingsstreampsp_t *p);
|
||||
|
||||
/**
|
||||
* Closes the file descriptor held by the stream.
|
||||
*
|
||||
* @param p Stream to close.
|
||||
*/
|
||||
void settingsStreamClosePSP(settingsstreampsp_t *p);
|
||||
|
||||
/**
|
||||
* Reads len bytes from the stream into buf.
|
||||
*
|
||||
* @param p Active stream.
|
||||
* @param buf Destination buffer.
|
||||
* @param len Number of bytes to read.
|
||||
* @return An error if fewer than len bytes are available.
|
||||
*/
|
||||
errorret_t settingsStreamReadBytesPSP(
|
||||
settingsstreampsp_t *p, void *buf, const size_t len
|
||||
);
|
||||
|
||||
/**
|
||||
* Writes len bytes from buf into the stream.
|
||||
*
|
||||
* @param p Active stream.
|
||||
* @param buf Source buffer.
|
||||
* @param len Number of bytes to write.
|
||||
* @return An error if the write fails.
|
||||
*/
|
||||
errorret_t settingsStreamWriteBytesPSP(
|
||||
settingsstreampsp_t *p, const void *buf, const size_t len
|
||||
);
|
||||
|
||||
/**
|
||||
* Seeks to an absolute byte position within the stream.
|
||||
*
|
||||
* @param p Active stream.
|
||||
* @param pos Target byte offset from the start of the file.
|
||||
* @return An error if the seek fails.
|
||||
*/
|
||||
errorret_t settingsStreamSeekPSP(settingsstreampsp_t *p, const size_t pos);
|
||||
Reference in New Issue
Block a user