Advertisement
Guest User

Untitled

a guest
Jun 27th, 2017
58
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.23 KB | None | 0 0
  1.  
  2. --[[
  3. RBXMX by einsteinK
  4.  
  5. Has a small XML parser from somewhere on Lua.org that works now.
  6. Allows (un)serializing ROBLOX instances (using the (old) XML format)
  7.  
  8. Returns a table with 2 methods:
  9. - Serialize({instance1,instance2,...}): Serializes the instances to XML and returns it (to .rbxmx)
  10. - Unserialize(str): Reverses the process and returns a table with the instances in the XML (from .rbxmx)
  11. When the framework sees that rbxmx.lua is present, it'll also add 2 functions to the Globals table:
  12. - saveinstance(file,instance): Serializes an instance and saves it to the file (in .rbxmx format)
  13. - loadinstance(file): Returns the (first) instance saved in the file (in .rbxmx format, otherwise errors)
  14. - saveplace(file): Like saveinstance for creating a .rbxlx so it's easier to feel good of yourself by stealing places
  15. (Only saves contents of: Workspace,Lighting,StarterPack,StarterGui,ReplicatedStorage,StarterPlayer,Teams and Chat)
  16. AGAIN: THIS ONLY SUPPORTS THE XML FORMAT (.rbxmx and .rbxlx) AND CAN ONLY SAVE/LOAD IN THIS FORMAT
  17. (well, saveinstance/loadinstance can be used on any file, even .exe, as long as the content are .rbxmx)
  18.  
  19. --]]
  20.  
  21. local function parseargs(s)
  22. local arg = {}
  23. string.gsub(s, "([%-%w]+)=([\"'])(.-)%2", function (w, _, a)
  24. arg[w] = a
  25. end)
  26. return arg
  27. end
  28.  
  29. local function parseXML(s)
  30. local stack = {}
  31. local top = {}
  32. table.insert(stack, top)
  33. local ni,c,label,xarg, empty
  34. local i, j = 1, 1
  35. while true do
  36. ni,j,c,label,xarg, empty = string.find(s, "<(%/?)([%w:]+)(.-)(%/?)>", i)
  37. if not ni then break end
  38. local text = string.sub(s, i, ni-1)
  39. if not string.find(text, "^%s*$") then
  40. table.insert(top, text)
  41. end
  42. if empty == "/" then -- empty element tag
  43. table.insert(top, {label=label, args=parseargs(xarg), empty=1})
  44. elseif c == "" then -- start tag
  45. top = {label=label, args=parseargs(xarg)}
  46. table.insert(stack, top) -- new level
  47. else -- end tag
  48. local toclose = table.remove(stack) -- remove top
  49. top = stack[#stack]
  50. if #stack < 1 then
  51. error("nothing to close with "..label)
  52. end
  53. if toclose.label ~= label then
  54. error("trying to close "..toclose.label.." with "..label)
  55. end
  56. table.insert(top, toclose)
  57. end
  58. i = j+1
  59. end
  60. local text = string.sub(s, i)
  61. if not string.find(text, "^%s*$") then
  62. table.insert(stack[#stack], text)
  63. end
  64. if #stack > 1 then
  65. error("unclosed "..stack[#stack].label)
  66. end return stack[1]
  67. end
  68.  
  69. local patterns = {
  70. Item = '<Item class="%s" referent="%s">\n';
  71. Property = '<%s name="%s">%s</%s>\n';
  72. CFrame = ([[<X>%f</X><Y>%f</Y><Z>%f</Z>
  73. <R00>%f</R00><R01>%f</R01><R02>%f</R02>
  74. <R10>%f</R10><R11>%f</R11><R12>%f</R12>
  75. <R20>%f</R20><R21>%f</R21><R22>%f</R22>]]):gsub("%s+","");
  76. Vector2 = [[<X>%f</X><Y>%f</Y>]];
  77. Vector3 = [[<X>%f</X><Y>%f</Y><Z>%f</Z>]];
  78. UDim2 = [[<XS>%f</XS><XO>%d</XO><YS>%f</YS><YO>%d</YO>]];
  79. Rect2D = [[<min><X>%f</X><Y>%f</Y></min><max><X>%f</X><Y>%f</Y></max>]];
  80. } local classP,classes = {},{}
  81.  
  82. local AD,_ad = "http://wiki.roblox.com/index.php/API:Class_reference/Dump/raw?action=raw"
  83. _ad,AD = pcall(game.HttpGet,game,AD,true)
  84. if not _ad then
  85. warn("Couldn't fetch JSON from Anaminus, falling back to api.txt")
  86. _ad,AD = pcall(readfile,"api.txt")
  87. if not _ad then error("Couldn't get the API in JSON format",0) end
  88. end
  89.  
  90. local enums = {}
  91. local function realTyp(typ)
  92. if typ == "Object" then
  93. return "Ref"
  94. end return typ
  95. end
  96.  
  97. local props
  98. for line in AD:gmatch("[^\n]+") do
  99. if line:find("^Class") then
  100. local name,parent = line:match("^Class (%w+)[%s:]*(%w*)")
  101. classes[name] = {Name=name,Parent=parent,Properties={}}
  102. props = classes[name].Properties
  103. elseif line:find("^Enum") then
  104. enums[line:match("^Enum (%w+)")],props = true
  105. elseif props and line:find("^\tProperty") then
  106. local typ,name,hmm = line:match("^\tProperty (%w+) %w+%.(%S+)(.*)$")
  107. if not hmm:find("%[") then props[name] = realTyp(typ) end
  108. end
  109. end
  110. for k,v in pairs(classes) do
  111. v.Parent = classes[v.Parent]
  112. end
  113.  
  114. -- Some properties are useless to save
  115. classes.Instance.Properties.Parent = nil
  116. classes.Instance.Properties.Archivable = nil
  117. classes.BasePart.Properties.Position = nil
  118. classes.BasePart.Properties.Velocity = nil
  119. classes.BasePart.Properties.RotVelocity = nil
  120.  
  121. classes.BasePart.Properties.CustomPhysicalProperties = nil -- TODO
  122.  
  123. local function classProps(class)
  124. if classP[class] then return classP[class] end
  125. local res,C = {},classes[class]
  126. while C do
  127. for k,v in pairs(C.Properties) do
  128. res[k] = v
  129. end C = C.Parent
  130. end classP[class] = res return res
  131. end
  132.  
  133. local function properties(obj)
  134. local props,keys = classProps(obj.ClassName),{}
  135. for k,v in pairs(props) do
  136. table.insert(keys,k)
  137. end local i = 0
  138. return function() i=i+1
  139. local key = keys[i]
  140. if not key then return end
  141. return props[key],key,obj[key]
  142. end
  143. end
  144.  
  145. for a,b,c in properties(Instance.new("TextButton")) do
  146. print(a,b,c)
  147. end
  148.  
  149. local serialize
  150. local function serializeInstance(obj,ref,tabs)
  151. if obj.ClassName == "" then return "" end
  152. if obj.ClassName == "Terrain" then return "" end
  153. local res = {("\t"):rep(tabs),patterns.Item:format(obj.ClassName,ref[obj]),("\t"):rep(tabs+1),"<Properties>\n"}
  154. --[[if obj:IsA("FormFactorPart") then
  155. res[#res+1] = ("\t"):rep(tabs+2)
  156. res[#res+1] = patterns.Property:format("a","FormFactor",serialize(obj.FormFactor,"FormFactor"),"a")
  157. end]]
  158. for typ,prop,val in properties(obj) do
  159. if prop ~= "FormFactor" and val ~= nil then
  160. if --[[val ~= "" and]] (typ ~= "Ref" or typ == "Ref" and ref[val]) then
  161. res[#res+1] = ("\t"):rep(tabs+2)
  162. local t = (typ == "Content" or typ == "Ref") and typ or 'a'
  163. res[#res+1] = patterns.Property:format(t,prop,serialize(val,typ,ref,prop),t)
  164. end
  165. end
  166. end res[#res+1] = ("\t"):rep(tabs+1)
  167. res[#res+1] = "</Properties>\n"
  168. for k,v in pairs(obj:GetChildren()) do
  169. if not v:IsA("PartOperation") and not v:IsA("MeshPart") then
  170. res[#res+1] = serializeInstance(v,ref,tabs+1)
  171. end
  172. end res[#res+1] = ("\t"):rep(tabs)
  173. res[#res+1] = "</Item>\n"
  174. return table.concat(res)
  175. end
  176.  
  177. local char = {
  178. ["<"] = "&lt;";
  179. [">"] = "&gt;";
  180. }
  181.  
  182. local hex = {}
  183. do
  184. local idk = {[0]=0,1,2,3,4,5,6,7,8,9,"A","B","C","D","E","F"}
  185. for i=0,255 do
  186. hex[i] = idk[math.floor(i/16)]..idk[i%16]
  187. end
  188. end
  189.  
  190. function serialize(val,typ,ref,prop)
  191. if prop == "Rotation" and typ == "Vector3" then
  192. local x,y,z = val.X,val.Y,val.Z
  193. x = x < -179.99999 and 180 or x > 179.99999 and 180 or x
  194. y = y < -179.99999 and 180 or y > 179.99999 and 180 or y
  195. z = z < -179.99999 and 180 or z > 179.99999 and 180 or z
  196. return patterns.Vector3:format(x,y,z)
  197. end
  198. if typ == "Ref" then
  199. return ref[val]
  200. elseif enums[typ] then
  201. return val.Value
  202. elseif typ == "string" then
  203. return val:gsub("[<>]",char)
  204. elseif typ == "CoordinateFrame" then
  205. local comps = {val:components()}
  206. for i=4,12 do local v = comps[i]
  207. if v < 0.00001 and v > -0.00001 then comps[i] = 0 end
  208. if v < -0.99999 then comps[i] = -1 end
  209. if v > 0.99999 then comps[i] = 1 end
  210. end return patterns.CFrame:format(unpack(comps))
  211. elseif typ == "Vector2" then
  212. return patterns.Vector2:format(val.X,val.Y)
  213. elseif typ == "Vector3" then
  214. return patterns.Vector3:format(val.X,val.Y,val.Z)
  215. elseif typ == "UDim2" then
  216. return patterns.UDim2:format(val.X.Scale,val.X.Offset,val.Y.Scale,val.Y.Offset)
  217. elseif typ == "Rect2D" then
  218. return patterns.Rect2D:format(val.Min.X,val.Min.Y,val.Max.X,val.Max.Y)
  219. elseif typ == "Content" then
  220. return "<url>"..val.."</url>"
  221. elseif typ == "BrickColor" then
  222. return val.Number
  223. elseif typ == "int" or typ == "float" or typ == "double" or typ == "bool" then
  224. return tostring(val)
  225. elseif typ == "Color3" then
  226. local r,g,b = val.r*255,val.g*255,val.b*255
  227. r,g,b = math.floor(r),math.floor(g),math.floor(b)
  228. return tonumber("FF"..hex[r]..hex[g]..hex[b],16)
  229. elseif typ == "ColorSequence" or typ == "NumberSequence" or typ == "NumberRange" then
  230. return tostring(val)
  231. elseif typ == "PhysicalProperties" then
  232. --[[
  233. <PhysicalProperties name="CustomPhysicalProperties">
  234. <CustomPhysics>false</CustomPhysics>
  235. </PhysicalProperties>]]
  236. error("TODO",0)
  237. end error("Unknown property type: "..typ,0)
  238. end
  239.  
  240. local function buildRef(obj,ref)
  241. ref[obj],ref[1] = ref[1],ref[1] + 1
  242. for k,v in pairs(obj:GetChildren()) do
  243. buildRef(v,ref)
  244. end return ref
  245. end
  246. local function Serialize(model)
  247. if typeof(model) == "Instance" then model = {model} end
  248. local res,ref = {'<roblox version="4">\n'},{1}
  249. for i=1,#model do
  250. res[i+1] = serializeInstance(model[i],buildRef(model[i],ref),1)
  251. end res[#res+1] = '</roblox>' return table.concat(res)
  252. --[[return table.concat{'<roblox version="4">\n';
  253. serializeInstance(model,buildRef(model,{1}),1);
  254. '</roblox>'
  255. }]]
  256. end
  257.  
  258. local cfProps = {"X","Y","Z","R00","R01","R02","R10","R11","R12","R20","R21","R22"}
  259. local bcProps = {Head=true,Torso=true,LeftArm=true,RightArm=true,LeftLeg=true,RightLeg=true,Brick=true}
  260. local function parseValue(item,ref,classname)
  261. local prop = classes[classname]
  262. while prop do
  263. local p = prop.Properties[item.args.name]
  264. if p then prop = p break end
  265. prop = prop.Parent
  266. end if not prop then return end
  267. local first = item[1]
  268. if prop == "string" then return first end
  269. if prop == "bool" then return first=="true" end
  270. if prop == "int" then return tonumber(first) end
  271. if prop == "float" then return tonumber(first) end
  272. if prop == "double" then return tonumber(first) end
  273. if prop == "Ref" then return {tonumber(first)} end
  274. if prop == "BrickColor" then return BrickColor.new(first) end
  275. if prop == "Color3" then
  276. -- definitely broken
  277. local eh = first:gmatch("[^%s,]+")
  278. return Color3.new(eh(),eh(),eh())
  279. end
  280. if prop == "Vector2" or prop == "Vector3" or prop == "CoordinateFrame" or prop == "UDim2" then
  281. local eh = {}
  282. for i=1,#item do
  283. eh[item[i].label] = tonumber(item[i][1]) or item[i][1]
  284. end local idk = {}
  285. if prop == "CoordinateFrame" then
  286. for k,v in pairs(cfProps) do
  287. idk[k] = eh[v]
  288. end return CFrame.new(unpack(idk))
  289. elseif prop == "Vector2" then
  290. return Vector2.new(eh.X,eh.Y)
  291. elseif prop == "Vector3" then
  292. return Vector3.new(eh.X,eh.Y,eh.Z)
  293. elseif prop == "UDim2" then
  294. return UDim2.new(eh.XS,eh.XO,eh.YS,eh.YO)
  295. end
  296. end
  297. if prop == "Content" then
  298. assert(first.label == "url","Idk how to handle non-url Content")
  299. return first[1]
  300. end
  301. if enums[prop] then return tonumber(first) end
  302. error("Unknown property type:"..prop,0)
  303. end
  304. local function parseProps(tab,ref,classname)
  305. local res = {}
  306. for i=1,#tab do
  307. local item = tab[i]
  308. local name = item.args.name
  309. if name then
  310. res[name] = parseValue(item,ref,classname)
  311. end
  312. end return res
  313. end
  314. local function parseItems(tab,ref,toRef,folderForError)
  315. local res,props = {}
  316. for i=1,#tab do
  317. local item = tab[i]
  318. if item.label == "Properties" then
  319. props = item
  320. elseif item.label == "Item" then
  321. local suc,obj = pcall(Instance.new,item.args.class)
  322. if not suc and folderForError then
  323. suc,obj = true,Instance.new("Folder")
  324. warn("Couldn't create an instance of",item.args.class..", using a folder instead")
  325. end
  326. if suc then
  327. if item.args.referent then
  328. ref[tonumber(item.args.referent)] = obj
  329. end res[#res+1] = obj
  330. local childs,properties = parseItems(item,ref,toRef,folderForError)
  331. for i=1,#childs do
  332. childs[i].Parent = obj
  333. end
  334. for k,v in pairs(properties) do
  335. if type(v) == "table" then
  336. toRef[#toRef+1] = {obj,k,v[1]}
  337. else
  338. if not pcall(function() obj[k] = v end) then
  339. warn("Couldn't set",obj.ClassName.."."..k,"to",typeof(v),v)
  340. end
  341. end
  342. end
  343. elseif item.args.class ~= "Status" then
  344. warn("Couldn't create an instance of",item.args.class)
  345. end
  346. end
  347. end
  348. props = props and parseProps(props,ref,tab.args.class)
  349. return res,props
  350. end
  351. local function Unserialize(str,folderForError)
  352. if str:sub(1,8) ~= "<roblox " then
  353. error("I only support the XML format...",0)
  354. end --error("Soon (TM)",0)
  355. local parsed = parseXML(str)[1]
  356. assert(parsed.label == "roblox","Doesn't start with <roblox> tag")
  357. local ref,toRef = {},{} local objects = parseItems(parsed,ref,toRef,folderForError)
  358. for k,v in pairs(toRef) do v[1][v[2]] = ref[v[3]] end return objects
  359. end
  360.  
  361. return {
  362. Serialize = Serialize;
  363. Unserialize = Unserialize;
  364. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement