Google Summer of Code 2022 has now ended, so this post will wrap up everything that’s been done and what is left to do.
The branch of my repository with the latest code for The Immortal can be found here: https://github.com/Quote58/scummvm/tree/immortal/engines/immortal
What has been done
– Initial analysis of the source code and ScummVM engine skeleton [relevant post] [relevant commit]
The original plan was to continue the implementation started in a previous GSoC for the DOS version of the game, however after taking a look through the source code for each version and discussing it with my mentor, the plan changed and I began work on implementing the Apple IIGS version instead.
– Support for ProDOS with Common::Archive [relevant post] [relevant commit]
The first challenge that presented itself when I began work on the Apple IIGS version was the file format for the game itself. The Immortal was originally packaged as ProDOS disks, which was an issue because ScummVM did not have support for reading this file format at all. Adding support required learning about and understanding the way ProDOS files work, and experimenting until I had code that could parse the entire disk and list the files within. Once it was possible to get and return files from the disk with a file name, I was encouraged to integrate this with the Common::Archive class in ScummVM, and proceeded to do so.
– Compression Routines [relevant post] [relevant commit]
Once the file could be read by the engine, I decided to begin by tackling something that looked fairly self contained. The compression/decompression routines did not seem to be tied in to any other functions within the engine, and so could serve as a way to get comfortable with what I would be doing in the rest of the engine, through something with a clear purpose and input/output. In the linked post, I talk about the process of beginning to translate the assembly of the engine into C++, with the compression routines as the example.
– Initial engine and subsystem skeletons [relevant post] [relevant commit]
After the compression functions were written, the next step was to start analyzing the files in the engine and begin to understand the main path of the game logic, the connections between different files, and ultimately create the initial engine skeletons. This meant creating the outline of immortal.cpp, kernal.cpp, and logic.cpp, and creating the necessary function stubs for everything along the way.
– Palette processing [relevant post] [relevant commit]
While creating the skeleton for kernal.cpp, I decided to implement the main palette processing functions. There are not too many functions related to this, and it was a core component of the engine, so I felt it was good to implement along the way. It also served as an example of how the assembly code makes heavy use of bit manipulation and interesting math/programming principles.
– Scope and engine outline [relevant post]
One part of translating the engine that took some time to get sorted out, was the general structure of the engine itself. It isn’t initially clear how each component of the engine relates to each other, but it is important for knowing how to structure the translation, what data types to choose, how to manage scope, etc. The linked post goes over the entire process of sorting through the files and determining the general scope and relation of each component in terms of ‘layers’ in the engine. This was important for the translation going forward, as it could inform decisions about how each new component that was translated related to the whole engine.
– Story subsystem [relevant post] [relevant commit]
The ‘story’ subsystem in the game is crucial, as it defines many of the important aspects of each level of the game. It defines what and where the objects, enemies, and more are in each level, as well as the dialog strings, the sprite animations, and more. The way it is implemented in the source code is unusual, and the linked post goes into detail about it.
– Cycles [relevant post] [relevant commit]
‘Cycles’ are the way that this engine handles sprite animations, and the linked post goes over the oddities with them.
– Flameset [relevant commit]
‘Flamesets’ are the torches that you find on the walls in each level. They are not objects or enemies, but are in fact their own little subsystem consisting of a sprite cycle, and code for interacting with fireballs, and even dimming the screen when they are ‘off’.
– Sprite rendering [relevant post] [relevant commit]
Sprite rendering involved many different subsystems and took some time. This is because the method for preparing and drawing sprites is a bit unusual, and translating it accurately was a challenge. The post goes into detail about some aspects, but there are several others throughout which detail the process of understanding them.
– Text parsing and printing [relevant post] [relevant commit]
– Hit gauge [relevant commit]
This acts as the wizard’s health bar in the game, and is drawn with sprites from the font sprite sheet.
– Implemented many functions for Kernal, Logic, Level, Room, Sprites, and more, creating the skeleton of the engine in a way that supports a continued, accurate translation
What is left to do
I had hoped to have some of the map drawing in time for this post, but unfortunately the map data is stored in multiple file types (.CNM and .UNV) which require unpacking and processing before they are usable, and the formats are specific to the Apple IIGS version of the game. I am working on it, but it will take more time to decipher.
- Drawing level data from maze files with drawchr (requires deciphering the .CNM and .UNV files)
- Implementing an input layer through ScummVM (the engine code currently references ‘actions’ and has function stubs that are called for input, but functions for getting input from ScummVM need to be written)
- Object and Monster subsystems
- Music and sound implementation
- Filling out the rest of the static and dynamic story entries (the first level is written out and the structure is there, it just needs the data for the other levels to be written out) (the static data for Str and Cyc are included, but Motive, Program, and ObjType still need to be written out)
- Filling out remaining functions in kernal.cpp, logic.cpp, level.cpp, and room.cpp
- Implementing motives, pickups, and other object/enemy structures and their related functions
My time with Google Summer of Code 2022 has been wonderful, and in some ways I am sad for it to be over. However I am also grateful to have been a part of it this year, as I have learned a lot and have gotten to contribute to something I think is meaningful. ScummVM is a great program, and a great community.
I was hesitant at first about making these blog posts, but I ended up enjoying and learning from them. It allowed me to strengthen my understanding of the code and concepts at hand, and it served as a good organizational tool, allowing me to keep track of progress each week.
I will continue to work on The Immortal, building from I have done the past few months towards a complete implementation of the game. And if possible I will continue to update this blog with progress.
Lastly, I want to say Thank you. To Google and ScummVM for this opportunity, to my mentor Criezy for the advice and code reviews, to Sev and all the other ScummVM members that helped me (especially with Git!), to my brother for pushing me to apply for GSoC in the first place as well as his support and help along the way, and to anyone that has read any or all of these blog posts or simply followed the progress in any number of ways. I am truly grateful to all of you for this past summer.