MAS Preparation, Part 1

Project modifications

In Preparing for Mac App Store Submission, the first set of changes you should make are to the project itself.

This installment describes the project modifications that need to be made to an existing Mac OS X game project.  The general assumptions are that the existing project is working and properly tested, and that, ultimately, you will want to maintain a single set of source code with conditional compilation to differentiate the store version from other builds.  Note that these are the steps that we took for Pretty Good Solitaire Mac Edition; your project may require some adjustments to these steps.  (Comments where any significant change is necessary would be very appreciated.)

So, without further ado…

0. Create a duplicate store project

Before making any other changes, create a duplicate copy of the entire project folder and name the copy appropriately.  In our case, the original folder was ‘Pretty Good Solitaire’ (which builds full and trial versions of the game) and we created a separate ‘pgsse’ (Pretty Good Solitaire [Mac/]Store Edition) folder for MAS modifications.

The version of your project that you will submit to the Mac App Store is a separate SKU (Shelf Keeping Unit), a build that uses slightly different code and configuration and which is distributed via a different channel.  While it is possible to have the store target within the main project, certain features (e.g., Power PC support, Mac OS X 10.5 support, downloadable data) are not supported in MAS, so it is easier to separate them (at first, anyway).  In any event, doing so at the start gives you a safe playground for making changes without messing up your working project.

1. Update the project version number

As just noted, the store build is a separate version and, thus, should have a different version number.  In our case, we decided that odd minor versions (e.g., “1.01”) would represent the store editions, while even minor versions (e.g., “1.00”) represented the direct downloadable editions.

2. Rename the primary build target

In the new project (of course), rename the primary build target to something appropriate.  In our case, we renamed the full version target to ‘Pretty Good Solitaire store’, which is now the store version.  You could also delete any redundant or obsolete targets remaining in the project.  For example, we deleted the trial version target, as it is built in the original project and the MAS version cannot have any vestiges of a trial version.

Note that this step is not strictly required, but it is a good idea to differentiate between targets, so it is always immediately obvious which project is active, and also to minimize any excess baggage, which reduces the possibility of mistakes.

3. Build with a current Xcode version

Make sure that you build the project with a current version of Xcode.  Submissions to the Mac App Store now require Xcode 4 and, as of this writing, the latest version is Xcode 4.2.1.

If you have an older version of Xcode, this would be a good time to upgrade, and although it will not be mentioned explicitly, you should build the project regularly, ideally after every change, to make sure that the build is not broken and behaves as expected.

4. Define a preprocessor variable

To allow for conditional compilation of certain source code, it is a good idea to define a preprocessor variableAPPSTORE, for any build targets intended for MAS.  In the ‘Build Settings’ for the target (or the whole project, if you prefer), find the setting for ‘Preprocessor Macros’ and add “APPSTORE” to each configuration.  Note that it is common to have different variables for ‘Release’ and ‘Debug’ configurations, so be careful to define APPSTORE for each one without accidentally removing or altering any existing definitions.

Of course, there is no requirement that the preprocessor variable be named “APPSTORE”, but beware that simply using “STORE” results in a naming conflict in the latest Mac OS X SDK.

5. Add necessary frameworks/libraries

In order to test the validity of app receipts, you will need to add the IOKit and Security frameworks and the crypto library.  From the ‘Build Phases’ of the primary target, open the ‘Link Binary With Libraries’ section and, by clicking on the ‘+’ symbol, add ‘IOKit.framework‘, ‘Security.framework‘ and ‘libcrypto.dylib‘.

Note that Xcode 4 adds these frameworks/libraries at the top level of your project; you will probably want to drag them into the ‘External Framework and Libraries’ folder with the other frameworks.

6. Configure debugging symbols

Despite submitting a release version to MAS, Apple requires debugging information to be included with a submission.  To accede to this requirement, you must set ‘Generate Debug Symbols’ to “Yes” and also set ‘Debug Information Format’ to “DWARF with dSYM File” (at least for the ‘Release’ configuration) in the ‘Build Settings’ of the target.

In our original project, we had all debug information disabled and/or stripped from the release builds, but one of our early issues was the lack of the dSYM file with debugging information for Apple.

7. Set correct build architecture

Finally, set ‘Architectures’ to an Intel (only) setting; for our project, that is “32-bit Intel”.

Even if your code and original project supports both PPC and Intel via a “Universal” application, the presence of a PPC build in your submission will result in rejection.  (We found that out the hard/lengthy way.)  At least now there are settings for this; in Xcode 3, you had to use “i386”, which was not even listed as a choice.

Conclusion

At this point, you should have a new project with a store target and all of the build settings configured appropriately.  In the next installment, Part 2: Property List (Info.plist) changes, we will discuss the necessary adjustments and additions to the information property list for your project.

Preparing for Mac App Store Submission

