Freescape Engine – Week 8

Freescape Engine

This engine is currently my biggest challenge, I still have some doubts of if my keymap support is implemented to its highest degree. The issue I faced is that there were too many files to be aware of while making small changes. Before that I’ll explain how the keymap was being generated per game, The initkeymap was initialized as a virtual function and it was being overridden for each game engine . For example if I wanted to map a key I had to first identify in which engine the actions were being used and in which they were not while keeping in mind the different keys mapped to each engine per game, I usually got lost in my thought process while navigating and had to rethink. Also I changed the structure of how the keymaps were being initialized. Firstly, the common actions were initialized in the parent class of each per game keymap then the actions that were not common between the games and had different keys were being mapped in their own initkeymap overridden  function.

Here is the PR.

Additional Notes

As of now there were few notes as I opened it for review and there were two thing I overlooked:

It seems that different keys are mapped into different platforms, somehow I was not able to Identify that and also there is an issue with the demo mode, I haven’t fully understood what the statement means yet, but that’s a problem for tomorrow’s me.

Week 8 – Application

In addition to continuing to finish some unfinished features, my work this week mainly focused on the final scenario, which involves users uploading local game checksums, meaning we need to deal with network interactions.

Previously, I had been using SSH port forwarding and tunneling requests through SSH to test my Flask application, but I had never actually accessed it through a browser using a domain name. When I was informed that I needed to expose the localhost running on the VM, I initially thought that I could simply start my Flask app with Gunicorn and then access it directly via domain:port. However, I underestimated the situation. The server only had port 80 open for external access, hosting several web pages.

After some investigation, I discovered that the web service was using Apache2 instead of the Nginx I was familiar with or the increasingly popular serverless setup, which made things feel a bit tricky initially. After researching some information and documentation, I decided to use a reverse proxy to mount my Flask application under the corresponding subdomain on the server. (Unlike PHP, Flask does not process requests based on files, so special configuration is required.)

However, after discussions, I chose a different approach: using mod_wsgi, as it is more compatible with the existing Apache than Gunicorn. After quickly reading through the official documentation and modifying the Apache configuration file, I successfully replaced the original PHP web pages.

TOUCHE Engine & TRECISION Engine – Week 7

TOUCHE Engine

I found it odd that keycodes were being stored in _flagsTable[600] but I couldn’t for the life of me find anywhere else in the engine where it was being used. So when the escape key is pressed, KEYCODE_ESCAPE is stored in _flagsTable[600] and somewhere else in the engine it is being processed.

so even though I mapped it into action I had to store the escape keycode in the _flagTable[600] otherwise it wouldn’t work. This is a hack that I am not proud of but could not find another way as of now. Other than that there was nothing in the engine that was hard to tackle.
Here is my PR.

TRECISION Engine

The issue I had with Trecision while mapping it was that the pause was little complicated to figure out as the loop calls itself recursively to see if any key has been pressed then it unpauses.

I disabled the keymap so if any key was pressed it would continue and for that I accommodated JoyInputs too so I introduced bool joyunpause which keeps track of if any joy button was pressed.

Additional Details

I was told by my mentors that I was not mapping joy buttons correctly. One major issue was that I was using JOY_BACK and JOY_GUIDE but these buttons including JOY_START are already in use by either global_keymap or hardware. So I was advised that if there are multiple keys and not enough joy_buttons, to further divide the keymap and enable and disable in different game situations so they do not overlap and that way you can use same joy button for different actions.

Week 7 – Fixing Details

This week, progress on the new feature work has been limited (though I did manage to complete some new features). On the one hand, there weren’t many features left to implement. On the other hand, I discovered some bugs from previous work this week and focused on fixing them.

The first issue was related to querying and displaying the history table for each fileset. Ideally, for example, if we merge fileset 2 into fileset 23, and then fileset 23 into fileset 123, the history page for fileset 123 should trace back to which filesets it was merged from. Initially, my code simply used SELECT * FROM history WHERE fileset = {id} OR oldfileset = {id}, which was clearly insufficient.

So, I initially thought about using a method similar to an unoptimized union-find (a habit from competitive programming 🤣) to quickly query merged filesets. However, I realized this would require an additional table (or file) to store the parent nodes of each node in the union-find structure, which seemed cumbersome. Therefore, I opted to straightforwardly write a recursive query function, which surprisingly performed well, and I used this naive method to solve the problem.

Another issue arose on the manual match page for each fileset. Although the program always identifies the fileset with the most matches, I found that the number of matched files between two filesets was significantly off during testing. Upon investigation, I discovered that the displayed count was consistently five times the actual number of matches. Where did this factor of five come from? Initially, I was baffled.

After logging several entries, I finally pinpointed the problem. The manual match page retrieves data from the database, while the dat_parser uses data from DAT files. These sources store file data in slightly different formats. For example, the format from a DAT file might look like:

