Merge pull request #10465 from jdgleaver/rzip-stream-additions

Expand functionality of 'rzip_stream' interface
This commit is contained in:
Autechre 2020-04-17 18:40:14 +02:00 committed by GitHub
commit acfa3fc4d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 504 additions and 7 deletions

View File

@ -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);

View File

@ -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,

View File

@ -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)
{

View File

@ -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,