from inc_noesis import *
import noesis
import rapi

def registerNoesisTypes():
	handle = noesis.register("Spike Chunsoft FTG .NPK",".npk")
	noesis.setHandlerTypeCheck(handle, noepyCheckType)
	noesis.setHandlerLoadModel(handle, noepyLoadModel)
	
	return 1
	
NOEPY_HEADER = "npak"
FORM2_SIGN = b'$PSD'
SKL_SIGN = b'$SKL'
MAT_SIGN = b'$MAT'
VTX_SIGN = b'$VTX'
SCN_SIGN = b'$SCN'
TXR_SIGN = b'$TXR'
TXI_SIGN = b'$TXI'
RSF_SIGN = b'$RSF'
matList = []

def readStringt(bs):
	chars = b''
	while True:
		c = bs.readBytes(1)
		if c == b'\x00':
			return chars
		chars += c

def Align(bs, n):
	value = bs.tell() % n
	if (value):
		bs.seek(n - value, 1)

def noepyCheckType(data):
   bs = NoeBitStream(data)
   if len(data) < 4:
      return 0
   if bs.readBytes(4).decode("ASCII") != NOEPY_HEADER:
      return 0
   return 1  

def noepyLoadModel(data, mdlList):
	fn = rapi.getExtensionlessName(rapi.getInputName())
	ctx = rapi.rpgCreateContext()
	bs = NoeBitStream(data)
	buff = bs.getBuffer(0x20,bs.dataSize)
		
	try:
		TXRBuff = buff[buff.index(TXR_SIGN):buff.index(TXI_SIGN)]
		TXRbs=NoeBitStream(TXRBuff)
	
		parserT1 = parseTXR(TXRbs,fn)
		textureList1 = parserT1[0]
		NPKVTempOffset = parserT1[1]
		Platf = parserT1[2]
	except:
		print("Textures for 1st form were not found")
		textureList1 = []
		NPKVTempOffset = 0
		Platf = 2
	
	try:
		BoneBuff = buff[buff.index(SKL_SIGN):buff.index(MAT_SIGN)]
		Bonebs = NoeBitStream(BoneBuff)
	
		parserB1 = parseBones(Bonebs)
		bones1 = parserB1[0]
		boneDict1 = parserB1[1]
	except:
		print("Bones were not found")
		bones1 = []
		boneDict1 = {}
	
	VTXBuff = buff[buff.index(VTX_SIGN):buff.index(SCN_SIGN)]
	VTXbs = NoeBitStream(VTXBuff)
	
	parserV1 = parseVTX(VTXbs, fn, bones1, boneDict1)
	mdl1 = parserV1[0]
	NPKITempOffset = parserV1[1]	
	
	mdl1.setModelMaterials(NoeModelMaterials(textureList1, matList))
	mdlList.append(mdl1)	
	
	bs.seek(0x7c)
	form2 = bs.readString()
	if form2 == "FormChangeModel1.srd":
		print("There's a 2nd form")
		tempbuff = buff[buff.index(FORM2_SIGN):]
		tempbuff = tempbuff[tempbuff.index(RSF_SIGN):]
		bs2 = NoeBitStream(tempbuff)
		buff2 = bs2.getBuffer(0x20,bs2.dataSize)
		
		extra2 = extraForm(buff2, NPKVTempOffset, NPKITempOffset, fn)
		mdl2 = extra2[0]
		NPKVTempOffset = extra2[1]
		NPKITempOffset = extra2[2]
		
		mdlList.append(mdl2)
	else:
		print("NO 2nd form\n")
	
	bs.seek(0x94)
	form3 = bs.readString()
	if form3 == "FormChangeModel2.srd":
		print("There's a 3rd form")
		tempbuff = buff2[buff2.index(RSF_SIGN):]
		if NPKITempOffset in [0x1298d4, 0xf9dc8, 0x59274] :
			tempbuff = tempbuff[0x20:]
			tempbuff = tempbuff[tempbuff.index(RSF_SIGN):]
		bs3 = NoeBitStream(tempbuff)
		buff3 = bs3.getBuffer(0x20,bs3.dataSize)
		
		extra3 = extraForm(buff3, NPKVTempOffset, NPKITempOffset, fn)
		mdl3 = extra3[0]
		NPKVTempOffset = extra3[1]
		NPKITempOffset = extra3[2]
		
		mdlList.append(mdl3)
	
	bs.seek(0xac)
	form4 = bs.readString()
	if form4 == "FormChangeModel3.srd":
		print("There's a 4th form")
		tempbuff = buff3[buff3.index(RSF_SIGN):]
		bs4 = NoeBitStream(tempbuff)
		buff4 = bs4.getBuffer(0x20,bs4.dataSize)
		
		extra4 = extraForm(buff4, NPKVTempOffset, NPKITempOffset, fn)
		mdl4 = extra4[0]
		NPKVTempOffset = extra4[1]
		NPKITempOffset = extra4[2]
		
		mdlList.append(mdl4)
	
	bs.seek(0xc4)
	form5 = bs.readString()
	if form5 == "FormChangeModel4.srd":
		print("There's a 5th form")
		tempbuff = buff4[buff4.index(RSF_SIGN):]
		bs5 = NoeBitStream(tempbuff)
		buff5 = bs5.getBuffer(0x20,bs5.dataSize)
		
		extra5 = extraForm(buff5, NPKVTempOffset, NPKITempOffset, fn)
		mdl5 = extra5[0]
		NPKVTempOffset = extra5[1]
		NPKITempOffset = extra5[2]
		
		mdlList.append(mdl5)
	
	return 1
	
def parseTXR(TXRbs,fn, NPKVbsOffset = 0):
	TexInfo = []
	texList = []
	while True:
		TXRbs.readBytes(0x16)
		width = TXRbs.readUShort()
		height = TXRbs.readUShort()
		TXRbs.readUShort()
		type1 = TXRbs.readUByte()
		num = TXRbs.readUByte()
		TXRbs.readBytes(0x12)
		platFlag = TXRbs.readUInt()
		TXRbs.readBytes(0xc)
		offset = TXRbs.readUInt() - 0x40000000
		
		if platFlag == 0x1300506 or num == 1:
			length = TXRbs.readUInt()
			TXRbs.readUInt64()
			if platFlag == 0x1300506:
				Platform = 1
			else:
				Platform = 2
		else:
			TXRbs.readBytes(0xc)
			count = num - 1
			for i in range(count):
				offs2 = TXRbs.readUInt() - 0x40000000
				le = TXRbs.readUInt()
				TXRbs.readUInt64()
			length = offs2 + le - offset
			Platform = 2
		offset += NPKVbsOffset
		texName = readStringt(TXRbs).decode('shift_jisx0213')
		Align(TXRbs,16)
		TXRbs.readBytes(0x10)
		TexInfo.append((rapi.getExtensionlessName(texName), width, height, type1, offset, length, Platform))
		if TXRbs.tell() == TXRbs.dataSize:
			break
	if (rapi.checkFileExists(fn + ".npkv")):
		npkvData = rapi.loadIntoByteArray(fn + ".npkv")
		NPKVbs = NoeBitStream(npkvData)
		print("NPKV found")
		for i, info in enumerate(TexInfo):
			textureName = info[0]
			width2 = info[1]
			height2 = info[2]
			type2 = info[3]
			offset2 = info[4]
			length2 = info[5]
			plat = info[6]
			NPKVbs.seek(offset2)
			textureData = NPKVbs.readBytes(length2)
			if type2 == 0xf:
				if plat == 1:
					textureData = rapi.callExtensionMethod("untile_1dthin", textureData, width2, height2, 4, 1)
				format = noesis.FOURCC_DXT1
			elif type2 == 0x14:
				if plat == 1:
					textureData = rapi.callExtensionMethod("untile_1dthin", textureData, width2, height2, 8, 1)
				format = noesis.FOURCC_ATI2
			elif type2 == 0x11:
				if plat == 1:
					textureData = rapi.callExtensionMethod("untile_1dthin", textureData, width2, height2, 8, 1)
				format = noesis.FOURCC_DXT5
			elif type2 == 0x10:
				format = noesis.FOURCC_DXT3
			textureData = rapi.imageDecodeDXT(textureData, width2, height2, format)
			format = noesis.NOESISTEX_RGBA32
			texture = NoeTexture(textureName, width2, height2, textureData, format)
			texList.append(texture)
		return (texList, NPKVbs.tell(), plat)
	else:
		print("NPKV not found")
		return (texList, 0,0)
	
