Heroes of Might and Magic V (Android Port)

Heroes of Might and Magic V (Android Port) is a port of Heroes of Might and Magic V to Android with native touch controls and a Direct3D 9 to OpenGL ES bridge.

README

pc_decomp_homm5

Heroes of Might and Magic V, Hammers of Fate and Tribes of the East on Android ARM64 through static x86 recompilation, with native touch controls and a Direct3D 9 to OpenGL ES bridge.

Current version: 0.3.2-dev. All three engines use ThinLTO; Tribes of the East also uses PGO. This is an experimental port for sideloading. The original game's menus, adventure maps, towns, battles, sound and saves run through a local Win32 compatibility host. The game and eight DLLs are translated from x86 to C ahead of time, then compiled into a separate native library for each edition with the Android NDK.

Supported original games

The build inputs are the Russian releases of the three games from Heroes of Might and Magic 5: Bundle on GOG. Use your own installed copies. Other languages, executable revisions and mods are not verified build inputs.

Edition IDOriginal versionInput manifestNative library
toeTribes of the East 3.1config/modules.jsonlibhomm5.so
vanillaHeroes V 1.6config/vanilla.modules.jsonlibhomm5_vanilla.so
hofHammers of Fate 2.1config/hof.modules.jsonlibhomm5_hof.so

The SHA-256 values of each required executable and DLL are pinned in these manifests. The lifter verifies every input before copying, unpacking or relocating it. The original installation is read only; unpacked images and generated C go into ignored local directories. The launcher provides separate game cards, processes, settings and saves. Existing ToE paths remain compatible with earlier builds. Native patch contracts also pin the edition-specific host mappings and required port features; APK packaging requires all three patched libraries and resource overlays, including when --skip-native is used.

Vanilla 1.6 and HoF 2.1 have device smoke coverage for profile creation, scenario loading, adventure maps and troop splitting. Original-binary ABI checks and host regression tests cover their adapted native entry points. Device gameplay coverage is recorded separately from successful generation and compilation.

The older games use ToE's widescreen anchor policy with their own window geometry. Their font scaling follows ToE's equal horizontal and vertical glyph scale. Legacy UI and town-music overrides are also installed as loose resources: expansion archives take precedence over a new base PAK. Packaging and CLI deployment verify that every loose resource exactly matches its reviewed override archive. Ubisoft tracking is disabled before the startup commands in all three editions.

Original EXE/DLL files, resource archives, music, videos, maps and saves are not distributed in this repository. The town music XML resources are read from the selected edition's original resource banks during packaging, checked against its manifest in config/music/, and patched in a separate Android overlay.

Current capabilities and limits

  • Original menus, profile creation, scenario loading, adventure maps, towns, battles, saving and loading have been exercised during development.
  • Touch input supports selection, movement, camera gestures, tooltips, spell targeting, troop and artifact transfers, and the Android keyboard.
  • Additional game settings control render scale, interface scale, frame limit, camera sensitivity, touch cursor offset and optional touch cursor display. The default frame limit is 60 FPS.
  • LAN play has been tested between Android devices and between Android and the original PC game with ToE 3.1. Peers use the same original game version and data; those checks do not establish LAN coverage for the other editions. Network discovery depends on the local Wi-Fi network.
  • The launcher accepts an installed game folder or a ZIP containing its files. It can convert DXT textures to ASTC/ETC2 and skip the intro videos.
  • An optional launcher switch, off by default, applies the MMH5.5 New Creature Framework patches to ToE 3.1 (1000 creature records, IDs up to 999; random maps skip creatures without map objects): the Original group of NewCreatureFramework.yml. Enable it only for a mod that provides those creatures: without their files the game does not start. Vanilla and HoF are unaffected.
  • Every edition renders with OpenGL ES. The experimental DXVK (Vulkan/Turnip) renderer for ToE 3.1 is disabled in this build; see Renderers.
  • ARM64 Android 8.0 or later and OpenGL ES 3.2 are required. The manifest targets API 28; this development package uses legacy external storage access.
  • Native libraries and APK packaging support 16 KiB alignment. This does not establish testing on a device with 16 KiB memory pages.
  • Campaign completion, every map, every GPU and every Android version have not been verified. Development test results are not a complete compatibility claim.

Renderers (Tribes of the East)

