1 /* Copyright (C) 2010-2020 The RetroArch team
2 *
3 * ---------------------------------------------------------------------------------------
4 * The following license statement only applies to this file (file_stream.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 #include <string.h>
26 #include <stdarg.h>
27 #include <ctype.h>
28 #include <errno.h>
29
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33
34 #ifdef _MSC_VER
35 #include <compat/msvc.h>
36 #endif
37
38 #include <streams/file_stream.h>
39 #define VFS_FRONTEND
40 #include <vfs/vfs_implementation.h>
41
42 static const int64_t vfs_error_return_value = -1;
43
44 static retro_vfs_get_path_t filestream_get_path_cb = NULL;
45 static retro_vfs_open_t filestream_open_cb = NULL;
46 static retro_vfs_close_t filestream_close_cb = NULL;
47 static retro_vfs_size_t filestream_size_cb = NULL;
48 static retro_vfs_truncate_t filestream_truncate_cb = NULL;
49 static retro_vfs_tell_t filestream_tell_cb = NULL;
50 static retro_vfs_seek_t filestream_seek_cb = NULL;
51 static retro_vfs_read_t filestream_read_cb = NULL;
52 static retro_vfs_write_t filestream_write_cb = NULL;
53 static retro_vfs_flush_t filestream_flush_cb = NULL;
54 static retro_vfs_remove_t filestream_remove_cb = NULL;
55 static retro_vfs_rename_t filestream_rename_cb = NULL;
56
57 struct RFILE
58 {
59 struct retro_vfs_file_handle *hfile;
60 bool error_flag;
61 bool eof_flag;
62 };
63
64 /* VFS Initialization */
65
filestream_vfs_init(const struct retro_vfs_interface_info * vfs_info)66 void filestream_vfs_init(const struct retro_vfs_interface_info* vfs_info)
67 {
68 const struct retro_vfs_interface* vfs_iface;
69
70 filestream_get_path_cb = NULL;
71 filestream_open_cb = NULL;
72 filestream_close_cb = NULL;
73 filestream_tell_cb = NULL;
74 filestream_size_cb = NULL;
75 filestream_truncate_cb = NULL;
76 filestream_seek_cb = NULL;
77 filestream_read_cb = NULL;
78 filestream_write_cb = NULL;
79 filestream_flush_cb = NULL;
80 filestream_remove_cb = NULL;
81 filestream_rename_cb = NULL;
82
83 vfs_iface = vfs_info->iface;
84
85 if (vfs_info->required_interface_version < FILESTREAM_REQUIRED_VFS_VERSION
86 || !vfs_iface)
87 return;
88
89 filestream_get_path_cb = vfs_iface->get_path;
90 filestream_open_cb = vfs_iface->open;
91 filestream_close_cb = vfs_iface->close;
92 filestream_size_cb = vfs_iface->size;
93 filestream_truncate_cb = vfs_iface->truncate;
94 filestream_tell_cb = vfs_iface->tell;
95 filestream_seek_cb = vfs_iface->seek;
96 filestream_read_cb = vfs_iface->read;
97 filestream_write_cb = vfs_iface->write;
98 filestream_flush_cb = vfs_iface->flush;
99 filestream_remove_cb = vfs_iface->remove;
100 filestream_rename_cb = vfs_iface->rename;
101 }
102
103 /* Callback wrappers */
filestream_exists(const char * path)104 bool filestream_exists(const char *path)
105 {
106 RFILE *dummy = NULL;
107
108 if (!path || !*path)
109 return false;
110
111 dummy = filestream_open(path,
112 RETRO_VFS_FILE_ACCESS_READ,
113 RETRO_VFS_FILE_ACCESS_HINT_NONE);
114
115 if (!dummy)
116 return false;
117
118 if (filestream_close(dummy) != 0)
119 if (dummy)
120 free(dummy);
121
122 dummy = NULL;
123 return true;
124 }
125
filestream_get_size(RFILE * stream)126 int64_t filestream_get_size(RFILE *stream)
127 {
128 int64_t output;
129
130 if (filestream_size_cb)
131 output = filestream_size_cb(stream->hfile);
132 else
133 output = retro_vfs_file_size_impl((libretro_vfs_implementation_file*)stream->hfile);
134
135 if (output == vfs_error_return_value)
136 stream->error_flag = true;
137
138 return output;
139 }
140
filestream_truncate(RFILE * stream,int64_t length)141 int64_t filestream_truncate(RFILE *stream, int64_t length)
142 {
143 int64_t output;
144
145 if (filestream_truncate_cb)
146 output = filestream_truncate_cb(stream->hfile, length);
147 else
148 output = retro_vfs_file_truncate_impl((libretro_vfs_implementation_file*)stream->hfile, length);
149
150 if (output == vfs_error_return_value)
151 stream->error_flag = true;
152
153 return output;
154 }
155
156 /**
157 * filestream_open:
158 * @path : path to file
159 * @mode : file mode to use when opening (read/write)
160 * @hints :
161 *
162 * Opens a file for reading or writing, depending on the requested mode.
163 * Returns a pointer to an RFILE if opened successfully, otherwise NULL.
164 **/
filestream_open(const char * path,unsigned mode,unsigned hints)165 RFILE* filestream_open(const char *path, unsigned mode, unsigned hints)
166 {
167 struct retro_vfs_file_handle *fp = NULL;
168 RFILE* output = NULL;
169
170 if (filestream_open_cb)
171 fp = (struct retro_vfs_file_handle*)
172 filestream_open_cb(path, mode, hints);
173 else
174 fp = (struct retro_vfs_file_handle*)
175 retro_vfs_file_open_impl(path, mode, hints);
176
177 if (!fp)
178 return NULL;
179
180 output = (RFILE*)malloc(sizeof(RFILE));
181 output->error_flag = false;
182 output->eof_flag = false;
183 output->hfile = fp;
184 return output;
185 }
186
filestream_gets(RFILE * stream,char * s,size_t len)187 char* filestream_gets(RFILE *stream, char *s, size_t len)
188 {
189 int c = 0;
190 char *p = s;
191 if (!stream)
192 return NULL;
193
194 /* get max bytes or up to a newline */
195
196 for (len--; len > 0; len--)
197 {
198 if ((c = filestream_getc(stream)) == EOF)
199 break;
200 *p++ = c;
201 if (c == '\n')
202 break;
203 }
204 *p = 0;
205
206 if (p == s && c == EOF)
207 return NULL;
208 return (s);
209 }
210
filestream_getc(RFILE * stream)211 int filestream_getc(RFILE *stream)
212 {
213 char c = 0;
214 if (stream && filestream_read(stream, &c, 1) == 1)
215 return (int)(unsigned char)c;
216 return EOF;
217 }
218
filestream_scanf(RFILE * stream,const char * format,...)219 int filestream_scanf(RFILE *stream, const char* format, ...)
220 {
221 char buf[4096];
222 char subfmt[64];
223 va_list args;
224 const char * bufiter = buf;
225 int64_t startpos = filestream_tell(stream);
226 int ret = 0;
227 int64_t maxlen = filestream_read(stream, buf, sizeof(buf)-1);
228
229 if (maxlen <= 0)
230 return EOF;
231
232 buf[maxlen] = '\0';
233
234 va_start(args, format);
235
236 while (*format)
237 {
238 if (*format == '%')
239 {
240 int sublen;
241 char* subfmtiter = subfmt;
242 bool asterisk = false;
243
244 *subfmtiter++ = *format++; /* '%' */
245
246 /* %[*][width][length]specifier */
247
248 if (*format == '*')
249 {
250 asterisk = true;
251 *subfmtiter++ = *format++;
252 }
253
254 while (isdigit(*format))
255 *subfmtiter++ = *format++; /* width */
256
257 /* length */
258 if (*format == 'h' || *format == 'l')
259 {
260 if (format[1] == format[0])
261 *subfmtiter++ = *format++;
262 *subfmtiter++ = *format++;
263 }
264 else if (
265 *format == 'j' ||
266 *format == 'z' ||
267 *format == 't' ||
268 *format == 'L')
269 {
270 *subfmtiter++ = *format++;
271 }
272
273 /* specifier - always a single character (except ]) */
274 if (*format == '[')
275 {
276 while (*format != ']')
277 *subfmtiter++ = *format++;
278 *subfmtiter++ = *format++;
279 }
280 else
281 *subfmtiter++ = *format++;
282
283 *subfmtiter++ = '%';
284 *subfmtiter++ = 'n';
285 *subfmtiter++ = '\0';
286
287 if (sizeof(void*) != sizeof(long*)) abort(); /* all pointers must have the same size */
288 if (asterisk)
289 {
290 int v = sscanf(bufiter, subfmt, &sublen);
291 if (v == EOF)
292 return EOF;
293 if (v != 0) break;
294 }
295 else
296 {
297 int v = sscanf(bufiter, subfmt, va_arg(args, void*), &sublen);
298 if (v == EOF)
299 return EOF;
300 if (v != 1) break;
301 }
302
303 ret++;
304 bufiter += sublen;
305 }
306 else if (isspace(*format))
307 {
308 while (isspace(*bufiter)) bufiter++;
309 format++;
310 }
311 else
312 {
313 if (*bufiter != *format)
314 break;
315 bufiter++;
316 format++;
317 }
318 }
319
320 va_end(args);
321 filestream_seek(stream, startpos+(bufiter-buf), RETRO_VFS_SEEK_POSITION_START);
322
323 return ret;
324 }
325
filestream_seek(RFILE * stream,int64_t offset,int seek_position)326 int64_t filestream_seek(RFILE *stream, int64_t offset, int seek_position)
327 {
328 int64_t output;
329
330 if (filestream_seek_cb)
331 output = filestream_seek_cb(stream->hfile, offset, seek_position);
332 else
333 output = retro_vfs_file_seek_impl((libretro_vfs_implementation_file*)stream->hfile, offset, seek_position);
334
335 if (output == vfs_error_return_value)
336 stream->error_flag = true;
337 stream->eof_flag = false;
338
339 return output;
340 }
341
filestream_eof(RFILE * stream)342 int filestream_eof(RFILE *stream)
343 {
344 return stream->eof_flag;
345 }
346
filestream_tell(RFILE * stream)347 int64_t filestream_tell(RFILE *stream)
348 {
349 int64_t output;
350
351 if (filestream_size_cb)
352 output = filestream_tell_cb(stream->hfile);
353 else
354 output = retro_vfs_file_tell_impl((libretro_vfs_implementation_file*)stream->hfile);
355
356 if (output == vfs_error_return_value)
357 stream->error_flag = true;
358
359 return output;
360 }
361
filestream_rewind(RFILE * stream)362 void filestream_rewind(RFILE *stream)
363 {
364 if (!stream)
365 return;
366 filestream_seek(stream, 0L, RETRO_VFS_SEEK_POSITION_START);
367 stream->error_flag = false;
368 stream->eof_flag = false;
369 }
370
filestream_read(RFILE * stream,void * s,int64_t len)371 int64_t filestream_read(RFILE *stream, void *s, int64_t len)
372 {
373 int64_t output;
374
375 if (filestream_read_cb)
376 output = filestream_read_cb(stream->hfile, s, len);
377 else
378 output = retro_vfs_file_read_impl(
379 (libretro_vfs_implementation_file*)stream->hfile, s, len);
380
381 if (output == vfs_error_return_value)
382 stream->error_flag = true;
383 if (output < len)
384 stream->eof_flag = true;
385
386 return output;
387 }
388
filestream_flush(RFILE * stream)389 int filestream_flush(RFILE *stream)
390 {
391 int output;
392
393 if (filestream_flush_cb)
394 output = filestream_flush_cb(stream->hfile);
395 else
396 output = retro_vfs_file_flush_impl((libretro_vfs_implementation_file*)stream->hfile);
397
398 if (output == vfs_error_return_value)
399 stream->error_flag = true;
400
401 return output;
402 }
403
filestream_delete(const char * path)404 int filestream_delete(const char *path)
405 {
406 if (filestream_remove_cb)
407 return filestream_remove_cb(path);
408
409 return retro_vfs_file_remove_impl(path);
410 }
411
filestream_rename(const char * old_path,const char * new_path)412 int filestream_rename(const char *old_path, const char *new_path)
413 {
414 if (filestream_rename_cb)
415 return filestream_rename_cb(old_path, new_path);
416
417 return retro_vfs_file_rename_impl(old_path, new_path);
418 }
419
filestream_get_path(RFILE * stream)420 const char* filestream_get_path(RFILE *stream)
421 {
422 if (filestream_get_path_cb)
423 return filestream_get_path_cb(stream->hfile);
424
425 return retro_vfs_file_get_path_impl((libretro_vfs_implementation_file*)stream->hfile);
426 }
427
filestream_write(RFILE * stream,const void * s,int64_t len)428 int64_t filestream_write(RFILE *stream, const void *s, int64_t len)
429 {
430 int64_t output;
431
432 if (filestream_write_cb)
433 output = filestream_write_cb(stream->hfile, s, len);
434 else
435 output = retro_vfs_file_write_impl((libretro_vfs_implementation_file*)stream->hfile, s, len);
436
437 if (output == vfs_error_return_value)
438 stream->error_flag = true;
439
440 return output;
441 }
442
filestream_putc(RFILE * stream,int c)443 int filestream_putc(RFILE *stream, int c)
444 {
445 char c_char = (char)c;
446 if (!stream)
447 return EOF;
448 return filestream_write(stream, &c_char, 1)==1 ? (int)(unsigned char)c : EOF;
449 }
450
filestream_vprintf(RFILE * stream,const char * format,va_list args)451 int filestream_vprintf(RFILE *stream, const char* format, va_list args)
452 {
453 static char buffer[8 * 1024];
454 int64_t num_chars = vsnprintf(buffer, sizeof(buffer),
455 format, args);
456
457 if (num_chars < 0)
458 return -1;
459 else if (num_chars == 0)
460 return 0;
461
462 return (int)filestream_write(stream, buffer, num_chars);
463 }
464
filestream_printf(RFILE * stream,const char * format,...)465 int filestream_printf(RFILE *stream, const char* format, ...)
466 {
467 va_list vl;
468 int result;
469 va_start(vl, format);
470 result = filestream_vprintf(stream, format, vl);
471 va_end(vl);
472 return result;
473 }
474
filestream_error(RFILE * stream)475 int filestream_error(RFILE *stream)
476 {
477 if (stream && stream->error_flag)
478 return 1;
479 return 0;
480 }
481
filestream_close(RFILE * stream)482 int filestream_close(RFILE *stream)
483 {
484 int output;
485 struct retro_vfs_file_handle* fp = stream->hfile;
486
487 if (filestream_close_cb)
488 output = filestream_close_cb(fp);
489 else
490 output = retro_vfs_file_close_impl((libretro_vfs_implementation_file*)fp);
491
492 if (output == 0)
493 free(stream);
494
495 return output;
496 }
497
498 /**
499 * filestream_read_file:
500 * @path : path to file.
501 * @buf : buffer to allocate and read the contents of the
502 * file into. Needs to be freed manually.
503 *
504 * Read the contents of a file into @buf.
505 *
506 * Returns: number of items read, -1 on error.
507 */
filestream_read_file(const char * path,void ** buf,int64_t * len)508 int64_t filestream_read_file(const char *path, void **buf, int64_t *len)
509 {
510 int64_t ret = 0;
511 int64_t content_buf_size = 0;
512 void *content_buf = NULL;
513 RFILE *file = filestream_open(path,
514 RETRO_VFS_FILE_ACCESS_READ,
515 RETRO_VFS_FILE_ACCESS_HINT_NONE);
516
517 if (!file)
518 {
519 *buf = NULL;
520 return 0;
521 }
522
523 content_buf_size = filestream_get_size(file);
524
525 if (content_buf_size < 0)
526 goto error;
527
528 content_buf = malloc((size_t)(content_buf_size + 1));
529
530 if (!content_buf)
531 goto error;
532 if ((int64_t)(uint64_t)(content_buf_size + 1) != (content_buf_size + 1))
533 goto error;
534
535 ret = filestream_read(file, content_buf, (int64_t)content_buf_size);
536 if (ret < 0)
537 goto error;
538
539 if (filestream_close(file) != 0)
540 if (file)
541 free(file);
542
543 *buf = content_buf;
544
545 /* Allow for easy reading of strings to be safe.
546 * Will only work with sane character formatting (Unix). */
547 ((char*)content_buf)[ret] = '\0';
548
549 if (len)
550 *len = ret;
551
552 return 1;
553
554 error:
555 if (file)
556 if (filestream_close(file) != 0)
557 free(file);
558 if (content_buf)
559 free(content_buf);
560 if (len)
561 *len = -1;
562 *buf = NULL;
563 return 0;
564 }
565
566 /**
567 * filestream_write_file:
568 * @path : path to file.
569 * @data : contents to write to the file.
570 * @size : size of the contents.
571 *
572 * Writes data to a file.
573 *
574 * Returns: true (1) on success, false (0) otherwise.
575 */
filestream_write_file(const char * path,const void * data,int64_t size)576 bool filestream_write_file(const char *path, const void *data, int64_t size)
577 {
578 int64_t ret = 0;
579 RFILE *file = filestream_open(path,
580 RETRO_VFS_FILE_ACCESS_WRITE,
581 RETRO_VFS_FILE_ACCESS_HINT_NONE);
582 if (!file)
583 return false;
584
585 ret = filestream_write(file, data, size);
586 if (filestream_close(file) != 0)
587 if (file)
588 free(file);
589
590 if (ret != size)
591 return false;
592
593 return true;
594 }
595
596 /* Returned pointer must be freed by the caller. */
filestream_getline(RFILE * stream)597 char* filestream_getline(RFILE *stream)
598 {
599 char *newline_tmp = NULL;
600 size_t cur_size = 8;
601 size_t idx = 0;
602 int in = 0;
603 char *newline = (char*)malloc(9);
604
605 if (!stream || !newline)
606 {
607 if (newline)
608 free(newline);
609 return NULL;
610 }
611
612 in = filestream_getc(stream);
613
614 while (in != EOF && in != '\n')
615 {
616 if (idx == cur_size)
617 {
618 cur_size *= 2;
619 newline_tmp = (char*)realloc(newline, cur_size + 1);
620
621 if (!newline_tmp)
622 {
623 free(newline);
624 return NULL;
625 }
626
627 newline = newline_tmp;
628 }
629
630 newline[idx++] = in;
631 in = filestream_getc(stream);
632 }
633
634 newline[idx] = '\0';
635 return newline;
636 }
637
filestream_get_vfs_handle(RFILE * stream)638 libretro_vfs_implementation_file* filestream_get_vfs_handle(RFILE *stream)
639 {
640 return (libretro_vfs_implementation_file*)stream->hfile;
641 }
642