summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2021-10-24 09:38:03 +1100
committer Vas Crabb <vas@vastheman.com>2021-10-24 09:38:03 +1100
commit5ab93130eb223aadabb6faebb1b4299e6000120c (patch)
treea0ae96119147db950ab90f2947865d1f876a97d9
parentd699e52606fbb3282799f3912bf8c9e70f7ab503 (diff)
Plugin updates:
Changed the hiscore plugin to save inside the "hiscore" folder in the plugin data directory. The old hiscore path setting has already been removed from MAME for some time. This means the plugin was always saving in the "hi" folder in the working directory with no way to change it, which is problematic on Linux and macOS, particularly for distro packagers. There are lots of plugin and UI changes in this release, so we may as well get this out of the way now. Also made it possible to change the "timed save" option from the Plugin Options menu, and save that in the data folder, too. Documented the input macro plugin. The only undocumented plugins now are the cheat plugin, the cheat finder plugin, and the port name plugin.
-rw-r--r--docs/source/plugins/hiscore.rst17
-rw-r--r--docs/source/plugins/index.rst1
-rw-r--r--docs/source/plugins/inputmacro.rst230
-rw-r--r--plugins/autofire/autofire_save.lua2
-rw-r--r--plugins/hiscore/init.lua160
-rw-r--r--plugins/inputmacro/inputmacro_menu.lua2
6 files changed, 355 insertions, 57 deletions
diff --git a/docs/source/plugins/hiscore.rst b/docs/source/plugins/hiscore.rst
index c3ac41d15d4..389dea598ac 100644
--- a/docs/source/plugins/hiscore.rst
+++ b/docs/source/plugins/hiscore.rst
@@ -13,7 +13,16 @@ The plugin includes a **hiscore.dat** file that contains the information on how
to save and restore high scores for supported systems. This file must be kept
up-to-date when system definitions change in MAME.
-High score data is saved in the **hi** folder in the working directory. A file
-with a name corresponding the system short name (or ROM set name) with the
-extension ``.hi``. For example, high scores for the game Moon Cresta will be
-saved in the file **mooncrst.hi** in the **hi** folder.
+High scores can be saved automatically either on exit, or a few seconds after
+they’re updated in memory. To change the setting, activate the main menu (press
+**Tab** during emulation by default), select **Plugin Options**, and then select
+**Hiscore Support**. Change the **Save scores** option by highlighting it and
+using the UI Left/Right keys, or clicking the arrows.
+
+High score data is saved in the **hiscore** folder in the plugin data folder
+(see the :ref:`homepath option <mame-commandline-homepath>`). A file with a
+name corresponding the system short name (or ROM set name) with the extension
+``.hi``. For example, high scores for the game Moon Cresta will be saved in the
+file **mooncrst.hi** in the **hiscore** folder in your plugin data folder. The
+settings for the hiscore support plugin are stored in the file **plugin.cfg** in
+the **hiscore** folder in the plugin data folder (this file is in JSON format).
diff --git a/docs/source/plugins/index.rst b/docs/source/plugins/index.rst
index 5fbc501eec8..3cb72f3cf92 100644
--- a/docs/source/plugins/index.rst
+++ b/docs/source/plugins/index.rst
@@ -68,5 +68,6 @@ sample code that you can use as a starting point when writing your own plugins.
dummy
gdbstub
hiscore
+ inputmacro
layout
timer
diff --git a/docs/source/plugins/inputmacro.rst b/docs/source/plugins/inputmacro.rst
new file mode 100644
index 00000000000..99f8a3448fc
--- /dev/null
+++ b/docs/source/plugins/inputmacro.rst
@@ -0,0 +1,230 @@
+.. _plugins-inputmacro:
+
+Input Macro Plugin
+==================
+
+.. contents:: :local:
+
+
+.. _plugins-inputmacro-intro:
+
+Introduction
+------------
+
+The input macro plugin allows you to trigger a sequence of emulated input
+actions with a key or button combination. This can help people with
+disabilities or injuries that make some input sequences difficult. It can also
+be used as a way to cheat in games that require rapid sequences of inputs, like
+the running events in Track & Field, or the eating minigame in Daisu-Kiss.
+
+To configure the input macro plugin, activate the main menu (press **Tab**
+during emulation by default), select **Plugin Options**, and then select **Input
+Macros**. Configured input macros for the current system are listed, along with
+their activation sequences (initially there will be no input macros configured).
+Select a macro to edit it, or choose **Add macro** to set up a new input macro.
+See :ref:`plugins-inputmacro-settings` for details on editing input macros. You
+can delete an input macro by highlighting it in the menu and pressing the UI
+Clear key (Del/Delete/Forward Delete on the keyboard by default).
+
+Input macros are saved in the **inputmacro** folder in the plugin data folder
+(see the :ref:`homepath option <mame-commandline-homepath>`). A file is created
+for each system with input macros configured, named according to the system’s
+short name (or ROM set name), with the extension ``.cfg``. For example, input
+macros for Daisu-Kiss will be saved in the file **daiskiss.cfg** in the
+**inputmacro** folder in your plugin data folder. The input macros are stored
+in JSON format.
+
+
+.. _plugins-inputmacro-settings:
+
+Editing input macros
+--------------------
+
+The options for editing input macros are the same whether you’re creating a new
+macro or editing an existing macro. Input macros consist of a sequence of
+*steps*. Each step optionally waits for a configurable delay, then activates
+one or more emulated inputs for a specified duration. You can choose what
+should happen if the activation sequence is still held when the final step of
+the macro completes: the emulated inputs can be released, the final step can be
+prolonged, or the macro can loop back to any step in the sequence.
+
+The settings in first section of the macro editing menu apply to the macro as a
+whole:
+
+* The **Name** will be used in the list of input macros, so it helps to make it
+ descriptive. Press the UI Select key (Return/Enter on the keyboard or the
+ first button on the first joystick by default) to edit the current name, or
+ press the UI Clear key to type a new name. Press the UI Select key before
+ moving to another menu item to save the new name; press the UI Cancel key
+ (Escape/Esc on the keyboard by default) to change discard the new name.
+* Select **Activation sequence** to set the key or button combination you want
+ to use to activate the macro. Keep in mind that regular input settings still
+ apply, so you probably want to use a combination that isn’t being used for any
+ other emulated input in the system.
+* Set **On release** to specify what should happen if the activation sequence is
+ released before the macro completes. When set to *Stop immediately*, any
+ emulated inputs activated by the macro will be released immediately, and no
+ further steps will be processed; when set to *Complete macro*, the macro will
+ continue to be processed until the end of the final step.
+* Set **When held** to specify what should happen if the activation sequence is
+ held after the final step of the macro completes. When set to *Release*, any
+ inputs activated by the macro will be released, and the macro will not be
+ reactivated until the activation sequence is released and pressed again; when
+ set to *Prolong step <n>* where *<n>* is the number of the final step of the
+ macro, the emulated inputs activated by the final step of the macro will
+ remain active until the activation sequence is released; when set to *Loop to
+ step <n>* where *<n>* is a step number, macro processing will return to that
+ step, including its delay, if the activation sequence is held after the final
+ step completes.
+
+Each step has delay, duration and input settings:
+
+* Set the **Delay** to the number of emulated video frame intervals to wait
+ before activating the inputs for the step. During the delay, no emulated
+ inputs will be activated by the macro. You can reset the setting to zero by
+ pressing the UI Clear key.
+* Set the **Duration** to the number of emulated video frame intervals to hold
+ the emulated inputs for the step active before moving to the next step (or
+ completing the macro in the case of the final step). You can reset the
+ setting to one frame by pressing the UI Clear key.
+* Set the **Input** settings to the emulated inputs to activate for the step.
+ Only non-toggle digital inputs are supported. Select **Add input** to set
+ multiple inputs for a step (this option will only appear after you set the
+ first input for the initially created step when creating a new macro). If
+ the step has multiple inputs, you can highlight an input on the menu and press
+ the UI Clear key to delete it (all steps must have at least one input, so you
+ can’t delete the only input for a step).
+* If the macro has multiple steps, you can select **Delete step** to delete a
+ step (this options does not appear if the macro only has a single step).
+ Remember to check that the **On release** and **When held** settings are
+ correct after deleting steps.
+
+To add a step to the macro, highlight **Add step at position** (below the
+existing steps), use the UI Left/Right keys or click the arrows to set the
+position where you’d like to insert the new step, and then press the UI Select
+key (or double-click the menu item) to add the new step. You will be prompted
+to set the first input for the new step. Remember to check the **On release**
+and **When held** settings after adding steps. The **Add step at position**
+item will only appear after you set the first input for the initially created
+step when creating a new macro.
+
+When creating a new macro, there is a **Cancel** option that changes to
+**Create** after you set the activating sequence and the first input for the
+initially created step. Select **Create** to finish creating the macro and
+return to the list of input macros. The new macro will be added at the end of
+the list. Press the UI Cancel key, or select **Cancel** before setting the
+activation sequence/input, to return to the previous menu without creating the
+new macro.
+
+When editing an existing macro, select **Done** or press the UI Cancel key to
+return to the list of input macros. Changes take effect immediately.
+
+
+.. _plugins-inputmacro-examples:
+
+Example macros
+--------------
+
+Raiden autofire
+~~~~~~~~~~~~~~~
+
+This provides player 1 autofire functionality using the space bar. The same
+thing could be achieved using the :ref:`plugins-autofire`, but this demonstrates
+a simple looping macro:
+
+* **Name**: P1 Autofire
+* **Activation sequence**: Kbd Space
+* **On release**: Stop immediately
+* **When held**: Loop to step 2
+* **Step 1**:
+
+ * **Delay (frames)**: 0
+ * **Duration (frames)**: 2
+ * **Input 1**: P1 Button 1
+* **Step 2**:
+
+ * **Delay (frames)**: 4
+ * **Duration (frames)**: 2
+ * **Input 1**: P1 Button 1
+
+The first step has no delay so that firing begins as soon as the space bar is
+pressed. The second step has sufficient delay to ensure the game recognises the
+button being pressed and released again. The second step is repeated as long as
+the space bar is held down.
+
+Track & Field sprint cheat
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This allows you to run in Konami Track & Field by holding a single button. This
+takes most of the skill (and fun) out of the game:
+
+* **Name**: P1 Sprint
+* **Activation sequence**: Kbd Shift
+* **On release**: Stop immediately
+* **When held**: Loop to step 2
+* **Step 1**:
+
+ * **Delay (frames)**: 0
+ * **Duration (frames)**: 1
+ * **Input 1**: P1 Button 1
+* **Step 2**:
+
+ * **Delay (frames)**: 1
+ * **Duration (frames)**: 1
+ * **Input 1**: P1 Button 3
+* **Step 3**:
+
+ * **Delay (frames)**: 1
+ * **Duration (frames)**: 1
+ * **Input 1**: P1 Button 1
+
+This macro rapidly alternates pressing buttons 1 and 3 – the pattern required to
+run in the game.
+
+Street Fighter II Shoryuken
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This macro allows you to perform a right-facing Shōryūken (Dragon Punch) by
+pressing a single key:
+
+* **Name**: 1P Shoryuken LP
+* **Activation sequence**: Kbd M
+* **On release**: Complete macro
+* **When held**: Prolong step 6
+* **Step 1**:
+
+ * **Delay (frames)**: 0
+ * **Duration (frames)**: 1
+ * **Input 1**: P1 Right
+* **Step 2**:
+
+ * **Delay (frames)**: 1
+ * **Duration (frames)**: 1
+ * **Input 1**: P1 Down
+* **Step 3**:
+
+ * **Delay (frames)**: 0
+ * **Duration (frames)**: 1
+ * **Input 1**: P1 Down
+ * **Input 2**: P1 Right
+* **Step 4**:
+
+ * **Delay (frames)**: 0
+ * **Duration (frames)**: 1
+ * **Input 1**: P1 Right
+* **Step 5**:
+
+ * **Delay (frames)**: 0
+ * **Duration (frames)**: 1
+ * **Input 1**: P1 Right
+ * **Input 2**: P1 Jab Punch
+* **Step 6**:
+
+ * **Delay (frames)**: 0
+ * **Duration (frames)**: 1
+ * **Input 1**: P1 Jab Punch
+
+This macro has has involves steps that activate multiple inputs. The macro will
+complete if the activation sequence is released early, allowing you to tap the
+key momentarily to perform the move. Holding the activation sequence holds down
+the attack button.
diff --git a/plugins/autofire/autofire_save.lua b/plugins/autofire/autofire_save.lua
index 3692540cd5b..3c713b4576c 100644
--- a/plugins/autofire/autofire_save.lua
+++ b/plugins/autofire/autofire_save.lua
@@ -76,7 +76,7 @@ function lib:save_settings(buttons)
if not attr then
lfs.mkdir(path)
elseif attr.mode ~= 'directory' then
- emu.print_error(string.format('Error autofire settings macros: "%s" is not a directory\n', path))
+ emu.print_error(string.format('Error saving autofire settings: "%s" is not a directory\n', path))
return
end
if #buttons == 0 then
diff --git a/plugins/hiscore/init.lua b/plugins/hiscore/init.lua
index 2a9687d1547..0865caa2721 100644
--- a/plugins/hiscore/init.lua
+++ b/plugins/hiscore/init.lua
@@ -1,16 +1,17 @@
-- hiscore.lua
-- by borgar@borgar.net, WTFPL license
--
--- This uses MAME's built-in Lua scripting to implment
+-- This uses MAME's built-in Lua scripting to implement
-- high-score saving with hiscore.dat infom just as older
-- builds did in the past.
--
-local exports = {}
-exports.name = "hiscore"
-exports.version = "1.0.0"
-exports.description = "Hiscore"
-exports.license = "WTFPL license"
-exports.author = { name = "borgar@borgar.net" }
+local exports = {
+ name = 'hiscore',
+ version = '1.0.0',
+ description = 'Hiscore',
+ license = 'WTFPL license',
+ author = { name = 'borgar@borgar.net' } }
+
local hiscore = exports
local hiscore_plugin_path = ""
@@ -21,46 +22,97 @@ end
function hiscore.startplugin()
+ local function get_data_path()
+ return emu.subst_env(manager.machine.options.entries.homepath:value():match('([^;]+)')) .. '/hiscore/'
+ end
+
+ -- configuration
+ local config_read = false
+ local timed_save = true
+
+ -- read configuration file from data directory
+ local function read_config()
+ if config_read then
+ return true
+ end
+ local filename = get_data_path() .. 'plugin.cfg'
+ local file = io.open(filename, 'r')
+ if file then
+ local json = require('json')
+ local parsed_settings = json.parse(file:read('a'))
+ file:close()
+ if parsed_settings then
+ if parsed_settings.only_save_at_exit and (parsed_settings.only_save_at_exit ~= 0) then
+ timed_save = false
+ end
+ -- TODO: other settings? maybe path overrides for hiscore.dat or the hiscore data?
+ config_read = true
+ return true
+ else
+ emu.print_error(string.format('Error loading hiscore plugin settings: error parsing file "%s" as JSON\n', filename))
+ end
+ end
+ return false
+ end
+
+ -- save configuration file
+ local function save_config()
+ local path = get_data_path()
+ local attr = lfs.attributes(path)
+ if not attr then
+ lfs.mkdir(path)
+ elseif attr.mode ~= 'directory' then
+ emu.print_error(string.format('Error saving hiscore plugin settings: "%s" is not a directory\n', path))
+ return
+ end
+ local settings = { only_save_at_exit = not timed_save }
+ -- TODO: other settings?
+ local filename = path .. 'plugin.cfg'
+ local json = require('json')
+ local data = json.stringify(settings, { indent = true })
+ local file = io.open(filename, 'w')
+ if not file then
+ emu.print_error(string.format('Error saving hiscore plugin settings: error opening file "%s" for writing\n', filename))
+ return
+ end
+ file:write(data)
+ file:close()
+ end
+
+ -- build menu
+ local function populate_menu()
+ local items = { }
+ local setting = timed_save and _p('plugin-hiscore', 'When updated') or _p('plugin-hiscore', 'On exit')
+ table.insert(items, { _p('plugin-hiscore', 'Hiscore Support Options'), '', 'off' })
+ table.insert(items, { '---', '', '' })
+ table.insert(items, { _p('plugin-hiscore', 'Save scores'), setting, timed_save and 'l' or 'r' })
+ return items
+ end
+
+ -- handle menu events
+ local function handle_menu(index, event)
+ if event == 'left' then
+ timed_save = false
+ return true
+ elseif event == 'right' then
+ timed_save = true
+ return true
+ end
+ return false
+ end
+
local hiscoredata_path = "hiscore.dat";
- local hiscore_path = "hi";
- local config_path = emu.subst_env(manager.options.entries.inipath:value():match("[^;]+") .. "/hiscore.ini");
local current_checksum = 0;
local default_checksum = 0;
- local config_read = false;
local scores_have_been_read = false;
local mem_check_passed = false;
local found_hiscore_entry = false;
- local timed_save = true;
local delaytime = 0;
- local positions = {};
- -- Configuration file will be searched in the first path defined
- -- in mame inipath option.
- local function read_config()
- if config_read then return true end;
- local file = io.open( config_path, "r" );
- if file then
- file:close()
- emu.print_verbose( "hiscore: config found" );
- local _conf = {}
- for line in io.lines(config_path) do
- token, spaces, value = string.match(line, '([^ ]+)([ ]+)([^ ]+)');
- if token ~= nil and token ~= '' then
- _conf[token] = value;
- end
- end
- hiscore_path = emu.subst_env(_conf["hi_path"] or hiscore_path);
- timed_save = _conf["only_save_at_exit"] ~= "1"
- -- hiscoredata_path = _conf["dat_path"]; -- don't know if I should do it, but wathever
- return true
- end
- return false
- end
-
local function parse_table ( dsting )
- local _table = {};
+ local _table = {}
for line in string.gmatch(dsting, '([^\n]+)') do
local delay = line:match('^@delay=([.%d]*)')
if delay and #delay > 0 then
@@ -154,13 +206,13 @@ function hiscore.startplugin()
end
- local function get_file_name ()
+ local function get_file_name()
local r;
if emu.softname() ~= "" then
local soft = emu.softname():match("([^:]*)$")
- r = hiscore_path .. '/' .. emu.romname() .. "_" .. soft .. ".hi";
+ r = get_data_path() .. emu.romname() .. "_" .. soft .. ".hi";
else
- r = hiscore_path .. '/' .. emu.romname() .. ".hi";
+ r = get_data_path() .. emu.romname() .. ".hi";
end
return r;
end
@@ -171,13 +223,13 @@ function hiscore.startplugin()
local output = io.open(get_file_name(), "wb");
if not output then
-- attempt to create the directory, and try again
- lfs.mkdir( hiscore_path );
+ lfs.mkdir( get_data_path() );
output = io.open(get_file_name(), "wb");
end
emu.print_verbose("hiscore: write_scores output")
if output then
for ri,row in ipairs(posdata) do
- t = {};
+ t = {}
for i=0,row["size"]-1 do
t[i+1] = row["mem"]:read_u8(row["addr"] + i)
end
@@ -264,16 +316,16 @@ function hiscore.startplugin()
end
local function reset()
- -- the notifier will still be attached even if the running game has no hiscore.dat entry
- if mem_check_passed and found_hiscore_entry then
- local checksum = check_scores(positions)
- if checksum ~= current_checksum and checksum ~= default_checksum then
- write_scores(positions)
+ -- the notifier will still be attached even if the running game has no hiscore.dat entry
+ if mem_check_passed and found_hiscore_entry then
+ local checksum = check_scores(positions)
+ if checksum ~= current_checksum and checksum ~= default_checksum then
+ write_scores(positions)
+ end
end
- end
- found_hiscore_entry = false
- mem_check_passed = false
- scores_have_been_read = false;
+ found_hiscore_entry = false
+ mem_check_passed = false
+ scores_have_been_read = false;
end
emu.register_start(function()
@@ -282,7 +334,7 @@ function hiscore.startplugin()
scores_have_been_read = false;
last_write_time = -10
emu.print_verbose("Starting " .. emu.gamename())
- config_read = read_config();
+ read_config();
local dat = read_hiscore_dat()
if dat and dat ~= "" then
emu.print_verbose( "hiscore: found hiscore.dat entry for " .. emu.romname() );
@@ -301,17 +353,23 @@ function hiscore.startplugin()
found_hiscore_entry = true
end
end)
+
emu.register_frame(function()
if found_hiscore_entry then
tick()
end
end)
+
emu.register_stop(function()
reset()
+ save_config()
end)
+
emu.register_prestart(function()
reset()
end)
+
+ emu.register_menu(handle_menu, populate_menu, _p('plugin-hiscore', 'Hiscore Support'))
end
return exports
diff --git a/plugins/inputmacro/inputmacro_menu.lua b/plugins/inputmacro/inputmacro_menu.lua
index 24c8caff29e..afb2071996b 100644
--- a/plugins/inputmacro/inputmacro_menu.lua
+++ b/plugins/inputmacro/inputmacro_menu.lua
@@ -609,7 +609,7 @@ function populate_macros()
end
end
else
- items[#items + 1] = { _p('plugin-inputmacro', 'No macros'), '', 'off' }
+ items[#items + 1] = { _p('plugin-inputmacro', '[no macros]'), '', 'off' }
end
macros_start_macro = nil