SickGear/lib/hachoir/parser/misc/file_3do.py

226 lines
7.4 KiB
Python

"""
3do model parser.
Author: Cyril Zorin
Creation date: 28 september 2006
"""
from hachoir.parser import Parser
from hachoir.field import (FieldSet,
UInt32, Int32, String, Float32,
RawBytes, PaddingBytes)
from hachoir.core.endian import LITTLE_ENDIAN, BIG_ENDIAN
from hachoir.parser.misc.common import Vertex, MapUV
class Vector(FieldSet):
def __init__(self, parent, name,
count, type, ename, edesc=None, description=None):
FieldSet.__init__(self, parent, name, description)
self.count = count
self.type = type
self.ename = ename + "[]"
self.edesc = edesc
try:
item_size = self.type.static_size(self.ename, self.edesc)
except TypeError:
item_size = self.type.static_size
if item_size:
self._size = item_size * self.count
def createFields(self):
for index in range(self.count):
yield self.type(self, self.ename, self.edesc)
class Face(FieldSet):
def createFields(self):
yield UInt32(self, "id")
yield UInt32(self, "type")
yield UInt32(self, "geometry_mode")
yield UInt32(self, "lighting_mode")
yield UInt32(self, "texture_mode")
yield UInt32(self, "nvertices")
yield Float32(self, "unknown[]", "unknown")
yield UInt32(self, "has_texture", "Has texture?")
yield UInt32(self, "has_material", "Has material?")
yield Vertex(self, "unknown[]")
yield Float32(self, "extra_light")
yield Vertex(self, "unknown[]")
yield Vertex(self, "normal")
if self["nvertices"].value:
yield Vector(self, "vertex_indices",
self["nvertices"].value, UInt32, "vertex")
if self["has_texture"].value:
yield Vector(self, "texture_vertex_indices",
self["nvertices"].value, UInt32, "texture_vertex")
if self["has_material"].value:
yield UInt32(self, "material_index", "material index")
def createDescription(self):
return "Face: id=%s" % self["id"].value
class Mesh(FieldSet):
def __init__(self, *args):
FieldSet.__init__(self, *args)
def createFields(self):
yield String(self, "name", 32, strip="\0")
yield UInt32(self, "id")
yield UInt32(self, "geometry_mode")
yield UInt32(self, "lighting_mode")
yield UInt32(self, "texture_mode")
yield UInt32(self, "nmesh_vertices")
yield UInt32(self, "ntexture_vertices")
yield UInt32(self, "nfaces")
nb_vert = self["nmesh_vertices"].value
if nb_vert:
yield Vector(self, "vertices",
nb_vert, Vertex, "vertex")
if self["ntexture_vertices"].value:
yield Vector(self, "texture vertices",
self["ntexture_vertices"].value, MapUV, "texture_vertex")
if nb_vert:
yield Vector(self, "light vertices",
nb_vert, Float32, "extra_light")
yield Vector(self, "unknown[]",
nb_vert, Float32, "unknown")
if self["nfaces"].value:
yield Vector(self, "faces", self["nfaces"].value, Face, "face")
if nb_vert:
yield Vector(self, "vertex normals",
nb_vert, Vertex, "normal")
yield UInt32(self, "has_shadow")
yield Float32(self, "unknown[]")
yield Float32(self, "radius")
yield Vertex(self, "unknown[]")
yield Vertex(self, "unknown[]")
def createDescription(self):
return 'Mesh "%s" (id %s)' % (self["name"].value, self["id"].value)
class Geoset(FieldSet):
def createFields(self):
yield UInt32(self, "count")
for index in range(self["count"].value):
yield Mesh(self, "mesh[]")
def createDescription(self):
return "Set of %s meshes" % self["count"].value
class Node(FieldSet):
def __init__(self, *args):
FieldSet.__init__(self, *args)
size = (188 - 4) * 8
if self["parent_offset"].value != 0:
size += 32
if self["first_child_offset"].value != 0:
size += 32
if self["next_sibling_offset"].value != 0:
size += 32
self._size = size
def createFields(self):
yield String(self, "name", 32, strip="\0")
yield PaddingBytes(self, "unknown[]", 32, pattern="\xCC")
yield UInt32(self, "flags")
yield UInt32(self, "id")
yield UInt32(self, "type")
yield Int32(self, "mesh_id")
yield UInt32(self, "depth")
yield Int32(self, "parent_offset")
yield UInt32(self, "nchildren")
yield UInt32(self, "first_child_offset")
yield UInt32(self, "next_sibling_offset")
yield Vertex(self, "pivot")
yield Vertex(self, "position")
yield Float32(self, "pitch")
yield Float32(self, "yaw")
yield Float32(self, "roll")
for index in range(4):
yield Vertex(self, "unknown_vertex[]")
if self["parent_offset"].value != 0:
yield UInt32(self, "parent_id")
if self["first_child_offset"].value != 0:
yield UInt32(self, "first_child_id")
if self["next_sibling_offset"].value != 0:
yield UInt32(self, "next_sibling_id")
def createDescription(self):
return 'Node "%s"' % self["name"].value
class Nodes(FieldSet):
def createFields(self):
yield UInt32(self, "count")
for index in range(self["count"].value):
yield Node(self, "node[]")
def createDescription(self):
return 'Nodes (%s)' % self["count"].value
class Materials(FieldSet):
def __init__(self, *args):
FieldSet.__init__(self, *args)
count = self["count"]
self._size = count.size + count.value * (32 * 8)
def createFields(self):
yield UInt32(self, "count")
for index in range(self["count"].value):
yield String(self, "filename[]", 32, "Material file name", strip="\0")
def createDescription(self):
return 'Material file names (%s)' % self["count"].value
class File3do(Parser):
PARSER_TAGS = {
"id": "3do",
"category": "misc",
"file_ext": ("3do",),
"mime": ("image/x-3do",),
"min_size": 8 * 4,
"description": "renderdroid 3d model."
}
endian = LITTLE_ENDIAN
def validate(self):
signature = self.stream.readBytes(0, 4)
return signature in (b'LDOM', b'MODL') # lazy endian-safe hack =D
def createFields(self):
# Read file signature, and fix endian if needed
yield String(self, "file_sig", 4, "File signature", charset="ASCII")
if self["file_sig"].value == "MODL":
self.endian = BIG_ENDIAN
# Read file content
yield Materials(self, "materials")
yield String(self, "model_name", 32, "model file name", strip="\0")
yield RawBytes(self, "unknown[]", 4)
yield UInt32(self, "ngeosets")
for index in range(self["ngeosets"].value):
yield Geoset(self, "geoset[]")
yield RawBytes(self, "unknown[]", 4)
yield Nodes(self, "nodes")
yield Float32(self, "model_radius")
yield Vertex(self, "insertion_offset")
# Read the end of the file
if self.current_size < self._size:
yield self.seekBit(self._size, "end")