src/file/SDL_rwops.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 09 Sep 2017 08:36:37 -0700
changeset 11480 e7a79b236dc0
parent 11416 7e7d8a125d6a
child 11811 5d94cb6b24d3
permissions -rw-r--r--
Fixed bug 3760 - RWops doesn't check for integer overflow when stdio_fseek only supports 32 bits

Simon Hug

When RWops seeks with fseek or fseeko it uses the types long or off_t which can be 32 bits on some platforms. stdio_seek does not check if the 64-bit integer for the offset fits into a 32-bit integer. Offsets equal or larger than 2 GiB will have implementation-defined behavior and failure states would be very confusing to debug.

The attached patch adds range checking by using the macros from limits.h for long type and some bit shifting for off_t because POSIX couldn't be bothered to specify min and max macros.

It also defines HAVE_FSEEKI64 in SDL_config_windows.h so that the Windows function gets picked up automatically with the default config.

And there's an additional error message for when ftell fails.
slouken@8582
     1
/*
slouken@8582
     2
  Simple DirectMedia Layer
slouken@10737
     3
  Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
slouken@8582
     4
slouken@8582
     5
  This software is provided 'as-is', without any express or implied
slouken@8582
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@8582
     7
  arising from the use of this software.
slouken@8582
     8
slouken@8582
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@8582
    10
  including commercial applications, and to alter it and redistribute it
slouken@8582
    11
  freely, subject to the following restrictions:
slouken@8582
    12
slouken@8582
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@8582
    14
     claim that you wrote the original software. If you use this software
slouken@8582
    15
     in a product, an acknowledgment in the product documentation would be
slouken@8582
    16
     appreciated but is not required.
slouken@8582
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@8582
    18
     misrepresented as being the original software.
slouken@8582
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@8582
    20
*/
icculus@11304
    21
icculus@11304
    22
/* We won't get fseeko64 on QNX if _LARGEFILE64_SOURCE is defined, but the
icculus@11304
    23
   configure script knows the C runtime has it and enables it. */
icculus@11304
    24
#ifndef __QNXNTO__
slouken@8582
    25
/* Need this so Linux systems define fseek64o, ftell64o and off64_t */
slouken@8582
    26
#define _LARGEFILE64_SOURCE
icculus@11304
    27
#endif
icculus@11304
    28
icculus@8093
    29
#include "../SDL_internal.h"
slouken@8582
    30
slouken@8582
    31
#if defined(__WIN32__)
slouken@8582
    32
#include "../core/windows/SDL_windows.h"
slouken@8582
    33
#endif
slouken@8582
    34
slouken@11480
    35
#ifdef HAVE_STDIO_H
slouken@11480
    36
#include <stdio.h>
slouken@11480
    37
#endif
slouken@11480
    38
slouken@11480
    39
#ifdef HAVE_LIMITS_H
slouken@11480
    40
#include <limits.h>
slouken@11480
    41
#endif
slouken@8582
    42
slouken@8582
    43
/* This file provides a general interface for SDL to read and write
slouken@8582
    44
   data sources.  It can easily be extended to files, memory, etc.
slouken@8582
    45
*/
slouken@8582
    46
slouken@8582
    47
#include "SDL_endian.h"
slouken@8582
    48
#include "SDL_rwops.h"
slouken@8582
    49
slouken@8582
    50
#ifdef __APPLE__
slouken@8582
    51
#include "cocoa/SDL_rwopsbundlesupport.h"
slouken@8582
    52
#endif /* __APPLE__ */
slouken@8582
    53
dimitris@8761
    54
#ifdef __ANDROID__
slouken@8582
    55
#include "../core/android/SDL_android.h"
slouken@8582
    56
#include "SDL_system.h"
slouken@8582
    57
#endif
slouken@8582
    58
gabomdq@8833
    59
#if __NACL__
gabomdq@8833
    60
#include "nacl_io/nacl_io.h"
gabomdq@8833
    61
#endif
gabomdq@8833
    62
slouken@8582
    63
#ifdef __WIN32__
slouken@8582
    64
slouken@8582
    65
/* Functions to read/write Win32 API file pointers */
slouken@8582
    66
slouken@8582
    67
#ifndef INVALID_SET_FILE_POINTER
slouken@8582
    68
#define INVALID_SET_FILE_POINTER 0xFFFFFFFF
slouken@8582
    69
#endif
slouken@8582
    70
slouken@8582
    71
#define READAHEAD_BUFFER_SIZE   1024
slouken@8582
    72
slouken@8582
    73
static int SDLCALL
slouken@8582
    74