Every edition, ToE 3.1 included, renders with the port's own Direct3D 9 to OpenGL ES 3.2 renderer.

An experimental DXVK renderer for ToE (DXVK 2.4.1 on the system Vulkan driver, or on a bundled Mesa Turnip driver on Adreno 6xx/7xx GPUs) is disabled for now: on the tested Adreno 650 with Turnip the game started, but the screen stayed black after the startup device reset. Its sources remain in the repository (android/dxvk, android/host/d3d9_dxvk.c), but the launcher offers only OpenGL ES, runs no graphics driver check and the APK carries no DXVK libraries. Re-enabling it for development takes the dxvk/turnip modes in the renderers of android/port.json (the launcher base then adds its Vulkan probe), HOMM5_DXVK in android/host/d3d9_dxvk.c and tools/build_game_apk.py --dxvk.

Build requirements

The current scripts build on Windows x64, without Gradle. Install:

ToolVersion
Python3.13 used during development
Android SDK platformplatforms;android-37.0 (the APK targets SDK 37)
Android SDK Build Toolsbuild-tools;36.1.0
Android NDK27.2.12479018
JDK17 or later; 24.0.1 used during development
UPX5.2.1, for unpacking the private copy of fmod.dll

The launcher is the shared port launcher base, a sibling project (../launcher, or an android/launcher submodule, or the folder named by PORT_LAUNCHER); tools/build_game_apk.py renders it into the package org.homm5.port.launcher from the game's description in android/port.json.

Python requirements are pinned in requirements.txt. The needed source snapshots of pcrecomp, MojoShader, dlmalloc, astcenc and etcpak are vendored in reference/; zlib sources support desktop regression tests. Versions, origins and local adaptations are recorded in dependencies.json and THIRD_PARTY_NOTICES.md. Keep the supplied MojoShader snapshot: a plain upstream checkout omits the legacy shader fixes used by this port.

Build the APK

Run these commands from the repository root:

python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -r requirements.txt

$env:ANDROID_HOME = Join-Path $env:LOCALAPPDATA "Android\Sdk"
$env:ANDROID_NDK = Join-Path $env:ANDROID_HOME "ndk\27.2.12479018"
$env:JAVA_HOME = Read-Host "JDK installation directory"
$homm5Upx = Read-Host "Path to upx.exe"

Copy-Item port.example.json port.json
notepad port.json

Set game_directory to the ToE installation, and set editions.vanilla.game_directory and editions.hof.game_directory to the base game and Hammers of Fate installations. A combined base-game/HoF installation can supply both paths, for example C:/Games/Heroes of Might and Magic V. Absolute paths and paths relative to the repository root are supported. Use forward slashes or escaped backslashes in JSON. The example uses originals/tribes-of-the-east. device is optional and is needed only for developer deployment tools. The personal port.json and the entire originals/ directory stay outside Git.

The scripts resolve the SDK using ANDROID_SDK, ANDROID_HOME, then ANDROID_SDK_ROOT, or the current account's usual Windows SDK location. ANDROID_NDK overrides the pinned NDK under that SDK. Set JAVA_HOME explicitly.

Generate the modules for all three editions, then compile and package:

foreach ($homm5Edition in @("toe", "vanilla", "hof")) {
    python -X utf8 tools/lift_homm5.py --edition $homm5Edition --upx $homm5Upx
    if ($LASTEXITCODE -ne 0) { throw "Generation failed: $homm5Edition" }
}
python -X utf8 tools/call_protocol.py
python -X utf8 tools/x87_locals.py
python -X utf8 tools/build_game_apk.py --thinlto --no-pgo

Output: build/android-game/heroes-v.apk.

tools/build_dxvk.py stages the ToE DXVK renderer in build/dxvk. It downloads nothing and rebuilds no DXVK source. It reuses the DXVK, adrenotools and Turnip binaries built by the sibling ut3 port, checks each one against its hash in dependencies.json, imports the needed DXVK Native headers into reference/dxvk, and compiles android/dxvk into libh5dxvk.so. --ut3, --dxvk-library, --turnip-runtime and --dxvk-headers select other locations. Run it again after changing android/dxvk or android/host/dxvk_methods.h; tools/generate_dxvk_bridge.py regenerates those tables from the pinned header. The APK builder packages the stage only with --dxvk (the renderer is disabled by default, see Renderers) and then refuses a stale stage. The DXVK libraries add about 6 MB to the APK and 24 MB when installed.

