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