diff --git a/src/emu/romload.cpp b/src/emu/romload.cpp index c7053b9a3b7..5b76dfeefde 100644 --- a/src/emu/romload.cpp +++ b/src/emu/romload.cpp @@ -53,13 +53,41 @@ auto next_parent_system(game_driver const &system) if (0 > parent) { sys = nullptr; + roms.clear(); + roms.shrink_to_fit(); return nullptr; } else { sys = &driver_list::driver(parent); roms = rom_build_entries(sys->rom); - return &roms[0]; + return &roms.front(); + } + }; +} + + +auto next_parent_device(device_t const &device, emu_options &options) +{ + return + [&options, type = &device.type(), roms = std::vector()] () mutable -> rom_entry const * + { + if (!type) + return nullptr; + type = type->parent_rom_device_type(); + if (!type) + { + roms.clear(); + roms.shrink_to_fit(); + return nullptr; + } + else + { + machine_config config(GAME_NAME(___empty), options); + machine_config::token const tok(config.begin_configuration(config.root_device())); + roms = config.device_add("_tmp", *type, 0)->rom_region_vector(); + config.device_remove("_tmp"); + return &roms.front(); } }; } @@ -106,7 +134,13 @@ std::vector make_software_searchpath(software_list_device &swlist, } -std::error_condition do_open_disk(const emu_options &options, std::initializer_list > > searchpath, const rom_entry *romp, chd_file &chd, std::function next_parent) +std::error_condition do_open_disk( + emu_options const &options, + std::initializer_list const> > searchpath, + rom_entry const *romp, + chd_file &chd, + std::function next_parent, + chd_file::open_parent_func const &open_parent) { // hashes are fixed, but we might need to try multiple filenames std::set tried; @@ -137,7 +171,7 @@ std::error_condition do_open_disk(const emu_options &options, std::initializer_l { fullpath = imgfile->fullpath(); imgfile.reset(); - result = chd.open(fullpath); + result = chd.open(fullpath, false, nullptr, open_parent); } } @@ -159,10 +193,7 @@ std::error_condition do_open_disk(const emu_options &options, std::initializer_l romp = nullptr; break; } - while (ROMENTRY_ISPARAMETER(parent) || ROMENTRY_ISSYSTEM_BIOS(parent) || ROMENTRY_ISDEFAULT_BIOS(parent)) - ++parent; - if (ROMENTRY_ISEND(parent)) - parent = nullptr; + parent = rom_first_region(parent); } else { @@ -183,6 +214,46 @@ std::error_condition do_open_disk(const emu_options &options, std::initializer_l return result; } + +auto open_parent_disk( + emu_options const &options, + std::initializer_list const> > searchpath, + std::function const &next_parent) +{ + return + [&options, searchpath, next_parent] (util::sha1_t const &sha1) -> std::unique_ptr + { + util::hash_collection hashes; + hashes.add_sha1(sha1); + std::function np(next_parent); + for (rom_entry const *parent = np(); parent; parent = np()) + { + parent = rom_first_region(parent); + while (parent) + { + while (parent && !ROMREGION_ISDISKDATA(parent)) + parent = rom_next_region(parent); + if (parent) + { + rom_entry const *romp = rom_first_file(parent); + while (romp) + { + if (util::hash_collection(romp->hashdata()) == hashes) + { + std::unique_ptr chd(new chd_file); + if (!do_open_disk(options, searchpath, romp, *chd, next_parent, nullptr)) + return chd; + } + romp = rom_next_file(romp); + } + parent = rom_next_region(parent); + } + } + } + return nullptr; + }; +} + } // anonymous namespace @@ -640,7 +711,11 @@ void rom_load_manager::region_post_process(memory_region *region, bool invert) up the parent and loading by checksum -------------------------------------------------*/ -std::unique_ptr rom_load_manager::open_rom_file(std::initializer_list > > searchpath, const rom_entry *romp, std::vector &tried_file_names, bool from_list) +std::unique_ptr rom_load_manager::open_rom_file( + search_paths searchpath, + const rom_entry *romp, + std::vector &tried_file_names, + bool from_list) { std::error_condition filerr = std::errc::no_such_file_or_directory; u32 const romsize = rom_file_size(romp); @@ -675,7 +750,13 @@ std::unique_ptr rom_load_manager::open_rom_file(std::initializer_list< } -std::unique_ptr rom_load_manager::open_rom_file(const std::vector &paths, std::vector &tried, bool has_crc, u32 crc, std::string_view name, std::error_condition &filerr) +std::unique_ptr rom_load_manager::open_rom_file( + const std::vector &paths, + std::vector &tried, + bool has_crc, + u32 crc, + std::string_view name, + std::error_condition &filerr) { // record the set names we search tried.insert(tried.end(), paths.begin(), paths.end()); @@ -718,7 +799,11 @@ int rom_load_manager::rom_fread(emu_file *file, u8 *buffer, int length, const ro entry -------------------------------------------------*/ -int rom_load_manager::read_rom_data(emu_file *file, const rom_entry *parent_region, const rom_entry *romp) +int rom_load_manager::read_rom_data( + emu_file *file, + memory_region ®ion, + const rom_entry *parent_region, + const rom_entry *romp) { int datashift = ROM_GETBITSHIFT(romp); int datamask = ((1 << ROM_GETBITWIDTH(romp)) - 1) << datashift; @@ -727,7 +812,7 @@ int rom_load_manager::read_rom_data(emu_file *file, const rom_entry *parent_regi int skip = ROM_GETSKIPCOUNT(romp); int reversed = ROM_ISREVERSED(romp); int numgroups = (numbytes + groupsize - 1) / groupsize; - u8 *base = m_region->base() + ROM_GETOFFSET(romp); + u8 *base = region.base() + ROM_GETOFFSET(romp); u32 tempbufsize; int i; @@ -738,7 +823,7 @@ int rom_load_manager::read_rom_data(emu_file *file, const rom_entry *parent_regi osd_printf_warning("Warning in RomModule definition: %s length not an even multiple of group size\n", romp->name()); /* make sure we only fill within the region space */ - if (ROM_GETOFFSET(romp) + numgroups * groupsize + (numgroups - 1) * skip > m_region->bytes()) + if (ROM_GETOFFSET(romp) + numgroups * groupsize + (numgroups - 1) * skip > region.bytes()) throw emu_fatalerror("Error in RomModule definition: %s out of memory region space\n", romp->name()); /* make sure the length was valid */ @@ -833,14 +918,14 @@ int rom_load_manager::read_rom_data(emu_file *file, const rom_entry *parent_regi fill_rom_data - fill a region of ROM space -------------------------------------------------*/ -void rom_load_manager::fill_rom_data(const rom_entry *romp) +void rom_load_manager::fill_rom_data(memory_region ®ion, const rom_entry *romp) { u32 numbytes = ROM_GETLENGTH(romp); int skip = ROM_GETSKIPCOUNT(romp); - u8 *base = m_region->base() + ROM_GETOFFSET(romp); + u8 *base = region.base() + ROM_GETOFFSET(romp); // make sure we fill within the region space - if (ROM_GETOFFSET(romp) + numbytes > m_region->bytes()) + if (ROM_GETOFFSET(romp) + numbytes > region.bytes()) throw emu_fatalerror("Error in RomModule definition: FILL out of memory region space\n"); // make sure the length was valid @@ -865,15 +950,15 @@ void rom_load_manager::fill_rom_data(const rom_entry *romp) copy_rom_data - copy a region of ROM space -------------------------------------------------*/ -void rom_load_manager::copy_rom_data(const rom_entry *romp) +void rom_load_manager::copy_rom_data(memory_region ®ion, const rom_entry *romp) { - u8 *base = m_region->base() + ROM_GETOFFSET(romp); + u8 *base = region.base() + ROM_GETOFFSET(romp); const std::string &srcrgntag = romp->name(); u32 numbytes = ROM_GETLENGTH(romp); u32 srcoffs = u32(strtol(romp->hashdata().c_str(), nullptr, 0)); /* srcoffset in place of hashdata */ /* make sure we copy within the region space */ - if (ROM_GETOFFSET(romp) + numbytes > m_region->bytes()) + if (ROM_GETOFFSET(romp) + numbytes > region.bytes()) throw emu_fatalerror("Error in RomModule definition: COPY out of target memory region space\n"); /* make sure the length was valid */ @@ -881,16 +966,16 @@ void rom_load_manager::copy_rom_data(const rom_entry *romp) throw emu_fatalerror("Error in RomModule definition: COPY has an invalid length\n"); /* make sure the source was valid */ - memory_region *region = machine().root_device().memregion(srcrgntag); - if (region == nullptr) + memory_region *src = machine().root_device().memregion(srcrgntag); + if (!src) throw emu_fatalerror("Error in RomModule definition: COPY from an invalid region\n"); /* make sure we find within the region space */ - if (srcoffs + numbytes > region->bytes()) + if (srcoffs + numbytes > src->bytes()) throw emu_fatalerror("Error in RomModule definition: COPY out of source memory region space\n"); /* fill the data */ - memcpy(base, region->base() + srcoffs, numbytes); + memcpy(base, src->base() + srcoffs, numbytes); } @@ -899,7 +984,13 @@ void rom_load_manager::copy_rom_data(const rom_entry *romp) for a region -------------------------------------------------*/ -void rom_load_manager::process_rom_entries(std::initializer_list > > searchpath, u8 bios, const rom_entry *parent_region, const rom_entry *romp, bool from_list) +void rom_load_manager::process_rom_entries( + search_paths searchpath, + u8 bios, + memory_region ®ion, + const rom_entry *parent_region, + const rom_entry *romp, + bool from_list) { u32 lastflags = 0; std::vector tried_file_names; @@ -921,13 +1012,13 @@ void rom_load_manager::process_rom_entries(std::initializer_listname() + ".dif"; @@ -1050,11 +1145,20 @@ std::error_condition rom_load_manager::open_disk_diff(emu_options &options, cons for a region -------------------------------------------------*/ -void rom_load_manager::process_disk_entries(std::initializer_list > > searchpath, std::string_view regiontag, const rom_entry *romp, std::function next_parent) +void rom_load_manager::process_disk_entries( + search_paths searchpath, + std::string_view regiontag, + const rom_entry *romp, + std::function next_parent, + chd_file::open_parent_func const &open_parent) { /* remove existing disk entries for this region */ - m_chd_list.erase(std::remove_if(m_chd_list.begin(), m_chd_list.end(), - [regiontag] (std::unique_ptr &chd) { return chd->region() == regiontag; }), m_chd_list.end()); + m_chd_list.erase( + std::remove_if( + m_chd_list.begin(), + m_chd_list.end(), + [®iontag] (std::unique_ptr &chd) { return chd->region() == regiontag; }), + m_chd_list.end()); /* loop until we hit the end of this region */ for ( ; !ROMENTRY_ISREGIONEND(romp); romp++) @@ -1071,7 +1175,9 @@ void rom_load_manager::process_disk_entries(std::initializer_listorig_chd(), next_parent); + err = do_open_disk(machine().options(), searchpath, romp, chd->orig_chd(), next_parent, open_parent); + if (!err && chd->orig_chd().parent_missing()) + err = chd_file::error::REQUIRES_PARENT; if (err) { std::vector tried; @@ -1142,7 +1248,11 @@ std::vector rom_load_manager::get_software_searchpath(software_list device -------------------------------------------------*/ -std::error_condition rom_load_manager::open_disk_image(const emu_options &options, const device_t &device, const rom_entry *romp, chd_file &image_chd) +std::error_condition rom_load_manager::open_disk_image( + emu_options &options, + const device_t &device, + const rom_entry *romp, + chd_file &image_chd) { const std::vector searchpath(device.searchpath()); @@ -1151,8 +1261,14 @@ std::error_condition rom_load_manager::open_disk_image(const emu_options &option if (driver) next_parent = next_parent_system(driver->system()); else - next_parent = [] () { return nullptr; }; - return do_open_disk(options, { searchpath }, romp, image_chd, std::move(next_parent)); + next_parent = next_parent_device(device, options); + chd_file::open_parent_func open_parent(open_parent_disk(options, { searchpath }, next_parent)); + std::error_condition const err( + do_open_disk(options, { searchpath }, romp, image_chd, std::move(next_parent), open_parent)); + if (!err && image_chd.parent_missing()) + return chd_file::error::REQUIRES_PARENT; + else + return err; } @@ -1161,12 +1277,24 @@ std::error_condition rom_load_manager::open_disk_image(const emu_options &option software item -------------------------------------------------*/ -std::error_condition rom_load_manager::open_disk_image(const emu_options &options, software_list_device &swlist, const software_info &swinfo, const rom_entry *romp, chd_file &image_chd) +std::error_condition rom_load_manager::open_disk_image( + emu_options &options, + software_list_device &swlist, + const software_info &swinfo, + const rom_entry *romp, + chd_file &image_chd) { std::vector parents; - std::vector searchpath = make_software_searchpath(swlist, swinfo, parents); + std::vector searchpath(make_software_searchpath(swlist, swinfo, parents)); searchpath.emplace_back(swlist.list_name()); // look for loose disk images in software list directory - return do_open_disk(options, { searchpath }, romp, image_chd, next_parent_software(parents)); + std::function next_parent(next_parent_software(parents)); + chd_file::open_parent_func open_parent(open_parent_disk(options, { searchpath }, next_parent)); + std::error_condition const err( + do_open_disk(options, { searchpath }, romp, image_chd, std::move(next_parent), open_parent)); + if (!err && image_chd.parent_missing()) + return chd_file::error::REQUIRES_PARENT; + else + return err; } @@ -1262,6 +1390,7 @@ void rom_load_manager::load_software_part_region(device_t &device, software_list // loop until we hit the end std::function next_parent; + chd_file::open_parent_func open_parent; for (const rom_entry *region = start_region; region != nullptr; region = rom_next_region(region)) { u32 regionlength = ROMREGION_GETLENGTH(region); @@ -1276,7 +1405,7 @@ void rom_load_manager::load_software_part_region(device_t &device, software_list endianness_t endianness = ROMREGION_ISBIGENDIAN(region) ? ENDIANNESS_BIG : ENDIANNESS_LITTLE; u8 width = ROMREGION_GETWIDTH(region) / 8; memory_region *memregion = machine().root_device().memregion(regiontag); - if (memregion != nullptr) + if (memregion) { normalize_flags_for_device(regiontag, width, endianness); @@ -1285,16 +1414,16 @@ void rom_load_manager::load_software_part_region(device_t &device, software_list } // remember the base and length - m_region = machine().memory().region_alloc(regiontag, regionlength, width, endianness); - LOG("Allocated %X bytes @ %p\n", m_region->bytes(), m_region->base()); + memregion = machine().memory().region_alloc(regiontag, regionlength, width, endianness); + LOG("Allocated %X bytes @ %p\n", memregion->bytes(), memregion->base()); if (ROMREGION_ISERASE(region)) // clear the region if it's requested - memset(m_region->base(), ROMREGION_GETERASEVAL(region), m_region->bytes()); - else if (m_region->bytes() <= 0x400000) // or if it's sufficiently small (<= 4MB) - memset(m_region->base(), 0, m_region->bytes()); + memset(memregion->base(), ROMREGION_GETERASEVAL(region), memregion->bytes()); + else if (memregion->bytes() <= 0x400000) // or if it's sufficiently small (<= 4MB) + memset(memregion->base(), 0, memregion->bytes()); #ifdef MAME_DEBUG else // if we're debugging, fill region with random data to catch errors - fill_random(m_region->base(), m_region->bytes()); + fill_random(memregion->base(), memregion->bytes()); #endif // update total number of roms @@ -1308,9 +1437,9 @@ void rom_load_manager::load_software_part_region(device_t &device, software_list if (ROMREGION_ISROMDATA(region)) { if (devsearch.empty()) - process_rom_entries({ swsearch }, 0U, region, region + 1, true); + process_rom_entries({ swsearch }, 0U, *memregion, region, region + 1, true); else - process_rom_entries({ swsearch, devsearch }, 0U, region, region + 1, true); + process_rom_entries({ swsearch, devsearch }, 0U, *memregion, region, region + 1, true); } else if (ROMREGION_ISDISKDATA(region)) { @@ -1320,11 +1449,15 @@ void rom_load_manager::load_software_part_region(device_t &device, software_list next_parent = next_parent_software(parents); else next_parent = [] () { return nullptr; }; + if (devsearch.empty()) + open_parent = open_parent_disk(machine().options(), { swsearch, disksearch }, next_parent); + else + open_parent = open_parent_disk(machine().options(), { swsearch, disksearch, devsearch }, next_parent); } if (devsearch.empty()) - process_disk_entries({ swsearch, disksearch }, regiontag, region + 1, next_parent); + process_disk_entries({ swsearch, disksearch }, regiontag, region + 1, next_parent, open_parent); else - process_disk_entries({ swsearch, disksearch, devsearch }, regiontag, region + 1, next_parent); + process_disk_entries({ swsearch, disksearch, devsearch }, regiontag, region + 1, next_parent, open_parent); } } @@ -1350,6 +1483,7 @@ void rom_load_manager::process_region_list() { searchpath.clear(); std::function next_parent; + chd_file::open_parent_func open_parent; for (const rom_entry *region = rom_first_region(device); region != nullptr; region = rom_next_region(region)) { u32 regionlength = ROMREGION_GETLENGTH(region); @@ -1368,23 +1502,23 @@ void rom_load_manager::process_region_list() normalize_flags_for_device(regiontag, width, endianness); // remember the base and length - m_region = machine().memory().region_alloc(regiontag, regionlength, width, endianness); - LOG("Allocated %X bytes @ %p\n", m_region->bytes(), m_region->base()); + memory_region *const memregion = machine().memory().region_alloc(regiontag, regionlength, width, endianness); + LOG("Allocated %X bytes @ %p\n", memregion->bytes(), memregion->base()); if (ROMREGION_ISERASE(region)) // clear the region if it's requested - memset(m_region->base(), ROMREGION_GETERASEVAL(region), m_region->bytes()); - else if (m_region->bytes() <= 0x400000) // or if it's sufficiently small (<= 4MB) - memset(m_region->base(), 0, m_region->bytes()); + memset(memregion->base(), ROMREGION_GETERASEVAL(region), memregion->bytes()); + else if (memregion->bytes() <= 0x400000) // or if it's sufficiently small (<= 4MB) + memset(memregion->base(), 0, memregion->bytes()); #ifdef MAME_DEBUG else // if we're debugging, fill region with random data to catch errors - fill_random(m_region->base(), m_region->bytes()); + fill_random(memregion->base(), memregion->bytes()); #endif // now process the entries in the region if (searchpath.empty()) searchpath = device.searchpath(); assert(!searchpath.empty()); - process_rom_entries({ searchpath }, device.system_bios(), region, region + 1, false); + process_rom_entries({ searchpath }, device.system_bios(), *memregion, region, region + 1, false); } else if (ROMREGION_ISDISKDATA(region)) { @@ -1397,9 +1531,10 @@ void rom_load_manager::process_region_list() if (driver) next_parent = next_parent_system(driver->system()); else - next_parent = [] () { return nullptr; }; + next_parent = next_parent_device(device, machine().options()); + open_parent = open_parent_disk(machine().options(), { searchpath }, next_parent); } - process_disk_entries({ searchpath }, regiontag, region + 1, next_parent); + process_disk_entries({ searchpath }, regiontag, region + 1, next_parent, open_parent); } } } @@ -1436,7 +1571,6 @@ rom_load_manager::rom_load_manager(running_machine &machine) , m_romsloadedsize(0) , m_romstotalsize(0) , m_chd_list() - , m_region(nullptr) , m_errorstring() , m_softwarningstring() { diff --git a/src/emu/romload.h b/src/emu/romload.h index c273a08b743..001f27aba83 100644 --- a/src/emu/romload.h +++ b/src/emu/romload.h @@ -102,11 +102,11 @@ protected: const_entry_iterator &operator=(const_entry_iterator &&) noexcept = default; public: - typedef T value_type; - typedef value_type const *pointer; - typedef value_type const &reference; - typedef std::ptrdiff_t difference_type; - typedef std::forward_iterator_tag iterator_category; + using value_type = T; + using pointer = value_type const *; + using reference = value_type const &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; reference operator*() const noexcept { return reinterpret_cast(*m_data); } pointer operator->() const noexcept { return reinterpret_cast(m_data); } @@ -387,6 +387,7 @@ class rom_load_manager { public: open_chd(std::string_view region) : m_region(region) { } + open_chd(std::string &®ion) : m_region(std::move(region)) { } std::string_view region() const { return m_region; } chd_file &chd() { return m_diffchd.opened() ? m_diffchd : m_origchd; } @@ -394,9 +395,9 @@ class rom_load_manager chd_file &diff_chd() { return m_diffchd; } private: - std::string m_region; /* disk region we came from */ - chd_file m_origchd; /* handle to the original CHD */ - chd_file m_diffchd; /* handle to the diff CHD */ + std::string m_region; // disk region we came from + chd_file m_origchd; // handle to the original CHD + chd_file m_diffchd; // handle to the diff CHD }; public: @@ -428,10 +429,12 @@ public: static std::vector get_software_searchpath(software_list_device &swlist, const software_info &swinfo); /* open a disk image, searching up the parent and loading by checksum */ - static std::error_condition open_disk_image(const emu_options &options, const device_t &device, const rom_entry *romp, chd_file &image_chd); - static std::error_condition open_disk_image(const emu_options &options, software_list_device &swlist, const software_info &swinfo, const rom_entry *romp, chd_file &image_chd); + static std::error_condition open_disk_image(emu_options &options, const device_t &device, const rom_entry *romp, chd_file &image_chd); + static std::error_condition open_disk_image(emu_options &options, software_list_device &swlist, const software_info &swinfo, const rom_entry *romp, chd_file &image_chd); private: + using search_paths = std::initializer_list > >; + void determine_bios_rom(device_t &device, std::string_view specbios); void count_roms(); void fill_random(u8 *base, u32 length); @@ -441,15 +444,40 @@ private: void display_loading_rom_message(const char *name, bool from_list); void display_rom_load_results(bool from_list); void region_post_process(memory_region *region, bool invert); - std::unique_ptr open_rom_file(std::initializer_list > > searchpath, const rom_entry *romp, std::vector &tried_file_names, bool from_list); - std::unique_ptr open_rom_file(const std::vector &paths, std::vector &tried, bool has_crc, u32 crc, std::string_view name, std::error_condition &filerr); + std::unique_ptr open_rom_file( + search_paths searchpath, + const rom_entry *romp, + std::vector &tried_file_names, + bool from_list); + std::unique_ptr open_rom_file( + const std::vector &paths, + std::vector &tried, + bool has_crc, + u32 crc, + std::string_view name, + std::error_condition &filerr); int rom_fread(emu_file *file, u8 *buffer, int length, const rom_entry *parent_region); - int read_rom_data(emu_file *file, const rom_entry *parent_region, const rom_entry *romp); - void fill_rom_data(const rom_entry *romp); - void copy_rom_data(const rom_entry *romp); - void process_rom_entries(std::initializer_list > > searchpath, u8 bios, const rom_entry *parent_region, const rom_entry *romp, bool from_list); - std::error_condition open_disk_diff(emu_options &options, const rom_entry *romp, chd_file &source, chd_file &diff_chd); - void process_disk_entries(std::initializer_list > > searchpath, std::string_view regiontag, const rom_entry *romp, std::function next_parent); + int read_rom_data(emu_file *file, memory_region ®ion, const rom_entry *parent_region, const rom_entry *romp); + void fill_rom_data(memory_region ®ion, const rom_entry *romp); + void copy_rom_data(memory_region ®ion, const rom_entry *romp); + void process_rom_entries( + search_paths searchpath, + u8 bios, + memory_region ®ion, + const rom_entry *parent_region, + const rom_entry *romp, + bool from_list); + std::error_condition open_disk_diff( + emu_options &options, + const rom_entry *romp, + chd_file &source, + chd_file &diff_chd); + void process_disk_entries( + search_paths searchpath, + std::string_view regiontag, + const rom_entry *romp, + std::function next_parent, + const chd_file::open_parent_func &open_parent); void normalize_flags_for_device(std::string_view rgntag, u8 &width, endianness_t &endian); void process_region_list(); @@ -467,8 +495,6 @@ private: std::vector> m_chd_list; /* disks */ - memory_region * m_region; // info about current region - std::string m_errorstring; // error string std::string m_softwarningstring; // software warning string }; diff --git a/src/frontend/mame/audit.cpp b/src/frontend/mame/audit.cpp index 129126bb2ef..dbbb9ad8f3d 100644 --- a/src/frontend/mame/audit.cpp +++ b/src/frontend/mame/audit.cpp @@ -656,6 +656,9 @@ media_auditor::audit_record &media_auditor::audit_one_disk(const rom_entry *rom, chd_file source; const std::error_condition err = rom_load_manager::open_disk_image(m_enumerator.options(), std::forward(args)..., rom, source); + // FIXME: A CHD with an invalid header or missing parent is treated as not found. + // We need a way to report more detailed errors for bad disk images. + // if we succeeded, get the hashes if (!err) { diff --git a/src/lib/util/chd.cpp b/src/lib/util/chd.cpp index 1766ba9898d..11f0b93eb86 100644 --- a/src/lib/util/chd.cpp +++ b/src/lib/util/chd.cpp @@ -318,6 +318,16 @@ util::random_read &chd_file::file() return *m_file; } +bool chd_file::parent_missing() const noexcept +{ + if (m_parent_missing) + return true; + else if (!m_parent) + return false; + else + return m_parent->parent_missing(); +} + /** * @fn util::sha1_t chd_file::sha1() * @@ -590,7 +600,12 @@ void chd_file::set_parent_sha1(util::sha1_t parent) * @return A std::error_condition. */ -std::error_condition chd_file::create(util::random_read_write::ptr &&file, uint64_t logicalbytes, uint32_t hunkbytes, uint32_t unitbytes, chd_codec_type compression[4]) +std::error_condition chd_file::create( + util::random_read_write::ptr &&file, + uint64_t logicalbytes, + uint32_t hunkbytes, + uint32_t unitbytes, + chd_codec_type compression[4]) { // make sure we don't already have a file open if (m_file) @@ -603,7 +618,7 @@ std::error_condition chd_file::create(util::random_read_write::ptr &&file, uint6 m_hunkbytes = hunkbytes; m_unitbytes = unitbytes; memcpy(m_compression, compression, sizeof(m_compression)); - m_parent = nullptr; + m_parent.reset(); // take ownership of the file m_file = std::move(file); @@ -626,7 +641,12 @@ std::error_condition chd_file::create(util::random_read_write::ptr &&file, uint6 * @return A std::error_condition. */ -std::error_condition chd_file::create(util::random_read_write::ptr &&file, uint64_t logicalbytes, uint32_t hunkbytes, chd_codec_type compression[4], chd_file &parent) +std::error_condition chd_file::create( + util::random_read_write::ptr &&file, + uint64_t logicalbytes, + uint32_t hunkbytes, + chd_codec_type compression[4], + chd_file &parent) { // make sure we don't already have a file open if (m_file) @@ -639,7 +659,7 @@ std::error_condition chd_file::create(util::random_read_write::ptr &&file, uint6 m_hunkbytes = hunkbytes; m_unitbytes = parent.unit_bytes(); memcpy(m_compression, compression, sizeof(m_compression)); - m_parent = &parent; + m_parent = std::shared_ptr(std::shared_ptr(), &parent); // take ownership of the file m_file = std::move(file); @@ -662,7 +682,12 @@ std::error_condition chd_file::create(util::random_read_write::ptr &&file, uint6 * @return A std::error_condition. */ -std::error_condition chd_file::create(std::string_view filename, uint64_t logicalbytes, uint32_t hunkbytes, uint32_t unitbytes, chd_codec_type compression[4]) +std::error_condition chd_file::create( + std::string_view filename, + uint64_t logicalbytes, + uint32_t hunkbytes, + uint32_t unitbytes, + chd_codec_type compression[4]) { // make sure we don't already have a file open if (m_file) @@ -702,7 +727,12 @@ std::error_condition chd_file::create(std::string_view filename, uint64_t logica * @return A std::error_condition. */ -std::error_condition chd_file::create(std::string_view filename, uint64_t logicalbytes, uint32_t hunkbytes, chd_codec_type compression[4], chd_file &parent) +std::error_condition chd_file::create( + std::string_view filename, + uint64_t logicalbytes, + uint32_t hunkbytes, + chd_codec_type compression[4], + chd_file &parent) { // make sure we don't already have a file open if (m_file) @@ -740,7 +770,11 @@ std::error_condition chd_file::create(std::string_view filename, uint64_t logica * @return A std::error_condition. */ -std::error_condition chd_file::open(std::string_view filename, bool writeable, chd_file *parent) +std::error_condition chd_file::open( + std::string_view filename, + bool writeable, + chd_file *parent, + const open_parent_func &open_parent) { // make sure we don't already have a file open if (m_file) @@ -754,12 +788,7 @@ std::error_condition chd_file::open(std::string_view filename, bool writeable, c return filerr; // now open the CHD - std::error_condition err = open(std::move(file), writeable, parent); - if (err) - return err; - - // we now own this file - return err; + return open(std::move(file), writeable, parent, open_parent); } /** @@ -776,7 +805,11 @@ std::error_condition chd_file::open(std::string_view filename, bool writeable, c * @return A std::error_condition. */ -std::error_condition chd_file::open(util::random_read_write::ptr &&file, bool writeable, chd_file *parent) +std::error_condition chd_file::open( + util::random_read_write::ptr &&file, + bool writeable, + chd_file *parent, + const open_parent_func &open_parent) { // make sure we don't already have a file open if (m_file) @@ -786,9 +819,9 @@ std::error_condition chd_file::open(util::random_read_write::ptr &&file, bool wr // open the file m_file = std::move(file); - m_parent = parent; + m_parent = std::shared_ptr(std::shared_ptr(), parent); m_cachehunk = ~0; - return open_common(writeable); + return open_common(writeable, open_parent); } /** @@ -816,7 +849,7 @@ void chd_file::close() m_unitbytes = 0; m_unitcount = 0; memset(m_compression, 0, sizeof(m_compression)); - m_parent = nullptr; + m_parent.reset(); m_parent_missing = false; // reset key offsets within the header @@ -936,7 +969,7 @@ std::error_condition chd_file::read_hunk(uint32_t hunknum, void *buffer) file_read(blockoffs, dest, m_hunkbytes); else if (m_parent_missing) throw std::error_condition(error::REQUIRES_PARENT); - else if (m_parent != nullptr) + else if (m_parent) m_parent->read_hunk(hunknum, dest); else memset(dest, 0, m_hunkbytes); @@ -2300,7 +2333,7 @@ std::error_condition chd_file::create_common() put_u32be(&rawheader[60], m_unitbytes); be_write_sha1(&rawheader[64], util::sha1_t::null); be_write_sha1(&rawheader[84], util::sha1_t::null); - be_write_sha1(&rawheader[104], (m_parent != nullptr) ? m_parent->sha1() : util::sha1_t::null); + be_write_sha1(&rawheader[104], m_parent ? m_parent->sha1() : util::sha1_t::null); // write the resulting header file_write(0, rawheader, sizeof(rawheader)); @@ -2368,7 +2401,7 @@ std::error_condition chd_file::create_common() * @return A std::error_condition. */ -std::error_condition chd_file::open_common(bool writeable) +std::error_condition chd_file::open_common(bool writeable, const open_parent_func &open_parent) { // wrap in try for proper error handling try @@ -2406,13 +2439,18 @@ std::error_condition chd_file::open_common(bool writeable) // make sure we have a parent if we need one (and don't if we don't) if (parentsha1 != util::sha1_t::null) { + if (!m_parent && open_parent) + m_parent = open_parent(parentsha1); + if (!m_parent) m_parent_missing = true; else if (m_parent->sha1() != parentsha1) throw std::error_condition(error::INVALID_PARENT); } else if (m_parent) + { throw std::error_condition(std::errc::invalid_argument); + } // finish opening the file create_open_common(); @@ -2789,7 +2827,7 @@ chd_file_compressor::~chd_file_compressor() void chd_file_compressor::compress_begin() { // reset state - m_walking_parent = (m_parent != nullptr); + m_walking_parent = bool(m_parent); m_total_in = 0; m_total_out = 0; m_compsha1.reset(); @@ -2922,7 +2960,7 @@ std::error_condition chd_file_compressor::compress_continue(double &progress, do } // if not, see if it's in the parent map - if (m_parent != nullptr) + if (m_parent) { uint64_t parentunit = m_parent_map.find(item.m_hash[0].m_crc16, item.m_hash[0].m_sha1); if (parentunit != hashmap::NOT_FOUND) diff --git a/src/lib/util/chd.h b/src/lib/util/chd.h index 3d9c54dedda..3e52cab4f9b 100644 --- a/src/lib/util/chd.h +++ b/src/lib/util/chd.h @@ -19,6 +19,8 @@ #include "osdcore.h" #include +#include +#include #include #include #include @@ -292,22 +294,25 @@ public: COMPRESSING }; + using open_parent_func = std::function (util::sha1_t const &)>; + // construction/destruction chd_file(); virtual ~chd_file(); // getters util::random_read &file(); - bool opened() const { return bool(m_file); } - uint32_t version() const { return m_version; } - uint64_t logical_bytes() const { return m_logicalbytes; } - uint32_t hunk_bytes() const { return m_hunkbytes; } - uint32_t hunk_count() const { return m_hunkcount; } - uint32_t unit_bytes() const { return m_unitbytes; } - uint64_t unit_count() const { return m_unitcount; } + bool opened() const noexcept { return bool(m_file); } + uint32_t version() const noexcept { return m_version; } + uint64_t logical_bytes() const noexcept { return m_logicalbytes; } + uint32_t hunk_bytes() const noexcept { return m_hunkbytes; } + uint32_t hunk_count() const noexcept { return m_hunkcount; } + uint32_t unit_bytes() const noexcept { return m_unitbytes; } + uint64_t unit_count() const noexcept { return m_unitcount; } bool compressed() const { return (m_compression[0] != CHD_CODEC_NONE); } - chd_codec_type compression(int index) const { return m_compression[index]; } - chd_file *parent() const { return m_parent; } + chd_codec_type compression(int index) const noexcept { return m_compression[index]; } + chd_file *parent() const noexcept { return m_parent.get(); } + bool parent_missing() const noexcept; util::sha1_t sha1(); util::sha1_t raw_sha1(); util::sha1_t parent_sha1(); @@ -324,8 +329,8 @@ public: std::error_condition create(util::random_read_write::ptr &&file, uint64_t logicalbytes, uint32_t hunkbytes, chd_codec_type compression[4], chd_file &parent); // file open - std::error_condition open(std::string_view filename, bool writeable = false, chd_file *parent = nullptr); - std::error_condition open(util::random_read_write::ptr &&file, bool writeable = false, chd_file *parent = nullptr); + std::error_condition open(std::string_view filename, bool writeable = false, chd_file *parent = nullptr, const open_parent_func &open_parent = nullptr); + std::error_condition open(util::random_read_write::ptr &&file, bool writeable = false, chd_file *parent = nullptr, const open_parent_func &open_parent = nullptr); // file close void close(); @@ -382,7 +387,7 @@ private: std::error_condition compress_v5_map(); void decompress_v5_map(); std::error_condition create_common(); - std::error_condition open_common(bool writeable); + std::error_condition open_common(bool writeable, const open_parent_func &open_parent); void create_open_common(); void verify_proper_compression_append(uint32_t hunknum); void hunk_write_compressed(uint32_t hunknum, int8_t compression, const uint8_t *compressed, uint32_t complength, util::crc16_t crc16); @@ -408,7 +413,7 @@ private: uint32_t m_unitbytes; // size of each unit in bytes uint64_t m_unitcount; // number of units represented chd_codec_type m_compression[4]; // array of compression types used - chd_file * m_parent; // pointer to parent file, or nullptr if none + std::shared_ptr m_parent; // pointer to parent file, or nullptr if none bool m_parent_missing; // are we missing our parent? // key offsets within the header