Palmpilot Timesheet Database Format, Version 1.5. |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
IntroductionThis document describes the internal database format used by the Palmpilot Timesheet application, version 1.5.1. This is the database held on your Palmpilot that gets backed up to the TimesheetDB.PDB file every time you Hotsync.For details about the PalmPilot .PDB file format, see the RoadCoders site articles. (Or just Google for it)! This document only describes the latest version (1.5) of the Timesheet database. This format will change in the future. In fact, I use the minor revision number to indicate the database format has changed, so version 1.4 and version 1.5 of Timesheet used slightly different databases. I will keep this document up to date as I release new versions, so it will always describe the latest database format. I've published this information for any programmers out there that are interested in developing their own utilities for Timesheet.
The First Four RecordsThe first four records of a Timesheet database are special. They are used to hold preference and category information used by the application itself and for the actual timesheet entry records. Some of this information should really be held in the Palmpilot preference database, but I had some problems using preference records under the GCC port and resorted to just storing prefs in the normal database.These first four records are guaranteed to exist if the Timesheet database does. So an 'empty' Timesheet database (one with no entries) will actually contain these four records. Record 0 - The Prefence RecordThe first record in the database holds the Timesheet preferences as well as some Client/Project/Task category information and at least one persistent timer record.Here's a byte-based description of the preferences record.
For 'C' programmers, here's the /****************************************************************** * Struct used to hold (and store) application internal data over * multiple app executions. ******************************************************************/ typedef struct { /* The number of entries in each category (clients/projects/tasks). */ Byte numCatEntries[3]; /* Preference related */ Byte newCatEntryIdx[3]; Byte prefFlags; Byte newCatHours; /* Following this point in the record, one or more TSEntryTimerType structures may appear, * one for each running timer (v1.5 currently only supports a single timer) */ } TSAppPrefType; /****************************************************************** * Struct used to hold (and store) entry timers at the end of the preferences * record. Hmmm, they probably shouldn't really go there (are they preferences?) * but doing it this way saves having to alter the database format by inserting new * records. ******************************************************************/ typedef struct { /* Entry timer related */ UInt recIdx; /* index of entry record we're timing. 0 if timing new entry. */ ULong secs; /* entry seconds since 1970. > 0 if we're actually timing. */ } TSEntryTimerType; /* Preference flag masks (1 bit per flag) */ #define TSPrefShortDateFlag (0x01) /* use system short date format instead of long */ #define TSPrefUlineClientFlag (0x02) /* underline chargeable clients */ #define TSPrefAutoCatsFlag (0x04) /* use auto categories for new entries */ #define TSPrefAutoDurFlag (0x08) /* use auto durations for new entries */ #define TSPrefDefaultChargeFlag (0x10) /* new entries are chargeable by default */ #define TSPrefAlwaysTimeFlag (0x20) /* when timing entries, keep timing when OK tapped */ #define TSPrefUNUSED1Flag (0x40) /* CURRENTLY UNUSED */ #define TSPrefUNUSED2Flag (0x80) /* CURRENTLY UNUSED */ /* Default prefs for new database */ #define TSPrefDefaultFlags (TSPrefUlineClientFlag) Records 1-3 - The Category RecordsFollowing the preferences record there are 3 category records that contain the information for the Client, Project and Task categories (in that order). These records all use the same format, but naturally contain different data for the appropriate category type. They will also be of different lengths depending on the number of categories that exist for the appropriate category type. Here's a byte-based description of the category record format.
The category names within a given category record are order alphabetically, except for the first category name which will always be 'none' and the last category name which will always be 'Edit...' (or 'Edit Clients...', 'Edit Projects...' etc). Existing category names appear at the beginning of the category record. Instead of holding a lot of empty category names, the record will simply end after the last category name. As new category names are added the record will expand to a maximum of 1938 bytes. The first 102 bytes of the category record contain the category translation table. This is a lookup table that provides an extra level of 'indirection' between the category names and the category indexes. This translation table is used to allow category editing without having to change all the corresponding entry records. The actual timesheet entry records (see below) contain indexes into the translation table which is used to provide an actual category index.
E.g.
For 'C' programmers, here's the /****************************************************************** * Struct used to hold Client, Project and Task category lists. ******************************************************************/ /* The maximum number of client, projects or task entries allowed, * including the the 2 automatic entries of 'none' and 'Edit...' */ #define TSMaxCatEntries (102) /* The length of each client, project or task list entry. */ #define TSMaxCatEntryLen (17 + 1) typedef struct { /* The translation table for the entries */ Byte transTable[TSMaxCatEntries]; /* The text of the actual entries, will always contain at least two automatic entries, * 'none' and 'Edit...' * Note *all* entries appear in this struct, but the actual record will only * containing the existing entries in low indexes. */ Char cats[TSMaxCatEntries][TSMaxCatEntryLen]; } TSCatRecType; The Entry RecordsTimesheet Duration FormatTimesheet stores entry durations in a single byte using the following format./****************************************************************** * Struct used to hold (and store) day records. ******************************************************************/ typedef struct { /* Number of entries in this day */ Byte numEntries; /* Date this day record relates to. */ DateType dayDate; } TSDayRecType; /****************************************************************** * Struct used to hold (and store) entry records. ******************************************************************/ typedef struct { /* Which client does this record relate to. */ Byte clientIdx; /* Which project does this record relate to. */ Byte projectIdx; /* Which task does this record relate to. */ Byte taskIdx; /* How many hours does this record use. */ Byte hours; /* Entry number of this entry within the current day (i.e. first entry in day = 1). * Used during binary searching of Timesheet database. * Must be updated each time a record is DELETED from day. */ Byte entryNum; /* Filler for future possible expansion */ Byte filler; /* Note there's no 'chargeable' variable. This because 'chargeable' is indicated by * setting the highest bit in the record category (part of attributes) on. */ /* Following the END of the structure is the first character of descriptive text * for to this record. */ } TSEntryRecType; Database Version HistoryHere's a brief history of the changes the Timesheet database has been through.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|