[{"name":"aaa", "size":123, "md5":"123456", "md5-5000":"45678"}, {"name":"bbb", "size":222, "md5":"12345678", "md5-5000":"4567890"}...]

But the format retrieved from the database might look like:

[{"name":"aaa", "size":123, "md5":"123456"}, {"name":"aaa", "size":123, "md5-5000":"45678"}...]

As a result, it became clear that scan-type filesets stored in the database had five types of file_checksum (meaning each file was treated as five separate files when matched), leading the counter to naturally increase by five for each such file encountered. After simply reorganizing the retrieved database data, I resolved this bug.

Lot of Refactoring

This week, work was somewhat slow. I started my week with finishing up restoring the feature to dump QML output to an XML file, which aids in visually comparing what is loaded from the QML file. This task primarily involved rewriting all the save script methods using Common::SeekableWriteStream. There were nearly 200 methods that needed rewriting.

Another task was fixing the loading of previous games such as Pilots3D and Pilots. Sev significantly contributed to this by implementing the reading of tag IDs based on the game target. Additionally, I made a simple fix in the qdFileManagerclass to address an issue where the wrong number of resource packs were stored due to zero-based indexing. You can check this commit for details on the fix.

Intro Video of Pilots-3d

Lastly, I worked on implementing video playback. Initially, this seemed straightforward, but it required a more sophisticated approach. My first method involved setting up a new rendering loop inside the existing one to play each video. However, Sev pointed out that this was not how the original engine rendered video. Instead of creating a new rendering loop in the play() method of the video player class, I needed to use the existing loop and write an update()method to run on each cycle. In each iteration, we would check if the video had stopped and then continue with the usual engine processes.

This is all for this week.

GROOVIE Engine & SHERLOCK Engine & LURE Engine – Week 6

GROOVIE Engine

Groovie games are point and click games so they do not need keys to navigate hence I only needed to add one key to the keymapper. I t did have a long and strenuous way of handling key input so had to create functions and variable to access the Action stored.
Here is the PR.

SHERLOCK Engine

Working on GROOVIE could be considered the calm before the storm. This is the first time I am unable to finish an engine. SHERLOCK Engine has two games, the tattoo game and the rose game. The Tattoo game was conventional and you could easily track how key inputs were being handled, unfortunately I was unable to figure out the pipeline of how keys are processed from EVENT_KEYDOWN to the use of each of the below variables in the rose game. This is how they stored the key inputs. I couldn’t track this back to EVENT_KEYDOWN. So the hotkeys come from game data that might be localized with the game, it seemed to me that I was using up too much time without making progress so after requesting my mentors I decided to move on to another engine. Unfortunately, I haven’t created a PR as there is no actual progress to be seen.

LURE Engine

Finally, back to working on engines where EVENT_KEYDOWN is being utilized. There was something unique about this engine and that is there are multiple places where events are being polled unlike previous engine all did it one place, it did make it easier to enable and disable keymap, Another thing, the escape key did multiple different actions but still I considered it all as one action for the clarity, for example it could be used to quit game in main screen or exit the save/restore game popups, i thought maybe, if needed, it is better for user to change key for all of them with one click rather than going to each individual action and changing the key.
Here is my PR.

Week 6 Progress and Midterm Evaluation

Hey everyone 👋🏻

Welcome back to my GSoC blog! This week marks the halfway point of our journey, and it’s a great time to reflect on how far we’ve come. Even though it may seem like a quieter week, it’s been focused on moving forward with the ongoing task. I’m excited to share what I’ve been up to and what’s next!

Task: Continuing Unstubbing the QTVR Xtra

This week, I focused exclusively on unstubbing the QTVR Xtra. As a quick reminder, the QTVR Xtra is essential for integrating QuickTime VR movies with Director projects.

Initially, we encountered a challenge due to limited documentation. Fortunately, Sev found an incredibly useful PDF containing comprehensive documentation for all the Xtra functions. This resource has been essential in ensuring the precise implementation of the Xtra functionalities.

What’s Next?

Next week, I’ll keep digging into the QTVR Xtra. There’s still some stuff to figure out, but I’m excited to tackle it. I’ll let you know how it goes and hopefully, I’ll have some cool stuff to show you soon!

Midterm Evaluation

With the midterm evaluation coming up, I’m feeling both excited and a little nervous. I’m looking forward to receiving feedback, which will help guide the next steps of the project. It’s also an opportunity to pause and celebrate the valuable lessons I’ve learned on this incredibly enriching journey so far. 🌟

Conclusion

Wrapping up Week 6 of this journey. Thank you for following along. Stay tuned for more updates as we continue forward!

Week6 – Refinement

This week’s work went relatively smoothly, and I encountered nothing too challenging. At the beginning of the week, sev reminded me that my original regex was matching too slowly. I reviewed the code and realized I had fallen into an X-Y problem myself.

