update libchdr
[pcsx_rearmed.git] / deps / libretro-common / streams / rzip_stream.c
1 /* Copyright  (C) 2010-2020 The RetroArch team
2  *
3  * ---------------------------------------------------------------------------------------
4  * The following license statement only applies to this file (rzip_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 <string/stdstring.h>
24 #include <file/file_path.h>
25
26 #include <streams/file_stream.h>
27 #include <streams/trans_stream.h>
28
29 #include <streams/rzip_stream.h>
30
31 /* Current RZIP file format version */
32 #define RZIP_VERSION 1
33
34 /* Compression level
35  * > zlib default of 6 provides the best
36  *   balance between file size and
37  *   compression speed */
38 #define RZIP_COMPRESSION_LEVEL 6
39
40 /* Default chunk size: 128kb */
41 #define RZIP_DEFAULT_CHUNK_SIZE 131072
42
43 /* Header sizes (in bytes) */
44 #define RZIP_HEADER_SIZE 20
45 #define RZIP_CHUNK_HEADER_SIZE 4
46
47 /* Holds all metadata for an RZIP file stream */
48 struct rzipstream
49 {
50    uint64_t size;
51    /* virtual_ptr: Used to track how much
52     * uncompressed data has been read */
53    uint64_t virtual_ptr;
54    RFILE* file;
55    const struct trans_stream_backend *deflate_backend;
56    void *deflate_stream;
57    const struct trans_stream_backend *inflate_backend;
58    void *inflate_stream;
59    uint8_t *in_buf;
60    uint8_t *out_buf;
61    uint32_t in_buf_size;
62    uint32_t in_buf_ptr;
63    uint32_t out_buf_size;
64    uint32_t out_buf_ptr;
65    uint32_t out_buf_occupancy;
66    uint32_t chunk_size;
67    bool is_compressed;
68    bool is_writing;
69 };
70
71 /* Header Functions */
72
73 /* Reads header information from RZIP file
74  * > Detects whether file is compressed or
75  *   uncompressed data
76  * > If compressed, extracts uncompressed
77  *   file/chunk sizes */
78 static bool rzipstream_read_file_header(rzipstream_t *stream)
79 {
80    unsigned i;
81    int64_t length;
82    uint8_t header_bytes[RZIP_HEADER_SIZE];
83
84    if (!stream)
85       return false;
86
87    for (i = 0; i < RZIP_HEADER_SIZE; i++)
88       header_bytes[i] = 0;
89
90    /* Attempt to read header bytes */
91    if ((length = filestream_read(stream->file, header_bytes, sizeof(header_bytes))) <= 0)
92       return false;
93
94    /* If file length is less than header size
95     * then assume this is uncompressed data */
96
97    /* Check 'magic numbers' - first 8 bytes
98     * of header */
99    if (
100        (length       < RZIP_HEADER_SIZE) || 
101        (header_bytes[0] !=           35) || /* # */
102        (header_bytes[1] !=           82) || /* R */
103        (header_bytes[2] !=           90) || /* Z */
104        (header_bytes[3] !=           73) || /* I */
105        (header_bytes[4] !=           80) || /* P */
106        (header_bytes[5] !=          118) || /* v */
107        (header_bytes[6] != RZIP_VERSION) || /* file format version number */
108        (header_bytes[7] !=           35))   /* # */
109    {
110       /* Reset file to start */
111       filestream_seek(stream->file, 0, SEEK_SET);
112       /* Get 'raw' file size */
113       stream->size          = filestream_get_size(stream->file);
114       stream->is_compressed = false;
115       return true;
116    }
117
118    /* Get uncompressed chunk size - next 4 bytes */
119    if ((stream->chunk_size = ((uint32_t)header_bytes[11] << 24) |
120                         ((uint32_t)header_bytes[10] << 16) |
121                         ((uint32_t)header_bytes[9]  <<  8) |
122                          (uint32_t)header_bytes[8]) == 0)
123       return false;
124
125    /* Get total uncompressed data size - next 8 bytes */
126    if ((stream->size = ((uint64_t)header_bytes[19] << 56) |
127                   ((uint64_t)header_bytes[18] << 48) |
128                   ((uint64_t)header_bytes[17] << 40) |
129                   ((uint64_t)header_bytes[16] << 32) |
130                   ((uint64_t)header_bytes[15] << 24) |
131                   ((uint64_t)header_bytes[14] << 16) |
132                   ((uint64_t)header_bytes[13] <<  8) |
133                    (uint64_t)header_bytes[12]) == 0)
134       return false;
135
136    stream->is_compressed = true;
137    return true;
138 }
139
140 /* Writes header information to RZIP file
141  * > ID 'magic numbers' + uncompressed
142  *   file/chunk sizes */
143 static bool rzipstream_write_file_header(rzipstream_t *stream)
144 {
145    unsigned i;
146    uint8_t header_bytes[RZIP_HEADER_SIZE];
147
148    if (!stream)
149       return false;
150
151    /* Populate header array */
152    for (i = 0; i < RZIP_HEADER_SIZE; i++)
153       header_bytes[i] = 0;
154
155    /* > 'Magic numbers' - first 8 bytes */
156    header_bytes[0]    =        35;    /* # */
157    header_bytes[1]    =        82;    /* R */
158    header_bytes[2]    =        90;    /* Z */
159    header_bytes[3]    =        73;    /* I */
160    header_bytes[4]    =        80;    /* P */
161    header_bytes[5]    =       118;    /* v */
162    header_bytes[6]    = RZIP_VERSION; /* file format version number */
163    header_bytes[7]    =        35;    /* # */
164
165    /* > Uncompressed chunk size - next 4 bytes */
166    header_bytes[11]   = (stream->chunk_size >> 24) & 0xFF;
167    header_bytes[10]   = (stream->chunk_size >> 16) & 0xFF;
168    header_bytes[9]    = (stream->chunk_size >>  8) & 0xFF;
169    header_bytes[8]    =  stream->chunk_size        & 0xFF;
170
171    /* > Total uncompressed data size - next 8 bytes */
172    header_bytes[19]   = (stream->size >> 56) & 0xFF;
173    header_bytes[18]   = (stream->size >> 48) & 0xFF;
174    header_bytes[17]   = (stream->size >> 40) & 0xFF;
175    header_bytes[16]   = (stream->size >> 32) & 0xFF;
176    header_bytes[15]   = (stream->size >> 24) & 0xFF;
177    header_bytes[14]   = (stream->size >> 16) & 0xFF;
178    header_bytes[13]   = (stream->size >>  8) & 0xFF;
179    header_bytes[12]   =  stream->size        & 0xFF;
180
181    /* Reset file to start */
182    filestream_seek(stream->file, 0, SEEK_SET);
183
184    /* Write header bytes */
185    return (filestream_write(stream->file,
186          header_bytes, sizeof(header_bytes)) == RZIP_HEADER_SIZE);
187 }
188
189 /* Stream Initialisation/De-initialisation */
190
191 /* Initialises all members of an rzipstream_t struct,
192  * reading config from existing file header if available */
193 static bool rzipstream_init_stream(
194       rzipstream_t *stream, const char *path, bool is_writing)
195 {
196    unsigned file_mode;
197
198    if (!stream)
199       return false;
200
201    /* Ensure stream has valid initial values */
202    stream->size              = 0;
203    stream->chunk_size        = RZIP_DEFAULT_CHUNK_SIZE;
204    stream->file              = NULL;
205    stream->deflate_backend   = NULL;
206    stream->deflate_stream    = NULL;
207    stream->inflate_backend   = NULL;
208    stream->inflate_stream    = NULL;
209    stream->in_buf            = NULL;
210    stream->in_buf_size       = 0;
211    stream->in_buf_ptr        = 0;
212    stream->out_buf           = NULL;
213    stream->out_buf_size      = 0;
214    stream->out_buf_ptr       = 0;
215    stream->out_buf_occupancy = 0;
216
217    /* Check whether this is a read or write stream */
218    stream->is_writing = is_writing;
219    if (stream->is_writing)
220    {
221       /* Written files are always compressed */
222       stream->is_compressed = true;
223       file_mode             = RETRO_VFS_FILE_ACCESS_WRITE;
224    }
225    /* For read files, must get compression status
226     * from file itself... */
227    else
228       file_mode             = RETRO_VFS_FILE_ACCESS_READ;
229
230    /* Open file */
231    if (!(stream->file = filestream_open(
232          path, file_mode, RETRO_VFS_FILE_ACCESS_HINT_NONE)))
233       return false;
234
235    /* If file is open for writing, output header
236     * (Size component cannot be written until
237     * file is closed...) */
238    if (stream->is_writing)
239    {
240       /* Note: could just write zeros here, but
241        * still want to identify this as an RZIP
242        * file if writing fails partway through */
243       if (!rzipstream_write_file_header(stream))
244          return false;
245    }
246    /* If file is open for reading, parse any existing
247     * header */
248    else if (!rzipstream_read_file_header(stream))
249       return false;
250
251    /* Initialise appropriate transform stream
252     * and determine associated buffer sizes */
253    if (stream->is_writing)
254    {
255       /* Compression */
256       if (!(stream->deflate_backend = trans_stream_get_zlib_deflate_backend()))
257          return false;
258
259       if (!(stream->deflate_stream = stream->deflate_backend->stream_new()))
260          return false;
261
262       /* Set compression level */
263       if (!stream->deflate_backend->define(
264             stream->deflate_stream, "level", RZIP_COMPRESSION_LEVEL))
265          return false;
266
267       /* Buffers
268        * > Input: uncompressed
269        * > Output: compressed */
270       stream->in_buf_size  = stream->chunk_size;
271       stream->out_buf_size = stream->chunk_size * 2;
272       /* > Account for minimum zlib overhead
273        *   of 11 bytes... */ 
274       stream->out_buf_size =
275             (stream->out_buf_size < (stream->in_buf_size + 11)) ?
276                   stream->out_buf_size + 11 :
277                   stream->out_buf_size;
278
279       /* Redundant safety check */
280       if (   (stream->in_buf_size  == 0)
281           || (stream->out_buf_size == 0))
282          return false;
283    }
284    /* When reading, don't need an inflate transform
285     * stream (or buffers) if source file is uncompressed */
286    else if (stream->is_compressed)
287    {
288       /* Decompression */
289       if (!(stream->inflate_backend = trans_stream_get_zlib_inflate_backend()))
290          return false;
291
292       if (!(stream->inflate_stream = stream->inflate_backend->stream_new()))
293          return false;
294
295       /* Buffers
296        * > Input: compressed
297        * > Output: uncompressed
298        * Note 1: Actual compressed chunk sizes are read
299        *         from the file - just allocate a sensible
300        *         default to minimise memory reallocations
301        * Note 2: If file header is valid, output buffer
302        *         should have a size of exactly stream->chunk_size.
303        *         Allocate some additional space, just for
304        *         redundant safety... */
305       stream->in_buf_size  = stream->chunk_size * 2;
306       stream->out_buf_size = stream->chunk_size + (stream->chunk_size >> 2);
307
308       /* Redundant safety check */
309       if (   (stream->in_buf_size  == 0)
310           || (stream->out_buf_size == 0))
311          return false;
312    }
313
314    /* Allocate buffers */
315    if (stream->in_buf_size > 0)
316    {
317       if (!(stream->in_buf = (uint8_t *)calloc(stream->in_buf_size, 1)))
318          return false;
319    }
320
321    if (stream->out_buf_size > 0)
322    {
323       if (!(stream->out_buf = (uint8_t *)calloc(stream->out_buf_size, 1)))
324          return false;
325    }
326
327    return true;
328 }
329
330 /* free()'s all members of an rzipstream_t struct
331  * > Also closes associated file, if currently open */
332 static int rzipstream_free_stream(rzipstream_t *stream)
333 {
334    int ret = 0;
335
336    if (!stream)
337       return -1;
338
339    /* Free transform streams */
340    if (stream->deflate_stream && stream->deflate_backend)
341       stream->deflate_backend->stream_free(stream->deflate_stream);
342
343    stream->deflate_stream  = NULL;
344    stream->deflate_backend = NULL;
345
346    if (stream->inflate_stream && stream->inflate_backend)
347       stream->inflate_backend->stream_free(stream->inflate_stream);
348
349    stream->inflate_stream  = NULL;
350    stream->inflate_backend = NULL;
351
352    /* Free buffers */
353    if (stream->in_buf)
354       free(stream->in_buf);
355    stream->in_buf = NULL;
356
357    if (stream->out_buf)
358       free(stream->out_buf);
359    stream->out_buf = NULL;
360
361    /* Close file */
362    if (stream->file)
363       ret = filestream_close(stream->file);
364    stream->file = NULL;
365
366    free(stream);
367
368    return ret;
369 }
370
371 /* File Open */
372
373 /* Opens a new or existing RZIP file
374  * > Supported 'mode' values are:
375  *   - RETRO_VFS_FILE_ACCESS_READ
376  *   - RETRO_VFS_FILE_ACCESS_WRITE
377  * > When reading, 'path' may reference compressed
378  *   or uncompressed data
379  * Returns NULL if arguments are invalid, file
380  * is invalid or an IO error occurs */
381 rzipstream_t* rzipstream_open(const char *path, unsigned mode)
382 {
383    rzipstream_t *stream = NULL;
384
385    /* Sanity check
386     * > Only RETRO_VFS_FILE_ACCESS_READ and
387     *   RETRO_VFS_FILE_ACCESS_WRITE are supported */
388    if (string_is_empty(path) ||
389        ((mode != RETRO_VFS_FILE_ACCESS_READ) &&
390         (mode != RETRO_VFS_FILE_ACCESS_WRITE)))
391       return NULL;
392
393    /* If opening in read mode, ensure file exists */
394    if ((mode == RETRO_VFS_FILE_ACCESS_READ) &&
395        !path_is_valid(path))
396       return NULL;
397
398    /* Allocate stream object */
399    if (!(stream = (rzipstream_t*)malloc(sizeof(*stream))))
400       return NULL;
401
402    stream->is_compressed   = false;
403    stream->is_writing      = false;
404    stream->size            = 0;
405    stream->chunk_size      = 0;
406    stream->virtual_ptr     = 0;
407    stream->file            = NULL;
408    stream->deflate_backend = NULL;
409    stream->deflate_stream  = NULL;
410    stream->inflate_backend = NULL;
411    stream->inflate_stream  = NULL;
412    stream->in_buf          = NULL;
413    stream->in_buf_size     = 0;
414    stream->in_buf_ptr      = 0;
415    stream->out_buf         = NULL;
416    stream->out_buf_size    = 0;
417    stream->out_buf_ptr     = 0;
418    stream->out_buf_occupancy = 0;
419
420    /* Initialise stream */
421    if (!rzipstream_init_stream(
422          stream, path,
423          (mode == RETRO_VFS_FILE_ACCESS_WRITE)))
424    {
425       rzipstream_free_stream(stream);
426       return NULL;
427    }
428
429    return stream;
430 }
431
432 /* File Read */
433
434 /* Reads and decompresses the next chunk of data
435  * in the RZIP file */
436 static bool rzipstream_read_chunk(rzipstream_t *stream)
437 {
438    unsigned i;
439    uint8_t chunk_header_bytes[RZIP_CHUNK_HEADER_SIZE];
440    uint32_t compressed_chunk_size;
441    uint32_t inflate_read;
442    uint32_t inflate_written;
443
444    if (!stream || !stream->inflate_backend || !stream->inflate_stream)
445       return false;
446
447    for (i = 0; i < RZIP_CHUNK_HEADER_SIZE; i++)
448       chunk_header_bytes[i] = 0;
449
450    /* Attempt to read chunk header bytes */
451    if (filestream_read(
452          stream->file, chunk_header_bytes, sizeof(chunk_header_bytes)) !=
453          RZIP_CHUNK_HEADER_SIZE)
454       return false;
455
456    /* Get size of next compressed chunk */
457    compressed_chunk_size = ((uint32_t)chunk_header_bytes[3] << 24) |
458                            ((uint32_t)chunk_header_bytes[2] << 16) |
459                            ((uint32_t)chunk_header_bytes[1] <<  8) |
460                             (uint32_t)chunk_header_bytes[0];
461    if (compressed_chunk_size == 0)
462       return false;
463
464    /* Resize input buffer, if required */
465    if (compressed_chunk_size > stream->in_buf_size)
466    {
467       free(stream->in_buf);
468       stream->in_buf      = NULL;
469
470       stream->in_buf_size = compressed_chunk_size;
471       stream->in_buf      = (uint8_t *)calloc(stream->in_buf_size, 1);
472       if (!stream->in_buf)
473          return false;
474
475       /* Note: Uncompressed data size is fixed, and read
476        * from the file header - we therefore don't attempt
477        * to resize the output buffer (if it's too small, then
478        * that's an error condition) */
479    }
480
481    /* Read compressed chunk from file */
482    if (filestream_read(
483          stream->file, stream->in_buf, compressed_chunk_size) !=
484          compressed_chunk_size)
485       return false;
486
487    /* Decompress chunk data */
488    stream->inflate_backend->set_in(
489          stream->inflate_stream,
490          stream->in_buf, compressed_chunk_size);
491
492    stream->inflate_backend->set_out(
493          stream->inflate_stream,
494          stream->out_buf, stream->out_buf_size);
495
496    /* Note: We have to set 'flush == true' here, otherwise we
497     * can't guarantee that the entire chunk will be written
498     * to the output buffer - this is inefficient, but not
499     * much we can do... */
500    if (!stream->inflate_backend->trans(
501          stream->inflate_stream, true,
502          &inflate_read, &inflate_written, NULL))
503       return false;
504
505    /* Error checking */
506    if (inflate_read != compressed_chunk_size)
507       return false;
508
509    if ((inflate_written == 0) ||
510        (inflate_written > stream->out_buf_size))
511       return false;
512
513    /* Record current output buffer occupancy
514     * and reset pointer */
515    stream->out_buf_occupancy = inflate_written;
516    stream->out_buf_ptr       = 0;
517
518    return true;
519 }
520
521 /* Reads (a maximum of) 'len' bytes from an RZIP file.
522  * Returns actual number of bytes read, or -1 in
523  * the event of an error */
524 int64_t rzipstream_read(rzipstream_t *stream, void *data, int64_t len)
525 {
526    int64_t data_len  = len;
527    uint8_t *data_ptr = (uint8_t *)data;
528    int64_t data_read = 0;
529
530    if (!stream || stream->is_writing || !data)
531       return -1;
532
533    /* If we are reading uncompressed data, simply
534     * 'pass on' the direct file access request */
535    if (!stream->is_compressed)
536       return filestream_read(stream->file, data, len);
537
538    /* Process input data */
539    while (data_len > 0)
540    {
541       int64_t read_size = 0;
542
543       /* Check whether we have reached the end
544        * of the file */
545       if (stream->virtual_ptr >= stream->size)
546          return data_read;
547
548       /* If everything in the output buffer has already
549        * been read, grab and extract the next chunk
550        * from disk */
551       if (stream->out_buf_ptr >= stream->out_buf_occupancy)
552          if (!rzipstream_read_chunk(stream))
553             return -1;
554
555       /* Get amount of data to 'read out' this loop
556        * > i.e. minimum of remaining output buffer
557        *   occupancy and remaining 'read data' size */
558       if ((read_size = stream->out_buf_occupancy - stream->out_buf_ptr) >
559             data_len)
560          read_size = data_len;
561
562       /* Copy as much cached data as possible into
563        * the read buffer */
564       memcpy(data_ptr, stream->out_buf + stream->out_buf_ptr, (size_t)read_size);
565
566       /* Increment pointers and remaining length */
567       stream->out_buf_ptr += read_size;
568       data_ptr            += read_size;
569       data_len            -= read_size;
570
571       stream->virtual_ptr += read_size;
572
573       data_read           += read_size;
574    }
575
576    return data_read;
577 }
578
579 /* Reads next character from an RZIP file.
580  * Returns character value, or EOF if no data
581  * remains.
582  * Note: Always returns EOF if file is open
583  * for writing. */
584 int rzipstream_getc(rzipstream_t *stream)
585 {
586    char c = 0;
587
588    if (!stream || stream->is_writing)
589       return EOF;
590
591    /* Attempt to read a single character */
592    if (rzipstream_read(stream, &c, 1) == 1)
593       return (int)(unsigned char)c;
594
595    return EOF;
596 }
597
598 /* Reads one line from an RZIP file and stores it
599  * in the character array pointed to by 's'.
600  * It stops reading when either (len-1) characters
601  * are read, the newline character is read, or the
602  * end-of-file is reached, whichever comes first.
603  * On success, returns 's'. In the event of an error,
604  * or if end-of-file is reached and no characters
605  * have been read, returns NULL. */
606 char* rzipstream_gets(rzipstream_t *stream, char *s, size_t len)
607 {
608    size_t str_len;
609    int c         = 0;
610    char *str_ptr = s;
611
612    if (!stream || stream->is_writing || (len == 0))
613       return NULL;
614
615    /* Read bytes until newline or EOF is reached,
616     * or string buffer is full */
617    for (str_len = (len - 1); str_len > 0; str_len--)
618    {
619       /* Get next character */
620       c = rzipstream_getc(stream);
621
622       /* Check for newline and EOF */
623       if (c == EOF)
624          break;
625
626       /* Copy character to string buffer */
627       *str_ptr++ = c;
628
629       /* Check for newline and EOF */
630       if (c == '\n')
631           break;
632    }
633
634    /* Add NUL termination */
635    *str_ptr = '\0';
636
637    /* Check whether EOF has been reached without
638     * reading any characters */
639    if ((str_ptr == s) && (c == EOF))
640       return NULL;
641
642    return (s);
643 }
644
645 /* Reads all data from file specified by 'path' and
646  * copies it to 'buf'.
647  * - 'buf' will be allocated and must be free()'d manually.
648  * - Allocated 'buf' size is equal to 'len'.
649  * Returns false in the event of an error */
650 bool rzipstream_read_file(const char *path, void **buf, int64_t *len)
651 {
652    int64_t bytes_read       = 0;
653    void *content_buf        = NULL;
654    int64_t content_buf_size = 0;
655    rzipstream_t *stream     = NULL;
656
657    if (!buf)
658       return false;
659
660    /* Attempt to open file */
661    if (!(stream = rzipstream_open(path, RETRO_VFS_FILE_ACCESS_READ)))
662    {
663       *buf = NULL;
664       return false;
665    }
666
667    /* Get file size */
668    if ((content_buf_size = rzipstream_get_size(stream)) < 0)
669       goto error;
670
671    if ((int64_t)(uint64_t)(content_buf_size + 1) != (content_buf_size + 1))
672       goto error;
673
674    /* Allocate buffer */
675    if (!(content_buf = malloc((size_t)(content_buf_size + 1))))
676       goto error;
677
678    /* Read file contents */
679    if ((bytes_read = rzipstream_read(stream, content_buf, content_buf_size)) <
680          0)
681       goto error;
682
683    /* Close file */
684    rzipstream_close(stream);
685    stream = NULL;
686
687    /* Add NUL termination for easy/safe handling of strings.
688     * Will only work with sane character formatting (Unix). */
689    ((char*)content_buf)[bytes_read] = '\0';
690
691    /* Assign buffer */
692    *buf = content_buf;
693
694    /* Assign length value, if required */
695    if (len)
696       *len = bytes_read;
697
698    return true;
699
700 error:
701    if (stream)
702       rzipstream_close(stream);
703    stream = NULL;
704
705    if (content_buf)
706       free(content_buf);
707    content_buf = NULL;
708
709    if (len)
710       *len = -1;
711
712    *buf = NULL;
713
714    return false;
715 }
716
717 /* File Write */
718
719 /* Compresses currently cached data and writes it
720  * as the next RZIP file chunk */
721 static bool rzipstream_write_chunk(rzipstream_t *stream)
722 {
723    unsigned i;
724    uint8_t chunk_header_bytes[RZIP_CHUNK_HEADER_SIZE];
725    uint32_t deflate_read;
726    uint32_t deflate_written;
727
728    if (!stream || !stream->deflate_backend || !stream->deflate_stream)
729       return false;
730
731    for (i = 0; i < RZIP_CHUNK_HEADER_SIZE; i++)
732       chunk_header_bytes[i] = 0;
733
734    /* Compress data currently held in input buffer */
735    stream->deflate_backend->set_in(
736          stream->deflate_stream,
737          stream->in_buf, stream->in_buf_ptr);
738
739    stream->deflate_backend->set_out(
740          stream->deflate_stream,
741          stream->out_buf, stream->out_buf_size);
742
743    /* Note: We have to set 'flush == true' here, otherwise we
744     * can't guarantee that the entire chunk will be written
745     * to the output buffer - this is inefficient, but not
746     * much we can do... */
747    if (!stream->deflate_backend->trans(
748          stream->deflate_stream, true,
749          &deflate_read, &deflate_written, NULL))
750       return false;
751
752    /* Error checking */
753    if (deflate_read != stream->in_buf_ptr)
754       return false;
755
756    if ((deflate_written == 0) ||
757        (deflate_written > stream->out_buf_size))
758       return false;
759
760    /* Write compressed chunk size to file */
761    chunk_header_bytes[3] = (deflate_written >> 24) & 0xFF;
762    chunk_header_bytes[2] = (deflate_written >> 16) & 0xFF;
763    chunk_header_bytes[1] = (deflate_written >>  8) & 0xFF;
764    chunk_header_bytes[0] =  deflate_written        & 0xFF;
765
766    if (filestream_write(
767          stream->file, chunk_header_bytes, sizeof(chunk_header_bytes)) !=
768          RZIP_CHUNK_HEADER_SIZE)
769       return false;
770
771    /* Write compressed data to file */
772    if (filestream_write(
773          stream->file, stream->out_buf, deflate_written) != deflate_written)
774       return false;
775
776    /* Reset input buffer pointer */
777    stream->in_buf_ptr = 0;
778
779    return true;
780 }
781
782 /* Writes 'len' bytes to an RZIP file.
783  * Returns actual number of bytes written, or -1
784  * in the event of an error */
785 int64_t rzipstream_write(rzipstream_t *stream, const void *data, int64_t len)
786 {
787    int64_t data_len        = len;
788    const uint8_t *data_ptr = (const uint8_t *)data;
789
790    if (!stream || !stream->is_writing || !data)
791       return -1;
792
793    /* Process input data */
794    while (data_len > 0)
795    {
796       int64_t cache_size = 0;
797
798       /* If input buffer is full, compress and write to disk */
799       if (stream->in_buf_ptr >= stream->in_buf_size)
800          if (!rzipstream_write_chunk(stream))
801             return -1;
802
803       /* Get amount of data to cache during this loop
804        * > i.e. minimum of space remaining in input buffer
805        *   and remaining 'write data' size */
806       if ((cache_size = stream->in_buf_size - stream->in_buf_ptr) > data_len)
807          cache_size = data_len;
808
809       /* Copy as much data as possible into
810        * the input buffer */
811       memcpy(stream->in_buf + stream->in_buf_ptr, data_ptr, (size_t)cache_size);
812
813       /* Increment pointers and remaining length */
814       stream->in_buf_ptr  += cache_size;
815       data_ptr            += cache_size;
816       data_len            -= cache_size;
817
818       stream->size        += cache_size;
819       stream->virtual_ptr += cache_size;
820    }
821
822    /* We always write the specified number of bytes
823     * (unless rzipstream_write_chunk() fails, in
824     * which we register a complete failure...) */
825    return len;
826 }
827
828 /* Writes a single character to an RZIP file.
829  * Returns character written, or EOF in the event
830  * of an error */
831 int rzipstream_putc(rzipstream_t *stream, int c)
832 {
833    char c_char = (char)c;
834
835    if (!stream || !stream->is_writing)
836       return EOF;
837
838    return (rzipstream_write(stream, &c_char, 1) == 1) ?
839          (int)(unsigned char)c : EOF;
840 }
841
842 /* Writes a variable argument list to an RZIP file.
843  * Ugly 'internal' function, required to enable
844  * 'printf' support in the higher level 'interface_stream'.
845  * Returns actual number of bytes written, or -1
846  * in the event of an error */
847 int rzipstream_vprintf(rzipstream_t *stream, const char* format, va_list args)
848 {
849    static char buffer[8 * 1024] = {0};
850    int64_t num_chars            = vsnprintf(buffer,
851          sizeof(buffer), format, args);
852
853    if (num_chars < 0)
854       return -1;
855    else if (num_chars == 0)
856       return 0;
857
858    return (int)rzipstream_write(stream, buffer, num_chars);
859 }
860
861 /* Writes formatted output to an RZIP file.
862  * Returns actual number of bytes written, or -1
863  * in the event of an error */
864 int rzipstream_printf(rzipstream_t *stream, const char* format, ...)
865 {
866    va_list vl;
867    int result = 0;
868
869    /* Initialise variable argument list */
870    va_start(vl, format);
871
872    /* Write variable argument list to file */
873    result = rzipstream_vprintf(stream, format, vl);
874
875    /* End using variable argument list */
876    va_end(vl);
877
878    return result;
879 }
880
881 /* Writes contents of 'data' buffer to file
882  * specified by 'path'.
883  * Returns false in the event of an error */
884 bool rzipstream_write_file(const char *path, const void *data, int64_t len)
885 {
886    int64_t bytes_written = 0;
887    rzipstream_t *stream  = NULL;
888
889    if (!data)
890       return false;
891
892    /* Attempt to open file */
893    if (!(stream = rzipstream_open(path, RETRO_VFS_FILE_ACCESS_WRITE)))
894       return false;
895
896    /* Write contents of data buffer to file */
897    bytes_written = rzipstream_write(stream, data, len);
898
899    /* Close file */
900    if (rzipstream_close(stream) == -1)
901       return false;
902
903    /* Check that the correct number of bytes
904     * were written */
905    return (bytes_written == len);
906 }
907
908 /* File Control */
909
910 /* Sets file position to the beginning of the
911  * specified RZIP file.
912  * Note: It is not recommended to rewind a file
913  * that is open for writing, since the caller
914  * may end up with a file containing junk data
915  * at the end (harmless, but a waste of space). */
916 void rzipstream_rewind(rzipstream_t *stream)
917 {
918    if (!stream)
919       return;
920
921    /* Note: rzipstream_rewind() has no way of
922     * reporting errors (higher level interface
923     * requires a void return type) - so if anything
924     * goes wrong, all we can do is print to stderr
925     * and bail out... */
926
927    /* If we are handling uncompressed data, simply
928     * 'pass on' the direct file access request */
929    if (!stream->is_compressed)
930    {
931       filestream_rewind(stream->file);
932       return;
933    }
934
935    /* If no file access has yet occurred, file is
936     * already at the beginning -> do nothing */
937    if (stream->virtual_ptr == 0)
938       return;
939
940    /* Check whether we are reading or writing */
941    if (stream->is_writing)
942    {
943       /* Reset file position to first chunk location */
944       filestream_seek(stream->file, RZIP_HEADER_SIZE, SEEK_SET);
945       if (filestream_error(stream->file))
946          return;
947
948       /* Reset pointers */
949       stream->virtual_ptr = 0;
950       stream->in_buf_ptr  = 0;
951
952       /* Reset file size */
953       stream->size        = 0;
954    }
955    else
956    {
957       /* Check whether first file chunk is currently
958        * buffered in memory */
959       if ((stream->virtual_ptr < stream->chunk_size) &&
960           (stream->out_buf_ptr < stream->out_buf_occupancy))
961       {
962          /* It is: No file access is therefore required
963           * > Just reset pointers */
964          stream->virtual_ptr = 0;
965          stream->out_buf_ptr = 0;
966       }
967       else
968       {
969          /* It isn't: Have to re-read the first chunk
970           * from disk... */
971
972          /* Reset file position to first chunk location */
973          filestream_seek(stream->file, RZIP_HEADER_SIZE, SEEK_SET);
974          if (filestream_error(stream->file))
975             return;
976
977          /* Read chunk */
978          if (!rzipstream_read_chunk(stream))
979             return;
980
981          /* Reset pointers */
982          stream->virtual_ptr = 0;
983          stream->out_buf_ptr = 0;
984       }
985    }
986 }
987
988 /* File Status */
989
990 /* Returns total size (in bytes) of the *uncompressed*
991  * data in an RZIP file.
992  * (If reading an uncompressed file, this corresponds
993  * to the 'physical' file size in bytes)
994  * Returns -1 in the event of a error. */
995 int64_t rzipstream_get_size(rzipstream_t *stream)
996 {
997    if (!stream)
998       return -1;
999
1000    if (stream->is_compressed)
1001       return stream->size;
1002    return filestream_get_size(stream->file);
1003 }
1004
1005 /* Returns EOF when no further *uncompressed* data
1006  * can be read from an RZIP file. */
1007 int rzipstream_eof(rzipstream_t *stream)
1008 {
1009    if (!stream)
1010       return -1;
1011
1012    if (stream->is_compressed)
1013       return (stream->virtual_ptr >= stream->size) ?
1014             EOF : 0;
1015    return filestream_eof(stream->file);
1016 }
1017
1018 /* Returns the offset of the current byte of *uncompressed*
1019  * data relative to the beginning of an RZIP file.
1020  * Returns -1 in the event of a error. */
1021 int64_t rzipstream_tell(rzipstream_t *stream)
1022 {
1023    if (!stream)
1024       return -1;
1025
1026    if (stream->is_compressed)
1027       return (int64_t)stream->virtual_ptr;
1028    return filestream_tell(stream->file);
1029 }
1030
1031 /* Returns true if specified RZIP file contains
1032  * compressed content */
1033 bool rzipstream_is_compressed(rzipstream_t *stream)
1034 {
1035    return stream && stream->is_compressed;
1036 }
1037
1038 /* File Close */
1039
1040 /* Closes RZIP file. If file is open for writing,
1041  * flushes any remaining buffered data to disk.
1042  * Returns -1 in the event of a error. */
1043 int rzipstream_close(rzipstream_t *stream)
1044 {
1045    if (!stream)
1046       return -1;
1047
1048    /* If we are writing, ensure that any
1049     * remaining uncompressed data is flushed to
1050     * disk and update file header */
1051    if (stream->is_writing)
1052    {
1053       if (stream->in_buf_ptr > 0)
1054          if (!rzipstream_write_chunk(stream))
1055             goto error;
1056
1057       if (!rzipstream_write_file_header(stream))
1058          goto error;
1059    }
1060
1061    /* Free stream
1062     * > This also closes the file */
1063    return rzipstream_free_stream(stream);
1064
1065 error:
1066    /* Stream must be free()'d regardless */
1067    rzipstream_free_stream(stream);
1068    return -1;
1069 }