InTesting

KS2Hash (KS2Hash)

Oct 13th, 2021 (edited)
126
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. -- NOTE: NOT THE SAME AS game:GetService('KeyframeSequenceProvider'):RegisterKeyframeSequence()
  2. -- - CHL
  3.  
  4. local ks2Hash = {}
  5.  
  6. local util = require(script.util)
  7. local constants = require(script.constants)
  8.  
  9. -- variables
  10. --  base
  11. local newBaseCharDict = util.misc:arrayToCharDict(constants.newBase:split'')
  12.  
  13. local str2Int = util.base:factStrToNumFromBase(newBaseCharDict)
  14. local int2Str = util.base:factNumToStrFromBase(newBaseCharDict)
  15.  
  16. local sciFiNota2Rational = util.scientificNotation:factSciFiNotifToNum(newBaseCharDict)
  17. local rational2SciFiNota = util.scientificNotation:factNumToSciFiNotif(newBaseCharDict)
  18.  
  19. --  misc
  20. local isCompressed = true
  21. local classnameSets
  22.  
  23. local defaultTagSubsitutions = not isCompressed and {
  24.     BEGIN = 'BEGIN';
  25.     END = 'END';
  26.     CREATE = 'CREATE';
  27.     ENDINDEX = 'ENDINDEX';
  28.     ENDRATIONAL = 'ENDRATIONAL';
  29.     BEGINSTRING = 'BEGINSTRING';
  30.     ENDSTRING = 'ENDSTRING';
  31.     ENDCFRAME = 'ENDCFRAME';
  32. } or {
  33.     BEGIN = 'q';
  34.     END = 'w';
  35.     CREATE = 'e';
  36.     ENDINDEX = 'r';
  37.     ENDRATIONAL = 't';
  38.     BEGINSTRING = 'y';
  39.     ENDSTRING = 'u';
  40.     ENDCFRAME = 'i';
  41. }
  42.  
  43. local cacheRef = {}
  44. local cacheRefCount = 0
  45.  
  46. -- functions
  47. function valueToNumber(value)
  48.     local newValue = {value}
  49.     local valueType = typeof(value)
  50.  
  51.     if valueType == 'string'then
  52.         newValue = {value:byte(1, #value)}
  53.     end
  54.  
  55.     return newValue
  56. end
  57.  
  58. function valueToString(value, tagSubs)
  59.     local valueType = typeof(value)
  60.     local newValue = value
  61.  
  62.     local nums = valueToNumber(value)
  63.    
  64.     -- ok this may need polishing
  65.     if valueType == 'string'then
  66.         local tagSub1 = tagSubs.BEGINSTRING;
  67.        
  68.         newValue = '||'.. tagSub1 .. '||'
  69.  
  70.         for ind, byte in next, nums do
  71.             local str = int2Str(byte)
  72.             if #str == 1 then
  73.                 str = newBaseCharDict[0] .. str
  74.             end
  75.  
  76.             nums[ind] = str
  77.         end
  78.  
  79.         newValue ..= table.concat(nums, '') .. '||'.. tagSubs.ENDSTRING ..'||'
  80.     elseif valueType == 'CFrame'then
  81.         local tagSub1 = tagSubs.ENDCFRAME
  82.        
  83.         local components = {value:GetComponents()}
  84.        
  85.         for i, component in next, components do
  86.             local scifiNotat = rational2SciFiNota(component)
  87.            
  88.             components[i] = scifiNotat.sign .. scifiNotat.rational .. '|' .. scifiNotat.exponent
  89.         end
  90.        
  91.         newValue = table.concat(components, '|') .. '||' .. tagSub1 .. '||'
  92.     end
  93.  
  94.     return newValue
  95. end
  96.  
  97. function cacheify(val)
  98.     cacheRefCount += 1
  99.     cacheRef[val] = cacheRefCount
  100. end
  101.  
  102. function serialize(inst, tagSubs)
  103.     tagSubs = tagSubs or defaultTagSubsitutions
  104.     assert(type(tagSubs) == 'table')
  105.    
  106.     local propertySet = assert(classnameSets[inst.ClassName])
  107.  
  108.     local res = '||' .. tagSubs.CREATE .. '||'
  109.  
  110.     -- start with classname
  111.     local className = inst.ClassName
  112.     local refIndex = cacheRef[className]
  113.  
  114.     if refIndex then
  115.         res ..= int2Str(refIndex) .. '||' .. tagSubs.ENDINDEX .. '||'
  116.     else
  117.         res ..= valueToString(className, tagSubs)
  118.         cacheify(className)
  119.     end
  120.  
  121.     -- cache instance
  122.     cacheify(inst)
  123.  
  124.     -- do properties
  125.     for _, set in next, propertySet do
  126.         if type(set) == 'string'then
  127.             set = {set}
  128.         end
  129.  
  130.         local instValue = inst[set[1]]
  131.  
  132.         local pType = set[2] or typeof(instValue)
  133.  
  134.         local expectedStr
  135.  
  136.         -- for each type of value
  137.         if pType == 'Instance' then
  138.             local vInst = cacheRef[instValue] or 0
  139.  
  140.             expectedStr = int2Str(vInst) .. '||'.. tagSubs.ENDINDEX .. '||'
  141.         elseif pType == 'string'then
  142.             local refIndex = cacheRef[instValue]
  143.            
  144.             if refIndex then
  145.                 expectedStr = int2Str(refIndex) .. '||'.. tagSubs.ENDINDEX .. '||'
  146.             else
  147.                 expectedStr = valueToString(instValue, tagSubs)
  148.                 cacheify(instValue)
  149.             end
  150.            
  151.         elseif pType == 'boolean'then
  152.             local numRep = instValue and 1 or 0
  153.  
  154.             expectedStr = int2Str(numRep)
  155.         elseif pType == 'EnumItem'then
  156.             local numRep = instValue.Value
  157.  
  158.             expectedStr = int2Str(numRep)
  159.         elseif pType == 'number'then -- including ints and rationals
  160.             local num = instValue
  161.  
  162.             local sciFiNotat = rational2SciFiNota(num)
  163.  
  164.             expectedStr = sciFiNotat.sign .. sciFiNotat.rational .. '|' .. sciFiNotat.exponent .. '||'.. tagSubs.ENDRATIONAL .. '||'
  165.         elseif pType == 'CFrame'then
  166.             expectedStr = valueToString(instValue, tagSubs)
  167.         else
  168.             warn('got unknown type', pType)
  169.         end
  170.  
  171.         res ..= expectedStr
  172.     end
  173.    
  174.     return res
  175. end
  176.  
  177. -- keyframeSequence to hash
  178. function ks2Hash:export(keyframeSequence, tagSubstitutions)
  179.     tagSubstitutions = tagSubstitutions or defaultTagSubsitutions
  180.    
  181.     assert(
  182.         typeof(keyframeSequence) == 'Instance' and keyframeSequence:IsA('KeyframeSequence') and
  183.             type(tagSubstitutions) == 'table'
  184.     )
  185.    
  186.     local sameTagSubs = true
  187.    
  188.     -- check if we need to see changed tags + check if they are there
  189.     for tagFrom, tagTo in next, defaultTagSubsitutions do
  190.         assert(type(tagSubstitutions[tagFrom]) == 'string')
  191.        
  192.         if tagTo ~= tagSubstitutions[tagFrom]then
  193.             sameTagSubs = false
  194.            
  195.             break
  196.         end
  197.     end
  198.    
  199.    
  200.     local res = '\nKS2Hash conversion by CHL\n'
  201.  
  202.     if not sameTagSubs then
  203.         -- write new tag subsitutions (still considered a comment tho).
  204.         res ..= '\nGot new tag subsitutions (use as the 2nd argument)\n + You can also fix any escape sequences\n\n{'
  205.        
  206.         for tagFrom, tagTo in next, tagSubstitutions do
  207.             res ..= '\n\t' .. tagFrom .. ' = \'' .. tagTo .. '\';'
  208.         end
  209.        
  210.         res ..= '\n}\n\n'
  211.     end
  212.    
  213.     res ..= '||'.. tagSubstitutions.BEGIN .. '||'
  214.    
  215.     cacheRef = {}
  216.     cacheRefCount = 0
  217.    
  218.     res ..= serialize(keyframeSequence, tagSubstitutions)
  219.  
  220.     for _, inst in next, keyframeSequence:GetDescendants()do
  221.         res ..= serialize(inst, tagSubstitutions)
  222.     end
  223.  
  224.     res ..= '||' .. tagSubstitutions.END .. '||'
  225.    
  226.     return res
  227. end
  228.  
  229. -- hash to keyframeSequence
  230. function ks2Hash:import(str, keyframeSequenceParent, tagSubs)
  231.     keyframeSequenceParent = keyframeSequenceParent or workspace
  232.     tagSubs = tagSubs or defaultTagSubsitutions
  233.    
  234.     assert(type(str) == 'string' and typeof(keyframeSequenceParent) == 'Instance' and
  235.         type(tagSubs) == 'table')
  236.    
  237.     for i in next, defaultTagSubsitutions do
  238.         assert(type(tagSubs[i]) == 'string')
  239.     end
  240.    
  241.     -- main
  242.     local p = 1
  243.     local mode = 'None'
  244.     local cache = {}
  245.     local currentInstanceIndex
  246.     local currentPropertyIndex = 1
  247.    
  248.     local result = {}
  249.  
  250.     -- functions for string fun
  251.     local function getStr(a, b)
  252.         return str:sub(a or p, b or p)
  253.     end
  254.  
  255.     local function printCharactersInPRange(chars)
  256.         chars = chars or 3
  257.         for a = -chars, chars do
  258.             print('[' .. a .. '] = ' .. getStr(p + a, p + a))
  259.         end
  260.     end
  261.  
  262.     local function checkStr(checkStr, noSkip)
  263.         local res = false
  264.  
  265.         local offset = #checkStr - 1
  266.  
  267.         res = getStr(p, p + offset) == checkStr
  268.  
  269.         if res and not noSkip then
  270.             p += offset
  271.         end
  272.  
  273.         return res
  274.     end
  275.  
  276.     local function getContentInString(beginSubStr, endSubStr)
  277.         p += (beginSubStr and #beginSubStr - 1) or -1
  278.  
  279.         local beginP = p + 1
  280.  
  281.         while not checkStr(endSubStr) do
  282.             p += 1
  283.  
  284.             if getStr() == ''then
  285.                 error('something really went wrong')
  286.             end
  287.         end
  288.  
  289.         local endP = p - #endSubStr
  290.  
  291.         local result = getStr(beginP, endP)
  292.  
  293.         return result
  294.     end
  295.  
  296.     local function getStringFromPos()
  297.         local str
  298.  
  299.         if checkStr('||' .. tagSubs.BEGINSTRING .. '||', true) then
  300.             local subHash = getContentInString('||' .. tagSubs.BEGINSTRING .. '||', '||' .. tagSubs.ENDSTRING .. '||')
  301.  
  302.             local bytes = {}
  303.  
  304.             for i = 1, #subHash / 2 do
  305.                 local byte =  str2Int(subHash:sub(i * 2 - 1, i * 2))
  306.  
  307.                 table.insert(bytes, byte)
  308.             end
  309.  
  310.             str = string.char(unpack(bytes))
  311.  
  312.             table.insert(cache, str)
  313.         else
  314.             local subHash = getContentInString(nil, '||' .. tagSubs.ENDINDEX .. '||')
  315.             local cacheIndex = str2Int(subHash)
  316.            
  317.             str = cache[cacheIndex]
  318.            
  319.             if not str then
  320.                 print(cacheIndex, cache)
  321.             end
  322.         end
  323.  
  324.         return str
  325.     end
  326.  
  327.     -- the main loop
  328.     while true do
  329.         local char = getStr()
  330.  
  331.         -- end if we reach there
  332.         if char == '' then
  333.             break
  334.         end
  335.  
  336.         -- the body
  337.         if mode == 'None'and checkStr('||'.. tagSubs.BEGIN ..'||') then
  338.             mode = 'Begin'
  339.         elseif mode == 'Begin'then
  340.             if checkStr('||'.. tagSubs.END .. '||') then
  341.                 mode = 'None'
  342.             elseif checkStr('||'.. tagSubs.CREATE .. '||') then
  343.                 mode = 'Create'
  344.             end
  345.         elseif mode == 'Create'then
  346.             if checkStr('||'.. tagSubs.END .. '||')then
  347.                 mode = 'None'
  348.             else
  349.                 --create instance
  350.                 local className = getStringFromPos()
  351.                
  352.                 local inst = Instance.new(className, workspace)
  353.                
  354.                 if className == 'KeyframeSequence'then
  355.                     table.insert(result, inst)
  356.                 end
  357.                
  358.                 table.insert(cache, inst)
  359.  
  360.                 currentInstanceIndex = #cache
  361.  
  362.                 mode = 'PropertyChange'
  363.             end
  364.         elseif mode == 'PropertyChange'then
  365.             if checkStr('||' .. tagSubs.END .. '||')then
  366.                 mode = 'None'
  367.             else
  368.                 local inst = cache[currentInstanceIndex]
  369.                 local propertiesSet = classnameSets[inst.ClassName]
  370.  
  371.                 local property = propertiesSet[currentPropertyIndex]
  372.                 local pType
  373.  
  374.                 if type(property)=='table'then
  375.                     pType = property[2]
  376.                     property = property[1]
  377.                 end
  378.  
  379.                 pType = pType or typeof(inst[property])
  380.  
  381.                 local newValue
  382.                
  383.                 if pType == 'Instance' then
  384.                     local subHash = getContentInString(nil, '||' .. tagSubs.ENDINDEX .. '||')
  385.                     local index = str2Int(subHash)
  386.                    
  387.                     newValue = cache[index] or keyframeSequenceParent
  388.                 elseif pType == 'string'then
  389.                     newValue = getStringFromPos()
  390.                 elseif pType == 'boolean'then
  391.                     local numRep = getStr()
  392.  
  393.                     newValue = str2Int(numRep) == 1
  394.                 elseif pType == 'EnumItem' then
  395.                     local numRep = getStr()
  396.  
  397.                     newValue = str2Int(numRep)
  398.                 elseif pType == 'number'then
  399.                     local scifiNotif = getContentInString(nil, '||' .. tagSubs.ENDRATIONAL .. '||'):split'|'
  400.                    
  401.                     local rational, exponent = unpack(scifiNotif)
  402.                     local sign, absRational = rational:sub(1,1), rational:sub(2)
  403.                    
  404.                     local num = sciFiNota2Rational{
  405.                         exponent = exponent;
  406.                         sign = sign;
  407.                         rational = absRational
  408.                     }
  409.                    
  410.                     newValue = num
  411.                 elseif pType == 'CFrame'then
  412.                     local strs = getContentInString(nil, '||' .. tagSubs.ENDCFRAME .. '||'):split'|'
  413.                    
  414.                     local componentsArgs = {}
  415.                    
  416.                     local currentSciFiNotat
  417.                    
  418.                     for i, str in next, strs do
  419.                         local isRational = i % 2 == 1
  420.                        
  421.                         if isRational then
  422.                             -- rational + sign
  423.                             currentSciFiNotat = {
  424.                                 sign = str:sub(1, 1);
  425.                                 rational = str:sub(2)
  426.                             }
  427.                         else
  428.                             -- exponent
  429.                             currentSciFiNotat.exponent = str
  430.                            
  431.                             local num = sciFiNota2Rational(currentSciFiNotat)
  432.                            
  433.                             table.insert(componentsArgs, num)
  434.                         end
  435.                     end
  436.                    
  437.                     newValue = CFrame.new(unpack(componentsArgs))
  438.                 else
  439.                     error'got unknown type'
  440.                 end
  441.                
  442.                 inst[property] = newValue
  443.  
  444.                 currentPropertyIndex += 1
  445.  
  446.                 if currentPropertyIndex > #propertiesSet then
  447.                     mode = 'Begin'
  448.                     currentPropertyIndex = 1
  449.                 end
  450.             end
  451.         end
  452.  
  453.         p += 1
  454.     end
  455.    
  456.     return result
  457. end
  458.  
  459. -- assign
  460. classnameSets = {
  461.     KeyframeSequence = {
  462.         {'Parent', 'Instance'};
  463.         'Name';
  464.         'Loop';
  465.         'Priority'
  466.     };
  467.     Keyframe = {
  468.         {'Parent', 'Instance'};
  469.         'Time';
  470.     };
  471.     Pose = {
  472.         {'Parent', 'Instance'};
  473.         'Name';
  474.         'CFrame';
  475.         'EasingDirection';
  476.         'EasingStyle';
  477.         'Weight'
  478.     }
  479. }
  480.  
  481. -- return
  482. return ks2Hash
RAW Paste Data