Pig! — Building a SkyOS Application in C++


Chapter 4

This Chapter of Pig! is something of a maintenance release: it doesn't add very much to our Pig! application, but expands the Humble Framework by quite a bit.  Although our ultimate goal for this chapter was the ability to store application resources in a single location, this was only the tip of a software iceberg.  Using the SkyOS 'package' concept, resources are files that are stored inside a single .zip archive that can be selectively extracted by the application.  This prompted the creation of the HResMgr class, which interacts with the user if things go wrong via the new Prompt() and SelectFile() functions.  Of course, in order to extract files from a .zip file, it was first necessary to create the HZipFile class, which required a class designed for massaging file names, i.e., HFileSpec.  Since a file name is really nothing more than a long string, it made sense to have HFileSpec be a special-purpose subclass of a generic string class; unfortunately, this also meant creating the HString generic string class.  Since 'unzipping' files is also memory-intensive,  the HMemory class appeared to help manage dynamic memory buffers.  Six new classes and who knows how many utility functions later, the 'new & improved' HApp class can now extract resources by name from a single package file!

As usual, all of the source code and a precompiled binary for this chapter can be downloaded here: PigChapter04.zip.  If you would like to report a bug—and I'm sure there's at least one in there somewhere—or would like to make a request, suggestion, or comment, you can reach me or through the SkyOS forums.

Humble Framework

With the addition of nearly a dozen new classes to the Humble Framework, it's now necessary to divide them into high-level categories to help keep track of what's where.  These new categories are:

For details or to download the latest version of the source code, please refer to the Humble Framework on-line documentation.  New classes added to this version are highlighted in the list below.  Classes that are still being tested are marked with () and should be used with caution.

ADT Classes

Base Classes

GUI Classes

SYS Classes

Development Tools

With the release of SkyOS 5.0 Beta 8, you  can now use version 3.3.3 of gcc and it's accompanying libraries.  Beginning with this Chapter, we will be using three (3) separate make files to build the Pig! application: humble-rules.mak as the top-level makefile, humble.mak for the Humble Framework, and a standard makefile to define the dependencies unique to Pig!.  The only change from previous chapters is that humble-rules.mak was formerly named just rules.mak; it was renamed to avoid conflicts with the 'official' rules.mak file released with the SkyOS SDK.  The following example is for building Pig! on a Windows system, using the Cygwin version of GCC; if you are using a different development environment, you will have to tailor the make files somewhat.  Your mileage may vary.

Since we are now using a more powerful version of GCC, it's an opportune time to take advantage of some of the features unique to GCC.  Perhaps the most sophisticated—and most annoying—is that all calls to printf and it's work-alikes (sprintf(), vsprintf(), scanf(), etc.) can be checked at compile-time for proper arguments using the __attribute__ ((format (printf,x,y))) syntax.  Don't worry if your using something other than GCC to compile the Humble Framework, all of these features are hidden behind the GCC_ONLY() macro in the HAL.h header file.

A feature the almost made it into this version of the Humble Framework is the concept of function-level tracing using the GCC -finstrument-functions capability.  The code is actually still there—lurking at the bottom of HDebug.h and commented out in humble.mak—but isn't currently enabled, nor is it documented.  Proceed at your own risk!

Application Classes

The Pig! application still consists of two unique classes, each of which has an interface (header) file and an implementation (source) file.  The classes are as follows:

The PigApp Class

The top-level class PigApp class—which represents the entire application—is declared in the header file Pig.h and is implemented in the source file Pig.cpp.  To take advantage of the new Framework classes, some minor changes have been made to this class:

The new HMenu and HMenuBar classes wrap the SkyGI widget_menu structure, and simplify the creation of top-level menus and the menu bar itself.  Each menu must have a unique non-zero identifier that is used to access the HMenu object; by convention, only the menu bar can have an ID of zero.  In addition, each menu item also has a non-zero identifier that is also the command issued (via a WM_COMMAND message) when the menu item is selected.  The HMenu class is still somewhat skeletal, and future versions will add item insertion / deletion and the ability to dynamically enable or disable menu items.

/*
** Create the main menu bar.
*/
ErrCode ec;
HMenu * pMenu;

ec = m_menuBar.Create(0);
RETURN_IF_ERR(ec);

if ((ec = m_menuBar.AddMenu(ID_FILE, "File", pMenu)) != NO_ERROR ||
    (ec = pMenu->AddItem(CMD_NEW_GAME, "New Game", false)) != NO_ERROR ||
    (ec = pMenu->AddSeparator()) != NO_ERROR ||
    (ec = pMenu->AddItem(CMD_APP_EXIT, "Exit")) != NO_ERROR)
  {
  DEBUG_LOG("PigApp::Create() => failed to create \"File\" menu (ec = %ld).\n", int32(ec));
  return ec;
  }

if ((ec = m_menuBar.AddMenu(ID_HELP, "Help", pMenu)) != NO_ERROR ||
    (ec = pMenu->AddItem(CMD_HELP_ABOUT, "About...")) != NO_ERROR)
  {
  DEBUG_LOG("PigApp::Create() => failed to create \"Help\" menu (ec = %ld).\n", int32(ec));
  return ec;
  }

The WSplash Class

The WSplash.cpp source file has been updated to use the new application resource capability: this required a one-line change to the WSplash::Create() method.

Application Resources

One of the utopian goals of every software developer is the ability to cleanly install—and uninstall—any software package with a minimum of effort.  Because of this, packages which spray files and configuration settings all over the file system can quickly become persona non grata, because trying to find and remove them can be frustrating.  Ever tried to completely remove Microsoft Office™ from a Windows™ system?  To avoid this kind of nest-fouling, the goal of the Humble Framework is to have every application consist of three files: the executable (.app), a resource file (.pkg) and a settings file (.ini); strictly speaking, the settings file is optional because the Framework will automatically generate it the first time the application is run.  Any files that the application requires can be stored inside the resource file, from where they will be automatically extracted when needed.  Future versions of the Humble Framework will be able to 'clean up' application resources, or ultimately eliminate the reliance on external files.

So why use a package (.pkg) file?  First of all, because it's easy: it's only a .zip archive with a different extension, which means there are numerous tools available to create and manage the resource file during development.  Secondly, the zip library has already been ported to SkyOS and is part of the standard distribution, so its fast, stable, and readily available.  Thirdly, the .zip file format already has all kinds of developer goodness inside, including industry-standard compression, hierarchical file names, and the ability to add comments to files contained inside the archive.

From within any application built with the Humble Framework, using resources can be as simple as a couple of  lines of code:

if (theApp.ExtractResource("Splash.jpg") != NO_ERROR)
    {
/* handle a 'resource not found' error */ }

This code snippet first goes out to the resource directory (currently the same directory as the resource file) and checks to see if the file has already been extracted; if so, no further work is necessary, and a successful result is returned.  If the file doesn't exist yet, the resource file—originally opened in the HApp::Init() method—is searched for a file with the name Splash.jpg, which if found, is automatically decompressed and written out to the resource directory.  This example uses a JPEG image, but an application resource can be anything: a text file, a binary data file, even an application or another resource file!  Since the .zip archive doesn't place many restrictions on what kinds of files can be stored within an archive, the Humble Framework resource manager is subsequently equally versatile.

This version of Pig! only uses one resource, which is the 'splash image' stored in Splash.jpg; obviously, future versions of Pig! will use significantly more than one resource!

Conclusion

The Pig! application is still very boring, and hasn't changed much since Chapter 1.  Now that the Humble Framework is starting to take shape, it's time to leave the faceless utility classes behind and start having some fun.  The next chapter of Pig! will focus on the Graphical User Interface (GUI) as we add some eye-candy and functionality to Pig!

Until the next chapter…Tschüß!

,,,^..^,,,

2004.11.30-13:08