TweetFollow Us on Twitter

Palm On X: Dissecting The PDB File

Volume Number: 21 (2005)
Issue Number: 8
Column Tag: Programming

Palm On X: Dissecting The PDB File

By Jose R. C. Cruz

Introduction

Welcome to the debut of Palm On X. This is the first of a series of articles dedicated to using MacOS X as a development platform for PalmOS (R). I hope that the information I present in these articles would help inspire others (including myself) to independently provide PalmOS development tools and utilities that take advantage of such MacOS X technologies such as Cocoa, Core Foundation and XCode.

These articles are not meant to teach PalmOS application development only. Those who want to learn PalmOS programming may be interested in checking out Palm Programming: The Developer's Guide written by Rhodes and McKeehan and published by O'Reilly.

What is a PDB file

PDB stands for Palm DataBase. It is a binary file format used by the PalmOS as the means to store data on a PDA handheld. It is designed to make efficient use of limited storage space while supporting a large variety of data types. It also allows the storage of metadata that is specific to a PalmOS application.

Structure of a PDB file

Figure 1 shows the general layout of the PDB file. The file itself is subdivided into 5 distinct data blocks: Header, Record List and Record Entry, Application Information, Sort Information, and Record Data.


Figure 1: General layout of the PDB file.

The Header Block

The Header block contains data describing the entire PDB file. It stores the name, version, database attributes, creation and modification dates, type and creator signatures, and offsets to any application-specific data.

The basic layout of the Header block is shown in Figure 2. Listing 1 shows the data structure that defines the block. The elements that comprise the DatabaseHdrType datatype are as follows:

  • name. The name of the file as a 32 character C-string .
  • attributes. The attributes associated with the file.
  • version. The version number of the file.
  • creationDate. Date when the file was last created.
  • modificationDate. Date when the file was last modified.
  • lastBackupDate. Date when the file was last backed up.
  • modificationNumber. The modification ID number assigned to the file by the PalmOS.
  • appInfoID. The offset of the optional AppInfo data block from the beginning of the file.
  • sortInfoID. The offset of the optional SortInfo data block from the beginning of the file.
  • type. The four-character signature assigned to the file.
  • creator. The four-character signature of the PalmOS application that created the file.
  • uniqueIDSeed. The unique ID number assigned to the file by the PalmOS.

The date elements are expressed in the number of seconds since 1970 January 1. This is true for PDB files that were created on PalmOS 4.x or later. Older files may use a different reference year (1900 or 1904), especially if the files were synchronized on a non-Windows platform. In either case, the PDB date values will have to be converted since MacOS X uses 2001 as its reference year.

The attributes element consists of 16 bit-flags. Each flag indicating how the PDB file is to be handled by the PalmOS system. Shown in Figure 3 is a breakdown of each bit flag and its significance.


Figure 2: Layout of the Header block.

Listing 1. Data structure of DatabaseHdrType.

struct
{
    unsigned char    name[32];
    unsigned short   attributes;
    unsigned short   version;
    unsigned long    creationDate;
    unsigned long    modificationDate;
    unsigned long    lastBackupDate;
    unsigned long    modificationNumber;
    unsigned long    appInfoID;
    unsigned long    sortInfoID;
    unsigned long    type;
    unsigned long    creator;
    unsigned long    uniqueIDSeed;
} 	DatabaseHdrType;


Figure 3. Attribute bit flags used in the Header block.

The Record List and Entries Blocks

Located immediately after the Header block is the Record List block. This block contains the number of record entries present in the PDB file and the location of the first record entry.

The basic layouts of the Record List and Entries blocks are shown in Figure 4. Listing 2 shows the data structure that defines the Record List block. The elements that comprises the RecordListType datatype are as follows:

  • nextRecordListID. Pointer to the next RecordListType structure. This pointer is updated by the PalmOS only when the current RecordListType structure was unable to add more items due to memory constraints. The default value is often 0x00000000.
  • numRecords. The number of record entries present in the block.
  • firstEntry. The upper two bytes of the first record entry in the PDB file. If there are no record entries, this element is assigned the value of 0x0000 to preserve a 4-bytes alignment.


Figure 4. Layout of the Record List and Entries blocks.

Listing 2. Data structure of RecordListType.

struct
{
     unsigned long   nextRecordListID;
     unsigned short  numRecords;
     unsigned short  firstEntry;
} 	RecordListType;

The Record Entry block exists only if the numRecords of the Record List block is a non-zero value. If present, this block is located immediately after the Record List block. It is also terminated with 0x0000 after the last record entry to maintain a 4-byte alignment.

A non-zero value for numRecords does not always mean that there are valid records present. For performance reasons, some PalmOS applications assign a number of NULL record entries in their PDB files to serve as placeholders. It is often a good idea to validate the offsets for each record entry prior to reading the actual record data.

Each entry in the Record List entry block is defined by the RecordEntryType datatype. Listing 3 shows the data structure of that datatype. The elements that comprise the datatype are as follows:

  • localChunkID. The offset of each raw record data from the beginning of the PDB file.
  • attributes. The attributes of each record data
  • uniqueID. A three-byte unique ID number assigned to each entry by the PalmOS.

The attributes element consists of 8 bit-flags. Each flag indicating how each PDB record data is to be handled by the application and by the PalmOS system. Figure 5 shows a breakdown of each bit flag and its significance.

Listing 3. Data structure of RecordEntryType.

struct
{
    unsigned long localChunkID;
    unsigned char attributes;
    unsigned char uniqueID[3];
}  RecordEntryType;


Figure 5. Attribute bit flags used in the Record Entry block.

The Application Info Block

The Application Info or AppInfo block is where a PalmOS application can store application-specific data. It is also where the PalmOS stores category information that allows users to group records into different categories. This latter feature is only available if the application itself supports the PalmOS Category APIs.

If the PDB file does not have an AppInfo block, the appInfoID element of the Header block will have a 0x00 value. However if the application does support the Category APIs, its PDB file would then have an AppInfo block with the category information located at the beginning of that block.

Figure 6 shows the basic layout of the AppInfo block with the category sub-block included. Listing 6 shows the data structure of the AppInfoType datatype that defines sub-block.


Figure 6. The category region of the AppInfo block.

Listing 6. Data structure of AppInfoType.

struct
{
    unsigned short renamedCategories;
    unsigned char  categoryLabels[16][16];
    unsigned char  categoryUniqueIDs[16];
    unsigned char  lastUniqueID;
    unsigned char  RSVD;
} 	AppInfoType;

The elements that comprise the AppInfoType are as follows

  • renamedCategories. The number of category labels renamed by the user.
  • categoryLabels. An array of 16 category labels. Each label has a maximum of 16 characters in length
  • categoryUniqueIDs. An array of 16 unique ID number assigned to each category label by the PalmOS.
  • lastUniqueID. The unique ID number of the last category label that was used

The region between the category sub-block and the next data block will contain data specific to the PalmOS application. It is up to the developer to define the data structure of the information stored within that region. Without knowing the exact data structure, the information contained in the application-specific portion of the AppInfo block can only be viewed as a simple hex dump.

To determine the size of the application-specific region, I use the following equation:


The preceding equation is correct only if the next data block happens to be a SortInfo block. If a SortInfo block does not exists, I use the following equation to determine the size of the application-specific region in the AppInfo block:


The Sort Information block

The Sort Information or SortInfo block is another area in the PDB file where a PalmOS application can store application-specific data. Like the AppInfo block, the PDB file will have a SortInfo block if the sortInfoID element in the Header block has a non-zero value. A value of 0x00 would mean the absence of a SortInfo block. However, unlike the AppInfo block, the SortInfo block is not used at all by the PalmOS (as of this writing).

The data format of the SortInfo block also differs from one application to another. Without knowing the exact data structure, the information contained in the SortInfo block can only be viewed as a simple hex dump.

To determine the size of the SortInfo block, I use the following equation:


The Record Data Block

The Record Data block contains all the records stored in the PDB file. The format of each record data differs from one application to another. Without knowing the exact data structure of each record, the raw record data can only be viewed as a simple hex dump.

The location of each record in the Record Data block is stored in the localChunkID element of the Record Entry sub-block. To determine the size of each record, I calculate the difference between the localChunkID of the current record and the localChunkID of the next one as follows:


However, if I want the size of the last record in the Record Data block, I calculate the difference between the size of the entire PDB file and the localChunkID of the last record as follows:


Mapping the PDB Data Into A Plist File

Exporting the PDB file data into an XML provides a number of advantages, the most notable of which are access and portability. For example, if I convert a PDB file into an equivalent XML file, I can edit the XML data using any text editor. I can also upload the XML file to any system (such as Windows, Mac, Unix, and so on) with no loss of information.

The most prevalent XML file format used on a MacOS X system is the plist file. This format uses a key-value system to store and differentiate its data. It is also relatively easy to generate. This is the format that I will use to export my PDB file data.

I defined five major keys for the plist file. Each key represents a data block in the PDB file. To ensure that each key name is unique, I use a reverse URL naming scheme similar to that used for Java classes. Note that the acronym PSRC is the ticker symbol for PalmSource, Inc.

Listing 7 is the XML structure for the com.psrc.pdb.header dictionary. This structure is a modified implementation of DatabaseHdrType (see Listing 1). Notice that I gathered all the date information under the dictionary key named date. I also gathered the appInfoID, sortInfoID and uniqueIDSeed data values into a sub-dictionary group called ID.

Listing 7. XML structure of com.psrc.pdb.header.

<key>com.psrc.pdb.header</key>
<dict>
   <key>name</key>
      <string></string>
   <key>attributes</key>
      <integer>0</integer>
   <key>version</key>
      <integer>0</integer>
   <key>date</key>
   <dict>
      <key>creation</key>
         <date>2005-05-18T19:29:44Z</date>
      <key>lastBackup</key>
         <date>2005-05-18T19:30:03Z</date>
      <key>modification</key>
         <date>2005-05-18T19:29:51Z</date>
   </dict>
   <key>modificationNumber</key>
      <integer>0</integer>
   <key>ID</key>
   <dict>
      <key>appInfo</key>
         <integer>0</integer>
      <key>sortInfo</key>
         <integer>0</integer>
      <key>uniqueSeed</key>
         <integer>0</integer>
   </dict>
   <key>type</key>
      <string></string>
   <key>creator</key>
      <string></string>
</dict>

Listing 8 is the XML structure for the com.psrc.pdb.record.list dictionary. This structure is a straightforward implementation of RecordListType (see Listing 2).

Listing 8. XML structure of com.psrc.pdb.record.list.

<key>com.psrc.pdb.record.list</key>
<dict>
   <key>nextRecordList</key>
      <integer>0</integer>
   <key>numRecords</key>
      <integer>0</integer>
   <key>firstEntry</key>
      <integer>0</integer>
</dict>

Listing 9 is the XML structure of the com.psrc.pdb.record.entry array. Each element in the array is a dictionary whose structure is an XML representation of RecordEntryType (see Listing 3). The order of each dictionary entry is unimportant. I can easily reconstruct the original order of each record entry in the PDB file by simply examining the value stored in the localChunk key.

Listing 9. XML structure of com.psrc.pdb.record.entry.

<key>com.psrc.pdb.record.entry</key>
   <array>
      <dict>
         <key>ID</key>
         <dict>
            <key>localChunk</key>
               <integer>0</integer>
            <key>unique</key>
               <integer>0</integer>
         </dict>
         <key>attributes</key>
            <integer>0</integer>
      </dict>
      ...
   </array>

Listing 10 is the XML structure of the com.psrc.pdb.info dictionary. This structure is an amalgam of both the AppInfo and SortInfo blocks. The appInfo dictionary is a straightforward implementation of AppInfoType (see Listing 6). The categories key is an array of dictionaries, each dictionary containing the values stored in the categoryLabel and categoryUniqueID elements of AppInfoType. By combining these values into a dictionary, I am able to preserve the one-to-one correspondence between category label and unique ID.

Any application-specific data found in the AppInfo block is stored using the <data></data> XML tag. I also used the same approach to store any application-specific data found in the SortInfo block.

Listing 10. XML structure of comp.psrc.pdb.info.

<key>com.psrc.pdb.info</key>
   <dict>
      <key>appInfo</key>
      <dict>
         <key>categoryRenamed</key>
         <integer>0</integer>
         <key>lastUniqueID</key>
         <integer>0</integer>
         <key>categories</key>
         <array>
               <dict>
               <key>id</key>
               <integer>0</integer>
               <key>label</key>
               <string></string>
            </dict>
            ...
         </array>
         <key>appSpecific</key>
         <data></data>
      </dict>
      <key>sortInfo</key>
      <data></data>
</dict>

Finally, Listing 11 is the XML structure of the com.psrc.pdb.record.data array. Like the sortInfo key, I use the <data></data> XML tag to store the raw record data exported from the PDB file.

Listing 11. XML structure of com.psrc.pdb.record.data.

<key>com.psrc.pdb.record.data</key>
   <array>
      <data></data>
      ...
      <data></data>
   </array>

Building A PDB Viewer

I will now show you how to design and develop a simple PDB Viewer using Cocoa. I used XCode 1.5 to develop the Cocoa application. My development system is an iBook G3/600 MHz unit running MacOS X 10.3.9.

I personally try to avoid using any features that are specific to one version of the OS when I design my applications. This allows me to maintain some level of cross-platform compatibility. It also gives me the freedom to port my source code using other development IDEs such as Metrowerks Codewarrior or Project Builder.

The User Interface Design

The UI design for the PDB Viewer is quite straightforward. I've decided to use a single window to display the information retrieved from the PDB file. I then use an NSTabView object to create four panel views. Each panel view is dedicated to each one of the PDB data blocks. Each view is also assigned a controller that will provide the necessary information to be displayed. I will talk about the controllers later in this article.

Since I am developing a data viewer, I've decided to disallow on-screen editing. On-screen editing is beyond the scope of this article. However, I do plan to revisit the concept in a future article.

The layout of the Header panel view is shown in Figure 7. This view will display the data contained in the PDB Header block. The controller assigned to this view is PDBHeader.


Figure 7. UI layout of the Header panel.

Figure 8 shows the layout of the Record List panel view. Data retrieved from the Record List sub-block are displayed in the three topmost NSTextField objects. Those retrieved from the Record Entry block are displayed in the NSTableView object. The controller assigned to this view is PDBRecList.


Figure 8. UI layout of the Record List panel.

The layout of the AppInfo panel view is shown in Figure 9. Integer data retrieved from the category sub-block of the AppInfo block is displayed in the two NSTextField objects. The category labels and their corresponding unique IDs are displayed in the NSTableView. Any application-specific data read from the AppInfo block is displayed in the NSTextView at the bottom. The controller assigned to this panel is PDBAppInfo.


Figure 9. UI layout of the AppInfo panel.

Finally, Figure 10 shows the layout of the Data panel view. Application-specific data read from the SortInfo block would be displayed in the NSTextView at the bottom. The raw record data from the PDB file would be displayed in the NSTableView together with the corresponding local IDs.

Two controllers are assigned to update this panel view. PDBSortInfo handles the SortInfo data whereas PDBRecData handles the raw record data.


Figure 10. UI layout of the Data panel.

The Controllers

A total of 11 controllers comprise the PDB Viewer application. The five controllers mentioned in the preceding section maintain the display of information in each of the four tab panel views. Three controllers are used for data formatting. As for the remaining three, one maintains a PDB data buffer, another handles file I/O, and the last one coordinates the signal traffic between previous 10 controllers.

For purposes of length, I will only list the contents of the header files of each controller class. To see both the header and source files for each controller class, download the entire XCode project of the PDB Viewer from the MacTech web site (www.mactech.com).

The Main Controller

The PDBMain controller (Listing 12) coordinates all the signal traffic between the other controllers in the PDB Viewer application. It also intercepts any File menu selections and then invokes the appropriate controllers in the appropriate order.

Listing 12. The PDBMain controller (PDBMain.h).

#import "PDBFileIO.h"
#import "PDBHeader.h"
#import "PDBRecList.h"
#import "PDBAppInfo.h"
#import "PDBSortInfo.h"
#import "PDBRecData.h"

@interface PDBMain : NSObject
{
     // public instance outlets
     IBOutlet NSTabView       *pdbPanel;

     IBOutlet PDBFileIO       *pdbFile;
     IBOutlet PDBHeader       *pdbHeader;
     IBOutlet PDBRecList      *pdbRecList;
     IBOutlet PDBAppInfo      *pdbAppInfo;
     IBOutlet PDBSortInfo     *pdbSortInfo;
     IBOutlet PDBRecData      *pdbRecData;
}

// public action methods
- (IBAction)openDoc:(id)sender;
- (IBAction)saveDoc:(id)sender;

// protected accessor methods
- (void) updateViews;
- (void) setPList;
@end

To illustrate the role of PDBMain as a traffic coordinator, I prepared two signal diagrams showing the signal traffic during file-input and file-output. I used different line colors to help differentiate between independent signal flows.

Figure 11 is the signal traffic inside the PDB Viewer when the user selects a PDB file to be viewed by choosing the Open PDB menu option from the File menu. PDBMain receives the signal from the File menu via its openDoc action method. It then calls selectInput from PDBFileIO to start the file selection process.

Once a PDB file has been selected and read, PDBFileIO calls setBuffer from the PDBBuffer to archive the PDB data. PDBFileIO returns control to PDBMain which then calls the updateView methods of each of the following controllers: PDBHeader: PDBRecList, PDBAppInfo, PDBSortInfo, and PDBRecData. Each of these five controllers get their respective data blocks from PDBBuffer via its getBytesAt methods. They then process and display the PDB information in the appropriate UI outlets.


Figure 11. Signal flow when a PDB file is selected for display.

Figure 12 is the signal traffic inside the PDB Viewer when the user exports the PDB data into a plist file by choosing the Save .plist As option from the File menu. PDBMain receives the signal from the File menu via its saveDoc method. It then calls the setPList methods of the following controllers: PDBHeader, PDBRecList, PDBAppInfo and PDBRecData.

These five controllers then encapsulate their respective data blocks as NSDictionary objects. They send their dictionary objects to PDBBuffer via its setPList:forKey method. Once PDBMain regains control, it calls the selectOutput method of PDBFileIO to start the save process.

PDBFileIO prompts the user for a plist filename and destination directory. It then retrieves the consolidated NSDictionary object from PDBFileIO via its getPList method. Finally, PDBFileIO invokes the writeToFile method of the NSDictionary object to save its data into a plist file.


Figure 12. Signal flow when the PDB data is being saved to a plist file.

Data I/O and Buffering

The PDBFileIO controller (Listing 13) handles all the data input and output operations. This controller prompts the user for the PDB file to be viewed. It also prompts the user for a plist file name and destination directory. It reads the PDB data and submits it to the PDBBuffer controller for storage. It also queries the PDBBuffer for the NSDictionary object to be saved as a plist file.

Listing 13. The PDBFileIO controller (PDBFileIO.h).

#import "PDBBuffer.h"

// define the following private constants
#define PDB_nameWithExtension      YES
#define PDB_nameOnly               NO

@interface PDBFileIO : NSObject
{
     // public instance outlets
     IBOutlet PDBBuffer  *pdbBuffer;

     // protected instance properties
     NSString            *srcPath;
}

// public accessor methods
- (void) selectInput;
- (void) selectOutput;

- (NSString *) getName:(BOOL)withExt;
@end

The PDBBuffer controller (Listing 14) manages the data retrieved from a selected PDB file. The controllers for each tab panel retrieve their respective data blocks through the getBytesAt method of the PDBBuffer.

PDBBuffer also manages an NSDictionary object that becomes the basis for the plist representation of the PDB data. The controllers for each tab panel submit their respective NSDictionary objects with the appropriate key values to the PDBBuffer using the setPlist:forKey method. The PDBBuffer then consolidates the NSDictionary objects into a single NSDictionary object for exportation.

Listing 14. The PDBBuffer controller (PDBBuffer.h).

@interface PDBBuffer : NSObject
{
     // protected instance properties
     NSMutableData         *dataBuffer;
     NSMutableDictionary   *dataDict;
}

// public accessor methods
- (NSData *) getBuffer;
- (NSData *) getDataAt:(unsigned int)offset 
      length:(unsigned int)length;
- (NSDictionary *) getPList;

- (void) setBuffer:(NSData *)fileData;
- (unsigned char *) getBytesAt:(unsigned int)offset 
      length:(unsigned int)length;
- (unsigned int) getBufferSize;

// public modifier methods
- (BOOL) setPList:(id)pdbData forKey:(id)pdbKey;
@end

Displaying PDB Information

As mentioned previously, there are 5 controllers that handle the display of data for each tab panel view. These controllers obtain their respective data block by querying PDBBuffer. These controllers also encapsulate their data as NSDictionary objects. They then send their NSDictionary objects to PDBBuffer to be consolidated for exportation.

The PDBHeader controller (Listing 15) handles the processing of data contained in the PDB header block. It displays the processed data in the Header tab panel of the PDB Viewer. The controller also calculates the time offsets needed to correctly display the PDB file's creation, modification and last-backup dates. This correction is necessary because to the different reference years used by the PalmOS and MacOS X.

Listing 15. The PDBHeader controller (PDBHeader.h).

#import "PDB.h"
#import "PDBBuffer.h"
#import "PDBFormatSig.h"

@interface PDBHeader : NSObject
{
     // protected instance outlets
    IBOutlet PDBBuffer        *dataSource;
    IBOutlet PDBFormatSig     *dataFormat;
     
    IBOutlet NSTextField      *fieldAttributes;
    IBOutlet NSTextField      *fieldDateBackup;
    IBOutlet NSTextField      *fieldDateCreate;
    IBOutlet NSTextField      *fieldDateModified;
    IBOutlet NSTextField      *fieldIDAppInfo;
    IBOutlet NSTextField      *fieldIDModified;
    IBOutlet NSTextField      *fieldIDSortInfo;
    IBOutlet NSTextField      *fieldIDUnique;
    IBOutlet NSTextField      *fieldName;
    IBOutlet NSTextField      *fieldSigCreator;
    IBOutlet NSTextField      *fieldSigType;
    IBOutlet NSTextField      *fieldVersion;

    // protected instance property
    NSData              *dataBuffer;
    DatabaseHdrType     *dataPointer;
}

// public modifier methods
- (void) updateView;
- (void) showData;
- (void) setPList;
- (NSDate *) adjustDate:(unsigned long)pdbDate;

// public accessor methods
- (unsigned int) getOffset:(PDBBlockTypes)block;
- (BOOL) hasBlock:(PDBBlockTypes)block;
- (BOOL) getData;
@end

The PDBRecList controller (Listing 16) handles the processing of data contained in the PDB Record List and Record Entry blocks. It displays the processed data in the Record List tab panel of the PDB Viewer. Since that panel happens to have an NSTableView object, the PDBRecList controller also serves as the table's data source. Finally, the controller populates the First Record Entry field if and only if there is at least one valid record in the PDB file.

Listing 16. The PDBRecList controller (PDBRecList.h).

#import "PDB.h"
#import "PDBBuffer.h"
#import "PDBFormatHex.h"

@interface PDBRecList : NSObject
{
     // protected instance outlets
     IBOutlet PDBBuffer       *dataSource;
     IBOutlet PDBFormatHex    *dataFormat;
     
     IBOutlet NSTextField *fieldIDLocal;
     IBOutlet NSTextField *fieldRecordCount;
     IBOutlet NSTextField *fieldRecordFirst;
     IBOutlet NSTableView *tableEntries;
     
     // protected instance properties
     NSData          *dataBuffer;
     NSMutableArray 	*dataRecords;    
     RecordListType 	*dataPointer;
}

// public modifier methods
- (void) updateView;
- (void) showRecordList;

// public accessor methods;
- (BOOL) hasEntries;
- (BOOL) hasRecords;
- (BOOL) getRecordList;
- (BOOL) getRecordEntries;

- (unsigned int) recordCount;
- (unsigned int) firstRecord;
- (unsigned int) getRecordAtIndex:(unsigned int)index;
- (void) setPList;
@end

The PDBAppInfo controller (Listing 17) handles the processing of data contained in the AppInfo block. It displays the processed data in the AppInfo tab panel of the PDB Viewer. Like the PDBRecList controller, it serves as a data source for the NSTableView object in the AppInfo panel. The controller also locates any application-specific data contained in the AppInfo data block. If found, the controller then displays the application-specific data in the appropriate NSTextView object using the PDBFormatData controller to reformat the data.

It should be noted that the PDBAppInfo controller only performs its data process if and only if the PDB data does include an AppInfo block.

Listing 17. The PDBAppInfo controller (PDBAppInfo.h).

#import "PDB.h"
#import "PDBBuffer.h"
#import "PDBHeader.h"
#import "PDBRecList.h"
#import "PDBFormatData.h"
#import "PDBSortInfo.h"

@interface PDBAppInfo : NSObject
{
     // protected instance outlets
    IBOutlet PDBBuffer        *dataSource;
    IBOutlet PDBHeader        *dataHeader;
    IBOutlet PDBRecList       *dataRecList;
    IBOutlet PDBFormatData    *dataStream;
    IBOutlet PDBSortInfo      *dataSortInfo;
    
    IBOutlet NSTextView    *fieldAppInfo;
    IBOutlet NSTextField   *fieldLastUniqueID;
    IBOutlet NSTextField   *fieldRenamedCount;
    IBOutlet NSTextField   *fieldDataLength;
    IBOutlet NSTableView   *tableCategories;
    
