GSoC Summary

Project description

The goal for the project was to finish the work on qdEngine and make all games based on it playable and completable. Milestones for the project were:

  1. Multiple MinigameManagers could be instantiated. Mini-games for dogncat are running.
  2. Games with advanced mini-games are supported.
  3. All games on qdEngine are supported.

Because I continued working on the engine since my first contribution at the start of March, I was able to finish the task in the first week on the GSoC period.

Thus, for the GSoC period itself, my task was to finish the work on existing engines.

What was done

The original goal for qdEngine was met and I was able to reach all milestones – all games are completable to the end. The news item with the engine announcement can be found here.

The second engine after qdEngine was SLUDGE. The engine was already very close to be done, so only I added a few minor enhancements:

  • Support for extended saves
  • Support for Return-to-Launcher and custom quit dialog
  • Fixed some crashes during autosaves

Following this, I worked on adding language support for the Teenagent game. At that time it only supported English strings, and my task was add support for Polish, Czech and Russian (fan-translated) strings. The challenge was that all language-related data was stored inside the executables, so I had to first extract them, and then modify create_teenagent tool, so that it generated the .dat file that contained the data for all languages in it. This task took quite some time, but in the end, I was able to extract all strings, pack them into dat file and make changes to the engine to support everything.

Shortly after this, I also added support for Polish voice-overs, the only version with voice acting.

After Teenagent, I worked on WAGE engine to support games made with World Builder game creation system. This included both fixing the bugs and also redumping the games, so that they are in right format (Macbinary). The latter took some time and iterations, however with the help of my mentor, we were able to produce the master collection and announce engine for the public testing.

After this I worked on playtesting and improving the MacVenture engine. I was able to fix a couple of blocking bugs, implement number of missing features and playtest games to the end.

Finishing the work on engine also includes taking screenshots, writing wiki pages and preparing news items. So in addition to writing code, I also helped with screenshots and wrote three news items for WAGE, SLUDGE and qdEngine.

Code merged

There were a number of pull requests created over whole period of GSoC. Below are some of the important ones:

qdEngine

Adding support for advanced minigames

Adding 32bpp support

Adding punycode support

SLUDGE

Add support for extended saves

WAGE

Various bug fixes

Adding fallback detection

More fixes

Teenagent

Adding language (textual) support for Polish, Czech and Russian

Adding Polish voice-overs

MacVenture

Bug fixes and improvements

What’s left to do

Finishing last bits of MacVenture:

  • Display Title and StartupScreen images on startup.
  • Extend MacDialog to support text input and use it everywhere in the engine instead of existing dialog class. This will make pop-up dialogs look more similar to original.
  • Sync Click-To-Continue button’s behavior with original. In other words, enable click-to-continue button when text inside console window needs to scrolled to be fully read to the end.
Conclusion

I really enjoyed my time spend with ScummVM this summer. It was my first time contributing to open source project and doing it with ScummVM was a great experience. I am pleased that I had a chance to work on a real world application used by players all around the world.

Working on this project helped me to improve my skills in navigating enormous codebases, reading and understanding other people’s code and correctly formulating and asking questions when get stuck.

Lastly, I want to thank project organizers at ScummVM team for selecting me and giving me a chance to participate in this program. Of course, I also want to say huge thanks to my mentor – Eugene Sandulenko, aka, sev, who guided and helped through this entire period. He was always there for help when I got stack and provided deep explanations when I did not know certain things. I also would like to thank other developers who reviewed my code, provided suggestions and pointed to mistakes. Finally, I am grateful to GSoC organizers for providing this incredible opportunity.

GSoC: Final Blog

Goals of the Project

The aim of the project was to integrate a File Integrity Service into ScummVM, along with a supporting application for managing the database. The main goals were:

  1. Populating the database with the detection entries used by ScummVM for game detection, followed by existing game file collections containing full MD5 checksums.
  2. Developing a utility for scanning game directories and generating data to further populate the database.
  3. Allowing users to verify their game files through this integrity service, and contribute new game variants.
  4. Building an application for managing the server and the database with role based access.

What I Did

  • A large portion of the work involved rewriting the codebase, as the existing logic for filtering and matching the filesets was not correct.
  • For ensuring correctness of code while matching old game collections, a lot of manual checking was required for over 100 different engines.
  • For the scan utility, I extended support for legacy Mac file formats (AppleDouble, MacBinary, etc.), ensuring proper matching with filesets present in the database.
  • For user integrity service, I worked on reporting information like Missing files, Unknown Files, Mismatched Files and Ok files. Further, I built a moderation queue system for user submitted files and also solidified the checks on user submitted data to allow submission of only valid data.
  • For the application, I added support for manually merging filesets, updating filesets within the application, improved search filtering and logging. Further, I added a configuration page for customising some user display settings and integrated Github OAuth with role-based access control (Admins, Moderators, and Read-only users).

Current State and What’s Left

The application is in a complete working state. All the workflows are functioning properly. The remaining step is to populate the database with all existing data so the integrity service can start operating officially.

Code that got merged

List of PRs that got merged – 

Server Side:

Github OAuth

Extending Web app features

User File Integrity Check, Web App updates and Project Restructuring

Scan Utility and Scanned fileset matching

Ruff formatter and linter

Macfiles support, Initial Seeding and matching with old game collections

Punycode

Readme Update

ScummVM:

Freeze issue for Integrity Dialog

 

Challenges and Learnings

The most challenging part was handling the variations across engines and game variants, and ensuring correctness in the filtering and matching process while populating the database. This often required manual validation of filesets. Working on this project taught me about the level of care needed to maintain the code and the importance of sharing the thoughts with the team. It was a highly rewarding experience working with ScummVM, and I am very grateful to my mentors Sev and Rvanlaar for their guidance and support throughout the project.

GSoC Final Work Product

Project Goals

The main goal of this project was to add text-to-speech, or TTS, to a variety of ScummVM engines. TTS enhances the accessibility of games supported by these engines and helps language learners. Furthermore, adding TTS to more engines standardizes the implementation of this feature, meaning the inclusion of TTS will be more consistent across engines. Adding TTS to an engine entails adding responsive voicing of text elements such as dialogue, menus, user input, objects, and credits, as well as adapting TTS to user actions and the functionalities of the games themselves.

What was Done

The original goal of the project was to add TTS to 12 engines, but I had enough time to add TTS to an additional 3 engines. Therefore, I added full TTS support to a total of 15 engines over the summer. In addition, I added TTS to 2 engines prior to the official start of GSoC, and I helped fix a few bugs for one engine.

Current State

All of my pull requests have been merged, meaning 17 engines now have full TTS implementations. Games that use these engines should have comprehensive and usable TTS.

What’s Left

All of my project’s goals, as well as a few stretch goals, have been completed. Nonetheless, some engines that support a wide variety of games, such as SCUMM, may need additional testing by end users. Any required changes found from this testing should only be tweaks to behavior: the core of the TTS implementations themselves should be complete. In addition, there are still a few engines left that need TTS support.

Code

Most PRs are for adding TTS to certain engines, but some are fixes for previously merged code. The following is a list of each PR I opened:

  1. TTS for Drascula: https://github.com/scummvm/scummvm/pull/6526
  2. TTS for TeenAgent: https://github.com/scummvm/scummvm/pull/6566
  3. TTS for WAGE: https://github.com/scummvm/scummvm/pull/6669
  4. Fix for Russian TTS for TeenAgent: https://github.com/scummvm/scummvm/pull/6722
  5. TTS for Cine: https://github.com/scummvm/scummvm/pull/6700
  6. TTS for CruisE: https://github.com/scummvm/scummvm/pull/6710
  7. TTS for Draci: https://github.com/scummvm/scummvm/pull/6742
  8. Fixes for TTS for CruisE: https://github.com/scummvm/scummvm/pull/6752
  9. TTS for MADE: https://github.com/scummvm/scummvm/pull/6779
  10. TTS for ADL: https://github.com/scummvm/scummvm/pull/6785
  11. TTS for Parallaction: https://github.com/scummvm/scummvm/pull/6795
  12. TTS for Prince: https://github.com/scummvm/scummvm/pull/6807
  13. TTS for EFH: https://github.com/scummvm/scummvm/pull/6820
  14. Bug fixes for EFH: https://github.com/scummvm/scummvm/pull/6821
  15. TTS for MM: https://github.com/scummvm/scummvm/pull/6835
  16. Czech and Polish TTS for TeenAgent: https://github.com/scummvm/scummvm/pull/6867
  17. TTS for SCUMM: https://github.com/scummvm/scummvm/pull/6856
  18. TTS for AGI: https://github.com/scummvm/scummvm/pull/6862
  19. TTS for Gob: https://github.com/scummvm/scummvm/pull/6880
  20. TTS for Got: https://github.com/scummvm/scummvm/pull/6888
  21. Fix for TTS for Got: https://github.com/scummvm/scummvm/pull/6900
  22. TTS for Hugo: https://github.com/scummvm/scummvm/pull/6897
Challenges and What was Learned

Ensuring TTS compatibility across numerous games, languages, and versions that a single engine may support was sometimes challenging to comprehensively address. Furthermore, some engines handle their text almost entirely with game scripts, which makes it more difficult to narrow down where text is being displayed and how to feed it to TTS. To address these problems, I had to test thoroughly, think about limitations and features that differ between games or platforms, and employ strategies such as recreating click boxes to properly voice text as the user interacts with the game. Thus, most challenges with adding TTS revolved around compatibility and logical voicing of text elements.

I learned a great amount this summer about working with a team, understanding and modifying code written by other developers, and writing code that fits into an existing codebase. I am now more comfortable with navigating and working on larger projects.

Conclusion

I enjoyed working on my Google Summer of Code project, and I found it to be a very entertaining and rewarding experience. I am happy that I was able to contribute something to ScummVM, and I hope that it will be helpful to users.

I would like to thank my mentor, criezy, for kindly guiding me throughout the summer, and sev, for guiding me as well. I would also like to thank all members of the ScummVM team that I interacted with for their patience and assistance, and the entire ScummVM team for their work on this application.

Expanding Director Engine compatibility in ScummVM

For the last 12 weeks I’ve been working on the Director Engine to increase support for it in ScummVM. Originally created by Macromedia, the Director Engine is under development for the last 9 years in ScummVM now. My goal was to inch ever closer to fully supporting games made using Director 4 and Director 5.


Goals for the Project:

I had outlined the following three major goals in my original proposal:
1. Completing support for all the workshop movies and implement all the stubbed functions.
2. Writing Director Files in ScummVM by implementing saveMovie lingo command.
3: Add support for titles SafeCracker and The Journeyman Project in the Director Engine.

Goal number 2 was the primary goal of the project, which I worked on first.

Goal number 3 was changed partially. Instead of working on the titles SafeCracker or Journeyman Project, I tried to work on Trektech.

Goal number 1 was changed. Instead of working on Stubbed functions and workshop movies, I worked on the ImGui visual debugger in the Director engine.

The goals for this project were regularly discussed with my mentor @sev.


What I did:

Before and during the initial few days of the coding period, I worked on the QTVR Xtra for the Director Engine. I implemented rendering in higher quality and warp (mode 0) for panoramas. I also implemented the swing transition while panning panoramas.

To add support for games like tkkg-1, I implemented the lingo command saveMovie which saves the current movie in its current state, effectively working as a save file.

I tried implementing support for movies as cast members which was an essential feature required for the title Trektech. However, I only went as far as loading them and processing lingo scripting for simple movies. I couldn’t get it to work for Trektech. Since, it proved too much for my level of experience, as per my mentor @sev’s advice, I left the work incomplete and pivoted to the next task.

I fixed the following bugs in Director.
1. Fix Duplication of cast members in case of multiple casts.
2. Avoid usage of  unreliable value LingoDec::Scripts::CastId.
3. Disabling event processing while b_alert is called.
4. Fix deprecation warnings in Director engine compilation.

I also made some upgrades to the ImGui visual debugger in the Director engine. Majorly including:
1. Rendering previews for some of the cast members.
2. Improving/refactoring the Script/Callstack window into the Execution Context window.
3. Improving the Score window to show continuation data.
4. Implement Loading/Saving ImGui state.
5. Reworking Functions window to use reliable references.
and a number of other small things.


The Current Status:

The saveMovie lingo command was successfully implemented. This was also tested and verified using the title tkkg-1.

Support for Trektech and movie cast members is incomplete. Simple test movies with basic lingo scripting work well. However, it is unreliable as seen by the crash in Trektech, as soon as it tries to load a movie cast member. I will continue working on this.

The ImGui debugger has seen a fair number of upgrades. However, it does need more work to be called complete. I still have a draft PR continuing the work.


Implemented code:

Here is a link to all the pull request that were merged during the period (18 in total).

Here is the link to the draft PR for ImGui improvements.


Challenges faced/Lessons Learned:

Director engine, as mentioned, is under development since the last 9 years. A lot of the development is already complete. Hence, it was difficult for me to grasp its large code base, given my limited experience with large projects. On top of that, My insistence on figuring out solutions independently proved harmful, sometimes resulting in slow progress.

The Director engine is a fun engine to work with. I learned a lot of concepts related to Software Development and Object Oriented Programming. I also learned how to use version control systems properly.  I also learned how to communicate programming ideas when there are multiple people working on the project.


I enjoyed working on ScummVM. So much so that I’ll continue contributing occasionally. I am thankful for the organizers of GSoC and my mentors @sev, @rvanlaar, @OMGPizzaGuy and @somaen, for guiding me through. I hope my contribution benefits developers as well as users of ScummVM.

GSoC Final Report

My project focused on extending ScummVM’s Keymapper system to a larger number of game engines. The Keymapper allows players to remap controls to their liking, but many engines in ScummVM still relied on fixed input handling. By integrating the Keymapper into more engines, the project set out to improve accessibility, provide a consistent user experience, and give players greater flexibility in how they play.


What I did:
I added Keymapper support to 24 engines, fixed a few bugs encountered during the implementations, and helped standardize keymapper action descriptions. This standardization reduces the number of unique strings in the codebase, easing the workload for translators.


The current state:
Keymapper support is now available in 24 additional engines, allowing players to remap controls in a wide range of games that previously relied on fixed input. Several of my pull requests have already been merged into the main ScummVM codebase, and the remaining ones are under review. Overall, the project goals have been successfully achieved, and the improvements are now part of ScummVM’s ongoing development.


What’s left to do:
All goals outlined in my proposal have been completed, I added Keymapper support to the 23(The first one was not part of proposal) engines I had committed to. Beyond the scope of the proposal, there are still around 40 engines in ScummVM without Keymapper support, which could be future work for anyone interested in continuing this effort.


List of PRs:

  1. Toltecs keymapper: Pull Request
  2. Sludge keymapper: Pull Request
  3. Supernova keymapper: Pull Request
  4. Voyeur keymapper: Pull Request
  5. Titanic keymapper: Pull Request
  6. Normalize keymapper action descriptions: Pull Request
  7. Sword25 keymapper: Pull Request
  8. TeenAgent keymapper: Pull Request
  9. NGI keymapper: Pull Request
  10. Buried keymapper: Pull Request
  11. Access keymapper: Pull Request
  12. EFH keymapper: Pull Request
  13. Sherlock keymapper: Pull Request
  14. Neverhood keymapper: Pull Request
  15. Prince keymapper: Pull Request
  16. Lab keymapper: Pull Request
  17. Petka keymapper: Pull Request
  18. Queen keymapper: Pull Request
  19. Fix capitalization in Queen keymapper action descriptions: Pull Request
  20. Pink keymapper: Pull Request
  21. Drascula keymapper: Pull Request
  22. Chamber keymapper: Pull Request
  23. Make EFH keymapper table use POD and avoid global constructors: Pull Request
  24. Hypno keymapper: Pull Request
  25. DM keymapper: Pull Request
  26. Private keymapper: Pull Request