Making a Mac OS X game project suitable for MAS

If you currently have a Mac product and have not already done so, you may be considering submission to the Mac App Store (MAS).

In the upcoming series of posts, I will be detailing the process that we went through to get Pretty Good Solitaire Mac Edition, and some of our other game products, successfully submitted to MAS.  There were a number of rejections along the way, as the App Store Review Guidelines [note: requires Mac developer agreement], while extensive, are not comprehensive (nor are they 100% consistent, as we had some products accepted and others rejected with identical behaviors).

Over multiple submissions, and fewer rejections, we developed a submission checklist which I will detail roughly (some items are specific to our games) in these upcoming posts:

We have had product in the Mac App Store since launch day, more than a year ago.  If you already have a game that runs on Mac OS X, it makes sense to make the several modifications to get it into MAS, another channel to find customers.  However, in our experience, it is not a viable substitute for direct downloadable sales.  The channel is not (yet) the primary ‘go to’ location for Mac software, although the availability of Lion (Mac OS X 10.7) only on MAS should shift more customers.  Additionally, there is the same downward pressure on pricing (towards free) seen on the iOS App Store, sales are lackluster, and (of course) you are giving 30% directly to Apple.

I would certainly not recommend developing a project solely for the Mac App Store, nor eliminating a direct downloadable sales channel in favor of MAS, but with an existing project it may be worth the fairly limited extra effort it takes to be there, too.

Accidentally Cheating at Backgammon

Why players perceive unfairness in Backgammon software

On a regular basis, inexperienced Backgammon players voice opinions about how a certain computer program or game server cheats.  The stochastic nature of the game lends itself to this kind of perception on the part of human beings.  Generally, there are a few primary reasons for this type of belief.

First, novice players often fail to recognize the complexities of the game of Backgammon, so what they perceive as an unnatural number of “lucky rolls” are not (necessarily) due to luck, but rather due to skillful play on the part of the opponent.  Expert players tend toward positions where a greater number of rolls would be considered good (i.e., “lucky”).  A higher percentage of good moves tends to make the dice appear biased in ones favor, and it is also key to good checker play.

In many cases, players also fail to understand the nature of truly random numbers.  It is often stated that, say, a certain number of doubles in a row indicates…  excuse me…  “proves” that the virtual dice are unfair when, in fact, a truly random number generator would have to produce any arbitrary sequence (whether or not a pattern is perceptible) given enough rolls.  Of course, we are talking about pseudo-random number generators (PRNG), so they are, by their very nature, not truly random.  However, one would have to do an actual study/count of the dice rolls to make any conclusion about any particular PRNG.

The reason for this need to analyze a PRNG scientifically, rather than anecdotally, seems fairly obvious.  Human beings have selective memory, which means that we tend to recall things that are out of the ordinary, so a number of doubles in a row stands out, whereas a statistically identical sequence of rolls that do not seem to show a pattern are not reported.  Likewise, a few very good (or very bad) rolls are more memorable than many run-of-the-mill rolls.

Related to this is the concept of apophenia, which is the human “experience of seeing patterns or connections in random or meaningless data.” [from Wikipedia]  Our minds have evolved to recognize patterns, so we can sometimes perceive things that are not there.  This is how people see images in clouds, hear music or sounds in white noise, and imagine divine imagery in oil stains or burnt toast.

All of these factors make it very easy for an average person to perceive unfairness in Backgammon software or servers (even in games against other human beings), and even trained experts can be fooled.

How experts demonstrate that Backgammon software is fair

There are a few key points that are usually made by experts when arguing that a particular Backgammon program does not cheat.  First, of course, one generally describes some of the aspects of the perception problem, as listed above.  In particular, reports are almost always anecdotal, so they can be dismissed quickly as having no scientific validity until somebody does an actual count and statistical analysis.

To dismiss accusations of manipulated dice (by software), the suggestion is to manually input dice rolls, which most (decent) programs allow, according to the rolls of physical dice recorded meticulously, or by changing to an alternative PRNG.  If the results stay statistically consistent, that argues against the idea that the rolls are manipulated.  Another common argument is that programs can “look ahead” to see which rolls are upcoming and make moves based on this prior knowledge, and manual input of dice rolls also removes this possibility.

Another method to test if dice rolls are being artificially manipulated is to switch sides and look for discrepancies.  In other words, start a game (or save one in progress) with a particular random number seed and play the rest of the game, recording the dice rolls for each side.  Then, restart (or load) the game and play the opposite side.  If the dice rolls remain the same, then no manipulation was done to bias the outcome.

A final, less scientific, approach is the simple “Why?” method, wherein one looks at the reasons why (and how) a programmer might decide to write a biased program.  Speaking as the primary programmer for MVP Backgammon Professional, from MVP Software, I can assure you that cheating would add a whole extra layer of (unwanted and unnecessary) complexity, so I certainly did not and would not include such code.  In fact, accusations of unfairness were troubling enough to MVP for the first version of the program that our version has a replaceable PRNG library so one can write ones own (with whatever extra checking is desired).

