-emu/ioport.cpp: Fixed "MAME lost my joystick settings!!!1!11!
-plugins: Gave the timecode plugin an option to count emulated frames.
This commit is contained in:
parent
e1863f8c6a
commit
dd43f801a1
@ -12,19 +12,22 @@ local timecode = exports
|
||||
|
||||
function timecode.startplugin()
|
||||
local file -- the timecode log file
|
||||
local enabled -- whether timecode recording is enabled
|
||||
local write -- whether to record a timecode on the next emulated frame
|
||||
local text -- name of current part
|
||||
local frame_count -- emulated frame counter
|
||||
local start_frame -- start frame count for current part
|
||||
local start_time -- start time for current part
|
||||
local total_time -- total time of parts so far this session
|
||||
local count -- current timecode number
|
||||
local show_counter -- whether to show elapsed time since last timecode
|
||||
local show_total -- whether to show the total time of parts
|
||||
|
||||
local frame_mode -- 0 to count frames, 1 to assume 60 Hz
|
||||
local hotkey_seq -- input sequence to record timecode
|
||||
local hotkey_pressed -- whether the hotkey was pressed on the last frame update
|
||||
local hotkey_cfg -- configuration string for the hotkey
|
||||
|
||||
local item_framemode -- menu index of frame mode item
|
||||
local item_hotkey -- menu index of hotkey item
|
||||
local commonui -- common UI helpers
|
||||
local hotkey_poller -- helper for configuring hotkey
|
||||
@ -35,48 +38,114 @@ function timecode.startplugin()
|
||||
end
|
||||
|
||||
|
||||
local function set_default_hotkey()
|
||||
hotkey_seq = manager.machine.input:seq_from_tokens('KEYCODE_F12 NOT KEYCODE_LSHIFT NOT KEYCODE_RSHIFT NOT KEYCODE_LALT NOT KEYCODE_RALT')
|
||||
hotkey_cfg = nil
|
||||
end
|
||||
|
||||
|
||||
local function load_settings()
|
||||
-- set defaults
|
||||
frame_mode = 1
|
||||
set_default_hotkey()
|
||||
|
||||
-- try to open configuration file
|
||||
local cfgname = get_settings_path() .. 'plugin.cfg'
|
||||
local cfgfile = io.open(cfgname, 'r')
|
||||
if not cfgfile then
|
||||
return -- probably harmless, configuration just doesn't exist yet
|
||||
end
|
||||
|
||||
-- parse settings as JSON
|
||||
local json = require('json')
|
||||
local settings = json.parse(cfgfile:read('a'))
|
||||
cfgfile:close()
|
||||
if not settings then
|
||||
emu.print_error(string.format('Error loading timecode recorder settings: error parsing file "%s" as JSON', cfgname))
|
||||
return
|
||||
end
|
||||
|
||||
-- recover frame mode
|
||||
local count_frames = settings.count_frames
|
||||
if count_frames ~= nil then
|
||||
frame_mode = count_frames and 0 or 1
|
||||
end
|
||||
|
||||
-- recover hotkey assignment
|
||||
hotkey_cfg = settings.hotkey
|
||||
if hotkey_cfg then
|
||||
local seq = manager.machine.input:seq_from_tokens(hotkey_cfg)
|
||||
if seq then
|
||||
hotkey_seq = seq
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function save_settings()
|
||||
local path = get_settings_path()
|
||||
local attr = lfs.attributes(path)
|
||||
if not attr then
|
||||
lfs.mkdir(path)
|
||||
elseif attr.mode ~= 'directory' then
|
||||
emu.print_error(string.format('Error saving timecode recorder settings: "%s" is not a directory', path))
|
||||
return
|
||||
end
|
||||
local json = require('json')
|
||||
local settings = { count_frames = frame_mode == 0 }
|
||||
if hotkey_cfg then
|
||||
settings.hotkey = hotkey_cfg
|
||||
end
|
||||
local data = json.stringify(settings, { indent = true })
|
||||
local cfgname = path .. 'plugin.cfg'
|
||||
local cfgfile = io.open(cfgname, 'w')
|
||||
if not cfgfile then
|
||||
emu.print_error(string.format('Error saving timecode recorder settings: error opening file "%s" for writing', cfgname))
|
||||
return
|
||||
end
|
||||
cfgfile:write(data)
|
||||
cfgfile:close()
|
||||
end
|
||||
|
||||
|
||||
local function process_frame()
|
||||
if (not manager.machine.paused) and file and write then
|
||||
if (not file) or manager.machine.paused then
|
||||
return
|
||||
end
|
||||
if write then
|
||||
write = false
|
||||
count = count + 1
|
||||
show_total = true
|
||||
|
||||
-- milliseconds from beginning of playback
|
||||
-- time from beginning of playback in milliseconds, HH:MM:SS.fff and frames
|
||||
local curtime = manager.machine.time
|
||||
local cursec = curtime.seconds
|
||||
local msec_start = (cursec * 1000) + curtime.msec
|
||||
local sec_start = curtime.seconds
|
||||
local msec_start = (sec_start * 1000) + curtime.msec
|
||||
local msec_start_str = string.format('%015d', msec_start)
|
||||
|
||||
-- display the timecode
|
||||
local curtime_str = string.format(
|
||||
'%02d:%02d:%02d.%03d',
|
||||
cursec // (60 * 60),
|
||||
(cursec // 60) % 60,
|
||||
cursec % 60,
|
||||
sec_start // (60 * 60),
|
||||
(sec_start // 60) % 60,
|
||||
sec_start % 60,
|
||||
msec_start % 1000)
|
||||
local frame_start_str = string.format('%015d', (frame_mode == 0) and frame_count or (msec_start * 60 // 1000))
|
||||
|
||||
-- milliseconds from previous timecode
|
||||
-- elapsed from previous timecode in milliseconds, HH:MM:SS.fff and frames
|
||||
local elapsed = curtime - start_time
|
||||
local elapsedsec = elapsed.seconds
|
||||
local msec_elapsed = (elapsedsec * 1000) + elapsed.msec
|
||||
local sec_elapsed = elapsed.seconds
|
||||
local msec_elapsed = (sec_elapsed * 1000) + elapsed.msec
|
||||
local msec_elapsed_str = string.format('%015d', msec_elapsed)
|
||||
|
||||
-- elapsed from previous timecode
|
||||
start_time = curtime
|
||||
local elapsed_str = string.format(
|
||||
'%02d:%02d:%02d.%03d',
|
||||
elapsedsec // (60 * 60),
|
||||
(elapsedsec // 60) % 60,
|
||||
elapsedsec % 60,
|
||||
sec_elapsed // (60 * 60),
|
||||
(sec_elapsed // 60) % 60,
|
||||
sec_elapsed % 60,
|
||||
msec_elapsed % 1000)
|
||||
local frame_elapsed_str = string.format('%015d', (frame_mode == 0) and (frame_count - start_frame) or (msec_elapsed * 60 // 1000))
|
||||
|
||||
-- number of frames from beginning of playback
|
||||
-- TODO: should this account for actual frame rate rather than assuming 60fps?
|
||||
local frame_start_str = string.format('%015d', msec_start * 60 // 1000)
|
||||
|
||||
-- number of frames from previous timecode
|
||||
-- TODO: should this account for actual frame rate rather than assuming 60fps?
|
||||
local frame_elapsed_str = string.format('%015d', msec_elapsed * 60 // 1000)
|
||||
-- update start of part
|
||||
start_frame = frame_count
|
||||
start_time = curtime
|
||||
|
||||
local message
|
||||
local key
|
||||
@ -125,6 +194,7 @@ function timecode.startplugin()
|
||||
msec_start_str, msec_elapsed_str,
|
||||
frame_start_str, frame_elapsed_str))
|
||||
end
|
||||
frame_count = frame_count + 1
|
||||
end
|
||||
|
||||
|
||||
@ -147,7 +217,7 @@ function timecode.startplugin()
|
||||
total_str = string.format(_p('plugin-timecode', 'TOTAL %02d:%02d '), (total // 60) % 60, total % 60)
|
||||
machine.render.ui_container:draw_text('left', 0, total_str, 0xf010f010, 0xff000000)
|
||||
end
|
||||
if enabled then
|
||||
if file then
|
||||
local pressed = machine.input:seq_pressed(hotkey_seq)
|
||||
if (not hotkey_pressed) and pressed then
|
||||
write = true
|
||||
@ -158,35 +228,15 @@ function timecode.startplugin()
|
||||
|
||||
|
||||
local function start()
|
||||
hotkey_seq = manager.machine.input:seq_from_tokens('KEYCODE_F12 NOT KEYCODE_LSHIFT NOT KEYCODE_RSHIFT NOT KEYCODE_LALT NOT KEYCODE_RALT')
|
||||
|
||||
-- try to load configuration
|
||||
local cfgname = get_settings_path() .. 'plugin.cfg'
|
||||
local cfgfile = io.open(cfgname, 'r')
|
||||
if cfgfile then
|
||||
local json = require('json')
|
||||
local settings = json.parse(cfgfile:read('a'))
|
||||
cfgfile:close()
|
||||
if not settings then
|
||||
emu.print_error(string.format('Error loading timecode recorder settings: error parsing file "%s" as JSON', cfgname))
|
||||
else
|
||||
hotkey_cfg = settings.hotkey
|
||||
if hotkey_cfg then
|
||||
local seq = manager.machine.input:seq_from_tokens(hotkey_cfg)
|
||||
if seq then
|
||||
hotkey_seq = seq
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
file = nil
|
||||
show_counter = false
|
||||
show_total = false
|
||||
load_settings()
|
||||
|
||||
-- only do timecode recording if we're doing input recording
|
||||
local options = manager.machine.options.entries
|
||||
local filename = options.record:value()
|
||||
enabled = #filename > 0
|
||||
show_counter = false
|
||||
show_total = false
|
||||
if enabled then
|
||||
if #filename > 0 then
|
||||
filename = filename .. '.timecode'
|
||||
emu.print_info(string.format('Record input timecode file: %s', filename))
|
||||
file = emu.file(options.input_directory:value(), 0x0e) -- FIXME: magic number for flags
|
||||
@ -194,10 +244,12 @@ function timecode.startplugin()
|
||||
if openerr then
|
||||
-- TODO: this used to throw a fatal error and log the error description
|
||||
emu.print_error('Failed to open file for input timecode recording')
|
||||
enabled = false
|
||||
file = nil
|
||||
else
|
||||
write = false
|
||||
text = ''
|
||||
frame_count = 0
|
||||
start_frame = 0
|
||||
start_time = emu.attotime()
|
||||
total_time = emu.attotime()
|
||||
count = 0
|
||||
@ -231,27 +283,7 @@ function timecode.startplugin()
|
||||
end
|
||||
|
||||
-- try to save settings
|
||||
local path = get_settings_path()
|
||||
local attr = lfs.attributes(path)
|
||||
if not attr then
|
||||
lfs.mkdir(path)
|
||||
elseif attr.mode ~= 'directory' then
|
||||
emu.print_error(string.format('Error saving timecode recorder settings: "%s" is not a directory', path))
|
||||
return
|
||||
end
|
||||
if hotkey_cfg then
|
||||
local json = require('json')
|
||||
local settings = { hotkey = hotkey_cfg }
|
||||
local data = json.stringify(settings, { indent = true })
|
||||
local cfgname = path .. 'plugin.cfg'
|
||||
local cfgfile = io.open(cfgname, 'w')
|
||||
if not cfgfile then
|
||||
emu.print_error(string.format('Error saving timecode recorder settings: error opening file "%s" for writing', cfgname))
|
||||
return
|
||||
end
|
||||
cfgfile:write(data)
|
||||
cfgfile:close()
|
||||
end
|
||||
save_settings()
|
||||
end
|
||||
|
||||
|
||||
@ -265,12 +297,22 @@ function timecode.startplugin()
|
||||
hotkey_poller = nil
|
||||
return true
|
||||
end
|
||||
elseif (index == item_hotkey) and (event == 'select') then
|
||||
if not commonui then
|
||||
commonui = require('commonui')
|
||||
elseif index == item_framemode then
|
||||
if (event == 'select') or (event == 'left') or (event == 'right') then
|
||||
frame_mode = (frame_mode ~= 0) and 0 or 1
|
||||
return true
|
||||
end
|
||||
elseif index == item_hotkey then
|
||||
if event == 'select' then
|
||||
if not commonui then
|
||||
commonui = require('commonui')
|
||||
end
|
||||
hotkey_poller = commonui.switch_polling_helper()
|
||||
return true
|
||||
elseif event == 'clear' then
|
||||
set_default_hotkey()
|
||||
return true
|
||||
end
|
||||
hotkey_poller = commonui.switch_polling_helper()
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
@ -280,8 +322,14 @@ function timecode.startplugin()
|
||||
local result = { }
|
||||
table.insert(result, { _p('plugin-timecode', 'Timecode Recorder'), '', 'off' })
|
||||
table.insert(result, { '---', '', '' })
|
||||
|
||||
local frame_mode_val = (frame_mode > 0) and _p('plugin-timecode', 'Assume 60 Hz') or _p('plugins-timecode', 'Count emulated frames')
|
||||
table.insert(result, { _p('plugin-timecode', 'Frame numbers'), frame_mode_val, (frame_mode > 0) and 'l' or 'r' })
|
||||
item_framemode = #result
|
||||
|
||||
table.insert(result, { _p('plugin-timecode', 'Hotkey'), manager.machine.input:seq_name(hotkey_seq), hotkey_poller and 'lr' or '' })
|
||||
item_hotkey = #result
|
||||
|
||||
if hotkey_poller then
|
||||
return hotkey_poller:overlay(result)
|
||||
else
|
||||
|
@ -695,7 +695,7 @@ const char *ioport_field::name() const
|
||||
const input_seq &ioport_field::seq(input_seq_type seqtype) const noexcept
|
||||
{
|
||||
// if no live state, return default
|
||||
if (m_live == nullptr)
|
||||
if (!m_live)
|
||||
return defseq(seqtype);
|
||||
|
||||
// if the sequence is the special default code, return the expanded default value
|
||||
@ -736,7 +736,7 @@ void ioport_field::set_defseq(input_seq_type seqtype, const input_seq &newseq)
|
||||
m_seq[seqtype] = newseq;
|
||||
|
||||
// also update live state unless previously customized
|
||||
if (m_live != nullptr && !was_changed)
|
||||
if (m_live && !was_changed)
|
||||
m_live->seq[seqtype] = newseq;
|
||||
}
|
||||
|
||||
@ -875,14 +875,18 @@ std::string ioport_field::key_name(int which) const
|
||||
// settings for the given input field
|
||||
//-------------------------------------------------
|
||||
|
||||
void ioport_field::get_user_settings(user_settings &settings) const noexcept
|
||||
void ioport_field::get_user_settings(user_settings &settings) const
|
||||
{
|
||||
// zap the entire structure
|
||||
settings = user_settings();
|
||||
|
||||
// copy the basics
|
||||
for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
|
||||
{
|
||||
settings.seq[seqtype] = seq(seqtype);
|
||||
if (m_live)
|
||||
settings.cfg[seqtype] = m_live->cfg[seqtype];
|
||||
}
|
||||
|
||||
// if there's a list of settings or we're an adjuster, copy the current value
|
||||
if (!m_settinglist.empty() || m_type == IPT_ADJUSTER)
|
||||
@ -909,16 +913,16 @@ void ioport_field::get_user_settings(user_settings &settings) const noexcept
|
||||
// settings for the given input field
|
||||
//-------------------------------------------------
|
||||
|
||||
void ioport_field::set_user_settings(const user_settings &settings) noexcept
|
||||
void ioport_field::set_user_settings(const user_settings &settings)
|
||||
{
|
||||
// copy the basics
|
||||
for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
|
||||
{
|
||||
const input_seq &defseq = manager().type_seq(m_type, m_player, input_seq_type(seqtype));
|
||||
if (defseq == settings.seq[seqtype])
|
||||
if (settings.seq[seqtype].is_default())
|
||||
m_live->seq[seqtype].set_default();
|
||||
else
|
||||
m_live->seq[seqtype] = settings.seq[seqtype];
|
||||
m_live->cfg[seqtype] = settings.cfg[seqtype];
|
||||
}
|
||||
|
||||
// if there's a list of settings or we're an adjuster, copy the current value
|
||||
@ -1898,7 +1902,21 @@ void ioport_manager::set_type_seq(ioport_type type, int player, input_seq_type s
|
||||
{
|
||||
input_type_entry *const entry = m_type_to_entry[type][player];
|
||||
if (entry)
|
||||
entry->set_seq(seqtype, newseq);
|
||||
{
|
||||
if (newseq.is_default())
|
||||
{
|
||||
entry->set_seq(seqtype, entry->defseq(seqtype));
|
||||
entry->set_cfg(seqtype, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
entry->set_seq(seqtype, newseq);
|
||||
if (!newseq.length())
|
||||
entry->set_cfg(seqtype, "NONE");
|
||||
else
|
||||
entry->set_cfg(seqtype, machine().input().seq_to_tokens(newseq));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2086,9 +2104,12 @@ void ioport_manager::load_config(config_type cfg_type, config_level cfg_level, u
|
||||
int type = token_to_input_type(portnode->get_attribute_string("type", ""), player);
|
||||
|
||||
// initialize sequences to invalid defaults
|
||||
input_seq newseq[SEQ_TYPE_TOTAL];
|
||||
for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
|
||||
newseq[seqtype].set(INPUT_CODE_INVALID);
|
||||
std::pair<input_seq, char const *> newseq[SEQ_TYPE_TOTAL];
|
||||
for (auto &seq : newseq)
|
||||
{
|
||||
seq.first.set(INPUT_CODE_INVALID);
|
||||
seq.second = "";
|
||||
}
|
||||
|
||||
// loop over new sequences
|
||||
for (util::xml::data_node const *seqnode = portnode->get_child("newseq"); seqnode; seqnode = seqnode->get_next_sibling("newseq"))
|
||||
@ -2098,19 +2119,28 @@ void ioport_manager::load_config(config_type cfg_type, config_level cfg_level, u
|
||||
if ((seqtype != -1) && seqnode->get_value())
|
||||
{
|
||||
if (!strcmp(seqnode->get_value(), "NONE"))
|
||||
newseq[seqtype].reset();
|
||||
newseq[seqtype].first.reset();
|
||||
else
|
||||
machine().input().seq_from_tokens(newseq[seqtype], seqnode->get_value());
|
||||
machine().input().seq_from_tokens(newseq[seqtype].first, seqnode->get_value());
|
||||
newseq[seqtype].second = seqnode->get_value();
|
||||
}
|
||||
}
|
||||
|
||||
// load into the appropriate place for the config type/level
|
||||
if (config_type::SYSTEM == cfg_type)
|
||||
{
|
||||
load_system_config(*portnode, type, player, newseq);
|
||||
}
|
||||
else if ((config_type::CONTROLLER == cfg_type) && (config_level::DEFAULT != cfg_level))
|
||||
{
|
||||
for (auto &seq : newseq)
|
||||
seq.second = "";
|
||||
load_controller_config(*portnode, type, player, newseq);
|
||||
}
|
||||
else
|
||||
{
|
||||
load_default_config(type, player, newseq);
|
||||
}
|
||||
}
|
||||
|
||||
// after applying the controller config, push that back into the backup, since that is
|
||||
@ -2230,7 +2260,10 @@ void ioport_manager::load_remap_table(util::xml::data_node const &parentnode)
|
||||
// to defaults for all systems
|
||||
//-------------------------------------------------
|
||||
|
||||
bool ioport_manager::load_default_config(int type, int player, const input_seq (&newseq)[SEQ_TYPE_TOTAL])
|
||||
bool ioport_manager::load_default_config(
|
||||
int type,
|
||||
int player,
|
||||
const std::pair<input_seq, char const *> (&newseq)[SEQ_TYPE_TOTAL])
|
||||
{
|
||||
// find a matching port in the list
|
||||
for (input_type_entry &entry : m_typelist)
|
||||
@ -2238,8 +2271,11 @@ bool ioport_manager::load_default_config(int type, int player, const input_seq (
|
||||
if (entry.type() == type && entry.player() == player)
|
||||
{
|
||||
for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
|
||||
if (newseq[seqtype][0] != INPUT_CODE_INVALID)
|
||||
entry.set_seq(seqtype, newseq[seqtype]);
|
||||
{
|
||||
if (newseq[seqtype].first[0] != INPUT_CODE_INVALID)
|
||||
entry.set_seq(seqtype, newseq[seqtype].first);
|
||||
entry.set_cfg(seqtype, newseq[seqtype].second);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -2252,7 +2288,11 @@ bool ioport_manager::load_default_config(int type, int player, const input_seq (
|
||||
// profile settings to defaults
|
||||
//-------------------------------------------------
|
||||
|
||||
bool ioport_manager::load_controller_config(util::xml::data_node const &portnode, int type, int player, const input_seq (&newseq)[SEQ_TYPE_TOTAL])
|
||||
bool ioport_manager::load_controller_config(
|
||||
util::xml::data_node const &portnode,
|
||||
int type,
|
||||
int player,
|
||||
const std::pair<input_seq, char const *> (&newseq)[SEQ_TYPE_TOTAL])
|
||||
{
|
||||
// without a tag, apply to the defaults for all systems
|
||||
char const *const tag = portnode.get_attribute_string("tag", nullptr);
|
||||
@ -2278,8 +2318,8 @@ bool ioport_manager::load_controller_config(util::xml::data_node const &portnode
|
||||
// if a sequence was specified, override the developer-specified default for the field
|
||||
for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
|
||||
{
|
||||
if (newseq[seqtype][0] != INPUT_CODE_INVALID)
|
||||
field.set_defseq(seqtype, newseq[seqtype]);
|
||||
if (newseq[seqtype].first[0] != INPUT_CODE_INVALID)
|
||||
field.set_defseq(seqtype, newseq[seqtype].first);
|
||||
}
|
||||
|
||||
// fetch configurable attributes
|
||||
@ -2342,7 +2382,11 @@ bool ioport_manager::load_controller_config(util::xml::data_node const &portnode
|
||||
// configuration for the current system
|
||||
//-------------------------------------------------
|
||||
|
||||
void ioport_manager::load_system_config(util::xml::data_node const &portnode, int type, int player, const input_seq (&newseq)[SEQ_TYPE_TOTAL])
|
||||
void ioport_manager::load_system_config(
|
||||
util::xml::data_node const &portnode,
|
||||
int type,
|
||||
int player,
|
||||
const std::pair<input_seq, char const *> (&newseq)[SEQ_TYPE_TOTAL])
|
||||
{
|
||||
// system-specific configuration should always apply by port/field
|
||||
char const *const tag = portnode.get_attribute_string("tag", nullptr);
|
||||
@ -2364,8 +2408,9 @@ void ioport_manager::load_system_config(util::xml::data_node const &portnode, in
|
||||
// if a sequence was specified, copy it in
|
||||
for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
|
||||
{
|
||||
if (newseq[seqtype][0] != INPUT_CODE_INVALID)
|
||||
field.live().seq[seqtype] = newseq[seqtype];
|
||||
if (newseq[seqtype].first[0] != INPUT_CODE_INVALID)
|
||||
field.live().seq[seqtype] = newseq[seqtype].first;
|
||||
field.live().cfg[seqtype] = newseq[seqtype].second;
|
||||
}
|
||||
|
||||
// fetch configurable attributes
|
||||
@ -2460,27 +2505,6 @@ void ioport_manager::save_config(config_type cfg_type, util::xml::data_node *par
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// save_sequence - add a node for an input
|
||||
// sequence
|
||||
//-------------------------------------------------
|
||||
|
||||
void ioport_manager::save_sequence(util::xml::data_node &parentnode, input_seq_type type, ioport_type porttype, const input_seq &seq)
|
||||
{
|
||||
// get the string for the sequence
|
||||
std::string seqstring;
|
||||
if (seq.length() == 0)
|
||||
seqstring.assign("NONE");
|
||||
else
|
||||
seqstring = machine().input().seq_to_tokens(seq);
|
||||
|
||||
// add the new node
|
||||
util::xml::data_node *const seqnode = parentnode.add_child("newseq", seqstring.c_str());
|
||||
if (seqnode != nullptr)
|
||||
seqnode->set_attribute("type", seqtypestrings[type]);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// save_this_input_field_type - determine if the
|
||||
// given port type is worth saving
|
||||
@ -2519,7 +2543,7 @@ void ioport_manager::save_default_inputs(util::xml::data_node &parentnode)
|
||||
// see if any of the sequences have changed
|
||||
input_seq_type seqtype;
|
||||
for (seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
|
||||
if (entry.seq(seqtype) != entry.defseq(seqtype))
|
||||
if (!entry.cfg(seqtype).empty())
|
||||
break;
|
||||
|
||||
// if so, we need to add a node
|
||||
@ -2527,15 +2551,21 @@ void ioport_manager::save_default_inputs(util::xml::data_node &parentnode)
|
||||
{
|
||||
// add a new port node
|
||||
util::xml::data_node *const portnode = parentnode.add_child("port", nullptr);
|
||||
if (portnode != nullptr)
|
||||
if (portnode)
|
||||
{
|
||||
// add the port information and attributes
|
||||
portnode->set_attribute("type", input_type_to_token(entry.type(), entry.player()).c_str());
|
||||
|
||||
// add only the sequences that have changed from the defaults
|
||||
for (input_seq_type type = SEQ_TYPE_STANDARD; type < SEQ_TYPE_TOTAL; ++type)
|
||||
if (entry.seq(type) != entry.defseq(type))
|
||||
save_sequence(*portnode, type, entry.type(), entry.seq(type));
|
||||
{
|
||||
if (!entry.cfg(type).empty())
|
||||
{
|
||||
util::xml::data_node *const seqnode = portnode->add_child("newseq", entry.cfg(type).c_str());
|
||||
if (seqnode)
|
||||
seqnode->set_attribute("type", seqtypestrings[type]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2566,22 +2596,22 @@ void ioport_manager::save_game_inputs(util::xml::data_node &parentnode)
|
||||
{
|
||||
// determine if we changed
|
||||
bool changed = false;
|
||||
for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
|
||||
changed |= (field.seq(seqtype) != field.defseq(seqtype));
|
||||
for (input_seq_type seqtype = SEQ_TYPE_STANDARD; (seqtype < SEQ_TYPE_TOTAL) && !changed; ++seqtype)
|
||||
changed = !field.live().cfg[seqtype].empty();
|
||||
|
||||
if (!field.is_analog())
|
||||
{
|
||||
// non-analog changes
|
||||
changed |= ((field.live().value & field.mask()) != (field.defvalue() & field.mask()));
|
||||
changed |= (field.live().toggle != field.toggle());
|
||||
changed = changed || ((field.live().value & field.mask()) != (field.defvalue() & field.mask()));
|
||||
changed = changed || (field.live().toggle != field.toggle());
|
||||
}
|
||||
else
|
||||
{
|
||||
// analog changes
|
||||
changed |= (field.live().analog->m_delta != field.delta());
|
||||
changed |= (field.live().analog->m_centerdelta != field.centerdelta());
|
||||
changed |= (field.live().analog->m_sensitivity != field.sensitivity());
|
||||
changed |= (field.live().analog->m_reverse != field.analog_reverse());
|
||||
changed = changed || (field.live().analog->m_delta != field.delta());
|
||||
changed = changed || (field.live().analog->m_centerdelta != field.centerdelta());
|
||||
changed = changed || (field.live().analog->m_sensitivity != field.sensitivity());
|
||||
changed = changed || (field.live().analog->m_reverse != field.analog_reverse());
|
||||
}
|
||||
|
||||
// if we did change, add a new node
|
||||
@ -2599,8 +2629,14 @@ void ioport_manager::save_game_inputs(util::xml::data_node &parentnode)
|
||||
|
||||
// add sequences if changed
|
||||
for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
|
||||
if (field.seq(seqtype) != field.defseq(seqtype))
|
||||
save_sequence(*portnode, seqtype, field.type(), field.seq(seqtype));
|
||||
{
|
||||
if (!field.live().cfg[seqtype].empty())
|
||||
{
|
||||
util::xml::data_node *const seqnode = portnode->add_child("newseq", field.live().cfg[seqtype].c_str());
|
||||
if (seqnode)
|
||||
seqnode->set_attribute("type", seqtypestrings[seqtype]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!field.is_analog())
|
||||
{
|
||||
|
@ -802,22 +802,25 @@ public:
|
||||
input_seq &defseq(input_seq_type seqtype = SEQ_TYPE_STANDARD) noexcept { return m_defseq[seqtype]; }
|
||||
const input_seq &defseq(input_seq_type seqtype = SEQ_TYPE_STANDARD) const noexcept { return m_defseq[seqtype]; }
|
||||
const input_seq &seq(input_seq_type seqtype = SEQ_TYPE_STANDARD) const noexcept { return m_seq[seqtype]; }
|
||||
const std::string &cfg(input_seq_type seqtype = SEQ_TYPE_STANDARD) const noexcept { return m_cfg[seqtype]; }
|
||||
|
||||
// setters
|
||||
void restore_default_seq() noexcept;
|
||||
void set_seq(input_seq_type seqtype, const input_seq &seq) noexcept { m_seq[seqtype] = seq; }
|
||||
template <typename... T> void set_cfg(input_seq_type seqtype, T &&... cfg) { m_cfg[seqtype].assign(std::forward<T>(cfg)...); }
|
||||
void replace_code(input_code oldcode, input_code newcode) noexcept;
|
||||
void configure_osd(const char *token, const char *name) noexcept;
|
||||
|
||||
private:
|
||||
// internal state
|
||||
ioport_type m_type; // IPT_* for this entry
|
||||
ioport_group m_group; // which group the port belongs to
|
||||
u8 m_player; // player number (0 is player 1)
|
||||
const char * m_token; // token used to store settings
|
||||
const char * m_name; // user-friendly name
|
||||
std::array<input_seq, SEQ_TYPE_TOTAL> m_defseq; // default input sequence
|
||||
std::array<input_seq, SEQ_TYPE_TOTAL> m_seq; // currently configured sequences
|
||||
ioport_type m_type; // IPT_* for this entry
|
||||
ioport_group m_group; // which group the port belongs to
|
||||
u8 m_player; // player number (0 is player 1)
|
||||
const char * m_token; // token used to store settings
|
||||
const char * m_name; // user-friendly name
|
||||
std::array<input_seq, SEQ_TYPE_TOTAL> m_defseq; // default input sequence
|
||||
std::array<input_seq, SEQ_TYPE_TOTAL> m_seq; // currently configured sequences
|
||||
std::array<std::string, SEQ_TYPE_TOTAL> m_cfg; // configuration strings
|
||||
};
|
||||
|
||||
|
||||
@ -1087,14 +1090,15 @@ public:
|
||||
{
|
||||
ioport_value value = 0; // for DIP switches
|
||||
input_seq seq[SEQ_TYPE_TOTAL]; // sequences of all types
|
||||
std::string cfg[SEQ_TYPE_TOTAL]; // configuration strings of all types
|
||||
s32 sensitivity = 0; // for analog controls
|
||||
s32 delta = 0; // for analog controls
|
||||
s32 centerdelta = 0; // for analog controls
|
||||
bool reverse = false; // for analog controls
|
||||
bool toggle = false; // for non-analog controls
|
||||
};
|
||||
void get_user_settings(user_settings &settings) const noexcept;
|
||||
void set_user_settings(const user_settings &settings) noexcept;
|
||||
void get_user_settings(user_settings &settings) const;
|
||||
void set_user_settings(const user_settings &settings);
|
||||
|
||||
private:
|
||||
void expand_diplocation(const char *location, std::string &errorbuf);
|
||||
@ -1163,6 +1167,7 @@ struct ioport_field_live
|
||||
digital_joystick::direction_t joydir; // digital joystick direction index
|
||||
bool lockout; // user lockout
|
||||
std::string name; // overridden name
|
||||
std::string cfg[SEQ_TYPE_TOTAL];// configuration strings
|
||||
};
|
||||
|
||||
|
||||
@ -1405,12 +1410,11 @@ private:
|
||||
|
||||
void load_config(config_type cfg_type, config_level cfg_level, util::xml::data_node const *parentnode);
|
||||
void load_remap_table(util::xml::data_node const &parentnode);
|
||||
bool load_default_config(int type, int player, const input_seq (&newseq)[SEQ_TYPE_TOTAL]);
|
||||
bool load_controller_config(util::xml::data_node const &portnode, int type, int player, const input_seq (&newseq)[SEQ_TYPE_TOTAL]);
|
||||
void load_system_config(util::xml::data_node const &portnode, int type, int player, const input_seq (&newseq)[SEQ_TYPE_TOTAL]);
|
||||
bool load_default_config(int type, int player, const std::pair<input_seq, char const *> (&newseq)[SEQ_TYPE_TOTAL]);
|
||||
bool load_controller_config(util::xml::data_node const &portnode, int type, int player, const std::pair<input_seq, char const *> (&newseq)[SEQ_TYPE_TOTAL]);
|
||||
void load_system_config(util::xml::data_node const &portnode, int type, int player, const std::pair<input_seq, char const *> (&newseq)[SEQ_TYPE_TOTAL]);
|
||||
|
||||
void save_config(config_type cfg_type, util::xml::data_node *parentnode);
|
||||
void save_sequence(util::xml::data_node &parentnode, input_seq_type type, ioport_type porttype, const input_seq &seq);
|
||||
bool save_this_input_field_type(ioport_type type);
|
||||
void save_default_inputs(util::xml::data_node &parentnode);
|
||||
void save_game_inputs(util::xml::data_node &parentnode);
|
||||
|
@ -300,8 +300,8 @@ bool input_sequence_poller::poll()
|
||||
m_modified = true;
|
||||
}
|
||||
|
||||
// if we're recorded at least one item and 2/3 of a second has passed, we're done
|
||||
if (m_last_ticks && ((m_last_ticks + (osd_ticks_per_second() * 2 / 3)) < newticks))
|
||||
// if we've recorded at least one item and one second has passed, we're done
|
||||
if (m_last_ticks && ((m_last_ticks + osd_ticks_per_second()) < newticks))
|
||||
return true;
|
||||
|
||||
// return false to indicate we are still polling
|
||||
|
@ -244,6 +244,12 @@ void lua_engine::initialize_input(sol::table &emu)
|
||||
ioport_field::user_settings settings;
|
||||
f.get_user_settings(settings);
|
||||
settings.seq[seq_type] = seq;
|
||||
if (seq.is_default())
|
||||
settings.cfg[seq_type].clear();
|
||||
else if (!seq.length())
|
||||
settings.cfg[seq_type] = "NONE";
|
||||
else
|
||||
settings.cfg[seq_type] = f.port().device().machine().input().seq_to_tokens(seq);
|
||||
f.set_user_settings(settings);
|
||||
};
|
||||
ioport_field_type["input_seq"] =
|
||||
|
@ -120,6 +120,7 @@ void menu_input_general::update_input(input_item_data &seqchangeditem)
|
||||
{
|
||||
const input_type_entry &entry = *reinterpret_cast<const input_type_entry *>(seqchangeditem.ref);
|
||||
machine().ioport().set_type_seq(entry.type(), entry.player(), seqchangeditem.seqtype, seqchangeditem.seq);
|
||||
seqchangeditem.seq = machine().ioport().type_seq(entry.type(), entry.player(), seqchangeditem.seqtype);
|
||||
}
|
||||
|
||||
|
||||
@ -233,9 +234,17 @@ void menu_input_specific::update_input(input_item_data &seqchangeditem)
|
||||
ioport_field::user_settings settings;
|
||||
|
||||
// yeah, the const_cast is naughty, but we know we stored a non-const reference in it
|
||||
reinterpret_cast<const ioport_field *>(seqchangeditem.ref)->get_user_settings(settings);
|
||||
ioport_field const &field(*reinterpret_cast<ioport_field const *>(seqchangeditem.ref));
|
||||
field.get_user_settings(settings);
|
||||
settings.seq[seqchangeditem.seqtype] = seqchangeditem.seq;
|
||||
reinterpret_cast<ioport_field *>(const_cast<void *>(seqchangeditem.ref))->set_user_settings(settings);
|
||||
if (seqchangeditem.seq.is_default())
|
||||
settings.cfg[seqchangeditem.seqtype].clear();
|
||||
else if (!seqchangeditem.seq.length())
|
||||
settings.cfg[seqchangeditem.seqtype] = "NONE";
|
||||
else
|
||||
settings.cfg[seqchangeditem.seqtype] = machine().input().seq_to_tokens(seqchangeditem.seq);
|
||||
const_cast<ioport_field &>(field).set_user_settings(settings);
|
||||
seqchangeditem.seq = field.seq(seqchangeditem.seqtype);
|
||||
}
|
||||
|
||||
|
||||
@ -276,7 +285,7 @@ void menu_input::menu_activated()
|
||||
void menu_input::toggle_none_default(input_seq &selected_seq, input_seq &original_seq, const input_seq &selected_defseq)
|
||||
{
|
||||
if (original_seq.empty()) // if we used to be "none", toggle to the default value
|
||||
selected_seq = selected_defseq;
|
||||
selected_seq.set_default();
|
||||
else // otherwise, toggle to "none"
|
||||
selected_seq.reset();
|
||||
}
|
||||
@ -425,12 +434,6 @@ void menu_input::handle(event const *ev)
|
||||
seqchangeditem = &item;
|
||||
break;
|
||||
|
||||
case IPT_UI_LEFT: // flip between set and append
|
||||
case IPT_UI_RIGHT: // not very discoverable, but with the prompt it isn't completely opaque
|
||||
if (record_next || !item.seq.empty())
|
||||
record_next = !record_next;
|
||||
break;
|
||||
|
||||
case IPT_UI_PREV_GROUP:
|
||||
{
|
||||
auto current = std::distance(data.data(), &item);
|
||||
@ -492,6 +495,21 @@ void menu_input::handle(event const *ev)
|
||||
record_next = false;
|
||||
lastitem = &item;
|
||||
}
|
||||
|
||||
// flip between set and append
|
||||
// not very discoverable, but with the prompt it isn't completely opaque
|
||||
if ((IPT_UI_LEFT == ev->iptkey) || (IPT_UI_RIGHT == ev->iptkey))
|
||||
{
|
||||
if (erroritem)
|
||||
{
|
||||
errormsg.clear();
|
||||
erroritem = nullptr;
|
||||
}
|
||||
else if (record_next || !item.seq.empty())
|
||||
{
|
||||
record_next = !record_next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if the sequence changed, update it
|
||||
|
@ -504,8 +504,7 @@ void menu::draw(uint32_t flags)
|
||||
visible_main_menu_height += 0.01f;
|
||||
|
||||
// if we are too wide or too tall, clamp it down
|
||||
if (visible_width + 2.0f * lr_border > 1.0f)
|
||||
visible_width = 1.0f - 2.0f * lr_border;
|
||||
visible_width = std::min(visible_width, 1.0f - ((lr_border + (aspect * UI_LINE_WIDTH)) * 2.0f));
|
||||
|
||||
// if the menu and extra menu won't fit, take away part of the regular menu, it will scroll
|
||||
if (visible_main_menu_height + visible_extra_menu_height + 2.0f * ui().box_tb_border() > 1.0f)
|
||||
|
Loading…
Reference in New Issue
Block a user