-
-
Save sfan5/9b366299a7e3a2103ffc70cdc36806b6 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local function formspec_escape(text) | |
text = string.gsub(text,"\\","\\\\") | |
text = string.gsub(text,"%]","\\]") | |
text = string.gsub(text,"%[","\\[") | |
text = string.gsub(text,";","\\;") | |
text = string.gsub(text,",","\\,") | |
return text | |
end | |
local function assert_fields(tbl, names, i) | |
for _, name in ipairs(names) do | |
local found = false | |
for fieldname, _ in ipairs(tbl) do | |
if fieldname == name then | |
found = true | |
end | |
end | |
if not found then | |
error("Element " .. i .. " has no '" .. name .. "' field") | |
end | |
end | |
end | |
local function assert_type(tbl, eln, ex, i) | |
if type(tbl[eln]) ~= ex then | |
error("The '" .. eln .. "' field of Element " .. i .. " is of type '" .. type(tbl[eln]) | |
.. "', expected was '" .. ex .. "'") | |
end | |
end | |
local function boolstr(b) | |
if b == true then | |
return "true" | |
else | |
return "false" | |
end | |
end | |
local conv_tbl = { | |
-- Type additional Check Func args arg types template | |
{"invsize", true, {"w", "h"}, {"number", "number"}, "size[{w},{h}]"}, | |
{"size", true, {"w", "h"}, {"number", "number"}, "size[{w},{h}]"}, | |
{"list", function(t) return t.startingitemindex == nil end, | |
{"inventorylocation", "listname", "x", "y", "w", "h"}, | |
{"string", "string", "number", "number", "number", "number"}, | |
"list[{inventorylocation};{listname};{x},{y};{w},{h}]"}, | |
{"list", function(t) return t.startingitemindex ~= nil end, | |
{"inventorylocation", "listname", "x", "y", "w", "h", "startingitemindex"}, | |
{"string", "string", "number", "number", "number", "number", "number"}, | |
"list[{inventorylocation};{listname};{x},{y};{w},{h};{startingitemindex}]"}, | |
{"image", true, {"x", "y", "w", "h", "texturename"}, | |
{"number", "number", "number", "number", "string"}, | |
"image[{x},{y};{w},{h};{texturename}]"}, | |
{"item_image", true, {"x", "y", "w", "h", "itemname"}, | |
{"number", "number", "number", "number", "string"}, | |
"item_image[{x},{y};{w},{h};{itemname}]"}, | |
{"background", true, {"x", "y", "w", "h", "texturename"}, | |
{"number", "number", "number", "number", "string"}, | |
"background[{x},{y};{w},{h};{texturename}]"}, | |
{"pwdfield", true, {"x", "y", "w", "h", "name", "label"}, | |
{"number", "number", "number", "number", "string", "string"}, | |
"pwdfield[{x},{y};{w},{h};{name};{label}]"}, | |
{"field", function(t) return t.x ~= nil end, | |
{"x", "y", "w", "h", "name", "label", "default"}, | |
{"number", "number", "number", "number", "string", "string", "string"}, | |
"field[{x},{y};{w},{h};{name};{label};{default}]"}, | |
{"field", function(t) return t.x == nil end, | |
{"name", "label", "default"}, | |
{"string", "string", "string"}, | |
"field[{name};{label};{default}]"}, | |
{"textarea", true, {"x", "y", "w", "h", "name", "label", "default"}, | |
{"number", "number", "number", "number", "string", "string", "string"}, | |
"textarea[{x},{y};{w},{h};{name};{label};{default}]"}, | |
{"label", true, {"x", "y", "label"}, {"number", "number", "string"}, | |
"label[{x},{y};{label}]"}, | |
{"vertlabel", true, {"x", "y", "label"}, {"number", "number", "string"}, | |
"vertlabel[{x},{y};{label}]"}, | |
{"button", true, {"x", "y", "w", "h", "name", "label"}, | |
{"number", "number", "number", "number", "string", "string"}, | |
"button[{x},{y};{w},{h};{name};{label}]"}, | |
{"image_button", function(t, i) | |
if t.noclip == nil and (t.drawborder ~= nil or t.pressed ~= nil) then | |
error("Element " .. i .. " does have 'drawborder' or 'pressed' field but not 'noclip'") | |
end | |
if t.drawborder == nil and t.pressed ~= nil then | |
error("Element " .. i .. " does have 'pressed' field but not 'drawborder'") | |
end | |
return t.noclip == nil and t.drawborder == nil and t.pressed == nil | |
end, | |
{"x", "y", "w", "h", "texturename", "name", "label"}, | |
{"number", "number", "number", "number", "string", "string", "string"}, | |
"image_button[{x},{y};{w},{h};{texturename};{name};{label}]"}, | |
{"image_button", function(t) | |
return t.noclip ~= nil and t.drawborder == nil and t.pressed == nil | |
end, | |
{"x", "y", "w", "h", "texturename", "name", "label", "noclip"}, | |
{"number", "number", "number", "number", "string", | |
"string", "string", "boolean"}, | |
"image_button[{x},{y};{w},{h};{texturename};{name};{label};{noclip};true]"}, | |
{"image_button", function(t) | |
return t.noclip ~= nil and t.drawborder ~= nil and t.pressed == nil | |
end, | |
{"x", "y", "w", "h", "texturename", "name", "label", "noclip", "drawborder"}, | |
{"number", "number", "number", "number", "string", | |
"string", "string", "boolean", "boolean"}, | |
"image_button[{x},{y};{w},{h};{texturename};{name};{label};{noclip};{drawborder}]"}, | |
{"image_button", function(t) | |
return t.noclip ~= nil and t.drawborder ~= nil and t.pressed ~= nil | |
end, | |
{"x", "y", "w", "h", "texturename", "name", "label", "noclip", "drawborder", "pressed"}, | |
{"number", "number", "number", "number", "string", | |
"string", "string", "boolean", "boolean", "string"}, | |
"image_button[{x},{y};{w},{h};{texturename};{name};{label};{noclip};{drawborder};{pressed}]"}, | |
{"item_image_button", true, | |
{"x", "y", "w", "h", "itemname", "name", "label"}, | |
{"number", "number", "number", "number", "string", "string", "string"}, | |
"item_image_button[{x},{y};{w},{h};{itemname};{name};{label}]"}, | |
{"button_exit", true, {"x", "y", "w", "h", "name", "label"}, | |
{"number", "number", "number", "number", "string", "string"}, | |
"button_exit[{x},{y};{w},{h};{name};{label}]"}, | |
{"image_button_exit", true, | |
{"x", "y", "w", "h", "texturename", "name", "label"}, | |
{"number", "number", "number", "number", "string", "string", "string"}, | |
"image_button_exit[{x},{y};{w},{h};{texturename};{name};{label}]"}, | |
{"textlist", function(t) return t.tranparent == nil end, | |
{"x", "y", "w", "h", "name", "list"}, | |
{"number", "number", "number", "number", "string", {"table", colors=true}}, | |
"textlist[{x},{y};{w},{h};{name};{list}]"}, | |
{"textlist", function(t) return t.tranparent ~= nil end, | |
{"x", "y", "w", "h", "name", "list", "transparent"}, | |
{"number", "number", "number", "number", "string", {"table", colors=true}, "boolean"}, | |
"textlist[{x},{y};{w},{h};{name};{list};{transparent}]"}, | |
{"tabheader", true, {"x", "y", "name", "captions", | |
"current_tab", "transparent", "drawborder"}, | |
{"number", "number", "string", "table", "number", | |
"boolean", "boolean"}, | |
"tabheader[{x},{y};{name};{captions};{current_tab};{transparent};{drawborder}]"}, | |
{"box", true, {"x", "y", "w", "h", "color"}, | |
{"number", "number", "number", "number", {"string", color=true}}, | |
"box[{x},{y};{w},{h};{color}]"}, | |
{"dropdown", true, {"x", "y", "w", "name", "items", "index"}, | |
{"number", "number", "number", "string", "table", "number"}, | |
"dropdown[{x},{y};{w};{name};{items};{index}]"}, | |
{"checkbox", function(t) return t.selected == nil end, | |
{"x", "y", "name", "label"}, | |
{"number", "number", "string", "string"}, | |
"checkbox[{x},{y};{name};{label}]"}, | |
{"checkbox", function(t) return t.selected ~= nil end, | |
{"x", "y", "name", "label", "selected"}, | |
{"number", "number", "string", "string", "boolean"}, | |
"checkbox[{x},{y};{name};{label};{selected}]"}, | |
} | |
function formspectable2formspecstring(tbl) | |
local outstring = "" | |
if type(tbl) ~= "table" then | |
error("Expected 'table' for first argument, got '" .. type(tbl) .. "'") | |
end | |
for i, el in ipairs(tbl) do | |
if type(el) ~= "table" then | |
error("Element " .. i .. " is not of type 'table', but '" .. type(el) .. "'") | |
end | |
local t = tbl[1] | |
if t == nil then | |
error("Element " .. i .. " has no type field") | |
end | |
local processed = false | |
for _, conv in conv_tbl do | |
if t == conv[1] then | |
if type(conv[2]) == "boolean" and conv[2] == true then | |
conv[2] = function(t) return true end | |
end | |
if conv[2](tbl, i) then | |
processed = true | |
assert_fields(tbl, conv[3], i) | |
for j, ftype in conv[4] do | |
if type(ftype) == "table" then | |
ftype = ftype[1] | |
end | |
assert_type(tbl, conv[3][j], ftype, i) | |
end | |
local out = conv[5] | |
for j, fname in conv[3] do | |
if type(tbl[fname]) == "string" then | |
if type(conv[4][j]) == "table" and conv[4][j].color then | |
_col = tbl[fname] | |
if #_col ~= 6 then | |
error("Argument " .. j .. " of Element " .. i .. " is " .. #_col .." characters long, expected was 6") | |
end | |
for l in 1, #_col do | |
_cc = string.byte(_col, l) | |
if (_cc >= 48 and _cc <= 57) or (_cc >= 65 and _cc <= 70) or (_cc >= 97 and _cc <= 102) then | |
--Character valid | |
else | |
error("Character " .. l .. " of Argument " .. j .. " of Element " .. i .. " is " .. string.char(_cc) .. ", expected was one of 0123456789ABCDEFabcdef") | |
end | |
end | |
end | |
out = out:gsub("{" .. fname .. "}", formspec_escape(tbl[fname])) | |
elseif type(tbl[fname]) == "number" then | |
out = out:gsub("{" .. fname .. "}", tbl[fname]) | |
elseif type(tbl[fname]) == "boolean" then | |
out = out:gsub("{" .. fname .. "}", boolstr(tbl[fname])) | |
elseif type(tbl[fname]) == "table" then | |
local out2 = "" | |
for k, tv in ipairs(tbl[fname]) do | |
if type(tv) == "string" then | |
if type(conv[4][j]) == "table" and conv[4][j].colors and tv:sub(1,1) == "#" then | |
tv = "#" .. tv | |
end | |
out2 = out2 .. formspec_escape(tv) | |
elseif type(tv) == "number" then | |
out2 = out2 .. tv | |
elseif type(tv) == "boolean" then | |
out2 = out2 .. boolstr(tv) | |
elseif type(tv) == "table" and type(conv[4][j]) == "table" and conv[4][j].colors then | |
_col = tv.color | |
_txt = tv.text | |
if _col == nil then | |
error("Element " .. i .. ", Argument " .. j .. " has no 'color' field") | |
elseif _txt == nil then | |
error("Element " .. i .. ", Argument " .. j .. " has no 'text' field") | |
end | |
if type(_col) ~= "string" then | |
error("The 'color' field of Element " .. i .. ", Argument " .. j .. " is of type '" .. type(_col) .. "', expected was 'string'") | |
elseif type(_txt) ~= "string" then | |
error("The 'text' field of Element " .. i .. ", Argument " .. j .. " is of type '" .. type(_col) .. "', expected was 'string'") | |
end | |
if #_col ~= 6 then | |
error("The 'color' field of Element " .. i .. ", Argument " .. j .. " is " .. #_col .." characters long, expected was 6") | |
end | |
for l in 1, #_col do | |
_cc = string.byte(_col, l) | |
if (_cc >= 48 and _cc <= 57) or (_cc >= 65 and _cc <= 70) or (_cc >= 97 and _cc <= 102) then | |
--Character valid | |
else | |
error("Character " .. l .. " of the 'color' field of Element " .. i .. ", Argument " .. j .. " is " .. string.char(_cc) .. ", expected was one of 0123456789ABCDEFabcdef") | |
end | |
end | |
out2 = out2 .. "#" .. _col .. formspec_escape(tv) | |
else | |
error("Cannot serialize " .. type(tv) .. " in table Element " .. i .. ", Argument " .. j) | |
end | |
if not k == #tbl[fname] then | |
out2 = out2 .. "," | |
end | |
end | |
out = out:gsub("{" .. fname .. "}", out2) | |
end | |
end | |
outstring = outstring .. out | |
end | |
end | |
end | |
if not processed then | |
error("Element " .. i .. " has unknown type '" .. t .. "'") | |
end | |
end | |
return outstring | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment