1 /* Copyright  (C) 2010-2018 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 
26 #include <file/nbio.h>
27 #include <encodings/utf.h>
28 
29 /* Assume W-functions do not work below Win2K and Xbox platforms */
30 #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0500 || defined(_XBOX)
31 
32 #ifndef LEGACY_WIN32
33 #define LEGACY_WIN32
34 #endif
35 
36 #endif
37 
38 struct nbio_stdio_t
39 {
40    FILE* f;
41    void* data;
42    size_t progress;
43    size_t len;
44    /*
45     * possible values:
46     * NBIO_READ, NBIO_WRITE - obvious
47     * -1 - currently doing nothing
48     * -2 - the pointer was reallocated since the last operation
49     */
50    signed char op;
51    signed char mode;
52 };
53 
54 #if !defined(_WIN32) || defined(LEGACY_WIN32)
55 static const char    *stdio_modes[] = { "rb", "wb", "r+b", "rb", "wb", "r+b" };
56 #else
57 static const wchar_t *stdio_modes[] = { L"rb", L"wb", L"r+b", L"rb", L"wb", L"r+b" };
58 #endif
59 
nbio_stdio_open(const char * filename,unsigned mode)60 static void *nbio_stdio_open(const char * filename, unsigned mode)
61 {
62    void *buf                   = NULL;
63    struct nbio_stdio_t* handle = NULL;
64    size_t len                  = 0;
65 #if !defined(_WIN32) || defined(LEGACY_WIN32)
66    FILE* f                     = fopen(filename, stdio_modes[mode]);
67 #else
68    wchar_t *filename_wide      = utf8_to_utf16_string_alloc(filename);
69    FILE* f                     = _wfopen(filename_wide, stdio_modes[mode]);
70 
71    if (filename_wide)
72       free(filename_wide);
73 #endif
74    if (!f)
75       return NULL;
76 
77    handle                = (struct nbio_stdio_t*)malloc(sizeof(struct nbio_stdio_t));
78 
79    if (!handle)
80       goto error;
81 
82    handle->f             = f;
83 
84    switch (mode)
85    {
86       case NBIO_WRITE:
87       case BIO_WRITE:
88          break;
89       default:
90          fseek(handle->f, 0, SEEK_END);
91          len = ftell(handle->f);
92          break;
93    }
94 
95    handle->mode          = mode;
96 
97    if (len)
98       buf                = malloc(len);
99 
100    if (len && !buf)
101       goto error;
102 
103    handle->data          = buf;
104    handle->len           = len;
105    handle->progress      = handle->len;
106    handle->op            = -2;
107 
108    return handle;
109 
110 error:
111    if (handle)
112       free(handle);
113    fclose(f);
114    return NULL;
115 }
116 
nbio_stdio_begin_read(void * data)117 static void nbio_stdio_begin_read(void *data)
118 {
119    struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data;
120    if (!handle)
121       return;
122 
123    if (handle->op >= 0)
124    {
125       puts("ERROR - attempted file read operation while busy");
126       abort();
127    }
128 
129    fseek(handle->f, 0, SEEK_SET);
130 
131    handle->op       = NBIO_READ;
132    handle->progress = 0;
133 }
134 
nbio_stdio_begin_write(void * data)135 static void nbio_stdio_begin_write(void *data)
136 {
137    struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data;
138    if (!handle)
139       return;
140 
141    if (handle->op >= 0)
142    {
143       puts("ERROR - attempted file write operation while busy");
144       abort();
145    }
146 
147    fseek(handle->f, 0, SEEK_SET);
148    handle->op = NBIO_WRITE;
149    handle->progress = 0;
150 }
151 
nbio_stdio_iterate(void * data)152 static bool nbio_stdio_iterate(void *data)
153 {
154    size_t amount               = 65536;
155    struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data;
156 
157    if (!handle)
158       return false;
159 
160    if (amount > handle->len - handle->progress)
161       amount = handle->len - handle->progress;
162 
163    switch (handle->op)
164    {
165       case NBIO_READ:
166          if (handle->mode == BIO_READ)
167          {
168             amount = handle->len;
169             fread((char*)handle->data, 1, amount, handle->f);
170          }
171          else
172             fread((char*)handle->data + handle->progress, 1, amount, handle->f);
173          break;
174       case NBIO_WRITE:
175          if (handle->mode == BIO_WRITE)
176          {
177             size_t written = 0;
178             amount = handle->len;
179             written = fwrite((char*)handle->data, 1, amount, handle->f);
180             if (written != amount)
181                return false;
182          }
183          else
184             fwrite((char*)handle->data + handle->progress, 1, amount, handle->f);
185          break;
186    }
187 
188    handle->progress += amount;
189 
190    if (handle->progress == handle->len)
191       handle->op = -1;
192    return (handle->op < 0);
193 }
194 
nbio_stdio_resize(void * data,size_t len)195 static void nbio_stdio_resize(void *data, size_t len)
196 {
197    void *new_data              = NULL;
198    struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data;
199    if (!handle)
200       return;
201 
202    if (handle->op >= 0)
203    {
204       puts("ERROR - attempted file resize operation while busy");
205       abort();
206    }
207    if (len < handle->len)
208    {
209       puts("ERROR - attempted file shrink operation, not implemented");
210       abort();
211    }
212 
213    handle->len      = len;
214    handle->progress = len;
215    handle->op       = -1;
216 
217    new_data         = realloc(handle->data, handle->len);
218 
219    if (new_data)
220       handle->data  = new_data;
221 }
222 
nbio_stdio_get_ptr(void * data,size_t * len)223 static void *nbio_stdio_get_ptr(void *data, size_t* len)
224 {
225    struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data;
226    if (!handle)
227       return NULL;
228    if (len)
229       *len = handle->len;
230    if (handle->op == -1)
231       return handle->data;
232    return NULL;
233 }
234 
nbio_stdio_cancel(void * data)235 static void nbio_stdio_cancel(void *data)
236 {
237    struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data;
238    if (!handle)
239       return;
240 
241    handle->op = -1;
242    handle->progress = handle->len;
243 }
244 
nbio_stdio_free(void * data)245 static void nbio_stdio_free(void *data)
246 {
247    struct nbio_stdio_t *handle = (struct nbio_stdio_t*)data;
248    if (!handle)
249       return;
250    if (handle->op >= 0)
251    {
252       puts("ERROR - attempted free() while busy");
253       abort();
254    }
255    fclose(handle->f);
256    free(handle->data);
257 
258    handle->f    = NULL;
259    handle->data = NULL;
260    free(handle);
261 }
262 
263 nbio_intf_t nbio_stdio = {
264    nbio_stdio_open,
265    nbio_stdio_begin_read,
266    nbio_stdio_begin_write,
267    nbio_stdio_iterate,
268    nbio_stdio_resize,
269    nbio_stdio_get_ptr,
270    nbio_stdio_cancel,
271    nbio_stdio_free,
272    "nbio_stdio",
273 };
274