Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- +---------------------+-----------+---------------------+
- -- | | | |
- -- | | skel2JSON | |
- -- | v0.9 | | Lua 5.1 |
- -- +---------------------+-----------+---------------------+
- -- by Bomb Bloke
- -- Converts Esoteric Software's Spine binary format to JSON.
- -- Checks current folder and all subfolders for *.skel files,
- -- and generates *.json files alongside of them.
- -- Aimed at spine formats 3.5.51 & 3.6.53 specifically.
- -- May also need access to your *.atlas files (for image dimension info).
- -- Performs JSON overwrites without asking.
- -- Related resources (note that they don't correspond to older Spine formats 100%):
- -- http://esotericsoftware.com/spine-json-format
- -- http://esotericsoftware.com/spine-binary-format
- -- Lua runtime if you need it:
- -- https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/luaforwindows/LuaForWindows_v5.1.4-46.exe
- ---------------------------------------------
- ------------ Config ------------
- ---------------------------------------------
- local supportDragonBones = true -- Makes various changes to the output to allow it to
- -- be handled by DB's Spine Importer 3.1.0 plugin.
- ---------------------------------------------
- ------------ Imports ------------
- ---------------------------------------------
- require "lfs"
- require "bit"
- ---------------------------------------------
- ------------ Variables ------------
- ---------------------------------------------
- local null, atlas, input = function() end
- local transformModes = {"normal", "onlyTranslation", "noRotationOrReflection", "noScale", "noScaleOrReflection"}
- local blendModes = {"normal", "additive", "multiply", "screen"}
- local attachmentModes = {"region", "boundingbox", "mesh", "linkedmesh", "path", "point", "clipping"}
- local positionModes = {"fixed", "percent"}
- local spacingModes = {"length", "fixed", "percent"}
- local rotateModes = {"tangent", "chain", "chainScale"}
- local slotModes = {"attachment", "color", "twoColor"}
- local boneModes = {"rotate", "translate", "scale", "shear"}
- local pathModes = {"position", "spacing", "mix"}
- local hex = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"}
- local keyPriority = {"time", "hash", "spine", "type", "uvs", "triangles", "vertices", "hull", "edges", "width", "height",
- "fps", "images", "name", "parent", "bone", "length", "rotation", "x", "y", "scaleX", "scaleY", "shearX", "shearY",
- "transform", "color", "dark", "attachment", "blend", "skeleton", "bones", "slots", "ik", "transform", "path",
- "skins", "events", "animations"}
- for i = 1, #keyPriority do
- keyPriority[keyPriority[i]] = i
- keyPriority[i] = nil
- end
- ---------------------------------------------
- ------------ Misc Functions ------------
- ---------------------------------------------
- -- Returns a hex string representation of abstract bytes.
- local function toHex(str)
- local result = {}
- for i = 1, #str do
- local val = str:byte(i)
- result[#result + 1] = hex[bit.rshift(val, 4) + 1] .. hex[bit.band(val, 15) + 1]
- end
- return table.concat(result)
- end
- -- Returns a file's name and extension as separate vals.
- local function getExt(filename)
- local div = filename:find("%.[^%.]*$")
- if not div then return filename end
- return filename:sub(1, div - 1), filename:sub(div + 1):lower()
- end
- -- Does this table contain a table, true/false?
- local function containsTable(table)
- if type(table) ~= "table" then return false end
- for k, v in pairs(table) do if type(v) == "table" then return true end end
- end
- -- Returns "val", so long as it doesn't match "fil" (or certain other things).
- local function filter(val, fil)
- if fil then
- if val ~= fil then return val end
- else
- if (type(val) == "number" and val ~= 0) or ((type(val) == "string" or type("val") == "table") and #val > 0) then return val end
- end
- end
- -- Rounds stuff, defaults to 2 places.
- local function round(val, places)
- places = places or 2
- return math.floor(val * 10^places + 0.5) / 10^places
- end
- ---------------------------------------------
- ----------- Reader Functions ----------
- ---------------------------------------------
- local function readInt(positive)
- local value = 0
- for i = 0, 28, 7 do
- local b = input:read(1):byte()
- value = value + bit.lshift(bit.band(b, 0x7F), i)
- if b < 128 then break end
- end
- -- ***********This PROBABLY won't work as intended:
- -- ... but it doesn't seem to be called anywhere important
- -- Only relevant to events, so proof read ouput for those
- if not positive then
- value = bit.bxor(bit.rshift(value, 1), -bit.band(value, 1))
- end
- return value
- end
- local function readInt32()
- local val = input:read(4)
- return val:byte(1) * 256 ^ 3 + val:byte(2) * 256 ^ 2 + val:byte(3) * 256 + val:byte(4)
- end
- local function readShort()
- local val = input:read(2)
- return val:byte(1) * 256 + val:byte(2)
- end
- -- This "should" handle UTF-8, but I don't personally need it to do so.
- local function readString()
- local len = readInt(true)
- return len > 0 and input:read(len - 1) or nil
- end
- local function readFloat(places)
- local val = input:read(4)
- local s = val:byte() < 128 and 1 or -1
- local e = bit.lshift(bit.band(val:byte(1), 127), 1) + bit.rshift(bit.band(val:byte(2), 128), 7)
- local f = bit.band(val:byte(2), 127) * 256 ^ 2 + val:byte(3) * 256 + val:byte(4)
- if e == 255 then
- return (f > 0 and 0 or s) / 0
- else
- local dec = 0
- for i = 0, 22 do if bit.band(f, 2^i) == 2^i then dec = dec + 2^(i-23) end end
- if e == 0 then
- if f > 0 then
- return round(s * 2^(-126) * dec, places)
- else
- return s * 0
- end
- else
- return round(s * 2^(e-127) * (1 + dec), places)
- end
- end
- end
- local function readBoolean()
- return input:read(1):byte() ~= 0
- end
- local function readVertices(vertexCount)
- local vertices = {}
- if readBoolean() then
- for l = 1, vertexCount do
- vertices[#vertices + 1] = readInt(true)
- for m = 1, vertices[#vertices] do
- vertices[#vertices + 1] = readInt(true)
- vertices[#vertices + 1] = readFloat()
- vertices[#vertices + 1] = readFloat()
- vertices[#vertices + 1] = readFloat()
- end
- end
- else
- for l = 1, vertexCount * 2 do vertices[l] = readFloat() end
- end
- return vertices
- end
- local function readCurve()
- local cType = input:read(1):byte()
- if cType == 1 then
- return "stepped"
- elseif cType == 2 then
- return {readFloat(3), readFloat(3), readFloat(3), readFloat(3)}
- end
- end
- ---------------------------------------------
- ----------- Main Decoding Function ----------
- ---------------------------------------------
- local function decodeSkel(entry)
- local skel = {["skeleton"] = {}, ["bones"] = {}, ["slots"] = {}, ["ik"] = {}, ["transform"] = {}, ["path"] = {}, ["skins"] = {}, ["events"] = {}, ["animations"] = {}}
- input = io.open(entry, "rb")
- -- ["skeleton"]
- skel.skeleton.hash = filter(readString())
- skel.skeleton.spine = filter(readString())
- if not (skel.skeleton.spine:sub(1, 3) == "3.5" or skel.skeleton.spine:sub(1, 3) == "3.6") then print("I think I can handle skels in the v3.5/3.6 range, but "..entry.." is v"..skel.skeleton.spine..". I'll try, but expect me to crash.") end
- skel.skeleton.width = readFloat()
- skel.skeleton.height = readFloat()
- nonessential = readBoolean()
- if nonessential then
- skel.skeleton.fps = filter(readFloat())
- skel.skeleton.images = filter(readString())
- end
- -- ["bones"]
- for i = 1, readInt(true) do
- local bone = {}
- bone.name = readString()
- if i > 1 then bone.parent = skel.bones[readInt(true) + 1].name end
- bone.rotation = filter(readFloat())
- bone.x = filter(readFloat())
- bone.y = filter(readFloat())
- bone.scaleX = filter(readFloat(3), 1)
- bone.scaleY = filter(readFloat(3), 1)
- bone.shearX = filter(readFloat())
- bone.shearY = filter(readFloat())
- bone.length = filter(readFloat())
- bone.transform = filter(transformModes[readInt(true) + 1], "normal")
- if supportDragonBones and bone.transform then
- if bone.transform == "onlyTranslation" or bone.transform == "noScale" or bone.transform == "noScaleOrReflection" then bone.inheritScale = false end
- if bone.transform == "onlyTranslation" or bone.transform == "noRotationOrReflection" then bone.inheritRotation = false end
- end
- if nonessential then bone.color = toHex(input:read(4)) end
- skel.bones[i] = bone
- end
- -- ["slots"]
- for i = 1, readInt(true) do
- local slot = {}
- slot.name = readString()
- slot.bone = skel.bones[readInt(true) + 1].name
- slot.color = filter(toHex(input:read(4)), "ffffffff")
- if skel.skeleton.spine >= "3.6.00" then slot.dark = filter(toHex(input:read(4)), "ffffffff") end
- if slot.dark then slot.dark = slot.dark:sub(3) end
- slot.attachment = filter(readString())
- slot.blend = filter(blendModes[readInt(true) + 1], "normal")
- skel.slots[i] = slot
- end
- if #skel.slots == 0 then skel.slots = nil end
- -- ["ik"]
- for i = 1, readInt(true) do
- local ik = {}
- ik.name = readString()
- ik.data = readInt(true)
- ik.bones = {}
- for j = 1, readInt(true) do ik.bones[j] = skel.bones[readInt(true) + 1].name end
- if #ik.bones == 0 then ik.bones = nil end
- ik.target = skel.bones[readInt(true) + 1].name
- ik.mix = readFloat()
- ik.bendPositive = input:read(1):byte() == 1
- skel.ik[i] = ik
- end
- if #skel.ik == 0 then skel.ik = nil end
- -- ["transform"]
- for i = 1, readInt(true) do
- local trans = {}
- trans.name = readString()
- trans.order = readInt(true)
- trans.bones = {}
- for j = 1, readInt(true) do trans.bones[#trans.bones + 1] = skel.bones[readInt(true) + 1].name end
- if #trans.bones == 0 then trans.bones = nil end
- trans.target = skel.bones[readInt(true) + 1].name
- if skel.skeleton.spine >= "3.6.00" then
- trans["local"] = readBoolean() or nil
- trans.relative = readBoolean() or nil
- end
- trans.rotation = filter(readFloat())
- trans.x = filter(readFloat())
- trans.y = filter(readFloat())
- trans.scaleX = filter(readFloat())
- trans.scaleY = filter(readFloat())
- trans.shearY = filter(readFloat())
- trans.rotateMix = filter(readFloat(), 1)
- trans.translateMix = filter(readFloat(), 1)
- trans.scaleMix = filter(readFloat(), 1)
- trans.shearMix = filter(readFloat(), 1)
- skel.transform[i] = trans
- end
- if #skel.transform == 0 then skel.transform = nil end
- -- ["path"]
- for i = 1, readInt(true) do
- local path = {}
- path.name = readString()
- path.order = readInt(true)
- path.bones = {}
- for j = 1, readInt(true) do path.bones[#path.bones + 1] = skel.bones[readInt(true) + 1].name end
- if #path.bones == 0 then path.bones = nil end
- path.target = skel.slots[readInt(true) + 1].name
- path.positionMode = positionModes[readInt(true) + 1]
- path.spacingMode = spacingModes[readInt(true) + 1]
- path.rotateMode = rotateModes[readInt(true) + 1]
- path.rotation = filter(readFloat())
- path.position = filter(readFloat())
- path.spacing = filter(readFloat())
- path.rotateMix = filter(readFloat(), 1)
- path.translateMix = filter(readFloat(), 1)
- skel.path[i] = path
- end
- if #skel.path == 0 then skel.path = nil end
- -- ["skins"]
- local skinNums, i, skinCount = {}, 0
- repeat
- local skin = {}
- skinNums[i + 1] = i > 0 and readString() or "default"
- skel.skins[skinNums[i + 1]] = skin
- for j = 1, readInt(true) do
- local slot = {}
- skin[skel.slots[readInt(true) + 1].name] = slot
- for k = 1, readInt(true) do
- local attachment = {}
- local attachmentName = readString()
- attachmentName = readString() or attachmentName
- local type = attachmentModes[input:read(1):byte() + 1]
- attachment.type = filter(type, "region")
- if type == "region" then
- attachment.path = filter(readString())
- attachment.rotation = filter(readFloat())
- attachment.x = filter(readFloat())
- attachment.y = filter(readFloat())
- attachment.scaleX = filter(readFloat(), 1)
- attachment.scaleY = filter(readFloat(), 1)
- attachment.width = readFloat()
- attachment.height = readFloat()
- attachment.color = filter(toHex(input:read(4)), "ffffffff")
- if supportDragonBones then for l = 1, #atlas do if atlas[l][attachmentName] then
- if attachment.width ~= atlas[l][attachmentName][1] and not attachment.scaleX then attachment.scaleX, attachment.width = attachment.width / atlas[l][attachmentName][1], atlas[l][attachmentName][1] end
- if attachment.height ~= atlas[l][attachmentName][2] and not attachment.scaleY then attachment.scaleY, attachment.height = attachment.height / atlas[l][attachmentName][2], atlas[l][attachmentName][2] end
- break
- end end end
- elseif type == "boundingbox" then
- attachment.vertexCount = readInt(true)
- attachment.vertices = readVertices(attachment.vertexCount)
- if nonessential then attachment.color = toHex(input:read(4)) end
- elseif type == "mesh" then
- attachment.path = filter(readString())
- attachment.color = filter(toHex(input:read(4)), "ffffffff")
- local vertexCount = readInt(true)
- attachment.uvs = {}
- for l = 1, vertexCount * 2 do attachment.uvs[l] = readFloat(5) end
- attachment.triangles = {}
- for l = 1, readInt(true) do attachment.triangles[l] = readShort() end
- attachment.vertices = readVertices(vertexCount)
- attachment.hull = readInt(true)
- attachment.edges = {}
- if nonessential then
- for l = 1, readInt(true) do attachment.edges[l] = readShort() end
- attachment.width = readFloat()
- attachment.height = readFloat()
- else
- for l = 0, attachment.hull - 1 do
- attachment.edges[#attachment.edges + 1] = l * 2
- attachment.edges[#attachment.edges + 1] = (l + 1) * 2
- end
- attachment.edges[#attachment.edges] = 0
- for l = 1, #atlas do if atlas[l][attachment.path or attachmentName] then
- attachment.width = atlas[l][attachment.path or attachmentName][1]
- attachment.height = atlas[l][attachment.path or attachmentName][2]
- break
- end end
- if supportDragonBones and not attachment.width then
- print("Warning: "..entry.." is missing image/width data for element "..attachmentName..", and I can't find the corresponding texture atlas data to supply it."..(#atlas==0 and " Pop a copy of the atlas into the skel's folder and try running me again." or ""))
- if #atlas == 0 then
- input:close()
- return
- end
- end
- end
- elseif type == "linkedmesh" then
- attachment.path = filter(readString())
- attachment.color = filter(toHex(input:read(4)), "ffffffff")
- attachment.skin = filter(readString())
- attachment.parent = filter(readString())
- attachment.deform = filter(readBoolean(), true)
- if nonessential then
- attachment.width = readFloat()
- attachment.height = readFloat()
- else
- for l = 1, #atlas do if atlas[l][attachment.path or attachmentName] then
- attachment.width = atlas[l][attachment.path or attachmentName][1]
- attachment.height = atlas[l][attachment.path or attachmentName][2]
- break
- end end
- if supportDragonBones and not attachment.width then
- print("Warning: "..entry.." is missing image/width data for element "..attachmentName..", and I can't find the corresponding texture atlas data to supply it."..(#atlas==0 and " Pop a copy of the atlas into the skel's folder and try running me again." or ""))
- if #atlas == 0 then
- input:close()
- return
- end
- end
- end
- elseif type == "path" then
- attachment.closed = readBoolean() or nil
- attachment.constantSpeed = readBoolean() or nil
- attachment.vertexCount = readInt(true)
- attachment.vertices = readVertices(attachment.vertexCount)
- attachment.lengths = {}
- for l = 1, attachment.vertexCount / 3 do attachment.lengths[l] = readFloat() end
- if nonessential then attachment.color = toHex(input:read(4)) end
- elseif type == "point" then
- attachment.rotation = filter(readFloat())
- attachment.x = filter(readFloat())
- attachment.y = filter(readFloat())
- if nonessential then attachment.color = toHex(input:read(4)) end
- elseif type == "clipping" then
- attachment["end"] = skel.slots[readInt(true) + 1].name
- attachment.vertexCount = readInt(true)
- attachment.vertices = readVertices(attachment.vertexCount)
- if nonessential then attachment.color = toHex(input:read(4)) end
- end
- slot[attachmentName] = attachment
- end
- end
- if i == 0 then skinCount = readInt(true) end
- i = i + 1
- until i > skinCount
- -- ["events"]
- local eventNums = {}
- for i = 1, readInt(true) do
- local event = {}
- eventNums[i] = readString()
- skel.events[eventNums[i]] = event
- event.int = filter(readInt(false)) -- ********* Suspect readInt(false) is unreliable
- event.float = filter(readFloat())
- event.string = filter(readString())
- end
- if not containsTable(skel.events) then skel.events = nil end
- -- ["animations"]
- for i = 1, readInt(true) do
- local animation = {}
- local aniName = readString()
- -- ["slots"]
- animation.slots = {}
- for j = 1, readInt(true) do
- local slot = {}
- local name = skel.slots[readInt(true) + 1].name
- animation.slots[name] = slot
- for k = 1, readInt(true) do
- local mode = {}
- local type = slotModes[input:read(1):byte() + 1]
- slot[type] = mode
- local frameCount = readInt(true)
- for l = 1, frameCount do
- if type == "attachment" then
- mode[l] = {["time"] = readFloat(4), ["name"] = readString() or null}
- elseif type == "color" then
- mode[l] = {["time"] = readFloat(4), ["color"] = toHex(input:read(4))}
- if l < frameCount then mode[l].curve = readCurve() end
- elseif type == "twoColor" then
- mode[l] = {["time"] = readFloat(4), ["light"] = toHex(input:read(4)), ["dark"] = toHex(input:read(4)):sub(3)}
- if l < frameCount then mode[l].curve = readCurve() end
- end
- end
- end
- if supportDragonBones then slot["twoColor"] = nil end
- if not containsTable(animation.slots[name]) then animation.slots[name] = nil end
- end
- if not containsTable(animation.slots) then animation.slots = nil end
- -- ["bones"]
- animation.bones = {}
- for j = 1, readInt(true) do
- local bone = {}
- animation.bones[skel.bones[readInt(true) + 1].name] = bone
- for k = 1, readInt(true) do
- local mode = {}
- local type = boneModes[input:read(1):byte() + 1]
- bone[type] = mode
- local frameCount = readInt(true)
- for l = 1, frameCount do
- if type == "rotate" then
- mode[l] = {["time"] = readFloat(4), ["angle"] = readFloat()}
- else
- mode[l] = {["time"] = readFloat(4), ["x"] = readFloat(type == "translate" and 2 or 3), ["y"] = readFloat(type == "translate" and 2 or 3)}
- end
- if l < frameCount then mode[l].curve = readCurve() end
- end
- end
- end
- if not containsTable(animation.bones) then animation.bones = nil end
- -- ["ik"]
- animation.ik = {}
- for j = 1, readInt(true) do
- local ik = {}
- animation.ik[skel.ik[readInt(true) + 1].name] = ik
- local frameCount = readInt(true)
- for k = 1, frameCount do
- ik[k] = {}
- ik[k].time = readFloat(4)
- ik[k].mix = readFloat(3)
- ik[k].bendPositive = input:read(1):byte() == 1
- if k < frameCount then ik[k].curve = readCurve() end
- end
- end
- if not containsTable(animation.ik) then animation.ik = nil end
- -- ["transform"]
- animation.transform = {}
- for j = 1, readInt(true) do
- local transform = {}
- animation.transform[skel.transform[readInt(true) + 1].name] = transform
- local frameCount = readInt(true)
- for k = 1, frameCount do
- transform[k] = {}
- transform[k].time = readFloat(4)
- transform[k].rotateMix = filter(readFloat(3), 1)
- transform[k].translateMix = filter(readFloat(3), 1)
- transform[k].scaleMix = filter(readFloat(3), 1)
- transform[k].shearMix = filter(readFloat(3), 1)
- if k < frameCount then transform[k].curve = readCurve() end
- end
- end
- if not containsTable(animation.transform) then animation.transform = nil end
- -- ["path"]
- animation.path = {}
- for j = 1, readInt(true) do
- local path = {}
- animation.path[skel.path[readInt(true) + 1].name] = path
- for k = 1, readInt(true) do
- local constraint = {}
- local type = pathModes[input:read(1):byte() + 1]
- path[type] = constraint
- local frameCount = readInt(true)
- for l = 1, frameCount do
- constraint[l] = {}
- constraint[l].time = readFloat(4)
- if type == "position" then
- constraint[l].position = readFloat(3)
- elseif type == "spacing" then
- constraint[l].spacing = readFloat(3)
- elseif type == "mix" then
- constraint[l].rotateMix = readFloat(3)
- constraint[l].translateMix = readFloat(3)
- end
- if l < frameCount then constraint[l].curve = readCurve() end
- end
- end
- end
- if not containsTable(animation.path) then animation.path = nil end
- -- ["deform"]
- animation.deform = {}
- for j = 1, readInt(true) do
- local skin = {}
- animation.deform[skinNums[readInt(true) + 1]] = skin
- for k = 1, readInt(true) do
- local slot = {}
- skin[skel.slots[readInt(true) + 1].name] = slot
- for l = 1, readInt(true) do
- local attachment = {}
- slot[readString()] = attachment
- local frameCount = readInt(true)
- for m = 1, frameCount do
- attachment[m] = {}
- attachment[m].time = readFloat(4)
- local daEnd = readInt(true)
- if daEnd ~= 0 then
- local count, start = 1, readInt(true)
- daEnd = daEnd + start - 1
- attachment[m].vertices = {}
- for n = start, daEnd do
- attachment[m].vertices[count] = readFloat(5)
- count = count + 1
- end
- end
- if m < frameCount then attachment[m].curve = readCurve() end
- end
- end
- end
- end
- if not containsTable(animation.deform) then animation.deform = nil end
- -- ["drawOrder"]
- animation.drawOrder = {}
- for j = 1, readInt(true) do
- local order = {["time"] = readFloat(4), ["offsets"] = {}}
- animation.drawOrder[j] = order
- for k = 1, readInt(true) do
- local offset = {}
- order.offsets[k] = offset
- offset.slot = skel.slots[readInt(true) + 1].name
- offset.offset = readInt(true)
- end
- end
- if #animation.drawOrder == 0 then animation.drawOrder = nil end
- -- ["events"]
- animation.events = {}
- for j = 1, readInt(true) do
- local event = {}
- animation.events[j] = event
- event.time = readFloat(4)
- event.name = eventNums[readInt(true) + 1]
- event.int = readInt(false) -- ********* Suspect readInt(false) is unreliable
- event.float = readFloat()
- event.string = readBoolean() and readString() or skel.events[event.name].string
- end
- if #animation.events == 0 then animation.events = nil end
- if containsTable(animation) then skel.animations[aniName] = animation end
- end
- if not containsTable(skel.animations) then skel.animations = nil end
- input:close()
- if supportDragonBones and skel.skins then
- for skin, skinTab in pairs(skel.skins) do
- for slot, slotTab in pairs(skinTab) do
- for attachment, attTab in pairs(slotTab) do
- if attTab.type == "linkedmesh" then
- if skel.animations then
- for animation, aniTab in pairs(skel.animations) do
- if aniTab.deform and aniTab.deform[skin] and aniTab.deform[skin][slot] and aniTab.deform[skin][slot][attTab.parent] and not aniTab.deform[skin][slot][attachment] then
- aniTab.deform[skin][slot][attachment] = aniTab.deform[skin][slot][attTab.parent]
- end
- end
- end
- slotTab[attachment] = skel.skins[attTab.skin or "default"][slot][attTab.parent]
- end
- end
- end
- end
- end
- -- Output JSON
- local output = io.open(getExt(entry) .. ".json", "w")
- local function writeJSON(value, indent)
- local tabs = string.rep("\9", indent)
- local cT = containsTable(value)
- local hashMap = #value == 0
- local sorted = {}
- if hashMap then
- for k in pairs(value) do sorted[#sorted + 1] = k end
- table.sort(sorted, function(a, b)
- if not (keyPriority[a] or keyPriority[b]) then return a < b end
- if keyPriority[a] and keyPriority[b] then return keyPriority[a] < keyPriority[b] end
- return keyPriority[a] and true or false
- end)
- else sorted = value end
- output:write((hashMap and "{" or "[") .. (cT and ("\n" .. tabs) or " "))
- for i = 1, #sorted do
- local thisVal
- if hashMap then
- local thisKey = sorted[i]
- thisVal = value[thisKey]
- output:write("\"" .. thisKey .. "\": ")
- else thisVal = sorted[i] end
- if type(thisVal) == "number" then
- output:write(thisVal)
- elseif type(thisVal) == "string" then
- output:write("\"" .. thisVal .. "\"")
- elseif type(thisVal) == "boolean" then
- output:write(tostring(thisVal))
- elseif type(thisVal) == "function" then
- output:write("null")
- elseif type(thisVal) == "table" then
- writeJSON(thisVal, indent + 1)
- end
- if i ~= #sorted then output:write(cT and (",\n" .. tabs) or ", ") end
- end
- output:write((cT and ("\n" .. string.rep("\9", indent - 1)) or " ") .. (hashMap and "}" or "]"))
- end
- writeJSON(skel, 0)
- output:close()
- end
- ---------------------------------------------
- ------------ Main Work Loop ------------
- ---------------------------------------------
- local function checkDir(dir)
- atlas = {}
- local skels, dirs = {}, {}
- for entry in lfs.dir(dir) do
- local path = dir .. "/" .. entry
- local attribs = lfs.attributes(path)
- if attribs then
- if entry ~= "." and entry ~= ".." and attribs.mode == "directory" then
- dirs[#dirs + 1] = path
- elseif attribs.mode == "file" then
- if (entry:sub(-6):lower() == ".atlas" or entry:sub(-10):lower() == ".atlas.txt") then
- atlas[#atlas + 1] = {}
- local atlas, curName = atlas[#atlas]
- for line in io.lines(path) do
- if line:sub(1, 1) ~= " " then curName = line end
- if line:find("orig:") then atlas[curName] = {tonumber(line:sub(9, line:find(",") - 1)), tonumber(line:sub(line:find(", ") + 2))} end
- end
- elseif entry:sub(-5):lower() == ".skel" then
- skels[#skels + 1] = path
- end
- end
- end
- end
- for i = 1, #skels do decodeSkel(skels[i]) end
- for i = 1, #dirs do checkDir(dirs[i]) end
- end
- return checkDir(".")
Add Comment
Please, Sign In to add comment