mirror of
https://github.com/libretro/RetroArch
synced 2025-01-31 15:32:59 +00:00
Merge pull request #10465 from jdgleaver/rzip-stream-additions
Expand functionality of 'rzip_stream' interface
This commit is contained in:
commit
acfa3fc4d4
@ -75,6 +75,9 @@ int64_t intfstream_read(intfstream_internal_t *intf,
|
||||
int64_t intfstream_write(intfstream_internal_t *intf,
|
||||
const void *s, uint64_t len);
|
||||
|
||||
int intfstream_printf(intfstream_internal_t *intf,
|
||||
const char* format, ...);
|
||||
|
||||
int64_t intfstream_get_ptr(intfstream_internal_t *intf);
|
||||
|
||||
char *intfstream_gets(intfstream_internal_t *intf,
|
||||
@ -101,6 +104,8 @@ uint32_t intfstream_get_offset_to_start(intfstream_internal_t *intf);
|
||||
|
||||
uint32_t intfstream_get_frame_size(intfstream_internal_t *intf);
|
||||
|
||||
bool intfstream_is_compressed(intfstream_internal_t *intf);
|
||||
|
||||
intfstream_t* intfstream_open_file(const char *path,
|
||||
unsigned mode, unsigned hints);
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <retro_common_api.h>
|
||||
|
||||
@ -90,6 +91,30 @@ rzipstream_t* rzipstream_open(const char *path, unsigned mode);
|
||||
* the event of an error */
|
||||
int64_t rzipstream_read(rzipstream_t *stream, void *data, int64_t len);
|
||||
|
||||
/* Reads next character from an RZIP file.
|
||||
* Returns character value, or EOF if no data
|
||||
* remains.
|
||||
* Note: Always returns EOF if file is open
|
||||
* for writing. */
|
||||
int rzipstream_getc(rzipstream_t *stream);
|
||||
|
||||
/* Reads one line from an RZIP file and stores it
|
||||
* in the character array pointed to by 's'.
|
||||
* It stops reading when either (len-1) characters
|
||||
* are read, the newline character is read, or the
|
||||
* end-of-file is reached, whichever comes first.
|
||||
* On success, returns 's'. In the event of an error,
|
||||
* or if end-of-file is reached and no characters
|
||||
* have been read, returns NULL. */
|
||||
char* rzipstream_gets(rzipstream_t *stream, char *s, size_t len);
|
||||
|
||||
/* Reads all data from file specified by 'path' and
|
||||
* copies it to 'buf'.
|
||||
* - 'buf' will be allocated and must be free()'d manually.
|
||||
* - Allocated 'buf' size is equal to 'len'.
|
||||
* Returns false in the event of an error */
|
||||
bool rzipstream_read_file(const char *path, void **buf, int64_t *len);
|
||||
|
||||
/* File Write */
|
||||
|
||||
/* Writes 'len' bytes to an RZIP file.
|
||||
@ -97,6 +122,38 @@ int64_t rzipstream_read(rzipstream_t *stream, void *data, int64_t len);
|
||||
* in the event of an error */
|
||||
int64_t rzipstream_write(rzipstream_t *stream, const void *data, int64_t len);
|
||||
|
||||
/* Writes a single character to an RZIP file.
|
||||
* Returns character written, or EOF in the event
|
||||
* of an error */
|
||||
int rzipstream_putc(rzipstream_t *stream, int c);
|
||||
|
||||
/* Writes a variable argument list to an RZIP file.
|
||||
* Ugly 'internal' function, required to enable
|
||||
* 'printf' support in the higher level 'interface_stream'.
|
||||
* Returns actual number of bytes written, or -1
|
||||
* in the event of an error */
|
||||
int rzipstream_vprintf(rzipstream_t *stream, const char* format, va_list args);
|
||||
|
||||
/* Writes formatted output to an RZIP file.
|
||||
* Returns actual number of bytes written, or -1
|
||||
* in the event of an error */
|
||||
int rzipstream_printf(rzipstream_t *stream, const char* format, ...);
|
||||
|
||||
/* Writes contents of 'data' buffer to file
|
||||
* specified by 'path'.
|
||||
* Returns false in the event of an error */
|
||||
bool rzipstream_write_file(const char *path, const void *data, int64_t len);
|
||||
|
||||
/* File Control */
|
||||
|
||||
/* Sets file position to the beginning of the
|
||||
* specified RZIP file.
|
||||
* Note: It is not recommended to rewind a file
|
||||
* that is open for writing, since the caller
|
||||
* may end up with a file containing junk data
|
||||
* at the end (harmless, but a waste of space). */
|
||||
void rzipstream_rewind(rzipstream_t *stream);
|
||||
|
||||
/* File Status */
|
||||
|
||||
/* Returns total size (in bytes) of the *uncompressed*
|
||||
@ -110,6 +167,15 @@ int64_t rzipstream_get_size(rzipstream_t *stream);
|
||||
* can be read from an RZIP file. */
|
||||
int rzipstream_eof(rzipstream_t *stream);
|
||||
|
||||
/* Returns the offset of the current byte of *uncompressed*
|
||||
* data relative to the beginning of an RZIP file.
|
||||
* Returns -1 in the event of a error. */
|
||||
int64_t rzipstream_tell(rzipstream_t *stream);
|
||||
|
||||
/* Returns true if specified RZIP file contains
|
||||
* compressed content */
|
||||
bool rzipstream_is_compressed(rzipstream_t *stream);
|
||||
|
||||
/* File Close */
|
||||
|
||||
/* Closes RZIP file. If file is open for writing,
|
||||
|
@ -349,6 +349,40 @@ int64_t intfstream_write(intfstream_internal_t *intf,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int intfstream_printf(intfstream_internal_t *intf,
|
||||
const char* format, ...)
|
||||
{
|
||||
va_list vl;
|
||||
int result;
|
||||
|
||||
if (!intf)
|
||||
return 0;
|
||||
|
||||
switch (intf->type)
|
||||
{
|
||||
case INTFSTREAM_FILE:
|
||||
va_start(vl, format);
|
||||
result = filestream_vprintf(intf->file.fp, format, vl);
|
||||
va_end(vl);
|
||||
return result;
|
||||
case INTFSTREAM_MEMORY:
|
||||
return -1;
|
||||
case INTFSTREAM_CHD:
|
||||
return -1;
|
||||
case INTFSTREAM_RZIP:
|
||||
#if defined(HAVE_ZLIB)
|
||||
va_start(vl, format);
|
||||
result = rzipstream_vprintf(intf->rzip.fp, format, vl);
|
||||
va_end(vl);
|
||||
return result;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t intfstream_get_ptr(intfstream_internal_t* intf)
|
||||
{
|
||||
if (!intf)
|
||||
@ -390,8 +424,11 @@ char *intfstream_gets(intfstream_internal_t *intf,
|
||||
break;
|
||||
#endif
|
||||
case INTFSTREAM_RZIP:
|
||||
/* Unsupported */
|
||||
#if defined(HAVE_ZLIB)
|
||||
return rzipstream_gets(intf->rzip.fp, buffer, len);
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@ -415,8 +452,11 @@ int intfstream_getc(intfstream_internal_t *intf)
|
||||
break;
|
||||
#endif
|
||||
case INTFSTREAM_RZIP:
|
||||
/* Unsupported */
|
||||
#if defined(HAVE_ZLIB)
|
||||
return rzipstream_getc(intf->rzip.fp);
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
return -1;
|
||||
@ -440,8 +480,11 @@ int64_t intfstream_tell(intfstream_internal_t *intf)
|
||||
break;
|
||||
#endif
|
||||
case INTFSTREAM_RZIP:
|
||||
/* Unsupported */
|
||||
#if defined(HAVE_ZLIB)
|
||||
return (int64_t)rzipstream_tell(intf->rzip.fp);
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
return -1;
|
||||
@ -463,7 +506,9 @@ void intfstream_rewind(intfstream_internal_t *intf)
|
||||
#endif
|
||||
break;
|
||||
case INTFSTREAM_RZIP:
|
||||
/* Unsupported */
|
||||
#if defined(HAVE_ZLIB)
|
||||
rzipstream_rewind(intf->rzip.fp);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -484,8 +529,11 @@ void intfstream_putc(intfstream_internal_t *intf, int c)
|
||||
case INTFSTREAM_CHD:
|
||||
break;
|
||||
case INTFSTREAM_RZIP:
|
||||
/* Unsupported */
|
||||
#if defined(HAVE_ZLIB)
|
||||
rzipstream_putc(intf->rzip.fp, c);
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -515,6 +563,30 @@ uint32_t intfstream_get_frame_size(intfstream_internal_t *intf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool intfstream_is_compressed(intfstream_internal_t *intf)
|
||||
{
|
||||
if (!intf)
|
||||
return false;
|
||||
|
||||
switch (intf->type)
|
||||
{
|
||||
case INTFSTREAM_FILE:
|
||||
return false;
|
||||
case INTFSTREAM_MEMORY:
|
||||
return false;
|
||||
case INTFSTREAM_CHD:
|
||||
return true;
|
||||
case INTFSTREAM_RZIP:
|
||||
#if defined(HAVE_ZLIB)
|
||||
return rzipstream_is_compressed(intf->rzip.fp);
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
intfstream_t* intfstream_open_file(const char *path,
|
||||
unsigned mode, unsigned hints)
|
||||
{
|
||||
|
@ -518,7 +518,7 @@ int64_t rzipstream_read(rzipstream_t *stream, void *data, int64_t len)
|
||||
uint8_t *data_ptr = (uint8_t *)data;
|
||||
int64_t data_read = 0;
|
||||
|
||||
if (!stream || stream->is_writing)
|
||||
if (!stream || stream->is_writing || !data)
|
||||
return -1;
|
||||
|
||||
/* If we are reading uncompressed data, simply
|
||||
@ -566,6 +566,151 @@ int64_t rzipstream_read(rzipstream_t *stream, void *data, int64_t len)
|
||||
return data_read;
|
||||
}
|
||||
|
||||
/* Reads next character from an RZIP file.
|
||||
* Returns character value, or EOF if no data
|
||||
* remains.
|
||||
* Note: Always returns EOF if file is open
|
||||
* for writing. */
|
||||
int rzipstream_getc(rzipstream_t *stream)
|
||||
{
|
||||
char c = 0;
|
||||
|
||||
if (!stream || stream->is_writing)
|
||||
return EOF;
|
||||
|
||||
/* Attempt to read a single character */
|
||||
if (rzipstream_read(stream, &c, 1) == 1)
|
||||
return (int)(unsigned char)c;
|
||||
|
||||
return EOF;
|
||||
}
|
||||
|
||||
/* Reads one line from an RZIP file and stores it
|
||||
* in the character array pointed to by 's'.
|
||||
* It stops reading when either (len-1) characters
|
||||
* are read, the newline character is read, or the
|
||||
* end-of-file is reached, whichever comes first.
|
||||
* On success, returns 's'. In the event of an error,
|
||||
* or if end-of-file is reached and no characters
|
||||
* have been read, returns NULL. */
|
||||
char* rzipstream_gets(rzipstream_t *stream, char *s, size_t len)
|
||||
{
|
||||
int c = 0;
|
||||
char *str_ptr = s;
|
||||
size_t str_len;
|
||||
|
||||
if (!stream || stream->is_writing || (len == 0))
|
||||
return NULL;
|
||||
|
||||
/* Read bytes until newline or EOF is reached,
|
||||
* or string buffer is full */
|
||||
for (str_len = (len - 1); str_len > 0; str_len--)
|
||||
{
|
||||
/* Get next character */
|
||||
c = rzipstream_getc(stream);
|
||||
|
||||
/* Check for newline and EOF */
|
||||
if ((c == '\n') || (c == EOF))
|
||||
break;
|
||||
|
||||
/* Copy character to string buffer */
|
||||
*str_ptr++ = c;
|
||||
}
|
||||
|
||||
/* Add NUL termination */
|
||||
*str_ptr = '\0';
|
||||
|
||||
/* Check whether EOF has been reached without
|
||||
* reading any characters */
|
||||
if ((str_ptr == s) && (c == EOF))
|
||||
return NULL;
|
||||
|
||||
return (s);
|
||||
}
|
||||
|
||||
/* Reads all data from file specified by 'path' and
|
||||
* copies it to 'buf'.
|
||||
* - 'buf' will be allocated and must be free()'d manually.
|
||||
* - Allocated 'buf' size is equal to 'len'.
|
||||
* Returns false in the event of an error */
|
||||
bool rzipstream_read_file(const char *path, void **buf, int64_t *len)
|
||||
{
|
||||
int64_t bytes_read = 0;
|
||||
void *content_buf = NULL;
|
||||
int64_t content_buf_size = 0;
|
||||
rzipstream_t *stream = NULL;
|
||||
|
||||
if (!buf)
|
||||
return false;
|
||||
|
||||
/* Attempt to open file */
|
||||
stream = rzipstream_open(path, RETRO_VFS_FILE_ACCESS_READ);
|
||||
|
||||
if (!stream)
|
||||
{
|
||||
fprintf(stderr, "[rzipstream] Failed to open file: %s\n", path ? path : "");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Get file size */
|
||||
content_buf_size = rzipstream_get_size(stream);
|
||||
|
||||
if (content_buf_size < 0)
|
||||
goto error;
|
||||
|
||||
if ((int64_t)(uint64_t)(content_buf_size + 1) != (content_buf_size + 1))
|
||||
goto error;
|
||||
|
||||
/* Allocate buffer */
|
||||
content_buf = malloc((size_t)(content_buf_size + 1));
|
||||
|
||||
if (!content_buf)
|
||||
goto error;
|
||||
|
||||
/* Read file contents */
|
||||
bytes_read = rzipstream_read(stream, content_buf, content_buf_size);
|
||||
|
||||
if (bytes_read < 0)
|
||||
{
|
||||
fprintf(stderr, "[rzipstream] Failed to read file: %s\n", path);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Close file */
|
||||
rzipstream_close(stream);
|
||||
stream = NULL;
|
||||
|
||||
/* Add NUL termination for easy/safe handling of strings.
|
||||
* Will only work with sane character formatting (Unix). */
|
||||
((char*)content_buf)[bytes_read] = '\0';
|
||||
|
||||
/* Assign buffer */
|
||||
*buf = content_buf;
|
||||
|
||||
/* Assign length value, if required */
|
||||
if (len)
|
||||
*len = bytes_read;
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
|
||||
if (stream)
|
||||
rzipstream_close(stream);
|
||||
stream = NULL;
|
||||
|
||||
if (content_buf)
|
||||
free(content_buf);
|
||||
content_buf = NULL;
|
||||
|
||||
if (len)
|
||||
*len = -1;
|
||||
|
||||
*buf = NULL;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* File Write */
|
||||
|
||||
/* Compresses currently cached data and writes it
|
||||
@ -638,7 +783,7 @@ int64_t rzipstream_write(rzipstream_t *stream, const void *data, int64_t len)
|
||||
int64_t data_len = len;
|
||||
const uint8_t *data_ptr = (const uint8_t *)data;
|
||||
|
||||
if (!stream || !stream->is_writing)
|
||||
if (!stream || !stream->is_writing || !data)
|
||||
return -1;
|
||||
|
||||
/* Process input data */
|
||||
@ -676,6 +821,194 @@ int64_t rzipstream_write(rzipstream_t *stream, const void *data, int64_t len)
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Writes a single character to an RZIP file.
|
||||
* Returns character written, or EOF in the event
|
||||
* of an error */
|
||||
int rzipstream_putc(rzipstream_t *stream, int c)
|
||||
{
|
||||
char c_char = (char)c;
|
||||
|
||||
if (!stream || !stream->is_writing)
|
||||
return EOF;
|
||||
|
||||
return (rzipstream_write(stream, &c_char, 1) == 1) ?
|
||||
(int)(unsigned char)c : EOF;
|
||||
}
|
||||
|
||||
/* Writes a variable argument list to an RZIP file.
|
||||
* Ugly 'internal' function, required to enable
|
||||
* 'printf' support in the higher level 'interface_stream'.
|
||||
* Returns actual number of bytes written, or -1
|
||||
* in the event of an error */
|
||||
int rzipstream_vprintf(rzipstream_t *stream, const char* format, va_list args)
|
||||
{
|
||||
static char buffer[8 * 1024] = {0};
|
||||
int64_t num_chars = vsprintf(buffer, format, args);
|
||||
|
||||
if (num_chars < 0)
|
||||
return -1;
|
||||
else if (num_chars == 0)
|
||||
return 0;
|
||||
|
||||
return (int)rzipstream_write(stream, buffer, num_chars);
|
||||
}
|
||||
|
||||
/* Writes formatted output to an RZIP file.
|
||||
* Returns actual number of bytes written, or -1
|
||||
* in the event of an error */
|
||||
int rzipstream_printf(rzipstream_t *stream, const char* format, ...)
|
||||
{
|
||||
va_list vl;
|
||||
int result = 0;
|
||||
|
||||
/* Initialise variable argument list */
|
||||
va_start(vl, format);
|
||||
|
||||
/* Write variable argument list to file */
|
||||
result = rzipstream_vprintf(stream, format, vl);
|
||||
|
||||
/* End using variable argument list */
|
||||
va_end(vl);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Writes contents of 'data' buffer to file
|
||||
* specified by 'path'.
|
||||
* Returns false in the event of an error */
|
||||
bool rzipstream_write_file(const char *path, const void *data, int64_t len)
|
||||
{
|
||||
int64_t bytes_written = 0;
|
||||
rzipstream_t *stream = NULL;
|
||||
|
||||
if (!data)
|
||||
return false;
|
||||
|
||||
/* Attempt to open file */
|
||||
stream = rzipstream_open(path, RETRO_VFS_FILE_ACCESS_WRITE);
|
||||
|
||||
if (!stream)
|
||||
{
|
||||
fprintf(stderr, "[rzipstream] Failed to open file: %s\n", path ? path : "");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Write contents of data buffer to file */
|
||||
bytes_written = rzipstream_write(stream, data, len);
|
||||
|
||||
/* Close file */
|
||||
if (rzipstream_close(stream) == -1)
|
||||
{
|
||||
fprintf(stderr, "[rzipstream] Failed to close file: %s\nData will be lost...\n", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check that the correct number of bytes
|
||||
* were written */
|
||||
if (bytes_written != len)
|
||||
{
|
||||
fprintf(stderr, "[rzipstream] Wrote incorrect number of bytes to file: %s\n", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* File Control */
|
||||
|
||||
/* Sets file position to the beginning of the
|
||||
* specified RZIP file.
|
||||
* Note: It is not recommended to rewind a file
|
||||
* that is open for writing, since the caller
|
||||
* may end up with a file containing junk data
|
||||
* at the end (harmless, but a waste of space). */
|
||||
void rzipstream_rewind(rzipstream_t *stream)
|
||||
{
|
||||
if (!stream)
|
||||
return;
|
||||
|
||||
/* Note: rzipstream_rewind() has no way of
|
||||
* reporting errors (higher level interface
|
||||
* requires a void return type) - so if anything
|
||||
* goes wrong, all we can do is print to stderr
|
||||
* and bail out... */
|
||||
|
||||
/* If we are handling uncompressed data, simply
|
||||
* 'pass on' the direct file access request */
|
||||
if (!stream->is_compressed)
|
||||
{
|
||||
filestream_rewind(stream->file);
|
||||
return;
|
||||
}
|
||||
|
||||
/* If no file access has yet occurred, file is
|
||||
* already at the beginning -> do nothing */
|
||||
if (stream->virtual_ptr == 0)
|
||||
return;
|
||||
|
||||
/* Check whether we are reading or writing */
|
||||
if (stream->is_writing)
|
||||
{
|
||||
/* Reset file position to first chunk location */
|
||||
filestream_seek(stream->file, RZIP_HEADER_SIZE, SEEK_SET);
|
||||
if (filestream_error(stream->file))
|
||||
{
|
||||
fprintf(
|
||||
stderr,
|
||||
"rzipstream_rewind(): Failed to reset file position...\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Reset pointers */
|
||||
stream->virtual_ptr = 0;
|
||||
stream->in_buf_ptr = 0;
|
||||
|
||||
/* Reset file size */
|
||||
stream->size = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Check whether first file chunk is currently
|
||||
* buffered in memory */
|
||||
if ((stream->virtual_ptr < stream->chunk_size) &&
|
||||
(stream->out_buf_ptr < stream->out_buf_occupancy))
|
||||
{
|
||||
/* It is: No file access is therefore required
|
||||
* > Just reset pointers */
|
||||
stream->virtual_ptr = 0;
|
||||
stream->out_buf_ptr = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* It isn't: Have to re-read the first chunk
|
||||
* from disk... */
|
||||
|
||||
/* Reset file position to first chunk location */
|
||||
filestream_seek(stream->file, RZIP_HEADER_SIZE, SEEK_SET);
|
||||
if (filestream_error(stream->file))
|
||||
{
|
||||
fprintf(
|
||||
stderr,
|
||||
"rzipstream_rewind(): Failed to reset file position...\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Read chunk */
|
||||
if (!rzipstream_read_chunk(stream))
|
||||
{
|
||||
fprintf(
|
||||
stderr,
|
||||
"rzipstream_rewind(): Failed to read first chunk of file...\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Reset pointers */
|
||||
stream->virtual_ptr = 0;
|
||||
stream->out_buf_ptr = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* File Status */
|
||||
|
||||
/* Returns total size (in bytes) of the *uncompressed*
|
||||
@ -708,6 +1041,27 @@ int rzipstream_eof(rzipstream_t *stream)
|
||||
return filestream_eof(stream->file);
|
||||
}
|
||||
|
||||
/* Returns the offset of the current byte of *uncompressed*
|
||||
* data relative to the beginning of an RZIP file.
|
||||
* Returns -1 in the event of a error. */
|
||||
int64_t rzipstream_tell(rzipstream_t *stream)
|
||||
{
|
||||
if (!stream)
|
||||
return -1;
|
||||
|
||||
return (int64_t)stream->virtual_ptr;
|
||||
}
|
||||
|
||||
/* Returns true if specified RZIP file contains
|
||||
* compressed content */
|
||||
bool rzipstream_is_compressed(rzipstream_t *stream)
|
||||
{
|
||||
if (!stream)
|
||||
return false;
|
||||
|
||||
return stream->is_compressed;
|
||||
}
|
||||
|
||||
/* File Close */
|
||||
|
||||
/* Closes RZIP file. If file is open for writing,
|
||||
|
Loading…
x
Reference in New Issue
Block a user