Basics of Palm databases
Palm devices use two types of database files: PRC and PDB (i.e., .prc and .pdb). PRC is used for Palm OS databases that are application programs, and stores resources of the application, while the PDB (Pilot Database) is used for Palm OS databases that contain only data. Both databases have a unique key that is unique for each application. These databases can be manipulated like stream reading and stream writing. It contains an attribute bit called the Backup Bit. Setting this bit indicates that no custom conduit will be backing up the database and that the database should be backed up during the HotSync process. If you create a database on the Palm Pilot and set the Backup Bit, you will find a copy of the database in PDB format in the Backup directory on the computer with which the HotSync was performed.
This application reads a CSV or an XML file and creates a .pdb file.
Major Sections of the PDB File
A PDB file format contains three sections:
- Header section
- Recordlist section
- Datalist section
i. Header Section
The header section contains 78 bytes, containing the name of the database and other information. Also, contained is the ID of the database that is unique for an application (the ID is globally unique, and can be obtained from the Palm OS site), the type of the database, and other attributes. The PDBHeader
class is used to create headers.
class PDBHeader
{
#region Declarations
char[] databaseName={'\0','\0','\0','\0','\0','\0','\0',
'\0','\0','\0','\0','\0','\0','\0','\0',
'\0','\0','\0','\0','\0','\0','\0','\0',
'\0','\0','\0','\0','\0','\0','\0',
'\0','\0'};
UInt16 fileAttributes = 0X0008;
UInt16 version = 0X0000;
UInt32 creationDate;
UInt32 modificationDate;
UInt32 lastBackupDate;
UInt32 modification=0X0000;
UInt32 appInfoArea=0X0000;
UInt32 sortInfoArea=0X0000;
char[] type={'d','a','t','a'};
char[] creatorID;
UInt32 uniqueID=0X0000;
UInt32 nextRecord=0X0000;
UInt16 numRecords;
#endregion
#region Properties
public UInt16 NumRecords
{
set
{
numRecords=value;
}
get
{
return numRecords;
}
}
public DateTime CreationDate
{
set
{
TimeSpan ts=DateTime.Now-value;
creationDate=(UInt32)ts.TotalSeconds;
}
}
public DateTime ModificationDate
{
set
{
TimeSpan ts=DateTime.Now-value;
modificationDate=(UInt32)ts.TotalSeconds;
}
}
public DateTime LastBackupDate
{
set
{
TimeSpan ts=DateTime.Now-value;
lastBackupDate=(UInt32)ts.TotalSeconds;
}
}
public string CreatorID
{
set
{
if (value.Length==4)
{
creatorID=value.ToCharArray();
}
else
{
throw new PDBException("Creator ID" +
" is invalid (must be 4 charactors)");;
}
}
get
{
return new String(creatorID);
}
}
public string DataBaseName
{
set
{
if (value.Length<=32)
{
databaseName=value.ToCharArray();
}
else
{
throw new PDBException("Database Name" +
" Exceeds 32 Charactors");;
}
}
get
{
return new String(databaseName);
}
}
#endregion
#region Functions
public PDBHeader()
{
TimeSpan ts=
DateTime.Now-DateTime.Parse("01-01-1904");
creationDate=(UInt32)ts.TotalSeconds;
modificationDate=(UInt32)ts.TotalSeconds;
lastBackupDate=(UInt32)ts.TotalSeconds;
numRecords=0;
}
public override string ToString()
{
string getAll="";
getAll+=HexEncoding.GetStringToChar(databaseName,32);
getAll+=fileAttributes.ToString("X4");
getAll+=version.ToString("X4");
getAll+=creationDate.ToString("X8");
getAll+=modificationDate.ToString("X8");
getAll+=lastBackupDate.ToString("X8");
getAll+=modification.ToString("X8");
getAll+=appInfoArea.ToString("X8");
getAll+=sortInfoArea.ToString("X8");
getAll+=HexEncoding.GetStringToChar(type,4);
getAll+=HexEncoding.GetStringToChar(creatorID,4);
getAll+=uniqueID.ToString("X8");
getAll+=nextRecord.ToString("X8");
getAll+=numRecords.ToString("X4");
return getAll;
}
ii. RecordList Section
The size of the recordlist section depends on the number of records. Each record uses 8 bits to store record information. The PDBRecordList
class is used to represent a recordlist.
class PDBRecordList
{
#region Declarations
UInt32 offset=0;
byte recordAttribute=0X40;
char[] uniqueID={'\0','\0','\0'};
string dataRecord="";
#endregion
#region Properties
public string DataRecord
{
set
{
dataRecord=value;
}
get
{
return dataRecord;
}
}
public UInt32 RecordOffset
{
set
{
offset=value;
}
get
{
return offset;
}
}
#endregion
#region Functions
public PDBRecordList(string str)
{
dataRecord=str;
}
public override string ToString()
{
string getAll="";
getAll+=offset.ToString("X8");
getAll+=recordAttribute.ToString("X2");
getAll+=HexEncoding.GetStringToChar(uniqueID,3);
return getAll;
}
#endregion
}
iii. Data Section
It is the main section to store information related to the records. Each record is separated by a null terminated character ('\0'). The size of each record may vary but the number of columns should be the same.
Using the code
The PDBCreators
class is used to create databases.
class PDBCreators
{
PDBHeader header=new PDBHeader();
ArrayList pdbRecord=new ArrayList();
public PDBCreators()
{
}
public PDBHeader PDBHeaders
{
set
{
header=value;
}
get
{
return header;
}
}
public Object[] AddRecord
{
set
{
string row="";
for (int i=0; i<value.Length; i++)
{
if (value[i].GetType().ToString()=="System.Int32")
continue;
row+=value[i].ToString().Trim()+'\0';
}
pdbRecord.Add(new PDBRecordList(row));
}
}
public void GeneratePDB(string fileName)
{
try
{
FileStream fs=new FileStream(fileName,FileMode.Create);
BinaryWriter bw=new BinaryWriter(fs);
int discard=0;
UInt32 index=78;
header.NumRecords=(UInt16)pdbRecord.Count;
bw.Write(HexEncoding.GetBytes(header.ToString(),
out discard));
index+=(UInt32)(pdbRecord.Count*8)+2;
string result="";
for (int i=0; ipdbRecord.Count; i++)
{
PDBRecordList rec=(PDBRecordList)pdbRecord[i];
rec.RecordOffset=index;
bw.Write(HexEncoding.GetBytes(rec.ToString(),
out discard));
index+=(UInt32)rec.DataRecord.Length;
result+=rec.DataRecord;
}
char[] padding={'\0','\0'};
bw.Write(HexEncoding.GetBytes(
HexEncoding.GetStringToChar(padding,2),
out discard));
result=HexEncoding.GetStringToChar(
result.ToCharArray(),result.Length);
bw.Write(HexEncoding.GetBytes(result,out discard));
bw.Close();
fs.Close();
}
catch (Exception exc)
{
throw new PDBException("Error" +
" in writing pdb file "+exc.Message);
}
}
}
The PDBGenerator
reads two file formats (CSV and XML) and displays data in table format in a DataGrid
. This data can be changed. It reads all table data from the XML file and displays table names in a combo box.
PDBCreators pdb=new PDBCreators();
pdb.PDBHeaders.DataBaseName=this.txtDBName.Text;
pdb.PDBHeaders.CreatorID=this.txtCreatorID.Text;
DataTable recordsTable;
if (recordsDataSet.Tables.Count>0 &&
this.cmbTableNames.Items.Count>0 &&
recordsDataSet.Tables.Contains(this.cmbTableNames.Text))
recordsTable=recordsDataSet.Tables[this.cmbTableNames.Text];
else
{
MessageBox.Show("There is no table to convert",
"PDB Generator",MessageBoxButtons.OK,
MessageBoxIcon.Information);
return;
}
for (int i=0; i<recordsTable.Rows.Count; i++)
pdb.AddRecord=recordsTable.Rows[i].ItemArray;
try
{
pdb.GeneratePDB(this.txtDestination.Text+"\\"+
this.txtDBName.Text+".pdb");
MessageBox.Show("PDB Generated Successfully",
"PDB Generator",MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
catch(Exception exc)
{
MessageBox.Show("Error in PDB Generation"+exc.ToString(),
"PDB Generator",MessageBoxButtons.OK,
MessageBoxIcon.Information);
}