{"id":80,"date":"2026-07-13T21:11:57","date_gmt":"2026-07-13T21:11:57","guid":{"rendered":"https:\/\/blogs.scummvm.org\/andy\/?p=80"},"modified":"2026-07-13T21:11:57","modified_gmt":"2026-07-13T21:11:57","slug":"week-7","status":"publish","type":"post","link":"https:\/\/blogs.scummvm.org\/andy\/2026\/07\/13\/week-7\/","title":{"rendered":"WEEK 7"},"content":{"rendered":"<p data-path-to-node=\"3\">Hello, everyone! With both the European and US Amiga releases of <i data-path-to-node=\"3\" data-index-in-node=\"48\">Kult \/ Chamber of the Sci-Mutant Priestess<\/i> playable from start to finish, this week I shifted focus from adding features to hardening what was already there. Last week I mentioned that ScummVM runs the engine through the Coverity and PVS-Studio static analysers, and that their reports had turned up a handful of issues worth looking at. This week I went through those reports properly, and on top of that I chased down a save\/load bug that had been quietly breaking a couple of rooms after loading a game.<\/p>\n<h2 data-path-to-node=\"4\">Working through the Coverity report<\/h2>\n<p data-path-to-node=\"5\">Static analysers are good at spotting the kind of code that is technically wrong but rarely bites in day-to-day play: unreachable branches, resources that are allocated but never freed, and fields that are read before they are written. Chamber is a reimplementation of a 1989 DOS game, so a lot of the code mirrors the original binary very closely \u2014 which means some of these findings are actually faithful reproductions of dead code that was already dead in the original.<\/p>\n<p data-path-to-node=\"6\">That distinction mattered while reviewing the reports, because I did not want to &#8220;fix&#8221; something that was deliberately mirroring the disassembly. So for each finding I went back to the original code to confirm what was really going on before touching anything.<\/p>\n<p data-path-to-node=\"7\">A few of the more interesting ones:<\/p>\n<ul data-path-to-node=\"8\">\n<li>\n<p data-path-to-node=\"8,0,0\">In <code data-path-to-node=\"8,0,0\" data-index-in-node=\"3\">CMD_21_VortTalk<\/code>, Coverity flagged a branch guarded by <code data-path-to-node=\"8,0,0\" data-index-in-node=\"57\">rand_value &gt;= 170<\/code> as unreachable. Checking the disassembly, the <code data-path-to-node=\"8,0,0\" data-index-in-node=\"121\">num == 7<\/code> case it belonged to is dead in the original too, so the branch could go.<\/p>\n<\/li>\n<li>\n<p data-path-to-node=\"8,1,0\">In <code data-path-to-node=\"8,1,0\" data-index-in-node=\"3\">drawRoomStatics<\/code>, there was a door check for <code data-path-to-node=\"8,1,0\" data-index-in-node=\"47\">index == 91<\/code>, but by the time that code runs the index is already constrained to the <code data-path-to-node=\"8,1,0\" data-index-in-node=\"131\">50..60<\/code> range, so the check can never be true.<\/p>\n<\/li>\n<li>\n<p data-path-to-node=\"8,2,0\">In the CGA blitter, <code data-path-to-node=\"8,2,0\" data-index-in-node=\"20\">blitToScreen<\/code> had an <code data-path-to-node=\"8,2,0\" data-index-in-node=\"40\">endY &gt; 200<\/code> clamp that could never fire, because <code data-path-to-node=\"8,2,0\" data-index-in-node=\"88\">dy<\/code> and <code data-path-to-node=\"8,2,0\" data-index-in-node=\"95\">h<\/code> are hardcoded to <code data-path-to-node=\"8,2,0\" data-index-in-node=\"114\">0<\/code> and <code data-path-to-node=\"8,2,0\" data-index-in-node=\"120\">200<\/code> just above it.<\/p>\n<\/li>\n<\/ul>\n<p data-path-to-node=\"9\">Alongside the dead code, the report caught two genuinely useful things: a memory leak and an uninitialised field. Several call sites were calling <code data-path-to-node=\"9\" data-index-in-node=\"146\">loadFond<\/code> \/ <code data-path-to-node=\"9\" data-index-in-node=\"157\">ega_loadFond<\/code> \/ <code data-path-to-node=\"9\" data-index-in-node=\"172\">loadSplash<\/code>, which return a <code data-path-to-node=\"9\" data-index-in-node=\"199\">Graphics::Surface<\/code>, and then throwing that surface away without freeing it \u2014 a small leak every time a room background or splash was loaded. And the engine&#8217;s <code data-path-to-node=\"9\" data-index-in-node=\"356\">_speaker<\/code> member was not being initialised in the constructor. Both are the sort of thing that is easy to miss by eye and exactly what these tools are for.<\/p>\n<h2 data-path-to-node=\"10\">Working through the PVS-Studio report<\/h2>\n<p data-path-to-node=\"11\">PVS-Studio leans towards a different class of finding \u2014 dead stores and redundant expressions \u2014 and it caught a similar mix:<\/p>\n<ul data-path-to-node=\"12\">\n<li>\n<p data-path-to-node=\"12,0,0\">Another impossible clamp in the CGA blitter, this time <code data-path-to-node=\"12,0,0\" data-index-in-node=\"55\">endX_bytes &gt; 80<\/code> on the horizontal axis, again dead because the width is hardcoded to a full <code data-path-to-node=\"12,0,0\" data-index-in-node=\"147\">320<\/code> pixels.<\/p>\n<\/li>\n<li>\n<p data-path-to-node=\"12,1,0\">A dead <code data-path-to-node=\"12,1,0\" data-index-in-node=\"7\">ofs++<\/code> in <code data-path-to-node=\"12,1,0\" data-index-in-node=\"16\">cga_ZoomInplace<\/code>, immediately followed by <code data-path-to-node=\"12,1,0\" data-index-in-node=\"57\">ofs<\/code> being reassigned to <code data-path-to-node=\"12,1,0\" data-index-in-node=\"81\">oofs<\/code>, so the increment never had any effect.<\/p>\n<\/li>\n<li>\n<p data-path-to-node=\"12,2,0\">In <code data-path-to-node=\"12,2,0\" data-index-in-node=\"3\">SCR_23_HidePortrait<\/code>, a left\/right button branch where both arms were identical \u2014 this handler simply has no special behaviour to skip, so the branch collapsed to a single path.<\/p>\n<\/li>\n<li>\n<p data-path-to-node=\"12,3,0\">In <code data-path-to-node=\"12,3,0\" data-index-in-node=\"3\">SCR_46_DeProfundisLowerHook<\/code>, the return value of <code data-path-to-node=\"12,3,0\" data-index-in-node=\"52\">getPuzzlSprite()<\/code> was being stored into <code data-path-to-node=\"12,3,0\" data-index-in-node=\"91\">sprofs<\/code> and then overwritten on the very next line, so the first store was pointless.<\/p>\n<\/li>\n<\/ul>\n<p data-path-to-node=\"13\">There were also a couple of intentional busy-wait loops that PVS-Studio flagged for having an empty <code data-path-to-node=\"13\" data-index-in-node=\"100\">;<\/code> body. These are genuinely meant to be empty \u2014 they are timing delays \u2014 so instead of silencing the warning I gave them an explicit <code data-path-to-node=\"13\" data-index-in-node=\"233\">{}<\/code> body to make the intent obvious to both the analyser and the next person reading the code.<\/p>\n<p data-path-to-node=\"14\">None of these are dramatic bugs, but clearing them keeps the engine&#8217;s future reports clean, so real regressions do not get lost in a wall of pre-existing noise.<\/p>\n<h2 data-path-to-node=\"15\">Fixing room state that vanished after loading a save<\/h2>\n<p data-path-to-node=\"16\">The most interesting problem this week was not a static-analysis finding at all \u2014 it was a save\/load bug I ran into while testing.<\/p>\n<p data-path-to-node=\"17\">Chamber builds each room from a base decor, but scripts frequently draw persistent changes on top of that: alternate decor sets, extra objects that appear once triggered, and the final frames of animations. The catch is that they draw these directly into the backbuffer. On a fresh visit that is fine, but when you saved and reloaded, the engine rebuilt the room from the base decor only \u2014 so anything a script had painted into the backbuffer was simply gone.<\/p>\n<p data-path-to-node=\"18\">In practice this meant that things like the De Profundis monster, or Deilos, would fail to reappear after loading a game, even though the game state said they should be there. The state was correct; the pixels that represented it had never been saved.<\/p>\n<p data-path-to-node=\"19\">The fix was to bump the save version to <code data-path-to-node=\"19\" data-index-in-node=\"40\">2<\/code> and store what was actually missing: the zone palette, the current video mode, and the backbuffer contents themselves. On load, if the video mode matches, the backbuffer is restored verbatim; otherwise the engine falls back to the old rebuild-from-decor behaviour, so older saves and cross-mode loads still work. While I was in there I also made load reapply the room palette and honour the launcher&#8217;s save-slot option, so booting straight into a save from the launcher lands you in a correctly coloured room.<\/p>\n<h2 data-path-to-node=\"3\">Looking ahead<\/h2>\n<p data-path-to-node=\"4\">That wraps up the cleanup pass I wanted to do before moving on. The engine is now noticeably tidier and more robust, which is a perfect state to leave it in. Starting next week, I will be shifting my focus to a completely different engine. I wonder if anyone can guess which one it will be?(Hint: It will be a great adventure!) Stay tuned, and thanks for reading.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hello, everyone! With both the European and US Amiga releases of Kult \/ Chamber of the Sci-Mutant Priestess playable from start to finish, this week I shifted focus from adding features to hardening what was already there. Last week I mentioned that ScummVM runs the engine through the Coverity and PVS-Studio static analysers, and that [&hellip;]<\/p>\n","protected":false},"author":31,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[],"class_list":["post-80","post","type-post","status-publish","format-standard","hentry","category-gsoc-2026"],"_links":{"self":[{"href":"https:\/\/blogs.scummvm.org\/andy\/wp-json\/wp\/v2\/posts\/80","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.scummvm.org\/andy\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.scummvm.org\/andy\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.scummvm.org\/andy\/wp-json\/wp\/v2\/users\/31"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.scummvm.org\/andy\/wp-json\/wp\/v2\/comments?post=80"}],"version-history":[{"count":1,"href":"https:\/\/blogs.scummvm.org\/andy\/wp-json\/wp\/v2\/posts\/80\/revisions"}],"predecessor-version":[{"id":81,"href":"https:\/\/blogs.scummvm.org\/andy\/wp-json\/wp\/v2\/posts\/80\/revisions\/81"}],"wp:attachment":[{"href":"https:\/\/blogs.scummvm.org\/andy\/wp-json\/wp\/v2\/media?parent=80"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.scummvm.org\/andy\/wp-json\/wp\/v2\/categories?post=80"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.scummvm.org\/andy\/wp-json\/wp\/v2\/tags?post=80"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}