IDOLM@STER One For All .pmd

Extraction and unpacking of game archives and compression, encryption, obfuscation, decoding of unknown files
EclipsedVisions
Posts: 5
Joined: Sun Mar 31, 2019 7:14 am

IDOLM@STER One For All .pmd

Post by EclipsedVisions »

Hello, I'm trying to make a .pmd idolmaster ofa extension plugin, and so far have managed to get bone count and names, texture count and names and mesh count, however i can't seem to be able to make sense of vertex format. I think that vertex data for the first submesh, ACC_HEADShape starts at 0xF5D4 but I'm not sure how to parse it and where the vertex/face count for each submesh is stored, and there's one strange issue with the printf function for texture names not diplaying the first 4 characters in texture name. Could anyone help me out with this? I'm pretty new to Python and reverse engineering game data in general so I'm a bit lost here. Here's the sample model: https://www.dropbox.com/s/pqpm8bki9vzwy ... r.pmd?dl=0
And this is my code:

Code: Select all

from inc_noesis import *

import noesis
import rapi

def registerNoesisTypes():
   handle = noesis.register("The IDOLM@STER One For All Model", ".pmd")
   noesis.setHandlerTypeCheck(handle, noepyCheckType)
   noesis.setHandlerLoadModel(handle, noepyLoadModel)
   noesis.logPopup()
       #print
   return 1

NOEPY_HEADER = "PMD"

def noepyCheckType(data):

   bs = NoeBitStream(data)
   if len(data) < 3:
      return 0
   if bs.readBytes(3).decode("ASCII").rstrip("\0") != NOEPY_HEADER:
      return 0
   return 1       

def noepyLoadModel(data, mdlList):
   ctx = rapi.rpgCreateContext()
   bs = NoeBitStream(data)

   bs.seek(0x10, NOESEEK_ABS) #PTR - Bone block

   bs.seek(0x13, NOESEEK_REL)
   BoneCount = bs.read("i") #Bone count
   print(BoneCount)
   bs.seek(0x70C, NOESEEK_ABS) #Bones
   bs.seek(0xFC, NOESEEK_REL) #First bone data
   BoneNames = []
   for i in range(0, BoneCount[0]-1):
      BoneNames.append (bs.readBytes(32).decode("UTF-8").rstrip("\0"))
      bs.seek(0xFC, NOESEEK_REL) #Skip bone data
   print(BoneNames)
   
   bs.seek(0xF448, NOESEEK_ABS) #PME - Mesh block
   
   bs.seek(0x13, NOESEEK_REL)
   MeshCount = bs.read("i") #Mesh count
   print(MeshCount)
   MeshNames = []
   #for i in range(0, MeshCount[0]):
   bs.seek(0x49, NOESEEK_REL) #Seek to mesh name
   MeshNames.append (bs.readBytes(32).decode("UTF-8").rstrip("\0"))
   bs.seek(0x17, NOESEEK_REL)
   VCount=bs.read("i")
   print(VCount)
   bs.seek(0x125, NOESEEK_REL) #Seek to vertex data
   VertBuff = bs.readBytes(VCount[0] * 0x2C)
   rapi.rpgBindPositionBufferOfs(VertBuff, noesis.RPGEODATA_FLOAT, 44, 0)
   rapi.rpgCommitTriangles(None, noesis.RPGEODATA_USHORT, VCount[0], noesis.RPGEO_POINTS, 1)
     
   mdl = rapi.rpgConstructModel()
   mdlList.append(mdl)
   
   bs.seek(0x10C240, NOESEEK_ABS) #PTE - Texture block

   bs.seek(0x13, NOESEEK_REL) #Texture count
   TexCount = bs.read("i")
   print(TexCount)
   bs.seek(0xA9, NOESEEK_REL)
   TexNames = []
   for i in range(0, TexCount[0]):
      TexNames.append (bs.readBytes(40).decode("UTF-8").rstrip("\0")+".gtf")
      bs.seek(0x148, NOESEEK_REL)
   print(TexNames)
   
   rapi.rpgClearBufferBinds()   
   return 1