Skip to content

Instantly share code, notes, and snippets.

@infval
Created March 21, 2020 21:40
Show Gist options
  • Save infval/fc1b80efe16b034887f3c8e0e4b7f91c to your computer and use it in GitHub Desktop.
Save infval/fc1b80efe16b034887f3c8e0e4b7f91c to your computer and use it in GitHub Desktop.
[PS2] Psi-Ops MOVIES.MPK unpacker / packer (PlayStation 2, Psi-Ops: The Mindgate Conspiracy)
#!/usr/bin/env python3
__version__ = "1.0"
__author__ = "infval"
import argparse
from pathlib import Path
from struct import unpack, pack
def unpack_mpk(mpk_path, unpack_path):
with open(mpk_path, "rb") as f:
header = f.read(16)
table_offset = unpack("<IIII", header)[2]
f.seek(table_offset)
table_header = f.read(16)
t_size, _, _, t_count = unpack("<IIII", table_header)
print("{:32} | {:10} | {:10}".format("Filename", "Offset", "Size"))
print("-" * (32 + 10 + 10 + 6))
for i in range(t_count):
f.seek(table_offset + 16 * (i + 1))
video = f.read(16)
v_name_offset, _, v_offset, v_size = unpack("<IIII", video)
f.seek(table_offset + v_name_offset)
v_name = f.read(32)
s = v_name.find(b"\x00")
v_name = v_name[:s].decode("latin_1")
print("{:32} | {:10} | {:10}".format(v_name, v_offset, v_size))
f.seek(v_offset)
with open(unpack_path / v_name, "wb") as out:
out.write(f.read(v_size))
def replace_mpk(mpk_path, unpack_path, new_mpk_path):
files = []
with open(mpk_path, "rb") as f:
header = f.read(16)
table_offset = unpack("<IIII", header)[2]
f.seek(table_offset)
table_header = f.read(16)
t_size, _, _, t_count = unpack("<IIII", table_header)
for i in range(t_count):
f.seek(table_offset + 16 * (i + 1))
video = f.read(16)
v_name_offset, _, v_offset, v_size = unpack("<IIII", video)
f.seek(table_offset + v_name_offset)
v_name = f.read(32)
s = v_name.find(b"\x00")
v_name = v_name[:s].decode("latin_1")
files.append(v_name)
f.seek(0)
header = f.read(2048)
f.seek(table_offset)
table = f.read(4096)
header = bytearray(header)
table = bytearray(table)
offset = 0x800
for i, filename in enumerate(files):
path = unpack_path / filename
size = path.stat().st_size
elem_pos = (i + 1) * 16
table[elem_pos+8: elem_pos+16] = pack("<II", offset, size)
offset += size
header[8: 8+4] = pack("<I", offset)
with open(new_mpk_path, "wb") as out:
out.write(header)
for filename in files:
print(filename)
path = unpack_path / filename
out.write(path.read_bytes())
out.write(table)
def get_argparser():
parser = argparse.ArgumentParser(description='[PS2] Psi-Ops MOVIES.MPK (un)packer v' + __version__)
parser.add_argument('--version', action='version', version='%(prog)s ' + __version__)
parser.add_argument('-i', '--input', metavar='INPUT_MPK',
help='movies.mpk file to (un)pack (default: movies.mpk)')
parser.add_argument('-o', '--output', metavar='OUTPUT_SFD',
help='directory for SFD files (default: filename + _unpack)')
parser.add_argument('-r', '--replace', metavar='OUTPUT_MPK',
help='create mpk with SFD files from OUTPUT_SFD; based on INPUT_MPK')
parser.add_argument('-y', action='store_true', help='overwrite output files without asking')
return parser
def main_cli():
parser = get_argparser()
args = parser.parse_args()
input_path = Path("movies.mpk")
if args.input:
input_path = Path(args.input)
unpack_path = Path("{}_unpack".format(input_path))
if args.output:
unpack_path = Path(args.output)
if args.replace:
new_mpk_path = Path(args.replace)
if new_mpk_path.exists() and not args.y:
answer = input("File '{}' already exists. Overwrite ? [y/N]".format(new_mpk_path))
if answer.strip().lower() != 'y':
return
print("### Pack ###")
replace_mpk(input_path, unpack_path, new_mpk_path)
else:
if unpack_path.exists() and not args.y:
answer = input("Directory '{}' already exists. Overwrite ? [y/N]".format(unpack_path))
if answer.strip().lower() != 'y':
return
unpack_path.mkdir(exist_ok=True)
print("### Unpack ###")
unpack_mpk(input_path, unpack_path)
if __name__ == '__main__':
main_cli()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment