
  How to Write a Hack

The HackMaster protocol is an open standard, and it is my hope that
third-party developers will take advantage of its many features in order
to code PalmOS system extensions that behave in a standard way and do
not conflict with each other. If everyone installs a patch in their own
proprietary manner, not only will users have to learn a new managing
method for each one, but extension conflicts will also arise almost
inevitably.

Still, the Hack API is intended to be an evolving structure. If there is
some feature that you would like your extension to be able to rely upon,
feel free to email me and I'll see if I can roll it into the next
HackMaster revision.

Introduction <#Intro>
The Hack File Format <#PRC>
Compiling and Linking a Hack <#Compiling>
Trap Patch Interaction Details <#Interaction>
Control Panel Interaction Details <#CDEV>
The Custom Procedure <#Custom>
Licensing Information <#Licensing>
Conclusion <#Conclusion>


    Introduction

HackMaster is a program for managing extensions to the Palm OS system
software, affectionately known as "Hacks". Typically, such extensions
hook themselves into one of the many system routine traps and thus are
called in addition to or in lieu of the standard Palm OS routines.

For instance, one could code up a Hack to replace the standard Keyboard
dialog with a new input method, or insert a procedure into the
event-handling code in order to perform special actions at the entry of
a certain Graffiti character, or even patch the text field routines to
allow hyperlinking between applications. The potential uses are even
wider than the system software itself.

Unfortunately, there are some problems with implementation. Where in
memory does one put the routine? How does one install it into the trap?
How do you deal with multiple patches on the same trap, especially if
they install and remove themselves in different orders? How do you
gracefully recover from a system reset?

It is to solve these problems that HackMaster was written. HackMaster
reads in special PalmPilot resource files of type 'HACK', which contain
the code for the patch and also whatever user interface routines are
necessary. Then HackMaster presents a standard interface to the user for
installing and removing Hacks, and for changing their configurations. It
handles all the dirty work of installing and removing the trap patches
and keeping track of multiple Hacks on the same trap. And it saves the
current extension configuration so that they can all be automatically
(or optionally) reinstalled on a system reset.

This document, then, will take you through the process of writing a
PalmOS system extension using the HackMaster protocol. In doing so, you
will be bypassing a lot of effort in the details of trap patching, and
you will be ensuring that your extensions will automatically coexist
peacefully with all other possible extensions.

I assume a fairly high level of Pilot programming knowledge. You should
definitely be able to code up a standalone PalmOS application before
attempting to write a Hack, since the facilities for debugging are much
less and the potential for mishaps is much greater. Some of the
techniques employed here are not part of the standard PalmOS
documentation, but with any luck Palm may adopt HackMaster for
'official' use in the future.


    The 'HACK' .PRC File

The files read by HackMaster are standard Palm resource files, just like
standalone applications. They contain code resources and UI resources
and whatever else you want to stuff into the resource database. They are
installed just like applications, but won't show up as programs to be
run, though they can be deleted in the Memory utility in the normal way.

For more information on compiling and linking the necessary 'code'
resources, skip to the next section <#Compiling>. This section outlines
the larger structure of the .prc file, specifying the resource IDs and
types for the various parts of the Hack to be recognized and function
properly.

The Hack file type is 'HACK', instead of that standard 'appl' for
application files. Note that each Hack, just like each application,
requires a unique creator code to identify it. These creator codes
should be registered with Palm Computing just like applications to
prevent conflicts. Anyway, here are the resources to include:


      The patch routines

The actual routines that will be inserted into the system as a trap
patch should be placed in 'code' resources with IDs starting with 1000
and increasing sequentially. Each Hack file can contain several patches
that will all be installed and removed at the same time, if a single
desired function requires multiple patches.

The 'code' resources will be called directly by the trap dispatcher, so
be sure they are linked in the appropriate way (see the next section
<#Compiling>). It is possible to have multiple patches in the same file
patching the same trap, but there isn't much purpose to this anyway.
Almost any trap can be patched, with the major exception of the Feature
Manager calls.


      The trap codes

Along with each patch 'code' resource, a resource of type 'TRAP' must
exist with the same ID containing the 2-byte trap code that the patch
should be installed in. To determine the trap code, have a look at the
SysTraps.h PalmOS header file, which declares a large enumerated type
for them. If you don't want to count them yourself, here is an annotated
copy <http://www.daggerware.com/hacktrap.txt> of the header file with
the trap codes listed directly.


      Custom routine

An optional 'code' resource of ID 1500 can contain a routine which
handles custom extension behavior. It can override or supplement the
standard HackMaster installation, removal, and reset recovery behavior.
You can use this to install patches in places besides traps, such as
interrupt service routines or application patches. This can also be used
to initialize database structures for the extension, and so forth, or
clean up allocated memory after a reset.

In general, unless you have a very good reason, you should almost always
choose to supplement the HackMaster behavior rather than replace it. For
more details on this custom routine, see a later section <#Custom> of
this file.


      The Hack name

A 'tAIN' resource of ID 3000 contains the name that is listed in the
HackMaster directory. If this is not present, your Hack will still be
listed, but with the label 'unknown'.


      The Hack icon

An optional 'ICON' resource of ID 3000 contains an icon for your Hack.
This resource is not presently supported by HackMaster, but I felt I
should include this future possibility in the API from the start, just
in case.


      The about box

An optional 'tFRM' resource of ID 3000 contains a dialog box that will
be displayed when the user presses the question mark icon in the
HackMaster list. This form will just be called with FrmDoDialog, so no
user interaction is supported beyond pressing an OK button. It should
describe the function of the extension, and provide whatever author
information you like.


      The control panel forms

An optional 'tFRM' resource of ID 2000 contains a form which will be
displayed when the user presses the plus icon in the HackMaster list.
This form should be used to support any options that the extension needs
to have set. Some patches require no interaction, but others need a lot
of configuration. This form is allowed to call other forms in the ID
range 2000-2999 if necessary, but the ID 2000 form is the only one
launched directly by HackMaster. For more details on the interaction
between the control panel forms and HackMaster, see a later section
<#CDEV> of this document.


      The control panel handlers

For each included control panel form, you must include a 'code' resource
with the same ID that contains an event handler for that form. This
routine will be installed with FrmSetEventHandler and called upon to
process UI events for that form. For the specifics of the duties of the
handler, see a later section <#CDEV> of this document.


      Other resources

Other UI resources, specifically those supporting the 'tFRM's, should
also be included. In general, keeping ID numbers close to those of the
'tFRM' is a good idea. Resource ID conflicts between HackMaster and an
extension are unlikely, due to the way the PalmOS searches for active
resources, but just in case HackMaster has kept all of its resources in
the range 9000-9999, except for the 'tAIN' and 'ICON' resoures of ID
1000 necessary for HackMaster to show up in the Applications list.


    Compiling and Linking a Hack

In several places, you are required to supply a procedure to be executed
in a separate 'code' resource. This is a minor exercise in creative
compiling and linking, but once you set it up right, the process is
completely automated in MPW.

The basic idea is that you want to link your code in such a way that
HackMaster can call the procedure you want by just jumping to the
beginning address of your code resource. To do this, you will need a
separate C source file for each code resource, and a separate compile
and link statement for each one. Only in the building of the resource
file do all the parts come together.

Unfortunately, since you are not compiling a free-standing application,
but just a code fragment, you will not be able to use the Pilot
Simulator at all. All of the compiling is done in MPW, and all debugging
will be on-Pilot. In the future, we may be able to figure out a way to
write a trap patch shell to run on the simulator, but for now you're
living on the bleeding edge.


      The source code

At a minimum, you will need a source code file for your trap patch. You
may need some more separate files for additional patches and for control
panel event handlers. In any case, the source code does not contain a
PilotMain routine or even a C main routine. Just write the procedure you
want to be called in the code resource (the routine will be specified in
the link stage). For example, a trap patch source file that will patch
SysHandleEvent looks like this:

#include <Pilot.h>

//no global variables allowed!

/*
    Put procedures called by the main routine here.
*/

Boolean MySysHandleEventTrap(EventPtr event)
{
  // the trap patch routine here
}

Quite simple, actually. For a control panel event handler, you do the
exact same thing, except that the routine to be called should have the
functional form of a standard event handler routine (the same as above,
but just because we coincidentally picked an event-handler routine to trap).


      Compiling

In MPW, the compile step is exactly the same as for a standard Pilot
application. You should include a compile statement for each source code
file in your trap patch project:

"{OBJ_DIR}mypatch1.c.o"  MakeFile "{SRC_DIR}mypatch1.c"
	 {CPP}  -o "{OBJ_DIR}mypatch1.c.o" "{SRC_DIR}mypatch1.c" 
			{C_OPTIONS}

In the above sample, the directories and compiler options should already
be defined by the makefile in the normal way. This step will get you the
object files you need to link into the code resources.


      Linking

This is the different step, in which you tell MPW that you're not making
a Pilot application, but freestanding code fragments instead. The link
options are different for just this case, and you do not include the
standard startup code object file like you do for regular Pilot
projects. Here is a sample link statement:

LINK_OPTIONS = -single -coderesource -rt CODE=1

	{LINK} {LINK_OPTIONS} -t rsrc -c RSED -m MyRoutine 
		"{OBJ_DIR}mypatch1.c.o" -o mypatch1.code

The first thing different is that the |-custom| link type has been
replaced with a |-coderesource| tag, indicating that the code should be
linked into a 'CODE' resource of ID 1 in the Macintosh resource output
file. Secondly, the |-m| tag is used in the link statement in order to
specify what routine in the file should be put in the beginning of the
resource. This is done in such a way that this routine will be run by
just jumping to the first byte of the CODE resource, which is how
HackMaster knows where your routines are.


      Resource compiling

Now we need to tell MPW how to put all these CODE resources together
with the patch's UI resources into a single .PRC file. For this, the
important part is the .r resource definition file. Here is a sample:

#include <BuildRules.h>
#include <SystemMgr.rh>

include "mypatch1.code" 'CODE' 1 as sysResTAppCode 1000;

#if LANGUAGE==LANGUAGE_ENGLISH
include ":Rsc:mypatch.rsrc";
#else
#error "The compiler variable LANGUAGE must be defined"
#endif

Note that in this resource file, no mention is made of the CODE 0 and
DATA 0 resources, which contain global variable information in a full
Pilot program. Here, just the one CODE resource is compiled as a 'code'
resource of ID 1000, exactly as a trap patch must be. If you had a
control panel event handler, you would need a second include statement:

|include "mypatch2.code" 'CODE' 1 as sysResTAppCode 2000;|

Note that this makes reference to a second output file, which must have
been compiled and linked separately. In your makefile, you can arrange
the dependencies such that all of the various compiles and links are
performed automatically on a project build.

The last step is to give MPW the command to build the .PRC file:

	{CC} -d RESOURCE_COMPILER {C_OPTIONS} 
			-e "{SRC_DIR}mypatch.r" > mypatch.i
	PilotRez -v 1 -t HACK -c ???? -it mypatch.i -ot "My Patch"
	Duplicate -y "MyPatch" "mypatch.prc"

Note that the application type for a Hack file is 'HACK', intead of
'appl'. Other than that, you get a standard .prc file that can be
installed and deleted in the usual way. However, it will not show up in
the Applications dialog, but only in the HackMaster list.


    Trap Patch Interaction Details

For an extension to successfully interact with the system and with other
extensions, a little bit of care is necessary. The duties of a Hack and
restrictions on the actions a Hack can perform are severe, due to the
fact that traps can be called from many different states of the Pilot.


      Calling the original trap routine

Upon installation, HackMaster creates a feature keyed by the patch's
creator code and resource ID which contains the address of the original
trap routine. The patch should obtain this address from the Feature
Manager and use it appropriately. If multiple patches are installed on
the same trap, this address might actually be that of the next patch in
line instead of the actual system routine.

Here is a sample code snippet showing how your trap routine on
SysHandleEvent in ID 1000 should call the system routine:

#define mycreator '????'
#define myresourceID 1000

Boolean MySysHandleEventTrap (EventPtr event)
{
   Boolean (*oldtrap)(EventPtr); //procedure pointer to old trap;
   DWord temp; //for the feature manager call type checking
   Boolean handled;

   FtrGet(mycreator,myresourceID,&temp); //get old trap address from HackMaster
   oldtrap=(Boolean (*)(EventPtr))temp; //set procedure pointer
   handled=false;
      //do whatever you want to with the event, handle it or not
   if (!handled) { //if you didn't handle it, call the old routine
      handled=oldtrap(event);
   }
   return handled;
}

Depending on the function of the Hack, the original routine can be used
in different ways. It could be ignored entirely, in order to replace its
functionality entirely with that of the Hack. Actions could be taken by
the Hack, and then the original routine called, as for instance an event
handler patch. The parameters of the routine could be altered and then
the original called, or the original called and then its results
manipulated. In general, unless you have a reason not to, the original
system routine should be called at some point, if for no other reason
than to allow other Hacks installed on the same trap to process the
event too.

Hacks can be installed and removed at will, so be sure to get this
address new at every function call, since it may have been changed to
reflect a new trap patch.


      Data storage issues

Patches are free-floating code snippets, so they do not have global
variable data. They inherit the stack of the currently running
procedure, so should not use excessive amounts of stack space. The
dynamic heap may be filled or empty, depending on whether or not the
running program is using it extensively (only use this for temporary
storage, since dynamic heap space is extremely precious). Patches may
access databases and resource files and features in order to retrieve
configuration data stored for them by the control panel routines.

Upon installation, the patch 'code' resource is locked in memory, and
remains so for the duration of its use. It is in one of the static
heaps, though, so memory protection needs to be kept in mind if you have
some visions of self-modifying code.


      Error handling

A Hack is in a unique situation. Since it is installed on an existing
system routine, it doesn't *have* to do anything. A default behavior is
already present in the system routine. Therefore Hacks have no excuse to
crash. Error check frequently, and if anything goes wrong, just do
nothing but call the original system routine and let it deal with the
situation.


    Control Panel Interaction Details

The interaction between HackMaster and the control panel form resources
contained in Hack files is very specific. Failure to follow the details
may result in fatal errors, so make sure you are doing what HackMaster
expects you to do.


      Duties of HackMaster

At the press of the plus icon, HackMaster executes a FrmGotoForm(2000)
command, jumping to the form with ID 2000, if present. This is the only
form directly launched by HackMaster in the control panel interface,
although the ID 2000 form may jump to other forms in the ID range
2000-2999 without difficulty.

HackMaster will trap frmLoadEvents with IDs in the range 2000-2999 and
perform the necessary setup for them. It will open the Hack's resource
file and call FrmInitForm for the form in question. It will also install
the 'code' resource with the same ID as the event handler for that form
using FrmSetEventHandler.

HackMaster will also trap frmCloseEvents. Upon doing so, it will
immediately pass the event on to the Hack's event handler and/or the
standard form event handler using FrmDispatchEvent. After this call
returns, HackMaster will close the Hack's resource file again.


      Duties of the control panel

The control panel must handle frmOpenEvents in order to draw the form
and initialize any structures it needs. It can rely upon its resource
file being opened and accessible for reading in other resources.

If other forms need to be brought up by the control panel, this can be
done with FrmGotoForm commands. FrmPopupForm is not recommended because
this will probably result in the resource file being opened twice by
HackMaster.

The control panel must provide a means for the user to exit it, such as
a 'Done' button. At such a close, the control panel should execute a
FrmGotoForm(9000) command to jump back to the HackMaster main list.

The control panel event handler, just like any other 'code' resource
discussed above, cannot rely on global variables. Feature manager calls
possibly in combination with database or dynamic heap allocation should
be used to save whatever state information is necessary between event
handler calls.

Note that since the control panel handler is called by the form event
dispatcher, system events will already have been handled at an earlier
stage, so it is not currently possible for the control panel to respond
to system-level events, such as hardware app button presses, for
instance, without serious workarounds.

The control panel must be able to gracefully handle the case of being
brought up when the extension is installed or not. If configuring of the
extension is not possible while it is running, the control panel should
detect this situation (with Feature Manager calls) and display a "sorry,
can't do that" dialog.


    The Custom Procedure

The API for the custom install/remove procedure in ID 1500 is still
being developed. Further details will be forthcoming when available.


    Licensing Information

Ah, finance. If you are a commercial company and you would like to write
and sell your own Hacks using this protocol, you are free and clear to
do so, and in fact I encourage you to adopt this standard to prevent
extension conflicts. However, the HackMaster program itself is
copyrighted shareware, and thus *cannot* be freely distributed with the
extensions you sell. It's not fair for you to get the profit for giving
customers my program, now is it?

Anyway, if you do want to distribute copies of HackMaster with other
software you have written, we'll have to arrange some sort of license.
Specifics vary on a case-by-case basis, but generally I would expect a
licensing fee per copy of 25 cents or one percent of the cost of the
product, whichever is less. This entitles you to distribute copies of
HackMaster, but does not mean that your customers are automatically
registered users with all the rights and privileges thereof (like
technical support, email updates, etc.)

Anyway, just email me and we'll talk specifics if this applies to you.
If, on the other hand, you are a shareware/freeware author and want to
distribute copies of HackMaster with your Hacks, go right ahead. Just be
somewhat obvious in mentioning the fact that HackMaster is indeed
shareware are should be registered with DaggerWare; the appropriate URL
is in the program itself.


    Conclusion

Well, now that I've totally bored you with technical details and
infuriated you with inane comments, I can wish you good luck in coding
up your own Hacks. It's definitely a powerful tool, with the potential
to do things on and to the Pilot that can't be done any other way.

So have fun!

------------------------------------------------------------------------


Archived from <http://www.daggerware.com/hackapi.htm>

