Skip to content

DarrCoh/reckless-drivin-sdl

Repository files navigation

Reckless Drivin' icon

Reckless Drivin'

macOS Linux

A native SDL2 port of the classic Mac OS racing game

Originally by Jonas Echterhoff (2000), brought back to life on modern hardware

macOS (Apple Silicon & Intel) • Linux • SDL2 • No Emulator Required


640x480 software-rendered, pixel-perfect; just like it was in 2000


The Story

Reckless Drivin' was one of those games. The kind that came pre-installed (or maybe my brothers found it on some shareware CD) on our family Mac back in the day. My brothers and I played it for hours, weaving through traffic, smashing into things, trying to beat each other's times.

Years later, I got hit with a tiny wave of nostalgia. I wanted to play it again, for old time's sake. Not in an emulator, not through some janky compatibility layer, but running natively on my MacBook.

The original source code had been released on GitHub by Jonas Echterhoff in 2019 under the MIT license. How hard could it be, you'd say? Turns out: very. The original code was pure Classic Mac OS: PowerPC, big-endian, 32-bit, built with CodeWarrior, using DrawSprocket, InputSprocket, the Mac Sound Manager, and Mac Resource Forks. None of that exists anymore. Every platform API had to be replaced. Every byte of game data was stored backwards relative to my CPU. It was a rabbit hole, and I went all the way down.

This is the result: a port of Reckless Drivin' with all 10 levels, every screen, and every effect, running natively with pixel-perfect graphics, proper audio, and full mouse + keyboard + gamepad controls.


Features

  • All 10 levels fully playable: including the encrypted shareware levels, properly decrypted with the free registration key
  • 24 driveable vehicles: sports cars, muscle cars, motorcycles, semi trucks, a tank, a helicopter, police interceptors, boats, and more; all selectable via the cheat mode level select
  • Pixel-perfect rendering: 640x480 ARGB1555, software-rendered and uploaded via SDL2 streaming texture
  • Full audio: 18-channel software mixer at 44.1 kHz stereo, with Doppler effect and 3D positional sound
  • Every screen implemented: main menu, help, high scores, preferences, pause, game over, and scrolling credits
  • High score table: top 10 scores rendered on-screen with keyboard name entry
  • Screen transitions: fade to/from black, venetian-blind shift-in effect, game over animation
  • Menu polish: hover highlights and press states on all buttons, click sound feedback
  • Remappable controls: rebind all 8 game keys from the Preferences menu
  • Gamepad support: SDL2 GameController API with analog stick + triggers
  • Resizable window: maintains 4:3 aspect ratio with letterboxing
  • Persistent preferences: settings, high scores, lap records, and key bindings saved between sessions
  • Native macOS app bundle: builds as a proper .app with SDL2 bundled inside; distribute as a DMG or zip it and send to a friend
  • Broad compatibility: macOS 11.0+ (Big Sur through current), Linux (x86_64, ARM64)
  • No external dependencies on macOS: SDL2 is bundled into the app automatically; no Homebrew or manual installs needed to run

Quick Start

Requirements

OS macOS (Apple Silicon or Intel), Linux
Compiler Clang or GCC (C11)
Build tool CMake 3.16+
Library SDL2

Build & Run on macOS

# Install CMake
brew install cmake

# Clone repository
git clone https://github.com/DarrCoh/reckless-drivin-sdl.git

# Build
cd reckless-drivin-sdl
./build-mac-app.sh

# Run (double-click or from terminal)
open build/RecklessDrivin.app

The build produces a self-contained RecklessDrivin.app bundle with the Data file and SDL2 bundled inside, plus a .dmg for easy distribution. No SDL2 install required on the recipient's machine.

Since the app is not notarized, macOS will quarantine it when downloaded. Recipients can either run xattr -cr /path/to/RecklessDrivin.app in Terminal, or open it and go to System Settings > Privacy & Security to allow it.

Build & Run on Linux

# Install CMake and SDL2 (Ubuntu/Debian)
sudo apt install cmake libsdl2-dev

# Clone repository
git clone https://github.com/DarrCoh/reckless-drivin-sdl.git

# Build
cd reckless-drivin-sdl
mkdir build && cd build
cmake ..
cmake --build .

# Run
./RecklessDrivin

