diff options
author | 2021-10-05 03:34:45 +1100 | |
---|---|---|
committer | 2021-10-05 03:34:45 +1100 | |
commit | aeb9eae87469e67bc6a91caf3840c34c11d959fc (patch) | |
tree | 45bffedf374dbce47caca633ad7bda547592e6c9 | |
parent | 33723892a3f06e678d817b36aef84364c32848ec (diff) |
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.
92 files changed, 3031 insertions, 1381 deletions
diff --git a/scripts/src/lib.lua b/scripts/src/lib.lua index fd8c5be2c27..0f3d637d131 100644 --- a/scripts/src/lib.lua +++ b/scripts/src/lib.lua @@ -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", diff --git a/src/devices/bus/a7800/a78_slot.cpp b/src/devices/bus/a7800/a78_slot.cpp index d55412058d1..65473693f62 100644 --- a/src/devices/bus/a7800/a78_slot.cpp +++ b/src/devices/bus/a7800/a78_slot.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); } diff --git a/src/devices/bus/a800/a800_slot.cpp b/src/devices/bus/a800/a800_slot.cpp index 9ccbb170e1f..4ae54a22663 100644 --- a/src/devices/bus/a800/a800_slot.cpp +++ b/src/devices/bus/a800/a800_slot.cpp @@ -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"); } diff --git a/src/devices/bus/apf/slot.cpp b/src/devices/bus/apf/slot.cpp index c43738fae2a..31cd2abf2d4 100644 --- a/src/devices/bus/apf/slot.cpp +++ b/src/devices/bus/apf/slot.cpp @@ -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); diff --git a/src/devices/bus/aquarius/slot.cpp b/src/devices/bus/aquarius/slot.cpp index 88be36900f4..0c846b6f53b 100644 --- a/src/devices/bus/aquarius/slot.cpp +++ b/src/devices/bus/aquarius/slot.cpp @@ -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)) diff --git a/src/devices/bus/astrocde/slot.cpp b/src/devices/bus/astrocde/slot.cpp index 0e72d9b57b9..b629c57322d 100644 --- a/src/devices/bus/astrocde/slot.cpp +++ b/src/devices/bus/astrocde/slot.cpp @@ -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); diff --git a/src/devices/bus/chanf/slot.cpp b/src/devices/bus/chanf/slot.cpp index f17e52653d0..3a4a9008c95 100644 --- a/src/devices/bus/chanf/slot.cpp +++ b/src/devices/bus/chanf/slot.cpp @@ -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); diff --git a/src/devices/bus/coleco/cartridge/exp.cpp b/src/devices/bus/coleco/cartridge/exp.cpp index 27c8bdff12c..cb12720f8dc 100644 --- a/src/devices/bus/coleco/cartridge/exp.cpp +++ b/src/devices/bus/coleco/cartridge/exp.cpp @@ -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"); } diff --git a/src/devices/bus/crvision/slot.cpp b/src/devices/bus/crvision/slot.cpp index ce1e2a5ea1e..1c86b7ed332 100644 --- a/src/devices/bus/crvision/slot.cpp +++ b/src/devices/bus/crvision/slot.cpp @@ -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); diff --git a/src/devices/bus/ekara/slot.cpp b/src/devices/bus/ekara/slot.cpp index badba2b0ec1..459e14317b6 100644 --- a/src/devices/bus/ekara/slot.cpp +++ b/src/devices/bus/ekara/slot.cpp @@ -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); } diff --git a/src/devices/bus/gamate/slot.cpp b/src/devices/bus/gamate/slot.cpp index 3f98a30927d..869abf24f98 100644 --- a/src/devices/bus/gamate/slot.cpp +++ b/src/devices/bus/gamate/slot.cpp @@ -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); diff --git a/src/devices/bus/gameboy/gb_slot.cpp b/src/devices/bus/gameboy/gb_slot.cpp index 79708f95314..0da541e9f5a 100644 --- a/src/devices/bus/gameboy/gb_slot.cpp +++ b/src/devices/bus/gameboy/gb_slot.cpp @@ -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); diff --git a/src/devices/bus/gba/gba_slot.cpp b/src/devices/bus/gba/gba_slot.cpp index ac036718e2d..f3a85302767 100644 --- a/src/devices/bus/gba/gba_slot.cpp +++ b/src/devices/bus/gba/gba_slot.cpp @@ -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); diff --git a/src/devices/bus/intv/slot.cpp b/src/devices/bus/intv/slot.cpp index 3e086f9c530..727b5af2b5c 100644 --- a/src/devices/bus/intv/slot.cpp +++ b/src/devices/bus/intv/slot.cpp @@ -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); - int type = INTV_STD; - 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 + int type = INTV_STD; 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); diff --git a/src/devices/bus/isa/omti8621.cpp b/src/devices/bus/isa/omti8621.cpp index f14d6808291..f61ab0e905f 100644 --- a/src/devices/bus/isa/omti8621.cpp +++ b/src/devices/bus/isa/omti8621.cpp @@ -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; } diff --git a/src/devices/bus/jakks_gamekey/slot.cpp b/src/devices/bus/jakks_gamekey/slot.cpp index e66e1fed6cd..f680211859a 100644 --- a/src/devices/bus/jakks_gamekey/slot.cpp +++ b/src/devices/bus/jakks_gamekey/slot.cpp @@ -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); } diff --git a/src/devices/bus/megadrive/md_slot.cpp b/src/devices/bus/megadrive/md_slot.cpp index f338fc25d87..47069101a44 100644 --- a/src/devices/bus/megadrive/md_slot.cpp +++ b/src/devices/bus/megadrive/md_slot.cpp @@ -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); } diff --git a/src/devices/bus/msx_slot/cartridge.cpp b/src/devices/bus/msx_slot/cartridge.cpp index 30c9f0a2b00..de998d6d2c8 100644 --- a/src/devices/bus/msx_slot/cartridge.cpp +++ b/src/devices/bus/msx_slot/cartridge.cpp @@ -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); } diff --git a/src/devices/bus/nes/aladdin.cpp b/src/devices/bus/nes/aladdin.cpp index 27f6c975201..325fd7bf87d 100644 --- a/src/devices/bus/nes/aladdin.cpp +++ b/src/devices/bus/nes/aladdin.cpp @@ -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"); } diff --git a/src/devices/bus/nes/nes_slot.cpp b/src/devices/bus/nes/nes_slot.cpp index fef76b520d0..53fc0a2f058 100644 --- a/src/devices/bus/nes/nes_slot.cpp +++ b/src/devices/bus/nes/nes_slot.cpp @@ -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); diff --git a/src/devices/bus/nubus/nubus_image.cpp b/src/devices/bus/nubus/nubus_image.cpp index f017b9cc5a1..bcf072338a6 100644 --- a/src/devices/bus/nubus/nubus_image.cpp +++ b/src/devices/bus/nubus/nubus_image.cpp @@ -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"); diff --git a/src/devices/bus/odyssey2/slot.cpp b/src/devices/bus/odyssey2/slot.cpp index ced47e99955..2abeb58737a 100644 --- a/src/devices/bus/odyssey2/slot.cpp +++ b/src/devices/bus/odyssey2/slot.cpp @@ -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); } diff --git a/src/devices/bus/pce/pce_slot.cpp b/src/devices/bus/pce/pce_slot.cpp index b9a65757afa..8f52410a1a3 100644 --- a/src/devices/bus/pce/pce_slot.cpp +++ b/src/devices/bus/pce/pce_slot.cpp @@ -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); diff --git a/src/devices/bus/scv/slot.cpp b/src/devices/bus/scv/slot.cpp index 86dc2d810c1..0d63399faea 100644 --- a/src/devices/bus/scv/slot.cpp +++ b/src/devices/bus/scv/slot.cpp @@ -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); diff --git a/src/devices/bus/sega8/sega8_slot.cpp b/src/devices/bus/sega8/sega8_slot.cpp index 8a998e71bf0..e6d6f17a5f4 100644 --- a/src/devices/bus/sega8/sega8_slot.cpp +++ b/src/devices/bus/sega8/sega8_slot.cpp @@ -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); diff --git a/src/devices/bus/snes/snes_slot.cpp b/src/devices/bus/snes/snes_slot.cpp index 7c7d4d9ea08..5f84ea91683 100644 --- a/src/devices/bus/snes/snes_slot.cpp +++ b/src/devices/bus/snes/snes_slot.cpp @@ -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); } diff --git a/src/devices/bus/ti99/gromport/cartridges.cpp b/src/devices/bus/ti99/gromport/cartridges.cpp index b4c3655a7e5..bc9c539fc9a 100644 --- a/src/devices/bus/ti99/gromport/cartridges.cpp +++ b/src/devices/bus/ti99/gromport/cartridges.cpp @@ -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()); diff --git a/src/devices/bus/vc4000/slot.cpp b/src/devices/bus/vc4000/slot.cpp index 8de01177448..94de9446dc2 100644 --- a/src/devices/bus/vc4000/slot.cpp +++ b/src/devices/bus/vc4000/slot.cpp @@ -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); diff --git a/src/devices/bus/vcs/vcs_slot.cpp b/src/devices/bus/vcs/vcs_slot.cpp index 5efcc4ba32d..3d0fe0e4fb9 100644 --- a/src/devices/bus/vcs/vcs_slot.cpp +++ b/src/devices/bus/vcs/vcs_slot.cpp @@ -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); } diff --git a/src/devices/bus/vectrex/slot.cpp b/src/devices/bus/vectrex/slot.cpp index 9e3ebd14fa6..ffb7f515379 100644 --- a/src/devices/bus/vectrex/slot.cpp +++ b/src/devices/bus/vectrex/slot.cpp @@ -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); - int type = VECTREX_STD; - - hook.image_file()->read(&rom[0], size); + std::size_t actual; + hook.image_file()->read(&rom[0], size, actual); - if (!memcmp(&rom[0x06], "SRAM", 4)) + int type = VECTREX_STD; + 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); diff --git a/src/devices/bus/wswan/slot.cpp b/src/devices/bus/wswan/slot.cpp index d890917db87..3e11eb00908 100644 --- a/src/devices/bus/wswan/slot.cpp +++ b/src/devices/bus/wswan/slot.cpp @@ -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); diff --git a/src/devices/imagedev/cassette.cpp b/src/devices/imagedev/cassette.cpp index 52103cb1e7d..338f1ed7413 100644 --- a/src/devices/imagedev/cassette.cpp +++ b/src/devices/imagedev/cassette.cpp @@ -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 diff --git a/src/devices/imagedev/chd_cd.cpp b/src/devices/imagedev/chd_cd.cpp index 4eacb3d3b58..753a7b88212 100644 --- a/src/devices/imagedev/chd_cd.cpp +++ b/src/devices/imagedev/chd_cd.cpp @@ -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; diff --git a/src/devices/imagedev/diablo.cpp b/src/devices/imagedev/diablo.cpp index 0a4107ac90a..cdc7665f61d 100644 --- a/src/devices/imagedev/diablo.cpp +++ b/src/devices/imagedev/diablo.cpp @@ -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); diff --git a/src/devices/imagedev/flopdrv.cpp b/src/devices/imagedev/flopdrv.cpp index 316b66ef713..1188519d648 100644 --- a/src/devices/imagedev/flopdrv.cpp +++ b/src/devices/imagedev/flopdrv.cpp @@ -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; diff --git a/src/devices/imagedev/floppy.cpp b/src/devices/imagedev/floppy.cpp index 5d3956e2cab..00d4b77ecd3 100644 --- a/src/devices/imagedev/floppy.cpp +++ b/src/devices/imagedev/floppy.cpp @@ -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; diff --git a/src/devices/imagedev/harddriv.cpp b/src/devices/imagedev/harddriv.cpp index 384bc8c3e92..ff623d58150 100644 --- a/src/devices/imagedev/harddriv.cpp +++ b/src/devices/imagedev/harddriv.cpp @@ -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); } diff --git a/src/devices/imagedev/midiin.cpp b/src/devices/imagedev/midiin.cpp index e2386afb532..63afc1c83f5 100644 --- a/src/devices/imagedev/midiin.cpp +++ b/src/devices/imagedev/midiin.cpp @@ -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); diff --git a/src/devices/machine/hp_dc100_tape.cpp b/src/devices/machine/hp_dc100_tape.cpp index 7d068af39d6..23cf9e4bead 100644 --- a/src/devices/machine/hp_dc100_tape.cpp +++ b/src/devices/machine/hp_dc100_tape.cpp @@ -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); diff --git a/src/emu/devdelegate.h b/src/emu/devdelegate.h index 63bfdb5c369..69381b2576b 100644 --- a/src/emu/devdelegate.h +++ b/src/emu/devdelegate.h @@ -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; } diff --git a/src/emu/diimage.cpp b/src/emu/diimage.cpp index 2c8d389c826..031ed46eb30 100644 --- a/src/emu/diimage.cpp +++ b/src/emu/diimage.cpp @@ -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; } diff --git a/src/emu/diimage.h b/src/emu/diimage.h index 6e5f74d7484..67d20c43402 100644 --- a/src/emu/diimage.h +++ b/src/emu/diimage.h @@ -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; diff --git a/src/emu/fileio.cpp b/src/emu/fileio.cpp index 0fa41d3cd11..6d5d6b8a806 100644 --- a/src/emu/fileio.cpp +++ b/src/emu/fileio.cpp @@ -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; } diff --git a/src/emu/fileio.h b/src/emu/fileio.h index f05706dffed..91aedad173e 100644 --- a/src/emu/fileio.h +++ b/src/emu/fileio.h @@ -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(); diff --git a/src/emu/image.cpp b/src/emu/image.cpp index 02cc25ce80b..5e58758eb89 100644 --- a/src/emu/image.cpp +++ b/src/emu/image.cpp @@ -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)); } } } diff --git a/src/emu/ioport.cpp b/src/emu/ioport.cpp index 4d9e8d186f7..41d7cac5622 100644 --- a/src/emu/ioport.cpp +++ b/src/emu/ioport.cpp @@ -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); } diff --git a/src/emu/recording.cpp b/src/emu/recording.cpp index f23513ad0c8..9e3c1f3f4ae 100644 --- a/src/emu/recording.cpp +++ b/src/emu/recording.cpp @@ -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; } diff --git a/src/emu/rendlay.cpp b/src/emu/rendlay.cpp index b33c58e8fef..b6709626d97 100644 --- a/src/emu/rendlay.cpp +++ b/src/emu/rendlay.cpp @@ -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; diff --git a/src/emu/rendutil.cpp b/src/emu/rendutil.cpp index a902a51bd85..2d0a823db28 100644 --- a/src/emu/rendutil.cpp +++ b/src/emu/rendutil.cpp @@ -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: diff --git a/src/emu/rendutil.h b/src/emu/rendutil.h index 5f1a401b592..4d2b89567e3 100644 --- a/src/emu/rendutil.h +++ b/src/emu/rendutil.h @@ -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); diff --git a/src/emu/save.cpp b/src/emu/save.cpp index 120fa883655..a27a98ee23c 100644 --- a/src/emu/save.cpp +++ b/src/emu/save.cpp @@ -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); }); } diff --git a/src/emu/softlist.cpp b/src/emu/softlist.cpp index d7dec822f3d..34517aa13ce 100644 --- a/src/emu/softlist.cpp +++ b/src/emu/softlist.cpp @@ -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, diff --git a/src/emu/softlist.h b/src/emu/softlist.h index 67c1f9b9535..7622e7e00d1 100644 --- a/src/emu/softlist.h +++ b/src/emu/softlist.h @@ -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, diff --git a/src/emu/softlist_dev.h b/src/emu/softlist_dev.h index b0e774b9519..e0e912a67f1 100644 --- a/src/emu/softlist_dev.h +++ b/src/emu/softlist_dev.h @@ -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 { diff --git a/src/emu/video.cpp b/src/emu/video.cpp index 2e63c76e96a..9ab18b03f4d 100644 --- a/src/emu/video.cpp +++ b/src/emu/video.cpp @@ -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()); } diff --git a/src/frontend/mame/media_ident.cpp b/src/frontend/mame/media_ident.cpp index fbc160f3bcf..b1a18ed7789 100644 --- a/src/frontend/mame/media_ident.cpp +++ b/src/frontend/mame/media_ident.cpp @@ -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) + 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++; + osd_printf_error("%s: error opening file\n", path); + return; } - else + std::uint64_t length; + if (file->length(length)) { - osd_printf_error("%s: error opening file\n", path); + 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++; } } diff --git a/src/frontend/mame/ui/icorender.cpp b/src/frontend/mame/ui/icorender.cpp index 5bda0836838..363e64bad8b 100644 --- a/src/frontend/mame/ui/icorender.cpp +++ b/src/frontend/mame/ui/icorender.cpp @@ -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"); } diff --git a/src/lib/formats/cbm_crt.cpp b/src/lib/formats/cbm_crt.cpp index a61c3247844..2aca7688711 100644 --- a/src/lib/formats/cbm_crt.cpp +++ b/src/lib/formats/cbm_crt.cpp @@ -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; diff --git a/src/lib/formats/cbm_crt.h b/src/lib/formats/cbm_crt.h index d7b257f3e9e..7696dcc276e 100644 --- a/src/lib/formats/cbm_crt.h +++ b/src/lib/formats/cbm_crt.h @@ -14,7 +14,7 @@ #include "formats/imageutl.h" -#include "corefile.h" +#include "utilfwd.h" //************************************************************************** diff --git a/src/lib/util/abi.h b/src/lib/util/abi.h new file mode 100644 index 00000000000..b9e18f16352 --- /dev/null +++ b/src/lib/util/abi.h @@ -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 diff --git a/src/lib/util/cdrom.cpp b/src/lib/util/cdrom.cpp index 7510a1f9b47..8545ed4725a 100644 --- a/src/lib/util/cdrom.cpp +++ b/src/lib/util/cdrom.cpp @@ -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; } diff --git a/src/lib/util/chd.cpp b/src/lib/util/chd.cpp index 29792fc36df..1b4800c74f1 100644 --- a/src/lib/util/chd.cpp +++ b/src/lib/util/chd.cpp @@ -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; diff --git a/src/lib/util/corefile.cpp b/src/lib/util/corefile.cpp index 0c72e1337be..6406aea49f4 100644 --- a/src/lib/util/corefile.cpp +++ b/src/lib/util/corefile.cpp @@ -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 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::uint64_t size() const override { return m_length; } - 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) - { - 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) + std::size_t readlen; + std::uint8_t bom[4]; + read(bom, 4, readlen); + if (readlen == 4) { - 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); - - // do a bounded copy from the buffer to the destination - bytes_read += safe_buffer_copy(m_buffer, 0, m_bufferbytes, buffer, bytes_read, length); + 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 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; - - file = std::make_unique<core_osd_file>(openflags, std::move(f), length); - return std::error_condition(); - } - catch (...) - { - return std::errc::not_enough_memory; - } + // 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; + + 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 diff --git a/src/lib/util/corefile.h b/src/lib/util/corefile.h index 19febbb46f1..802dd1348a1 100644 --- a/src/lib/util/corefile.h +++ b/src/lib/util/corefile.h @@ -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 diff --git a/src/lib/util/delegate.cpp b/src/lib/util/delegate.cpp index 09c2d77496f..880df3bc505 100644 --- a/src/lib/util/delegate.cpp +++ b/src/lib/util/delegate.cpp @@ -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; } diff --git a/src/lib/util/delegate.h b/src/lib/util/delegate.h index 609b6667bfc..df1d6ebcba3 100644 --- a/src/lib/util/delegate.h +++ b/src/lib/util/delegate.h @@ -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; diff --git a/src/lib/util/dynamicclass.cpp b/src/lib/util/dynamicclass.cpp new file mode 100644 index 00000000000..fc9924c09cf --- /dev/null +++ b/src/lib/util/dynamicclass.cpp @@ -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 diff --git a/src/lib/util/dynamicclass.h b/src/lib/util/dynamicclass.h new file mode 100644 index 00000000000..3eaf60d954c --- /dev/null +++ b/src/lib/util/dynamicclass.h @@ -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 diff --git a/src/lib/util/dynamicclass.ipp b/src/lib/util/dynamicclass.ipp new file mode 100644 index 00000000000..d44c5e50e7d --- /dev/null +++ b/src/lib/util/dynamicclass.ipp @@ -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 diff --git a/src/lib/util/flac.cpp b/src/lib/util/flac.cpp index 13bf7aed8eb..9fb0c4c46dc 100644 --- a/src/lib/util/flac.cpp +++ b/src/lib/util/flac.cpp @@ -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; diff --git a/src/lib/util/flac.h b/src/lib/util/flac.h index ffcb7904722..226afef95e6 100644 --- a/src/lib/util/flac.h +++ b/src/lib/util/flac.h @@ -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 diff --git a/src/lib/util/harddisk.cpp b/src/lib/util/harddisk.cpp index 66d98582bb6..ad0108092c8 100644 --- a/src/lib/util/harddisk.cpp +++ b/src/lib/util/harddisk.cpp @@ -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; } diff --git a/src/lib/util/harddisk.h b/src/lib/util/harddisk.h index f6dd39acd70..a586df0f4e0 100644 --- a/src/lib/util/harddisk.h +++ b/src/lib/util/harddisk.h @@ -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 diff --git a/src/lib/util/ioprocs.cpp b/src/lib/util/ioprocs.cpp index 1f52a3daf97..0dcea206571 100644 --- a/src/lib/util/ioprocs.cpp +++ b/src/lib/util/ioprocs.cpp @@ -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 diff --git a/src/lib/util/ioprocs.h b/src/lib/util/ioprocs.h index f2826273c5d..efe0a231a11 100644 --- a/src/lib/util/ioprocs.h +++ b/src/lib/util/ioprocs.h @@ -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 diff --git a/src/lib/util/ioprocsfilter.cpp b/src/lib/util/ioprocsfilter.cpp index b25c0493ab1..68b9c90d018 100644 --- a/src/lib/util/ioprocsfilter.cpp +++ b/src/lib/util/ioprocsfilter.cpp @@ -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 diff --git a/src/lib/util/ioprocsfilter.h b/src/lib/util/ioprocsfilter.h index a146dd737ce..137dda9ce95 100644 --- a/src/lib/util/ioprocsfilter.h +++ b/src/lib/util/ioprocsfilter.h @@ -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; diff --git a/src/lib/util/msdib.cpp b/src/lib/util/msdib.cpp index 58d7429fce1..3120e8cb68c 100644 --- a/src/lib/util/msdib.cpp +++ b/src/lib/util/msdib.cpp @@ -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; diff --git a/src/lib/util/msdib.h b/src/lib/util/msdib.h index 79c1bb6e796..8d8a9078c5b 100644 --- a/src/lib/util/msdib.h +++ b/src/lib/util/msdib.h @@ -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 diff --git a/src/lib/util/path.cpp b/src/lib/util/path.cpp index e69de29bb2d..1b8a2f424dd 100644 --- a/src/lib/util/path.cpp +++ b/src/lib/util/path.cpp @@ -0,0 +1,3 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +#include "path.h" diff --git a/src/lib/util/png.cpp b/src/lib/util/png.cpp index 0605206bbf1..e2fc9aecfe7 100644 --- a/src/lib/util/png.cpp +++ b/src/lib/util/png.cpp @@ -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) { - uint8_t mhdr[28]; - - if (fp.write(MNG_Signature, 8) != 8) - return png_error::FILE_ERROR; + 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]; 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 diff --git a/src/lib/util/png.h b/src/lib/util/png.h index 8c80f0aa5ea..d802c1a5a82 100644 --- a/src/lib/util/png.h +++ b/src/lib/util/png.h @@ -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 diff --git a/src/lib/util/xmlfile.cpp b/src/lib/util/xmlfile.cpp index adcf56a15c7..b290f6e5381 100644 --- a/src/lib/util/xmlfile.cpp +++ b/src/lib/util/xmlfile.cpp @@ -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); diff --git a/src/lib/util/xmlfile.h b/src/lib/util/xmlfile.h index ba3a1176e25..8d0f2b8c36c 100644 --- a/src/lib/util/xmlfile.h +++ b/src/lib/util/xmlfile.h @@ -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); diff --git a/src/mame/machine/coco_vhd.cpp b/src/mame/machine/coco_vhd.cpp index 407d8b506d5..29368003056 100644 --- a/src/mame/machine/coco_vhd.cpp +++ b/src/mame/machine/coco_vhd.cpp @@ -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; diff --git a/src/osd/modules/render/d3d/d3dhlsl.cpp b/src/osd/modules/render/d3d/d3dhlsl.cpp index 6aa69383233..b923deb6f17 100644 --- a/src/osd/modules/render/d3d/d3dhlsl.cpp +++ b/src/osd/modules/render/d3d/d3dhlsl.cpp @@ -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)) diff --git a/src/tools/chdman.cpp b/src/tools/chdman.cpp index d1cb963a8f8..4301c9cc55b 100644 --- a/src/tools/chdman.cpp +++ b/src/tools/chdman.cpp @@ -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); } diff --git a/src/tools/imgtool/stream.cpp b/src/tools/imgtool/stream.cpp index ed9814e504a..014d5735514 100644 --- a/src/tools/imgtool/stream.cpp +++ b/src/tools/imgtool/stream.cpp @@ -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; diff --git a/src/tools/pngcmp.cpp b/src/tools/pngcmp.cpp index 624023425af..480f9fadb14 100644 --- a/src/tools/pngcmp.cpp +++ b/src/tools/pngcmp.cpp @@ -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; } } diff --git a/src/tools/regrep.cpp b/src/tools/regrep.cpp index b0072accfab..b18b6b1383e 100644 --- a/src/tools/regrep.cpp +++ b/src/tools/regrep.cpp @@ -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 */ diff --git a/src/tools/split.cpp b/src/tools/split.cpp index 24953c3d7cd..10078060448 100644 --- a/src/tools/split.cpp +++ b/src/tools/split.cpp @@ -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"); diff --git a/src/tools/srcclean.cpp b/src/tools/srcclean.cpp index 85cf28a22a2..b8402d31f7a 100644 --- a/src/tools/srcclean.cpp +++ b/src/tools/srcclean.cpp @@ -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); |