summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/ioport.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/ioport.cpp')
-rw-r--r--src/emu/ioport.cpp2201
1 files changed, 1282 insertions, 919 deletions
diff --git a/src/emu/ioport.cpp b/src/emu/ioport.cpp
index e74502439fe..712df85db29 100644
--- a/src/emu/ioport.cpp
+++ b/src/emu/ioport.cpp
@@ -2,110 +2,41 @@
// copyright-holders:Aaron Giles
/***************************************************************************
- ioport.c
+ ioport.cpp
Input/output port handling.
-****************************************************************************
-
- Theory of operation
-
- ------------
- OSD controls
- ------------
-
- There are three types of controls that the OSD can provide as potential
- input devices: digital controls, absolute analog controls, and relative
- analog controls.
-
- Digital controls have only two states: on or off. They are generally
- mapped to buttons and digital joystick directions (like a gamepad or a
- joystick hat). The OSD layer must return either 0 (off) or 1 (on) for
- these types of controls.
-
- Absolute analog controls are analog in the sense that they return a
- range of values depending on how much a given control is moved, but they
- are physically bounded. This means that there is a minimum and maximum
- limit to how far the control can be moved. They are generally mapped to
- analog joystick axes, lightguns, most PC steering wheels, and pedals.
- The OSD layer must determine the minimum and maximum range of each
- analog device and scale that to a value between -65536 and +65536
- representing the position of the control. -65536 generally refers to
- the topmost or leftmost position, while +65536 refers to the bottommost
- or rightmost position. Note that pedals are a special case here, the
- OSD layer needs to return half axis as full -65536 to + 65536 range.
-
- Relative analog controls are analog as well, but are not physically
- bounded. They can be moved continually in one direction without limit.
- They are generally mapped to trackballs and mice. Because they are
- unbounded, the OSD layer can only return delta values since the last
- read. Because of this, it is difficult to scale appropriately. For
- MAME's purposes, when mapping a mouse devices to a relative analog
- control, one pixel of movement should correspond to 512 units. Other
- analog control types should be scaled to return values of a similar
- magnitude. Like absolute analog controls, negative values refer to
- upward or leftward movement, while positive values refer to downward
- or rightward movement.
-
- -------------
- Game controls
- -------------
-
- Similarly, the types of controls used by arcade games fall into the same
- three categories: digital, absolute analog, and relative analog. The
- tricky part is how to map any arbitrary type of OSD control to an
- arbitrary type of game control.
-
- Digital controls: used for game buttons and standard 4/8-way joysticks,
- as well as many other types of game controls. Mapping an OSD digital
- control to a game's OSD control is trivial. For OSD analog controls,
- the MAME core does not directly support mapping any OSD analog devices
- to digital controls. However, the OSD layer is free to enumerate digital
- equivalents for analog devices. For example, each analog axis in the
- Windows OSD code enumerates to two digital controls, one for the
- negative direction (up/left) and one for the position direction
- (down/right). When these "digital" inputs are queried, the OSD layer
- checks the axis position against the center, adding in a dead zone,
- and returns 0 or 1 to indicate its position.
-
- Absolute analog controls: used for analog joysticks, lightguns, pedals,
- and wheel controls. Mapping an OSD absolute analog control to this type
- is easy. OSD relative analog controls can be mapped here as well by
- accumulating the deltas and bounding the results. OSD digital controls
- are mapped to these types of controls in pairs, one for a decrement and
- one for an increment, but apart from that, operate the same as the OSD
- relative analog controls by accumulating deltas and applying bounds.
- The speed of the digital delta is user-configurable per analog input.
- In addition, most absolute analog control types have an autocentering
- feature that is activated when using the digital increment/decrement
- sequences, which returns the control back to the center at a user-
- controllable speed if no digital sequences are pressed.
-
- Relative analog controls: used for trackballs and dial controls. Again,
- mapping an OSD relative analog control to this type is straightforward.
- OSD absolute analog controls can't map directly to these, but if the OSD
- layer provides a digital equivalent for each direction, it can be done.
- OSD digital controls map just like they do for absolute analog controls,
- except that the accumulated deltas are not bounded, but rather wrap.
-
***************************************************************************/
#include "emu.h"
-#include "emuopts.h"
+
#include "config.h"
-#include "xmlfile.h"
-#include "profiler.h"
-#include "ui/uimain.h"
+#include "emuopts.h"
+#include "fileio.h"
#include "inputdev.h"
+#include "main.h"
#include "natkeyboard.h"
+#include "profiler.h"
+
+#include "ui/uimain.h"
+
+#include "util/corestr.h"
+#include "util/ioprocsfilter.h"
+#include "util/language.h"
+#include "util/multibyte.h"
+#include "util/unicode.h"
+#include "util/xmlfile.h"
#include "osdepend.h"
-#include <ctype.h>
-#include <time.h>
+#include <algorithm>
+#include <cctype>
+#include <ctime>
+#include <sstream>
namespace {
+
// temporary: set this to 1 to enable the originally defined behavior that
// a field specified via PORT_MODIFY which intersects a previously-defined
// field completely wipes out the previous definition
@@ -156,6 +87,22 @@ inline s32 apply_scale(s32 value, s64 scale)
return (s64(value) * scale) / (1 << 24);
}
+//-------------------------------------------------
+// compute_shift -- get shift required to right-
+// align an I/O port field value
+//-------------------------------------------------
+
+inline u8 compute_shift(ioport_value mask)
+{
+ u8 result = 0U;
+ while (mask && !BIT(mask, 0))
+ {
+ mask >>= 1;
+ ++result;
+ }
+ return result;
+}
+
//**************************************************************************
@@ -188,6 +135,7 @@ const struct
{ INPUT_STRING_3C_1C, "3 Coins/1 Credit" },
{ INPUT_STRING_8C_3C, "8 Coins/3 Credits" },
{ INPUT_STRING_4C_2C, "4 Coins/2 Credits" },
+ { INPUT_STRING_5C_2C, "5 Coins/2 Credits" },
{ INPUT_STRING_2C_1C, "2 Coins/1 Credit" },
{ INPUT_STRING_5C_3C, "5 Coins/3 Credits" },
{ INPUT_STRING_3C_2C, "3 Coins/2 Credits" },
@@ -196,6 +144,7 @@ const struct
{ INPUT_STRING_3C_3C, "3 Coins/3 Credits" },
{ INPUT_STRING_2C_2C, "2 Coins/2 Credits" },
{ INPUT_STRING_1C_1C, "1 Coin/1 Credit" },
+ { INPUT_STRING_3C_5C, "3 Coins/5 Credits" },
{ INPUT_STRING_4C_5C, "4 Coins/5 Credits" },
{ INPUT_STRING_3C_4C, "3 Coins/4 Credits" },
{ INPUT_STRING_2C_3C, "2 Coins/3 Credits" },
@@ -289,7 +238,158 @@ const struct
{ INPUT_STRING_None, "None" },
};
-} // TODO: anonymous namespace
+const char *const input_gm_notes_names[128] = {
+ "C-1", "C-1#", "D-1", "D-1#", "E-1", "F-1", "F-1#", "G-1", "G-1#", "A-1", "A-1#", "B-1",
+ "C0", "C0#", "D0", "D0#", "E0", "F0", "F0#", "G0", "G0#", "A0", "A0#", "B0",
+ "C1", "C1#", "D1", "D1#", "E1", "F1", "F1#", "G1", "G1#", "A1", "A1#", "B1",
+ "C2", "C2#", "D2", "D2#", "E2", "F2", "F2#", "G2", "G2#", "A2", "A2#", "B2",
+ "C3", "C3#", "D3", "D3#", "E3", "F3", "F3#", "G3", "G3#", "A3", "A3#", "B3",
+ "C4", "C4#", "D4", "D4#", "E4", "F4", "F4#", "G4", "G4#", "A4", "A4#", "B4",
+ "C5", "C5#", "D5", "D5#", "E5", "F5", "F5#", "G5", "G5#", "A5", "A5#", "B5",
+ "C6", "C6#", "D6", "D6#", "E6", "F6", "F6#", "G6", "G6#", "A6", "A6#", "B6",
+ "C7", "C7#", "D7", "D7#", "E7", "F7", "F7#", "G7", "G7#", "A7", "A7#", "B7",
+ "C8", "C8#", "D8", "D8#", "E8", "F8", "F8#", "G8", "G8#", "A8", "A8#", "B8",
+ "C9", "C9#", "D9", "D9#", "E9", "F9", "F9#", "G9"
+};
+
+inline bool input_seq_good(running_machine &machine, input_seq const &seq)
+{
+ if (INPUT_CODE_INVALID == seq[0])
+ return false;
+ else if (seq.empty())
+ return true;
+ else
+ return input_seq::end_code != machine.input().seq_clean(seq)[0];
+}
+
+
+std::string substitute_player(std::string_view name, u8 player)
+{
+ using util::lang_translate;
+
+ std::string result;
+ while (!name.empty())
+ {
+ auto const found = name.find('%');
+ if ((std::string_view::npos == found) || (name.length() == found + 1))
+ {
+ result.append(name);
+ break;
+ }
+ switch (name[found + 1])
+ {
+ case '%':
+ result.append(name.substr(0, found + 1));
+ break;
+ case 'p':
+ result.append(name.substr(0, found));
+ result.append(util::string_format(_("input-name", "P%1$u"), player + 1));
+ break;
+ default:
+ result.append(name.substr(0, found + 2));
+ }
+ name.remove_prefix(found + 2);
+ }
+ return result;
+}
+
+
+
+// ======================> inp_header
+
+// header at the front of INP files
+class inp_header
+{
+public:
+ // parameters
+ static constexpr unsigned MAJVERSION = 3;
+ static constexpr unsigned MINVERSION = 0;
+
+ bool read(emu_file &f)
+ {
+ return f.read(m_data, sizeof(m_data)) == sizeof(m_data);
+ }
+ bool write(emu_file &f) const
+ {
+ return f.write(m_data, sizeof(m_data)) == sizeof(m_data);
+ }
+
+ bool check_magic() const
+ {
+ return 0 == std::memcmp(MAGIC, m_data + OFFS_MAGIC, OFFS_BASETIME - OFFS_MAGIC);
+ }
+ u64 get_basetime() const
+ {
+ return get_u64le(m_data + OFFS_BASETIME);
+ }
+ unsigned get_majversion() const
+ {
+ return m_data[OFFS_MAJVERSION];
+ }
+ unsigned get_minversion() const
+ {
+ return m_data[OFFS_MINVERSION];
+ }
+ std::string get_sysname() const
+ {
+ return get_string<OFFS_SYSNAME, OFFS_APPDESC>();
+ }
+ std::string get_appdesc() const
+ {
+ return get_string<OFFS_APPDESC, OFFS_END>();
+ }
+
+ void set_magic()
+ {
+ std::memcpy(m_data + OFFS_MAGIC, MAGIC, OFFS_BASETIME - OFFS_MAGIC);
+ }
+ void set_basetime(u64 time)
+ {
+ put_u64le(m_data + OFFS_BASETIME, time);
+ }
+ void set_version()
+ {
+ m_data[OFFS_MAJVERSION] = MAJVERSION;
+ m_data[OFFS_MINVERSION] = MINVERSION;
+ }
+ void set_sysname(std::string const &name)
+ {
+ set_string<OFFS_SYSNAME, OFFS_APPDESC>(name);
+ }
+ void set_appdesc(std::string const &desc)
+ {
+ set_string<OFFS_APPDESC, OFFS_END>(desc);
+ }
+
+private:
+ template <std::size_t BEGIN, std::size_t END> void set_string(std::string const &str)
+ {
+ std::size_t const used = (std::min<std::size_t>)(str.size() + 1, END - BEGIN);
+ std::memcpy(m_data + BEGIN, str.c_str(), used);
+ if ((END - BEGIN) > used)
+ std::memset(m_data + BEGIN + used, 0, (END - BEGIN) - used);
+ }
+ template <std::size_t BEGIN, std::size_t END> std::string get_string() const
+ {
+ char const *const begin = reinterpret_cast<char const *>(m_data + BEGIN);
+ return std::string(begin, std::find(begin, reinterpret_cast<char const *>(m_data + END), '\0'));
+ }
+
+ static constexpr std::size_t OFFS_MAGIC = 0x00; // 0x08 bytes
+ static constexpr std::size_t OFFS_BASETIME = 0x08; // 0x08 bytes (little-endian binary integer)
+ static constexpr std::size_t OFFS_MAJVERSION = 0x10; // 0x01 bytes (binary integer)
+ static constexpr std::size_t OFFS_MINVERSION = 0x11; // 0x01 bytes (binary integer)
+ // 0x02 bytes reserved
+ static constexpr std::size_t OFFS_SYSNAME = 0x14; // 0x0c bytes (ASCII)
+ static constexpr std::size_t OFFS_APPDESC = 0x20; // 0x20 bytes (ASCII)
+ static constexpr std::size_t OFFS_END = 0x40;
+
+ static u8 const MAGIC[OFFS_BASETIME - OFFS_MAGIC];
+
+ u8 m_data[OFFS_END];
+};
+
+} // anonymous namespace
// XML attributes for the different types
@@ -304,7 +404,7 @@ u8 const inp_header::MAGIC[inp_header::OFFS_BASETIME - inp_header::OFFS_MAGIC] =
// BUILT-IN CORE MAPPINGS
//**************************************************************************
-#include "inpttype.h"
+#include "inpttype.ipp"
@@ -321,16 +421,13 @@ u8 const inp_header::MAGIC[inp_header::OFFS_BASETIME - inp_header::OFFS_MAGIC] =
// to the current list
//-------------------------------------------------
-void ioport_list::append(device_t &device, std::string &errorbuf)
+void ioport_list::append(device_t &device, std::ostream &errorbuf)
{
// no constructor, no list
ioport_constructor constructor = device.input_ports();
- if (constructor == nullptr)
+ if (!constructor)
return;
- // reset error buffer
- errorbuf.clear();
-
// detokenize into the list
(*constructor)(device, *this, errorbuf);
@@ -349,24 +446,22 @@ void ioport_list::append(device_t &device, std::string &errorbuf)
// input_type_entry - constructors
//-------------------------------------------------
-input_type_entry::input_type_entry(ioport_type type, ioport_group group, int player, const char *token, const char *name, input_seq standard)
- : m_next(nullptr),
- m_type(type),
- m_group(group),
- m_player(player),
- m_token(token),
- m_name(name)
+input_type_entry::input_type_entry(ioport_type type, ioport_group group, int player, const char *token, const char *name, input_seq standard) noexcept :
+ m_type(type),
+ m_group(group),
+ m_player(player),
+ m_token(token),
+ m_name(name)
{
m_defseq[SEQ_TYPE_STANDARD] = m_seq[SEQ_TYPE_STANDARD] = standard;
}
-input_type_entry::input_type_entry(ioport_type type, ioport_group group, int player, const char *token, const char *name, input_seq standard, input_seq decrement, input_seq increment)
- : m_next(nullptr),
- m_type(type),
- m_group(group),
- m_player(player),
- m_token(token),
- m_name(name)
+input_type_entry::input_type_entry(ioport_type type, ioport_group group, int player, const char *token, const char *name, input_seq standard, input_seq decrement, input_seq increment) noexcept :
+ m_type(type),
+ m_group(group),
+ m_player(player),
+ m_token(token),
+ m_name(name)
{
m_defseq[SEQ_TYPE_STANDARD] = m_seq[SEQ_TYPE_STANDARD] = standard;
m_defseq[SEQ_TYPE_INCREMENT] = m_seq[SEQ_TYPE_INCREMENT] = increment;
@@ -375,11 +470,41 @@ input_type_entry::input_type_entry(ioport_type type, ioport_group group, int pla
//-------------------------------------------------
+// name - gets the display name for the input
+// type
+//-------------------------------------------------
+
+std::string input_type_entry::name() const
+{
+ using util::lang_translate;
+
+ if (!m_name)
+ return std::string();
+ else if ((group() < IPG_PLAYER1) || (group() > IPG_PLAYER10))
+ return _("input-name", m_name);
+ else
+ return substitute_player(_("input-name", m_name), player());
+}
+
+
+//-------------------------------------------------
+// replace_code - replace all instances of
+// oldcodewith newcode in all sequences
+//-------------------------------------------------
+
+void input_type_entry::replace_code(input_code oldcode, input_code newcode) noexcept
+{
+ for (input_seq &seq : m_seq)
+ seq.replace(oldcode, newcode);
+}
+
+
+//-------------------------------------------------
// configure_osd - set the token and name of an
// OSD entry
//-------------------------------------------------
-void input_type_entry::configure_osd(const char *token, const char *name)
+void input_type_entry::configure_osd(const char *token, const char *name) noexcept
{
assert(m_type >= IPT_OSD_1 && m_type <= IPT_OSD_16);
m_token = token;
@@ -392,10 +517,9 @@ void input_type_entry::configure_osd(const char *token, const char *name)
// from the default
//-------------------------------------------------
-void input_type_entry::restore_default_seq()
+void input_type_entry::restore_default_seq() noexcept
{
- for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
- m_seq[seqtype] = defseq(seqtype);
+ m_seq = m_defseq;
}
@@ -407,13 +531,12 @@ void input_type_entry::restore_default_seq()
// digital_joystick - constructor
//-------------------------------------------------
-digital_joystick::digital_joystick(int player, int number)
- : m_next(nullptr),
- m_player(player),
- m_number(number),
- m_current(0),
- m_current4way(0),
- m_previous(0)
+digital_joystick::digital_joystick(int player, int number) :
+ m_player(player),
+ m_number(number),
+ m_current(0),
+ m_current4way(0),
+ m_previous(0)
{
}
@@ -426,7 +549,7 @@ digital_joystick::digital_joystick(int player, int number)
digital_joystick::direction_t digital_joystick::add_axis(ioport_field &field)
{
direction_t direction = direction_t((field.type() - (IPT_DIGITAL_JOYSTICK_FIRST + 1)) % 4);
- m_field[direction].append(*global_alloc(simple_list_wrapper<ioport_field>(&field)));
+ m_field[direction].emplace_front(field);
return direction;
}
@@ -446,10 +569,10 @@ void digital_joystick::frame_update()
// read all the associated ports
running_machine *machine = nullptr;
for (direction_t direction = JOYDIR_UP; direction < JOYDIR_COUNT; ++direction)
- for (const simple_list_wrapper<ioport_field> &i : m_field[direction])
+ for (const std::reference_wrapper<ioport_field> &i : m_field[direction])
{
- machine = &i.object()->machine();
- if (machine->input().seq_pressed(i.object()->seq(SEQ_TYPE_STANDARD)))
+ machine = &i.get().machine();
+ if (machine->input().seq_pressed(i.get().seq(SEQ_TYPE_STANDARD)))
m_current |= 1 << direction;
}
@@ -496,6 +619,8 @@ void digital_joystick::frame_update()
m_current4way &= ~(UP_BIT | DOWN_BIT);
}
}
+ else
+ m_current4way &= m_current;
}
@@ -515,7 +640,7 @@ bool ioport_condition::eval() const
return true;
// otherwise, read the referenced port and switch off the condition type
- ioport_value condvalue = m_port->read();
+ ioport_value const condvalue = m_port->read();
switch (m_condition)
{
case ALWAYS: return true;
@@ -550,11 +675,10 @@ void ioport_condition::initialize(device_t &device)
// ioport_setting - constructor
//-------------------------------------------------
-ioport_setting::ioport_setting(ioport_field &field, ioport_value _value, const char *_name)
- : m_next(nullptr),
- m_field(field),
- m_value(_value),
- m_name(_name)
+ioport_setting::ioport_setting(ioport_field &field, ioport_value _value, const char *_name) :
+ m_field(field),
+ m_value(_value),
+ m_name(_name)
{
}
@@ -568,11 +692,10 @@ ioport_setting::ioport_setting(ioport_field &field, ioport_value _value, const c
// ioport_diplocation - constructor
//-------------------------------------------------
-ioport_diplocation::ioport_diplocation(const char *name, u8 swnum, bool invert)
- : m_next(nullptr),
- m_name(name),
- m_number(swnum),
- m_invert(invert)
+ioport_diplocation::ioport_diplocation(std::string_view name, u8 swnum, bool invert) :
+ m_name(name),
+ m_number(swnum),
+ m_invert(invert)
{
}
@@ -586,38 +709,41 @@ ioport_diplocation::ioport_diplocation(const char *name, u8 swnum, bool invert)
// ioport_field - constructor
//-------------------------------------------------
-ioport_field::ioport_field(ioport_port &port, ioport_type type, ioport_value defvalue, ioport_value maskbits, const char *name)
- : m_next(nullptr),
- m_port(port),
- m_modcount(port.modcount()),
- m_mask(maskbits),
- m_defvalue(defvalue & maskbits),
- m_type(type),
- m_player(0),
- m_flags(0),
- m_impulse(0),
- m_name(name),
- m_write_param(0),
- m_digital_value(false),
- m_min(0),
- m_max(maskbits),
- m_sensitivity(0),
- m_delta(0),
- m_centerdelta(0),
- m_crosshair_axis(CROSSHAIR_AXIS_NONE),
- m_crosshair_scale(1.0),
- m_crosshair_offset(0),
- m_crosshair_altaxis(0),
- m_full_turn_count(0),
- m_remap_table(nullptr),
- m_way(0)
+ioport_field::ioport_field(ioport_port &port, ioport_type type, ioport_value defvalue, ioport_value maskbits, const char *name) :
+ m_next(nullptr),
+ m_port(port),
+ m_modcount(port.modcount()),
+ m_mask(maskbits),
+ m_defvalue(defvalue & maskbits),
+ m_type(type),
+ m_player(0),
+ m_flags(0),
+ m_impulse(0),
+ m_name(name),
+ m_read(port.device()),
+ m_write(port.device()),
+ m_write_param(0),
+ m_digital_value(false),
+ m_min(0),
+ m_max(maskbits),
+ m_sensitivity(0),
+ m_delta(0),
+ m_centerdelta(0),
+ m_crosshair_axis(CROSSHAIR_AXIS_NONE),
+ m_crosshair_scale(1.0),
+ m_crosshair_offset(0),
+ m_crosshair_altaxis(0),
+ m_crosshair_mapper(port.device()),
+ m_full_turn_count(0),
+ m_remap_table(nullptr),
+ m_way(0)
{
// reset sequences and chars
for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
m_seq[seqtype].set_default();
- for (int i = 0; i < ARRAY_LENGTH(m_chars); i++)
- std::fill(std::begin(m_chars[i]), std::end(m_chars[i]), char32_t(0));
+ for (auto &chars : m_chars)
+ std::fill(std::begin(chars), std::end(chars), UCHAR_INVALID);
// for DIP switches and configs, look for a default value from the owner
if (type == IPT_DIPSWITCH || type == IPT_CONFIG)
@@ -635,18 +761,39 @@ ioport_field::ioport_field(ioport_port &port, ioport_type type, ioport_value def
}
}
+
+//-------------------------------------------------
+// ~ioport_field - destructor
+//-------------------------------------------------
+
+ioport_field::~ioport_field()
+{
+}
+
+
+//-------------------------------------------------
+// set_value - programmatically set field value
+//-------------------------------------------------
+
void ioport_field::set_value(ioport_value value)
{
- m_digital_value = value != 0;
+ if (is_analog())
+ live().analog->set_value(s32(value));
+ else
+ m_digital_value = value != 0;
}
//-------------------------------------------------
-// ~ioport_field - destructor
+// clear_value - clear programmatic override
//-------------------------------------------------
-ioport_field::~ioport_field()
+void ioport_field::clear_value()
{
+ if (is_analog())
+ live().analog->clear_value();
+ else
+ m_digital_value = false;
}
@@ -655,16 +802,25 @@ ioport_field::~ioport_field()
// field (this must never return nullptr)
//-------------------------------------------------
-const char *ioport_field::name() const
+std::string ioport_field::name() const
{
- // if we have a non-default name, use that
- if (m_live != nullptr && !m_live->name.empty())
- return m_live->name.c_str();
- if (m_name != nullptr)
+ using util::lang_translate;
+
+ // if we have an overridden name, use that
+ if (m_live && !m_live->name.empty())
+ return m_live->name;
+
+ // if no specific name, use the generic name for the type
+ if (!m_name)
+ return manager().type_name(m_type, m_player);
+
+ // return name for non-controller fields as-is
+ ioport_group const group = manager().type_group(m_type, m_player);
+ if ((group < IPG_PLAYER1) || (group > IPG_PLAYER10))
return m_name;
- // otherwise, return the name associated with the type
- return manager().type_name(m_type, m_player);
+ // substitute the player number in if necessary
+ return substitute_player(m_name, m_player);
}
@@ -673,18 +829,14 @@ const char *ioport_field::name() const
// given input field
//-------------------------------------------------
-const input_seq &ioport_field::seq(input_seq_type seqtype) const
+const input_seq &ioport_field::seq(input_seq_type seqtype) const noexcept
{
- // if no live state, return default
- if (m_live == nullptr)
- return defseq(seqtype);
-
- // if the sequence is the special default code, return the expanded default value
- if (m_live->seq[seqtype].is_default())
- return manager().type_seq(m_type, m_player, seqtype);
+ // if the sequence is not the special default code, return it
+ if (m_live && !m_live->seq[seqtype].is_default())
+ return m_live->seq[seqtype];
- // otherwise, return the sequence as-is
- return m_live->seq[seqtype];
+ // otherwise return the default sequence
+ return defseq(seqtype);
}
@@ -693,7 +845,7 @@ const input_seq &ioport_field::seq(input_seq_type seqtype) const
// the given input field
//-------------------------------------------------
-const input_seq &ioport_field::defseq(input_seq_type seqtype) const
+const input_seq &ioport_field::defseq(input_seq_type seqtype) const noexcept
{
// if the sequence is the special default code, return the expanded default value
if (m_seq[seqtype].is_default())
@@ -711,14 +863,8 @@ const input_seq &ioport_field::defseq(input_seq_type seqtype) const
void ioport_field::set_defseq(input_seq_type seqtype, const input_seq &newseq)
{
- const bool was_changed = seq(seqtype) != defseq(seqtype);
-
// set the new sequence
m_seq[seqtype] = newseq;
-
- // also update live state unless previously customized
- if (m_live != nullptr && !was_changed)
- m_live->seq[seqtype] = newseq;
}
@@ -727,11 +873,11 @@ void ioport_field::set_defseq(input_seq_type seqtype, const input_seq &newseq)
// field
//-------------------------------------------------
-ioport_type_class ioport_field::type_class() const
+ioport_type_class ioport_field::type_class() const noexcept
{
// inputs associated with specific players
- ioport_group group = manager().type_group(m_type, m_player);
- if (group >= IPG_PLAYER1 && group <= IPG_PLAYER10)
+ ioport_group const group = manager().type_group(m_type, m_player);
+ if ((group >= IPG_PLAYER1) && (group <= IPG_PLAYER10))
return INPUT_CLASS_CONTROLLER;
// keys (names derived from character codes)
@@ -739,7 +885,7 @@ ioport_type_class ioport_field::type_class() const
return INPUT_CLASS_KEYBOARD;
// configuration settings (specific names required)
- if (m_type == IPT_CONFIG)
+ if (m_type == IPT_CONFIG || m_type == IPT_ADJUSTER)
return INPUT_CLASS_CONFIG;
// DIP switches (specific names required)
@@ -762,14 +908,11 @@ ioport_type_class ioport_field::type_class() const
std::vector<char32_t> ioport_field::keyboard_codes(int which) const
{
- if (which >= ARRAY_LENGTH(m_chars))
+ if (which >= std::size(m_chars))
throw emu_fatalerror("Tried to access keyboard_code with out-of-range index %d\n", which);
- std::vector<char32_t> result;
- for (int i = 0; i < ARRAY_LENGTH(m_chars[which]) && m_chars[which][i] != 0; i++)
- result.push_back(m_chars[which][i]);
-
- return result;
+ auto &chars = m_chars[which];
+ return std::vector<char32_t>(std::begin(chars), std::find(std::begin(chars), std::end(chars), UCHAR_INVALID));
}
@@ -856,33 +999,35 @@ std::string ioport_field::key_name(int which) const
// settings for the given input field
//-------------------------------------------------
-void ioport_field::get_user_settings(user_settings &settings)
+void ioport_field::get_user_settings(user_settings &settings) const
{
// zap the entire structure
- memset(&settings, 0, sizeof(settings));
+ 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)
settings.value = m_live->value;
- // if there's analog data, extract the analog settings
if (m_live->analog != nullptr)
{
+ // if there's analog data, extract the analog settings
settings.sensitivity = m_live->analog->sensitivity();
settings.delta = m_live->analog->delta();
settings.centerdelta = m_live->analog->centerdelta();
settings.reverse = m_live->analog->reverse();
}
-
- // non-analog settings
else
{
+ // non-analog settings
settings.toggle = m_live->toggle;
- settings.autofire = m_live->autofire;
}
}
@@ -897,31 +1042,29 @@ 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
if (!m_settinglist.empty() || m_type == IPT_ADJUSTER)
m_live->value = settings.value;
- // if there's analog data, extract the analog settings
- if (m_live->analog != nullptr)
+ if (m_live->analog)
{
+ // if there's analog data, extract the analog settings
m_live->analog->m_sensitivity = settings.sensitivity;
m_live->analog->m_delta = settings.delta;
m_live->analog->m_centerdelta = settings.centerdelta;
m_live->analog->m_reverse = settings.reverse;
}
-
- // non-analog settings
else
{
+ // non-analog settings
m_live->toggle = settings.toggle;
- m_live->autofire = settings.autofire;
}
}
@@ -937,7 +1080,7 @@ const char *ioport_field::setting_name() const
assert(!m_settinglist.empty());
// scan the list of settings looking for a match on the current value
- for (ioport_setting &setting : m_settinglist)
+ for (ioport_setting const &setting : m_settinglist)
if (setting.enabled())
if (setting.value() == m_live->value)
return setting.name();
@@ -957,7 +1100,7 @@ bool ioport_field::has_previous_setting() const
assert(!m_settinglist.empty());
// scan the list of settings looking for a match on the current value
- for (ioport_setting &setting : m_settinglist)
+ for (ioport_setting const &setting : m_settinglist)
if (setting.enabled())
return (setting.value() != m_live->value);
@@ -976,30 +1119,32 @@ void ioport_field::select_previous_setting()
assert(!m_settinglist.empty());
// scan the list of settings looking for a match on the current value
- ioport_setting *prevsetting = nullptr;
+ auto prevsetting = m_settinglist.end();
bool found_match = false;
- for (ioport_setting &setting : m_settinglist)
- if (setting.enabled())
+ for (auto setting = m_settinglist.begin(); m_settinglist.end() != setting; ++setting)
+ {
+ if (setting->enabled())
{
- if (setting.value() == m_live->value)
+ if (setting->value() == m_live->value)
{
found_match = true;
- if (prevsetting != nullptr)
+ if (m_settinglist.end() != prevsetting)
break;
}
- prevsetting = &setting;
+ prevsetting = setting;
}
+ }
// if we didn't find a matching value, select the first
if (!found_match)
{
- for (prevsetting = m_settinglist.first(); prevsetting != nullptr; prevsetting = prevsetting->next())
- if (prevsetting->enabled())
- break;
+ prevsetting = m_settinglist.begin();
+ while ((m_settinglist.end() != prevsetting) && !prevsetting->enabled())
+ ++prevsetting;
}
// update the value to the previous one
- if (prevsetting != nullptr)
+ if (m_settinglist.end() != prevsetting)
m_live->value = prevsetting->value();
}
@@ -1016,7 +1161,8 @@ bool ioport_field::has_next_setting() const
// scan the list of settings looking for a match on the current value
bool found = false;
- for (ioport_setting &setting : m_settinglist)
+ for (ioport_setting const &setting : m_settinglist)
+ {
if (setting.enabled())
{
if (found)
@@ -1024,7 +1170,7 @@ bool ioport_field::has_next_setting() const
if (setting.value() == m_live->value)
found = true;
}
-
+ }
return false;
}
@@ -1040,27 +1186,29 @@ void ioport_field::select_next_setting()
assert(!m_settinglist.empty());
// scan the list of settings looking for a match on the current value
- ioport_setting *nextsetting = nullptr;
- ioport_setting *setting;
- for (setting = m_settinglist.first(); setting != nullptr; setting = setting->next())
- if (setting->enabled())
- if (setting->value() == m_live->value)
- break;
+ auto setting = m_settinglist.begin();
+ while ((m_settinglist.end() != setting) && (!setting->enabled() || (setting->value() != m_live->value)))
+ ++setting;
// if we found one, scan forward for the next valid one
- if (setting != nullptr)
- for (nextsetting = setting->next(); nextsetting != nullptr; nextsetting = nextsetting->next())
- if (nextsetting->enabled())
- break;
+ auto nextsetting = setting;
+ if (m_settinglist.end() != nextsetting)
+ {
+ ++nextsetting;
+ while ((m_settinglist.end() != nextsetting) && !nextsetting->enabled())
+ ++nextsetting;
+ }
// if we hit the end, search from the beginning
- if (nextsetting == nullptr)
- for (nextsetting = m_settinglist.first(); nextsetting != nullptr; nextsetting = nextsetting->next())
- if (nextsetting->enabled())
- break;
+ if (m_settinglist.end() == nextsetting)
+ {
+ nextsetting = m_settinglist.begin();
+ while ((m_settinglist.end() != nextsetting) && !nextsetting->enabled())
+ ++nextsetting;
+ }
// update the value to the previous one
- if (nextsetting != nullptr)
+ if (m_settinglist.end() != nextsetting)
m_live->value = nextsetting->value();
}
@@ -1098,19 +1246,6 @@ void ioport_field::frame_update(ioport_value &result)
// if the state changed, look for switch down/switch up
bool curstate = m_digital_value || machine().input().seq_pressed(seq());
- if (m_live->autofire && !machine().ioport().get_autofire_toggle())
- {
- if (curstate)
- {
- if (m_live->autopressed > machine().ioport().get_autofire_delay())
- m_live->autopressed = 0;
- else if (m_live->autopressed > machine().ioport().get_autofire_delay() / 2)
- curstate = false;
- m_live->autopressed++;
- }
- else
- m_live->autopressed = 0;
- }
bool changed = false;
if (curstate != m_live->last)
{
@@ -1139,7 +1274,7 @@ void ioport_field::frame_update(ioport_value &result)
// toggle controls: flip the toggle state or advance to the next setting
if (m_live->toggle)
{
- if (m_settinglist.count() == 0)
+ if (m_settinglist.empty())
m_live->value ^= m_mask;
else
select_next_setting();
@@ -1192,17 +1327,17 @@ void ioport_field::frame_update(ioport_value &result)
//-------------------------------------------------
-// crosshair_position - compute the crosshair
+// crosshair_read - compute the crosshair
// position
//-------------------------------------------------
-void ioport_field::crosshair_position(float &x, float &y, bool &gotx, bool &goty)
+float ioport_field::crosshair_read() const
{
- double value = m_live->analog->crosshair_read();
+ float value = m_live->analog->crosshair_read();
// apply the scale and offset
if (m_crosshair_scale < 0)
- value = -(1.0 - value) * m_crosshair_scale;
+ value = -(1.0f - value) * m_crosshair_scale;
else
value *= m_crosshair_scale;
value += m_crosshair_offset;
@@ -1211,29 +1346,7 @@ void ioport_field::crosshair_position(float &x, float &y, bool &gotx, bool &goty
if (!m_crosshair_mapper.isnull())
value = m_crosshair_mapper(value);
- // handle X axis
- if (m_crosshair_axis == CROSSHAIR_AXIS_X)
- {
- x = value;
- gotx = true;
- if (m_crosshair_altaxis != 0)
- {
- y = m_crosshair_altaxis;
- goty = true;
- }
- }
-
- // handle Y axis
- else
- {
- y = value;
- goty = true;
- if (m_crosshair_altaxis != 0)
- {
- x = m_crosshair_altaxis;
- gotx = true;
- }
- }
+ return value;
}
@@ -1243,82 +1356,85 @@ void ioport_field::crosshair_position(float &x, float &y, bool &gotx, bool &goty
// descriptions
//-------------------------------------------------
-void ioport_field::expand_diplocation(const char *location, std::string &errorbuf)
+void ioport_field::expand_diplocation(const char *location, std::ostream &errorbuf)
{
// if nothing present, bail
- if (location == nullptr)
+ if (!location)
return;
- m_diploclist.reset();
+ m_diploclist.clear();
// parse the string
- std::string name; // Don't move this variable inside the loop, lastname's lifetime depends on it being outside
- const char *lastname = nullptr;
+ std::string_view lastname;
const char *curentry = location;
int entries = 0;
- while (*curentry != 0)
+ while (*curentry)
{
// find the end of this entry
const char *comma = strchr(curentry, ',');
- if (comma == nullptr)
+ if (!comma)
comma = curentry + strlen(curentry);
// extract it to tempbuf
- std::string tempstr;
- tempstr.assign(curentry, comma - curentry);
+ std::string_view tempstr(curentry, comma - curentry);
// first extract the switch name if present
- const char *number = tempstr.c_str();
- const char *colon = strchr(tempstr.c_str(), ':');
+ std::string_view::size_type number = 0;
+ std::string_view::size_type const colon = tempstr.find(':');
- // allocate and copy the name if it is present
- if (colon != nullptr)
+ std::string_view name;
+ if (colon != std::string_view::npos)
{
- lastname = name.assign(number, colon - number).c_str();
+ // allocate and copy the name if it is present
+ lastname = tempstr.substr(0, colon);
number = colon + 1;
+ if (lastname.empty())
+ {
+ util::stream_format(errorbuf, "Switch location '%s' has empty switch name!\n", location);
+ lastname = "UNK";
+ }
+ name = lastname;
}
-
- // otherwise, just copy the last name
else
{
- if (lastname == nullptr)
+ // otherwise, just copy the last name
+ if (lastname.empty())
{
- errorbuf.append(string_format("Switch location '%s' missing switch name!\n", location));
- lastname = (char *)"UNK";
+ util::stream_format(errorbuf, "Switch location '%s' missing switch name!\n", location);
+ lastname = "UNK";
}
- name.assign(lastname);
+ name = lastname;
}
// if the number is preceded by a '!' it's active high
- bool invert = false;
- if (*number == '!')
- {
- invert = true;
- number++;
- }
+ bool const invert = tempstr[number] == '!';
+ if (invert)
+ ++number;
// now scan the switch number
int swnum = -1;
- if (sscanf(number, "%d", &swnum) != 1)
- errorbuf.append(string_format("Switch location '%s' has invalid format!\n", location));
+ if (sscanf(&tempstr[number], "%d", &swnum) != 1)
+ util::stream_format(errorbuf, "Switch location '%s' has invalid format!\n", location);
+ else if (0 >= swnum)
+ util::stream_format(errorbuf, "Switch location '%s' has switch number that is not positive!\n", location);
// allocate a new entry
- m_diploclist.append(*global_alloc(ioport_diplocation(name.c_str(), swnum, invert)));
+ if (0 < swnum)
+ m_diploclist.emplace_back(name, swnum, invert);
entries++;
// advance to the next item
curentry = comma;
- if (*curentry != 0)
+ if (*curentry)
curentry++;
}
// then verify the number of bits in the mask matches
- ioport_value temp;
- int bits;
- for (bits = 0, temp = m_mask; temp != 0 && bits < 32; bits++)
- temp &= temp - 1;
- if (bits != entries)
- errorbuf.append(string_format("Switch location '%s' does not describe enough bits for mask %X\n", location, m_mask));
+ int const bits = population_count_32(m_mask);
+ if (bits > entries)
+ util::stream_format(errorbuf, "Switch location '%s' does not describe enough bits for mask %X\n", location, m_mask);
+ else if (bits < entries)
+ util::stream_format(errorbuf, "Switch location '%s' describes too many bits for mask %X\n", location, m_mask);
}
@@ -1329,9 +1445,9 @@ void ioport_field::expand_diplocation(const char *location, std::string &errorbu
void ioport_field::init_live_state(analog_field *analog)
{
// resolve callbacks
- m_read.bind_relative_to(device());
- m_write.bind_relative_to(device());
- m_crosshair_mapper.bind_relative_to(device());
+ m_read.resolve();
+ m_write.resolve();
+ m_crosshair_mapper.resolve();
// allocate live state
m_live = std::make_unique<ioport_field_live>(*this, analog);
@@ -1352,17 +1468,15 @@ void ioport_field::init_live_state(analog_field *analog)
// ioport_field_live - constructor
//-------------------------------------------------
-ioport_field_live::ioport_field_live(ioport_field &field, analog_field *analog)
- : analog(analog),
- joystick(nullptr),
- value(field.defvalue()),
- impulse(0),
- last(0),
- toggle(field.toggle()),
- joydir(digital_joystick::JOYDIR_COUNT),
- autofire(false),
- autopressed(0),
- lockout(false)
+ioport_field_live::ioport_field_live(ioport_field &field, analog_field *analog) :
+ analog(analog),
+ joystick(nullptr),
+ value(field.defvalue()),
+ impulse(0),
+ last(0),
+ toggle(field.toggle()),
+ joydir(digital_joystick::JOYDIR_COUNT),
+ lockout(false)
{
// fill in the basic values
for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
@@ -1379,7 +1493,7 @@ ioport_field_live::ioport_field_live(ioport_field &field, analog_field *analog)
if (field.type_class() == INPUT_CLASS_KEYBOARD && field.specific_name() == nullptr)
{
// loop through each character on the field
- for (int which = 0; which < 4; which++)
+ for (int which = 0; which < (1 << (UCHAR_SHIFT_END - UCHAR_SHIFT_BEGIN + 1)); which++)
{
std::vector<char32_t> const codes = field.keyboard_codes(which);
if (codes.empty())
@@ -1387,12 +1501,16 @@ ioport_field_live::ioport_field_live(ioport_field &field, analog_field *analog)
name.append(string_format("%-*s ", std::max(SPACE_COUNT - 1, 0), field.key_name(which)));
}
- // trim extra spaces
- strtrimspace(name);
-
// special case
if (name.empty())
name.assign("Unnamed Key");
+ else
+ {
+ // trim extra spaces
+ auto pos = name.find_last_not_of(' ');
+ assert(pos < name.size());
+ name.erase(pos + 1);
+ }
}
}
@@ -1406,12 +1524,12 @@ ioport_field_live::ioport_field_live(ioport_field &field, analog_field *analog)
// ioport_port - constructor
//-------------------------------------------------
-ioport_port::ioport_port(device_t &owner, const char *tag)
- : m_next(nullptr),
- m_device(owner),
- m_tag(tag),
- m_modcount(0),
- m_active(0)
+ioport_port::ioport_port(device_t &owner, const char *tag) :
+ m_next(nullptr),
+ m_device(owner),
+ m_tag(tag),
+ m_modcount(0),
+ m_active(0)
{
}
@@ -1513,7 +1631,7 @@ void ioport_port::frame_update()
m_live->digital = 0;
// now loop back and modify based on the inputs
- for (ioport_field &field : fields())
+ for (ioport_field &field : m_fieldlist)
field.frame_update(m_live->digital);
}
@@ -1523,7 +1641,7 @@ void ioport_port::frame_update()
// wholly overlapped by other fields
//-------------------------------------------------
-void ioport_port::collapse_fields(std::string &errorbuf)
+void ioport_port::collapse_fields(std::ostream &errorbuf)
{
ioport_value maskbits = 0;
int lastmodcount = -1;
@@ -1552,13 +1670,13 @@ void ioport_port::collapse_fields(std::string &errorbuf)
// for errors
//-------------------------------------------------
-void ioport_port::insert_field(ioport_field &newfield, ioport_value &disallowedbits, std::string &errorbuf)
+void ioport_port::insert_field(ioport_field &newfield, ioport_value &disallowedbits, std::ostream &errorbuf)
{
// verify against the disallowed bits, but only if we are condition-free
if (newfield.condition().none())
{
if ((newfield.mask() & disallowedbits) != 0)
- errorbuf.append(string_format("INPUT_TOKEN_FIELD specifies duplicate port bits (port=%s mask=%X)\n", tag(), newfield.mask()));
+ util::stream_format(errorbuf, "INPUT_TOKEN_FIELD specifies duplicate port bits (port=%s mask=%X)\n", tag(), newfield.mask());
disallowedbits |= newfield.mask();
}
@@ -1567,14 +1685,14 @@ void ioport_port::insert_field(ioport_field &newfield, ioport_value &disallowedb
for (ioport_field *field = m_fieldlist.first(); field != nullptr; field = nextfield)
{
nextfield = field->next();
- if ((field->mask() & newfield.mask()) != 0 &&
+ if ((field->mask() & newfield.mask()) &&
(newfield.condition().none() || field->condition().none() || field->condition() == newfield.condition()))
{
// reduce the mask of the field we found
field->reduce_mask(newfield.mask());
// if the new entry fully overrides the previous one, we nuke
- if (INPUT_PORT_OVERRIDE_FULLY_NUKES_PREVIOUS || field->mask() == 0)
+ if (!field->mask() || (INPUT_PORT_OVERRIDE_FULLY_NUKES_PREVIOUS && (field->type() != IPT_UNUSED) && (field->type() != IPT_UNKNOWN)))
m_fieldlist.remove(*field);
}
}
@@ -1630,10 +1748,10 @@ void ioport_port::update_defvalue(bool flush_defaults)
// ioport_port_live - constructor
//-------------------------------------------------
-ioport_port_live::ioport_port_live(ioport_port &port)
- : defvalue(0),
- digital(0),
- outputvalue(0)
+ioport_port_live::ioport_port_live(ioport_port &port) :
+ defvalue(0),
+ digital(0),
+ outputvalue(0)
{
// iterate over fields
for (ioport_field &field : port.fields())
@@ -1641,15 +1759,15 @@ ioport_port_live::ioport_port_live(ioport_port &port)
// allocate analog state if it's analog
analog_field *analog = nullptr;
if (field.is_analog())
- analog = &analoglist.append(*global_alloc(analog_field(field)));
+ analog = &analoglist.emplace_back(field);
// allocate a dynamic field for reading
if (field.has_dynamic_read())
- readlist.append(*global_alloc(dynamic_field(field)));
+ readlist.emplace_back(field);
// allocate a dynamic field for writing
if (field.has_dynamic_write())
- writelist.append(*global_alloc(dynamic_field(field)));
+ writelist.emplace_back(field);
// let the field initialize its live state
field.init_live_state(analog);
@@ -1666,22 +1784,18 @@ ioport_port_live::ioport_port_live(ioport_port &port)
// ioport_manager - constructor
//-------------------------------------------------
-ioport_manager::ioport_manager(running_machine &machine)
- : m_machine(machine),
- m_safe_to_read(false),
- m_last_frame_time(attotime::zero),
- m_last_delta_nsec(0),
- m_record_file(machine.options().input_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS),
- m_playback_file(machine.options().input_directory(), OPEN_FLAG_READ),
- m_playback_accumulated_speed(0),
- m_playback_accumulated_frames(0),
- m_timecode_file(machine.options().input_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS),
- m_timecode_count(0),
- m_timecode_last_time(attotime::zero),
- m_autofire_toggle(false),
- m_autofire_delay(3) // 1 seems too fast for a bunch of games
+ioport_manager::ioport_manager(running_machine &machine) :
+ m_machine(machine),
+ m_safe_to_read(false),
+ m_last_frame_time(attotime::zero),
+ m_last_delta_nsec(0),
+ m_playback_accumulated_speed(0),
+ m_playback_accumulated_frames(0),
+ m_deselected_card_config(),
+ m_applied_device_defaults(false)
{
- memset(m_type_to_entry, 0, sizeof(m_type_to_entry));
+ for (auto &entries : m_type_to_entry)
+ std::fill(std::begin(entries), std::end(entries), nullptr);
}
@@ -1700,13 +1814,18 @@ time_t ioport_manager::initialize()
init_port_types();
// if we have a token list, proceed
- device_iterator iter(machine().root_device());
- for (device_t &device : iter)
+ device_enumerator iter(machine().root_device());
{
- std::string errors;
- m_portlist.append(device, errors);
- if (!errors.empty())
- osd_printf_error("Input port errors:\n%s", errors.c_str());
+ std::ostringstream errors;
+ for (device_t &device : iter)
+ {
+ m_portlist.append(device, errors);
+ if (errors.tellp())
+ {
+ osd_printf_error("Input port errors:\n%s", std::move(errors).str());
+ errors.str("");
+ }
+ }
}
// renumber player numbers for controller ports
@@ -1719,11 +1838,14 @@ time_t ioport_manager::initialize()
if (&port.second->device() == &device)
{
for (ioport_field &field : port.second->fields())
- if (field.type_class()==INPUT_CLASS_CONTROLLER)
+ {
+ if (field.type_class() == INPUT_CLASS_CONTROLLER)
{
- if (players < field.player() + 1) players = field.player() + 1;
+ if (players < field.player() + 1)
+ players = field.player() + 1;
field.set_player(field.player() + player_offset);
}
+ }
}
}
player_offset += players;
@@ -1734,20 +1856,20 @@ time_t ioport_manager::initialize()
port.second->init_live_state();
// handle autoselection of devices
- init_autoselect_devices(IPT_AD_STICK_X, IPT_AD_STICK_Y, IPT_AD_STICK_Z, OPTION_ADSTICK_DEVICE, "analog joystick");
- init_autoselect_devices(IPT_PADDLE, IPT_PADDLE_V, 0, OPTION_PADDLE_DEVICE, "paddle");
- init_autoselect_devices(IPT_PEDAL, IPT_PEDAL2, IPT_PEDAL3, OPTION_PEDAL_DEVICE, "pedal");
- init_autoselect_devices(IPT_LIGHTGUN_X, IPT_LIGHTGUN_Y, 0, OPTION_LIGHTGUN_DEVICE, "lightgun");
- init_autoselect_devices(IPT_POSITIONAL, IPT_POSITIONAL_V, 0, OPTION_POSITIONAL_DEVICE, "positional");
- init_autoselect_devices(IPT_DIAL, IPT_DIAL_V, 0, OPTION_DIAL_DEVICE, "dial");
- init_autoselect_devices(IPT_TRACKBALL_X, IPT_TRACKBALL_Y, 0, OPTION_TRACKBALL_DEVICE, "trackball");
- init_autoselect_devices(IPT_MOUSE_X, IPT_MOUSE_Y, 0, OPTION_MOUSE_DEVICE, "mouse");
+ init_autoselect_devices({ IPT_AD_STICK_X, IPT_AD_STICK_Y, IPT_AD_STICK_Z }, OPTION_ADSTICK_DEVICE, "analog joystick");
+ init_autoselect_devices({ IPT_PADDLE, IPT_PADDLE_V }, OPTION_PADDLE_DEVICE, "paddle");
+ init_autoselect_devices({ IPT_PEDAL, IPT_PEDAL2, IPT_PEDAL3 }, OPTION_PEDAL_DEVICE, "pedal");
+ init_autoselect_devices({ IPT_LIGHTGUN_X, IPT_LIGHTGUN_Y }, OPTION_LIGHTGUN_DEVICE, "lightgun");
+ init_autoselect_devices({ IPT_POSITIONAL, IPT_POSITIONAL_V }, OPTION_POSITIONAL_DEVICE, "positional");
+ init_autoselect_devices({ IPT_DIAL, IPT_DIAL_V }, OPTION_DIAL_DEVICE, "dial");
+ init_autoselect_devices({ IPT_TRACKBALL_X, IPT_TRACKBALL_Y }, OPTION_TRACKBALL_DEVICE, "trackball");
+ init_autoselect_devices({ IPT_MOUSE_X, IPT_MOUSE_Y }, OPTION_MOUSE_DEVICE, "mouse");
// look for 4-way diagonal joysticks and change the default map if we find any
const char *joystick_map_default = machine().options().joystick_map();
if (joystick_map_default[0] == 0 || strcmp(joystick_map_default, "auto") == 0)
for (auto &port : m_portlist)
- for (ioport_field &field : port.second->fields())
+ for (ioport_field const &field : port.second->fields())
if (field.live().joystick != nullptr && field.rotated())
{
input_class_joystick &devclass = downcast<input_class_joystick &>(machine().input().device_class(DEVICE_CLASS_JOYSTICK));
@@ -1755,16 +1877,15 @@ time_t ioport_manager::initialize()
break;
}
- // initialize natural keyboard
- m_natkeyboard = std::make_unique<natural_keyboard>(machine());
-
// register callbacks for when we load configurations
- machine().configuration().config_register("input", config_load_delegate(&ioport_manager::load_config, this), config_save_delegate(&ioport_manager::save_config, this));
+ machine().configuration().config_register(
+ "input",
+ configuration_manager::load_delegate(&ioport_manager::load_config, this),
+ configuration_manager::save_delegate(&ioport_manager::save_config, this));
// open playback and record files if specified
time_t basetime = playback_init();
record_init();
- timecode_init();
return basetime;
}
@@ -1777,7 +1898,7 @@ time_t ioport_manager::initialize()
void ioport_manager::init_port_types()
{
// convert the array into a list of type states that can be modified
- construct_core_types(m_typelist);
+ emplace_core_types(m_typelist);
// ask the OSD to customize the list
machine().osd().customize_input_type_list(m_typelist);
@@ -1800,39 +1921,71 @@ void ioport_manager::init_port_types()
// in and the corresponding option
//-------------------------------------------------
-void ioport_manager::init_autoselect_devices(int type1, int type2, int type3, const char *option, const char *ananame)
+void ioport_manager::init_autoselect_devices(std::initializer_list<ioport_type> types, std::string_view option, std::string_view ananame)
{
+ static std::pair<char const *, char const *> const CLASS_OPTIONS[] = {
+ { "mouse", OPTION_MOUSE },
+ { "joystick", OPTION_JOYSTICK },
+ { "lightgun", OPTION_LIGHTGUN } };
+
// if nothing specified, ignore the option
- const char *stemp = machine().options().value(option);
- if (stemp[0] == 0 || strcmp(stemp, "none") == 0)
+ auto const autooption = machine().options().get_entry(option);
+ char const *const autoclass = autooption->value();
+ if (!autoclass || !*autoclass || !std::strcmp(autoclass, "none"))
return;
- // extract valid strings
+ // if the device class is enabled anyway or disabled at a higher priority level, do nothing
+ auto const classname = std::find_if(
+ std::begin(CLASS_OPTIONS),
+ std::end(CLASS_OPTIONS),
+ [&autoclass] (auto const &x) { return !std::strcmp(autoclass, x.first); });
+ if (std::end(CLASS_OPTIONS) != classname)
+ {
+ if (machine().options().bool_value(classname->second))
+ return;
+
+ auto const classoption = machine().options().get_entry(classname->second);
+ if (classoption->priority() > autooption->priority())
+ {
+ osd_printf_verbose("Input: Won't autoenable %s in presence of a %s as it's disabled at a higher priority\n", autoclass, ananame);
+ return;
+ }
+ }
+
+ // find matching device class
input_class *autoenable_class = nullptr;
for (input_device_class devclass = DEVICE_CLASS_FIRST_VALID; devclass <= DEVICE_CLASS_LAST_VALID; ++devclass)
- if (strcmp(stemp, machine().input().device_class(devclass).name()) == 0)
+ {
+ if (!std::strcmp(autoclass, machine().input().device_class(devclass).name()))
{
autoenable_class = &machine().input().device_class(devclass);
break;
}
- if (autoenable_class == nullptr)
+ }
+ if (!autoenable_class)
{
- osd_printf_error("Invalid %s value %s; reverting to keyboard\n", option, stemp);
+ osd_printf_error("Invalid %s value %s; reverting to keyboard\n", option, autoclass);
autoenable_class = &machine().input().device_class(DEVICE_CLASS_KEYBOARD);
}
- // only scan the list if we haven't already enabled this class of control
- if (!autoenable_class->enabled())
- for (auto &port : m_portlist)
- for (ioport_field &field : port.second->fields())
+ // nothing to do if the class is already enabled
+ if (autoenable_class->enabled())
+ return;
- // if this port type is in use, apply the autoselect criteria
- if ((type1 != 0 && field.type() == type1) || (type2 != 0 && field.type() == type2) || (type3 != 0 && field.type() == type3))
- {
- osd_printf_verbose("Input: Autoenabling %s due to presence of a %s\n", autoenable_class->name(), ananame);
- autoenable_class->enable();
- break;
- }
+ // scan the port list
+ for (auto &port : m_portlist)
+ {
+ for (ioport_field const &field : port.second->fields())
+ {
+ // if this port type is in use, apply the autoselect criteria
+ if (std::find(std::begin(types), std::end(types), field.type()) != std::end(types))
+ {
+ osd_printf_verbose("Input: Autoenabling %s due to presence of a %s\n", autoenable_class->name(), ananame);
+ autoenable_class->enable();
+ return;
+ }
+ }
+ }
}
@@ -1846,7 +1999,6 @@ void ioport_manager::exit()
// close any playback or recording files
playback_end();
record_end();
- timecode_end();
}
@@ -1864,15 +2016,21 @@ ioport_manager::~ioport_manager()
// type/player
//-------------------------------------------------
-const char *ioport_manager::type_name(ioport_type type, u8 player)
+std::string ioport_manager::type_name(ioport_type type, u8 player) const
{
+ using util::lang_translate;
+
// if we have a machine, use the live state and quick lookup
- input_type_entry *entry = m_type_to_entry[type][player];
- if (entry != nullptr && entry->name() != nullptr)
- return entry->name();
+ input_type_entry const *const entry = m_type_to_entry[type][player];
+ if (entry)
+ {
+ std::string name = entry->name();
+ if (!name.empty())
+ return name;
+ }
// if we find nothing, return a default string (not a null pointer)
- return "???";
+ return _("input-name", "???");
}
@@ -1881,7 +2039,7 @@ const char *ioport_manager::type_name(ioport_type type, u8 player)
// type/player
//-------------------------------------------------
-ioport_group ioport_manager::type_group(ioport_type type, int player)
+ioport_group ioport_manager::type_group(ioport_type type, int player) const noexcept
{
input_type_entry *entry = m_type_to_entry[type][player];
if (entry != nullptr)
@@ -1897,7 +2055,7 @@ ioport_group ioport_manager::type_group(ioport_type type, int player)
// given type/player
//-------------------------------------------------
-const input_seq &ioport_manager::type_seq(ioport_type type, int player, input_seq_type seqtype)
+const input_seq &ioport_manager::type_seq(ioport_type type, int player, input_seq_type seqtype) const noexcept
{
assert(type >= 0 && type < IPT_COUNT);
assert(player >= 0 && player < MAX_PLAYERS);
@@ -1917,11 +2075,25 @@ const input_seq &ioport_manager::type_seq(ioport_type type, int player, input_se
// the given type/player
//-------------------------------------------------
-void ioport_manager::set_type_seq(ioport_type type, int player, input_seq_type seqtype, const input_seq &newseq)
+void ioport_manager::set_type_seq(ioport_type type, int player, input_seq_type seqtype, const input_seq &newseq) noexcept
{
- input_type_entry *entry = m_type_to_entry[type][player];
- if (entry != nullptr)
- entry->m_seq[seqtype] = newseq;
+ input_type_entry *const entry = m_type_to_entry[type][player];
+ if (entry)
+ {
+ 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));
+ }
+ }
}
@@ -1941,10 +2113,10 @@ bool ioport_manager::type_pressed(ioport_type type, int player)
// ioport_type_class exists in at least one port
//-------------------------------------------------
-bool ioport_manager::type_class_present(ioport_type_class inputclass)
+bool ioport_manager::type_class_present(ioport_type_class inputclass) const noexcept
{
for (auto &port : m_portlist)
- for (ioport_field &field : port.second->fields())
+ for (ioport_field const &field : port.second->fields())
if (field.type_class() == inputclass)
return true;
return false;
@@ -1956,11 +2128,11 @@ bool ioport_manager::type_class_present(ioport_type_class inputclass)
// players
//-------------------------------------------------
-int ioport_manager::count_players() const
+int ioport_manager::count_players() const noexcept
{
int max_player = 0;
for (auto &port : m_portlist)
- for (ioport_field &field : port.second->fields())
+ for (ioport_field const &field : port.second->fields())
if (field.type_class() == INPUT_CLASS_CONTROLLER && max_player <= field.player() + 1)
max_player = field.player() + 1;
@@ -1969,30 +2141,6 @@ int ioport_manager::count_players() const
//-------------------------------------------------
-// crosshair_position - return the extracted
-// crosshair values for the given player
-//-------------------------------------------------
-
-bool ioport_manager::crosshair_position(int player, float &x, float &y)
-{
- // read all the lightgun values
- bool gotx = false, goty = false;
- for (auto &port : m_portlist)
- for (ioport_field &field : port.second->fields())
- if (field.player() == player && field.crosshair_axis() != CROSSHAIR_AXIS_NONE && field.enabled())
- {
- field.crosshair_position(x, y, gotx, goty);
-
- // if we got both, stop
- if (gotx && goty)
- break;
- }
-
- return (gotx && goty);
-}
-
-
-//-------------------------------------------------
// frame_update - core logic for per-frame input
// port updating
//-------------------------------------------------
@@ -2005,7 +2153,7 @@ digital_joystick &ioport_manager::digjoystick(int player, int number)
return joystick;
// create a new one
- return m_joystick_list.append(*global_alloc(digital_joystick(player, number)));
+ return m_joystick_list.emplace_back(player, number);
}
@@ -2028,7 +2176,7 @@ void ioport_manager::frame_update_callback()
void ioport_manager::frame_update()
{
-g_profiler.start(PROFILER_INPUT);
+ auto profile = g_profiler.start(PROFILER_INPUT);
// record/playback information about the current frame
attotime curtime = machine().time();
@@ -2065,8 +2213,6 @@ g_profiler.start(PROFILER_INPUT);
if (dynfield.field().type() != IPT_OUTPUT)
dynfield.write(newvalue);
}
-
-g_profiler.stop();
}
@@ -2092,8 +2238,19 @@ s32 ioport_manager::frame_interpolate(s32 oldval, s32 newval)
// data from the XML nodes
//-------------------------------------------------
-void ioport_manager::load_config(config_type cfg_type, util::xml::data_node const *parentnode)
+void ioport_manager::load_config(config_type cfg_type, config_level cfg_level, util::xml::data_node const *parentnode)
{
+ // make sure device defaults get applied at some point
+ if ((cfg_type > config_type::CONTROLLER) && !m_applied_device_defaults)
+ {
+ apply_device_defaults();
+
+ // after applying controller config, push that to the backup, as it's what we'll diff against
+ for (input_type_entry &entry : m_typelist)
+ for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
+ entry.defseq(seqtype) = entry.seq(seqtype);
+ }
+
// in the completion phase, we finish the initialization with the final ports
if (cfg_type == config_type::FINAL)
{
@@ -2102,32 +2259,39 @@ void ioport_manager::load_config(config_type cfg_type, util::xml::data_node cons
}
// early exit if no data to parse
- if (parentnode == nullptr)
+ if (!parentnode)
return;
- // iterate over all the remap nodes for controller configs only
- if (cfg_type == config_type::CONTROLLER)
- load_remap_table(parentnode);
-
// load device map table for controller configs only
if (cfg_type == config_type::CONTROLLER)
{
- std::unique_ptr<devicemap_table_type> devicemap_table = std::make_unique<devicemap_table_type>();
+ // iterate over device remapping entries
+ input_manager::devicemap_table devicemap;
for (util::xml::data_node const *mapdevice_node = parentnode->get_child("mapdevice"); mapdevice_node != nullptr; mapdevice_node = mapdevice_node->get_next_sibling("mapdevice"))
{
- const char *devicename = mapdevice_node->get_attribute_string("device", nullptr);
- const char *controllername = mapdevice_node->get_attribute_string("controller", nullptr);
- if (devicename != nullptr && controllername != nullptr)
- {
- devicemap_table->insert(std::make_pair(std::string(devicename), std::string(controllername)));
- }
+ char const *const devicename = mapdevice_node->get_attribute_string("device", nullptr);
+ char const *const controllername = mapdevice_node->get_attribute_string("controller", nullptr);
+ if (devicename && controllername)
+ devicemap.emplace(devicename, controllername);
}
- // map device to controller if we have a device map
- if (!devicemap_table->empty())
+ // we can't rearrange controllers after applying device-supplied defaults
+ if (!m_applied_device_defaults)
{
- machine().input().map_device_to_controller(devicemap_table.get());
+ // map device to controller if we have a device map
+ if (!devicemap.empty())
+ machine().input().map_device_to_controller(devicemap);
+
+ // add extra default assignments for input devices
+ apply_device_defaults();
+ }
+ else if (!devicemap.empty())
+ {
+ osd_printf_warning("Controller configuration: Only <mapdevice> elements from the first applicable <system> element are applied\n");
}
+
+ // iterate over any input code remapping nodes
+ load_remap_table(*parentnode);
}
// iterate over all the port nodes
@@ -2138,37 +2302,106 @@ void ioport_manager::load_config(config_type cfg_type, util::xml::data_node cons
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"))
{
// with a valid type, parse out the new sequence
input_seq_type seqtype = token_to_seq_type(seqnode->get_attribute_string("type", ""));
- if (seqtype != -1 && seqnode->get_value() != nullptr)
+ if ((seqtype != -1) && seqnode->get_value())
{
- if (strcmp(seqnode->get_value(), "NONE") == 0)
- newseq[seqtype].set();
+ if (!strcmp(seqnode->get_value(), "NONE"))
+ 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());
+ if (config_type::CONTROLLER != cfg_type)
+ newseq[seqtype].second = seqnode->get_value();
}
}
- // if we're loading default ports, apply to the defaults
- if (cfg_type != config_type::GAME)
- load_default_config(portnode, type, player, newseq);
+ // 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))
+ load_controller_config(*portnode, type, player, newseq);
else
- load_game_config(portnode, type, player, newseq);
+ load_default_config(type, player, newseq);
}
- // after applying the controller config, push that back into the backup, since that is
- // what we will diff against
if (cfg_type == config_type::CONTROLLER)
+ {
+ // after applying the controller config, push that back into the backup, since that is what we will diff against
for (input_type_entry &entry : m_typelist)
for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
entry.defseq(seqtype) = entry.seq(seqtype);
+ }
+ else if (cfg_type == config_type::SYSTEM)
+ {
+ // load keyboard enable/disable state
+ std::vector<bool> kbd_enable_set;
+ bool keyboard_enabled = false, missing_enabled = false;
+ natural_keyboard &natkbd = machine().natkeyboard();
+ for (util::xml::data_node const *kbdnode = parentnode->get_child("keyboard"); kbdnode; kbdnode = kbdnode->get_next_sibling("keyboard"))
+ {
+ char const *const tag = kbdnode->get_attribute_string("tag", nullptr);
+ int const enabled = kbdnode->get_attribute_int("enabled", -1);
+ if (tag && (0 <= enabled))
+ {
+ size_t i;
+ for (i = 0; natkbd.keyboard_count() > i; ++i)
+ {
+ if (!strcmp(natkbd.keyboard_device(i).tag(), tag))
+ {
+ if (kbd_enable_set.empty())
+ kbd_enable_set.resize(natkbd.keyboard_count(), false);
+ kbd_enable_set[i] = true;
+ if (enabled)
+ {
+ if (!natkbd.keyboard_is_keypad(i))
+ keyboard_enabled = true;
+ natkbd.enable_keyboard(i);
+ }
+ else
+ {
+ natkbd.disable_keyboard(i);
+ }
+ break;
+ }
+ }
+ missing_enabled = missing_enabled || (enabled && (natkbd.keyboard_count() <= i));
+ }
+ }
+
+ // if keyboard enable configuration was loaded, patch it up for principle of least surprise
+ if (!kbd_enable_set.empty())
+ {
+ for (size_t i = 0; natkbd.keyboard_count() > i; ++i)
+ {
+ if (!natkbd.keyboard_is_keypad(i))
+ {
+ if (!keyboard_enabled && missing_enabled)
+ {
+ natkbd.enable_keyboard(i);
+ keyboard_enabled = true;
+ }
+ else if (!kbd_enable_set[i])
+ {
+ if (keyboard_enabled)
+ natkbd.disable_keyboard(i);
+ else
+ natkbd.enable_keyboard(i);
+ keyboard_enabled = true;
+ }
+ }
+ }
+ }
+ }
}
@@ -2177,11 +2410,11 @@ void ioport_manager::load_config(config_type cfg_type, util::xml::data_node cons
// global remapping table
//-------------------------------------------------
-void ioport_manager::load_remap_table(util::xml::data_node const *parentnode)
+void ioport_manager::load_remap_table(util::xml::data_node const &parentnode)
{
// count items first so we can allocate
int count = 0;
- for (util::xml::data_node const *remapnode = parentnode->get_child("remap"); remapnode != nullptr; remapnode = remapnode->get_next_sibling("remap"))
+ for (util::xml::data_node const *remapnode = parentnode.get_child("remap"); remapnode != nullptr; remapnode = remapnode->get_next_sibling("remap"))
count++;
// if we have some, deal with them
@@ -2193,7 +2426,7 @@ void ioport_manager::load_remap_table(util::xml::data_node const *parentnode)
// build up the remap table
count = 0;
- for (util::xml::data_node const *remapnode = parentnode->get_child("remap"); remapnode != nullptr; remapnode = remapnode->get_next_sibling("remap"))
+ for (util::xml::data_node const *remapnode = parentnode.get_child("remap"); remapnode != nullptr; remapnode = remapnode->get_next_sibling("remap"))
{
input_code origcode = machine().input().code_from_token(remapnode->get_attribute_string("origcode", ""));
input_code newcode = machine().input().code_from_token(remapnode->get_attribute_string("newcode", ""));
@@ -2208,89 +2441,320 @@ void ioport_manager::load_remap_table(util::xml::data_node const *parentnode)
// loop over the remapping table, then over default ports, replacing old with new
for (int remapnum = 0; remapnum < count; remapnum++)
for (input_type_entry &entry : m_typelist)
- for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
- entry.m_seq[seqtype].replace(oldtable[remapnum], newtable[remapnum]);
+ entry.replace_code(oldtable[remapnum], newtable[remapnum]);
}
}
//-------------------------------------------------
-// load_default_config - apply configuration
-// data to the default mappings
+// load_default_config - apply input settings
+// to defaults for all systems
//-------------------------------------------------
-bool ioport_manager::load_default_config(util::xml::data_node const *portnode, int type, int player, const input_seq *newseq)
+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)
+ {
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.m_seq[seqtype] = newseq[seqtype];
+ {
+ if (input_seq_good(machine(), newseq[seqtype].first))
+ entry.set_seq(seqtype, newseq[seqtype].first);
+ entry.set_cfg(seqtype, newseq[seqtype].second);
+ }
return true;
}
-
+ }
return false;
}
//-------------------------------------------------
-// load_game_config - apply configuration
-// data to the current set of input ports
+// load_controller_config - apply controler
+// profile settings to defaults
//-------------------------------------------------
-bool ioport_manager::load_game_config(util::xml::data_node const *portnode, int type, int player, const input_seq *newseq)
+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])
{
- // read the mask, index, and defvalue attributes
- const char *tag = portnode->get_attribute_string("tag", nullptr);
- ioport_value mask = portnode->get_attribute_int("mask", 0);
- ioport_value defvalue = portnode->get_attribute_int("defvalue", 0);
+ // without a tag, apply to the defaults for all systems
+ char const *const tag = portnode.get_attribute_string("tag", nullptr);
+ if (!tag)
+ return load_default_config(type, player, newseq);
+
+ // ensure the port actually exists
+ auto const port(m_portlist.find(tag));
+ if (m_portlist.end() == port)
+ return false;
+ ioport_value const mask = portnode.get_attribute_int("mask", 0);
+ if (!mask)
+ return false;
+
+ // find the matching field
+ ioport_value const defvalue = portnode.get_attribute_int("defvalue", 0);
+ bool matched = false;
+ for (ioport_field &field : port->second->fields())
+ {
+ // find the matching mask and default value
+ if (field.type() == type && field.player() == player &&
+ field.mask() == mask && (field.defvalue() & mask) == (defvalue & mask))
+ {
+ // 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 (input_seq_good(machine(), newseq[seqtype].first))
+ {
+ field.live().seq[seqtype] = newseq[seqtype].first;
+ field.set_defseq(seqtype, newseq[seqtype].first);
+ }
+ }
+
+ // fetch configurable attributes
+ if (!field.live().analog)
+ {
+ // for non-analog fields
+
+ // can't practically set value here
+
+ // fetch yes/no for toggle setting
+ char const *const togstring = portnode.get_attribute_string("toggle", nullptr);
+ if (togstring && !strcmp(togstring, "yes"))
+ {
+ field.live().toggle = true;
+ field.m_flags |= ioport_field::FIELD_FLAG_TOGGLE;
+ }
+ else if (togstring && !strcmp(togstring, "no"))
+ {
+ field.live().toggle = false;
+ field.m_flags &= ~ioport_field::FIELD_FLAG_TOGGLE;
+ }
+ }
+ else
+ {
+ // for analog fields
+
+#if 0 // changing this stuff causes issues because of the way it's tied up with the analog_field object
+ // get base attributes
+ field.live().analog->m_delta = field.m_delta = portnode.get_attribute_int("keydelta", field.delta());
+ field.live().analog->m_centerdelta = field.m_centerdelta = portnode.get_attribute_int("centerdelta", field.centerdelta());
+ field.live().analog->m_sensitivity = field.m_sensitivity = portnode.get_attribute_int("sensitivity", field.sensitivity());
+
+ // fetch yes/no for reverse setting
+ char const *const revstring = portnode.get_attribute_string("reverse", nullptr);
+ if (revstring && !strcmp(revstring, "yes"))
+ {
+ field.live().analog->m_reverse = true;
+ field.m_flags |= ioport_field::ANALOG_FLAG_REVERSE;
+ }
+ else if (revstring && !strcmp(revstring, "no"))
+ {
+ field.live().analog->m_reverse = false;
+ field.m_flags &= ~ioport_field::ANALOG_FLAG_REVERSE;
+ }
+#endif
+ }
+
+ // only break out of the loop for unconditional inputs
+ if (field.condition().condition() == ioport_condition::ALWAYS)
+ return true;
+ else
+ matched = true;
+ }
+ }
+
+ // return whether any field matched
+ return matched;
+}
- // find the port we want; if no tag, search them all
- for (auto &port : m_portlist)
- if (tag == nullptr || strcmp(port.second->tag(), tag) == 0)
- for (ioport_field &field : port.second->fields())
- // find the matching mask and defvalue
- if (field.type() == type && field.player() == player &&
- field.mask() == mask && (field.defvalue() & mask) == (defvalue & mask))
+//-------------------------------------------------
+// load_system_config - apply saved input
+// configuration for the current system
+//-------------------------------------------------
+
+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);
+ ioport_value const mask = portnode.get_attribute_int("mask", 0);
+ ioport_value const defvalue = portnode.get_attribute_int("defvalue", 0);
+ if (!tag || !mask)
+ return;
+
+ // find the port we want
+ auto const port(m_portlist.find(tag));
+ if (m_portlist.end() != port)
+ {
+ for (ioport_field &field : port->second->fields())
+ {
+ // find the matching mask and default value
+ if (field.type() == type && field.player() == player &&
+ field.mask() == mask && (field.defvalue() & mask) == (defvalue & mask))
+ {
+ // if a sequence was specified, copy it in
+ for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype)
{
- // 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 (input_seq_good(machine(), newseq[seqtype].first))
+ field.live().seq[seqtype] = newseq[seqtype].first;
+ field.live().cfg[seqtype] = newseq[seqtype].second;
+ }
- // fetch configurable attributes
+ // fetch configurable attributes
+ if (!field.live().analog)
+ {
// for non-analog fields
- if (field.live().analog == nullptr)
- {
- // fetch the value
- field.live().value = portnode->get_attribute_int("value", field.defvalue());
- // fetch yes/no for toggle setting
- const char *togstring = portnode->get_attribute_string("toggle", nullptr);
- if (togstring != nullptr)
- field.live().toggle = (strcmp(togstring, "yes") == 0);
- }
+ // fetch the value
+ field.live().value = portnode.get_attribute_int("value", field.defvalue());
+ // fetch yes/no for toggle setting
+ char const *const togstring = portnode.get_attribute_string("toggle", nullptr);
+ if (togstring && !strcmp(togstring, "yes"))
+ field.live().toggle = true;
+ else if (togstring && !strcmp(togstring, "no"))
+ field.live().toggle = false;
+ }
+ else
+ {
// for analog fields
- else
+
+ // get base attributes
+ field.live().analog->m_delta = portnode.get_attribute_int("keydelta", field.delta());
+ field.live().analog->m_centerdelta = portnode.get_attribute_int("centerdelta", field.centerdelta());
+ field.live().analog->m_sensitivity = portnode.get_attribute_int("sensitivity", field.sensitivity());
+
+ // fetch yes/no for reverse setting
+ char const *const revstring = portnode.get_attribute_string("reverse", nullptr);
+ if (revstring && !strcmp(revstring, "yes"))
+ field.live().analog->m_reverse = true;
+ else if (revstring && !strcmp(revstring, "no"))
+ field.live().analog->m_reverse = false;
+ }
+
+ // only break out of the loop for unconditional inputs
+ if (field.condition().condition() == ioport_condition::ALWAYS)
+ break;
+ }
+ }
+ }
+ else
+ {
+ // see if this belongs to a slot card that isn't inserted
+ std::string_view parent_tag(tag);
+ auto pos(parent_tag.rfind(':'));
+ if (pos && (std::string_view::npos != pos))
+ {
+ parent_tag = parent_tag.substr(0, pos);
+ if (!machine().root_device().subdevice(parent_tag))
+ {
+ for (pos = parent_tag.rfind(':'); pos && (std::string_view::npos != pos); pos = parent_tag.rfind(':'))
+ {
+ std::string_view const child_tag(parent_tag.substr(pos + 1));
+ parent_tag = parent_tag.substr(0, pos);
+ device_t const *const parent_device(machine().root_device().subdevice(parent_tag));
+ if (parent_device)
{
- // get base attributes
- field.live().analog->m_delta = portnode->get_attribute_int("keydelta", field.delta());
- field.live().analog->m_centerdelta = portnode->get_attribute_int("centerdelta", field.centerdelta());
- field.live().analog->m_sensitivity = portnode->get_attribute_int("sensitivity", field.sensitivity());
-
- // fetch yes/no for reverse setting
- const char *revstring = portnode->get_attribute_string("reverse", nullptr);
- if (revstring != nullptr)
- field.live().analog->m_reverse = (strcmp(revstring, "yes") == 0);
+ device_slot_interface const *slot;
+ if (parent_device->interface(slot) && (slot->option_list().find(std::string(child_tag)) != slot->option_list().end()))
+ {
+ if (!m_deselected_card_config)
+ m_deselected_card_config = util::xml::file::create();
+ portnode.copy_into(*m_deselected_card_config);
+ }
+ break;
}
- return true;
}
+ }
+ }
+ }
+}
- return false;
+
+//-------------------------------------------------
+// apply_device_defaults - add default assignments
+// supplied by input devices
+//-------------------------------------------------
+
+void ioport_manager::apply_device_defaults()
+{
+ // make sure this only happens once
+ assert(!m_applied_device_defaults);
+ m_applied_device_defaults = true;
+
+ // TODO: come up with a way to deal with non-multi device classes here?
+ for (input_device_class classno = DEVICE_CLASS_FIRST_VALID; DEVICE_CLASS_LAST_VALID >= classno; ++classno)
+ {
+ input_class &devclass = machine().input().device_class(classno);
+ for (int devnum = 0; devclass.maxindex() >= devnum; ++devnum)
+ {
+ // make sure device exists
+ input_device const *const device = devclass.device(devnum);
+ if (!device)
+ continue;
+
+ // iterate over default assignments
+ for (auto [porttype, seqtype, seq] : device->default_assignments())
+ {
+ assert(!seq.empty());
+ assert(seq.is_valid());
+ assert(!seq.is_default());
+
+ // only apply UI assignments for first device in a class
+ if ((IPT_UI_FIRST < porttype) && (IPT_UI_LAST > porttype) && (device->devindex() != 0))
+ continue;
+
+ // limit to maximum player count
+ if (device->devindex() >= MAX_PLAYERS)
+ continue;
+
+ // find a matching port in the list
+ auto const found = std::find_if(
+ m_typelist.begin(),
+ m_typelist.end(),
+ [type = porttype, device] (input_type_entry const &entry)
+ {
+ return (entry.type() == type) && (entry.player() == device->devindex());
+ });
+ if (m_typelist.end() == found)
+ continue;
+
+ // start with the current setting
+ input_seq remapped(found->seq(seqtype));
+ if (!remapped.empty())
+ remapped += input_seq::or_code;
+
+ // append adjusting the device index
+ for (int i = 0, len = seq.length(); i < len; ++i)
+ {
+ input_code code = seq[i];
+ if (!code.internal())
+ {
+ assert(code.device_class() == classno);
+ assert(code.device_index() == 0);
+ assert(code.item_id() >= ITEM_ID_FIRST_VALID);
+ assert(code.item_id() <= ITEM_ID_ABSOLUTE_MAXIMUM);
+ code.set_device_index(device->devindex());
+ }
+ remapped += code;
+ }
+
+ // apply to the entry
+ found->set_seq(seqtype, remapped);
+ }
+ }
+ }
}
@@ -2307,39 +2771,18 @@ bool ioport_manager::load_game_config(util::xml::data_node const *portnode, int
void ioport_manager::save_config(config_type cfg_type, util::xml::data_node *parentnode)
{
// if no parentnode, ignore
- if (parentnode == nullptr)
+ if (!parentnode)
return;
// default ports save differently
if (cfg_type == config_type::DEFAULT)
save_default_inputs(*parentnode);
- else
+ else if (cfg_type == config_type::SYSTEM)
save_game_inputs(*parentnode);
}
//-------------------------------------------------
-// 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
//-------------------------------------------------
@@ -2377,7 +2820,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
@@ -2385,15 +2828,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]);
+ }
+ }
}
}
}
@@ -2408,30 +2857,38 @@ void ioport_manager::save_default_inputs(util::xml::data_node &parentnode)
void ioport_manager::save_game_inputs(util::xml::data_node &parentnode)
{
+ // save keyboard enable/disable state
+ natural_keyboard &natkbd = machine().natkeyboard();
+ for (size_t i = 0; natkbd.keyboard_count() > i; ++i)
+ {
+ util::xml::data_node *const kbdnode = parentnode.add_child("keyboard", nullptr);
+ kbdnode->set_attribute("tag", natkbd.keyboard_device(i).tag());
+ kbdnode->set_attribute_int("enabled", natkbd.keyboard_enabled(i));
+ }
+
// iterate over ports
for (auto &port : m_portlist)
- for (ioport_field &field : port.second->fields())
- if (save_this_input_field_type(field.type()))
+ for (ioport_field const &field : port.second->fields())
+ if (save_this_input_field_type(field.type()) && field.enabled())
{
// 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();
- // non-analog changes
if (!field.is_analog())
{
- changed |= ((field.live().value & field.mask()) != (field.defvalue() & field.mask()));
- changed |= (field.live().toggle != field.toggle());
+ // non-analog changes
+ changed = changed || ((field.live().value & field.mask()) != (field.defvalue() & field.mask()));
+ changed = changed || (field.live().toggle != field.toggle());
}
-
- // analog changes
else
{
- 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());
+ // analog changes
+ 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
@@ -2439,7 +2896,7 @@ void ioport_manager::save_game_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 identifying information and attributes
portnode->set_attribute("tag", port.second->tag());
@@ -2449,21 +2906,26 @@ 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]);
+ }
+ }
- // write out non-analog changes
if (!field.is_analog())
{
+ // write out non-analog changes
if ((field.live().value & field.mask()) != (field.defvalue() & field.mask()))
portnode->set_attribute_int("value", field.live().value & field.mask());
if (field.live().toggle != field.toggle())
portnode->set_attribute("toggle", field.live().toggle ? "yes" : "no");
}
-
- // write out analog changes
else
{
+ // write out analog changes
if (field.live().analog->m_delta != field.delta())
portnode->set_attribute_int("keydelta", field.live().analog->m_delta);
if (field.live().analog->m_centerdelta != field.centerdelta())
@@ -2476,6 +2938,13 @@ void ioport_manager::save_game_inputs(util::xml::data_node &parentnode)
}
}
}
+
+ // preserve configuration for deselected slot cards
+ if (m_deselected_card_config)
+ {
+ for (util::xml::data_node const *node = m_deselected_card_config->get_first_child(); node; node = node->get_next_sibling())
+ node->copy_into(parentnode);
+ }
}
@@ -2489,27 +2958,34 @@ void ioport_manager::save_game_inputs(util::xml::data_node &parentnode)
// file
//-------------------------------------------------
-template<typename _Type>
-_Type ioport_manager::playback_read(_Type &result)
+template<typename Type>
+Type ioport_manager::playback_read(Type &result)
{
// protect against nullptr handles if previous reads fail
- if (!m_playback_file.is_open())
- result = 0;
+ if (!m_playback_stream)
+ return result = Type(0);
// read the value; if we fail, end playback
- else if (m_playback_file.read(&result, sizeof(result)) != sizeof(result))
+ auto const [err, actual] = read(*m_playback_stream, &result, sizeof(result));
+ if (err)
+ {
+ playback_end("Read error");
+ return result = Type(0);
+ }
+ else if (sizeof(result) != actual)
{
playback_end("End of file");
- result = 0;
+ return result = Type(0);
}
- // return the appropriate value
- else if (sizeof(result) == 8)
+ // normalize byte order
+ if (sizeof(result) == 8)
result = little_endianize_int64(result);
else if (sizeof(result) == 4)
result = little_endianize_int32(result);
else if (sizeof(result) == 2)
result = little_endianize_int16(result);
+
return result;
}
@@ -2534,19 +3010,20 @@ time_t ioport_manager::playback_init()
return 0;
// open the playback file
- osd_file::error filerr = m_playback_file.open(filename);
+ m_playback_file = std::make_unique<emu_file>(machine().options().input_directory(), OPEN_FLAG_READ);
+ std::error_condition const filerr = m_playback_file->open(filename);
// return an explicit error if file isn't found in given path
- if(filerr == osd_file::error::NOT_FOUND)
+ if (filerr == std::errc::no_such_file_or_directory)
fatalerror("Input file %s not found\n",filename);
// TODO: bail out any other error laconically for now
- if(filerr != osd_file::error::NONE)
- fatalerror("Failed to open file %s for playback (code error=%d)\n",filename,int(filerr));
+ if (filerr)
+ fatalerror("Failed to open file %s for playback (%s:%d %s)\n", filename, filerr.category().name(), filerr.value(), filerr.message());
// read the header and verify that it is a modern version; if not, print an error
inp_header header;
- if (!header.read(m_playback_file))
+ if (!header.read(*m_playback_file))
fatalerror("Input file is corrupt or invalid (missing header)\n");
if (!header.check_magic())
fatalerror("Input file invalid or in an older, unsupported format\n");
@@ -2558,15 +3035,15 @@ time_t ioport_manager::playback_init()
osd_printf_info("INP version %u.%u\n", header.get_majversion(), header.get_minversion());
time_t basetime = header.get_basetime();
osd_printf_info("Created %s\n", ctime(&basetime));
- osd_printf_info("Recorded using %s\n", header.get_appdesc().c_str());
+ osd_printf_info("Recorded using %s\n", header.get_appdesc());
// verify the header against the current game
std::string const sysname = header.get_sysname();
if (sysname != machine().system().name)
- osd_printf_info("Input file is for machine '%s', not for current machine '%s'\n", sysname.c_str(), machine().system().name);
+ osd_printf_info("Input file is for machine '%s', not for current machine '%s'\n", sysname, machine().system().name);
// enable compression
- m_playback_file.compress(FCOMPRESS_MEDIUM);
+ m_playback_stream = util::zlib_read(*m_playback_file, 16386);
return basetime;
}
@@ -2578,10 +3055,11 @@ time_t ioport_manager::playback_init()
void ioport_manager::playback_end(const char *message)
{
// only applies if we have a live file
- if (m_playback_file.is_open())
+ if (m_playback_stream)
{
// close the file
- m_playback_file.close();
+ m_playback_stream.reset();
+ m_playback_file.reset();
// pop a message
if (message != nullptr)
@@ -2594,7 +3072,8 @@ void ioport_manager::playback_end(const char *message)
osd_printf_info("Average recorded speed: %d%%\n", u32((m_playback_accumulated_speed * 200 + 1) >> 21));
// close the program at the end of inp file playback
- if (machine().options().exit_after_playback()) {
+ if (machine().options().exit_after_playback())
+ {
osd_printf_info("Exiting MAME now...\n");
machine().schedule_exit();
}
@@ -2610,7 +3089,7 @@ void ioport_manager::playback_end(const char *message)
void ioport_manager::playback_frame(const attotime &curtime)
{
// if playing back, fetch the information and verify
- if (m_playback_file.is_open())
+ if (m_playback_stream)
{
// first the absolute time
seconds_t seconds_temp;
@@ -2636,7 +3115,7 @@ void ioport_manager::playback_frame(const attotime &curtime)
void ioport_manager::playback_port(ioport_port &port)
{
// if playing back, fetch information about this port
- if (m_playback_file.is_open())
+ if (m_playback_stream)
{
// read the default value and the digital state
playback_read(port.live().defvalue);
@@ -2661,16 +3140,24 @@ void ioport_manager::playback_port(ioport_port &port)
// record_write - write a value to the record file
//-------------------------------------------------
-template<typename _Type>
-void ioport_manager::record_write(_Type value)
+template<typename Type>
+void ioport_manager::record_write(Type value)
{
// protect against nullptr handles if previous reads fail
- if (!m_record_file.is_open())
+ if (!m_record_stream)
return;
- // read the value; if we fail, end playback
- if (m_record_file.write(&value, sizeof(value)) != sizeof(value))
- record_end("Out of space");
+ // normalize byte order
+ if (sizeof(value) == 8)
+ value = little_endianize_int64(value);
+ else if (sizeof(value) == 4)
+ value = little_endianize_int32(value);
+ else if (sizeof(value) == 2)
+ value = little_endianize_int16(value);
+
+ // write the value; if we fail, end recording
+ if (write(*m_record_stream, &value, sizeof(value)).first)
+ record_end("Write error");
}
template<>
@@ -2680,29 +3167,6 @@ void ioport_manager::record_write<bool>(bool value)
record_write(byte);
}
-template<typename _Type>
-void ioport_manager::timecode_write(_Type value)
-{
- // protect against nullptr handles if previous reads fail
- if (!m_timecode_file.is_open())
- return;
-
- // read the value; if we fail, end playback
- if (m_timecode_file.write(&value, sizeof(value)) != sizeof(value))
- timecode_end("Out of space");
-}
-
-/*template<>
-void ioport_manager::timecode_write<bool>(bool value)
-{
- u8 byte = u8(value);
- timecode_write(byte);
-}*/
-template<>
-void ioport_manager::timecode_write<std::string>(std::string value) {
- timecode_write(value.c_str());
-}
-
//-------------------------------------------------
// record_init - initialize INP recording
@@ -2716,9 +3180,10 @@ void ioport_manager::record_init()
return;
// open the record file
- osd_file::error filerr = m_record_file.open(filename);
- if (filerr != osd_file::error::NONE)
- throw emu_fatalerror("ioport_manager::record_init: Failed to open file for recording");
+ m_record_file = std::make_unique<emu_file>(machine().options().input_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
+ std::error_condition const filerr = m_record_file->open(filename);
+ if (filerr)
+ throw emu_fatalerror("ioport_manager::record_init: Failed to open file for recording (%s:%d %s)", filerr.category().name(), filerr.value(), filerr.message());
// get the base time
system_time systime;
@@ -2733,55 +3198,13 @@ void ioport_manager::record_init()
header.set_appdesc(util::string_format("%s %s", emulator_info::get_appname(), emulator_info::get_build_version()));
// write it
- header.write(m_record_file);
+ header.write(*m_record_file);
// enable compression
- m_record_file.compress(FCOMPRESS_MEDIUM);
+ m_record_stream = util::zlib_write(*m_record_file, 6, 16384);
}
-void ioport_manager::timecode_init()
-{
- // check if option -record_timecode is enabled
- if (!machine().options().record_timecode())
- {
- machine().video().set_timecode_enabled(false);
- return;
- }
- // if no file, nothing to do
- const char *record_filename = machine().options().record();
- if (record_filename[0] == 0)
- {
- machine().video().set_timecode_enabled(false);
- return;
- }
-
- machine().video().set_timecode_enabled(true);
-
- // open the record file
- std::string filename;
- filename.append(record_filename).append(".timecode");
- osd_printf_info("Record input timecode file: %s\n", record_filename);
-
- osd_file::error filerr = m_timecode_file.open(filename.c_str());
- if (filerr != osd_file::error::NONE)
- throw emu_fatalerror("ioport_manager::timecode_init: Failed to open file for input timecode recording");
-
- m_timecode_file.puts(std::string("# ==========================================\n").c_str());
- m_timecode_file.puts(std::string("# TIMECODE FILE FOR VIDEO PREVIEW GENERATION\n").c_str());
- m_timecode_file.puts(std::string("# ==========================================\n").c_str());
- m_timecode_file.puts(std::string("#\n").c_str());
- m_timecode_file.puts(std::string("# VIDEO_PART: code of video timecode\n").c_str());
- m_timecode_file.puts(std::string("# START: start time (hh:mm:ss.mmm)\n").c_str());
- m_timecode_file.puts(std::string("# ELAPSED: elapsed time (hh:mm:ss.mmm)\n").c_str());
- m_timecode_file.puts(std::string("# MSEC_START: start time (milliseconds)\n").c_str());
- m_timecode_file.puts(std::string("# MSEC_ELAPSED: elapsed time (milliseconds)\n").c_str());
- m_timecode_file.puts(std::string("# FRAME_START: start time (frames)\n").c_str());
- m_timecode_file.puts(std::string("# FRAME_ELAPSED: elapsed time (frames)\n").c_str());
- m_timecode_file.puts(std::string("#\n").c_str());
- m_timecode_file.puts(std::string("# VIDEO_PART======= START======= ELAPSED===== MSEC_START===== MSEC_ELAPSED=== FRAME_START==== FRAME_ELAPSED==\n").c_str());
-}
-
//-------------------------------------------------
// record_end - end INP recording
//-------------------------------------------------
@@ -2789,10 +3212,11 @@ void ioport_manager::timecode_init()
void ioport_manager::record_end(const char *message)
{
// only applies if we have a live file
- if (m_record_file.is_open())
+ if (m_record_stream)
{
// close the file
- m_record_file.close();
+ m_record_stream.reset(); // TODO: check for errors flushing the last compressed block before doing this
+ m_record_file.reset();
// pop a message
if (message != nullptr)
@@ -2801,19 +3225,6 @@ void ioport_manager::record_end(const char *message)
}
-void ioport_manager::timecode_end(const char *message)
-{
- // only applies if we have a live file
- if (m_timecode_file.is_open()) {
- // close the file
- m_timecode_file.close();
-
- // pop a message
- if (message != nullptr)
- machine().popmessage("Recording Timecode Ended\nReason: %s", message);
- }
-}
-
//-------------------------------------------------
// record_frame - start of frame callback for
// recording
@@ -2822,7 +3233,7 @@ void ioport_manager::timecode_end(const char *message)
void ioport_manager::record_frame(const attotime &curtime)
{
// if recording, record information about the current frame
- if (m_record_file.is_open())
+ if (m_record_stream)
{
// first the absolute time
record_write(curtime.seconds());
@@ -2831,97 +3242,6 @@ void ioport_manager::record_frame(const attotime &curtime)
// then the current speed
record_write(u32(machine().video().speed_percent() * double(1 << 20)));
}
-
- if (m_timecode_file.is_open() && machine().video().get_timecode_write())
- {
- // Display the timecode
- m_timecode_count++;
- std::string const current_time_str = string_format("%02d:%02d:%02d.%03d",
- (int)curtime.seconds() / (60 * 60),
- (curtime.seconds() / 60) % 60,
- curtime.seconds() % 60,
- (int)(curtime.attoseconds()/ATTOSECONDS_PER_MILLISECOND));
-
- // Elapsed from previous timecode
- attotime const elapsed_time = curtime - m_timecode_last_time;
- m_timecode_last_time = curtime;
- std::string const elapsed_time_str = string_format("%02d:%02d:%02d.%03d",
- elapsed_time.seconds() / (60 * 60),
- (elapsed_time.seconds() / 60) % 60,
- elapsed_time.seconds() % 60,
- int(elapsed_time.attoseconds()/ATTOSECONDS_PER_MILLISECOND));
-
- // Number of ms from beginning of playback
- int const mseconds_start = curtime.seconds()*1000 + curtime.attoseconds()/ATTOSECONDS_PER_MILLISECOND;
- std::string const mseconds_start_str = string_format("%015d", mseconds_start);
-
- // Number of ms from previous timecode
- int mseconds_elapsed = elapsed_time.seconds()*1000 + elapsed_time.attoseconds()/ATTOSECONDS_PER_MILLISECOND;
- std::string const mseconds_elapsed_str = string_format("%015d", mseconds_elapsed);
-
- // Number of frames from beginning of playback
- int const frame_start = mseconds_start * 60 / 1000;
- std::string const frame_start_str = string_format("%015d", frame_start);
-
- // Number of frames from previous timecode
- int frame_elapsed = mseconds_elapsed * 60 / 1000;
- std::string const frame_elapsed_str = string_format("%015d", frame_elapsed);
-
- std::string message;
- std::string timecode_text;
- std::string timecode_key;
- bool show_timecode_counter = false;
- if (m_timecode_count==1) {
- message = string_format("TIMECODE: Intro started at %s", current_time_str);
- timecode_key = "INTRO_START";
- timecode_text = "INTRO";
- show_timecode_counter = true;
- }
- else if (m_timecode_count==2) {
- machine().video().add_to_total_time(elapsed_time);
- message = string_format("TIMECODE: Intro duration %s", elapsed_time_str);
- timecode_key = "INTRO_STOP";
- //timecode_text = "INTRO";
- }
- else if (m_timecode_count==3) {
- message = string_format("TIMECODE: Gameplay started at %s", current_time_str);
- timecode_key = "GAMEPLAY_START";
- timecode_text = "GAMEPLAY";
- show_timecode_counter = true;
- }
- else if (m_timecode_count==4) {
- machine().video().add_to_total_time(elapsed_time);
- message = string_format("TIMECODE: Gameplay duration %s", elapsed_time_str);
- timecode_key = "GAMEPLAY_STOP";
- //timecode_text = "GAMEPLAY";
- }
- else if (m_timecode_count % 2 == 1) {
- message = string_format("TIMECODE: Extra %d started at %s", (m_timecode_count-3)/2, current_time_str);
- timecode_key = string_format("EXTRA_START_%03d", (m_timecode_count-3)/2);
- timecode_text = string_format("EXTRA %d", (m_timecode_count-3)/2);
- show_timecode_counter = true;
- }
- else {
- machine().video().add_to_total_time(elapsed_time);
- message = string_format("TIMECODE: Extra %d duration %s", (m_timecode_count-4)/2, elapsed_time_str);
- timecode_key = string_format("EXTRA_STOP_%03d", (m_timecode_count-4)/2);
- }
-
- osd_printf_info("%s \n", message.c_str());
- machine().popmessage("%s \n", message.c_str());
-
- m_timecode_file.printf(
- "%-19s %s %s %s %s %s %s\n",
- timecode_key.c_str(),
- current_time_str.c_str(), elapsed_time_str.c_str(),
- mseconds_start_str.c_str(), mseconds_elapsed_str.c_str(),
- frame_start_str.c_str(), frame_elapsed_str.c_str());
-
- machine().video().set_timecode_write(false);
- machine().video().set_timecode_text(timecode_text);
- machine().video().set_timecode_start(m_timecode_last_time);
- machine().ui().set_show_timecode_counter(show_timecode_counter);
- }
}
@@ -2932,7 +3252,7 @@ void ioport_manager::record_frame(const attotime &curtime)
void ioport_manager::record_port(ioport_port &port)
{
// if recording, store information about this port
- if (m_record_file.is_open())
+ if (m_record_stream)
{
// store the default value and digital state
record_write(port.live().defvalue);
@@ -2962,16 +3282,28 @@ void ioport_manager::record_port(ioport_port &port)
// ioport_configurer - constructor
//-------------------------------------------------
-ioport_configurer::ioport_configurer(device_t &owner, ioport_list &portlist, std::string &errorbuf)
- : m_owner(owner),
- m_portlist(portlist),
- m_errorbuf(errorbuf),
- m_curport(nullptr),
- m_curfield(nullptr),
- m_cursetting(nullptr)
+ioport_configurer::ioport_configurer(device_t &owner, ioport_list &portlist, std::ostream &errorbuf) :
+ m_owner(owner),
+ m_portlist(portlist),
+ m_errorbuf(errorbuf),
+ m_curport(nullptr),
+ m_curfield(nullptr),
+ m_cursetting(nullptr),
+ m_curshift(0)
{
}
+//-------------------------------------------------
+// field_set_gm_note - set a ioport as a general
+// midi-encoded note number. Only sets the name
+// for now
+//-------------------------------------------------
+
+ioport_configurer& ioport_configurer::field_set_gm_note(u8 note)
+{
+ field_set_name(input_gm_notes_names[note]);
+ return *this;
+}
//-------------------------------------------------
// string_from_token - convert an
@@ -2991,12 +3323,9 @@ const char *ioport_configurer::string_from_token(const char *string)
#if false // Set true, If you want to take care missing-token or wrong-sorting
// otherwise, scan the list for a matching string and return it
- {
- int index;
- for (index = 0; index < ARRAY_LENGTH(input_port_default_strings); index++)
+ for (int index = 0; index < std::size(input_port_default_strings); index++)
if (input_port_default_strings[index].id == uintptr_t(string))
return input_port_default_strings[index].string;
- }
return "(Unknown Default)";
#else
@@ -3037,9 +3366,9 @@ ioport_configurer& ioport_configurer::port_modify(const char *tag)
std::string fulltag = m_owner.subtag(tag);
// find the existing port
- m_curport = m_portlist.find(fulltag.c_str())->second.get();
+ m_curport = m_portlist.find(fulltag)->second.get();
if (m_curport == nullptr)
- throw emu_fatalerror("Requested to modify nonexistent port '%s'", fulltag.c_str());
+ throw emu_fatalerror("Requested to modify nonexistent port '%s'", fulltag);
// bump the modification count, and reset current field/setting
m_curport->m_modcount++;
@@ -3061,10 +3390,11 @@ ioport_configurer& ioport_configurer::field_alloc(ioport_type type, ioport_value
// append the field
if (type != IPT_UNKNOWN && type != IPT_UNUSED)
m_curport->m_active |= mask;
- m_curfield = &m_curport->m_fieldlist.append(*global_alloc(ioport_field(*m_curport, type, defval, mask, string_from_token(name))));
+ m_curfield = &m_curport->m_fieldlist.append(*new ioport_field(*m_curport, type, defval, mask, string_from_token(name)));
// reset the current setting
m_cursetting = nullptr;
+ m_curshift = 0;
return *this;
}
@@ -3075,16 +3405,17 @@ ioport_configurer& ioport_configurer::field_alloc(ioport_type type, ioport_value
ioport_configurer& ioport_configurer::field_add_char(std::initializer_list<char32_t> charlist)
{
- for (int index = 0; index < ARRAY_LENGTH(m_curfield->m_chars); index++)
- if (m_curfield->m_chars[index][0] == 0)
- {
- const size_t char_count = ARRAY_LENGTH(m_curfield->m_chars[index]);
- assert(charlist.size() > 0 && charlist.size() <= char_count);
+ if (m_curshift < std::size(m_curfield->m_chars))
+ {
+ auto &chars = m_curfield->m_chars[m_curshift++];
+ assert(chars[0] == UCHAR_INVALID);
+ assert(charlist.size() <= std::size(chars));
- for (size_t i = 0; i < char_count; i++)
- m_curfield->m_chars[index][i] = i < charlist.size() ? *(charlist.begin() + i) : 0;
- return *this;
- }
+ std::copy(charlist.begin(), charlist.end(), std::begin(chars));
+ std::fill(std::begin(chars) + charlist.size(), std::end(chars), UCHAR_INVALID);
+
+ return *this;
+ }
std::ostringstream s;
bool is_first = true;
@@ -3093,7 +3424,7 @@ ioport_configurer& ioport_configurer::field_add_char(std::initializer_list<char3
util::stream_format(s, "%s%d", is_first ? "" : ",", (int)ch);
is_first = false;
}
- throw emu_fatalerror("PORT_CHAR(%s) could not be added - maximum amount exceeded\n", s.str().c_str());
+ throw emu_fatalerror("PORT_CHAR(%s) could not be added - maximum amount exceeded\n", s.str());
}
@@ -3115,12 +3446,11 @@ ioport_configurer& ioport_configurer::field_add_code(input_seq_type which, input
ioport_configurer& ioport_configurer::setting_alloc(ioport_value value, const char *name)
{
// make sure we have a field
- if (m_curfield == nullptr)
- throw emu_fatalerror("alloc_setting called with no active field (value=%X name=%s)\n", value, name);
+ if (!m_curfield)
+ throw emu_fatalerror("setting_alloc called with no active field (value=%X name=%s)\n", value, name);
- m_cursetting = global_alloc(ioport_setting(*m_curfield, value & m_curfield->mask(), string_from_token(name)));
// append a new setting
- m_curfield->m_settinglist.append(*m_cursetting);
+ m_cursetting = &m_curfield->m_settinglist.emplace_back(*m_curfield, value & m_curfield->mask(), string_from_token(name));
return *this;
}
@@ -3132,7 +3462,7 @@ ioport_configurer& ioport_configurer::setting_alloc(ioport_value value, const ch
ioport_configurer& ioport_configurer::set_condition(ioport_condition::condition_t condition, const char *tag, ioport_value mask, ioport_value value)
{
- ioport_condition &target = (m_cursetting != nullptr) ? m_cursetting->condition() : m_curfield->condition();
+ ioport_condition &target = m_cursetting ? m_cursetting->condition() : m_curfield->condition();
target.set(condition, tag, mask, value);
return *this;
}
@@ -3169,11 +3499,10 @@ ioport_configurer& ioport_configurer::onoff_alloc(const char *name, ioport_value
// dynamic_field - constructor
//-------------------------------------------------
-dynamic_field::dynamic_field(ioport_field &field)
- : m_next(nullptr),
- m_field(field),
- m_shift(0),
- m_oldval(field.defvalue())
+dynamic_field::dynamic_field(ioport_field &field) :
+ m_field(field),
+ m_shift(0),
+ m_oldval(field.defvalue())
{
// fill in the data
for (ioport_value mask = field.mask(); !(mask & 1); mask >>= 1)
@@ -3190,15 +3519,15 @@ dynamic_field::dynamic_field(ioport_field &field)
void dynamic_field::read(ioport_value &result)
{
// skip if not enabled
- if (!m_field.enabled())
- return;
-
- // call the callback to read a new value
- ioport_value newval = m_field.m_read();
- m_oldval = newval;
+ if (m_field.enabled())
+ {
+ // call the callback to read a new value
+ ioport_value newval = m_field.m_read();
+ m_oldval = newval;
- // merge in the bits (don't invert yet, as all digitals are inverted together)
- result = (result & ~m_field.mask()) | ((newval << m_shift) & m_field.mask());
+ // merge in the bits (don't invert yet, as all digitals are inverted together)
+ result = (result & ~m_field.mask()) | ((newval << m_shift) & m_field.mask());
+ }
}
@@ -3210,15 +3539,15 @@ void dynamic_field::read(ioport_value &result)
void dynamic_field::write(ioport_value newval)
{
// skip if not enabled
- if (!m_field.enabled())
- return;
-
- // if the bits have changed, call the handler
- newval = (newval & m_field.mask()) >> m_shift;
- if (m_oldval != newval)
+ if (m_field.enabled())
{
- m_field.m_write(m_field, m_field.m_write_param, m_oldval, newval);
- m_oldval = newval;
+ // if the bits have changed, call the handler
+ newval = (newval & m_field.mask()) >> m_shift;
+ if (m_oldval != newval)
+ {
+ m_field.m_write(m_field, m_field.m_write_param, m_oldval, newval);
+ m_oldval = newval;
+ }
}
}
@@ -3227,45 +3556,37 @@ void dynamic_field::write(ioport_value newval)
// analog_field - constructor
//-------------------------------------------------
-analog_field::analog_field(ioport_field &field)
- : m_next(nullptr),
- m_field(field),
- m_shift(0),
- m_adjdefvalue(field.defvalue() & field.mask()),
- m_adjmin(field.minval() & field.mask()),
- m_adjmax(field.maxval() & field.mask()),
- m_sensitivity(field.sensitivity()),
- m_reverse(field.analog_reverse()),
- m_delta(field.delta()),
- m_centerdelta(field.centerdelta()),
- m_accum(0),
- m_previous(0),
- m_previousanalog(0),
- m_minimum(INPUT_ABSOLUTE_MIN),
- m_maximum(INPUT_ABSOLUTE_MAX),
- m_center(0),
- m_reverse_val(0),
- m_scalepos(0),
- m_scaleneg(0),
- m_keyscalepos(0),
- m_keyscaleneg(0),
- m_positionalscale(0),
- m_absolute(false),
- m_wraps(false),
- m_autocenter(false),
- m_single_scale(false),
- m_interpolate(false),
- m_lastdigital(false)
-{
- // compute the shift amount and number of bits
- for (ioport_value mask = field.mask(); !(mask & 1); mask >>= 1)
- m_shift++;
-
- // initialize core data
- m_adjdefvalue >>= m_shift;
- m_adjmin >>= m_shift;
- m_adjmax >>= m_shift;
-
+analog_field::analog_field(ioport_field &field) :
+ m_field(field),
+ m_shift(compute_shift(field.mask())),
+ m_adjdefvalue((field.defvalue() & field.mask()) >> m_shift),
+ m_adjmin((field.minval() & field.mask()) >> m_shift),
+ m_adjmax((field.maxval() & field.mask()) >> m_shift),
+ m_adjoverride((field.defvalue() & field.mask()) >> m_shift),
+ m_sensitivity(field.sensitivity()),
+ m_reverse(field.analog_reverse()),
+ m_delta(field.delta()),
+ m_centerdelta(field.centerdelta()),
+ m_accum(0),
+ m_previous(0),
+ m_previousanalog(0),
+ m_minimum(osd::input_device::ABSOLUTE_MIN),
+ m_maximum(osd::input_device::ABSOLUTE_MAX),
+ m_center(0),
+ m_reverse_val(0),
+ m_scalepos(0),
+ m_scaleneg(0),
+ m_keyscalepos(0),
+ m_keyscaleneg(0),
+ m_positionalscale(0),
+ m_absolute(false),
+ m_wraps(false),
+ m_autocenter(false),
+ m_single_scale(false),
+ m_interpolate(false),
+ m_lastdigital(false),
+ m_use_adjoverride(false)
+{
// set basic parameters based on the configured type
switch (field.type())
{
@@ -3284,7 +3605,7 @@ analog_field::analog_field(ioport_field &field)
case IPT_PEDAL:
case IPT_PEDAL2:
case IPT_PEDAL3:
- m_center = INPUT_ABSOLUTE_MIN;
+ m_center = osd::input_device::ABSOLUTE_MIN;
m_accum = apply_inverse_sensitivity(m_center);
m_absolute = true;
m_autocenter = true;
@@ -3303,7 +3624,7 @@ analog_field::analog_field(ioport_field &field)
// set each position to be 512 units
case IPT_POSITIONAL:
case IPT_POSITIONAL_V:
- m_positionalscale = compute_scale(field.maxval(), INPUT_ABSOLUTE_MAX - INPUT_ABSOLUTE_MIN);
+ m_positionalscale = compute_scale(field.maxval(), osd::input_device::ABSOLUTE_MAX - osd::input_device::ABSOLUTE_MIN);
m_adjmin = 0;
m_adjmax = field.maxval() - 1;
m_wraps = field.analog_wraps();
@@ -3329,21 +3650,23 @@ analog_field::analog_field(ioport_field &field)
fatalerror("Unknown analog port type -- don't know if it is absolute or not\n");
}
- // further processing for absolute controls
if (m_absolute)
{
+ // further processing for absolute controls
+
// if the default value is pegged at the min or max, use a single scale value for the whole axis
m_single_scale = (m_adjdefvalue == m_adjmin) || (m_adjdefvalue == m_adjmax);
// if not "single scale", compute separate scales for each side of the default
if (!m_single_scale)
{
- // unsigned
- m_scalepos = compute_scale(m_adjmax - m_adjdefvalue, INPUT_ABSOLUTE_MAX - 0);
- m_scaleneg = compute_scale(m_adjdefvalue - m_adjmin, 0 - INPUT_ABSOLUTE_MIN);
-
- if (m_adjmin > m_adjmax)
- m_scaleneg = -m_scaleneg;
+ // unsigned, potentially passing through zero
+ m_scalepos = compute_scale(
+ (m_adjmax - m_adjdefvalue) & (field.mask() >> m_shift),
+ osd::input_device::ABSOLUTE_MAX);
+ m_scaleneg = compute_scale(
+ (m_adjdefvalue - m_adjmin) & (field.mask() >> m_shift),
+ -osd::input_device::ABSOLUTE_MIN);
// reverse point is at center
m_reverse_val = 0;
@@ -3351,7 +3674,7 @@ analog_field::analog_field(ioport_field &field)
else
{
// single axis that increases from default
- m_scalepos = compute_scale(m_adjmax - m_adjmin, INPUT_ABSOLUTE_MAX - INPUT_ABSOLUTE_MIN);
+ m_scalepos = compute_scale(m_adjmax - m_adjmin, osd::input_device::ABSOLUTE_MAX - osd::input_device::ABSOLUTE_MIN);
// make the scaling the same for easier coding when we need to scale
m_scaleneg = m_scalepos;
@@ -3360,11 +3683,11 @@ analog_field::analog_field(ioport_field &field)
m_reverse_val = m_maximum;
}
}
-
- // relative and positional controls all map directly with a 512x scale factor
else
{
- // The relative code is set up to allow specifing PORT_MINMAX and default values.
+ // relative and positional controls all map directly with a 512x scale factor
+
+ // The relative code is set up to allow specifying PORT_MINMAX and default values.
// The validity checks are purposely set up to not allow you to use anything other
// a default of 0 and PORT_MINMAX(0,mask). This is in case the need arises to use
// this feature in the future. Keeping the code in does not hurt anything.
@@ -3375,15 +3698,17 @@ analog_field::analog_field(ioport_field &field)
if (m_wraps)
m_adjmax++;
- m_minimum = (m_adjmin - m_adjdefvalue) * INPUT_RELATIVE_PER_PIXEL;
- m_maximum = (m_adjmax - m_adjdefvalue) * INPUT_RELATIVE_PER_PIXEL;
+ m_minimum = (m_adjmin - m_adjdefvalue) * osd::input_device::RELATIVE_PER_PIXEL;
+ m_maximum = (m_adjmax - m_adjdefvalue) * osd::input_device::RELATIVE_PER_PIXEL;
// make the scaling the same for easier coding when we need to scale
- m_scaleneg = m_scalepos = compute_scale(1, INPUT_RELATIVE_PER_PIXEL);
+ m_scaleneg = m_scalepos = compute_scale(1, osd::input_device::RELATIVE_PER_PIXEL);
if (m_field.analog_reset())
+ {
// delta values reverse from center
m_reverse_val = 0;
+ }
else
{
// positional controls reverse from their max range
@@ -3392,11 +3717,11 @@ analog_field::analog_field(ioport_field &field)
// relative controls reverse from 1 past their max range
if (m_wraps)
{
- // FIXME: positional needs -1, using INPUT_RELATIVE_PER_PIXEL skips a position (and reads outside the table array)
- if(field.type() == IPT_POSITIONAL || field.type() == IPT_POSITIONAL_V)
- m_reverse_val --;
+ // FIXME: positional needs -1, using osd::input_device::RELATIVE_PER_PIXEL skips a position (and reads outside the table array)
+ if (field.type() == IPT_POSITIONAL || field.type() == IPT_POSITIONAL_V)
+ m_reverse_val--;
else
- m_reverse_val -= INPUT_RELATIVE_PER_PIXEL;
+ m_reverse_val -= osd::input_device::RELATIVE_PER_PIXEL;
}
}
}
@@ -3469,13 +3794,14 @@ s32 analog_field::apply_settings(s32 value) const
else if (m_single_scale)
// it's a pedal or the default value is equal to min/max
// so we need to adjust the center to the minimum
- value -= INPUT_ABSOLUTE_MIN;
+ value -= osd::input_device::ABSOLUTE_MIN;
// map differently for positive and negative values
+ const s32 adjust = m_field.analog_reset() ? 0 : (1 << 23);
if (value >= 0)
- value = apply_scale(value, m_scalepos);
+ value = ((s64(value) * m_scalepos) + adjust) / (1 << 24);
else
- value = apply_scale(value, m_scaleneg);
+ value = ((s64(value) * m_scaleneg) - adjust) / (1 << 24);
value += m_adjdefvalue;
// for relative devices, wrap around when we go past the edge
@@ -3495,6 +3821,29 @@ s32 analog_field::apply_settings(s32 value) const
//-------------------------------------------------
+// set_value - override the value that will be
+// read from the field
+//-------------------------------------------------
+
+void analog_field::set_value(s32 value)
+{
+ m_use_adjoverride = true;
+ m_adjoverride = std::clamp(value, m_adjmin, m_adjmax);
+}
+
+
+//-------------------------------------------------
+// clear_value - clear programmatic override
+//-------------------------------------------------
+
+void analog_field::clear_value()
+{
+ m_use_adjoverride = false;
+ m_adjoverride = m_adjdefvalue;
+}
+
+
+//-------------------------------------------------
// frame_update - update the internals of a
// single analog field periodically
//-------------------------------------------------
@@ -3515,7 +3864,23 @@ void analog_field::frame_update(running_machine &machine)
// if we got an absolute input, it overrides everything else
if (itemclass == ITEM_CLASS_ABSOLUTE)
{
- if (m_previousanalog != rawvalue)
+ if (!m_absolute && !m_positionalscale)
+ {
+ // if port is relative, we use the value to simulate the speed of relative movement
+ // sensitivity adjustment is allowed for this mode
+ if (rawvalue)
+ {
+ if (m_field.analog_reset())
+ m_accum = rawvalue / 8;
+ else
+ m_accum += rawvalue / 8;
+
+ // do not bother with other control types if the analog data is changing
+ m_lastdigital = false;
+ return;
+ }
+ }
+ else if (m_previousanalog != rawvalue)
{
// only update if analog value changed
m_previousanalog = rawvalue;
@@ -3528,38 +3893,30 @@ void analog_field::frame_update(running_machine &machine)
// if port is absolute, then just return the absolute data supplied
m_accum = apply_inverse_sensitivity(rawvalue);
}
- else if (m_positionalscale != 0)
+ else
{
+ assert(m_positionalscale); // only way to get here due to previous if
+
// if port is positional, we will take the full analog control and divide it
// into positions, that way as the control is moved full scale,
// it moves through all the positions
- rawvalue = apply_scale(rawvalue - INPUT_ABSOLUTE_MIN, m_positionalscale) * INPUT_RELATIVE_PER_PIXEL + m_minimum;
+ rawvalue = apply_scale(rawvalue - osd::input_device::ABSOLUTE_MIN, m_positionalscale) * osd::input_device::RELATIVE_PER_PIXEL + m_minimum;
// clamp the high value so it does not roll over
rawvalue = std::min(rawvalue, m_maximum);
m_accum = apply_inverse_sensitivity(rawvalue);
}
- else
- // if port is relative, we use the value to simulate the speed of relative movement
- // sensitivity adjustment is allowed for this mode
- m_accum += rawvalue;
- m_lastdigital = false;
// do not bother with other control types if the analog data is changing
+ m_lastdigital = false;
return;
}
- else
- {
- // we still have to update fake relative from joystick control
- if (!m_absolute && m_positionalscale == 0)
- m_accum += rawvalue;
- }
}
// if we got it from a relative device, use that as the starting delta
// also note that the last input was not a digital one
s32 delta = 0;
- if (itemclass == ITEM_CLASS_RELATIVE && rawvalue != 0)
+ if (itemclass == ITEM_CLASS_RELATIVE && rawvalue)
{
delta = rawvalue;
m_lastdigital = false;
@@ -3611,9 +3968,9 @@ void analog_field::frame_update(running_machine &machine)
s32 center = apply_inverse_sensitivity(m_center);
if (m_lastdigital && !keypressed)
{
- // autocenter from positive values
if (m_accum >= center)
{
+ // autocenter from positive values
m_accum -= apply_scale(m_centerdelta, m_keyscalepos);
if (m_accum < center)
{
@@ -3621,10 +3978,9 @@ void analog_field::frame_update(running_machine &machine)
m_lastdigital = false;
}
}
-
- // autocenter from negative values
else
{
+ // autocenter from negative values
m_accum += apply_scale(m_centerdelta, m_keyscaleneg);
if (m_accum > center)
{
@@ -3650,6 +4006,13 @@ void analog_field::read(ioport_value &result)
if (!m_field.enabled())
return;
+ // if set programmatically, only use the override value
+ if (m_use_adjoverride)
+ {
+ result = m_adjoverride;
+ return;
+ }
+
// start with the raw value
s32 value = m_accum;
@@ -3703,7 +4066,7 @@ ioport_type ioport_manager::token_to_input_type(const char *string, int &player)
return ioport_type(ipnum);
// find the token in the list
- for (input_type_entry &entry : m_typelist)
+ for (const input_type_entry &entry : m_typelist)
if (entry.token() != nullptr && !strcmp(entry.token(), string))
{
player = entry.player();
@@ -3741,7 +4104,7 @@ std::string ioport_manager::input_type_to_token(ioport_type type, int player)
input_seq_type ioport_manager::token_to_seq_type(const char *string)
{
// look up the string in the table of possible sequence types and return the index
- for (int seqindex = 0; seqindex < ARRAY_LENGTH(seqtypestrings); seqindex++)
+ for (int seqindex = 0; seqindex < std::size(seqtypestrings); seqindex++)
if (!core_stricmp(string, seqtypestrings[seqindex]))
return input_seq_type(seqindex);
return SEQ_TYPE_INVALID;