🌐 English · 日本語 · Français · Español · Português (BR)
Native 16:9 (480×272) widescreen patch for Street Fighter Alpha 3 MAX / Street Fighter Zero 3 Double Upper on PSP.
The game's internal display option must be set to "Normal" (un-stretched 4:3). This patch makes the engine render the background natively in 16:9: the viewport is opened to the full 480 px and the background tile layers are extended with real stage tiles on the left and right, kept centered — no stretching, no triple-render duplication.
| Stock (4:3, pillarbox) | Patched (native 16:9) |
|---|---|
![]() |
![]() |
| Version | 1.1 |
| Supported dumps | EU ULES-00235 (v1.01) · US ULUS-10062 · JP ULJM-05082 (v1.01, Zero 3 Double Upper) |
| Tested on | PPSSPP |
| Input | a decrypted EBOOT.BIN (ELF) |
| Method | pure-Python binary patcher, pattern-scan (no hard-coded offsets) |
- Viewport opener — render window 384 → 480 px, side pillarbox removed.
- Background widescreen — the three background tile drawers (16 px and 32 px layers) get extra columns + a left shift, so the background fills the whole 480 px centered.
- Painted-decor / object layers (temples, trees, foreground props, and the title/menu/character-select art — a separate render path the v1.0 patch never touched) are now extended to 480 px instead of being clipped at the old 4:3 bounds.
- Wrap-seam vertical-shift fix — v1.0 shifted the tile layers left to fill the widened side, but the wrap-around trigger wasn't adjusted, so the last column / scroll seam read one tile-row too low (a ~16 px downward shift). All three tile drawers now compensate the trigger, so seams line up exactly.
- Third tile drawer (
FUN_177c8) — the one tilemap routine v1.0 left in 4:3 now gets the full widescreen treatment (count + left shift + seam fix).
The character sprites, HUD and gameplay are untouched and stay centered.
At the extreme edges of a stage (where the tilemap simply contains no more tiles) a small margin can remain in those rare camera positions. This is a data limit of the original stage, not a code limit. Mid-stage the background is full 16:9.
python sfa3_ws_patternpatcher.py EBOOT.BIN EBOOT_WS.BIN
Then run EBOOT_WS.BIN (or repack it into the ISO — see below) and set the
game's internal display option to Normal.
The patcher refuses to write if anything looks wrong (missing/duplicate signature), so it never produces a half-patched file, and it recognises an already-patched EBOOT or ISO up front and leaves it alone.
Ready-made cheats are in cheats/, one per region. They apply the exact
same memory edits as the patcher, continuously at runtime, so you don't need to
decrypt or repack anything — just drop the file in and enable it.
- Copy
cheats/<DISC-ID>.iniintomemstick/PSP/Cheats/(e.g.ULES00235.ini). - PPSSPP: Settings → System → Enable cheats.
- Launch the game → Pause → Cheats → tick "16:9 Widescreen (native, v1.1)".
- Set the game's internal display option to Normal.
⚠️ Version-specific. Cheat addresses are fixed for the dumps listed above (EU/JP v1.01). A different revision shifts the addresses — in that case use the Python patcher instead, which locates everything by pattern and adapts to any revision. (Your dump's revision shows in PPSSPP's game info, or as_1.0xin save-state filenames.)
The same script takes the disc image and does the whole job:
python sfa3_ws_patternpatcher.py GAME.iso GAME_WS.iso
It walks the ISO9660 filesystem (no hard-coded LBA), finds
PSP_GAME/SYSDIR/EBOOT.BIN, patches it and writes a new ISO — then re-opens that
ISO and verifies it: the EBOOT reads back exactly as patched and every other
file is byte-identical (sha1 per file).
-
ISO holding a decrypted EBOOT (a "DECRYPTED" ISO, or one you repacked once already): patched in place — the patch never changes the file size, so not a single LBA moves.
-
Retail ISO (encrypted
~PSPEBOOT): nothing to decrypt either. A retail UMD keeps the unencrypted ELF right beside the signed one, asPSP_GAME/SYSDIR/BOOT.BIN— byte-for-byte the same program as PPSSPP's decrypted dump (verified on EU, US and JP). The script falls back to it automatically, patches it and writes it into theEBOOT.BINslot; both slots end up patched (--no-bootleavesBOOT.BINalone). On this game the patched ELF is smaller than the encrypted EBOOT, so it fits the existing extent and no LBA moves.For an ISO without a plain
BOOT.BIN, supply a decrypted EBOOT instead:python sfa3_ws_patternpatcher.py GAME.iso GAME_WS.iso --eboot ULES00235_EBOOT.BIN(or
--decrypter "<cmd>", called as<cmd> <in> <out>, for a PRXDecrypter-style tool). If the result then needs more room than the original, the image is resized the way UMDGen would: following sectors shifted, every directory-record LBA, both path tables and the volume space size fixed. -
CSO input is read directly (
GAME.cso→ patched.iso); use maxcso if you want a CSO back. ZSO/DAX are not supported. -
--listprints the ISO tree ·--dry-runwrites nothing · re-running on an already-patched ISO just says so.
Tested on retail and decrypted ISOs of all three regions (EU ULES-00235,
US ULUS-10062, JP ULJM-05082): in each case the patched EBOOT read back out of
the ISO carries exactly the 62 word edits of that region's cheat file, and every
other file on the disc is byte-identical.
A decrypted EBOOT inside an ISO runs fine in PPSSPP; real hardware/CFW still needs a re-signed EBOOT (
sign_np). Should an ISO carry no plainBOOT.BIN, the decryption step becomes unavoidable — UMDGen cannot do it either; it only performs the filesystem surgery, which is exactly what this script replaces.
The EBOOT inside a retail ISO is encrypted (~PSP / PSAR header). You must
decrypt it first. Re-encryption is not required for PPSSPP.
PPSSPP can dump the decrypted EBOOT for you:
- Settings → Tools → Developer tools → enable "Dump Decrypted EBOOT.BIN on game boot".
- Boot the game once.
- The decrypted file appears in
memstick/PSP/SYSTEM/DUMP/<DISC-ID>_EBOOT.BIN(e.g.ULES00235_EBOOT.BIN). Copy it out.
Alternatively use PRXDecrypter on a real/emulated PSP, which writes a decrypted
BOOT.BIN.
python sfa3_ws_patternpatcher.py ULES00235_EBOOT.BIN EBOOT_WS.BIN
- Open the original ISO in UMDGen.
- Navigate to
PSP_GAME/SYSDIR/EBOOT.BIN. - Right-click → Import / Replace file and select
EBOOT_WS.BIN.- PPSSPP runs a decrypted EBOOT placed in the ISO directly; no signing needed for emulator use.
- File → Save As a new ISO.
Real-hardware use needs a signed EBOOT (e.g.
sign_np) and CFW; this guide targets PPSSPP.
In-game: Options → Display → Normal (not "Wide"/stretched). The widescreen now comes from real rendered tiles, not stretching.
| File | Purpose |
|---|---|
sfa3_ws_patternpatcher.py |
the patcher — one file, takes an ISO/CSO or a decrypted EBOOT |
cheats/<DISC-ID>.ini |
ready-made PPSSPP cheats (per region, no repack needed) |
README.md |
this file — user guide |
TECHNICAL.md |
full reverse-engineering write-up: every patch explained |
LICENSE |
MIT (patcher code only); game © Capcom |
| Tool | Role | Where |
|---|---|---|
| Python 3 | runs the patcher (stdlib only — no pip install) |
python.org |
| PPSSPP | EBOOT decryption dump · GE/CPU debuggers used for the reverse-engineering | ppsspp.org |
| UMDGen | repack the patched EBOOT into the ISO — optional, sfa3_ws_patternpatcher.py does it |
(Windows ISO tool) |
| PRXDecrypter | alternative EBOOT decryption on real/CFW PSP | (PSP homebrew) |
| sign_np | re-sign EBOOT for real-hardware use (not needed for PPSSPP) | (PSP homebrew) |
Requirements: Python 3.8+. No third-party packages — the patcher imports only
os,sys,structfrom the standard library.
Reverse-engineered from the decrypted EBOOT via static MIPS disassembly and PPSSPP's GE/CPU debuggers. The patch is a set of in-place instruction edits plus two small code-caves placed in existing executable padding — no file size change.
Original game © Capcom.
Original patcher under MIT. Acknowledgments (PPSSPP for testing) and legal notes in CREDITS.md. No game/EBOOT is distributed — provide your own.

