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