//
//	clipreg.cs
//
//	レジストリテーブル
//
//	CLiP - Common Library for P/ECE
//	Copyright (C) 2017 Naoyuki Sawa
//
//	* Sun Feb 26 22:25:44 JST 2017 Naoyuki Sawa
//	- 1st リリース。
//	- おおよそ、C言語版の同名の関数と同じように使えると思います。
//	  一点、大きな違いとして、C言語版の関数では、名前(int値)の可変引数の終端を-1で表していましたが、C#版では終端の-1は不要になりました。
//	  逆に、-1を付けてしまうとそれも名前の一部と見なされて検索に失敗するので、C#版では名前の可変引数に-1を付けないように注意して下さい。
//	* Tue Feb 28 21:46:05 JST 2017 Naoyuki Sawa
//	- REG_open_key_v()等の引数namesの型を、(int[])⇒(IEnumerable<int>)に変更しました。
//	  REG_open_key_v()等の引数namesに渡す配列は、List<int>等を使って動的に作成する事も多いと思うので、int[]でもList<int>でも受け付けられるようにするためです。
//	  尚、REG_open_key_l()等の引数namesの型は、今回変更した件とは関係無いので、(params int[])のままです。
//	* Wed Mar 01 22:47:42 JST 2017 Naoyuki Sawa
//	- REG_get_blob()の戻り値の型を、(byte[])⇒(int)に変更しました。
//	  C#版では、BLOBデータをbyte配列として返す方が便利かと思って(byte[])にしていたのですが、やはりBLOBを参照するたびに毎回BLOB全体をコピーするのはオーバーヘッドが大き過ぎると思ったので、C言語版と同様にBLOBへのポインタ(厳密にはC#版ではBLOBの先頭位置のインデクス)を返す事にしました。
//	- 上記の変更に伴い、REG_get_blob_size()を追加しました。
//	  REG_get_blob()の戻り値の型を(byte[])としていた時は、REG_get_blob_size()は不要と思って削除していたのですが、REG_get_blob()の戻り値の型を(int)に変更したので、やはりREG_get_blob_size()が必要になり、C言語版と同様のREG_get_blob_size()を追加しました。
//	- アプリケーション側においては、REG_get_blob()が返したBLOBのインデクスのままで、レジストリデータ内を参照して処理出来る場合は、その方が効率が良いです。
//	  しかし、BLOBデータの単体のbyte配列が必要な場合は、以下のような処理でbyte配列を作成して下さい。
//	  ○例1
//	  │int addr = REG_get_blob(TBL_RegTbl,keyBody,blobName);
//	  │if(addr != -1) {
//	  │  int size = REG_get_blob_size(TBL_RegTbl, addr/*-1可*/);
//	  │  byte[] blob = new byte[size];
//	  │  Array.Copy(TBL_RegTbl,addr,blob,0,size);
//	  │}
//	  ○例2
//	  │int addr = REG_get_blob(TBL_RegTbl,keyBody,blobName);
//	  │int size = REG_get_blob_size(TBL_RegTbl,addr/*-1可*/);
//	  │if(size != 0) {
//	  │  byte[] blob = new byte[size];
//	  │  Array.Copy(TBL_RegTbl,addr,blob,0,size);
//	  │}
//	  ×ダメな例
//	  │int addr = REG_get_blob(TBL_RegTbl,keyBody,blobName);
//	  │int size = REG_get_blob_size(TBL_RegTbl,addr/*-1可*/);
//	  │byte[] blob = new byte[size];
//	  │Array.Copy(TBL_RegTbl,addr/*-1不可*/,blob,0,size);	//Array.Copy()はmemcpy()と違って、(size==0)だったとしても先にaddrを検査するようです。この方法では、BLOBが存在しなかった場合に、空のバイト配列が出来るのではなく、Array.Copy()がエラー停止してしまうので、この方法はダメです。
//	* Fri Mar 03 22:34:24 JST 2017 Naoyuki Sawa
//	- clipptr_net.csモジュールを追加した事に伴い、ポインタの扱いを大幅に変更しました。
//	  これまでは、配列とインデクスを保持する必要が有りましたが、今後はC言語版と同様に、配列ポインタ一つを保持すれば良くなりました。
//	- 今回は変更量がかなり多かったので、変更前のコードはコメントアウトして残していません。
//	  変更前のコードを参照する場合は、前回のアーカイブを参照して下さい。
//	* Fri Mar 10 22:30:11 JST 2017 Naoyuki Sawa
//	- Unityで使う場合は、REG_get_string(),REG_get_nth_string()において、ShiftJis⇒Unicodeを自前で行うように変更しました。
//	  変更した理由の詳細は、clipmisc_uni.csの同日のコメントを参照して下さい。
//	* Mon Mar 20 22:44:17 JST 2017 Naoyuki Sawa
//	- これまでは、Unityで使う場合のみ、REG_get_string(),REG_get_nth_string()において、ShiftJis⇒Unicodeを自前で行っていましたが、clipunic.csモジュールを追加した事に伴い、常にShiftJis⇒Unicodeを自前で行うように変更しました。
//	  Unity以外ならば、これまで通り.NET FrameworkのEncodingを使っても構わないのですが、自前のShiftJis⇒Unicode変換の検証のために、Unity以外でも自前のShiftJis⇒Unicode変換を使う事にしました。
//
using System;
using System.Collections.Generic;
using System.Text;
namespace org.piece_me {
	public static partial class libclip {
		//*****************************************************************************
		//	辞書アクセス型の関数
		//*****************************************************************************
		private struct REG_data_addr {
			public int	data;	//データ(符号無し24ビット)
			public BytePtr	addr;	//アドレス
		}
		//-----------------------------------------------------------------------------
		private static BytePtr REG_bsearch(BytePtr addr, int name, int n/*>=0*/) {
			int nameL, nameH, diff;
			BytePtr mid;
			nameL  = (byte)name;
			name >>= 8;
			nameH  = (byte)name;
			name >>= 8;
			if(name == 0) {	//名前は符号無し16ビットである。範囲外の名前が指定された場合に下位16bitが偶然一致しないように、上位16ビットが0でなければ先に除外する。
				while(n != 0) {
					mid = addr + ((n >> 1) * (2/*name*/+3/*data*/));
					diff = mid[0/*nameH*/] - nameH;
					if(diff == 0) {
						diff = mid[1/*nameL*/] - nameL;
						if(diff == 0) { return mid + 2/*data*/; }	//見つかったらdataへのポインタを返す。
					}
					if(diff < 0) {
						addr = mid + (2/*name*/+3/*data*/);
						n--;
					}
					n >>= 1;
				}
			}
			return null;	//見つからなければnullを返す。
		}
		//-----------------------------------------------------------------------------
		private static REG_data_addr REG_op(BytePtr addr, int name, int type) {
			int mask, n, data;
			//nullアドレスに対する呼び出しならば要素が無いのと同じと見なして帰る。
			if(!addr) { return new REG_data_addr(); }
			//このキーボディが持っている配列タイプのマスクを取得する。
			// - bit31=キー,bit30=値,bit29=文字列,bit28=BLOB,bit27～24=0(未使用),bit27～0=0
			mask = addr.Read() << 24;
			for(;;) {
				//このキーボディがこのタイプの配列を持っていたら…
				if(mask < 0) {
					mask <<= 1;
					//配列の(要素数-1)を取得する。
					n   = addr.Read();	//┐
					n <<= 8;		//├符号無し16ビット,ビッグエンディアン
					n  |= addr.Read();	//┘
					//(要素数-1)⇒要素数に変換する。
					n++;
				//このキーボディがこのタイプの配列を持っていなければ…
				} else {
					mask <<= 1;
					//このキーボディが持っている配列タイプが残っていなければ帰る。
					if(mask == 0) { return new REG_data_addr(); }
					//配列の要素数を0とする。
					n = 0;
				}
				//この配列が指定されたタイプの配列ならば抜ける。
				if(--type < 0) { break; }
				//この配列をスキップする。
				addr += n * (2/*name*/+3/*data*/);
			}
		    {{//--- REG_op()とREG_nth()とで処理の違いはこの範囲だけです。 ---
			//この配列から指定された名前に一致する要素を検索する。
			addr = REG_bsearch(addr, name, n);	//見つかったらdataへのポインタが返される。見つからなければnullが返される。
			//指定された名前に一致する要素が見つからなければ帰る。
			if(!addr) { return new REG_data_addr(); }
		    }}//--- REG_op()とREG_nth()とで処理の違いはこの範囲だけです。 ---
			data   = addr.Read();		//┐
			data <<= 8;			//│
			data  |= addr.Read();		//├符号無し24ビット,ビッグエンディアン
			data <<= 8;			//│
			data  |= addr.Read();		//┘
			addr  += (data << 8) >> 8;	//アドレス	データを符号付き24ビットとしてアドレスに加算する。REG_get_value()から呼び出された場合は使用しない。
			//戻り値 data = データ(符号無し24ビット)	REG_get_value()から呼び出された場合はこっちを使う。
			//       addr = アドレス			REG_get_value()以外から呼び出された場合はこっちを使う。
			return new REG_data_addr() { data = data, addr = addr };
		}
		//=============================================================================
		//	基本的な関数
		//=============================================================================
		public static BytePtr REG_open_key(BytePtr keyBody, int keyName) {
			REG_data_addr data_addr = REG_op(keyBody, keyName, 0/*キー*/);
			return data_addr.addr;
		}
		//-----------------------------------------------------------------------------
		public static int REG_get_value(BytePtr keyBody, int valueName) {
			REG_data_addr data_addr = REG_op(keyBody, valueName, 1/*値*/);
			if(!data_addr.addr) { return -1; }
			return data_addr.data;
		}
		//-----------------------------------------------------------------------------
		public static string REG_get_string(BytePtr keyBody, int stringName) {
			REG_data_addr data_addr = REG_op(keyBody, stringName, 2/*文字列*/);
			if(!data_addr.addr) { return null; }
//{{2017/03/20変更:これまでは、Unityで使う場合のみ、REG_get_string(),REG_get_nth_string()において、ShiftJis⇒Unicodeを自前で行っていましたが、clipunic.csモジュールを追加した事に伴い、常にShiftJis⇒Unicodeを自前で行うように変更しました。
//#if     !UNITY_5_3_OR_NEWER
//			int top = data_addr.addr.Offset;
//		    //{{要注意
//			int end = Array.IndexOf<byte>(data_addr.addr.Array, 0, top);	//ok
//		    //	int end = Array.IndexOf(data_addr.addr.Array, (byte)0, top);	//ok
//		    //	int end = Array.IndexOf(data_addr.addr.Array, 0, top);		//NG	byte配列からint値(0)を検索する事になり一致しない。
//		    //}}要注意
//			return Encoding.GetEncoding("Shift_JIS").GetString(data_addr.addr.Array, top, end - top);
//#else //!UNITY_5_3_OR_NEWER
//			return ShiftJisToUnicodeStr(data_addr.addr);
//#endif//!UNITY_5_3_OR_NEWER
//↓2017/03/20変更:これまでは、Unityで使う場合のみ、REG_get_string(),REG_get_nth_string()において、ShiftJis⇒Unicodeを自前で行っていましたが、clipunic.csモジュールを追加した事に伴い、常にShiftJis⇒Unicodeを自前で行うように変更しました。
			return ShiftJisToUnicodeStr(data_addr.addr);
//}}2017/03/20変更:これまでは、Unityで使う場合のみ、REG_get_string(),REG_get_nth_string()において、ShiftJis⇒Unicodeを自前で行っていましたが、clipunic.csモジュールを追加した事に伴い、常にShiftJis⇒Unicodeを自前で行うように変更しました。
		}
		//-----------------------------------------------------------------------------
		public static VoidPtr REG_get_blob(BytePtr keyBody, int blobName) {
			REG_data_addr data_addr = REG_op(keyBody, blobName, 3/*BLOB*/);
			return data_addr.addr;
		}
		//=============================================================================
		//	ユーティリティ関数
		//=============================================================================
		public static BytePtr REG_open_key_l(BytePtr keyBody, params int[] names) {
			return REG_open_key_v(keyBody, names);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static BytePtr REG_open_key_v(BytePtr keyBody, IEnumerable<int> names) {
			IEnumerator<int> e = names.GetEnumerator();
			if(!e.MoveNext()) { return keyBody; }	//「REG_open_key_l(keyBody)」の形で呼び出された場合は、エラーとしてnullを返すのではなく、keyBody自身を返す方が自然です。
			int name = e.Current;
		    //{{C言語版のREG_open_get_v()に相当する処理です。C#版にはREG_open_get_v()は有りません。
			while(e.MoveNext()) {
				keyBody = REG_open_key(keyBody, name);	//(keyBody=null)が指定されたり、REG_open_key()がnullを返して(keyBody=null)になっても、継続して安全です。
				name = e.Current;
			}
		    //}}C言語版のREG_open_get_v()に相当する処理です。C#版にはREG_open_get_v()は有りません。
			return REG_open_key(keyBody, name);
		}
		//-----------------------------------------------------------------------------
		public static int REG_get_value_l(BytePtr keyBody, params int[] names) {
			return REG_get_value_v(keyBody, names);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static int REG_get_value_v(BytePtr keyBody, IEnumerable<int> names) {
			IEnumerator<int> e = names.GetEnumerator();
			if(!e.MoveNext()) { return -1; }
			int name = e.Current;
		    //{{C言語版のREG_open_get_v()に相当する処理です。C#版にはREG_open_get_v()は有りません。
			while(e.MoveNext()) {
				keyBody = REG_open_key(keyBody, name);	//(keyBody=null)が指定されたり、REG_open_key()がnullを返して(keyBody=null)になっても、継続して安全です。
				name = e.Current;
			}
		    //}}C言語版のREG_open_get_v()に相当する処理です。C#版にはREG_open_get_v()は有りません。
			return REG_get_value(keyBody, name);
		}
		//-----------------------------------------------------------------------------
		public static string REG_get_string_l(BytePtr keyBody, params int[] names) {
			return REG_get_string_v(keyBody, names);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static string REG_get_string_v(BytePtr keyBody, IEnumerable<int> names) {
			IEnumerator<int> e = names.GetEnumerator();
			if(!e.MoveNext()) { return null; }
			int name = e.Current;
		    //{{C言語版のREG_open_get_v()に相当する処理です。C#版にはREG_open_get_v()は有りません。
			while(e.MoveNext()) {
				keyBody = REG_open_key(keyBody, name);	//(keyBody=null)が指定されたり、REG_open_key()がnullを返して(keyBody=null)になっても、継続して安全です。
				name = e.Current;
			}
		    //}}C言語版のREG_open_get_v()に相当する処理です。C#版にはREG_open_get_v()は有りません。
			return REG_get_string(keyBody, name);
		}
		//-----------------------------------------------------------------------------
		public static VoidPtr REG_get_blob_l(BytePtr keyBody, params int[] names) {
			return REG_get_blob_v(keyBody, names);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static VoidPtr REG_get_blob_v(BytePtr keyBody, IEnumerable<int> names) {
			IEnumerator<int> e = names.GetEnumerator();
			if(!e.MoveNext()) { return null; }
			int name = e.Current;
		    //{{C言語版のREG_open_get_v()に相当する処理です。C#版にはREG_open_get_v()は有りません。
			while(e.MoveNext()) {
				keyBody = REG_open_key(keyBody, name);	//(keyBody=null)が指定されたり、REG_open_key()がnullを返して(keyBody=null)になっても、継続して安全です。
				name = e.Current;
			}
		    //}}C言語版のREG_open_get_v()に相当する処理です。C#版にはREG_open_get_v()は有りません。
			return REG_get_blob(keyBody, name);
		}
		//*****************************************************************************
		//	インデクスアクセス型,及び,イテレーション用の関数
		//*****************************************************************************
		private static REG_data_addr REG_nth(BytePtr addr, int index, out int p_name, int type) {
			int mask, n, data;
		    //{{--- REG_op()とREG_nth()とで処理の違いはこの範囲だけです。 ---
			p_name = -1;	//エラー抑制
		    //}}--- REG_op()とREG_nth()とで処理の違いはこの範囲だけです。 ---
			//nullアドレスに対する呼び出しならば要素が無いのと同じと見なして帰る。
			if(!addr) { return new REG_data_addr(); }
			//このキーボディが持っている配列タイプのマスクを取得する。
			// - bit31=キー,bit30=値,bit29=文字列,bit28=BLOB,bit27～24=0(未使用),bit27～0=0
			mask = addr.Read() << 24;
			for(;;) {
				//このキーボディがこのタイプの配列を持っていたら…
				if(mask < 0) {
					mask <<= 1;
					//配列の(要素数-1)を取得する。
					n   = addr.Read();	//┐
					n <<= 8;		//├符号無し16ビット,ビッグエンディアン
					n  |= addr.Read();	//┘
					//(要素数-1)⇒要素数に変換する。
					n++;
				//このキーボディがこのタイプの配列を持っていなければ…
				} else {
					mask <<= 1;
					//このキーボディが持っている配列タイプが残っていなければ帰る。
					if(mask == 0) { return new REG_data_addr(); }
					//配列の要素数を0とする。
					n = 0;
				}
				//この配列が指定されたタイプの配列ならば抜ける。
				if(--type < 0) { break; }
				//この配列をスキップする。
				addr += n * (2/*name*/+3/*data*/);
			}
		    {{//--- REG_op()とREG_nth()とで処理の違いはこの範囲だけです。 ---
			int name;
			//指定されたインデクスが配列の要素数以上ならば帰る。
			if((uint)index >= (uint)n) { return new REG_data_addr(); }
			//指定されたインデクスの要素のアドレスを求める。
			addr  += index * (2/*keyName*/+3/*keyBodyDist*/);
			//要素の名前を取得(,格納)する。
			name   = addr.Read();	//┐
			name <<= 8;		//├符号無し16ビット,ビッグエンディアン
			name  |= addr.Read();	//┘
			p_name = name;
		    }}//--- REG_op()とREG_nth()とで処理の違いはこの範囲だけです。 ---
			data   = addr.Read();		//┐
			data <<= 8;			//│
			data  |= addr.Read();		//├符号無し24ビット,ビッグエンディアン
			data <<= 8;			//│
			data  |= addr.Read();		//┘
			addr  += (data << 8) >> 8;	//アドレス	データを符号付き24ビットとしてアドレスに加算する。REG_get_value()から呼び出された場合は使用しない。
			//戻り値 data = データ(符号無し24ビット)	REG_get_value()から呼び出された場合はこっちを使う。
			//       addr = アドレス			REG_get_value()以外から呼び出された場合はこっちを使う。
			return new REG_data_addr() { data = data, addr = addr };
		}
		//=============================================================================
		//	基本的な関数
		//=============================================================================
		public static BytePtr REG_open_nth_key(BytePtr keyBody, int index) {
			int p_keyName;
			return REG_open_nth_key(keyBody, index, out p_keyName);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static BytePtr REG_open_nth_key(BytePtr keyBody, int index, out int p_keyName) {
			REG_data_addr data_addr = REG_nth(keyBody, index, out p_keyName, 0/*キー*/);
			return data_addr.addr;
		}
		//-----------------------------------------------------------------------------
		public static int REG_get_nth_value(BytePtr keyBody, int index) {
			int p_valueName;
			return REG_get_nth_value(keyBody, index, out p_valueName);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static int REG_get_nth_value(BytePtr keyBody, int index, out int p_valueName) {
			REG_data_addr data_addr = REG_nth(keyBody, index, out p_valueName, 1/*値*/);
			if(!data_addr.addr) { return -1; }
			return data_addr.data;
		}
		//-----------------------------------------------------------------------------
		public static string REG_get_nth_string(BytePtr keyBody, int index) {
			int p_stringName;
			return REG_get_nth_string(keyBody, index, out p_stringName);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static string REG_get_nth_string(BytePtr keyBody, int index, out int p_stringName) {
			REG_data_addr data_addr = REG_nth(keyBody, index, out p_stringName, 2/*文字列*/);
			if(!data_addr.addr) { return null; }
//{{2017/03/20変更:これまでは、Unityで使う場合のみ、REG_get_string(),REG_get_nth_string()において、ShiftJis⇒Unicodeを自前で行っていましたが、clipunic.csモジュールを追加した事に伴い、常にShiftJis⇒Unicodeを自前で行うように変更しました。
//#if     !UNITY_5_3_OR_NEWER
//			int top = data_addr.addr.Offset;
//		    //{{要注意
//			int end = Array.IndexOf<byte>(data_addr.addr.Array, 0, top);	//ok
//		    //	int end = Array.IndexOf(data_addr.addr.Array, (byte)0, top);	//ok
//		    //	int end = Array.IndexOf(data_addr.addr.Array, 0, top);		//NG	byte配列からint値(0)を検索する事になり一致しない。
//		    //}}要注意
//			return Encoding.GetEncoding("Shift_JIS").GetString(data_addr.addr.Array, top, end - top);
//#else //!UNITY_5_3_OR_NEWER
//			return ShiftJisToUnicodeStr(data_addr.addr);
//#endif//!UNITY_5_3_OR_NEWER
//↓2017/03/20変更:これまでは、Unityで使う場合のみ、REG_get_string(),REG_get_nth_string()において、ShiftJis⇒Unicodeを自前で行っていましたが、clipunic.csモジュールを追加した事に伴い、常にShiftJis⇒Unicodeを自前で行うように変更しました。
			return ShiftJisToUnicodeStr(data_addr.addr);
//}}2017/03/20変更:これまでは、Unityで使う場合のみ、REG_get_string(),REG_get_nth_string()において、ShiftJis⇒Unicodeを自前で行っていましたが、clipunic.csモジュールを追加した事に伴い、常にShiftJis⇒Unicodeを自前で行うように変更しました。
		}
		//-----------------------------------------------------------------------------
		public static VoidPtr REG_get_nth_blob(BytePtr keyBody, int index) {
			int p_blobName;
			return REG_get_nth_blob(keyBody, index, out p_blobName);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static VoidPtr REG_get_nth_blob(BytePtr keyBody, int index, out int p_blobName) {
			REG_data_addr data_addr = REG_nth(keyBody, index, out p_blobName, 3/*BLOB*/);
			return data_addr.addr;
		}
		//=============================================================================
		//	ユーティリティ関数
		//=============================================================================
		public static BytePtr REG_open_nth_key_l(BytePtr keyBody, int index, params int[] names) {
			int p_keyName;
			return REG_open_nth_key_l(keyBody, index, out p_keyName, names);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static BytePtr REG_open_nth_key_l(BytePtr keyBody, int index, out int p_keyName, params int[] names) {
			return REG_open_nth_key_v(keyBody, index, out p_keyName, names);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static BytePtr REG_open_nth_key_v(BytePtr keyBody, int index, IEnumerable<int> names) {
			int p_keyName;
			return REG_open_nth_key_v(keyBody, index, out p_keyName, names);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static BytePtr REG_open_nth_key_v(BytePtr keyBody, int index, out int p_keyName, IEnumerable<int> names) {
			return REG_open_nth_key(REG_open_key_v(keyBody, names), index, out p_keyName);
		}
		//-----------------------------------------------------------------------------
		public static int REG_get_nth_value_l(BytePtr keyBody, int index, params int[] names) {
			int p_valueName;
			return REG_get_nth_value_l(keyBody, index, out p_valueName, names);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static int REG_get_nth_value_l(BytePtr keyBody, int index, out int p_valueName, params int[] names) {
			return REG_get_nth_value_v(keyBody, index, out p_valueName, names);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static int REG_get_nth_value_v(BytePtr keyBody, int index, IEnumerable<int> names) {
			int p_valueName;
			return REG_get_nth_value_v(keyBody, index, out p_valueName, names);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static int REG_get_nth_value_v(BytePtr keyBody, int index, out int p_valueName, IEnumerable<int> names) {
			return REG_get_nth_value(REG_open_key_v(keyBody, names), index, out p_valueName);
		}
		//-----------------------------------------------------------------------------
		public static string REG_get_nth_string_l(BytePtr keyBody, int index, params int[] names) {
			int p_stringName;
			return REG_get_nth_string_l(keyBody, index, out p_stringName, names);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static string REG_get_nth_string_l(BytePtr keyBody, int index, out int p_stringName, params int[] names) {
			return REG_get_nth_string_v(keyBody, index, out p_stringName, names);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static string REG_get_nth_string_v(BytePtr keyBody, int index, IEnumerable<int> names) {
			int p_stringName;
			return REG_get_nth_string_v(keyBody, index, out p_stringName, names);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static string REG_get_nth_string_v(BytePtr keyBody, int index, out int p_stringName, IEnumerable<int> names) {
			return REG_get_nth_string(REG_open_key_v(keyBody, names), index, out p_stringName);
		}
		//-----------------------------------------------------------------------------
		public static VoidPtr REG_get_nth_blob_l(BytePtr keyBody, int index, params int[] names) {
			int p_blobName;
			return REG_get_nth_blob_l(keyBody, index, out p_blobName, names);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static VoidPtr REG_get_nth_blob_l(BytePtr keyBody, int index, out int p_blobName, params int[] names) {
			return REG_get_nth_blob_v(keyBody, index, out p_blobName, names);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static VoidPtr REG_get_nth_blob_v(BytePtr keyBody, int index, IEnumerable<int> names) {
			int p_blobName;
			return REG_get_nth_blob_v(keyBody, index, out p_blobName, names);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static VoidPtr REG_get_nth_blob_v(BytePtr keyBody, int index, out int p_blobName, IEnumerable<int> names) {
			return REG_get_nth_blob(REG_open_key_v(keyBody, names), index, out p_blobName);
		}
		//*****************************************************************************
		//	要素数を取得する関数
		//*****************************************************************************
		private static int REG_num(BytePtr addr, int type) {
			int mask, n;
			//nullアドレスに対する呼び出しならば要素が無いのと同じと見なして帰る。
			if(!addr) { return 0; }
			//このキーボディが持っている配列タイプのマスクを取得する。
			// - bit31=キー,bit30=値,bit29=文字列,bit28=BLOB,bit27～24=0(未使用),bit27～0=0
			mask = addr.Read() << 24;
			for(;;) {
				//このキーボディがこのタイプの配列を持っていたら…
				if(mask < 0) {
					mask <<= 1;
					//配列の(要素数-1)を取得する。
					n   = addr.Read();	//┐
					n <<= 8;		//├符号無し16ビット,ビッグエンディアン
					n  |= addr.Read();	//┘
					//(要素数-1)⇒要素数に変換する。
					n++;
				//このキーボディがこのタイプの配列を持っていなければ…
				} else {
					mask <<= 1;
					//このキーボディが持っている配列タイプが残っていなければ帰る。
					if(mask == 0) { return 0; }
					//配列の要素数を0とする。
					n = 0;
				}
				//この配列が指定されたタイプの配列ならば抜ける。
				if(--type < 0) { break; }
				//この配列をスキップする。
				addr += n * (2/*name*/+3/*data*/);
			}
		    //↑↑↑ここまではREG_op(),REG_nth()と全く同じ処理です。↑↑↑
			return n;
		}
		//=============================================================================
		//	基本的な関数
		//=============================================================================
		public static int REG_num_key(BytePtr keyBody) {
			return REG_num(keyBody, 0/*キー*/);
		}
		//-----------------------------------------------------------------------------
		public static int REG_num_value(BytePtr keyBody) {
			return REG_num(keyBody, 1/*値*/);
		}
		//-----------------------------------------------------------------------------
		public static int REG_num_string(BytePtr keyBody) {
			return REG_num(keyBody, 2/*文字列*/);
		}
		//-----------------------------------------------------------------------------
		public static int REG_num_blob(BytePtr keyBody) {
			return REG_num(keyBody, 3/*BLOB*/);
		}
		//=============================================================================
		//	ユーティリティ関数
		//=============================================================================
		public static int REG_num_key_l(BytePtr keyBody, params int[] names) {
			return REG_num_key_v(keyBody, names);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static int REG_num_key_v(BytePtr keyBody, IEnumerable<int> names) {
			return REG_num_key(REG_open_key_v(keyBody, names));
		}
		//-----------------------------------------------------------------------------
		public static int REG_num_value_l(BytePtr keyBody, params int[] names) {
			return REG_num_value_v(keyBody, names);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static int REG_num_value_v(BytePtr keyBody, IEnumerable<int> names) {
			return REG_num_value(REG_open_key_v(keyBody, names));
		}
		//-----------------------------------------------------------------------------
		public static int REG_num_string_l(BytePtr keyBody, params int[] names) {
			return REG_num_string_v(keyBody, names);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static int REG_num_string_v(BytePtr keyBody, IEnumerable<int> names) {
			return REG_num_string(REG_open_key_v(keyBody, names));
		}
		//-----------------------------------------------------------------------------
		public static int REG_num_blob_l(BytePtr keyBody, params int[] names) {
			return REG_num_blob_v(keyBody, names);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static int REG_num_blob_v(BytePtr keyBody, IEnumerable<int> names) {
			return REG_num_blob(REG_open_key_v(keyBody, names));
		}
		//*****************************************************************************
		//	BLOBのデータ構造について
		//*****************************************************************************
		public static int REG_get_blob_size(VoidPtr _blob/*null可*/) {
			BytePtr blob = _blob;
			int size;
			if(!blob) { return 0; }	//nullポインタが渡された場合は(size=0)を返します。REG_get_blob()の結果がnullかどうかを確認せずにREG_get_blob_size()に渡しても安全です。
			blob  -= 3;
			size   = blob.Read();	//┐
			size <<= 8;		//│
			size  |= blob.Read();	//├符号無し24ビットBE
			size <<= 8;		//│
			size  |= blob.Read();	//┘
			size++;			//(バイト数-1)⇒バイト数に変換する。
			return size;
		}
	}
}