Any challenges or important things you learned during the project:
Each engine in ScummVM handles input differently, so I had to study and adapt to new codebases before adding Keymapper support. In doing so, I often uncovered and fixed unrelated bugs. Working on a new engine every 3–4 days also greatly improved my ability to read, understand, and modify existing code, and gave me confidence in working with large, mature projects.


Conclusion:
This project successfully achieved its goals by bringing Keymapper support to 24 engines in ScummVM, making input more flexible and accessible for players. Along the way, I learned how to work effectively with large codebases and contribute to a mature open-source project. I’d like to thank my mentors and the ScummVM team for their guidance and support, and GSoC for giving me the opportunity to work on a project that will benefit both developers and players.

Prepare to Descend Deep Into the Mine to Uncover Its Dark Secrets

The ScummVM team is happy to announce full support for the first-person survival adventure Penumbra: Overture!

Step into the boots of Philip, who is drawn to Greenland in search of answers after receiving a letter from his long-lost father. Once there, you find yourself trapped inside an abandoned mine… with no way out but deeper in.

The game is fully 3D and presented in a first-person perspective, featuring elements of stealth and physics-based puzzle-solving.

If you own a copy of the game and want to help test this release, download a daily build and be sure to read our testing guidelines.

Don’t forget your torch. You’re going to need it.

Another upgrade to the ImGui Debugger

For the last week of GSoC, I was working on some more improvements to the ImGui Debugger in the Director engine. Making the ImGui debugger as close to the original Director engine as possible will be very helpful in adding support for games in the future.

The first thing to do was to revamp the Functions window. It used to show all the handlers from all the scripts in the window in a single table. Since, that was not only messy and not properly navigable, @sev asked me to revamp it so that there are two views:

 

 

 

 

 

One: that shows all the handlers just like how it was shown previously and two: one that shows each script, and each script (‘Lscr’ context) and under each script we have all the handlers in that script. This allows us to navigate between scripts very easily. This also means, in the second view, the filtering works better, i.e. we can filter out all the handlers with a certain name (e.g. mouseUp) or those associated with a particular cast member (say no. 123) easily.

After that, we noticed that some of the scripts didn’t show the cast member number in the Execution Context window properly (e.g. -1 for the topicmaker:mnew in the screenshot):

Now, the cast id (even though present in the ‘Lscr’ data stream, is not reliable) is fetched from the associated cast member. Since cast IDs and script IDs are linked together, we can map the script ID to its corresponding cast ID. But, it was possible that some of the cast members are not loaded, because of lack of support or some error (e.g. Picture cast member). Hence, at first I thought, forcing the loading of their cast member info might solve this problem. However, I was wrong. But I thought it might still be a good and harmless addition, hence I made a PR.

It turns out that some of the ‘Lscr’ contexts are factory scripts, i.e. they have a parent script (which is associated with a cast member) and the handlers in the current script context are called in reference to the parent’s cast member. As you can see from the following screenshot of LCARS___.dir loaded into the origin Director engine, the handler `topicmaker:mnew` is part of a movie script (cast ID: 1285):

Even while dumping the scripts, ProjectorRays/ScummVM dumps the handlers in the current script as part of the parent script and completely ignores the current script (the one without a cast member associated):

This is the list of all the parent scripts and their child script:

“`
Who is: 236, whose parent: 89
Who is: 239, whose parent: 93
Who is: 232, whose parent: 234
Who is: 188, whose parent: 599
Who is: 597, whose parent: 623
Who is: 216, whose parent: 624
Who is: 21, whose parent: 646
Who is: 230, whose parent: 657
Who is: 26, whose parent: 721
Who is: 131, whose parent: 722
Who is: 24, whose parent: 723
Who is: 179, whose parent: 725
Who is: 131, whose parent: 727
Who is: 163, whose parent: 728
Who is: 199, whose parent: 729
Who is: 131, whose parent: 730
“`
Hence, the solution was to not show the associated castID but rather the script ID, which is unique to each script. Hence, it required storing the parent number and the script number in the ScriptContext class, and show that in the Execution Context window. This solved the problem. @sev then asked me to change all the references to a handler in a similar way, including the way we name the dumped scripts. This honestly took way too much time to figure out.

Also, the scripts are now shown in a separate window when we click on a handler in the Functions window rather than showing them in the Execution Context window. Hence, we can see as many scripts as we want at the same time.

Also, the handlers in the call stack are now selectable. You can click on them to jump to the exact byte code in the script shown below in the Execution Context window. To make the scrolling work, I had to include a separate boolean in the ImGuiState struct.

After that, there were a bunch of minor issues, like the scrolling in the Execution Context window when we jump to the definition, sanity checks for fetching `ScriptContext` through the handler Director::DT::getScriptContext, marking the script dirty when a button in the control panel is placed, and some other stuff too minor to notice here.

Also, in the Score, the original director shows whether or not a sprite changes in subsequent frames. Like follows:

I was working on adding a similar functionality in the Score window, but I’m only halfway there.

I’ll be adding the rest of the functionality in the next two days.

Hence, overall a slightly less than average week. I was busy over the weekend for the academic project for final year (my faculty advisor is not happy with our progress). Initially I planned on making this post my final submission, but later decided against it. I’ll be making a separate post underlining all the progress that I made over the last  12 weeks as my final submission.

Week 12

 

Last week was mainly spent by fixing left-over bugs from previously worked engines: WAGE, MacVenture and SLUDGE.

At the beginning of the week, me and Sev finished work on redumping and preparing ultimate archive of WAGE-based games. Finally, after multiple checks the we were able to prepare the collection and release it on the site, together with the engine announcement. However, a couple of issues was reported by the devs, so I returned back to the engine to fix them. In particular, I had to fix issues with closing the game, where confirmations pop-ups were not really working, i.e. the games were not closing. Also, for some games, closing them in midst of playing sounds resulted in crash, because the thread for audio was referencing the deleted engine object. Lastly, there were crashes due to null pointer accesses in scripts, which are could be either due to buggy scripts or due to engine issues. There is a possibility for the last one, since the game that was crashing (Mormonoids from the Deep) also throws exception in the Java codebase, which was ported to ScummVM.

Another engine to which I returned was SLUDGE. There was a PR by ccawley2011 to add the game (demo) called Otto Experiment. However, the problem was that it was crashing at the startup. The problem as it turned out was that the code was not looking for the file inside the game directory, only in the save game dir. Also, in general, the support for reading .ini files was missing. This was mainly used for language setting, some graphical preferences and so on. This feature appeared to be rare in general, however it was used in Otto Experiment and Cubert Badbone, so it must have been dealt with.

Last but not least, I did some work on on MacVenture. There was a few a differences with the Mac versions that needed be done: main game window names (which were not upper-cased) and console window.

Week 12: Keymapper Support for Hypno and Dungeon Master

This week I worked on the Hypno and Dungeon Master engines. I also got my Chamber keymapper PR merged 🎉.


Hypno Engine

Working on the Hypno engine felt a bit nostalgic—it was actually the first engine I touched in ScummVM when I fixed a small bug as an intake task for GSoC.

The engine supports three different games, each with its own input handling. Because of this, I had to create separate keymaps for each game, rather than a single shared one.

Apart from that, the implementation was fairly straightforward. The main tasks were:

  • Disabling the keymapper in certain sections where the game uses full keyboard input

  • Enabling/disabling keymaps for some of the menus

Nothing particularly tricky, but it was nice to revisit the engine where my GSoC journey began.


Dungeon Master Engine

Dungeon Master was another unannounced engine that I added keymapper support to. Fortunately, it didn’t have any blocking bugs or missing features, so I was able to test it thoroughly without issues.

