local S={}local N=function(S,N)local G={}for S,N in ipairs(N:GetEnumItems())do G[N.Value]=N end S[N]=G end for G,p in ipairs(Enum:GetEnums())do N(S,p)end return{EnumItem=function(N,G,p,H)if N then return string.pack("\073\050",H.Value)else return S[Enum[p]][string.unpack("\073\050",H)]end end;ColorSequence=function(S,N)if S then local S=""local G=string.packsize("\102 \073\049 \073\049 \073\049")for N,G in ipairs(N.Keypoints)do local p=G local H=p.Value local m,x,f=math.floor(H.R*255),math.floor(H.G*255),math.floor(H.B*255)local j=string.pack("\102 \073\049 \073\049 \073\049",p.Time,m,x,f)S=S..j end return S else local S={}local G=string.packsize("\102 \073\049 \073\049 \073\049")for p=1,#N,G do local H=N:sub(p,p+G)local m,x,f,j=string.unpack("\102 \073\049 \073\049 \073\049",H)table.insert(S,ColorSequenceKeypoint.new(m,Color3.new(x/255,f/255,j/255)))end return ColorSequence.new(S)end end,ColorSequenceKeypoint=function(S,N)if S then local S=N.Value local G,p,H=math.floor(S.R*255),math.floor(S.G*255),math.floor(S.B*255)return string.pack("\102 \073\049 \073\049 \073\049",N.Time,G,p,H)else local S,G,p,H=string.unpack("\102 \073\049 \073\049 \073\049",N)return ColorSequenceKeypoint.new(S,Color3.new(G/255,p/255,H/255))end end;NumberSequence=function(S,N)if S then local S=""local G=getNativeSize(nil)local p=G*3 for N,G in ipairs(N.Keypoints)do local p=deflate(nil,G.Time,G.Value,G.Envelope)S=S..p end return S else local S={}local G=getNativeSize(nil)local p=G*3 for G=1,#N,p do local H=N:sub(G,G+p)local m,x,f=flate(nil,H,3)table.insert(S,NumberSequenceKeypoint.new(m,x,f))end return NumberSequence.new(S)end end,NumberSequenceKeypoint=function(S,N)if S then return deflate(nil,N.Time,N.Value,N.Envelope)else local S,G,p=flate(nil,N,3)return NumberSequenceKeypoint.new(S,G,p)end end,Rect=function(S,N)if S then return deflate(nil,N.Min.X,N.Min.Y,N.Max.X,N.Max.Y)else local S,G,p,H=flate(nil,N,4)return Rect.new(S,G,p,H)end end,Ray=function(S,N)if S then return deflate(nil,N.Orgin.X,N.Orgin.Y,N.Orgin.Z,N.Direction.X,N.Direction.Y,N.Direction.Z)else local S,G,p,H,m,x=flate(nil,N,6)return Ray.new(Vector3.new(S,G,p,H,m,x))end end;PhysicalProperties=function(S,N)if S then return deflate(nil,N.Density,N.Friction,N.Elasticity,N.FrictionWeight,N.ElasticityWeight)else local S,G,p,H,m=flate(nil,N,5)return PhysicalProperties.new(S,G,p,H,m)end end,NumberRange=function(S,N)if S then return deflate(nil,N.Min,N.Max)else local S,G=flate(nil,N,2)return NumberRange.new(S,G)end end;UDim=function(S,N)if S then return deflate(nil,N.Scale,N.Offset)else local S,G=flate(nil,N,2)return UDim2.new(S,G)end end;Color3=function(S,N)if S then local S,G,p=math.round(N.R*255),math.round(N.G*255),math.round(N.B*255)return deflate("\073\049",S,G,p)else local S,G,p=flate("\073\049",N,3)local H,m,x=S/255,G/255,p/255 return Color3.new(H,m,x)end end,UDim2=function(S,N)if S then return deflate(nil,N.X.Scale,N.X.Offset,N.Y.Scale,N.Y.Offset)else local S,G,p,H=flate(nil,N,4)return UDim2.new(S,G,p,H)end end,Vector3=function(S,N)if S then if N then return deflate(nil,N.X,N.Y,N.Z)end else local S,G,p=flate(nil,N,3)return Vector3.new(S,G,p)end end;Vector3int16=function(S,N)if S then if N then return deflate("\105\050",N.X,N.Y,N.Z)end else local S,G,p=flate("\105\050",N,3)return Vector3.new(S,G,p)end end,Vector2=function(S,N)if S then if N then return deflate(nil,N.X,N.Y)end else local S,G=flate(nil,N,2)return Vector2.new(S,G)end end;Vector2int16=function(S,N)if S then if N then return deflate("\105\050",N.X,N.Y)end else local S,G=flate("\105\050",N,2)return Vector2.new(S,G)end end,Content=function(S,N)return N end,ProtectedString=function(S,N)return N end,string=function(S,N)return N end,bool=function(S,N)if S then return({[true]="\035";[false]="\036"})[N]else return({["\035"]=true;["\036"]=false})[N]end end,float=function(S,N)if S then return deflate("\102",N)else local S=flate("\102",N,1)return S end end;double=function(S,N)if S then return deflate("\100",N)else local S=flate("\100",N,1)return S end end,int=function(S,N)if S then return deflate("\105",math.floor(N))else local S=flate("\105",N,1)return S end end;int64=function(S,N)if S then return deflate("\105\056",math.floor(N))else local S=flate("\105\056",N,1)return S end end;SurfaceType=function(N,G)if N then return deflate(nil,G.Value)else local N=flate(nil,G,1)return S[Enum.SurfaceType][N]end end,BrickColor=function(S,N)if S then return deflate(nil,math.floor(N.Number))else local S=flate(nil,N,1)return BrickColor.new(S)end end,Material=function(N,G)if N then return deflate(nil,G.Value)else local N=flate(nil,G,1)return S[Enum.Material][N]end end,Faces=function(S,N)if S then local S=splitbyte(string.char(0))for N,G in ipairs(table.pack(N.Top,N.Bottom,N.Left,N.Right,N.Back,N.Front))do S[N]=G end return formbyte(N)else local S={}local G=splitbyte(N)for N,G in ipairs(G)do if N<=5 then S[N]=G end end return Faces.new(table.unpack(S))end end;CFrame=function(S,N)if S then return deflate(nil,N:components())else local S,G,p,H,m,x,f,j,D,W,y,c=flate(nil,N,12)return CFrame.new(S,G,p,H,m,x,f,j,D,W,y,c)end end,CoordinateFrame=function(S,N)if S then return deflate(nil,N:components())else local S,G,p,H,m,x,f,j,D,W,y,c=flate(nil,N,12)return CFrame.new(S,G,p,H,m,x,f,j,D,W,y,c)end end}

local EnumStorage = {} 
local cache = function(storage,enum) 
	local Table  = {}
	for _,v in ipairs(enum:GetEnumItems()) do
		Table[v.Value] = v 
	end
	storage[enum] = Table
end 

for i,enum in ipairs(Enum:GetEnums()) do 
	cache(EnumStorage,enum)
end


return {
	-- Comment for other developers who want to make their own serizliers!
	-- This is stupid complicated, alot of contextual information is used to make this work 
	-- Instance -> Propertyname -> Class -> Class.Name | SubEnum -> EnumValue 
	-- or  Instance|[PropertyName->ClassName(APIInstance[PropertyName].Class)][Value] (as little as 5bytes!)
	-- Irregular Conversion ! < More Context > , certain value are contextual such as SubEnum[PropertyName/Class]
	--ENCODE: Store EnumValue, SubEnum is contextual. 
	--DECODE: Index = Enum[SubEnum] | EnumStorage [ Index ] [ EnumValue ] - > Enum[SubEnum][EnumValue]	
	["EnumItem"] = function(isClass,API,SubEnum,EnumValue) 
		if isClass then 
			return string.pack("I2",EnumValue.Value)
		else 	
			return EnumStorage[Enum[SubEnum]][string.unpack("I2",EnumValue)]	
		end
	end,
	-- Normal Conversion 
 	["ColorSequence"] = function(isClass,ColorSequenceValue) 
		if isClass then 
			local encodeStr = ""
			local blockSize =  string.packsize("f I1 I1 I1")
			for i,v in ipairs(ColorSequenceValue.Keypoints) do 
				local ColorKeypoint = v 
				local C3 = ColorKeypoint.Value
				local r, g, b = math.floor(C3.R*255), math.floor(C3.G*255), math.floor(C3.B*255)
				local block =  string.pack("f I1 I1 I1",ColorKeypoint.Time,r,g,b) --  further optimizations are possible to store
				encodeStr=encodeStr..block 
			end
			return encodeStr 
		else 
			local array  = {} 
			local blockSize =  string.packsize("f I1 I1 I1")
			for i=1,#ColorSequenceValue,blockSize do 
				local block = ColorSequenceValue:sub(i,i+blockSize) 
				local Time , r,g,b  = string.unpack("f I1 I1 I1",block) 
				table.insert(array,ColorSequenceKeypoint.new(Time,Color3.new(r/255,g/255,b/255)))
			end
			return ColorSequence.new(array)
		end
	end,
	["ColorSequenceKeypoint"] = function(isClass,ColorKeypoint) 
		if isClass then 
			local C3 = ColorKeypoint.Value
			local r, g, b = math.floor(C3.R*255), math.floor(C3.G*255), math.floor(C3.B*255)
			return string.pack("f I1 I1 I1",ColorKeypoint.Time,r,g,b) --  further optimizations are possible to store
		else
			local Time , r,g,b  = string.unpack("f I1 I1 I1",ColorKeypoint)
			return ColorSequenceKeypoint.new(Time,Color3.new(r/255,g/255,b/255))
		end
	end,
	["NumberSequence"] = function(isClass,NumberSequenceValue) 
		if isClass then 
			-- Basic binary array 
			local encodeStr = ""
			local nativeFloatSize = getNativeSize(nil) 
			local blockSize = nativeFloatSize*3 
			for i,v in ipairs(NumberSequenceValue.Keypoints) do 
				local block = deflate(nil,v.Time,v.Value,v.Envelope)
				encodeStr = encodeStr..block 
			end 
			
			return encodeStr
		else
			local array = {} 
			local nativeFloatSize = getNativeSize(nil) 
			local blockSize = nativeFloatSize*3 
			for i=1,#NumberSequenceValue,blockSize do 
				local block = NumberSequenceValue:sub(i,i+blockSize) 
				local a,b,c = flate(nil,block,3) 
				table.insert(array,NumberSequenceKeypoint.new(a,b,c))
			end
			--warn(array)
			return NumberSequence.new(array)
		end
	end,
	["NumberSequenceKeypoint"] = function(isClass,NumberKeypoint)
		if isClass then 
			return deflate(nil,NumberKeypoint.Time,NumberKeypoint.Value,NumberKeypoint.Envelope)
		else 
			local a,b,c = flate(nil,NumberKeypoint,3) 
			return NumberSequenceKeypoint.new(a,b,c)
		end
	end,
	["Rect"] = function(isClass,RectValue)
		if isClass then 
			return deflate(nil,RectValue.Min.X,RectValue.Min.Y,RectValue.Max.X,RectValue.Max.Y)
		else 
			local a,b,c,d = flate(nil,RectValue,4)
			return Rect.new(a,b,c,d)
		end
	end,
	["Ray"] = function(isClass,RayValue) 
		if isClass then 
			return deflate(nil,RayValue.Orgin.X,RayValue.Orgin.Y,RayValue.Orgin.Z,RayValue.Direction.X,RayValue.Direction.Y,RayValue.Direction.Z)
		else 
			local x,y,z,x1,y1,z1 = flate(nil,RayValue,6)
			return Ray.new(Vector3.new(x,y,z,x1,y1,z1))
		end
	end,
	["PhysicalProperties"] = function(isClass,PhysicalPropertiesValue) 
		if isClass then 
			return deflate(nil,PhysicalPropertiesValue.Density,PhysicalPropertiesValue.Friction,PhysicalPropertiesValue.Elasticity,
				PhysicalPropertiesValue.FrictionWeight,PhysicalPropertiesValue.ElasticityWeight)
		else 
			local a,b,c,d,e = flate(nil,PhysicalPropertiesValue,5)
			return PhysicalProperties.new(a,b,c,d,e)
		end
	end,
	["NumberRange"] = function(isClass,NumberRangeValue) 
		if isClass then 
			return deflate(nil,NumberRangeValue.Min,NumberRangeValue.Max)
		else 
			local a,b = flate(nil,NumberRangeValue,2)
			return NumberRange.new(a,b)
		end
	end,
	["UDim"] = function(isClass,value)
		if isClass then 
			return deflate(nil,value.Scale,value.Offset) 
		else 
			local a,b = flate(nil,value,2)
			return UDim2.new(a,b)
		end
	end,
	["Color3"] = function(isClass,C3) 
		if isClass then 
			local r, g, b = math.round(C3.R*255), math.round(C3.G*255), math.round(C3.B*255)
			return deflate("I1",r,g,b)	
		else 
			local r1,g2,b2 = flate("I1",C3,3) 
			local r,g,b = r1/255,g2/255,b2/255
			return Color3.new(r,g,b)
		end
	end,
	["UDim2"] = function(isClass,value)
		if isClass then
			return  deflate(nil,value.X.Scale,value.X.Offset,value.Y.Scale,value.Y.Offset)
		else 
			local a,b,c,d = flate(nil,value,4)
			return UDim2.new(a,b,c,d)
		end
	end,
	["Vector3"] = function(isClass,vector) 
		if isClass then 
			if vector then 
				return deflate(nil,vector.X,vector.Y,vector.Z)
			end
		else 
			local X,Y,Z = flate(nil,vector,3)
			return Vector3.new(X,Y,Z)
		end
	end,
	["Vector3int16"] = function(isClass,vector) 
		if isClass then 
			if vector then 
				return deflate("i2",vector.X,vector.Y,vector.Z)
			end
		else 
			local X,Y,Z = flate("i2",vector,3)
			return Vector3.new(X,Y,Z)
		end
	end,
	["Vector2"] = function(isClass,vector) 
		if isClass then 
			if vector then 
				return deflate(nil,vector.X,vector.Y)
			end
		else 
			local X,Y = flate(nil,vector,2)
			return Vector2.new(X,Y)
		end
	end,
	["Vector2int16"] = function(isClass,vector) 
		if isClass then 
			if vector then 
				return deflate("i2",vector.X,vector.Y)
			end
		else 
			local X,Y = flate("i2",vector,2)
			return Vector2.new(X,Y)
		end
	end,
	["Content"]= function(isClass,str) 
		return str
	end,
	["ProtectedString"] = function(isClass,str) 
		return str
	end,
	["string"] = function(isClass,str) 
		return str 
	end,
	["bool"] = function(isClass,bool) 
		if isClass then 
			return ({[true]="#",[false]="$"})[bool]
		else 
			return ({["#"]=true,["$"]=false})[bool]
		end
	end,
	["float"] = function(isCLass,float) 
		if isCLass then 
			return deflate("f",float)
		else 
			local a = flate("f",float,1)
			return a 
		end
	end,
	["double"] = function(isCLass,float) 
		if isCLass then 
			return deflate("d",float)
		else 
			local a = flate("d",float,1)
			return a 
		end
	end,
	["int"] = function(isCLass,float) 
		if isCLass then 
			return deflate("i",math.floor(float))
		else 
			local a = flate("i",float,1)
			return a 
		end
	end,
	["int64"] = function(isCLass,float) 
		if isCLass then 
			return deflate("i8",math.floor(float))
		else 
			local a = flate("i8",float,1)
			return a 
		end
	end,
	["SurfaceType"] = function(isClass,surfaceType) 
		if isClass then 
			return deflate(nil,surfaceType.Value)
		else 
			local id = flate(nil,surfaceType,1)
			return EnumStorage[Enum.SurfaceType][id]
		end
	end,
	["BrickColor"] = function(isClass,brickColor)  
		if isClass then 
			return deflate(nil,math.floor(brickColor.Number))
		else 
			local id = flate(nil,brickColor,1)
			return BrickColor.new(id)
		end
	end,
	["Material"] = function(isClass,material)
		if isClass then
			return deflate(nil,material.Value)
		else  
			local id = flate(nil,material,1)
			return EnumStorage[Enum.Material][id]
		end
	end,
	["Faces"] = function(isClass,faces) 
		if isClass then 
			local byte = splitbyte(string.char(0))
			for i,v in ipairs(table.pack(faces.Top,faces.Bottom,faces.Left,faces.Right,faces.Back,faces.Front)) do 
				byte[i] = v 
			end
			-- table.unpack removes the tuple for some reason ?  
			return formbyte(faces)
		else 
			local face = {}
			local newValues = splitbyte(faces)
			for i,v in ipairs(newValues) do 
				if i <= 5 then 
					face[i] = v
				end
			end
			return Faces.new(table.unpack(face))
		end
	end,
	["CFrame"] = function(isClass,Cframe) 
		if isClass then 
			return deflate(nil,Cframe:components())
		else 
			-- yeah just thank string.unpack!
			local a,b,c,d,e,f,g,h,i,j,k,l = flate(nil,Cframe,12)
			return CFrame.new(a,b,c,d,e,f,g,h,i,j,k,l)
		end
	end,
	["CoordinateFrame"] = function(isClass,Cframe) 
		if isClass then 
			 return deflate(nil,Cframe:components())
		else 
			local a,b,c,d,e,f,g,h,i,j,k,l = flate(nil,Cframe,12)
			return CFrame.new(a,b,c,d,e,f,g,h,i,j,k,l)
		end
	end
}
