Rage Racer PC

Rage Racer PC brings Rage Racer to PC.

README

Rage Racer PC

Native Windows, Linux and macOS port of Rage Racer (PAL / Europe, SCES-006.50). It contains the complete game, platform packaging and runtime compatibility layer.

Running a release

Extract the whole archive, then open Rage Racer.app on macOS, Rage-Racer.exe on Windows, or rage-racer on Linux. Select your legally obtained Rage Racer CUE (with its BIN tracks alongside it), Track 01 BIN, or CHD when prompted. Native assets are imported automatically and the game starts with the modern renderer. No separate launcher or extractor is required.

Press F10 to switch to enhanced classic rendering. Video settings are in rage-port.ini; see the classic renderer guide for resolution, widescreen and FPS examples. No game data is supplied in the archive.

Build from source

The project uses CMake and its pinned PSY-Z compatibility layer:

git clone --recurse-submodules https://github.com/khasinski/rage-racer-pc.git
cd rage-racer-pc
cmake -S . -B build/release -DCMAKE_BUILD_TYPE=Release
cmake --build build/release --parallel

The native game, importer, tools and in-tree tests require a C17 compiler; vendored dependencies retain the language standard selected by their upstream build files.

Linux desktop builds need SDL's Wayland and/or X11 XRandR development dependencies. Check CMake's final SDL summary for wayland or xrandr; the dependency list used by releases is in the Linux workflow. A build missing both can report a fallback 60 Hz even on a 120 Hz desktop. The modern renderer logs its SDL video driver and selected refresh rate when video.fps=vsync.

For local development, provide a legally obtained disc image through the runtime disc setting (paths may be relative or absolute):

RAGE_PORT_DISC_CUE="/path/to/Rage Racer (Europe).cue" ./build/release/rage-racer

The resulting executable is build/release/rage-racer on Linux, build/release/Rage Racer.app on macOS and build/release/Release/rage-racer.exe with the Visual Studio generator on Windows. Release downloads are supplied as ZIP archives for macOS arm64, Linux x86-64 and Windows x86-64. Every archive contains the documented rage-port.ini modern-renderer preset.

Scenario files

race-scenario.ini can launch a race through the normal asset-loading flow without manually navigating the menus:

./rage-racer --scenario race-scenario.ini
./rage-racer --scenario custom.ini --set race.class=3 --set race.course=2

--set section.key=value options override values from the INI file. The optional grid contains the eleven rival car IDs in retail start-position order; -1 leaves a slot empty. The player retains the normal starting position. --set race.variant=N selects a zero-based asset variant within the chosen car's catalog range before loading its assets. Omitting it preserves the normal car setup; out-of-range values are ignored with a diagnostic. The launcher passes this file directly to the game as --scenario; it no longer expands it into a collection of environment variables.

For repeatable local runs, the native rage-scenario tool provides checked short options for the common race values and forwards them to that same public interface. It does not create a second scenario format:

./build/rage-scenario race-scenario.ini --binary ./build/release/rage-racer \
  --class 3 --course 2 --car 9 --dry-run

After the race, after_finish = menu (the default) leaves the replay and result screens to the retail flow but stops scenario automation, returning control to the player. Use repeat to launch the configured race again or exit to close the port after leaving the result screens. The same setting is available as --after-finish menu|repeat|exit.

The scenario file accepts every launch setting below. Numeric class, course and car values are the retail zero-based indices.

SectionSettingValues / meaning
racemodegrand-prix, time-attack
raceseriesgrand-prix, extra-gp; time attack always uses Grand Prix
raceclass0 to 5
racecourse0 to 3
racecar0 to 12
racetransmissiondefault, automatic, manual; original per-car restrictions still apply
raceafter_finishmenu, repeat, exit
racegriddefault, or exactly 11 comma-separated car IDs; -1 leaves a slot empty
bootdirecttrue skips race-selection menus; false drives the normal menus
bootskip_sequencesSkip the opening FMV and Grand Prix prologue when true
startplayer_track_pointPlayer track-point index
startrival_track_pointsUp to 11 comma-separated point indices; - keeps the retail pose
startplayer_x, player_zExact world coordinates; both must be present
startplayer_headingOptional exact heading used with player_x / player_z
startcameraInitial retail camera-view index
startfreezeReapply the configured diagnostic placement every frame

For renderer and rear-view debugging, scenarios may start cars at exact points of the loaded track:

[start]
player_track_point = 120
rival_track_points = 118,116,114,112,110,108,106,104,102,100,98
# Reapply these positions every frame for deterministic rendering diagnostics.
freeze = true

The engine recalculates world position, height, heading, track section and lap progress from these indices. A - entry keeps that rival's retail grid pose.

Native frame replay

Renderer bugs can be reproduced without navigating the game again. This debug function is disabled in the shipped configuration. Set [diagnostics] marker_capture = true, then press M in modern mode to write markers/marker-N-world.bin, the displayed images, the legacy scene record and a text dump of the native draw spans. The world file contains the exact renderer-neutral cameras, transforms, materials and mesh instances used for that frame; it contains no disc assets. With the default marker_capture = false, pressing M has no diagnostic side effects.

Build and replay it offscreen with:

cmake --build build/release --target rage-frame-replay
build/release/rage-frame-replay markers/marker-0-world.bin \
  --assets build/release/native-assets \
  --output marker-0-replay.ppm \
  --draws marker-0-replay.txt

Use --width and --height to match the captured target. A legacy marker's exact camera can be applied to another compatible world snapshot with --camera-scene markers/marker-0-scene.bin. --probe X,Y lists every clipped native triangle covering a pixel, including its depth, material, source mesh and authored bias. Automated diagnostics.modern_dump_scene = true captures the same .world.bin and .draws.txt sidecars next to a modern frame dump.

Runtime requirements

  • macOS on Apple Silicon (arm64), a glibc-based x86-64 Linux distribution, or 64-bit Windows 10/11
  • A legally obtained Rage Racer disc image: a .cue sheet with all referenced track files, a Track 01 .bin, or a CHD Movies are decoded in process, so no external tools are needed.

On first launch the port looks for a .cue, .bin or .chd image next to the executable and uses it when one is there. Otherwise it opens a native file picker and remembers the answer. The compiled C importer converts the selected disc's models, textures and sky directly into renderer-native data in memory; there is no setup command, asset download, Python runtime or classic-renderer fallback. To pick a different disc later, start the game with --set disc.choose=1, or pin one with [disc] image in rage-port.ini.

Normal runtime settings are read from rage-port.ini. Use --config FILE for an alternative file and --set section.key=value for an individual override.

There are exactly two renderer modes. classic presents the faithful PS1-compatible output. modern presents native RenderWorld geometry with a normal depth buffer, configurable internal scale, optional 16:9 presentation, an interpolated frame rate and FXAA. It never falls back to captured PS1 3D. The supplied file defaults to modern:

[video]
renderer = modern
internal_scale = 4
aspect = 16:9
fps = vsync
post = fxaa
toggle_renderer_key = F10

The complete normal runtime configuration is listed below. Every option can be placed in rage-port.ini or supplied as --set section.key=value.

Video settings:

SettingValues
rendererclassic, modern
internal_scale0.5 to 16
aspectauto, 4:3, 16:9
fpslogic, vsync, or an integer from 1 to 1000
draw_distance0 to 16
texture_filternearest, linear
postnone, fxaa
gradingoff, vibrant
toggle_renderer_keyan SDL key name, such as F10

Display, content and storage settings:

SectionSettingValues / meaningShipped value
hudanchorcenter, edgesedges
hudshow_lap_timestrue, falsetrue
hudshow_time_limittrue, falsetrue
camerachase_turn_lookahead0 to 1; 0 is retail0
modernassetsOptional explicit prebuilt native-asset cache for renderer development, or disc; omitted always imports from the selected discomitted
modernray_tracingExperimental hybrid rays: off, shadows, reflections, full. May cause visual artifacts and substantially reduce performance.full
modernmirror_distance0.25 to 8, capped at the main view distance1
timingstandardauto, pal, ntscauto
contentcar_namesinternational, japaneseinternational
contentprologueinternational, japaneseinternational
discimagePath to a CUE, Track 01 BIN or CHDremembered picker choice
discchoosetrue opens the picker againfalse
modsdirectoryExtracted-asset or semantic mod directoryomitted

Diagnostic settings:

SettingValues / meaningShipped value
logauto, or an explicit log pathauto
marker_captureEnables the M-key frame/world debug bundlefalse
marker_historyRetains 16 preceding frames when marker capture is enabledfalse
marker_probe_x, marker_probe_yOptional modern-target pixel to list overlapping native triangles in each marker-1, -1
performanceLogs average modern renderer costs every 120 framesfalse