That is, I was focused on using regex to solve the string-matching problem, but I overlooked the fact that using an extra regex might not be necessary at all. My current expression can indeed match all the cases in the existing dat files, but the problem is that it’s not necessary to do so. Since the structure within each “rom” section is fixed and different blocks are always separated by a space, token matching is sufficient. There’s no need to use a complex regex to cover all edge cases, and the performance will be much better (linear time complexity).

As planned last week, I added a detection_type column to the file table. This makes it clearer when recalculating the megakey.

Due to the addition of the detection_type column and set.dat , the code logic requires some extra handling. Therefore, I also refactored the original code, decoupling the matching-related code through modularization to facilitate future development and expansion.

“Your move, Commander.”

Moonbase Commander, one of the very last games ever created in the SCUMM engine, is now finally supported and ready for public testing!

Although not an adventure game, Moonbase Commander, developed by Humongous Entertainment (creators of Putt-Putt, Freddi Fish, Pajama Sam, Backyard Sports, etc.) is a turn-based strategy game that isn’t like any other strategy games at its time. The main objective is to prevent your opponents from taking over a planet before you can. Its unique aim, timing of your power, and launch aspect makes this one of the most “easy-to-learn, hard-to-master” type games. You can easily spawn other hubs from your central hub, spawn collectors at the energy pools, fire projectiles at your opponents’ hubs, the list goes on! It even has a custom map editor, which allows you to create your own maps and share them with other players!

Even though the game can be played via single player Skirmishes and Challenge mode, you can also play multiplayer games either with other people online, or through other devices connected to your Local Area Network, both of which are fully supported! Just click on Multiplayer in the game’s menu and you can create a game and let your friends join! Easy as that!

Tired of playing the same maps over and over again? We have incorporated the map generator from the popular fanmade tool Moonbase Console and all of its algorithms and configurations within ScummVM! You can enable this feature by downloading the latest daily build, go to the Game Options dialog and click on the “Generate random maps” checkbox. This will generate a new randomized map when starting a Skirmish or a Multiplayer game. This can be disabled at any time by unchecking the same checkmark.

Dedicated players have organized a Discord server, where you can find people willing to play online and discuss all things Moonbase Commander! You can join the server here!

You can purchase the game on ZOOM-Platform, Steam, or GOG, or download and try the demo, both multiplayer and the map generator are supported there as well!

Many thanks to the current IP holder Rebellion for providing access to the game’s source code to the team!

Compiling Sources and Verifying targets

This week was a little haywire. It involved a lot. of debugging and getting a deeper understand of the engine.

Compiling the original Engine.

For the past few weeks we were stuck on problems that were getting very hard to debug. Hence we decided that it would be helpful for us to compile the original engine sources. With this we could compare the two using breakpoints and debug messages. I tried to compile the original but it was taking way to long for me to do so. Sev was kind enough to do it for me and compare the two versions we had.

Once we got the original to work, we could compare the two and realised that there was an error while reading the qml file. Once we fixed the issue we were able to render the next frame.

The First Scene in Shveik

In the mean time I implemented a placeholder mini game for the engine to move forward. However to our surprise our struggles weren’t over yet. Shortly we ran into a crash that was again very hard to solve. It again took us quite some time to debut this issue (again sev came to rescue over here). I will cut the story short here and jump straight to the conclusion. The issue was that were trying to launch the wrong target. For all these 4 weeks we were working on shveik, however it was using several QDA files of the version 105, but we had the sources for 104. Essentially we were not going to support that game, and we had to move on with a different one.

 

Marvellous Mice Adventures: Meeting Sea Rat

We chose a different game target to test our engine. The game we decided to go ahead with was 3mice1, since it was the closest to sheik’s release date. We managed to launch the game, and bravo it just worked perfectly fine. There were no graphical glitches, whatsoever. However the challenge that still remained was the previous games didn’t really launch properly inside of ScummVM. We had an enum definition in our codebase that was used to read tagIDs of the qml file that was being read. Over the course of the development of the engine, they had inserted new tagIDs at several points, leading to different versions of the enum. Hence much older games, like pilots3d or pilots3 had issues while launching them. We solve this problem by dynamically generating our tagMap and then searching for different values there.

Inside of Marvellous Mice Adventures: Meeting Sea Rat

One more feature that we implemented this week was to dump the data to an xml file. What the earlier engine did was that once it loaded the qml file, it dumped all that data into an xml file. This was being called from the editor, however since we were not porting the editor we wanted to call this when --dump-scripts would be passed as command line arguments. This would help us visually verify from the xml if we were reading the qml file properly or not.

Throughout the week I also made sporadic progress in areas, like save/load game and sound. However none of those things are complete. In the coming week I would be hopeful of making more concrete progress as next week would mark the half mark for GSoC.