Porting Adventure Game Studio Games to the Mac

18th April 2020 | Games

During the 1980s, there was a plethora of competing computer systems (Atari, Apple ][, DOS, Macintosh, Amiga, etc.), which was instrumental in encouraging Sierra On-Line to develop their AGI and SCI game engines to support many of these systems. Since those game engines were interpreters, it was the game engine which needed to be ported, but the resources and code could remain fairly consistent, which reduced the effort to bring the games to multiple platforms.

After things shuffled out and settled down to two or three platforms during the 1990s, most games came out for DOS/Windows. Only a handful of games were ported to the Mac, and even if they were, it was often years later. In an effort to come up with a more platform neutral solution, I developed for my Master's Thesis the Platform Independent Game Engine which was based off of cross-platform frameworks like C++, OpenGL, and OpenAL. This was more of a proof of concept than a full fledged tool for game development.

Several months ago, Steven Alexander (of Infamous Quests fame) directed me to a way that shows how games created with the Adventure Game Studio can be ported to the Mac with minimal effort. (Note: Minimal to the level that it doesn't involve having to rewrite 80% of the code to support another platform.) The process to take the resources from the Windows version of an AGS game and turn it into a Mac application only takes a couple of minutes and does not require fragile third party frameworks like Wine. However, the process does not end there after copying a couple of files.

This article will detail several areas to help add the extra polish to make your game port feel like a proper Mac app by designing an appropriate app icon, configuring the Info.plist, and finally code signing and notarizing the app for security. I'll demonstrate porting the hypothetical game Knight's Quest (where you play the intrepid adventure Sir Club Cracker and roam the countryside picking up anything which hasn't been nailed down) and how to add the extra polish to make it into a "proper" Mac application.

Porting

The aforementioned link to the Adventure Game Studio Forum post details one method how to set up AGS to create a Mac version of a game. Fortunately, there is already a gameless AGS shell application which can be modified for your game to work on the Mac without having to go through a number of convoluted steps to retrofit the Adventure Game Studio to work on the Mac to develop an application. Download the pre-compiled shell Mac application which is built for version 3.4.4 of AGS. This is an empty app, but we will soon populate it with the required game assets. Mac apps are bundles, essentially a folder with a collection of additional folders and files contained within. To port an existing Windows AGS game, we will need to move a couple of the Windows assets into the appropriate locations in the Mac app.

Right-click on the file AGS.app and select Show Package Contents from the context menu. This will reveal the barebones contents of the app. Inside the Contents folder is an Info.plist, a MacOS folder (which contains the AGS executable), PkgInfo, and an empty Resources folder.

Take the executable file (.exe) of your Windows game (not the winsetup.exe file that comes with some AGS games) and rename it to ac2game.dat. If you run the file command against the ac2game.dat file, you will see it is still a Windows executable file.

$ file ac2game.dat
ac2game.dat: PE32 executable (GUI) Intel 80386, for MS Windows

Next, copy the ac2game.dat, acsetup.cfg, audio.vox, music.vox, speech.vox, and any other support files (such as files ending in .dll, .tra, .000, .001, etc.) to the Resources folder. Your project may not have all of these files, but most projects will contain at least ac2game.dat, acsetup.cfg, and audio.vox. Once this is done, the package folder structure should look similar to the following screenshot.

Next rename the app bundle from AGS to the name of your game.

...and now the port is done! Really. That wasn't so difficult, was it? Except, it's not quite a polished Mac app, yet. To have the proper look and feel of a proper Mac app, we still need to add that extra shine.

App Icon

One of the first things you'll notice about an app is its icon. Like so many things in life, it's important to make a good first impression. The same applies here. Right now, the app has a generic looking icon. For so many years, icons were limited to small, pixelated blobs, but modern Mac icons come in a wide range of resolutions from a tiny 16x16 to a glorious 1024x1024.

If you have an older Mac on hand, use Icon Composer (one of the many utilities which comes along with Xcode), otherwise, use an app like Icon Slate to create a Mac icon file (icns).

If you are starting with this barebones AGS app, the next section will not apply, but this situation did come up with one app I helped port to the Mac where the app's icon was set using an old fashioned resource fork, a throwback of the Classic Mac OS days. If you need to check if there is a icon resource fork present, go to the Terminal and cd into the app bundle, then list the files contained. If you see a file that says Icon?, then there is a resource fork hidden within to represent the app's icon.

% cd SomeOtherGame.app
$ ls -la
total 2568
drwxr-xr-x@  4 chadarmstrong  staff  128 Jan  1 14:15 .
drwxr-xr-x  11 chadarmstrong  staff  352 Jan  1 20:33 ..
drwxr-xr-x@  6 chadarmstrong  staff  192 Jan  1 14:15 Contents
-rw-r--r--@  1 chadarmstrong  staff    0 Nov 26 08:26 Icon?

To remove the resource fork, either delete Icon? from the command line, or if you prefer a more visual method, right-click on the app and select the Get Info menu. In the Get Info window, click on the app icon in the top left of the window, then hit the Delete key on your keyboard.

Other resource forks might still be lurking within the app, but we will take care of those later.

Place the app icon file into the Resources folder of the app bundle. Next, we will modify the Info.plist to add the app icon and other important details.

Info.plist

The skeleton AGS app contains an Info.plist, which can be opened up in any text editor or Xcode. A couple of additions, modifications, and a deletion are necessary to properly customize for your game.

Following is a subset of the Info.plist file with the necessary changes.


<?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>CFBundleDisplayName</key>
	<string>Knight's Quest</string>
	<key>CFBundleName</key>
	<string>Knight's Quest</string>
	<key>CFBundleIdentifier</key>
	<string>com.edenwaith.kq</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0</string>
	<key>CFBundleVersion</key>
	<string>1</string>
	<key>LSApplicationCategoryType</key>
	<string>public.app-category.adventure-games</string>
	<key>NSHumanReadableCopyright</key>
	<string>Copyright © 2020 Edenwaith. All Rights Reserved</string>
	<key>CFBundleIconFile</key>
	<string>KQ-Icon</string>
	.
	.
	.
</dict>
</plist>

Now when you launch the app, the new app icon will appear in the Dock.

Code Signing

Code signing is one of those things which has given me several new grey hairs over the years. If code signing wasn't enough, Apple has now added yet another layer of security with app notarization. Notarizing an app does require several additional steps, but it has fortunately proven to have not been too harrowing of an experience to figure out and implement. If you are going to make an app for macOS Mojave, Catalina, or later, it is a good idea to both code sign and notarize the app so macOS will not pester the player with annoying warnings about the validity and security of the app.

I have written about about code signing before, so this is not a brand new topic, but with the introduction of notarization, there is some new information to share.

With the introduction of Xcode 11, there are new types of development and distribution certificates (e.g. Apple Development, Apple Distribution), but for this example, I use the older style Developer ID Application certificate for signing the app for distribution.

Let's start by verifying that the app is not code signed:

$ codesign --verify --verbose=4 Knight\'s\ Quest.app
Knight's Quest.app: code object is not signed at all
In architecture: x86_64

$ spctl --verbose=4 --assess --type execute Knight\'s\ Quest.app
Knight's Quest.app: rejected
source=no usable signature

This looks as expected. The next step is to code sign the app bundle. In the past, we would use a command like the following:

codesign --force --sign "Developer ID Application: John Doe (12AB34567D)" MyGreatApp.app/

However, with the introduction of notarization, there is a new set of options to add to further harden the code signing process: --options runtime

codesign --force -v --sign "Developer ID Application: John Doe (12AB34567D)" --options runtime Knight\'s\ Quest.app

If everything works as hoped, you will see a successful message like this:

Knight's Quest.app/: signed app bundle with Mach-O thin (x86_64) [com.edenwaith.kq]

If the code signing was successful, skip to the next section about Notarization.

Unfortunately, it is far too easy for things to go wrong. With some apps I've helped port, there would be resource forks or other Finder info hidden somewhere in the app, so I'd see an error message like this one:

SomeOtherGame.app: resource fork, Finder information, or similar detritus not allowed

This error is due to a security hardening change that was introduced with iOS 10, macOS Sierra, watchOS 3, and tvOS 10. According to Apple:

Code signing no longer allows any file in an app bundle to have an extended attribute containing a resource fork or Finder info.

The first time I saw this error, I determined that the "offending" file was the icon file, so I used the following command:

find . -type f -name '*.icns' -exec xattr -c {} \;

In one case, the problem was because I had added a Finder tag on the app bundle. Removing the tag fixed the problem. With another instance, some other file was causing issues, but I was not able to immediately discover which was the suspect file.

To ferret out the problem, I used the xattr command to see what extended attributes were available in the app bundle.

$ xattr -lr Knight\'s\ Quest.app/
Knight's Quest.app//Contents/_CodeSignature: com.apple.quarantine: 01c1;5e0d5c84;sharingd;E82F3462-E848-4A3A-846C-C497474C0E1C
Knight's Quest.app//Contents/MacOS/AGS: com.apple.quarantine: 01c1;5e0d5c84;sharingd;E82F3462-E848-4A3A-846C-C497474C0E1C
Knight's Quest.app//Contents/MacOS: com.apple.quarantine: 01c1;5e0d5c84;sharingd;E82F3462-E848-4A3A-846C-C497474C0E1C
Knight's Quest.app//Contents/Resources/audio.vox: com.apple.quarantine: 01c1;5e2e7a68;Firefox;7587AE66-F597-423C-8787-1DAE23ECA136
Knight's Quest.app//Contents/Resources/ac2game.dat: com.apple.quarantine: 01c1;5e2e7a68;Firefox;7587AE66-F597-423C-8787-1DAE23ECA136
Knight's Quest.app//Contents/Resources/acsetup.cfg: com.apple.quarantine: 01c1;5e2e7a68;Firefox;7587AE66-F597-423C-8787-1DAE23ECA136
Knight's Quest.app//Contents/Resources: com.apple.quarantine: 01c1;5e0d5c84;sharingd;E82F3462-E848-4A3A-846C-C497474C0E1C
Knight's Quest.app//Contents/Info.plist: com.apple.lastuseddate#PS:
00000000  CD 72 0D 5E 00 00 00 00 B4 1E 64 2C 00 00 00 00  |.r.^......d,....|
00000010
Knight's Quest.app//Contents/Info.plist: com.apple.quarantine: 0181;5e0d5c84;sharingd;E82F3462-E848-4A3A-846C-C497474C0E1C
Knight's Quest.app//Contents/PkgInfo: com.apple.quarantine: 0181;5e0d5c84;sharingd;E82F3462-E848-4A3A-846C-C497474C0E1C
Knight's Quest.app//Contents: com.apple.quarantine: 01c1;5e0d5c84;sharingd;E82F3462-E848-4A3A-846C-C497474C0E1C
Knight's Quest.app/: com.apple.quarantine: 01c1;5e0d5c84;sharingd;E82F3462-E848-4A3A-846C-C497474C0E1C

To clean up the extraneous resource forks (and other detritus), use the command:

xattr -cr Knight\'s\ Quest.app/

Once the cruft has been removed, verify with xattr -lr again and then try code signing the app once more. Perform one more set of verifications to ensure that things look good.

$ codesign --verify --verbose=4 Knight\'s\ Quest.app
Knight's Quest.app: valid on disk
Knight's Quest.app: satisfies its Designated Requirement

$ spctl --verbose=4 --assess --type execute Knight\'s\ Quest.app
Knight's Quest.app: accepted
source=Developer ID

Notarization

Now on to the new stuff! The first thing you'll need to do is generate an app-specific password for this app. Log in to Manage Your Apple ID page with your Apple developer credentials. Under the Security section, tap on the Generate Password... link. In the pop up, enter in a description for the app (e.g. Knight's Quest), and then an app-specific password will be generated. The app-specific password will be a sixteen character password that will look similar to this: wcag-omwd-xzxc-jcaw . Save this password in a secure place! You will need this password for notarizing the app.

The next step will involve packaging the app to be notarized. It can be packaged as either a zip archive (zip), disk image (dmg), or an installer package (pkg). Since this is just a single app bundle, a zip file will work.

// Zip up the app
ditto -c -k --sequesterRsrc --keepParent *app KQ.zip

For the next step, if your Apple ID is only part of a single developer account, you can skip ahead. However, if your Apple ID is associated with multiple accounts (such as multiple companies), then you will need to obtain more specific information before notarizing the app. Otherwise, if you try to notarize an app and your Apple ID belongs to multiple accounts, you will see this error:

Error:: altool[7230:1692365] *** Error: Your Apple ID account is attached to other iTunes providers. You will need to specify which provider you intend to submit content to by using the -itc_provider command. Please contact us if you have questions or need help. (1627)

To get the list of providers linked to your Apple ID, use this command:

xcrun iTMSTransporter -m provider -u john.doe@edenwaith.com -p wcag-omwd-xzxc-jcaw

This command will output a bunch of cruft, but ends with:

Provider listing:
   - Long Name -     - Short Name -
1  John Doe    		JohnDoe18675309
2  Acme, Co.  		AcmeCo

In this hypothetical example, the developer John Doe has his own personal Apple developer account and also belongs to Acme's developer account, as well. For this project, John will use his personal account, so he will use the short name of JohnDoe18675309, which will be used as the ASC provider value in the following command. Again, you can omit the asc-provider option for the notarization call if your credentials are associated with only a single team.

To notarize the app, you will use the application launcher tool (altool), which can also be used for other purposes (such as uploading the app to Apple's servers). The format of notarizing is as follows:

xcrun altool --notarize-app --primary-bundle-id com.example.appname --username APPLE_DEV_EMAIL --password APP_SPECIFIC_PASSWORD --asc-provider PROVIDER_SHORT_NAME  --file AppName.zip

For our example, we filled in the blanks with the following values:

xcrun altool --notarize-app --primary-bundle-id "com.edenwaith.kq" -u john.doe@edenwaith.com -p wcag-omwd-xzxc-jcaw --asc-provider JohnDoe18675309 --file KQ.zip

For a more in depth break out of what each of these options mean, refer to Davide Barranca's excellent post on this topic.

This process can take awhile, depending on how large your file is, since it needs to get uploaded to Apple's servers and be notarized. Unfortunately, this potentially long wait time comes with consequences if there is an error. One reason notarization might fail is if you need to accept Apple's terms of agreement (which may have also been updated), so you might see an error like:

/var/folders/pv/xtfd6hjn7hd8kpt70q0vwl8w0000gq/T/15A31C27-F6AF-48E8-9116-A30A7C76AD03/com.edenwaith.kq.itmsp - Error Messages:
		You must first sign the relevant contracts online. (1048)
2020-04-11 11:23:19.745 altool[22640:1119844] *** Error: You must first sign the relevant contracts online. (1048)

One approach to fix this is to log in to the Apple developer portal and check to see if new terms of service need to be reviewed. If so, agree to the terms, and wait a couple of minutes before trying to notarize again. You can also check Xcode's license agreement from the Terminal by typing in the command:

sudo xcodebuild -license

However, if everything works properly with uploading the file, you will get a message like this:

No errors uploading 'KQ.zip'.
RequestUUID = 2de2e2f9-242f-3c78-9937-1a7ef60f3007

You'll want that RequestUUID value so you can check on the notarization status to see if the package has been approved yet. After uploading your app, the notarization process typically takes anywhere from several minutes to an hour. When the process completes, you receive an email indicating the outcome. Additionally, you can use the following command to check the status of the notarization process:

$ xcrun altool --notarization-info 2de2e2f9-242f-3c78-9937-1a7ef60f3007 -u john.doe@edenwaith.com -p wcag-omwd-xzxc-jcaw
No errors getting notarization info.

          Date: 2020-01-04 22:44:42 +0000
          Hash: adf86725dec3ab7c26be17178e07efaf3b2806f743fefd0dd1059f68dcf45398
    LogFileURL: https://osxapps-ssl.itunes.apple.com/itunes-assets/Enigma123/v4/a5/15/64/2d...
   RequestUUID: 2de2e2f9-242f-3c78-9937-1a7ef60f3007
        Status: success
   Status Code: 0
Status Message: Package Approved

Once the package has been approved, we can move on to the stapling the ticket to the app.

Stapler

At this point, the app has been validated and notarized, so if an app launches, macOS will check with the servers and verify that the app has been properly notarized. However, if the computer is not online, then it cannot perform this step, so it is useful to staple the ticket to the app for offline usage.

xcrun stapler staple -v KQ.app

Once again, a ton of text will scroll across the Terminal. If it works, the last line should read: "The staple and validate action worked!"

For sanity's sake, validate that stapling worked:

stapler validate Knight\'s\ Quest.app
Process: Knight's Quest.app
The validate action worked!

However, if the stapling hasn't been completed, there will be an error like this:

$ stapler validate Knight\'s\ Quest.app/
Processing: Knight's Quest.app
Knight's Quest.app does not have a ticket stapled to it.

For a final verification, use spctl to verify that the app has been properly signed and notarized:

$ spctl -a -v Knight\'s\ Quest.app
Knight's Quest.app: accepted
source=Notarized Developer ID

Notice that this check varies slightly from when we first did this check before the notarization — the source now says Notarized Developer ID.

Final Packaging

Now that the game has been ported, code signed, and notarized, it is time to package it up for distribution. There are a variety of ways to do so, whether via an installer, a disk image, or a zip archive. If you are distributing the game via download from your website, you'll probably want to bundle the game and any other extras (such as README files, game manuals, etc.) into a single containing folder. If you are going to upload the game to Steam, then you may not want an enclosing folder.

To be a good internet citizen, make sure when the zip archive is created, that any Mac-specific files are removed, such as the .DS_Store file and __MACOSX, which stores excess metadata and resource forks.

zip -r KQ.zip . -x ".*" -x "__MACOSX"

This example zips up everything in the current directory, but excludes any dot files and any unwanted metadata. With the game packaged up, it's ready to go!

Ported Games

It has been a pleasure and joy to help bring a number of adventure games to the Mac. Below is the list of AGS games I've helped port. I highly recommend that if you enjoy adventure games, give them a try! I hope that this extensive tutorial has been useful in learning how to port games developed with the Adventure Game Studio to the Mac, in addition to learning how to code sign and notarize a Mac application. If you would like assistance in porting, contact me via e-mail or Twitter.

Resources