mirror of
https://github.com/Insality/druid.git
synced 2025-09-27 18:12:19 +02:00
Update editor scripts
This commit is contained in:
179
druid/editor_scripts/defold_parser/system/config.lua
Normal file
179
druid/editor_scripts/defold_parser/system/config.lua
Normal file
@@ -0,0 +1,179 @@
|
||||
local M = {}
|
||||
|
||||
-- Define a set of keys that should not have quotes
|
||||
M.string_keys = {
|
||||
text = true,
|
||||
id = true,
|
||||
value = true,
|
||||
rename_patterns = true,
|
||||
}
|
||||
|
||||
|
||||
M.ALWAYS_LIST = {
|
||||
attributes = true,
|
||||
nodes = true,
|
||||
images = true,
|
||||
children = true,
|
||||
fonts = true,
|
||||
layers = true,
|
||||
textures = true,
|
||||
embedded_components = true,
|
||||
embedded_instances = true,
|
||||
collection_instances = true,
|
||||
instances = true,
|
||||
}
|
||||
|
||||
|
||||
M.with_dot_params = {
|
||||
"x",
|
||||
"y",
|
||||
"z",
|
||||
"w",
|
||||
"alpha",
|
||||
"outline_alpha",
|
||||
"shadow_alpha",
|
||||
"text_leading",
|
||||
"text_tracking",
|
||||
"pieFillAngle",
|
||||
"innerRadius",
|
||||
"leading",
|
||||
"tracking",
|
||||
"data",
|
||||
"t_x",
|
||||
"t_y",
|
||||
"spread",
|
||||
"start_delay",
|
||||
"inherit_velocity",
|
||||
"start_delay_spread",
|
||||
"duration_spread",
|
||||
"start_offset",
|
||||
"outline_width",
|
||||
"shadow_x",
|
||||
"shadow_y",
|
||||
"aspect_ratio",
|
||||
"far_z",
|
||||
"mass",
|
||||
"linear_damping",
|
||||
"angular_damping",
|
||||
"gain",
|
||||
"pan",
|
||||
"speed",
|
||||
"duration"
|
||||
}
|
||||
|
||||
M.KEY_ORDER = {
|
||||
["font"] = {
|
||||
"extrude_borders",
|
||||
"images",
|
||||
"inner_padding",
|
||||
"margin",
|
||||
"font",
|
||||
"material",
|
||||
"size",
|
||||
"antialias",
|
||||
"alpha",
|
||||
"outline_alpha",
|
||||
"outline_width",
|
||||
"shadow_alpha",
|
||||
"shadow_blur",
|
||||
"shadow_x",
|
||||
"shadow_y",
|
||||
"extra_characters",
|
||||
"output_format",
|
||||
"all_chars",
|
||||
"cache_width",
|
||||
"cache_height",
|
||||
"render_mode",
|
||||
},
|
||||
["atlas"] = {
|
||||
"id",
|
||||
"images",
|
||||
"playback",
|
||||
"fps",
|
||||
"flip_horizontal",
|
||||
"flip_vertical",
|
||||
"image",
|
||||
"sprite_trim_mode",
|
||||
"images",
|
||||
"animations",
|
||||
"margin",
|
||||
"extrude_borders",
|
||||
"inner_padding",
|
||||
"max_page_width",
|
||||
"max_page_height",
|
||||
"rename_patterns",
|
||||
},
|
||||
["gui"] = {
|
||||
"position",
|
||||
"rotation",
|
||||
"scale",
|
||||
"size",
|
||||
"color",
|
||||
"type",
|
||||
"blend_mode",
|
||||
"text",
|
||||
"texture",
|
||||
"font",
|
||||
"id",
|
||||
"xanchor",
|
||||
"yanchor",
|
||||
"pivot",
|
||||
"outline",
|
||||
"shadow",
|
||||
"adjust_mode",
|
||||
"line_break",
|
||||
"parent",
|
||||
"layer",
|
||||
"inherit_alpha",
|
||||
"slice9",
|
||||
"outerBounds",
|
||||
"innerRadius",
|
||||
"perimeterVertices",
|
||||
"pieFillAngle",
|
||||
"clipping_mode",
|
||||
"clipping_visible",
|
||||
"clipping_inverted",
|
||||
"alpha",
|
||||
"outline_alpha",
|
||||
"shadow_alpha",
|
||||
"overridden_fields",
|
||||
"template",
|
||||
"template_node_child",
|
||||
"text_leading",
|
||||
"text_tracking",
|
||||
"size_mode",
|
||||
"spine_scene",
|
||||
"spine_default_animation",
|
||||
"spine_skin",
|
||||
"spine_node_child",
|
||||
"particlefx",
|
||||
"custom_type",
|
||||
"enabled",
|
||||
"visible",
|
||||
|
||||
-- Scene
|
||||
"scripts",
|
||||
"fonts",
|
||||
"textures",
|
||||
"background_color",
|
||||
"nodes",
|
||||
"layers",
|
||||
"material",
|
||||
"layouts",
|
||||
"adjust_reference",
|
||||
"max_nodes",
|
||||
"spine_scenes",
|
||||
"particlefxs",
|
||||
"resources",
|
||||
"materials",
|
||||
"max_dynamic_textures",
|
||||
|
||||
-- Vectors
|
||||
"x",
|
||||
"y",
|
||||
"z",
|
||||
"w",
|
||||
},
|
||||
}
|
||||
|
||||
return M
|
139
druid/editor_scripts/defold_parser/system/parser.lua
Normal file
139
druid/editor_scripts/defold_parser/system/parser.lua
Normal file
@@ -0,0 +1,139 @@
|
||||
local config = require("druid.editor_scripts.defold_parser.system.config")
|
||||
|
||||
local M = {}
|
||||
|
||||
-- Example: "name: value"
|
||||
M.REGEX_KEY_COLUM_VALUE = "^%s*([%w_]+):%s*(.+)$"
|
||||
-- Example: "name {"
|
||||
M.REGEX_START_TABLE = "^%s*([%w_]*)%s*{%s*$"
|
||||
-- Example: "}"
|
||||
M.REGEX_END_TABLE = "^%s*}%s*$"
|
||||
|
||||
|
||||
---@param value string
|
||||
---@return string
|
||||
function M.unescape_text_field(value)
|
||||
-- Splitting the value by new lines and processing each line
|
||||
local lines = {}
|
||||
for line in value:gmatch("[^\r\n]+") do
|
||||
line = line:gsub('\\"', '"') -- Unescaping quotes
|
||||
line = line:gsub("\\n", "") -- Removing newline escapes
|
||||
line = line:gsub("\\", "") -- Unescaping backslashes
|
||||
table.insert(lines, line)
|
||||
end
|
||||
|
||||
-- Reconstructing the value
|
||||
value = table.concat(lines, "\n")
|
||||
return value
|
||||
end
|
||||
|
||||
|
||||
function M.is_multiline_value(value)
|
||||
return value:find("\\n\"") ~= nil
|
||||
end
|
||||
|
||||
|
||||
---@param value any
|
||||
---@param property_name string|nil
|
||||
---@return any
|
||||
function M.decode_value(value, property_name)
|
||||
if value:match('^".*"$') then
|
||||
-- Removing the quotes from the string
|
||||
value = value:sub(2, -2)
|
||||
|
||||
-- Check if value is escaped
|
||||
-- If ends with \n
|
||||
if value:sub(-2) == "\\n" then
|
||||
value = value:gsub('\\"', '"') -- Unescaping quotes
|
||||
value = value:gsub("\\n", "")
|
||||
value = value:gsub("\\", "")
|
||||
end
|
||||
|
||||
elseif value:match('^%-?[0-9.E%-]+$') then
|
||||
-- Converting to number
|
||||
value = tonumber(value)
|
||||
end
|
||||
|
||||
-- Specific handling for the "text" property
|
||||
if property_name == "text" then
|
||||
value = tostring(value)
|
||||
else
|
||||
if value == "true" then
|
||||
value = true
|
||||
elseif value == "false" then
|
||||
value = false
|
||||
end
|
||||
end
|
||||
|
||||
if property_name == "text" and M.is_multiline_value(value) and type(value) == "string" then
|
||||
value = M.unescape_text_field(value)
|
||||
end
|
||||
|
||||
return value
|
||||
end
|
||||
|
||||
|
||||
---@param parent_object table
|
||||
---@param name string
|
||||
---@param stack table
|
||||
function M.new_inner_struct(parent_object, name, stack)
|
||||
local new_object = {}
|
||||
M.apply_value(parent_object, name, new_object)
|
||||
|
||||
local is_object_always_list = config.ALWAYS_LIST[name]
|
||||
if is_object_always_list and not M.is_array(parent_object[name]) then
|
||||
parent_object[name] = { parent_object[name] }
|
||||
end
|
||||
|
||||
table.insert(stack, new_object)
|
||||
end
|
||||
|
||||
|
||||
---Apply value to the object, if the value is already present, convert it to an array
|
||||
---@param object table
|
||||
---@param name string
|
||||
---@param value any
|
||||
---@return table object
|
||||
function M.apply_value(object, name, value)
|
||||
local is_object_always_list = config.ALWAYS_LIST[name]
|
||||
if object[name] == nil then
|
||||
object[name] = value
|
||||
if is_object_always_list then
|
||||
object[name] = { object[name] }
|
||||
end
|
||||
return object
|
||||
end
|
||||
|
||||
-- Convert to array if not already
|
||||
if not M.is_array(object[name]) then
|
||||
object[name] = { object[name] }
|
||||
end
|
||||
|
||||
table.insert(object[name], value)
|
||||
return object
|
||||
end
|
||||
|
||||
|
||||
---@param object table
|
||||
---@param value string
|
||||
---@return table @object
|
||||
function M.apply_multiline_value(object, name, value)
|
||||
if object[name] == nil then
|
||||
object[name] = value
|
||||
else
|
||||
object[name] = object[name] .. "\n" .. value
|
||||
end
|
||||
|
||||
return object
|
||||
end
|
||||
|
||||
|
||||
--- Check if table is array
|
||||
---@param t table
|
||||
---@return boolean
|
||||
function M.is_array(t)
|
||||
return type(t) == "table" and t[1] ~= nil
|
||||
end
|
||||
|
||||
|
||||
return M
|
163
druid/editor_scripts/defold_parser/system/system.lua
Normal file
163
druid/editor_scripts/defold_parser/system/system.lua
Normal file
@@ -0,0 +1,163 @@
|
||||
local parser = require("druid.editor_scripts.defold_parser.system.parser")
|
||||
|
||||
local M = {}
|
||||
|
||||
|
||||
--- Check if table-array contains element
|
||||
---@param table table
|
||||
---@param element any
|
||||
---@return number|boolean index of element or false
|
||||
function M.contains(table, element)
|
||||
for index, value in pairs(table) do
|
||||
if value == element then
|
||||
return index
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
---@param file_path string
|
||||
---@return string|nil, string|nil @success, reason
|
||||
function M.read_file(file_path)
|
||||
local file = io.open(file_path, "r")
|
||||
if file == nil then
|
||||
return nil, "Could not open file: " .. file_path
|
||||
end
|
||||
|
||||
local content = file:read("*a")
|
||||
file:close()
|
||||
|
||||
return content, nil
|
||||
end
|
||||
|
||||
|
||||
---@param file_path string
|
||||
---@param content string
|
||||
---@return boolean, string|nil @success, reason
|
||||
function M.write_file(file_path, content)
|
||||
local file = io.open(file_path, "w")
|
||||
if file == nil then
|
||||
return false, "Could not open file: " .. file_path
|
||||
end
|
||||
|
||||
file:write(content)
|
||||
file:close()
|
||||
|
||||
return true, nil
|
||||
end
|
||||
|
||||
|
||||
---@param line string
|
||||
function M.unescape_line(line)
|
||||
-- Trim whitespaces
|
||||
line = line:match("^%s*(.-)%s*$")
|
||||
|
||||
-- Remove first and last quote symbols only if exists
|
||||
if line:sub(1, 1) == '"' and line:sub(-1) == '"' then
|
||||
line = line:sub(2, -2)
|
||||
end
|
||||
|
||||
-- Trim whitespaces
|
||||
line = line:match("^%s*(.-)%s*$")
|
||||
|
||||
-- Splitting the value by new lines and processing each line
|
||||
line = line:gsub('\\"', '"') -- Unescaping quotes
|
||||
line = line:gsub("\\n", "") -- Removing newline escapes
|
||||
line = line:gsub("\\", "") -- Unescaping backslashes
|
||||
|
||||
return line
|
||||
end
|
||||
|
||||
|
||||
---@param line string
|
||||
---@return string, string, string, boolean @new_object_name, name, value, end_struct_flag
|
||||
function M.split_line(line)
|
||||
local new_object_name = line:match(parser.REGEX_START_TABLE)
|
||||
local name, value = line:match(parser.REGEX_KEY_COLUM_VALUE)
|
||||
local end_struct_flag = line:match(parser.REGEX_END_TABLE)
|
||||
|
||||
-- We hit a line what is contains only value, like multiline strings
|
||||
if not name and not value then
|
||||
value = line
|
||||
end
|
||||
|
||||
return new_object_name, name, value, end_struct_flag
|
||||
end
|
||||
|
||||
|
||||
-- what a crap...
|
||||
local LAST_USED_NAME = nil
|
||||
|
||||
---@param unescaped_line string @line to parse
|
||||
---@param stack table @stack of objects
|
||||
---@return boolean
|
||||
function M.parse_line(unescaped_line, stack)
|
||||
unescaped_line = unescaped_line:match("^%s*(.-)%s*$")
|
||||
|
||||
-- Use last object to insert data
|
||||
local object = stack[#stack]
|
||||
local line = M.unescape_line(unescaped_line)
|
||||
local inner_object_name, name, value, end_struct_flag = M.split_line(line)
|
||||
|
||||
local is_just_new_line = (unescaped_line == "\"\\n\"")
|
||||
if not end_struct_flag and (line == "\"" or line == "") and (not is_just_new_line) then
|
||||
if LAST_USED_NAME ~= "text" then
|
||||
end_struct_flag = true
|
||||
end
|
||||
end
|
||||
|
||||
if inner_object_name then
|
||||
parser.new_inner_struct(object, inner_object_name, stack)
|
||||
object = stack[#stack]
|
||||
end
|
||||
|
||||
if name and value ~= nil then
|
||||
-- If value is nested object...
|
||||
if value:sub(1, 1) == '"' then
|
||||
value = value:sub(2, -1)
|
||||
end
|
||||
if value:sub(-1) == '"' then
|
||||
value = value:sub(1, -2)
|
||||
end
|
||||
|
||||
local unescape_line = M.unescape_line(value)
|
||||
local new_object_name, field_name, _, end_flag = M.split_line(unescape_line)
|
||||
|
||||
if (new_object_name or field_name or end_flag) and name ~= "text" then
|
||||
parser.new_inner_struct(object, name, stack)
|
||||
object = stack[#stack]
|
||||
|
||||
M.parse_line(value, stack)
|
||||
else
|
||||
-- Just a hack honestly
|
||||
-- If first character is a quote, then remove it
|
||||
if value:sub(1, 1) == '"' then
|
||||
value = value:sub(2, -1)
|
||||
end
|
||||
if value:sub(-1) == '"' then
|
||||
value = value:sub(1, -2)
|
||||
end
|
||||
|
||||
value = parser.decode_value(value, name)
|
||||
LAST_USED_NAME = name
|
||||
parser.apply_value(object, name, value)
|
||||
end
|
||||
end
|
||||
|
||||
if not name and value and not inner_object_name and not end_struct_flag then
|
||||
-- We should to add value to the last as a multiline data
|
||||
parser.apply_multiline_value(object, LAST_USED_NAME, value)
|
||||
end
|
||||
|
||||
if end_struct_flag then
|
||||
-- Go back to the parent object
|
||||
table.remove(stack)
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
return M
|
Reference in New Issue
Block a user