The build produces a standard executable and copies Data next to it.


Controls

Keyboard (defaults, remappable in Preferences)

Action Key
Accelerate Up
Brake / Reverse Down
Steer Left Left
Steer Right Right
Kickdown Left Shift
Brake Space
Mine Z
Missile X
Pause P
Quit Escape

To rebind keys: Preferences > Controls > Configure

Menu Shortcuts

Action Key
Start Game Enter / S / N
Preferences P
High Scores C / O
Help H
Quit Q
Level Select Shift + Enter

Level Select (Cheat Mode)

Press Shift + Enter from the main menu to open the level select screen. From here you can:

  • Pick any level (1-10): with names, descriptions, and time limits shown
  • Choose from 24 vehicles: with a live sprite preview, name, and description
  • Navigate with arrow keys or mouse, confirm with Enter
Full vehicle list
Land Heavy Water
Sports Car Semi Truck Speedboat
Muscle Car Delivery Truck Cargo Ship
Sedan Bus Barge
Station Wagon Monster Truck Police Boat
Compact Tank Armed Patrol Boat
Coupe APC
Buggy Helicopter
Motorcycle
Sport Bike
Police Car
Police Bike
Police Chopper

Gamepad

D-pad for steering, left stick for analog control, face buttons for actions, triggers for kickdown/brake, Start for pause.

Mouse

Click the license plate buttons on the main menu. Click anywhere to dismiss help, score, and pause screens.


The Porting Journey

This wasn't a simple recompile. The original game was written for a world that no longer exists.

The Platform Layer

The original game used five major Mac-specific APIs. Every one had to be replaced:

Original Mac API Purpose SDL2 Replacement
DrawSprocket Screen management, page flipping, gamma fades SDL2 window + renderer + streaming texture
InputSprocket Gamepad/keyboard input abstraction SDL2 event polling + keyboard state + mouse
Sound Manager Multi-channel audio mixing Custom 18-channel software mixer
Resource Manager Loading assets from resource forks Custom resource fork parser
DriverServices High-resolution timing SDL_GetPerformanceCounter()

The Compatibility Layer

The original code uses Mac-specific types everywhere: UInt8, UInt16, Handle, Str255, nil... compat.h maps all of these to modern C equivalents, plus provides NewPtr(), NewHandle(), BlockMoveData(), and byte-swapping macros.

The Resource Fork Parser

Mac OS stored data in "resource forks": a structured binary format with typed, numbered entries. The game's Data file contains 22 Pack resources (compressed game data), 10 PPic resources (PICT images in both 8-bit and 16-bit formats), and 1 Chck resource. The custom parser (resources.c) reads the resource map and returns data by type + ID. Pack resources use LZRW3-A compression (public domain, by Ross Williams).

Endianness: Everything Is Backwards

PowerPC is big-endian. Apple Silicon is little-endian. Every multi-byte value in every data structure was stored in the wrong byte order: 16-bit pixels, 32-bit object fields, RLE sprite tokens, sound headers, floating-point road data, and even the XOR encryption key for locked levels. Getting any one of these wrong produced garbled colors, corrupted sprites, or audio that sounded like a fax machine.

The sizeof(long) Bug

On 32-bit PowerPC, sizeof(long) == 4. On 64-bit ARM64, sizeof(long) == 8. The original RLE renderer had manual copy loops using long-sized chunks; each iteration copied 8 bytes instead of 4, overwriting adjacent memory and causing immediate SIGBUS crashes. Four separate rendering functions had to be rewritten.

The RLE Sprite Format

Custom 4-byte token format (1 byte type + 3 bytes big-endian data). Token parsing used *(unsigned long*)ptr: wrong endianness AND wrong size on ARM64. Every token parser in the game had to be replaced with explicit byte reads.

The Sound System

Sound data contains embedded Mac SoundHeader structures (not raw PCM). Standard headers (22 bytes, 8-bit unsigned, ~11 kHz) and Extended headers (64 bytes, 16-bit signed BE, 44.1 kHz) are mixed throughout. The mixer had to detect the header type, extract the actual PCM data, and apply per-channel rate correction (basePitch = nativeRate / 44100).

The PICT Renderer