def parseVTX(VTXbs,fn,bones,boneDict, NPKIbsOffset = 0):
	VTXInfo = []
	bindBoneList = []
	UVSize = 8
	
	while True:
		VTXbs.readUInt()
		VTXSize = VTXbs.read(">i")[0]
		RSISize = VTXbs.read(">i")[0]
		VTXbs.readUInt()
		tempOffset = VTXbs.tell()
		VTXbs.readBytes(0x6)
		cond = VTXbs.readUShort()
		VCount = VTXbs.readUInt()
		if cond == 520:
			VTXbs.readBytes(0x54)
		elif cond == 519:
			VTXbs.readBytes(0x4c)
		elif cond == 518:
			VTXbs.readBytes(0x44)
		elif cond == 517:
			VTXbs.readBytes(0x3C)
		elif cond == 516:
			VTXbs.readBytes(0x34)
		VBSize = VTXbs.readUInt()
		if cond not in [516, 517]:
			WeightAddress = VTXbs.readUInt()
			WeightSize = VTXbs.readUInt()
			UVAddress = VTXbs.readUInt()
			UVSize = VTXbs.readUInt()
		VTXbs.readUShort()
		bindboneListOfs = VTXbs.readUShort()
		if bindboneListOfs:
			VTXbs.seek(tempOffset+bindboneListOfs)
			bb = []
			while (VTXbs.tell() < tempOffset+VTXSize):
				name = VTXbs.readString()
				if name not in ["uvset","map1","map11", "map12", "damage_00", "damage_00(0)", "UVMap", "Col", "colorSet1","SculptFreezeColorTemp"]:
					bb.append(name)
			bindBoneList = bb
		else:
			VTXbs.seek(tempOffset+VTXSize)
		Align(VTXbs,16)
		VTXbs.readBytes(0x20)
		VAddress = VTXbs.readUInt() - 0x20000000 + NPKIbsOffset
		VEndAddress = VTXbs.readUInt()
		VTXbs.readUInt64()
		FaceAddress = VTXbs.readUInt() - 0x20000000 + NPKIbsOffset
		FCount = VTXbs.readUInt() // 2
		VTXbs.readUInt64()
		MeshName = VTXbs.readString()
		Align(VTXbs,16)
		VTXbs.readBytes(0x10)
		VTXInfo.append((MeshName, VAddress, VCount, VBSize, UVSize, FaceAddress, FCount, bindBoneList, cond))
		if VTXbs.tell() == VTXbs.dataSize:
			break
	
	if (rapi.checkFileExists(fn + ".npki")):
		npkiData = rapi.loadIntoByteArray(fn + ".npki")
		NPKIbs = NoeBitStream(npkiData)
		print("NPKI found\n")
		for i, info in enumerate(VTXInfo):
			submeshName = info[0]
			VertAddress = info[1]
			VertCount = info[2]
			VertBSize = info[3]
			UVsSize = info[4]
			FAddress = info[5]
			FICount = info[6]
			BindBoneList = info[7]
			Flag = info[8]
			rapi.rpgSetName(submeshName)
			matList.append(NoeMaterial(submeshName, ""))
			rapi.rpgSetMaterial(submeshName)
			NPKIbs.seek(VertAddress)
			VertBuff = NPKIbs.readBytes(VertCount * VertBSize)
			rapi.rpgBindPositionBufferOfs(VertBuff, noesis.RPGEODATA_FLOAT, VertBSize, 0)
			
			bidx = []
			bwgt = []
			if (Flag not in [516, 517]):
				for j in range(VertCount):
					for k in range(4):
						bwgt.append(NPKIbs.readFloat())
					for k in range(4):
						bidx.append(NPKIbs.readUInt())
				
				for j in range (len(bidx)):
					acutalIdx = boneDict[BindBoneList[bidx[j]]]
					bidx[j] = acutalIdx
				bidxList = struct.pack("iiii"*VertCount, *bidx)
				bwgtList = struct.pack("ffff"*VertCount, *bwgt)
				rapi.rpgBindBoneIndexBuffer(bidxList, noesis.RPGEODATA_INT, 16, 4)
				rapi.rpgBindBoneWeightBuffer(bwgtList, noesis.RPGEODATA_FLOAT, 16, 4) 
			else:
				bidxList = struct.pack("i"*VertCount, *[0]*VertCount)
				bwgtList = struct.pack("f"*VertCount, *[1.0]*VertCount)
				rapi.rpgBindBoneIndexBuffer(bidxList, noesis.RPGEODATA_INT, 4, 1)
				rapi.rpgBindBoneWeightBuffer(bwgtList, noesis.RPGEODATA_FLOAT, 4, 1)
			
			
			if Flag not in [516, 517]:
				UVBuff = NPKIbs.readBytes(VertCount * UVsSize)
				rapi.rpgBindNormalBufferOfs(VertBuff, noesis.RPGEODATA_FLOAT, VertBSize, 16)
				rapi.rpgBindTangentBufferOfs(VertBuff, noesis.RPGEODATA_FLOAT, VertBSize, VertBSize-16)
				if UVsSize >= 8 and UVsSize < 16:
					rapi.rpgBindUV1BufferOfs(UVBuff, noesis.RPGEODATA_FLOAT, UVsSize, UVsSize-8)
				elif UVsSize >= 16 and UVsSize < 24:
					rapi.rpgBindUV1BufferOfs(UVBuff, noesis.RPGEODATA_FLOAT, UVsSize, UVsSize-16)
					rapi.rpgBindUV2BufferOfs(UVBuff, noesis.RPGEODATA_FLOAT, UVsSize, UVsSize-8)
				elif UVsSize >= 24 and UVsSize < 32:
					rapi.rpgBindUV1BufferOfs(UVBuff, noesis.RPGEODATA_FLOAT, UVsSize, UVsSize-24)
					rapi.rpgBindUVXBufferOfs(UVBuff, noesis.RPGEODATA_FLOAT, UVsSize, 1, 2, UVsSize-16)
					rapi.rpgBindUVXBufferOfs(UVBuff, noesis.RPGEODATA_FLOAT, UVsSize, 2, 2, UVsSize-8)
			else:
				rapi.rpgBindNormalBufferOfs(VertBuff, noesis.RPGEODATA_FLOAT, VertBSize, 12)
				if VertBSize == 0x34:
					rapi.rpgBindUV1BufferOfs(VertBuff, noesis.RPGEODATA_FLOAT, VertBSize, 28)
					rapi.rpgBindUVXBufferOfs(VertBuff, noesis.RPGEODATA_FLOAT, VertBSize, 1, 2, 36)
					rapi.rpgBindUVXBufferOfs(VertBuff, noesis.RPGEODATA_FLOAT, VertBSize, 2, 2, 44)
				else:
					rapi.rpgBindUV1BufferOfs(VertBuff, noesis.RPGEODATA_FLOAT, VertBSize, 24)
			
			NPKIbs.seek(FAddress)
			FaceBuff = NPKIbs.readBytes(FICount * 2)
			rapi.rpgCommitTriangles(FaceBuff, noesis.RPGEODATA_USHORT, FICount, noesis.RPGEO_TRIANGLE, 1)
			rapi.rpgClearBufferBinds()
		
		rapi.rpgSetOption(noesis.RPGOPT_SWAPHANDEDNESS, 1)
		model = rapi.rpgConstructModel()
		model.setBones(bones)
		rapi.rpgReset()
		return (model,NPKIbs.tell())
	else:
		print("NPKI not found\n")
		model = NoeModel()
		return (model,0)

