Commit | Line | Data |
---|---|---|
3719602c PC |
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 | }; |