import struct import os import sys input={} # '''0x0 : "vertex.position", # 0x1 : "vertex.texcoord", # 0x4 : "vertex.color?"}''' output={} # {0x0 : "glPosition", # 0x2 : "glTexcoord", # 0x4 : "glTexcoord", # 0x6 : "glColor?", # 0x8 : "glTexcoord?"} lineIndentLevel={} def indentLine(k): if not(k in lineIndentLevel): lineIndentLevel[k]=0 lineIndentLevel[k]+=1 def unindentLine(k): if not(k in lineIndentLevel): lineIndentLevel[k]=0 lineIndentLevel[k]-=1 def resetIndentLevel(): for k in lineIndentLevel: lineIndentLevel[k]=0 def getWord(b, k, n=4): return sum(list(map(lambda c: b[k+c]<<(c*8),range(n)))) def convFloat24(f): #seee eeee mmmm mmmm mmmm mmmm if f==0x0: return 0.0 s=f>>23 e=(f>>16)&0x7F m=f&0xffff x=pow(2.0,e-63)*(1 + m*pow(2.0,-16)) if f&0x800000!=0: return -x return x # # doesn't quite work, but could be a more accurate approach ? # def convFloat24(val): # if val==0x0: # return 0.0 # tmp=((val>>16)&0xFF)+0x40 # out=(tmp<<23)|((val&0x800000)<<31)|((val&0xFFFF)<<7) # try: # return (struct.unpack("f",struct.pack("I",out)))[0] # except: # return (val) def parseSymbol(b,o): len=0 while getWord(b,o+len,1)!=0x00: len+=1 return(b[o:o+len].decode("ascii")) def parseExtTable(data): l=len(data) out=[] for i in range(0,l,8): out+=[(getWord(data, i), getWord(data, i+0x4))] return out def getRegisterNameSRC(v): if v<0x80: return getRegisterNameSRC1(v) elif v<0x88: return "i"+str(v-0x80) else: return "b"+str(v-0x88) def getRegisterNameSRC1(v): if v<0x10: return "v"+str(v&0xF) elif v<0x20: return "r"+str(v-0x10) elif v<0x80: return "c"+str(v-0x20) else: return ("r%02X"%(v)) def getRegisterNameSRC2(v): return getRegisterNameSRC1(v) def getRegisterNameDST(v): if v<0x10: return ("o%X"%(v)) elif v<0x20: return "r"+str(v-0x10) else: return ("r%02X"%(v)) def getRegisterName(v): return ("r%02X"%(v)) # if v<16: # return "v"+str(v&0xF) # elif v<120: # return "c"+str(v-16) # else: # return "b"+str(v-0x88) def getInputSymbol(v, vt, ut, idx): src=getRegisterNameSRC(v) return getInputSymbolFromString(src, vt, ut, idx) def getInputSymbolFromString(src, vt, ut, idx): idxstr="" if idx==1: idxstr="[idx1]" elif idx==2: idxstr="[idx2]" elif idx==3: idxstr="[lcnt]" if src in vt: return vt[src]+idxstr if src in ut: return ut[src]+idxstr else: f=src.find(".") if f>=0: src=getInputSymbolFromString(src[:f], vt, ut, idx)+src[f:] idxstr="" return src+idxstr def getOutputSymbol(v, ot): dst=getRegisterNameDST(v) return ot[dst] if dst in ot else dst def getLabelSymbol(v, t): return t[v][1] if v in t else hex(v) def initIndent(): global numIdent numIdent=0 def indentOut(): global numIdent numIdent=numIdent+1 def unindentOut(): global numIdent numIdent=numIdent-1 def iprint(s, e=False): global numIdent if e: print(" "*numIdent+s,end='') else: print(" "*numIdent+s) comp=["x", "y", "z", "w"] def parseComponentMask(v): out="" for i in range(4): if v&(1<<(3-i))!=0x0: out+=comp[i] else: out+="_" return out def parseExt(v): return {"src1" : (v>>5)&0xFF, "src2" : (v>>14)&0xFF, "src3" : (v>>23)&0xFF, "nsrc1" : (v>>4)&0x1, #negation bit "nsrc2" : (v>>13)&0x1, #negation bit "nsrc3" : (v>>22)&0x1, #negation bit "dst" : (v)&0x1F, "dstcomp" : parseComponentMask(v&0xF), "rest" : (v>>22)} def parseComponentSwizzle(v): out="" for i in range(4): out+=comp[(v>>((3-i)*2))&0x3] return out def parseInstFormat10(k, v, lt={}): return {"opcode" : v>>26, "src2" : (v>>7)&0x1F, "src1" : (v>>12)&0x7F, "idx_1" : (v>>19)&0x3, "idx_2" : 0x0, "cmpY" : (v>>21)&0x7, "cmpX" : (v>>24)&0x7, "extid" : (v)&0x7F} def parseInstFormat1(k, v, lt={}): return {"opcode" : v>>26, "src2" : (v>>7)&0x1F, "src1" : (v>>12)&0x7F, "idx_1" : (v>>19)&0x3, "idx_2" : 0x0, "dst" : (v>>21)&0x1F, "extid" : (v)&0x7F} def parseInstFormat8(k, v, lt={}): return {"opcode" : v>>26, "src2" : (v>>7)&0x7F, "src1" : (v>>14)&0x1F, "idx_1" : 0x0, "idx_2" : (v>>19)&0x3, "dst" : (v>>21)&0x1F, "extid" : (v)&0x7F} def parseInstFormat9(k, v, lt={}): return {"opcode" : v>>26, "dst" : (v>>24)&0x1F, "src1" : (v>>17)&0x7F, "src2" : (v>>10)&0x7F, "src3" : (v>>5)&0x1F, "idx_1" : 0x0, "idx_2" : 0x0, "extid" : (v)&0x1F} def parseInstFormat12(k, v, lt={}): return {"opcode" : v>>26, "dst" : (v>>24)&0x1F, "src1" : (v>>17)&0x7F, "src2" : (v>>12)&0x1F, "src3" : (v>>5)&0x7F, "idx_1" : 0x0, "idx_2" : 0x0, "extid" : (v)&0x1F} def parseInstFormat2(k, v, lt={}): ret={"opcode" : v>>26, "addr" : (v>>8)&0x3FFC, "flags" : (v>>22)&0xF, "ret" : (v)&0x3FF} if ret["opcode"]==0x28: #IF? for i in range(k+4,ret["addr"],4): indentLine(i) for i in range(ret["addr"],ret["addr"]+ret["ret"]*4,4): indentLine(i) if ret["ret"]>0: lt[ret["addr"]]=(-1,"ELSE_%X"%(k)) return ret #? def parseInstFormat3(k, v, lt={}): return {"opcode" : v>>26, "src2" : (v>>0)&0x1F, "src1" : (v>>7)&0x7F, "dst" : (v>>14)&0x1F} #? def parseInstFormat6(k, v, lt={}): return {"opcode" : v>>26, "vtxid" : (v>>24)&0x3, "primid" : (v>>22)&0x3} # MOV? def parseInstFormat4(k, v, lt={}): return {"opcode" : v>>26, "src1" : (v>>7)&0x7F, "dst" : (v>>14)&0x1F, "extid" : (v)&0x3F} # CONDJUMP def parseInstFormat5(k, v, lt={}): ret={"opcode" : v>>26, "addr" : (v>>8)&0x3FFC, "bool" : (v>>22)&0xF, "ret" : (v)&0x3FF} if ret["opcode"]==0x27: #IFU for i in range(k+4,ret["addr"],4): indentLine(i) for i in range(ret["addr"],ret["addr"]+ret["ret"]*4,4): indentLine(i) if ret["ret"]>0: lt[ret["addr"]]=(-1,"ELSE_%X"%(k)) elif ret["opcode"]==0x29: #LOOP for i in range(k+4,ret["addr"]+4,4): indentLine(i) return ret def outputStringList(k, strl, fmtl): l=len(strl) if k in lineIndentLevel and lineIndentLevel[k]>0: out=" "*lineIndentLevel[k] else: out="" if l==len(fmtl): for i in range(l): str=strl[i] fmt=fmtl[i] if fmt: v=len(str) if v>26 if k in lt: iprint("%08x [--------] "%(k), True) unindentLine(k) outputStringList(k, [lt[k][1]+":"], [8]) indentLine(k) iprint("%08x [%08x] "%(k,v), True) if opcode in instList: fmt=instList[opcode]["format"] inst=fmtList[fmt][0](k, v, lt) fmtList[fmt][1](k, instList[opcode]["name"], inst, e, lt, vt, ut, ot) elif opcode==0x21: # outputStringList(k,["END"],[8]) outputStringList(k,["NOP"],[8]) elif opcode==0x22: outputStringList(k,["END"],[8]) elif opcode==0x2A: inst=parseInstFormat1(k, v) outputStringList(k,["EMITVERTEX"],[10]) else: inst=parseInstFormat1(k, v) if inst["extid"] < len(e): ext=e[inst["extid"]][0] extd=parseExt(ext) printInstFormat1(k, "???%02X"%(inst["opcode"]), inst, e, lt, vt, ut, ot) else: inst=parseInstFormat3(k, v) outputStringList(k,["???%02X"%(inst["opcode"]), getOutputSymbol(inst["dst"], ot), " <- ", getInputSymbol(inst["src1"], vt, ut, 0), " , ", getInputSymbol(inst["src2"], vt, ut, 0), "(invalid extension id)"], [8, 16, None, 16, None, 16, None]) k+=0x4 def parseDVLP(data, lt, vt, ut, ot, k): l=len(data) extOffset=getWord(data, 0x10) fnOffset=getWord(data, 0x18) # for i in range(fnOffset, l): # if k==0: # break # elif data[i]==0: # k-=1 # print(parseSymbol(data,i)) extSize=getWord(data, 0x14)*8 ext=parseExtTable(data[extOffset:(extOffset+extSize)]) codeOffset=getWord(data, 0x8) codeSize=getWord(data, 0xC)*4 parseCode(data[codeOffset:(codeOffset+codeSize)], ext, lt, vt, ut, ot) def parseLabelTable(data, sym): l=len(data) out={} for i in range(0,l,0x10): id=getWord(data,i,1) loc=getWord(data,i+0x4)*4 off=getWord(data,i+0xC) out[loc]=(id,parseSymbol(sym,off)) return out def transformRegisterValue(v): if v<16: return (v&0xF) return v+16 def parseVarTable(data, sym): l=len(data) iprint("Uniforms :") indentOut() src={} for i in range(0,l,0x8): off=getWord(data,i) v1=getWord(data,i+4,2) v2=getWord(data,i+6,2) base=transformRegisterValue(v1) end=transformRegisterValue(v2) # iprint(getRegisterNameSRC(base)+" - "+getRegisterNameSRC(end)+" : "+parseSymbol(sym,off)) iprint(getRegisterNameSRC(base)+" - "+getRegisterNameSRC(end)+" : "+parseSymbol(sym,off)+" ("+hex(getWord(data,i))+", "+hex(getWord(data,i+4))+")") if base==end: name=parseSymbol(sym,off) src[getRegisterNameSRC(base)]=name else: for k in range(base, end+1): name=parseSymbol(sym,off)+"["+str(k-base)+"]" src[getRegisterNameSRC(k)]=name unindentOut() print("") return src def parseConstTable(data, sym): l=len(data) iprint("Constants :") indentOut() out={} for i in range(0,l,0x14): type=getWord(data,i,2) r=getWord(data,i+2,2) name=None if type==0x0: #constant bool vec=False if getWord(data,i+4)==0x0 else True r+=0x88 name=str(vec) elif type==0x1: #constant integer vec4 vec=[hex(getWord(data,i+k,1)) for k in range(4,8,1)] r+=0x80 out[getRegisterNameSRC(r)+".x"]=vec[0] out[getRegisterNameSRC(r)+".y"]=vec[1] out[getRegisterNameSRC(r)+".z"]=vec[2] out[getRegisterNameSRC(r)+".w"]=vec[3] else: #constant float24 vec4 (should be type==0x2 but would rather output potential unknowns too) vec=[convFloat24(getWord(data,i+k)) for k in range(4,0x14,4)] r+=0x20 name="["+", ".join(["%4.2f"%(v) for v in vec])+"]" # iprint(getRegisterNameSRC(r)+" = "+str(vec)+" ("+str([hex(getWord(data,i+k)) for k in range(0,0x14,4)])+")") iprint(getRegisterNameSRC(r)+" = "+str(vec)) if name!=None: out[getRegisterNameSRC(r)]=name unindentOut() print("") return out outputTypes={0x0 : "result.position", 0x1 : "result.normalquat", #maybe 0x2 : "result.color", 0x3 : "result.texcoord0", 0x4 : "result.texcoord0w", 0x5 : "result.texcoord1", 0x6 : "result.texcoord2", # 0x7 : "?", #sets outreg info to 0x1f1f1f1f... 0x8 : "result.view", #"result.view" seems to be pre-projmatrix vertex coordinates } def parseOutputTable(data, sym): l=len(data) iprint("Output :") indentOut() out={} for i in range(0,l,0x8): off=getWord(data,i+4) v1=getWord(data,i,2) v2=getWord(data,i+2,2) dst=getRegisterNameDST(v2) if v1 in outputTypes: out[dst]=outputTypes[v1] iprint("o"+str(v2)+" = "+(outputTypes[v1] if v1 in outputTypes else hex(v1))+" ("+hex(off)+", "+hex(v1)+", "+hex(v2)+")") unindentOut() print("") return out def parseDVLE(data,dvlp, k): l=len(data) iprint("DVLE "+str(k)) shaderType=getWord(data, 0x6, 1) mainStart=getWord(data, 0x8)*4 mainEnd=getWord(data, 0xC)*4 resetIndentLevel() iprint("unkval "+hex(getWord(data, 0x4, 2))) iprint("vertex shader" if shaderType==0x0 else "geometry shader") iprint("main : "+hex(mainStart)+"-"+hex(mainEnd)) print("") # # temporarily filter out geometry shaders # if shaderType!=0x0: # return # # temporarily filter out vertex shaders # if shaderType==0x0: # return codeStartOffset=getWord(data, 0x8) codeEndOffset=getWord(data, 0xC) unifOffset=getWord(data, 0x18) unifSize=getWord(data, 0x1C)*0x14 labelOffset=getWord(data, 0x20) labelSize=getWord(data, 0x24)*0x10 outputOffset=getWord(data, 0x28) outputSize=getWord(data, 0x2C)*0x8 varOffset=getWord(data, 0x30) varSize=getWord(data, 0x34)*0x8 symbolOffset=getWord(data, 0x38) symbolSize=getWord(data, 0x3C) sym=data[symbolOffset:(symbolOffset+symbolSize)] labelTable=parseLabelTable(data[labelOffset:(labelOffset+labelSize)],sym) varTable=parseVarTable(data[varOffset:(varOffset+varSize)],sym) unifTable=parseConstTable(data[unifOffset:(unifOffset+unifSize)],sym) outputTable=parseOutputTable(data[outputOffset:(outputOffset+outputSize)],sym) parseDVLP(dvlp, labelTable, varTable, unifTable, outputTable, k) print("") return (labelTable,varTable,unifTable,range(codeStartOffset,codeEndOffset)) def parseDVLB(data): l=len(data) n=getWord(data, 0x4) dvleTable={} labelTable={} varTable={} unifTable={} dvlp=data[(0x8+0x4*n):l] for i in range(n): offset=getWord(data, 0x8+0x4*i) r=parseDVLE(data[offset:l],dvlp,i) # for k in r[3]: # dvleTable[k*4]=i # labelTable.update(r[0]) # varTable[i]=r[1] # unifTable[i]=r[2] # parseDVLP(dvlp,labelTable,varTable,unifTable,dvleTable) if len(sys.argv)<2: print("AEMSTRO :") print(" aemstro.py ") else: initIndent() src1fn=sys.argv[1] data=bytearray(open(src1fn, "rb").read()) l=len(data) for i in range(0,l-4,4): if getWord(data, i)==0x424C5644: parseDVLB(data[i:l])