summaryrefslogtreecommitdiffstatshomepage
path: root/src/tools/imgtool/stream.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/imgtool/stream.c')
-rw-r--r--src/tools/imgtool/stream.c536
1 files changed, 536 insertions, 0 deletions
diff --git a/src/tools/imgtool/stream.c b/src/tools/imgtool/stream.c
new file mode 100644
index 00000000000..5c9cf4050c6
--- /dev/null
+++ b/src/tools/imgtool/stream.c
@@ -0,0 +1,536 @@
+// license:BSD-3-Clause
+// copyright-holders:Nathan Woods
+/***************************************************************************
+
+ stream.c
+
+ Code for implementing Imgtool streams
+
+***************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <zlib.h>
+
+#include "unzip.h"
+#include "osdcore.h"
+#include "imgtool.h"
+
+enum imgtype_t
+{
+ IMG_FILE,
+ IMG_MEM
+};
+
+struct imgtool_stream
+{
+ imgtype_t imgtype;
+ int write_protect;
+ const char *name; // needed for clear
+ UINT64 position;
+ UINT64 filesize;
+
+ union
+ {
+ core_file *file;
+ UINT8 *buffer;
+ } u;
+};
+
+
+
+static imgtool_stream *stream_open_zip(const char *zipname, const char *subname, int read_or_write)
+{
+ imgtool_stream *imgfile = NULL;
+// zip_error ziperr;
+ zip_file *z = NULL;
+ const zip_file_header *zipent;
+ FILE *f;
+
+ if (read_or_write)
+ goto error;
+
+ /* check to see if the file exists */
+ f = fopen(zipname, "r");
+ if (!f)
+ goto error;
+ fclose(f);
+
+ imgfile = (imgtool_stream *)malloc(sizeof(imgtool_stream));
+ if (!imgfile)
+ goto error;
+
+ memset(imgfile, 0, sizeof(*imgfile));
+ imgfile->imgtype = IMG_MEM;
+ imgfile->write_protect = 1;
+ imgfile->position = 0;
+
+// ziperr =
+ zip_file_open(zipname, &z);
+ if (!z)
+ goto error;
+
+ zipent = zip_file_first_file(z);
+ while(zipent && subname && strcmp(subname, zipent->filename))
+ zipent = zip_file_next_file(z);
+ if (!zipent)
+ goto error;
+
+ imgfile->filesize = zipent->uncompressed_length;
+ imgfile->u.buffer = (UINT8*)malloc(zipent->uncompressed_length);
+ if (!imgfile->u.buffer)
+ goto error;
+
+ if (zip_file_decompress(z, imgfile->u.buffer, zipent->uncompressed_length))
+ goto error;
+
+ zip_file_close(z);
+ return imgfile;
+
+error:
+ if (z)
+ zip_file_close(z);
+ if (imgfile)
+ {
+ if (imgfile->u.buffer)
+ free(imgfile->u.buffer);
+ free(imgfile);
+ }
+ return NULL;
+}
+
+
+
+imgtool_stream *stream_open(const char *fname, int read_or_write)
+{
+ file_error filerr;
+ const char *ext;
+ imgtool_stream *imgfile = NULL;
+ static const UINT32 write_modes[] =
+ {
+ OPEN_FLAG_READ,
+ OPEN_FLAG_WRITE | OPEN_FLAG_CREATE,
+ OPEN_FLAG_READ | OPEN_FLAG_WRITE,
+ OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE
+ };
+ core_file *f = NULL;
+ char *buf = NULL;
+ int len, i;
+ imgtool_stream *s = NULL;
+ char c;
+
+ /* maybe we are just a ZIP? */
+ ext = strrchr(fname, '.');
+ if (ext && !core_stricmp(ext, ".zip"))
+ return stream_open_zip(fname, NULL, read_or_write);
+
+ filerr = core_fopen(fname, write_modes[read_or_write], &f);
+ if (filerr != FILERR_NONE)
+ {
+ if (!read_or_write)
+ {
+ len = strlen(fname);
+
+ /* can't open the file; try opening ZIP files with other names */
+ buf = (char*)malloc(len + 1);
+ if (!buf)
+ goto error;
+ strcpy(buf, fname);
+
+ for(i = len-1; !s && (i >= 0); i--)
+ {
+ if ((buf[i] == '\\') || (buf[i] == '/'))
+ {
+ c = buf[i];
+ buf[i] = '\0';
+ s = stream_open_zip(buf, buf + i + 1, read_or_write);
+ buf[i] = c;
+ }
+ }
+ free(buf);
+ buf = NULL;
+
+ if (s)
+ return s;
+ }
+
+ /* ah well, it was worth a shot */
+ goto error;
+ }
+
+ imgfile = (imgtool_stream *)malloc(sizeof(imgtool_stream));
+ if (!imgfile)
+ goto error;
+
+ /* Normal file */
+ memset(imgfile, 0, sizeof(*imgfile));
+ imgfile->imgtype = IMG_FILE;
+ imgfile->position = 0;
+ imgfile->filesize = core_fsize(f);
+ imgfile->write_protect = read_or_write ? 0 : 1;
+ imgfile->u.file = f;
+ imgfile->name = fname;
+ return imgfile;
+
+error:
+ if (imgfile != NULL)
+ free((void *) imgfile);
+ if (f != NULL)
+ core_fclose(f);
+ if (buf)
+ free(buf);
+ return (imgtool_stream *) NULL;
+}
+
+
+
+imgtool_stream *stream_open_write_stream(int size)
+{
+ imgtool_stream *imgfile;
+
+ imgfile = (imgtool_stream *)malloc(sizeof(imgtool_stream));
+ if (!imgfile)
+ return NULL;
+
+ imgfile->imgtype = IMG_MEM;
+ imgfile->write_protect = 0;
+ imgfile->position = 0;
+ imgfile->filesize = size;
+ imgfile->u.buffer = (UINT8*)malloc(size);
+
+ if (!imgfile->u.buffer)
+ {
+ free(imgfile);
+ return NULL;
+ }
+
+ return imgfile;
+}
+
+
+
+imgtool_stream *stream_open_mem(void *buf, size_t sz)
+{
+ imgtool_stream *imgfile;
+
+ imgfile = (imgtool_stream *)malloc(sizeof(imgtool_stream));
+ if (!imgfile)
+ return NULL;
+
+ memset(imgfile, 0, sizeof(*imgfile));
+ imgfile->imgtype = IMG_MEM;
+ imgfile->position = 0;
+ imgfile->write_protect = 0;
+
+ imgfile->filesize = sz;
+ imgfile->u.buffer = (UINT8*)buf;
+ return imgfile;
+}
+
+
+
+void stream_close(imgtool_stream *s)
+{
+ assert(s != NULL);
+
+ switch(s->imgtype)
+ {
+ case IMG_FILE:
+ if (s->u.file != NULL)
+ {
+ core_fclose(s->u.file);
+ s->u.file = NULL;
+ }
+ break;
+
+ case IMG_MEM:
+ if (s->u.buffer != NULL)
+ {
+ free(s->u.buffer);
+ s->u.buffer = NULL;
+ }
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+ free((void *) s);
+}
+
+
+
+core_file *stream_core_file(imgtool_stream *stream)
+{
+ return (stream->imgtype == IMG_FILE) ? stream->u.file : NULL;
+}
+
+
+
+UINT32 stream_read(imgtool_stream *stream, void *buf, UINT32 sz)
+{
+ UINT32 result = 0;
+
+ switch(stream->imgtype)
+ {
+ case IMG_FILE:
+ assert(sz == (UINT32) sz);
+ core_fseek(stream->u.file, stream->position, SEEK_SET);
+ result = core_fread(stream->u.file, buf, (UINT32) sz);
+ break;
+
+ case IMG_MEM:
+ /* do we have to limit sz? */
+ if (sz > (stream->filesize - stream->position))
+ sz = (UINT32) (stream->filesize - stream->position);
+
+ memcpy(buf, stream->u.buffer + stream->position, sz);
+ result = sz;
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+ stream->position += result;
+ return result;
+}
+
+
+
+UINT32 stream_write(imgtool_stream *s, const void *buf, UINT32 sz)
+{
+ void *new_buffer;
+ UINT32 result = 0;
+
+ switch(s->imgtype)
+ {
+ case IMG_MEM:
+ if (!s->write_protect)
+ {
+ /* do we have to expand the buffer? */
+ if (s->filesize < s->position + sz)
+ {
+ /* try to expand the buffer */
+ if (s->u.buffer) free(s->u.buffer);
+ new_buffer = malloc(s->position + sz);
+ if (new_buffer)
+ {
+ s->u.buffer = (UINT8*)new_buffer;
+ s->filesize = s->position + sz;
+ }
+ }
+
+ /* do we have to limit sz? */
+ if (sz > (s->filesize - s->position))
+ sz = (UINT32) (s->filesize - s->position);
+
+ memcpy(s->u.buffer + s->position, buf, sz);
+ result = sz;
+ }
+ break;
+
+ case IMG_FILE:
+ core_fseek(s->u.file, s->position, SEEK_SET);
+ result = core_fwrite(s->u.file, buf, sz);
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+
+ /* advance the file pointer */
+ s->position += result;
+
+ /* did we grow the file */
+ if (s->position > s->filesize)
+ s->filesize = s->position;
+ return result;
+}
+
+
+
+UINT64 stream_size(imgtool_stream *s)
+{
+ return s->filesize;
+}
+
+
+
+void *stream_getptr(imgtool_stream *f)
+{
+ void *ptr;
+
+ switch(f->imgtype)
+ {
+ case IMG_MEM:
+ ptr = f->u.buffer;
+ break;
+
+ default:
+ ptr = NULL;
+ break;
+ }
+ return ptr;
+}
+
+
+
+int stream_seek(imgtool_stream *s, INT64 pos, int where)
+{
+ UINT64 size;
+
+ size = stream_size(s);
+
+ switch(where)
+ {
+ case SEEK_CUR:
+ pos += s->position;
+ break;
+ case SEEK_END:
+ pos += size;
+ break;
+ }
+
+ if (pos < 0)
+ s->position = 0;
+ else
+ s->position = MIN(size, pos);
+
+ if (s->position < pos)
+ stream_fill(s, '\0', pos - s->position);
+
+ return 0;
+}
+
+
+
+UINT64 stream_tell(imgtool_stream *s)
+{
+ return s->position;
+}
+
+
+
+UINT64 stream_transfer(imgtool_stream *dest, imgtool_stream *source, UINT64 sz)
+{
+ UINT64 result = 0;
+ UINT64 readsz;
+ char buf[1024];
+
+ while(sz && (readsz = stream_read(source, buf, MIN(sz, sizeof(buf)))))
+ {
+ stream_write(dest, buf, readsz);
+ sz -= readsz;
+ result += readsz;
+ }
+ return result;
+}
+
+
+
+UINT64 stream_transfer_all(imgtool_stream *dest, imgtool_stream *source)
+{
+ return stream_transfer(dest, source, stream_size(source));
+}
+
+
+
+int stream_crc(imgtool_stream *s, unsigned long *result)
+{
+ size_t sz;
+ void *ptr;
+
+ switch(s->imgtype)
+ {
+ case IMG_MEM:
+ *result = crc32(0, (unsigned char *) s->u.buffer, (size_t) s->filesize);
+ break;
+
+ default:
+ sz = stream_size(s);
+ ptr = malloc(sz);
+ if (!ptr)
+ return IMGTOOLERR_OUTOFMEMORY;
+ stream_seek(s, 0, SEEK_SET);
+ if (stream_read(s, ptr, sz) != sz)
+ {
+ free(ptr);
+ return IMGTOOLERR_READERROR;
+ }
+ *result = crc32(0, (const Bytef*)ptr, sz);
+ free(ptr);
+ break;
+ }
+ return 0;
+}
+
+
+
+int file_crc(const char *fname, unsigned long *result)
+{
+ int err;
+ imgtool_stream *f;
+
+ f = stream_open(fname, OSD_FOPEN_READ);
+ if (!f)
+ return IMGTOOLERR_FILENOTFOUND;
+
+ err = stream_crc(f, result);
+ stream_close(f);
+ return err;
+}
+
+
+
+UINT64 stream_fill(imgtool_stream *f, unsigned char b, UINT64 sz)
+{
+ UINT64 outsz;
+ char buf[1024];
+
+ outsz = 0;
+ memset(buf, b, MIN(sz, sizeof(buf)));
+
+ while(sz)
+ {
+ outsz += stream_write(f, buf, MIN(sz, sizeof(buf)));
+ sz -= MIN(sz, sizeof(buf));
+ }
+ return outsz;
+}
+
+
+
+int stream_isreadonly(imgtool_stream *s)
+{
+ return s->write_protect;
+}
+
+
+
+UINT32 stream_putc(imgtool_stream *stream, char c)
+{
+ return stream_write(stream, &c, 1);
+}
+
+
+
+UINT32 stream_puts(imgtool_stream *stream, const char *s)
+{
+ return stream_write(stream, s, strlen(s));
+}
+
+
+
+UINT32 stream_printf(imgtool_stream *stream, const char *fmt, ...)
+{
+ va_list va;
+ char buf[256];
+
+ va_start(va, fmt);
+ vsprintf(buf, fmt, va);
+ va_end(va);
+
+ return stream_puts(stream, buf);
+}