
The Lost Vikings (LostVikings port)
Port nativo de The Lost Vikings (LostVikings port) para PC.
The Lost Vikings (LostVikings port)
Port nativo basado en su ficha de origen.
README del repositorio
Lost Vikings
A native Linux / Windows port of the 1992 DOS game The Lost Vikings
(Silicon & Synapse / Interplay). Built by feeding the original 16-bit MS-DOS
executable through an m2c decompiler and wrapping the resulting C++ in an
SDL2 shell. A parallel v2 mirror is being grown alongside the
m2c-decompiled code with the goal of fully replacing the original VM
bytecode-for-bytecode, frame-for-frame. Sound is NATIVE: the original
Miles/AIL driver blob (SBPFM.ADV) from DATA.DAT runs inside an 8086
interpreter and its OPL3 writes are rendered by the Nuked core — the same
register stream a real Sound Blaster Pro got in 1992.
Discussion: the Telegram chat t.me/LostVikingsRE — questions, bug reports, the reverse engineering itself; GitHub issues work as well.

⚠️ Requires the copyrighted
DATA.DATfrom a legal copy of the original game. All build modes need it —DATA.DATholds the game content (levels, sprites, audio); the bundled*_static.binfiles only replace the static EXE image, not the game assets.
Pre-built downloads
Every push to any branch refreshes the rolling latest release with six
artifacts — the game for three platforms, and the test-mode build (the m2c
original and the v2 mirror in lockstep, every frame cross-checked: the
verification build, not the one to play) for the same three:
| File | Platform | Build |
|---|---|---|
vikings-linux-x86_64.tar.gz | Linux x86_64 | the game |
vikings-windows-x86_64.zip | Windows x86_64 | the game |
vikings-linux-arm64.tar.gz | Linux arm64 (aarch64) | the game |
vikings-linux-x86_64-test.tar.gz | Linux x86_64 | test mode |
vikings-windows-x86_64-test.zip | Windows x86_64 | test mode |
vikings-linux-arm64-test.tar.gz | Linux arm64 (aarch64) | test mode |
All six bundles need DATA.DAT supplied separately (see below).
Each archive contains:
vikings/vikings.exe— the gamevikings_keymap_editor/.exe— GUI key-rebind utilityvikings_keymap.cfg— default key bindings (editable)ds_static.bin,exe_static.bin— DS/EXE snapshots used by the v2 mirrorREADME.md
SDL2 and the C++ runtime are statically linked, so no extra .so / .dll
needs to live next to the executable. The Linux bundles depend on glibc
alone (2.35 or newer — Ubuntu 22.04, Debian 12 and everything later):
their SDL2 is built from source with the dlopen'ed backends, so X11,
Wayland, PulseAudio, PipeWire, ALSA and libdecor are picked up at run time
from whatever the machine has, none of them is a link-time dependency.
Get the latest from the Releases page.
DATA.DAT
DATA.DAT is the original 1992 asset file (levels, sprites, audio). It
is copyrighted and not redistributed with this project. To run any
build you need a legal copy:
- GOG sells the game (cheapest legal route). After install,
DATA.DATlives in the install dir. - The Blizzard Arcade Collection ships a different repack and is not a drop-in replacement.
Place DATA.DAT next to the vikings binary and launch (the game looks in
the directory it was started from first, then next to the binary; the name is
matched without regard to case, as DOS did, so a data.dat works too).
If the archive cannot be opened or is not the right one, the game stops the
way the original did — its message on the terminal and in a message box:
Cannot find 'data.dat' file in the current directory. (no such file in
either place) or Data.dat has been corrupted. Please re-install. (a different
archive: the Blizzard Arcade Collection repack, a truncated copy). The archive
this port is built against is 1 570 444 bytes, md5
d0f8a04202070a54d1909758ecd962b5.
The bundled ds_static.bin / exe_static.bin snapshots are not a
DATA.DAT substitute — they hold the static EXE image (initial DS, the
seg001 text/menu data, lookup tables). Test mode reconstructs this
from m2c::m[] populated by C++ static initialisers; the game build skips
m2c entirely and loads it from the snapshot files at startup. Game
content (levels, sprites, sound) still comes from DATA.DAT in both
modes.
Build from source
Linux
git submodule update --init
sudo apt-get install build-essential libsdl2-dev pkg-config # Debian/Ubuntu
make -j$(nproc)
./vikings
Windows (mingw-w64 cross-compile)
Toolchain + SDL2 for mingw:
- Arch x86_64:
pacman -S mingw-w64-gcc mingw-w64-sdl2 mingw-w64-pkg-config - Arch aarch64: same packages from AUR (
yay -S …); building gcc-mingw on ARM takes hours. - Debian/Ubuntu:
apt install g++-mingw-w64-x86-64 mingw-w64-toolsplus manually unpacking the SDL2 mingw devel tarball into the mingw sysroot (/usr/x86_64-w64-mingw32/).
make clean
make WIN=1 -j$(nproc)
Output: vikings.exe. Linux-only debug instrumentation (execinfo
backtraces, perf_event_open watchpoints, dladdr) is compiled out under
#ifdef __linux__ on Windows — gameplay is unaffected. The FNSELFTEST
unit oracle is POSIX-only (fork/mmap isolator): Windows builds compile it
but refuse to run it with an explicit message.
Override the triplet (e.g. for i686) with make WIN=1 MINGW_TRIPLET=i686-w64-mingw32.
Build flags reference
All flags compose. make clean between mode changes.
| Flag | Effect |
|---|---|
RELEASE=1 | -O2 -fno-omit-frame-pointer (default -O0). The m2c-decompiled units are pinned at -O0 even here — the mechanically translated goto-labyrinth is not -O2-clean (miscompiles); everything else gets -O2 |
STATIC=1 | static-link libgcc + libstdc++ |
SDL_STATIC=1 | static-link libSDL2.a via pkg-config --static |
WIN=1 | mingw-w64 cross-compile → vikings.exe |
TEST=1 | test mode: the m2c original and the v2 mirror in lockstep (the verification build); without it make builds the game |
HEADLESS=1 | no-display testing build → vikings_headless (TEST=1 HEADLESS=1 is the judge of the scenario replays) |
Example: full static, optimised, Windows release:
make WIN=1 RELEASE=1 STATIC=1 SDL_STATIC=1 -j$(nproc)
Keymap editor
Standalone SDL2 utility for editing vikings_keymap.cfg:
make keymap_editor # → ./vikings_keymap_editor
./vikings_keymap_editor # opens an editor window
Click a row → press a key to rebind it → Save. ESC cancels a bind. Ctrl+S = save, Ctrl+Q = quit.
Modes
The game (the default build)
make builds the v2 reimplementation alone: the m2c-decompiled sources
are excluded from the build entirely. Faster compile, roughly half the
binary size. Still needs DATA.DAT for game content; the bundled
*_static.bin files supply the static EXE image that test mode gets
from m2c's C++ initialisers. This is what the release bundles carry and
what the console content pack, the options menu, co-op and the sound
modules run on.
Test mode (make TEST=1)
Runs the m2c-decompiled original VM and the v2 mirror in lockstep.
Two SDL windows open — one rendered by the orig path, one by the v2 path. The
verify infrastructure cross-checks every frame and flags any divergence
in DS state, VM state, audio, or render output. This is the verification
build the scenario replays judge; make clean when switching modes.
HEADLESS mode
No display, no audio device — uses SDL's dummy drivers. Drives the game
deterministically from a replay file and exits with code 1 on the
first verify divergence. Built for CI and reproducible bug hunting;
composes with both modes (TEST=1 HEADLESS=1 is the canon judge,
HEADLESS=1 alone the game's headless build):
TEST=1 HEADLESS=1 RELEASE=1 make -j$(nproc)
./vikings_headless --replay-input=tests/replays/empty.inp --max-frames=200
Divergence dumps land in --dump-dir=<path> (defaults to
/tmp/headless_<pid>/) as PPM frame pairs, DS binary diffs, and a
context.txt explaining what diverged.
Console content (SNES / Genesis)
The material of the console versions — the SNES parallax layers of the
42 levels, the five SNES-only levels in the progression (TR33, SNDS,
TMPL, RVTS, PDDY, with their passwords), the Genesis interludes between
the worlds, the SNES 1993 level variants, the console finale, the twelve
languages of the Blizzard Arcade Collection and the SNES music and
effects — is converted from the console ROM images by the tools of
tools/assets/ (integrate_snes.py and the converters it drives) on
top of the open tree of your DATA.DAT. None of it is in the
repository: you need your own SNES DE image and Genesis image (any file
names — they are found by their SHA-256). The game reads it (test mode
keeps DATA.DAT as the oracle of the orig-vs-mirror verification and
ignores the pack):
mkdir -p roms && cp /path/to/LostVikingsDE.sfc /path/to/LV.gen roms/ # or --snes-rom / --genesis-rom
make content # = python3 tools/assets/build_content.py (python3, ~25 s)
make -j$(nproc)
./vikings
content/ (the open asset tree plus .compiled/ and exe_static.bin,
~25 MB) is picked up by the game binary beside it, or run from that
directory; the start-up log says console content pack content. The
F1 menu then switches the features: PARALLAX, SCENES and
FINALE are on by default, SNES BALANCE is off, LANGUAGE and SOUND (PC /
SNES / SC55 / MT32) as you like.
The pack is judged as it is built: build_content.py ends with
tools/assets/content_check.py, which derives every level's needs from
its own data — the spawn table, the class records of its script, the
animation code of those classes and the children they spawn — and fails
the build when a sprite bank or an animation chunk a level needs is not
in its lists (an object that never spawns, a wrong sprite), when the
engine's tables overflow, or when a converted level's head, spawn table,
palette animation or sprite palette rows drifted from its SNES source
(the sprite rows are the PC palette chunks whose colours the SNES level
loads: a donor level's rows colour the donor's enemies, not these); the
original's own quirks are calibrated out on the PC levels first. make content-check repeats the report; make content-smoke is the dynamic
half (tests/content_smoke.sh): the game binary starts every level of
the pack under SDL's dummy drivers, spawns every entry of its spawn table
once (V2_SPAWN_SWEEP=1), and any V2-SPAWN-FAIL / V2-ANIM-RES-MISS
the engine prints that the judge did not expect fails the slot. make content-regress (tests/content_regress.sh) replays the reported cases
on the pack — the sand column of TMPL standing while the vikings walk up
to it — and make sound-header-check (tests/sound_header_check.sh)
plays copies of DATA.DAT whose SETUP header says "no sound card" or
another port, which must sound exactly like the file as it is.
The game's frame pacing follows the display: the DOS game renders three
sub-frames per game frame, one per 60 Hz refresh of Mode X, moving the
camera and the sprites on every one, and the port waits for the
display's vsync in the same way (60 Hz: every refresh, 120 Hz: every
other, 144 Hz: two-three-two...), so each sub-frame is shown once, as on
a VGA. SMOOTH < NONE | AUTO | ON > (F1, smooth=0/1/2) interpolates the
camera and the sprites between the two newest sub-frames at the display's
own rate; AUTO — the default — does that only on a display whose refresh
is no multiple of 60 (75, 90, 144, 165 Hz), where the 60 Hz schedule is
uneven, and stays off on 60 and 120 Hz where there is nothing to smooth.
The menu line shows the decision and the refresh the game is locked to.
PACING < VSYNC | VRR > (pacing=0/1) is for G-Sync / FreeSync displays:
VRR presents every sub-frame the moment the game flips it, without a
vsync wait, and the display follows the game's 60 Hz. AUDIO BUF < 256 |
512 | 1024 > (audio_buffer=) is the sound device's buffer in frames,
512 (11.6 ms) by default, applied at the next start. STATS [ON]
(stats=1) draws a diagnostics overlay: the vsync lock and its refresh,
the present interval, sub-frames per game frame with dropped and
repeated ones, the game frame's wall time and work, and the audio
buffer with its underruns — the numbers to quote when motion or sound
misbehaves. MUSIC VOL and SFX VOL (music_volume= / sfx_volume=,
0..100 in steps of 10) scale the music and the effects separately in
every sound mode — through the game's own driver (the sequence volume
of the AIL player, which the SC-55 and the MT-32 inherit) and through
the console engine's track volumes in SNES mode; at 100 the original's
sound calls are untouched, and a change reaches the music playing at
once.
The twelve languages are Blizzard's translations from the Blizzard
Arcade Collection and are not in the repository either: copy
strings/locale.strings and lv_snes_strings.json from the
collection's assets/ folder next to DATA.DAT (or pass --bac <its assets dir>; a Steam or Battle.net installation in the usual place is
found by itself) and the build adds the language banks — the level and
intro dialogues, the title menu (NEW GAME / PASSWORD / QUIT TO DOS,
INVALID PASSWORD), the game-over prompt and the lines of the Genesis
interludes. Without them the pack has the English original only. The extra levels have no switch — they
are simply in the progression. V2_CONTENT=0 runs the plain archive
with the same binary; V2_ASSETS_DIR=<dir>/.compiled V2_EXE_STATIC=<dir>/exe_static.bin point at a pack elsewhere (the
level editor's scratch tree has the same layout:
tools/assets/edit_server.py --scratch <dir>).
The game's release bundles carry the builder as content-tools/ (the
scripts and their inputs from the repo — no game data, no console
data): python3 content-tools/tools/assets/build_content.py in the
bundle directory, with DATA.DAT and the two images beside vikings,
writes content/ there. The pack itself is never redistributed: it is
derived from DATA.DAT and the console images.
Level editor and mods
The game's content is editable: maps, spawn tables, level size, quad
templates, tile pixels and collision masks, palettes, sprite and
animation banks, the dialogue texts, and the object logic — the world
scripts that drive every class, in a readable language with a compiler
that reproduces the original scripts byte for byte. A browser editor
(tools/assets/edit_server.py, Python 3, no dependencies) works on a
scratch copy of the open asset tree and drives the loop save → pack →
play; the differences can be exported as a mod package and applied by
anyone with the game and their own DATA.DAT:
python3 tools/data/extract_datadat.py DATA.DAT # the archive -> assets_raw/
python3 tools/assets/assetc.py # -> assets/, the open tree (every chunk round-trip verified)
python3 tools/assets/edit_server.py # http://127.0.0.1:8137/
python3 tools/assets/edit_server.py --scratch content --import-mod their_mod.json # play someone's mod: import into a tree named content/, then pack
The full guide is EDITOR.md: the pages of the editor, the
logic language (tools/data/LVD_LANGUAGE.md, statement reference
tools/data/LVD_REFERENCE.md), testing a level, the command-line tools,
mod packages and their current limits. The release bundles ship the
tools and the guide in content-tools/.
Sound options
F1 → SOUND < PC | SNES | SC55 | MT32 > (saved as sound=0..3 in
v2_options.cfg). What each one needs:
- PC — the original: the game's Sound Blaster Pro / AdLib driver
running in the interpreter, its OPL3 register stream rendered by the
Nuked OPL3 core. Nothing to add. The sound and music cards chosen in
the original SETUP (the header record of
DATA.DAT) are ignored: the port always runs the Sound Blaster Pro FM configuration, the one card it models; a header set up for another card is reported in the log and left as it is. SETUP's "no music" / "no sound effects" switches are honoured as the original honours them. - SNES — the SNES DE music and effects: the console's SPC driver and
its banks, converted from the SNES image into the content pack. Needs
content/(the section above); takes effect at the next level. - SC55 — the game as an owner of a Roland SC-55 heard it in 1992:
the game runs its own MT-32 configuration and the module on the MIDI
cable is an emulated SC-55 (Nuked-SC55). Needs the module's ROM images
in
roms/sc55/of the working directory (the bundle directory), orsc55_roms=<dir>inv2_options.cfg, named as Nuked-SC55 names them — the set is detected by the files present: SC-55mk2rom1.bin rom2.bin waverom1.bin waverom2.bin rom_sm.bin; SC-55 (mk1)sc55_rom1.bin sc55_rom2.bin sc55_waverom1.bin sc55_waverom2.bin sc55_waverom3.bin; the SC-55st, CM-300/SCC-1, SCB-55 and SC-155 sets under their Nuked-SC55 names likewise. - MT32 — the game's own MT-32 configuration on an emulated MT-32
(Munt): its MT-32 driver, the 64 custom timbres and the MT-32
arrangements of the music — the machine the music was written for.
Needs the module's ROMs in
roms/mt32/(ormt32_roms=<dir>): a control ROM and a PCM ROM of an MT-32 or CM-32L version Munt knows (MT32_CONTROL.ROM+MT32_PCM.ROM,CM32L_CONTROL.ROM+CM32L_PCM.ROM...); the files are recognised by content, the names do not matter.
The ROM images are Roland's and are not included. Without them you keep
the PC sound and a short message on the screen says what is missing
(the log line V2-SC55: / V2-MT32: has the detail). A module boots
for a few seconds after the switch; chosen in the saved options, the MT-32
plays from the game's first note, while a switch during the game restarts
the current track from its top. For a check, V2_MT32_DUMP=<file.mid>
writes the MIDI stream the module receives (MT32 and SC55) and
V2_MIDI_DUMP=<file.mid> the FM driver's own events, as standard MIDI
files.
CLI options
The three entry points have different surfaces.
Test-mode vikings (make TEST=1):
| Flag | Description |
|---|---|
--debug | enable orig debug-build cheats (F4 INT3, F5/F6 level skip) |
--keymap=<path> | load a custom keymap (default ./vikings_keymap.cfg) |
The game, vikings (make):
| Flag | Description |
|---|---|
--debug | same as test mode |
--keymap=<path> | same as test mode |
--record-input=<file> | record SDL input to a frame-based .inp log |
--replay-input=<file> | replay an .inp log instead of live keyboard |
--replay-strict | ignore live keyboard even after the replay queue is exhausted |
--max-frames=<N> | exit cleanly after N frames (0 = unlimited) |
The game also understands two teleport env vars: V2_LOAD_STATE=<file>
loads a full v2-world snapshot before the first frame (play on from that
point), and V2_SAVE_STATE=<file> writes one back (with both set, the
save happens immediately after the load — the byte-identical file pair
is the roundtrip self-test).
vikings_headless (make HEADLESS=1):
| Flag | Description |
|---|---|
--replay-input=<file> | REQUIRED. Frame-based .inp replay driving input |
--keymap=<path> | optional keymap override |
--dump-dir=<path> | divergence dump output dir (default /tmp/headless_<pid>/) |
--max-frames=<N> | exit cleanly after N game frames (default 10000) |
--seed=<N> | PRNG seed (default 0) |
Replay files are frame-based and SDL-independent (record actions by
name) so they reproduce identically across machines and runs. A game
controller's buttons are recorded as the bits they set (B0100 is the
RIGHT bit; B0100@2 the same bit of player 2) and replayed as bits, not
as keyboard keys. A recording starts with an # options line holding
the settings that shape the game (SNES BALANCE, SCENES, FINALE, WIDE,
LANGUAGE, KEYS 123); a replay applies them over the local
v2_options.cfg, and the F1 menu keeps those settings fixed while a
recording or a replay runs.
Headless also honors env vars: V2_GOLDEN_DUMP=<file> writes the
named-field text dump of the final shadow DS at every clean exit path
(the golden end-state channel), V2_SAVE_STATE=<file> writes the full
teleport snapshot at the same points, and V2_FAST_VSYNC=1 shrinks the
historical 4 ms vsync nap to a 200 µs yield (the test scripts set it by
default; V2_FAST_VSYNC=0 restores the slow path).
Default key bindings
The keyboard table of the DOS original, as vikings_keymap.cfg lists
it. The game reads that file from its own directory; when the file is
present it replaces the built-in defaults entirely, so append to it
rather than starting a new one.
| Key | Function |
|---|---|
| Arrow keys, keypad 8 / 2 / 4 / 6 | direction input (keypad 5 = down, as under DOS) |
| Space / Enter / F, keypad + | primary action button |
| D | secondary action button |
| Tab, Caps Lock | inventory |
| Esc, P | pause / menu |
| Ctrl (Left/Right), keypad 7, Home | previous viking |
| Keypad 0, keypad 9, Insert, PgUp | next viking |
| S | use / talk (Alt+S toggles the sound effects) |
| E | use the carried item |
| Alt (Left/Right) | spec key (used in Alt-combos) |
| F10 | pause menu |
| Y / N | yes / no in dialogs |
| M (with Alt) | toggle the music |
| F1 | the options menu (this port) |
| F4 | debug INT 3 (needs --debug) |
| F5 / F6 | prev / next level cheat (needs --debug) |
| Q (with Alt) | reset |
The DOS game has no direct viking selection: the keys 1 / 2 / 3 only
type letters on the password screen. The F1 option KEYS 123 (off by
default; viking_keys=1 in v2_options.cfg) makes them select Erik,
Baleog and Olaf, with the same rules as the cycle keys (a viking who is
dead or, in co-op, held by another player is not taken). With the
option off nothing of those keys reaches the game; a recording carries
the setting it was made with, and in a network game the host's setting
holds for everybody.
Edit via vikings_keymap_editor or hand-edit vikings_keymap.cfg. The
config format is documented inline at the top of that file.
Game controllers
Any controller SDL2 recognises as a game controller works without configuration. The first one acts as player 1 beside the keyboard; the second and third become players 2 and 3 in local co-op (docs2/COOP.md).
| Button | Function |
|---|---|
| D-pad, left stick | direction input |
| A | primary action button |
| B | secondary action button (D) |
| X | use / talk (S) |
| Y | use the carried item (E) |
| LB / RB | previous / next viking |
| Back | inventory (Tab) |
| Start | pause / menu (Esc) |
| R3 (right stick click) | the F1 options menu: d-pad or left stick moves and changes, A toggles, B closes |
On a Steam Deck add the game as a non-Steam game and keep a gamepad layout in Steam Input (a player reports the default one works out of the box); a keyboard-and-mouse layout hides the controller from the game.
Testing
HEADLESS=1 RELEASE=1 make -j$(nproc)
./tests/smoke.sh # 200-frame baseline (empty.inp)
./tests/scenarios.sh # all replays + golden end-state check
GOLDEN=update ./tests/scenarios.sh # (re)take tests/golden_states/ from a green run
./tests/fnselftest_parallel.sh # ~500 direct per-function units (orig-oracle vs v2)
./tests/fuzz_harness.sh 600 5 # 5×600-frame random fuzz
./tests/fuzz_coverage.py --max-frames 1200 # coverage-guided fuzz
scenarios.sh compares every run's final shadow-DS dump against
tests/golden_states/<name>.txt when that file exists — the state
oracle that outlives the verify scaffolding. The unit set isolates
~500 original functions one at a time (fork-per-case oracle) and diffs
the v2 twin byte-for-byte; both suites run green in minutes thanks to
the fast-vsync default.
Without DATA.DAT in cwd the smoke run still verifies that the binary
boots, SDL dummy drivers work, the replay parser handles empty.inp, no
segfault during init, and the dump dir is created. With DATA.DAT
present the same scripts exercise real gameplay.
Benches of the game build (the windowed binary under SDL's dummy drivers; they need what the feature needs):
bash tests/locale_box.sh # the language banks reach the level dialogues: the first box of
# level 0 in en/ru/ja against tests/golden_locale/ (needs content/;
# UPDATE=1 retakes the goldens, LANGS="..." picks languages)
bash tests/mt32_dump.sh # the MT-32 world: its MIDI stream against the level's MT-32 track
bash tests/wide_canon.sh # the wide view: the canon replays at 320 px, byte for byte
bash tests/quit_chain_check.sh # the quit to DOS (make quit-chain-check): the title's F10 and the
# prompt's "End game" leave by the original's loc_10e35 chain — the
# archive closed, [92FF] = 0 in the state image saved at the exit
The canon runs without a language bank and without the audio thread or the presenter, so these three are the regression tests of the translations, the MT-32 world and the wide view; they do not run in CI.
More detail: tests/README.md and HEADLESS_MODE_ANALYSIS.md.
CI
.github/workflows/build.yml runs on every push (any branch) and every
PR, producing the six artifacts described above. The jobs: test
(x86_64 and arm64: the HEADLESS build, tests/smoke.sh, the scenario
replays), linux (a 2×2 matrix: x86_64 and arm64, the game and test
mode — the portable build: ubuntu-22.04 runners for the glibc 2.35
floor, SDL2 2.30.10 built from source with its dlopen'ed backends and
cached, clang 18 from apt.llvm.org, and a check that glibc is the only
dynamic dependency), windows, windows-test and release. Ubuntu's
own libSDL2.a links its backends directly, so a bundle built against it
needed libpulse, libwayland, libdecor and the rest on the target machine;
the source build costs the job 2–3 min more. The tests and the release
builds run in parallel — the two compiles of the transpiled VM share no
objects and take ~19 min each on the hosted runners, so one job doing
both in sequence was the workflow's critical path (46 min against ~27
now); release needs every job, so nothing is published while the
smoke test or a replay fails on either architecture. Every push to any
branch also publishes a GitHub Release:
- Branch push → the rolling pre-release
latest(one release on the page: the previouslatestand its tag are deleted and created again at the new commit, titledlatest (branch @ short_sha)). - Tag push
v*→ stable release named after the tag. - PR events do not create releases.
Optional: full-gameplay CI smoke
The CI test job (x86_64 and arm64) runs tests/smoke.sh and, when
DATA.DAT is present, tests/scenarios.sh. Without access to DATA.DAT
the smoke test runs the init-only path (still useful for catching link /
SDL / replay parser regressions) and the replays are skipped. To upgrade
CI to a full gameplay smoke, host DATA.DAT on a server you control and
add the URL as a repo secret:
gh secret set DATA_DAT_URL --body 'https://user:pass@yourserver/DATA.DAT'
CI then curls DATA.DAT from that URL once and caches it. The secret
is masked in logs by GitHub Actions automatically.
Project layout
src/
runtime/asm.cpp entry point for test mode
vikings.exe*.cpp m2c-decompiled DOS executable (do not hand-edit)
_data.cpp m2c-decompiled DS image
sdl/v2_main.cpp entry point of the game (the default build)
sdl/v2_vm.cpp v2 mirror VM — the reimplementation
sdl/v2_hash_hot.cpp verify hash kernels (per-file -O2 island)
sdl/v2_gamestate.{h,cpp} phase-D typed DS model + serializer (golden/teleport)
sdl/v2_render_funcs.cpp v2 render path (tiles, sprites, HUD, glyphs)
sdl/v2_keymap.{h,cpp} runtime keymap loader / saver
sdl/v2_input_recorder* frame-based input record/replay
sdl/headless/ HEADLESS mode entry + dump infra
sdl/keymap_editor/ standalone keymap editor binary
sdl/render.cpp shared SDL window / event loop for orig mode
sdl/play.cpp SDL audio device (mixes the native OPL3 render)
sdl/v2_ail_interp.cpp 8086 interpreter running the ORIGINAL SBPFM.ADV
sdl/v2_ail.cpp AIL glue: driver boot, seg002 bridge, tick pump
sdl/v2_native_opl.cpp Nuked OPL3 chip model + tick pacing + mixdown
rendering/seg003_*.{c,h} hand-written seg003 helpers
adlmidi/ git submodule; ONLY chips/nuked/nukedopl3.c is built
tests/ HEADLESS test scripts + replays
tests/golden_states/ per-replay final-state dumps (the golden oracle)
.github/workflows/build.yml CI: test (HEADLESS smoke + replays, x86_64/arm64) ∥ linux (x86_64/arm64) + windows × {the game, test mode} → release
docs2/ holds reverse-engineering notes generated during the
m2c-decompile audit; safe to ignore for everyday work.
License
The port code in src/sdl/, src/runtime/, src/rendering/ and the build
infrastructure is the work of this project. The m2c output under
src/vikings.exe*.cpp and src/_data.cpp is mechanically derived from
the original 1992 Lost Vikings executable, which remains the property of
Blizzard Entertainment (formerly Silicon & Synapse). The DATA.DAT
asset file is also Blizzard's property and is not redistributed.
The src/adlmidi/ submodule keeps its upstream license
(see libADLMIDI).
src/sdl/third_party/xbrz/ (the FILTER < XBRZ > presenter option, xBRZ 1.8
by Zenju) is GPL-3.0 with the MAME exception, src/sdl/third_party/hqx/
(FILTER < HQX >) is LGPL-2.1,
src/sdl/third_party/snes_spc/ (the SNES sound option) is LGPL-2.1+,
src/sdl/third_party/mt32emu/ (the MT-32 sound option, libmt32emu from
Munt) is LGPL-2.1-or-later, and
src/sdl/third_party/nuked_sc55/ (the SC-55 sound option, from
Nuked-SC55) is GPL-2.0-or-later:
a binary built from this tree is distributed under the GPL (version 3, because of xBRZ). The SC-55 and
MT-32 options also need the modules' ROM images, which are Roland's and are
not included — put your own sets in roms/sc55/ (Nuked-SC55's file names:
rom1.bin rom2.bin waverom1.bin waverom2.bin rom_sm.bin for the SC-55mk2)
and roms/mt32/ (MT32_CONTROL.ROM + MT32_PCM.ROM, or the CM-32L pair),
or point sc55_roms=<dir> / mt32_roms=<dir> in v2_options.cfg at them;
without them the SOUND option falls back to the original OPL3 render. The
MT-32 option is the game's own MT-32 configuration (its MT-32 driver, timbre
bank and MT-32 arrangements of the music run on the emulated module) — the
music was written for that machine; the SC-55 option feeds that same MT-32
stream to the emulated SC-55, as an SC-55 owner heard the game in 1992.
Comentarios (0)
Inicia sesión para dejar un comentario.
Sé el primero en comentar sobre este juego.
