Categories
Week 4

Week 4: Sword25, TeenAgent and NGI

This week, I focused on bringing keymapper support to three engines: Sword25, Teenagent, and NGI. Each had its own quirks, but none were too complex—so I was able to make steady progress across the board.


Sword25

The Sword25 engine uses script-driven input handling. That made things straightforward—no refactoring needed.

My job here was mostly investigative: figure out what each key does and document it for the keymapper. Since input behavior was defined in scripts, I didn’t need to touch much engine-level code. The entire process was smooth and quick.


Teenagent

Teenagent was also fairly simple. It involved defining a few custom actions for specific keys and setting up mouse input. The engine doesn’t have complex input modes or conditionally processed input, so once I identified the key behaviors, wiring up the keymapper didn’t take long.


NGI

The NGI engine was the most challenging of the three this week. While it didn’t introduce any new types of problems, it did have more keys and more complexity around where input is processed.

One big issue: keys pressed during menus, modals, or intros weren’t processed through the same input flow as in-game actions. That meant keymapper actions would either:

  • Be ignored in certain contexts, or worse…

  • Be interpreted in places they shouldn’t be

To fix this, I had to carefully trace the engine’s input handling paths and add logic to enable or disable the keymapper at the right times. Most of my time on NGI was spent understanding those flows and placing the toggles precisely.

Once that was in place, implementing custom actions and mapping key behaviors was straightforward.


Wrap-Up

This week, I:

  • Implemented keymapper support for Sword25, Teenagent, and NGI
  • Got the NGI keymapper PR merged
  • Got the keymapper action label normalization PR merged

Next week, I’ll continue this momentum and explore new engines.

Categories
Week 3

Week 3: Titanic, Tetraedge, and a Sea of Strings

This week was quite busy and eventful! I had my keymapper implementations for the Supernova and Voyeur engines successfully merged, and I also completed keymapper support for Titanic and Tetraedge. In addition, I took on an extra task to help normalize all keymapper action descriptions across the project.


Titanic: The Most Challenging Engine So Far

Out of all the engines I’ve worked on so far, Titanic has definitely been the most complex.

Implementing the keymapper took me about four days, most of which were spent trying to understand how input was being handled. Unlike other engines, Titanic processes input through a system of “messages.” Key presses are encapsulated in these messages and passed around to various components that need to handle input. This indirect approach made the input flow harder to trace.

Even though I’m still not 100% confident I fully understand every detail of the mechanism, I eventually got the keymapper working by mimicking how raw keycodes were originally processed. After two days of reading and experimenting, things finally started to click — and from that point on, it was just a matter of replacing keycodes with action-based mappings and writing the action bindings.


Tetraedge: The Easiest One Yet

In contrast to Titanic, Tetraedge was a breeze. The engine only uses three keys and mouse input, so I was able to wrap up the keymapper integration in half a day. Always nice to have a quick win!


Action Label Normalization: A Multilingual Cleanup

ScummVM’s GUI is actively translated into many languages by the community using Weblate. Weblate automatically picks up strings that are explicitly marked for translation in the code.

To reduce the translators’ workload, it’s important that identical strings are reused consistently across the codebase. Weblate treats strings with even minor differences — like different capitalization — as separate entries. Unfortunately, over the years, different contributors have used varying styles (sentence case, title case, etc.) for keymapper action descriptions.

To help streamline translations, I went through and normalized 1100+ action labels to follow sentence capitalization. The task itself wasn’t difficult, but it was quite repetitive. Fortunately, sev helped by providing a git grep and awk-based workflow that made identifying and editing the labels much faster. With that in place, I was able to get through the normalization process much more efficiently.


Wrapping Up

This week, I:

  • Got my Supernova and Voyeur keymapper PRs merged 🎉

  • Implemented full keymapper support for Titanic (the hardest one so far)

  • Added keymapper support to Tetraedge (the easiest one yet)

  • Normalized 1100+ keymapper action labels for consistent translations across the GUI

Next week, I plan to keep the momentum going and tackle keymapper support for more engines. With each engine, I’m getting a better understanding of the diverse input systems in ScummVM—and figuring out how to adapt the keymapper to each one’s quirks.

Categories
Week 2

Week 2: Supernova & Voyeur

This week, I continued working on implementing keymapper for more game engines. The highlight was getting my SLUDGE keymapper PR merged, and diving into two new engines: Supernova and Voyeur.


Supernova Engine: The Challenge of Custom Actions

The Supernova engine powers the Mission Supernova series—old-school point-and-click adventures that are surprisingly engaging (and tough!). To test my changes, I had to play through parts of the game, and I genuinely enjoyed the puzzles and storytelling, despite the lack of modern visuals. Honestly, without the walkthrough, I would’ve spent more time solving puzzles than coding the keymapper.

Unlike SLUDGE, this engine uses engine-driven input handling, which gave me the flexibility to define custom actions instead of just simulating keypresses. Why is this better? Because:

Custom actions give the engine full control over what happens when a key is pressed, while mimicking keypresses can cause issues like duplicate handling or unexpected default behavior still triggering.

The biggest challenge I faced in Supernova was how the engine handled keyboard input. It stored the raw keycode of every key press in a single variable called _key, which was then used for both gameplay actions and full-text input.

This became a problem when introducing custom actions. Unlike raw keycodes, custom actions are more abstract—they represent “what to do” rather than “what key was pressed.” But if I allowed both to be handled the same way, the engine might mistakenly store a custom action as if it were a real keypress, which would break things like text input.

The Solution

I separated the handling of keycodes and actions using two variables:

  • _key → stores the raw keycode (used only when text input is needed)

  • _action → stores the custom action (used during normal gameplay)

Then, I made sure the engine would:

  • Disable the keymapper when full keyboard input was required (e.g., entering text). This way, real keypresses would go through, and _key would be filled correctly.

  • Enable the keymapper during gameplay. In this mode, keypresses would be intercepted, converted into actions, and stored in _action.

This setup allowed me to cleanly separate when to use keycodes and when to use actions, without breaking any existing input logic. Once this system was in place, replacing the original key handling with action-based logic was pretty straightforward. All I had to do was replace hardcoded keycodes with custom actions.


Voyeur Engine: Simple, Then Smarter

Compared to Supernova, Voyeur was a breeze. The game is almost entirely mouse-driven, with very little keyboard interaction. I initially thought I could get away with two simple binds—left and right click—and be done in a few hours.

But… why stop there?

I decided to improve it by breaking the keymap into multiple context-specific keymaps, depending on where the player is in the game. For example, different menus or in-game sequences would have their own set of keybinds with clearer action labels.

The tricky part was figuring out where in the code to enable/disable each keymap. That took a whole day of reading through the engine, but once I nailed it, the final implementation was clean and intuitive.


Wrap-Up

This week, I:

  • Got my SLUDGE keymapper PR merged 🎉

  • Implemented full keymapper support for Supernova (with custom actions)

  • Added smarter, context-aware keymapping to Voyeur

Next week, I plan to continue this momentum and bring keymapper support to more engines. The work is getting smoother now that I’m more familiar with ScummVM’s input systems—and every engine brings a new twist to solve.

Categories
Week 1

Week 1: Diving into SLUDGE Engine

This week marked the beginning of my journey with ScummVM for Google Summer of Code, and I kicked things off by working on the SLUDGE engine.

For those unfamiliar, SLUDGE is an engine that enables developers to create and publish their own point-and-click adventure games. These games are highly script-driven, especially in how they handle input. Keyboard input, in particular, is passed directly to the script, and from there, it’s entirely up to the game developers to define how those inputs are interpreted and used in their game logic.

My main goal for the week was to implement ScummVM’s keymapper support in the SLUDGE engine. However, this came with its own set of challenges.

Each SLUDGE-based game defines its own key bindings for different actions. That meant I had to manually identify the default controls for over 10 supported games. This process was quite time-consuming, as there was no centralized documentation. In some cases, I found the information in the game’s source code, in others through manuals, and occasionally I had to just play through the game and note what each key did.

While this part of the task was tedious, it was relatively straightforward. The real challenge came when I had to figure out when to disable the keymapper.

Why? Because some games allow players to enter custom save file names, which means they need unrestricted keyboard input. If the keymapper remained active during such input modes, it would interfere with typing. Unlike many other engines, SLUDGE doesn’t offer a built-in pause menu or modal state, so each developer handled menus differently.

After some digging through the engine source and various game scripts, I discovered two important functions: `freeze()` and `unfreeze()`. These were used every time a menu was shown or hidden. While not all menus required full keyboard input (most only needed mouse interaction), this discovery gave me a reliable enough hook. I could safely disable the keymapper when a menu was active and re-enable it when gameplay resumed.

With that, I successfully implemented keymapper support for the SLUDGE engine in ScummVM!

Categories
Week 0

Introduction

Hi everyone! I’m Prime, a third-year computer science student and lifelong gamer. This summer, I’ll be working with ScummVM as part of Google Summer of Code 2025.

My project, “Add Keymapper to More Games,” focuses on integrating ScummVM’s keymapper system into more of its supported game engines. This will allow players to customize controls across a wide range of classic games — making gameplay smoother and more accessible on different input devices like keyboards and gamepads.

Over the summer, I plan to add keymapper support to over 20 engines. For each engine, I’ll analyze its input system, replace hardcoded key handling with keymapper logic, and thoroughly test the changes to ensure everything works as expected.

A huge thank you to my mentors, the ScummVM community, and the GSoC organizers. I’m looking forward to contributing to the preservation and improvement of these classic games!