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