git subrepo clone https://github.com/libretro/libretro-common.git deps/libretro-common
[pcsx_rearmed.git] / deps / libretro-common / file / nbio / nbio_stdio.c
1 /* Copyright  (C) 2010-2020 The RetroArch team
2  *
3  * ---------------------------------------------------------------------------------------
4  * The following license statement only applies to this file (nbio_stdio.c).
5  * ---------------------------------------------------------------------------------------
6  *
7  * Permission is hereby granted, free of charge,
8  * to any person obtaining a copy of this software and associated documentation files (the "Software"),
9  * to deal in the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
11  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #if defined(WIIU)
26 #include <malloc.h>
27 #endif
28
29 #include <file/nbio.h>
30 #include <encodings/utf.h>
31
32 /* Assume W-functions do not work below Win2K and Xbox platforms */
33 #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
34
35 #ifndef LEGACY_WIN32
36 #define LEGACY_WIN32
37 #endif
38
39 #endif
40
41 #if defined(_WIN32)
42 #if defined(_MSC_VER) && _MSC_VER >= 1400
43 #define ATLEAST_VC2005
44 #endif
45 #endif
46
47 #if (defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE - 0) >= 200112) || (defined(__POSIX_VISIBLE) && __POSIX_VISIBLE >= 200112) || (defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112) || __USE_LARGEFILE || (defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64)
48 #ifndef HAVE_64BIT_OFFSETS
49 #define HAVE_64BIT_OFFSETS
50 #endif
51 #endif
52
53 struct nbio_stdio_t
54 {
55    FILE* f;
56    void* data;
57    size_t progress;
58    size_t len;
59    /*
60     * possible values:
61     * NBIO_READ, NBIO_WRITE - obvious
62     * -1 - currently doing nothing
63     * -2 - the pointer was reallocated since the last operation
64     */
65    signed char op;
66    signed char mode;
67 };
68
69 #if !defined(_WIN32) || defined(LEGACY_WIN32)
70 static const char    *stdio_modes[] = { "rb", "wb", "r+b", "rb", "wb", "r+b" };
71 #else
72 static const wchar_t *stdio_modes[] = { L"rb", L"wb", L"r+b", L"rb", L"wb", L"r+b" };
73 #endif
74
75 static int64_t fseek_wrap(FILE *f, int64_t offset, int origin)
76 {
77 #ifdef ATLEAST_VC2005
78    /* VC2005 and up have a special 64-bit fseek */
79    return _fseeki64(f, offset, origin);
80 #elif defined(HAVE_64BIT_OFFSETS)
81    return fseeko(f, (off_t)offset, origin);
82 #else
83    return fseek(f, (long)offset, origin);
84 #endif
85 }
86
87 static int64_t ftell_wrap(FILE *f)
88 {
89 #ifdef ATLEAST_VC2005
90    /* VC2005 and up have a special 64-bit ftell */
91    return _ftelli64(f);
92 #elif defined(HAVE_64BIT_OFFSETS)
93    return ftello(f);
94 #else
95    return ftell(f);
96 #endif
97 }
98
99 static void *nbio_stdio_open(const char * filename, unsigned mode)
100 {
101    void *buf                   = NULL;
102    struct nbio_stdio_t* handle = NULL;
103    int64_t len                 = 0;
104 #if !defined(_WIN32) || defined(LEGACY_WIN32)
105    FILE* f                     = fopen(filename, stdio_modes[mode]);
106 #else
107    wchar_t *filename_wide      = utf8_to_utf16_string_alloc(filename);
108    FILE* f                     = _wfopen(filename_wide, stdio_modes[mode]);
109
110    if (filename_wide)
111       free(filename_wide);
112 #endif
113    if (!f)
114       return NULL;
115
116    handle                = (struct nbio_stdio_t*)malloc(sizeof(struct nbio_stdio_t));
117
118    if (!handle)
119       goto error;
120
121    handle->f             = f;
122
123    switch (mode)
124    {
125       case NBIO_WRITE:
126       case BIO_WRITE:
127          break;
128       default:
129          fseek_wrap(handle->f, 0, SEEK_END);
130          len = ftell_wrap(handle->f);
131          break;
132    }
133
134    handle->mode          = mode;
135
136 #if defined(WIIU)
137    /* hit the aligned-buffer fast path on Wii U */
138    if (len)
139       buf                = memalign(0x40, (size_t)len);
140 #else
141    if (len)
142       buf                = malloc((size_t)len);
143 #endif
144
145    if (len && !buf)
146       goto error;
147
148    handle->data          = buf;
149    handle->len           = len;
150    handle->progress      = handle->len;
151    handle->op            = -2;
152
153    return handle;
154
155 error:
156    if (handle)
157       free(handle);
158    fclose(f);
159    return NULL;
160 }
161
162 static void nbio_stdio_begin_read(void *data)
163 {
164    struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data;
165    if (!handle)
166       return;
167
168    if (handle->op >= 0)
169       abort();
170
171    fseek_wrap(handle->f, 0, SEEK_SET);
172
173    handle->op       = NBIO_READ;
174    handle->progress = 0;
175 }
176
177 static void nbio_stdio_begin_write(void *data)
178 {
179    struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data;
180    if (!handle)
181       return;
182
183    if (handle->op >= 0)
184       abort();
185
186    fseek_wrap(handle->f, 0, SEEK_SET);
187    handle->op = NBIO_WRITE;
188    handle->progress = 0;
189 }
190
191 static bool nbio_stdio_iterate(void *data)
192 {
193    size_t amount               = 65536;
194    struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data;
195
196    if (!handle)
197       return false;
198
199    if (amount > handle->len - handle->progress)
200       amount = handle->len - handle->progress;
201
202    switch (handle->op)
203    {
204       case NBIO_READ:
205          if (handle->mode == BIO_READ)
206          {
207             amount = handle->len;
208             fread((char*)handle->data, 1, amount, handle->f);
209          }
210          else
211             fread((char*)handle->data + handle->progress, 1, amount, handle->f);
212          break;
213       case NBIO_WRITE:
214          if (handle->mode == BIO_WRITE)
215          {
216             size_t written = 0;
217             amount = handle->len;
218             written = fwrite((char*)handle->data, 1, amount, handle->f);
219             if (written != amount)
220                return false;
221          }
222          else
223             fwrite((char*)handle->data + handle->progress, 1, amount, handle->f);
224          break;
225    }
226
227    handle->progress += amount;
228
229    if (handle->progress == handle->len)
230       handle->op = -1;
231    return (handle->op < 0);
232 }
233
234 static void nbio_stdio_resize(void *data, size_t len)
235 {
236    void *new_data              = NULL;
237    struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data;
238    if (!handle)
239       return;
240
241    if (handle->op >= 0)
242       abort();
243    if (len < handle->len)
244       abort();
245
246    handle->len      = len;
247    handle->progress = len;
248    handle->op       = -1;
249
250    new_data         = realloc(handle->data, handle->len);
251
252    if (new_data)
253       handle->data  = new_data;
254 }
255
256 static void *nbio_stdio_get_ptr(void *data, size_t* len)
257 {
258    struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data;
259    if (!handle)
260       return NULL;
261    if (len)
262       *len = handle->len;
263    if (handle->op == -1)
264       return handle->data;
265    return NULL;
266 }
267
268 static void nbio_stdio_cancel(void *data)
269 {
270    struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data;
271    if (!handle)
272       return;
273
274    handle->op = -1;
275    handle->progress = handle->len;
276 }
277
278 static void nbio_stdio_free(void *data)
279 {
280    struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data;
281    if (!handle)
282       return;
283    if (handle->op >= 0)
284       abort();
285    fclose(handle->f);
286    free(handle->data);
287
288    handle->f    = NULL;
289    handle->data = NULL;
290    free(handle);
291 }
292
293 nbio_intf_t nbio_stdio = {
294    nbio_stdio_open,
295    nbio_stdio_begin_read,
296    nbio_stdio_begin_write,
297    nbio_stdio_iterate,
298    nbio_stdio_resize,
299    nbio_stdio_get_ptr,
300    nbio_stdio_cancel,
301    nbio_stdio_free,
302    "nbio_stdio",
303 };