== Files and Saves Declared in ``. 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. DVX is the exception, because its programs share one current directory: there `DATA/` is beside the program, in its folder under DVX's `APPS`, and `SAVES/` is in the program's own settings folder, `CONFIG\APPS\JOEYLIB\NAME`, which DVX keeps apart from the program files so a reinstall never overwrites it. The calls below find both there by themselves. 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"); } ----