#! /usr/bin/env python import sys import struct base_CASE = 0xCA5E0000L # 0x00005ECA in little endian notation def CASE (elem): return base_CASE + elem CASE_String_Table = CASE(0x0200) CASE_Reference_System = CASE(0x1002) CASE_Vertex_Positions = CASE(0x0801) CASE_Vertex_Normals = CASE(0x0802) CASE_Vertex_Textures = CASE(0x0803) CASE_Triangles = CASE(0x0901) CASE_Bone_Transform = CASE(0x0506) CASE_Texture_Mapping = CASE(0x0E06) CASE_End_Of_Table = CASE(0xFFFF) registered_parsers = {} class CASE_parser: def __init__ (self, case, format, description): self.__case = case self.__format = format self.__description = description class CASE_table: def consume_field (self, format): format = '<' + format # little endian size = struct.calcsize (format) field = struct.unpack (format, self.__data[self.__pointer : self.__pointer + size]) self.__pointer += size return field def peek_field (self, format): format = '<' + format # little endian size = struct.calcsize (format) field = struct.unpack (format, self.__data[self.__pointer : self.__pointer + size]) return field def __init__ (self, data): self.entry_size = 12 self.__entries = struct.unpack('<1L', data[0:4])[0] self.__header = data[4:16] self.__data = data[16 : 16 + self.__entries * self.entry_size] self.__pointer = 0 self.__at_end = False assert len (self.__data) == self.__entries * self.entry_size def __iter__ (self): return self def next (self): if self.__at_end: raise StopIteration field = self.consume_field ('3L') case = field[0] start = field[1] extra = field[2] if case == CASE_End_Of_Table: size = 0 self.__at_end = True else: next_field = self.peek_field ('3L') size = next_field[1] - start return (case, start, size, extra) def init_parser_from_file (name): global file_pointer global file_data file = open(name, 'r') file_data = file.read() file.close() file_pointer = 0 def discard_header (size): global file_pointer global file_data old_size = len(file_data) file_data = file_data[size:] file_pointer -= size assert len (file_data) == old_size - size def parse_field (data, start, format): format = '<' + format # little endian size = struct.calcsize (format) field = struct.unpack (format, data[start : start + size]) return field def consume_field (format): global file_pointer start = file_pointer file_pointer += struct.calcsize ('<' + format) field = parse_field (file_data, start, format) return field def parse_opaque_header (): # Constant header, unknown meaning field = consume_field ('64s') assert field[0] == '\x2A\x30\x39\x04\x18\x46\x6C\x66\x8D\x6D\x26\x23\x9A\x52\xC1\x7A' + \ '\x70\x10\xE0\x44\x84\x23\x26\x32\x25\x3C\x0A\x64\xF7\x26\x61\x1F' + \ '\x25\x3C\x0A\x64\xF7\x26\x61\x1F\x44\x68\x2A\x3B\xA8\x78\xE4\x61' + \ '\x58\x58\x71\x5F\x08\x39\xAC\x1D\x7A\x3D\x21\x7F\x60\x4A\xF6\x37' header = field[0] discard_header (len (header)) # CASE 0x0000: partly unknown, holds size of file content (all file but discarded header) field = consume_field ('5L') assert field[0] == CASE(0x0000) assert field[4] == len (file_data) # 0x0000 0x0000 0x0000 field = consume_field ('3L') assert field[0] == 0x0000 assert field[1] == 0x0000 assert field[2] == 0x0000 # CASE: 0x0102: unknown field = consume_field ('5L') assert field[0] == CASE(0x0102) # CASE: 0x0103: unknown field = consume_field ('5L') assert field[0] == CASE(0x0103) # CASE: 0x0101: partly unknown, holds absolute position (within whole file) of EOF field field = consume_field ('5L') assert field[0] == CASE(0x0101) # Absolute position of EOF field in file, +64 for already discarded header, -28 for EOF field size assert field[2] == len (file_data) + 64 - 28 def parse_CASE_unknown (case, data, extra): print '(unknwon: 0x%08X)' % case print ' size: %d' % len(data) print ' extra: %d' % extra print ' data : ...' def parse_CASE_table (data, extra = 0): if data: #print 'Unpacking CASE table' for field in CASE_table (data): #print ' 0x%08X: %8d %8d %8d' % field if field[0] == CASE(0xFFFF): # CASE 0xFFFF: next CASE table parse_CASE_table (data[field[1]:]) elif registered_parsers.has_key (field[0]): registered_parsers[field[0]] (data[field[1]:field[1]+field[2]], field[3]) else: parse_CASE_unknown (field[0], data[field[1]:field[1]+field[2]], field[3]) else: #print 'End of tables' pass def parse_file (name): # read file name init_parser_from_file (name) # header and other unknown data parse_opaque_header() # CASE tables parse_CASE_table (file_data[file_pointer:]) # main for name in sys.argv[1:]: #sys.stderr.write ('%s\n' % name) print name parse_file (name)