{"id":47,"date":"2021-07-19T21:15:24","date_gmt":"2021-07-19T21:15:24","guid":{"rendered":"https:\/\/blogs.scummvm.org\/ayyg\/?p=47"},"modified":"2022-05-26T15:20:56","modified_gmt":"2022-05-26T06:20:56","slug":"the-beginning-of-the-end-progress-report","status":"publish","type":"post","link":"https:\/\/blogs.scummvm.org\/ayyg\/2021\/07\/19\/the-beginning-of-the-end-progress-report\/","title":{"rendered":"The Beginning of the End (Progress Report)"},"content":{"rendered":"\r\n<p>Although this week had relatively weak progress due to me getting a vaccine shot, we still progressed in various fronts.<\/p>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Saves<\/h2>\r\n\r\n\r\n\r\n<p>Let&#8217;s pick up from where we left off last time. Work on implementing the rest of the save system continued. We decided to try something new to count the number of bytes of each chunk &#8211; but first, a little refresher.<\/p>\r\n\r\n\r\n\r\n<div class=\"wp-block-image\">\r\n<figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"540\" height=\"88\" class=\"wp-image-48\" src=\"https:\/\/blogs.scummvm.org\/ayyg\/wp-content\/uploads\/sites\/7\/2021\/07\/image-1.png\" alt=\"\" srcset=\"https:\/\/blogs.scummvm.org\/ayyg\/wp-content\/uploads\/sites\/7\/2021\/07\/image-1.png 540w, https:\/\/blogs.scummvm.org\/ayyg\/wp-content\/uploads\/sites\/7\/2021\/07\/image-1-300x49.png 300w\" sizes=\"auto, (max-width: 540px) 100vw, 540px\" \/>\r\n<figcaption>The basic structure of a SAGA2 save chunk.<\/figcaption>\r\n<\/figure>\r\n<\/div>\r\n\r\n\r\n\r\n<p>Each chunk starts with an 8-byte header: the 4-byte chunk name, followed by a uint32 determining the size of the chunk data. Originally the saving program determined the necessary size through a series of sizeof() operations. However, due to alignment issues and platform differences this approach is not portable. I mentioned changing everything to constants, but that in and of itself is a dangerous process, because all of the bytes would have to be counted mostly by hand, and a mistake would be fatally easy to make.<\/p>\r\n\r\n\r\n\r\n<p>Instead, what sev proposed to do was to to write the chunk data to an intermediate buffer and stream that to the <code>OutSaveFile<\/code> along with the chunk size. This way nothing has to be counted manually.<\/p>\r\n\r\n\r\n\r\n<p>In order to achieve that, we used <code><a href=\"https:\/\/doxygen.scummvm.org\/d2\/df9\/class_common_1_1_memory_write_stream_dynamic.html\" data-type=\"URL\" data-id=\"https:\/\/doxygen.scummvm.org\/d2\/df9\/class_common_1_1_memory_write_stream_dynamic.html\">Common::MemoryWriteStreamDynamic<\/a><\/code> and defined some macros:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>#define CHUNK_BEGIN Common::MemoryWriteStreamDynamic *out = new Common::MemoryWriteStreamDynamic(DisposeAfterUse::YES)\r\n\r\n#define CHUNK_END outS-&gt;writeUint32LE(out-&gt;size()); \\\r\n\toutS-&gt;write(out-&gt;getData(), out-&gt;size()); \\\r\n\tdelete out<\/code><\/pre>\r\n\r\n\r\n\r\n<p>So when saving, we can simply do the following:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>void saveGlobals(Common::OutSaveFile *outS) {\r\n\toutS-&gt;write(\"GLOB\", 4);\r\n\tCHUNK_BEGIN;\r\n\tout-&gt;writeUint32LE(objectIndex);\r\n\tout-&gt;writeUint32LE(actorIndex);\r\n\tout-&gt;writeUint16LE(brotherBandingEnabled);\r\n\tout-&gt;writeUint16LE(centerActorIndicatorEnabled);\r\n\tout-&gt;writeUint16LE(interruptableMotionsPaused);\r\n\tout-&gt;writeUint16LE(objectStatesPaused);\r\n\tout-&gt;writeUint16LE(actorStatesPaused);\r\n\tout-&gt;writeUint16LE(actorTasksPaused);\r\n\tout-&gt;writeUint16LE(combatBehaviorEnabled);\r\n\tout-&gt;writeUint16LE(backgroundSimulationPaused);\r\n\tCHUNK_END;\r\n}<\/code><\/pre>\r\n\r\n\r\n\r\n<p>In this example the macro doesn&#8217;t seem to do much good since the number of bytes here is easily countable, but it is very helpful in bigger saving steps, and it is also less fallible than a human counting.<\/p>\r\n\r\n\r\n\r\n<p>Another development is that I learned booleans are saved as Uint16 in the original &#8211; go figure. After fixing that our saves ran on the original and vice-versa, hooray!<sup>1<\/sup><\/p>\r\n\r\n\r\n\r\n<p>And then finally after we finished work on getting the save\/loading done, we implemented support for ScummVM&#8217;s extended savefile format, with the ability to see playtime and screenshots from the launcher. The process consisted of implementing <code>Saga2Engine::saveGameState<\/code> and using <code>appendExtendedSave<\/code> to write the extended save data<sup>2<\/sup> (and then reading it back with <code>readSavegameHeader<\/code>).<\/p>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Global Constructors<\/h2>\r\n\r\n\r\n\r\n<p>This week we also finished the rest of the global constructors. Not much to report, other than that some global constructors are quite sneaky.<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>static const StaticTilePoint NullTile = {(int16)minint16, (int16)minint16, (int16)minint16};\r\n\r\nstatic auxAudioTheme aats[AUXTHEMES] = {\r\n\t{false, {NullTile, 0}, 0},\r\n\t{false, {NullTile, 0}, 0}\r\n};<\/code><\/pre>\r\n\r\n\r\n\r\n<p>The above doesn&#8217;t generate errors when <code>NullTile<\/code> is defined within the same compilation unit, but it gives out a warning if it&#8217;s defined somewhere else. Go figure.<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>StaticWindow autoMapDecorations[numAutoMapPanels] = {\r\n\t{*(autoMapPanelRects[0]), NULL, autoMapTopPanelResID},\r\n\t{*(autoMapPanelRects[1]), NULL, autoMapMidPanelResID},\r\n\t{*(autoMapPanelRects[2]), NULL, autoMapBotPanelResID}\r\n};<\/code><\/pre>\r\n\r\n\r\n\r\n<p>Dereferencing here also gives out a global constructor warning. I thought it was some kind of copy constructor thing going on, but I have no idea. To fix that I simply got rid of <code>autoMapPanelRects<\/code> and used the rects directly:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>StaticWindow autoMapDecorations[numAutoMapPanels] = {\r\n\t{autoMapTopPanelRect, NULL, autoMapTopPanelResID},\r\n\t{autoMapMidPanelRect, NULL, autoMapMidPanelResID},\r\n\t{autoMapBotPanelRect, NULL, autoMapBotPanelResID}\r\n};<\/code><\/pre>\r\n\r\n\r\n\r\n<p>To fix the other constructors I did the same steps as previously: either define it within Saga2Engine or relevant classes or transform it into a pointer and initialize it somewhere inside the file if it is static (or both).<\/p>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\">Debug Console<\/h2>\r\n\r\n\r\n\r\n<p>This is an interesting one. ScummVM has support for a debug console, <a href=\"https:\/\/wiki.scummvm.org\/index.php\/Debugging_ScummVM\">if you didn&#8217;t know<\/a>. An example is implemented within Quux Engine, but I based myself off of SAGA Engine.<\/p>\r\n\r\n\r\n\r\n<p>To implement it, you create a new file <code>console.h<\/code> and there create a class that inherits from <code>GUI::Debugger<\/code>:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>class Console : public GUI::Debugger {\r\npublic:\r\n\tConsole(Saga2Engine *vm);\r\n\t~Console() override;\r\n\r\nprivate:\r\n\tSaga2Engine *_vm;\r\n\r\n\tbool cmdKillProtag(int argc, const char **argv);\r\n...\r\n\tbool cmdGotoPlace(int argc, const char **argv);\r\n};\r\n<\/code><\/pre>\r\n\r\n\r\n\r\n<p>And then you initialize it within <code>Engine::run()<\/code>:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>_console = new Console(this);\r\nsetDebugger(_console);<\/code><\/pre>\r\n\r\n\r\n\r\n<p>And then within <code>console.cpp<\/code> you implement the debug console commands:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-code\"><code>Console::Console(Saga2Engine *vm) : GUI::Debugger() {\r\n\t_vm = vm;\r\n\r\n\tregisterCmd(\"kill_protag\", WRAP_METHOD(Console, cmdKillProtag));\r\n...\r\n\tregisterCmd(\"goto_place\", WRAP_METHOD(Console, cmdGotoPlace));\r\n}\r\n\r\nConsole::~Console() {\r\n}\r\n\r\nbool Console::cmdKillProtag(int argc, const char **argv) {\r\n\tdebugPrintf(\"Killing protagonist\\n\");\r\n\r\n\tActor *protag = (Actor *)GameObject::objectAddress(ActorBaseID);\r\n\tprotag-&gt;getStats()-&gt;vitality = 0;\r\n\r\n\treturn true;\r\n}\r\n...<\/code><\/pre>\r\n\r\n\r\n\r\n<p>It is really quite the interesting tool to use and develop commands for. Not only that, we plan on using it to playtest and determine easily if the game is completable. <em>We&#8217;re developing. Ain&#8217;t got time for playing here!<\/em><\/p>\r\n\r\n\r\n\r\n<div class=\"wp-block-image\">\r\n<figure class=\"aligncenter\">\r\n<figure id=\"attachment_122\" aria-describedby=\"caption-attachment-122\" style=\"width: 573px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/blogs.scummvm.org\/ayyg\/wp-content\/uploads\/sites\/7\/2021\/07\/unknown.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-122\" src=\"https:\/\/blogs.scummvm.org\/ayyg\/wp-content\/uploads\/sites\/7\/2021\/07\/unknown.png\" alt=\"\" width=\"573\" height=\"430\" srcset=\"https:\/\/blogs.scummvm.org\/ayyg\/wp-content\/uploads\/sites\/7\/2021\/07\/unknown.png 573w, https:\/\/blogs.scummvm.org\/ayyg\/wp-content\/uploads\/sites\/7\/2021\/07\/unknown-300x225.png 300w, https:\/\/blogs.scummvm.org\/ayyg\/wp-content\/uploads\/sites\/7\/2021\/07\/unknown-480x360.png 480w\" sizes=\"auto, (max-width: 573px) 100vw, 573px\" \/><\/a><figcaption id=\"caption-attachment-122\" class=\"wp-caption-text\">Command usefulness factor? It is easily verifiable.<\/figcaption><\/figure>\r\n<\/figure>\r\n<\/div>\r\n\r\n\r\n<hr class=\"wp-block-separator\" \/>\r\n\r\n\r\n<p>[1] Remember me talking about enums? Weird thing is I didn&#8217;t have to change their size in order for it to work. Oh well.<\/p>\r\n\r\n\r\n\r\n<p>[2] We actually had to change <code>appendExtendedSave<\/code> and add the method <code>appendExtendedSaveToStream<\/code> in order for it to work with WriteStreams.<\/p>\r\n","protected":false},"excerpt":{"rendered":"<p>Although this week had relatively weak progress due to me getting a vaccine shot, we still progressed in various fronts. Saves Let&#8217;s pick up from where we left off last time. Work on implementing the rest of the save system continued. We decided to try something new to count the number of bytes of each [&hellip;]<\/p>\n","protected":false},"author":7,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-47","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/blogs.scummvm.org\/ayyg\/wp-json\/wp\/v2\/posts\/47","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.scummvm.org\/ayyg\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.scummvm.org\/ayyg\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.scummvm.org\/ayyg\/wp-json\/wp\/v2\/users\/7"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.scummvm.org\/ayyg\/wp-json\/wp\/v2\/comments?post=47"}],"version-history":[{"count":4,"href":"https:\/\/blogs.scummvm.org\/ayyg\/wp-json\/wp\/v2\/posts\/47\/revisions"}],"predecessor-version":[{"id":123,"href":"https:\/\/blogs.scummvm.org\/ayyg\/wp-json\/wp\/v2\/posts\/47\/revisions\/123"}],"wp:attachment":[{"href":"https:\/\/blogs.scummvm.org\/ayyg\/wp-json\/wp\/v2\/media?parent=47"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.scummvm.org\/ayyg\/wp-json\/wp\/v2\/categories?post=47"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.scummvm.org\/ayyg\/wp-json\/wp\/v2\/tags?post=47"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}