1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles
3 /***************************************************************************
4
5 emuopts.cpp
6
7 Options file and command line management.
8
9 ***************************************************************************/
10
11 #include "emu.h"
12 #include "emuopts.h"
13 #include "drivenum.h"
14 #include "softlist_dev.h"
15 #include "hashfile.h"
16
17 #include <stack>
18
19
20 //**************************************************************************
21 // CORE EMULATOR OPTIONS
22 //**************************************************************************
23
24 const options_entry emu_options::s_option_entries[] =
25 {
26 // unadorned options - only a single one supported at the moment
27 { OPTION_SYSTEMNAME, nullptr, OPTION_STRING, nullptr },
28 { OPTION_SOFTWARENAME, nullptr, OPTION_STRING, nullptr },
29
30 // config options
31 { nullptr, nullptr, OPTION_HEADER, "CORE CONFIGURATION OPTIONS" },
32 { OPTION_READCONFIG ";rc", "1", OPTION_BOOLEAN, "enable loading of configuration files" },
33 { OPTION_WRITECONFIG ";wc", "0", OPTION_BOOLEAN, "write configuration to (driver).ini on exit" },
34
35 // search path options
36 { nullptr, nullptr, OPTION_HEADER, "CORE SEARCH PATH OPTIONS" },
37 { OPTION_HOMEPATH, ".", OPTION_STRING, "path to base folder for plugin data (read/write)" },
38 { OPTION_MEDIAPATH ";rp;biospath;bp", "roms", OPTION_STRING, "path to ROM sets and hard disk images" },
39 { OPTION_HASHPATH ";hash_directory;hash", "hash", OPTION_STRING, "path to software definition files" },
40 { OPTION_SAMPLEPATH ";sp", "samples", OPTION_STRING, "path to audio sample sets" },
41 { OPTION_ARTPATH, "artwork", OPTION_STRING, "path to artwork files" },
42 { OPTION_CTRLRPATH, "ctrlr", OPTION_STRING, "path to controller definitions" },
43 { OPTION_INIPATH, ".;ini;ini/presets", OPTION_STRING, "path to ini files" },
44 { OPTION_FONTPATH, ".", OPTION_STRING, "path to font files" },
45 { OPTION_CHEATPATH, "cheat", OPTION_STRING, "path to cheat files" },
46 { OPTION_CROSSHAIRPATH, "crosshair", OPTION_STRING, "path to crosshair files" },
47 { OPTION_PLUGINSPATH, "plugins", OPTION_STRING, "path to plugin files" },
48 { OPTION_LANGUAGEPATH, "language", OPTION_STRING, "path to UI translation files" },
49 { OPTION_SWPATH, "software", OPTION_STRING, "path to loose software" },
50
51 // output directory options
52 { nullptr, nullptr, OPTION_HEADER, "CORE OUTPUT DIRECTORY OPTIONS" },
53 { OPTION_CFG_DIRECTORY, "cfg", OPTION_STRING, "directory to save configurations" },
54 { OPTION_NVRAM_DIRECTORY, "nvram", OPTION_STRING, "directory to save NVRAM contents" },
55 { OPTION_INPUT_DIRECTORY, "inp", OPTION_STRING, "directory to save input device logs" },
56 { OPTION_STATE_DIRECTORY, "sta", OPTION_STRING, "directory to save states" },
57 { OPTION_SNAPSHOT_DIRECTORY, "snap", OPTION_STRING, "directory to save/load screenshots" },
58 { OPTION_DIFF_DIRECTORY, "diff", OPTION_STRING, "directory to save hard drive image difference files" },
59 { OPTION_COMMENT_DIRECTORY, "comments", OPTION_STRING, "directory to save debugger comments" },
60
61 // state/playback options
62 { nullptr, nullptr, OPTION_HEADER, "CORE STATE/PLAYBACK OPTIONS" },
63 { OPTION_STATE, nullptr, OPTION_STRING, "saved state to load" },
64 { OPTION_AUTOSAVE, "0", OPTION_BOOLEAN, "automatically restore state on start and save on exit for supported systems" },
65 { OPTION_REWIND, "0", OPTION_BOOLEAN, "enable rewind savestates" },
66 { OPTION_REWIND_CAPACITY "(1-2048)", "100", OPTION_INTEGER, "rewind buffer size in megabytes" },
67 { OPTION_PLAYBACK ";pb", nullptr, OPTION_STRING, "playback an input file" },
68 { OPTION_RECORD ";rec", nullptr, OPTION_STRING, "record an input file" },
69 { OPTION_RECORD_TIMECODE, "0", OPTION_BOOLEAN, "record an input timecode file (requires -record option)" },
70 { OPTION_EXIT_AFTER_PLAYBACK, "0", OPTION_BOOLEAN, "close the program at the end of playback" },
71
72 { OPTION_MNGWRITE, nullptr, OPTION_STRING, "optional filename to write a MNG movie of the current session" },
73 { OPTION_AVIWRITE, nullptr, OPTION_STRING, "optional filename to write an AVI movie of the current session" },
74 { OPTION_WAVWRITE, nullptr, OPTION_STRING, "optional filename to write a WAV file of the current session" },
75 { OPTION_SNAPNAME, "%g/%i", OPTION_STRING, "override of the default snapshot/movie naming; %g == gamename, %i == index" },
76 { OPTION_SNAPSIZE, "auto", OPTION_STRING, "specify snapshot/movie resolution (<width>x<height>) or 'auto' to use minimal size " },
77 { OPTION_SNAPVIEW, "internal", OPTION_STRING, "specify snapshot/movie view or 'internal' to use internal pixel-aspect views" },
78 { OPTION_SNAPBILINEAR, "1", OPTION_BOOLEAN, "specify if the snapshot/movie should have bilinear filtering applied" },
79 { OPTION_STATENAME, "%g", OPTION_STRING, "override of the default state subfolder naming; %g == gamename" },
80 { OPTION_BURNIN, "0", OPTION_BOOLEAN, "create burn-in snapshots for each screen" },
81
82 // performance options
83 { nullptr, nullptr, OPTION_HEADER, "CORE PERFORMANCE OPTIONS" },
84 { OPTION_AUTOFRAMESKIP ";afs", "0", OPTION_BOOLEAN, "enable automatic frameskip adjustment to maintain emulation speed" },
85 { OPTION_FRAMESKIP ";fs(0-10)", "0", OPTION_INTEGER, "set frameskip to fixed value, 0-10 (upper limit with autoframeskip)" },
86 { OPTION_SECONDS_TO_RUN ";str", "0", OPTION_INTEGER, "number of emulated seconds to run before automatically exiting" },
87 { OPTION_THROTTLE, "1", OPTION_BOOLEAN, "throttle emulation to keep system running in sync with real time" },
88 { OPTION_SLEEP, "1", OPTION_BOOLEAN, "enable sleeping, which gives time back to other applications when idle" },
89 { OPTION_SPEED "(0.01-100)", "1.0", OPTION_FLOAT, "controls the speed of gameplay, relative to realtime; smaller numbers are slower" },
90 { OPTION_REFRESHSPEED ";rs", "0", OPTION_BOOLEAN, "automatically adjust emulation speed to keep the emulated refresh rate slower than the host screen" },
91 { OPTION_LOWLATENCY ";lolat", "0", OPTION_BOOLEAN, "draws new frame before throttling to reduce input latency" },
92
93 // render options
94 { nullptr, nullptr, OPTION_HEADER, "CORE RENDER OPTIONS" },
95 { OPTION_KEEPASPECT ";ka", "1", OPTION_BOOLEAN, "maintain aspect ratio when scaling to fill output screen/window" },
96 { OPTION_UNEVENSTRETCH ";ues", "1", OPTION_BOOLEAN, "allow non-integer ratios when scaling to fill output screen/window horizontally or vertically" },
97 { OPTION_UNEVENSTRETCHX ";uesx", "0", OPTION_BOOLEAN, "allow non-integer ratios when scaling to fill output screen/window horizontally"},
98 { OPTION_UNEVENSTRETCHY ";uesy", "0", OPTION_BOOLEAN, "allow non-integer ratios when scaling to fill otuput screen/window vertially"},
99 { OPTION_AUTOSTRETCHXY ";asxy", "0", OPTION_BOOLEAN, "automatically apply -unevenstretchx/y based on source native orientation"},
100 { OPTION_INTOVERSCAN ";ios", "0", OPTION_BOOLEAN, "allow overscan on integer scaled targets"},
101 { OPTION_INTSCALEX ";sx", "0", OPTION_INTEGER, "set horizontal integer scale factor"},
102 { OPTION_INTSCALEY ";sy", "0", OPTION_INTEGER, "set vertical integer scale factor"},
103
104 // rotation options
105 { nullptr, nullptr, OPTION_HEADER, "CORE ROTATION OPTIONS" },
106 { OPTION_ROTATE, "1", OPTION_BOOLEAN, "rotate the game screen according to the game's orientation when needed" },
107 { OPTION_ROR, "0", OPTION_BOOLEAN, "rotate screen clockwise 90 degrees" },
108 { OPTION_ROL, "0", OPTION_BOOLEAN, "rotate screen counterclockwise 90 degrees" },
109 { OPTION_AUTOROR, "0", OPTION_BOOLEAN, "automatically rotate screen clockwise 90 degrees if vertical" },
110 { OPTION_AUTOROL, "0", OPTION_BOOLEAN, "automatically rotate screen counterclockwise 90 degrees if vertical" },
111 { OPTION_FLIPX, "0", OPTION_BOOLEAN, "flip screen left-right" },
112 { OPTION_FLIPY, "0", OPTION_BOOLEAN, "flip screen upside-down" },
113
114 // artwork options
115 { nullptr, nullptr, OPTION_HEADER, "CORE ARTWORK OPTIONS" },
116 { OPTION_ARTWORK_CROP ";artcrop", "0", OPTION_BOOLEAN, "crop artwork so emulated screen image fills output screen/window in one axis" },
117 { OPTION_FALLBACK_ARTWORK, nullptr, OPTION_STRING, "fallback artwork if no external artwork or internal driver layout defined" },
118 { OPTION_OVERRIDE_ARTWORK, nullptr, OPTION_STRING, "override artwork for external artwork and internal driver layout" },
119
120 // screen options
121 { nullptr, nullptr, OPTION_HEADER, "CORE SCREEN OPTIONS" },
122 { OPTION_BRIGHTNESS "(0.1-2.0)", "1.0", OPTION_FLOAT, "default game screen brightness correction" },
123 { OPTION_CONTRAST "(0.1-2.0)", "1.0", OPTION_FLOAT, "default game screen contrast correction" },
124 { OPTION_GAMMA "(0.1-3.0)", "1.0", OPTION_FLOAT, "default game screen gamma correction" },
125 { OPTION_PAUSE_BRIGHTNESS "(0.0-1.0)", "0.65", OPTION_FLOAT, "amount to scale the screen brightness when paused" },
126 { OPTION_EFFECT, "none", OPTION_STRING, "name of a PNG file to use for visual effects, or 'none'" },
127
128 // vector options
129 { nullptr, nullptr, OPTION_HEADER, "CORE VECTOR OPTIONS" },
130 { OPTION_BEAM_WIDTH_MIN, "1.0", OPTION_FLOAT, "set vector beam width minimum" },
131 { OPTION_BEAM_WIDTH_MAX, "1.0", OPTION_FLOAT, "set vector beam width maximum" },
132 { OPTION_BEAM_DOT_SIZE, "1.0", OPTION_FLOAT, "set vector beam size for dots" },
133 { OPTION_BEAM_INTENSITY_WEIGHT, "0", OPTION_FLOAT, "set vector beam intensity weight " },
134 { OPTION_FLICKER, "0", OPTION_FLOAT, "set vector flicker effect" },
135
136 // sound options
137 { nullptr, nullptr, OPTION_HEADER, "CORE SOUND OPTIONS" },
138 { OPTION_SAMPLERATE ";sr(1000-1000000)", "48000", OPTION_INTEGER, "set sound output sample rate" },
139 { OPTION_SAMPLES, "1", OPTION_BOOLEAN, "enable the use of external samples if available" },
140 { OPTION_VOLUME ";vol", "0", OPTION_INTEGER, "sound volume in decibels (-32 min, 0 max)" },
141 { OPTION_SPEAKER_REPORT, "0", OPTION_INTEGER, "print report of speaker ouput maxima (0=none, or 1-4 for more detail)" },
142
143 // input options
144 { nullptr, nullptr, OPTION_HEADER, "CORE INPUT OPTIONS" },
145 { OPTION_COIN_LOCKOUT ";coinlock", "1", OPTION_BOOLEAN, "ignore coin inputs if coin lockout output is active" },
146 { OPTION_CTRLR, nullptr, OPTION_STRING, "preconfigure for specified controller" },
147 { OPTION_MOUSE, "0", OPTION_BOOLEAN, "enable mouse input" },
148 { OPTION_JOYSTICK ";joy", "1", OPTION_BOOLEAN, "enable joystick input" },
149 { OPTION_LIGHTGUN ";gun", "0", OPTION_BOOLEAN, "enable lightgun input" },
150 { OPTION_MULTIKEYBOARD ";multikey", "0", OPTION_BOOLEAN, "enable separate input from each keyboard device (if present)" },
151 { OPTION_MULTIMOUSE, "0", OPTION_BOOLEAN, "enable separate input from each mouse device (if present)" },
152 { OPTION_STEADYKEY ";steady", "0", OPTION_BOOLEAN, "enable steadykey support" },
153 { OPTION_UI_ACTIVE, "0", OPTION_BOOLEAN, "enable user interface on top of emulated keyboard (if present)" },
154 { OPTION_OFFSCREEN_RELOAD ";reload", "0", OPTION_BOOLEAN, "convert lightgun button 2 into offscreen reload" },
155 { OPTION_JOYSTICK_MAP ";joymap", "auto", OPTION_STRING, "explicit joystick map, or auto to auto-select" },
156 { OPTION_JOYSTICK_DEADZONE ";joy_deadzone;jdz(0.00-1)", "0.3", OPTION_FLOAT, "center deadzone range for joystick where change is ignored (0.0 center, 1.0 end)" },
157 { OPTION_JOYSTICK_SATURATION ";joy_saturation;jsat(0.00-1)", "0.85", OPTION_FLOAT, "end of axis saturation range for joystick where change is ignored (0.0 center, 1.0 end)" },
158 { OPTION_NATURAL_KEYBOARD ";nat", "0", OPTION_BOOLEAN, "specifies whether to use a natural keyboard or not" },
159 { OPTION_JOYSTICK_CONTRADICTORY ";joy_contradictory","0", OPTION_BOOLEAN, "enable contradictory direction digital joystick input at the same time" },
160 { OPTION_COIN_IMPULSE, "0", OPTION_INTEGER, "set coin impulse time (n<0 disable impulse, n==0 obey driver, 0<n set time n)" },
161
162 // input autoenable options
163 { nullptr, nullptr, OPTION_HEADER, "CORE INPUT AUTOMATIC ENABLE OPTIONS" },
164 { OPTION_PADDLE_DEVICE ";paddle", "keyboard", OPTION_STRING, "enable (none|keyboard|mouse|lightgun|joystick) if a paddle control is present" },
165 { OPTION_ADSTICK_DEVICE ";adstick", "keyboard", OPTION_STRING, "enable (none|keyboard|mouse|lightgun|joystick) if an analog joystick control is present" },
166 { OPTION_PEDAL_DEVICE ";pedal", "keyboard", OPTION_STRING, "enable (none|keyboard|mouse|lightgun|joystick) if a pedal control is present" },
167 { OPTION_DIAL_DEVICE ";dial", "keyboard", OPTION_STRING, "enable (none|keyboard|mouse|lightgun|joystick) if a dial control is present" },
168 { OPTION_TRACKBALL_DEVICE ";trackball", "keyboard", OPTION_STRING, "enable (none|keyboard|mouse|lightgun|joystick) if a trackball control is present" },
169 { OPTION_LIGHTGUN_DEVICE, "keyboard", OPTION_STRING, "enable (none|keyboard|mouse|lightgun|joystick) if a lightgun control is present" },
170 { OPTION_POSITIONAL_DEVICE, "keyboard", OPTION_STRING, "enable (none|keyboard|mouse|lightgun|joystick) if a positional control is present" },
171 { OPTION_MOUSE_DEVICE, "mouse", OPTION_STRING, "enable (none|keyboard|mouse|lightgun|joystick) if a mouse control is present" },
172
173 // debugging options
174 { nullptr, nullptr, OPTION_HEADER, "CORE DEBUGGING OPTIONS" },
175 { OPTION_VERBOSE ";v", "0", OPTION_BOOLEAN, "display additional diagnostic information" },
176 { OPTION_LOG, "0", OPTION_BOOLEAN, "generate an error.log file" },
177 { OPTION_OSLOG, "0", OPTION_BOOLEAN, "output error.log data to system diagnostic output (debugger or standard error)" },
178 { OPTION_DEBUG ";d", "0", OPTION_BOOLEAN, "enable/disable debugger" },
179 { OPTION_UPDATEINPAUSE, "0", OPTION_BOOLEAN, "keep calling video updates while in pause" },
180 { OPTION_DEBUGSCRIPT, nullptr, OPTION_STRING, "script for debugger" },
181 { OPTION_DEBUGLOG, "0", OPTION_BOOLEAN, "write debug console output to debug.log" },
182
183 // comm options
184 { nullptr, nullptr, OPTION_HEADER, "CORE COMM OPTIONS" },
185 { OPTION_COMM_LOCAL_HOST, "0.0.0.0", OPTION_STRING, "local address to bind to" },
186 { OPTION_COMM_LOCAL_PORT, "15112", OPTION_STRING, "local port to bind to" },
187 { OPTION_COMM_REMOTE_HOST, "127.0.0.1", OPTION_STRING, "remote address to connect to" },
188 { OPTION_COMM_REMOTE_PORT, "15112", OPTION_STRING, "remote port to connect to" },
189 { OPTION_COMM_FRAME_SYNC, "0", OPTION_BOOLEAN, "sync frames" },
190
191 // misc options
192 { nullptr, nullptr, OPTION_HEADER, "CORE MISC OPTIONS" },
193 { OPTION_DRC, "1", OPTION_BOOLEAN, "enable DRC CPU core if available" },
194 { OPTION_DRC_USE_C, "0", OPTION_BOOLEAN, "force DRC to use C backend" },
195 { OPTION_DRC_LOG_UML, "0", OPTION_BOOLEAN, "write DRC UML disassembly log" },
196 { OPTION_DRC_LOG_NATIVE, "0", OPTION_BOOLEAN, "write DRC native disassembly log" },
197 { OPTION_BIOS, nullptr, OPTION_STRING, "select the system BIOS to use" },
198 { OPTION_CHEAT ";c", "0", OPTION_BOOLEAN, "enable cheat subsystem" },
199 { OPTION_SKIP_GAMEINFO, "0", OPTION_BOOLEAN, "skip displaying the system information screen at startup" },
200 { OPTION_UI_FONT, "default", OPTION_STRING, "specify a font to use" },
201 { OPTION_UI, "cabinet", OPTION_STRING, "type of UI (simple|cabinet)" },
202 { OPTION_RAMSIZE ";ram", nullptr, OPTION_STRING, "size of RAM (if supported by driver)" },
203 { OPTION_CONFIRM_QUIT, "0", OPTION_BOOLEAN, "ask for confirmation before exiting" },
204 { OPTION_UI_MOUSE, "1", OPTION_BOOLEAN, "display UI mouse cursor" },
205 { OPTION_LANGUAGE ";lang", "English", OPTION_STRING, "set UI display language" },
206 { OPTION_NVRAM_SAVE ";nvwrite", "1", OPTION_BOOLEAN, "save NVRAM data on exit" },
207
208 { nullptr, nullptr, OPTION_HEADER, "SCRIPTING OPTIONS" },
209 { OPTION_AUTOBOOT_COMMAND ";ab", nullptr, OPTION_STRING, "command to execute after machine boot" },
210 { OPTION_AUTOBOOT_DELAY, "0", OPTION_INTEGER, "delay before executing autoboot command (seconds)" },
211 { OPTION_AUTOBOOT_SCRIPT ";script", nullptr, OPTION_STRING, "Lua script to execute after machine boot" },
212 { OPTION_CONSOLE, "0", OPTION_BOOLEAN, "enable emulator Lua console" },
213 { OPTION_PLUGINS, "1", OPTION_BOOLEAN, "enable Lua plugin support" },
214 { OPTION_PLUGIN, nullptr, OPTION_STRING, "list of plugins to enable" },
215 { OPTION_NO_PLUGIN, nullptr, OPTION_STRING, "list of plugins to disable" },
216
217 { nullptr, nullptr, OPTION_HEADER, "HTTP SERVER OPTIONS" },
218 { OPTION_HTTP, "0", OPTION_BOOLEAN, "enable HTTP server" },
219 { OPTION_HTTP_PORT, "8080", OPTION_INTEGER, "HTTP server port" },
220 { OPTION_HTTP_ROOT, "web", OPTION_STRING, "HTTP server document root" },
221
222 { nullptr }
223 };
224
225
226
227 //**************************************************************************
228 // CUSTOM OPTION ENTRIES AND SUPPORT CLASSES
229 //**************************************************************************
230
231 namespace
232 {
233 // custom option entry for the system name
234 class system_name_option_entry : public core_options::entry
235 {
236 public:
system_name_option_entry(emu_options & host)237 system_name_option_entry(emu_options &host)
238 : entry(OPTION_SYSTEMNAME)
239 , m_host(host)
240 {
241 }
242
value() const243 virtual const char *value() const noexcept override
244 {
245 // This is returning an empty string instead of nullptr to signify that
246 // specifying the value is a meaningful operation. The option types that
247 // return nullptr are option types that cannot have a value (e.g. - commands)
248 //
249 // See comments in core_options::entry::value() and core_options::simple_entry::value()
250 return m_host.system() ? m_host.system()->name : "";
251 }
252
253 protected:
internal_set_value(std::string && newvalue)254 virtual void internal_set_value(std::string &&newvalue) override
255 {
256 m_host.set_system_name(std::move(newvalue));
257 }
258
259 private:
260 emu_options &m_host;
261 };
262
263 // custom option entry for the software name
264 class software_name_option_entry : public core_options::entry
265 {
266 public:
software_name_option_entry(emu_options & host)267 software_name_option_entry(emu_options &host)
268 : entry(OPTION_SOFTWARENAME)
269 , m_host(host)
270 {
271 }
272
273 protected:
internal_set_value(std::string && newvalue)274 virtual void internal_set_value(std::string &&newvalue) override
275 {
276 m_host.set_software(std::move(newvalue));
277 }
278
279 private:
280 emu_options &m_host;
281 };
282
283 // custom option entry for slots
284 class slot_option_entry : public core_options::entry
285 {
286 public:
slot_option_entry(const char * name,slot_option & host)287 slot_option_entry(const char *name, slot_option &host)
288 : entry(name)
289 , m_host(host)
290 {
291 }
292
value() const293 virtual const char *value() const noexcept override
294 {
295 const char *result = nullptr;
296 if (m_host.specified())
297 {
298 // m_temp is a temporary variable used to keep the specified value
299 // so the result can be returned as 'const char *'. Obviously, this
300 // value will be trampled upon if value() is called again. This doesn't
301 // happen in practice
302 //
303 // In reality, I want to really return std::optional<std::string> here
304 // FIXME: the std::string assignment can throw exceptions, and returning std::optional<std::string> also isn't safe in noexcept
305 m_temp = m_host.specified_value();
306 result = m_temp.c_str();
307 }
308 return result;
309 }
310
311 protected:
internal_set_value(std::string && newvalue)312 virtual void internal_set_value(std::string &&newvalue) override
313 {
314 m_host.specify(std::move(newvalue), false);
315 }
316
317 private:
318 slot_option & m_host;
319 mutable std::string m_temp;
320 };
321
322 // custom option entry for images
323 class image_option_entry : public core_options::entry
324 {
325 public:
image_option_entry(std::vector<std::string> && names,image_option & host)326 image_option_entry(std::vector<std::string> &&names, image_option &host)
327 : entry(std::move(names))
328 , m_host(host)
329 {
330 }
331
value() const332 virtual const char *value() const noexcept override
333 {
334 return m_host.value().c_str();
335 }
336
337 protected:
internal_set_value(std::string && newvalue)338 virtual void internal_set_value(std::string &&newvalue) override
339 {
340 m_host.specify(std::move(newvalue), false);
341 }
342
343 private:
344 image_option &m_host;
345 };
346
347 // existing option tracker class; used by slot/image calculus to identify existing
348 // options for later purging
349 template<typename T>
350 class existing_option_tracker
351 {
352 public:
existing_option_tracker(const std::unordered_map<std::string,T> & map)353 existing_option_tracker(const std::unordered_map<std::string, T> &map)
354 {
355 m_vec.reserve(map.size());
356 for (const auto &entry : map)
357 m_vec.push_back(&entry.first);
358 }
359
360 template<typename TStr>
remove(const TStr & str)361 void remove(const TStr &str)
362 {
363 auto iter = std::find_if(
364 m_vec.begin(),
365 m_vec.end(),
366 [&str](const auto &x) { return *x == str; });
367 if (iter != m_vec.end())
368 m_vec.erase(iter);
369 }
370
begin()371 std::vector<const std::string *>::iterator begin() { return m_vec.begin(); }
end()372 std::vector<const std::string *>::iterator end() { return m_vec.end(); }
373
374 private:
375 std::vector<const std::string *> m_vec;
376 };
377
378
379 //-------------------------------------------------
380 // get_full_option_names
381 //-------------------------------------------------
382
get_full_option_names(const device_image_interface & image)383 std::vector<std::string> get_full_option_names(const device_image_interface &image)
384 {
385 std::vector<std::string> result;
386 bool same_name = image.instance_name() == image.brief_instance_name();
387
388 result.push_back(image.instance_name());
389 if (!same_name)
390 result.push_back(image.brief_instance_name());
391
392 if (image.instance_name() != image.canonical_instance_name())
393 {
394 result.push_back(image.canonical_instance_name());
395 if (!same_name)
396 result.push_back(image.brief_instance_name() + "1");
397 }
398 return result;
399 }
400
401
402 //-------------------------------------------------
403 // conditionally_peg_priority
404 //-------------------------------------------------
405
conditionally_peg_priority(core_options::entry::weak_ptr & entry,bool peg_priority)406 void conditionally_peg_priority(core_options::entry::weak_ptr &entry, bool peg_priority)
407 {
408 // if the [image|slot] entry was specified outside of the context of the options sytem, we need
409 // to peg the priority of any associated core_options::entry at the maximum priority
410 if (peg_priority && !entry.expired())
411 entry.lock()->set_priority(OPTION_PRIORITY_MAXIMUM);
412 }
413 }
414
415
416 //**************************************************************************
417 // EMU OPTIONS
418 //**************************************************************************
419
420 //-------------------------------------------------
421 // emu_options - constructor
422 //-------------------------------------------------
423
emu_options(option_support support)424 emu_options::emu_options(option_support support)
425 : m_support(support)
426 , m_system(nullptr)
427 , m_coin_impulse(0)
428 , m_joystick_contradictory(false)
429 , m_sleep(true)
430 , m_refresh_speed(false)
431 , m_ui(UI_CABINET)
432 {
433 // add entries
434 if (support == option_support::FULL || support == option_support::GENERAL_AND_SYSTEM)
435 add_entry(std::make_shared<system_name_option_entry>(*this));
436 if (support == option_support::FULL)
437 add_entry(std::make_shared<software_name_option_entry>(*this));
438 add_entries(emu_options::s_option_entries);
439
440 // adding handlers to keep copies of frequently requested options in member variables
441 set_value_changed_handler(OPTION_COIN_IMPULSE, [this](const char *value) { m_coin_impulse = int_value(OPTION_COIN_IMPULSE); });
442 set_value_changed_handler(OPTION_JOYSTICK_CONTRADICTORY, [this](const char *value) { m_joystick_contradictory = bool_value(OPTION_JOYSTICK_CONTRADICTORY); });
443 set_value_changed_handler(OPTION_SLEEP, [this](const char *value) { m_sleep = bool_value(OPTION_SLEEP); });
444 set_value_changed_handler(OPTION_REFRESHSPEED, [this](const char *value) { m_refresh_speed = bool_value(OPTION_REFRESHSPEED); });
445 set_value_changed_handler(OPTION_UI, [this](const std::string &value)
446 {
447 if (value == "simple")
448 m_ui = UI_SIMPLE;
449 else
450 m_ui = UI_CABINET;
451 });
452 }
453
454
455 //-------------------------------------------------
456 // emu_options - destructor
457 //-------------------------------------------------
458
~emu_options()459 emu_options::~emu_options()
460 {
461 }
462
463
464 //-------------------------------------------------
465 // system_name
466 //-------------------------------------------------
467
system_name() const468 const char *emu_options::system_name() const
469 {
470 return m_system ? m_system->name : "";
471 }
472
473
474 //-------------------------------------------------
475 // set_system_name - called to set the system
476 // name; will adjust slot/image options as appropriate
477 //-------------------------------------------------
478
set_system_name(std::string && new_system_name)479 void emu_options::set_system_name(std::string &&new_system_name)
480 {
481 const game_driver *new_system = nullptr;
482
483 // we are making an attempt - record what we're attempting
484 m_attempted_system_name = std::move(new_system_name);
485
486 // was a system name specified?
487 if (!m_attempted_system_name.empty())
488 {
489 // if so, first extract the base name (the reason for this is drag-and-drop on Windows; a side
490 // effect is a command line like 'mame pacman.foo' will work correctly, but so be it)
491 std::string new_system_base_name = core_filename_extract_base(m_attempted_system_name, true);
492
493 // perform the lookup (and error if it cannot be found)
494 int index = driver_list::find(new_system_base_name.c_str());
495 if (index < 0)
496 throw options_error_exception("Unknown system '%s'", m_attempted_system_name);
497 new_system = &driver_list::driver(index);
498 }
499
500 // did we change anything?
501 if (new_system != m_system)
502 {
503 // if so, specify the new system and update (if we're fully supporting slot/image options)
504 m_system = new_system;
505 m_software_name.clear();
506 if (m_support == option_support::FULL)
507 update_slot_and_image_options();
508 }
509 }
510
511
512 //-------------------------------------------------
513 // set_system_name - called to set the system
514 // name; will adjust slot/image options as appropriate
515 //-------------------------------------------------
516
set_system_name(const std::string & new_system_name)517 void emu_options::set_system_name(const std::string &new_system_name)
518 {
519 set_system_name(std::string(new_system_name));
520 }
521
522
523 //-------------------------------------------------
524 // update_slot_and_image_options
525 //-------------------------------------------------
526
update_slot_and_image_options()527 void emu_options::update_slot_and_image_options()
528 {
529 bool changed;
530 do
531 {
532 changed = false;
533
534 // first we add and remove slot options depending on what has been configured in the
535 // device, bringing m_slot_options up to a state where it matches machine_config
536 if (add_and_remove_slot_options())
537 changed = true;
538
539 // second, we perform an analgous operation with m_image_options
540 if (add_and_remove_image_options())
541 changed = true;
542
543 // if we changed anything, we should reevaluate existing options
544 if (changed)
545 reevaluate_default_card_software();
546 } while (changed);
547 }
548
549
550 //-------------------------------------------------
551 // add_and_remove_slot_options - add any missing
552 // and/or purge extraneous slot options
553 //-------------------------------------------------
554
add_and_remove_slot_options()555 bool emu_options::add_and_remove_slot_options()
556 {
557 bool changed = false;
558
559 // first, create a list of existing slot options; this is so we can purge
560 // any stray slot options that are no longer pertinent when we're done
561 existing_option_tracker<::slot_option> existing(m_slot_options);
562
563 // it is perfectly legal for this to be called without a system; we
564 // need to check for that condition!
565 if (m_system)
566 {
567 // create the configuration
568 machine_config config(*m_system, *this);
569
570 for (const device_slot_interface &slot : slot_interface_iterator(config.root_device()))
571 {
572 // come up with the canonical name of the slot
573 const char *slot_option_name = slot.slot_name();
574
575 // erase this option from existing (so we don't purge it later)
576 existing.remove(slot_option_name);
577
578 // do we need to add this option?
579 if (!has_slot_option(slot_option_name))
580 {
581 // we do - add it to m_slot_options
582 auto pair = std::make_pair(slot_option_name, ::slot_option(*this, slot.default_option()));
583 ::slot_option &new_option(m_slot_options.emplace(std::move(pair)).first->second);
584 changed = true;
585
586 // for non-fixed slots, this slot needs representation in the options collection
587 if (!slot.fixed())
588 {
589 // first device? add the header as to be pretty
590 const char *header = "SLOT DEVICES";
591 if (!header_exists(header))
592 add_header(header);
593
594 // create a new entry in the options
595 auto new_entry = new_option.setup_option_entry(slot_option_name);
596
597 // and add it
598 add_entry(std::move(new_entry), header);
599 }
600 }
601
602 }
603 }
604
605 // at this point we need to purge stray slot options that may no longer be pertinent
606 for (auto &opt_name : existing)
607 {
608 auto iter = m_slot_options.find(*opt_name);
609 assert(iter != m_slot_options.end());
610
611 // if this is represented in core_options, remove it
612 if (iter->second.option_entry())
613 remove_entry(*iter->second.option_entry());
614
615 // remove this option
616 m_slot_options.erase(iter);
617 changed = true;
618 }
619
620 return changed;
621 }
622
623
624 //-------------------------------------------------
625 // add_and_remove_slot_options - add any missing
626 // and/or purge extraneous slot options
627 //-------------------------------------------------
628
add_and_remove_image_options()629 bool emu_options::add_and_remove_image_options()
630 {
631 // The logic for image options is superficially similar to the logic for slot options, but
632 // there is one larger piece of complexity. The image instance names (returned by the
633 // image_instance() call and surfaced in the UI) may change simply because we've added more
634 // devices. This is because the instance_name() for a singular cartridge device might be
635 // "cartridge" starting out, but become "cartridge1" when another cartridge device is added.
636 //
637 // To get around this behavior, our internal data structures work in terms of what is
638 // returned by canonical_instance_name(), which will be something like "cartridge1" both
639 // for a singular cartridge device and the first cartridge in a multi cartridge system.
640 //
641 // The need for this behavior was identified by Tafoid when the following command line
642 // regressed:
643 //
644 // mame snes bsxsore -cart2 bszelda
645 //
646 // Before we were accounting for this behavior, 'bsxsore' got stored in "cartridge" and
647 // the association got lost when the second cartridge was added.
648
649 bool changed = false;
650
651 // first, create a list of existing image options; this is so we can purge
652 // any stray slot options that are no longer pertinent when we're done; we
653 // have to do this for both "flavors" of name
654 existing_option_tracker<::image_option> existing(m_image_options_canonical);
655
656 // wipe the non-canonical image options; we're going to rebuild it
657 m_image_options.clear();
658
659 // it is perfectly legal for this to be called without a system; we
660 // need to check for that condition!
661 if (m_system)
662 {
663 // create the configuration
664 machine_config config(*m_system, *this);
665
666 // iterate through all image devices
667 for (device_image_interface &image : image_interface_iterator(config.root_device()))
668 {
669 const std::string &canonical_name(image.canonical_instance_name());
670
671 // erase this option from existing (so we don't purge it later)
672 existing.remove(canonical_name);
673
674 // do we need to add this option?
675 auto iter = m_image_options_canonical.find(canonical_name);
676 ::image_option *this_option = iter != m_image_options_canonical.end() ? &iter->second : nullptr;
677 if (!this_option)
678 {
679 // we do - add it to both m_image_options_canonical and m_image_options
680 auto pair = std::make_pair(canonical_name, ::image_option(*this, image.canonical_instance_name()));
681 this_option = &m_image_options_canonical.emplace(std::move(pair)).first->second;
682 changed = true;
683
684 // if this image is user loadable, we have to surface it in the core_options
685 if (image.user_loadable())
686 {
687 // first device? add the header as to be pretty
688 const char *header = "IMAGE DEVICES";
689 if (!header_exists(header))
690 add_header(header);
691
692 // name this options
693 auto names = get_full_option_names(image);
694
695 // create a new entry in the options
696 auto new_entry = this_option->setup_option_entry(std::move(names));
697
698 // and add it
699 add_entry(std::move(new_entry), header);
700 }
701 }
702
703 // whether we added it or we didn't, we have to add it to the m_image_option map
704 m_image_options[image.instance_name()] = this_option;
705 }
706 }
707
708 // at this point we need to purge stray image options that may no longer be pertinent
709 for (auto &opt_name : existing)
710 {
711 auto iter = m_image_options_canonical.find(*opt_name);
712 assert(iter != m_image_options_canonical.end());
713
714 // if this is represented in core_options, remove it
715 if (iter->second.option_entry())
716 remove_entry(*iter->second.option_entry());
717
718 // remove this option
719 m_image_options_canonical.erase(iter);
720 changed = true;
721 }
722
723 return changed;
724 }
725
726
727 //-------------------------------------------------
728 // reevaluate_default_card_software - based on recent
729 // changes in what images are mounted, give drivers
730 // a chance to specify new default slot options
731 //-------------------------------------------------
732
reevaluate_default_card_software()733 void emu_options::reevaluate_default_card_software()
734 {
735 // if we don't have a system specified, this is
736 // a meaningless operation
737 if (!m_system)
738 return;
739
740 bool found;
741 do
742 {
743 // set up the machine_config
744 machine_config config(*m_system, *this);
745 found = false;
746
747 // iterate through all slot devices
748 for (device_slot_interface &slot : slot_interface_iterator(config.root_device()))
749 {
750 // retrieve info about the device instance
751 auto &slot_opt(slot_option(slot.slot_name()));
752
753 // device_slot_interface::get_default_card_software() allows a device that
754 // implements both device_slot_interface and device_image_interface to
755 // probe an image and specify the card device that should be loaded
756 //
757 // In the repeated cycle of adding slots and slot devices, this gives a chance
758 // for devices to "plug in" default software list items. Of course, the fact
759 // that this is all shuffling options is brittle and roundabout, but such is
760 // the nature of software lists.
761 //
762 // In reality, having some sort of hook into the pipeline of slot/device evaluation
763 // makes sense, but the fact that it is joined at the hip to device_image_interface
764 // and device_slot_interface is unfortunate
765 std::string default_card_software = get_default_card_software(slot);
766 if (slot_opt.default_card_software() != default_card_software)
767 {
768 slot_opt.set_default_card_software(std::move(default_card_software));
769
770 // calling set_default_card_software() can cause a cascade of slot/image
771 // evaluations; we need to bail out of this loop because the iterator
772 // may be bad
773 found = true;
774 break;
775 }
776 }
777 } while (found);
778 }
779
780
781 //-------------------------------------------------
782 // get_default_card_software
783 //-------------------------------------------------
784
get_default_card_software(device_slot_interface & slot)785 std::string emu_options::get_default_card_software(device_slot_interface &slot)
786 {
787 std::string image_path;
788 std::function<bool(util::core_file &, std::string&)> get_hashfile_extrainfo;
789
790 // figure out if an image option has been specified, and if so, get the image path out of the options
791 device_image_interface *image = dynamic_cast<device_image_interface *>(&slot);
792 if (image)
793 {
794 image_path = image_option(image->instance_name()).value();
795
796 get_hashfile_extrainfo = [image, this](util::core_file &file, std::string &extrainfo)
797 {
798 util::hash_collection hashes = image->calculate_hash_on_file(file);
799
800 return hashfile_extrainfo(
801 hash_path(),
802 image->device().mconfig().gamedrv(),
803 hashes,
804 extrainfo);
805 };
806 }
807
808 // create the hook
809 get_default_card_software_hook hook(image_path, std::move(get_hashfile_extrainfo));
810
811 // and invoke the slot's implementation of get_default_card_software()
812 return slot.get_default_card_software(hook);
813 }
814
815
816 //-------------------------------------------------
817 // set_software - called to load "unqualified"
818 // software out of a software list (e.g. - "mame nes 'zelda'")
819 //-------------------------------------------------
820
set_software(std::string && new_software)821 void emu_options::set_software(std::string &&new_software)
822 {
823 // identify any options as a result of softlists
824 software_options softlist_opts = evaluate_initial_softlist_options(new_software);
825
826 while (!softlist_opts.slot.empty() || !softlist_opts.image.empty())
827 {
828 // track how many options we have
829 size_t before_size = softlist_opts.slot.size() + softlist_opts.image.size();
830
831 // keep a list of deferred options, in case anything is applied
832 // out of order
833 software_options deferred_opts;
834
835 // distribute slot options
836 for (auto &slot_opt : softlist_opts.slot)
837 {
838 auto iter = m_slot_options.find(slot_opt.first);
839 if (iter != m_slot_options.end())
840 iter->second.specify(std::move(slot_opt.second));
841 else
842 deferred_opts.slot[slot_opt.first] = std::move(slot_opt.second);
843 }
844
845 // distribute image options
846 for (auto &image_opt : softlist_opts.image)
847 {
848 auto iter = m_image_options.find(image_opt.first);
849 if (iter != m_image_options.end())
850 iter->second->specify(std::move(image_opt.second));
851 else
852 deferred_opts.image[image_opt.first] = std::move(image_opt.second);
853 }
854
855 // keep any deferred options for the next round
856 softlist_opts = std::move(deferred_opts);
857
858 // do we have any pending options after failing to distribute any?
859 size_t after_size = softlist_opts.slot.size() + softlist_opts.image.size();
860 if ((after_size > 0) && after_size >= before_size)
861 throw options_error_exception("Could not assign software option");
862 }
863
864 // we've succeeded; update the set name
865 m_software_name = std::move(new_software);
866 }
867
868
869 //-------------------------------------------------
870 // evaluate_initial_softlist_options
871 //-------------------------------------------------
872
evaluate_initial_softlist_options(const std::string & software_identifier)873 emu_options::software_options emu_options::evaluate_initial_softlist_options(const std::string &software_identifier)
874 {
875 software_options results;
876
877 // load software specified at the command line (if any of course)
878 if (!software_identifier.empty())
879 {
880 // we have software; first identify the proper game_driver
881 if (!m_system)
882 throw options_error_exception("Cannot specify software without specifying system");
883
884 // and set up a configuration
885 machine_config config(*m_system, *this);
886 software_list_device_iterator iter(config.root_device());
887 if (iter.count() == 0)
888 throw emu_fatalerror(EMU_ERR_FATALERROR, "Error: unknown option: %s\n", software_identifier.c_str());
889
890 // and finally set up the stack
891 std::stack<std::string> software_identifier_stack;
892 software_identifier_stack.push(software_identifier);
893
894 // we need to keep evaluating softlist identifiers until the stack is empty
895 while (!software_identifier_stack.empty())
896 {
897 // pop the identifier
898 std::string current_software_identifier = std::move(software_identifier_stack.top());
899 software_identifier_stack.pop();
900
901 // and parse it
902 std::string list_name, software_name;
903 auto colon_pos = current_software_identifier.find_first_of(':');
904 if (colon_pos != std::string::npos)
905 {
906 list_name = current_software_identifier.substr(0, colon_pos);
907 software_name = current_software_identifier.substr(colon_pos + 1);
908 }
909 else
910 {
911 software_name = current_software_identifier;
912 }
913
914 // loop through all softlist devices, and try to find one capable of handling the requested software
915 bool found = false;
916 bool compatible = false;
917 for (software_list_device &swlistdev : iter)
918 {
919 if (list_name.empty() || (list_name == swlistdev.list_name()))
920 {
921 const software_info *swinfo = swlistdev.find(software_name);
922 if (swinfo != nullptr)
923 {
924 // loop through all parts
925 for (const software_part &swpart : swinfo->parts())
926 {
927 // only load compatible software this way
928 if (swlistdev.is_compatible(swpart) == SOFTWARE_IS_COMPATIBLE)
929 {
930 // we need to find a mountable image slot, but we need to ensure it is a slot
931 // for which we have not already distributed a part to
932 device_image_interface *image = software_list_device::find_mountable_image(
933 config,
934 swpart,
935 [&results](const device_image_interface &candidate) { return results.image.count(candidate.instance_name()) == 0; });
936
937 // did we find a slot to put this part into?
938 if (image != nullptr)
939 {
940 // we've resolved this software
941 results.image[image->instance_name()] = string_format("%s:%s:%s", swlistdev.list_name(), software_name, swpart.name());
942
943 // does this software part have a requirement on another part?
944 const char *requirement = swpart.feature("requirement");
945 if (requirement)
946 software_identifier_stack.push(requirement);
947 }
948 compatible = true;
949 }
950 found = true;
951 }
952
953 // identify other shared features specified as '<<slot name>>_default'
954 //
955 // example from SMS:
956 //
957 // <software name = "alexbmx">
958 // ...
959 // <sharedfeat name = "ctrl1_default" value = "paddle" />
960 // </software>
961 for (const feature_list_item &fi : swinfo->shared_info())
962 {
963 const std::string default_suffix = "_default";
964 if (fi.name().size() > default_suffix.size()
965 && fi.name().compare(fi.name().size() - default_suffix.size(), default_suffix.size(), default_suffix) == 0)
966 {
967 std::string slot_name = fi.name().substr(0, fi.name().size() - default_suffix.size());
968 results.slot[slot_name] = fi.value();
969 }
970 }
971 }
972 }
973 if (compatible)
974 break;
975 }
976
977 if (!compatible)
978 {
979 software_list_device::display_matches(config, nullptr, software_name);
980
981 // The text of this options_error_exception() is then passed to osd_printf_error() in cli_frontend::execute(). Therefore, it needs
982 // to be human readable text. We want to snake through a message about software incompatibility while being silent if that is not
983 // the case.
984 //
985 // Arguably, anything related to user-visible text should really be done within src/frontend. The invocation of
986 // software_list_device::display_matches() should really be done there as well
987 if (!found)
988 throw options_error_exception("");
989 else
990 throw options_error_exception("Software '%s' is incompatible with system '%s'\n", software_name, m_system->name);
991 }
992 }
993 }
994 return results;
995 }
996
997
998 //-------------------------------------------------
999 // find_slot_option
1000 //-------------------------------------------------
1001
find_slot_option(const std::string & device_name) const1002 const slot_option *emu_options::find_slot_option(const std::string &device_name) const
1003 {
1004 auto iter = m_slot_options.find(device_name);
1005 return iter != m_slot_options.end() ? &iter->second : nullptr;
1006 }
1007
find_slot_option(const std::string & device_name)1008 slot_option *emu_options::find_slot_option(const std::string &device_name)
1009 {
1010 auto iter = m_slot_options.find(device_name);
1011 return iter != m_slot_options.end() ? &iter->second : nullptr;
1012 }
1013
1014
1015
1016 //-------------------------------------------------
1017 // slot_option
1018 //-------------------------------------------------
1019
slot_option(const std::string & device_name) const1020 const slot_option &emu_options::slot_option(const std::string &device_name) const
1021 {
1022 const ::slot_option *opt = find_slot_option(device_name);
1023 assert(opt && "Attempt to access non-existent slot option");
1024 return *opt;
1025 }
1026
slot_option(const std::string & device_name)1027 slot_option &emu_options::slot_option(const std::string &device_name)
1028 {
1029 ::slot_option *opt = find_slot_option(device_name);
1030 assert(opt && "Attempt to access non-existent slot option");
1031 return *opt;
1032 }
1033
1034
1035 //-------------------------------------------------
1036 // image_option
1037 //-------------------------------------------------
1038
image_option(const std::string & device_name) const1039 const image_option &emu_options::image_option(const std::string &device_name) const
1040 {
1041 auto iter = m_image_options.find(device_name);
1042 assert(iter != m_image_options.end() && "Attempt to access non-existent image option");
1043 return *iter->second;
1044 }
1045
image_option(const std::string & device_name)1046 image_option &emu_options::image_option(const std::string &device_name)
1047 {
1048 auto iter = m_image_options.find(device_name);
1049 assert(iter != m_image_options.end() && "Attempt to access non-existent image option");
1050 return *iter->second;
1051 }
1052
1053
1054 //-------------------------------------------------
1055 // command_argument_processed
1056 //-------------------------------------------------
1057
command_argument_processed()1058 void emu_options::command_argument_processed()
1059 {
1060 // some command line arguments require that the system name be set, so we can get slot options
1061 if (command_arguments().size() == 1 && !core_iswildstr(command_arguments()[0].c_str()) &&
1062 (command() == "listdevices" || (command() == "listslots") || (command() == "listmedia") || (command() == "listsoftware")))
1063 {
1064 set_system_name(command_arguments()[0]);
1065 }
1066 }
1067
1068
1069 //**************************************************************************
1070 // SLOT OPTIONS
1071 //**************************************************************************
1072
1073 //-------------------------------------------------
1074 // slot_option ctor
1075 //-------------------------------------------------
1076
slot_option(emu_options & host,const char * default_value)1077 slot_option::slot_option(emu_options &host, const char *default_value)
1078 : m_host(host)
1079 , m_specified(false)
1080 , m_default_value(default_value ? default_value : "")
1081 {
1082 }
1083
1084
1085 //-------------------------------------------------
1086 // slot_option::value
1087 //-------------------------------------------------
1088
value() const1089 const std::string &slot_option::value() const
1090 {
1091 // There are a number of ways that the value can be determined; there
1092 // is a specific order of precedence:
1093 //
1094 // 1. Highest priority is whatever may have been specified by the user (whether it
1095 // was specified at the command line, an INI file, or in the UI). We keep track
1096 // of whether these values were specified this way
1097 //
1098 // Take note that slots have a notion of being "selectable". Slots that are not
1099 // marked as selectable cannot be specified with this technique
1100 //
1101 // 2. Next highest is what is returned from get_default_card_software()
1102 //
1103 // 3. Last in priority is what was specified as the slot default. This comes from
1104 // device setup
1105 if (m_specified)
1106 return m_specified_value;
1107 else if (!m_default_card_software.empty())
1108 return m_default_card_software;
1109 else
1110 return m_default_value;
1111 }
1112
1113
1114 //-------------------------------------------------
1115 // slot_option::specified_value
1116 //-------------------------------------------------
1117
specified_value() const1118 std::string slot_option::specified_value() const
1119 {
1120 std::string result;
1121 if (m_specified)
1122 {
1123 result = m_specified_bios.empty()
1124 ? m_specified_value
1125 : util::string_format("%s,bios=%s", m_specified_value, m_specified_bios);
1126 }
1127 return result;
1128 }
1129
1130
1131 //-------------------------------------------------
1132 // slot_option::specify
1133 //-------------------------------------------------
1134
specify(std::string && text,bool peg_priority)1135 void slot_option::specify(std::string &&text, bool peg_priority)
1136 {
1137 // record the old value; we may need to trigger an update
1138 const std::string old_value = value();
1139
1140 // we need to do some elementary parsing here
1141 const char *bios_arg = ",bios=";
1142 const size_t pos = text.find(bios_arg);
1143 if (pos != std::string::npos)
1144 {
1145 m_specified = true;
1146 m_specified_value = text.substr(0, pos);
1147 m_specified_bios = text.substr(pos + strlen(bios_arg));
1148 }
1149 else
1150 {
1151 m_specified = true;
1152 m_specified_value = std::move(text);
1153 m_specified_bios = "";
1154 }
1155
1156 conditionally_peg_priority(m_entry, peg_priority);
1157
1158 // we may have changed
1159 possibly_changed(old_value);
1160 }
1161
1162
1163 //-------------------------------------------------
1164 // slot_option::specify
1165 //-------------------------------------------------
1166
specify(const std::string & text,bool peg_priority)1167 void slot_option::specify(const std::string &text, bool peg_priority)
1168 {
1169 specify(std::string(text), peg_priority);
1170 }
1171
1172
1173 //-------------------------------------------------
1174 // slot_option::set_default_card_software
1175 //-------------------------------------------------
1176
set_default_card_software(std::string && s)1177 void slot_option::set_default_card_software(std::string &&s)
1178 {
1179 // record the old value; we may need to trigger an update
1180 const std::string old_value = value();
1181
1182 // update the default card software
1183 m_default_card_software = std::move(s);
1184
1185 // we may have changed
1186 possibly_changed(old_value);
1187 }
1188
1189
1190 //-------------------------------------------------
1191 // slot_option::possibly_changed
1192 //-------------------------------------------------
1193
possibly_changed(const std::string & old_value)1194 void slot_option::possibly_changed(const std::string &old_value)
1195 {
1196 if (value() != old_value)
1197 m_host.update_slot_and_image_options();
1198 }
1199
1200
1201 //-------------------------------------------------
1202 // slot_option::set_bios
1203 //-------------------------------------------------
1204
set_bios(std::string && text)1205 void slot_option::set_bios(std::string &&text)
1206 {
1207 if (!m_specified)
1208 {
1209 m_specified = true;
1210 m_specified_value = value();
1211 }
1212 m_specified_bios = std::move(text);
1213 }
1214
1215
1216 //-------------------------------------------------
1217 // slot_option::setup_option_entry
1218 //-------------------------------------------------
1219
setup_option_entry(const char * name)1220 core_options::entry::shared_ptr slot_option::setup_option_entry(const char *name)
1221 {
1222 // this should only be called once
1223 assert(m_entry.expired());
1224
1225 // create the entry and return it
1226 core_options::entry::shared_ptr entry = std::make_shared<slot_option_entry>(name, *this);
1227 m_entry = entry;
1228 return entry;
1229 }
1230
1231
1232 //**************************************************************************
1233 // IMAGE OPTIONS
1234 //**************************************************************************
1235
1236 //-------------------------------------------------
1237 // image_option ctor
1238 //-------------------------------------------------
1239
image_option(emu_options & host,const std::string & canonical_instance_name)1240 image_option::image_option(emu_options &host, const std::string &canonical_instance_name)
1241 : m_host(host)
1242 , m_canonical_instance_name(canonical_instance_name)
1243 {
1244 }
1245
1246
1247 //-------------------------------------------------
1248 // image_option::specify
1249 //-------------------------------------------------
1250
specify(const std::string & value,bool peg_priority)1251 void image_option::specify(const std::string &value, bool peg_priority)
1252 {
1253 if (value != m_value)
1254 {
1255 m_value = value;
1256 m_host.reevaluate_default_card_software();
1257 }
1258 conditionally_peg_priority(m_entry, peg_priority);
1259 }
1260
specify(std::string && value,bool peg_priority)1261 void image_option::specify(std::string &&value, bool peg_priority)
1262 {
1263 if (value != m_value)
1264 {
1265 m_value = std::move(value);
1266 m_host.reevaluate_default_card_software();
1267 }
1268 conditionally_peg_priority(m_entry, peg_priority);
1269 }
1270
1271
1272 //-------------------------------------------------
1273 // image_option::setup_option_entry
1274 //-------------------------------------------------
1275
setup_option_entry(std::vector<std::string> && names)1276 core_options::entry::shared_ptr image_option::setup_option_entry(std::vector<std::string> &&names)
1277 {
1278 // this should only be called once
1279 assert(m_entry.expired());
1280
1281 // create the entry and return it
1282 core_options::entry::shared_ptr entry = std::make_shared<image_option_entry>(std::move(names), *this);
1283 m_entry = entry;
1284 return entry;
1285 }
1286