joeylib2/docs/ApiFile.adoc
2026-09-26 21:02:41 -05:00

151 lines
3.5 KiB
Text

== Files and Saves
Declared in `<joey/file.h>`.
Two directories sit beside the executable, staged there by each platform's disk
packager:
* `DATA/` -- read-only game content: levels, sprites, audio.
* `SAVES/` -- writable saves and preferences.
Both are addressed by a *bare name relative to their directory*. The prefix is
added for you, so never hard-code `"DATA/"` or `"SAVES/"` yourself.
[IMPORTANT]
====
Keep names portable across the retro filesystems: at most 8 characters plus a
short extension, letters and digits only, so they satisfy both ProDOS and DOS
8.3. Do not depend on case being preserved.
====
A failed open or write is non-fatal and simply reports failure, so save code
can degrade gracefully on a platform or volume where `SAVES/` is unavailable.
=== jlDataOpen
[source,c]
----
FILE *jlDataOpen(const char *name, const char *mode);
----
Opens a read-only data file. The `DATA/` prefix is prepended automatically.
[cols="1,1,4", options="header"]
|===
|Parameter |Type |Description
|`name` |`const char *` |Name relative to `DATA/`, for example
`"levels/title.dat"`.
|`mode` |`const char *` |Standard `fopen` mode string.
|===
*Returns:* a `FILE *`, or `NULL` if the path would overflow the internal buffer
or the file cannot be opened.
=== jlSaveOpen
[source,c]
----
FILE *jlSaveOpen(const char *name, const char *mode);
----
Opens a save file for streaming. For write and append modes the `SAVES/`
directory is created on demand. Use this for large or incremental saves; for a
whole-file blob prefer `jlSaveWrite` and `jlSaveRead`.
[cols="1,1,4", options="header"]
|===
|Parameter |Type |Description
|`name` |`const char *` |Name relative to `SAVES/`, for example `"hero.sav"`.
|`mode` |`const char *` |Standard `fopen` mode string.
|===
*Returns:* a `FILE *`, or `NULL` on failure.
=== jlSaveWrite
[source,c]
----
bool jlSaveWrite(const char *name, const void *buf, uint32_t len);
----
Writes a whole save file, replacing any existing contents.
[cols="1,1,4", options="header"]
|===
|Parameter |Type |Description
|`name` |`const char *` |Name relative to `SAVES/`.
|`buf` |`const void *` |Data to write.
|`len` |`uint32_t` |Byte count.
|===
*Returns:* `true` only if all `len` bytes were written.
=== jlSaveRead
[source,c]
----
uint32_t jlSaveRead(const char *name, void *buf, uint32_t max);
----
Reads a whole save file.
[cols="1,1,4", options="header"]
|===
|Parameter |Type |Description
|`name` |`const char *` |Name relative to `SAVES/`.
|`buf` |`void *` |Destination buffer.
|`max` |`uint32_t` |Buffer capacity.
|===
*Returns:* the number of bytes read; 0 if the file is missing or empty.
=== jlSaveExists
[source,c]
----
bool jlSaveExists(const char *name);
----
[cols="1,1,4", options="header"]
|===
|Parameter |Type |Description
|`name` |`const char *` |Name relative to `SAVES/`.
|===
*Returns:* `true` if the file exists and can be opened for reading.
=== jlSaveDelete
[source,c]
----
bool jlSaveDelete(const char *name);
----
[cols="1,1,4", options="header"]
|===
|Parameter |Type |Description
|`name` |`const char *` |Name relative to `SAVES/`.
|===
*Returns:* `true` if the file was removed.
=== jlDiskFree
[source,c]
----
uint32_t jlDiskFree(void);
----
*Parameters:* none.
*Returns:* free space in bytes on the volume holding `SAVES/`. Returns 0 if it
cannot be determined -- some ports have no query for it -- and saturates at
`UINT32_MAX` on volumes larger than 4 GB.
[source,c]
----
// Check before writing a large save on a floppy-based target.
if (jlDiskFree() < neededBytes) {
showMessage("Disk full");
}
----