2 Jul 03, 2018 K.Ohta <whatisthis.sowhat@gmail.com>
5 武田俊也氏によるCommon Source Code Project (CSP)は、前に書いた文章でも書いてるように、
6 非常に設計の出来が良くて色々な人が参加しやすいように基本構造が出来ていて、しかも、GPLv2で
7 配布しているメリットが大きいわけですが、四五年ほど(もっとかも)ポーティングやFM-7シリーズを
8 移植するのにやっていると、もう少し改良したほうがよくないか?というものが度々出てきて、
9 ひんしゅくを買うかも知れないなどと震えながら、改良とか機能を「わがままに」追加してきました。
10 で、最近も色々作業をしていて、save_state()やload_state()で、Read/Writeのエレメント部分を
11 ベタ書きしていて、その後に後処理をやってるので、バグを仕込む温床になるよねー。と言う感じの
16 ・ステートのセーブロードは、エントリーを一個の関数で固めて行い、その関数が持ってる
17 エントリーリストに基づき、実際のデータをセーブもしくはロードできるようにする事で、
18 「セーブとロードで順番間違えたから、見た目正しくてもデータぶっ壊れてた」と言う悲
21 ・それでも「凡ミス」はつきまとうものですから、CCITT-CRC32演算を、各データブロックごと
22 に計算して、一個の塊の末尾に結果を置くと同時に、簡単なヘッダや識別子で、このデータは
23 なんのステートを表現してるか見えやすくする…要は、デバッグにも改変にも有効な構造に変える。
25 ・他のホストマシンでステートロード/セーブしたときでも、ステートデータが使えるように
26 しておきたい。となると、エンディアンなどの問題を解決しておかないといけない。
28 と言う辺りを実際にやろうと考えて作業しております。
32 csp_state_utils:: : セーブやロードを実行する為のクラス。
33 一つのデバイスやVM、その他の「もの」(DISK:: とか)ごとに、このクラスを
36 csp_state_data_saver:: 実際のデータを読んだり書いたりするクラス。通常はcsp_state_utils::
37 クラスの内部のみで使うが、呼び出す側で特殊な処理が必要な場合は、使用する。
39 又、実際の使用は、サポートルーチンやマクロを使って行うほうがよい。
42 (論理)数値 : bool , uintXX_t, intXX_t, float, double, long double
43 文字 (*1) : _TCHAR, _TCHAR *
45 固定長配列 : 1D_ARRAY, 2D_ARRAY, 3D_ARRAY
47 クラス : FIFO*, CUR_TIME_T
48 特殊 : DECL_STATE_ENTRY_CMT_RECORDING() : テープ録音(VM)絡みの処理に使う。
51 (*1) 単純に_TCHARの配列 として宣言した場合には、配列、
52 DECL_STATE_ENTRY_STRING だと、0ターミネートの文字列として扱う
54 2.2 ファイル(今後分割するかも知れません)
55 src/statesub.h : 使用者側が#include すべきヘッダ。必要な定義などは、このヘッダにまとめて読み込まれます。
56 src/state_data.h : 主に、csp_state_data_saver:: の内部定義が入ってる。
57 src/statesub.cpp : 処理実態の大半が入ってます。
58 src/common.cpp : calc_crc32() など、補助ルーチンを新設しました。
59 src/vm/device.h : エントリー定義に便利なメソッド定義をDEVICE:: にしてあります。
62 csp_state_utils::csp_state_utils(int _version = 1, int device_id = 1,
63 const _TCHAR *classname = NULL,
64 CSP_Logger *p_logger = NULL)
65 : コアクラスのコンストラクタ。Qt版では、QObjectの継承をして構成されてる。
66 (Qtの SIGNAL/SLOTモデルのメッセージングに対応するため)。
69 void csp_state_utils::add_entry(const _TCHAR *__name, T*p,
70 int _len = 1, int _num = -1,
71 bool is_const = false, int stride = 0);
73 : セーブ項目変数を登録する。変数はポインタでなければならない。
74 次項で示す、DECL_STATE_ENTRY_foo系の宣言マクロを使うことを、
76 __name は、変数の名前、pは変数のあるポインタ(&var)、
77 _lenは、変数の*要素数* (length / sizeof(T))。
78 __numは、構造体の配列などで、メンバを記述する時に、配列内の
79 「番号」を記述するための変数、is_constはロード時に内容変更さ
81 strideは、構造体全体の大きさ。sizeof(struct foo)。
82 構造体の配列で、それぞれのメンバを定義するのに冗長さを抑え、
86 void add_entry_vararray(const _TCHAR *__name, T **p, void *datalen,
87 bool assume_byte = false, int __num = -1,
90 : 可変長配列(malloc/freeで管理される)を登録する。
91 配列の先頭のポインタのポインタをpに、変数の要素数(Tな変数の個数)を
92 格納する変数のポインタをdatalenに登録する。
94 void add_entry_fifo(const _TCHAR *__name, FIFO **p,
95 int _len = 1, int __num = -1, int stride = 0);
99 void add_entry_cur_time_t(const _TCHAR *__name, cur_time_t *p,
100 int _len = 1, int __num = -1, int stride = 0);
102 : cur_time_t 型のエントリ関数。ポインタ渡しでなければならない。
104 void add_entry_scrntype_t(const _TCHAR *__name, cur_time_t *p,
105 int _len = 1, int __num = -1, int stride = 0);
107 : scrntype_t 型のエントリ関数。ポインタ渡しでなければならない。
109 void add_entry_string(const _TCHAR *__name, _TCHAR *p,
110 int _len = 1, int __num = -1, bool is_const = false);
114 void add_entry_cmt_recording(const _TCHAR *__name,
115 FILEIO **__fio, bool* __flag, _TCHAR *__path);
117 : ロードセーブ時に、テープに録音する動作をしていた時に、
118 リジューム動作が必要になるときのエントリ。
119 __fio : テープイメージセーブ用に使われるFILEIO::クラスの
121 __flag: trueで録音が残ってると示すbool方変数へのポインタ。
122 __path: 読み書きしていた仮想テープのパス。
124 bool save_state(FILEIO *__fio, uint32 *pcrc = NULL);
125 bool load_state(FILEIO *__fio, uint32 *pcrc = NULL);
126 : add_entry_FOO()で登録したエントリをload/saveする。
127 __fio はsave_state(FILEIO *fio) のfio。
128 *pcrc は、CRC変数のポインタ(なくてもよい。)
130 bool csp_state_data_saver::pre_proc_saving(uint32_t *sumseed, bool *__stat);
131 bool csp_state_data_saver::pre_proc_loading(uint32_t *sumseed, bool *__stat);
132 : ステートセーブ/ロード前の事前処理をする。
133 sumseed は、CRCを初期化したい変数のポインタ(通常、この変
134 数の初期値は0xffffffffである)。
135 __stat は、成功か失敗かを知るときの判定変数へのポインタ。
137 bool csp_state_data_saver::post_proc_saving(uint32_t *sumseed, bool *__stat);
139 sumseedにあるCRCを基に最終的な計算を行い、その値を追加する。
141 bool csp_state_data_saver::post_proc_loading(uint32_t *sumseed, bool *__stat);
143 セーブ時に追加されたCRC値とsumseedから計算されたCRCを照合
146 2.4 API(2) エントリ宣言マクロなど
149 : そのデバイスなどのセーブステート項目を宣言する関数(スケルトンがDEVICE::にある)
151 DEVICE::enter_decl_state(int version);
152 : 各デバイスのdecl_state() 関数の冒頭に置くことで、
153 csp_state_utilsクラスをnew ・初期設定する。
155 DEVICE::leave_decl_state();
156 : del_state()の末尾につけると良い。
159 DECL_STATE_ENTRY_FOO(__name)
160 : FOOは基本的な変数型やFIFO*など : 一個の要素を宣言する。
161 DECL_STATE_ENTRY_FOO_MEMBER(__name, __num)
162 : FOOは基本的な変数型 : 一個の要素が__num目配列の要素のだと宣言する。
163 DECL_STATE_ENTRY_FOO_STRIDE(__name, __len, __stride)
164 : FOOは基本的な変数型 : 大きさ__strideでああり、__len回の配列となってる構造体内の
167 DECL_STATE_ENTRY_1D_ARRAY(__name, __lenvar)
168 : 1次元配列(要素数__lenvar)を定義する。__lenvar = sizeof(ARRAY) / sizeof(__name)。
170 DECL_STATE_ENTRY_2D_ARRAY(__name, x, y)
171 : 2次元配列(要素数[x][y])を定義する。
173 DECL_STATE_ENTRY_3D_ARRAY(__name, x, y, z)
174 : 3次元配列(要素数[x][y][z])を定義する。
176 DECL_STATE_ENTRY_STRING(__name, __len)
177 : 文字列型変数__name (最大長__len - 1)を定義する。
179 DECL_STATE_ENTRY_VARARRAY_VAR(__name, __sizevar)
180 : 可変長変数へのポインタ__nameが、__sizevarの値によってバイト数を
182 uint8_t *__name = malloc(__sizevar);
184 DECL_STATE_ENTRY_VARARRAY_VAR(__name, __sizevar);
186 注意:__sizevarのセーブ・ロードは、__nameより前に行うこと。
188 DECL_STATE_ENTRY_CMT_RECORDING(__fio, __flag, __path)
190 __fioをつかって、仮想テープを「録音」してることを__flagが示した場合に、
191 書かれた仮想テープのデータをステートに書き込み、そのサイズも書き込む。
192 __flagが録音していないと示した場合、(uint32_t)0をステートに書き込む。
194 録音データがセーブされていた場合は、__pathが示すファイルにそのデータを
199 ・各データは、ビッグエンディアンのバイナリ値で記録される(例外あり)。
200 ・文字列型データは、\0ターミネートして記録される。
201 ・実数型(float, double, long double)は、アスキー文字列に変換された形で記録される。
202 0.0 = "0.0" + '\0' など。
203 ・各デバイスのステート(チャンクの)書式は以下のようになっている:
205 HEADER : ヘッダ ("CSP_SAVE" + '\0')
206 CLASSNAME : デバイス名(foo->this_device_name + '\0')
207 DEVICE_ID : デバイスID(foo->this_device_id INT32)
208 STATE_VER : デバイス内のステートのヴァージョン (INT32)
209 INTERNAL_VER : ステートフレーム側のヴァージョン
210 [バイナリデータ列] : DECL_STATE_ENTRY_fooで定義された実データの並び
211 CRC32 : HEADER欄からバイナリデータ列末尾までのCRC32値をUINT32 Big Endianで記録する。
214 ・VM::save_state()でセーブされるデータは、以下のような構造になっている:
215 HEADER : "CSP_SAVE" + '\0'
216 CLASSNAME : (例) "CSP::VM_HEAD" + '\0'
218 STATE_VER : 各VMの(vm/foo/foo.cppの)STATE_VERSION
220 [バイナリデータ列] : [必要に応じて。0バイトの場合もある]
223 [DEVICE #2 (通常Event)のステートチャンク)]
224 [DEVICE #3 のステートチャンク)]
228 [DEVICE #n(最後のデバイス) のステートチャンク)]
231 ※ DEVICE #1は通常DUMMY DEVICEであり、セーブ項目が、ない。