diff --git a/PyCmo/PyCmo.py b/PyCmo/PyCmo.py index d43f291..0bb6ec9 100644 --- a/PyCmo/PyCmo.py +++ b/PyCmo/PyCmo.py @@ -1,6 +1,7 @@ -import VTReader -import VTStruct +import VTReader, VTStruct, VTConstants + +ckFile = VTStruct.CKFile() +VTReader.CKFileReader.Load(ckFile, "D:\\libcmo21\\PyCmo\\Language.nmo", VTConstants.CK_LOAD_FLAGS(VTConstants.CK_LOAD_FLAGS.CK_LOAD_DEFAULT)) +print(ckFile) +print(str(ckFile.m_FileObjects)) -with open("D:\\libcmo21\\PyCmo\\Gameplay.nmo", 'rb') as fs: - composition = VTReader.ReadCKComposition(fs) - print(composition.Header) \ No newline at end of file diff --git a/PyCmo/VTConstants.py b/PyCmo/VTConstants.py index c69a075..9b63ce2 100644 --- a/PyCmo/VTConstants.py +++ b/PyCmo/VTConstants.py @@ -1,50 +1,70 @@ import enum +class CKEnumItem(object): + def __init__(self, name: str, value: int): + self.name: str = name + self.value: int = value + class CKEnum(object): def __init__(self, val: int): self.m_Value: int = val + self.__ItemsList = ( + CKEnumItem(attr, getattr(self, attr)) + for attr in dir(self) if not attr.startswith("__") and not callable(getattr(self, attr)) + ) + + def Set(self, data: int): + self.m_Value = data + + def __iter__(self): + return self.__ItemsList def __repr__(self): for i in self: - if i.value == val: - return i.name + if i.value == self.m_Value: + return '<' + i.name + '>' - return "[None]" + return "<[None] {:d}>".format(self.m_Value) def __str__(self): return self.__repr__() class CKFlagEnum(CKEnum): - def Contain(self: CKFlagEnum, probe: int): - return bool(self.m_Value & probe) - - def Add(self: CKFlagEnum, data: int): + def Add(self, data: int): self.m_Value = self.m_Value | data + def __contains__(self, probe): + if isinstance(probe, int): + return bool(self.m_Value & probe) + if isinstance(probe, CKFlagEnum): + return bool(self.m_Value & probe.m_Value) + + return False + def __repr__(self): pending = [] for i in self: # if it have exactly same entry, return directly - if i.value == val: + if i.value == self.m_Value: return i.name # check exist - if bool(val & i.value): + if bool(self.m_Value & i.value): pending.append(i.name) result = ', '.join(pending) - return result if len(result) != 9 else "[None]" + return ('<' + result + '>') if len(result) != 9 else "<[None] {:d}>".format(self.m_Value) -class CK_FILE_WRITEMODE(CKFlagEnum, enum.IntEnum): +class CK_FILE_WRITEMODE(CKFlagEnum): CKFILE_UNCOMPRESSED =0 # Save data uncompressed CKFILE_CHUNKCOMPRESSED_OLD =1 # Obsolete CKFILE_EXTERNALTEXTURES_OLD=2 # Obsolete : use CKContext::SetGlobalImagesSaveOptions instead. CKFILE_FORVIEWER =4 # Don't save Interface Data within the file, the level won't be editable anymore in the interface CKFILE_WHOLECOMPRESSED =8 # Compress the whole file -class CK_LOAD_FLAGS(CKFlagEnum, enum.IntEnum): +class CK_LOAD_FLAGS(CKFlagEnum): CK_LOAD_ANIMATION =1<<0 # Load animations CK_LOAD_GEOMETRY =1<<1 # Load geometry. CK_LOAD_DEFAULT =CK_LOAD_GEOMETRY|CK_LOAD_ANIMATION # Load animations & geometry @@ -56,8 +76,85 @@ class CK_LOAD_FLAGS(CKFlagEnum, enum.IntEnum): CK_LOAD_CHECKDEPENDENCIES =1<<7 # Check if every plugins needed are availables CK_LOAD_ONLYBEHAVIORS =1<<8 # -CK_CLASSID = int -class CKCID(enum.IntEnum): +class CK_FO_OPTIONS(CKEnum): + CK_FO_DEFAULT = 0 + CK_FO_RENAMEOBJECT = 1 + CK_FO_REPLACEOBJECT = 2 + CK_FO_DONTLOADOBJECT = 3 + +CK_ID = int + +ClassHierarchy = { + 1: ("CKCID_OBJECT", ), + 2: ("CKCID_OBJECT", "CKCID_PARAMETERIN", ), + 4: ("CKCID_OBJECT", "CKCID_PARAMETEROPERATION", ), + 5: ("CKCID_OBJECT", "CKCID_STATE", ), + 6: ("CKCID_OBJECT", "CKCID_BEHAVIORLINK", ), + 8: ("CKCID_OBJECT", "CKCID_BEHAVIOR", ), + 9: ("CKCID_OBJECT", "CKCID_BEHAVIORIO", ), + 12: ("CKCID_OBJECT", "CKCID_RENDERCONTEXT", ), + 13: ("CKCID_OBJECT", "CKCID_KINEMATICCHAIN", ), + 11: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", ), + 15: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_OBJECTANIMATION", ), + 16: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_ANIMATION", ), + 18: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_ANIMATION", "CKCID_KEYEDANIMATION", ), + 19: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", ), + 52: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_DATAARRAY", ), + 10: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_SCENE", ), + 21: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_LEVEL", ), + 22: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_PLACE", ), + 23: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_GROUP", ), + 24: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_SOUND", ), + 25: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_SOUND", "CKCID_WAVESOUND", ), + 26: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_SOUND", "CKCID_MIDISOUND", ), + 30: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_MATERIAL", ), + 31: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_TEXTURE", ), + 32: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_MESH", ), + 53: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_MESH", "CKCID_PATCHMESH", ), + 47: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_RENDEROBJECT", ), + 27: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_RENDEROBJECT", "CKCID_2DENTITY", ), + 28: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_RENDEROBJECT", "CKCID_2DENTITY", "CKCID_SPRITE", ), + 29: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_RENDEROBJECT", "CKCID_2DENTITY", "CKCID_SPRITETEXT", ), + 33: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", ), + 50: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_GRID", ), + 36: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_CURVEPOINT", ), + 37: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_SPRITE3D", ), + 43: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_CURVE", ), + 34: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_CAMERA", ), + 35: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_CAMERA", "CKCID_TARGETCAMERA", ), + 38: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_LIGHT", ), + 39: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_LIGHT", "CKCID_TARGETLIGHT", ), + 40: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_CHARACTER", ), + 41: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_3DOBJECT", ), + 42: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_3DOBJECT", "CKCID_BODYPART", ), + 46: ("CKCID_OBJECT", "CKCID_PARAMETER", ), + 45: ("CKCID_OBJECT", "CKCID_PARAMETER", "CKCID_PARAMETERLOCAL", ), + 55: ("CKCID_OBJECT", "CKCID_PARAMETER", "CKCID_PARAMETERLOCAL", "CKCID_PARAMETERVARIABLE", ), + 3: ("CKCID_OBJECT", "CKCID_PARAMETER", "CKCID_PARAMETEROUT", ), + 48: ("CKCID_OBJECT", "CKCID_INTERFACEOBJECTMANAGER", ), + 49: ("CKCID_OBJECT", "CKCID_CRITICALSECTION", ), + 51: ("CKCID_OBJECT", "CKCID_LAYER", ), + 54: ("CKCID_OBJECT", "CKCID_PROGRESSIVEMESH", ), + 20: ("CKCID_OBJECT", "CKCID_SYNCHRO", ), + 80: ("CKCID_OBJECTARRAY", ), + 81: ("CKCID_SCENEOBJECTDESC", ), + 82: ("CKCID_ATTRIBUTEMANAGER", ), + 83: ("CKCID_MESSAGEMANAGER", ), + 84: ("CKCID_COLLISIONMANAGER", ), + 85: ("CKCID_OBJECTMANAGER", ), + 86: ("CKCID_FLOORMANAGER", ), + 87: ("CKCID_RENDERMANAGER", ), + 88: ("CKCID_BEHAVIORMANAGER", ), + 89: ("CKCID_INPUTMANAGER", ), + 90: ("CKCID_PARAMETERMANAGER", ), + 91: ("CKCID_GRIDMANAGER", ), + 92: ("CKCID_SOUNDMANAGER", ), + 93: ("CKCID_TIMEMANAGER", ), + -1: ("CKCID_CUIKBEHDATA", ), + 56: ("CKCID_MAXCLASSID", ), + 128: ("CKCID_MAXMAXCLASSID", ), +} +class CK_CLASSID(CKEnum): CKCID_OBJECT = 1 CKCID_PARAMETERIN = 2 CKCID_PARAMETEROPERATION = 4 @@ -129,91 +226,74 @@ class CKCID(enum.IntEnum): CKCID_MAXCLASSID = 56 CKCID_MAXMAXCLASSID = 128 - __ClassHierarchy = { - 1: ("CKCID_OBJECT", ), - 2: ("CKCID_OBJECT", "CKCID_PARAMETERIN", ), - 4: ("CKCID_OBJECT", "CKCID_PARAMETEROPERATION", ), - 5: ("CKCID_OBJECT", "CKCID_STATE", ), - 6: ("CKCID_OBJECT", "CKCID_BEHAVIORLINK", ), - 8: ("CKCID_OBJECT", "CKCID_BEHAVIOR", ), - 9: ("CKCID_OBJECT", "CKCID_BEHAVIORIO", ), - 12: ("CKCID_OBJECT", "CKCID_RENDERCONTEXT", ), - 13: ("CKCID_OBJECT", "CKCID_KINEMATICCHAIN", ), - 11: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", ), - 15: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_OBJECTANIMATION", ), - 16: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_ANIMATION", ), - 18: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_ANIMATION", "CKCID_KEYEDANIMATION", ), - 19: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", ), - 52: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_DATAARRAY", ), - 10: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_SCENE", ), - 21: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_LEVEL", ), - 22: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_PLACE", ), - 23: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_GROUP", ), - 24: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_SOUND", ), - 25: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_SOUND", "CKCID_WAVESOUND", ), - 26: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_SOUND", "CKCID_MIDISOUND", ), - 30: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_MATERIAL", ), - 31: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_TEXTURE", ), - 32: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_MESH", ), - 53: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_MESH", "CKCID_PATCHMESH", ), - 47: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_RENDEROBJECT", ), - 27: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_RENDEROBJECT", "CKCID_2DENTITY", ), - 28: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_RENDEROBJECT", "CKCID_2DENTITY", "CKCID_SPRITE", ), - 29: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_RENDEROBJECT", "CKCID_2DENTITY", "CKCID_SPRITETEXT", ), - 33: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", ), - 50: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_GRID", ), - 36: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_CURVEPOINT", ), - 37: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_SPRITE3D", ), - 43: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_CURVE", ), - 34: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_CAMERA", ), - 35: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_CAMERA", "CKCID_TARGETCAMERA", ), - 38: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_LIGHT", ), - 39: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_LIGHT", "CKCID_TARGETLIGHT", ), - 40: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_CHARACTER", ), - 41: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_3DOBJECT", ), - 42: ("CKCID_OBJECT", "CKCID_SCENEOBJECT", "CKCID_BEOBJECT", "CKCID_3DENTITY", "CKCID_3DOBJECT", "CKCID_BODYPART", ), - 46: ("CKCID_OBJECT", "CKCID_PARAMETER", ), - 45: ("CKCID_OBJECT", "CKCID_PARAMETER", "CKCID_PARAMETERLOCAL", ), - 55: ("CKCID_OBJECT", "CKCID_PARAMETER", "CKCID_PARAMETERLOCAL", "CKCID_PARAMETERVARIABLE", ), - 3: ("CKCID_OBJECT", "CKCID_PARAMETER", "CKCID_PARAMETEROUT", ), - 48: ("CKCID_OBJECT", "CKCID_INTERFACEOBJECTMANAGER", ), - 49: ("CKCID_OBJECT", "CKCID_CRITICALSECTION", ), - 51: ("CKCID_OBJECT", "CKCID_LAYER", ), - 54: ("CKCID_OBJECT", "CKCID_PROGRESSIVEMESH", ), - 20: ("CKCID_OBJECT", "CKCID_SYNCHRO", ), - 80: ("CKCID_OBJECTARRAY", ), - 81: ("CKCID_SCENEOBJECTDESC", ), - 82: ("CKCID_ATTRIBUTEMANAGER", ), - 83: ("CKCID_MESSAGEMANAGER", ), - 84: ("CKCID_COLLISIONMANAGER", ), - 85: ("CKCID_OBJECTMANAGER", ), - 86: ("CKCID_FLOORMANAGER", ), - 87: ("CKCID_RENDERMANAGER", ), - 88: ("CKCID_BEHAVIORMANAGER", ), - 89: ("CKCID_INPUTMANAGER", ), - 90: ("CKCID_PARAMETERMANAGER", ), - 91: ("CKCID_GRIDMANAGER", ), - 92: ("CKCID_SOUNDMANAGER", ), - 93: ("CKCID_TIMEMANAGER", ), - -1: ("CKCID_CUIKBEHDATA", ), - 56: ("CKCID_MAXCLASSID", ), - 128: ("CKCID_MAXMAXCLASSID", ), - } - - def __init__(self, code: CK_CLASSID): - self.code: CK_CLASSID = code def __repr__(self): - hierarchy = self.__ClassHierarchy.get(self.code, None) + hierarchy = ClassHierarchy.get(self.m_Value, None) if hierarchy is None: - return "[Undefined]" + return "[Undefined] {:d}".format(self.m_Value) return "{} ({})".format( hierarchy[-1], ' -> '.join(hierarchy) ) + def __str__(self): + hierarchy = ClassHierarchy.get(self.m_Value, None) + if hierarchy is None: + return "[Undefined] {:d}".format(self.m_Value) + return hierarchy[-1] -CKERROR = int -class CKERR(): +ErrorDescription = { + 0: "Operation successful", + -1: "One of the parameter passed to the function was invalid", + -2: "One of the parameter passed to the function was invalid", + -3: "The parameter size was invalid", + -4: "The operation type didn't exist", + -5: "The function used to execute the operation is not yet implemented", + -6: "There was not enough memory to perform the action", + -7: "The function is not yet implemented", + -11: "There was an attempt to remove something not present", + -13: "There is no level currently created", + -14: "There is no level currently created", + -16: "The notification message was not used", + -17: "Attempt to add an item that was already present", + -18: "the render context is not valid", + -19: "the render context is not activated for rendering", + -20: "there was no plugins to load this kind of file", + -21: "there was no plugins to save this kind of file", + -22: "attempt to load an invalid file", + -23: "attempt to load with an invalid plugin", + -24: "attempt use an object that wasnt initialized", + -25: "attempt use a message type that wasn't registred", + -29: "No dll file found in the parse directory", + -30: "this dll has already been registred", + -31: "this dll does not contain information to create the prototype", + -34: "Invalid Object (attempt to Get an object from an invalid ID)", + -35: "Invalid window was provided as console window", + -36: "Invalid kinematic chain ( end and start effector may not be part of the same hierarchy )", + -37: "Keyboard not attached or not working properly", + -38: "Mouse not attached or not working properly", + -39: "Joystick not attached or not working properly", + -40: "Try to link imcompatible Parameters", + -44: "There is no render engine dll", + -45: "There is no current level (use CKSetCurrentLevel )", + -46: "Sound Management has been disabled", + -47: "DirectInput Management has been disabled", + -48: "Guid is already in use or invalid", + -49: "There was no more free space on disk when trying to save a file", + -50: "Impossible to write to file (write-protection ?)", + -51: "The behavior cannnot be added to this entity", + -52: "The behavior cannnot be added to this entity", + -53: "A manager was registered more than once", + -54: "CKprocess or TimeManager process while CK is paused will fail", + -55: "Some plugins were missing whileloading a file", + -56: "Virtools version too old to load this file", + -57: "CRC Error while loading file", + -58: "A Render context is already in Fullscreen Mode", + -59: "Operation was cancelled by user", + -121: "there were no animation key at the given index", + -122: "attemp to acces an animation key with an invalid index", + -123: "the animation is invalid (no entity associated or zero length)", +} +class CKERROR(CKEnum): CKERR_OK = 0 CKERR_INVALIDPARAMETER = -1 CKERR_INVALIDPARAMETERTYPE = -2 @@ -265,61 +345,6 @@ class CKERR(): CKERR_INVALIDINDEX = -122 CKERR_INVALIDANIMATION = -123 - __ErrorDescription = { - 0: "Operation successful", - -1: "One of the parameter passed to the function was invalid", - -2: "One of the parameter passed to the function was invalid", - -3: "The parameter size was invalid", - -4: "The operation type didn't exist", - -5: "The function used to execute the operation is not yet implemented", - -6: "There was not enough memory to perform the action", - -7: "The function is not yet implemented", - -11: "There was an attempt to remove something not present", - -13: "There is no level currently created", - -14: "There is no level currently created", - -16: "The notification message was not used", - -17: "Attempt to add an item that was already present", - -18: "the render context is not valid", - -19: "the render context is not activated for rendering", - -20: "there was no plugins to load this kind of file", - -21: "there was no plugins to save this kind of file", - -22: "attempt to load an invalid file", - -23: "attempt to load with an invalid plugin", - -24: "attempt use an object that wasnt initialized", - -25: "attempt use a message type that wasn't registred", - -29: "No dll file found in the parse directory", - -30: "this dll has already been registred", - -31: "this dll does not contain information to create the prototype", - -34: "Invalid Object (attempt to Get an object from an invalid ID)", - -35: "Invalid window was provided as console window", - -36: "Invalid kinematic chain ( end and start effector may not be part of the same hierarchy )", - -37: "Keyboard not attached or not working properly", - -38: "Mouse not attached or not working properly", - -39: "Joystick not attached or not working properly", - -40: "Try to link imcompatible Parameters", - -44: "There is no render engine dll", - -45: "There is no current level (use CKSetCurrentLevel )", - -46: "Sound Management has been disabled", - -47: "DirectInput Management has been disabled", - -48: "Guid is already in use or invalid", - -49: "There was no more free space on disk when trying to save a file", - -50: "Impossible to write to file (write-protection ?)", - -51: "The behavior cannnot be added to this entity", - -52: "The behavior cannnot be added to this entity", - -53: "A manager was registered more than once", - -54: "CKprocess or TimeManager process while CK is paused will fail", - -55: "Some plugins were missing whileloading a file", - -56: "Virtools version too old to load this file", - -57: "CRC Error while loading file", - -58: "A Render context is already in Fullscreen Mode", - -59: "Operation was cancelled by user", - -121: "there were no animation key at the given index", - -122: "attemp to acces an animation key with an invalid index", - -123: "the animation is invalid (no entity associated or zero length)", - } - - def __init__(self, code: CKERROR): - self.code: CKERROR = code def __repr__(self): - return self.__ErrorDescription.get(self.code, "[Undefined]") + return ErrorDescription.get(self.m_Value, "[Undefined] {:d}".format(self.m_Value)) diff --git a/PyCmo/VTReader.py b/PyCmo/VTReader.py index 5e7e6d6..4a6694e 100644 --- a/PyCmo/VTReader.py +++ b/PyCmo/VTReader.py @@ -3,50 +3,52 @@ import PyCmoMisc import struct, io, datetime, zlib import typing -g_HeaderPacker = struct.Struct('<' + 'i' * 8) +g_Header1Packer = struct.Struct(' VTConstants.CKERROR: if self.m_Parser is None: - return VTConstants.CKERR.CKERR_INVALIDPARAMETER - header = VTStruct.CKFileHeader() + return VTConstants.CKERROR(VTConstants.CKERROR.CKERR_INVALIDPARAMETER) + parser = self.m_Parser - # check magic words - magic_words = self.m_Parser.GetReader()[0:4] + # ========== magic words ========== + magic_words = parser.GetReader()[0:4] if magic_words != b'Nemo': - return VTConstants.CKERR.CKERR_INVALIDFILE + return VTConstants.CKERROR(VTConstants.CKERROR.CKERR_INVALIDFILE) - # read header1 - if self.m_Parser.GetSize() < 0x20: - return VTConstants.CKERR.CKERR_INVALIDFILE - header1 = g_HeaderPacker.unpack(self.m_Parser.GetReader().read(8 * 4)) + # ========== read header1 ========== + if parser.GetSize() < 0x20: + return VTConstants.CKERROR(VTConstants.CKERROR.CKERR_INVALIDFILE) + header1 = list(g_Header1Packer.unpack(parser.GetReader().read(8 * 4))) # check header1 if header1[5]: # i don't know what is this fields stands for - header1 = tuple(0 for _ in range(8)) + header1 = list(0 for _ in range(8)) # virtools is too old to open this file. file is too new. if header1[4] > 9: # file version - return VTConstants.CKERR.CKERR_OBSOLETEVIRTOOLS + return VTConstants.CKERROR(VTConstants.CKERROR.CKERR_OBSOLETEVIRTOOLS) - # read header2 + # ========== read header2 ========== # file ver < 5 do not have second header if header1[4] < 5: - header2 = tuple(0 for _ in range(8)) + header2 = list(0 for _ in range(8)) else: - if self.m_Parser.GetSize() < 0x40: - return VTConstants.CKERR.CKERR_INVALIDFILE + if parser.GetSize() < 0x40: + return VTConstants.CKERROR(VTConstants.CKERROR.CKERR_INVALIDFILE) - header2 = g_HeaderPacker.unpack(self.m_Parser.GetReader().read(8 * 4)) + header2 = g_Header2Packer.unpack(parser.GetReader().read(8 * 4)) # forcely reset too big product ver if header2[5] >= 12: # product version header2[5] = 0 header2[6] = 0x1010000 # product build - # assign value + # ========== assign value ========== self.m_FileInfo.ProductVersion = header2[5] self.m_FileInfo.ProductBuild = header2[6] self.m_FileInfo.FileWriteMode.m_Value = header1[6] @@ -62,31 +64,78 @@ class CKFileReader(): self.m_FileInfo.DataUnPackSize = header2[1] self.m_FileInfo.Crc = header1[2] + # ========== crc and body unpacker ========== + # file version greater than 8 have crc feature and compression feature + if self.m_FileInfo.FileVersion >= 8: + # reset crc field of header + header1[2] = 0 - # process date independently - # date is in BCD code - day = PyCmoMisc.BcdCodeToDecCode((raw_date >> 24) & 0xff) - month = PyCmoMisc.BcdCodeToDecCode((raw_date >> 16) & 0xff - 1) - month = (month % 12) + 1 - year = PyCmoMisc.BcdCodeToDecCode(raw_date & 0xffff) - header.Timestamp = datetime.date(year, month, day) + # compute crc + gotten_crc = zlib.adler32(g_Header1Packer.pack(*header1), 0) - if header.FileVersion >= 8: - # check crc - gotten_crc = zlib.adler32(b'Nemo Fi\0', 0) - gotten_crc = zlib.adler32(struct.pack("<6I", 0, raw_date, header.FileVersion, header.FileVersion2, header.SaveFlags, header.PrewHdrPackSize), gotten_crc) # reset crc as zero - gotten_crc = zlib.adler32(struct.pack("<8I", header.DataPackSize, header.DataUnpackSize, header.ManagerCount, header.ObjectCount, header.MaxIDSaved, header.ProductVersion, header.ProductBuild, header.PrewHdrUnpackSize), gotten_crc) - gotten_crc = zlib.adler32(fs.read(header.PrewHdrPackSize), gotten_crc) - gotten_crc = zlib.adler32(fs.read(header.DataPackSize), gotten_crc) - if gotten_crc != header.Crc: - raise Exception("Crc Error") + reader = parser.GetReader() + pos_cache = reader.tell() + reader.seek(8 * 4, io.SEEK_SET) + gotten_crc = zlib.adler32(reader.read( + 8 * 4 + self.m_FileInfo.Hdr1PackSize + self.m_FileInfo.DataPackSize + ), gotten_crc) + reader.seek(pos_cache, io.SEEK_SET) + + # compare crc + if gotten_crc != self.m_FileInfo.Crc: + return VTConstants.CKERROR(VTConstants.CKERROR.CKERR_FILECRCERROR) + + + # compare size to decide wheher use compress feature + if self.m_FileInfo.Hdr1UnPackSize != self.m_FileInfo.Hdr1PackSize: + # create a new parser for following analyze + parser = VTUtils.SmallZlibFileReader(parser, self.m_FileInfo.Hdr1PackSize, self.m_FileInfo.Hdr1UnPackSize) + + # ========== object list read ========== + # file ver >= 7 have this features + if self.m_FileInfo.FileVersion >= 7: + self.m_SaveIDMax = self.m_FileInfo.MaxIDSaved + # resize + self.m_FileObjects = list(VTStruct.CKFileObject() for _ in range(self.m_FileInfo.ObjectCount)) + + # read data + reader = parser.GetReader() + for fileobj in self.m_FileObjects: + # set useless fields + fileobj.ObjPtr = None + fileobj.Name = None + fileobj.Data = None + + # read basic fields + (fileobj.Object, cid, fileobj.FileIndex, namelen) = g_FileObjectPacker.unpack( + reader.read(g_FileObjectPacker.size) + ) + fileobj.ObjectCid.Set(cid) + + # read name + if namelen > 0: + fileobj.Name = reader.read(namelen).decode('utf-8', errors = 'ignore') + + # ========== dep list read ========== + # only file ver >= 8, this sector is existed + if self.m_FileInfo.FileVersion >= 8: + pass - return header - @staticmethod - def ReadCKComposition(fs: io.BufferedReader): - composition = VTStruct.CKComposition() + def Load(self: VTStruct.CKFile, filename: str, load_flags: VTConstants.CK_LOAD_FLAGS) -> VTConstants.CKERROR: + # todo: clear first - composition.Header = ReadCKFileHeader(fs) - return composition + # setup basic fields + self.m_FileName = filename + self.m_Flags = load_flags + + # setup parser + self.m_Parser = VTUtils.RawFileReader(filename) + + # read header first + err = CKFileReader.ReadFileHeaders(self) + + # todo: finish other read + + return err diff --git a/PyCmo/VTStruct.py b/PyCmo/VTStruct.py index ea22c65..1dd740d 100644 --- a/PyCmo/VTStruct.py +++ b/PyCmo/VTStruct.py @@ -2,12 +2,24 @@ import VTConstants, VTUtils import PyCmoMisc import datetime +class CKGUID(): + def __init__(self): + self.D1: int = 0 + self.D2: int = 0 + def __init__(self, d1: int, d2: int): + self.D1: int = d1 + self.D2: int = d2 + def __repr__(self): + return f" tuple[int]: + def GetProductBuildTuple(self) -> tuple[int]: return ( (self.ProductBuild >> 24) & 0xff, (self.ProductBuild >> 16) & 0xff, @@ -32,29 +44,73 @@ class CKFileInfo: self.ProductBuild & 0xff ) - def __repr__(self: CKFileInfo) -> str: - return f"""Version (File / CK): {self.FileVersion:08X} / {self.CKVersion:08X} -Product (Version / Build): {self.ProductVersion:d} / {'.0'.join(self.GetProductBuildTuple())} -Save Flags: {self.SaveFlags} -File Size: {PyCmoMisc.OutputSizeHumanReadable(self.FileSize)} -Crc: 0x{self.Crc:08X} + def __repr__(self) -> str: + return f"""""" + def __str__(self) -> str: + return self.__repr__() +class CKFileObject(): + def __init__(self): + self.Object: VTConstants.CK_ID = 0 + self.CreatedObject: VTConstants.CK_ID = 0 + self.ObjectCid: VTConstants.CK_CLASSID = VTConstants.CK_CLASSID(VTConstants.CK_CLASSID.CKCID_OBJECT) + self.ObjPtr = None + self.Name: str = None + self.Data = None + self.PostPackSize: int = 0 + self.PrePackSize: int = 0 + self.Options: VTConstants.CK_FO_OPTIONS = VTConstants.CK_FO_OPTIONS(VTConstants.CK_FO_OPTIONS.CK_FO_DEFAULT) + self.FileIndex: int = 0 + self.SaveFlags: VTConstants.CK_FILE_WRITEMODE = VTConstants.CK_FILE_WRITEMODE(0) + def __repr__(self) -> str: + return f'<{self.Name if self.Name else "[Anonymous]"}: {self.ObjectCid}>' + def __str__(self) -> str: + return self.__repr__() + +class CKFilePluginDependencies(): + def __init__(self): + self.m_PluginCategory: int = 0 + self.m_Guids: list[CKGUID] = [] + self.ValidGuids: list[bool] = [] + + def __repr__(self) -> str: + return repr(self.m_Guids) + def __str__(self) -> str: + return self.__repr__() class CKFile(object): def __init__(self): - self.m_FileName: str = '' + self.m_SaveIDMax: int = 0 + self.m_FileObjects: list[CKFileObject] = [] + self.m_ManagersData = [] + self.m_PluginDep: list[CKFilePluginDependencies] = [] + self.m_IndexByClassId: list[list[int]] = [] + self.m_IncludedFiles: list[str] = [] + self.m_FileInfo: CKFileInfo = CKFileInfo() + + self.m_Flags: VTConstants.CK_LOAD_FLAGS = VTConstants.CK_LOAD_FLAGS(VTConstants.CK_LOAD_FLAGS.CK_LOAD_DEFAULT) + self.m_FileName: str = '' self.m_Parser: VTUtils.UniversalFileReader = None - def __repr__(self: CKFile) -> str: - return self.m_FileInfo + self.m_ReadFileDataDone: bool = False + + def __repr__(self) -> str: + return repr(self.m_FileInfo) + def __str__(self) -> str: + return self.__repr__() diff --git a/PyCmo/VTUtils.py b/PyCmo/VTUtils.py index 40d5f01..ed38ffa 100644 --- a/PyCmo/VTUtils.py +++ b/PyCmo/VTUtils.py @@ -6,7 +6,7 @@ class RawFileReader(): def __init__(self, filename: str): self.__size: int = os.path.getsize(filename) self.__fs = open(filename, 'rb') - self.__mm: mmap.mmap = mmap.mmap(self.__fs.fileno, 0, access = mmap.ACCESS_READ) + self.__mm: mmap.mmap = mmap.mmap(self.__fs.fileno(), 0, access = mmap.ACCESS_READ) def __del__(self): self.__mm.close() @@ -49,7 +49,10 @@ class LargeZlibFileReader(): return self.__mm class SmallZlibFileReader(): - def __init__(self): + def __init__(self, raw_reader: RawFileReader, comp_size: int, uncomp_size: int): + # set size + self.__size: int = uncomp_size + # create io self.__ss: io.BytesIO = io.BytesIO() @@ -61,13 +64,16 @@ class SmallZlibFileReader(): while buf: self.__ss.write(parser.decompress(buf)) buf = reader.read(io.DEFAULT_BUFFER_SIZE) - self._ss.write(parser.flush()) + self.__ss.write(parser.flush()) + + # reset cursor + self.__ss.seek(0, io.SEEK_SET) def __del__(self): del self.__ss def GetSize(self) -> int: - return len(self.__ss.getvalue()) + return self.__size def GetReader(self) -> io.BytesIO: return self.__ss