windows_file_open(SDL_RWops * context, const char *filename, const char *mode)
slouken@8582
    75
{
slouken@8582
    76
    UINT old_error_mode;
slouken@8582
    77
    HANDLE h;
slouken@8582
    78
    DWORD r_right, w_right;
slouken@8582
    79
    DWORD must_exist, truncate;
slouken@8582
    80
    int a_mode;
slouken@8582
    81
slouken@8582
    82
    if (!context)
slouken@8582
    83
        return -1;              /* failed (invalid call) */
slouken@8582
    84
slouken@8582
    85
    context->hidden.windowsio.h = INVALID_HANDLE_VALUE;   /* mark this as unusable */
slouken@8582
    86
    context->hidden.windowsio.buffer.data = NULL;
slouken@8582
    87
    context->hidden.windowsio.buffer.size = 0;
slouken@8582
    88
    context->hidden.windowsio.buffer.left = 0;
slouken@8582
    89
slouken@8582
    90
    /* "r" = reading, file must exist */
slouken@8582
    91
    /* "w" = writing, truncate existing, file may not exist */
slouken@8582
    92
    /* "r+"= reading or writing, file must exist            */
slouken@8582
    93
    /* "a" = writing, append file may not exist             */
slouken@8582
    94
    /* "a+"= append + read, file may not exist              */
slouken@8582
    95
    /* "w+" = read, write, truncate. file may not exist    */
slouken@8582
    96
slouken@8582
    97
    must_exist = (SDL_strchr(mode, 'r') != NULL) ? OPEN_EXISTING : 0;
slouken@8582
    98
    truncate = (SDL_strchr(mode, 'w') != NULL) ? CREATE_ALWAYS : 0;
slouken@8582
    99
    r_right = (SDL_strchr(mode, '+') != NULL
slouken@8582
   100
               || must_exist) ? GENERIC_READ : 0;
slouken@8582
   101
    a_mode = (SDL_strchr(mode, 'a') != NULL) ? OPEN_ALWAYS : 0;
slouken@8582
   102
    w_right = (a_mode || SDL_strchr(mode, '+')
slouken@8582
   103
               || truncate) ? GENERIC_WRITE : 0;
slouken@8582
   104
slouken@8582
   105
    if (!r_right && !w_right)   /* inconsistent mode */
slouken@8582
   106
        return -1;              /* failed (invalid call) */
slouken@8582
   107
slouken@8582
   108
    context->hidden.windowsio.buffer.data =
slouken@8582
   109
        (char *) SDL_malloc(READAHEAD_BUFFER_SIZE);
slouken@8582
   110
    if (!context->hidden.windowsio.buffer.data) {
slouken@8582
   111
        return SDL_OutOfMemory();
slouken@8582
   112
    }
slouken@8582
   113
    /* Do not open a dialog box if failure */
slouken@8582
   114
    old_error_mode =
slouken@8582
   115
        SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
slouken@8582
   116
slouken@8582
   117
    {
slouken@8582
   118
        LPTSTR tstr = WIN_UTF8ToString(filename);
slouken@8582
   119
        h = CreateFile(tstr, (w_right | r_right),
slouken@8582
   120
                       (w_right) ? 0 : FILE_SHARE_READ, NULL,
slouken@8582
   121
                       (must_exist | truncate | a_mode),
slouken@8582
   122
                       FILE_ATTRIBUTE_NORMAL, NULL);
slouken@8582
   123
        SDL_free(tstr);
slouken@8582
   124
    }
slouken@8582
   125
slouken@8582
   126
    /* restore old behavior */
slouken@8582
   127
    SetErrorMode(old_error_mode);
slouken@8582
   128
slouken@8582
   129
    if (h == INVALID_HANDLE_VALUE) {
slouken@8582
   130
        SDL_free(context->hidden.windowsio.buffer.data);
slouken@8582
   131
        context->hidden.windowsio.buffer.data = NULL;
slouken@8582
   132
        SDL_SetError("Couldn't open %s", filename);
slouken@8582
   133
        return -2;              /* failed (CreateFile) */
slouken@8582
   134
    }
slouken@8582
   135
    context->hidden.windowsio.h = h;
slouken@8582
   136
    context->hidden.windowsio.append = a_mode ? SDL_TRUE : SDL_FALSE;
slouken@8582
   137
slouken@8582
   138
    return 0;                   /* ok */
slouken@8582
   139
}
slouken@8582
   140
slouken@8582
   141
static Sint64 SDLCALL
slouken@8582
   142
windows_file_size(SDL_RWops * context)
slouken@8582
   143
{
slouken@8582
   144
    LARGE_INTEGER size;
slouken@8582
   145
slouken@8582
   146
    if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE) {
slouken@8582
   147
        return SDL_SetError("windows_file_size: invalid context/file not opened");
slouken@8582
   148
    }
slouken@8582
   149
slouken@8582
   150
    if (!GetFileSizeEx(context->hidden.windowsio.h, &size)) {
slouken@8582
   151
        return WIN_SetError("windows_file_size");
slouken@8582
   152
    }
slouken@8582
   153
slouken@8582
   154
    return size.QuadPart;
slouken@8582
   155
}
slouken@8582
   156
slouken@8582
   157
static Sint64 SDLCALL
slouken@8582
   158
windows_file_seek(SDL_RWops * context, Sint64 offset, int whence)
slouken@8582
   159
{
slouken@8582
   160
    DWORD windowswhence;
slouken@8582
   161
    LARGE_INTEGER windowsoffset;
slouken@8582
   162
slouken@8582
   163
    if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE) {
slouken@8582
   164
        return SDL_SetError("windows_file_seek: invalid context/file not opened");
slouken@8582
   165
    }
slouken@8582
   166
slouken@8582
   167
    /* FIXME: We may be able to satisfy the seek within buffered data */
slouken@8582
   168
    if (whence == RW_SEEK_CUR && context->hidden.windowsio.buffer.left) {
slouken@8582
   169
        offset -= (long)context->hidden.windowsio.buffer.left;
slouken@8582
   170
    }
slouken@8582
   171
    context->hidden.windowsio.buffer.left = 0;
slouken@8582
   172
slouken@8582
   173
    switch (whence) {
slouken@8582
   174
    case RW_SEEK_SET:
slouken@8582
   175
        windowswhence = FILE_BEGIN;
slouken@8582
   176
        break;
slouken@8582
   177
    case RW_SEEK_CUR:
slouken@8582
   178
        windowswhence = FILE_CURRENT;
slouken@8582
   179
        break;
slouken@8582
   180
    case RW_SEEK_END:
slouken@8582
   181
        windowswhence = FILE_END;
slouken@8582
   182
        break;
slouken@8582
   183
    default:
slouken@8582
   184
        return SDL_SetError("windows_file_seek: Unknown value for 'whence'");
slouken@8582
   185
    }
slouken@8582
   186
slouken@8582
   187
    windowsoffset.QuadPart = offset;
slouken@8582
   188
    if (!SetFilePointerEx(context->hidden.windowsio.h, windowsoffset, &windowsoffset, windowswhence)) {
slouken@8582
   189
        return WIN_SetError("windows_file_seek");
slouken@8582
   190
    }
slouken@8582
   191
    return windowsoffset.QuadPart;
slouken@8582
   192
}
slouken@8582
   193
slouken@8582
   194
static size_t SDLCALL
slouken@8582
   195
windows_file_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum)
slouken@8582
   196
{
slouken@8582
   197
    size_t total_need;
slouken@8582
   198
    size_t total_read = 0;
slouken@8582
   199
    size_t read_ahead;
slouken@8582
   200
    DWORD byte_read;
slouken@8582
   201
slouken@8582
   202
    total_need = size * maxnum;
slouken@8582
   203
slouken@8582
   204
    if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE
slouken@8582
   205
        || !total_need)
