I reverse engineered and ported Ducks

borancar1 pts1 comments

GitHub - borancar/Ducks · GitHub

/" data-turbo-transient="true" />

Skip to content

Type / to search

Sign in<br>Sign upAppearance settings

You signed in with another tab or window. Reload to refresh your session.<br>You signed out in another tab or window. Reload to refresh your session.<br>You switched accounts on another tab or window. Reload to refresh your session.

Dismiss alert

{{ message }}

borancar

Ducks

Public

Notifications<br>You must be signed in to change notification settings

Fork

Star

master

BranchesTags

Go to file

CodeOpen more actions menu

Folders and files<br>NameNameLast commit message<br>Last commit date<br>Latest commit

History<br>159 Commits<br>159 Commits

.clang-format

.clang-format

.gitignore

.gitignore

LICENSE

LICENSE

Makefile

Makefile

README.md

README.md

dos_io.c

dos_io.c

egg.c

egg.c

game.c

game.c

game.h

game.h

sdl_io.c

sdl_io.c

sound.c

sound.c

View all files

Repository files navigation

Ducks! — the game's own code, reconstructed

C reconstructed from the disassembly of Ducks! v1.2 (Tim Furnish / Hungry<br>Software, 1998-2000), one file per unit of the original we can argue for. It<br>builds and plays:

make run

You need your own copy of the game, and make run will fetch it for you.<br>Nothing of Ducks! is distributed here — no executable, no .egg data, no artwork.<br>What make run does is download the archive from an archive page,<br>https://www.kieranmillar.com/ducks/, and keep the one file the port needs:<br>Eggs/Main.egg, which holds every picture, level, sound and line of text in the<br>game. That page is not the authors' — it is where a copy can still be found.

It needs curl and 7z, does nothing if Eggs/ is already there, and does not<br>touch the network at all if DUCKS_GAME_DIR points at a copy you already have.<br>make on its own just builds; make eggs just fetches; clean deliberately<br>leaves Eggs/ alone.

What you need

A C compiler, make, and SDL3 — not SDL2; the two are not interchangeable and<br>the build looks for pkgconfig(sdl3). curl and 7z are needed only by<br>make eggs, to fetch the game data.

Fedora<br>sudo dnf install gcc make pkgconf-pkg-config SDL3-devel curl 7zip

Debian / Ubuntu<br>sudo apt install build-essential pkg-config libsdl3-dev curl p7zip-full

Arch<br>sudo pacman -S base-devel sdl3 curl p7zip

macOS<br>brew install sdl3 sevenzip (compiler from the Xcode command line tools)

The Fedora line is the one this was built with; the others are the usual names<br>for the same four things. If a package has been renamed, what you want is the<br>SDL3 development package — the one that installs SDL3/SDL.h and an sdl3.pc.<br>Check it with:

pkg-config --modversion sdl3

Debian and Ubuntu only started carrying libsdl3-dev recently (trixie, 24.10).<br>On anything older, build SDL3 from https://github.com/libsdl-org/SDL. SDL2 is<br>not a fallback: the port uses SDL3's audio streams and its<br>SDL_GetKeyFromScancode with a modifier state.

What this is

The original sources are not available. This is what the code must have looked<br>like for the compiler to have emitted what it did: the game's own code segment,<br>0x04ca0-0x14620, read back out of the disassembly. Every routine in it is<br>transcribed and nothing stands in for one.

Every function carries the image offset it was read from, so any line can be<br>checked against the binary, and where a name or a type is a guess it says so.

Two backends, one interface. game.h declares what the game needs from below<br>it; dos_io.c is the original reconstructed - VGA ports, Mode X planes, INT 33h -<br>and sdl_io.c is the same interface on SDL3. The game cannot tell which one it is<br>linked against, which is the test of whether the split was drawn in the right<br>place.

dos_io.c is a record rather than a second build: reimplementing the DOS side is<br>not the aim, so it holds what we know about the original's hardware layer - the<br>Mode X planes, the DAC, the CRTC, INT 33h - and the Makefile does not compile it.

Written as C99, not as period C. The aim is a port that builds and runs, so a<br>construct is written the clearest modern way that matches the instructions. It is<br>not an attempt to feed Turbo C++ 3.0 something byte-identical.

Files

file<br>what is in it

game.c<br>the game: main and init, the resource and string loaders, the fonts, every menu and screen, the save format, the hall of fame, the level loader, the frame, every entity's behaviour, the tools, the particles, the endings and the cutscenes. In address order, which is the order the compiler emitted them

game.h<br>its types, its globals, and the interface both backends implement

sdl_io.c<br>that interface on SDL3: a linear framebuffer, an SDL palette, a 70 Hz deadline in place of the retrace spin, the mouse, the audio device

dos_io.c<br>that interface as the original: BIOS mode, Mode X planes, the DAC, the page flip, INT 33h, and every drawing primitive

sound.c<br>the sound module: the id-to-sample map, the eight voices, and the original's additive mixer

egg.c<br>the egg reader: the directory, the chunk decoder, the shifted-string...

game sdl3 make ducks from original

Related Articles