rwops: Renamed SDL_RWops to SDL_IOStream, and other related symbols.

This commit is contained in:
Ryan C. Gordon
2024-03-14 19:32:50 -04:00
parent fe33b2a81b
commit fc7afa9cbf
36 changed files with 1194 additions and 1132 deletions

View File

@@ -301,7 +301,7 @@ Your game probably has data files. Here's how to access them.
Filesystem access works like a Unix filesystem; you have a single directory
tree, possibly interpolated from several mounted locations, no drive letters,
'/' for a path separator. You can access them with standard file APIs like
open() or fopen() or SDL_RWops. You can read or write from the filesystem.
open() or fopen() or SDL_IOStream. You can read or write from the filesystem.
By default, you probably have a "MEMFS" filesystem (all files are stored in
memory, but access to them is immediate and doesn't need to block). There are

View File

@@ -191,7 +191,7 @@ SDL_FreeWAV has been removed and calls can be replaced with SDL_free.
SDL_LoadWAV() is a proper function now and no longer a macro (but offers the same functionality otherwise).
SDL_LoadWAV_RW() and SDL_LoadWAV() return an int now: zero on success, -1 on error, like most of SDL. They no longer return a pointer to an SDL_AudioSpec.
SDL_LoadWAV_IO() and SDL_LoadWAV() return an int now: zero on success, -1 on error, like most of SDL. They no longer return a pointer to an SDL_AudioSpec.
SDL_AudioCVT interface has been removed, the SDL_AudioStream interface (for audio supplied in pieces) or the new SDL_ConvertAudioSamples() function (for converting a complete audio buffer in one call) can be used instead.
@@ -253,6 +253,7 @@ The following functions have been renamed:
* SDL_AudioStreamGet() => SDL_GetAudioStreamData()
* SDL_AudioStreamPut() => SDL_PutAudioStreamData()
* SDL_FreeAudioStream() => SDL_DestroyAudioStream()
* SDL_LoadWAV_RW() => SDL_LoadWAV_IO()
* SDL_NewAudioStream() => SDL_CreateAudioStream()
@@ -1152,80 +1153,79 @@ The following symbols have been renamed:
## SDL_rwops.h
The following symbols have been renamed:
* RW_SEEK_CUR => SDL_RW_SEEK_CUR
* RW_SEEK_END => SDL_RW_SEEK_END
* RW_SEEK_SET => SDL_RW_SEEK_SET
* RW_SEEK_CUR => SDL_IO_SEEK_CUR
* RW_SEEK_END => SDL_IO_SEEK_END
* RW_SEEK_SET => SDL_IO_SEEK_SET
SDL_RWops is now an opaque structure. The existing APIs to create a RWops (SDL_RWFromFile, etc) still function as expected, but to make a custom RWops with app-provided function pointers, call SDL_OpenRW and provide the function pointers through there. To call into a RWops's functionality, use the standard APIs (SDL_ReadRW, etc) instead of calling into function pointers directly.
SDL_RWops is now an opaque structure, and has been renamed to SDL_IOStream. The SDL3 APIs to create an SDL_IOStream (SDL_IOFromFile, etc) are renamed but otherwise still function as they did in SDL2. However, to make a custom SDL_IOStream with app-provided function pointers, call SDL_OpenIO and provide the function pointers through there. To call into an SDL_IOStream's functionality, use the standard APIs (SDL_ReadIO, etc), as the function pointers are concealed.
The RWops function pointers are now in a separate structure called SDL_RWopsInteface, which is provided to SDL_OpenRW. All the functions now take a `void *` userdata argument for their first parameter instead of an SDL_RWops, since that's now an opaque structure.
The RWops function pointers are now in a separate structure called SDL_IOStreamInterface, which is provided to SDL_OpenIO when creating a custom SDL_IOStream implementation. All the functions now take a `void *` userdata argument for their first parameter instead of an SDL_IOStream, since that's now an opaque structure.
SDL_RWread and SDL_RWwrite (and the read and write function pointers) have a different function signature in SDL3 in addition to being renamed.
SDL_RWread and SDL_RWwrite (and the read and write function pointers) have a different function signature in SDL3, in addition to being renamed.
Previously they looked more like stdio:
```c
size_t SDL_RWread(SDL_RWops *context, void *ptr, size_t size, size_t maxnum);
size_t SDL_RWwrite(SDL_RWops *context, const void *ptr, size_t size, size_t maxnum);
size_t SDL_RWread(SDL_IOStream *context, void *ptr, size_t size, size_t maxnum);
size_t SDL_RWwrite(SDL_IOStream *context, const void *ptr, size_t size, size_t maxnum);
```
But now they look more like POSIX:
```c
size_t SDL_ReadRW(void *userdata, void *ptr, size_t size);
size_t SDL_WriteRW(void *userdata, const void *ptr, size_t size);
size_t SDL_ReadIO(void *userdata, void *ptr, size_t size);
size_t SDL_WriteIO(void *userdata, const void *ptr, size_t size);
```
Code that used to look like this:
```c
size_t custom_read(void *ptr, size_t size, size_t nitems, SDL_RWops *stream)
size_t custom_read(void *ptr, size_t size, size_t nitems, SDL_IOStream *stream)
{
return SDL_RWread(stream, ptr, size, nitems);
}
```
should be changed to:
```c
size_t custom_read(void *ptr, size_t size, size_t nitems, SDL_RWops *stream)
size_t custom_read(void *ptr, size_t size, size_t nitems, SDL_IOStream *stream, SDL_IOStatus *status)
{
if (size > 0 && nitems > 0) {
return SDL_ReadRW(stream, ptr, size * nitems) / size;
return SDL_ReadIO(stream, ptr, size * nitems) / size;
}
return 0;
}
```
SDL_RWops::type was removed and has no replacement; it wasn't meaningful for app-provided implementations at all, and wasn't much use for SDL's internal implementations, either.
SDL_IOStream::type was removed; it wasn't meaningful for app-provided implementations at all, and wasn't much use for SDL's internal implementations, either. If you _have_ to identify the type, you can examine the SDL_IOStream's properties to detect built-in implementations.
SDL_RWopsInterface::close implementations should clean up their own userdata, but not call SDL_CloseRW on themselves; now the contract is always that SDL_CloseRW is called, which calls `->close` and then frees the opaque object.
SDL_IOStreamInterface::close implementations should clean up their own userdata, but not call SDL_CloseIO on themselves; now the contract is always that SDL_CloseIO is called, which calls `->close` before freeing the opaque object.
SDL_AllocRW(), SDL_FreeRW(), SDL_RWclose() and direct access to the `->close` function pointer have been removed from the API, so there's only one path to manage RWops lifetimes now: SDL_OpenIO() and SDL_CloseIO().
SDL_RWFromFP has been removed from the API, due to issues when the SDL library uses a different C runtime from the application.
SDL_AllocRW(), SDL_FreeRW(), SDL_CloseRW() and direct access to the `->close` function pointer have been removed from the API, so there's only one path to manage RWops lifetimes now: SDL_OpenRW() and SDL_CloseRW().
You can implement this in your own code easily:
```c
#include <stdio.h>
typedef struct RWopsStdioFPData
typedef struct IOStreamStdioFPData
{
FILE *fp;
SDL_bool autoclose;
} RWopsStdioFPData;
} IOStreamStdioFPData;
static Sint64 SDLCALL stdio_seek(void *userdata, Sint64 offset, int whence)
{
FILE *fp = ((RWopsStdioFPData *) userdata)->fp;
FILE *fp = ((IOStreamStdioFPData *) userdata)->fp;
int stdiowhence;
switch (whence) {
case SDL_RW_SEEK_SET:
case SDL_IO_SEEK_SET:
stdiowhence = SEEK_SET;
break;
case SDL_RW_SEEK_CUR:
case SDL_IO_SEEK_CUR:
stdiowhence = SEEK_CUR;
break;
case SDL_RW_SEEK_END:
case SDL_IO_SEEK_END:
stdiowhence = SEEK_END;
break;
default:
@@ -1242,9 +1242,9 @@ static Sint64 SDLCALL stdio_seek(void *userdata, Sint64 offset, int whence)
return SDL_Error(SDL_EFSEEK);
}
static size_t SDLCALL stdio_read(void *userdata, void *ptr, size_t size)
static size_t SDLCALL stdio_read(void *userdata, void *ptr, size_t size, SDL_IOStatus *status)
{
FILE *fp = ((RWopsStdioFPData *) userdata)->fp;
FILE *fp = ((IOStreamStdioFPData *) userdata)->fp;
const size_t bytes = fread(ptr, 1, size, fp);
if (bytes == 0 && ferror(fp)) {
SDL_Error(SDL_EFREAD);
@@ -1252,9 +1252,9 @@ static size_t SDLCALL stdio_read(void *userdata, void *ptr, size_t size)
return bytes;
}
static size_t SDLCALL stdio_write(void *userdata, const void *ptr, size_t size)
static size_t SDLCALL stdio_write(void *userdata, const void *ptr, size_t size, SDL_IOStatus *status)
{
FILE *fp = ((RWopsStdioFPData *) userdata)->fp;
FILE *fp = ((IOStreamStdioFPData *) userdata)->fp;
const size_t bytes = fwrite(ptr, 1, size, fp);
if (bytes == 0 && ferror(fp)) {
SDL_Error(SDL_EFWRITE);
@@ -1264,7 +1264,7 @@ static size_t SDLCALL stdio_write(void *userdata, const void *ptr, size_t size)
static int SDLCALL stdio_close(void *userdata)
{
RWopsStdioData *rwopsdata = (RWopsStdioData *) userdata;
IOStreamStdioData *rwopsdata = (IOStreamStdioData *) userdata;
int status = 0;
if (rwopsdata->autoclose) {
if (fclose(rwopsdata->fp) != 0) {
@@ -1274,19 +1274,19 @@ static int SDLCALL stdio_close(void *userdata)
return status;
}
SDL_RWops *SDL_RWFromFP(FILE *fp, SDL_bool autoclose)
SDL_IOStream *SDL_RWFromFP(FILE *fp, SDL_bool autoclose)
{
SDL_RWopsInterface iface;
RWopsStdioFPData *rwopsdata;
SDL_RWops *rwops;
SDL_IOStreamInterface iface;
IOStreamStdioFPData *rwopsdata;
SDL_IOStream *rwops;
rwopsdata = (RWopsStdioFPData *) SDL_malloc(sizeof (*rwopsdata));
rwopsdata = (IOStreamStdioFPData *) SDL_malloc(sizeof (*rwopsdata));
if (!rwopsdata) {
return NULL;
}
SDL_zero(iface);
/* There's no stdio_size because SDL_SizeRW emulates it the same way we'd do it for stdio anyhow. */
/* There's no stdio_size because SDL_SizeIO emulates it the same way we'd do it for stdio anyhow. */
iface.seek = stdio_seek;
iface.read = stdio_read;
iface.write = stdio_write;
@@ -1295,7 +1295,7 @@ SDL_RWops *SDL_RWFromFP(FILE *fp, SDL_bool autoclose)
rwopsdata->fp = fp;
rwopsdata->autoclose = autoclose;
rwops = SDL_OpenRW(&iface, rwopsdata);
rwops = SDL_OpenIO(&iface, rwopsdata);
if (!rwops) {
iface.close(rwopsdata);
}
@@ -1303,15 +1303,22 @@ SDL_RWops *SDL_RWFromFP(FILE *fp, SDL_bool autoclose)
}
```
The internal `FILE *` is available through a standard SDL_IOStream property, for streams made through SDL_IOFromFile() that use stdio behind the scenes; apps use this pointer at their own risk and should make sure that SDL and the app are using the same C runtime.
The functions SDL_ReadU8(), SDL_ReadU16LE(), SDL_ReadU16BE(), SDL_ReadU32LE(), SDL_ReadU32BE(), SDL_ReadU64LE(), and SDL_ReadU64BE() now return SDL_TRUE if the read succeeded and SDL_FALSE if it didn't, and store the data in a pointer passed in as a parameter.
The following functions have been renamed:
* SDL_RWclose() => SDL_CloseRW()
* SDL_RWread() => SDL_ReadRW()
* SDL_RWseek() => SDL_SeekRW()
* SDL_RWsize() => SDL_SizeRW()
* SDL_RWtell() => SDL_TellRW()
* SDL_RWwrite() => SDL_WriteRW()
* SDL_CloseRW() => SDL_CloseIO()
* SDL_RWFromConstMem() => SDL_IOFromConstMem()
* SDL_RWFromFile() => SDL_IOFromFile()
* SDL_RWFromMem() => SDL_IOFromMem()
* SDL_RWclose() => SDL_CloseIO()
* SDL_RWread() => SDL_ReadIO()
* SDL_RWseek() => SDL_SeekIO()
* SDL_RWsize() => SDL_SizeIO()
* SDL_RWtell() => SDL_TellIO()
* SDL_RWwrite() => SDL_WriteIO()
* SDL_ReadBE16() => SDL_ReadU16BE()
* SDL_ReadBE32() => SDL_ReadU32BE()
* SDL_ReadBE64() => SDL_ReadU64BE()
@@ -1325,6 +1332,10 @@ The following functions have been renamed:
* SDL_WriteLE32() => SDL_WriteU32LE()
* SDL_WriteLE64() => SDL_WriteU64LE()
The following structures have been renamed:
* SDL_RWops => SDL_IOStream
## SDL_sensor.h
SDL_SensorID has changed from Sint32 to Uint32, with an invalid ID being 0.
@@ -1448,8 +1459,10 @@ The following functions have been renamed:
* SDL_GetColorKey() => SDL_GetSurfaceColorKey()
* SDL_HasColorKey() => SDL_SurfaceHasColorKey()
* SDL_HasSurfaceRLE() => SDL_SurfaceHasRLE()
* SDL_LoadBMP_RW() => SDL_LoadBMP_IO()
* SDL_LowerBlit() => SDL_BlitSurfaceUnchecked()
* SDL_LowerBlitScaled() => SDL_BlitSurfaceUncheckedScaled()
* SDL_SaveBMP_RW() => SDL_SaveBMP_IO()
* SDL_SetClipRect() => SDL_SetSurfaceClipRect()
* SDL_SetColorKey() => SDL_SetSurfaceColorKey()
* SDL_UpperBlit() => SDL_BlitSurface()

View File

@@ -42,7 +42,7 @@ Here is a rough list of what works, and what doesn't:
* threads
* timers (via SDL_GetTicks(), SDL_AddTimer(), SDL_GetPerformanceCounter(),
SDL_GetPerformanceFrequency(), etc.)
* file I/O via SDL_RWops
* file I/O via SDL_IOStream
* mouse input (unsupported on Windows Phone)
* audio, via SDL's WASAPI backend (if you want to record, your app must
have "Microphone" capabilities enabled in its manifest, and the user must