Skip to content

Repository files navigation

AirStrike 3D Logo

AirStrike 3D — Reverse Engineering Toolkit

Reverse engineering the AirStrike 3D game series

CI Status Release Status License MIT CMake 3.31+ C++26 Python 3.13 GitHub Issues GitHub Stars Last Commit

PCGamingWiki · Original Game · Community (Reddit)


Table of Contents


Overview

My nostalgic journey into reverse engineering AirStrike 3D — the first PC game that captured my imagination as a kid. This repository contains tools and research for understanding the game's internals.

overlay preview

overlay wireframe


Project Status

Active research & tooling.
This project is a living archive. Engine analysis is ongoing, new tools are added as formats are documented, and the Ghidra database is updated with fresh discoveries. Contributions from fellow reverse engineers and preservationists are welcome.

Milestone Status
.apk archive format ✅ Documented & tooling complete
.mdl model format ✅ Bidirectional converter
Save file format ✅ Decryption + preview
ASProtect 1.0 unpacking ✅ Static unpacker + manual guide
Engine v2.06 analysis 🔄 In progress (Ghidra)
Engine v2.51 analysis 🔄 In progress (Ghidra)
Engine v2.71 analysis 🔄 In progress (Ghidra)
v2.50 / Air Force Missions ⏳ Pending (tracked in #1)

Repository Structure

airstrike3d-tools/
├── .github/              # Branding assets & templates
├── 2_06/                 # AirStrike 2 (engine v2.06) binaries & data
├── 2_51/                 # AirStrike 2 D3D8 (engine v2.51) binaries & data
├── 2_71/                 # Gulf Thunder (engine v2.71) binaries & data
├── cmake/                # CMake modules, toolchains & code-quality configs
├── external/             # Vendored dependencies (GLAD, etc.)
├── ghidra/               # Ghidra project files for both game versions
├── scripts/              # Python tooling
│   ├── level_viewer.py
│   ├── mdl_obj_converter.py
│   ├── paktool.py
│   ├── save_editor.py
│   └── static_exe_unpacker.py
└── src/                  # C++ source code
    ├── game/             # Decompiled/reconstructed game logic (C)
    └── proxy/            # BASS proxy DLL for runtime injection & overlay

About the Game

AirStrike 3D is a helicopter shoot-em-up series developed by DivoGames (Nizhny Novgorod, Russia) and published through Alawar Entertainment. The engine and all three franchise titles were built by a two-person team.

Developers

Name Role Links
Anton Petrov Engine architect, CTO & co-founder LinkedIn
Dmitry Zakharov Co-founder

Both names are embedded as string literals ({Anton Petrov}, {Dmitry Zakharov}) in the Gulf Thunder executable's credits data. Petrov describes the engine on LinkedIn as "my first game engine featuring a custom scripting language and hardware-accelerated 3D graphics — powered three titles in the Air Strike 3D franchise".

After DivoGames, Petrov became CTO at Game Insight (2012–2019, Nizhny Novgorod department), then co-founded Colossi Games in Cyprus (2020–present).

Deaddybear → DivoGames

Before DivoGames was officially founded (~2004), the initial AirStrike chapters were developed under a group called Deaddybear. Community research on r/airstrike3d found that Deaddybear's earlier game Treasure Mole used a nearly identical .pak archive format — confirming shared codebase ancestry. Deaddybear also released Bomberman vs Digger (2002).

Franchise Timeline

Year Title Publisher Genre Engine Known Alias
2002 AirStrike 3D: Operation W.A.T. Alawar Helicopter shooter v1.x (OpenGL, Deaddybear era) Air Assault 3D, Air Hawk
2004 AirStrike 2 Alawar / self Helicopter shooter v2.06 (OpenGL 1.1, MSVC 7.0) АвиаНалет 2 (ru)
2005 AirStrike 2 D3D8 Alawar / self Helicopter shooter v2.51 (Direct3D 8, MSVC 7.1)
2005 AirStrike II: Gulf Thunder Alawar Helicopter shooter v2.71 (Direct3D 8, MSVC 8.0) Desert Hawk
2007 Air Force Missions MyPlayCity Helicopter shooter v2.50 (unconfirmed, same engine lineage)
2007 Space Strike MyPlayCity Space shooter unknown Galaxy Strike, Звёздный Удар

Known retail rebrands (same binary, different publisher skin):

  • AirStrike 3D: Operation W.A.T."Air Assault 3D" / "Air Hawk"
  • AirStrike II: Gulf Thunder"Desert Hawk"
  • Space Strike"Galaxy Strike"

AirStrike 2 D3D8 (v2.51) is the Direct3D 8 port of AirStrike 2 — same game, updated graphics backend. Not to be confused with Gulf Thunder (v2.71), which is a distinct title with new content.

Air Force Missions and Space Strike are distinct DivoGames titles — separate from the Alawar-published trilogy — released in 2007 under a MyPlayCity distribution deal. Air Force Missions is a helicopter shooter sharing visible engine DNA with Operation W.A.T. (version string 2.50 observed in binary); Space Strike is a space shooter, unrelated gameplay-wise. Neither title's asset format compatibility with v2.06/v2.71 tooling has been confirmed — requires binary diff. Issue tracked at #1.

DivoGames was acquired by Game Insight in 2012; both 2007 titles are now part of that catalog.

graph TD
    %% People
    AP["👤 Anton Petrov\nEngine Architect · CTO · Co-founder"]
    DZ["👤 Dmitry Zakharov\nCo-founder"]

    %% Orgs / Groups
    DB["🐻 Deaddybear\n~2000–2004"]
    DG["🏢 DivoGames Ltd.\nNizhny Novgorod · 2004–2012"]
    GI["🏢 Game Insight NN\nNizhny Novgorod · 2012–2019"]
    CG["🏢 Colossi Games\nCyprus · 2020–present"]
    AL["📦 Alawar Entertainment\nPublisher"]
    MPC["📦 MyPlayCity\nPublisher"]

    %% Engine lineage
    ENG1["⚙️ Engine v1.x\nOpenGL · Deaddybear era"]
    ENG206["⚙️ Engine v2.06\nOpenGL 1.1 · MSVC 7.0\ncompiled 2004-05-15"]
    ENG250["⚙️ Engine v2.50\nunconfirmed · same lineage"]
    ENG251["⚙️ Engine v2.51\nDirect3D 8 · MSVC 7.1\nAirStrike 2 D3D8 port"]
    ENG271["⚙️ Engine v2.71\nDirect3D 8 · MSVC 8.0\ncompiled 2007-05-15"]

    %% Games
    TM["🎮 Treasure Mole\n.pak format — shared codebase"]
    BVD["🎮 Bomberman vs Digger\n2002"]
    AS1["🎮 AirStrike 3D: Op. W.A.T.\n2002"]
    AS2["🎮 AirStrike 2\n2004"]
    AS2D3D["🎮 AirStrike 2 D3D8\n2005"]
    GT["🎮 AirStrike II: Gulf Thunder\n2005"]
    AFM["🎮 Air Force Missions\n2007"]
    SS["🎮 Space Strike\n2007"]

    %% Aliases
    AA["🏷️ Air Assault 3D\nAir Hawk"]
    DH["🏷️ Desert Hawk"]
    GS["🏷️ Galaxy Strike\nЗвёздный Удар"]

    %% Evidence nodes
    EV1["🔍 EVIDENCE\nString literals in Gulf.exe:\n{Anton Petrov} {Dmitry Zakharov}"]
    EV2["🔍 EVIDENCE\nLinkedIn: 'my first game engine\npowered three titles'"]
    EV3["🔍 EVIDENCE\nMSVC RTTI: .?AVIntroPageDivoGames@@\nDivo Master debug string"]
    EV4["🔍 EVIDENCE\nr/airstrike3d research:\n.pak format shared with Treasure Mole"]

    %% Acquisition
    ACQ["📋 Acquisition 2012\nGame Insight buys DivoGames"]

    %% People → Orgs
    AP --> DB
    DZ --> DB
    DB -->|"~2004 rebranded/founded"| DG
    AP -->|"CTO · co-founder"| DG
    DZ -->|"co-founder"| DG
    AP -->|"CTO 2012–2019"| GI
    AP -->|"co-founded 2020"| CG
    DG -->|"acquired by"| ACQ
    ACQ --> GI

    %% Evidence links
    EV1 -.->|"confirms"| AP
    EV1 -.->|"confirms"| DZ
    EV2 -.->|"confirms"| AP
    EV3 -.->|"confirms"| DG
    EV4 -.->|"confirms"| DB

    %% Engine lineage
    ENG1 -->|"evolved to"| ENG206
    ENG206 -->|"evolved to"| ENG271
    ENG206 -->|"D3D8 port"| ENG251
    ENG206 -.->|"possible fork"| ENG250

    %% Games → Engine
    AS1 --> ENG1
    AS2 --> ENG206
    AS2D3D --> ENG251
    GT --> ENG271
    AFM --> ENG250
    SS -.->|"engine unknown"| DG

    %% Deaddybear games
    DB --> TM
    DB --> BVD
    DB --> AS1

    %% DivoGames games
    DG --> AS2
    DG --> AS2D3D
    DG --> GT
    DG --> AFM
    DG --> SS

    %% Publishers
    AL -->|"published"| AS1
    AL -->|"published"| AS2
    AL -->|"published"| AS2D3D
    AL -->|"published"| GT
    MPC -->|"published"| AFM
    MPC -->|"published"| SS

    %% Aliases
    AS1 -.->|"rebrand"| AA
    GT -.->|"rebrand"| DH
    SS -.->|"rebrand"| GS

    %% Styling
    classDef person fill:#1a3a5c,stroke:#4a9eda,color:#e8f4fd
    classDef org fill:#1a2a1a,stroke:#4aaa4a,color:#e8fde8
    classDef engine fill:#2a1a3a,stroke:#9a4aed,color:#f0e8fd
    classDef game fill:#2a1a1a,stroke:#ed6a4a,color:#fde8e8
    classDef alias fill:#1a2a2a,stroke:#4aaaaa,color:#e8fdfd,stroke-dasharray:4 2
    classDef evidence fill:#2a2a1a,stroke:#aaa04a,color:#fdfde8,stroke-dasharray:2 2
    classDef event fill:#2a1a2a,stroke:#aa4a6a,color:#fde8f0

    class AP,DZ person
    class DB,DG,GI,CG,AL,MPC org
    class ENG1,ENG206,ENG250,ENG251,ENG271 engine
    class TM,BVD,AS1,AS2,AS2D3D,GT,AFM,SS game
    class AA,DH,GS alias
    class EV1,EV2,EV3,EV4 evidence
    class ACQ event
Loading

Engine Internals

Custom C++ engine with no third-party framework. Uses Quake-style subsystem prefixes:

Subsystem Naming

Subsystem Prefix Examples
Game logic G_ G_LoadBin, G_LoadLevelList
Renderer R_ R_LoadModel, R_RegisterModel, R_RegisterShadow
Sound S_ S_Init, S_RegisterSound
Window MW_ MW_CreateWindow

Graphics API Evolution

Version API Compiler Compile timestamp Rich header
v2.06 (as3d2.exe) OpenGL 1.1 (opengl32.dll, glu32.dll) MSVC 7.0 (.NET 2002/2003) 2004-05-15 10:12:58 UTC
v2.51 (as3d2.exe) Direct3D 8 (d3d8.dll) MSVC 7.1 (.NET 2003)
v2.71 (Gulf.exe) Direct3D 8 (d3d8.dll) MSVC 8.0 (VS2005) 2007-05-15 13:49:28 UTC

Third-Party Libraries

  • BASS — Audio library. 3D positional audio, EAX effects, MO3/tracker module playback.
  • libjpegCopyright (C) 1996, Thomas G. Lane (found in Gulf exe strings).
  • zlib + libpng — PNG texture support.
  • Custom scripting language — Confirmed by Petrov on LinkedIn, no public documentation survived.

Asset Formats

Format Extension Description
Archives .apk Custom encrypted containers (XOR, 1024-byte key table). Not Android APK.
Models .mdl Custom 3D format with version checks (R_LoadModel: Illegal model version.)
Textures .tga Standard Targa. Organized in gfx/, menu/, tiles/ dirs.
Levels maps/levels.txt Plaintext level list (encrypted inside .apk)
Audio .mo3 Tracker modules via BASS library
Config config.ini Plaintext, stored alongside the executable

RTTI / C++ Details

MSVC RTTI type descriptors found in the Gulf binary (e.g. .?AVIntroPageDivoGames@@), confirming C++ with virtual inheritance and RTTI enabled. Divo Master string suggests an internal tool or debug mode.


ASProtect 1.0 Analysis

The v2.06 executable (as3d2.exe, 199,680 bytes) is packed with ASProtect 1.0 by Alexey Solodovnikov.

Identification

Indicator Value Meaning
Entry point .data section (0x1DB3001) Packer stub, not original code
EP signature 60 E8 01 00 00 00 PUSHAD + CALL +1 — textbook ASProtect 1.0
Section flags All 0xC0000040 (RWX) Packer rewrites all section attributes
.text entropy 8.00 (maximum) Fully encrypted/compressed
Visible IAT 3 imports: GetProcAddress, GetModuleHandleA, LoadLibraryA Real IAT resolved at runtime
Compression aPLib (LZ77 variant) See scripts/static_exe_unpacker.py
Hashes MD5: 1ba6f0187c43d07587e5212f1cb14190 SHA256: bc68bf37...81fb1a

How It Works

  1. Section wiping — Original section names erased, all flags set to 0xC0000040. Two .data stubs appended.
  2. aPLib decompression — Compressed .text stored in oversized .data (VirtSize 30 MB, RawSize 4 KB).
  3. OEP byte stealing — First bytes of Original Entry Point executed inside the stub before jumping to OEP+N.
  4. IAT redirection — Import calls routed through ASProtect memory; executes first instructions of real API in-place, then jumps mid-body.
  5. Anti-debugIsDebuggerPresent(), RDTSC timing, SEH breakpoint detection, debugger driver CreateFile() probes.
  6. Checksums — Code integrity verification to detect runtime patching.
  7. Anti-disasm — Junk bytes after CALL instructions break linear-sweep disassemblers (W32DASM, SOURCER); IDA handles fine.

v2.51 / v2.71 — No Protection

Both v2.51 and v2.71 ship completely unprotected — no ASProtect, no packing, no anti-debug tricks:

Version EP location Entropy IAT Strings
v2.51 (as3d2.exe) .text normal Full Readable
v2.71 (Gulf.exe) .text 6.83 Full Developer credits, error strings plainly readable

Much better targets for engine analysis compared to the ASProtect-wrapped v2.06.


Toolkit

APK Archive Extraction

# Extract game assets from encrypted .apk archives
python scripts/paktool.py extract pak0.apk          # Extract all files
python scripts/paktool.py pack extracted_dir/ new.apk  # Repack modified assets

MDL ↔ OBJ Converter

python scripts/mdl_obj_converter.py some_file.mdl
python scripts/mdl_obj_converter.py some_file.obj

Save Previewer

python scripts/save_editor.py decrypt game.bin -o decrypted.bin

Audio Conversion

# Convert MO3 tracker modules to standard audio
sudo dnf install libopenmpt openmpt123
openmpt123 --render file.mo3 --output file.wav

Graphics Viewing

# Best TGA texture viewer for Linux
# https://github.com/bluescan/tacentview
tacentview texture.tga

Linux Compatibility

Running via Steam Proton (Fedora + AMD GPU)

# Fix OpenGL extension issues for old games
MESA_EXTENSION_MAX_YEAR=2003 %command%

Add this to the game's launch options in Steam.

Technical Notes

  • Archive Format: Custom encrypted APK containers (not Android APK)
  • Executable: ASProtect v1.0 packed (detected via YARA rules)
  • Assets: TGA textures, MDL 3D models, MO3 audio modules
  • Encryption: XOR cipher with 1024-byte key table

Build & Development

Prerequisites

  • CMake 3.31 or newer
  • Python 3.13
  • Ninja (used by non-MSVC presets)
  • Clang (for native Windows builds) or LLVM-MinGW (for Linux → Windows cross-compilation)
  • Visual Studio 2022 (optional, for local MSVC builds)

Quick Start

  1. Clone this repository
  2. Extract game assets: python scripts/paktool.py extract /path/to/pak0.apk
  3. Browse extracted files in the created directory
  4. Convert audio files as needed

Building C++ Components

Linux → Windows (cross-compile via LLVM-MinGW)

  1. Download llvm-mingw from mstorsjo/llvm-mingw releases:

    • llvm-mingw-YYYYMMDD-ucrt-ubuntu-20.04-x86_64.tar.xz for Windows 10+ (UCRT)
    • llvm-mingw-YYYYMMDD-msvcrt-ubuntu-20.04-x86_64.tar.xz for Windows 7+ (legacy CRT)
  2. Extract to repository root in directory llvm-mingw

  3. Run:

cmake --preset llvm-mingw-i686
cmake --workflow --preset llvm-mingw-i686-release

Note: The preset uses jobs=1 due to an LLD linker deadlock on parallel linking in the MinGW context.

Windows (native Clang)

cmake --preset clang_windows_x86
cmake --workflow --preset clang_windows_x86-release

Uses pure Clang (clang/clang++ GNU driver) targeting 32-bit Windows with the Ninja Multi-Config generator. This is the recommended fast path for CI and local Windows builds.

Windows (optional, Visual Studio 2022)

cmake --preset msvc
cmake --workflow --preset msvc-release

Available for local development when Visual Studio 2022 is preferred. Not used in CI.

Game Configuration

The build system automatically generates config.ini for each game version during deployment. This ensures the game starts directly with sensible defaults instead of showing the launcher configuration window.

Default Settings

Each game version owns its template:

Version Template Notes
2.06 2_06/config.ini.in OpenGL defaults
2.51 2_51/config.ini.in Direct3D 8 defaults
2.71 2_71/config.ini.in Operation Gulf / Direct3D 8

Shared Lua plugins live in lua/ and are copied into each deploy tree as runtime plugins/.

Customizing Configuration

Edit the version template, then rebuild/deploy:

# 2_51/config.ini.in
[Display]
VideoMode=10
Fullscreen=0
WaitVSync=1

Why Auto-Generate?

The original games shipped without config.ini and required users to configure settings via a launcher dialog on first run. This automated approach:

  • Eliminates manual setup — games start immediately with tested defaults
  • Keeps version-specific settings — OpenGL and D3D8 configs stay separate
  • Supports automation — CTest can launch games without human intervention
  • Preserves defaults — templates track optimal settings for modern systems

Testing with CTest

The project includes CTest integration for validating game deployment and Proton launcher functionality across all three game versions (2_06, 2_51, 2_71).

Test Structure

Test Purpose Platform Typical Duration
deploy_fixture Build target and stage deployment artifacts Any <30s
deploy_files_exist Verify exe, DLLs, data, config.ini present Any <1s
proton_available Detect Proton + Steam Linux Runtime Linux only <2s
emulator_launch Launch game via Proton, verify it starts Linux + Proton 5–30s

Total: 12 tests (4 per version × 3 versions)

All tests use CTest fixtures to ensure deployment completes before validation. Non-Linux hosts automatically skip Proton tests via SKIP_REGULAR_EXPRESSION.

Running Tests

Quick validation (deployment only, cross-platform):

ctest --test-dir build/llvm-mingw-i686 --label-regex deploy --output-on-failure

Full emulator validation (Linux with Proton installed):

# Run all tests for a specific version
ctest --test-dir build/llvm-mingw-i686 -R "2_71" --output-on-failure

# Run all tests across all versions
ctest --test-dir build/llvm-mingw-i686 --output-on-failure --parallel

Using presets (recommended for local testing):

# Workflow preset WITH tests (requires Steam + Proton installed)
cmake --workflow --preset llvm-mingw-i686-release-with-tests

# Fast iteration: Debug build + tests, no packaging
cmake --workflow --preset llvm-mingw-i686-debug-with-tests

Note: Standard CI workflow presets (llvm-mingw-i686-release, clang_windows_x86-release, msvc-release) do NOT include tests to avoid requiring Steam/Proton on CI runners. Use the -with-tests variants for local development. The -debug-with-tests variants skip packaging for faster iteration.

Configuration

Cache variables:

Variable Default Description
AS3D_ENABLE_TESTS ON Build and register CTest emulator tests
AS3D_EMULATOR_TEST_TIMEOUT 5 Timeout (seconds) for emulator launch tests

Adjust timeout for slow CI runners or fast local iteration:

# Increase timeout for CI
cmake --preset llvm-mingw-i686 -DAS3D_EMULATOR_TEST_TIMEOUT=15

# Disable tests entirely (faster configure)
cmake --preset llvm-mingw-i686 -DAS3D_ENABLE_TESTS=OFF

Development Workflow

Iterating on deployment logic:

# 1. Modify deploy_game.cmake or version CMakeLists.txt
# 2. Reconfigure (CTest picks up changes automatically)
cmake --preset llvm-mingw-i686

# 3. Run only deploy-tier tests (fast feedback)
ctest --test-dir build/llvm-mingw-i686 --label-regex "deploy;fixture" --output-on-failure

# 4. When deploy passes, run full suite
ctest --test-dir build/llvm-mingw-i686 --output-on-failure

Fast iteration with debug presets:

# Use debug-with-tests preset for rapid development cycles
# Skips packaging, includes debug symbols, runs all tests
cmake --workflow --preset llvm-mingw-i686-debug-with-tests

# Make changes, rebuild, and test again (much faster than release)
# Edit code...
cmake --build build/llvm-mingw-i686 --config Debug
ctest --test-dir build/llvm-mingw-i686 --output-on-failure

# When satisfied, switch to release for final validation
cmake --workflow --preset llvm-mingw-i686-release-with-tests

Debugging a failing test:

# Verbose output + stop on first failure
ctest --test-dir build/llvm-mingw-i686 -R "emulator_launch_2_71" --verbose --stop-on-failure

# Inspect deployment directory manually
ls -la build/llvm-mingw-i686/2_71/

# Check DLL specifically
file build/llvm-mingw-i686/2_71/bass.dll
objdump -p build/llvm-mingw-i686/2_71/bass.dll | grep -A5 "DLL Name"

# Run emulator directly (bypass CTest)
./build/llvm-mingw-i686/2_71/run_game.sh --debug

Test Output

CTest prints short progress by default. For detailed diagnostics:

# Full output (stdout + stderr from each test)
ctest --test-dir build/llvm-mingw-i686 --output-on-failure --verbose

# JSON output (for CI parsing)
ctest --test-dir build/llvm-mingw-i686 --output-junit test-results.xml

Troubleshooting

Symptom Cause Fix
PROTON_SKIP on Linux Steam not found or Proton not installed Install Steam + Proton, verify ~/.steam/steam/steamapps/common/Proton* exists
deploy_fixture_* fails Build incomplete or missing game binaries Run cmake --build build --target deploy_game_<version> first
emulator_launch_* fails Game crashed or failed to start Check test output for exit code, inspect logs/*.log in deploy dir
All tests pass but game doesn't run Proton prefix corrupted Delete ~/.proton_prefixes/<exe>/ and retry

Ghidra Project

🔒 Since the v2.06 executable is protected with ASProtect 1.0, I opted for a straightforward approach on Linux: attach a simple debugger and single-step until the unpacking loop surfaces. The game unpacks itself in-place, spawning threads along the way — at some point the debugger detaches into ntdll magic 🪄. The trick is to pause at any moment and grab the address of the function you're interested in (e.g., the main loop).

🎯 The next step is using x64dbg with the DumpEx plugin — dump at the address of the main loop function. And that's all!

📊 Stats:

  • 📦 Game size: 31.2 MB
  • 🔍 In the Ghidra project I have marked some of the interesting places:
    • 🎮 Loading models
    • 💾 Working with saves
    • 🔧 Core game mechanics

🚀 Usage:

Just clone and open with Ghidra — the project is ready to explore yourself!

Maybe some time someone will reverse it completely 😏 🦀⚡


Contributing

We welcome contributions from reverse engineers, preservationists, and enthusiasts. Please see .github/contributing.md for guidelines on coding standards, commit conventions, and the pull request workflow.


Legal Notice

Educational and preservation purposes only. Respect original copyrights.

This project is intended for research, education, and game preservation. All game binaries, assets, and trademarks are property of their respective owners (DivoGames / Game Insight / Alawar Entertainment / MyPlayCity). Do not use these tools to circumvent copy protection for commercial gain or to distribute copyrighted material without authorization.


License

This repository is licensed under the MIT License.

Because knowledge should be free, just like the joy of playing games.


Acknowledgments

To that old PC that could barely run the game but somehow made it magical anyway.


Related Resources

About

Toolkit for AirStrike 3D game analysis and apk asset extraction/package + save file view. For educational research and game preservation.

Topics

Resources

Contributing

Security policy

Stars

23 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages