mirror of
https://github.com/Awakenchan/LanternEngine.git
synced 2026-11-10 17:02:57 -10:00
93 lines
4.5 KiB
Python
93 lines
4.5 KiB
Python
#!/usr/bin/env python3
|
|
"""The game files bbport runs: Bloodborne CUSA03173 with the 1.09 update merged in.
|
|
|
|
Other versions start and then fail inside the game's code (the base game 1.00 faults at guest
|
|
offset 0x20348b8): hooks and patches use the addresses of this one executable. The check compares
|
|
the loaded executable image, whose hash is the same whatever tool dumped it (the SELF headers
|
|
around it differ). BB_SKIP_GAME_CHECK=1 skips it.
|
|
|
|
An image that differs from 1.09's in a few places only (a 1.09 eboot with byte patches, such as
|
|
the 60 FPS packages) starts with a warning: image_109_hashes.txt holds a hash of each 64 KiB of the
|
|
1.09 image, and every engine hook and patch checks its own bytes before it writes. More than
|
|
PATCHED_CHUNKS pieces differing is another version or a bad dump.
|
|
"""
|
|
import hashlib
|
|
import os
|
|
from pathlib import Path
|
|
|
|
SUPPORTED_TITLE = 'CUSA03173'
|
|
SUPPORTED_VERSION = '01.09'
|
|
SUPPORTED_IMAGE = '071df19c8880086d97182dbc057bc8cb37badaca57d9112683836b24a0444c0a'
|
|
CHUNK = 1 << 16
|
|
PATCHED_CHUNKS = 32
|
|
|
|
|
|
def differing_chunks(image):
|
|
"""How many 64 KiB pieces of `image` differ from the 1.09 image; None when its size differs or
|
|
the list is missing."""
|
|
try:
|
|
lines = (Path(__file__).with_name('image_109_hashes.txt')).read_text(encoding='utf-8').split()
|
|
except OSError:
|
|
return None
|
|
expected = [line for line in lines if len(line) == 16]
|
|
if (len(image) + CHUNK - 1) // CHUNK != len(expected):
|
|
return None
|
|
return sum(hashlib.sha256(image[i * CHUNK:(i + 1) * CHUNK]).hexdigest()[:16] != want
|
|
for i, want in enumerate(expected))
|
|
|
|
|
|
def image_sha256(game):
|
|
"""The SHA-256 of eboot.bin's loaded image (its loadable segments at their addresses)."""
|
|
from prepare import parse_self, span
|
|
elf, header, ph, _segments, _missing = parse_self((Path(game) / 'eboot.bin').read_bytes())
|
|
loads = [p for p in ph if p['type'] in (1, 0x61000010)]
|
|
image = bytearray(max(p['vaddr'] + p['memsz'] for p in loads))
|
|
for p in loads:
|
|
image[p['vaddr']:p['vaddr'] + p['filesz']] = span(elf, p['offset'], p['filesz'])
|
|
return hashlib.sha256(image).hexdigest()
|
|
|
|
|
|
def problem(game, image_hash=None, image=None):
|
|
"""None for the supported game, else (kind, title, version): kind is 'missing_update' (base
|
|
game or an older update), 'wrong_eboot' (param.sfo says 1.09, eboot.bin is another version),
|
|
'other_title' (another edition or region) or 'unreadable'."""
|
|
if os.environ.get('BB_SKIP_GAME_CHECK') == '1':
|
|
return None
|
|
from prepare import sfo
|
|
try:
|
|
info = sfo((Path(game) / 'sce_sys/param.sfo').read_bytes())
|
|
except (OSError, ValueError, IndexError):
|
|
info = {}
|
|
title, version = info.get('TITLE_ID', '?'), info.get('APP_VER', '?')
|
|
try:
|
|
if (image_hash or image_sha256(game)) == SUPPORTED_IMAGE:
|
|
return None
|
|
except (OSError, ValueError, IndexError, StopIteration, KeyError):
|
|
return 'unreadable', title, version
|
|
if image is not None and (n := differing_chunks(image)) is not None and n <= PATCHED_CHUNKS:
|
|
print(f'Game check: eboot.bin is 1.09 with {n} of {(len(image) + CHUNK - 1) // CHUNK} '
|
|
'64 KiB pieces changed (a patched eboot, e.g. a 60 FPS package); starting anyway. '
|
|
'If the game crashes, use the clean eboot.bin from the dumped 1.09 update.')
|
|
return None
|
|
if title != SUPPORTED_TITLE:
|
|
return 'other_title', title, version
|
|
if version != SUPPORTED_VERSION:
|
|
return 'missing_update', title, version
|
|
return 'wrong_eboot', title, version
|
|
|
|
|
|
def explain(kind, title, version):
|
|
"""What is wrong and what to do, for the log (English)."""
|
|
found = f'Found {title} version {version} (sce_sys/param.sfo).'
|
|
return {
|
|
'missing_update': f'{found} Lantern Engine needs the 1.09 update merged into the game folder: copy '
|
|
'everything from the dumped 1.09 update (e.g. CUSA03173-patch) into the '
|
|
'game folder, replacing files (eboot.bin and sce_sys too).',
|
|
'wrong_eboot': f'{found} param.sfo is from 1.09 but eboot.bin is not: copy eboot.bin from '
|
|
'the dumped 1.09 update into the game folder, replacing the old one.',
|
|
'other_title': f'{found} Only Bloodborne {SUPPORTED_TITLE} with update 1.09 is supported '
|
|
'for now; other editions and regions have a different executable.',
|
|
'unreadable': f'{found} eboot.bin could not be read as a decrypted PS4 executable: dump '
|
|
'the game and the 1.09 update again.',
|
|
}[kind]
|