BombBloke

skel2JSON.lua

Nov 6th, 2020 (edited)
270
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 26.63 KB | None | 0 0
  1. -- +---------------------+-----------+---------------------+
  2. -- |                     |           |                     |
  3. -- |                     | skel2JSON |                     |
  4. -- | v0.9                |           |             Lua 5.1 |
  5. -- +---------------------+-----------+---------------------+
  6.  
  7. -- by Bomb Bloke
  8.  
  9. -- Converts Esoteric Software's Spine binary format to JSON.
  10. -- Checks current folder and all subfolders for *.skel files,
  11. -- and generates *.json files alongside of them.
  12.  
  13. -- Aimed at spine formats 3.5.51 & 3.6.53 specifically.
  14. -- May also need access to your *.atlas files (for image dimension info).
  15. -- Performs JSON overwrites without asking.
  16.  
  17. -- Related resources (note that they don't correspond to older Spine formats 100%):
  18. -- http://esotericsoftware.com/spine-json-format
  19. -- http://esotericsoftware.com/spine-binary-format
  20.  
  21. -- Lua runtime if you need it:
  22. -- https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/luaforwindows/LuaForWindows_v5.1.4-46.exe
  23.  
  24. ---------------------------------------------
  25. ------------        Config       ------------
  26. ---------------------------------------------
  27.  
  28. local supportDragonBones = true  -- Makes various changes to the output to allow it to
  29.                                  -- be handled by DB's Spine Importer 3.1.0 plugin.
  30.  
  31. ---------------------------------------------
  32. ------------       Imports       ------------
  33. ---------------------------------------------
  34.  
  35. require "lfs"
  36. require "bit"
  37.  
  38. ---------------------------------------------
  39. ------------      Variables      ------------
  40. ---------------------------------------------
  41.  
  42. local null, atlas, input = function() end
  43.  
  44. local transformModes = {"normal", "onlyTranslation", "noRotationOrReflection", "noScale", "noScaleOrReflection"}
  45. local blendModes = {"normal", "additive", "multiply", "screen"}
  46. local attachmentModes = {"region", "boundingbox", "mesh", "linkedmesh", "path", "point", "clipping"}
  47. local positionModes = {"fixed", "percent"}
  48. local spacingModes = {"length", "fixed", "percent"}
  49. local rotateModes = {"tangent", "chain", "chainScale"}
  50. local slotModes = {"attachment", "color", "twoColor"}
  51. local boneModes = {"rotate", "translate", "scale", "shear"}
  52. local pathModes = {"position", "spacing", "mix"}
  53.  
  54. local hex = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"}
  55.  
  56. local keyPriority = {"time", "hash", "spine", "type", "uvs", "triangles", "vertices", "hull", "edges", "width", "height",
  57.     "fps", "images", "name", "parent", "bone", "length", "rotation", "x", "y", "scaleX", "scaleY", "shearX", "shearY",
  58.     "transform", "color", "dark", "attachment", "blend", "skeleton", "bones", "slots", "ik", "transform", "path",
  59.     "skins", "events", "animations"}
  60.  
  61. for i = 1, #keyPriority do
  62.     keyPriority[keyPriority[i]] = i
  63.     keyPriority[i] = nil
  64. end
  65.  
  66. ---------------------------------------------
  67. ------------    Misc Functions   ------------
  68. ---------------------------------------------
  69.  
  70. -- Returns a hex string representation of abstract bytes.
  71. local function toHex(str)
  72.     local result = {}
  73.    
  74.     for i = 1, #str do
  75.         local val = str:byte(i)
  76.         result[#result + 1] = hex[bit.rshift(val, 4) + 1] .. hex[bit.band(val, 15) + 1]
  77.     end
  78.    
  79.     return table.concat(result)
  80. end
  81.  
  82. -- Returns a file's name and extension as separate vals.
  83. local function getExt(filename)
  84.     local div = filename:find("%.[^%.]*$")
  85.     if not div then return filename end
  86.     return filename:sub(1, div - 1), filename:sub(div + 1):lower()
  87. end
  88.  
  89. -- Does this table contain a table, true/false?
  90. local function containsTable(table)
  91.     if type(table) ~= "table" then return false end
  92.     for k, v in pairs(table) do if type(v) == "table" then return true end end
  93. end
  94.  
  95. -- Returns "val", so long as it doesn't match "fil" (or certain other things).
  96. local function filter(val, fil)
  97.     if fil then
  98.         if val ~= fil then return val end
  99.     else
  100.         if (type(val) == "number" and val ~= 0) or ((type(val) == "string" or type("val") == "table") and #val > 0) then return val end
  101.     end
  102. end
  103.  
  104. -- Rounds stuff, defaults to 2 places.
  105. local function round(val, places)
  106.     places = places or 2
  107.     return math.floor(val * 10^places + 0.5) / 10^places
  108. end
  109.  
  110. ---------------------------------------------
  111. -----------    Reader Functions    ----------
  112. ---------------------------------------------
  113.  
  114. local function readInt(positive)
  115.     local value = 0
  116.  
  117.     for i = 0, 28, 7 do
  118.         local b = input:read(1):byte()
  119.         value = value + bit.lshift(bit.band(b, 0x7F), i)
  120.         if b < 128 then break end
  121.     end
  122.  
  123.     -- ***********This PROBABLY won't work as intended:
  124.     -- ... but it doesn't seem to be called anywhere important
  125.     -- Only relevant to events, so proof read ouput for those
  126.     if not positive then
  127.         value = bit.bxor(bit.rshift(value, 1), -bit.band(value, 1))
  128.     end
  129.  
  130.     return value
  131. end
  132.  
  133. local function readInt32()
  134.     local val = input:read(4)
  135.     return val:byte(1) * 256 ^ 3 + val:byte(2) * 256 ^ 2 + val:byte(3) * 256 + val:byte(4)
  136. end
  137.  
  138. local function readShort()
  139.     local val = input:read(2)
  140.     return val:byte(1) * 256 + val:byte(2)
  141. end
  142.  
  143. -- This "should" handle UTF-8, but I don't personally need it to do so.
  144. local function readString()
  145.     local len = readInt(true)
  146.     return len > 0 and input:read(len - 1) or nil
  147. end
  148.  
  149. local function readFloat(places)
  150.     local val = input:read(4)
  151.  
  152.     local s = val:byte() < 128 and 1 or -1
  153.     local e = bit.lshift(bit.band(val:byte(1), 127), 1) + bit.rshift(bit.band(val:byte(2), 128), 7)
  154.     local f = bit.band(val:byte(2), 127) * 256 ^ 2 + val:byte(3) * 256 + val:byte(4)
  155.  
  156.     if e == 255 then
  157.         return (f > 0 and 0 or s) / 0
  158.     else
  159.         local dec = 0
  160.         for i = 0, 22 do if bit.band(f, 2^i) == 2^i then dec = dec + 2^(i-23) end end
  161.  
  162.         if e == 0 then
  163.             if f > 0 then
  164.                 return round(s * 2^(-126) * dec, places)
  165.             else
  166.                 return s * 0
  167.             end
  168.         else
  169.             return round(s * 2^(e-127) * (1 + dec), places)
  170.         end
  171.     end
  172. end
  173.  
  174. local function readBoolean()
  175.     return input:read(1):byte() ~= 0
  176. end
  177.  
  178. local function readVertices(vertexCount)
  179.     local vertices = {}
  180.    
  181.     if readBoolean() then
  182.         for l = 1, vertexCount do
  183.             vertices[#vertices + 1] = readInt(true)
  184.             for m = 1, vertices[#vertices] do
  185.                 vertices[#vertices + 1] = readInt(true)
  186.                 vertices[#vertices + 1] = readFloat()
  187.                 vertices[#vertices + 1] = readFloat()
  188.                 vertices[#vertices + 1] = readFloat()
  189.             end
  190.         end
  191.     else
  192.         for l = 1, vertexCount * 2 do vertices[l] = readFloat() end
  193.     end
  194.  
  195.     return vertices
  196. end
  197.  
  198. local function readCurve()
  199.     local cType = input:read(1):byte()
  200.    
  201.     if cType == 1 then
  202.         return "stepped"
  203.     elseif cType == 2 then
  204.         return {readFloat(3), readFloat(3), readFloat(3), readFloat(3)}
  205.     end
  206. end
  207.  
  208. ---------------------------------------------
  209. ----------- Main Decoding Function ----------
  210. ---------------------------------------------
  211.  
  212. local function decodeSkel(entry)
  213.     local skel = {["skeleton"] = {}, ["bones"] = {}, ["slots"] = {}, ["ik"] = {}, ["transform"] = {}, ["path"] = {}, ["skins"] = {}, ["events"] = {}, ["animations"] = {}}
  214.    
  215.     input = io.open(entry, "rb")
  216.    
  217.     -- ["skeleton"]
  218.     skel.skeleton.hash = filter(readString())
  219.     skel.skeleton.spine = filter(readString())
  220.    
  221.     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
  222.    
  223.     skel.skeleton.width = readFloat()
  224.     skel.skeleton.height = readFloat()
  225.  
  226.     nonessential = readBoolean()
  227.  
  228.     if nonessential then
  229.         skel.skeleton.fps = filter(readFloat())
  230.         skel.skeleton.images = filter(readString())
  231.     end
  232.  
  233.     -- ["bones"]
  234.     for i = 1, readInt(true) do
  235.         local bone = {}
  236.  
  237.         bone.name = readString()
  238.         if i > 1 then bone.parent = skel.bones[readInt(true) + 1].name end
  239.         bone.rotation = filter(readFloat())
  240.         bone.x = filter(readFloat())
  241.         bone.y = filter(readFloat())
  242.         bone.scaleX = filter(readFloat(3), 1)
  243.         bone.scaleY = filter(readFloat(3), 1)
  244.         bone.shearX = filter(readFloat())
  245.         bone.shearY = filter(readFloat())
  246.         bone.length = filter(readFloat())
  247.         bone.transform = filter(transformModes[readInt(true) + 1], "normal")
  248.        
  249.         if supportDragonBones and bone.transform then
  250.             if bone.transform == "onlyTranslation" or bone.transform == "noScale" or bone.transform == "noScaleOrReflection" then bone.inheritScale = false end
  251.             if bone.transform == "onlyTranslation" or bone.transform == "noRotationOrReflection" then bone.inheritRotation = false end
  252.         end
  253.        
  254.         if nonessential then bone.color = toHex(input:read(4)) end
  255.  
  256.         skel.bones[i] = bone
  257.     end
  258.  
  259.     -- ["slots"]
  260.     for i = 1, readInt(true) do
  261.         local slot = {}
  262.  
  263.         slot.name = readString()
  264.         slot.bone = skel.bones[readInt(true) + 1].name
  265.         slot.color = filter(toHex(input:read(4)), "ffffffff")
  266.         if skel.skeleton.spine >=  "3.6.00" then slot.dark = filter(toHex(input:read(4)), "ffffffff") end
  267.         if slot.dark then slot.dark = slot.dark:sub(3) end
  268.         slot.attachment = filter(readString())
  269.         slot.blend = filter(blendModes[readInt(true) + 1], "normal")
  270.  
  271.         skel.slots[i] = slot
  272.     end
  273.  
  274.     if #skel.slots == 0 then skel.slots = nil end
  275.    
  276.     -- ["ik"]
  277.     for i = 1, readInt(true) do
  278.         local ik = {}
  279.  
  280.         ik.name = readString()
  281.         ik.data = readInt(true)
  282.        
  283.         ik.bones = {}
  284.         for j = 1, readInt(true) do ik.bones[j] = skel.bones[readInt(true) + 1].name end
  285.         if #ik.bones == 0 then ik.bones = nil end
  286.        
  287.         ik.target = skel.bones[readInt(true) + 1].name
  288.         ik.mix = readFloat()
  289.         ik.bendPositive = input:read(1):byte() == 1
  290.        
  291.         skel.ik[i] = ik
  292.     end
  293.  
  294.     if #skel.ik == 0 then skel.ik = nil end
  295.    
  296.     -- ["transform"]
  297.     for i = 1, readInt(true) do
  298.         local trans = {}
  299.        
  300.         trans.name = readString()
  301.         trans.order = readInt(true)
  302.        
  303.         trans.bones = {}
  304.         for j = 1, readInt(true) do trans.bones[#trans.bones + 1] = skel.bones[readInt(true) + 1].name end
  305.         if #trans.bones == 0 then trans.bones = nil end
  306.        
  307.         trans.target = skel.bones[readInt(true) + 1].name
  308.        
  309.         if skel.skeleton.spine >=  "3.6.00" then
  310.             trans["local"] = readBoolean() or nil
  311.             trans.relative = readBoolean() or nil
  312.         end
  313.        
  314.         trans.rotation = filter(readFloat())
  315.         trans.x = filter(readFloat())
  316.         trans.y = filter(readFloat())
  317.         trans.scaleX = filter(readFloat())
  318.         trans.scaleY = filter(readFloat())
  319.         trans.shearY = filter(readFloat())
  320.         trans.rotateMix = filter(readFloat(), 1)
  321.         trans.translateMix = filter(readFloat(), 1)
  322.         trans.scaleMix = filter(readFloat(), 1)
  323.         trans.shearMix = filter(readFloat(), 1)
  324.        
  325.         skel.transform[i] = trans
  326.     end
  327.    
  328.     if #skel.transform == 0 then skel.transform = nil end
  329.    
  330.     -- ["path"]
  331.     for i = 1, readInt(true) do
  332.         local path = {}
  333.        
  334.         path.name = readString()
  335.         path.order = readInt(true)
  336.        
  337.         path.bones = {}
  338.         for j = 1, readInt(true) do path.bones[#path.bones + 1] = skel.bones[readInt(true) + 1].name end
  339.         if #path.bones == 0 then path.bones = nil end
  340.        
  341.         path.target = skel.slots[readInt(true) + 1].name
  342.         path.positionMode = positionModes[readInt(true) + 1]
  343.         path.spacingMode = spacingModes[readInt(true) + 1]
  344.         path.rotateMode = rotateModes[readInt(true) + 1]
  345.         path.rotation = filter(readFloat())
  346.         path.position = filter(readFloat())
  347.         path.spacing = filter(readFloat())
  348.         path.rotateMix = filter(readFloat(), 1)
  349.         path.translateMix = filter(readFloat(), 1)
  350.        
  351.         skel.path[i] = path
  352.     end
  353.    
  354.     if #skel.path == 0 then skel.path = nil end
  355.    
  356.     -- ["skins"]
  357.     local skinNums, i, skinCount = {}, 0
  358.    
  359.     repeat
  360.         local skin = {}
  361.         skinNums[i + 1] = i > 0 and readString() or "default"
  362.         skel.skins[skinNums[i + 1]] = skin
  363.        
  364.         for j = 1, readInt(true) do
  365.             local slot = {}
  366.             skin[skel.slots[readInt(true) + 1].name] = slot
  367.            
  368.             for k = 1, readInt(true) do
  369.                 local attachment = {}
  370.                 local attachmentName = readString()
  371.                 attachmentName = readString() or attachmentName
  372.                
  373.                 local type = attachmentModes[input:read(1):byte() + 1]
  374.                 attachment.type = filter(type, "region")
  375.                
  376.                 if type == "region" then
  377.                     attachment.path = filter(readString())
  378.                     attachment.rotation = filter(readFloat())
  379.                     attachment.x = filter(readFloat())
  380.                     attachment.y = filter(readFloat())
  381.                     attachment.scaleX = filter(readFloat(), 1)
  382.                     attachment.scaleY = filter(readFloat(), 1)
  383.                     attachment.width = readFloat()
  384.                     attachment.height = readFloat()
  385.                     attachment.color = filter(toHex(input:read(4)), "ffffffff")
  386.                    
  387.                     if supportDragonBones then for l = 1, #atlas do if atlas[l][attachmentName] then
  388.                         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
  389.                         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
  390.                         break
  391.                     end end end
  392.                 elseif type == "boundingbox" then
  393.                     attachment.vertexCount = readInt(true)
  394.                     attachment.vertices = readVertices(attachment.vertexCount)
  395.                     if nonessential then attachment.color = toHex(input:read(4)) end
  396.                 elseif type == "mesh" then
  397.                     attachment.path = filter(readString())
  398.                     attachment.color = filter(toHex(input:read(4)), "ffffffff")
  399.                     local vertexCount = readInt(true)
  400.                     attachment.uvs = {}
  401.                     for l = 1, vertexCount * 2 do attachment.uvs[l] = readFloat(5) end
  402.                     attachment.triangles = {}
  403.                     for l = 1, readInt(true) do attachment.triangles[l] = readShort() end
  404.                     attachment.vertices = readVertices(vertexCount)
  405.                     attachment.hull = readInt(true)
  406.                     attachment.edges = {}
  407.                    
  408.                     if nonessential then
  409.                         for l = 1, readInt(true) do attachment.edges[l] = readShort() end
  410.                         attachment.width = readFloat()
  411.                         attachment.height = readFloat()
  412.                     else
  413.                         for l = 0, attachment.hull - 1 do
  414.                             attachment.edges[#attachment.edges + 1] = l * 2
  415.                             attachment.edges[#attachment.edges + 1] = (l + 1) * 2
  416.                         end
  417.                        
  418.                         attachment.edges[#attachment.edges] = 0
  419.                        
  420.                         for l = 1, #atlas do if atlas[l][attachment.path or attachmentName] then
  421.                             attachment.width = atlas[l][attachment.path or attachmentName][1]
  422.                             attachment.height = atlas[l][attachment.path or attachmentName][2]
  423.                             break
  424.                         end end
  425.                        
  426.                         if supportDragonBones and not attachment.width then
  427.                             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 ""))
  428.                             if #atlas == 0 then
  429.                                 input:close()
  430.                                 return
  431.                             end
  432.                         end
  433.                     end
  434.                 elseif type == "linkedmesh" then
  435.                     attachment.path = filter(readString())
  436.                     attachment.color = filter(toHex(input:read(4)), "ffffffff")
  437.                     attachment.skin = filter(readString())
  438.                     attachment.parent = filter(readString())
  439.                     attachment.deform = filter(readBoolean(), true)
  440.                    
  441.                     if nonessential then
  442.                         attachment.width = readFloat()
  443.                         attachment.height = readFloat()
  444.                     else
  445.                         for l = 1, #atlas do if atlas[l][attachment.path or attachmentName] then
  446.                             attachment.width = atlas[l][attachment.path or attachmentName][1]
  447.                             attachment.height = atlas[l][attachment.path or attachmentName][2]
  448.                             break
  449.                         end end
  450.                        
  451.                         if supportDragonBones and not attachment.width then
  452.                             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 ""))
  453.                             if #atlas == 0 then
  454.                                 input:close()
  455.                                 return
  456.                             end
  457.                         end
  458.                     end
  459.                 elseif type == "path" then
  460.                     attachment.closed = readBoolean() or nil
  461.                     attachment.constantSpeed = readBoolean() or nil
  462.                     attachment.vertexCount = readInt(true)
  463.                     attachment.vertices = readVertices(attachment.vertexCount)
  464.                    
  465.                     attachment.lengths = {}
  466.                     for l = 1, attachment.vertexCount / 3 do attachment.lengths[l] = readFloat() end
  467.                    
  468.                     if nonessential then attachment.color = toHex(input:read(4)) end
  469.                 elseif type == "point" then
  470.                     attachment.rotation = filter(readFloat())
  471.                     attachment.x = filter(readFloat())
  472.                     attachment.y = filter(readFloat())
  473.                     if nonessential then attachment.color = toHex(input:read(4)) end
  474.                 elseif type == "clipping" then
  475.                     attachment["end"] = skel.slots[readInt(true) + 1].name
  476.                     attachment.vertexCount = readInt(true)
  477.                     attachment.vertices = readVertices(attachment.vertexCount)
  478.                     if nonessential then attachment.color = toHex(input:read(4)) end
  479.                 end
  480.                
  481.                 slot[attachmentName] = attachment
  482.             end
  483.         end
  484.        
  485.         if i == 0 then skinCount = readInt(true) end
  486.         i = i + 1
  487.     until i > skinCount
  488.    
  489.     -- ["events"]
  490.     local eventNums = {}
  491.    
  492.     for i = 1, readInt(true) do
  493.         local event = {}
  494.        
  495.         eventNums[i] = readString()
  496.         skel.events[eventNums[i]] = event
  497.        
  498.         event.int = filter(readInt(false))   -- ********* Suspect readInt(false) is unreliable
  499.         event.float = filter(readFloat())
  500.         event.string = filter(readString())
  501.     end
  502.  
  503.     if not containsTable(skel.events) then skel.events = nil end
  504.    
  505.     -- ["animations"]
  506.     for i = 1, readInt(true) do
  507.         local animation = {}
  508.         local aniName = readString()
  509.        
  510.         -- ["slots"]
  511.         animation.slots = {}
  512.         for j = 1, readInt(true) do
  513.             local slot = {}
  514.             local name = skel.slots[readInt(true) + 1].name
  515.             animation.slots[name] = slot
  516.            
  517.             for k = 1, readInt(true) do
  518.                 local mode = {}
  519.                 local type = slotModes[input:read(1):byte() + 1]
  520.                 slot[type] = mode
  521.                
  522.                 local frameCount = readInt(true)
  523.                 for l = 1, frameCount do
  524.                     if type == "attachment" then
  525.                         mode[l] = {["time"] = readFloat(4), ["name"] = readString() or null}
  526.                     elseif type == "color" then
  527.                         mode[l] = {["time"] = readFloat(4), ["color"] = toHex(input:read(4))}
  528.                         if l < frameCount then mode[l].curve = readCurve() end
  529.                     elseif type == "twoColor" then
  530.                         mode[l] = {["time"] = readFloat(4), ["light"] = toHex(input:read(4)), ["dark"] = toHex(input:read(4)):sub(3)}
  531.                         if l < frameCount then mode[l].curve = readCurve() end
  532.                     end
  533.                 end
  534.             end
  535.            
  536.             if supportDragonBones then slot["twoColor"] = nil end
  537.             if not containsTable(animation.slots[name]) then animation.slots[name] = nil end
  538.         end
  539.        
  540.         if not containsTable(animation.slots) then animation.slots = nil end
  541.        
  542.         -- ["bones"]
  543.         animation.bones = {}
  544.         for j = 1, readInt(true) do
  545.             local bone = {}
  546.             animation.bones[skel.bones[readInt(true) + 1].name] = bone
  547.            
  548.             for k = 1, readInt(true) do
  549.                 local mode = {}
  550.                 local type = boneModes[input:read(1):byte() + 1]
  551.                 bone[type] = mode
  552.                
  553.                 local frameCount = readInt(true)
  554.                 for l = 1, frameCount do
  555.                     if type == "rotate" then
  556.                         mode[l] = {["time"] = readFloat(4), ["angle"] = readFloat()}
  557.                     else
  558.                         mode[l] = {["time"] = readFloat(4), ["x"] = readFloat(type == "translate" and 2 or 3), ["y"] = readFloat(type == "translate" and 2 or 3)}
  559.                     end
  560.                    
  561.                     if l < frameCount then mode[l].curve = readCurve() end
  562.                 end
  563.             end
  564.         end
  565.        
  566.         if not containsTable(animation.bones) then animation.bones = nil end
  567.        
  568.         -- ["ik"]
  569.         animation.ik = {}
  570.         for j = 1, readInt(true) do
  571.             local ik = {}
  572.             animation.ik[skel.ik[readInt(true) + 1].name] = ik
  573.            
  574.             local frameCount = readInt(true)
  575.             for k = 1, frameCount do
  576.                 ik[k] = {}
  577.                 ik[k].time = readFloat(4)
  578.                 ik[k].mix = readFloat(3)
  579.                 ik[k].bendPositive = input:read(1):byte() == 1
  580.                 if k < frameCount then ik[k].curve = readCurve() end
  581.             end
  582.         end
  583.        
  584.         if not containsTable(animation.ik) then animation.ik = nil end
  585.        
  586.         -- ["transform"]
  587.         animation.transform = {}
  588.         for j = 1, readInt(true) do
  589.             local transform = {}
  590.             animation.transform[skel.transform[readInt(true) + 1].name] = transform
  591.            
  592.             local frameCount = readInt(true)
  593.             for k = 1, frameCount do
  594.                 transform[k] = {}
  595.                 transform[k].time = readFloat(4)
  596.                 transform[k].rotateMix = filter(readFloat(3), 1)
  597.                 transform[k].translateMix = filter(readFloat(3), 1)
  598.                 transform[k].scaleMix = filter(readFloat(3), 1)
  599.                 transform[k].shearMix = filter(readFloat(3), 1)
  600.                 if k < frameCount then transform[k].curve = readCurve() end
  601.             end
  602.         end
  603.        
  604.         if not containsTable(animation.transform) then animation.transform = nil end
  605.        
  606.         -- ["path"]
  607.         animation.path = {}
  608.         for j = 1, readInt(true) do
  609.             local path = {}
  610.             animation.path[skel.path[readInt(true) + 1].name] = path
  611.            
  612.             for k = 1, readInt(true) do
  613.                 local constraint = {}
  614.                 local type = pathModes[input:read(1):byte() + 1]
  615.                 path[type] = constraint
  616.                
  617.                 local frameCount = readInt(true)
  618.                 for l = 1, frameCount do
  619.                     constraint[l] = {}
  620.                     constraint[l].time = readFloat(4)
  621.                    
  622.                     if type == "position" then
  623.                         constraint[l].position = readFloat(3)
  624.                     elseif type == "spacing" then
  625.                         constraint[l].spacing = readFloat(3)
  626.                     elseif type == "mix" then
  627.                         constraint[l].rotateMix = readFloat(3)
  628.                         constraint[l].translateMix = readFloat(3)
  629.                     end
  630.                    
  631.                     if l < frameCount then constraint[l].curve = readCurve() end
  632.                 end
  633.             end
  634.         end
  635.        
  636.         if not containsTable(animation.path) then animation.path = nil end
  637.        
  638.         -- ["deform"]
  639.         animation.deform = {}
  640.         for j = 1, readInt(true) do
  641.             local skin = {}
  642.             animation.deform[skinNums[readInt(true) + 1]] = skin
  643.            
  644.             for k = 1, readInt(true) do
  645.                 local slot = {}
  646.                 skin[skel.slots[readInt(true) + 1].name] = slot
  647.                
  648.                 for l = 1, readInt(true) do
  649.                     local attachment = {}
  650.                     slot[readString()] = attachment
  651.                    
  652.                     local frameCount = readInt(true)
  653.                     for m = 1, frameCount do
  654.                         attachment[m] = {}
  655.                        
  656.                         attachment[m].time = readFloat(4)
  657.                        
  658.                         local daEnd = readInt(true)
  659.                         if daEnd ~= 0 then
  660.                             local count, start = 1, readInt(true)
  661.                             daEnd = daEnd + start - 1
  662.                            
  663.                             attachment[m].vertices = {}
  664.                            
  665.                             for n = start, daEnd do
  666.                                 attachment[m].vertices[count] = readFloat(5)
  667.                                 count = count + 1
  668.                             end
  669.                         end
  670.                        
  671.                         if m < frameCount then attachment[m].curve = readCurve() end   
  672.                     end
  673.                 end
  674.             end
  675.         end
  676.        
  677.         if not containsTable(animation.deform) then animation.deform = nil end
  678.        
  679.         -- ["drawOrder"]
  680.         animation.drawOrder = {}
  681.         for j = 1, readInt(true) do
  682.             local order = {["time"] = readFloat(4), ["offsets"] = {}}
  683.             animation.drawOrder[j] = order
  684.            
  685.             for k = 1, readInt(true) do
  686.                 local offset = {}
  687.                 order.offsets[k] = offset
  688.                
  689.                 offset.slot = skel.slots[readInt(true) + 1].name
  690.                 offset.offset = readInt(true)
  691.             end
  692.         end
  693.        
  694.         if #animation.drawOrder == 0 then animation.drawOrder = nil end
  695.        
  696.         -- ["events"]
  697.         animation.events = {}
  698.         for j = 1, readInt(true) do
  699.             local event = {}
  700.             animation.events[j] = event
  701.            
  702.             event.time = readFloat(4)
  703.             event.name = eventNums[readInt(true) + 1]
  704.             event.int = readInt(false)   -- ********* Suspect readInt(false) is unreliable
  705.             event.float = readFloat()
  706.             event.string = readBoolean() and readString() or skel.events[event.name].string
  707.         end
  708.        
  709.         if #animation.events == 0 then animation.events = nil end
  710.        
  711.         if containsTable(animation) then skel.animations[aniName] = animation end
  712.     end
  713.    
  714.     if not containsTable(skel.animations) then skel.animations = nil end
  715.    
  716.     input:close()
  717.    
  718.     if supportDragonBones and skel.skins then
  719.         for skin, skinTab in pairs(skel.skins) do
  720.             for slot, slotTab in pairs(skinTab) do
  721.                 for attachment, attTab in pairs(slotTab) do
  722.                     if attTab.type == "linkedmesh" then
  723.                         if skel.animations then
  724.                             for animation, aniTab in pairs(skel.animations) do
  725.                                 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
  726.                                     aniTab.deform[skin][slot][attachment] = aniTab.deform[skin][slot][attTab.parent]
  727.                                 end
  728.                             end
  729.                         end
  730.                        
  731.                         slotTab[attachment] = skel.skins[attTab.skin or "default"][slot][attTab.parent]
  732.                     end
  733.                 end
  734.             end
  735.         end
  736.     end
  737.  
  738.     -- Output JSON
  739.     local output = io.open(getExt(entry) .. ".json", "w")
  740.  
  741.     local function writeJSON(value, indent)
  742.         local tabs = string.rep("\9", indent)
  743.         local cT = containsTable(value)
  744.         local hashMap = #value == 0
  745.  
  746.         local sorted = {}
  747.         if hashMap then
  748.             for k in pairs(value) do sorted[#sorted + 1] = k end
  749.             table.sort(sorted, function(a, b)
  750.                 if not (keyPriority[a] or keyPriority[b]) then return a < b end
  751.                 if keyPriority[a] and keyPriority[b] then return keyPriority[a] < keyPriority[b] end
  752.                 return keyPriority[a] and true or false
  753.             end)
  754.         else sorted = value end
  755.  
  756.         output:write((hashMap and "{" or "[") .. (cT and ("\n" .. tabs) or " "))
  757.  
  758.         for i = 1, #sorted do
  759.             local thisVal
  760.  
  761.             if hashMap then
  762.                 local thisKey = sorted[i]
  763.                 thisVal = value[thisKey]
  764.                 output:write("\"" .. thisKey .. "\": ")
  765.             else thisVal = sorted[i] end
  766.  
  767.             if type(thisVal) == "number" then
  768.                 output:write(thisVal)
  769.             elseif type(thisVal) == "string" then
  770.                 output:write("\"" .. thisVal .. "\"")
  771.             elseif type(thisVal) == "boolean" then
  772.                 output:write(tostring(thisVal))
  773.             elseif type(thisVal) == "function" then
  774.                 output:write("null")
  775.             elseif type(thisVal) == "table" then
  776.                 writeJSON(thisVal, indent + 1)
  777.             end
  778.  
  779.             if i ~= #sorted then output:write(cT and (",\n" .. tabs) or ", ") end
  780.         end
  781.  
  782.         output:write((cT and ("\n" .. string.rep("\9", indent - 1)) or " ") .. (hashMap and "}" or "]"))
  783.     end
  784.  
  785.     writeJSON(skel, 0)
  786.    
  787.     output:close()
  788. end
  789.  
  790. ---------------------------------------------
  791. ------------    Main Work Loop   ------------
  792. ---------------------------------------------
  793.  
  794. local function checkDir(dir)
  795.     atlas = {}
  796.     local skels, dirs = {}, {}
  797.    
  798.     for entry in lfs.dir(dir) do
  799.         local path = dir .. "/" .. entry
  800.         local attribs = lfs.attributes(path)
  801.    
  802.         if attribs then
  803.             if entry ~= "." and entry ~= ".." and attribs.mode == "directory" then
  804.                 dirs[#dirs + 1] = path
  805.             elseif attribs.mode == "file" then
  806.                 if (entry:sub(-6):lower() == ".atlas" or entry:sub(-10):lower() == ".atlas.txt") then
  807.                     atlas[#atlas + 1] = {}
  808.                     local atlas, curName = atlas[#atlas]
  809.  
  810.                     for line in io.lines(path) do
  811.                         if line:sub(1, 1) ~= " " then curName = line end
  812.                         if line:find("orig:") then atlas[curName] = {tonumber(line:sub(9, line:find(",") - 1)), tonumber(line:sub(line:find(", ") + 2))} end
  813.                     end
  814.                 elseif entry:sub(-5):lower() == ".skel" then
  815.                     skels[#skels + 1] = path
  816.                 end
  817.             end
  818.         end
  819.     end
  820.    
  821.     for i = 1, #skels do decodeSkel(skels[i]) end
  822.     for i = 1, #dirs do checkDir(dirs[i]) end
  823. end
  824.  
  825. return checkDir(".")
Add Comment
Please, Sign In to add comment