<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Posts on felsqualle.com</title>
    <link>https://felsqualle.com/posts/</link>
    <description>Recent content in Posts on felsqualle.com</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Tue, 16 Dec 2025 22:30:00 +0100</lastBuildDate><atom:link href="https://felsqualle.com/posts/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Saving the Masters of the Elements From Getting Lost to Time: Epilogue.</title>
      <link>https://felsqualle.com/posts/2025/10/saving-the-masters-of-the-elements-epilogue/</link>
      <pubDate>Sun, 19 Oct 2025 22:30:00 +0200</pubDate>
      
      <guid>https://felsqualle.com/posts/2025/10/saving-the-masters-of-the-elements-epilogue/</guid>
      <description>&lt;p&gt;Every journey comes to an end, and this is the end of a very personal journey that started &lt;a href=&#34;https://felsqualle.com/posts/2025/05/saving-the-masters-of-the-elements-part-1/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;back in May 2025&lt;/a&gt;
. A lot has happened since then: I &lt;a href=&#34;https://felsqualle.com/posts/2025/07/saving-the-masters-of-the-elements-part-2/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;uncovered&lt;/a&gt;
 what breaks the game, &lt;a href=&#34;https://felsqualle.com/posts/2025/07/saving-the-masters-of-the-elements-part-3/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;how to fix it&lt;/a&gt;
, bought all available versions of the game I could find (including some quite expensive shipping from Japan to Germany), did some &lt;a href=&#34;https://felsqualle.com/posts/2025/08/saving-the-masters-of-the-elements-part-4/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;last-minute fixes&lt;/a&gt;
, and &lt;a href=&#34;https://felsqualle.com/posts/2025/10/saving-the-masters-of-the-elements-part-5/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;discovered some secrets&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;Thanks to the help of my incredibly talented friends from the ScummVM project, and thanks to everyone following me on this adventure over the last couple of months, I finally proudly present: The patch files required to play &amp;ldquo;Masters of the Elements&amp;rdquo; on Windows 2000 and higher, up to Windows 11.&lt;/p&gt;
&lt;p&gt;Enjoy!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: All downloads listed below only contain the changes necessary to fix the compatibility issues. An origial copy is required to play the game. Without the original game, these files are worthless.&lt;/em&gt;&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Edition&lt;/th&gt;
          &lt;th&gt;Patch&lt;/th&gt;
          &lt;th&gt;Data directory&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;Masters of the Elements&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;melements-win-en_patch.zip&#34;&gt;melements-win-en_patch.zip&lt;/a&gt;
&lt;/td&gt;
          &lt;td&gt;MVM&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Masters of Elements: Unmei no Shihaisha&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;melements-win-jp_patch.zip&#34;&gt;melements-win-jp_patch.zip&lt;/a&gt;
&lt;/td&gt;
          &lt;td&gt;(none)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Le Maître des Éléments&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;melements-win-fr_patch.zip&#34;&gt;melements-win-fr_patch.zip&lt;/a&gt;
&lt;/td&gt;
          &lt;td&gt;MdE&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Meister Zufall und die Herrscher der Elemente&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;melements-win-de_patch.zip&#34;&gt;melements-win-de_patch.zip&lt;/a&gt;
&lt;/td&gt;
          &lt;td&gt;MVM&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;I Signori degli Elementi&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;melements-win-it_patch.zip&#34;&gt;melements-win-it_patch.zip&lt;/a&gt;
&lt;/td&gt;
          &lt;td&gt;MVM&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Meesters van Macht&lt;/td&gt;
          &lt;td&gt;&lt;a href=&#34;melements-win-nl_patch.zip&#34;&gt;melements-win-nl_patch.zip&lt;/a&gt;
&lt;/td&gt;
          &lt;td&gt;MvM&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;installation&#34;&gt;Installation&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Download the patch set that matches your version.&lt;/li&gt;
&lt;li&gt;Copy the contents of the data directory from your disc to your hard drive.&lt;/li&gt;
&lt;li&gt;Copy the contents of the &lt;code&gt;Xtras&lt;/code&gt; directory from the patch files in your new game directory.&lt;/li&gt;
&lt;li&gt;Copy the file &lt;code&gt;scripts.cst&lt;/code&gt; to &lt;code&gt;data/global/&lt;/code&gt;, and delete the &lt;code&gt;scripts.cxt&lt;/code&gt; in the same directory.&lt;/li&gt;
&lt;li&gt;Copy the file &lt;code&gt;intro.dir&lt;/code&gt; to &lt;code&gt;data/intro/&lt;/code&gt;, and delete the file &lt;code&gt;intro.dxr&lt;/code&gt; in the same directory.&lt;/li&gt;
&lt;li&gt;Start the game with the executable (&lt;code&gt;MVM.EXE&lt;/code&gt; or &lt;code&gt;MDE.EXE&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Have fun!&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;readme--faq&#34;&gt;README &amp;amp; FAQ&lt;/h2&gt;
&lt;h3 id=&#34;i-cant-find-the-data-directory-on-my-disc&#34;&gt;I can&amp;rsquo;t find the data directory on my disc&lt;/h3&gt;
&lt;p&gt;On most of the releases, the data directory is hidden by default. Enable the &amp;ldquo;Show hidden items&amp;rdquo; option in Explorer to reveal it.&lt;/p&gt;
&lt;h3 id=&#34;when-copying-the-data-directory-to-my-hard-drive-my-pc-locks-up-on-the-folders-zdat-zydat-or-zzdat&#34;&gt;When copying the data directory to my hard drive, my PC locks up on the folders ZDAT, ZYDAT, or ZZDAT&lt;/h3&gt;
&lt;p&gt;Some releases of the English and German versions of the game use a copy protection called &amp;ldquo;copy-X&amp;rdquo;. This copy protection uses a &amp;ldquo;blank&amp;rdquo; area on the disc that contains absolutely no information. In the data directory, you&amp;rsquo;ll find a (hidden) folder called &lt;code&gt;ZDAT&lt;/code&gt;, &lt;code&gt;ZYDAT&lt;/code&gt;, or &lt;code&gt;ZZDAT&lt;/code&gt; containing about 100 MB of data. And here&amp;rsquo;s the trick: The data is not there, but the files are &amp;ldquo;mapped&amp;rdquo; to that blank area, causing your CD reader to hang. To check if your disc is affected, visually inspect the data side of the disc: If there&amp;rsquo;s a visible ring (about 1-2mm thick) at the outer area of the disc, then this disc is protected.&lt;/p&gt;
&lt;p&gt;To mitigate this, open the data directory and copy everything &lt;em&gt;except&lt;/em&gt; the dummy folders: The game never checks these files.&lt;/p&gt;
&lt;h3 id=&#34;the-game-runs-in-a-tiny-window-with-a-very-large-black-border-what-can-i-do&#34;&gt;The game runs in a tiny window with a very large black border. What can I do?&lt;/h3&gt;
&lt;p&gt;When you start the game, you&amp;rsquo;ll notice that the game is rendered really, really small, especially when using a larger screen resolution. The game is designed around a screen resolution of 640x480 pixels, with a color depth of 256 colors. Since the game cannot change its resolution, it relies on helper applications that do the resolution switching. Unfortunately, these helper applications won&amp;rsquo;t work anymore. The perfect solution for this problem is a small utility called &lt;a href=&#34;https://www.bcheck.net/apps/reso&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Res-o-Matic&lt;/a&gt;
 by bCheck. This tool allows you to select an application and specify the resolution it should run in. Then, it will create a shortcut file that switches your display to the chosen resolution, launches the application, and returns to the original resolution after the program exits. Really convenient! I used this tool for the entirety of this series, so I can&amp;rsquo;t recommend it enough. Thank you, bCheck!&lt;/p&gt;
&lt;h3 id=&#34;at-startup-i-get-an-error-message-regarding-temporary-file-access-with-error-code--34&#34;&gt;At startup, I get an error message regarding temporary file access with error code -34&lt;/h3&gt;
&lt;p&gt;This issue mainly occurs on the French and Italian releases, and on my version of Meesters van Macht (which is a re-release primarily marketed in Belgium). The game is notoriously susceptible to breaking whenever anything else on your system is trying to access the game data files the moment the game itself requests access to them. Unfortunately, the affected versions bundle one data file required by the game (&lt;code&gt;CHECK.DXR&lt;/code&gt;) in the main executable.&lt;/p&gt;
&lt;p&gt;The problem: When trying to start the game the first couple of times, Windows Defender will reliably check the main executable, thus blocking read access to the embedded &lt;code&gt;CHECK.DXR&lt;/code&gt; file. For the French and Dutch versions, you can &lt;strong&gt;safely ignore&lt;/strong&gt; this error message, since by the time the intro screen from the publishers finishes playing, access to &lt;code&gt;CHECK.DXR&lt;/code&gt; is released, and the game continues just fine.&lt;/p&gt;
&lt;p&gt;The Italian version, however, has no additional intro screen, so it tries to directly jump into the (blocked) &lt;code&gt;CHECK.DXR&lt;/code&gt; file. One thing &lt;code&gt;CHECK.DXR&lt;/code&gt; does is to determine if you are running the game on Windows or Macintosh, and if this check fails, you&amp;rsquo;ll get some very bad side effects, like broken audio and missing speech output. If you see this error message on the Italian version, the game &lt;strong&gt;will&lt;/strong&gt; break, so you have to close the game and start over.&lt;/p&gt;
&lt;p&gt;To mitigate this, I recommend excluding the game directory of the affected versions from your antivirus and any backup/cloud synchronisation software. What also worked pretty reliably for me was to wait a couple of minutes after copying over the game files to let things settle down. YMMV.&lt;/p&gt;
&lt;h3 id=&#34;the-japanese-version-shows-a-text-box-with-garbled-text&#34;&gt;The Japanese version shows a text box with garbled text&lt;/h3&gt;
&lt;p&gt;That&amp;rsquo;s normal and will happen if your machine is set to &lt;em&gt;any&lt;/em&gt; other locale than Japanese. You can safely ignore this error.&lt;/p&gt;
&lt;h3 id=&#34;the-dutch-version-complains-that-my-system-is-not-set-to-256-colors&#34;&gt;The Dutch version complains that my system is not set to 256 colors&lt;/h3&gt;
&lt;p&gt;You can safely ignore this warning and continue to play the game.&lt;/p&gt;
&lt;h3 id=&#34;in-the-italian-version-the-colors-at-the-beginning-of-the-room-of-gravity-are-messed-up&#34;&gt;In the Italian version, the colors at the beginning of the Room of Gravity are messed up&lt;/h3&gt;
&lt;p&gt;Yes, and right now, I don&amp;rsquo;t understand &lt;em&gt;why&lt;/em&gt; this happens. This is one of those bugs that should either happen on &lt;em&gt;all&lt;/em&gt; versions or &lt;em&gt;not at all&lt;/em&gt;. I&amp;rsquo;m currently investigating.&lt;/p&gt;
&lt;h3 id=&#34;will-the-game-still-work-on-windows-95-and-98&#34;&gt;Will the game still work on Windows 95 and 98?&lt;/h3&gt;
&lt;p&gt;Yes, though I can&amp;rsquo;t guarantee that there are no color issues on some configurations. I recommend to stick to the unpatched version.&lt;/p&gt;
&lt;h3 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s next?&lt;/h3&gt;
&lt;p&gt;Well, for this project, this is it. I will look into the remaining issues related to parallel file access and the graphics issues in the Italian version, but right now, it is time for me to take a break and focus on something else. I still have some hardware from 1999 sitting around here that&amp;rsquo;s waiting to see a datacenter from the inside again for way too long, but that&amp;rsquo;s a story for another time.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2025/10/saving-the-masters-of-the-elements-epilogue/endgame_featured.webp&#34; alt=&#34;The End&#34; title=&#34;The End&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;…to be continued… one day.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;but-there-is-one-more-thing&#34;&gt;But there is one more thing.&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2025/10/saving-the-masters-of-the-elements-epilogue/melements-win-de_scummvm_prev.webp&#34; alt=&#34;The End&#34; title=&#34;The End&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;em&gt;…to be continued. For real.&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Saving the Masters of the Elements From Getting Lost to Time: Part 5</title>
      <link>https://felsqualle.com/posts/2025/10/saving-the-masters-of-the-elements-part-5/</link>
      <pubDate>Sat, 04 Oct 2025 10:40:00 +0200</pubDate>
      
      <guid>https://felsqualle.com/posts/2025/10/saving-the-masters-of-the-elements-part-5/</guid>
      <description>&lt;p&gt;While working with the game files of &lt;a href=&#34;https://felsqualle.com/posts/2025/05/saving-the-masters-of-the-elements-part-1/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Masters of the Elements&lt;/a&gt;
, I found something exciting hidden in one of the script files: Multiple, undocumented cheat and debug codes.&lt;/p&gt;
&lt;p&gt;Well, they were undocumented up until now, so without further ado, but a &lt;em&gt;&lt;strong&gt;hefty&lt;/strong&gt;&lt;/em&gt; spoiler warning, let&amp;rsquo;s see how we can bend the laws of time, train bats, or get infinitely lucky.&lt;/p&gt;
&lt;p&gt;I broke down the debug and cheat codes into three sections: Common codes, room codes, and debug codes. The common codes are primarily used for navigating between the different rooms. The room codes, however, will trigger specific actions that are present in this room only. The debug codes are interesting, but not really useful except for development purposes.&lt;/p&gt;
&lt;p&gt;To use a code, all you have to do is type it at the correct location (or everywhere if no location is specified). There is no visible input field or cheat mode you have to enter before typing the first code. Capitalization does not matter; for documentation purposes, I left them as they are written in the game files.&lt;/p&gt;
&lt;h2 id=&#34;common-cheat-codes&#34;&gt;Common cheat codes&lt;/h2&gt;
&lt;table class=&#34;no-wrap-col-1&#34;&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Code&lt;/th&gt;
          &lt;th&gt;Effect&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;Needs master control&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;willy&lt;/td&gt;
          &lt;td&gt;Enable master control code.&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;no&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;allPages&lt;/td&gt;
          &lt;td&gt;Immediately collect all 5 pages that are hidden in the rooms.&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;allStories&lt;/td&gt;
          &lt;td&gt;Immediately unlock all story pages in the entire book.&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;noStories&lt;/td&gt;
          &lt;td&gt;Remove all story pages from the book.&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;intro&lt;/td&gt;
          &lt;td&gt;Go back to the intro screen&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;goGrav&lt;/td&gt;
          &lt;td&gt;Enter the Room of Gravity&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;goWarm&lt;/td&gt;
          &lt;td&gt;Enter the Room of Warmth&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;goTime&lt;/td&gt;
          &lt;td&gt;Enter the Room of Time&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;goElec&lt;/td&gt;
          &lt;td&gt;Enter the Room of Electricity&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;goLight&lt;/td&gt;
          &lt;td&gt;Enter the Room of Light&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;goChance&lt;/td&gt;
          &lt;td&gt;Enter the Garden of Chance&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;room-codes&#34;&gt;Room codes&lt;/h2&gt;
&lt;table class=&#34;no-wrap-col-1&#34;&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Code&lt;/th&gt;
          &lt;th&gt;Effect&lt;/th&gt;
          &lt;th&gt;Location&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;Needs master control&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;IJsfontein&lt;/td&gt;
          &lt;td&gt;Enable/disable two player mode in the juggling game. The blue hand can be controlled using the Ctrl and Alt keys.&lt;/td&gt;
          &lt;td&gt;Room of Gravity&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;no&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Bombilla&lt;/td&gt;
          &lt;td&gt;Enable/disable aim assist when trying to catch the flies.&lt;/td&gt;
          &lt;td&gt;Room of Light&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;no&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Merlyn&lt;/td&gt;
          &lt;td&gt;Re-shuffle the locations of all windows in the tower.&lt;/td&gt;
          &lt;td&gt;Room of Light&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;no&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;twoHands&lt;/td&gt;
          &lt;td&gt;Enable two player mode in the juggling game.&lt;br&gt;The blue hand can be controlled using the Ctrl and Alt keys.&lt;/td&gt;
          &lt;td&gt;Room of Gravity&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;oneHand&lt;/td&gt;
          &lt;td&gt;Disable two player mode in the juggling game.&lt;/td&gt;
          &lt;td&gt;Room of Gravity&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Spiral&lt;/td&gt;
          &lt;td&gt;Immediately finish the electric loop puzzle. You  may have to change locations within the Room of Electricity in order to see an effect.&lt;/td&gt;
          &lt;td&gt;Room of Electricity&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Poort&lt;br&gt;Gate&lt;/td&gt;
          &lt;td&gt;Open the small gate at the end of the electric loop puzzle. Both cheat codes work exactly the same.&lt;/td&gt;
          &lt;td&gt;Room of Electricity&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Train&lt;/td&gt;
          &lt;td&gt;The train holding the battery will immediately appear.&lt;/td&gt;
          &lt;td&gt;Room of Electricity&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Co&lt;/td&gt;
          &lt;td&gt;Summon your companion, even if you haven&amp;rsquo;t solved the juggling  game yet.&lt;/td&gt;
          &lt;td&gt;Room of Light&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Pan&lt;/td&gt;
          &lt;td&gt;Immediately catch the bat and start your journey to the top.&lt;/td&gt;
          &lt;td&gt;Room of Light&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;BigBen&lt;/td&gt;
          &lt;td&gt;Toggle between night and day mode. You will have to wait up to one minute until you see an effect.&lt;/td&gt;
          &lt;td&gt;Room of Light&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Mirror&lt;/td&gt;
          &lt;td&gt;Automatically orient all mirrors to their correct location.&lt;/td&gt;
          &lt;td&gt;Room of Light&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;boom&lt;/td&gt;
          &lt;td&gt;The railway gate will open at the &lt;em&gt;next&lt;/em&gt; drop.&lt;/td&gt;
          &lt;td&gt;Room of Time&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;fuel&lt;/td&gt;
          &lt;td&gt;The train to the Room of Electricity will depart soon, all aboard!&lt;/td&gt;
          &lt;td&gt;Room of Time&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;plant&lt;/td&gt;
          &lt;td&gt;The plant will grow by one step. But be careful, it will die very quickly too!&lt;/td&gt;
          &lt;td&gt;Room of Time&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;fire&lt;/td&gt;
          &lt;td&gt;Immediately ignite the stove.&lt;/td&gt;
          &lt;td&gt;Room of Warmth&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;boat&lt;/td&gt;
          &lt;td&gt;Skip all mini-games.&lt;/td&gt;
          &lt;td&gt;Garden of Chance&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;lucky&lt;/td&gt;
          &lt;td&gt;Use the cheat code, press and hold the Ctrl key at the very last choice you have to make in the game, and your luck will increase to infinity.&lt;/td&gt;
          &lt;td&gt;Garden of Chance&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;debug-codes&#34;&gt;Debug codes&lt;/h2&gt;
&lt;table class=&#34;no-wrap-col-1&#34;&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Code&lt;/th&gt;
          &lt;th&gt;Effect&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;Needs master control&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;showmem&lt;/td&gt;
          &lt;td&gt;Show memory information.&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;removemem&lt;/td&gt;
          &lt;td&gt;Hide memory information.&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;serial&lt;/td&gt;
          &lt;td&gt;Show internal serial number. Purpose unknown.&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;machineslow&lt;/td&gt;
          &lt;td&gt;When starting the game, it will calculate your current system speed and make some adjustments to it dynamically based on the results. This mainly affects drawing and animation speed, so slower machines are not overloaded. The higher the &amp;ldquo;slowness&amp;rdquo; value, the slower the game runs. Not relevant on modern machines, based on testing, the &amp;ldquo;cut-off&amp;rdquo; speed is somewhere above a Pentium 133 MHz where no slowdowns are introduced.&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;setms1&lt;/td&gt;
          &lt;td&gt;Restore the playback speed, simulating a system that is powerful enough to run the game at full speed&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;setms2&lt;/td&gt;
          &lt;td&gt;Set slowness factor to 2&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;setms3&lt;/td&gt;
          &lt;td&gt;Set slowness factor to 3&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;setms4&lt;/td&gt;
          &lt;td&gt;Set slowness factor to 4 (almost unplayable)&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;setms5&lt;/td&gt;
          &lt;td&gt;Set slowness factor to 5 (unplayable)&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;unloadcast&lt;/td&gt;
          &lt;td&gt;Appears to trigger a function that unloads cast members.Function call seems to be broken, no visible effect. Do not use.&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;directsound&lt;/td&gt;
          &lt;td&gt;Show DirectSound information.&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;SoundOff&lt;/td&gt;
          &lt;td&gt;Disable audio output.&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;SoundOn&lt;/td&gt;
          &lt;td&gt;Enable audio output.&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;yes&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;em&gt;&lt;a href=&#34;https://felsqualle.com/posts/2025/10/saving-the-masters-of-the-elements-epilogue/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&amp;hellip;continue reading Part 6 of this series&amp;hellip;&lt;/a&gt;
&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>86Box Now Supports the SafeDisc Copy Protection for CUE/BIN Image Files</title>
      <link>https://felsqualle.com/posts/2025/09/86box-safedisc-copy-protection-for-cue-bin-images/</link>
      <pubDate>Wed, 03 Sep 2025 23:30:00 +0200</pubDate>
      
      <guid>https://felsqualle.com/posts/2025/09/86box-safedisc-copy-protection-for-cue-bin-images/</guid>
      <description>&lt;p&gt;While recently going through the latest commit messages for the incredible PC emulator 86Box in preparation &lt;a href=&#34;https://86box.net/2025/08/24/86box-v5-0.html&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;for their release of version 5.0&lt;/a&gt;
, I stumbled across commit &lt;a href=&#34;https://github.com/86Box/86Box/commit/8944c920aee2ee631af530cab53f57b9753e1e3b&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;8944c92&lt;/a&gt;
 with the following commit message: &lt;code&gt;CD-ROM: Parity and CRC checking support, System Shock 2 now works with a directly mounted .CUE image as well.&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;My initial thought? &amp;ldquo;Wow, they just added support for the SafeDisc copy protection, this is awesome! I can play &lt;em&gt;The Sims&lt;/em&gt; and &lt;em&gt;Age of Empires 2&lt;/em&gt; now!&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Well, the games themselves were already compatible with 86Box, but it was the SafeDisc copy protection that rendered the games unusable.&lt;/p&gt;
&lt;p&gt;Let me explain.&lt;/p&gt;
&lt;h2 id=&#34;wait-what-is-safedisc&#34;&gt;Wait, what is SafeDisc?&lt;/h2&gt;
&lt;div class=&#34;notice info&#34; &gt;
&lt;p class=&#34;first notice-title&#34;&gt;&lt;span class=&#34;icon-notice baseline&#34;&gt;&lt;svg&gt;&lt;use href=&#34;#info-notice&#34;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Whenever I&amp;rsquo;m writing about &amp;ldquo;SafeDisc protected discs&amp;rdquo;, I am strictly referring to CD-ROMs. While there are also SafeDisc variants that support DVD, they only use the ATIP and virtual drive checks. Therefore, the code changes discussed here will have no effect.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;SafeDisc is a copy protection technology that was popular in the late 90s to the early 2000s. Coincidentally, two of my favorite titles, &lt;em&gt;The Sims&lt;/em&gt; (including all their expansion packs and later &amp;ldquo;Deluxe&amp;rdquo; and &amp;ldquo;Complete Collection&amp;rdquo; editions) and &lt;em&gt;Age of Empires 2&lt;/em&gt;, are protected with SafeDisc as well. Each time the protected application or game is started, SafeDisc performs various checks to ensure that the original disc is inserted into the user&amp;rsquo;s computer. If SafeDisc detects the original disc and passes all authentication steps, it decrypts the program data and launches the application. If authentication fails, the program won&amp;rsquo;t start, and you&amp;rsquo;ll receive an error message, typically asking you to insert the correct CD-ROM into your drive.&lt;/p&gt;
&lt;p&gt;The term &lt;em&gt;copy protection&lt;/em&gt; is very accurate: SafeDisc was effective at preventing &lt;em&gt;copies&lt;/em&gt; written to blank CD-Rs, but not very effective at preventing &lt;em&gt;dumping&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Depending on the version of SafeDisc, the tool incorporates various technologies to prevent copies:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Intentionally bad sectors that cause read errors&lt;/li&gt;
&lt;li&gt;So-called &amp;ldquo;weak&amp;rdquo; sectors that exploit flaws in the EFM encoding&lt;/li&gt;
&lt;li&gt;ATIP detection being able to recognize CD-Rs&lt;/li&gt;
&lt;li&gt;Detection of virtual drives&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The detection of virtual drives was quickly bypassed by drive emulation software, allowing it to &amp;ldquo;hide&amp;rdquo; the virtual drive from the rest of the system, making SafeDisc unable to detect it.&lt;/p&gt;
&lt;p&gt;The ATIP detection tries to read the ATIP information of the disc. The ATIP is an area that sits &amp;ldquo;before&amp;rdquo; the data area of a (re)writable disc. It contains information about the characteristics of the disc, such as the type, manufacturer, and additional data that helps CD writers calibrate their writing parameters. If SafeDisc detects that you are running the application from a CD-R, it will refuse to work. To bypass the ATIP check, you must either use drive emulation software or a CD-ROM drive that can only read, but not write, CDs.&lt;/p&gt;
&lt;p&gt;The &amp;ldquo;weak&amp;rdquo; sectors are an interesting phenomenon. When &lt;em&gt;reading&lt;/em&gt; the disc, the weak sectors will read just fine like any other sector. However, when you try to write the image to a CD-R again, your CD writer will likely either refuse to burn the image during the process or the copy will not work. Before the data is sent to the laser that actually burns the data onto the disc, your CD writer will have to go through a step called &amp;ldquo;&lt;a href=&#34;https://en.wikipedia.org/wiki/Eight-to-fourteen_modulation&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;EFM encoding&lt;/a&gt;
&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;The data within the weak sectors is laid out in a way that tries to confuse the EFM encoding by creating special patterns. To this day, making copies of the weak sectors is not easily possible with modern drives. During the authentication process, SafeDisc will try to read the weak sectors and refuse to work if they can&amp;rsquo;t be read correctly. Please note that the weak sectors only affect &lt;em&gt;writing new copies&lt;/em&gt;, but not &lt;em&gt;reading the original disc&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;If you are interested in all the details regarding the weak sectors and how EFM works, there&amp;rsquo;s a great article about it &lt;a href=&#34;https://web.archive.org/web/20090603002402/http://sirdavidguy.coolfreepages.com/SafeDisc_2_Technical_Info.html&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;available online&lt;/a&gt;
.&lt;/p&gt;
&lt;h2 id=&#34;intentionally-breaking-data&#34;&gt;Intentionally breaking data&lt;/h2&gt;
&lt;p&gt;To recap, we are - in theory - already clearing 3 out of the (up to) 4 checks SafeDisc implements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The virtual drive detection is not effective since we can emulate multiple drive models, and due to the full-system emulation, all applications will see a valid drive.&lt;/li&gt;
&lt;li&gt;The ATIP check will always pass since we are creating the image from a valid, original disc, so there&amp;rsquo;s no ATIP information to pass to SafeDisc.&lt;/li&gt;
&lt;li&gt;The weak sectors will always read fine since we are not working with a burned disc that&amp;rsquo;s potentially affected by the bad EFM encoding.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The most important part of SafeDisc, starting with the very first version, is the introduction of intentionally bad sectors. Reading CDs is hard. You are reading data with a laser from a thin metal layer with tiny grooves in it, through a thin sheet of clear plastic. That&amp;rsquo;s not reliable! To mitigate reading errors through smudges or scratched discs, CDs contain multiple levels of error detection and (to a degree) correction.&lt;/p&gt;
&lt;p&gt;For demonstration purposes, I&amp;rsquo;ll work with a CUE/BIN dump I created from my original German Age of Empires 2 disc, which has a &lt;a href=&#34;http://redump.org/disc/42641/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;matching entry in the redump.org database&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;When creating a dump file from a disc protected with SafeDisc, you&amp;rsquo;ll notice some read errors that will always show up, usually in the first 10000 sectors:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[LBA:    841] C2 error (bits:  312)                          
[LBA:    844] C2 error (bits:  312)                          
[LBA:    866] C2 error (bits:  312)                          
[LBA:    869] C2 error (bits:  312)                          
[LBA:    872] C2 error (bits:  312)                          
[LBA:    899] C2 error (bits:  312)                          
[LBA:    902] C2 error (bits:  312)                          
...                         
[LBA:   9961] C2 error (bits:  312)                             
[LBA:   9991] C2 error (bits:  312)                             
[LBA:  10006] C2 error (bits:  312)                             
[LBA:  10009] C2 error (bits:  312)                             
[LBA:  10012] C2 error (bits:  312)                             
[LBA:  10039] C2 error (bits:  312)                             
[LBA:  10042] C2 error (bits:  312)                             
media errors: 
 SCSI: 0
 C2: 812
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;C2 errors are reading errors that can be corrected to a certain degree by the drive&amp;rsquo;s error correction capabilities. If the damage is too severe, the drive will start to interpolate the missing data with a &amp;ldquo;best guess&amp;rdquo;. For CD audio, this is not a big deal: You either don&amp;rsquo;t hear the side effects of the interpolation or you&amp;rsquo;ll get a crackling or popping noise for a fraction of a second. But for a CD-ROM containing data, that&amp;rsquo;s not acceptable; even if we flip one single bit, the data will be invalid.&lt;/p&gt;
&lt;p&gt;For this reason, CD-ROMs contain a second, higher level of error correction and detection: EDC/ECC.&lt;/p&gt;
&lt;p&gt;A sector on a standard Mode 1 CD-ROM contains 2352 bytes of data (excluding subchannel information which are not relevant here), and in CUE/BIN images, we capture all 2352 bytes. 2048 bytes are dedicated to the &amp;ldquo;usable&amp;rdquo; data itself, and the 288 bytes &amp;ldquo;above&amp;rdquo; contain the error correction information. From these 288 bytes, the lower 4 bytes are reserved for error &lt;em&gt;detection&lt;/em&gt; using a CRC checksum. Then, there&amp;rsquo;s a &amp;ldquo;gap&amp;rdquo; of 8 reserved bytes, followed by 276 bytes of error &lt;em&gt;correction&lt;/em&gt; information using parity data.&lt;/p&gt;
&lt;p&gt;When a drive reads a sector, it will check the 2048 data bytes against the 4 bytes of the checksum. If the checksum matches, it knows that the sector is good and tells the operating system that it could read the sector without problems. Otherwise, if the data and checksum don&amp;rsquo;t match, the drive knows that it read incorrect data and tries to rebuild the missing data using the 276 bytes of parity data. Assuming the read error is not too severe, the parity data contains enough information to reconstruct the original data. In this case, the drive will also report a good read to the operating system.&lt;/p&gt;
&lt;p&gt;However, if there is too much original data missing, the error correction will fail because there&amp;rsquo;s not enough parity information to reconstruct the missing bits. Then, the drive will return a reading error to the operating system.&lt;/p&gt;
&lt;p&gt;SafeDisc &lt;em&gt;intentionally&lt;/em&gt; places damaged sectors on the disc. It doesn&amp;rsquo;t contain any game data; it is only there for internal use by SafeDisc itself. During the authentication process, SafeDisc instructs the drive to (randomly) read some of the intentionally bad sectors. And unlike the weak sectors, the sectors &lt;em&gt;need&lt;/em&gt; to be unreadable. If the drive can read the deliberately damaged sectors, the authentication will fail, and SafeDisc reports the disc as missing or a copy.&lt;/p&gt;
&lt;p&gt;Using bad sectors serves two purposes: Many drives will try really hard to correct the bad sectors, sometimes needing hours for a simple copy. Earlier software wasn&amp;rsquo;t designed with this protection in mind, so it would often simply fail if it encountered a reading error. Why would you want to burn bad data in the first place? And speaking of burning, many earlier disc copying software couldn&amp;rsquo;t burn bad data. The software or the drive would either refuse to work, or &lt;em&gt;silently&lt;/em&gt; rebuild the checksum fields to &amp;ldquo;match&amp;rdquo; the user data it could read, &lt;em&gt;fixing&lt;/em&gt; the reading errors that should not be fixed.&lt;/p&gt;
&lt;h2 id=&#34;sometimes-fixing-errors-is-not-good&#34;&gt;Sometimes, fixing errors is not good&lt;/h2&gt;
&lt;p&gt;So, if SafeDisc &lt;em&gt;can&lt;/em&gt; read the damaged sectors, it knows it is a copy. And until the commit mentioned at the beginning of this article, 86Box couldn&amp;rsquo;t emulate read errors. Even though the image is dumped correctly and contains the &amp;ldquo;bad&amp;rdquo; sectors, filled with dummy data to ensure a broken checksum and invalid parity information, 86Box&amp;rsquo;s drive emulation would happily report a &lt;em&gt;good&lt;/em&gt; read. And yes, this breaks SafeDisc.&lt;/p&gt;
&lt;p&gt;To demonstrate the emulation flaw, I loaded the image I created from my original disc into 86Box and ran a surface scan with an old version of IsoBuster.&lt;/p&gt;
&lt;p&gt;With the old drive emulation code, the disc reads without errors, even though it should have 812 errors:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2025/09/86box-safedisc-copy-protection-for-cue-bin-images/86box_aoe2_isobuster_noerror.webp&#34; alt=&#34;Surface scan performed on the Age of Empires 2 disc using the old emulation code, showing no errors&#34; title=&#34;Surface scan performed on the Age of Empires 2 disc using the old emulation code, showing no errors&#34;&gt;
&lt;/p&gt;
&lt;p&gt;When I run the surface scan with the new drive emulation code, the 812 errors are reported correctly as reading errors:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2025/09/86box-safedisc-copy-protection-for-cue-bin-images/86box_aoe2_isobuster_errorfound.webp&#34; alt=&#34;Surface scan performed on the Age of Empires 2 disc using the new emulation code, showing 812 reading errors&#34; title=&#34;Surface scan performed on the Age of Empires 2 disc using the new emulation code, showing 812 reading errors&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2025/09/86box-safedisc-copy-protection-for-cue-bin-images/86box_aoe2_isobuster_errorlog.webp&#34; alt=&#34;Surface scan performed on the Age of Empires 2 disc using the new emulation code, showing 812 reading errors&#34; title=&#34;Surface scan performed on the Age of Empires 2 disc using the new emulation code, showing 812 reading errors&#34;&gt;
&lt;/p&gt;
&lt;p&gt;And while the game previously wouldn&amp;rsquo;t pass the copy protection, asking me to insert the correct CD-ROM into the drive&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2025/09/86box-safedisc-copy-protection-for-cue-bin-images/86box_aoe2_disc_error.webp&#34; alt=&#34;Age of Empires 2 using the old emulation code, asking for insert the correct CD-ROM&#34; title=&#34;Age of Empires 2 using the old emulation code, asking for insert the correct CD-ROM&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&amp;hellip; it now allows me to go back to medieval times!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2025/09/86box-safedisc-copy-protection-for-cue-bin-images/86box_aoe2_ingame.webp&#34; alt=&#34;Age of Empires 2 using the new emulation code, showing the main menu in-game&#34; title=&#34;Age of Empires 2 using the new emulation code, showing the main menu in-game&#34;&gt;
&lt;/p&gt;
&lt;h2 id=&#34;about-version-compatibility&#34;&gt;About version compatibility&lt;/h2&gt;
&lt;p&gt;Age of Empires 2 is using the very old SafeDisc 1.30.010. SafeDisc 1.x only uses the intentional reading errors to detect copies. Especially the ATIP checks and the virtual drive detection are a novelty to SafeDisc 3.x and 4.x. The newest disc using SafeDisc I own is my copy of &lt;a href=&#34;http://redump.org/disc/74564/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;The Sims: Complete Collection&lt;/a&gt;
, which uses SafeDisc 4.50.000. Right now, I couldn&amp;rsquo;t get the game to work on 86Box 5.0, using the stable build with the old dynarec. When it tries to perform the copy protection, the application segfaults. This is not related to the game, but also happens when starting The Sims Creator that&amp;rsquo;s bundled with the game and also does the SafeDisc check. I also tried with build 7706 using the new dynarec and ran into the same issue.&lt;/p&gt;
&lt;p&gt;So while earlier versions of SafeDisc now seem to work just fine, you might run into problems with &amp;ldquo;newer&amp;rdquo; games. Time to dust off my stash of discs to get some more samples&amp;hellip;&lt;/p&gt;
&lt;p&gt;Thanks, dear 86Box team - keep up your great work, you are awesome!&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Saving the Masters of the Elements From Getting Lost to Time: Part 4</title>
      <link>https://felsqualle.com/posts/2025/08/saving-the-masters-of-the-elements-part-4/</link>
      <pubDate>Sun, 31 Aug 2025 10:00:00 +0200</pubDate>
      
      <guid>https://felsqualle.com/posts/2025/08/saving-the-masters-of-the-elements-part-4/</guid>
      <description>&lt;p&gt;In the &lt;a href=&#34;https://felsqualle.com/posts/2025/07/saving-the-masters-of-the-elements-part-3/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;previous part of this series&lt;/a&gt;
, we finally fixed the game itself by removing the broken library and replacing it with a more modern alternative.&lt;/p&gt;
&lt;p&gt;However, we are not done yet - there are still some minor issues left to fix. Let&amp;rsquo;s go!&lt;/p&gt;
&lt;p&gt;Starting the game is a whole puzzle by itself. Depending on the release, the intro screen, which also serves as the main menu, is broken starting with Windows 2000. This issue significantly hinders the game&amp;rsquo;s usability, as you&amp;rsquo;ll either get almost all UI elements (except the one that actually starts the game) or none (unless you know the exact location and hover the mouse over them).&lt;/p&gt;
&lt;p&gt;When rendered correctly, the intro screen is supposed to look like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2025/08/saving-the-masters-of-the-elements-part-4/melements-win-de_fixed_intro.webp&#34; alt=&#34;This is how the intro screen should look like (German release)&#34; title=&#34;This is how the intro screen should look like (German release)&#34;&gt;
&lt;/p&gt;
&lt;p&gt;The rendering issue is not too bad in the German release. The only UI element that is missing is the &amp;ldquo;pull switch&amp;rdquo; that starts the game. Luckily, you can just use the &lt;em&gt;Hilfe&lt;/em&gt; button, since that one will start the game as well.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2025/08/saving-the-masters-of-the-elements-part-4/melements_intro_less_broken_ui.webp&#34; alt=&#34;Missing UI elements in the intro screen (German release)&#34; title=&#34;Missing UI elements in the intro screen (German release)&#34;&gt;
&lt;/p&gt;
&lt;p&gt;For comparison, here&amp;rsquo;s a screenshot of a properly rendered intro screen of the English release:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2025/08/saving-the-masters-of-the-elements-part-4/melements_intro_fixed_featured.webp&#34; alt=&#34;This is how the intro screen should look like (English release)&#34; title=&#34;This is how the intro screen should look like (English release)&#34;&gt;
&lt;/p&gt;
&lt;p&gt;As you can see in the screenshot below, the English (as well as the French and Dutch) releases are affected more severely. The UI elements are entirely missing, unless you hover the mouse cursor at the exact location. This makes it basically impossible to start the game unless you played it before and remember the exact locations of the UI elements.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2025/08/saving-the-masters-of-the-elements-part-4/melements_intro_broken_ui.webp&#34; alt=&#34;Missing UI elements in the intro screen (English release)&#34; title=&#34;Missing UI elements in the intro screen (English release)&#34;&gt;
&lt;/p&gt;
&lt;p&gt;Like many Macromedia Director games, &lt;em&gt;Masters of the Elements&lt;/em&gt; relies on a screen resolution of 640x480 and a color depth of 256 colors. While 256 colors were common back in the day, modern Windows releases and graphics drivers basically only support this color mode through emulation and by wrapping the necessary functions. Furthermore, Macromedia Director supports multiple color palettes and even provides options to swap them mid-movie, allowing more colors than the initial color palette provides.&lt;/p&gt;
&lt;p&gt;Setting the screen resolution and proper color depth is accomplished by a small launcher executable that all versions of &lt;em&gt;Masters of the Elements&lt;/em&gt; provide. This application sets the screen resolution and color depth, then launches the Macromedia Director projector executable, ultimately starting the game. And yes, this launcher executable is also broken on modern Windows. The non-native 256 colors combined with the palette assignment in Macromedia Director is what breaks the intro screen.&lt;/p&gt;
&lt;p&gt;I was able to track down the broken palette assignment in the &lt;code&gt;INTRO\INTRO.DIR&lt;/code&gt; file. The relevant code is the &lt;code&gt;on startIntro&lt;/code&gt; function in Movie Script 31:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;on startIntro
  set navigationZones to []
  set navigationZones to [rect(0, 0, 640, 480)]
  set navigationPoints to [[300, 170]]
  set navigationDirection to [1]
  set navigationScripts to [&amp;#34;zoomInOnBook&amp;#34;]
  puppetPalette(&amp;#34;black palette&amp;#34;, 25)
  updateStage()
  set the loc of sprite 1 to point(2000, 2000)
  setSprite(4, iassets, 21)
  setSprite(2, iassets, 11)
  showButtons()
  ...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Removing&lt;/strong&gt; the line&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;puppetPalette(&amp;#34;black palette&amp;#34;, 25)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;which transitions to the palette called &lt;code&gt;black palette&lt;/code&gt; with a speed setting of &lt;code&gt;25&lt;/code&gt; (where &lt;code&gt;1&lt;/code&gt; is the slowest and &lt;code&gt;60&lt;/code&gt; the fastest possible setting) fixes the rendering issues without any notable side effects. Since using this palette won&amp;rsquo;t cause issues when running the game on Windows 95/98/ME using the native 256 color space, my conclusion is that a) the palette transition breaks the 256 color mapping into the 32-bit color space of current Windows and b) the color mapping is working well enough to avoid any negative side effects.&lt;/p&gt;
&lt;p&gt;The odd one out is the Italian release. Right now, I don&amp;rsquo;t fully understand what is wrong with this version. The UI elements in the intro screen render correctly; however, both the clouds at the beginning and the transition to the Room of Gravity are rendered using the incorrect color palette. Luckily for us, this is the only issue I could find, so this is more likely related to the transition itself and not directly to the color palette.&lt;/p&gt;
&lt;p&gt;When running the game natively without any modifications on Windows 95/98/ME, the transition to the Room of Gravity looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2025/08/saving-the-masters-of-the-elements-part-4/melements-win-issue1_fixed.webp&#34; alt=&#34;Palette rendering issue in the Room of Gravity in the Italian release of Masters of the Elements, Sample 1&#34; &gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2025/08/saving-the-masters-of-the-elements-part-4/melements-win-issue2_fixed.webp&#34; alt=&#34;Palette rendering issue in the Room of Gravity in the Italian release of Masters of the Elements, Sample 2&#34; title=&#34;Transition to the Room of Gravity, correct color palette&#34;&gt;
&lt;/p&gt;
&lt;p&gt;Comparing the screenshots from above with the broken rendering reveals that the solid blue background of the &amp;ldquo;first half&amp;rdquo; of the room transition is replaced with solid black:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2025/08/saving-the-masters-of-the-elements-part-4/melements-win-it_issue1.webp&#34; alt=&#34;Palette rendering issue in the Room of Gravity in the Italian release of Masters of the Elements, Sample 1&#34; &gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2025/08/saving-the-masters-of-the-elements-part-4/melements-win-it_issue2.webp&#34; alt=&#34;Palette rendering issue in the Room of Gravity in the Italian release of Masters of the Elements, Sample 2&#34; title=&#34;Transition to the Room of Gravity, bad color palette&#34;&gt;
&lt;/p&gt;
&lt;p&gt;Interestingly, this version &lt;em&gt;includes&lt;/em&gt; the line in the INTRO.DIR movie that breaks all the other versions, so they likely used an entirely different palette or changed &lt;em&gt;something&lt;/em&gt; I have yet to find when building the movie files themselves.&lt;/p&gt;
&lt;p&gt;Minor changes across the different language releases are a recurring theme for this game. Many strings are hard-coded into the movie files, so they vary slightly across all versions. Furthermore, there&amp;rsquo;s a movie file called &lt;code&gt;CHECK.DIR&lt;/code&gt; that&amp;rsquo;s embedded into the projector file for the French, Italian, and Dutch release I own, while the other releases have this movie as a seperate file. At this point, I&amp;rsquo;m convinced that I  don&amp;rsquo;t own the truly original Dutch release, but rather the later re-release primarily marketed in Belgium. If someone has the original Dutch release from 1997 for sale, please get in touch with me.&lt;/p&gt;
&lt;p&gt;When you start the game, you&amp;rsquo;ll notice that the game is rendered really, really small, especially when using a larger screen resolution. As I briefly mentioned, the game originally switched to 640x480 by using the (now broken) helper application. Initially, I tried to use the compatibility option provided by Windows, but the program never notifies Windows when it closes, so you then have to &lt;em&gt;manually&lt;/em&gt; revert back from 640x480 to your native screen resolution. This gets annoying really, really fast.&lt;/p&gt;
&lt;p&gt;The perfect solution for this problem is a small utility called &lt;a href=&#34;https://www.bcheck.net/apps/reso&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Res-o-Matic&lt;/a&gt;
 by bCheck. This tool lets you pick an application and the resolution it should run in. Then, it will create a shortcut file that switches your display to the selected resolution, launches the application, and returns to the original resolution after the program exits. Really convenient! I used this tool for the entirety of this series, so I can&amp;rsquo;t recommend it enough. Thank you, bCheck!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2025/08/saving-the-masters-of-the-elements-part-4/reso7_screenshot.webp&#34; alt=&#34;Screenshot of the reso7 tool&#34; title=&#34;Screenshot of the reso7 tool&#34;&gt;
&lt;/p&gt;
&lt;p&gt;To summarize, the game is now fully functional, for the first time in about 20 yearss. We have successfully fixed all the issues that we could. Mission accomplished?&lt;/p&gt;
&lt;p&gt;Well&amp;hellip; I think I just found some subtle changes in the Lingo code across the different releases, and they are &lt;em&gt;not&lt;/em&gt; related to the localization. This can only mean one thing: There might be multiple versions of the game engine.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;a href=&#34;https://felsqualle.com/posts/2025/10/saving-the-masters-of-the-elements-part-5/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&amp;hellip;continue reading Part 5 of this series&amp;hellip;&lt;/a&gt;
&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Saving the Masters of the Elements From Getting Lost to Time: Part 3</title>
      <link>https://felsqualle.com/posts/2025/07/saving-the-masters-of-the-elements-part-3/</link>
      <pubDate>Sun, 13 Jul 2025 15:00:00 +0200</pubDate>
      
      <guid>https://felsqualle.com/posts/2025/07/saving-the-masters-of-the-elements-part-3/</guid>
      <description>&lt;p&gt;I briefly talked about &lt;code&gt;Xtras&lt;/code&gt; and &lt;code&gt;XObjects&lt;/code&gt; in the &lt;a href=&#34;https://felsqualle.com/posts/2025/05/saving-the-masters-of-the-elements-part-1/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;first part of this series&lt;/a&gt;
. To recap, Xtras and XObjects are specially crafted libraries that extend Macromedia Director&amp;rsquo;s functionality by exporting additional functions that can then be used in the Lingo scripts powering the individual application. This allows a deeper integration with the operating system, since the Xtras and XObjects can provide a bridge between Macromedia Director and the operating system&amp;rsquo;s API.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Masters of the Elements&lt;/em&gt; uses such a third-party XObject for controlling the mouse cursor as we discovered in the &lt;a href=&#34;https://felsqualle.com/posts/2025/05/saving-the-masters-of-the-elements-part-1/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;previous articles&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;Now that we know how it breaks the game and what &lt;a href=&#34;https://felsqualle.com/posts/2025/07/saving-the-masters-of-the-elements-part-2/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;it actually does&lt;/a&gt;
, it is time to look for a proper replacement.&lt;/p&gt;
&lt;h2 id=&#34;finding-the-xtra&#34;&gt;Finding the Xtra&lt;/h2&gt;
&lt;p&gt;Thankfully, the Macromedia Director community was very active back in the day, featuring countless developers who created both commercial and non-commercial Xtras. When looking for an Xtra that covers a specific function, the place to go is the &lt;a href=&#34;https://www.deansdirectortutorials.com/MileHighTable/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Director Mile High Table O&amp;rsquo;Products&lt;/a&gt;
 provided by &lt;a href=&#34;https://www.deansdirectortutorials.com/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Dean&amp;rsquo;s Director Tutorials&lt;/a&gt;
. This website is an incredible resource for anything related to Macromedia Director, so I highly recommend checking it out if you are interested in learning more!&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&#34;https://www.deansdirectortutorials.com/MileHighTable/#MouseAndKeyboard&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Mouse and Keyboard&lt;/a&gt;
 section of this list, I spotted two possible candidates for a replacement: &lt;a href=&#34;https://web.archive.org/web/20071013195939/http://pros.orange.fr:80/freextras/index_en.html#SetMouseLocXtra&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;SetMouseLoc Xtra&lt;/code&gt;&lt;/a&gt;
 by Laurent Cozic, and &lt;a href=&#34;https://web.archive.org/web/20160314233605/http://scirius.com/SetMouseXtra.htm&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;code&gt;SetMouseXtra&lt;/code&gt;&lt;/a&gt;
 by Stephan Eichhorn from Scirius Development.&lt;/p&gt;
&lt;p&gt;According to the Director Mile High Table O&amp;rsquo;Products list, &lt;code&gt;SetMouseLoc Xtra&lt;/code&gt; is only compatible with Macromedia Director 8.5 and higher. Since we are working with a game made with Director 6.0.2, choosing this Xtra is not a viable option. &lt;code&gt;SetMouseXtra&lt;/code&gt;, however, claims to be compatible with Macromedia Director 5 to 11. Since &lt;code&gt;SetMouseXtra&lt;/code&gt; is distributed as freeware without any special licensing requirements, this seems to be an excellent choice.&lt;/p&gt;
&lt;p&gt;Looking at the interface description of SetMouseXtra reveals that this Xtra is not only a viable option, but a complete match. It exports one function, and expects the same parameters as &lt;code&gt;PUTCURS.dll&lt;/code&gt; does: The &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; coordinates.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;xtra SetMouseXtra               -- Version 1.0
-- --------------------------------------------------------------------
-- this Xtra allows to set the mouse position through Lingo
-- --------------------------------------------------------------------
1997 by Stephan Eichhorn, Scirius Multimedia
-- e-mail:  &amp;lt;redacted&amp;gt;
-- WWW:     http://www.scirius.com
* SetMouse integer x,integer y     -- set the cursor to position x,y
-- all x,y are in screen coordinates,
-- not relative to the stage!
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;the-integration&#34;&gt;The Integration&lt;/h2&gt;
&lt;p&gt;After &lt;a href=&#34;https://web.archive.org/web/20160429032931/http://scirius.com/SetMouseXtra/SetMouseXtra.zip&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;downloading&lt;/a&gt;
 the .zip archive with the Xtra files, I extracted the file &lt;code&gt;SMXTRA.X32&lt;/code&gt; from the &lt;code&gt;Windows&lt;/code&gt; directory inside the archive to the &lt;code&gt;XTRAS&lt;/code&gt; directory of my copy of the game.&lt;/p&gt;
&lt;p&gt;Contrary to the old XObjects, the newer Xtras introduced with Macromedia Director 5 support autoloading. As soon as the Xtra is placed in the application&amp;rsquo;s &lt;code&gt;XTRAS&lt;/code&gt; directory, all functions will be available immediately after startup without having to use &lt;code&gt;OpenXLib&lt;/code&gt;. Both the old XObject and our new Xtra are so similar that we can use it as a simple drop-in replacement by modifying the &lt;code&gt;moveTheCursor&lt;/code&gt; function, where we replace the old &lt;code&gt;myMouse(mSet...)&lt;/code&gt; call with &lt;code&gt;SetMouse()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Old function call&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;on moveTheCursor X, y
...
  if platform = #windows then
    set offSetX to the stageLeft + (4 * (the runMode = &amp;#34;Author&amp;#34;))
    set offSetY to the stageTop + (42 * (the runMode = &amp;#34;Author&amp;#34;))
    myMouse(mSet, integer(X + offSetX), integer(y + offSetY))
  else
...
end
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;em&gt;&lt;strong&gt;New function call&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;on moveTheCursor X, y
...
  if platform = #windows then
    set offSetX to the stageLeft + (4 * (the runMode = &amp;#34;Author&amp;#34;))
    set offSetY to the stageTop + (42 * (the runMode = &amp;#34;Author&amp;#34;))
    SetMouse(integer(X + offSetX), integer(y + offSetY))
  else
...
end
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is how the final script looks after replacing the &amp;ldquo;broken&amp;rdquo; calls to the old XObject with the new ones from our replacement Xtra:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;on definePlatform
  global platform
  if the machineType = 256 then
    set platform to #windows
  else
    set platform to #macintosh
  end if
end

on initCursorObject
  global myMouse, platform, objectPath
  if platform = #windows then
  else
    openXLib(objectPath &amp;amp; &amp;#34;MoveMous.XOB&amp;#34;)
    set myMouse to MoveMouse(mnew)
  end if
end

on exitCursorObject
  global myMouse, platform, objectPath
  if platform = #windows then
  else
    closeXLib(objectPath &amp;amp; &amp;#34;MoveMous.XOB&amp;#34;)
  end if
end

on moveTheCursor X, y
  global myMouse, platform, offSetX, offSetY
  if platform = #windows then
    set offSetX to the stageLeft + (4 * (the runMode = &amp;#34;Author&amp;#34;))
    set offSetY to the stageTop + (42 * (the runMode = &amp;#34;Author&amp;#34;))
    SetMouse(integer(X + offSetX), integer(y + offSetY))
  else
    myMouse(mSetMouseLoc, integer(X), integer(y))
  end if
  set a to the mouseH
end
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;what-about-macintosh&#34;&gt;What about Macintosh?&lt;/h2&gt;
&lt;p&gt;The modified script works on both Windows and Macintosh, since I left the parts specific to Macintosh intact. However, it is very unlikely that this modified file will ever affect a Macintosh version. &lt;em&gt;Masters of the Elements&lt;/em&gt; is distributed as a so-called &amp;ldquo;Hybrid disc&amp;rdquo;, which means that the disc contains two distinct file systems. The ISO9660 file system, which you probably know from .iso files, contains all the files that are responsible for the Windows version. The Macintosh files, however, are stored in a separate &lt;code&gt;HFS&lt;/code&gt; file system on the same disc.&lt;/p&gt;
&lt;p&gt;On a Windows system, the &lt;code&gt;HFS&lt;/code&gt; partition won&amp;rsquo;t even be visible without using third-party tools. Hence, the chance of unvoluntarily overwriting the &amp;ldquo;wrong&amp;rdquo; file is pretty much zero, but for the sake of completeness, I didn&amp;rsquo;t unnecessarily remove the Macintosh integration.&lt;/p&gt;
&lt;h2 id=&#34;there-and-back-again&#34;&gt;There and Back Again&lt;/h2&gt;
&lt;video class=&#34;video-shortcode&#34; preload=&#34;auto&#34; poster=&#34;melements-working-demo-poster_featured.webp&#34; controls width=&#34;100%&#34;&gt;
    &lt;source src=&#34;https://felsqualle.com/posts/2025/07/saving-the-masters-of-the-elements-part-3/melements-working-demo.mp4&#34; type=&#34;video/mp4&#34;&gt;
    There should have been a video here but your browser does not seem
    to support it.
&lt;/video&gt;

&lt;p&gt;Finally, after more than 20 years, the game is fully playable again. I replayed the first scene in the Room of Gravity and successfully solved the Room of Warmth. Yes, I had to cut the video a bit; I&amp;rsquo;m not that great at baking pancakes, and I couldn&amp;rsquo;t trigger the shortcut through the egg at the juggling puzzle.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fun fact:&lt;/strong&gt; If you are running the game on a slower system, it gives you an advantage by slowing down the animation speed. &amp;ldquo;Slower&amp;rdquo; in this context means something along the lines of a Pentium 133. To determine the system speed, the game runs a couple of checks at the beginning of the game, where it tries to draw sprites and transitions as fast as possible. If you are below a certain threshold, then the code path that slows down the entire game triggers.&lt;/p&gt;
&lt;p&gt;I wonder if I can use this to create a cheat that makes the game easier. Oh, and there&amp;rsquo;s one thing left to do.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;a href=&#34;https://felsqualle.com/posts/2025/08/saving-the-masters-of-the-elements-part-4/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&amp;hellip;continue reading Part 4 of this series&amp;hellip;&lt;/a&gt;
&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>BTS#3: Removing Unwanted Services and Modules for a Headless Raspberry Pi Zero 2 W</title>
      <link>https://felsqualle.com/posts/2025/07/bts3-removing-unwanted-modules-for-headless-pi-zero/</link>
      <pubDate>Wed, 09 Jul 2025 14:00:00 +0200</pubDate>
      
      <guid>https://felsqualle.com/posts/2025/07/bts3-removing-unwanted-modules-for-headless-pi-zero/</guid>
      <description>&lt;p&gt;For my next small side project, I&amp;rsquo;m running a Raspberry Pi Zero 2 W as a proxy server for my website. In fact, right now, fabulous.system is served from this exact Pi!&lt;/p&gt;
&lt;p&gt;For this task, the limited CPU capabilities are not that big of a deal, but the limited RAM is. Sometimes, 512 MB is really not &lt;em&gt;enough&lt;/em&gt; memory. Clearly I can&amp;rsquo;t simply add more RAM to the Pi, so I decided to disable as many hardware features as possible.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t need WiFi, sound or accelerated video; all I need is USB for my Ethernet adapters.&lt;/p&gt;
&lt;div class=&#34;notice warning&#34; &gt;
&lt;p class=&#34;first notice-title&#34;&gt;&lt;span class=&#34;icon-notice baseline&#34;&gt;&lt;svg&gt;&lt;use href=&#34;#warning-notice&#34;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Modifying the startup configuration and kernel modules can lead to a non-bootable system or unwanted behavior. Be careful if you don&amp;rsquo;t have physical access to the device!&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;I started by removing all services I don&amp;rsquo;t need, since I have no use for WiFi, Bluetooth, sound handling, or local console setup. Since I am not going to use the GPIO pins and the Raspberry Pi Zero 2W has no built-in EEPROM, I can deactivate these services as well.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;sudo systemctl disable --now avahi-daemon Bluetooth console-setup rpi-eeprom-update triggerhappy ModemManager wpa_supplicant alsa-restore rpc-statd-notify hciuart Bluetooth
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next, on to the hardware side of things!&lt;/p&gt;
&lt;p&gt;In the startup configuration located at &lt;code&gt;/boot/firmware/config.txt&lt;/code&gt;, remove the line&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;dtoverlay=vc4-kms-v3d
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;and add&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;dtparam=i2c_arm=off
dtparam=i2s=off
dtparam=spi=off
dtparam=audio=off
dtoverlay=disable-wifi
dtoverlay=disable-bt

display_auto_detect=0
camera_auto_detect=0
gpu_mem=16
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Reboot the system to apply the changes. This modification disables (most) of the audio and higher-level video handling as well as some lower-end components like i²c and SPI. Setting &lt;code&gt;gpu_mem&lt;/code&gt; to 16 restricts the GPU core to 16 MB of VRAM. Unfortunately, we can&amp;rsquo;t go any lower; 16 MB is the bare minimum. On this model of the Raspberry Pi series, the GPU (also known as VideoCore) does much more than just video output; it is also responsible for certain aspects of memory management and loading the initial firmware.&lt;/p&gt;
&lt;p&gt;Finally, I blacklisted all kernel modules that are not essential for my use-case. To do this, modify the file &lt;code&gt;/etc/modprobe.d/raspi-blacklist.conf&lt;/code&gt; and add the following entries:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;blacklist bcm2835_codec
blacklist bcm2835_isp
blacklist Bluetooth
blacklist bnep
blacklist btbcm
blacklist drm
blacklist rfkill
blacklist snd
blacklist snd_bcm2835
blacklist snd_compress
blacklist snd_pcm
blacklist snd_pcm_dmaengine
blacklist snd_soc_core
blacklist snd_soc_hdmi_codec
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This eliminates Bluetooth, DRM, and sound support. While it is tempting to disable the &lt;code&gt;vc4&lt;/code&gt; module as well: Don&amp;rsquo;t. I tried, and it &lt;em&gt;somewhat&lt;/em&gt; worked, but led to issues with the USB Ethernet adapters causing hanging kernel threads when they were under heavy load. As far as I can tell, the remaining modules that are loaded at boot time are absolutely required.&lt;/p&gt;
&lt;p&gt;With these modifications in place, the fully functional system now requires 83 Megabytes of RAM! This leaves me with plenty of space for further additions, such as an rsync server, a caching DNS proxy, and some caching for nginx.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2025/07/bts3-removing-unwanted-modules-for-headless-pi-zero/pi-zero-ram-usage_featured.webp&#34; alt=&#34;System, SSH, nginx &amp;amp; Anubis: All in 83 MB!&#34; title=&#34;System, SSH, nginx &amp;amp; Anubis: All in 83 MB&#34;&gt;
&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Saving the Masters of the Elements From Getting Lost to Time: Part 2</title>
      <link>https://felsqualle.com/posts/2025/07/saving-the-masters-of-the-elements-part-2/</link>
      <pubDate>Sat, 05 Jul 2025 18:00:00 +0200</pubDate>
      
      <guid>https://felsqualle.com/posts/2025/07/saving-the-masters-of-the-elements-part-2/</guid>
      <description>&lt;p&gt;In Macromedia Director, everything is related to cinematography and theaters. A Director project consists of individual movies, timed and coordinated by the score. The canvas where you place all the objects is the stage; objects and scripts compose the cast, and each object and script is a cast member.&lt;/p&gt;
&lt;p&gt;Then, there&amp;rsquo;s the Projector, the executable file that plays all movies in the defined order. And just like a real movie projector shines light through the film, we have to reverse this process: Splitting the beam of light to reveal the real source.&lt;/p&gt;
&lt;h2 id=&#34;introducing-projectorrays&#34;&gt;Introducing ProjectorRays&lt;/h2&gt;
&lt;p&gt;As briefly mentioned in my &lt;a href=&#34;https://felsqualle.com/posts/2025/05/saving-the-masters-of-the-elements-part-1/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;previous article&lt;/a&gt;
, more or less complex Director movies contain scripts written in a small BASIC (and later JavaScript)-like language, called Lingo. The (human-readable) Lingo code is then compiled into a (machine-readable) bytecode. To protect their software and intellectual property from reverse-engineering efforts, Macromedia Director offers a feature to &amp;ldquo;protect&amp;rdquo; a movie. The protection consists of stripping down the plain text Lingo sources, leaving only the compiled parts intact, and on top of that, adding a checksum algorithm that prevents any tampering with the Director movie internals.&lt;/p&gt;
&lt;p&gt;The protected movies get special file extensions, &lt;code&gt;.DXR&lt;/code&gt; or &lt;code&gt;.CXT&lt;/code&gt;, and are impossible to be opened with the Macromedia Director Authoring system.&lt;/p&gt;
&lt;p&gt;Thankfully, there&amp;rsquo;s a project called &lt;a href=&#34;https://github.com/ProjectorRays/ProjectorRays&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;ProjectorRays&lt;/a&gt;
, which solves this problem. ProjectorRays takes the protected &lt;code&gt;.DXR&lt;/code&gt; and &lt;code&gt;.CXT&lt;/code&gt; files, and decompiles them to their editable &lt;code&gt;.DIR&lt;/code&gt; and &lt;code&gt;.CST&lt;/code&gt; counterparts. Except for the original Dutch release of the game, &lt;em&gt;Masters of the Elements&lt;/em&gt; uses this kind of protection as well.&lt;/p&gt;
&lt;p&gt;After downloading the latest version from the &lt;a href=&#34;https://github.com/ProjectorRays/ProjectorRays/releases&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;release page&lt;/a&gt;
, we can start feeding the encrypted files to ProjectorRays itself.&lt;/p&gt;
&lt;p&gt;For Windows, ProjectorRays ships as a single executable file, so we can run all &lt;code&gt;.DXR&lt;/code&gt; and &lt;code&gt;.CXT&lt;/code&gt; files in one go without having to install any additional dependencies.&lt;/p&gt;
&lt;p&gt;Starting from the directory containing our working copy of the game files, we have to open a PowerShell terminal and pass all &lt;code&gt;.DXR&lt;/code&gt; and &lt;code&gt;.CXT&lt;/code&gt; files to ProjectorRays. While we&amp;rsquo;re at it, I highly recommend deleting the .DXR and .CXT files after the decompilation step&amp;mdash;I noticed that the Director software gets really confused if both variants are present.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-Powershell&#34; data-lang=&#34;Powershell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;1&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#1&#34;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;Get-ChildItem -Recurse -Filter &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#39;*.dxr&amp;#39;&lt;/span&gt; | ForEach-Object { X:\Path\To\projectorrays.&lt;span style=&#34;color:#79c0ff&#34;&gt;exe&lt;/span&gt; decompile &lt;span style=&#34;color:#79c0ff&#34;&gt;$_&lt;/span&gt;.&lt;span style=&#34;color:#79c0ff&#34;&gt;FullName&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;2&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#2&#34;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;Get-ChildItem -Recurse -Filter &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#39;*.cxt&amp;#39;&lt;/span&gt; | ForEach-Object { X:\Path\To\projectorrays.&lt;span style=&#34;color:#79c0ff&#34;&gt;exe&lt;/span&gt; decompile &lt;span style=&#34;color:#79c0ff&#34;&gt;$_&lt;/span&gt;.&lt;span style=&#34;color:#79c0ff&#34;&gt;FullName&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;3&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#3&#34;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;Get-ChildItem -Recurse -Filter &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#39;*.dxr&amp;#39;&lt;/span&gt; | ForEach-Object { rm &lt;span style=&#34;color:#79c0ff&#34;&gt;$_&lt;/span&gt;.&lt;span style=&#34;color:#79c0ff&#34;&gt;FullName&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;4&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#4&#34;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;Get-ChildItem -Recurse -Filter &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#39;*.cxt&amp;#39;&lt;/span&gt; | ForEach-Object { rm &lt;span style=&#34;color:#79c0ff&#34;&gt;$_&lt;/span&gt;.&lt;span style=&#34;color:#79c0ff&#34;&gt;FullName&lt;/span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&#34;the-debugging-begins&#34;&gt;The debugging begins&lt;/h2&gt;
&lt;p&gt;After decompiling the Director files and deleting the original, protected files, we are ready to open the starting movie in Macromedia Director. The German variant I&amp;rsquo;m currently working with was built using Macromedia Director 6.0.2. To avoid any compatibility issues, I&amp;rsquo;m using this exact version of the authoring software.&lt;/p&gt;
&lt;p&gt;And indeed, this is the correct file.&lt;/p&gt;
&lt;p&gt;As soon as I hit the &amp;ldquo;Play&amp;rdquo; button in Director, the movie starts to play back, and eventually confronts me with the same error message. Well, not exactly the same, as this time, we get the option to start debugging!&lt;/p&gt;
&lt;video class=&#34;video-shortcode&#34; preload=&#34;auto&#34; poster=&#34;melements-de-intro-director-poster.webp&#34; controls width=&#34;100%&#34;&gt;
    &lt;source src=&#34;https://felsqualle.com/posts/2025/07/saving-the-masters-of-the-elements-part-2/melements-de-intro-director.mp4&#34; type=&#34;video/mp4&#34;&gt;
    There should have been a video here but your browser does not seem
    to support it.
&lt;/video&gt;

&lt;p&gt;Clicking the &amp;ldquo;Script&amp;rdquo; button takes us right to the point where everything starts to fail, and unsurprisingly, the problematic script is called &lt;code&gt;Cursor Script&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2025/07/saving-the-masters-of-the-elements-part-2/melements-de-cursor-script.webp&#34; alt=&#34;Movie Script 8: Cursor Script&#34; title=&#34;Movie Script 8: Cursor Script&#34;&gt;
&lt;/p&gt;
&lt;h2 id=&#34;lingo-lingo&#34;&gt;Lingo? Lingo!&lt;/h2&gt;
&lt;p&gt;As I mentioned in my previous article, Director is using a scripting language called Lingo. When I first looked at it, it thought it was very readable, even without any prior experience with it.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s break it down, shall we?&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;on definePlatform
  global platform
  if the machineType = 256 then
    set platform to #windows
  else
    set platform to #macintosh
  end if
end
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The script begins by checking whether we are running on Windows or Macintosh. This check is important because we can&amp;rsquo;t load a Windows Xtra file or XObject on a Macintosh and vice versa.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;on initCursorObject
  global myMouse, platform, objectPath
  if objectp(myMouse) then
    myMouse(mdispose)
  end if
  if platform = #windows then
    openXLib(objectPath &amp;amp; &amp;#34;Putcurs.dll&amp;#34;)
    set myMouse to Putcurs(mnew)
  else
    openXLib(objectPath &amp;amp; &amp;#34;MoveMous.XOB&amp;#34;)
    set myMouse to MoveMouse(mnew)
  end if
end

on exitCursorObject
  global myMouse, platform, objectPath
  if objectp(myMouse) then
    myMouse(mdispose)
  end if
  if platform = #windows then
    closeXLib(objectPath &amp;amp; &amp;#34;Putcurs.dll&amp;#34;)
  else
    closeXLib(objectPath &amp;amp; &amp;#34;MoveMous.XOB&amp;#34;)
  end if
end
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Every time we want to use the &lt;code&gt;myMouse&lt;/code&gt; object, we have to load (the broken) &lt;code&gt;Putcurs.dll&lt;/code&gt; file on Windows or &lt;code&gt;MoveMous.XOB&lt;/code&gt;, which is responsible for handling the cursor-related functions on the Macintosh. Upon initialization of &lt;code&gt;myMouse&lt;/code&gt;, we load the XObject with &lt;code&gt;openXLib&lt;/code&gt;, and when we don&amp;rsquo;t need the object anymore, we unload the XObject by using &lt;code&gt;closeXLib&lt;/code&gt;.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;on moveTheCursor X, y
  global myMouse, platform, offSetX, offSetY
  if not objectp(myMouse) then
    return 
  end if
  if platform = #windows then
    set offSetX to the stageLeft + (4 * (the runMode = &amp;#34;Author&amp;#34;))
    set offSetY to the stageTop + (42 * (the runMode = &amp;#34;Author&amp;#34;))
    myMouse(mSet, integer(X + offSetX), integer(y + offSetY))
  else
    myMouse(mSetMouseLoc, integer(X), integer(y))
  end if
  set a to the mouseH
end
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Calling &lt;code&gt;moveTheCursor&lt;/code&gt; uses the function &lt;code&gt;mSet&lt;/code&gt; exported by &lt;code&gt;Putcurs.dll&lt;/code&gt;. The values &lt;code&gt;X&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; refer to screen coordinates and are then passed to &lt;code&gt;mSet&lt;/code&gt;. We don&amp;rsquo;t need to worry about &lt;code&gt;mSetMouseLoc&lt;/code&gt; though, because this is only used by the Macintosh version we are not working on.&lt;/p&gt;
&lt;h2 id=&#34;bare-minimum&#34;&gt;Bare minimum&lt;/h2&gt;
&lt;p&gt;We already know that &lt;code&gt;Putcurs.dll&lt;/code&gt; is broken beyond repair, so if we want to do any further work about it, we first need to check what it does inside the game itself.&lt;/p&gt;
&lt;p&gt;Looking at the Lingo code, we know that it controls the position of the mouse cursor, but not how the game uses it. And surely, the game &lt;em&gt;will&lt;/em&gt; use it somehow, because otherwise, there would be no reason to include it in the first place.&lt;/p&gt;
&lt;p&gt;To solve this mystery, I modified the Lingo script and removed all parts that are not absolutely necessary. All that&amp;rsquo;s left over are some stubs for the functions themselves, so Lingo won&amp;rsquo;t trip over when trying to call them.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;on definePlatform
  global platform
  if the machineType = 256 then
    set platform to #windows
  else
    set platform to #macintosh
  end if
end

on initCursorObject
end

on exitCursorObject
end

on moveTheCursor X, y
end
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I saved the modified file &lt;code&gt;global/scripts.cst&lt;/code&gt;, and started the game, this time using the original game executable.&lt;/p&gt;
&lt;p&gt;The moment of truth, the moment we all waited for.&lt;/p&gt;
&lt;video class=&#34;video-shortcode&#34; preload=&#34;auto&#34; poster=&#34;melements-de-cursor-glitch-poster.webp&#34; controls width=&#34;100%&#34;&gt;
    &lt;source src=&#34;https://felsqualle.com/posts/2025/07/saving-the-masters-of-the-elements-part-2/melements-de-cursor-glitch.mp4&#34; type=&#34;video/mp4&#34;&gt;
    There should have been a video here but your browser does not seem
    to support it.
&lt;/video&gt;

&lt;p&gt;As expected, the Tivola intro runs, but this time, I get past the error message, and the next movie (called &lt;code&gt;INTRO.DXR/.DIR&lt;/code&gt;) is loaded. I kept the dialog with the mole in the video to check if playing back the audio samples works correctly, because the game uses some legacy DirectX components that are thankfully still included in Windows 11.&lt;/p&gt;
&lt;p&gt;The conversation with the mole works just fine, and so does the transition to the Room of Gravity. And here is where the first quirks are visible.&lt;/p&gt;
&lt;h2 id=&#34;so-close-yet-so-far&#34;&gt;So close, yet so far&lt;/h2&gt;
&lt;p&gt;When I try to turn the ring to the left (instead of the right), the cursor also jumps to the left. In the original game, the mouse cursor stays at the same position. This effect is also visible when I try to manipulate the handle controlling the crane in the first room.&lt;/p&gt;
&lt;p&gt;Purely cosmetic at this point, so it&amp;rsquo;s not a deal breaker.&lt;/p&gt;
&lt;p&gt;But a couple of seconds later, in the Room of Warmth, the game breaks. Here, you are supposed to light a match by striking it along the priming surface of the matchbox. You control the mouse cursor in two dimensions, vertically and horizontally. Horizontally, it works without problems, but vertically, it is impossible; the mouse cursor will always slowly creep up or down. The same problem also happens when trying to control the pan; there is no way to control vertical movement. Not included in the video is the second room with an unsolvable puzzle: The Room of Electricity, where you won&amp;rsquo;t be able to solve the maze due to a lack of control over the mouse cursor.&lt;/p&gt;
&lt;p&gt;To summarize, every time the game tries to perform a drag-and-drop movement, it will break. With a working &lt;code&gt;Putcurs.dll&lt;/code&gt; implementation, the game continuously resets the cursor position to the &amp;ldquo;real&amp;rdquo; starting location when it encounters said drag-and-drop operations. And without it, it cannot be completed.&lt;/p&gt;
&lt;p&gt;Somehow, we need to find a way to replicate the features provided by &lt;code&gt;Putcurs.dll&lt;/code&gt;; there&amp;rsquo;s no way around it. How, you might ask? Well, as I mentioned, the Macromedia Director community was very active back in the day&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;a href=&#34;https://felsqualle.com/posts/2025/07/saving-the-masters-of-the-elements-part-3/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&amp;hellip;continue reading Part 3 of this series&amp;hellip;&lt;/a&gt;
&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Saving the Masters of the Elements From Getting Lost to Time: Part 1</title>
      <link>https://felsqualle.com/posts/2025/05/saving-the-masters-of-the-elements-part-1/</link>
      <pubDate>Wed, 14 May 2025 18:00:00 +0100</pubDate>
      
      <guid>https://felsqualle.com/posts/2025/05/saving-the-masters-of-the-elements-part-1/</guid>
      <description>&lt;p&gt;&lt;em&gt;Masters of the Elements&lt;/em&gt; (or &lt;em&gt;Meister Zufall und die Herrscher der Elemente&lt;/em&gt; in Germany) is an educational adventure video game developed by the Dutch studio &lt;a href=&#34;https://ijsfontein.nl/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;IJsfontein Interactive Media&lt;/a&gt;
, initially released in late 1997 for Macintosh and Microsoft Windows.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t remember when I got the game; it was most likely Christmas 1999 or 2000. What I do remember, though, is that I spent many hours wandering around the game and trying to solve the puzzles.&lt;/p&gt;
&lt;p&gt;And I remember the day the game would stop working forever: The day I updated my PC to Windows 2000.&lt;/p&gt;
&lt;p&gt;The game will not work on anything newer than Windows 95, 98, or ME. No matter which compatibility mode you try, as soon as you try to launch the game on Windows 2000, it will fail with a very non-descriptive error message.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2025/05/saving-the-masters-of-the-elements-part-1/melements-putcurs-error-featured.webp&#34; alt=&#34;Script error: Handler not defined&#34; title=&#34;Script error: Handler not defined&#34;&gt;
&lt;/p&gt;
&lt;p&gt;When you click on &amp;ldquo;Stop&amp;rdquo;, the game immediately quits. When you try &amp;ldquo;Continue,&amp;rdquo; the game sits there forever and eventually crashes.&lt;/p&gt;
&lt;p&gt;For many years, I have not found a solution to this problem. Yes, there was a patch for running the game on Windows XP, but it was so hard to find that I got it as late as the year 2023. Furthermore, it only works with the version released in Belgium and introduces issues in all other releases.&lt;/p&gt;
&lt;h2 id=&#34;hello-director&#34;&gt;Hello, Director!&lt;/h2&gt;
&lt;p&gt;Luckily for me, the game was created with &lt;a href=&#34;https://en.wikipedia.org/wiki/Adobe_Director&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Macromedia Director&lt;/a&gt;
, a multimedia authoring suite developed between the mid-90s and mid-2000s. If you played any educational title during this period, there&amp;rsquo;s a high chance it used Director as well—thousands of applications and games are known to use it. In case you have never heard of Director, I&amp;rsquo;m sure you have heard about Shockwave&amp;mdash;yes, that&amp;rsquo;s the web variant of Director applications.&lt;/p&gt;
&lt;p&gt;The ScummVM development team is currently working on support for Director applications, so I know some very knowledgeable people who are incredibly familiar with it.&lt;/p&gt;
&lt;p&gt;Macromedia Director started as a tool for creating multimedia presentations and interactive applications, and it became popular thanks to its relative ease of use. Since it features a complete scripting language, Lingo, it can also be used for advanced games.&lt;/p&gt;
&lt;p&gt;It also includes the concept of &lt;em&gt;XObjects&lt;/em&gt; and &lt;em&gt;Xtras&lt;/em&gt;, which are third-party add-ons that enhance Director&amp;rsquo;s functionality even more. &lt;em&gt;XObjects&lt;/em&gt; were the initial variant of these add-ons, and evolved to &lt;em&gt;Xtras&lt;/em&gt; starting with Director 5.0. Thanks to backward compatibility, Director 6.0 supports &lt;em&gt;XObjects&lt;/em&gt; as well.&lt;/p&gt;
&lt;h2 id=&#34;the-investigation-begins&#34;&gt;The investigation begins&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s look at the error message: &amp;ldquo;Script error: Handler not defined, #Putcurs&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;The error message tells us that a movie or Lingo script tries to call a function called &lt;code&gt;Putcurs&lt;/code&gt;, but fails to find it. Since &lt;code&gt;Putcurs&lt;/code&gt; is not a command built into Director&amp;rsquo;s language set itself, it is very likely that one of the XObjects or Xtras is causing the problem.&lt;/p&gt;
&lt;p&gt;Conveniently, Director applications usually have a directory called &lt;code&gt;Xtras&lt;/code&gt; which include both the XObjects and the Xtras, and &lt;em&gt;Masters of the Elements&lt;/em&gt; is no exception:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;...
-r-xr-xr-x 1 fabulous None 273K Jan 29  1997 PMATIC.X32
-r-xr-xr-x 1 fabulous None 4.5K Aug 31  1993 PUTCURS.DLL
-r-xr-xr-x 1 fabulous None 112K May 11  1995 SOUNDIMP.X32
...
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There it is, a suspiciously small DLL file. Looking at the string table of the DLL reveals that it is indeed an XObject, and a very old one as well.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;--Put Cursor XObject. 31 ago 95. Mauricio Piacentini Tabuleiro da Baiana Multimedia
putCurs
I      mNew                --Creates a new instance of the XObject
X      mDispose            --Disposes of XObject instance
III    mSet, Xpos, Ypos    --Set the cursor to a new position
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Why does it work on Windows 95/98/ME, but not on Windows 2000, XP, or anything else?&lt;/p&gt;
&lt;h2 id=&#34;windows-backward-compatiblity-is-not-always-amazing&#34;&gt;Windows backward compatiblity is not always amazing&lt;/h2&gt;
&lt;p&gt;A quick check with &lt;code&gt;file&lt;/code&gt; reveals that it is a 16-bit DLL, written with Windows 3.1 in mind. Remember, the game was released in 1997 using Director 6.0, so this is an odd choice.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;PUTCURS.DLL: MS-DOS executable, NE version 5 for MS Windows 3.10 (DLL)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Director 6.0 is a 32-bit application, and now we know it needs to include the 16-bit DLL file to access the functions exported by said DLL. And that&amp;rsquo;s the problem: This is impossible on the Windows NT family.&lt;/p&gt;
&lt;p&gt;Windows generally can&amp;rsquo;t mix 32-bit and 16-bit code in the same process. Instead, it relies on a method called &amp;ldquo;thunking&amp;rdquo;. There are two types of thunks: &lt;a href=&#34;https://ftp.zx.net.nz/pub/archive/ftp.microsoft.com/MISC/KB/en-us/125/710.HTM&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Generic thunks and flat thunks&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;Generic thunks support loading a 32-bit DLL from within a 16-bit application, and they are supported in Windows 9x and the NT series, including Windows 2000. Generic thunks are a relic from transitioning the 16-bit DOS-based world to modern Win32: Developers could update their libraries &lt;em&gt;first&lt;/em&gt;, and then adapt their applications accordingly.&lt;/p&gt;
&lt;p&gt;The other type of thunk is called a &amp;ldquo;flat thunk&amp;rdquo;, and it works in both directions: A 32-bit application can call 16-bit code, and a 16-bit application can call 32-bit code. And there is one catch: Flat thunks only work on Windows 9x.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s why it breaks, and that&amp;rsquo;s why it will never work again. Unless&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;a href=&#34;https://felsqualle.com/posts/2025/07/saving-the-masters-of-the-elements-part-2/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&amp;hellip;continue reading Part 2 of this series&amp;hellip;&lt;/a&gt;
&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>The Day Anubis Saved Our Websites From a DDoS Attack</title>
      <link>https://felsqualle.com/posts/2025/05/anubis-saved-our-websites-from-a-ddos-attack/</link>
      <pubDate>Thu, 01 May 2025 14:00:00 +0100</pubDate>
      
      <guid>https://felsqualle.com/posts/2025/05/anubis-saved-our-websites-from-a-ddos-attack/</guid>
      <description>&lt;p&gt;One part of my work for the ScummVM project is helping to keep the server infrastructure up and running, including our primary server, which hosts our website, wiki, forums, and some internal applications.&lt;/p&gt;
&lt;p&gt;About three weeks ago, I started receiving monitoring notifications indicating an increased load on the MariaDB server. This in itself is nothing too unusual. It usually means nothing but a sudden influx of new visitors, and in most cases, it is just a link being shared somewhere or a single IP trying to annoy us.&lt;/p&gt;
&lt;p&gt;The notifications popped up and disappeared as quickly as they appeared. I started to look into the log files of our web server, and I didn&amp;rsquo;t notice anything too unusual, maybe a bit more background noise. This went on for a couple of days without seriously impacting our server or accessibility&amp;ndash;it was a tad slower than usual.&lt;/p&gt;
&lt;p&gt;And then the website went down.&lt;/p&gt;
&lt;p&gt;We use a stack consisting of Apache2, PHP-FPM, and MariaDB to host the web applications. The server logs revealed that everything was saturated. Apache2 refused to accept new connections, the PHP-FPM pools were completely filled, and MariaDB also had no connections left.&lt;/p&gt;
&lt;p&gt;Now, it was time to find out what was going on. Hoping that it was just one single IP trying to annoy us, I opened the access log of the day and was greeted by this:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;127.0.0.1 - - [24/Apr/2025:23:42:29 +0000] &amp;#34;GET /index.php?days=30&amp;amp;from=20250417123108&amp;amp;hidemyself=1&amp;amp;limit=500&amp;amp;target=Lure_of_the_Temptress&amp;amp;title=Special%3ARecentChangesLinked HTTP/1.1&amp;#34; 200 6366 &amp;#34;-&amp;#34; &amp;#34;Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.2 (KHTML, like Gecko) Chrome/16.0.843.0 Safari/534.2&amp;#34;
127.0.0.1 - - [24/Apr/2025:23:42:29 +0000] &amp;#34;GET /index.php?from=20250417205327&amp;amp;hidemyself=0&amp;amp;limit=100&amp;amp;target=California_Pacific_Computer_Company&amp;amp;title=Special%3ARecentChangesLinked HTTP/1.1&amp;#34; 200 6363 &amp;#34;-&amp;#34; &amp;#34;Mozilla/5.0 (compatible; MSIE 6.0; Windows NT 4.0; Trident/3.1)&amp;#34;
127.0.0.1 - - [24/Apr/2025:23:42:29 +0000] &amp;#34;GET /index.php?days=30&amp;amp;from=20250410022141&amp;amp;hidebots=0&amp;amp;hideliu=1&amp;amp;hideminor=1&amp;amp;target=The_Big_Red_Adventure&amp;amp;title=Special%3ARecentChangesLinked HTTP/1.1&amp;#34; 200 6368 &amp;#34;-&amp;#34; &amp;#34;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_5_3; rv:1.9.4.20) Gecko/8520-08-18 14:24:31.076782 Firefox/3.8&amp;#34;
127.0.0.1 - - [24/Apr/2025:23:42:29 +0000] &amp;#34;GET /index.php?days=1&amp;amp;from=20250424060651&amp;amp;fromFormatted=06%3A06%2C+24+April+2025&amp;amp;hideminor=1&amp;amp;limit=100&amp;amp;target=RAMA&amp;amp;title=Special%3ARecentChangesLinked HTTP/1.1&amp;#34; 200 6368 &amp;#34;-&amp;#34; &amp;#34;Mozilla/5.0 (X11; Linux i686; rv:1.9.7.20) Gecko/4195-09-07 16:38:05.879333 Firefox/3.8&amp;#34;
127.0.0.1 - - [24/Apr/2025:23:42:29 +0000] &amp;#34;GET /index.php?days=30&amp;amp;from=20250424183156&amp;amp;fromFormatted=18%3A31%2C+24+April+2025&amp;amp;hideminor=1&amp;amp;limit=250&amp;amp;target=AGOS%2FVersions&amp;amp;title=Special%3ARecentChangesLinked HTTP/1.1&amp;#34; 200 6367 &amp;#34;-&amp;#34; &amp;#34;Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.0 (KHTML, like Gecko) Chrome/39.0.887.0 Safari/534.0&amp;#34;
127.0.0.1 - - [24/Apr/2025:23:42:29 +0000] &amp;#34;GET /index.php?days=30&amp;amp;from=20250411043805&amp;amp;hidebots=0&amp;amp;target=OpenTasks%2FEngine%2FImprove_WME&amp;amp;title=Special%3ARecentChangesLinked HTTP/1.1&amp;#34; 200 6367 &amp;#34;-&amp;#34; &amp;#34;Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; rv:1.9.3.20) Gecko/9958-03-18 16:15:48.117981 Firefox/14.0&amp;#34;
127.0.0.1 - - [24/Apr/2025:23:42:29 +0000] &amp;#34;GET /index.php?days=30&amp;amp;from=20250411042538&amp;amp;hidebots=0&amp;amp;hidemyself=1&amp;amp;limit=250&amp;amp;target=Compiling_ScummVM%2FPlayStation_Portable&amp;amp;title=Special%3ARecentChangesLinked HTTP/1.1&amp;#34; 200 6363 &amp;#34;-&amp;#34; &amp;#34;Opera/9.13.(X11; Linux i686; ce-RU) Presto/2.9.173 Version/11.00&amp;#34;
127.0.0.1 - - [24/Apr/2025:23:42:29 +0000] &amp;#34;GET /api.php?action=feedrecentchanges&amp;amp;days=14&amp;amp;feedformat=atom&amp;amp;from=20250405110953&amp;amp;hidebots=1&amp;amp;hidemyself=1&amp;amp;limit=50&amp;amp;target=Summer_of_Code%2FGSoC2010&amp;amp;urlversion=1 HTTP/1.1&amp;#34; 200 6364 &amp;#34;-&amp;#34; &amp;#34;Mozilla/5.0 (Windows NT 6.1) AppleWebKit/531.2 (KHTML, like Gecko) Chrome/24.0.862.0 Safari/531.2&amp;#34;
127.0.0.1 - - [24/Apr/2025:23:42:29 +0000] &amp;#34;GET /index.php?days=30&amp;amp;from=20250421165249&amp;amp;fromFormatted=16%3A52%2C+21+April+2025&amp;amp;limit=100&amp;amp;target=Template%3AMain_Contact&amp;amp;title=Special%3ARecentChangesLinked HTTP/1.1&amp;#34; 200 6366 &amp;#34;-&amp;#34; &amp;#34;Opera/9.61.(X11; Linux x86_64; st-ZA) Presto/2.9.160 Version/12.00&amp;#34;
127.0.0.1 - - [24/Apr/2025:23:42:29 +0000] &amp;#34;GET /index.php?returnto=Special%3ARecentChangesLinked&amp;amp;returntoquery=from%3D20250418162237%26fromFormatted%3D16%253A22%252C%2B18%2BApril%2B2025%26hidemyself%3D1%26target%3DAGIWiki%252FAl_Pond_-_On_Holiday&amp;amp;title=Special%3AUserLogin HTTP/1.1&amp;#34; 200 6365 &amp;#34;-&amp;#34; &amp;#34;Mozilla/5.0 (compatible; MSIE 7.0; Windows 98; Win 9x 4.90; Trident/3.1)&amp;#34;
127.0.0.1 - - [24/Apr/2025:23:42:29 +0000] &amp;#34;GET /index.php?days=30&amp;amp;from=20250417091241&amp;amp;hidebots=1&amp;amp;limit=250&amp;amp;target=Summer_of_Code%2FApplication%2F2007&amp;amp;title=Special%3ARecentChangesLinked HTTP/1.1&amp;#34; 200 6366 &amp;#34;-&amp;#34; &amp;#34;Mozilla/5.0 (iPod; U; CPU iPhone OS 4_1 like Mac OS X; nr-ZA) AppleWebKit/535.26.3 (KHTML, like Gecko) Version/3.0.5 Mobile/8B114 Safari/6535.26.3&amp;#34;
127.0.0.1 - - [24/Apr/2025:23:42:29 +0000] &amp;#34;GET /api.php?action=webapp-manifest HTTP/2.0&amp;#34; 200 2102 &amp;#34;https://wiki.scummvm.org/index.php?title=Hopkins_FBI&amp;#34; &amp;#34;Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Mobile Safari/537.36&amp;#34;
127.0.0.1 - - [24/Apr/2025:23:42:29 +0000] &amp;#34;GET /index.php?days=30&amp;amp;from=20250417023112&amp;amp;hidebots=0&amp;amp;hideminor=1&amp;amp;hidemyself=1&amp;amp;limit=250&amp;amp;target=AGIWiki%2FSpecial_flags&amp;amp;title=Special%3ARecentChangesLinked HTTP/1.1&amp;#34; 200 6367 &amp;#34;-&amp;#34; &amp;#34;Mozilla/5.0 (compatible; MSIE 7.0; Windows 98; Trident/3.1)&amp;#34;
127.0.0.1 - - [24/Apr/2025:23:42:29 +0000] &amp;#34;GET /index.php?days=30&amp;amp;from=20250416060403&amp;amp;hideanons=1&amp;amp;limit=100&amp;amp;target=Summer_of_Code%2FApplication%2F2007&amp;amp;title=Special%3ARecentChangesLinked HTTP/1.1&amp;#34; 200 6367 &amp;#34;-&amp;#34; &amp;#34;Mozilla/5.0 (Linux; Android 4.3) AppleWebKit/536.0 (KHTML, like Gecko) Chrome/51.0.880.0 Safari/536.0&amp;#34;
127.0.0.1 - - [24/Apr/2025:23:42:30 +0000] &amp;#34;GET /index.php?days=1&amp;amp;hidebots=0&amp;amp;hideminor=1&amp;amp;hidemyself=0&amp;amp;limit=250&amp;amp;mobileaction=toggle_view_mobile&amp;amp;target=HOWTO-Tips_And_Tricks&amp;amp;title=Special%3ARecentChangesLinked HTTP/1.1&amp;#34; 200 6366 &amp;#34;-&amp;#34; &amp;#34;Mozilla/5.0 (Android 4.4.3; Mobile; rv:58.0) Gecko/58.0 Firefox/58.0&amp;#34;
127.0.0.1 - - [24/Apr/2025:23:42:30 +0000] &amp;#34;GET /index.php?days=30&amp;amp;from=20250415120719&amp;amp;limit=250&amp;amp;target=Time_Zone&amp;amp;title=Special%3ARecentChangesLinked HTTP/1.1&amp;#34; 200 6366 &amp;#34;-&amp;#34; &amp;#34;Mozilla/5.0 (iPad; CPU iPad OS 1_1_5 like Mac OS X) AppleWebKit/532.1 (KHTML, like Gecko) FxiOS/12.3t5461.0 Mobile/69A052 Safari/532.1&amp;#34;
127.0.0.1 - - [24/Apr/2025:23:42:30 +0000] &amp;#34;GET /index.php?title=SCI/Testing&amp;amp;direction=next&amp;amp;oldid=14195 HTTP/1.1&amp;#34; 200 6364 &amp;#34;-&amp;#34; &amp;#34;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5; rv:123.0esr) Gecko/20100101 Firefox/123.0esr&amp;#34;
127.0.0.1 - - [24/Apr/2025:23:42:30 +0000] &amp;#34;GET /index.php?days=14&amp;amp;from=20250417034946&amp;amp;hideliu=1&amp;amp;hideminor=1&amp;amp;target=Nippon_Safes_Inc.&amp;amp;title=Special%3ARecentChangesLinked HTTP/1.1&amp;#34; 200 6364 &amp;#34;-&amp;#34; &amp;#34;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1; rv:1.9.6.20) Gecko/9899-07-01 03:29:48.393829 Firefox/3.8&amp;#34;
127.0.0.1 - - [24/Apr/2025:23:42:30 +0000] &amp;#34;GET /index.php?returnto=Special%3ARecentChangesLinked&amp;amp;returntoquery=days%3D30%26from%3D20250410005945%26hidebots%3D1%26hideminor%3D1%26hidemyself%3D1%26target%3DUser%253ASpookypeanut&amp;amp;title=Special%3AUserLogin HTTP/1.1&amp;#34; 200 6367 &amp;#34;-&amp;#34; &amp;#34;Mozilla/5.0 (Windows; U; Windows 95) AppleWebKit/533.2.2 (KHTML, like Gecko) Version/4.1 Safari/533.2.2&amp;#34;
127.0.0.1 - - [24/Apr/2025:23:42:30 +0000] &amp;#34;GET /index.php?days=30&amp;amp;from=20250410094930&amp;amp;hidebots=1&amp;amp;hideminor=1&amp;amp;hidemyself=1&amp;amp;limit=100&amp;amp;target=Loom&amp;amp;title=Special%3ARecentChangesLinked HTTP/1.1&amp;#34; 200 6364 &amp;#34;-&amp;#34; &amp;#34;Mozilla/5.0 (iPhone; CPU iPhone OS 13_7 like Mac OS X) AppleWebKit/533.1 (KHTML, like Gecko) FxiOS/9.0k8480.0 Mobile/92A641 Safari/533.1&amp;#34;
127.0.0.1 - - [24/Apr/2025:23:42:30 +0000] &amp;#34;GET /index.php?days=30&amp;amp;from=20250425184120&amp;amp;fromFormatted=18%3A41%2C+25+April+2025&amp;amp;hideminor=1&amp;amp;hidemyself=1&amp;amp;target=Indiana_Jones_and_the_Fate_of_Atlantis&amp;amp;title=Special%3ARecentChangesLinked HTTP/1.1&amp;#34; 200 6365 &amp;#34;-&amp;#34; &amp;#34;Mozilla/5.0 (iPod; U; CPU iPhone OS 4_1 like Mac OS X; pl-PL) AppleWebKit/535.5.2 (KHTML, like Gecko) Version/4.0.5 Mobile/8B116 Safari/6535.5.2&amp;#34;
127.0.0.1 - - [24/Apr/2025:23:42:31 +0000] &amp;#34;GET /index.php?diff=39241&amp;amp;oldid=29636&amp;amp;mobileaction=toggle_view_desktop HTTP/2.0&amp;#34; 200 2104 &amp;#34;https://wiki.scummvm.org/index.php?diff=39241&amp;amp;oldid=29636&amp;amp;mobileaction=toggle_view_desktop&amp;#34; &amp;#34;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.3&amp;#34;
127.0.0.1 - - [24/Apr/2025:23:42:31 +0000] &amp;#34;GET /index.php?days=30&amp;amp;from=20250407050329&amp;amp;hideliu=1&amp;amp;hideminor=1&amp;amp;hidemyself=1&amp;amp;target=Summer_of_Code%2FGSoC_Ideas_2020&amp;amp;title=Special%3ARecentChangesLinked HTTP/1.1&amp;#34; 200 6367 &amp;#34;-&amp;#34; &amp;#34;Mozilla/5.0 (Android 2.2; Mobile; rv:51.0) Gecko/51.0 Firefox/51.0&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For privacy reasons, I replaced the real IPs with 127.0.0.1, but trust me, there were many IPs&amp;ndash;around 35.000, to be precise&amp;ndash;from residential networks all over the world. At this scale, it makes no sense to even consider blocking individual IPs, subnets, or entire networks. Due to the open nature of the project, geo-blocking isn&amp;rsquo;t an option either.&lt;/p&gt;
&lt;p&gt;The main problem is time. The URLs accessed in the attack are the most expensive ones the wiki offers since they heavily depend on the database and are highly dynamic, requiring some processing time in PHP. This is the worst-case scenario since it throws the server into a death spiral.&lt;/p&gt;
&lt;p&gt;First, the database starts to lag or even refuse new connections. This, combined with the steadily increasing server load, leads to slower PHP execution. Eventually, all resources in the PHP-FPM pools are used up, and since Apache2 doesn&amp;rsquo;t get a reply from PHP-FPM in time, it waits until it runs out of free connections. At this point, the website dies. Restarting the stack immediately solves the problem for a couple of minutes at best until the server starves again.&lt;/p&gt;
&lt;p&gt;To bring the website back up, I cranked up the configuration of our stack to insane values, risking that the server would eventually run out of memory.&lt;/p&gt;
&lt;p&gt;I needed a proper solution, something that takes the load away from the web application stack.&lt;/p&gt;
&lt;h2 id=&#34;hi-anubis&#34;&gt;Hi, Anubis!&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://anubis.techaro.lol/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Anubis&lt;/a&gt;
 is a program that checks incoming connections, processes them, and only forwards &amp;ldquo;good&amp;rdquo; connections to the web application. To do so, Anubis sits between the server or proxy responsible for accepting HTTP/HTTPS and the server that provides the application.&lt;/p&gt;
&lt;p&gt;Designed to protect websites from AI scraper bots, Anubis primarily focuses on parameters like the user agent sent with the request and looks for oddities in the connection. &amp;ldquo;Known good&amp;rdquo; and harmless clients are always accepted, and &amp;ldquo;Known bad&amp;rdquo; clients are always denied. In case the defaults are not working for your application, Anubis allows extensive configuration with customizable &lt;a href=&#34;https://anubis.techaro.lol/docs/admin/policies&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;bot policy definitions&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;And then, there&amp;rsquo;s the in-between, the part where the real magic happens. Many bots disguise themselves as standard browsers to circumvent filtering based on the user agent. So, if something claims to be a browser, it should behave like one, right? To verify this, Anubis presents a &lt;a href=&#34;https://anubis.techaro.lol/docs/design/why-proof-of-work/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;proof-of-work challenge&lt;/a&gt;
 that the browser needs to solve. If the challenge passes, it forwards the incoming request to the web application protected by Anubis; otherwise, the request is denied.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Solving&lt;/em&gt; the challenge&amp;ndash;which is valid for one week once passed&amp;ndash;takes a couple of seconds on the client side, occupying CPU time. &lt;em&gt;Checking&lt;/em&gt; if the browser solved the very fast on the server side, taking up virtually no resources.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2025/05/anubis-saved-our-websites-from-a-ddos-attack/anubis_in_action.webp&#34; alt=&#34;Anubis presenting the proof-of-work challenge&#34; title=&#34;Anubis presenting the proof-of-work challenge&#34;&gt;
&lt;/p&gt;
&lt;p&gt;As a regular user, all you&amp;rsquo;ll notice is a loading screen when accessing the website. As an attacker with stupid bots, you&amp;rsquo;ll never get through. As an attacker with clever bots, you&amp;rsquo;ll end up exhausting your own resources. As an AI company trying to scrape the website, you&amp;rsquo;ll quickly notice that CPU time can be expensive if used on a large scale.&lt;/p&gt;
&lt;p&gt;Long story short, deploying Anubis immediately solved our issues. In fact, you can see the exact time in our monitoring.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2025/05/anubis-saved-our-websites-from-a-ddos-attack/database_load.webp&#34; alt=&#34;Monitoring showing the drop in MariaDB usage after deploying Anubis&#34; title=&#34;Monitoring showing the drop in MariaDB usage after deploying Anubis&#34;&gt;
&lt;/p&gt;
&lt;p&gt;I didn&amp;rsquo;t get a single notification afterward. The server load has never been lower. The attack itself is still ongoing at the time of writing this article. To me, Anubis is not only a blocker for AI scrapers. Anubis is a DDoS protection.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Credits: Anubis is created by &lt;a href=&#34;https://techaro.lol/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Techaro&lt;/a&gt;
. The Anubis mascot is created by &lt;a href=&#34;https://bsky.app/profile/celphase.bsky.social&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;CELPHASE&lt;/a&gt;
&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Yes, You Can Still Run Internet Explorer in Windows 11</title>
      <link>https://felsqualle.com/posts/2025/01/you-can-still-run-internet-explorer-in-windows-11/</link>
      <pubDate>Thu, 23 Jan 2025 20:00:00 +0100</pubDate>
      
      <guid>https://felsqualle.com/posts/2025/01/you-can-still-run-internet-explorer-in-windows-11/</guid>
      <description>&lt;p&gt;In June 2022, Microsoft &lt;a href=&#34;https://learn.microsoft.com/en-us/lifecycle/products/internet-explorer-11&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;announced&lt;/a&gt;
 that Internet Explorer would be phased out. In February 2023, they deactivated it with a Windows 10 updated and encouraged users to switch to their Edge browser.&lt;/p&gt;
&lt;p&gt;If you ever tried to start Internet Explorer through &lt;code&gt;iexplore.exe&lt;/code&gt; after this date or on a fresh Windows 11 installation, Windows opens the Edge browser instead. If you want to use a web application that only supports Internet Explorer, Microsoft recommends using the compatibility mode in the Edge browser.&lt;/p&gt;
&lt;p&gt;But what if I tell you that &lt;em&gt;to this date&lt;/em&gt;, Internet Explorer is deeply embedded in Windows 11 and can be used without even having the Edge browser installed?&lt;/p&gt;
&lt;p&gt;Booting a new Windows 11 installation that has been modified by &lt;a href=&#34;https://github.com/ntdevlabs/tiny11builder&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;tiny11&lt;/a&gt;
 to remove all components of the Edge browser reveals the first problem: You can&amp;rsquo;t even access Edge to get to the compatibility mode. Instead, all you get is an error message that Internet Explorer 11 is no longer supported, and you should visit the Windows App Store and get a new browser instead.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2025/01/you-can-still-run-internet-explorer-in-windows-11/win11_iexplore_error_featured.webp&#34; alt=&#34;Error message indicating that Internet Explorer 11 is no longer supported and the user is prompted to search the Windows 11 App Store for a new browser&#34; title=&#34;Internet Explorer 11 is no longer supported&#34;&gt;
&lt;/p&gt;
&lt;p&gt;So&amp;mdash;what do we have to do to get the world&amp;rsquo;s once-most famous browser back?&lt;/p&gt;
&lt;p&gt;Create a new script (for example called &lt;code&gt;iexplore.ps1&lt;/code&gt;) with the following two lines of PowerShell code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-Powershell&#34; data-lang=&#34;Powershell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;1&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#1&#34;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;$iexplore&lt;/span&gt; = New-Object -ComObject &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;InternetExplorer.Application&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;2&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#2&#34;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;$iexplore&lt;/span&gt;.&lt;span style=&#34;color:#79c0ff&#34;&gt;Visible&lt;/span&gt; = &lt;span style=&#34;color:#79c0ff&#34;&gt;$true&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;That&amp;rsquo;s it!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;After setting the PowerShell &lt;a href=&#34;https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.security/set-executionpolicy?view=powershell-7.4&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;ExecutionPolicy&lt;/a&gt;
 to &lt;code&gt;Unrestricted&lt;/code&gt;, you can launch the new script.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2025/01/you-can-still-run-internet-explorer-in-windows-11/win11_iexplore_running.webp&#34; alt=&#34;Internet Explorer is running in Windows 11!&#34; title=&#34;Internet Explorer is running in Windows 11&#34;&gt;
&lt;/p&gt;
&lt;h2 id=&#34;at-this-point-you-might-wonder-how-this-is-possible&#34;&gt;At this point, you might wonder how this is possible.&lt;/h2&gt;
&lt;p&gt;The magic word is &lt;code&gt;ComObject&lt;/code&gt;, which refers to the &lt;a href=&#34;https://learn.microsoft.com/en-us/windows/win32/com/the-component-object-model&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Component Object Model&lt;/a&gt;
. Introduced with Windows 3.1 back in 1992, it is by no means a new technology.&lt;/p&gt;
&lt;p&gt;Luckily, COM also covers Internet-enabled components using ActiveX, which relies on Internet Explorer to run. By creating a new instance of the &lt;code&gt;InternetExplorer.Application&lt;/code&gt; object, we can still launch the complete program without any compatibility modes enabled. As I said&amp;mdash;to this day, Internet Explorer is still considered an OS component in Windows.&lt;/p&gt;
&lt;h2 id=&#34;how-long-will-it-last&#34;&gt;How long will it last?&lt;/h2&gt;
&lt;p&gt;Microsoft announced that the Internet Explorer compatibility of Microsoft Edge will be &lt;a href=&#34;https://learn.microsoft.com/en-us/lifecycle/faq/internet-explorer-microsoft-edge#what-if-my-enterprise-line-of-business--lob--application-has-a-dependency-on-a-version-of-internet-explorer-that-reached-end-of-support-&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;present through at least 2029&lt;/a&gt;
. And even after this date, there&amp;rsquo;s a high chance that using the COM as a neat &amp;ldquo;loader&amp;rdquo; will still work.&lt;/p&gt;
&lt;p&gt;Windows truly has exceptional backward compatibility.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>BTS#2: Fix Font Rendering for Hugo&#39;s Code Blocks in iOS</title>
      <link>https://felsqualle.com/posts/2025/01/bts2-fix-ios-font-rendering-for-code-blocks-in-hugo/</link>
      <pubDate>Mon, 20 Jan 2025 14:00:00 +0100</pubDate>
      
      <guid>https://felsqualle.com/posts/2025/01/bts2-fix-ios-font-rendering-for-code-blocks-in-hugo/</guid>
      <description>&lt;p&gt;For &lt;a href=&#34;https://felsqualle.com/posts/2025/01/bts1-submitting-entire-websites-to-archive-org/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;my previous article&lt;/a&gt;
, I added &lt;a href=&#34;https://gohugo.io/content-management/syntax-highlighting/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Syntax highlighting&lt;/a&gt;
 to my Hugo installation.&lt;/p&gt;
&lt;p&gt;Reviewing the page on my iPhone, I noticed something weird&amp;mdash;the font rendering for the code blocks was entirely off.&lt;/p&gt;
&lt;p&gt;Time to fix it!&lt;/p&gt;
&lt;p&gt;Hugo&amp;rsquo;s support for syntax highlighting provides an easy way to make your code snippets more readable. To add them to your page, you add the highlighting shortcode and describe the language in which your code is written.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;{{&amp;lt; highlight bash&amp;gt;}}
...
{{&amp;lt; / highlight &amp;gt;}}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Unfortunately, the rendering engine on iOS is not fully compatible with the CSS generated by the syntax highlighting. This leads to broken font sizes, resulting in code that is very hard to read.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2025/01/bts2-fix-ios-font-rendering-for-code-blocks-in-hugo/ios_code_rendering_broken_featured.webp&#34; alt=&#34;Broken font rendering in iOS&#34; &gt;
&lt;/p&gt;
&lt;p&gt;Thankfully, there is an easy fix.&lt;/p&gt;
&lt;p&gt;Applying the &lt;a href=&#34;https://github.com/adityatelange/hugo-PaperMod/issues/828#issuecomment-1171994855&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;following code snippet&lt;/a&gt;
 to your stylesheet mitigates the broken rendering of the code blocks:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;1&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#1&#34;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt; &lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;/* Fixes iOS font sizing anomaly */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;2&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#2&#34;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt; &lt;span style=&#34;color:#7ee787&#34;&gt;code&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;3&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#3&#34;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    text-size-adjust: &lt;span style=&#34;color:#a5d6ff&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#ff7b72&#34;&gt;%&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;4&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#4&#34;&gt;4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&#34;color:#79c0ff&#34;&gt;-ms-&lt;/span&gt;text-size-adjust: &lt;span style=&#34;color:#a5d6ff&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#ff7b72&#34;&gt;%&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;5&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#5&#34;&gt;5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&#34;color:#79c0ff&#34;&gt;-moz-&lt;/span&gt;text-size-adjust: &lt;span style=&#34;color:#a5d6ff&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#ff7b72&#34;&gt;%&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;6&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#6&#34;&gt;6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&#34;color:#79c0ff&#34;&gt;-webkit-&lt;/span&gt;text-size-adjust: &lt;span style=&#34;color:#a5d6ff&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#ff7b72&#34;&gt;%&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;7&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#7&#34;&gt;7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Besides adding the workaround, no additional changes to your Markdown files are required.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2025/01/bts2-fix-ios-font-rendering-for-code-blocks-in-hugo/ios_code_rendering_fixed.webp&#34; alt=&#34;Fixed font rendering in iOS&#34; &gt;
&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>BTS#1: Submitting Entire Websites to web.archive.org Through Sitemap Parsing</title>
      <link>https://felsqualle.com/posts/2025/01/bts1-submitting-entire-websites-to-archive-org/</link>
      <pubDate>Sun, 19 Jan 2025 22:00:00 +0100</pubDate>
      
      <guid>https://felsqualle.com/posts/2025/01/bts1-submitting-entire-websites-to-archive-org/</guid>
      <description>&lt;p&gt;This is the first episode of a new series: BTS, short for &amp;ldquo;Behind the Scenes&amp;rdquo;. In this series, I&amp;rsquo;m going through some scripts and techniques I use to build and maintain felsqualle.com.&lt;/p&gt;
&lt;p&gt;Over the weekend, I wrote a script that parses my entire website and submits all URLs to the Internet Archive&amp;rsquo;s &lt;a href=&#34;https://web.archive.org/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Wayback Machine&lt;/a&gt;
, including all outgoing links.&lt;/p&gt;
&lt;div class=&#34;notice info&#34; &gt;
&lt;p class=&#34;first notice-title&#34;&gt;&lt;span class=&#34;icon-notice baseline&#34;&gt;&lt;svg&gt;&lt;use href=&#34;#info-notice&#34;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Update 2025-01-21: I found out that the Wayback Machine has a limit of 200 submissions per day and IP address. In case your site is larger, you might want to limit your submissions to the latest changes of your website.&lt;/p&gt;&lt;/div&gt;

&lt;h2 id=&#34;why-do-i-care-about-the-archive&#34;&gt;Why do I care about the Archive&lt;/h2&gt;
&lt;p&gt;I have to admit that I was not always a massive fan of the Archive&amp;rsquo;s approach of keeping websites for all eternity&amp;mdash;in the end, I want to have the opportunity to remove content if I no longer identify with it, right?&lt;/p&gt;
&lt;p&gt;Well, &lt;em&gt;I can&lt;/em&gt;. A few years ago, I spotted a website in the Archive hosted on a domain I previously owned and let expire. Naturally, my content was there, then a gap of a couple of years, and suddenly, the new owner started to publish political stuff on the domain&amp;mdash;content I can not identify with.&lt;/p&gt;
&lt;p&gt;I contacted their support, and after providing evidence and the exact period where I owned the domain, they removed any references to the previously published content. We solved the situation within a couple of days, and working with their support staff was really great.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Then felsqualle.com happened.&lt;/strong&gt; Given the more obscure nature of the content presented here, finding reliable sources is increasingly challenging. There are multiple articles on this website that wouldn&amp;rsquo;t exist without the help of the Wayback Machine. One day, people will think about your website the same way.&lt;/p&gt;
&lt;h2 id=&#34;a-brief-introduction-to-sitemaps&#34;&gt;A brief introduction to sitemaps&lt;/h2&gt;
&lt;p&gt;In short, sitemaps represent your website&amp;rsquo;s table of contents. Following a &lt;a href=&#34;https://www.sitemaps.org/protocol.html&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;standardized format&lt;/a&gt;
, their primary purpose is to help web crawlers like search engines to find your content. In their most basic form, sitemaps only list all URLs on your website. Additionally, they can include information about a URL&amp;rsquo;s &amp;ldquo;weight&amp;rdquo; (or importance) and the date of the URL&amp;rsquo;s last modification.&lt;/p&gt;
&lt;p&gt;For example, this is the beginning of this website&amp;rsquo;s sitemap:
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-XML&#34; data-lang=&#34;XML&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;1&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#1&#34;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#8b949e;font-weight:bold;font-style:italic&#34;&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;utf-8&amp;#34; standalone=&amp;#34;yes&amp;#34;?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;2&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#2&#34;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#7ee787&#34;&gt;&amp;lt;urlset&lt;/span&gt; xmlns=&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;http://www.sitemaps.org/schemas/sitemap/0.9&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;3&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#3&#34;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  xmlns:xhtml=&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;http://www.w3.org/1999/xhtml&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#7ee787&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;4&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#4&#34;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&#34;color:#7ee787&#34;&gt;&amp;lt;url&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;5&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#5&#34;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&#34;color:#7ee787&#34;&gt;&amp;lt;loc&amp;gt;&lt;/span&gt;https://felsqualle.com/categories/&lt;span style=&#34;color:#7ee787&#34;&gt;&amp;lt;/loc&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;6&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#6&#34;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&#34;color:#7ee787&#34;&gt;&amp;lt;lastmod&amp;gt;&lt;/span&gt;2024-06-22T22:00:00+02:00&lt;span style=&#34;color:#7ee787&#34;&gt;&amp;lt;/lastmod&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;7&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#7&#34;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&#34;color:#7ee787&#34;&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;8&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#8&#34;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&#34;color:#7ee787&#34;&gt;&amp;lt;url&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;9&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#9&#34;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&#34;color:#7ee787&#34;&gt;&amp;lt;loc&amp;gt;&lt;/span&gt;https://felsqualle.com/posts/2024/06/new-installation-media-for-ms-dos-4/&lt;span style=&#34;color:#7ee787&#34;&gt;&amp;lt;/loc&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;10&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#10&#34;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&#34;color:#7ee787&#34;&gt;&amp;lt;lastmod&amp;gt;&lt;/span&gt;2024-06-22T22:00:00+02:00&lt;span style=&#34;color:#7ee787&#34;&gt;&amp;lt;/lastmod&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;11&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#11&#34;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&#34;color:#7ee787&#34;&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;12&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#12&#34;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&#34;color:#7ee787&#34;&gt;&amp;lt;url&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;13&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#13&#34;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&#34;color:#7ee787&#34;&gt;&amp;lt;loc&amp;gt;&lt;/span&gt;https://felsqualle.com/&lt;span style=&#34;color:#7ee787&#34;&gt;&amp;lt;/loc&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;14&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#14&#34;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;    &lt;span style=&#34;color:#7ee787&#34;&gt;&amp;lt;lastmod&amp;gt;&lt;/span&gt;2024-06-22T22:00:00+02:00&lt;span style=&#34;color:#7ee787&#34;&gt;&amp;lt;/lastmod&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;15&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#15&#34;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&#34;color:#7ee787&#34;&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;16&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#16&#34;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;The simple structure of the sitemap XML makes parsing relatively easy.&lt;/p&gt;
&lt;p&gt;In fact, it is so simple that I managed to use a single Bash script with &lt;code&gt;curl&lt;/code&gt; as the only dependency.&lt;/p&gt;
&lt;h2 id=&#34;the-script&#34;&gt;The script&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;1&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#1&#34;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#8b949e;font-weight:bold;font-style:italic&#34;&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;2&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#2&#34;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#8b949e;font-weight:bold;font-style:italic&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;3&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#3&#34;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# Auto-announce all pages and related links to archive.org by&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;4&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#4&#34;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# crawling the sitemap.xml rendered by Hugo, fetched locally&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;5&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#5&#34;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;SUBMISSION_URL&lt;/span&gt;&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;https://web.archive.org/save/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;6&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#6&#34;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;SOURCE_URL&lt;/span&gt;&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;https://felsqualle.com&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;7&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#7&#34;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;8&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#8&#34;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;try_count&lt;/span&gt;&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;9&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#9&#34;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;10&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#10&#34;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;announce_url&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;()&lt;/span&gt; &lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;11&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#11&#34;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	curl -s -o /dev/null -w &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;%{http_code}&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;SUBMISSION_URL&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;url_to_announce&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;12&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#12&#34;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;13&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#13&#34;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;14&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#14&#34;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;archive_url&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;()&lt;/span&gt; &lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;15&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#15&#34;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# Increment trial counter on each run since we don&amp;#39;t know about the result here&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;16&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#16&#34;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;((&lt;/span&gt;try_count++&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;17&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#17&#34;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;18&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#18&#34;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&#34;color:#ff7b72&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;[[&lt;/span&gt; &lt;span style=&#34;color:#ff7b72&#34;&gt;$(&lt;/span&gt;announce_url &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;url_to_announce&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff7b72&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#39;302&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;]]&lt;/span&gt;; &lt;span style=&#34;color:#ff7b72&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;19&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#19&#34;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		echo &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;Successfully archived \&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;url_to_announce&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;\&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;20&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#20&#34;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&#34;color:#79c0ff&#34;&gt;try_count&lt;/span&gt;&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;21&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#21&#34;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&#34;color:#ff7b72&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;22&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#22&#34;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		echo &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;Retrying to archive \&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;url_to_announce&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;\&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;23&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#23&#34;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;24&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#24&#34;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# Give the archive.org API some time to cool down...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;25&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#25&#34;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		sleep &lt;span style=&#34;color:#a5d6ff&#34;&gt;30&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;26&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#26&#34;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;27&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#27&#34;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# Retry announcing the same URL again until we reach the failure threshold&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;28&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#28&#34;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&#34;color:#ff7b72&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;[&lt;/span&gt; &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;$try_count&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt; -lt &lt;span style=&#34;color:#a5d6ff&#34;&gt;5&lt;/span&gt; &lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;[&lt;/span&gt; &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;$try_count&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt; -ne &lt;span style=&#34;color:#a5d6ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;]&lt;/span&gt;; &lt;span style=&#34;color:#ff7b72&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;29&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#29&#34;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			archive_url &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;url_to_announce&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;30&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#30&#34;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&#34;color:#ff7b72&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;31&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#31&#34;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			echo &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;Giving up on url \&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;url_to_announce&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;\&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;32&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#32&#34;&gt;32&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&#34;color:#79c0ff&#34;&gt;try_count&lt;/span&gt;&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;33&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#33&#34;&gt;33&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&#34;color:#ff7b72&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a5d6ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;34&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#34&#34;&gt;34&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&#34;color:#ff7b72&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;35&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#35&#34;&gt;35&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&#34;color:#ff7b72&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;36&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#36&#34;&gt;36&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;37&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#37&#34;&gt;37&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;38&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#38&#34;&gt;38&lt;/a&gt;&lt;/span&gt;&lt;span&gt;extract_external_urls&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;()&lt;/span&gt; &lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;39&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#39&#34;&gt;39&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	curl -s -f -L &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;url_to_announce&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt; | grep -Eo &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#39;&amp;#34;(http|https)://[a-zA-Z0-9#~.*,/!?=+&amp;amp;_%:-]*&amp;#34;&amp;#39;&lt;/span&gt; | grep -v &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;SOURCE_URL&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt; | sed &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#39;s/\&amp;#34;//g&amp;#39;&lt;/span&gt; | sort -u
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;40&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#40&#34;&gt;40&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;41&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#41&#34;&gt;41&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;42&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#42&#34;&gt;42&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# Starting the main loop, iterating through the&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;43&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#43&#34;&gt;43&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# entire content of sitemap.xml.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;44&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#44&#34;&gt;44&lt;/a&gt;&lt;/span&gt;&lt;span&gt;grep loc public/sitemap.xml | grep -v legal | grep -v privacy | sed &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#39;s/    &amp;lt;loc&amp;gt;//g&amp;#39;&lt;/span&gt; | sed &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#39;s/&amp;lt;\/loc&amp;gt;//g&amp;#39;&lt;/span&gt; | &lt;span style=&#34;color:#ff7b72&#34;&gt;while&lt;/span&gt; &lt;span style=&#34;color:#79c0ff&#34;&gt;IFS&lt;/span&gt;&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;=&lt;/span&gt; read -r url_to_announce; &lt;span style=&#34;color:#ff7b72&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;45&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#45&#34;&gt;45&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;46&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#46&#34;&gt;46&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# Start with building a list of all external URLs present on the current page&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;47&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#47&#34;&gt;47&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&#34;color:#79c0ff&#34;&gt;external_url_list&lt;/span&gt;&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff7b72&#34;&gt;$(&lt;/span&gt;extract_external_urls &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;url_to_announce&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff7b72&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;48&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#48&#34;&gt;48&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;49&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#49&#34;&gt;49&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# Send archive request for current (internal) URL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;50&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#50&#34;&gt;50&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	archive_url &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;url_to_announce&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;51&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#51&#34;&gt;51&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;52&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#52&#34;&gt;52&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# Iterate through any external URLs we found and send archive request&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;53&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#53&#34;&gt;53&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&#34;color:#ff7b72&#34;&gt;for&lt;/span&gt; url_to_announce in &lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;external_url_list&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;; &lt;span style=&#34;color:#ff7b72&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;54&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#54&#34;&gt;54&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		archive_url &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;url_to_announce&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;55&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#55&#34;&gt;55&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&#34;color:#ff7b72&#34;&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;56&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#56&#34;&gt;56&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#ff7b72&#34;&gt;done&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&#34;the-script--demystified&#34;&gt;The script &amp;mdash; demystified&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s break it down, shall we?&lt;/p&gt;
&lt;p&gt;First, we need to parse the sitemap.xml file (located at &lt;code&gt;public/sitemap.xml&lt;/code&gt;) and extract all URLs we can find in our main loop. Then, we call &lt;code&gt;extract_external_urls&lt;/code&gt;, and &lt;code&gt;archive_url&lt;/code&gt; for both the internal and the outgoing/external links.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;1&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#1&#34;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;grep loc public/sitemap.xml | grep -v legal | grep -v privacy | sed &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#39;s/    &amp;lt;loc&amp;gt;//g&amp;#39;&lt;/span&gt; | sed &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#39;s/&amp;lt;\/loc&amp;gt;//g&amp;#39;&lt;/span&gt; | &lt;span style=&#34;color:#ff7b72&#34;&gt;while&lt;/span&gt; &lt;span style=&#34;color:#79c0ff&#34;&gt;IFS&lt;/span&gt;&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;=&lt;/span&gt; read -r url_to_announce; &lt;span style=&#34;color:#ff7b72&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;2&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#2&#34;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# Start with building a list of all external URLs present on the current page&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;3&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#3&#34;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&#34;color:#79c0ff&#34;&gt;external_url_list&lt;/span&gt;&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff7b72&#34;&gt;$(&lt;/span&gt;extract_external_urls &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;url_to_announce&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff7b72&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;4&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#4&#34;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;5&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#5&#34;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# Send archive request for current (internal) URL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;6&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#6&#34;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	archive_url &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;url_to_announce&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;7&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#7&#34;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;8&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#8&#34;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# Iterate through any external URLs we found and send archive request&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;9&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#9&#34;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&#34;color:#ff7b72&#34;&gt;for&lt;/span&gt; url_to_announce in &lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;external_url_list&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;; &lt;span style=&#34;color:#ff7b72&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;10&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#10&#34;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		archive_url &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;url_to_announce&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;11&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#11&#34;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&#34;color:#ff7b72&#34;&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;12&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#12&#34;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#ff7b72&#34;&gt;done&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For &lt;code&gt;extract_external_urls&lt;/code&gt;, we fetch the HTML code of all URLs from the sitemap with &lt;code&gt;curl&lt;/code&gt; again. &lt;code&gt;grep -v &amp;quot;${SOURCE_URL}&amp;quot;&lt;/code&gt; ensures that we exclude all internal links. This &lt;code&gt;grep&lt;/code&gt; command can be further expanded to exclude external links that are present on every page or in a navigation area.
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;1&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#1&#34;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;extract_external_urls&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;()&lt;/span&gt; &lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;2&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#2&#34;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	curl -s -f -L &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;url_to_announce&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt; | grep -Eo &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#39;&amp;#34;(http|https)://[a-zA-Z0-9#~.*,/!?=+&amp;amp;_%:-]*&amp;#34;&amp;#39;&lt;/span&gt; | grep -v &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;SOURCE_URL&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt; | sed &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#39;s/\&amp;#34;//g&amp;#39;&lt;/span&gt; | sort -u
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;3&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#3&#34;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;We can now track down all pages from the sitemap, and also have a list of all external URLs included on every page. To submit the URL to the Wayback Machine, we only have to send a single request to their endpoint.
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;1&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#1&#34;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;announce_url&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;()&lt;/span&gt; &lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;2&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#2&#34;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	curl -s -o /dev/null -w &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;%{http_code}&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;SUBMISSION_URL&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;url_to_announce&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;3&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#3&#34;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;For the front page, this expands to the following expression:
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;1&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#1&#34;&gt;1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;announce_url&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;()&lt;/span&gt; &lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;2&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#2&#34;&gt;2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	curl -s -o /dev/null -w &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;%{http_code}&amp;#34;&lt;/span&gt; https://web.archive.org/save/https://felsqualle.com/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;3&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#3&#34;&gt;3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;Finally, we need some (now annotated) logic to glue everything together.
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;1&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#1&#34;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;archive_url&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;()&lt;/span&gt; &lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;2&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#2&#34;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# Initially, we start with a try_count of 0.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;3&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#3&#34;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# Since web.archive.org is often overloaded, we have to expect&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;4&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#4&#34;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# multiple retries until we get a successful submission.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;5&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#5&#34;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# We have to avoid deadlocks, so we need to limit the amount&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;6&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#6&#34;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# of retries somehow.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;7&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#7&#34;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;((&lt;/span&gt;try_count++&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;8&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#8&#34;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;9&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#9&#34;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&#34;color:#ff7b72&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;[[&lt;/span&gt; &lt;span style=&#34;color:#ff7b72&#34;&gt;$(&lt;/span&gt;announce_url &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;url_to_announce&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff7b72&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#39;302&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;]]&lt;/span&gt;; &lt;span style=&#34;color:#ff7b72&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;10&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#10&#34;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# For checking the status of our submission, we rely on the&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;11&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#11&#34;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# 302 redirection web.archive.org performs after it successfully&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;12&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#12&#34;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# saved a website.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;13&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#13&#34;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;14&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#14&#34;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# In case we receive any other HTTP status code ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;15&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#15&#34;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		echo &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;Successfully archived \&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;url_to_announce&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;\&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;16&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#16&#34;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&#34;color:#79c0ff&#34;&gt;try_count&lt;/span&gt;&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;17&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#17&#34;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&#34;color:#ff7b72&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;18&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#18&#34;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# ... we need to do some work.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;19&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#19&#34;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		echo &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;Retrying to archive \&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;url_to_announce&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;\&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;20&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#20&#34;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;21&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#21&#34;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# Give the archive.org API some time to cool down.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;22&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#22&#34;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# Sometimes, multiple subsequent calls lead to rate limiting&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;23&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#23&#34;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# that we can mitigate by slowing down our requests.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;24&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#24&#34;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		sleep &lt;span style=&#34;color:#a5d6ff&#34;&gt;30&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;25&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#25&#34;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;26&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#26&#34;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# Retry announcing the same URL again until we reach the failure threshold.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;27&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#27&#34;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# We want to try until we reach our retry threshold of 5 attepts.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;28&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#28&#34;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# At the same time, we have to ensure that our current counter is&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;29&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#29&#34;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# _NOT_ 0. In theory, this should never happen, but _if_ it does,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;30&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#30&#34;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# we are stuck in an endless loop.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;31&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#31&#34;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&#34;color:#ff7b72&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;[&lt;/span&gt; &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;$try_count&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt; -lt &lt;span style=&#34;color:#a5d6ff&#34;&gt;5&lt;/span&gt; &lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;]&lt;/span&gt; &lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;[&lt;/span&gt; &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;$try_count&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt; -ne &lt;span style=&#34;color:#a5d6ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;]&lt;/span&gt;; &lt;span style=&#34;color:#ff7b72&#34;&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;32&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#32&#34;&gt;32&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			archive_url &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;url_to_announce&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;33&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#33&#34;&gt;33&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&#34;color:#ff7b72&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;34&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#34&#34;&gt;34&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# When reaching our threshold, we simply give up and continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;35&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#35&#34;&gt;35&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# with the next URL.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;36&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#36&#34;&gt;36&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# We reset our counter and exit with an errorcode (which we don&amp;#39;t check yet)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;37&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#37&#34;&gt;37&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			echo &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;Giving up on url \&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;url_to_announce&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;\&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;38&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#38&#34;&gt;38&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&#34;color:#79c0ff&#34;&gt;try_count&lt;/span&gt;&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;39&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#39&#34;&gt;39&lt;/a&gt;&lt;/span&gt;&lt;span&gt;			&lt;span style=&#34;color:#ff7b72&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a5d6ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;40&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#40&#34;&gt;40&lt;/a&gt;&lt;/span&gt;&lt;span&gt;		&lt;span style=&#34;color:#ff7b72&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;41&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#41&#34;&gt;41&lt;/a&gt;&lt;/span&gt;&lt;span&gt;	&lt;span style=&#34;color:#ff7b72&#34;&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;42&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#42&#34;&gt;42&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;With this script, I was able to update the copy of my website that is currently stored on archive.org. And thanks to some extensive testing, I discovered that archive.org has a limit of 5 submissions per URL and day &amp;mdash; oops.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Creating New Installation Media for MS-DOS 4.0</title>
      <link>https://felsqualle.com/posts/2024/06/new-installation-media-for-ms-dos-4/</link>
      <pubDate>Sat, 22 Jun 2024 22:00:00 +0200</pubDate>
      
      <guid>https://felsqualle.com/posts/2024/06/new-installation-media-for-ms-dos-4/</guid>
      <description>&lt;p&gt;On April 25, 2024, Microsoft &lt;a href=&#34;https://felsqualle.com/posts/2024/05/the-broken-source-code-for-ms-dos-4-has-been-restored/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;released the source code of MS-/PC-DOS 4.00&lt;/a&gt;
 to the public, and the community managed to provide &lt;a href=&#34;https://felsqualle.com/posts/2024/05/a-minor-update-ms-dos-4-1-is-here/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;MS-DOS 4.01 as well.&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;While the original release was available as a set of six 5,25&amp;quot; floppy disks (and optionally three 3,5&amp;quot; disks for version 4.01), this open-source release does not have official installation media.&lt;/p&gt;
&lt;p&gt;Recreating the original media would theoretically be possible, but I want to focus on the 720k images for this project. Having to swap around six floppies multiple times isn&amp;rsquo;t practical. Furthermore, we can build images for 1.44 MB floppy disks for increased compatibility: The SELECT utility used for the installation process doesn&amp;rsquo;t like it if you install it on floppies with a different format than the source disks are written in.&lt;/p&gt;
&lt;p&gt;The open-source release is missing a few program files and the entire DOSSHELL environment, so that&amp;rsquo;s something we also have to deal with.&lt;/p&gt;
&lt;h2 id=&#34;the-missing-link&#34;&gt;The missing LINK&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s start with the missing files. To check if the correct floppy is present in the drive, the SELECT utility used to install MS-DOS 4.0 doesn&amp;rsquo;t check the disk label but directly checks if the files it is supposed to copy are available on the disk. If a file is missing, it simply tells you you have the wrong floppy inserted.&lt;/p&gt;
&lt;p&gt;While this isn&amp;rsquo;t an issue for DOSSHELL at the moment&amp;mdash;it is optional and only installed upon request&amp;mdash;this is a massive problem for two other missing files.&lt;/p&gt;
&lt;p&gt;In total, the following core files are missing from the open-source release:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HIMEM.SYS&lt;/li&gt;
&lt;li&gt;GWBASIC.EXE&lt;/li&gt;
&lt;li&gt;LINK.EXE&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;HIMEM.SYS is the easiest one. It is not referenced in the SELECT code at all, which means it can&amp;rsquo;t be one of the &amp;ldquo;marker files&amp;rdquo; indicating a specific floppy disk. We can skip it altogether, and nothing will break.&lt;/p&gt;
&lt;p&gt;The other two files, however, are mentioned in &lt;code&gt;SEL_FILE.INC&lt;/code&gt;, which would cause an issue if the files were absent. To fix this, I created two simple executables with Turbo Pascal 5.5:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;program Gwbasic;
begin
  writeln(&amp;#39;This release of MS-DOS 4.0 is based on the code released&amp;#39;);
  writeln(&amp;#39;to the general public in April 2024.&amp;#39;);
  writeln(&amp;#39;&amp;#39;);
  writeln(&amp;#39;GWBASIC.EXE is not included in this release.&amp;#39;);
end.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Since SELECT doesn&amp;rsquo;t do any checksum comparison, it won&amp;rsquo;t complain about these stubs, and the installation will happily continue.&lt;/p&gt;
&lt;h2 id=&#34;removing-dosshell&#34;&gt;Removing DOSSHELL&lt;/h2&gt;
&lt;p&gt;DOSSHELL, however, is an entirely different problem. While it is optional and not selected by default, the user can enable it during installation. This will inevitably break the installation since we don&amp;rsquo;t have any DOSSHELL components in the open-source release.&lt;/p&gt;
&lt;p&gt;We need to disable DOSSHELL in two places:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/new-installation-media-for-ms-dos-4/dosshell_select_1.webp&#34; alt=&#34;MS-DOS Shell Option&#34; title=&#34;The MS-DOS Shell Option screen&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/new-installation-media-for-ms-dos-4/dosshell_select_2.webp&#34; alt=&#34;Review Selection&#34; title=&#34;The Review Selection selection&#34;&gt;
&lt;/p&gt;
&lt;p&gt;The file responsible for handling both screens is &lt;code&gt;SELECT3.ASM&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Conveniently, we get a hint on how to disable the first screen:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;     82 ;  The CHOOSE SHELL SCREEN is always presented.
     83 ;  This screen allows the user to decide whether or not the DOS
     84 ;  shell will be installed.
     85 ;  Valid keys are ENTER, ESC, F1, F3 and numeric 1 and 2.
     86 ;----
     87 ; Note:  This screen (and, hence, all shell support) can be eradicated
     88 ;       by defining the symbol NOSHELL.
     89 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     90 choose_shell_screen:                                            ;AN000;
     91 IFNDEF NOSHELL
     92         INIT_PQUEUE             PAN_choose_shell                ;AN000; initialize queue
     93         PREPARE_PANEL           PAN_HBAR                        ;AN000; prepare horizontal bar
     94         PREPARE_CHILDREN                                        ;AN000; prepare child panels
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;NOSHELL EQU 1&lt;/code&gt; defines the required symbol, and as expected, the first screen is skipped.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;     82 ;  The CHOOSE SHELL SCREEN is always presented.
     83 ;  This screen allows the user to decide whether or not the DOS
     84 ;  shell will be installed.
     85 ;  Valid keys are ENTER, ESC, F1, F3 and numeric 1 and 2.
     86 ;----
     87 ; Note:  This screen (and, hence, all shell support) can be eradicated
     88 ;       by defining the symbol NOSHELL.
     89 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     90 choose_shell_screen:                                            ;AN000;
&amp;gt;&amp;gt;&amp;gt;  91 NOSHELL EQU 1
     92 IFNDEF NOSHELL
     93         INIT_PQUEUE             PAN_choose_shell                ;AN000; initialize queue
     94         PREPARE_PANEL           PAN_HBAR                        ;AN000; prepare horizontal bar
     95         PREPARE_CHILDREN                                        ;AN000; prepare child panels
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Unfortunately, the &amp;ldquo;Review Selection&amp;rdquo; screen doesn&amp;rsquo;t honor this symbol. This screen can override all previously set variables regarding the features that should be enabled. Furthermore, the entire menu system is scattered across multiple files, and changing one entry means we have to rewrite all other menu options.&lt;/p&gt;
&lt;p&gt;Forcing the return value to &lt;code&gt;E_SHELL_NO&lt;/code&gt;, which would remove DOSSHELL integration, doesn&amp;rsquo;t work either.&lt;/p&gt;
&lt;p&gt;I got it somewhat working once, but this caused the menu system to break, and I managed to &amp;ldquo;enable&amp;rdquo; XMA support on an emulated machine that definitely doesn&amp;rsquo;t support it. At this point, I realized that modifying the menu system was beyond my non-existent assembler skills.&lt;/p&gt;
&lt;p&gt;The &amp;ldquo;Review Selection&amp;rdquo; screen is controlled by the &lt;code&gt;REVIEW_DISK_SCREEN&lt;/code&gt; and &lt;code&gt;REVIEW_DISKETTE_SCREEN&lt;/code&gt; functions in &lt;code&gt;SELECT3.ASM&lt;/code&gt;. Since I couldn&amp;rsquo;t &lt;em&gt;disable&lt;/em&gt; the menu option, I dusted off my crowbar:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;    221            RET_SUPPORT          SUPPORT_STATUS,E_GRAPHICS_C,F_GRAPHICS;AN000; GRAPHICS support
    222            RET_SUPPORT          SUPPORT_STATUS,E_SHARE_C,F_SHARE;AN000;      SHARE support
    223            RET_SUPPORT          SUPPORT_STATUS,E_SHELL_C,F_SHELL;AN000;      SHELL support
    224            RET_SUPPORT          SUPPORT_STATUS,E_VDISK_C,F_VDISK;AN000;      VDISK support
&amp;gt;&amp;gt;&amp;gt; 225            INIT_VAR             F_SHELL,E_SHELL_NO
    226            PUSH_HEADING         REVIEW_DISK_SCREEN              ;AN000;    save screen address on SELECT STACK
    227            GOTO                 DOS_PARAMETERS_SCREEN           ;AN000;    goto the next screen (DOS_PARAMETERS)
&lt;/code&gt;&lt;/pre&gt;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;    277            RET_SUPPORT          SUPPORT_STATUS,E_SHELL_B,F_SHELL;AN000;      SHELL support
    278            RET_SUPPORT          SUPPORT_STATUS,E_VDISK_B,F_VDISK;AN000;      VDISK support
&amp;gt;&amp;gt;&amp;gt; 279            INIT_VAR             F_SHELL,E_SHELL_NO
    280            PUSH_HEADING         REVIEW_DISKETTE_SCREEN          ;AN000; save screen address onto SELECT STACK
    281            GOTO                 DOS_PARAMETERS_SCREEN           ;AN000; goto the next screen (DOS_PARAMETERS)
    282         .ELSE                                                   ;AN000;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Using &lt;code&gt;INIT_VAR&lt;/code&gt; here always forces &lt;code&gt;F_SHELL&lt;/code&gt; to &lt;code&gt;E_SHELL_NO&lt;/code&gt;, discarding the user&amp;rsquo;s selection which was previously stored with &lt;code&gt;RET_SUPPORT&lt;/code&gt;. Thus, it effectively prevents DOSSHELL from ever being installed.&lt;/p&gt;
&lt;p&gt;Yes, this is incredibly ugly, but also very effective.&lt;/p&gt;
&lt;h2 id=&#34;creating-the-disk-images&#34;&gt;Creating the disk images&lt;/h2&gt;
&lt;p&gt;With the fixed SELECT executable on hand, it was time to create the floppy images. Luckily, it fits on two 720kB floppies, so disk swapping is not too bad. Please note that it would also fit on a single 1,44MB floppy, but this will heavily confuse SELECT and break support for installing to a set of floppies.&lt;/p&gt;
&lt;p&gt;For 3.5&amp;quot; floppy disks, the disk size doesn&amp;rsquo;t matter, so both 720kB and 1.44MB disks are a viable option, but you really should create both a dedicated &lt;code&gt;INSTALL&lt;/code&gt; and &lt;code&gt;OPERATING&lt;/code&gt; floppy. Unfortunately, you can&amp;rsquo;t split the images on two 1.2MB 5.25&amp;quot; disks since SELECT will expect all six floppies in this case.&lt;/p&gt;
&lt;p&gt;Creating the floppy images was as simple as copying all required files using the official release as a template and making the first disk bootable.&lt;/p&gt;
&lt;p&gt;Installing MS-DOS 4.0 on a hard drive worked without any issues. While the installation to a set of floppies &lt;em&gt;should&lt;/em&gt; work, I had mixed results even when using the images from the original release, so your mileage may vary.&lt;/p&gt;
&lt;h2 id=&#34;downloads&#34;&gt;Downloads&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;v400_install_1440.zip&#34;&gt;MS-DOS 4.00 Installation Disks (2x 1.44MB)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;v400_install_720.zip&#34;&gt;MS-DOS 4.00 Installation Disks (2x 720kB)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;v401_install_1440.zip&#34;&gt;MS-DOS 4.01 Installation Disks (2x 1.44MB)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;v401_install_720.zip&#34;&gt;MS-DOS 4.00 Installation Disks (2x 720kB)&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Have fun!&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>If I Ever Get a Dog, I&#39;ll Name It Rover: A Brief Introduction to Microsoft Bob</title>
      <link>https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/</link>
      <pubDate>Sun, 16 Jun 2024 20:00:00 +0200</pubDate>
      
      <guid>https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/</guid>
      <description>&lt;p&gt;March 1995. Although PCs had been around for many years at this point, less than &lt;a href=&#34;https://web.archive.org/web/20240611174601/http://scholar.lib.vt.edu/VA-news/VA-Pilot/issues/1996/vp960527/05250416.htm&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;40% of households in the US&lt;/a&gt;
 owned one. Back then, people mostly associated PCs with being a machine used at work, not a thing you&amp;rsquo;d even need at home. They were complicated and&amp;hellip; unfriendly.&lt;/p&gt;
&lt;p&gt;Just a few months before the launch of the all-new Windows 95, Microsoft Bob was an attempt to create a user-friendly interface that runs on top of Windows to increase the popularity of PCs at home and show how valuable a PC might be for purposes other than business.&lt;/p&gt;
&lt;p&gt;This interface provides a virtual home inside your computer, divided into multiple rooms resembling your house&amp;rsquo;s rooms. It&amp;rsquo;s designed to be intuitive and user-friendly, so you don&amp;rsquo;t need to know what a word processor is or how the applications are named &amp;mdash; for example, just click on the pen and paper on your virtual desk and start writing a letter.&lt;/p&gt;
&lt;h2 id=&#34;introduction&#34;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;When starting Microsoft Bob for the first time, you are greeted by Rover Retriever, the main mascot who will guide you through the program.&lt;/p&gt;
&lt;p&gt;While the concept of having a virtual assistant as an integrated online help was new at the time, it would later gain popularity in the form of &lt;em&gt;Clippit&lt;/em&gt;, &lt;a href=&#34;https://en.wikipedia.org/wiki/Office_Assistant&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;the virtual assistant integrated into Microsoft Office&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/signup.webp&#34; alt=&#34;Please welcome Rover, your friendly assistant!&#34; title=&#34;Please welcome Rover, your friendly assistant!&#34;&gt;
&lt;/p&gt;
&lt;p&gt;After telling Rover a bit about yourself, setting a password for your account, and selecting a secret room only you have access to, you will enter your virtual home for the first time.&lt;/p&gt;
&lt;p&gt;Then, Rover will take you on a tour of your new home so you know exactly where to find the different applications Microsoft Bob offers.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/tour1.webp&#34; alt=&#34;Welcome home&#34; title=&#34;Welcome home&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/tour2.webp&#34; alt=&#34;The different applications&#34; title=&#34;The different applications&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/tour3.webp&#34; alt=&#34;The Letter Writer&#34; title=&#34;The Letter Writer&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/tour4.webp&#34; alt=&#34;The Calendar&#34; title=&#34;The Calendar&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/tour5.webp&#34; alt=&#34;The Checkbook&#34; title=&#34;The Checkbook&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/tour6.webp&#34; alt=&#34;The Financial Guide&#34; title=&#34;The Financial Guide&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/tour7.webp&#34; alt=&#34;The Household Manager&#34; title=&#34;The Household Manager&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/tour8.webp&#34; alt=&#34;E-Mail&#34; title=&#34;E-Mail&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/tour9.webp&#34; alt=&#34;The Address Book&#34; title=&#34;The Address Book&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/tour10.webp&#34; alt=&#34;GeoSafari&#34; title=&#34;GeoSafari&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/tour11.webp&#34; alt=&#34;Putting it all together!&#34; title=&#34;Putting it all together!&#34;&gt;
&lt;/p&gt;
&lt;p&gt;But wait, there are more objects on the screen, so what do they do? At first, I was worried I&amp;rsquo;d miss an application hiding behind one of the other objects.&lt;/p&gt;
&lt;p&gt;However, the applications Rover mentioned are &lt;em&gt;really&lt;/em&gt; the only ones included in Microsoft Bob. Everything else is cosmetic.&lt;/p&gt;
&lt;p&gt;If you are unsure where your application objects are located, simply press the &amp;ldquo;F1&amp;rdquo; key to reveal all clickable hotspots.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/hotkeys.webp&#34; alt=&#34;Hotspots&#34; title=&#34;The Hotspots&#34;&gt;
&lt;/p&gt;
&lt;p&gt;Clicking on the books on the shelf, for example, reveals that they are purely decorative and don&amp;rsquo;t add any functionality. As you can see on the screenshot, you can replace the decorative objects, but more on that later.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/decorative_explaination.webp&#34; alt=&#34;The Encyclopedia - looks nice, but does nothing&#34; title=&#34;The Encyclopedia - looks nice, but does nothing&#34;&gt;
&lt;/p&gt;
&lt;h2 id=&#34;the-applications&#34;&gt;The applications&lt;/h2&gt;
&lt;p&gt;Time to get some tasks done! Let&amp;rsquo;s have a look at the various applications bundled with Microsoft Bob.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;#the-letter-writer&#34;&gt;The Letter Writer&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#the-calendar&#34;&gt;The Calendar&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#the-checkbook&#34;&gt;The Checkbook&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#the-financial-guide&#34;&gt;The Financial Guide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#the-household-manager&#34;&gt;The Household Manager&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#e-mail&#34;&gt;E-Mail&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#the-address-book&#34;&gt;The Address Book&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;#geosafari&#34;&gt;GeoSafari&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;the-letter-writer&#34;&gt;The Letter Writer&lt;/h3&gt;
&lt;p&gt;At first glance, the Letter Writer feels like a stripped-down version of Microsoft Word. When launching the application by clicking the &amp;ldquo;Pen &amp;amp; Paper&amp;rdquo; object, Rover will guide you through the steps for, well, writing a letter.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/letter1.webp&#34; alt=&#34;The Letter Writer&#34; title=&#34;The Letter Writer&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/letter2.webp&#34; alt=&#34;Picking a letter name&#34; title=&#34;Pick a letter name&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/letter3.webp&#34; alt=&#34;Select the type of letter&amp;hellip;&#34; title=&#34;Select the type of the letter...&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/letter4.webp&#34; alt=&#34;&amp;hellip; add decorations &amp;hellip;&#34; title=&#34;... add decorations ...&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/letter5.webp&#34; alt=&#34;&amp;hellip; add more decorations &amp;hellip;&#34; title=&#34;... add more decorations ...&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/letter6.webp&#34; alt=&#34;&amp;hellip; and more &amp;hellip;&#34; title=&#34;... and more ...&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/letter7.webp&#34; alt=&#34;Select an address or pick one from the premade address books&#34; title=&#34;Select an address or pick one from the premade address books&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/letter8.webp&#34; alt=&#34;And finally get work done!&#34; title=&#34;And finally get work done!&#34;&gt;
&lt;/p&gt;
&lt;p&gt;The application itself looks pretty straightforward and allows for many simple customizations. You can pick from various border styles, and premade cliparts for every occasion. It even includes the famous WordArt!&lt;/p&gt;
&lt;p&gt;Letter Writer includes numerous templates. The pre-filled address book includes politicians, airlines, travel information agencies, departments for environmental concerns, newspapers, magazines, insurance companies, and others.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/badmatch.webp&#34; alt=&#34;Happy Birthday&amp;hellip; Alaska Airlines!&#34; title=&#34;Happy Birthday... Alaska Airlines!&#34;&gt;
&lt;/p&gt;
&lt;p&gt;Yes, it is pretty versatile and a helpful start if you never used a word processor before. However, if you have some experience with Microsoft Word, it feels slow and tedious to use in comparison.&lt;/p&gt;
&lt;p&gt;Will the next click allow me to start typing? Ah, no, another step in the assistant. No, I don&amp;rsquo;t want to pick an address from my address book because I don&amp;rsquo;t have any addresses yet. Yes, I&amp;rsquo;d like to have a nice picture in my letter. Sure, why not? Makes things a bit more personal. Thank you!&lt;/p&gt;
&lt;p&gt;When starting the application, you can&amp;rsquo;t skip the assistant altogether. You have to click three times to leave it and being able to work.&lt;/p&gt;
&lt;p&gt;Rover either didn&amp;rsquo;t mention it, or I missed it when skipping through the assistant: There&amp;rsquo;s no dedicated way to save the letter (file). Just exit Letter Writer, and it will be automatically saved for later use.&lt;/p&gt;
&lt;h3 id=&#34;the-calendar&#34;&gt;The Calendar&lt;/h3&gt;
&lt;p&gt;Clicking on the calendar on your virtual wall opens the Bob Calendar application.&lt;/p&gt;
&lt;p&gt;Since the calendar offers fewer options than the Letter Writer, Rover is also much less intrusive. You can access all options right at the beginning and don&amp;rsquo;t have to click through a lengthy guide.&lt;/p&gt;
&lt;p&gt;Add events, set reminders, and create your To-Do list &amp;mdash; there&amp;rsquo;s not much to say about this application.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/calendar1.webp&#34; alt=&#34;The Calendar&#34; title=&#34;The Calendar&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/calendar2.webp&#34; alt=&#34;Creating a new event&#34; title=&#34;Creating a new event&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/calendar3.webp&#34; alt=&#34;Set a reminder&#34; title=&#34;Setting a reminder&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/calendar4.webp&#34; alt=&#34;The To Do list&#34; title=&#34;The To Do list&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/calendar5.webp&#34; alt=&#34;Here&amp;rsquo;s your reminder!&#34; title=&#34;Here&amp;#39;s yor reminder!&#34;&gt;
&lt;/p&gt;
&lt;h3 id=&#34;the-checkbook&#34;&gt;The Checkbook&lt;/h3&gt;
&lt;p&gt;The Checkbook is the leading financial application of Microsoft Bob. First, you are introduced to another assistant: Lexi.&lt;/p&gt;
&lt;p&gt;Considering that Checkbook is a fairly large application, having a tour makes sense and matches the philosophy of Microsoft Bob: &amp;ldquo;Explain everything&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/checkbook2.webp&#34; alt=&#34;A new tour guide&#34; title=&#34;A new tour guide&#34;&gt;
&lt;/p&gt;
&lt;p&gt;The application is divided into multiple sections:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Account Book&lt;/li&gt;
&lt;li&gt;The Bill Basket&lt;/li&gt;
&lt;li&gt;The Report Folder&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the account book, you add your various bank accounts for payments and deposits. You&amp;rsquo;ll record all your transactions there, so you see exactly where your money goes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/checkbook5.webp&#34; alt=&#34;The Account Book&#34; title=&#34;The Account Book&#34;&gt;
&lt;/p&gt;
&lt;p&gt;Next is the bill basket. It contains your upcoming bills and deposits. Thanks to the integration in the Bob Calendar, you&amp;rsquo;ll never miss a payment again. A payment or deposit will be automatically recorded in the account book when it is due.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/checkbook7.webp&#34; alt=&#34;The Bill Basket&#34; title=&#34;The Bill Basket&#34;&gt;
&lt;/p&gt;
&lt;p&gt;The third and final section is the report folder. As the name implies, you can create all sorts of reports here to check your financial health.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/checkbook9.webp&#34; alt=&#34;The Report Folder&#34; title=&#34;The Report Folder&#34;&gt;
&lt;/p&gt;
&lt;p&gt;After showcasing the different areas, Lexi continues with the initial account setup.&lt;/p&gt;
&lt;p&gt;Ironically, one of the first options shows that Microsoft didn&amp;rsquo;t have a clear target audience for Microsoft Bob: You can import an existing Microsoft Money file. If you already have a financial application that offers more options than Checkbook and is way more sophisticated, why do you even &lt;em&gt;want&lt;/em&gt; this&amp;hellip; downgrade?&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/checkbook11.webp&#34; alt=&#34;The downgrade option&#34; title=&#34;The downgrade option&#34;&gt;
&lt;/p&gt;
&lt;p&gt;The tutorial itself consists of several steps. You add your accounts, your first round of upcoming bills, and your paycheck. Additionally, you can import these transactions to your personal or household calendar, the latter being shared across all users with an account in your Microsoft Bob installation.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/checkbook12.webp&#34; alt=&#34;Setting up your accounts&#34; title=&#34;Setting up your accounts&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/checkbook17.webp&#34; alt=&#34;Entering your upcoming bills and deposits&#34; title=&#34;Entering your upcoming bills and deposits&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/checkbook18.webp&#34; alt=&#34;Your bills&#34; title=&#34;Your bills&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/checkbook23.webp&#34; alt=&#34;Your deposits&#34; title=&#34;Your deposits&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/checkbook24.webp&#34; alt=&#34;Your paycheck&#34; title=&#34;Your paycheck&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/checkbook27.webp&#34; alt=&#34;The integration into the calendar&#34; title=&#34;The integration into the calendar&#34;&gt;
&lt;/p&gt;
&lt;p&gt;Bob Checkbook also provided an online service: Pay On-Line. Using this (now defunct) service, you could send payments to individuals and businesses in the US - right from your computer and in the blink of an eye.&lt;/p&gt;
&lt;p&gt;Once you manage to get past the tutorial, Lexi will not be as intrusive as Rover in Letter Writer. On subsequent starts, you can get straight to the various areas of the program without having to look at how to get rid of too much helpful advice.&lt;/p&gt;
&lt;h3 id=&#34;the-financial-guide&#34;&gt;The Financial Guide&lt;/h3&gt;
&lt;p&gt;Bob Financial Guide is best described as a weird mix of a tutorial on life and creating oddly specific lists.&lt;/p&gt;
&lt;p&gt;You start with an overview covering important aspects of life: Finance, buying a house, some personal milestones, your retirement.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/financial1.webp&#34; alt=&#34;The Table of Contents&#34; title=&#34;The Table of Contents&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/financial2.webp&#34; alt=&#34;Is it a tutorial or a program for managing lists?&#34; title=&#34;Is it a tutorial or a program for managing lists?&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/financial3.webp&#34; alt=&#34;The tutorial&amp;hellip;&#34; title=&#34;The tutorial...&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/financial4.webp&#34; alt=&#34;&amp;hellip;and the list management&#34; title=&#34;...and the list management&#34;&gt;
&lt;/p&gt;
&lt;p&gt;Here, you can either modify the premade lists or generate your own.&lt;/p&gt;
&lt;p&gt;Sure, some things in life require quite a lot of planning, but I don&amp;rsquo;t know if Microsoft Bob is the right tool. It works, but it feels clumsy, and I had a hard time finding the lists I created &amp;mdash; did I put them in &amp;ldquo;Buying a Home&amp;rdquo; or &amp;ldquo;My Home&amp;rdquo;? I am trying to remember.&lt;/p&gt;
&lt;h3 id=&#34;the-household-manager&#34;&gt;The Household Manager&lt;/h3&gt;
&lt;p&gt;Essentially, the Household Manager is the same as the Financial Guide, with different topics. And if I said &amp;ldquo;oddly specific&amp;rdquo; for the lists in the Financial Guide, then I have to say that we are entering the &amp;ldquo;absurdly specific&amp;rdquo; territory for the Household Manager.^&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/household1.webp&#34; alt=&#34;The Table of Contents&#34; title=&#34;The Table of Contents&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/household2.webp&#34; alt=&#34;The kitchen&#34; title=&#34;The kitchen...&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/household3.webp&#34; alt=&#34;&amp;hellip;and keeping track of it!&#34; title=&#34;...and keeping track of it!&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/household4.webp&#34; alt=&#34;I told you it is oddly specific&#34; title=&#34;I told you it is oddly specific&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/household5.webp&#34; alt=&#34;I can&amp;rsquo;t tell you how much bonus points I gave it for including a cat manual!&#34; title=&#34;I can&amp;#39;t tell you how much bonus points I gave it for including a cat manual&#34;&gt;
&lt;/p&gt;
&lt;p&gt;It suffers from the same flaw as the Financial Guide: It is easy to add information, but finding it again is hard. I appreciate the included cat manual, though.&lt;/p&gt;
&lt;h3 id=&#34;e-mail&#34;&gt;E-Mail&lt;/h3&gt;
&lt;p&gt;Bob E-Mail was an E-Mail service provided by MCI Mail in collaboration with Microsoft. After signing up for their service, you get a nice @bob.com address to send and receive emails. Thanks to the integration into Letter Writer and the Address Book, you could send messages directly through these applications.&lt;/p&gt;
&lt;p&gt;Unfortunately, the service is long gone, so there&amp;rsquo;s nothing I can show except for the startup screen.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/email.webp&#34; alt=&#34;Sign up!&#34; title=&#34;Sign up!&#34;&gt;
&lt;/p&gt;
&lt;h3 id=&#34;the-address-book&#34;&gt;The Address Book&lt;/h3&gt;
&lt;p&gt;The Address Book integrates with Letter Writer and Bob E-Mail. You can add, modify, and find addresses and use them as recipient addresses when writing a letter or sending it electronically.&lt;/p&gt;
&lt;p&gt;It is simple and effective - exactly what you&amp;rsquo;d expect from an address book.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/address2.webp&#34; alt=&#34;Entering addresses&amp;hellip;&#34; title=&#34;Entering addresses...&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/address3.webp&#34; alt=&#34;&amp;hellip;and retrieving them&#34; title=&#34;...and retrieving them&#34;&gt;
&lt;/p&gt;
&lt;h3 id=&#34;geosafari&#34;&gt;GeoSafari&lt;/h3&gt;
&lt;p&gt;GeoSafari is the only educational title and thus more geared towards children. This time, Hank will replace Rover, who will guide you through the program.&lt;/p&gt;
&lt;p&gt;The main objective of this application is to solve geographical puzzles: find capitals and countries, name monuments, have fun with flags, and visit the solar system.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/geo2.webp&#34; alt=&#34;Select a quiz&#34; title=&#34;Select a quiz&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/geo3.webp&#34; alt=&#34;Geography: Europe&#34; title=&#34;Geography: Europe&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/geo5.webp&#34; alt=&#34;A trip to the outside world&#34; title=&#34;A trip to the outside world&#34;&gt;
&lt;/p&gt;
&lt;h2 id=&#34;feeling-at-home&#34;&gt;Feeling at home&lt;/h2&gt;
&lt;p&gt;Microsoft Bob provides an advanced customization system. You start in your living room, but there are more rooms to explore. Functionally, the rooms do exactly the same: They contain objects that either start programs or are purely decorative. Objects can be added, replaced, or rearranged.&lt;/p&gt;
&lt;p&gt;The only room that works slightly differently is the personal room you selected when you launched the application for the first time. It will be marked as &amp;ldquo;hidden&amp;rdquo;, so the other users won&amp;rsquo;t be able to see it.
In addition to the premade rooms, you can create your very own rooms. There&amp;rsquo;s a surprisingly extensive catalog of objects to choose from, providing a high level of customization.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/room1.webp&#34; alt=&#34;The Family Room&#34; title=&#34;The Family Room&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/room2.webp&#34; alt=&#34;The Study Room&#34; title=&#34;The Study Room&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/room3.webp&#34; alt=&#34;The Mouse Hole&#34; title=&#34;The Mouse Hole&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/room4.webp&#34; alt=&#34;The Safe&#34; title=&#34;The Safe&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/room5.webp&#34; alt=&#34;My personal study room&#34; title=&#34;My personal study room&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/room6.webp&#34; alt=&#34;Create a new room&amp;hellip;&#34; title=&#34;Create a new room...&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/room7.webp&#34; alt=&#34;&amp;hellip; and add some objects&#34; title=&#34;... and add some objects&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/06/if-i-ever-get-a-dog-ill-name-it-rover-ms-bob-retrospective/room8.webp&#34; alt=&#34;Home, sweet home&#34; title=&#34;Home, sweet home&#34;&gt;
&lt;/p&gt;
&lt;p&gt;However, transitioning between the different rooms takes quite a lot of time, even on the Pentium with 75 MHz backed by 16 MB of RAM I&amp;rsquo;m emulating for this review.&lt;/p&gt;
&lt;h2 id=&#34;conclusion-and-legacy&#34;&gt;Conclusion and legacy&lt;/h2&gt;
&lt;p&gt;Microsoft Bob is one of the most spectacular product failures the company ever experienced. Reviews and sales were so bad that they discontinued it in early 1996, giving it a lifespan of just under a year.&lt;/p&gt;
&lt;p&gt;Microsoft Bob had high system requirements and even felt slow on PCs faster than standard hardware at the time. Combined with a retail price of $99, which adjusted for inflation equals around $200 in 2024, the limited set of included applications wasn&amp;rsquo;t helping sales either. Initially, Microsoft planned a whole software ecosystem around Bob, but in the end, only &lt;em&gt;Microsoft Great Greetings&lt;/em&gt;, a software for creating greeting cards at home, was released specifically for Microsoft Bob.&lt;/p&gt;
&lt;p&gt;Looking at the visuals, the number of different objects, and the written content (remember, there&amp;rsquo;s a tutorial on how to handle your &lt;em&gt;cat&lt;/em&gt;!), you can clearly see that the development team and designers put a lot of effort into Microsoft Bob.&lt;/p&gt;
&lt;p&gt;Do I think it is a &lt;em&gt;bad&lt;/em&gt; product? No, but I think it is &lt;em&gt;pointless&lt;/em&gt;. It looks nice but provides little additional value. The bundled applications are stripped down, redundant, and sometimes even hard to use. Sure, you can add your Windows programs to Bob and assign them to objects, but this will make things way slower than just starting them through the Program Manager. All in all, it feels like &lt;em&gt;Microsoft Office: Preschool Edition&lt;/em&gt; or a weird crossover between Windows and &lt;em&gt;The Sims&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;My recommendation for 1995 computer users:&lt;/strong&gt; Get a good video tutorial for Windows 3.1 instead and learn how to use the real thing.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Let&#39;s Build a fabulous.community</title>
      <link>https://felsqualle.com/posts/2024/06/lets-build-a-fabulous-community/</link>
      <pubDate>Fri, 07 Jun 2024 20:00:00 +0200</pubDate>
      
      <guid>https://felsqualle.com/posts/2024/06/lets-build-a-fabulous-community/</guid>
      <description>&lt;p&gt;Today, I&amp;rsquo;m excited to finally announce a new side project I&amp;rsquo;ve been working on for quite a while.&lt;/p&gt;
&lt;p&gt;To be more precise, this will be &lt;em&gt;our&lt;/em&gt; new side project that we&amp;rsquo;ll be working on in the future.&lt;/p&gt;
&lt;p&gt;Please welcome &lt;a href=&#34;https://fabulous.community&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;fabulous.community&lt;/a&gt;
, a brand new message board for everything related to retro-computing, and the contents of this website. Yes, my initial idea was to find a way to make comments on the articles published here easier than sending me an e-mail.&lt;/p&gt;
&lt;p&gt;Since just adding a comment section would be way too easy (and boring), I thought about&amp;hellip; well, more engagement.&lt;/p&gt;
&lt;p&gt;So, here it is.&lt;/p&gt;
&lt;p&gt;While many communities use instant messaging and chat services, niche topics like the cursed computing stuff well beyond any mainstream stuff benefit from a slower-paced message board. Oh, and it provides some essential features like proper search functionality and a way to access and archive the content published here in the future!&lt;/p&gt;
&lt;p&gt;I created a comment section in the sub-forum dedicated to this website for my most important articles and already linked the &amp;ldquo;comment sections&amp;rdquo; to the website.&lt;/p&gt;
&lt;p&gt;For the next couple of weeks, the &lt;a href=&#34;https://fabulous.community/forum/board/6-building-a-fabulous-community/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Building a fabulous.community&lt;/a&gt;
 forum will be the most important place for the entire project since this is where I&amp;rsquo;d like to collect everything that matters to you.&lt;/p&gt;
&lt;p&gt;Registrations are open, so please take a seat!&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>About Improving MS-DOS Screenshots With Calculated Distortion</title>
      <link>https://felsqualle.com/posts/2024/05/adding-aspect-ratio-correction-to-dos-screenshots/</link>
      <pubDate>Thu, 30 May 2024 17:00:00 +0200</pubDate>
      
      <guid>https://felsqualle.com/posts/2024/05/adding-aspect-ratio-correction-to-dos-screenshots/</guid>
      <description>&lt;h2 id=&#34;introduction&#34;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Have you ever noticed that a pixel-perfect screenshot from a virtual machine running MS-DOS in the default VGA text mode looks slightly off? The characters are too wide, and the whole image seems stretched.&lt;/p&gt;
&lt;p&gt;At the very least, it doesn&amp;rsquo;t evoke the same nostalgic feeling as the old CRT days, does it?&lt;/p&gt;
&lt;p&gt;But did you know that what you&amp;rsquo;re seeing is a perfect representation of the actual image, while your monitor from 1995 was deceiving you? Let&amp;rsquo;s have a closer look.&lt;/p&gt;
&lt;p&gt;Using a VGA or VGA-compatible display adapter, MS-DOS draws an image with 80 characters per line and 25 lines of text in total. In this mode, each character is 9 pixels wide and 16 pixels tall.&lt;/p&gt;
&lt;p&gt;Knowing the size per character and number of characters per screen, we can calculate the exact image size: (80*9)x(25*16)=720x400.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/05/adding-aspect-ratio-correction-to-dos-screenshots/textmode_unscaled_noscale.webp&#34; alt=&#34;MS-DOS text mode output, unscaled&#34; title=&#34;MS-DOS text mode output, unscaled&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/05/adding-aspect-ratio-correction-to-dos-screenshots/scandisk_unscaled_noscale.webp&#34; alt=&#34;ScanDisk, unscaled&#34; title=&#34;ScanDisk, unscaled&#34;&gt;
&lt;/p&gt;
&lt;p&gt;So, even though we know that 720x400 is the correct image size, &lt;em&gt;why does it look so wrong?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Compared to the usual screen resolutions from the time, 720x400 is an odd number. When considering monitors from the mid-90s, we are familiar with resolutions of 640x480, 800x600, and 1024x768, which all have one thing in common: An aspect ratio of 4:3.&lt;/p&gt;
&lt;h2 id=&#34;43-sounds-familiar&#34;&gt;4:3? Sounds familiar!&lt;/h2&gt;
&lt;p&gt;Yes, that was the aspect ratio of monitors from the past. Since you wanted to use the full screen size, you adjusted the settings on your monitor until you got a nice image. Contrary to modern displays, CRTs didn&amp;rsquo;t have a fixed pixel size - in theory, you could draw a &amp;ldquo;pixel&amp;rdquo; that filled an entire line if you told the magnets bending the electron beam to do so.&lt;/p&gt;
&lt;p&gt;Yep, that&amp;rsquo;s it. The pixel-perfect screenshots are correct; your settings were wrong 30 years ago. Sort of.&lt;/p&gt;
&lt;p&gt;With this in mind, we can easily recreate an image resembling the original monitors&amp;rsquo; visuals. We just have to stretch the image to an aspect ratio of 4:3.&lt;/p&gt;
&lt;p&gt;The best resolution for the images rendered in 720x400 is 720x540, which perfectly hits the 4:3 ratio. Since we want to introduce as few artifacts as possible, we only stretch the image in one dimension.&lt;/p&gt;
&lt;p&gt;And suddenly, the images look perfectly how we remember them:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/05/adding-aspect-ratio-correction-to-dos-screenshots/textmode_scaled.webp&#34; alt=&#34;MS-DOS text mode output, with aspect-ratio correction applied&#34; title=&#34;MS-DOS text mode output, with aspect-ratio correction applied&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/05/adding-aspect-ratio-correction-to-dos-screenshots/scandisk_scaled.webp&#34; alt=&#34;ScanDisk, with aspect-ratio correction applied&#34; title=&#34;ScanDisk, with aspect-ratio correction applied&#34;&gt;
&lt;/p&gt;
&lt;h2 id=&#34;behind-the-scenes&#34;&gt;Behind the scenes&lt;/h2&gt;
&lt;p&gt;To illustrate what happens with the image, I prepared a little demo.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s draw an image at &lt;strong&gt;720x400&lt;/strong&gt; with alternating black and white lines, each line being exactly 1 pixel tall. The picture looks incredibly sharp - it is pixel-perfect.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/05/adding-aspect-ratio-correction-to-dos-screenshots/sample_720x400_noscale_featured.png&#34; alt=&#34;Perfect pixels. Zoom in if the lines are too blurry on your screen.&#34; title=&#34;Perfect pixels. Zoom in if the lines are too blurry on your screen.&#34;&gt;
&lt;/p&gt;
&lt;p&gt;Now, let&amp;rsquo;s stretch the image to &lt;strong&gt;720x540&lt;/strong&gt; because we want to fix it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/05/adding-aspect-ratio-correction-to-dos-screenshots/sample_720x540.png&#34; alt=&#34;Exactly the same amount of lines, but with aspect-ratio correction applied. Zoom in if the lines are too blurry on your screen.&#34; title=&#34;Exactly the same amount of lines, but with aspect-ratio correction applied. Zoom in if the lines are too blurry on your screen.&#34;&gt;
&lt;/p&gt;
&lt;p&gt;I assume you don&amp;rsquo;t want to count all the individual lines, but if you did, you&amp;rsquo;d notice that the number stayed the same. We made the pixels a bit larger, adapting them to our desired screen size. Remember, we have an &lt;em&gt;infinite&lt;/em&gt; number of possible sizes for the individual pixels.&lt;/p&gt;
&lt;h2 id=&#34;so-is-it-always-720x400&#34;&gt;So, is it always 720x400?&lt;/h2&gt;
&lt;p&gt;Unfortunatly not. VGA, the VESA standard, and custom modes used by some manufacturers &lt;a href=&#34;https://cs.lmu.edu/~ray/notes/pcvideomodes/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;allow for a wide variety of resolutions and character sizes&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;For example, the Windows 98 boot screen, technically using MS-DOS at this boot stage, is rendered at &lt;strong&gt;640x400&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/05/adding-aspect-ratio-correction-to-dos-screenshots/win98boot_640x400_noscale.webp&#34; alt=&#34;Windows 98 boot screen, unscaled&#34; title=&#34;Windows 98 boot screen, unscaled&#34;&gt;
&lt;/p&gt;
&lt;p&gt;In this case, our target resolution is &lt;strong&gt;640x480&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/05/adding-aspect-ratio-correction-to-dos-screenshots/win98boot_640x480.webp&#34; alt=&#34;Windows 98 boot screen, scaled&#34; title=&#34;Windows 98 boot screen, scaled&#34;&gt;
&lt;/p&gt;
&lt;p&gt;To make matters even worse, other graphic modes, like CGA and EGA, again support a multitude of resolutions. The general rule is to stretch your image to the closest size &lt;strong&gt;that matches the magic aspect ratio of 4:3&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Check the width of the rendered image and adjust the height accordingly. You&amp;rsquo;ll soon get used to the most common modes.&lt;/p&gt;
&lt;h2 id=&#34;one-line-of-imagemagick&#34;&gt;One line of ImageMagick&lt;/h2&gt;
&lt;p&gt;For image manipulation, my tool of choice is ImageMagick whenever possible. You can easily apply the aspect-ratio correction using the &lt;code&gt;convert&lt;/code&gt; command. All you need to know is the exact size of your source image and the target resolution.&lt;/p&gt;
&lt;p&gt;To convert the picture of the Windows 98 boot screen, I used the following command:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;convert win98boot_640x400.png -resize 640x480\! win98boot_640x480.png
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That&amp;rsquo;s it!&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>3D Pinball for Windows: The Space Cadet&#39;s Mission Continues</title>
      <link>https://felsqualle.com/posts/2024/05/3d-pinball-for-windows-space-cadet-the-mission-continues/</link>
      <pubDate>Mon, 27 May 2024 21:00:00 +0200</pubDate>
      
      <guid>https://felsqualle.com/posts/2024/05/3d-pinball-for-windows-space-cadet-the-mission-continues/</guid>
      <description>&lt;p&gt;Do you know what computer games and the Voyager space probes have in common? &lt;a href=&#34;https://blogs.nasa.gov/voyager/2024/05/22/voyager-1-resumes-sending-science-data-from-two-instruments/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Clever engineering can extend their missions way beyond their intended lifetime.&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;3D Pinball for Windows – Space Cadet, the iconic game bundled with the Microsoft Plus! packs and included in Windows NT4 up to Windows XP, is preserved for decades to come thanks to a reverse engineering project by Andrey Muzychenko.&lt;/p&gt;
&lt;p&gt;Andrey&amp;rsquo;s work involved decompiling the executable that shipped with Windows XP and &lt;a href=&#34;https://github.com/k4zmu2a/SpaceCadetPinball&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;creating a modern reimplementation using SDL2&lt;/a&gt;
. This means you can now enjoy both the bundled version and the &amp;lsquo;Full Tilt! Pinball&amp;rsquo; version on Windows, Linux and macOS.&lt;/p&gt;
&lt;p&gt;Back in the day, I learned about the game when I got it with my Windows XP installation. I remember spending way too much time with it, even during IT classes when the teacher wasn&amp;rsquo;t paying attention.&lt;/p&gt;
&lt;p&gt;The required game assets are not included in the reimplementation for legal reasons. The easiest way is to copy the original files from a Windows XP installation CD. The files are stored in the &lt;code&gt;I386&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;Copy the following files to your hard drive:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;pinball.da_
pinball.ex_
pinball.mi_
pinball2.mi_
font.da_
table.bm_
sound*.wa_
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The files are compressed using a DEFLATE algorithm. You can decompress them with 7-Zip or by using the &lt;code&gt;EXPAND&lt;/code&gt; command, which is included in basically all Windows versions up to Windows 11.&lt;/p&gt;
&lt;p&gt;After downloading the &lt;a href=&#34;https://github.com/k4zmu2a/SpaceCadetPinball/releases&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;latest release&lt;/a&gt;
 for your platform, copy the files in the release archive to the directory where you extracted the original game files.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s it! Start the game and have fun!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/05/3d-pinball-for-windows-space-cadet-the-mission-continues/pinball_space_cadet_featured.webp&#34; alt=&#34;Launch Ramp To Accept Re-Entry Training!&#34; title=&#34;Launch Ramp To Accept Re-Entry Training!&#34;&gt;
&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>A Minor Update: The Source Code for MS-DOS 4.01 Is Here</title>
      <link>https://felsqualle.com/posts/2024/05/a-minor-update-ms-dos-4-1-is-here/</link>
      <pubDate>Sat, 25 May 2024 17:30:00 +0200</pubDate>
      
      <guid>https://felsqualle.com/posts/2024/05/a-minor-update-ms-dos-4-1-is-here/</guid>
      <description>&lt;p&gt;A couple of weeks ago, Microsoft &lt;a href=&#34;https://felsqualle.com/posts/2024/05/the-broken-source-code-for-ms-dos-4-has-been-restored/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;released the source code of MS-/PC-DOS 4.00&lt;/a&gt;
 to the public.&lt;/p&gt;
&lt;p&gt;Due to various bugs, DOS 4.00 was a relatively short-lived release, and it was replaced by DOS 4.01 just a couple of months later.&lt;/p&gt;
&lt;p&gt;Howard M. Harte (hharte), who already &lt;a href=&#34;https://github.com/hharte/MS-DOS/commit/1f506100a818cb9b6c2b29aeda8d4d24d094c477&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;fixed various flaws in the official source code release of MS-DOS 4.00&lt;/a&gt;
, managed to figure out the differences between DOS 4.00 and 4.01 &amp;mdash; we now have access to the improved version as well!&lt;/p&gt;
&lt;p&gt;I imported &lt;a href=&#34;https://github.com/lotharsm/MS-DOS/commit/7e51e4249ee0e6185b4e3eed9902c5cf786f052a&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Howard&amp;rsquo;s patch&lt;/a&gt;
 into &lt;a href=&#34;https://github.com/lotharsm/MS-DOS/tree/dos-4.01-reconstructed&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;my fork of the MS-DOS source code&lt;/a&gt;
 and prepared a new source code bundle on a set of floppy disk images.&lt;/p&gt;
&lt;h2 id=&#34;downloads&#34;&gt;Downloads&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;v401_src_plain.zip&#34;&gt;MS-DOS 4.01 Source Code Archive (ZIP file)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;v401_src_1440.zip&#34;&gt;MS-DOS 4.01 Source Code Archive (4x 1.44MB, self-extracting RAR archive for DOS)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;msdos401_1440.ima&#34;&gt;MS-DOS 4.01 (compiled from source, bootable, 1x 1.44MB)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;pcdos401_1440.ima&#34;&gt;(Almost) PC-DOS 4.01 (compiled from source, bootable, 1x 1.44MB)&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can either use the files in the ZIP archive on DOSBox or try the floppy images containing a self-extracting RAR archive on real hardware or in an emulator.&lt;/p&gt;
&lt;p&gt;Please note that if you want to use the floppy images, you must copy the contents of all four disks to a new directory and run &lt;code&gt;V401_SRC.EXE&lt;/code&gt; from there, which will extract all files in the current working directory. Please note that the self-extracting RAR archive requires an 80386 or higher and 8 MB of RAM.&lt;/p&gt;
&lt;p&gt;The compiled files in &lt;code&gt;MS-DOS 4.01 (compiled from source, bootable, 1x 1.44MB)&lt;/code&gt; match the April 1989 MS-DOS 4.01 release, available at &lt;a href=&#34;https://archive.org/details/ms-dos-4.00-and-4.01&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;archive.org&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;You can flip two switches in &lt;code&gt;INC/VERSION.INC&lt;/code&gt;, which allows the IBM PC-DOS variant to be built instead of the &amp;ldquo;original&amp;rdquo; Microsoft variant. The generated binaries are available in the image &lt;code&gt;PC-DOS 4.01 (compiled from source, bootable, 1x 1.44MB)&lt;/code&gt;. However, due to compilation errors, the PC-DOS 4.01 build lacks the &lt;code&gt;XMA2EMS.SYS&lt;/code&gt; driver.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t know if this driver was meant to be included in the IBM variant or if it is an MS-DOS exclusive, so you should consider this release to be incomplete as of now.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/05/a-minor-update-ms-dos-4-1-is-here/msdos_v401_version_screenshot_featured.webp&#34; alt=&#34;MS-DOS 4.01&#34; title=&#34;MS-DOS 4.01&#34;&gt;
&lt;/p&gt;
&lt;hr&gt;</description>
    </item>
    
    <item>
      <title>The Broken Source Code for MS-DOS 4.00 Has Been Restored</title>
      <link>https://felsqualle.com/posts/2024/05/the-broken-source-code-for-ms-dos-4-has-been-restored/</link>
      <pubDate>Wed, 01 May 2024 18:00:00 +0200</pubDate>
      
      <guid>https://felsqualle.com/posts/2024/05/the-broken-source-code-for-ms-dos-4-has-been-restored/</guid>
      <description>&lt;p&gt;On April 25, 2024, Microsoft &lt;a href=&#34;https://cloudblogs.microsoft.com/opensource/2024/04/25/open-sourcing-ms-dos-4-0/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;released the source code of MS-/PC-DOS 4.00&lt;/a&gt;
 to the public.&lt;/p&gt;
&lt;p&gt;While this release is exciting and essential to computing history, Microsoft&amp;rsquo;s release process was not entirely flawless: An &lt;a href=&#34;https://www.os2museum.com/wp/how-not-to-release-historic-source-code/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;accidental conversion from codepage 437 to UTF-8&lt;/a&gt;
 destroyed parts of the source code and caused build failures.&lt;/p&gt;
&lt;p&gt;In less than a week, on April 30, 2024, the community provided all necessary patches to restore the source code to its original state, matching the 1988 release of MS-DOS 4.00.&lt;/p&gt;
&lt;p&gt;To get a working source tree for myself, I imported the patches by &lt;a href=&#34;https://github.com/hharte/MS-DOS/commit/1f506100a818cb9b6c2b29aeda8d4d24d094c477&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Howard M. Harte (hharte)&lt;/a&gt;
 and &lt;a href=&#34;https://hg.pushbx.org/ecm/msdos4/rev/63a05668c5f3&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;E. C. Masloch (ecm)&lt;/a&gt;
 to &lt;a href=&#34;https://github.com/lotharsm/MS-DOS/commits/dos-4.00-reconstructed/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;my MS-DOS fork on GitHub&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;Additionally, I converted the source files from LF to CRLF because it is very likely that all source files were originally stored with CRLF line endings. A fix to the SETENV.BAT batch file was also applied to fix broken include paths. I want to make absolutely clear that all changes that brought the code to a working, complete state are &lt;em&gt;not&lt;/em&gt; mine and I was not involved in figuring out the correct code fixes.&lt;/p&gt;
&lt;h2 id=&#34;downloads&#34;&gt;Downloads&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;v400_src_plain.zip&#34;&gt;MS-DOS 4.00 Source Code Archive (ZIP file)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;v400_src_1440.zip&#34;&gt;MS-DOS 4.00 Source Code Archive (4x 1.44MB, self-extracting RAR archive for DOS)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;msdos400_1440.ima&#34;&gt;MS-DOS 4.00 (compiled from source, bootable, 1x 1.44MB)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;pcdos400_1440.ima&#34;&gt;(Almost) PC-DOS 4.00 (compiled from source, bootable, 1x 1.44MB)&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can either use the files in the ZIP archive on DOSBox or try the floppy images containing a self-extracting RAR archive on real hardware or in an emulator.&lt;/p&gt;
&lt;p&gt;Please note that if you want to use the floppy images, you must copy the contents of all four disks to a new directory and run &lt;code&gt;V400_SRC.EXE&lt;/code&gt; from there, which will extract all files in the current working directory. Please note that the self-extracting RAR archive requires an 80386 or higher and 8 MB of RAM.&lt;/p&gt;
&lt;p&gt;The compiled files in &lt;code&gt;MS-DOS 4.00 (compiled from source, bootable, 1x 1.44MB)&lt;/code&gt; match the October 1988 MS-DOS 4.00 release, available at &lt;a href=&#34;https://archive.org/details/ms-dos-4.00-and-4.01&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;archive.org&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;You can flip two switches in &lt;code&gt;INC/VERSION.INC&lt;/code&gt;, which allows building the IBM PC-DOS variant instead of the &amp;ldquo;original&amp;rdquo; Microsoft variant. The generated binaries are available in the image &lt;code&gt;PC-DOS 4.00 (compiled from source, bootable, 1x 1.44MB)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;However, this build comes with an interesting oddity. The boot loader and &lt;code&gt;SYS.COM&lt;/code&gt; expect &lt;code&gt;IBMBIO.COM&lt;/code&gt; and &lt;code&gt;IBMDOS.COM&lt;/code&gt; instead of &lt;code&gt;IO.SYS&lt;/code&gt; and &lt;code&gt;MSDOS.SYS&lt;/code&gt;, and there are multiple checksum mismatches, so we get a different build.&lt;/p&gt;
&lt;p&gt;But here&amp;rsquo;s the catch: Booting the system and running &lt;code&gt;VER&lt;/code&gt; shows the Microsoft copyright notice instead of the IBM one &amp;mdash; there&amp;rsquo;s no matching entry in &lt;code&gt;MESSAGES/USA-MS.MSG&lt;/code&gt;.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>The Making of the MS-DOS Command Reference Bot</title>
      <link>https://felsqualle.com/posts/2024/01/making-of-msdosreference-mastodon-bot/</link>
      <pubDate>Sun, 21 Jan 2024 12:00:00 +0100</pubDate>
      
      <guid>https://felsqualle.com/posts/2024/01/making-of-msdosreference-mastodon-bot/</guid>
      <description>&lt;h2 id=&#34;a-brief-introduction&#34;&gt;A brief introduction&lt;/h2&gt;
&lt;p&gt;I launched my &lt;a href=&#34;https://manitu.social/@msdosreference&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Daily MS-DOS command&lt;/a&gt;
 bot a few months ago.&lt;/p&gt;
&lt;p&gt;The initial idea was straightforward: Pick a random command or program that ships with MS-DOS 6.22 and post a brief description.&lt;/p&gt;
&lt;p&gt;With a bit of help from some websites that archived the MS-DOS reference manual, I finished the initial revision of the JSON-based data file including all the comments with a short description in a matter of hours. I wrote a simple Bash script to pick a random command, fetch the description from the JSON file, and publish a post through the Mastodon API.&lt;/p&gt;
&lt;p&gt;Easy. But shortly after that, a question arose: &amp;ldquo;What about screenshots?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;At first, I thought about going through every command in my data file, spinning up a VM, executing the command, taking a screenshot, and linking to it in the data file itself. While this approach is possible, it is tedious. There&amp;rsquo;s no real challenge in repeatedly typing in commands and trying to keep track of the screenshots I already took. Moreover, any change in the command list would require spinning up a VM, executing the command, taking a screenshot, and modifying the data file to include the modified screenshot.&lt;/p&gt;
&lt;p&gt;But what if I could do all of this &lt;em&gt;dynamically&lt;/em&gt; by taking screenshots at the moment my MS-DOS VM is executing the command I requested?&lt;/p&gt;
&lt;h2 id=&#34;setting-up-the-ms-dos-622-virtual-machine&#34;&gt;Setting up the MS-DOS 6.22 virtual machine&lt;/h2&gt;
&lt;p&gt;The first step was to create a virtual machine with QEMU to run the commands from the data file. For this scenario, QEMU works exceptionally well thanks to the built-in monitor interface that will allow interacting with the VM from the host system.&lt;/p&gt;
&lt;p&gt;I started by creating an image file that will be the virtual hard drive for the MS-DOS installation.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;qemu-img create msdos622.img 512M
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A basic installation of MS-DOS 6.22 consists of three floppy images. The first floppy is bootable and will start the installation process. For convenience, I named the floppy images &lt;code&gt;Disk1.img&lt;/code&gt;, &lt;code&gt;Disk2.img&lt;/code&gt;, and &lt;code&gt;Disk3.img&lt;/code&gt; in the same order the setup program will request them.&lt;/p&gt;
&lt;p&gt;I went with a very basic 486-based VM with a whopping RAM size of 8 MB - this should be enough for getting all the included tools up and running.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;qemu-system-i386 -drive format=raw,file=Disk1.img,index=0,if=floppy -drive format=raw,file=msdos622.img,index=0,if=ide -m 8 -cpu 486
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After starting QEMU, the VM booted from the floppy image &lt;code&gt;Disk1.img&lt;/code&gt; and initiated the installer.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/01/making-of-msdosreference-mastodon-bot/msdosreference_dossetup_1.webp&#34; alt=&#34;MS-DOS 6.22: Initial setup screen&#34; title=&#34;MS-DOS 6.22: Initial setup screen&#34;&gt;
&lt;/p&gt;
&lt;p&gt;Guided by the installer, I partitioned the virtual hard drive and let the installer start copying the contents from the floppy image to the virtual hard drive. Once the installer is done copying the files from the first floppy, it will request the user to insert the second floppy into the disk drive.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/01/making-of-msdosreference-mastodon-bot/msdosreference_dossetup_2.webp&#34; alt=&#34;Swapping floppy disks&#34; title=&#34;Swapping floppy disks&#34;&gt;
&lt;/p&gt;
&lt;p&gt;To replace &lt;code&gt;Disk1.img&lt;/code&gt; with &lt;code&gt;Disk2.img&lt;/code&gt; (and &lt;code&gt;Disk3.img&lt;/code&gt; afterwards) in the VM, I switched to the QEMU monitor by pressing &lt;code&gt;Ctrl+Alt+2&lt;/code&gt; inside the QEMU window. The QEMU monitor gives us complete control over the VM, so replacing the floppy image is possible with the following command:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;change floppy0 Disk2.img
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With the new floppy image in place, I switched back to the VM with &lt;code&gt;Ctrl+Alt+1&lt;/code&gt; and let the setup continue. Inserting the third floppy image works in the exact same way.&lt;/p&gt;
&lt;p&gt;Once the installer completes processing all three floppies, it asks the user to eject the floppy from the drive, ensuring that the machine will boot from the fresh installation instead of the floppy drive.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/01/making-of-msdosreference-mastodon-bot/msdosreference_dossetup_3.webp&#34; alt=&#34;Time to reboot!&#34; title=&#34;Time to reboot!&#34;&gt;
&lt;/p&gt;
&lt;p&gt;Using the QEMU monitor again, ejecting the floppy image is as easy as&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;eject floppy0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;One reboot later, we have the new installation up and running. Success!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/01/making-of-msdosreference-mastodon-bot/msdosreference_dosboot.webp&#34; alt=&#34;Hello there, DOS!&#34; title=&#34;Hello there, DOS!&#34;&gt;
&lt;/p&gt;
&lt;h2 id=&#34;connecting-to-the-monitor&#34;&gt;Connecting to the monitor&lt;/h2&gt;
&lt;p&gt;After the initial setup, it is time to hook up the QEMU monitor to the host system.&lt;/p&gt;
&lt;p&gt;Thankfully, QEMU provides a way to connect to the QEMU monitor via the network so that I can send arbitrary commands to the VM. Additionally, I switched the virtual console to text-only mode because the bot and thus QEMU is running on a server without a graphical interface.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;qemu-system-i386 -hda msdos622.img -m 8 -cpu 486 -monitor tcp:localhost:9999,server,nowait -nographic &amp;amp;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Starting the VM this way will expose the QEMU monitor to port &lt;code&gt;9999&lt;/code&gt; of the host machine. With the monitor exposed, the host machine can send the commands to port &lt;code&gt;9999&lt;/code&gt; using tools like &lt;code&gt;nc&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;QEMU&amp;rsquo;s &lt;code&gt;sendkey&lt;/code&gt; command will only accept one character at a time, which is not ideal. One way to mitigate this problem would be splitting the DOS commands into single characters. The much more convenient way is using a script called &lt;a href=&#34;https://gist.github.com/zambonin/fb195abe7ef2e7530819b9baea9bb99b&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;sendkeys.awk&lt;/a&gt;
 by &lt;a href=&#34;https://gist.github.com/zambonin&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;zambonin&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;This script accepts full strings and split them into single characters, including some conversion magic:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ echo &amp;#34;ver&amp;#34; | awk -f sendkeys.awk | timeout 1 nc localhost 9999
QEMU 8.2.0 monitor - type &amp;#39;help&amp;#39; for more information
(qemu) sendkey v
(qemu) vsendkey e
(qemu) esendkey r
(qemu) rsendkey ret
(qemu)
...
MS-DOS Version 6.22


C:\&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With the DOS VM listening to the input, it is time to take care of the output!&lt;/p&gt;
&lt;h2 id=&#34;creating-screenshots-with-the-screendump-command&#34;&gt;Creating screenshots with the screendump command&lt;/h2&gt;
&lt;p&gt;QEMU provides the &lt;code&gt;screendump&lt;/code&gt; command through the monitor, which does exactly what I needed to create the screenshots. The command creates a bitmap file in the &lt;a href=&#34;https://netpbm.sourceforge.net/doc/ppm.html&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;PPM&lt;/a&gt;
 format, which resembles a simple bitmap file.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/01/making-of-msdosreference-mastodon-bot/msdosreference_screendump.webp&#34; alt=&#34;Screenshot taken with the screendump command&#34; title=&#34;Screenshot taken with the screendump command&#34;&gt;
&lt;/p&gt;
&lt;p&gt;Because this format is pretty non-standard nowadays and incompatible with most web applications, the best way to convert the files is with the &lt;code&gt;convert&lt;/code&gt; utility provided by &lt;a href=&#34;https://imagemagick.org/index.php&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;ImageMagick&lt;/a&gt;
.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;convert ms-dos-vm-screendump.ppm ms-dos-vm-screendump.webp
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;the-json-data-file&#34;&gt;The JSON data file&lt;/h2&gt;
&lt;p&gt;As mentioned earlier, I am using a JSON file to store the commands along with their descriptions. Each entry in the data file follows the same pattern:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;  {
    &amp;#34;command&amp;#34;: &amp;#34;SYS&amp;#34;,
    &amp;#34;description&amp;#34;: &amp;#34;Copies MS-DOS system files to a disk&amp;#39;s master boot record.&amp;#34;,
    &amp;#34;verbose_description&amp;#34;: &amp;#34;The SYS command is used to copy MS-DOS system files to a disk&amp;#39;s master boot record (MBR), making it bootable. It&amp;#39;s often used to create bootable system disks for MS-DOS installations.&amp;#34;,
    &amp;#34;show_help&amp;#34;: &amp;#34;yes&amp;#34;
  },
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Each message the bot posts contains the command, the description and a more verbose description in a second paragraph. If &lt;code&gt;show_help&lt;/code&gt; is set to &lt;code&gt;yes&lt;/code&gt;, the bot passes the &lt;code&gt;/?&lt;/code&gt; argument to &lt;code&gt;command&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I use this option for commands that provide no meaningful screen output when invoked without additional parameters like files or disk drives.&lt;/p&gt;
&lt;h2 id=&#34;the-script&#34;&gt;The script&lt;/h2&gt;
&lt;p&gt;With the VM prepared and the JSON file in place, there is only one step left: Writing a simple script that picks a command, launches the VM, grabs the screenshot, and uploads it through the Mastodon API.&lt;/p&gt;
&lt;p&gt;Because the content of the JSON file is dynamic and can change quite often, I want to do as little hard coding as possible. First, I check the length of the JSON file to see how many commands the script can pick from.&lt;/p&gt;
&lt;p&gt;After picking a random command, the individual elements for the JSON entry are concatenated into the final string &lt;code&gt;QUOTE&lt;/code&gt;, which contains the complete post message.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# Calculate number of entries in json datafile
MAXNUMBER=$(jq &amp;#39;.[].command&amp;#39; dosreference.json | wc -l)
MAXNUMBER=&amp;#34;$((MAXNUMBER-1))&amp;#34;
RANDOMNUMBER=$(shuf -i 0-$MAXNUMBER -n 1)

# Fetch random entry from json datafile
COMMAND=$(jq -r --argjson randomnumber &amp;#34;${RANDOMNUMBER}&amp;#34; &amp;#39;.[$randomnumber].command&amp;#39; dosreference.json)
COMMAND_DESCRIPTION=&amp;#34;$(jq -r --argjson randomnumber &amp;#34;${RANDOMNUMBER}&amp;#34; &amp;#39;.[$randomnumber].description&amp;#39; dosreference.json) \n\n&amp;#34;
COMMAND_VERBOSE_DESCRIPTION=&amp;#34;$(jq -r --argjson randomnumber &amp;#34;${RANDOMNUMBER}&amp;#34; &amp;#39;.[$randomnumber].verbose_description&amp;#39; dosreference.json)&amp;#34;
COMMAND_SHOW_HELP=&amp;#34;$(jq -r --argjson randomnumber &amp;#34;${RANDOMNUMBER}&amp;#34; &amp;#39;.[$randomnumber].show_help&amp;#39; dosreference.json)&amp;#34;
QUOTE=&amp;#34;status=$(echo &amp;#34;${COMMAND}&amp;#34;: &amp;#34;${COMMAND_DESCRIPTION}&amp;#34;&amp;#34;${COMMAND_VERBOSE_DESCRIPTION}&amp;#34;)&amp;#34;

if [ &amp;#34;$COMMAND_SHOW_HELP&amp;#34; = &amp;#34;yes&amp;#34; ]; then
    COMMAND=&amp;#34;${COMMAND} /?&amp;#34;
fi
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Then, I start the VM and pass the command crafted from the JSON output to it.&lt;/p&gt;
&lt;p&gt;After taking the screenshot, the VM is killed through the monitor, ready to relaunched the next time the cron job responsible for the script is triggered.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;qemu-system-i386 -hda msdos622.img -m 8 -cpu 486 -monitor tcp:localhost:9999,server,nowait -nographic &amp;amp;
sleep 30

# Execute command in VM
echo &amp;#34;cls&amp;#34;        | awk -f qemu-sendkeys.awk | timeout 1 nc localhost 9999
echo &amp;#34;${COMMAND}&amp;#34; | awk -f qemu-sendkeys.awk | timeout 5 nc localhost 9999

# Create screenshot of VM
echo &amp;#34;screendump ms-dos-vm-screendump.ppm&amp;#34; | timeout 1 nc localhost 9999

# Kill VM
echo &amp;#34;quit&amp;#34; | timeout 1 nc localhost 9999
convert ms-dos-vm-screendump.ppm ms-dos-vm-screendump.webp
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The timing here is completely hard-coded because I cannot determine if the VM has fully launched. Since the server hosting the bot also provides some other services, I went with a safe timeout of 30 seconds - this should be well enough to get a bare MS-DOS VM ready. The timeouts for the calls to &lt;code&gt;nc&lt;/code&gt; are required, though - without it, &lt;code&gt;nc&lt;/code&gt; would wait &amp;rsquo;til eternity for me to hit the &amp;ldquo;Enter&amp;rdquo; key.&lt;/p&gt;
&lt;p&gt;The final step: Upload the screenshot to Mastodon, wait for it to process, and attach the post message.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# Upload image to Mastodon
MEDIA_ID=$(curl -s -X POST -H &amp;#34;Content-Type: multipart/form-data&amp;#34; https://manitu.social/api/v2/media -H &amp;#39;Authorization: Bearer XXXXXXXXXXXXXXXX&amp;#39; --form file=&amp;#34;@ms-dos-vm-screendump.webp&amp;#34; | jq -r &amp;#39;.id&amp;#39;)

# Wait for the image to process...
sleep 10

# Go!
curl -g https://manitu.social/api/v1/statuses -H &amp;#34;Authorization: Bearer XXXXXXXXXXXXXXXX&amp;#34; -F &amp;#34;$QUOTE&amp;#34; -F &amp;#34;media_ids[]=${MEDIA_ID}&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And we are done!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2024/01/making-of-msdosreference-mastodon-bot/msdosreference_featured.webp&#34; alt=&#34;MS-DOS command of the day: SYS!&#34; title=&#34;MS-DOS command of the day: SYS!&#34;&gt;
&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Connecting MS-DOS 6.22 to the internet --- and the fediverse</title>
      <link>https://felsqualle.com/posts/2023/08/connecting-ms-dos-to-the-internet-and-the-fediverse/</link>
      <pubDate>Sat, 05 Aug 2023 22:00:00 +0200</pubDate>
      
      <guid>https://felsqualle.com/posts/2023/08/connecting-ms-dos-to-the-internet-and-the-fediverse/</guid>
      <description>&lt;p&gt;Microsoft released the last standalone version of MS-DOS as MS-DOS 6.22 in 1994.&lt;/p&gt;
&lt;p&gt;Even though this operating system is decades old and a true relic of the past, people still develop new software for it. Thanks to a couple of modern tools, we can connect MS-DOS to the internet. Since just having a connection to the outside world itself is pretty boring, we connect MS-DOS 6.22 to something fairly modern and amazing: the fediverse.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s have a look.&lt;/p&gt;
&lt;h1 id=&#34;a-little-bit-of-history&#34;&gt;A little bit of history&lt;/h1&gt;
&lt;p&gt;Today, getting connected to the internet is very simple: Just enable your WiFi or connect an ethernet cable and you are good to go. In many cases, everything just works without having to configure anything.&lt;/p&gt;
&lt;p&gt;Modern systems include all the drivers and tools you need for an internet connection right out of the box.&lt;/p&gt;
&lt;p&gt;Back then, things were not &lt;em&gt;that&lt;/em&gt; easy.&lt;/p&gt;
&lt;p&gt;For connecting a machine running MS-DOS to the internet, you needed the matching packet driver for your network card and a set of tools for providing the TCP/IP stack. Yes, TCP/IP is not the only way to do networking, and since other protocols were a lot more popular in the world of small to medium-sized businesses, it was a common practice to ship the OS without any networking capabilities so you could install whatever software your network required.&lt;/p&gt;
&lt;h1 id=&#34;setting-up-the-connection&#34;&gt;Setting up the connection&lt;/h1&gt;
&lt;p&gt;Thanks to emulation, hardware compatibility isn&amp;rsquo;t a problem anymore, so we can just emulate a network card compatible with the widespread NE2000 standard which &lt;a href=&#34;https://86box.net/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;86Box&lt;/a&gt;
 conveniently supports.&lt;/p&gt;
&lt;p&gt;As I mentioned before, we need both the networking driver and the TCP/IP stack. For the driver, we are going to use the NE2000/NE2100 driver available at &lt;a href=&#34;http://www.georgpotthast.de/sioux/packet.htm&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;http://www.georgpotthast.de/sioux/packet.htm&lt;/a&gt;
. Support for TCP/IP will be provided by the &lt;a href=&#34;http://brutmanlabs.org/mTCP/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;mTCP toolset&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;If you are mainly interested in connecting MS-DOS to the fediverse, you only need the packet driver, since DOStodon, the software we will be using, includes a full TCP/IP stack. However, if you want to use your brand new Internet connection for other software, I recommend that you install mTCP as well.&lt;/p&gt;
&lt;p&gt;In 86Box, we can use SLiRP for connection handling, so we don&amp;rsquo;t need to worry about it on your host machine at all. SLiRP&amp;rsquo;s virtual router is able to provide dynamic networking configuration with DHCP, making it the perfect match for mTCP&amp;rsquo;s DHCP client.&lt;/p&gt;
&lt;p&gt;To add the virtual network card to the VM, open the VM settings and use the following configuration:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Mode: SLiRP&lt;/li&gt;
&lt;li&gt;Adapter: [ISA16] Novell NE2000&lt;/li&gt;
&lt;li&gt;Address: 0x300&lt;/li&gt;
&lt;li&gt;IRQ: IRQ 3 (or any other IRQ if IRQ 3 is already used by other virtual cards)&lt;/li&gt;
&lt;li&gt;BIOS address: Disabled&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/08/connecting-ms-dos-to-the-internet-and-the-fediverse/86box_network_card_setup.webp&#34; alt=&#34;86Box: Network card configuration&#34; title=&#34;86Box: Network card configuration&#34;&gt;
&lt;/p&gt;
&lt;p&gt;The hardware is done, now to the software side of things!&lt;/p&gt;
&lt;h1 id=&#34;part-1-the-internet&#34;&gt;Part 1: The internet.&lt;/h1&gt;
&lt;p&gt;To add some life to the virtual network card, we extract the .ZIP archives of the NE2000/NE2100 driver and the mTCP package. The software is pretty small and will fit on a single 1.44MB floppy disk. For this experiment, I created a floppy image with WinImage and copied all the files to it.&lt;/p&gt;
&lt;div class=&#34;notice info&#34; &gt;
&lt;p class=&#34;first notice-title&#34;&gt;&lt;span class=&#34;icon-notice baseline&#34;&gt;&lt;svg&gt;&lt;use href=&#34;#info-notice&#34;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Do not skip the &lt;code&gt;SAMPLES&lt;/code&gt; directory of the mTCP package - we&amp;rsquo;ll need it later!&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;Installing the networking stack is pretty easy. After booting the VM, we mount the floppy image containing the driver and mTCP to the VM and copy everything to the &lt;code&gt;C:\NET&lt;/code&gt; directory.&lt;/p&gt;
&lt;p&gt;After copying the files, it is time to check if the networking driver works. To do so, we try to load the NE2000 driver manually:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;C:\NET\NE2000.COM 0x60 3 0x300
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/08/connecting-ms-dos-to-the-internet-and-the-fediverse/msdos_6_22_ne2000_loading.webp&#34; alt=&#34;The NE2000 driver is working!&#34; title=&#34;The NE2000 driver is working!&#34;&gt;
&lt;/p&gt;
&lt;p&gt;Great, the driver found the network card!&lt;/p&gt;
&lt;p&gt;Now, we can try to get an IP address. First, we have to copy the file &lt;code&gt;SAMPLES\SAMPLE.CFG&lt;/code&gt; to &lt;code&gt;C:\NET\MTCP.CFG&lt;/code&gt;. Since we are using only the DHCP client and none of the other services (like an FTP or HTTP server) from the mTCP package, the default configuration works just fine.&lt;/p&gt;
&lt;p&gt;Prior to using mTCP, we have to tell it where the configuration file is located by using the &lt;code&gt;SET MTCPCFG=C:\NET\MTCP.CFG&lt;/code&gt; command. Finally, we can try to get an IP address.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/08/connecting-ms-dos-to-the-internet-and-the-fediverse/msdos_6_22_ip_assigned.webp&#34; alt=&#34;Success, we have an IP address!&#34; title=&#34;Success, we have an IP address!&#34;&gt;
&lt;/p&gt;
&lt;p&gt;As you can see, the DHCP server provided by the virtual router assigned an IP address to the VM. The DHCP server doesn&amp;rsquo;t seem to support requesting a hostname, but for our purposes, this won&amp;rsquo;t matter at all.&lt;/p&gt;
&lt;p&gt;Time to test if outbound connections work: &lt;code&gt;C:\NET\PING.EXE felsqualle.com&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/08/connecting-ms-dos-to-the-internet-and-the-fediverse/msdos_6_22_ping_working.webp&#34; alt=&#34;Hello there!&#34; title=&#34;Hello there!&#34;&gt;
&lt;/p&gt;
&lt;p&gt;Now that we confirmed that the setup we want to run is working properly, it is time to auto-load it in the &lt;code&gt;AUTOEXEC.BAT&lt;/code&gt; file with the following modifications:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;SET PATH=C:\DOS;C:\NET
SET MTCPCFG=C:\NET\MTCP.CFG
LH C:\NET\NE2000.COM 0x60 3 0x300
C:\NET\DHCP.EXE
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;These modifications will ensure that the networking stack will be loaded each time the system starts up.&lt;/p&gt;
&lt;h1 id=&#34;part-2-the-fediverse&#34;&gt;Part 2: The fediverse.&lt;/h1&gt;
&lt;p&gt;Now that we have a working internet connection, it is time to connect MS-DOS to the fediverse.&lt;/p&gt;
&lt;p&gt;Let me introduce you to &lt;a href=&#34;https://github.com/SuperIlu/DOStodon&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;DOStodon&lt;/a&gt;
, a Mastodon client for MS-DOS, created by &lt;a href=&#34;https://github.com/SuperIlu&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;SuperIlu&lt;/a&gt;
. DOStodon is powered by &lt;a href=&#34;https://github.com/SuperIlu/DOjS&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;DOjS&lt;/a&gt;
, a JavaScript implementation for MS-DOS which is also created by SuperIlu.&lt;/p&gt;
&lt;p&gt;Installing DOStodon is very simple. All we have to do is to get a &lt;a href=&#34;https://github.com/SuperIlu/DOStodon/archive/refs/heads/main.zip&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;copy of the git repository&lt;/a&gt;
, which includes the necessary program files already compiled.&lt;/p&gt;
&lt;p&gt;I transferred the extracted files to an ISO image and mounted the image in the virtual machine. Then, I copied all files to a new directory.&lt;/p&gt;
&lt;p&gt;When starting DOStodon for the first time, we have to pass the login credentials as arguments. Since DOStodon stores the login information, we only have to do this on the first start.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;C:\PROGRAMS\DOSTODON\DOSTODON.BAT example.com youraddress@example.com yourPassword123`
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After a couple of seconds, DOStodon starts up and shows us the Home timeline. As you&amp;rsquo;ll see in the next couple of screenshots, this is not just a simple tech demo. DOStodon is a complete Mastodon client with all the timelines, hashtag search, and a compositor for new posts and replies.&lt;/p&gt;
&lt;p&gt;And because a picture is worth a thousand words, I&amp;rsquo;ll close this article with some screenshots.&lt;/p&gt;
&lt;p&gt;Enjoy!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/08/connecting-ms-dos-to-the-internet-and-the-fediverse/msdos_6_22_dostodon_home_featured.webp&#34; alt=&#34;Home timeline&#34; title=&#34;Home timeline&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/08/connecting-ms-dos-to-the-internet-and-the-fediverse/msdos_6_22_dostodon_notifications.webp&#34; alt=&#34;Notifications&#34; title=&#34;Notifications&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/08/connecting-ms-dos-to-the-internet-and-the-fediverse/msdos_6_22_dostodon_tags.webp&#34; alt=&#34;Tag search&#34; title=&#34;Tag search&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/08/connecting-ms-dos-to-the-internet-and-the-fediverse/msdos_6_22_dostodon_compositor.webp&#34; alt=&#34;Creating new posts&amp;hellip;&#34; title=&#34;Creating new posts...&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/08/connecting-ms-dos-to-the-internet-and-the-fediverse/msdos_6_22_dostodon_reply_editor.webp&#34; alt=&#34;&amp;hellip;and replies!&#34; title=&#34;...and replies&#34;&gt;
&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Smashing the limits: Installing Windows XP in DOSBox-X</title>
      <link>https://felsqualle.com/posts/2023/07/installing-windows-xp-in-dosbox-x/</link>
      <pubDate>Wed, 26 Jul 2023 00:00:00 +0200</pubDate>
      
      <guid>https://felsqualle.com/posts/2023/07/installing-windows-xp-in-dosbox-x/</guid>
      <description>&lt;p&gt;In my &lt;a href=&#34;https://felsqualle.com/posts/2023/07/installing-windows-2000-in-dosbox-x/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;previous article&lt;/a&gt;
, I described how I managed to install Windows 2000 in DOSBox-X.&lt;/p&gt;
&lt;p&gt;Even though this experiment was successful, I was not really happy with the results. While I got Windows 2000 working, I didn&amp;rsquo;t want to stop there. The final goal for the project was to get Windows XP running instead. However, after multiple attempts I gave up, thinking that Windows XP was impossible to use.&lt;/p&gt;
&lt;p&gt;Well - I was wrong. But let&amp;rsquo;s start at the beginning.&lt;/p&gt;
&lt;div class=&#34;notice info&#34; &gt;
&lt;p class=&#34;first notice-title&#34;&gt;&lt;span class=&#34;icon-notice baseline&#34;&gt;&lt;svg&gt;&lt;use href=&#34;#info-notice&#34;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;At the time of writing this article, running Windows XP isn&amp;rsquo;t officially supported by the DOSBox-X project. Don&amp;rsquo;t blame the developers for any issues - this is very experimental.&lt;/p&gt;&lt;/div&gt;

&lt;h2 id=&#34;previously&#34;&gt;Previously&amp;hellip;&lt;/h2&gt;
&lt;p&gt;Since I wasn&amp;rsquo;t able to do a clean install of Windows 2000 due to driver and compatibility issues as described in my previous article, I started with the upgrade route right away. Upgrading from Windows 98 to Windows XP is officially supported, so I was pretty confident this would work for me as well.&lt;/p&gt;
&lt;p&gt;I created a copy of the hard drive image after each step, so I was able to start the upgrade procedure with a clean installation of Windows 98 (Second Edition).&lt;/p&gt;
&lt;p&gt;After mounting the Windows XP ISO image, I was greeted by the Windows XP Setup.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-xp-in-dosbox-x/windows_xp_upgrade_clean_selection.webp&#34; alt=&#34;Windows XP Setup: Select between upgrade and clean install&#34; title=&#34;Windows XP Setup: Select between upgrade and clean install&#34;&gt;
&lt;/p&gt;
&lt;p&gt;Because of the issues I had with the clean installation of Windows 2000, I used the &amp;ldquo;Upgrade&amp;rdquo; installation type instead because that&amp;rsquo;s what worked for Windows 2000 as well.&lt;/p&gt;
&lt;p&gt;After typing in the product key, the Setup was about to start the file copy process. In fact, it even started copying the installation files but failed at the very first file.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-xp-in-dosbox-x/windows_98_xp_setup_error.webp&#34; alt=&#34;Windows XP Setup failing to copy files during the upgrade&#34; title=&#34;Windows XP Setup failing to copy files during the upgrade&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&amp;ldquo;The ISO must be bad!&amp;rdquo; I said to myself. I obtained this ISO file many years ago, so some silent file corruption was not entirely impossible. To avoid any potential issues caused by bad images, I always create checksum files immediately after obtaining the image. So, I compared the ISO image with the checksums I had - and to my surprise, I got a perfect match. The ISO image itself is absolutely fine since I have used it multiple times before.&lt;/p&gt;
&lt;p&gt;To further investigate, I even went ahead and reinstalled Windows 98 from scratch to rule out any possible installation issues, but to my dismay, I encountered the same error message again.&lt;/p&gt;
&lt;p&gt;Great. Getting another ISO image or starting with a fresh installation would have been &lt;em&gt;way&lt;/em&gt; too easy, right?!&lt;/p&gt;
&lt;h2 id=&#34;back-to-square-one&#34;&gt;Back to square one.&lt;/h2&gt;
&lt;p&gt;Since the upgrade didn&amp;rsquo;t work as planned, I decided to proceed with a clean installation instead. Unfortunately, I encountered difficulties booting DOSBox-X from the ISO image. As a workaround, I launched the Setup from within another fresh Windows 98 installation just like I did for Windows 2000.&lt;/p&gt;
&lt;p&gt;After my previous experience with the Windows XP Setup, I mentally prepared myself for yet another potential failure. However, to my surprise, this time the Setup didn&amp;rsquo;t just throw an error message at me; instead, it smoothly proceeded to copy over the temporary installation files. Crisis averted!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-xp-in-dosbox-x/windows_98_xp_setup_clean_copy_files.webp&#34; alt=&#34;Windows XP Setup copying files, started from Windows 98&#34; title=&#34;Windows XP Setup copying files, started from Windows 98&#34;&gt;
&lt;/p&gt;
&lt;p&gt;One reboot later, I was greeted by the beloved and very familiar blue hue of the Windows XP Setup.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-xp-in-dosbox-x/windows_xp_text_installer.webp&#34; alt=&#34;Windows XP Setup starting at the initial stage&#34; title=&#34;Windows XP Setup starting at the initial stage&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-xp-in-dosbox-x/windows_xp_text_installer_warning.webp&#34; alt=&#34;Windows XP Setup: Yes, I know we have a previous version installed&#34; title=&#34;Windows XP Setup: Yes, I know we have a previous version installed&#34;&gt;
&lt;/p&gt;
&lt;p&gt;And now - the moment of truth. After confirming that we want to overwrite the existing Windows installation, Windows XP Setup started the next step of the installation by copying over the temporary installation files to the Windows directory.&lt;/p&gt;
&lt;p&gt;I can&amp;rsquo;t say that I was surprised when the project stumbled across yet another issue. After copying over some files atrociously slow, it failed. No matter how often I retried, it always failed. I couldn&amp;rsquo;t track down the issue to one particular file though because on subsequent runs (yes, I tried!), it failed at a &lt;em&gt;different&lt;/em&gt; file.&lt;/p&gt;
&lt;p&gt;Based on the experience I had with the Windows 2000 installer I &lt;em&gt;think&lt;/em&gt; this is related to either the IDE driver or the emulation of the IDE controller and the devices are simply dropping out of the system.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-xp-in-dosbox-x/windows_xp_text_installer_copy_error.webp&#34; alt=&#34;Windows XP Setup failing to copy files&#34; title=&#34;Windows XP Setup failing to copy files&#34;&gt;
&lt;/p&gt;
&lt;p&gt;The next obvious way to solve this issue was to reboot the system and try to run Setup again. Unfortunately, this wasn&amp;rsquo;t an option either because after a reboot it simply failed to copy the very first file - turns out that Windows Setup already started to overwrite vital parts of the previous installation.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-xp-in-dosbox-x/windows_xp_text_installer_copy_error_after_reboot.webp&#34; alt=&#34;Windows XP Setup failing to copy any file after a reboot&#34; title=&#34;Windows XP Setup failing to copy any file after a reboot&#34;&gt;
&lt;/p&gt;
&lt;p&gt;At this point, I gave up. Running Windows XP in DOSBox-X clearly wasn&amp;rsquo;t possible. Heck, I couldn&amp;rsquo;t even get the installation to work!&lt;/p&gt;
&lt;h2 id=&#34;new-technology-from-1999&#34;&gt;New Technology from 1999&lt;/h2&gt;
&lt;p&gt;&amp;ldquo;It is the NT conversion, stupid!&amp;rdquo;, I yelled at myself a couple of days later.&lt;/p&gt;
&lt;p&gt;While Windows 98 is using MS-DOS to bootstrap the system and uses parts of it in its kernel, Windows XP uses the Windows NT platform instead. Just like Windows 2000!&lt;/p&gt;
&lt;p&gt;What if swapping out the kernel requires some sort of low-level access I don&amp;rsquo;t have in DOSBox-X?&lt;/p&gt;
&lt;p&gt;So I installed Windows 98 again, updated it to Windows 2000, fixed the missing taskbar, verified the system was working, mounted the Windows XP ISO, and started the installer again.&lt;/p&gt;
&lt;p&gt;Because I was afraid that a clean installation would fail again, I went with the upgrade route once more. Not really knowing what to expect, I clicked on the magic button and waited.&lt;/p&gt;
&lt;p&gt;And yes - this time, the upgrade seemed to work! No error messages this time!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-xp-in-dosbox-x/windows_xp_setup_stage_1.webp&#34; alt=&#34;Windows XP Setup started from Windows 2000&#34; title=&#34;Windows XP Setup started from Windows 2000&#34;&gt;
&lt;/p&gt;
&lt;p&gt;One reboot later, I noticed something very promising.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-xp-in-dosbox-x/windows_xp_setup_stage_2.webp&#34; alt=&#34;Windows XP Setup: Now with colors!&#34; title=&#34;Windows XP Setup: Now with colors!&#34;&gt;
&lt;/p&gt;
&lt;p&gt;Instead of starting in the text-based installer again, I got a colorful graphic installer, indicating that Windows Setup was taking a different route this time. And amazingly, the installation ran just fine without any issues.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-xp-in-dosbox-x/windows_xp_setup_stage_3.webp&#34; alt=&#34;Windows XP Setup: Installing Windows&#34; title=&#34;Windows XP Setup: Installing Windows!&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-xp-in-dosbox-x/windows_xp_setup_stage_4.webp&#34; alt=&#34;Windows XP Setup: Finalizing installation&#34; title=&#34;Windows XP Setup: Finalizing installation&#34;&gt;
&lt;/p&gt;
&lt;p&gt;With this part of the installation complete, there was one last reboot to survive.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-xp-in-dosbox-x/windows_xp_booting_featured.webp&#34; alt=&#34;Windows XP finally booting&#34; title=&#34;Windows XP finally booting&#34;&gt;
&lt;/p&gt;
&lt;p&gt;Windows XP was bootable and the installation itself was complete!&lt;/p&gt;
&lt;p&gt;Soon thereafter, things started to go downhill though. After the first boot, Windows XP is supposed to start the so-called &amp;ldquo;Out of Box Experience&amp;rdquo; (OOBE) in order to finalize the installation by setting up some options and the user account.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-xp-in-dosbox-x/windows_xp_oobe.webp&#34; alt=&#34;Windows XP OOBE&#34; title=&#34;Windows XP OOBE&#34;&gt;
&lt;/p&gt;
&lt;p&gt;The OOBE started, but what an experience it was! This wasn&amp;rsquo;t an experience. This was pure madness!&lt;/p&gt;
&lt;p&gt;The &lt;a href=&#34;https://soundcloud.com/stanlepard/1996-internet-starter-kit-velkommen-original-mix&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;beautiful setup music&lt;/a&gt;
 was cluttered with tons of distortion sounding like incredible compression artifacts caused by a &lt;em&gt;very&lt;/em&gt; unhappy WMA decoder.&lt;/p&gt;
&lt;p&gt;In fact, the performance was so poor that the system completely locked up and I had no other choice than rebooting the system.&lt;/p&gt;
&lt;p&gt;Fortunately, Windows XP is able to migrate the user accounts from Windows 2000 because I had no option to create a new account - I guess I avoided yet another dead end.&lt;/p&gt;
&lt;p&gt;And &lt;em&gt;yet another&lt;/em&gt; reboot later - there it was. In all its glory.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-xp-in-dosbox-x/windows_xp_winver.webp&#34; alt=&#34;Windows XP - it&amp;rsquo;s alive!&#34; title=&#34;Windows XP - it&amp;#39;s alive!&#34;&gt;
&lt;/p&gt;
&lt;p&gt;I did it. Windows XP is running successfully in DOSBox-X.&lt;/p&gt;
&lt;p&gt;Obviously, I had to retry the games I tried on Windows 2000. &lt;em&gt;The Sims&lt;/em&gt; in the various versions I tried before still failed, but this was expected. &lt;em&gt;Roller Coaster Tycoon&lt;/em&gt; improved by a lot. I couldn&amp;rsquo;t spot any sound issues this time and the game was perfectly playable.&lt;/p&gt;
&lt;p&gt;And last but not least: Yes, the iconic &lt;em&gt;3D Pinball: Space Cadet&lt;/em&gt; worked flawlessly as well.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-xp-in-dosbox-x/windows_xp_3d_pinball.webp&#34; alt=&#34;Windows XP - 3D Pinball Space Cadet&#34; title=&#34;Windows XP - 3D Pinball Space Cadet&#34;&gt;
&lt;/p&gt;
&lt;h1 id=&#34;closing-words&#34;&gt;Closing words&lt;/h1&gt;
&lt;p&gt;This is what I like about emulation and playing around with those systems so much. Even though some tasks seem to be impossible at first, there are always new routes to explore, helping you to achieve things many people (including yourself!) say are impossible.&lt;/p&gt;
&lt;p&gt;I love it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-xp-in-dosbox-x/windows_xp_turn_off_computer.webp&#34; alt=&#34;It is now safe to turn off your computer.&#34; title=&#34;It is now safe to turn off your computer.&#34;&gt;
&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Breaking the limits: Installing Windows 2000 in DOSBox-X</title>
      <link>https://felsqualle.com/posts/2023/07/installing-windows-2000-in-dosbox-x/</link>
      <pubDate>Mon, 03 Jul 2023 22:00:00 +0200</pubDate>
      
      <guid>https://felsqualle.com/posts/2023/07/installing-windows-2000-in-dosbox-x/</guid>
      <description>&lt;p&gt;DOSBox was one of the first emulators I used when I learned about emulation back in the early 2000s. Originally developed with its own DOS implementation for running games and legacy applications in mind, newer forks like &lt;a href=&#34;https://dosbox-x.com/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;DOSBox-X&lt;/a&gt;
 and &lt;a href=&#34;https://dosbox-staging.github.io/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;DOSBox Staging&lt;/a&gt;
 started to support &amp;ldquo;real&amp;rdquo; system emulation as well, providing similar functionality as &lt;a href=&#34;https://pcem-emulator.co.uk/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;PCem&lt;/a&gt;
 or &lt;a href=&#34;https://86box.net/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;86Box&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;While originally only targeting MS-DOS emulation, DOSBox-X provides official support for Windows 95 and Windows 98.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Well,&amp;rdquo; I said to myself, &amp;ldquo;if they already support Windows 9x, I could try running Windows 2000 on it! It shouldn&amp;rsquo;t be &lt;em&gt;that&lt;/em&gt; hard, right?!&amp;rdquo;&lt;/p&gt;
&lt;div class=&#34;notice info&#34; &gt;
&lt;p class=&#34;first notice-title&#34;&gt;&lt;span class=&#34;icon-notice baseline&#34;&gt;&lt;svg&gt;&lt;use href=&#34;#info-notice&#34;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;At the time of writing this article, running Windows 2000 isn&amp;rsquo;t officially supported by the DOSBox-X project. Don&amp;rsquo;t blame the developers for any issues - this is very experimental.&lt;/p&gt;&lt;/div&gt;

&lt;h2 id=&#34;but-windows-2000-isnt-dos&#34;&gt;But Windows 2000 isn&amp;rsquo;t DOS!&lt;/h2&gt;
&lt;p&gt;Yes, that&amp;rsquo;s correct. While Windows 95, 98, and ME (also referred to as the &amp;ldquo;Windows 9x series&amp;rdquo;) are using MS-DOS as part of their kernel, Windows 2000 runs on the Windows NT platform. Clearly, I won&amp;rsquo;t be able to start it using the DOS runtime environment DOSBox-X provides, but using their full system emulation should be possible.&lt;/p&gt;
&lt;p&gt;As the base configuration for DOSBox-X, I used the configuration template for Windows 98 provided in the &lt;a href=&#34;https://dosbox-x.com/wiki/Guide%3AInstalling-Windows-98#_dosbox_x_config_file&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;DOSBox-X wiki&lt;/a&gt;
. Since the recommended system specifications for Windows 2000 include 128 MB of RAM, I modified the configuration file accordingly.&lt;/p&gt;
&lt;p&gt;I started by creating a virtual hard drive image with a size of 8GB using the &lt;code&gt;IMGMAKE&lt;/code&gt; command and mounted both the virtual hard drive as well as the Windows 2000 ISO file.&lt;/p&gt;
&lt;p&gt;Since I wasn&amp;rsquo;t able to boot the Windows 2000 ISO image, I used the boot floppy images shipped on the Windows 2000 disc to start the installer.&lt;/p&gt;
&lt;h2 id=&#34;a-promising-start&#34;&gt;A promising start&lt;/h2&gt;
&lt;p&gt;I was pretty surprised that the floppy-based installer started without any issues. Expecting a smooth journey, I cycled through the three floppy images.&lt;/p&gt;
&lt;p&gt;After loading the files from the third floppy, the Windows 2000 installer checked the system hardware, tried to find the virtual hard drive - and failed.
&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-2000-in-dosbox-x/windows_2000_inaccessible_boot_device.webp&#34; alt=&#34;Windows 2000: Inacessible boot device&#34; title=&#34;Windows 2000: Inacessible boot device&#34;&gt;
&lt;/p&gt;
&lt;p&gt;Great, what an amazing start! I tried to reboot the virtual system a couple of times, but without success. It looks like the IDE driver in Windows 2000 doesn&amp;rsquo;t support the virtual IDE interface created by DOSBox-X. Sure, you can load additional drivers when starting the Windows 2000 setup by pressing the &lt;code&gt;F6&lt;/code&gt; button, but without a matching driver, this won&amp;rsquo;t work either.&lt;/p&gt;
&lt;h2 id=&#34;plan-b-choosing-the-update-route&#34;&gt;Plan B: Choosing the update route!&lt;/h2&gt;
&lt;p&gt;Luckily, I might be able to work around this issue by attempting to upgrade a Windows 98 installation to Windows 2000 instead.&lt;/p&gt;
&lt;p&gt;To do so, I mounted the Windows 98 (Second Edition) ISO file in DOSBox-X. To make sure that the hard drive image is clean, I used the &lt;code&gt;FORMAT&lt;/code&gt; utility shipped on the Windows 98 CD-ROM to initialize the virtual hard drive.&lt;/p&gt;
&lt;p&gt;While the retail versions of Windows 98 are not bootable by default, the CD-ROM for the OEM release can boot straight from the CD using the El Torito format. Since the release I had was indeed a OEM version, I didn&amp;rsquo;t have to work with a boot floppy this time. As recommended by the DOSBox-X documentation, I set the emulation core to &lt;code&gt;normal&lt;/code&gt; instead of &lt;code&gt;dynamic_x86&lt;/code&gt; to avoid crashes during the installation.&lt;/p&gt;
&lt;p&gt;The installation itself was pretty easy and completed without any issues. I opted for a &amp;ldquo;minimal&amp;rdquo; installation since my plan was to replace the Windows 98 installation with Windows 2000 after the first boot.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-2000-in-dosbox-x/windows_98_setup_screen.webp&#34; alt=&#34;Welcome to Windows 98 setup&#34; title=&#34;Welcome to Windows 98 setup&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-2000-in-dosbox-x/windows_98_component_selection.webp&#34; alt=&#34;Windows 98: Component selection&#34; title=&#34;Windows 98: Component selection&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-2000-in-dosbox-x/windows_98_setup_running.webp&#34; alt=&#34;Windows 98: Please sit back and relax!&#34; title=&#34;Windows 98: Please sit back and relax!&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-2000-in-dosbox-x/windows_98_first_boot.webp&#34; alt=&#34;Windows 98: Booting for the first time&#34; title=&#34;Windows 98: Booting for the first time&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-2000-in-dosbox-x/windows_98_finalized.webp&#34; alt=&#34;Windows 98: We have a working system!&#34; title=&#34;Windows 98: We have a working system!&#34;&gt;
&lt;/p&gt;
&lt;p&gt;Part one: Done!&lt;/p&gt;
&lt;h2 id=&#34;part-2-the-windows-2000-upgrade&#34;&gt;Part 2: The Windows 2000 upgrade&lt;/h2&gt;
&lt;p&gt;Now it&amp;rsquo;s time for the exciting part of the project: attempting to upgrade the new Windows 98 installation to Windows 2000.&lt;/p&gt;
&lt;p&gt;I loaded the Windows 2000 ISO image and launched the installer. After selecting the upgrade option, the setup asked if I wanted to convert the hard drive to the NTFS filesystem. Since I wasn&amp;rsquo;t able to boot from NTFS during my earlier attempts, I skipped this step and went with FAT32 instead.&lt;/p&gt;
&lt;p&gt;I assume that the hard drive emulation is only able to boot from FAT, but this is just an educated guess and not being able to boot from NTFS might just be the result of a configuration error on my end.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-2000-in-dosbox-x/windows_2000_update_prompt_1.webp&#34; alt=&#34;Would you like to upgrade to Windows 2000?&#34; title=&#34;Would you like to upgrade to Windows 2000?&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-2000-in-dosbox-x/windows_2000_update_prompt_2.webp&#34; alt=&#34;Welcome to the Windows 2000 Setup Wizard&#34; title=&#34;Welcome to the Windows 2000 Setup Wizard&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-2000-in-dosbox-x/windows_2000_skip_ntfs_conversion.webp&#34; alt=&#34;Do we want to upgrade to NTFS?&#34; title=&#34;Do we want to upgrade to NTFS?&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-2000-in-dosbox-x/windows_2000_setup_preparation.webp&#34; alt=&#34;Copying the required setup files, preparing for the next setup stage&#34; title=&#34;Copying the required setup files, preparing for the next setup stage&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-2000-in-dosbox-x/windows_2000_restart_to_setup.webp&#34; alt=&#34;Reboot!&#34; title=&#34;Reboot!&#34;&gt;
&lt;/p&gt;
&lt;p&gt;Now that the first part of the setup is complete, I should be able to boot into both the Windows 98 installation and the Windows 2000 installation.&lt;/p&gt;
&lt;h2 id=&#34;the-moment-of-truth&#34;&gt;The moment of truth&amp;hellip;&lt;/h2&gt;
&lt;p&gt;I restarted the virtual machine and selected the Windows 2000 setup from the boot menu. After loading the initial setup files from the hard drive, the Windows 2000 installer checked the system hardware, tried to find the virtual hard drive - and successfully detected it!&lt;/p&gt;
&lt;p&gt;Immediately, the installer proceeded to the next stage of the installation by copying all the required systems files. Usually, these files are copied from the CD-ROM, but since I&amp;rsquo;m performing an upgrade instead of a clean installation, the files are copied from the hard drive itself.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-2000-in-dosbox-x/windows_2000_text_installer.webp&#34; alt=&#34;Installing Windows 2000 actually works!&#34; title=&#34;Installing Windows 2000 actually works!&#34;&gt;
&lt;/p&gt;
&lt;p&gt;A few minutes later, the system attempted to boot into the newly installed system. I honestly didn&amp;rsquo;t expect it to work, but to my surprise, it booted successfully on the first try.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-2000-in-dosbox-x/windows_2000_first_kernel_boot_featured.webp&#34; alt=&#34;Booting into Windows 2000 for the first time&#34; title=&#34;Booting into Windows 2000 for the first time&#34;&gt;
&lt;/p&gt;
&lt;p&gt;What a relief!&lt;/p&gt;
&lt;p&gt;Being able to boot into the next stage of the Windows 2000 installation indicated that it might just work.&lt;/p&gt;
&lt;p&gt;And it did! The system started the final part of the installation, performing the hardware initialization and completing the final setup.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-2000-in-dosbox-x/windows_2000_hardware_detection.webp&#34; alt=&#34;Windows 2000: Hardware detection&#34; title=&#34;Windows 2000: Hardware detection&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-2000-in-dosbox-x/windows_2000_setup_progress_1.webp&#34; alt=&#34;Windows 2000: Performing final tasks&#34; title=&#34;Windows 2000: Performing final tasks&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-2000-in-dosbox-x/windows_2000_setup_progress_2.webp&#34; alt=&#34;Windows 2000: Performing final tasks&#34; title=&#34;Windows 2000: Performing final tasks&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-2000-in-dosbox-x/windows_2000_account_creation.webp&#34; alt=&#34;Creating the Windows 2000 user account&#34; title=&#34;Creating the Windows 2000 user account&#34;&gt;
&lt;/p&gt;
&lt;h2 id=&#34;mission-complete&#34;&gt;Mission complete?&lt;/h2&gt;
&lt;p&gt;After completing the final steps of the installation, I had to reboot the system one more time. At this point, I was feeling very confident that I would have a system that just works. I pressed the magic &amp;ldquo;reboot&amp;rdquo; button, patiently waited for the boot process to complete, logged into the system, and&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-2000-in-dosbox-x/windows_2000_explorer_crash.webp&#34; alt=&#34;Windows 2000 crashing explorer.exe&#34; title=&#34;Windows 2000 crashing explorer.exe&#34;&gt;
&lt;/p&gt;
&lt;p&gt;Oops.&lt;/p&gt;
&lt;p&gt;The system started, but for some reason that I have yet to discover, explorer.exe constantly crashes. I attempted to restart it multiple times through the Task Manager and even rebooted the system, but without success.&lt;/p&gt;
&lt;p&gt;As a result, I don&amp;rsquo;t have a functioning taskbar, and I&amp;rsquo;m unable to browse the system using the file explorer.&lt;/p&gt;
&lt;p&gt;Luckily, I was able to run &lt;code&gt;cmd.exe&lt;/code&gt; through Task Manager, so accessing the system and launching additional programs was still possible.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-2000-in-dosbox-x/windows_2000_cmd_calc.webp&#34; alt=&#34;Windows 2000: Using the system with cmd.exe&#34; title=&#34;Windows 2000: Using the system with cmd.exe&#34;&gt;
&lt;/p&gt;
&lt;p&gt;Even though the Explorer was completely broken, Internet Explorer on the other hand worked just fine. Since Internet Explorer allows local file browsing, I had a very nice replacement on hand.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-2000-in-dosbox-x/windows_2000_navigate_with_ie.webp&#34; alt=&#34;Windows 2000: Internet Explorer works perfectly fine!&#34; title=&#34;Windows 2000: Internet Explorer works perfectly fine!&#34;&gt;
&lt;/p&gt;
&lt;p&gt;But well, I should probably fix it.&lt;/p&gt;
&lt;h2 id=&#34;update-rollup-1-to-the-rescue&#34;&gt;Update Rollup 1 to the rescue!&lt;/h2&gt;
&lt;p&gt;Since I had no idea what caused the crashes, my first troubleshooting step was to install all available updates.&lt;/p&gt;
&lt;p&gt;The Windows 2000 ISO I used for the installation already included the latest Service Pack for Windows 2000. However, at the end of the Windows 2000 lifecycle, Microsoft released a package called &amp;ldquo;Update Rollup 1&amp;rdquo; which bundled all the updates they had released for Windows 2000.&lt;/p&gt;
&lt;p&gt;The Update Rollup 1 package (&lt;code&gt;Windows2000-KB891861-v2-x86-ENU.exe&lt;/code&gt;) is still available online through various archival sites. I created a new ISO image with the update file, mounted it in DOSBox-X, and started the installation.&lt;/p&gt;
&lt;p&gt;After what felt like an eternity and a reboot later, I noticed a slight difference compared to the previous reboot.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-2000-in-dosbox-x/windows_2000_working_taskbar.webp&#34; alt=&#34;Windows 2000: Now with 100% more Taskbars!&#34; title=&#34;Windows 2000: Now with 100% more Taskbars&#34;&gt;
&lt;/p&gt;
&lt;p&gt;No more Explorer crashes and a working taskbar!&lt;/p&gt;
&lt;h2 id=&#34;what-about-gaming&#34;&gt;What about gaming?&lt;/h2&gt;
&lt;p&gt;With a working base system, I wanted to see what I can do with the system. First, I tried to run &lt;em&gt;3D Pinball Space Cadet&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-2000-in-dosbox-x/windows_2000_3d_pinball.webp&#34; alt=&#34;3D Pinball Space Cadet in Windows 2000 in DOSBox-X&#34; title=&#34;3D Pinball Space Cadet in Windows 2000 in DOSBox-X&#34;&gt;
&lt;/p&gt;
&lt;p&gt;The game works perfectly fine, with smooth graphics and flawless sound.&lt;/p&gt;
&lt;p&gt;However, the next games I tried provided a much worse experience. &lt;em&gt;Rollercoaster Tycoon&lt;/em&gt; starts just fine with smooth graphics, but the sound is completely unusable with heavy stuttering. I tried to work around it by tuning the DirectX acceleration settings, but I had no luck.&lt;/p&gt;
&lt;p&gt;Next, I tried to install a copy of &lt;em&gt;The Sims&lt;/em&gt;. The original installer for the base game repeatedly crashed DOSBox-X with an error message indicating some unimplemented features. The installer for &lt;em&gt;The Sims: Deluxe Edition&lt;/em&gt; crashed Windows 2000 with a bluescreen. The only version I was able to successfully install was &lt;em&gt;The Sims: Complete Collection&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Notice how I wrote &lt;em&gt;install&lt;/em&gt; instead of &lt;em&gt;run&lt;/em&gt; here? As soon as I tried to start the game, it simply crashed with an error message. At this point, I wasn&amp;rsquo;t too surprised - I guess that the CD-ROM emulation in DOSBox-X is simply not able to handle the SafeDisc copy protection of the game.&lt;/p&gt;
&lt;h2 id=&#34;closing-words&#34;&gt;Closing words&lt;/h2&gt;
&lt;p&gt;From a usability point of view, this project doesn&amp;rsquo;t make any sense at all. For running games from the late 90s to the early 2000s, Windows 98 provides much better compatibility. You can either run Windows 98 in DOSBox-X or - if you want to be able to emulate a specific system - use a full system emulator like 86Box instead. Since 86Box emulates real, existing hardware, driver issues are much less likely as well.&lt;/p&gt;
&lt;p&gt;But from a more technical perspective, this project was both interesting and impressive. Trying to see what emulation can achieve is always fascinating - and sometimes, you get results that seem to be impossible at first glance.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/07/installing-windows-2000-in-dosbox-x/windows_2000_turn_off.webp&#34; alt=&#34;It is now safe to turn off your computer.&#34; title=&#34;It is now safe to turn off your computer.&#34;&gt;
&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Encounter wild Pokémon in your terminal!</title>
      <link>https://felsqualle.com/posts/2023/06/encounter-wild-pokemon-in-your-terminal/</link>
      <pubDate>Wed, 21 Jun 2023 21:00:00 +0200</pubDate>
      
      <guid>https://felsqualle.com/posts/2023/06/encounter-wild-pokemon-in-your-terminal/</guid>
      <description>&lt;p&gt;A few days ago, I posted an image that showcased an ASCII art representation of a Raichu from the Alola region. This image appeared after logging into my server using SSH. Surprisingly, it sparked curiosity, and I received several inquiries about the magic behind it.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s delve into the details.&lt;/p&gt;
&lt;p&gt;If you haven&amp;rsquo;t heard about &lt;a href=&#34;https://pkmn.li/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;pkmn.li&lt;/a&gt;
, you should definitely have a look. pkmn.li is a website that shows random ASCII art of Pokémon each time you open the website. glow, the author of the project, provides a lot of interesting details about it &lt;a href=&#34;https://glow.li/posts/pkmn/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;on their website&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;Since the output provided by the website contains all the necessary ANSI sequences for coloring, and assuming that your terminal supports true color (which it probably does), you aren&amp;rsquo;t limited to a web browser.&lt;/p&gt;
&lt;p&gt;Instead, you can fetch the website with &lt;code&gt;curl&lt;/code&gt; and view the sprites right in your terminal.&lt;/p&gt;
&lt;p&gt;All I had to do was add the command &lt;code&gt;curl https://pkmn.li&lt;/code&gt; to the &lt;code&gt;.bash_profile&lt;/code&gt; file of my shell account. Since the contents of the &lt;code&gt;.bash_profile&lt;/code&gt; file (or &lt;code&gt;.bashrc&lt;/code&gt; on some systems) are executed each time a shell session is opened, you&amp;rsquo;ll get a random Pokémon encounter each time you open your terminal or launch an SSH session.&lt;/p&gt;
&lt;p&gt;However, this only works for Bash, so if you are using a different shell on your system, you&amp;rsquo;ll have to find the matching &amp;ldquo;autostart&amp;rdquo; file.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2023/06/encounter-wild-pokemon-in-your-terminal/pokemon_terminal_featured.webp&#34; alt=&#34;And all I got was a Zubat.&#34; title=&#34;And all I got was a Zubat.&#34;&gt;
&lt;/p&gt;
&lt;p&gt;Gotta catch &amp;rsquo;em all!&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Meta is a danger to the fediverse and we have to be prepared</title>
      <link>https://felsqualle.com/posts/2023/06/meta-is-a-danger-to-the-fediverse/</link>
      <pubDate>Mon, 19 Jun 2023 21:00:00 +0200</pubDate>
      
      <guid>https://felsqualle.com/posts/2023/06/meta-is-a-danger-to-the-fediverse/</guid>
      <description>&lt;p&gt;Back in March 2023, we heard about Meta&amp;rsquo;s plans to join the fediverse for the first time after they confirmed that they are working on a Twitter-like alternative using the ActivityPub protocol. Codename: Project 92.&lt;/p&gt;
&lt;p&gt;Great! Now we can connect with even more people using our familiar apps! But there&amp;rsquo;s a catch.&lt;/p&gt;
&lt;p&gt;In the last couple of days, new rumors appeared.&lt;/p&gt;
&lt;p&gt;According to various sources, some fediverse admins had secret meetings with Meta under NDAs.&lt;/p&gt;
&lt;p&gt;Talking about an open network like the fediverse with a huge company behind locked doors is an incredibly bad idea. I think the fact that they don&amp;rsquo;t want to publically discuss what Meta&amp;rsquo;s plans for the fediverse are proves that they most certainly &lt;em&gt;won&amp;rsquo;t&lt;/em&gt; care about openness or federation.&lt;/p&gt;
&lt;p&gt;Sure, they will connect to the existing network. But what if their platform gains enough traction so that a substantial amount of users switch over to their instance? At this point, they only have to lock down their network again. Or maybe they propose changes to ActivityPub that implement a recommendation algorithm. Or an ad platform.&lt;/p&gt;
&lt;p&gt;Meta doesn&amp;rsquo;t care about the users. They care about their company and increasing value for their sharesholders. Always remember that the users are nothing but a source of revenue for them.&lt;/p&gt;
&lt;p&gt;If we want the fediverse to survive as it is right now, we have to step in right at the beginning. We shouldn&amp;rsquo;t federate with them. We should once and for all prove that social media platforms run by big corporations are nothing but a failed experiment and a relic from the past.&lt;/p&gt;
&lt;p&gt;Be vigilant.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Export your own followers from Mastodon using the Mastodon API</title>
      <link>https://felsqualle.com/posts/2023/06/fetch-own-followers-from-mastodon-api/</link>
      <pubDate>Fri, 16 Jun 2023 21:00:00 +0200</pubDate>
      
      <guid>https://felsqualle.com/posts/2023/06/fetch-own-followers-from-mastodon-api/</guid>
      <description>&lt;p&gt;With decentralization and federation in mind, Mastodon allows exporting a list of all the accounts you follow directly from within the GUI.&lt;/p&gt;
&lt;p&gt;This function generates a CSV file you can later import to another server. This function is especially helpful if you want to create a backup of your data or if you don&amp;rsquo;t want to do a full migration (and lose your first account), but open up a second account instead.&lt;/p&gt;
&lt;p&gt;Unfortunately, Mastodon doesn&amp;rsquo;t provide an option to export a list of your own &lt;em&gt;followers&lt;/em&gt;. Well, at least not from within the GUI. But hey, we have an API to play with!&lt;/p&gt;
&lt;div class=&#34;notice warning&#34; &gt;
&lt;p class=&#34;first notice-title&#34;&gt;&lt;span class=&#34;icon-notice baseline&#34;&gt;&lt;svg&gt;&lt;use href=&#34;#warning-notice&#34;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;This is a proof of concept. While the following script works for me, there is absolutely no sanity checking. It worked for my pretty small accounts, but I can&amp;rsquo;t guarantee that you won&amp;rsquo;t hit rate limits on larger accounts or crowded servers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Please do not use this script to scrape accounts that are not yours!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you want to run this script against an instance that is not entirely controlled by you, I recommend asking the owner/operators first if they agree with mass-querying their API and importing large amounts of data.&lt;/p&gt;&lt;/div&gt;

&lt;p&gt;I came up with the idea when I started my soft migration to a secondary account. In order to grow my network, I decided that I want to follow back as many accounts as possible.&lt;/p&gt;
&lt;p&gt;Importing the accounts I follow was no problem at all since the Mastodon web interface provides an option to export your own follows. The generated CSV file can be imported on a second account, bringing back all your follows in the blink of an eye!
&lt;img src=&#34;https://felsqualle.com/posts/2023/06/fetch-own-followers-from-mastodon-api/mastodon_export_gui_featured.webp&#34; alt=&#34;CSV export in the Mastodon web interface&#34; title=&#34;CSV export in the Mastodon web interface&#34;&gt;
&lt;/p&gt;
&lt;p&gt;But what about your &lt;em&gt;followers&lt;/em&gt;? As you might notice, the CSV option is missing in the web interface. Well, &lt;em&gt;usually&lt;/em&gt; you don&amp;rsquo;t need to export your followers. While you can control which accounts &lt;em&gt;you&lt;/em&gt; want to follow, your &lt;em&gt;followers&lt;/em&gt; are an entirely different story.&lt;/p&gt;
&lt;p&gt;Regardless of account migrations or recovery after data loss, you can&amp;rsquo;t bring back your followers unless they decide to follow your account again. This might be the reason why a GUI option to export your followers is missing - most people will never use it.&lt;/p&gt;
&lt;p&gt;However, the necessary data is accessible using the Mastodon API. All we need is &lt;code&gt;bash&lt;/code&gt;, &lt;code&gt;curl&lt;/code&gt;, &lt;code&gt;jq&lt;/code&gt;, and &lt;code&gt;sed&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The Mastodon API provides an endpoint called &lt;code&gt;/api/v1/accounts/$userid/followers&lt;/code&gt; that returns the followers of any account. Fortunately, we don&amp;rsquo;t have to deal with authentication since the endpoint is entirely public.&lt;/p&gt;
&lt;p&gt;But there&amp;rsquo;s a catch: Calling the API only returns 40 to 80 results, depending on the configuration of the instance and the query options. While this looks like an artificial limitation at first glance, it prevents the API to return too large replies - just assume your account has 10.000 followers.&lt;/p&gt;
&lt;p&gt;To fetch all accounts, we have to iterate over multiple virtual pages of the API result until we run out of pages to query. After the last page is fetched and we get no reference to a &amp;ldquo;next&amp;rdquo; page, we know that the operation is finished.&lt;/p&gt;
&lt;p&gt;The script itself is pretty straightforward:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;1&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#1&#34;&gt; 1&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#8b949e;font-weight:bold;font-style:italic&#34;&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;2&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#2&#34;&gt; 2&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#8b949e;font-weight:bold;font-style:italic&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# Fetch own followers via the Mastodon API&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;3&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#3&#34;&gt; 3&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# Tested up to Mastodon v4.3.3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;4&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#4&#34;&gt; 4&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;5&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#5&#34;&gt; 5&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# Enter your user details&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;6&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#6&#34;&gt; 6&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;username&lt;/span&gt;&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;yourMastodonUsername&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;7&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#7&#34;&gt; 7&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;instance&lt;/span&gt;&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;https://example.com&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;8&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#8&#34;&gt; 8&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;9&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#9&#34;&gt; 9&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# Preparations&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;10&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#10&#34;&gt;10&lt;/a&gt;&lt;/span&gt;&lt;span&gt;echo &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;Get account ID from the given username...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;11&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#11&#34;&gt;11&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;userid&lt;/span&gt;&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff7b72&#34;&gt;$(&lt;/span&gt;curl --silent &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;instance&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;/api/v1/accounts/lookup?acct=&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;username&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt; | jq -r .id&lt;span style=&#34;color:#ff7b72&#34;&gt;)&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;12&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#12&#34;&gt;12&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;13&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#13&#34;&gt;13&lt;/a&gt;&lt;/span&gt;&lt;span&gt;echo &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;Found account ID: &lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;$userid&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;14&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#14&#34;&gt;14&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;initial_url&lt;/span&gt;&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;instance&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;/api/v1/accounts/&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;userid&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;/followers&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;15&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#15&#34;&gt;15&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;16&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#16&#34;&gt;16&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# Fetch initial JSON output&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;17&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#17&#34;&gt;17&lt;/a&gt;&lt;/span&gt;&lt;span&gt;echo &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;Checking URL &lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;initial_url&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;18&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#18&#34;&gt;18&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;json_output&lt;/span&gt;&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff7b72&#34;&gt;$(&lt;/span&gt;curl --silent &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;initial_url&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff7b72&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;19&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#19&#34;&gt;19&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;nextstep&lt;/span&gt;&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff7b72&#34;&gt;$(&lt;/span&gt;curl --head --silent &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;initial_url&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt; | grep -i &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;next&amp;#34;&lt;/span&gt; | cut -d &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;;&amp;#34;&lt;/span&gt; -f1 | sed -n &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#39;s/.*&amp;lt;\(.*\)&amp;gt;.*/\1/p&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ff7b72&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;20&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#20&#34;&gt;20&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;21&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#21&#34;&gt;21&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# Main loop: Fetch all remaining accounts&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;22&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#22&#34;&gt;22&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#ff7b72&#34;&gt;while&lt;/span&gt; &lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;[&lt;/span&gt; &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;nextstep&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;]&lt;/span&gt;; &lt;span style=&#34;color:#ff7b72&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;23&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#23&#34;&gt;23&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  echo &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;Checking URL &lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;nextstep&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;24&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#24&#34;&gt;24&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&#34;color:#79c0ff&#34;&gt;json_output&lt;/span&gt;&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;+=&lt;/span&gt;&lt;span style=&#34;color:#ff7b72&#34;&gt;$(&lt;/span&gt;curl --silent &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;nextstep&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ff7b72&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;25&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#25&#34;&gt;25&lt;/a&gt;&lt;/span&gt;&lt;span&gt;  &lt;span style=&#34;color:#79c0ff&#34;&gt;nextstep&lt;/span&gt;&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff7b72&#34;&gt;$(&lt;/span&gt;curl --head --silent &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;nextstep&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;  | grep -i &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;next&amp;#34;&lt;/span&gt; | cut -d &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;;&amp;#34;&lt;/span&gt; -f1 | sed -n &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#39;s/.*&amp;lt;\(.*\)&amp;gt;.*/\1/p&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#ff7b72&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;26&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#26&#34;&gt;26&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#ff7b72&#34;&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;27&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#27&#34;&gt;27&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;28&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#28&#34;&gt;28&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#8b949e;font-style:italic&#34;&gt;# Save output to CSV file&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;29&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#29&#34;&gt;29&lt;/a&gt;&lt;/span&gt;&lt;span&gt;echo &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;Writing CSV file...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;30&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#30&#34;&gt;30&lt;/a&gt;&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;timestamp&lt;/span&gt;&lt;span style=&#34;color:#ff7b72;font-weight:bold&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ff7b72&#34;&gt;$(&lt;/span&gt;date +%s&lt;span style=&#34;color:#ff7b72&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;31&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#31&#34;&gt;31&lt;/a&gt;&lt;/span&gt;&lt;span&gt;echo &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;Account address,Show boosts,Notify on new posts&amp;#34;&lt;/span&gt;       &amp;gt;  &lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;username&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;_follower_accounts_&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;timestamp&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;.csv
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;32&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#32&#34;&gt;32&lt;/a&gt;&lt;/span&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;33&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#33&#34;&gt;33&lt;/a&gt;&lt;/span&gt;&lt;span&gt;echo &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;json_output&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;&lt;/span&gt; | jq -r &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#39;.[].acct | .+ &amp;#34;,true,false,&amp;#34;&amp;#39;&lt;/span&gt; &amp;gt;&amp;gt; &lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;username&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;_follower_accounts_&lt;span style=&#34;color:#a5d6ff&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#79c0ff&#34;&gt;timestamp&lt;/span&gt;&lt;span style=&#34;color:#a5d6ff&#34;&gt;}&lt;/span&gt;.csv
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#6e7681&#34; id=&#34;34&#34;&gt;&lt;a style=&#34;outline:none;text-decoration:none;color:inherit&#34; href=&#34;#34&#34;&gt;34&lt;/a&gt;&lt;/span&gt;&lt;span&gt;echo &lt;span style=&#34;color:#a5d6ff&#34;&gt;&amp;#34;done!&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;First, we have to get our account ID. We can easily perform a lookup using the &lt;code&gt;/api/v1/accounts/lookup?acct=&lt;/code&gt; endpoint. Next, we grab the first batch of JSON by querying &lt;code&gt;/api/v1/accounts/$userid/followers&lt;/code&gt;. This will not only return the first round of accounts but also provide the &amp;ldquo;next&amp;rdquo; URL in the HTTP response. Now, we simply extract the next URL, get the JSON reply from the API and check if we have a new &amp;ldquo;next&amp;rdquo; URL. We repeat this process until there is no &amp;ldquo;next&amp;rdquo; URL present in the HTTP response which indicates that we are on the very last page.&lt;/p&gt;
&lt;p&gt;Finally, we have the &lt;em&gt;entire&lt;/em&gt; API response in the &lt;code&gt;json_output&lt;/code&gt; variable.&lt;/p&gt;
&lt;p&gt;The last step consists of writing a proper CSV header and querying the JSON output we got earlier using &lt;code&gt;jq&lt;/code&gt;. Finally, we write the prepared output into the CSV file.&lt;/p&gt;
&lt;p&gt;The CSV we created has the same format as the &lt;code&gt;following_accounts.csv&lt;/code&gt; created by the web interface when using the export of your follows. Thus, we can use this CSV and import it to the &amp;ldquo;Following list&amp;rdquo; using the web interface and follow all of our followers at once!&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Virtual archaeology: Recreating the very first PC I ever used</title>
      <link>https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/</link>
      <pubDate>Mon, 12 Dec 2022 08:00:00 +0100</pubDate>
      
      <guid>https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/</guid>
      <description>&lt;p&gt;There are some things in life you will remember forever. In case you are just like me, then the very first PC you ever used is most likely one of these things.&lt;/p&gt;
&lt;p&gt;I still remember the specifications of the family computer 6-year-old me used in 1999, even though its mainboard died around 15 years ago.&lt;/p&gt;
&lt;p&gt;It was a Packard Bell PC, powered by an Intel Pentium II @ 233MHz, 32 MB of RAM, a 20x CD-ROM-Drive (no DVD!), and a Matrox Mystique GPU.&lt;/p&gt;
&lt;p&gt;I knew that the system was running Windows 95 with a custom OEM software bundle. For years, I thought that rebuilding the system with the original software was impossible. The recovery CD was long gone and there was no copy to be found on any archival site.&lt;/p&gt;
&lt;p&gt;By pure luck, I recently found a copy of the original recovery CD for sale online - I simply had to get it. It turned out that trying to recreate the system on a virtual machine was quite an adventure involving a deep dive into the inner workings of the Packard Bell recovery process.&lt;/p&gt;
&lt;h2 id=&#34;the-system-in-detail&#34;&gt;The system in detail&lt;/h2&gt;
&lt;p&gt;Even though I still know most of the specifications, the exact model of the system was unknown, so I couldn&amp;rsquo;t easily try to find a recovery CD based on the model number or name. According to the specs I had, the most likely candidate is a &lt;a href=&#34;https://web.archive.org/web/19980214202026/http://www.packardbell.com/products/97models/pl3000.asp&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Packard Bell Platinum 3000&lt;/a&gt;
 from late 1997. Looking at the picture of the case, I was sure that this was the model I had. Luckily, this case model (codenamed &amp;lsquo;Milano&amp;rsquo;) was pretty rare and this model was the only one I could find with this exact case and system specifications.&lt;/p&gt;
&lt;p&gt;The specifications don&amp;rsquo;t match exactly though, e.g. I remember that the hard disk was a bit smaller and closer to the 2 GB range. Furthermore, the included software listed on that page doesn&amp;rsquo;t match, but this might be caused by regional differences.&lt;/p&gt;
&lt;p&gt;Finding some detailed information about the exact hardware used for this model turned out to be more difficult than I initially thought. After some research, I think the most accurate match I could find is a system with the codename &lt;a href=&#34;https://web.archive.org/web/20221203222441/http://www.uktsupport.co.uk/pb/faq/model.htm&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Pulsar 24&lt;/a&gt;
 based on the &amp;lsquo;Hermes&amp;rsquo; platform.&lt;/p&gt;
&lt;p&gt;Since Windows 95 is horribly broken in any modern virtualization environment, I used 86Box instead. Choosing 86Box also allowed me to pick the hardware I wanted to emulate, helping to recreate the system as closely as possible. 86Box doesn&amp;rsquo;t support the exact hardware though, so I had to improvise a bit.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Type&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;Original hardware&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;Emulated hardware&lt;/th&gt;
          &lt;th style=&#34;text-align: center&#34;&gt;Explanation&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;Base machine &lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;PB Pulsar 24&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;Micronics Spitfire&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;Original model unsupported, same chipset (Intel 440LX), same mainboard?&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;CPU&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;Pentium II (Klamath) @ 233 MHz&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;Pentium II (Klamath) @ 166 Mhz&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;Host system limitations&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;RAM&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;32 MB&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;32 MB&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;./.&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Hard Drive&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;~2 GB&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;~8 GB&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;Allowing for future experiments and upgrades&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;GPU&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;Matrox Mystique 200&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;S3 Vision964 (ELSA Winner 2000 Pro/X)&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;Original model unsupported&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Sound card&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;Aztech Sound 16C&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;Sound Blaster 32 PnP&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;Original model unsupported&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Network&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;USR X2 Winmodem&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;Realtek RTL 8029AS&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;Original model unsupported, no networking with Host&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;CD-ROM&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;NEC CD-ROM drive, 20x&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;HITACHI CDR-8130, 20x&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;Original model unsupported&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Floppy drive&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;3.5in, 1.44 MB&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;3.5in, 1.44 MB&lt;/td&gt;
          &lt;td style=&#34;text-align: center&#34;&gt;./.&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;This configuration was still very close to the original machine, thanks to the Micronics Spitfire I could emulate, which is likely the same mainboard as the one used for the Packard Bell Platinum 3000. It even shares the same BIOS revision.&lt;/p&gt;
&lt;p&gt;The other components are pretty sane defaults, offering good driver support in Windows 95.&lt;/p&gt;
&lt;h2 id=&#34;getting-started&#34;&gt;Getting started&lt;/h2&gt;
&lt;p&gt;Luckily, I remembered parts of the software collection that was bundled with the system I used back in the day. The online listing for the CD I bought only referred to the software collection, so I wouldn&amp;rsquo;t have been able to locate it just by the system&amp;rsquo;s model name.&lt;/p&gt;
&lt;p&gt;Unfortunately, the part number for the Master CD listed for the Hermes platform doesn&amp;rsquo;t exactly match the copy I have, but it is still a very close match. While Hermes is supposed to have a Master CD with the part number &lt;strong&gt;6709881101&lt;/strong&gt;, the copy I have has the part number &lt;strong&gt;6709880503&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The last two digits are not important because they are indicating the language used on the CD. While &lt;strong&gt;01&lt;/strong&gt; marks an English release, &lt;strong&gt;03&lt;/strong&gt; is the language code for Germany. All in all, I wasn&amp;rsquo;t too worried about the part numbers because the most likely explaination is a regional difference again.&lt;/p&gt;
&lt;p&gt;A much bigger problem was the missing accompanying boot floppy that initializes the system restore process, and the fact I couldn&amp;rsquo;t find a copy online. I tried to start with a different period-correct Packard Bell boot floppy from archive.org, but all I got was an installer that immediately crashed.&lt;/p&gt;
&lt;p&gt;Thinking it was a bad boot floppy that caused the issue, I started the system with a generic Windows 95 boot floppy and launched the setup file on the CD manually. For some reason though, the installer started and immediately told me the restore process was completed. Unsurprisingly, it was not. The hard drive was as empty as before.&lt;/p&gt;
&lt;p&gt;This meant that I had to find another way to start the installation, bypassing the original setup file.&lt;/p&gt;
&lt;h2 id=&#34;a-first-look-at-the-master-cd&#34;&gt;A first look at the Master CD&lt;/h2&gt;
&lt;p&gt;To be able to understand how the recovery process works, I started by examining the file layout on the recovery CD.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[ROOT DIRECTORY]
│   CRCLOG.DAT
│
├───ARJ
│       ARJ.EXE
│       ARJ.PIF
│       WINDOWS.ARJ
│
├───CABS
│       ACROREAD.CAB
│       ADIBOU.CAB
│       AIMS.CAB
│       AUTOEXP.CAB
│       CDEXTRA.CAB
...
│       WORD8_5.CAB
│       WORD8_6.CAB
│       WORKS_1.CAB
│       WORKS_2.CAB
│       WWHACKE.CAB
│
├───TSRC
│   ├───3DMOUSE
│   │       3DMOUSE.INF
│   │
│   ├───ATI
│   │       NOFILE.TXT
│   │
│   ├───AZTECH
│   │       AZT472.CAB
│   │       AZT480D.CAB
...
│   │
│   ├───COMMAND
│   │       ANSI.SYS
│   │       ATTRIB.EXE
│   │       CHKDSK.EXE
│   │       CHOICE.COM
│   │       COUNTRY.SYS
│   │       DEBUG.EXE
...
│
└───WIN95
        ARJ.EXE
        ARJ.PIF
        BIOSLOCK.EXE
        BIOSLOCK.PIF
        KILL.COM
        KILL.PIF
        MASTCOUN.INI
        OEMSETUP.EXE
        PBHOUSE.PCX
        PBRESTOR.EXE
        PCXDUMP.EXE
        PSCRIPT.EXE
        PSCRIPT.PIF
        ROMAN.FON
        SVGA256.BGI
        TREECRC.EXE
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The file layout doesn&amp;rsquo;t look too complicated. &lt;code&gt;ARJ&lt;/code&gt; contains the main archive with the base installation, &lt;code&gt;CABS&lt;/code&gt; contains all the additional software. By looking at the names of the .cab files, I was able to verify that the entire preinstalled software library as I remembered was there. Yay!&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;TSRC&lt;/code&gt; directory, we find some additional drivers and what looks like a very basic DOS installation. The &lt;code&gt;WIN95&lt;/code&gt; directory holds the installer and restoration executables I didn&amp;rsquo;t manage to get up and running previously.&lt;/p&gt;
&lt;p&gt;Looking into the &lt;code&gt;WINDOWS.ARJ&lt;/code&gt; archive was very promising. Considering the filenames and folders present in the archive, it looked like a complete factory image of the hard drive. In theory, all I had to do is uncompress it to the hard drive and start working from there.&lt;/p&gt;
&lt;p&gt;Moments later, I was incredibly disappointed when I noticed a very small, but very important detail in WinRAR&amp;rsquo;s file listing.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/win95_arj_overview.png&#34; alt=&#34;Listing the contents of the WINDOWS.ARJ archive&#34; title=&#34;Listing the contents of the WINDOWS.ARJ archive&#34;&gt;
&lt;/p&gt;
&lt;p&gt;Do you see the &lt;code&gt;*&lt;/code&gt; at the end of each file? This means that the files are encrypted and you can&amp;rsquo;t uncompress them without the proper password. And unfortunately, I don&amp;rsquo;t have the password.&lt;/p&gt;
&lt;h2 id=&#34;back-to-square-one&#34;&gt;Back to square one.&lt;/h2&gt;
&lt;p&gt;Great. With all the files encrypted, there is no way to simply extract the archive as I initially wanted to do. I had to take a closer look at the executables for the restore process itself.&lt;/p&gt;
&lt;p&gt;A quick examination of the non-matching-but-close boot floppy revealed that the file for starting the system restore process is &lt;code&gt;\WIN95\OEMSETUP.EXE&lt;/code&gt;. Not sure where to begin, I quickly ran &lt;code&gt;grep &amp;quot;WINDOWS.ARJ&amp;quot; OEMSETUP.EXE&lt;/code&gt; - and there was a match in the binary file. This was very promising because it meant that there was at least a reference to the magical archive file in the setup program.&lt;/p&gt;
&lt;p&gt;The next step was to try to find some more valuable information in the executable by using the &lt;code&gt;strings&lt;/code&gt; command provided by my MinGW64 environment. While looking through the program output, I found something amazing:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;%c:\win95\arj.exe x -y -i -gP127A783D %c:\arj\windows.arj c:\
md c:\cabs
%c:\win95\arj.exe x -y -i -gP127A783D %c:\cabs\inf.arj c:\cabs
%c:\content.txt
[switches]
%s %c:\cabs\*.* C:\cabs /S
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There it is! The archive password and all the instructions I needed to get the base installation up and running.&lt;/p&gt;
&lt;h2 id=&#34;extracting-the-base-archive&#34;&gt;Extracting the base archive&lt;/h2&gt;
&lt;p&gt;With the instructions I found in the OEMSETUP file, it was time to start the manual reinstallation process. I booted the system using my generic Windows 95 floppy and prepared the hard drive using &lt;code&gt;FDISK&lt;/code&gt; and &lt;code&gt;FORMAT&lt;/code&gt;. Since the archive contains the files &lt;code&gt;MSDOS.SYS&lt;/code&gt;, &lt;code&gt;IO.SYS&lt;/code&gt;, and &lt;code&gt;COMMAND.COM&lt;/code&gt;, I made the hard drive bootable by running &lt;code&gt;FDISK /MBR&lt;/code&gt; immediately after partitioning to get a bootable system.&lt;/p&gt;
&lt;p&gt;Time for the moment of truth.&lt;/p&gt;
&lt;p&gt;I started the extraction process by running &lt;code&gt;D:\WIN95\ARJ.EXE x -y -gP127A783D D:\ARJ\WINDOWS.ARJ C:\&lt;/code&gt;, not exactly knowing what to expect. After all, it wasn&amp;rsquo;t an exact match for the Master Boot Floppy, so all I could do was hope for some sort of password-sharing.&lt;/p&gt;
&lt;p&gt;Needless to say, I was &lt;em&gt;very&lt;/em&gt; happy after seeing the following lines scrolling by:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/uncompressing_windows_arj.png&#34; alt=&#34;Uncompressing WINDOWS.ARJ&#34; title=&#34;Uncompressing the WINDOWS.ARJ archive to the hard drive&#34;&gt;
&lt;/p&gt;
&lt;p&gt;Extracting the &lt;code&gt;WIN95\WINDOWS.ARJ&lt;/code&gt; archive finished without further issues, so I continued by extracting &lt;code&gt;CABS\INF.ARJ&lt;/code&gt; the same way. And luckily, this archive extracted just fine as well.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/uncompressing_inf_arj.png&#34; alt=&#34;Uncompressing INF.ARJ&#34; title=&#34;Uncompressing the INF.ARJ archive to the hard drive&#34;&gt;
&lt;/p&gt;
&lt;p&gt;With both archives extracted, I was ready to copy over the remaining bits and pieces I previously figured out by examining the OEMSETUP executable. To document the process, I created a batch file that performed all the steps I knew the original installer would do.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;@echo off
echo Prepare drive...
FDISK
FDISK /MBR
FORMAT C: /Q /U /V:PACKARDBELL

echo Extract encrypted archives...
D:\WIN95\ARJ.EXE x -y -gP127A783D D:\ARJ\WINDOWS.ARJ C:\
D:\WIN95\ARJ.EXE x -y -gP127A783D D:\CABS\INF.ARJ C:\CABS

echo Copy remaining cab files
D:\TSRC\COMMAND\XCOPY.EXE D:\CABS\*.* C:\CABS /S

echo Extraction done, please reboot!
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After copying over the remaining .cab files from the CD, I was ready to remove the boot floppy and reset the system, hoping for a flawless recovery process.&lt;/p&gt;
&lt;p&gt;For the fraction of a second, things looked pretty promising as I was greeted with the &amp;ldquo;Starting Windows 95&amp;hellip;&amp;rdquo; banner.&lt;/p&gt;
&lt;p&gt;However, instead of getting a successful boot I was confronted with an image telling me that the hard drive was locked and the boot process stopped. I tried resetting the system multiple times, but I couldn&amp;rsquo;t get the system to boot.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/disk_locked.png&#34; alt=&#34;Disque Dur Protégé&#34; title=&#34;Disque Dur Protégé - the disk protection is kicking in&#34;&gt;
&lt;/p&gt;
&lt;h2 id=&#34;not-to-be-sold-seperately&#34;&gt;Not to be sold seperately&lt;/h2&gt;
&lt;p&gt;Back in the day, Packard Bell invented a couple of methods to ensure their recovery disks would work on the original system only. After some research, I learned that this led to the introduction of a hidden sector that is placed on the hard drive during manufacturing that holds some critical information.&lt;/p&gt;
&lt;p&gt;The setup process checks if this sector is present which would indicate a genuine Packard Bell system. Moreover, this allowed Packard Bell to use the same Master CD for multiple system variants, e.g. by shipping various software bundles depending on the exact system specifications.&lt;/p&gt;
&lt;p&gt;I realized that this also means easily swapping the hard drive wasn&amp;rsquo;t possible back then because of the missing hidden sector. In theory, a copy of the hidden sector could be placed on the Master Boot Floppy, but since I don&amp;rsquo;t have a valid copy, I can&amp;rsquo;t confirm that theory.&lt;/p&gt;
&lt;p&gt;On the other hand, the service technicians had to have a way to rebuild the hidden sector in case the original software was lost and the hard drive had to be replaced.&lt;/p&gt;
&lt;h2 id=&#34;introducing-hscenter&#34;&gt;Introducing HSCENTER&lt;/h2&gt;
&lt;p&gt;The tool that was used for managing this hidden sector is called &lt;code&gt;HSCENTER&lt;/code&gt;. I was pretty surprised that this tool was included in the base image of the restore CD, so I didn&amp;rsquo;t have to find a third-party source.&lt;/p&gt;
&lt;p&gt;I booted the system from my Windows 95 boot diskette again. HSCENTER is hidden in the &lt;code&gt;C:\DRIVERS\&lt;/code&gt; directory after the initial uncompressing stage is completed. Using &lt;code&gt;HSCENTER /?&lt;/code&gt; revealed quite a lot of options:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/hscenter_help.png&#34; alt=&#34;HSCENTER help command&#34; title=&#34;Command overview of HSCENTER&#34;&gt;
&lt;/p&gt;
&lt;p&gt;Without a proper manual for the application, I had to figure out how to use the tool. &lt;code&gt;/CREATEHS&lt;/code&gt; wasn&amp;rsquo;t an option because I didn&amp;rsquo;t have the &lt;code&gt;SAUDIT.TXT&lt;/code&gt; file (I guess that this holds the sector information in case you have a proper Master Boot Floppy). Instead, I had to build the hidden sector by myself.&lt;/p&gt;
&lt;p&gt;Since none of the options seemed like an obvious solution, I simply launched HSCENTER without any additional options.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/hscenter_overview.png&#34; alt=&#34;HSCENTER.EXE main menu&#34; title=&#34;HSCENTER.EXE main menu&#34;&gt;
&lt;/p&gt;
&lt;p&gt;My first choice was to select the &lt;code&gt;HS MANAGER&lt;/code&gt; option, so I could check if the tool was supposed to do what I needed.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/hscenter_manager.png&#34; alt=&#34;HSCENTER Manager Menu&#34; title=&#34;The main menu of the HSCENTER Manager Menu&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/hscenter_hs_manager_menu.png&#34; alt=&#34;HSCENTER READ AND MODIFY CURRENT HS&#34; title=&#34;HSCENTER: READ AND MODIFY CURRENT HS&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/hscenter_hs_hardware_information.png&#34; alt=&#34;HSCENTER Hardware Information&#34; title=&#34;HSCENTER Hardware Information&#34;&gt;
&lt;/p&gt;
&lt;p&gt;Reading and modifying an &lt;em&gt;existing&lt;/em&gt; hidden sector seems to be possible from within this menu. But currently, there is no sector to &lt;em&gt;modify&lt;/em&gt; and I have to &lt;em&gt;create&lt;/em&gt; it.&lt;/p&gt;
&lt;p&gt;The next obvious choice was to try the &lt;code&gt;ENTER HS PARAMETERS FOR BURNIN PROCESS&lt;/code&gt; option. After selecting the manufacturer (this was around the time when Packard Bell was sold to Zenith Data Systems), I seemed to have hit a dead end again.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/hscenter_manufacturers.png&#34; alt=&#34;HSCENTER.EXE manufacturer selection&#34; title=&#34;HSCENTER.EXE manufacturer selection&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/hscenter_hs_params.png&#34; alt=&#34;HSCENTER.EXE manufacturing parameters&#34; title=&#34;HSCENTER.EXE showing an input field for the manufacturer information&#34;&gt;
&lt;/p&gt;
&lt;p&gt;The main issue is that I don&amp;rsquo;t have the necessary information to proceed with the setup process. This means that the validity checks for the entered fields will fail, potentially causing a broken hidden sector and triggering the disk lock.&lt;/p&gt;
&lt;h2 id=&#34;a-field-trip-to-the-service-technicians&#34;&gt;A FIELD trip to the service technicians&lt;/h2&gt;
&lt;p&gt;Additionally, the setup process uses the part number and format switch to determine the exact machine configuration, so even in the best-case scenario, I would likely end up with a bare-bones system without any additional software or drivers.&lt;/p&gt;
&lt;p&gt;The command line switches &lt;code&gt;/BURNIN&lt;/code&gt; and &lt;code&gt;/MASTERCD&lt;/code&gt; both sent me to the same screen where I had to enter the missing manufacturing details.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/FIELD&lt;/code&gt; looked like a way better option. According to the description, this was meant to be used by the technical support department - e.g. after replacing a failed hard drive.&lt;/p&gt;
&lt;p&gt;I then realized that the various options were primarily targeted toward the different service departments. &lt;code&gt;/MASTERCD&lt;/code&gt; refers to the Master CD development process itself, &lt;code&gt;/BURNIN&lt;/code&gt; is meant for a technician that assembles and prepares the system, while &lt;code&gt;/FIELD&lt;/code&gt; is referring to a field-service technician.&lt;/p&gt;
&lt;p&gt;This was exactly what I was looking for. With the &lt;code&gt;/FIELD&lt;/code&gt; option, HSCENTER guided me through a simple interface where I could pick some of the components and software collections, presumably based on the different model variants sold at the time.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/hscenter_field_1.png&#34; alt=&#34;HSCENTER Menu (language selection)&#34; &gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/hscenter_field_2.png&#34; alt=&#34;HSCENTER Menu (country selection)&#34; &gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/hscenter_field_3.png&#34; alt=&#34;HSCENTER Menu (motherboard selection)&#34; &gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/hscenter_field_4.png&#34; alt=&#34;HSCENTER Menu (zipdrive selection)&#34; &gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/hscenter_field_5.png&#34; alt=&#34;HSCENTER Menu (radio card selection)&#34; &gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/hscenter_field_6.png&#34; alt=&#34;HSCENTER Menu (mouse selection)&#34; &gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/hscenter_field_7.png&#34; alt=&#34;HSCENTER Menu (additional graphics card)&#34; &gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/hscenter_field_8.png&#34; alt=&#34;HSCENTER Menu (Keyboard selection)&#34; &gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/hscenter_field_9.png&#34; alt=&#34;HSCENTER Menu (Infrared receiver)&#34; &gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/hscenter_field_10.png&#34; alt=&#34;HSCENTER Menu (Selecting the software pack)&#34; &gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/hscenter_field_11.png&#34; alt=&#34;HSCENTER Menu (overview)&#34; &gt;
&lt;/p&gt;
&lt;p&gt;After completing the process by using the &lt;code&gt;SAVE AND QUIT&lt;/code&gt; option, I started HSCENTER again to verify if the hidden sector had been written properly.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/hscenter_hardware_overview_with_hs.png&#34; alt=&#34;HSCENTER Menu (sector overview)&#34; &gt;
&lt;/p&gt;
&lt;p&gt;Finally! I was confident that I now had a properly written hidden sector. But there was only one way to find out.&lt;/p&gt;
&lt;p&gt;I rebooted the system again without the boot floppy attached, noticed the &amp;ldquo;Starting Windows 95&amp;hellip;&amp;rdquo; banner again, and prepared for yet another failure. Much to my surprise, the boot process finally continued, starting the usual Windows 95 setup procedure.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/first_preboot.png&#34; alt=&#34;First Windows 95 setup stage&#34; &gt;
&lt;/p&gt;
&lt;h2 id=&#34;flawless-victory-and-a-pretty-usual-setup-procedure&#34;&gt;Flawless victory and a pretty usual setup procedure&lt;/h2&gt;
&lt;p&gt;The remaining installation was pretty simple. After the initial hardware detection and the first Windows 95 bootstrap setup, the machine started into an additional unattended installer that performed all further installation steps.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/initial_hardware_detection.png&#34; alt=&#34;Windows 95 hardware detection (stage 1)&#34; title=&#34;Performing an initial hardware detection step. This also installs some additional drivers that were not present in a stock Windows 95 installation&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/initial_hardware_detection_part2.png&#34; alt=&#34;Windows 95 hardware detection (stage 2)&#34; title=&#34;Windows 95 detecting the installed Plug-and-Play compatible hardware&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/first_boot_featured.png&#34; alt=&#34;First successful boot of Windows 95&#34; title=&#34;And we have our first successful boot! Note the custom Packard Bell branding and the Internet Explorer addition!&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/installing_additional_software_1.png&#34; alt=&#34;Installing additional software (part 1)&#34; title=&#34;Installing additional software (part 1)&#34;&gt;
&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/installing_additional_software_2.png&#34; alt=&#34;Installing additional software (part 2)&#34; title=&#34;Installing additional software (part 2)&#34;&gt;
&lt;/p&gt;
&lt;p&gt;After each reboot, you can see parts of the build environment that is running in the background before it launches the &amp;lsquo;Please Wait&amp;rsquo; overlay. The text message in the editor refers to this environment as &amp;lsquo;Audit mode&amp;rsquo;, allowing for additional tests.&lt;/p&gt;
&lt;p&gt;Unfortunately, I couldn&amp;rsquo;t find any additional information about this environment, so I don&amp;rsquo;t know if this was only meant for the installation steps or if there was an additional service environment the service technicians could use.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/starting_unattended_installer.png&#34; alt=&#34;Starting the unattended installer&#34; &gt;
&lt;/p&gt;
&lt;p&gt;After about 30 minutes, the installation was complete and I could enter my username and password as well as the OEM product key.&lt;/p&gt;
&lt;h2 id=&#34;closing-words&#34;&gt;Closing words&lt;/h2&gt;
&lt;p&gt;And there it is. I had to reboot the system one more time before I was greeted by this ugly yet still somewhat familiar desktop. Even though I hadn&amp;rsquo;t seen the icons for more than 15 years, I immediately recognized the &amp;ldquo;Packard Bell Navigator&amp;rdquo; and the &amp;ldquo;CyberTrio&amp;rdquo; icons.&lt;/p&gt;
&lt;p&gt;Surprisingly, the system overview dialog claimed that the machine I was emulating is powered by a Pentium Pro CPU, even though I was running a Pentium II emulation. I&amp;rsquo;m not sure if this is a flaw caused by the emulation itself (it is properly detected in the BIOS though) or if the version of Windows 95 that shipped with the system was a bit too old in order to be able to properly detect the CPU.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/finished_1.png&#34; alt=&#34;Finished setup (screenshot 1)&#34; &gt;
&lt;/p&gt;
&lt;p&gt;Obviously, I didn&amp;rsquo;t do this whole restoration process just for a minimal Windows 95 installation. I was looking for the software that was originally bundled with the system.&lt;/p&gt;
&lt;p&gt;While skipping through the various categories of the Start menu, there was only one thing left to say: &amp;ldquo;This is it.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/virtual-archaeology-recreating-my-first-computer/finished_2.png&#34; alt=&#34;Finished setup (screenshot 2)&#34; &gt;
&lt;/p&gt;
&lt;p&gt;Was it worth it?&lt;/p&gt;
&lt;p&gt;Well, technically: Totally not. For retro-gaming purposes, it would have been way easier to just install Windows 98 (SE) without having to worry about this rather complicated process.&lt;/p&gt;
&lt;p&gt;But personally: Absolutely.&lt;/p&gt;
&lt;p&gt;This was the machine I used to learn how to use a computer. The machine I used to play my very first games, the machine I used to learn for school, and the machine I later used for my first Linux experiments.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d say this machine was pretty important for me.&lt;/p&gt;
&lt;p&gt;I miss it.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Using qemu-guest-agent as interface between VMs and Proxmox host systems</title>
      <link>https://felsqualle.com/posts/2022/12/qemu-guest-agent-as-proxmox-interface/</link>
      <pubDate>Fri, 09 Dec 2022 18:00:00 +0100</pubDate>
      
      <guid>https://felsqualle.com/posts/2022/12/qemu-guest-agent-as-proxmox-interface/</guid>
      <description>&lt;p&gt;Since virtual machines created with KVM/QEMU are not simple containers but quite isolated from the host&amp;rsquo;s environment, exchanging data and information between the host and the VMs can be a bit tricky. Therefore, QEMU offers a companion service called qemu-guest-agent for Linux guests. qemu-guest-agent acts as an interface between the VMs and the host system.&lt;/p&gt;
&lt;p&gt;Some features like passing ACPI information for a clean guest shutdown are pretty well-known. However, did you know that you can even send commands to your VMs directly from your Proxmox host system?&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.proxmox.com/en/proxmox-ve&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Proxmox Virtual Environment&lt;/a&gt;
 uses KVM/QEMU as virtualization technology. Since calling the qemu-guest-agent interface is not very intuitive by itself, Proxmox provides the &lt;a href=&#34;https://pve.proxmox.com/pve-docs/qm.1.html&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;qm guest&lt;/a&gt;
 command which acts like a bridge between the host system and the VMs.&lt;/p&gt;
&lt;p&gt;Using the &lt;code&gt;qm guest command&lt;/code&gt; requires &lt;code&gt;qemu-guest-agent&lt;/code&gt; to be installed inside your Linux guests. Since the guest agent is pretty essential in case you want to be able to do properly shut down the VMs without performing virtual powercuts or being able to create consistent backups, you want to install it on all of your Linux guests anyways.&lt;/p&gt;
&lt;p&gt;In order to use the following commands, you need to get the VMID of your virtual machines. You can get them either from the Proxmox dashboard or by running the &lt;code&gt;qm list&lt;/code&gt; command on your host system.&lt;/p&gt;
&lt;h2 id=&#34;reset-user-passwords-including-the-root-user&#34;&gt;Reset user passwords (including the root user)&lt;/h2&gt;
&lt;p&gt;I basically learned about &lt;code&gt;qm&lt;/code&gt; by accident. Usually, I tend to lock all local user accounts in order to make (virtual) console access impossible. Especially on VMs, this might not even be exactly helpful regarding security, but this is some sort of personal preference. Due to a configuration error on my end, I locked myself out of a VM running on my Proxmox host, so I was not able to SSH into it anymore.&lt;/p&gt;
&lt;p&gt;The obvious solution would be to shut down the VM, boot a rescue environment or live distribution, mount the disk and fix the configuration there. I knew the VM was performing some maintenance tasks in the background which do not like sudden interruptions, so I needed some sort of backdoor.&lt;/p&gt;
&lt;p&gt;This is where &lt;code&gt;qm guest passwd&lt;/code&gt; comes in handy. As the command suggests, it can be used to set user passwords in your VM, even if there&amp;rsquo;s no direct access to it anymore.&lt;/p&gt;
&lt;p&gt;As an example, the following command resets the password for the user root on VMID 100:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;qm guest passwd 100 root
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After invoking this command, qm will interactively tell you how to set a new user password for the account in question. If the user was previously locked with &lt;code&gt;passwd -l&lt;/code&gt;, the user will be automatically unlocked.&lt;/p&gt;
&lt;h2 id=&#34;run-arbitrary-commands-in-your-vm&#34;&gt;Run arbitrary commands in your VM&lt;/h2&gt;
&lt;p&gt;As you might have noticed, we have full access to the guest operating system using the QEMU Guest Agent (or QGA for short). Therefore, we can pass &lt;em&gt;any&lt;/em&gt; command to the guest. Side note: This is a friendly reminder that you really want to care about the security of your host system.&lt;/p&gt;
&lt;p&gt;The command we are looking for is &lt;code&gt;qm guest exec&lt;/code&gt; (again using VMID 100 as an example):&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;qm guest exec 100 -- ping google.com -c 3
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;qm exec&lt;/code&gt; will pass the command to the VM, execute it and wait for the command to finish or break with an exit code. Afterwards, it will return the exit code as well as the contents of stdout and stderr in JSON format. Please be aware that all commands are executed with root privileges.&lt;/p&gt;
&lt;p&gt;Long-running tasks can be sent to the background using the &lt;code&gt;--synchronous=0&lt;/code&gt; parameter. You can check the state of the process by using the &lt;code&gt;qm guest exec-status $yourVMID $PID&lt;/code&gt; command.&lt;/p&gt;
&lt;h2 id=&#34;get-additional-vm-information-and-perform-low-level-tasks&#34;&gt;Get additional VM information and perform low-level tasks&lt;/h2&gt;
&lt;p&gt;Next, let&amp;rsquo;s take a look at &lt;code&gt;qm guest cmd&lt;/code&gt;. Now, we are not executing commands within the guest operating system itself. Instead, with cmd we call various QGA commands on a lower level. Those commands are more focused on maintenance and less on interaction with the guest operating system.&lt;/p&gt;
&lt;p&gt;In order to use this feature, use the following command:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;qm guest cmd $yourVMID $Command
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I compiled a list of valid commands below. Just like the qm guest exec command, the output will be JSON formatted.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;&lt;strong&gt;Command                &lt;/strong&gt;&lt;/th&gt;
          &lt;th&gt;&lt;strong&gt;Description&lt;/strong&gt;&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;fsfreeze-freeze&lt;/td&gt;
          &lt;td&gt;Send the fsfreeze-freeze command to the guest, locks the file system on the guest machine&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;fsfreeze-status&lt;/td&gt;
          &lt;td&gt;Query fsfreeze-status, returns ‘frozen’ or ‘thawed’&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;fsfreeze-thaw&lt;/td&gt;
          &lt;td&gt;Thawing a frozen filesystem returns control to the guest operating system&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;fstrim&lt;/td&gt;
          &lt;td&gt;Issue the TRIM command to the VM’s block device(s). The VM has to be configured with the discard option or running with SSD emulation enabeld and the guest’s filesystem needs to be TRIM aware. Note: Since running a full fstrim takes quite a while, it’s not uncommon that the command seemingly fails with ‘qmp command ‘guest-fstrim’ failed – got timeout’. Check the guest’s message log for ‘guest-fstrim called’.&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;get-fsinfo&lt;/td&gt;
          &lt;td&gt;Returns all mounted filesystems&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;get-host-name&lt;/td&gt;
          &lt;td&gt;Returns the hostname of the guest system&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;get-memory-block-info&lt;/td&gt;
          &lt;td&gt;Displays memory information&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;get-memory-blocks&lt;/td&gt;
          &lt;td&gt;Displays memory information&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;get-osinfo&lt;/td&gt;
          &lt;td&gt;Displays various OS information like the distribution, kernel version and architecture&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;get-time&lt;/td&gt;
          &lt;td&gt;Get the system time of the guest OS&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;get-timezone&lt;/td&gt;
          &lt;td&gt;Get the timezone set in the guest OS&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;get-users&lt;/td&gt;
          &lt;td&gt;Get all users currently logged into the guest OS&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;get-vcpus&lt;/td&gt;
          &lt;td&gt;Get information about the assigned virtual CPUs&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;info&lt;/td&gt;
          &lt;td&gt;Return a list of available commands that can be issued through QGA&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;network-get-interfaces&lt;/td&gt;
          &lt;td&gt;Return a list of configured network interfaces along with traffic statistics&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;ping&lt;/td&gt;
          &lt;td&gt;Check if the VM is up and qemu-guest-agent is running. If this command times out, the VM is either offline or qemu-guest-agent is not working properly. Note: This is not a ‘traditional’ ping over a network connection. Instead, the host is trying to communicate with the VM over QGA.&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;shutdown&lt;/td&gt;
          &lt;td&gt;Send the ACPI shutdown command to the VM&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;suspend-disk&lt;/td&gt;
          &lt;td&gt;Send the suspend-to-disk command to the VM&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;suspend-hybrid&lt;/td&gt;
          &lt;td&gt;Send the suspend-hybrid (faster suspend-to-disk?) to the VM&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;suspend-ram&lt;/td&gt;
          &lt;td&gt;Send the suspend-to-ram command to the VM&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The QEMU Guest Agent provides an extensive interface for communication between the host system and the VMs.&lt;/p&gt;
&lt;p&gt;You can either send any arbitray command to the guest systems or use QGA to get some details about the VMs themselves. It is also possible to go a bit more low-level, so you can shutdown your VMs or let the host system take control over the file system by essentially freezing it.&lt;/p&gt;
&lt;p&gt;This is especially helpful if you want to use Proxmox&amp;rsquo; built-in snapshots. However, if you use the backup and snapshot functions from the Proxmox GUI, freezing and thawing is handled automatically.&lt;/p&gt;
&lt;p&gt;Currently, only a subset of the QGA protocol is abstracted by the &lt;code&gt;qm&lt;/code&gt; command. Since calling the other commands supported by QGA is a little bit more complicated, I’ll cover this in a seperate article.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Restoring support for 16-bit applications in modern Windows versions</title>
      <link>https://felsqualle.com/posts/2022/12/16-bit-support-in-64-bit-windows/</link>
      <pubDate>Fri, 02 Dec 2022 21:00:00 +0100</pubDate>
      
      <guid>https://felsqualle.com/posts/2022/12/16-bit-support-in-64-bit-windows/</guid>
      <description>&lt;p&gt;Windows has some pretty amazing backward compatibility. In many cases, you can run ancient 32-bit Win32 applications just fine on your current system.&lt;/p&gt;
&lt;p&gt;However, there&amp;rsquo;s one issue: If you ever tried to run a 16-bit application from the Windows 3.x days, any 64-bit Windows version (starting from Windows XP) will refuse to run the application with an error message indicating that you should ask the vendor for a compatible version.&lt;/p&gt;
&lt;p&gt;On the other hand, the modern 32-bit versions of Windows run these applications just fine.&lt;/p&gt;
&lt;p&gt;Thanks to two amazing open-source projects, you can bring back 16-bit compatibility to the 64-bit Windows era.&lt;/p&gt;
&lt;h2 id=&#34;why-did-microsoft-drop-support-for-16-bit-applications&#34;&gt;Why did Microsoft drop support for 16-bit applications?&lt;/h2&gt;
&lt;p&gt;Back in the old days, things were rather simple. Up to (and including) Windows ME, all consumer versions of Windows ran on top of MS-DOS, so support for 16-bit applications was basically built-in (Windows 98 for example even includes the Program Manager &lt;code&gt;PROGMAN.EXE&lt;/code&gt; from Windows 3.x). For the NT series, Microsoft had to pick a different route because it didn&amp;rsquo;t have any MS-DOS kernel to rely on. This led to the invention of NTVDM, the &lt;em&gt;NT Virtual DOS Machine&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;NTVDM was first introduced with the release of Windows NT 3.1 back in 1993. The NTVDM subsystem provides a runtime environment replicating a system running on MS-DOS 5.0 including all required BIOS calls, the Windows 3.1 kernel, and the Win16 API itself. Since NTVDM doesn&amp;rsquo;t provide real hardware emulation, it still relies on the CPU being able to run the original 16-bit code natively.&lt;/p&gt;
&lt;p&gt;Microsoft removed the NTVDM in all 64-bit Windows releases, so running DOS- and Win16-based applications is not possible anymore.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/16-bit-support-in-64-bit-windows/featured_win16_incompatible_notice.png&#34; alt=&#34;This app can&amp;rsquo;t run on your PC&#34; title=&#34;This app can&amp;#39;t run on your PC - yet.&#34;&gt;
&lt;/p&gt;
&lt;p&gt;This removal was not a deliberate choice made by the Windows developers. Instead, the lack of 16-bit support is caused by the design of the x86_64 architecture itself.&lt;/p&gt;
&lt;p&gt;CPUs based on the x86_64 architecture can run in two modes: &lt;em&gt;Long mode&lt;/em&gt; and &lt;em&gt;Legacy mode&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;In &lt;em&gt;Long mode&lt;/em&gt;, the CPU executes 64-bit code natively, while 32-bit applications run in a special compatibility mode. &lt;em&gt;Long mode&lt;/em&gt; doesn&amp;rsquo;t provide support for the VM86 CPU code required by NTVDM in order to enable 16-bit segments for addressing, so it had to be removed in all x86_64 versions of Windows.&lt;/p&gt;
&lt;p&gt;When the CPU runs in &lt;em&gt;Legacy mode&lt;/em&gt;, it provides full support for both 32-bit and 16-bit applications.&lt;/p&gt;
&lt;p&gt;Unfortunately, the only way to switch between both operating modes is a hard reset of the CPU itself, making a seamless integration of both operating modes impossible without restarting the whole system. Because booting a 64-bit Windows system will always force the CPU to run in &lt;em&gt;Long mode&lt;/em&gt;, there is no way to get back to a mode that allows native execution of 16-bit applications at runtime.&lt;/p&gt;
&lt;p&gt;In fact, even switching to the 32-bit compatibility mode within &lt;em&gt;Long mode&lt;/em&gt; itself (and &lt;em&gt;not&lt;/em&gt; the legacy mode) requires an additional abstraction layer. Every time you launch a 32-bit application on your 64-bit Windows installation, the operating system invokes the &lt;em&gt;WoW64 subsystem&lt;/em&gt; that acts as a compatibility layer providing the interface required to run the 32-bit application on a 64-bit system.&lt;/p&gt;
&lt;h2 id=&#34;like-fine-wine&#34;&gt;Like fine WINE&lt;/h2&gt;
&lt;p&gt;Considering the limitations the x86_64 architecture imposes, the only way to get 16-bit code running is by introducing an emulation layer that is able to replicate a CPU with the proper instruction set. Additionally, we need some software that provides a runtime environment for the Win16 applications.&lt;/p&gt;
&lt;p&gt;Combining an 80386 emulator and a Win16 runtime environment - that&amp;rsquo;s exactly what otya128 accomplished with their &lt;a href=&#34;https://github.com/otya128/otvdm&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;otvdm project&lt;/a&gt;
. otvdm uses the 80386 emulator developed by the MAME project and combines it with the well-known Windows runtime environment WINE.&lt;/p&gt;
&lt;p&gt;Installing otvdm is incredibly easy and provides a seamless integration in your current Windows installation.&lt;/p&gt;
&lt;p&gt;First, you have to download the current version from &lt;a href=&#34;https://github.com/otya128/otvdm/releases/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;GitHub&amp;rsquo;s release page&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;I recommend extracting the contents of the .zip archive you just downloaded to &lt;code&gt;C:\otvdm&lt;/code&gt; since this directory is available for all users on the system and is also not protected by UAC. The latter is important because in order to save DLLs in the virtual WINDOWS folder provided by otvdm, write access to the otvdm directory is required.&lt;/p&gt;
&lt;p&gt;All you have to do to get otvdm added to your system is to click on the &lt;code&gt;install&lt;/code&gt; shortcut. The installer tool adds numerous keys to your Windows registry, so administrative access is required. After a couple of seconds, the installer window closes and the installation is complete.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s it. Now, each time you want to launch a Win16 application, your system detects it and passes the program to otvdm which in turn is able to run it. In theory, otvdm also provides native DOS emulation as well, but since it is rather limited, a solution like 86Box or PCem might be a better choice for MS-DOS applications.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2022/12/16-bit-support-in-64-bit-windows/fileman_win16.png&#34; alt=&#34;FILEMAN.EXE from Windows 3.x - running on Windows 11.&#34; title=&#34;FILEMAN.EXE from Windows 3.x - running on Windows 11.&#34;&gt;
&lt;/p&gt;
&lt;p&gt;You don&amp;rsquo;t have to start otvdm manually after booting up your system or in case you want to launch a Win16 application. Thanks to the installed registry keys, it will automatically kick in as soon as it detects a Win16 application.&lt;/p&gt;
&lt;p&gt;Updating otvdm is pretty easy too: Just download the new release, replace all files (but &lt;em&gt;do not&lt;/em&gt; delete the contents of the &lt;code&gt;C&lt;/code&gt; and &lt;code&gt;WINDOWS&lt;/code&gt; directories) and run the installer again.&lt;/p&gt;
&lt;p&gt;Pretty incredible, if you ask me. And a great example why the open-source community is so important for software preservation. But that&amp;rsquo;s a topic for another day.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Welcome!</title>
      <link>https://felsqualle.com/posts/2022/11/welcome/</link>
      <pubDate>Sun, 20 Nov 2022 15:00:00 +0100</pubDate>
      
      <guid>https://felsqualle.com/posts/2022/11/welcome/</guid>
      <description>&lt;p&gt;&amp;ldquo;A new project? Again?!&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Yes, I know. My personal projects are as persistent as volatile memory. But hey - not this time!&lt;/p&gt;
&lt;p&gt;Welcome to &lt;strong&gt;felsqualle.com&lt;/strong&gt;, where we take a deep-dive into the wonderful world of mostly ancient (but awesome!) technology.&lt;/p&gt;
&lt;p&gt;For those who don&amp;rsquo;t know me: Hi, I&amp;rsquo;m felsqualle. I&amp;rsquo;m a systems administrator at the German webhosting company &lt;a href=&#34;https://www.manitu.de&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;manitu&lt;/a&gt;
 by day and a general-purpose tinkerer and project co-lead for &lt;a href=&#34;https://www.scummvm.org&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;ScummVM&lt;/a&gt;
 at night.&lt;/p&gt;
&lt;p&gt;I got my first PC when I was 6 years old, which means that I&amp;rsquo;m getting pretty close to hitting 25 years of PC usage. During my long-lasting journey, I used each and every consumer version of Windows (yes, I tried Windows 1.0!) and most versions of MS-DOS. My very first GNU/Linux experience was back in 2003 when I got my hands on a copy of &lt;a href=&#34;https://knoppix.net&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Knoppix&lt;/a&gt;
, which was one of the most famous distributions of live GNU/Linux systems back then while still being maintained today.&lt;/p&gt;
&lt;p&gt;At some point, I got interested in retro-computing - and for some reason, this passion never faded away. And this is what I want to share with you as well.&lt;/p&gt;
&lt;p&gt;So, what should you expect? Well, in this blog, I&amp;rsquo;ll not only share some of my sysadmins tricks as I did previously, but I&amp;rsquo;ll also tell you about devices and software that nobody really talks about anymore.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll find some familiar stuff and pieces of software where I believe that I have one of the &lt;em&gt;very&lt;/em&gt; last copies in existence.&lt;/p&gt;
&lt;p&gt;Sounds pretty exciting, doesn&amp;rsquo;t it?&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Immersive slowness or why I added artificial loading times for Myst to ScummVM</title>
      <link>https://felsqualle.com/posts/2021/07/immersive-slowness-or-why-i-added-artificial-loading-times-for-myst-to-scummvm/</link>
      <pubDate>Mon, 19 Jul 2021 09:00:00 +0100</pubDate>
      
      <guid>https://felsqualle.com/posts/2021/07/immersive-slowness-or-why-i-added-artificial-loading-times-for-myst-to-scummvm/</guid>
      <description>&lt;p&gt;Ever since I discovered the Myst series back in 2005, I&amp;rsquo;m in love with it. To me, the Myst series feels like an immersive trip to another world - it is truly something different compared to your average point-and-click adventure game. Needless to say that especially the first entries in the series - the original Myst and its successor Riven - are truly remarkable games.&lt;/p&gt;
&lt;p&gt;In my opinion, the immersion these games provide is partially created due to technical limitations.&lt;/p&gt;
&lt;p&gt;The original Myst was released in 1993 on this incredible new format called &amp;lsquo;CD-ROM&amp;rsquo;, allowing for a whopping 650 Megabytes of storage.&lt;/p&gt;
&lt;p&gt;At that time, CD-ROM drives were slow - like double-speed slow. This means access times of 80 to 200ms as well as a blazing transfer speed of 300KB/s under ideal conditions. Since even the largest hard drives had a capacity of around 1000 Megabytes, installing the game wasn&amp;rsquo;t an option. Furthermore, RAM constraints made any caching of the datafiles impossible.&lt;/p&gt;
&lt;h2 id=&#34;this-is-where-immersion-kicks-in&#34;&gt;This is where immersion kicks in&lt;/h2&gt;
&lt;p&gt;Myst uses some pretty clever compression techniques and an optimized file layout on the disc. Since each time you move through the game, your computer needs to load the next image. On the next move, it needs to load yet another image. The game basically forces you to &lt;em&gt;slowly walk&lt;/em&gt; through the worlds of Myst.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not sure if this was even part of the original design concept. However, it really feels that your are not supposed to rush through the game. Everything tells you to slowly explore while making sure not to miss any hints you need to solve the puzzles.&lt;/p&gt;
&lt;p&gt;While many adventure games are limiting playback speed on their own (e.g. due to character movement), this does not apply to Myst. When you play the game on original hardware, then the loading times seem perfectly fine since you expect them.&lt;/p&gt;
&lt;p&gt;As soon as you eliminate the loading times at all, e.g. by playing the games with ScummVM, then you might notice something is off. Now, you are not &lt;em&gt;forced&lt;/em&gt; to slowly explore, but you can simply &lt;em&gt;run&lt;/em&gt; through the game. Even though I&amp;rsquo;m obviously not forced to take advantage of the instant loading, the &lt;em&gt;feeling&lt;/em&gt; that I can do this is affecting the experience in a negative way for me.&lt;/p&gt;
&lt;h2 id=&#34;the-solution&#34;&gt;The solution&lt;/h2&gt;
&lt;p&gt;Looking for a way to improve the experience, &lt;a href=&#34;https://github.com/scummvm/scummvm/pull/3129&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;I recently implemented loading time simulation to ScummVM&lt;/a&gt;
. After activating this feature, it adds a delay between the different screen transitions in order to simulate the loading times of a real CD-ROM drive.&lt;/p&gt;
&lt;p&gt;In order to show you the difference, I prepared a small video. The first half of the video shows the previous without any loading times behaviour. Obviously, I&amp;rsquo;m really exaggerating here. It should be pretty obvious that this is clearly not how the game should be played. I enable the new loading time simulation at around the 50 second mark.&lt;/p&gt;
&lt;video class=&#34;video-shortcode&#34; preload=&#34;auto&#34; poster=&#34;&#34; controls width=&#34;100%&#34;&gt;
    &lt;source src=&#34;https://felsqualle.com/posts/2021/07/immersive-slowness-or-why-i-added-artificial-loading-times-for-myst-to-scummvm/myst-cdrom-delay.mp4&#34; type=&#34;video/mp4&#34;&gt;
    There should have been a video here but your browser does not seem
    to support it.
&lt;/video&gt;

&lt;p&gt;While the implementation was pretty straight-forward, it was not exactly easy to get the timing right. The main issue is that I don&amp;rsquo;t have really period correct hardware at the moment. The oldest system I own is a Pentium III with 500 MHz, featuring a 16x Philips CD-RW drive and a 48x &amp;ldquo;generic&amp;rdquo; drive. Fortunately, this generic drive is compatible with &lt;a href=&#34;https://web.archive.org/web/20140419193946/http://www.cd-bremse.de/cdbremse.htm&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Jörg Fiebelkorn&amp;rsquo;s CD-Bremse&lt;/a&gt;
. This tool allows you to throttle your CD-ROM drives by enforcing lower reading speeds. Unfortunately, I wasn&amp;rsquo;t able to go down to double-speed since the drive wouldn&amp;rsquo;t go slower than 4x.&lt;/p&gt;
&lt;p&gt;This meant I couldn&amp;rsquo;t rely on measurements alone. Instead, I read through many, &lt;em&gt;many&lt;/em&gt; datasheets of various CD-ROM drives. I also tried to emulate slower drives with 86Box since they allow setting transfer speeds as well. Even though I didn&amp;rsquo;t look at the code, I have the feeling that 86Box only emulates &lt;em&gt;transfer speeds&lt;/em&gt;, but not &lt;em&gt;access time&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&#34;limitations-and-whats-coming-up-next&#34;&gt;Limitations and what&amp;rsquo;s coming up next&lt;/h2&gt;
&lt;p&gt;All in all, the current delays are pretty arbitrary and might be subject to change in future versions. I tried to be as accurate as possible, but I simply can&amp;rsquo;t guarantee I got them absolutely right. It is also noteworthy that applied delays are the same on the original Myst and Myst: Masterpiece Edition. Due to the lack of samples, I wasn&amp;rsquo;t able to verify this, but I&amp;rsquo;d expect Myst: Masterpiece Edition to have longer loading times on the same hardware due to increased file size compared to the original edition.&lt;/p&gt;
&lt;p&gt;One option would be to make the delay a configurable option, allowing for a wider variety of &amp;lsquo;drives&amp;rsquo;. For now, the new option is disabled by default. Eventuelly, we&amp;rsquo;ll probably consider making this the new default. However, this will most likely not happen until the next release is out since I want to gather feedback from our users first.&lt;/p&gt;
&lt;h2 id=&#34;closing-words&#34;&gt;Closing words&lt;/h2&gt;
&lt;p&gt;Currently, the loading time simulation is available for Myst and Myst: Masterpiece Edition. The same concept could be applied to Riven as well. There are two things to consider:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Timing:&lt;/strong&gt; Internally, Riven uses some counters and timers to keep track of - well - time-based events. I need to dig deeper into the codebase to make sure that adding artificial delays between scene transitions won&amp;rsquo;t mess with the timing. This might even involve multiple playthroughs with varying conditions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The installation itself:&lt;/strong&gt; At least the version distributed on five CDs I own provides three options during the installation: Minimal, standard and full installation. Depending on which type you choose, loading times will obviously vary as well.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want to give this new feature a try, you need to download a current daily build from the ScummVM website and grab your copy of Myst or Myst: Masterpiece Edition. In you don&amp;rsquo;t own the game yet: &lt;a href=&#34;https://www.gog.com/game/myst_masterpiece_edition?pp=22d200f8670dbdb3e253a90eee5098477c95c23d&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Myst: Masterpiece Edition is available at GOG.com as digital download*&lt;/a&gt;
.&lt;/p&gt;
&lt;p&gt;After adding the game to ScummVM, you find the new feature in the in-game settings by hitting the &amp;lsquo;F5&amp;rsquo; key.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://felsqualle.com/posts/2021/07/immersive-slowness-or-why-i-added-artificial-loading-times-for-myst-to-scummvm/myst-cdrom-delay-option.png&#34; alt=&#34;image&#34; &gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What do you think? Any feedback is very welcome, especially when you have the chance to play the game on original hardware.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;*&lt;em&gt;: This link is a special affiliate link. If you visit GOG.com via this link, the ScummVM project gets a commission for any purchases you make. For you, the price is exactly the same while you are still supporting the project. Thank you ❤️!&lt;/em&gt;&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>Using Snap packages in Gentoo</title>
      <link>https://felsqualle.com/posts/2020/03/using-snap-packages-in-gentoo/</link>
      <pubDate>Mon, 09 Mar 2020 14:36:48 +0100</pubDate>
      
      <guid>https://felsqualle.com/posts/2020/03/using-snap-packages-in-gentoo/</guid>
      <description>&lt;p&gt;One of the main advantages of &lt;a href=&#34;https://snapcraft.io/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;snap packages&lt;/a&gt;
 is the possibility to use them not only on one Linux distribution like &amp;rsquo;traditional&amp;rsquo; packages, but on a wide variety of distributions without having to modify or rebuild them. Many distributions provide the necessary snapd daemon in their repositories.&lt;/p&gt;
&lt;p&gt;It is entirely possible to use snap packages with Gentoo too. Even building new snap packages with snapcraft and multipass or LXD will be possible afterwards.&lt;/p&gt;
&lt;h2 id=&#34;prerequisites&#34;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;First and foremost: systemd is a mandatory requirement since snapd is not compatible with OpenRC. Since snapcraft requires elevated privileges, sudo should be installed and properly configured too.&lt;/p&gt;
&lt;p&gt;In order to aquire the necessary ressources and permissions on the host system, snapd is using the AppArmor framework. In case you are using your own kernel, it might be necessary to manually enable AppArmor support in its configuration. The binary packages provided by the Gentoo project have AppArmor support already enabled.&lt;/p&gt;
&lt;p&gt;I won&amp;rsquo;t cover the migration of a Gentoo based system to systemd at this point. Since this topic is so extensive, I&amp;rsquo;ll publish a separate article at a later point in time.&lt;/p&gt;
&lt;h2 id=&#34;apparmor-and-systemd&#34;&gt;AppArmor and systemd&lt;/h2&gt;
&lt;p&gt;We need to compile &lt;code&gt;systemd&lt;/code&gt; with the USE flags &lt;code&gt;policykit&lt;/code&gt; and &lt;code&gt;apparmor&lt;/code&gt; enabled, &lt;code&gt;libseccomp&lt;/code&gt; with &lt;code&gt;static-libs&lt;/code&gt;. Therefore we&amp;rsquo;ll add the following entries to the &lt;code&gt;/etc/portage/package.use&lt;/code&gt; file:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;sys-apps/systemd policykit apparmor
sys-libs/libseccomp static-libs
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The snapd ebuild is masked by default, so we unmask it in the /etc/portage/package.accept_keywords file:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;app-containers/snapd ~amd64
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In the next step, we (re-) build systemd and AppArmor.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;emerge sys-apps/systemd
emerge sys-apps/apparmor
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In order to make AppArmor available right after the system is booted up, it&amp;rsquo;s necessary to modify the bootloader configuration. We need to add the following line to &lt;code&gt;/etc/default/grub&lt;/code&gt; or modify it accordingly:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;GRUB_CMDLINE_LINUX_DEFAULT=&amp;#34;apparmor=1 security=apparmor&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Afterwards, the GRUB configuration gets rewritten with the command&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;grub-mkconfig -o /boot/grub/grub.cfg
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Reboot the system to apply the chenges. In case you are using a different bootloader, please check the proper documentation.&lt;/p&gt;
&lt;h2 id=&#34;installing-snapd-itself&#34;&gt;Installing snapd itself&lt;/h2&gt;
&lt;p&gt;Finally, we install the snapd package and enable the necessary systemd units:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;emerge --ask app-containers/snapd
systemctl enable --now snapd
systemctl enable --now snapd.socket
systemctl enable --now snapd.apparmor
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;additional-permissions-for-snapcraft&#34;&gt;Additional permissions for snapcraft&lt;/h2&gt;
&lt;p&gt;In case we want to allow unprivileged user accounts to create new snaps with snapcraft, we need to add them to the groups &lt;code&gt;adm&lt;/code&gt; or &lt;code&gt;lxd&lt;/code&gt;. The &lt;code&gt;adm&lt;/code&gt; group is required if you want to use multipass for providing the build VM, &lt;code&gt;lxd&lt;/code&gt; in case you want to use LXD instead.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;gpasswd --add username adm
gpasswd --add username lxd
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That&amp;rsquo;s it - now you should be able to run all snap packages you want. You can verify that your installation is working by checking out the &lt;a href=&#34;https://snapcraft.io/hello-world&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;hello-world snap&lt;/a&gt;
 provided by Canonical.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>apache2: Redirect multiple domains to the same website</title>
      <link>https://felsqualle.com/posts/2020/01/apache2-redirect-multiple-domains-same-website/</link>
      <pubDate>Sun, 12 Jan 2020 00:00:00 +0000</pubDate>
      
      <guid>https://felsqualle.com/posts/2020/01/apache2-redirect-multiple-domains-same-website/</guid>
      <description>&lt;p&gt;Over the last couple of years, I aquired a nice collection of various domains. Since I only keep them for historical purposes, I decided to redirect all domains to this blog domain. Usually, you can achieve such a redirection by simply pointing all your domains to the same virtual host within your web server configuration in case you are using WordPress. However, this won&amp;rsquo;t work when using some caching plugins. Some .htaccess magic will help though.&lt;/p&gt;
&lt;p&gt;The following .htaccess snippet redirects every domain that&amp;rsquo;s not called &lt;code&gt;www.targetdomain.com&lt;/code&gt; to the - well - desired target domain:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;RewriteEngine on
RewriteCond %{HTTP_HOST} !^www.targetdomain.com
RewriteRule (.*) https://www.targetdomain.com/$1 [R=permanent,QSA,L]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The redirection also works for all queries - &lt;code&gt;https://olddomain.com/foo&lt;/code&gt; will always redirect to &lt;code&gt;https://www.targetdomain.com/foo&lt;/code&gt;.&lt;/p&gt;</description>
    </item>
    
    <item>
      <title>apache2: mod_autoindex and UTF-8</title>
      <link>https://felsqualle.com/posts/2020/01/apache2-mod-autoindex-and-utf8/</link>
      <pubDate>Fri, 10 Jan 2020 15:00:00 +0100</pubDate>
      
      <guid>https://felsqualle.com/posts/2020/01/apache2-mod-autoindex-and-utf8/</guid>
      <description>&lt;p&gt;For my &lt;a href=&#34;https://scummvm.serra.me&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;ScummVM nightly server&lt;/a&gt;
, I’m using the apache2 module mod_autoindex. I noticed that – even though I enabled UTF-8 support in the apache configuration – one vHost was still served with ISO-8859 encoding.&lt;/p&gt;
&lt;p&gt;The problem is that the autoindex module simply ignores the global charset settings. Instead, it insists on using ISO-8859 for the vHost without further configuration.&lt;/p&gt;
&lt;p&gt;In order to enable UTF-8 for sites generated by mod_autoindex, the parameter &lt;code&gt;Charset=UTF-8&lt;/code&gt; must be added to the IndexOptions of the specific vHost configuration.&lt;/p&gt;</description>
    </item>
    
  </channel>
</rss>

