NoTextForSpeech

asdsdsd

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