/* Copyright (C) 2010-2018 The RetroArch team * * --------------------------------------------------------------------------------------- * The following license statement only applies to this file (nbio_stdio.c). * --------------------------------------------------------------------------------------- * * Permission is hereby granted, free of charge, * to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include /* Assume W-functions do not work below Win2K and Xbox platforms */ #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX) #ifndef LEGACY_WIN32 #define LEGACY_WIN32 #endif #endif struct nbio_stdio_t { FILE* f; void* data; size_t progress; size_t len; /* * possible values: * NBIO_READ, NBIO_WRITE - obvious * -1 - currently doing nothing * -2 - the pointer was reallocated since the last operation */ signed char op; signed char mode; }; #if !defined(_WIN32) || defined(LEGACY_WIN32) static const char *stdio_modes[] = { "rb", "wb", "r+b", "rb", "wb", "r+b" }; #else static const wchar_t *stdio_modes[] = { L"rb", L"wb", L"r+b", L"rb", L"wb", L"r+b" }; #endif static void *nbio_stdio_open(const char * filename, unsigned mode) { void *buf = NULL; struct nbio_stdio_t* handle = NULL; size_t len = 0; #if !defined(_WIN32) || defined(LEGACY_WIN32) FILE* f = fopen(filename, stdio_modes[mode]); #else wchar_t *filename_wide = utf8_to_utf16_string_alloc(filename); FILE* f = _wfopen(filename_wide, stdio_modes[mode]); if (filename_wide) free(filename_wide); #endif if (!f) return NULL; handle = (struct nbio_stdio_t*)malloc(sizeof(struct nbio_stdio_t)); if (!handle) goto error; handle->f = f; switch (mode) { case NBIO_WRITE: case BIO_WRITE: break; default: fseek(handle->f, 0, SEEK_END); len = ftell(handle->f); break; } handle->mode = mode; if (len) buf = malloc(len); if (len && !buf) goto error; handle->data = buf; handle->len = len; handle->progress = handle->len; handle->op = -2; return handle; error: if (handle) free(handle); fclose(f); return NULL; } static void nbio_stdio_begin_read(void *data) { struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data; if (!handle) return; if (handle->op >= 0) { puts("ERROR - attempted file read operation while busy"); abort(); } fseek(handle->f, 0, SEEK_SET); handle->op = NBIO_READ; handle->progress = 0; } static void nbio_stdio_begin_write(void *data) { struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data; if (!handle) return; if (handle->op >= 0) { puts("ERROR - attempted file write operation while busy"); abort(); } fseek(handle->f, 0, SEEK_SET); handle->op = NBIO_WRITE; handle->progress = 0; } static bool nbio_stdio_iterate(void *data) { size_t amount = 65536; struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data; if (!handle) return false; if (amount > handle->len - handle->progress) amount = handle->len - handle->progress; switch (handle->op) { case NBIO_READ: if (handle->mode == BIO_READ) { amount = handle->len; fread((char*)handle->data, 1, amount, handle->f); } else fread((char*)handle->data + handle->progress, 1, amount, handle->f); break; case NBIO_WRITE: if (handle->mode == BIO_WRITE) { size_t written = 0; amount = handle->len; written = fwrite((char*)handle->data, 1, amount, handle->f); if (written != amount) return false; } else fwrite((char*)handle->data + handle->progress, 1, amount, handle->f); break; } handle->progress += amount; if (handle->progress == handle->len) handle->op = -1; return (handle->op < 0); } static void nbio_stdio_resize(void *data, size_t len) { struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data; if (!handle) return; if (handle->op >= 0) { puts("ERROR - attempted file resize operation while busy"); abort(); } if (len < handle->len) { puts("ERROR - attempted file shrink operation, not implemented"); abort(); } handle->len = len; handle->data = realloc(handle->data, handle->len); handle->op = -1; handle->progress = handle->len; } static void *nbio_stdio_get_ptr(void *data, size_t* len) { struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data; if (!handle) return NULL; if (len) *len = handle->len; if (handle->op == -1) return handle->data; return NULL; } static void nbio_stdio_cancel(void *data) { struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data; if (!handle) return; handle->op = -1; handle->progress = handle->len; } static void nbio_stdio_free(void *data) { struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data; if (!handle) return; if (handle->op >= 0) { puts("ERROR - attempted free() while busy"); abort(); } fclose(handle->f); free(handle->data); handle->f = NULL; handle->data = NULL; free(handle); } nbio_intf_t nbio_stdio = { nbio_stdio_open, nbio_stdio_begin_read, nbio_stdio_begin_write, nbio_stdio_iterate, nbio_stdio_resize, nbio_stdio_get_ptr, nbio_stdio_cancel, nbio_stdio_free, "nbio_stdio", };