10 Years!

One whole decade in the ASP.

On this date back in 1999, I joined the ASP (Association of Shareware Professionals), so this is the 10th anniversary of my membership in this important trade organization.  (As a side note, through some game playing, it is also the 5th anniversary of the second company membership for Sherry.)  I was the last member to join in the 1900s, having taken almost ten years to join in the first place, and I immediately regretted not joining earlier.

In the past 10 years, I spent nearly half of that time in a volunteer position, mostly the 4.5 years during which I was a Director, including two stints as ASP Chairman of the Board.  I do not currently hold any official job, concentrating on developing our company and products, but I still strongly believe in the value of membership for access to the private newsgroups alone, nevermind other benefits.  In fact, I am now a Lifetime Member to be sure to always have this wealth of information and experience available to me.  (Anybody who fails to join just because of the “shareware” word in the organization name is making a very poor business decision.)

Clearly, one of the biggest benefits of ASP membership to me was to network with successful shareware publishers, which led directly to our association with Goodsol Development, which involvement has now lasted more than 8 years and could easily have paid for annual ASP membership dues into the next millennium.  That is only one contact I have made, but I have both learned and profited from many of the other members of the ASP.  Join Now!

Speaking of Goodsol, in wrapping up the year, I had a chance to review the products we shipped during 2009:

That is not too bad a list for year, but I bet that we can beat that in 2010!  (We already have three products on the publishing schedule, and 5 more big projects in the immediate pipeline.)

[Note to self:  Press the ‘Publish’ button when the article is finished and proofread.]

Pretty Good Solitaire Mac Edition 2.10

A major update to our best-selling Mac solitaire game is published.

Last week, Pretty Good Solitaire Mac Edition 2.10 was released by Goodsol Development, capping a very successful period of development.

Pretty Good Solitaire Mac Edition 2.10 adds 99 new games since the previous version (2.0), bringing the total to 200 games, nearly double the previous count.  The full (purchased) version includes a few more bonus games, so our Mac users now have access to 245 solitaire games, and this is a free upgrade to all previous PGSME customers.

What makes this particular release special is that, for the first time, Mac users have access to game features that are not (yet) available to Windows users.  In particular, PGSME includes climb mode for all of the 200 (+45) supported games, which gives users of this title access to 99 (+11) climb mode online high score listings that are not yet accessible from any of our products for Windows.  (Goodsol Solitaire 101 supports climb mode for all its games, though.)

Pretty Good Solitaire Mac Edition 2.10 is available for only $24.95 and can be ordered via secure server, and you can get the (optional) CD for only $7.50 more.  A 30% discount is available for registered Pretty Good Solitaire [Windows] users.  Of course, as with all of our game products, a trial version is available for download.

But wait!  There’s more!! PGSME 2.20, with 300 games, has already been announced for release in 2010, and it will be a free upgrade for everybody who purchases the current version.  Buy now!

Guilt by [non-]Association

There goes the “neighborhood”.

Going into the past weekend, one of our product sites had a problem in which accessing the page caused a very scary (and completely incorrect) “Reported Attack Site!” message in Firefox browsers, and a similar message in Safari (and Chrome as well, reportedly).  Of the major browsers, only Internet Explorer was allowing direct traffic to two specific pages, because it was the only one that does not (by default, anyway) subscribe to the StopBadware.org database.  To access our site, a user would have to click to ignore a message that said, more or less, “Run away from here and never come back.”

The problem began last Thursday, when FileKicker, a Digital River company that provides download bandwidth for many independent software publishers (including Goodsol Development, until recently), got blacklisted on the aforementioned database.  This meant that downloads from FileKicker generated the scary message, presumably because they delivered some “badware” somewhere, although I have no evidence (nor much doubt) that this happened.  The report was filed by Google.

On Friday, two of our pages that linked to downloads there were blacklisted as well because, I guess, Google assumed that if FileKicker was bad, anybody who linked there must be bad, too.  This is the “bad neighborhood” idea: we never linked to anything classified as badware or even any third-party software, but if we linked to a “bad” site, we must be bad ourselves.  Of course, the fact that FileKicker provided services for thousands of clients does not seem to matter.  This was bad on Windows, but devastating on Mac OS X, where Safari has the vast majority of the market.

By very early Sunday morning, due to quick action from Goodsol to remove all FileKicker links, and a subsequent retraction from Google, our pages were no longer banned, but all our direct links to FileKicker downloads (such as those stored at Apple Downloads) were still a major problem.  It took until yesterday [Wednesday] evening (i.e., six days) before FileKicker got this problem resolved for their downloads, with precious little information provided to customers in the interim.

