mirror of
https://github.com/holub/mame
synced 2025-06-06 12:53:46 +03:00
Revert "state: Turns the current state save feature to a menu [Nathan Woods]"
This breaks saving a state from a joystick button, e.g. the joy%i-%i.sta states, which are rather useful when you don't have a keyboard handy.
This commit is contained in:
parent
472be8055f
commit
cb29a590d0
@ -159,8 +159,6 @@ files {
|
||||
MAME_DIR .. "src/frontend/mame/ui/sndmenu.cpp",
|
||||
MAME_DIR .. "src/frontend/mame/ui/sndmenu.h",
|
||||
MAME_DIR .. "src/frontend/mame/ui/starimg.ipp",
|
||||
MAME_DIR .. "src/frontend/mame/ui/state.cpp",
|
||||
MAME_DIR .. "src/frontend/mame/ui/state.h",
|
||||
MAME_DIR .. "src/frontend/mame/ui/toolbar.ipp",
|
||||
MAME_DIR .. "src/frontend/mame/ui/utils.cpp",
|
||||
MAME_DIR .. "src/frontend/mame/ui/utils.h",
|
||||
|
@ -1,330 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Nathan Woods
|
||||
/***************************************************************************
|
||||
|
||||
ui/state.cpp
|
||||
|
||||
Menus for saving and loading state
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#include "emu.h"
|
||||
#include "machine.h"
|
||||
#include "emuopts.h"
|
||||
#include "ui/state.h"
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
ANONYMOUS NAMESPACE
|
||||
***************************************************************************/
|
||||
|
||||
namespace {
|
||||
|
||||
//-------------------------------------------------
|
||||
// is_valid_state_char - is the specified character
|
||||
// a valid state filename character?
|
||||
//-------------------------------------------------
|
||||
|
||||
bool is_valid_state_char(char32_t ch)
|
||||
{
|
||||
return uchar_is_printable(ch) && osd_is_valid_filename_char(ch);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// get_entry_char
|
||||
//-------------------------------------------------
|
||||
|
||||
char32_t get_entry_char(const osd::directory::entry &entry)
|
||||
{
|
||||
char32_t result = 0;
|
||||
|
||||
// first, is this a file that ends with *.sta?
|
||||
if (entry.type == osd::directory::entry::entry_type::FILE
|
||||
&& core_filename_ends_with(entry.name, ".sta"))
|
||||
{
|
||||
std::string basename = core_filename_extract_base(entry.name);
|
||||
|
||||
|
||||
char32_t ch;
|
||||
if (uchar_from_utf8(&ch, basename.c_str(), basename.length() == basename.length()) && is_valid_state_char(ch))
|
||||
result = ch;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
namespace ui {
|
||||
|
||||
/***************************************************************************
|
||||
FILE ENTRY
|
||||
***************************************************************************/
|
||||
|
||||
char32_t menu_load_save_state_base::s_last_file_selected;
|
||||
|
||||
//-------------------------------------------------
|
||||
// file_entry ctor
|
||||
//-------------------------------------------------
|
||||
|
||||
menu_load_save_state_base::file_entry::file_entry(char32_t entry_char, const std::chrono::system_clock::time_point &last_modified)
|
||||
: m_entry_char(entry_char)
|
||||
, m_last_modified(last_modified)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
BASE CLASS FOR LOAD AND SAVE
|
||||
***************************************************************************/
|
||||
|
||||
//-------------------------------------------------
|
||||
// ctor
|
||||
//-------------------------------------------------
|
||||
|
||||
menu_load_save_state_base::menu_load_save_state_base(mame_ui_manager &mui, render_container &container, const char *header, const char *footer, bool must_exist)
|
||||
: menu(mui, container)
|
||||
, m_header(header)
|
||||
, m_footer(footer)
|
||||
, m_must_exist(must_exist)
|
||||
, m_was_paused(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// dtor
|
||||
//-------------------------------------------------
|
||||
|
||||
menu_load_save_state_base::~menu_load_save_state_base()
|
||||
{
|
||||
// resume if appropriate (is the destructor really the right place
|
||||
// to do this sort of activity?)
|
||||
if (!m_was_paused)
|
||||
machine().resume();
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// populate
|
||||
//-------------------------------------------------
|
||||
|
||||
void menu_load_save_state_base::populate(float &customtop, float &custombottom)
|
||||
{
|
||||
// open the state directory
|
||||
std::string statedir = state_directory();
|
||||
osd::directory::ptr dir = osd::directory::open(statedir);
|
||||
|
||||
// create a separate vector, so we can add sorted entries to the menu
|
||||
std::vector<const file_entry *> m_entries_vec;
|
||||
|
||||
// populate all file entries
|
||||
m_file_entries.clear();
|
||||
if (dir)
|
||||
{
|
||||
const osd::directory::entry *entry;
|
||||
while ((entry = dir->read()) != nullptr)
|
||||
{
|
||||
char32_t entry_char = get_entry_char(*entry);
|
||||
if (entry_char)
|
||||
{
|
||||
if (core_filename_ends_with(entry->name, ".sta"))
|
||||
{
|
||||
file_entry fileent(entry_char, entry->last_modified);
|
||||
auto iter = m_file_entries.emplace(std::make_pair(entry_char, std::move(fileent))).first;
|
||||
m_entries_vec.push_back(&iter->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sort the vector; put recently modified state files at the top
|
||||
std::sort(
|
||||
m_entries_vec.begin(),
|
||||
m_entries_vec.end(),
|
||||
[this](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)
|
||||
{
|
||||
// get the time as a local time string
|
||||
char time_string[128];
|
||||
auto last_modified_time_t = std::chrono::system_clock::to_time_t(entry->last_modified());
|
||||
std::strftime(time_string, sizeof(time_string), "%c", std::localtime(&last_modified_time_t));
|
||||
|
||||
// format the text
|
||||
std::string text = util::string_format("%s: %s",
|
||||
utf8_from_uchar(entry->entry_char()),
|
||||
time_string);
|
||||
|
||||
// append the menu item
|
||||
void *itemref = itemref_from_file_entry(*entry);
|
||||
item_append(std::move(text), std::string(), 0, itemref);
|
||||
|
||||
// is this item selected?
|
||||
if (entry->entry_char() == s_last_file_selected)
|
||||
set_selection(itemref);
|
||||
}
|
||||
|
||||
// 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
|
||||
m_was_paused = machine().paused();
|
||||
if (!m_was_paused)
|
||||
machine().pause();
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// handle
|
||||
//-------------------------------------------------
|
||||
|
||||
void menu_load_save_state_base::handle()
|
||||
{
|
||||
// process the menu
|
||||
const event *event = process(0);
|
||||
|
||||
// process the event
|
||||
if ((event != nullptr) && (event->iptkey == IPT_UI_SELECT))
|
||||
{
|
||||
// user selected one of the entries
|
||||
const file_entry &entry = file_entry_from_itemref(event->itemref);
|
||||
slot_selected(entry.entry_char());
|
||||
}
|
||||
else if ((event != nullptr) && (event->iptkey == IPT_SPECIAL)
|
||||
&& is_valid_state_char(event->unichar)
|
||||
&& (!m_must_exist || is_present(event->unichar)))
|
||||
{
|
||||
// user pressed a shortcut key
|
||||
slot_selected(event->unichar);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// slot_selected
|
||||
//-------------------------------------------------
|
||||
|
||||
void menu_load_save_state_base::slot_selected(char32_t entry_char)
|
||||
{
|
||||
// handle it
|
||||
process_file(utf8_from_uchar(entry_char));
|
||||
|
||||
// record the last slot touched
|
||||
s_last_file_selected = entry_char;
|
||||
|
||||
// no matter what, pop out
|
||||
menu::stack_pop(machine());
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// custom_render - perform our special rendering
|
||||
//-------------------------------------------------
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// itemref_from_file_entry
|
||||
//-------------------------------------------------
|
||||
|
||||
void *menu_load_save_state_base::itemref_from_file_entry(const menu_load_save_state_base::file_entry &entry)
|
||||
{
|
||||
return (void *)&entry;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// file_entry_from_itemref
|
||||
//-------------------------------------------------
|
||||
|
||||
const menu_load_save_state_base::file_entry &menu_load_save_state_base::file_entry_from_itemref(void *itemref)
|
||||
{
|
||||
return *((const file_entry *)itemref);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// is_present
|
||||
//-------------------------------------------------
|
||||
|
||||
std::string menu_load_save_state_base::state_directory() const
|
||||
{
|
||||
return util::string_format("%s%s%s",
|
||||
machine().options().state_directory(),
|
||||
PATH_SEPARATOR,
|
||||
machine().system().name);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// is_present
|
||||
//-------------------------------------------------
|
||||
|
||||
bool menu_load_save_state_base::is_present(char32_t entry_char) const
|
||||
{
|
||||
return m_file_entries.find(entry_char) != m_file_entries.end();
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
LOAD STATE
|
||||
***************************************************************************/
|
||||
|
||||
//-------------------------------------------------
|
||||
// ctor
|
||||
//-------------------------------------------------
|
||||
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// process_file
|
||||
//-------------------------------------------------
|
||||
|
||||
void menu_load_state::process_file(std::string &&file_name)
|
||||
{
|
||||
machine().schedule_load(std::move(file_name));
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
SAVE STATE
|
||||
***************************************************************************/
|
||||
|
||||
//-------------------------------------------------
|
||||
// ctor
|
||||
//-------------------------------------------------
|
||||
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// process_file
|
||||
//-------------------------------------------------
|
||||
|
||||
void menu_save_state::process_file(std::string &&file_name)
|
||||
{
|
||||
machine().schedule_save(std::move(file_name));
|
||||
}
|
||||
|
||||
|
||||
} // namespace ui
|
@ -1,90 +0,0 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Nathan Woods
|
||||
/***************************************************************************
|
||||
|
||||
ui/state.h
|
||||
|
||||
Menus for saving and loading state
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef MAME_FRONTEND_UI_STATE_H
|
||||
#define MAME_FRONTEND_UI_STATE_H
|
||||
|
||||
#include "ui/menu.h"
|
||||
|
||||
namespace ui {
|
||||
|
||||
// ======================> menu_load_save_state_base
|
||||
|
||||
class menu_load_save_state_base : public menu
|
||||
{
|
||||
public:
|
||||
virtual ~menu_load_save_state_base() override;
|
||||
virtual void populate(float &customtop, float &custombottom) override;
|
||||
virtual void handle() override;
|
||||
virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override;
|
||||
|
||||
protected:
|
||||
menu_load_save_state_base(mame_ui_manager &mui, render_container &container, const char *header, const char *footer, bool must_exist);
|
||||
virtual void process_file(std::string &&file_name) = 0;
|
||||
|
||||
private:
|
||||
class file_entry
|
||||
{
|
||||
public:
|
||||
file_entry() = delete;
|
||||
file_entry(const file_entry &) = delete;
|
||||
file_entry(file_entry &&) = default;
|
||||
file_entry(char32_t entry_char, const std::chrono::system_clock::time_point &last_modified);
|
||||
|
||||
char32_t entry_char() const { return m_entry_char; }
|
||||
const std::chrono::system_clock::time_point &last_modified() const { return m_last_modified; }
|
||||
|
||||
private:
|
||||
char32_t m_entry_char;
|
||||
std::chrono::system_clock::time_point m_last_modified;
|
||||
};
|
||||
|
||||
static char32_t s_last_file_selected;
|
||||
|
||||
std::unordered_map<char32_t, file_entry> m_file_entries;
|
||||
const char * m_header;
|
||||
const char * m_footer;
|
||||
bool m_must_exist;
|
||||
bool m_was_paused;
|
||||
|
||||
static void *itemref_from_file_entry(const file_entry &entry);
|
||||
static const file_entry &file_entry_from_itemref(void *itemref);
|
||||
void slot_selected(char32_t entry_char);
|
||||
std::string state_directory() const;
|
||||
bool is_present(char32_t entry_char) const;
|
||||
};
|
||||
|
||||
// ======================> menu_load_state
|
||||
|
||||
class menu_load_state : public menu_load_save_state_base
|
||||
{
|
||||
public:
|
||||
menu_load_state(mame_ui_manager &mui, render_container &container);
|
||||
|
||||
protected:
|
||||
virtual void process_file(std::string &&file_name) override;
|
||||
};
|
||||
|
||||
// ======================> menu_save_state
|
||||
|
||||
class menu_save_state : public menu_load_save_state_base
|
||||
{
|
||||
public:
|
||||
menu_save_state(mame_ui_manager &mui, render_container &container);
|
||||
|
||||
protected:
|
||||
virtual void process_file(std::string &&file_name) override;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif // MAME_FRONTEND_UI_STATE_H
|
@ -27,7 +27,6 @@
|
||||
#include "ui/mainmenu.h"
|
||||
#include "ui/filemngr.h"
|
||||
#include "ui/sliders.h"
|
||||
#include "ui/state.h"
|
||||
#include "ui/viewgfx.h"
|
||||
#include "imagedev/cassette.h"
|
||||
|
||||
@ -184,7 +183,8 @@ mame_ui_manager::mame_ui_manager(running_machine &machine)
|
||||
, m_show_profiler(false)
|
||||
, m_popup_text_end(0)
|
||||
, m_mouse_arrow_texture(nullptr)
|
||||
, m_mouse_show(false) {}
|
||||
, m_mouse_show(false)
|
||||
, m_load_save_hold(false) {}
|
||||
|
||||
mame_ui_manager::~mame_ui_manager()
|
||||
{
|
||||
@ -988,8 +988,10 @@ void mame_ui_manager::draw_profiler(render_container &container)
|
||||
|
||||
void mame_ui_manager::start_save_state()
|
||||
{
|
||||
show_menu();
|
||||
ui::menu::stack_push<ui::menu_save_state>(*this, machine().render().ui_container());
|
||||
machine().pause();
|
||||
m_load_save_hold = true;
|
||||
using namespace std::placeholders;
|
||||
set_handler(ui_callback_type::GENERAL, std::bind(&mame_ui_manager::handler_load_save, this, _1, uint32_t(LOADSAVE_SAVE)));
|
||||
}
|
||||
|
||||
|
||||
@ -999,8 +1001,10 @@ void mame_ui_manager::start_save_state()
|
||||
|
||||
void mame_ui_manager::start_load_state()
|
||||
{
|
||||
show_menu();
|
||||
ui::menu::stack_push<ui::menu_load_state>(*this, machine().render().ui_container());
|
||||
machine().pause();
|
||||
m_load_save_hold = true;
|
||||
using namespace std::placeholders;
|
||||
set_handler(ui_callback_type::GENERAL, std::bind(&mame_ui_manager::handler_load_save, this, _1, uint32_t(LOADSAVE_LOAD)));
|
||||
}
|
||||
|
||||
|
||||
@ -1265,6 +1269,111 @@ uint32_t mame_ui_manager::handler_ingame(render_container &container)
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// handler_load_save - leads the user through
|
||||
// specifying a game to save or load
|
||||
//-------------------------------------------------
|
||||
|
||||
uint32_t mame_ui_manager::handler_load_save(render_container &container, uint32_t state)
|
||||
{
|
||||
std::string filename;
|
||||
char file = 0;
|
||||
|
||||
// if we're not in the middle of anything, skip
|
||||
if (state == LOADSAVE_NONE)
|
||||
return 0;
|
||||
|
||||
// okay, we're waiting for a key to select a slot; display a message
|
||||
if (state == LOADSAVE_SAVE)
|
||||
draw_message_window(container, _("Select position to save to"));
|
||||
else
|
||||
draw_message_window(container, _("Select position to load from"));
|
||||
|
||||
// if load/save state sequence is still being pressed, do not read the filename yet
|
||||
if (m_load_save_hold) {
|
||||
bool seq_in_progress = false;
|
||||
const input_seq &load_save_seq = state == LOADSAVE_SAVE ?
|
||||
machine().ioport().type_seq(IPT_UI_SAVE_STATE) :
|
||||
machine().ioport().type_seq(IPT_UI_LOAD_STATE);
|
||||
|
||||
for (int i = 0; i < load_save_seq.length(); i++)
|
||||
if (machine().input().code_pressed_once(load_save_seq[i]))
|
||||
seq_in_progress = true;
|
||||
|
||||
if (seq_in_progress)
|
||||
return state;
|
||||
else
|
||||
m_load_save_hold = false;
|
||||
}
|
||||
|
||||
// check for cancel key
|
||||
if (machine().ui_input().pressed(IPT_UI_CANCEL))
|
||||
{
|
||||
// display a popup indicating things were cancelled
|
||||
if (state == LOADSAVE_SAVE)
|
||||
machine().popmessage(_("Save cancelled"));
|
||||
else
|
||||
machine().popmessage(_("Load cancelled"));
|
||||
|
||||
// reset the state
|
||||
machine().resume();
|
||||
return UI_HANDLER_CANCEL;
|
||||
}
|
||||
|
||||
// check for A-Z or 0-9
|
||||
for (input_item_id id = ITEM_ID_A; id <= ITEM_ID_Z; ++id)
|
||||
if (machine().input().code_pressed_once(input_code(DEVICE_CLASS_KEYBOARD, 0, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, id)))
|
||||
file = id - ITEM_ID_A + 'a';
|
||||
if (file == 0)
|
||||
for (input_item_id id = ITEM_ID_0; id <= ITEM_ID_9; ++id)
|
||||
if (machine().input().code_pressed_once(input_code(DEVICE_CLASS_KEYBOARD, 0, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, id)))
|
||||
file = id - ITEM_ID_0 + '0';
|
||||
if (file == 0)
|
||||
for (input_item_id id = ITEM_ID_0_PAD; id <= ITEM_ID_9_PAD; ++id)
|
||||
if (machine().input().code_pressed_once(input_code(DEVICE_CLASS_KEYBOARD, 0, ITEM_CLASS_SWITCH, ITEM_MODIFIER_NONE, id)))
|
||||
file = id - ITEM_ID_0_PAD + '0';
|
||||
if (file == 0)
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
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)))
|
||||
{
|
||||
filename = util::string_format("joy%i-%i", joy_index, id - ITEM_ID_BUTTON1 + 1);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return state;
|
||||
}
|
||||
else
|
||||
{
|
||||
filename = util::string_format("%c", file);
|
||||
}
|
||||
|
||||
// display a popup indicating that the save will proceed
|
||||
if (state == LOADSAVE_SAVE)
|
||||
{
|
||||
machine().popmessage(_("Save to position %s"), filename);
|
||||
machine().schedule_save(std::move(filename));
|
||||
}
|
||||
else
|
||||
{
|
||||
machine().popmessage(_("Load from position %s"), filename);
|
||||
machine().schedule_load(std::move(filename));
|
||||
}
|
||||
|
||||
// avoid handling the name of the save state slot as a seperate input
|
||||
machine().ui_input().mark_all_as_pressed();
|
||||
|
||||
// remove the pause and reset the state
|
||||
machine().resume();
|
||||
return UI_HANDLER_CANCEL;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// request_quit
|
||||
//-------------------------------------------------
|
||||
|
@ -245,6 +245,7 @@ private:
|
||||
std::unique_ptr<uint8_t[]> m_non_char_keys_down;
|
||||
render_texture * m_mouse_arrow_texture;
|
||||
bool m_mouse_show;
|
||||
bool m_load_save_hold;
|
||||
ui_options m_ui_options;
|
||||
|
||||
std::unique_ptr<ui::machine_info> m_machine_info;
|
||||
|
Loading…
Reference in New Issue
Block a user