Possibility for Backgammon software to cheat without malice aforethought

This whole topic was reinvigorated when yet another thread appeared on rec.games.backgammon recently, entitled “Jellyfish.  Cheating or just Lucky” [links to Google groups].  Through dozens of messages, some people suggested/argued that the Backgammon program Jellyfish seemed to cheat, while two other popular programs, GNU Backgammon and Snowie, did not.

Interestingly (and, n.b., anecdotally), when testing MVP Backgammon, I had a similar experience.  I was simply testing relative strength with a series of 25-point matches between my program and these others.  Whereas the strength of my neural network was comparable to the others, it got beaten significantly by Jellyfish when it rolled the dice.  When MVPBG rolled, it was much closer.  As a final test, I played one match with manual rolls, and it was again close.  At this point, I figured out the likely problem (leaving alive the possibility that it was just sheer chance).

The whole purpose of a neural network is to discover connections and patterns in provided data, and the conclusions are affected by the design of the inputs (essentially, which raw data is supplied) and, of course, the requested output(s).  In our design, we basically supplied the number of checkers on each point (in a special format), the number on the bar, and the number borne off.  This specifies a pure position in the game (with no knowledge about moves or rolls), and our outputs were designed to estimate the probability of each potential game outcome (win, loss, or winning/losing either a gammon or backgammon).  The neural network was only used for evaluation; the selection of moves was based on the evaluation of the resulting position (and cube decisions were calculated mathematically from the neural network outputs).

Theoretically, we could provide irrelevant inputs (e.g., outside temperature) and during training, their influence on the network would tend toward zero.  However, providing somewhat related data, such as the last game move, could give the neural network just enough information to begin to anticipate an outcome and bias the outputs.  More directly, providing the current dice roll, or perhaps designing the neural network to rate individual moves based on that roll, gives the network additional information that could be used to actually predict the next pseudo-random roll, especially if the particular PRNG is not very good.  After all, guessing what the next roll would be based on the position and previous roll is exactly the kind of task that neural networks are designed to solve.

Based on this observation, I suggest that it is possible that the programmers of Jellyfish may have inadvertently, and with no malicious intent whatsoever, provided their neural network with just a little too much information, and it may have taken that information to (at least partially) figure out the random number sequence and then draw conclusions that were not intended.

This would be a very interesting (and perhaps slightly startling) example of emergent behavior in a computer system.  It would, however, explain why a program could pass all of the tests to “prove” it is not cheating, but still have an observable bias when using its own dice.  I suppose we could call it “computer intuition“.  Of course, without more scientific study, it could still just be called “luck“.

API Design Dilemma

I need to decide how to define certain parameter types.

The general situation is this:  I am refactoring a piece of C++ code to be part of a separate library, so I am in the process of defining and documenting an API for using the included classes and methods.  A fundamental design consideration is that the library may be called by third parties, without access to the source code, so I need to make the code as close to bulletproof as possible.  (For internal development, at least I know the methods used, how the API will be utilized, and that it will not be abused terribly.)

During this process, I encountered a theoretical dilemma about how to handle certain parameters.  Specifically, I was working on method definitions that included sizes and counts that should never be negative (but, of course, I need to prepare for abuse).  As an example, say I am reviewing a method that is declared like this:

    bool FillBuffer ( byte* pBuffer, int nSize );

Here, pBuffer is a pointer to the buffer to be filled, and nSize is the size of that buffer.  Of course, it is not possible for a buffer to be a negative size, so my initial reaction was to redefine it as:

    bool FillBuffer ( byte* pBuffer, unsigned uSize );

This makes perfect sense from a theoretical standpoint, but then a practical consideration occurred to me.  I always verify parameters with an assertion and, for a public method (as here), abort the routine if verification fails, with an exception or error return as appropriate.  In this case, my original method would assert nSize to be greater than zero (and return false), which would catch any negatives.  The new method would only catch the case where uSize was zero, but if a careless programmer cast a signed integer (or, worse, let the compiler do it), the current validation check would not identify a problem.

So, there were a few obvious solutions that I considered:

  • I could leave the original definition alone, which would catch obvious parameter errors, but would be theoretically incorrect, and if a programmer wanted to pass the size of a static buffer, using sizeof(), there would be a signed/unsigned mismatch.
  • I could use the new method definition, which would be correct in theory, and just trust programmers not to abuse the method with invalid parameters (and let them suffer if they do).
  • I could use the new method definition and add a sanity check so an extremely large buffer size (i.e., likely a negative value cast improperly) would be rejected, but the drawback there is that any such check would be somewhat arbitrary, and it would limit the functionality for any programmer who truly wanted to use an enormous buffer.

Each of these solutions has advantages and drawbacks.  I dislike having a parameter take a type that is not accurate (though not so much as to not have written this code in the first place), but I dislike arbitrary limits even more.  However, I know that defensive design is important here, since a careless programmer is, in my experience, the most likely to complain that the library or API is at fault.  (I was once threatened with physical violence when I produced a critical review of code written by a nominal “programmer”.)

At this point, I am leaning toward a hybrid solution by overloading the method with both (or multiple) definitions, the original checking for negative sizes as usual before doing an explicit cast of the size value and passing processing to the new/correct method.  The advantage is that passing an actual negative number (or signed type) will result in that extra checking, and a programmer could pass a buffer size up to the limit of the unsigned type.  The disadvantages are the additional work needed to create the extra stub(s), loss of type checking during static analysis, and the fact that our careless friend could still cast a value to create problems (but then it should be quite obvious, at least).

This post is an exercise in the process of working through a problem by simply writing down the issues, which often results in a solution (or decision) by the time one is finished with the description.  (It did here.)  I would, however, welcome any comments on my proposed solution, or other suggestions.

Finally, yes, I know that int and unsigned are not ideal parameter types for this in the first place, but I used them for the purpose of illustration.  (The principle also applies to object counts and other similar parameter types.)

Disk Images Revisited

There are some issues under Snow Leopard.

In a blog post last year, I gave detailed instructions on Making Mac Disk Images Pretty.  Unfortunately, I have discovered that the most recent version of Mac OS X has some issues of which developers should be aware when using that technique (not that there is any obvious alternative).

I recently upgraded my development system to Mac OS X 10.6 (Snow Leopard).  Apple is fairly aggressive with requirements for its development tools,  and I need to upgrade in order to use the latest iPhone SDK.  Of course, I already had a Snow Leopard partition that I had used for testing, so I knew that our software would work, and I never experienced any previous problems.

When the time came to build an update for one of our products (FreeCell Plus 4.00/4.01), I had a few minor niggles with Xcode 3.2.1 (the latest version), described later, but I got the product built and packaged properly.  Then it was time for testing on our other supported versions of Mac OS X, Tiger (10.4) and Leopard (10.5), and I was surprised to learn that, although the software itself ran flawlessly, the disk images showed no background image when mounted, and the folder size was wrong.  The icons were in the correct locations, relative to the top left corner of the window, so the formatting was not entirely ignored, just effectively ignored.

After some research into the problem, I found the answer in this DropDMG forum thread, where it is stated that Apple has confirmed this as a bug in Snow Leopard.  I moved DropDMG over to my Leopard partition, rebooted to that version of Mac OS X (10.5), and build my packages there.  That solved the problem, and the background images showed in all supported versions of OS X.

Conclusion: In order to have background images appear in disk images on older versions of Mac OS X, you must build the disk image under Leopard or earlier, not Snow Leopard.

Caveats: Sometimes, when opening a prepared disk image under Snow Leopard, there is a blank space on the bottom (probably not coincidentally exactly the same size as the folder toolbar), which does not appear on other versions of Mac OS X.  Also under 10.6, the folder toolbar includes a slider control for quickly changing icon sizes (a.k.a., fouling up your layout).  I have not been able to determine the cause of the former issue (yet) nor the reason for the latter.  (How often does one actually change icon sizes that this is even remotely necessary?)

There were some build issues with Xcode 3.2.1 under Snow Leopard.

As I mentioned, I experienced a couple minor build issues after the upgrade to Xcode 3.2.1, both of which were addressed, albeit with differing degrees of success.

First, the default compiler in the latest version of Xcode is GNU 4.2, which is incompatible with the 10.4 (universal) SDK, and the very first build let me know that.  Faced with a choice, downgrading the compiler to GNU 4.0 versus upgrading to the 10.6 SDK, I chose the latter, which I saw as the path of least resistance (see “Apple is fairly aggressive with requirements for its development tools” above).  Unfortunately, at least for our project, the 10.6 SDK does not function as advertised and, despite properly setting the target OS to 10.4 (and obviously not using any 10.5 or 10.6 features), a product built with the 10.6 SDK will not run under Tiger (period), although it does work under Leopard.  I switched (back) to GNU 4.0 and the 10.4 SDK and all was well.

Second, the latest version of Xcode upgrades its tools (obviously), and one seemingly innocuous change to the linker was to eliminate the need for the “-mlong-branch” compile option for certain object files (generally kernel files, which means primarily for Apple and not so much for the rest of us).  Unfortunately, a warning message was added to that effect, yet Apple did not update its PPC object files accordingly.  As a result, every time one links a universal binary (supporting PPC), the linker throws this warning: “Object file compiled with -mlong-branch which is no longer needed.”  The only permanent solution is to recompile certain (protected) development files oneself, or wait until Apple fixes the problem.

The latter issue is extremely annoying to me, because now I am in the position of either ignoring linker warnings, which I never advise, or spending hours (or days) figuring out how to rebuild a portion of the development tools.  Can you say, “not my job“?  Personally, from my experience of Apple over the last few years, my bet is that this problem will soon be addressed by a campaign to suggest that developers should not continue to be stuck in the past supporting PPC systems.

Does anybody want a piece of that action?

Three Steps to Apple Help, Part 3

Accessing Apple Help from C++ Code (Carbon)

The third/final step in implementing Apple Help is programmatically displaying the main contents or individual topics within your help pages.  This, of course, assumes that you have already created a set of HTML help pages for Apple Help and then indexed and integrated these files into your Xcode project.

Accessing Apple Help from C++ using Carbon is fundamentally simple.  The Apple Help Reference contains only 4 functions (plus one constant enumeration that is no longer used).  There is a little bit of necessary mucking about with bundle references and whatnot that may be unfamiliar to programmers who have not worked with Mac development before, but it is all fairly straightforward.

In order to access Apple Help from the application, you first register your help files using the AHRegisterHelpBook() function.  This only needs to be done once, ideally during program initialization, and we do it something like this:

bool RegisterHelp ( void )
{

    CFBundleRef const bundle = CFBundleGetMainBundle ( );
    if ( !bundle )
        return false;

    CFURLRef const location = CFBundleCopyBundleURL ( bundle );
    if ( !location )
        return false;

    FSRef file;
    if ( !CFURLGetFSRef ( location, &file ) )
    {
        CFRelease ( location );
        return false;
    }

    _Error = AHRegisterHelpBook ( &file );

    CFRelease ( location );

    if ( _Error )
        return false;

    return true;

}

(Additional validity checking and error reporting has been stripped from this sample code for clarity.)

In the above example, most of the code is just to obtain the FSRef (file system reference) of the application bundle from which the help is to be displayed, and it assumes that you want help for the current application.  (Obviously, you could instead pass a reference to any other bundle and access a different help system, but that would be unusual.)

Once the help files are registered, you can display the main help page using the AHGotoPage() function (with default parameters), which we accomplish with a function similar to this:

void ShowHelpContents ( void )
{

    CFBundleRef const bundle = CFBundleGetMainBundle ( );
    if ( !bundle )
        return;

    CFStringRef const key = CFSTR( "CFBundleHelpBookName" );
    CFTypeRef const book =
        CFBundleGetValueForInfoDictionaryKey ( bundle, key );
    if ( !book )
        return;

    CFStringRef const help = (CFStringRef)book;

    _Error = AHGotoPage ( help, NULL, NULL );

}

(Additional validity checking and error reporting has been stripped from this sample code for clarity.)

You can also display a specific help topic by using the AHLookupAnchor() function, for which we use something like this:

void ShowHelpTopic ( CFStringRef topic )
{

    if ( !topic )
        return;

    CFBundleRef const bundle = CFBundleGetMainBundle ( );
    if ( !bundle )
        return;

    CFStringRef const key = CFSTR( "CFBundleHelpBookName" );
    CFTypeRef const book =
        CFBundleGetValueForInfoDictionaryKey ( bundle, key );
    if ( !book )
        return;

    CFStringRef const help = (CFStringRef)book;

    _Error = AHLookupAnchor ( help, topic );

}

(Additional validity checking and error reporting has been stripped from this sample code for clarity.)

In both of these last two examples, the function reads the CFBundleHelpBookName string from the main application bundle and uses that string for the name of the help system, passed as the first parameter to the appropriate Apple Help function.  (You could, of course, hard code this string, but if you were thinking that this is a good idea, shame on you.)

Accessing Apple Help from Objective-C (Cocoa)

If you are using Objective-C and Cocoa, much of the help system handling is done behind the scenes (which is, in part, why some programmers find Objective-C to be objectionable).  If you follow the steps in the previous two posts (Part 1 and Part 2) to insert your Apple Help into a Cocoa project, your main help system will work automatically.

The main help page in a Cocoa application is accessible by the user through the ‘Help->[ApplicationName] Help’ menu option by default.  If you wish to bring up the main page from your Objective-C code, you may need to reference an anchor on that page (see below) or use the Carbon method described above.

To access a specific help topic (anchor) from Objective-C and Cocoa, this is done through the shared NSHelpManager object using the openHelpAnchor:inBook: method.  For example, to open the ‘rules’ topic, you would use the following code:

    NSString *book = [[NSBundle mainBundle]
        objectForInfoDictionaryKey:@"CFBundleHelpBookName"];
    [[NSHelpManager sharedHelpManager]
        openHelpAnchor:@"rules" inBook:book];

The documentation for NSHelpManager is quite clear that all of its functionality is simply a number of wrappers around the functions described in the first section of this post, so you could always use those functions directly, if desired.

