Architecture Overview

Free Eggbert's architecture closely mirrors the original Win32/DirectX 3 game structure, reconstructed from binary analysis. The codebase is structured as a set of cooperating C++ classes managed through a classic Win32 application model.

Layered Architecture

┌──────────────────────────────────────┐
│       Game Logic (C++20)             │
│  blupi.cpp / CDecor / CEvent / …     │
├──────────────────────────────────────┤
│  Free API  (Win32 → cross-platform)  │
│  Free Direct (DirectX → SDL3)        │
├──────────────────────────────────────┤
│  SDL3 / SDL_image / SDL_mixer        │
└──────────────────────────────────────┘

Game code only includes WinAPI-like and DirectX-like compatibility headers (windows.h, ddraw.h, dsound.h, etc.). SDL is never included directly by game source files.

Main Entry Point

Source file: src/blupi.cpp
Entry function: WinMain() (Win32-style)

blupi.cpp contains the application bootstrap, window creation, message loop, and top-level object instantiation.

Initialization Sequence

  1. Create the application window
  2. Instantiate CPixmap — DirectDraw surface manager
  3. Call CPixmap::Create() — initialize display surfaces
  4. Call CPixmap::CacheAll(TRUE, …) — load all image assets
  5. Instantiate CSound — audio system
  6. Instantiate CNetwork — DirectPlay (fails silently in most configs)
  7. Instantiate CDecor — game world and logic engine
  8. Instantiate CEvent — input handler and game phase dispatcher
  9. Start multimedia timer for game tick updates
  10. Enter the main message loop
⚠️
Known Initialization Issue
The decompiled code calls CPixmap::Create() twice during startup — once directly, and once inside CPixmap::CacheAll(TRUE, …). This is a suspected decompilation artifact. Real DirectDraw may reject the second initialization. See Known Issues.

Main Game Loop

The application uses a classic Win32 message loop with a multimedia timer:

// WinMain (blupi.cpp) — simplified
g_updateTimer = timeSetEvent(g_timerInterval, g_timerInterval / 4,
                             TimerStep, NULL, TIME_PERIODIC);

while (TRUE) {
    if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
        if (!GetMessage(&msg, NULL, 0, 0))
            return msg.wParam;
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    } else {
        if (!g_bActive) WaitMessage();
    }
}

The timer callback posts update messages. The window procedure handles WM_UPDATE by calling:

UpdateFrame();
SetDecor();
g_pPixmap->Display();

Core Classes

ClassHeaderSource(s)Responsibility
CDecor include/decor.hpp src/decor*.cpp Central game engine — world state, all game logic
CEvent include/event.hpp src/event.cpp Input handling, game phase dispatch, keyboard/mouse
CPixmap include/pixmap.hpp src/pixmap.cpp DirectDraw surface manager, sprite blitting
CSound include/sound.hpp src/sound.cpp, src/soundbass.cpp Audio playback — DirectSound or BASS backend
CNetwork include/network.hpp src/network.cpp DirectPlay session management, multiplayer
CButton include/button.hpp src/button.cpp UI button rendering and hit-testing
CJauge include/jauge.hpp src/jauge.cpp Progress/gauge bar rendering
CMenu include/menu.hpp src/menu.cpp Menu rendering and navigation

CDecor Source File Decomposition

CDecor is the most important class. Its implementation is split across 8 source files by responsibility:

FileResponsibility
src/decor.cppInitialization, rendering, main step loop, region/music
src/decblupi.cppPlayer movement, jumping, vehicle logic, physics
src/decblock.cppBlock/crate interaction, push/pull, door logic
src/decmove.cppMoving object (enemy/bomb/effect) step logic
src/decnet.cppNetwork game synchronization and event dispatch
src/decdesign.cppLevel editor — place/remove tiles and objects
src/decio.cppSave/load game state to .blp files
src/dectables.cppStatic lookup tables for decor

World Grid System

The game world is a 100×100 grid of cells. Each cell stores a sprite index into the background/decor spritesheet.

  • Grid dimensions: MAXCELX × MAXCELY = 100×100
  • Object sprites: 64×64 px (DIMOBJX, DIMOBJY)
  • Player sprites: 60×60 px (DIMBLUPIX, DIMBLUPIY)
  • Explosion sprites: 128×128 px (DIMEXPLOX, DIMEXPLOY)

Render Channel System

CPixmap manages 15 independent DirectDraw surfaces, each identified by a channel constant. Layers are composited in order to build the final frame:

ChannelValueContent
CHBACK0Background terrain
CHOBJECT1Foreground objects and pickups
CHBLUPI2Player character
CHDECOR3Decorative overlays
CHBUTTON4UI buttons
CHJAUGE5Gauge/progress bars
CHTEXT6Normal text characters
CHLITTLE7Small text characters
CHMAP8Minimap
CHEXPLO9Explosion sprites
CHELEMENT10Particle/element effects
CHBLUPI1–311–13Player character variants (multiplayer)
CHTEMP14Temporary scratch surface

See Image Channels for the full reference.

Game Phase System

Game state is controlled by game phases, dispatched as Windows messages (WM_USER + N) and handled by CEvent. Examples:

  • WM_PHASE_INIT — startup initialization
  • WM_PHASE_PLAY — normal gameplay
  • WM_PHASE_WIN / WM_PHASE_LOST — end of level
  • WM_PHASE_SETUP — settings screen
  • WM_PHASE_BUILD — level editor
  • WM_PHASE_SERVICE / SESSION / MULTI — multiplayer lobby

See Game Phases Reference for all 60+ phases.

Moving Object System

Up to 200 moving objects (MAXMOVEOBJECT) can be active simultaneously. They are tracked in a fixed-size MoveObject[200] array within CDecor. Each object has a type (TYPE_*), position, animation state, and motion trajectory.

Object types include enemies (fish, bird, wasp), vehicles, bombs, collectibles, and visual effects. See Object Types.

Player State

Unlike the Planet Blupi engine which uses a Blupi[100] array, Free Eggbert stores the player state as individual member fields directly in CDecor (m_blupiPos, m_blupiAction, m_blupiDir, etc.).

In multiplayer, up to 4 players are tracked via network packets.

Audio Architecture

Audio is managed by CSound with two selectable backends controlled by the _BASS compile flag:

  • Default (_BASS=FALSE): src/sound.cpp — DirectSound/MCI path
  • BASS library (_BASS=TRUE): src/soundbass.cpp — BASS + BASSMIDI

See Audio System for full documentation.