slouken@8582
   206
        return 0;
slouken@8582
   207
slouken@8582
   208
    if (context->hidden.windowsio.buffer.left > 0) {
slouken@8582
   209
        void *data = (char *) context->hidden.windowsio.buffer.data +
slouken@8582
   210
            context->hidden.windowsio.buffer.size -
slouken@8582
   211
            context->hidden.windowsio.buffer.left;
slouken@8582
   212
        read_ahead =
slouken@8582
   213
            SDL_min(total_need, context->hidden.windowsio.buffer.left);
slouken@8582
   214
        SDL_memcpy(ptr, data, read_ahead);
slouken@8582
   215
        context->hidden.windowsio.buffer.left -= read_ahead;
slouken@8582
   216
slouken@8582
   217
        if (read_ahead == total_need) {
slouken@8582
   218
            return maxnum;
slouken@8582
   219
        }
slouken@8582
   220
        ptr = (char *) ptr + read_ahead;
slouken@8582
   221
        total_need -= read_ahead;
slouken@8582
   222
        total_read += read_ahead;
slouken@8582
   223
    }
slouken@8582
   224
slouken@8582
   225
    if (total_need < READAHEAD_BUFFER_SIZE) {
slouken@8582
   226
        if (!ReadFile
slouken@8582
   227
            (context->hidden.windowsio.h, context->hidden.windowsio.buffer.data,
slouken@8582
   228
             READAHEAD_BUFFER_SIZE, &byte_read, NULL)) {
slouken@8582
   229
            SDL_Error(SDL_EFREAD);
slouken@8582
   230
            return 0;
slouken@8582
   231
        }
slouken@8582
   232
        read_ahead = SDL_min(total_need, (int) byte_read);
slouken@8582
   233
        SDL_memcpy(ptr, context->hidden.windowsio.buffer.data, read_ahead);
slouken@8582
   234
        context->hidden.windowsio.buffer.size = byte_read;
slouken@8582
   235
        context->hidden.windowsio.buffer.left = byte_read - read_ahead;
slouken@8582
   236
        total_read += read_ahead;
slouken@8582
   237
    } else {
slouken@8582
   238
        if (!ReadFile
slouken@8582
   239
            (context->hidden.windowsio.h, ptr, (DWORD)total_need, &byte_read, NULL)) {
slouken@8582
   240
            SDL_Error(SDL_EFREAD);
slouken@8582
   241
            return 0;
slouken@8582
   242
        }
slouken@8582
   243
        total_read += byte_read;
slouken@8582
   244
    }
slouken@8582
   245
    return (total_read / size);
slouken@8582
   246
}
slouken@8582
   247
slouken@8582
   248
static size_t SDLCALL
slouken@8582
   249
windows_file_write(SDL_RWops * context, const void *ptr, size_t size,
slouken@8582
   250
                 size_t num)
slouken@8582
   251
{
slouken@8582
   252
slouken@8582
   253
    size_t total_bytes;
slouken@8582
   254
    DWORD byte_written;
slouken@8582
   255
    size_t nwritten;
slouken@8582
   256
slouken@8582
   257
    total_bytes = size * num;
slouken@8582
   258
slouken@8582
   259
    if (!context || context->hidden.windowsio.h == INVALID_HANDLE_VALUE
slouken@8582
   260
        || total_bytes <= 0 || !size)
slouken@8582
   261
        return 0;
slouken@8582
   262
slouken@8582
   263
    if (context->hidden.windowsio.buffer.left) {
slouken@8582
   264
        SetFilePointer(context->hidden.windowsio.h,
slouken@8582
   265
                       -(LONG)context->hidden.windowsio.buffer.left, NULL,
slouken@8582
   266
                       FILE_CURRENT);
slouken@8582
   267
        context->hidden.windowsio.buffer.left = 0;
slouken@8582
   268
    }
slouken@8582
   269
slouken@8582
   270
    /* if in append mode, we must go to the EOF before write */
slouken@8582
   271
    if (context->hidden.windowsio.append) {
slouken@8582
   272
        if (SetFilePointer(context->hidden.windowsio.h, 0L, NULL, FILE_END) ==
slouken@8582
   273
            INVALID_SET_FILE_POINTER) {
slouken@8582
   274
            SDL_Error(SDL_EFWRITE);
slouken@8582
   275
            return 0;
slouken@8582
   276
        }
slouken@8582
   277
    }
slouken@8582
   278
slouken@8582
   279
    if (!WriteFile
slouken@8582
   280
        (context->hidden.windowsio.h, ptr, (DWORD)total_bytes, &byte_written, NULL)) {
slouken@8582
   281
        SDL_Error(SDL_EFWRITE);
slouken@8582
   282
        return 0;
slouken@8582
   283
    }
slouken@8582
   284
slouken@8582
   285
    nwritten = byte_written / size;
slouken@8582
   286
    return nwritten;
slouken@8582
   287
}
slouken@8582
   288
slouken@8582
   289
static int SDLCALL
slouken@8582
   290
windows_file_close(SDL_RWops * context)
slouken@8582
   291
{
slouken@8582
   292
slouken@8582
   293
    if (context) {
slouken@8582
   294
        if (context->hidden.windowsio.h != INVALID_HANDLE_VALUE) {
slouken@8582
   295
            CloseHandle(context->hidden.windowsio.h);
slouken@8582
   296
            context->hidden.windowsio.h = INVALID_HANDLE_VALUE;   /* to be sure */
slouken@8582
   297
        }
slouken@8582
   298
        SDL_free(context->hidden.windowsio.buffer.data);
slouken@8582
   299
        context->hidden.windowsio.buffer.data = NULL;
slouken@8582
   300
        SDL_FreeRW(context);
slouken@8582
   301
    }
icculus@8162
   302
    return 0;
slouken@8582
   303
}
slouken@8582
   304
#endif /* __WIN32__ */
slouken@8582
   305
slouken@8582
   306
#ifdef HAVE_STDIO_H
slouken@8582
   307
slouken@11230
   308
