summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/drivenum.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/drivenum.cpp')
-rw-r--r--src/emu/drivenum.cpp412
1 files changed, 412 insertions, 0 deletions
diff --git a/src/emu/drivenum.cpp b/src/emu/drivenum.cpp
new file mode 100644
index 00000000000..857c4bab3fb
--- /dev/null
+++ b/src/emu/drivenum.cpp
@@ -0,0 +1,412 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ drivenum.c
+
+ Driver enumeration helpers.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "drivenum.h"
+#include "softlist.h"
+#include <ctype.h>
+
+
+
+//**************************************************************************
+// DRIVER LIST
+//**************************************************************************
+
+//-------------------------------------------------
+// driver_list - constructor
+//-------------------------------------------------
+
+driver_list::driver_list()
+{
+}
+
+
+//-------------------------------------------------
+// find - find a driver by name
+//-------------------------------------------------
+
+int driver_list::find(const char *name)
+{
+ // if no name, bail
+ if (name == NULL)
+ return -1;
+
+ // create a dummy item for comparison purposes
+ game_driver driver;
+ driver.name = name;
+ game_driver *driverptr = &driver;
+
+ // binary search to find it
+ const game_driver **result = reinterpret_cast<const game_driver **>(bsearch(&driverptr, s_drivers_sorted, s_driver_count, sizeof(*s_drivers_sorted), driver_sort_callback));
+ return (result == NULL) ? -1 : result - s_drivers_sorted;
+}
+
+
+//-------------------------------------------------
+// matches - true if we match, taking into
+// account wildcards in the wildstring
+//-------------------------------------------------
+
+bool driver_list::matches(const char *wildstring, const char *string)
+{
+ // can only match internal drivers if the wildstring starts with an underscore
+ if (string[0] == '_' && (wildstring == NULL || wildstring[0] != '_'))
+ return false;
+
+ // match everything else normally
+ return (wildstring == NULL || core_strwildcmp(wildstring, string) == 0);
+}
+
+
+//-------------------------------------------------
+// driver_sort_callback - compare two items in
+// an array of game_driver pointers
+//-------------------------------------------------
+
+int driver_list::driver_sort_callback(const void *elem1, const void *elem2)
+{
+ const game_driver * const *item1 = reinterpret_cast<const game_driver * const *>(elem1);
+ const game_driver * const *item2 = reinterpret_cast<const game_driver * const *>(elem2);
+ return core_stricmp((*item1)->name, (*item2)->name);
+}
+
+
+//-------------------------------------------------
+// penalty_compare - compare two strings for
+// closeness and assign a score.
+//-------------------------------------------------
+
+int driver_list::penalty_compare(const char *source, const char *target)
+{
+ int gaps = 1;
+ bool last = true;
+
+ // scan the strings
+ for ( ; *source && *target; target++)
+ {
+ // do a case insensitive match
+ bool match = (tolower((UINT8)*source) == tolower((UINT8)*target));
+
+ // if we matched, advance the source
+ if (match)
+ source++;
+
+ // if the match state changed, count gaps
+ if (match != last)
+ {
+ last = match;
+ if (!match)
+ gaps++;
+ }
+ }
+
+ // penalty if short string does not completely fit in
+ for ( ; *source; source++)
+ gaps++;
+
+ // if we matched perfectly, gaps == 0
+ if (gaps == 1 && *source == 0 && *target == 0)
+ gaps = 0;
+
+ return gaps;
+}
+
+
+
+//**************************************************************************
+// DRIVER ENUMERATOR
+//**************************************************************************
+
+//-------------------------------------------------
+// driver_enumerator - constructor
+//-------------------------------------------------
+
+driver_enumerator::driver_enumerator(emu_options &options)
+ : m_current(-1),
+ m_filtered_count(0),
+ m_options(options),
+ m_included(s_driver_count),
+ m_config(s_driver_count)
+{
+ memset(&m_included[0], 0, s_driver_count);
+ memset(&m_config[0], 0, s_driver_count*sizeof(m_config[0]));
+ include_all();
+}
+
+
+driver_enumerator::driver_enumerator(emu_options &options, const char *string)
+ : m_current(-1),
+ m_filtered_count(0),
+ m_options(options),
+ m_included(s_driver_count),
+ m_config(s_driver_count)
+{
+ memset(&m_included[0], 0, s_driver_count);
+ memset(&m_config[0], 0, s_driver_count*sizeof(m_config[0]));
+ filter(string);
+}
+
+
+driver_enumerator::driver_enumerator(emu_options &options, const game_driver &driver)
+ : m_current(-1),
+ m_filtered_count(0),
+ m_options(options),
+ m_included(s_driver_count),
+ m_config(s_driver_count)
+{
+ memset(&m_included[0], 0, s_driver_count);
+ memset(&m_config[0], 0, s_driver_count*sizeof(m_config[0]));
+ filter(driver);
+}
+
+
+//-------------------------------------------------
+// ~driver_enumerator - destructor
+//-------------------------------------------------
+
+driver_enumerator::~driver_enumerator()
+{
+ // configs are freed by the cache
+}
+
+
+//-------------------------------------------------
+// config - return a machine_config for the given
+// driver, allocating on demand if needed
+//-------------------------------------------------
+
+machine_config &driver_enumerator::config(int index, emu_options &options) const
+{
+ assert(index >= 0 && index < s_driver_count);
+
+ // if we don't have it cached, add it
+ if (m_config[index] == NULL)
+ {
+ // if our cache is full, release the head entry
+ if (m_config_cache.count() == CONFIG_CACHE_COUNT)
+ {
+ config_entry *first = m_config_cache.first();
+ m_config[first->index()] = NULL;
+ m_config_cache.remove(*first);
+ }
+
+ // allocate the config and add it to the end of the list
+ machine_config *config = m_config[index] = global_alloc(machine_config(*s_drivers_sorted[index], options));
+ m_config_cache.append(*global_alloc(config_entry(*config, index)));
+ }
+ return *m_config[index];
+}
+
+
+//-------------------------------------------------
+// filter - filter the driver list against the
+// given string
+//-------------------------------------------------
+
+int driver_enumerator::filter(const char *filterstring)
+{
+ // reset the count
+ exclude_all();
+
+ // match name against each driver in the list
+ for (int index = 0; index < s_driver_count; index++)
+ if (matches(filterstring, s_drivers_sorted[index]->name))
+ include(index);
+
+ return m_filtered_count;
+}
+
+
+//-------------------------------------------------
+// filter - filter the driver list against the
+// given driver
+//-------------------------------------------------
+
+int driver_enumerator::filter(const game_driver &driver)
+{
+ // reset the count
+ exclude_all();
+
+ // match name against each driver in the list
+ for (int index = 0; index < s_driver_count; index++)
+ if (s_drivers_sorted[index] == &driver)
+ include(index);
+
+ return m_filtered_count;
+}
+
+
+//-------------------------------------------------
+// include_all - include all non-internal drivers
+//-------------------------------------------------
+
+void driver_enumerator::include_all()
+{
+ memset(&m_included[0], 1, sizeof(m_included[0]) * s_driver_count);
+ m_filtered_count = s_driver_count;
+
+ // always exclude the empty driver
+ int empty = find("___empty");
+ assert(empty != -1);
+ m_included[empty] = 0;
+}
+
+
+//-------------------------------------------------
+// next - get the next driver matching the given
+// filter
+//-------------------------------------------------
+
+bool driver_enumerator::next()
+{
+ // always advance one
+ release_current();
+ m_current++;
+
+ // if we have a filter, scan forward to the next match
+ while (m_current < s_driver_count)
+ {
+ if (m_included[m_current])
+ break;
+ m_current++;
+ }
+
+ // return true if we end up in range
+ return (m_current >= 0 && m_current < s_driver_count);
+}
+
+
+//-------------------------------------------------
+// next_excluded - get the next driver that is
+// not currently included in the list
+//-------------------------------------------------
+
+bool driver_enumerator::next_excluded()
+{
+ // always advance one
+ release_current();
+ m_current++;
+
+ // if we have a filter, scan forward to the next match
+ while (m_current < s_driver_count)
+ {
+ if (!m_included[m_current])
+ break;
+ m_current++;
+ }
+
+ // return true if we end up in range
+ return (m_current >= 0 && m_current < s_driver_count);
+}
+
+
+//-------------------------------------------------
+// driver_sort_callback - compare two items in
+// an array of game_driver pointers
+//-------------------------------------------------
+
+void driver_enumerator::find_approximate_matches(const char *string, int count, int *results)
+{
+#undef rand
+
+ // if no name, pick random entries
+ if (string == NULL || string[0] == 0)
+ {
+ // seed the RNG first
+ srand(osd_ticks());
+
+ // allocate a temporary list
+ std::vector<int> templist(m_filtered_count);
+ int arrayindex = 0;
+ for (int index = 0; index < s_driver_count; index++)
+ if (m_included[index])
+ templist[arrayindex++] = index;
+ assert(arrayindex == m_filtered_count);
+
+ // shuffle
+ for (int shufnum = 0; shufnum < 4 * s_driver_count; shufnum++)
+ {
+ int item1 = rand() % m_filtered_count;
+ int item2 = rand() % m_filtered_count;
+ int temp = templist[item1];
+ templist[item1] = templist[item2];
+ templist[item2] = temp;
+ }
+
+ // copy out the first few entries
+ for (int matchnum = 0; matchnum < count; matchnum++)
+ results[matchnum] = templist[matchnum % m_filtered_count];
+ return;
+ }
+
+ // allocate memory to track the penalty value
+ std::vector<int> penalty(count);
+
+ // initialize everyone's states
+ for (int matchnum = 0; matchnum < count; matchnum++)
+ {
+ penalty[matchnum] = 9999;
+ results[matchnum] = -1;
+ }
+
+ // scan the entire drivers array
+ for (int index = 0; index < s_driver_count; index++)
+ if (m_included[index])
+ {
+ // skip things that can't run
+ if ((s_drivers_sorted[index]->flags & MACHINE_NO_STANDALONE) != 0)
+ continue;
+
+ // pick the best match between driver name and description
+ int curpenalty = penalty_compare(string, s_drivers_sorted[index]->description);
+ int tmp = penalty_compare(string, s_drivers_sorted[index]->name);
+ curpenalty = MIN(curpenalty, tmp);
+
+ // insert into the sorted table of matches
+ for (int matchnum = count - 1; matchnum >= 0; matchnum--)
+ {
+ // stop if we're worse than the current entry
+ if (curpenalty >= penalty[matchnum])
+ break;
+
+ // as long as this isn't the last entry, bump this one down
+ if (matchnum < count - 1)
+ {
+ penalty[matchnum + 1] = penalty[matchnum];
+ results[matchnum + 1] = results[matchnum];
+ }
+ results[matchnum] = index;
+ penalty[matchnum] = curpenalty;
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// release_current - release bulky memory
+// structures from the current entry because
+// we're done with it
+//-------------------------------------------------
+
+void driver_enumerator::release_current()
+{
+ // skip if no current entry
+ if (m_current < 0 || m_current >= s_driver_count)
+ return;
+
+ // skip if we haven't cached a config
+ if (m_config[m_current] == NULL)
+ return;
+
+ // iterate over software lists in this entry and reset
+ software_list_device_iterator deviter(m_config[m_current]->root_device());
+ for (software_list_device *swlistdev = deviter.first(); swlistdev != NULL; swlistdev = deviter.next())
+ swlistdev->release();
+}