AnimX importer done

Finished the importer for now, it may contain errors but those would be part of the library
This commit is contained in:
989onan
2024-12-15 16:09:18 -05:00
parent ff4ae71832
commit 2337449a77
6 changed files with 287 additions and 212 deletions
+14 -5
View File
@@ -1,12 +1,21 @@
import ctypes
import typing
import struct
import struct
from io import BytesIO
def writeNullable(data: BytesIO, value: = None):
data.write(struct.pack("?", value == None))
if(value == None):
return
data.write()
def ReadCSharp_str(data: BytesIO) -> str:
charamount = read7bitEncoded_int(data)
string: str = data.read(charamount).decode('utf-8', errors="replace")
print("read string: "+string)
#print("read string: "+string)
return string
def WriteCSharp_str(data: BytesIO, string: str) -> str:
@@ -29,7 +38,7 @@ def read7bitEncoded_ulong(data: BytesIO) -> int:
return num
def read7bitEncoded_int(data: BytesIO) -> int:
num: int= int(0)
num: int = int(0)
num2:int = int(0)
while (num2 != 35):
b: int = int(struct.unpack('<B', data.read(1))[0])
@@ -41,7 +50,7 @@ def read7bitEncoded_int(data: BytesIO) -> int:
def write7bitEncoded_ulong(data: BytesIO, integer: int) -> None:
while integer > int(0):
b: int = ctypes.c_ubyte(integer & int(127))
b: int = int(integer & int(127))
integer >>= 7
if integer > int(0):
b |= 128
@@ -54,4 +63,4 @@ def write7bitEncoded_int(data: BytesIO, value: int) -> None:
while(num >= int(128)):
data.write(int(num | int(128)))
num >>= 7
data.Write(int(num))
data.Write(int(num))
+154 -110
View File
@@ -1,5 +1,6 @@
from __future__ import annotations
from os import replace
from re import S
from types import FrameType
import lz4.block
@@ -10,6 +11,7 @@ import typing
import struct
from io import BytesIO
KeyframeInterpolation: dict[str, int] = {
"Hold": 1,
"Linear": 2,
@@ -18,28 +20,17 @@ KeyframeInterpolation: dict[str, int] = {
}
class KeyFrame():
time: resonite_types.float = resonite_types.float(0)
interpolation: resonite_types.int = resonite_types.int(0)
time: resonite_types.float
interpolation: resonite_types.byte
value: resonite_types.ResoType
left_tan: resonite_types.ResoType
right_tan: resonite_types.ResoType
def __getattr__(self, name: str):
if name == "interpolation":
interp: int = 0
if (self["left_tan"] != None and self["right_tan"] != None):
interp = 3
return resonite_types.int(interp)
return super().__getattribute__(name)
def __init__(self):
pass
self.time = resonite_types.float(0)
self.interpolation = resonite_types.byte(0)
def RequiresTangents(self) -> bool:
@@ -48,14 +39,17 @@ class KeyFrame():
return False
class ResoTrack(resonite_types.ResoType):
node: resonite_types.string = resonite_types.string("")
property: resonite_types.string = resonite_types.string("")
node: resonite_types.string
property: resonite_types.string
Owner: AnimX
FrameType: type[resonite_types.ResoType]
keyframes: list[KeyFrame] = []
FrameType: str
keyframes: list[KeyFrame]
def __init__(self,FrameType):
self.FrameType = FrameType
self.keyframes = []
self.node = resonite_types.string("")
self.property = resonite_types.string("")
def write(self, data: BytesIO):
self.node.write(data)
@@ -65,10 +59,12 @@ class ResoTrack(resonite_types.ResoType):
def read(self, data:BytesIO):
self.node.read(data)
self.property.read(data)
track_amount: int = int(common.read7bitEncoded_ulong(data))
#print(track_amount)
for i in range(0, track_amount):
key: KeyFrame = KeyFrame()
key.value = self.FrameType()
key.value = eval(self.FrameType+"()")
self.keyframes.append(key)
def removeKeyframe(self, time: float | int) -> bool:
@@ -143,28 +139,35 @@ class ResoTrack(resonite_types.ResoType):
class RawTrack(ResoTrack):
interval: resonite_types.float = resonite_types.float(0)
interval: resonite_types.float
def __getattr__(self, name: str):
if name == "interval":
return self.Owner.interval.x
return self.Owner.interval
return super().__getattribute__(name)
def __init__(self, FrameType):
super().__init__(FrameType)
self.interval = resonite_types.float(0)
def write(self, data: BytesIO):
super().write(data)
self.interval.write(data)
for key in self.keyframes:
key.value.write(data)
if self.FrameType == "resonite_types.string":
resonite_types.writeNullable(data, key.value)
else:
key.value.write(data)
def read(self, data:BytesIO):
super().read(data)
self.interval.read(data)
for key in self.keyframes:
key.value.read(data)
if self.FrameType == "resonite_types.string":
resonite_types.readNullable(data, key.value)
else:
key.value.read(data)
def addKeyframe(self, keyframe: KeyFrame) -> int:
num: int = super().addKeyframe(keyframe)
@@ -187,10 +190,28 @@ class DiscreteTrack(ResoTrack):
def write(self, data: BytesIO):
super().write(data)
self.interval.write(data)
for key in self.keyframes:
if key.value == None:
key.value = eval(self.FrameType+"()")
if self.FrameType == "resonite_types.string":
resonite_types.writeNullable(data, key.value)
else:
key.value.write(data)
key.time.write(data)
def read(self, data:BytesIO):
super().read(data)
self.interval.read(data)
for key in self.keyframes:
if key.value == None:
key.value = eval(self.FrameType+"()")
if self.FrameType == "resonite_types.string":
resonite_types.readNullable(data, key.value)
else:
key.value.read(data)
key.time.read(data)
def addKeyframe(self, keyframe: KeyFrame) -> int:
num: int = super().addKeyframe(keyframe)
@@ -203,23 +224,27 @@ class DiscreteTrack(ResoTrack):
class CurveTrack(ResoTrack):
interpolations: bool = False
tangents: bool = False
sharedinterpolation: resonite_types.int = resonite_types.int(-1)
interpolations: bool
tangents: bool
sharedinterpolation: resonite_types.byte
def __getattr__(self, name: str):
if name == "interpolations":
integerframe: int = self.keyframes[0].interpolation.x
for key in self.keyframes:
if key.interpolation.x != self.sharedinterpolation.x:
if key.interpolation.x != integerframe:
return True
elif name == "tangents":
for key in self.keyframes:
if key.interpolation.x == 3 or key.interpolation.x == 4:
if key.RequiresTangents():
return True
return super().__getattribute__(name)
def __init__(self, FrameType):
super().__init__(FrameType)
self.sharedinterpolation = resonite_types.byte(-1)
self.interpolations = False
self.tangents = False
def write(self, data: BytesIO):
super().write(data)
@@ -234,45 +259,57 @@ class CurveTrack(ResoTrack):
for key in self.keyframes:
if key.value == None:
key.value = self.FrameType()
key.value.write(data)
key.value = eval(self.FrameType+"()")
if self.FrameType == "resonite_types.string":
resonite_types.writeNullable(data, key.value)
else:
key.value.write(data)
key.time.write(data)
if(self.tangents):
for key in self.keyframes:
key.left_tan.write(data)
key.right_tan.write(data)
if self.FrameType == "resonite_types.string":
resonite_types.writeNullable(data, key.left_tan)
resonite_types.writeNullable(data, key.right_tan)
else:
key.left_tan.write(data)
key.right_tan.write(data)
def read(self, data:BytesIO):
super().read(data)
flags: int = struct.unpack("<B",data.read(1))[0]
self.interpolations = (flags & 1) > 0
self.tangents = (flags & 2) > 0
interp: bool = (flags & 1) > 0
tan: bool = (flags & 2) > 0
print(str(self.interpolations))
print(str(self.tangents))
#print(str(interp))
#print(str(tan))
#print(flags)
#print(len(self.keyframes))
if(self.interpolations):
if(interp):
for key in self.keyframes:
#print("reading interp")
key.interpolation.read(data)
else:
self.sharedinterpolation.read(data)
for key in self.keyframes:
print("key read!")
if key.value == None:
key.value = self.FrameType()
key.value.read(data)
key.value = eval(self.FrameType+"()")
if self.FrameType == "resonite_types.string":
resonite_types.readNullable(data, key.value)
else:
key.value.read(data)
key.time.read(data)
if(self.tangents):
if(tan):
for key in self.keyframes:
if key.left_tan == None:
key.left_tan = self.FrameType()
if key.right_tan == None:
key.right_tan = self.FrameType()
key.left_tan.read(data)
key.right_tan.read(data)
if self.FrameType == "resonite_types.string":
resonite_types.readNullable(data, key.left_tan)
resonite_types.readNullable(data, key.right_tan)
else:
key.left_tan.read(data)
key.right_tan.read(data)
@@ -313,56 +350,56 @@ class BezierTrack(ResoTrack):
"""PLACE HOLDER METHOD, DO NOT USE"""
raise Exception("BezierTrack track type is unsupported in resonite's code")
#This is weird, but thank you python - @989onan
TrackTypes: list[type] = [
RawTrack,
DiscreteTrack,
CurveTrack,
BezierTrack
TrackTypes: list[str] = [
"RawTrack",
"DiscreteTrack",
"CurveTrack",
"BezierTrack"
]
#TODO: add all types here
#wooooo - @989onan
elementTypes: list[type[resonite_types.ResoType]] = [
resonite_types.bool,
resonite_types.bool2,
resonite_types.bool3,
resonite_types.bool4,
resonite_types.byte,
resonite_types.ushort,
resonite_types.uint,
resonite_types.ulong,
resonite_types.sbyte,
resonite_types.short,
resonite_types.int,
resonite_types.long,
resonite_types.int2,
resonite_types.int3,
resonite_types.int4,
resonite_types.uint2,
resonite_types.uint3,
resonite_types.uint4,
resonite_types.long2,
resonite_types.long3,
resonite_types.long4,
resonite_types.float,
resonite_types.float2,
resonite_types.float3,
resonite_types.float4,
resonite_types.floatQ,
resonite_types.float2x2,
resonite_types.float3x3,
resonite_types.float4x4,
resonite_types.double,
resonite_types.double2,
resonite_types.double3,
resonite_types.double4,
resonite_types.doubleQ,
resonite_types.double2x2,
resonite_types.double3x3,
resonite_types.double4x4,
resonite_types.color,
resonite_types.color32,
resonite_types.string
elementTypes: list[str] = [
"resonite_types.bool",
"resonite_types.bool2",
"resonite_types.bool3",
"resonite_types.bool4",
"resonite_types.byte",
"resonite_types.ushort",
"resonite_types.uint",
"resonite_types.ulong",
"resonite_types.sbyte",
"resonite_types.short",
"resonite_types.int",
"resonite_types.long",
"resonite_types.int2",
"resonite_types.int3",
"resonite_types.int4",
"resonite_types.uint2",
"resonite_types.uint3",
"resonite_types.uint4",
"resonite_types.long2",
"resonite_types.long3",
"resonite_types.long4",
"resonite_types.float",
"resonite_types.float2",
"resonite_types.float3",
"resonite_types.float4",
"resonite_types.floatQ",
"resonite_types.float2x2",
"resonite_types.float3x3",
"resonite_types.float4x4",
"resonite_types.double",
"resonite_types.double2",
"resonite_types.double3",
"resonite_types.double4",
"resonite_types.doubleQ",
"resonite_types.double2x2",
"resonite_types.double3x3",
"resonite_types.double4x4",
"resonite_types.color",
"resonite_types.color32",
"resonite_types.string"
]
@@ -372,20 +409,27 @@ class AnimX():
"""
To use Raw Track properly, please set interval (seconds between frames) after reading/creating.\n
Represents data to be written to or read from an AnimX file.
To use Raw Track properly, please set interval (seconds between frames) after creating.\n
Represents data to be written to or read from an AnimX file.\n
default interval to use would be 30.
"""
file_version: resonite_types.int = resonite_types.int()
track_amount: resonite_types.int = resonite_types.int()
global_duration: resonite_types.float = resonite_types.float()
name: resonite_types.string = resonite_types.string()
file_version: resonite_types.int
track_amount: resonite_types.int
global_duration: resonite_types.float
name: resonite_types.string
tracks: list[ResoTrack] = []
tracks: list[ResoTrack]
interval: resonite_types.float = resonite_types.float(1/25) #default value
interval: resonite_types.float
def __init__(self):
self.tracks = []
self.file_version = resonite_types.int()
self.track_amount = resonite_types.int()
self.global_duration = resonite_types.float()
self.name = resonite_types.string()
self.interval = resonite_types.float(1/25) #default value
pass
@classmethod
@@ -424,7 +468,7 @@ class AnimX():
self.track_amount.x = common.read7bitEncoded_ulong(data)
self.global_duration.read(data)
print("track amont: "+str(self.track_amount.x))
print("track amount: "+str(self.track_amount.x))
print("file vers: "+str(self.file_version.x))
self.name.read(data)
@@ -453,8 +497,8 @@ class AnimX():
data.read(8) #fuck off stream headers - @989onan
data.read(8) #fuck off stream headers - @989onan
filelmza: bytes = bytes(AnimX.decompress_lzma(data.read(), lzma.FORMAT_RAW, filters))
print(f"decompressed bytes: {filelmza[:100]}")
#print("binary below:")
#print("b'{}'".format(''.join('\\x{:02x}'.format(b) for b in filelmza[:100])))
data = BytesIO(filelmza)
case _:
raise Exception("Invalid encoding")
@@ -515,10 +559,10 @@ class AnimX():
return True
@classmethod
def GetTrackType(cls, trackType2: int, value_type: type[resonite_types.ResoType], data: BytesIO) -> ResoTrack:
Track = TrackTypes[trackType2](value_type)
print(type(Track))
print(value_type)
def GetTrackType(cls, trackType2: int, value_type: str, data: BytesIO) -> ResoTrack:
Track: ResoTrack = eval(TrackTypes[trackType2]+"(value_type)")
#print(value_type)
#print(type(Track))
Track.read(data)
return Track
+24 -13
View File
@@ -17,6 +17,19 @@ class ResoType():
def read(cls, data: BytesIO):
pass
def writeNullable(data: BytesIO, value: ResoType = None):
data.write(struct.pack("?", value == None))
if(value == None):
return
value.write(data)
def readNullable(data: BytesIO, value: ResoType = None):
hasval: bool = struct.unpack("?", data.read(1))
if not hasval:
return
value.read(data)
#These below are collection of the basic resonite typing made from C#. This is in order to store data in a sane way and decode/encode it.
class color(ResoType):
@@ -75,8 +88,8 @@ class byte(ResoType):
def __int__(self):
return self.x
def __init__(self):
pass
def __init__(self,value=0):
self.x = value
def write(self, data: BytesIO):
data.write(struct.pack("<B", self.x))
@@ -120,8 +133,8 @@ class short(ResoType):
def __int__(self):
return self.x
def __init__(self):
pass
def __init__(self,value=0):
self.x = value
def write(self, data: BytesIO):
data.write(struct.pack("<h", self.x))
@@ -264,7 +277,7 @@ class int4(int3):
pass
def read(self,data: BytesIO):
super().write(data)
super().read(data)
self.w = struct.unpack("<i", data.read(4))[0]
def write(self, data: BytesIO):
@@ -298,7 +311,7 @@ class uint2(uint):
pass
def read(self,data: BytesIO):
super().write(data)
super().read(data)
self.y = struct.unpack("<I", data.read(4))[0]
def write(self, data: BytesIO):
@@ -312,7 +325,7 @@ class uint3(uint2):
pass
def read(self,data: BytesIO):
super().write(data)
super().read(data)
self.z = struct.unpack("<I", data.read(4))[0]
def write(self, data: BytesIO):
@@ -326,7 +339,7 @@ class uint4(uint3):
pass
def read(self,data: BytesIO):
super().write(data)
super().read(data)
self.w = struct.unpack("<I", data.read(4))[0]
def write(self, data: BytesIO):
@@ -529,8 +542,7 @@ class doubleQ(double4):
def write(self, data: BytesIO):
super().write(data)
@classmethod
def read(cls, data: BytesIO):
def read(self, data: BytesIO):
super().read(data)
@@ -587,7 +599,7 @@ class float4(float3):
def __init__(self):
pass
def read(self,data: BytesIO):
def read(self, data: BytesIO):
super().read(data)
self.w = struct.unpack("<f", data.read(4))[0]
@@ -649,6 +661,5 @@ class floatQ(float4):
def write(self, data: BytesIO):
super().write(data)
@classmethod
def read(cls, data: BytesIO):
def read(self, data: BytesIO):
super().read(data)