#ifdef HAVE_FOPEN64
slouken@11230
   309
#define fopen   fopen64
slouken@11230
   310
#endif
slouken@11230
   311
#ifdef HAVE_FSEEKO64
slouken@11230
   312
#define fseek_off_t off64_t
slouken@11230
   313
#define fseek   fseeko64
slouken@11230
   314
#define ftell   ftello64
slouken@11230
   315
#elif defined(HAVE_FSEEKO)
slouken@11480
   316
#if defined(OFF_MIN) && defined(OFF_MAX)
slouken@11480
   317
#define FSEEK_OFF_MIN OFF_MIN
slouken@11480
   318
#define FSEEK_OFF_MAX OFF_MAX
slouken@11480
   319
#elif defined(HAVE_LIMITS_H)
slouken@11480
   320
/* POSIX doesn't specify the minimum and maximum macros for off_t so
slouken@11480
   321
 * we have to improvise and dance around implementation-defined
slouken@11480
   322
 * behavior. This may fail if the off_t type has padding bits or
slouken@11480
   323
 * is not a two's-complement representation. The compilers will detect
slouken@11480
   324
 * and eliminate the dead code if off_t has 64 bits.
slouken@11480
   325
 */
slouken@11480
   326
#define FSEEK_OFF_MAX (((((off_t)1 << (sizeof(off_t) * CHAR_BIT - 2)) - 1) << 1) + 1)
slouken@11480
   327
#define FSEEK_OFF_MIN (-(FSEEK_OFF_MAX) - 1)
slouken@11480
   328
#endif
slouken@11230
   329
#define fseek_off_t off_t
slouken@11230
   330
#define fseek   fseeko
slouken@11230
   331
#define ftell   ftello
slouken@11230
   332
#elif defined(HAVE__FSEEKI64)
slouken@11230
   333
#define fseek_off_t __int64
slouken@11230
   334
#define fseek   _fseeki64
slouken@11230
   335
#define ftell   _ftelli64
slouken@11234
   336
#else
slouken@11480
   337
#ifdef HAVE_LIMITS_H
slouken@11480
   338
#define FSEEK_OFF_MIN LONG_MIN
slouken@11480
   339
#define FSEEK_OFF_MAX LONG_MAX
slouken@11480
   340
#endif
slouken@11234
   341
#define fseek_off_t long
slouken@11230
   342
#endif
slouken@11230
   343
slouken@8582
   344
/* Functions to read/write stdio file pointers */
slouken@8582
   345
slouken@8582
   346
static Sint64 SDLCALL
slouken@8582
   347
stdio_size(SDL_RWops * context)
slouken@8582
   348
{
slouken@8582
   349
    Sint64 pos, size;
slouken@8582
   350
slouken@8582
   351
    pos = SDL_RWseek(context, 0, RW_SEEK_CUR);
slouken@8582
   352
    if (pos < 0) {
slouken@8582
   353
        return -1;
slouken@8582
   354
    }
slouken@8582
   355
    size = SDL_RWseek(context, 0, RW_SEEK_END);
slouken@8582
   356
slouken@8582
   357
    SDL_RWseek(context, pos, RW_SEEK_SET);
slouken@8582
   358
    return size;
slouken@8582
   359
}
slouken@8582
   360
slouken@8582
   361
static Sint64 SDLCALL
slouken@8582
   362
stdio_seek(SDL_RWops * context, Sint64 offset, int whence)
slouken@8582
   363
{
slouken@11480
   364
#if defined(FSEEK_OFF_MIN) && defined(FSEEK_OFF_MAX)
slouken@11480
   365
    if (offset < (Sint64)(FSEEK_OFF_MIN) || offset > (Sint64)(FSEEK_OFF_MAX)) {
slouken@11480
   366
        return SDL_SetError("Seek offset out of range");
slouken@11480
   367
    }
slouken@11480
   368
#endif
slouken@11480
   369
slouken@11230
   370
    if (fseek(context->hidden.stdio.fp, (fseek_off_t)offset, whence) == 0) {
slouken@11480
   371
        Sint64 pos = ftell(context->hidden.stdio.fp);
slouken@11480
   372
        if (pos < 0) {
slouken@11480
   373
            return SDL_SetError("Couldn't get stream offset");
slouken@11480
   374
        }
slouken@11480
   375
        return pos;
slouken@8582
   376
    }
slouken@8582
   377
    return SDL_Error(SDL_EFSEEK);
slouken@8582
   378
}
slouken@8582
   379
slouken@8582
   380
static size_t SDLCALL
slouken@8582
   381
stdio_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum)
slouken@8582
   382
{
slouken@8582
   383
    size_t nread;
slouken@8582
   384
slouken@8582
   385
    nread = fread(ptr, size, maxnum, context->hidden.stdio.fp);
slouken@8582
   386
    if (nread == 0 && ferror(context->hidden.stdio.fp)) {
slouken@8582
   387
        SDL_Error(SDL_EFREAD);
slouken@8582
   388
    }
icculus@8162
   389
    return nread;
slouken@8582
   390
}
slouken@8582
   391
slouken@8582
   392
static size_t SDLCALL
slouken@8582
   393
stdio_write(SDL_RWops * context, const void *ptr, size_t size, size_t num)
slouken@8582
   394
{
slouken@8582
   395
    size_t nwrote;
slouken@8582
   396
slouken@8582
   397
    nwrote = fwrite(ptr, size, num, context->hidden.stdio.fp);
slouken@8582
   398
    if (nwrote == 0 && ferror(context->hidden.stdio.fp)) {
slouken@8582
   399
        SDL_Error(SDL_EFWRITE);
slouken@8582
   400
    }
icculus@8162
   401
    return nwrote;
slouken@8582
   402
}
slouken@8582
   403
slouken@8582
   404
static int SDLCALL
slouken@8582
   405
stdio_close(SDL_RWops * context)
slouken@8582
   406
{
slouken@8582
   407
    int status = 0;
slouken@8582
   408
    if (context) {
slouken@8582
   409
        if (context->hidden.stdio.autoclose) {
slouken@8582
   410
            /* WARNING:  Check the return value here! */
slouken@8582
   411
            if (fclose(context->hidden.stdio.fp) != 0) {
slouken@8582
   412
                status = SDL_Error(SDL_EFWRITE);
slouken@8582
   413
            }
slouken@8582
   414
        }
slouken@8582
   415
        SDL_FreeRW(context);
slouken@8582
   416
    }
slouken@8582
   417
    return status;
slouken@8582
   418
}
slouken@8582
   419
#endif /* !HAVE_STDIO_H */
slouken@8582
   420
slouken@8582
   421
/* Functions to read/write memory pointers */
slouken@8582
   422
slouken@8582
   423
static Sint64 SDLCALL
slouken@8582
   424
mem_size(SDL_RWops * context)
slouken@8582
   425
{
slouken@8582
   426
    return (Sint64)(context->hidden.mem.stop - context->hidden.mem.base);
slouken@8582
   427
}
slouken@8582
   428
slouken@8582
   429
static Sint64 SDLCALL
slouken@8582
   430
mem_seek(SDL_RWops * context, Sint64 offset, int whence)
slouken@8582
   431
{
slouken@8582
   432
    Uint8 *newpos;
slouken@8582
   433
slouken@8582
   434
    switch (whence) {
slouken@8582
   435
    case RW_SEEK_SET:
slouken@8582
   436
        newpos = context->hidden.mem.base + offset;
slouken@8582
   437
        break;
slouken@8582
   438
    case RW_SEEK_CUR:
slouken@8582
   439
        newpos = context->hidden.mem.here + offset;
slouken@8582
   440
        break;
slouken@8582
   441
    case RW_SEEK_END:
slouken@8582
   442
        newpos = context->hidden.mem.stop + offset;
slouken@8582
   443
        break;
slouken@8582
   444
    default:
slouken@8582
   445
        return SDL_SetError("Unknown value for 'whence'");
slouken@8582
   446
    }
slouken@8582
   447
    if (newpos < context->hidden.mem.base) {
slouken@8582
   448
        newpos = context->hidden.mem.base;
slouken@8582
   449
    }
slouken@8582
   450
    if (newpos > context->hidden.mem.stop) {
slouken@8582
   451
        newpos = context->hidden.mem.stop;
slouken@8582
   452
    }
slouken@8582
   453
    context->hidden.mem.here = newpos;
slouken@8582
   454
    return (Sint64)(context->hidden.mem.here - context->hidden.mem.base);
slouken@8582
   455
}
slouken@8582
   456
slouken@8582
   457
static size_t SDLCALL
slouken@8582
   458
mem_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum)
slouken@8582
   459
{
slouken@8582
   460
    size_t total_bytes;
slouken@8582
   461
    size_t mem_available;
slouken@8582
   462
slouken@8582
   463
    total_bytes = (maxnum * size);
slouken@8582
   464
    if ((maxnum <= 0) || (size <= 0)
slouken@8582
   465
        || ((total_bytes / maxnum) != (size_t) size)) {
slouken@8582
   466
        return 0;
slouken@8582
   467
    }
slouken@8582
   468
slouken@8582
   469
    mem_available = (context->hidden.mem.stop - context->hidden.mem.here);
slouken@8582
   470
    if (total_bytes > mem_available) {
slouken@8582
   471
        total_bytes = mem_available;
slouken@8582
   472
    }
slouken@8582
   473
slouken@8582
   474
    SDL_memcpy(ptr, context->hidden.mem.here, total_bytes);
slouken@8582
   475
    context->hidden.mem.here += total_bytes;
slouken@8582
   476
slouken@8582
   477
    return (total_bytes / size);
slouken@8582
   478
}
slouken@8582
   479
slouken@8582
   480
static size_t SDLCALL
slouken@8582
   481
mem_write(SDL_RWops * context, const void *ptr, size_t size, size_t num)
slouken@8582
   482
{
slouken@8582
   483
    if ((context->hidden.mem.here + (num * size)) > context->hidden.mem.stop) {
slouken@8582
   484
        num = (context->hidden.mem.stop - context->hidden.mem.here) / size;
slouken@8582
   485
    }
slouken@8582
   486
    SDL_memcpy(context->hidden.mem.here, ptr, num * size);
slouken@8582
   487
    context->hidden.mem.here += num * size;
icculus@8162
   488
    return num;
slouken@8582
   489
}
slouken@8582
   490
slouken@8582
   491
static size_t SDLCALL
slouken@8582
   492
mem_writeconst(SDL_RWops * context, const void *ptr, size_t size, size_t num)
slouken@8582
   493
{
slouken@8582
   494
    SDL_SetError("Can't write to read-only memory");
icculus@8162
   495
    return 0;
slouken@8582
   496
}
slouken@8582
   497
slouken@8582
   498
static int SDLCALL
slouken@8582
   499
mem_close(SDL_RWops * context)
slouken@8582
   500
{
slouken@8582
   501
    if (context) {
slouken@8582
   502
        SDL_FreeRW(context);
slouken@8582
   503
    }
icculus@8162
   504
    return 0;
slouken@8582
   505
}
slouken@8582
   506
slouken@8582
   507
slouken@8582
   508
/* Functions to create SDL_RWops structures from various data sources */
slouken@8582
   509
slouken@8582
   510
SDL_RWops *
slouken@8582
   511
