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