Packaging requires XZ Utils 5.4+ for its ARM64 branch filter (XZ, PATH, or Git for Windows' mingw64/bin/xz.exe). All three engines are stored as XZ assets with 128 MiB independent chunks. The small bundled XZ Embedded decoder unpacks only the selected engine in the background on its first start after an update. Its size and SHA-256 are checked before an atomic rename from .part; later starts reuse the completed copy in private app storage. Allow about 131 MB for ToE or 220 MB for each legacy engine, plus the previous copy while replacing it after an update. Compression changes APK size, not the game engines, resources or their installed sizes.

Generation creates checked PE images and C sources under generated/ for ToE, generated/vanilla/ for the base game and generated/hof/ for HoF. Function catalogs use the matching edition directory, while the original ToE catalog paths remain under config/. These are all local build outputs. Compilation fingerprints sources, headers, compiler and flags before reusing objects. The APK builder compiles the texture converter and Java launcher, includes the prepared PE images and port overlays, aligns the APK and signs it with a locally generated development key. Native builds remap embedded source paths to repository-relative names.

The two analysis steps produce build/call-protocol/summary.json and x87.json for ToE. Run them again after regenerating or repairing its lifted functions. The legacy editions use their own generated code and ABI mappings; they do not reuse ToE register or x87 analysis reports.

For lower memory use and fewer compilation workers:

foreach ($homm5Edition in @("toe", "vanilla", "hof")) {
    python -X utf8 tools/build_android.py --edition $homm5Edition --shared --thinlto --no-pgo --jobs 4 --thinlto-jobs 2
    if ($LASTEXITCODE -ne 0) { throw "Native build failed: $homm5Edition" }
}
python -X utf8 tools/build_game_apk.py --skip-native --thinlto --no-pgo

--no-pgo makes the documented build independent of local training profiles. Profiles stay outside Git and belong to their original engine: ToE uses build/pgo/homm5.profdata, Vanilla uses build/pgo-vanilla/homm5.profdata and HoF uses build/pgo-hof/homm5.profdata. To use available edition-specific defaults:

python -X utf8 tools/build_game_apk.py --thinlto

For an individual native build, --pgo-use PATH selects another compatible LLVM profile for that edition. Do not apply a ToE profile to the other engines. Performance can differ between builds using profiles and the profile-independent build above.

The signing key is created in build/android/debug.keystore. Keep your local key if you want subsequent APKs to update the same installation. Another key requires uninstalling the old package or signing with the original key. Keys and keystores are excluded from Git.

Install and provide game resources

  1. Install your APK on an ARM64 Android device and open Heroes V.
  2. Grant file access when the launcher asks: Android 11 and later need All files access; Android 8–10 ask for storage, and a permanently refused permission opens the app's settings. Choose the game folder (a folder inside or above it works too) or a ZIP. A folder holding several editions, for example Vanilla + HoF, is assigned to each of them. A ZIP is unpacked once into /sdcard/HeroesV/game, game-hof or game-vanilla (or a folder you pick), keeping only the folders the game reads and checking every file; a combined Vanilla + HoF archive is unpacked once for both. An offline GOG installer EXE is not a game ZIP.
  3. Vanilla 1.6 requires bin/, data/ and DataLS/, including DataLS/p6-data.pak. HoF 2.1 requires bina1/, base data/ and dataa1/; it does not use the Vanilla DataLS/ bank. ToE requires bin/ and data/.
  4. Texture conversion is optional and can take time and additional storage space.
  5. Start the game from the launcher; keep a separate copy of your original game files.

The launcher shows a card per edition with Play, or Return to the game while it runs. Options: skip the opening videos (per edition, on by default), the players for LAN over Tailscale/VPN, and New Creature Framework (ToE). On GPUs without DXT textures it converts them to ASTC or ETC2, can undo that, or delete the originals afterwards to save space (irreversible). Cleanup previews and removes the map editor, installers, manuals and store files. About shows the version, the GitHub page, the update check and the language (English or Russian). Updating from 0.3.x keeps the chosen game folders, edition and options; saves stay in /sdcard/HeroesV/userdata*. On Android 17 the launcher asks for local-network access before the first start.

The main menu's Переключить игру opens an Android dialog with the installed games and Открыть лаунчер. Switching saves the current engine's profile and closes it before starting the selected game. The ordinary exit dialog keeps its original confirmation buttons.

The application ID is org.homm5.port.

EditionDeveloper resource copyPrepared PE imagesSettings, profiles and saves
ToE/sdcard/HeroesV/game//sdcard/HeroesV/lifted//sdcard/HeroesV/userdata/
Vanilla/sdcard/HeroesV/game-vanilla//sdcard/HeroesV/lifted-vanilla//sdcard/HeroesV/userdata-vanilla/
HoF/sdcard/HeroesV/game-hof//sdcard/HeroesV/lifted-hof//sdcard/HeroesV/userdata-hof/

The launcher can also use another selected resource directory. Its writable folders remain under /sdcard/HeroesV/, as listed above.

Optional installation through ADB:

python tools/build_game_apk.py --skip-native --thinlto --install

If several devices are connected, pass --serial SERIAL or set ANDROID_SERIAL or HOMM5_SERIAL locally. No device identifier is included in the repository.

For the standalone developer executable, build and deploy the selected edition:

python tools/build_android.py --edition vanilla --no-pgo
python tools/deploy.py --edition vanilla --data --serial SERIAL

deploy.py defaults to toe; --edition hof selects HoF. It validates the selected executable's mandatory patch receipt before any device operation and again before uploading native code. --data copies only that edition's resource banks and shared assets, then applies private profile, intro, music and UI overrides. It never modifies the installed PC game. Omitting --data updates the prepared PE images and executable only. The executable is uploaded through a temporary file and renamed; no game is started automatically.

The script prints a launch command with explicit --game, --data and --images arguments; use those arguments for legacy executables because the bare executable's default paths are the historical ToE paths. Developer executables use /data/local/tmp/homm5[-vanilla|-hof]/homm5-arm64; deployment receipts are build/deploy-receipt[-vanilla|-hof].json.

Touch controls

GestureAction
TapOriginal selection or contextual action
Double tap your hero or townOpen its screen
Drag the mapMove the camera
Two fingersMove, zoom and rotate the camera
Two nearby fingers moving vertically togetherTilt the camera, including in battle
Brief simultaneous tap with three fingers on the fieldRestore camera tilt and rotation; in battle, also restore its position
Hold an interface elementShow its tooltip
Swipe left or right on a scenario's hero, faction, bonus, team or player-state iconCycle through the scenario's allowed choices. The active icon stays highlighted with its name and tooltip; heroes also show their specialization description
While aiming in battle, rest a second fingerShow the details card of the creature under the first finger at once; lift the second finger to hide it and keep aiming. Lifting the first finger while the second rests gives no order
While aiming in battle, tap with a second fingerShow the details card until the first finger lifts (no order is given); each further second-finger tap switches once between the card and the creature's effects
Tap a portrait on the battle initiative bar, then tap it againThe first tap selects the portrait (it stays highlighted); the second shows the creature's details card, which stays open without holding; each further tap on the portrait switches between the card and the effects; a touch elsewhere closes it
Hold a map or town object, or the battle initiative barShow its tooltip and details card; each second-finger tap on a held initiative bar portrait switches its card once
Drag your active shooter onto an enemy in battleMelee attack instead of a shot, like Ctrl+click on the PC
Brief simultaneous tap with two fingers on the adventure mapStop the moving hero or cancel an aim
Double tap the minimap, or tap it with two fingersToggle the highlighting of objects, like holding Alt on the PC; the camera stays. A single tap on the minimap moves the camera after a short pause (0.4 s)
Swipe left or right across the spellbookNext or previous page
Swipe up or down across the spellbookPrevious or next bookmark (school); the orcish book of ToE switches its page. A tap that misses a bookmark by a little takes the nearest one
Drag a stack during troop deploymentMove it between the field and the reserve slots; the dragged portrait stays centred under the finger
Hold a deployment reserve portrait and tap the reserve bar with a second fingerSplit one creature into the next free reserve slot per tap. Hold the second finger on a destination slot to open the original army split dialog
Swipe from a dropdown, move to an item and releaseSelect that item; a second finger cancels
Swipe on a sliderMove its value from where it is; a tap or hold does not move it
Swipe a listScroll
Drag troops or artifactsT