SDL_RWFromFile(const char *file, const char *mode)
slouken@8582
   512
{
slouken@8582
   513
    SDL_RWops *rwops = NULL;
slouken@8582
   514
    if (!file || !*file || !mode || !*mode) {
slouken@8582
   515
        SDL_SetError("SDL_RWFromFile(): No file or no mode specified");
slouken@8582
   516
        return NULL;
slouken@8582
   517
    }
dimitris@8761
   518
#if defined(__ANDROID__)
slouken@8582
   519
#ifdef HAVE_STDIO_H
slouken@8582
   520
    /* Try to open the file on the filesystem first */
slouken@8582
   521
    if (*file == '/') {
slouken@8582
   522
        FILE *fp = fopen(file, mode);
slouken@8582
   523
        if (fp) {
slouken@8582
   524
            return SDL_RWFromFP(fp, 1);
slouken@8582
   525
        }
slouken@8582
   526
    } else {
slouken@8582
   527
        /* Try opening it from internal storage if it's a relative path */
slouken@8582
   528
        char *path;
slouken@8582
   529
        FILE *fp;
slouken@8582
   530
slouken@8582
   531
        path = SDL_stack_alloc(char, PATH_MAX);
slouken@8582
   532
        if (path) {
slouken@8582
   533
            SDL_snprintf(path, PATH_MAX, "%s/%s",
slouken@8582
   534
                         SDL_AndroidGetInternalStoragePath(), file);
slouken@8582
   535
            fp = fopen(path, mode);
slouken@8582
   536
            SDL_stack_free(path);
slouken@8582
   537
            if (fp) {
slouken@8582
   538
                return SDL_RWFromFP(fp, 1);
slouken@8582
   539
            }
slouken@8582
   540
        }
slouken@8582
   541
    }
slouken@8582
   542
#endif /* HAVE_STDIO_H */
slouken@8582
   543
slouken@8582
   544
    /* Try to open the file from the asset system */
slouken@8582
   545
    rwops = SDL_AllocRW();
slouken@8582
   546
    if (!rwops)
slouken@8582
   547
        return NULL;            /* SDL_SetError already setup by SDL_AllocRW() */
slouken@8582
   548
    if (Android_JNI_FileOpen(rwops, file, mode) < 0) {
slouken@8582
   549
        SDL_FreeRW(rwops);
slouken@8582
   550
        return NULL;
slouken@8582
   551
    }
slouken@8582
   552
    rwops->size = Android_JNI_FileSize;
slouken@8582
   553
    rwops->seek = Android_JNI_FileSeek;
slouken@8582
   554
    rwops->read = Android_JNI_FileRead;
slouken@8582
   555
    rwops->write = Android_JNI_FileWrite;
slouken@8582
   556
    rwops->close = Android_JNI_FileClose;
slouken@8582
   557
    rwops->type = SDL_RWOPS_JNIFILE;
slouken@8582
   558
slouken@8582
   559
#elif defined(__WIN32__)
slouken@8582
   560
    rwops = SDL_AllocRW();
slouken@8582
   561
    if (!rwops)
slouken@8582
   562
        return NULL;            /* SDL_SetError already setup by SDL_AllocRW() */
slouken@8582
   563
    if (windows_file_open(rwops, file, mode) < 0) {
slouken@8582
   564
        SDL_FreeRW(rwops);
slouken@8582
   565
        return NULL;
slouken@8582
   566
    }
slouken@8582
   567
    rwops->size = windows_file_size;
slouken@8582
   568
    rwops->seek = windows_file_seek;
slouken@8582
   569
    rwops->read = windows_file_read;
slouken@8582
   570
    rwops->write = windows_file_write;
slouken@8582
   571
    rwops->close = windows_file_close;
slouken@8582
   572
    rwops->type = SDL_RWOPS_WINFILE;
slouken@8582
   573
slouken@8582
   574
#elif HAVE_STDIO_H
slouken@8582
   575
    {
slouken@8582
   576
        #ifdef __APPLE__
slouken@8582
   577
        FILE *fp = SDL_OpenFPFromBundleOrFallback(file, mode);
slouken@8582
   578
        #elif __WINRT__
slouken@8582
   579
        FILE *fp = NULL;
slouken@8582
   580
        fopen_s(&fp, file, mode);
slouken@8582
   581
        #else
slouken@8582
   582
        FILE *fp = fopen(file, mode);
slouken@8582
   583
        #endif
slouken@8582
   584
        if (fp == NULL) {
slouken@8582
   585
            SDL_SetError("Couldn't open %s", file);
slouken@8582
   586
        } else {
slouken@8582
   587
            rwops = SDL_RWFromFP(fp, 1);
slouken@8582
   588
        }
slouken@8582
   589
    }
slouken@8582
   590
#else
slouken@8582
   591
    SDL_SetError("SDL not compiled with stdio support");
slouken@8582
   592
#endif /* !HAVE_STDIO_H */
slouken@8582
   593
icculus@8162
   594
    return rwops;
slouken@8582
   595
}
slouken@8582
   596
slouken@8582
   597
#ifdef HAVE_STDIO_H
slouken@8582
   598
SDL_RWops *
slouken@8582
   599
SDL_RWFromFP(FILE * fp, SDL_bool autoclose)
slouken@8582
   600
{
slouken@8582
   601
    SDL_RWops *rwops = NULL;
slouken@8582
   602
slouken@8582
   603
    rwops = SDL_AllocRW();
slouken@8582
   604
    if (rwops != NULL) {
slouken@8582
   605
        rwops->size = stdio_size;
slouken@8582
   606
        rwops->seek = stdio_seek;
slouken@8582
   607
        rwops->read = stdio_read;
slouken@8582
   608
        rwops->write = stdio_write;
slouken@8582
   609
        rwops->close = stdio_close;
slouken@8582
   610
        rwops->hidden.stdio.fp = fp;
slouken@8582
   611
        rwops->hidden.stdio.autoclose = autoclose;
slouken@8582
   612
        rwops->type = SDL_RWOPS_STDFILE;
slouken@8582
   613
    }
icculus@8162
   614
    return rwops;
slouken@8582
   615
}
slouken@8582
   616
#else
slouken@8582
   617
SDL_RWops *
slouken@8582
   618
SDL_RWFromFP(void * fp, SDL_bool autoclose)
slouken@8582
   619
{
slouken@8582
   620
    SDL_SetError("SDL not compiled with stdio support");
slouken@8582
   621
    return NULL;
slouken@8582
   622
}
slouken@8582
   623
#endif /* HAVE_STDIO_H */
slouken@8582
   624
slouken@8582
   625
SDL_RWops *
slouken@8582
   626
