Advertisement
Guest User

Untitled

a guest
Jan 11th, 2021
841
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 32.01 KB | None | 0 0
  1. #Version 1.2.1
  2.  
  3. from inc_noesis import *
  4. import os
  5.  
  6. ## credits :-
  7. # akderebur and I for researching the format and writing the script
  8. # rfuzzo for the CP77Tools
  9. # SurprisedMango, Divined, Yoratoni, ZeRoY, Banishingshift for some help for the tests
  10.  
  11. ## WIP cr2w metadata parsing
  12.  
  13. bHighestLODOnly = True #if put to True, the low poly meshes won't be loaded
  14. bLoadRigFile = True #if put to True, enables selecting a paired rig file with the skeleton hierarchy info
  15. bLoadSeveralRigFiles = False #if put to True, same as above but with several rig files and without validity check
  16. bParentToRootIfNoParent = False #if put to True, unparented bones will be parented to Root
  17.  
  18. matList = []
  19. texList = []
  20. txList = []
  21. miList = []
  22. mtList = []
  23. importList = []
  24. red_info = {}
  25. red_idx = {}
  26. cr2w_list = {}
  27. cr2w_external_list = []
  28. cr2w_external_data = []
  29. cr2w_textures = []
  30.  
  31. bTextureFileType = "dds"
  32. cp77_path = "C:\\dev\\cyberpunk\\out\\"
  33.  
  34. def registerNoesisTypes():
  35. handle = noesis.register("CP2077",".mesh;.cookedapp")
  36. noesis.setHandlerTypeCheck(handle, CheckType)
  37. noesis.setHandlerLoadModel(handle, LoadModel)
  38. return 1
  39.  
  40. def CheckType(data):
  41. bs = NoeBitStream(data)
  42. if len(data) < 16:
  43. print("Invalid file, too small")
  44. return 0
  45. return 1
  46.  
  47. def peek(bs,offset,dataType):
  48. curr_ptr = bs.tell()
  49. bs.seek(offset)
  50.  
  51. if dataType == "UShort":
  52. val = bs.readUShort()
  53. bs.seek(curr_ptr)
  54. return val
  55. else:
  56. bs.seek(curr_ptr)
  57.  
  58.  
  59. class CR2WData:
  60. def __init__(self,srcFile,exports,impList,extList):
  61. self.name = srcFile
  62. self.cr2w = []
  63. self.variables = []
  64. self.Imports = impList
  65. self.External = extList
  66.  
  67. class CR2WFile:
  68. def __init__(self, bs,srcFile):
  69. self.rootPtr = bs.tell()
  70. self.redfile = srcFile #os.path.basename(rapi.getInputName())
  71. cr2w_list[self.redfile] = {}
  72. #cr2w_external_list[self.redfile] = {}
  73. #print("[CR2W]::"+srcFile)
  74. cr2w_chunks = findAllFlags(bs, b'\x43\x52\x32\x57', bs.getSize())
  75. if len(cr2w_chunks) < 1:
  76. return 1
  77. idx = 0
  78. for i in cr2w_chunks:
  79. bs.seek(i)
  80. cr2wf = CR2WFile2(bs,srcFile)
  81. cr2w_list[self.redfile][str(idx)] = []
  82. #cr2w_external_list[self.redfile][str(idx)] = []
  83. if len(cr2wf.External) > 0:
  84. for exfile in cr2wf.External:
  85. if exfile not in cr2w_external_list:
  86. cr2w_external_list.append(exfile)
  87.  
  88. if len(cr2wf.Imports) > 0:
  89. for impfile in cr2wf.Imports:
  90. if impfile not in cr2w_external_list:
  91. cr2w_external_list.append(impfile)
  92.  
  93. if len(cr2wf.ExportInfo) > 0:
  94. for exp in cr2wf.ExportInfo:
  95. cr2w_list[self.redfile][str(idx)].append(exp.variables)
  96. idx += 1
  97.  
  98. class CR2WFile2:
  99. def __init__(self, bs,srcFile):
  100. #relative offset in current CR2W chunk
  101. self.redfile = srcFile
  102. self.rptr = bs.tell()
  103. self.ptr = bs.tell()
  104. self.header = CR2WFileHeader(bs)
  105. self.toc = []
  106. for i in range(10):
  107. self.toc.append(CR2WTable(bs,self.rptr))
  108. self.m_strings = []
  109. self.str_offsets = {}
  110. self.Names = []
  111. self.NHash = {}
  112. self.Imports = []
  113. self.ExportInfo = []
  114. self.Buffers = []
  115. self.External = []
  116.  
  117. self.StringDictionary = {}
  118. self.str_offsets = {}
  119. self.str_idx = []
  120. self.m_strings = {}
  121. self.import_names = []
  122. #print("fileheader=====")
  123. #print(vars(self.header))
  124. self.readStrings(bs)
  125. self.readNames(bs)
  126. self.readImports(bs)
  127. self.readProps(bs)
  128. self.readExports(bs)
  129. self.readBufferInfo(bs)
  130. self.readBufferData()
  131. #Table[0]
  132. def readStrings(self,bs):
  133. curr_ptr = bs.tell()
  134. sPtr,size,crc = self.toc[0].info()
  135. bs.seek(sPtr)
  136. idx = 0
  137. self.m_strings = []
  138. while(bs.tell() < (size + sPtr)):
  139. str_offset = bs.tell() - sPtr
  140. self.m_strings.append(bs.readString())
  141. self.str_offsets[str_offset] = idx
  142. idx+=1
  143. return 1
  144.  
  145. def strOffset(self,soffset):
  146. sidx = self.str_offsets[soffset]
  147. return self.m_strings[sidx]
  148.  
  149. #Table[1]
  150. def readNames(self,bs):
  151. curr_ptr = bs.tell()
  152. nPtr,nlen,crc = self.toc[1].info()
  153. bs.seek(nPtr)
  154. for idx in range(nlen):
  155. nval = bs.readUInt()
  156. nhash = bs.readUInt()
  157. self.Names.append(self.strOffset(nval))
  158. #self.nHash.append(nhash)
  159.  
  160. #Table[2]
  161. def readImports(self, bs):
  162. iPtr,ilen,crc = self.toc[2].info()
  163. bs.seek(iPtr)
  164. self.Imports = []
  165. #self.ImportsInfo = []
  166. for i in range(ilen):
  167. impObj = {}
  168. depotPath = bs.readUInt()
  169. className = bs.readUShort()
  170. flags = bs.readUShort()
  171. self.Imports.append(self.strOffset(depotPath))
  172. return 1
  173.  
  174. #Table[3]
  175. def readProps(self, bs):
  176. pPtr,plen,crc = self.toc[3].info()
  177. bs.seek(pPtr)
  178. self.Properties = []
  179. for i in range(plen):
  180. pObj = {}
  181. clsName = bs.readUShort()
  182. pObj["className"] = clsName
  183. pObj["classFlags"] = bs.readUShort()
  184. pObj["propertyName"] = bs.readUShort()
  185. pObj["propertyFlags"] = bs.readUShort()
  186. pObj["hash"] = bs.readUInt64()
  187. pObj["value"] = ''
  188. pObj["REDType"] = self.Names[clsName]
  189. pObj["REDName"] = self.Names[pObj["propertyName"]]
  190. self.Properties.append(pObj)
  191. return 1
  192.  
  193. def readREDInfo(self,bs):
  194. curr_ptr = bs.tell()
  195. rnidx = bs.readUShort()
  196. rtidx = bs.readUShort()
  197. if rnidx >= len(self.Names):
  198. #print("error "+self.redfile)
  199. #print("@"+ str(bs.tell()))
  200. val = "skip"
  201. return [str(rnidx),str(rtidx),val]
  202. rName = self.Names[rnidx]
  203. if rtidx >= len(self.Names):
  204. #print("error "+self.redfile)
  205. #print("@"+ str(bs.tell()))
  206. val = "skip"
  207. return [rName,str(rtidx),val]
  208. rType = self.Names[rtidx]
  209.  
  210. val = {}
  211.  
  212. if rType == "CName":
  213. ntype = bs.readUInt()
  214. rval = self.Names[bs.readUShort()]
  215. return [rName,rType,rval]
  216. elif rType == "ECookingPlatform":
  217. dlen = bs.readUInt()
  218. rVal = self.Names[bs.readUShort()]
  219. return [rName,rType,rVal]
  220. elif rType == "array:CName":
  221. cNameArr = [];
  222. arrSize = bs.readUInt()
  223. arrLen = bs.readUInt()
  224. for i in range(arrLen):
  225. cNameArr.append(self.Names[bs.readUShort()])
  226. bs.readUShort()
  227. return [rName,rType,cNameArr]
  228. elif rType == "array:EMaterialVertexFactory":
  229. rArr = [];
  230. dataPtr = bs.tell()
  231. dataLen = bs.readUInt()
  232. arrLen = bs.readUInt()
  233. for i in range(arrLen):
  234. rArr.append(self.Names[bs.readUShort()])
  235. checkPtr = dataPtr + dataLen
  236. if bs.tell() < checkPtr:
  237. bs.seek(checkPtr)
  238. return [rName,rType,rArr]
  239. elif rType == "Color":
  240. #dlen = bs.readUInt()
  241. bs.readUByte()
  242. col = {}
  243. xn,xt,xv = self.readREDInfo(bs)
  244. col[xn] = xv
  245. xn2,xt2,xv2 = self.readREDInfo(bs)
  246. col[xn2] = xv2
  247. xn3,xt3,xv3 = self.readREDInfo(bs)
  248. col[xn3] = xv3
  249. xn4,xt4,xv4 = self.readREDInfo(bs)
  250. col[xn4] = xv4
  251. bs.readUShort()
  252.  
  253. return [rName,rType,col]
  254. elif rType == "Vector4":
  255. bs.readUByte()
  256. vec = {}
  257. xn,xt,xv = self.readREDInfo(bs)
  258. vec[xn] = xv
  259.  
  260. yn,yt,yv = self.readREDInfo(bs)
  261. vec[yn] = yv
  262.  
  263. zn,zt,zv = self.readREDInfo(bs)
  264. vec[zn] = zv
  265.  
  266. wn,wt,wv = self.readREDInfo(bs)
  267. vec[wn] = wv
  268. return [rName,rType,vec]
  269.  
  270. elif rType == "Uint8":
  271. dlen = bs.readUInt()
  272. rval = bs.readUByte()
  273. return [rName,rType,rval]
  274. elif rType == "[3]Uint32":
  275. dlen = bs.readUInt()
  276. inum = bs.readUInt()
  277. rval = []
  278. for i in range(inum):
  279. rval.append(bs.readUInt())
  280. return [rName,rType,rval]
  281.  
  282. elif rType == "Float":
  283. flPtr = bs.tell()
  284. fflag = bs.readUByte()
  285. bs.seek(flPtr)
  286. if fflag == 8:
  287. dlen = bs.readUInt()
  288. rval = bs.readFloat()
  289. else:
  290. rval = bs.readFloat()
  291. return [rName,rType,rval]
  292. elif rType == "Bool":
  293.  
  294. dlen = bs.readUInt()
  295. rval = bs.readUByte()
  296. return [rName,rType,rval]
  297. elif rType == "rRef:IMaterial":
  298. dlen = bs.readUInt()
  299. bmid = bs.readUShort() - 1
  300. if len(self.Imports) > bmid:
  301. rval = self.Imports[bmid]
  302. self.External.append(rval)
  303. else:
  304. rval = bmid + 1
  305. return [rName,rType,rval]
  306.  
  307. elif rType == "rRef:ITexture":
  308. texFile = bs.readUShort()
  309. rval = self.Imports[texFile-1]
  310. self.External.append(rval)
  311. return [rName,rType,rval]
  312. elif rType == "rRef:CHairProfile":
  313. texFile = bs.readUShort()
  314. rval = self.Imports[texFile-1]
  315. self.External.append(rval)
  316. return [rName,rType,rval]
  317. elif rType == "rRef:Multilayer_Mask":
  318. texFile = bs.readUShort()
  319. rval = self.Imports[texFile-1]
  320. self.External.append(rval)
  321. return [rName,rType,rval]
  322. elif rType == "rRef:Multilayer_Setup":
  323. texFile = bs.readUShort()
  324. rval = self.Imports[texFile-1]
  325. self.External.append(rval)
  326. return [rName,rType,rval]
  327. else:
  328. #skip = bs.tell()
  329. #val = bs.readUInt()
  330. #skip = skip + val
  331. #if val > 4:
  332. # bs.seek(skip)
  333. val = "skip"
  334. return [rName,rType,val]
  335.  
  336. def readExportInfo(self,bs,offset,size,redType):
  337. curr_ptr = bs.tell()
  338. xPtr = offset
  339. endPtr = xPtr + size
  340. bs.seek(xPtr + 1)
  341. cVars = {}
  342.  
  343. if redType == "meshMeshAppearance":
  344.  
  345. xn,xt,xv = self.readREDInfo(bs)
  346. if xt == "array:CName":
  347. cVars[xn] = xv
  348. if bs.tell() < (endPtr - 4):
  349. xn2,xt2,xv2 = self.readREDInfo(bs)
  350. cVars[xn2] = xv2
  351.  
  352. elif xt == "CName":
  353. cVars[xn] = xv
  354. if bs.tell() < (endPtr - 4):
  355. xn2,xt2,xv2 = self.readREDInfo(bs)
  356. cVars[xn2] = xv2
  357. else:
  358. cVars[xn] = xv
  359.  
  360. elif redType == "CMaterialInstance":
  361.  
  362. mi = {}
  363. peekVal = peek(bs,bs.tell(),"UShort")
  364.  
  365. while peekVal > 0:
  366. xn,xt,xv = self.readREDInfo(bs)
  367. mi[xn] = xv
  368. peekVal = peek(bs,bs.tell(),"UShort")
  369. #print(mi)
  370. skip = bs.readUShort()
  371.  
  372. num_entries = bs.readUInt()
  373.  
  374. for i in range(num_entries):
  375. dataPtr = bs.tell()
  376. dataLen = bs.readUInt()
  377. xn,xt,xv = self.readREDInfo(bs)
  378. mi[xn] = xv
  379. #print(str(i)+":: "+xn+" :: "+xt+" @ "+str(dataPtr)+" -> "+str(dataLen))
  380. checkPtr = dataPtr + dataLen
  381. if bs.tell() < checkPtr:
  382. bs.seek(checkPtr)
  383. if dataLen > size:
  384. cVars[redType] = mi
  385. return cVars
  386. if xv == "skip" and dataLen > 8:
  387. bs.seek(dataPtr + dataLen)
  388.  
  389. cVars[redType] = mi
  390.  
  391. else:
  392. xn,xt,xv = self.readREDInfo(bs)
  393. cVars[xn+"_"+xt] = xv
  394.  
  395. bs.seek(curr_ptr);
  396. return cVars
  397.  
  398. #Table[4]
  399. def readExports(self, bs):
  400. curr_ptr = bs.tell()
  401. xPtr,xlen,crc = self.toc[4].info()
  402. bs.seek(xPtr)
  403. self.Chunks = []
  404. self.ExportInfo = []
  405. if xlen < 1:
  406. return 1
  407. for i in range(xlen):
  408. ex = CR2WExport(bs,i)
  409. ex.REDType = self.Names[ex.className]
  410. ex.dataOffset += self.rptr
  411.  
  412. self.ExportInfo.append(ex)
  413. for x in range(xlen):
  414. exp = self.ExportInfo[x]
  415. #print("[EXPORT]::"+exp.REDType)
  416. exData = self.readExportInfo(bs,exp.dataOffset,exp.dataSize,exp.REDType)
  417. #print(exData)
  418. self.ExportInfo[x].variables = exData
  419. return 1
  420.  
  421. #Table[5]
  422. def readBufferInfo(self, bs):
  423. curr_ptr = bs.tell()
  424. bPtr,blen,crc = self.toc[5].info()
  425. bs.seek(bPtr)
  426. self.Buffers = []
  427. if blen < 1:
  428. return 1
  429. for i in range(blen):
  430. bf = CR2WBuffer(bs)
  431. self.Buffers.append(bf)
  432.  
  433. def readBufferData(self):
  434. if len(self.Buffers) < 1:
  435. return 1
  436. fd = os.path.dirname(rapi.getInputName())
  437. fn = os.path.basename(rapi.getInputName())
  438. bpath = fd + "\\" + fn + ".0.buffer"
  439. if (rapi.checkFileExists(bpath)):
  440. bs2 = NoeBitStream(rapi.loadIntoByteArray(bpath))
  441. bs2.seek(0x6,1)
  442. if bs2.readUShort()==0x7FFF:
  443. return 1
  444. else:
  445. bs2.seek(0)
  446. cr2wtmp2 = CR2WFile(bs2,bpath)
  447. return 1
  448.  
  449. class CR2WTable:
  450. def __init__(self, bs,rel):
  451. self.offset = bs.readUInt() + rel
  452. self.len = bs.readUInt()
  453. self.crc32 = bs.readUInt()
  454. def info(self):
  455. return [self.offset,self.len,self.crc32]
  456.  
  457. class CR2WFileHeader:
  458. def __init__(self, bs):
  459. self.magic = bs.readUInt()
  460. self.version = bs.readUInt()
  461. self.flags = bs.readUInt()
  462. self.timeStamp = bs.readUInt64()
  463. self.buildVersion = bs.readUInt()
  464. self.fileSize = bs.readUInt()
  465. self.bufferSize = bs.readUInt()
  466. self.crc32 = bs.readUInt()
  467. self.numChunks = bs.readUInt()
  468.  
  469. class CR2WExport:
  470. def __init__(self, bs,chunkIndex):
  471. self.REDType = ""
  472. self.REDName = ""# redtyoe + str(ChunkIndex)
  473. self.className = bs.readUShort()
  474. self.objectFlags= bs.readUShort()
  475. self.parentID = bs.readUInt()
  476. self.dataSize = bs.readUInt()
  477. self.dataOffset = bs.readUInt()
  478. self.template = bs.readUInt()
  479. self.crc32 = bs.readUInt()
  480. self.ChunkIndex = chunkIndex + 1
  481. self.variables = {}
  482. self.data = []
  483. #self.data"] = self.readExportInfo(bs,exObj["dataOffset"],exObj["dataSize"],exObj["REDType"])
  484.  
  485. class CR2WBuffer:
  486. def __init__(self, bs):
  487. self.flags = bs.readUInt()
  488. self.index= bs.readUInt()
  489. self.offset = bs.readUInt()
  490. self.diskSize = bs.readUInt()
  491. self.memSize = bs.readUInt()
  492. self.crc32 = bs.readUInt()
  493. self.data = []
  494. self.data = []
  495.  
  496. def CR2WDump():
  497.  
  498. for extfile in cr2w_external_list:
  499. ipath = cp77_path + extfile
  500. iext = os.path.splitext(ipath)[1]
  501. iname = os.path.splitext(ipath)[0]
  502.  
  503. if iext == ".xbm":
  504. cr2w_textures.append(extfile)
  505. if (rapi.checkFileExists(ipath)):
  506. txList.append(ipath)
  507. else: #if iext == ".mi":
  508. cr2w_external_data.append(extfile)
  509. if (rapi.checkFileExists(ipath)):
  510. bs = NoeBitStream(rapi.loadIntoByteArray(ipath))
  511. cr2w_data = CR2WFile(bs,ipath)
  512. #else:
  513. # cr2w_external_data.append(extfile)
  514.  
  515. def LoadMats(mesh_name):
  516.  
  517. #thisName = rapi.getLocalFileName(rapi.getInputName())
  518. fd = os.path.dirname(rapi.getInputName())
  519. fn = os.path.basename(rapi.getInputName())
  520. tp = fd + "\\textures\\" + fn.split(".")[0] + "_"
  521. diffuseTex = tp + "d01." + bTextureFileType
  522. normalTex = tp + "n01." + bTextureFileType
  523.  
  524. material = NoeMaterial(mesh_name, "")
  525.  
  526. if (rapi.checkFileExists(diffuseTex)):
  527. material.setTexture(diffuseTex)
  528.  
  529. if (rapi.checkFileExists(normalTex)):
  530. material.setNormalTexture(normalTex)
  531.  
  532. rapi.rpgSetMaterial(mesh_name)
  533. matList.append(material)
  534. return 1
  535.  
  536. def buildFlagFromNames(names, nameToIndex, padding):
  537. flag = bytes()
  538. for name in names:
  539. flag += (nameToIndex[name]).to_bytes(2,byteorder='little')
  540. for i in range(padding):
  541. flag += b'\x00'
  542. return flag
  543.  
  544. def findAllFlags(bs, flag, maxOffset):
  545. a=True
  546. found = []
  547. endPtr = maxOffset - (len(flag)+1)
  548. while a and bs.tell() < endPtr:
  549. checkPoint = bs.tell()
  550. temp = bs.readBytes(len(flag))
  551. if temp == flag:
  552. found.append(checkPoint)
  553. #bs.seek(checkPoint)
  554. #a = False
  555. #return True
  556. else :
  557. bs.seek(checkPoint+1)
  558. return found
  559.  
  560. def findFlag(bs, flag, maxOffset):
  561. a=True
  562. while a and bs.tell() < maxOffset -1:
  563. checkPoint = bs.tell()
  564. temp = bs.readBytes(len(flag))
  565. if temp == flag:
  566. bs.seek(checkPoint)
  567. a = False
  568. return True
  569. else :
  570. bs.seek(checkPoint+1)
  571. return False
  572.  
  573. def ParseChunkHeader(bs):
  574. optr = bs.tell()
  575. magic = bs.readUInt()
  576. #print("--MAGIC--"+str(magic))
  577. version = bs.readUInt()
  578. bs.readBytes(16)
  579. dataOffset = bs.readUInt() + optr
  580. maxOffset = dataOffset
  581. bs.readBytes(12)
  582. stringSectionOffset = bs.readUInt() + optr
  583. stringSectionSize = bs.readUInt()
  584. bs.readBytes(4)
  585. stringSectionEndOffset = bs.readUInt() + optr
  586. hashTableEntryCount = bs.readUInt()
  587. bs.readBytes(4)
  588. #hashTableEndOffset = bs.readUInt()
  589. importSectionOffset = bs.readUInt() + optr
  590.  
  591. importCount = bs.readUInt()
  592. #bs.readBytes(4)
  593. #unkStruct1SectionEndOffset = bs.readUInt()
  594.  
  595. #unkStruct2Count = bs.readUInt()
  596. #bs.readBytes(4)
  597. #unkStruct2SectionEndOffset = bs.readUInt()
  598.  
  599. #unkStruct3Count = bs.readUInt()
  600. #bs.readBytes(4)
  601. #unkStruct3SectionEndOffset = bs.readUInt()
  602.  
  603. #unkStruct4Count = bs.readUInt()
  604. #bs.readBytes(4)
  605. #unkStruct4SectionEndOffset = bs.readUInt()
  606.  
  607. #unkStruct5Count = bs.readUInt()
  608. #bs.readBytes(4)
  609.  
  610. bs.seek(stringSectionOffset)
  611. #grab names
  612. indexToName = []
  613. strOffset = {}
  614. implist = []
  615. nameToIndex = {}
  616. index = 0
  617. #print("----strings @ "+str(stringSectionOffset))
  618. while(bs.tell() < stringSectionEndOffset):
  619. strIdx = bs.tell() - stringSectionOffset
  620. name = bs.readString()
  621. #print(name)
  622. indexToName.append(name)
  623. nameToIndex[name] = index
  624. strOffset[strIdx] = name
  625. index+=1
  626.  
  627. bs.seek(importSectionOffset)
  628. for i in range(importCount):
  629. imp = strOffset[bs.readUInt()]
  630. if imp not in importList:
  631. importList.append(imp)
  632. implist.append(imp)
  633. bs.readUInt()
  634.  
  635. bs.seek(optr)
  636. #print("----imports----")
  637. #print(implist)
  638. #print(strOffset)
  639. #print(indexToName)
  640. #bs.seek(unkStruct4SectionEndOffset + unkStruct5Count * 16)
  641. #indexToName, nameToIndex, maxOffset = ParseHeader(bs)
  642. return [strOffset, implist, indexToName, nameToIndex, maxOffset]
  643.  
  644. def ParseHeader(bs):
  645. magic = bs.readUInt()
  646. version = bs.readUInt()
  647. bs.readBytes(16)
  648. dataOffset = bs.readUInt()
  649. maxOffset = dataOffset
  650. bs.readBytes(12)
  651. stringSectionOffset = bs.readUInt()
  652. stringSectionSize = bs.readUInt()
  653. bs.readBytes(4)
  654. stringSectionEndOffset = bs.readUInt()
  655. hashTableEntryCount = bs.readUInt()
  656. bs.readBytes(4)
  657. #hashTableEndOffset = bs.readUInt()
  658. importSectionOffset = bs.readUInt()
  659.  
  660. importCount = bs.readUInt()
  661.  
  662. #unkStruct1Count = bs.readUInt()
  663. bs.readBytes(4)
  664. unkStruct1SectionEndOffset = bs.readUInt()
  665.  
  666. unkStruct2Count = bs.readUInt()
  667. bs.readBytes(4)
  668. unkStruct2SectionEndOffset = bs.readUInt()
  669.  
  670. unkStruct3Count = bs.readUInt()
  671. bs.readBytes(4)
  672. unkStruct3SectionEndOffset = bs.readUInt()
  673.  
  674. unkStruct4Count = bs.readUInt()
  675. bs.readBytes(4)
  676. unkStruct4SectionEndOffset = bs.readUInt()
  677.  
  678. unkStruct5Count = bs.readUInt()
  679. bs.readBytes(4)
  680.  
  681. bs.seek(stringSectionOffset)
  682. #grab names
  683. indexToName = []
  684. nameToIndex = {}
  685. strOffset = {}
  686. index = 0
  687. while(bs.tell() < stringSectionEndOffset):
  688. strIdx = bs.tell() - stringSectionOffset
  689. name = bs.readString()
  690. indexToName.append(name)
  691. nameToIndex[name] = index
  692. strOffset[strIdx] = name
  693. index+=1
  694.  
  695. bs.seek(importSectionOffset)
  696. for i in range(importCount):
  697. importList.append(strOffset[bs.readUInt()])
  698. bs.readUInt()
  699.  
  700. print("===root imports===")
  701. print(importList)
  702.  
  703. bs.seek(unkStruct4SectionEndOffset + unkStruct5Count * 16)
  704. return [indexToName, nameToIndex, maxOffset]
  705.  
  706. def LoadRig(br, meshBones, bindMatrices):
  707. indexToName, nameToIndex, maxOffset = ParseHeader(br)
  708.  
  709. # Read bone names
  710. bNameFlag = buildFlagFromNames(["boneNames","array:CName"],nameToIndex,0)
  711. rigBones = []
  712. if findFlag(br, bNameFlag, maxOffset):
  713. br.seek(0x8,1)
  714. boneC = br.readInt()
  715. for i in range(boneC):
  716. rigBones.append(indexToName[br.readUShort()])
  717. bfail = False
  718. #print(rigBones)
  719. #print("----")
  720. # Verify rig
  721. for boneN in meshBones:
  722. if boneN not in rigBones:
  723. print(boneN)
  724. bfail=True
  725.  
  726. if bfail:
  727. return [[], []]
  728. # Parenting info
  729. findFlag(br, b'\xFF\xFF\x00\x00', br.getSize())
  730.  
  731. parIds = []
  732. for b in range(boneC):
  733. parIds.append(br.readShort())
  734. bones = []
  735. for b in range(boneC):
  736. boneName = rigBones[b]
  737. bonePos = NoeVec3.fromBytes(br.readBytes(12))
  738. bonePos = NoeVec3([bonePos.vec3[0], bonePos.vec3[1], bonePos.vec3[2]])
  739. br.readFloat()
  740. boneRot = NoeQuat.fromBytes(br.readBytes(16)).transpose()
  741. boneScl = NoeQuat.fromBytes(br.readBytes(16))
  742. if boneName in meshBones: # Set bind matrix
  743. bInd = meshBones.index(boneName)
  744. matrix = NoeMat44(bindMatrices[bInd]).toMat43().inverse()
  745. else:
  746. matrix = boneRot.toMat43()
  747. matrix[3] = bonePos
  748. if b:
  749. matrix*=bones[parIds[b]].getMatrix()
  750.  
  751. bones.append(NoeBone(b, rigBones[b], matrix, None, parIds[b]))
  752.  
  753. # Return bones and name list (for easy remap)
  754. return [bones, rigBones]
  755.  
  756. def LoadRig2(br, meshBones, boneNameToParentName):
  757. stringIndexToName, nameToStringIndex, maxOffset = ParseHeader(br)
  758. checkPoint = br.tell()
  759.  
  760. # Read bone names
  761. bNameFlag = buildFlagFromNames(["boneNames","array:CName"],nameToStringIndex,0)
  762. boneNamesToIndex = {}
  763. IndexToRigBoneName = []
  764. names = []
  765. if findFlag(br, bNameFlag, maxOffset):
  766. br.seek(0x8,1)
  767. boneC = br.readInt()
  768. for i in range(boneC):
  769. index = br.readUShort()
  770. IndexToRigBoneName.append(stringIndexToName[index])
  771. boneNamesToIndex[stringIndexToName[index]] = i
  772. names.append(stringIndexToName[index])
  773.  
  774. # Parenting info
  775. findFlag(br, b'\xFF\xFF\x00\x00', br.getSize())
  776.  
  777. parIds = []
  778. for b in range(boneC):
  779. parIds.append(br.readShort())
  780.  
  781. for name in names:
  782. b = boneNamesToIndex[name]
  783. if name not in boneNameToParentName:
  784. if parIds[b] >=0 and IndexToRigBoneName[parIds[b]] in meshBones:
  785. boneNameToParentName[name] = IndexToRigBoneName[parIds[b]]
  786. else:
  787. if bParentToRootIfNoParent:
  788. boneNameToParentName[name] = "Root"
  789. else:
  790. boneNameToParentName[name] = ""
  791.  
  792. missingParent = []
  793. for name in meshBones:
  794. if name not in boneNameToParentName:
  795. missingParent.append(name)
  796.  
  797. if missingParent:
  798. print("Bones without parenting info left :")
  799. for name in missingParent:
  800. print(name)
  801. return True
  802. else:
  803. print("All bones have parenting info, proceeding")
  804. return False
  805.  
  806. def LoadModel(data, mdlList):
  807.  
  808. ctx = rapi.rpgCreateContext()
  809. bs = NoeBitStream(data)
  810.  
  811. indexToName, nameToIndex, maxOffset = ParseHeader(bs)
  812. checkPoint = bs.tell()
  813. bs.seek(0)
  814. cr2wtmp = CR2WFile(bs,os.path.basename(rapi.getInputName()))
  815.  
  816. bs.seek(checkPoint)
  817. noesis.logPopup()
  818. #Quantization info
  819. quantScaleFlag = buildFlagFromNames(["quantizationScale","Vector4"],nameToIndex,0)
  820. if not findFlag(bs, quantScaleFlag, maxOffset):
  821. print("No quantization scale found")
  822. return 0
  823. bs.seek(0x9,1)
  824. qScale = []
  825. for i in range(4):
  826. bs.seek(0x8,1)
  827. qScale.append(bs.readFloat())
  828. bs.seek(checkPoint)
  829.  
  830. quantOffFlag = buildFlagFromNames(["quantizationOffset","Vector4"],nameToIndex,0)
  831. if not findFlag(bs, quantOffFlag, maxOffset):
  832. print("No quantization offset found")
  833. return 0
  834. bs.seek(0x9,1)
  835. qOff = []
  836. for i in range(4):
  837. bs.seek(0x8,1)
  838. qOff.append(bs.readFloat())
  839. bs.seek(checkPoint)
  840.  
  841. qMat = NoeMat43()
  842. for i in range(3):
  843. qMat[i][i] = qScale[i]
  844. qMat[3] = NoeVec3(qOff[:-1])
  845.  
  846.  
  847. #get BBox info, not necessary though since scaling is done by the quantizationScale
  848. boxFlag = buildFlagFromNames(["boundingBox","Box"],nameToIndex,0)
  849. if not findFlag(bs, boxFlag, maxOffset):
  850. print("No BBox")
  851. return 0
  852. bs.seek(0x12,1)
  853. minBBox = []
  854. for i in range(4):
  855. bs.seek(0x8,1)
  856. minBBox.append(bs.readFloat())
  857. bs.seek(0xB,1)
  858. maxBBox = []
  859. for i in range(4):
  860. bs.seek(0x8,1)
  861. maxBBox.append(bs.readFloat())
  862.  
  863.  
  864. checkPoint = bs.tell()
  865. #get different vert/idx counts
  866. vCounts = []
  867. idxCounts = []
  868. vertDefs = []
  869. posFlag = buildFlagFromNames(["numVertices","Uint16"],nameToIndex,0)
  870. while findFlag(bs, posFlag, maxOffset):
  871. bs.seek(8,1)
  872. vCounts.append(bs.readUShort())
  873. bs.seek(8,1)
  874. idxCounts.append(bs.readUInt())
  875.  
  876. submeshCount = len(idxCounts)
  877. bs.seek(checkPoint)
  878.  
  879. #Grab bones if relevant
  880. boneNames=["Root"]
  881. glBoneNames=[]
  882. boneNameToParentName={}
  883. boneNameToParentName["Root"] = ""
  884. bones = []
  885. bMap = []
  886.  
  887. boneLoadLoop = True if bLoadRigFile else False
  888. bSkeletonBuilt = False
  889. if "boneRigMatrices" in nameToIndex:
  890. bs.seek(checkPoint)
  891. boneFlags = buildFlagFromNames(["boneRigMatrices","array:Matrix"],nameToIndex,0)
  892. if findFlag(bs, boneFlags, maxOffset):
  893. bs.seek(0x4,1)
  894. sectionSize = bs.readUInt()
  895. boneCount = int(sectionSize/239) #Can be grabbed at the beginning too
  896. for i in range(boneCount):
  897. boneNames.append(indexToName[nameToIndex["boneNames"] + i + 1])
  898.  
  899. lst = [[NoeVec4([1,0,0,0]), NoeVec4([0,1,0,0]), NoeVec4([0,0,1,0]), NoeVec4([0,0,0,1])]] #Root placeholder
  900. for i in range(boneCount):
  901. bs.seek(3,1)
  902. v = []
  903. for j in range(4):
  904. bs.seek(11,1)
  905. for k in range(4):
  906. bs.seek(8,1)
  907. v.append(bs.readFloat())
  908. lst.append([NoeVec4(v[4*u:4*u+4]) for u in range(4)])
  909.  
  910. while boneLoadLoop:
  911. rigData = rapi.loadPairedFileOptional("rig file", ".rig")
  912. if rigData is not None:
  913. if bLoadSeveralRigFiles:
  914. br = NoeBitStream(rigData)
  915. if not LoadRig2(br, boneNames, boneNameToParentName):
  916. boneLoadLoop = False
  917. else:
  918. br = NoeBitStream(rigData)
  919. bones, glBoneNames = LoadRig(br, boneNames, lst)
  920. if bones:
  921. boneLoadLoop = False
  922. bSkeletonBuilt = True
  923. else:
  924. print("Invalid rig file, choose another one")
  925. else:
  926. boneLoadLoop = False
  927.  
  928. if not bSkeletonBuilt:
  929. for i,l in enumerate(lst):
  930. matrix = NoeMat44(l).toMat43().inverse()
  931. if boneNames[i] in boneNameToParentName:
  932. bone = NoeBone(i, boneNames[i], matrix, boneNameToParentName[boneNames[i]], 0)
  933. else:
  934. if i != 0 and bParentToRootIfNoParent:
  935. bone = NoeBone(i, boneNames[i], matrix, None, 0)
  936. else:
  937. bone = NoeBone(i, boneNames[i], matrix, None, -1)
  938. bones.append(bone)
  939. bMap.append(i+1)
  940. else:
  941. for i in range(len(boneNames)):
  942. bMap.append(glBoneNames.index(boneNames[i]))
  943.  
  944. bs.seek(checkPoint)
  945.  
  946. # Index offsets for multiple meshes
  947. indOffs = []
  948. indOffs.append(0)
  949. if "teOffset" in nameToIndex:
  950. indOffFlag = buildFlagFromNames(["teOffset","Uint32"],nameToIndex,0)
  951. while findFlag(bs, indOffFlag, maxOffset):
  952. bs.seek(8, 1)
  953. indOffs.append(bs.readUInt())
  954. bs.seek(checkPoint)
  955. # Vertex Component Definitions
  956. vDefFlag = buildFlagFromNames(["vertexLayout","GpuWrapApiVertexLayoutDesc"],nameToIndex,0)
  957. uvSeen = 0
  958. while findFlag(bs, vDefFlag, maxOffset):
  959. bs.seek(17, 1)
  960. compC = bs.readInt()
  961. bs.seek(1, 1)
  962.  
  963. vertDef = []
  964. for c in range(compC):
  965. bs.seek(8, 1)
  966. compTInd = bs.readUShort()
  967. bs.seek(8, 1)
  968. compNInd = bs.readUShort()
  969.  
  970. if compTInd >= len(indexToName) or compNInd >= len(indexToName):
  971. break
  972.  
  973. compType = indexToName[compTInd]
  974. compName = indexToName[compNInd]
  975.  
  976. if compName == "PS_TexCoord":
  977. uvSeen = 1
  978.  
  979. if uvSeen == 1:
  980. uvSeen = 0
  981.  
  982. vertDef.append([compName, compType])
  983.  
  984. # Skip stream usage/type/index for now
  985. testName = indexToName[bs.readUShort()]
  986. if testName == "streamIndex" or testName == "usageIndex":
  987. bs.seek(7, 1)
  988. testName = indexToName[bs.readUShort()]
  989. if testName == "streamIndex":
  990. bs.seek(7, 1)
  991. testName = indexToName[bs.readUShort()]
  992. if testName == "streamType":
  993. bs.seek(11, 1)
  994. else:
  995. bs.seek(1, 1)
  996. elif testName == "streamType":
  997. bs.seek(11, 1)
  998. else:
  999. bs.seek(1, 1)
  1000. else:
  1001. bs.seek(1, 1)
  1002.  
  1003. if compName == "PS_DestructionIndices":
  1004. bs.seek(22, 1)
  1005. vertDefs.append(vertDef)
  1006.  
  1007.  
  1008. bs.seek(checkPoint)
  1009. # Offsets for where vertex components start
  1010. # Some components are grouped and only 1 offset for them
  1011. cmpOffFlag = buildFlagFromNames(["byteOffsets","static:5,Uint32"],nameToIndex,0)
  1012. # Vertex component offsets
  1013. vCompOffs = []
  1014. while findFlag(bs, cmpOffFlag, maxOffset):
  1015. bs.seek(8, 1)
  1016. offC = bs.readInt()
  1017. offs = []
  1018. for a in range(offC):
  1019. offs.append(bs.readInt())
  1020. vCompOffs.append(offs)
  1021. bs.seek(checkPoint)
  1022.  
  1023. #Get index section offset, not necessary but better to have in case something wrong happens with align and stuff
  1024. indexOfsFlag = buildFlagFromNames(["indexBufferOffset","Uint32"],nameToIndex,0)
  1025. if not findFlag(bs, indexOfsFlag, maxOffset):
  1026. print("couldn't find index offset")
  1027. return 0
  1028. bs.seek(8,1)
  1029. idxOffset = bs.readUInt()
  1030. bs.seek(checkPoint)
  1031.  
  1032. #Grab LOD info
  1033. lodInfo = []
  1034. if bHighestLODOnly:
  1035. LODInfoFlag = buildFlagFromNames(["lodMask","Uint8"],nameToIndex,0)
  1036. while findFlag(bs, LODInfoFlag, maxOffset):
  1037. bs.seek(8, 1)
  1038. lodInfo.append(bs.readUByte())
  1039.  
  1040. #Grab correct paired buffer file
  1041. bBufferDetected = False
  1042. thisName = rapi.getLocalFileName(rapi.getInputName())
  1043. dir = os.path.dirname(rapi.getInputName())
  1044. for root, dirs, files in os.walk(dir):
  1045. for fileName in files:
  1046.  
  1047. lowerName = fileName.lower()
  1048. if lowerName.endswith(".buffer") and lowerName.split(".")[0] == "".join(thisName.split(".")[:-1]):
  1049. bufferPath = os.path.join(root, fileName)
  1050.  
  1051. if (rapi.checkFileExists(bufferPath)):
  1052. bs2 = NoeBitStream(rapi.loadIntoByteArray(bufferPath))
  1053. bs2.seek(0x6,1)
  1054. if bs2.readUShort()==0x7FFF:
  1055. bs = NoeBitStream(rapi.loadIntoByteArray(bufferPath))
  1056. print("Detected : " + lowerName)
  1057. bBufferDetected = True
  1058. break
  1059. #else:
  1060. #bs2.seek(0)
  1061. #cr2wtmp2 = CR2WFile(bs2)
  1062.  
  1063. if not bBufferDetected:
  1064. print("Couldn't find a suitable buffer file")
  1065. return 0
  1066.  
  1067. #$## parse imports/external list ??
  1068. #CR2WDump()
  1069. #print(" ")
  1070. #print("==========cr2w_external_list=============")
  1071. #print(cr2w_external_data)
  1072. #print("--------------------")
  1073. #print(" ")
  1074. #print("==========cr2w_textures=============")
  1075. #print(cr2w_textures)
  1076.  
  1077. #print(" ")
  1078. #print("==========cr2w_list=============")
  1079.  
  1080. #!!! need to split string by 0x4000,
  1081. #print(cr2w_list)
  1082. #print("--------------------")
  1083.  
  1084.  
  1085. #Parsing semantics
  1086. posBuffers = []
  1087. posBuffers2 = []
  1088. uvBuffers = []
  1089. normBuffers = []
  1090. posBStrides = []
  1091. vDefInd = 0
  1092. for i,vc in enumerate(vCounts):
  1093. if i >= len(indOffs):#breaks, avoid issue with shared buffs used on a few meshes. May need to use streamIndex info for these
  1094. break
  1095. if bHighestLODOnly and i < len(lodInfo):
  1096. if lodInfo[i] > 1:
  1097. break
  1098. vertDef = vertDefs[vDefInd]
  1099. vCompOff = vCompOffs[vDefInd]
  1100. vDefInd = vDefInd + 1
  1101. skinBICount = 0
  1102. skinBWCount = 0
  1103.  
  1104. posBStride = 8
  1105. for comp in vertDef:
  1106. if comp[0] == "PS_SkinIndices":
  1107. skinBICount +=1
  1108. posBStride+=4
  1109. elif comp[0] == "PS_SkinWeights":
  1110. skinBWCount +=1
  1111. posBStride+=4
  1112. elif comp[0] == "PS_ExtraData":
  1113. posBStride+=8
  1114.  
  1115. # Normally need to make a reader that can read any data type
  1116. # But I will just assume each component will use the same type every time
  1117. uvAdded = 0
  1118. if(vCompOff[0] < bs.getSize()):
  1119. bs.seek(vCompOff[0])
  1120. else:
  1121. print("Error, wrong buffer file used, try to rename the mesh and the .buffer")
  1122. return 0
  1123. for comp in vertDef:
  1124. #positions
  1125. start = bs.tell()
  1126. if comp[0] == "PS_Position":
  1127. buffer = bs.readBytes(vc*(posBStride))
  1128. rapi.rpgBindPositionBuffer(buffer,noesis.RPGEODATA_SHORT, posBStride)
  1129. if(skinBICount)>0: #assuming that BI count always equal as BW count, so assuming no weird stuff
  1130. rapi.rpgBindBoneIndexBufferOfs(buffer, noesis.RPGEODATA_UBYTE, posBStride, 8, 4 * skinBICount)
  1131. rapi.rpgBindBoneWeightBufferOfs(buffer, noesis.RPGEODATA_UBYTE, posBStride, 8 + 4*skinBICount, 4 * skinBWCount)
  1132. elif comp[0] == "PS_TexCoord": # Can be multiple
  1133. if uvAdded == 0: #prevent UV2 layer messing up
  1134. if(vCompOff[1] < bs.getSize()):
  1135. bs.seek(vCompOff[1])
  1136. else:
  1137. print("Error, wrong buffer file used, try to rename the mesh and the .buffer")
  1138. return 0
  1139. rapi.rpgBindUV1Buffer(bs.readBytes(vc*4), noesis.RPGEODATA_HALFFLOAT, 0x4)
  1140. elif uvAdded == 1:
  1141. if(vCompOff[3] < bs.getSize()):
  1142. bs.seek(vCompOff[3])
  1143. else:
  1144. print("Error, wrong buffer file used, try to rename the mesh and the .buffer")
  1145. return 0
  1146. rapi.rpgBindUV2BufferOfs(bs.readBytes(vc*8), noesis.RPGEODATA_HALFFLOAT, 0x8, 0x4)
  1147. uvAdded = uvAdded + 1
  1148. elif comp[0] == "PS_Normal": #more research needed
  1149. if(vCompOff[2] < bs.getSize()):
  1150. bs.seek(vCompOff[2])
  1151. else:
  1152. print("Error, wrong buffer file used, try to rename the mesh and the .buffer")
  1153. return 0
  1154. buff = rapi.decodeNormals32(bs.readBytes(vc*8),8, 10, 10, 10,0)
  1155. rapi.rpgBindNormalBuffer(buff, noesis.RPGEODATA_FLOAT, 0xC)
  1156. #elif comp[0] == "PS_Tangent": #seem interleaved with normals
  1157. # normBuffers.append(bs.readBytes(vc*4)) #PT_Dec4
  1158. # elif comp[0] == "PS_Color":
  1159. # bs.seek(vCompOff[3])
  1160. # bs.seek(vc * 4,1) #PT_Color
  1161. else:
  1162. continue
  1163.  
  1164. #grab indices, commit, clear buffers
  1165. bs.seek(idxOffset + indOffs[i])
  1166. rapi.rpgSetName("submesh"+str(i))
  1167. rapi.rpgSetTransform(qMat)
  1168. if bMap:
  1169. rapi.rpgSetBoneMap(bMap)
  1170.  
  1171. # Hacky Method of Loading Tex
  1172. LoadMats("submesh"+str(i))
  1173.  
  1174. rapi.rpgCommitTriangles(bs.readBytes(idxCounts[i]*2), noesis.RPGEODATA_USHORT, idxCounts[i], noesis.RPGEO_TRIANGLE)
  1175. rapi.rpgClearBufferBinds()
  1176.  
  1177. rapi.rpgSetOption(noesis.RPGOPT_TRIWINDBACKWARD,1)
  1178. try:
  1179. mdl = rapi.rpgConstructModel()
  1180. except:
  1181. mdl = NoeModel()
  1182. if(bones):
  1183. mdl.setBones(bones)
  1184. mdl.setModelMaterials(NoeModelMaterials(texList, matList))
  1185. mdlList.append(mdl)
  1186.  
  1187. return 1
  1188.  
  1189.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement