Skip to content

Instantly share code, notes, and snippets.

@MrBrax
Created May 18, 2017 22:20
Show Gist options
  • Save MrBrax/1f3ae06c9320863f1d7b79b988c03e60 to your computer and use it in GitHub Desktop.
Save MrBrax/1f3ae06c9320863f1d7b79b988c03e60 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
from subprocess import call
import sys, os, getopt
import struct
import wave
import ntpath
import json
from PIL import Image, ImageDraw, ImagePalette
def convertBITD( w, h, f, start, size ):
bitmapValues = [[0 for x in range( w )] for y in range( h )]
draw_x = 0
draw_y = 0
f.seek(start)
while f.tell() <= start + size:
rLen = struct.unpack('>B', f.read(1) )[0]
if 0x101 - rLen > 0x7F:
#doLog(" lin (" + str(draw_x) + "," + str(draw_y) + " - len " + str(rLen) + ")" )
rLen += 1
for j in range(0, rLen):
#if f.tell() >= l['offset'] + l['length']:
# break
val = struct.unpack('>B', f.read(1) )[0]
#doLog(" lin - value (" + str( 0xFF - val ) + ")" )
#doLog(" lin - put pixel (" + str( draw_x ) + "," + str(draw_y) + "=" + str( 0xFF - val ) + ")")
bitmapValues[ draw_y ][ draw_x ] = 0xFF - val
draw_x += 1
if draw_x >= w:
#doLog(" lin - line change (" + str( draw_x-1 ) + "/" + str(draw_y+1) + "@" + str( f.tell() - start ) + ")")
if w % 2:
draw_x = -1
else:
draw_x = 0
draw_y += 1
if draw_y >= h:
#doLog(" lin - exceeded height (" + str( (start+size) - f.tell() ) + " bytes left)")
return bitmapValues
else:
rLen = 0x101 - rLen
val = struct.unpack('>B', f.read(1) )[0]
#doLog(" rle (" + str(draw_x) + "," + str(draw_y) + " - len " + str(rLen) + ")" )
#doLog(" rle - value (" + str( 0xFF - val ) + ")" )
for j in range(0, rLen):
#if f.tell() >= l['offset'] + l['length']:
# break
#doLog(" rle - put pixel (" + str( draw_x ) + "," + str(draw_y) + "=" + str( 0xFF - val ) + ")")
bitmapValues[ draw_y ][ draw_x ] = 0xFF - val
draw_x += 1
if draw_x >= w:
#doLog(" rle - line change (" + str( draw_x-1 ) + "/" + str(draw_y+1) + "@" + str( f.tell() - start ) + ")")
if w % 2:
draw_x = -1
else:
draw_x = 0
draw_y += 1
if draw_y >= h:
#doLog(" rle - exceeded height (" + str( (start+size) - f.tell() ) + " bytes left)")
return bitmapValues
return bitmapValues
fileNum = 1
entries = []
castList = []
metaList = {}
cfgInputCST = sys.argv[1]
cfgFileName = ntpath.basename(cfgInputCST)
# cfgCastNum = int(sys.argv[2]) - 1
logfile = open("cst_" + cfgFileName + ".log", "w")
def doLog(t):
global logfile
logfile.write(t + "\n")
print(t)
f = open(cfgInputCST, "rb")
# f = open(sys.argv[1], "rb")
outFolder = "cst_out/" + cfgFileName
if not os.path.exists(outFolder):
print("MAKE FOLDER")
os.makedirs(outFolder)
# pos 0-4 (XFIR)
RIFX_SIGN = f.read(4).decode("utf-8")
doLog( "RIFX_SIGN: " + str( RIFX_SIGN ) )
# pos 4-8 (Length)
SIZE = struct.unpack('i', f.read(4) )[0]
doLog( "SIZE: " + str( SIZE ) + " (" + str( round( SIZE / 1024 ) ) + "kb)" )
# pos 8-12
SIGN = f.read(4)
doLog( "SIGN: " + str( SIGN ) )
f.seek(60) # pos 60
rawFileNum = struct.unpack('i', f.read(4) )[0]
doLog( "File num: " + str( rawFileNum ) )
f.read(12) # pos 76, file block begin
doLog("\n\n--- READ POINTERS ---")
for i in range(0, rawFileNum):
pointerOffset = f.tell()
entryType = f.read(4).decode("utf-8")[::-1] # 4
entryLength = struct.unpack('i', f.read(4) )[0] # 8
entryOffset = struct.unpack('i', f.read(4) )[0] # 12
#if entryType != "free":
# doLog("[POINT " + str(i) + " @ " + str(pointerOffset) + "][" + (entryType) + "] Length: " + str( entryLength ) + ", Offset: " + str( entryOffset ) )
#else:
# doLog("[POINT " + str(i) + " @ " + str(pointerOffset) + "][----]")
entries.append({
'num': i,
'name': '',
'type': entryType,
'length': entryLength,
'offset': entryOffset,
'poffset': pointerOffset,
'files': [],
'friendlyType': ''
})
f.read(8)
doLog("\n\n--- READ FILES ---")
for e in entries:
if e['type'] == "free":
continue
f.seek( e['offset'], 0 )
fEntryHeaderRaw = f.read(4)
fEntryLengthRaw = f.read(4)
fEntryHeader = fEntryHeaderRaw.decode("utf-8")[::-1]
fEntryLength = struct.unpack('i', fEntryLengthRaw )[0]
e['headerRaw'] = fEntryHeaderRaw
e['lengthRaw'] = fEntryLengthRaw
# doLog("[FILE " + str(e['num']) + " @ " + str(e['offset']) + "][" + e['type'] + "->" + fEntryHeader + "]")
if e['type'] == 'KEY*':
doLog("--- KEY @ " + str( e['offset'] ) + " ---")
fUnknownNum1 = struct.unpack('i', f.read(4) )[0]
fUnknownNum2 = struct.unpack('i', f.read(4) )[0]
fEntryNum = struct.unpack('i', f.read(4) )[0]
# doLog(" fUnknownNum1: " + str( fUnknownNum1 ) + ", fUnknownNum2: " + str( fUnknownNum2 ) + ", fEntryNum: " + str( fEntryNum ) )
for i in range(0, fEntryNum):
castFileSlot = struct.unpack('i', f.read(4) )[0]
castSlot = struct.unpack('i', f.read(4) )[0]
castType = f.read(4).decode("utf-8")
# doLog("[KEY " + str(i) + "] Cast file slot offset: " + str( castFileSlot ) + ", Cast slot offset: " + str( castSlot ) + ", Type: " + str( castType ) )
# entries[ castSlot ]['slotNum'] = castSlot
# entries[ castSlot ]['fileSlot'] = castFileSlot
# entries[ castSlot ]['fileObj'] = entries[ castFileSlot ]
entries[ castSlot ]['files'].append( entries[ castFileSlot ] )
# doLog(" KeyCastOffset: " + str( castOffset ) + ", KeyCastId: " + str( castId ) + ", KeyCastType: " + str( castType ) )
if e['type'] == 'STXT':
f.read(4) # content
textLength = struct.unpack('>i', f.read(4) )[0]
textPadding = struct.unpack('>i', f.read(4) )[0]
# fPad = struct.unpack('i', f.read(1) )[0]
textContent = f.read( textLength )
e['content'] = textContent
if e['type'] == 'BITD':
e['content'] = f.read(fEntryLength)
if e['type'] == 'sndS':
e['content'] = f.read(fEntryLength)
if e['type'] == 'CASt':
# 3 - field
# 6 - audio
castType = struct.unpack('>i', f.read(4) )[0]
e['castType'] = castType
castDataLen = struct.unpack('>i', f.read(4) )[0]
e['dataLength'] = castDataLen
castDataEnd = struct.unpack('>i', f.read(4) )[0]
e['dataEnd'] = castDataEnd
if castType == 6:
e['friendlyType'] = 'sound'
castSub1 = struct.unpack('>i', f.read(4) )[0]
f.read(8)
castSub2 = struct.unpack('>i', f.read(4) )[0]
f.read( castSub1 - castSub2 )
castFields = struct.unpack('>h', f.read(2) )[0]
# doLog(" [INFO] Fields: " + str(castFields) )
for i in range(0, castFields):
f.read(4)
castInfoLen = struct.unpack('>i', f.read(4) )[0]
castInfoName = f.read( struct.unpack('b', f.read(1) )[0] ).decode('utf-8')
f.read(1)
castInfoCodec = f.read( struct.unpack('b', f.read(1) )[0] ).decode('utf-8')
e['name'] = castInfoName
e['codec'] = castInfoCodec
# doLog(" [INFO] Type: " + str(castType) + ", Length: " + str(castInfoLen) )
if castType == 1:
f.read(46)
castInfoName = f.read( struct.unpack('b', f.read(1) )[0] ).decode('ansi')
e['name'] = castInfoName
e['friendlyType'] = 'bitmap'
f.read(3)
e['paddingH'] = struct.unpack('>h', f.read(2) )[0]
e['paddingW'] = struct.unpack('>h', f.read(2) )[0]
e['height'] = struct.unpack('>h', f.read(2) )[0] - e['paddingH']
e['width'] = struct.unpack('>h', f.read(2) )[0] - e['paddingW']
e['constant'] = f.read(4) # struct.unpack('>i', f.read(4) )[0]
f.read(4)
e['regy'] = struct.unpack('>h', f.read(2) )[0] - e['paddingH']
e['regx'] = struct.unpack('>h', f.read(2) )[0] - e['paddingW']
e['bitdepth'] = struct.unpack('>h', f.read(2) )[0]
e['palette'] = struct.unpack('>h', f.read(2) )[0]
if castType == 3:
e['friendlyType'] = 'field'
f.read(70)
castInfoName = f.read( struct.unpack('b', f.read(1) )[0] ).decode('ansi')
e['name'] = castInfoName
if e['type'] == "CAS*":
for i in range(0, round(fEntryLength/4) ):
castSlot = struct.unpack('>i', f.read(4) )[0]
entries[ castSlot ]['memberNum'] = i + 1
castList.append( entries[ castSlot ] )
#metaList[ castSlot ] = {
#}
'''
for i in range(0, 64):
e = castList[i]
if 'fileObj' in e:
doLog( str(i) + ": " + e['fileObj']['type'] );
'''
#e = castList[ cfgCastNum ]
tmp = Image.open( "pal.bmp" )
mullePalette = tmp.palette
tmp.close()
for e in castList:
# doLog("[CAST " + str(e['num']) + "]")
if e['type'] != 'CASt':
continue
metaList[ e['memberNum'] ] = {}
doLog("[CAST " + str(e['memberNum']) + "]")
doLog(" [TYPE] " + str( e["castType"] ) + " (" + str( e["friendlyType"] ) + ")" )
metaList[ e['memberNum'] ]['castType'] = e['castType']
metaList[ e['memberNum'] ]['castTypeF'] = e['friendlyType']
if 'codec' in e:
doLog(" [CODEC] " + str( e["codec"] ) )
metaList[ e['memberNum'] ]['soundCodec'] = e['codec']
if 'width' in e:
doLog(" [SIZE] " + str( e["width"] ) + "x" + str( e["height"] ) )
metaList[ e['memberNum'] ]['imageWidth'] = e['width']
metaList[ e['memberNum'] ]['imageHeight'] = e['height']
if 'paddingW' in e:
doLog(" [PAD] " + str( e["paddingW"] ) + "x" + str( e["paddingH"] ) )
if 'regx' in e:
doLog(" [REG] " + str( e["regx"] ) + "," + str( e["regy"] ) )
metaList[ e['memberNum'] ]['imageRegX'] = e['regx']
metaList[ e['memberNum'] ]['imageRegY'] = e['regy']
if 'bitdepth' in e:
doLog(" [BITDEPTH] " + str( e["bitdepth"] ) )
if 'palette' in e:
doLog(" [PALETTE] " + str( e["palette"] ) )
doLog(" [SYS] POffset: " + str( e["poffset"] ) + ", Offset: " + str( e["offset"] ) + ", Length: " + str( e["length"] ) + ", Data length: " + str( e["dataLength"] ) + ", Data end: " + str( e["dataEnd"] ) )
if 'name' in e:
doLog(" [INFO] Name: " + str( e["name"] ) )
metaList[ e['memberNum'] ]['name'] = e['name']
for l in e['files']:
# doLog(" [INFO] Codec: " + str( castInfoCodec ) )
doLog(" [LINKED] Num: " + str(l["num"]) + ", Type: " + l['type'] + ", POffset: " + str( l['poffset'] ) + ", Offset: " + str( l['offset'] ) + ", Length: " + str( l['length'] ) )
if l['type'] == "sndS":
snd = wave.open( outFolder + "/" + str(e['memberNum']) + ".wav", "w")
snd.setnchannels(1)
snd.setsampwidth(1)
snd.setframerate(22050.0)
snd.writeframesraw( l['content'] )
snd.close()
if l['type'] == "BITD":
if e["width"] <= 0 or e["height"] <= 0:
continue
'''
bitm = open( outFolder + "/" + str(e['memberNum']) + ".bitd", "wb")
bitm.write( l['content'] )
bitm.close()
'''
im = Image.new("P", (e["width"], e["height"]) )
dr = ImageDraw.Draw(im)
tmp = Image.open( "pal.bmp" )
im.palette = tmp.palette
tmp.close()
bitmapValues = convertBITD( e['width'], e['height'], f, l['offset'] + 8, l['length'] )
draw_x = 0
draw_y = 0
doit = True
# doLog( str( colours ) )
x = 0
y = 0
# doLog( str(len(colours[0])) + ", " + str(len(colours[1])) + ", " + str(len(colours[2])) )
for y in range( 0, e['height'] ):
for x in range( 0, e['width'] ):
dr.point( (x, y), bitmapValues[y][x] )
im.save( outFolder + "/" + str(e['memberNum']) + ".bmp", "BMP")
call("magick convert " + outFolder + "/" + str(e['memberNum']) + ".bmp " + outFolder + "/" + str(e['memberNum']) + "O.png")
call("magick convert " + outFolder + "/" + str(e['memberNum']) + ".bmp -transparent \"#FFFFFF\" " + outFolder + "/" + str(e['memberNum']) + "T.png")
del dr
if l['type'] == "STXT":
txt = open( outFolder + "/" + str(e['memberNum']) + ".txt", "wb")
txt.write( l['content'] )
txt.close()
'''
cst = open( outFolder + "/" + str(e['memberNum']) + ".cast", "wb")
f.seek( e['offset'], 0 )
cst.write( f.read( e['length'] + 8 ) )
cst.close()
'''
if e["castType"] == 4:
doLog("PALETTE!!")
break
doLog("")
logfile.close()
f.close()
meta = open( outFolder + "/metadata.json", "w")
meta.write( json.dumps( metaList ) )
meta.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment