PORTCLUB
Cabecera de The Lost Vikings (LostVikings port)
Port nativo

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.

Build

Discussion: the Telegram chat t.me/LostVikingsRE — questions, bug reports, the reverse engineering itself; GitHub issues work as well.

Intro Level 1

⚠️ Requires the copyrighted DATA.DAT from a legal copy of the original game. All build modes need it — DATA.DAT holds the game content (levels, sprites, audio); the bundled *_static.bin files 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:

FilePlatformBuild
vikings-linux-x86_64.tar.gzLinux x86_64the game
vikings-windows-x86_64.zipWindows x86_64the game
vikings-linux-arm64.tar.gzLinux arm64 (aarch64)the game
vikings-linux-x86_64-test.tar.gzLinux x86_64test mode
vikings-windows-x86_64-test.zipWindows x86_64test mode
vikings-linux-arm64-test.tar.gzLinux arm64 (aarch64)test mode

All six bundles need DATA.DAT supplied separately (see below).

Each archive contains:

  • vikings / vikings.exe — the game
  • vikings_keymap_editor / .exe — GUI key-rebind utility
  • vikings_keymap.cfg — default key bindings (editable)
  • ds_static.bin, exe_static.bin — DS/EXE snapshots used by the v2 mirror
  • README.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.DAT lives 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-tools plus 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.

FlagEffect
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=1static-link libgcc + libstdc++
SDL_STATIC=1static-link libSDL2.a via pkg-config --static
WIN=1mingw-w64 cross-compile → vikings.exe
TEST=1test mode: the m2c original and the v2 mirror in lockstep (the verification build); without it make builds the game
HEADLESS=1no-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), or sc55_roms=<dir> in v2_options.cfg, named as Nuked-SC55 names them — the set is detected by the files present: SC-55mk2 rom1.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/ (or mt32_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):

FlagDescription
--debugenable orig debug-build cheats (F4 INT3, F5/F6 level skip)
--keymap=<path>load a custom keymap (default ./vikings_keymap.cfg)

The game, vikings (make):

FlagDescription
--debugsame 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-strictignore 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):

FlagDescription
--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.

KeyFunction
Arrow keys, keypad 8 / 2 / 4 / 6direction input (keypad 5 = down, as under DOS)
Space / Enter / F, keypad +primary action button
Dsecondary action button
Tab, Caps Lockinventory
Esc, Ppause / menu
Ctrl (Left/Right), keypad 7, Homeprevious viking
Keypad 0, keypad 9, Insert, PgUpnext viking
Suse / talk (Alt+S toggles the sound effects)
Euse the carried item
Alt (Left/Right)spec key (used in Alt-combos)
F10pause menu
Y / Nyes / no in dialogs
M (with Alt)toggle the music
F1the options menu (this port)
F4debug INT 3 (needs --debug)
F5 / F6prev / 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).

ButtonFunction
D-pad, left stickdirection input
Aprimary action button
Bsecondary action button (D)
Xuse / talk (S)
Yuse the carried item (E)
LB / RBprevious / next viking
Backinventory (Tab)
Startpause / 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 previous latest and its tag are deleted and created again at the new commit, titled latest (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.