Edenwaith Blog

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 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. A workaround to some of these limitations is to use the FrogFind search engine, and it makes browsing the Web on an older machine a little more feasible. Not perfect, and still somewhat painful, but possible.

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.

[Update: 6 February 2023] Older machines are a snapshot of their time, but they can prove to be indespensibly useful when using some software that was built for that era. Occasionally, interesting nuggets are unearthed, such as the Carbon version of the Neko Project II PC-98 emulator, which seemed more complete than the more recent versions of that project. Using NP2 Carbon, I was able to play the Japanese version of Quest for Glory I. Alongside DOSBox, these emulators can play plenty of great, retro games (which is also how I play a lot of games on the Mac). While there are more modern apps which can emulate the PC-98, I found NP2 Carbon to be the easiest one to use.

[Update: 22 April 2023]

When in the process of making a screencast of the sound quality of the Japanese version of QFG1 in a PC-98 emulator, I originally tried using QuickTime X in macOS Ventura, but I noticed a major problem in that it was recording audio from the Mac's microphone, which meant it was also picking up external noises (such as typing on the keyboard). This led me down an interesting rabbit hole of trying to record video and audio on a modern Mac, which also involved using other utilities like Soundflower, Loopback, and QuickTime while jumping through additional security hoops to enable them. The easier solution turned out to use older software, such as Ambrosia Software's Snapz Pro X 2. Fortunately, I still had an old license that worked with Snapz Pro 2.2.2 on Mac OS X Leopard. This software worked beautifully to record both the video and audio and create a relatively small video file.

When I was researching how to transfer data between two SD cards for the Nintendo Switch, I came across a new piece of modern software which can run PowerPC, Intel, and Apple Silicon processor: SD Card Formatter. Considering the age of the PowerBook, one must generally rely on older software, so it is quite a treat to find modern software that is still designed to run on PowerPC Macs. This is a particularly interesting case since this is the only app I've seen (so far) which has been built for all three processors. I imagine this involves performing multiple builds and then stitching them together into a single Universal Binary. Well done, Tuxera.

Conclusion

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

AGI Studio for Mac 1.3.2

9th January 2021 | Programming

Two years ago I ported QT AGI Studio to the Mac, an application that can view, modify, and create Sierra On-Line AGI games from the 1980s. It was a great start and an indispensable tool for creating King's Quest 1 - Redux, but there were still several items on the TODO list to complete.

The previous TODO list:

Pretty much everything was checked off and more. The only item not completed was the sound player for Mac. Future features and improvements include building an Apple silicon (M1) version and upgrade to the Qt 5 libraries so this project can be built on more modern hardware and software (the current development machine is a 2007 MacBook Pro running Mac OS X 10.6.8).

I had been working on some improvements to AGI Studio earlier last year, but when I came across Chris Cromer's 1.3.2 branch of the original project, I integrated in his improvements which fixed a couple of issues with the drawing tools. I also added a number of Mac-specific improvements to the app (keyboard shortcuts, fixed UI alignment issues, code signing and notarization).

New Features

Glob (Why V3 Games Weren't Opening)

The biggest fault of the previous version of AGI Studio for Mac is that it could not open up Version 3 AGI games, which include the both Manhunter games, Gold Rush!, and the AGI version of King's Quest IV. (The SCI version of KQ4 is more well known, but an AGI version was also developed at the same time to be able to run on less powerful computers of the time.) I parsed through AGI Studio's code to determine the source of the problem with opening V3 games. When working through a problem, often the best way is to isolate the issue, so I wrote the following small C program to determine why AGI Studio was not able to open up V3 AGI games. The issue centers around the glob function which generates path names matching a pattern. Below is a stand-alone C program which can be compiled and run in the same directory as an AGI game to determine the game's version number.

The issue was that when glob is run on the Mac, it is case sensitive with the name of the path or file being passed in as an argument. Other operating systems may be case insensitive, so the capitalization of a file name would not matter. The original code for Linux and Mac looked like this:

sprintf(tmp, "%s/*vol.0", name);

if (glob (tmp, GLOB_ERR | GLOB_NOSORT, NULL, &globbuf)) {
	...
}

The problem with glob on the Mac is due to its case sensitivity. In the original code, file names like dir and vol were in all lowercase, yet the file names of these Sierra games are generally in all caps (e.g. KQ4VOL.0, MH2DIR). When glob tried to find a pattern that matched vol.0, it couldn't find any files. By changing terms like dir or vol to DIR and VOL in the code resolved the problem. A simple solution for a wily problem.

Build Script and Code Signing

While QT AGI Studio does create a Mac app bundle, it is missing numerous steps. The macdeployqt utility helps a bit by adding the missing QT frameworks to the app bundle, but the older version (4.8) I have been using is incomplete in how it sets things up and requires a number of additional steps to get the frameworks configured properly. As I mentioned in my previous post about QT AGI Studio, the install_name_tool utility needs to be called against the Qt3Support, QtGui, and QtCore frameworks to properly set up their paths. After I had completed these steps, I then code signed and notarized the app, but encountered the first of numerous errors.

In the initial port of AGI Studio, I did not bother with code signing the app bundle, because code signing almost always comes with unwanted headaches. I was not wrong in this assumption, as trying to code sign and then notarize AGI Studio resulted in numerous issues. I first tried to use SD Notary to sign the release build of AGI Studio.app 1.3.2, but it threw the following error:

20:40:21.170: Creating working copy...
20:40:21.252: Clearing extended attributes...
20:40:21.841: Examining contents...
20:41:10.605: Signing '.../Contents/Frameworks/QtSql.framework/Versions/4/QtSql'...
20:41:12.082: Signing '.../Contents/Frameworks/QtSql.framework/Versions/4'...
20:41:12.897: Error 105553131805344 signing 'qt-agistudio/agistudio-build-Desktop-Release/AGI Studio - Working/AGI Studio.app/Contents/Frameworks/QtSql.framework/Versions/4': qt-agistudio/agistudio-build-Desktop-Release/AGI Studio - Working/AGI Studio.app/Contents/Frameworks/QtSql.framework/Versions/4: bundle format unrecognized, invalid, or unsuitable

After some research, I learned this issue was because the QT frameworks generated by macdeployqt did not include the Info.plist file. I also discovered that another failure point is that the Info.plist files were missing the CFBundleVersion and CFBundleIdentifier key-value pairs, which are necessary to properly code sign the bundled QT frameworks. I manually copied over the plist file from the original framework and then added the missing key-value pairs using the built-in developer utility PlistBuddy.

cp /Library/Frameworks/Qt3Support.framework/Contents/Info.plist ./agistudio.app/Contents/Frameworks/Qt3Support.framework/Resources/
/usr/libexec/PlistBuddy -c "Add :CFBundleVersion string 4.8" ./agistudio.app/Contents/Frameworks/Qt3Support.framework/Resources/Info.plist
/usr/libexec/PlistBuddy -c "Add :CFBundleIdentifier string org.qt-project.Qt3Support" ./agistudio.app/Contents/Frameworks/Qt3Support.framework/Resources/Info.plist

With the updated Info.plist put in place, I tried code signing again, but this time from the command line.

codesign --deep --force  -v --sign "Developer ID Application: John Doe (12AB34567D)" --options runtime AGI\ Studio.app
AGI Studio.app: resource fork, Finder information, or similar detritus not allowed

I've seen this "detritus" error before when I've ported games to the Mac, so I knew what was needed to clean up the app by using the xattr command.

% xattr -lr AGI\ Studio.app
AGI Studio.app/Contents/Resources/template/.DS_Store: com.apple.FinderInfo:
00000000  00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00  |........@.......|
00000010  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  |................|
00000020
AGI Studio.app/Contents/Resources/template/template/.DS_Store: com.apple.FinderInfo:
00000000  00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00  |........@.......|
00000010  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  |................|
00000020
AGI Studio.app/Contents/Resources/application.icns: NSStoreType: IconsFileStore
AGI Studio.app/Contents/Resources/application.icns: NSStoreUUID: 92D51D68-6006-43DC-ADF5-CD20294177D9
AGI Studio.app/Contents/Resources/application.icns: com.apple.FinderInfo:
00000000  00 00 00 00 69 63 6E 43 00 00 00 00 00 00 00 00  |....icnC........|
00000010  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  |................|
00000020
AGI Studio.app/Contents/Info.plist: com.apple.FinderInfo:
00000000  54 45 58 54 00 00 00 00 00 00 00 00 00 00 00 00  |TEXT............|
00000010  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  |................|
00000020
AGI Studio.app/Contents/Info.plist: com.apple.TextEncoding: UTF-8;134217984
% xattr -cr AGI\ Studio.app
% xattr -lr AGI\ Studio.app

I then did another code sign and verified it with spctl, but this resulted in another error:

spctl --verbose=4 --assess --type execute AGI\ Studio.app
AGI Studio.app: rejected (embedded framework contains modified or invalid version)

As I mentioned earlier, code signing can be quite the frustrating and harrowing experience when things don't go smoothly. After yet more research I discovered that this latest error was due to the QT frameworks not being structured properly, so I was going to need to perform additional maintenance on the frameworks. In addition to adding the Info.plist file, I needed to move the Resources folder and then create symlinks for the framework's executable (e.g. QtCore) and for Resources and Current. The bundled QtCore framework created by macdeployqt has the following directory structure:


QtCore.framework/
    Resources/
    Versions/
        4/
            QtCore

However, it should be structured like the following to match Apple's recommendations on how to construct a framework:


QtCore.framework/
    QtCore -> Versions/Current/QtCore
    Resources -> Versions/Current/Resources
    Versions/
        Current -> 4
        4/
            QtCore
            Resources/
                Info.plist

A lot of this stems from the usage of older Qt utilities, and when code signing was updated with OS X 10.9.5 Mavericks, it required the frameworks to be properly structured, and the old style did not work. To fix this issue, I created the setup_framework function in the build_app_bundle.sh script to help automate the restructuring of the six QT frameworks.

Now with the frameworks properly set up, the script then copies over the help files, the template files to create a new AGI game, and the primary Info.plist for the main app bundle. The final step is to rename the app bundle from agistudio.app (the name that AGI Studio names the app bundle) to AGI Studio.app.

The final script to build the Mac app bundle for AGI Studio.

By this point, I thought I was in the clear. I was able to successfully code sign and notarize the app without any errors. I downloaded the app to another computer to verify that it can open and properly pass Gatekeeper's checks. The good news is that it did get past Gatekeeper. The bad news is that it crashed immediately:

Process:               agistudio [12959]
Path:                  /Applications/AGI Studio 1.3.2.app/Contents/MacOS/agistudio
Identifier:            com.edenwaith.agistudio
Version:               1.3.2 (1.3.2)
Code Type:             X86-64 (Native)
Parent Process:        ??? [1]
Responsible:           agistudio [12959]
User ID:               501

Date/Time:             2021-01-01 20:27:40.525 -0700
OS Version:            Mac OS X 10.14.6 (18G7016)
Report Version:        12
Anonymous UUID:        BFCBBC98-7790-AD41-180E-4AF385B05848

Sleep/Wake UUID:       81F20208-DCC6-45DF-BA39-286064CC2BCF

Time Awake Since Boot: 210000 seconds
Time Since Wake:       6500 seconds

System Integrity Protection: enabled

Crashed Thread:        0

Exception Type:        EXC_CRASH (SIGABRT)
Exception Codes:       0x0000000000000000, 0x0000000000000000
Exception Note:        EXC_CORPSE_NOTIFY

Termination Reason:    DYLD, [0x1] Library missing

Application Specific Information:
dyld: launch, loading dependent libraries

Dyld Error Message:
  Library not loaded: @executable_path/../Frameworks/Qt3Support.framework/Versions/4/Qt3Support
  Referenced from: /Applications/AGI Studio 1.3.2.app/Contents/MacOS/agistudio
  Reason: no suitable image found.  Did find:
	/Applications/AGI Studio 1.3.2.app/Contents/MacOS/../Frameworks/Qt3Support.framework/Versions/4/Qt3Support: code signing blocked mmap() of '/Applications/AGI Studio 1.3.2.app/Contents/MacOS/../Frameworks/Qt3Support.framework/Versions/4/Qt3Support'
	/Applications/AGI Studio 1.3.2.app/Contents/MacOS/../Frameworks/Qt3Support.framework/Versions/4/Qt3Support: stat() failed with errno=1
	/Applications/AGI Studio 1.3.2.app/Contents/MacOS/../Frameworks/Qt3Support.framework/Versions/4/Qt3Support: code signing blocked mmap() of '/Applications/AGI Studio 1.3.2.app/Contents/MacOS/../Frameworks/Qt3Support.framework/Versions/4/Qt3Support'
	/Applications/AGI Studio 1.3.2.app/Contents/MacOS/../Frameworks/Qt3Support.framework/Versions/4/Qt3Support: stat() failed with errno=1

Binary Images:
       0x100000000 -        0x10010cfe7 +com.edenwaith.agistudio (1.3.2 - 1.3.2)  /Applications/AGI Studio 1.3.2.app/Contents/MacOS/agistudio
       0x10e267000 -        0x10e2d170f  dyld (655.1.1) <95E169F0-A47B-3876-BA72-9B57AF091984> /usr/lib/dyld

Great. Now what? Yes, time for yet more research. It looks like the hardened runtime that is necessary for notarizing an app had issues with the QT frameworks yet again. To disable the library validation, I needed to create a small Entitlements.plist file and add the com.apple.security.cs.disable-library-validation value.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>com.apple.security.cs.disable-library-validation</key>
	<true/>
</dict>
</plist>

The final (and working) version to properly code sign AGI Studio for Mac:

codesign --deep --force -v --sign "Developer ID Application: John Doe (12AB34567D)" --options runtime --timestamp --entitlements ./Entitlements.plist AGI\ Studio.app

Apple is always keeping code signing a "fun and exciting" process. This problems in trying to properly set up and deploy this app were compounded by using an older version of the QT tools and frameworks, mostly QT 3 and 4 libraries, whereas the current version of QT is version 6 which was released just a month ago. For any future development, I plan on updating the project to use at least QT 5, and hopefully this will also allow the option to make a universal binary version that can run natively on both Intel and Apple Silicon Macs.

References

Edenwaith 2021

7th January 2021 | Edenwaith

We are a mere week into 2021, and it has been confirmed that the world is still delightfully mad. Despite the past year being nuttier than Mr. Peanut, some interesting projects did come about this past year. I continued my exploration with games, primarily porting games developed with Adventure Game Studio to the Mac, but also wrote about Gabriel Knight 3, Curse of Monkey Island, and Quest for Glory III. I also wrote a plug-in for the Acorn image editor to transform a photo to look like an 8-bit image. A fourth part of the Reverse Engineering Sierra's Adventure Game Interpreter series was also written to show how to have a little fun with hacking Sierra games (and mocking "poisonous" snakes).

Just as the calendar flipped over, I released an update to the Mac version of AGI Studio, the utility I used to King's Quest 1 - Redux.

Software updates:

Development for Permanent Eraser 3.0 has officially started and will have a new focus and be geared for more modern Macs.

I have been mulling over the idea of retiring 33 RPM for a number of years, especially since it no longer works on macOS Catalina and Big Sur. At this time I am sending it off to a well earned retirement since there has been no real movement towards trying to rewrite the entire app (so far).

One project I mentioned I was working on last year was writing a novel. I was successful in completing the first draft, and I am currently in the process of editing the second draft. During this year's NaNoWriMo, I might visit the intersection of writing and technology by investigating TWINE. I might even try resurrecting my old text-based game Psychomancer this way.

A couple of game-related projects are planned for this year, including porting a couple more AGS games to the Mac.

Agifier: EGA De-make Plug-in for Acorn

28th December 2020 | Programming

The original King's Quest provided a dramatic jump in graphical abilities for 1984: a simulated 3D environment (in 16 glorious colors) where the player's character could move in front, behind, and around objects on the screen (3D environments love prepositions). For a time when graphics were generally limited to static images displayed in either monochrome or perhaps 4 CGA colors, this was quite revolutionary. What was cutting-edge for the time comes across as incredibly simplistic and bland today, yet it delivers its own particular brand of charm (deeply fueled by nostalgia), but with its limited palette of 16 bright colors, it offers an interesting set of constraints to generate digital art.

My work on King's Quest I - Redux focused mostly functional improvements, but I did make some small graphical improvements in the game (updating inventory objects, filling in inconsistent shading, removing invasive palm trees and sickly looking flora, etc.). Despite the limitations of the AGI graphics capabilities, the AGI version of King's Quest IV and Gold Rush! displayed what the engine could perform.

In the third part of my series which inspected reverse engineering Sierra's AGI game engine, I focused primarily on how colors are represented in these games. Trying to downscale a realistic photo to look like 80s computer graphics provides for some interesting challenges — some fairly trivial and others far more in depth.

To further explore the possibilities of creating semi-realistic artwork from actual photos, I created a plug-in for the Acorn image editor. This plug-in attempts to generate 80s-style graphics from real images, which could potentially be added to an AGI game. Based upon the nature and inspiration of the script, it has been named Agifier.

Screenshots

Below are examples of the Agifier script being run on a photo of Moraine Lake in Banff National Park. For some images, the plug-in does a fairly decent job, but it is not perfect, especially where subtle colors are concerned. The manually retouched image is the same as the second photo, but with the sky coloring updated by hand. This script tries to best match real world colors to EGA colors, but there are areas where manual intervention is needed for corrections.

Original

Modified by Agifier Acorn Plug-In

Manually Retouched

Download + Installation

Conclusion

As of this writing, this is version 1.1 of the Agifier plug-in. The initial version was written back in July 2020, but it lacked the proper reduction of colors to the 16 color EGA palette. There are still several areas of improvement such as resizing using double-wide pixels (so each drawn pixel's dimensions are actually 2x1), additional color correction, and ensuring that resizing an image uses the nearest neighbor algorithm (not Lanczos or affine transform) to create a more pixel-y appearance. A follow up post will go into further detail about the technical aspects of this script.

References

« Newer posts Older posts »