Platform Support

Free Eggbert supports multiple platforms through the FreeDirect/SDL3 abstraction layer.

Summary

PlatformBackendStatusBuild Method
Windows (native DirectX) DirectDraw / DirectSound Partial Visual Studio 2022
Windows (FreeDirect) SDL3 Partial CMake
Linux SDL3 (FreeDirect) Partial CMake
macOS SDL3 (FreeDirect) Needs Verification CMake
WebAssembly Emscripten + SDL3 Partial emcmake
Android SDL3 (NDK) Partial Gradle + CMake

Windows

Native DirectX (Visual Studio)

The original build target uses the real DirectX 3 SDK (dxsdk3 submodule) with Visual Studio 2022. This path uses DirectDraw and DirectSound directly.

🔍
Needs Verification
The native DirectX 3 path may expose decompilation artifacts that the FreeDirect backend tolerates but real DirectDraw rejects. See Known Issues for details on the suspected double CPixmap::Create() call and fullscreen initialization problems.

CMake + FreeDirect

Building with CMake on Windows uses the FreeDirect backend instead of real DirectX. This requires the third_party/ SDL submodules.

git submodule update --init --recursive
cmake -S . -B build -G "Visual Studio 17 2022" -DSPEEDY_BLUPI_BACKEND=FREEDIRECT
cmake --build build --config Debug

Linux / macOS

Build using CMake with the FreeDirect backend. No system SDL packages required by default.

git submodule update --init --recursive
cmake -S . -B build -DSPEEDY_BLUPI_BACKEND=FREEDIRECT
cmake --build build -j

On Linux the default compiler is GCC or Clang. The permissive compiler flags (-fpermissive, -fms-extensions, etc.) handle decompiled code constructs that strict C++ would reject.

WebAssembly (Emscripten)

Build

source /path/to/emsdk/emsdk_env.sh
emcmake cmake -S . -B cmake-build-web -DCMAKE_BUILD_TYPE=Debug
cmake --build cmake-build-web -j
emrun cmake-build-web/bin/SPEEDY_BLUPI_WINDOWS.html

Assets

Game assets are preloaded into the Emscripten virtual filesystem at build time from gamefiles/DATA, gamefiles/IMAGE08, gamefiles/IMAGE16, and gamefiles/SOUND.

Persistent Saves

Save data is stored in the browser's IndexedDB via Emscripten IDBFS, mounted at /save. This persists across page reloads. Clearing browser site data resets the saves.

// Export save data
Module.ccall('FreeEggbert_ExportPersistentData', null, [], []);

// Import save data
Module.ccall('FreeEggbert_ImportPersistentData', null, [], []);

Web Rendering

On widescreen displays, the game image is automatically letterboxed using SDL3's logical presentation mode. Touch and mouse coordinates are mapped from physical screen coordinates into game logical coordinates — touches on black bars do not register as in-game clicks.

Message Loop Consideration

The native build uses a classic Win32 message loop with a multimedia timer. Emscripten does not work well with a blocking infinite loop, so the web version needs a Free API / launcher-level main-loop adapter. This is an active area of work and is handled at the FreeDirect layer, not in game code.

Android

Prerequisites

ToolRecommended Version
Android StudioLadybug (2024.2) or newer
Android SDKAPI level 35
Android NDK30.0.14904198
CMake (NDK bundle)3.21+ via SDK Manager
Java (JDK)17 (bundled with Android Studio)

Setup

  1. Open Android Studio → Settings → SDK Manager → SDK Tools
  2. Check NDK (Side by side) version 30.0.14904198
  3. Check CMake version 3.21+
  4. Click Apply and let Android Studio download and install
  5. Create android/local.properties:
    cp android/local.properties.template android/local.properties
    # Edit and set sdk.dir to your Android SDK path

Game Assets on Android

Game assets are linked via symbolic links in android/app/src/main/assets/:

android/app/src/main/assets/ ├── data → ../../../../../gamefiles/DATA ├── image08 → ../../../../../gamefiles/IMAGE08 ├── image16 → ../../../../../gamefiles/IMAGE16 └── sound → ../../../../../gamefiles/SOUND

If symbolic links don't work, copy the directories instead:

cd android/app/src/main/assets
cp -r ../../../../../gamefiles/DATA  data
cp -r ../../../../../gamefiles/IMAGE08 image08
cp -r ../../../../../gamefiles/IMAGE16 image16
cp -r ../../../../../gamefiles/SOUND  sound

Build

cd android
./gradlew assembleDebug

Output: android/app/build/outputs/apk/debug/app-debug.apk

Install and Run

# Clear previous install (resets extracted assets and save data)
adb shell pm clear org.openeggbert.freeeggbert

# Install
adb install -r android/app/build/outputs/apk/debug/app-debug.apk

# Launch
adb shell am start -n org.openeggbert.freeeggbert/.FreeEggbertActivity

Logcat Diagnostics

adb logcat | grep -E "SDL|FREEAPI|FREE_DIRECT|Eggbert|free-eggbert"
Log Tag / MessageMeaning
free-api: SDL_main startingSDL_main entry point reached
free-api: FreeApiAndroidSetup beginAndroid asset setup started
FREEAPI_ANDROID: smoke-test data/config.def EXISTSRequired config file found
free-api: asset extraction completeAll assets extracted successfully
FREE_DIRECT_PRESENT: source=…Render loop is running
FREE_DIRECT_INPUT: raw=…Input events being processed

Release APK

# Create a keystore
keytool -genkey -v -keystore android/free-eggbert-release.keystore \
        -alias free-eggbert -keyalg RSA -keysize 2048 -validity 10000

# Create android/key.properties with keystore credentials
# Build the signed release APK
cd android
./gradlew assembleRelease

Android Troubleshooting

ProblemSolution
INSTALL_FAILED_UPDATE_INCOMPATIBLE Uninstall first: adb uninstall org.openeggbert.freeeggbert
App exits immediately Asset extraction failed. Check logcat for free-api: entries. Verify symlinks in android/app/src/main/assets/
No audio Verify sound files are present in the APK. Check SDL_mixer WAV support.
Crash on launch Check logcat for native crash. Common: missing libmain.so or incomplete git submodule update

Rendering Across Platforms

Free Eggbert uses a fixed 640×480 logical resolution. On all platforms via FreeDirect, the game renders to this size and SDL3 handles scaling to the actual display:

  • Desktop: Windowed at 640×480, resizable with letterboxing
  • Web: Letterboxed to fit the browser window
  • Android: Letterboxed to the device screen, black bars on sides or top/bottom