summaryrefslogtreecommitdiffstats
path: root/docs/release/src/frontend/mame/ui/inifile.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'docs/release/src/frontend/mame/ui/inifile.cpp')
-rw-r--r--docs/release/src/frontend/mame/ui/inifile.cpp583
1 files changed, 583 insertions, 0 deletions
diff --git a/docs/release/src/frontend/mame/ui/inifile.cpp b/docs/release/src/frontend/mame/ui/inifile.cpp
new file mode 100644
index 00000000000..02713cd5f57
--- /dev/null
+++ b/docs/release/src/frontend/mame/ui/inifile.cpp
@@ -0,0 +1,583 @@
+// license:BSD-3-Clause
+// copyright-holders:Maurizio Petrarota, Vas Crabb
+/***************************************************************************
+
+ ui/inifile.cpp
+
+ UI INIs file manager.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "ui/inifile.h"
+
+#include "ui/moptions.h"
+
+#include "language.h"
+
+#include "drivenum.h"
+#include "softlist_dev.h"
+
+#include "corestr.h"
+
+#include <algorithm>
+#include <cstring>
+#include <iterator>
+
+
+namespace {
+
+char const FAVORITE_FILENAME[] = "favorites.ini";
+
+} // anonymous namespace
+
+
+//-------------------------------------------------
+// ctor
+//-------------------------------------------------
+
+inifile_manager::inifile_manager(ui_options &options)
+ : m_options(options)
+ , m_ini_index()
+{
+ // scan directories and create index
+ file_enumerator path(m_options.categoryini_path());
+ for (osd::directory::entry const *dir = path.next(); dir; dir = path.next())
+ {
+ std::string name(dir->name);
+ if (core_filename_ends_with(name, ".ini"))
+ {
+ emu_file file(m_options.categoryini_path(), OPEN_FLAG_READ);
+ if (!file.open(name))
+ {
+ init_category(std::move(name), file);
+ file.close();
+ }
+ }
+ }
+ std::stable_sort(m_ini_index.begin(), m_ini_index.end(), [] (auto const &x, auto const &y) { return 0 > core_stricmp(x.first.c_str(), y.first.c_str()); });
+}
+
+//-------------------------------------------------
+// load and indexing ini files
+//-------------------------------------------------
+
+// HBMAME - rewritten using native file usage because mame's file.gets doesn't always work
+void inifile_manager::load_ini_category(size_t file, size_t category, std::unordered_set<game_driver const *> &result) const
+{
+ std::string const &filename(m_ini_index[file].first);
+ FILE *fp = fopen(filename.c_str(), "r");
+ if (!fp)
+ {
+ osd_printf_error("Failed to open category file %s for reading\n", filename);
+ return;
+ }
+
+ int64_t const offset(m_ini_index[file].second[category].second);
+ if (fseek(fp, offset, SEEK_SET))
+ {
+ fclose(fp);
+ osd_printf_error("Failed to seek to category offset in file %s\n", filename);
+ return;
+ }
+
+ char rbuf[MAX_CHAR_INFO];
+ while (fgets(rbuf, MAX_CHAR_INFO, fp) && rbuf[0] && ('[' != rbuf[0]))
+ {
+ auto const tail(std::find_if(std::begin(rbuf), std::prev(std::end(rbuf)), [] (char ch) { return !ch || ('\r' == ch) || ('\n' == ch); }));
+ *tail = '\0';
+ int const dfind(driver_list::find(rbuf));
+ if (0 <= dfind)
+ result.emplace(&driver_list::driver(dfind));
+ }
+
+ fclose(fp);
+}
+
+//-------------------------------------------------
+// initialize category
+//-------------------------------------------------
+
+// HBMAME - rewritten using native file usage because mame's file.gets doesn't always work
+void inifile_manager::init_category(std::string &&filename, emu_file &file)
+{
+ FILE *fp;
+ fp = fopen(file.fullpath(), "r");
+ if (!fp)
+ return;
+ categoryindex index;
+ char rbuf[MAX_CHAR_INFO];
+ std::string name;
+ while (fgets(rbuf, std::size(rbuf), fp))
+ {
+ if ('[' == rbuf[0])
+ {
+ auto const head(std::next(std::begin(rbuf)));
+ auto const tail(std::find_if(head, std::end(rbuf), [] (char ch) { return !ch || (']' == ch); }));
+ name.assign(head, tail);
+ if ("FOLDER_SETTINGS" != name)
+ index.emplace_back(std::move(name), ftell(fp));
+ }
+ }
+ std::stable_sort(index.begin(), index.end(), [] (auto const &x, auto const &y) { return 0 > core_stricmp(x.first.c_str(), y.first.c_str()); });
+ if (!index.empty())
+ m_ini_index.emplace_back(file.fullpath(), std::move(index));
+
+ fclose(fp);
+}
+
+
+/**************************************************************************
+ FAVORITE MANAGER
+**************************************************************************/
+
+bool favorite_manager::favorite_compare::operator()(ui_software_info const &lhs, ui_software_info const &rhs) const
+{
+ assert(lhs.driver);
+ assert(rhs.driver);
+
+ if (!lhs.startempty)
+ {
+ if (rhs.startempty)
+ return false;
+ else if (lhs.listname < rhs.listname)
+ return true;
+ else if (lhs.listname > rhs.listname)
+ return false;
+ else if (lhs.shortname < rhs.shortname)
+ return true;
+ else if (lhs.shortname > rhs.shortname)
+ return false;
+ }
+ else if (!rhs.startempty)
+ {
+ return true;
+ }
+
+ return 0 > std::strncmp(lhs.driver->name, rhs.driver->name, std::size(lhs.driver->name));
+}
+
+bool favorite_manager::favorite_compare::operator()(ui_software_info const &lhs, game_driver const &rhs) const
+{
+ assert(lhs.driver);
+
+ if (!lhs.startempty)
+ return false;
+ else
+ return 0 > std::strncmp(lhs.driver->name, rhs.name, std::size(rhs.name));
+}
+
+bool favorite_manager::favorite_compare::operator()(game_driver const &lhs, ui_software_info const &rhs) const
+{
+ assert(rhs.driver);
+
+ if (!rhs.startempty)
+ return true;
+ else
+ return 0 > std::strncmp(lhs.name, rhs.driver->name, std::size(lhs.name));
+}
+
+bool favorite_manager::favorite_compare::operator()(ui_software_info const &lhs, running_software_key const &rhs) const
+{
+ assert(lhs.driver);
+ assert(std::get<1>(rhs));
+
+ if (lhs.startempty)
+ return true;
+ else if (lhs.listname < std::get<1>(rhs))
+ return true;
+ else if (lhs.listname > std::get<1>(rhs))
+ return false;
+ else if (lhs.shortname < std::get<2>(rhs))
+ return true;
+ else if (lhs.shortname > std::get<2>(rhs))
+ return false;
+ else
+ return 0 > std::strncmp(lhs.driver->name, std::get<0>(rhs).name, std::size(lhs.driver->name));
+}
+
+bool favorite_manager::favorite_compare::operator()(running_software_key const &lhs, ui_software_info const &rhs) const
+{
+ assert(std::get<1>(lhs));
+ assert(rhs.driver);
+
+ if (rhs.startempty)
+ return false;
+ else if (std::get<1>(lhs) < rhs.listname)
+ return true;
+ else if (std::get<1>(lhs) > rhs.listname)
+ return false;
+ else if (std::get<2>(lhs) < rhs.shortname)
+ return true;
+ else if (std::get<2>(lhs) > rhs.shortname)
+ return false;
+ else
+ return 0 > std::strncmp(std::get<0>(lhs).name, rhs.driver->name, std::size(rhs.driver->name));
+}
+
+
+//-------------------------------------------------
+// construction/destruction
+//-------------------------------------------------
+
+favorite_manager::favorite_manager(ui_options &options)
+ : m_options(options)
+ , m_favorites()
+ , m_sorted()
+ , m_need_sort(true)
+{
+ emu_file file(m_options.ui_path(), OPEN_FLAG_READ);
+ if (!file.open(FAVORITE_FILENAME))
+ {
+ char readbuf[1024];
+ file.gets(readbuf, std::size(readbuf));
+
+ while (readbuf[0] == '[')
+ file.gets(readbuf, std::size(readbuf));
+
+ while (file.gets(readbuf, std::size(readbuf)))
+ {
+ ui_software_info tmpmatches;
+ tmpmatches.shortname = chartrimcarriage(readbuf);
+ file.gets(readbuf, std::size(readbuf));
+ tmpmatches.longname = chartrimcarriage(readbuf);
+ file.gets(readbuf, std::size(readbuf));
+ tmpmatches.parentname = chartrimcarriage(readbuf);
+ file.gets(readbuf, std::size(readbuf));
+ tmpmatches.year = chartrimcarriage(readbuf);
+ file.gets(readbuf, std::size(readbuf));
+ tmpmatches.publisher = chartrimcarriage(readbuf);
+ file.gets(readbuf, std::size(readbuf));
+ tmpmatches.supported = software_support(atoi(readbuf));
+ file.gets(readbuf, std::size(readbuf));
+ tmpmatches.part = chartrimcarriage(readbuf);
+ file.gets(readbuf, std::size(readbuf));
+ chartrimcarriage(readbuf);
+ auto dx = driver_list::find(readbuf);
+ if (0 > dx)
+ continue;
+ tmpmatches.driver = &driver_list::driver(dx);
+ file.gets(readbuf, std::size(readbuf));
+ tmpmatches.listname = chartrimcarriage(readbuf);
+ file.gets(readbuf, std::size(readbuf));
+ tmpmatches.interface = chartrimcarriage(readbuf);
+ file.gets(readbuf, std::size(readbuf));
+ tmpmatches.instance = chartrimcarriage(readbuf);
+ file.gets(readbuf, std::size(readbuf));
+ tmpmatches.startempty = atoi(readbuf);
+ file.gets(readbuf, std::size(readbuf));
+ tmpmatches.parentlongname = chartrimcarriage(readbuf);
+ file.gets(readbuf, std::size(readbuf));
+ //tmpmatches.usage = chartrimcarriage(readbuf); TODO: recover multi-line info
+ file.gets(readbuf, std::size(readbuf));
+ tmpmatches.devicetype = chartrimcarriage(readbuf);
+ file.gets(readbuf, std::size(readbuf));
+ tmpmatches.available = atoi(readbuf);
+
+ // need to populate this, it isn't displayed anywhere else
+ tmpmatches.infotext.append(tmpmatches.longname);
+ tmpmatches.infotext.append(1, '\n');
+ tmpmatches.infotext.append(_("swlist-info", "Software list/item"));
+ tmpmatches.infotext.append(1, '\n');
+ tmpmatches.infotext.append(tmpmatches.listname);
+ tmpmatches.infotext.append(1, ':');
+ tmpmatches.infotext.append(tmpmatches.shortname);
+
+ m_favorites.emplace(std::move(tmpmatches));
+ }
+ file.close();
+ }
+}
+
+
+//-------------------------------------------------
+// add
+//-------------------------------------------------
+
+void favorite_manager::add_favorite_system(game_driver const &driver)
+{
+ add_impl(driver);
+}
+
+void favorite_manager::add_favorite_software(ui_software_info const &swinfo)
+{
+ add_impl(swinfo);
+}
+
+void favorite_manager::add_favorite(running_machine &machine)
+{
+ apply_running_machine(
+ machine,
+ [this, &machine] (game_driver const &driver, device_image_interface *imagedev, software_info const *software, bool &done)
+ {
+ if (imagedev)
+ {
+ // creating this is fairly expensive, but we'll assume this usually succeeds
+ software_part const *const part(imagedev->part_entry());
+ assert(software);
+ assert(part);
+ ui_software_info info(
+ *software,
+ *part,
+ driver,
+ imagedev->software_list_name(),
+ imagedev->instance_name(),
+ strensure(imagedev->image_type_name()));
+
+ // assume it's available if it's mounted
+ info.available = true;
+
+ // look up the parent in the list if necessary (eugh, O(n) walk)
+ if (!info.parentname.empty())
+ {
+ auto const listdev = software_list_device::find_by_name(machine.config(), info.listname);
+ assert(listdev);
+ for (software_info const &other : listdev->get_info())
+ {
+ if (other.shortname() == info.parentname)
+ {
+ info.parentlongname = other.longname();
+ break;
+ }
+ }
+ }
+
+ // hooray for move semantics!
+ add_impl(std::move(info));
+ }
+ else
+ {
+ add_impl(driver);
+ }
+ });
+}
+
+template <typename T> void favorite_manager::add_impl(T &&key)
+{
+ auto const ins(m_favorites.emplace(std::forward<T>(key)));
+ if (ins.second)
+ {
+ if (!m_sorted.empty())
+ m_sorted.emplace_back(std::ref(*ins.first));
+ m_need_sort = true;
+ save_favorites();
+ }
+}
+
+
+//-------------------------------------------------
+// check
+//-------------------------------------------------
+
+bool favorite_manager::is_favorite_system(game_driver const &driver) const
+{
+ return check_impl(driver);
+}
+
+bool favorite_manager::is_favorite_software(ui_software_info const &swinfo) const
+{
+ auto found(m_favorites.lower_bound(swinfo));
+ if ((m_favorites.end() != found) && (found->listname == swinfo.listname) && (found->shortname == swinfo.shortname))
+ return true;
+ else if (m_favorites.begin() == found)
+ return false;
+
+ // need to back up and check for matching software with lexically earlier driver
+ --found;
+ return (found->listname == swinfo.listname) && (found->shortname == swinfo.shortname);
+}
+
+bool favorite_manager::is_favorite(running_machine &machine) const
+{
+ bool result(false);
+ apply_running_machine(
+ machine,
+ [this, &result] (game_driver const &driver, device_image_interface *imagedev, software_info const *software, bool &done)
+ {
+ assert(!result);
+ result = imagedev
+ ? check_impl(running_software_key(driver, imagedev->software_list_name(), software->shortname()))
+ : check_impl(driver);
+ done = done || result;
+ });
+ return result;
+}
+
+bool favorite_manager::is_favorite_system_software(ui_software_info const &swinfo) const
+{
+ return check_impl(swinfo);
+}
+
+template <typename T> bool favorite_manager::check_impl(T const &key) const
+{
+ return m_favorites.find(key) != m_favorites.end();
+}
+
+
+//-------------------------------------------------
+// remove
+//-------------------------------------------------
+
+void favorite_manager::remove_favorite_system(game_driver const &driver)
+{
+ remove_impl(driver);
+}
+
+void favorite_manager::remove_favorite_software(ui_software_info const &swinfo)
+{
+ remove_impl(swinfo);
+}
+
+void favorite_manager::remove_favorite(running_machine &machine)
+{
+ apply_running_machine(
+ machine,
+ [this] (game_driver const &driver, device_image_interface *imagedev, software_info const *software, bool &done)
+ {
+ done = imagedev
+ ? remove_impl(running_software_key(driver, imagedev->software_list_name(), software->shortname()))
+ : remove_impl(driver);
+ });
+}
+
+template <typename T> bool favorite_manager::remove_impl(T const &key)
+{
+ auto const found(m_favorites.find(key));
+ if (m_favorites.end() != found)
+ {
+ m_favorites.erase(found);
+ m_sorted.clear();
+ m_need_sort = true;
+ save_favorites();
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+//-------------------------------------------------
+// implementation
+//-------------------------------------------------
+
+template <typename T>
+void favorite_manager::apply_running_machine(running_machine &machine, T &&action)
+{
+ bool done(false);
+
+ // TODO: this should be changed - it interacts poorly with cartslots on arcade systems
+ if ((machine.system().flags & machine_flags::MASK_TYPE) == machine_flags::TYPE_ARCADE)
+ {
+ action(machine.system(), nullptr, nullptr, done);
+ }
+ else
+ {
+ bool have_software(false);
+ for (device_image_interface &image_dev : image_interface_enumerator(machine.root_device()))
+ {
+ software_info const *const sw(image_dev.software_entry());
+ if (image_dev.exists() && image_dev.loaded_through_softlist() && sw)
+ {
+ assert(image_dev.software_list_name());
+
+ have_software = true;
+ action(machine.system(), &image_dev, sw, done);
+ if (done)
+ return;
+ }
+ }
+
+ if (!have_software)
+ action(machine.system(), nullptr, nullptr, done);
+ }
+}
+
+void favorite_manager::update_sorted()
+{
+ if (m_need_sort)
+ {
+ if (m_sorted.empty())
+ std::copy(m_favorites.begin(), m_favorites.end(), std::back_inserter(m_sorted));
+
+ assert(m_favorites.size() == m_sorted.size());
+ std::stable_sort(
+ m_sorted.begin(),
+ m_sorted.end(),
+ [] (ui_software_info const &lhs, ui_software_info const &rhs) -> bool
+ {
+ assert(lhs.driver);
+ assert(rhs.driver);
+
+ int cmp;
+
+ cmp = core_stricmp(lhs.longname.c_str(), rhs.longname.c_str());
+ if (0 > cmp)
+ return true;
+ else if (0 < cmp)
+ return false;
+
+ cmp = core_stricmp(lhs.driver->type.fullname(), rhs.driver->type.fullname());
+ if (0 > cmp)
+ return true;
+ else if (0 < cmp)
+ return false;
+
+ cmp = std::strcmp(lhs.listname.c_str(), rhs.listname.c_str());
+ if (0 > cmp)
+ return true;
+ else if (0 < cmp)
+ return false;
+
+ return false;
+ });
+
+ m_need_sort = false;
+ }
+}
+
+void favorite_manager::save_favorites()
+{
+ // attempt to open the output file
+ emu_file file(m_options.ui_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
+ if (!file.open(FAVORITE_FILENAME))
+ {
+ if (m_favorites.empty())
+ {
+ // delete it if there are no favorites
+ file.remove_on_close();
+ }
+ else
+ {
+ // generate the favorite INI
+ file.puts("[ROOT_FOLDER]\n[Favorite]\n\n");
+ util::ovectorstream buf;
+ for (ui_software_info const &info : m_favorites)
+ {
+ buf.clear();
+ buf.rdbuf()->clear();
+
+ buf << info.shortname << '\n';
+ buf << info.longname << '\n';
+ buf << info.parentname << '\n';
+ buf << info.year << '\n';
+ buf << info.publisher << '\n';
+ util::stream_format(buf, "%d\n", int(info.supported));
+ buf << info.part << '\n';
+ util::stream_format(buf, "%s\n", info.driver->name);
+ buf << info.listname << '\n';
+ buf << info.interface << '\n';
+ buf << info.instance << '\n';
+ util::stream_format(buf, "%d\n", info.startempty);
+ buf << info.parentlongname << '\n';
+ buf << '\n'; //buf << info.usage << '\n'; TODO: store multi-line info in a recoverable format
+ buf << info.devicetype << '\n';
+ util::stream_format(buf, "%d\n", info.available);
+
+ file.puts(util::buf_to_string_view(buf));
+ }
+ }
+ file.close();
+ }
+}