macdevcenter.com
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Programming With Cocoa
Inside StYNCies, Part 2

by Matthew Russell
03/18/2005

The first installment of this dynamic duo developed a user interface that lives up in your menubar like the system clock. This piece finishes off the series by reverse-engineering the storage format of the StickiesDatabase file to develop your own API to Stickies and to finish off your project for syncing Stickies with your iPod.

A Quick Word About Reverse-Engineering

In recent times, a "hack" has been redefined as a clever solution to a problem. Dictionary.com, going in a different direction, defines a person who hacks as "One who uses programming skills to gain illegal access to a computer network or file." Let's be clear that this is something we're definitely not doing.

We're simply reverse-engineering a fairly well known file format in order to provide some interoperability. The file format is unencrypted, and its data could actually be parsed out using regular expressions, but that would be very crude and there wouldn't be a lot to learn. So instead, we'll use some standard Cocoa tools to develop a nice API.

Before you ever reverse-engineer any file format or unpublished specification, you must realize that reverse-engineering is a double- edged sword: On the one side, it gives you the ability to exploit the utility of an application to provide exciting interoperability features with other apps that aren't currently there. On the other side, the next operating system update or release might provide an update that breaks your API, and so you have to track it down and fix it. Being mindful of these things, let's have some fun.

Essential Tools

The Rosetta Stone provided the key to Egyptian hieroglyphics back in 1799. We don't have a Rosetta Stone, but we do have some other handy tools and clues for unscrambling the StickiesDatabase file format. Namely, our trusty text editor VIM, the class-dump utility, and some of Apple's developer documentation.

If you don't have class-dump installed already (type "class-dump" in Terminal to check), you can download it from here. VIM comes packaged with OS X, and Apple's developer documentation is easily found in Xcode's help menu. Take a moment to preview the usage info for class-dump as well as Apple's documentation for NSUnarchiver before moving on.

You'll need to backup the StickiesDatabase file found in your ~/Library/ directory for reasons you'll understand in just a moment. In Terminal, you can type "mv StickiesDatabase StickiesDatabase.bak" to rename it. When you open up Stickies again, you'll see the three default notes. Close two of them without saving and leave only a few words of text in the remaining note. Close Stickies. With VIM or another text editor, open up the "new" StickiesDatabase file and look at it. Having a very simple file to work with makes the entire reverse-engineering process much easier than dealing with multiple notes with all sorts of things embedded in them.

StickiesDatabase in VIM The StickiesDatabase file in VIM. The database consists of a single yellow sticky note with the text "A FEW WORDS" written on it.

Some Detective Work

From peeking at your StickiesDatabase file, it's obvious that it's not a textual plist file (type "man plist" in Terminal for more info on the plist format). If you've done any Cocoa programming before, you know that NSUnarchiver and its new counterpart NSKeyedUnarchiver are the standard Cocoa ways of reading data from files, so these two classes are good places to start.

NSArchiver and NSUnarchiver make it really easy to write and read data to disk. Each class that is to be archived simply implements the NSCoding protocol. Objects that take care of this detail can be stored individually to disk or in other archivable data structures that also implement the protocol. If you were to guess, how would you think multiple sticky notes would be stored to disk? NSMutableArray sure does seem plausible. Remember seeing the words "NSMutableArray" as plain text in your StickiesDatabase file? Knowing that NSArray inherits from NSMutableArray, and NSMutableArray inherits from NSObject, look again at StickiesDatabase in VIM, and take a stab about the structure of the file format.

From reading the documentation, you know that unarchiving data with NSUnarchiver requires calling its unarchiveObjectWithFile: method on an object whose constituent subparts are also unarchivable. So if you were to unarchive a file such as ~/Library/StickiesDatabase containing an NSMutableArray filled with archivable objects, you'd simply do it as so:

NSMutableArray *array = 
        [[NSMutableArray alloc] initWithArray:
                [NSUnarchiver unarchiveObjectWithFile:
                        [@"~/Library/StickiesDatabase" 
                                stringByExpandingTildeInPath]]];

Again, in order for unarchiveObjectWithFile: to be successful, all of the objects encountered during the unarchive process have to actually exist, be of the correct form, and implement the NSCoding protocol. To be of the correct form, the objects must have the correct variables declared in their interface. Sound like a chore? Well, class-dump helps a lot.

Inside the Stickies Application Bundle, there's a Mach-O executable. Running class-dump on it provides all of the interface details we need. In Terminal, type "cd /Applications/Stickies.app/Contents/MacOS" and you'll find the Stickies executable there by typing "ls" to get a directory listing. Run class dump on it and save the output to a file on your desktop like so: "class-dump Stickies > ~/Desktop/Stickies.dump". Take a moment to look at the output with VIM or TextEdit. It's pretty neat. The part we're most interested in is inline below.

//Part of the output from running class-dump
//on the Stickies Mach-O executable.

@interface Document : NSObject <NSCoding>
{
    int mWindowColor;
    int mWindowFlags;
    struct _NSRect mWindowFrame;
    NSData *mRTFDData;
    NSDate *mCreationDate;
    NSDate *mModificationDate;
}

- (id)init;
- (void)dealloc;
- (id)initWithCoder:(id)fp8;
- (id)initWithData:(id)fp8;
- (void)encodeWithCoder:(id)fp8;
- (id)creationDate;
- (void)setCreationDate:(id)fp8;
- (id)modificationDate;
- (void)setModificationDate:(id)fp8;
- (id)RTFDData;
- (void)setRTFDData:(id)fp8;
- (int)windowColor;
- (void)setWindowColor:(int)fp8;
- (int)windowFlags;
- (void)setWindowFlags:(int)fp8;
- (struct _NSRect)windowFrame;
- (void)setWindowFrame:(struct _NSRect)fp8;

@end

There are a couple of things worth mentioning at this point. One is that there's a lot of user interface stuff that goes into any nice Cocoa application; Stickies is no exception, despite its simplicity. Another thing is that the Document class is the only one that implements the NSCoding protocol. This is good news for us because this interface is all that we need to implement in order to unravel the mystery of the StickiesDatabase file. Do you remember seeing the word "Document" in the StickiesDatabase file? By this point, you've most likely realized that NSArchived files contain plain text names for all of their archived classes. The data in these classes, however, is another story, as we will see.

Pages: 1, 2

Next Pagearrow