SDL_RWFromMem(void *mem, int size)
slouken@8582
   627
{
slouken@8582
   628
    SDL_RWops *rwops = NULL;
slouken@8582
   629
    if (!mem) {
slouken@8582
   630
      SDL_InvalidParamError("mem");
icculus@8162
   631
      return rwops;
slouken@8582
   632
    }
slouken@8582
   633
    if (!size) {
slouken@8582
   634
      SDL_InvalidParamError("size");
icculus@8162
   635
      return rwops;
slouken@8582
   636
    }
slouken@8582
   637
slouken@8582
   638
    rwops = SDL_AllocRW();
slouken@8582
   639
    if (rwops != NULL) {
slouken@8582
   640
        rwops->size = mem_size;
slouken@8582
   641
        rwops->seek = mem_seek;
slouken@8582
   642
        rwops->read = mem_read;
slouken@8582
   643
        rwops->write = mem_write;
slouken@8582
   644
        rwops->close = mem_close;
slouken@8582
   645
        rwops->hidden.mem.base = (Uint8 *) mem;
slouken@8582
   646
        rwops->hidden.mem.here = rwops->hidden.mem.base;
slouken@8582
   647
        rwops->hidden.mem.stop = rwops->hidden.mem.base + size;
slouken@8582
   648
        rwops->type = SDL_RWOPS_MEMORY;
slouken@8582
   649
    }
icculus@8162
   650
    return rwops;
slouken@8582
   651
}
slouken@8582
   652
slouken@8582
   653
SDL_RWops *
slouken@8582
   654
SDL_RWFromConstMem(const void *mem, int size)
slouken@8582
   655
{
slouken@8582
   656
    SDL_RWops *rwops = NULL;
slouken@8582
   657
    if (!mem) {
slouken@8582
   658
      SDL_InvalidParamError("mem");
icculus@8162
   659
      return rwops;
slouken@8582
   660
    }
slouken@8582
   661
    if (!size) {
slouken@8582
   662
      SDL_InvalidParamError("size");
icculus@8162
   663
      return rwops;
slouken@8582
   664
    }
slouken@8582
   665
slouken@8582
   666
    rwops = SDL_AllocRW();
slouken@8582
   667
    if (rwops != NULL) {
slouken@8582
   668
        rwops->size = mem_size;
slouken@8582
   669
        rwops->seek = mem_seek;
slouken@8582
   670
        rwops->read = mem_read;
slouken@8582
   671
        rwops->write = mem_writeconst;
slouken@8582
   672
        rwops->close = mem_close;
slouken@8582
   673
        rwops->hidden.mem.base = (Uint8 *) mem;
slouken@8582
   674
        rwops->hidden.mem.here = rwops->hidden.mem.base;
slouken@8582
   675
        rwops->hidden.mem.stop = rwops->hidden.mem.base + size;
slouken@8582
   676
        rwops->type = SDL_RWOPS_MEMORY_RO;
slouken@8582
   677
    }
icculus@8162
   678
    return rwops;
slouken@8582
   679
}
slouken@8582
   680
slouken@8582
   681
SDL_RWops *
slouken@8582
   682
SDL_AllocRW(void)
slouken@8582
   683
{
slouken@8582
   684
    SDL_RWops *area;
slouken@8582
   685
slouken@8582
   686
    area = (SDL_RWops *) SDL_malloc(sizeof *area);
slouken@8582
   687
    if (area == NULL) {
slouken@8582
   688
        SDL_OutOfMemory();
slouken@8582
   689
    } else {
slouken@8582
   690
        area->type = SDL_RWOPS_UNKNOWN;
slouken@8582
   691
    }
icculus@8162
   692
    return area;
slouken@8582
   693
}
slouken@8582
   694
slouken@8582
   695
void
slouken@8582
   696
SDL_FreeRW(SDL_RWops * area)
slouken@8582
   697
{
slouken@8582
   698
    SDL_free(area);
slouken@8582
   699
}
slouken@8582
   700
slouken@11200
   701
/* Load all the data from an SDL data stream */
slouken@11200
   702
void *
slouken@11200
   703
SDL_LoadFile_RW(SDL_RWops * src, size_t *datasize, int freesrc)
slouken@11200
   704
{
slouken@11200
   705
    const int FILE_CHUNK_SIZE = 1024;
slouken@11200
   706
    Sint64 size;
slouken@11200
   707
    size_t size_read, size_total;
slouken@11200
   708
    void *data = NULL, *newdata;
slouken@11200
   709
slouken@11200
   710
    if (!src) {
slouken@11200
   711
        SDL_InvalidParamError("src");
slouken@11200
   712
        return NULL;
slouken@11200
   713
    }
slouken@11200
   714
slouken@11200
   715
    size = SDL_RWsize(src);
slouken@11200
   716
    if (size < 0) {
slouken@11200
   717
        size = FILE_CHUNK_SIZE;
slouken@11200
   718
    }
slouken@11236
   719
    data = SDL_malloc((size_t)(size + 1));
slouken@11200
   720
slouken@11200
   721
    size_total = 0;
slouken@11200
   722
    for (;;) {
icculus@11416
   723
        if ((((Sint64)size_total) + FILE_CHUNK_SIZE) > size) {
slouken@11200
   724
            size = (size_total + FILE_CHUNK_SIZE);
slouken@11236
   725
            newdata = SDL_realloc(data, (size_t)(size + 1));
slouken@11200
   726
            if (!newdata) {
slouken@11200
   727
                SDL_free(data);
slouken@11200
   728
                data = NULL;
slouken@11200
   729
                SDL_OutOfMemory();
slouken@11200
   730
                goto done;
slouken@11200
   731
            }
slouken@11200
   732
            data = newdata;
slouken@11200
   733
        }
slouken@11200
   734
slouken@11200
   735
        size_read = SDL_RWread(src, (char *)data+size_total, 1, (size_t)(size-size_total));
slouken@11200
   736
        if (size_read == 0) {
slouken@11200
   737
            break;
slouken@11200
   738
        }
slouken@11200
   739
        size_total += size_read;
slouken@11200
   740
    }
slouken@11200
   741
slouken@11200
   742
    if (datasize) {
slouken@11200
   743
        *datasize = size_total;
slouken@11200
   744
    }
slouken@11200
   745
    ((char *)data)[size_total] = '\0';
slouken@11200
   746
slouken@11200
   747
done:
slouken@11200
   748
    if (freesrc && src) {
slouken@11200
   749
        SDL_RWclose(src);
slouken@11200
   750
    }
slouken@11200
   751
    return data;
slouken@11200
   752
}
slouken@11200
   753
