NoTextForSpeech

Untitled

May 12th, 2025 (edited)
19
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 117.12 KB | None | 0 0
  1. --!native
  2. --!optimize 2
  3. --!divine-intellect
  4. -- https://discord.gg/wx4ThpAsmw
  5.  
  6. local function string_find(s, pattern)
  7. return string.find(s, pattern, nil, true)
  8. end
  9.  
  10. local function ArrayToDict(t, hydridMode, valueOverride, typeStrict)
  11. local tmp = {}
  12.  
  13. if hydridMode then
  14. for any1, any2 in t do
  15. if type(any1) == "number" then
  16. tmp[any2] = valueOverride or true
  17. elseif type(any2) == "table" then
  18. tmp[any1] = ArrayToDict(any2, hydridMode) -- any1 is Class, any2 is Name
  19. else
  20. tmp[any1] = any2
  21. end
  22. end
  23. else
  24. for _, key in t do
  25. if not typeStrict or typeStrict and type(key) == typeStrict then
  26. tmp[key] = true
  27. end
  28. end
  29. end
  30.  
  31. return tmp
  32. end
  33.  
  34. local global_container
  35. do
  36. local filename = "UniversalMethodFinder"
  37.  
  38. local finder
  39. finder, global_container = loadstring(
  40. game:HttpGet("https://raw.githubusercontent.com/luau/SomeHub/main/" .. filename .. ".luau", true),
  41. filename
  42. )()
  43.  
  44. finder({
  45. -- readbinarystring = 'string.find(...,"bin",nil,true)', -- ! Could match some unwanted stuff (getbinaryindex)
  46. -- request = 'string.find(...,"request",nil,true) and not string.find(...,"internal",nil,true)',
  47. base64encode = 'local a={...}local b=a[1]local function c(a,b)return string.find(a,b,nil,true)end;return c(b,"encode")and(c(b,"base64")or c(string.lower(tostring(a[2])),"base64"))',
  48. -- cloneref = 'string.find(...,"clone",nil,true) and string.find(...,"ref",nil,true)',
  49. -- decompile = '(string.find(...,"decomp",nil,true) and string.sub(...,#...) ~= "s")',
  50. gethiddenproperty = 'string.find(...,"get",nil,true) and string.find(...,"h",nil,true) and string.find(...,"prop",nil,true) and string.sub(...,#...) ~= "s"',
  51. gethui = 'string.find(...,"get",nil,true) and string.find(...,"h",nil,true) and string.find(...,"ui",nil,true)',
  52. -- getcon = 'string.find(...,"get",nil,true) and (string.find(...,"conn",nil,true) or string.find(...,"sig",nil,true)) and string.sub(...,#(...))=="s"',
  53. getnilinstances = 'string.find(...,"nil",nil,true) and string.find(...,"get",nil,true) and string.sub(...,#...) == "s"', -- ! Could match some unwanted stuff
  54. getscriptbytecode = 'string.find(...,"get",nil,true) and string.find(...,"bytecode",nil,true)', -- or string.find(...,"dump",nil,true) and string.find(...,"string",nil,true) due to Fluxus (dumpstring returns a function)
  55. hash = 'local a={...}local b=a[1]local function c(a,b)return string.find(a,b,nil,true)end;return c(b,"hash")and c(string.lower(tostring(a[2])),"crypt")',
  56. protectgui = 'string.find(...,"protect",nil,true) and string.find(...,"ui",nil,true) and not string.find(...,"un",nil,true)',
  57. setthreadidentity = 'string.find(...,"identity",nil,true) and string.find(...,"set",nil,true)',
  58. }, true, 10)
  59. end
  60.  
  61. local identify_executor = identifyexecutor or getexecutorname or whatexecutor
  62.  
  63. local EXECUTOR_NAME = identify_executor and identify_executor() or ""
  64.  
  65. -- local cloneref = global_container.cloneref
  66. local gethiddenproperty = global_container.gethiddenproperty
  67.  
  68. -- These should be universal enough
  69. local appendfile = appendfile
  70. local readfile = readfile
  71. local writefile = writefile
  72.  
  73. local getscriptbytecode = global_container.getscriptbytecode -- * A lot of assumptions are made based on whether this function is defined or not. So in certain edge cases, like if the executor defines "decompile" or "getscripthash" function yet doesn't define this function there might be loss of functionality of the saveinstance. Although that would be very rare and weird
  74. local base64encode = global_container.base64encode
  75. local sha384
  76.  
  77. local service = setmetatable({}, {
  78. __index = function(self, serviceName)
  79. local o, s = pcall(Instance.new, serviceName)
  80. local Service = o and s
  81. or game:GetService(serviceName)
  82. or settings():GetService(serviceName)
  83. or UserSettings():GetService(serviceName)
  84.  
  85. -- if cloneref then
  86. -- Service = cloneref(Service)
  87. -- end
  88. if Service then
  89. self[serviceName] = Service
  90. end
  91. return Service
  92. end,
  93. })
  94.  
  95. local gethiddenproperty_fallback
  96. do -- * Load Region of Déjà Vu
  97. local UGCValidationService -- = service.UGCValidationService
  98.  
  99. gethiddenproperty_fallback = function(instance, propertyName)
  100. if not UGCValidationService then
  101. UGCValidationService = service.UGCValidationService
  102. end
  103. return UGCValidationService:GetPropertyValue(instance, propertyName) -- TODO Sadly there's no way to tell whether value is actually nil or the function just couldn't read it (always returns nil for "Class" category properties)
  104. -- TODO `category ~= "Class"` causes WeldConstraint Part1Internal to be read as nil and not get unfiltered. Currently, there are no properties of category "Class" that match the following: NotScriptable, can be read with gethiddenproperty_fallback accurately (it always outputs nil for "Class" category, making that check useless anyway) & don't have a NotScriptableFix.
  105. end
  106. if gethiddenproperty then
  107. local o, r = pcall(gethiddenproperty, workspace, "StreamOutBehavior")
  108. if not o or r ~= nil and typeof(r) ~= "EnumItem" then -- * Tests if gethiddenproperty is broken
  109. gethiddenproperty = nil
  110. else
  111. o, r = pcall(gethiddenproperty, Instance.new("AnimationRigData", Instance.new("Folder")), "parent") -- * Tests how it reacts to property overlap (shadowing) due to AnimationRigData.parent; expected BinaryString
  112.  
  113. if o and r ~= nil and type(r) ~= "string" then
  114. gethiddenproperty = nil
  115. end
  116. end
  117. end
  118. local function benchmark(f1, f2, ...)
  119. local ranking = table.create(2)
  120. for i, f in { f1, f2 } do
  121. local start = os.clock()
  122. for _ = 1, 50 do
  123. f(...)
  124. end
  125. ranking[i] = { t = os.clock() - start, f = f }
  126. end
  127. table.sort(ranking, function(a, b)
  128. return a.t < b.t
  129. end)
  130. return ranking[1].f
  131. end
  132.  
  133. local test_str = string.rep("\1\0\0\0\1\2\3\4\5\6\7", 50)
  134.  
  135. do
  136. if not bit32.byteswap or not pcall(bit32.byteswap, 1) then -- Because Fluxus is missing byteswap
  137. bit32 = table.clone(bit32)
  138.  
  139. local function tobit(num)
  140. num %= (bit32.bxor(num, 32))
  141. if 0x80000000 < num then
  142. num -= bit32.bxor(num, 32)
  143. end
  144. return num
  145. end
  146.  
  147. bit32.byteswap = function(num)
  148. local BYTE_SIZE = 8
  149. local MAX_BYTE_VALUE = 255
  150.  
  151. num %= bit32.bxor(2, 32)
  152.  
  153. local a = bit32.band(num, MAX_BYTE_VALUE)
  154. num = bit32.rshift(num, BYTE_SIZE)
  155.  
  156. local b = bit32.band(num, MAX_BYTE_VALUE)
  157. num = bit32.rshift(num, BYTE_SIZE)
  158.  
  159. local c = bit32.band(num, MAX_BYTE_VALUE)
  160. num = bit32.rshift(num, BYTE_SIZE)
  161.  
  162. local d = bit32.band(num, MAX_BYTE_VALUE)
  163. num = tobit(bit32.lshift(bit32.lshift(bit32.lshift(a, BYTE_SIZE) + b, BYTE_SIZE) + c, BYTE_SIZE) + d)
  164. return num
  165. end
  166.  
  167. table.freeze(bit32)
  168. end
  169.  
  170. -- Credits @Reselim
  171. local reselim_base64encode
  172. pcall(function()
  173. local b64_enc_buf = loadstring(
  174. game:HttpGet("https://raw.githubusercontent.com/Reselim/Base64/master/Base64.lua", true),
  175. "Base64"
  176. )().encode
  177. reselim_base64encode = function(raw)
  178. return buffer.tostring(b64_enc_buf(buffer.fromstring(raw)))
  179. end
  180. end)
  181.  
  182. -- * Tests if base64encode exists and works properly then benchmark it
  183. if base64encode and base64encode("\1\0\0\0\1") == "AQAAAAE=" then
  184. if reselim_base64encode then
  185. base64encode = benchmark(base64encode, reselim_base64encode, test_str)
  186. end
  187. else
  188. base64encode = reselim_base64encode
  189. end
  190.  
  191. assert(base64encode, "base64encode not found")
  192. end
  193.  
  194. do
  195. local hash = global_container.hash
  196.  
  197. if hash then
  198. sha384 = function(data)
  199. return hash(data, "sha384")
  200. end
  201. end
  202.  
  203. local filename = "RequireOnlineModule"
  204.  
  205. -- Credits @boatbomber
  206. local hashlib_sha384
  207. pcall(function()
  208. hashlib_sha384 = loadstring(
  209. game:HttpGet("https://raw.githubusercontent.com/luau/SomeHub/main/" .. filename .. ".luau", true),
  210. filename
  211. )()(4544052033).sha384
  212. end)
  213.  
  214. -- * Tests if sha384 exists then benchmark it
  215. if hashlib_sha384 then
  216. if sha384 then
  217. sha384 = benchmark(sha384, hashlib_sha384, test_str)
  218. else
  219. sha384 = hashlib_sha384
  220. end
  221. end
  222.  
  223. assert(sha384, "sha384 hash function not found")
  224. end
  225. end
  226.  
  227. -- local custom_decompiler
  228.  
  229. -- if getscriptbytecode then
  230. -- end
  231.  
  232. local SharedStrings = {}
  233. local SharedString_identifiers = setmetatable({
  234. identifier = 1e15, -- 1 quadrillion, up to 9.(9) quadrillion, in theory this shouldn't ever run out and be enough for all sharedstrings ever imaginable
  235. -- TODO: worst case, add fallback to str randomizer once numbers run out : )
  236. }, {
  237. __index = function(self, str)
  238. local Identifier = base64encode(tostring(self.identifier)) -- tostring is only needed for built-in base64encode, Reselim's doesn't need it as buffers autoconvert
  239. self.identifier += 1
  240.  
  241. self[str] = Identifier -- ? The value of the md5 attribute is a Base64-encoded key. <SharedString> type elements use this key to refer to the value of the string. The value is the text content, which is Base64-encoded. Historically, the key was the MD5 hash of the string value. However, this is not required; the key can be any value that will uniquely identify the shared string. Roblox currently uses BLAKE2b truncated to 16 bytes..
  242. return Identifier
  243. end,
  244. })
  245.  
  246. local inherited_properties = {}
  247. local default_instances = {}
  248. local referents, ref_size = {}, 0 -- ? Roblox encodes all <Item> elements with a referent attribute. Each value is generated by starting with the prefix RBX, followed by a UUID version 4, with - characters removed, and all characters converted to uppercase.
  249.  
  250. local function __BIT(...) -- * Credits to Friend (you know yourself)
  251. local Value = 0
  252.  
  253. for i, bit in { ... } do
  254. if bit then
  255. Value += 2 ^ (i - 1)
  256. end
  257. end
  258.  
  259. return Value
  260. end
  261.  
  262. local function GetRef(instance)
  263. local ref = referents[instance]
  264. if not ref then
  265. ref = ref_size
  266. referents[instance] = ref
  267. ref_size += 1
  268. end
  269. return ref
  270. end
  271.  
  272. local function index(self, index_name)
  273. return self[index_name]
  274. end
  275.  
  276. local CLIENT_VERSION = tonumber(string.split(version(), ".")[2])
  277.  
  278. local attr_Type_IDs = {
  279. string = 0x02,
  280. boolean = 0x03,
  281. -- int32 = 0x04,
  282. -- float = 0x05,
  283. number = 0x06,
  284. -- Array = 0x07,
  285. -- Dictionary = 0x08,
  286. UDim = 0x09,
  287. UDim2 = 0x0A,
  288. Ray = 0x0B,
  289. Faces = 0x0C,
  290. Axes = 0x0D,
  291. BrickColor = 0x0E,
  292. Color3 = 0x0F,
  293. Vector2 = 0x10,
  294. Vector3 = 0x11,
  295. Vector2int16 = 0x12,
  296. Vector3int16 = 0x13,
  297. CFrame = 0x14,
  298. EnumItem = 0x15,
  299. NumberSequence = 0x17,
  300. NumberSequenceKeypoint = 0x18,
  301. ColorSequence = 0x19,
  302. ColorSequenceKeypoint = 0x1A,
  303. NumberRange = 0x1B,
  304. Rect = 0x1C,
  305. PhysicalProperties = 0x1D,
  306. Region3 = 0x1F,
  307. Region3int16 = 0x20,
  308. Font = 0x21,
  309. }
  310. local CFrame_Rotation_IDs = {
  311. ["\0\0\128\63\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\63\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\63"] = 0x02,
  312. ["\0\0\128\63\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\191\0\0\0\0\0\0\128\63\0\0\0\0"] = 0x03,
  313. ["\0\0\128\63\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\191\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\191"] = 0x05,
  314. ["\0\0\128\63\0\0\0\0\0\0\0\128\0\0\0\0\0\0\0\0\0\0\128\63\0\0\0\0\0\0\128\191\0\0\0\0"] = 0x06,
  315. ["\0\0\0\0\0\0\128\63\0\0\0\0\0\0\128\63\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\191"] = 0x07,
  316. ["\0\0\0\0\0\0\0\0\0\0\128\63\0\0\128\63\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\63\0\0\0\0"] = 0x09,
  317. ["\0\0\0\0\0\0\128\191\0\0\0\0\0\0\128\63\0\0\0\0\0\0\0\128\0\0\0\0\0\0\0\0\0\0\128\63"] = 0x0a,
  318. ["\0\0\0\0\0\0\0\0\0\0\128\191\0\0\128\63\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\191\0\0\0\0"] = 0x0c,
  319. ["\0\0\0\0\0\0\128\63\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\63\0\0\128\63\0\0\0\0\0\0\0\0"] = 0x0d,
  320. ["\0\0\0\0\0\0\0\0\0\0\128\191\0\0\0\0\0\0\128\63\0\0\0\0\0\0\128\63\0\0\0\0\0\0\0\0"] = 0x0e,
  321. ["\0\0\0\0\0\0\128\191\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\191\0\0\128\63\0\0\0\0\0\0\0\0"] = 0x10,
  322. ["\0\0\0\0\0\0\0\0\0\0\128\63\0\0\0\0\0\0\128\191\0\0\0\0\0\0\128\63\0\0\0\0\0\0\0\128"] = 0x11,
  323. ["\0\0\128\191\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\63\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\191"] = 0x14,
  324. ["\0\0\128\191\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\63\0\0\0\0\0\0\128\63\0\0\0\128"] = 0x15,
  325. ["\0\0\128\191\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\191\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\63"] = 0x17,
  326. ["\0\0\128\191\0\0\0\0\0\0\0\128\0\0\0\0\0\0\0\0\0\0\128\191\0\0\0\0\0\0\128\191\0\0\0\128"] = 0x18,
  327. ["\0\0\0\0\0\0\128\63\0\0\0\128\0\0\128\191\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\63"] = 0x19,
  328. ["\0\0\0\0\0\0\0\0\0\0\128\191\0\0\128\191\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\63\0\0\0\0"] = 0x1b,
  329. ["\0\0\0\0\0\0\128\191\0\0\0\128\0\0\128\191\0\0\0\0\0\0\0\128\0\0\0\0\0\0\0\0\0\0\128\191"] = 0x1c,
  330. ["\0\0\0\0\0\0\0\0\0\0\128\63\0\0\128\191\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\191\0\0\0\0"] = 0x1e,
  331. ["\0\0\0\0\0\0\128\63\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\191\0\0\128\191\0\0\0\0\0\0\0\0"] = 0x1f,
  332. ["\0\0\0\0\0\0\0\0\0\0\128\63\0\0\0\0\0\0\128\63\0\0\0\128\0\0\128\191\0\0\0\0\0\0\0\0"] = 0x20,
  333. ["\0\0\0\0\0\0\128\191\0\0\0\0\0\0\0\0\0\0\0\0\0\0\128\63\0\0\128\191\0\0\0\0\0\0\0\0"] = 0x22,
  334. ["\0\0\0\0\0\0\0\0\0\0\128\191\0\0\0\0\0\0\128\191\0\0\0\128\0\0\128\191\0\0\0\0\0\0\0\128"] = 0x23,
  335. }
  336. local Binary_Descriptors
  337. Binary_Descriptors = {
  338. __SEQUENCE = function(raw, valueFormatter, keypointSize, Envelope)
  339. local Keypoints = raw.Keypoints
  340. local Keypoints_n = #Keypoints
  341.  
  342. local len = 4 + (keypointSize or 12) * Keypoints_n
  343. local b = buffer.create(len)
  344. local offset = 0
  345.  
  346. buffer.writeu32(b, offset, Keypoints_n)
  347. offset += 4
  348.  
  349. for _, keypoint in Keypoints do
  350. buffer.writef32(b, offset, Envelope or keypoint.Envelope)
  351. offset += 4
  352. buffer.writef32(b, offset, keypoint.Time)
  353. offset += 4
  354.  
  355. local Value = keypoint.Value
  356. if valueFormatter then
  357. offset += valueFormatter(Value, b, offset)
  358. else
  359. buffer.writef32(b, offset, Value)
  360. offset += 4
  361. end
  362. end
  363.  
  364. return b, len
  365. end,
  366. --------------------------------------------------------------
  367. --------------------------------------------------------------
  368. --------------------------------------------------------------
  369. ["string"] = function(raw)
  370. local raw_len = #raw
  371. local len = 4 + raw_len
  372.  
  373. local b = buffer.create(len)
  374.  
  375. buffer.writeu32(b, 0, raw_len)
  376. buffer.writestring(b, 4, raw)
  377.  
  378. return b, len
  379. end,
  380. ["boolean"] = function(raw)
  381. local b = buffer.create(1)
  382.  
  383. buffer.writeu8(b, 0, raw and 1 or 0)
  384.  
  385. return b, 1
  386. end,
  387. ["number"] = function(raw) -- double
  388. local b = buffer.create(8)
  389.  
  390. buffer.writef64(b, 0, raw)
  391.  
  392. return b, 8
  393. end,
  394. ["UDim"] = function(raw)
  395. local b = buffer.create(8)
  396.  
  397. buffer.writef32(b, 0, raw.Scale)
  398. buffer.writei32(b, 4, raw.Offset)
  399.  
  400. return b, 8
  401. end,
  402. ["UDim2"] = function(raw)
  403. local b = buffer.create(16)
  404.  
  405. local UDim__descriptor = Binary_Descriptors.UDim
  406. local X = UDim__descriptor(raw.X)
  407. buffer.copy(b, 0, X)
  408. local Y = UDim__descriptor(raw.Y)
  409. buffer.copy(b, 8, Y)
  410.  
  411. return b, 16
  412. end,
  413. ["Ray"] = function(raw)
  414. local b = buffer.create(24)
  415.  
  416. local Vector3__descriptor = Binary_Descriptors.Vector3
  417. local Origin = Vector3__descriptor(raw.Origin)
  418. buffer.copy(b, 0, Origin)
  419. local Direction = Vector3__descriptor(raw.Direction)
  420. buffer.copy(b, 12, Direction)
  421.  
  422. return b, 24
  423. end,
  424. ["Faces"] = function(raw)
  425. local b = buffer.create(4)
  426.  
  427. buffer.writeu32(b, 0, __BIT(raw.Right, raw.Top, raw.Back, raw.Left, raw.Bottom, raw.Front))
  428.  
  429. return b, 4
  430. end,
  431. ["Axes"] = function(raw)
  432. local b = buffer.create(4)
  433.  
  434. buffer.writeu32(b, 0, __BIT(raw.X, raw.Y, raw.Z))
  435.  
  436. return b, 4
  437. end,
  438. ["BrickColor"] = function(raw)
  439. local b = buffer.create(4)
  440.  
  441. buffer.writeu32(b, 0, raw.Number)
  442.  
  443. return b, 4
  444. end,
  445. ["Color3"] = function(raw)
  446. local b = buffer.create(12)
  447.  
  448. buffer.writef32(b, 0, raw.R)
  449. buffer.writef32(b, 4, raw.G)
  450. buffer.writef32(b, 8, raw.B)
  451.  
  452. return b, 12
  453. end,
  454. ["Vector2"] = function(raw)
  455. local b = buffer.create(8)
  456.  
  457. buffer.writef32(b, 0, raw.X)
  458. buffer.writef32(b, 4, raw.Y)
  459.  
  460. return b, 8
  461. end,
  462. ["Vector3"] = function(raw)
  463. local b = buffer.create(12)
  464.  
  465. buffer.writef32(b, 0, raw.X)
  466. buffer.writef32(b, 4, raw.Y)
  467. buffer.writef32(b, 8, raw.Z)
  468.  
  469. return b, 12
  470. end,
  471. ["Vector2int16"] = function(raw)
  472. local b = buffer.create(4)
  473.  
  474. buffer.writei16(b, 0, raw.X)
  475. buffer.writei16(b, 2, raw.Y)
  476.  
  477. return b, 4
  478. end,
  479. ["Vector3int16"] = function(raw)
  480. local b = buffer.create(6)
  481.  
  482. buffer.writei16(b, 0, raw.X)
  483. buffer.writei16(b, 2, raw.Y)
  484. buffer.writei16(b, 4, raw.Z)
  485.  
  486. return b, 6
  487. end,
  488. ["CFrame"] = function(raw)
  489. local X, Y, Z, R00, R01, R02, R10, R11, R12, R20, R21, R22 = raw:GetComponents()
  490.  
  491. local rotation_ID = CFrame_Rotation_IDs[string.pack("<fffffffff", R00, R01, R02, R10, R11, R12, R20, R21, R22)]
  492.  
  493. local len = rotation_ID and 13 or 49
  494. local b = buffer.create(len)
  495.  
  496. -- ? TODO cleaner but slower ?
  497. -- local write_vector3 = Descriptors.Vector3
  498. -- local pos = write_vector3(raw.Position)
  499. -- buffer.copy(b, 0, pos)
  500.  
  501. buffer.writef32(b, 0, X)
  502. buffer.writef32(b, 4, Y)
  503. buffer.writef32(b, 8, Z)
  504.  
  505. if rotation_ID then
  506. buffer.writeu8(b, 12, rotation_ID)
  507. else
  508. buffer.writeu8(b, 12, 0x0)
  509.  
  510. -- ? TODO cleaner but slower ?
  511. -- buffer.copy(b, 13, write_vector3(raw.XVector)) -- R00, R10, R20
  512. -- buffer.copy(b, 13 + 12, write_vector3(raw.YVector)) -- R01, R11, R21
  513. -- buffer.copy(b, 13 + 24, write_vector3(raw.ZVector)) -- R02, R12, R22
  514.  
  515. buffer.writef32(b, 13, R00)
  516. buffer.writef32(b, 17, R01)
  517. buffer.writef32(b, 21, R02)
  518.  
  519. buffer.writef32(b, 25, R10)
  520. buffer.writef32(b, 29, R11)
  521. buffer.writef32(b, 33, R12)
  522.  
  523. buffer.writef32(b, 37, R20)
  524. buffer.writef32(b, 41, R21)
  525. buffer.writef32(b, 45, R22)
  526. end
  527.  
  528. return b, len
  529. end,
  530. ["EnumItem"] = function(raw)
  531. local b_Name, Name_size = Binary_Descriptors.string(tostring(raw.EnumType))
  532.  
  533. local len = Name_size + 4
  534. local b = buffer.create(len)
  535.  
  536. buffer.copy(b, 0, b_Name)
  537. buffer.writeu32(b, Name_size, raw.Value)
  538.  
  539. return b, len
  540. end,
  541. ["NumberSequence"] = nil,
  542. -- ["NumberSequenceKeypoint"] = nil, -- TODO Only impl. if necessary because NumberSequence will reference this therefore slowing down itself
  543. ["ColorSequence"] = function(raw)
  544. return Binary_Descriptors.__SEQUENCE(raw, function(color3, b, offset)
  545. buffer.copy(b, offset, Binary_Descriptors.Color3(color3))
  546. return 12
  547. end, 20, 0)
  548. end,
  549. -- ["ColorSequenceKeypoint"] = nil, -- TODO Only impl. if necessary because ColorSequence will reference this therefore slowing down itself
  550. ["NumberRange"] = function(raw)
  551. local b = buffer.create(8)
  552.  
  553. buffer.writef32(b, 0, raw.Min)
  554. buffer.writef32(b, 4, raw.Max)
  555.  
  556. return b, 8
  557. end,
  558. ["Rect"] = function(raw)
  559. local b = buffer.create(16)
  560.  
  561. local Vector2__descriptor = Binary_Descriptors.Vector2
  562. local Min = Vector2__descriptor(raw.Min)
  563. buffer.copy(b, 0, Min)
  564. local Max = Vector2__descriptor(raw.Max)
  565. buffer.copy(b, 8, Max)
  566.  
  567. return b, 16
  568. end,
  569. ["PhysicalProperties"] = function(raw) -- ? Not sure yet (https://github.com/RobloxAPI/spec/blob/master/properties/drafts/AttributesSerializeFull.md#physicalproperties)
  570. local len = 1
  571. if raw then
  572. len += 20
  573. end
  574. local b = buffer.create(len)
  575.  
  576. buffer.writeu8(b, 0, raw and 1 or 0)
  577.  
  578. if raw then
  579. buffer.writef32(b, 1, raw.Density)
  580. buffer.writef32(b, 5, raw.Friction)
  581. buffer.writef32(b, 9, raw.Elasticity)
  582. buffer.writef32(b, 13, raw.FrictionWeight)
  583. buffer.writef32(b, 17, raw.ElasticityWeight)
  584. end
  585.  
  586. return b, len
  587. end,
  588. ["Region3"] = function(raw)
  589. local b = buffer.create(24)
  590.  
  591. local Vector3__descriptor = Binary_Descriptors.Vector3
  592. local Min = Vector3__descriptor(raw.Min)
  593. buffer.copy(b, 0, Min)
  594. local Max = Vector3__descriptor(raw.Max)
  595. buffer.copy(b, 12, Max)
  596.  
  597. return b, 24
  598. end,
  599. ["Region3int16"] = function(raw)
  600. local b = buffer.create(12)
  601.  
  602. local Vector3int16__descriptor = Binary_Descriptors.Vector3int16
  603. local Min = Vector3int16__descriptor(raw.Min)
  604. buffer.copy(b, 0, Min)
  605. local Max = Vector3int16__descriptor(raw.Max)
  606. buffer.copy(b, 6, Max)
  607.  
  608. return b, 12
  609. end,
  610. ["Font"] = 636 < math.huge and function(raw)
  611. local string__descriptor = Binary_Descriptors.string
  612.  
  613. local b_Family, Family_size = string__descriptor(raw.Family)
  614. local b_CachedFaceId, CachedFaceId_size = string__descriptor("")
  615.  
  616. local len = 3 + Family_size + CachedFaceId_size
  617. local b = buffer.create(len)
  618.  
  619. local ok_w, weight = pcall(index, raw, "Weight")
  620. local ok_s, style = pcall(index, raw, "Style")
  621.  
  622. buffer.writeu16(b, 0, ok_w and weight.Value or 0)
  623. buffer.writeu8(b, 2, ok_s and style.Value or 0)
  624.  
  625. buffer.copy(b, 3, b_Family)
  626. buffer.copy(b, 3 + Family_size, b_CachedFaceId)
  627.  
  628. return b, len
  629. end or function(raw)
  630. local string__descriptor = Binary_Descriptors.string
  631.  
  632. local b_Family, Family_size = string__descriptor(raw.Family)
  633. local b_CachedFaceId, CachedFaceId_size = string__descriptor("")
  634.  
  635. local len = 3 + Family_size + CachedFaceId_size
  636. local b = buffer.create(len)
  637.  
  638. local FontString = tostring(raw)
  639.  
  640. local EmptyWeight = string_find(FontString, "Weight = ,")
  641. local EmptyStyle = string_find(FontString, "Style = }")
  642.  
  643. buffer.writeu16(b, 0, EmptyWeight and 0 or raw.Weight.Value)
  644. buffer.writeu8(b, 2, EmptyStyle and 0 or raw.Style.Value)
  645.  
  646. buffer.copy(b, 3, b_Family)
  647. buffer.copy(b, 3 + Family_size, b_CachedFaceId)
  648.  
  649. return b, len
  650. end,
  651. }
  652. do
  653. Binary_Descriptors.NumberSequence = Binary_Descriptors.__SEQUENCE
  654. end
  655.  
  656. local ESCAPES = {
  657. ["&"] = "&amp;",
  658. ["<"] = "&lt;",
  659. [">"] = "&gt;",
  660. ['"'] = "&#34;",
  661. ["'"] = "&#39;",
  662. ["\0"] = "",
  663. }
  664.  
  665. local ESCAPES_PATTERN = "a-zA-Z"
  666.  
  667. for rangeStart, rangeEnd in string.gmatch(ESCAPES_PATTERN, "(.)%-(.)") do
  668. for charCode = string.byte(rangeStart), string.byte(rangeEnd) do
  669. local char = string.char(charCode)
  670. if not ESCAPES[char] then
  671. ESCAPES[char] = "&#" .. charCode .. ";"
  672. end
  673. end
  674. end
  675.  
  676.  
  677. local XML_Descriptors
  678. XML_Descriptors = {
  679. __CDATA = function(raw) -- ? Normally Roblox doesn't use CDATA unless the string has newline characters (\n); We rather CDATA everything for sake of speed
  680. return "<![CDATA[" .. raw .. "]]>"
  681. end,
  682. __ENUM = function(raw)
  683. return raw.Value, "token"
  684. end,
  685. __NORMALIZE_NUMBER = function(raw)
  686. if raw ~= raw then
  687. return "NAN"
  688. elseif raw == math.huge then
  689. return "INF"
  690. elseif raw == -math.huge then
  691. return "-INF"
  692. end
  693.  
  694. return raw
  695. end,
  696. __NORMALIZE_RANGE = function(raw)
  697. return raw ~= raw and "0" or raw -- Normally we should return "-nan(ind)" instead of "0" but this adds more compatibility
  698. end,
  699. __MINMAX = function(min, max, descriptor)
  700. return "<min>" .. descriptor(min) .. "</min><max>" .. descriptor(max) .. "</max>"
  701. end,
  702. __PROTECTEDSTRING = function(raw) -- ? its purpose is to "protect" data from being treated as ordinary character data during processing;
  703. return string_find(raw, "]]>") and string.gsub(raw, ESCAPES_PATTERN, ESCAPES) or XML_Descriptors.__CDATA(raw)
  704. end,
  705. __SEQUENCE = function(raw, valueFormatter)
  706. -- The value is the text content, formatted as a space-separated list of floating point numbers.
  707. -- tostring(raw) also works (but way slower rn)
  708. local __NORMALIZE_RANGE = XML_Descriptors.__NORMALIZE_RANGE
  709.  
  710. local sequence = ""
  711.  
  712. for _, keypoint in raw.Keypoints do
  713. local Value = keypoint.Value
  714.  
  715. sequence ..= keypoint.Time .. " " .. (valueFormatter and valueFormatter(Value) or __NORMALIZE_RANGE(Value) .. " " .. __NORMALIZE_RANGE(
  716. keypoint.Envelope
  717. ) .. " ") -- ? Trailing whitespace is only needed for lune compatibility
  718. end
  719.  
  720. return sequence
  721. end,
  722. __VECTOR = function(X, Y, Z) -- Each element is a <float>
  723. local Value = "<X>" .. X .. "</X><Y>" .. Y .. "</Y>" -- There is no Vector without at least two Coordinates.. (Vector1, at least on Roblox)
  724.  
  725. if Z then
  726. Value ..= "<Z>" .. Z .. "</Z>"
  727. end
  728.  
  729. return Value
  730. end,
  731. --------------------------------------------------------------
  732. --------------------------------------------------------------
  733. --------------------------------------------------------------
  734. Axes = function(raw)
  735. -- The text of this element is formatted as an integer between 0 and 7
  736.  
  737. return "<axes>" .. __BIT(raw.X, raw.Y, raw.Z) .. "</axes>"
  738. end,
  739.  
  740. -- ? Roblox uses CDATA only for these (try to prove this wrong): CollisionGroupData, SmoothGrid, MaterialColors, PhysicsGrid
  741. -- ! Assuming all base64 encoded strings won't have newlines
  742.  
  743. BinaryString = function(raw)
  744. return raw == "" and "" or base64encode(raw)
  745. end,
  746.  
  747. BrickColor = function(raw)
  748. return raw.Number -- * Roblox encodes the tags as "int", but this is not required for Roblox to properly decode the type. For better compatibility, it is preferred that third-party implementations encode and decode "BrickColor" tags instead. Could also use "int" or "Color3uint8"
  749. end,
  750. CFrame = function(raw)
  751. local X, Y, Z, R00, R01, R02, R10, R11, R12, R20, R21, R22 = raw:GetComponents()
  752. return XML_Descriptors.__VECTOR(X, Y, Z)
  753. .. "<R00>"
  754. .. R00
  755. .. "</R00><R01>"
  756. .. R01
  757. .. "</R01><R02>"
  758. .. R02
  759. .. "</R02><R10>"
  760. .. R10
  761. .. "</R10><R11>"
  762. .. R11
  763. .. "</R11><R12>"
  764. .. R12
  765. .. "</R12><R20>"
  766. .. R20
  767. .. "</R20><R21>"
  768. .. R21
  769. .. "</R21><R22>"
  770. .. R22
  771. .. "</R22>",
  772. "CoordinateFrame"
  773. end,
  774. Color3 = function(raw) -- Each element is a <float>
  775. return "<R>" .. raw.R .. "</R><G>" .. raw.G .. "</G><B>" .. raw.B .. "</B>" -- ? It is recommended that Color3 is encoded with elements instead of text.
  776. end,
  777. Color3uint8 = function(raw)
  778. -- https://github.com/rojo-rbx/rbx-dom/blob/master/docs/xml.md#color3uint8
  779. -- ? It is recommended that Color3uint8 is encoded with text instead of elements.
  780.  
  781. return 0xFF000000
  782. + (math.floor(raw.R * 255) * 0x10000)
  783. + (math.floor(raw.G * 255) * 0x100)
  784. + math.floor(raw.B * 255)
  785. -- return bit32.bor(
  786. -- bit32.bor(bit32.bor(bit32.lshift(0xFF, 24), bit32.lshift(0xFF * raw.R, 16)), bit32.lshift(0xFF * raw.G, 8)),
  787. -- 0xFF * raw.B
  788. -- )
  789.  
  790. -- return tonumber(string.format("0xFF%02X%02X%02X",raw.R*255,raw.G*255,raw.B*255))
  791. end,
  792. ColorSequence = function(raw)
  793. -- The value is the text content, formatted as a space-separated list of FLOATing point numbers.
  794.  
  795. return XML_Descriptors.__SEQUENCE(raw, function(color3)
  796. local __NORMALIZE_RANGE = XML_Descriptors.__NORMALIZE_RANGE
  797.  
  798. return __NORMALIZE_RANGE(color3.R)
  799. .. " "
  800. .. __NORMALIZE_RANGE(color3.G)
  801. .. " "
  802. .. __NORMALIZE_RANGE(color3.B)
  803. .. " 0 "
  804. end)
  805. end,
  806. Content = function(raw)
  807. local SourceType = raw.SourceType
  808. return SourceType == Enum.ContentSourceType.None and "<null></null>"
  809. or SourceType == Enum.ContentSourceType.Uri and "<uri>" .. XML_Descriptors.string(raw.Uri) .. "</uri>"
  810. or SourceType == Enum.ContentSourceType.Object and "<Ref>" .. GetRef(raw.Object)
  811. .. "</Ref>" -- TODO Not sure, run tests
  812. end,
  813. ContentId = function(raw)
  814. return raw == "" and "<null></null>" or "<url>" .. XML_Descriptors.string(raw) .. "</url>", "Content" -- TODO Remove "Content" str once Roblox fully releases Content DataType
  815. end,
  816. CoordinateFrame = function(raw)
  817. return "<CFrame>" .. XML_Descriptors.CFrame(raw) .. "</CFrame>"
  818. end,
  819. -- DateTime = function(raw) return raw.UnixTimestampMillis end, -- ? Not sure
  820. Faces = function(raw)
  821. -- The text of this element is formatted as an integer between 0 and 63
  822. return "<faces>" .. __BIT(raw.Right, raw.Top, raw.Back, raw.Left, raw.Bottom, raw.Front) .. "</faces>"
  823. end,
  824. Font = 636 < CLIENT_VERSION
  825. and function(raw)
  826. -- TODO (OPTIONAL ELEMENT): Figure out how to determine (ContentId) <CachedFaceId><url>rbxasset://fonts/GothamSSm-Medium.otf</url></CachedFaceId>
  827. --[[
  828. ? game:GetService("TextService"):GetFontMemoryData()
  829. ? rbxasset://fonts/families/{Enum.Font.BuilderSans.Name}.json
  830. ]]
  831.  
  832. local ok_w, weight = pcall(index, raw, "Weight")
  833. local ok_s, style = pcall(index, raw, "Style")
  834.  
  835. return "<Family>"
  836. .. XML_Descriptors.ContentId(raw.Family)
  837. .. "</Family><Weight>"
  838. .. (ok_w and XML_Descriptors.__ENUM(weight) or "")
  839. .. "</Weight><Style>"
  840. .. (ok_s and style.Name or "") -- Weird but this field accepts .Name of enum instead..
  841. .. "</Style>"
  842. end
  843. or function(raw)
  844. local FontString = tostring(raw) -- TODO: Temporary fix
  845.  
  846. local EmptyWeight = string_find(FontString, "Weight = ,")
  847. local EmptyStyle = string_find(FontString, "Style = }")
  848.  
  849. return "<Family>"
  850. .. XML_Descriptors.ContentId(raw.Family)
  851. .. "</Family><Weight>"
  852. .. (EmptyWeight and "" or XML_Descriptors.__ENUM(raw.Weight))
  853. .. "</Weight><Style>"
  854. .. (EmptyStyle and "" or raw.Style.Name) -- Weird but this field accepts .Name of enum instead..
  855. .. "</Style>"
  856. end,
  857. NumberRange = function(raw) -- tostring(raw) also works
  858. -- The value is the text content, formatted as a space-separated list of floating point numbers.
  859. local __NORMALIZE_RANGE = XML_Descriptors.__NORMALIZE_RANGE
  860.  
  861. return __NORMALIZE_RANGE(raw.Min) .. " " .. __NORMALIZE_RANGE(raw.Max) --[[.. " "]] -- ! This might be required for compatibility; __NORMALIZE_RANGE is not needed here but it fixes the issue where "nan 10" value would reset to "0 0"
  862. end,
  863. NumberSequence = nil,
  864. -- NumberSequence = Descriptors.__SEQUENCE,
  865.  
  866. -- Path2DControlPoint = function(raw) -- ? Not sure
  867. -- local udim2 = XML_Descriptors.UDim2
  868. -- return "<Position>"
  869. -- .. udim2(raw.Position)
  870. -- .. "</Position>"
  871. -- .. "<LeftTangent>"
  872. -- .. udim2(raw.LeftTangent)
  873. -- .. "</LeftTangent>"
  874. -- .. "<RightTangent>"
  875. -- .. udim2(raw.RightTangent)
  876. -- .. "</RightTangent>"
  877. -- end,
  878.  
  879. PhysicalProperties = function(raw)
  880. --[[
  881. Contains at least one CustomPhysics element, which is interpreted according to the bool type. If this value is true, then the tag also contains an element for each component of the PhysicalProperties:
  882.  
  883. Density
  884. Friction
  885. Elasticity
  886. FrictionWeight
  887. ElasticityWeight
  888.  
  889. The value of each component is represented by the text content formatted as a 32-bit floating point number (see float)
  890. ]]
  891.  
  892. local CustomPhysics = "<CustomPhysics>" .. XML_Descriptors.bool(raw and true or false) .. "</CustomPhysics>"
  893.  
  894. return raw
  895. and CustomPhysics .. "<Density>" .. raw.Density .. "</Density><Friction>" .. raw.Friction .. "</Friction><Elasticity>" .. raw.Elasticity .. "</Elasticity><FrictionWeight>" .. raw.FrictionWeight .. "</FrictionWeight><ElasticityWeight>" .. raw.ElasticityWeight .. "</ElasticityWeight>"
  896. or CustomPhysics
  897. end,
  898. -- ProtectedString = function(raw) return tostring(raw), "ProtectedString" end,
  899. Ray = function(raw)
  900. local vector3 = XML_Descriptors.Vector3
  901.  
  902. return "<origin>" .. vector3(raw.Origin) .. "</origin><direction>" .. vector3(raw.Direction) .. "</direction>"
  903. end,
  904. Rect = function(raw)
  905. return XML_Descriptors.__MINMAX(raw.Min, raw.Max, XML_Descriptors.Vector2), "Rect2D"
  906. end,
  907. Region3 = function(raw) -- ? Not sure yet (/Network/Replicator.cpp#L1306)
  908. local Translation = raw.CFrame.Position
  909. local HalfSize = raw.Size * 0.5
  910.  
  911. return XML_Descriptors.__MINMAX(
  912. Translation - HalfSize, -- /App/util/Region3.cpp#L38
  913. Translation + HalfSize, -- /App/util/Region3.cpp#L42
  914. XML_Descriptors.Vector3
  915. )
  916. end,
  917. Region3int16 = function(raw) -- ? Not sure yet (/App/v8tree/EnumProperty.cpp#L346)
  918. return XML_Descriptors.__MINMAX(raw.Min, raw.Max, XML_Descriptors.Vector3int16)
  919. end,
  920. SharedString = function(raw)
  921. raw = raw == "" and "" or base64encode(raw)
  922.  
  923. local Identifier = SharedString_identifiers[raw]
  924.  
  925. if SharedStrings[Identifier] == nil then
  926. SharedStrings[Identifier] = raw
  927. end
  928.  
  929. return Identifier
  930. end,
  931. SecurityCapabilities = nil,
  932. -- SystemAddress = function(raw) return raw end, -- PeerId? -- ? Not sure
  933. -- TweenInfo = function(raw) -- ? Not sure
  934. -- local __NORMALIZE_NUMBER = XML_Descriptors.__NORMALIZE_NUMBER
  935. -- local enum = XML_Descriptors.__ENUM
  936.  
  937. -- return "<Time>"
  938. -- .. __NORMALIZE_NUMBER(raw.Time)
  939. -- .. "</Time><DelayTime>"
  940. -- .. __NORMALIZE_NUMBER(raw.DelayTime)
  941. -- .. "</DelayTime><RepeatCount>"
  942. -- .. __NORMALIZE_NUMBER(raw.RepeatCount)
  943. -- .. "</RepeatCount><Reverses>"
  944. -- .. XML_Descriptors.bool(raw.Reverses)
  945. -- .. "</Reverses><EasingDirection>"
  946. -- .. enum(raw.EasingDirection)
  947. -- .. "</EasingDirection><EasingStyle>"
  948. -- .. enum(raw.EasingStyle)
  949. -- .. "</EasingStyle>"
  950. -- end,
  951. UDim = function(raw)
  952. --[[
  953. S: Represents the Scale component. Interpreted as a <float>.
  954. O: Represents the Offset component. Interpreted as an <int>.
  955. ]]
  956.  
  957. return "<S>" .. raw.Scale .. "</S><O>" .. raw.Offset .. "</O>"
  958. end,
  959. UDim2 = function(raw)
  960. --[[
  961. XS: Represents the X.Scale component. Interpreted as a <float>.
  962. XO: Represents the X.Offset component. Interpreted as an <int>.
  963. YS: Represents the Y.Scale component. Interpreted as a <float>.
  964. YO: Represents the Y.Offset component. Interpreted as an <int>.
  965. ]]
  966.  
  967. local X, Y = raw.X, raw.Y
  968.  
  969. return "<XS>"
  970. .. X.Scale
  971. .. "</XS><XO>"
  972. .. X.Offset
  973. .. "</XO><YS>"
  974. .. Y.Scale
  975. .. "</YS><YO>"
  976. .. Y.Offset
  977. .. "</YO>"
  978. end,
  979.  
  980. -- UniqueId = function(raw) -- ? Not sure -- ? No idea if this even needs a Descriptor
  981. -- --[[
  982. -- UniqueId properties might be random everytime Studio saves a place file
  983. -- and don't have a use right now outside of packages, which SSI doesn't
  984. -- account for anyway. They generate diff noise, so we shouldn't serialize
  985. -- them until we have to.
  986. -- ]]
  987. -- -- https://github.com/MaximumADHD/Roblox-Client-Tracker/blob/roblox/LuaPackages/Packages/_Index/ApolloClientTesting/ApolloClientTesting/utilities/common/makeUniqueId.lua#L62
  988. -- return ""
  989. -- end,
  990.  
  991. Vector2 = function(raw)
  992. --[[
  993. X: Represents the X component. Interpreted as a <float>.
  994. Y: Represents the Y component. Interpreted as a <float>.
  995. ]]
  996. return XML_Descriptors.__VECTOR(raw.X, raw.Y)
  997. end,
  998. Vector2int16 = nil,
  999. -- Vector2int16 = Descriptors.Vector2, -- except as <int>
  1000. Vector3 = function(raw)
  1001. --[[
  1002. X: Represents the X component. Interpreted as a <float>.
  1003. Y: Represents the Y component. Interpreted as a <float>.
  1004. Z: Represents the Z component. Interpreted as a <float>.
  1005. ]]
  1006. return XML_Descriptors.__VECTOR(raw.X, raw.Y, raw.Z)
  1007. end,
  1008. Vector3int16 = nil,
  1009. -- Vector3int16 = Descriptors.Vector3, -- except as <int>\
  1010. bool = function(raw)
  1011. return raw and "true" or "false"
  1012. end,
  1013. double = nil, -- Float64
  1014. float = nil, -- Float32
  1015. int = nil, -- Int32
  1016. int64 = nil, -- Int64 (long)
  1017. string = function(raw)
  1018. return (raw == nil or raw == "") and ""
  1019. or string_find(raw, "]]>") and string.gsub(raw, ESCAPES_PATTERN, ESCAPES)
  1020. or XML_Descriptors.__CDATA(string.gsub(raw, "\0", ""))
  1021. end,
  1022.  
  1023. --------------------------------------------------------------
  1024. -----------%localappdata%/Roblox/GlobalSettings_13.xml--------
  1025. --------------------------------------------------------------
  1026. -- QDir = function(raw) -- ? Not sure
  1027. -- return raw
  1028. -- end,
  1029. -- QFont = function(raw) -- ? Not sure
  1030. -- return raw
  1031. -- end,
  1032. }
  1033.  
  1034. do
  1035. local BASE_CAPABILITIES
  1036. pcall(function()
  1037. BASE_CAPABILITIES = SecurityCapabilities.new()
  1038. end)
  1039. if BASE_CAPABILITIES then
  1040. local CAPABILITY_BITS = {
  1041. Plugin = 2 ^ 0, ---------------- 0
  1042. LocalUser = 2 ^ 1, ------------- 1
  1043. WritePlayer = 2 ^ 2, ----------- 2
  1044. RobloxScript = 2 ^ 3, ---------- 3
  1045. RobloxEngine = 2 ^ 4, ---------- 4
  1046. NotAccessible = 2 ^ 5, --------- 5
  1047. RunClientScript = 2 ^ 8, ------- 8
  1048. RunServerScript = 2 ^ 9, ------- 9
  1049. AccessOutsideWrite = 2 ^ 11, --- 11 (0xb)
  1050. Unassigned = 2 ^ 15, ----------- 15 (0xf)
  1051. AssetRequire = 2 ^ 16, --------- 16 (0x10)
  1052. LoadString = 2 ^ 17, ----------- 17 (0x11)
  1053. ScriptGlobals = 2 ^ 18, -------- 18 (0x12)
  1054. CreateInstances = 2 ^ 19, ------ 19 (0x13)
  1055. Basic = 2 ^ 20, ---------------- 20 (0x14)
  1056. Audio = 2 ^ 21, ---------------- 21 (0x15)
  1057. DataStore = 2 ^ 22, ------------ 22 (0x16)
  1058. Network = 2 ^ 23, -------------- 23 (0x17)
  1059. Physics = 2 ^ 24, -------------- 24 (0x18)
  1060. UI = 2 ^ 25, ------------------- 25 (0x19)
  1061. CSG = 2 ^ 26, ------------------ 26 (0x1a)
  1062. Chat = 2 ^ 27, ----------------- 27 (0x1b)
  1063. Animation = 2 ^ 28, ------------ 28 (0x1c)
  1064. Avatar = 2 ^ 29, --------------- 29 (0x1d)
  1065. Input = 2 ^ 30, ---------------- 30 (0x1e)
  1066. Environment = 2 ^ 31, ---------- 31 (0x1f)
  1067. RemoteEvent = 2 ^ 32, ---------- 32 (0x20)
  1068. LegacySound = 2 ^ 33, ---------- 33 (0x21)
  1069. Players = 2 ^ 34, -------------- 34 (0x22)
  1070. CapabilityControl = 2 ^ 35, ---- 35 (0x23)
  1071. InternalTest = 2 ^ 60, --------- 60 (0x3c), Related to TestingGameScript
  1072. PluginOrOpenCloud = 2 ^ 61, ---- 61 (0x3d)
  1073. Assistant = 2 ^ 62, ------------ 62 (0x3e)
  1074. -- Restricted = 2 ^ 63, ----------- for negative (highest bit for signed integers)
  1075. }
  1076.  
  1077. XML_Descriptors.SecurityCapabilities = function(raw)
  1078. -- TODO tostring & string.split aren't ideal but this is the only way until the feature is out of the experimental phase
  1079.  
  1080. if raw == BASE_CAPABILITIES then
  1081. return 0
  1082. end
  1083.  
  1084. local result = 0
  1085.  
  1086. for _, flag in string.split(tostring(raw), " | ") do
  1087. local bit = CAPABILITY_BITS[flag]
  1088. if bit then
  1089. result += bit
  1090. end
  1091. end
  1092.  
  1093. return result
  1094. end
  1095. end
  1096. end
  1097.  
  1098. for descriptorName, redirectName in
  1099. {
  1100. NumberSequence = "__SEQUENCE",
  1101. Vector2int16 = "Vector2",
  1102. Vector3int16 = "Vector3",
  1103. double = "__NORMALIZE_NUMBER",
  1104. float = "__NORMALIZE_NUMBER",
  1105. int = "__NORMALIZE_NUMBER",
  1106. int64 = "__NORMALIZE_NUMBER",
  1107. }
  1108. do
  1109. XML_Descriptors[descriptorName] = XML_Descriptors[redirectName]
  1110. end
  1111. if CLIENT_VERSION < math.huge then -- math.huge because unknown yet
  1112. -- ! For sake of compatibility with older clients, Roblox currently does this too but it WILL probably change in the future so keep track of that. ONCE ROBLOX MOVES AWAY FROM THIS, MAKE SURE TO KEEP SUPPORT FOR VERY OLD CLIENTS; 645 <= CLIENT Ver < ??? (ver when roblox moves away), THIS IS COMPLICATED SO PROBABLY NO SUPPORT, JUST USE OLDER VER OF USSI; NEWEST CLIENTS
  1113. XML_Descriptors.Content = XML_Descriptors.ContentId
  1114. end
  1115.  
  1116. local ClassList
  1117.  
  1118. do
  1119. local ClassPropertyExceptions = {
  1120. Whitelist = { TriangleMeshPart = ArrayToDict({ "CollisionFidelity" }) },
  1121. Blacklist = {
  1122. LuaSourceContainer = ArrayToDict({ "ScriptGuid" }),
  1123. Instance = ArrayToDict({ "UniqueId", "HistoryId" }),
  1124. },
  1125. }
  1126.  
  1127. local NotScriptableFixes = { --[[
  1128. For more info:
  1129. - https://github.com/luau/UniversalSynSaveInstance/blob/main/Tools/NotScriptable-Related/Potentially%20Missing%20Properties%20Dumper/Potentially%20Missing%20Properties%20Dumper.luau
  1130. - https://github.com/luau/UniversalSynSaveInstance/blob/main/Tools/NotScriptable-Related/NotScriptable%20Dumper/NotScriptable%20Dumper.py
  1131. ]]
  1132. Instance = {
  1133. AttributesSerialize = function(instance)
  1134. -- * There are certain restrictions for names of attributes
  1135. -- https://create.roblox.com/docs/reference/engine/classes/Instance#SetAttribute
  1136. -- But it seems like even if those are present, Studio still opens the file just fine
  1137. -- So there is no need to check for them currently
  1138.  
  1139. -- TODO: merge sequence Descriptors and some other descriptors where possible (check xml descriptors)
  1140. -- ? Return early for empty tags (this proved equally as fast when done using counter/next)
  1141.  
  1142. local attrs = instance:GetAttributes()
  1143.  
  1144. if not next(attrs) then
  1145. return ""
  1146. end
  1147.  
  1148. local attrs_n = 0
  1149. local buffer_size = 4
  1150. local attrs_sorted = {}
  1151. local attrs_formatted = table.clone(attrs)
  1152. for attr, val in attrs do
  1153. attrs_n += 1
  1154. attrs_sorted[attrs_n] = attr
  1155.  
  1156. local Type = typeof(val)
  1157.  
  1158. local Descriptor = Binary_Descriptors[Type]
  1159. local attr_size
  1160.  
  1161. attrs_formatted[attr], attr_size = Descriptor(val)
  1162.  
  1163. buffer_size += 5 + #attr + attr_size
  1164. end
  1165.  
  1166. table.sort(attrs_sorted)
  1167.  
  1168. local b = buffer.create(buffer_size)
  1169.  
  1170. local offset = 0
  1171.  
  1172. buffer.writeu32(b, offset, attrs_n)
  1173. offset += 4
  1174.  
  1175. local string__descriptor = Binary_Descriptors.string
  1176. for _, attr in attrs_sorted do
  1177. local b_Name, Name_size = string__descriptor(attr)
  1178.  
  1179. buffer.copy(b, offset, b_Name)
  1180. offset += Name_size
  1181.  
  1182. buffer.writeu8(b, offset, attr_Type_IDs[typeof(attrs[attr])])
  1183. offset += 1
  1184.  
  1185. local bb = attrs_formatted[attr]
  1186.  
  1187. buffer.copy(b, offset, bb)
  1188. offset += buffer.len(bb)
  1189. end
  1190.  
  1191. return buffer.tostring(b)
  1192. end,
  1193. DefinesCapabilities = "Sandboxed",
  1194. Tags = function(instance)
  1195. -- https://github.com/RobloxAPI/spec/blob/master/properties/Tags.md
  1196.  
  1197. local tags = instance:GetTags()
  1198.  
  1199. if #tags == 0 then
  1200. return ""
  1201. end
  1202.  
  1203. return table.concat(tags, "\0")
  1204. end,
  1205. },
  1206.  
  1207. -- DebuggerBreakpoint = {line="Line"}, -- ? This shouldn't appear in live games (try to prove this wrong)
  1208. BallSocketConstraint = { MaxFrictionTorqueXml = "MaxFrictionTorque" },
  1209. BasePart = {
  1210. Color3uint8 = "Color",
  1211. MaterialVariantSerialized = "MaterialVariant",
  1212. size = "Size",
  1213. },
  1214. DoubleConstrainedValue = { value = "Value" },
  1215. IntConstrainedValue = { value = "Value" },
  1216.  
  1217. -- CustomEvent = {PersistedCurrentValue=function(instance) -- * Class is Deprecated and :SetValue doesn't seem to affect GetCurrentValue anymore
  1218. -- local Receiver = instance:GetAttachedReceivers()[1]
  1219. -- if Receiver then
  1220. -- return Receiver:GetCurrentValue()
  1221. -- else
  1222. -- error("No Receiver", 2)
  1223. -- end
  1224. -- end},
  1225. Terrain = {
  1226. AcquisitionMethod = "LastUsedModificationMethod", -- ? Not sure
  1227. MaterialColors = function(instance) -- https://github.com/RobloxAPI/spec/blob/master/properties/MaterialColors.md
  1228. local TERRAIN_MATERIAL_COLORS =
  1229. { --https://github.com/rojo-rbx/rbx-dom/blob/master/rbx_dom_lua/src/customProperties.lua#L5
  1230. Enum.Material.Grass,
  1231. Enum.Material.Slate,
  1232. Enum.Material.Concrete,
  1233. Enum.Material.Brick,
  1234. Enum.Material.Sand,
  1235. Enum.Material.WoodPlanks,
  1236. Enum.Material.Rock,
  1237. Enum.Material.Glacier,
  1238. Enum.Material.Snow,
  1239. Enum.Material.Sandstone,
  1240. Enum.Material.Mud,
  1241. Enum.Material.Basalt,
  1242. Enum.Material.Ground,
  1243. Enum.Material.CrackedLava,
  1244. Enum.Material.Asphalt,
  1245. Enum.Material.Cobblestone,
  1246. Enum.Material.Ice,
  1247. Enum.Material.LeafyGrass,
  1248. Enum.Material.Salt,
  1249. Enum.Material.Limestone,
  1250. Enum.Material.Pavement,
  1251. }
  1252.  
  1253. local b = buffer.create(69) -- 69 bytes: 6 reserved + 63 for colors (21 materials * 3 components)
  1254. local offset = 6 -- 6 reserved bytes
  1255.  
  1256. local RGB_components = { "R", "G", "B" }
  1257.  
  1258. for _, material in TERRAIN_MATERIAL_COLORS do
  1259. local color = instance:GetMaterialColor(material)
  1260. for _, component in RGB_components do
  1261. buffer.writeu8(b, offset, math.floor(color[component] * 255)) -- ? math.floor seems unneeded but it makes it faster
  1262. offset += 1
  1263. end
  1264. end
  1265.  
  1266. return buffer.tostring(b)
  1267. end,
  1268. },
  1269. TriangleMeshPart = {
  1270. FluidFidelityInternal = "FluidFidelity",
  1271. },
  1272. MeshPart = { InitialSize = "MeshSize" },
  1273. PartOperation = { InitialSize = "MeshSize" },
  1274. Part = { shape = "Shape" },
  1275. TrussPart = { style = "Style" },
  1276. FormFactorPart = {
  1277. formFactorRaw = "FormFactor",
  1278. },
  1279. Fire = { heat_xml = "Heat", size_xml = "Size" },
  1280. Humanoid = { Health_XML = "Health" },
  1281. HumanoidDescription = {
  1282. EmotesDataInternal = function(instance)
  1283. local emotes_data = ""
  1284. for name, ids in instance:GetEmotes() do
  1285. emotes_data ..= name .. "^" .. table.concat(ids, "^") .. "^\\"
  1286. end
  1287. return emotes_data
  1288. end,
  1289. EquippedEmotesDataInternal = function(instance)
  1290. local equipped_emotes_data = ""
  1291. for _, emote in instance:GetEquippedEmotes() do
  1292. equipped_emotes_data ..= emote.Slot .. "^" .. emote.Name .. "\\"
  1293. end
  1294. return equipped_emotes_data
  1295. end,
  1296. },
  1297. LocalizationTable = {
  1298. Contents = function(instance)
  1299. return instance:GetContents() --service.HttpService:JSONEncode(instance:GetEntries())
  1300. end,
  1301. },
  1302. MaterialService = { Use2022MaterialsXml = "Use2022Materials" },
  1303.  
  1304. Model = {
  1305. ScaleFactor = function(instance)
  1306. return instance:GetScale()
  1307. end,
  1308. WorldPivotData = "WorldPivot", -- TODO This doesn't accurately represent whether optional type property is present or not (it's never nil), gethiddenproperty or gethiddenproperty_fallback is preferred
  1309. },
  1310. PackageLink = { PackageIdSerialize = "PackageId", VersionIdSerialize = "VersionNumber" },
  1311. Players = { MaxPlayersInternal = "MaxPlayers", PreferredPlayersInternal = "PreferredPlayers" }, -- ? Only needed for execs that lack LocalUserSecurity (Level 2, 5, 9), even so, it's a pretty useless information as it can be viewed elsewhere
  1312.  
  1313. StarterPlayer = { AvatarJointUpgrade_Serialized = "AvatarJointUpgrade" },
  1314. Smoke = { size_xml = "Size", opacity_xml = "Opacity", riseVelocity_xml = "RiseVelocity" },
  1315. Sound = {
  1316. xmlRead_MaxDistance_3 = "RollOffMaxDistance", -- * Also MaxDistance
  1317. },
  1318. -- ViewportFrame = { -- * Pointless because these reflect CurrentCamera's properties
  1319. -- CameraCFrame = function(instance) -- *
  1320. -- local CurrentCamera = instance.CurrentCamera
  1321. -- if CurrentCamera then
  1322. -- return CurrentCamera.CFrame
  1323. -- else
  1324. -- error("No CurrentCamera", 2)
  1325. -- end
  1326. -- end,
  1327. -- -- CameraFieldOfView =
  1328. -- },
  1329. WeldConstraint = {
  1330. Part0Internal = "Part0",
  1331. Part1Internal = "Part1",
  1332. -- State = function(instance)
  1333. -- -- If untouched then default state is 3 (default true)
  1334. -- return instance.Enabled and 1 or 0
  1335. -- end,
  1336. },
  1337. Workspace = {
  1338. -- SignalBehavior2 = "SignalBehavior", -- * Both are NotScriptable so it doesn't make sense to keep
  1339. CollisionGroupData = function()
  1340. local collision_groups = game:GetService("PhysicsService"):GetRegisteredCollisionGroups()
  1341.  
  1342. local col_groups_n = #collision_groups
  1343.  
  1344. if col_groups_n == 0 then
  1345. return "\1\0"
  1346. end
  1347.  
  1348. local buffer_size = 2 -- Initial size
  1349.  
  1350. for _, group in collision_groups do
  1351. buffer_size += 7 + #group.name
  1352. end
  1353.  
  1354. local b = buffer.create(buffer_size)
  1355.  
  1356. local offset = 0
  1357.  
  1358. buffer.writeu8(b, offset, 1) -- ? [CONSTANT] Version byte (likely)
  1359. offset += 1
  1360. buffer.writeu8(b, offset, col_groups_n) -- Group count
  1361. offset += 1
  1362.  
  1363. for i, group in collision_groups do
  1364. local name, id, mask = group.name, i - 1, group.mask
  1365. local name_len = #name
  1366.  
  1367. buffer.writeu8(b, offset, id) -- ID
  1368. offset += 1
  1369.  
  1370. buffer.writeu8(b, offset, 4) -- ? [CONSTANT] Not sure what this is (also not sure about u8, could be i8)
  1371. offset += 1
  1372.  
  1373. buffer.writei32(b, offset, mask) -- Mask value as signed 32-bit integer
  1374. offset += 4
  1375.  
  1376. buffer.writeu8(b, offset, name_len) -- Name length
  1377. offset += 1
  1378. buffer.writestring(b, offset, name) -- Name
  1379. offset += name_len
  1380. end
  1381.  
  1382. return buffer.tostring(b)
  1383. end,
  1384. },
  1385. }
  1386.  
  1387. local function FetchAPI()
  1388. -- Credits @MaximumADHD
  1389.  
  1390. local API_Dump
  1391.  
  1392. local ok, err = pcall(function()
  1393. if EXECUTOR_NAME == "Zenith" or EXECUTOR_NAME == "Velocity" then -- TODO Temp fix as it crashes on HttpGet for sites that return 403 code
  1394. return
  1395. end
  1396.  
  1397. local CLIENT_VERSION_str = tostring(CLIENT_VERSION)
  1398. local ok, result = pcall(readfile, CLIENT_VERSION_str)
  1399. if
  1400. ok
  1401. and result
  1402. and result ~= ""
  1403. and pcall(service.HttpService.JSONDecode, service.HttpService, result)
  1404. then
  1405. API_Dump = result
  1406. return
  1407. end
  1408.  
  1409. local matching_versions, is_matched = {}
  1410.  
  1411. -- * https://setup.rbxcdn.com/versionQTStudio seems to be a bit behind DeployHistory.txt
  1412. local DeployHistory = string.split(game:HttpGet("https://setup.rbxcdn.com/DeployHistory.txt", true), "\n")
  1413. for i = #DeployHistory, 1, -1 do
  1414. local line = DeployHistory[i]
  1415.  
  1416. local file_version = string.match(line, "file version: ([%d, ]+)")
  1417. if file_version then
  1418. if string.split(file_version, ", ")[2] == CLIENT_VERSION_str then
  1419. is_matched = true
  1420.  
  1421. local version_hash = string.match(line, "(version%-[^%s]+)")
  1422. if version_hash then
  1423. matching_versions[version_hash] = true
  1424. end
  1425. elseif is_matched then
  1426. break
  1427. end
  1428. end
  1429. end
  1430.  
  1431. for version_hash in matching_versions do
  1432. ok, result = pcall(
  1433. game.HttpGet,
  1434. game,
  1435. "https://setup.rbxcdn.com/" .. version_hash .. "-Full-API-Dump.json",
  1436. true
  1437. )
  1438. if ok then
  1439. local o, r = pcall(service.HttpService.JSONDecode, service.HttpService, result)
  1440. if o then
  1441. API_Dump = service.HttpService:JSONEncode(r.Classes) -- minify it
  1442. break
  1443. end
  1444. end
  1445. end
  1446.  
  1447. writefile(CLIENT_VERSION_str, API_Dump)
  1448. end)
  1449.  
  1450. if not ok or not API_Dump then
  1451. warn("[DEBUG] Failed to get " .. version() .. " API Dump, trying latest..")
  1452. warn("[DEBUG]", err)
  1453. API_Dump = service.HttpService:JSONEncode(
  1454. service.HttpService:JSONDecode(
  1455. game:HttpGet(
  1456. "https://raw.githubusercontent.com/MaximumADHD/Roblox-Client-Tracker/roblox/Mini-API-Dump.json",
  1457. true
  1458. )
  1459. ).Classes
  1460. )
  1461. end
  1462.  
  1463. local classList = {}
  1464.  
  1465. local ClassesWhitelist, ClassesBlacklist = ClassPropertyExceptions.Whitelist, ClassPropertyExceptions.Blacklist
  1466.  
  1467. for _, API_Class in service.HttpService:JSONDecode(API_Dump) do
  1468. local ClassProperties, ClassProperties_size = {}, 1
  1469. local Class = {
  1470. Properties = ClassProperties,
  1471. Superclass = API_Class.Superclass,
  1472. }
  1473.  
  1474. local ClassTags = API_Class.Tags
  1475. local ClassName = API_Class.Name
  1476.  
  1477. if ClassTags then
  1478. Class.Tags = ArrayToDict(ClassTags, nil, nil, "string") -- or {}
  1479. end
  1480.  
  1481. local NotScriptableFixClass = NotScriptableFixes[ClassName]
  1482.  
  1483. -- ? Check 96ea8b2a755e55a78aedb55a7de7e83980e11077 commit - If a NotScriptableFix is needed that relies on another NotScriptable Property (which doesn't really make sense in the first place)
  1484.  
  1485. local ClassWhitelist, ClassBlacklist = ClassesWhitelist[ClassName], ClassesBlacklist[ClassName]
  1486.  
  1487. for _, Member in API_Class.Members do
  1488. -- ? print(game:GetService("ReflectionService"):GetPropertyNames("TextBox"))
  1489. if Member.MemberType == "Property" then
  1490. local Serialization = Member.Serialization
  1491.  
  1492. if Serialization.CanLoad then -- If Roblox doesn't save it why should we; If Roblox doesn't load it we don't need to save it
  1493. --[[
  1494. -- ! CanSave replaces "Tags.Deprecated" check because there are some old properties which are deprecated yet have CanSave.
  1495. Example: Humanoid.Health is CanSave false due to Humanoid.Health_XML being CanSave true (obsolete properties basically) - in this case both of them will Load. (aka PropertyPatches)
  1496. CanSave being on same level as CanLoad also fixes potential issues with overlapping properties like Color, Color3 & Color3uint8 of BasePart, out of which only Color3uint8 should save
  1497. This also fixes everything in IgnoreClassProperties automatically without need to hardcode :)
  1498. A very simple fix for many problems that saveinstance scripts encounter!
  1499. --]]
  1500. local PropertyName = Member.Name
  1501. if
  1502. (Serialization.CanSave or ClassWhitelist and ClassWhitelist[PropertyName])
  1503. and not (ClassBlacklist and ClassBlacklist[PropertyName])
  1504. then
  1505. local MemberTags = Member.Tags
  1506.  
  1507. local ValueType = Member.ValueType
  1508. local ValueType_Name = ValueType.Name
  1509.  
  1510. if 645 <= CLIENT_VERSION and ValueType_Name == "Content" then -- TODO: Remove after Roblox adds a descriptor for it
  1511. continue
  1512. end
  1513.  
  1514. local Special, PreferredDescriptorName
  1515.  
  1516. if MemberTags then
  1517. for _, tag in MemberTags do
  1518. if type(tag) == "table" then
  1519. PreferredDescriptorName = tag.PreferredDescriptorName
  1520. if PreferredDescriptorName and Special then
  1521. break
  1522. end
  1523. elseif tag == "NotScriptable" then
  1524. Special = true
  1525. if PreferredDescriptorName then
  1526. break
  1527. end
  1528. end
  1529. end
  1530. end
  1531.  
  1532. -- if not Special then
  1533. local Property = {
  1534. Name = PropertyName,
  1535. Category = ValueType.Category,
  1536. -- Default = Member.Default,
  1537. -- Tags = MemberTags,
  1538. ValueType = ValueType_Name,
  1539.  
  1540. Special = Special,
  1541.  
  1542. CanRead = nil,
  1543. }
  1544.  
  1545. if string.sub(ValueType_Name, 1, 8) == "Optional" then
  1546. -- Extract the string after "Optional"
  1547. Property.Optional = string.sub(ValueType_Name, 9)
  1548. end
  1549.  
  1550. if NotScriptableFixClass then
  1551. local NotScriptableFix = NotScriptableFixClass[PropertyName]
  1552. if NotScriptableFix then
  1553. Property.Fallback = type(NotScriptableFix) == "function" and NotScriptableFix
  1554. or PreferredDescriptorName and function(instance)
  1555. local o, r = pcall(index, instance, PreferredDescriptorName)
  1556. if o then
  1557. return r
  1558. end
  1559. return instance[NotScriptableFix]
  1560. end
  1561. or function(instance)
  1562. return instance[NotScriptableFix]
  1563. end
  1564. end
  1565. elseif PreferredDescriptorName then
  1566. Property.Fallback = function(instance)
  1567. return instance[PreferredDescriptorName]
  1568. end
  1569. end
  1570. ClassProperties[ClassProperties_size] = Property
  1571. ClassProperties_size += 1
  1572.  
  1573. -- end
  1574. end
  1575. end
  1576. end
  1577. end
  1578.  
  1579. classList[ClassName] = Class
  1580. end
  1581.  
  1582. -- classList.Instance.Properties.Parent = nil -- ? Not sure if this is a better option than filtering through properties to remove this
  1583.  
  1584. return classList
  1585. end
  1586.  
  1587. local ok, result = pcall(FetchAPI)
  1588. if ok then
  1589. ClassList = result
  1590. else
  1591. warn("Failed to load the API Dump")
  1592. warn(result)
  1593. return
  1594. end
  1595. end
  1596.  
  1597. local GLOBAL_ENV = getgenv and getgenv() or _G or shared
  1598.  
  1599. --[=[
  1600. @class SynSaveInstance
  1601. Represents the options for saving instances with custom settings using the synsaveinstance function.
  1602. ]=]
  1603.  
  1604. --- @interface CustomOptions table
  1605. --- * Structure of the main CustomOptions table.
  1606. --- * Note: Options are case-insensitive, meaning you could type `NilInstances` option as `nilInStaNces` and it would still be valid.
  1607. --- @within SynSaveInstance
  1608. --- @field __DEBUG_MODE boolean -- This will print debug logs to console about unusual scenarios. Recommended to enable if you wish to help us improve our products and find bugs / issues with it! ___Default:___ false
  1609. --- @field ReadMe boolean --___Default:___ true
  1610. --- @field SafeMode boolean -- Kicks you before Saving, which prevents you from being detected in any game. ___Default:___ false
  1611. --- @field ShutdownWhenDone boolean -- Shuts the game down after saveinstance is finished. ___Default:___ false
  1612. --- @field AntiIdle boolean -- Prevents the 20-minute-Idle Kick. ___Default:___ true
  1613. --- Anonymous {boolean|table{UserId = string, Name = string}} -- * **RISKY:** Cleans the file of any info related to your account like: Name, UserId. This is useful for some games that might store that info in GUIs or other Instances. Might potentially mess up parts of strings that contain characters that match your Name or parts of numbers that match your UserId. Can also be a table with UserId & Name keys. ___Default:___ false
  1614. --- @field ShowStatus boolean -- ___Default:___ true
  1615. --- @field Callback function -- If set, the serialized data will be sent to the callback function instead of to file. ___Default:___ false
  1616. --- @field mode string -- Valid modes: full, optimized, scripts. Change this to invalid mode like "invalid" if you only want ExtraInstances. "optimized" mode is **NOT** supported with *@Object* option. ___Default:___ `"optimized"`
  1617. --- @field noscripts boolean -- ___Aliases:___ `Decompile`. ___Default:___ false
  1618. --- @field scriptcache boolean -- ___Default:___ true
  1619. --- @field decomptype string -- * Deprecated. ___Default:___ Uses your executor's decompiler, if available.
  1620. --- @field timeout number -- If the decompilation run time exceeds this value it gets cancelled. Set to -1 to disable timeout (unreliable). ***Aliases***: `DecompileTimeout`. ___Default:___ 10
  1621. --- @field DecompileJobless boolean -- Includes already decompiled code in the output. No new scripts are decompiled. ___Default:___ false
  1622. --- @field SaveBytecode boolean -- Includes bytecode in the output. Useful if you wish to be able to decompile it yourself later. ___Default:___ false
  1623. --- .DecompileIgnore {Instance | Instance.ClassName | [Instance.ClassName] = {Instance.Name}} -- * Ignores match & it's descendants by default. To Ignore only the instance itself set the value to `= false`. Examples: "Chat", - Matches any instance with "Chat" ClassName, Players = {"MyPlayerName"} - Matches "Players" Class AND "MyPlayerName" Name ONLY, `workspace` - matches Instance by reference, `[workspace] = false` - matches Instance by reference and only ignores the instance itself and not it's descendants. ___Default:___ {TextChatService}
  1624. --- .IgnoreList {Instance | Instance.ClassName | [Instance.ClassName] = {Instance.Name}} -- Structure is similar to **@DecompileIgnore** except `= false` meaning if you ignore one instance it will automatically ignore it's descendants. ___Default:___ {CoreGui, CorePackages}
  1625. --- .ExtraInstances {Instance} -- If used with any invalid mode (like "invalidmode") it will only save these instances. ___Default:___ {}
  1626. --- @field IgnoreProperties table -- Ignores properties by Name. ___Default:___ {}
  1627. --- @field SaveCacheInterval number -- The less the value the more often it saves, but that would mean less performance due to constantly saving. ___Default:___ 0x1600 * 10
  1628. --- @field FilePath string -- Must only contain the name of the file, no file extension. ___Default:___ false
  1629. --- @field Object Instance -- * If provided, saves as .rbxmx (Model file) instead. If Object is game, it will be saved as a .rbxl file. **MUST BE AN INSTANCE REFERENCE, FOR EXAMPLE - *game.Workspace***. `"optimized"` mode is **NOT** supported with this option. If IsModel is set to false then Object specified here will be saved as a place file. ___Default:___ false
  1630. --- @field IsModel boolean -- If Object is specified then sets to true automatically, unless you set it to false. ___Default:___ false
  1631. --- @field NilInstances boolean -- Save instances that aren't Parented (Parented to nil). ___Default:___ false
  1632. --- .NilInstancesFixes {[Instance.ClassName] = function} -- * This can cause some Classes to be fixed even though they might not need the fix (better be safe than sorry though). For example, Bones inherit from Attachment if we dont define them in the NilInstancesFixes then this will catch them anyways. **TO AVOID THIS BEHAVIOR USE THIS EXAMPLE:** {ClassName_That_Doesnt_Need_Fix = false}. ___Default:___ {Animator = function, AdPortal = function, BaseWrap = function, Attachment = function}
  1633. --- @field IgnoreDefaultProperties boolean -- Ignores default properties during saving. ___Default:___ true
  1634. --- @field IgnoreNotArchivable boolean -- Ignores the Archivable property and saves Non-Archivable instances. ___Default:___ true
  1635. --- @field IgnorePropertiesOfNotScriptsOnScriptsMode boolean -- Ignores property of every instance that is not a script in "scripts" mode. ___Default:___ false
  1636. --- @field IgnoreSpecialProperties boolean -- Prevents calls to `gethiddenproperty` and uses fallback methods instead. This also helps with crashes. If your file is corrupted after saving, you can try turning this on. ___Default:___ false
  1637. --- @field IsolateLocalPlayer boolean -- Saves Children of LocalPlayer as separate folder and prevents any instance of ClassName Player with .Name identical to LocalPlayer.Name from saving. ___Default:___ false
  1638. --- @field IsolateStarterPlayer boolean -- If enabled, StarterPlayer will be cleared and the saved starter player will be placed into folders. ___Default:___ false
  1639. --- @field IsolateLocalPlayerCharacter boolean -- Saves Children of LocalPlayer.Character as separate folder and prevents any instance of ClassName Player with .Name identical to LocalPlayer.Name from saving. ___Default:___ false
  1640. --- @field RemovePlayerCharacters boolean -- Ignore player characters while saving. (Enables SaveNotCreatable automatically). ___Default:___ true
  1641. --- @field SaveNotCreatable boolean -- * Includes non-serializable instances as Folder objects (Name is misleading as this is mostly a fix for certain NilInstances and isn't always related to NotCreatable). ___Default:___ false
  1642. --- .NotCreatableFixes table<Instance.ClassName> -- * {"Player"} is the same as {Player = "Folder"}; Format like {SpawnLocation = "Part"} is only to be used when SpawnLocation inherits from "Part" AND "Part" is Creatable. ___Default:___ { "", "Player", "PlayerScripts", "PlayerGui", "TouchTransmitter" }
  1643. --- @field IsolatePlayers boolean -- * This option does save players, it's just they won't show up in Studio and can only be viewed through the place file code (in text editor). More info at https://github.com/luau/UniversalSynSaveInstance/issues/2. ___Default:___ false
  1644. --- @field AlternativeWritefile boolean -- * Splits file content string into segments and writes them using appendfile. This might help with crashes when it starts writing to file. Though there is a risk of appendfile working incorrectly on some executors. ___Default:___ true
  1645. --- @field IgnoreDefaultPlayerScripts boolean -- * **RISKY: Ignores Default PlayerScripts like PlayerModule & RbxCharacterSounds. Prevents crashes on certain Executors. ___Default:___ true
  1646. --- @field IgnoreSharedStrings boolean -- * **RISKY: FIXES CRASHES (TEMPORARY, TESTED ON ROEXEC ONLY). FEEL FREE TO DISABLE THIS TO SEE IF IT WORKS FOR YOU**. ___Default:___ true
  1647. --- @field SharedStringOverwrite boolean -- * **RISKY:** if the process is not finished aka crashed then none of the affected values will be available. SharedStrings can also be used for ValueTypes that aren't `SharedString`, this behavior is not documented anywhere but makes sense (Could create issues though, due to _potential_ ValueType mix-up, only works on certain types which are all base64 encoded so far). Reason: Allows for potential smaller file size (can also be bigger in some cases). ___Default:___ false
  1648. --- @field TreatUnionsAsParts boolean -- * **RISKY:** Converts all UnionOperations to Parts. Useful if your Executor isn't able to save (read) Unions, because otherwise they will be invisible. ___Default:___ false (except Solara)
  1649.  
  1650. --- @interface OptionsAliases
  1651. --- @within SynSaveInstance
  1652. --- Aliases for the [SynSaveInstance.CustomOptions table].
  1653. --- @field FilePath string -- FileName
  1654. --- @field IgnoreDefaultProperties string -- IgnoreDefaultProps
  1655. --- @field SaveNotCreatable string -- SaveNonCreatable
  1656. --- @field IsolatePlayers string -- SavePlayers
  1657. --- @field scriptcache string -- DecompileJobless
  1658. --- @field timeout string -- DecompileTimeout
  1659. --- @field IgnoreNotArchivable string -- IgnoreArchivable
  1660. --- @field RemovePlayerCharacters string -- INVERSE SavePlayerCharacters
  1661.  
  1662. --[=[
  1663. @function saveinstance
  1664. Saves instances with specified options. Example:
  1665. ```lua
  1666. local Params = {
  1667. RepoURL = "https://raw.githubusercontent.com/luau/SynSaveInstance/main/",
  1668. SSI = "saveinstance",
  1669. }
  1670.  
  1671. local synsaveinstance = loadstring(game:HttpGet(Params.RepoURL .. Params.SSI .. ".luau", true), Params.SSI)()
  1672.  
  1673. local CustomOptions = { SafeMode = true, timeout = 15, SaveBytecode = true }
  1674.  
  1675. synsaveinstance(CustomOptions)
  1676. ```
  1677. @within SynSaveInstance
  1678. @yields
  1679. @param Parameter_1 variant<table, table<Instance>> -- Can either be [SynSaveInstance.CustomOptions table] or a filled with instances ({Instance}), (then it will be treated as ExtraInstances with an invalid mode and IsModel will be true).
  1680. @param Parameter_2 table -- [OPTIONAL] If present, then Parameter_2 will be assumed to be [SynSaveInstance.CustomOptions table]. And then if the Parameter_1 is an Instance, then it will be assumed to be [SynSaveInstance.CustomOptions table].Object. If Parameter_1 is a table filled with instances ({Instance}), then it will be assumed to be [SynSaveInstance.CustomOptions table].ExtraInstances and IsModel will be true). This exists for sake compatibility with `saveinstance(game, {})`
  1681. ]=]
  1682.  
  1683. local function synsaveinstance(CustomOptions, CustomOptions2)
  1684. if GLOBAL_ENV.USSI then
  1685. return
  1686. end
  1687. GLOBAL_ENV.USSI = true
  1688. do
  1689. local setthreadidentity = global_container.setthreadidentity
  1690. if setthreadidentity then
  1691. pcall(setthreadidentity, 8) -- ? Arceus X Fix
  1692. end
  1693. end
  1694.  
  1695. local currentstr, currentsize, totalsize, chunks = "", 0, 0, table.create(1)
  1696. local savebuffer, savebuffer_size = {}, 1
  1697. local header =
  1698. '<!-- Saved by UniversalSynSaveInstance (Join to Copy Games) https://discord.gg/wx4ThpAsmw --><roblox version="4">'
  1699.  
  1700. local StatusText
  1701.  
  1702. local OPTIONS = {
  1703. mode = "optimized",
  1704. noscripts = false,
  1705. scriptcache = true,
  1706. -- decomptype = "",
  1707. timeout = 10,
  1708. -- * New:
  1709. __DEBUG_MODE = false,
  1710.  
  1711. -- Binary = false, -- true in syn newer versions (false in our case because no binary support yet), Description: Saves everything in Binary Mode (rbxl/rbxm).
  1712. Callback = false,
  1713. --Clipboard/CopyToClipboard = false, -- Description: If set to true, the serialized data will be set to the clipboard, which can be later pasted into studio easily. Useful for saving models.
  1714. -- MaxThreads = 3 -- Description: The number of decompilation threads that can run at once. More threads means it can decompile for scripts at a time.
  1715. -- DisableCompression = false, --Description: Disables compression in the binary output
  1716.  
  1717. DecompileJobless = false,
  1718. DecompileIgnore = { -- * Clean these up (merged Old Syn and New Syn)
  1719. -- "Chat",
  1720. "TextChatService",
  1721. ModuleScript = nil,
  1722. },
  1723. IgnoreDefaultPlayerScripts = EXECUTOR_NAME ~= "Wave" and true,
  1724. SaveBytecode = false,
  1725.  
  1726. IgnoreProperties = {},
  1727.  
  1728. IgnoreList = { "CoreGui", "CorePackages" },
  1729.  
  1730. ExtraInstances = {},
  1731. NilInstances = false,
  1732. NilInstancesFixes = {},
  1733.  
  1734. SaveCacheInterval = 0x1600 * 10,
  1735. ShowStatus = true,
  1736. SafeMode = false,
  1737. ShutdownWhenDone = false,
  1738. AntiIdle = true,
  1739. Anonymous = false,
  1740. ReadMe = true,
  1741. FilePath = false,
  1742. Object = false,
  1743. IsModel = false,
  1744.  
  1745. IgnoreDefaultProperties = true,
  1746. IgnoreNotArchivable = true,
  1747. IgnorePropertiesOfNotScriptsOnScriptsMode = false,
  1748. IgnoreSpecialProperties = ArrayToDict({ "Fluxus", "Delta", "Solara" })[EXECUTOR_NAME] or false, -- ! Please submit more Executors that crash on gethiddenproperty (with this disabled basically)
  1749.  
  1750. IsolateLocalPlayer = false, -- #service.StarterGui:GetChildren() == 0
  1751. IsolateLocalPlayerCharacter = false,
  1752. IsolatePlayers = false,
  1753. IsolateStarterPlayer = false,
  1754. RemovePlayerCharacters = true,
  1755.  
  1756. SaveNotCreatable = false,
  1757. NotCreatableFixes = {
  1758. -- "CloudLocalizationTable",
  1759. -- "InputObject",
  1760. -- "LodDataEntity",
  1761. -- "Path",
  1762. -- "Translator",
  1763. "", -- * FilteredSelection
  1764. "AnimationTrack",
  1765. "Player",
  1766. "PlayerGui",
  1767. "PlayerScripts",
  1768. "PlayerMouse",
  1769. "ScreenshotHud",
  1770. "StudioData",
  1771. "TextSource",
  1772. "TouchTransmitter",
  1773. },
  1774.  
  1775. -- ! Risky
  1776.  
  1777. IgnoreSharedStrings = EXECUTOR_NAME ~= "Wave" and true,
  1778. SharedStringOverwrite = false,
  1779. TreatUnionsAsParts = EXECUTOR_NAME == "Solara", -- TODO Temporary true (once removed, remove Note from docs too)
  1780. AlternativeWritefile = not ArrayToDict({ "WRD", "Xeno", "Zorara" })[EXECUTOR_NAME],
  1781.  
  1782. OptionsAliases = { -- You can't really modify these as a user
  1783. DecompileTimeout = "timeout",
  1784. FileName = "FilePath",
  1785. IgnoreArchivable = "IgnoreNotArchivable",
  1786. IgnoreDefaultProps = "IgnoreDefaultProperties",
  1787. SaveNonCreatable = "SaveNotCreatable",
  1788. SavePlayers = "IsolatePlayers",
  1789. },
  1790. }
  1791.  
  1792. local OPTIONS_lowercase, CustomOptions_valid = {}, {}
  1793. for option_name in OPTIONS do
  1794. local option_name_lowercase = string.lower(option_name)
  1795. if OPTIONS_lowercase[option_name_lowercase] then
  1796. warn("DUPLICATE OPTION", option_name)
  1797. else
  1798. OPTIONS_lowercase[option_name_lowercase] = option_name
  1799. end
  1800. end
  1801. for option_alias, option_name in OPTIONS.OptionsAliases do
  1802. local option_name_lowercase = string.lower(option_alias)
  1803. if OPTIONS_lowercase[option_name_lowercase] then
  1804. warn("DUPLICATE ALIAS", option_alias)
  1805. else
  1806. OPTIONS_lowercase[option_name_lowercase] = option_name
  1807. end
  1808. end
  1809.  
  1810. do -- * Load Settings
  1811. local function construct_NilinstanceFix(Name, ClassName, Separate)
  1812. return function(instance, instancePropertyOverrides)
  1813. local Exists
  1814.  
  1815. if not Separate then
  1816. Exists = OPTIONS.NilInstancesFixes[Name]
  1817. end
  1818.  
  1819. local Fix
  1820.  
  1821. local DoesntExist = not Exists
  1822. if DoesntExist then
  1823. Fix = Instance.new(ClassName)
  1824. if not Separate then
  1825. OPTIONS.NilInstancesFixes[Name] = Fix
  1826. end
  1827. -- Fix.Name = Name
  1828.  
  1829. instancePropertyOverrides[Fix] =
  1830. { __SaveSpecific = true, __Children = { instance }, Properties = { Name = Name } }
  1831. else
  1832. Fix = Exists
  1833. table.insert(instancePropertyOverrides[Fix].__Children, instance)
  1834. end
  1835.  
  1836. -- InstancesOverrides[instance].Parent = AnimationController
  1837. if DoesntExist then
  1838. return Fix
  1839. end
  1840. end
  1841. end
  1842.  
  1843. -- TODO: Merge BaseWrap & Attachment & AdPortal fix (put all under MeshPart container)
  1844. -- TODO?:
  1845. -- DebuggerWatch DebuggerWatch must be a child of ScriptDebugger
  1846. -- PluginAction Parent of PluginAction must be Plugin or PluginMenu that created it!
  1847. OPTIONS.NilInstancesFixes.Animator = construct_NilinstanceFix(
  1848. "Animator has to be placed under Humanoid or AnimationController",
  1849. "AnimationController"
  1850. )
  1851. OPTIONS.NilInstancesFixes.AdPortal = construct_NilinstanceFix("AdPortal must be parented to a Part", "Part")
  1852. OPTIONS.NilInstancesFixes.Attachment =
  1853. construct_NilinstanceFix("Attachments must be parented to a BasePart or another Attachment", "Part") -- * Bones inherit from Attachments
  1854. OPTIONS.NilInstancesFixes.BaseWrap =
  1855. construct_NilinstanceFix("BaseWrap must be parented to a MeshPart", "MeshPart")
  1856. OPTIONS.NilInstancesFixes.PackageLink =
  1857. construct_NilinstanceFix("Package already has a PackageLink", "Folder", true)
  1858.  
  1859. if CustomOptions2 and type(CustomOptions2) == "table" then
  1860. local tmp = CustomOptions
  1861. local Type = typeof(tmp)
  1862. CustomOptions = CustomOptions2
  1863. if Type == "Instance" then
  1864. CustomOptions.Object = tmp
  1865. elseif Type == "table" and typeof(tmp[1]) == "Instance" then
  1866. CustomOptions.ExtraInstances = tmp
  1867. OPTIONS.IsModel = true
  1868. end
  1869. end
  1870.  
  1871. local Type = typeof(CustomOptions)
  1872.  
  1873. if Type == "table" then
  1874. if typeof(CustomOptions[1]) == "Instance" then
  1875. OPTIONS.mode = "invalidmode"
  1876. OPTIONS.ExtraInstances = CustomOptions
  1877. OPTIONS.IsModel = true
  1878. CustomOptions = {}
  1879. else
  1880. for key, value in CustomOptions do
  1881. local option = OPTIONS_lowercase[string.lower(key)]
  1882.  
  1883. if option then
  1884. OPTIONS[option] = value
  1885. CustomOptions_valid[option] = true
  1886. end
  1887. end
  1888. local Decompile = CustomOptions.Decompile
  1889. if Decompile ~= nil then
  1890. OPTIONS.noscripts = not Decompile
  1891. end
  1892. local SavePlayerCharacters = CustomOptions.SavePlayerCharacters
  1893. if SavePlayerCharacters ~= nil then
  1894. OPTIONS.RemovePlayerCharacters = not SavePlayerCharacters
  1895. end
  1896. local RemovePlayers = CustomOptions.RemovePlayers
  1897. if RemovePlayers ~= nil then
  1898. OPTIONS.IsolatePlayers = not RemovePlayers
  1899. end
  1900. end
  1901. elseif Type == "Instance" then
  1902. OPTIONS.mode = "invalidmode"
  1903. OPTIONS.Object = CustomOptions
  1904. CustomOptions = {}
  1905. else
  1906. CustomOptions = {}
  1907. end
  1908. end
  1909.  
  1910. if OPTIONS.IgnoreDefaultPlayerScripts then
  1911. -- TODO This is a bad workaround, find a better automatic way
  1912. -- TODO Look into https://robloxapi.github.io/ref/class/LuaSourceContainer.html#member-isPlayerScript
  1913. local DecompileIgnore = OPTIONS.DecompileIgnore
  1914.  
  1915. local Path = service.StarterPlayer:FindFirstChild("StarterPlayerScripts")
  1916. local Exclude = { ModuleScript = { "PlayerModule" }, LocalScript = { "RbxCharacterSounds" } }
  1917. if Path then
  1918. for _, className in Exclude do
  1919. for _, name in className do
  1920. local Found = Path:FindFirstChild(name)
  1921. if Found then
  1922. table.insert(DecompileIgnore, Found)
  1923. end
  1924. end
  1925. end
  1926. end
  1927. end
  1928.  
  1929. local InstancesOverrides = {}
  1930.  
  1931. local DecompileIgnore, IgnoreList, IgnoreProperties, NotCreatableFixes =
  1932. ArrayToDict(OPTIONS.DecompileIgnore, true),
  1933. ArrayToDict(OPTIONS.IgnoreList, true),
  1934. ArrayToDict(OPTIONS.IgnoreProperties),
  1935. ArrayToDict(OPTIONS.NotCreatableFixes, true, "Folder")
  1936.  
  1937. local __DEBUG_MODE = OPTIONS.__DEBUG_MODE
  1938.  
  1939. if __DEBUG_MODE and type(__DEBUG_MODE) ~= "function" then
  1940. __DEBUG_MODE = warn
  1941. end
  1942.  
  1943. local FilePath = OPTIONS.FilePath
  1944. local SaveCacheInterval = OPTIONS.SaveCacheInterval
  1945. local ToSaveInstance = OPTIONS.Object
  1946. local IsModel = OPTIONS.IsModel
  1947.  
  1948. if ToSaveInstance and CustomOptions.IsModel == nil then
  1949. IsModel = true
  1950. end
  1951.  
  1952. local IgnoreDefaultProperties = OPTIONS.IgnoreDefaultProperties
  1953. local IgnoreNotArchivable = not OPTIONS.IgnoreNotArchivable
  1954. local IgnorePropertiesOfNotScriptsOnScriptsMode = OPTIONS.IgnorePropertiesOfNotScriptsOnScriptsMode
  1955.  
  1956. local old_gethiddenproperty
  1957. if OPTIONS and gethiddenproperty then
  1958. old_gethiddenproperty = gethiddenproperty
  1959. gethiddenproperty = nil
  1960. end
  1961.  
  1962. local SaveNotCreatable = OPTIONS.SaveNotCreatable
  1963. local TreatUnionsAsParts = OPTIONS.TreatUnionsAsParts
  1964.  
  1965. local DecompileJobless = OPTIONS.DecompileJobless
  1966. if DecompileJobless then
  1967. OPTIONS.scriptcache = true
  1968. end
  1969. local ScriptCache = OPTIONS.scriptcache and getscriptbytecode
  1970.  
  1971. local Timeout = OPTIONS.timeout
  1972.  
  1973. local IgnoreSharedStrings = OPTIONS.IgnoreSharedStrings
  1974. local SharedStringOverwrite = OPTIONS.SharedStringOverwrite
  1975.  
  1976. local ldeccache = GLOBAL_ENV.scriptcache
  1977.  
  1978. local DecompileIgnoring, ToSaveList, ldecompile, placename, elapse_t, SaveNotCreatableWillBeEnabled, RecoveredScripts
  1979.  
  1980. if OPTIONS.ReadMe then
  1981. RecoveredScripts = {}
  1982. end
  1983.  
  1984. if ScriptCache and not ldeccache then
  1985. ldeccache = {}
  1986. GLOBAL_ENV.scriptcache = ldeccache
  1987. end
  1988.  
  1989. if ToSaveInstance == game then
  1990. OPTIONS.mode = "full"
  1991. ToSaveInstance = nil
  1992. IsModel = nil
  1993. end
  1994.  
  1995. local function isLuaSourceContainer(instance)
  1996. return instance:IsA("LuaSourceContainer")
  1997. end
  1998.  
  1999. do
  2000. local mode = string.lower(OPTIONS.mode)
  2001. local tmp = table.clone(OPTIONS.ExtraInstances)
  2002.  
  2003. local PlaceName = game.PlaceId
  2004.  
  2005. pcall(function()
  2006. PlaceName ..= " " .. service.MarketplaceService:GetProductInfo(PlaceName).Name
  2007. end)
  2008.  
  2009. local function sanitizeFileName(str)
  2010. return string.sub(string.gsub(string.gsub(string.gsub(str, "[^%w _]", ""), " +", " "), " +$", ""), 1, 240)
  2011. end
  2012.  
  2013. if ToSaveInstance then
  2014. if mode == "optimized" then -- ! NOT supported with Model file mode
  2015. mode = "full"
  2016. end
  2017.  
  2018. for _, key in
  2019. {
  2020. "IsolateLocalPlayer",
  2021. "IsolateLocalPlayerCharacter",
  2022. "IsolatePlayers",
  2023. "IsolateStarterPlayer",
  2024. "NilInstances",
  2025. }
  2026. do
  2027. if CustomOptions_valid[key] == nil then
  2028. OPTIONS[key] = false
  2029. end
  2030. end
  2031. end
  2032.  
  2033. if IsModel then
  2034. placename = (
  2035. FilePath
  2036. or sanitizeFileName("model " .. PlaceName .. " " .. (ToSaveInstance or tmp[1] or game):GetFullName())
  2037. ) .. ".rbxmx"
  2038. else
  2039. placename = (FilePath or sanitizeFileName("place " .. PlaceName)) .. ".rbxlx"
  2040. end
  2041.  
  2042. if GLOBAL_ENV[placename] then
  2043. -- warn("UniversalSynSaveInstance is already saving to this file")
  2044. return
  2045. end
  2046.  
  2047. GLOBAL_ENV[placename] = true
  2048. GLOBAL_ENV.USSI = nil
  2049. if mode ~= "scripts" then
  2050. IgnorePropertiesOfNotScriptsOnScriptsMode = nil
  2051. end
  2052.  
  2053. local TempRoot = ToSaveInstance or game
  2054.  
  2055. if mode == "full" then
  2056. if not ToSaveInstance then
  2057. local Children = TempRoot:GetChildren()
  2058. if 0 < #Children then
  2059. local tmp_dict = ArrayToDict(tmp)
  2060. for _, child in Children do
  2061. if not tmp_dict[child] then
  2062. table.insert(tmp, child)
  2063. end
  2064. end
  2065. end
  2066. end
  2067. elseif mode == "optimized" then -- ! Incompatible with .rbxmx (Model file) mode
  2068. -- if IsolatePlayers then
  2069. -- table.insert(_list_0, "Players")
  2070. -- end
  2071. local tmp_dict = ArrayToDict(tmp)
  2072.  
  2073. for _, serviceName in
  2074. {
  2075. "Workspace",
  2076. "Players",
  2077. "Lighting",
  2078. "MaterialService",
  2079. "ReplicatedFirst",
  2080. "ReplicatedStorage",
  2081.  
  2082. "ServerScriptService", -- LoadStringEnabled property (doesn't replicate); Just in case
  2083. "ServerStorage", -- Just in case
  2084.  
  2085. "StarterGui",
  2086. "StarterPack",
  2087. "StarterPlayer",
  2088. "Teams",
  2089. "SoundService",
  2090. "Chat",
  2091. "TextChatService",
  2092.  
  2093. "LocalizationService", -- For LocalizationTables
  2094. -- "InsertService",
  2095. "JointsService",
  2096.  
  2097. -- "TestService",
  2098. -- "VoiceChatService",
  2099. }
  2100. do
  2101. local _service = game:FindService(serviceName)
  2102. if _service and not tmp_dict[_service] then
  2103. table.insert(tmp, _service)
  2104. end
  2105. end
  2106. elseif mode == "scripts" then
  2107. -- TODO: Only save paths that lead to scripts (nothing else)
  2108. -- Currently saves paths along with children of each tree
  2109. local unique = {}
  2110. for _, instance in TempRoot:GetDescendants() do
  2111. if isLuaSourceContainer(instance) then
  2112. local Parent = instance.Parent
  2113. while Parent and Parent ~= TempRoot do
  2114. instance = instance.Parent
  2115. Parent = instance.Parent
  2116. end
  2117. if Parent then
  2118. unique[instance] = true
  2119. end
  2120. end
  2121. end
  2122. for instance in unique do
  2123. table.insert(tmp, instance)
  2124. end
  2125. end
  2126.  
  2127. ToSaveList = tmp
  2128.  
  2129. if ToSaveInstance then
  2130. table.insert(ToSaveList, 1, ToSaveInstance)
  2131. end
  2132. end
  2133.  
  2134. local IsolateLocalPlayer = OPTIONS.IsolateLocalPlayer
  2135. local IsolateLocalPlayerCharacter = OPTIONS.IsolateLocalPlayerCharacter
  2136. local IsolatePlayers = OPTIONS.IsolatePlayers
  2137. local IsolateStarterPlayer = OPTIONS.IsolateStarterPlayer
  2138. local NilInstances = OPTIONS.NilInstances
  2139.  
  2140. if NilInstances and enablenilinstances then -- ? Solara fix
  2141. enablenilinstances()
  2142. end
  2143. local function get_size_format()
  2144. local Size
  2145.  
  2146. -- local totalsize = #totalstr
  2147.  
  2148. for i, unit in
  2149. {
  2150. "B",
  2151. "KB",
  2152. "MB",
  2153. "GB",
  2154. "TB",
  2155. }
  2156. do
  2157. if totalsize < 0x400 ^ i then
  2158. Size = math.floor(totalsize / (0x400 ^ (i - 1)) * 10) / 10 .. " " .. unit
  2159. break
  2160. end
  2161. end
  2162.  
  2163. return Size
  2164. end
  2165.  
  2166. local RunService = service.RunService
  2167. local function wait_for_render()
  2168. RunService.RenderStepped:Wait()
  2169. end
  2170.  
  2171. local Loading
  2172. local function run_with_loading(text, keepStatus, waitForRender, taskFunction, ...)
  2173. local previousStatus
  2174.  
  2175. if StatusText then
  2176. if keepStatus then
  2177. previousStatus = StatusText.Text
  2178. end
  2179. Loading = task.spawn(function()
  2180. local spinner_count = 0
  2181. local chars = { "|", "/", "—", "\\" }
  2182. local chars_size = #chars
  2183.  
  2184. local function getLoadingText()
  2185. spinner_count += 1
  2186.  
  2187. if chars_size < spinner_count then
  2188. spinner_count = 1
  2189. end
  2190.  
  2191. return chars[spinner_count]
  2192. end
  2193.  
  2194. text ..= " "
  2195.  
  2196. while true do
  2197. StatusText.Text = text .. getLoadingText()
  2198. task.wait(0.25)
  2199. end
  2200. end)
  2201. if waitForRender then
  2202. wait_for_render()
  2203. end
  2204. end
  2205.  
  2206. local result = { taskFunction(...) }
  2207.  
  2208. if Loading then
  2209. task.cancel(Loading)
  2210. Loading = nil
  2211. if previousStatus then
  2212. StatusText.Text = previousStatus
  2213. end
  2214. end
  2215.  
  2216. return unpack(result)
  2217. end
  2218.  
  2219. local function construct_TimeoutHandler(timeout, f, timeout_ret)
  2220. return timeout < 0 and function(script)
  2221. return pcall(f, script)
  2222. end or function(script) -- TODO Ideally use ... (vararg) instead of `script` in case this is reused for something other than `decompile` & `getscriptbytecode`
  2223. local thread = coroutine.running()
  2224. local timeoutThread, isCancelled
  2225.  
  2226. timeoutThread = task.delay(timeout, function()
  2227. isCancelled = true -- TODO task.cancel
  2228. coroutine.resume(thread, nil, timeout_ret)
  2229. end)
  2230.  
  2231. task.spawn(function()
  2232. local ok, result = pcall(f, script)
  2233.  
  2234. if isCancelled then
  2235. return
  2236. end
  2237.  
  2238. task.cancel(timeoutThread)
  2239.  
  2240. while coroutine.status(thread) ~= "suspended" do
  2241. task.wait()
  2242. end
  2243.  
  2244. coroutine.resume(thread, ok, result)
  2245. end)
  2246.  
  2247. return coroutine.yield()
  2248. end
  2249. end
  2250.  
  2251. local getbytecode
  2252. if getscriptbytecode then
  2253. getbytecode = construct_TimeoutHandler(3, getscriptbytecode) -- ? Solara fix
  2254. end
  2255.  
  2256. local SaveBytecode
  2257. if OPTIONS.SaveBytecode and getscriptbytecode then
  2258. SaveBytecode = function(script)
  2259. local s, bytecode = getbytecode(script)
  2260.  
  2261. if s and bytecode and bytecode ~= "" then
  2262. return "-- Bytecode (Base64):\n-- " .. base64encode(bytecode) .. "\n\n"
  2263. end
  2264. end
  2265. end
  2266.  
  2267. do
  2268. local Decompiler = decompile --OPTIONS.decomptype == "custom" and custom_decompiler or decompile or custom_decompiler
  2269.  
  2270. -- if Decompiler == custom_decompiler then -- Cope
  2271. -- local key = "DecompileTimeout"
  2272. -- if CustomOptions[key] == nil then
  2273. -- local Option = GetAlias(key)
  2274. -- if CustomOptions[Option] == nil then
  2275. -- Timeout = 1
  2276. -- end
  2277. -- end
  2278.  
  2279. -- end
  2280.  
  2281. if OPTIONS.noscripts then
  2282. ldecompile = function()
  2283. return "-- Decompiling is disabled"
  2284. end
  2285. elseif Decompiler then
  2286. local decomp = construct_TimeoutHandler(Timeout, Decompiler, "Decompiler timed out")
  2287.  
  2288. ldecompile = function(script)
  2289. -- local name = scr.ClassName .. scr.Name
  2290. local hashed_bytecode
  2291. if ScriptCache then
  2292. local s, bytecode = getbytecode(script)
  2293. local cached
  2294.  
  2295. if s then
  2296. if not bytecode or bytecode == "" then
  2297. return "-- The Script is Empty"
  2298. end
  2299. hashed_bytecode = sha384(bytecode)
  2300. cached = ldeccache[hashed_bytecode]
  2301. end
  2302.  
  2303. if cached then
  2304. if __DEBUG_MODE then
  2305. __DEBUG_MODE("Found in Cache", script:GetFullName())
  2306. end
  2307. return cached
  2308. end
  2309. else
  2310. if DecompileJobless then
  2311. return "-- Not found in already decompiled ScriptCache"
  2312. end
  2313.  
  2314. task.wait() -- TODO Maybe remove?
  2315. end
  2316.  
  2317. local ok, result = run_with_loading("Decompiling " .. script.Name, true, nil, decomp, script)
  2318. if not result then
  2319. ok, result = false, "Empty Output"
  2320. end
  2321.  
  2322. local output
  2323. if ok then
  2324. result = string.gsub(result, "\0", "\\0") -- ? Some decompilers sadly output \0 which prevents files from opening
  2325. output = result
  2326. else
  2327. output = "--[[ Failed to decompile. Reason:\n" .. (result or "") .. "\n]]"
  2328. end
  2329.  
  2330. if ScriptCache and hashed_bytecode then -- TODO there might(?) be an edgecase where it manages to decompile (built-in) even though getscriptbytecode failed, and the output won't get cached
  2331. ldeccache[hashed_bytecode] = output -- ? Should we cache even if it timed out?
  2332. if __DEBUG_MODE then
  2333. __DEBUG_MODE("Cached", script:GetFullName())
  2334. end
  2335. end
  2336.  
  2337. return output
  2338. end
  2339. else
  2340. ldecompile = function()
  2341. return "-- Your Executor does NOT have a Decompiler"
  2342. end
  2343. end
  2344. end
  2345.  
  2346. local function GetLocalPlayer()
  2347. return service.Players.LocalPlayer
  2348. or service.Players:GetPropertyChangedSignal("LocalPlayer"):Wait()
  2349. or service.Players.LocalPlayer
  2350. end
  2351.  
  2352. local function filterLinkedSource(str)
  2353. local o, r = pcall(service.HttpService.JSONDecode, service.HttpService, str)
  2354. if o and r.errors then
  2355. return
  2356. end
  2357. return true
  2358. end
  2359.  
  2360. local function replaceClassName(instance, InstanceName, ClassName)
  2361. local InstanceOverride
  2362. if InstanceName ~= ClassName then -- TODO Compare against default instance instead (TouchTransmitter is called TouchInterest by default)
  2363. InstanceOverride = InstancesOverrides[instance]
  2364. if not InstanceOverride then
  2365. InstanceOverride = { Properties = { Name = "[" .. ClassName .. "] " .. InstanceName } }
  2366. InstancesOverrides[instance] = InstanceOverride
  2367. end
  2368. end
  2369. return InstanceOverride
  2370. end
  2371.  
  2372. local function filterPropVal(result, propertyName, category) -- ? raw == nil thanks to SerializedDefaultAttributes; "can't get value" - due to WriteOnly tag; "Invalid value for enum " - "StreamingPauseMode" (old games probably) Roexec
  2373. return result == nil
  2374. or result == "can't get value"
  2375. or type(result) == "string"
  2376. and (category == "Enum" or string_find(result, "Unable to get property " .. propertyName))
  2377. end
  2378.  
  2379. local __BREAK = "__BREAK" .. service.HttpService:GenerateGUID(false)
  2380.  
  2381. local function ReadProperty(instance, property, propertyName, special, category, optional)
  2382. local raw = __BREAK
  2383.  
  2384. local InstanceOverride = InstancesOverrides[instance]
  2385. if InstanceOverride then
  2386. local PropertiesOverride = InstanceOverride.Properties
  2387. if PropertiesOverride then
  2388. local PropertyOverride = PropertiesOverride[propertyName]
  2389. if PropertyOverride ~= nil then
  2390. return PropertyOverride
  2391. end
  2392. end
  2393. end
  2394.  
  2395. local CanRead = property.CanRead
  2396.  
  2397. if CanRead == false then -- * Skips because we've checked this property before
  2398. return __BREAK
  2399. end
  2400.  
  2401. if special then
  2402. if gethiddenproperty then
  2403. local ok, result = pcall(gethiddenproperty, instance, propertyName)
  2404.  
  2405. if ok then
  2406. raw = result
  2407. end
  2408.  
  2409. if filterPropVal(raw, propertyName, category) then
  2410. -- * Skip next time we encounter this too perhaps (unless there's a chance for it to be readable on other instance, somehow)
  2411.  
  2412. if result ~= nil or not optional then
  2413. if __DEBUG_MODE then
  2414. __DEBUG_MODE("Filtered", propertyName)
  2415. end
  2416. -- Property.Special = false
  2417. property.CanRead = false
  2418. end
  2419.  
  2420. return __BREAK -- ? We skip it because even if we use "" it will just reset to default in most cases, unless it's a string tag for example (same as not being defined)
  2421. end
  2422. end
  2423. else
  2424. if CanRead then
  2425. raw = instance[propertyName]
  2426. else -- Assuming CanRead == nil (untested)
  2427. local ok, result = pcall(index, instance, propertyName)
  2428.  
  2429. if ok then
  2430. raw = result
  2431. elseif gethiddenproperty then -- ! Be careful with this 'and gethiddenproperty' logic
  2432. ok, result = pcall(gethiddenproperty, instance, propertyName)
  2433.  
  2434. if ok then
  2435. raw = result
  2436.  
  2437. property.Special = true
  2438. end
  2439. end
  2440.  
  2441. property.CanRead = ok
  2442.  
  2443. if not ok or filterPropVal(raw, propertyName, category) then
  2444. return __BREAK
  2445. end
  2446. end
  2447. end
  2448.  
  2449. return raw
  2450. end
  2451.  
  2452. local function ReturnItem(className, instance)
  2453. return '<Item class="' .. className .. '" referent="' .. GetRef(instance) .. '"><Properties>' -- TODO: Ideally this shouldn't return <Properties> as well as the line below to close it IF IgnorePropertiesOfNotScriptsOnScriptsMode is Enabled OR If all properties are default (reduces file size by at least 1.4%)
  2454. end
  2455.  
  2456. local function ReturnProperty(tag, propertyName, value)
  2457. return "<" .. tag .. ' name="' .. propertyName .. '">' .. value .. "</" .. tag .. ">"
  2458. end
  2459.  
  2460. local function ReturnValueAndTag(raw, valueType, descriptor)
  2461. local value, tag = (descriptor or XML_Descriptors[valueType])(raw)
  2462.  
  2463. return value, tag or valueType
  2464. end
  2465.  
  2466. local function InheritsFix(fixes, className, instance)
  2467. local Fix = fixes[className]
  2468. if Fix then
  2469. return Fix
  2470. elseif Fix == nil then
  2471. for class_name, fix in fixes do
  2472. if instance:IsA(class_name) then
  2473. return fix
  2474. end
  2475. end
  2476. end
  2477. end
  2478.  
  2479. local function GetInheritedProps(className)
  2480. local prop_list = {}
  2481. local layer = ClassList[className]
  2482. while layer do
  2483. local layer_props = layer.Properties
  2484. table.move(layer_props, 1, #layer_props, #prop_list + 1, prop_list)
  2485.  
  2486. -- for _, prop in layer.Properties do
  2487. -- prop_list[prop_count] = prop -- ? table.clone is needed for case where .Default is modified
  2488. -- prop_count += 1
  2489. -- end
  2490.  
  2491. layer = ClassList[layer.Superclass]
  2492. end
  2493. inherited_properties[className] = prop_list
  2494. return prop_list
  2495. end
  2496.  
  2497. local CHUNK_LIMIT = 200 * 1024 * 1024 -- string length overflow prevention
  2498. local function save_cache(final)
  2499. local savestr = table.concat(savebuffer)
  2500. currentstr ..= savestr -- TODO: Causes "not enough memory" error on some exec
  2501.  
  2502. -- writefile(placename, totalstr)
  2503. -- appendfile(placename, savestr) -- * supposedly causes uneven amount of Tags (e.g. <Item> must be closed with </Item> but sometimes there's more of one than the other). While being under load, the function produces unexpected output?
  2504. local savestr_len = #savestr
  2505. totalsize += savestr_len
  2506. currentsize += savestr_len
  2507.  
  2508. table.clear(savebuffer)
  2509. savebuffer_size = 1
  2510.  
  2511. if CHUNK_LIMIT < currentsize or final then
  2512. table.insert(chunks, { size = currentsize, str = currentstr })
  2513. currentstr, currentsize = "", 0
  2514. end
  2515.  
  2516. if StatusText then
  2517. StatusText.Text = "Saving.. Size: " .. get_size_format()
  2518. end
  2519. -- ? Needed for at least 1fps (status text)
  2520. -- task.wait()
  2521. wait_for_render()
  2522. end
  2523.  
  2524. local function save_specific(className, properties)
  2525. local Ref = Instance.new(className) -- ! Assuming anything passed here is Creatable
  2526. local Item = ReturnItem(Ref.ClassName, Ref)
  2527.  
  2528. for propertyName, val in properties do
  2529. local whitelisted, value, tag
  2530.  
  2531. -- TODO: Improve all sort of overrides & exceptions in the code (code below is awful)
  2532. if "Source" == propertyName then
  2533. tag = "ProtectedString"
  2534. value = XML_Descriptors.__PROTECTEDSTRING(val)
  2535. whitelisted = true
  2536. elseif "Name" == propertyName then
  2537. whitelisted = true
  2538. value, tag = ReturnValueAndTag(val, "string") -- * Doubt ValueType will change
  2539. end
  2540.  
  2541. if whitelisted then
  2542. Item ..= ReturnProperty(tag, propertyName, value)
  2543. end
  2544. end
  2545. Item ..= "</Properties>"
  2546. return Item
  2547. end
  2548.  
  2549. local function save_hierarchy(hierarchy)
  2550. for _, instance in hierarchy do
  2551. if IgnoreNotArchivable and not instance.Archivable then
  2552. continue
  2553. end
  2554.  
  2555. local SkipEntirely = IgnoreList[instance]
  2556. if SkipEntirely then
  2557. continue
  2558. end
  2559.  
  2560. local ClassName = instance.ClassName
  2561.  
  2562. local InstanceName = instance.Name
  2563.  
  2564. do
  2565. local OnIgnoredList = IgnoreList[ClassName]
  2566. if OnIgnoredList and (OnIgnoredList == true or OnIgnoredList[InstanceName]) then
  2567. continue
  2568. end
  2569. end
  2570.  
  2571. if not DecompileIgnoring then
  2572. DecompileIgnoring = DecompileIgnore[instance]
  2573.  
  2574. if DecompileIgnoring == nil then
  2575. local DecompileIgnored = DecompileIgnore[ClassName]
  2576. if DecompileIgnored then
  2577. DecompileIgnoring = DecompileIgnored == true or DecompileIgnored[InstanceName]
  2578. end
  2579. end
  2580.  
  2581. if DecompileIgnoring then
  2582. DecompileIgnoring = instance
  2583. elseif DecompileIgnoring == false then
  2584. DecompileIgnoring = 1 -- Ignore one instance
  2585. end
  2586. end
  2587.  
  2588. local InstanceOverride, ClassNameOverride, ClassTagOverride
  2589.  
  2590. do
  2591. local Fix = NotCreatableFixes[ClassName]
  2592.  
  2593. if Fix then
  2594. if SaveNotCreatable then
  2595. ClassName, InstanceOverride = Fix, replaceClassName(instance, InstanceName, ClassName)
  2596. else
  2597. continue -- They won't show up in Studio anyway (Enable SaveNotCreatable if you wish to bypass this)
  2598. end
  2599. else -- ! Assuming nothing that is a PartOperation or inherits from it is in NotCreatableFixes
  2600. if TreatUnionsAsParts and instance:IsA("PartOperation") then
  2601. ClassName, InstanceOverride = "Part", replaceClassName(instance, InstanceName, ClassName)
  2602. ClassNameOverride = "BasePart" -- * Mutual Superclass for PartOperation and Part; For properties only
  2603. elseif not ClassList[ClassName] then -- ? API Dump is outdated then
  2604. if __DEBUG_MODE then
  2605. __DEBUG_MODE("Class not Found", ClassName)
  2606. end
  2607.  
  2608. ClassTagOverride = ClassName -- ? To at least retain .ClassName unlike the rest of the class-specific properties
  2609. ClassName = "Folder" -- ? replaceClassName is not needed because of the ClassTagOverride
  2610. end
  2611. end
  2612. end
  2613.  
  2614. if not InstanceOverride then
  2615. InstanceOverride = InstancesOverrides[instance]
  2616. end
  2617.  
  2618. -- ? The reason we only save .Name (and few other props in save_specific) is because
  2619. -- ? we can be sure this is a custom container (ex. NilInstancesFixes)
  2620. -- ? However, in case of NotCreatableFixes, the Instance might have Tags, Attributes etc. that can potentially be saved (even though it's a Folder)
  2621. if InstanceOverride and InstanceOverride.__SaveSpecific then
  2622. savebuffer[savebuffer_size] = save_specific(ClassName, InstanceOverride.Properties) -- ! Assuming anything that has __SaveSpecific will have .Properties
  2623. savebuffer_size += 1
  2624. else
  2625. -- local Properties =
  2626. savebuffer[savebuffer_size] = ReturnItem(ClassTagOverride or ClassName, instance) -- TODO: Ideally this shouldn't return <Properties> as well as the line below to close it IF IgnorePropertiesOfNotScriptsOnScriptsMode is ENABLED
  2627. savebuffer_size += 1
  2628. if not (IgnorePropertiesOfNotScriptsOnScriptsMode and not isLuaSourceContainer(instance)) then
  2629. local default_instance, new_def_inst
  2630.  
  2631. if IgnoreDefaultProperties then
  2632. default_instance = default_instances[ClassName]
  2633. if not default_instance then
  2634. local ClassTags = ClassList[ClassName].Tags
  2635. if not (ClassTags and ClassTags.NotCreatable) then -- __api_dump_class_not_creatable__ also indicates this
  2636. -- ! NotCreatableFixes are exceptions to the check above meaning if we don't keep the NotCreatableFixes updated then Instance.new below might start erroring in the future potentially; HOWEVER IsPropertyModified solves this issue and no updates are really needed as NotCreatableFixes is up-to-date as of VERSION-HERE (which is when IPM gets enabled)
  2637. new_def_inst = Instance.new(ClassName) -- ! Assuming anything that doesn't have NotCreatable is possible to create (therefore no pcall); EXCEPTION NOTED ABOVE
  2638.  
  2639. default_instance = {}
  2640.  
  2641. default_instances[ClassName] = default_instance
  2642. elseif __DEBUG_MODE then
  2643. __DEBUG_MODE("Unable to create default Instance", ClassName)
  2644. end
  2645. end
  2646. end
  2647. local proplist
  2648. do
  2649. local class = ClassNameOverride or ClassName
  2650. proplist = inherited_properties[class]
  2651. if not proplist then
  2652. proplist = GetInheritedProps(class)
  2653. inherited_properties[class] = proplist
  2654. end
  2655. end
  2656. for _, Property in proplist do
  2657. local PropertyName = Property.Name
  2658.  
  2659. if IgnoreProperties[PropertyName] then
  2660. continue
  2661. end
  2662.  
  2663. local ValueType = Property.ValueType
  2664.  
  2665. if IgnoreSharedStrings and ValueType == "SharedString" then -- ? More info in Options
  2666. continue
  2667. end
  2668.  
  2669. local Special, Category, Optional = Property.Special, Property.Category, Property.Optional
  2670.  
  2671. local raw = ReadProperty(instance, Property, PropertyName, Special, Category, Optional)
  2672.  
  2673. if raw == __BREAK then -- ! Assuming __BREAK is always returned when there's a failure to read a property
  2674. local ok, result = pcall(gethiddenproperty_fallback, instance, PropertyName) -- * This helps in reading: Vector3int16, OptionalCoordinateFrame DataTypes. It also acts as an almost entire fallback for gethiddenproperty in case it is missing
  2675.  
  2676. if result == nil and not Optional then
  2677. ok = nil
  2678. end
  2679.  
  2680. if ok then
  2681. raw = result
  2682. else
  2683. local Fallback = Property.Fallback
  2684.  
  2685. if Fallback then
  2686. ok, result = pcall(Fallback, instance)
  2687.  
  2688. if ok then
  2689. raw = result
  2690. else
  2691. if __DEBUG_MODE then
  2692. -- TODO Maybe remove the fix during runtime if it fails to avoid re-trying
  2693. __DEBUG_MODE("Fix Failed", PropertyName)
  2694. end
  2695. continue
  2696. end
  2697. else
  2698. continue
  2699. end
  2700. end
  2701. end
  2702.  
  2703. if SharedStringOverwrite and ValueType == "BinaryString" then -- TODO: Convert this to table if more types are added
  2704. ValueType = "SharedString"
  2705. end
  2706.  
  2707. -- Special = Property.Special -- ? Read TODO below (must be updated if it's used frequently afterwards)
  2708.  
  2709. if
  2710. default_instance
  2711. and not Property.Special -- TODO: .Special is checked more than once (because it might be updated during ReadProperty)
  2712. and not (PropertyName == "Source" and isLuaSourceContainer(instance))
  2713. then -- ? Could be not just "Source" in the future
  2714. if new_def_inst then
  2715. default_instance[PropertyName] = index(new_def_inst, PropertyName)
  2716. end
  2717. if default_instance[PropertyName] == raw then
  2718. continue
  2719. end
  2720. end
  2721.  
  2722. -- Serialization start
  2723.  
  2724. local tag, value
  2725. if Category == "Class" then
  2726. tag = "Ref"
  2727. if raw then
  2728. if SaveNotCreatableWillBeEnabled then
  2729. local Fix = NotCreatableFixes[raw.ClassName]
  2730. if
  2731. Fix
  2732. and (
  2733. PropertyName == "PlayerToHideFrom"
  2734. or ValueType ~= "Instance" and ValueType ~= Fix
  2735. )
  2736. then
  2737. -- * To avoid errors
  2738. continue
  2739. end
  2740. end
  2741.  
  2742. value = GetRef(raw)
  2743. else
  2744. value = "null"
  2745. end
  2746. elseif Category == "Enum" then -- ! We do this order (Enums before Descriptors) specifically because Font Enum might get a Font Descriptor despite having Enum Category, unlike Font DataType which that Descriptor is meant for
  2747. value, tag = XML_Descriptors.__ENUM(raw)
  2748. else
  2749. local Descriptor = XML_Descriptors[ValueType]
  2750.  
  2751. if Descriptor then
  2752. value, tag = ReturnValueAndTag(raw, ValueType, Descriptor)
  2753. elseif "ProtectedString" == ValueType then -- TODO: Try fitting this inside Descriptors
  2754. tag = ValueType
  2755.  
  2756. if PropertyName == "Source" then
  2757. if DecompileIgnoring then -- ? Should this really prevent extraction of the original source if present ?
  2758. if DecompileIgnoring == 1 then
  2759. DecompileIgnoring = nil
  2760. end
  2761. value = "-- Ignored"
  2762. else
  2763. local should_decompile = true
  2764. local LinkedSource
  2765. local LinkedSource_Url = instance.LinkedSource -- ! Assuming every Class that has ProtectedString Source property also has a LinkedSource property
  2766. local hasLinkedSource = LinkedSource_Url ~= ""
  2767. local LinkedSource_type
  2768. if hasLinkedSource then
  2769. local Path = instance:GetFullName()
  2770. if RecoveredScripts then
  2771. table.insert(RecoveredScripts, Path)
  2772. end
  2773.  
  2774. LinkedSource = string.match(LinkedSource_Url, "%w+$") -- TODO: No sure if this pattern matches all possible cases. Example is: 'rbxassetid://0&hash=cd73dd2fe5e5013137231c227da3167e'
  2775. if LinkedSource then
  2776. if ScriptCache then
  2777. local cached = ldeccache[LinkedSource]
  2778.  
  2779. if cached then
  2780. value = cached
  2781. should_decompile = nil
  2782. end
  2783. end
  2784. if should_decompile then
  2785. if DecompileJobless then
  2786. value = "-- Not found in LinkedSource ScriptCache"
  2787. should_decompile = nil
  2788. end
  2789.  
  2790. LinkedSource_type = string.find(LinkedSource, "%a") and "hash"
  2791. or "id"
  2792.  
  2793. local asset = LinkedSource_type .. "=" .. LinkedSource
  2794.  
  2795. local ok, source = pcall(function()
  2796. -- Credits @halffalse
  2797. return game:HttpGet(
  2798. "https://assetdelivery.roproxy.com/v1/asset/?" .. asset
  2799. )
  2800. end)
  2801.  
  2802. if ok and filterLinkedSource(source) then
  2803. if ScriptCache then
  2804. ldeccache[LinkedSource] = source
  2805. end
  2806.  
  2807. value = source
  2808.  
  2809. should_decompile = nil
  2810. end
  2811. end
  2812. else --if __DEBUG_MODE then -- * We print this anyway because very important
  2813. warn(
  2814. "FAILED TO EXTRACT ORIGINAL SCRIPT SOURCE (OPEN A GITHUB ISSUE): ",
  2815. instance:GetFullName(),
  2816. LinkedSource_Url
  2817. )
  2818. end
  2819. end
  2820.  
  2821. if should_decompile then
  2822. local isLocalScript = instance:IsA("LocalScript")
  2823. if
  2824. isLocalScript and instance.RunContext == Enum.RunContext.Server
  2825. or not isLocalScript
  2826. and instance:IsA("Script")
  2827. and instance.RunContext ~= Enum.RunContext.Client
  2828. then
  2829. value = "-- [FilteringEnabled] Server Scripts are IMPOSSIBLE to save" -- TODO: Could be not just server scripts in the future
  2830. else
  2831. value = ldecompile(instance)
  2832. if SaveBytecode then
  2833. local output = SaveBytecode(instance)
  2834. if output then
  2835. value = output .. value
  2836. end
  2837. end
  2838. end
  2839. end
  2840.  
  2841. value = "-- Saved by UniversalSynSaveInstance (Join to Copy Games) https://discord.gg/wx4ThpAsmw\n\n"
  2842. .. (hasLinkedSource and "-- Original Source: https://assetdelivery.roblox.com/v1/asset/?" .. (LinkedSource_type or "id") .. "=" .. (LinkedSource or LinkedSource_Url) .. "\n\n" or "")
  2843. .. value
  2844. end
  2845. end
  2846. value = XML_Descriptors.__PROTECTEDSTRING(value)
  2847. else
  2848. --OptionalCoordinateFrame and so on, we make it dynamic
  2849.  
  2850. if Optional then
  2851. Descriptor = XML_Descriptors[Optional]
  2852.  
  2853. if Descriptor then
  2854. if raw == nil then
  2855. -- * It can be empty, because it's optional
  2856. -- ? Though why even save it if it's empty considering it's optional
  2857. continue
  2858. -- value, tag = "", ValueType
  2859. else
  2860. value, tag = ReturnValueAndTag(raw, ValueType, Descriptor)
  2861. end
  2862. end
  2863. end
  2864. end
  2865. end
  2866.  
  2867. if tag then
  2868. savebuffer[savebuffer_size] = ReturnProperty(tag, PropertyName, value)
  2869. savebuffer_size += 1
  2870. else --if __DEBUG_MODE then -- * We print this anyway because very important
  2871. warn("UNSUPPORTED TYPE (OPEN A GITHUB ISSUE): ", ValueType, ClassName, PropertyName)
  2872. end
  2873. end
  2874. end
  2875. savebuffer[savebuffer_size] = "</Properties>"
  2876. savebuffer_size += 1
  2877.  
  2878. if SaveCacheInterval < savebuffer_size then
  2879. save_cache()
  2880. end
  2881. end
  2882.  
  2883. if SkipEntirely ~= false then -- ? We save instance without it's descendants in this case (== false)
  2884. local Children = InstanceOverride and InstanceOverride.__Children or instance:GetChildren()
  2885.  
  2886. if #Children ~= 0 then
  2887. save_hierarchy(Children)
  2888. end
  2889. end
  2890.  
  2891. if DecompileIgnoring and DecompileIgnoring == instance then
  2892. DecompileIgnoring = nil
  2893. end
  2894.  
  2895. savebuffer[savebuffer_size] = "</Item>"
  2896. savebuffer_size += 1
  2897. end
  2898. end
  2899.  
  2900. local function save_extra(name, hierarchy, customClassName, source)
  2901. savebuffer[savebuffer_size] = save_specific((customClassName or "Folder"), { Name = name, Source = source })
  2902. savebuffer_size += 1
  2903. if hierarchy then
  2904. save_hierarchy(hierarchy)
  2905. end
  2906. savebuffer[savebuffer_size] = "</Item>"
  2907. savebuffer_size += 1
  2908. end
  2909.  
  2910. local function save_game()
  2911. do
  2912. if IsModel then
  2913. --[[
  2914. -- ? Roblox encodes the following additional attributes. These are not required. Moreover, any defined schemas are ignored, and not required for a file to be valid: xmlns:xmime="http://www.w3.org/2005/05/xmlmime" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.roblox.com/roblox.xsd"
  2915. Also http can be converted to https but not sure if Roblox cares
  2916. -- ? <External>null</External><External>nil</External> - <External> is a legacy concept that is no longer used.
  2917. ]]
  2918. header ..= '<Meta name="ExplicitAutoJoints">true</Meta>'
  2919. end
  2920.  
  2921. writefile(placename, header) -- TODO This is sort of useless if writefile will be used at the end (like if AlternativeWritefile and Callback are unused)
  2922. end
  2923.  
  2924. -- TODO Find a better solution for this
  2925. SaveNotCreatableWillBeEnabled = SaveNotCreatable
  2926. or (IsolateLocalPlayer or IsolateLocalPlayerCharacter) and IsolateLocalPlayer
  2927. or IsolatePlayers
  2928. or NilInstances and global_container.getnilinstances -- ! Make sure this accurately reflects everything below
  2929.  
  2930. save_hierarchy(ToSaveList)
  2931.  
  2932. if IsolateLocalPlayer or IsolateLocalPlayerCharacter then
  2933. local LocalPlayer = service.Players.LocalPlayer
  2934. if LocalPlayer then
  2935. if IsolateLocalPlayer then
  2936. SaveNotCreatable = true
  2937. save_extra("LocalPlayer", LocalPlayer:GetChildren())
  2938. end
  2939. if IsolateLocalPlayerCharacter then
  2940. local LocalPlayerCharacter = LocalPlayer.Character
  2941. if LocalPlayerCharacter then
  2942. save_extra("LocalPlayer Character", LocalPlayerCharacter:GetChildren())
  2943. end
  2944. end
  2945. end
  2946. end
  2947.  
  2948. if IsolateStarterPlayer then
  2949. -- SaveNotCreatable = true -- TODO: Enable if StarterPlayerScripts or StarterCharacterScripts stop showing up in isolated folder in Studio
  2950. save_extra("StarterPlayer", service.StarterPlayer:GetChildren())
  2951. end
  2952.  
  2953. if IsolatePlayers then
  2954. SaveNotCreatable = true
  2955. save_extra("Players", service.Players:GetChildren())
  2956. end
  2957.  
  2958. if NilInstances and global_container.getnilinstances then
  2959. local nil_instances, nil_instances_size = {}, 1
  2960.  
  2961. local NilInstancesFixes = OPTIONS.NilInstancesFixes
  2962.  
  2963. for _, instance in global_container.getnilinstances() do
  2964. if instance == game then
  2965. instance = nil
  2966. -- break
  2967. else
  2968. local ClassName = instance.ClassName
  2969.  
  2970. local Fix = InheritsFix(NilInstancesFixes, ClassName, instance)
  2971.  
  2972. if Fix then
  2973. instance = Fix(instance, InstancesOverrides)
  2974. -- continue
  2975. end
  2976.  
  2977. local Class = ClassList[ClassName]
  2978. if Class then
  2979. local ClassTags = Class.Tags
  2980. if ClassTags and ClassTags.Service then -- For CSGDictionaryService, NonReplicatedCSGDictionaryService, LogService, ProximityPromptService, TestService & more
  2981. -- instance.Parent = game
  2982. instance = nil
  2983. -- continue
  2984. end
  2985. end
  2986. end
  2987. if instance then
  2988. nil_instances[nil_instances_size] = instance
  2989. nil_instances_size += 1
  2990. end
  2991. end
  2992. SaveNotCreatable = true
  2993. save_extra("Nil Instances", nil_instances)
  2994. end
  2995.  
  2996. if OPTIONS.ReadMe then
  2997. save_extra(
  2998. "README",
  2999. nil,
  3000. "Script",
  3001. "--[[\n"
  3002. .. (#RecoveredScripts ~= 0 and "\t\tIMPORTANT: Original Source of these Scripts was Recovered: " .. service.HttpService:JSONEncode(
  3003. RecoveredScripts
  3004. ) .. "\n" or "")
  3005. .. [[
  3006. Thank you for using UniversalSynSaveInstance (Join to Copy Games) https://discord.gg/wx4ThpAsmw.
  3007.  
  3008. If you didn't save in Binary (rbxl) - it's recommended to save the game right away to take advantage of the binary format & to preserve values of certain properties if you used IgnoreDefaultProperties setting (as they might change in the future).
  3009. You can do that by going to FILE -> Save to File As -> Make sure File Name ends with .rbxl -> Save
  3010.  
  3011. ServerStorage, ServerScriptService and Server Scripts are IMPOSSIBLE to save because of FilteringEnabled.
  3012.  
  3013. If your player cannot spawn into the game, please move the scripts in StarterPlayer somewhere else. Then run `game:GetService("Players").CharacterAutoLoads = true`.
  3014. And use "Play Here" to start game instead of "Play" to spawn your Character where your Camera currently is.
  3015.  
  3016. If the chat system does not work, please use the explorer and delete everything inside the TextChatService/Chat service(s).
  3017. Or run `game:GetService("Chat"):ClearAllChildren() game:GetService("TextChatService"):ClearAllChildren()`
  3018.  
  3019. If Union and MeshPart collisions don't work, run the script below in the Studio Command Bar:
  3020.  
  3021.  
  3022. local C = game:GetService("CoreGui")
  3023. local D = Enum.CollisionFidelity.Default
  3024.  
  3025. for _, v in game:GetDescendants() do
  3026. if v:IsA("TriangleMeshPart") and not v:IsDescendantOf(C) then
  3027. v.CollisionFidelity = D
  3028. end
  3029. end
  3030. print("Done")
  3031.  
  3032. If you can't move the Camera, run this script in the Studio Command Bar:
  3033.  
  3034. workspace.CurrentCamera.CameraType = Enum.CameraType.Fixed
  3035.  
  3036. Or Destroy the Camera.
  3037.  
  3038. This file was generated with the following settings:
  3039. ]]
  3040. .. service.HttpService:JSONEncode(OPTIONS)
  3041. .. "\n\n\t\tElapsed time: "
  3042. .. os.clock() - elapse_t
  3043. .. " PlaceId: "
  3044. .. game.PlaceId
  3045. .. " PlaceVersion: "
  3046. .. game.PlaceVersion
  3047. .. " Client Version: "
  3048. .. version()
  3049. .. " Executor: "
  3050. .. (identify_executor and table.concat({ identify_executor() }, " ") or "Unknown")
  3051. .. "\n]]"
  3052. )
  3053. end
  3054. do
  3055. local tmp = { "<SharedStrings>" }
  3056. for identifier, value in SharedStrings do
  3057. table.insert(tmp, '<SharedString md5="' .. identifier .. '">' .. value .. "</SharedString>")
  3058. end
  3059.  
  3060. if 1 < #tmp then -- TODO: This sucks so much because we try to iterate a table just to check this (check above)
  3061. savebuffer[savebuffer_size] = table.concat(tmp)
  3062. savebuffer_size += 1
  3063. savebuffer[savebuffer_size] = "</SharedStrings>"
  3064. savebuffer_size += 1
  3065. end
  3066. end
  3067.  
  3068. savebuffer[savebuffer_size] =
  3069. "</roblox><!-- Saved by UniversalSynSaveInstance (Join to Copy Games) https://discord.gg/wx4ThpAsmw -->"
  3070. savebuffer_size += 1
  3071. save_cache(true)
  3072. do
  3073. -- ! Assuming we only write to file once hence why we only filter once
  3074. -- TODO This might cause issues on non-unique Usernames (ex. "Cake" if game is about cakes then everything supposedly related to your name will be replaced with "Roblox"); Certain UserIds might also affect numbers, like if your UserId is 2481848 and there is some number that goes like "1.248184818837" then that the matched part will be replaced with 1, potentially making the number incorrect.
  3075. -- TODO So for now it's best to keep this disabled by default
  3076. -- TODO It's also not smart to filter entire file string at the end as this might also affect decompiled scripts content, which has no way of containing any user-related information. It would be better to use gsub in string Descriptor and such
  3077. if OPTIONS.Anonymous then
  3078. local LocalPlayer = service.Players.LocalPlayer
  3079. if LocalPlayer then
  3080. local function gsubCaseInsensitive(input, search, replacement) -- * Credits to friends
  3081. local inputLower = string.lower(input)
  3082.  
  3083. search = string.lower(search)
  3084.  
  3085. local lastFinish = 0
  3086. local subStrings = {}
  3087. local search_len = #search
  3088. local input_len = #input
  3089. while search_len <= input_len - lastFinish do
  3090. local init = lastFinish + 1
  3091.  
  3092. local start, finish = string.find(inputLower, search, init, true)
  3093.  
  3094. if start == nil then
  3095. break
  3096. end
  3097.  
  3098. table.insert(subStrings, string.sub(input, init, start - 1))
  3099.  
  3100. lastFinish = finish
  3101. end
  3102.  
  3103. if lastFinish == 0 then
  3104. return input
  3105. end
  3106.  
  3107. table.insert(subStrings, string.sub(input, lastFinish + 1))
  3108.  
  3109. return table.concat(subStrings, replacement)
  3110. end
  3111.  
  3112. local Anonymous = type(OPTIONS.Anonymous) == "table" and OPTIONS.Anonymous
  3113. or { UserId = "1", Name = "Roblox" }
  3114.  
  3115. for _, chunk in chunks do
  3116. chunk.str = gsubCaseInsensitive(
  3117. string.gsub(chunk.str, LocalPlayer.UserId, Anonymous.UserId),
  3118. LocalPlayer.Name,
  3119. Anonymous.Name
  3120. )
  3121. end
  3122. end
  3123. end
  3124.  
  3125. local Callback = OPTIONS.Callback
  3126. if Callback then
  3127. local totalstr = header
  3128. for _, chunk in chunks do
  3129. totalstr ..= chunk.str
  3130. end
  3131. Callback(totalstr, chunks, totalsize)
  3132. elseif OPTIONS.AlternativeWritefile and appendfile then
  3133. local SEGMENT_SIZE = 4145728 -- Celery has an arbitrary savefile/appendfile size limit of ~4MB for reasons unknown. This is a workaround to save the file in segments.
  3134.  
  3135. local totallen, currentlen = math.ceil(totalsize / SEGMENT_SIZE), 1
  3136.  
  3137. for _, chunk in chunks do
  3138. local length = math.ceil(chunk.size / SEGMENT_SIZE)
  3139. for i = 1, length do
  3140. local savestr = string.sub(chunk.str, (i - 1) * SEGMENT_SIZE + 1, i * SEGMENT_SIZE)
  3141.  
  3142. run_with_loading(
  3143. "Writing to File " .. math.round(currentlen / totallen * 100) .. "% (Depends on Exec)",
  3144. nil,
  3145. true,
  3146. appendfile,
  3147. placename,
  3148. savestr
  3149. )
  3150. currentlen += 1
  3151.  
  3152. if i ~= length then
  3153. task.wait()
  3154. end
  3155. end
  3156. end
  3157. else
  3158. local totalstr = header
  3159. for _, chunk in chunks do
  3160. totalstr ..= chunk.str
  3161. end
  3162. run_with_loading(
  3163. "Writing " .. get_size_format() .. " to File (Depends on Exec)",
  3164. nil,
  3165. true,
  3166. writefile,
  3167. placename,
  3168. totalstr
  3169. )
  3170. end
  3171. end
  3172. table.clear(SharedStrings)
  3173. end
  3174.  
  3175. local Connections
  3176. do
  3177. local Players = service.Players
  3178.  
  3179. if IgnoreList.Model ~= true then
  3180. Connections = {}
  3181. local function ignoreCharacter(player)
  3182. table.insert(
  3183. Connections,
  3184. player.CharacterAdded:Connect(function(character)
  3185. IgnoreList[character] = true
  3186. end)
  3187. )
  3188.  
  3189. local Character = player.Character
  3190. if Character then
  3191. IgnoreList[Character] = true
  3192. end
  3193. end
  3194.  
  3195. if OPTIONS.RemovePlayerCharacters then
  3196. table.insert(
  3197. Connections,
  3198. Players.PlayerAdded:Connect(function(player)
  3199. ignoreCharacter(player)
  3200. end)
  3201. )
  3202. for _, player in Players:GetPlayers() do
  3203. ignoreCharacter(player)
  3204. end
  3205. else
  3206. IgnoreNotArchivable = false -- TODO Bad solution (Characters are NotArchivable); Also make sure the next solution is compatible with IsolateLocalPlayerCharacter
  3207. if IsolateLocalPlayerCharacter then
  3208. task.spawn(function()
  3209. ignoreCharacter(GetLocalPlayer())
  3210. end)
  3211. end
  3212. end
  3213. end
  3214. if IsolateLocalPlayer and IgnoreList.Player ~= true then
  3215. task.spawn(function()
  3216. IgnoreList[GetLocalPlayer()] = true
  3217. end)
  3218. end
  3219. end
  3220.  
  3221. if IsolateStarterPlayer then
  3222. IgnoreList.StarterPlayer = false
  3223. end
  3224.  
  3225. if IsolatePlayers then
  3226. IgnoreList.Players = false
  3227. end
  3228.  
  3229. if OPTIONS.ShowStatus then
  3230. do
  3231. local Exists = GLOBAL_ENV._statustext
  3232. if Exists then
  3233. Exists:Destroy()
  3234. end
  3235. end
  3236.  
  3237. local StatusGui = Instance.new("ScreenGui")
  3238.  
  3239. GLOBAL_ENV._statustext = StatusGui
  3240.  
  3241. StatusGui.DisplayOrder = 2e9
  3242. pcall(function() -- ? Compatibility with level 2
  3243. StatusGui.OnTopOfCoreBlur = true
  3244. end)
  3245.  
  3246. StatusText = Instance.new("TextLabel")
  3247.  
  3248. StatusText.Text = "Saving..."
  3249.  
  3250. StatusText.BackgroundTransparency = 1
  3251. StatusText.Font = Enum.Font.Code
  3252. StatusText.AnchorPoint = Vector2.new(1)
  3253. StatusText.Position = UDim2.new(1)
  3254. StatusText.Size = UDim2.new(0.3, 0, 0, 20)
  3255.  
  3256. StatusText.TextColor3 = Color3.new(1, 1, 1)
  3257. StatusText.TextScaled = true
  3258. StatusText.TextStrokeTransparency = 0.7
  3259. StatusText.TextXAlignment = Enum.TextXAlignment.Right
  3260. StatusText.TextYAlignment = Enum.TextYAlignment.Top
  3261.  
  3262. StatusText.Parent = StatusGui
  3263.  
  3264. local function randomString()
  3265. local length = math.random(10, 20)
  3266. local randomarray = table.create(length)
  3267. for i = 1, length do
  3268. randomarray[i] = string.char(math.random(32, 126))
  3269. end
  3270. return table.concat(randomarray)
  3271. end
  3272.  
  3273. if global_container.gethui then
  3274. StatusGui.Name = randomString()
  3275. StatusGui.Parent = global_container.gethui()
  3276. else
  3277. if global_container.protectgui then
  3278. StatusGui.Name = randomString()
  3279. global_container.protectgui(StatusGui)
  3280. StatusGui.Parent = game:GetService("CoreGui")
  3281. else
  3282. local RobloxGui = game:GetService("CoreGui"):FindFirstChild("RobloxGui")
  3283. if RobloxGui then
  3284. StatusGui.Parent = RobloxGui
  3285. else
  3286. StatusGui.Name = randomString()
  3287. StatusGui.Parent = game:GetService("CoreGui")
  3288. end
  3289. end
  3290. end
  3291. end
  3292.  
  3293. do
  3294. local SafeMode = OPTIONS.SafeMode
  3295. if SafeMode then
  3296. task.spawn(function()
  3297. local LocalPlayer = GetLocalPlayer()
  3298.  
  3299. local PlayerScripts = LocalPlayer:FindFirstChild("PlayerScripts")
  3300. if PlayerScripts then
  3301. local function construct_InstanceOverride(instance)
  3302. local children = instance:GetChildren()
  3303. InstancesOverrides[instance] = {
  3304. __Children = children,
  3305. }
  3306. for _, child in children do
  3307. construct_InstanceOverride(child)
  3308. end
  3309. end
  3310. construct_InstanceOverride(PlayerScripts)
  3311.  
  3312. InstancesOverrides[LocalPlayer] = {
  3313. __Children = LocalPlayer:GetChildren(),
  3314. Properties = { Name = "[" .. LocalPlayer.ClassName .. "] " .. LocalPlayer.Name },
  3315. }
  3316. end
  3317.  
  3318. LocalPlayer:Kick("\n[SAFEMODE] Saving in Progress..\nPlease do NOT leave")
  3319. wait_for_render()
  3320. task.delay(10, service.GuiService.ClearError, service.GuiService)
  3321. end)
  3322.  
  3323. service.RunService:Set3dRenderingEnabled(false)
  3324. end
  3325.  
  3326. local anti_idle
  3327. if OPTIONS.AntiIdle then
  3328. task.spawn(function()
  3329. local Idled = GetLocalPlayer().Idled
  3330. if getconnections then
  3331. for _, c in getconnections(Idled) do
  3332. if not pcall(function()
  3333. c:Disable()
  3334. end) then
  3335. pcall(function()
  3336. c:Disconnect()
  3337. end)
  3338. end
  3339. end
  3340. end
  3341. anti_idle = Idled:Connect(function()
  3342. service.VirtualInputManager:SendMouseWheelEvent(
  3343. service.UserInputService:GetMouseLocation().X,
  3344. service.UserInputService:GetMouseLocation().Y,
  3345. true,
  3346. game
  3347. )
  3348. end)
  3349. end)
  3350. end
  3351.  
  3352. elapse_t = os.clock()
  3353.  
  3354. local ok, err = xpcall(save_game, function(err)
  3355. return debug.traceback(err)
  3356. end)
  3357.  
  3358. if SafeMode then
  3359. service.GuiService:ClearError()
  3360. service.RunService:Set3dRenderingEnabled(true)
  3361. end
  3362.  
  3363. if old_gethiddenproperty then
  3364. gethiddenproperty = old_gethiddenproperty
  3365. end
  3366.  
  3367. if anti_idle then
  3368. anti_idle:Disconnect()
  3369. end
  3370. if Connections then
  3371. for _, connection in Connections do
  3372. connection:Disconnect()
  3373. end
  3374. end
  3375. GLOBAL_ENV[placename] = nil
  3376. if StatusText then
  3377. task.spawn(function()
  3378. elapse_t = os.clock() - elapse_t
  3379. local Log10 = math.log10(elapse_t)
  3380. local ExtraTime = 10
  3381. if ok then
  3382. StatusText.Text = string.format("Saved! Time %.3f seconds; Size %s", elapse_t, get_size_format())
  3383. StatusText.TextColor3 = Color3.new(0, 1)
  3384. task.wait(Log10 * 2 + ExtraTime)
  3385. else
  3386. if Loading then
  3387. task.cancel(Loading)
  3388. Loading = nil
  3389. end
  3390. StatusText.Text = "Failed! Check F9 console for more info"
  3391. StatusText.TextColor3 = Color3.new(1)
  3392. warn("Error found while saving:")
  3393. warn(err)
  3394. task.wait(Log10 + ExtraTime)
  3395. end
  3396. StatusText:Destroy()
  3397. end)
  3398. end
  3399.  
  3400. if OPTIONS.ShutdownWhenDone and ok then
  3401. game:Shutdown()
  3402. end
  3403. end
  3404. end
  3405.  
  3406. return synsaveinstance
Add Comment
Please, Sign In to add comment