93 lines
3.6 KiB
Python
93 lines
3.6 KiB
Python
|
import VTStruct, VTUtils, VTConstants
|
||
|
import PyCmoMisc
|
||
|
import struct, io, datetime, zlib
|
||
|
import typing
|
||
|
|
||
|
g_HeaderPacker = struct.Struct('<' + 'i' * 8)
|
||
|
|
||
|
class CKFileReader():
|
||
|
|
||
|
@staticmethod
|
||
|
def ReadFileHeaders(self: VTStruct.CKFile) -> VTConstants.CKERROR:
|
||
|
if self.m_Parser is None:
|
||
|
return VTConstants.CKERR.CKERR_INVALIDPARAMETER
|
||
|
header = VTStruct.CKFileHeader()
|
||
|
|
||
|
# check magic words
|
||
|
magic_words = self.m_Parser.GetReader()[0:4]
|
||
|
if magic_words != b'Nemo':
|
||
|
return VTConstants.CKERR.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))
|
||
|
|
||
|
# check header1
|
||
|
if header1[5]: # i don't know what is this fields stands for
|
||
|
header1 = tuple(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
|
||
|
|
||
|
# read header2
|
||
|
# file ver < 5 do not have second header
|
||
|
if header1[4] < 5:
|
||
|
header2 = tuple(0 for _ in range(8))
|
||
|
else:
|
||
|
if self.m_Parser.GetSize() < 0x40:
|
||
|
return VTConstants.CKERR.CKERR_INVALIDFILE
|
||
|
|
||
|
header2 = g_HeaderPacker.unpack(self.m_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
|
||
|
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]
|
||
|
|
||
|
|
||
|
# 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)
|
||
|
|
||
|
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")
|
||
|
|
||
|
return header
|
||
|
|
||
|
|
||
|
@staticmethod
|
||
|
def ReadCKComposition(fs: io.BufferedReader):
|
||
|
composition = VTStruct.CKComposition()
|
||
|
|
||
|
composition.Header = ReadCKFileHeader(fs)
|
||
|
return composition
|