mirror of
https://github.com/s0liton/ps5-romm.git
synced 2026-10-06 09:00:34 +02:00
100 lines
3.2 KiB
Python
Executable File
100 lines
3.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Tiny disc images and PARAM.SFO files for tests.
|
|
|
|
make_disc.py psp <out.iso> <DISC_ID>
|
|
make_disc.py sfo <out.sfo> KEY=VALUE ...
|
|
make_disc.py cso <out.cso> <DISC_ID>
|
|
make_disc.py gc <out.iso> <GAMEID>
|
|
"""
|
|
import struct
|
|
import sys
|
|
|
|
SECTOR = 2048
|
|
|
|
|
|
def sfo(values):
|
|
keys = list(values)
|
|
key_table = b""
|
|
key_offsets = []
|
|
for k in keys:
|
|
key_offsets.append(len(key_table))
|
|
key_table += k.encode() + b"\0"
|
|
key_table += b"\0" * (-len(key_table) % 4)
|
|
data = b""
|
|
index = b""
|
|
for i, k in enumerate(keys):
|
|
v = values[k].encode() + b"\0"
|
|
size = len(v) + (-len(v) % 4)
|
|
index += struct.pack("<HHIII", key_offsets[i], 0x0204, len(v), size, len(data))
|
|
data += v + b"\0" * (size - len(v))
|
|
key_start = 20 + len(index)
|
|
data_start = key_start + len(key_table)
|
|
return b"\0PSF" + struct.pack("<IIII", 0x0101, key_start, data_start, len(keys)) + index + key_table + data
|
|
|
|
|
|
def both16(v):
|
|
return struct.pack("<H", v) + struct.pack(">H", v)
|
|
|
|
|
|
def both32(v):
|
|
return struct.pack("<I", v) + struct.pack(">I", v)
|
|
|
|
|
|
def record(name, lba, size, is_dir):
|
|
rec = bytes([0, 0]) + both32(lba) + both32(size) + bytes(7) + bytes([2 if is_dir else 0, 0, 0]) + both16(1)
|
|
rec += bytes([len(name)]) + name
|
|
if len(rec) % 2:
|
|
rec += b"\0"
|
|
return bytes([len(rec)]) + rec[1:]
|
|
|
|
|
|
def psp_iso(disc_id):
|
|
param = sfo({"DISC_ID": disc_id, "TITLE": "Test Game"})
|
|
img = bytearray(SECTOR * 21)
|
|
pvd = bytearray(SECTOR)
|
|
pvd[0] = 1
|
|
pvd[1:6] = b"CD001"
|
|
pvd[6] = 1
|
|
pvd[156:156 + 34] = record(b"\0", 18, SECTOR, True)
|
|
img[16 * SECTOR:17 * SECTOR] = pvd
|
|
img[17 * SECTOR:17 * SECTOR + 7] = b"\xffCD001\x01"
|
|
root = record(b"\0", 18, SECTOR, True) + record(b"\1", 18, SECTOR, True) + record(b"PSP_GAME", 19, SECTOR, True)
|
|
img[18 * SECTOR:18 * SECTOR + len(root)] = root
|
|
game = record(b"\0", 19, SECTOR, True) + record(b"\1", 18, SECTOR, True) + record(b"PARAM.SFO;1", 20, len(param), False)
|
|
img[19 * SECTOR:19 * SECTOR + len(game)] = game
|
|
img[20 * SECTOR:20 * SECTOR + len(param)] = param
|
|
return bytes(img)
|
|
|
|
|
|
def cso(iso, block=2048):
|
|
import zlib
|
|
blocks = [iso[i:i + block] for i in range(0, len(iso), block)]
|
|
header = b"CISO" + struct.pack("<IQIBBH", 24, len(iso), block, 1, 0, 0)
|
|
body, index = b"", []
|
|
offset = len(header) + 4 * (len(blocks) + 1)
|
|
for i, b in enumerate(blocks):
|
|
index.append(offset + len(body))
|
|
c = zlib.compressobj(9, zlib.DEFLATED, -15)
|
|
z = c.compress(b) + c.flush()
|
|
if i % 2 or len(z) >= len(b): # store some blocks uncompressed
|
|
index[-1] |= 0x80000000
|
|
z = b
|
|
body += z
|
|
index.append(offset + len(body))
|
|
return header + b"".join(struct.pack("<I", x) for x in index) + body
|
|
|
|
|
|
if __name__ == "__main__":
|
|
kind, out = sys.argv[1], sys.argv[2]
|
|
if kind == "psp":
|
|
data = psp_iso(sys.argv[3])
|
|
elif kind == "cso":
|
|
data = cso(psp_iso(sys.argv[3]))
|
|
elif kind == "sfo":
|
|
data = sfo(dict(a.split("=", 1) for a in sys.argv[3:]))
|
|
elif kind == "gc":
|
|
data = sys.argv[3].encode()[:6] + bytes(0x20) + b"\xc2\x33\x9f\x3d" + bytes(64)
|
|
else:
|
|
sys.exit("unknown kind")
|
|
open(out, "wb").write(data)
|