The engine came with a decent number of keys to map, but overall the process was smooth and didn’t pose any significant difficulties.


Wrap-Up

This week, I:

  • Added keymapper support for Hypno and Dungeon Master

  • Got my Chamber PR merged 🎉

Week 12

Welcome to this week’s blog. This week, I added features related to updating metadata as well as file data directly from the application UI, along with some smaller fixes and improvements.

For any fileset, you can now update metadata fields directly from the UI. For user filesets in particular, there is an additional step of adding metadata first particularly gameid and engineid as they require creating entries in separate tables. To make filling metadata easier, I also added a dropdown feature that displays all existing values for a field from the database. This way, moderators can either type in a new value or directly choose an existing one. In addition to metadata, I added functionality to update individual files as well. This can be useful for tasks such as manually marking a file as detection file or updating other fields.

For better reliability, confirmation dialogs have been added for most buttons, such as deleting/updating files and adding/updating metadata. Further a separate button has been added for deleting the entire fileset. Another improvement is the ability to delete all filesets in bulk that appear in a filtered search result in the fileset search page.

To enhance logging for scanned files, a new field called data_path has been introduced. This field stores the relative path of the game directory, which is particularly useful when multiple files are scanned at once. This information can later be included in scan.dat related logs.

Lastly, I added an endpoint for sending a fileset ID as a mail notification. This is suppose to be triggered from the mail server whenever a user submits any fileset-related information, using a predefined mail structure in the ScummVM application. (This feature has not yet been integrated with the mail server.)

Week 12: Got and Hugo

Introduction

This week, I opened PRs for adding text-to-speech to Got and Hugo. They were relatively simple engines, and neither had any major challenges.


Got

I started this week by finishing TTS for Got. As previously mentioned, Got was quite simple for TTS: most of its text was easy to locate in the code, and text was rarely primarily handled by the game scripts. Thus, most of the game’s text could be voiced rather easily. For cleaner voicing, I included checks for when the score, jewels, or keys change, allowing their values to only be voiced when they change; delays as the credits are voiced to prevent them from moving too quickly for TTS to keep up; and voicing of dialogue as soon as it is started, so that it syncs well with the text while it appears one character at a time.

Nevertheless, one consideration for Got was the best means of cleaning text. In most cases, newlines break up sentences in dialogue, which results in choppy voicing if they aren’t replaced. However, some dialogue, such as signs, use newlines instead of punctuation to break up distinct sentences. If these newlines are replaced, TTS awkwardly pronounces everything as one quick sentence. My solution to this issue was to keep the newlines only if there are two or more in a row, as this is usually the case for sentences separated exclusively by newlines. Singular newlines tend to break up whole sentences, and thus should be replaced.

Ultimately, Got was one of the simpler engines that I’ve worked on. It required some care for properly cleaning up text and guarding against awkward voicing, but it otherwise offered few unique challenges.


Hugo

After Got, I worked on adding TTS to Hugo. Like Got, Hugo was a fairly simple engine for TTS. Its games are straightforward and similar to each other, which made it easier to implement functional TTS for all scenarios. Furthermore, it has few unique procedures of displaying text, so voicing most or all of its text only required adding TTS calls to a few methods. Nevertheless, Hugo required some consideration for its dialog boxes. Unlike most other engines that I’ve worked with, Hugo displays a considerable amount of its text in ScummVM dialog boxes, which have their own TTS. This means that opening or working with some dialog boxes, like the top menu, stops all TTS. Stopping TTS in this manner, however, can interrupt voicing of the sound setting, which is shown in Hugo’s score line. Voicing it as soon as it changes doesn’t work properly, as in many cases, the top menu is immediately opened again, thus interrupting the voicing. Therefore, my solution was to keep trying to voice the new sound setting for as long as the cursor is in a position to open the top menu. In this way, once the top menu finally fully closes and doesn’t open again, the sound setting will be voiced.

Other aspects of Hugo were rather simple. It mostly entailed implementing more TTS for the aforementioned dialog boxes, as their built-in TTS only voices text as it’s hovered over and only one line at a time, and it seemed to me that these boxes needed to be voiced as one clean paragraph or sentence and as soon as they appear; voicing scoring changes; and correctly queuing voicing when dialog boxes, user input, and scores are voiced, to prevent them from interrupting each other as dialog boxes appear.

In conclusion, Hugo was fairly simple. Because its games involved few different means of displaying text and simple controls, it didn’t offer that many unique challenges.


Conclusion

This week, I finished adding TTS to Got and Hugo. Both of these engines were simple to work with, and they didn’t have any major caveats or challenges.

Announcing support for World Builder-based games

After many years of development, games made with World Builder are ready for testing. The World Builder creation system was released for the original Macintosh and was extremely popular among developers. As such, we're announcing that over 160 games built on the engine are ready for public testing in ScummVM. Notable games made with World Builder include:

  • Ray's Maze. The game follows Frank Farley, a part-time adventurer, who finds himself in a Maze full of dangerous monsters and deadly traps. The objective of the game is to solve puzzles and escape the maze.
  • Radical Castle. You are given the role of a squire, who must bring back the stolen "Oracle" to the King to escape a fatal punishment.
  • Enchanced Scepters. The first game created using World Builder, and one of the few commercial games made with it. The player must find fire, earth, air, and water scepters and bring them back to the castle.

Most games made with WAGE are either freeware or shareware, and for your convenience we've collected all titles with these types into one big downloadable archive. So grab it from our game downloads page, get yourself a daily development build, and start your journey. As always, if you happen to have any issues, please submit the bug reports to our issue tracker. Lastly, contact us if you have some other unrecognised games or a new demo.

ImGui: Refine the debugger to a usable state

This week again, I continued working on the ImGui visual debugger in Director, mostly working on small bugs and broken features caused by the work in the previous weeks.

The Execution Context window shows the lingo scripts being executed. To make it show multiple handlers from the same script at once, I had made changes that broke the navigation. Stepping in, stepping over and stepping out in the control panel didn’t work properly, jumping multiple instructions at once. The problem was that startOffsets of the script weren’t getting stored, since I needed to render multiple handlers, each with a separate ImGuiScript object. These startOffsets store the offsets of each line of script. While stepping in/over/out, the lingo program counter is compared with these to find out which line is getting executed at the moment.  I had to make sure that the ImGuiScript object associated with handler currently being executed persisted and was passed by reference to the renderScript() function so that it will store the startOffsets properly. Also I had to make sure that the _script->_dbg._isScriptDirty flag is set after all the scripts are rendered. Previously it was set to false after rendering the first script.

In the lingo scripts in the Execution Context window, if there is a call to a function, you can press the function to go to its definition. However, the search for the handler was flawed. The value we were passing to the function that fetches the ImGuiScript as the castId of target script, was wrong. It took me some time (and some messing around with ProjectorRays) to figure out that this is the Id (or index) of the script (in cast of a localcall, this is the index of the script in the script context (consisting of scripts in the same file/’Lscr’ resource), whereas in an extcall it was the index of the script in the global context (consisting of all scripts)).

This is obviously wrong, hence I had to find out the actual castId of the script which contains the handler, since castId and scriptId are coupled, I can find the castId using the scriptId and pass that, which worked as expected.

During this, I also realized that for some reason, some of the scripts weren’t showing up in the debugger at all, apparently for no reason at all. Upon some investigation, I found out that the way the scripts were fetched was, that the LingoDec decoder reads these scripts and the corresponding data. One of which was the castId of the script, which it turns out isn’t reliable. It may be wrong, it may have duplicates, can’t be trusted. Hence, instead, from the HashMap that maps the castID of a cast member to its script id, I fetch the castID and use that to fetch the script, which worked like a charm. This PR should explain the issue in detail. This also solved an issue where, when asked to dump the scripts, the names of the files dumped and the scripts didn’t match at all.

I had to also solve scrolling issue in the Scripts window. After jumping to a handler, if the script contained only one handler, the scrolling worked fine, but in case of multiple handlers, it used to jump to the last handler. Had to introduce a _scrollTo flag which was set only when rendering the current script.

I also got rendering of text and shape previews this week. In order to render them, I was at first checking which channel had the text/shape to be rendered, and pass that channel and a ManagedSurface to Window::inkBlitFrom(), and showing the surface in the preview. However, obviously, this meant that if a shape/text is not on the stage at the moment, it won’t show a preview. Hence, I had to prepare a dummy channel with the correct sprite and relevant properties (e.g. thickness, foreground color, background color, pattern, etc.) and then blit it to a temporary surface for preview. This honestly took some time to figure out.

Other than this, there were some minuscule things, too unnecessary to mention here.

Hence, overall a pretty normal week again. Next week is the final week for coding after which there is the final submission. I hope to have done enough to count the project as a success.

This week also brought good news in the form of a job offer. In most engineering colleges in India, recruiters visit the college to hire final year students as graduate trainees. I was lucky enough to be selected at one such offer. Now that I have this security, I can continue to contribute to ScummVM even after GSoC. Not as regularly but over weekends and holidays. Working on ImGui has been fun but I wanted to directly work on making a game work in ScummVM’s Director, similar to how @rvanlaar was working in SafeCracker. Couldn’t unfortunately succeed at that during GSoC but I’m planning on doing so afterwards. Looking forward to that!

Week 11: Keymapper Support for Drascula and Chamber

This week I worked on two more engines: Drascula and Chamber. I also got my PRs for the Pink, EFH, Drascula, and Lab engines merged 🎉.


Drascula Engine

The Drascula engine wasn’t particularly difficult, but it came with a decent number of keys and keymaps. The process was straightforward overall, though it was somewhat time-consuming due to the sheer amount of keys that needed replacing.

One interesting aspect of this engine was that I came across my first in-game easter egg while testing the key actions—definitely a fun surprise during the work.

I also had to handle enabling and disabling keymaps in a few spots. Nothing too complicated, but still took time.


Chamber Engine

Chamber became my second unannounced engine (after Sludge engine) where I added keymapper support. Normally, I’ve been focusing only on announced and tested engines, to avoid running into unrelated bugs that might interfere with the keymapper work.

In this case, I only realized the engine was unannounced after I had already started working on it. Since I didn’t encounter any gameplay-breaking issues, I decided to go ahead and finish the keymapper implementation.

The work itself was on the simpler side. Chamber only had a handful of keys, and just a single keymap that needed to be toggled on and off. Figuring this out was quick and straightforward.


Wrap-Up

This week, I:

  • Added keymapper support for Drascula and Chamber

  • Got my Pink, EFH, Drascula, and Lab PRs merged 🎉

Week 11

In the previous I continued to work printing the task. I’ve added the initial options to printing such as layout (portrait, landscape), margins as well as the selecting the printer itself. In the process of adding these, I’ve customed myself a bit to Win32 printing API, since this is the only backend with which I was testing the actual printout.

First of all, I continued the work from the last week by introducing PrintingDialog, which will be opened when printout happens. To it, I’ve added layout and printer selection. In Win32 Print Spooler API, printer list is a string array, so for now, in the PrintingManager class, this virtual method is also Common::StringArray. Implement it was quite easy, since the documentation is there and not much code was needed. By adding printer selection, users can select the real physical printer or virtual one to get PDF-output. Since I have the actual printer, I started testing the code and see what was the output:

Tiny ScummVM logo printed as a test
CLUT8 test remained from the original PR

As you can see, while the logo and were printed, they were too small and not centered. This made me to spend some time actually looking how different platforms handle image printing. After comparing printer dialogs in different browsers, operating systems, I concluded I definitely needed to add scaling (or, rather, fitting to paper) and margins. To not waste more paper, I’ve been testing with PDF output since then, and was able to get the printing centered and fit to paper.

After that I started to think about preview image functionality, which is common these days in printer dialogs. Luckily, there is already an ImageAlbumDialog present, which had more enhanced image output as well as image preview. That meant I only needed to migrate all dialog code there. I’ve managed to make printing work when the right command is send, however, implementing GUI nice looking correctly (so that both preview appears on the left and printer settings on the right) appeared to be a little harder, and is not yet finished. This week I plan to fix this, and as was commented and suggested earlier start adding printing support to actual engines and see if the current API functionality is enough or needs to be adapted. Before that I plan to quickly fix a few things (mostly visual differences) in MacVenture engine, so that it looks and plays like in the original Macintosh.

 

Week 11

Welcome to this week’s blog. This week, I worked on testing the workflow for scan data as well as the user file integrity service.

For the scan data, we tested with the WAGE game archives, which provided a good opportunity to test both the scan utility and the scan matching for the Mac files. Some fixes were indeed needed for the matching process. Initially, I was using both size (data fork size) and size-rd (resource fork’s data section size) simultaneously while filtering filesets. However, this was incorrect, since detection filesets only contain one of these at a time. Additionally, I fixed how matched entries were being processed. Previously, entries matched with detection were placed for manual merge to add specific files while avoiding unnecessary ones like license or readme files from commercial games. However, it made more sense to merge them automatically and later remove such files if necessary—especially since, for the archives like WAGE, the issue of extra files from commercial games would not occur.

I also carried out testing for the user integrity service, focusing on different response cases:

  1. All files are okay when a full fileset matches.

  2. Extra files are present.

  3. Some files are missing.

Another missing piece was reporting files due to checksum mismatches, which previously was being classified under extra files. This is now fixed. I also reviewed the manual merge process for user filesets. Unlike set filesets, the source fileset (user fileset here) should not be deleted after a manual merge, since it could be a possible new variant which would need additional metadata information. To support this, I implemented a feature to update fileset metadata—though it still requires some refinement. An additional thing that I need to add is to create an endpoint in the web server that can be triggered by the mail server. This endpoint will provide the mail information, particularly the user fileset ID, for which the user has provided some additional information via the pre-drafted email that is promted when user uses the ‘check integrity’ feature in the ScummVM application.

A few other fixes this week included:

  • Deleting multiple files from a fileset through dashboard: Previously, the query was being generated incorrectly. Instead of ‘DELETE FROM file WHERE id IN (‘1’, ‘2’, ‘3’)’ it was generating ‘DELETE FROM file WHERE id IN (‘1, 2, 3′)’ which, of course, did not work. This issue is now fixed.

  • Search filter issue: A bug occurred when a single quote (‘) was used as a value in search filters, breaking the query due to missing escaping for the quote. This has also been fixed.

Week 11: Gob

Introduction

This week, I opened a PR for adding text-to-speech to Gob, my first stretch goal. It was an engine that posed a few more challenges than usual, but that was entertaining to work on. I also started early work on adding TTS to Got as my next stretch goal.


Gob

Gob was a rather interesting engine for adding TTS to, primarily because of how it handles text and buttons. Unlike many of the engines that I’ve worked on, hotspots and text are inherently separated in Gob. This meant that there was no easy way to retrieve the text of a button or menu as the user hovers over it, which makes it more difficult to voice. Furthermore, without knowing which text is part of a clickable hotspot and which text is not, text that shouldn’t be voiced the instant it appears will be voiced. My solution to this issue was to maintain an array, _hotspotText, which holds printed text and their positions. Then, as hotspots are added, their collision boxes are checked against those of the text array. If they intersect, the hotspot text’s collision rect is expanded to that of the hotspot. Through this method, hotspot text can be voiced as the user hovers over it, while most other non-hotspot text is voiced by a separate method to make sure that all text on screen is voiced. Nonetheless, refining this strategy took some time, as I encountered issues with properly removing elements from _hotspotText when text is removed from the screen, which I resolved by deleting unassigned hotspot text in certain methods and removing pieces of assigned hotspot text when their corresponding hotspots are removed; the cursor being instantly placed over a hotspot in some cases, interrupting the speaking of non-hotspot text, which was resolved by queuing hotspot text when a non-hotspot piece of text is voiced; and having to move hotspot text in o1_copySprite, as the text itself is sometimes moved there, and not moving the hotspot text with it results in a disjointed collision.

