'''Noesis import plugin. Written by Tsukihime''' from inc_noesis import * import noesis import rapi import math def registerNoesisTypes(): '''Register the plugin. Just change the Game name and extension.''' handle = noesis.register("Cabal Online", ".ebm;.ech") noesis.setHandlerTypeCheck(handle, noepyCheckType) noesis.setHandlerLoadModel(handle, noepyLoadModel) return 1 def noepyCheckType(data): '''Verify that the format is supported by this plugin. Default yes''' if len(data) < 4: return 0 try: bs = NoeBitStream(data) idstring = bs.readUInt() if idstring not in [0x0003ED03, 0x0003EE03, 0x0003EF03, 0x0003F003]: return 0 return 1 except: return 0 def noepyLoadModel(data, mdlList): '''Build the model, set materials, bones, and animations. You do not need all of them as long as they are empty lists (they are by default)''' parser = SanaeParser(data) parser.parse_file() ctx = rapi.rpgCreateContext() for i in range(len(parser.vertBuffs)): vertBuff, numVerts = parser.vertBuffs[i] idxBuff, numIdx, matNum = parser.idxBuffs[i] matList = [parser.matList[matNum]] texList = [parser.texList[matNum]] matName = matList[0].name rapi.rpgReset() rapi.rpgBindPositionBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 32, 0) rapi.rpgBindNormalBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 32, 12) rapi.rpgBindUV1BufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 32, 24) rapi.rpgSetMaterial(matName) rapi.rpgCommitTriangles(idxBuff, noesis.RPGEODATA_USHORT, numIdx, noesis.RPGEO_TRIANGLE, 1) mdl = rapi.rpgConstructModel() mdl.setModelMaterials(NoeModelMaterials(texList, matList)) mdl.setBones(parser.boneList) mdl.setAnims(parser.animList) mdlList.append(mdl) return 1 class SanaeParser(object): def __init__(self, data): '''Initialize some data. Refer to Sanae.py to see what is already initialized''' self.inFile = NoeBitStream(data) self.vertBuffs = [] self.idxBuffs = [] self.animList = [] self.texList = [] self.matList = [] self.boneList = [] self.numBones = 0 def read_name(self): string = self.inFile.readBytes(self.inFile.readUShort()) try: return noeStrFromBytes(string) except: return "mesh" def parse_texture(self): texName = self.read_name() texSize = self.inFile.readUInt() texData = self.inFile.readBytes(texSize) self.inFile.seek(26, 1) tex = rapi.loadTexByHandler(texData, ".dds") if tex is not None: tex.name = texName self.texList.append(tex) return texName def parse_materials(self, numMat): for i in range(numMat): diffuse = self.inFile.read('4f') ambient = self.inFile.read('4f') specular = self.inFile.read('4f') emissive = self.inFile.read('4f') power = self.inFile.readFloat() matName = "material[%d]" %len(self.matList) texName = self.parse_texture() material = NoeMaterial(matName, texName) material.setDefaultBlend(0) self.matList.append(material) def parse_animations(self, numAnims): for i in range(numAnims): name = self.read_name() numBones = self.inFile.readUShort() for j in range(numBones): boneName = self.read_name() numTranslate = self.inFile.readUInt() for k in range(numTranslate): keyFrameSec = self.inFile.readFloat() x, y, z = self.inFile.read('3f') keyFrameIndex = int(math.floor(keyFrameSec*100)) numRotate = self.inFile.readUInt() for k in range(numRotate): keyFrameSec = self.inFile.readFloat() x, y, z, w = self.inFile.read('4f') keyFrameIndex = int(math.floor(keyFrameSec*100)) def parse_bones(self, numBones): for i in range(numBones): boneName = self.read_name() parentIndex = self.inFile.readUInt() boneMatrix = self.inFile.read('16f') parentMatrix = self.inFile.read('16f') def parse_vertices(self, numVerts): return self.inFile.readBytes(32*numVerts) def parse_faces(self, numIdx): return self.inFile.readBytes(numIdx*2) def parse_weights(self): numWeights = self.inFile.readUShort() for i in range(numWeights): for j in range(self.numBones): count = self.inFile.readUInt() boneIDs = self.inFile.read('%dL' %count) weights = self.inFile.read('%df' %count) def parse_meshes(self, numMesh): print(numMesh, "meshes") for i in range(numMesh): meshName = self.read_name() worldMatrix = self.inFile.read('16f') localMatrix = self.inFile.read('16f') self.inFile.readInt() matNum = self.inFile.readByte() numVerts = self.inFile.readUShort() numIdx = self.inFile.readUShort() * 3 vertBuff = self.parse_vertices(numVerts) idxBuff = self.parse_faces(numIdx) chunk = self.inFile.readUInt() if chunk == 0x41470205: self.parse_weights() else: self.inFile.seek(-4, 1) self.vertBuffs.append([vertBuff, numVerts]) self.idxBuffs.append([idxBuff, numIdx, matNum]) def parse_file(self): '''Main parser method''' idstring = self.inFile.readUInt() self.inFile.readUShort() self.inFile.readUInt() bbMin = self.inFile.read('3f') bbMax = self.inFile.read('3f') self.inFile.readUInt() while self.inFile.tell() != self.inFile.dataSize: chunk = self.inFile.readUInt() if chunk in [0x41470201]: numMat = self.inFile.readUShort() self.parse_materials(numMat) elif chunk == 0x41470202: numMesh = self.inFile.readUShort() self.parse_meshes(numMesh) elif chunk == 0x41470203: numBones = self.inFile.readUShort() self.numBones = numBones self.parse_bones(numBones) elif chunk == 0x41470204: numAnims = self.inFile.readUShort() self.parse_animations(numAnims)