Vearie

bz2msh.py - BZ2 Mesh Parser

May 9th, 2021 (edited)
1,087
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 24.07 KB | None | 0 0
  1. """This module provides a parser and writer for BZ2 .msh files."""
  2. VERSION = 1.11
  3.  
  4. import json
  5. from ctypes import sizeof, Structure, Array
  6. from ctypes import c_ubyte, c_int32, c_uint16, c_uint32, c_uint16, c_float
  7.  
  8. MSH_END_OF_OPTIONALS = 0x9709513F
  9. MSH_MATERIAL = 0x9709513E
  10. MSH_TEXTURE = 0x7951FC0B
  11. MSH_CHILD = 0xF74C51EE
  12. MSH_SIBLING = 0xB8990880
  13. MSH_END = 0xA93EB864
  14. MSH_EOF = 0xE3BB47F1
  15.  
  16. # From "renderflags.txt"
  17. DP_WAIT = 0x1
  18. RS_NOVTXCHECK = 0x2
  19. DP_DONOTCLIP = 0x4
  20. DP_DONOTUPDATEEXTENTS = 0x8
  21. DP_DONOTLIGHT = 0x10 # __e
  22. RS_DRAWTEXT = 0x20
  23. RS_NOALPHA = 0x40
  24. RS_RESERVED1 = 0x80
  25. RS_COLLIDABLE = 0x100 # __c
  26. RS_2SIDED = 0x200 # __2
  27. RS_HIDDEN = 0x400 # __h
  28. RS_NOFOG = 0x800
  29. RS_BLACKFOG = 0x1000
  30. RS_NOSORT = 0x2000
  31. RS_TEXMIRROR = 0x4000
  32. RS_TEXCLAMP = 0x8000
  33. RS_SRC_ZERO = 0x10000
  34. RS_SRC_ONE = 0x20000
  35. RS_SRC_SRCCOLOR = 0x30000
  36. RS_SRC_INVSRCCOLOR = 0x40000
  37. RS_SRC_SRCALPHA = 0x50000
  38. RS_SRC_INVSRCALPHA = 0x60000
  39. RS_SRC_DSTALPHA = 0x70000
  40. RS_SRC_INVDSTALPHA = 0x80000
  41. RS_SRC_DSTCOLOR = 0x90000
  42. RS_SRC_INVDSTCOLOR = 0xa0000
  43. RS_SRC_SRCALPHASAT = 0xb0000
  44. RS_DST_ZERO = 0x100000
  45. RS_DST_ONE = 0x200000 # __g (doesn't seem to work in BZCC)
  46. RS_DST_SRCCOLOR = 0x300000
  47. RS_DST_INVSRCCOLOR = 0x400000
  48. RS_DST_SRCALPHA = 0x500000
  49. RS_DST_INVSRCALPHA = 0x600000
  50. RS_DST_DSTALPHA = 0x700000
  51. RS_DST_INVDSTALPHA = 0x800000
  52. RS_DST_DSTCOLOR = 0x900000
  53. RS_DST_INVDSTCOLOR = 0xa00000
  54. RS_DST_SRCALPHASAT = 0xb00000
  55. RS_RESERVED2 = 0x1000000
  56. RS_RESERVED3 = 0x2000000
  57. RS_RESERVED4 = 0x4000000
  58. RS_RESERVED5 = 0x8000000
  59. RS_TEX_DECAL = 0x10000000
  60. RS_TEX_MODULATE = 0x20000000
  61. RS_TEX_DECALALPHA = 0x30000000
  62. RS_TEX_MODULATEALPHA = 0x40000000
  63. RS_TEX_DECALMASK = 0x50000000
  64. RS_TEX_MODULATEMASK = 0x60000000
  65. RS_TEX_ADD = 0x80000000
  66. DP_MASK = 0x1d
  67. RS_TEXBORDER = 0xc000
  68. RS_NOZWRITE = 0x80000000
  69. RS_SRC_MASK = 0xf0000
  70. RS_DST_MASK = 0xf00000
  71. RS_TEX_MASK = 0xf0000000
  72. RS_BLEND_MASK = 0xf0ff0000
  73. RS_BLEND_DEF = 0x40650000
  74. RS_BLEND_GLOW = 0x40250000
  75. RS_SRC_NONE = 0x0
  76. RS_DST_NONE = 0x0
  77. RS_BLEND_STENCIL_INC = 0x40000000
  78. RS_BLEND_STENCIL_DEC = 0x40100000
  79. RS_BLEND_STENCIL_USE = 0x40010000
  80. RS_BLEND_NODRAW = 0x40210000
  81.  
  82. class ZeroLengthName(Exception): pass
  83. class UnknownBlock(Exception): pass
  84. class InvalidFormat(Exception): pass
  85.  
  86. def read_optional_blocks(f):
  87.     block_type_check = c_uint32()
  88.    
  89.     material = None
  90.     f.readinto(block_type_check)
  91.     if block_type_check.value == MSH_MATERIAL:
  92.         material = Material(f)
  93.     else:
  94.         f.seek(f.tell() - sizeof(c_uint32))
  95.    
  96.     texture = None
  97.     f.readinto(block_type_check)
  98.     if block_type_check.value == MSH_TEXTURE:
  99.         texture = Texture(f)
  100.     else:
  101.         f.seek(f.tell() - sizeof(c_uint32))
  102.    
  103.     had_end_marker = False
  104.     f.readinto(block_type_check)
  105.     if block_type_check.value == MSH_END_OF_OPTIONALS:
  106.         had_end_marker = True
  107.     else:
  108.         f.seek(f.tell() - sizeof(c_uint32))
  109.    
  110.     return material, texture, had_end_marker
  111.  
  112. # This class provides a function that returns a recursive JSON represenation of its data.
  113. class StructureJSON(Structure):
  114.     def json(self):
  115.         json_handled_types = (int, str, float, list, tuple, bool)
  116.         j = {}
  117.        
  118.         for field_name, field_type in self._fields_:
  119.             field_value = getattr(self, field_name)
  120.            
  121.             if issubclass(field_type, __class__):
  122.                 # The field is an object of a class that inherits from this class
  123.                 field_value = field_value.json()
  124.            
  125.             elif type(field_value) in json_handled_types:
  126.                 pass # Primitives handled by python's JSON serializer
  127.            
  128.             elif type(field_value) in (bytes, bytearray):
  129.                 field_value = field_value.decode("ascii", "ignore")
  130.            
  131.             else:
  132.                 try:
  133.                     # Iterable (e.g. float or index array)
  134.                     field_value = [value for value in field_value]
  135.                 except TypeError:
  136.                     field_value = str(field_value)
  137.            
  138.             j[field_name] = field_value
  139.        
  140.         return j
  141.  
  142. class UVPair(StructureJSON):
  143.     _fields_ = [
  144.         ("u", c_float),
  145.         ("v", c_float)
  146.     ]
  147.    
  148.     def __iter__(self):
  149.         yield self.u
  150.         yield self.v
  151.  
  152. class Vector(StructureJSON):
  153.     _fields_ = [
  154.         ("x", c_float),
  155.         ("y", c_float),
  156.         ("z", c_float)
  157.     ]
  158.    
  159.     def __iter__(self):
  160.         yield self.x
  161.         yield self.y
  162.         yield self.z
  163.  
  164. class Vertex(StructureJSON):
  165.     _fields_ = [
  166.         ("pos", Vector),
  167.         ("norm", Vector),
  168.         ("uv", UVPair)
  169.     ]
  170.  
  171. class ColorValue(StructureJSON):
  172.     _fields_ = [
  173.         ("r", c_float),
  174.         ("g", c_float),
  175.         ("b", c_float),
  176.         ("a", c_float)
  177.     ]
  178.    
  179.     def __iter__(self):
  180.         yield self.r
  181.         yield self.g
  182.         yield self.b
  183.         yield self.a
  184.  
  185. class Color(StructureJSON):
  186.     _fields_ = [
  187.         ("b", c_ubyte),
  188.         ("g", c_ubyte),
  189.         ("r", c_ubyte),
  190.         ("a", c_ubyte)
  191.     ]
  192.    
  193.     def __iter__(self):
  194.         yield self.b
  195.         yield self.g
  196.         yield self.r
  197.         yield self.a
  198.  
  199. class Matrix(StructureJSON):
  200.     _fields_ = [
  201.         ("right", c_float * 4),
  202.         ("up", c_float * 4),
  203.         ("front", c_float * 4),
  204.         ("posit", c_float * 4)
  205.     ]
  206.    
  207.     def __iter__(self):
  208.         yield [f for f in self.right]
  209.         yield [f for f in self.up]
  210.         yield [f for f in self.front]
  211.         yield [f for f in self.posit]
  212.  
  213. class Quaternion(StructureJSON):
  214.     _fields_ = [
  215.         ("s", c_float),
  216.         ("x", c_float),
  217.         ("y", c_float),
  218.         ("z", c_float)
  219.     ]
  220.    
  221.     def __iter__(self):
  222.         yield s
  223.         yield x
  224.         yield y
  225.         yield z
  226.  
  227. class AnimKey(StructureJSON):
  228.     _fields_ = [
  229.         ("frame", c_float),
  230.         ("type", c_uint32),
  231.         ("quat", Quaternion),
  232.         ("vect", Vector)
  233.     ]
  234.  
  235. class BlockHeader(StructureJSON):
  236.     _fields_ = [
  237.         ("fileType", c_ubyte * 4),
  238.         ("verID", c_uint32),
  239.         ("blockCount", c_uint32),
  240.         ("notUsed", c_ubyte * 32)
  241.     ]
  242.  
  243. class BlockInfo(StructureJSON):
  244.     _fields_ = [
  245.         ("key", c_uint32),
  246.         ("size", c_uint32)
  247.     ]
  248.  
  249. class Sphere(StructureJSON):
  250.     _fields_ = [
  251.         ("radius", c_float),
  252.         ("matrix", Matrix),
  253.         ("Width", c_float),
  254.         ("Height", c_float),
  255.         ("Breadth", c_float)
  256.     ]
  257.  
  258. class MSH_Header(StructureJSON):
  259.     _fields_ = [
  260.         ("dummy", c_float),
  261.         ("scale", c_float),
  262.         ("indexed", c_uint32),
  263.         ("moveAnim", c_uint32),
  264.         ("oldPipe", c_uint32),
  265.         ("isSingleGeometry", c_uint32),
  266.         ("skinned", c_uint32)
  267.     ]
  268.  
  269. class FaceObj(StructureJSON):
  270.     _fields_ = [
  271.         ("buckyIndex", c_uint16),
  272.         ("verts", c_uint16 * 3),
  273.         ("norms", c_uint16 * 3),
  274.         ("uvs", c_uint16 * 3)
  275.     ]
  276.  
  277. class VertIndex(StructureJSON):
  278.     _fields_ = [
  279.         ("weight", c_float),
  280.         ("index", c_uint16),
  281.     ]
  282.  
  283. class VertIndexContainer:
  284.     def __init__(self, count, array):
  285.         self.count = count
  286.         self.array = array
  287.    
  288.     def json(self):
  289.         return {
  290.             "count": self.count,
  291.             "array": [item.json() for item in self.array],
  292.         }
  293.  
  294. class Plane(StructureJSON):
  295.     _fields_ = [
  296.         ("d", c_float),
  297.         ("x", c_float),
  298.         ("y", c_float),
  299.         ("z", c_float)
  300.     ]
  301.    
  302.     def __iter__(self):
  303.         yield d
  304.         yield x
  305.         yield y
  306.         yield z
  307.  
  308. class BuckyDesc:
  309.     def __init__(self, f=None):
  310.         self.flags = c_uint32()
  311.         self.vert_count = c_uint32()
  312.         self.index_count = c_uint32()
  313.        
  314.         self.material = None
  315.         self.texture = None
  316.         self.end_marker = False
  317.        
  318.         if f:
  319.             self.read(f)
  320.    
  321.     def read(self, f):
  322.         f.readinto(self.flags)
  323.         f.readinto(self.index_count)
  324.         f.readinto(self.vert_count)
  325.         self.material, self.texture, self.end_marker = read_optional_blocks(f)
  326.    
  327.     def json(self):
  328.         j = {
  329.             "flags": self.flags.value,
  330.             "indexCount": self.vert_count.value,
  331.             "vertCount": self.index_count.value
  332.         }
  333.        
  334.         if self.material:
  335.             j["matBlock"] = self.material.json()
  336.        
  337.         if self.texture:
  338.             j["matTexture"] = self.texture.json()
  339.        
  340.         return j
  341.  
  342. class VertGroup:
  343.     def __init__(self, f=None):
  344.         self.state_index = c_uint32()
  345.         self.vert_count = c_uint32()
  346.         self.index_count = c_uint32()
  347.         self.plane_index = c_uint32()
  348.        
  349.         self.material = None
  350.         self.texture = None
  351.         self.end_marker = False
  352.        
  353.         if f:
  354.             self.read(f)
  355.    
  356.     def read(self, f):
  357.         f.readinto(self.state_index)
  358.         f.readinto(self.vert_count)
  359.         f.readinto(self.index_count)
  360.         f.readinto(self.plane_index)
  361.         self.material, self.texture, self.end_marker = read_optional_blocks(f)
  362.    
  363.     def json(self):
  364.         j = {
  365.             "stateIndex": self.state_index.value,
  366.             "vertCount": self.vert_count.value,
  367.             "indexCount": self.index_count.value,
  368.             "planeIndex": self.plane_index.value
  369.         }
  370.        
  371.         if self.material:
  372.             j["matBlock"] = self.material.json()
  373.        
  374.         if self.texture:
  375.             j["matTexture"] = self.texture.json()
  376.        
  377.         return j
  378.  
  379. class Material:
  380.     def __init__(self, f=None):
  381.         # Default material names are generated with a CRC function
  382.         # from diffuse, specular, etc inputs into an unsigned 32 bit integer,
  383.         # which is then turned into a hex string appended to "mat".
  384.         self.name = ""
  385.         self.diffuse = ColorValue()
  386.         self.specular = ColorValue()
  387.         self.specular_power = c_float()
  388.         self.emissive = ColorValue()
  389.         self.ambient = ColorValue()
  390.        
  391.         if f:
  392.             self.read(f)
  393.    
  394.     def read(self, f):
  395.         name_length = c_uint16()
  396.         f.readinto(name_length)
  397.         self.name = f.read(name_length.value)[0:-1].decode("ascii", "ignore")
  398.         f.readinto(self.diffuse)
  399.         f.readinto(self.specular)
  400.         f.readinto(self.specular_power)
  401.         f.readinto(self.emissive)
  402.         f.readinto(self.ambient)
  403.    
  404.     def json(self):
  405.         return {
  406.             "name": {
  407.                 "string": self.name,
  408.                 "length": len(self.name)+1,
  409.             },
  410.        
  411.             "diffuse": self.diffuse.json(),
  412.             "specular": self.specular.json(),
  413.             "specularPower": self.specular_power.value,
  414.             "emissive": self.emissive.json(),
  415.             "ambient": self.ambient.json()
  416.         }
  417.  
  418. class Texture:
  419.     def __init__(self, f=None):
  420.         self.name = ""
  421.         self.texture_type = c_uint32()
  422.         self.mipmaps = c_uint32()
  423.        
  424.         if f:
  425.             self.read(f)
  426.    
  427.     def read(self, f):
  428.         name_length = c_uint16()
  429.         f.readinto(name_length)
  430.         self.name = f.read(name_length.value)[0:-1].decode("ascii", "ignore")
  431.         f.readinto(self.texture_type)
  432.         f.readinto(self.mipmaps)
  433.    
  434.     def json(self):
  435.         return {
  436.             "name": {
  437.                 "string": self.name,
  438.                 "length": len(self.name)+1,
  439.             },
  440.            
  441.             "mipMapCount": self.mipmaps.value,
  442.             "type": self.texture_type.value
  443.         }
  444.  
  445. class Anim:
  446.     def __init__(self, f=None):
  447.         self.index = c_uint32()
  448.         self.max_frame = c_float()
  449.         self.states = []
  450.        
  451.         if f:
  452.             self.read(f)
  453.    
  454.     def read(self, f):
  455.         f.readinto(self.index)
  456.         f.readinto(self.max_frame)
  457.        
  458.         count = c_uint32()
  459.         f.readinto(count)
  460.         self.states = (AnimKey * count.value)()
  461.         f.readinto(self.states)
  462.    
  463.     def json(self):
  464.         return {
  465.             "index": self.index.value,
  466.             "maxFrame": self.max_frame.value,
  467.             "keys": [state.json() for state in self.states]
  468.         }
  469.  
  470. class AnimList:
  471.     def __init__(self, f=None):
  472.         self.name = ""
  473.         self.anim_type = c_uint32()
  474.         self.max_frame = c_float()
  475.         self.end_frame = c_float()
  476.        
  477.         self.states = []
  478.         self.animations = []
  479.        
  480.         if f:
  481.             self.read(f)
  482.    
  483.     def read(self, f):
  484.         count = c_uint32()
  485.         name_length = c_uint16()
  486.         f.readinto(name_length)
  487.         self.name = f.read(name_length.value)[0:-1].decode("ascii", "ignore")
  488.        
  489.         f.readinto(self.anim_type)
  490.         f.readinto(self.max_frame)
  491.         f.readinto(self.end_frame)
  492.        
  493.         f.readinto(count)
  494.         self.states = (AnimKey * count.value)()
  495.         f.readinto(self.states)
  496.        
  497.         f.readinto(count)
  498.         self.animations = []
  499.         for animation_index in range(count.value):
  500.             self.animations += [Anim(f)]
  501.    
  502.     def json(self):
  503.         return {
  504.             "name": {
  505.                 "string": self.name,
  506.                 "length": len(self.name)+1,
  507.             },
  508.            
  509.             "type": self.anim_type.value,
  510.             "maxFrame": self.max_frame.value,
  511.             "endFrame": self.end_frame.value,
  512.            
  513.             "animations": [animation.json() for animation in self.animations],
  514.             "states": [animkey.json() for animkey in self.states]
  515.         }
  516.  
  517. class Mesh:
  518.     def __init__(self, f, block, level=0):
  519.         self.block = block
  520.        
  521.         self.name = ""
  522.         self.state_index = c_uint32()
  523.         self.is_single_geom = c_int32()
  524.         self.renderflags = c_uint32()
  525.         self.matrix = Matrix()
  526.        
  527.         self.vert_colors = (Color * 0)()
  528.         self.planes = (Plane * 0)()
  529.         self.vertex = (Vertex * 0)()
  530.         self.vert_groups = []
  531.         self.indices = (c_uint16 * 0)()
  532.        
  533.         self.child = None
  534.         self.sibling = None
  535.        
  536.         # Used to hierarchize like an XSI
  537.         self.meshes = []
  538.         self.level = level
  539.        
  540.         if f:
  541.             self.read(f)
  542.    
  543.     def read(self, f):
  544.         count = c_uint32()
  545.         name_length = c_uint16()
  546.        
  547.         f.readinto(name_length)
  548.         self.name = f.read(name_length.value)[0:-1].decode("ascii", "ignore")
  549.        
  550.         if len(self.name) <= 0:
  551.             raise ZeroLengthName()
  552.        
  553.         f.readinto(self.state_index)
  554.         f.readinto(self.is_single_geom)
  555.         f.readinto(self.renderflags)
  556.         f.readinto(self.matrix)
  557.        
  558.         f.readinto(count)
  559.         self.vert_colors = (Color * count.value)()
  560.         f.readinto(self.vert_colors)
  561.        
  562.         f.readinto(count)
  563.         self.planes = (Plane * count.value)()
  564.         f.readinto(self.planes)
  565.        
  566.         f.readinto(count)
  567.         self.vertex = (Vertex * count.value)()
  568.         f.readinto(self.vertex)
  569.        
  570.         f.readinto(count)
  571.         self.vert_groups = []
  572.         for i in range(count.value):
  573.             self.vert_groups += [VertGroup(f)]
  574.        
  575.         f.readinto(count)
  576.         self.indices = (c_uint16 * count.value)()
  577.         f.readinto(self.indices)
  578.    
  579.     def walk(self, indentation_level=1):
  580.         for mesh in self.meshes:
  581.             yield mesh, indentation_level
  582.             yield from mesh.walk(indentation_level+1)
  583.    
  584.     def json(self):
  585.         j = {
  586.             "name": {
  587.                 "string": self.name,
  588.                 "length": len(self.name)+1
  589.             },
  590.            
  591.             "isSingleGeometry": self.is_single_geom.value,
  592.             "renderFlags": self.renderflags.value,
  593.             "stateIndex": self.state_index.value,
  594.             "objectMatrix": self.matrix.json(),
  595.            
  596.             "localColors": [color.json() for color in self.vert_colors],
  597.             "localGroups": [group.json() for group in self.vert_groups],
  598.             "localIndices": [index for index in self.indices],
  599.             "localPlanes": [plane.json() for plane in self.planes],
  600.             "localVertex": [vertex.json() for vertex in self.vertex]
  601.         }
  602.        
  603.         if self.child:
  604.             j["child"] = self.child.json()
  605.        
  606.         if self.sibling:
  607.             j["siblings"] = [self.sibling.json()]
  608.        
  609.         return j
  610.  
  611. class Block:
  612.     def __init__(self, f, msh):
  613.         self.msh = msh
  614.        
  615.         self.block_info = BlockInfo()
  616.         self.sphere = Sphere()
  617.         self.msh_header = MSH_Header()
  618.         self.name = ""
  619.        
  620.         self.vertices = (Vector * 0)()
  621.         self.vertex_normals = (Vector * 0)()
  622.         self.uvs = (UVPair * 0)()
  623.         self.vert_colors = (Color * 0)()
  624.         self.faces = (FaceObj * 0)()
  625.         self.buckydescriptions = []
  626.         self.vert_to_state = []
  627.         self.vert_groups = []
  628.         self.indices = (c_uint16 * 0)()
  629.         self.planes = (Plane * 0)()
  630.         self.state_matrices = (Matrix * 0)()
  631.         self.states = (AnimKey * 0)()
  632.         self.anim_list = []
  633.         self.root = None
  634.        
  635.         if f:
  636.             self.read(f)
  637.    
  638.     def read(self, f):
  639.         f.readinto(self.block_info)
  640.        
  641.         count = c_uint32()
  642.         block_type = c_uint32()
  643.         name_length = c_uint16()
  644.         f.readinto(name_length)
  645.         self.name = f.read(name_length.value)[0:-1].decode("ascii", "ignore")
  646.         f.readinto(self.sphere)
  647.         f.readinto(self.msh_header)
  648.        
  649.         f.readinto(count)
  650.         self.vertices = (Vector * count.value)()
  651.         f.readinto(self.vertices)
  652.        
  653.         f.readinto(count)
  654.         self.vertex_normals = (Vector * count.value)()
  655.         f.readinto(self.vertex_normals)
  656.        
  657.         f.readinto(count)
  658.         self.uvs = (UVPair * count.value)()
  659.         f.readinto(self.uvs)
  660.        
  661.         f.readinto(count)
  662.         self.vert_colors = (Color * count.value)()
  663.         f.readinto(self.vert_colors)
  664.        
  665.         f.readinto(count)
  666.         self.faces = (FaceObj * count.value)()
  667.         f.readinto(self.faces)
  668.        
  669.         f.readinto(count)
  670.         for index in range(count.value):
  671.             self.buckydescriptions += [BuckyDesc(f)]
  672.        
  673.         f.readinto(count)
  674.         self.vert_to_state = []
  675.         array_count = c_uint32()
  676.         for index in range(count.value):
  677.             f.readinto(array_count)
  678.             array = []
  679.             for count_index in range(array_count.value):
  680.                 # f.readinto() for VertIndex causes 8 byte read.
  681.                 weight = c_float()
  682.                 vertex_index = c_uint16()
  683.                 f.readinto(weight)
  684.                 f.readinto(vertex_index)
  685.                 array += [VertIndex(weight, vertex_index)]
  686.            
  687.             self.vert_to_state += [VertIndexContainer(array_count.value, array)]
  688.        
  689.         f.readinto(count)
  690.         self.vert_groups = []
  691.         for i in range(count.value):
  692.             self.vert_groups += [VertGroup(f)]
  693.        
  694.         f.readinto(count)
  695.         self.indices = (c_uint16 * count.value)()
  696.         f.readinto(self.indices)
  697.        
  698.         f.readinto(count)
  699.         self.planes = (Plane * count.value)()
  700.         f.readinto(self.planes)
  701.        
  702.         f.readinto(count)
  703.         self.state_matrices = (Matrix * count.value)()
  704.         f.readinto(self.state_matrices)
  705.        
  706.         f.readinto(count)
  707.         self.states = (AnimKey * count.value)()
  708.         f.readinto(self.states)
  709.        
  710.         f.readinto(count)
  711.         self.animation_list = []
  712.         for animlist_index in range(count.value):
  713.             self.animation_list += [AnimList(f)]
  714.        
  715.         self.root = Mesh(f, self, 0)
  716.         self.meshes = [self.root]
  717.        
  718.         in_mesh = 1
  719.         indentation_level = 0 # 0 is root level
  720.         mesh_at = [self.root]
  721.        
  722.         while in_mesh > 0:
  723.             f.readinto(block_type)
  724.             if block_type.value == MSH_CHILD:
  725.                 this_mesh = Mesh(f, self, indentation_level + 1)
  726.                 mesh_at[indentation_level].child = this_mesh
  727.                 mesh_at[indentation_level].meshes += [this_mesh]
  728.                
  729.                 indentation_level += 1
  730.                 in_mesh += 1
  731.                
  732.                 if len(mesh_at) < indentation_level+1:
  733.                     mesh_at += [this_mesh]
  734.                 else:
  735.                     mesh_at[indentation_level] = this_mesh
  736.            
  737.             elif block_type.value == MSH_SIBLING:
  738.                 this_mesh = Mesh(f, self, indentation_level)
  739.                 mesh_at[indentation_level].sibling = this_mesh
  740.                 mesh_at[indentation_level-1].meshes += [this_mesh]
  741.                 mesh_at[indentation_level] = this_mesh
  742.                
  743.                 in_mesh += 1
  744.            
  745.             elif block_type.value == MSH_END:
  746.                 in_mesh -= 1
  747.                
  748.                 while in_mesh < indentation_level:
  749.                     indentation_level -= 1
  750.            
  751.             else:
  752.                 raise UnknownBlock("Unhandled Mesh Block %s - Note that oldpoop is not supported." % hex(block_type.value))
  753.        
  754.         f.readinto(block_type)
  755.        
  756.         if block_type.value != MSH_EOF:
  757.             raise InvalidFormat("Unexpected EOF")
  758.    
  759.     def walk(self):
  760.         if self.root:
  761.             yield self.root, 0 # 0 indentation level
  762.             yield from self.root.walk()
  763.    
  764.     def json(self):
  765.         j = {
  766.             "name": {
  767.                 "string": self.name,
  768.                 "length": len(self.name)+1
  769.             },
  770.            
  771.             "bigSphere": self.sphere.json(),
  772.             "blockInfo": self.block_info.json(),
  773.            
  774.             "vertices": [vertex.json() for vertex in self.vertices],
  775.             "normals": [nomral.json() for nomral in self.vertex_normals],
  776.             "uvs": [uv.json() for uv in self.uvs],
  777.             "colors": [color.json() for color in self.vert_colors],
  778.             "faces": [face.json() for face in self.faces],
  779.             "buckys": [bucky.json() for bucky in self.buckydescriptions],
  780.             "vertToState": [vts.json() for vts in self.vert_to_state],
  781.             "groups": [vg.json() for vg in self.vert_groups],
  782.             "indices": [index for index in self.indices],
  783.             "planes": [plane.json() for plane in self.planes],
  784.             "stateMats": [state_mat.json() for state_mat in self.state_matrices],
  785.             "States": [state.json() for state in self.states],
  786.             "animList": [al.json() for al in self.animation_list],
  787.            
  788.             "mesh": self.root.json()
  789.         }
  790.        
  791.         j.update(self.msh_header.json())
  792.        
  793.         return j
  794.  
  795. class MSH:
  796.     def __init__(self, file_path):
  797.         self.block_header = BlockHeader()
  798.         self.blocks = []
  799.        
  800.         with open(file_path, "rb") as f:
  801.             self.read(f)
  802.    
  803.     def read(self, f):
  804.         """Read from MSH open in binary read mode."""
  805.         f.readinto(self.block_header)
  806.        
  807.         for block_index in range(self.block_header.blockCount):
  808.             self.blocks += [Block(f, self)]
  809.    
  810.     def write(self, f):
  811.         """Write MSH to file open in binary write mode, write to a file path or writable object."""
  812.         DISABLE_END_OF_OPTIONALS = True # Prevent older .msh parsers from crashing (e.g. OMDL1 viewer)
  813.        
  814.         locally_opened = False
  815.         if not hasattr(f, "write"):
  816.             f = open(f, "wb")
  817.             locally_opened = True
  818.        
  819.         def write_name(f, name):
  820.             written_name = name.encode() + b"\0"
  821.             f.write(c_uint16(len(written_name)))
  822.             f.write(written_name)
  823.        
  824.         def write_optionals(f, optionals_container):
  825.             if optionals_container.material:
  826.                 f.write(c_uint32(MSH_MATERIAL))
  827.                 write_name(f, optionals_container.material.name)
  828.                 f.write(optionals_container.material.diffuse)
  829.                 f.write(optionals_container.material.specular)
  830.                 f.write(optionals_container.material.specular_power)
  831.                 f.write(optionals_container.material.emissive)
  832.                 f.write(optionals_container.material.ambient)
  833.            
  834.             if optionals_container.texture:
  835.                 f.write(c_uint32(MSH_TEXTURE))
  836.                 write_name(f, optionals_container.texture.name)
  837.                 f.write(optionals_container.texture.texture_type)
  838.                 f.write(optionals_container.texture.mipmaps)
  839.            
  840.             if optionals_container.end_marker and not DISABLE_END_OF_OPTIONALS:
  841.                 f.write(c_uint32(MSH_END_OF_OPTIONALS))
  842.        
  843.         def write_vert_group(f, vert_group):
  844.             f.write(vert_group.state_index)
  845.             f.write(vert_group.vert_count)
  846.             f.write(vert_group.index_count)
  847.             f.write(vert_group.plane_index)
  848.             write_optionals(f, vert_group)
  849.        
  850.         def write_mesh(f, mesh):
  851.             write_name(f, mesh.name)
  852.             f.write(mesh.state_index)
  853.             f.write(mesh.is_single_geom)
  854.             f.write(mesh.renderflags)
  855.             f.write(mesh.matrix)
  856.             f.write(c_uint32(len(mesh.vert_colors)))
  857.             f.write(mesh.vert_colors)
  858.             f.write(c_uint32(len(mesh.planes)))
  859.             f.write(mesh.planes)
  860.             f.write(c_uint32(len(mesh.vertex)))
  861.             f.write(mesh.vertex)
  862.            
  863.             f.write(c_uint32(len(mesh.vert_groups)))
  864.             for vert_group in mesh.vert_groups:
  865.                 write_vert_group(f, vert_group)
  866.            
  867.             f.write(c_uint32(len(mesh.indices)))
  868.             f.write(mesh.indices)
  869.            
  870.             if mesh.child:
  871.                 f.write(c_uint32(MSH_CHILD))
  872.                 write_mesh(f, mesh.child)
  873.            
  874.             f.write(c_uint32(MSH_END))
  875.            
  876.             if mesh.sibling:
  877.                 f.write(c_uint32(MSH_SIBLING))
  878.                 write_mesh(f, mesh.sibling)
  879.        
  880.         self.block_header.blockCount = len(self.blocks)
  881.         f.write(self.block_header)
  882.         for block in self.blocks:
  883.             f.write(block.block_info)
  884.             write_name(f, block.name)
  885.             f.write(block.sphere)
  886.             f.write(block.msh_header)
  887.            
  888.             f.write(c_uint32(len(block.vertices)))
  889.             f.write(block.vertices)
  890.             f.write(c_uint32(len(block.vertex_normals)))
  891.             f.write(block.vertex_normals)
  892.             f.write(c_uint32(len(block.uvs)))
  893.             f.write(block.uvs)
  894.             f.write(c_uint32(len(block.vert_colors)))
  895.             f.write(block.vert_colors)
  896.             f.write(c_uint32(len(block.faces)))
  897.             f.write(block.faces)
  898.            
  899.             f.write(c_uint32(len(block.buckydescriptions)))
  900.             for bucky in block.buckydescriptions:
  901.                 f.write(bucky.flags)
  902.                 f.write(bucky.index_count)
  903.                 f.write(bucky.vert_count)
  904.                 write_optionals(f, bucky)
  905.            
  906.             f.write(c_uint32(len(block.vert_to_state)))
  907.             for vts_container in block.vert_to_state:
  908.                 f.write(c_uint32(vts_container.count))
  909.                 for vts in vts_container.array:
  910.                     f.write(c_float(vts.weight))
  911.                     f.write(c_uint16(vts.index))
  912.            
  913.             f.write(c_uint32(len(block.vert_groups)))
  914.             for vert_group in block.vert_groups:
  915.                 write_vert_group(f, vert_group)
  916.            
  917.             f.write(c_uint32(len(block.indices)))
  918.             f.write(block.indices)
  919.             f.write(c_uint32(len(block.planes)))
  920.             f.write(block.planes)
  921.             f.write(c_uint32(len(block.state_matrices)))
  922.             f.write(block.state_matrices)
  923.             f.write(c_uint32(len(block.states)))
  924.             f.write(block.states)
  925.            
  926.             f.write(c_uint32(len(block.animation_list)))
  927.             for al in block.animation_list:
  928.                 write_name(f, al.name)
  929.                 f.write(al.anim_type)
  930.                 f.write(al.max_frame)
  931.                 f.write(al.end_frame)
  932.                 f.write(c_uint32(len(al.states)))
  933.                 f.write(al.states)
  934.                 f.write(c_uint32(len(al.animations)))
  935.                 for a in al.animations:
  936.                     f.write(a.index)
  937.                     f.write(a.max_frame)
  938.                     f.write(c_uint32(len(a.states)))
  939.                     f.write(a.states)
  940.            
  941.             if block.root:
  942.                 write_mesh(f, block.root)
  943.            
  944.             f.write(c_uint32(MSH_EOF))
  945.        
  946.         if locally_opened:
  947.             f.close()
  948.    
  949.     def walk(self):
  950.         for block in self.blocks:
  951.             yield from block.walk()
  952.    
  953.     def to_json(self, file_path, indent=None):
  954.         with open(file_path, "w") as f:
  955.             j = {
  956.                 "verID": self.block_header.verID,
  957.                 "blockCount": self.block_header.blockCount,
  958.                 "fileType": bytearray(self.block_header.fileType).decode("ascii", "ignore"),
  959.                 "notUsed": " ".join(["%02X" % x for x in self.block_header.notUsed])
  960.             }
  961.            
  962.             j["meshRoot"] = [block.json() for block in self.blocks]
  963.            
  964.             # If you want improved performance in writing & parsing JSON data, do not sort keys or indent.
  965.             f.write(json.dumps(j, sort_keys=bool(indent), indent=indent))
  966.  
  967. # Dump .msh data into humanly readable JSON file
  968. if __name__ == "__main__":
  969.     import sys, os
  970.     for msh_file in sys.argv[1::]:
  971.         msh = MSH(msh_file)
  972.         json_file = os.path.join(os.path.dirname(msh_file), os.path.basename(msh_file) + ".json")
  973.         if not os.path.exists(json_file):
  974.             msh.to_json(json_file, "\t")
  975.  
Add Comment
Please, Sign In to add comment