UFC 3 — native PS4 port for Linux and Windows

UFC 3 is a native PC port of EA Sports UFC 3 for PlayStation 4 that runs on Linux and Windows with Vulkan.

README

UFC 3 — native PS4 port for Linux and Windows

Listed on PlayPorted Support me on Ko-fi

EA SPORTS UFC 3 (PlayStation 4, CUSA06534, update 1.14) running as a native x86-64 program on Linux and Windows. This is not a CPU emulator: the game's original code runs directly on the PC's processor, a dedicated runtime replaces the PS4 system libraries, and a renderer derived from shadPS4 translates the PS4's GPU work to Vulkan.

It is built on SacredFire (Crash Team Racing: Nitro-Fueled), which itself comes from bbport, the native Bloodborne port. All code is GPL-2.0-or-later.

No game files are included. You need your own dump of UFC 3 (CUSA06534) and its 1.14 update. This project is not affiliated with or endorsed by Electronic Arts, Sony Interactive Entertainment or the UFC. EA SPORTS UFC, PlayStation and related marks belong to their owners and are named only to identify the game.


Status

Current release: 0.1.9 (notes). What's new since 0.1.6:

  • 0.1.9: the real fix for the slow Career menu after the 2nd belt (PR #41 by @nomadsdad), a clean exit on Windows (PR #42), and a version check in every log (VERSION MISMATCH when an install mixes versions).
  • 0.1.8:
    • a controller can disconnect and reconnect without crashing the game;
    • blood and sweat are fixed with BB_WRITE_WINDOW=2;
    • old cached textures are freed, so FPS should no longer go down over long sessions.
  • 0.1.7: two Career crashes are fixed (entering Career or loading a save after a title fight, and moving the Create Fighter sliders), and the launcher explains when the CPU is too old.

Playable. Menus, loading, fights (including the menu's attract-mode fight), sound, controller and saves. What works today:

  • Career: it saves, and saves made after a title fight load again. It no longer crashes on entry or when you move the Create Fighter sliders or change presets. Changing hair/head repeatedly in the fighter editor no longer hangs the GPU, and the menu stays fast after you win belts.
  • Blood and sweat on hits, with the created fighter's skin still correct.
  • PS4 Pro mode (the game renders 1080p instead of 900p), with a checkbox in the Windows launcher (BB_NEO=1 on Linux).
  • 2K / 4K (experimental): 2560x1440 and 3840x2160 internal resolution in the launcher (validated on Linux only; see Launcher settings).
  • 90 FPS in the launcher's FPS limit, next to 60 / 120 / 144 / 240 / Unlimited.
  • Hair and beards with proper coverage instead of square cards.
  • Created-fighter skin that survives repeated editing (no black or garbled patches).
  • Shadows without acne or flicker from the emulated depth format.
  • Career portraits and the post-fight screen: your fighter shows up on the post-fight menu, in the career pause menu and on the Saved Careers cards.
  • Game language selector in the Windows launcher.
  • Launchers on both systems (Windows: UFC3.exe; Linux: a GTK launcher), with Offline / Join server / Host server.
  • Online between friends (private hosting, rematch included); see Online.
SceneLinux · Ryzen 5 5600 + RTX 4070Windows · Omen 16 (RTX 3060 Laptop, 6 GB)
Menus / loading screens240 FPS (the game's own cap)240 FPS (Immediate present mode)
Career fight, no FPS limit—60–70 FPS (0.1.5, write windows on, log folder on a local disk)
Fight, no FPS limit137–196 FPS (write windows on)66–83 FPS (0.1.4, a Quick Fight without write windows)
Fight, 120 FPS limitsteady 120 FPS—
  • Tested by the maintainer on NVIDIA (Linux and Windows); AMD fixes come from @luscamovi's RX 9070, and players have reported on other AMD and NVIDIA cards (RX 7800 XT, RTX 4090…).
  • Write windows (a faster CPU/GPU memory path, see Performance) are on on both Linux and Windows since 0.1.5: UFC-48 fixed the Windows heap corruption of 0.1.4.

Known issues

  • Fights on Windows still run below Linux.
  • Keep the log folder on a local disk for the best FPS.
  • "Clear the shader cache at start" recompiles every shader on each start: leave it off.
  • The 0.1.8 fixes (Career menu FPS, controller reconnect, blood/sweat in mode 2, long sessions) were checked with tests and code review, not yet by many players: if one does not fix your case, please reopen the issue with the new log.
  • The 2K/4K internal resolutions are still experimental. If the game crashes with one of them, try again without it before you report the crash.
  • Moving the window to another monitor during startup can crash the NVIDIA driver (Swapchain::Destroy).
  • Some long hairstyles leave a slightly lighter line at the base of the neck.
  • Portuguese (Portugal) (BB_LANGUAGE=7) is listed but the game has no text for it: pick Portuguese (Brazil) (17).

Requirements

  • x86-64 CPU with AVX2, BMI1, MOVBE and LZCNT (Intel Haswell / 4th-gen Core or newer, or AMD Ryzen). Older CPUs with only AVX (Sandy/Ivy Bridge, Xeon E5 v1/v2) are not supported yet.

  • GPU with Vulkan 1.3. ≥ 6 GB of VRAM recommended (below that a more aggressive texture collector kicks in).

  • A dump of the game: the CUSA06534 base folder and the 1.14 update, for example:

    PS4/CUSA06534          (base game, eboot.bin)
    PS4/CUSA06534-patch    (1.14 update; -PATCH, -update, _patch… also work)
    

    scripts/merge_update.py merges them automatically into CUSA06534-merged using hard links (no extra disk space; on drives without hard links the files are copied).

Windows

  1. Download the zip from Releases (or build it, see Building) and extract it.
  2. Open UFC3.exe. Pick the CUSA06534 folder (the base game; an update next to it is detected automatically) and press Play.
  3. On first launch the game is prepared (a few seconds) in %LOCALAPPDATA%\UFC3Native. Each session's log goes to %LOCALAPPDATA%\UFC3Native\logs\; the launcher's Open logs folder button opens it. If the game crashes, send us that session's .log (with Frame statistics checked if you can reproduce it).
Launcher settings (God of War III port style)

Since 0.1.5 the launcher has a UFC 3 dark/gold look, with your dump's own artwork in the banner.

SettingValuesNotes
Player nametext (16 ASCII characters)The name the game shows.
Game languageEnglish (US), French, German, Italian, Spanish (Spain / Latin America), Portuguese (Brazil / Portugal), Polish, Russian, Japanese, Chinese (Traditional)BB_LANGUAGE. Portugal has no text in the game: use Brazil.
Controls...per PS4 inputKeyboard key (click, then press it) and gamepad button. Saved to bbport.ini.
Online playOffline / Join server / Host serverJoin: the host's address in Join address. Host: pick this PC's address in Host address (Rescan, Copy IP to share it); the server starts with Play and stops with the game. See Online.
Game resolution720p / 1080p / 2560x1440 (2K, experimental) / 3840x2160 (4K, experimental)What the game renders. 2K/4K use PS4 Pro mode with a 4K output; in game choose VIDEO → RESOLUTION → Favor Resolution (the option only appears in these modes). Validated on Linux only.
Display resolutionSame / 1080p / 1440p (2K) / 4KWindow size; the game's frame is scaled to it (use FSR 1.0 + sharpening). Fullscreen uses the monitor's size.
PS4 Pro modeon / offBB_NEO: the game renders 1080p instead of 900p, at a lower frame rate.
FPS limit60 / 90 / 120 / 144 / 240 / UnlimitedSaved by value; the game never goes above 240.
Present modeMailbox / VSync (FIFO) / ImmediateOn NVIDIA, Mailbox stops at 2× the monitor's refresh rate; Immediate reaches 240 (may tear).
UpscalingOff / AMD FSR 1.0For an internal resolution below the monitor's.
Sharpness (RCAS)Off / Low / Medium / High
FPS counteron / offTop left.
Frame statisticson / offPeriodic FPS, readback and (on Windows) memory figures in the log; useful in crash reports.
Clear the shader cache at starton / offRecompiles every shader on each start: leave it off.
Log foldera folder (empty: %LOCALAPPDATA%\UFC3Native\logs)Where the run logs and frame/readback CSVs go; BB_LOG_DIR wins over it. A network drive works, but a local disk gives the best FPS.

Environment variables you already have set take precedence over the launcher (for example a .bat with set BB_WRITE_WINDOW=2 before starting UFC3.exe). Since 0.1.7 you no longer need BB_WRITE_WINDOW=0 to avoid the Career crashes.

Linux

# dependencies: C/C++ compiler, CMake, Vulkan, SDL3 and Python 3 (see shell.nix / build.sh)
git clone <this repo> ufc3 && cd ufc3
git submodule update --init --recursive
bash build.sh                                       # builds the runtime + renderer
python3 scripts/merge_update.py /path/PS4/CUSA06534 # base + 1.14 -> /path/PS4/CUSA06534-merged
BB_GAME_DIR=/path/PS4/CUSA06534-merged bash run.sh  # defaults to orig/CUSA06534

run.sh detects the ufc3 profile and applies the game's defaults; any variable can be overridden on the command line (BB_FPS_LIMIT=0 bash run.sh).

There is also a GTK 4 launcher (game folder, player name, language, resolution, Offline / Join / Host) that needs Python PyGObject and GTK 4: python3 launcher/ufc3_launcher.py from the repo, or ./ufc3.sh with no arguments in the Linux package (./ufc3.sh --play /path/to/CUSA06534 plays from the command line). Logs go to ~/.local/share/UFC3Native/logs/.

Linux AppImage

Download the Linux AppImage from Releases, then:

chmod +x UFC3Native-x86_64.AppImage
./UFC3Native-x86_64.AppImage             # GTK launcher; close it without Play to test
./UFC3Native-x86_64.AppImage --play      # saved settings, same arguments as ufc3.sh

Release assets include the version in the filename; substitute that name above. Like the tar.gz, this requires glibc 2.38+, system Python 3 and, for the launcher, PyGObject + GTK 4 (python-gobject gtk4 on Arch; python3-gi gir1.2-gtk-4.0 on Debian/Ubuntu). Python runs the bundled preparation scripts and private online server; Python and GTK are not bundled. Vulkan and the window system also come from the host. Normal AppImage mounting needs FUSE 2 (libfuse2t64 on Ubuntu 24.04), or use ./UFC3Native-x86_64.AppImage --appimage-extract-and-run without FUSE. The mounted image is read-only: settings, prepared files, saves and logs remain in ~/.local/share/UFC3Native (respecting XDG_DATA_HOME and BB_DATA_DIR, plus BB_LOG_DIR for logs). Keep your game dump/update outside the image; merging the update creates a sibling CUSA06534-merged directory as with the tar.gz.

To package, build with ufc3-build, run bash scripts/package-linux.sh, then bash scripts/package-appimage.sh. The latter wraps the existing tar.gz unchanged and writes dist/UFC3Native-x86_64.AppImage and its .sha256. Install appimagetool on PATH, or set APPIMAGETOOL=/absolute/path/to/appimagetool; an executable x86_64 appimagetool AppImage works too, without FUSE on the build machine. No tool is downloaded automatically; a missing tool produces an installation hint. Use BB_ALLOW_DIRTY=1 when making the tar.gz from an uncommitted local test build. Releases publish all three packages with SHA-256 checksums.

Online (experimental)

Private hosting between friends: one player runs the server on their own PC, with no public matchmaking. Either Windows or Linux can host, and the two PCs may run different systems; both need the same port build and the same game dump/update.

  • The host picks Host server in the launcher, selects their address, shares it (Copy IP) and presses Play. The friend picks Join server, enters that address and presses Play.
  • Both enter Online → Quick Fight and search at about the same time. Rematch works.
  • Outside your LAN, use a VPN such as Tailscale on both PCs and share the host's VPN IP (never 127.0.0.1).

Step by step: docs/ufc3/HOW-TO-PLAY-ONLINE.md; ports, logs and troubleshooting: docs/ufc3/ONLINE-HOSTING.md.

Advanced: the launchers set BB_ONLINE_SERVER=<host address> for the game (without it the console stays offline). An inherited BB_ONLINE_SERVER overrides the launcher's Offline/Join choice. You can also run the server yourself (python3 tools/ufc3_server.py --bind 0.0.0.0) and have both players use Join server. BB_NET_LOG=0 silences the network log (on by default in this mode). Details: docs/ufc3/ONLINE_IMPLEMENTATION.md.

Building the Windows zip (on Linux)

bash scripts/build-windows.sh                 # llvm-mingw cross-compile, LTO on
BB_ALLOW_DIRTY=1 bash scripts/package-windows.sh
# -> dist/UFC3Native-windows-x86_64.zip  (UFC3.exe, bin/, embedded Python, scripts/)
Publishing a release

Write the notes in docs/ufc3/releases/<version>.md, commit, then:

bash scripts/release.sh <version>   # tags ufc3-v<version>, builds, packages, pushes, creates the GitHub release

Key environment variables

VariableUFC 3 defaultWhat it does
BB_FPS_LIMIT0 on Windows (unlimited; the launcher offers 60/90/120/144/240), 120 in run.shFPS limit (0 = none).
BB_GUEST_FPS_LIMIT0FPS limit applied to the game's own frame loop (once per frame in sceGnmSubmitDone), instead of holding finished frames in the flip queue as BB_FPS_LIMIT does: the next frame reads the pad after the wait, so the limit adds no input latency. With BB_FRAMES_AHEAD=1 it lowered the GPU wait per frame from 4.7 to 1.9 ms on a GTX 1650.
BB_NEO0 (launcher: "PS4 Pro mode")PS4 Pro mode: the game renders 1080p instead of 900p (about 80 FPS in fights on an RTX 4070).
BB_PRESENT_MODEImmediate (Windows)Mailbox, Fifo or Immediate.
BB_VBLANK_HZ / BB_FLIP_RATE480 / 0The game asks for 30 Hz; with flip rate 0 and a high virtual vblank it runs at the real rate (the simulation follows real frame time).
BB_READBACKS / BB_READBACK_LINEAR2 / 1Precise reads of GPU-written memory (without them the fight renders black, as UFC 4 does in shadPS4).
BB_WRITE_STEP1CPU writes next to a few GPU-written bytes are single-stepped instead of waiting for the GPU; write windows need it. Validated on Windows (UFC-48 coherent windows) with correct created/career skin.
BB_WRITE_WINDOW1 (run.sh and the Windows launcher, since 0.1.5)Pages shared by CPU and GPU data stay open for the CPU until the game's next submission; then only what changed is uploaded (see Performance). The 0.1.4 Windows heap corruption is fixed by UFC-48's coherent windows (validated on an RTX 3060: second fight and contract without a crash). BB_WRITE_WINDOW=0 turns it off. BB_WRITE_WINDOW=2 opens a window only when the faulting write's exact bytes are known (a guest memcpy/memmove/memset, or a guest store decoded as one plain store) and they do not cover GPU bytes; any other write is handled as with 0 (single step or readback). It removes the per-frame readbacks of the Map(WRITE_DISCARD) + memcpy at guest +0x431c4f and the stores at +0x4219b8 (see docs/ufc3/WINDOWS-EXACT-WRITE-WINDOWS.md). Since 0.1.8 mode 2 no longer overwrites GPU data next to a CPU write, which broke blood and sweat.
BB_PREUPLOAD1Background upload of rewritten game memory into VRAM (1 = resident memory, 2 = all of it). Restores blood/sweat on hits (UFC-49); validated on Linux and Windows. A user-set value overrides the default.
BB_ALPHA_TO_MASKonHair/beard coverage (DB_ALPHA_TO_MASK emulated as a shader dither). 0 turns it off; coverage uses Vulkan alpha-to-coverage on MSAA targets; colourmap limits it to draws sampling the hair colour map (Create Fighter only).
SHADPS4_BC_MODE3GPU-compressed textures go through memory (created-fighter skin). 0 restores the older compressed-view/copy behaviour, for comparison.
BB_T4_PRESERVE_ALIAS1Keeps the portrait the game draws into a larger render target when it re-reads it at the portrait's size (post-fight fighter, career portraits). 0 disables it.
BB_LDS_ORDER1Subgroup memory barriers around shared-memory (LDS) stores in translated shaders, which the GCN code expects (octagon floor grain on AMD). 0 turns them off; clear the shader cache when changing it.
BB_GPU_WRITE_TWINS0Incompatible with precise readbacks (deadlock).
BB_INTERNAL_WIDTH/HEIGHT, BB_FSR1, BB_RCASfrom the launcherInternal resolution and upscaling.
BB_WINDOW_SIZEfrom the launcherWindow size (2560x1440, 3840x2160…); the game's frame is scaled to it.
BB_SKIP_MOVIES1The EA intro and the first-start video play a short black clip (share/skip_movie.mkv).
BB_USER_NAMEPlayer 1The PS4 user name the game shows (16 bytes).
BB_LANGUAGE1 (launcher: "Game language")The PS4 system language: 1 English (US), 2 French, 3 Spanish (Spain), 4 German, 5 Italian, 0 Japanese, 7 Portuguese (Portugal; the game has no text for it), 8 Russian, 10 Chinese (Traditional), 16 Polish, 17 Portuguese (Brazil), 20 Spanish (Latin America).
BB_FRAME_STATS1 in run.shPeriodic statistics in the log (FPS, µs per draw, readbacks, write sites…).
BB_UI_BACKPRESSURE1A UI scene built 8 times since the last frame waits for the next frame, up to 250 ms. This fixes the Career crash in the "UI Update" thread (0.1.7). 0 turns it off.
BB_APPEARANCE_BACKPRESSURE1When the fighter appearance pool is full, the game waits up to 250 ms for an entry to be freed. This fixes the Create Fighter crash (0.1.7). 0 turns it off.
BB_DEPTH_AS_COLOR_REUSE1A shadow map read as color is converted only again after it changes, instead of at every draw. This fixes the slow Career menu after winning the 2nd belt (0.1.8). 0 converts at every draw, as before.
BB_GC_STALE_SECONDSonFrees cached textures that the game overwrote and that were not used for 60 s, even when memory use is low (0.1.8). N (1–63) changes the number of seconds; 0 turns it off.
BB_GROWTH_SECONDS60How often the log prints a Growth line with cache sizes, memory and FPS. 0 turns it off.

There are also purely diagnostic switches (*_TRACE, *_SKIP, *_DRAWS…); they are described in the release notes.

How it works

  • Native execution. eboot.bin is converted offline (scripts/prepare.py, link_libc.py, link_modules.py) into a flat memory image with the game's own PS4 libc and libSceFios2 linked in as native code. TLS accesses (fs:) are rewritten (gs: on Linux, the TEB TLS slot on Windows).
  • Runtime (src/runtime_*.c): memory, threads, mutexes/semaphores/event flags, files, audio (ATRAC9, and MP3 for AJM), controller (SDL), saves, user services and network/online stubs. On Windows: SRWLOCK-based mutexes, high-resolution timers, timeBeginPeriod(1), raised priority and EcoQoS disabled.
  • GPU (gpu/): an adapted shadPS4 renderer. PM4 commands are decoded on one thread and draws are recorded into Vulkan on another (a two-stage pipeline), helped by draw-preparation and recording threads. GPU-written memory is page-protected so CPU