#! /usr/bin/env python

## Format figured out by Mikael 'fpn' Kalms

import sys, stat, traceback, string     # std
from rwutils import *
import mshcls

f = None                                # current file

## Following routines are for chunks without fixed granularity
def materialschunksize(cnt):
    s = f.tell()
    for i in range(cnt):
        n = read_word(f)                # since n is allways 2
        for j in range(n * 2):          # im not sure if n * 2 is correct
            f.seek(read_word(f), 1)
        f.seek(12, 1)
    e = f.tell()
    f.seek(s)
    return e - s

def skeletonchunksize(cnt):
    s = f.tell()
    for i in range(cnt):
        len = read_word(f)
        f.seek(len + 2 + 4 + 4 + 4 + 14 + 4, 1)
    e = f.tell()
    f.seek(s)
    return e - s

def skeldatachunksize(cnt):             # not at all sure about this
    offsets = [24, 24, 72, 72]          # n >> 1?
    s = f.tell()
    for i in range(cnt):
        n = read_word(f)
        f.seek(offsets[n], 1)
    e = f.tell()
    f.seek(s)
    return e - s
        
C_CALCNEXT = 0                          # fn to calculate next chunk's offset
C_CHUNKCLS = 1                          # class for chunk
C_IGNOREIT = 2                          # ignore this chunk

chunks = [(lambda n: n * 16,     mshcls.OVertData, 1), 
          (lambda n: n * 8,      mshcls.OTriData, 1),  
          (lambda n: n * 6,      mshcls.OTriInfoData, 1),
          (lambda n: 0,          mshcls.OHole, 1),
          (lambda n: n * 12,     mshcls.OVertNormalsData, 1),
          (lambda n: n * 12,     mshcls.OTriNormalsData, 1),
          (lambda n: 0,          mshcls.OHole, 1),
          (lambda n: n * 24,     mshcls.OTexData, 1),
          (materialschunksize,   mshcls.OMatData, 1),
          (lambda n: n * 20,     mshcls.OUnk1, 1),
          (skeldatachunksize,    mshcls.OUnk2, 1),
          (lambda n: n,          mshcls.OUnkPathData, 1),
          (lambda n: n * 4,      mshcls.OObjData, 1),
          (lambda n: n * 12,     mshcls.OTriXData, 1)]

lastchunkmsh = lambda n: n * 98,  mshcls.O98Gran, 1
lastchunkamh = skeletonchunksize, mshcls.OSkelData, 1

def dochunk(chunk):
    print '%08X: %s' % (f.tell() + 4,  chunk[C_CHUNKCLS].__doc__),
    if chunk[C_IGNOREIT]:
        n = read_word(f)
        print n
        f.seek(chunk[C_CALCNEXT](n), 1)
    else:
        print
        print '=' * 40
        object = chunk[C_CHUNKCLS](f)
        print object
    
def domesh(name):
    print '*' * 40
    print name

    global f
    f = open(name, 'rb')
    f.seek(0, 2)
    size = f.tell()
    f.seek(0)

    sig = read_word(f)
    if sig != 0x6e71:
        print 'bad primary signature', hex(sig)
        return

    tid = read_word(f)
    if tid == mshcls.TID_STATIC:
        print 'static mesh'
        lastchunk = lastchunkmsh
        part_anchor = '[6 0]?'
    elif tid == mshcls.TID_DYNAMIC:
        print 'hier mesh'
        lastchunk = lastchunkamh
        part_anchor = '[7 4]?'
    else:
        print 'bad type identifier', hex(tid)
        return
        
    print mshcls.str_hex(f.read(0x10))
    print 'size: ', read_word(f), size
    print mshcls.str_hex(f.read(0x10))
    n_parts = read_word(f)
    print 'Contains:', n_parts, 'parts'

    ## Hack? to handle multipart static meshes
    ## (only? hmd*.msh)
    if tid == mshcls.TID_STATIC and n_parts > 1:
       save_chunk = chunks.pop(len(chunks) - 1)
        
    try:
        for i in range(n_parts):
            print '-' * 40
            print 'Part:', i, 
            print part_anchor, read_word(f), read_word(f)

            for chunk in chunks:
                if size - f.tell() < 4:
                    break
                dochunk(chunk)

        dochunk(lastchunk)
        here = f.tell()
        print '%08X: EOF: %08X, Diff: %08X(%d)' % (here, size,
                                                   size - here,
                                                   size - here)
        if size - here:
            print '<Gap between last chunk and EOF>'
        print '\n' * 2
    except:
        print 'something is wrong while processing', name
        traceback.print_exc(file = sys.stdout)
    f.close()

    if tid == mshcls.TID_STATIC and n_parts > 1:
        chunks.append(save_chunk)
        
    return
        
def main(names):
    """%s -h
    """
    for name in names:
        domesh(name)

def show_format():
    for chunk in chunks + [lastchunkmsh, lastchunkamh]:
        print '*' * 40
        print chunk[C_CHUNKCLS].__doc__
        print chunk[C_CHUNKCLS].fmt
        if chunk[C_IGNOREIT]:
            print 'not shown by default'
        print
        
help ="""%s: [options] input0.[msh|amh] input1.[msh|amh] ... inputN.[msh|amh]
options:
\t-h      print this help
\t-f      describe chunks
\t-[0,%d] toggle nth chunk's detailed information
\t-a      show detailed info for all chunks
\t-n      do not show detailed information at all
"""


# this is a mess
if __name__ == '__main__':
    argv = sys.argv[1:]
    if not argv:
        print main.__doc__ % sys.argv[0]
        sys.exit(1)

    # functional style rules or what? ;)
    args  = map(lambda e: e[1:], filter(lambda e: e[0] == '-', argv))
    names = filter(lambda e: e[1:] not in args, argv)
    nums  = filter(lambda e:             
                   reduce(lambda l, r: l and r,
                          map(lambda e: e in string.digits, e)), args)
    
    if 'h' in args or '-help' in args:
        print help % (sys.argv[0], len(chunks))
        sys.exit(0)

    if 'f' in args:
        show_format()
        sys.exit(0)

    if not names:
        sys.stderr.write('nothing to do\n')
        sys.exit(0)
        
    def setchunksto(ignore):
        global lastchunkmsh, lastchunkamh
        for i in range(len(chunks)):
            c = chunks[i]
            # Tuples are immutable
            chunks[i] = (c[0], c[1], ignore)
	c = lastchunkmsh, lastchunkamh
        lastchunkmsh = (c[0][0], c[0][1], ignore)
        lastchunkamh = (c[1][0], c[1][1], ignore)
        
    if 'a' in args:
        setchunksto(0)

    if 'n' in args:
        setchunksto(1)

    for arg in args:
        if arg not in ['f', 'h', '-help', 'a', 'n'] and arg not in nums:
            sys.stderr.write('there is no -%s option\n' % arg)
            
    for e in nums:
        try:
            n = int(e)
            if n < len(chunks):
                c = chunks[n]
                # Tuples are immutable
                chunks[n] = (c[0], c[1], not c[2])
            elif n == len(chunks):
                lastchunkmsh = (lastchunkmsh[0], lastchunkmsh[1],
                                not lastchunkmsh[2])
                lastchunkamh = (lastchunkamh[0], lastchunkamh[1],
                                not lastchunkamh[2])
            else:
                sys.stderr.write('max -x arg is %s\n' % (len(chunks)))
                
        except:
            pass

    main(names)