Menu screens are stored as Mac PICT v2 images with LZRW3-A compression. Two pixel formats are used: 16-bit DirectBitsRect (0x009A) for most screens, and 8-bit indexed PackBitsRect (0x0098) with embedded color tables for others. Both formats use PackBits row compression.

The Encryption

Levels 4-10 were encrypted in the original shareware distribution. The XOR-based decryption operates on 32-bit words in memory, but the encryption was applied on big-endian PowerPC, so the key's byte pattern in memory is reversed on little-endian ARM64. A byte-swap of the key before XOR was required for decryption to produce valid data. The free registration key (name: Free, code: B3FB09B1EB) was published by the author when the game went freeware.

The Pascal String Off-by-One

One crash only appeared during gameplay when picking up a specific power-up. The "ADDONS LOCKED" text effect used a Pascal string: a format where the first byte is the length, followed by that many characters. The length byte said 15 (\x0f), but the actual string "ADDONShLOCKEDf" was only 14 characters. On the original Mac, the text renderer had no bounds checking; it would read the 15th byte (a NUL from the struct initializer), try to look up font glyph 63 (0 - 'A' + 128) in the sprite pack, land on some random memory, and get away with it. On the SDL port, bounds checking on pack lookups returned NULL, and NULL + 8 became a segfault. A bug that silently survived 25 years on PowerPC.

The NULL Safety Audit

The Pascal string crash revealed a broader pattern: the original code never checks return values. NewObject() allocates a game object and looks up its type definition from a resource pack; if the type ID doesn't exist, the original Mac code would silently read garbage memory and keep going. GetSortedPackEntry() and GetUnsortedPackEntry() are called dozens of times across the codebase, and every single callsite assumes success. On PowerPC with flat memory and no ASLR, dereferencing a wild pointer might corrupt something silently or just happen to land on valid-ish data. On ARM64 with strict memory protection, it's an instant segfault.

A full audit of every pack lookup callsite across the codebase found 15 unguarded dynamic-ID paths: places where the lookup ID comes from gameplay state (object types, weapon projectiles, debris, explosions, smoke, font glyphs) rather than hardcoded constants. Each was given a NULL guard with graceful degradation: NewObject() unlinks and frees on failure, KillObject() falls back to RemoveObject(), particle effects silently skip, and sprite draws bail out early. The result is a game that can survive a missing resource entry without crashing; something the original never needed because the Mac OS memory model was far more forgiving.

The Optimization Pass

A performance audit of the finished port found it was mostly clean: no memory leaks, no redundant texture creation, no O(n^2) collision loops, packs decompressed once and cached. But the build had been running at -O0 (no optimizations) the entire time. Enabling -O3 with Link-Time Optimization shrank the binary from 272 KB to 211 KB and gave the compiler room to inline and optimize across all 30+ source files. The one hot-path issue was pow(10, i) being called every frame for score rendering: a floating-point exponentiation where an integer lookup table ({1, 10, 100, 1000, ...}) does the same job in a single array access.

The App Bundle

The original Reckless Drivin' was a proper Mac application: double-clickable, with an icon in the Dock. The SDL port should be too. CMake builds a native .app bundle on macOS: Info.plist for metadata, the Data file in Contents/Resources, and SDL2.framework bundled inside Contents/Frameworks. The build script downloads the official SDL2 universal framework and produces a fat binary (arm64 + x86_64), so the app runs on both Apple Silicon and Intel Macs. The deployment target is macOS 11.0 (Big Sur), the first macOS release with Apple Silicon support. A packaging script produces a .dmg for easy distribution.


Architecture

                        +----------------+
                        |    main.c      |
                        |  Game Loop     |
                        +-------+--------+
                                |
          +------------+--------+--------+--------------+
          |            |        |        |              |
     +----v----+ +-----v--+ +--v--+ +---v------+ +----v-----+
     |Renderer | |Interface| |Sound| |Physics   | | Objects  |
     | Road    | | Menus   | |Mixer| |Collision | | Control  |
     | Sprites | | Prefs   | |     | |          | |          |
     | RLE/FX  | | HUD     | |     | |          | |          |
     +----+----+ +----+----+ +--+--+ +----------+ +----------+
          |           |         |
     +----v-----------v---------v-----------------------------+
     |                platform_sdl.c                          |
     |     Screen  |  Input  |  Audio  |  Timing  | Mouse    |
     +----------------------------+---------------------------+
                                  |
     +----------------------------v---------------------------+
     |                    SDL2 Library                        |
     +--------------------------------------------------------+

     +--------------------------------------------------------+
     |  compat.h  |  resources.c  |  lzrw.c  |  textrender   |
     |  Mac types    Resource fork   LZRW3-A    Bitmap font   |
     +--------------------------------------------------------+

