20) PDBファイルの構造<その1>
PDBファイルは、『Palm Data Base』の略なのですが、一般的にデータベースといわれているものに比べると、それほど複雑ではありません。
したがって、これらの構造がわかれば、VBなどでも比較的簡単に、PDBファイルを作成できるでしょう。
今回は、最も簡単なPDBファイルの構造を解析してみたいと思いますが、この単純なファイルが理解できれば、後の応用は、それほど難しくないと思います。

●まずは下準備
最も簡単な構造のデータベースといえば、NS Basicで言うところの、キーを使わないデータベースです。
これらは、DbPut()や、DbGet()によってアクセスできるモノです。
今回は、次のようなコードで作成されるデータベースをサンプルに考えてみましょう。
    Dim Db as Database
    Dim res as Integer

    res=DbCreate(Db,"DB-CREATE-TEST",0,"Test")

    If res=0 then

        res=DbOpen(Db,"DB-CREATE-TEST",0)

        res=DbPosition(Db,1,0)
        res=DbPut(Db,"NS BASIC")

        res=DbPosition(Db,2,0)
        res=DbPut(Db,"mizuno-ami")

        res=DbPosition(Db,3,0)
        res=DbPut(Db,"Simple Sample")

        res=DbClose(Db)
    End if
一方、読み込みについては、こちらのコードです。
Field1006に、レコード番号を入力して、Field1008にその内容を表示するコードです。
    Dim Db as Database
    Dim res as Integer
    Dim intSet as Integer
    Dim strData as String

    intSet=Val(Field1006.Text)

    res=DbOpen(Db,"DB-CREATE-TEST",0)

    res=DbPosition(Db,intSet,0)
    res=DbGet(Db,strData)

    res=DbClose(Db)

    Field1008.text=strData
NS Basicで、これらのコードを使って、データベースを作りましょう。
それぞれを適当なボタンのイベントモジュールに記述するだけなので簡単ですね。
ちなみに、読み込みコードの必要な2つのフィールド名は、適当に変更して使いましょう。

これをコンパイルし、HotSync後、Palm上で実行すれば、Palm上にデータベースが作成されているはずです。
一応、1レコード目くらいは確認しておきましょう。
1を入れて『NS BASIC』が表示されればOKです。



作成が確認できたら、もう1度HotSyncして下さい。
完了すると、PC上のBackupフォルダに『DB-CREATE-TEST.pdb』というファイルがあるはずです。
これが、目的とするデータベースファイルです。

さて、PC上で、これらのデータを閲覧するために、適当なバイナリエディタを用意して下さい。
単にダンプリストが表示できれば問題ありませんので、フリーの簡単なものでよいと思います。




●データベースの構造:ヘッダ情報
データを閲覧しながら、簡単にデータベースの構造を紹介していきましょう。
これは、Web上の様々なドキュメントで述べられているので、あまり詳しく書く必要はないと思いますが、おおよそ次のような3つの部分から構成されています。

  • ヘッダ
  • ロケーション情報
  • 実際のレコードデータ

必要な場合、アプリケーション情報などを含ませますが、今回のデータベースには含まれません。

まず、ヘッダ情報を見ていきます。
これは、78バイトのデータブロックになっており、その構造は以下の通りです。
オフセットサイズ
(バイト)
名前説明
+0000H32データベース名
Database Name
データベースの名前(PC上のファイル名とは違う)
+0020H2フラグ
flags
このファイルの属性
+0022H2バージョン
version
バージョン情報
+0024H4作成日
create time
1904年1月1日 0:00からの経過秒
+0028H4更新日
modified time
+002CH4バックアップ日
backup time
+0030H4更新回数
modified number
更新された回数
+0034H4アプリケーション情報サイズ
application info size
情報がない場合は、00 00 00 00
+0038H4ソート情報サイズ
sort info size
+003CH4タイプ
type
データベースだと、data
+0040H4クリエータID
creatorID
クリエータID
+0044H4unique id seedユニーク番号(気にしない)
+0048H4next record list次のレコード番号(気にしない)
+004CH2レコード数
number of records
レコード数
いくつか知っているキーワードがあるようなので、データを覗きながら、説明することにしましょう。

まず、データベース名は、先頭から32バイトになります。エディタを見る限りでは、確かに、収められているようですね。
ただ、その32バイトの領域中に指定していないデータ「ANL」などの文字があります。
実は、データの末端を「00H」というコードで判別していますので、最初に「00H」があるところまでしか読み込まれません。
確認してみると、確かに「DB-CREATE-TEST」の後ろ、オフセットで+0EHの部分に「00H」が書き込まれています。
ということで、正確には31文字 +「00H」が正解、ということになります。



次の2バイトは、データベースの属性をあらわすフラグですが、ここに書き込まれているのは「0008H」です。詳しい意味は色々あるようですが、実用的にはこの値でOKです。
これは「HotSync時に、PCにバックアップをする」という意味です。

その次の2バイトはバージョン番号です。これを見る限り「0000H」ですから、新規の場合には「0000H」にすればよいのでしょう

次の時間に関する値は、1904/01/01 00:00:00 からの経過秒が4バイトで収められています。
16進数なので、一目見ただけでは、よくわかりませんが、とりあえず、バックアップ時間は「00 00 00 00」でよさそうです。



次の更新回数は、変更した回数に応じて、値が増えるものらしいですが、新規に作る場合は、あまり気にしなくてもよさそうです。

アプリケーション情報サイズは「00 00 00 00」ですが、これは、ここにその情報がないことを表します。
同じくソート情報サイズも「00 00 00 00」で、同じくソート情報も持っていないことを表しています。
簡単なデータベースでは、これらの値は必要ありません。

さて、わかりやすいデータ『タイプ』が現れました。
これは、その名前のとおり、データベースファイルの型を表すもので、DbCreate()で作られるデータベースは、「data」型になります。
続いて、お馴染みの『クリエータID』です。
これは、書き込んだ「Test」がそのまま収められていますね。



次の8バイトは、ほとんど意味のないものです。忘れましょう(笑)

そして、最後の2バイトがレコード数です。
書き込んだデータは3つですから、ここには「00 03」と書き込まれています。
レコード数が2バイト、ということは、理論上、65535レコードが収められることを表していますね。



以上が、78バイトのヘッダ情報です。
表の赤く色付けされた部分が、比較的重要な項目で、その他は、忘れてしまっても十分だと思われます。


●データベースの構造:ロケーション情報
さて、このヘッダ情報に続くのがロケーション情報と、実際のデータです。
このロケーション情報と実際のデータは、1:1の関係になっていますので、レコード数分だけ、つまり、この例では3つのロケーション情報と、3つのデータが存在します。



まず、ロケーション情報を見てみましょう。

1件のロケーション情報は、次のような8バイトのデータです。

オフセットサイズ
(バイト)
名前説明
+0000H4オフセット
offset
そのレコードの先頭からの開始位置
+0004H1属性
attributes
そのレコードの属性
+0005H3ユニークID
uniqueID
そのレコードの連番

これは、各データに対する見出し(Index)と考えておけばよいでしょう。
最初の4バイトのオフセットは、各データの先頭位置を表しています。
要するに、先頭何バイト目からデータが始まっていますよ、ということを表しているもので、例えば、1レコード目「NS BASIC」は、先頭から「0068H」から始まっていますが、ロケーション情報のオフセットも「00 00 00 68」と確かにその値になっています。
同様に、2レコード目、3レコード目も確認してみましょう。確かに、そうなっていますね?



次の属性の1バイトは、ここでは「40H」と覚えておいて下さい。
PDBでは、データベース全体の属性に加え、レコード毎に属性を持つことが出来ます。
HotSync時に、必要なレコード情報だけ同期できるように工夫された作りじゃないかと思います。

そして、最後のユニークIDですが、これは、各レコードにユニークな番号を割り振るためのものです。
確かに、連番になっていますが「1E 90 01」、「1E 90 02」の「1E 90」はどこから来たのでしょうか?
実は、これは考えなくても良いデータで、PalmがHotSync時などに適当に割り振ってくれます。
したがって、新たに作る場合は「00 00 00」を収めておけば問題なく動作します。

少々乱暴ですが、ロケーション情報の8バイトで重要なのはオフセットの4バイトだけと覚えておいても間違いではないでしょう。


●データベースの構造:実データ
レコード数から、ロケーション情報全体のサイズを知ることが出来ます。
各レコードのロケーション情報が8バイトですから、単純に掛け算をすればよいことになります。
ロケーション情報全体のサイズを知って、何がわかるのでしょうか?
実は、これが、実データの先頭位置を知ることになります。
ヘッダ情報は、78バイトで、この例では3レコードですから、3×8バイト=24バイトがロケーション情報の大きさになります。
したがって、78+24=102 -> 066H が実レコードの先頭位置です。

しかし、この例を見てみると、実際のデータの先頭位置は、068Hで、計算とは2バイトのずれがありますね。
実は、CPUは自分が扱いやすいようにデータの位置を変えることがあります。これをアライメントと呼びますが、それがこの2バイトだと思われます。
これは、ある意味、無用の空間ですので、自分で作る場合はあまり気にしなくても良いのですが、コンピュータ側に作成させると、このようなことが起きる可能性があることは知っておきましょう。

少々乱暴ですが、バイナリエディタで、この2バイトを削除して、この2バイトが無効な空間であることを試して見ましょう。
余分と思われる2バイトを削除して、各オフセット情報を書替えます。

Before:


After:


これをHotSyncして確認しても、無事に動作するはずです。
ということで、PDBの構造は、ヘッダ+ロケーション情報、とその直後に実データがある、と理解すれば良くなりました。

各レコードの先頭位置は、ロケーション情報から読み取ることが出来ます。
一方、各レコードデータの最後はどうなっているんでしょうか。ちょっと、各レコード間に注目してみましょう。
ここには、必ず「00 00」が書かれています。これが、レコードの区切りですが、実際は「00」が終端を表すコードで、もう1つの「00」がレコードの終端を表しているようです。



ただし、将来において、この仕様は変更になることが考えられますので、ロケーション情報が、先頭の位置を表していることだけに注意をしておくのが良いでしょう。


以上のように、とても簡単ですが、PDBファイルの中身を解析してみました。
NS Basic側で数値変数を使ったときなどは、どうなるのか、また、キー付きデータベースの場合はどうなるのか?
基本的な内容と解析方法については、あまり変りません。バイナリエディタを駆使すれば、それほど難しいことではないでしょう。
また、ここまでのことが理解できれば、比較的簡単にPC側からPDBファイルを作成できるのではないでしょうか?
是非、試してみてください。