def parseBones(Bonebs):
	bones0 = []
	boneDict0 = {}	
	
	Bonebs.readUInt64()
	Bonebs.readUInt64()
	tempOffset2 = Bonebs.tell()
	Bonebs.readBytes(0x6)
	BCount = Bonebs.readUShort()
	Bonebs.readUInt()
	boneData = [None]*200
	lastFlag = 255
	
	for i in range(BCount):
		BoneNameOffset = Bonebs.readUInt()
		tempOffset3 = Bonebs.tell()
		Bonebs.seek(tempOffset2 + BoneNameOffset)
		BoneName = Bonebs.readString()
		Bonebs.seek(tempOffset3)
		currentID = Bonebs.read(">h")[0]
		parentID = currentID - 1
		currentFlag = Bonebs.readShort()
		Bonebs.readBytes(0x30) #localBoneMtx
		worldBoneMtx = NoeMat43.fromBytes(Bonebs.readBytes(0x30)).transpose().inverse().swapHandedness()
		boneData[currentID] = i
		
		if lastFlag != 255:
			bones0.append(NoeBone(i,BoneName, worldBoneMtx, None, boneData[parentID]))
		else:
			bones0.append(NoeBone(i,BoneName, worldBoneMtx, None, i-1))
		
		Bonebs.readBytes(0x18)
		lastFlag = currentFlag
		
	for b in range (BCount):
		boneDict0[bones0[b].name] = b
	
	return (bones0, boneDict0)