    // protected instance properties
    NSData        *dataBuffer, *dataSpecific;
    AppInfoType   *dataPointer;
}

// public modifier methods
- (void) updateView;
- (void) showDataInfo;
- (void) showDataSpecific;
- (void) setPList;

// public accessor methods
- (BOOL) getDataInfo;
- (BOOL) getDataSpecific;
@end

The PDBSortInfo (Listing 18) controller handles the processing of data contained in the SortInfo block. It displays the processed data in the NSTextView object located in the Data tab panel of the PDB Viewer. Like the PDBAppInfo controller, it only performs its data process if and only if the PDB data does include a SortInfo block. The controller also makes use of the PDBFormatData controller to reformat the data in human-readable form.

Listing 18. The PDBSortInfo controller (PDBSortInfo.h).

#import "PDB.h"
#import "PDBBuffer.h"
#import "PDBHeader.h"
#import "PDBRecList.h"
#import "PDBFormatData.h"

@interface PDBSortInfo : NSObject
{
     // protected instance outlets
    IBOutlet PDBBuffer        *dataSource;
    IBOutlet PDBHeader        *dataHeader;
    IBOutlet PDBRecList       *dataRecList;
    IBOutlet PDBFormatData    *dataStream;

    IBOutlet NSTextView    *fieldSortInfo;
    IBOutlet NSTextField   *fieldDataLength;
    
    // protected instance properties
    NSData 	*dataBuffer;
}

// public modifier methods
- (void) updateView;

// public accessor methods
- (BOOL) getData;
- (NSData *) getSortInfo;
@end

The PDBRecData controller (Listing 19) handles the processing of any existing raw record data contained in the PDB file. It determines the location and length of each raw record using the information provided by the PDBRecList controller. It then parses out these records and displays them in the NSTableView object located on the Data panel of the PDB Viewer.

Listing 19. The PDBRecData controller (PDBRecData.h)

#import "PDB.h"
#import "PDBBuffer.h"
#import "PDBRecList.h"
#import "PDBFormatHex.h"
#import "PDBFormatData.h"

@interface PDBRecData : NSObject
{
     // protected instance outlets
     IBOutlet PDBBuffer       *dataSource;
     IBOutlet PDBRecList      *dataRecList;
     IBOutlet PDBFormatHex    *dataFormat;
     IBOutlet PDBFormatData   *dataStream;

     IBOutlet NSTableView 	*tableRecords;

     // protected instance properties
     NSMutableArray     *dataRecords;
}

// public modifier methods
- (void) updateView;
- (void) setPList;

// public accessor methods
- (BOOL) getData;
@end

Data Formatting

There are three controllers subclassed from the NSFormatter object.. First of these is PDBFormatSig (Listing 20). This controller takes the original integer values of the type and creator signatures of the PDB file and converts them into the familiar four-character signatures.

Two of the NSTextFields in the Header tab panel uses PDBFormatSig as their formatter. PDBHeader also uses PDBFormatSig to format the type/creator signatures when preparing the PDB header data for output to a plist file.

Listing 20. The PDBFormatSig controller (PDBFormatSig.h)

@interface PDBFormatSig : NSFormatter
{
}

// public modifier methods
- (NSString *) int2sig:(unsigned int)intArg;
@end

The second controller, PDBFormatHex (Listing 21), takes an unsigned integer value and generates the corresponding hexadecimal string.

Three NSTextFields in the Data tab panel use PDBFormatHex as their formatter. These three fields display the attributes, AppInfo and SortInfo IDs stored in the PDB header block. The NSTextField used to display the nextRecordID value in the Record List tab panel also uses PDBFormatHex as its formatter. Finally, the NSTableViews in both the Record List and Data tab panels use PDBFormatHex to format the values displayed in their Local ID columns.

Listing 21. The PDBFormatHex controller (PDBFormatHex.h)

@interface PDBFormatHex : NSFormatter
{
}

// public modifier methods
- (NSString *) int2hex:(unsigned int)intArg;
@end

The third controller, PDBFormatData (Listing 22), takes an NSData object and converts each byte value into some human-readable form. A byte value of 0x00 is represented by a bullet (*). Byte values within the range of 0x20 and 0x7e are represented by their equivalent ASCII character. Any other byte values are represented by an ellipsis (...).

The PDBFormatData is used by the PDBAppInfo and PDBSortInfo controllers to format any application-specific data prior to being displayed in their respective NSTextViews. PDBRecData controller also uses PDBFormatData to format any raw record data prior to being displayed in the Record Data column of the NSTableView object.

Listing 22. The PDBFormatData controller (PDBFormatData.h)

@interface PDBFormatData : NSFormatter
{
}

// public modifier methods
- (NSString *)data2stream:(NSData *)dataArg;
@end

Possible Improvements

For the purposes of this article, the PDB Viewer only allows me to load and view the data contained inside a PDB file as well as export it to a plist file. There are however, a number of directions I can pursue that would further improve upon the application's usability. Some of the more notable ones are as follows:

  • Add the ability to load and view a plist file containing PDB data and generate a PDB file from the data read. To accomplish this, PDBFileIO needs to correctly recognize that the data being read is coming from a plist file. PDBBuffer will then separate the data into individual blocks (Header, Record List, etc..) and store each block as an NSDictionary entry. Then PDBHeader and its kind will then asks for their respective data blocks to be parsed and displayed accordingly.
  • Use a hex-editor style UI to display any application-specific data from the AppInfo and SortInfo data blocks. One way to accomplish this is to subclass NSTableView. The subclass (let's call it PDBHexTable) would consist of 18 columns: one for the data offsets, one for the ASCII stream and the rest for the hexadecimal values. Also, a controller (PDBHexShow) would be designed to handle the display of data in the PDBHexTable.
  • Allow on-screen editing of PDB data and then save the edited data back into a separate PDB or plist file. The controllers for each of the tab panel views (such as PDBHeader) will have to be modified to support this feature. Also, PDBFileIO will have to be modified to allow users to select the type of file into which the data would be saved.

Summary

I have shown you the basic structure of a PDB file and how information is stored in that file. I have also demonstrated how to use XCode and Cocoa to build a basic PDB Viewer that allows us to view the contents of a PDB file. I was also able to add an additional feature to the PDB Viewer thus allowing me to export the PDB data into a plist file for later viewing and perhaps even editing.

Once again, the complete XCode project for the PDB Viewer is available for downloading at the MacTech website (www.mactech.com).

In my next article, I will cover the basic structure of a Palm Resource file (or PRC). Learning the data structure of the PRC file is an important step towards learning PalmOS development as this is the universal executable format used by a PalmOS application. I will show how to develop a PRC Viewer that would allow me to view the contents of a PRC file as well as save the PRC data into a plist file for later viewing/editing. In addition, I will also show how to allow the PRC Viewer to load the contents of a plist file (which contains PRC data) and then recreate a PRC file.

Bibliography and References

Stone, Denise. "Palm OS (R) File Formats". Exploring Palm OS (R). Document Number 3120-002. 2004 Nov 9. pp. 4 - 12, 14 - 19. PalmSource, Inc.

Wilson, Greg; Ostrem, Jean; Rey, Christopher. Palm OS Programmers (R) Companion, Volume 1. Document Number 3004-008. 2003 Sept 4. PalmSource, Inc.

Rhodes, Neil; McKeehan, Julie. Palm Programming: The Developer's Guide. Copyright 1999. O'Reilly & Associates.


JC is a freelance engineering consultant currently residing in North Vancouver, BC. He divides his time between custom application development for OS X, technical writing and teaching origami to children at the local district libraries.

 
AAPL
$119.00
Apple Inc.
+1.40
MSFT
$47.75
Microsoft Corpora
+0.28
GOOG
$540.37
Google Inc.
-0.71

MacTech Search:
Community Search:

Software Updates via MacUpdate

HoudahSpot 3.9.6 - Advanced file search...
HoudahSpot is a powerful file search tool built upon MacOS X Spotlight. Spotlight unleashed Create detailed queries to locate the exact file you need Narrow down searches. Zero in on files Save... Read more
RapidWeaver 6.0.3 - Create template-base...
RapidWeaver is a next-generation Web design application to help you easily create professional-looking Web sites in minutes. No knowledge of complex code is required, RapidWeaver will take care of... Read more
iPhoto Library Manager 4.1.10 - Manage m...
iPhoto Library Manager lets you organize your photos into multiple iPhoto libraries. Separate your high school and college photos from your latest summer vacation pictures. Or keep some photo... Read more
iExplorer 3.5.1.9 - View and transfer al...
iExplorer is an iPhone browser for Mac lets you view the files on your iOS device. By using a drag and drop interface, you can quickly copy files and folders between your Mac and your iPhone or... Read more
MacUpdate Desktop 6.0.3 - Discover and i...
MacUpdate Desktop 6 brings seamless 1-click installs and version updates to your Mac. With a free MacUpdate account and MacUpdate Desktop 6, Mac users can now install almost any Mac app on macupdate.... Read more
SteerMouse 4.2.2 - Powerful third-party...
SteerMouse is an advanced driver for USB and Bluetooth mice. It also supports Apple Mighty Mouse very well. SteerMouse can assign various functions to buttons that Apple's software does not allow,... Read more
iMazing 1.1 - Complete iOS device manage...
iMazing (was DiskAid) is the ultimate iOS device manager with capabilities far beyond what iTunes offers. With iMazing and your iOS device (iPhone, iPad, or iPod), you can: Copy music to and from... Read more
PopChar X 7.0 - Floating window shows av...
PopChar X helps you get the most out of your font collection. With its crystal-clear interface, PopChar X provides a frustration-free way to access any font's special characters. Expanded... Read more
Carbon Copy Cloner 4.0.3 - Easy-to-use b...
Carbon Copy Cloner backups are better than ordinary backups. Suppose the unthinkable happens while you're under deadline to finish a project: your Mac is unresponsive and all you hear is an ominous,... Read more
ForeverSave 2.1.3 - Universal auto-save...
ForeverSave auto-saves all documents you're working on while simultaneously doing backup versioning in the background. Lost data can be quickly restored at any time. Losing data, caused by... Read more

Latest Forum Discussions

See All

Raby (Games)
Raby 1.0.3 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0.3 (iTunes) Description: ***WARNING - Raby runs on: iPhone 5, iPhone 5C, iPhone 5S, iPhone 6, iPhone 6 Plus, iPad Mini Retina, iPad Mini 3, iPad 4, iPad Air,... | Read more »
Oddworld: Stranger's Wrath (Games)
Oddworld: Stranger's Wrath 1.0 Device: iOS Universal Category: Games Price: $5.99, Version: 1.0 (iTunes) Description: ** PLEASE NOTE: Oddworld Stranger's Wrath requires at least an iPhone 4S, iPad 2, iPad Mini or iPod Touch 5th gen... | Read more »
Bounce On Back (Games)
Bounce On Back 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: | Read more »
Make Way for Fat Chicken, from the Maker...
Make Way for Fat Chicken, from the Makers of Scrap Squad Posted by Jessica Fisher on November 26th, 2014 [ permalink ] Relevant Games has announced they will be releasing their reverse tower defense game, | Read more »
Tripnary Review
Tripnary Review By Jennifer Allen on November 26th, 2014 Our Rating: :: TRAVEL BUCKET LISTiPhone App - Designed for the iPhone, compatible with the iPad Want to create a travel bucket list? Tripnary is a fun way to do exactly that... | Read more »
Ossian Studios’ RPG, The Shadow Sun, is...
Ossian Studios’ RPG, The Shadow Sun, is Now Available for $4.99 Posted by Jessica Fisher on November 26th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Mmmm, Tasty – Having the Angry Birds for...
The very first Angry Birds debuted on iOS back in 2009. When you sit back and tally up the number of Angry Birds games out there and the impact they’ve had on pop culture as a whole, you just need to ask yourself: “How would the birds taste... | Read more »
Rescue Quest Review
Rescue Quest Review By Jennifer Allen on November 26th, 2014 Our Rating: :: PATH BASED MATCH-3Universal App - Designed for iPhone and iPad Guide a wizard to safety by matching gems. Rescue Quest might not be an entirely original... | Read more »
You Can Play the Final Chapter of Lone W...
You Can Play the Final Chapter of Lone Wolf: Dawn Over V’taag Right Now Posted by Jessica Fisher on November 26th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Swords of Anima (Games)
Swords of Anima 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: A new tactical turn-based RPG experience. Command the Savior Rex Squad in an epic journey of courage and deception. Can you... | Read more »

Price Scanner via MacPrices.net

2014 1.4GHz Mac mini on sale for $449, save $...
 B&H Photo has the new 1.4GHz Mac mini on sale for $449.99 including free shipping plus NY tax only. Their price is $50 off MSRP, and it’s the lowest price available for this new model. Adorama... Read more
Early Black Friday pricing on 27-inch 5K iMac...
 B&H Photo continues to offer Black Friday sale prices on the 27″ 3.5GHz 5K iMac, in stock today and on sale for $2299 including free shipping plus NY sales tax only. Their price is $200 off MSRP... Read more
Early Black Friday sale prices on iPad Air 2,...
 MacMall is discounting iPad Air 2s by up to $75 off MSRP as part of their Black Friday sale. Shipping is free: - 16GB iPad Air WiFi: $459 $40 off - 64GB iPad Air WiFi: $559 $40 off - 128GB iPad Air... Read more
Early Black Friday MacBook Air sale prices, $...
 MacMall has posted early Black Friday MacBook Air sale prices. Save $101 on all models for a limited time: - 11″ 1.4GHz/128GB MacBook Air: $798 - 11″ 1.4GHz/256GB MacBook Air: $998 - 13″ 1.4GHz/... Read more
Why iPhone 6 Tablet/Laptop Cannibalization Is...
247wallst.com blogger Douglas A. McIntyre noted last week that according to research posted on the Applovin blog site the iPhone 6 is outselling the iPhone 6 Plus by a wide margin . Hardly a surprise... Read more
Worldwide Tablet Growth Expected to Slow to 7...
The global tablet market is expected to record massive deceleration in 2014 with year-over-year growth slowing to 7.2%, down from 52.5% in 2013, according to a new forecast from International Data... Read more
Touchscreen Glove Company Announces New Produ...
Surrey, United Kingdom based TouchAbility specializes in design and manufacture of a wide variety of products compatible with touchscreen devices including smartphones, tablets and computers. Their... Read more
OtterBox Alpha Glass Screen Protectors for iP...
To complement the bigger, sharper displays on the latest Apple devices, OtterBox has introduced Alpha Glass screen protectors to the iPhone 6 and iPhone 6 Plus. The fortified glass screen protectors... Read more
Early Black Friday Mac Pro sale, 6-Core 3.5GH...
 B&H Photo has the 6-Core 3.5GHz Mac Pro on sale today for $3499 including free shipping plus NY sales tax. Their price is $500 off MSRP, and it’s the lowest price available for this model from... Read more
Early Black Friday sale price: 15-inch 2.2GHz...
 B&H Photo has the 2014 15″ 2.2GHz Retina MacBook Pro on sale today for $1699.99. Shipping is free, and B&H charges NY sales tax only. Their price is $300 off MSRP, equalling Best Buy’s price... Read more

Jobs Board

*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
Senior Event Manager, *Apple* Retail Market...
…This senior level position is responsible for leading and imagining the Apple Retail Team's global event strategy. Delivering an overarching brand story; in-store, Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.