#Script by chrrox

from inc_noesis import *

def registerNoesisTypes():
	handle = noesis.register("Knights Contract", ".hxt")
	noesis.setHandlerTypeCheck(handle, kcCheckType)
	noesis.setHandlerLoadModel(handle, kcLoadModel)
	#noesis.logPopup()
	return 1

def kcCheckType(data):
	td = NoeBitStream(data)
	return 1


def dataAlign(pos,pad):
	if (pos % pad) > 0:
		return((pad - (pos) % pad))
	else:
		return(0)

class hxtFile: 
         
	def __init__(self, bs):
		self.bs = bs
		self.texList   = []
		self.vtxList   = []
		self.matList   = []
		self.boneList  = []

	def loadAll(self, bs):
		bs.seek(0x80, NOESEEK_ABS)
		#Texture
		unk00,unk01,unk02,texCount,unk03 = bs.read(">5i")
		#print(unk00,unk01,unk02,texCount,unk03)
		bs.seek(0xA4, NOESEEK_ABS)
		folderBaseNameSize =  bs.read(">i")[0]
		folderBaseName = bs.readBytes(folderBaseNameSize).decode("ASCII").rstrip("\0")
		#print(folderBaseName)
		bs.seek(0x6C, NOESEEK_REL)
		baseName = rapi.getExtensionlessName(rapi.getLocalFileName(rapi.getLastCheckedName()))
		txbFile = None
		if( rapi.checkFileExists( rapi.getDirForFilePath( rapi.getLastCheckedName() ) + baseName + ".txb" ) ):
			txbFile = rapi.loadIntoByteArray( rapi.getDirForFilePath( rapi.getLastCheckedName() ) + baseName + ".txb" )
		for a in range(0,texCount):
			texNameSize =  bs.read(">i")[0]
			texName = bs.readBytes(texNameSize).decode("ASCII").rstrip("\0")
			#print(texName)
			bs.seek(0x2A, NOESEEK_REL)
			texType, texUnk = bs.read(">2b")
			texPreLoadSize =  bs.read(">i")[0]
			texPreLoadData = bs.readBytes(texPreLoadSize)
			tunk00,tunk01,tunk02 = bs.read(">3i")
			width,height,one,mips,tunk06,picsize = bs.read(">6i")
			texLod01 = bs.read(">6i")
			texLod02 = bs.read(">6i")
			texLod03 = bs.read(">6i")
			offset,totalsize,size,zsize = bs.read(">4i")
			texLodInfo01 = bs.read(">4i")
			texLodInfo02 = bs.read(">4i")
			tunk03,tunk04,tunk05 = bs.read(">3i")
			#bs.seek(0xA8, NOESEEK_REL)
			if txbFile:
				texData = b''
				#print("YES")
				#print(width,height,one,mips,tunk06,picsize)
				#print(offset,totalsize,size,zsize)
				#print(texType)
				cmpData = txbFile[offset:offset + zsize]
				if size == 0:
					size = totalsize
					texData += rapi.decompInflate(cmpData, size, -15)
				else:
					texData += rapi.decompInflate(cmpData, size, -15)
					while totalsize != 0:
						#print(offset,zsize,totalsize)
						totalsize -= size
						if totalsize > 0:
							offset += zsize
							size,zsize = noeUnpack(">ii", txbFile[offset:offset + 8])
							offset += 8
							cmpData = txbFile[offset:offset + zsize]
							texData += rapi.decompInflate(cmpData, size, -15)
				
				#print(len(texData))
				texFmt = 0
				if texType == 71:
					texFmt = noesis.NOESISTEX_DXT1
				#DXT5
				elif texType == 78:
					texFmt = noesis.NOESISTEX_DXT5

				tex1 = NoeTexture(rapi.getLocalFileName(texName), width, height, texData, texFmt)
				self.texList.append(tex1)
		#print(bs.tell())
		#ddm
		ddmBase = (bs.tell() + 4)
		bs.seek(0xAC, NOESEEK_REL)
		bonetype ,boneCount = bs.read(">ii")
		#print(hex(bonetype) ,hex(boneCount))
		boneMap = {}
		for a in range(0,boneCount):
			bid = bs.read("B")[0]
			boneMap[bid] = a
		boneParrent = bs.readBytes(boneCount * 1)
		#print(bs.tell())
		bs.seek(dataAlign((bs.tell() - ddmBase) , 8), NOESEEK_REL)
		if bonetype == 0x13:
			#boneData2 = bs.readBytes(16 * boneCount)
			for a in range(0,boneCount):
				px,py,pz,pw = bs.read(">4f")
				boneMtx = NoeQuat([0,0,0,1]).toMat43()
				boneMtx[3] = [px,py,pz]
				newBone = NoeBone(a, str(a), boneMtx, None, -1)
				self.boneList.append(newBone)
			#print(bs.tell())
			boneData4 = bs.readBytes(4 * boneCount)
			#print(bs.tell())
			boneCount2 = bs.read(">i")[0]
			boneData5 = bs.readBytes(48 * boneCount2)
			#print(bs.tell())
		elif bonetype == 0x17:
			#boneData2 = bs.readBytes(16 * boneCount)
			for a in range(0,boneCount):
				px,py,pz,pw = bs.read(">4f")
				boneMtx = NoeQuat([0,0,0,1]).toMat43()
				boneMtx[3] = [px,py,pz]
				newBone = NoeBone(a, str(a), boneMtx, None, -1)
				self.boneList.append(newBone)
			#print(bs.tell())
			boneData3 = bs.readBytes(16 * boneCount)
			#print(bs.tell())
			boneData4 = bs.readBytes(4 * boneCount)
			#print(bs.tell())
			boneCount2 = bs.read(">i")[0]
			boneData5 = bs.readBytes(48 * boneCount2)
			#print(bs.tell())
		elif bonetype == 0x1B:
			#boneData2 = bs.readBytes(16 * boneCount)
			for a in range(0,boneCount):
				px,py,pz,pw = bs.read(">4f")
				boneMtx = NoeQuat([0,0,0,1]).toMat43()
				boneMtx[3] = [px,py,pz]
				newBone = NoeBone(a, str(a), boneMtx, None, -1)
				self.boneList.append(newBone)
			#print(bs.tell())
			boneData3 = bs.readBytes(16 * boneCount)
			#print(bs.tell())
			boneData4 = bs.readBytes(4 * boneCount)
			#print(bs.tell())
			boneCount2 = bs.read(">i")[0]
			boneData5 = bs.readBytes(48 * boneCount2)
			#print(bs.tell())
		elif bonetype == 0x1F:
			#boneData2 = bs.readBytes(16 * boneCount)
			for a in range(0,boneCount):
				px,py,pz,pw = bs.read(">4f")
				boneMtx = NoeQuat([0,0,0,1]).toMat43()
				boneMtx[3] = [px,py,pz]
				newBone = NoeBone(a, str(a), boneMtx, None, -1)
				self.boneList.append(newBone)
			#print(bs.tell())
			boneData3 = bs.readBytes(16 * boneCount)
			#print(bs.tell())
			boneData4 = bs.readBytes(16 * boneCount)
			#print(bs.tell())
			boneData4 = bs.readBytes(4 * boneCount)
			#print(bs.tell())
			boneCount2 = bs.read(">i")[0]
			boneData5 = bs.readBytes(48 * boneCount2)
			#print(bs.tell())
		else:
			return 0
		unk10,unkCount,unk12 = bs.read(">3i")
		for a in range(0,unkCount):
			unkDataSize =  bs.read(">i")[0]
			unkData = bs.readBytes(unkDataSize)
		#print(bs.tell())
		#materials
		matCount = bs.read(">i")[0]
		for a in range(0,matCount):
			matNameSize =  bs.read(">i")[0]
			matName = bs.readBytes(matNameSize).decode("ASCII").rstrip("\0")
			material = NoeMaterial(matName, "")
			emissiveMaterial = NoeMaterial(matName + "_emissive", "")
			emissiveMaterial.setBlendMode("GL_SRC_ALPHA","GL_ONE")
			#material.setFlags(noesis.NMATFLAG_TWOSIDED, 1)
			#material.setDefaultBlend(0)
			#print(matName)
			for b in range(0,4):
				matTexNameSize =  bs.read(">i")[0]
				matTexName = bs.readBytes(matTexNameSize).decode("ASCII").rstrip("\0")
				bs.seek(0x20, NOESEEK_REL)
				#print(matTexName)
				if b == 0:
					material.setTexture(matTexName)
				elif b == 1:
					time = 1.0
					pulseRate = 0.005
					emissiveMaterial.setTexture(matTexName)
					emissiveMaterial.setDiffuseColor([1, 1, 1, 1])
					emissiveMaterial.setExpr_diffuse_r("0.75 + sin(time * %f) * 0.25"%pulseRate)
					emissiveMaterial.setExpr_diffuse_g("0.75 + sin(time * %f) * 0.25"%pulseRate)
					emissiveMaterial.setExpr_diffuse_b("0.75 + sin(time * %f) * 0.25"%pulseRate)
					if matTexName != "":
						for c in range(0,len(self.texList)):
							if self.texList[c].name == matTexName:
								if len(self.texList[c].pixelData) > 56:
									material.setNextPass(emissiveMaterial)
				elif b == 2:
					material.setNormalTexture(matTexName)
					emissiveMaterial.setNormalTexture(matTexName)
				elif b == 3:
					material.setSpecularTexture(matTexName)
			self.matList.append(material)
			bs.seek(0xA8, NOESEEK_REL)
		#print(bs.tell())
		#mesh
		meshCount = bs.read(">i")[0]
		for a in range(0,meshCount):

			#print(str(a))
			#rapi.rpgSetMaterial(rsiString.split(':')[1])
			subMeshCount,meshType,vtxCount,bidCount,faceCount = bs.read(">5i")
			faceBuff = bs.readBytes(2 * faceCount)
			#print(subMeshCount,meshType,vtxCount,bidCount,faceCount)
			if meshType == 8:
				vtxStride = 0x2C
				vertBuff = bs.readBytes(vtxStride * vtxCount)
			elif meshType == 7:
				vtxStride = 0x28
				vertBuff = bs.readBytes(vtxStride * vtxCount)
			elif meshType == 3:
				vtxStride = 0x30
				vertBuff = bs.readBytes(vtxStride * vtxCount)
			else:
				return 0
			print("Mesh " + str(bs.tell()))
			usedBones = bs.read(">" + bidCount * "i")
			print(usedBones)
			bMap = []
			for b in range(0,bidCount):
				bMap.append(boneMap[usedBones[b]])
			print(bMap)
			meshInfo2 = ()
			for b in range(0,subMeshCount):
				print(str(b))
				meshInfo1 = bs.read(">" + 14 * "i")
				subUsedBonesCount =  bs.read(">i")[0]
				meshInfo2 += bs.read(">" + subUsedBonesCount * "i")
				meshInfo3 = bs.read(">" + 11 * "f")
				rapi.rpgSetName(self.matList[meshInfo1[4]].name)
				rapi.rpgSetMaterial(self.matList[meshInfo1[4]].name)
				rapi.rpgSetBoneMap(bMap)
				#print(meshInfo1)
				print(meshInfo2)
				#print(meshInfo3)
				#print("meshType ",str(meshType))
				posBuff = (vertBuff[(meshInfo1[2] * vtxStride):(meshInfo1[2] * vtxStride) + (meshInfo1[3] * vtxStride)])
				idxBuff = (faceBuff[(meshInfo1[12] * 2):(meshInfo1[12] * 2) + (meshInfo1[13] * 2)])
				if meshType == 8:
					rapi.rpgBindPositionBufferOfs(posBuff, noesis.RPGEODATA_FLOAT, vtxStride, 0)
					decodedNormals = rapi.decodeNormals32(posBuff[12:], vtxStride, -11, -11, -10, NOE_BIGENDIAN)
					decodedNormals = rapi.swapEndianArray(decodedNormals, 4)
					rapi.rpgBindNormalBuffer(decodedNormals, noesis.RPGEODATA_FLOAT, 12)
					rapi.rpgBindColorBufferOfs(posBuff, noesis.RPGEODATA_UBYTE, vtxStride, 0x18, 4)
					rapi.rpgBindUV1BufferOfs(posBuff, noesis.RPGEODATA_HALFFLOAT, vtxStride, 0x1C)
					#rapi.rpgBindUV2BufferOfs(posBuff, noesis.RPGEODATA_HALFFLOAT, vtxStride, 0x20)
					rapi.rpgBindBoneIndexBufferOfs(posBuff, noesis.RPGEODATA_UBYTE, vtxStride, 0x24, 4)
					rapi.rpgBindBoneWeightBufferOfs(posBuff, noesis.RPGEODATA_UBYTE, vtxStride, 0x28, 4)
				elif meshType == 7:
					rapi.rpgBindPositionBufferOfs(posBuff, noesis.RPGEODATA_FLOAT, vtxStride, 0)
					decodedNormals = rapi.decodeNormals32(posBuff[12:], vtxStride, -11, -11, -10, NOE_BIGENDIAN)
					decodedNormals = rapi.swapEndianArray(decodedNormals, 4)
					rapi.rpgBindNormalBuffer(decodedNormals, noesis.RPGEODATA_FLOAT, 12)
					rapi.rpgBindColorBufferOfs(posBuff, noesis.RPGEODATA_UBYTE, vtxStride, 0x18, 4)
					rapi.rpgBindUV1BufferOfs(posBuff, noesis.RPGEODATA_HALFFLOAT, vtxStride, 0x1C)
					rapi.rpgBindBoneIndexBufferOfs(posBuff, noesis.RPGEODATA_UBYTE, vtxStride, 0x20, 4)
					rapi.rpgBindBoneWeightBufferOfs(posBuff, noesis.RPGEODATA_UBYTE, vtxStride, 0x24, 4)
				elif meshType == 3:
					rapi.rpgBindPositionBufferOfs(posBuff, noesis.RPGEODATA_FLOAT, vtxStride, 0)
					decodedNormals = rapi.decodeNormals32(posBuff[12:], vtxStride, -11, -11, -10, NOE_BIGENDIAN)
					decodedNormals = rapi.swapEndianArray(decodedNormals, 4)
					rapi.rpgBindNormalBuffer(decodedNormals, noesis.RPGEODATA_FLOAT, 12)
					rapi.rpgBindColorBufferOfs(posBuff, noesis.RPGEODATA_UBYTE, vtxStride, 0x18, 4)
					rapi.rpgBindUV1BufferOfs(posBuff, noesis.RPGEODATA_HALFFLOAT, vtxStride, 0x1C)
					rapi.rpgBindBoneIndexBufferOfs(posBuff, noesis.RPGEODATA_USHORT, vtxStride, 0x20, 4)
					rapi.rpgBindBoneWeightBufferOfs(posBuff, noesis.RPGEODATA_UBYTE, vtxStride, 0x2C, 4)
				if meshInfo1[0] == 3:
					rapi.rpgCommitTriangles(idxBuff, noesis.RPGEODATA_USHORT, meshInfo1[13], noesis.RPGEO_TRIANGLE, 1)
				elif meshInfo1[0] == 4:
					rapi.rpgCommitTriangles(idxBuff, noesis.RPGEODATA_USHORT, meshInfo1[13], noesis.RPGEO_TRIANGLE_STRIP, 1)
				rapi.rpgClearBufferBinds()

	def loadMeshNames(self, bs):
		pass

def kcLoadModel(data, mdlList):
	ctx = rapi.rpgCreateContext()
	rapi.rpgSetOption(noesis.RPGOPT_BIGENDIAN, 1)
	hxt = hxtFile(NoeBitStream(data, NOE_BIGENDIAN))
	hxt.loadAll(hxt.bs)
	try:
		mdl = rapi.rpgConstructModel()
	except:
		mdl = NoeModel()
	mdl.setModelMaterials(NoeModelMaterials(hxt.texList, hxt.matList))
	mdlList.append(mdl); mdl.setBones(hxt.boneList)	
	return 1