There you have it.  At this point, you should know enough to properly implement Apple Help in your Carbon or Cocoa application for Mac OS X.  The process is a little convoluted to suss out from the documentation, so I hope this provides a decent jump start.

Three Steps to Apple Help, Part 2

Adding Help Files to an Xcode Project

The second step in implementing Apple Help is integrating the help files into an Xcode project, so they will be properly added to an application bundle, and generating an Apple Help index file.  This article describes the process continuing from the HTML files generated in the previous blog entry (Part 1).  Be sure to read the comments there, too, where Alexander Halser of Help & Manual describes an alternative (and probably quicker) method of adding the necessary ‘name’ tags.

First, transfer the entire folder containing the HTML help files to your Mac OS X development system (as/if necessary).  [A discussion of how this is done is far below the scope of this article.]  The rest of this article assumes that this folder is named “Help”; rename the folder or adjust the instructions as appropriate.

Next, you will be adding the files to the application bundle.  Open your project in Xcode.  Highlight the ‘Resources’ group, right-click (or Command-click) it, and select ‘Add->Existing Files…’ from the context menu.  Choose the ‘Help’ folder and then click the ‘Add’ button.  In the next dialog box, mark the ‘Copy items into destination group’s folder (if needed)’ check box, select the ‘Create Folder Reference for any added folders’ radio button, and then click the ‘Add’ button.  This will create a folder reference in the project, such that the entire contents of the ‘<Project>/Help’ folder will be added to the bundle as a unit.  In other words, you can add, delete, or change files in that folder from outside Xcode and those changes will be reflected in the build without explicitly adjusting the project file.

If you want to add a localized help file instead (which is not a bad idea, even if translating the help file is not in the immediate plans), the above process is slightly different.  In that case, explicitly copy (or move) the ‘Help’ folder into the appropriate project language folder, such as ‘<Project>/English.lproj’.  Follow the same procedure (‘Add->Existing Files…’), but select the ‘<Project>/English.lproj/Help’ folder, and do not (or, rather, it is unnecessary to) mark the check box, but the radio button is still important.  In this case, you will see that instead of just adding a ‘Help’ subgroup to the ‘Resources’ group, Xcode will automatically add the group as ‘Help/English’ (or whichever language).  Repeat as necessary for additional languages.

Now that the help (HTML) files will be properly copied to your application bundle(s), you need to identify these files as the Apple Help files for the project.  To do this, open the ‘Info.plist’ file for the project, a property list that contains global project settings.  Right-click (or Command-click) and select ‘Add Row’ from the context menu, choosing ‘Help Book directory path’ [or ‘CFBundleHelpBookFolder’].  Edit the value of that row to be “Help”.  Using the same procedure, add a ‘Help Book main page CONTENT attribute’ [or ‘CFBundleHelpBookName’] row, and set its value to the identical string from the “AppleTitle’ meta name (e.g., “Pretty Good Solitaire Help” [sans quotes]).  You can now close Xcode.

Creating an Apple Help Index

In order for a user to be able to search your help file, you must create an Apple Help index file.  The tool you need for this is (the enigmatically named) Help Indexer, which should already be installed on your system in the ‘/Developer/Applications/Utilities’ folder.  (I find it convenient to create an alias to this particular utility in a more accessible location.)

Launch the Help Indexer tool.  On the first execution (and thereafter as necessary), you will want to set the utility preferences (‘Help Indexer->Preferences…’), as they determine the parameters for the Apple Help index (or indices) you will create.  In the ‘Index Style’ panel, clear the ‘Generate Panther-compatible Indices’ checkbox, unless you plan to support Mac OS X 10.3 or earlier.  (At this point, we only support 10.4 [Tiger] and later [Leopard and Snow Leopard].)  I recommend selecting the ‘Errors and warnings’ radio button in the ‘Logging Options’ panel, as including the status messages for large help files simply produces too much noise that could hide any valid warning or error messages.

To actually generate the help index file, once the tool is configured, is quite simple.  Click the ‘Select…’ button, browse to select/highlight the ‘Help’ folder, and click the ‘Open’ button.  Then, click the ‘Create Index’ button, and the process begins.  When finished, review the warning/error messages (if any), and then click ‘Quit’ to close the tool.  (Note that, if you do generate a Panther-compatible index, the messages are shown in a separate tab, and you may get a ‘Warning: [filename].html — Error parsing file.  Check validity of HTML.’ message for each file.  In our experience, we were unable to eliminate these warnings, but the indexing still seemed to work properly on the older systems.)

At this point, you can open Xcode and, if you look into your ‘Help’ group (added above), you will see that a new ‘Help.helpindex’ file has been added (as well as a Panther-compatible ‘Help idx’ file, if selected).  The advantage of adding the folder as a reference group, as noted earlier, is that changes to the folder contents (such as adding the index file) are automatically reflected in the project.  The corresponding disadvantage, however, is that these changes do not necessarily trigger a bundle rebuild, so after making help file changes, you will want to either clean your target (‘Build->Clean’ or Shift-Command-K) or explicitly incorporate the changes by right-clicking (or Control-clicking) on the project group that hold the help files and selecting the ‘Preprocess’ command from the context menu.

Now you have a fully indexed Apple Help system incorporated into your project.  All that remains is to add code to programmatically access the main help content, as well as individual topics, which I will cover in the third installment, so please stay tuned.

Three Steps to Apple Help, Part 1

Building an HTML Help File

The first step in implementing Apple Help is building an HTML help file.  Apple Help is the default help system in Mac OS X, and it is fundamentally a system for displaying HTML 4.01 from an application.  In order to use some (or any) of the features of Apple Help, one must do some minor customization, which is what I intend to describe here.  (The actual writing process is left as an exercise for the reader.)

For an overview of Apple Help, see this Apple Help Programming Guide.

Any decent HTML (or text) editor can be used to produce the necessary help files, but since ours is a help system for a product available on both Mac OS X and Windows, it makes sense to start with a full-featured help editor under Windows.  In our case, we use Help & Manual, which is an excellent and capable product.  (Note that we still choose to use the older H&M 4, since version 5 added the silly ribbon toolbar interface.)

Starting with a help project appropriate for Windows HTML Help (.chm), and with a tool that either works with or exports separate HTML files, there are only a few extra steps required to make an equivalent version for Apple Help.  In H&M, you use the ‘Browser-based Help’ option to write the necessary HTML files (as described later).

The common method of accessing a topic in HTML Help (as well as in the older WinHelp format) is via help context numbers.  Apple Help, on the other hand, uses a named anchor tag for each topic that needs to be accessed directly from an application.  For example, for a topic referenced as ‘contents’, you would add the following HTML tag to the topic:

<a name="contents" />

A similar tag should be added to every desired topic page, each with a unique name.  In H&M, this can be done by using the ‘Insert|Plain HTML Code…’ menu option, or you could edit the HTML directly, of course.  I found it most useful to have the basic tag on the clipboard, paste it onto each page, and then directly edit/add the name portion.

Important Note: Do not use spaces or capital letters for the topic names. Apple Help seems to have trouble finding such tags, so context help and searching may fail.

The next step is to export the separate HTML files (if necessary).  In H&M, this is accomplished by generating ‘Browser-based Help’ (via ‘File|Compile Help File…’ or the toolbar).  First, however, you will want to click ‘Customize Browser-based Help’ and make a few changes to the default options.  On the ‘Layout’ page, select the page layout that puts everything in the same window (i.e., no frames, no scripts).  Next, on the ‘Popup Topics’ page, select ‘HTML encoded topics’, which should remove any Javascript.  Finally, on the ‘HTML Export Options’ page, change the ‘Extension for HTML topic files” to “.html” (from “.htm”).

Once your help files are together, you need to add two custom meta tags to the header of the main help page (the one you want to display when the help is opened generically).  The “AppleTitle” meta tag sets the title of the help file, and the “AppleIcon” meta tag sets the name of the help icon (described below).  For Pretty Good Solitaire Mac Edition, which has all the help files in a ‘Help’ folder, we add the following lines inside the page header (<HEAD>) of our contents page, just after the title (<TITLE></TITLE>):

<meta name="AppleTitle" content="Pretty Good Solitaire Help" />
<meta name="AppleIcon" content="Help/help.png" />

The help icon (‘help.png’, in the above example) should be a 16×16, 32-bit image (with alpha transparency) that represents the help file, usually a small version of the application icon.  Place this image in the same folder with all of the HTML files.  Note that Apple Help in recent versions of OS X do not display the help icon prominently (if at all), but help icons were important in earlier versions.

Finally, in order to appear similar to other Mac applications, you will want to change the fonts in your CSS (you did use CSS, right?) to prefer ‘Geneva’ over ‘Arial’ for the display font.  H&M generates a ‘default.css’ file, so to make this change, you need only replace each instance of “font-family: ‘Arial’;” in this file with “font-family: ‘Geneva’, ‘Arial’;” and you are done.

As a final hint, if you are using Help & Manual, you can provide a link to the keyword index (automatically generated) by manually adding a link similar to the following (for the ‘pgsme’ project):

<a href="pgsme_kwindex.html">Help Index</a>

At this point, you should have a folder containing all of the HTML files for your help system, with the necessary tags, and a help icon file.  Now, just prepare to transfer these files onto your Mac development system and wait for the second installment, in which I will explain how to generate the Apple Help index and add the help files to an Xcode project.

Windows 7 Escapes

Microsoft releases its latest operating system.

Last week, Microsoft published Windows 7, the successor to Vista, to some fanfare.  The reviews have been fairly positive, perhaps because Vista softened the audience, but it seems that the sales tag line should be “Windows 7: what Vista should have been.

We tested the new operating system, in a couple of virtual machines, by running Sun VirtualBox virtualization software.  My first impression was that this was a great productVirtualBox, I mean.  Although I have not yet delved into the advanced features, it did exactly what we needed with a minimum of fuss and bother.  We were able to test both Windows 7 x86 (32-bit) and Windows 7 x64 (64-bit) on the same 32-bit Vista host system without serious problems.  In fact, the only problem we experienced was an inability to find a 64-bit sound driver through Windows Update (so our x64 VM did not have sound).

Back to the operating system, we found it to be perfectly adequate.  It performed as expected and was easy to use (for somebody already used to Vista, anyway).  There is nothing particularly revolutionary, but (as with the update from Windows 98 to Windows 98se) everything seems to be more polished, with a few minor features added.  The big change, really, is that the 64-bit version is becoming mainstream (whereas the 64-bit support for the previous versions was not quite ready for prime time).  It is for this reason that I am likely to upgrade my development system to Windows 7 x64 in the near future.

Some of the changes include the ability to “pin” an icon to the task bar (replacing the Quick Launch toolbar), and a new management feature for task bar (a.k.a., tray) icons, allowing the user to control when these icons are displayed or hidden.  In order to encourage users to make use of this feature, Microsoft kindly made their own icons here very ugly, so the first customization item is to get rid of them.  (This will also facilitate ignoring the loads of useless preinstalled junk that comes on some new computers.)

As far as developers are concerned, nothing much has changed.  As long as your software is Vista-compliant, it will probably run unmodified under Windows 7. All of our recent product releases worked just fine; the only items noted were the (now) missing Quick Launch toolbar and the task bar icon disappearing (i.e., being hidden) after its first appearance.  The only significant caveat, though, is that 16-bit applications (those built for Windows 3.1) will not run (directly) in Windows 7 x64.

Of course, I do have a couple of recommendations for Microsoft to consider in the future.  First, they should probably do a better job of considering the symbolism they (perhaps, inadvertently) put forward.  The default wallpaper for the beta and release candidate versions of Windows 7 was a male Siamese Fighting Fish, also known as a betta.  (“Get it?”)  The latter name may be punny, but the fact that these fish (at least at the pet store) have to have separate enclosures because they are nasty and do not play nicely with the other fish suggests that Win7 is intended to be likewise.

Also, perhaps underlining the above weakness in Microsoft’s overhyped marketing capabilities, I will point out that they had great successes with Windows 3.0 and Windows 3.1 (standard version numbering), as well as Windows 95, Windows 98, and Windows 2000 (release year numbering), moderate successes with Windows 286/386 (hardware support), Windows NT, and Windows XP (opaque version lettering), but commercial failures with Windows Me, Windows Vista, and even Windows (1.0, initial release) itself.  I suggest that Microsoft stop trying to “evoke” some greater vision and stick to practical version identification.

Finally, I note that the “Starting Windows” screen of Windows 7 is the best feature of the product, nearly perfect for its purpose.  It is too bad that the same design was not carried through to the system icons, which really are a step backwards in aesthetic terms.

No Magic Numbers

Action Solitaire 1.31 is now available for download.

After the last posting, we discovered a rather significant bug in Action Solitaire. It was fortunate that it was discovered in house, but unfortunate that it was not found during beta testing and, hence, required a public update. The problem caused two of the 65 games to behave incorrectly (or even crash) when large or huge card sizes were selected, either explicitly or implicitly through automatic sizing.

The problem turned out to be magic numbers in the code. We released the first version of Action Solitaire back in 2003, which was six years of coding experience ago and at a time when I felt under some (self-imposed) pressure to get the product finished. Unlike some of the other projects, the source code for this game has not been refactored, except to the extent necessary to make updates for Vista and add new games, so I never revisited these (working) games to see the problem.

For those who do not know, a magic number is an explicit and undocumented constant in the source code for a program, so named because the value works like magic, without any proper explanation. In this particular case, the width and height of an image buffer were set to constant values, calculated (manually) to accommodate an area based on the largest card sizes supported by the game at that time. Rather than actually letting the computer determine the necessary buffer size, based on named constant values (e.g., MaxCardWidth and MaxCardHeight), the code just used constant numbers directly. When the maximum card size increased, the buffer was too small and problems ensued. Such are the dangers of magic numbers.

It did not take long to find and fix the problem, but it should not have been necessary in the first place, especially since we had standards, even back then, that discouraged the use of magic numbers. I made an exception and got burned. Ouch.

Anyway, Action Solitaire can be downloaded here, and I guarantee ample opportunity to find other bugs in this product, but the game is fun (and addictive) as well.