'''Noesis import plugin. Written by Tsukihime''' from inc_noesis import * import noesis import rapi import operator from functools import reduce def registerNoesisTypes(): '''Register the plugin. Just change the Game name and extension.''' handle = noesis.register("Unity Engine", ".43") noesis.setHandlerTypeCheck(handle, noepyCheckType) noesis.setHandlerLoadModel(handle, noepyLoadModel) return 1 def noepyCheckType(data): '''Verify that the format is supported by this plugin. Default yes''' return 1 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)''' ctx = rapi.rpgCreateContext() parser = SanaeParser(data) parser.parse_file() mdl = rapi.rpgConstructModel() mdl.setModelMaterials(NoeModelMaterials(parser.texList, parser.matList)) mdl.setBones(parser.boneList) mdl.setAnims(parser.animList) mdlList.append(mdl) return 1 class Mesh(object): def __init__(self): self.name = "" self.idxStart = 0 self.vertStart = 0 self.numIdx = 0 self.numVerts = 0 self.vertSize = 0 self.idxBuff = bytes() self.vertBuff = bytes() class SanaeParser(object): def __init__(self, data): '''Initialize some data. Refer to Sanae.py to see what is already initialized''' self.version = 0 self.meshType = 0 self.inFile = NoeBitStream(data) self.animList = [] self.texList = [] self.matList = [] self.boneList = [] self.meshList = [] def build_meshes(self): idxStart = 0 for mesh in self.meshList: idxEnd = idxStart + mesh.numIdx * 2 idxBuff = self.idxBuff[idxStart : idxEnd] idxStart = idxEnd rapi.rpgSetMaterial(rapi.getExtensionlessName(rapi.getInputName())) if self.version == 4: if self.meshType == 2: rapi.rpgCommitTriangles(idxBuff, noesis.RPGEODATA_USHORT, mesh.numIdx, noesis.RPGEO_TRIANGLE_STRIP, 1) else: rapi.rpgCommitTriangles(idxBuff, noesis.RPGEODATA_USHORT, mesh.numIdx, noesis.RPGEO_TRIANGLE, 1) elif self.version == 5 : rapi.rpgCommitTriangles(idxBuff, noesis.RPGEODATA_USHORT, mesh.numIdx, noesis.RPGEO_TRIANGLE_STRIP, 1) def read_name(self): #string = self.inFile.readBytes(n) return noeStrFromBytes(string) def parse_mesh_info(self, count): for i in range(count): mesh = Mesh() if self.version == 4: mesh.idxStart = self.inFile.readInt() mesh.numIdx = self.inFile.readInt() self.meshType = self.inFile.readInt() if self.meshType == 2: self.inFile.readInt() mesh.vertStart = self.inFile.readInt() mesh.numVerts = self.inFile.readInt() elif self.version == 5: mesh.idxStart, mesh.numIdx, self.meshType, mesh.vertStart, mesh.numVerts, unk = self.inFile.read("6L") self.inFile.read("6f") self.meshList.append(mesh) def parse_faces(self): size = self.inFile.readInt() idxBuff = self.inFile.readBytes(size) pos = self.inFile.tell() if pos % 4 != 0: self.inFile.seek((4 - (pos %4)) % 4, 1) return idxBuff def parse_unk1(self): count = self.inFile.readInt() for i in range(count): self.inFile.readBytes(64) def parse_unk2(self): count = self.inFile.readInt() self.inFile.read("%dL" %count) def parse_unk3(self): count = self.inFile.readUInt() if count == 0: pass else: self.inFile.seek(count * 32, 1) def parse_unk4(self): count = self.inFile.readUInt() if count == 0: pass else: self.inFile.seek(count * 64, 1) def parse_mesh4_ver0(self): print("Mesh Version 0") ## Strange part faceType = self.inFile.readShort() flag1 = self.inFile.readByte() flag10 = self.inFile.readByte() self.inFile.readInt() if faceType != 0: print("Unknown face type ", faceType) return if flag1 == 0: flag11 = self.inFile.readByte() flag12 = self.inFile.readByte() flag13 = self.inFile.readByte() flag14 = self.inFile.readByte() self.inFile.readInt() if flag13 == 0: self.parse_unk1() self.parse_unk2() self.inFile.readFloat() flag21, flag22, flag23, flag24 = self.inFile.read("4B") if flag21 != 0: print("Unknown flag 21: ", flag21) return if flag22 == 0: self.inFile.readInt() # Finally, we move to face indices self.idxBuff = self.parse_faces() self.parse_unk3() if flag1 == 0: if flag13 == 1: count = self.inFile.readInt() self.inFile.readBytes(count * 64) elif flag1 == 1: count = self.inFile.readInt() self.inFile.readBytes(count * 64) ## Vertex area self.inFile.readUInt() numVerts = self.inFile.readUInt() unk = self.inFile.readUInt() # 6, 8 if unk == 6: self.inFile.read("9L") vertSize = self.inFile.readInt() self.inFile.read("9L") elif unk == 8: vertSize = 24 self.inFile.read("3L") self.inFile.readByte() vt1 = self.inFile.readByte() if vt1 == 24: vertSize = 32 self.inFile.readByte() self.inFile.readByte() self.inFile.read("3L") self.inFile.readByte() vt2 = self.inFile.readByte() self.inFile.readByte() self.inFile.readByte() if vt2 != 0: vertSize = vt2 + 16 size = self.inFile.readInt() vertBuffSize = vertSize * numVerts uvBuffSize = 8 * numVerts extraSize = size - vertBuffSize - uvBuffSize vertBuff = self.inFile.readBytes(vertBuffSize) # Extra stuff? self.inFile.readBytes(extraSize) uvBuff = self.inFile.readBytes(uvBuffSize) rapi.rpgBindUV1Buffer(uvBuff, noesis.RPGEODATA_FLOAT, 8) print("Vert size: ", vertSize) if vertSize == 20: rapi.rpgBindPositionBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 20, 0) #rapi.rpgBindNormalBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 20, 12) elif vertSize == 24: rapi.rpgBindPositionBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 24, 0) rapi.rpgBindNormalBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 24, 12) elif vertSize == 32: rapi.rpgBindPositionBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 32, 0) rapi.rpgBindNormalBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 32, 12) elif vertSize == 36: rapi.rpgBindPositionBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 36, 0) rapi.rpgBindNormalBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 36, 12) elif vertSize == 40: rapi.rpgBindPositionBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 40, 0) rapi.rpgBindNormalBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 40, 12) elif vertSize == 48: rapi.rpgBindPositionBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 48, 0) rapi.rpgBindNormalBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 48, 12) elif vertSize == 52: rapi.rpgBindPositionBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 52, 0) rapi.rpgBindNormalBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 52, 12) elif vertSize == 56: rapi.rpgBindPositionBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 56, 0) rapi.rpgBindNormalBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 56, 12) elif vertSize == 60: rapi.rpgBindPositionBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 60, 0) rapi.rpgBindNormalBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 60, 12) elif vertSize == 64: rapi.rpgBindPositionBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 64, 0) rapi.rpgBindNormalBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 64, 12) else: print("Unknown vert size: %s" %vertSize) self.build_meshes() def parse_mesh4_ver2(self): print("Mesh Version 2") self.inFile.readInt() # face indices self.idxBuff = self.parse_faces() self.parse_unk3() self.parse_unk4() # Vertices self.inFile.readInt() numVerts = self.inFile.readInt() self.inFile.readInt() self.inFile.readInt() vertSize = self.inFile.readInt() self.inFile.read("13L") size = self.inFile.readInt() vertBuff = self.inFile.readBytes(numVerts * vertSize) print("Vert size: ", vertSize) if vertSize == 32: rapi.rpgBindPositionBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 32, 0) rapi.rpgBindNormalBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 32, 12) rapi.rpgBindUV1BufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 32, 24) elif vertSize == 40: rapi.rpgBindPositionBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 40, 0) rapi.rpgBindNormalBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 40, 12) rapi.rpgBindUV1BufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 40, 24) elif vertSize == 48: rapi.rpgBindPositionBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 48, 0) rapi.rpgBindNormalBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 48, 12) rapi.rpgBindUV1BufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 48, 24) elif vertSize == 56: rapi.rpgBindPositionBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 56, 0) rapi.rpgBindNormalBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 56, 12) rapi.rpgBindUV1BufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 56, 24) elif vertSize == 60: rapi.rpgBindPositionBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 60, 0) rapi.rpgBindNormalBufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 60, 12) rapi.rpgBindUV1BufferOfs(vertBuff, noesis.RPGEODATA_FLOAT, 60, 24) else: print("Unknown vert size: ", vertSize) self.build_meshes() def parse_ver4(self): # Seek to byte 44 from the end self.inFile.seek(self.inFile.dataSize-44, 0) type = self.inFile.readInt() if type == 11: print("Unknown model type with no face indices") return self.inFile.seek(0) print("Version 4") numMesh = self.inFile.readInt() self.parse_mesh_info(numMesh) if self.meshType == 0: self.parse_mesh4_ver0() elif self.meshType == 2: self.parse_mesh4_ver2() else: print("Unknown mesh type: ", self.meshType) def parse_ver5(self): '''Parser for version 5 models''' print("Version 5") self.inFile.readInt() numMesh = self.inFile.readInt() print ("Num Mesh: ", numMesh) self.parse_mesh_info(numMesh) self.inFile.readInt() self.idxBuff = self.parse_faces() numPos = self.inFile.readInt() vertBuff = self.inFile.readBytes(numPos * 12) # ?? Unknown buffer count = self.inFile.readInt() self.inFile.readBytes(count * 32) count = self.inFile.readInt() self.inFile.readBytes(count * 64) print(self.inFile.tell()) numUV = self.inFile.readInt() uvBuff = self.inFile.readBytes(numUV * 8) print("UV end", self.inFile.tell()) count = self.inFile.readInt() buf = self.inFile.readBytes(count * 8) # ?? count = self.inFile.readInt() buf = self.inFile.readBytes(count * 16) # ?? print(self.inFile.tell()) numNorms = self.inFile.readInt() normBuff = self.inFile.readBytes(count * 12) rapi.rpgBindPositionBuffer(vertBuff, noesis.RPGEODATA_FLOAT, 12) rapi.rpgBindNormalBuffer(normBuff, noesis.RPGEODATA_FLOAT, 12) rapi.rpgBindUV1Buffer(uvBuff, noesis.RPGEODATA_FLOAT, 8) self.build_meshes() def parse_file(self): '''Main parser method''' type = self.inFile.readInt() type2 = self.inFile.readInt() self.inFile.seek(0) # In version 4, the first int is the number of meshes # and the second int is the start of the indices for # the given mesh. This should always be 0 # In version 5, the first int is always a 1, and the # second int is the number of meshes if type2 == 0: self.version = 4 self.parse_ver4() else: self.version = 5 self.parse_ver5()