libcmo21/PyCmo/VTReader.py

142 lines
5.3 KiB
Python
Raw Normal View History

2023-02-03 15:34:16 +08:00
import VTStruct, VTUtils, VTConstants
import PyCmoMisc
import struct, io, datetime, zlib
import typing
2023-02-03 23:20:48 +08:00
g_Header1Packer = struct.Struct('<iiIiiiii')
g_Header2Packer = struct.Struct('<iiiiiiii')
g_FileObjectPacker = struct.Struct('<iiii')
2023-02-03 15:34:16 +08:00
class CKFileReader():
@staticmethod
def ReadFileHeaders(self: VTStruct.CKFile) -> VTConstants.CKERROR:
if self.m_Parser is None:
2023-02-03 23:20:48 +08:00
return VTConstants.CKERROR(VTConstants.CKERROR.CKERR_INVALIDPARAMETER)
parser = self.m_Parser
2023-02-03 15:34:16 +08:00
2023-02-03 23:20:48 +08:00
# ========== magic words ==========
magic_words = parser.GetReader()[0:4]
2023-02-03 15:34:16 +08:00
if magic_words != b'Nemo':
2023-02-03 23:20:48 +08:00
return VTConstants.CKERROR(VTConstants.CKERROR.CKERR_INVALIDFILE)
2023-02-03 15:34:16 +08:00
2023-02-03 23:20:48 +08:00
# ========== read header1 ==========
if parser.GetSize() < 0x20:
return VTConstants.CKERROR(VTConstants.CKERROR.CKERR_INVALIDFILE)
header1 = list(g_Header1Packer.unpack(parser.GetReader().read(8 * 4)))
2023-02-03 15:34:16 +08:00
# check header1
if header1[5]: # i don't know what is this fields stands for
2023-02-03 23:20:48 +08:00
header1 = list(0 for _ in range(8))
2023-02-03 15:34:16 +08:00
# virtools is too old to open this file. file is too new.
if header1[4] > 9: # file version
2023-02-03 23:20:48 +08:00
return VTConstants.CKERROR(VTConstants.CKERROR.CKERR_OBSOLETEVIRTOOLS)
2023-02-03 15:34:16 +08:00
2023-02-03 23:20:48 +08:00
# ========== read header2 ==========
2023-02-03 15:34:16 +08:00
# file ver < 5 do not have second header
if header1[4] < 5:
2023-02-03 23:20:48 +08:00
header2 = list(0 for _ in range(8))
2023-02-03 15:34:16 +08:00
else:
2023-02-03 23:20:48 +08:00
if parser.GetSize() < 0x40:
return VTConstants.CKERROR(VTConstants.CKERROR.CKERR_INVALIDFILE)
2023-02-03 15:34:16 +08:00
2023-02-03 23:20:48 +08:00
header2 = g_Header2Packer.unpack(parser.GetReader().read(8 * 4))
2023-02-03 15:34:16 +08:00
# forcely reset too big product ver
if header2[5] >= 12: # product version
header2[5] = 0
header2[6] = 0x1010000 # product build
2023-02-03 23:20:48 +08:00
# ========== assign value ==========
2023-02-03 15:34:16 +08:00
self.m_FileInfo.ProductVersion = header2[5]
self.m_FileInfo.ProductBuild = header2[6]
self.m_FileInfo.FileWriteMode.m_Value = header1[6]
self.m_FileInfo.CKVersion = header1[3]
self.m_FileInfo.FileVersion = header1[4]
self.m_FileInfo.FileSize = self.m_Parser.GetSize()
self.m_FileInfo.ManagerCount = header2[2]
self.m_FileInfo.ObjectCount = header2[3]
self.m_FileInfo.MaxIDSaved = header2[4]
self.m_FileInfo.Hdr1PackSize = header1[7]
self.m_FileInfo.Hdr1UnPackSize = header2[7]
self.m_FileInfo.DataPackSize = header2[0]
self.m_FileInfo.DataUnPackSize = header2[1]
self.m_FileInfo.Crc = header1[2]
2023-02-03 23:20:48 +08:00
# ========== 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
# compute crc
gotten_crc = zlib.adler32(g_Header1Packer.pack(*header1), 0)
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
2023-02-03 15:34:16 +08:00
2023-02-03 23:20:48 +08:00
@staticmethod
def Load(self: VTStruct.CKFile, filename: str, load_flags: VTConstants.CK_LOAD_FLAGS) -> VTConstants.CKERROR:
# todo: clear first
# setup basic fields
self.m_FileName = filename
self.m_Flags = load_flags
# setup parser
self.m_Parser = VTUtils.RawFileReader(filename)
2023-02-03 15:34:16 +08:00
2023-02-03 23:20:48 +08:00
# read header first
err = CKFileReader.ReadFileHeaders(self)
2023-02-03 15:34:16 +08:00
2023-02-03 23:20:48 +08:00
# todo: finish other read
2023-02-03 15:34:16 +08:00
2023-02-03 23:20:48 +08:00
return err