/*************************************************************************** corefile.c File access functions. Copyright Nicola Salmoria and the MAME Team. Visit http://mamedev.org for licensing and usage restrictions. ***************************************************************************/ #include "corefile.h" #include "unicode.h" #include #include #include /*************************************************************************** VALIDATION ***************************************************************************/ #if !defined(CRLF) || (CRLF < 1) || (CRLF > 3) #error CRLF undefined: must be 1 (CR), 2 (LF) or 3 (CR/LF) #endif /*************************************************************************** CONSTANTS ***************************************************************************/ #define FILE_BUFFER_SIZE 512 #define OPEN_FLAG_HAS_CRC 0x10000 /*************************************************************************** TYPE DEFINITIONS ***************************************************************************/ enum _text_file_type { TFT_OSD = 0, /* OSD depdendent encoding format used when BOMs missing */ TFT_UTF8, /* UTF-8 */ TFT_UTF16BE, /* UTF-16 (big endian) */ TFT_UTF16LE, /* UTF-16 (little endian) */ TFT_UTF32BE, /* UTF-32 (UCS-4) (big endian) */ TFT_UTF32LE /* UTF-32 (UCS-4) (little endian) */ }; typedef enum _text_file_type text_file_type; typedef struct _zlib_data zlib_data; struct _zlib_data { z_stream stream; UINT8 buffer[1024]; UINT64 realoffset; UINT64 nextoffset; }; /* typedef struct _core_file core_file -- declared in corefile.h */ struct _core_file { osd_file * file; /* OSD file handle */ zlib_data * zdata; /* compression data */ UINT32 openflags; /* flags we were opened with */ UINT8 data_allocated; /* was the data allocated by us? */ UINT8 * data; /* file data, if RAM-based */ UINT64 offset; /* current file offset */ UINT64 length; /* total file length */ text_file_type text_type; /* text output format */ char back_chars[UTF8_CHAR_MAX]; /* buffer to hold characters for ungetc */ int back_char_head; /* head of ungetc buffer */ int back_char_tail; /* tail of ungetc buffer */ UINT64 bufferbase; /* base offset of internal buffer */ UINT32 bufferbytes; /* bytes currently loaded into buffer */ UINT8 buffer[FILE_BUFFER_SIZE]; /* buffer data */ }; /*************************************************************************** FUNCTION PROTOTYPES ***************************************************************************/ /* misc helpers */ static UINT32 safe_buffer_copy(const void *source, UINT32 sourceoffs, UINT32 sourcelen, void *dest, UINT32 destoffs, UINT32 destlen); static file_error osd_or_zlib_read(core_file *file, void *buffer, UINT64 offset, UINT32 length, UINT32 *actual); static file_error osd_or_zlib_write(core_file *file, const void *buffer, UINT64 offset, UINT32 length, UINT32 *actual); /*************************************************************************** INLINE FUNCTIONS ***************************************************************************/ /*------------------------------------------------- is_directory_separator - is a given character a directory separator? The following logic works for most platforms -------------------------------------------------*/ INLINE int is_directory_separator(char c) { return (c == '\\' || c == '/' || c == ':'); } /*************************************************************************** FILE OPEN/CLOSE ***************************************************************************/ /*------------------------------------------------- core_fopen - open a file for access and return an error code -------------------------------------------------*/ file_error core_fopen(const char *filename, UINT32 openflags, core_file **file) { file_error filerr = FILERR_NOT_FOUND; /* allocate the file itself */ *file = (core_file *)malloc(sizeof(**file)); if (*file == NULL) return FILERR_OUT_OF_MEMORY; memset(*file, 0, sizeof(**file)); /* attempt to open the file */ filerr = osd_open(filename, openflags, &(*file)->file, &(*file)->length); (*file)->openflags = openflags; /* handle errors and return */ if (filerr != FILERR_NONE) { core_fclose(*file); *file = NULL; } return filerr; } /*------------------------------------------------- core_fopen_ram_internal - open a RAM-based buffer for file-like access, possibly copying the data, and return an error code -------------------------------------------------*/ static file_error core_fopen_ram_internal(const void *data, size_t length, int copy_buffer, UINT32 openflags, core_file **file) { /* can only do this for read access */ if ((openflags & OPEN_FLAG_WRITE) != 0) return FILERR_INVALID_ACCESS; if ((openflags & OPEN_FLAG_CREATE) != 0) return FILERR_INVALID_ACCESS; /* allocate the file itself */ *file = (core_file *)malloc(sizeof(**file) + (copy_buffer ? length : 0)); if (*file == NULL) return FILERR_OUT_OF_MEMORY; memset(*file, 0, sizeof(**file)); /* copy the buffer, if we're asked to */ if (copy_buffer) { void *dest = ((UINT8 *) *file) + sizeof(**file); memcpy(dest, data, length); data = dest; } /* claim the buffer */ (*file)->data = (UINT8 *)data; (*file)->length = length; (*file)->openflags = openflags; return FILERR_NONE; } /*------------------------------------------------- core_fopen_ram - open a RAM-based buffer for file-like access and return an error code -------------------------------------------------*/ file_error core_fopen_ram(const void *data, size_t length, UINT32 openflags, core_file **file) { return core_fopen_ram_internal(data, length, FALSE, openflags, file); } /*------------------------------------------------- core_fopen_ram_copy - open a copy of a RAM-based buffer for file-like access and return an error code -------------------------------------------------*/ file_error core_fopen_ram_copy(const void *data, size_t length, UINT32 openflags, core_file **file) { return core_fopen_ram_internal(data, length, TRUE, openflags, file); } /*------------------------------------------------- core_fclose - closes a file -------------------------------------------------*/ void core_fclose(core_file *file) { /* close files and free memory */ if (file->zdata != NULL) core_fcompress(file, FCOMPRESS_NONE); if (file->file != NULL) osd_close(file->file); if (file->data != NULL && file->data_allocated) free(file->data); free(file); } /*------------------------------------------------- core_fcompress - enable/disable streaming file compression via zlib; level is 0 to disable compression, or up to 9 for max compression -------------------------------------------------*/ file_error core_fcompress(core_file *file, int level) { file_error result = FILERR_NONE; /* can only do this for read-only and write-only cases */ if ((file->openflags & OPEN_FLAG_WRITE) != 0 && (file->openflags & OPEN_FLAG_READ) != 0) return FILERR_INVALID_ACCESS; /* if we have been compressing, flush and free the data */ if (file->zdata != NULL && level == FCOMPRESS_NONE) { int zerr = Z_OK; /* flush any remaining data if we are writing */ while ((file->openflags & OPEN_FLAG_WRITE) != 0 && zerr != Z_STREAM_END) { UINT32 actualdata; file_error filerr; /* deflate some more */ zerr = deflate(&file->zdata->stream, Z_FINISH); if (zerr != Z_STREAM_END && zerr != Z_OK) { result = FILERR_INVALID_DATA; break; } /* write the data */ if (file->zdata->stream.avail_out != sizeof(file->zdata->buffer)) { filerr = osd_write(file->file, file->zdata->buffer, file->zdata->realoffset, sizeof(file->zdata->buffer) - file->zdata->stream.avail_out, &actualdata); if (filerr != FILERR_NONE) break; file->zdata->realoffset += actualdata; file->zdata->stream.next_out = file->zdata->buffer; file->zdata->stream.avail_out = sizeof(file->zdata->buffer); } } /* end the appropriate operation */ if ((file->openflags & OPEN_FLAG_WRITE) != 0) deflateEnd(&file->zdata->stream); else inflateEnd(&file->zdata->stream); /* free memory */ free(file->zdata); file->zdata = NULL; } /* if we are just starting to compress, allocate a new buffer */ if (file->zdata == NULL && level > FCOMPRESS_NONE) { int zerr; /* allocate memory */ file->zdata = (zlib_data *)malloc(sizeof(*file->zdata)); if (file->zdata == NULL) return FILERR_OUT_OF_MEMORY; memset(file->zdata, 0, sizeof(*file->zdata)); /* initialize the stream and compressor */ if ((file->openflags & OPEN_FLAG_WRITE) != 0) { file->zdata->stream.next_out = file->zdata->buffer; file->zdata->stream.avail_out = sizeof(file->zdata->buffer); zerr = deflateInit(&file->zdata->stream, level); } else zerr = inflateInit(&file->zdata->stream); /* on error, return an error */ if (zerr != Z_OK) { free(file->zdata); file->zdata = NULL; return FILERR_OUT_OF_MEMORY; } /* flush buffers */ file->bufferbytes = 0; /* set the initial offset */ file->zdata->realoffset = file->offset; file->zdata->nextoffset = file->offset; } return result; } /*************************************************************************** FILE POSITIONING ***************************************************************************/ /*------------------------------------------------- core_fseek - seek within a file -------------------------------------------------*/ int core_fseek(core_file *file, INT64 offset, int whence) { int err = 0; /* error if compressing */ if (file->zdata != NULL) return 1; /* flush any buffered char */ file->back_char_head = 0; file->back_char_tail = 0; /* switch off the relative location */ switch (whence) { case SEEK_SET: file->offset = offset; break; case SEEK_CUR: file->offset += offset; break; case SEEK_END: file->offset = file->length + offset; break; } return err; } /*------------------------------------------------- core_ftell - return the current file position -------------------------------------------------*/ UINT64 core_ftell(core_file *file) { /* return the current offset */ return file->offset; } /*------------------------------------------------- core_feof - return 1 if we're at the end of file -------------------------------------------------*/ int core_feof(core_file *file) { /* check for buffered chars */ if (file->back_char_head != file->back_char_tail) return 0; /* if the offset == length, we're at EOF */ return (file->offset >= file->length); } /*------------------------------------------------- core_fsize - returns the size of a file -------------------------------------------------*/ UINT64 core_fsize(core_file *file) { return file->length; } /*************************************************************************** FILE READ ***************************************************************************/ /*------------------------------------------------- core_fread - read from a file -------------------------------------------------*/ UINT32 core_fread(core_file *file, void *buffer, UINT32 length) { file_error filerr; UINT32 bytes_read = 0; /* flush any buffered char */ file->back_char_head = 0; file->back_char_tail = 0; /* handle real files */ if (file->data == NULL) { /* if we're within the buffer, consume that first */ if (file->offset >= file->bufferbase && file->offset < file->bufferbase + file->bufferbytes) bytes_read += safe_buffer_copy(file->buffer, file->offset - file->bufferbase, file->bufferbytes, buffer, bytes_read, length); /* if we've got a small amount left, read it into the buffer first */ if (bytes_read < length) { if (length - bytes_read < sizeof(file->buffer) / 2) { /* read as much as makes sense into the buffer */ file->bufferbase = file->offset + bytes_read; file->bufferbytes = 0; filerr = osd_or_zlib_read(file, file->buffer, file->bufferbase, sizeof(file->buffer), &file->bufferbytes); /* do a bounded copy from the buffer to the destination */ bytes_read += safe_buffer_copy(file->buffer, 0, file->bufferbytes, buffer, bytes_read, length); } /* read the remainder directly from the file */ else { UINT32 new_bytes_read = 0; filerr = osd_or_zlib_read(file, (UINT8 *)buffer + bytes_read, file->offset + bytes_read, length - bytes_read, &new_bytes_read); bytes_read += new_bytes_read; } } } /* handle RAM-based files */ else bytes_read += safe_buffer_copy(file->data, (UINT32)file->offset, file->length, buffer, bytes_read, length); /* return the number of bytes read */ file->offset += bytes_read; return bytes_read; } /*------------------------------------------------- core_fgetc - read a character from a file -------------------------------------------------*/ int core_fgetc(core_file *file) { int result; /* refresh buffer, if necessary */ if (file->back_char_head == file->back_char_tail) { utf16_char utf16_buffer[UTF16_CHAR_MAX]; char utf8_buffer[UTF8_CHAR_MAX]; char default_buffer[16]; unicode_char uchar = (unicode_char) ~0; int readlen, charlen; /* do we need to check the byte order marks? */ if (file->offset == 0) { UINT8 bom[4]; int pos = 0; if (core_fread(file, bom, 4) == 4) { if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf) { file->text_type = TFT_UTF8; pos = 3; } else if (bom[0] == 0x00 && bom[1] == 0x00 && bom[2] == 0xfe && bom[3] == 0xff) { file->text_type = TFT_UTF32BE; pos = 4; } else if (bom[0] == 0xff && bom[1] == 0xfe && bom[2] == 0x00 && bom[3] == 0x00) { file->text_type = TFT_UTF32LE; pos = 4; } else if (bom[0] == 0xfe && bom[1] == 0xff) { file->text_type = TFT_UTF16BE; pos = 2; } else if (bom[0] == 0xff && bom[1] == 0xfe) { file->text_type = TFT_UTF16LE; pos = 2; } else { file->text_type = TFT_OSD; pos = 0; } } core_fseek(file, pos, SEEK_SET); } /* fetch the next character */ switch (file->text_type) { default: case TFT_OSD: readlen = core_fread(file, default_buffer, sizeof(default_buffer)); if (readlen > 0) { charlen = osd_uchar_from_osdchar(&uchar, default_buffer, readlen / sizeof(default_buffer[0])); core_fseek(file, (INT64) (charlen * sizeof(default_buffer[0])) - readlen, SEEK_CUR); } break; case TFT_UTF8: readlen = core_fread(file, utf8_buffer, sizeof(utf8_buffer)); if (readlen > 0) { charlen = uchar_from_utf8(&uchar, utf8_buffer, readlen / sizeof(utf8_buffer[0])); core_fseek(file, (INT64) (charlen * sizeof(utf8_buffer[0])) - readlen, SEEK_CUR); } break; case TFT_UTF16BE: readlen = core_fread(file, utf16_buffer, sizeof(utf16_buffer)); if (readlen > 0) { charlen = uchar_from_utf16be(&uchar, utf16_buffer, readlen / sizeof(utf16_buffer[0])); core_fseek(file, (INT64) (charlen * sizeof(utf16_buffer[0])) - readlen, SEEK_CUR); } break; case TFT_UTF16LE: readlen = core_fread(file, utf16_buffer, sizeof(utf16_buffer)); if (readlen > 0) { charlen = uchar_from_utf16le(&uchar, utf16_buffer, readlen / sizeof(utf16_buffer[0])); core_fseek(file, (INT64) (charlen * sizeof(utf16_buffer[0])) - readlen, SEEK_CUR); } break; case TFT_UTF32BE: if (core_fread(file, &uchar, sizeof(uchar)) == sizeof(uchar)) uchar = BIG_ENDIANIZE_INT32(uchar); break; case TFT_UTF32LE: if (core_fread(file, &uchar, sizeof(uchar)) == sizeof(uchar)) uchar = LITTLE_ENDIANIZE_INT32(uchar); break; } if (uchar != ~0) { /* place the new character in the ring buffer */ file->back_char_head = 0; file->back_char_tail = utf8_from_uchar(file->back_chars, ARRAY_LENGTH(file->back_chars), uchar); /* assert(file->back_char_tail != -1);*/ } } /* now read from the ring buffer */ if (file->back_char_head != file->back_char_tail) { result = file->back_chars[file->back_char_head++]; file->back_char_head %= ARRAY_LENGTH(file->back_chars); } else result = EOF; return result; } /*------------------------------------------------- core_ungetc - put back a character read from a file -------------------------------------------------*/ int core_ungetc(int c, core_file *file) { file->back_chars[file->back_char_tail++] = (char) c; file->back_char_tail %= ARRAY_LENGTH(file->back_chars); return c; } /*------------------------------------------------- core_fgets - read a line from a text file -------------------------------------------------*/ char *core_fgets(char *s, int n, core_file *file) { char *cur = s; /* loop while we have characters */ while (n > 0) { int c = core_fgetc(file); if (c == EOF) break; /* if there's a CR, look for an LF afterwards */ if (c == 0x0d) { int c2 = core_fgetc(file); if (c2 != 0x0a) core_ungetc(c2, file); *cur++ = 0x0d; n--; break; } /* if there's an LF, reinterp as a CR for consistency */ else if (c == 0x0a) { *cur++ = 0x0d; n--; break; } /* otherwise, pop the character in and continue */ *cur++ = c; n--; } /* if we put nothing in, return NULL */ if (cur == s) return NULL; /* otherwise, terminate */ if (n > 0) *cur++ = 0; return s; } /*------------------------------------------------- core_fbuffer - return a pointer to the file buffer; if it doesn't yet exist, load the file into RAM first -------------------------------------------------*/ const void *core_fbuffer(core_file *file) { file_error filerr; UINT32 read_length; /* if we already have data, just return it */ if (file->data != NULL) return file->data; /* allocate some memory */ file->data = (UINT8 *)malloc(file->length); if (file->data == NULL) return NULL; file->data_allocated = TRUE; /* read the file */ filerr = osd_or_zlib_read(file, file->data, 0, file->length, &read_length); if (filerr != FILERR_NONE || read_length != file->length) { free(file->data); file->data = NULL; return NULL; } /* close the file because we don't need it anymore */ osd_close(file->file); file->file = NULL; return file->data; } /*------------------------------------------------- core_fload - open a file with the specified filename, read it into memory, and return a pointer -------------------------------------------------*/ file_error core_fload(const char *filename, void **data, UINT32 *length) { core_file *file = NULL; file_error err; UINT64 size; /* attempt to open the file */ err = core_fopen(filename, OPEN_FLAG_READ, &file); if (err != FILERR_NONE) return err; /* get the size */ size = core_fsize(file); if ((UINT32)size != size) { core_fclose(file); return FILERR_OUT_OF_MEMORY; } /* allocate memory */ *data = malloc(size); if (length != NULL) *length = (UINT32)size; /* read the data */ if (core_fread(file, *data, size) != size) { core_fclose(file); free(*data); return FILERR_FAILURE; } return FILERR_NONE; } /*************************************************************************** FILE WRITE ***************************************************************************/ /*------------------------------------------------- core_fwrite - write to a file -------------------------------------------------*/ UINT32 core_fwrite(core_file *file, const void *buffer, UINT32 length) { UINT32 bytes_written = 0; file_error filerr; /* can't write to RAM-based stuff */ if (file->data != NULL) return 0; /* flush any buffered char */ file->back_char_head = 0; file->back_char_tail = 0; /* invalidate any buffered data */ file->bufferbytes = 0; /* do the write */ filerr = osd_or_zlib_write(file, buffer, file->offset, length, &bytes_written); /* return the number of bytes read */ file->offset += bytes_written; file->length = MAX(file->length, file->offset); return bytes_written; } /*------------------------------------------------- core_fputs - write a line to a text file -------------------------------------------------*/ int core_fputs(core_file *f, const char *s) { char convbuf[1024]; char *pconvbuf = convbuf; int count = 0; /* is this the beginning of the file? if so, write a byte order mark */ if (f->offset == 0 && !(f->openflags & OPEN_FLAG_NO_BOM)) { *pconvbuf++ = (char)0xef; *pconvbuf++ = (char)0xbb; *pconvbuf++ = (char)0xbf; } /* convert '\n' to platform dependant line endings */ while (*s != 0) { if (*s == '\n') { if (CRLF == 1) /* CR only */ *pconvbuf++ = 13; else if (CRLF == 2) /* LF only */ *pconvbuf++ = 10; else if (CRLF == 3) /* CR+LF */ { *pconvbuf++ = 13; *pconvbuf++ = 10; } } else *pconvbuf++ = *s; s++; /* if we overflow, break into chunks */ if (pconvbuf >= convbuf + ARRAY_LENGTH(convbuf) - 10) { count += core_fwrite(f, convbuf, pconvbuf - convbuf); pconvbuf = convbuf; } } /* final flush */ if (pconvbuf != convbuf) count += core_fwrite(f, convbuf, pconvbuf - convbuf); return count; } /*------------------------------------------------- core_vfprintf - vfprintf to a text file -------------------------------------------------*/ int core_vfprintf(core_file *f, const char *fmt, va_list va) { char buf[1024]; vsnprintf(buf, sizeof(buf), fmt, va); return core_fputs(f, buf); } /*------------------------------------------------- core_fprintf - vfprintf to a text file -------------------------------------------------*/ int CLIB_DECL core_fprintf(core_file *f, const char *fmt, ...) { int rc; va_list va; va_start(va, fmt); rc = core_vfprintf(f, fmt, va); va_end(va); return rc; } /*************************************************************************** FILENAME UTILITIES ***************************************************************************/ /*------------------------------------------------- core_filename_extract_base - extract the base name from a filename; note that this makes assumptions about path separators -------------------------------------------------*/ astring *core_filename_extract_base(astring *result, const char *name, int strip_extension) { /* find the start of the name */ const char *start = name + strlen(name); while (start > name && !is_directory_separator(start[-1])) start--; /* copy the rest into an astring */ astring_cpyc(result, start); /* chop the extension if present */ if (strip_extension) astring_substr(result, 0, astring_rchr(result, 0, '.')); return result; } /*------------------------------------------------- core_filename_ends_with - does the given filename end with the specified extension? -------------------------------------------------*/ int core_filename_ends_with(const char *filename, const char *extension) { int namelen = strlen(filename); int extlen = strlen(extension); int matches = TRUE; /* work backwards checking for a match */ while (extlen > 0) if (tolower(filename[--namelen]) != tolower(extension[--extlen])) { matches = FALSE; break; } return matches; } /*************************************************************************** MISC HELPERS ***************************************************************************/ /*------------------------------------------------- safe_buffer_copy - copy safely from one bounded buffer to another -------------------------------------------------*/ static UINT32 safe_buffer_copy(const void *source, UINT32 sourceoffs, UINT32 sourcelen, void *dest, UINT32 destoffs, UINT32 destlen) { UINT32 sourceavail = sourcelen - sourceoffs; UINT32 destavail = destlen - destoffs; UINT32 bytes_to_copy = MIN(sourceavail, destavail); if (bytes_to_copy > 0) memcpy((UINT8 *)dest + destoffs, (const UINT8 *)source + sourceoffs, bytes_to_copy); return bytes_to_copy; } /*------------------------------------------------- osd_or_zlib_read - wrapper for osd_read that handles zlib-compressed data -------------------------------------------------*/ static file_error osd_or_zlib_read(core_file *file, void *buffer, UINT64 offset, UINT32 length, UINT32 *actual) { /* if no compression, just pass through */ if (file->zdata == NULL) return osd_read(file->file, buffer, offset, length, actual); /* if the offset doesn't match the next offset, fail */ if (offset != file->zdata->nextoffset) return FILERR_INVALID_ACCESS; /* set up the destination */ file->zdata->stream.next_out = (Bytef *)buffer; file->zdata->stream.avail_out = length; while (file->zdata->stream.avail_out != 0) { file_error filerr; UINT32 actualdata; int zerr = Z_OK; /* if we didn't make progress, report an error or the end */ if (file->zdata->stream.avail_in != 0) zerr = inflate(&file->zdata->stream, Z_SYNC_FLUSH); if (zerr != Z_OK) { *actual = length - file->zdata->stream.avail_out; file->zdata->nextoffset += *actual; return (zerr == Z_STREAM_END) ? FILERR_NONE : FILERR_INVALID_DATA; } /* fetch more data if needed */ if (file->zdata->stream.avail_in == 0) { filerr = osd_read(file->file, file->zdata->buffer, file->zdata->realoffset, sizeof(file->zdata->buffer), &actualdata); if (filerr != FILERR_NONE) return filerr; file->zdata->realoffset += actualdata; file->zdata->stream.next_in = file->zdata->buffer; file->zdata->stream.avail_in = sizeof(file->zdata->buffer); } } /* we read everything */ *actual = length; file->zdata->nextoffset += *actual; return FILERR_NONE; } /*------------------------------------------------- osd_or_zlib_write - wrapper for osd_write that handles zlib-compressed data -------------------------------------------------*/ static file_error osd_or_zlib_write(core_file *file, const void *buffer, UINT64 offset, UINT32 length, UINT32 *actual) { /* if no compression, just pass through */ if (file->zdata == NULL) return osd_write(file->file, buffer, offset, length, actual); /* if the offset doesn't match the next offset, fail */ if (offset != file->zdata->nextoffset) return FILERR_INVALID_ACCESS; /* set up the source */ file->zdata->stream.next_in = (Bytef *)buffer; file->zdata->stream.avail_in = length; while (file->zdata->stream.avail_in != 0) { file_error filerr; UINT32 actualdata; int zerr; /* if we didn't make progress, report an error or the end */ zerr = deflate(&file->zdata->stream, Z_NO_FLUSH); if (zerr != Z_OK) { *actual = length - file->zdata->stream.avail_in; file->zdata->nextoffset += *actual; return FILERR_INVALID_DATA; } /* write more data if we are full up */ if (file->zdata->stream.avail_out == 0) { filerr = osd_write(file->file, file->zdata->buffer, file->zdata->realoffset, sizeof(file->zdata->buffer), &actualdata); if (filerr != FILERR_NONE) return filerr; file->zdata->realoffset += actualdata; file->zdata->stream.next_out = file->zdata->buffer; file->zdata->stream.avail_out = sizeof(file->zdata->buffer); } } /* we read everything */ *actual = length; file->zdata->nextoffset += *actual; return FILERR_NONE; }