Edenwaith Blog

Differences Between the Floppy Disk and CD Versions of Quest for Glory 4

19th October 2021 | Games

It's October, which means it is time to play some Quest for Glory: Shadows of Darkness! I've played this game numerous times over the past 25 years, and I still occasionally find new things, but this year I decided something different by playing both the floppy disk and CD versions of the game. This game was initially released in 1993, but it was rushed out to make the holiday season and was notoriously buggy. The CD version of the game followed a year later which included voice acting, in addition to many bug fixes.

I played through both the floppy disk and CD versions to look for differences between the two, in addition to various bugs (what is this — a bug hunt?). I was able to get through most of the floppy disk version, but when I returned to the Dark One's cave, it got stuck and acted like the hero kept trying to leave the cave instead of entering it. Unfortunate, but close enough for horseshoes and hand grenades to compare these two versions of Quest for Glory 4.

Floppy

CD

Other

Going through these two versions of this game shows the importance of proper testing and how much effort goes into creating a game. While the floppy disk version was mostly playable until the end, it was interesting to come across areas which were buggy, missing animations, or were changed for the CD version. The update did clean up a number of issues, but even still there were a number of misses (such as missing or incorrect voice tracks). What wasn't expected several years after the game's release would be speed-related bugs that would plague many of the early 90s Sierra games. Fortunately modern utilities like DOSBox, ScummVM, and the NewRisingSun patch help immensely to curtail the worst of the issues and make this game a joy to experience over and over again.

Quest for Glory 4 Wraith Freezing Bug

18th October 2021 | Games

Quest for Glory: Shadows of Darkness (QFG4) is no stranger to bugs. Errors 47 and 52 drive more fear into gamers' hearts than all of the undead creatures featured in this horror-infused game. The original version of the game had been rushed out far too early to meet the impending holiday deadline, which resulted in a very buggy game. I recently tried playing through the floppy disk version of QFG4 and got most of the way through until the game got stuck near the end upon returning to the Dark One's cave. The CD version of the game fixed a number of bugs, added some polish, and included the excellent voice acting, which makes the game feel barren without it.

Yet, even with these improvements, there remained a number of bugs, some which were only unearthed later as computers got faster. Many Sierra games from the early 90s were similarly afflicted by speed-related issues, which required utilities like Turbo or MoSlo to try and slow down the computer so the game would run properly. The indispensable NewRisingSun patch has been an incredible blessing to fix many of the most egregious issues in QFG4. When I first played this game, I encountered both Errors 47 and 52 within the first hour of game play. Fortunately, with the NRS patch, I haven't seen either of these errors for many years when I play the game under DOSBox.

Unfortunately, I have come across a random bug (that others have also noticed) when after fighting a wraith at its burial mound, the game would essentially lock up. One can see the drop down menu, but pretty much everything except the Settings menu is greyed out. In the margins of one of my maps I had scribbled down this note about this issue:

"When fighting wraiths, wait until it appears before fighting it. Otherwise, the game will freeze after the fight."

I found that this issue was somewhat random at times. Sometimes I would go into a screen with a wraith and have no problem triggering the wraith to appear. Other times the wraith would be a no-show. The key is to look for the red dot floating around the burial mound. If you don't see it immediately, then leave the screen and return at another time. But once the floating red dot is visible, approach the mound and wait until the wraith fully appears before initiating combat.

I never encountered this particular problem with the floppy disk version of QFG4, so this particular bug might be a side effect of the NRS patch. Another post recommends removing the wraith patch files 53.SCR and 53.HEP from the PATCHES folder. These patches fix the wraith draining health bug, but might also be causing this occasional issue, as well. A temporary work around is remove the two 53 files, fight the five wraiths at their burial mounds, then replace the two patch files. Otherwise, just return to the mounds if the wraith doesn't appear immediately.

And as always, check out Sierra Help for numerous other tips and patches to resolve various issues in Quest for Glory 4 and other Sierra games.

Implementing NSServices for macOS

18th September 2021 | Programming

As far back as 2005, I was looking at adding support for NSServices to Permanent Eraser. However, due to poor design decisions, it was not possible to implement them due to timing issues. With Snow Leopard's improved support for NSServices, I created a plug-in service to allow a user to right-click on a file and be able to erase it with Permanent Eraser.

With the intended successor to Permanent Eraser 2, I have been experimenting with adding support for NSServices. NSServices have been around for a long time, so the available information is somewhat jumbled, especially with some additions which were added with Mac OS X 10.5.

Info.plist

The first step is to add NSServices key-value pair the Info.plist. Below is a simple example to configure a single service.


<key>NSServices</key>
<array>  
	<dict>
		<key>NSMenuItem</key>
		<dict>
			<key>default</key>
			<string>Service Menu Title</string>
		</dict>
		<key>NSMessage</key>
		<string>doSomeStuffMethodName</string>
		<key>NSPortName</key>
		<string>SomeApp</string>
		<key>NSRequiredContext</key>
		<dict/>
		<key>NSSendFileTypes</key>
		<array>
			<string>public.item</string>
		</array>
	</dict>
</array>

Here is another example with some excellent comments on what the various keys represent.


<key>NSServices</key>
<array>        
    <dict>
        <key>NSMenuItem</key>
        <dict>
            <key>default</key>
            <string>Folder Handling Demo</string>
        </dict>
        <key>NSMessage</key>
        <string>handleServices</string> <!-- This specifies the selector -->
        <key>NSPortName</key>
        <string>Tmp</string>       <!-- This is the name of the app -->

        <!-- Here we're limiting where the service will appear. -->
        <key>NSRequiredContext</key>
        <dict>
            <key>NSTextContent</key>
            <string>FilePath</string>
        </dict>
        <!-- This service is only really useful from the Finder. So
         we want the finder only to send us the URI "public.directory"
         which *will* include packages (on the off-chance you want to
         see the full package directory name) -->
        <key>NSSendFileTypes</key>
        <array>
            <!-- Check out "System-Declared Uniform Type Identifiers"
             in the Apple documentation for the various UTI types.
             In this example, all we want is a directory, which is
             a super-type to other types (e.g. public.folder) -->
            <string>public.folder</string>
        </array>
    </dict>
</array>

For Permanent Eraser, I have a more complex version set up.


<key>NSServices</key>
<array>
	<dict>
		<key>NSKeyEquivalent</key>
		<dict>
			<key>default</key>
			<string>E</string>
		</dict>
		<key>NSMenuItem</key>
		<dict>
			<key>default</key>
			<string>Erase File</string>
		</dict>
		<key>NSMessage</key>
		<string>eraseService</string>
		<key>NSPortName</key>
		<string>Permanent Eraser</string>
		<key>NSRequiredContext</key>
		<dict>
			<key>NSTextContent</key>
			<array>
				<string>FilePath</string>
			</array>
		</dict>
		<!--
		<key>NSSendFileTypes</key>
		<array>
			<string>public.file-url</string>
			<string>public.url</string>
			<string>public.item</string>
			<string>public.folder</string>
		</array>
		-->
		<key>NSSendTypes</key>
		<array>
			<string>NSStringPboardType</string>
			<string>NSURLPboardType</string>
			<string>public.utf8-plain-text</string>
			<string>public.url</string>
			<string>public.file-url</string>
		</array>
	</dict>
</array>

Commented out is the NSSendFileTypes key-value pair, which is similar in its functionality to NSSendTypes. According to the Services Implementation Guide the primary difference is that NSSendFileTypes only accepts Uniform Type Identifiers (UTIs), whereas NSSendTypes can accept the older style pasteboard types (e.g. NSStringPboardType, NSURLPboardType) and UTIs.

To localize strings for keys like NSMenuItem and NSServiceDescription, create a ServicesMenu.strings file with the translated strings. If a string (such as for NSServiceDescription) is particularly long, use a shorter token like SERVICE_DESCRIPTION for the key in the strings file.

The Code

From the AppDelegate's applicationDidFinishLaunching method, I call the following setupServiceProvider() method, which creates an instance of the ContextualMenuServiceProvider class and then calls NSUpdateDynamicServices() to dynamically refresh any new services.


func setupServiceProvider() {
	NSApplication.shared.servicesProvider = ContextualMenuServiceProvider()
	// Call this for sanity's sake to refresh the known services
	NSUpdateDynamicServices()
}
The ContextualMenuServiceProvider.swift file:

import Foundation
import Cocoa

class ContextualMenuServiceProvider: NSObject {

	@objc func eraseService(_ pasteboard: NSPasteboard, userData: String?, error: AutoreleasingUnsafeMutablePointer <NSString>) {
		
		// Just for reference, looking at the number of available pasteboard types
		if let pBoardTypes = pasteboard.types {
			NSLog("Number of pasteboard types: \(pBoardTypes.count)")
			NSLog("Pasteboard Types: \(pBoardTypes)")
		}
		
		// NSFilenamesPboardType is unavailable in Swift, use NSPasteboard.PasteboardType.fileURL
		guard let pboardInfo = pasteboard.string(forType: NSPasteboard.PasteboardType.fileURL) else { 
			NSLog("Could not find an appropriate pasteboard type")
			return
		}
	
		let urlPath = URL(fileURLWithPath: pboardInfo)
		let standardizedURL = URL(fileURLWithPath: pboardInfo).standardized
		let messageText = "Hola info \(pboardInfo) of type \(pboardInfoType) at \(urlPath.absoluteURL) with standardized URL \(standardizedURL)"
		NSLog(messageText)
	}
}

File System Path Types

For Permanent Eraser, I need to get the path for the selected file. When parsing out the returned file path from the pasteboard info, it returned an unintelligible path like: file:///.file/id=6571367.8622082855 . This is certainly not what I was expecting and resulted in some confusion until I learned more about the various methods that macOS can represent a file's path. What I was receiving here was a file reference URL. The advantage of this type is that it can point to the same file, even if the original file is moved (somewhat similar to how a Mac alias can still point to the correct file even if it is relocated). However, what I wanted was a path-based URL, which is easier for me to read and work with.

For most URLs, you build the URL by concatenating directory and file names together using the appropriate NSURL methods until you have the path to the item. A URL built in that way is referred to as a path-based URL because it stores the names needed to traverse the directory hierarchy to locate the item. (You also build string-based paths by concatenating directory and file-names together, with the results stored in a slightly different format than that used by the NSURL class.) In addition to path-based URLs, you can also create a file reference URL, which identifies the location of the file or directory using a unique ID.

All of the following entries are valid references to a file called MyFile.txt in a user’s Documents directory:

Path-based URL: file://localhost/Users/steve/Documents/MyFile.txt

File reference URL: file:///.file/id=6571367.2773272/

String-based path: /Users/steve/Documents/MyFile.txt

There are several ways to convert the file reference URL to a more readable format. Using a quick AppleScript from the Terminal, one can get the string-based path this way:

osascript -e 'get posix path of posix file "file:///.file/id=6571367.4833330"'

In Swift the conversion to a path-based URL is like:

let standardizedURL = URL(fileURLWithPath: pboardInfoFileURL).standardized

Testing

To test if your new service works, copy the application into the Applications folder and then launch your app to ensure that the system recognizes the new service. The NSUpdateDynamicServices() call is used to help refresh the system. However if it appears that macOS is not updating properly, then try running the following commands from the Terminal:

/System/Library/CoreServices/pbs -flush

To list the registered services, use pbs with the -dump_pboard option.

/System/Library/CoreServices/pbs -dump_pboard

References

Another Day, Another Buckazoid

18th June 2021 | Writing

This piece of poor prose is inspired by an idea posed by the Space Quest Historian: What was the Sarien guard doing who was standing outside the laundry room on the Deltaur?

Another day, another buckazoid...

Fooblat licked his lips again, mostly out of boredom, but also because they were so dry. The Deltaur's air circulation system pushing around stale air through the ship didn't much help either.

Not seeing anyone else in the hallway, the Sarien dared to remove his hand from his holstered Pulseray and patted around his pockets for any Bert's Labion Bees lip balm...nothing. The only thing he had in his pocket was a badge displaying his name and the desultory word "Temp" in bright red letters. As one of the "inferior" Red Badges, he fully expected the full-time Blue Badge employees to spit on him first before bothering to talk to him. He was only here because he needed the money. The temp job was almost up, and once they made shore, he was going to hop off this place like a tick leaping off a drowning Hipoglopus.

Standing in front of the Emergency Exit was where he had been stationed today. And it was locked. What in the name of the seven suns was the point of guarding a LOCKED FREAKING DOOR?! Fooblat sighed heavily and then shifted his weight from one leg to another to prevent them from falling asleep. This shift was terminally boring, which had reduced his mind to the consistency of mucus from an Antarean Slime Devil. He couldn't wait for this shift to finish so he could get back to playing some video games.

The door to his right unexpectedly swiped open and another guard exited the laundry room. Fooblat straighted up, doing his best to look alert and professional. He stared straight forward, hoping that the newcomer would ignore him. As one of those "lowly" temps, the regular crew had taken immeasurable delight in teasing and taunting the Red Badges.

"Got a light?" came an unexpected question.

Not expecting the odd question, a flustered Fooblat picked out the first random response swimming around in the non-liquified bits of his brain. "Sorry, don't drink."

The response didn't seem to do anything to deter the newcomer. The other Sarien continued on with a variety of questions and random dialog. Fooblat tried his best to sound disinterested, hoping this annoying visitor would go away.

"Is this the way to the Star Generator?"

"Don't ask me, I just work here," Fooblat responded, trying to be as unhelpful as possible.

Fooblat finally dared to glance at the other Sarien, who was still wearing his helmet indoors. Who was this, anyway? Butston? Loplatz? Who knew, Sariens all looked the same with their helmets on. Still, it piqued his curiosity. "Hey bud, what's with the helmet indoors?"

"Ssshh!" the other guard exclaimed. "I'm hiding from the boss."

"Oh," Fooblat said dimly, futilely trying to grasp this round bit of logic. "That makes sense, I guess."

The mystery guard continued his litany of inane questions. At one point, Fooblat swore the other Sarien was leaning in to kiss him. But with THESE dry lips?! Fortunately nothing came of it besides his startled imagination and more dull conversation. Just as he was about to make good use of the Pulseray to bore an extra orifice into the chatty guard's skull, he was finally asked something interesting.

"Have you played King's Quest III?"

Fooblat's eyes opened wide with delighted surprise. "Yes," he happily responded. "I just bought it last week. It was on sale for 128,000 Buckazoids at Tiny's Holodisk Shop. What a bargain!!!"

Radio Shock had already been sold out of the game, so he was glad he was able to still procure a copy at Tiny's. He tried to engage the guard further about the game, but apparently he had only spent the time doing chores for the evil wizard. Fooblat got an odd impression that this other Sarien secretly enjoyed sweeping and emptying chamber pots.

Eventually, a dull silence uncomfortably wedged itself between them. Fooblat returned to staring at the opposite wall, counting down the microseconds until his shift was over.

"Well," the other guard said, rudely interrupting the blissful silence. "I'm off to find the little boys room."

Fooblat heaved another sigh once the other guard left. If his boss had caught him chatting with another guard, he would have gotten yelled at again. Damn Blue Badges with their smugness and steady paychecks.

His mind returned back to a state of thick, primordial soup, the only singular thought being that of getting back to King's Quest III after his shift. He knew there must be a key for that locked cabinet in the wizard's study — but where?

He checked the time and frowned. It was already past his shift and his replacement hadn't shown up yet. Typical. Never could trust another Sarien to show up on time. This was digging into his precious game time. He frowned again in continued annoyance. This temp job would be done in two days, and then, goodbye Deltaur!

Fooblat frowned for the third time in as many minutes. Still no replacement. Screw it. He was done with his shift. There were plenty of other guards to patrol the ship.

Besides, it's not like anyone was going to use this Emergency Exit...

EdenList 2.2.0 + The Retirement of 33 RPM

3rd June 2021 | Edenwaith

A couple of short announcements:

EdenList 2.2.0

A couple months ago, EdenList 2.2.0 for iOS and iPadOS was released. This now includes search functionality to quickly dig through large lists. This particular feature is one of my favorite features ever, and is used nearly every time I use the app. The app has also been tested and verified on iOS/iPadOS 14.

EdenList can be downloaded here free of charge.

The Retirement of 33 RPM

I've mulled numerous times over the past several years on what I plan on doing with 33 RPM, a utility which can change the speed and pitch of music. 33 RPM is highly dependent upon Apple's now-deprecated QuickTime framework, so getting 33 RPM to work on more modern versions of macOS (or even Apple's other platforms like iOS) would require an entire rewrite, which is an arduous task. Most of my time these days has been focused on more games-related endeavors, the occasional update to EdenList, and a slow rewrite of Permanent Eraser.

After more than eight years of inactivity, the time has come to formally retire 33 RPM where it will join the archives of other retired Edenwaith software. I've had a variety of ideas of where to take 33 RPM, but those ideas are best served by more dedicated apps. If you are looking for a good iPhone music player, check out Cs Music Player. For music transcription, there is the awesome Capo for Mac, iPhone, and iPad.

Many of Edenwaith's Mac projects were started in the early days of Mac OS X to fill various niches or to scratch a personal itch of something I wanted to create. 33 RPM is still a useful little (and free) utility for Macs running Mac OS X 10.4 (including PowerPC Macs) through macOS 10.14. That's not a bad run. So long 33 RPM, and thanks for all the fun.

Using a PowerBook in 2021

11th April 2021 | Apple

It has been recently announced that the venerable TenFourFox web browser for PowerPC (PPC) Macs was going to cease regular development, which rekindled my interest in playing around with my trusty PowerBook G4, which only gets occasional use if I'm testing a PowerPC version of some of my own software. Such is the way of aging hardware and software: the necessity to support them wanes over time, but it does question how useful can an 18 year old laptop be in 2021. Can it still be useful, or is it relegated to a hobbyist's endeavors?

With the retirement of TenFourFox (and its companion Classilla), I learned about another modernized web browser for PowerPC Macs: Leopard Webkit. TenFourFox is an amazing and much appreciated project to try and bring a modern web browser to PPC systems, but it runs very sluggishly on my PowerBook. Leopard WebKit, however, runs very quickly in comparison and makes browsing the web on a Mac from 2003 feel viable. Leopard WebKit also comes with a number of scripts to help modernize Mac OS X Leopard, including downloading QuickTime 7.7 (my version was slightly older at 7.6.9).

However, TenFourFox has been leveraged to be even more useful with various extensions, add-ons, tips and tricks. FoxBox is useful for for working with specific websites, especially YouTube. There are also a number of other tricks that can optimize the speed of TenFourFox to remove unnecessary overhead with modern web browsing.

One of the things that prevents a PowerPC Mac from being fully useful today is the same thing which hamstrung Classic Mac OS over time — the internet. Trying to keep up with the latest security is an eternal game of cat-and-mouse, and as older systems no longer get the latest security certificates and patches, it renders less and less network services available. Even on Mac OS X Snow Leopard, I can no longer work with GitHub, and a number of websites are not accessible.

When I look at the Docks on my PowerBook versus my modern iMac, I see many of the same or similar tools: web browsers, Xcode, an SCM utility (Cornerstone, but now Tower), a Terminal, Permanent Eraser, Transmit, DOSBox, Acorn, AGI Studio, BBEdit and TextWrangler. Revisiting this older system has been an interesting task to search for "new" software or just try to update things as much as possible and discovering a couple of interesting gems in the process.

But how useful is this software? For some tasks, they can easily do the job. I still have a license for BBEdit 8 and was able to get a copy of BBEdit 8.2.6 (which is what is currently being using to compose this blog post), Acorn 1.5.5 to create the poster image, and Transmit 5.1.9 to upload to the website. Even a couple of years ago, I used a similar combination of BBEdit Lite 6.1.2 and Transmit 1.7 on Mac OS 9 to update a blog entry.

[Update: 25 April 2021] I've recently been reading up on how to design fonts, and the book I'm reading mentions using Adobe InDesign to make the full use of OpenType fonts. InDesign is a program I haven't had much use for, but a number of years ago, Adobe did provide the full set of Creative Suite 2 products for free from their website after they shut down the activation servers for the these products. These are fairly mature products and can still be quite useful, as long as you have the proper hardware and operating system to run them. There are even active game developers still using Adobe Photoshop CS2 because it does everything they need, evidence that the latest doesn't always equate to the greatest for everyone.

Unfortunately, a cursory search shows that Adobe has taken down the original page which allowed one to download Acrobat 7 and the other CS2 products. Fortunately, there are other options such as using sites like the Wayback Machine, Macintosh Repository, and Macintosh Garden which still have backup copies of these software packages. If you have a PowerPC Mac and need to do some image editing, illustration, or layout design, these apps are still wonderful tools, great if you might need to use them only occasionally and don't want to pay the monthly "Adobe tax" for their Creative Cloud service.

It's been 20 years since the release of Mac OS X, and in many regards, the general usage of this operating system has not changed too much since its debut (certainly not as much as the jump from Mac OS 9 to X). We reached a useful level with our computers years ago as platforms have matured, and additional features have been mostly incremental improvements.

There have been some developers who have supported a platform well past the expected expiration date. Even a number of my own software products such as Permanent Eraser, EdenMath, 33 RPM, and EdenList for Mac (in addition to numerous retired projects) still work on PowerPC Macs. I've done my best to maintain support for PowerPC Macs for many years, but it certainly has become increasingly difficult to be able to support both older and newer systems. With the release of the Apple Silicon Macs, this is even more of a sign that support for PowerPC is only waning even further, and so will Intel Macs over time. And in another 15 years, perhaps we'll be migrating to yet another hardware architecture.

To answer the original question, can PowerPC Macs still be useful? It ultimately depends on what you are trying to do. If you are not heavily reliant upon network services, but have some old utility or game from the PowerPC-era, then they can still be feasible machines. But for modern day-to-day workhorses, they have already been put out to pasture.

Resources

King's Quest IV Easter Eggs (AGI Edition)

4th April 2021 | Games

In the past several years I've written up blog posts which have listed various Easter eggs, glitches, and oddities in old Sierra games. This year is dedicated to all of those fun quirks in the AGI version of King's Quest IV, which comes with several Easter eggs not found in the more commonly played SCI version of the game.

Software Pirates

King's Quest IV was released in 1988 (or as Al Lowe mentioned in an interview, it was specifically released on September 31st), during an era when games were released on floppy disks, which were easily copied by kids and cheapskates. That so many of their games were being pirated was not lost on Sierra. At the start up screen, type ALT + D to get into debug mode and then press ENTER/RETURN a couple of times. At the command prompt, type pirate and one of the following images will appear.

KQ4 Rap

This and "Beam Me" are likely the most well known Easter eggs in the AGI version of KQ4 (which were excluded from the SCI version of the game).

After defeating Lolotte, return to the dungeon cell and type rap kq, in which Rosella will perform some awkward gyrations along to some snarky prose by the exhausted game developers. Making games is tough work, deadlines loom, and Christmas doesn't move.

Beam Me

Similar to the KQ4 Rap egg, after defeating Lolotte, go to the corridor outside of the dungeon and type beam me, which will take Rosella to a secret room populated by the game developers. Note that John Hamilton is one of the mentioned personnel when you look at the people in this room. More about John in a bit.

John was Here

I came across this secret "cave painting" a few months ago when inspecting the priority bands in various rooms of this game. The "John" mentioned in this scrawling is likely John Hamilton, one of the programmers on the AGI version of KQ4. More about this Easter egg was written here.

James Whale

If you look at the boat in the whale's mouth, it identifies the skeleton as "James". This is James in a whale, which might be a play on the name of James Whale, the director of classic horror films like Frankenstein and The Invisible Man.

Procrastination

One of the innovative features of King's Quest IV was not just the passage of real time (King's Quest III had already implemented that feature), but the day to night sequence. There is a limited amount of time to complete the game, generally enough, but it is possible to lose by waiting too long. If the game becomes 6:00 AM and you haven't killed Lolotte yet, the game will be over, as displayed in these screenshots for the "death" scene.

Letting Graham Die

This is not an Easter egg, per se, but probably not a scene you see too often unless you forgot to get the magic fruit or ate it.

Secret Code

Early boxed copies of King's Quest IV bore a sticker for a contest to win a trip to England. If you could complete the game and earn all 230 points, you would be presented with a secret code, which was used as evidence that you completed the entire game for the contest. The updated SCI version of the game included in later removed the code, since the contest was long over.

And if you ever wondered if someone did win that fabulous trip to England (or other fantastic prizes), here's the answer:

Resources

Installing the AGI Version of King's Quest IV

21st February 2021 | Games

One thing which makes King's Quest IV unique is that it was the only Sierra game which was simultaneously developed for two different game engines: AGI and SCI. The version most people have played is the SCI version, which featured advancements such as higher graphics resolution and sound card support. This is also the version which comes with KQ collections. The AGI version was intended for computers which wouldn't be able to take full advantage of the new features in the SCI version, plus it was provided as the base to port to older systems like the Apple IIGS and Atari ST. The only way to play the AGI version of KQ4 is to do some searching on the internet or procure a very rare boxed copy.

Like nearly all games of this era, KQ4 came on multiple floppy disks. The AGI version came on three 3.5" 720K disks, whereas the SCI version required four 3.5" disks. If I was playing this game on a Tandy in 1988, I would be doing disk swapping as Rosella moved throughout the land of Tamir, but with modern computers, the preferred method is to emulate installing the game onto a hard drive. Using DOSBox to install a game from a CD is not overly complex, but trying to install from multiple floppy disks involves more work. This blog details the process to install King's Quest IV (or another multi-floppy disk DOS game) onto a Mac.

To start, I used Disk Utility to create the disk images, but they are saved out as dmg instead of img. To convert the disk images to be useful to DOSBox, I used the following commands from the Terminal:

hdiutil convert Disk1.dmg -format RdWr -o disk1.img
hdiutil convert Disk2.dmg -format RdWr -o disk2.img
hdiutil convert Disk3.dmg -format RdWr -o disk3.img

Once I had created disk images for all three floppy disks for the AGI version of King's Quest IV, I then tried the following command in DOSBox 0.74-3 to mount all three disk images:

imgmount a -t floppy disk1.img disk2.img disk3.img

The resulting error:

Using multiple files is only supported for cue/iso images.

It looks like I am not the only person who has encountered this issue. Older versions of DOSBox supported this functionality to mount multiple disk images, but it appears this feature hasn't been reintroduced back in the past decade. Since mounting multiple floppy disk images wasn't working, I tried an alternate approach by converting the DMG files to ISO images.

hdiutil convert Disk1.dmg -format UDTO -o disk1.iso
hdiutil convert Disk2.dmg -format UDTO -o disk2.iso
hdiutil convert Disk3.dmg -format UDTO -o disk3.iso

For some odd reason, hdiutil appends a cdr file extension to each of the ISO files, but a simple rename is all that is needed to finish creating the ISO files. Next up I tried to mount the series of ISO files.

imgmount a -t iso disk1.iso disk2.iso disk3.iso

Error: 
Could not load image file: /Applications/DOSBox/dosbox/SIERRA/KQ4/KQ4Disks/disk2.iso
MSCDEX: Failure: Invalid file or unable to open.

Stymied by DOSBox's lack of cooporation, I tried DOSBox-X, a derivation of DOSBox, which is very similar to its parent project, but contains extra features and support for more than just DOS games. I then tried IMGMOUNT to mount multiple floppy disk images, and it worked! With the floppy disks now mounted under the a: drive, I switched over to that drive and ran INSTALLH.BAT to begin the installation process. When prompted, I used the Swap menu in DOSBox-X to change out the virtual floppy disks. If this process had worked properly under DOSBox, I would have used the keyboard shortcut CTRL+F4 to switch between the various disks.

With King's Quest IV now installed, I could use tools like DOSBox or ScummVM to launch and play the game.

Cave Painting in King's Quest IV

21st February 2021 | Games


I was using AGI Studio to inspect the background images in the AGI version of King's Quest IV and came across something interesting with one of the priority images, which defines the walkable areas and provides the sense of depth in a scene. Picture 57 displays the inside of the three witches' cave. However, the priority screen for picture 57 reveals a hidden cave drawing of a person waving underneath the words "John was Here". So who was John? A quick look at the intro credits gives us a good clue as one of the programmers with this version of KQ4 was John Hamilton.

Programmers are often known for their love of slipping in Easter eggs into games, so it would not be far fetched if this bit of crude artistic work was a well hidden gem by one of the game's programmers. According to The Sierra Chest, the AGI version of King's Quest IV was the only Sierra title John Hamilton worked on. The MobyGames site has a little more info about him and mentions that KQ4 was his first title and then went to work on a number of other games up through the early 2000s. So, Mr. Hamilton, if you are around, hello! We found your cave sketchings (some 30+ years later)!

Agifier: Technical Details

18th January 2021 | Programming

The initial post about Agifier announced the capabilities of this Acorn plug-in, but the focus of this post will be about the more in-depth details on the research and construction of the software.

The initial considerations to convert an image to have the appearance of a 1980s computer game were resizing and coloring. Resizing seemed simple enough (in principle, at least), but the details would prove to be a little more complex than just altering the size of the image. Modifying the colors proved to be a very deep rabbit hole.

Resizing

The AGI Sierra On-Line games from the 80s had a screen resolution of 160 x 200 (width x height), but due to the nature of the double-wide pixels, it stretched out to 320 x 200. To emulate this appearance, the original image needs to have its width resized down to 320 pixels (and maintain the aspect ratio of the image so the height is also modified accordingly) and then resized again to 160 pixels in width, but retain the new height, and finally stretch the width back out to 320 pixels. The concept is straightforward, but there are numerous resizing algorithms available. The Acorn app has three available resizing options: Lanczos, Simple Affine, and Nearest Neighbor. The following images display the difference of each of these resizing algorithms on the same photo of Moraine Lake.

Lanczos (Slightly blurry)


Simple Affine (Clearest)


Nearest Neighbor (Most pixelated)


Since I am going for the pixelated look, I want to use the Nearest Neighbor approach. The Acorn plug-in makes heavy use of CIImage and CIFilters, and from what I experimented with (and used to some degree), there is not a CIFilter to resize an image using the nearest neighbor algorithm, whereas there are the CIFilters CILanczosScaleTransform and CIAffineTransform for the other resizing algorithms. To work around this and ensure there was the proper pixelated appearance with hard edges (versus the blurrier edges produced by the other algorithms), I constructed a new NSBitmapImageRep and then created a CIImage object from the bitmap.

// Draw out the updated colors to the resizedBitmap to create the pixelated double-wide pixels look
// This bitmap is twice the width of resizedImage
NSBitmapImageRep *resizedBitmap = [[NSBitmapImageRep alloc]
          initWithBitmapDataPlanes:NULL
                        pixelsWide:width*2
                        pixelsHigh:height
                     bitsPerSample:8
                   samplesPerPixel:4
                          hasAlpha:YES
                          isPlanar:NO
                    colorSpaceName:NSCalibratedRGBColorSpace
                       bytesPerRow:0
                      bitsPerPixel:0];
resizedBitmap.size = NSMakeSize(width*2, height);
	
// Use GCD to help parallelize this, otherwise this is noticeably slooooow
dispatch_apply(width, dispatch_get_global_queue(0, 0), ^(size_t x) {
	for (size_t y = 0; y < height; y++) {
		// Map each of the possible 64 colors to a 16 color EGA palette.
		NSColor *originalPixelColor = [bitmap colorAtX:x y:y];
		NSColor *newPixelColor = [self closestEGAColor: originalPixelColor];
		
		// Draw out the double-wide pixel to resizedBitmap
		[resizedBitmap setColor: newPixelColor atX: (2*x) y: y];
		[resizedBitmap setColor: newPixelColor atX: (2*x)+1 y: y];
	}
});

CIImage *outputImage = [[CIImage alloc] initWithBitmapImageRep: resizedBitmap];

The closestEGAColor method will be discussed in the next section on how to determine an approximate EGA color.

Coloring

The visual capabilities of computer monitors advanced quickly from 1980 - 1995, advancing from monochrome displays to millions of colors. The time period that the Agifier script is trying to emulate is the later half of the 1980s where the 16 color EGA palette was the standard. Trying to down sample colors to match up to EGA colors proves to be tricky. My first attempt involved using a standard algebraic formula to try and find the EGA color which was closest to a given pixel's color.


d = ( R 2 R 1 ) 2 + ( G 2 G 1 ) 2 + ( B 2 B 1 ) 2

The algorithm loops through all 16 EGA colors and does the calculation to determine which color is the closest. If the distance is equal to 0, that represents an exact color match.


// The original code that performed a mathematical calculation of the "closest" EGA color, 	
int indexOfClosestColor = 0;
double shortestDistance = 255 * sqrt(3.0);

// Loop through all 16 possible EGA colors
// Perform the calculation of how "far" pixelColor is from an EGA color
// If the distance is 0, then it is a perfect match.  Stop looping.
// Otherwise, keep looping and just keep track of the color with the "shortest" distance.
CGFloat r2 = [pixelColor redComponent];
CGFloat g2 = [pixelColor greenComponent];
CGFloat b2 = [pixelColor blueComponent];

for (int i = 0; i < 16; i++)
{
	NSColor *currentColor = colorPalette[i];
	
	CGFloat r1 = [currentColor redComponent];
	CGFloat g1 = [currentColor greenComponent];
	CGFloat b1 = [currentColor blueComponent];
	
	// Good old algebra used to calculate the distance between the color components in 3D space
	CGFloat distance = sqrt(pow((r2 - r1), 2) + pow((g2 - g1), 2) + pow((b2 - b1), 2));
	
	if (distance == 0.0)
	{
		shortestDistance = distance;
		indexOfClosestColor = i;
		break;
	}
	else if (i == 0)
	{
		shortestDistance = distance;
		indexOfClosestColor = i;
	}
	else
	{
		// What if distance == shortestDistance?
		if (distance < shortestDistance)
		{
			shortestDistance = distance;
			indexOfClosestColor = i;
		}
	}
}

Closest Color

This did not always produce the most optimal results. Compare this photo to the ones above. It has a muted tone, and many of the greens and browns are washed out and become a shade of grey. Considering that green is one of the primary color components in the RGB space, green should be far more prominent.

For my next approach, I rounded each RGB component to it's closest 1/3 value, since each of the EGA component values are stepped by a third (hex values of 0x00, 0x55, 0xAA, and 0xFF or 0.0, 0.3333, 0.6666, 1.0 in decimal). The bestColorComponentValue method displays the process of rounding a color component (red, green, or blue), so each component can have one of four different values. This results in a palette of 64 different colors (4 x 4 x 4 = 64), which is the full EGA color palette. The traditional 16 color palette favored by 80s Sierra games is only a subset of the entire array of EGA colors.

// Reduce each color component to the closest EGA-style equivalent value.
// In hex, each component can only be 0x00, 0x55, 0xAA, or 0xFF (0, 85, 170, 255)
// 0x00 = 0 = 0
// 0x55 = 85 = 0.333333
// 0xAA = 170 = 0.6666667
// 0xFF = 255 = 1.0
- (CGFloat) bestColorComponentValue: (CGFloat) colorComponent
{
	if (colorComponent < 0.166666)
	{
		return 0.0; // 0x00
	}
	else if (colorComponent < 0.5)
	{
		return 1.0/3.0; // 0x55
	}
	else if (colorComponent < 0.833333)
	{
		return 2.0/3.0; // 0xAA
	}
	else
	{
		return 1.0; // 0xFF
	}
}

64 EGA Colors

This produced closer results. What I didn't realize at the time was I had generated the full 64 EGA color palette by using this method. From here I then tried to calculate the equivalent 16 color variant using the mathematical equation, but the colors were still not quite right.

64 Down Sampled to 16 EGA Colors

I also tried modifying the saturation levels, which helped at times by strengthening the intensity of the colors. Working with the EGA palette can be an interesting challenge since it is limited, and its set of colors are strong, which excludes the many shades we see in the real world. Considering that each picture can be different, changing settings such as the saturation will work for some but not for others.

The results of the initial 1.0 version of the Agifier script are available in this tweet thread, which includes examples others tried and recommendations of other techniques, such as posterization and quantization. (Side note: For even more in-depth color techniques, check out Floyd-Steinberg dithering.)

When I returned to this project several months later, I did some research and came across a post which clued me in how close I had come. This post mentioned steps very similar to those I had taken. I was very close and just needed to take one more step.

From the 64 color palette, I then needed to manually "translate" each of the 64 colors down to 16 colors. Some colors were simple, but others were far more difficult. Some colors were directly in between two other colors, so I had to experiment with what worked best. I used a script which used the closest color equation to make a best guess, and I would then determine whether or not I agreed with that decision. After running a couple of landscape photos through the Agifier script, I tweaked it to help bring out the greens so they didn't get muted and turned to either grey or brown. The math was useful at times when I was indecisive about which color to choose, but for most of the colors, I manually determined the best match.

Below are two different representations of the 64 color EGA palette. The same set of colors, just grouped differently to display how the colors compare and contrast. In the first example, the darker colors are in the top left, greens in the bottom left, reds in the top right, and lighter colors in the bottom right. In the second example, the reds and greens are more predominant on the left side, but more paisley colors are on the right. Same set of colors, represented two different ways.

64-Color EGA Palette: Example 1
#000000   #550000   #AA0000   #FF0000  
#000055   #550055   #AA0055   #FF0055  
#0000AA   #5500AA   #AA00AA   #FF00AA  
#0000FF   #5500FF   #AA00FF   #FF00FF  
#005500   #555500   #AA5500   #FF5500  
#005555   #555555   #AA5555   #FF5555  
#0055AA   #5555AA   #AA55AA   #FF55AA  
#0055FF   #5555FF   #AA55FF   #FF55FF  
#00AA00   #55AA00   #AAAA00   #FFAA00  
#00AA55   #55AA55   #AAAA55   #FFAA55  
#00AAAA   #55AAAA   #AAAAAA   #FFAAAA  
#00AAFF   #55AAFF   #AAAAFF   #FFAAFF  
#00FF00   #55FF00   #AAFF00   #FFFF00  
#00FF55   #55FF55   #AAFF55   #FFFF55  
#00FFAA   #55FFAA   #AAFFAA   #FFFFAA  
#00FFFF   #55FFFF   #AAFFFF   #FFFFFF  

64-Color EGA Palette: Example 2
#000000   #000055   #0000AA   #0000FF  
#005500   #005555   #0055AA   #0055FF  
#00AA00   #00AA55   #00AAAA   #00AAFF  
#00FF00   #00FF55   #00FFAA   #00FFFF  
#550000   #550055   #5500AA   #5500FF  
#555500   #555555   #5555AA   #5555FF  
#55AA00   #55AA55   #55AAAA   #55AAFF  
#55FF00   #55FF55   #55FFAA   #55FFFF  
#AA0000   #AA0055   #AA00AA   #AA00FF  
#AA5500   #AA5555   #AA55AA   #AA55FF  
#AAAA00   #AAAA55   #AAAAAA   #AAAAFF  
#AAFF00   #AAFF55   #AAFFAA   #AAFFFF  
#FF0000   #FF0055   #FF00AA   #FF00FF  
#FF5500   #FF5555   #FF55AA   #FF55FF  
#FFAA00   #FFAA55   #FFAAAA   #FFAAFF  
#FFFF00   #FFFF55   #FFFFAA   #FFFFFF  

EGA Look-up Table

Based upon the 64-color EGA tables, I matched each color up with an appropriate color from the 16-color EGA table. The colors which are equivalent are denoted in bold. Some of the colors required some experimentation to see what would produce more life like images and not wash out the colors in the process. For some of the colors which were directly between two 16 palette EGA colors (e.g. #000055, #5555AA, or #FFFFAA), I tended to side with the color rather than a highlight (black, greys, white).

64-Color EGA Palette
#000000   0
#000055   1
#0000AA   1
#0000FF   1
#005500   2
#005555   8
#0055AA   1
#0055FF   9
#00AA00   2
#00AA55   2
#00AAAA   3
#00AAFF   3
#00FF00   2
#00FF55   10
#00FFAA   3
#00FFFF   11
#550000   4
#550055   8
#5500AA   1
#5500FF   9
#555500   8
#555555   8
#5555AA   9
#5555FF   9
#55AA00   2
#55AA55   2
#55AAAA   3
#55AAFF   11
#55FF00   10
#55FF55   10
#55FFAA   10
#55FFFF   11
#AA0000   4
#AA0055   4
#AA00AA   5
#AA00FF   5
#AA5500   6
#AA5555   6
#AA55AA   5
#AA55FF   9
#AAAA00   2
#AAAA55   7
#AAAAAA   7
#AAAAFF   7
#AAFF00   10
#AAFF55   10
#AAFFAA   7
#AAFFFF   11
#FF0000   4
#FF0055   12
#FF00AA   5
#FF00FF   13
#FF5500   12
#FF5555   12
#FF55AA   12
#FF55FF   13
#FFAA00   6
#FFAA55   14
#FFAAAA   7
#FFAAFF   13
#FFFF00   14
#FFFF55   14
#FFFFAA   14
#FFFFFF   15
16-Color EGA Palette
0 #000000  
1 #0000AA  
2 #00AA00  
3 #00AAAA  
4 #AA0000  
5 #AA00AA  
6 #AA5500  
7 #AAAAAA  
8 #555555  
9 #5555FF  
10 #55FF55  
11 #55FFFF  
12 #FF5555  
13 #FF55FF  
14 #FFFF55  
15 #FFFFFF  

By using a look-up table instead of doing a mathematical calculation, this speeds up the process in determining new colors in the plug-in.

// Get the normalized color where each RGB component is set to either 0x00, 0x55, 0xAA, or 0xFF
NSColor *updatedPixelColor = [self closerEGAColor:pixelColor];
NSString *updatedPixelHexValue = [self convertNSColorToHex:updatedPixelColor];
// Find the closest matching color in the 16-color palette
NSNumber *colorPaletteIndex = colorMatch[updatedPixelHexValue]; 

return colorPalette[[colorPaletteIndex intValue]];

Conclusion

As seen in this final example, the colors are far more vibrant than in the Closest Color example where nearly all colors beside the blues became dull shades of grey. The level of research which could go into trying to sample truer colors could get incredibly deep and complex, but for now this is a decent representation to display what an AGI-era Sierra game could have looked like, even with only 16 colors and 32,000 pixels (160x200 screen dimension).

To summarize the steps the Agifier plug-in (version 1.2) uses to create low-res image in the style of a 1980s Sierra computer game:

References

Older posts »