|
@@ -58,11 +58,12 @@ struct SDL_IOStream
|
|
|
|
|
|
typedef struct IOStreamWindowsData
|
|
|
{
|
|
|
- bool append;
|
|
|
HANDLE h;
|
|
|
void *data;
|
|
|
size_t size;
|
|
|
size_t left;
|
|
|
+ bool append;
|
|
|
+ bool autoclose;
|
|
|
} IOStreamWindowsData;
|
|
|
|
|
|
|
|
@@ -73,7 +74,7 @@ typedef struct IOStreamWindowsData
|
|
|
|
|
|
#define READAHEAD_BUFFER_SIZE 1024
|
|
|
|
|
|
-static bool SDLCALL windows_file_open(IOStreamWindowsData *iodata, const char *filename, const char *mode)
|
|
|
+static HANDLE SDLCALL windows_file_open(const char *filename, const char *mode)
|
|
|
{
|
|
|
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
|
|
|
UINT old_error_mode;
|
|
@@ -83,9 +84,6 @@ static bool SDLCALL windows_file_open(IOStreamWindowsData *iodata, const char *f
|
|
|
DWORD must_exist, truncate;
|
|
|
int a_mode;
|
|
|
|
|
|
- SDL_zerop(iodata);
|
|
|
- iodata->h = INVALID_HANDLE_VALUE; // mark this as unusable
|
|
|
-
|
|
|
// "r" = reading, file must exist
|
|
|
// "w" = writing, truncate existing, file may not exist
|
|
|
// "r+"= reading or writing, file must exist
|
|
@@ -100,18 +98,13 @@ static bool SDLCALL windows_file_open(IOStreamWindowsData *iodata, const char *f
|
|
|
w_right = (a_mode || SDL_strchr(mode, '+') || truncate) ? GENERIC_WRITE : 0;
|
|
|
|
|
|
if (!r_right && !w_right) {
|
|
|
- return false; // inconsistent mode
|
|
|
+ return INVALID_HANDLE_VALUE; // inconsistent mode
|
|
|
}
|
|
|
// failed (invalid call)
|
|
|
|
|
|
- iodata->data = (char *)SDL_malloc(READAHEAD_BUFFER_SIZE);
|
|
|
- if (!iodata->data) {
|
|
|
- return false;
|
|
|
- }
|
|
|
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
|
|
|
// Do not open a dialog box if failure
|
|
|
- old_error_mode =
|
|
|
- SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
|
|
|
+ old_error_mode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
|
|
|
#endif
|
|
|
|
|
|
{
|
|
@@ -132,15 +125,9 @@ static bool SDLCALL windows_file_open(IOStreamWindowsData *iodata, const char *f
|
|
|
#endif
|
|
|
|
|
|
if (h == INVALID_HANDLE_VALUE) {
|
|
|
- SDL_free(iodata->data);
|
|
|
- iodata->data = NULL;
|
|
|
SDL_SetError("Couldn't open %s", filename);
|
|
|
- return false; // failed (CreateFile)
|
|
|
}
|
|
|
- iodata->h = h;
|
|
|
- iodata->append = a_mode ? true : false;
|
|
|
-
|
|
|
- return true; // ok
|
|
|
+ return h;
|
|
|
}
|
|
|
|
|
|
static Sint64 SDLCALL windows_file_size(void *userdata)
|
|
@@ -184,7 +171,7 @@ static Sint64 SDLCALL windows_file_seek(void *userdata, Sint64 offset, SDL_IOWhe
|
|
|
|
|
|
windowsoffset.QuadPart = offset;
|
|
|
if (!SetFilePointerEx(iodata->h, windowsoffset, &windowsoffset, windowswhence)) {
|
|
|
- return WIN_SetError("windows_file_seek");
|
|
|
+ return WIN_SetError("Error seeking in datastream");
|
|
|
}
|
|
|
return windowsoffset.QuadPart;
|
|
|
}
|
|
@@ -215,7 +202,9 @@ static size_t SDLCALL windows_file_read(void *userdata, void *ptr, size_t size,
|
|
|
|
|
|
if (total_need < READAHEAD_BUFFER_SIZE) {
|
|
|
if (!ReadFile(iodata->h, iodata->data, READAHEAD_BUFFER_SIZE, &bytes, NULL)) {
|
|
|
- SDL_SetError("Error reading from datastream");
|
|
|
+ if (GetLastError() != ERROR_HANDLE_EOF && GetLastError() != ERROR_BROKEN_PIPE) {
|
|
|
+ WIN_SetError("Error reading from datastream");
|
|
|
+ }
|
|
|
return 0;
|
|
|
}
|
|
|
read_ahead = SDL_min(total_need, bytes);
|
|
@@ -225,7 +214,9 @@ static size_t SDLCALL windows_file_read(void *userdata, void *ptr, size_t size,
|
|
|
total_read += read_ahead;
|
|
|
} else {
|
|
|
if (!ReadFile(iodata->h, ptr, (DWORD)total_need, &bytes, NULL)) {
|
|
|
- SDL_SetError("Error reading from datastream");
|
|
|
+ if (GetLastError() != ERROR_HANDLE_EOF && GetLastError() != ERROR_BROKEN_PIPE) {
|
|
|
+ WIN_SetError("Error reading from datastream");
|
|
|
+ }
|
|
|
return 0;
|
|
|
}
|
|
|
total_read += bytes;
|
|
@@ -241,7 +232,7 @@ static size_t SDLCALL windows_file_write(void *userdata, const void *ptr, size_t
|
|
|
|
|
|
if (iodata->left) {
|
|
|
if (!SetFilePointer(iodata->h, -(LONG)iodata->left, NULL, FILE_CURRENT)) {
|
|
|
- SDL_SetError("Error seeking in datastream");
|
|
|
+ WIN_SetError("Error seeking in datastream");
|
|
|
return 0;
|
|
|
}
|
|
|
iodata->left = 0;
|
|
@@ -252,13 +243,13 @@ static size_t SDLCALL windows_file_write(void *userdata, const void *ptr, size_t
|
|
|
LARGE_INTEGER windowsoffset;
|
|
|
windowsoffset.QuadPart = 0;
|
|
|
if (!SetFilePointerEx(iodata->h, windowsoffset, &windowsoffset, FILE_END)) {
|
|
|
- SDL_SetError("Error seeking in datastream");
|
|
|
+ WIN_SetError("Error seeking in datastream");
|
|
|
return 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (!WriteFile(iodata->h, ptr, (DWORD)total_bytes, &bytes, NULL)) {
|
|
|
- SDL_SetError("Error writing to datastream");
|
|
|
+ WIN_SetError("Error writing to datastream");
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -269,13 +260,58 @@ static SDL_bool SDLCALL windows_file_close(void *userdata)
|
|
|
{
|
|
|
IOStreamWindowsData *iodata = (IOStreamWindowsData *) userdata;
|
|
|
if (iodata->h != INVALID_HANDLE_VALUE) {
|
|
|
- CloseHandle(iodata->h);
|
|
|
+ if (iodata->autoclose) {
|
|
|
+ CloseHandle(iodata->h);
|
|
|
+ }
|
|
|
iodata->h = INVALID_HANDLE_VALUE; // to be sure
|
|
|
}
|
|
|
SDL_free(iodata->data);
|
|
|
SDL_free(iodata);
|
|
|
return true;
|
|
|
}
|
|
|
+
|
|
|
+SDL_IOStream *SDL_IOFromHandle(HANDLE handle, const char *mode, bool autoclose)
|
|
|
+{
|
|
|
+ IOStreamWindowsData *iodata = (IOStreamWindowsData *) SDL_calloc(1, sizeof (*iodata));
|
|
|
+ if (!iodata) {
|
|
|
+ if (autoclose) {
|
|
|
+ CloseHandle(handle);
|
|
|
+ }
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ SDL_IOStreamInterface iface;
|
|
|
+ SDL_INIT_INTERFACE(&iface);
|
|
|
+ if (GetFileType(handle) == FILE_TYPE_DISK) {
|
|
|
+ iface.size = windows_file_size;
|
|
|
+ iface.seek = windows_file_seek;
|
|
|
+ }
|
|
|
+ iface.read = windows_file_read;
|
|
|
+ iface.write = windows_file_write;
|
|
|
+ iface.close = windows_file_close;
|
|
|
+
|
|
|
+ iodata->h = handle;
|
|
|
+ iodata->append = (SDL_strchr(mode, 'a') != NULL);
|
|
|
+ iodata->autoclose = autoclose;
|
|
|
+
|
|
|
+ iodata->data = (char *)SDL_malloc(READAHEAD_BUFFER_SIZE);
|
|
|
+ if (!iodata->data) {
|
|
|
+ iface.close(iodata);
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ SDL_IOStream *iostr = SDL_OpenIO(&iface, iodata);
|
|
|
+ if (!iostr) {
|
|
|
+ iface.close(iodata);
|
|
|
+ } else {
|
|
|
+ const SDL_PropertiesID props = SDL_GetIOProperties(iostr);
|
|
|
+ if (props) {
|
|
|
+ SDL_SetPointerProperty(props, SDL_PROP_IOSTREAM_WINDOWS_HANDLE_POINTER, iodata->h);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return iostr;
|
|
|
+}
|
|
|
#endif // defined(SDL_PLATFORM_WINDOWS)
|
|
|
|
|
|
#if defined(HAVE_STDIO_H) && !defined(SDL_PLATFORM_WINDOWS)
|
|
@@ -357,12 +393,12 @@ static Sint64 SDLCALL stdio_seek(void *userdata, Sint64 offset, SDL_IOWhence whe
|
|
|
if (is_noop || fseek(iodata->fp, (fseek_off_t)offset, stdiowhence) == 0) {
|
|
|
const Sint64 pos = ftell(iodata->fp);
|
|
|
if (pos < 0) {
|
|
|
- SDL_SetError("Couldn't get stream offset");
|
|
|
+ SDL_SetError("Couldn't get stream offset: %s", strerror(errno));
|
|
|
return -1;
|
|
|
}
|
|
|
return pos;
|
|
|
}
|
|
|
- SDL_SetError("Error seeking in datastream");
|
|
|
+ SDL_SetError("Error seeking in datastream: %s", strerror(errno));
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
@@ -371,7 +407,12 @@ static size_t SDLCALL stdio_read(void *userdata, void *ptr, size_t size, SDL_IOS
|
|
|
IOStreamStdioData *iodata = (IOStreamStdioData *) userdata;
|
|
|
const size_t bytes = fread(ptr, 1, size, iodata->fp);
|
|
|
if (bytes == 0 && ferror(iodata->fp)) {
|
|
|
- SDL_SetError("Error reading from datastream");
|
|
|
+ if (errno == EAGAIN) {
|
|
|
+ *status = SDL_IO_STATUS_NOT_READY;
|
|
|
+ clearerr(iodata->fp);
|
|
|
+ } else {
|
|
|
+ SDL_SetError("Error reading from datastream: %s", strerror(errno));
|
|
|
+ }
|
|
|
}
|
|
|
return bytes;
|
|
|
}
|
|
@@ -381,28 +422,50 @@ static size_t SDLCALL stdio_write(void *userdata, const void *ptr, size_t size,
|
|
|
IOStreamStdioData *iodata = (IOStreamStdioData *) userdata;
|
|
|
const size_t bytes = fwrite(ptr, 1, size, iodata->fp);
|
|
|
if (bytes == 0 && ferror(iodata->fp)) {
|
|
|
- SDL_SetError("Error writing to datastream");
|
|
|
+ if (errno == EAGAIN) {
|
|
|
+ *status = SDL_IO_STATUS_NOT_READY;
|
|
|
+ clearerr(iodata->fp);
|
|
|
+ } else {
|
|
|
+ SDL_SetError("Error writing to datastream: %s", strerror(errno));
|
|
|
+ }
|
|
|
}
|
|
|
return bytes;
|
|
|
}
|
|
|
|
|
|
+static SDL_bool SDLCALL stdio_flush(void *userdata, SDL_IOStatus *status)
|
|
|
+{
|
|
|
+ IOStreamStdioData *iodata = (IOStreamStdioData *) userdata;
|
|
|
+ if (fflush(iodata->fp) != 0) {
|
|
|
+ if (errno == EAGAIN) {
|
|
|
+ *status = SDL_IO_STATUS_NOT_READY;
|
|
|
+ return false;
|
|
|
+ } else {
|
|
|
+ return SDL_SetError("Error flushing datastream: %s", strerror(errno));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
static SDL_bool SDLCALL stdio_close(void *userdata)
|
|
|
{
|
|
|
IOStreamStdioData *iodata = (IOStreamStdioData *) userdata;
|
|
|
bool status = true;
|
|
|
if (iodata->autoclose) {
|
|
|
if (fclose(iodata->fp) != 0) {
|
|
|
- status = SDL_SetError("Error writing to datastream");
|
|
|
+ status = SDL_SetError("Error writing to datastream: %s", strerror(errno));
|
|
|
}
|
|
|
}
|
|
|
SDL_free(iodata);
|
|
|
return status;
|
|
|
}
|
|
|
|
|
|
-static SDL_IOStream *SDL_IOFromFP(FILE *fp, bool autoclose)
|
|
|
+SDL_IOStream *SDL_IOFromFP(FILE *fp, bool autoclose)
|
|
|
{
|
|
|
- IOStreamStdioData *iodata = (IOStreamStdioData *) SDL_malloc(sizeof (*iodata));
|
|
|
+ IOStreamStdioData *iodata = (IOStreamStdioData *) SDL_calloc(1, sizeof (*iodata));
|
|
|
if (!iodata) {
|
|
|
+ if (autoclose) {
|
|
|
+ fclose(fp);
|
|
|
+ }
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
@@ -412,6 +475,7 @@ static SDL_IOStream *SDL_IOFromFP(FILE *fp, bool autoclose)
|
|
|
iface.seek = stdio_seek;
|
|
|
iface.read = stdio_read;
|
|
|
iface.write = stdio_write;
|
|
|
+ iface.flush = stdio_flush;
|
|
|
iface.close = stdio_close;
|
|
|
|
|
|
iodata->fp = fp;
|
|
@@ -424,6 +488,7 @@ static SDL_IOStream *SDL_IOFromFP(FILE *fp, bool autoclose)
|
|
|
const SDL_PropertiesID props = SDL_GetIOProperties(iostr);
|
|
|
if (props) {
|
|
|
SDL_SetPointerProperty(props, SDL_PROP_IOSTREAM_STDIO_FILE_POINTER, fp);
|
|
|
+ SDL_SetNumberProperty(props, SDL_PROP_IOSTREAM_FILE_DESCRIPTOR_NUMBER, fileno(fp));
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -542,7 +607,7 @@ SDL_IOStream *SDL_IOFromFile(const char *file, const char *mode)
|
|
|
SDL_SetError("%s is not a regular file or pipe", file);
|
|
|
return NULL;
|
|
|
}
|
|
|
- return SDL_IOFromFP(fp, 1);
|
|
|
+ return SDL_IOFromFP(fp, true);
|
|
|
}
|
|
|
} else if (SDL_strncmp(file, "content://", 10) == 0) {
|
|
|
// Try opening content:// URI
|
|
@@ -573,7 +638,7 @@ SDL_IOStream *SDL_IOFromFile(const char *file, const char *mode)
|
|
|
SDL_SetError("%s is not a regular file or pipe", path);
|
|
|
return NULL;
|
|
|
}
|
|
|
- return SDL_IOFromFP(fp, 1);
|
|
|
+ return SDL_IOFromFP(fp, true);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -605,32 +670,9 @@ SDL_IOStream *SDL_IOFromFile(const char *file, const char *mode)
|
|
|
}
|
|
|
|
|
|
#elif defined(SDL_PLATFORM_WINDOWS)
|
|
|
- IOStreamWindowsData *iodata = (IOStreamWindowsData *) SDL_malloc(sizeof (*iodata));
|
|
|
- if (!iodata) {
|
|
|
- return NULL;
|
|
|
- }
|
|
|
-
|
|
|
- if (!windows_file_open(iodata, file, mode)) {
|
|
|
- windows_file_close(iodata);
|
|
|
- return NULL;
|
|
|
- }
|
|
|
-
|
|
|
- SDL_IOStreamInterface iface;
|
|
|
- SDL_INIT_INTERFACE(&iface);
|
|
|
- iface.size = windows_file_size;
|
|
|
- iface.seek = windows_file_seek;
|
|
|
- iface.read = windows_file_read;
|
|
|
- iface.write = windows_file_write;
|
|
|
- iface.close = windows_file_close;
|
|
|
-
|
|
|
- iostr = SDL_OpenIO(&iface, iodata);
|
|
|
- if (!iostr) {
|
|
|
- windows_file_close(iodata);
|
|
|
- } else {
|
|
|
- const SDL_PropertiesID props = SDL_GetIOProperties(iostr);
|
|
|
- if (props) {
|
|
|
- SDL_SetPointerProperty(props, SDL_PROP_IOSTREAM_WINDOWS_HANDLE_POINTER, iodata->h);
|
|
|
- }
|
|
|
+ HANDLE handle = windows_file_open(file, mode);
|
|
|
+ if (handle != INVALID_HANDLE_VALUE) {
|
|
|
+ iostr = SDL_IOFromHandle(handle, mode, true);
|
|
|
}
|
|
|
|
|
|
#elif defined(HAVE_STDIO_H)
|
|
@@ -669,7 +711,7 @@ SDL_IOStream *SDL_IOFromMem(void *mem, size_t size)
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
- IOStreamMemData *iodata = (IOStreamMemData *) SDL_malloc(sizeof (*iodata));
|
|
|
+ IOStreamMemData *iodata = (IOStreamMemData *) SDL_calloc(1, sizeof (*iodata));
|
|
|
if (!iodata) {
|
|
|
return NULL;
|
|
|
}
|
|
@@ -703,7 +745,7 @@ SDL_IOStream *SDL_IOFromConstMem(const void *mem, size_t size)
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
- IOStreamMemData *iodata = (IOStreamMemData *) SDL_malloc(sizeof (*iodata));
|
|
|
+ IOStreamMemData *iodata = (IOStreamMemData *) SDL_calloc(1, sizeof (*iodata));
|
|
|
if (!iodata) {
|
|
|
return NULL;
|
|
|
}
|
|
@@ -803,7 +845,7 @@ static SDL_bool SDLCALL dynamic_mem_close(void *userdata)
|
|
|
|
|
|
SDL_IOStream *SDL_IOFromDynamicMem(void)
|
|
|
{
|
|
|
- IOStreamDynamicMemData *iodata = (IOStreamDynamicMemData *) SDL_malloc(sizeof (*iodata));
|
|
|
+ IOStreamDynamicMemData *iodata = (IOStreamDynamicMemData *) SDL_calloc(1, sizeof (*iodata));
|
|
|
if (!iodata) {
|
|
|
return NULL;
|
|
|
}
|
|
@@ -816,11 +858,6 @@ SDL_IOStream *SDL_IOFromDynamicMem(void)
|
|
|
iface.write = dynamic_mem_write;
|
|
|
iface.close = dynamic_mem_close;
|
|
|
|
|
|
- iodata->data.base = NULL;
|
|
|
- iodata->data.here = NULL;
|
|
|
- iodata->data.stop = NULL;
|
|
|
- iodata->end = NULL;
|
|
|
-
|
|
|
SDL_IOStream *iostr = SDL_OpenIO(&iface, iodata);
|
|
|
if (iostr) {
|
|
|
iodata->stream = iostr;
|
|
@@ -922,6 +959,10 @@ void *SDL_LoadFile_IO(SDL_IOStream *src, size_t *datasize, SDL_bool closeio)
|
|
|
if (size_read > 0) {
|
|
|
size_total += size_read;
|
|
|
continue;
|
|
|
+ } else if (SDL_GetIOStatus(src) == SDL_IO_STATUS_NOT_READY) {
|
|
|
+ // Wait for the stream to be ready
|
|
|
+ SDL_Delay(1);
|
|
|
+ continue;
|
|
|
}
|
|
|
|
|
|
// The stream status will remain set for the caller to check
|
|
@@ -981,9 +1022,11 @@ Sint64 SDL_GetIOSize(SDL_IOStream *context)
|
|
|
Sint64 SDL_SeekIO(SDL_IOStream *context, Sint64 offset, SDL_IOWhence whence)
|
|
|
{
|
|
|
if (!context) {
|
|
|
- return SDL_InvalidParamError("context");
|
|
|
+ SDL_InvalidParamError("context");
|
|
|
+ return -1;
|
|
|
} else if (!context->iface.seek) {
|
|
|
- return SDL_Unsupported();
|
|
|
+ SDL_Unsupported();
|
|
|
+ return -1;
|
|
|
}
|
|
|
return context->iface.seek(context->userdata, offset, whence);
|
|
|
}
|
|
@@ -1086,6 +1129,26 @@ size_t SDL_IOvprintf(SDL_IOStream *context, SDL_PRINTF_FORMAT_STRING const char
|
|
|
return bytes;
|
|
|
}
|
|
|
|
|
|
+SDL_bool SDL_FlushIO(SDL_IOStream *context)
|
|
|
+{
|
|
|
+ bool result = true;
|
|
|
+
|
|
|
+ if (!context) {
|
|
|
+ return SDL_InvalidParamError("context");
|
|
|
+ }
|
|
|
+
|
|
|
+ context->status = SDL_IO_STATUS_READY;
|
|
|
+ SDL_ClearError();
|
|
|
+
|
|
|
+ if (context->iface.flush) {
|
|
|
+ result = context->iface.flush(context->userdata, &context->status);
|
|
|
+ }
|
|
|
+ if (!result && (context->status == SDL_IO_STATUS_READY)) {
|
|
|
+ context->status = SDL_IO_STATUS_ERROR;
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
// Functions for dynamically reading and writing endian-specific values
|
|
|
|
|
|
SDL_bool SDL_ReadU8(SDL_IOStream *src, Uint8 *value)
|