mirror of
https://github.com/holub/mame
synced 2025-07-05 09:57:47 +03:00
util: Further API cleanups: (#8661)
* Turned `core_file` into an implementation of `random_read_write`. * Turned PNG errors into a standard error category. * Added a helper for generating what look like derived classes on-the-fly.
This commit is contained in:
parent
33723892a3
commit
aeb9eae874
@ -26,9 +26,7 @@ project "utils"
|
||||
}
|
||||
|
||||
files {
|
||||
MAME_DIR .. "src/lib/util/bitstream.h",
|
||||
MAME_DIR .. "src/lib/util/coretmpl.h",
|
||||
MAME_DIR .. "src/lib/util/lrucache.h",
|
||||
MAME_DIR .. "src/lib/util/abi.h",
|
||||
MAME_DIR .. "src/lib/util/avhuff.cpp",
|
||||
MAME_DIR .. "src/lib/util/avhuff.h",
|
||||
MAME_DIR .. "src/lib/util/aviio.cpp",
|
||||
@ -36,6 +34,7 @@ project "utils"
|
||||
MAME_DIR .. "src/lib/util/base64.hpp",
|
||||
MAME_DIR .. "src/lib/util/bitmap.cpp",
|
||||
MAME_DIR .. "src/lib/util/bitmap.h",
|
||||
MAME_DIR .. "src/lib/util/bitstream.h",
|
||||
MAME_DIR .. "src/lib/util/cdrom.cpp",
|
||||
MAME_DIR .. "src/lib/util/cdrom.h",
|
||||
MAME_DIR .. "src/lib/util/chd.cpp",
|
||||
@ -54,6 +53,7 @@ project "utils"
|
||||
MAME_DIR .. "src/lib/util/corefile.h",
|
||||
MAME_DIR .. "src/lib/util/corestr.cpp",
|
||||
MAME_DIR .. "src/lib/util/corestr.h",
|
||||
MAME_DIR .. "src/lib/util/coretmpl.h",
|
||||
MAME_DIR .. "src/lib/util/coreutil.cpp",
|
||||
MAME_DIR .. "src/lib/util/coreutil.h",
|
||||
MAME_DIR .. "src/lib/util/crypto.hpp",
|
||||
@ -61,6 +61,9 @@ project "utils"
|
||||
MAME_DIR .. "src/lib/util/delegate.h",
|
||||
MAME_DIR .. "src/lib/util/disasmintf.cpp",
|
||||
MAME_DIR .. "src/lib/util/disasmintf.h",
|
||||
MAME_DIR .. "src/lib/util/dynamicclass.cpp",
|
||||
MAME_DIR .. "src/lib/util/dynamicclass.h",
|
||||
MAME_DIR .. "src/lib/util/dynamicclass.ipp",
|
||||
MAME_DIR .. "src/lib/util/endianness.h",
|
||||
MAME_DIR .. "src/lib/util/flac.cpp",
|
||||
MAME_DIR .. "src/lib/util/flac.h",
|
||||
@ -80,6 +83,7 @@ project "utils"
|
||||
MAME_DIR .. "src/lib/util/ioprocsvec.h",
|
||||
MAME_DIR .. "src/lib/util/jedparse.cpp",
|
||||
MAME_DIR .. "src/lib/util/jedparse.h",
|
||||
MAME_DIR .. "src/lib/util/lrucache.h",
|
||||
MAME_DIR .. "src/lib/util/md5.cpp",
|
||||
MAME_DIR .. "src/lib/util/md5.h",
|
||||
MAME_DIR .. "src/lib/util/msdib.cpp",
|
||||
|
@ -508,16 +508,18 @@ std::string a78_cart_slot_device::get_default_card_software(get_default_card_sof
|
||||
{
|
||||
if (hook.image_file())
|
||||
{
|
||||
const char *slot_string;
|
||||
std::vector<uint8_t> head(128);
|
||||
int type = A78_TYPE0, mapper;
|
||||
uint64_t len;
|
||||
hook.image_file()->length(len); // FIXME: check error return
|
||||
|
||||
// Load and check the header
|
||||
hook.image_file()->read(&head[0], 128);
|
||||
uint8_t head[128];
|
||||
std::size_t actual;
|
||||
hook.image_file()->read(&head[0], 128, actual); // FIXME: check error return or read returning short
|
||||
|
||||
// let's try to auto-fix some common errors in the header
|
||||
mapper = validate_header((head[53] << 8) | head[54], false);
|
||||
int const mapper = validate_header((head[53] << 8) | head[54], false);
|
||||
|
||||
int type = A78_TYPE0;
|
||||
switch (mapper & 0x2e)
|
||||
{
|
||||
case 0x0000:
|
||||
@ -534,7 +536,7 @@ std::string a78_cart_slot_device::get_default_card_software(get_default_card_sof
|
||||
break;
|
||||
case 0x0022:
|
||||
case 0x0026:
|
||||
if (hook.image_file()->size() > 0x40000)
|
||||
if (len > 0x40000)
|
||||
type = A78_MEGACART;
|
||||
else
|
||||
type = A78_VERSABOARD;
|
||||
@ -560,7 +562,7 @@ std::string a78_cart_slot_device::get_default_card_software(get_default_card_sof
|
||||
type = A78_TYPE8;
|
||||
|
||||
logerror("Cart type: %x\n", type);
|
||||
slot_string = a78_get_slot(type);
|
||||
char const *const slot_string = a78_get_slot(type);
|
||||
|
||||
return std::string(slot_string);
|
||||
}
|
||||
|
@ -392,15 +392,16 @@ std::string a800_cart_slot_device::get_default_card_software(get_default_card_so
|
||||
{
|
||||
if (hook.image_file())
|
||||
{
|
||||
const char *slot_string;
|
||||
std::vector<uint8_t> head(0x10);
|
||||
uint32_t len = hook.image_file()->size();
|
||||
int type = A800_8K;
|
||||
uint64_t len;
|
||||
hook.image_file()->length(len); // FIXME: check error return
|
||||
|
||||
// check whether there is an header, to identify the cart type
|
||||
int type = A800_8K;
|
||||
if ((len % 0x1000) == 0x10)
|
||||
{
|
||||
hook.image_file()->read(&head[0], 0x10);
|
||||
size_t actual;
|
||||
uint8_t head[0x10];
|
||||
hook.image_file()->read(&head[0], 0x10, actual); // FIXME: check error return or read returning short
|
||||
type = identify_cart_type(&head[0]);
|
||||
}
|
||||
else // otherwise try to guess based on size
|
||||
@ -414,12 +415,12 @@ std::string a800_cart_slot_device::get_default_card_software(get_default_card_so
|
||||
if (type >= A5200_4K)
|
||||
osd_printf_info("This game is not designed for A800. You might want to run it in A5200.\n");
|
||||
|
||||
slot_string = a800_get_slot(type);
|
||||
char const *const slot_string = a800_get_slot(type);
|
||||
|
||||
return std::string(slot_string);
|
||||
}
|
||||
else
|
||||
return software_get_default_slot("a800_8k");
|
||||
|
||||
return software_get_default_slot("a800_8k");
|
||||
}
|
||||
|
||||
|
||||
@ -427,15 +428,16 @@ std::string a5200_cart_slot_device::get_default_card_software(get_default_card_s
|
||||
{
|
||||
if (hook.image_file())
|
||||
{
|
||||
const char *slot_string;
|
||||
std::vector<uint8_t> head(0x10);
|
||||
uint32_t len = hook.image_file()->size();
|
||||
int type = A5200_8K;
|
||||
uint64_t len;
|
||||
hook.image_file()->length(len); // FIXME: check error return
|
||||
|
||||
// check whether there is an header, to identify the cart type
|
||||
int type = A5200_8K;
|
||||
if ((len % 0x1000) == 0x10)
|
||||
{
|
||||
hook.image_file()->read(&head[0], 0x10);
|
||||
size_t actual;
|
||||
uint8_t head[0x10];
|
||||
hook.image_file()->read(&head[0], 0x10, actual); // FIXME: check error return or read returning short
|
||||
type = identify_cart_type(&head[0]);
|
||||
}
|
||||
else
|
||||
@ -447,12 +449,12 @@ std::string a5200_cart_slot_device::get_default_card_software(get_default_card_s
|
||||
if (type < A5200_4K)
|
||||
osd_printf_info("This game is not designed for A5200. You might want to run it in A800 or A800XL.\n");
|
||||
|
||||
slot_string = a800_get_slot(type);
|
||||
char const *const slot_string = a800_get_slot(type);
|
||||
|
||||
return std::string(slot_string);
|
||||
}
|
||||
else
|
||||
return software_get_default_slot("a5200");
|
||||
|
||||
return software_get_default_slot("a5200");
|
||||
}
|
||||
|
||||
|
||||
@ -460,15 +462,16 @@ std::string xegs_cart_slot_device::get_default_card_software(get_default_card_so
|
||||
{
|
||||
if (hook.image_file())
|
||||
{
|
||||
const char *slot_string;
|
||||
std::vector<uint8_t> head(0x10);
|
||||
uint32_t len = hook.image_file()->size();
|
||||
int type = A800_8K;
|
||||
uint64_t len;
|
||||
hook.image_file()->length(len); // FIXME: check error return
|
||||
|
||||
// check whether there is an header, to identify the cart type
|
||||
int type = A800_8K;
|
||||
if ((len % 0x1000) == 0x10)
|
||||
{
|
||||
hook.image_file()->read(&head[0], 0x10);
|
||||
size_t actual;
|
||||
uint8_t head[0x10];
|
||||
hook.image_file()->read(&head[0], 0x10, actual); // FIXME: check error return or read returning short
|
||||
type = identify_cart_type(&head[0]);
|
||||
}
|
||||
if (type != A800_XEGS)
|
||||
@ -480,12 +483,12 @@ std::string xegs_cart_slot_device::get_default_card_software(get_default_card_so
|
||||
osd_printf_info("You might want to run it in A800 or A800XL.\n");
|
||||
}
|
||||
|
||||
slot_string = a800_get_slot(type);
|
||||
char const *const slot_string = a800_get_slot(type);
|
||||
|
||||
return std::string(slot_string);
|
||||
}
|
||||
else
|
||||
return software_get_default_slot("xegs");
|
||||
|
||||
return software_get_default_slot("xegs");
|
||||
}
|
||||
|
||||
|
||||
|
@ -202,17 +202,17 @@ std::string apf_cart_slot_device::get_default_card_software(get_default_card_sof
|
||||
{
|
||||
if (hook.image_file())
|
||||
{
|
||||
const char *slot_string;
|
||||
uint32_t size = hook.image_file()->size();
|
||||
int type = APF_STD;
|
||||
uint64_t size;
|
||||
hook.image_file()->length(size); // FIXME: check error return
|
||||
|
||||
// attempt to identify Space Destroyer, which needs 1K of additional RAM
|
||||
int type = APF_STD;
|
||||
if (size == 0x1800)
|
||||
type = APF_SPACEDST;
|
||||
if (size > 0x2000)
|
||||
type = APF_BASIC;
|
||||
|
||||
slot_string = apf_get_slot(type);
|
||||
char const *const slot_string = apf_get_slot(type);
|
||||
|
||||
//printf("type: %s\n", slot_string);
|
||||
|
||||
|
@ -117,15 +117,17 @@ std::string aquarius_cartridge_slot_device::get_default_card_software(get_defaul
|
||||
|
||||
if (hook.image_file())
|
||||
{
|
||||
const char *slot_string = "rom";
|
||||
uint32_t len = hook.image_file()->size();
|
||||
uint64_t len;
|
||||
hook.image_file()->length(len); // FIXME: check error return
|
||||
|
||||
const char *slot_string = "rom";
|
||||
if (len >= 0x10000)
|
||||
{
|
||||
std::vector<uint8_t> header(16);
|
||||
uint8_t header[16];
|
||||
|
||||
hook.image_file()->seek(len - 0x2000, SEEK_SET);
|
||||
hook.image_file()->read(&header[0], 16);
|
||||
size_t actual;
|
||||
hook.image_file()->seek(len - 0x2000, SEEK_SET); // FIXME: check error return
|
||||
hook.image_file()->read(&header[0], 16, actual); // FIXME: check error return or read returning short
|
||||
|
||||
// detect SuperCart header
|
||||
if (!memcmp(&header[0], SC08_HEADER, 16) || !memcmp(&header[0], SC16_HEADER, 16))
|
||||
|
@ -179,16 +179,16 @@ std::string astrocade_cart_slot_device::get_default_card_software(get_default_ca
|
||||
{
|
||||
if (hook.image_file())
|
||||
{
|
||||
const char *slot_string;
|
||||
uint32_t size = hook.image_file()->size();
|
||||
int type = ASTROCADE_STD;
|
||||
uint64_t size;
|
||||
hook.image_file()->length(size); // FIXME: check error return
|
||||
|
||||
int type = ASTROCADE_STD;
|
||||
if (size == 0x40000)
|
||||
type = ASTROCADE_256K;
|
||||
if (size == 0x80000)
|
||||
type = ASTROCADE_512K;
|
||||
|
||||
slot_string = astrocade_get_slot(type);
|
||||
char const *const slot_string = astrocade_get_slot(type);
|
||||
|
||||
//printf("type: %s\n", slot_string);
|
||||
|
||||
|
@ -196,16 +196,16 @@ std::string channelf_cart_slot_device::get_default_card_software(get_default_car
|
||||
{
|
||||
if (hook.image_file())
|
||||
{
|
||||
const char *slot_string;
|
||||
uint32_t len = hook.image_file()->size();
|
||||
int type;
|
||||
uint64_t len;
|
||||
hook.image_file()->length(len); // FIXME: check error return
|
||||
|
||||
int type;
|
||||
if (len == 0x40000)
|
||||
type = CF_MULTI;
|
||||
else
|
||||
type = CF_CHESS; // is there any way to detect the other carts from fullpath?
|
||||
|
||||
slot_string = chanf_get_slot(type);
|
||||
char const *const slot_string = chanf_get_slot(type);
|
||||
|
||||
//printf("type: %s\n", slot_string);
|
||||
|
||||
|
@ -106,16 +106,16 @@ std::string colecovision_cartridge_slot_device::get_default_card_software(get_de
|
||||
{
|
||||
if (hook.image_file())
|
||||
{
|
||||
uint32_t length = hook.image_file()->size();
|
||||
uint64_t length;
|
||||
hook.image_file()->length(length); // FIXME: check error return
|
||||
|
||||
if (length == 0x100000 || length == 0x200000)
|
||||
return software_get_default_slot("xin1");
|
||||
|
||||
if (length > 0x8000)
|
||||
{
|
||||
// Assume roms longer than 32K are megacarts.
|
||||
if (length > 0x8000) // Assume roms longer than 32K are megacarts.
|
||||
return software_get_default_slot("megacart");
|
||||
}
|
||||
}
|
||||
|
||||
return software_get_default_slot("standard");
|
||||
}
|
||||
|
||||
|
@ -209,10 +209,10 @@ std::string crvision_cart_slot_device::get_default_card_software(get_default_car
|
||||
{
|
||||
if (hook.image_file())
|
||||
{
|
||||
const char *slot_string;
|
||||
uint32_t size = hook.image_file()->size();
|
||||
int type = CRV_4K;
|
||||
uint64_t size;
|
||||
hook.image_file()->length(size); // FIXME: check error return
|
||||
|
||||
int type = CRV_4K;
|
||||
switch (size)
|
||||
{
|
||||
case 0x4800:
|
||||
@ -238,7 +238,7 @@ std::string crvision_cart_slot_device::get_default_card_software(get_default_car
|
||||
break;
|
||||
}
|
||||
|
||||
slot_string = crvision_get_slot(type);
|
||||
char const *const slot_string = crvision_get_slot(type);
|
||||
|
||||
//printf("type: %s\n", slot_string);
|
||||
|
||||
|
@ -184,17 +184,17 @@ std::string ekara_cart_slot_device::get_default_card_software(get_default_card_s
|
||||
{
|
||||
if (hook.image_file())
|
||||
{
|
||||
const char *slot_string;
|
||||
uint32_t len = hook.image_file()->size();
|
||||
uint64_t len;
|
||||
hook.image_file()->length(len); // FIXME: check error return, guard against excessively large files
|
||||
std::vector<uint8_t> rom(len);
|
||||
int type;
|
||||
|
||||
hook.image_file()->read(&rom[0], len);
|
||||
size_t actual;
|
||||
hook.image_file()->read(&rom[0], len, actual); // FIXME: check error return or read returning short
|
||||
|
||||
type = get_cart_type(&rom[0], len);
|
||||
slot_string = ekara_get_slot(type);
|
||||
int const type = get_cart_type(&rom[0], len);
|
||||
char const *const slot_string = ekara_get_slot(type);
|
||||
|
||||
printf("type: %s\n", slot_string);
|
||||
//printf("type: %s\n", slot_string);
|
||||
|
||||
return std::string(slot_string);
|
||||
}
|
||||
|
@ -203,15 +203,15 @@ std::string gamate_cart_slot_device::get_default_card_software(get_default_card_
|
||||
{
|
||||
if (hook.image_file())
|
||||
{
|
||||
const char *slot_string;
|
||||
uint32_t len = hook.image_file()->size();
|
||||
uint64_t len;
|
||||
hook.image_file()->length(len); // FIXME: check error return, guard against excessively large files
|
||||
std::vector<uint8_t> rom(len);
|
||||
int type;
|
||||
|
||||
hook.image_file()->read(&rom[0], len);
|
||||
size_t actual;
|
||||
hook.image_file()->read(&rom[0], len, actual); // FIXME: check error return or read returning short
|
||||
|
||||
type = get_cart_type(&rom[0], len);
|
||||
slot_string = gamate_get_slot(type);
|
||||
int const type = get_cart_type(&rom[0], len);
|
||||
char const *const slot_string = gamate_get_slot(type);
|
||||
|
||||
//printf("type: %s\n", slot_string);
|
||||
|
||||
|
@ -584,21 +584,21 @@ std::string gb_cart_slot_device_base::get_default_card_software(get_default_card
|
||||
{
|
||||
if (hook.image_file())
|
||||
{
|
||||
const char *slot_string;
|
||||
uint32_t len = hook.image_file()->size(), offset = 0;
|
||||
uint64_t len;
|
||||
hook.image_file()->length(len); // FIXME: check error return, guard against excessively large files
|
||||
std::vector<uint8_t> rom(len);
|
||||
int type;
|
||||
|
||||
hook.image_file()->read(&rom[0], len);
|
||||
size_t actual;
|
||||
hook.image_file()->read(&rom[0], len, actual); // FIXME: check error return or read returning short
|
||||
|
||||
uint32_t offset = 0;
|
||||
if ((len % 0x4000) == 512)
|
||||
offset = 512;
|
||||
|
||||
if (get_mmm01_candidate(&rom[offset], len - offset))
|
||||
offset += (len - 0x8000);
|
||||
|
||||
type = get_cart_type(&rom[offset], len - offset);
|
||||
slot_string = gb_get_slot(type);
|
||||
int const type = get_cart_type(&rom[offset], len - offset);
|
||||
char const *const slot_string = gb_get_slot(type);
|
||||
|
||||
//printf("type: %s\n", slot_string);
|
||||
|
||||
|
@ -883,15 +883,15 @@ std::string gba_cart_slot_device::get_default_card_software(get_default_card_sof
|
||||
{
|
||||
if (hook.image_file())
|
||||
{
|
||||
const char *slot_string;
|
||||
uint32_t len = hook.image_file()->size();
|
||||
uint64_t len;
|
||||
hook.image_file()->length(len); // FIXME: check error return, guard against excessively large files
|
||||
std::vector<uint8_t> rom(len);
|
||||
int type;
|
||||
|
||||
hook.image_file()->read(&rom[0], len);
|
||||
size_t actual;
|
||||
hook.image_file()->read(&rom[0], len, actual); // FIXME: check error return or read returning short
|
||||
|
||||
type = get_cart_type(&rom[0], len);
|
||||
slot_string = gba_get_slot(type);
|
||||
int const type = get_cart_type(&rom[0], len);
|
||||
char const *const slot_string = gba_get_slot(type);
|
||||
|
||||
//printf("type: %s\n", slot_string);
|
||||
|
||||
|
@ -440,13 +440,14 @@ std::string intv_cart_slot_device::get_default_card_software(get_default_card_so
|
||||
{
|
||||
if (hook.image_file())
|
||||
{
|
||||
const char *slot_string;
|
||||
uint32_t len = hook.image_file()->size();
|
||||
uint64_t len;
|
||||
hook.image_file()->length(len); // FIXME: check error return, guard against excessively large files
|
||||
std::vector<uint8_t> rom(len);
|
||||
|
||||
size_t actual;
|
||||
hook.image_file()->read(&rom[0], len, actual); // FIXME: check error return or read returning short
|
||||
|
||||
int type = INTV_STD;
|
||||
|
||||
hook.image_file()->read(&rom[0], len);
|
||||
|
||||
if (rom[0] == 0xa8 && (rom[1] == (rom[2] ^ 0xff)))
|
||||
{
|
||||
// it's .ROM file, so that we don't have currently any way to distinguish RAM-equipped carts
|
||||
@ -454,18 +455,17 @@ std::string intv_cart_slot_device::get_default_card_software(get_default_card_so
|
||||
else
|
||||
{
|
||||
// assume it's .BIN and try to use .hsi file to determine type (just RAM)
|
||||
int start;
|
||||
int mapper, rom[5], ram, extra;
|
||||
std::string extrainfo;
|
||||
|
||||
if (hook.hashfile_extrainfo(extrainfo))
|
||||
{
|
||||
int mapper, rom[5], ram, extra;
|
||||
sscanf(extrainfo.c_str() ,"%d %d %d %d %d %d %d", &mapper, &rom[0], &rom[1], &rom[2],
|
||||
&rom[3], &ram, &extra);
|
||||
|
||||
if (ram)
|
||||
{
|
||||
start = ((ram & 0xf0) >> 4) * 0x1000;
|
||||
int const start = ((ram & 0xf0) >> 4) * 0x1000;
|
||||
if (start == 0xd000)
|
||||
type = INTV_RAM;
|
||||
if (start == 0x8800)
|
||||
@ -475,7 +475,7 @@ std::string intv_cart_slot_device::get_default_card_software(get_default_card_so
|
||||
|
||||
}
|
||||
|
||||
slot_string = intv_get_slot(type);
|
||||
char const *const slot_string = intv_get_slot(type);
|
||||
|
||||
//printf("type: %s\n", slot_string);
|
||||
|
||||
|
@ -1458,9 +1458,9 @@ void omti_disk_image_device::device_reset()
|
||||
{
|
||||
logerror("device_reset_omti_disk\n");
|
||||
|
||||
if (exists() && fseek(0, SEEK_END) == 0)
|
||||
if (exists() && !fseek(0, SEEK_END))
|
||||
{
|
||||
uint32_t disk_size = (uint32_t)(ftell() / OMTI_DISK_SECTOR_SIZE);
|
||||
uint32_t disk_size = uint32_t(ftell() / OMTI_DISK_SECTOR_SIZE);
|
||||
uint16_t disk_type = disk_size >= 300000 ? OMTI_DISK_TYPE_348_MB : OMTI_DISK_TYPE_155_MB;
|
||||
if (disk_type != m_type) {
|
||||
logerror("device_reset_omti_disk: disk size=%d blocks, disk type=%x\n", disk_size, disk_type);
|
||||
@ -1477,12 +1477,10 @@ image_init_result omti_disk_image_device::call_create(int format_type, util::opt
|
||||
{
|
||||
logerror("device_create_omti_disk: creating OMTI Disk with %d blocks\n", m_sector_count);
|
||||
|
||||
int x;
|
||||
unsigned char sectordata[OMTI_DISK_SECTOR_SIZE]; // empty block data
|
||||
|
||||
|
||||
memset(sectordata, 0x55, sizeof(sectordata));
|
||||
for (x = 0; x < m_sector_count; x++)
|
||||
|
||||
for (int x = 0; x < m_sector_count; x++)
|
||||
{
|
||||
if (fwrite(sectordata, OMTI_DISK_SECTOR_SIZE)
|
||||
< OMTI_DISK_SECTOR_SIZE)
|
||||
@ -1490,5 +1488,6 @@ image_init_result omti_disk_image_device::call_create(int format_type, util::opt
|
||||
return image_init_result::FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
return image_init_result::PASS;
|
||||
}
|
||||
|
@ -182,17 +182,17 @@ std::string jakks_gamekey_slot_device::get_default_card_software(get_default_car
|
||||
{
|
||||
if (hook.image_file())
|
||||
{
|
||||
const char *slot_string;
|
||||
uint32_t len = hook.image_file()->size();
|
||||
uint64_t len;
|
||||
hook.image_file()->length(len); // FIXME: check error return, guard against excessively large files
|
||||
std::vector<uint8_t> rom(len);
|
||||
int type;
|
||||
|
||||
hook.image_file()->read(&rom[0], len);
|
||||
size_t actual;
|
||||
hook.image_file()->read(&rom[0], len, actual); // FIXME: check error return or read returning short
|
||||
|
||||
type = get_cart_type(&rom[0], len);
|
||||
slot_string = jakks_gamekey_get_slot(type);
|
||||
int const type = get_cart_type(&rom[0], len);
|
||||
char const *const slot_string = jakks_gamekey_get_slot(type);
|
||||
|
||||
printf("type: %s\n", slot_string);
|
||||
//printf("type: %s\n", slot_string);
|
||||
|
||||
return std::string(slot_string);
|
||||
}
|
||||
|
@ -906,18 +906,17 @@ std::string base_md_cart_slot_device::get_default_card_software(get_default_card
|
||||
{
|
||||
if (hook.image_file())
|
||||
{
|
||||
const char *slot_string;
|
||||
uint32_t len = hook.image_file()->size(), offset = 0;
|
||||
uint64_t len;
|
||||
hook.image_file()->length(len); // FIXME: check error return, guard against excessively large files
|
||||
std::vector<uint8_t> rom(len);
|
||||
int type;
|
||||
|
||||
hook.image_file()->read(&rom[0], len);
|
||||
size_t actual;
|
||||
hook.image_file()->read(&rom[0], len, actual); // FIXME: check error return or read returning short
|
||||
|
||||
if (genesis_is_SMD(&rom[0x200], len - 0x200))
|
||||
offset = 0x200;
|
||||
uint32_t const offset = genesis_is_SMD(&rom[0x200], len - 0x200) ? 0x200 : 0;
|
||||
|
||||
type = get_cart_type(&rom[offset], len - offset);
|
||||
slot_string = md_get_slot(type);
|
||||
int const type = get_cart_type(&rom[offset], len - offset);
|
||||
char const *const slot_string = md_get_slot(type);
|
||||
|
||||
return std::string(slot_string);
|
||||
}
|
||||
|
@ -278,8 +278,6 @@ std::string msx_slot_cartridge_device::get_default_card_software(get_default_car
|
||||
if (hook.image_file())
|
||||
{
|
||||
const char *slot_string = "nomapper";
|
||||
uint32_t length = hook.image_file()->size();
|
||||
std::vector<uint8_t> rom(length);
|
||||
int type = NOMAPPER;
|
||||
|
||||
// Check if there's some mapper related information in the hashfiles
|
||||
@ -323,7 +321,11 @@ std::string msx_slot_cartridge_device::get_default_card_software(get_default_car
|
||||
if (type == NOMAPPER)
|
||||
{
|
||||
// Not identified through hashfile, try automatic detection
|
||||
hook.image_file()->read(&rom[0], length);
|
||||
uint64_t length;
|
||||
hook.image_file()->length(length); // FIXME: check error return, guard against excessively large files
|
||||
std::vector<uint8_t> rom(length);
|
||||
size_t actual;
|
||||
hook.image_file()->read(&rom[0], length, actual); // FIXME: check error return or read returning short
|
||||
type = get_cart_type(&rom[0], length);
|
||||
}
|
||||
|
||||
|
@ -141,16 +141,16 @@ std::string nes_aladdin_slot_device::get_default_card_software(get_default_card_
|
||||
{
|
||||
if (hook.image_file())
|
||||
{
|
||||
const char *slot_string = "algn";
|
||||
uint32_t len = hook.image_file()->size();
|
||||
uint64_t len;
|
||||
hook.image_file()->length(len); // FIXME: check error return, guard against excessively large files
|
||||
std::vector<uint8_t> rom(len);
|
||||
uint8_t mapper;
|
||||
|
||||
hook.image_file()->read(&rom[0], len);
|
||||
size_t actual;
|
||||
hook.image_file()->read(&rom[0], len, actual); // FIXME: check error return or read returning short
|
||||
|
||||
mapper = (rom[6] & 0xf0) >> 4;
|
||||
mapper |= rom[7] & 0xf0;
|
||||
uint8_t const mapper = ((rom[6] & 0xf0) >> 4) | (rom[7] & 0xf0);
|
||||
|
||||
const char *slot_string = "algn";
|
||||
// if (mapper == 71)
|
||||
// slot_string = "algn";
|
||||
if (mapper == 232)
|
||||
@ -158,8 +158,8 @@ std::string nes_aladdin_slot_device::get_default_card_software(get_default_card_
|
||||
|
||||
return std::string(slot_string);
|
||||
}
|
||||
else
|
||||
return software_get_default_slot("algn");
|
||||
|
||||
return software_get_default_slot("algn");
|
||||
}
|
||||
|
||||
|
||||
|
@ -944,16 +944,17 @@ std::string nes_cart_slot_device::get_default_card_software(get_default_card_sof
|
||||
{
|
||||
if (hook.image_file())
|
||||
{
|
||||
const char *slot_string = "nrom";
|
||||
uint32_t len = hook.image_file()->size();
|
||||
uint64_t len;
|
||||
hook.image_file()->length(len); // FIXME: check error return, guard against excessively large files
|
||||
std::vector<uint8_t> rom(len);
|
||||
|
||||
hook.image_file()->read(&rom[0], len);
|
||||
size_t actual;
|
||||
hook.image_file()->read(&rom[0], len, actual); // FIXME: check error return or read returning short
|
||||
|
||||
const char *slot_string = "nrom";
|
||||
if ((rom[0] == 'N') && (rom[1] == 'E') && (rom[2] == 'S'))
|
||||
slot_string = get_default_card_ines(hook, &rom[0], len);
|
||||
|
||||
if ((rom[0] == 'U') && (rom[1] == 'N') && (rom[2] == 'I') && (rom[3] == 'F'))
|
||||
else if ((rom[0] == 'U') && (rom[1] == 'N') && (rom[2] == 'I') && (rom[3] == 'F'))
|
||||
slot_string = get_default_card_unif(&rom[0], len);
|
||||
|
||||
return std::string(slot_string);
|
||||
|
@ -86,16 +86,16 @@ void nubus_image_device::messimg_disk_image_device::device_start()
|
||||
{
|
||||
m_data = nullptr;
|
||||
|
||||
if (exists() && fseek(0, SEEK_END) == 0)
|
||||
if (exists() && !fseek(0, SEEK_END))
|
||||
{
|
||||
m_size = (uint32_t)ftell();
|
||||
m_size = uint32_t(ftell());
|
||||
}
|
||||
}
|
||||
|
||||
image_init_result nubus_image_device::messimg_disk_image_device::call_load()
|
||||
{
|
||||
fseek(0, SEEK_END);
|
||||
m_size = (uint32_t)ftell();
|
||||
m_size = uint32_t(ftell());
|
||||
if (m_size > (256*1024*1024))
|
||||
{
|
||||
osd_printf_error("Mac image too large: must be 256MB or less!\n");
|
||||
|
@ -182,10 +182,10 @@ std::string o2_cart_slot_device::get_default_card_software(get_default_card_soft
|
||||
{
|
||||
if (hook.image_file())
|
||||
{
|
||||
const char *slot_string;
|
||||
u32 size = hook.image_file()->size();
|
||||
int type = (size == 0x4000) ? O2_RALLY : O2_STD;
|
||||
slot_string = o2_get_slot(type);
|
||||
uint64_t size;
|
||||
hook.image_file()->length(size); // FIXME: check error return
|
||||
int const type = (size == 0x4000) ? O2_RALLY : O2_STD;
|
||||
char const *const slot_string = o2_get_slot(type);
|
||||
|
||||
return std::string(slot_string);
|
||||
}
|
||||
|
@ -321,15 +321,15 @@ std::string pce_cart_slot_device::get_default_card_software(get_default_card_sof
|
||||
{
|
||||
if (hook.image_file())
|
||||
{
|
||||
const char *slot_string;
|
||||
uint32_t len = hook.image_file()->size();
|
||||
uint64_t len;
|
||||
hook.image_file()->length(len); // FIXME: check error return, guard against excessively large files
|
||||
std::vector<uint8_t> rom(len);
|
||||
int type;
|
||||
|
||||
hook.image_file()->read(&rom[0], len);
|
||||
size_t actual;
|
||||
hook.image_file()->read(&rom[0], len, actual); // FIXME: check error return or read returning short
|
||||
|
||||
type = get_cart_type(&rom[0], len);
|
||||
slot_string = pce_get_slot(type);
|
||||
int const type = get_cart_type(&rom[0], len);
|
||||
char const *const slot_string = pce_get_slot(type);
|
||||
|
||||
//printf("type: %s\n", slot_string);
|
||||
|
||||
|
@ -238,15 +238,15 @@ std::string scv_cart_slot_device::get_default_card_software(get_default_card_sof
|
||||
{
|
||||
if (hook.image_file())
|
||||
{
|
||||
const char *slot_string;
|
||||
uint32_t len = hook.image_file()->size();
|
||||
uint64_t len;
|
||||
hook.image_file()->length(len); // FIXME: check error return, guard against excessively large files
|
||||
std::vector<uint8_t> rom(len);
|
||||
int type;
|
||||
|
||||
hook.image_file()->read(&rom[0], len);
|
||||
size_t actual;
|
||||
hook.image_file()->read(&rom[0], len, actual); // FIXME: check error return or read returning short
|
||||
|
||||
type = get_cart_type(&rom[0], len);
|
||||
slot_string = scv_get_slot(type);
|
||||
int const type = get_cart_type(&rom[0], len);
|
||||
char const *const slot_string = scv_get_slot(type);
|
||||
|
||||
//printf("type: %s\n", slot_string);
|
||||
|
||||
|
@ -666,18 +666,17 @@ std::string sega8_cart_slot_device::get_default_card_software(get_default_card_s
|
||||
{
|
||||
if (hook.image_file())
|
||||
{
|
||||
const char *slot_string;
|
||||
uint32_t len = hook.image_file()->size(), offset = 0;
|
||||
uint64_t len;
|
||||
hook.image_file()->length(len); // FIXME: check error return, guard against excessively large files
|
||||
std::vector<uint8_t> rom(len);
|
||||
int type;
|
||||
|
||||
hook.image_file()->read(&rom[0], len);
|
||||
size_t actual;
|
||||
hook.image_file()->read(&rom[0], len, actual); // FIXME: check error return or read returning short
|
||||
|
||||
if ((len % 0x4000) == 512)
|
||||
offset = 512;
|
||||
uint32_t const offset = ((len % 0x4000) == 512) ? 512 : 0;
|
||||
|
||||
type = get_cart_type(&rom[offset], len - offset);
|
||||
slot_string = sega8_get_slot(type);
|
||||
int const type = get_cart_type(&rom[offset], len - offset);
|
||||
char const *const slot_string = sega8_get_slot(type);
|
||||
|
||||
//printf("type: %s\n", slot_string);
|
||||
|
||||
|
@ -1021,13 +1021,14 @@ std::string base_sns_cart_slot_device::get_default_card_software(get_default_car
|
||||
{
|
||||
if (hook.image_file())
|
||||
{
|
||||
const char *slot_string;
|
||||
uint32_t offset;
|
||||
uint32_t len = hook.image_file()->size();
|
||||
uint64_t len;
|
||||
hook.image_file()->length(len); // FIXME: check error return, guard against excessively large file
|
||||
std::vector<uint8_t> rom(len);
|
||||
int type = 0, addon = 0;
|
||||
|
||||
hook.image_file()->read(&rom[0], len);
|
||||
size_t actual;
|
||||
hook.image_file()->read(&rom[0], len, actual); // FIXME: check for error result or read returning short
|
||||
|
||||
offset = snes_skip_header(&rom[0], len);
|
||||
|
||||
@ -1070,7 +1071,7 @@ std::string base_sns_cart_slot_device::get_default_card_software(get_default_car
|
||||
break;
|
||||
}
|
||||
|
||||
slot_string = sns_get_slot(type);
|
||||
char const *const slot_string = sns_get_slot(type);
|
||||
|
||||
return std::string(slot_string);
|
||||
}
|
||||
|
@ -257,7 +257,10 @@ image_init_result ti99_cartridge_device::call_load()
|
||||
}
|
||||
else
|
||||
{
|
||||
std::error_condition err = rpk_open(machine().options(), util::core_file_read(image_core_file()), machine().system().name, m_rpk);
|
||||
util::core_file::ptr proxy;
|
||||
std::error_condition err = util::core_file::open_proxy(image_core_file(), proxy);
|
||||
if (!err)
|
||||
err = rpk_open(machine().options(), std::move(proxy), machine().system().name, m_rpk);
|
||||
if (err)
|
||||
{
|
||||
LOGMASKED(LOG_WARN, "Failed to load cartridge '%s': %s\n", basename(), err.message().c_str());
|
||||
|
@ -226,17 +226,17 @@ std::string vc4000_cart_slot_device::get_default_card_software(get_default_card_
|
||||
{
|
||||
if (hook.image_file())
|
||||
{
|
||||
const char *slot_string;
|
||||
uint32_t size = hook.image_file()->size();
|
||||
std::uint64_t size;
|
||||
hook.image_file()->length(size); // FIXME: check error result
|
||||
int type = VC4000_STD;
|
||||
|
||||
// attempt to identify the non-standard types
|
||||
if (size > 0x1000) // 6k rom + 1k ram - Chess2 only
|
||||
type = VC4000_CHESS2;
|
||||
else if (size > 0x0800) // some 4k roms have 1k of mirrored ram
|
||||
else if (size > 0x0800) // some 4k roms have 1k of mirrored RAM
|
||||
type = VC4000_RAM1K;
|
||||
|
||||
slot_string = vc4000_get_slot(type);
|
||||
char const *const slot_string = vc4000_get_slot(type);
|
||||
|
||||
//printf("type: %s\n", slot_string);
|
||||
|
||||
|
@ -762,15 +762,15 @@ std::string vcs_cart_slot_device::get_default_card_software(get_default_card_sof
|
||||
{
|
||||
if (hook.image_file())
|
||||
{
|
||||
const char *slot_string;
|
||||
uint32_t len = hook.image_file()->size();
|
||||
uint64_t len;
|
||||
hook.image_file()->length(len); // FIXME: check error return, guard against excessively large files
|
||||
std::vector<uint8_t> rom(len);
|
||||
int type;
|
||||
|
||||
hook.image_file()->read(&rom[0], len);
|
||||
size_t actual;
|
||||
hook.image_file()->read(&rom[0], len, actual); // FIXME: check error return or read returning short
|
||||
|
||||
type = identify_cart_type(&rom[0], len);
|
||||
slot_string = vcs_get_slot(type);
|
||||
int const type = identify_cart_type(&rom[0], len);
|
||||
char const *const slot_string = vcs_get_slot(type);
|
||||
|
||||
return std::string(slot_string);
|
||||
}
|
||||
|
@ -198,19 +198,21 @@ std::string vectrex_cart_slot_device::get_default_card_software(get_default_card
|
||||
{
|
||||
if (hook.image_file())
|
||||
{
|
||||
const char *slot_string;
|
||||
uint32_t size = hook.image_file()->size();
|
||||
// FIXME: consider oversize files, check for errors, and check for read returning early
|
||||
// it's also really wasteful to read the whole file here when only a small part of it is used for identification
|
||||
std::uint64_t size;
|
||||
hook.image_file()->length(size);
|
||||
std::vector<uint8_t> rom(size);
|
||||
std::size_t actual;
|
||||
hook.image_file()->read(&rom[0], size, actual);
|
||||
|
||||
int type = VECTREX_STD;
|
||||
|
||||
hook.image_file()->read(&rom[0], size);
|
||||
|
||||
if (!memcmp(&rom[0x06], "SRAM", 4))
|
||||
if (!memcmp(&rom[0x06], "SRAM", 4)) // FIXME: bounds check!
|
||||
type = VECTREX_SRAM;
|
||||
if (size > 0x8000)
|
||||
type = VECTREX_64K;
|
||||
|
||||
slot_string = vectrex_get_slot(type);
|
||||
char const *const slot_string = vectrex_get_slot(type);
|
||||
|
||||
//printf("type: %s\n", slot_string);
|
||||
|
||||
|
@ -277,13 +277,22 @@ std::string ws_cart_slot_device::get_default_card_software(get_default_card_soft
|
||||
{
|
||||
if (hook.image_file())
|
||||
{
|
||||
// FIXME: multiple issues in this function
|
||||
// * Check for error from getting file length
|
||||
// * Check for file length too large for size_t
|
||||
// * File length is bytes but vector is sized in words
|
||||
// * Catch out-of-memory when resizing vector
|
||||
// * Check for error reading
|
||||
// * Consider big-endian hosts - word data needs swapping
|
||||
const char *slot_string;
|
||||
u32 size = hook.image_file()->size();
|
||||
std::uint64_t size = 0;
|
||||
hook.image_file()->length(size);
|
||||
std::vector<u16> rom(size);
|
||||
int type;
|
||||
u32 nvram;
|
||||
|
||||
hook.image_file()->read(&rom[0], size);
|
||||
size_t actual;
|
||||
hook.image_file()->read(&rom[0], size, actual);
|
||||
|
||||
// nvram size is not really used here, but we set it up nevertheless
|
||||
type = get_cart_type(&rom[0], size, nvram);
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "formats/imageutl.h"
|
||||
|
||||
#include "util/ioprocs.h"
|
||||
#include "util/ioprocsfilter.h"
|
||||
|
||||
#define LOG_WARN (1U<<1) // Warnings
|
||||
#define LOG_DETAIL (1U<<2) // Details
|
||||
@ -260,7 +261,7 @@ image_init_result cassette_image_device::internal_load(bool is_create)
|
||||
check_for_file();
|
||||
if (is_create || (length()==0)) // empty existing images are fine to write over.
|
||||
{
|
||||
auto io = util::core_file_read_write(image_core_file(), 0x00);
|
||||
auto io = util::random_read_write_fill(image_core_file(), 0x00);
|
||||
if (io)
|
||||
{
|
||||
// creating an image
|
||||
@ -285,7 +286,7 @@ image_init_result cassette_image_device::internal_load(bool is_create)
|
||||
// we probably don't want to retry...
|
||||
retry = false;
|
||||
|
||||
auto io = util::core_file_read_write(image_core_file(), 0x00);
|
||||
auto io = util::random_read_write_fill(image_core_file(), 0x00);
|
||||
if (io)
|
||||
{
|
||||
// try opening the cassette
|
||||
|
@ -91,7 +91,10 @@ image_init_result cdrom_image_device::call_load()
|
||||
|
||||
if (!loaded_through_softlist()) {
|
||||
if (is_filetype("chd") && is_loaded()) {
|
||||
err = m_self_chd.open(util::core_file_read_write(image_core_file())); // CDs are never writeable
|
||||
util::core_file::ptr proxy;
|
||||
err = util::core_file::open_proxy(image_core_file(), proxy);
|
||||
if (!err)
|
||||
err = m_self_chd.open(std::move(proxy)); // CDs are never writeable
|
||||
if (err)
|
||||
goto error;
|
||||
chd = &m_self_chd;
|
||||
|
@ -127,7 +127,10 @@ image_init_result diablo_image_device::call_create(int create_format, util::opti
|
||||
|
||||
/* create the CHD file */
|
||||
chd_codec_type compression[4] = { CHD_CODEC_NONE };
|
||||
std::error_condition err = m_origchd.create(util::core_file_read_write(image_core_file()), uint64_t(totalsectors) * uint64_t(sectorsize), hunksize, sectorsize, compression);
|
||||
util::core_file::ptr proxy;
|
||||
std::error_condition err = util::core_file::open_proxy(image_core_file(), proxy);
|
||||
if (!err)
|
||||
m_origchd.create(std::move(proxy), uint64_t(totalsectors) * uint64_t(sectorsize), hunksize, sectorsize, compression);
|
||||
if (err)
|
||||
return image_init_result::FAIL;
|
||||
|
||||
@ -219,14 +222,19 @@ image_init_result diablo_image_device::internal_load_dsk()
|
||||
}
|
||||
else
|
||||
{
|
||||
err = m_origchd.open(util::core_file_read_write(image_core_file()), true);
|
||||
util::core_file::ptr proxy;
|
||||
err = util::core_file::open_proxy(image_core_file(), proxy);
|
||||
if (!err)
|
||||
err = m_origchd.open(std::move(proxy), true);
|
||||
if (!err)
|
||||
{
|
||||
m_chd = &m_origchd;
|
||||
}
|
||||
else if (err == chd_file::error::FILE_NOT_WRITEABLE)
|
||||
{
|
||||
err = m_origchd.open(util::core_file_read_write(image_core_file()), false);
|
||||
err = util::core_file::open_proxy(image_core_file(), proxy);
|
||||
if (!err)
|
||||
err = m_origchd.open(std::move(proxy), false);
|
||||
if (!err)
|
||||
{
|
||||
err = open_disk_diff(device().machine().options(), basename_noext(), m_origchd, m_diffchd);
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "formats/imageutl.h"
|
||||
|
||||
#include "util/ioprocs.h"
|
||||
#include "util/ioprocsfilter.h"
|
||||
|
||||
|
||||
#define VERBOSE 0
|
||||
@ -426,7 +427,7 @@ image_init_result legacy_floppy_image_device::internal_floppy_device_load(bool i
|
||||
|
||||
floperr_t err;
|
||||
check_for_file();
|
||||
auto io = util::core_file_read_write(image_core_file(), 0xff);
|
||||
auto io = util::random_read_write_fill(image_core_file(), 0xff);
|
||||
if (!io)
|
||||
{
|
||||
err = FLOPPY_ERROR_OUTOFMEMORY;
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "formats/imageutl.h"
|
||||
|
||||
#include "util/ioprocs.h"
|
||||
#include "util/ioprocsfilter.h"
|
||||
#include "util/zippath.h"
|
||||
|
||||
/*
|
||||
@ -403,7 +404,7 @@ void floppy_image_device::commit_image()
|
||||
return;
|
||||
|
||||
check_for_file();
|
||||
auto io = util::core_file_read_write(image_core_file(), 0xff);
|
||||
auto io = util::random_read_write_fill(image_core_file(), 0xff);
|
||||
if(!io) {
|
||||
popmessage("Error, out of memory");
|
||||
return;
|
||||
@ -543,7 +544,7 @@ floppy_image_format_t *floppy_image_device::identify(std::string filename)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto io = util::core_file_read(std::move(fd), 0xff);
|
||||
auto io = util::random_read_fill(std::move(fd), 0xff);
|
||||
if(!io) {
|
||||
seterror(std::errc::not_enough_memory, nullptr);
|
||||
return nullptr;
|
||||
@ -591,7 +592,7 @@ void floppy_image_device::init_floppy_load(bool write_supported)
|
||||
image_init_result floppy_image_device::call_load()
|
||||
{
|
||||
check_for_file();
|
||||
auto io = util::core_file_read(image_core_file(), 0xff);
|
||||
auto io = util::random_read_fill(image_core_file(), 0xff);
|
||||
if(!io) {
|
||||
seterror(std::errc::not_enough_memory, nullptr);
|
||||
return image_init_result::FAIL;
|
||||
|
@ -148,7 +148,10 @@ image_init_result harddisk_image_device::call_create(int create_format, util::op
|
||||
|
||||
/* create the CHD file */
|
||||
chd_codec_type compression[4] = { CHD_CODEC_NONE };
|
||||
std::error_condition err = m_origchd.create(util::core_file_read_write(image_core_file()), (uint64_t)totalsectors * (uint64_t)sectorsize, hunksize, sectorsize, compression);
|
||||
util::core_file::ptr proxy;
|
||||
std::error_condition err = util::core_file::open_proxy(image_core_file(), proxy);
|
||||
if (!err)
|
||||
err = m_origchd.create(std::move(proxy), uint64_t(totalsectors) * uint64_t(sectorsize), hunksize, sectorsize, compression);
|
||||
if (err)
|
||||
return image_init_result::FAIL;
|
||||
|
||||
@ -250,7 +253,10 @@ image_init_result harddisk_image_device::internal_load_hd()
|
||||
|
||||
if (!memcmp("MComprHD", header, 8))
|
||||
{
|
||||
err = m_origchd.open(util::core_file_read_write(image_core_file()), true);
|
||||
util::core_file::ptr proxy;
|
||||
err = util::core_file::open_proxy(image_core_file(), proxy);
|
||||
if (!err)
|
||||
err = m_origchd.open(std::move(proxy), true);
|
||||
|
||||
if (!err)
|
||||
{
|
||||
@ -258,7 +264,10 @@ image_init_result harddisk_image_device::internal_load_hd()
|
||||
}
|
||||
else if (err == chd_file::error::FILE_NOT_WRITEABLE)
|
||||
{
|
||||
err = m_origchd.open(util::core_file_read_write(image_core_file()), false);
|
||||
err = util::core_file::open_proxy(image_core_file(), proxy);
|
||||
if (!err)
|
||||
err = m_origchd.open(std::move(proxy), false);
|
||||
|
||||
if (!err)
|
||||
{
|
||||
err = open_disk_diff(device().machine().options(), basename_noext(), m_origchd, m_diffchd);
|
||||
@ -295,7 +304,7 @@ image_init_result harddisk_image_device::internal_load_hd()
|
||||
{
|
||||
skip = header[0x8] | (header[0x9] << 8) | (header[0xa] << 16) | (header[0xb] << 24);
|
||||
uint32_t data_size = header[0xc] | (header[0xd] << 8) | (header[0xe] << 16) | (header[0xf] << 24);
|
||||
if (data_size == image_core_file().size() - skip)
|
||||
if (data_size == length() - skip)
|
||||
{
|
||||
osd_printf_verbose("harddriv: detected Anex86 HDI, data at %08x\n", skip);
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ image_init_result midiin_device::call_load()
|
||||
// if the parsing succeeds, schedule the start to happen at least
|
||||
// 10 seconds after starting to allow the keyboards to initialize
|
||||
// TODO: this should perhaps be a driver-configurable parameter?
|
||||
if (m_sequence.parse(reinterpret_cast<u8 *>(ptr()), length()))
|
||||
if (m_sequence.parse(reinterpret_cast<u8 const *>(ptr()), length()))
|
||||
{
|
||||
m_sequence_start = std::max(machine().time(), attotime(10, 0));
|
||||
m_timer->adjust(attotime::zero);
|
||||
|
@ -30,7 +30,7 @@
|
||||
#include "emu.h"
|
||||
#include "hp_dc100_tape.h"
|
||||
|
||||
#include "util/ioprocs.h"
|
||||
#include "util/ioprocsfilter.h"
|
||||
|
||||
// Debugging
|
||||
#include "logmacro.h"
|
||||
@ -110,7 +110,7 @@ void hp_dc100_tape_device::call_unload()
|
||||
|
||||
if (m_image_dirty) {
|
||||
check_for_file();
|
||||
auto io = util::core_file_read_write(image_core_file(), 0);
|
||||
auto io = util::random_read_write_fill(image_core_file(), 0);
|
||||
if (io) {
|
||||
m_image.save_tape(*io);
|
||||
m_image_dirty = false;
|
||||
@ -606,7 +606,7 @@ image_init_result hp_dc100_tape_device::internal_load(bool is_create)
|
||||
|
||||
check_for_file();
|
||||
if (is_create) {
|
||||
auto io = util::core_file_read_write(image_core_file(), 0);
|
||||
auto io = util::random_read_write_fill(image_core_file(), 0);
|
||||
if (!io) {
|
||||
LOG("out of memory\n");
|
||||
seterror(std::errc::not_enough_memory, nullptr);
|
||||
@ -616,7 +616,7 @@ image_init_result hp_dc100_tape_device::internal_load(bool is_create)
|
||||
m_image.clear_tape();
|
||||
m_image.save_tape(*io);
|
||||
} else {
|
||||
auto io = util::core_file_read(image_core_file(), 0);
|
||||
auto io = util::random_read_fill(image_core_file(), 0);
|
||||
if (!io) {
|
||||
LOG("out of memory\n");
|
||||
seterror(std::errc::not_enough_memory, nullptr);
|
||||
|
@ -101,7 +101,7 @@ public:
|
||||
named_delegate &operator=(std::nullptr_t) noexcept { reset(); return *this; }
|
||||
|
||||
// getters
|
||||
char const *name() const { return m_name; }
|
||||
char const *name() const noexcept { return m_name; }
|
||||
|
||||
// unsetter
|
||||
void reset() noexcept { basetype::reset(); m_name = nullptr; }
|
||||
|
@ -434,11 +434,13 @@ bool device_image_interface::run_hash(util::core_file &file, u32 skip_bytes, uti
|
||||
hashes.reset();
|
||||
|
||||
// figure out the size, and "cap" the skip bytes
|
||||
u64 size = file.size();
|
||||
skip_bytes = (u32) std::min((u64) skip_bytes, size);
|
||||
u64 size;
|
||||
if (file.length(size))
|
||||
return false;
|
||||
skip_bytes = u32(std::min<u64>(skip_bytes, size));
|
||||
|
||||
// seek to the beginning
|
||||
file.seek(skip_bytes, SEEK_SET);
|
||||
file.seek(skip_bytes, SEEK_SET); // TODO: check error return
|
||||
u64 position = skip_bytes;
|
||||
|
||||
// keep on reading hashes
|
||||
@ -448,9 +450,10 @@ bool device_image_interface::run_hash(util::core_file &file, u32 skip_bytes, uti
|
||||
uint8_t buffer[8192];
|
||||
|
||||
// read bytes
|
||||
const u32 count = (u32) std::min(size - position, (u64) sizeof(buffer));
|
||||
const u32 actual_count = file.read(buffer, count);
|
||||
if (actual_count == 0)
|
||||
const size_t count = size_t(std::min<u64>(size - position, sizeof(buffer)));
|
||||
size_t actual_count;
|
||||
const std::error_condition filerr = file.read(buffer, count, actual_count);
|
||||
if (filerr || !actual_count)
|
||||
return false;
|
||||
position += actual_count;
|
||||
|
||||
@ -460,7 +463,7 @@ bool device_image_interface::run_hash(util::core_file &file, u32 skip_bytes, uti
|
||||
hashes.end();
|
||||
|
||||
// cleanup
|
||||
file.seek(0, SEEK_SET);
|
||||
file.seek(0, SEEK_SET); // TODO: check error return
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -156,37 +156,93 @@ public:
|
||||
void message(const char *format, ...) ATTR_PRINTF(2,3);
|
||||
|
||||
bool exists() const noexcept { return !m_image_name.empty(); }
|
||||
|
||||
// get image file path/name
|
||||
const char *filename() const noexcept { return m_image_name.empty() ? nullptr : m_image_name.c_str(); }
|
||||
const char *basename() const noexcept { return m_basename.empty() ? nullptr : m_basename.c_str(); }
|
||||
const char *basename_noext() const noexcept { return m_basename_noext.empty() ? nullptr : m_basename_noext.c_str(); }
|
||||
const std::string &filetype() const noexcept { return m_filetype; }
|
||||
bool is_filetype(std::string_view candidate_filetype) const;
|
||||
|
||||
bool is_open() const noexcept { return bool(m_file); }
|
||||
util::core_file &image_core_file() const noexcept { return *m_file; }
|
||||
u64 length() { check_for_file(); return m_file->size(); }
|
||||
bool is_readonly() const noexcept { return m_readonly; }
|
||||
u32 fread(void *buffer, u32 length) { check_for_file(); return m_file->read(buffer, length); }
|
||||
u32 fread(std::unique_ptr<u8[]> &ptr, u32 length) { ptr = std::make_unique<u8[]>(length); return fread(ptr.get(), length); }
|
||||
u32 fread(std::unique_ptr<u8[]> &ptr, u32 length, offs_t offset) { ptr = std::make_unique<u8[]>(length); return fread(ptr.get() + offset, length - offset); }
|
||||
u32 fwrite(const void *buffer, u32 length) { check_for_file(); return m_file->write(buffer, length); }
|
||||
int fseek(s64 offset, int whence) { check_for_file(); return m_file->seek(offset, whence); }
|
||||
u64 ftell() { check_for_file(); return m_file->tell(); }
|
||||
int fgetc() { char ch; if (fread(&ch, 1) != 1) ch = '\0'; return ch; }
|
||||
char *fgets(char *buffer, u32 length) { check_for_file(); return m_file->gets(buffer, length); }
|
||||
int image_feof() { check_for_file(); return m_file->eof(); }
|
||||
void *ptr() { check_for_file(); return const_cast<void *>(m_file->buffer()); }
|
||||
// configuration access
|
||||
|
||||
// image file I/O wrappers
|
||||
// TODO: move away from using these and let implementations use the I/O interface directly
|
||||
// FIXME: don't swallow errors
|
||||
u64 length()
|
||||
{
|
||||
check_for_file();
|
||||
u64 result = 0;
|
||||
m_file->length(result);
|
||||
return result;
|
||||
}
|
||||
u32 fread(void *buffer, u32 length)
|
||||
{
|
||||
check_for_file();
|
||||
size_t actual;
|
||||
m_file->read(buffer, length, actual);
|
||||
return actual;
|
||||
}
|
||||
u32 fwrite(const void *buffer, u32 length)
|
||||
{
|
||||
check_for_file();
|
||||
size_t actual;
|
||||
m_file->write(buffer, length, actual);
|
||||
return actual;
|
||||
}
|
||||
std::error_condition fseek(s64 offset, int whence)
|
||||
{
|
||||
check_for_file();
|
||||
return m_file->seek(offset, whence);
|
||||
}
|
||||
u64 ftell()
|
||||
{
|
||||
check_for_file();
|
||||
u64 result = 0;
|
||||
m_file->tell(result);
|
||||
return result;
|
||||
}
|
||||
int fgetc()
|
||||
{
|
||||
char ch;
|
||||
if (fread(&ch, 1) != 1)
|
||||
ch = '\0';
|
||||
return ch;
|
||||
}
|
||||
char *fgets(char *buffer, u32 length)
|
||||
{
|
||||
check_for_file();
|
||||
return m_file->gets(buffer, length);
|
||||
}
|
||||
bool image_feof()
|
||||
{
|
||||
check_for_file();
|
||||
return m_file->eof();
|
||||
}
|
||||
const void *ptr()
|
||||
{
|
||||
check_for_file();
|
||||
return m_file->buffer();
|
||||
}
|
||||
|
||||
// allocate and read into buffers
|
||||
u32 fread(std::unique_ptr<u8 []> &ptr, u32 length) { ptr = std::make_unique<u8 []>(length); return fread(ptr.get(), length); }
|
||||
u32 fread(std::unique_ptr<u8 []> &ptr, u32 length, offs_t offset) { ptr = std::make_unique<u8 []>(length); return fread(ptr.get() + offset, length - offset); }
|
||||
|
||||
// access to software list item information
|
||||
const software_info *software_entry() const noexcept;
|
||||
const software_part *part_entry() const noexcept { return m_software_part_ptr; }
|
||||
const char *software_list_name() const noexcept { return m_software_list_name.c_str(); }
|
||||
bool loaded_through_softlist() const noexcept { return m_software_part_ptr != nullptr; }
|
||||
|
||||
// working directory
|
||||
void set_working_directory(std::string_view working_directory) { m_working_directory = working_directory; }
|
||||
void set_working_directory(const char *working_directory) { m_working_directory = working_directory; }
|
||||
void set_working_directory(std::string &&working_directory) { m_working_directory = std::move(working_directory); }
|
||||
const std::string &working_directory() const { return m_working_directory; }
|
||||
|
||||
// access to software list properties and ROM data areas
|
||||
u8 *get_software_region(const char *tag);
|
||||
u32 get_software_region_length(const char *tag);
|
||||
const char *get_feature(const char *feature_name) const;
|
||||
|
@ -285,7 +285,9 @@ util::hash_collection &emu_file::hashes(std::string_view types)
|
||||
return m_hashes;
|
||||
|
||||
// compute the hash
|
||||
m_hashes.compute(filedata, m_file->size(), needed.c_str());
|
||||
std::uint64_t length;
|
||||
if (!m_file->length(length))
|
||||
m_hashes.compute(filedata, length, needed.c_str());
|
||||
return m_hashes;
|
||||
}
|
||||
|
||||
@ -445,17 +447,18 @@ std::error_condition emu_file::compressed_file_ready()
|
||||
// seek - seek within a file
|
||||
//-------------------------------------------------
|
||||
|
||||
int emu_file::seek(s64 offset, int whence)
|
||||
std::error_condition emu_file::seek(s64 offset, int whence)
|
||||
{
|
||||
// load the ZIP file now if we haven't yet
|
||||
if (compressed_file_ready())
|
||||
return 1;
|
||||
std::error_condition err = compressed_file_ready();
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
// seek if we can
|
||||
if (m_file)
|
||||
return m_file->seek(offset, whence);
|
||||
|
||||
return 1;
|
||||
return std::errc::bad_file_descriptor; // TODO: revisit this error condition
|
||||
}
|
||||
|
||||
|
||||
@ -465,13 +468,15 @@ int emu_file::seek(s64 offset, int whence)
|
||||
|
||||
u64 emu_file::tell()
|
||||
{
|
||||
// FIXME: need better interface to report errors
|
||||
// load the ZIP file now if we haven't yet
|
||||
if (compressed_file_ready())
|
||||
return 0;
|
||||
|
||||
// tell if we can
|
||||
if (m_file)
|
||||
return m_file->tell();
|
||||
u64 result;
|
||||
if (m_file && !m_file->tell(result))
|
||||
return result;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -501,13 +506,15 @@ bool emu_file::eof()
|
||||
|
||||
u64 emu_file::size()
|
||||
{
|
||||
// FIXME: need better interface to report errors
|
||||
// use the ZIP length if present
|
||||
if (m_zipfile != nullptr)
|
||||
if (m_zipfile)
|
||||
return m_ziplength;
|
||||
|
||||
// return length if we can
|
||||
if (m_file)
|
||||
return m_file->size();
|
||||
u64 result;
|
||||
if (m_file && !m_file->length(result))
|
||||
return result;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -519,15 +526,17 @@ u64 emu_file::size()
|
||||
|
||||
u32 emu_file::read(void *buffer, u32 length)
|
||||
{
|
||||
// FIXME: need better interface to report errors
|
||||
// load the ZIP file now if we haven't yet
|
||||
if (compressed_file_ready())
|
||||
return 0;
|
||||
|
||||
// read the data if we can
|
||||
size_t actual = 0;
|
||||
if (m_file)
|
||||
return m_file->read(buffer, length);
|
||||
m_file->read(buffer, length, actual);
|
||||
|
||||
return 0;
|
||||
return actual;
|
||||
}
|
||||
|
||||
|
||||
@ -591,11 +600,13 @@ char *emu_file::gets(char *s, int n)
|
||||
|
||||
u32 emu_file::write(const void *buffer, u32 length)
|
||||
{
|
||||
// FIXME: need better interface to report errors
|
||||
// write the data if we can
|
||||
size_t actual = 0;
|
||||
if (m_file)
|
||||
return m_file->write(buffer, length);
|
||||
m_file->write(buffer, length, actual);
|
||||
|
||||
return 0;
|
||||
return actual;
|
||||
}
|
||||
|
||||
|
||||
|
@ -168,7 +168,7 @@ public:
|
||||
void close();
|
||||
|
||||
// position
|
||||
int seek(s64 offset, int whence);
|
||||
std::error_condition seek(s64 offset, int whence);
|
||||
u64 tell();
|
||||
bool eof();
|
||||
u64 size();
|
||||
|
@ -124,7 +124,7 @@ void image_manager::config_load(config_type cfg_type, config_level cfg_level, ut
|
||||
{
|
||||
const char *const working_directory = node->get_attribute_string("directory", nullptr);
|
||||
if (working_directory != nullptr)
|
||||
image.set_working_directory(working_directory);
|
||||
image.set_working_directory(std::string_view(working_directory));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2730,7 +2730,7 @@ time_t ioport_manager::playback_init()
|
||||
osd_printf_info("Input file is for machine '%s', not for current machine '%s'\n", sysname, machine().system().name);
|
||||
|
||||
// enable compression
|
||||
m_playback_stream = util::zlib_read(util::core_file_read(m_playback_file), 16386);
|
||||
m_playback_stream = util::zlib_read(m_playback_file, 16386);
|
||||
return basetime;
|
||||
}
|
||||
|
||||
@ -2911,7 +2911,7 @@ void ioport_manager::record_init()
|
||||
header.write(m_record_file);
|
||||
|
||||
// enable compression
|
||||
m_record_stream = util::zlib_write(util::core_file_read_write(m_record_file), 6, 16384);
|
||||
m_record_stream = util::zlib_write(m_record_file, 6, 16384);
|
||||
}
|
||||
|
||||
|
||||
|
@ -264,10 +264,10 @@ bool mng_movie_recording::initialize(std::unique_ptr<emu_file> &&file, bitmap_t
|
||||
set_frame_period(attotime::from_hz(rate));
|
||||
|
||||
m_mng_file = std::move(file);
|
||||
util::png_error pngerr = util::mng_capture_start(*m_mng_file, snap_bitmap, rate);
|
||||
if (pngerr != util::png_error::NONE)
|
||||
osd_printf_error("Error capturing MNG, png_error=%d\n", std::underlying_type_t<util::png_error>(pngerr));
|
||||
return pngerr == util::png_error::NONE;
|
||||
std::error_condition const pngerr = util::mng_capture_start(*m_mng_file, snap_bitmap, rate);
|
||||
if (pngerr)
|
||||
osd_printf_error("Error capturing MNG (%s:%d %s)\n", pngerr.category().name(), pngerr.value(), pngerr.message());
|
||||
return !pngerr;
|
||||
}
|
||||
|
||||
|
||||
@ -285,8 +285,8 @@ bool mng_movie_recording::append_single_video_frame(bitmap_rgb32 &bitmap, const
|
||||
pnginfo.add_text(ent.first, ent.second);
|
||||
}
|
||||
|
||||
util::png_error error = util::mng_capture_frame(*m_mng_file, pnginfo, bitmap, palette_entries, palette);
|
||||
return error == util::png_error::NONE;
|
||||
std::error_condition const error = util::mng_capture_frame(*m_mng_file, pnginfo, bitmap, palette_entries, palette);
|
||||
return !error;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1860,7 +1860,7 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
bool load_bitmap(util::core_file &file)
|
||||
bool load_bitmap(util::random_read &file)
|
||||
{
|
||||
ru_imgformat const format = render_detect_image(file);
|
||||
switch (format)
|
||||
@ -1890,9 +1890,16 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void load_svg(util::core_file &file)
|
||||
void load_svg(util::random_read &file)
|
||||
{
|
||||
u64 len(file.size());
|
||||
std::error_condition filerr;
|
||||
u64 len;
|
||||
filerr = file.length(len);
|
||||
if (filerr)
|
||||
{
|
||||
osd_printf_warning("Error getting length of component image '%s'\n", m_imagefile);
|
||||
return;
|
||||
}
|
||||
if ((std::numeric_limits<size_t>::max() - 1) < len)
|
||||
{
|
||||
osd_printf_warning("Component image '%s' is too large to read into memory\n", m_imagefile);
|
||||
@ -1907,9 +1914,9 @@ private:
|
||||
svgbuf[len] = '\0';
|
||||
for (char *ptr = svgbuf.get(); len; )
|
||||
{
|
||||
u32 const block(u32(std::min<u64>(std::numeric_limits<u32>::max(), len)));
|
||||
u32 const read(file.read(ptr, block));
|
||||
if (!read)
|
||||
size_t read;
|
||||
filerr = file.read(ptr, size_t(len), read);
|
||||
if (filerr || !read)
|
||||
{
|
||||
osd_printf_warning("Error reading component image '%s'\n", m_imagefile);
|
||||
return;
|
||||
|
@ -25,7 +25,7 @@ namespace {
|
||||
|
||||
struct jpeg_corefile_source : public jpeg_source_mgr
|
||||
{
|
||||
static void source(j_decompress_ptr cinfo, util::core_file &file);
|
||||
static void source(j_decompress_ptr cinfo, util::random_read &file);
|
||||
|
||||
private:
|
||||
static constexpr unsigned INPUT_BUF_SIZE = 4096;
|
||||
@ -40,7 +40,8 @@ private:
|
||||
{
|
||||
jpeg_corefile_source &src = *static_cast<jpeg_corefile_source *>(cinfo->src);
|
||||
|
||||
size_t nbytes = src.infile->read(src.buffer, INPUT_BUF_SIZE);
|
||||
size_t nbytes;
|
||||
src.infile->read(src.buffer, INPUT_BUF_SIZE, nbytes); // TODO: check error return
|
||||
|
||||
if (0 >= nbytes)
|
||||
{
|
||||
@ -79,12 +80,12 @@ private:
|
||||
{
|
||||
}
|
||||
|
||||
util::core_file *infile;
|
||||
util::random_read *infile;
|
||||
JOCTET *buffer;
|
||||
bool start_of_file;
|
||||
};
|
||||
|
||||
void jpeg_corefile_source::source(j_decompress_ptr cinfo, util::core_file &file)
|
||||
void jpeg_corefile_source::source(j_decompress_ptr cinfo, util::random_read &file)
|
||||
{
|
||||
jpeg_corefile_source *src;
|
||||
if (!cinfo->src)
|
||||
@ -662,7 +663,7 @@ void render_line_to_quad(const render_bounds *bounds, float width, float length_
|
||||
into a bitmap
|
||||
-------------------------------------------------*/
|
||||
|
||||
void render_load_msdib(bitmap_argb32 &bitmap, util::core_file &file)
|
||||
void render_load_msdib(bitmap_argb32 &bitmap, util::random_read &file)
|
||||
{
|
||||
// deallocate previous bitmap
|
||||
bitmap.reset();
|
||||
@ -682,7 +683,7 @@ void render_load_msdib(bitmap_argb32 &bitmap, util::core_file &file)
|
||||
bitmap
|
||||
-------------------------------------------------*/
|
||||
|
||||
void render_load_jpeg(bitmap_argb32 &bitmap, util::core_file &file)
|
||||
void render_load_jpeg(bitmap_argb32 &bitmap, util::random_read &file)
|
||||
{
|
||||
// deallocate previous bitmap
|
||||
bitmap.reset();
|
||||
@ -764,7 +765,7 @@ cleanup:
|
||||
bitmap
|
||||
-------------------------------------------------*/
|
||||
|
||||
bool render_load_png(bitmap_argb32 &bitmap, util::core_file &file, bool load_as_alpha_to_existing)
|
||||
bool render_load_png(bitmap_argb32 &bitmap, util::random_read &file, bool load_as_alpha_to_existing)
|
||||
{
|
||||
// deallocate if we're not overlaying alpha
|
||||
if (!load_as_alpha_to_existing)
|
||||
@ -772,15 +773,15 @@ bool render_load_png(bitmap_argb32 &bitmap, util::core_file &file, bool load_as_
|
||||
|
||||
// read the PNG data
|
||||
util::png_info png;
|
||||
util::png_error const result = png.read_file(file);
|
||||
if (result != util::png_error::NONE)
|
||||
std::error_condition const result = png.read_file(file);
|
||||
if (result)
|
||||
{
|
||||
osd_printf_error("Error reading PNG file\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// if less than 8 bits, upsample
|
||||
if (util::png_error::NONE != png.expand_buffer_8bit())
|
||||
if (png.expand_buffer_8bit())
|
||||
{
|
||||
osd_printf_error("Error upsampling PNG bitmap\n");
|
||||
return false;
|
||||
@ -790,7 +791,7 @@ bool render_load_png(bitmap_argb32 &bitmap, util::core_file &file, bool load_as_
|
||||
if (!load_as_alpha_to_existing)
|
||||
{
|
||||
// non-alpha case
|
||||
if (util::png_error::NONE != png.copy_to_bitmap(bitmap, hasalpha))
|
||||
if (png.copy_to_bitmap(bitmap, hasalpha))
|
||||
{
|
||||
osd_printf_error("Error copying PNG bitmap to MAME bitmap\n");
|
||||
return false;
|
||||
@ -930,13 +931,13 @@ static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const util::png_info
|
||||
render_detect_image - detect image format
|
||||
-------------------------------------------------*/
|
||||
|
||||
ru_imgformat render_detect_image(util::core_file &file)
|
||||
ru_imgformat render_detect_image(util::random_read &file)
|
||||
{
|
||||
// PNG: check for valid header
|
||||
{
|
||||
util::png_error const png = util::png_info::verify_header(file);
|
||||
file.seek(0, SEEK_SET);
|
||||
if (util::png_error::NONE == png)
|
||||
std::error_condition const png = util::png_info::verify_header(file);
|
||||
file.seek(0, SEEK_SET); // TODO: check error return
|
||||
if (!png)
|
||||
return RENDUTIL_IMGFORMAT_PNG;
|
||||
}
|
||||
|
||||
@ -953,7 +954,7 @@ ru_imgformat render_detect_image(util::core_file &file)
|
||||
jpeg_corefile_source::source(&cinfo, file);
|
||||
jpeg_read_header(&cinfo, TRUE);
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
file.seek(0, SEEK_SET);
|
||||
file.seek(0, SEEK_SET); // TODO: check error return
|
||||
return RENDUTIL_IMGFORMAT_JPEG;
|
||||
|
||||
notjpeg:
|
||||
|
@ -43,10 +43,10 @@ void render_resample_argb_bitmap_hq(bitmap_argb32 &dest, bitmap_argb32 &source,
|
||||
bool render_clip_line(render_bounds *bounds, const render_bounds *clip);
|
||||
bool render_clip_quad(render_bounds *bounds, const render_bounds *clip, render_quad_texuv *texcoords);
|
||||
void render_line_to_quad(const render_bounds *bounds, float width, float length_extension, render_bounds *bounds0, render_bounds *bounds1);
|
||||
void render_load_msdib(bitmap_argb32 &bitmap, util::core_file &file);
|
||||
void render_load_jpeg(bitmap_argb32 &bitmap, util::core_file &file);
|
||||
bool render_load_png(bitmap_argb32 &bitmap, util::core_file &file, bool load_as_alpha_to_existing = false);
|
||||
ru_imgformat render_detect_image(util::core_file &file);
|
||||
void render_load_msdib(bitmap_argb32 &bitmap, util::random_read &file);
|
||||
void render_load_jpeg(bitmap_argb32 &bitmap, util::random_read &file);
|
||||
bool render_load_png(bitmap_argb32 &bitmap, util::random_read &file, bool load_as_alpha_to_existing = false);
|
||||
ru_imgformat render_detect_image(util::random_read &file);
|
||||
|
||||
|
||||
|
||||
|
@ -272,12 +272,14 @@ save_error save_manager::write_file(emu_file &file)
|
||||
{
|
||||
if (file.seek(0, SEEK_SET))
|
||||
return false;
|
||||
writer = util::core_file_read_write(file);
|
||||
return bool(writer);
|
||||
util::core_file::ptr proxy;
|
||||
std::error_condition filerr = util::core_file::open_proxy(file, proxy);
|
||||
writer = std::move(proxy);
|
||||
return !filerr && writer;
|
||||
},
|
||||
[&file, &writer] ()
|
||||
{
|
||||
writer = util::zlib_write(util::core_file_read_write(file), 6, 16384);
|
||||
writer = util::zlib_write(file, 6, 16384);
|
||||
return bool(writer);
|
||||
});
|
||||
return (STATERR_NONE != err) ? err : writer->finalize() ? STATERR_WRITE_ERROR : STATERR_NONE;
|
||||
@ -303,12 +305,14 @@ save_error save_manager::read_file(emu_file &file)
|
||||
{
|
||||
if (file.seek(0, SEEK_SET))
|
||||
return false;
|
||||
reader = util::core_file_read(file);
|
||||
return bool(reader);
|
||||
util::core_file::ptr proxy;
|
||||
std::error_condition filerr = util::core_file::open_proxy(file, proxy);
|
||||
reader = std::move(proxy);
|
||||
return !filerr && reader;
|
||||
},
|
||||
[&file, &reader] ()
|
||||
{
|
||||
reader = util::zlib_read(util::core_file_read(file), 16384);
|
||||
reader = util::zlib_read(file, 16384);
|
||||
return bool(reader);
|
||||
});
|
||||
}
|
||||
|
@ -192,7 +192,7 @@ class softlist_parser
|
||||
public:
|
||||
// construction (== execution)
|
||||
softlist_parser(
|
||||
util::core_file &file,
|
||||
util::random_read &file,
|
||||
std::string_view filename,
|
||||
std::string &listname,
|
||||
std::string &description,
|
||||
@ -239,12 +239,10 @@ private:
|
||||
void parse_soft_end(const char *name);
|
||||
|
||||
// internal parsing state
|
||||
util::core_file & m_file;
|
||||
const std::string_view m_filename;
|
||||
std::list<software_info> & m_infolist;
|
||||
std::ostream & m_errors;
|
||||
struct XML_ParserStruct * m_parser;
|
||||
bool m_done;
|
||||
std::string & m_listname;
|
||||
std::string & m_description;
|
||||
bool m_data_accum_expected;
|
||||
@ -260,17 +258,15 @@ private:
|
||||
//-------------------------------------------------
|
||||
|
||||
softlist_parser::softlist_parser(
|
||||
util::core_file &file,
|
||||
util::random_read &file,
|
||||
std::string_view filename,
|
||||
std::string &listname,
|
||||
std::string &description,
|
||||
std::list<software_info> &infolist,
|
||||
std::ostream &errors) :
|
||||
m_file(file),
|
||||
m_filename(filename),
|
||||
m_infolist(infolist),
|
||||
m_errors(errors),
|
||||
m_done(false),
|
||||
m_listname(listname),
|
||||
m_description(description),
|
||||
m_data_accum_expected(false),
|
||||
@ -280,7 +276,7 @@ softlist_parser::softlist_parser(
|
||||
{
|
||||
// create the parser
|
||||
m_parser = XML_ParserCreate_MM(nullptr, nullptr, nullptr);
|
||||
if (m_parser == nullptr)
|
||||
if (!m_parser)
|
||||
throw std::bad_alloc();
|
||||
|
||||
// set the handlers
|
||||
@ -289,13 +285,15 @@ softlist_parser::softlist_parser(
|
||||
XML_SetCharacterDataHandler(m_parser, &softlist_parser::data_handler);
|
||||
|
||||
// parse the file contents
|
||||
m_file.seek(0, SEEK_SET);
|
||||
file.seek(0, SEEK_SET);
|
||||
char buffer[1024];
|
||||
while (!m_done)
|
||||
for (bool done = false; !done; )
|
||||
{
|
||||
u32 length = m_file.read(buffer, sizeof(buffer));
|
||||
m_done = m_file.eof();
|
||||
if (XML_Parse(m_parser, buffer, length, m_done) == XML_STATUS_ERROR)
|
||||
size_t length;
|
||||
file.read(buffer, sizeof(buffer), length); // TODO: better error handling
|
||||
if (!length)
|
||||
done = true;
|
||||
if (XML_Parse(m_parser, buffer, length, done) == XML_STATUS_ERROR)
|
||||
{
|
||||
parse_error("%s", parser_error());
|
||||
break;
|
||||
@ -885,7 +883,7 @@ void softlist_parser::parse_soft_end(const char *tagname)
|
||||
|
||||
|
||||
void parse_software_list(
|
||||
util::core_file &file,
|
||||
util::random_read &file,
|
||||
std::string_view filename,
|
||||
std::string &listname,
|
||||
std::string &description,
|
||||
|
@ -148,7 +148,7 @@ private:
|
||||
|
||||
// parses a software list
|
||||
void parse_software_list(
|
||||
util::core_file &file,
|
||||
util::random_read &file,
|
||||
std::string_view filename,
|
||||
std::string &listname,
|
||||
std::string &description,
|
||||
|
@ -91,8 +91,6 @@ private:
|
||||
// device representing a software list
|
||||
class software_list_device : public device_t
|
||||
{
|
||||
friend class softlist_parser;
|
||||
|
||||
public:
|
||||
enum class softlist_type
|
||||
{
|
||||
|
@ -339,9 +339,9 @@ void video_manager::save_snapshot(screen_device *screen, emu_file &file)
|
||||
// now do the actual work
|
||||
const rgb_t *palette = (screen != nullptr && screen->has_palette()) ? screen->palette().palette()->entry_list_adjusted() : nullptr;
|
||||
int entries = (screen != nullptr && screen->has_palette()) ? screen->palette().entries() : 0;
|
||||
util::png_error error = util::png_write_bitmap(file, &pnginfo, m_snap_bitmap, entries, palette);
|
||||
if (error != util::png_error::NONE)
|
||||
osd_printf_error("Error generating PNG for snapshot: png_error = %d\n", std::underlying_type_t<util::png_error>(error));
|
||||
std::error_condition const error = util::png_write_bitmap(file, &pnginfo, m_snap_bitmap, entries, palette);
|
||||
if (error)
|
||||
osd_printf_error("Error generating PNG for snapshot (%s:%d %s)\n", error.category().name(), error.value(), error.message());
|
||||
}
|
||||
|
||||
|
||||
|
@ -238,8 +238,7 @@ void media_identifier::digest_file(std::vector<file_info> &info, char const *pat
|
||||
if (!util::core_file::open(path, OPEN_FLAG_READ, file))
|
||||
{
|
||||
jed_data jed;
|
||||
auto ptr = util::core_file_read(std::move(file));
|
||||
if (ptr && JEDERR_NONE == jed_parse(*ptr, &jed))
|
||||
if (JEDERR_NONE == jed_parse(*file, &jed))
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -261,30 +260,35 @@ void media_identifier::digest_file(std::vector<file_info> &info, char const *pat
|
||||
|
||||
// load the file and process if it opens and has a valid length
|
||||
util::core_file::ptr file;
|
||||
if (!util::core_file::open(path, OPEN_FLAG_READ, file) && file)
|
||||
{
|
||||
util::hash_collection hashes;
|
||||
hashes.begin(util::hash_collection::HASH_TYPES_CRC_SHA1);
|
||||
std::uint8_t buf[1024];
|
||||
for (std::uint64_t remaining = file->size(); remaining; )
|
||||
{
|
||||
std::uint32_t const block = std::min<std::uint64_t>(remaining, sizeof(buf));
|
||||
if (file->read(buf, block) < block)
|
||||
{
|
||||
osd_printf_error("%s: error reading file\n", path);
|
||||
return;
|
||||
}
|
||||
remaining -= block;
|
||||
hashes.buffer(buf, block);
|
||||
}
|
||||
hashes.end();
|
||||
info.emplace_back(path, file->size(), std::move(hashes), file_flavour::RAW);
|
||||
m_total++;
|
||||
}
|
||||
else
|
||||
if (util::core_file::open(path, OPEN_FLAG_READ, file) || !file)
|
||||
{
|
||||
osd_printf_error("%s: error opening file\n", path);
|
||||
return;
|
||||
}
|
||||
std::uint64_t length;
|
||||
if (file->length(length))
|
||||
{
|
||||
osd_printf_error("%s: error getting file length\n", path);
|
||||
return;
|
||||
}
|
||||
util::hash_collection hashes;
|
||||
hashes.begin(util::hash_collection::HASH_TYPES_CRC_SHA1);
|
||||
std::uint8_t buf[1024];
|
||||
for (std::uint64_t remaining = length; remaining; )
|
||||
{
|
||||
std::size_t const block = std::min<std::uint64_t>(remaining, sizeof(buf));
|
||||
std::size_t actual;
|
||||
if (file->read(buf, block, actual) || !actual)
|
||||
{
|
||||
osd_printf_error("%s: error reading file\n", path);
|
||||
return;
|
||||
}
|
||||
remaining -= actual;
|
||||
hashes.buffer(buf, actual);
|
||||
}
|
||||
hashes.end();
|
||||
info.emplace_back(path, length, std::move(hashes), file_flavour::RAW);
|
||||
m_total++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,10 +80,9 @@ bool load_ico_png(util::core_file &fp, icon_dir_entry_t const &dir, bitmap_argb3
|
||||
if (9U >= dir.size)
|
||||
return false;
|
||||
fp.seek(dir.offset, SEEK_SET);
|
||||
util::png_error const err(util::png_read_bitmap(fp, bitmap));
|
||||
switch (err)
|
||||
std::error_condition const err(util::png_read_bitmap(fp, bitmap));
|
||||
if (!err)
|
||||
{
|
||||
case util::png_error::NONE:
|
||||
// found valid PNG image
|
||||
assert(bitmap.valid());
|
||||
if ((dir.get_width() == bitmap.width()) && ((dir.get_height() == bitmap.height())))
|
||||
@ -100,16 +99,20 @@ bool load_ico_png(util::core_file &fp, icon_dir_entry_t const &dir, bitmap_argb3
|
||||
dir.get_height());
|
||||
}
|
||||
return true;
|
||||
|
||||
case util::png_error::BAD_SIGNATURE:
|
||||
}
|
||||
else if (util::png_error::BAD_SIGNATURE == err)
|
||||
{
|
||||
// doesn't look like PNG data - just fall back to DIB without the file header
|
||||
return false;
|
||||
|
||||
default:
|
||||
}
|
||||
else
|
||||
{
|
||||
// invalid PNG data or I/O error
|
||||
LOG(
|
||||
"Error %u reading PNG image data from ICO file at offset %u (directory size %u)\n",
|
||||
unsigned(err),
|
||||
"Error %s:%d %s reading PNG image data from ICO file at offset %u (directory size %u)\n",
|
||||
err.category().name(),
|
||||
err.value(),
|
||||
err.message(),
|
||||
dir.offset,
|
||||
dir.size);
|
||||
return false;
|
||||
@ -175,9 +178,13 @@ bool load_ico_image(util::core_file &fp, unsigned index, icon_dir_entry_t const
|
||||
bool load_ico_image(util::core_file &fp, unsigned count, unsigned index, bitmap_argb32 &bitmap)
|
||||
{
|
||||
// read the directory entry
|
||||
std::error_condition err;
|
||||
size_t actual;
|
||||
icon_dir_entry_t dir;
|
||||
fp.seek(sizeof(icon_dir_t) + (sizeof(icon_dir_entry_t) * index), SEEK_SET);
|
||||
if (fp.read(&dir, sizeof(dir)) != sizeof(dir))
|
||||
err = fp.seek(sizeof(icon_dir_t) + (sizeof(icon_dir_entry_t) * index), SEEK_SET);
|
||||
if (!err)
|
||||
err = fp.read(&dir, sizeof(dir), actual);
|
||||
if (err || (sizeof(dir) != actual))
|
||||
{
|
||||
LOG("Failed to read ICO file directory entry %u\n", index);
|
||||
return false;
|
||||
@ -204,9 +211,13 @@ bool load_ico_image(util::core_file &fp, unsigned count, unsigned index, bitmap_
|
||||
int images_in_ico(util::core_file &fp)
|
||||
{
|
||||
// read and check the icon file header
|
||||
std::error_condition err;
|
||||
size_t actual;
|
||||
icon_dir_t header;
|
||||
fp.seek(0, SEEK_SET);
|
||||
if (fp.read(&header, sizeof(header)) != sizeof(header))
|
||||
err = fp.seek(0, SEEK_SET);
|
||||
if (!err)
|
||||
err = fp.read(&header, sizeof(header), actual);
|
||||
if (err || (sizeof(header) != actual))
|
||||
{
|
||||
LOG("Failed to read ICO file header\n");
|
||||
return -1;
|
||||
@ -272,9 +283,10 @@ void render_load_ico_highest_detail(util::core_file &fp, bitmap_argb32 &bitmap)
|
||||
{
|
||||
// now load all the directory entries
|
||||
size_t const dir_bytes(sizeof(icon_dir_entry_t) * count);
|
||||
std::unique_ptr<icon_dir_entry_t []> dir(new icon_dir_entry_t [count]);
|
||||
std::unique_ptr<unsigned []> index(new unsigned [count]);
|
||||
if (count && (fp.read(dir.get(), dir_bytes) != dir_bytes))
|
||||
std::unique_ptr<icon_dir_entry_t []> dir(new (std::nothrow) icon_dir_entry_t [count]);
|
||||
std::unique_ptr<unsigned []> index(new (std::nothrow) unsigned [count]);
|
||||
size_t actual;
|
||||
if (count && (!dir || !index || fp.read(dir.get(), dir_bytes, actual) || (dir_bytes != actual)))
|
||||
{
|
||||
LOG("Failed to read ICO file directory entries\n");
|
||||
}
|
||||
|
@ -41,6 +41,8 @@
|
||||
|
||||
#include "cbm_crt.h"
|
||||
|
||||
#include "corefile.h"
|
||||
|
||||
#include "osdcore.h" // osd_printf_*
|
||||
|
||||
|
||||
@ -134,9 +136,10 @@ std::string cbm_crt_get_card(util::core_file &file)
|
||||
{
|
||||
// read the header
|
||||
cbm_crt_header header;
|
||||
file.read(&header, CRT_HEADER_LENGTH);
|
||||
size_t actual;
|
||||
std::error_condition err = file.read(&header, CRT_HEADER_LENGTH, actual);
|
||||
|
||||
if (memcmp(header.signature, CRT_SIGNATURE, 16) == 0)
|
||||
if (!err && (CRT_HEADER_LENGTH == actual) && !memcmp(header.signature, CRT_SIGNATURE, 16))
|
||||
{
|
||||
uint16_t hardware = pick_integer_be(header.hardware, 0, 2);
|
||||
|
||||
@ -153,11 +156,14 @@ std::string cbm_crt_get_card(util::core_file &file)
|
||||
|
||||
bool cbm_crt_read_header(util::core_file &file, size_t *roml_size, size_t *romh_size, int *exrom, int *game)
|
||||
{
|
||||
size_t actual;
|
||||
|
||||
// read the header
|
||||
cbm_crt_header header;
|
||||
file.read(&header, CRT_HEADER_LENGTH);
|
||||
if (file.read(&header, CRT_HEADER_LENGTH, actual))
|
||||
return false;
|
||||
|
||||
if (memcmp(header.signature, CRT_SIGNATURE, 16) != 0)
|
||||
if ((CRT_HEADER_LENGTH != actual) || (memcmp(header.signature, CRT_SIGNATURE, 16) != 0))
|
||||
return false;
|
||||
|
||||
uint16_t hardware = pick_integer_be(header.hardware, 0, 2);
|
||||
@ -177,11 +183,12 @@ bool cbm_crt_read_header(util::core_file &file, size_t *roml_size, size_t *romh_
|
||||
while (!file.eof())
|
||||
{
|
||||
cbm_crt_chip chip;
|
||||
file.read(&chip, CRT_CHIP_LENGTH);
|
||||
if (file.read(&chip, CRT_CHIP_LENGTH, actual) || (CRT_CHIP_LENGTH != actual))
|
||||
return false;
|
||||
|
||||
uint16_t address = pick_integer_be(chip.start_address, 0, 2);
|
||||
uint16_t size = pick_integer_be(chip.image_size, 0, 2);
|
||||
uint16_t type = pick_integer_be(chip.chip_type, 0, 2);
|
||||
const uint16_t address = pick_integer_be(chip.start_address, 0, 2);
|
||||
const uint16_t size = pick_integer_be(chip.image_size, 0, 2);
|
||||
const uint16_t type = pick_integer_be(chip.chip_type, 0, 2);
|
||||
|
||||
if (LOG)
|
||||
{
|
||||
@ -198,7 +205,8 @@ bool cbm_crt_read_header(util::core_file &file, size_t *roml_size, size_t *romh_
|
||||
default: osd_printf_verbose("Invalid CHIP loading address!\n"); break;
|
||||
}
|
||||
|
||||
file.seek(size, SEEK_CUR);
|
||||
if (file.seek(size, SEEK_CUR))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -211,25 +219,33 @@ bool cbm_crt_read_header(util::core_file &file, size_t *roml_size, size_t *romh_
|
||||
|
||||
bool cbm_crt_read_data(util::core_file &file, uint8_t *roml, uint8_t *romh)
|
||||
{
|
||||
if (file.seek(CRT_HEADER_LENGTH, SEEK_SET))
|
||||
return false;
|
||||
|
||||
uint32_t roml_offset = 0;
|
||||
uint32_t romh_offset = 0;
|
||||
|
||||
file.seek(CRT_HEADER_LENGTH, SEEK_SET);
|
||||
|
||||
while (!file.eof())
|
||||
{
|
||||
size_t actual;
|
||||
|
||||
cbm_crt_chip chip;
|
||||
file.read(&chip, CRT_CHIP_LENGTH);
|
||||
if (file.read(&chip, CRT_CHIP_LENGTH, actual) || (CRT_CHIP_LENGTH != actual))
|
||||
return false;
|
||||
|
||||
uint16_t address = pick_integer_be(chip.start_address, 0, 2);
|
||||
uint16_t size = pick_integer_be(chip.image_size, 0, 2);
|
||||
const uint16_t address = pick_integer_be(chip.start_address, 0, 2);
|
||||
const uint16_t size = pick_integer_be(chip.image_size, 0, 2);
|
||||
|
||||
std::error_condition err;
|
||||
switch (address)
|
||||
{
|
||||
case 0x8000: file.read(roml + roml_offset, size); roml_offset += size; break;
|
||||
case 0xa000: file.read(romh + romh_offset, size); romh_offset += size; break;
|
||||
case 0xe000: file.read(romh + romh_offset, size); romh_offset += size; break;
|
||||
case 0x8000: err = file.read(roml + roml_offset, size, actual); roml_offset += size; break;
|
||||
case 0xa000: err = file.read(romh + romh_offset, size, actual); romh_offset += size; break;
|
||||
case 0xe000: err = file.read(romh + romh_offset, size, actual); romh_offset += size; break;
|
||||
// FIXME: surely one needs to report an error or skip over the data if the load address is not recognised?
|
||||
}
|
||||
if (err) // TODO: check size - all bets are off if the address isn't recognised anyway
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
#include "formats/imageutl.h"
|
||||
|
||||
#include "corefile.h"
|
||||
#include "utilfwd.h"
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
|
117
src/lib/util/abi.h
Normal file
117
src/lib/util/abi.h
Normal file
@ -0,0 +1,117 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Vas Crabb
|
||||
/// \file
|
||||
/// \brief ABI feature macros
|
||||
///
|
||||
/// Macros that are useful for writing ABI-dependent code.
|
||||
#ifndef MAME_LIB_UTIL_ABI_H
|
||||
#define MAME_LIB_UTIL_ABI_H
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
/// \brief Itanium C++ ABI
|
||||
///
|
||||
/// Value of #MAME_ABI_CXX_TYPE when compiled with a variant of the
|
||||
/// Itanium C++ ABI.
|
||||
/// \sa MAME_ABI_CXX_TYPE MAME_ABI_CXX_MSVC
|
||||
#define MAME_ABI_CXX_ITANIUM 0
|
||||
|
||||
/// \brief Microsoft Visual C++ ABI
|
||||
///
|
||||
/// Value of #MAME_ABI_CXX_TYPE when compiled with a variant of the
|
||||
/// Microsoft Visual C++ ABI.
|
||||
/// \sa MAME_ABI_CXX_TYPE MAME_ABI_CXX_ITANIUM
|
||||
#define MAME_ABI_CXX_MSVC 1
|
||||
|
||||
|
||||
/// \brief Standard Itanium C++ ABI member function pointers
|
||||
///
|
||||
/// Value of #MAME_ABI_CXX_ITANIUM_MFP_TYPE when compiled with a variant
|
||||
/// of the Itanium C++ ABI using the standard representation of
|
||||
/// pointers to non-static member functions.
|
||||
/// \sa MAME_ABI_CXX_ITANIUM_MFP_TYPE MAME_ABI_CXX_ITANIUM_MFP_ARM
|
||||
#define MAME_ABI_CXX_ITANIUM_MFP_STANDARD 0
|
||||
|
||||
/// \brief ARM Itanium C++ ABI member function pointers
|
||||
///
|
||||
/// Value of #MAME_ABI_CXX_ITANIUM_MFP_TYPE when compiled with a variant
|
||||
/// of the Itanium C++ ABI using the 32-bit ARM representation of
|
||||
/// pointers to non-static member functions.
|
||||
/// \sa MAME_ABI_CXX_ITANIUM_MFP_TYPE MAME_ABI_CXX_ITANIUM_MFP_STANDARD
|
||||
#define MAME_ABI_CXX_ITANIUM_MFP_ARM 1
|
||||
|
||||
|
||||
/// \def MAME_ABI_FNDESC_SIZE
|
||||
/// \brief Size of function descriptors
|
||||
///
|
||||
/// Size of function descriptors as a multiple of the size of a pointer,
|
||||
/// or zero if function pointers point to the function entry point
|
||||
/// directly.
|
||||
#if (defined(__ppc64__) || defined(__PPC64__)) && !defined(__APPLE__) && !defined(__LITTLE_ENDIAN__)
|
||||
#define MAME_ABI_FNDESC_SIZE 3 // entry point (PC), TOC (R2), environment (R11)
|
||||
#elif defined(__ia64__)
|
||||
#define MAME_ABI_FNDESC_SIZE 2 // GP, entry point
|
||||
#else
|
||||
#define MAME_ABI_FNDESC_SIZE 0 // function pointers point to entry point directly
|
||||
#endif
|
||||
|
||||
|
||||
/// \def MAME_ABI_CXX_TYPE
|
||||
/// \brief C++ ABI type
|
||||
///
|
||||
/// A constant representing the C++ ABI.
|
||||
/// \sa MAME_ABI_CXX_ITANIUM MAME_ABI_CXX_MSVC
|
||||
#if defined(_MSC_VER)
|
||||
#define MAME_ABI_CXX_TYPE MAME_ABI_CXX_MSVC
|
||||
#else
|
||||
#define MAME_ABI_CXX_TYPE MAME_ABI_CXX_ITANIUM
|
||||
#endif
|
||||
|
||||
|
||||
/// \def MAME_ABI_CXX_MEMBER_CALL
|
||||
/// \brief Member function calling convention qualifier
|
||||
///
|
||||
/// A qualifier for functions and function pointers that may be used to
|
||||
/// specify that the calling convention for non-static member functions
|
||||
/// should be used.
|
||||
#if defined(__GNUC__) && defined(__MINGW32__) && !defined(__x86_64__) && defined(__i386__)
|
||||
#define MAME_ABI_CXX_MEMBER_CALL __thiscall
|
||||
#else
|
||||
#define MAME_ABI_CXX_MEMBER_CALL
|
||||
#endif
|
||||
|
||||
|
||||
/// \def MAME_ABI_CXX_VTABLE_FNDESC
|
||||
/// \brief Whether function descriptors are stored in virtual tables
|
||||
///
|
||||
/// Non-zero if function descriptors are stored in virtual tables
|
||||
/// directly, or zero if function entries in virtual tables are
|
||||
/// conventional function pointers.
|
||||
/// \sa MAME_ABI_FNDESC_SIZE
|
||||
#if defined(__ia64__)
|
||||
#define MAME_ABI_CXX_VTABLE_FNDESC 1 // function descriptors stored directly in vtable
|
||||
#else
|
||||
#define MAME_ABI_CXX_VTABLE_FNDESC 0 // conventional function pointers in vtable
|
||||
#endif
|
||||
|
||||
|
||||
/// \def MAME_ABI_CXX_ITANIUM_MFP_TYPE
|
||||
/// Itanium C++ member function representation
|
||||
///
|
||||
/// A constant representing the representation of pointers to non-static
|
||||
/// member functions in use with the Itanium C++ ABI. Only valid if
|
||||
/// compiled with a variant of the Itanium C++ ABI.
|
||||
/// \sa MAME_ABI_CXX_ITANIUM_MFP_STANDARD MAME_ABI_CXX_ITANIUM_MFP_ARM
|
||||
/// MAME_ABI_CXX_TYPE
|
||||
#if defined(__arm__) || defined(__ARMEL__) || defined(__aarch64__)
|
||||
#define MAME_ABI_CXX_ITANIUM_MFP_TYPE MAME_ABI_CXX_ITANIUM_MFP_ARM
|
||||
#elif defined(__MIPSEL__) || defined(__mips_isa_rev) || defined(__mips64)
|
||||
#define MAME_ABI_CXX_ITANIUM_MFP_TYPE MAME_ABI_CXX_ITANIUM_MFP_ARM
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
#define MAME_ABI_CXX_ITANIUM_MFP_TYPE MAME_ABI_CXX_ITANIUM_MFP_ARM
|
||||
#else
|
||||
#define MAME_ABI_CXX_ITANIUM_MFP_TYPE MAME_ABI_CXX_ITANIUM_MFP_STANDARD
|
||||
#endif
|
||||
|
||||
#endif // MAME_LIB_UTIL_ABI_H
|
@ -510,8 +510,11 @@ std::error_condition read_partial_sector(cdrom_file *file, void *dest, uint32_t
|
||||
|
||||
// printf("Reading sector %d from track %d at offset %lu\n", chdsector, tracknum, sourcefileoffset);
|
||||
|
||||
srcfile.seek(sourcefileoffset, SEEK_SET);
|
||||
srcfile.read(dest, length);
|
||||
size_t actual;
|
||||
result = srcfile.seek(sourcefileoffset, SEEK_SET);
|
||||
if (!result)
|
||||
result = srcfile.read(dest, length, actual);
|
||||
// FIXME: if (actual < length) report error
|
||||
|
||||
needswap = file->track_info.track[tracknum].swap;
|
||||
}
|
||||
|
@ -702,17 +702,14 @@ std::error_condition chd_file::create(std::string_view filename, uint64_t logica
|
||||
std::error_condition filerr = util::core_file::open(filename, OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, file);
|
||||
if (filerr)
|
||||
return filerr;
|
||||
util::random_read_write::ptr io = util::core_file_read_write(std::move(file));
|
||||
if (!io)
|
||||
return std::errc::not_enough_memory;
|
||||
|
||||
// create the file normally, then claim the file
|
||||
std::error_condition chderr = create(std::move(io), logicalbytes, hunkbytes, unitbytes, compression);
|
||||
std::error_condition chderr = create(std::move(file), logicalbytes, hunkbytes, unitbytes, compression);
|
||||
|
||||
// if an error happened, close and delete the file
|
||||
if (chderr)
|
||||
{
|
||||
io.reset();
|
||||
file.reset();
|
||||
osd_file::remove(std::string(filename)); // FIXME: allow osd_file to use std::string_view
|
||||
}
|
||||
return chderr;
|
||||
@ -745,17 +742,14 @@ std::error_condition chd_file::create(std::string_view filename, uint64_t logica
|
||||
std::error_condition filerr = util::core_file::open(filename, OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, file);
|
||||
if (filerr)
|
||||
return filerr;
|
||||
util::random_read_write::ptr io = util::core_file_read_write(std::move(file));
|
||||
if (!io)
|
||||
return std::errc::not_enough_memory;
|
||||
|
||||
// create the file normally, then claim the file
|
||||
std::error_condition chderr = create(std::move(io), logicalbytes, hunkbytes, compression, parent);
|
||||
std::error_condition chderr = create(std::move(file), logicalbytes, hunkbytes, compression, parent);
|
||||
|
||||
// if an error happened, close and delete the file
|
||||
if (chderr)
|
||||
{
|
||||
io.reset();
|
||||
file.reset();
|
||||
osd_file::remove(std::string(filename)); // FIXME: allow osd_file to use std::string_view
|
||||
}
|
||||
return chderr;
|
||||
@ -787,12 +781,9 @@ std::error_condition chd_file::open(std::string_view filename, bool writeable, c
|
||||
std::error_condition filerr = util::core_file::open(filename, openflags, file);
|
||||
if (filerr)
|
||||
return filerr;
|
||||
util::random_read_write::ptr io = util::core_file_read_write(std::move(file));
|
||||
if (!io)
|
||||
return std::errc::not_enough_memory;
|
||||
|
||||
// now open the CHD
|
||||
std::error_condition err = open(std::move(io), writeable, parent);
|
||||
std::error_condition err = open(std::move(file), writeable, parent);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
|
||||
|
||||
namespace util {
|
||||
@ -45,26 +46,30 @@ class core_proxy_file : public core_file
|
||||
{
|
||||
public:
|
||||
core_proxy_file(core_file &file) noexcept : m_file(file) { }
|
||||
virtual ~core_proxy_file() override { }
|
||||
|
||||
virtual int seek(std::int64_t offset, int whence) override { return m_file.seek(offset, whence); }
|
||||
virtual std::uint64_t tell() const override { return m_file.tell(); }
|
||||
virtual std::error_condition seek(std::int64_t offset, int whence) noexcept override { return m_file.seek(offset, whence); }
|
||||
virtual std::error_condition tell(std::uint64_t &result) noexcept override { return m_file.tell(result); }
|
||||
virtual std::error_condition length(std::uint64_t &result) noexcept override { return m_file.length(result); }
|
||||
|
||||
virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override { return m_file.read(buffer, length, actual); }
|
||||
virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override { return m_file.read_at(offset, buffer, length, actual); }
|
||||
|
||||
virtual std::error_condition finalize() noexcept override { return m_file.finalize(); }
|
||||
virtual std::error_condition flush() noexcept override { return m_file.flush(); }
|
||||
virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override { return m_file.write(buffer, length, actual); }
|
||||
virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override { return m_file.write_at(offset, buffer, length, actual); }
|
||||
|
||||
virtual bool eof() const override { return m_file.eof(); }
|
||||
virtual std::uint64_t size() const override { return m_file.size(); }
|
||||
|
||||
virtual std::uint32_t read(void *buffer, std::uint32_t length) override { return m_file.read(buffer, length); }
|
||||
virtual int getc() override { return m_file.getc(); }
|
||||
virtual int ungetc(int c) override { return m_file.ungetc(c); }
|
||||
virtual char *gets(char *s, int n) override { return m_file.gets(s, n); }
|
||||
virtual const void *buffer() override { return m_file.buffer(); }
|
||||
virtual void const *buffer() override { return m_file.buffer(); }
|
||||
|
||||
virtual std::uint32_t write(const void *buffer, std::uint32_t length) override { return m_file.write(buffer, length); }
|
||||
virtual int puts(std::string_view s) override { return m_file.puts(s); }
|
||||
virtual int vprintf(util::format_argument_pack<std::ostream> const &args) override { return m_file.vprintf(args); }
|
||||
virtual std::error_condition truncate(std::uint64_t offset) override { return m_file.truncate(offset); }
|
||||
|
||||
virtual std::error_condition flush() override { return m_file.flush(); }
|
||||
|
||||
private:
|
||||
core_file &m_file;
|
||||
};
|
||||
@ -99,12 +104,12 @@ protected:
|
||||
{
|
||||
}
|
||||
|
||||
bool read_access() const { return 0U != (m_openflags & OPEN_FLAG_READ); }
|
||||
bool write_access() const { return 0U != (m_openflags & OPEN_FLAG_WRITE); }
|
||||
bool no_bom() const { return 0U != (m_openflags & OPEN_FLAG_NO_BOM); }
|
||||
bool read_access() const noexcept { return 0U != (m_openflags & OPEN_FLAG_READ); }
|
||||
bool write_access() const noexcept { return 0U != (m_openflags & OPEN_FLAG_WRITE); }
|
||||
bool no_bom() const noexcept { return 0U != (m_openflags & OPEN_FLAG_NO_BOM); }
|
||||
|
||||
bool has_putback() const { return m_back_char_head != m_back_char_tail; }
|
||||
void clear_putback() { m_back_char_head = m_back_char_tail = 0; }
|
||||
bool has_putback() const noexcept { return m_back_char_head != m_back_char_tail; }
|
||||
void clear_putback() noexcept { m_back_char_head = m_back_char_tail = 0; }
|
||||
|
||||
private:
|
||||
std::uint32_t const m_openflags; // flags we were opened with
|
||||
@ -119,7 +124,7 @@ private:
|
||||
class core_in_memory_file : public core_text_file
|
||||
{
|
||||
public:
|
||||
core_in_memory_file(std::uint32_t openflags, void const *data, std::size_t length, bool copy)
|
||||
core_in_memory_file(std::uint32_t openflags, void const *data, std::size_t length, bool copy) noexcept
|
||||
: core_text_file(openflags)
|
||||
, m_data_allocated(false)
|
||||
, m_data(copy ? nullptr : data)
|
||||
@ -136,20 +141,26 @@ public:
|
||||
|
||||
~core_in_memory_file() override { purge(); }
|
||||
|
||||
virtual int seek(std::int64_t offset, int whence) override;
|
||||
virtual std::uint64_t tell() const override { return m_offset; }
|
||||
virtual bool eof() const override;
|
||||
virtual std::uint64_t size() const override { return m_length; }
|
||||
virtual std::error_condition seek(std::int64_t offset, int whence) noexcept override;
|
||||
virtual std::error_condition tell(std::uint64_t &result) noexcept override { result = m_offset; return std::error_condition(); }
|
||||
virtual std::error_condition length(std::uint64_t &result) noexcept override { result = m_length; return std::error_condition(); }
|
||||
|
||||
virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override;
|
||||
virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override;
|
||||
|
||||
virtual std::error_condition finalize() noexcept override { return std::error_condition(); }
|
||||
virtual std::error_condition flush() noexcept override { clear_putback(); return std::error_condition(); }
|
||||
virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override { actual = 0; return std::errc::bad_file_descriptor; }
|
||||
virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override { actual = 0; return std::errc::bad_file_descriptor; }
|
||||
|
||||
virtual bool eof() const override;
|
||||
|
||||
virtual std::uint32_t read(void *buffer, std::uint32_t length) override;
|
||||
virtual void const *buffer() override { return m_data; }
|
||||
|
||||
virtual std::uint32_t write(void const *buffer, std::uint32_t length) override { return 0; }
|
||||
virtual std::error_condition truncate(std::uint64_t offset) override;
|
||||
virtual std::error_condition flush() override { clear_putback(); return std::error_condition(); }
|
||||
|
||||
protected:
|
||||
core_in_memory_file(std::uint32_t openflags, std::uint64_t length)
|
||||
core_in_memory_file(std::uint32_t openflags, std::uint64_t length) noexcept
|
||||
: core_text_file(openflags)
|
||||
, m_data_allocated(false)
|
||||
, m_data(nullptr)
|
||||
@ -158,10 +169,11 @@ protected:
|
||||
{
|
||||
}
|
||||
|
||||
bool is_loaded() const { return nullptr != m_data; }
|
||||
void *allocate()
|
||||
bool is_loaded() const noexcept { return nullptr != m_data; }
|
||||
|
||||
void *allocate() noexcept
|
||||
{
|
||||
if (m_data)
|
||||
if (m_data || (std::numeric_limits<std::size_t>::max() < m_length))
|
||||
return nullptr;
|
||||
void *data = malloc(m_length);
|
||||
if (data)
|
||||
@ -171,7 +183,8 @@ protected:
|
||||
}
|
||||
return data;
|
||||
}
|
||||
void purge()
|
||||
|
||||
void purge() noexcept
|
||||
{
|
||||
if (m_data && m_data_allocated)
|
||||
free(const_cast<void *>(m_data));
|
||||
@ -179,14 +192,14 @@ protected:
|
||||
m_data = nullptr;
|
||||
}
|
||||
|
||||
std::uint64_t offset() const { return m_offset; }
|
||||
void add_offset(std::uint32_t increment) { m_offset += increment; m_length = (std::max)(m_length, m_offset); }
|
||||
std::uint64_t length() const { return m_length; }
|
||||
void set_length(std::uint64_t value) { m_length = value; m_offset = (std::min)(m_offset, m_length); }
|
||||
std::uint64_t offset() const noexcept { return m_offset; }
|
||||
void add_offset(std::size_t increment) noexcept { m_offset += increment; m_length = (std::max)(m_length, m_offset); }
|
||||
std::uint64_t length() const noexcept { return m_length; }
|
||||
void set_length(std::uint64_t value) noexcept { m_length = value; }
|
||||
|
||||
static std::size_t safe_buffer_copy(
|
||||
void const *source, std::size_t sourceoffs, std::size_t sourcelen,
|
||||
void *dest, std::size_t destoffs, std::size_t destlen);
|
||||
void *dest, std::size_t destoffs, std::size_t destlen) noexcept;
|
||||
|
||||
private:
|
||||
bool m_data_allocated; // was the data allocated by us?
|
||||
@ -196,35 +209,38 @@ private:
|
||||
};
|
||||
|
||||
|
||||
class core_osd_file : public core_in_memory_file
|
||||
class core_osd_file final : public core_in_memory_file
|
||||
{
|
||||
public:
|
||||
core_osd_file(std::uint32_t openmode, osd_file::ptr &&file, std::uint64_t length)
|
||||
: core_in_memory_file(openmode, length)
|
||||
, m_file(std::move(file))
|
||||
, m_bufferbase(0)
|
||||
, m_bufferbytes(0)
|
||||
{
|
||||
}
|
||||
~core_osd_file() override;
|
||||
|
||||
virtual std::uint32_t read(void *buffer, std::uint32_t length) override;
|
||||
virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override;
|
||||
virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override;
|
||||
|
||||
virtual std::error_condition finalize() noexcept override;
|
||||
virtual std::error_condition flush() noexcept override;
|
||||
virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override;
|
||||
virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override;
|
||||
|
||||
virtual void const *buffer() override;
|
||||
|
||||
virtual std::uint32_t write(void const *buffer, std::uint32_t length) override;
|
||||
virtual std::error_condition truncate(std::uint64_t offset) override;
|
||||
virtual std::error_condition flush() override;
|
||||
|
||||
protected:
|
||||
|
||||
bool is_buffered() const { return (offset() >= m_bufferbase) && (offset() < (m_bufferbase + m_bufferbytes)); }
|
||||
bool is_buffered(std::uint64_t offset) const noexcept { return (offset >= m_bufferbase) && (offset < (m_bufferbase + m_bufferbytes)); }
|
||||
|
||||
private:
|
||||
static constexpr std::size_t FILE_BUFFER_SIZE = 512;
|
||||
|
||||
osd_file::ptr m_file; // OSD file handle
|
||||
std::uint64_t m_bufferbase; // base offset of internal buffer
|
||||
std::uint32_t m_bufferbytes; // bytes currently loaded into buffer
|
||||
std::uint64_t m_bufferbase = 0U; // base offset of internal buffer
|
||||
std::uint32_t m_bufferbytes = 0U; // bytes currently loaded into buffer
|
||||
std::uint8_t m_buffer[FILE_BUFFER_SIZE]; // buffer data
|
||||
};
|
||||
|
||||
@ -240,54 +256,57 @@ private:
|
||||
|
||||
int core_text_file::getc()
|
||||
{
|
||||
int result;
|
||||
|
||||
// refresh buffer, if necessary
|
||||
if (m_back_char_head == m_back_char_tail)
|
||||
{
|
||||
// do we need to check the byte order marks?
|
||||
if (tell() == 0)
|
||||
std::uint64_t pos;
|
||||
if (!tell(pos))
|
||||
{
|
||||
std::uint8_t bom[4];
|
||||
int pos = 0;
|
||||
|
||||
if (read(bom, 4) == 4)
|
||||
if (!pos)
|
||||
{
|
||||
if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf)
|
||||
std::size_t readlen;
|
||||
std::uint8_t bom[4];
|
||||
read(bom, 4, readlen);
|
||||
if (readlen == 4)
|
||||
{
|
||||
m_text_type = text_file_type::UTF8;
|
||||
pos = 3;
|
||||
}
|
||||
else if (bom[0] == 0x00 && bom[1] == 0x00 && bom[2] == 0xfe && bom[3] == 0xff)
|
||||
{
|
||||
m_text_type = text_file_type::UTF32BE;
|
||||
pos = 4;
|
||||
}
|
||||
else if (bom[0] == 0xff && bom[1] == 0xfe && bom[2] == 0x00 && bom[3] == 0x00)
|
||||
{
|
||||
m_text_type = text_file_type::UTF32LE;
|
||||
pos = 4;
|
||||
}
|
||||
else if (bom[0] == 0xfe && bom[1] == 0xff)
|
||||
{
|
||||
m_text_type = text_file_type::UTF16BE;
|
||||
pos = 2;
|
||||
}
|
||||
else if (bom[0] == 0xff && bom[1] == 0xfe)
|
||||
{
|
||||
m_text_type = text_file_type::UTF16LE;
|
||||
pos = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_text_type = text_file_type::OSD;
|
||||
pos = 0;
|
||||
if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf)
|
||||
{
|
||||
m_text_type = text_file_type::UTF8;
|
||||
pos = 3;
|
||||
}
|
||||
else if (bom[0] == 0x00 && bom[1] == 0x00 && bom[2] == 0xfe && bom[3] == 0xff)
|
||||
{
|
||||
m_text_type = text_file_type::UTF32BE;
|
||||
pos = 4;
|
||||
}
|
||||
else if (bom[0] == 0xff && bom[1] == 0xfe && bom[2] == 0x00 && bom[3] == 0x00)
|
||||
{
|
||||
m_text_type = text_file_type::UTF32LE;
|
||||
pos = 4;
|
||||
}
|
||||
else if (bom[0] == 0xfe && bom[1] == 0xff)
|
||||
{
|
||||
m_text_type = text_file_type::UTF16BE;
|
||||
pos = 2;
|
||||
}
|
||||
else if (bom[0] == 0xff && bom[1] == 0xfe)
|
||||
{
|
||||
m_text_type = text_file_type::UTF16LE;
|
||||
pos = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_text_type = text_file_type::OSD;
|
||||
pos = 0;
|
||||
}
|
||||
}
|
||||
seek(pos, SEEK_SET); // FIXME: don't assume seeking is possible, check for errors
|
||||
}
|
||||
seek(pos, SEEK_SET);
|
||||
}
|
||||
|
||||
// fetch the next character
|
||||
// FIXME: all of this plays fast and loose with error checking and seeks backwards far too frequently
|
||||
char16_t utf16_buffer[UTF16_CHAR_MAX];
|
||||
auto uchar = char32_t(~0);
|
||||
switch (m_text_type)
|
||||
@ -296,7 +315,8 @@ int core_text_file::getc()
|
||||
case text_file_type::OSD:
|
||||
{
|
||||
char default_buffer[16];
|
||||
auto const readlen = read(default_buffer, sizeof(default_buffer));
|
||||
std::size_t readlen;
|
||||
read(default_buffer, sizeof(default_buffer), readlen);
|
||||
if (readlen > 0)
|
||||
{
|
||||
auto const charlen = osd_uchar_from_osdchar(&uchar, default_buffer, readlen / sizeof(default_buffer[0]));
|
||||
@ -308,7 +328,8 @@ int core_text_file::getc()
|
||||
case text_file_type::UTF8:
|
||||
{
|
||||
char utf8_buffer[UTF8_CHAR_MAX];
|
||||
auto const readlen = read(utf8_buffer, sizeof(utf8_buffer));
|
||||
std::size_t readlen;
|
||||
read(utf8_buffer, sizeof(utf8_buffer), readlen);
|
||||
if (readlen > 0)
|
||||
{
|
||||
auto const charlen = uchar_from_utf8(&uchar, utf8_buffer, readlen / sizeof(utf8_buffer[0]));
|
||||
@ -319,7 +340,8 @@ int core_text_file::getc()
|
||||
|
||||
case text_file_type::UTF16BE:
|
||||
{
|
||||
auto const readlen = read(utf16_buffer, sizeof(utf16_buffer));
|
||||
std::size_t readlen;
|
||||
read(utf16_buffer, sizeof(utf16_buffer), readlen);
|
||||
if (readlen > 0)
|
||||
{
|
||||
auto const charlen = uchar_from_utf16be(&uchar, utf16_buffer, readlen / sizeof(utf16_buffer[0]));
|
||||
@ -330,7 +352,8 @@ int core_text_file::getc()
|
||||
|
||||
case text_file_type::UTF16LE:
|
||||
{
|
||||
auto const readlen = read(utf16_buffer, sizeof(utf16_buffer));
|
||||
std::size_t readlen;
|
||||
read(utf16_buffer, sizeof(utf16_buffer), readlen);
|
||||
if (readlen > 0)
|
||||
{
|
||||
auto const charlen = uchar_from_utf16le(&uchar, utf16_buffer, readlen / sizeof(utf16_buffer[0]));
|
||||
@ -340,13 +363,23 @@ int core_text_file::getc()
|
||||
break;
|
||||
|
||||
case text_file_type::UTF32BE:
|
||||
if (read(&uchar, sizeof(uchar)) == sizeof(uchar))
|
||||
uchar = big_endianize_int32(uchar);
|
||||
{
|
||||
// FIXME: deal with read returning short
|
||||
std::size_t readlen;
|
||||
read(&uchar, sizeof(uchar), readlen);
|
||||
if (sizeof(uchar) == readlen)
|
||||
uchar = big_endianize_int32(uchar);
|
||||
}
|
||||
break;
|
||||
|
||||
case text_file_type::UTF32LE:
|
||||
if (read(&uchar, sizeof(uchar)) == sizeof(uchar))
|
||||
uchar = little_endianize_int32(uchar);
|
||||
{
|
||||
// FIXME: deal with read returning short
|
||||
std::size_t readlen;
|
||||
read(&uchar, sizeof(uchar), readlen);
|
||||
if (sizeof(uchar) == readlen)
|
||||
uchar = little_endianize_int32(uchar);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -360,6 +393,7 @@ int core_text_file::getc()
|
||||
}
|
||||
|
||||
// now read from the ring buffer
|
||||
int result;
|
||||
if (m_back_char_head == m_back_char_tail)
|
||||
result = EOF;
|
||||
else
|
||||
@ -433,21 +467,32 @@ char *core_text_file::gets(char *s, int n)
|
||||
|
||||
|
||||
/*-------------------------------------------------
|
||||
puts - write a line to a text file
|
||||
puts - write a string to a text file
|
||||
-------------------------------------------------*/
|
||||
|
||||
int core_text_file::puts(std::string_view s)
|
||||
{
|
||||
// TODO: what to do about write errors or short writes (interrupted)?
|
||||
// The API doesn't lend itself to reporting the error as the return
|
||||
// value includes extra bytes inserted like the UTF-8 marker and
|
||||
// carriage returns.
|
||||
char convbuf[1024];
|
||||
char *pconvbuf = convbuf;
|
||||
int count = 0;
|
||||
|
||||
// is this the beginning of the file? if so, write a byte order mark
|
||||
if (tell() == 0 && !no_bom())
|
||||
if (!no_bom())
|
||||
{
|
||||
*pconvbuf++ = char(0xef);
|
||||
*pconvbuf++ = char(0xbb);
|
||||
*pconvbuf++ = char(0xbf);
|
||||
std::uint64_t offset;
|
||||
if (!tell(offset))
|
||||
{
|
||||
if (!offset)
|
||||
{
|
||||
*pconvbuf++ = char(0xef);
|
||||
*pconvbuf++ = char(0xbb);
|
||||
*pconvbuf++ = char(0xbf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// convert '\n' to platform dependant line endings
|
||||
@ -471,14 +516,20 @@ int core_text_file::puts(std::string_view s)
|
||||
// if we overflow, break into chunks
|
||||
if (pconvbuf >= convbuf + std::size(convbuf) - 10)
|
||||
{
|
||||
count += write(convbuf, pconvbuf - convbuf);
|
||||
std::size_t written;
|
||||
write(convbuf, pconvbuf - convbuf, written); // FIXME: error ignored here
|
||||
count += written;
|
||||
pconvbuf = convbuf;
|
||||
}
|
||||
}
|
||||
|
||||
// final flush
|
||||
if (pconvbuf != convbuf)
|
||||
count += write(convbuf, pconvbuf - convbuf);
|
||||
{
|
||||
std::size_t written;
|
||||
write(convbuf, pconvbuf - convbuf, written); // FIXME: error ignored here
|
||||
count += written;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
@ -507,27 +558,49 @@ int core_text_file::vprintf(util::format_argument_pack<std::ostream> const &args
|
||||
seek - seek within a file
|
||||
-------------------------------------------------*/
|
||||
|
||||
int core_in_memory_file::seek(std::int64_t offset, int whence)
|
||||
std::error_condition core_in_memory_file::seek(std::int64_t offset, int whence) noexcept
|
||||
{
|
||||
// flush any buffered char
|
||||
clear_putback();
|
||||
clear_putback(); // TODO: report errors; also, should the argument check happen before this?
|
||||
|
||||
// switch off the relative location
|
||||
switch (whence)
|
||||
{
|
||||
case SEEK_SET:
|
||||
if (0 > offset)
|
||||
return std::errc::invalid_argument;
|
||||
m_offset = offset;
|
||||
break;
|
||||
return std::error_condition();
|
||||
|
||||
case SEEK_CUR:
|
||||
if (0 > offset)
|
||||
{
|
||||
if (std::uint64_t(-offset) > m_offset)
|
||||
return std::errc::invalid_argument;
|
||||
}
|
||||
else if ((std::numeric_limits<std::uint64_t>::max() - offset) < m_offset)
|
||||
{
|
||||
return std::errc::invalid_argument;
|
||||
}
|
||||
m_offset += offset;
|
||||
break;
|
||||
return std::error_condition();
|
||||
|
||||
case SEEK_END:
|
||||
if (0 > offset)
|
||||
{
|
||||
if (std::uint64_t(-offset) > m_length)
|
||||
return std::errc::invalid_argument;
|
||||
}
|
||||
else if ((std::numeric_limits<std::uint64_t>::max() - offset) < m_length)
|
||||
{
|
||||
return std::errc::invalid_argument;
|
||||
}
|
||||
m_offset = m_length + offset;
|
||||
break;
|
||||
return std::error_condition();
|
||||
|
||||
default:
|
||||
return std::errc::invalid_argument;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -550,14 +623,29 @@ bool core_in_memory_file::eof() const
|
||||
read - read from a file
|
||||
-------------------------------------------------*/
|
||||
|
||||
std::uint32_t core_in_memory_file::read(void *buffer, std::uint32_t length)
|
||||
std::error_condition core_in_memory_file::read(void *buffer, std::size_t length, std::size_t &actual) noexcept
|
||||
{
|
||||
clear_putback();
|
||||
|
||||
// handle RAM-based files
|
||||
auto const bytes_read = safe_buffer_copy(m_data, std::size_t(m_offset), std::size_t(m_length), buffer, 0, length);
|
||||
m_offset += bytes_read;
|
||||
return bytes_read;
|
||||
if (m_offset < m_length)
|
||||
actual = safe_buffer_copy(m_data, std::size_t(m_offset), std::size_t(m_length), buffer, 0, length);
|
||||
else
|
||||
actual = 0U;
|
||||
m_offset += actual;
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
std::error_condition core_in_memory_file::read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept
|
||||
{
|
||||
clear_putback();
|
||||
|
||||
// handle RAM-based files
|
||||
if (offset < m_length)
|
||||
actual = safe_buffer_copy(m_data, std::size_t(offset), std::size_t(m_length), buffer, 0, length);
|
||||
else
|
||||
actual = 0U;
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
|
||||
@ -583,7 +671,7 @@ std::error_condition core_in_memory_file::truncate(std::uint64_t offset)
|
||||
|
||||
std::size_t core_in_memory_file::safe_buffer_copy(
|
||||
void const *source, std::size_t sourceoffs, std::size_t sourcelen,
|
||||
void *dest, std::size_t destoffs, std::size_t destlen)
|
||||
void *dest, std::size_t destoffs, std::size_t destlen) noexcept
|
||||
{
|
||||
auto const sourceavail = sourcelen - sourceoffs;
|
||||
auto const destavail = destlen - destoffs;
|
||||
@ -618,45 +706,65 @@ core_osd_file::~core_osd_file()
|
||||
read - read from a file
|
||||
-------------------------------------------------*/
|
||||
|
||||
std::uint32_t core_osd_file::read(void *buffer, std::uint32_t length)
|
||||
std::error_condition core_osd_file::read(void *buffer, std::size_t length, std::size_t &actual) noexcept
|
||||
{
|
||||
// since osd_file works like pread/pwrite, implement in terms of read_at
|
||||
// core_osd_file is delcared final, so a derived class can't interfere
|
||||
std::error_condition err = read_at(offset(), buffer, length, actual);
|
||||
add_offset(actual);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
std::error_condition core_osd_file::read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept
|
||||
{
|
||||
if (!m_file || is_loaded())
|
||||
return core_in_memory_file::read(buffer, length);
|
||||
return core_in_memory_file::read_at(offset, buffer, length, actual);
|
||||
|
||||
// flush any buffered char
|
||||
clear_putback();
|
||||
|
||||
std::uint32_t bytes_read = 0;
|
||||
actual = 0U;
|
||||
std::error_condition err;
|
||||
|
||||
// if we're within the buffer, consume that first
|
||||
if (is_buffered())
|
||||
bytes_read += safe_buffer_copy(m_buffer, offset() - m_bufferbase, m_bufferbytes, buffer, bytes_read, length);
|
||||
if (is_buffered(offset))
|
||||
actual += safe_buffer_copy(m_buffer, offset - m_bufferbase, m_bufferbytes, buffer, actual, length);
|
||||
|
||||
// if we've got a small amount left, read it into the buffer first
|
||||
if (bytes_read < length)
|
||||
if (actual < length)
|
||||
{
|
||||
if ((length - bytes_read) < (sizeof(m_buffer) / 2))
|
||||
if ((length - actual) < (sizeof(m_buffer) / 2))
|
||||
{
|
||||
// read as much as makes sense into the buffer
|
||||
m_bufferbase = offset() + bytes_read;
|
||||
m_bufferbytes = 0;
|
||||
m_file->read(m_buffer, m_bufferbase, sizeof(m_buffer), m_bufferbytes);
|
||||
m_bufferbase = offset + actual;
|
||||
err = m_file->read(m_buffer, m_bufferbase, sizeof(m_buffer), m_bufferbytes);
|
||||
|
||||
// do a bounded copy from the buffer to the destination
|
||||
bytes_read += safe_buffer_copy(m_buffer, 0, m_bufferbytes, buffer, bytes_read, length);
|
||||
// do a bounded copy from the buffer to the destination if it succeeded
|
||||
if (!err)
|
||||
actual += safe_buffer_copy(m_buffer, 0, m_bufferbytes, buffer, actual, length);
|
||||
else
|
||||
m_bufferbytes = 0U;
|
||||
}
|
||||
else
|
||||
{
|
||||
// read the remainder directly from the file
|
||||
std::uint32_t new_bytes_read = 0;
|
||||
m_file->read(reinterpret_cast<std::uint8_t *>(buffer) + bytes_read, offset() + bytes_read, length - bytes_read, new_bytes_read);
|
||||
bytes_read += new_bytes_read;
|
||||
do
|
||||
{
|
||||
// may need to split into chunks if size_t is larger than 32 bits
|
||||
std::uint32_t const chunk = std::min<std::common_type_t<std::uint32_t, std::size_t> >(std::numeric_limits<std::uint32_t>::max(), length - actual);
|
||||
std::uint32_t bytes_read;
|
||||
err = m_file->read(reinterpret_cast<std::uint8_t *>(buffer) + actual, offset + actual, chunk, bytes_read);
|
||||
if (err || !bytes_read)
|
||||
break;
|
||||
actual += bytes_read;
|
||||
}
|
||||
while (actual < length);
|
||||
}
|
||||
}
|
||||
|
||||
// return the number of bytes read
|
||||
add_offset(bytes_read);
|
||||
return bytes_read;
|
||||
// return any errors
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
@ -672,17 +780,29 @@ void const *core_osd_file::buffer()
|
||||
if (!is_loaded() && length())
|
||||
{
|
||||
// allocate some memory
|
||||
void *buf = allocate();
|
||||
void *const buf = allocate();
|
||||
if (!buf)
|
||||
return nullptr;
|
||||
|
||||
// read the file
|
||||
std::uint32_t read_length = 0;
|
||||
auto const filerr = m_file->read(buf, 0, length(), read_length);
|
||||
if (filerr || (read_length != length()))
|
||||
purge();
|
||||
else
|
||||
m_file.reset(); // close the file because we don't need it anymore
|
||||
std::uint64_t bytes_read = 0;
|
||||
std::uint64_t remaining = length();
|
||||
std::uint8_t *ptr = reinterpret_cast<std::uint8_t *>(buf);
|
||||
while (remaining)
|
||||
{
|
||||
std::uint32_t const chunk = std::min<std::common_type_t<std::uint32_t, std::size_t> >(std::numeric_limits<std::uint32_t>::max(), remaining);
|
||||
std::uint32_t read_length;
|
||||
std::error_condition const filerr = m_file->read(ptr, bytes_read, chunk, read_length);
|
||||
if (filerr || !read_length)
|
||||
{
|
||||
purge();
|
||||
return core_in_memory_file::buffer();
|
||||
}
|
||||
bytes_read += read_length;
|
||||
remaining -= read_length;
|
||||
ptr += read_length;
|
||||
}
|
||||
m_file.reset(); // close the file because we don't need it anymore
|
||||
}
|
||||
return core_in_memory_file::buffer();
|
||||
}
|
||||
@ -692,25 +812,46 @@ void const *core_osd_file::buffer()
|
||||
write - write to a file
|
||||
-------------------------------------------------*/
|
||||
|
||||
std::uint32_t core_osd_file::write(void const *buffer, std::uint32_t length)
|
||||
std::error_condition core_osd_file::write(void const *buffer, std::size_t length, std::size_t &actual) noexcept
|
||||
{
|
||||
// since osd_file works like pread/pwrite, implement in terms of write_at
|
||||
// core_osd_file is delcared final, so a derived class can't interfere
|
||||
std::error_condition err = write_at(offset(), buffer, length, actual);
|
||||
add_offset(actual);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
std::error_condition core_osd_file::write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept
|
||||
{
|
||||
// can't write to RAM-based stuff
|
||||
if (is_loaded())
|
||||
return core_in_memory_file::write(buffer, length);
|
||||
return core_in_memory_file::write_at(offset, buffer, length, actual);
|
||||
|
||||
// flush any buffered char
|
||||
clear_putback();
|
||||
|
||||
// invalidate any buffered data
|
||||
m_bufferbytes = 0;
|
||||
m_bufferbytes = 0U;
|
||||
|
||||
// do the write
|
||||
std::uint32_t bytes_written = 0;
|
||||
m_file->write(buffer, offset(), length, bytes_written);
|
||||
|
||||
// return the number of bytes written
|
||||
add_offset(bytes_written);
|
||||
return bytes_written;
|
||||
// do the write - may need to split into chunks if size_t is larger than 32 bits
|
||||
actual = 0U;
|
||||
while (length)
|
||||
{
|
||||
// bytes written not valid on error
|
||||
std::uint32_t const chunk = std::min<std::common_type_t<std::uint32_t, std::size_t> >(std::numeric_limits<std::uint32_t>::max(), length);
|
||||
std::uint32_t bytes_written;
|
||||
std::error_condition err = m_file->write(buffer, offset, chunk, bytes_written);
|
||||
if (err)
|
||||
return err;
|
||||
assert(chunk >= bytes_written);
|
||||
offset += bytes_written;
|
||||
buffer = reinterpret_cast<std::uint8_t const *>(buffer) + bytes_written;
|
||||
length -= bytes_written;
|
||||
actual += bytes_written;
|
||||
set_length((std::max)(this->length(), offset));
|
||||
}
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
|
||||
@ -724,7 +865,7 @@ std::error_condition core_osd_file::truncate(std::uint64_t offset)
|
||||
return core_in_memory_file::truncate(offset);
|
||||
|
||||
// truncate file
|
||||
auto const err = m_file->truncate(offset);
|
||||
std::error_condition err = m_file->truncate(offset);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@ -738,7 +879,16 @@ std::error_condition core_osd_file::truncate(std::uint64_t offset)
|
||||
flush - flush file buffers
|
||||
-------------------------------------------------*/
|
||||
|
||||
std::error_condition core_osd_file::flush()
|
||||
std::error_condition core_osd_file::finalize() noexcept
|
||||
{
|
||||
if (is_loaded())
|
||||
return core_in_memory_file::finalize();
|
||||
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
|
||||
std::error_condition core_osd_file::flush() noexcept
|
||||
{
|
||||
if (is_loaded())
|
||||
return core_in_memory_file::flush();
|
||||
@ -747,7 +897,7 @@ std::error_condition core_osd_file::flush()
|
||||
clear_putback();
|
||||
|
||||
// invalidate any buffered data
|
||||
m_bufferbytes = 0;
|
||||
m_bufferbytes = 0U;
|
||||
|
||||
return m_file->flush();
|
||||
}
|
||||
@ -765,24 +915,19 @@ std::error_condition core_osd_file::flush()
|
||||
return an error code
|
||||
-------------------------------------------------*/
|
||||
|
||||
std::error_condition core_file::open(std::string_view filename, std::uint32_t openflags, ptr &file)
|
||||
std::error_condition core_file::open(std::string_view filename, std::uint32_t openflags, ptr &file) noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
// attempt to open the file
|
||||
osd_file::ptr f;
|
||||
std::uint64_t length = 0;
|
||||
auto const filerr = osd_file::open(std::string(filename), openflags, f, length); // FIXME: allow osd_file to accept std::string_view
|
||||
if (filerr)
|
||||
return filerr;
|
||||
// attempt to open the file
|
||||
osd_file::ptr f;
|
||||
std::uint64_t length = 0;
|
||||
auto const filerr = osd_file::open(std::string(filename), openflags, f, length); // FIXME: allow osd_file to accept std::string_view
|
||||
if (filerr)
|
||||
return filerr;
|
||||
|
||||
file = std::make_unique<core_osd_file>(openflags, std::move(f), length);
|
||||
return std::error_condition();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return std::errc::not_enough_memory;
|
||||
}
|
||||
try { file = std::make_unique<core_osd_file>(openflags, std::move(f), length); }
|
||||
catch (...) { return std::errc::not_enough_memory; }
|
||||
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
|
||||
@ -791,21 +936,26 @@ std::error_condition core_file::open(std::string_view filename, std::uint32_t op
|
||||
like access and return an error code
|
||||
-------------------------------------------------*/
|
||||
|
||||
std::error_condition core_file::open_ram(void const *data, std::size_t length, std::uint32_t openflags, ptr &file)
|
||||
std::error_condition core_file::open_ram(void const *data, std::size_t length, std::uint32_t openflags, ptr &file) noexcept
|
||||
{
|
||||
// can only do this for read access
|
||||
if ((openflags & OPEN_FLAG_WRITE) || (openflags & OPEN_FLAG_CREATE))
|
||||
return std::errc::invalid_argument;
|
||||
|
||||
try
|
||||
{
|
||||
file.reset(new core_in_memory_file(openflags, data, length, false));
|
||||
return std::error_condition();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// if length is non-zero, data must be non-null
|
||||
if (length && !data)
|
||||
return std::errc::invalid_argument;
|
||||
|
||||
// platforms where size_t is larger than 64 bits are theoretically possible
|
||||
if (std::uint64_t(length) != length)
|
||||
return std::errc::file_too_large;
|
||||
|
||||
ptr result(new (std::nothrow) core_in_memory_file(openflags, data, length, false));
|
||||
if (!result)
|
||||
return std::errc::not_enough_memory;
|
||||
}
|
||||
|
||||
file = std::move(result);
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
|
||||
@ -815,25 +965,26 @@ std::error_condition core_file::open_ram(void const *data, std::size_t length, s
|
||||
error code
|
||||
-------------------------------------------------*/
|
||||
|
||||
std::error_condition core_file::open_ram_copy(void const *data, std::size_t length, std::uint32_t openflags, ptr &file)
|
||||
std::error_condition core_file::open_ram_copy(void const *data, std::size_t length, std::uint32_t openflags, ptr &file) noexcept
|
||||
{
|
||||
// can only do this for read access
|
||||
if ((openflags & OPEN_FLAG_WRITE) || (openflags & OPEN_FLAG_CREATE))
|
||||
return std::errc::invalid_argument;
|
||||
|
||||
try
|
||||
{
|
||||
ptr result(new core_in_memory_file(openflags, data, length, true));
|
||||
if (!result->buffer())
|
||||
return std::errc::not_enough_memory;
|
||||
// if length is non-zero, data must be non-null
|
||||
if (length && !data)
|
||||
return std::errc::invalid_argument;
|
||||
|
||||
file = std::move(result);
|
||||
return std::error_condition();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// platforms where size_t is larger than 64 bits are theoretically possible
|
||||
if (std::uint64_t(length) != length)
|
||||
return std::errc::file_too_large;
|
||||
|
||||
ptr result(new (std::nothrow) core_in_memory_file(openflags, data, length, true));
|
||||
if (!result || !result->buffer())
|
||||
return std::errc::not_enough_memory;
|
||||
}
|
||||
|
||||
file = std::move(result);
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
|
||||
@ -842,7 +993,7 @@ std::error_condition core_file::open_ram_copy(void const *data, std::size_t leng
|
||||
object and return an error code
|
||||
-------------------------------------------------*/
|
||||
|
||||
std::error_condition core_file::open_proxy(core_file &file, ptr &proxy)
|
||||
std::error_condition core_file::open_proxy(core_file &file, ptr &proxy) noexcept
|
||||
{
|
||||
ptr result(new (std::nothrow) core_proxy_file(file));
|
||||
if (!result)
|
||||
@ -868,75 +1019,91 @@ core_file::~core_file()
|
||||
pointer
|
||||
-------------------------------------------------*/
|
||||
|
||||
std::error_condition core_file::load(std::string_view filename, void **data, std::uint32_t &length)
|
||||
std::error_condition core_file::load(std::string_view filename, void **data, std::uint32_t &length) noexcept
|
||||
{
|
||||
ptr file;
|
||||
std::error_condition err;
|
||||
|
||||
// attempt to open the file
|
||||
auto const err = open(filename, OPEN_FLAG_READ, file);
|
||||
ptr file;
|
||||
err = open(filename, OPEN_FLAG_READ, file);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
// get the size
|
||||
auto const size = file->size();
|
||||
if (std::uint32_t(size) != size)
|
||||
std::uint64_t size;
|
||||
err = file->length(size);
|
||||
if (err)
|
||||
return err;
|
||||
else if (std::uint32_t(size) != size) // TODO: change interface to use size_t rather than uint32_t for output size
|
||||
return std::errc::file_too_large;
|
||||
|
||||
// allocate memory
|
||||
*data = malloc(size);
|
||||
*data = std::malloc(std::size_t(size));
|
||||
if (!*data)
|
||||
return std::errc::not_enough_memory;
|
||||
length = std::uint32_t(size);
|
||||
|
||||
// read the data
|
||||
if (file->read(*data, size) != size)
|
||||
if (size)
|
||||
{
|
||||
free(*data);
|
||||
return std::errc::io_error; // TODO: revisit this error code
|
||||
std::size_t actual;
|
||||
err = file->read(*data, std::size_t(size), actual);
|
||||
if (err || (size != actual))
|
||||
{
|
||||
std::free(*data);
|
||||
*data = nullptr;
|
||||
if (err)
|
||||
return err;
|
||||
else
|
||||
return std::errc::io_error; // TODO: revisit this error code - either interrupted by an async signal or file truncated out from under us
|
||||
}
|
||||
}
|
||||
|
||||
// close the file and return data
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
std::error_condition core_file::load(std::string_view filename, std::vector<uint8_t> &data)
|
||||
std::error_condition core_file::load(std::string_view filename, std::vector<uint8_t> &data) noexcept
|
||||
{
|
||||
ptr file;
|
||||
std::error_condition err;
|
||||
|
||||
// attempt to open the file
|
||||
auto const err = open(filename, OPEN_FLAG_READ, file);
|
||||
ptr file;
|
||||
err = open(filename, OPEN_FLAG_READ, file);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
// get the size
|
||||
auto const size = file->size();
|
||||
if (std::uint32_t(size) != size)
|
||||
std::uint64_t size;
|
||||
err = file->length(size);
|
||||
if (err)
|
||||
return err;
|
||||
else if (std::size_t(size) != size)
|
||||
return std::errc::file_too_large;
|
||||
|
||||
// allocate memory
|
||||
try { data.resize(size); }
|
||||
try { data.resize(std::size_t(size)); }
|
||||
catch (...) { return std::errc::not_enough_memory; }
|
||||
|
||||
// read the data
|
||||
if (file->read(&data[0], size) != size)
|
||||
if (size)
|
||||
{
|
||||
data.clear();
|
||||
return std::errc::io_error; // TODO: revisit this error code
|
||||
std::size_t actual;
|
||||
err = file->read(&data[0], std::size_t(size), actual);
|
||||
if (err || (size != actual))
|
||||
{
|
||||
data.clear();
|
||||
if (err)
|
||||
return err;
|
||||
else
|
||||
return std::errc::io_error; // TODO: revisit this error code - either interrupted by an async signal or file truncated out from under us
|
||||
}
|
||||
}
|
||||
|
||||
// close the file and return data
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------
|
||||
protected constructor
|
||||
-------------------------------------------------*/
|
||||
|
||||
core_file::core_file()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ioprocs.h"
|
||||
#include "osdfile.h"
|
||||
#include "strformat.h"
|
||||
|
||||
@ -34,7 +35,7 @@ namespace util {
|
||||
TYPE DEFINITIONS
|
||||
***************************************************************************/
|
||||
|
||||
class core_file
|
||||
class core_file : public random_read_write
|
||||
{
|
||||
public:
|
||||
typedef std::unique_ptr<core_file> ptr;
|
||||
@ -43,16 +44,16 @@ public:
|
||||
// ----- file open/close -----
|
||||
|
||||
// open a file with the specified filename
|
||||
static std::error_condition open(std::string_view filename, std::uint32_t openflags, ptr &file);
|
||||
static std::error_condition open(std::string_view filename, std::uint32_t openflags, ptr &file) noexcept;
|
||||
|
||||
// open a RAM-based "file" using the given data and length (read-only)
|
||||
static std::error_condition open_ram(const void *data, std::size_t length, std::uint32_t openflags, ptr &file);
|
||||
static std::error_condition open_ram(const void *data, std::size_t length, std::uint32_t openflags, ptr &file) noexcept;
|
||||
|
||||
// open a RAM-based "file" using the given data and length (read-only), copying the data
|
||||
static std::error_condition open_ram_copy(const void *data, std::size_t length, std::uint32_t openflags, ptr &file);
|
||||
static std::error_condition open_ram_copy(const void *data, std::size_t length, std::uint32_t openflags, ptr &file) noexcept;
|
||||
|
||||
// open a proxy "file" that forwards requests to another file object
|
||||
static std::error_condition open_proxy(core_file &file, ptr &proxy);
|
||||
static std::error_condition open_proxy(core_file &file, ptr &proxy) noexcept;
|
||||
|
||||
// close an open file
|
||||
virtual ~core_file();
|
||||
@ -60,24 +61,12 @@ public:
|
||||
|
||||
// ----- file positioning -----
|
||||
|
||||
// adjust the file pointer within the file
|
||||
virtual int seek(std::int64_t offset, int whence) = 0;
|
||||
|
||||
// return the current file pointer
|
||||
virtual std::uint64_t tell() const = 0;
|
||||
|
||||
// return true if we are at the EOF
|
||||
virtual bool eof() const = 0;
|
||||
|
||||
// return the total size of the file
|
||||
virtual std::uint64_t size() const = 0;
|
||||
|
||||
|
||||
// ----- file read -----
|
||||
|
||||
// standard binary read from a file
|
||||
virtual std::uint32_t read(void *buffer, std::uint32_t length) = 0;
|
||||
|
||||
// read one character from the file
|
||||
virtual int getc() = 0;
|
||||
|
||||
@ -92,15 +81,12 @@ public:
|
||||
virtual const void *buffer() = 0;
|
||||
|
||||
// open a file with the specified filename, read it into memory, and return a pointer
|
||||
static std::error_condition load(std::string_view filename, void **data, std::uint32_t &length);
|
||||
static std::error_condition load(std::string_view filename, std::vector<uint8_t> &data);
|
||||
static std::error_condition load(std::string_view filename, void **data, std::uint32_t &length) noexcept;
|
||||
static std::error_condition load(std::string_view filename, std::vector<uint8_t> &data) noexcept;
|
||||
|
||||
|
||||
// ----- file write -----
|
||||
|
||||
// standard binary write to a file
|
||||
virtual std::uint32_t write(const void *buffer, std::uint32_t length) = 0;
|
||||
|
||||
// write a line of text to the file
|
||||
virtual int puts(std::string_view s) = 0;
|
||||
|
||||
@ -113,13 +99,6 @@ public:
|
||||
|
||||
// file truncation
|
||||
virtual std::error_condition truncate(std::uint64_t offset) = 0;
|
||||
|
||||
// flush file buffers
|
||||
virtual std::error_condition flush() = 0;
|
||||
|
||||
|
||||
protected:
|
||||
core_file();
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
|
@ -87,11 +87,11 @@ delegate_generic_function delegate_mfp_itanium::convert_to_generic(delegate_gene
|
||||
// apply the "this" delta to the object first - the value is shifted to the left one bit position for the ARM-like variant
|
||||
LOG("Input this=%p ptr=%p adj=%ld ", reinterpret_cast<void const *>(object), reinterpret_cast<void const *>(m_function), long(m_this_delta));
|
||||
object = reinterpret_cast<delegate_generic_class *>(
|
||||
reinterpret_cast<std::uint8_t *>(object) + (m_this_delta >> (MAME_DELEGATE_ITANIUM_ARM ? 1 : 0)));
|
||||
reinterpret_cast<std::uint8_t *>(object) + (m_this_delta >> ((MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? 1 : 0)));
|
||||
LOG("Calculated this=%p ", reinterpret_cast<void const *>(object));
|
||||
|
||||
// test the virtual member function flag - it's the low bit of either the ptr or adj field, depending on the variant
|
||||
if (MAME_DELEGATE_ITANIUM_ARM ? !(m_this_delta & 1) : !(m_function & 1))
|
||||
if ((MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? !(m_this_delta & 1) : !(m_function & 1))
|
||||
{
|
||||
// conventional function pointer
|
||||
LOG("ptr=%p\n", reinterpret_cast<void const *>(m_function));
|
||||
@ -100,7 +100,7 @@ delegate_generic_function delegate_mfp_itanium::convert_to_generic(delegate_gene
|
||||
else
|
||||
{
|
||||
// byte index into the vtable to the function
|
||||
std::uint8_t const *const vtable_ptr = *reinterpret_cast<std::uint8_t const *const *>(object) + m_function - (MAME_DELEGATE_ITANIUM_ARM ? 0 : 1);
|
||||
std::uint8_t const *const vtable_ptr = *reinterpret_cast<std::uint8_t const *const *>(object) + m_function - ((MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? 0 : 1);
|
||||
delegate_generic_function result;
|
||||
if (MAME_DELEGATE_VT_DESCRIPTOR)
|
||||
result = reinterpret_cast<delegate_generic_function>(uintptr_t(vtable_ptr));
|
||||
@ -159,7 +159,7 @@ delegate_generic_function delegate_mfp_msvc::adjust_this_pointer(delegate_generi
|
||||
{
|
||||
// relative jump with 32-bit displacement (typically a resolved PLT entry)
|
||||
LOG("Found relative jump at %p ", func);
|
||||
func += 5 + *reinterpret_cast<std::int32_t const *>(func + 1);
|
||||
func += std::ptrdiff_t(5) + *reinterpret_cast<std::int32_t const *>(func + 1);
|
||||
LOG("redirecting to %p\n", func);
|
||||
continue;
|
||||
}
|
||||
|
@ -114,6 +114,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "abi.h"
|
||||
|
||||
#include <any>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
@ -143,35 +145,21 @@
|
||||
#if defined(__MINGW32__) && !defined(__x86_64__) && defined(__i386__)
|
||||
#define MAME_DELEGATE_USE_TYPE MAME_DELEGATE_TYPE_COMPATIBLE
|
||||
//#define MAME_DELEGATE_USE_TYPE MAME_DELEGATE_TYPE_ITANIUM
|
||||
//#define MAME_DELEGATE_MEMBER_ABI __thiscall
|
||||
//#define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 1
|
||||
#elif defined(__clang__) && defined(__i386__) && defined(_WIN32)
|
||||
#define MAME_DELEGATE_USE_TYPE MAME_DELEGATE_TYPE_COMPATIBLE
|
||||
#else
|
||||
#define MAME_DELEGATE_USE_TYPE MAME_DELEGATE_TYPE_ITANIUM
|
||||
#define MAME_DELEGATE_MEMBER_ABI
|
||||
#define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 0
|
||||
#endif
|
||||
#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64))
|
||||
#define MAME_DELEGATE_MEMBER_ABI
|
||||
#define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 0
|
||||
#define MAME_DELEGATE_USE_TYPE MAME_DELEGATE_TYPE_MSVC
|
||||
#else
|
||||
#define MAME_DELEGATE_USE_TYPE MAME_DELEGATE_TYPE_COMPATIBLE
|
||||
#endif
|
||||
|
||||
#if defined(__arm__) || defined(__ARMEL__) || defined(__aarch64__)
|
||||
#define MAME_DELEGATE_ITANIUM_ARM 1
|
||||
#elif defined(__MIPSEL__) || defined(__mips_isa_rev) || defined(__mips64)
|
||||
#define MAME_DELEGATE_ITANIUM_ARM 1
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
#define MAME_DELEGATE_ITANIUM_ARM 1
|
||||
#else
|
||||
#define MAME_DELEGATE_ITANIUM_ARM 0
|
||||
#endif
|
||||
|
||||
#if MAME_DELEGATE_USE_TYPE == MAME_DELEGATE_TYPE_COMPATIBLE
|
||||
#define MAME_DELEGATE_MEMBER_ABI
|
||||
#define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 0
|
||||
#endif
|
||||
|
||||
@ -359,7 +347,7 @@ ReturnType delegate_mfp_compatible::method_stub(delegate_generic_class *object,
|
||||
///
|
||||
/// Supports the two most popular pointer to member function
|
||||
/// implementations described in the Itanium C++ ABI. Both of these
|
||||
/// consist of a pointer followed by a pointer followed by a ptrdiff_t.
|
||||
/// consist of a pointer followed by a ptrdiff_t.
|
||||
///
|
||||
/// The first variant is used when member the least significant bit of a
|
||||
/// member function pointer need never be set and vtable entry offsets
|
||||
@ -367,8 +355,8 @@ ReturnType delegate_mfp_compatible::method_stub(delegate_generic_class *object,
|
||||
/// it is a conventional function pointer to the member function. If
|
||||
/// the pointer is odd, it is a byte offset into the vtable plus one.
|
||||
/// The ptrdiff_t is a byte offset to add to the this pointer. A null
|
||||
/// member function pointer is represented by setting the pointer to
|
||||
/// a null pointer.
|
||||
/// member function pointer is represented by setting the pointer to a
|
||||
/// null pointer.
|
||||
///
|
||||
/// The second variant is used when the least significant bit of a
|
||||
/// pointer to a member function may need to be set or it may not be
|
||||
@ -408,7 +396,7 @@ public:
|
||||
|
||||
bool isnull() const
|
||||
{
|
||||
if (MAME_DELEGATE_ITANIUM_ARM)
|
||||
if (MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM)
|
||||
return !reinterpret_cast<void (*)()>(m_function) && !(m_this_delta & 1);
|
||||
else
|
||||
return !reinterpret_cast<void (*)()>(m_function);
|
||||
@ -690,7 +678,7 @@ public:
|
||||
// define our traits
|
||||
template <class FunctionClass> using traits = delegate_traits<FunctionClass, ReturnType, Params...>;
|
||||
using generic_static_func = typename traits<delegate_generic_class>::static_func_type;
|
||||
typedef MAME_DELEGATE_MEMBER_ABI generic_static_func generic_member_func;
|
||||
typedef MAME_ABI_CXX_MEMBER_CALL generic_static_func generic_member_func;
|
||||
|
||||
// generic constructor
|
||||
delegate_base() = default;
|
||||
|
229
src/lib/util/dynamicclass.cpp
Normal file
229
src/lib/util/dynamicclass.cpp
Normal file
@ -0,0 +1,229 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Vas Crabb
|
||||
#include "dynamicclass.ipp"
|
||||
|
||||
#include <locale>
|
||||
#include <new>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
namespace util {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/// \brief Complete object locator equivalent structure
|
||||
///
|
||||
/// Structure used for locating the complete object and type information
|
||||
/// for the MSVC C++ ABI on 64-bit targets. A pointer to this structure
|
||||
/// appears immediately before the first virtual member function entry
|
||||
/// in the virtual function table.
|
||||
struct dynamic_derived_class_base::msvc_complete_object_locator_equiv
|
||||
{
|
||||
unsigned long signature; ///< Magic number, always 1
|
||||
unsigned long offset; ///< Offset from complete object to current object
|
||||
unsigned long ctor_disp_offset; ///< For avoiding extra constructor virtual tables for virtual inheritance
|
||||
int type_info_offs; ///< Offset to type info
|
||||
int class_info_offs; ///< Offset to class hierarchy info
|
||||
int self_offs; ///< Offset to this structure for calculating module base
|
||||
};
|
||||
|
||||
|
||||
/// \brief Type info equivalent structure
|
||||
///
|
||||
/// Structure equivalent to the implementation of std::type_info for the
|
||||
/// MSVC C++ ABI. The structure is followed immediately by the
|
||||
/// decorated name. The pointer to the undecorated name is normally
|
||||
/// populated lazily when the \c name member function is called.
|
||||
struct dynamic_derived_class_base::msvc_type_info_equiv
|
||||
{
|
||||
void const *vptr; ///< Pointer to virtual table
|
||||
char const *undecorated; ///< Pointer to the undecorated name
|
||||
char decorated[1]; ///< First character of the decorated name
|
||||
};
|
||||
|
||||
|
||||
/// \brief Construct dynamic derived class base
|
||||
///
|
||||
/// Creates type info for a class with a single base class using the
|
||||
/// specified name. If the name contains multiple components separated
|
||||
/// by \c :: separators, it is interpreted as a nested class name.
|
||||
/// \param [in] name The name for the dynamic class. Components must
|
||||
/// start with an alphabetic character or an underscore, and may
|
||||
/// contain only alphanumeric characters and underscores.
|
||||
/// \exception std::invalid_argument Thrown if the class name is invalid
|
||||
/// or unsupported.
|
||||
/// \exception std::bad_alloc Thrown if allocating memory for the type
|
||||
/// info fails.
|
||||
dynamic_derived_class_base::dynamic_derived_class_base(std::string_view name) :
|
||||
m_base_vtable(nullptr)
|
||||
{
|
||||
assert(!reinterpret_cast<void *>(std::uintptr_t(static_cast<void (*)()>(nullptr))));
|
||||
assert(!reinterpret_cast<void (*)()>(std::uintptr_t(static_cast<void *>(nullptr))));
|
||||
|
||||
std::locale const &clocale(std::locale::classic());
|
||||
if (name.empty() || (('_' != name[0]) && !std::isalpha(name[0], clocale)) || (std::find_if_not(name.begin(), name.end(), [&clocale] (char c) { return (':' == c) || ('_' == c) || std::isalnum(c, clocale); }) != name.end()))
|
||||
throw std::invalid_argument("Invalid class name");
|
||||
|
||||
std::ostringstream str;
|
||||
str.imbue(clocale);
|
||||
|
||||
#if MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC
|
||||
str << "class " << name;
|
||||
m_name = std::move(str).str();
|
||||
str.str(std::string());
|
||||
|
||||
str << ".?AV";
|
||||
std::string_view::size_type found = name.find(':');
|
||||
while (std::string_view::npos != found)
|
||||
{
|
||||
if (((found + 2) >= name.length()) || (name[found + 1] != ':') || (('_' != name[found + 2]) && !std::isalpha(name[found + 2], clocale)))
|
||||
throw std::invalid_argument("Invalid class name");
|
||||
|
||||
str.write(&name[0], found);
|
||||
str << '@';
|
||||
name.remove_prefix(found + 2);
|
||||
found = name.find(':');
|
||||
}
|
||||
str << name << "@@";
|
||||
std::string const mangled = std::move(str).str();
|
||||
|
||||
m_type_info = reinterpret_cast<msvc_type_info_equiv *>(
|
||||
operator new (
|
||||
offsetof(msvc_type_info_equiv, decorated) + mangled.length() + 1,
|
||||
std::align_val_t(alignof(msvc_type_info_equiv))));
|
||||
m_type_info->vptr = *reinterpret_cast<void const *const *>(&typeid(dynamic_derived_class_base));
|
||||
m_type_info->undecorated = m_name.c_str();
|
||||
std::copy_n(mangled.c_str(), mangled.length() + 1, m_type_info->decorated);
|
||||
#else
|
||||
class base { };
|
||||
class derived : base { };
|
||||
|
||||
m_type_info.vptr = *reinterpret_cast<void const *const *>(&typeid(derived));
|
||||
|
||||
std::string_view::size_type found = name.find(':');
|
||||
bool const qualified = std::string::npos != found;
|
||||
if (qualified)
|
||||
str << 'N';
|
||||
while (std::string_view::npos != found)
|
||||
{
|
||||
if (((found + 2) >= name.length()) || (name[found + 1] != ':') || (('_' != name[found + 2]) && !std::isalpha(name[found + 2], clocale)))
|
||||
throw std::invalid_argument("Invalid class name");
|
||||
|
||||
str << found;
|
||||
str.write(&name[0], found);
|
||||
name.remove_prefix(found + 2);
|
||||
found = name.find(':');
|
||||
}
|
||||
str << name.length() << name;
|
||||
if (qualified)
|
||||
str << 'E';
|
||||
m_name = std::move(str).str();
|
||||
|
||||
m_type_info.name = m_name.c_str();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
dynamic_derived_class_base::~dynamic_derived_class_base()
|
||||
{
|
||||
#if MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC
|
||||
operator delete (m_type_info, std::align_val_t(alignof(msvc_type_info_equiv)));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// \brief Get virtual table index for member function
|
||||
///
|
||||
/// Gets the virtual table index represented by a pointer to a virtual
|
||||
/// member function. The \p slot argument must refer to a virtual
|
||||
/// member function returning a supported type that does not require
|
||||
/// \c this pointer adjustment.
|
||||
/// \param [in] slot Internal representation of pointer to a virtual
|
||||
/// member function. May be modified.
|
||||
/// \param [in] size Size of the member function pointer type for the
|
||||
/// \p slot argument.
|
||||
/// \return The virtual table index of the member function, in terms of
|
||||
/// the size of a virtual member function in the virtual table.
|
||||
/// \exception std::invalid_argument Thrown if the \p slot argument is
|
||||
/// not a supported virtual member function.
|
||||
std::size_t dynamic_derived_class_base::resolve_virtual_member_slot(
|
||||
member_function_pointer_equiv &slot,
|
||||
std::size_t size)
|
||||
{
|
||||
#if MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC
|
||||
if ((sizeof(msvc_mi_member_function_pointer_equiv) <= size) && slot.adj)
|
||||
throw std::invalid_argument("Member function requires this pointer adjustment");
|
||||
#if defined(__x86_64__) || defined(_M_X64)
|
||||
std::uint8_t const *func = reinterpret_cast<std::uint8_t const *>(slot.ptr);
|
||||
while (0xe9 == func[0]) // relative jump with 32-bit displacement (typically a resolved PLT entry)
|
||||
func += std::ptrdiff_t(5) + *reinterpret_cast<std::int32_t const *>(func + 1);
|
||||
if ((0x48 == func[0]) && (0x8b == func[1]) && (0x01 == func[2]))
|
||||
{
|
||||
if ((0xff == func[3]) && ((0x20 == func[4]) || (0x60 == func[4]) || (0xa0 == func[4])))
|
||||
{
|
||||
// MSVC virtual function call thunk - mov rax,QWORD PTR [rcx] ; jmp QWORD PTR [rax+...]
|
||||
if (0x20 == func[4]) // no displacement
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if (0x60 == func[4]) // 8-bit displacement
|
||||
{
|
||||
auto const index = *reinterpret_cast<std::int8_t const *>(func + 5);
|
||||
if (index % (sizeof(std::uintptr_t) * MEMBER_FUNCTION_SIZE))
|
||||
throw std::invalid_argument("Invalid member function virtual table index");
|
||||
return index / sizeof(std::uintptr_t) / MEMBER_FUNCTION_SIZE;
|
||||
}
|
||||
else // 32-bit displacement
|
||||
{
|
||||
auto const index = *reinterpret_cast<std::int32_t const *>(func + 5);
|
||||
if (index % (sizeof(std::uintptr_t) * MEMBER_FUNCTION_SIZE))
|
||||
throw std::invalid_argument("Invalid member function virtual table index");
|
||||
return index / sizeof(std::uintptr_t) / MEMBER_FUNCTION_SIZE;
|
||||
}
|
||||
}
|
||||
else if ((0x48 == func[3]) && (0x8b == func[4]))
|
||||
{
|
||||
// clang virtual function call thunk - mov rax,QWORD PTR [rcx] ; mov rax,QWORD PTR [rax+...] ; jmp rax
|
||||
if ((0x00 == func[5]) && (0x48 == func[6]) && (0xff == func[7]) && (0xe0 == func[8]))
|
||||
{
|
||||
// no displacement
|
||||
return 0;
|
||||
}
|
||||
else if ((0x40 == func[5]) && (0x48 == func[7]) && (0xff == func[8]) && (0xe0 == func[9]))
|
||||
{
|
||||
// 8-bit displacement
|
||||
auto const index = *reinterpret_cast<std::int8_t const *>(func + 6);
|
||||
if (index % (sizeof(std::uintptr_t) * MEMBER_FUNCTION_SIZE))
|
||||
throw std::invalid_argument("Invalid member function virtual table index");
|
||||
return index / sizeof(std::uintptr_t) / MEMBER_FUNCTION_SIZE;
|
||||
}
|
||||
else if ((0x80 == func[5]) && (0x48 == func[10]) && (0xff == func[11]) && (0xe0 == func[12]))
|
||||
{
|
||||
// 32-bit displacement
|
||||
auto const index = *reinterpret_cast<std::int32_t const *>(func + 6);
|
||||
if (index % (sizeof(std::uintptr_t) * MEMBER_FUNCTION_SIZE))
|
||||
throw std::invalid_argument("Invalid member function virtual table index");
|
||||
return index / sizeof(std::uintptr_t) / MEMBER_FUNCTION_SIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw std::invalid_argument("Not a supported pointer to a virtual member function");
|
||||
#else
|
||||
throw std::runtime_error("Unsupported architecture");
|
||||
#endif
|
||||
#else
|
||||
if (!slot.is_virtual())
|
||||
throw std::invalid_argument("Not a pointer to a virtual member function");
|
||||
if (slot.this_pointer_offset())
|
||||
throw std::invalid_argument("Member function requires this pointer adjustment");
|
||||
if (MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_STANDARD)
|
||||
slot.ptr -= 1;
|
||||
if (slot.ptr % (sizeof(std::uintptr_t) * MEMBER_FUNCTION_SIZE))
|
||||
throw std::invalid_argument("Invalid member function virtual table index");
|
||||
return slot.ptr / sizeof(std::uintptr_t) / MEMBER_FUNCTION_SIZE;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace util
|
488
src/lib/util/dynamicclass.h
Normal file
488
src/lib/util/dynamicclass.h
Normal file
@ -0,0 +1,488 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Vas Crabb
|
||||
/// \file
|
||||
/// \brief Dynamic derived class creator
|
||||
///
|
||||
/// Allows on-the-fly creation of derived classes that override selected
|
||||
/// virtual member functions of the base class. The overridden base
|
||||
/// class member functions can be changed even while instances exist.
|
||||
/// This is particularly useful in conjunction with code generation.
|
||||
///
|
||||
/// Only implemented for configurations where it is possible to define a
|
||||
/// non-member function that uses the same calling convention as a
|
||||
/// non-static member function, and subject to a number of limitations
|
||||
/// on classes that can be used as base classes.
|
||||
///
|
||||
/// Itanium C++ ABI support is reasonably complete.
|
||||
///
|
||||
/// MSVC C++ABI support is subject to additional limitations:
|
||||
/// * Instances of dynamic derived classes appear to be instance of
|
||||
/// their respective base classes when subject to RTTI introspection.
|
||||
/// * Member functions returning class, structure or union types cannot
|
||||
/// be overridden.
|
||||
/// * Member functions returning scalar types that are not returned in
|
||||
/// registers cannot be overridden.
|
||||
/// * Member functions cannot be overridden for classes built with clang
|
||||
/// with optimisation disabled.
|
||||
/// * Base class implementations of member functions can only be
|
||||
/// resolved if they are virtual member functions that can be
|
||||
/// overridden.
|
||||
/// * Overriding member functions and resolving base class
|
||||
/// implementations of member functions is substantially more
|
||||
/// expensive than for the Itanium C++ ABI.
|
||||
/// * Only x86-64 targets are supported.
|
||||
///
|
||||
/// It goes without saying that this is not something you are supposed
|
||||
/// to do. This depends heavily on implementation details, and careful
|
||||
/// use by the developer. Use it at your own risk.
|
||||
#ifndef MAME_LIB_UTIL_DYNAMICCLASS_H
|
||||
#define MAME_LIB_UTIL_DYNAMICCLASS_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "abi.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <typeinfo>
|
||||
#include <utility>
|
||||
|
||||
|
||||
namespace util {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/// \brief Dynamic derived class base
|
||||
///
|
||||
/// Provides common members for dynamic derived classes that do not
|
||||
/// depend on the size of the virtual table.
|
||||
class dynamic_derived_class_base
|
||||
{
|
||||
protected:
|
||||
/// \brief Size of a member function virtual table entry
|
||||
///
|
||||
/// The size of an entry representing a virtual member function in a
|
||||
/// virtual table, as a multiple of the size of a pointer.
|
||||
static constexpr std::size_t MEMBER_FUNCTION_SIZE = MAME_ABI_CXX_VTABLE_FNDESC ? MAME_ABI_FNDESC_SIZE : 1;
|
||||
|
||||
/// \brief Virtual table entries before first member function
|
||||
///
|
||||
/// The number of pointer-sized entries before the first entry that
|
||||
/// is part of a member function representation.
|
||||
///
|
||||
/// For the MSVC C++ ABI, only one of these is an ABI requirement
|
||||
/// (the pointer to the complete object locator). The other isused
|
||||
/// internally to allow the base virtual table pointer to be
|
||||
/// recovered.
|
||||
static constexpr std::size_t VTABLE_PREFIX_ENTRIES = (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC) ? (1 + 1) : 2;
|
||||
|
||||
/// \brief Virtual table entries generated for a virtual destructor
|
||||
///
|
||||
/// The number of member function entries generated in the virtual
|
||||
/// table for a virtual destructor.
|
||||
static constexpr std::size_t VTABLE_DESTRUCTOR_ENTRIES = (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC) ? 1 : 2;
|
||||
|
||||
/// \brief Virtual table index for base virtual table recovery
|
||||
///
|
||||
/// Index of the virtual table entry used for recovering the base
|
||||
/// class virtual table pointer, relative to the location an
|
||||
/// instance virtual table pointer points to.
|
||||
static constexpr std::ptrdiff_t VTABLE_BASE_RECOVERY_INDEX = (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC) ? -2 : -1;
|
||||
|
||||
/// \brief Single inheritance class type info equivalent structure
|
||||
///
|
||||
/// Structure equivalent to the implementation of std::type_info for
|
||||
/// a class with a single direct base class for the Itanium C++ ABI.
|
||||
struct itanium_si_class_type_info_equiv
|
||||
{
|
||||
void const *vptr; ///< Pointer to single inheritance class type info virtual table
|
||||
char const *name; ///< Mangled name of the class
|
||||
std::type_info const *base_type; ///< Pointer to type info for the base class
|
||||
};
|
||||
|
||||
/// \brief Member function pointer equivalent structure
|
||||
///
|
||||
/// Structure equivalent to the representation of a pointer to a
|
||||
/// member function for the Itanium C++ ABI.
|
||||
struct itanium_member_function_pointer_equiv
|
||||
{
|
||||
uintptr_t ptr; ///< Function pointer or virtual table index
|
||||
ptrdiff_t adj; ///< Offset to add to \c this pointer before call
|
||||
|
||||
constexpr bool is_virtual() const
|
||||
{
|
||||
return (MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? bool(adj & 1) : bool(ptr & 1);
|
||||
}
|
||||
|
||||
constexpr uintptr_t function_pointer() const
|
||||
{
|
||||
//assert(!is_virtual()); not constexpr
|
||||
return ptr;
|
||||
}
|
||||
|
||||
constexpr uintptr_t virtual_table_entry_offset() const
|
||||
{
|
||||
//assert(is_virtual()); not constexpr
|
||||
return ptr - ((MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_STANDARD) ? 1 : 0);
|
||||
}
|
||||
|
||||
constexpr ptrdiff_t this_pointer_offset() const
|
||||
{
|
||||
return adj >> ((MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? 1 : 0);
|
||||
}
|
||||
};
|
||||
|
||||
struct msvc_complete_object_locator_equiv;
|
||||
struct msvc_type_info_equiv;
|
||||
|
||||
/// \brief Single inheritance member function pointer equivalent
|
||||
///
|
||||
/// Structure equivalent to the representation of a pointer to a
|
||||
/// member function of a single inheritance class for the MSVC C++
|
||||
/// ABI.
|
||||
struct msvc_si_member_function_pointer_equiv
|
||||
{
|
||||
uintptr_t ptr; ///< Pointer to function, PLT entry, or virtual member call thunk
|
||||
};
|
||||
|
||||
/// \brief Multiple inheritance member function pointer equivalent
|
||||
///
|
||||
/// Structure equivalent to the representation of a pointer to a
|
||||
/// member function of a Multiple inheritance class for the MSVC C++
|
||||
/// ABI.
|
||||
struct msvc_mi_member_function_pointer_equiv : msvc_si_member_function_pointer_equiv
|
||||
{
|
||||
int adj; ///< Offset to \c this pointer to add before call
|
||||
};
|
||||
|
||||
#if MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC
|
||||
using member_function_pointer_equiv = msvc_mi_member_function_pointer_equiv;
|
||||
|
||||
template <typename T>
|
||||
using supported_return_type = std::bool_constant<std::is_void_v<T> || std::is_scalar_v<T> || std::is_reference_v<T> >;
|
||||
#else
|
||||
using member_function_pointer_equiv = itanium_member_function_pointer_equiv;
|
||||
|
||||
template <typename T>
|
||||
using supported_return_type = std::true_type;
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
struct member_function_pointer_pun
|
||||
{
|
||||
static_assert(
|
||||
#if MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC
|
||||
(sizeof(T) == sizeof(msvc_si_member_function_pointer_equiv)) || (sizeof(T) == sizeof(msvc_mi_member_function_pointer_equiv)),
|
||||
#else
|
||||
sizeof(T) == sizeof(itanium_member_function_pointer_equiv),
|
||||
#endif
|
||||
"Unsupported member function pointer representation");
|
||||
|
||||
union type
|
||||
{
|
||||
T ptr;
|
||||
member_function_pointer_equiv equiv;
|
||||
};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using member_function_pointer_pun_t = typename member_function_pointer_pun<T>::type;
|
||||
|
||||
template <class Base, typename Extra>
|
||||
class value_type
|
||||
{
|
||||
private:
|
||||
template <typename... T, typename... U, std::size_t... N, std::size_t... O>
|
||||
value_type(
|
||||
std::tuple<T...> &a,
|
||||
std::tuple<U...> &b,
|
||||
std::integer_sequence<std::size_t, N...>,
|
||||
std::integer_sequence<std::size_t, O...>) :
|
||||
base(std::forward<T>(std::get<N>(a))...),
|
||||
extra(std::forward<U>(std::get<O>(b))...)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename... T>
|
||||
value_type(T &&... a) :
|
||||
base(std::forward<T>(a)...)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename... T, typename... U>
|
||||
value_type(std::piecewise_construct_t, std::tuple<T...> a, std::tuple<U...> b) :
|
||||
value_type(a, b, std::make_integer_sequence<std::size_t, sizeof...(T)>(), std::make_integer_sequence<std::size_t, sizeof...(U)>())
|
||||
{
|
||||
}
|
||||
|
||||
template <typename R, typename... T>
|
||||
std::pair<R MAME_ABI_CXX_MEMBER_CALL (*)(void *, T...), void *> resolve_base_member_function(
|
||||
R (Base::*func)(T...))
|
||||
{
|
||||
return dynamic_derived_class_base::resolve_base_member_function(base, func);
|
||||
}
|
||||
|
||||
template <typename R, typename... T>
|
||||
std::pair<R MAME_ABI_CXX_MEMBER_CALL (*)(void const *, T...), void const *> resolve_base_member_function(
|
||||
R (Base::*func)(T...) const) const
|
||||
{
|
||||
return dynamic_derived_class_base::resolve_base_member_function(base, func);
|
||||
}
|
||||
|
||||
template <typename R, typename... T>
|
||||
R call_base_member_function(R (Base::*func)(T...), T... args)
|
||||
{
|
||||
auto const resolved = dynamic_derived_class_base::resolve_base_member_function(base, func);
|
||||
return resolved.first(resolved.second, std::forward<T>(args)...);
|
||||
}
|
||||
|
||||
template <typename R, typename... T>
|
||||
R call_base_member_function(R (Base::*func)(T...) const, T... args) const
|
||||
{
|
||||
auto const resolved = dynamic_derived_class_base::resolve_base_member_function(base, func);
|
||||
return resolved.first(resolved.second, std::forward<T>(args)...);
|
||||
}
|
||||
|
||||
Base base;
|
||||
Extra extra;
|
||||
};
|
||||
|
||||
template <class Base>
|
||||
class value_type<Base, void>
|
||||
{
|
||||
public:
|
||||
template <typename... T> value_type(T &&... args) : base(std::forward<T>(args)...) { }
|
||||
|
||||
template <typename R, typename... T>
|
||||
std::pair<R MAME_ABI_CXX_MEMBER_CALL (*)(void *, T...), void *> resolve_base_member_function(
|
||||
R (Base::*func)(T...))
|
||||
{
|
||||
return dynamic_derived_class_base::resolve_base_member_function(base, func);
|
||||
}
|
||||
|
||||
template <typename R, typename... T>
|
||||
std::pair<R MAME_ABI_CXX_MEMBER_CALL (*)(void const *, T...), void const *> resolve_base_member_function(
|
||||
R (Base::*func)(T...) const) const
|
||||
{
|
||||
return dynamic_derived_class_base::resolve_base_member_function(base, func);
|
||||
}
|
||||
|
||||
template <typename R, typename... T>
|
||||
R call_base_member_function(R (Base::*func)(T...), T... args)
|
||||
{
|
||||
auto const resolved = dynamic_derived_class_base::resolve_base_member_function(base, func);
|
||||
return resolved.first(resolved.second, std::forward<T>(args)...);
|
||||
}
|
||||
|
||||
template <typename R, typename... T>
|
||||
R call_base_member_function(R (Base::*func)(T...) const, T... args) const
|
||||
{
|
||||
auto const resolved = dynamic_derived_class_base::resolve_base_member_function(base, func);
|
||||
return resolved.first(resolved.second, std::forward<T>(args)...);
|
||||
}
|
||||
|
||||
Base base;
|
||||
};
|
||||
|
||||
template <class Base, typename Extra, typename Enable = void>
|
||||
struct destroyer;
|
||||
|
||||
/// \brief Destroyer for base classes with virtual destructors
|
||||
///
|
||||
/// Provides implementations of the complete object destructor and
|
||||
/// deleting destructor required to allow deleting instances of
|
||||
/// dynamic derived classes through pointers to the base class type.
|
||||
/// \tparam Base The base class type.
|
||||
/// \tparam Extra The extra data type.
|
||||
template <class Base, typename Extra>
|
||||
struct destroyer<Base, Extra, std::enable_if_t<std::has_virtual_destructor_v<Base> > >
|
||||
{
|
||||
using pointer_type = std::unique_ptr<Base>;
|
||||
|
||||
static void MAME_ABI_CXX_MEMBER_CALL complete_object_destructor(
|
||||
value_type<Base, Extra> &object);
|
||||
|
||||
static void MAME_ABI_CXX_MEMBER_CALL deleting_destructor(
|
||||
value_type<Base, Extra> *object);
|
||||
|
||||
static void *MAME_ABI_CXX_MEMBER_CALL scalar_deleting_destructor(
|
||||
value_type<Base, Extra> *object,
|
||||
unsigned int flags);
|
||||
};
|
||||
|
||||
/// \brief Destroyer for base classes without virtual destructors
|
||||
///
|
||||
/// Used as the deleter type allowing a \c std::unique_ptr to
|
||||
/// correctly delete instances of a dynamic derived class when the
|
||||
/// the base class type does not have a virtual destructor.
|
||||
/// \tparam Base The base class type.
|
||||
/// \tparam Extra The extra data type.
|
||||
template <class Base, typename Extra>
|
||||
struct destroyer<Base, Extra, std::enable_if_t<!std::has_virtual_destructor_v<Base> > >
|
||||
{
|
||||
static_assert(sizeof(Base) == sizeof(value_type<Base, Extra>), "Value type does not have expected layout");
|
||||
|
||||
using pointer_type = std::unique_ptr<Base, destroyer>;
|
||||
|
||||
void operator()(Base *object) const;
|
||||
};
|
||||
|
||||
dynamic_derived_class_base(std::string_view name);
|
||||
~dynamic_derived_class_base();
|
||||
|
||||
static std::size_t resolve_virtual_member_slot(member_function_pointer_equiv &slot, std::size_t size);
|
||||
|
||||
#if MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC
|
||||
msvc_type_info_equiv *m_type_info;
|
||||
#else
|
||||
itanium_si_class_type_info_equiv m_type_info; ///< Type info for the dynamic derived class
|
||||
#endif
|
||||
std::string m_name; ///< Storage for the class name (mangled for Itanium, undecorated for MSVC)
|
||||
void const *m_base_vtable; ///< Saved base class virtual table pointer
|
||||
|
||||
private:
|
||||
static std::ptrdiff_t base_vtable_offset();
|
||||
|
||||
template <typename Base>
|
||||
static std::uintptr_t const *get_base_vptr(Base const &object);
|
||||
|
||||
template <typename Base>
|
||||
static void restore_base_vptr(Base &object);
|
||||
|
||||
template <typename Base, typename R, typename... T>
|
||||
static std::pair<R MAME_ABI_CXX_MEMBER_CALL (*)(void *, T...), void *> resolve_base_member_function(
|
||||
Base &object,
|
||||
R (Base::*func)(T...));
|
||||
|
||||
template <typename Base, typename R, typename... T>
|
||||
static std::pair<R MAME_ABI_CXX_MEMBER_CALL (*)(void const *, T...), void const *> resolve_base_member_function(
|
||||
Base const &object,
|
||||
R (Base::*func)(T...) const);
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
|
||||
/// \brief Dynamic derived class
|
||||
///
|
||||
/// Allows dynamically creating classes derived from a supplied base
|
||||
/// class and overriding virtual member functions. A class must meet a
|
||||
/// number of requirements to be suitable for use as a base class. Most
|
||||
/// of these requirements cannot be checked automatically. It is the
|
||||
/// developer's responsibility to ensure the requirements are met.
|
||||
///
|
||||
/// The dynamic derived class object must not be destroyed until after
|
||||
/// all instances of the class have been destroyed.
|
||||
///
|
||||
/// When destroying an instance of the dynamic derived class, the base
|
||||
/// class vtable is restored before the extra data destructor is called.
|
||||
/// This allows the extra data type to hold a smart pointer to the
|
||||
/// dynamic derived class so it can be cleaned up automatically when all
|
||||
/// instances are destroyed. However, it means you must keep in mind
|
||||
/// that the base class implementation of all virtual member functions
|
||||
/// will be restored before the extra data destructor is called.
|
||||
/// \tparam Base Base class for the dynamic derived class. Must meet
|
||||
/// the following requirements:
|
||||
/// * Must be a concrete class with at least one public constructor
|
||||
/// and a public destructor.
|
||||
/// * Must have at least one virtual member function.
|
||||
/// * Must not have any direct or indirect virtual base classes.
|
||||
/// * Must have no secondary virtual tables. This means the class and
|
||||
/// all of its direct and indirect base classes may only inherit
|
||||
/// virtual member functions from one base class at most.
|
||||
/// * If the class has a virtual destructor, it must be declared
|
||||
/// declared before any other virtual member functions in the least
|
||||
/// derived base class containing virtual member functions.
|
||||
/// \tparam Extra Extra data type, or \c void if not required. Must be
|
||||
/// a concrete type with at least one public constructor and a public
|
||||
/// destructor.
|
||||
/// \tparam VirtualCount The total number of virtual member functions of
|
||||
/// the base class, excluding the virtual destructor if present. This
|
||||
/// must be correct, and cannot be checked automatically. It is the
|
||||
/// developer's responsibility to ensure the value is correct. This
|
||||
/// potentially includes additional entries for overridden member
|
||||
/// functions with covariant return types and implicitly-declared
|
||||
/// assignment operators.
|
||||
template <class Base, typename Extra, std::size_t VirtualCount>
|
||||
class dynamic_derived_class : private detail::dynamic_derived_class_base
|
||||
{
|
||||
public:
|
||||
/// \brief Type used to store base class and extra data
|
||||
///
|
||||
/// Has a member \c base of the base class type, and a member
|
||||
/// \c extra of the extra data type if it is not \c void.
|
||||
///
|
||||
/// Provides \c resolve_base_member_function and
|
||||
/// \c call_base_member_function member functions to assist with
|
||||
/// calling the base implementation of overridden virtual member
|
||||
/// functions.
|
||||
using type = value_type<Base, Extra>;
|
||||
|
||||
/// \brief Smart pointer to instance
|
||||
///
|
||||
/// A unique pointer type suitable for taking ownership of an
|
||||
/// instance of the dynamic derived class.
|
||||
using pointer = typename destroyer<Base, Extra>::pointer_type;
|
||||
|
||||
dynamic_derived_class(dynamic_derived_class const &) = delete;
|
||||
dynamic_derived_class &operator=(dynamic_derived_class const &) = delete;
|
||||
|
||||
dynamic_derived_class(std::string_view name);
|
||||
dynamic_derived_class(dynamic_derived_class const &prototype, std::string_view name);
|
||||
|
||||
/// \brief Get type info for dynamic derived class
|
||||
///
|
||||
/// Gets a reference to the type info for the dynamic derived class.
|
||||
/// The reference remains valid and unique as long as the dynamic
|
||||
/// derived class is not destroyed.
|
||||
/// \return A reference to an object equivalent to \c std::type_info.
|
||||
std::type_info const &type_info() const
|
||||
{
|
||||
#if MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC
|
||||
return *reinterpret_cast<std::type_info const *>(m_type_info);
|
||||
#else
|
||||
return *reinterpret_cast<std::type_info const *>(&m_type_info);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename R, typename... T>
|
||||
void override_member_function(R (Base::*slot)(T...), R MAME_ABI_CXX_MEMBER_CALL (*func)(type &, T...));
|
||||
|
||||
template <typename R, typename... T>
|
||||
void override_member_function(R (Base::*slot)(T...) const, R MAME_ABI_CXX_MEMBER_CALL (*func)(type const &, T...));
|
||||
|
||||
template <typename R, typename... T>
|
||||
void restore_base_member_function(R (Base::*slot)(T...));
|
||||
|
||||
template <typename... T>
|
||||
pointer instantiate(type *&object, T &&... args);
|
||||
|
||||
private:
|
||||
static_assert(sizeof(std::uintptr_t) == sizeof(std::ptrdiff_t), "Pointer and pointer difference must be the same size");
|
||||
static_assert(sizeof(void *) == sizeof(void (*)()), "Code and data pointers must be the same size");
|
||||
static_assert(std::is_polymorphic_v<Base>, "Base class must be polymorphic");
|
||||
|
||||
static constexpr std::size_t FIRST_OVERRIDABLE_MEMBER_OFFSET = std::has_virtual_destructor_v<Base> ? VTABLE_DESTRUCTOR_ENTRIES : 0;
|
||||
static constexpr std::size_t VIRTUAL_MEMBER_FUNCTION_COUNT = VirtualCount + FIRST_OVERRIDABLE_MEMBER_OFFSET;
|
||||
static constexpr std::size_t VTABLE_SIZE = VTABLE_PREFIX_ENTRIES + (VIRTUAL_MEMBER_FUNCTION_COUNT * MEMBER_FUNCTION_SIZE);
|
||||
|
||||
void override_member_function(member_function_pointer_equiv &slot, std::uintptr_t func, std::size_t size);
|
||||
|
||||
std::array<std::uintptr_t, VTABLE_SIZE> m_vtable;
|
||||
std::bitset<VirtualCount> m_overridden;
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
|
||||
#endif // MAME_LIB_UTIL_DYNAMICCLASS_H
|
505
src/lib/util/dynamicclass.ipp
Normal file
505
src/lib/util/dynamicclass.ipp
Normal file
@ -0,0 +1,505 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Vas Crabb
|
||||
#ifndef MAME_LIB_UTIL_DYNAMICCLASS_IPP
|
||||
#define MAME_LIB_UTIL_DYNAMICCLASS_IPP
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "dynamicclass.h"
|
||||
|
||||
|
||||
namespace util {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/// \brief Get offset to saved base virtual table pointer from type info
|
||||
///
|
||||
/// Gets the offset to the saved base class virtual table pointer from
|
||||
/// the location the dynamic derived class virtual table entry used for
|
||||
/// recovery points to.
|
||||
/// \return Offset from the type info to saved base class virtual table
|
||||
/// pointer in bytes.
|
||||
inline std::ptrdiff_t dynamic_derived_class_base::base_vtable_offset()
|
||||
{
|
||||
#if MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC
|
||||
return 0;
|
||||
#else
|
||||
return
|
||||
reinterpret_cast<std::uint8_t *>(&reinterpret_cast<dynamic_derived_class_base *>(std::uintptr_t(0))->m_base_vtable) -
|
||||
reinterpret_cast<std::uint8_t *>(&reinterpret_cast<dynamic_derived_class_base *>(std::uintptr_t(0))->m_type_info);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// \brief Get base class virtual table pointer
|
||||
///
|
||||
/// Gets the base class virtual pointer for an instance of a dynamic
|
||||
/// derived class.
|
||||
/// \tparam Base The base class type (usually determined automatically).
|
||||
/// \param [in] object Base class member of dynamic derived class
|
||||
/// instance.
|
||||
/// \return Pointer to first virtual member function entry in base class
|
||||
/// virtual table.
|
||||
template <class Base>
|
||||
inline std::uintptr_t const *dynamic_derived_class_base::get_base_vptr(
|
||||
Base const &object)
|
||||
{
|
||||
auto const vptr = *reinterpret_cast<std::uintptr_t const *>(&object);
|
||||
auto const recovery = reinterpret_cast<std::uintptr_t const *>(vptr)[VTABLE_BASE_RECOVERY_INDEX];
|
||||
return *reinterpret_cast<std::uintptr_t const *const *>(recovery + base_vtable_offset());
|
||||
}
|
||||
|
||||
|
||||
/// \brief Restore base class virtual table pointer
|
||||
///
|
||||
/// Restores the base class virtual pointer in an instance of a dynamic
|
||||
/// derived class. Must be called before calling the base class
|
||||
/// destructor.
|
||||
/// \tparam Base The base class type (usually determined automatically).
|
||||
/// \param [in,out] object Base class member of dynamic derived class
|
||||
/// instance.
|
||||
template <class Base>
|
||||
inline void dynamic_derived_class_base::restore_base_vptr(
|
||||
Base &object)
|
||||
{
|
||||
auto &vptr = *reinterpret_cast<std::uintptr_t *>(&object);
|
||||
auto const recovery = reinterpret_cast<std::uintptr_t const *>(vptr)[VTABLE_BASE_RECOVERY_INDEX];
|
||||
vptr = *reinterpret_cast<std::uintptr_t const *>(recovery + base_vtable_offset());
|
||||
assert(reinterpret_cast<void const *>(vptr));
|
||||
}
|
||||
|
||||
|
||||
/// \brief Resolve pointer to base class member function
|
||||
///
|
||||
/// Given an instance and pointer to a base class member function, gets
|
||||
/// the adjusted \c this pointer and conventional function pointer.
|
||||
/// \tparam Base The base class type (usually determined automatically).
|
||||
/// \tparam R Return type of member function (usually determined
|
||||
/// automatically).
|
||||
/// \tparam T Parameter types expected by the member function (usually
|
||||
/// determined automatically).
|
||||
/// \param [in] object Base class member of dynamic derived class
|
||||
/// instance.
|
||||
/// \param [in] func Pointer to member function of base class.
|
||||
/// \return A \c std::pair containing the conventional function pointer
|
||||
/// and adjusted \c this pointer.
|
||||
/// \exception std::invalid_argument Thrown if the \p func argument is
|
||||
/// not a supported member function. This includes non-virtual member
|
||||
/// functions and virtual member functions that aren't supported for
|
||||
/// overriding when using the MSVC C++ ABI.
|
||||
template <typename Base, typename R, typename... T>
|
||||
inline std::pair<R MAME_ABI_CXX_MEMBER_CALL (*)(void *, T...), void *> dynamic_derived_class_base::resolve_base_member_function(
|
||||
Base &object,
|
||||
R (Base::*func)(T...))
|
||||
{
|
||||
static_assert(supported_return_type<R>::value, "Unsupported member function return type");
|
||||
member_function_pointer_pun_t<decltype(func)> thunk;
|
||||
thunk.ptr = func;
|
||||
#if MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC
|
||||
std::size_t const index = resolve_virtual_member_slot(thunk.equiv, sizeof(func));
|
||||
auto const vptr = reinterpret_cast<std::uintptr_t const *>(get_base_vptr(object));
|
||||
std::uintptr_t const* const entryptr = vptr + (index * MEMBER_FUNCTION_SIZE);
|
||||
return std::make_pair(
|
||||
MAME_ABI_CXX_VTABLE_FNDESC
|
||||
? reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void *, T...)>(std::uintptr_t(entryptr))
|
||||
: reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void *, T...)>(*entryptr),
|
||||
&object);
|
||||
#else
|
||||
if (thunk.equiv.is_virtual())
|
||||
{
|
||||
assert(!thunk.equiv.this_pointer_offset());
|
||||
auto const vptr = reinterpret_cast<std::uint8_t const *>(get_base_vptr(object));
|
||||
auto const entryptr = reinterpret_cast<std::uintptr_t const *>(vptr + thunk.equiv.virtual_table_entry_offset());
|
||||
return std::make_pair(
|
||||
MAME_ABI_CXX_VTABLE_FNDESC
|
||||
? reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void *, T...)>(std::uintptr_t(entryptr))
|
||||
: reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void *, T...)>(*entryptr),
|
||||
&object);
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::make_pair(
|
||||
reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void *, T...)>(thunk.equiv.function_pointer()),
|
||||
reinterpret_cast<std::uint8_t *>(&object) + thunk.equiv.this_pointer_offset());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename Base, typename R, typename... T>
|
||||
inline std::pair<R MAME_ABI_CXX_MEMBER_CALL (*)(void const *, T...), void const *> dynamic_derived_class_base::resolve_base_member_function(
|
||||
Base const &object,
|
||||
R (Base::*func)(T...) const)
|
||||
{
|
||||
static_assert(supported_return_type<R>::value, "Unsupported member function return type");
|
||||
member_function_pointer_pun_t<decltype(func)> thunk;
|
||||
thunk.ptr = func;
|
||||
#if MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC
|
||||
std::size_t const index = resolve_virtual_member_slot(thunk.equiv, sizeof(func));
|
||||
auto const vptr = reinterpret_cast<std::uintptr_t const *>(get_base_vptr(object));
|
||||
std::uintptr_t const* const entryptr = vptr + (index * MEMBER_FUNCTION_SIZE);
|
||||
return std::make_pair(
|
||||
MAME_ABI_CXX_VTABLE_FNDESC
|
||||
? reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void const *, T...)>(std::uintptr_t(entryptr))
|
||||
: reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void const *, T...)>(*entryptr),
|
||||
&object);
|
||||
#else
|
||||
if (thunk.equiv.is_virtual())
|
||||
{
|
||||
assert(!thunk.equiv.this_pointer_offset());
|
||||
auto const vptr = reinterpret_cast<std::uint8_t const *>(get_base_vptr(object));
|
||||
auto const entryptr = reinterpret_cast<std::uintptr_t const *>(vptr + thunk.equiv.virtual_table_entry_offset());
|
||||
return std::make_pair(
|
||||
MAME_ABI_CXX_VTABLE_FNDESC
|
||||
? reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void const *, T...)>(std::uintptr_t(entryptr))
|
||||
: reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void const *, T...)>(*entryptr),
|
||||
&object);
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::make_pair(
|
||||
reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void const *, T...)>(thunk.equiv.function_pointer()),
|
||||
reinterpret_cast<std::uint8_t const *>(&object) + thunk.equiv.this_pointer_offset());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// \brief Complete object destructor for dynamic derived class
|
||||
///
|
||||
/// Restores the base class virtual table pointer, calls the extra data
|
||||
/// and base class destructors, but does not free the memory occupied by
|
||||
/// the object. Used when the base class type has a virtual destructor
|
||||
/// to allow deleting instances of a dynamic derived class through
|
||||
/// pointers to the base class type.
|
||||
///
|
||||
/// Only used for the Itanium C++ ABI.
|
||||
/// \param [in] object Reference to the object to destroy.
|
||||
template <class Base, typename Extra>
|
||||
void MAME_ABI_CXX_MEMBER_CALL dynamic_derived_class_base::destroyer<Base, Extra, std::enable_if_t<std::has_virtual_destructor_v<Base> > >::complete_object_destructor(
|
||||
value_type<Base, Extra> &object)
|
||||
{
|
||||
restore_base_vptr(object.base);
|
||||
object.~value_type();
|
||||
}
|
||||
|
||||
|
||||
/// \brief Deleting destructor for dynamic derived class
|
||||
///
|
||||
/// Restores the base class virtual table pointer, calls the extra data
|
||||
/// and base class destructors, and frees the memory occupied by the
|
||||
/// object. Used when the base class type has a virtual destructor to
|
||||
/// allow deleting instances of a dynamic derived class through pointers
|
||||
/// to the base class type.
|
||||
///
|
||||
/// Only used for the Itanium C++ ABI.
|
||||
/// \param [in] object Pointer to the object to destroy.
|
||||
template <class Base, typename Extra>
|
||||
void MAME_ABI_CXX_MEMBER_CALL dynamic_derived_class_base::destroyer<Base, Extra, std::enable_if_t<std::has_virtual_destructor_v<Base> > >::deleting_destructor(
|
||||
value_type<Base, Extra> *object)
|
||||
{
|
||||
restore_base_vptr(object->base);
|
||||
delete object;
|
||||
}
|
||||
|
||||
|
||||
/// \brief Destructor for dynamic derived class
|
||||
///
|
||||
/// Restores the base class virtual table pointer, calls the extra data
|
||||
/// and base class destructors, and optionally frees the memory occupied
|
||||
/// by the object. Used when the base class type has a virtual
|
||||
/// destructor to allow deleting instances of a dynamic derived class
|
||||
/// through pointers to the base class type.
|
||||
///
|
||||
/// Only used for the MSVC C++ ABI.
|
||||
/// \param [in] object Pointer to the object to destroy.
|
||||
/// \param [in] flags If bit 0 is set, the memory occupied by the object
|
||||
/// will be freed.
|
||||
/// \return The supplied object pointer.
|
||||
template <class Base, typename Extra>
|
||||
void *MAME_ABI_CXX_MEMBER_CALL dynamic_derived_class_base::destroyer<Base, Extra, std::enable_if_t<std::has_virtual_destructor_v<Base> > >::scalar_deleting_destructor(
|
||||
value_type<Base, Extra> *object,
|
||||
unsigned int flags)
|
||||
{
|
||||
restore_base_vptr(object->base);
|
||||
object->~value_type();
|
||||
if (flags & 1)
|
||||
operator delete (static_cast<void *>(object));
|
||||
return object;
|
||||
}
|
||||
|
||||
|
||||
/// \brief Deleter for dynamic derived classes
|
||||
///
|
||||
/// Restores the base class virtual table pointer, calls the extra data
|
||||
/// and base class destructors, and frees the memory occupied by the
|
||||
/// object. Used to delete instances of a dynamic derived class when
|
||||
/// the base class type does not have a virtual destructor.
|
||||
/// \param [in] object Pointer to the object to destroy.
|
||||
template <class Base, typename Extra>
|
||||
void dynamic_derived_class_base::destroyer<Base, Extra, std::enable_if_t<!std::has_virtual_destructor_v<Base> > >::operator()(
|
||||
Base *object) const
|
||||
{
|
||||
restore_base_vptr(*object);
|
||||
delete reinterpret_cast<value_type<Base, Extra> *>(object);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
/// \brief Create a dynamic derived class
|
||||
///
|
||||
/// Creates a new dynamic derived class. No base member functions are
|
||||
/// overridden initially.
|
||||
/// \param [in] name The unmangled name for the new dynamic derived
|
||||
/// class. This will be mangled for use in the generated type info.
|
||||
template <class Base, typename Extra, std::size_t VirtualCount>
|
||||
dynamic_derived_class<Base, Extra, VirtualCount>::dynamic_derived_class(
|
||||
std::string_view name) :
|
||||
detail::dynamic_derived_class_base(name)
|
||||
{
|
||||
#if MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC
|
||||
m_vtable[0] = std::uintptr_t(&m_base_vtable); // for restoring the base vtable
|
||||
#else
|
||||
m_type_info.base_type = &typeid(Base);
|
||||
m_vtable[0] = 0; // offset to top
|
||||
m_vtable[1] = std::uintptr_t(&m_type_info); // type info
|
||||
#endif
|
||||
if constexpr (std::has_virtual_destructor_v<Base>)
|
||||
{
|
||||
if (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC)
|
||||
{
|
||||
if (MAME_ABI_CXX_VTABLE_FNDESC)
|
||||
{
|
||||
std::copy_n(
|
||||
reinterpret_cast<std::uintptr_t const *>(std::uintptr_t(&destroyer<Base, Extra>::scalar_deleting_destructor)),
|
||||
MEMBER_FUNCTION_SIZE,
|
||||
&m_vtable[VTABLE_PREFIX_ENTRIES]);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_vtable[VTABLE_PREFIX_ENTRIES] = std::uintptr_t(&destroyer<Base, Extra>::scalar_deleting_destructor);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (MAME_ABI_CXX_VTABLE_FNDESC)
|
||||
{
|
||||
std::copy_n(
|
||||
reinterpret_cast<std::uintptr_t const *>(std::uintptr_t(&destroyer<Base, Extra>::complete_object_destructor)),
|
||||
MEMBER_FUNCTION_SIZE,
|
||||
&m_vtable[VTABLE_PREFIX_ENTRIES]);
|
||||
std::copy_n(
|
||||
reinterpret_cast<std::uintptr_t const *>(std::uintptr_t(&destroyer<Base, Extra>::deleting_destructor)),
|
||||
MEMBER_FUNCTION_SIZE,
|
||||
&m_vtable[VTABLE_PREFIX_ENTRIES + MEMBER_FUNCTION_SIZE]);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_vtable[VTABLE_PREFIX_ENTRIES] = std::uintptr_t(&destroyer<Base, Extra>::complete_object_destructor);
|
||||
m_vtable[VTABLE_PREFIX_ENTRIES + 1] = std::uintptr_t(&destroyer<Base, Extra>::deleting_destructor);
|
||||
}
|
||||
}
|
||||
}
|
||||
std::fill(
|
||||
std::next(m_vtable.begin(), VTABLE_PREFIX_ENTRIES + (FIRST_OVERRIDABLE_MEMBER_OFFSET * MEMBER_FUNCTION_SIZE)),
|
||||
m_vtable.end(),
|
||||
std::uintptr_t(static_cast<void *>(nullptr)));
|
||||
}
|
||||
|
||||
|
||||
/// \brief Create a dynamic derived class using a prototype
|
||||
///
|
||||
/// Creates a new dynamic derived class using an existing dynamic
|
||||
/// derived class as a prototype. Overridden base class member
|
||||
/// functions are inherited from the current state of the prototype.
|
||||
/// The new dynamic derived class is not affected by any future
|
||||
/// modifications to the prototype. The prototype may be destroyed
|
||||
/// safely before the new dynamic derived class is destroyed (provided
|
||||
/// all its instances are destroyed first).
|
||||
/// \param [in] prototype The dynamic derived class to use as a
|
||||
/// prototype.
|
||||
/// \param [in] name The unmangled name for the new dynamic derived
|
||||
/// class. This will be mangled for use in the generated type info.
|
||||
template <class Base, typename Extra, std::size_t VirtualCount>
|
||||
dynamic_derived_class<Base, Extra, VirtualCount>::dynamic_derived_class(
|
||||
dynamic_derived_class const &prototype,
|
||||
std::string_view name) :
|
||||
detail::dynamic_derived_class_base(name),
|
||||
m_vtable(prototype.m_vtable),
|
||||
m_overridden(prototype.m_overridden)
|
||||
{
|
||||
m_base_vtable = prototype.m_base_vtable;
|
||||
#if MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC
|
||||
m_vtable[0] = std::uintptr_t(&m_base_vtable); // for restoring the base vtable
|
||||
#else
|
||||
m_type_info.base_type = &typeid(Base);
|
||||
m_vtable[1] = std::uintptr_t(&m_type_info); // type info
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/// \brief Override a virtual member function
|
||||
///
|
||||
/// Replace the virtual table entry for the specified base member
|
||||
/// function with the supplied function. This applies to existing
|
||||
/// instances as well as newly created instances. Note that if you are
|
||||
/// using some technique to resolve pointers to virtual member functions
|
||||
/// in advance, resolved pointers may not reflect the change.
|
||||
/// \tparam R Return type of member function to override (usually
|
||||
/// determined automatically).
|
||||
/// \tparam T Parameter types expected by the member function to
|
||||
/// override (usually determined automatically).
|
||||
/// \param [in] slot A pointer to the base class member function to
|
||||
/// override. Must be a pointer to a virtual member function.
|
||||
/// \param [in] func A pointer to the function to use to override the
|
||||
/// base class member function.
|
||||
/// \exception std::invalid_argument Thrown if the \p slot argument is
|
||||
/// not a supported virtual member function.
|
||||
/// \sa restore_base_member_function
|
||||
template <class Base, typename Extra, std::size_t VirtualCount>
|
||||
template <typename R, typename... T>
|
||||
void dynamic_derived_class<Base, Extra, VirtualCount>::override_member_function(
|
||||
R (Base::*slot)(T...),
|
||||
R MAME_ABI_CXX_MEMBER_CALL (*func)(type &, T...))
|
||||
{
|
||||
static_assert(supported_return_type<R>::value, "Unsupported member function return type");
|
||||
member_function_pointer_pun_t<decltype(slot)> thunk;
|
||||
thunk.ptr = slot;
|
||||
override_member_function(thunk.equiv, std::uintptr_t(func), sizeof(func));
|
||||
}
|
||||
|
||||
template <class Base, typename Extra, std::size_t VirtualCount>
|
||||
template <typename R, typename... T>
|
||||
void dynamic_derived_class<Base, Extra, VirtualCount>::override_member_function(
|
||||
R (Base::*slot)(T...) const,
|
||||
R MAME_ABI_CXX_MEMBER_CALL (*func)(type const &, T...))
|
||||
{
|
||||
static_assert(supported_return_type<R>::value, "Unsupported member function return type");
|
||||
member_function_pointer_pun_t<decltype(slot)> thunk;
|
||||
thunk.ptr = slot;
|
||||
override_member_function(thunk.equiv, std::uintptr_t(func), sizeof(func));
|
||||
}
|
||||
|
||||
|
||||
/// \brief Restore the base implementation of a member function
|
||||
///
|
||||
/// If the specified virtual member function of the base class has been
|
||||
/// overridden, restore the base class implementation. This applies to
|
||||
/// existing instances as well as newly created instances. Note that if
|
||||
/// you are using some technique to resolve pointers to virtual member
|
||||
/// functions in advance, resolved pointers may not reflect the change.
|
||||
/// \tparam R Return type of member function to restore (usually
|
||||
/// determined automatically).
|
||||
/// \tparam T Parameter types expected by the member function to
|
||||
/// to restore (usually determined automatically).
|
||||
/// \param [in] slot A pointer to the base class member function to
|
||||
/// restore. Must be a pointer to a virtual member function.
|
||||
/// \exception std::invalid_argument Thrown if the \p slot argument is
|
||||
/// not a supported virtual member function.
|
||||
/// \sa override_member_function
|
||||
template <class Base, typename Extra, std::size_t VirtualCount>
|
||||
template <typename R, typename... T>
|
||||
void dynamic_derived_class<Base, Extra, VirtualCount>::restore_base_member_function(
|
||||
R (Base::*slot)(T...))
|
||||
{
|
||||
member_function_pointer_pun_t<decltype(slot)> thunk;
|
||||
thunk.ptr = slot;
|
||||
std::size_t const index = resolve_virtual_member_slot(thunk.equiv, sizeof(slot));
|
||||
assert(index < VIRTUAL_MEMBER_FUNCTION_COUNT);
|
||||
assert(FIRST_OVERRIDABLE_MEMBER_OFFSET <= index);
|
||||
if (m_overridden[index - FIRST_OVERRIDABLE_MEMBER_OFFSET] && m_base_vtable)
|
||||
{
|
||||
std::copy_n(
|
||||
reinterpret_cast<std::uintptr_t const *>(m_base_vtable) + (index * MEMBER_FUNCTION_SIZE),
|
||||
MEMBER_FUNCTION_SIZE,
|
||||
&m_vtable[VTABLE_PREFIX_ENTRIES + (index * MEMBER_FUNCTION_SIZE)]);
|
||||
}
|
||||
m_overridden[index - FIRST_OVERRIDABLE_MEMBER_OFFSET] = false;
|
||||
}
|
||||
|
||||
|
||||
/// \brief Create a new instance
|
||||
///
|
||||
/// Creates a new instance of the dynamic derived class constructed with
|
||||
/// the supplied arguments.
|
||||
/// \tparam T Constructor argument types (usually determined
|
||||
/// automatically).
|
||||
/// \param [out] object Receives an pointer to the object storing the
|
||||
/// base type and extra data.
|
||||
/// \param [in] args Constructor arguments for the object to be
|
||||
/// instantiated. If the first argument is
|
||||
/// \c std::piecewise_construct, it must be followed by two tuples
|
||||
/// containing the arguments to forward to the base class and extra
|
||||
/// data constructors, respectively. If the first argument is not
|
||||
/// \c std::piecewise_construct, all the arguments are forwarded to
|
||||
/// the base class constructor.
|
||||
/// \return A unique pointer to the new instance.
|
||||
template <class Base, typename Extra, std::size_t VirtualCount>
|
||||
template <typename... T>
|
||||
typename dynamic_derived_class<Base, Extra, VirtualCount>::pointer dynamic_derived_class<Base, Extra, VirtualCount>::instantiate(
|
||||
type *&object,
|
||||
T &&... args)
|
||||
{
|
||||
std::unique_ptr<type> result(new type(std::forward<T>(args)...));
|
||||
auto &vptr = *reinterpret_cast<std::uintptr_t const **>(&result->base);
|
||||
if (!m_base_vtable)
|
||||
{
|
||||
assert(std::uintptr_t(result.get()) == std::uintptr_t(&result->base));
|
||||
m_base_vtable = vptr;
|
||||
if (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC)
|
||||
m_vtable[1] = vptr[-1]; // use the base class complete object locator - too hard to fake
|
||||
for (std::size_t i = 0; VirtualCount > i; ++i)
|
||||
{
|
||||
if (!m_overridden[i])
|
||||
{
|
||||
std::size_t const offset = (i + FIRST_OVERRIDABLE_MEMBER_OFFSET) * MEMBER_FUNCTION_SIZE;
|
||||
std::copy_n(vptr + offset, MEMBER_FUNCTION_SIZE, &m_vtable[VTABLE_PREFIX_ENTRIES + offset]);
|
||||
}
|
||||
}
|
||||
}
|
||||
vptr = &m_vtable[VTABLE_PREFIX_ENTRIES];
|
||||
object = result.get();
|
||||
return pointer(&result.release()->base);
|
||||
}
|
||||
|
||||
|
||||
/// \brief Replace member function in virtual table
|
||||
///
|
||||
/// Does the actual work involved in replacing a virtual table entry to
|
||||
/// override a virtual member function of the base class, avoiding
|
||||
/// duplication between overloads.
|
||||
/// \param [in] slot Internal representation of pointer to a virtual
|
||||
/// member function of the base class. May be modified.
|
||||
/// \param [in] func A pointer to the function to use to override the
|
||||
/// base class member function reinterpreted as an unsigned integer of
|
||||
/// equivalent size.
|
||||
/// \param [in] size Size of the member function pointer type for the
|
||||
/// \p slot argument.
|
||||
/// \exception std::invalid_argument Thrown if the \p slot argument is
|
||||
/// not a supported virtual member function.
|
||||
template <class Base, typename Extra, std::size_t VirtualCount>
|
||||
void dynamic_derived_class<Base, Extra, VirtualCount>::override_member_function(
|
||||
member_function_pointer_equiv &slot,
|
||||
std::uintptr_t func,
|
||||
std::size_t size)
|
||||
{
|
||||
std::size_t const index = resolve_virtual_member_slot(slot, size);
|
||||
assert(index < VIRTUAL_MEMBER_FUNCTION_COUNT);
|
||||
assert(FIRST_OVERRIDABLE_MEMBER_OFFSET <= index);
|
||||
m_overridden[index - FIRST_OVERRIDABLE_MEMBER_OFFSET] = true;
|
||||
if (MAME_ABI_CXX_VTABLE_FNDESC)
|
||||
{
|
||||
std::copy_n(
|
||||
reinterpret_cast<std::uintptr_t const *>(func),
|
||||
MEMBER_FUNCTION_SIZE,
|
||||
&m_vtable[VTABLE_PREFIX_ENTRIES + (index * MEMBER_FUNCTION_SIZE)]);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_vtable[VTABLE_PREFIX_ENTRIES + index] = func;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
|
||||
#endif // MAME_LIB_UTIL_DYNAMICCLASS_IPP
|
@ -10,9 +10,10 @@
|
||||
|
||||
#include "flac.h"
|
||||
|
||||
#include "corefile.h"
|
||||
#include "ioprocs.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
|
||||
|
||||
@ -37,7 +38,7 @@ flac_encoder::flac_encoder(void *buffer, uint32_t buflength)
|
||||
}
|
||||
|
||||
|
||||
flac_encoder::flac_encoder(util::core_file &file)
|
||||
flac_encoder::flac_encoder(util::random_write &file)
|
||||
{
|
||||
init_common();
|
||||
reset(file);
|
||||
@ -102,7 +103,7 @@ bool flac_encoder::reset(void *buffer, uint32_t buflength)
|
||||
// reset - reset state with new file parameters
|
||||
//-------------------------------------------------
|
||||
|
||||
bool flac_encoder::reset(util::core_file &file)
|
||||
bool flac_encoder::reset(util::random_write &file)
|
||||
{
|
||||
// configure the output
|
||||
m_compressed_start = nullptr;
|
||||
@ -187,7 +188,16 @@ uint32_t flac_encoder::finish()
|
||||
{
|
||||
// process the data and return the amount written
|
||||
FLAC__stream_encoder_finish(m_encoder);
|
||||
return (m_file != nullptr) ? m_file->tell() : m_compressed_offset;
|
||||
if (m_file)
|
||||
{
|
||||
std::uint64_t result = 0;
|
||||
m_file->tell(result); // TODO: check for error result, consider this may be too big for uint32_t
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_compressed_offset;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -232,29 +242,30 @@ FLAC__StreamEncoderWriteStatus flac_encoder::write_callback(const FLAC__byte buf
|
||||
size_t offset = 0;
|
||||
while (offset < bytes)
|
||||
{
|
||||
// if we're ignoring, continue to do so
|
||||
if (m_ignore_bytes != 0)
|
||||
{
|
||||
// if we're ignoring, continue to do so
|
||||
size_t ignore = std::min(bytes - offset, size_t(m_ignore_bytes));
|
||||
offset += ignore;
|
||||
m_ignore_bytes -= ignore;
|
||||
}
|
||||
|
||||
// if we haven't hit the end of metadata, process a new piece
|
||||
else if (!m_found_audio)
|
||||
{
|
||||
// if we haven't hit the end of metadata, process a new piece
|
||||
assert(bytes - offset >= 4);
|
||||
m_found_audio = ((buffer[offset] & 0x80) != 0);
|
||||
m_ignore_bytes = (buffer[offset + 1] << 16) | (buffer[offset + 2] << 8) | buffer[offset + 3];
|
||||
offset += 4;
|
||||
}
|
||||
|
||||
// otherwise process as audio data and copy to the output
|
||||
else
|
||||
{
|
||||
// otherwise process as audio data and copy to the output
|
||||
int count = bytes - offset;
|
||||
if (m_file != nullptr)
|
||||
m_file->write(buffer, count);
|
||||
if (m_file)
|
||||
{
|
||||
size_t actual;
|
||||
m_file->write(buffer, count, actual); // TODO: check for errors
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_compressed_offset + count <= m_compressed_length)
|
||||
@ -316,7 +327,7 @@ flac_decoder::flac_decoder(const void *buffer, uint32_t length, const void *buff
|
||||
// flac_decoder - constructor
|
||||
//-------------------------------------------------
|
||||
|
||||
flac_decoder::flac_decoder(util::core_file &file)
|
||||
flac_decoder::flac_decoder(util::read_stream &file)
|
||||
: m_decoder(FLAC__stream_decoder_new()),
|
||||
m_file(&file),
|
||||
m_compressed_offset(0),
|
||||
@ -422,7 +433,7 @@ bool flac_decoder::reset(uint32_t sample_rate, uint8_t num_channels, uint32_t bl
|
||||
// reset - reset state with new file parameter
|
||||
//-------------------------------------------------
|
||||
|
||||
bool flac_decoder::reset(util::core_file &file)
|
||||
bool flac_decoder::reset(util::read_stream &file)
|
||||
{
|
||||
m_file = &file;
|
||||
m_compressed_start = nullptr;
|
||||
@ -517,12 +528,11 @@ FLAC__StreamDecoderReadStatus flac_decoder::read_callback(FLAC__byte buffer[], s
|
||||
{
|
||||
uint32_t expected = *bytes;
|
||||
|
||||
// if a file, just read
|
||||
if (m_file != nullptr)
|
||||
*bytes = m_file->read(buffer, expected);
|
||||
|
||||
// otherwise, copy from memory
|
||||
else
|
||||
if (m_file) // if a file, just read
|
||||
{
|
||||
m_file->read(buffer, expected, *bytes); // TODO: check for errors
|
||||
}
|
||||
else // otherwise, copy from memory
|
||||
{
|
||||
// copy from primary buffer first
|
||||
uint32_t outputpos = 0;
|
||||
|
@ -13,6 +13,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "utilfwd.h"
|
||||
|
||||
#include <FLAC/all.h>
|
||||
|
||||
#include <cstdint>
|
||||
@ -22,8 +24,6 @@
|
||||
// TYPE DEFINITIONS
|
||||
//**************************************************************************
|
||||
|
||||
namespace util { class core_file; }
|
||||
|
||||
// ======================> flac_encoder
|
||||
|
||||
class flac_encoder
|
||||
@ -32,7 +32,7 @@ public:
|
||||
// construction/destruction
|
||||
flac_encoder();
|
||||
flac_encoder(void *buffer, uint32_t buflength);
|
||||
flac_encoder(util::core_file &file);
|
||||
flac_encoder(util::random_write &file);
|
||||
~flac_encoder();
|
||||
|
||||
// configuration
|
||||
@ -48,7 +48,7 @@ public:
|
||||
// reset
|
||||
bool reset();
|
||||
bool reset(void *buffer, uint32_t buflength);
|
||||
bool reset(util::core_file &file);
|
||||
bool reset(util::random_write &file);
|
||||
|
||||
// encode a buffer
|
||||
bool encode_interleaved(const int16_t *samples, uint32_t samples_per_channel, bool swap_endian = false);
|
||||
@ -65,7 +65,7 @@ private:
|
||||
|
||||
// internal state
|
||||
FLAC__StreamEncoder * m_encoder; // actual encoder
|
||||
util::core_file * m_file; // output file
|
||||
util::random_write * m_file; // output file
|
||||
uint32_t m_compressed_offset; // current offset with the compressed stream
|
||||
FLAC__byte * m_compressed_start; // start of compressed data
|
||||
uint32_t m_compressed_length; // length of the compressed stream
|
||||
@ -90,7 +90,7 @@ public:
|
||||
// construction/destruction
|
||||
flac_decoder();
|
||||
flac_decoder(const void *buffer, uint32_t length, const void *buffer2 = nullptr, uint32_t length2 = 0);
|
||||
flac_decoder(util::core_file &file);
|
||||
flac_decoder(util::read_stream &file);
|
||||
~flac_decoder();
|
||||
|
||||
// getters (valid after reset)
|
||||
@ -105,7 +105,7 @@ public:
|
||||
bool reset();
|
||||
bool reset(const void *buffer, uint32_t length, const void *buffer2 = nullptr, uint32_t length2 = 0);
|
||||
bool reset(uint32_t sample_rate, uint8_t num_channels, uint32_t block_size, const void *buffer, uint32_t length);
|
||||
bool reset(util::core_file &file);
|
||||
bool reset(util::read_stream &file);
|
||||
|
||||
// decode to a buffer; num_samples must be a multiple of the block size
|
||||
bool decode_interleaved(int16_t *samples, uint32_t num_samples, bool swap_endian = false);
|
||||
@ -125,8 +125,8 @@ private:
|
||||
static void error_callback_static(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
|
||||
|
||||
// output state
|
||||
FLAC__StreamDecoder * m_decoder; // actual encoder
|
||||
util::core_file * m_file; // output file
|
||||
FLAC__StreamDecoder * m_decoder; // actual decoder
|
||||
util::read_stream * m_file; // input file
|
||||
uint32_t m_sample_rate; // decoded sample rate
|
||||
uint8_t m_channels; // decoded number of channels
|
||||
uint8_t m_bits_per_sample; // decoded bits per sample
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
#include "harddisk.h"
|
||||
|
||||
#include "corefile.h"
|
||||
#include "ioprocs.h"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
@ -21,9 +21,9 @@
|
||||
|
||||
struct hard_disk_file
|
||||
{
|
||||
chd_file * chd; /* CHD file */
|
||||
util::core_file *fhandle; /* core_file if not a CHD */
|
||||
hard_disk_info info; /* hard disk info */
|
||||
chd_file * chd; // CHD file
|
||||
util::random_read_write * fhandle; // file if not a CHD
|
||||
hard_disk_info info; // hard disk info
|
||||
};
|
||||
|
||||
|
||||
@ -73,11 +73,16 @@ hard_disk_file *hard_disk_open(chd_file *chd)
|
||||
return file;
|
||||
}
|
||||
|
||||
hard_disk_file *hard_disk_open(util::core_file &corefile, uint32_t skipoffs)
|
||||
hard_disk_file *hard_disk_open(util::random_read_write &corefile, uint32_t skipoffs)
|
||||
{
|
||||
// bail if getting the file length fails
|
||||
std::uint64_t length;
|
||||
if (corefile.length(length))
|
||||
return nullptr;
|
||||
|
||||
hard_disk_file *file;
|
||||
|
||||
/* allocate memory for the hard disk file */
|
||||
// allocate memory for the hard disk file
|
||||
file = (hard_disk_file *)malloc(sizeof(hard_disk_file));
|
||||
if (file == nullptr)
|
||||
return nullptr;
|
||||
@ -91,7 +96,7 @@ hard_disk_file *hard_disk_open(util::core_file &corefile, uint32_t skipoffs)
|
||||
file->info.fileoffset = skipoffs;
|
||||
|
||||
// attempt to guess geometry in case this is an ATA situation
|
||||
for (uint32_t totalsectors = (corefile.size() - skipoffs) / file->info.sectorbytes; ; totalsectors++)
|
||||
for (uint32_t totalsectors = (length - skipoffs) / file->info.sectorbytes; ; totalsectors++)
|
||||
for (uint32_t cursectors = 63; cursectors > 1; cursectors--)
|
||||
if (totalsectors % cursectors == 0)
|
||||
{
|
||||
@ -118,9 +123,7 @@ hard_disk_file *hard_disk_open(util::core_file &corefile, uint32_t skipoffs)
|
||||
void hard_disk_close(hard_disk_file *file)
|
||||
{
|
||||
if (file->fhandle)
|
||||
{
|
||||
file->fhandle->flush();
|
||||
}
|
||||
|
||||
free(file);
|
||||
}
|
||||
@ -164,7 +167,7 @@ hard_disk_info *hard_disk_get_info(hard_disk_file *file)
|
||||
-------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @fn uint32_t hard_disk_read(hard_disk_file *file, uint32_t lbasector, void *buffer)
|
||||
* @fn bool hard_disk_read(hard_disk_file *file, uint32_t lbasector, void *buffer)
|
||||
*
|
||||
* @brief Hard disk read.
|
||||
*
|
||||
@ -172,22 +175,24 @@ hard_disk_info *hard_disk_get_info(hard_disk_file *file)
|
||||
* @param lbasector The sector number (Linear Block Address) to read.
|
||||
* @param buffer The buffer where the hard disk data will be placed.
|
||||
*
|
||||
* @return An uint32_t.
|
||||
* @return True if the operation succeeded
|
||||
*/
|
||||
|
||||
uint32_t hard_disk_read(hard_disk_file *file, uint32_t lbasector, void *buffer)
|
||||
bool hard_disk_read(hard_disk_file *file, uint32_t lbasector, void *buffer)
|
||||
{
|
||||
std::error_condition err;
|
||||
if (file->chd)
|
||||
{
|
||||
std::error_condition err = file->chd->read_units(lbasector, buffer);
|
||||
err = file->chd->read_units(lbasector, buffer);
|
||||
return !err;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t actual = 0;
|
||||
file->fhandle->seek(file->info.fileoffset + (lbasector * file->info.sectorbytes), SEEK_SET);
|
||||
actual = file->fhandle->read(buffer, file->info.sectorbytes);
|
||||
return (actual == file->info.sectorbytes);
|
||||
size_t actual = 0;
|
||||
err = file->fhandle->seek(file->info.fileoffset + (lbasector * file->info.sectorbytes), SEEK_SET);
|
||||
if (!err)
|
||||
err = file->fhandle->read(buffer, file->info.sectorbytes, actual);
|
||||
return !err && (actual == file->info.sectorbytes);
|
||||
}
|
||||
}
|
||||
|
||||
@ -198,7 +203,7 @@ uint32_t hard_disk_read(hard_disk_file *file, uint32_t lbasector, void *buffer)
|
||||
-------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @fn uint32_t hard_disk_write(hard_disk_file *file, uint32_t lbasector, const void *buffer)
|
||||
* @fn bool hard_disk_write(hard_disk_file *file, uint32_t lbasector, const void *buffer)
|
||||
*
|
||||
* @brief Hard disk write.
|
||||
*
|
||||
@ -206,25 +211,28 @@ uint32_t hard_disk_read(hard_disk_file *file, uint32_t lbasector, void *buffer)
|
||||
* @param lbasector The sector number (Linear Block Address) to write.
|
||||
* @param buffer The buffer containing the data to write.
|
||||
*
|
||||
* @return An uint32_t.
|
||||
* @return True if the operation succeeded
|
||||
*/
|
||||
|
||||
uint32_t hard_disk_write(hard_disk_file *file, uint32_t lbasector, const void *buffer)
|
||||
bool hard_disk_write(hard_disk_file *file, uint32_t lbasector, const void *buffer)
|
||||
{
|
||||
std::error_condition err;
|
||||
if (file->chd)
|
||||
{
|
||||
std::error_condition err = file->chd->write_units(lbasector, buffer);
|
||||
err = file->chd->write_units(lbasector, buffer);
|
||||
return !err;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t actual = 0;
|
||||
file->fhandle->seek(file->info.fileoffset + (lbasector * file->info.sectorbytes), SEEK_SET);
|
||||
actual = file->fhandle->write(buffer, file->info.sectorbytes);
|
||||
return (actual == file->info.sectorbytes);
|
||||
size_t actual = 0;
|
||||
err = file->fhandle->seek(file->info.fileoffset + (lbasector * file->info.sectorbytes), SEEK_SET);
|
||||
if (!err)
|
||||
err = file->fhandle->write(buffer, file->info.sectorbytes, actual);
|
||||
return !err && (actual == file->info.sectorbytes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------
|
||||
hard_disk_set_block_size - sets the block size
|
||||
for a non-CHD-backed hard disk (a bare file).
|
||||
@ -259,6 +267,6 @@ bool hard_disk_set_block_size(hard_disk_file *file, uint32_t blocksize)
|
||||
else
|
||||
{
|
||||
file->info.sectorbytes = blocksize;
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ struct hard_disk_info
|
||||
***************************************************************************/
|
||||
|
||||
hard_disk_file *hard_disk_open(chd_file *chd);
|
||||
hard_disk_file *hard_disk_open(util::core_file &corefile, uint32_t skipoffs);
|
||||
hard_disk_file *hard_disk_open(util::random_read_write &corefile, uint32_t skipoffs);
|
||||
|
||||
void hard_disk_close(hard_disk_file *file);
|
||||
|
||||
@ -50,7 +50,7 @@ hard_disk_info *hard_disk_get_info(hard_disk_file *file);
|
||||
|
||||
bool hard_disk_set_block_size(hard_disk_file *file, uint32_t blocksize);
|
||||
|
||||
uint32_t hard_disk_read(hard_disk_file *file, uint32_t lbasector, void *buffer);
|
||||
uint32_t hard_disk_write(hard_disk_file *file, uint32_t lbasector, const void *buffer);
|
||||
bool hard_disk_read(hard_disk_file *file, uint32_t lbasector, void *buffer);
|
||||
bool hard_disk_write(hard_disk_file *file, uint32_t lbasector, const void *buffer);
|
||||
|
||||
#endif // MAME_LIB_UTIL_HARDDISK_H
|
||||
|
@ -736,296 +736,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// helper class for holding a core_file and closing it (or not) as necessary
|
||||
|
||||
class core_file_adapter_base : public virtual random_access
|
||||
{
|
||||
public:
|
||||
virtual ~core_file_adapter_base()
|
||||
{
|
||||
if (m_close)
|
||||
delete m_file;
|
||||
}
|
||||
|
||||
virtual std::error_condition seek(std::int64_t offset, int whence) noexcept override
|
||||
{
|
||||
if (!m_file->seek(offset, whence))
|
||||
return std::error_condition();
|
||||
else
|
||||
return std::errc::invalid_argument; // TODO: better error reporting for core_file
|
||||
}
|
||||
|
||||
virtual std::error_condition tell(std::uint64_t &result) noexcept override
|
||||
{
|
||||
// no error reporting
|
||||
result = m_file->tell();
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
virtual std::error_condition length(std::uint64_t &result) noexcept override
|
||||
{
|
||||
// no error reporting
|
||||
result = m_file->size();
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
protected:
|
||||
core_file_adapter_base(core_file::ptr &&file) noexcept : m_file(file.release()), m_close(true)
|
||||
{
|
||||
assert(m_file);
|
||||
}
|
||||
|
||||
core_file_adapter_base(core_file &file) noexcept : m_file(&file), m_close(false)
|
||||
{
|
||||
}
|
||||
|
||||
core_file &file() noexcept
|
||||
{
|
||||
return *m_file;
|
||||
}
|
||||
|
||||
private:
|
||||
core_file *const m_file;
|
||||
bool const m_close;
|
||||
};
|
||||
|
||||
|
||||
// core_file read implementation
|
||||
|
||||
class core_file_read_adapter : public core_file_adapter_base, public virtual random_read
|
||||
{
|
||||
public:
|
||||
core_file_read_adapter(core_file::ptr &&file) noexcept : core_file_adapter_base(std::move(file))
|
||||
{
|
||||
}
|
||||
|
||||
core_file_read_adapter(core_file &file) noexcept : core_file_adapter_base(file)
|
||||
{
|
||||
}
|
||||
|
||||
virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override
|
||||
{
|
||||
// TODO: should the client have to deal with reading less than expected even if EOF isn't hit?
|
||||
if (std::numeric_limits<std::uint32_t>::max() < length)
|
||||
{
|
||||
actual = 0U;
|
||||
return std::errc::invalid_argument;
|
||||
}
|
||||
|
||||
// no way to distinguish between EOF and error
|
||||
actual = file().read(buffer, std::uint32_t(length));
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override
|
||||
{
|
||||
actual = 0U;
|
||||
|
||||
// TODO: should the client have to deal with reading less than expected even if EOF isn't hit?
|
||||
if (std::numeric_limits<std::uint32_t>::max() < length)
|
||||
return std::errc::invalid_argument;
|
||||
|
||||
// no error reporting for tell
|
||||
std::uint64_t const oldpos = file().tell();
|
||||
if (file().seek(offset, SEEK_SET))
|
||||
{
|
||||
file().seek(oldpos, SEEK_SET);
|
||||
return std::errc::invalid_argument; // TODO: better error reporting for core_file
|
||||
}
|
||||
|
||||
// no way to distinguish between EOF and error
|
||||
actual = file().read(buffer, std::uint32_t(length));
|
||||
|
||||
if (!file().seek(oldpos, SEEK_SET))
|
||||
return std::error_condition();
|
||||
else
|
||||
return std::errc::invalid_argument; // TODO: better error reporting for core_file
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// core_file read/write implementation
|
||||
|
||||
class core_file_read_write_adapter : public core_file_read_adapter, public random_read_write
|
||||
{
|
||||
public:
|
||||
using core_file_read_adapter::core_file_read_adapter;
|
||||
|
||||
virtual std::error_condition finalize() noexcept override
|
||||
{
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
virtual std::error_condition flush() noexcept override
|
||||
{
|
||||
return file().flush();
|
||||
}
|
||||
|
||||
virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override
|
||||
{
|
||||
actual = 0U;
|
||||
while (length)
|
||||
{
|
||||
std::uint32_t const chunk = std::min<std::common_type_t<std::uint32_t, std::size_t> >(std::numeric_limits<std::uint32_t>::max(), length);
|
||||
std::uint32_t const written = file().write(buffer, chunk);
|
||||
actual += written;
|
||||
if (written < chunk)
|
||||
return std::errc::io_error; // TODO: better error reporting for core_file
|
||||
buffer = reinterpret_cast<std::uint8_t const *>(buffer) + written;
|
||||
length -= written;
|
||||
}
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override
|
||||
{
|
||||
actual = 0U;
|
||||
|
||||
// no error reporting for tell
|
||||
std::uint64_t const oldpos = file().tell();
|
||||
if (file().seek(offset, SEEK_SET))
|
||||
{
|
||||
file().seek(oldpos, SEEK_SET);
|
||||
return std::errc::invalid_argument; // TODO: better error reporting for core_file
|
||||
}
|
||||
|
||||
std::error_condition err;
|
||||
while (!err && length)
|
||||
{
|
||||
std::uint32_t const chunk = std::min<std::common_type_t<std::uint32_t, std::size_t> >(std::numeric_limits<std::uint32_t>::max(), length);
|
||||
std::uint32_t const written = file().write(buffer, chunk);
|
||||
actual += written;
|
||||
if (written < chunk)
|
||||
{
|
||||
err = std::errc::io_error; // TODO: better error reporting for core_file
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer = reinterpret_cast<std::uint8_t const *>(buffer) + written;
|
||||
length -= written;
|
||||
}
|
||||
}
|
||||
|
||||
if (!file().seek(oldpos, SEEK_SET) || err)
|
||||
return err;
|
||||
else
|
||||
return std::errc::invalid_argument; // TODO: better error reporting for core_file
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// core_file helper that fills space when writing past the end-of-file
|
||||
|
||||
class core_file_read_write_filler : public random_read_fill_wrapper<core_file_read_write_adapter>
|
||||
{
|
||||
public:
|
||||
core_file_read_write_filler(core_file::ptr &&file, std::uint8_t fill) noexcept : random_read_fill_wrapper<core_file_read_write_adapter>(std::move(file))
|
||||
{
|
||||
set_filler(fill);
|
||||
}
|
||||
|
||||
core_file_read_write_filler(core_file &file, std::uint8_t fill) noexcept : random_read_fill_wrapper<core_file_read_write_adapter>(file)
|
||||
{
|
||||
set_filler(fill);
|
||||
}
|
||||
|
||||
virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override
|
||||
{
|
||||
// no error reporting for tell or size
|
||||
std::uint64_t const offset = file().tell();
|
||||
std::uint64_t endpos = file().size();
|
||||
if (endpos < offset)
|
||||
{
|
||||
if (file().seek(endpos, SEEK_SET))
|
||||
{
|
||||
file().seek(offset, SEEK_SET);
|
||||
actual = 0U;
|
||||
return std::errc::invalid_argument; // TODO: better error reporting for core_file
|
||||
}
|
||||
std::uint8_t block[1024];
|
||||
std::fill_n(
|
||||
block,
|
||||
std::min<std::common_type_t<std::size_t, std::uint64_t> >(std::size(block), offset - endpos),
|
||||
get_filler());
|
||||
do
|
||||
{
|
||||
std::uint32_t const chunk = std::min<std::common_type_t<std::size_t, std::uint64_t> >(sizeof(block), offset - endpos);
|
||||
std::uint32_t const filled = file().write(block, chunk);
|
||||
endpos += filled;
|
||||
if (chunk != filled)
|
||||
{
|
||||
file().seek(offset, SEEK_SET);
|
||||
actual = 0U;
|
||||
return std::errc::io_error; // TODO: better error reporting for core_file
|
||||
}
|
||||
}
|
||||
while (endpos < offset);
|
||||
}
|
||||
|
||||
return core_file_read_write_adapter::write(buffer, length, actual);
|
||||
}
|
||||
|
||||
virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override
|
||||
{
|
||||
actual = 0U;
|
||||
|
||||
// no error reporting for tell
|
||||
std::uint64_t const oldpos = file().tell();
|
||||
if (file().seek(0, SEEK_END))
|
||||
{
|
||||
file().seek(oldpos, SEEK_SET);
|
||||
return std::errc::invalid_argument; // TODO: better error reporting for core_file
|
||||
}
|
||||
std::uint64_t endpos = file().tell();
|
||||
|
||||
std::error_condition err;
|
||||
if (endpos > offset)
|
||||
{
|
||||
if (file().seek(offset, SEEK_SET))
|
||||
err = std::errc::invalid_argument; // TODO: better error reporting for core_file
|
||||
}
|
||||
else if (endpos < offset)
|
||||
{
|
||||
std::uint8_t block[1024];
|
||||
std::fill_n(
|
||||
block,
|
||||
std::min<std::common_type_t<std::size_t, std::uint64_t> >(std::size(block), offset - endpos),
|
||||
get_filler());
|
||||
do
|
||||
{
|
||||
std::uint32_t const chunk = std::min<std::common_type_t<std::size_t, std::uint64_t> >(sizeof(block), offset - endpos);
|
||||
std::uint32_t const filled = file().write(block, chunk);
|
||||
endpos += filled;
|
||||
if (chunk != filled)
|
||||
err = std::errc::io_error; // TODO: better error reporting for core_file
|
||||
}
|
||||
while (!err && (endpos < offset));
|
||||
}
|
||||
|
||||
while (!err && length)
|
||||
{
|
||||
std::uint32_t const chunk = std::min<std::common_type_t<std::uint32_t, std::size_t> >(std::numeric_limits<std::uint32_t>::max(), length);
|
||||
std::uint32_t const written = file().write(buffer, chunk);
|
||||
actual += written;
|
||||
if (written < chunk)
|
||||
{
|
||||
err = std::errc::io_error; // TODO: better error reporting for core_file
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer = reinterpret_cast<std::uint8_t const *>(buffer) + written;
|
||||
length -= written;
|
||||
}
|
||||
}
|
||||
|
||||
if (!file().seek(oldpos, SEEK_SET) || err)
|
||||
return err;
|
||||
else
|
||||
return std::errc::invalid_argument; // TODO: better error reporting for core_file
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
@ -1181,68 +891,4 @@ random_read_write::ptr osd_file_read_write(osd_file &file) noexcept
|
||||
return random_read_write::ptr(new (std::nothrow) osd_file_read_write_adapter(file));
|
||||
}
|
||||
|
||||
|
||||
// creating core_file read adapters
|
||||
|
||||
random_read::ptr core_file_read(core_file::ptr &&file) noexcept
|
||||
{
|
||||
random_read::ptr result;
|
||||
if (file)
|
||||
result.reset(new (std::nothrow) core_file_read_adapter(std::move(file)));
|
||||
return result;
|
||||
}
|
||||
|
||||
random_read::ptr core_file_read(core_file::ptr &&file, std::uint8_t filler) noexcept
|
||||
{
|
||||
std::unique_ptr<random_read_fill_wrapper<core_file_read_adapter> > result;
|
||||
if (file)
|
||||
result.reset(new (std::nothrow) decltype(result)::element_type(std::move(file)));
|
||||
if (result)
|
||||
result->set_filler(filler);
|
||||
return result;
|
||||
}
|
||||
|
||||
random_read::ptr core_file_read(core_file &file) noexcept
|
||||
{
|
||||
return random_read::ptr(new (std::nothrow) core_file_read_adapter(file));
|
||||
}
|
||||
|
||||
random_read::ptr core_file_read(core_file &file, std::uint8_t filler) noexcept
|
||||
{
|
||||
std::unique_ptr<random_read_fill_wrapper<core_file_read_adapter> > result;
|
||||
result.reset(new (std::nothrow) decltype(result)::element_type(file));
|
||||
if (result)
|
||||
result->set_filler(filler);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// creating core_file read/write adapters
|
||||
|
||||
random_read_write::ptr core_file_read_write(core_file::ptr &&file) noexcept
|
||||
{
|
||||
random_read_write::ptr result;
|
||||
if (file)
|
||||
result.reset(new (std::nothrow) core_file_read_write_adapter(std::move(file)));
|
||||
return result;
|
||||
}
|
||||
|
||||
random_read_write::ptr core_file_read_write(core_file::ptr &&file, std::uint8_t filler) noexcept
|
||||
{
|
||||
random_read_write::ptr result;
|
||||
if (file)
|
||||
result.reset(new (std::nothrow) core_file_read_write_filler(std::move(file), filler));
|
||||
return result;
|
||||
}
|
||||
|
||||
random_read_write::ptr core_file_read_write(core_file &file) noexcept
|
||||
{
|
||||
return random_read_write::ptr(new (std::nothrow) core_file_read_write_adapter(file));
|
||||
}
|
||||
|
||||
random_read_write::ptr core_file_read_write(core_file &file, std::uint8_t filler) noexcept
|
||||
{
|
||||
return random_read_write::ptr(new (std::nothrow) core_file_read_write_filler(file, filler));
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
|
@ -253,16 +253,6 @@ random_read::ptr osd_file_read(osd_file &file) noexcept;
|
||||
random_read_write::ptr osd_file_read_write(std::unique_ptr<osd_file> &&file) noexcept;
|
||||
random_read_write::ptr osd_file_read_write(osd_file &file) noexcept;
|
||||
|
||||
random_read::ptr core_file_read(std::unique_ptr<core_file> &&file) noexcept;
|
||||
random_read::ptr core_file_read(std::unique_ptr<core_file> &&file, std::uint8_t filler) noexcept;
|
||||
random_read::ptr core_file_read(core_file &file) noexcept;
|
||||
random_read::ptr core_file_read(core_file &file, std::uint8_t filler) noexcept;
|
||||
|
||||
random_read_write::ptr core_file_read_write(std::unique_ptr<core_file> &&file) noexcept;
|
||||
random_read_write::ptr core_file_read_write(std::unique_ptr<core_file> &&file, std::uint8_t filler) noexcept;
|
||||
random_read_write::ptr core_file_read_write(core_file &file) noexcept;
|
||||
random_read_write::ptr core_file_read_write(core_file &file, std::uint8_t filler) noexcept;
|
||||
|
||||
} // namespace util
|
||||
|
||||
#endif // MAME_LIB_UTIL_IOPROCS_H
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "ioprocsfilter.h"
|
||||
|
||||
#include "ioprocs.h"
|
||||
#include "ioprocsfill.h"
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
@ -335,7 +336,7 @@ private:
|
||||
template <typename T>
|
||||
class filter_base
|
||||
{
|
||||
protected:
|
||||
public:
|
||||
filter_base(std::unique_ptr<T> &&object) noexcept : m_object(object.release()), m_owned(true)
|
||||
{
|
||||
assert(m_object);
|
||||
@ -345,6 +346,7 @@ protected:
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
~filter_base()
|
||||
{
|
||||
if (m_owned)
|
||||
@ -362,6 +364,111 @@ private:
|
||||
};
|
||||
|
||||
|
||||
// helper for forwarding to a read stream
|
||||
|
||||
template <typename T>
|
||||
class read_stream_proxy : public virtual read_stream, public T
|
||||
{
|
||||
public:
|
||||
using T::T;
|
||||
|
||||
virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override
|
||||
{
|
||||
return this->object().read(buffer, length, actual);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// helper for forwarding to a write stream
|
||||
|
||||
template <typename T>
|
||||
class write_stream_proxy : public virtual write_stream, public T
|
||||
{
|
||||
public:
|
||||
using T::T;
|
||||
|
||||
virtual std::error_condition finalize() noexcept override
|
||||
{
|
||||
return this->object().finalize();
|
||||
}
|
||||
|
||||
virtual std::error_condition flush() noexcept override
|
||||
{
|
||||
return this->object().flush();
|
||||
}
|
||||
|
||||
virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override
|
||||
{
|
||||
return this->object().write(buffer, length, actual);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// helper for forwarding to random-access storage
|
||||
|
||||
template <typename T>
|
||||
class random_access_proxy : public virtual random_access, public T
|
||||
{
|
||||
public:
|
||||
using T::T;
|
||||
|
||||
virtual std::error_condition seek(std::int64_t offset, int whence) noexcept override
|
||||
{
|
||||
return this->object().seek(offset, whence);
|
||||
}
|
||||
|
||||
virtual std::error_condition tell(std::uint64_t &result) noexcept override
|
||||
{
|
||||
return this->object().tell(result);
|
||||
}
|
||||
|
||||
virtual std::error_condition length(std::uint64_t &result) noexcept override
|
||||
{
|
||||
return this->object().length(result);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// helper for forwarding to random-access read storage
|
||||
|
||||
template <typename T>
|
||||
class random_read_proxy : public virtual random_read, public read_stream_proxy<T>
|
||||
{
|
||||
public:
|
||||
using read_stream_proxy<T>::read_stream_proxy;
|
||||
|
||||
virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override
|
||||
{
|
||||
return this->object().read_at(offset, buffer, length, actual);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// helper for forwarding to random-access write storage
|
||||
|
||||
template <typename T>
|
||||
class random_write_proxy : public virtual random_write, public write_stream_proxy<T>
|
||||
{
|
||||
public:
|
||||
using write_stream_proxy<T>::write_stream_proxy;
|
||||
|
||||
virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override
|
||||
{
|
||||
return this->object().write_at(offset, buffer, length, actual);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// helper for forwarding to random-access read/write storage
|
||||
|
||||
template <typename T>
|
||||
class random_read_write_proxy : public random_read_write, protected random_write_proxy<random_read_proxy<T> >
|
||||
{
|
||||
public:
|
||||
using random_write_proxy<random_read_proxy<T> >::random_write_proxy;
|
||||
};
|
||||
|
||||
|
||||
// filter for decompressing deflated data
|
||||
|
||||
template <typename Stream>
|
||||
@ -553,6 +660,91 @@ private:
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
// creating filters that fill unread space
|
||||
|
||||
read_stream::ptr read_stream_fill(read_stream::ptr &&stream, std::uint8_t filler) noexcept
|
||||
{
|
||||
std::unique_ptr<read_stream_fill_wrapper<read_stream_proxy<filter_base<read_stream> > > > result;
|
||||
if (stream)
|
||||
result.reset(new (std::nothrow) decltype(result)::element_type(std::move(stream)));
|
||||
if (result)
|
||||
result->set_filler(filler);
|
||||
return result;
|
||||
}
|
||||
|
||||
random_read::ptr random_read_fill(random_read::ptr &&stream, std::uint8_t filler) noexcept
|
||||
{
|
||||
std::unique_ptr<random_read_fill_wrapper<random_read_proxy<random_access_proxy<filter_base<random_read> > > > > result;
|
||||
if (stream)
|
||||
result.reset(new (std::nothrow) decltype(result)::element_type(std::move(stream)));
|
||||
if (result)
|
||||
result->set_filler(filler);
|
||||
return result;
|
||||
}
|
||||
|
||||
read_stream::ptr read_stream_fill(read_stream &stream, std::uint8_t filler) noexcept
|
||||
{
|
||||
std::unique_ptr<read_stream_fill_wrapper<read_stream_proxy<filter_base<read_stream> > > > result;
|
||||
result.reset(new (std::nothrow) decltype(result)::element_type(stream));
|
||||
if (result)
|
||||
result->set_filler(filler);
|
||||
return result;
|
||||
}
|
||||
|
||||
random_read::ptr random_read_fill(random_read &stream, std::uint8_t filler) noexcept
|
||||
{
|
||||
std::unique_ptr<random_read_fill_wrapper<random_read_proxy<random_access_proxy<filter_base<random_read> > > > > result;
|
||||
result.reset(new (std::nothrow) decltype(result)::element_type(stream));
|
||||
if (result)
|
||||
result->set_filler(filler);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// creating filters that fill unwritten space
|
||||
|
||||
random_write::ptr random_write_fill(random_write::ptr &&stream, std::uint8_t filler) noexcept
|
||||
{
|
||||
std::unique_ptr<random_write_fill_wrapper<random_write_proxy<random_access_proxy<filter_base<random_write> > > > > result;
|
||||
if (stream)
|
||||
result.reset(new (std::nothrow) decltype(result)::element_type(std::move(stream)));
|
||||
if (result)
|
||||
result->set_filler(filler);
|
||||
return result;
|
||||
}
|
||||
|
||||
random_write::ptr random_write_fill(random_write &stream, std::uint8_t filler) noexcept
|
||||
{
|
||||
std::unique_ptr<random_write_fill_wrapper<random_write_proxy<random_access_proxy<filter_base<random_write> > > > > result;
|
||||
result.reset(new (std::nothrow) decltype(result)::element_type(stream));
|
||||
if (result)
|
||||
result->set_filler(filler);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// creating filters that fill unread/unwritten space
|
||||
|
||||
random_read_write::ptr random_read_write_fill(random_read_write::ptr &&stream, std::uint8_t filler) noexcept
|
||||
{
|
||||
std::unique_ptr<random_read_write_fill_wrapper<random_read_write_proxy<random_access_proxy<filter_base<random_read_write> > > > > result;
|
||||
if (stream)
|
||||
result.reset(new (std::nothrow) decltype(result)::element_type(std::move(stream)));
|
||||
if (result)
|
||||
result->set_filler(filler);
|
||||
return result;
|
||||
}
|
||||
|
||||
random_read_write::ptr random_read_write_fill(random_read_write &stream, std::uint8_t filler) noexcept
|
||||
{
|
||||
std::unique_ptr<random_read_write_fill_wrapper<random_read_write_proxy<random_access_proxy<filter_base<random_read_write> > > > > result;
|
||||
result.reset(new (std::nothrow) decltype(result)::element_type(stream));
|
||||
if (result)
|
||||
result->set_filler(filler);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// creating decompressing filters
|
||||
|
||||
read_stream::ptr zlib_read(read_stream::ptr &&stream, std::size_t read_chunk) noexcept
|
||||
|
@ -18,6 +18,17 @@
|
||||
|
||||
namespace util {
|
||||
|
||||
std::unique_ptr<read_stream> read_stream_fill(std::unique_ptr<read_stream> &&stream, std::uint8_t filler) noexcept;
|
||||
std::unique_ptr<random_read> random_read_fill(std::unique_ptr<random_read> &&stream, std::uint8_t filler) noexcept;
|
||||
std::unique_ptr<read_stream> read_stream_fill(read_stream &stream, std::uint8_t filler) noexcept;
|
||||
std::unique_ptr<random_read> random_read_fill(random_read &stream, std::uint8_t filler) noexcept;
|
||||
|
||||
std::unique_ptr<random_write> random_write_fill(std::unique_ptr<random_write> &&stream, std::uint8_t filler) noexcept;
|
||||
std::unique_ptr<random_write> random_write_fill(random_write &stream, std::uint8_t filler) noexcept;
|
||||
|
||||
std::unique_ptr<random_read_write> random_read_write_fill(std::unique_ptr<random_read_write> &&stream, std::uint8_t filler) noexcept;
|
||||
std::unique_ptr<random_read_write> random_read_write_fill(random_read_write &stream, std::uint8_t filler) noexcept;
|
||||
|
||||
std::unique_ptr<read_stream> zlib_read(std::unique_ptr<read_stream> &&stream, std::size_t read_chunk) noexcept;
|
||||
std::unique_ptr<read_stream> zlib_read(std::unique_ptr<random_read> &&stream, std::size_t read_chunk) noexcept;
|
||||
std::unique_ptr<read_stream> zlib_read(read_stream &stream, std::size_t read_chunk) noexcept;
|
||||
|
@ -11,6 +11,8 @@
|
||||
#include "msdib.h"
|
||||
|
||||
#include "coretmpl.h"
|
||||
#include "ioprocs.h"
|
||||
|
||||
#include "eminline.h"
|
||||
#include "osdcore.h"
|
||||
|
||||
@ -91,7 +93,7 @@ union bitmap_headers
|
||||
};
|
||||
|
||||
|
||||
bool dib_parse_mask(uint32_t mask, unsigned &shift, unsigned &bits)
|
||||
bool dib_parse_mask(std::uint32_t mask, unsigned &shift, unsigned &bits)
|
||||
{
|
||||
shift = count_leading_zeros_32(mask);
|
||||
mask <<= shift;
|
||||
@ -113,7 +115,7 @@ void dib_truncate_channel(unsigned &shift, unsigned &bits)
|
||||
}
|
||||
|
||||
|
||||
uint8_t dib_splat_sample(uint8_t val, unsigned bits)
|
||||
std::uint8_t dib_splat_sample(std::uint8_t val, unsigned bits)
|
||||
{
|
||||
assert(8U >= bits);
|
||||
for (val <<= (8U - bits); bits && (8U > bits); bits <<= 1)
|
||||
@ -122,11 +124,13 @@ uint8_t dib_splat_sample(uint8_t val, unsigned bits)
|
||||
}
|
||||
|
||||
|
||||
msdib_error dib_read_file_header(core_file &fp, std::uint32_t &filelen)
|
||||
msdib_error dib_read_file_header(read_stream &fp, std::uint32_t &filelen)
|
||||
{
|
||||
std::size_t actual;
|
||||
|
||||
// the bitmap file header doesn't use natural alignment
|
||||
bitmap_file_header file_header;
|
||||
if (fp.read(file_header, sizeof(file_header)) != sizeof(file_header))
|
||||
if (fp.read(file_header, sizeof(file_header), actual) || (sizeof(file_header) != actual))
|
||||
{
|
||||
LOG("Error reading DIB file header\n");
|
||||
return msdib_error::FILE_TRUNCATED;
|
||||
@ -161,7 +165,7 @@ msdib_error dib_read_file_header(core_file &fp, std::uint32_t &filelen)
|
||||
|
||||
|
||||
msdib_error dib_read_bitmap_header(
|
||||
core_file &fp,
|
||||
random_read &fp,
|
||||
bitmap_headers &header,
|
||||
unsigned &palette_bytes,
|
||||
bool &indexed,
|
||||
@ -170,6 +174,8 @@ msdib_error dib_read_bitmap_header(
|
||||
std::size_t &row_bytes,
|
||||
std::uint32_t length)
|
||||
{
|
||||
std::size_t actual;
|
||||
|
||||
// check that these things haven't been padded somehow
|
||||
static_assert(sizeof(bitmap_core_header) == 12U, "compiler has applied padding to bitmap_core_header");
|
||||
static_assert(sizeof(bitmap_info_header) == 56U, "compiler has applied padding to bitmap_info_header");
|
||||
@ -179,7 +185,7 @@ msdib_error dib_read_bitmap_header(
|
||||
if (sizeof(header.core) > length)
|
||||
return msdib_error::FILE_TRUNCATED;
|
||||
std::memset(&header, 0, sizeof(header));
|
||||
if (fp.read(&header.core.size, sizeof(header.core.size)) != sizeof(header.core.size))
|
||||
if (fp.read(&header.core.size, sizeof(header.core.size), actual) || (sizeof(header.core.size) != actual))
|
||||
{
|
||||
LOG("Error reading DIB header size (length %u)\n", length);
|
||||
return msdib_error::FILE_TRUNCATED;
|
||||
@ -209,12 +215,18 @@ msdib_error dib_read_bitmap_header(
|
||||
{
|
||||
palette_bytes = 3U;
|
||||
std::uint32_t const header_read(std::min<std::uint32_t>(header.core.size, sizeof(header.core)) - sizeof(header.core.size));
|
||||
if (fp.read(&header.core.width, header_read) != header_read)
|
||||
if (fp.read(&header.core.width, header_read, actual) || (header_read != actual))
|
||||
{
|
||||
LOG("Error reading DIB core header from image data (%u bytes)\n", length);
|
||||
return msdib_error::FILE_TRUNCATED;
|
||||
}
|
||||
fp.seek(header.core.size - sizeof(header.core.size) - header_read, SEEK_CUR);
|
||||
if (fp.seek(header.core.size - sizeof(header.core.size) - header_read, SEEK_CUR))
|
||||
{
|
||||
LOG(
|
||||
"Error seeking past additional DIB header data (%u bytes)\n",
|
||||
header.core.size - sizeof(header.core.size) - header_read);
|
||||
return msdib_error::FILE_ERROR;
|
||||
}
|
||||
header.core.width = little_endianize_int16(header.core.width);
|
||||
header.core.height = little_endianize_int16(header.core.height);
|
||||
header.core.planes = little_endianize_int16(header.core.planes);
|
||||
@ -256,12 +268,18 @@ msdib_error dib_read_bitmap_header(
|
||||
{
|
||||
palette_bytes = 4U;
|
||||
std::uint32_t const header_read(std::min<std::uint32_t>(header.info.size, sizeof(header.info)) - sizeof(header.info.size));
|
||||
if (fp.read(&header.info.width, header_read) != header_read)
|
||||
if (fp.read(&header.info.width, header_read, actual) || (header_read != actual))
|
||||
{
|
||||
LOG("Error reading DIB info header from image data (%u bytes)\n", length);
|
||||
return msdib_error::FILE_TRUNCATED;
|
||||
}
|
||||
fp.seek(header.info.size - sizeof(header.info.size) - header_read, SEEK_CUR);
|
||||
if (fp.seek(header.info.size - sizeof(header.info.size) - header_read, SEEK_CUR))
|
||||
{
|
||||
LOG(
|
||||
"Error seeking past additional DIB header data (%u bytes)\n",
|
||||
header.info.size - sizeof(header.info.size) - header_read);
|
||||
return msdib_error::FILE_ERROR;
|
||||
}
|
||||
header.info.width = little_endianize_int32(header.info.width);
|
||||
header.info.height = little_endianize_int32(header.info.height);
|
||||
header.info.planes = little_endianize_int16(header.info.planes);
|
||||
@ -379,7 +397,7 @@ msdib_error dib_read_bitmap_header(
|
||||
|
||||
|
||||
|
||||
msdib_error msdib_verify_header(core_file &fp)
|
||||
msdib_error msdib_verify_header(random_read &fp)
|
||||
{
|
||||
msdib_error err;
|
||||
|
||||
@ -420,7 +438,7 @@ msdib_error msdib_verify_header(core_file &fp)
|
||||
}
|
||||
|
||||
|
||||
msdib_error msdib_read_bitmap(core_file &fp, bitmap_argb32 &bitmap)
|
||||
msdib_error msdib_read_bitmap(random_read &fp, bitmap_argb32 &bitmap)
|
||||
{
|
||||
std::uint32_t file_length;
|
||||
msdib_error const headerr(dib_read_file_header(fp, file_length));
|
||||
@ -431,9 +449,10 @@ msdib_error msdib_read_bitmap(core_file &fp, bitmap_argb32 &bitmap)
|
||||
}
|
||||
|
||||
|
||||
msdib_error msdib_read_bitmap_data(core_file &fp, bitmap_argb32 &bitmap, std::uint32_t length, std::uint32_t dirheight)
|
||||
msdib_error msdib_read_bitmap_data(random_read &fp, bitmap_argb32 &bitmap, std::uint32_t length, std::uint32_t dirheight)
|
||||
{
|
||||
// read the bitmap header
|
||||
std::size_t actual;
|
||||
bitmap_headers header;
|
||||
unsigned palette_bytes;
|
||||
bool indexed;
|
||||
@ -477,17 +496,18 @@ msdib_error msdib_read_bitmap_data(core_file &fp, bitmap_argb32 &bitmap, std::ui
|
||||
if (indexed)
|
||||
{
|
||||
// read palette and convert
|
||||
std::unique_ptr<std::uint8_t []> palette_data;
|
||||
try { palette_data.reset(new std::uint8_t [palette_size]); }
|
||||
catch (std::bad_alloc const &) { return msdib_error::OUT_OF_MEMORY; }
|
||||
if (fp.read(palette_data.get(), palette_size) != palette_size)
|
||||
std::unique_ptr<std::uint8_t []> palette_data(new (std::nothrow) std::uint8_t [palette_size]);
|
||||
if (!palette_data)
|
||||
return msdib_error::OUT_OF_MEMORY;
|
||||
if (fp.read(palette_data.get(), palette_size, actual) || (palette_size != actual))
|
||||
{
|
||||
LOG("Error reading palette from DIB image data (%u bytes)\n", length);
|
||||
return msdib_error::FILE_TRUNCATED;
|
||||
}
|
||||
std::size_t const palette_usable(std::min<std::size_t>(palette_entries, std::size_t(1) << header.info.bpp));
|
||||
try { palette.reset(new rgb_t [palette_usable]); }
|
||||
catch (std::bad_alloc const &) { return msdib_error::OUT_OF_MEMORY; }
|
||||
palette.reset(new (std::nothrow) rgb_t [palette_usable]);
|
||||
if (!palette)
|
||||
return msdib_error::OUT_OF_MEMORY;
|
||||
std::uint8_t const *ptr(palette_data.get());
|
||||
for (std::size_t i = 0; palette_usable > i; ++i, ptr += palette_bytes)
|
||||
palette[i] = rgb_t(ptr[2], ptr[1], ptr[0]);
|
||||
@ -496,7 +516,13 @@ msdib_error msdib_read_bitmap_data(core_file &fp, bitmap_argb32 &bitmap, std::ui
|
||||
{
|
||||
// skip over the palette if necessary
|
||||
if (palette_entries)
|
||||
fp.seek(palette_bytes * palette_entries, SEEK_CUR);
|
||||
{
|
||||
if (fp.seek(palette_bytes * palette_entries, SEEK_CUR))
|
||||
{
|
||||
LOG("Error seeking past DIB palette data (%u bytes)\n", palette_bytes * palette_entries);
|
||||
return msdib_error::FILE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
// convert masks to shifts
|
||||
bool const masks_contiguous(
|
||||
@ -545,14 +571,14 @@ msdib_error msdib_read_bitmap_data(core_file &fp, bitmap_argb32 &bitmap, std::ui
|
||||
}
|
||||
|
||||
// allocate the bitmap and process row data
|
||||
std::unique_ptr<std::uint8_t []> row_data;
|
||||
try {row_data.reset(new std::uint8_t [row_bytes]); }
|
||||
catch (std::bad_alloc const &) { return msdib_error::OUT_OF_MEMORY; }
|
||||
std::unique_ptr<std::uint8_t []> row_data(new (std::nothrow) std::uint8_t [row_bytes]);
|
||||
if (!row_data)
|
||||
return msdib_error::OUT_OF_MEMORY;
|
||||
bitmap.allocate(header.info.width, header.info.height);
|
||||
int const y_inc(top_down ? 1 : -1);
|
||||
for (std::int32_t i = 0, y = top_down ? 0 : (header.info.height - 1); header.info.height > i; ++i, y += y_inc)
|
||||
{
|
||||
if (fp.read(row_data.get(), row_bytes) != row_bytes)
|
||||
if (fp.read(row_data.get(), row_bytes, actual) || (row_bytes != actual))
|
||||
{
|
||||
LOG("Error reading DIB row %d data from image data\n", i);
|
||||
return msdib_error::FILE_TRUNCATED;
|
||||
@ -615,7 +641,7 @@ msdib_error msdib_read_bitmap_data(core_file &fp, bitmap_argb32 &bitmap, std::ui
|
||||
{
|
||||
for (std::int32_t i = 0, y = top_down ? 0 : (header.info.height - 1); header.info.height > i; ++i, y += y_inc)
|
||||
{
|
||||
if (fp.read(row_data.get(), mask_row_bytes) != mask_row_bytes)
|
||||
if (fp.read(row_data.get(), mask_row_bytes, actual) || (mask_row_bytes != actual))
|
||||
{
|
||||
LOG("Error reading DIB mask row %d data from image data\n", i);
|
||||
return msdib_error::FILE_TRUNCATED;
|
||||
|
@ -13,7 +13,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "bitmap.h"
|
||||
#include "corefile.h"
|
||||
#include "utilfwd.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
@ -36,9 +36,9 @@ enum class msdib_error
|
||||
UNSUPPORTED_FORMAT
|
||||
};
|
||||
|
||||
msdib_error msdib_verify_header(core_file &fp);
|
||||
msdib_error msdib_read_bitmap(core_file &fp, bitmap_argb32 &bitmap);
|
||||
msdib_error msdib_read_bitmap_data(core_file &fp, bitmap_argb32 &bitmap, std::uint32_t length, std::uint32_t dirheight = 0U);
|
||||
msdib_error msdib_verify_header(random_read &fp);
|
||||
msdib_error msdib_read_bitmap(random_read &fp, bitmap_argb32 &bitmap);
|
||||
msdib_error msdib_read_bitmap_data(random_read &fp, bitmap_argb32 &bitmap, std::uint32_t length, std::uint32_t dirheight = 0U);
|
||||
|
||||
} // namespace util
|
||||
|
||||
|
@ -0,0 +1,3 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Vas Crabb
|
||||
#include "path.h"
|
@ -10,13 +10,16 @@
|
||||
|
||||
#include "png.h"
|
||||
|
||||
#include "osdcomm.h"
|
||||
#include "ioprocs.h"
|
||||
#include "unicode.h"
|
||||
|
||||
#include "osdcomm.h"
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
@ -102,6 +105,38 @@ inline void put_16bit(uint8_t *v, uint16_t data) { *reinterpret_cast<uint16_t *>
|
||||
inline void put_32bit(uint8_t *v, uint32_t data) { *reinterpret_cast<uint32_t *>(v) = big_endianize_int32(data); }
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
TYPE DEFINITIONS
|
||||
***************************************************************************/
|
||||
|
||||
class png_category_impl : public std::error_category
|
||||
{
|
||||
public:
|
||||
virtual char const *name() const noexcept override { return "png"; }
|
||||
|
||||
virtual std::string message(int condition) const override
|
||||
{
|
||||
using namespace std::literals;
|
||||
static std::string_view const s_messages[] = {
|
||||
"No error"sv,
|
||||
"Unknown filter algorithm"sv,
|
||||
"Bad file signature"sv,
|
||||
"Decompression error"sv,
|
||||
"Image file truncated"sv,
|
||||
"Image file corrupt"sv,
|
||||
"Unknown chunk type"sv,
|
||||
"Compression error"sv,
|
||||
"Unsupported image format"sv };
|
||||
if ((0 <= condition) && (std::size(s_messages) > condition))
|
||||
return std::string(s_messages[condition]);
|
||||
else
|
||||
return "Unknown error"s;
|
||||
}
|
||||
};
|
||||
|
||||
png_category_impl const f_png_category_instance;
|
||||
|
||||
|
||||
class png_private
|
||||
{
|
||||
private:
|
||||
@ -120,7 +155,7 @@ private:
|
||||
std::unique_ptr<std::uint8_t []> data;
|
||||
};
|
||||
|
||||
png_error process(std::list<image_data_chunk> const &idata)
|
||||
std::error_condition process(std::list<image_data_chunk> const &idata)
|
||||
{
|
||||
// do some basic checks for unsupported images
|
||||
if (!pnginfo.bit_depth || (std::size(samples) <= pnginfo.color_type) || !samples[pnginfo.color_type])
|
||||
@ -137,14 +172,15 @@ private:
|
||||
pass_offset[pass + 1] = pass_offset[pass] + get_pass_bytes(pass);
|
||||
|
||||
// allocate memory for the filtered image
|
||||
try { pnginfo.image.reset(new std::uint8_t [pass_offset[pass_count]]); }
|
||||
catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
|
||||
pnginfo.image.reset(new (std::nothrow) std::uint8_t [pass_offset[pass_count]]);
|
||||
if (!pnginfo.image)
|
||||
return std::errc::not_enough_memory;
|
||||
|
||||
// decompress image data
|
||||
png_error error = png_error::NONE;
|
||||
std::error_condition error;
|
||||
error = decompress(idata, pass_offset[pass_count]);
|
||||
std::uint32_t const bpp(get_bytes_per_pixel());
|
||||
for (unsigned pass = 0; (pass_count > pass) && (png_error::NONE == error); ++pass)
|
||||
for (unsigned pass = 0; (pass_count > pass) && !error; ++pass)
|
||||
{
|
||||
// compute some basic parameters
|
||||
std::pair<std::uint32_t, std::uint32_t> const dimensions(get_pass_dimensions(pass));
|
||||
@ -153,7 +189,7 @@ private:
|
||||
// we de-filter in place, stripping the filter bytes off the rows
|
||||
uint8_t *dst(&pnginfo.image[pass_offset[pass]]);
|
||||
uint8_t const *src(dst);
|
||||
for (std::uint32_t y = 0; (dimensions.second > y) && (png_error::NONE == error); ++y)
|
||||
for (std::uint32_t y = 0; (dimensions.second > y) && !error; ++y)
|
||||
{
|
||||
// first byte of each row is the filter type
|
||||
uint8_t const filter(*src++);
|
||||
@ -164,13 +200,13 @@ private:
|
||||
}
|
||||
|
||||
// if we errored, free the image data
|
||||
if (error != png_error::NONE)
|
||||
if (error)
|
||||
pnginfo.image.reset();
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
png_error decompress(std::list<image_data_chunk> const &idata, std::uint32_t expected)
|
||||
std::error_condition decompress(std::list<image_data_chunk> const &idata, std::uint32_t expected)
|
||||
{
|
||||
// only deflate is permitted
|
||||
if (0 != pnginfo.compression_method)
|
||||
@ -186,7 +222,11 @@ private:
|
||||
stream.avail_in = 0;
|
||||
stream.next_in = Z_NULL;
|
||||
zerr = inflateInit(&stream);
|
||||
if (Z_OK != zerr)
|
||||
if (Z_ERRNO == zerr)
|
||||
return std::error_condition(errno, std::generic_category());
|
||||
else if (Z_MEM_ERROR == zerr)
|
||||
return std::errc::not_enough_memory;
|
||||
else if (Z_OK != zerr)
|
||||
return png_error::DECOMPRESS_ERROR;
|
||||
|
||||
// decompress IDAT blocks
|
||||
@ -209,12 +249,12 @@ private:
|
||||
|
||||
// it's all good if we got end-of-stream or we have with no data remaining
|
||||
if ((Z_OK == inflateEnd(&stream)) && ((Z_STREAM_END == zerr) || ((Z_OK == zerr) && (idata.end() == it) && !stream.avail_in)))
|
||||
return png_error::NONE;
|
||||
return std::error_condition();
|
||||
else
|
||||
return png_error::DECOMPRESS_ERROR;
|
||||
return png_error::DECOMPRESS_ERROR; // TODO: refactor this function for more fine-grained error reporting?
|
||||
}
|
||||
|
||||
png_error unfilter_row(std::uint8_t type, uint8_t const *src, uint8_t *dst, uint8_t const *dstprev, int bpp, std::uint32_t rowbytes)
|
||||
std::error_condition unfilter_row(std::uint8_t type, uint8_t const *src, uint8_t *dst, uint8_t const *dstprev, int bpp, std::uint32_t rowbytes)
|
||||
{
|
||||
if (0 != pnginfo.filter_method)
|
||||
return png_error::UNKNOWN_FILTER;
|
||||
@ -223,14 +263,14 @@ private:
|
||||
{
|
||||
case PNG_PF_None: // no filter, just copy
|
||||
std::copy_n(src, rowbytes, dst);
|
||||
return png_error::NONE;
|
||||
return std::error_condition();
|
||||
|
||||
case PNG_PF_Sub: // SUB = previous pixel
|
||||
dst = std::copy_n(src, bpp, dst);
|
||||
src += bpp;
|
||||
for (std::uint32_t x = bpp; rowbytes > x; ++x, ++src, ++dst)
|
||||
*dst = *src + dst[-bpp];
|
||||
return png_error::NONE;
|
||||
return std::error_condition();
|
||||
|
||||
case PNG_PF_Up: // UP = pixel above
|
||||
if (dstprev)
|
||||
@ -242,7 +282,7 @@ private:
|
||||
{
|
||||
std::copy_n(src, rowbytes, dst);
|
||||
}
|
||||
return png_error::NONE;
|
||||
return std::error_condition();
|
||||
|
||||
case PNG_PF_Average: // AVERAGE = average of pixel above and previous pixel
|
||||
if (dstprev)
|
||||
@ -259,7 +299,7 @@ private:
|
||||
for (std::uint32_t x = bpp; rowbytes > x; ++x, ++src, ++dst)
|
||||
*dst = *src + (dst[-bpp] >> 1);
|
||||
}
|
||||
return png_error::NONE;
|
||||
return std::error_condition();
|
||||
|
||||
case PNG_PF_Paeth: // PAETH = special filter
|
||||
for (std::uint32_t x = 0; rowbytes > x; ++x, ++src, ++dst)
|
||||
@ -273,14 +313,14 @@ private:
|
||||
int32_t const dc(std::abs(prediction - pc));
|
||||
*dst = ((da <= db) && (da <= dc)) ? (*src + pa) : (db <= dc) ? (*src + pb) : (*src + pc);
|
||||
}
|
||||
return png_error::NONE;
|
||||
return std::error_condition();
|
||||
|
||||
default: // unknown filter type
|
||||
return png_error::UNKNOWN_FILTER;
|
||||
}
|
||||
}
|
||||
|
||||
png_error process_chunk(std::list<image_data_chunk> &idata, std::unique_ptr<std::uint8_t []> &&data, uint32_t type, uint32_t length)
|
||||
std::error_condition process_chunk(std::list<image_data_chunk> &idata, std::unique_ptr<std::uint8_t []> &&data, uint32_t type, uint32_t length)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
@ -312,7 +352,7 @@ private:
|
||||
|
||||
case PNG_CN_IDAT: // image data
|
||||
try { idata.emplace_back(length, std::move(data)); }
|
||||
catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
|
||||
catch (std::bad_alloc const &) { return std::errc::not_enough_memory; }
|
||||
break;
|
||||
|
||||
case PNG_CN_gAMA: // gamma
|
||||
@ -353,7 +393,7 @@ private:
|
||||
}
|
||||
catch (std::bad_alloc const &)
|
||||
{
|
||||
return png_error::OUT_OF_MEMORY;
|
||||
return std::errc::not_enough_memory;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -363,7 +403,7 @@ private:
|
||||
return png_error::UNKNOWN_CHUNK;
|
||||
break;
|
||||
}
|
||||
return png_error::NONE;
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
unsigned get_pass_count() const
|
||||
@ -405,23 +445,31 @@ private:
|
||||
return ((samples[pnginfo.color_type] * pnginfo.bit_depth) + 7) >> 3;
|
||||
}
|
||||
|
||||
static png_error read_chunk(core_file &fp, std::unique_ptr<std::uint8_t []> &data, std::uint32_t &type, std::uint32_t &length)
|
||||
static std::error_condition read_chunk(read_stream &fp, std::unique_ptr<std::uint8_t []> &data, std::uint32_t &type, std::uint32_t &length)
|
||||
{
|
||||
std::error_condition err;
|
||||
std::size_t actual;
|
||||
std::uint8_t tempbuff[4];
|
||||
|
||||
/* fetch the length of this chunk */
|
||||
if (fp.read(tempbuff, 4) != 4)
|
||||
err = fp.read(tempbuff, 4, actual);
|
||||
if (err)
|
||||
return err;
|
||||
else if (4 != actual)
|
||||
return png_error::FILE_TRUNCATED;
|
||||
length = fetch_32bit(tempbuff);
|
||||
|
||||
/* fetch the type of this chunk */
|
||||
if (fp.read(tempbuff, 4) != 4)
|
||||
err = fp.read(tempbuff, 4, actual);
|
||||
if (err)
|
||||
return err;
|
||||
else if (4 != actual)
|
||||
return png_error::FILE_TRUNCATED;
|
||||
type = fetch_32bit(tempbuff);
|
||||
|
||||
/* stop when we hit an IEND chunk */
|
||||
if (type == PNG_CN_IEND)
|
||||
return png_error::NONE;
|
||||
return std::error_condition();
|
||||
|
||||
/* start the CRC with the chunk type (but not the length) */
|
||||
std::uint32_t crc = crc32(0, tempbuff, 4);
|
||||
@ -430,11 +478,18 @@ private:
|
||||
if (length)
|
||||
{
|
||||
/* allocate memory for this chunk */
|
||||
try { data.reset(new std::uint8_t [length]); }
|
||||
catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
|
||||
data.reset(new (std::nothrow) std::uint8_t [length]);
|
||||
if (!data)
|
||||
return std::errc::not_enough_memory;
|
||||
|
||||
/* read the data from the file */
|
||||
if (fp.read(data.get(), length) != length)
|
||||
err = fp.read(data.get(), length, actual);
|
||||
if (err)
|
||||
{
|
||||
data.reset();
|
||||
return err;
|
||||
}
|
||||
else if (length != actual)
|
||||
{
|
||||
data.reset();
|
||||
return png_error::FILE_TRUNCATED;
|
||||
@ -445,7 +500,13 @@ private:
|
||||
}
|
||||
|
||||
/* read the CRC */
|
||||
if (fp.read(tempbuff, 4) != 4)
|
||||
err = fp.read(tempbuff, 4, actual);
|
||||
if (err)
|
||||
{
|
||||
data.reset();
|
||||
return err;
|
||||
}
|
||||
else if (4 != actual)
|
||||
{
|
||||
data.reset();
|
||||
return png_error::FILE_TRUNCATED;
|
||||
@ -459,7 +520,7 @@ private:
|
||||
return png_error::FILE_CORRUPT;
|
||||
}
|
||||
|
||||
return png_error::NONE;
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
png_info & pnginfo;
|
||||
@ -469,7 +530,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
png_error copy_to_bitmap(bitmap_argb32 &bitmap, bool &hasalpha) const
|
||||
std::error_condition copy_to_bitmap(bitmap_argb32 &bitmap, bool &hasalpha) const
|
||||
{
|
||||
// do some basic checks for unsupported images
|
||||
if ((8 > pnginfo.bit_depth) || (pnginfo.bit_depth % 8))
|
||||
@ -595,14 +656,14 @@ public:
|
||||
|
||||
// set hasalpha flag and return
|
||||
hasalpha = 0xffU != accumalpha;
|
||||
return png_error::NONE;
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
png_error expand_buffer_8bit()
|
||||
std::error_condition expand_buffer_8bit()
|
||||
{
|
||||
// nothing to do if we're at 8 or greater already
|
||||
if (pnginfo.bit_depth >= 8)
|
||||
return png_error::NONE;
|
||||
return std::error_condition();
|
||||
|
||||
// do some basic checks for unsupported images
|
||||
if (!pnginfo.bit_depth || (8 % pnginfo.bit_depth))
|
||||
@ -623,9 +684,9 @@ public:
|
||||
}
|
||||
|
||||
// allocate a new buffer at 8-bit
|
||||
std::unique_ptr<std::uint8_t []> outbuf;
|
||||
try { outbuf.reset(new std::uint8_t [outp_offset[pass_count]]); }
|
||||
catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
|
||||
std::unique_ptr<std::uint8_t []> outbuf(new (std::nothrow) std::uint8_t [outp_offset[pass_count]]);
|
||||
if (!outbuf)
|
||||
return std::errc::not_enough_memory;
|
||||
|
||||
// upsample bitmap
|
||||
std::uint8_t const bytesamples(8 / pnginfo.bit_depth);
|
||||
@ -679,13 +740,13 @@ public:
|
||||
|
||||
pnginfo.image = std::move(outbuf);
|
||||
pnginfo.bit_depth = 8;
|
||||
return png_error::NONE;
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
png_error read_file(core_file &fp)
|
||||
std::error_condition read_file(read_stream &fp)
|
||||
{
|
||||
// initialize the data structures
|
||||
png_error error = png_error::NONE;
|
||||
std::error_condition error;
|
||||
pnginfo.reset();
|
||||
std::list<image_data_chunk> idata;
|
||||
|
||||
@ -693,13 +754,13 @@ public:
|
||||
error = verify_header(fp);
|
||||
|
||||
// loop until we hit an IEND chunk
|
||||
while (png_error::NONE == error)
|
||||
while (!error)
|
||||
{
|
||||
// read a chunk
|
||||
std::unique_ptr<std::uint8_t []> chunk_data;
|
||||
std::uint32_t chunk_type = 0, chunk_length;
|
||||
error = read_chunk(fp, chunk_data, chunk_type, chunk_length);
|
||||
if (png_error::NONE == error)
|
||||
if (!error)
|
||||
{
|
||||
if (chunk_type == PNG_CN_IEND)
|
||||
break; // stop when we hit an IEND chunk
|
||||
@ -709,29 +770,33 @@ public:
|
||||
}
|
||||
|
||||
// finish processing the image
|
||||
if (png_error::NONE == error)
|
||||
if (!error)
|
||||
error = process(idata);
|
||||
|
||||
// if we have an error, free all the output data
|
||||
if (error != png_error::NONE)
|
||||
if (error)
|
||||
pnginfo.reset();
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static png_error verify_header(core_file &fp)
|
||||
static std::error_condition verify_header(read_stream &fp)
|
||||
{
|
||||
std::uint8_t signature[sizeof(PNG_SIGNATURE)];
|
||||
|
||||
// read 8 bytes
|
||||
if (fp.read(signature, sizeof(signature)) != sizeof(signature))
|
||||
std::size_t actual;
|
||||
std::error_condition err = fp.read(signature, sizeof(signature), actual);
|
||||
if (err)
|
||||
return err;
|
||||
else if (sizeof(signature) != actual)
|
||||
return png_error::FILE_TRUNCATED;
|
||||
|
||||
// return an error if we don't match
|
||||
if (std::memcmp(signature, PNG_SIGNATURE, sizeof(PNG_SIGNATURE)))
|
||||
return png_error::BAD_SIGNATURE;
|
||||
|
||||
return png_error::NONE;
|
||||
return std::error_condition();
|
||||
}
|
||||
};
|
||||
|
||||
@ -752,7 +817,7 @@ constexpr unsigned png_private::ADAM7_Y_OFFS[7];
|
||||
core stream
|
||||
-------------------------------------------------*/
|
||||
|
||||
png_error png_info::verify_header(core_file &fp)
|
||||
std::error_condition png_info::verify_header(read_stream &fp)
|
||||
{
|
||||
return png_private::verify_header(fp);
|
||||
}
|
||||
@ -762,7 +827,7 @@ png_error png_info::verify_header(core_file &fp)
|
||||
read_file - read a PNG from a core stream
|
||||
-------------------------------------------------*/
|
||||
|
||||
png_error png_info::read_file(core_file &fp)
|
||||
std::error_condition png_info::read_file(read_stream &fp)
|
||||
{
|
||||
return png_private(*this).read_file(fp);
|
||||
}
|
||||
@ -773,20 +838,20 @@ png_error png_info::read_file(core_file &fp)
|
||||
bitmap
|
||||
-------------------------------------------------*/
|
||||
|
||||
png_error png_read_bitmap(core_file &fp, bitmap_argb32 &bitmap)
|
||||
std::error_condition png_read_bitmap(read_stream &fp, bitmap_argb32 &bitmap)
|
||||
{
|
||||
png_error result;
|
||||
std::error_condition result;
|
||||
png_info pnginfo;
|
||||
png_private png(pnginfo);
|
||||
|
||||
// read the PNG data
|
||||
result = png.read_file(fp);
|
||||
if (png_error::NONE != result)
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
// resample to 8bpp if necessary
|
||||
result = png.expand_buffer_8bit();
|
||||
if (png_error::NONE != result)
|
||||
if (result)
|
||||
{
|
||||
pnginfo.free_data();
|
||||
return result;
|
||||
@ -803,7 +868,7 @@ png_error png_read_bitmap(core_file &fp, bitmap_argb32 &bitmap)
|
||||
bitmap
|
||||
-------------------------------------------------*/
|
||||
|
||||
png_error png_info::copy_to_bitmap(bitmap_argb32 &bitmap, bool &hasalpha)
|
||||
std::error_condition png_info::copy_to_bitmap(bitmap_argb32 &bitmap, bool &hasalpha)
|
||||
{
|
||||
return png_private(*this).copy_to_bitmap(bitmap, hasalpha);
|
||||
}
|
||||
@ -814,7 +879,7 @@ png_error png_info::copy_to_bitmap(bitmap_argb32 &bitmap, bool &hasalpha)
|
||||
sub 8-bit to 8-bit
|
||||
-------------------------------------------------*/
|
||||
|
||||
png_error png_info::expand_buffer_8bit()
|
||||
std::error_condition png_info::expand_buffer_8bit()
|
||||
{
|
||||
return png_private(*this).expand_buffer_8bit();
|
||||
}
|
||||
@ -829,7 +894,7 @@ png_error png_info::expand_buffer_8bit()
|
||||
add_text - add a text entry to the png_info
|
||||
-------------------------------------------------*/
|
||||
|
||||
png_error png_info::add_text(std::string_view keyword, std::string_view text)
|
||||
std::error_condition png_info::add_text(std::string_view keyword, std::string_view text)
|
||||
{
|
||||
// apply rules to keyword
|
||||
char32_t prev(0);
|
||||
@ -859,8 +924,8 @@ png_error png_info::add_text(std::string_view keyword, std::string_view text)
|
||||
|
||||
// allocate a new text element
|
||||
try { textlist.emplace_back(std::piecewise_construct, std::forward_as_tuple(keyword), std::forward_as_tuple(text)); }
|
||||
catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
|
||||
return png_error::NONE;
|
||||
catch (std::bad_alloc const &) { return std::errc::not_enough_memory; }
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
|
||||
@ -869,10 +934,12 @@ png_error png_info::add_text(std::string_view keyword, std::string_view text)
|
||||
the given file
|
||||
-------------------------------------------------*/
|
||||
|
||||
static png_error write_chunk(core_file &fp, const uint8_t *data, uint32_t type, uint32_t length)
|
||||
static std::error_condition write_chunk(write_stream &fp, const uint8_t *data, uint32_t type, uint32_t length)
|
||||
{
|
||||
uint8_t tempbuff[8];
|
||||
uint32_t crc;
|
||||
std::error_condition err;
|
||||
std::size_t written;
|
||||
std::uint8_t tempbuff[8];
|
||||
std::uint32_t crc;
|
||||
|
||||
/* stuff the length/type into the buffer */
|
||||
put_32bit(tempbuff + 0, length);
|
||||
@ -880,23 +947,32 @@ static png_error write_chunk(core_file &fp, const uint8_t *data, uint32_t type,
|
||||
crc = crc32(0, tempbuff + 4, 4);
|
||||
|
||||
/* write that data */
|
||||
if (fp.write(tempbuff, 8) != 8)
|
||||
return png_error::FILE_ERROR;
|
||||
err = fp.write(tempbuff, 8, written);
|
||||
if (err)
|
||||
return err;
|
||||
else if (8 != written)
|
||||
return std::errc::io_error;
|
||||
|
||||
/* append the actual data */
|
||||
if (length > 0)
|
||||
{
|
||||
if (fp.write(data, length) != length)
|
||||
return png_error::FILE_ERROR;
|
||||
err = fp.write(data, length, written);
|
||||
if (err)
|
||||
return err;
|
||||
else if (length != written)
|
||||
return std::errc::io_error;
|
||||
crc = crc32(crc, data, length);
|
||||
}
|
||||
|
||||
/* write the CRC */
|
||||
put_32bit(tempbuff, crc);
|
||||
if (fp.write(tempbuff, 4) != 4)
|
||||
return png_error::FILE_ERROR;
|
||||
err = fp.write(tempbuff, 4, written);
|
||||
if (err)
|
||||
return err;
|
||||
else if (4 != written)
|
||||
return std::errc::io_error;
|
||||
|
||||
return png_error::NONE;
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
|
||||
@ -905,13 +981,19 @@ static png_error write_chunk(core_file &fp, const uint8_t *data, uint32_t type,
|
||||
chunk to the given file by deflating it
|
||||
-------------------------------------------------*/
|
||||
|
||||
static png_error write_deflated_chunk(core_file &fp, uint8_t *data, uint32_t type, uint32_t length)
|
||||
static std::error_condition write_deflated_chunk(random_write &fp, uint8_t *data, uint32_t type, uint32_t length)
|
||||
{
|
||||
uint64_t lengthpos = fp.tell();
|
||||
uint8_t tempbuff[8192];
|
||||
uint32_t zlength = 0;
|
||||
std::error_condition err;
|
||||
std::uint64_t lengthpos;
|
||||
err = fp.tell(lengthpos);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
std::size_t written;
|
||||
std::uint8_t tempbuff[8192];
|
||||
std::uint32_t zlength = 0;
|
||||
z_stream stream;
|
||||
uint32_t crc;
|
||||
std::uint32_t crc;
|
||||
int zerr;
|
||||
|
||||
/* stuff the length/type into the buffer */
|
||||
@ -920,15 +1002,22 @@ static png_error write_deflated_chunk(core_file &fp, uint8_t *data, uint32_t typ
|
||||
crc = crc32(0, tempbuff + 4, 4);
|
||||
|
||||
/* write that data */
|
||||
if (fp.write(tempbuff, 8) != 8)
|
||||
return png_error::FILE_ERROR;
|
||||
err = fp.write(tempbuff, 8, written);
|
||||
if (err)
|
||||
return err;
|
||||
else if (8 != written)
|
||||
return std::errc::io_error;
|
||||
|
||||
/* initialize the stream */
|
||||
memset(&stream, 0, sizeof(stream));
|
||||
stream.next_in = data;
|
||||
stream.avail_in = length;
|
||||
zerr = deflateInit(&stream, Z_DEFAULT_COMPRESSION);
|
||||
if (zerr != Z_OK)
|
||||
if (Z_ERRNO == zerr)
|
||||
return std::error_condition(errno, std::generic_category());
|
||||
else if (Z_MEM_ERROR == zerr)
|
||||
return std::errc::not_enough_memory;
|
||||
else if (Z_OK != zerr)
|
||||
return png_error::COMPRESS_ERROR;
|
||||
|
||||
/* now loop until we run out of data */
|
||||
@ -943,10 +1032,16 @@ static png_error write_deflated_chunk(core_file &fp, uint8_t *data, uint32_t typ
|
||||
if (stream.avail_out < sizeof(tempbuff))
|
||||
{
|
||||
int bytes = sizeof(tempbuff) - stream.avail_out;
|
||||
if (fp.write(tempbuff, bytes) != bytes)
|
||||
err = fp.write(tempbuff, bytes, written);
|
||||
if (err)
|
||||
{
|
||||
deflateEnd(&stream);
|
||||
return png_error::FILE_ERROR;
|
||||
return err;
|
||||
}
|
||||
else if (bytes != written)
|
||||
{
|
||||
deflateEnd(&stream);
|
||||
return std::errc::io_error;
|
||||
}
|
||||
crc = crc32(crc, tempbuff, bytes);
|
||||
zlength += bytes;
|
||||
@ -960,29 +1055,45 @@ static png_error write_deflated_chunk(core_file &fp, uint8_t *data, uint32_t typ
|
||||
if (zerr != Z_OK)
|
||||
{
|
||||
deflateEnd(&stream);
|
||||
return png_error::COMPRESS_ERROR;
|
||||
if (Z_ERRNO == zerr)
|
||||
return std::error_condition(errno, std::generic_category());
|
||||
else if (Z_MEM_ERROR == zerr)
|
||||
return std::errc::not_enough_memory;
|
||||
else
|
||||
return png_error::COMPRESS_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/* clean up deflater(maus) */
|
||||
zerr = deflateEnd(&stream);
|
||||
if (zerr != Z_OK)
|
||||
if (Z_ERRNO == zerr)
|
||||
return std::error_condition(errno, std::generic_category());
|
||||
else if (Z_MEM_ERROR == zerr)
|
||||
return std::errc::not_enough_memory;
|
||||
else if (Z_OK != zerr)
|
||||
return png_error::COMPRESS_ERROR;
|
||||
|
||||
/* write the CRC */
|
||||
put_32bit(tempbuff, crc);
|
||||
if (fp.write(tempbuff, 4) != 4)
|
||||
return png_error::FILE_ERROR;
|
||||
err = fp.write(tempbuff, 4, written);
|
||||
if (err)
|
||||
return err;
|
||||
else if (4 != written)
|
||||
return std::errc::io_error;
|
||||
|
||||
/* seek back and update the length */
|
||||
fp.seek(lengthpos, SEEK_SET);
|
||||
err = fp.seek(lengthpos, SEEK_SET);
|
||||
if (err)
|
||||
return err;
|
||||
put_32bit(tempbuff + 0, zlength);
|
||||
if (fp.write(tempbuff, 4) != 4)
|
||||
return png_error::FILE_ERROR;
|
||||
err = fp.write(tempbuff, 4, written);
|
||||
if (err)
|
||||
return err;
|
||||
else if (4 != written)
|
||||
return std::errc::io_error;
|
||||
|
||||
/* return to the end */
|
||||
fp.seek(lengthpos + 8 + zlength + 4, SEEK_SET);
|
||||
return png_error::NONE;
|
||||
return fp.seek(lengthpos + 8 + zlength + 4, SEEK_SET);
|
||||
}
|
||||
|
||||
|
||||
@ -991,26 +1102,24 @@ static png_error write_deflated_chunk(core_file &fp, uint8_t *data, uint32_t typ
|
||||
bitmap to a palettized image
|
||||
-------------------------------------------------*/
|
||||
|
||||
static png_error convert_bitmap_to_image_palette(png_info &pnginfo, const bitmap_t &bitmap, int palette_length, const rgb_t *palette)
|
||||
static std::error_condition convert_bitmap_to_image_palette(png_info &pnginfo, bitmap_t const &bitmap, int palette_length, const rgb_t *palette)
|
||||
{
|
||||
int rowbytes;
|
||||
int x, y;
|
||||
|
||||
/* set the common info */
|
||||
pnginfo.width = bitmap.width();
|
||||
pnginfo.height = bitmap.height();
|
||||
pnginfo.bit_depth = 8;
|
||||
pnginfo.color_type = 3;
|
||||
pnginfo.num_palette = 256;
|
||||
rowbytes = pnginfo.width;
|
||||
int const rowbytes = pnginfo.width;
|
||||
|
||||
/* allocate memory for the palette */
|
||||
try { pnginfo.palette.reset(new std::uint8_t [3 * 256]); }
|
||||
catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
|
||||
pnginfo.palette.reset(new (std::nothrow) std::uint8_t [3 * 256]);
|
||||
if (!pnginfo.palette)
|
||||
return std::errc::not_enough_memory;
|
||||
|
||||
/* build the palette */
|
||||
std::fill_n(pnginfo.palette.get(), 3 * 256, 0);
|
||||
for (x = 0; x < palette_length; x++)
|
||||
for (int x = 0; x < palette_length; x++)
|
||||
{
|
||||
rgb_t color = palette[x];
|
||||
pnginfo.palette[3 * x + 0] = color.r();
|
||||
@ -1019,29 +1128,26 @@ static png_error convert_bitmap_to_image_palette(png_info &pnginfo, const bitmap
|
||||
}
|
||||
|
||||
/* allocate memory for the image */
|
||||
try
|
||||
{
|
||||
pnginfo.image.reset(new std::uint8_t [pnginfo.height * (rowbytes + 1)]);
|
||||
}
|
||||
catch (std::bad_alloc const &)
|
||||
pnginfo.image.reset(new (std::nothrow) std::uint8_t [pnginfo.height * (rowbytes + 1)]);
|
||||
if (!pnginfo.image)
|
||||
{
|
||||
pnginfo.palette.reset();
|
||||
return png_error::OUT_OF_MEMORY;
|
||||
return std::errc::not_enough_memory;
|
||||
}
|
||||
|
||||
/* copy in the pixels, specifying a nullptr filter */
|
||||
for (y = 0; y < pnginfo.height; y++)
|
||||
for (int y = 0; y < pnginfo.height; y++)
|
||||
{
|
||||
uint16_t const *src = reinterpret_cast<uint16_t const *>(bitmap.raw_pixptr(y));
|
||||
uint8_t *dst = &pnginfo.image[y * (rowbytes + 1)];
|
||||
|
||||
/* store the filter byte, then copy the data */
|
||||
*dst++ = 0;
|
||||
for (x = 0; x < pnginfo.width; x++)
|
||||
for (int x = 0; x < pnginfo.width; x++)
|
||||
*dst++ = *src++;
|
||||
}
|
||||
|
||||
return png_error::NONE;
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
|
||||
@ -1050,77 +1156,75 @@ static png_error convert_bitmap_to_image_palette(png_info &pnginfo, const bitmap
|
||||
bitmap to an RGB image
|
||||
-------------------------------------------------*/
|
||||
|
||||
static png_error convert_bitmap_to_image_rgb(png_info &pnginfo, const bitmap_t &bitmap, int palette_length, const rgb_t *palette)
|
||||
static std::error_condition convert_bitmap_to_image_rgb(png_info &pnginfo, bitmap_t const &bitmap, int palette_length, const rgb_t *palette)
|
||||
{
|
||||
int alpha = (bitmap.format() == BITMAP_FORMAT_ARGB32);
|
||||
int rowbytes;
|
||||
int x, y;
|
||||
bool const alpha = (bitmap.format() == BITMAP_FORMAT_ARGB32);
|
||||
|
||||
/* set the common info */
|
||||
pnginfo.width = bitmap.width();
|
||||
pnginfo.height = bitmap.height();
|
||||
pnginfo.bit_depth = 8;
|
||||
pnginfo.color_type = alpha ? 6 : 2;
|
||||
rowbytes = pnginfo.width * (alpha ? 4 : 3);
|
||||
int const rowbytes = pnginfo.width * (alpha ? 4 : 3);
|
||||
|
||||
/* allocate memory for the image */
|
||||
try { pnginfo.image.reset(new std::uint8_t [pnginfo.height * (rowbytes + 1)]); }
|
||||
catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
|
||||
pnginfo.image.reset(new (std::nothrow) std::uint8_t [pnginfo.height * (rowbytes + 1)]);
|
||||
if (!pnginfo.image)
|
||||
return std::errc::not_enough_memory;
|
||||
|
||||
/* copy in the pixels, specifying a nullptr filter */
|
||||
for (y = 0; y < pnginfo.height; y++)
|
||||
for (int y = 0; y < pnginfo.height; y++)
|
||||
{
|
||||
uint8_t *dst = &pnginfo.image[y * (rowbytes + 1)];
|
||||
|
||||
/* store the filter byte, then copy the data */
|
||||
*dst++ = 0;
|
||||
|
||||
/* 16bpp palettized format */
|
||||
if (bitmap.format() == BITMAP_FORMAT_IND16)
|
||||
{
|
||||
/* 16bpp palettized format */
|
||||
uint16_t const *src16 = reinterpret_cast<uint16_t const *>(bitmap.raw_pixptr(y));
|
||||
for (x = 0; x < pnginfo.width; x++)
|
||||
for (int x = 0; x < pnginfo.width; x++)
|
||||
{
|
||||
rgb_t color = palette[*src16++];
|
||||
rgb_t const color = palette[*src16++];
|
||||
*dst++ = color.r();
|
||||
*dst++ = color.g();
|
||||
*dst++ = color.b();
|
||||
}
|
||||
}
|
||||
|
||||
/* 32-bit RGB direct */
|
||||
else if (bitmap.format() == BITMAP_FORMAT_RGB32)
|
||||
{
|
||||
/* 32-bit RGB direct */
|
||||
uint32_t const *src32 = reinterpret_cast<uint32_t const *>(bitmap.raw_pixptr(y));
|
||||
for (x = 0; x < pnginfo.width; x++)
|
||||
for (int x = 0; x < pnginfo.width; x++)
|
||||
{
|
||||
rgb_t raw = *src32++;
|
||||
rgb_t const raw = *src32++;
|
||||
*dst++ = raw.r();
|
||||
*dst++ = raw.g();
|
||||
*dst++ = raw.b();
|
||||
}
|
||||
}
|
||||
|
||||
/* 32-bit ARGB direct */
|
||||
else if (bitmap.format() == BITMAP_FORMAT_ARGB32)
|
||||
{
|
||||
/* 32-bit ARGB direct */
|
||||
uint32_t const *src32 = reinterpret_cast<uint32_t const *>(bitmap.raw_pixptr(y));
|
||||
for (x = 0; x < pnginfo.width; x++)
|
||||
for (int x = 0; x < pnginfo.width; x++)
|
||||
{
|
||||
rgb_t raw = *src32++;
|
||||
rgb_t const raw = *src32++;
|
||||
*dst++ = raw.r();
|
||||
*dst++ = raw.g();
|
||||
*dst++ = raw.b();
|
||||
*dst++ = raw.a();
|
||||
}
|
||||
}
|
||||
|
||||
/* unsupported format */
|
||||
else
|
||||
{
|
||||
/* unsupported format */
|
||||
return png_error::UNSUPPORTED_FORMAT;
|
||||
}
|
||||
}
|
||||
|
||||
return png_error::NONE;
|
||||
return std::error_condition();
|
||||
}
|
||||
|
||||
|
||||
@ -1129,17 +1233,17 @@ static png_error convert_bitmap_to_image_rgb(png_info &pnginfo, const bitmap_t &
|
||||
chunks to the given file
|
||||
-------------------------------------------------*/
|
||||
|
||||
static png_error write_png_stream(core_file &fp, png_info &pnginfo, const bitmap_t &bitmap, int palette_length, const rgb_t *palette)
|
||||
static std::error_condition write_png_stream(random_write &fp, png_info &pnginfo, const bitmap_t &bitmap, int palette_length, const rgb_t *palette)
|
||||
{
|
||||
uint8_t tempbuff[16];
|
||||
png_error error;
|
||||
std::error_condition error;
|
||||
|
||||
// create an unfiltered image in either palette or RGB form
|
||||
if (bitmap.format() == BITMAP_FORMAT_IND16 && palette_length <= 256)
|
||||
error = convert_bitmap_to_image_palette(pnginfo, bitmap, palette_length, palette);
|
||||
else
|
||||
error = convert_bitmap_to_image_rgb(pnginfo, bitmap, palette_length, palette);
|
||||
if (error != png_error::NONE)
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
// if we wanted to get clever and do filtering, we would do it here
|
||||
@ -1153,18 +1257,18 @@ static png_error write_png_stream(core_file &fp, png_info &pnginfo, const bitmap
|
||||
put_8bit(tempbuff + 11, pnginfo.filter_method);
|
||||
put_8bit(tempbuff + 12, pnginfo.interlace_method);
|
||||
error = write_chunk(fp, tempbuff, PNG_CN_IHDR, 13);
|
||||
if (error != png_error::NONE)
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
// write the PLTE chunk
|
||||
if (pnginfo.num_palette > 0)
|
||||
error = write_chunk(fp, pnginfo.palette.get(), PNG_CN_PLTE, pnginfo.num_palette * 3);
|
||||
if (error != png_error::NONE)
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
// write a single IDAT chunk
|
||||
error = write_deflated_chunk(fp, pnginfo.image.get(), PNG_CN_IDAT, pnginfo.height * (compute_rowbytes(pnginfo) + 1));
|
||||
if (error != png_error::NONE)
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
// write TEXT chunks
|
||||
@ -1172,7 +1276,7 @@ static png_error write_png_stream(core_file &fp, png_info &pnginfo, const bitmap
|
||||
for (png_info::png_text const &text : pnginfo.textlist)
|
||||
{
|
||||
try { textbuf.resize(text.first.length() + 1 + text.second.length()); }
|
||||
catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
|
||||
catch (std::bad_alloc const &) { return std::errc::not_enough_memory; }
|
||||
std::uint8_t *dst(&textbuf[0]);
|
||||
|
||||
// convert keyword to ISO-8859-1
|
||||
@ -1201,7 +1305,7 @@ static png_error write_png_stream(core_file &fp, png_info &pnginfo, const bitmap
|
||||
}
|
||||
|
||||
error = write_chunk(fp, &textbuf[0], PNG_CN_tEXt, dst - &textbuf[0]);
|
||||
if (error != png_error::NONE)
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -1210,16 +1314,20 @@ static png_error write_png_stream(core_file &fp, png_info &pnginfo, const bitmap
|
||||
}
|
||||
|
||||
|
||||
png_error png_write_bitmap(core_file &fp, png_info *info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette)
|
||||
std::error_condition png_write_bitmap(random_write &fp, png_info *info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette)
|
||||
{
|
||||
// use a dummy pnginfo if none passed to us
|
||||
png_info pnginfo;
|
||||
if (info == nullptr)
|
||||
if (!info)
|
||||
info = &pnginfo;
|
||||
|
||||
// write the PNG signature
|
||||
if (fp.write(PNG_SIGNATURE, sizeof(PNG_SIGNATURE)) != sizeof(PNG_SIGNATURE))
|
||||
return png_error::FILE_ERROR;
|
||||
std::size_t written;
|
||||
std::error_condition err = fp.write(PNG_SIGNATURE, sizeof(PNG_SIGNATURE), written);
|
||||
if (err)
|
||||
return err;
|
||||
else if (sizeof(PNG_SIGNATURE) != written)
|
||||
return std::errc::io_error;
|
||||
|
||||
/* write the rest of the PNG data */
|
||||
return write_png_stream(fp, *info, bitmap, palette_length, palette);
|
||||
@ -1233,25 +1341,16 @@ png_error png_write_bitmap(core_file &fp, png_info *info, bitmap_t const &bitmap
|
||||
|
||||
********************************************************************************/
|
||||
|
||||
/**
|
||||
* @fn png_error mng_capture_start(core_file &fp, bitmap_t &bitmap, unsigned rate)
|
||||
*
|
||||
* @brief Mng capture start.
|
||||
*
|
||||
* @param [in,out] fp If non-null, the fp.
|
||||
* @param [in,out] bitmap The bitmap.
|
||||
* @param rate The framerate.
|
||||
*
|
||||
* @return A png_error.
|
||||
*/
|
||||
|
||||
png_error mng_capture_start(core_file &fp, bitmap_t &bitmap, unsigned rate)
|
||||
std::error_condition mng_capture_start(random_write &fp, bitmap_t const &bitmap, unsigned rate)
|
||||
{
|
||||
std::size_t written;
|
||||
std::error_condition err = fp.write(MNG_Signature, 8, written);
|
||||
if (err)
|
||||
return err;
|
||||
else if (8 != written)
|
||||
return std::errc::io_error;
|
||||
|
||||
uint8_t mhdr[28];
|
||||
|
||||
if (fp.write(MNG_Signature, 8) != 8)
|
||||
return png_error::FILE_ERROR;
|
||||
|
||||
memset(mhdr, 0, 28);
|
||||
put_32bit(mhdr + 0, bitmap.width());
|
||||
put_32bit(mhdr + 4, bitmap.height());
|
||||
@ -1260,38 +1359,27 @@ png_error mng_capture_start(core_file &fp, bitmap_t &bitmap, unsigned rate)
|
||||
return write_chunk(fp, mhdr, MNG_CN_MHDR, 28);
|
||||
}
|
||||
|
||||
/**
|
||||
* @fn png_error mng_capture_frame(core_file &fp, png_info *info, bitmap_t &bitmap, int palette_length, const rgb_t *palette)
|
||||
*
|
||||
* @brief Mng capture frame.
|
||||
*
|
||||
* @param [in,out] fp If non-null, the fp.
|
||||
* @param [in,out] info If non-null, the information.
|
||||
* @param [in,out] bitmap The bitmap.
|
||||
* @param palette_length Length of the palette.
|
||||
* @param palette The palette.
|
||||
*
|
||||
* @return A png_error.
|
||||
*/
|
||||
|
||||
png_error mng_capture_frame(core_file &fp, png_info &info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette)
|
||||
std::error_condition mng_capture_frame(random_write &fp, png_info &info, bitmap_t const &bitmap, int palette_length, rgb_t const *palette)
|
||||
{
|
||||
return write_png_stream(fp, info, bitmap, palette_length, palette);
|
||||
}
|
||||
|
||||
/**
|
||||
* @fn png_error mng_capture_stop(core_file &fp)
|
||||
*
|
||||
* @brief Mng capture stop.
|
||||
*
|
||||
* @param [in,out] fp If non-null, the fp.
|
||||
*
|
||||
* @return A png_error.
|
||||
*/
|
||||
|
||||
png_error mng_capture_stop(core_file &fp)
|
||||
std::error_condition mng_capture_stop(random_write &fp)
|
||||
{
|
||||
return write_chunk(fp, nullptr, MNG_CN_MEND, 0);
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------------------
|
||||
png_category - gets the PNG error category
|
||||
instance
|
||||
-------------------------------------------------*/
|
||||
|
||||
std::error_category const &png_category() noexcept
|
||||
{
|
||||
return f_png_category_instance;
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
|
@ -14,13 +14,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "bitmap.h"
|
||||
#include "corefile.h"
|
||||
#include "utilfwd.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <system_error>
|
||||
#include <utility>
|
||||
|
||||
|
||||
@ -31,12 +32,9 @@ namespace util {
|
||||
***************************************************************************/
|
||||
|
||||
/* Error types */
|
||||
enum class png_error
|
||||
enum class png_error : int
|
||||
{
|
||||
NONE,
|
||||
OUT_OF_MEMORY,
|
||||
UNKNOWN_FILTER,
|
||||
FILE_ERROR,
|
||||
UNKNOWN_FILTER = 1,
|
||||
BAD_SIGNATURE,
|
||||
DECOMPRESS_ERROR,
|
||||
FILE_TRUNCATED,
|
||||
@ -46,6 +44,9 @@ enum class png_error
|
||||
UNSUPPORTED_FORMAT
|
||||
};
|
||||
|
||||
std::error_category const &png_category() noexcept;
|
||||
inline std::error_condition make_error_condition(png_error err) noexcept { return std::error_condition(int(err), png_category()); }
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
@ -59,16 +60,16 @@ public:
|
||||
|
||||
~png_info() { free_data(); }
|
||||
|
||||
png_error read_file(core_file &fp);
|
||||
png_error copy_to_bitmap(bitmap_argb32 &bitmap, bool &hasalpha);
|
||||
png_error expand_buffer_8bit();
|
||||
std::error_condition read_file(read_stream &fp);
|
||||
std::error_condition copy_to_bitmap(bitmap_argb32 &bitmap, bool &hasalpha);
|
||||
std::error_condition expand_buffer_8bit();
|
||||
|
||||
png_error add_text(std::string_view keyword, std::string_view text);
|
||||
std::error_condition add_text(std::string_view keyword, std::string_view text);
|
||||
|
||||
void free_data();
|
||||
void reset() { free_data(); operator=(png_info()); }
|
||||
|
||||
static png_error verify_header(core_file &fp);
|
||||
static std::error_condition verify_header(read_stream &fp);
|
||||
|
||||
std::unique_ptr<std::uint8_t []> image;
|
||||
std::uint32_t width, height;
|
||||
@ -101,14 +102,21 @@ private:
|
||||
FUNCTION PROTOTYPES
|
||||
***************************************************************************/
|
||||
|
||||
png_error png_read_bitmap(core_file &fp, bitmap_argb32 &bitmap);
|
||||
std::error_condition png_read_bitmap(read_stream &fp, bitmap_argb32 &bitmap);
|
||||
|
||||
png_error png_write_bitmap(core_file &fp, png_info *info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette);
|
||||
std::error_condition png_write_bitmap(random_write &fp, png_info *info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette);
|
||||
|
||||
png_error mng_capture_start(core_file &fp, bitmap_t &bitmap, unsigned rate);
|
||||
png_error mng_capture_frame(core_file &fp, png_info &info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette);
|
||||
png_error mng_capture_stop(core_file &fp);
|
||||
std::error_condition mng_capture_start(random_write &fp, bitmap_t const &bitmap, unsigned rate);
|
||||
std::error_condition mng_capture_frame(random_write &fp, png_info &info, bitmap_t const &bitmap, int palette_length, rgb_t const *palette);
|
||||
std::error_condition mng_capture_stop(random_write &fp);
|
||||
|
||||
} // namespace util
|
||||
|
||||
|
||||
namespace std {
|
||||
|
||||
template <> struct is_error_condition_enum<util::png_error> : public std::true_type { };
|
||||
|
||||
} // namespace std
|
||||
|
||||
#endif // MAME_LIB_UTIL_PNG_H
|
||||
|
@ -47,7 +47,8 @@ void write_escaped(core_file &file, std::string const &str)
|
||||
std::string::size_type const found = str.find_first_of("\"&<>", pos);
|
||||
if (found != std::string::npos)
|
||||
{
|
||||
file.write(&str[pos], found - pos);
|
||||
std::size_t written;
|
||||
file.write(&str[pos], found - pos, written);
|
||||
switch (str[found])
|
||||
{
|
||||
case '"': file.puts("""); pos = found + 1; break;
|
||||
@ -59,7 +60,8 @@ void write_escaped(core_file &file, std::string const &str)
|
||||
}
|
||||
else
|
||||
{
|
||||
file.write(&str[pos], str.size() - pos);
|
||||
std::size_t written;
|
||||
file.write(&str[pos], str.size() - pos, written);
|
||||
pos = found;
|
||||
}
|
||||
}
|
||||
@ -118,28 +120,28 @@ file::ptr file::create()
|
||||
// read - parse an XML file into its nodes
|
||||
//-------------------------------------------------
|
||||
|
||||
file::ptr file::read(util::core_file &file, parse_options const *opts)
|
||||
file::ptr file::read(read_stream &file, parse_options const *opts)
|
||||
{
|
||||
parse_info info;
|
||||
int done;
|
||||
|
||||
// set up the parser
|
||||
parse_info info;
|
||||
if (!expat_setup_parser(info, opts))
|
||||
return ptr();
|
||||
|
||||
// loop through the file and parse it
|
||||
bool done;
|
||||
do
|
||||
{
|
||||
char tempbuf[TEMP_BUFFER_SIZE];
|
||||
|
||||
// read as much as we can
|
||||
int bytes = file.read(tempbuf, sizeof(tempbuf));
|
||||
done = file.eof();
|
||||
size_t bytes;
|
||||
file.read(tempbuf, sizeof(tempbuf), bytes); // TODO: better error handling
|
||||
done = !bytes;
|
||||
|
||||
// parse the data
|
||||
if (XML_Parse(info.parser, tempbuf, bytes, done) == XML_STATUS_ERROR)
|
||||
{
|
||||
if (opts != nullptr && opts->error != nullptr)
|
||||
if (opts && opts->error)
|
||||
{
|
||||
opts->error->error_message = XML_ErrorString(XML_GetErrorCode(info.parser));
|
||||
opts->error->error_line = XML_GetCurrentLineNumber(info.parser);
|
||||
|
@ -223,7 +223,7 @@ public:
|
||||
static ptr create();
|
||||
|
||||
// parse an XML file into its nodes
|
||||
static ptr read(util::core_file &file, parse_options const *opts);
|
||||
static ptr read(read_stream &file, parse_options const *opts);
|
||||
|
||||
// parse an XML string into its nodes
|
||||
static ptr string_read(const char *string, parse_options const *opts);
|
||||
|
@ -135,10 +135,9 @@ void coco_vhd_image_device::coco_vhd_readwrite(uint8_t data)
|
||||
}
|
||||
|
||||
/* perform the seek */
|
||||
seek_position = ((uint64_t) 256) * m_logical_record_number;
|
||||
seek_position = uint64_t(256) * m_logical_record_number;
|
||||
total_size = length();
|
||||
result = fseek(std::min(seek_position, total_size), SEEK_SET);
|
||||
if (result < 0)
|
||||
if (fseek(std::min(seek_position, total_size), SEEK_SET))
|
||||
{
|
||||
m_status = VHDSTATUS_ACCESS_DENIED;
|
||||
return;
|
||||
|
@ -365,9 +365,9 @@ void shaders::render_snapshot(IDirect3DSurface9 *surface)
|
||||
pnginfo.add_text("System", text2);
|
||||
|
||||
// now do the actual work
|
||||
util::png_error error = util::png_write_bitmap(file, &pnginfo, snapshot, 1 << 24, nullptr);
|
||||
if (error != util::png_error::NONE)
|
||||
osd_printf_error("Error generating PNG for HLSL snapshot: png_error = %d\n", std::underlying_type_t<util::png_error>(error));
|
||||
std::error_condition const error = util::png_write_bitmap(file, &pnginfo, snapshot, 1 << 24, nullptr);
|
||||
if (error)
|
||||
osd_printf_error("Error generating PNG for HLSL snapshot (%s:%d %s)\n", error.category().name(), error.value(), error.message());
|
||||
|
||||
result = snap_copy_target->UnlockRect();
|
||||
if (FAILED(result))
|
||||
|
@ -247,11 +247,16 @@ class chd_rawfile_compressor : public chd_file_compressor
|
||||
{
|
||||
public:
|
||||
// construction/destruction
|
||||
chd_rawfile_compressor(util::core_file &file, std::uint64_t offset = 0, std::uint64_t maxoffset = std::numeric_limits<std::uint64_t>::max())
|
||||
chd_rawfile_compressor(util::random_read &file, std::uint64_t offset = 0, std::uint64_t maxoffset = std::numeric_limits<std::uint64_t>::max())
|
||||
: m_file(file)
|
||||
, m_offset(offset)
|
||||
, m_maxoffset((std::min)(maxoffset, file.size()))
|
||||
{
|
||||
// TODO: what to do about error getting file size?
|
||||
std::uint64_t filelen;
|
||||
if (!file.length(filelen))
|
||||
m_maxoffset = (std::min)(maxoffset, filelen);
|
||||
else
|
||||
m_maxoffset = maxoffset;
|
||||
}
|
||||
|
||||
// read interface
|
||||
@ -262,13 +267,16 @@ public:
|
||||
return 0;
|
||||
if (offset + length > m_maxoffset)
|
||||
length = m_maxoffset - offset;
|
||||
m_file.seek(offset, SEEK_SET);
|
||||
return m_file.read(dest, length);
|
||||
if (m_file.seek(offset, SEEK_SET)) // FIXME: better error reporting?
|
||||
return 0;
|
||||
std::size_t actual;
|
||||
m_file.read(dest, length, actual); // FIXME: check for error return
|
||||
return actual;
|
||||
}
|
||||
|
||||
private:
|
||||
// internal state
|
||||
util::core_file & m_file;
|
||||
util::random_read & m_file;
|
||||
std::uint64_t m_offset;
|
||||
std::uint64_t m_maxoffset;
|
||||
};
|
||||
@ -425,11 +433,15 @@ public:
|
||||
}
|
||||
else
|
||||
{
|
||||
m_file->seek((src_frame_start >= split_track_start)
|
||||
? src_frame_start - split_track_start
|
||||
: src_frame_start, SEEK_SET);
|
||||
uint32_t count = m_file->read(dest, bytesperframe);
|
||||
if (count != bytesperframe)
|
||||
std::error_condition err = m_file->seek(
|
||||
(src_frame_start >= split_track_start)
|
||||
? src_frame_start - split_track_start
|
||||
: src_frame_start,
|
||||
SEEK_SET);
|
||||
std::size_t count = 0;
|
||||
if (!err)
|
||||
err = m_file->read(dest, bytesperframe, count);
|
||||
if (err || (count != bytesperframe))
|
||||
report_error(1, "Error reading input file (%s)'", m_lastfile);
|
||||
}
|
||||
|
||||
@ -1693,7 +1705,9 @@ static void do_create_raw(parameters_map ¶ms)
|
||||
// process input start/end (needs to know hunk_size)
|
||||
uint64_t input_start;
|
||||
uint64_t input_end;
|
||||
parse_input_start_end(params, input_file->size(), hunk_size, hunk_size, input_start, input_end);
|
||||
uint64_t input_size = 0;
|
||||
input_file->length(input_size); // FIXME: check error return
|
||||
parse_input_start_end(params, input_size, hunk_size, hunk_size, input_start, input_end);
|
||||
|
||||
// process compression
|
||||
chd_codec_type compression[4];
|
||||
@ -1708,7 +1722,7 @@ static void do_create_raw(parameters_map ¶ms)
|
||||
if (output_parent.opened())
|
||||
printf("Parent CHD: %s\n", params.find(OPTION_OUTPUT_PARENT)->second->c_str());
|
||||
printf("Input file: %s\n", input_file_str->second->c_str());
|
||||
if (input_start != 0 || input_end != input_file->size())
|
||||
if (input_start != 0 || input_end != input_size)
|
||||
{
|
||||
printf("Input start: %s\n", big_int_string(input_start).c_str());
|
||||
printf("Input length: %s\n", big_int_string(input_end - input_start).c_str());
|
||||
@ -1789,7 +1803,9 @@ static void do_create_hd(parameters_map ¶ms)
|
||||
uint64_t input_end = 0;
|
||||
if (input_file)
|
||||
{
|
||||
parse_input_start_end(params, input_file->size(), hunk_size, hunk_size, input_start, input_end);
|
||||
uint64_t input_size = 0;
|
||||
input_file->length(input_size); // FIXME: check error return
|
||||
parse_input_start_end(params, input_size, hunk_size, hunk_size, input_start, input_end);
|
||||
filesize = input_end - input_start;
|
||||
}
|
||||
else
|
||||
@ -1897,8 +1913,10 @@ static void do_create_hd(parameters_map ¶ms)
|
||||
printf("Parent CHD: %s\n", params.find(OPTION_OUTPUT_PARENT)->second->c_str());
|
||||
if (input_file)
|
||||
{
|
||||
uint64_t input_size = 0;
|
||||
input_file->length(input_size); // FIXME: check error return
|
||||
printf("Input file: %s\n", input_file_str->second->c_str());
|
||||
if (input_start != 0 || input_end != input_file->size())
|
||||
if (input_start != 0 || input_end != input_size)
|
||||
{
|
||||
printf("Input start: %s\n", big_int_string(input_start).c_str());
|
||||
printf("Input length: %s\n", big_int_string(filesize).c_str());
|
||||
@ -2361,8 +2379,9 @@ static void do_extract_raw(parameters_map ¶ms)
|
||||
report_error(1, "Error reading CHD file (%s): %s", *params.find(OPTION_INPUT)->second, err.message());
|
||||
|
||||
// write to the output
|
||||
uint32_t count = output_file->write(&buffer[0], bytes_to_read);
|
||||
if (count != bytes_to_read)
|
||||
size_t count;
|
||||
std::error_condition const writerr = output_file->write(&buffer[0], bytes_to_read, count);
|
||||
if (writerr || (count != bytes_to_read))
|
||||
report_error(1, "Error writing to file; check disk space (%s)", *output_file_str->second);
|
||||
|
||||
// advance
|
||||
@ -2555,8 +2574,9 @@ static void do_extract_cd(parameters_map ¶ms)
|
||||
if (bufferoffs == buffer.size() || frame == actualframes - 1)
|
||||
{
|
||||
output_bin_file->seek(outputoffs, SEEK_SET);
|
||||
uint32_t byteswritten = output_bin_file->write(&buffer[0], bufferoffs);
|
||||
if (byteswritten != bufferoffs)
|
||||
size_t byteswritten;
|
||||
std::error_condition const writerr = output_bin_file->write(&buffer[0], bufferoffs, byteswritten);
|
||||
if (writerr || (byteswritten != bufferoffs))
|
||||
report_error(1, "Error writing frame %d to file (%s): %s\n", frame, *output_file_str->second, "Write error");
|
||||
outputoffs += bufferoffs;
|
||||
bufferoffs = 0;
|
||||
@ -2898,23 +2918,28 @@ static void do_dump_metadata(parameters_map ¶ms)
|
||||
// create the file
|
||||
if (output_file_str != params.end())
|
||||
{
|
||||
std::error_condition const filerr = util::core_file::open(*output_file_str->second, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, output_file);
|
||||
std::error_condition filerr;
|
||||
|
||||
filerr = util::core_file::open(*output_file_str->second, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, output_file);
|
||||
if (filerr)
|
||||
report_error(1, "Unable to open file (%s): %s", *output_file_str->second, filerr.message());
|
||||
|
||||
// output the metadata
|
||||
uint32_t count = output_file->write(&buffer[0], buffer.size());
|
||||
if (count != buffer.size())
|
||||
size_t count;
|
||||
filerr = output_file->write(&buffer[0], buffer.size(), count);
|
||||
if (!filerr)
|
||||
filerr = output_file->flush();
|
||||
if (filerr || (count != buffer.size()))
|
||||
report_error(1, "Error writing file (%s)", *output_file_str->second);
|
||||
output_file.reset();
|
||||
|
||||
// provide some feedback
|
||||
printf("File (%s) written, %s bytes\n", output_file_str->second->c_str(), big_int_string(buffer.size()).c_str());
|
||||
}
|
||||
|
||||
// flush to stdout
|
||||
else
|
||||
{
|
||||
// flush to stdout
|
||||
// FIXME: check for errors
|
||||
fwrite(&buffer[0], 1, buffer.size(), stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
@ -198,10 +198,11 @@ stream::stream(bool wp, util::core_file::ptr &&f)
|
||||
: imgtype(IMG_FILE)
|
||||
, write_protect(wp)
|
||||
, position(0)
|
||||
, filesize(f->size())
|
||||
, filesize(0)
|
||||
, file(std::move(f))
|
||||
, buffer(nullptr)
|
||||
{
|
||||
file->length(filesize); // FIXME: check error return
|
||||
}
|
||||
|
||||
|
||||
@ -389,20 +390,19 @@ util::core_file *stream::core_file()
|
||||
|
||||
uint32_t stream::read(void *buf, uint32_t sz)
|
||||
{
|
||||
uint32_t result = 0;
|
||||
size_t result = 0;
|
||||
|
||||
switch(imgtype)
|
||||
{
|
||||
case IMG_FILE:
|
||||
assert(sz == (uint32_t) sz);
|
||||
file->seek(position, SEEK_SET);
|
||||
result = file->read(buf, (uint32_t) sz);
|
||||
if (!file->seek(position, SEEK_SET))
|
||||
file->read(buf, sz, result); // FIXME: check error return
|
||||
break;
|
||||
|
||||
case IMG_MEM:
|
||||
/* do we have to limit sz? */
|
||||
// do we have to limit sz?
|
||||
if (sz > (filesize - position))
|
||||
sz = (uint32_t) (filesize - position);
|
||||
sz = uint32_t(filesize - position);
|
||||
|
||||
memcpy(buf, buffer + position, sz);
|
||||
result = sz;
|
||||
@ -423,29 +423,29 @@ uint32_t stream::read(void *buf, uint32_t sz)
|
||||
|
||||
uint32_t stream::write(const void *buf, uint32_t sz)
|
||||
{
|
||||
void *new_buffer;
|
||||
uint32_t result = 0;
|
||||
size_t result = 0;
|
||||
|
||||
switch(imgtype)
|
||||
{
|
||||
case IMG_MEM:
|
||||
if (!write_protect)
|
||||
{
|
||||
/* do we have to expand the buffer? */
|
||||
if (filesize < position + sz)
|
||||
// do we have to expand the buffer?
|
||||
const uint64_t end = position + sz;
|
||||
if ((filesize < end) && (size_t(end) == end))
|
||||
{
|
||||
/* try to expand the buffer */
|
||||
new_buffer = realloc(buffer , position + sz);
|
||||
// try to expand the buffer
|
||||
void *const new_buffer = realloc(buffer, size_t(end));
|
||||
if (new_buffer)
|
||||
{
|
||||
buffer = (uint8_t*)new_buffer;
|
||||
filesize = position + sz;
|
||||
buffer = reinterpret_cast<uint8_t *>(new_buffer);
|
||||
filesize = end;
|
||||
}
|
||||
}
|
||||
|
||||
/* do we have to limit sz? */
|
||||
// do we have to limit sz?
|
||||
if (sz > (filesize - position))
|
||||
sz = (uint32_t) (filesize - position);
|
||||
sz = uint32_t(filesize - position);
|
||||
|
||||
memcpy(buffer + position, buf, sz);
|
||||
result = sz;
|
||||
@ -453,8 +453,8 @@ uint32_t stream::write(const void *buf, uint32_t sz)
|
||||
break;
|
||||
|
||||
case IMG_FILE:
|
||||
file->seek(position, SEEK_SET);
|
||||
result = file->write(buf, sz);
|
||||
if (!file->seek(position, SEEK_SET))
|
||||
file->write(buf, sz, result); // FIXME: check error return
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -462,10 +462,10 @@ uint32_t stream::write(const void *buf, uint32_t sz)
|
||||
break;
|
||||
}
|
||||
|
||||
/* advance the file pointer */
|
||||
// advance the file pointer
|
||||
position += result;
|
||||
|
||||
/* did we grow the file */
|
||||
// did we grow the file
|
||||
if (position > filesize)
|
||||
filesize = position;
|
||||
return result;
|
||||
|
@ -8,16 +8,19 @@
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#include "corefile.h"
|
||||
#include "png.h"
|
||||
|
||||
#include "osdfile.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
#include <cassert>
|
||||
#include "osdfile.h"
|
||||
#include "png.h"
|
||||
|
||||
#include <new>
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
CONSTANTS & DEFINES
|
||||
***************************************************************************/
|
||||
@ -74,7 +77,6 @@ static int generate_png_diff(const std::string& imgfile1, const std::string& img
|
||||
int width, height, maxwidth;
|
||||
util::core_file::ptr file;
|
||||
std::error_condition filerr;
|
||||
util::png_error pngerr;
|
||||
int error = 100;
|
||||
|
||||
/* open the source image */
|
||||
@ -86,11 +88,11 @@ static int generate_png_diff(const std::string& imgfile1, const std::string& img
|
||||
}
|
||||
|
||||
/* load the source image */
|
||||
pngerr = util::png_read_bitmap(*file, bitmap1);
|
||||
filerr = util::png_read_bitmap(*file, bitmap1);
|
||||
file.reset();
|
||||
if (pngerr != util::png_error::NONE)
|
||||
if (filerr)
|
||||
{
|
||||
printf("Could not read %s (%d)\n", imgfile1.c_str(), int(pngerr));
|
||||
printf("Could not read %s (%s)\n", imgfile1.c_str(), filerr.message().c_str());
|
||||
goto error;
|
||||
}
|
||||
|
||||
@ -103,11 +105,11 @@ static int generate_png_diff(const std::string& imgfile1, const std::string& img
|
||||
}
|
||||
|
||||
/* load the source image */
|
||||
pngerr = util::png_read_bitmap(*file, bitmap2);
|
||||
filerr = util::png_read_bitmap(*file, bitmap2);
|
||||
file.reset();
|
||||
if (pngerr != util::png_error::NONE)
|
||||
if (filerr)
|
||||
{
|
||||
printf("Could not read %s (%d)\n", imgfile2.c_str(), int(pngerr));
|
||||
printf("Could not read %s (%s)\n", imgfile2.c_str(), filerr.message().c_str());
|
||||
goto error;
|
||||
}
|
||||
|
||||
@ -176,11 +178,11 @@ static int generate_png_diff(const std::string& imgfile1, const std::string& img
|
||||
printf("Could not open %s (%s)\n", outfilename.c_str(), filerr.message().c_str());
|
||||
goto error;
|
||||
}
|
||||
pngerr = util::png_write_bitmap(*file, nullptr, finalbitmap, 0, nullptr);
|
||||
filerr = util::png_write_bitmap(*file, nullptr, finalbitmap, 0, nullptr);
|
||||
file.reset();
|
||||
if (pngerr != util::png_error::NONE)
|
||||
if (filerr)
|
||||
{
|
||||
printf("Could not write %s (%d)\n", outfilename.c_str(), int(pngerr));
|
||||
printf("Could not write %s (%s)\n", outfilename.c_str(), filerr.message().c_str());
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
@ -6,16 +6,19 @@
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
#include "corefile.h"
|
||||
#include "corestr.h"
|
||||
#include "png.h"
|
||||
|
||||
#include "osdcomm.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
#include <new>
|
||||
#include <cassert>
|
||||
#include "corefile.h"
|
||||
#include "corestr.h"
|
||||
#include "osdcomm.h"
|
||||
#include "png.h"
|
||||
|
||||
|
||||
using util::string_format;
|
||||
|
||||
@ -153,7 +156,7 @@ static summary_file *sort_file_list(void);
|
||||
|
||||
/* HTML helpers */
|
||||
static util::core_file::ptr create_file_and_output_header(std::string &filename, std::string &templatefile, std::string &title);
|
||||
static void output_footer_and_close_file(util::core_file::ptr &&file, std::string &templatefile, std::string &title);
|
||||
static void output_footer_and_close_file(util::write_stream::ptr &&file, std::string &templatefile, std::string &title);
|
||||
|
||||
/* report generators */
|
||||
static void output_report(std::string &dirname, std::string &tempheader, std::string &tempfooter, summary_file *filelist);
|
||||
@ -575,7 +578,8 @@ static util::core_file::ptr create_file_and_output_header(std::string &filename,
|
||||
/* print a header */
|
||||
std::string modified(templatefile);
|
||||
strreplace(modified, "<!--TITLE-->", title.c_str());
|
||||
file->write(modified.c_str(), modified.length());
|
||||
std::size_t written;
|
||||
file->write(modified.c_str(), modified.length(), written); // FIXME: check for errors
|
||||
|
||||
/* return the file */
|
||||
return file;
|
||||
@ -587,11 +591,12 @@ static util::core_file::ptr create_file_and_output_header(std::string &filename,
|
||||
standard footer to an HTML file and close it
|
||||
-------------------------------------------------*/
|
||||
|
||||
static void output_footer_and_close_file(util::core_file::ptr &&file, std::string &templatefile, std::string &title)
|
||||
static void output_footer_and_close_file(util::write_stream::ptr &&file, std::string &templatefile, std::string &title)
|
||||
{
|
||||
std::string modified(templatefile);
|
||||
strreplace(modified, "<!--TITLE-->", title.c_str());
|
||||
file->write(modified.c_str(), modified.length());
|
||||
std::size_t written;
|
||||
file->write(modified.c_str(), modified.length(), written); // FIXME: check for errors
|
||||
file.reset();
|
||||
}
|
||||
|
||||
@ -839,7 +844,6 @@ static int generate_png_diff(const summary_file *curfile, std::string &destdir,
|
||||
int bitmapcount = 0;
|
||||
util::core_file::ptr file;
|
||||
std::error_condition filerr;
|
||||
util::png_error pngerr;
|
||||
int error = -1;
|
||||
int starty;
|
||||
|
||||
@ -859,9 +863,9 @@ static int generate_png_diff(const summary_file *curfile, std::string &destdir,
|
||||
goto error;
|
||||
|
||||
/* load the source image */
|
||||
pngerr = util::png_read_bitmap(*file, bitmaps[bitmapcount++]);
|
||||
filerr = util::png_read_bitmap(*file, bitmaps[bitmapcount++]);
|
||||
file.reset();
|
||||
if (pngerr != util::png_error::NONE)
|
||||
if (filerr)
|
||||
goto error;
|
||||
}
|
||||
|
||||
@ -928,9 +932,9 @@ static int generate_png_diff(const summary_file *curfile, std::string &destdir,
|
||||
filerr = util::core_file::open(dstfilename, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, file);
|
||||
if (filerr)
|
||||
goto error;
|
||||
pngerr = util::png_write_bitmap(*file, nullptr, finalbitmap, 0, nullptr);
|
||||
filerr = util::png_write_bitmap(*file, nullptr, finalbitmap, 0, nullptr);
|
||||
file.reset();
|
||||
if (pngerr != util::png_error::NONE)
|
||||
if (filerr)
|
||||
goto error;
|
||||
|
||||
/* if we get here, we are error free */
|
||||
|
@ -86,13 +86,17 @@ static int split_file(const char *filename, const char *basename, uint32_t split
|
||||
}
|
||||
|
||||
// get the total length
|
||||
totallength = infile->size();
|
||||
if (infile->length(totallength))
|
||||
{
|
||||
fprintf(stderr, "Fatal error: unable to get length of file\n");
|
||||
goto cleanup;
|
||||
}
|
||||
if (totallength < splitsize)
|
||||
{
|
||||
fprintf(stderr, "Fatal error: file is smaller than the split size\n");
|
||||
goto cleanup;
|
||||
}
|
||||
if ((uint64_t)splitsize * MAX_PARTS < totallength)
|
||||
if ((uint64_t(splitsize) * MAX_PARTS) < totallength)
|
||||
{
|
||||
fprintf(stderr, "Fatal error: too many splits (maximum is %d)\n", MAX_PARTS);
|
||||
goto cleanup;
|
||||
@ -133,12 +137,11 @@ static int split_file(const char *filename, const char *basename, uint32_t split
|
||||
// now iterate until done
|
||||
for (partnum = 0; partnum < 1000; partnum++)
|
||||
{
|
||||
uint32_t actual, length;
|
||||
|
||||
printf("Reading part %d...", partnum);
|
||||
|
||||
// read as much as we can from the file
|
||||
length = infile->read(splitbuffer, splitsize);
|
||||
size_t length;
|
||||
infile->read(splitbuffer, splitsize, length); // FIXME check error return
|
||||
if (length == 0)
|
||||
break;
|
||||
|
||||
@ -163,8 +166,9 @@ static int split_file(const char *filename, const char *basename, uint32_t split
|
||||
printf(" writing %s.%03d...", basefilename.c_str(), partnum);
|
||||
|
||||
// write the data
|
||||
actual = outfile->write(splitbuffer, length);
|
||||
if (actual != length)
|
||||
size_t actual;
|
||||
filerr = outfile->write(splitbuffer, length, actual);
|
||||
if (filerr || (actual != length) || outfile->flush())
|
||||
{
|
||||
printf("\n");
|
||||
fprintf(stderr, "Fatal error: Error writing output file (out of space?)\n");
|
||||
@ -285,8 +289,6 @@ static int join_file(const char *filename, const char *outname, int write_output
|
||||
// now iterate through each file
|
||||
while (splitfile->gets(buffer, sizeof(buffer)))
|
||||
{
|
||||
uint32_t length, actual;
|
||||
|
||||
// make sure the hash and filename are in the right place
|
||||
if (strncmp(buffer, "hash=", 5) != 0 || strncmp(buffer + 5 + SHA1_DIGEST_SIZE * 2, " file=", 6) != 0)
|
||||
{
|
||||
@ -300,6 +302,7 @@ static int join_file(const char *filename, const char *outname, int write_output
|
||||
|
||||
// read the file's contents
|
||||
infilename.insert(0, basepath);
|
||||
uint32_t length;
|
||||
filerr = util::core_file::load(infilename.c_str(), &splitbuffer, length);
|
||||
if (filerr)
|
||||
{
|
||||
@ -324,8 +327,9 @@ static int join_file(const char *filename, const char *outname, int write_output
|
||||
{
|
||||
printf(" writing...");
|
||||
|
||||
actual = outfile->write(splitbuffer, length);
|
||||
if (actual != length)
|
||||
size_t actual;
|
||||
filerr = outfile->write(splitbuffer, length, actual);
|
||||
if (filerr || (actual != length) || outfile->flush())
|
||||
{
|
||||
printf("\n");
|
||||
fprintf(stderr, "Fatal error: Error writing output file (out of space?)\n");
|
||||
|
@ -2287,9 +2287,15 @@ int main(int argc, char *argv[])
|
||||
|
||||
// read/process in chunks
|
||||
output.clear();
|
||||
std::uint64_t remaining(infile->size());
|
||||
std::uint32_t block;
|
||||
while (remaining && (0U != (block = infile->read(original, (std::min)(std::uint64_t(sizeof(original)), remaining)))))
|
||||
std::uint64_t remaining;
|
||||
if (infile->length(remaining))
|
||||
{
|
||||
util::stream_format(std::cerr, "Can't get length of %1$s\n", argv[i]);
|
||||
++failures;
|
||||
continue;
|
||||
}
|
||||
std::size_t block;
|
||||
while (remaining && !infile->read(original, (std::min)(std::uint64_t(sizeof(original)), remaining), block) && block)
|
||||
{
|
||||
remaining -= block;
|
||||
cleaner->process(original, original + block);
|
||||
|
Loading…
Reference in New Issue
Block a user