summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/emuopts.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/emuopts.cpp')
-rw-r--r--src/emu/emuopts.cpp893
1 files changed, 824 insertions, 69 deletions
diff --git a/src/emu/emuopts.cpp b/src/emu/emuopts.cpp
index 2863ef35caf..823fb2e071f 100644
--- a/src/emu/emuopts.cpp
+++ b/src/emu/emuopts.cpp
@@ -10,6 +10,11 @@
#include "emu.h"
#include "emuopts.h"
+#include "drivenum.h"
+#include "softlist_dev.h"
+#include "hashfile.h"
+
+#include <stack>
//**************************************************************************
@@ -213,6 +218,167 @@ const options_entry emu_options::s_option_entries[] =
//**************************************************************************
+// CUSTOM OPTION ENTRIES AND SUPPORT CLASSES
+//**************************************************************************
+
+namespace
+{
+ // custom option entry for the system name
+ class system_name_option_entry : public core_options::entry
+ {
+ public:
+ system_name_option_entry(emu_options &host)
+ : entry(OPTION_SYSTEMNAME)
+ , m_host(host)
+ {
+ }
+
+ virtual const char *value() const override
+ {
+ return m_host.system() ? m_host.system()->name : "";
+ }
+
+ protected:
+ virtual void internal_set_value(std::string &&newvalue) override
+ {
+ m_host.set_system_name(newvalue);
+ }
+
+ private:
+ emu_options &m_host;
+ };
+
+ // custom option entry for the software name
+ class software_name_option_entry : public core_options::entry
+ {
+ public:
+ software_name_option_entry(emu_options &host)
+ : entry(OPTION_SOFTWARENAME)
+ , m_host(host)
+ {
+ }
+
+ protected:
+ virtual void internal_set_value(std::string &&newvalue) override
+ {
+ m_host.set_software(newvalue);
+ }
+
+ private:
+ emu_options &m_host;
+ };
+
+ // custom option entry for slots
+ class slot_option_entry : public core_options::entry
+ {
+ public:
+ slot_option_entry(const char *name, slot_option &host)
+ : entry(name)
+ , m_host(host)
+ {
+ }
+
+ virtual const char *value() const override
+ {
+ const char *result = nullptr;
+ if (m_host.specified())
+ {
+ m_temp = m_host.specified_value();
+ result = m_temp.c_str();
+ }
+ return result;
+ }
+
+ protected:
+ virtual void internal_set_value(std::string &&newvalue) override
+ {
+ m_host.specify(std::move(newvalue));
+ }
+
+ private:
+ slot_option & m_host;
+ mutable std::string m_temp;
+ };
+
+ // custom option entry for images
+ class image_option_entry : public core_options::entry
+ {
+ public:
+ image_option_entry(std::vector<std::string> &&names, image_option &host)
+ : entry(std::move(names))
+ , m_host(host)
+ {
+ }
+
+ virtual const char *value() const override
+ {
+ return m_host.value().c_str();
+ }
+
+ protected:
+ virtual void internal_set_value(std::string &&newvalue) override
+ {
+ m_host.specify(std::move(newvalue));
+ }
+
+ private:
+ image_option &m_host;
+ };
+
+ // existing option tracker class; used by slot/image calculus to identify existing
+ // options for later purging
+ template<typename T>
+ class existing_option_tracker
+ {
+ public:
+ existing_option_tracker(const std::unordered_map<std::string, T> &map)
+ {
+ m_vec.reserve(map.size());
+ for (const auto &entry : map)
+ m_vec.push_back(&entry.first);
+ }
+
+ template<typename TStr>
+ void remove(TStr str)
+ {
+ auto iter = std::find_if(
+ m_vec.begin(),
+ m_vec.end(),
+ [&str](const auto &x) { return *x == str; });
+ if (iter != m_vec.end())
+ m_vec.erase(iter);
+ }
+
+ std::vector<const std::string *>::iterator begin() { return m_vec.begin(); }
+ std::vector<const std::string *>::iterator end() { return m_vec.end(); }
+
+ private:
+ std::vector<const std::string *> m_vec;
+ };
+
+
+ //-------------------------------------------------
+ // get_full_option_names
+ //-------------------------------------------------
+
+ std::vector<std::string> get_full_option_names(const device_image_interface &image)
+ {
+ std::vector<std::string> result;
+
+ result.push_back(image.instance_name());
+ result.push_back(image.brief_instance_name());
+
+ if (strcmp(image.device_typename(image.image_type()), image.instance_name().c_str()) == 0)
+ {
+ result.push_back(image.instance_name() + "1");
+ result.push_back(image.brief_instance_name() + "1");
+ }
+ return result;
+ }
+}
+
+
+//**************************************************************************
// EMU OPTIONS
//**************************************************************************
@@ -220,125 +386,611 @@ const options_entry emu_options::s_option_entries[] =
// emu_options - constructor
//-------------------------------------------------
-emu_options::emu_options()
-: core_options()
-, m_coin_impulse(0)
-, m_joystick_contradictory(false)
-, m_sleep(true)
-, m_refresh_speed(false)
-, m_ui(UI_CABINET)
+emu_options::emu_options(bool general_only)
+ : m_system(nullptr)
+ , m_coin_impulse(0)
+ , m_joystick_contradictory(false)
+ , m_sleep(true)
+ , m_refresh_speed(false)
+ , m_ui(UI_CABINET)
{
+ // add entries
+ if (!general_only)
+ {
+ add_entry(std::make_shared<system_name_option_entry>(*this));
+ add_entry(std::make_shared<software_name_option_entry>(*this));
+ }
add_entries(emu_options::s_option_entries);
+
+ // adding handlers to keep copies of frequently requested options in member variables
+ set_value_changed_handler(OPTION_COIN_IMPULSE, [this](const char *value) { m_coin_impulse = int_value(OPTION_COIN_IMPULSE); });
+ set_value_changed_handler(OPTION_JOYSTICK_CONTRADICTORY, [this](const char *value) { m_joystick_contradictory = bool_value(OPTION_JOYSTICK_CONTRADICTORY); });
+ set_value_changed_handler(OPTION_SLEEP, [this](const char *value) { m_sleep = bool_value(OPTION_SLEEP); });
+ set_value_changed_handler(OPTION_REFRESHSPEED, [this](const char *value) { m_sleep = bool_value(OPTION_REFRESHSPEED); });
+ set_value_changed_handler(OPTION_UI, [this](const std::string &value)
+ {
+ if (value == "simple")
+ m_ui = UI_SIMPLE;
+ else
+ m_ui = UI_CABINET;
+ });
+}
+
+
+//-------------------------------------------------
+// emu_options - destructor
+//-------------------------------------------------
+
+emu_options::~emu_options()
+{
}
//-------------------------------------------------
-// value_changed - to prevent tagmap
-// lookups keep copies of frequently requested
-// options in member variables.
+// system_name
//-------------------------------------------------
-void emu_options::value_changed(const std::string &name, const std::string &value)
+const char *emu_options::system_name() const
{
- if (name == OPTION_COIN_IMPULSE)
+ return m_system ? m_system->name : "";
+}
+
+
+//-------------------------------------------------
+// set_system_name - called to set the system
+// name; will adjust slot/image options as appropriate
+//-------------------------------------------------
+
+void emu_options::set_system_name(const std::string &new_system_name)
+{
+ const game_driver *new_system = nullptr;
+
+ // was a system name specified?
+ if (!new_system_name.empty())
{
- m_coin_impulse = int_value(OPTION_COIN_IMPULSE);
+ // if so, first extract the base name (the reason for this is drag-and-drop on Windows; a side
+ // effect is a command line like 'mame pacman.foo' will work correctly, but so be it)
+ std::string new_system_base_name = core_filename_extract_base(new_system_name, true);
+
+ // perform the lookup (and error if it cannot be found)
+ int index = driver_list::find(new_system_base_name.c_str());
+ if (index < 0)
+ throw options_error_exception("Unknown system '%s'", new_system_name);
+ new_system = &driver_list::driver(index);
}
- else if (name == OPTION_JOYSTICK_CONTRADICTORY)
+
+ // did we change anything?
+ if (new_system != m_system)
{
- m_joystick_contradictory = bool_value(OPTION_JOYSTICK_CONTRADICTORY);
+ // if so, specify the new system and update
+ m_system = new_system;
+ update_slot_and_image_options();
}
- else if (name == OPTION_SLEEP)
+}
+
+
+//-------------------------------------------------
+// update_slot_and_image_options
+//-------------------------------------------------
+
+void emu_options::update_slot_and_image_options()
+{
+ bool changed;
+ do
{
- m_sleep = bool_value(OPTION_SLEEP);
- }
- else if (name == OPTION_REFRESHSPEED)
+ changed = false;
+
+ // first we add and remove slot options depending on what has been configured in the
+ // device, bringing m_slot_options up to a state where it matches machine_config
+ if (add_and_remove_slot_options())
+ changed = true;
+
+ // second, we perform an analgous operation with m_image_options
+ if (add_and_remove_image_options())
+ changed = true;
+
+ // if we changed anything, we should reevaluate existing options
+ if (changed)
+ reevaluate_default_card_software();
+ } while (changed);
+}
+
+
+//-------------------------------------------------
+// add_and_remove_slot_options - add any missing
+// and/or purge extraneous slot options
+//-------------------------------------------------
+
+bool emu_options::add_and_remove_slot_options()
+{
+ bool changed = false;
+
+ // first, create a list of existing slot options; this is so we can purge
+ // any stray slot options that are no longer pertinent when we're done
+ existing_option_tracker<::slot_option> existing(m_slot_options);
+
+ // it is perfectly legal for this to be called without a system; we
+ // need to check for that condition!
+ if (m_system)
{
- m_refresh_speed = bool_value(OPTION_REFRESHSPEED);
+ // create the configuration
+ machine_config config(*m_system, *this);
+
+ for (const device_slot_interface &slot : slot_interface_iterator(config.root_device()))
+ {
+ // come up with the cannonical name of the slot
+ const char *slot_option_name = slot.slot_name();
+
+ // erase this option from existing (so we don't purge it later)
+ existing.remove(slot_option_name);
+
+ // do we need to add this option?
+ if (!has_slot_option(slot_option_name))
+ {
+ // we do - add it to m_slot_options
+ auto pair = std::make_pair(slot_option_name, ::slot_option(*this, slot.default_option()));
+ ::slot_option &new_option(m_slot_options.emplace(std::move(pair)).first->second);
+ changed = true;
+
+ // for non-fixed slots, this slot needs representation in the options collection
+ if (!slot.fixed())
+ {
+ // first device? add the header as to be pretty
+ const char *header = "SLOT DEVICES";
+ if (!header_exists(header))
+ add_header(header);
+
+ // create a new entry in the options
+ auto new_entry = new_option.setup_option_entry(slot_option_name);
+
+ // and add it
+ add_entry(std::move(new_entry), header);
+ }
+ }
+
+ }
}
- else if (name == OPTION_UI)
+
+ // at this point we need to purge stray slot options that may no longer be pertinent
+ for (auto &opt_name : existing)
{
- if (value == "simple")
- m_ui = UI_SIMPLE;
- else
- m_ui = UI_CABINET;
+ auto iter = m_slot_options.find(*opt_name);
+ assert(iter != m_slot_options.end());
+
+ // if this is represented in core_options, remove it
+ if (iter->second.option_entry())
+ remove_entry(*iter->second.option_entry());
+
+ // remove this option
+ m_slot_options.erase(iter);
+ changed = true;
}
+
+ return changed;
}
//-------------------------------------------------
-// override_get_value - when saving to an INI, we
-// need to hook into that process so we can write
-// out image/slot options
+// add_and_remove_slot_options - add any missing
+// and/or purge extraneous slot options
//-------------------------------------------------
-core_options::override_get_value_result emu_options::override_get_value(const char *name, std::string &value) const
+bool emu_options::add_and_remove_image_options()
{
- if (name)
+ // The logic for image options is superficially similar to the logic for slot options, but
+ // there is one larger piece of complexity. The image instance names (returned by the
+ // image_instance() call and surfaced in the UI) may change simply because we've added more
+ // devices. This is because the instance_name() for a singular cartridge device might be
+ // "cartridge" starting out, but become "cartridge1" when another cartridge device is added.
+ //
+ // To get around this behavior, our internal data structures work in terms of what is
+ // returned by cannonical_instance_name(), which will be something like "cartridge1" both
+ // for a singular cartridge device and the first cartridge in a multi cartridge system.
+ //
+ // The need for this behavior was identified by Tafoid when the following command line
+ // regressed:
+ //
+ // mame snes bsxsore -cart2 bszelda
+ //
+ // Before we were accounting for this behavior, 'bsxsore' got stored in "cartridge" and
+ // the association got lost when the second cartridge was added.
+
+ bool changed = false;
+
+ // first, create a list of existing image options; this is so we can purge
+ // any stray slot options that are no longer pertinent when we're done; we
+ // have to do this for both "flavors" of name
+ existing_option_tracker<::image_option> existing(m_image_options_cannonical);
+
+ // wipe the non-cannonical image options; we're going to rebuild it
+ m_image_options.clear();
+
+ // it is perfectly legal for this to be called without a system; we
+ // need to check for that condition!
+ if (m_system)
{
- auto slotiter = m_slot_options.find(name);
- if (slotiter != m_slot_options.end())
+ // create the configuration
+ machine_config config(*m_system, *this);
+
+ // iterate through all image devices
+ for (device_image_interface &image : image_interface_iterator(config.root_device()))
{
- value = slotiter->second.specified_value();
- return slotiter->second.specified()
- ? override_get_value_result::OVERRIDE
- : override_get_value_result::SKIP;
+ const std::string &cannonical_name(image.cannonical_instance_name());
+
+ // erase this option from existing (so we don't purge it later)
+ existing.remove(cannonical_name);
+
+ // do we need to add this option?
+ auto iter = m_image_options_cannonical.find(cannonical_name);
+ ::image_option *this_option = iter != m_image_options_cannonical.end() ? &iter->second : nullptr;
+ if (!this_option)
+ {
+ // we do - add it to both m_image_options_cannonical and m_image_options
+ auto pair = std::make_pair(cannonical_name, ::image_option(*this, image.cannonical_instance_name()));
+ this_option = &m_image_options_cannonical.emplace(std::move(pair)).first->second;
+ changed = true;
+
+ // if this image is user loadable, we have to surface it in the core_options
+ if (image.user_loadable())
+ {
+ // first device? add the header as to be pretty
+ const char *header = "IMAGE DEVICES";
+ if (!header_exists(header))
+ add_header(header);
+
+ // name this options
+ auto names = get_full_option_names(image);
+
+ // create a new entry in the options
+ auto new_entry = this_option->setup_option_entry(std::move(names));
+
+ // and add it
+ add_entry(std::move(new_entry), header);
+ }
+ }
+
+ // whether we added it or we didn't, we have to add it to the m_image_option map
+ m_image_options[image.instance_name()] = this_option;
}
+ }
+
+ // at this point we need to purge stray image options that may no longer be pertinent
+ for (auto &opt_name : existing)
+ {
+ auto iter = m_image_options_cannonical.find(*opt_name);
+ assert(iter != m_image_options_cannonical.end());
+
+ // if this is represented in core_options, remove it
+ if (iter->second.option_entry())
+ remove_entry(*iter->second.option_entry());
+
+ // remove this option
+ m_image_options_cannonical.erase(iter);
+ changed = true;
+ }
+
+ return changed;
+}
+
+
+//-------------------------------------------------
+// reevaluate_default_card_software - based on recent
+// changes in what images are mounted, give drivers
+// a chance to specify new default slot options
+//-------------------------------------------------
- auto imageiter = m_image_options.find(name);
- if (imageiter != m_image_options.end())
+void emu_options::reevaluate_default_card_software()
+{
+ if (m_system)
+ {
+ bool found;
+ do
{
- value = imageiter->second;
- return override_get_value_result::OVERRIDE;
- }
+ // set up the machine_config
+ machine_config config(*m_system, *this);
+ found = false;
+
+ // iterate through all slot devices
+ for (device_slot_interface &slot : slot_interface_iterator(config.root_device()))
+ {
+ // retrieve info about the device instance
+ auto &slot_opt(slot_option(slot.slot_name()));
+
+ // device_slot_interface::get_default_card_software() is essentially a hook
+ // that lets devices provide a feedback loop to force a specified software
+ // list entry to be loaded
+ //
+ // In the repeated cycle of adding slots and slot devices, this gives a chance
+ // for devices to "plug in" default software list items. Of course, the fact
+ // that this is all shuffling options is brittle and roundabout, but such is
+ // the nature of software lists.
+ //
+ // In reality, having some sort of hook into the pipeline of slot/device evaluation
+ // makes sense, but the fact that it is joined at the hip to device_image_interface
+ // and device_slot_interface is unfortunate
+ std::string default_card_software = get_default_card_software(slot);
+ if (slot_opt.default_card_software() != default_card_software)
+ {
+ slot_opt.set_default_card_software(std::move(default_card_software));
+
+ // calling set_default_card_software() can cause a cascade of slot/image
+ // evaluations; we need to bail out of this loop because the iterator
+ // may be bad
+ found = true;
+ break;
+ }
+ }
+ } while (found);
}
+}
- return override_get_value_result::NONE;
+
+//-------------------------------------------------
+// get_default_card_software
+//-------------------------------------------------
+
+std::string emu_options::get_default_card_software(device_slot_interface &slot)
+{
+ std::string image_path;
+ std::function<bool(util::core_file &, std::string&)> get_hashfile_extrainfo;
+
+ // figure out if an image option has been specified, and if so, get the image path out of the options
+ device_image_interface *image = dynamic_cast<device_image_interface *>(&slot);
+ if (image)
+ {
+ image_path = image_option(image->instance_name()).value();
+
+ get_hashfile_extrainfo = [image, this](util::core_file &file, std::string &extrainfo)
+ {
+ util::hash_collection hashes = image->calculate_hash_on_file(file);
+
+ return hashfile_extrainfo(
+ hash_path(),
+ image->device().mconfig().gamedrv(),
+ hashes,
+ extrainfo);
+ };
+ }
+
+ // create the hook
+ get_default_card_software_hook hook(image_path, std::move(get_hashfile_extrainfo));
+
+ // and invoke the slot's implementation of get_default_card_software()
+ return slot.get_default_card_software(hook);
}
//-------------------------------------------------
-// override_set_value - when parsing an INI, we
-// need to hook into into it so we can do the same
-// crazy slot logic done in mameopt
+// set_software - called to load "unqualified"
+// software out of a software list (e.g. - "mame nes 'zelda'")
//-------------------------------------------------
-bool emu_options::override_set_value(const char *name, const std::string &value)
+void emu_options::set_software(const std::string &new_software)
{
- auto slotiter = m_slot_options.find(name);
- if (slotiter != m_slot_options.end())
+ // identify any options as a result of softlists
+ software_options softlist_opts = evaluate_initial_softlist_options(new_software);
+
+ while (!softlist_opts.slot.empty() || !softlist_opts.image.empty())
{
- slotiter->second.specify(std::string(value));
- return true;
+ // track how many options we have
+ size_t before_size = softlist_opts.slot.size() + softlist_opts.image.size();
+
+ // keep a list of deferred options, in case anything is applied
+ // out of order
+ software_options deferred_opts;
+
+ // distribute slot options
+ for (auto &slot_opt : softlist_opts.slot)
+ {
+ auto iter = m_slot_options.find(slot_opt.first);
+ if (iter != m_slot_options.end())
+ iter->second.specify(std::move(slot_opt.second));
+ else
+ deferred_opts.slot[slot_opt.first] = std::move(slot_opt.second);
+ }
+
+ // distribute image options
+ for (auto &image_opt : softlist_opts.image)
+ {
+ auto iter = m_image_options.find(image_opt.first);
+ if (iter != m_image_options.end())
+ iter->second->specify(std::move(image_opt.second));
+ else
+ deferred_opts.image[image_opt.first] = std::move(image_opt.second);
+ }
+
+ // keep any deferred options for the next round
+ softlist_opts = std::move(deferred_opts);
+
+ // do we have any pending options after failing to distribute any?
+ size_t after_size = softlist_opts.slot.size() + softlist_opts.image.size();
+ if ((after_size > 0) && after_size >= before_size)
+ throw options_error_exception("Could not assign software option");
}
+}
+
+
+//-------------------------------------------------
+// evaluate_initial_softlist_options
+//-------------------------------------------------
+
+emu_options::software_options emu_options::evaluate_initial_softlist_options(const std::string &software_identifier)
+{
+ software_options results;
- auto imageiter = m_image_options.find(name);
- if (imageiter != m_image_options.end())
+ // load software specified at the command line (if any of course)
+ if (!software_identifier.empty())
{
- // We've found a potential image slot for this value. However, we're only going to specify it
- // if the current image option is empty. This is because if there is an image option already
- // present, it is almost certain that this was because something was specified at the command
- // line and we're parsing an INI. Because INIs have less priority than the command line, this
- // should be ignored
- //
- // Obviously, this ignores that INIs themselves have their own prioritization, so this should be
- // considered to be a hack. Instead of having image options being just a straight map of std::string
- // it should really be a structure where the priority can be recorded
- if (imageiter->second.empty())
- imageiter->second = value;
- return true;
+ // we have software; first identify the proper game_driver
+ if (!m_system)
+ throw options_error_exception("Cannot specify software without specifying system");
+
+ // and set up a configuration
+ machine_config config(*m_system, *this);
+ software_list_device_iterator iter(config.root_device());
+ if (iter.count() == 0)
+ throw emu_fatalerror(EMU_ERR_FATALERROR, "Error: unknown option: %s\n", software_identifier.c_str());
+
+ // and finally set up the stack
+ std::stack<std::string> software_identifier_stack;
+ software_identifier_stack.push(software_identifier);
+
+ // we need to keep evaluating softlist identifiers until the stack is empty
+ while (!software_identifier_stack.empty())
+ {
+ // pop the identifier
+ std::string current_software_identifier = std::move(software_identifier_stack.top());
+ software_identifier_stack.pop();
+
+ // and parse it
+ std::string list_name, software_name;
+ auto colon_pos = current_software_identifier.find_first_of(':');
+ if (colon_pos != std::string::npos)
+ {
+ list_name = current_software_identifier.substr(0, colon_pos);
+ software_name = current_software_identifier.substr(colon_pos + 1);
+ }
+ else
+ {
+ software_name = current_software_identifier;
+ }
+
+ // loop through all softlist devices, and try to find one capable of handling the requested software
+ bool found = false;
+ bool compatible = false;
+ for (software_list_device &swlistdev : iter)
+ {
+ if (list_name.empty() || (list_name == swlistdev.list_name()))
+ {
+ const software_info *swinfo = swlistdev.find(software_name);
+ if (swinfo != nullptr)
+ {
+ // loop through all parts
+ for (const software_part &swpart : swinfo->parts())
+ {
+ // only load compatible software this way
+ if (swlistdev.is_compatible(swpart) == SOFTWARE_IS_COMPATIBLE)
+ {
+ // we need to find a mountable image slot, but we need to ensure it is a slot
+ // for which we have not already distributed a part to
+ device_image_interface *image = software_list_device::find_mountable_image(
+ config,
+ swpart,
+ [&results](const device_image_interface &candidate) { return results.image.count(candidate.instance_name()) == 0; });
+
+ // did we find a slot to put this part into?
+ if (image != nullptr)
+ {
+ // we've resolved this software
+ results.image[image->instance_name()] = string_format("%s:%s:%s", swlistdev.list_name(), software_name, swpart.name());
+
+ // does this software part have a requirement on another part?
+ const char *requirement = swpart.feature("requirement");
+ if (requirement)
+ software_identifier_stack.push(requirement);
+ }
+ compatible = true;
+ }
+ found = true;
+ }
+
+ // identify other shared features specified as '<<slot name>>_default'
+ //
+ // example from SMS:
+ //
+ // <software name = "alexbmx">
+ // ...
+ // <sharedfeat name = "ctrl1_default" value = "paddle" />
+ // </software>
+ for (const feature_list_item &fi : swinfo->shared_info())
+ {
+ const std::string default_suffix = "_default";
+ if (fi.name().size() > default_suffix.size()
+ && fi.name().compare(fi.name().size() - default_suffix.size(), default_suffix.size(), default_suffix) == 0)
+ {
+ std::string slot_name = fi.name().substr(0, fi.name().size() - default_suffix.size());
+ results.slot[slot_name] = fi.value();
+ }
+ }
+ }
+ }
+ if (compatible)
+ break;
+ }
+
+ if (!compatible)
+ {
+ software_list_device::display_matches(config, nullptr, software_name);
+ if (!found)
+ throw options_error_exception("");
+ else
+ throw options_error_exception("Software '%s' is incompatible with system '%s'\n", software_name, m_system->name);
+ }
+ }
}
+ return results;
+}
+
+
+//-------------------------------------------------
+// slot_option
+//-------------------------------------------------
+
+const slot_option &emu_options::slot_option(const std::string &device_name) const
+{
+ auto iter = m_slot_options.find(device_name);
+ assert(iter != m_slot_options.end() && "Attempt to access non-existent slot option");
+ return iter->second;
+}
+
+slot_option &emu_options::slot_option(const std::string &device_name)
+{
+ auto iter = m_slot_options.find(device_name);
+ assert(iter != m_slot_options.end() && "Attempt to access non-existent slot option");
+ return iter->second;
+}
+
+
+//-------------------------------------------------
+// has_slot_option
+//-------------------------------------------------
- return false;
+bool emu_options::has_slot_option(const std::string &device_name) const
+{
+ return m_slot_options.find(device_name) != m_slot_options.end();
}
//-------------------------------------------------
+// image_option
+//-------------------------------------------------
+
+const image_option &emu_options::image_option(const std::string &device_name) const
+{
+ auto iter = m_image_options.find(device_name);
+ assert(iter != m_image_options.end() && "Attempt to access non-existent image option");
+ return *iter->second;
+}
+
+image_option &emu_options::image_option(const std::string &device_name)
+{
+ auto iter = m_image_options.find(device_name);
+ assert(iter != m_image_options.end() && "Attempt to access non-existent image option");
+ return *iter->second;
+}
+
+
+//**************************************************************************
+// SLOT OPTIONS
+//**************************************************************************
+
+//-------------------------------------------------
// slot_option ctor
//-------------------------------------------------
-slot_option::slot_option(const char *default_value)
- : m_specified(false)
+slot_option::slot_option(emu_options &host, const char *default_value)
+ : m_host(host)
+ , m_specified(false)
, m_default_value(default_value ? default_value : "")
{
}
@@ -396,10 +1048,12 @@ std::string slot_option::specified_value() const
void slot_option::specify(std::string &&text)
{
+ // record the old value; we may need to trigger an update
+ const std::string old_value = value();
+
// we need to do some elementary parsing here
const char *bios_arg = ",bios=";
-
- size_t pos = text.find(bios_arg);
+ const size_t pos = text.find(bios_arg);
if (pos != std::string::npos)
{
m_specified = true;
@@ -412,6 +1066,37 @@ void slot_option::specify(std::string &&text)
m_specified_value = std::move(text);
m_specified_bios = "";
}
+
+ // we may have changed
+ possibly_changed(old_value);
+}
+
+
+//-------------------------------------------------
+// slot_option::set_default_card_software
+//-------------------------------------------------
+
+void slot_option::set_default_card_software(std::string &&s)
+{
+ // record the old value; we may need to trigger an update
+ const std::string old_value = value();
+
+ // update the default card software
+ m_default_card_software = std::move(s);
+
+ // we may have changed
+ possibly_changed(old_value);
+}
+
+
+//-------------------------------------------------
+// slot_option::possibly_changed
+//-------------------------------------------------
+
+void slot_option::possibly_changed(const std::string &old_value)
+{
+ if (value() != old_value)
+ m_host.update_slot_and_image_options();
}
@@ -428,3 +1113,73 @@ void slot_option::set_bios(std::string &&text)
}
m_specified_bios = std::move(text);
}
+
+
+//-------------------------------------------------
+// slot_option::setup_option_entry
+//-------------------------------------------------
+
+core_options::entry::shared_ptr slot_option::setup_option_entry(const char *name)
+{
+ // this should only be called once
+ assert(m_entry.expired());
+
+ // create the entry and return it
+ core_options::entry::shared_ptr entry = std::make_shared<slot_option_entry>(name, *this);
+ m_entry = entry;
+ return entry;
+}
+
+
+//**************************************************************************
+// IMAGE OPTIONS
+//**************************************************************************
+
+//-------------------------------------------------
+// image_option ctor
+//-------------------------------------------------
+
+image_option::image_option(emu_options &host, const std::string &cannonical_instance_name)
+ : m_host(host)
+ , m_cannonical_instance_name(cannonical_instance_name)
+{
+}
+
+
+//-------------------------------------------------
+// image_option::specify
+//-------------------------------------------------
+
+void image_option::specify(const std::string &value)
+{
+ if (value != m_value)
+ {
+ m_value = value;
+ m_host.reevaluate_default_card_software();
+ }
+}
+
+void image_option::specify(std::string &&value)
+{
+ if (value != m_value)
+ {
+ m_value = std::move(value);
+ m_host.reevaluate_default_card_software();
+ }
+}
+
+
+//-------------------------------------------------
+// image_option::setup_option_entry
+//-------------------------------------------------
+
+core_options::entry::shared_ptr image_option::setup_option_entry(std::vector<std::string> &&names)
+{
+ // this should only be called once
+ assert(m_entry.expired());
+
+ // create the entry and return it
+ core_options::entry::shared_ptr entry = std::make_shared<image_option_entry>(std::move(names), *this);
+ m_entry = entry;
+ return entry;
+}