mirror of
https://github.com/SickGear/SickGear.git
synced 2024-11-17 18:35:04 +00:00
227 lines
7.4 KiB
Python
227 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")
|