Module:Files/Address
From CloudModding MM Wiki
This module is used to implement a template that generates address boxes
Input
Header
- space - Denotes what "space" the address box will use for reference. supported spaces are offset, ram, rom, manual, the global context and certain files (at the moment, only code (File). Makes it simpler to document both ram and rom addresses with a single reference
- name - Sets address box's caption, or overrides the default caption if the space is set to offset, ram, or rom
- subpage - Sets super page for the address table, and adds a link to each row to the relevant subpage for that version.
- hideoff - Optional. Hides the offset column if set to true. Default value is false
- hideend - Optional. Hides the end column(s) if set to true. Default value is false
Records
- ver# - Sets version name. Only a specific set of values will be accepted here
- ref# - Sets what type of reference is being recorded. Values are offset, ram, rom, and manual.
If ref# is set to offset, ram, or rom, you can use the following parameters:
- start# - Sets the start address or offset for the reference
- end# - Optional. Sets the end address or offset for the reference
- size# - Optional. Sets the size spanned by the reference
Otherwise, when ref# is set to manual, you can set the following parameters.
- offset# - Sets the start offset for the reference
- ram-start# - sets the ram address start for the reference
- ram-end#
- rom-start#
- rom-end#
Note that offset# can be omitted if hideoff = true. Similarly, if space = ram, rom-start#/end# can be omitted, and if space = rom, ram-start#/end# can be omitted.
local p = {}
local origArgs
local args = {}
local root
local yesno = require('Module:Yesno')
local versions =
{
["Invalid"]={ k=-1, n="Invalid Version Code"},
["debug"]={ k=1, n="Debug" },
["j 1.0"]={ k=2, n="J 1.0" },
["j 1.1"]={ k=3, n="J 1.1" },
["u 1.0"]={ k=4, n="U 1.0" },
["ntsc-j 1.0"]={ k=2, n="J 1.0" },
["ntsc-j 1.1"]={ k=3, n="J 1.1" },
["ntsc-u"]={ k=4, n="U 1.0" },
["pal 1.0"]={ k=5, n="PAL 1.0" },
["pal 1.1"]={ k=6, n="PAL 1.1" },
["jp gc"]={ k=7, n="JP GC" },
["usa gc"]={ k=8, n="USA GC" },
["pal gc"]={ k=9, n="PAL GC" },
["demo"]={ k=10, n="Demo" }
}
local function GetVersionInfo(key) -- key is the string key used to look up a table value
local t = versions[key:lower()]
if t == nil then
t = versions["Invalid"]
end
return t
end
local data = {
ram = {
display="[[VRam]]", ram=true, rom=false, offset=false
},
rom = {
display="[[VRom]]", ram=false, rom=true, offset=false
},
off = {
display="Offset", ram=false, rom=false, offset=true
},
manual = {
display="<Set Display>", ram=true, rom=true, offset=true
},
code = {
display="[[Code (File)]]", ram=true, rom=true, offset=true,
["Debug"] = {
rom = { 0xC95000, 0xE12600 },
ram = { 0x800B6AC0, 0x802340C0 },
},
["J 1.0"] = {
rom = { 0xB5F000, 0xC9B6F0 },
ram = { 0x800A76A0, 0x801E3D90 },
},
["J 1.1"] = {
rom = { 0xB5F000, 0xC9BA60 },
ram = { 0x800A75E0, 0x801E4040 },
},
["U 1.0"] = {
rom = { 0xB3C000, 0xC7A4E0 },
ram = { 0x800A5AC0, 0x801E3FA0 },
},
["PAL 1.0"] = {
rom = { 0xC8A000, 0xDBF730 },
ram = { 0x800A5D60, 0x801DB490 },
},
["PAL 1.1"] = {
rom = { 0xC8A000, 0xDBF850 },
ram = { 0x800A5FE0, 0x801DB830 },
},
["JP GC"] = {
rom = { 0xB45000, 0xC80D40 },
ram = { 0x800A6420, 0x801E2160 },
},
["USA GC"] = {
rom = { 0xB4B000, 0xC86D30 },
ram = { 0x800A6440, 0x801E2170 },
},
["PAL GC"] = {
rom = { 0xC99000, 0xDCC820 },
ram = { 0x800A65A0, 0x801D9DC0 },
},
},
ovl_player_actor = {
display="[[ovl_player_actor|Link Actor]]", ram=true, rom=true, offset=true,
["Debug"] = {
rom = { 0xE46460, 0xE7FF20 },
ram = { 0x80833ED0, 0x8086DA20 },
},
["J 1.0"] = {
rom = { 0xCC9A20, 0xCFE660 },
ram = { 0x8082E390, 0x808630F0 },
},
["U 1.0"] = {
rom = { 0xCA7F00, 0xCDCF60 },
ram = { 0x8082DA90, 0x80862B70 },
},
},
ovl_kaleido_scope={
display="[[ovl_kaleido_scope|Pause Screen]]", ram=true, rom=true, offset=true,
["Debug"] = {
rom = { 0xE2C6A0, 0xE46460 },
ram = { 0x8081A0D0, 0x80833ED0 },
},
["J 1.0"] = {
rom = { 0xCB14B0, 0xCC9A20 },
ram = { 0x80815DE0, 0x8082E390 },
},
["U 1.0"] = {
rom = { 0xC90550, 0xCA7F00 },
ram = { 0x808160A0, 0x8082DA90 },
},
},
gctxt =
{
display="[[Global Context]]", ram=true, rom=false, offset=true,
["Debug"] = { ram = { 0x80448700, 0x80448700+0x019268 }, },
["J 1.0"] = { ram = { 0x803E6CF0, 0x803E6CF0+0x019238 }, },
["U 1.0"] = { ram = {0x803E6B20, 0x803E6B20+0x19258}, },
},
}
local function getRowOrder()
-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local prefix = 'ver'
local tbl = {}
local keys = {}
local nums = {}
for k, v in pairs(args) do --keys, values in arguments
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$') --get param group the version is bound to
if num then
local verId = GetVersionInfo(args[k]).k
table.insert(keys, verId);
tbl[verId] = tonumber(num) --key is sort order, value is row
end
end
table.sort(keys)
for _, k in ipairs(keys) do
table.insert(nums, tbl[k])
end
return nums
end
local function preprocessSingleArg(argName)
-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
local function preprocessArgs(prefixTable, step)
-- Assign the parameters with the given prefixes to the args table, in order, in batches
-- of the step size specified. This is to prevent references etc. from appearing in the
-- wrong order. The prefixTable should be an array containing tables, each of which has
-- two possible fields, a "prefix" string and a "depend" table. The function always parses
-- parameters containing the "prefix" string, but only parses parameters in the "depend"
-- table if the prefix parameter is present and non-blank.
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
if type(step) ~= 'number' then
error("Invalid step value detected", 2)
end
-- Get arguments without a number suffix, and check for bad input.
for i,v in ipairs(prefixTable) do
if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
-- Get arguments with number suffixes.
local a = 1 -- Counter variable.
local moreArgumentsExist = true
while moreArgumentsExist == true do
moreArgumentsExist = false
for i = a, a + step - 1 do
for j,v in ipairs(prefixTable) do
local prefixArgName = v.prefix .. tostring(i)
if origArgs[prefixArgName] then
moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.
preprocessSingleArg(prefixArgName)
end
-- Process the depend table if the prefix argument is present and not blank, or
-- we are processing "prefix1" and "prefix" is present and not blank, and
-- if the depend table is present.
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
for j,dependValue in ipairs(v.depend) do
local dependArgName = dependValue .. tostring(i)
preprocessSingleArg(dependArgName)
end
end
end
end
a = a + step
end
end
local function renderTableTop(spaceRef)
root = mw.html.create('table')
root
:attr('id', 'Addr')
:addClass('wikitable')
:attr('align', 'right')
local caption = root :tag('caption')
if args.name then
if args.space=='ram' or args.space=='rom' or args.space=='manual' then
caption:wikitext(args.name)
else
caption :wikitext(spaceRef.display .. '<br>' .. args.name)
end
else
caption :wikitext(spaceRef.display)
end
local header = root:tag('tr')
header
:tag('th')
:wikitext('Version')
if spaceRef.offset == true and args.hideoff ~= true then
header
:tag('th')
:wikitext('Offset')
end
local colspan = 2
if args.hideend == true then
colspan = 1
end
if spaceRef.rom then
header
:tag('th')
:attr('colspan',colspan)
:wikitext('[[VRom]]')
end
if spaceRef.ram then
header
:tag('th')
:attr('colspan',colspan)
:wikitext('[[VRam]]')
end
end
local function renderDataCell(row, value, f)
f = f or '%08X'
row :tag('td'):css('font-size', '90%'):tag('tt'):wikitext(string.format(f, value))
end
local function renderDataGroup(row, d)
-- renders cells with a shared header column (i.e. VRam/VRom)
if d.Start < 0 then
renderDataCell(row, "?", "%s")
else
renderDataCell(row, d.Start)
end
if args.hideend ~= true then
if d.End < d.Start or d.End < 0 then
renderDataCell(row, "?", "%s")
else
renderDataCell(row, d.End)
end
end
end
local function renderRow(ver, off, rom, ram)
if args.subpage then
ver = "[[" .. args.subpage .."/" .. ver .. "|" .. ver .. "]]"
end
local row = root:tag('tr')
row :tag('td'):wikitext(ver)
if off.Show and args.hideoff ~= true then
if off.Start < 0 then
renderDataCell(row, '?', '%s')
else
renderDataCell(row, off.Start, '%06X')
end
end
if rom.Show then
renderDataGroup(row, rom)
end
if ram.Show then
renderDataGroup(row, ram)
end
return result
end
local function processRowManual(spaceRef, rowArgs)
if not rowArgs.ver then return end
rowArgs.offset = rowArgs.offset == nil and -1 or tonumber(rowArgs.offset,16)
rowArgs['rom-start'] = rowArgs['rom-start'] == nil and -1 or tonumber(rowArgs['rom-start'],16)
rowArgs['rom-end'] = rowArgs['rom-end'] == nil and -1 or tonumber(rowArgs['rom-end'],16)
rowArgs['ram-start'] = rowArgs['ram-start'] == nil and -1 or tonumber(rowArgs['ram-start'],16)
rowArgs['ram-end'] = rowArgs['ram-end'] == nil and -1 or tonumber(rowArgs['ram-end'],16)
local off = { Show=spaceRef.offset, Start=rowArgs.offset, End=-1 }
local rom = { Show=spaceRef.rom, Start=rowArgs['rom-start'], End=rowArgs['rom-end'] }
local ram = { Show=spaceRef.ram, Start=rowArgs['ram-start'], End=rowArgs['ram-end'] }
renderRow(rowArgs.ver, off,rom,ram)
end
local function processRow(spaceRef, rowArgs)
if not rowArgs.ver or not rowArgs.start then return end
if not rowArgs.ref then
rowArgs.ref = 'off'
elseif rowArgs.ref == 'offset' then
rowArgs.ref = 'off'
end
local verAddr = spaceRef[rowArgs.ver] -- can be nil
local off = { Show=spaceRef.offset, Start=-1, End=-1 }
local rom = { Show=spaceRef.rom, Start=-1, End=-1 }
local ram = { Show=spaceRef.ram, Start=-1, End=-1 }
rowArgs.start = tonumber(rowArgs.start,16) %0x80000000
if rowArgs['end'] then
rowArgs['end'] = tonumber(rowArgs['end'],16) %0x80000000
end
if rowArgs.size then
rowArgs.size = tonumber(rowArgs.size,16)
end
if not rowArgs['end'] and rowArgs.size then
rowArgs['end'] = rowArgs.start + rowArgs.size
end
if not rowArgs['end'] then
rowArgs['end'] = -1 -- avoids proper error checking
end
-- Calculate data
if rowArgs.ref == 'off' then
off.Start = rowArgs.start
off.End = rowArgs['end']
if verAddr and ram.Show then
ram.Start = verAddr.ram[1] + off.Start
ram.End = verAddr.ram[1] + off.End
end
if verAddr and rom.Show then
rom.Start = verAddr.rom[1]+off.Start
rom.End = verAddr.rom[1]+off.End
end
elseif rowArgs.ref == 'ram' then
ram.Start = rowArgs.start + 0x80000000
ram.End = rowArgs['end'] + 0x80000000
if verAddr and off.Show then
off.Start = ram.Start - verAddr.ram[1]
off.End = ram.End - verAddr.ram[1]
end
if verAddr and rom.Show then
rom.Start = verAddr.rom[1]+off.Start
rom.End = verAddr.rom[1]+off.End
end
elseif rowArgs.ref == 'rom' then
rom.Start = rowArgs.start
rom.End = rowArgs['end']
if verAddr and off.Show then
off.Start = rom.Start - verAddr.rom[1]
off.End = rom.End - verAddr.rom[1]
end
if verAddr and ram.Show then
ram.Start = verAddr.ram[1] + off.Start
ram.End = verAddr.ram[1] + off.End
end
end
renderRow(rowArgs.ver, off,rom,ram)
end
local function renderRows(space)
local rownums = getRowOrder()
for k, num in ipairs(rownums) do
local ref = args['ref' .. tostring(num)]
local version = GetVersionInfo(args['ver' .. tostring(num)]).n
if ref and ref == 'manual' then
processRowManual(space,{
ver = version,
offset = args['offset' .. tostring(num)],
['rom-start'] = args['rom-start' .. tostring(num)],
['rom-end'] = args['rom-end' .. tostring(num)],
['ram-start'] = args['ram-start' .. tostring(num)],
['ram-end'] = args['ram-end' .. tostring(num)],
})
else
processRow(space,{
ver = version,
ref = args['ref' .. tostring(num)],
start = args['start' .. tostring(num)],
['end'] = args['end' .. tostring(num)],
size = args['size' .. tostring(num)],
})
end
end
end
local function _address()
local space
if args.space then
space = data[args.space]
else return "<Error: no space set>" end
renderTableTop(space)
renderRows(space)
return tostring(root)
end
-- performs auto-magic calculations by referencing a database
function p.address(frame)
-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame
end
preprocessSingleArg('space')
preprocessSingleArg('name')
preprocessSingleArg('hideoff')
preprocessSingleArg('hideend')
preprocessSingleArg('subpage')
preprocessArgs({
{ prefix = 'ver'},
{ prefix = 'ref'},
{ prefix = 'start'},
{ prefix = 'end'},
{ prefix = 'size'},
-- For manual declaration of addresses
{ prefix = 'offset' },
{ prefix = 'rom-start'},
{ prefix = 'rom-end'},
{ prefix = 'ram-start'},
{ prefix = 'ram-end'},
},10)
args.hideoff = args.hideoff == nil and false or yesno(args.hideoff)
args.hideend = args.hideend == nil and false or yesno(args.hideend)
return _address()
end
-- Genenerates a table of supported version codes
function p.versions(frame)
function compare (a,b) return a[1] < b[1] end
local troot = mw.html.create('table')
troot
:addClass('wikitable')
:addClass('mw-collapsible')
:addClass('mw-collapsed')
local caption = troot:tag('caption')
caption:wikitext("Supported Versions")
local header = troot:tag('tr')
header
:tag('th')
:wikitext('Code')
:tag('th')
:wikitext('Version')
local t = {}
for k, v in pairs(versions) do
table.insert(t, {v.k, k, v.n})
end
table.sort(t, compare)
for k, v in ipairs(t) do
local row = troot:tag('tr')
row:tag('td')
:wikitext(v[2])
:tag('td')
:wikitext(v[3])
end
return tostring(troot)
end
return p