Another problem with Gob was timing of dialogue. From what I could tell, there are no unique indicators for when dialogue starts, progresses, or ends in Gob, which causes issues during dialogue interactions, as they may move too fast and interrupt voicing. In addition, dialogue appears to be displayed as TOT text, but so are object names, which means that TOT text can’t be set to always interrupt or always queue without causing awkward voicing. My solution was to only queue text if the user isn’t hovering over a hotspot, as in most cases, this means that it’s text that should be queued. After resolving this issue, most of Gob’s other problems – including needing to improve voicing of Ween’s notepad, which awkwardly tries to voice one character or section at a time as the user types, by only voicing it when it first opens – were rather straightforward.

Ultimately, Gob was an interesting engine because of how it handles hotspots and dialogue. It required different solutions from previous engines, and I’m quite happy with the result, though there may be more problems to resolve in the future.


Got

After Gob, I started work on adding TTS to Got. So far, Got seems very simple for TTS. Most timing and menus do not seem to be handled by game scripts, which makes it easier to locate where to voice text and to implement clean voicing. At the moment, nothing difficult has emerged, but this may change as I add more TTS to the engine.


Conclusion

This week, I added TTS to Gob, and started work on adding TTS to Got. Gob had several considerable intricacies, while Got appears to be rather simple so far. Next week, I’ll be continuing work on Got, and perhaps starting work on adding TTS to another engine if time permits. However, my semester starts next week, which will limit the time I have to work on my project.

ImGui: Improving the Visual Debugger

This week, I continued my work on Director ImGui debugger from last week. Working on ImGui has been bit of a fun exercise since, it is an open canvas. There are a lot of opportunities to improve upon, and it all depends on what a developer might find useful while working on Director engine.

First of all, there were a bunch of impurities with the ImGui debugger. e.g.

  • Many child widgets have the same problem as the breakpoint window error pop-up from last week. i.e. widgets having the same label and id causing the ImGui throw an pop-up error saying changes made to one widget will be reflected to the other. I solved the same problem in the following places:
  • i.   Vars Window
    ii. Watched Variables
    iii. Scripts handler widget
  • I also improved the search for handler in the getHandler function. Instead of searching for the handler in all casts, we can single out the cast that has the handler in it.
  • When displaying Prop list in the execution handler window, there was a missing line, which I promptly added.

And a bunch of other small stuff, too small to be worth noting here.

Another feature that was stubbed in the debugger was being able to load and save the current state of the debugger into a persistent file. So, we can save a position in the game we are currently working on, and load the same position every time we start  a debugging session.
We went with the JSON format to store the state. We are currently storing the following things (and loading them back):
1) Window Positions
2) Score frame number
3) Windows that are open/closed
4) Global, Local and Watched Variables
5) Breakpoints
6) Log
7) Colors
Here, @OMGThePizzaGuy and @lephilousophe showed me `ImGui::SaveIniSettingsToMemory/ImGui::LoadIniSettingsFromMemory` which made my job much easier.

In the Watched Vars window, I am also showing Local Variables and Global Variables with different colors. On top of that, Local Variables that are out of scope are shown with different color.

All variables are now shown with `renderVariable()` which allows variables to be clicked on to add them to the WatchedVars list and also show their current value.

Previously, the Scripts window only showed the handler being executed at that moment. However, for better navigation, @sev suggested that all the scripts from the same lingo context should be shown in the same window. This took me a while. Since, now not only was I rendering multiple (ImGui) widgets for multiple (Director) windows, I was also showing rendering multiple handler in the same Scripts widget. This caused a bunch of problems with stepping over e.g. pressing step over caused the script to move multiple steps forward, and with showing the current line in the handler (with a little yellow arrow), because the _state->_dbg._isScriptDirty was getting set to false immediately after the first script was rendered. Also, the scroll to the current line was also not working. This took some time and debugging to fix.

When @sev started review on my PR, he immediately pointed out that I was making a separate ImGuiState::state for each window, which in hindsight I realized was a mistake. So, I reworked it to only make a separate instances of the objects that are needed to hold the execution context of a window. This also took a while to get right. There was a bunch of unexpected behavior including, the scripts were not showing up the first time the ‘step’ button was pressed in the control panel.

I also fixed a deprecation warning that occurred while compiling the Director engine. I had to create a custom copy constructor for the MacWindow class since the synthesized copy constructor was calling a deprecated copy constructor for the ManagedSurface class. Instead I had to use a copyFrom method as suggested.
There was another instance of use of deprecated method operator= for the ManagedSurface class, used copyFrom there as well.

Overall, a somewhat normal week. I hope to make even more improvements to the ImGui debugger. e.g. Adding a trace for watched variables is next on the list.

Week 10: Queen and Pink Engines

This week I worked on adding keymapper support to the Queen and Pink engines. On top of that, I also got my Neverhood, Prince, and Queen keymapper PRs merged.


Queen Engine

The Queen engine was moderately challenging. It came with a good number of keys, but the real twist was the variable keybinds for some actions. these depended on the language of the player’s copy of the game. This was somewhat similar to what I encountered earlier in the Sherlock engine.

Another tricky part was that the keybind selection logic was embedded deep in the engine code. To make it compatible with the keymapper, I had to refactor the code so that the language-based key selection happened in the keymapper section instead. This made it accessible for usage in the keymapper initialization.

Some sections of the game also needed keymapper toggling. those were relatively straightforward to set up.


Pink Engine

Pink, at first glance, seemed simpler—fewer than 10 keys in total. But as always, appearances can be deceiving.

In the game, you can’t move your character freely. Movement only happens when you click an interactable object or character, at which point the character walks to the target and interacts with it automatically.

The key actions didn’t just trigger standard gameplay—they altered this entire interaction sequence. For example:

  • One action skips the walking animation entirely.

  • Another lets you walk to the target but cancels the interaction.

  • Yet another skips both walking and interaction, simply teleporting you there.

There were also keys that modified or skipped the sequence that plays when you interact with a target—like skipping a conversation, skipping part of it, or even restarting a dialog from the beginning if you missed something.

Identifying exactly what each key did was the hardest part here, but once that was figured out, mapping them to the keymapper went smoothly.


Wrap-Up

This week, I:

  • Added keymapper support for the Queen and Pink engines

  • Got my Neverhood, Prince, and Queen PRs merged 🎉

Week 10

In the previous week I finished with fixing leftover bugs in MacVenture and later started working on bringing printing support.

The last problem that was left from the week before that was polishing console window. In particular, there were problems with how the text was displayed when new text was added or when the window was resized. To make things easier I decided to change that console window from Graphics::MacWindow to Graphics::MacTextWindow. That allowed to take care of text wrapping and scroll management at once. However, to take it fully work, a few adjustments have to be made, as the class probably was initially created for the console with input (editable text). For example, to make the cursor be focused on the right place required to a little workaround, where I made the window editable, append the text, and make it uneditable again. Despite this, after it was implemented, I also added support for saving the text, so the save/loading functionality is also complete now.

After this with Sev’s suggestion, I started working on bringing printing support. It would useful for the MacVenture titles as all four games give a chance to print the diploma after completing the game. In addition it could be added to numerous HE games and others. I did not start from scratch, but rather from the previous attempt from few years back. I took the first few commits, and the later ones with some fixes. Then I added a simple test in testbed, so that I could test how the resulting pdf file. Once that was tested, I started adding code on top of it. I added a dialog for printing and along with it a feature to save the input surface as an image.

This week I plan continue the work on this1 and start actually implementing printing feature for the engines that needs it.

Week 10

Welcome to this week’s blog. This week, my work focused on enhancing API security, adding github authentication, refining project structure, and introducing a faster Python package manager (UV).

API Security Improvements

I implemented some checks on the validation endpoint, which processes the user game files data sent from the ScummVM application. These checks are designed to prevent any kind of brute-force attempts –

Checks on validation endpoint

On top of that, I introduced rate limiting using Flask-Limiter. Currently, the validation endpoint allows a maximum of 3 requests per minute per user.

GitHub OAuth & Role-Based Access

GitHub OAuth authentication is now in place, introducing a three-level role-based system. Though, I have tested it with my own dummy organisation, the integration with ScummVM is remaining:

  • Admin – Full access, plus the ability to clear the database.

  • Moderators – Same permissions as Admin, except database clearing.

  • Read-Only – Logged-in users with viewing rights only.

Github OAuth
Project Restructuring & UV Integration

As suggested by my mentor Rvanlaar, I restructured the project into a Python module, making the import logic cleaner and improving overall modularity. I also added UV, a high-performance Python package and project manager, offering faster dependency handling compared to pip.

Other Fixes & Improvements
  • Updated the apache config file to use the Python virtual environment instead of the global installation.

  • Correctly decode MacBinary filenames from headers using MacRoman instead of UTF-8.

  • Improved error handling for the scan utlility.

  • Use one of size or size-rd for filtering filesets for scan.dat in case of macfiles instead of both simultaneously.

Week 10: AGI

Introduction

This week, I opened a PR for adding text-to-speech to AGI. It was the last engine marked on my proposal, which means that I have now made PRs for every engine that was planned for my project. This is a major milestone, and gives me time to work on Gob as a stretch goal.


AGI

I spent much of the week working on AGI, which was fortunately a rather simple engine. While it supports many different games and fangames, most of these games seem to function very similarly: there may be introductions or other screens that display text using TextMgr::display, with most other text being displayed through popup windows, typed commands, or menus. Thus, AGI was fairly straightforward, requiring only a handful of TTS calls in key methods, as well as some consideration for when to voice the status menu and clock, which are always visible – I settled for voicing one or the other primarily when the game is loaded, the status menu changes, and the clock is enabled, to avoid voicing them too frequently – and when to stop TTS.

The major concerns with AGI were how it handles timing and TextMgr::display across games. Some games appear to use timer variables to signal when it’s time to change the text on screen, while others use when a sound ends. Therefore, from what I could tell, there was no consistent way to predict when text will change, which made it difficult to delay text changes until TTS is finished. I eventually settled for queuing text and delaying room and window changes until TTS finishes, which seems to be enough to allow TTS to voice everything displayed on screen in a reasonable manner. In addition, TextMgr::display differs between games: some call it only once, while others call it every frame, which necessitates keeping track of the previously said text to avoid speech loops. However, much of the text passed through this method is displayed in sentences or chunks, rather than all at once in a single call. This makes simply tracking the previously said text fail and results in awkward, choppy voicing when a sentence is broken across lines. My solution to this issue was to combine the text passed to TextMgr::display in a _combinedText variable, and then voice this when the game script returns or halts. Using this method, paragraphs and blocks of text are spoken cleanly as one sentence, and the previously said text can simply be set to this combined text to prevent speech loops. I also decided to add in newlines between these pieces of text if they aren’t displayed on subsequent rows, since in most cases, pieces of text that aren’t in subsequent rows shouldn’t be voiced as one quick sentence.

In conclusion, AGI was a fairly simple engine, since its games didn’t have much complexity. Most of the work went into handling timing and subsequently displayed text, as these factors can differ significantly between its many games. Otherwise, AGI fangames seem to have little variation in how they display text, which simplified work on it, though there’s a chance that games that aren’t fangames may function differently, as I mainly worked with fangames.


Gob

After opening a PR for AGI, I started work on Gob. So far, Gob doesn’t seem too complex: its text seems limited and appears to be displayed through only a few select methods. However, certain games, like Adibou 2, separate text from many of their buttons, which requires a technique to sync them. So far, I’ve decided on a method of checking whether the displayed text intersects with a hotspot, then expanding the collision rectangle of the text accordingly, which seems to work fairly well. Nonetheless, I would like to see if I can make this solution more robust for better compatibility, as it seems that Gob supports a variety of games with many differences between them.


Conclusion

This week, I opened a PR for my last planned engine, AGI, and started work on Gob as a stretch goal. I plan to have a PR up for Gob by the end of this week, which may possibly give me enough time to start another stretch goal.

Week 9: Lab and Petka

This week, I focused on adding keymapper support to two engines: Lab (used by The Labyrinth of Time) and Petka (used by Red Comrades 1 and 2)


Lab Engine

The Lab engine was moderately complex due to the game’s dual-interface design. The Labyrinth of Time features two main interaction modes: a point-and-click exploration interface and an inventory screen. This meant I had to track which interface the player was currently in and enable or disable the appropriate keymappers accordingly.

There are multiple ways to switch between these modes—such as right-clicking or clicking the inventory button—so I had to carefully identify and hook into all the transitions to ensure the keymappers toggled appropriately.

I also encountered, for the first time, a case where the game interface slightly varied depending on the version of the game. It was a simple adjustment but interesting to note.


Petka Engine

In contrast, Petka was simple and quick to implement. The game uses only a few keys and has a single interface, so a single keymapper was sufficient.

The only minor complication was the language barrier—the game is entirely in Russian, which made understanding certain actions and UI flows a bit more difficult. However, with some investigation and context clues, I was able to complete the mapping without major issues.


Wrap-Up

This week, I:

  • Implemented keymapper support for the Lab engine

  • Implemented keymapper support for the Petka engine

Week 9: MacVenture III

Another week spent on improving and finishing the MacVenture engine. In the last week I focused on improving things that happen after the winning the game, i.e, loading diploma window & dialog, implementing “Clean/Mess up” functions. I also fixed the bitmap problem I had with with a few items in Uninvited game. Lastly, I spent time fixing the resize button and porting the leftover methods from the original codebase.

I started the week with adding support for the diploma window. There was already a pointer variable in the codebase, but until before I did not know what kind of diploma it was referring to. The diploma we are talking about here is the certificate you get after completing the games.

To implement it, a first added another constructor for the ImageAsset class, as the format for the diploma image was a little different from the rest of images. Once that was done, the diploma image already started showing:

Diploma after completing Shadowgate

After that, I needed to add the dialog that had with “Print” and “Exit” buttons. There was already a Dialog class for that, however only with prebuilt options. Similarly, I added the constructor for this, however, the text was a little off. To mitigate the issue, I added an additional MacText text so that line wrapping can be easily handled. Once that was added the dialog at the bottom started showing correctly:

After that I started fixing the bug where certain items (charm and statue) in Uninvited game were incorrectly displayed:

In fact, you can see the issue in one of the images in the previous post. For debugging, I first identified the object id of the item, then looked how the image was loaded. The problem was in ImageAsset::decodePPIC0() method, in particular, in the section where 32 bits were read and then bitshifted to the right, until most significant byte was taken. At first I thought the problem was that BitStream32BEMSB which was used here was behaving a little different from the GFile class in the original, and because of that these artifacts were happening. And indeed, in the original codebase, the position of the stream did not advance right away after getBits() was called, but was incrementing the internal variable and advanced the position only after that variable was greater than 0xa. I thought maybe I should wrap BitStream template inside the custom class to imitate the behavior of the original code. However, after thinking a little more and checking the walkHuff() method, where similar thing was happening, I realized than none of that actually needed. BitStream32BEMSB was advancing stream position correctly in the first place, so the only thing which was needed was to read the first 16 bits, and skip the other half. And indeed, this two line change fixed the issue:

Another thing I copied from the original javascript codebase was “Clean/Mess up” functions. Clean up is handy when there a lot items in the inventory, and you want the thing sorted. This was a simply copy-n-paste, however, the end result is quite satisfying and as said, useful for players:

At the end of week, I started polishing the console window The window where text (log) is displayed. There were small issues fixed away: numbers not showing and resize button not working. The last one was similar to the close button covered in this post. The last thing which needs to be properly done with console window is getting rid of unnecessary new lines, making the scrolling properly work and showing the text correctly when resizing (it gets clipped, not wrapped).

Week 9

Welcome to this week’s blog. This week was a busy one due to my college workload, but I mostly focused on enhancing the webpage. I worked on the configuration page, the manual merge dashboard, filtering, search-related improvements, and more.

  • Configuration Page:
    I added a new configuration page that allows users to customize their preferences, including:

    • Number of filesets per page

    • Number of logs per page

    • Column width percentages for the fileset search page

    • Column width percentages for the log page

    All these preferences are stored in cookies for persistence.

    User Configuration Page
  • Manual Merge Dashboard:
    I performed some refactoring of the codebase for manual merging. Additionally, I added options to:

    • Show either all files or only the common ones

    • Display either all fields of the files, or just the full-size MD5 and size (or size-rd in the case of Mac files)

  • Search Functionality:
    I improved the search system with the following features:

    • Exact match: Values wrapped in double quotes are matched exactly

    • OR search: Multiple terms separated by spaces are treated as an OR

    • AND search: Terms separated by + are treated as an AND

  • Sorting Enhancements:
    The sorting feature now includes three states for each column: ascending, descending, and default (unsorted).

Minor Fixes & Improvements
  • Added favicon to display on the webpage tab
  • Implemented checksum-based filtering in the fileset search page
  • Included metadata information in seeding logs (unless --skiplog is passed)
Goals for Next Week
  • Add GitHub-based authentication
  • Implement a three-tier user system: admin, moderator, and read-only
  • Add validation checks on user data to prevent brute force attacks
  • Refactor the entire project into a Python module for better structure and cleaner imports

Working on ImGui is fun!

This week I worked on some improvements on the ImGui visual debugger in the Director engine created by @sev. It allows us to see the lingo scripts being processed, cast members and their properties, score and a bunch of other stuff.

There was an extremely minor problem with the scripts window. The script window allows us to put breakpoints into the movie. However, the check boxes for the breakpoints were all being rendered with the same ID. ImGui has an label+ID system to name UI elements. Each new UI element must have a unique label+ID combination. Which was a simple mistake, rectified immediately.

The second thing was in case there are multiple windows, i.e. there are (Director) windows present other than the stage, then the ImGui just clubs all of the scripts being processed into a single (ImGui) Scripts window. So, we need to have a separate pane for the script being processed in each (Director) window.

For this, my idea was to create a separate `ImGui::_state` for each (Director) window and store them in a hashmap. Each time `showExecutionContext()` is called, we take the `ImGui::_state` for each window, and render them.
_state = getWindowState(stage);
updateCurrentScript();

Although, I ran into a multiple problems:
1. The execution context (ImGui) window once turned on, should stay turned on, but it was disappearing as soon as the (Director) window changes.
2. The control panel was debugging the script for the wrong (Director) window.
3. The other (ImGui) windows were being turned off whenever the (Director) window changes.
4. The multiple windows were showing the same script because the LingoState wasn’t changed in the global lingo context g_lingo.
5. The other panes were not showing any scripts because the `getHandler()` function wasn’t able to find the lingo script with the correct cast ID.
Solved these one by one and created a pull request yesterday.

Also, the callstack is currently a separate (ImGui) window. We also need to combine that with the scripts into a new (ImGui) window named the Execution Context. I did the refactoring. The Execution Context now looks like follows:

There was also a minor font rendering issue in the Score (ImGui) window. Apparently there were multiple breaking changes in the 1.92.0 update for ImGui regarding Font changes. However, it didn’t take long to figure out that it was just a one line change. I just had to set the `ImGui::IO::DefaultFont`, which is NULL by default. Previously when called:
ImGui::PushFont(ImGui::GetIO().DefaultFont)
The PushFont function would see that DefaultFont is NULL, and automatically push the first font added to using ImGui::AddTTF****. However, after the update, if the parameter to ImGui::PushFont is NULL, the font is unaffected and only the font size is changed. Hence, I had to manually make sure that ImGui::IO::DefaultFont is not NULL.

Overall, I didn’t feel stuck at any point. The process of figuring this out was smooth. However, this still took me a week to finish because I took an off day in the middle of the week. Also, due to my house hunt (which was a pain and a half) I used to start late @10PM IST and get tired by 2AM IST and doze off! Although I had written about it in my proposal, that I’ll have less time once my college starts, this is less than I had anticipated. This makes me worry about my GSoC project’s success. This week will be better, now that I (hopefully) have a stable abode. I’ll start consistently @6PM IST and put in 6-7 hours before calling it a day.

Here’s to hoping for a better week (again)!

Week 9: SCUMM

Introduction

This week, I opened a PR for adding text-to-speech to SCUMM, which was an interesting engine to work on. I’m quite happy with the result, though with the wide variety of games supported by SCUMM, it may need more work in the future.


SCUMM

Most of my week was spent on adding TTS to SCUMM, an engine that was neither especially difficult nor especially easy to work on. On one hand, SCUMM games are very similar to most of the games I’ve worked on before. Most of them have few menus and rather simple means of displaying text, unlike engines like Efh or MM. Thus, creating a user-friendly TTS system was fairly easy for SCUMM. In addition, finding where text is displayed wasn’t difficult: most of it goes through printString and drawString, with actor speech being displayed with displayDialog. Furthermore, most of the GUI controls and buttons come with labels built into them, which makes voicing buttons when they’re hovered over a very simple process. As such, much of the work for SCUMM involved simply adding TTS voicing calls to these functions, while accounting for situations such as subtitles for voiced dialog and the need to delay the disappearance of text until TTS is finished.

On the other hand, however, SCUMM is larger than some of the engines I’ve worked with previously, and it supports many different games. This means that compatibility issues are a recurring problem, as what works for TTS in one version of the SCUMM engine may not work in others. A good example of this is verbMouseOver. When I first came across this method, I thought that it would be a great place to voice verbs when they’re hovered over. However, SCUMM version 5 – or at least, Indiana Jones and the Fate of Atlantis – doesn’t seem to reliably use this function for detection of hovering over verbs. In addition, while some games only call drawVerb once as a verb is hovered over, games like Fate of Atlantis call it every frame. Thus, to try to voice verbs as robustly as possible, I decided to add code to drawVerb, which most SCUMM versions seem to go through for verb drawing, to check whether the current verb is highlighted before voicing it, a strategy that seemed to work for many games. There were other compatibility issues that I had to resolve as well, such as SCUMM versions 0, 1, and 2 using custom text encodings that needed to be replicated; drawVerb sometimes being used to print strings that aren’t verbs, requiring them to be voiced even if they aren’t highlighted; and Passport to Adventure having a special help menu with buttons that aren’t considered GUI controls, which required a means of storing the text for each button and detecting when they’re hovered over.

Another concern was that SCUMM versions 7 and 8 use their own methods for displaying text, though they were fortunately similar enough to those used by earlier versions that it wasn’t too difficult to voice them. Humongous Entertainment games also seem to have different means of handling text, but because they don’t have much text in the first place, I didn’t have to worry as much about them. Thus, most of the compatibility issues were in earlier SCUMM versions, with later ones having fewer problems.

Ultimately, the most difficult component of SCUMM was the wide variety of games supported. Each version has its own ways of handling text that have to be considered, requiring careful thought about the best places to voice or stop text. I’m fairly happy with my implementation of TTS for this engine, but because of its many games, there may be some oddities that will need to be resolved.


Conclusion

I opened a PR for SCUMM this week, which was an interesting engine to explore, due to its greater size and variety of versions. I also revisited my MADE PR, an engine that had its own compatibility issues, with text indices varying across game versions, that should be solved now. Next week, I’ll be working on AGI, the last engine listed on my project.