Last active
July 10, 2023 05:50
-
-
Save smealum/8807124 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import struct | |
import math | |
import os | |
import sys | |
from PIL import Image | |
from subprocess import call | |
if sys.version > '3': | |
buffer = memoryview | |
def getWord(b, k, n=4): | |
return sum(list(map(lambda c: b[k+c]<<(c*8),range(n)))) | |
totalVcount=1 | |
currentVcount=0 | |
totalNcount=1 | |
currentNcount=0 | |
hasNormals=False | |
totalTCcount=1 | |
currentTCcount=0 | |
def parseVertices(b, o, s, f): | |
global hasNormals | |
global currentVcount, currentNcount, currentTCcount | |
if f==0x0C: | |
#for 3-word vertex descriptors | |
for i in range(int(s/f)): | |
arr=[struct.unpack('f',buffer(srcdata[(o+k*4):(o+(k+1)*4)]))[0] for k in range(3)] | |
print("v "+str(arr[0])+" "+str(arr[1])+" "+str(arr[2])) | |
currentVcount=currentVcount+1 | |
o=o+f | |
if f==0x14: | |
#for 5-word vertex descriptors | |
for i in range(int(s/f)): | |
arr=[struct.unpack('f',buffer(srcdata[(o+k*4):(o+(k+1)*4)]))[0] for k in range(5)] | |
print("v "+str(arr[0])+" "+str(arr[1])+" "+str(arr[2])) | |
print("vt "+str(arr[3])+" "+str(arr[4])) | |
currentVcount=currentVcount+1 | |
currentTCcount=currentTCcount+1 | |
o=o+f | |
elif f==0x18: | |
#for 6-word vertex descriptors | |
for i in range(int(s/f)): | |
arr=[struct.unpack('f',buffer(srcdata[(o+k*4):(o+(k+1)*4)]))[0] for k in range(6)] | |
print("v "+str(arr[0])+" "+str(arr[1])+" "+str(arr[2])) | |
print("vt "+str(arr[4])+" "+str(arr[5])) | |
currentVcount=currentVcount+1 | |
currentTCcount=currentTCcount+1 | |
o=o+f | |
elif f==0x20 or f==0x24 or f==0x28: | |
#for 8-word vertex descriptors | |
#for 9-word vertex descriptors | |
#for 10-word vertex descriptors | |
for i in range(int(s/f)): | |
arr=[struct.unpack('f',buffer(srcdata[(o+k*4):(o+(k+1)*4)]))[0] for k in range(8)] | |
print("v "+str(arr[0])+" "+str(arr[1])+" "+str(arr[2])) | |
print("vn "+str(arr[3])+" "+str(arr[4])+" "+str(arr[5])) | |
print("vt "+str(arr[6])+" "+str(arr[7])) | |
hasNormals=True | |
currentVcount=currentVcount+1 | |
currentNcount=currentNcount+1 | |
currentTCcount=currentTCcount+1 | |
o=o+f | |
def parseFaces(b, o, s, f): | |
global hasNormals | |
global totalVcount, totalNcount, totalTCcount | |
global currentVcount, currentNcount, currentTCcount | |
if f==1: | |
#for u8 face descriptors | |
for i in range(int(s/(3))): | |
arr=[struct.unpack('B',buffer(srcdata[(o+k):(o+(k+1))]))[0] for k in range(8)] | |
if hasNormals: | |
print("f "+str(arr[0]+totalVcount)+"/"+str(arr[0]+totalTCcount)+"/"+str(arr[0]+totalNcount)+" "+ | |
str(arr[1]+totalVcount)+"/"+str(arr[1]+totalTCcount)+"/"+str(arr[1]+totalNcount)+" "+ | |
str(arr[2]+totalVcount)+"/"+str(arr[2]+totalTCcount)+"/"+str(arr[2]+totalNcount)) | |
else: | |
print("f "+str(arr[0]+totalVcount)+" "+str(arr[1]+totalVcount)+" "+str(arr[2]+totalVcount)) | |
o=o+3 | |
elif f==3: | |
#for u16 face descriptors | |
for i in range(int(s/(3*2))): | |
arr=[struct.unpack('H',buffer(srcdata[(o+k*2):(o+(k+1)*2)]))[0] for k in range(8)] | |
if hasNormals: | |
print("f "+str(arr[0]+totalVcount)+"/"+str(arr[0]+totalTCcount)+"/"+str(arr[0]+totalNcount)+" "+ | |
str(arr[1]+totalVcount)+"/"+str(arr[1]+totalTCcount)+"/"+str(arr[1]+totalNcount)+" "+ | |
str(arr[2]+totalVcount)+"/"+str(arr[2]+totalTCcount)+"/"+str(arr[2]+totalNcount)) | |
else: | |
print("f "+str(arr[0]+totalVcount)+" "+str(arr[1]+totalVcount)+" "+str(arr[2]+totalVcount)) | |
o=o+6 | |
totalVcount=totalVcount+currentVcount | |
totalNcount=totalNcount+currentNcount | |
totalTCcount=totalTCcount+currentTCcount | |
currentVcount=0 | |
currentNcount=0 | |
currentTCcount=0 | |
def parseSOBJ(b, o): | |
global hasNormals | |
dataStructOffset=getWord(b, o+0x44) | |
print("# SOBJ "+hex(o)) | |
hasNormals=False | |
if(dataStructOffset!=0): | |
dataStructOffset=o+dataStructOffset | |
faceDataFormat=getWord(b,dataStructOffset+0x18,1) | |
faceDataSize=getWord(b,dataStructOffset+0x20) | |
faceDataOffset=dataStructOffset+0x24+getWord(b,dataStructOffset+0x24) | |
vertexDataFormat=getWord(b,dataStructOffset+0x68,1) | |
vertexDataSize=getWord(b,dataStructOffset+0x58) | |
vertexDataOffset=dataStructOffset+0x5C+getWord(b,dataStructOffset+0x5C) | |
print("# vertex format "+hex(vertexDataFormat)) | |
parseVertices(b, vertexDataOffset, vertexDataSize, vertexDataFormat) | |
print("g SOBJ_"+hex(o)) | |
parseFaces(b, faceDataOffset, faceDataSize, faceDataFormat) | |
#returns RGBA tuple (with added pixel size) | |
#format ID only confirmed for 0, 4, 5 and 7 ! | |
def parsePixel(b, o, f): | |
if f==0: | |
#RGBA8 | |
pixel=getWord(b, o, 1) | |
return ((getWord(b, o+3, 1), getWord(b, o+2, 1), getWord(b, o+1, 1), getWord(b, o, 1)), 4) | |
elif f==1: | |
#RGB8 | |
pixel=getWord(b, o, 1) | |
return ((getWord(b, o, 1), getWord(b, o+1, 1), getWord(b, o+2, 1), 255), 3) | |
elif f==2: | |
#RGBA5551 | |
pixel=getWord(b, o, 2) | |
return ((((pixel>>11)&0x1F)*8, ((pixel>>6)&0x1F)*8, ((pixel>>1)&0x1F)*8, (255 if pixel&0x1==1 else 0)), 2) | |
elif f==3: | |
#RGB565 | |
pixel=getWord(b, o, 2) | |
return ((((pixel>>11)&0x1F)*8, ((pixel>>5)&0x3F)*4, ((pixel)&0x1F)*8, 255), 2) | |
elif f==4: | |
#RGBA4 | |
pixel=getWord(b, o, 2) | |
return ((((pixel>>12)&0xF)*16, ((pixel>>8)&0xF)*16, ((pixel>>4)&0xF)*16, (pixel&0xF)*16), 2) | |
elif f==5: | |
#LA8 | |
pixel=getWord(b, o+1, 1) | |
return ((pixel, pixel, pixel, getWord(b, o, 1)),2) | |
elif f==7: | |
#L8 | |
pixel=getWord(b, o, 1) | |
return ((pixel, pixel, pixel, 255),1) | |
# elif f==0xA: | |
# #? | |
# return ((0,0,0,0), 1) | |
# elif f==0xC: | |
# #? | |
# return ((0,0,0,0), 1) | |
elif f==0xD: | |
#A4 ? | |
return ((0,0,0,0), 1) | |
else: | |
print("# unknown format : "+hex(f)) | |
return ((0,0,0,0), 1) | |
tileOrder=[0,1,8,9,2,3,10,11,16,17,24,25,18,19,26,27,4,5,12,13,6,7,14,15,20,21,28,29,22,23,30,31,32,33,40,41,34,35,42,43,48,49,56,57,50,51,58,59,36,37,44,45,38,39,46,47,52,53,60,61,54,55,62,63] | |
def parseTile(im, b, o, f, x, y): | |
global tileOrder | |
for k in range(8*8): | |
i=tileOrder[k]%8 | |
j=int((tileOrder[k]-i)/8) | |
pixel=parsePixel(b,o,f) | |
im.putpixel((x+i,y+j), pixel[0]) | |
o=o+pixel[1] | |
return o | |
def parseTexture(b, o, s, w, h, f): | |
im = Image.new("RGB", (w,h)) | |
dstname="tex_"+hex(o)+".png" | |
if f==0xB or f==0xC: | |
#ETC1; used external etc program (see https://gist.github.com/smealum/8897237 ) | |
open("tmp_etc","wb").write(b[o:(o+s)]) | |
#not super clean | |
call(["etc.exe", "tmp_etc", str(w), str(h)]) | |
imgData=bytearray(open("tmp_etc.data","rb").read()) | |
for j in range(0,h): | |
for i in range(0,w): | |
k=(i+j*w)*4 | |
im.putpixel((i,j), (imgData[k],imgData[k+1],imgData[k+2],imgData[k+3])) | |
else: | |
for j in range(0,h,8): | |
for i in range(0,w,8): | |
o=parseTile(im, b, o, f, i, j) | |
im.save(dstname) | |
def parseTXOB(b, o): | |
textureDataWidth=getWord(b,o+0x18) | |
textureDataHeight=getWord(b,o+0x14) | |
if textureDataWidth!=0 and textureDataHeight!=0: | |
textureDataFormat=getWord(b,o+0x30,1) | |
textureDataSize=getWord(b,o+0x40) | |
textureDataOffset=o+0x44+getWord(b,o+0x44) | |
#o=o+0x20 | |
#print(hex(textureDataOffset)+" : "+hex(getWord(b,o))+" "+hex(getWord(b,o+4*1))+" "+hex(getWord(b,o+4*2))+" "+hex(getWord(b,o+4*3))+" "+hex(getWord(b,o+4*4))+" "+hex(getWord(b,o+4*5))+" "+hex(getWord(b,o+4*6))+" "+hex(getWord(b,o+4*7))+" "+ | |
# hex(getWord(b,o+4*8))+" "+hex(getWord(b,o+4*9))+" "+hex(getWord(b,o+4*10))+" ") | |
parseTexture(b, textureDataOffset, textureDataSize, textureDataWidth, textureDataHeight, textureDataFormat) | |
srcfn=sys.argv[1] | |
srcdata=bytearray(open(srcfn, 'rb').read()) | |
for i in range(int(len(srcdata)/4)): | |
#scan for SOBJ | |
if getWord(srcdata, i*4)==0x4A424F53: | |
parseSOBJ(srcdata,i*4) | |
# #scan for TXOB | |
if getWord(srcdata, i*4)==0x424F5854: | |
parseTXOB(srcdata,i*4) | |
@jabberwockxeno : just run it on a cgfx file.
@pakoito : i didn't find it anywhere, I REd it.
@smealum: I'm assuming that's the native format that the models are in? Obviously, that requires us to extract them. How would we do that?
how to use it ??
@smealum This script generates broken obj files. Can you fix it or give something to repair the files once done?
True necroposting here
yeah 😆
but I have no choice
I need a commandline tool to extract cgfx files
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Some cool stuff you have here. Where did you find the cgfx file format specification?