Input-device settings:

SettingValues / meaningShipped value
analogEnable analog gamepads as NeGcon devicestrue
wheelEnable raw SDL racing wheelstrue
wheel_steering_axisSDL axis index 0 to 310
wheel_steering_invertedtrue, falsefalse
wheel_throttle_axisSDL axis index 0 to 312
wheel_brake_axisSDL axis index 0 to 313
wheel_pedals_invertedtrue, falsetrue
wheel_cross_buttonSDL button index 0 to 63, or -10
wheel_square_buttonSDL button index 0 to 63, or -11
wheel_circle_buttonSDL button index 0 to 63, or -12
wheel_triangle_buttonSDL button index 0 to 63, or -13
wheel_l1_buttonSDL button index 0 to 63, or -14
wheel_r1_buttonSDL button index 0 to 63, or -15
wheel_start_buttonSDL button index 0 to 63, or -19
force_feedbacktrue, falsetrue
ffb_gainMaster strength, 0 to 10.55
ffb_centerSpeed-weighted centering, 0 to 10.80
ffb_slideHow light the wheel goes in a slide, 0 to 10.70
ffb_collisionImpact kick, 0 to 10.75
ffb_roadCrests and camber, 0 to 10.25
ffb_damperResistance to a fast hand, 0 to 10.20
ffb_min_forceLifts weak torque over wheel friction, 0 to 10.06
ffb_soft_lockWall past the game's steering lock, 0 to 10.85
ffb_engineEngine vibration, 0 to 10
ffb_inverttrue, falsefalse

Each of the steering, throttle and brake axes accepts _deadzone (0..0.99), _saturation (0.01..1), _linearity (-2..2) and _scaling (0.01..10). Defaults are 0, 1, 0, 1, except steering_linearity = 0.5. The 16 keyboard keys up, right, down, left, cross, circle, square, triangle, l1, r1, l2, r2, select, start, l3 and r3 accept SDL key names and are listed with their defaults under Controls.

The legacy disc.cue name, video.bloom no-op and documented RAGE_PORT_* automation environment variables remain accepted for old test setups, but new configuration should use the keys above.

nearest retains the hard texel edges of the source artwork. linear smooths texture sampling in the enhanced 3D renderers and in the final 2D presentation. The checked-in rage-port.ini documents the remaining sections and their ranges.

Raw racing wheels are supported even when SDL does not classify them as a gamepad. They use the game's NeGcon analog path; wheel_steering_axis, wheel_throttle_axis, wheel_brake_axis, pedal inversion and the face/shift button indexes can be adjusted under [input]. The defaults follow common Logitech layouts, while keyboard input remains available for menus.

A wheel that can play a constant force gets one, built from the car's steering, speed, slide and impacts. Pause the race and confirm the FFB row to turn it on or off; left and right on that row change the strength in steps of 5 percent. The choice is saved next to the remembered disc path. The other ffb_* scales stay in [input]. A gamepad takes the same impacts as rumble. On a wheel with no constant-force support, and on macOS where most rims have no force-feedback driver, the effect stays off and the race runs as before.

In modern or enhanced classic 16:9, [hud] anchor = edges moves the corner HUD into the added widescreen area; use center for the retail 4:3 positions. Set show_lap_times or show_time_limit to false to hide those race displays.

The third-person chase camera can look into a turn as the car steers. Adjust [camera] chase_turn_lookahead from 0 (retail camera) to 1 (up to roughly 30 degrees at full steering lock). It is optional and defaults to the unchanged retail camera (0).

Set [content] car_names = japanese to use the Japanese-release model names: Alouette, Instinct, Victoire, Tempest and Dragone. This changes labels only; the cars, save data and handling remain identical.

Set [content] prologue = japanese for the longer Japanese-release Grand Prix prologue text. The English narration is recorded into Track 02 of the Japanese disc, not the executable, so it plays when a legally obtained NTSC-J CUE/CHD is selected. With a PAL disc the extended text uses the PAL instrumental track.

Modern mode imports its renderer-native assets automatically from the selected disc. Developers can still select a prebuilt cache with [modern] assets = /path/to/native-assets; an explicitly configured missing or incompatible cache is an error, so a typo cannot silently change the rendered content.

Press F10 while playing to switch between classic and modern rendering without restarting the race; the key is configurable as [video] toggle_renderer_key. F4 goes full screen and back.

In the modern renderer the rear-view mirror is a native second camera. It renders the complete semantic track and traffic scene into its own small color and depth target, reflects it left-to-right, and composites it under the original HUD frame. It does not reuse the PS1 mirror ordering table, GTE matrix, or classic visibility list.

The port keeps game saves and the selected disc location in the user's local application data. For compatibility with older portable releases, an existing bu00 directory beside the executable takes priority (beside the .app bundle on macOS). The game does not create a portable directory automatically. Otherwise memory-card saves are regular files in the bu00 subdirectory: under ~/Library/Application Support/Rage Racer/ on macOS, ${XDG_STATE_HOME:-$HOME/.local/state}/rage-racer/ on Linux, and %APPDATA%\Rage Racer\ on Windows. They no longer depend on the directory from which the executable was launched. Each normal launch appends diagnostics to rage-racer.log in the platform's application-state directory. Configure [diagnostics] log to choose another file. The disc can likewise be pinned with [disc] image; the older cue key, RAGE_PORT_LOG_PATH and RAGE_PORT_DISC_CUE overrides remain available for automation.

Rage Racer uses mixed-mode CD audio. For the complete soundtrack, select a CUE or CHD that describes Track 01 and audio Tracks 02–17. Selecting Track 01 BIN directly is supported and provides the game, FMVs and all modern-renderer assets, but it cannot describe the separate CD-audio tracks. Keep all BIN files referenced by the CUE together and prefer that full CUE on first run.

日本版について

日本版(NTSC-J)のディスクでも動作しますが、一部のメッセージは表示されません。 メモリーカードの確認、車の説明、ネジコンの調整画面の説明などが空欄になります。

このポートは欧州版の実行ファイルを移植したものです。欧州版はアルファベットの フォントから一文字ずつ文章を組み立てますが、日本版は同じビデオメモリの位置に、 文章そのものを描いた画像を置いています。日本版のディスクには欧州版が必要とする フォントが存在しないため、これらのメッセージは描画されません。

画面の配置の問題ではなく、二つの版がテキストを別の方法で持っているためです。 それ以外のメニュー、レース、動画は日本版でも問題なく動作します。

About the Japanese release

An NTSC-J disc runs, but the messages the game builds from its shared text sheet stay blank: the memory card prompts, the car descriptions, and the NeGcon calibration instructions.

The port carries the European executable. That one keeps an alphabet at a fixed place in video memory and sets a message one letter at a time. The Japanese release puts whole pre-drawn Japanese sentences in that same place instead, so the alphabet the European code looks for is not on the disc at all and those messages draw nothing. It is not a matter of where the text sits on screen; the two releases hold text in different ways. Everything else runs.

Controls

Gamepad

A connected gamepad is driven as a NeGcon, the analog controller the game was built around. The left stick steers in proportion to how far it is pushed and the triggers meter the throttle and the brake, while the d-pad still steers at full lock so either input can be used. Dead zone and steering sensitivity are the game's own NeGcon settings, adjustable in the OPTIONS menu as "play" and "max twist". Set [input] analog = false to keep a pad on the digital mapping instead.

The response of each axis can be shaped, using the same four settings and the same ranges DuckStation gives a NeGcon, so values can be copied between them. The axes are steering, throttle and brake, which are the twist and the I and II buttons of a real NeGcon:

[input]
steering_deadzone = 0.1
steering_linearity = 0.5

deadzone runs 0 to 0.99 and saturation 0.01 to 1, both as fractions of the travel. scaling runs 0.01 to 10. linearity runs -2 to 2 and is an exponent rather than a fraction: 0 is a straight line, above it softens the middle of the throw, below it sharpens it. Steering defaults to 0.5; throttle and brake remain linear at 0. A value out of range is reported and ignored.

These sit in front of the game's own NeGcon dead zone and twist range, which stay where they always were, in the OPTIONS menu.

Keyboard

The default bindings emulate the original PlayStation pad:

Game actionDefault key
Accelerate (CROSS)X
Brake / reverse (SQUARE)Z
Change camera (TRIANGLE)S
Handbrake (CIRCLE)D
SteerArrow keys
Shift down (`