slouken@8582
   754
/* Functions for dynamically reading and writing endian-specific values */
slouken@8582
   755
slouken@8582
   756
Uint8
slouken@8582
   757
SDL_ReadU8(SDL_RWops * src)
slouken@8582
   758
{
slouken@8582
   759
    Uint8 value = 0;
slouken@8582
   760
icculus@8162
   761
    SDL_RWread(src, &value, sizeof (value), 1);
slouken@8582
   762
    return value;
slouken@8582
   763
}
slouken@8582
   764
slouken@8582
   765
Uint16
slouken@8582
   766
SDL_ReadLE16(SDL_RWops * src)
slouken@8582
   767
{
slouken@8582
   768
    Uint16 value = 0;
slouken@8582
   769
icculus@8162
   770
    SDL_RWread(src, &value, sizeof (value), 1);
icculus@8162
   771
    return SDL_SwapLE16(value);
slouken@8582
   772
}
slouken@8582
   773
slouken@8582
   774
Uint16
slouken@8582
   775
SDL_ReadBE16(SDL_RWops * src)
slouken@8582
   776
{
slouken@8582
   777
    Uint16 value = 0;
slouken@8582
   778
icculus@8162
   779
    SDL_RWread(src, &value, sizeof (value), 1);
icculus@8162
   780
    return SDL_SwapBE16(value);
slouken@8582
   781
}
slouken@8582
   782
slouken@8582
   783
Uint32
slouken@8582
   784
SDL_ReadLE32(SDL_RWops * src)
slouken@8582
   785
{
slouken@8582
   786
    Uint32 value = 0;
slouken@8582
   787
icculus@8162
   788
    SDL_RWread(src, &value, sizeof (value), 1);
icculus@8162
   789
    return SDL_SwapLE32(value);
slouken@8582
   790
}
slouken@8582
   791
slouken@8582
   792
Uint32
slouken@8582
   793
SDL_ReadBE32(SDL_RWops * src)
slouken@8582
   794
{
slouken@8582
   795
    Uint32 value = 0;
slouken@8582
   796
icculus@8162
   797
    SDL_RWread(src, &value, sizeof (value), 1);
icculus@8162
   798
    return SDL_SwapBE32(value);
slouken@8582
   799
}
slouken@8582
   800
slouken@8582
   801
Uint64
slouken@8582
   802
SDL_ReadLE64(SDL_RWops * src)
slouken@8582
   803
{
slouken@8582
   804
    Uint64 value = 0;
slouken@8582
   805
icculus@8162
   806
    SDL_RWread(src, &value, sizeof (value), 1);
icculus@8162
   807
    return SDL_SwapLE64(value);
slouken@8582
   808
}
slouken@8582
   809
slouken@8582
   810
Uint64
slouken@8582
   811
SDL_ReadBE64(SDL_RWops * src)
slouken@8582
   812
{
slouken@8582
   813
    Uint64 value = 0;
slouken@8582
   814
icculus@8162
   815
    SDL_RWread(src, &value, sizeof (value), 1);
icculus@8162
   816
    return SDL_SwapBE64(value);
slouken@8582
   817
}
slouken@8582
   818
slouken@8582
   819
size_t
slouken@8582
   820
SDL_WriteU8(SDL_RWops * dst, Uint8 value)
slouken@8582
   821
{
icculus@8162
   822
    return SDL_RWwrite(dst, &value, sizeof (value), 1);
slouken@8582
   823
}
slouken@8582
   824
slouken@8582
   825
size_t
slouken@8582
   826
SDL_WriteLE16(SDL_RWops * dst, Uint16 value)
slouken@8582
   827
{
icculus@8162
   828
    const Uint16 swapped = SDL_SwapLE16(value);
icculus@8162
   829
    return SDL_RWwrite(dst, &swapped, sizeof (swapped), 1);
slouken@8582
   830
}
slouken@8582
   831
slouken@8582
   832
size_t
slouken@8582
   833
SDL_WriteBE16(SDL_RWops * dst, Uint16 value)
slouken@8582
   834
{
icculus@8162
   835
    const Uint16 swapped = SDL_SwapBE16(value);
icculus@8162
   836
    return SDL_RWwrite(dst, &swapped, sizeof (swapped), 1);
slouken@8582
   837
}
slouken@8582
   838
slouken@8582
   839
size_t
slouken@8582
   840
SDL_WriteLE32(SDL_RWops * dst, Uint32 value)
slouken@8582
   841
{
icculus@8162
   842
    const Uint32 swapped = SDL_SwapLE32(value);
icculus@8162
   843
    return SDL_RWwrite(dst, &swapped, sizeof (swapped), 1);
slouken@8582
   844
}
slouken@8582
   845
slouken@8582
   846
size_t
slouken@8582
   847
SDL_WriteBE32(SDL_RWops * dst, Uint32 value)
slouken@8582
   848
{
icculus@8162
   849
    const Uint32 swapped = SDL_SwapBE32(value);
icculus@8162
   850
    return SDL_RWwrite(dst, &swapped, sizeof (swapped), 1);
slouken@8582
   851
}
slouken@8582
   852
slouken@8582
   853
size_t
slouken@8582
   854
SDL_WriteLE64(SDL_RWops * dst, Uint64 value)
slouken@8582
   855
{
icculus@8162
   856
    const Uint64 swapped = SDL_SwapLE64(value);
icculus@8162
   857
    return SDL_RWwrite(dst, &swapped, sizeof (swapped), 1);
slouken@8582
   858
}
slouken@8582
   859
slouken@8582
   860
size_t
slouken@8582
   861
SDL_WriteBE64(SDL_RWops * dst, Uint64 value)
slouken@8582
   862
{
icculus@8162
   863
    const Uint64 swapped = SDL_SwapBE64(value);
icculus@8162
   864
    return SDL_RWwrite(dst, &swapped, sizeof (swapped), 1);
slouken@8582
   865
}
slouken@8582
   866
slouken@8582
   867
/* vi: set ts=4 sw=4 expandtab: */