Skip to content

Instantly share code, notes, and snippets.

@YurgenJurgensen
Created November 25, 2021 23:43
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 YurgenJurgensen/eb32739bf6bd70af691d0c1731f64224 to your computer and use it in GitHub Desktop.
Save YurgenJurgensen/eb32739bf6bd70af691d0c1731f64224 to your computer and use it in GitHub Desktop.
Quick-and-dirty PSP UMD SFO extractor
from typing import List, Dict, Optional
import string, struct, sys, json, os
def lereadu32(src: bytes, off: int) -> int:
"""
Read from supplied bytes object as a little-endian unsigned 32-bit integer and return the result
"""
return struct.unpack("<I", src[off:off+4])[0]
def lereadu16(src: bytes, off: int) -> int:
"""
Read from supplied bytes object as a little-endian unsigned 16-bit integer and return the result
"""
return struct.unpack("<H", src[off:off+2])[0]
def readcstr(src: bytes, off: int, max_len: Optional[int]=None) -> str:
"""
Read from supplied bytes object as a null-terminated string and return the result
"""
if max_len is not None:
slc = src[off:off+max_len]
else:
slc = src[off:]
for n, b in enumerate(slc):
if b == 0:
slc = slc[:n]
return slc.decode('utf8')
def readfixedstr(src: bytes, off: int, len: int) -> str:
"""
Read from supplied bytes object as a fixed-length string and return the result
"""
return src[off:off+len].decode('utf8')
def main():
if len(sys.argv) != 2:
print("Usage: %s: UMD_root_path")
sys.exit(1)
umd_root = sys.argv[1]
try:
with open(os.path.join(umd_root, "umd_data.bin"), 'rb') as f:
umd_data = f.read()
except FileNotFoundError:
print("Couldn't open umd_data.bin.")
sys.exit(1)
umd_name = ''
n = 0
for x in umd_data:
if x == b'|'[0]:
break
n += 1
else:
print("Error parsing umd_data.bin")
print(umd_data)
sys.exit(1)
umd_name = umd_data[:n].decode('utf8')
with open("sfoinfo.txt", "w"): pass
for root, dir, files in os.walk(umd_root):
for candidate in files:
if os.path.splitext(candidate)[1] != ".sfo":
continue
path = os.path.join(root, candidate)
print("Opening sfo file:", path)
with open(path, 'rb') as f:
sfodata = f.read()
if lereadu32(sfodata, 0) != 0x46535000:
print("Bad magic: 0x%08x, 0x%08x" % (lereadu32(sfodata, 0), 0x46535000))
sys.exit(1)
version = "%d.%02d" % (sfodata[0x04], sfodata[0x05])
key_table_start = lereadu32(sfodata, 0x08)
data_table_start = lereadu32(sfodata, 0x0C)
table_entries = lereadu32(sfodata, 0x10)
print("Read header: Version: %s, Key Off: 0x%x Data off: 0x%x Entries: %d" % (version, key_table_start, data_table_start, table_entries))
keys = [] # type: List[str]
data = []
indices = [] # type: List[Dict]
for i in range(table_entries):
entry_base = 0x14 + (0x10 * i)
indices.append({
"key_off": lereadu16(sfodata, entry_base),
"data_fmt": (sfodata[entry_base + 0x02], sfodata[entry_base + 0x03]),
"data_len": lereadu32(sfodata, entry_base + 0x04),
"data_max_len": lereadu32(sfodata, entry_base + 0x08),
"data_off": lereadu32(sfodata, entry_base + 0x0c)
})
print("Read index %d: %s" % (i, indices[i]))
keys.append(readcstr(sfodata, indices[i]["key_off"] + key_table_start))
if indices[i]["data_fmt"][1] == 0x00:
data.append(readfixedstr(sfodata, indices[i]["data_off"] + data_table_start, indices[i]["data_len"]))
if indices[i]["data_fmt"][1] == 0x02:
data.append(readcstr(sfodata, indices[i]["data_off"] + data_table_start, indices[i]["data_max_len"]))
elif indices[i]["data_fmt"][1] == 0x04:
data.append(str(lereadu32(sfodata, indices[i]["data_off"] + data_table_start)))
else:
raise ValueError("Bad format for index %d: %02x%02x" % (i, indices[i]["data_fmt"][0], indices[i]["data_fmt"][1]))
print("Read data object - %s: %s" % (keys[i], data[i]))
with open("sfoinfo.txt", 'a') as f:
print("SFO file: %s" % (os.path.join(umd_name, os.path.relpath(path, umd_root))), file=f)
print("SFO Version: %s" % (version), file=f)
for i in range(table_entries):
print("%s: %s" % (keys[i], data[i]), file=f)
f.write("\n")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment