'''Dark Souls .flv/.flver model importer. Note that there are little and big endian versions of the game, and they are basically the same format. Therefore, all''' from inc_noesis import * import noesis import rapi def registerNoesisTypes(): '''Register the plugin. Just change the Game name and extension.''' handle = noesis.register("Dark Souls", ".flv;.flver") 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) < 12: return 0 try: bs = NoeBitStream(data) idstring = noeStrFromBytes(bs.readBytes(6)) if idstring == "FLVER": return 1 return 0 except: return 0 def get_endian(data): bs = NoeBitStream(data) bs.seek(6) endian = bs.readByte() if endian == 0x4C: # "L" return 0 elif endian == 0x42: # "B" 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)''' endian = get_endian(data) ctx = rapi.rpgCreateContext() parser = SanaeParser(data, endian) 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 FLVER_mesh(object): def __init__(self): self.numFaceGroups = 0 self.numIndices = [] self.idxOffsets = [] self.idxBuffs = [] #one mesh may have multiple parts self.numVerts = 0 self.vertSize = 0 self.vertOfs = 0 self.vertSectionSize = 0 self.vertBuff = bytes() class SanaeParser(object): def __init__(self, data, endian=0): self.inFile = NoeBitStream(data, endian) self.animList = [] self.texList = [] self.matList = [] self.boneList = [] self.meshList = [] self.dataOfs = 0 #offset to mesh data if endian: rapi.rpgSetOption(noesis.RPGOPT_BIGENDIAN, 1) def build_meshes(self): rapi.rpgSetOption(noesis.RPGOPT_TRIWINDBACKWARD, 1) for mesh in self.meshList: print(mesh.vertSize) if mesh.vertSize == 28: rapi.rpgBindPositionBufferOfs(mesh.vertBuff, noesis.RPGEODATA_FLOAT, 28, 0) elif mesh.vertSize == 32: rapi.rpgBindPositionBufferOfs(mesh.vertBuff, noesis.RPGEODATA_FLOAT, 32, 0) #rapi.rpgBindNormalBufferOfs(mesh.vertBuff, noesis.RPGEODATA_FLOAT, 32, 20) rapi.rpgBindUV1BufferOfs(mesh.vertBuff, noesis.RPGEODATA_HALFFLOAT, 32, 16) elif mesh.vertSize == 36: rapi.rpgBindPositionBufferOfs(mesh.vertBuff, noesis.RPGEODATA_FLOAT, 36, 0) elif mesh.vertSize == 40: rapi.rpgBindPositionBufferOfs(mesh.vertBuff, noesis.RPGEODATA_FLOAT, 40, 0) elif mesh.vertSize == 44: rapi.rpgBindPositionBufferOfs(mesh.vertBuff, noesis.RPGEODATA_FLOAT, 44, 0) #for j in range(mesh.numFaceGroups): # Not sure for j in range(1): numIdx = mesh.numIndices[j] idxBuff = mesh.idxBuffs[j] rapi.rpgSetMaterial("WP_A_1550small.tga") rapi.rpgCommitTriangles(idxBuff, noesis.RPGEODATA_USHORT, numIdx, noesis.RPGEO_TRIANGLE_STRIP, 1) #rapi.rpgCommitTriangles(None, noesis.RPGEODATA_USHORT, mesh.numVerts, noesis.RPGEO_POINTS, 1) def get_indices(self, numIdx): return self.inFile.readBytes(numIdx*2) def parse_faces(self): for mesh in self.meshList: for i in range(mesh.numFaceGroups): numIdx = mesh.numIndices[i] idxOfs = mesh.idxOffsets[i] self.inFile.seek(idxOfs) idxBuff = self.get_indices(numIdx) mesh.idxBuffs.append(idxBuff) def parse_vertices(self): for mesh in self.meshList: self.inFile.seek(mesh.vertOfs) print(mesh.vertOfs, mesh.vertSectionSize) mesh.vertBuff = self.inFile.readBytes(mesh.vertSectionSize) def parse_materials(self, numMat): for i in range(numMat): self.inFile.read('8L') def parse_bones(self, numBones): for i in range(numBones): self.inFile.seek(64, 1) def parse_unk1(self, count): for i in range(count): self.inFile.seek(128, 1) def parse_part_info(self, numParts): for mesh in self.meshList: self.inFile.read('8L') numFaceGroups = self.inFile.readUInt() self.inFile.read('3L') mesh.numFaceGroups = numFaceGroups def parse_face_info(self): for mesh in self.meshList: for i in range(mesh.numFaceGroups): groupNum = self.inFile.readUInt() self.inFile.readUInt() numIdx = self.inFile.readUInt() idxOfs = self.inFile.readUInt() + self.dataOfs idxSize = self.inFile.readUInt() self.inFile.read('3L') mesh.numIndices.append(numIdx) mesh.idxOffsets.append(idxOfs) def parse_vertex_info(self): for mesh in self.meshList: self.inFile.read('2L') vertSize = self.inFile.readUInt() numVerts = self.inFile.readUInt() self.inFile.readUInt() unk = self.inFile.readUInt() sectionSize = self.inFile.readUInt() vertOfs = self.inFile.readUInt() + self.dataOfs mesh.numVerts = numVerts mesh.vertSize = vertSize mesh.vertOfs = vertOfs mesh.vertSectionSize = sectionSize def parse_file(self): '''Main parser method''' #header idstring = self.inFile.readBytes(6) #version? unk, type1, type2 = self.inFile.read('3H') self.dataOfs = self.inFile.readUInt() dataSize = self.inFile.readUInt() numBones = self.inFile.readUInt() numMat = self.inFile.readUInt() count = self.inFile.readUInt() numParts = self.inFile.readUInt() numMesh = self.inFile.readUInt() #create some mesh objects for i in range(numMesh): mesh = FLVER_mesh() self.meshList.append(mesh) self.inFile.read('6f') self.inFile.seek(64, 1) self.parse_bones(numBones) self.parse_materials(numMat) self.parse_unk1(count) self.parse_part_info(numParts) self.parse_face_info() self.parse_vertex_info() #parse data self.parse_faces() self.parse_vertices() self.build_meshes()