Skip to content

Instantly share code, notes, and snippets.

@Nisto

Nisto/seq2psf.py Secret

Created September 27, 2018 00:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Nisto/431792c1b645525b9cc60c2fa1b4c532 to your computer and use it in GitHub Desktop.
Save Nisto/431792c1b645525b9cc60c2fa1b4c532 to your computer and use it in GitHub Desktop.
import os
import sys
import struct
import zlib
import glob
#
# CHANGE THESE VARIABLES AS NEEDED
#
DO_MINIPSF = 1
SEQ_ADDR = 0x80100000
VH_ADDR = 0x80120000
VB_ADDR = 0x80140000
PSF_COMMON_TAGS = [
"utf8=1",
"game=...",
"artist=...",
"genre=...",
"year=...",
"copyright=...",
"psfby=...",
]
#
# DO NOT CHANGE THESE VARIABLES
#
EXE_HEADER_SIZE = 0x800
def get_u32_le(buf, off=0):
return struct.unpack("<I", buf[off:off+4])[0]
def put_u32_le(buf, off, val):
buf[off:off+4] = struct.pack("<I", val)
def writemem(exebuf, addr, data):
text_start = get_u32_le(exebuf, 0x18)
offset = EXE_HEADER_SIZE + (addr - text_start)
exebuf[offset:offset+len(data)] = data
psf_header_t = struct.Struct("<4sIII")
def exe2psf(exebuf, tags = PSF_COMMON_TAGS):
zbuf = zlib.compress(exebuf, 9)
header = psf_header_t.pack(
b"PSF\x01", 0, len(zbuf), zlib.crc32(zbuf)
)
return header + zbuf + bytes(
"[TAG]%s" % "\n".join(tags), encoding="utf-8"
)
def main(argc=len(sys.argv), argv=sys.argv):
if argc != 2:
input("Usage: %s <input_dir>" % argv[0])
return 1
dir_in = os.path.realpath(argv[1])
if not os.path.isdir(dir_in):
input("ERROR: Directory path invalid")
return 1
drv_path = os.path.join(dir_in, "driver.exe")
if not os.path.isfile(drv_path):
input("ERROR: Driver not found!")
return 1
dir_out = os.path.join(dir_in, "out")
if not os.path.isdir(dir_out):
os.makedirs(dir_out)
with open(drv_path, "rb") as drv:
drvbuf = bytearray( drv.read() )
if DO_MINIPSF:
psflib_path = os.path.join(dir_out, "driver.psflib")
with open(psflib_path, "wb") as psflib:
psflib.write( exe2psf(drvbuf) )
vh_glob = os.path.join(dir_in, "*.vh")
for vh_path in glob.glob(vh_glob):
stem = os.path.splitext(vh_path)[0]
vb_path = "%s.vb" % stem
if not os.path.isfile(vb_path):
input("ERROR: VB not found for %s" % vh_path)
return 1
seq_paths = glob.glob("%s*.seq" % stem)
seq_count = len(seq_paths)
if seq_count < 1:
input("ERROR: SEQ not found for %s" % vh_path)
return 1
with open(vh_path, "rb") as vh:
vhbuf = vh.read()
with open(vb_path, "rb") as vb:
vbbuf = vb.read()
if DO_MINIPSF:
if seq_count == 1: # write minipsf for VH+VB+SEQ data
psf_tags = PSF_COMMON_TAGS[:]
psf_tags.append("_lib=driver.psflib")
for seq_path in seq_paths:
with open(seq_path, "rb") as seq:
seqbuf = seq.read()
text_start = min(VH_ADDR, VB_ADDR, SEQ_ADDR)
text_end = max( VH_ADDR + len(vhbuf), VB_ADDR + len(vbbuf), SEQ_ADDR + len(seqbuf) )
text_size = text_end - text_start
exebuf = bytearray(EXE_HEADER_SIZE + text_size)
exebuf[:EXE_HEADER_SIZE] = drvbuf[:EXE_HEADER_SIZE]
put_u32_le(exebuf, 0x18, text_start)
put_u32_le(exebuf, 0x1C, text_size)
writemem(exebuf, VH_ADDR, vhbuf)
writemem(exebuf, VB_ADDR, vbbuf)
writemem(exebuf, SEQ_ADDR, seqbuf)
seq_stem = os.path.splitext(seq_path)[0]
seq_name = os.path.basename(seq_stem)
psf_path = os.path.join(dir_out, "%s.minipsf" % seq_name)
with open(psf_path, "wb") as psf:
psf.write( exe2psf(exebuf, psf_tags) )
elif seq_count > 1: # write psflib for VH+VB data and minipsf for SEQ data
psflib_name = os.path.basename(stem)
psflib_path = os.path.join(dir_out, "%s.psflib" % psflib_name)
text_start = min(VH_ADDR, VB_ADDR)
text_end = max( VH_ADDR + len(vhbuf), VB_ADDR + len(vbbuf) )
text_size = text_end - text_start
exebuf = bytearray(EXE_HEADER_SIZE + text_size)
exebuf[:EXE_HEADER_SIZE] = drvbuf[:EXE_HEADER_SIZE]
put_u32_le(exebuf, 0x18, text_start)
put_u32_le(exebuf, 0x1C, text_size)
writemem(exebuf, VH_ADDR, vhbuf)
writemem(exebuf, VB_ADDR, vbbuf)
with open(psflib_path, "wb") as psflib:
psflib.write( exe2psf(exebuf) )
psf_tags = PSF_COMMON_TAGS[:]
psf_tags.append("_lib=driver.psflib")
psf_tags.append("_lib2=%s.psflib" % psflib_name)
for seq_path in seq_paths:
with open(seq_path, "rb") as seq:
seqbuf = seq.read()
text_start = SEQ_ADDR
text_end = SEQ_ADDR + len(seqbuf)
text_size = len(seqbuf)
exebuf = bytearray(EXE_HEADER_SIZE + text_size)
exebuf[:EXE_HEADER_SIZE] = drvbuf[:EXE_HEADER_SIZE]
put_u32_le(exebuf, 0x18, text_start)
put_u32_le(exebuf, 0x1C, text_size)
writemem(exebuf, SEQ_ADDR, seqbuf)
seq_stem = os.path.splitext(seq_path)[0]
seq_name = os.path.basename(seq_stem)
psf_path = os.path.join(dir_out, "%s.minipsf" % seq_name)
with open(psf_path, "wb") as psf:
psf.write( exe2psf(exebuf, psf_tags) )
else:
tplbuf = drvbuf[:]
writemem(tplbuf, VH_ADDR, vhbuf)
writemem(tplbuf, VB_ADDR, vbbuf)
for seq_path in seq_paths:
exebuf = tplbuf[:]
with open(seq_path, "rb") as seq:
seqbuf = seq.read()
writemem(exebuf, SEQ_ADDR, seqbuf)
seq_stem = os.path.splitext(seq_path)[0]
seq_name = os.path.basename(seq_stem)
psf_path = os.path.join(dir_out, "%s.psf" % seq_name)
with open(psf_path, "wb") as psf:
psf.write( exe2psf(exebuf) )
return 0
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment