Hi, this is week 3 of my GSoC journey. After spending the last two weeks chasing crashes across the DM engine, this week turned into something more like detective work — digging into array indices, stale pointers, and byte alignment bugs that were quietly corrupting things under the hood.
This week, I faced freezes along with crashes. Some of these bugs only revealed themselves when stress-testing specific actions, like throwing explosives at a creature group, which would lock up the game entirely instead of just throwing an error.
Stride and Seek
Flying items rendering with garbled pixels when shrunk on screen. The cause was in blitToBitmapShrinkWithPalChange, which computed the destination row stride from destPixelWidth, the raw pixel width. But allocated bitmap rows are actually 8-byte aligned via getNormalizedByteWidth, so the real stride is wider than that. Using the raw width meant every row started at the wrong offset, bleeding into the next and corrupting pixel data.
Fix: compute destStride = getNormalizedByteWidth(destPixelWidth / 2) * 2 and use it instead of destPixelWidth when indexing destLine = &destBitmap[destY * destStride]
Before:
After:
The Defense Drift
Next was a crash in processEventEnableChampionAction, but the more dangerous part of this bug wasn’t the crash. _actionDefense is a lookup table indexed by action type, via _actionIndex, but the code was indexing it with curChampion->_actionDefense instead, the champion’s current defense value. That’s an arbitrary runtime number, not a valid index, so when it grew large enough to exceed the table size, the read went out of bounds and crashed outright. That part was at least loud and easy to notice.
The real problem showed up when the stat stayed within bounds. The lookup would quietly succeed, but against the wrong table entry, subtracting an incorrect defense bonus every time a combat action ended. There was no crash, no error, nothing to flag it. Just a small wrong number applied again and again, every fight, every action. Over enough time, that drift compounded into a champion whose defense stat had wandered far from what it should be, sometimes ending up nearly immortal, sometimes alarmingly fragile, with nothing in the logs to explain why.
Fix: a one-character typo fix, replacing _actionDefense[curChampion->_actionDefense] with _actionDefense[curChampion->_actionIndex].
The Gimme Slot Bug
Next one came from the gimme debug command. Cmd_gimme loops over every slot for a given thing type, but unused slots have their first word set to thingNone and the loop wasn’t checking for that before passing them to getIconIndex, which calls into getWeaponInfo or getObjectType. Those ended up reading garbage from empty slots, sometimes crashing.
Fix: read the raw first word of each slot, and skip it with continue if it equals thingNone.
The Duplicate Item Bug
Another gimme bug. When it allocated a new item slot by copying thing data and bumping _thingCounts, it forgot to update dummyThing’s index to point at the new slot, still handing over the original thingIndex instead of thingCount. Two references ended up pointing at the same thing slot, causing duplicate item corruption and crashes whenever one reference was modified or deleted.
Fix: one line, dummyThing.setIndex(thingCount), after the allocation.
The Object Aspect Crash
Next one was inside drawObjectsCreaturesProjectilesExplosions, the function responsible for rendering objects, creatures, and projectiles in the dungeon view. It chained getObjectInfoIndex straight into _objectInfos[...]._objectAspectIndex and then into _objectAspects209[...], all in one expression, with no validation along the way. But getObjectInfoIndex can return an out-of-range value for invalid or corrupt things, and _objectAspectIndex itself can exceed k85_ObjAspectCount, causing crashes.
Fix: guard both indices before use, skipping the object with continue if either is out of range.
An Enum That Wasn’t One
Next one was about ActiveGroup::_directions, typed as Direction, an enum. But the field doesn’t actually store a single direction, it stores packed 2-bit direction values for up to 4 creatures combined together, built via getGroupValueUpdatedWithCreatureValue. That’s a bit-packed integer, not a valid enum value, and casting a packed integer to an enum type is undefined behavior in C++.
Fix: change the field type from Direction to uint16, and remove the (Direction) casts at every assignment site, letting the packed value be stored and manipulated as a plain integer like it should’ve been.
The Freeze That Wouldn’t Crash
This one ate up most of my week, and it didn’t go down easy. The game would freeze, not crash, whenever explosives were thrown at certain creature groups. No ASan output to point at anything, since nothing was actually invalid memory. GDB’s backtrace wasn’t any more helpful either, it didn’t even surface the method actually responsible.
So it came down to reasoning it out manually. The freeze only ever happened when throwing explosives at a group with loot to drop, which meant something to do with damaging groups and handing out their possessions. That pointed toward GroupMan, so I started going through its methods one by one, comparing them against the original reversed code to spot where our implementation had drifted.
That led to dropCreatureFixedPossessions, which iterates a fixedPossessions pointer array to hand out loot. Two early-exit continue paths, one for skipping a random drop, one for when no unused thing slot was available, both forgot to advance the pointer before looping back. Without that advance, the loop kept re-reading the exact same array entry forever, spinning in place instead of crashing.
Fix: advance currFixedPossession = *fixedPossessions++ in both continue branches before skipping, so the loop always makes forward progress.
The Projectile Crash
Next one started simple, throw a sword or any projectile, and the game crashed. No obvious cause at first. Going through the methods from the GDB backtrace one by one eventually led to initializeGraphicData, where the loop over 6 projectile scale levels was writing all 6 byte-counts to the same fixed offsets, derivedBitmapIndex, derivedBitmapIndex+6, derivedBitmapIndex+12, every iteration, missing + projectileScaleIndex. Only the last iteration’s values stuck, leaving every earlier entry sized incorrectly.
Fix: add + projectileScaleIndex to all three cache offsets, so each scale level writes to its own correct slot.
New Addition
Added a nuke debugger command. Killing monsters manually just to clear a path for testing wasted time, so nuke finds the group directly in front of the party via mapCoordsAfterRelMovement and groupGetThing, then removes it instantly with groupDelete. No gameplay impact.
Looking Ahead, Faster
I’m going to push to finish the DM engine faster going forward, since there are more engines waiting in line after this one. I’ll do my best to wrap it up as quickly as I can, though I’ll admit, it’s turned out to be far more complex and broken than I initially expected.
Thank you for reading. See You in the Next One 🙂

See the cursor at the right edge of the window? That is the exit. But when I clicked on it, it hit 


So naturally, just like with the main game screen, we let the menu to build its 8 bit layout but merge it before rendering.

Currently, as you can see, there’s a transparency issue in the icon that I am trying to solve at this moment. Coming back to the inventory, the first item inside the inventory is joey’s circuit board just as it is in BASS. Same goes for the bar which is used to open the ‘FIRE EXIT’.
You can see the inventory items in the images above.
Thanks for reading 🙂




What’s Next
