1 /* Copyright (C) 2010-2020 The RetroArch team
3 * ---------------------------------------------------------------------------------------
4 * The following license statement only applies to this file (rzip_stream.c).
5 * ---------------------------------------------------------------------------------------
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:
13 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
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.
23 #include <string/stdstring.h>
24 #include <file/file_path.h>
26 #include <streams/file_stream.h>
27 #include <streams/trans_stream.h>
29 #include <streams/rzip_stream.h>
31 /* Current RZIP file format version */
32 #define RZIP_VERSION 1
35 * > zlib default of 6 provides the best
36 * balance between file size and
37 * compression speed */
38 #define RZIP_COMPRESSION_LEVEL 6
40 /* Default chunk size: 128kb */
41 #define RZIP_DEFAULT_CHUNK_SIZE 131072
43 /* Header sizes (in bytes) */
44 #define RZIP_HEADER_SIZE 20
45 #define RZIP_CHUNK_HEADER_SIZE 4
47 /* Holds all metadata for an RZIP file stream */
51 /* virtual_ptr: Used to track how much
52 * uncompressed data has been read */
55 const struct trans_stream_backend *deflate_backend;
57 const struct trans_stream_backend *inflate_backend;
63 uint32_t out_buf_size;
65 uint32_t out_buf_occupancy;
71 /* Header Functions */
73 /* Reads header information from RZIP file
74 * > Detects whether file is compressed or
76 * > If compressed, extracts uncompressed
78 static bool rzipstream_read_file_header(rzipstream_t *stream)
82 uint8_t header_bytes[RZIP_HEADER_SIZE];
87 for (i = 0; i < RZIP_HEADER_SIZE; i++)
90 /* Attempt to read header bytes */
91 if ((length = filestream_read(stream->file, header_bytes, sizeof(header_bytes))) <= 0)
94 /* If file length is less than header size
95 * then assume this is uncompressed data */
97 /* Check 'magic numbers' - first 8 bytes
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)) /* # */
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;
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)
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)
136 stream->is_compressed = true;
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)
146 uint8_t header_bytes[RZIP_HEADER_SIZE];
151 /* Populate header array */
152 for (i = 0; i < RZIP_HEADER_SIZE; i++)
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; /* # */
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;
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;
181 /* Reset file to start */
182 filestream_seek(stream->file, 0, SEEK_SET);
184 /* Write header bytes */
185 return (filestream_write(stream->file,
186 header_bytes, sizeof(header_bytes)) == RZIP_HEADER_SIZE);
189 /* Stream Initialisation/De-initialisation */
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)
201 /* Ensure stream has valid initial values */
203 stream->chunk_size = RZIP_DEFAULT_CHUNK_SIZE;
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;
217 /* Check whether this is a read or write stream */
218 stream->is_writing = is_writing;
219 if (stream->is_writing)
221 /* Written files are always compressed */
222 stream->is_compressed = true;
223 file_mode = RETRO_VFS_FILE_ACCESS_WRITE;
225 /* For read files, must get compression status
226 * from file itself... */
228 file_mode = RETRO_VFS_FILE_ACCESS_READ;
231 if (!(stream->file = filestream_open(
232 path, file_mode, RETRO_VFS_FILE_ACCESS_HINT_NONE)))
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)
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))
246 /* If file is open for reading, parse any existing
248 else if (!rzipstream_read_file_header(stream))
251 /* Initialise appropriate transform stream
252 * and determine associated buffer sizes */
253 if (stream->is_writing)
256 if (!(stream->deflate_backend = trans_stream_get_zlib_deflate_backend()))
259 if (!(stream->deflate_stream = stream->deflate_backend->stream_new()))
262 /* Set compression level */
263 if (!stream->deflate_backend->define(
264 stream->deflate_stream, "level", RZIP_COMPRESSION_LEVEL))
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
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;
279 /* Redundant safety check */
280 if ( (stream->in_buf_size == 0)
281 || (stream->out_buf_size == 0))
284 /* When reading, don't need an inflate transform
285 * stream (or buffers) if source file is uncompressed */
286 else if (stream->is_compressed)
289 if (!(stream->inflate_backend = trans_stream_get_zlib_inflate_backend()))
292 if (!(stream->inflate_stream = stream->inflate_backend->stream_new()))
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);
308 /* Redundant safety check */
309 if ( (stream->in_buf_size == 0)
310 || (stream->out_buf_size == 0))
314 /* Allocate buffers */
315 if (stream->in_buf_size > 0)
317 if (!(stream->in_buf = (uint8_t *)calloc(stream->in_buf_size, 1)))
321 if (stream->out_buf_size > 0)
323 if (!(stream->out_buf = (uint8_t *)calloc(stream->out_buf_size, 1)))
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)
339 /* Free transform streams */
340 if (stream->deflate_stream && stream->deflate_backend)
341 stream->deflate_backend->stream_free(stream->deflate_stream);
343 stream->deflate_stream = NULL;
344 stream->deflate_backend = NULL;
346 if (stream->inflate_stream && stream->inflate_backend)
347 stream->inflate_backend->stream_free(stream->inflate_stream);
349 stream->inflate_stream = NULL;
350 stream->inflate_backend = NULL;
354 free(stream->in_buf);
355 stream->in_buf = NULL;
358 free(stream->out_buf);
359 stream->out_buf = NULL;
363 ret = filestream_close(stream->file);
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)
383 rzipstream_t *stream = NULL;
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)))
393 /* If opening in read mode, ensure file exists */
394 if ((mode == RETRO_VFS_FILE_ACCESS_READ) &&
395 !path_is_valid(path))
398 /* Allocate stream object */
399 if (!(stream = (rzipstream_t*)malloc(sizeof(*stream))))
402 stream->is_compressed = false;
403 stream->is_writing = false;
405 stream->chunk_size = 0;
406 stream->virtual_ptr = 0;
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;
420 /* Initialise stream */
421 if (!rzipstream_init_stream(
423 (mode == RETRO_VFS_FILE_ACCESS_WRITE)))
425 rzipstream_free_stream(stream);
434 /* Reads and decompresses the next chunk of data
435 * in the RZIP file */
436 static bool rzipstream_read_chunk(rzipstream_t *stream)
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;
444 if (!stream || !stream->inflate_backend || !stream->inflate_stream)
447 for (i = 0; i < RZIP_CHUNK_HEADER_SIZE; i++)
448 chunk_header_bytes[i] = 0;
450 /* Attempt to read chunk header bytes */
452 stream->file, chunk_header_bytes, sizeof(chunk_header_bytes)) !=
453 RZIP_CHUNK_HEADER_SIZE)
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)
464 /* Resize input buffer, if required */
465 if (compressed_chunk_size > stream->in_buf_size)
467 free(stream->in_buf);
468 stream->in_buf = NULL;
470 stream->in_buf_size = compressed_chunk_size;
471 stream->in_buf = (uint8_t *)calloc(stream->in_buf_size, 1);
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) */
481 /* Read compressed chunk from file */
483 stream->file, stream->in_buf, compressed_chunk_size) !=
484 compressed_chunk_size)
487 /* Decompress chunk data */
488 stream->inflate_backend->set_in(
489 stream->inflate_stream,
490 stream->in_buf, compressed_chunk_size);
492 stream->inflate_backend->set_out(
493 stream->inflate_stream,
494 stream->out_buf, stream->out_buf_size);
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))
506 if (inflate_read != compressed_chunk_size)
509 if ((inflate_written == 0) ||
510 (inflate_written > stream->out_buf_size))
513 /* Record current output buffer occupancy
514 * and reset pointer */
515 stream->out_buf_occupancy = inflate_written;
516 stream->out_buf_ptr = 0;
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)
526 int64_t data_len = len;
527 uint8_t *data_ptr = (uint8_t *)data;
528 int64_t data_read = 0;
530 if (!stream || stream->is_writing || !data)
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);
538 /* Process input data */
541 int64_t read_size = 0;
543 /* Check whether we have reached the end
545 if (stream->virtual_ptr >= stream->size)
548 /* If everything in the output buffer has already
549 * been read, grab and extract the next chunk
551 if (stream->out_buf_ptr >= stream->out_buf_occupancy)
552 if (!rzipstream_read_chunk(stream))
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) >
560 read_size = data_len;
562 /* Copy as much cached data as possible into
564 memcpy(data_ptr, stream->out_buf + stream->out_buf_ptr, (size_t)read_size);
566 /* Increment pointers and remaining length */
567 stream->out_buf_ptr += read_size;
568 data_ptr += read_size;
569 data_len -= read_size;
571 stream->virtual_ptr += read_size;
573 data_read += read_size;
579 /* Reads next character from an RZIP file.
580 * Returns character value, or EOF if no data
582 * Note: Always returns EOF if file is open
584 int rzipstream_getc(rzipstream_t *stream)
588 if (!stream || stream->is_writing)
591 /* Attempt to read a single character */
592 if (rzipstream_read(stream, &c, 1) == 1)
593 return (int)(unsigned char)c;
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)
612 if (!stream || stream->is_writing || (len == 0))
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--)
619 /* Get next character */
620 c = rzipstream_getc(stream);
622 /* Check for newline and EOF */
626 /* Copy character to string buffer */
629 /* Check for newline and EOF */
634 /* Add NUL termination */
637 /* Check whether EOF has been reached without
638 * reading any characters */
639 if ((str_ptr == s) && (c == EOF))
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)
652 int64_t bytes_read = 0;
653 void *content_buf = NULL;
654 int64_t content_buf_size = 0;
655 rzipstream_t *stream = NULL;
660 /* Attempt to open file */
661 if (!(stream = rzipstream_open(path, RETRO_VFS_FILE_ACCESS_READ)))
668 if ((content_buf_size = rzipstream_get_size(stream)) < 0)
671 if ((int64_t)(uint64_t)(content_buf_size + 1) != (content_buf_size + 1))
674 /* Allocate buffer */
675 if (!(content_buf = malloc((size_t)(content_buf_size + 1))))
678 /* Read file contents */
679 if ((bytes_read = rzipstream_read(stream, content_buf, content_buf_size)) <
684 rzipstream_close(stream);
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';
694 /* Assign length value, if required */
702 rzipstream_close(stream);
719 /* Compresses currently cached data and writes it
720 * as the next RZIP file chunk */
721 static bool rzipstream_write_chunk(rzipstream_t *stream)
724 uint8_t chunk_header_bytes[RZIP_CHUNK_HEADER_SIZE];
725 uint32_t deflate_read;
726 uint32_t deflate_written;
728 if (!stream || !stream->deflate_backend || !stream->deflate_stream)
731 for (i = 0; i < RZIP_CHUNK_HEADER_SIZE; i++)
732 chunk_header_bytes[i] = 0;
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);
739 stream->deflate_backend->set_out(
740 stream->deflate_stream,
741 stream->out_buf, stream->out_buf_size);
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))
753 if (deflate_read != stream->in_buf_ptr)
756 if ((deflate_written == 0) ||
757 (deflate_written > stream->out_buf_size))
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;
766 if (filestream_write(
767 stream->file, chunk_header_bytes, sizeof(chunk_header_bytes)) !=
768 RZIP_CHUNK_HEADER_SIZE)
771 /* Write compressed data to file */
772 if (filestream_write(
773 stream->file, stream->out_buf, deflate_written) != deflate_written)
776 /* Reset input buffer pointer */
777 stream->in_buf_ptr = 0;
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)
787 int64_t data_len = len;
788 const uint8_t *data_ptr = (const uint8_t *)data;
790 if (!stream || !stream->is_writing || !data)
793 /* Process input data */
796 int64_t cache_size = 0;
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))
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;
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);
813 /* Increment pointers and remaining length */
814 stream->in_buf_ptr += cache_size;
815 data_ptr += cache_size;
816 data_len -= cache_size;
818 stream->size += cache_size;
819 stream->virtual_ptr += cache_size;
822 /* We always write the specified number of bytes
823 * (unless rzipstream_write_chunk() fails, in
824 * which we register a complete failure...) */
828 /* Writes a single character to an RZIP file.
829 * Returns character written, or EOF in the event
831 int rzipstream_putc(rzipstream_t *stream, int c)
833 char c_char = (char)c;
835 if (!stream || !stream->is_writing)
838 return (rzipstream_write(stream, &c_char, 1) == 1) ?
839 (int)(unsigned char)c : EOF;
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)
849 static char buffer[8 * 1024] = {0};
850 int64_t num_chars = vsnprintf(buffer,
851 sizeof(buffer), format, args);
855 else if (num_chars == 0)
858 return (int)rzipstream_write(stream, buffer, num_chars);
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, ...)
869 /* Initialise variable argument list */
870 va_start(vl, format);
872 /* Write variable argument list to file */
873 result = rzipstream_vprintf(stream, format, vl);
875 /* End using variable argument list */
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)
886 int64_t bytes_written = 0;
887 rzipstream_t *stream = NULL;
892 /* Attempt to open file */
893 if (!(stream = rzipstream_open(path, RETRO_VFS_FILE_ACCESS_WRITE)))
896 /* Write contents of data buffer to file */
897 bytes_written = rzipstream_write(stream, data, len);
900 if (rzipstream_close(stream) == -1)
903 /* Check that the correct number of bytes
905 return (bytes_written == len);
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)
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
927 /* If we are handling uncompressed data, simply
928 * 'pass on' the direct file access request */
929 if (!stream->is_compressed)
931 filestream_rewind(stream->file);
935 /* If no file access has yet occurred, file is
936 * already at the beginning -> do nothing */
937 if (stream->virtual_ptr == 0)
940 /* Check whether we are reading or writing */
941 if (stream->is_writing)
943 /* Reset file position to first chunk location */
944 filestream_seek(stream->file, RZIP_HEADER_SIZE, SEEK_SET);
945 if (filestream_error(stream->file))
949 stream->virtual_ptr = 0;
950 stream->in_buf_ptr = 0;
952 /* Reset file size */
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))
962 /* It is: No file access is therefore required
963 * > Just reset pointers */
964 stream->virtual_ptr = 0;
965 stream->out_buf_ptr = 0;
969 /* It isn't: Have to re-read the first chunk
972 /* Reset file position to first chunk location */
973 filestream_seek(stream->file, RZIP_HEADER_SIZE, SEEK_SET);
974 if (filestream_error(stream->file))
978 if (!rzipstream_read_chunk(stream))
982 stream->virtual_ptr = 0;
983 stream->out_buf_ptr = 0;
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)
1000 if (stream->is_compressed)
1001 return stream->size;
1002 return filestream_get_size(stream->file);
1005 /* Returns EOF when no further *uncompressed* data
1006 * can be read from an RZIP file. */
1007 int rzipstream_eof(rzipstream_t *stream)
1012 if (stream->is_compressed)
1013 return (stream->virtual_ptr >= stream->size) ?
1015 return filestream_eof(stream->file);
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)
1026 if (stream->is_compressed)
1027 return (int64_t)stream->virtual_ptr;
1028 return filestream_tell(stream->file);
1031 /* Returns true if specified RZIP file contains
1032 * compressed content */
1033 bool rzipstream_is_compressed(rzipstream_t *stream)
1035 return stream && stream->is_compressed;
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)
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)
1053 if (stream->in_buf_ptr > 0)
1054 if (!rzipstream_write_chunk(stream))
1057 if (!rzipstream_write_file_header(stream))
1062 * > This also closes the file */
1063 return rzipstream_free_stream(stream);
1066 /* Stream must be free()'d regardless */
1067 rzipstream_free_stream(stream);