This was a ridiculous episode, which produced many insights:

  1. The problem was first reported in the newsgroups of the Association of Shareware Professionals (by Dexter Bell of The Utility Factory, developer of FileBoss, an excellent file manager).  This is one of those situations in which ASP membership (and participation) was invaluable for rapid response.
  2. Digital River claims to be “the global leader in e-commerce”, a public company with close to $3 Billion in annual transactions, yet it took DR three times as long to fix the problem as Goodsol Development, a MicroISV, and never informed its clients until well after ASP members informed them.
  3. SWMirror, an independently operated download service run by Mitchell Vincent, was able to provide (better) services to affected publishers and have many downloads restored before FileKicker, part of a conglomerate with more than 1000 employees, even acknowledged the problem.
  4. The pattern of Digital River buying successful companies serving the shareware industry and turning them into garbage is intact; in fact, that record may now be unblemished.  Dealing with DR companies should only be done with due deliberation.  (read: “Do not touch them with a bargepole.”)
  5. The concept that Google can, with a simple electronic “report”, essentially shut down an internet business overnight, is more than a little scary.  Imagine launching a product that could compete with Google (or a blog being critical of them) and having most of your traffic cut off by a similar unsubstantiated report.
  6. The whole internet is a “bad neighborhood”. In fact, Google itself would be the worst culprit of all, since it provides links to nearly every crack site, domain squatter, malware distributor, and internet fraud out there.

Really, I am definitely in favor of a system to eliminate (or castrate) true spammers and distributors of malware, but when an honest company that has been doing business online safely almost since the inception of the web is economically impacted, things have gone too far.

Here endeth the rant.

Most Popular Solitaire 2.01

A maintenance release of our popular Windows and Mac game is released.

Last week, Goodsol Development published Most Popular Solitaire 2.01, an update to this title available for both Windows and Mac OS X.  This update fixes a couple of bugs that were uncovered since the release of MPS version 2.00 back in May.

Most Popular Solitaire is a collection of 30 of the most popular solitaire games, including Klondike (a.k.a., Solitaire), FreeCell (same deals as Windows FreeCell), and Spider (plus the One Suit and Two Suits variants), as well as some more unusual games, such as Crazy Quilt.  There are 13 more bonus game variants for registered users, for whom this is a free (and recommended) update.

If you are looking for a fun collection of solitaire games, but feel overwhelmed when confronted with hundreds of different games, try Most Popular Solitaire.  You can download and try either the Windows 98/Me/XP/Vista/7 version or the Mac OS X 10.4+ version, or you can simply and safely purchase online for only $16.95 (with an optional CD for $7.50).

Note that the Windows and Mac OS X are compatible, such that all initial deals are identical, saved games can be exchanged between platforms, and they both use the same online high score tables.  This allows for result comparisons and discussion of games in the (active) Goodsol discussion forum.

(Yes, we have been very busy on the development side lately, and an even bigger release is scheduled for next week…)

Goodsol Solitaire 101 version 1.02

Another maintenance update for this Windows solitaire product is released.

Goodsol Development has published Goodsol Solitaire 101 version 1.02, an update to this popular solitaire title for Windows.  The product features 101 different types of card solitaire, as well as 34 more bonus games when the full game is purchased.

This free update fixes the few bugs reported since GS101 version 1.01, which was released back in April, and is recommended for all users.  Fortunately, there were no issues associated with the release of Windows 7, so the software is fully compatible.  (This should be the last Windows-only release of GS101, as the Mac OS X version is expected soon.)

For more information, please visit the Goodsol Solitaire 101 web site, or just download the trial version now and try it.  The program can be purchased now for only $19.95, with an optional CD available for $7.50 more.

Curmudgeon Day 2009

Curmudgeon Day Defined

Curmudgeon Day is the day after Thanksgiving (in the United States), a quasi-official holiday that extends the weekend to four days.  It is celebrated by staying home and doing whatever activities suit you, whether spending time on something important or nothing worthwhile at all.  Notably, this involves not flocking like a braindead zombie to retail outlets because advertising and the media tell you to do so.

Learn all about the holiday by reading Curmudgeon Day posts going back to 2004 (although the tradition dates much further back than that).

Today, I played soccer with friends, spent time with my family, watched television, and emptied the backlog on my ‘to do’ list.  I am now ready to return to work refreshed and reinvigorated.

“Whatever! Whatever! I do what I want!!”Eric Cartman

Happy Thanksgiving 2009!

Much for which to be Thankful

This day is traditionally for reflecting upon those things that make us thankful, a tradition that has been in my family, literally, for 388 years. [*]

Our company begins the holiday season by taking the Wednesday afternoon before Thanksgiving away from work and, instead, hosts a game party for employees and guests, with board games, card games, food and drink.  In truth, we have in recent years included some party games on game consoles, but given the nature of our business…

For the business, I am thankful that our development efforts in recent years are starting to bear fruit, that we have successfully completed several projects over the past year, and that the economy is on the upswing without the bottom having a devastating impact on our company.  Personally, I am thankful that, despite a number of health issues over the past year, my family is relatively healthy, that we have made financial progress over the last year, and (to be trite) for friends and family.

We are now in the process of preparing the feast, which will include turkey (x2), ham, potatoes, beans, stuffing, cranberry sauce, apple pie, and (non-traditional, but yummy) cheesecake.  I was just about to insert a picture here of the “camp cooking” apparatus that was to be used to prepare one of the birds, but word just came down that a structural failure has destined the turkey for oven roasting instead.  (“Christmas!  We will do it at Christmas,” I am told.)

Anyway, here are our best wishes to all of you, whatever the day (and season) may hold.

[*] My Great Great Great Great Great Great Great Great Grandfather is credited with the First Thanksgiving.  Here is a small snippet of his account:

They begane now to gather in ye small harvest they had, and to fitte up their houses and dwellings against winter, being all well recovered in health & strenght, and had all things in good plenty”  — William Bradford, Of Plimoth Plantation

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.