1-- license:BSD-3-Clause 2-- copyright-holders:Carl 3-- data files are json files named <romname>.json 4-- { 5-- "import":"<import filename>" 6-- "ports":{ 7-- "<ioport name>":{ 8-- "labels":{ 9-- "<field mask>":{ 10-- "player":<int player number>, 11-- "name":"<field label>" 12-- } 13-- },{ 14-- ... 15-- } 16-- } 17-- } 18-- any additional metadata can be included for other usage 19-- and will be ignored 20local exports = {} 21exports.name = "portname" 22exports.version = "0.0.1" 23exports.description = "IOPort name/translation plugin" 24exports.license = "The BSD 3-Clause License" 25exports.author = { name = "Carl" } 26 27local portname = exports 28 29function portname.startplugin() 30 local json = require("json") 31 local ctrlrpath = lfs.env_replace(manager:options().entries.ctrlrpath:value():match("([^;]+)")) 32 local function get_filename(nosoft) 33 local filename 34 if emu.softname() ~= "" and not nosoft then 35 local soft = emu.softname():match("([^:]*)$") 36 filename = emu.romname() .. "_" .. soft .. ".json" 37 else 38 filename = emu.romname() .. ".json" 39 end 40 return filename 41 end 42 43 local function parse_names(ctable, depth) 44 if depth >= 5 then 45 emu.print_error("portname: max import depth exceeded\n") 46 return 47 end 48 if ctable.import then 49 local file = emu.file(ctrlrpath .. "/portname", "r") 50 local ret = file:open(ctable.import) 51 if not ret then 52 parse_names(json.parse(file:read(file:size())), depth + 1) 53 end 54 end 55 if not ctable.ports then 56 return 57 end 58 for pname, port in pairs(ctable.ports) do 59 local ioport = manager:machine():ioport().ports[pname] 60 if ioport then 61 for mask, label in pairs(port.labels) do 62 for num3, field in pairs(ioport.fields) do 63 local nummask = tonumber(mask, 16) 64 if nummask == field.mask and label.player == field.player then 65 field.live.name = label.name 66 end 67 end 68 end 69 end 70 end 71 end 72 73 emu.register_start(function() 74 local file = emu.file(ctrlrpath .. "/portname", "r") 75 local ret = file:open(get_filename()) 76 if ret then 77 if emu.softname() ~= "" then 78 local parent 79 for tag, image in pairs(manager:machine().images) do 80 parent = image.software_parent 81 if parent ~= "" then 82 break 83 end 84 end 85 if parent ~= "" then 86 ret = file:open(emu.romname() .. "_" .. parent:match("([^:]*)$") .. ".json") 87 end 88 end 89 if ret then 90 ret = file:open(get_filename(true)) 91 if ret then 92 ret = file:open(manager:machine():system().parent .. ".json") 93 if ret then 94 return 95 end 96 end 97 end 98 end 99 parse_names(json.parse(file:read(file:size())), 0) 100 end) 101 102 local function menu_populate() 103 return {{ _("Save input names to file"), "", 0 }} 104 end 105 106 local function menu_callback(index, event) 107 if event == "select" then 108 local ports = {} 109 for pname, port in pairs(manager:machine():ioport().ports) do 110 local labels = {} 111 local sort = {} 112 for fname, field in pairs(port.fields) do 113 local mask = string.format("%x", field.mask) 114 if not labels[mask] then 115 sort[#sort + 1] = mask 116 labels[mask] = { name = fname, player = field.player } 117 setmetatable(labels[mask], { __tojson = function(v,s) 118 local label = { name = v.name, player = v.player } 119 setmetatable(label, { __jsonorder = { "player", "name" }}) 120 return json.stringify(label) end }) 121 end 122 end 123 if #sort > 0 then 124 table.sort(sort, function(i, j) return tonumber(i, 16) < tonumber(j, 16) end) 125 setmetatable(labels, { __jsonorder = sort }) 126 ports[pname] = { labels = labels } 127 end 128 end 129 local function check_path(path) 130 local attr = lfs.attributes(path) 131 if not attr then 132 lfs.mkdir(path) 133 if not lfs.attributes(path) then 134 manager:machine():popmessage(_("Failed to save input name file")) 135 emu.print_verbose("portname: unable to create path " .. path .. "\n") 136 return false 137 end 138 elseif attr.mode ~= "directory" then 139 manager:machine():popmessage(_("Failed to save input name file")) 140 emu.print_verbose("portname: path exists but isn't directory " .. path .. "\n") 141 return false 142 end 143 return true 144 end 145 if not check_path(ctrlrpath) then 146 return false 147 end 148 if not check_path(ctrlrpath .. "/portname") then 149 return false 150 end 151 local filename = get_filename() 152 local file = io.open(ctrlrpath .. "/portname/" .. filename, "r") 153 if file then 154 emu.print_verbose("portname: input name file exists " .. filename .. "\n") 155 manager:machine():popmessage(_("Failed to save input name file")) 156 file:close() 157 return false 158 end 159 file = io.open(ctrlrpath .. "/portname/" .. filename, "w") 160 local ctable = { romname = emu.romname(), ports = ports } 161 if emu.softname() ~= "" then 162 ctable.softname = emu.softname() 163 end 164 setmetatable(ctable, { __jsonorder = { "romname", "softname", "ports" }}) 165 file:write(json.stringify(ctable, { indent = true })) 166 file:close() 167 manager:machine():popmessage(string.format(_("Input port name file saved to %s"), ctrlrpath .. "/portname/" .. filename)) 168 end 169 return false 170 end 171 172 emu.register_menu(menu_callback, menu_populate, _("Input ports")) 173end 174 175return exports 176