📜 ⬆️ ⬇️

Recreating an old DOS game in C ++ 17

In 2016, I started working on a hobby project for reverse engineering of the game Duke Nukem II and recreating its engine from scratch. The project has the name Rigel Engine and is laid out in open source ( its page on GitHub ). Today, more than two and a half years later, on my engine, you can already go through the entire shareware-episode of the original game with the gameplay almost identical to the original. Here is a video with the passage of the first level:


What can he do? Rigel Engine works as a complete replacement for the original binary DOS file ( NUKEM2.EXE ). You can copy it to the game directory and it counts all the data from it, or you can specify the path to the game data as a command line argument. The engine is built and executed under Windows, Mac OS X and Linux. It is based on SDL and OpenGL 3 / OpenGL ES 2, and is written in C ++ 17.

It implements the game logic of all enemies and game mechanics from the Shareware episode, plus most of the menu system. In addition, you can import saved games and a high score table from the original game.

Moreover, the engine has advantages over the original:
')

While I do not think Rigel Engine is completely "ready." But this is a great stage in development and a good opportunity to write again about the engine (old posts are published here and here ). Let's start by looking at the current state of the code, and find out how I got to it.

How much is in the code engine?


At the time of writing, RigelEngine consists of 270 source files containing more than 25 thousand lines of code (no comments / blank lines). Of these, 10 files and 2.5 thousand lines - unit tests. Detailed breakdown taking into account empty lines and comments posted here .

What is in all this code? Some common infrastructure and support functions, such fundamental things as rendering, and a bunch of small pieces of logic. Besides all this, the biggest parts are:


Of course, all this code needed to be written, and this brings us to the next question.

How much work did it take?


Although two and a half years have passed since the project began, I have not worked on it all this time. For a couple of months I didn’t work on the project at all, in some others I spent only a few hours on it. But there were moments when I was working on Rigel Engine quite actively. Looking at the commit schedule on Github, you can get a rough idea of ​​how my work was distributed over time:


According to the schedule, we see that 1081 commits were made to the master branch. However, even before creating the repository, I worked on a closed one, in which there were 247 commits, which in total gives us 1328 commits. In addition, there were several prototype branches that I used for research and experimentation, but never merged with the main one; besides, before merging, I sometimes squeezed the big stories of commits into more concise ones.

It should also be said that writing the code was not the only part of the project — another important part was reverse engineering. I spent quite a few hours studying the disassembled code of the original executable file in Ida Pro (in the free version), taking notes, writing pseudo-code and planning the implementation of the elements of my version. In addition, I conducted active testing of the original game, launching it in DOSBox and on the original equipment (different 386 and 486 machines purchased on eBay). I collected test levels for separate observation of specific enemies and studying game mechanics, recorded video clips using DOSBox, and scanned the recordings frame by frame to confirm my conclusions made while studying assembly code. After the implementation of the enemy or game mechanics, I usually recorded a video clip from my version and compared it frame by frame with the original to confirm the accuracy of my implementation.

Here are some photos of my notes:


Reverse engineering of the camera control code. A large rectangle indicates a screen. Dotted lines indicate zones in which a player can move without moving the camera. If you're interested, the camera control code itself can be found here .


General notes that help in understanding the assembly code. On the left - the order of updating the original game at a high level. On the right are notes about bit fields indicating the state of some game objects.


Transcription assembly code in pseudocode. Usually I do it quite mechanistically, transcribing without thinking about what the code is doing, and then I use the version in pseudocode to understand the underlying logic. And based on it, I am already inventing my own implementation. See the ready code here .


Pseudocode cleaned version of the logic of the enemy. The headers indicate the states of the finite state machine, the code below explains what should happen in the corresponding states. It was created on the basis of a raw pseudocode obtained by transcribing an assembly code. Ready code can be found here .

In the end, the project turned out to be very exciting, and I learned a lot from it: about reverse engineering, 16-bit x86 assembler, low-level VGA programming, the strict restrictions that PC developers had to face in the early 90s; In addition, I made many discoveries about the internal features of the original game and how strange and bizarre some of them were reaized - this topic in itself deserves a separate series of posts.

What's next


In addition to adding the latest remaining functions and finishing support for the registered version, I have a few ideas for improving and extending the capabilities of the Rigel Engine, not to mention the cleanup and refactoring of the code - as usual, the best way to create software architecture becomes apparent only after the creation of this software is completed.

As for future improvements, here are some of the points that I was thinking about implementing:


I have no “road map” for the future, so I can implement these items in any order. But before all this next step will be the integration of Dear ImGui to further build the options menu, which is not in the game yet; in addition, it will enable and disable the improvements listed above. In the end I’ll say that I will be grateful for any assistance in working on GitHub !

Source: https://habr.com/ru/post/454570/


All Articles