Project Structure

RecklessDrivin-SDL/
├── CMakeLists.txt           # Build configuration (app bundle + SDL2 bundling on macOS)
├── README.md
├── Data                     # Original game resources (Mac resource fork)
├── build-mac-app.sh         # macOS build + DMG packaging script
├── build-linux-app.sh       # Linux build + AppImage packaging script
├── packaging/
│   ├── osx/                 # macOS: Info.plist template, icons (Big Sur + original 2002)
│   └── linux/               # Linux: AppRun, desktop entry, icon
├── include/
│   ├── compat.h             # Mac OS type compatibility layer
│   ├── platform.h           # SDL2 platform abstraction API
│   ├── resources.h          # Resource fork parser
│   ├── textrender.h         # Shared bitmap font rendering
│   ├── lzrw.h               # LZRW3-A decompression
│   └── ...                  # Original game headers
└── src/
    ├── main.c               # Entry point & main loop
    ├── platform_sdl.c       # SDL2 backend (screen, input, audio, timing, mouse)
    ├── resources.c           # Mac resource fork parser
    ├── lzrw.c               # LZRW3-A decompressor
    ├── interface.c           # Menu system & PICT renderer (8-bit + 16-bit)
    ├── preferences.c         # In-game preferences UI
    ├── high.c               # High score display & name entry
    ├── textrender.c          # 8x8 bitmap font for UI screens
    ├── gameendseq.c          # Scrolling credits sequence
    ├── screenfx.c            # Screen transitions (shift-in, game over anim)
    ├── sound.c              # SoundHeader parsing & channel management
    ├── renderframe.c        # Main frame renderer
    ├── roaddraw.c           # Road texture rendering
    ├── rle.c                # RLE sprite decompression & drawing
    ├── sprites.c            # Sprite loading & rendering
    ├── textfx.c             # In-game text effect rendering
    ├── objects.c            # Game object management
    ├── objectPhysics.c      # Physics simulation
    ├── objectCollision.c    # Collision detection
    ├── objectControl.c      # AI driving behavior
    ├── gameframe.c          # Per-frame game logic
    ├── gameinitexit.c       # Level loading & game state management
    ├── input.c              # Input processing & key remapping UI
    ├── packs.c              # Pack loading, decryption & decompression
    ├── pause.c              # Pause screen
    ├── particlefx.c         # Particle effects
    ├── random.c             # TT800 pseudo-random number generator
    └── ...                  # Additional modules

Known Limitations

  • Cl16 resource id=8 not found at startup (cosmetic; a synthetic fallback palette is used)
  • PPic 1009 (original scrolling credits image) contains QuickDraw text commands that can't be rasterized; replaced with bitmap font credits
  • Gamepad button remapping is not yet exposed in the UI (SDL2's standard mappings handle most controllers automatically)

Credits

Original game: Jonas Echterhoff (2000), released under the MIT License in 2019

SDL2 port: Darren Cohen

Built with: Claude Code

macOS Big Sur icon, packaging & CI: orazioedoardo (universal macOS builds, Linux AppImages, DMG packaging)

LZRW3-A compression: Ross Williams (public domain)

SDL2: libsdl.org

Free registration key: Published by Jonas Echterhoff at jonasechterhoff.com

Standing on the shoulders of Nate Craddock, whose Open Reckless Drivin' project and blog posts were an invaluable guide. His reverse-engineering work identifying the LZRW3-A compression format, discovering the 4-byte offset via Ghidra, documenting the XOR decryption key, and mapping the binary structures saved us countless hours. This port wouldn't exist without his trailblazing.

License

The original Reckless Drivin' source code is released under the MIT License by Jonas Echterhoff. This port inherits that license.


Built because some games are worth bringing back.

About

Native SDL2 port of Reckless Drivin' (2000) — runs on macOS and Linux, no emulator required

Topics

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors