mirror of
https://github.com/SickGear/SickGear.git
synced 2024-11-30 00:13:38 +00:00
186 lines
5.1 KiB
Python
186 lines
5.1 KiB
Python
|
"""
|
||
|
3D Studio Max file (.3ds) parser.
|
||
|
Author: Victor Stinner
|
||
|
"""
|
||
|
|
||
|
from hachoir.parser import Parser
|
||
|
from hachoir.field import (StaticFieldSet, FieldSet,
|
||
|
UInt16, UInt32, RawBytes, Enum, CString)
|
||
|
from hachoir.parser.image.common import RGB
|
||
|
from hachoir.core.endian import LITTLE_ENDIAN
|
||
|
from hachoir.core.text_handler import textHandler, hexadecimal
|
||
|
from hachoir.parser.misc.common import Vertex, MapUV
|
||
|
|
||
|
|
||
|
def readObject(parent):
|
||
|
yield CString(parent, "name", "Object name")
|
||
|
size = parent["size"].value * 8
|
||
|
while parent.current_size < size:
|
||
|
yield Chunk(parent, "chunk[]")
|
||
|
|
||
|
|
||
|
def readTextureFilename(parent):
|
||
|
yield CString(parent, "filename", "Texture filename")
|
||
|
|
||
|
|
||
|
def readVersion(parent):
|
||
|
yield UInt32(parent, "version", "3DS file format version")
|
||
|
|
||
|
|
||
|
def readMaterialName(parent):
|
||
|
yield CString(parent, "name", "Material name")
|
||
|
|
||
|
|
||
|
class Polygon(StaticFieldSet):
|
||
|
format = (
|
||
|
(UInt16, "a", "Vertex A"),
|
||
|
(UInt16, "b", "Vertex B"),
|
||
|
(UInt16, "c", "Vertex C"),
|
||
|
(UInt16, "flags", "Flags"))
|
||
|
|
||
|
|
||
|
def readMapList(parent):
|
||
|
yield UInt16(parent, "count", "Map count")
|
||
|
for index in range(parent["count"].value):
|
||
|
yield MapUV(parent, "map_uv[]", "Mapping UV")
|
||
|
|
||
|
|
||
|
def readColor(parent):
|
||
|
yield RGB(parent, "color")
|
||
|
|
||
|
|
||
|
def readVertexList(parent):
|
||
|
yield UInt16(parent, "count", "Vertex count")
|
||
|
for index in range(0, parent["count"].value):
|
||
|
yield Vertex(parent, "vertex[]", "Vertex")
|
||
|
|
||
|
|
||
|
def readPolygonList(parent):
|
||
|
count = UInt16(parent, "count", "Vertex count")
|
||
|
yield count
|
||
|
for i in range(0, count.value):
|
||
|
yield Polygon(parent, "polygon[]")
|
||
|
size = parent["size"].value * 8
|
||
|
while parent.current_size < size:
|
||
|
yield Chunk(parent, "chunk[]")
|
||
|
|
||
|
|
||
|
class Chunk(FieldSet):
|
||
|
# List of chunk type name
|
||
|
type_name = {
|
||
|
0x0011: "Color",
|
||
|
0x4D4D: "Main chunk",
|
||
|
0x0002: "File version",
|
||
|
0x3D3D: "Materials and objects",
|
||
|
0x4000: "Object",
|
||
|
0x4100: "Mesh (triangular)",
|
||
|
0x4110: "Vertices list",
|
||
|
0x4120: "Polygon (faces) list",
|
||
|
0x4140: "Map UV list",
|
||
|
0x4130: "Object material",
|
||
|
0xAFFF: "New material",
|
||
|
0xA000: "Material name",
|
||
|
0xA010: "Material ambient",
|
||
|
0xA020: "Material diffuse",
|
||
|
0xA030: "Texture specular",
|
||
|
0xA200: "Texture",
|
||
|
0xA300: "Texture filename",
|
||
|
|
||
|
# Key frames
|
||
|
0xB000: "Keyframes",
|
||
|
0xB002: "Object node tag",
|
||
|
0xB006: "Light target node tag",
|
||
|
0xB007: "Spot light node tag",
|
||
|
0xB00A: "Keyframes header",
|
||
|
0xB009: "Keyframe current time",
|
||
|
0xB030: "Node identifier",
|
||
|
0xB010: "Node header",
|
||
|
0x7001: "Viewport layout"
|
||
|
}
|
||
|
|
||
|
chunk_id_by_type = {
|
||
|
0x4d4d: "main",
|
||
|
0x0002: "version",
|
||
|
0x3d3d: "obj_mat",
|
||
|
0xb000: "keyframes",
|
||
|
0xafff: "material[]",
|
||
|
0x4000: "object[]",
|
||
|
0x4110: "vertices_list",
|
||
|
0x4120: "polygon_list",
|
||
|
0x4140: "mapuv_list",
|
||
|
0x4100: "mesh"
|
||
|
}
|
||
|
|
||
|
# List of chunks which contains other chunks
|
||
|
sub_chunks = \
|
||
|
(0x4D4D, 0x4100, 0x3D3D, 0xAFFF, 0xA200,
|
||
|
0xB002, 0xB006, 0xB007,
|
||
|
0xA010, 0xA030, 0xA020, 0xB000)
|
||
|
|
||
|
# List of chunk type handlers
|
||
|
handlers = {
|
||
|
0xA000: readMaterialName,
|
||
|
0x4000: readObject,
|
||
|
0xA300: readTextureFilename,
|
||
|
0x0011: readColor,
|
||
|
0x0002: readVersion,
|
||
|
0x4110: readVertexList,
|
||
|
0x4120: readPolygonList,
|
||
|
0x4140: readMapList
|
||
|
}
|
||
|
|
||
|
def __init__(self, *args):
|
||
|
FieldSet.__init__(self, *args)
|
||
|
|
||
|
# Set description
|
||
|
self._description = "Chunk: %s" % self["type"].display
|
||
|
|
||
|
# Set name based on type field
|
||
|
type = self["type"].value
|
||
|
if type in Chunk.chunk_id_by_type:
|
||
|
self._name = Chunk.chunk_id_by_type[type]
|
||
|
else:
|
||
|
self._name = "chunk_%04x" % type
|
||
|
|
||
|
# Guess chunk size
|
||
|
self._size = self["size"].value * 8
|
||
|
|
||
|
def createFields(self):
|
||
|
yield Enum(textHandler(UInt16(self, "type", "Chunk type"), hexadecimal), Chunk.type_name)
|
||
|
yield UInt32(self, "size", "Chunk size (in bytes)")
|
||
|
content_size = self["size"].value - 6
|
||
|
if content_size == 0:
|
||
|
return
|
||
|
type = self["type"].value
|
||
|
if type in Chunk.sub_chunks:
|
||
|
while self.current_size < self.size:
|
||
|
yield Chunk(self, "chunk[]")
|
||
|
else:
|
||
|
if type in Chunk.handlers:
|
||
|
yield from Chunk.handlers[type](self)
|
||
|
else:
|
||
|
yield RawBytes(self, "data", content_size)
|
||
|
|
||
|
|
||
|
class File3ds(Parser):
|
||
|
endian = LITTLE_ENDIAN
|
||
|
PARSER_TAGS = {
|
||
|
"id": "3ds",
|
||
|
"category": "misc",
|
||
|
"file_ext": ("3ds",),
|
||
|
"mime": ("image/x-3ds",),
|
||
|
"min_size": 16 * 8,
|
||
|
"description": "3D Studio Max model"
|
||
|
}
|
||
|
|
||
|
def validate(self):
|
||
|
if self.stream.readBytes(0, 2) != b"MM":
|
||
|
return "Wrong signature"
|
||
|
if self["main/version/version"].value not in (2, 3):
|
||
|
return "Unknown format version"
|
||
|
return True
|
||
|
|
||
|
def createFields(self):
|
||
|
while not self.eof:
|
||
|
yield Chunk(self, "chunk[]")
|