mirror of
https://github.com/holub/mame
synced 2025-10-06 09:00:04 +03:00
498 lines
8.5 KiB
C++
498 lines
8.5 KiB
C++
// 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
|
|
{
|
|
typedef std::unique_ptr<imgtool_stream> ptr;
|
|
|
|
imgtool_stream(bool wp)
|
|
: imgtype(IMG_FILE)
|
|
, write_protect(wp)
|
|
, name(nullptr)
|
|
, position(0)
|
|
, filesize(0)
|
|
, file()
|
|
, buffer(nullptr)
|
|
{
|
|
}
|
|
|
|
imgtool_stream(
|
|
bool wp,
|
|
util::core_file::ptr &&f)
|
|
: imgtype(IMG_FILE)
|
|
, write_protect(wp)
|
|
, name(nullptr)
|
|
, position(0)
|
|
, filesize(f->size())
|
|
, file(std::move(f))
|
|
, buffer(nullptr)
|
|
{
|
|
}
|
|
|
|
imgtool_stream(bool wp, std::size_t size)
|
|
: imgtype(IMG_MEM)
|
|
, write_protect(wp)
|
|
, name(nullptr)
|
|
, position(0)
|
|
, filesize(size)
|
|
, file()
|
|
, buffer(reinterpret_cast<std::uint8_t *>(malloc(size)))
|
|
{
|
|
}
|
|
|
|
imgtool_stream(bool wp, std::size_t size, void *buf)
|
|
: imgtype(IMG_MEM)
|
|
, write_protect(wp)
|
|
, name(nullptr)
|
|
, position(0)
|
|
, filesize(size)
|
|
, file()
|
|
, buffer(reinterpret_cast<std::uint8_t *>(buf))
|
|
{
|
|
}
|
|
|
|
~imgtool_stream()
|
|
{
|
|
free(buffer);
|
|
}
|
|
|
|
imgtype_t imgtype;
|
|
bool write_protect;
|
|
const char *name; // needed for clear
|
|
std::uint64_t position;
|
|
std::uint64_t filesize;
|
|
|
|
util::core_file::ptr file;
|
|
std::uint8_t *buffer;
|
|
};
|
|
|
|
|
|
|
|
static imgtool_stream *stream_open_zip(const char *zipname, const char *subname, int read_or_write)
|
|
{
|
|
if (read_or_write)
|
|
return nullptr;
|
|
|
|
/* check to see if the file exists */
|
|
FILE *f = fopen(zipname, "r");
|
|
if (!f)
|
|
return nullptr;
|
|
fclose(f);
|
|
|
|
imgtool_stream::ptr imgfile(new imgtool_stream(true));
|
|
|
|
imgfile->imgtype = IMG_MEM;
|
|
|
|
zip_file *z = nullptr;
|
|
const zip_file_header *zipent = nullptr;
|
|
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->buffer = reinterpret_cast<std::uint8_t *>(malloc(zipent->uncompressed_length));
|
|
if (!imgfile->buffer)
|
|
goto error;
|
|
|
|
if (zip_file_decompress(z, imgfile->buffer, zipent->uncompressed_length))
|
|
goto error;
|
|
|
|
zip_file_close(z);
|
|
return imgfile.release();
|
|
|
|
error:
|
|
if (z)
|
|
zip_file_close(z);
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
|
|
imgtool_stream *stream_open(const char *fname, int read_or_write)
|
|
{
|
|
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
|
|
};
|
|
imgtool_stream *s = nullptr;
|
|
char c;
|
|
|
|
/* maybe we are just a ZIP? */
|
|
const char *ext = strrchr(fname, '.');
|
|
if (ext && !core_stricmp(ext, ".zip"))
|
|
return stream_open_zip(fname, nullptr, read_or_write);
|
|
|
|
util::core_file::ptr f = nullptr;
|
|
auto const filerr = util::core_file::open(fname, write_modes[read_or_write], f);
|
|
if (filerr != FILERR_NONE)
|
|
{
|
|
if (!read_or_write)
|
|
{
|
|
int const len = strlen(fname);
|
|
|
|
/* can't open the file; try opening ZIP files with other names */
|
|
std::vector<char> buf(len + 1);
|
|
strcpy(&buf[0], fname);
|
|
|
|
for (int i = len-1; !s && (i >= 0); i--)
|
|
{
|
|
if ((buf[i] == '\\') || (buf[i] == '/'))
|
|
{
|
|
c = buf[i];
|
|
buf[i] = '\0';
|
|
s = stream_open_zip(&buf[0], &buf[i + 1], read_or_write);
|
|
buf[i] = c;
|
|
}
|
|
}
|
|
|
|
if (s)
|
|
return s;
|
|
}
|
|
|
|
/* ah well, it was worth a shot */
|
|
return nullptr;
|
|
}
|
|
|
|
imgtool_stream::ptr imgfile(new imgtool_stream(read_or_write ? false : true, std::move(f)));
|
|
|
|
/* Normal file */
|
|
imgfile->name = fname;
|
|
return imgfile.release();
|
|
}
|
|
|
|
|
|
|
|
imgtool_stream *stream_open_write_stream(int size)
|
|
{
|
|
imgtool_stream::ptr imgfile(new imgtool_stream(false, size));
|
|
if (!imgfile->buffer)
|
|
return nullptr;
|
|
|
|
return imgfile.release();
|
|
}
|
|
|
|
|
|
|
|
imgtool_stream *stream_open_mem(void *buf, size_t sz)
|
|
{
|
|
imgtool_stream::ptr imgfile(new imgtool_stream(false, sz, buf));
|
|
|
|
return imgfile.release();
|
|
}
|
|
|
|
|
|
|
|
void stream_close(imgtool_stream *s)
|
|
{
|
|
assert(s != nullptr);
|
|
|
|
delete s;
|
|
}
|
|
|
|
|
|
|
|
util::core_file *stream_core_file(imgtool_stream *stream)
|
|
{
|
|
return (stream->imgtype == IMG_FILE) ? stream->file.get() : nullptr;
|
|
}
|
|
|
|
|
|
|
|
UINT32 stream_read(imgtool_stream *stream, void *buf, UINT32 sz)
|
|
{
|
|
UINT32 result = 0;
|
|
|
|
switch(stream->imgtype)
|
|
{
|
|
case IMG_FILE:
|
|
assert(sz == (UINT32) sz);
|
|
stream->file->seek(stream->position, SEEK_SET);
|
|
result = stream->file->read(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->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->buffer) free(s->buffer);
|
|
new_buffer = malloc(s->position + sz);
|
|
if (new_buffer)
|
|
{
|
|
s->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->buffer + s->position, buf, sz);
|
|
result = sz;
|
|
}
|
|
break;
|
|
|
|
case IMG_FILE:
|
|
s->file->seek(s->position, SEEK_SET);
|
|
result = s->file->write(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->buffer;
|
|
break;
|
|
|
|
default:
|
|
ptr = nullptr;
|
|
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->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);
|
|
}
|