summaryrefslogtreecommitdiffstatshomepage
path: root/plugins
diff options
context:
space:
mode:
author cracyc <cracyc@users.noreply.github.com>2016-09-05 21:43:17 -0500
committer cracyc <cracyc@users.noreply.github.com>2016-09-05 21:43:17 -0500
commit46ab9213f5f49bc4d6afc63ac03459fde78ddd85 (patch)
treee460e7249fdf7f3ece72be215fb6382abe77010a /plugins
parent63ecb9a387e236f36d1aa6dcd3035289db3ca830 (diff)
ui/selgame: get dat info from plugin [Carl]
plugins/data: add data plugin [Carl]
Diffstat (limited to 'plugins')
-rw-r--r--plugins/data/button_char.lua245
-rw-r--r--plugins/data/data_command.lua25
-rw-r--r--plugins/data/data_gameinit.lua23
-rw-r--r--plugins/data/data_hiscore.lua803
-rw-r--r--plugins/data/data_history.lua22
-rw-r--r--plugins/data/data_mameinfo.lua22
-rw-r--r--plugins/data/data_messinfo.lua23
-rw-r--r--plugins/data/data_story.lua28
-rw-r--r--plugins/data/data_sysinfo.lua22
-rw-r--r--plugins/data/init.lua60
-rw-r--r--plugins/data/load_dat.lua97
-rw-r--r--plugins/data/plugin.json10
12 files changed, 1380 insertions, 0 deletions
diff --git a/plugins/data/button_char.lua b/plugins/data/button_char.lua
new file mode 100644
index 00000000000..cee756c2603
--- /dev/null
+++ b/plugins/data/button_char.lua
@@ -0,0 +1,245 @@
+local default_text =
+{
+ -- Alphabetic Buttons (NeoGeo): A~D,H,Z
+ ["A"] = 1, -- BTN_A
+ ["B"] = 2, -- BTN_B
+ ["C"] = 3, -- BTN_C
+ ["D"] = 4, -- BTN_D
+ ["H"] = 8, -- BTN_H
+ ["Z"] = 26, -- BTN_Z
+ -- Numerical Buttons (Capcom): 1~10
+ ["a"] = 27, -- BTN_1
+ ["b"] = 28, -- BTN_2
+ ["c"] = 29, -- BTN_3
+ ["d"] = 30, -- BTN_4
+ ["e"] = 31, -- BTN_5
+ ["f"] = 32, -- BTN_6
+ ["g"] = 33, -- BTN_7
+ ["h"] = 34, -- BTN_8
+ ["i"] = 35, -- BTN_9
+ ["j"] = 36, -- BTN_10
+ -- Directions of Arrow, Joystick Ball
+ ["+"] = 39, -- BTN_+
+ ["."] = 40, -- DIR_...
+ ["1"] = 41, -- DIR_1
+ ["2"] = 42, -- DIR_2
+ ["3"] = 43, -- DIR_3
+ ["4"] = 44, -- DIR_4
+ ["5"] = 45, -- Joystick Ball
+ ["6"] = 46, -- DIR_6
+ ["7"] = 47, -- DIR_7
+ ["8"] = 48, -- DIR_8
+ ["9"] = 49, -- DIR_9
+ ["N"] = 50, -- DIR_N
+ -- Special Buttons
+ ["S"] = 51, -- BTN_START
+ ["P"] = 53, -- BTN_PUNCH
+ ["K"] = 54, -- BTN_KICK
+ ["G"] = 55, -- BTN_GUARD
+ -- Composition of Arrow Directions
+ ["!"] = 90, -- Arrow
+ ["k"] = 100, -- Half Circle Back
+ ["l"] = 101, -- Half Circle Front Up
+ ["m"] = 102, -- Half Circle Front
+ ["n"] = 103, -- Half Circle Back Up
+ ["o"] = 104, -- 1/4 Cir For 2 Down
+ ["p"] = 105, -- 1/4 Cir Down 2 Back
+ ["q"] = 106, -- 1/4 Cir Back 2 Up
+ ["r"] = 107, -- 1/4 Cir Up 2 For
+ ["s"] = 108, -- 1/4 Cir Back 2 Down
+ ["t"] = 109, -- 1/4 Cir Down 2 For
+ ["u"] = 110, -- 1/4 Cir For 2 Up
+ ["v"] = 111, -- 1/4 Cir Up 2 Back
+ ["w"] = 112, -- Full Clock Forward
+ ["x"] = 113, -- Full Clock Back
+ ["y"] = 114, -- Full Count Forward
+ ["z"] = 115, -- Full Count Back
+ ["L"] = 116, -- 2x Forward
+ ["M"] = 117, -- 2x Back
+ ["Q"] = 118, -- Dragon Screw Forward
+ ["R"] = 119, -- Dragon Screw Back
+ -- Big letter Text
+ ["^"] = 121, -- AIR
+ ["?"] = 122, -- DIR
+ ["X"] = 124, -- TAP
+ -- Condition of Positions
+ ["|"] = 125, -- Jump
+ ["O"] = 126, -- Hold
+ ["-"] = 127, -- Air
+ ["="] = 128, -- Squatting
+ ["~"] = 131, -- Charge
+ -- Special Character Text
+ ["`"] = 135, -- Small Dot
+ ["@"] = 136, -- Double Ball
+ [")"] = 137, -- Single Ball
+ ["("] = 138, -- Solid Ball
+ ["*"] = 139, -- Star
+ ["&"] = 140, -- Solid star
+ ["%"] = 141, -- Triangle
+ ["$"] = 142, -- Solid Triangle
+ ["#"] = 143, -- Double Square
+ ["]"] = 144, -- Single Square
+ ["["] = 145, -- Solid Square
+ ["{"] = 146, -- Down Triangle
+ ["}"] = 147, -- Solid Down Triangle
+ ["<"] = 148, -- Diamond
+ [">"] = 149, -- Solid Diamond
+}
+
+local expand_text =
+{
+ -- Alphabetic Buttons (NeoGeo): S (Slash Button)
+ ["s"] = 19, -- BTN_S
+ -- Special Buttons
+ ["S"] = 52, -- BTN_SELECT
+ -- Multiple Punches & Kicks
+ ["E"] = 57, -- Light Punch
+ ["F"] = 58, -- Middle Punch
+ ["G"] = 59, -- Strong Punch
+ ["H"] = 60, -- Light Kick
+ ["I"] = 61, -- Middle Kick
+ ["J"] = 62, -- Strong Kick
+ ["T"] = 63, -- 3 Kick
+ ["U"] = 64, -- 3 Punch
+ ["V"] = 65, -- 2 Kick
+ ["W"] = 66, -- 2 Pick
+ -- Composition of Arrow Directions
+ ["!"] = 91, -- Continue Arrow
+ -- Charge of Arrow Directions
+ ["1"] = 92, -- Charge DIR_1
+ ["2"] = 93, -- Charge DIR_2
+ ["3"] = 94, -- Charge DIR_3
+ ["4"] = 95, -- Charge DIR_4
+ ["6"] = 96, -- Charge DIR_6
+ ["7"] = 97, -- Charge DIR_7
+ ["8"] = 98, -- Charge DIR_8
+ ["9"] = 99, -- Charge DIR_9
+ -- Big letter Text
+ ["M"] = 123, -- MAX
+ -- Condition of Positions
+ ["-"] = 129, -- Close
+ ["="] = 130, -- Away
+ ["*"] = 132, -- Serious Tap
+ ["?"] = 133, -- Any Button
+}
+
+local convert_text =
+{
+ -- Alphabetic Buttons: A~Z
+ ["A-button"] = 1, -- BTN_A
+ ["B-button"] = 2, -- BTN_B
+ ["C-button"] = 3, -- BTN_C
+ ["D-button"] = 4, -- BTN_D
+ ["E-button"] = 5, -- BTN_E
+ ["F-button"] = 6, -- BTN_F
+ ["G-button"] = 7, -- BTN_G
+ ["H-button"] = 8, -- BTN_H
+ ["I-button"] = 9, -- BTN_I
+ ["J-button"] = 10, -- BTN_J
+ ["K-button"] = 11, -- BTN_K
+ ["L-button"] = 12, -- BTN_L
+ ["M-button"] = 13, -- BTN_M
+ ["N-button"] = 14, -- BTN_N
+ ["O-button"] = 15, -- BTN_O
+ ["P-button"] = 16, -- BTN_P
+ ["Q-button"] = 17, -- BTN_Q
+ ["R-button"] = 18, -- BTN_R
+ ["S-button"] = 19, -- BTN_S
+ ["T-button"] = 20, -- BTN_T
+ ["U-button"] = 21, -- BTN_U
+ ["V-button"] = 22, -- BTN_V
+ ["W-button"] = 23, -- BTN_W
+ ["X-button"] = 24, -- BTN_X
+ ["Y-button"] = 25, -- BTN_Y
+ ["Z-button"] = 26, -- BTN_Z
+ -- Special Moves and Buttons
+ ["decrease"] = 37, -- BTN_DEC
+ ["increase"] = 38, -- BTN_INC
+ ["BALL"] = 45, -- Joystick Ball
+ ["start"] = 51, -- BTN_START
+ ["select"] = 52, -- BTN_SELECT
+ ["punch"] = 53, -- BTN_PUNCH
+ ["kick"] = 54, -- BTN_KICK
+ ["guard"] = 55, -- BTN_GUARD
+ ["L-punch"] = 57, -- Light Punch
+ ["M-punch"] = 58, -- Middle Punch
+ ["S-punch"] = 59, -- Strong Punch
+ ["L-kick"] = 60, -- Light Kick
+ ["M-kick"] = 61, -- Middle Kick
+ ["S-kick"] = 62, -- Strong Kick
+ ["3-kick"] = 63, -- 3 Kick
+ ["3-punch"] = 64, -- 3 Punch
+ ["2-kick"] = 65, -- 2 Kick
+ ["2-punch"] = 66, -- 2 Pick
+ -- Custom Buttons and Cursor Buttons
+ ["custom1"] = 67, -- CUSTOM_1
+ ["custom2"] = 68, -- CUSTOM_2
+ ["custom3"] = 69, -- CUSTOM_3
+ ["custom4"] = 70, -- CUSTOM_4
+ ["custom5"] = 71, -- CUSTOM_5
+ ["custom6"] = 72, -- CUSTOM_6
+ ["custom7"] = 73, -- CUSTOM_7
+ ["custom8"] = 74, -- CUSTOM_8
+ ["up"] = 75, -- (Cursor Up)
+ ["down"] = 76, -- (Cursor Down)
+ ["left"] = 77, -- (Cursor Left)
+ ["right"] = 78, -- (Cursor Right)
+ -- Player Lever
+ ["lever"] = 79, -- Non Player Lever
+ ["nplayer"] = 80, -- Gray Color Lever
+ ["1player"] = 81, -- 1 Player Lever
+ ["2player"] = 82, -- 2 Player Lever
+ ["3player"] = 83, -- 3 Player Lever
+ ["4player"] = 84, -- 4 Player Lever
+ ["5player"] = 85, -- 5 Player Lever
+ ["6player"] = 86, -- 6 Player Lever
+ ["7player"] = 87, -- 7 Player Lever
+ ["8player"] = 88, -- 8 Player Lever
+ -- Composition of Arrow Directions
+ ["-->"] = 90, -- Arrow
+ ["==>"] = 91, -- Continue Arrow
+ ["hcb"] = 100, -- Half Circle Back
+ ["huf"] = 101, -- Half Circle Front Up
+ ["hcf"] = 102, -- Half Circle Front
+ ["hub"] = 103, -- Half Circle Back Up
+ ["qfd"] = 104, -- 1/4 Cir For 2 Down
+ ["qdb"] = 105, -- 1/4 Cir Down 2 Back
+ ["qbu"] = 106, -- 1/4 Cir Back 2 Up
+ ["quf"] = 107, -- 1/4 Cir Up 2 For
+ ["qbd"] = 108, -- 1/4 Cir Back 2 Down
+ ["qdf"] = 109, -- 1/4 Cir Down 2 For
+ ["qfu"] = 110, -- 1/4 Cir For 2 Up
+ ["qub"] = 111, -- 1/4 Cir Up 2 Back
+ ["fdf"] = 112, -- Full Clock Forward
+ ["fub"] = 113, -- Full Clock Back
+ ["fuf"] = 114, -- Full Count Forward
+ ["fdb"] = 115, -- Full Count Back
+ ["xff"] = 116, -- 2x Forward
+ ["xbb"] = 117, -- 2x Back
+ ["dsf"] = 118, -- Dragon Screw Forward
+ ["dsb"] = 119, -- Dragon Screw Back
+ -- Big letter Text
+ ["AIR"] = 121, -- AIR
+ ["DIR"] = 122, -- DIR
+ ["MAX"] = 123, -- MAX
+ ["TAP"] = 124, -- TAP
+ -- Condition of Positions
+ ["jump"] = 125, -- Jump
+ ["hold"] = 126, -- Hold
+ ["air"] = 127, -- Air
+ ["sit"] = 128, -- Squatting
+ ["close"] = 129, -- Close
+ ["away"] = 130, -- Away
+ ["charge"] = 131, -- Charge
+ ["tap"] = 132, -- Serious Tap
+ ["button"] = 133, -- Any Button
+}
+
+local function convert_char(str)
+ str = str:gsub("@([a-zA-Z%-]+)", function(s) if convert_text[s] then return utf8.char(convert_text[s] + 0xe000) end return s end)
+ str = str:gsub("_(.)", function(s) if default_text[s] then return utf8.char(default_text[s] + 0xe000) end return s end)
+ str = str:gsub("%^(.)", function(s) if expand_text[s] then return utf8.char(expand_text[s] + 0xe000) end return s end)
+ return str
+end
+
+return convert_char
diff --git a/plugins/data/data_command.lua b/plugins/data/data_command.lua
new file mode 100644
index 00000000000..a868b792c22
--- /dev/null
+++ b/plugins/data/data_command.lua
@@ -0,0 +1,25 @@
+local dat = {}
+local info, ver
+
+local datread = require("data/load_dat")
+datread, ver = datread.open("command.dat", nil)
+
+function dat.check(set, softlist)
+ if softlist or not datread then
+ return nil
+ end
+ local status
+ status, info = pcall(datread, "cmd", "info", set)
+ if not status or not info then
+ return nil
+ end
+ local convert = require("data/button_char")
+ info = "#jf\n" .. convert(info)
+ return "Command"
+end
+
+function dat.get()
+ return info
+end
+
+return dat
diff --git a/plugins/data/data_gameinit.lua b/plugins/data/data_gameinit.lua
new file mode 100644
index 00000000000..040cfb7745b
--- /dev/null
+++ b/plugins/data/data_gameinit.lua
@@ -0,0 +1,23 @@
+local dat = {}
+local ver, info
+
+local datread = require("data/load_dat")
+datread, ver = datread.open("gameinit.dat", "# GAMEINIT.DAT")
+
+function dat.check(set, softlist)
+ if softlist or not datread then
+ return nil
+ end
+ local status
+ status, info = pcall(datread, "mame", "info", set)
+ if not status or not info then
+ return nil
+ end
+ return "Gameinit"
+end
+
+function dat.get()
+ return info
+end
+
+return dat
diff --git a/plugins/data/data_hiscore.lua b/plugins/data/data_hiscore.lua
new file mode 100644
index 00000000000..5f1a0f70521
--- /dev/null
+++ b/plugins/data/data_hiscore.lua
@@ -0,0 +1,803 @@
+-- to use this get the package from http://greatstone.free.fr/hi2txt/
+-- extract the hi2txt.zip and place it in your history path
+
+local dat = {}
+local env = {}
+local output
+local curset
+
+function dat.check(set, softlist)
+ if softlist then
+ return nil
+ end
+ local datpath
+ local function xml_parse()
+ local file = emu.file(lfs.env_replace(mame_manager:ui():options().entries.historypath:value():gsub("([^;]+)", "%1/hi2txt")), 1)
+ local ret = file:open(set .. ".xml")
+ local table
+ if ret then
+ return nil
+ end
+ datpath = file:fullpath():gsub(".zip", "/")
+ local data = file:read(file:size())
+ data = data:match("<hi2txt>(.*)</ *hi2txt>")
+ local function get_tags(str, parent)
+ local arr = {}
+ while str ~= "" do
+ local tag, attr, stop
+ tag, attr, stop, str = str:match("<([%w!_%-]+) ?(.-)(/?)[ %-]->(.*)")
+
+ if not tag then
+ return arr
+ end
+ if tag:sub(0, 3) ~= "!--" then
+ local block = {}
+ if stop ~= "/" then
+ local nest
+ nest, str = str:match("(.-)</ *" .. tag .. " *>(.*)")
+ local children = get_tags(nest, tag)
+ if not next(children) then
+ nest = nest:gsub("<!--.-%-%->", "")
+ nest = nest:gsub("^%s*(.-)%s*$", "%1")
+ block["text"] = nest
+ else
+ block = children
+ end
+ end
+ if attr then
+ for name, value in attr:gmatch("([-%w]-)=\"(.-)\"") do
+ block[name] = value:gsub("^(.-)$", "%1")
+ end
+ end
+ if not arr[tag] then
+ arr[tag] = {}
+ end
+ if parent == "structure" or parent == "output" or parent == "format" or parent == "loop" or parent == "concat" then
+ block["tag"] = tag
+ arr[#arr + 1] = block
+ elseif tag == "charset" or tag == "bitmask" then
+ arr[tag][block["id"]] = block
+ elseif tag == "case" or tag == "char" then
+ arr[tag][block["src"]] = block
+ else
+ arr[tag][#arr[tag] + 1] = block
+ end
+ end
+ end
+ return arr
+ end
+ return get_tags(data, "")
+ end
+
+ local function parse_table(xml)
+ local total_size = 0
+ local s = { "data = open('" .. xml.structure[1].file .. "', size)\noffset = 1\narr = {}" }
+ local fparam = {}
+ if xml.bitmask then
+ local bitmask = "bitmask = {"
+ for id, masks in pairs(xml.bitmask) do
+ bitmask = bitmask .. "['" .. id .. "'] = {"
+ for num, mask in ipairs(masks.character) do
+ mask = mask:gsub("( )", "")
+ local newmask = tonumber(mask, 2)
+ local shift = 0
+ local count = 8
+ while (newmask & 1) == 0 do
+ newmask = newmask >> 1
+ shift = shift + 1
+ end
+ if masks.byte-completion and masks.byte-completion == "no" then
+ count = 0
+ while (newmask >> count) & 1 == 1 do
+ count = count + 1
+ end
+ end
+ bitmask = bitmask .. "{ mask = " .. newmask .. ", ishift = " .. shift .. ", oshift = " .. count .. "},"
+ end
+ bitmask = bitmask .. "},"
+ end
+ s[#s + 1] = bitmask .. "}"
+ end
+ if xml.charset then
+ local charset = "charset = {"
+ for id, set in pairs(xml.charset) do
+ local default
+ charset = charset .. "['" .. id .. "'] = {"
+ for src, char in pairs(set.char) do
+ if char.default and char.default == "yes" then
+ default = char.dst
+ end
+ charset = charset .. "[" .. src .. "]" .. " = '" .. char.dst .. "',"
+ end
+ if default then
+ charset = charset .. "default = " .. default
+ end
+ charset = charset .. "},"
+ end
+ s[#s + 1] = charset .. "}"
+ end
+
+ local function check_format(formstr)
+ local formats = {}
+ local ret = " function tempform(val)"
+ formstr = formstr:gsub("&gt;", ">")
+ formstr:gsub("([^;]+)", function(s) formats[#formats + 1] = s end)
+ for num, form in ipairs(formats) do
+ local oper
+ local first, rest = form:match("^(.)(.*)$")
+ if first == "*" and tonumber(rest) then
+ oper = " val = val * " .. rest
+ elseif first == "/" and tonumber(rest) then
+ oper = " val = val / " .. rest
+ elseif first == "d" and tonumber(rest) then
+ oper = " val = math.floor(val / " .. rest .. ")"
+ elseif first == "D" and tonumber(rest) then
+ oper = " val = math.floor((val / " .. rest .. ") + 0.5)"
+ elseif first == "-" and tonumber(rest) then
+ oper = " val = val - " .. rest
+ elseif first == "+" and tonumber(rest) then
+ oper = " val = val + " .. rest
+ elseif first == "%" and tonumber(rest) then
+ oper = " val = val % " .. rest
+ elseif first == ">" and tonumber(rest) then
+ oper = " val = val << " .. rest
+ elseif first == "L" and (rest == "C" or rest == "owercase") then
+ oper = " val = val:lower()"
+ elseif first == "U" and (rest == "C" or rest == "ppercase") then
+ oper = " val = val:upper()"
+ elseif first == "C" and rest == "apitalize" then
+ oper = " val = val:gsub('^(.)', function(s) return s:upper() end)"
+ elseif first == "R" and (rest == "" or rest == "ound") then
+ oper = " val = math.floor(" .. var .. " + 0.5)"
+ elseif first == "T" then
+ local trim, char = rest:match("rim(L?R?)(.)$")
+ if trim == "L" or trim == "" then
+ oper = " val = val:gsub('^(" .. char .. "*)', '')"
+ end
+ if trim == "R" or trim == "" then
+ oper = " val = val:gsub('(" .. char .. "*)$', '')"
+ end
+ elseif first == "P" then
+ local pad, count, char = rest:match("ad(L?R?)(%d-)(.)$")
+ if pad == "L" then
+ oper = " val = string.rep('" .. char .. "', " .. count .. " - #val) .. val"
+ elseif pad == "R" then
+ oper = "val = val .. string.rep('" .. char .. "', " .. count .. " - #val)"
+ elseif pad == nil then
+ local prefix = rest:match("refix(.*)")
+ if prefix then
+ oper = " val = '" .. rest .. "' .. val"
+ end
+ end
+ elseif first == "S" then
+ local suffix = rest:match("uffix(.*)")
+ if suffix then
+ oper = " val = val .. '" .. rest .. "'"
+ end
+ elseif (first == "h" and rest == "exadecimal_string") or (first == "0" and rest == "x") then
+ oper = " val = string.format('0x%x', val)"
+ elseif (first == "L" and rest == "oopIndex") then
+ oper = " val = index"
+ end
+ if not oper then
+ oper = " val = format['" .. form .. "'](val, {"
+ for num1, colpar in ipairs(fparam[form]) do
+ oper = oper .. "arr['" .. colpar .. "'][i].val or arr['" .. colpar .. "'][1].val,"
+ end
+ oper = oper .. "})"
+ end
+ ret = ret .. oper
+ end
+ return ret .. " return val\nend"
+ end
+
+ if xml.format then
+ local format = { "format = {" }
+ for num, form in ipairs(xml.format) do
+ local param = {}
+ format[#format + 1] = "['" .. form["id"] .. "'] = "
+ --[[if form["input-as-subcolumns-input"] then
+ format[#format + 1] = "input_as_subcolumns_input = '" .. form["input-as-subcolumns-input"] .. "',"
+ end]]--
+ format[#format + 1] = "function(val, param) "
+ if form["formatter"] then
+ format[#format + 1] = "function tempform(val) "
+ end
+ if form["apply-to"] == "char" then
+ format[#format + 1] = "val = val:gsub('(.)', function(val) "
+ end
+ format[#format + 1] = "local temp = val"
+ for num1, op in ipairs(form) do
+ if op.tag == "add" then
+ format[#format + 1] = "val = val + " .. op.text
+ elseif op.tag == "prefix" then
+ format[#format + 1] = "val = '" .. op.text .. "' .. val "
+ elseif op.tag == "suffix" then
+ format[#format + 1] = "val = val .. '" .. op.text .. "'"
+ elseif op.tag == "multiply" then
+ format[#format + 1] = "val = val * " .. op.text
+ elseif op.tag == "divide" then
+ format[#format + 1] = "val = val / " .. op.text
+ elseif op.tag == "sum" then
+ format[#format + 1] = "val = 0"
+ for num2, col in ipairs(op) do
+ param[#param + 1] = col["id"]
+ if col["format"] then
+ local colform = check_format(col["format"])
+ format[#format + 1] = colform .. " val = val + tempform(val)"
+ else
+ format[#format + 1] = "val = val + param[" .. #param .. "]"
+ end
+ end
+ elseif op.tag == "concat" then
+ format[#format + 1] = "val = ''"
+ for num2, col in ipairs(op) do
+ if col["tag"] == "txt" then
+ format[#format + 1] = "val = val .. '" .. col["text"] .. "'"
+ elseif col["format"] then
+ param[#param + 1] = col["id"]
+ local n = #param
+ format[#format + 1] = function() return " " .. check_format(col["format"]) .. " val = val .. tempform(param[" .. n .. "])" end
+ else
+ param[#param + 1] = col["id"]
+ format[#format + 1] = "val = val .. param[" .. #param .. "]"
+ end
+ end
+ elseif op.tag == "min" then
+ format[#format + 1] = "val = 0x7fffffffffffffff"
+ for num2, col in ipairs(op) do
+ param[#param + 1] = col["id"]
+ format[#format + 1] = "val = math.min(val, param[" .. #param .. "])"
+ end
+ elseif op.tag == "max" then
+ format[#format + 1] = "val = 0"
+ for num2, col in ipairs(op) do
+ param[#param + 1] = col["id"]
+ format[#format + 1] = "val = math.max(val, param[" .. #param .. "])"
+ end
+ elseif op.tag == "pad" then
+ format[#format + 1] = "if type(val) == 'number' then val = tostring(val) end"
+ format[#format + 1] = "if #val < " .. op.max .. " then"
+ if op.direction == "left" then
+ format[#format + 1] = "val = string.rep('" .. op.text .. "', " .. op.max .. " - #val) .. val"
+ elseif op.direction == "right" then
+ format[#format + 1] = "val = val .. string.rep('" .. op.text .. "', " .. op.max .. " - #val)"
+ end
+ format[#format + 1] = "end"
+ elseif op.tag == "trim" then
+ if op.direction == "left" or op.direction == "both" then
+ format[#format + 1] = "val = val:gsub('^(" .. op.text .. "*)', '')"
+ end
+ if op.direction == "right" or op.direction == "both" then
+ format[#format + 1] = "val = val:gsub('(" .. op.text .. "*)$', '')"
+ end
+ elseif op.tag == "substract" then
+ format[#format + 1] = "val = val - " .. op.text
+ elseif op.tag == "remainder" then
+ format[#format + 1] = "val = val % " .. op.text
+ elseif op.tag == "trunc" then
+ format[#format + 1] = "val = math.floor(val)"
+ elseif op.tag == "round" then
+ format[#format + 1] = "val = math.floor(val + 0.5)"
+ elseif op.tag == "divide_trunc" then
+ format[#format + 1] = "val = math.floor(val / " .. op.text .. ")"
+ elseif op.tag == "divide_round" then
+ format[#format + 1] = "val = math.floor((val / " .. op.text .. ") + 0.5)"
+ elseif op.tag == "replace" then
+ format[#format + 1] = "val = val:gsub('" .. op.src .. "', '" .. op.dst .. "')"
+ elseif op.tag == "shift" then
+ format[#format + 1] = "val = val << " .. op.text
+ elseif op.tag == "lowercase" then
+ format[#format + 1] = "val = val:lower()"
+ elseif op.tag == "uppercase" then
+ format[#format + 1] = "val = val:upper()"
+ elseif op.tag == "capitalize" then
+ format[#format + 1] = "val = val:gsub('^(.)', function(s) return s:upper() end)"
+ elseif op.tag == "loopindex" then
+ param[#param + 1] = "loopindex"
+ format[#format + 1] = "val = param[" .. #param .. "]"
+ elseif op.tag == "case" then
+ format[#format + 1] = "val = temp"
+ if not tonumber(op["src"]) then
+ op["src"] = "'" .. op["src"] .. "'"
+ end
+ if not tonumber(op["dst"]) then
+ op["dst"] = "'" .. op["dst"] .. "'"
+ end
+ if op["default"] == "yes" then
+ format[#format + 1] = "local default = " .. op["dst"]
+ end
+ if op["operator-format"] then
+ format[#format + 1] = function() return " val = ".. check_format(col["operator-format"]) end
+ end
+ if not op["operator"] then
+ op["operator"] = "=="
+ else
+ op["operator"] = op["operator"]:gsub("&lt;", "<")
+ op["operator"] = op["operator"]:gsub("&gt;", ">")
+ end
+ format[#format + 1] = "if val " .. op["operator"] .. " " .. op["src"] .. " then"
+ format[#format + 1] = "val = " .. op["dst"]
+ if op["format"] then
+ format[#format + 1] = function() return " val = ".. check_format(col["operator-format"]) end
+ end
+ format[#format + 1] = "return val\n end"
+ end
+
+ end
+ fparam[form["id"]] = param
+ if form["apply-to"] == "char" then
+ format[#format + 1] = "if default then\nreturn default\nend\nreturn val\nend)\nreturn val\nend"
+ else
+ format[#format + 1] = "if default then\nreturn default\nend\nreturn val\nend"
+ end
+ if form["formatter"] then
+ format[#format + 1] = "return string.format('" .. form["formatter"] .. "', tempform(val))\nend"
+ end
+ format[#format + 1] = ","
+ end
+ for num, line in ipairs(format) do
+ if type(line) == "string" then
+ s[#s + 1] = line
+ elseif type(line) == "function" then
+ s[#s + 1] = line()
+ end
+ end
+ s[#s + 1] = "}"
+ end
+ local function parse_elem(elem, loopelem)
+ local ret = 0
+ if elem["tag"] == "loop" then
+ if elem["skip-first-bytes"] then
+ s[#s + 1] = "offset = offset + " .. elem["skip-first-bytes"]
+ end
+ s[#s + 1] = "for i = 1, " .. elem["count"] .. " do"
+ for num, elt in ipairs(elem) do
+ index = parse_elem(elt, elem)
+ end
+ s[#s + 1] = "end"
+ if elem["skip-last-bytes"] then
+ s[#s + 1] = "offset = offset + " .. elem["skip-last-bytes"]
+ end
+ elseif elem["tag"] == "elt" then
+ s[#s + 1] = "if not arr['" .. elem["id"] .. "'] then arr['" .. elem["id"] .. "'] = {} end\nelem = {}"
+ s[#s + 1] = "bytes = table.pack(string.unpack('" .. string.rep("c1", elem["size"]) .. "', data, offset))"
+ if loopelem then
+ total_size = total_size + elem["size"] * loopelem["count"]
+ else
+ total_size = total_size + elem["size"]
+ end
+ s[#s + 1] = "offset = table.remove(bytes)"
+ if elem["decoding-profile"] then
+ if elem["decoding-profile"] == "base-40" then
+ elem["src-unit-size"] = 16
+ elem["base"] = "40"
+ elem["dst-unit-size"] = 3
+ elem["ascii-offset"] = 64
+ elseif elem["decoding-profile"] == "base-32" then
+ elem["src-unit-size"] = 16
+ elem["base"] = "32"
+ elem["dst-unit-size"] = 3
+ elem["ascii-offset"] = 64
+ elseif elem["decoding-profile"] == "bcd" then
+ elem["endianness"] = "big-endian"
+ elem["nibble-skip"] = "odd"
+ elem["base"] = "16"
+ elseif elem["decoding-profile"] == "bcd-le" then
+ elem["endianness"] = "big-endian"
+ elem["nibble-skip"] = "odd"
+ elem["base"] = "16"
+ end
+ end
+ local bytedec = elem["swap-skip-order"] or "byte-swap;bit-swap;byte-skip;endianness;byte-trim;nibble-skip;bitmask"
+ local bytedecl = {}
+ bytedec:gsub("([^;]*)", function(c) bytedecl[#bytedecl + 1] = c end)
+ for num, func in ipairs(bytedecl) do
+ if elem[func] then
+ fixfunc = func:gsub("-", "_")
+ if func == "bitmask" then
+ s[#s + 1] = "bytes = " .. fixfunc .. "(bytes, bitmask['" .. elem[func] .. "'])"
+ else
+ s[#s + 1] = "bytes = " .. fixfunc .. "(bytes, '" .. elem[func] .. "')"
+ end
+ end
+ end
+ if elem["type"] == "int" then
+ s[#s + 1] = "value = string.unpack('>I' .. #bytes, table.concat(bytes))"
+ elem["base"] = elem["base"] or "10"
+ if elem["base"] == "16" then
+ s[#s + 1] = "value = frombcd(value)"
+ end
+ s[#s + 1] = "elem.val = value"
+ elseif elem["type"] == "text" then
+ if elem["ascii-step"] then
+ s[#s + 1] = "bytes = ascii_step(bytes, " .. elem["ascii-step"] .. ")"
+ end
+ if elem["ascii-offset"] then
+ s[#s + 1] = "bytes = ascii_offset(bytes, " .. elem["ascii-offset"] .. ")"
+ end
+ if elem["base"] then
+ s[#s + 1] = "bytes = basechar(bytes, " .. elem["base"] .. ")"
+ end
+ if elem["charset"] then
+ local charsets = {}
+ elem["charset"]:gsub("([^;]*)", function(s) charsets[#charsets + 1] = s return "" end)
+ for num, charset in pairs(charsets) do
+ if charset:match("^CS_") then
+ s[#s + 1] = "bytes = charset_conv(bytes, " .. charset .. ")"
+ elseif charset ~= "" then
+ s[#s + 1] = "bytes = charset_conv(bytes, charset['" .. charset .. "'])"
+ end
+ end
+ end
+ s[#s + 1] = "elem.val = table.concat(bytes)"
+ end
+ local index
+ if elem["table-index"] then
+ index = tonumber(elem["table-index"])
+ end
+ if not index and loopelem then
+ local total = loopelem["count"]
+ local start = loopelem["start"] or 0
+ local step = loopelem["step"] or 1
+ local ref, reftype
+ if elem["table-index"] then
+ elem["table-index"]:match("([%w ]*):([%a_]*)")
+ end
+ if not elem["table-index"] or elem["table-index"] == "loop_index" then
+ index = "(i - 1) * " .. step .. " + " .. start
+ elseif elem["table-index"] == "loop_reverse_index" then
+ index = "(" .. total .. "- i) * " .. step .. " + " .. start
+ elseif elem["table-index"] == "itself" then
+ index = "value"
+ elseif elem["table-index"] == "last" then
+ index = "lastindex"
+ elseif reftype then
+ index = reftype .. "(arr, '" .. ref .. "')"
+ end
+ end
+ if index then
+ s[#s + 1] = "elem.index = " .. index
+ s[#s + 1] = "lastindex = elem.index"
+ end
+ s[#s + 1] = "arr['" .. elem["id"] .. "'][#arr['" .. elem["id"] .. "'] + 1] = elem"
+ end
+ return index
+ end
+ for num, elem in ipairs(xml.structure[1]) do
+ if elem["tag"] == "loop" or elem["tag"] == "elt" then
+ parse_elem(elem)
+ end
+ end
+ table.insert(s, 1, "size = " .. total_size)
+
+ s[#s + 1] = "output = ''"
+
+ for num1, fld in ipairs(xml.output[1]) do
+ if not fld["display"] or fld["display"] == "always" then
+ if fld["tag"] == "field" then
+ if not fld["src"] then
+ fld["src"] = fld["id"]
+ end
+ s[#s + 1] = "output = output .. '" .. fld["id"] .. " '"
+ s[#s + 1] = "val = arr['" .. fld["src"] .. "'][1]"
+ if fld["format"] then
+ s[#s + 1] = check_format(fld["format"])
+ s[#s + 1] = "val = tempform(val)"
+ end
+ s[#s + 1] = "output = output .. val .. '\\n'"
+ elseif fld["tag"] == "table" then
+ local head = {}
+ local dat = {}
+ local loopcnt
+ local igncol, ignval
+ if fld["line-ignore"] then
+ igncol, ignval = fld["line-ignore"]:match("([^:]*):(.*)")
+ end
+ for num2, col in ipairs(fld["column"]) do
+ if not col["display"] or col["display"] == "always" then
+ if not col["src"] then
+ col["src"] = col["id"]
+ end
+ if not loopcnt and col["src"] ~= "index" then
+ table.insert(dat, 1, "for i = 1, #arr['" .. col["src"] .. "'] do")
+ table.insert(dat, 2, "index = arr['" .. col["src"] .. "'][i].index")
+ table.insert(dat, 3, "line = ''")
+ loopcnt = true
+ end
+ head[#head + 1] = "output = output .. '" .. col["id"] .. "\\t'"
+ if col["src"] == "index" then
+ dat[#dat + 1] = "val = index"
+ else
+ dat[#dat + 1] = "if arr['" .. col["src"] .. "'] then val = arr['" .. col["src"] .. "'][i].val end"
+ end
+ if col["format"] then
+ dat[#dat + 1] = check_format(col["format"])
+ dat[#dat + 1] = "val = tempform(val)"
+ end
+ if igncol == col["id"] then
+ dat[#dat + 1] = "checkval = val"
+ end
+ dat[#dat + 1] = "line = line .. val .. '\\t'"
+ end
+ end
+ if igncol then
+ dat[#dat + 1] = "if checkval ~= " .. ignval .. " then output = output .. line .. '\\n' end\nend"
+ else
+ dat[#dat + 1] = "output = output .. line .. '\\n'\nend"
+ end
+ s[#s + 1] = table.concat(head, "\n") .. "\noutput = output .. '\\n'"
+ s[#s + 1] = table.concat(dat, "\n")
+ end
+ end
+ end
+ s[#s + 1] = "return output"
+
+ -- cache script
+ local script = table.concat(s, "\n")
+ local scrpath = datpath .. "/"
+ local scrfile = io.open(scrpath .. set .. ".lua", "w+")
+ if not scrfile then
+ lfs.mkdir(scrpath)
+ scrfile = io.open(scrpath .. set .. ".lua", "w+")
+ end
+ if scrfile then
+ scrfile:write(script)
+ end
+ return script
+ end
+
+ if #env == 0 then
+ function env.open(file, size)
+ if file == ".hi" then
+ local path = "hi"
+ local ini = emu.file(lfs.env_replace(manager:options().entries.inipath:value()), 1)
+ local ret = ini:open("hiscore.ini")
+ if not ret then
+ local inifile = ini:read(ini:size())
+ for line in inifile:gmatch("[^\n\r]") do
+ token, value = string.match(line, '([^ ]+) ([^ ]+)');
+ if token == "hi_path" then
+ path = value
+ break
+ end
+ end
+ end
+ file = path .. "/" .. set .. ".hi"
+ else
+ file = lfs.env_replace(manager:options().entries.nvram_directory:value()) .. "/" .. set .. "/" .. file
+ end
+ local f = io.open(file, "rb")
+ local content = f:read("*all")
+ f:close()
+ if #content < size then
+ content = content .. string.rep("\0", size - #content)
+ end
+ return content
+ end
+
+ function env.endianness(bytes, endian)
+ local newbytes = {}
+ if endian == "little_endian" then
+ for i = 1, #bytes do
+ newbytes[i] = bytes[#bytes - i + 1]
+ end
+ else
+ newbytes = bytes
+ end
+ return newbytes
+ end
+
+ function env.byte_skip(bytes, skip)
+ local newbytes = {}
+ if skip == "odd" then
+ -- lua lists are 1 based so use even indexes
+ for i = 2, #bytes, 2 do
+ newbytes[i/2] = bytes[i]
+ end
+ elseif skip == "even" then
+ for i = 1, #bytes, 2 do
+ newbytes[(i+1)/2] = bytes[i]
+ end
+ elseif skip == "1000" then
+ for i = 1, #bytes, 4 do
+ newbytes[(i+3)/4] = bytes[i]
+ end
+ elseif skip == "0100" then
+ for i = 2, #bytes, 4 do
+ newbytes[(i+2)/4] = bytes[i]
+ end
+ elseif skip == "0010" then
+ for i = 3, #bytes, 4 do
+ newbytes[(i+1)/4] = bytes[i]
+ end
+ elseif skip == "0001" then
+ for i = 4, #bytes, 4 do
+ newbytes[i/4] = bytes[i]
+ end
+ else
+ skip = tonumber(skip)
+ for i = 1, #bytes do
+ if bytes[i] ~= skip then
+ newbytes[#newbytes + 1] = bytes[i]
+ end
+ end
+ end
+ return newbytes
+ end
+
+ function env.byte_trim(bytes, val)
+ val = tonumber(val)
+ for i = 1, #bytes do
+ if bytes[i] ~= val then
+ return bytes
+ end
+ table.remove(bytes, 1)
+ end
+ return bytes
+ end
+
+ function env.byte_swap(bytes, val)
+ local newbytes = {}
+ val = tonumber(val)
+ for i = 1, #bytes do
+ local off = i + val - 1 - 2 * ((i - 1) % val)
+ if off > #bytes then -- ??
+ break
+ end
+ newbytes[i] = bytes[off]
+ end
+ return newbytes
+ end
+
+ function env.nibble_skip(bytes, skip)
+ local newbytes = {}
+ if skip == "odd" then
+ for i = 1, #bytes, 2 do
+ val = bytes[i]:byte(1)
+ newbytes[(i+1)/2] = string.char(((val & 0x0f) << 4) | (val & 0x0f))
+ end
+ elseif skip == "even" then
+ for i = 1, #bytes, 2 do
+ val = bytes[i]:byte(1)
+ newbytes[(i+1)/2] = string.char((val & 0xf0) | ((val & 0xf0) >> 4))
+ end
+ end
+ return newbytes
+ end
+
+ function env.bit_swap(bytes, swap)
+ if swap == "yes" then
+ for i = 1, #bytes do
+ val = bytes[i]:byte(1)
+ bytes[i] = string.char(((val & 1) << 7) | ((val & 2) << 5) | ((val & 4) << 3) | ((val & 8) << 1) | ((val & 0x10) >> 1) | ((val & 0x20) >> 3) | ((val & 0x40) >> 5) | ((val & 0x80) >> 7))
+ end
+ end
+ return bytes
+ end
+
+ function env.bitmask(bytes, mask)
+ local newbytes = 0
+ bytes = string.unpack(">I" .. #bytes, table.concat(bytes))
+ for i = 1, #mask do
+ newbytes = newbytes | (((bytes >> mask.ishift) & mask.mask) << mask.oshift)
+ end
+ bytes = {}
+ while newbytes ~= 0 do
+ bytes[#bytes + 1] = newbytes & 0xff
+ newbytes = newbytes >> 8
+ end
+ newbytes = {}
+ for i = 1, #bytes do
+ newbytes[i] = string.char(bytes[#bytes + 1 - i])
+ end
+ return newbytes
+ end
+
+ function env.frombcd(val)
+ local result = 0
+ local mul = 1
+ while val ~= 0 do
+ result = result + ((val % 16) * mul)
+ val = val >> 4
+ mul = mul * 10
+ end
+ return result
+ end
+
+ function env.basechar(bytes, base)
+ if base == "32" then
+ elseif base == "40" then
+ end
+ return bytes
+ end
+
+ function env.charset_conv(bytes, charset)
+ if type(charset) == "string" then
+ local chartype, offset, delta = charset:match("CS_(%w*)%[?(%-?%d?%d?),?(%d?%d?)%]?")
+ if chartype == "NUMBER" then
+
+ end
+ return
+ end
+ for num, char in ipairs(bytes) do
+ char = string.byte(char)
+ if charset[char] then
+ bytes[num] = charset[char]
+ elseif charset.default then
+ bytes[num] = charset.default
+ end
+ end
+ return bytes
+ end
+
+ function env.ascii_step(bytes, step)
+ for num, char in ipairs(bytes) do
+ bytes[num] = string.char(char:byte() / step)
+ end
+ return bytes
+ end
+
+ function env.ascii_offset(bytes, offset)
+ for num, char in ipairs(bytes) do
+ bytes[num] = string.char(char:byte() + offset)
+ end
+ return bytes
+ end
+
+ env.tostring = tostring
+ env.type = type
+ env.table = { pack = table.pack, concat = table.concat, insert = table.insert, remove = table.remove }
+ env.string = { unpack = string.unpack, format = string.format, rep = string.rep, gsub = string.gsub, lower = string.lower, upper = string.upper }
+ env.math = { min = math.min, max = math.max, floor = math.floor }
+ end
+
+ if curset == set then
+ if output then
+ return "High Scores"
+ else
+ return nil
+ end
+ end
+ output = nil
+ curset = set
+
+ local scrfile = emu.file(lfs.env_replace(mame_manager:ui():options().entries.historypath:value():gsub("([^;]+)", "%1/hi2txt")), 1)
+ local ret = scrfile:open(set .. ".lua")
+ local script
+ if ret then
+ local xml = xml_parse()
+ if not xml then
+ return nil
+ end
+ local status
+ status, script = pcall(parse_table, xml)
+ if not status then
+ emu.print_verbose("error creating hi score parse script: " .. script)
+ return nil
+ end
+ else
+ script = scrfile:read(scrfile:size())
+ end
+ local scr, err = load(script, script, "t", env)
+ if err then
+ emu.print_verbose("error loading hi score script file: " .. err)
+ else
+ status, output = pcall(scr, xml_table)
+ if not status then
+ emu.print_verbose("error in hi score parse script: " .. output)
+ output = nil
+ end
+ end
+ if output then
+ return "High Scores"
+ else
+ return nil
+ end
+end
+
+function dat.get()
+ return output
+end
+
+return dat
diff --git a/plugins/data/data_history.lua b/plugins/data/data_history.lua
new file mode 100644
index 00000000000..29da2f62fd5
--- /dev/null
+++ b/plugins/data/data_history.lua
@@ -0,0 +1,22 @@
+local dat = {}
+local ver, info
+local datread = require("data/load_dat")
+datread, ver = datread.open("history.dat", "## REVISION:")
+
+function dat.check(set, softlist)
+ if not datread then
+ return nil
+ end
+ local status
+ status, info = pcall(datread, "bio", softlist or "info", set)
+ if not status or not info then
+ return nil
+ end
+ return "History"
+end
+
+function dat.get()
+ return info
+end
+
+return dat
diff --git a/plugins/data/data_mameinfo.lua b/plugins/data/data_mameinfo.lua
new file mode 100644
index 00000000000..c22175b91e4
--- /dev/null
+++ b/plugins/data/data_mameinfo.lua
@@ -0,0 +1,22 @@
+local dat = {}
+local ver, info
+local datread = require("data/load_dat")
+datread, ver = datread.open("mameinfo.dat", "# MAMEINFO.DAT")
+
+function dat.check(set, softlist)
+ if softlist or not datread then
+ return nil
+ end
+ local status
+ status, info = pcall(datread, "mame", "info", set)
+ if not status or not info then
+ return nil
+ end
+ return "Mameinfo"
+end
+
+function dat.get()
+ return info
+end
+
+return dat
diff --git a/plugins/data/data_messinfo.lua b/plugins/data/data_messinfo.lua
new file mode 100644
index 00000000000..f616882cb78
--- /dev/null
+++ b/plugins/data/data_messinfo.lua
@@ -0,0 +1,23 @@
+local dat = {}
+local ver, info
+
+local datread = require("data/load_dat")
+datread, ver = datread.open("messinfo.dat", "# MESSINFO.DAT")
+
+function dat.check(set, softlist)
+ if softlist or not datread then
+ return nil
+ end
+ local status
+ status, info = pcall(datread, "mame", "info", set)
+ if not status or not info then
+ return nil
+ end
+ return "Messinfo"
+end
+
+function dat.get()
+ return info
+end
+
+return dat
diff --git a/plugins/data/data_story.lua b/plugins/data/data_story.lua
new file mode 100644
index 00000000000..398fba2da4f
--- /dev/null
+++ b/plugins/data/data_story.lua
@@ -0,0 +1,28 @@
+local dat = {}
+local ver, info
+local datread = require("data/load_dat")
+local space_wid = mame_manager:ui():get_char_width(0x200a)
+datread, ver = datread.open("story.dat", "# version")
+
+function dat.check(set, softlist)
+ if softlist or not datread then
+ return nil
+ end
+ local status
+ status, info = pcall(datread, "story", "info", set)
+ if not status or not info then
+ return nil
+ end
+ info = "#jf\n" .. info:gsub("([^%s]-)(%f[_]_+%f[0-9])([0-9.]+)",
+ function(name, sep, score)
+ local wid = mame_manager:ui():get_string_width(name .. score, 1.0)
+ return name .. string.rep(utf8.char(0x200a), math.floor((.4 - wid) / space_wid)) .. score
+ end)
+ return "Mamescore"
+end
+
+function dat.get()
+ return info
+end
+
+return dat
diff --git a/plugins/data/data_sysinfo.lua b/plugins/data/data_sysinfo.lua
new file mode 100644
index 00000000000..2a545cad1d5
--- /dev/null
+++ b/plugins/data/data_sysinfo.lua
@@ -0,0 +1,22 @@
+local dat = {}
+local ver, info
+local datread = require("data/load_dat")
+datread, ver = datread.open("sysinfo.dat", "# This file was generated on")
+
+function dat.check(set, softlist)
+ if softlist or not datread then
+ return nil
+ end
+ local status
+ status, info = pcall(datread, "bio", "info", set)
+ if not status or not info then
+ return nil
+ end
+ return "Sysinfo"
+end
+
+function dat.get()
+ return info
+end
+
+return dat
diff --git a/plugins/data/init.lua b/plugins/data/init.lua
new file mode 100644
index 00000000000..9b947cc69f9
--- /dev/null
+++ b/plugins/data/init.lua
@@ -0,0 +1,60 @@
+-- license:BSD-3-Clause
+-- copyright-holders:Carl
+-- A data script should contain two functions check which takes a set name and returns the data
+-- heading if it supports the set otherwise nil and get which returns the data
+-- the script should be named data_<name>.lua
+-- this is set default on in the plugin.json
+local exports = {}
+exports.name = "data"
+exports.version = "0.0.1"
+exports.description = "Data plugin"
+exports.license = "The BSD 3-Clause License"
+exports.author = { name = "Carl" }
+
+local data = exports
+
+function data.set_folder(path)
+ data.path = path
+end
+
+function data.startplugin()
+ local data_scr = {}
+ local valid_lst = {}
+ emu.register_start(function()
+ for file in lfs.dir(data.path) do
+ local name = string.match(file, "^(data_.*).lua$")
+ if name then
+ local script = require("data/" .. name)
+ if script then
+ data_scr[#data_scr + 1] = script
+ end
+ end
+ end
+ end)
+ emu.register_callback(function(set)
+ local ret = ""
+ valid_lst = {}
+ for num, scr in ipairs(data_scr) do
+ local setname, softname = set:match("^([^,]+),?(.*)$")
+ if softname == "" then
+ softname = nil
+ end
+ local name = scr.check(setname, softname)
+ if name then
+ if ret == "" then
+ ret = name
+ else
+ ret = ret .. "," .. name
+ end
+ valid_lst[#valid_lst + 1] = scr
+ end
+ end
+ return ret
+ end, "data_list")
+
+ emu.register_callback(function(num)
+ return valid_lst[num + 1].get()
+ end, "data")
+end
+
+return exports
diff --git a/plugins/data/load_dat.lua b/plugins/data/load_dat.lua
new file mode 100644
index 00000000000..a113da7fd63
--- /dev/null
+++ b/plugins/data/load_dat.lua
@@ -0,0 +1,97 @@
+local datfile = {}
+
+function datfile.open(file, vertag)
+ local data = {}
+ local ver
+ local filepath
+ local fh
+ for path in mame_manager:ui():options().entries.historypath:value():gmatch("([^;]+)") do
+ filepath = lfs.env_replace(path) .. "/" .. file
+ fh = io.open(filepath, "rb")
+ if fh then
+ break
+ end
+ end
+ if not fh then
+ return nil
+ end
+ do
+ local inblock = false
+ local buffer = fh:read("a")
+ if vertag then
+ local match = buffer:match("^" .. vertag .. "%s([^%s]+)")
+ if match then
+ ver = match
+ end
+ end
+ local function gmatchpos()
+ local pos = 1
+ local function iter()
+ local spos, epos = buffer:find("\n$", pos, true)
+ if not spos then
+ return nil
+ end
+ spos = spos + 1
+ local spos, epos, match = buffer:find("([^\n]+)", spos)
+ pos = epos + 1
+ return match, pos, iter
+ end
+ return iter
+ end
+ for line, epos, iter in gmatchpos() do
+
+ local flag = line:sub(1, 1)
+ if flag ~= "#" then
+ if flag == "$" then
+ if line:sub(1, 4) == "$end" then
+ inblock = false
+ elseif not inblock then
+ local tag, set = line:match("^%$([^%s=]+)=?([^%s]*)")
+ if set and set ~= "" then
+ local tags = {}
+ local sets = {}
+ local tag1 = ""
+ tag:gsub("([^,]+)", function(s) tags[#tags + 1] = s end)
+ set:gsub("([^,]+)", function(s) sets[#sets + 1] = s end)
+ repeat
+ tag1, epos = iter()
+ until tag1:sub(1, 1) == "$"
+ tag1 = tag1:match("^$([^%s]*)")
+ if not data[tag1] then
+ data[tag1] = {}
+ end
+ for num1, tag2 in pairs(tags) do
+ if not data[tag1][tag2] then
+ data[tag1][tag2] = {}
+ end
+ for num2, set in pairs(sets) do
+ data[tag1][tag2][set] = epos
+ end
+ end
+ end
+ inblock = true
+ end
+ end
+ end
+ end
+ end
+ fh:close()
+ fh = io.open(filepath, "r")
+ local function read(tag1, tag2, set)
+ local output = {}
+ if not data[tag1][tag2][set] then
+ return nil
+ end
+ fh:seek("set", data[tag1][tag2][set])
+ for line in fh:lines() do
+ if line == "$end" then
+ return table.concat(output, "\n")
+ end
+ output[#output + 1] = line
+ end
+ end
+
+ return read, ver
+end
+
+return datfile
diff --git a/plugins/data/plugin.json b/plugins/data/plugin.json
new file mode 100644
index 00000000000..89c36d6bd36
--- /dev/null
+++ b/plugins/data/plugin.json
@@ -0,0 +1,10 @@
+{
+ "plugin": {
+ "name": "data",
+ "description": "Data plugin",
+ "version": "0.0.1",
+ "author": "Carl",
+ "type": "plugin",
+ "start": "true"
+ }
+}