mirror of
https://github.com/holub/mame
synced 2025-04-25 09:50:04 +03:00
Cleaned up save/load state menu code.
* Fixed display names for states saved with keyboard keys * Wait for at least one frame with no keys pressed before saving/loading.
This commit is contained in:
parent
90beaec1c6
commit
f5fb44a9db
@ -9,13 +9,14 @@
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef MAME_FRONTEND_UI_MENUITEM_H
|
||||
#define MAME_FRONTEND_UI_MENUITEM_H
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
namespace ui {
|
||||
|
||||
// special menu item for separators
|
||||
#define MENU_SEPARATOR_ITEM "---"
|
||||
|
||||
@ -38,7 +39,7 @@ public:
|
||||
|
||||
std::string text;
|
||||
std::string subtext;
|
||||
uint32_t flags;
|
||||
uint32_t flags;
|
||||
void *ref;
|
||||
menu_item_type type; // item type (eventually will go away when itemref is proper ui_menu_item class rather than void*)
|
||||
};
|
||||
|
@ -10,7 +10,9 @@
|
||||
|
||||
#include "emu.h"
|
||||
#include "ui/state.h"
|
||||
|
||||
#include "emuopts.h"
|
||||
#include "inputdev.h"
|
||||
|
||||
|
||||
namespace ui {
|
||||
@ -21,20 +23,6 @@ namespace ui {
|
||||
|
||||
namespace {
|
||||
|
||||
const int MAX_SAVED_STATE_JOYSTICK = 4;
|
||||
|
||||
//-------------------------------------------------
|
||||
// keyboard_code
|
||||
//-------------------------------------------------
|
||||
|
||||
input_code keyboard_code(input_item_id id)
|
||||
{
|
||||
// only supported for A-Z|0-9
|
||||
assert((id >= ITEM_ID_A && id <= ITEM_ID_Z) || (id >= ITEM_ID_0 && id <= ITEM_ID_9));
|
||||
return input_code(DEVICE_CLASS_KEYBOARD, 0, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, id);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// keyboard_input_item_name
|
||||
//-------------------------------------------------
|
||||
@ -57,20 +45,15 @@ std::string keyboard_input_item_name(input_item_id id)
|
||||
|
||||
std::pair<std::string, std::string> code_item_pair(const running_machine &machine, input_item_id id)
|
||||
{
|
||||
// identify the input code name (translated appropriately)
|
||||
input_code code = keyboard_code(id);
|
||||
std::string code_name = machine.input().code_name(code);
|
||||
strmakelower(code_name);
|
||||
// only supported for A-Z|0-9
|
||||
assert((id >= ITEM_ID_A && id <= ITEM_ID_Z) || (id >= ITEM_ID_0 && id <= ITEM_ID_9));
|
||||
input_code const code = input_code(DEVICE_CLASS_KEYBOARD, 0, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, id);
|
||||
|
||||
// identify the keyboard item name
|
||||
std::string input_item_name = keyboard_input_item_name(id);
|
||||
|
||||
// return them
|
||||
return std::make_pair(code_name, input_item_name);
|
||||
return std::make_pair(keyboard_input_item_name(id), machine.input().code_name(code));
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
};
|
||||
|
||||
/***************************************************************************
|
||||
FILE ENTRY
|
||||
@ -104,8 +87,8 @@ menu_load_save_state_base::menu_load_save_state_base(mame_ui_manager &mui, rende
|
||||
, m_header(header)
|
||||
, m_footer(footer)
|
||||
, m_must_exist(must_exist)
|
||||
, m_pause_checked(false)
|
||||
, m_was_paused(false)
|
||||
, m_keys_released(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -139,11 +122,33 @@ void menu_load_save_state_base::populate(float &customtop, float &custombottom)
|
||||
m_filename_to_code_map.emplace(code_item_pair(machine(), id));
|
||||
for (input_item_id id = ITEM_ID_0; id <= ITEM_ID_9; id++)
|
||||
m_filename_to_code_map.emplace(code_item_pair(machine(), id));
|
||||
|
||||
// do joysticks
|
||||
input_class const &sticks = machine().input().device_class(DEVICE_CLASS_JOYSTICK);
|
||||
if (sticks.enabled())
|
||||
{
|
||||
for (int i = 0; sticks.maxindex() >= i; ++i)
|
||||
{
|
||||
input_device const *const stick = sticks.device(i);
|
||||
if (stick)
|
||||
{
|
||||
for (input_item_id j = ITEM_ID_BUTTON1; (ITEM_ID_BUTTON32 >= j) && (stick->maxitem() >= j); ++j)
|
||||
{
|
||||
input_device_item const *const item = stick->item(j);
|
||||
if (item && (item->itemclass() == ITEM_CLASS_SWITCH))
|
||||
{
|
||||
m_filename_to_code_map.emplace(
|
||||
util::string_format("joy%i-%i", i, j - ITEM_ID_BUTTON1 + 1),
|
||||
machine().input().code_name(item->code()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// open the state directory
|
||||
std::string statedir = state_directory();
|
||||
osd::directory::ptr dir = osd::directory::open(statedir);
|
||||
osd::directory::ptr dir = osd::directory::open(state_directory());
|
||||
|
||||
// create a separate vector, so we can add sorted entries to the menu
|
||||
std::vector<const file_entry *> m_entries_vec;
|
||||
@ -173,12 +178,12 @@ void menu_load_save_state_base::populate(float &customtop, float &custombottom)
|
||||
|
||||
// sort the vector; put recently modified state files at the top
|
||||
std::sort(
|
||||
m_entries_vec.begin(),
|
||||
m_entries_vec.end(),
|
||||
[](const file_entry *a, const file_entry *b)
|
||||
{
|
||||
return a->last_modified() > b->last_modified();
|
||||
});
|
||||
m_entries_vec.begin(),
|
||||
m_entries_vec.end(),
|
||||
[] (const file_entry *a, const file_entry *b)
|
||||
{
|
||||
return a->last_modified() > b->last_modified();
|
||||
});
|
||||
|
||||
// add the entries
|
||||
for (const file_entry *entry : m_entries_vec)
|
||||
@ -190,11 +195,11 @@ void menu_load_save_state_base::populate(float &customtop, float &custombottom)
|
||||
|
||||
// format the text
|
||||
std::string text = util::string_format("%s: %s",
|
||||
entry->visible_name(),
|
||||
time_string);
|
||||
entry->visible_name(),
|
||||
time_string);
|
||||
|
||||
// append the menu item
|
||||
void *itemref = itemref_from_file_entry(*entry);
|
||||
void *const itemref = itemref_from_file_entry(*entry);
|
||||
item_append(std::move(text), std::string(), 0, itemref);
|
||||
|
||||
// is this item selected?
|
||||
@ -202,18 +207,25 @@ void menu_load_save_state_base::populate(float &customtop, float &custombottom)
|
||||
set_selection(itemref);
|
||||
}
|
||||
|
||||
if (m_entries_vec.empty())
|
||||
{
|
||||
item_append(_("No save states found"), std::string(), 0, nullptr);
|
||||
set_selection(nullptr);
|
||||
}
|
||||
item_append(menu_item_type::SEPARATOR);
|
||||
|
||||
// set up custom render proc
|
||||
customtop = ui().get_line_height() + 3.0f * ui().box_tb_border();
|
||||
custombottom = ui().get_line_height() + 3.0f * ui().box_tb_border();
|
||||
|
||||
// pause if appropriate
|
||||
if (!m_pause_checked)
|
||||
{
|
||||
m_was_paused = machine().paused();
|
||||
if (!m_was_paused)
|
||||
machine().pause();
|
||||
m_pause_checked = true;
|
||||
}
|
||||
m_was_paused = machine().paused();
|
||||
if (!m_was_paused)
|
||||
machine().pause();
|
||||
|
||||
// get ready to poll inputs
|
||||
machine().input().reset_polling();
|
||||
m_keys_released = false;
|
||||
}
|
||||
|
||||
|
||||
@ -224,14 +236,17 @@ void menu_load_save_state_base::populate(float &customtop, float &custombottom)
|
||||
void menu_load_save_state_base::handle()
|
||||
{
|
||||
// process the menu
|
||||
const event *event = process(0);
|
||||
event const *const event = process(0);
|
||||
|
||||
// process the event
|
||||
if (event && (event->iptkey == IPT_UI_SELECT))
|
||||
{
|
||||
// user selected one of the entries
|
||||
const file_entry &entry = file_entry_from_itemref(event->itemref);
|
||||
slot_selected(std::string(entry.file_name()));
|
||||
if (event->itemref)
|
||||
{
|
||||
// user selected one of the entries
|
||||
file_entry const &entry = file_entry_from_itemref(event->itemref);
|
||||
slot_selected(std::string(entry.file_name()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -249,12 +264,9 @@ void menu_load_save_state_base::handle()
|
||||
|
||||
std::string menu_load_save_state_base::get_visible_name(const std::string &file_name)
|
||||
{
|
||||
if (file_name.size() == 1)
|
||||
{
|
||||
auto iter = m_filename_to_code_map.find(file_name);
|
||||
if (iter != m_filename_to_code_map.end())
|
||||
return iter->second;
|
||||
}
|
||||
auto const iter = m_filename_to_code_map.find(file_name);
|
||||
if (iter != m_filename_to_code_map.end())
|
||||
return iter->second;
|
||||
|
||||
// otherwise these are the same
|
||||
return file_name;
|
||||
@ -267,28 +279,22 @@ std::string menu_load_save_state_base::get_visible_name(const std::string &file_
|
||||
|
||||
std::string menu_load_save_state_base::poll_inputs()
|
||||
{
|
||||
// poll A-Z
|
||||
for (input_item_id id = ITEM_ID_A; id <= ITEM_ID_Z; id++)
|
||||
input_code const code = machine().input().poll_switches();
|
||||
if (INPUT_CODE_INVALID == code)
|
||||
{
|
||||
if (machine().input().code_pressed_once(keyboard_code(id)))
|
||||
return keyboard_input_item_name(id);
|
||||
m_keys_released = true;
|
||||
}
|
||||
|
||||
// poll 0-9
|
||||
for (input_item_id id = ITEM_ID_0; id <= ITEM_ID_9; id++)
|
||||
else if (m_keys_released)
|
||||
{
|
||||
if (machine().input().code_pressed_once(keyboard_code(id)))
|
||||
input_item_id const id = code.item_id();
|
||||
|
||||
// keyboard A-Z and 0-9
|
||||
if (((ITEM_ID_A <= id) && (ITEM_ID_Z >= id)) || ((ITEM_ID_0 <= id) && (ITEM_ID_9 >= id)))
|
||||
return keyboard_input_item_name(id);
|
||||
}
|
||||
|
||||
// poll joysticks
|
||||
for (int joy_index = 0; joy_index <= MAX_SAVED_STATE_JOYSTICK; joy_index++)
|
||||
{
|
||||
for (input_item_id id = ITEM_ID_BUTTON1; id <= ITEM_ID_BUTTON32; ++id)
|
||||
{
|
||||
if (machine().input().code_pressed_once(input_code(DEVICE_CLASS_JOYSTICK, joy_index, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, id)))
|
||||
return util::string_format("joy%i-%i", joy_index, id - ITEM_ID_BUTTON1 + 1);
|
||||
}
|
||||
// joystick buttons
|
||||
if ((DEVICE_CLASS_JOYSTICK == code.device_class()) && (ITEM_CLASS_SWITCH == code.item_class()) && (ITEM_MODIFIER_NONE == code.item_modifier()) && (ITEM_ID_BUTTON1 <= id) && (ITEM_ID_BUTTON32 >= id))
|
||||
return util::string_format("joy%i-%i", code.device_index(), id - ITEM_ID_BUTTON1 + 1);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@ -328,7 +334,16 @@ void menu_load_save_state_base::slot_selected(std::string &&name)
|
||||
|
||||
void menu_load_save_state_base::custom_render(void *selectedref, float top, float bottom, float origx1, float origy1, float origx2, float origy2)
|
||||
{
|
||||
extra_text_render(top, bottom, origx1, origy1, origx2, origy2, m_header, m_footer);
|
||||
extra_text_render(top, bottom, origx1, origy1, origx2, origy2, m_header, nullptr);
|
||||
if (m_footer)
|
||||
{
|
||||
char const *const text[] = { m_footer };
|
||||
draw_text_box(
|
||||
std::begin(text), std::end(text),
|
||||
origx1, origx2, origy2 + ui().box_tb_border(), origy2 + bottom,
|
||||
ui::text_layout::CENTER, ui::text_layout::NEVER, false,
|
||||
ui().colors().text_color(), ui().colors().background_color(), 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -360,9 +375,9 @@ std::string menu_load_save_state_base::state_directory() const
|
||||
{
|
||||
const char *stateopt = machine().options().state_name();
|
||||
return util::string_format("%s%s%s",
|
||||
machine().options().state_directory(),
|
||||
PATH_SEPARATOR,
|
||||
machine().get_statename(stateopt));
|
||||
machine().options().state_directory(),
|
||||
PATH_SEPARATOR,
|
||||
machine().get_statename(stateopt));
|
||||
}
|
||||
|
||||
|
||||
@ -385,7 +400,7 @@ bool menu_load_save_state_base::is_present(const std::string &name) const
|
||||
//-------------------------------------------------
|
||||
|
||||
menu_load_state::menu_load_state(mame_ui_manager &mui, render_container &container)
|
||||
: menu_load_save_state_base(mui, container, _("Load State"), _("Select position to load from"), true)
|
||||
: menu_load_save_state_base(mui, container, _("Load State"), _("Select state to load"), true)
|
||||
{
|
||||
}
|
||||
|
||||
@ -409,7 +424,7 @@ void menu_load_state::process_file(std::string &&file_name)
|
||||
//-------------------------------------------------
|
||||
|
||||
menu_save_state::menu_save_state(mame_ui_manager &mui, render_container &container)
|
||||
: menu_load_save_state_base(mui, container, _("Save State"), _("Select position to save to"), false)
|
||||
: menu_load_save_state_base(mui, container, _("Save State"), _("Press a key or joystick button, or select state to overwrite"), false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -7,14 +7,16 @@
|
||||
Menus for saving and loading state
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef MAME_FRONTEND_UI_STATE_H
|
||||
#define MAME_FRONTEND_UI_STATE_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ui/menu.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace ui {
|
||||
|
||||
// ======================> menu_load_save_state_base
|
||||
@ -54,14 +56,15 @@ private:
|
||||
|
||||
std::unordered_map<std::string, file_entry> m_file_entries;
|
||||
std::unordered_map<std::string, std::string> m_filename_to_code_map;
|
||||
const char * m_header;
|
||||
const char * m_footer;
|
||||
bool m_must_exist;
|
||||
bool m_pause_checked;
|
||||
char const *const m_header;
|
||||
char const *const m_footer;
|
||||
bool const m_must_exist;
|
||||
bool m_was_paused;
|
||||
bool m_keys_released;
|
||||
|
||||
static void *itemref_from_file_entry(const file_entry &entry);
|
||||
static const file_entry &file_entry_from_itemref(void *itemref);
|
||||
|
||||
void try_select_slot(std::string &&name);
|
||||
void slot_selected(std::string &&name);
|
||||
std::string state_directory() const;
|
||||
@ -92,6 +95,6 @@ protected:
|
||||
virtual void process_file(std::string &&file_name) override;
|
||||
};
|
||||
|
||||
};
|
||||
} // namespace ui
|
||||
|
||||
#endif // MAME_FRONTEND_UI_STATE_H
|
||||
|
Loading…
Reference in New Issue
Block a user