Newer
Older
Import / projects / Gameloft / bne_lib / tools / GameloftTools / rk.py
import os
import sys
import collections
import struct
import traceback

assert sys.version_info >= (2, 7), 'Python with version less than 2.7 is not supported, your version is: %s' % sys.version

# RK model parser

_chunkTypesNames = [
  'RKCHUNK_TYPE_UNUSED',
  'RKCHUNK_TYPE_MATERIAL_MESH_ARRAY',
  'RKCHUNK_TYPE_MATERIAL_ARRAY',
  'RKCHUNK_TYPE_VERTEX_ARRAY',
  'RKCHUNK_TYPE_INDEX_ARRAY',
  'RKCHUNK_TYPE_REFPOINT_ARRAY',
  'RKCHUNK_TYPE_VOLUME_ARRAY',
  'RKCHUNK_TYPE_BONE_ARRAY',
  'RKCHUNK_TYPE_TRIANGLE_COLLISION_VERTEX_ARRAY',
  'RKCHUNK_TYPE_TRIANGLE_COLLISION_INDEX_ARRAY',
  'RKCHUNK_TYPE_TRIANGLE_COLLISION_MESH_ARRAY',
  'RKCHUNK_TYPE_CONVEX_COLLISION_VERTEX_ARRAY',
  'RKCHUNK_TYPE_CONVEX_COLLISION_MESH_ARRAY',
  'RKCHUNK_TYPE_VERTEX_ELEMENTS',
  'RKCHUNK_TYPE_COOKED_TRIANGLE_MESH_ARRAY',
  'RKCHUNK_TYPE_COOKED_TRIANGLE_DATA_ARRAY',
  'RKCHUNK_TYPE_SUB_OBJECT_NAME',
  'RKCHUNK_TYPE_VERTEX_SKIN_ARRAY',
  'RKCHUNK_TYPE_SPLINE_KNOT_ARRAY',
  'RKCHUNK_TYPE_SPLINE_ARRAY',
  'RKCHUNK_TYPE_MODEL_PIVOT',
  'RKCHUNK_TYPE_PHYSICSSHAPE_ARRAY',
  'RKCHUNK_TYPE_PHYSICSVOLUME_ARRAY',
  'RKCHUNK_TYPE_ANIM_BONE_MAP_ARRAY',
]

def _readRKString(f, size=64):
  data = f.read(size)
  for idx in range(len(data)):
    if data[idx] == '\0':
      return str(data[:idx])
  return str(data)
  
def parseANIM(filePath):
  params = collections.OrderedDict()
  params['type'] = 'animation'
  params['filePath'] = filePath
  with open(filePath, 'rb') as f:
    params['id'] = _readRKString(f, 8)
    versionMajor, versionMinor = struct.unpack('II', f.read(8))    
    params['versionMajor'] = versionMajor
    params['versionMinor'] = versionMinor
    params['name'] = _readRKString(f)
    
    bonesCount, keyFramesCount, compressionType, frameRate = struct.unpack('IIII', f.read(16))
    params['bonesCount'] = bonesCount
    params['keyFramesCount'] = keyFramesCount
    params['compression'] = compressionType
    params['frameRate'] = frameRate
  return params
    
def parseRK(filePath):
  params = collections.OrderedDict()
  params['type'] = 'model'
  params['filePath'] = filePath
  with open(filePath, 'rb') as f:
    params['id'] = _readRKString(f, 8)
    versionMajor, versionMinor = struct.unpack('II', f.read(8))    
    params['versionMajor'] = versionMajor
    params['versionMinor'] = versionMinor
    params['name'] = _readRKString(f)    

    chunks = {}
    try:
      if params['versionMajor'] >= 2:
        for i in range(24):
          chunkType, chunkOffset, chunkElementsCount, chunkSize = struct.unpack('IIII', f.read(16))
          if chunkType != 0:
            if chunkType >= len(_chunkTypesNames): 
              chunkTypeName = 'RKCHUNK_TYPE_MODEL_UNKNOWN_%i' % chunkType
            else:
              chunkTypeName = _chunkTypesNames[chunkType]
            assert not chunkTypeName in params
            chunks[chunkTypeName] = chunkElementsCount        
    except Exception, e:
      print 'Error parsing file chunks: %s' % filePath
      traceback.print_exc()

  params['clipsCount'] = 0
  animClipsFile = os.path.splitext(filePath)[0] + '.csv'
  if os.path.exists(animClipsFile):
    with open(animClipsFile) as f:
      lines = f.readlines()
      lines = filter(lambda x : bool(x.strip()), lines)
      params['clipsCount'] = len(lines)

  params['keyFramesCount'] = 0
  params['animRawSize'] = 0
  animFile = os.path.splitext(filePath)[0] + '.anim'
  if os.path.exists(animFile):
    params['animRawSize'] = os.path.getsize(animFile)
    with open(animFile, 'rb') as f:
      animName = _readRKString(f)
      bonesCount, keyFramesCount = struct.unpack('II', f.read(8))
      params['keyFramesCount'] = keyFramesCount

  params['meshesCount'] = 0
  if 'RKCHUNK_TYPE_MATERIAL_MESH_ARRAY' in chunks:
    params['meshesCount'] = chunks['RKCHUNK_TYPE_MATERIAL_MESH_ARRAY']

  params['bonesCount'] = 0
  if 'RKCHUNK_TYPE_BONE_ARRAY' in chunks:
    params['bonesCount'] = chunks['RKCHUNK_TYPE_BONE_ARRAY']

  params['materialsCount'] = 0
  if 'RKCHUNK_TYPE_MATERIAL_ARRAY' in chunks:
    params['materialsCount'] = chunks['RKCHUNK_TYPE_MATERIAL_ARRAY']

  params.update(chunks)

  return params
  
def parse(filePath):
  if filePath.endswith('.anim'):
    return parseANIM(filePath)
  else:
    return parseRK(filePath)

def getVersion(filePath):
  params = parse(filePath)
  return params['versionMajor'], params['versionMinor']

if __name__ == '__main__':
  if len(sys.argv) != 2:
    print 'Usage: [python.exe] rk.py <filename.rk>'
    sys.exit(1)

  rk = parse(sys.argv[1])
  for key, value in rk.items():
    print '%s = %s' % (key, value)