Huge Update

This commit is contained in:
htt-py
2023-10-10 20:28:27 +02:00
parent 7e13b72012
commit 4cf4b71904
3 changed files with 1799 additions and 203 deletions

View File

@@ -10,16 +10,16 @@ local synsaveinstance = loadstring(game:HttpGet(Params.RepoURL .. Params.SSI ..
# Syn Save Instance
Or shortly SSI, a project aimed at resurrecting saveinstance function from [2019 Synapse X leaked source code](https://github.com/Acrillis/SynapseX).<br />
Or shortly SSI, a project aimed at resurrecting saveinstance function from [Synapse X Source 2019].<br />
Reason: Many Executors fail miserably at providing good user experience when it comes to tinkering with saving instances
# TO-DOs:
- [ ] Add `continue` where needed
- [ ] Add Documentation similar to [KRNL](https://app.archbee.com/public/PREVIEW-2Jp4SDaAD4P1COFfx1p_t/PREVIEW-EtjA4sQe5zYUxIHwA6CqJ#mDB9D) or [Synapse X](https://docs.synapse.to/reference/misc.html?highlight=saveins#save-instance) / [Synapse X Old](https://synapsexdocs.github.io/custom-lua-functions/misc-functions/#save-instance)
- [ ] Add fallback function for appendfile (whether through storing current xml as string or with use of readfile)
- [x] Add `continue` where needed
- [ ] Add Documentation similar to [KRNL Docs] or [Synapse X Docs] / [Synapse X Docs Old]
- [x] ~~Add fallback function for appendfile (whether through storing current xml as string or with use of readfile)~~ Removed Appendfile entirely
- [x] Add getproperties as fallback for specialinfo
- [ ] Add Redirects to some special (in a bad way 😡) values, more info @ ~~[PropertyPatches](https://github.com/MaximumADHD/Roblox-File-Format/blob/main/Plugins/GenerateApiDump/PropertyPatches.lua#L72)~~ [PropertyPatches](https://github.com/rojo-rbx/rbx-dom/tree/master/patches)+[Bonus](https://github.com/rojo-rbx/rbx-dom/blob/9716af360307eb5da5f97d54d84d694b2bc06acf/rbx_dom_lua/src/customProperties.lua), otherwise they will fallback to default when file is opened
- [ ] Add Redirects to some special (in a bad way 😡) values, more info @ ~~[PropertyPatches v1]~~ [PropertyPatches v2]+[PropertyPatches v3], otherwise they will fallback to default when file is opened
- Not all though, test each & see if it carries over or not (when file is opened)..
- All current redirects: [Here](https://github.com/luau/SynSaveInstance/blob/main/TODO/PropertyPatches)
- [ ] Add more Fixes for Errors that **_can_** pop up during opening process
@@ -30,70 +30,110 @@ Reason: Many Executors fail miserably at providing good user experience when it
- [x] Add --!native tag just in case
- [ ] Find default values of binarystring properties (MaximumADHD might have a clue)
- LOOK INTO Instance:IsPropertyModified & Instance:ResetPropertyToDefault
- [ ] ~~Auto-Detect DataTypes/ValueType Categories of Properties (CFrame, UDim2 so on)~~ Full API Dump Solves this ?
- [x] ~~Auto-Detect DataTypes/ValueType Categories of Properties (CFrame, UDim2 so on)~~ Full API Dump Solves this ?
- [x] Bring said DataType serializer into an outside function
- [ ] ~~Bypass NotCreatable by hardcoding links/references/indexes to said Classes
- [ ] ~~Bypass NotCreatable by hardcoding links/references/indexes to said Classes~~ Should be Solved by IsPropertyModified
- Example: Terrain class can be indexed by doing workspace.Terrain but is NotCreatable
- [x] Check if table.concat is actually the fastest way as compared to other alternatives (IT'S NOT)
- [ ] Do ~~clean-up in inheritor &~~ (API Dumps solve this, illogical) automatically assume the top-most class that owns the property, while also cleaning up said property from classes that inherit from it
- [x] Do ~~clean-up in inheritor &~~ (API Dumps solve this, illogical) automatically assume the top-most class that owns the property, while also cleaning up said property from classes that inherit from it
- This will be only be needed if we try to implement our own scanning for hidden properties in which case a lot of duplicates might arise that need to be tracked down to instance they all inherit from & cleaned up respectively
- [x] Fix indexes being mixed up after table.remove shifting
- [x] Hidden properties
- [x] ~~Scan for them~~ Full API Dump Solves this
- [ ] ~~Scan game & map instances in format {ClassName = {Instance1, Instance2} }, if none found then attempt to create proper Replica for them~~ Full API Dump Solves this
- [x] ~~Scan game & map instances in format {ClassName = {Instance1, Instance2} }, if none found then attempt to create proper Replica for them~~ Full API Dump Solves this
* This will help with getting many ValueTypes accurately, especially BinaryStrings vs strings
- [] ~~Inherit them properly & do the clean-up~~ Full API Dump Solves this
- [x] ~~Inherit them properly & do the clean-up~~ Full API Dump Solves this
- [x] ~~Tell whether ValueType is string or BinaryString~~ Full API Dump Solves this
- [ ] Look into adding support for Binary Format Output (rbxl/rbxm)
- .RBXL files are similar to .RBXLX files but are saved in Binary format, which helps reduce the file size.
- ! Check out string.pack & string.unpack for more information !
- ! Check out string.pack/.unpack & bit32 library for more information !
- [ ] Support for Model files:
- [x] rbxmx (xml)
- [ ] rbxm (binary)
- [ ] ~~Possibly convert to non-Name tables & use instance references instead (Perhaps make a config Bool Toggle for this, false by default), ex. DecompileIgnore = {game.CoreGui}~~ Add too much complexity for now
- This will allow for more flexibility of saveinstancing
- [x] ~~Remove Useless tables & functions of specialinfo~~ Repurposed
- [ ] Replace all operators with compound operators if possible, make sure to add other LuaU syntax too (matters for performance!)
- [x] Implement [Luau Syntax] (matters for performance!):
- [x] Compound Operators
- [x] Avoid using `next`, `ipairs` & `pairs`
- [ ] Type-checking (😩🙀)
- [ ] `local maxValue = if a > b then a else b` expressions
- [ ] print(`Bob has {count} apple(s)!`) expressions
- [] Floor division
- [ ] Speed things up as much as possible
- Requires benchmarks
- Requires looking at other scripts of ours that are aimed at speed & performance
- [x] Support for NotScriptable Properties
- Requires gethiddenproperty support
- [ ] Support for as many [KRNL-like saveinstance options](https://app.archbee.com/public/PREVIEW-2Jp4SDaAD4P1COFfx1p_t/PREVIEW-EtjA4sQe5zYUxIHwA6CqJ#mDB9D):
- [ ] Support for as many [KRNL-like saveinstance Options]:
- Change mode to invalid mode like "custom" if you only want to save ExtraInstances
* [x] Decompile (! This takes priority over OPTIONS.noscripts if set !)
* [x] DecompileIgnore
* [x] DecompileTimeout (! This takes priority over OPTIONS.timeout if set !)
* [x] ExtraInstances
* [x] FilePath
* [x] IgnoreDefaultProps
* [x] IsolateStarterPlayer
* [x] NilInstances
* [x] Object (for .rbxmx files)
* [x] RemovePlayerCharacters
* [x] SavePlayers
* [x] ShowStatus
* [ ] IsolatePlayerGui
- [ ] Support for as many Executors as possible (🤢🤮)
- [x] ~~Use getspecialinfo fallback function carefully as it's hardcoded~~ Useless because there's no way to tell if the Property Values of those instances are default or not
- LOOK INTO Instance:IsPropertyModified & Instance:ResetPropertyToDefault
- [ ] Isolators must clear
- [x] Isolators must clear
- [x] Store all functions outside that are used during saveinstancing for sake of performance
- [ ] Remove buffersize, savebuffer & so on for sake of performance by concatenating <Item> strings to total string then writing it to file (no extra steps like table.concat)
- [x] ~~Remove buffersize, savebuffer & so on for sake of performance by concatenating <Item> strings to total string then writing it to file (no extra steps like table.concat)~~ table.concat proved faster in the case of huge amount of concatenations
- Test table.concat vs string ..= with a full buffer (this benchmark differs per usecase)
- [ ] Make sure BinaryStrings are compared to Defaults properly (aka in same format)
- [ ] Add Option to restart saveinstance from the point that it crashed on
- [ ] Check out DataType exceptions [Here](https://github.com/rojo-rbx/rbx-dom/blob/master/rbx_reflector/src/cli/generate.rs#L260)
- [ ] Check out [DataType Exceptions]
- [x] Add README Similar to current Synapse
- [ ] Ignore all properties of instances that aren't Local or Module Scripts except Name if mode is set to "scripts"
- [ ] Maybe modes should do more than just affecting the list of instances to save, like changing IgnoreDefaultProperties to false if mode is "full" for example
- [x] Add Support for SharedStrings [Docs](https://github.com/RobloxAPI/spec/blob/master/formats/rbxlx.md#sharedstring)
- Fun fact: 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). By replacing `<BinaryString name="Tags">Base64EncodedValue</BinaryString>` with `<SharedString name="Tags">UniqueIdentifierForSharedString</SharedString>`and putting `<SharedString md5="UniqueIdentifierForSharedString">Base64EncodedValue</SharedString>` into SharedStrings container you can achieve this amazing behaviour. This should be only enabled using an optional setting ()<br />Only known to work with (probably because both are base64 encoded):
- [x] Add Support for [SharedStrings]
- Fun fact: 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). By replacing `<BinaryString name="Tags">Base64EncodedValue</BinaryString>` with `<SharedString name="Tags">UniqueIdentifierForSharedString</SharedString>` & putting `<SharedString md5="UniqueIdentifierForSharedString">Base64EncodedValue</SharedString>` into SharedStrings container you can achieve this amazing behaviour. This should be only enabled using an optional setting<br />Only known to work with (probably because both are base64 encoded):
* BinaryString
- [ ] Add Lua & Luau versions instead of merged
- [x] Add Lua & Luau versions instead of merged (WARNING: LUAU WILL ALWAYS BE MORE UPDATED THAN LUA VERSION - IDC & CBA SIMPLY, lua version exists just for sake of old & bad executors, ask devs of your executors to support luau as its latest & greatest)
- [ ] Add Support for multiple Instances to be saved as a model
- [ ] Do something about devs renaming Services therefore bypassing Ignore lists (CoreGui/CorePackages are not affected)
- LOOK INTO Instance:IsPropertyModified & Instance:ResetPropertyToDefault
- [ ] Fix Player's Characters not being visible (must Refresh MeshId)
- "https://assetdelivery.roblox.com/v1/asset/?id=" Could cause issues too (needs testing)
- Perhaps add a possible FIX script to README
- [ ] Be able to exclude / blacklist any mentions of certain string in other strings
- Example: You wish to blacklist your player's name from appearing in any property value
- Default options like IsolateSomething might also use / influence this
- [ ] Force disable ParticleEmitters in case something like IgnorePropertiesOfNotScriptsOnScriptsMode is enabled (they stack in one place and create huge lag)
# Acknowledgments
This document is based largely on the efforts of [@Anaminus](https://github.com/Anaminus) & [@Dekkonot](https://github.com/Dekkonot), authors of the [Rbxlx Format Specifications](https://github.com/RobloxAPI/spec/blob/master/formats/rbxlx.md). Additional
This document is based largely on the efforts of [@Anaminus] & [@Dekkonot], authors of the [Roblox Format Specifications]. Additional
resources include:
- [Syngp Synapse X 2019 Source code](https://github.com/Acrillis/SynapseX) for base saveinstance code (extended by [@mblouka](https://github.com/mblouka) & [@Acrillis](https://github.com/Acrillis))
- [Roblox File Format](https://github.com/MaximumADHD/Roblox-File-Format) for a list of redirects of old/deprecated xml properties that still use the old tag values
- [Roblox Client Tracker](https://github.com/MaximumADHD/Roblox-Client-Tracker) for an extended & close to full JSON Api Dump (with hidden properties & default values)
- [Syngp Synapse X Source code 2019][Synapse X Source 2019] for base saveinstance code (extended by [@mblouka] & [@Acrillis])
- [Roblox File Format] for a list of redirects of old/deprecated xml properties that still use the old tag values
- [Roblox Client Tracker] for an extended & close to full JSON Api Dump (with hidden properties & default values)
[@Acrillis]: https://github.com/Acrillis
[@Anaminus]: https://github.com/Anaminus
[@Dekkonot]: https://github.com/Dekkonot
[@mblouka]: https://github.com/mblouka
[DataType Exceptions]: https://github.com/rojo-rbx/rbx-dom/blob/master/rbx_reflector/src/cli/generate.rs#L260
[KRNL Docs]: https://app.archbee.com/public/PREVIEW-2Jp4SDaAD4P1COFfx1p_t/PREVIEW-EtjA4sQe5zYUxIHwA6CqJ#mDB9D
[KRNL-like saveinstance Options]: https://app.archbee.com/public/PREVIEW-2Jp4SDaAD4P1COFfx1p_t/PREVIEW-EtjA4sQe5zYUxIHwA6CqJ#mDB9D
[Luau Syntax]: https://luau-lang.org/syntax
[Roblox Client Tracker]: https://github.com/MaximumADHD/Roblox-Client-Tracker
[Roblox File Format]: https://github.com/MaximumADHD/Roblox-File-Format
[Roblox Format Specifications]: https://github.com/RobloxAPI/spec/
[SharedStrings]: https://github.com/RobloxAPI/spec/blob/master/formats/rbxlx.md#sharedstring
[Synapse X Docs Old]: https://synapsexdocs.github.io/custom-lua-functions/misc-functions/#save-instance
[Synapse X Docs]: https://docs.synapse.to/reference/misc.html?highlight=saveins#save-instance
[Synapse X Source 2019]: https://github.com/Acrillis/SynapseX
[PropertyPatches v1]: https://github.com/MaximumADHD/Roblox-File-Format/blob/main/Plugins/GenerateApiDump/PropertyPatches.lua#L72
[PropertyPatches v2]: https://github.com/rojo-rbx/rbx-dom/tree/master/patches
[PropertyPatches v3]: https://github.com/rojo-rbx/rbx-dom/blob/9716af360307eb5da5f97d54d84d694b2bc06acf/rbx_dom_lua/src/customProperties.lua

1411
saveinstance.lua Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -8,8 +8,9 @@ local finder, globalcontainer = loadstring(game:HttpGet(Params.RepoURL .. Params
finder({
-- readbinarystring = '(...):find("bin",nil,true)', -- ! Could match some unwanted stuff
decompile = '(...):find("decomp",nil,true) or (...):find("assembl",nil,true)',
decompile = '((...):find("decomp",nil,true) and (...):sub(#...) ~= "s") or (...):find("assembl",nil,true)',
gethiddenproperty = '(...):find("get",nil,true) and (...):find("h",nil,true) and (...):find("prop",nil,true) and (...):sub(#...) ~= "s"',
sethiddenproperty = '(...):find("set",nil,true) and (...):find("h",nil,true) and (...):find("prop",nil,true) and (...):sub(#...) ~= "s"',
gethui = '(...):find("get",nil,true) and (...):find("h",nil,true) and (...):find("ui",nil,true)',
getnilinstances = '(...):find("nil",nil,true)', -- ! Could match some unwanted stuff
getproperties = '(...):find("get",nil,true) and (...):find("prop",nil,true) and (...):sub(#...) == "s"',
@@ -20,6 +21,7 @@ finder({
local decompile = globalcontainer.decompile
local gethiddenproperty = globalcontainer.gethiddenproperty
local sethiddenproperty = globalcontainer.sethiddenproperty
local getproperties = globalcontainer.getproperties
local writefile = globalcontainer.writefile
@@ -105,10 +107,11 @@ Descriptors = {
__BINARYSTRING = Base64_Encode,
__BIT = function(...) -- * Credits to Friend (you know yourself)
local Value = 0
for Index, Bit in { ... } do
local PackedArgs = { ... }
for Index = 1, #PackedArgs do
local Bit = PackedArgs[Index]
if Bit then
Value += 2 ^ (Index - 1)
Value = Value + 2 ^ (Index - 1)
end
end
@@ -196,8 +199,8 @@ Descriptors = {
local Converted = ""
local Keypoints = raw.Keypoints
for _index_2 = 1, #Keypoints do
local v = Keypoints[_index_2]
for Index = 1, #Keypoints do
local v = Keypoints[Index]
local Value = v.Value
Converted ..= v.Time .. " " .. Value.R .. " " .. Value.G .. " " .. Value.B .. " 0 " -- * " 0" is Envelope: Has the range 0 - 1. Currently unused by Roblox.
end
@@ -233,8 +236,8 @@ Descriptors = {
--The value is the text content, formatted as a space-separated list of floating point numbers.
local Converted = ""
local Keypoints = raw.Keypoints
for _index_2 = 1, #Keypoints do
local v = Keypoints[_index_2]
for Index = 1, #Keypoints do
local v = Keypoints[Index]
Converted ..= v.Time .. " " .. v.Value .. " " .. v.Envelope .. " "
end
@@ -395,8 +398,11 @@ if getproperties then
globalcontainer.getspecialinfo = function(instance)
local specialinfo = getproperties(instance)
for Property, Value in getreal(instance) do
specialinfo[Property] = Value
local ok, result = pcall(getreal, instance) -- * Some executors only allow certain Classes for this method (like UnionOperation, MeshPart, Terrain), for example Electron
if ok then
for Property, Value in result do
specialinfo[Property] = Value
end
end
return specialinfo
@@ -411,10 +417,24 @@ local getspecialinfo = globalcontainer.getspecialinfo
local function getsafeproperty(instance, PropertyName)
return instance[PropertyName]
end
local function setsafeproperty(instance, PropertyName, Value)
instance[PropertyName] = Value
end
local function IsPropertyModified(instance, ProperyName)
return instance:IsPropertyModified(ProperyName)
end
local function ResetPropertyToDefault(instance, ProperyName)
instance:ResetPropertyToDefault(ProperyName)
end
local function SetProperty(instance, PropertyName, Value)
local ok = pcall(setsafeproperty, instance, PropertyName, Value)
if not ok then
ok = pcall(sethiddenproperty, instance, PropertyName, Value)
end
return ok
end
local function ReadProperty(Property, instance, PropertyName, specialProperties, Special)
local raw
@@ -482,11 +502,29 @@ end
local ldeccache = {}
local function ArrayToDictionary(Table)
local function ArrayToDictionary(Table, HybridMode)
local tmp = table.create(#Table)
for _, Key in Table do
tmp[Key] = true
if HybridMode == "table" then
for Some1, Some2 in Table do
if type(Some1) == "number" then
tmp[Some2] = true
else
tmp[Some1] = ArrayToDictionary(Some2, "table") -- Some1 is Class, Some2 is Name
end
end
elseif HybridMode == "bool" then
for Some1, Some2 in Table do
if type(Some1) == "number" then
tmp[Some2] = true
else
tmp[Some1] = Some2
end
end
else
for _, Key in Table do
tmp[Key] = true
end
end
return tmp
@@ -656,7 +694,7 @@ local referents = setmetatable({
})
local function ReturnItem(ClassName, instance)
return '<Item class="' .. ClassName .. '" referent="' .. referents[instance] .. '"><Properties>'
return '<Item class="' .. ClassName .. '" referent="' .. referents[instance] .. '"><Properties>' -- TODO: Ideally this shouldn't return <Properties> as well as the line below to close it IF IgnorePropertiesOfNotScriptsOnScriptsMode is ENABLED
end
local function ReturnProperty(Tag, PropertyName, Value)
return "<" .. Tag .. ' name="' .. PropertyName .. '">' .. Value .. "</" .. Tag .. ">"
@@ -721,6 +759,7 @@ StatusText.TextStrokeTransparency = 0.7
StatusText.TextXAlignment = Enum.TextXAlignment.Right
StatusText.TextYAlignment = Enum.TextYAlignment.Top
local ScriptsClasses = ArrayToDictionary({ "LocalScript", "ModuleScript", Script = false }, "bool")
local function synsaveinstance(CustomOptions)
table.clear(SharedStrings)
@@ -735,31 +774,47 @@ local function synsaveinstance(CustomOptions)
-- decomptype = "new", -- * Deprecated
timeout = 30,
--* New:
__DEBUG_MODE = false,
__DEBUG_MODE = false, -- Recommended to enable if you wish to help us improve our products and find bugs / issues with it!
DecompileIgnore = { -- * Clean these up (merged Old Syn and New Syn)
"Chat",
"TextChatService",
},
}, -- Descendants of ClassNames specified here will be saved but not decompiled
--[[ Explanation of structure for DecompileIgnore
{
"Chat", - This affects any descendants of instance with "Chat" ClassName
Players = {"MyPlayerName"} - - This affects any descendants of instance with "Players" .Class AND "MyPlayerName" .Name ONLY
}
]]
InstancesBlacklist = { "CoreGui", "CorePackages" },
--[[ Explanation of structure for InstancesBlacklist
{
"CoreGui", - This affects any descendants of instance with "Chat" ClassName
Players = {"MyPlayerName"} - - This affects any descendants of instance with "Players" .Class AND "MyPlayerName" .Name ONLY
}
]]
ExtraInstances = {},
NilInstances = true,
ShowStatus = true,
Instance = false, -- If provided, saves as .rbxmx (Model file) instead -- ! MUST BE AN INSTANCE REFERENCE like game.Workspace for example; "optimized" mode is NOT supported with this option
-- Binary = false, -- true in actualy syn newer versions
FilePath = false, -- does not need to contain a file extension, only the name of the file.
Object = false, -- 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 like game.Workspace for example; "optimized" mode is NOT supported with this option
-- Binary = false, -- true in syn newer versions (false in our case because no binary support yet)
-- Decompile = not OPTIONS.noscripts, -- ! This takes priority over OPTIONS.noscripts if set
-- DecompileTimeout = OPTIONS.timeout, -- ! This takes priority over OPTIONS.timeout if set
IgnoreDefaultProperties = true,
IgnoreSpecialProperties = false, -- true will disable Terrain too
IgnoreNotArchivable = true,
-- IsolateStarterPlayer = true,
IsolateLocalPlayer = true, -- Saves Children of LocalPlayer as separate folder and prevents any instance with .Name identical to LocalPlayer.Name from saving
IgnorePropertiesOfNotScriptsOnScriptsMode = false, -- Ignores property of every instance that is not a script in "scripts" mode
IgnoreSpecialProperties = false, -- true will disable Terrain & some other things
-- IsolatePlayerGui = false,
IsolateStarterPlayer = true, --If enabled, StarterPlayer will be cleared and the saved starter player will be placed into folders.
IsolateLocalPlayer = false, -- Saves Children of LocalPlayer as separate folder and prevents any instance of ClassName Player with .Name identical to LocalPlayer.Name from saving
IsolateLocalPlayerCharacter = false, -- Saves Children of LocalPlayer.Character as separate folder and prevents any instance of ClassName Player with .Name identical to LocalPlayer.Name from saving
-- MaxThreads = 3
-- RemovePlayerCharacters = true,
RemovePlayerCharacters = true, -- If enabled, player characters will not be saved.
SavePlayers = false,
SaveCacheInterval = 0x1600,
SaveCacheInterval = 0x1600, -- The less the more often it saves, but that would mean less performance due to constantly saving
ReadMe = true,
-- ! Risky
-- AllowResettingProperties = false, -- Enables Resetting of properties for sake of checking their default value (Useful for cases when Instance is NotCreatable like services yet we need to get the default value ) then sets the property back to the original value, which might get detected by some games
AllowResettingProperties = false, -- Enables Resetting of properties for sake of checking their default value (Useful for cases when Instance is NotCreatable like services yet we need to get the default value ) then sets the property back to the original value, which might get detected by some games --! WARNING: Sometimes Properties might not be able to be set to the original value due to circumstances
SharedStringOverwrite = false, -- ! 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)
}
@@ -777,6 +832,10 @@ local function synsaveinstance(CustomOptions)
if DecompileTimeout ~= nil then
OPTIONS.timeout = DecompileTimeout
end
local IgnoreDefaultProps = CustomOptions.IgnoreDefaultProps
if IgnoreDefaultProps ~= nil then
OPTIONS.IgnoreDefaultProperties = IgnoreDefaultProps
end
else
CustomOptions = {}
end
@@ -809,7 +868,13 @@ local function synsaveinstance(CustomOptions)
return "-- Decompiling is NOT supported on your executor"
end
end
local ToSaveInstance = OPTIONS.Instance
local ToSaveInstance = OPTIONS.Object
if ToSaveInstance == game then
ToSaveInstance = nil
end
local FilePath = OPTIONS.FilePath
local IgnorePropertiesOfNotScriptsOnScriptsMode = OPTIONS.IgnorePropertiesOfNotScriptsOnScriptsMode
local placename
local ToSaveList
@@ -822,6 +887,12 @@ local function synsaveinstance(CustomOptions)
if mode == "optimized" then -- ! NOT supported with .rbxmx
mode = "full"
end
if not CustomOptions.IsolateLocalPlayerCharacter then
OPTIONS.IsolateLocalPlayerCharacter = false
end
if not CustomOptions.IsolateStarterPlayer then
OPTIONS.IsolateStarterPlayer = false
end
if not CustomOptions.IsolateLocalPlayer then
OPTIONS.IsolateLocalPlayer = false
end
@@ -829,9 +900,13 @@ local function synsaveinstance(CustomOptions)
OPTIONS.NilInstances = false
end
placename = "model" .. PlaceId .. "_" .. ToSaveInstance:GetDebugId(0) .. ".rbxmx" -- * GetDebugId is only unique per instance within same game session, after rejoining it might be different
placename = (FilePath or "model" .. PlaceId .. "_" .. ToSaveInstance:GetDebugId(0)) .. ".rbxmx" -- * GetDebugId is only unique per instance within same game session, after rejoining it might be different
else
placename = "place" .. PlaceId .. ".rbxlx"
placename = (FilePath or "place" .. PlaceId) .. ".rbxlx"
end
if mode ~= "scripts" then
IgnorePropertiesOfNotScriptsOnScriptsMode = nil
end
local TempRoot = ToSaveInstance or game
@@ -869,9 +944,8 @@ local function synsaveinstance(CustomOptions)
local cach = {}
for _index_0 = 1, #Hierarchy do
local instance = Hierarchy[_index_0]
local ClassName = instance.ClassName
if ClassName == "LocalScript" or ClassName == "ModuleScript" then
if ScriptsClasses[instance.ClassName] then
local Parent = instance.Parent
while Parent and Parent ~= TempRoot do
instance = instance.Parent
@@ -890,33 +964,37 @@ local function synsaveinstance(CustomOptions)
end
local DecompileIgnore, InstancesBlacklist =
ArrayToDictionary(OPTIONS.DecompileIgnore), ArrayToDictionary(OPTIONS.InstancesBlacklist)
ArrayToDictionary(OPTIONS.DecompileIgnore, "table"), ArrayToDictionary(OPTIONS.InstancesBlacklist, "table")
local IgnoreSpecialProperties = OPTIONS.IgnoreSpecialProperties
local IgnoreNotArchivable = OPTIONS.IgnoreNotArchivable
local __DEBUG_MODE = OPTIONS.__DEBUG_MODE
local AllowResettingProperties = OPTIONS.AllowResettingProperties
local IgnoreDefaultProperties = OPTIONS.IgnoreDefaultProperties
local IgnoreNotArchivable = OPTIONS.IgnoreNotArchivable
local IgnoreSpecialProperties = OPTIONS.IgnoreSpecialProperties
local IsolateLocalPlayer = OPTIONS.IsolateLocalPlayer
local IsolateLocalPlayerCharacter = OPTIONS.IsolateLocalPlayerCharacter
local IsolateStarterPlayer = OPTIONS.IsolateStarterPlayer
local SaveCacheInterval = OPTIONS.SaveCacheInterval
local SharedStringOverwrite = OPTIONS.SharedStringOverwrite
local __DEBUG_MODE = OPTIONS.__DEBUG_MODE
local function getsizeformat()
local size
for i, BinaryPrefix in
{
"B",
"KB",
"MB",
"GB",
"TB",
}
do
local Size
local BinaryPrefixes = {
"B",
"KB",
"MB",
"GB",
"TB",
}
for Index = 1, #BinaryPrefixes do
local buffersize = #total
if buffersize < 0x400 ^ i then
size = math.floor(buffersize / (0x400 ^ (i - 1)) * 10) / 10 .. " " .. BinaryPrefix
if buffersize < 0x400 ^ Index then
Size = math.floor(buffersize / (0x400 ^ (Index - 1)) * 10) / 10 .. " " .. BinaryPrefixes[Index]
break
end
end
return size
return Size
end
local function savecache()
@@ -937,165 +1015,190 @@ local function synsaveinstance(CustomOptions)
savecache()
end
for _index_0 = 1, #Hierarchy do
repeat
local instance = Hierarchy[_index_0]
local instance = Hierarchy[_index_0]
local ClassName = instance.ClassName
local InstanceName = instance.Name
local ClassName = instance.ClassName
local InstanceName = instance.Name
local Blacklisted = InstancesBlacklist[ClassName]
if
instance.RobloxLocked
or not ClassList[ClassName]
or IgnoreNotArchivable and not instance.Archivable
or Blacklisted and (Blacklisted == true or Blacklisted[InstanceName])
then
continue
end
if
instance.RobloxLocked
or InstancesBlacklist[InstanceName]
or not ClassList[ClassName]
or IgnoreNotArchivable and not instance.Archivable
then
break
end
if not DecompileIgnoring then
local DecompileIgnored = DecompileIgnore[ClassName]
DecompileIgnoring = DecompileIgnored
and (DecompileIgnored == true or DecompileIgnored[InstanceName])
and instance
end
if not DecompileIgnoring then
DecompileIgnoring = DecompileIgnore[InstanceName] and instance
end
local Properties = inheritedproperties[ClassName]
savebuffer[#savebuffer + 1] = ReturnItem(ClassName, instance)
local Properties = inheritedproperties[ClassName]
savebuffer[#savebuffer + 1] = ReturnItem(ClassName, instance) -- TODO: Ideally this shouldn't return <Properties> as well as the line below to close it IF IgnorePropertiesOfNotScriptsOnScriptsMode is ENABLED
local Ignored
if IgnorePropertiesOfNotScriptsOnScriptsMode and ScriptsClasses[ClassName] == nil then
Ignored = true
end
if not Ignored then
local specialProperties, Replica
for _index_1 = 1, #Properties do
local Property = Properties[_index_1]
local PropertyName = Property.Name
repeat
local Special = Property.Special
if IgnoreSpecialProperties and Special then
break
end
local raw
raw, specialProperties =
ReadProperty(Property, instance, PropertyName, specialProperties, Special)
if raw == "__BREAK" then
break
end
local ValueType = Property.ValueType
if SharedStringOverwrite and ValueType == "BinaryString" then -- TODO: Convert this to table if more types are added
ValueType = "SharedString"
local Special = Property.Special
if IgnoreSpecialProperties and Special then
continue
end
local raw
raw, specialProperties = ReadProperty(Property, instance, PropertyName, specialProperties, Special)
if raw == "__BREAK" then
continue
end
local ValueType = Property.ValueType
if SharedStringOverwrite and ValueType == "BinaryString" then -- TODO: Convert this to table if more types are added
ValueType = "SharedString"
end
local Category = Property.Category
if IgnoreDefaultProperties and PropertyName ~= "Source" then -- ? Source is special, might need to be changed to check for LuaSourceContainer IsA instead
local ok, IsModified = pcall(IsPropertyModified, instance, PropertyName) -- ? Not yet enabled lol (580)
if ok and not IsModified then
continue
end
local Category = Property.Category
local Default = Property.Default
if IgnoreDefaultProperties and PropertyName ~= "Source" then -- ? Source is special, might need to be changed to check for LuaSourceContainer IsA instead
local ok, IsModified = pcall(IsPropertyModified, instance, PropertyName) -- ? Not yet enabled lol (580)
if ok and not IsModified then
break
if OverwriteDefaults[Default] then
local ClassTags = ClassList[ClassName].Tags
local NotCreatable = ClassTags and ClassTags.NotCreatable
local Reset
if NotCreatable then -- TODO: This whole block should only run if Replica doesn't exist yet, except ResetPropertyToDefault because it's needed for just about every property of NotCreatable objects (in order to check default if undefined in API Dump)
if AllowResettingProperties then
Reset = pcall(ResetPropertyToDefault, instance, PropertyName)
if Reset and not Replica then
Replica = instance
end
end
elseif not Replica then
Replica = classreplicas[ClassName]
end
local Default = Property.Default
if Replica and not (NotCreatable and not Reset) then
Default = ReadProperty(Property, Replica, PropertyName, specialProperties, Special)
-- * Improve this along with specialProperties (merge or maybe store the method to Property.Special), get this property at any cost
if OverwriteDefaults[Default] then
local ClassTags = ClassList[ClassName].Tags
if not (ClassTags and ClassTags.NotCreatable) then
Replica = classreplicas[ClassName]
Default = ReadProperty(Property, Replica, PropertyName, specialProperties, Special)
-- * Improve this along with specialProperties (merge or maybe store the method to Property.Special), get this property at any cost
Default = ApiFormatify(Default, Category, ValueType)
Property.Default = Default
-- if Property.Special then
-- end
if Reset and not SetProperty(Replica, PropertyName, raw) and __DEBUG_MODE then -- It has been reset
warn(
"FAILED TO SET BACK TO ORIGINAL VALUE (OPEN A GITHUB ISSUE): ",
ValueType,
ClassName,
PropertyName
)
end
elseif Default == "default" and ValueType == "PhysicalProperties" then
Default = "nil"
Default = ApiFormatify(Default, Category, ValueType)
Property.Default = Default
-- if Property.Special then
-- end
end
if ApiFormatify(raw, Category, ValueType, Default) == Default then -- ! PhysicalProperties, Font, CFrame, BrickColor (and Enum to some extent) aren't being defaulted properly in the api dump, meaning an issue must be created.. (They're not being tostringed or fail to do so)
-- print("Default not serializing", PropertyName)
break
end
elseif Default == "default" and ValueType == "PhysicalProperties" then
Default = "nil"
Property.Default = Default
end
local tag, value
if Category == "Class" then
tag = "Ref"
if raw then
value = referents[raw]
else
value = "null"
end
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
value, tag = Descriptors.__ENUM(raw)
if ApiFormatify(raw, Category, ValueType, Default) == Default then -- ! PhysicalProperties, Font, CFrame, BrickColor (and Enum to some extent) aren't being defaulted properly in the api dump, meaning an issue must be created.. (They're not being tostringed or fail to do so)
-- print("Default not serializing", PropertyName)
continue
end
end
local tag, value
if Category == "Class" then
tag = "Ref"
if raw then
value = referents[raw]
else
local Descriptor = Descriptors[ValueType]
value = "null"
end
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
value, tag = Descriptors.__ENUM(raw)
else
local Descriptor = Descriptors[ValueType]
if Descriptor then
value, tag = ReturnValueAndTag(raw, ValueType, Descriptor)
elseif "BinaryString" == ValueType then -- TODO: Try fitting this inside Descriptors
tag = ValueType
value = Descriptors.__BINARYSTRING(raw)
if Descriptor then
value, tag = ReturnValueAndTag(raw, ValueType, Descriptor)
elseif "BinaryString" == ValueType then -- TODO: Try fitting this inside Descriptors
tag = ValueType
value = Descriptors.__BINARYSTRING(raw)
if
PropertyName == "SmoothGrid"
or PropertyName == "MaterialColors"
or PropertyName == "PhysicsGrid"
then
value = Descriptors.__CDATA(value)
end
elseif "ProtectedString" == ValueType then -- TODO: Try fitting this inside Descriptors
tag = ValueType
if
PropertyName == "SmoothGrid"
or PropertyName == "MaterialColors"
or PropertyName == "PhysicsGrid"
then
value = Descriptors.__CDATA(value)
end
elseif "ProtectedString" == ValueType then -- TODO: Try fitting this inside Descriptors
tag = ValueType
if PropertyName == "Source" then
if ClassName == "Script" then
value = "-- Server scripts can NOT be decompiled"
if PropertyName == "Source" then
if ScriptsClasses[ClassName] == false then
value = "-- Server scripts can NOT be decompiled" --TODO: Could be not just server scrippts in the future
else
if DecompileIgnoring then
value = "-- Ignored"
else
if DecompileIgnoring then
value = "-- Ignored"
else
value = ldecompile(instance)
end
value = ldecompile(instance)
end
end
end
value = Descriptors.__PROTECTEDSTRING(value)
else
--OptionalCoordinateFrame and so on, we make it dynamic
local startIDX, endIDX = Find(ValueType, "Optional")
if startIDX == 1 then
-- Extract the string after "Optional"
value = Descriptors.__PROTECTEDSTRING(value)
else
--OptionalCoordinateFrame and so on, we make it dynamic
local startIDX, endIDX = Find(ValueType, "Optional")
if startIDX == 1 then
-- Extract the string after "Optional"
Descriptor = Descriptors[ValueType:sub(endIDX + 1)]
Descriptor = Descriptors[ValueType:sub(endIDX + 1)]
if Descriptor then
if raw ~= nil then
value, tag = ReturnValueAndTag(raw, ValueType, Descriptor)
else
value, tag = "", ValueType
end
if Descriptor then
if raw ~= nil then
value, tag = ReturnValueAndTag(raw, ValueType, Descriptor)
else
value, tag = "", ValueType
end
end
end
end
end
if tag then
savebuffer[#savebuffer + 1] = ReturnProperty(tag, PropertyName, value)
elseif __DEBUG_MODE then
warn("UNSUPPORTED TYPE (OPEN A GITHUB ISSUE): ", ValueType, ClassName, PropertyName)
end
until true
end
savebuffer[#savebuffer + 1] = "</Properties>"
local Children = Afterwards or instance:GetChildren()
if #Children ~= 0 then
savehierarchy(Children)
if tag then
savebuffer[#savebuffer + 1] = ReturnProperty(tag, PropertyName, value)
elseif __DEBUG_MODE then
warn("UNSUPPORTED TYPE (OPEN A GITHUB ISSUE): ", ValueType, ClassName, PropertyName)
end
end
end
savebuffer[#savebuffer + 1] = "</Properties>"
local Children = Afterwards or instance:GetChildren()
if #Children ~= 0 then
savehierarchy(Children)
end
if DecompileIgnoring and DecompileIgnoring == instance then
DecompileIgnoring = nil
end
if DecompileIgnoring and DecompileIgnoring == instance then
DecompileIgnoring = nil
end
savebuffer[#savebuffer + 1] = "</Item>"
until true
savebuffer[#savebuffer + 1] = "</Item>"
end
end
local function saveextra(Name, Hierarchy, CustomClassName, Source)
@@ -1169,19 +1272,30 @@ local function synsaveinstance(CustomOptions)
savehierarchy(ToSaveList)
end
if OPTIONS.IsolateLocalPlayer then
if IsolateLocalPlayer or IsolateLocalPlayerCharacter then
local Players = service.Players
local LocalPlayer = Players.LocalPlayer
local LocalPlayer_Children = LocalPlayer:GetChildren()
saveextra("LocalPlayer", LocalPlayer_Children)
if IsolateLocalPlayer then
saveextra("LocalPlayer", LocalPlayer:GetChildren())
end
if IsolateLocalPlayerCharacter then
local LocalPlayerCharacter = LocalPlayer.Character
if LocalPlayerCharacter then
saveextra("LocalPlayer Character", LocalPlayerCharacter:GetChildren())
end
end
end
if IsolateStarterPlayer then
saveextra("StarterPlayer", service.StarterPlayer:GetChildren())
end
if nilinstances then
saveextra("Nil Instances", nilinstances)
end
saveextra("README", nil, "Script", "--[[\n" .. [[
if OPTIONS.ReadMe then
saveextra("README", nil, "Script", "--[[\n" .. [[
Thank you for using SynSaveInstance Revival.
We recommended to save the game right away to take advantage of the binary format (if you didn't save in binary).
We recommended to save the game right away to take advantage of the binary format (if you didn't save in binary) AND to preserve values of certain properties if you used IgnoreDefaultProperties setting (as they might change in the future).
If your player cannot spawn into the game, please move the scripts in StarterPlayer elsewhere. (This is done by default)
If the chat system does not work, please use the explorer and delete everything inside the Chat service.
Or run `game:GetService("Chat"):ClearAllChildren()`
@@ -1202,7 +1316,7 @@ local function synsaveinstance(CustomOptions)
This file was generated with the following settings:
]] .. service.HttpService:JSONEncode(OPTIONS) .. "\n]]")
end
local tmp = { "<SharedStrings>" }
for Identifier, Value in SharedStrings do
tmp[#tmp + 1] = '<SharedString md5="' .. Identifier .. '">' .. Value .. "</SharedString>"
@@ -1225,15 +1339,46 @@ local function synsaveinstance(CustomOptions)
StatusTextClone = StatusText:Clone()
StatusTextClone.Parent = StatusGui
end
if not OPTIONS.SavePlayers then
InstancesBlacklist.Players = true
if not OPTIONS.SavePlayers and not InstancesBlacklist.Players then
InstancesBlacklist.Players = {}
end
if OPTIONS.IsolateLocalPlayer then
if OPTIONS.RemovePlayerCharacters then
local Players = service.Players
local T = InstancesBlacklist.Model
if T ~= true then
if not T then
T = {}
end
for _, Player in Players:GetPlayers() do
T[Player.Name] = true
end
InstancesBlacklist.Model = T
end
end
if IsolateStarterPlayer then
InstancesBlacklist.StarterPlayer = true
end
if IsolateLocalPlayer or IsolateLocalPlayerCharacter then
local Players = service.Players
local LocalPlayer = Players.LocalPlayer
local LocalPlayerName = LocalPlayer.Name
if IsolateLocalPlayer and InstancesBlacklist.Player ~= true then
if not InstancesBlacklist.Player then
InstancesBlacklist.Player = {}
end
InstancesBlacklist[LocalPlayer.Name] = true
InstancesBlacklist.Player[LocalPlayerName] = true
end
if IsolateLocalPlayerCharacter and InstancesBlacklist.Model ~= true then
if not InstancesBlacklist.Model then
InstancesBlacklist.Model = {}
end
InstancesBlacklist.Model[LocalPlayerName] = true
end
end
do
local elapse_t = os.clock()
local ok, err = pcall(savegame)
@@ -1246,7 +1391,7 @@ local function synsaveinstance(CustomOptions)
StatusTextClone.Text = string.format("Saved! Time %.2f seconds; Size %s", elapse_t, getsizeformat())
task.wait(Log10 * 2 + 3)
else
StatusTextClone.Text = "Failed!\nCheck F9 console for more info"
StatusTextClone.Text = "Failed! Check F9 console for more info"
warn("Error encountered while saving")
warn("Information about error:")
warn(err)