Advertisement
Graf_Rav

Untitled

Jan 7th, 2018
201
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 12.38 KB | None | 0 0
  1. import bpy
  2. import bpy.props
  3. import bpy_extras
  4. import mathutils
  5.  
  6. from mathutils import Vector, Matrix
  7. from bpy_extras.io_utils import ImportHelper, orientation_helper_factory, axis_conversion, _check_axis_conversion
  8. from bpy.props import *
  9.  
  10. import bmesh
  11.  
  12. import re
  13. import glob
  14. import os
  15. import hashlib
  16.  
  17. from struct import *
  18.  
  19. bl_info = {
  20.     "name": "Ninja Ripper mesh data (.rip)",
  21.     "author": "Alexander Gavrilov",
  22.     "version": (0, 2),
  23.     "blender": (2, 77, 0),
  24.     "location": "File > Import-Export > Ninja Ripper (.rip) ",
  25.     "description": "Import Ninja Ripper mesh data",
  26.     "warning": "",
  27.     "category": "Import-Export",
  28. }
  29.  
  30. def read_uint(fh):
  31.     return unpack('I', fh.read(4))[0]
  32.  
  33. def read_string(fh):
  34.     str = b''
  35.     while True:
  36.         c = fh.read(1)
  37.         if c == b'\0' or c == b'':
  38.             return str.decode('cp437')
  39.         else:
  40.             str = str + c
  41.  
  42. def concat_attrs(datalists):
  43.     result = []
  44.     for i in range(len(datalists[0])):
  45.         data = []
  46.         for l in datalists:
  47.             data.extend(l[i])
  48.         result.append(data)
  49.     return result
  50.  
  51. class RipLogInfo(object):
  52.     def __init__(self):
  53.         self.log_file_cache = {}
  54.  
  55.     def verify_texture_match(self, basename, stages, texlist):
  56.         if len(stages.keys()) != len(texlist):
  57.             print('Texture count mismatch vs log for %s: %d vs %d' %
  58.                     (basename, len(stages.keys()), len(texlist)))
  59.             return False
  60.  
  61.         for i,key in enumerate(stages.keys()):
  62.             if texlist[i].lower() != stages[key].lower():
  63.                 print('Texture name mismatch vs log for %s[%d]: %s vs %s' %
  64.                         (basename, i, stages[key], texlist[i]))
  65.                 return False
  66.  
  67.         return True
  68.  
  69.     def get_texture_stages(self, filename, texlist):
  70.         dirname, basename = os.path.split(os.path.realpath(filename))
  71.         if dirname == '' or basename == '':
  72.             return None
  73.  
  74.         logdir, subdir = os.path.split(dirname)
  75.         if logdir == '' or subdir == '':
  76.             return None
  77.  
  78.         logkey = logdir.lower()
  79.         if logkey not in self.log_file_cache:
  80.             self.log_file_cache[logkey] = self.parse_log(logdir)
  81.  
  82.         logtable = self.log_file_cache[logkey]
  83.         filetable = logtable.get(subdir.lower(), {})
  84.         stages = filetable.get(basename.lower(), None)
  85.  
  86.         if stages and self.verify_texture_match(filename, stages, texlist):
  87.             return stages
  88.         else:
  89.             return None
  90.  
  91.     def find_log(self, logdir):
  92.         if not os.path.isdir(logdir):
  93.             return None
  94.  
  95.         for file in os.listdir(logdir):
  96.             if file.lower().endswith(".exe.log.txt"):
  97.                 return os.path.join(logdir, file)
  98.  
  99.         return None
  100.  
  101.     def parse_log(self, logdir):
  102.         logpath = self.find_log(logdir)
  103.         if not logpath:
  104.             return {}
  105.  
  106.         fh = open(logpath, "rt", encoding='cp437')
  107.         try:
  108.             stage_pattern = re.compile(r'^\S+\s+\S+\s+Texture stage #(\d+)\s.*\\([^\\]+)\\(Tex_\d+_\d+\.dds)\s*$')
  109.             mesh_pattern = re.compile(r'^\S+\s+\S+\s+Mesh saved as:.*\\([^\\]+)\\(Mesh_\d+\.rip)\s*$')
  110.             logtable = {}
  111.             stage_accum = {}
  112.  
  113.             for line in fh:
  114.                 match = mesh_pattern.fullmatch(line)
  115.                 if match:
  116.                     subdir = match.group(1).lower()
  117.                     if subdir not in logtable:
  118.                         logtable[subdir] = {}
  119.                     logtable[subdir][match.group(2).lower()] = stage_accum
  120.                     stage_accum = {}
  121.                 else:
  122.                     match = stage_pattern.fullmatch(line)
  123.                     if match:
  124.                         stage_accum[int(match.group(1))] = match.group(3)
  125.  
  126.             return logtable
  127.         finally:
  128.             fh.close()
  129.  
  130.  
  131. class HLSLShaderInfo(object):
  132.     def __init__(self, fname):
  133.         self.filename = fname
  134.         self.lines = []
  135.         self.version = None
  136.         self.used_attrs = {}
  137.         self.used_samplers = {}
  138.  
  139.     def parse_file(self):
  140.         fh = open(self.filename, "rt", encoding='cp437')
  141.         try:
  142.             comment_pattern = re.compile('//|#')
  143.             split_pattern = re.compile('^\s*(\S+)(?:\s+(\S|\S.*\S))?\s*$')
  144.  
  145.             for line in fh:
  146.                 m = comment_pattern.search(line)
  147.                 if m:
  148.                     line = line[0:m.start()]
  149.  
  150.                 m = split_pattern.fullmatch(line.lower())
  151.                 if not m:
  152.                     continue
  153.  
  154.                 cmd = [m.group(1)]
  155.                 if m.group(2):
  156.                     cmd.extend(map(lambda s: s.strip(), m.group(2).split(',')))
  157.  
  158.                 self.lines.append(cmd)
  159.  
  160.             # Check valid version string:
  161.             if len(self.lines) == 0 or not re.fullmatch('[pv]s_\d+_\d+', self.lines[0][0]):
  162.                 return False
  163.  
  164.             self.version = self.lines[0][0]
  165.  
  166.             # Scan for use declarations
  167.             declname_pattern = re.compile('dcl_([a-z]+)(?:([0-9]+).*|[^a-z0-9].*)?')
  168.  
  169.             for cmd in self.lines:
  170.                 if len(cmd) < 2 or not cmd[0].startswith('dcl_'):
  171.                     continue
  172.                 if cmd[1].startswith('v'):
  173.                     m = declname_pattern.fullmatch(cmd[0])
  174.                     if m:
  175.                         attr = m.group(1).upper()
  176.                         id = int(m.group(2) or 0)
  177.                         if attr not in self.used_attrs:
  178.                             self.used_attrs[attr] = set([id])
  179.                         else:
  180.                             self.used_attrs[attr].add(id)
  181.  
  182.                 elif cmd[1].startswith('s'):
  183.                     m = re.match('^s(\d+)', cmd[1])
  184.                     if m:
  185.                         self.used_samplers[int(m.group(1))] = cmd[0][4:]
  186.  
  187.             return True
  188.         finally:
  189.             fh.close()
  190.  
  191. class RipFileAttribute(object):
  192.     def __init__(self, fh):
  193.         self.semantic = read_string(fh)
  194.         self.semantic_index = read_uint(fh)
  195.         self.offset = read_uint(fh)
  196.         self.size = read_uint(fh)
  197.         self.end = self.offset + self.size
  198.         self.items = read_uint(fh)
  199.  
  200.         format = ''
  201.         codes = ['f', 'I', 'i']
  202.         for j in range(self.items):
  203.             id = read_uint(fh)
  204.             format = format + (codes[id] if id <= 2 else 'I')
  205.  
  206.         self.format = format
  207.         self.data = []
  208.  
  209.     def get_hashtag(self):
  210.         return "[%s:%d:%d:%d:%s]" % (self.semantic, self.semantic_index, self.offset, self.size, self.format)
  211.  
  212.     def parse_vertex(self, buffer):
  213.         self.data.append(unpack(self.format, buffer[self.offset : self.end]))
  214.  
  215.     def as_floats(self, arity=4, divisor=1.0):
  216.         if self.format == 'f'*min(arity,self.items):
  217.             return self.data
  218.         elif self.format[0:arity] == 'f'*arity:
  219.             return list(map(lambda v: v[0:arity], self.data))
  220.         else:
  221.             def convert(item):
  222.                 return tuple(map(lambda v: float(v)/divisor, item[0:arity]))
  223.             return list(map(convert, self.data))
  224.  
  225. class RipFile(object):
  226.     def __init__(self, filename, riplog=None):
  227.         self.filename = filename
  228.         self.riplog = riplog
  229.         self.dirname = os.path.dirname(filename)
  230.         self.basename = os.path.basename(filename)
  231.         self.faces = []
  232.         self.attributes = []
  233.         self.shaders = []
  234.         self.textures = []
  235.         self.texture_stages = None
  236.         self.num_verts = 0
  237.         self.shader_vert = None
  238.         self.shader_frag = None
  239.         self.data_hash = ""
  240.  
  241.     def parse_file(self):
  242.         fh = open(self.filename, "rb")
  243.         try:
  244.             magic = read_uint(fh)
  245.             if magic != 0xDEADC0DE:
  246.                 raise RuntimeError("Invalid file magic: %08d" % (magic))
  247.  
  248.             version = read_uint(fh)
  249.             if version != 4:
  250.                 raise RuntimeError("Invalid file version: %d" % (version))
  251.  
  252.             num_faces = read_uint(fh)
  253.             self.num_verts = read_uint(fh)
  254.             block_size = read_uint(fh)
  255.             num_tex = read_uint(fh)
  256.             num_shaders = read_uint(fh)
  257.             num_attrs = read_uint(fh)
  258.  
  259.             datahash = hashlib.sha1()
  260.  
  261.             for i in range(num_attrs):
  262.                 attr = RipFileAttribute(fh)
  263.                 self.attributes.append(attr)
  264.                 datahash.update(attr.get_hashtag().encode('utf-8'))
  265.  
  266.             for i in range(num_tex):
  267.                 self.textures.append(read_string(fh))
  268.  
  269.             if self.riplog:
  270.                 self.texture_stages = self.riplog.get_texture_stages(self.filename, self.textures)
  271.  
  272.             for i in range(num_shaders):
  273.                 self.shaders.append(read_string(fh))
  274.  
  275.             for i in range(num_faces):
  276.                 data = fh.read(4*3)
  277.                 face = unpack('III', data)
  278.  
  279.                 # Omit degenerate triangles - they are sometimes used to merge strips
  280.                 if face[0] != face[1] and face[1] != face[2] and face[0] != face[2]:
  281.                     self.faces.append(face)
  282.                     datahash.update(data)
  283.  
  284.             datahash.update(b"|")
  285.  
  286.             for i in range(self.num_verts):
  287.                 data = fh.read(block_size)
  288.                 datahash.update(data)
  289.                 for attr in self.attributes:
  290.                     attr.parse_vertex(data)
  291.  
  292.             self.data_hash = datahash.hexdigest()
  293.         finally:
  294.             fh.close()
  295.  
  296.     def parse_shaders(self):
  297.         dirs = [
  298.             self.dirname,
  299.             os.path.join(self.dirname, "..", "Shaders")
  300.         ]
  301.  
  302.         for fname in self.shaders:
  303.             for dir in dirs:
  304.                 path = os.path.join(dir, fname)
  305.                 if os.path.isfile(path):
  306.                     shader = HLSLShaderInfo(path)
  307.                     if shader.parse_file():
  308.                         if shader.version.startswith('v'):
  309.                             self.shader_vert = shader
  310.                         else:
  311.                             self.shader_frag = shader
  312.                     break
  313.  
  314.  
  315.     def find_attrs(self, semantic):
  316.         return [attr for attr in self.attributes if attr.semantic == semantic]
  317.  
  318.     def is_used_attr(self, attr):
  319.         if not self.shader_vert:
  320.             return True
  321.  
  322.         used = self.shader_vert.used_attrs
  323.         return attr.semantic in used and attr.semantic_index in used[attr.semantic]
  324.  
  325.     def get_textures(self, filter=True):
  326.         samplers = None
  327.         if self.shader_frag and filter:
  328.             samplers = self.shader_frag.used_samplers
  329.         if samplers and len(samplers) == 0:
  330.             return {}
  331.  
  332.         stages = self.texture_stages
  333.  
  334.         if not stages:
  335.             return dict(enumerate(self.textures))
  336.         else:
  337.             if filter:
  338.                 return dict([(id,stages[id]) for id in stages.keys() if id in samplers])
  339.             else:
  340.                 return stages
  341.  
  342.     def has_textures(self, filter=True):
  343.         return len(self.get_textures(filter)) > 0
  344.  
  345. class BaseDuplicateTracker(object):
  346.     def __init__(self):
  347.         self.file_hashes = {}
  348.         self.hash_missing_textures = True
  349.  
  350.     def hash_file(self, fname):
  351.         if not os.path.isfile(fname):
  352.             return None
  353.  
  354.         try:
  355.             hash = hashlib.sha1()
  356.             with open(fname, "rb") as f:
  357.                 for chunk in iter(lambda: f.read(4096), b""):
  358.                     hash.update(chunk)
  359.             return hash.hexdigest()
  360.         except IOError as e:
  361.             print("I/O error(%d): %s" % (e.errno, e.strerror))
  362.             return None
  363.  
  364.     def get_file_hash(self, filename):
  365.         fullpath = os.path.realpath(filename)
  366.         if fullpath in self.file_hashes:
  367.             return self.file_hashes[fullpath]
  368.  
  369.         hash = self.hash_file(fullpath)
  370.         self.file_hashes[fullpath] = hash
  371.         return hash
  372.  
  373.     def is_sharing_mesh(self):
  374.         return False
  375.  
  376.     def create_texture(self, fullpath):
  377.         try:
  378.             teximage = bpy.data.images.load(fullpath, True)
  379.             if teximage.users > 0:
  380.                 for tex in bpy.data.textures:
  381.                     if tex.type == 'IMAGE' and tex.image == teximage:
  382.                         return tex
  383.  
  384.             name,ext = os.path.splitext(os.path.basename(fullpath))
  385.             texobj = bpy.data.textures.new(name, type='IM
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement