Allow clone CHDs to use parent CHDs as parents.

* util/chd.cpp: Allow caller to provide a helper for finding parent CHDs
  and expose (recursive) missing parent status.
* emu/romload.cpp: Search parent systems/devices/software for parent
  CHDs on encountering a delta CHD.
* emu/romload.cpp: Report error on delta CHDs when parent can't be
  found.
* emu/romload.cpp: Check parents for matching CHDs with different names
  for devices as well as systems and software.
This commit is contained in:
Vas Crabb 2023-10-20 05:47:15 +11:00
parent 4dfc36ae47
commit d1172bf710
5 changed files with 319 additions and 113 deletions

View File

@ -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<rom_entry>()] () 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<std::string> make_software_searchpath(software_list_device &swlist,
}
std::error_condition do_open_disk(const emu_options &options, std::initializer_list<std::reference_wrapper<const std::vector<std::string> > > searchpath, const rom_entry *romp, chd_file &chd, std::function<const rom_entry * ()> next_parent)
std::error_condition do_open_disk(
emu_options const &options,
std::initializer_list<std::reference_wrapper<std::vector<std::string> const> > searchpath,
rom_entry const *romp,
chd_file &chd,
std::function<rom_entry const * ()> next_parent,
chd_file::open_parent_func const &open_parent)
{
// hashes are fixed, but we might need to try multiple filenames
std::set<std::string> 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<std::reference_wrapper<std::vector<std::string> const> > searchpath,
std::function<rom_entry const * ()> const &next_parent)
{
return
[&options, searchpath, next_parent] (util::sha1_t const &sha1) -> std::unique_ptr<chd_file>
{
util::hash_collection hashes;
hashes.add_sha1(sha1);
std::function<rom_entry const * ()> 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_file> 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<emu_file> rom_load_manager::open_rom_file(std::initializer_list<std::reference_wrapper<const std::vector<std::string> > > searchpath, const rom_entry *romp, std::vector<std::string> &tried_file_names, bool from_list)
std::unique_ptr<emu_file> rom_load_manager::open_rom_file(
search_paths searchpath,
const rom_entry *romp,
std::vector<std::string> &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<emu_file> rom_load_manager::open_rom_file(std::initializer_list<
}
std::unique_ptr<emu_file> rom_load_manager::open_rom_file(const std::vector<std::string> &paths, std::vector<std::string> &tried, bool has_crc, u32 crc, std::string_view name, std::error_condition &filerr)
std::unique_ptr<emu_file> rom_load_manager::open_rom_file(
const std::vector<std::string> &paths,
std::vector<std::string> &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 &region,
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 &region, 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 &region, 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<std::reference_wrapper<const std::vector<std::string> > > 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 &region,
const rom_entry *parent_region,
const rom_entry *romp,
bool from_list)
{
u32 lastflags = 0;
std::vector<std::string> tried_file_names;
@ -921,13 +1012,13 @@ void rom_load_manager::process_rom_entries(std::initializer_list<std::reference_
if (ROMENTRY_ISFILL(romp))
{
if (!ROM_GETBIOSFLAGS(romp) || (ROM_GETBIOSFLAGS(romp) == bios))
fill_rom_data(romp);
fill_rom_data(region, romp);
romp++;
}
else if (ROMENTRY_ISCOPY(romp))
{
copy_rom_data(romp++);
copy_rom_data(region, romp++);
}
else if (ROMENTRY_ISFILE(romp))
{
@ -965,7 +1056,7 @@ void rom_load_manager::process_rom_entries(std::initializer_list<std::reference_
// attempt to read using the modified entry
if (!ROMENTRY_ISIGNORE(&modified_romp) && !irrelevantbios)
/*readresult = */read_rom_data(file.get(), parent_region, &modified_romp);
/*readresult = */read_rom_data(file.get(), region, parent_region, &modified_romp);
}
while (ROMENTRY_ISCONTINUE(romp) || ROMENTRY_ISIGNORE(romp));
@ -1004,7 +1095,11 @@ void rom_load_manager::process_rom_entries(std::initializer_list<std::reference_
open_disk_diff - open a DISK diff file
-------------------------------------------------*/
std::error_condition rom_load_manager::open_disk_diff(emu_options &options, const rom_entry *romp, chd_file &source, chd_file &diff_chd)
std::error_condition rom_load_manager::open_disk_diff(
emu_options &options,
const rom_entry *romp,
chd_file &source,
chd_file &diff_chd)
{
// TODO: use system name and/or software list name in the path - the current setup doesn't scale
std::string fname = romp->name() + ".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<std::reference_wrapper<const std::vector<std::string> > > searchpath, std::string_view regiontag, const rom_entry *romp, std::function<const rom_entry * ()> next_parent)
void rom_load_manager::process_disk_entries(
search_paths searchpath,
std::string_view regiontag,
const rom_entry *romp,
std::function<const rom_entry * ()> 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<open_chd> &chd) { return chd->region() == regiontag; }), m_chd_list.end());
m_chd_list.erase(
std::remove_if(
m_chd_list.begin(),
m_chd_list.end(),
[&regiontag] (std::unique_ptr<open_chd> &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_list<std::reference
/* first open the source drive */
// FIXME: we've lost the ability to search parents here
LOG("Opening disk image: %s\n", filename.c_str());
err = do_open_disk(machine().options(), searchpath, romp, chd->orig_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<std::string> tried;
@ -1142,7 +1248,11 @@ std::vector<std::string> 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<std::string> 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<software_info const *> parents;
std::vector<std::string> searchpath = make_software_searchpath(swlist, swinfo, parents);
std::vector<std::string> 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<const rom_entry * ()> 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<const rom_entry * ()> 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<const rom_entry * ()> 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()
{

View File

@ -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<reference>(*m_data); }
pointer operator->() const noexcept { return reinterpret_cast<pointer>(m_data); }
@ -387,6 +387,7 @@ class rom_load_manager
{
public:
open_chd(std::string_view region) : m_region(region) { }
open_chd(std::string &&region) : 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<std::string> 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<std::reference_wrapper<const std::vector<std::string> > >;
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<emu_file> open_rom_file(std::initializer_list<std::reference_wrapper<const std::vector<std::string> > > searchpath, const rom_entry *romp, std::vector<std::string> &tried_file_names, bool from_list);
std::unique_ptr<emu_file> open_rom_file(const std::vector<std::string> &paths, std::vector<std::string> &tried, bool has_crc, u32 crc, std::string_view name, std::error_condition &filerr);
std::unique_ptr<emu_file> open_rom_file(
search_paths searchpath,
const rom_entry *romp,
std::vector<std::string> &tried_file_names,
bool from_list);
std::unique_ptr<emu_file> open_rom_file(
const std::vector<std::string> &paths,
std::vector<std::string> &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<std::reference_wrapper<const std::vector<std::string> > > 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<std::reference_wrapper<const std::vector<std::string> > > searchpath, std::string_view regiontag, const rom_entry *romp, std::function<const rom_entry * ()> next_parent);
int read_rom_data(emu_file *file, memory_region &region, const rom_entry *parent_region, const rom_entry *romp);
void fill_rom_data(memory_region &region, const rom_entry *romp);
void copy_rom_data(memory_region &region, const rom_entry *romp);
void process_rom_entries(
search_paths searchpath,
u8 bios,
memory_region &region,
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<const rom_entry * ()> 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<std::unique_ptr<open_chd>> 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
};

View File

@ -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<T>(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)
{

View File

@ -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<chd_file>(std::shared_ptr<chd_file>(), &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<chd_file>(std::shared_ptr<chd_file>(), 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)

View File

@ -19,6 +19,8 @@
#include "osdcore.h"
#include <atomic>
#include <functional>
#include <memory>
#include <string>
#include <string_view>
#include <system_error>
@ -292,22 +294,25 @@ public:
COMPRESSING
};
using open_parent_func = std::function<std::unique_ptr<chd_file> (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<chd_file> m_parent; // pointer to parent file, or nullptr if none
bool m_parent_missing; // are we missing our parent?
// key offsets within the header