def extraForm(buff2, NPKVTempOffset, NPKITempOffset, fn):
	if NPKVTempOffset:
		NPKV_TEMP_BYTES = NPKVTempOffset.to_bytes(4, byteorder='little')
		values2 = bytearray(NPKV_TEMP_BYTES)
		if values2[0] > 0x80:
			values2[0] = 0x00
			values2[1] +=1
		elif values2[0] != 0x00:
			values2[0] = 0x80
		NPKVTempOffset = int.from_bytes(bytes(values2), byteorder='little', signed=False)
		
	try:
		TXRBuff2 = buff2[buff2.index(TXR_SIGN):buff2.index(TXI_SIGN)]
		TXRbs2 = NoeBitStream(TXRBuff2)
		
		parserT2 = parseTXR(TXRbs2,fn,NPKVTempOffset)
		textureList2 = parserT2[0]
		NPKVTempOffset = parserT2[1]
	except:
		print("There's no texture for this extra form")
		textureList2 = []
	
	try:
		BoneBuff2 = buff2[buff2.index(SKL_SIGN):buff2.index(MAT_SIGN)]
		Bonebs2 = NoeBitStream(BoneBuff2)
		
		parserB2 = parseBones(Bonebs2)
		bones2 = parserB2[0]
		boneDict2 = parserB2[1]
	except:
		print("Bones were not found")
		bones2 = []
		boneDict2 = {}
	
	NPKI_TEMP_BYTES = NPKITempOffset.to_bytes(4, byteorder='little')
	values = bytearray(NPKI_TEMP_BYTES)
	if values[0] > 0x80:
		values[0] = 0x00
		values[1] +=1
	elif values[0] != 0x00:
		values[0] = 0x80	
	NPKIOffset_num = int.from_bytes(bytes(values), byteorder='little', signed=False)
	VTXBuff2 = buff2[buff2.index(VTX_SIGN):buff2.index(SCN_SIGN)]
	VTXbs2 = NoeBitStream(VTXBuff2)		
	parserV2 = parseVTX(VTXbs2, fn, bones2, boneDict2, NPKIOffset_num)
	model2 = parserV2[0]
	NPKITempOffset = parserV2[1]
	
	model2.setModelMaterials(NoeModelMaterials(textureList2, matList))
	return (model2, NPKVTempOffset, NPKITempOffset)