文字列テーブルのデータ構造

文字列テーブルから文字列を取り出すときには対になるラベルを用います。
C++ の場合には enum を使って実現するのが効率が良いと思われます。

ラベル 日本語 英語
STR_HELLO こんにちは。 Hello.
STR_BYE さようなら。 Good bye.

このテーブルから

enum {
  STR_HELLO, //!< こんにちは。
  STR_BYE    //!< さようなら。
};

このような列挙の定義を生成することにします。


実際のデータ構造は、文字列データへのポインタ配列を使用します。
イメージ的には以下のようになります。

     +--------------------+---
  +--|文字列#0へのポインタ|
  |  +--------------------+ 文字列ポインタ配列
+-|--|文字列#1へのポインタ|
| |  +--------------------+---
| +->|こんにちは。\0      |
|    +--------------------+ 文字列データブロック
+--->|さようなら。\0      |
     +--------------------+---

この構造にヘッダ部分をくっつけて、文字列テーブルデータ構造を作ります。
ヘッダ部分には、文字列テーブルデータに関する情報、文字列データのサイズなどを含めます。

/*
+--------------+
|  StringTable |
+--------------+
|ポインタ配列部|
+--------------+
|文字列データ部|
+--------------+
*/
struct StringTable {
  char  signature[4];   //!< 文字列テーブルデータをあらわす識別子
  ulong version;        //!< バージョンナンバー
  ulong indexOffset;    //!< インデックス(ポインタ配列)のデータ先頭からのバイトオフセット
  ulong indexCount;     //!< インデックスの要素数
  ulong stringOffset;   //!< 文字列データのデータ先頭からのバイトオフセット
  ulong stringDataSize; //!< 文字列データブロックのバイトサイズ
};

識別子をつけておくと、読み込んだデータが処理対象として正しいかどうかのチェックが出来ます。
バージョンナンバーを用意することで、仕様変更があった場合に古いデータにも対応することが出来ます。
ポインタ配列部はデータの先頭から文字列データ部に含まれる各文字列データの先頭の文字までの
バイトオフセットの配列になります。
データ具体例は以下の通りです。

|<----       32 byte       ---->|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|S|T|B|L|      1|     24|      2| signature, version, indexOffset, indexCount,
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     32|     26|     32|     45| stringOffset, stringDataSize, 文字列 #0 オフセット、 文字列 #1 オフセット
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|こ |ん |に |ち |は |。 |0|さ |よ 文字列 #0, 文字列 #1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |う |な |ら |。 |0|           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

indexOffset, stringOffset, および各文字列オフセットは、ファイルからデータを読み込んで
StringTable のアドレスを加算して、キャストすることでポインタとして扱えます。
C++ ではなく C 風な使い方なのが少し気になりますが、この方が簡単・便利になります。
(もちろん、整数とポインタの間のキャストがうまく行く環境に限られます。)
キャストをこのデータを直接扱う部分に閉じ込めてやれば、他のソースはキレイな C++
コードを保つことが出来るということで手を打とうと思います。