/*	
 *	DxlibHelper.cpp
 *
 *	DxLibヘルパー関数
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2015-2016 Naoyuki Sawa
 *
 *	* Mon Dec 21 21:11:43 JST 2015 Naoyuki Sawa
 *	- 1st リリース。
 *	  DxLibの関数を呼び出すために、当モジュールはC++としてコンパイルされる必要が有ります。
 *	  ただし、当モジュールが提供する関数は、Cリンケージで問題ありません。
 *	* Tue Jan 12 16:36:58 JST 2016 Naoyuki Sawa
 *	- 全ての関数に色指定の引数を追加しました。
 *	  色指定の引数は、D3DCOLOR形式(0x??RRGGBB)で指定して下さい。
 *	  D3DCOLOR形式とDxLibのGetrgb()形式は、必ずしも一致するとは限らない事に注意して下さい。
 *	* Wed Jan 13 21:47:42 JST 2016 Naoyuki Sawa
 *	- Direct3D互換のD3DCOLOR形式マクロを追加しました。
 *	- DxLib用の描画関数ラッパーを追加しました。
 *	* Mon Jan 25 21:57:35 JST 2016 Naoyuki Sawa
 *	- play_grp(),stop_grp(),auto_free_grp()を追加しました。
 *	  ムービーグラフィックの再生状態を管理する関数です。
 *	- アプリケーションは、draw_grp()等でムービーグラフィックを描画する場合に、毎フレームplay_grp()を呼び出して下さい。
 *	  毎フレームに一回、auto_free_grp()を呼び出して下さい。
 *	  普通の使い方では、stop_grp()を呼び出す必要は有りません。
 *	- draw_ext()の引数を変更しました。
 *	  任意の位置を中心に、拡縮出来るようになりました。
 *	* Tue Jan 26 21:39:05 JST 2016 Naoyuki Sawa
 *	- play_grp()で再生開始直後、少なくとも最初のムービーフレームがデコードされるまで待つ事にしました。
 *	  最初のムービーフレームがデコードされる前にこのグラフィックハンドルを使ってDrawGraph()等を呼び出すと、ゴミが描画されてしまう問題が生じるからです。
 *	  尚、上記の待ち処理は、再生開始直後のみに限定するのが適切です。
 *	  もし常に毎フレーム待ち処理を行うと、極端に動作が重くなります。
 *	* Sun Jan 31 21:43:46 JST 2016 Naoyuki Sawa
 *	- 特に良く使う描画関数の、優先度付きバージョンを定義しました。
 *	  他にも優先度付き描画を行いたい描画関数が有れば、このモジュールに追加して下さい。
 *	* Mon Feb 01 21:19:50 JST 2016 Naoyuki Sawa
 *	- draw_spr(),draw_spr2()を追加しました。
 *	- CbkQue_draw_spr(),CbkQue_draw_spr2()を追加しました。
 *	* Wed Feb 03 21:48:04 JST 2016 Naoyuki Sawa
 *	- draw_box()を、draw_box()とdraw_box_fill()に分けました。
 *	  draw_oval()を、draw_oval()とdraw_oval_fill()に分けました。
 *	- CbkQue_draw_box()を、CbkQue_draw_box()とCbkQue_draw_box_fill()に分けました。
 *	  CbkQue_draw_oval()を、CbkQue_draw_oval()とCbkQue_draw_oval_fill()に分けました。
 *	- 分けた理由は、塗り潰す方を使う事が大半なので、パラメータで指定するよりも関数を分ける方が使い易いからです。
 *	* Wed Feb 03 22:37:19 JST 2016 Naoyuki Sawa
 *	- load_grp()とfree_grp()も/clip/test.win/cpp.cpp⇒/clip/embedfont.cppへ移しました。
 *	  load_grp()とfree_grp()を、アプリケーションが直接呼び出す必要は無いので、embedfont.cppのローカル関数としました。
 *	- 今後は、アプリケーション側で、load_grp()とfree_grp()を定義する必要は有りません。
 *	  その代わりに、必要に応じてfnPreLoadGraph()を定義して下さい。
 *	  詳細は、ST_TexInf構造体と、load_grp()の実装を参照して下さい。
 *	* Wed Feb 03 22:03:58 JST 2016 Naoyuki Sawa
 *	- get_sprDef()を追加しました。
 *	  □使用例:ムービーをスプライトとして描画するには、以下のように処理して下さい。		⇒{{2016/02/05追記:play_spr(),stop_spr()を使えばもっと簡単に書けるようになりました。}}
 *	  │ST_SprDef stSprDef;
 *	  │get_sprDef(iSpr, &stSprDef); //スプライトのグラフィック番号を取得する。
 *	  │play_grp(stSprDef.iTexNo);   //ムービーを再生,又は,再生継続する。
 *	  │draw_spr(x, y, iSpr);        //ムービーをスプライトとして描画する。
 *	- PlayMovieToGraph()でムービーを再生中のグラフィックを、DrawRectRotaGraph3()で描画しようとした時の問題を、回避する処理を追加しました。
 *	  詳細は、draw_spr2()関数内のコメントを参照して下さい。
 *	* Fri Feb 05 21:49:20 JST 2016 Naoyuki Sawa
 *	- play_spr()とstop_spr()を追加しました。
 *	  これまでもget_sprDef()とplay_grp(),及び,stop_grp()を組み合わせれば可能ではあったのですが、使用頻度が高いので手間を省くためにユーティリティ関数として用意する事にしました。
 *	  □使用例:ムービーをスプライトとして描画するには、以下のように処理して下さい。
 *	  │play_spr(iSpr);       //ムービーを再生,又は,再生継続する。
 *	  │draw_spr(x, y, iSpr); //ムービーをスプライトとして描画する。
 *	* Sun Feb 14 21:59:00 JST 2016 Naoyuki Sawa
 *	- DxLib用のサウンド関数ラッパーを追加しました。
 *	  これまでは/clip/test.win/cpp.cppの中に置いていましたが、ある程度安定したので当モジュールの中へ移す事にしました。
 *	* Wed Feb 17 21:39:25 JST 2016 Naoyuki Sawa
 *	- DxLib用のサウンド関数ラッパーを修正し、TAPセットで複数の論理チャネルが同一のデバイスチャネルを共有した場合も、正しく動作するようにしました。
 *	  これまでは、DxLibにデバイスチャネルの概念が無い事を考慮していなかったので、TAPセットで論理チャネル:デバイスチャネルが1:1の場合しか正しく動作していませんでした。
 *	  今回、デバイスチャネルを当モジュール内で擬似的に管理するようにして、TAPセットで複数の論理チャネルが同一のデバイスチャネルを共有した場合も、正しく動作するようになりました。
 *	  詳細は、DxLib用のサウンド関数ラッパーの実装部を参照して下さい。
 *	  ちなみに、P/ECEの場合は、P/ECEカーネル,又は,CLiPライブラリのサウンド関数にデバイスチャネルの概念が有るので、上記のような擬似的な管理は不要です。
 *	- GetSubMonitorRect()を追加しました。
 *	* Thu Feb 18 21:49:28 JST 2016 Naoyuki Sawa
 *	- draw_ext()の引数を変更しました。
 *	  拡大縮小を、縦横別々に指定出来るようにしました。
 *	* Fri Feb 19 21:59:00 JST 2016 Naoyuki Sawa
 *	- draw_ext()の引数を変更しました。
 *	  これまでは拡縮だけでしたが、回転も出来るようにしました。
 *	  詳細は、draw_ext()の実装箇所のコメントを参照して下さい。
 *	* Fri Feb 26 21:51:12 JST 2016 Naoyuki Sawa
 *	- ST_TexInf.fnPreDrawGraphを追加しました。
 *	  このコールバックを用いる事によって、アプリケーション内の処理で、play_grp()を一か所に纏める事が出来ます。
 *	  それ以外にも、描画の直前に、描画するテクスチャに応じて、描画モードを切り替えるような利用方法も可能です。
 *	- このコールバックで通知されるのは、'テクスチャ番号'であって、'スプライト番号'ではない事に注意して下さい。
 *	  スプライトシートを用いている場合は、このコールバック内で、スプライトシート内の個々のスプライトを識別する事は出来ません。
 *	  とはいえ、このコールバックを追加した目的の大半は、前述のplay_grp()を一か所に纏める目的なので、現在の所は支障有りません。
 *	* Sun Feb 28 23:18:54 JST 2016 Naoyuki Sawa
 *	- デバッグビルド時の、デバッグメッセージの出力先を変更しました。
 *	  変更前は直接stderrへ出力していましたが、変更後はsyslog(LOG_DEBUG,～)へ出力するようにしました。
 *	  ・変更前と同様にstderrにも表示したい場合は、アプリケーションが'openlog(NULL,LOG_PERROR,LOG_USER)'のように設定して下さい。
 *	  ・デバッグにビルド時でも、デバッグメッセージを抑制したい場合は、アプリケーションが'setlogmask(LOG_UPTO(LOG_INFO))'のように設定して下さい。
 *	  尚、リリースビルド時は、これまでと同じく、デバッグメッセージの出力自体を行いません。
 *	* Wed Mar 09 21:18:31 JST 2016 Naoyuki Sawa
 *	- 「Microsoft Platform SDK Febuary 2003 (Last version with VC6 support)」を導入した事に伴い、WINVERと_WIN32_WINNTの定義を、0x0400⇒0x0502に変更しました。
 *	  詳細は、clipstd.h の同日のコメントを参照して下さい。
 *	  それによって、当モジュール内でEnumDisplayMonitors()を明示的に宣言する必要が無くなり、EnumDisplayMonitors()の明示的な宣言を削除しました。
 *	* Fri Mar 11 21:58:48 JST 2016 Naoyuki Sawa
 *	- ファイル名を、embedfont.h,embedfont.cpp⇒DxlibHelper.h,DxlibHelper.cppに変更しました。
 *	  ファイル名を変更した理由は、元々当モジュールは埋め込みテクスチャフォント描画の機能だけを提供していたのですが、その後の関数追加によってDxLib用のヘルパー関数全般を実装するモジュールになり、ファイル名と実態が一致しなくなっていたからです。
 *	  今回、ファイル名を変更した事によって、ファイル名と実態が一致するようになりました。
 *	* Wed Mar 23 21:50:41 JST 2016 Naoyuki Sawa
 *	- get_grp_total_time(),get_spr_total_time()を追加しました。
 *	  2016/03/23現在の所、ogv専用です。
 *	  もし今後、ogv以外(avi等)のムービーに対して当関数を呼び出す必要が生じたら、ogv以外(avi等)のムービーに対する処理を実装して下さい。
 *	  詳細は、get_grp_total_time()関数内のコメントを参照して下さい。
 *	- 今回追加した関数は、DxLibの関数GetMovieTotalFrameToGraph()とGetOneFrameTimeMovieToGraph()をラッピングしたものです。
 *	  追加した目的は、実行時にムービーの長さを求めて、再生し終わるまで待つ場合などに使用するためです。
 *	  別の方法として、GetMovieStateToGraph()を使って待つ方法も考えましたが、その方法は使い辛い事が判ったので、GetMovieStateToGraph()はラッピングしませんでした。
 *	  GetMovieStateToGraph()が使い辛い理由は、CbkQue_draw_grp()等を使ってムービーを再生している場合、実際に再生開始するのがそのフレームの終了時まで遅延してしまい、そのフレームの中で判断を行うと'停止済み'と判断してしまうからです。
 *	  それを回避しようとするとフラグ管理が複雑になってバグの原因になりそうなので、それよりも単純にムービーの総時間を求めてその時間ぶん待つ方が良いと思いました。
 *	* Sun Apr 03 21:51:26 JST 2016 Naoyuki Sawa
 *	- Sofdecムービー対応を追加しました。
 *	  Sofdecムービー対応を有効にしてビルドするには、DxlibHelper.cpp内のUSE_SOFDECシンボルを定義して下さい。
 *	- 2016/04/03現在、/clip/test.win/app.h,及び,cpp.cppにもUSE_SOFDECシンボルが有りますが、全く別物で有る事に注意して下さい。
 *	  DxlibHelper.cppのUSE_SOFDECシンボルは、ライブラリとしてSofdecムービー対応を有効にするかどうかの切り替えです。
 *	  /clip/test.win/app.h,及び,cpp.cppのUSE_SOFDECシンボルは、アプリケーションがSofdecムービーを使用するかどうかの切り替えです。
 *	- Sofdecムービーを使用する場合は、アプリケーション側でSofdecHelper_Init(),SofdecHelper_Exit(),SofdecHelper_Exec()を呼び出して下さい。
 *	  Sofdecムービー対応を有効にしていても、実際にSofdecムービーを使用しない場合は、アプリケーション側でSofdecHelper_Init(),SofdecHelper_Exit(),SofdecHelper_Exec()を呼び出す必要は有りません。
 *	* Mon Apr 04 21:27:10 JST 2016 Naoyuki Sawa
 *	- get_grp_total_time()に、Sofdecムービー対応の処理を追加しました。
 *	* Wed Apr 06 21:53:01 JST 2016 Naoyuki Sawa
 *	- ST_TexInf構造体に、name_Loopフィールドを追加しました。
 *	  play_grp(),及び,play_spr()関数の、bLoop引数を削除しました。
 *	  play_grp()が自動的に、レジストリデータのテクスチャ定義の、ループフラグを参照するように変更しました。
 *	  もうアプリケーションのプログラム内で、ループムービーか非ループムービーかを指定する必要は有りません。
 *	* Thu Apr 07 21:58:31 JST 2016 Naoyuki Sawa
 *	- ST_TexInf.fnPreDrawGraphを廃止しました。
 *	  play_grp()をローカル変数に変更しました。play_spr()を削除しました。
 *	- ムービーグラフィックを参照する都度(具体的には内部関数load_grp()が呼ばれた時)、内部で自動的にplay_grp()の処理を行うようにしました。
 *	  もう、アプリケーションが明示的にplay_grp()を呼び出す必要は有りません。
 *	  このように変更した理由は、一つはアプリケーションの手間を省くためですが、もう一つ、重要な理由が有ります。
 *	  これまでは、ムービーグラフィックをロードしたにもかかわらずplay_grp()を呼び出さなかった場合、ムービーグラフィックを自動的に破棄する契機が無くなり、ムービーグラフィックが残ってしまう問題が有りました。
 *	  静止画グラフィックならば問題無いのですが、ムービーグラフィックはDxLibの制限で同時に21個位しか存在出来ないようなので(仕様上は100個だが実際に試すと21個位でエラーになる)、残ってしまうと問題が生じます。
 *	  今後は、ムービーグラフィックをロードした瞬間にplay_grp()の処理を行い、その後、ムービーグラフィックを描画しなくなったら自動的に破棄しますので、ムービーグラフィックが残ってしまう問題は無くなりました。
 *	- TBL_PlayingGrp[]のサイズを、MAX_MOVIE_NUM⇒16 に変更しました。
 *	  変更した理由は、以下の通りです。
 *	  これまで、TBL_PlayingGrp[]のサイズを、MAX_MOVIE_NUM(=100)としていました。
 *	  しかし、DxLibの仕様上はMAX_MOVIE_NUM(=100)個のムービーを同時に扱えるはずですが、実際にはムービーサイズに関係無く21個ぐらいでテクスチャが作成出来ないエラーになるようです。(これは前々から気付いていました)
 *	  従って、TBL_PlayingGrp[]のサイズが100もあっても、ほとんど無駄でした。
 *	  本日load_grp()が毎回play_grp()を呼び出すように変更した事に伴い、TBL_PlayingGrp[]のサイズが無駄に大きいと全体への負荷が高くなります。(とは言え100要素リニア比較する程度なので、現在のCPUではほとんど問題無いとは思いますが…)
 *	  そこで、TBL_PlayingGrp[]のサイズを、MAX_MOVIE_NUM⇒16 に変更する事にしました。
 *	  実際には16個もムービーを同時再生する事は無いと思うので、実質アプリケーションへの影響は無いと思います。
 *	* Wed Apr 20 10:51:26 JST 2016 Naoyuki Sawa
 *	- USE_DRAWROTAGRAPHFASTシンボルが定義されたら、DrawRotaGraph系の関数のFastバージョンを使用するようにしました。
 *	  Fastバージョンの関数は、DxLib Ver 3.16aで追加された関数です。
 *	  座標計算のアルゴリズムが簡略化され、±1ピクセル程度の描画誤差が生ずる代わりに、処理負荷が抑えられています。
 *	- draw_mod(),draw_spr3(),CbkQue_draw_mod(),CbkQue_draw_spr3()を追加しました。
 *	* Thu Apr 28 21:03:22 JST 2016 Naoyuki Sawa
 *	- frambpe.cにてBytePairEncodingDecoder_Decode()の引数を追加した事に伴い、当モジュール内のBytePairEncodingDecoder_Decode()を呼び出している個所にも引数を追加しました。
 *	  当モジュールの動作自体には変更有りません。
 *	* Wed May 04 21:28:21 JST 2016 Naoyuki Sawa
 *	- ADX対応を追加しました。
 *	  init_snd()の_use_adx引数に0を指定して初期化すれば、これまで通りDxlibのサウンド機能を使ってサウンドを再生します。
 *	  init_snd()の_use_adx引数に1を指定して初期化すれば、ADXのサウンド機能を使ってサウンドを再生します。
 *	  ADXのサウンド機能を使う場合、init_snd()を呼び出すより前に、アプリケーション側でSofdecHelper_Init()の引数の上位16ビットにチャネル数を指定して初期化しておいて下さい。
 *	  上記以外のプログラムインターフェイスはこれまでと同じであり、アプリケーションはTAPシーケンサとTAPミキサーの関数を介してサウンドを再生して下さい。
 *	- Dxlibのサウンド機能よりも、ADXのサウンド機能の方が、直観的であり使い易いです。
 *	  DxlibはDirectSound由来の構造になっているためか(?)フレーズとチャネルが一体化している使い辛さがあり、現在の当モジュールの使い方では一種類のフレーズを複数のチャネルで同時に鳴らせません。
 *	  それに対して、ADXのサウンド機能はフレーズとチャネルが分離している判り易い構造であり(P/ECEも同様です)、一種類のフレーズを複数のチャネルで鳴らす処理も自然に行えます。
 *	  ただし、ADXは圧縮サウンドファイルの形式が特殊な独自形式で、データを作り辛いという欠点が有ります。(CRIの開発環境のcriatomencd.exe等はフリーソフトではないため)
 *	- そんなわけで、Dxlibのサウンド機能にもADXのサウンド機能にもそれぞれ利点欠点が有るため、当モジュールはどちらも対応出来るようにしておきました。
 *	  どちらを使うかで圧縮サウンドファイルの形式を変える必要が有るので、データを作り始める時にどちらかを選択する方が良いと思います。
 *	* Mon Apr 25 22:45:55 JST 2016 Naoyuki Sawa
 *	- DxLib Ver 3.16c(2016/05/05版)で、GetMovieBaseImageToGraph()にImageUpdateFlagSetOnly引数が追加された事に伴い、ImageUpdateFlagSetOnly=TRUEにして呼び出すように変更しました。
 *	* Mon May 09 21:19:29 JST 2016 Naoyuki Sawa
 *	- ADXのサウンド機能を完全に切り離してビルド出来るようにしました。(前回までは常にSofdecHelper.dllをリンクしてしまっていました。)
 *	  USE_ADXとUSE_SOFDECをどちらも定義しなければ、SofdecHelper.dllが無くても当ライブラリをビルド出来、SofdecHelper.dllが無くてもアプリケーションを実行出来ます。
 *	  純粋にDxLibだけを使うアプリケーションを作成する場合は、上記のようにして当ライブラリをビルドしておいて下さい。
 *	- ADXのサウンド機能対応を有効にしてビルドするには、DxlibHelper.cpp内のUSE_ADXシンボルを定義して下さい。
 *	  2016/05/09現在、/clip/test.win/app.h,及び,cpp.cppにもUSE_ADXシンボルが有りますが、全く別物で有る事に注意して下さい。
 *	  DxlibHelper.cppのUSE_ADXシンボルは、ライブラリとしてADXのサウンド機能対応を有効にするかどうかの切り替えです。
 *	  /clip/test.win/app.h,及び,cpp.cppのUSE_ADXシンボルは、アプリケーションがADXのサウンド機能を使用するかどうかの切り替えです。
 *	* Sun May 15 21:18:41 JST 2016 Naoyuki Sawa
 *	- 既存のauto_free_grp()関数を、auto_stop_grp()に名前変更しました。
 *	  処理内容は変えていません。
 *	- 名前変更した理由は、この関数は「今回のフレームでplay_grp()が呼ばれなかったムービーグラフィックを自動的に停止する」関数であり、言い換えると「自動的にstop_grp()を行う」関数だからです。
 *	  変更前の名前では、「自動的にfree_grp()を行う」関数のように見えますが、実際にはそうではありません。
 *	  この関数は、ムービーグラフィックだけに関する処理であり、静止画グラフィックは開放しません。
 *	  上記のような混乱を避けるために、関数名が実態に合うように変更する事にしました。
 *	* Mon May 16 21:56:02 JST 2016 Naoyuki Sawa
 *	- free_all_res(),free_all_grp(),free_all_snd()を追加しました。
 *	  使用方法は、free_all_res(),free_all_grp(),free_all_snd()の実装部のコメントを参照して下さい。
 *	* Sun Jul 31 21:00:54 JST 2016 Naoyuki Sawa
 *	- Sofdecムービーの再生処理を、ARGBでなくYUVAを使用するようにして高速化しました。
 *	  やや大幅にコードを変更したので、変更前のコードはコメントアウトして残してありません。
 *	  変更前のコードを参照する必要が有る場合は、前回のアーカイブを参照して下さい。
 *	  尚、今回の当モジュールの変更による、グローバル関数仕様の変更は無く、アプリケーションのソースコードの修正は不要です。
 *	- 高速化した方法は、以下の通りです。
 *	  □変更前は、以下の流れで再生処理を行っていました。
 *	  │①Sofdec2のAPI criMana_InitializeFrameARGB32()を使って、デコード済みのYUVプレーンから、ARGB形式の基本イメージデータを取得する。
 *	  │  ・criMana_InitializeFrameARGB32()は、CPUによるYUVA⇒ARGB変換処理であり、かなり遅いようです。これが、速度低下の原因の一つになっていました。
 *	  │  ・criMana_InitializeFrameARGB32()は、バッファへ大量のメモリ書き込みも伴います。これも、速度低下の原因の一つになっていました。
 *	  │②Dxlib関数の、ReCreateGraphFromBaseImage()を使って、ARGB形式のテクスチャを更新します。
 *	  │  ・この時、Dxlib内部でカラーマッチング処理を経由しているか判らないのですが、結構遅いようです。これも、速度低下の原因の一つになっていました。
 *	  │③通常のグラフィック描画と同じ方法で、ムービーテクスチャを描画します。
 *	  □変更後は、以下の流れで再生処理を行うようになりました。
 *	  │①デコード済みのYUVプレーンを直接参照するようにしました。
 *	  │  ・Sofdec2のムービーデコード処理以外には、オーバーヘッドは生じなくなりました。
 *	  │②Dxlib関数の、ReCreateGraphFromBaseImage()を使って、YUVA形式のテクスチャを更新します。
 *	  │  ・UプレーンとVプレーンが少し小さい分、転送面積的に少し高速になりました。
 *	  │    また、この時、Dxlib内部でカラーマッチング処理は経由しないようですし、総合的にARGB形式のテクスチャ更新よりも高速なようです。
 *	  │③通常のグラフィック描画とは処理分けして、ピクセルシェーダを用いて、YUVA形式のムービーテクスチャを描画します。
 *	* Mon Aug 01 21:12:56 JST 2016 Naoyuki Sawa
 *	- DxLibがDirect3D9を選択した場合も、Sofdec2ムービーを再生出来るようにしました。
 *	- 「USE_SOFDEC_D3D9」シンボルを定義すると、DxLibがDirect3D9を選択した場合も、Sofdec2ムービーを再生出来るようにします。
 *	  「USE_SOFDEC_D3D9」シンボルを定義しなければ、DxLibがDirect3D11を選択した場合しか、Sofdec2ムービーを再生出来ません。
 *	  具体的には、以下の通りです。
 *	- 2016/07/31の変更により、Sofdec2ムービーの描画には、ピクセルシェーダを使用するようになりました。
 *	  ピクセルシェーダは、Direct3D9用,又は,Direct3D11用よって、異なるシェーダモデルを使う必要が有り、互換性が有りません。
 *	  2016/07/31の時点ではDirect3D11しか想定していなかったのですが、Direct3D9が選択されてしまうPCもたまに有るので、Direct3D9にも対応しておく事にしました。
 *	  例えば、Intel GMA X3000や、ATI Mobility Radeon HD 4200では、DxLibがDirect3D9を選択するようです。(D3D11のフィーチャーレベル不足のようです。)
 *	- 尚、今回のDirect3D9対応は、表示が出来る程度の対応であり、速度やメモリ効率は考慮していません。
 *	  DxLibにおいて、DX_GRAPHICSIMAGE_FORMAT_3D_ONE_I8はD3D11の場合はD_DXGI_FORMAT_R8_UNORMにマップされますが、D3D9の場合はA8R8G8B8にマップされるようです。
 *	  シェーダの処理的には同じ処理で同じ表示結果になるのですが、おそらく、メモリ効率も速度効率も悪いと思います。(詳細は未検証)
 *	  極力、DxLibがDirect3D11を選択する環境で、実行するようにして下さい。
 *	- もし今後、Direct3D9対応が不要になった場合は、「USE_SOFDEC_D3D9」シンボルを定義しないように変更して下さい。
 *	* Tue Aug 02 21:11:08 JST 2016 Naoyuki Sawa
 *	- 自然遷移通知を追加しました。
 *	  詳細は、cliptaps.c の同日のコメントを参照して下さい。
 *	- 2016/08/02現在はまだ、自然遷移通知を有効に利用しておらず、再生開始処理と同じ処理を行っています。
 *	  今後、当モジュールがADX2のシームレス連続再生機能に対応する時に、自然遷移通知を利用する予定です。
 *	  尚、SofdecHelper.dllは、2016/08/01時点でADX2のシームレス連続再生機能に対応済みです。
 *	* Tue Aug 02 22:11:15 JST 2016 Naoyuki Sawa
 *	- cliptimm.hのST_TimMgrFunc構造体を変更したことに伴い、当モジュールも変更しました。
 *	  詳細は、cliptimm.h の同日のコメントを参照して下さい。
 *	* Sun Aug 07 21:13:31 JST 2016 Naoyuki Sawa
 *	- DxLibにDrawOval_Rect()が追加された事に伴い、draw_oval_subr()が使用する関数を、DrawOval()⇒DrawOval_Rect()に変更しました。
 *	  draw_oval_subr()の描画における誤差が低減しましたが、見た目の動作は変更有りません。
 *	* Fri Aug 12 23:26:15 JST 2016 Naoyuki Sawa
 *	- draw_strf_handle(),draw_strv_handle(),draw_str_handle(),CbkQue_draw_strf_handle()を追加しました。
 *	  DrawExtendStringToHandle()を利用して、文字列を描画します。
 *	  DrawExtendStringToHandle()を利用する事にした理由は、以下の通りです。
 *	- これまで、DxLibのDrawString()系関数は遅いと思っていたので、避けていました。
 *	  その代わりに、埋め込みテクスチャフォントを使用して、draw_str()等で文字列を描画していました。
 *	  しかし、埋め込みテクスチャフォントは小さいので、大きな文字を描画した時に見栄えが悪い問題が有りました。
 *	  改めて試してみた所、DxLibのDrawString()系関数でフォントを変えながら描画すると確かに遅いのですが、DrawStringToHandle()系関数を使えば遅くない事が判りました。
 *	  そこで、DrawStringToHandle()系関数の一つである、DrawExtendStringToHandle()を使って文字列を描画する事にしました。
 *	- draw_str_handle()等のFontHandle引数には、CreateFontToHandle()で作成したフォントハンドルを直接指定して下さい。
 *	  CbkQue_draw_strf_handle()を呼び出す場合、実際に描画が行われるまで、フォントハンドルを削除しないように注意して下さい。
 *	- 今後は、埋め込みテクスチャフォントによる描画(draw_str()等)は敢えて使用する必要は無いかも知れませんが、これまで安定して動作していたので残しておく事にします。
 *	* Sun Aug 14 21:50:35 JST 2016 Naoyuki Sawa
 *	- draw_strf_handle(),draw_strv_handle(),draw_str_handle(),CbkQue_draw_strf_handle()に、引数(ax,ay)を追加しました。
 *	  文字列を描画するアンカーポイントを指定するための引数です。
 *	  例えば、(ax=0.5,ay=1.0)を指定すると、引数(x,y)で指定した座標が、文字列の左右中央,下端に一致するように、文字列を描画します。
 *	- draw_ext(),CbkQue_draw_ext()の、引数の順序を変更しました。
 *	  draw_strf_handle(),draw_strv_handle(),draw_str_handle(),CbkQue_draw_strf_handle()の引数(ax,ay)が、色を指定する引数の直前である事と同様に、
 *	  draw_ext(),CbkQue_draw_ext()の引数(ax,ay)も、色を指定する引数の直前にする方が判り易いと思ったからです。
 *	- draw_str(),draw_strv(),draw_strf(),draw_str_framed(),draw_strv_framed(),draw_strf_framed(),CbkQue_draw_strf(),CbkQue_draw_strf_framed()にも、引数(ax,ay)を追加しました。
 *	  使用方法は、draw_strf_handle()等と同じです。
 *	  詳細は、上記のコメントを参照して下さい。
 *	* Tue Aug 16 21:54:11 JST 2016 Naoyuki Sawa
 *	- DxLib用の埋め込みテクスチャフォント描画の処理を、DxlibHelper.cpp⇒DxlibHelper_EmbFnt.cppへ分離しました。
 *	* Fri Aug 19 21:29:24 JST 2016 Naoyuki Sawa
 *	- cliptaps.hのST_TapSeqInfo.TBL_TapSet[0]フィールドを廃止した事に伴い、DxlibHelper.cpp内のinit_snd()において、処理で明示的にアドレスを求めるようにしました。
 *	  フィールドの宣言を廃止しただけで、メモリレイアウトは変わっていません。
 *	  詳細は、cliptaps.h,及び,cliptaps.cの、同日のコメントを参照して下さい。
 *	* Wed Aug 31 23:56:52 JST 2016 Naoyuki Sawa
 *	- draw_grp_ts(),draw_spr_ts(),及び,CbkQue_draw_grp_ts(),CbkQue_draw_spr_ts()を追加しました。
 *	* Wed Sep 07 21:52:10 JST 2016 Naoyuki Sawa
 *	- 64ビット対応のための変更を行いました。
 *	  変更内容は、最近数日間に他のモジュールに対して行った変更と同様の明示的なキャストの追加等なので、今回は変更内容について詳細なコメントは記載しませんでした。
 *	  最近数日間の、他のモジュールに対して行った変更のコメントを参照して下さい。
 *	* Sat Sep 10 21:46:35 JST 2016 Naoyuki Sawa
 *	- テクスチャの自動スケーリングを追加しました。
 *	  主に動画のデコード負荷低減のために、データ作成時に1/Nのサイズに縮小してエンコードしておき、実行時にN倍して表示するために追加した処理です。
 *	  当面は上記の通り動画でしか利用する予定は有りませんが、静止画でも動作するはずなので、静止画の容量削減のために利用する事も出来ると思います。
 *	- 2016/09/10現在は、自動スケーリング対象であるかどうかは、グラフィックデータのファイル名によって判断しています。
 *	  具体的には、ファイル名の末尾が'-1x'～'-9x'ならば、エンコード時に1/1～1/9に縮小していると見なして、実行時に1倍～9倍にして表示します。
 *	  実際には2倍,4倍,8倍しか使わない予定ですが、処理としてはそれ以外の倍率も共通の処理です。
 *	- 上記のように、グラフィックデータのファイル名によって判断する方法は、実行時の文字列比較処理が多過ぎる問題が有ります。
 *	  まずは簡単に実装して検証するためにこのような方法を取りましたが、今後、レジストリに自動スケーリング値を格納する等、もっと効率の良い方法を検討する予定です。(※TODO:)
 *	  自動スケーリング値の判断方法を変更する場合は、本日追加したローカル関数GetAutoScale()の処理を変更するだけで変更出来ると思います。
 *	- draw_ext()関数がまだ、自動スケーリング未対応です。(※TODO:)
 *	  詳細は、draw_ext()関数内のコメントを参照して下さい。
 *	* Tue Sep 13 21:11:40 JST 2016 Naoyuki Sawa
 *	- 描画オフセット処理(SetDrawOffset())を追加しました。
 *	  全ての描画処理に対するオフセットを指定するための機能です。
 *	  優先度付きバージョンであるCbkQue_SetDrawOffset()を使用すれば、特定の優先度の範囲の描画に対して個別にオフセットを適用する事も出来ます。
 *	* Sat Sep 17 21:29:14 JST 2016 Naoyuki Sawa
 *	- prep_grp(),prep_spr()を追加しました。
 *	  詳細は、/clip/tool/SofdecHelper/SofdecHelper.cの同日のコメントを参照して下さい。
 *	* Sun Sep 18 21:28:04 JST 2016 Naoyuki Sawa
 *	- prep_phr()を追加しました。
 *	  詳細は、/clip/tool/SofdecHelper/SofdecHelper.cの同日のコメントを参照して下さい。
 *	* Mon Sep 19 22:17:04 JST 2016 Naoyuki Sawa
 *	- SofdecHelper_Prepare()が戻り値を返すようになった事に伴い、prep_grp(),prep_spr()も戻り値を返すように変更しました。
 *	  戻り値の仕様は、/clip/tool/SofdecHelper/SofdecHelper.cの同日のコメントを参照して下さい。
 *	- AdxHelper_Prepare()が戻り値を返すようになった事に伴い、prep_phr()も戻り値を返すように変更しました。
 *	  戻り値の仕様は、/clip/tool/SofdecHelper/SofdecHelper.cの同日のコメントを参照して下さい。
 *	* Sat Sep 24 21:37:23 JST 2016 Naoyuki Sawa
 *	- draw_spr_ts()が誤って描画オフセットを二重に加算してしまっていたバグを修正しました。
 *	  draw_spr_ts()はdraw_grp_ts()を呼び出し、draw_grp_ts()が描画オフセットを加算するので、draw_spr_ts()での加算は不要でした。
 *	  ただし、draw_spr_ts_SofdecYUV()を呼び出す場合は、draw_spr_ts_SofdecYUV()は描画オフセットを加算しないので、draw_grp_ts()で描画オフセットを加算しておく必要があります。
 *	  従って今回の修正内容は、draw_spr_ts()内で描画オフセットを加算していた位置を、draw_spr_ts_SofdecYUV()を呼び出す直前へ変更しました。
 *	* Tue Oct 04 23:23:40 JST 2016 Naoyuki Sawa
 *	- draw_grp_thick_polyline(),draw_spr_thick_polyline(),及び,CbkQue_draw_grp_thick_polyline(),CbkQue_draw_spr_thick_polyline()を追加しました。
 *	* Wed Oct 05 21:20:31 JST 2016 Naoyuki Sawa
 *	- draw_grp_or_spr_thick_polyline()にて、テクスチャ座標の取得に無駄な平均計算を行っていたのを修正しました。
 *	* Fri Oct 07 21:38:15 JST 2016 Naoyuki Sawa
 *	- calc_thick_polyline_edge_p()に、長さ0の線分が指定された場合の安全対策を追加しました。
 *	  これまでは、draw_grp_or_spr_thick_polyline()に指定した引数の中に、長さ0の線分が含まれていた場合に、calc_thick_polyline_edge_p()の中で、ゼロ除算例外が発生していました。
 *	  今回、上記の対策を追加した事ににより、ゼロ除算例外が発生する事は無くなりました。
 *	  ただし、長さ0の線分の箇所で、太いポリラインの接合が途切れたような描画になってしまいますが、見た目にさほど不自然ではないし、ゼロ除算例外で停止するよりは良いと思います。
 *	  ＜例＞これまでは以下のコードでゼロ除算例外が発生していましたが、今後は発生しなくなりました。
 *	  │const int xy[][2]={{100,100},{300,100},{300,100},{300,500}};
 *	  │const double uv[][2][2]={{{0,0},{0,1}},{{0.3,0},{0.3,1}},{{0.7,0},{0.7,1}},{{1,0},{1,1}}};
 *	  │draw_spr_thick_polyline(xy,uv,4,50,SprNo_OPEN);
 *	* Wed Oct 12 21:38:09 JST 2016 Naoyuki Sawa
 *	- AdxHelperのプレイヤーidを、正しくは物理チャネル番号に対応付けて決定すべき所、間違って論理チャネル番号に対応付けて決定していたバグを修正しました。
 *	  たまたま、/clip/test.win/cpp.cppのTBL_TapSet[]の雛形では、論理チャネルと物理チャネルを1:1に割り当てていたので、上記のバグが顕在化しませんでした。
 *	  論理チャネルと物理チャネルをN:1に対応付けると、AdxHelperのプレイヤーidの上限を超えた物理チャネルに対して操作してしまう恐れが有りました。
 *	  上述の通り、まだ実際のアプリケーションではバグが顕在化していないのですが、明らかに誤りだったので、修正しておく事にしました。
 *	  尚、ADXを使用せずに、DxLibのサウンド機能を使用する場合(use_adx=0)は、元々、物理チャネルに対応付ける処理になっていたので、問題は有りませんでした。
 *	* Thu Oct 13 21:52:21 JST 2016 Naoyuki Sawa
 *	- TapSeq_fnPlay,TapSeq_fnStop,TapSeq_fnLoop,TapSeq_fnEnd,TapSeq_fnNext,TapSeq_fnCtrl,TapSeq_fnGetPhrTime,TapSeq_fnGetPhrNextの引数に、pTapSeqを追加しました。
 *	  詳細は、cliptaps.hの同日のコメントを参照して下さい。
 *	* Sun Oct 23 10:04:30 JST 2016 Naoyuki Sawa
 *	- draw_polygon(),draw_polygon_fill(),及び,CbkQue_draw_polygon(),CbkQue_draw_polygon_fill()を追加しました。
 *	* Mon Oct 24 21:50:37 JST 2016 Naoyuki Sawa
 *	- vec2i_split_polygon_into_triangles()の戻り値の意味を変更した事に伴い、draw_polygon_fill()の処理も変更しました。
 *	* Wed Nov 02 21:33:27 JST 2016 Naoyuki Sawa
 *	- play_phr()にて、AdxHelper_Play()で再生開始した直後に、set_att()を呼ぶのを忘れていたのを修正しました。
 *	  修正前は、ADXでの再生直後に、同じチャネルで前に鳴っていた音の音量が、次にミキサーの周期処理が実行されるまで継続してしまう問題が有りました。(一瞬ではありますが)
 *	  尚、DxLibを使う場合(oggファイル等を再生する場合)は、既にset_att()を呼び出していたので問題有りません。
 *	* Sat Nov 05 21:12:06 JST 2016 Naoyuki Sawa
 *	- draw_box_mc(),及び,CbkQue_draw_box_mc()を追加しました。
 *	  '/clip/nuklear/nuklear_dxlib.cpp'のnk_dxlib_draw_rect_multi_color()を本実装するために必要になったからです。
 */
#include "clip.h"
/****************************************************************************
 *	
 ****************************************************************************/
#define USE_DRAWROTAGRAPHFAST	//DrawRotaGraph系の関数のFastバージョンを使用しない場合はこの行をコメントアウトして下さい。
#define USE_AUTOSCALE		//テクスチャの自動スケーリングを使用しない場合はこの行をコメントアウトして下さい。
#define USE_DRAWOFFSET		//描画オフセットを使用しない場合はこの行をコメントアウトして下さい。
#define USE_SOFDEC		//Sofdecを使用しない場合はこの行をコメントアウトして下さい。
#define USE_ADX			//ADXを使用しない場合はこの行をコメントアウトして下さい。
/****************************************************************************
 *	ローカル関数宣言(前方参照を行う関数だけ)
 ****************************************************************************/
static void play_grp(int iGrp, int bCont/*0=再生開始,1=再生継続*/);
/****************************************************************************
 *	オートスケーリング
 ****************************************************************************/
#ifdef  USE_AUTOSCALE
//iTexNoの自動スケーリング値を取得する。
static int GetAutoScale(int iTexNo) {
	static ght_hash_table_t* ht_AutoScale;	//テクスチャ番号⇒自動スケーリング値
	//ハッシュテーブルが未作成ならば、作成する。
	if(!ht_AutoScale) { ght_set_rehash((ht_AutoScale = ght_create(0)), 1); }
	//既に自動スケーリング値が登録されているか?
#ifndef _WIN64
	int AutoScale = (int)ght_get(ht_AutoScale, sizeof iTexNo, &iTexNo);		//グラフィック番号⇒自動スケーリング値を取得する。	//32ビット版
#else //_WIN64
	int AutoScale = (intptr_t)ght_get(ht_AutoScale, sizeof iTexNo, &iTexNo);	//グラフィック番号⇒自動スケーリング値を取得する。	//64ビット版
#endif//_WIN64
	//まだ自動スケーリング値が登録されていなければ…
	if(!AutoScale) {
		//レジストリから、グラフィックデータのファイル名を取得する。
		const char* path = REG_get_string_l(stTexInf.pTexDef, iTexNo, stTexInf.name_Path, -1);
		if(!path) { DIE(); }
		//ファイル名の中に、'-1x'～'-9x'が有るか?(もっと厳密に検査する方が望ましいのだが、実用上はこれでも充分だろう。)
		regexp* prog = regcomp("-([1-9])x");
		if(regexec(prog, path)) {
			AutoScale = *prog->startp[1] - '0';	//ファイル名の中に'-1x'～'-9x'が有れば、自動スケーリング値と見なす。
		} else {
			AutoScale = 1;				//ファイル名の中に'-1x'～'-9x'が無ければ、1倍とする。
		}
		//正規表現を開放する。
		free(prog);
		//自動スケーリング値を登録する。
#ifndef _WIN64
		if(ght_insert(ht_AutoScale, (void*)AutoScale, sizeof iTexNo, &iTexNo)) { DIE(); }		//バグ	//32ビット版
#else //_WIN64
		if(ght_insert(ht_AutoScale, (void*)(intptr_t)AutoScale, sizeof iTexNo, &iTexNo)) { DIE(); }	//バグ	//64ビット版
#endif//_WIN64
	}
	//自動スケーリング値を返す。
	return AutoScale;
}
#endif//USE_AUTOSCALE
/****************************************************************************
 *	描画オフセット
 ****************************************************************************/
#ifdef  USE_DRAWOFFSET
static int	DrawOffsetX;	//全ての描画処理に対するオフセットX
static int	DrawOffsetY;	//全ての描画処理に対するオフセットY
/*--------------------------------------------------------------------------*/
//全ての描画処理に対するオフセットを設定する。
void SetDrawOffset(int x, int y) {
	DrawOffsetX	= x;	//全ての描画処理に対するオフセットXを格納する。
	DrawOffsetY	= y;	//全ての描画処理に対するオフセットYを格納する。
}
/*--------------------------------------------------------------------------*/
#ifdef  __CLIP_CBKQ_H__
struct ST_CbkQue_SetDrawOffset {
	int		x;
	int		y;
};
static void fn_CbkQue_SetDrawOffset(ST_CbkQue* pCbkQue, int pri, void* param) {
	struct ST_CbkQue_SetDrawOffset* p = (struct ST_CbkQue_SetDrawOffset*)param;
	SetDrawOffset(p->x, p->y);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//□SetDrawOffset()の優先度付きバージョン
void CbkQue_SetDrawOffset(ST_CbkQue* pCbkQue, int pri, int x, int y) {
	struct ST_CbkQue_SetDrawOffset* p = (struct ST_CbkQue_SetDrawOffset*)CbkQue_AddMem(pCbkQue, pri, fn_CbkQue_SetDrawOffset, sizeof(struct ST_CbkQue_SetDrawOffset));
	p->x		= x;
	p->y		= y;
}
#endif//__CLIP_CBKQ_H__
#endif//USE_DRAWOFFSET
/****************************************************************************
 *	優先度付き描画
 ****************************************************************************/
#ifdef  __CLIP_CBKQ_H__
struct ST_CbkQue_SetDrawArea {
	int		x1;
	int		y1;
	int		x2;
	int		y2;
};
static void fn_CbkQue_SetDrawArea(ST_CbkQue* pCbkQue, int pri, void* param) {
	struct ST_CbkQue_SetDrawArea* p = (struct ST_CbkQue_SetDrawArea*)param;
	if(SetDrawArea(p->x1, p->y1, p->x2, p->y2)) { DIE(); }
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//□SetDrawArea()の優先度付きバージョン
void CbkQue_SetDrawArea(ST_CbkQue* pCbkQue, int pri, int x1, int y1, int x2, int y2) {
	struct ST_CbkQue_SetDrawArea* p = (struct ST_CbkQue_SetDrawArea*)CbkQue_AddMem(pCbkQue, pri, fn_CbkQue_SetDrawArea, sizeof(struct ST_CbkQue_SetDrawArea));
	p->x1		= x1;
	p->y1		= y1;
	p->x2		= x2;
	p->y2		= y2;
}
/*--------------------------------------------------------------------------*/
static void fn_CbkQue_SetDrawAreaFull(ST_CbkQue* pCbkQue, int pri, void* param) {
	if(SetDrawAreaFull()) { DIE(); }
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//□SetDrawAreaFull()の優先度付きバージョン
void CbkQue_SetDrawAreaFull(ST_CbkQue* pCbkQue, int pri) {
	CbkQue_Add(pCbkQue, pri, fn_CbkQue_SetDrawAreaFull, NULL);
}
/*--------------------------------------------------------------------------*/
struct ST_CbkQue_draw_grp {
	int		x;
	int		y;
	int		iGrp;
	int		rgba;
	int		mode;
};
static void fn_CbkQue_draw_grp(ST_CbkQue* pCbkQue, int pri, void* param) {
	struct ST_CbkQue_draw_grp* p = (struct ST_CbkQue_draw_grp*)param;
	draw_grp(p->x, p->y, p->iGrp, p->rgba, p->mode);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//□draw_grp()の優先度付きバージョン
void CbkQue_draw_grp(ST_CbkQue* pCbkQue, int pri, int x, int y, int iGrp, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
	struct ST_CbkQue_draw_grp* p = (struct ST_CbkQue_draw_grp*)CbkQue_AddMem(pCbkQue, pri, fn_CbkQue_draw_grp, sizeof(struct ST_CbkQue_draw_grp));
	p->x		= x;
	p->y		= y;
	p->iGrp		= iGrp;
	p->rgba		= rgba;
	p->mode		= mode;
}
/*--------------------------------------------------------------------------*/
struct ST_CbkQue_draw_ext {
	int		x;
	int		y;
	int		iGrp;
	double		scale_x;
	double		scale_y;
	double		rot;
	double		ax;
	double		ay;
	int		rgba;
	int		mode;
};
static void fn_CbkQue_draw_ext(ST_CbkQue* pCbkQue, int pri, void* param) {
	struct ST_CbkQue_draw_ext* p = (struct ST_CbkQue_draw_ext*)param;
	draw_ext(p->x, p->y, p->iGrp, p->scale_x, p->scale_y, p->rot, p->ax, p->ay, p->rgba, p->mode);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//□draw_ext()の優先度付きバージョン
void CbkQue_draw_ext(ST_CbkQue* pCbkQue, int pri, int x, int y, int iGrp, double scale_x, double scale_y, double rot, double ax, double ay, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
	struct ST_CbkQue_draw_ext* p = (struct ST_CbkQue_draw_ext*)CbkQue_AddMem(pCbkQue, pri, fn_CbkQue_draw_ext, sizeof(struct ST_CbkQue_draw_ext));
	p->x		= x;
	p->y		= y;
	p->iGrp		= iGrp;
	p->scale_x	= scale_x;
	p->scale_y	= scale_y;
	p->rot		= rot;
	p->ax		= ax;
	p->ay		= ay;
	p->rgba		= rgba;
	p->mode		= mode;
}
/*--------------------------------------------------------------------------*/
struct ST_CbkQue_draw_mod {
	int		x1;
	int		y1;
	int		x2;
	int		y2;
	int		x3;
	int		y3;
	int		x4;
	int		y4;
	int		iGrp;
	int		rgba;
	int		mode;
};
static void fn_CbkQue_draw_mod(ST_CbkQue* pCbkQue, int pri, void* param) {
	struct ST_CbkQue_draw_mod* p = (struct ST_CbkQue_draw_mod*)param;
	draw_mod(p->x1, p->y1, p->x2, p->y2, p->x3, p->y3, p->x4, p->y4, p->iGrp, p->rgba, p->mode);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//□draw_mod()の優先度付きバージョン
void CbkQue_draw_mod(ST_CbkQue* pCbkQue, int pri, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, int iGrp, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
	struct ST_CbkQue_draw_mod* p = (struct ST_CbkQue_draw_mod*)CbkQue_AddMem(pCbkQue, pri, fn_CbkQue_draw_mod, sizeof(struct ST_CbkQue_draw_mod));
	p->x1		= x1;
	p->y1		= y1;
	p->x2		= x2;
	p->y2		= y2;
	p->x3		= x3;
	p->y3		= y3;
	p->x4		= x4;
	p->y4		= y4;
	p->iGrp		= iGrp;
	p->rgba		= rgba;
	p->mode		= mode;
}
/*--------------------------------------------------------------------------*/
struct ST_CbkQue_draw_grp_ts {
	int		n;
	int		iGrp;
	int		rgba;
	int		mode;
    //	int		xy[/*n*/][2/*x,y*/];
    //	double		uv[/*n*/][2/*u,v*/];
};
static void fn_CbkQue_draw_grp_ts(ST_CbkQue* pCbkQue, int pri, void* param) {
	struct ST_CbkQue_draw_grp_ts* p = (struct ST_CbkQue_draw_grp_ts*)param;
	int    (*_xy)[2/*x,y*/] = (int    (*)[2/*x,y*/])(  p +    1);
	double (*_uv)[2/*u,v*/] = (double (*)[2/*u,v*/])(_xy + p->n);
	draw_grp_ts(_xy, _uv, p->n, p->iGrp, p->rgba, p->mode);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//□draw_grp_ts()の優先度付きバージョン
void CbkQue_draw_grp_ts(ST_CbkQue* pCbkQue, int pri, const int xy[/*n*/][2/*x,y*/], const double uv[/*n*/][2/*u,v*/], int n, int iGrp, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
	struct ST_CbkQue_draw_grp_ts* p = (struct ST_CbkQue_draw_grp_ts*)CbkQue_AddMem(pCbkQue, pri, fn_CbkQue_draw_grp_ts, sizeof(struct ST_CbkQue_draw_grp_ts) + (sizeof(int[2/*x,y*/]) * n) + (sizeof(double[2/*u,v*/]) * n));
	int    (*_xy)[2/*x,y*/] = (int    (*)[2/*x,y*/])(  p + 1);
	double (*_uv)[2/*u,v*/] = (double (*)[2/*u,v*/])(_xy + n);
	p->n		= n;
	p->iGrp		= iGrp;
	p->rgba		= rgba;
	p->mode		= mode;
	memcpy(_xy, xy, sizeof(   int[2/*x,y*/]) * n);
	memcpy(_uv, uv, sizeof(double[2/*u,v*/]) * n);
}
/*--------------------------------------------------------------------------*/
struct ST_CbkQue_draw_box {
	int		x;
	int		y;
	int		w;
	int		h;
	int		rgba;
	int		mode;
	int		fill;
};
static void fn_CbkQue_draw_box(ST_CbkQue* pCbkQue, int pri, void* param) {
	struct ST_CbkQue_draw_box* p = (struct ST_CbkQue_draw_box*)param;
	(*(p->fill ? draw_box_fill : draw_box))(p->x, p->y, p->w, p->h, p->rgba, p->mode);
}
static void CbkQue_draw_box_subr(ST_CbkQue* pCbkQue, int pri, int x, int y, int w, int h, int rgba, int mode, int fill) {
	struct ST_CbkQue_draw_box* p = (struct ST_CbkQue_draw_box*)CbkQue_AddMem(pCbkQue, pri, fn_CbkQue_draw_box, sizeof(struct ST_CbkQue_draw_box));
	p->x		= x;
	p->y		= y;
	p->w		= w;
	p->h		= h;
	p->rgba		= rgba;
	p->mode		= mode;
	p->fill		= fill;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//□draw_box()の優先度付きバージョン
void CbkQue_draw_box(ST_CbkQue* pCbkQue, int pri, int x, int y, int w, int h, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
	CbkQue_draw_box_subr(pCbkQue, pri, x, y, w, h, rgba, mode, 0/*fill*/);
}
//□draw_box_fill()の優先度付きバージョン
void CbkQue_draw_box_fill(ST_CbkQue* pCbkQue, int pri, int x, int y, int w, int h, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
	CbkQue_draw_box_subr(pCbkQue, pri, x, y, w, h, rgba, mode, 1/*fill*/);
}
/*--------------------------------------------------------------------------*/
struct ST_CbkQue_draw_box_mc {
	int		x;
	int		y;
	int		w;
	int		h;
	int		rgba1;
	int		rgba2;
	int		rgba3;
	int		rgba4;
	int		mode;
};
static void fn_CbkQue_draw_box_mc(ST_CbkQue* pCbkQue, int pri, void* param) {
	struct ST_CbkQue_draw_box_mc* p = (struct ST_CbkQue_draw_box_mc*)param;
	draw_box_mc(p->x, p->y, p->w, p->h, p->rgba1, p->rgba2, p->rgba3, p->rgba4, p->mode);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//□CbkQue_draw_box_mc()の優先度付きバージョン
void CbkQue_draw_box_mc(ST_CbkQue* pCbkQue, int pri, int x, int y, int w, int h, int rgba1/*左上*/, int rgba2/*右上*/, int rgba3/*右下*/, int rgba4/*左下*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
	struct ST_CbkQue_draw_box_mc* p = (struct ST_CbkQue_draw_box_mc*)CbkQue_AddMem(pCbkQue, pri, fn_CbkQue_draw_box_mc, sizeof(struct ST_CbkQue_draw_box_mc));
	p->x		= x;
	p->y		= y;
	p->w		= w;
	p->h		= h;
	p->rgba1	= rgba1;
	p->rgba2	= rgba2;
	p->rgba3	= rgba3;
	p->rgba4	= rgba4;
	p->mode		= mode;
}
/*--------------------------------------------------------------------------*/
struct ST_CbkQue_draw_oval {
	int		x;
	int		y;
	int		w;
	int		h;
	int		rgba;
	int		mode;
	int		fill;
};
static void fn_CbkQue_draw_oval(ST_CbkQue* pCbkQue, int pri, void* param) {
	struct ST_CbkQue_draw_oval* p = (struct ST_CbkQue_draw_oval*)param;
	(*(p->fill ? draw_oval_fill : draw_oval))(p->x, p->y, p->w, p->h, p->rgba, p->mode);
}
static void CbkQue_draw_oval_subr(ST_CbkQue* pCbkQue, int pri, int x, int y, int w, int h, int rgba, int mode, int fill) {
	struct ST_CbkQue_draw_oval* p = (struct ST_CbkQue_draw_oval*)CbkQue_AddMem(pCbkQue, pri, fn_CbkQue_draw_oval, sizeof(struct ST_CbkQue_draw_oval));
	p->x		= x;
	p->y		= y;
	p->w		= w;
	p->h		= h;
	p->rgba		= rgba;
	p->mode		= mode;
	p->fill		= fill;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//□draw_oval()の優先度付きバージョン
void CbkQue_draw_oval(ST_CbkQue* pCbkQue, int pri, int x, int y, int w, int h, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
	CbkQue_draw_oval_subr(pCbkQue, pri, x, y, w, h, rgba, mode, 0/*fill*/);
}
//□draw_oval_fill()の優先度付きバージョン
void CbkQue_draw_oval_fill(ST_CbkQue* pCbkQue, int pri, int x, int y, int w, int h, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
	CbkQue_draw_oval_subr(pCbkQue, pri, x, y, w, h, rgba, mode, 1/*fill*/);
}
/*--------------------------------------------------------------------------*/
struct ST_CbkQue_draw_polygon {
	int		n;
	int		iSpr;
	int		rgba;
	int		mode;
	int		fill;
	int		xy[0/*n*/][2/*x,y*/];
};
static void fn_CbkQue_draw_polygon(ST_CbkQue* pCbkQue, int pri, void* param) {
	struct ST_CbkQue_draw_polygon* p = (struct ST_CbkQue_draw_polygon*)param;
	(*(p->fill ? draw_polygon_fill : draw_polygon))(p->xy, p->n, p->rgba, p->mode);
}
static void CbkQue_draw_polygon_subr(ST_CbkQue* pCbkQue, int pri, const int xy[][2], int n, int rgba, int mode, int fill) {
	struct ST_CbkQue_draw_polygon* p = (struct ST_CbkQue_draw_polygon*)CbkQue_AddMem(pCbkQue, pri, fn_CbkQue_draw_polygon, sizeof(struct ST_CbkQue_draw_polygon) + (sizeof(int[2/*x,y*/]) * n));
	p->n		= n;
	p->rgba		= rgba;
	p->mode		= mode;
	p->fill		= fill;
	memcpy(p->xy, xy, sizeof(int[2/*x,y*/]) * n);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//□draw_polygon()の優先度付きバージョン
void CbkQue_draw_polygon(ST_CbkQue* pCbkQue, int pri, const int xy[/*n*/][2/*x,y*/], int n/*3～*/, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
	CbkQue_draw_polygon_subr(pCbkQue, pri, xy, n, rgba, mode, 0/*fill*/);
}
//□draw_polygon_fill()の優先度付きバージョン
void CbkQue_draw_polygon_fill(ST_CbkQue* pCbkQue, int pri, const int xy[/*n*/][2/*x,y*/], int n/*3～*/, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
	CbkQue_draw_polygon_subr(pCbkQue, pri, xy, n, rgba, mode, 1/*fill*/);
}
/*--------------------------------------------------------------------------*/
struct ST_CbkQue_draw_line {
	int		x1;
	int		y1;
	int		x2;
	int		y2;
	int		w;
	int		rgba;
	int		mode;
};
static void fn_CbkQue_draw_line(ST_CbkQue* pCbkQue, int pri, void* param) {
	struct ST_CbkQue_draw_line* p = (struct ST_CbkQue_draw_line*)param;
	draw_line(p->x1, p->y1, p->x2, p->y2, p->w, p->rgba, p->mode);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//□draw_line()の優先度付きバージョン
void CbkQue_draw_line(ST_CbkQue* pCbkQue, int pri, int x1, int y1, int x2, int y2, int w /*= 1*/, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
	struct ST_CbkQue_draw_line* p = (struct ST_CbkQue_draw_line*)CbkQue_AddMem(pCbkQue, pri, fn_CbkQue_draw_line, sizeof(struct ST_CbkQue_draw_line));
	p->x1		= x1;
	p->y1		= y1;
	p->x2		= x2;
	p->y2		= y2;
	p->w		= w;
	p->rgba		= rgba;
	p->mode		= mode;
}
/*--------------------------------------------------------------------------*/
struct ST_CbkQue_draw_spr {
	int		x;
	int		y;
	int		iSpr;
	int		rgba;
	int		mode;
};
static void fn_CbkQue_draw_spr(ST_CbkQue* pCbkQue, int pri, void* param) {
	struct ST_CbkQue_draw_spr* p = (struct ST_CbkQue_draw_spr*)param;
	draw_spr(p->x, p->y, p->iSpr, p->rgba, p->mode);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//□draw_spr()の優先度付きバージョン
void CbkQue_draw_spr(ST_CbkQue* pCbkQue, int pri, int x, int y, int iSpr, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
	struct ST_CbkQue_draw_spr* p = (struct ST_CbkQue_draw_spr*)CbkQue_AddMem(pCbkQue, pri, fn_CbkQue_draw_spr, sizeof(struct ST_CbkQue_draw_spr));
	p->x		= x;
	p->y		= y;
	p->iSpr		= iSpr;
	p->rgba		= rgba;
	p->mode		= mode;
}
/*--------------------------------------------------------------------------*/
struct ST_CbkQue_draw_spr2 {
	int		x;
	int		y;
	int		iSpr;
	double		scale_x;
	double		scale_y;
	double		rot;
	int		rgba;
	int		mode;
};
static void fn_CbkQue_draw_spr2(ST_CbkQue* pCbkQue, int pri, void* param) {
	struct ST_CbkQue_draw_spr2* p = (struct ST_CbkQue_draw_spr2*)param;
	draw_spr2(p->x, p->y, p->iSpr, p->scale_x, p->scale_y, p->rot, p->rgba, p->mode);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//□draw_spr2()の優先度付きバージョン
void CbkQue_draw_spr2(ST_CbkQue* pCbkQue, int pri, int x, int y, int iSpr, double scale_x, double scale_y, double rot, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
	struct ST_CbkQue_draw_spr2* p = (struct ST_CbkQue_draw_spr2*)CbkQue_AddMem(pCbkQue, pri, fn_CbkQue_draw_spr2, sizeof(struct ST_CbkQue_draw_spr2));
	p->x		= x;
	p->y		= y;
	p->iSpr		= iSpr;
	p->scale_x	= scale_x;
	p->scale_y	= scale_y;
	p->rot		= rot;
	p->rgba		= rgba;
	p->mode		= mode;
}
/*--------------------------------------------------------------------------*/
struct ST_CbkQue_draw_spr3 {
	int		x1;
	int		y1;
	int		x2;
	int		y2;
	int		x3;
	int		y3;
	int		x4;
	int		y4;
	int		iSpr;
	int		rgba;
	int		mode;
};
static void fn_CbkQue_draw_spr3(ST_CbkQue* pCbkQue, int pri, void* param) {
	struct ST_CbkQue_draw_spr3* p = (struct ST_CbkQue_draw_spr3*)param;
	draw_spr3(p->x1, p->y1, p->x2, p->y2, p->x3, p->y3, p->x4, p->y4, p->iSpr, p->rgba, p->mode);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//□draw_spr3()の優先度付きバージョン
void CbkQue_draw_spr3(ST_CbkQue* pCbkQue, int pri, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, int iSpr, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
	struct ST_CbkQue_draw_spr3* p = (struct ST_CbkQue_draw_spr3*)CbkQue_AddMem(pCbkQue, pri, fn_CbkQue_draw_spr3, sizeof(struct ST_CbkQue_draw_spr3));
	p->x1		= x1;
	p->y1		= y1;
	p->x2		= x2;
	p->y2		= y2;
	p->x3		= x3;
	p->y3		= y3;
	p->x4		= x4;
	p->y4		= y4;
	p->iSpr		= iSpr;
	p->rgba		= rgba;
	p->mode		= mode;
}
/*--------------------------------------------------------------------------*/
struct ST_CbkQue_draw_spr_ts {
	int		n;
	int		iSpr;
	int		rgba;
	int		mode;
    //	int		xy[/*n*/][2/*x,y*/];
    //	double		uv[/*n*/][2/*u,v*/];
};
static void fn_CbkQue_draw_spr_ts(ST_CbkQue* pCbkQue, int pri, void* param) {
	struct ST_CbkQue_draw_spr_ts* p = (struct ST_CbkQue_draw_spr_ts*)param;
	int    (*_xy)[2/*x,y*/] = (int    (*)[2/*x,y*/])(  p +    1);
	double (*_uv)[2/*u,v*/] = (double (*)[2/*u,v*/])(_xy + p->n);
	draw_spr_ts(_xy, _uv, p->n, p->iSpr, p->rgba, p->mode);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//□draw_spr_ts()の優先度付きバージョン
void CbkQue_draw_spr_ts(ST_CbkQue* pCbkQue, int pri, const int xy[/*n*/][2/*x,y*/], const double uv[/*n*/][2/*u,v*/], int n, int iSpr, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
	struct ST_CbkQue_draw_spr_ts* p = (struct ST_CbkQue_draw_spr_ts*)CbkQue_AddMem(pCbkQue, pri, fn_CbkQue_draw_spr_ts, sizeof(struct ST_CbkQue_draw_spr_ts) + (sizeof(int[2/*x,y*/]) * n) + (sizeof(double[2/*u,v*/]) * n));
	int    (*_xy)[2/*x,y*/] = (int    (*)[2/*x,y*/])(  p + 1);
	double (*_uv)[2/*u,v*/] = (double (*)[2/*u,v*/])(_xy + n);
	p->n		= n;
	p->iSpr		= iSpr;
	p->rgba		= rgba;
	p->mode		= mode;
	memcpy(_xy, xy, sizeof(   int[2/*x,y*/]) * n);
	memcpy(_uv, uv, sizeof(double[2/*u,v*/]) * n);
}
/*--------------------------------------------------------------------------*/
struct ST_CbkQue_draw_strf_handle {
	int		x;
	int		y;
	int		FontHandle;
	double		scale_x;
	double		scale_y;
	double		ax;
	double		ay;
	int		rgb;
	int		edge_rgb;
	char		str[0];
};
static void fn_CbkQue_draw_strf_handle(ST_CbkQue* pCbkQue, int pri, void* param) {
	struct ST_CbkQue_draw_strf_handle* p = (struct ST_CbkQue_draw_strf_handle*)param;
	draw_str_handle(p->x, p->y, p->FontHandle, p->scale_x, p->scale_y, p->ax, p->ay, p->rgb, p->edge_rgb, p->str);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//□draw_strf_handle()の優先度付きバージョン
void CbkQue_draw_strf_handle(ST_CbkQue* pCbkQue, int pri, int x, int y, int FontHandle, double scale_x, double scale_y, double ax, double ay, int rgb, int edge_rgb, const char* fmt, ...) {
	struct ST_CbkQue_draw_strf_handle* p;
	va_list ap;
	va_start(ap, fmt);
	p = (struct ST_CbkQue_draw_strf_handle*)CbkQue_AddMem(pCbkQue, pri, fn_CbkQue_draw_strf_handle, sizeof(struct ST_CbkQue_draw_strf_handle) + vscprintf(fmt, ap) + 1/*nul*/);
	va_end(ap);
	p->x		= x;
	p->y		= y;
	p->FontHandle	= FontHandle;
	p->scale_x	= scale_x;
	p->scale_y	= scale_y;
	p->ax		= ax;
	p->ay		= ay;
	p->rgb		= rgb;
	p->edge_rgb	= edge_rgb;
	va_start(ap, fmt);
	vsprintf(p->str, fmt, ap);
	va_end(ap);
}
#endif//__CLIP_CBKQ_H__
/****************************************************************************
 *	Sofdec2ムービー
 ****************************************************************************/
#ifdef  USE_SOFDEC
/*--------------------------------------------------------------------------*/
// * Mon Aug 01 21:12:56 JST 2016 Naoyuki Sawa
// - DxLibがDirect3D9を選択した場合も、Sofdec2ムービーを再生出来るようにしました。
// - 「USE_SOFDEC_D3D9」シンボルを定義すると、DxLibがDirect3D9を選択した場合も、Sofdec2ムービーを再生出来るようにします。
//   「USE_SOFDEC_D3D9」シンボルを定義しなければ、DxLibがDirect3D11を選択した場合しか、Sofdec2ムービーを再生出来ません。
//   具体的には、以下の通りです。
// - 2016/07/31の変更により、Sofdec2ムービーの描画には、ピクセルシェーダを使用するようになりました。
//   ピクセルシェーダは、Direct3D9用,又は,Direct3D11用よって、異なるシェーダモデルを使う必要が有り、互換性が有りません。
//   2016/07/31の時点ではDirect3D11しか想定していなかったのですが、Direct3D9が選択されてしまうPCもたまに有るので、Direct3D9にも対応しておく事にしました。
//   例えば、Intel GMA X3000や、ATI Mobility Radeon HD 4200では、DxLibがDirect3D9を選択するようです。(D3D11のフィーチャーレベル不足のようです。)
// - 尚、今回のDirect3D9対応は、表示が出来る程度の対応であり、速度やメモリ効率は考慮していません。
//   DxLibにおいて、DX_GRAPHICSIMAGE_FORMAT_3D_ONE_I8はD3D11の場合はD_DXGI_FORMAT_R8_UNORMにマップされますが、D3D9の場合はA8R8G8B8にマップされるようです。
//   シェーダの処理的には同じ処理で同じ表示結果になるのですが、おそらく、メモリ効率も速度効率も悪いと思います。(詳細は未検証)
//   極力、DxLibがDirect3D11を選択する環境で、実行するようにして下さい。
// - もし今後、Direct3D9対応が不要になった場合は、「USE_SOFDEC_D3D9」シンボルを定義しないように変更して下さい。
#define USE_SOFDEC_D3D9		//このシンボルを定義すると、DxLibがDirect3D9を選択した場合も、Sofdec2ムービーを再生出来ます。
/*--------------------------------------------------------------------------*/
static int GetPixelShaderYUV420();
static void MakeGraph_SofdecYUV(int iManaPlayer, int* TBL_GrHandle/*[4]*/);
static void DeleteGraph_SofdecYUV(int* TBL_GrHandle/*[4]*/);
static void fnGetFrameYUV(void* y_imagebuf, int y_bufsize, int y_pitch,
                          void* u_imagebuf, int u_bufsize, int u_pitch,
                          void* v_imagebuf, int v_bufsize, int v_pitch,
                          void* a_imagebuf, int a_bufsize, int a_pitch,
                          void* user_data);
static void draw_grp_SofdecYUV(int x, int y, int* TBL_GrHandle/*[4]*/, int rgba, int mode);
static void draw_ext_SofdecYUV(int x, int y, int* TBL_GrHandle/*[4]*/, double scale_x, double scale_y, double rot, double ax, double ay, int rgba, int mode);
static void draw_mod_SofdecYUV(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, int* TBL_GrHandle/*[4]*/, int rgba, int mode);
static void draw_grp_ts_SofdecYUV(const int xy[/*n*/][2/*x,y*/], const double uv[/*n*/][2/*u,v*/], int n, int* TBL_GrHandle/*[4]*/, int rgba, int mode);
static void draw_spr_SofdecYUV(int x, int y, int* TBL_GrHandle/*[4]*/, int rgba, int mode, const ST_SprDef* pSprDef);
static void draw_spr2_SofdecYUV(int x, int y, int* TBL_GrHandle/*[4]*/, double scale_x, double scale_y, double rot, int rgba, int mode, const ST_SprDef* pSprDef);
static void draw_spr3_SofdecYUV(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, int* TBL_GrHandle/*[4]*/, int rgba, int mode, const ST_SprDef* pSprDef);
static void draw_spr_ts_SofdecYUV(const int xy[/*n*/][2/*x,y*/], const double uv[/*n*/][2/*u,v*/], int n, int* TBL_GrHandle/*[4]*/, int rgba, int mode, const ST_SprDef* pSprDef);
/*--------------------------------------------------------------------------*/
//【Direct3D11,シェーダモデル4.0用】
//□ピクセルシェーダのバイナリデータYUV420_PS_CSO[]は、以下の手順で作成しました。
//│①ピクセルシェーダのソースを「YUV420_PS.hlsl」という名前で保存する。
//│②コマンドプロンプトで次のように実行する。
//│  │"C:\Home\Share\DxLib_VC\Tool\ShaderCompiler\ShaderCompiler.exe" /Tps_4_0 /FoYUV420_PS.cso YUV420_PS.hlsl
//│  │dpbpe.exe YUV420_PS.cso
//│  │dbin2c.exe YUV420_PS.cso.bpe > YUV420_PS.c
//│  ※尚、上記はDxLibの簡易シェーダーコンパイルソフトを使った例ですが、Visual Studio 2015付属の"C:\Program Files (x86)\Windows Kits\8.1\bin\x86\fxc.exe"を使っても同じです。
//│③YUV420_PS.cを取り込んで、配列定義に'static'を追加し、配列名をYUV420_PS.CSO[]⇒YUV420_PS_CSOに修正する。
#if 0	//ピクセルシェーダのソースここから───────────────────────────────────────────────────────────────────────────────────────────────────────────
//	
//	YUV420_PS.hlsl
//
//	Sofdec2ムービー用YUV420ピクセルシェーダ(Direct3D11,シェーダモデル4.0)
//
//	CLiP - Common Library for P/ECE
//	Copyright (C) 2016 Naoyuki Sawa
//
//	* Sat Jul 30 22:40:08 JST 2016 Naoyuki Sawa
//	- 1st リリース。
//
#if 0	//↓こっちは間違い。
//DxGraphicsD3D11.cppのGraphics_D3D11_DrawPrimitive2DToShader()がInputLayoutにD3D11_VERTEX_INPUTLAYOUT_BLENDTEX_2Dを指定しているので入力形式は以下の形式に固定です。
struct PixelShaderInput {
	float4		Diffuse		:COLOR0;
	float4		Specular	:COLOR1;	//この情報は使わないがDxLibが渡すのでレイアウトに含めておく必要が有る。
	float2		TexCoord0	:TEXCOORD0;
	float2		TexCoord1	:TEXCOORD1;	//この情報は使わないがDxLibが渡すのでレイアウトに含めておく必要が有る。(実際には最後のフィールドは省略しても動作するようだが…)
};
#else	//↓こっちが正しい。
//「\DxLibMakeTest\Shader\Windows\Direct3D11\Base\Base_PS.hlsl」をを参考にしました。
struct PixelShaderInput {
	float4		Position	:SV_POSITION;	//この情報は使わないがDxLibが渡すのでレイアウトに含めておく必要が有る。描画する各ピクセルに対応するスクリーン座標が左上(0,0)～右下(1,1)の座標系で渡されるようです。
	float4		Diffuse		:COLOR0;
	float2		TexCoord0	:TEXCOORD0;
	float2		TexCoord1	:TEXCOORD1;	//この情報は使わないがDxLibが渡すのでレイアウトに含めておく必要が有る。(実際には最後のフィールドは省略しても動作するようだが…)
};
#endif
//～～～ここより下は「C:\cri\pc\samples\crimana\simple_playback\smp_ps_video_texture_yuv.hlsl」を参考にしました。～～～
Texture2D	TextureY	:register(t0);
Texture2D	TextureU	:register(t1);
Texture2D	TextureV	:register(t2);
Texture2D	TextureA	:register(t3);
SamplerState	Sampler		:register(s0);
float4 main(PixelShaderInput input) : SV_TARGET {
	//--- YUV420デコードの定型処理を行う。 ---
	//「C:\cri\pc\samples\crimana\simple_playback\smp_ps_video_texture_yuv.hlsl」を参考にしました。
	float4 ColorY = TextureY.Sample(Sampler, input.TexCoord0);
	float4 ColorU = TextureU.Sample(Sampler, input.TexCoord0) - 0.5f;
	float4 ColorV = TextureV.Sample(Sampler, input.TexCoord0) - 0.5f;
	float4 ColorA = TextureA.Sample(Sampler, input.TexCoord0);
	float4 Output = (ColorY.r - 0.0625f) * 1.164f;		//DxLibにおいてDX_GRAPHICSIMAGE_FORMAT_3D_ONE_I8はD_DXGI_FORMAT_R8_UNORMにマップされるのでテクセルのr成分で参照出来る。以下同様。ビデオカードがD_DXGI_FORMAT_R8_UNORMに対応していない場合、D_DXGI_FORMAT_R8G8_UNORM,又は,D_DXGI_FORMAT_R8G8B8A8_UNORMにマップされる事も有るようだが、そのような環境が手元に無かった。まずD_DXGI_FORMAT_R8_UNORMと考えて大丈夫だろう。(参照:DxGraphicsD3D11.cppのGraphics_D3D11_Device_Create())
	Output.r +=                     ColorV.r * 1.596f;
	Output.g -= ColorU.r * 0.392f + ColorV.r * 0.813f;
	Output.b += ColorU.r * 2.017f;
	Output.a  = ColorA.r;
	//--- ディフューズカラーを適用する。 ---
	Output *= input.Diffuse;
	//--- アルファテストを行う。 ---
	//「\DxLibMakeTest\Shader\Windows\Direct3D11\PixelShader.h」を参考にしました。
	//高速化(検証していないが)のためだけで描画結果は同じです。必須ではありません。
	if(Output.a <= 0.0f) { discard; }
	return Output;
}
#endif	//ピクセルシェーダのソースここまで───────────────────────────────────────────────────────────────────────────────────────────────────────────
/* C:/Home/Share/Piece/test.win/YUV420_PS.cso.bpe */
static const unsigned char YUV420_PS_CSO[] = {
0x79,0x72,0x00,0x01,0x02,0x03,0x04,0x05,0x07,0x08,0x09,0x0a,0x0c,0x0d,0x0e,0x0f,
0x10,0x12,0x16,0x18,0x1a,0x1c,0x1d,0x1e,0x20,0x22,0x28,0x29,0x2a,0x2b,0x2c,0x2e,
0x31,0x32,0x33,0x34,0x35,0x36,0x38,0x39,0x3a,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,
0x45,0x46,0x47,0x48,0x49,0x4c,0x4d,0x4e,0x4f,0x50,0x52,0x53,0x54,0x55,0x56,0x58,
0x59,0x5a,0x5f,0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x68,0x69,0x6c,0x6d,0x6f,0x70,
0x71,0x72,0x73,0x74,0x75,0x78,0x7a,0x7e,0x80,0x81,0x84,0x87,0x8c,0x94,0x96,0x98,
0xa6,0xab,0xb4,0xb8,0xba,0xbc,0xbd,0xbf,0xc2,0xc4,0xc5,0xc8,0xcc,0xcd,0xce,0xd6,
0xde,0xdf,0xe2,0xe4,0xe8,0xef,0xf2,0xf4,0xfc,0xfd,0xff,0x01,0x01,0x7a,0x7a,0x01,
0x7a,0x0f,0x01,0x7b,0x7b,0x01,0x7b,0x7c,0x02,0x7f,0x7d,0x01,0x0f,0x7c,0x03,0x79,
0x79,0x01,0x7d,0x2a,0x02,0x80,0x85,0x04,0x7c,0x09,0x7a,0x7e,0x7e,0x7f,0x0f,0x05,
0x7c,0x82,0x0f,0x83,0x7c,0x86,0x81,0x05,0x01,0x3b,0x01,0x7a,0x8f,0x7c,0x7b,0x7c,
0x8c,0x83,0x8d,0x01,0x90,0x04,0x82,0x06,0x8e,0x0c,0x80,0x12,0x3e,0x3c,0x3c,0x47,
0x50,0x47,0x91,0x4e,0x96,0x52,0x54,0x53,0x9f,0x75,0x89,0x7a,0x9b,0x7c,0x99,0x84,
0x84,0x94,0x98,0x95,0x30,0x9c,0xa0,0x9e,0x9a,0xa1,0x2f,0xa4,0xa5,0xa7,0x9d,0x06,
0x7a,0x07,0x7a,0x07,0x7e,0x0a,0x81,0x0a,0x87,0x0f,0x42,0x29,0x5c,0x30,0xa6,0x50,
0x47,0x56,0xb3,0x60,0x01,0x76,0x92,0x78,0xb7,0x7a,0x02,0x80,0x7c,0x80,0x82,0x86,
0x87,0x87,0xa9,0x88,0x7e,0x89,0x20,0xa8,0xa2,0xae,0xb1,0xb2,0xb8,0xc1,0x7b,0x02,
0x7c,0x04,0x7a,0x0a,0x7a,0x0d,0x30,0x0e,0x93,0x13,0x81,0x17,0x75,0x17,0xb4,0x1e,
0x26,0x1f,0x1f,0x25,0x81,0x2b,0x3b,0x2e,0x39,0x31,0x3a,0x36,0xd1,0x37,0x2d,0x3a,
0x7c,0x3d,0xd4,0x41,0xd5,0x4e,0x4c,0x55,0x7a,0x60,0xb6,0x66,0x7f,0x7a,0x03,0x7a,
0x7b,0x7c,0x08,0x7c,0xac,0x81,0x27,0x82,0xb5,0x88,0x7b,0x8a,0x88,0x8b,0x42,0x8b,
0xca,0x92,0xc9,0x97,0x44,0xad,0xce,0xbc,0xaf,0xbd,0xda,0xe7,0x1b,0xe9,0xe8,0x2e,
0x3e,0x2c,0x2d,0x6d,0x0a,0x58,0x18,0x02,0x5b,0x74,0x71,0x04,0x77,0x1c,0x4f,0x6f,
0x72,0x41,0x72,0x80,0x22,0x06,0xde,0x22,0x7c,0x3e,0xb9,0x72,0xb9,0x12,0xdb,0x62,
0x05,0x7a,0xd0,0x2f,0x30,0x14,0x02,0x7e,0xde,0x14,0x7b,0x05,0x84,0x01,0xb9,0x73,
0x7c,0x64,0xe2,0xba,0x80,0x68,0xaa,0x7b,0xa3,0x6c,0xaa,0x80,0xa3,0x6e,0xaa,0x83,
0xa3,0x70,0xaa,0x04,0x7c,0xa3,0x3a,0x43,0xd7,0x4b,0xb4,0xab,0x3f,0xab,0x3c,0xab,
0x3d,0xab,0x2b,0x01,0x35,0x4a,0x45,0x50,0x4d,0x51,0x4d,0x48,0x52,0x17,0x19,0x39,
0x1a,0x17,0x32,0x34,0x3a,0x34,0x17,0x3a,0x49,0x43,0x46,0xcb,0x2d,0x4d,0xd7,0x4a,
0x4b,0xcb,0xcc,0x20,0xcc,0x26,0x23,0x20,0x1e,0x21,0xcd,0x1f,0xd9,0x60,0x33,0xd2,
0x59,0x94,0xdd,0x49,0x93,0x80,0x04,0xc8,0x7c,0x52,0xbe,0xba,0x0e,0x0e,0xd8,0xbe,
0x8e,0x04,0x04,0xd8,0xc4,0xe1,0x8e,0x0b,0xd6,0x38,0x37,0x3a,0x33,0x3b,0x33,0x37,
0x36,0x01,0xd3,0x34,0x37,0x39,0x91,0x2f,0x3e,0xd3,0x37,0xd0,0xb6,0x37,0xd2,0x1d,
0xba,0xdd,0x17,0xbe,0xc8,0xd6,0xcf,0x39,0x31,0x2f,0x3b,0xd9,0x3a,0x32,0x2e,0x39,
0x5e,0xdb,0x2a,0x7c,0x5f,0x7c,0x40,0xc5,0x01,0xe3,0xa8,0x8b,0xc0,0xbb,0xc0,0x82,
0x83,0xc0,0x97,0x7c,0xa2,0xe6,0x75,0x8d,0x80,0xe6,0x20,0x95,0x47,0xc5,0xe4,0x49,
0x7a,0x03,0x83,0xa9,0x81,0xb5,0x0f,0xdc,0xe3,0xbd,0xe0,0x02,0xc3,0x18,0xeb,0xe0,
0x03,0xc3,0x2c,0xeb,0xa6,0x5d,0x51,0x97,0xc3,0x10,0xaf,0x8f,0x7b,0x57,0x65,0x25,
0xc6,0x67,0x81,0x5f,0x0a,0x8b,0x03,0x2a,0x7e,0x7a,0x63,0x33,0x6b,0x29,0x69,0x17,
0x38,0x29,0xbf,0x10,0xb0,0xc2,0xea,0x2c,0xe5,0x5a,0x11,0x86,0xbf,0x18,0xe5,0x26,
0x61,0x6a,0x28,0xdf,0x20,0xc6,0x18,0xb0,0xc2,0x13,0x7d,0x57,0x2b,0x93,0xbf,0x2c,
0xb0,0xc2,0xea,0x75,0x81,0xc7,0xbb,0x30,0x16,0xbb,0x15,0xad,0x10,0xbc,0xdc,0xdf,
0x0c,0x90,0x04,0x0a,0x87,0x24,0xac,0xe4,0xc7,0x8b,0x28,0x7a,0x02,0x3a,0xcf,0x3b,
0x52,0x7c,0x10,0x7c,0x03,0xe1,0x7c,0x07,0x7e,0xc4,0x8a,0x7e,0x8c,0x8a,0xe2,0x8a,
0x93,0x00,0x00,
};
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#ifdef  USE_SOFDEC_D3D9
//【Direct3D9,シェーダモデル2.0用】
//□ピクセルシェーダのバイナリデータYUV420_PS_D3D9_PSO[]は、以下の手順で作成しました。
//│①ピクセルシェーダのソースを「YUV420_PS_D3D9.fx」という名前で保存する。
//│②コマンドプロンプトで次のように実行する。
//│  │"C:\Home\Share\DxLib_VC\Tool\ShaderCompiler\ShaderCompiler.exe" /Tps_2_0 /FoYUV420_PS_D3D9.pso YUV420_PS_D3D9.fx
//│  │dpbpe.exe YUV420_PS_D3D9.pso
//│  │dbin2c.exe YUV420_PS_D3D9.pso.bpe > YUV420_PS_D3D9.c
//│  ※尚、上記はDxLibの簡易シェーダーコンパイルソフトを使った例ですが、Visual Studio 2015付属の"C:\Program Files (x86)\Windows Kits\8.1\bin\x86\fxc.exe"を使っても同じです。
//│③YUV420_PS_D3D9.cを取り込んで、配列定義に'static'を追加し、配列名をYUV420_PS_D3D9.PSO[]⇒YUV420_PS_D3D9_PSOに修正する。
#if 0	//ピクセルシェーダのソースここから───────────────────────────────────────────────────────────────────────────────────────────────────────────
//	
//	YUV420_PS_D3D9.fx
//
//	Sofdec2ムービー用YUV420ピクセルシェーダ(Direct3D9,シェーダモデル2.0)
//
//	CLiP - Common Library for P/ECE
//	Copyright (C) 2016 Naoyuki Sawa
//
//	* Mon Aug 01 21:12:56 JST 2016 Naoyuki Sawa
//	- 1st リリース。
//
//「\DxLib_VC\Tool\ShaderCompiler\Sample\SamplePS.fx」をを参考にしました。
struct PixelShaderInput {
	float4		Diffuse		:COLOR0;
	float4		Specular	:COLOR1;
	float2		TexCoord0	:TEXCOORD0;
	float2		TexCoord1	:TEXCOORD1;
};
sampler		SamplerY	:register(s0);
sampler		SamplerU	:register(s1);
sampler		SamplerV	:register(s2);
sampler		SamplerA	:register(s3);
float4 main(PixelShaderInput input) : COLOR0 {
	//--- YUV420デコードの定型処理を行う。 ---
	//「C:\cri\pc\samples\crimana\simple_playback\smp_ps_video_texture_yuv.hlsl」を参考にしました。
	float4 ColorY = tex2D(SamplerY, input.TexCoord0);
	float4 ColorU = tex2D(SamplerU, input.TexCoord0) - 0.5f;
	float4 ColorV = tex2D(SamplerV, input.TexCoord0) - 0.5f;
	float4 ColorA = tex2D(SamplerA, input.TexCoord0);
	float4 Output = (ColorY.r - 0.0625f) * 1.164f;		//DxLibにおいてDX_GRAPHICSIMAGE_FORMAT_3D_ONE_I8はD3D11の場合はD_DXGI_FORMAT_R8_UNORMにマップされますがD3D9の場合はA8R8G8B8にマップされるようだ。従ってD3D9の場合もD3D11の場合と同様にテクセルのr成分で参照出来る。(効率は悪そうだが...)
	Output.r +=                     ColorV.r * 1.596f;
	Output.g -= ColorU.r * 0.392f + ColorV.r * 0.813f;
	Output.b += ColorU.r * 2.017f;
	Output.a  = ColorA.r;
	//--- ディフューズカラーを適用する。 ---
	Output *= input.Diffuse;
	//--- アルファテストを行う。 ---
	//「\DxLibMakeTest\Shader\Windows\Direct3D11\PixelShader.h」を参考にしました。
	//高速化(検証していないが)のためだけで描画結果は同じです。必須ではありません。
	if(Output.a <= 0.0f) { discard; }
	return Output;
}
#endif	//ピクセルシェーダのソースここまで───────────────────────────────────────────────────────────────────────────────────────────────────────────
/* C:/Home/Share/Piece/clip/test.win/YUV420_PS_D3D9.pso.bpe */
static const unsigned char YUV420_PS_D3D9_PSO[] = {
0x57,0x46,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x08,0x0a,0x0c,0x0e,0x0f,0x16,0x1c,
0x1f,0x20,0x28,0x29,0x2e,0x30,0x31,0x32,0x33,0x35,0x39,0x3e,0x3f,0x40,0x41,0x42,
0x43,0x46,0x48,0x49,0x4c,0x4d,0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x58,0x59,0x5f,
0x61,0x63,0x64,0x65,0x66,0x68,0x69,0x6c,0x6d,0x6f,0x70,0x72,0x73,0x74,0x78,0x80,
0x81,0x87,0x88,0x90,0x94,0xa0,0xa4,0xaa,0xab,0xb0,0xb4,0xba,0xbd,0xbf,0xc0,0xc5,
0xc8,0xcc,0xdc,0xe3,0xe4,0xf4,0xfd,0xfe,0xff,0x01,0x01,0x01,0x58,0x01,0x02,0x58,
0x3e,0x58,0x58,0x03,0x58,0x04,0x58,0x47,0x47,0x59,0x5c,0x44,0x0c,0x5a,0x01,0x05,
0x58,0x3a,0x32,0x53,0x5b,0x59,0x5d,0x66,0x0f,0x02,0x5a,0x39,0x37,0x59,0x3e,0x64,
0x36,0x65,0x0c,0x01,0x03,0x01,0x05,0x02,0x3e,0x04,0x59,0x0a,0x6e,0x28,0x60,0x2a,
0x58,0x2f,0x72,0x42,0x67,0x44,0x53,0x46,0x5b,0x47,0x5f,0x48,0x6c,0x57,0x5b,0x58,
0x5e,0x61,0x08,0x62,0x71,0x63,0x05,0x68,0x7d,0x69,0x74,0x6b,0x80,0x73,0x44,0x76,
0x08,0x78,0x01,0x7f,0x84,0x06,0x58,0x1e,0x83,0x3e,0x58,0x57,0x57,0x5a,0x3e,0x5e,
0x87,0x75,0x7c,0x7e,0x44,0x81,0x85,0x86,0x26,0x01,0x70,0x03,0x3e,0x0c,0x5a,0x13,
0x19,0x15,0x15,0x44,0x58,0x5a,0x8d,0x5b,0x67,0x5d,0x5e,0x5d,0x6f,0x77,0x7a,0x89,
0x03,0x8a,0x77,0x98,0x06,0x01,0x9b,0x56,0x57,0x20,0x01,0x1f,0x29,0x1d,0x1e,0x0e,
0x59,0x52,0x5c,0x9b,0x05,0x59,0x0e,0x5c,0x02,0x58,0x51,0x59,0x36,0x90,0x04,0x62,
0x0b,0x01,0x3d,0x60,0x41,0x70,0x62,0x5a,0x07,0x01,0x43,0x60,0x45,0x90,0x03,0x62,
0x09,0x01,0x48,0x60,0x4d,0x70,0x59,0x5a,0x6d,0x50,0x81,0x1d,0x8e,0x2a,0x8e,0x2b,
0x8e,0x2d,0x85,0x60,0x39,0x3b,0x2e,0x16,0x2e,0x14,0x01,0x24,0x35,0x30,0x3a,0x38,
0x3b,0x38,0x33,0x3c,0x10,0x11,0x27,0x12,0x10,0x21,0x23,0x28,0x23,0x10,0x28,0x34,
0x2f,0x31,0x64,0x10,0x1f,0x38,0x69,0x35,0x6b,0x10,0x93,0x16,0x93,0x19,0x18,0x16,
0x13,0x17,0x94,0x15,0x01,0x8f,0x58,0x61,0x59,0x4c,0x88,0x4b,0x54,0x55,0x43,0x1b,
0x4a,0x22,0x50,0x1b,0x8f,0x5a,0x61,0x4e,0x10,0x25,0x1b,0x19,0x49,0x4f,0x1a,0x40,
0x0d,0x02,0x1c,0x5c,0x8f,0x6d,0x61,0x88,0x4c,0x59,0x5b,0x5c,0x58,0x97,0x0c,0x42,
0x97,0x04,0x48,0x75,0x01,0x8c,0x02,0x8c,0x03,0x8c,0x04,0x7c,0x1e,0x7b,0x79,0x03,
0x8b,0x5a,0x79,0x02,0x8b,0x6d,0x79,0x01,0x8b,0x04,0x01,0x79,0x04,0x83,0x03,0x7b,
0x02,0x6a,0x6a,0x44,0x9d,0x6a,0x6f,0x95,0x03,0x5e,0x63,0x6f,0x58,0x6a,0x8d,0x5d,
0x9c,0x2a,0x82,0x91,0x7b,0x08,0x91,0x58,0x5b,0x2a,0x96,0x03,0x9a,0x82,0x3f,0x9d,
0x9a,0x96,0x05,0x9c,0x46,0x82,0x99,0x5a,0x08,0x3e,0x04,0x88,0x7e,0x68,0x6a,0x7a,
0x82,0x3e,0x06,0x7b,0x0c,0x8a,0x53,0x65,0x42,0x2c,0x63,0x92,0x7a,0x3f,0x03,0x95,
0x6d,0x2a,0x44,0x1d,0x58,0x02,0x92,0x99,0x01,0x08,0x6c,0x3e,0x89,0x58,0x00,0x00,
};
#endif//USE_SOFDEC_D3D9
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//Sofdec2ムービー用YUV420ピクセルシェーダを取得する。
static int GetPixelShaderYUV420() {
	static int PsHandle = -1; /* ＳＴＡＴＩＣです！ */
	if(PsHandle == -1) {									//初回ならば…
		const void* src;
		switch(GetUseDirect3DVersion()) {						//使用しているDirect3Dのバージョンを取得する(DX_DIRECT3D_9など)
		default:DIE();
#ifdef  USE_SOFDEC_D3D9
		case DX_DIRECT3D_9:
		case DX_DIRECT3D_9EX: src = YUV420_PS_D3D9_PSO; break;	//【Direct3D9,シェーダモデル2.0用】
#endif//USE_SOFDEC_D3D9
		case DX_DIRECT3D_11:  src = YUV420_PS_CSO;      break;	//【Direct3D11,シェーダモデル4.0用】
		}
		int size = BytePairEncodingDecoder_Decode(src, NULL, 0, NULL/*srcEndPtr*/);	//展開サイズを取得する。
		void* mem = malloc(size);							//展開バッファを確保する。
		if(!mem) { DIE(); }	//メモリ不足
		BytePairEncodingDecoder_Decode(src, mem, size, NULL/*srcEndPtr*/);		//展開する。
		if((PsHandle = LoadPixelShaderFromMem(mem, size)) == -1) { DIE(); }		//ピクセルシェーダをロードする。
		free(mem);									//展開バッファを開放する。
	}
	return PsHandle;
}
/*--------------------------------------------------------------------------*/
//Sofdec2ムービー用の、YUVAグラフィックハンドルを作成する。
// - アルファ無しムービーでも、常に、アルファプレーンも作成します。
//   全てのプレーンの初期値は、255でフィルされた状態となっています。
static void MakeGraph_SofdecYUV(int iManaPlayer, int* TBL_GrHandle/*[4]*/) {
	static void* old_GraphData;	/* ＳＴＡＴＩＣです！ */
	static int   old_GraphDataSize;	/* ＳＴＡＴＩＣです！ */
	//Sofdec2ムービーの情報を取得する。
//不要	int DispWidth  = SofdecHelper_GetDispWidth(iManaPlayer);	//┬ムービーサイズのパディング抜き
//不要	int DispHeight = SofdecHelper_GetDispHeight(iManaPlayer);	//┘
	int Width      = SofdecHelper_GetWidth(iManaPlayer);		//┬ムービーサイズのパディング込み
	int Height     = SofdecHelper_GetHeight(iManaPlayer);		//┘
//不要	int Alpha      = SofdecHelper_GetAlpha(iManaPlayer);		//アルファストリームの数(0,又は,1)
	//既存のデータサイズよりも大きければ、データを再割り当てする。
	int new_GraphDataSize = Width * Height;
	if( new_GraphDataSize > old_GraphDataSize) {
		old_GraphData = realloc(old_GraphData, new_GraphDataSize);
		if(!old_GraphData) { DIE(); }	//メモリ不足
		memset((char*)old_GraphData + old_GraphDataSize, -1, new_GraphDataSize - old_GraphDataSize);	//アルファプレーンのために、初期値は255でフィルしておく。
		old_GraphDataSize = new_GraphDataSize;
	}
	//基本イメージデータ構造体を初期化する。
	BASEIMAGE BaseImage;
	memset(&BaseImage, 0, sizeof BaseImage);
	const COLORDATA* pColorData = GetTexColorData(DX_GRAPHICSIMAGE_FORMAT_3D_ONE_I8);	//┐
	if(!pColorData) { DIE(); }								//├基本イメージデータ構造体の色情報を初期化する。別の方法として、CreateGrayColorData()でTBL_BaseImage[i].ColorDataを初期化しても表示は正しいのだが、ReCreateGraphFromBaseImage()が極端に遅くなる問題が生じる。多分、単純転送ではなくカラーマッチング処理を経由するようになるのだと思う。従って、GetTexColorData(DX_GRAPHICSIMAGE_FORMAT_3D_ONE_I8)の方が適切である。
	BaseImage.ColorData = *pColorData;							//┘
	BaseImage.GraphData = old_GraphData;
	//基本イメージデータからグラフィックハンドルを作成する。
	//Y
	BaseImage.Width  = Width;
	BaseImage.Height = Height;
	BaseImage.Pitch  = Width;
	TBL_GrHandle[0/*Y*/] = CreateGraphFromBaseImage(&BaseImage);
	if(TBL_GrHandle[0/*Y*/] == -1) { DIE(); }
	//A
//不要	if(Alpha) {	⇒この判定はおこなわない。アルファ無しムービーでも、常に、アルファプレーンも作成します。
		TBL_GrHandle[3/*A*/] = CreateGraphFromBaseImage(&BaseImage);
		if(TBL_GrHandle[3/*A*/] == -1) { DIE(); }
//不要	}
	//U
	if(BaseImage.Width  & 1) { DIE(); }	//┐
	   BaseImage.Width  >>= 1;		//├Sofdec2のムービーサイズはパディング込みで8の倍数であるはず。
	if(BaseImage.Height & 1) { DIE(); }	//│
	   BaseImage.Height >>= 1;		//┘
	BaseImage.Pitch  = Width;
	TBL_GrHandle[1/*U*/] = CreateGraphFromBaseImage(&BaseImage);
	if(TBL_GrHandle[1/*U*/] == -1) { DIE(); }
	//V
	TBL_GrHandle[2/*V*/] = CreateGraphFromBaseImage(&BaseImage);
	if(TBL_GrHandle[2/*V*/] == -1) { DIE(); }
//{{念のため
	{
		int i;
		for(i = 0; i < 4; i++) {
			//必須ではないが、念のために、以下の検査を行う事にした。
			// - もしSofdec2ムービーの縦横サイズが大き過ぎて、上記で作成した個々のグラフィックハンドルが、複数のテクスチャに分割されて確保された場合、ここでエラー停止する事にした。
			//   DrawPrimitive2DToShader()で補正するのは困難だからです。
			//   最近のグラフィックカードはかなり大きなサイズのテクスチャも作成出来るので、DxLibが分割する事も無いとは思うのだが、もしそうなった場合のためにこの処理を行う事にした。
			// - 尚、もしビデオカードが(2^n)のテクスチャしか作成出来ない場合、BaseImageで指定したサイズよりも大きなサイズで確保される可能性も有るが、それは問題有りません。
			//   DrawPrimitive2DToShader()で補正するのは容易だからです。
			//   とは言え、最近のグラフィックカードならば(2^n)以外のテクスチャも作成出来ると思うので、このケースもほとんど発生しないと思います。(手元の環境では発生しませんでした。)
			int TextureWidth, TextureHeight;
			if(GetGraphTextureSize(TBL_GrHandle[i], &TextureWidth, &TextureHeight)) { DIE(); }	//グラフィックハンドルが持つ一つ目のテクスチャのサイズを得る。
			switch(i) {
			default:DIE();
			case 0:	//Y
			case 3:	//A
				if((TextureWidth <  Width      ) || (TextureHeight <  Height      )) { DIE(); }
				if((TextureWidth >  Width      ) || (TextureHeight >  Height      )) { /** これはok **/ }
				break;
			case 1:	//U
			case 2:	//V
				if((TextureWidth < (Width >> 1)) || (TextureHeight < (Height >> 1))) { DIE(); }
				if((TextureWidth > (Width >> 1)) || (TextureHeight > (Height >> 1))) { /** これはok **/ }
				break;
			}
		}
	}
//}}念のため
}
/*--------------------------------------------------------------------------*/
//MakeGraph_SofdecYUV()で作成したグラフィックハンドルを解放する。
static void DeleteGraph_SofdecYUV(int* TBL_GrHandle/*[4]*/) {
	if(DeleteGraph(TBL_GrHandle[0/*Y*/])) { DIE(); }
	if(DeleteGraph(TBL_GrHandle[1/*U*/])) { DIE(); }
	if(DeleteGraph(TBL_GrHandle[2/*V*/])) { DIE(); }
	if(DeleteGraph(TBL_GrHandle[3/*A*/])) { DIE(); }
}
/*--------------------------------------------------------------------------*/
//SofdecHelper_GetFrameYUV()から呼び出される、アプリケーション定義のコールバック関数。
static void fnGetFrameYUV(void* y_imagebuf, int y_bufsize, int y_pitch,
                          void* u_imagebuf, int u_bufsize, int u_pitch,
                          void* v_imagebuf, int v_bufsize, int v_pitch,
                          void* a_imagebuf, int a_bufsize, int a_pitch,
                          void* user_data) {
	int* TBL_GrHandle/*[4]*/ = (int*)user_data;
	//グラフィックハンドルのサイズを取得する。
	// - SofdecHelper_GetWidth(),及び,SofdecHelper_GetHeight()で得られる値に相当する。
	int Width, Height;
	if(GetGraphSize(TBL_GrHandle[0], &Width, &Height)) { DIE(); }
	//基本イメージデータ構造体を初期化する。
	BASEIMAGE BaseImage;
	memset(&BaseImage, 0, sizeof BaseImage);
	const COLORDATA* pColorData = GetTexColorData(DX_GRAPHICSIMAGE_FORMAT_3D_ONE_I8);	//別の方法として、CreateGrayColorData()でTBL_BaseImage[i].ColorDataを初期化しても表示は正しいのだが、ReCreateGraphFromBaseImage()が極端に遅くなる問題が生じる。多分、単純転送ではなくカラーマッチング処理を経由するようになるのだと思う。従って、GetTexColorData(DX_GRAPHICSIMAGE_FORMAT_3D_ONE_I8)の方が適切である。
	if(!pColorData) { DIE(); }
	BaseImage.ColorData = *pColorData;
	//基本イメージデータから既存のグラフィックハンドルに画像データを転送する。
	//Y
	BaseImage.Width     = Width;
	BaseImage.Height    = Height;
	BaseImage.Pitch     = y_pitch;
	BaseImage.GraphData = y_imagebuf;
	if(ReCreateGraphFromBaseImage(&BaseImage, TBL_GrHandle[0/*Y*/])) { DIE(); }
	//A
	if(a_imagebuf) {	//アルファ付きムービーならば…
		BaseImage.Pitch     = a_pitch;
		BaseImage.GraphData = a_imagebuf;
		if(ReCreateGraphFromBaseImage(&BaseImage, TBL_GrHandle[3/*A*/])) { DIE(); }
	}
	//U
	if(BaseImage.Width  & 1) { DIE(); }	//┐
	   BaseImage.Width  >>= 1;		//├Sofdec2のムービーサイズはパディング込みで8の倍数であるはず。
	if(BaseImage.Height & 1) { DIE(); }	//│
	   BaseImage.Height >>= 1;		//┘
	BaseImage.Pitch     = u_pitch;
	BaseImage.GraphData = u_imagebuf;
	if(ReCreateGraphFromBaseImage(&BaseImage, TBL_GrHandle[1/*U*/])) { DIE(); }
	//V
	BaseImage.Pitch     = v_pitch;
	BaseImage.GraphData = v_imagebuf;
	if(ReCreateGraphFromBaseImage(&BaseImage, TBL_GrHandle[2/*V*/])) { DIE(); }
}
/*--------------------------------------------------------------------------*/
static void draw_grp_SofdecYUV(int x, int y, int* TBL_GrHandle/*[4]*/, int rgba, int mode) {
	ST_SprDef stSprDef;
	memset(&stSprDef, 0, sizeof stSprDef);
	if(GetGraphSize(TBL_GrHandle[0], &stSprDef.w, &stSprDef.h)) { DIE(); }
	draw_spr_SofdecYUV(x, y, TBL_GrHandle, rgba, mode, &stSprDef);
}
/*--------------------------------------------------------------------------*/
static void draw_ext_SofdecYUV(int x, int y, int* TBL_GrHandle/*[4]*/, double scale_x, double scale_y, double rot, double ax, double ay, int rgba, int mode) {
	ST_SprDef stSprDef;
	memset(&stSprDef, 0, sizeof stSprDef);
	if(GetGraphSize(TBL_GrHandle[0], &stSprDef.w, &stSprDef.h)) { DIE(); }
	int cx = stSprDef.w * ax;
	int cy = stSprDef.h * ay;
	MATRIX m = MGetIdent();
	m = MMult(m, MGetTranslate(VGet(-cx, -cy, 0)));
	m = MMult(m, MGetScale(VGet(scale_x, scale_y, 1)));
	m = MMult(m, MGetRotZ(rot));
	m = MMult(m, MGetTranslate(VGet(x + cx, y + cy, 0)));
	//                                ~~~~    ~~~~ 要注意!!draw_ext()のコメントを参照して下さい。
	VECTOR v1 = VTransform(VGet(         0,          0, 0), m);
	VECTOR v2 = VTransform(VGet(stSprDef.w,          0, 0), m);
	VECTOR v3 = VTransform(VGet(stSprDef.w, stSprDef.h, 0), m);
	VECTOR v4 = VTransform(VGet(         0, stSprDef.h, 0), m);
	draw_spr3_SofdecYUV(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v4.x, v4.y, TBL_GrHandle, rgba, mode, &stSprDef);
}
/*--------------------------------------------------------------------------*/
static void draw_mod_SofdecYUV(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, int* TBL_GrHandle/*[4]*/, int rgba, int mode) {
	ST_SprDef stSprDef;
	memset(&stSprDef, 0, sizeof stSprDef);
	if(GetGraphSize(TBL_GrHandle[0], &stSprDef.w, &stSprDef.h)) { DIE(); }
	draw_spr3_SofdecYUV(x1, y1, x2, y2, x3, y3, x4, y4, TBL_GrHandle, rgba, mode, &stSprDef);
}
/*--------------------------------------------------------------------------*/
static void draw_grp_ts_SofdecYUV(const int xy[/*n*/][2/*x,y*/], const double uv[/*n*/][2/*u,v*/], int n, int* TBL_GrHandle/*[4]*/, int rgba, int mode) {
	ST_SprDef stSprDef;
	memset(&stSprDef, 0, sizeof stSprDef);
	if(GetGraphSize(TBL_GrHandle[0], &stSprDef.w, &stSprDef.h)) { DIE(); }
	draw_spr_ts_SofdecYUV(xy, uv, n, TBL_GrHandle, rgba, mode, &stSprDef);
}
/*--------------------------------------------------------------------------*/
static void draw_spr_SofdecYUV(int x, int y, int* TBL_GrHandle/*[4]*/, int rgba, int mode, const ST_SprDef* pSprDef) {
	draw_spr2_SofdecYUV(x, y, TBL_GrHandle, 1.0, 1.0, 0.0, rgba, mode, pSprDef);
}
/*--------------------------------------------------------------------------*/
static void draw_spr2_SofdecYUV(int x, int y, int* TBL_GrHandle/*[4]*/, double scale_x, double scale_y, double rot, int rgba, int mode, const ST_SprDef* pSprDef) {
	MATRIX m = MGetIdent();
	m = MMult(m, MGetTranslate(VGet(-pSprDef->cx, -pSprDef->cy, 0)));
	m = MMult(m, MGetScale(VGet(scale_x, scale_y, 1)));
	m = MMult(m, MGetRotZ(rot));
	m = MMult(m, MGetTranslate(VGet(x, y, 0)));
	VECTOR v1 = VTransform(VGet(         0,          0, 0), m);
	VECTOR v2 = VTransform(VGet(pSprDef->w,          0, 0), m);
	VECTOR v3 = VTransform(VGet(pSprDef->w, pSprDef->h, 0), m);
	VECTOR v4 = VTransform(VGet(         0, pSprDef->h, 0), m);
	draw_spr3_SofdecYUV(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v4.x, v4.y, TBL_GrHandle, rgba, mode, pSprDef);
}
/*--------------------------------------------------------------------------*/
//※TODO:draw_spr3_SofdecYUV()の処理はほとんどdraw_spr_ts_SofdecYUV()と同じなので、draw_spr3_SofdecYUV()がdraw_spr_ts_SofdecYUV()を呼び出すようにして単純化できるはずだ。そうしろ。(2016/08/30メモ)
static void draw_spr3_SofdecYUV(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, int* TBL_GrHandle/*[4]*/, int rgba, int mode, const ST_SprDef* pSprDef) {
	int i, PsHandle, TextureWidth, TextureHeight;
	VERTEX2DSHADER TBL_Vertex[4];
	//シェーダを設定する。
	PsHandle = GetPixelShaderYUV420();
	if(SetUsePixelShader(PsHandle)) { DIE(); }
	for(i = 0; i < 4; i++) {
		if(SetUseTextureToShader(i, TBL_GrHandle[i])) { DIE(); }
	}
	//頂点データを作成する。
	memset(TBL_Vertex, 0, sizeof TBL_Vertex);
	if(GetGraphTextureSize(TBL_GrHandle[0], &TextureWidth, &TextureHeight)) { DIE(); }	//DrawPrimitive2DToShader()等でUV座標を指定する場合、テクスチャサイズをGetGraphTextureSize()で取得して補正するのが適切である。(参照資料「ＤＸライブラリ質問掲示板 - DrawPolygon2DToShaderの不具合」(http://hpcgi2.nifty.com/natupaji/bbs/patio.cgi?mode=view&no=2945))
	TBL_Vertex[0].pos.x = x1;
	TBL_Vertex[0].pos.y = y1;
	TBL_Vertex[1].pos.x = x2;
	TBL_Vertex[1].pos.y = y2;
	TBL_Vertex[2].pos.x = x4;							//┐
	TBL_Vertex[2].pos.y = y4;							//├引数は右回り。頂点データはTRIANGLESTRIP形式。
	TBL_Vertex[3].pos.x = x3;							//│
	TBL_Vertex[3].pos.y = y3;							//┘
	TBL_Vertex[0].u = (float)(pSprDef->sx             ) / (float)TextureWidth;
	TBL_Vertex[0].v = (float)(pSprDef->sy             ) / (float)TextureHeight;
	TBL_Vertex[1].u = (float)(pSprDef->sx + pSprDef->w) / (float)TextureWidth;
	TBL_Vertex[1].v = (float)(pSprDef->sy             ) / (float)TextureHeight;
	TBL_Vertex[2].u = (float)(pSprDef->sx             ) / (float)TextureWidth;	//┐
	TBL_Vertex[2].v = (float)(pSprDef->sy + pSprDef->h) / (float)TextureHeight;	//├引数は右回り。頂点データはTRIANGLESTRIP形式。
	TBL_Vertex[3].u = (float)(pSprDef->sx + pSprDef->w) / (float)TextureWidth;	//│
	TBL_Vertex[3].v = (float)(pSprDef->sy + pSprDef->h) / (float)TextureHeight;	//┘
	TBL_Vertex[0].rhw = TBL_Vertex[1].rhw = TBL_Vertex[2].rhw = TBL_Vertex[3].rhw = 1.0;
	TBL_Vertex[0].dif = TBL_Vertex[1].dif = TBL_Vertex[2].dif = TBL_Vertex[3].dif = GetColorU8(RGBA_GETRED(rgba), RGBA_GETGREEN(rgba), RGBA_GETBLUE(rgba), RGBA_GETALPHA(rgba));
	{
		int BlendMode, BlendParam;
		if(GetDrawBlendMode(&BlendMode, &BlendParam)) { DIE(); }					//退避
		if(SetDrawBlendMode(mode, 0/*シェーダ描画の場合はこのパラメータは使用しない*/)) { DIE(); }
		DrawPrimitive2DToShader(
			TBL_Vertex,
			4,
			DX_PRIMTYPE_TRIANGLESTRIP);	//DX_PRIMTYPE_TRIANGLEFANにすると何故か、描画されなかったり(Intel HD Graphics 4400)、ディスプレイドライバが落ちたり(NVIDIA GeForce GT 735M)します。DX_PRIMTYPE_TRIANGLESTRIPで描画すれば良いので使用上の問題は無いのだが、何故DX_PRIMTYPE_TRIANGLEFANが動作しないのか要調査です。	⇒⇒⇒⇒	調査しました。D3D10以降ではトライアングルファンはサポートされなくなったそうです。(参照資料「Stack Exchange - Why are triangle fans not supported in Direct3D 10 or later?」(http://gamedev.stackexchange.com/questions/35547/why-are-triangle-fans-not-supported-in-direct3d-10-or-later))
		if(SetDrawBlendMode(BlendMode, BlendParam)) { DIE(); }						//復帰
	}
	//シェーダを解除する。
	if(SetUsePixelShader(-1)) { DIE(); }
	for(i = 0; i < 4; i++) {
		if(SetUseTextureToShader(i, -1)) { DIE(); }
	}
}
/*--------------------------------------------------------------------------*/
static void draw_spr_ts_SofdecYUV(const int xy[/*n*/][2/*x,y*/], const double uv[/*n*/][2/*u,v*/], int n, int* TBL_GrHandle/*[4]*/, int rgba, int mode, const ST_SprDef* pSprDef) {
	int i, PsHandle, TextureWidth, TextureHeight;
	COLOR_U8 dif;
	VERTEX2DSHADER* TBL_Vertex = (VERTEX2DSHADER*)alloca(sizeof(VERTEX2DSHADER) * n);
	//シェーダを設定する。
	PsHandle = GetPixelShaderYUV420();
	if(SetUsePixelShader(PsHandle)) { DIE(); }
	for(i = 0; i < 4; i++) {
		if(SetUseTextureToShader(i, TBL_GrHandle[i])) { DIE(); }
	}
	//頂点データを作成する。
	memset(TBL_Vertex, 0, sizeof(VERTEX2DSHADER) * n);
	if(GetGraphTextureSize(TBL_GrHandle[0], &TextureWidth, &TextureHeight)) { DIE(); }	//DrawPrimitive2DToShader()等でUV座標を指定する場合、テクスチャサイズをGetGraphTextureSize()で取得して補正するのが適切である。(参照資料「ＤＸライブラリ質問掲示板 - DrawPolygon2DToShaderの不具合」(http://hpcgi2.nifty.com/natupaji/bbs/patio.cgi?mode=view&no=2945))
	dif = GetColorU8(RGBA_GETRED(rgba), RGBA_GETGREEN(rgba), RGBA_GETBLUE(rgba), RGBA_GETALPHA(rgba));
	for(i = 0; i < n; i++) {
		TBL_Vertex[i].pos.x = xy[i][0/*x*/];
		TBL_Vertex[i].pos.y = xy[i][1/*y*/];
		TBL_Vertex[i].rhw = 1.0;
		TBL_Vertex[i].u = ((uv[i][0/*u*/] * pSprDef->w) + pSprDef->sx) / TextureWidth;
		TBL_Vertex[i].v = ((uv[i][1/*v*/] * pSprDef->h) + pSprDef->sy) / TextureHeight;
		TBL_Vertex[i].dif = dif;
	}
	{
		int BlendMode, BlendParam;
		if(GetDrawBlendMode(&BlendMode, &BlendParam)) { DIE(); }					//退避
		if(SetDrawBlendMode(mode, 0/*シェーダ描画の場合はこのパラメータは使用しない*/)) { DIE(); }
		DrawPrimitive2DToShader(
			TBL_Vertex,
			n,
			DX_PRIMTYPE_TRIANGLESTRIP);	//DX_PRIMTYPE_TRIANGLEFANにすると何故か、描画されなかったり(Intel HD Graphics 4400)、ディスプレイドライバが落ちたり(NVIDIA GeForce GT 735M)します。DX_PRIMTYPE_TRIANGLESTRIPで描画すれば良いので使用上の問題は無いのだが、何故DX_PRIMTYPE_TRIANGLEFANが動作しないのか要調査です。	⇒⇒⇒⇒	調査しました。D3D10以降ではトライアングルファンはサポートされなくなったそうです。(参照資料「Stack Exchange - Why are triangle fans not supported in Direct3D 10 or later?」(http://gamedev.stackexchange.com/questions/35547/why-are-triangle-fans-not-supported-in-direct3d-10-or-later))
		if(SetDrawBlendMode(BlendMode, BlendParam)) { DIE(); }						//復帰
	}
	//シェーダを解除する。
	if(SetUsePixelShader(-1)) { DIE(); }
	for(i = 0; i < 4; i++) {
		if(SetUseTextureToShader(i, -1)) { DIE(); }
	}
}
#endif//USE_SOFDEC
/****************************************************************************
 *	DxLib用の描画関数ラッパー
 ****************************************************************************/
static ght_hash_table_t*	ht_grp;		//グラフィック番号⇒(グラフィックハンドル+1)
#ifdef  USE_SOFDEC
typedef struct _ST_PlayingUsm {
	int		iManaPlayer;		//Sofdecプレイヤーid			Sofdecプレイヤーidは1以上の値です。詳細は、/clip/tool/SofdecHelper/SofdecHelper.cの、2016/04/02のコメントを参照して下さい。
	int		TBL_GrHandle[4];	//YUVAグラフィックハンドル		MakeGraph_SofdecYUV()で作成します。DeleteGraph_SofdecYUV()で解放します。
} ST_PlayingUsm;
static ght_hash_table_t*	ht_usm;		//グラフィック番号⇒(ST_PlayingUsm*)
#endif//USE_SOFDEC
/*--------------------------------------------------------------------------*/
//レジストリ名⇒グラフィックデータを読み込む。
static int load_grp(int iGrp) {
	//ハッシュテーブルが未作成ならば、作成する。
	if(!ht_grp) { ght_set_rehash((ht_grp = ght_create(0)), 1); }
	//既に読み込まれているか?
//{{2016/09/07変更:64ビット対応のための変更を行いました。
//	int gr = (int)ght_get(ht_grp, sizeof iGrp, &iGrp);	//グラフィック番号⇒(グラフィックハンドル+1)を取得する。
//↓2016/09/07変更:64ビット対応のための変更を行いました。
#ifndef _WIN64
	int gr = (int)ght_get(ht_grp, sizeof iGrp, &iGrp);	//グラフィック番号⇒(グラフィックハンドル+1)を取得する。	//32ビット版
#else //_WIN64
	int gr = (intptr_t)ght_get(ht_grp, sizeof iGrp, &iGrp);	//グラフィック番号⇒(グラフィックハンドル+1)を取得する。	//64ビット版
#endif//_WIN64
//}}2016/09/07変更:64ビット対応のための変更を行いました。
	//まだ読み込まれていなければ…
	if(!gr) {
		char ext[MAX_EXT];
		//レジストリから、グラフィックデータのファイル名を取得する。
		const char* fname = REG_get_string_l(stTexInf.pTexDef, iGrp, stTexInf.name_Path, -1);
		if(!fname) { DIE(); }
	    #ifdef  DEBUG
		syslog(LOG_DEBUG, "LoadGraph(\"%s\")\n", fname);
	    #endif//DEBUG
		//アプリケーション定義の関数が指定されていたら、呼び出す。
		// - アプリケーションはiGrpを見て、必要に応じてSetMovieRightImageAlphaFlag()やSetTransColor()等を呼び出して下さい。
		//   この下の処理で、LoadGraph()によって読み込まれる画像や動画の、読み込み設定を行うためです。
		//   全ての画像や動画に対してそのような設定を行う必要が無ければ、(stTexInf.fnPreLoadGraph=NULL)にしても構いません。
		if(stTexInf.fnPreLoadGraph) { (*stTexInf.fnPreLoadGraph)(iGrp); }
		//ファイル名の拡張子を取得しておく。
		splitpath(fname, NULL, NULL, NULL, ext);
#ifdef  USE_SOFDEC
		//ファイル名の拡張子が".usm"ならば…
		if(!strcasecmp(ext, ".usm")) {
			//構造体のメモリを確保する。
			ST_PlayingUsm* pUsm = (ST_PlayingUsm*)malloc(sizeof ST_PlayingUsm);
			if(!pUsm) { DIE(); }	//メモリ不足
			//Sofdecムービーを開く。
			pUsm->iManaPlayer = SofdecHelper_Load(fname);
			//Sofdec2ムービー用の、YUVAグラフィックハンドルを作成する。
			MakeGraph_SofdecYUV(pUsm->iManaPlayer, pUsm->TBL_GrHandle);
			//ハッシュテーブルが未作成ならば、作成する。
			if(!ht_usm) { ght_set_rehash((ht_usm = ght_create(0)), 1); }
			//グラフィック番号⇒(ST_PlayingUsm*)を登録する。
			if(ght_insert(ht_usm, pUsm, sizeof iGrp, &iGrp)) { DIE(); }	//バグ
			//Yプレーンを代表としてht_grpに登録する。
			gr = pUsm->TBL_GrHandle[0];
		//ファイル名の拡張子が".usm"以外ならば…
		} else {
			//グラフィックデータを読み込む。
			gr = LoadGraph(fname);
			if(gr == -1) { DIE(); }
		}
#else //USE_SOFDEC
		//グラフィックデータを読み込む。
		gr = LoadGraph(fname);
		if(gr == -1) { DIE(); }
#endif//USE_SOFDEC
		gr++;	//ハッシュテーブルに0は登録出来ないので+1して0を避ける。(gr!=-1)だから(gr+1)は必ず0以外になる。
		//グラフィック番号⇒(グラフィックハンドル+1)を登録する。
//{{2016/09/07変更:64ビット対応のための変更を行いました。
//		if(ght_insert(ht_grp, (void*)gr, sizeof iGrp, &iGrp)) { DIE(); }	//バグ
//↓2016/09/07変更:64ビット対応のための変更を行いました。
#ifndef _WIN64
		if(ght_insert(ht_grp, (void*)gr, sizeof iGrp, &iGrp)) { DIE(); }	//バグ		//32ビット版
#else //_WIN64
		if(ght_insert(ht_grp, (void*)(intptr_t)gr, sizeof iGrp, &iGrp)) { DIE(); }	//バグ	//64ビット版
#endif//_WIN64
//}}2016/09/07変更:64ビット対応のための変更を行いました。
		//ムービーグラフィックならば、自動的にplay_grp()を行う。
		// - ムービーグラフィックかどうかを判定する方法は、'ムービーグラフィックの拡張子か'を判定するのではなく、'静止画グラフィックの拡張子以外か'を判定する事にした。
		//   DxLibの仕様で、ムービーグラフィックの種類は規定されていないが、静止画グラフィックの種類は規定されているからです。
		//   □「ＤＸライブラリ　関数リファレンスページ - LoadGraph」(http://dxlib.o.oo7.jp/function/dxfunc_graph1.html#R3N2)より:
		//   │尚、読み込むことの出来る画像形式は BMP,JPEG,PNG,DDS,ARGB,TGA の６種類です。
		//ファイル名の拡張子が、静止画グラフィックの拡張子ならば…
		if(!strcasecmp(ext, ".bmp" ) ||	//─BMP
		   !strcasecmp(ext, ".jpeg") ||	//┬JPEG
		   !strcasecmp(ext, ".jpg" ) ||	//┘
		   !strcasecmp(ext, ".png" ) ||	//─PNG
		   !strcasecmp(ext, ".dds" ) ||	//─DDS
		   !strcasecmp(ext, ".argb") ||	//─ARGB
		   !strcasecmp(ext, ".tga" )) {	//─TGA
			/** no job **/
		//ファイル名の拡張子が、静止画グラフィックの拡張子以外ならば…
		} else {
			//ムービーグラフィックであると判断して、自動的にplay_grp()を行う。
			// - もし、実際にはムービーグラフィックでなかった場合は、play_grp()の中でエラー停止するだろう。
			//   その場合、上記の'静止画グラフィックの拡張子'と判断すべき拡張子が、抜けている可能性が有る。
			play_grp(iGrp, 0/*再生初回*/);
		}
	//既に読み込まれていたら…
	} else {
		//初回のload_grp()時にplay_grp(iGrp,0)で再生開始したグラフィック(=ムービーグラフィック)であったならば、再生継続を行う。
		// - 初回のload_grp()時にplay_grp(iGrp,0)で再生開始しなかったグラフィック(=静止画グラフィック)に対しては、play_grp(iGrp,1)は何も行わずダミー処理となる。
		play_grp(iGrp, 1/*再生継続*/);
	}
	gr--;	//上記の+1と相殺する。
	//グラフィックデータのハンドルを返す。
	return gr;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//既に読み込んだグラフィックデータを削除する。
static void free_grp(int iGrp) {
	//ハッシュテーブルが未作成ならば、明らかにまだ読み込まれていない。
	if(!ht_grp) { return; }		//ここまで
	//既に読み込まれているか?
//{{2016/09/07変更:64ビット対応のための変更を行いました。
//	int gr = (int)ght_remove(ht_grp, sizeof iGrp, &iGrp);	//グラフィック番号⇒(グラフィックハンドル+1)を登録解除する。
//↓2016/09/07変更:64ビット対応のための変更を行いました。
#ifndef _WIN64
	int gr = (int)ght_remove(ht_grp, sizeof iGrp, &iGrp);	//グラフィック番号⇒(グラフィックハンドル+1)を登録解除する。		//32ビット版
#else //_WIN64
	int gr = (intptr_t)ght_remove(ht_grp, sizeof iGrp, &iGrp);	//グラフィック番号⇒(グラフィックハンドル+1)を登録解除する。	//64ビット版
#endif//_WIN64
//}}2016/09/07変更:64ビット対応のための変更を行いました。
	//まだ読み込まれていなければ、何もしない。
	if(!gr) { return; }		//ここまで
	gr--;	//上記の+1と相殺する。
    #ifdef  DEBUG
	const char* fname = REG_get_string_l(stTexInf.pTexDef, iGrp, stTexInf.name_Path, -1);
	if(!fname) { DIE(); }
	syslog(LOG_DEBUG, "DeleteGraph(\"%s\")\n", fname);
    #endif//DEBUG
#ifdef  USE_SOFDEC
	//ハッシュテーブルが作成されていたら、Sofdecムービーである可能性が有る。
	if(ht_usm) {
		//Sofdecムービーならば…
		ST_PlayingUsm* pUsm = (ST_PlayingUsm*)ght_remove(ht_usm, sizeof iGrp, &iGrp);	//グラフィック番号⇒(ST_PlayingUsm*)を登録解除する。
		if(pUsm) {
			//Sofdecムービーを閉じる。
			SofdecHelper_Free(pUsm->iManaPlayer);
			//MakeGraph_SofdecYUV()で作成したグラフィックハンドルを解放する。
			// - load_grp()にて、Yプレーンを代表としてht_grpに登録しているはずである。
			//   YプレーンもDeleteGraph_SofdecYUV()で削除されるので、この下のDeleteGraph(gr)は不要である。
			if(pUsm->TBL_GrHandle[0] != gr) { DIE(); }	//バグ
			DeleteGraph_SofdecYUV(pUsm->TBL_GrHandle);
			//構造体のメモリを開放する。
			free(pUsm);	//忘れないで!!
			return;		//ここまで
		}
	}
#endif//USE_SOFDEC
	//グラフィックデータのハンドルを削除する。
	if(DeleteGraph(gr)) { DIE(); }
}
/*--------------------------------------------------------------------------*/
//グラフィックを描画する。
void draw_grp(int x, int y, int iGrp, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
#ifdef  USE_AUTOSCALE
	{
		int AutoScale = GetAutoScale(iGrp);
		if(AutoScale != 1) {
			draw_ext(x, y, iGrp, 1.0, 1.0, 0.0, 0.0, 0.0, rgba, mode);	//AutoScaleは呼び出し先で掛けるので、ここでは掛けない事に注意せよ。
			return;	//ここまで
		}
	}
#endif//USE_AUTOSCALE
#ifdef  USE_DRAWOFFSET
	if(DrawOffsetX || DrawOffsetY) {
		x += DrawOffsetX;
		y += DrawOffsetY;
	}
#endif//USE_DRAWOFFSET
	int gr = load_grp(iGrp);
#ifdef  USE_SOFDEC
	if(ht_usm) {	//ハッシュテーブルが作成されていたら、Sofdecムービーである可能性が有る。
		ST_PlayingUsm* pUsm = (ST_PlayingUsm*)ght_get(ht_usm, sizeof iGrp, &iGrp);
		if(pUsm) {	//Sofdecムービーならば…
			draw_grp_SofdecYUV(x, y, pUsm->TBL_GrHandle, rgba, mode);
			return;	//ここまで
		}
	}
#endif//USE_SOFDEC
	{
		int BlendMode, BlendParam, Red, Green, Blue;
		if(GetDrawBlendMode(&BlendMode, &BlendParam)) { DIE(); }					//退避
		if(GetDrawBright(&Red, &Green, &Blue)) { DIE(); }						//退避
		if(SetDrawBlendMode(mode, RGBA_GETALPHA(rgba))) { DIE(); }					//設定
		if(SetDrawBright(RGBA_GETRED(rgba), RGBA_GETGREEN(rgba), RGBA_GETBLUE(rgba))) { DIE(); }	//設定
		/*if(*/DrawGraph(
			x,
			y,
			gr, TRUE/*TransFlag*/)/*) { DIE(); }*/;							//描画系関数は極小領域の描画時にエラーを返す事が有るようなのでエラーを無視する事にした。
		if(SetDrawBlendMode(BlendMode, BlendParam)) { DIE(); }						//復帰
		if(SetDrawBright(Red, Green, Blue)) { DIE(); }							//復帰
	}
}
/*--------------------------------------------------------------------------*/
//拡縮回転グラフィックを描画する。
// - (x,y)には、拡縮回転する前の左上座標を指定して下さい。(ax,ay)には、拡縮回転のアンカーポイントを比率で指定して下さい。
//   たとえば、100x100ピクセルのグラフィックを、(x,y)=(0,0),(ax,ay)=(0.5,1.0),(scale_x=2.0,scale_y=2.0,rot=0.0)を指定して描画すると、
//   画面上の(0,0)-(10,10)に配置したグラフィックを、(50,100)を中心に縦横二倍に拡大したイメージとなります。
//   すなわち、(25,-100)-(75,100)の領域にグラフィックを描画します。
//   上記の例では回転パラメータを指定していませんが、もし回転パラメータを指定した場合は(50,100)を中心に回転します。
// - draw_ext()とdraw_spr2()との違いは、以下の二点です。
//   ・draw_ext()はグラフィック全体を描画します。draw_spr2()はスプライト定義に従ってグラフィックの一部を描画する事も出来ます。
//   ・draw_ext()は(x,y)パラメータに拡縮回転前の左上座標を指定します。draw_spr2()は(x,y)パラメータに拡縮回転の中心座標を指定します。
//     draw_ext()はセル単位で座標を管理して描画するグラフィック(UI要素やパズルゲームのチップ等)に対して、一時的な拡縮回転エフェクトを加える用途を想定しています。
//     それに対して、draw_spr2()はオブジェクトのピボット位置で座標を管理して描画するスプライト(プレイヤーキャラや弾など)を拡縮回転描画する用途を想定しています。
void draw_ext(int x, int y, int iGrp, double scale_x, double scale_y, double rot, double ax, double ay, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
#ifdef  USE_AUTOSCALE
	{
		int AutoScale = GetAutoScale(iGrp);
		if(AutoScale != 1) {
			//scale_x *= AutoScale;	┬×当関数は中心座標の計算が特殊なので、単純に左のようにしたのでは正しくない(ばす)。
			//scale_y *= AutoScale;	┘　当関数は使用頻度が低いのでとりあえず未実装としておく事にした。使う時に対応しろ。
			DIE();	//※TODO:2016/09/10
		}
	}
#endif//USE_AUTOSCALE
#ifdef  USE_DRAWOFFSET
	if(DrawOffsetX || DrawOffsetY) {
		x += DrawOffsetX;
		y += DrawOffsetY;
	}
#endif//USE_DRAWOFFSET
	int gr = load_grp(iGrp);
#ifdef  USE_SOFDEC
	if(ht_usm) {	//ハッシュテーブルが作成されていたら、Sofdecムービーである可能性が有る。
		ST_PlayingUsm* pUsm = (ST_PlayingUsm*)ght_get(ht_usm, sizeof iGrp, &iGrp);
		if(pUsm) {	//Sofdecムービーならば…
			draw_ext_SofdecYUV(x, y, pUsm->TBL_GrHandle, scale_x, scale_y, rot, ax, ay, rgba, mode);
			return;	//ここまで
		}
	}
#endif//USE_SOFDEC
	int w, h, cx, cy;
	if(GetGraphSize(gr, &w, &h)) { DIE(); }
	cx = w * ax;
	cy = h * ay;
	x += cx;	//┬DrawRotaGraph3()は回転中心を描画原点として描画するので、draw_ext()の関数仕様の違いのためにここでずらす。
	y += cy;	//┘
	{
		int BlendMode, BlendParam, Red, Green, Blue;
		if(GetDrawBlendMode(&BlendMode, &BlendParam)) { DIE(); }					//退避
		if(GetDrawBright(&Red, &Green, &Blue)) { DIE(); }						//退避
		if(SetDrawBlendMode(mode, RGBA_GETALPHA(rgba))) { DIE(); }					//設定
		if(SetDrawBright(RGBA_GETRED(rgba), RGBA_GETGREEN(rgba), RGBA_GETBLUE(rgba))) { DIE(); }	//設定
	    #ifndef USE_DRAWROTAGRAPHFAST
		/*if(*/DrawRotaGraph3(
	    #else //USE_DRAWROTAGRAPHFAST
		/*if(*/DrawRotaGraphFast3(
	    #endif//USE_DRAWROTAGRAPHFAST
			x,
			y,
			cx,
			cy,
			scale_x,
			scale_y,
			rot,
			gr, TRUE/*TransFlag*/)/*) { DIE(); }*/;							//描画系関数は極小領域の描画時にエラーを返す事が有るようなのでエラーを無視する事にした。
		if(SetDrawBlendMode(BlendMode, BlendParam)) { DIE(); }						//復帰
		if(SetDrawBright(Red, Green, Blue)) { DIE(); }							//復帰
	}
}
/*--------------------------------------------------------------------------*/
//自由変形グラフィックを描画する。
void draw_mod(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, int iGrp, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
#ifdef  USE_AUTOSCALE
	{
		//当関数は、自動スケーリングの対応は不要です。
		//描画先の座標を直接指定する仕様だから、テクスチャのサイズに依存しないからです。
		/** no job **/
	}
#endif//USE_AUTOSCALE
#ifdef  USE_DRAWOFFSET
	if(DrawOffsetX || DrawOffsetY) {
		x1 += DrawOffsetX;
		y1 += DrawOffsetY;
		x2 += DrawOffsetX;
		y2 += DrawOffsetY;
		x3 += DrawOffsetX;
		y3 += DrawOffsetY;
		x4 += DrawOffsetX;
		y4 += DrawOffsetY;
	}
#endif//USE_DRAWOFFSET
	int gr = load_grp(iGrp);
#ifdef  USE_SOFDEC
	if(ht_usm) {	//ハッシュテーブルが作成されていたら、Sofdecムービーである可能性が有る。
		ST_PlayingUsm* pUsm = (ST_PlayingUsm*)ght_get(ht_usm, sizeof iGrp, &iGrp);
		if(pUsm) {	//Sofdecムービーならば…
			draw_mod_SofdecYUV(x1, y1, x2, y2, x3, y3, x4, y4, pUsm->TBL_GrHandle, rgba, mode);
			return;	//ここまで
		}
	}
#endif//USE_SOFDEC
	{
		int BlendMode, BlendParam, Red, Green, Blue;
		if(GetDrawBlendMode(&BlendMode, &BlendParam)) { DIE(); }					//退避
		if(GetDrawBright(&Red, &Green, &Blue)) { DIE(); }						//退避
		if(SetDrawBlendMode(mode, RGBA_GETALPHA(rgba))) { DIE(); }					//設定
		if(SetDrawBright(RGBA_GETRED(rgba), RGBA_GETGREEN(rgba), RGBA_GETBLUE(rgba))) { DIE(); }	//設定
		/*if(*/DrawModiGraph(
			x1,
			y1,
			x2,
			y2,
			x3,
			y3,
			x4,
			y4,
			gr, TRUE/*TransFlag*/)/*) { DIE(); }*/;							//描画系関数は極小領域の描画時にエラーを返す事が有るようなのでエラーを無視する事にした。
		if(SetDrawBlendMode(BlendMode, BlendParam)) { DIE(); }						//復帰
		if(SetDrawBright(Red, Green, Blue)) { DIE(); }							//復帰
	}
}
/*--------------------------------------------------------------------------*/
//トライアングルストリップを描画する。(関数名の'ts'は'TriangleStrip'の略です。)
void draw_grp_ts(const int xy[/*n*/][2/*x,y*/], const double uv[/*n*/][2/*u,v*/], int n, int iGrp, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
#ifdef  USE_AUTOSCALE
	{
		//当関数は、自動スケーリングの対応は不要です。
		//描画先の座標を直接指定する仕様だから、テクスチャのサイズに依存しないからです。
		/** no job **/
	}
#endif//USE_AUTOSCALE
#ifdef  USE_DRAWOFFSET
	if(DrawOffsetX || DrawOffsetY) {
		int (*_xy)[2/*x,y*/] = (int (*)[2])alloca(sizeof(int[2/*x,y*/]) * n);
		int i;
		for(i = 0; i < n; i++) {
			_xy[i][0/*x*/] = xy[i][0/*x*/] + DrawOffsetX;
			_xy[i][1/*y*/] = xy[i][1/*y*/] + DrawOffsetY;
		}
		xy = _xy;
	}
#endif//USE_DRAWOFFSET
	int gr = load_grp(iGrp);
#ifdef  USE_SOFDEC
	if(ht_usm) {	//ハッシュテーブルが作成されていたら、Sofdecムービーである可能性が有る。
		ST_PlayingUsm* pUsm = (ST_PlayingUsm*)ght_get(ht_usm, sizeof iGrp, &iGrp);
		if(pUsm) {	//Sofdecムービーならば…
			draw_grp_ts_SofdecYUV(xy, uv, n, pUsm->TBL_GrHandle, rgba, mode);
			return;	//ここまで
		}
	}
#endif//USE_SOFDEC
	int i, Width, Height, TextureWidth, TextureHeight;
	double UScale, VScale;
	COLOR_U8 dif;
	//頂点データを作成する。
	VERTEX2D* TBL_Vertex = (VERTEX2D*)alloca(sizeof(VERTEX2D) * n);
	memset(TBL_Vertex, 0, sizeof(VERTEX2D) * n);
	if(GetGraphSize(gr, &Width, &Height)) { DIE(); }
	if(GetGraphTextureSize(gr, &TextureWidth, &TextureHeight)) { DIE(); }	//DrawPrimitive2DToShader()等でUV座標を指定する場合、テクスチャサイズをGetGraphTextureSize()で取得して補正するのが適切である。(参照資料「ＤＸライブラリ質問掲示板 - DrawPolygon2DToShaderの不具合」(http://hpcgi2.nifty.com/natupaji/bbs/patio.cgi?mode=view&no=2945))
	UScale = (double)Width  / (double)TextureWidth;
	VScale = (double)Height / (double)TextureHeight;
	dif = GetColorU8(RGBA_GETRED(rgba), RGBA_GETGREEN(rgba), RGBA_GETBLUE(rgba), RGBA_GETALPHA(rgba));
	for(i = 0; i < n; i++) {
		TBL_Vertex[i].pos.x = xy[i][0/*x*/];
		TBL_Vertex[i].pos.y = xy[i][1/*y*/];
		TBL_Vertex[i].rhw = 1.0;
		TBL_Vertex[i].u = uv[i][0/*u*/] * UScale;
		TBL_Vertex[i].v = uv[i][1/*v*/] * VScale;
		TBL_Vertex[i].dif = dif;
	}
	{
		int BlendMode, BlendParam;
		if(GetDrawBlendMode(&BlendMode, &BlendParam)) { DIE(); }					//退避
		if(SetDrawBlendMode(mode, 255/*【注意】*/)) { DIE(); }	//【注意】SetDrawBlendMode()の第二引数は、difのα成分と掛け合わされるので、255(=100%)にしておく。尚、α成分はSetDrawBlendMode()の第二引数で指定するよりも、difのα成分で指定する方が内部処理の効率が良いようだ。DxLibMakeTest\DxGraphics.cppのNS_DrawPrimitive2D()を参照せよ。
		/*if(*/DrawPrimitive2D(
			TBL_Vertex,
			n,
			DX_PRIMTYPE_TRIANGLESTRIP,	//DX_PRIMTYPE_TRIANGLEFANにすると何故か、描画されなかったり(Intel HD Graphics 4400)、ディスプレイドライバが落ちたり(NVIDIA GeForce GT 735M)します。DX_PRIMTYPE_TRIANGLESTRIPで描画すれば良いので使用上の問題は無いのだが、何故DX_PRIMTYPE_TRIANGLEFANが動作しないのか要調査です。	⇒⇒⇒⇒	調査しました。D3D10以降ではトライアングルファンはサポートされなくなったそうです。(参照資料「Stack Exchange - Why are triangle fans not supported in Direct3D 10 or later?」(http://gamedev.stackexchange.com/questions/35547/why-are-triangle-fans-not-supported-in-direct3d-10-or-later))
			gr, TRUE/*TransFlag*/)/*) { DIE(); }*/;
		if(SetDrawBlendMode(BlendMode, BlendParam)) { DIE(); }						//復帰
	}
}
/*--------------------------------------------------------------------------*/
//矩形を描画する。
static void draw_box_subr(int x, int y, int w, int h, int rgba, int mode, int fill) {
#ifdef  USE_DRAWOFFSET
	if(DrawOffsetX || DrawOffsetY) {
		x += DrawOffsetX;
		y += DrawOffsetY;
	}
#endif//USE_DRAWOFFSET
	{
		int BlendMode, BlendParam, Red, Green, Blue;
		if(GetDrawBlendMode(&BlendMode, &BlendParam)) { DIE(); }					//退避
		if(GetDrawBright(&Red, &Green, &Blue)) { DIE(); }						//退避
		if(SetDrawBlendMode(mode, RGBA_GETALPHA(rgba))) { DIE(); }					//設定
		if(SetDrawBright(255, 255, 255)) { DIE(); }							//設定
		/*if(*/DrawBox(
			x,
			y,
			x + w,
			y + h,
			GetColor(RGBA_GETRED(rgba), RGBA_GETGREEN(rgba), RGBA_GETBLUE(rgba)),
			fill)/*) { DIE(); }*/;									//描画系関数は極小領域の描画時にエラーを返す事が有るようなのでエラーを無視する事にした。
		if(SetDrawBlendMode(BlendMode, BlendParam)) { DIE(); }						//復帰
		if(SetDrawBright(Red, Green, Blue)) { DIE(); }							//復帰
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void draw_box(int x, int y, int w, int h, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
	draw_box_subr(x, y, w, h, rgba, mode, 0/*fill*/);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void draw_box_fill(int x, int y, int w, int h, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
	draw_box_subr(x, y, w, h, rgba, mode, 1/*fill*/);
}
/*--------------------------------------------------------------------------*/
//矩形をグラデーションで塗り潰す。
void draw_box_mc(int x, int y, int w, int h, int rgba1/*左上*/, int rgba2/*右上*/, int rgba3/*右下*/, int rgba4/*左下*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
#ifdef  USE_DRAWOFFSET
	if(DrawOffsetX || DrawOffsetY) {
		x += DrawOffsetX;
		y += DrawOffsetY;
	}
#endif//USE_DRAWOFFSET
	{
		int BlendMode, BlendParam;
		if(GetDrawBlendMode(&BlendMode, &BlendParam)) { DIE(); }					//退避
		if(SetDrawBlendMode(mode, 255/*【注意】*/)) { DIE(); }	//【注意】SetDrawBlendMode()の第二引数は、difのα成分と掛け合わされるので、255(=100%)にしておく。尚、α成分はSetDrawBlendMode()の第二引数で指定するよりも、difのα成分で指定する方が内部処理の効率が良いようだ。DxLibMakeTest\DxGraphics.cppのNS_DrawPrimitive2D()を参照せよ。
		{
			VERTEX2D TBL_Vertex[4];
			memset(TBL_Vertex, 0, sizeof TBL_Vertex);
			TBL_Vertex[0].pos.x = TBL_Vertex[2].pos.x = x;			//┐
			TBL_Vertex[1].pos.x = TBL_Vertex[3].pos.x = x + w;		//├TriangleStripの並び
			TBL_Vertex[0].pos.y = TBL_Vertex[1].pos.y = y;			//│
			TBL_Vertex[2].pos.y = TBL_Vertex[3].pos.y = y + h;		//┘
			TBL_Vertex[0].dif.r = RGBA_GETRED(  rgba1);	//┐		//┐
			TBL_Vertex[0].dif.g = RGBA_GETGREEN(rgba1);	//├左上	//│
			TBL_Vertex[0].dif.b = RGBA_GETBLUE( rgba1);	//│		//│
			TBL_Vertex[0].dif.a = RGBA_GETALPHA(rgba1);	//┘		//│
			TBL_Vertex[1].dif.r = RGBA_GETRED(  rgba2);	//┐		//│
			TBL_Vertex[1].dif.g = RGBA_GETGREEN(rgba2);	//├右上	//│
			TBL_Vertex[1].dif.b = RGBA_GETBLUE( rgba2);	//│		//│
			TBL_Vertex[1].dif.a = RGBA_GETALPHA(rgba2);	//┘		//├TriangleStripの並び
			TBL_Vertex[2].dif.r = RGBA_GETRED(  rgba4);	//┐		//│
			TBL_Vertex[2].dif.g = RGBA_GETGREEN(rgba4);	//├左下	//│
			TBL_Vertex[2].dif.b = RGBA_GETBLUE( rgba4);	//│		//│
			TBL_Vertex[2].dif.a = RGBA_GETALPHA(rgba4);	//┘		//│
			TBL_Vertex[3].dif.r = RGBA_GETRED(  rgba3);	//┐		//│
			TBL_Vertex[3].dif.g = RGBA_GETGREEN(rgba3);	//├右下	//│
			TBL_Vertex[3].dif.b = RGBA_GETBLUE( rgba3);	//│		//│
			TBL_Vertex[3].dif.a = RGBA_GETALPHA(rgba3);	//┘		//┘
			TBL_Vertex[0].rhw = TBL_Vertex[1].rhw = TBL_Vertex[2].rhw = TBL_Vertex[3].rhw = 1.0;	//テクスチャ無しの場合でも、rhwは設定する必要が有るようです。試しにrhw=0にしてみると、何も描かれませんでした。
			/*if(*/DrawPrimitive2D(
				TBL_Vertex,
				4,
				DX_PRIMTYPE_TRIANGLESTRIP,	//DX_PRIMTYPE_TRIANGLEFANにすると何故か、描画されなかったり(Intel HD Graphics 4400)、ディスプレイドライバが落ちたり(NVIDIA GeForce GT 735M)します。DX_PRIMTYPE_TRIANGLESTRIPで描画すれば良いので使用上の問題は無いのだが、何故DX_PRIMTYPE_TRIANGLEFANが動作しないのか要調査です。	⇒⇒⇒⇒	調査しました。D3D10以降ではトライアングルファンはサポートされなくなったそうです。(参照資料「Stack Exchange - Why are triangle fans not supported in Direct3D 10 or later?」(http://gamedev.stackexchange.com/questions/35547/why-are-triangle-fans-not-supported-in-direct3d-10-or-later))
				DX_NONE_GRAPH,
				TRUE/*TransFlag*/)/*) { DIE(); }*/;						//描画系関数は極小領域の描画時にエラーを返す事が有るようなのでエラーを無視する事にした。
		}
		if(SetDrawBlendMode(BlendMode, BlendParam)) { DIE(); }						//復帰
	}
}
/*--------------------------------------------------------------------------*/
//楕円を描画する。
static void draw_oval_subr(int x, int y, int w, int h, int rgba, int mode, int fill) {
#ifdef  USE_DRAWOFFSET
	if(DrawOffsetX || DrawOffsetY) {
		x += DrawOffsetX;
		y += DrawOffsetY;
	}
#endif//USE_DRAWOFFSET
	{
		int BlendMode, BlendParam, Red, Green, Blue;
		if(GetDrawBlendMode(&BlendMode, &BlendParam)) { DIE(); }					//退避
		if(GetDrawBright(&Red, &Green, &Blue)) { DIE(); }						//退避
		if(SetDrawBlendMode(mode, RGBA_GETALPHA(rgba))) { DIE(); }					//設定
		if(SetDrawBright(255, 255, 255)) { DIE(); }							//設定
//{{2016/08/07変更:DxLibにDrawOval_Rect()が追加された事に伴い、draw_oval_subr()が使用する関数を、DrawOval()⇒DrawOval_Rect()に変更しました。
//		/*if(*/DrawOval(
//			x + w / 2,
//			y + h / 2,
//			w / 2,
//			h / 2,
//			GetColor(RGBA_GETRED(rgba), RGBA_GETGREEN(rgba), RGBA_GETBLUE(rgba)),
//			fill)/*) { DIE(); }*/;									//描画系関数は極小領域の描画時にエラーを返す事が有るようなのでエラーを無視する事にした。
//↓2016/08/07変更:DxLibにDrawOval_Rect()が追加された事に伴い、draw_oval_subr()が使用する関数を、DrawOval()⇒DrawOval_Rect()に変更しました。
		/*if(*/DrawOval_Rect(
			x,
			y,
			x + w,
			y + h,
			GetColor(RGBA_GETRED(rgba), RGBA_GETGREEN(rgba), RGBA_GETBLUE(rgba)),
			fill)/*) { DIE(); }*/;									//描画系関数は極小領域の描画時にエラーを返す事が有るようなのでエラーを無視する事にした。
//}}2016/08/07変更:DxLibにDrawOval_Rect()が追加された事に伴い、draw_oval_subr()が使用する関数を、DrawOval()⇒DrawOval_Rect()に変更しました。
		if(SetDrawBlendMode(BlendMode, BlendParam)) { DIE(); }						//復帰
		if(SetDrawBright(Red, Green, Blue)) { DIE(); }							//復帰
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void draw_oval(int x, int y, int w, int h, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
	draw_oval_subr(x, y, w, h, rgba, mode, 0/*fill*/);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void draw_oval_fill(int x, int y, int w, int h, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
	draw_oval_subr(x, y, w, h, rgba, mode, 1/*fill*/);
}
/*--------------------------------------------------------------------------*/
//多角形を描画する。
void draw_polygon(const int xy[/*n*/][2/*x,y*/], int n/*3～*/, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
#ifdef  USE_DRAWOFFSET
	if(DrawOffsetX || DrawOffsetY) {
		//※TODOO:未対応
	}
#endif//USE_DRAWOFFSET
	if(n < 3) { DIE(); }	//(n<3)で当関数を呼び出してはいけません。
	{
		int BlendMode, BlendParam, Red, Green, Blue;
		if(GetDrawBlendMode(&BlendMode, &BlendParam)) { DIE(); }					//退避
		if(GetDrawBright(&Red, &Green, &Blue)) { DIE(); }						//退避
		if(SetDrawBlendMode(mode, RGBA_GETALPHA(rgba))) { DIE(); }					//設定
		if(SetDrawBright(255, 255, 255)) { DIE(); }							//設定
		{
			int x1 = xy[n - 1][0/*x*/];
			int y1 = xy[n - 1][1/*y*/];
			int i;
			for(i = 0; i < n; i++) {
				int x2 = xy[i][0/*x*/];
				int y2 = xy[i][1/*y*/];
				/*if(*/DrawLine(
					x1,
					y1,
					x2,
					y2,
					GetColor(RGBA_GETRED(rgba), RGBA_GETGREEN(rgba), RGBA_GETBLUE(rgba)));	//描画系関数は極小領域の描画時にエラーを返す事が有るようなのでエラーを無視する事にした。
				x1 = x2;
				y1 = y2;
			}
		}
		if(SetDrawBlendMode(BlendMode, BlendParam)) { DIE(); }						//復帰
		if(SetDrawBright(Red, Green, Blue)) { DIE(); }							//復帰
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void draw_polygon_fill(const int xy[/*n*/][2/*x,y*/], int n/*3～*/, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
#ifdef  USE_DRAWOFFSET
	if(DrawOffsetX || DrawOffsetY) {
		//※TODOO:未対応
	}
#endif//USE_DRAWOFFSET
	if(n < 3) { DIE(); }	//(n<3)で当関数を呼び出してはいけません。
	//多角形を三角形に分割する。
	vec2i (*tri_p)[3] = (vec2i(*)[3])alloca(sizeof(vec2i[3]) * (n - 2));
//{{2016/10/24変更:vec2i_split_polygon_into_triangles()の戻り値の意味を変更した事に伴い、draw_polygon_fill()の処理も変更しました。
//	if(vec2i_split_polygon_into_triangles((vec2i*)xy, n, tri_p)) {	//分割成功ならば…
//↓2016/10/24変更:vec2i_split_polygon_into_triangles()の戻り値の意味を変更した事に伴い、draw_polygon_fill()の処理も変更しました。
	int n_tri = vec2i_split_polygon_into_triangles((vec2i*)xy, n, tri_p);	//切り取れた三角形の数(0～(n-2))を取得する。
	if(n_tri > (n - 2)) { DIE(); }	//三角形の数が(n-2)を超える事は無いはず。もし超えたらvec2i_split_polygon_into_triangles()のバグです。
	if(n_tri) {	//切り取れた三角形が一つ以上有れば…(これは三角形を一つも描かない場合の無駄なステート変更を避けるためだけの判定です。この判定をしなくても結果は同じになります。)
//}}2016/10/24変更:vec2i_split_polygon_into_triangles()の戻り値の意味を変更した事に伴い、draw_polygon_fill()の処理も変更しました。
		int BlendMode, BlendParam, Red, Green, Blue;
		if(GetDrawBlendMode(&BlendMode, &BlendParam)) { DIE(); }					//退避
		if(GetDrawBright(&Red, &Green, &Blue)) { DIE(); }						//退避
		if(SetDrawBlendMode(mode, RGBA_GETALPHA(rgba))) { DIE(); }					//設定
		if(SetDrawBright(255, 255, 255)) { DIE(); }							//設定
		{
			int i;
//{{2016/10/24変更:vec2i_split_polygon_into_triangles()の戻り値の意味を変更した事に伴い、draw_polygon_fill()の処理も変更しました。
//			for(i = 0; i < (n - 2); i++) {
//↓2016/10/24変更:vec2i_split_polygon_into_triangles()の戻り値の意味を変更した事に伴い、draw_polygon_fill()の処理も変更しました。
			for(i = 0; i < n_tri; i++) {
//}}2016/10/24変更:vec2i_split_polygon_into_triangles()の戻り値の意味を変更した事に伴い、draw_polygon_fill()の処理も変更しました。
				/*if(*/DrawTriangle(
					tri_p[i][0].x, tri_p[i][0].y,
					tri_p[i][1].x, tri_p[i][1].y,
					tri_p[i][2].x, tri_p[i][2].y,
					rgba,
					TRUE)/*) { DIE(); }*/;							//描画系関数は極小領域の描画時にエラーを返す事が有るようなのでエラーを無視する事にした。
			}
		}
		if(SetDrawBlendMode(BlendMode, BlendParam)) { DIE(); }						//復帰
		if(SetDrawBright(Red, Green, Blue)) { DIE(); }							//復帰
	}
}
/*--------------------------------------------------------------------------*/
//直線を描画する。
void draw_line(int x1, int y1, int x2, int y2, int w /*= 1*/, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
#ifdef  USE_DRAWOFFSET
	if(DrawOffsetX || DrawOffsetY) {
		x1 += DrawOffsetX;
		y1 += DrawOffsetY;
		x2 += DrawOffsetX;
		y2 += DrawOffsetY;
	}
#endif//USE_DRAWOFFSET
	{
		int BlendMode, BlendParam, Red, Green, Blue;
		if(GetDrawBlendMode(&BlendMode, &BlendParam)) { DIE(); }					//退避
		if(GetDrawBright(&Red, &Green, &Blue)) { DIE(); }						//退避
		if(SetDrawBlendMode(mode, RGBA_GETALPHA(rgba))) { DIE(); }					//設定
		if(SetDrawBright(255, 255, 255)) { DIE(); }							//設定
		/*if(*/DrawLine(
			x1,
			y1,
			x2,
			y2,
			GetColor(RGBA_GETRED(rgba), RGBA_GETGREEN(rgba), RGBA_GETBLUE(rgba)),
			w)/*) { DIE(); }*/;									//描画系関数は極小領域の描画時にエラーを返す事が有るようなのでエラーを無視する事にした。
		if(SetDrawBlendMode(BlendMode, BlendParam)) { DIE(); }						//復帰
		if(SetDrawBright(Red, Green, Blue)) { DIE(); }							//復帰
	}
}
/*--------------------------------------------------------------------------*/
//フォントハンドルを使用して文字列を描画する。
void draw_strf_handle(int x, int y, int FontHandle, double scale_x, double scale_y, double ax, double ay, int rgb, int edge_rgb, const char* fmt, ...) {
	va_list ap;
	va_start(ap, fmt);
	draw_strv_handle(x, y, FontHandle, scale_x, scale_y, ax, ay, rgb, edge_rgb, fmt, ap);
	va_end(ap);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void draw_strv_handle(int x, int y, int FontHandle, double scale_x, double scale_y, double ax, double ay, int rgb, int edge_rgb, const char* fmt, va_list ap) {
	char* s = strdup_vprintf(fmt, ap);
	draw_str_handle(x, y, FontHandle, scale_x, scale_y, ax, ay, rgb, edge_rgb, s);
	free(s);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void draw_str_handle(int x, int y, int FontHandle, double scale_x, double scale_y, double ax, double ay, int rgb, int edge_rgb, const char* s) {
#ifdef  USE_DRAWOFFSET
	if(DrawOffsetX || DrawOffsetY) {
		x += DrawOffsetX;
		y += DrawOffsetY;
	}
#endif//USE_DRAWOFFSET
	{
		int BlendMode, BlendParam, Red, Green, Blue;
		if(GetDrawBlendMode(&BlendMode, &BlendParam)) { DIE(); }					//退避
		if(GetDrawBright(&Red, &Green, &Blue)) { DIE(); }						//退避
		if(SetDrawBlendMode(DX_BLENDMODE_NOBLEND, 0)) { DIE(); }					//設定
		if(SetDrawBright(255, 255, 255)) { DIE(); }							//設定
		if(ax || ay) {											//!(ax||ay)の場合もこのブロックの処理を行っても同じだが、高速化のために場合分けする事にした。
			int size_x, size_y;
			/*if(*/GetDrawExtendStringSizeToHandle(
				&size_x,
				&size_y,
				NULL,
				scale_x,
				scale_y,
				s,
				strlen(s),
				FontHandle)/*) { DIE(); }*/;							//GetDrawExtendStringSize()の戻り値は最後の行の幅(?)であり0以外ならばエラーを示すのではない。GetDrawExtendStringSize()の戻り値を検査してはいけない。
			x -= (size_x * ax);
			y -= (size_y * ay);
		}
		if(DrawExtendStringToHandle(
			x,
			y,
			scale_x,
			scale_y,
			s,
			rgb,
			FontHandle,
			edge_rgb)) { DIE(); }									//他の描画系関数はエラー発生条件が不明なためエラー検査をコメントアウトしていますが、当関数はフォントハンドルが無効な場合にエラーになるのが明らかなので、エラー検査をコメントアウトしない事にしました。
		if(SetDrawBlendMode(BlendMode, BlendParam)) { DIE(); }						//復帰
		if(SetDrawBright(Red, Green, Blue)) { DIE(); }							//復帰
	}
}
/*--------------------------------------------------------------------------*/
//スプライト定義を取得する。
void get_sprDef(int iSpr, ST_SprDef* pSprDef) {
	const uint8_t* pSprKey = REG_open_key(stSprInf.pSprDef, iSpr);
//不要	if(!pSprKey) { DIE(); }	//ここで判定しなくても、この下で取得する値がマイナスかどうかで、まとめて判定出来ます。
	pSprDef->iTexNo	= REG_get_value(pSprKey, stSprInf.name_TexNo);
	pSprDef->sx	= REG_get_value(pSprKey, stSprInf.name_x);
	pSprDef->sy	= REG_get_value(pSprKey, stSprInf.name_y);
	pSprDef->w	= REG_get_value(pSprKey, stSprInf.name_w);
	pSprDef->h	= REG_get_value(pSprKey, stSprInf.name_h);
	pSprDef->cx	= REG_get_value(pSprKey, stSprInf.name_cx);
	pSprDef->cy	= REG_get_value(pSprKey, stSprInf.name_cy);
	pSprDef->iParam	= REG_get_value(pSprKey, stSprInf.name_Param);	//(未使用)
	if((pSprDef->iTexNo | pSprDef->sx | pSprDef->sy | pSprDef->w | pSprDef->h | pSprDef->cx | pSprDef->cy | pSprDef->iParam) < 0) { DIE(); }
	pSprDef->cx = pSprDef->cx << 8 >> 8;
	pSprDef->cy = pSprDef->cy << 8 >> 8;
}
/*--------------------------------------------------------------------------*/
//スプライトを描画する。
void draw_spr(int x, int y, int iSpr, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
	ST_SprDef stSprDef;
	get_sprDef(iSpr, &stSprDef);
#ifdef  USE_AUTOSCALE
	{
		int AutoScale = GetAutoScale(stSprDef.iTexNo);
		if(AutoScale != 1) {
			draw_spr2(x, y, iSpr, 1.0, 1.0, 0.0, rgba, mode);	//AutoScaleは呼び出し先で掛けるので、ここでは掛けない事に注意せよ。
			return;	//ここまで
		}
	}
#endif//USE_AUTOSCALE
#ifdef  USE_DRAWOFFSET
	if(DrawOffsetX || DrawOffsetY) {
		x += DrawOffsetX;
		y += DrawOffsetY;
	}
#endif//USE_DRAWOFFSET
	int gr = load_grp(stSprDef.iTexNo);
#ifdef  USE_SOFDEC
	if(ht_usm) {	//ハッシュテーブルが作成されていたら、Sofdecムービーである可能性が有る。
		ST_PlayingUsm* pUsm = (ST_PlayingUsm*)ght_get(ht_usm, sizeof stSprDef.iTexNo, &stSprDef.iTexNo);
		if(pUsm) {	//Sofdecムービーならば…
			draw_spr_SofdecYUV(x, y, pUsm->TBL_GrHandle, rgba, mode, &stSprDef);
			return;	//ここまで
		}
	}
#endif//USE_SOFDEC
	x -= stSprDef.cx;	//┬DrawRectGraph()は常に左上を描画原点として描画するので、呼び出し側で明示的に描画原点をずらす必要が有る。
	y -= stSprDef.cy;	//┘
	{
		int BlendMode, BlendParam, Red, Green, Blue;
		if(GetDrawBlendMode(&BlendMode, &BlendParam)) { DIE(); }					//退避
		if(GetDrawBright(&Red, &Green, &Blue)) { DIE(); }						//退避
		if(SetDrawBlendMode(mode, RGBA_GETALPHA(rgba))) { DIE(); }					//設定
		if(SetDrawBright(RGBA_GETRED(rgba), RGBA_GETGREEN(rgba), RGBA_GETBLUE(rgba))) { DIE(); }	//設定
		/*if(*/DrawRectGraph(
			x,
			y,
			stSprDef.sx,
			stSprDef.sy,
			stSprDef.w,
			stSprDef.h,
			gr, TRUE/*TransFlag*/, FALSE/*TurnFlag*/)/*) { DIE(); }*/;				//描画系関数は極小領域の描画時にエラーを返す事が有るようなのでエラーを無視する事にした。
		if(SetDrawBlendMode(BlendMode, BlendParam)) { DIE(); }						//復帰
		if(SetDrawBright(Red, Green, Blue)) { DIE(); }							//復帰
	}
}
/*--------------------------------------------------------------------------*/
//拡縮回転スプライトを描画する。
void draw_spr2(int x, int y, int iSpr, double scale_x, double scale_y, double rot, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
	ST_SprDef stSprDef;
	get_sprDef(iSpr, &stSprDef);
#ifdef  USE_AUTOSCALE
	{
		int AutoScale = GetAutoScale(stSprDef.iTexNo);
		if(AutoScale != 1) {
			scale_x *= AutoScale;
			scale_y *= AutoScale;
		}
	}
#endif//USE_AUTOSCALE
#ifdef  USE_DRAWOFFSET
	if(DrawOffsetX || DrawOffsetY) {
		x += DrawOffsetX;
		y += DrawOffsetY;
	}
#endif//USE_DRAWOFFSET
	int gr = load_grp(stSprDef.iTexNo);
#ifdef  USE_SOFDEC
	if(ht_usm) {	//ハッシュテーブルが作成されていたら、Sofdecムービーである可能性が有る。
		ST_PlayingUsm* pUsm = (ST_PlayingUsm*)ght_get(ht_usm, sizeof stSprDef.iTexNo, &stSprDef.iTexNo);
		if(pUsm) {	//Sofdecムービーならば…
			draw_spr2_SofdecYUV(x, y, pUsm->TBL_GrHandle, scale_x, scale_y, rot, rgba, mode, &stSprDef);
			return;	//ここまで
		}
	}
#endif//USE_SOFDEC
//不要	x -= stSprDef.cx;	//┬DrawRectRotaGraph3()は回転中心を描画原点として描画するので、呼び出し側で描画原点をずらしてはいけない。
//不要	y -= stSprDef.cy;	//┘
	{
		int BlendMode, BlendParam, Red, Green, Blue;
		if(GetDrawBlendMode(&BlendMode, &BlendParam)) { DIE(); }					//退避
		if(GetDrawBright(&Red, &Green, &Blue)) { DIE(); }						//退避
		if(SetDrawBlendMode(mode, RGBA_GETALPHA(rgba))) { DIE(); }					//設定
		if(SetDrawBright(RGBA_GETRED(rgba), RGBA_GETGREEN(rgba), RGBA_GETBLUE(rgba))) { DIE(); }	//設定
#if 0	//★本当はこのブロックのコードで正しいのだが…
	    #ifndef USE_DRAWROTAGRAPHFAST
		/*if(*/DrawRectRotaGraph3(
	    #else //USE_DRAWROTAGRAPHFAST
		/*if(*/DrawRectRotaGraphFast3(
	    #endif//USE_DRAWROTAGRAPHFAST
			x,
			y,
			stSprDef.sx,
			stSprDef.sy,
			stSprDef.w,
			stSprDef.h,
			stSprDef.cx,
			stSprDef.cy,
			scale_x,
			scale_y,
			rot,
			gr, TRUE/*TransFlag*/, FALSE/*TurnFlag*/)/*) { DIE(); }*/;				//描画系関数は極小領域の描画時にエラーを返す事が有るようなのでエラーを無視する事にした。
#else	//★DxLib(Ver 3.15c時点)のバグ(?)回避のために、以下のように変更しました。
	// * Wed Feb 03 22:03:58 JST 2016 Naoyuki Sawa
	// - PlayMovieToGraph()でムービーを再生中のグラフィックを、DrawRectRotaGraph3()で描画しようとすると、ムービーが1フレーム目で止まってしまうようです。(Ogv形式のムービーで確認しました。)
	//   DrawRectRotaGraph()やDrawRectRotaGraph2()でも駄目で、DrawRectExtendGraph()やDrawRotaGraph()ならば大丈夫みたいです。
	//   DxLib(Ver 3.15c時点)のバグ(?)のような気がします。
	// - とりあえず、回転角度が0度ならばDrawRectExtendGraph()を使うようにして、回避する事にしました。
	//   この回避処理は、ムービーグラフィックだけでなく、普通のグラフィックを回転角度0度で描こうとした時にも適用されてしまいますが、描画結果は同じなので問題有りません。
	//   尚、この対策で全て解決する訳ではなく、ムービーグラフィックを回転描画しようとした場合は下記の回避処理を通らないので、ムービーが1フレーム目で止まってしまいます。
	//   現状では、ムービーを回転描画しないように、注意して下さい。
	//   別の対策として、DrawRotaGraph()を使う方法も有るのですが、実際にムービーを回転描画するケースは少ないと思うので、下記の対策だけに留める事にしました。
	//   今後、DxLibのバグ(?)が修正されて、ムービーグラフィックをDrawRectRotaGraph3()で描画しても問題無くなったら、以下のブロックは削除して上のブロックに戻す予定です。
	// - DxLibのソースのNS_DrawRectRotaGraph3()を見たところ、何となく原因が判りました。
	//   DrawRectRotaGraph3()は、NS_DerivationGraph()を呼び出して矩形部分を切り取った一時的なグラフィックを作成して、それを描画しています。
	//   切り取ったグラフィックにはMovieHandle属性が継承されず、下位の描画関数の中で、UpdateMovie()が呼び出されないのではないかと思います。
	//   良く考えるとDrawRectRotaGraph3()自体が非公開関数なので、多少動作がおかしいのは止むを得ないような気もします。
		if(rot) {
		    #ifndef USE_DRAWROTAGRAPHFAST
			/*if(*/DrawRectRotaGraph3(
		    #else //USE_DRAWROTAGRAPHFAST
			/*if(*/DrawRectRotaGraphFast3(
		    #endif//USE_DRAWROTAGRAPHFAST
				x,
				y,
				stSprDef.sx,
				stSprDef.sy,
				stSprDef.w,
				stSprDef.h,
				stSprDef.cx,
				stSprDef.cy,
				scale_x,
				scale_y,
				rot,
				gr, TRUE/*TransFlag*/, FALSE/*TurnFlag*/)/*) { DIE(); }*/;			//描画系関数は極小領域の描画時にエラーを返す事が有るようなのでエラーを無視する事にした。
		} else {
			/*if(*/DrawRectExtendGraph(
				x - (stSprDef.cx * scale_x),				//DestX1
				y - (stSprDef.cy * scale_y),				//DestY1
				x - (stSprDef.cx * scale_x) + (stSprDef.w * scale_x),	//DestX2
				y - (stSprDef.cy * scale_y) + (stSprDef.h * scale_y),	//DestY2
				stSprDef.sx,						//SrcX
				stSprDef.sy,						//SrcY
				stSprDef.w,						//SrcWidth
				stSprDef.h,						//SrcHeight
				gr, TRUE/*TransFlag*/)/*) { DIE(); }*/;						//描画系関数は極小領域の描画時にエラーを返す事が有るようなのでエラーを無視する事にした。
		}
#endif
		if(SetDrawBlendMode(BlendMode, BlendParam)) { DIE(); }						//復帰
		if(SetDrawBright(Red, Green, Blue)) { DIE(); }							//復帰
	}
}
/*--------------------------------------------------------------------------*/
//自由変形スプライトを描画する。
void draw_spr3(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, int iSpr, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
	ST_SprDef stSprDef;
	get_sprDef(iSpr, &stSprDef);	//自由変形スプライトにおいては、スプライトの描画原点(stSprDef.cx,stSprDef.cy)は参照しない。
#ifdef  USE_AUTOSCALE
	{
		//当関数は、自動スケーリングの対応は不要です。
		//描画先の座標を直接指定する仕様だから、テクスチャのサイズに依存しないからです。
		/** no job **/
	}
#endif//USE_AUTOSCALE
#ifdef  USE_DRAWOFFSET
	if(DrawOffsetX || DrawOffsetY) {
		x1 += DrawOffsetX;
		y1 += DrawOffsetY;
		x2 += DrawOffsetX;
		y2 += DrawOffsetY;
		x3 += DrawOffsetX;
		y3 += DrawOffsetY;
		x4 += DrawOffsetX;
		y4 += DrawOffsetY;
	}
#endif//USE_DRAWOFFSET
	int gr = load_grp(stSprDef.iTexNo);
#ifdef  USE_SOFDEC
	if(ht_usm) {	//ハッシュテーブルが作成されていたら、Sofdecムービーである可能性が有る。
		ST_PlayingUsm* pUsm = (ST_PlayingUsm*)ght_get(ht_usm, sizeof stSprDef.iTexNo, &stSprDef.iTexNo);
		if(pUsm) {	//Sofdecムービーならば…
			draw_spr3_SofdecYUV(x1, y1, x2, y2, x3, y3, x4, y4, pUsm->TBL_GrHandle, rgba, mode, &stSprDef);
			return;	//ここまで
		}
	}
#endif//USE_SOFDEC
	{
		int BlendMode, BlendParam, Red, Green, Blue;
		if(GetDrawBlendMode(&BlendMode, &BlendParam)) { DIE(); }					//退避
		if(GetDrawBright(&Red, &Green, &Blue)) { DIE(); }						//退避
		if(SetDrawBlendMode(mode, RGBA_GETALPHA(rgba))) { DIE(); }					//設定
		if(SetDrawBright(RGBA_GETRED(rgba), RGBA_GETGREEN(rgba), RGBA_GETBLUE(rgba))) { DIE(); }	//設定
		/*if(*/DrawRectModiGraph(
			x1,
			y1,
			x2,
			y2,
			x3,
			y3,
			x4,
			y4,
			stSprDef.sx,
			stSprDef.sy,
			stSprDef.w,
			stSprDef.h,
			gr, TRUE/*TransFlag*/)/*) { DIE(); }*/;							//描画系関数は極小領域の描画時にエラーを返す事が有るようなのでエラーを無視する事にした。
		if(SetDrawBlendMode(BlendMode, BlendParam)) { DIE(); }						//復帰
		if(SetDrawBright(Red, Green, Blue)) { DIE(); }							//復帰
	}
}
/*--------------------------------------------------------------------------*/
//トライアングルストリップを描画する。(関数名の'ts'は'TriangleStrip'の略です。)
void draw_spr_ts(const int xy[/*n*/][2/*x,y*/], const double uv[/*n*/][2/*u,v*/], int n, int iSpr, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
	ST_SprDef stSprDef;
	get_sprDef(iSpr, &stSprDef);
#ifdef  USE_AUTOSCALE
	{
		//当関数は、自動スケーリングの対応は不要です。
		//描画先の座標を直接指定する仕様だから、テクスチャのサイズに依存しないからです。
		/** no job **/
	}
#endif//USE_AUTOSCALE
//{{2016/09/24削除:draw_spr_ts()が誤って描画オフセットを二重に加算してしまっていたバグを修正しました。draw_spr_ts()はdraw_grp_ts()を呼び出し、draw_grp_ts()が描画オフセットを加算するので、draw_spr_ts()での加算は不要でした。ただし、draw_spr_ts_SofdecYUV()を呼び出す場合は、draw_spr_ts_SofdecYUV()は描画オフセットを加算しないので、draw_grp_ts()で描画オフセットを加算しておく必要があります。従って今回の修正内容は、draw_spr_ts()内で描画オフセットを加算していた位置を、draw_spr_ts_SofdecYUV()を呼び出す直前へ変更しました。
//#ifdef  USE_DRAWOFFSET
//	if(DrawOffsetX || DrawOffsetY) {
//		int (*_xy)[2/*x,y*/] = (int (*)[2])alloca(sizeof(int[2/*x,y*/]) * n);
//		int i;
//		for(i = 0; i < n; i++) {
//			_xy[i][0/*x*/] = xy[i][0/*x*/] + DrawOffsetX;
//			_xy[i][1/*y*/] = xy[i][1/*y*/] + DrawOffsetY;
//		}
//		xy = _xy;
//	}
//#endif//USE_DRAWOFFSET
//}}2016/09/24削除:draw_spr_ts()が誤って描画オフセットを二重に加算してしまっていたバグを修正しました。draw_spr_ts()はdraw_grp_ts()を呼び出し、draw_grp_ts()が描画オフセットを加算するので、draw_spr_ts()での加算は不要でした。ただし、draw_spr_ts_SofdecYUV()を呼び出す場合は、draw_spr_ts_SofdecYUV()は描画オフセットを加算しないので、draw_grp_ts()で描画オフセットを加算しておく必要があります。従って今回の修正内容は、draw_spr_ts()内で描画オフセットを加算していた位置を、draw_spr_ts_SofdecYUV()を呼び出す直前へ変更しました。
	int gr = load_grp(stSprDef.iTexNo);
#ifdef  USE_SOFDEC
	if(ht_usm) {	//ハッシュテーブルが作成されていたら、Sofdecムービーである可能性が有る。
		ST_PlayingUsm* pUsm = (ST_PlayingUsm*)ght_get(ht_usm, sizeof stSprDef.iTexNo, &stSprDef.iTexNo);
		if(pUsm) {	//Sofdecムービーならば…
//{{2016/09/24追加:draw_spr_ts()が誤って描画オフセットを二重に加算してしまっていたバグを修正しました。draw_spr_ts()はdraw_grp_ts()を呼び出し、draw_grp_ts()が描画オフセットを加算するので、draw_spr_ts()での加算は不要でした。ただし、draw_spr_ts_SofdecYUV()を呼び出す場合は、draw_spr_ts_SofdecYUV()は描画オフセットを加算しないので、draw_grp_ts()で描画オフセットを加算しておく必要があります。従って今回の修正内容は、draw_spr_ts()内で描画オフセットを加算していた位置を、draw_spr_ts_SofdecYUV()を呼び出す直前へ変更しました。
#ifdef  USE_DRAWOFFSET
			if(DrawOffsetX || DrawOffsetY) {
				int (*_xy)[2/*x,y*/] = (int (*)[2])alloca(sizeof(int[2/*x,y*/]) * n);
				int i;
				for(i = 0; i < n; i++) {
					_xy[i][0/*x*/] = xy[i][0/*x*/] + DrawOffsetX;
					_xy[i][1/*y*/] = xy[i][1/*y*/] + DrawOffsetY;
				}
				xy = _xy;
			}
#endif//USE_DRAWOFFSET
//}}2016/09/24追加:draw_spr_ts()が誤って描画オフセットを二重に加算してしまっていたバグを修正しました。draw_spr_ts()はdraw_grp_ts()を呼び出し、draw_grp_ts()が描画オフセットを加算するので、draw_spr_ts()での加算は不要でした。ただし、draw_spr_ts_SofdecYUV()を呼び出す場合は、draw_spr_ts_SofdecYUV()は描画オフセットを加算しないので、draw_grp_ts()で描画オフセットを加算しておく必要があります。従って今回の修正内容は、draw_spr_ts()内で描画オフセットを加算していた位置を、draw_spr_ts_SofdecYUV()を呼び出す直前へ変更しました。
			draw_spr_ts_SofdecYUV(xy, uv, n, pUsm->TBL_GrHandle, rgba, mode, &stSprDef);
			return;	//ここまで
		}
	}
#endif//USE_SOFDEC
	int i, Width, Height;
	//テクスチャ座標データを作成する。
	double (*_uv)[2/*u,v*/] = (double (*)[2/*u,v*/])alloca(sizeof(double[2/*u,v*/]) * n);
	memset(_uv, 0, sizeof(double[2/*u,v*/]) * n);
	if(GetGraphSize(gr, &Width, &Height)) { DIE(); }
	for(i = 0; i < n; i++) {
		_uv[i][0/*u*/] = ((uv[i][0/*u*/] * stSprDef.w) + stSprDef.sx) / Width;
		_uv[i][1/*v*/] = ((uv[i][1/*v*/] * stSprDef.h) + stSprDef.sy) / Height;
	}
	draw_grp_ts(xy, _uv, n, stSprDef.iTexNo, rgba, mode);
}
/****************************************************************************
 *	太いポリラインの描画
 ****************************************************************************/
//与えられた、始点(x1,y1),終点(x2,y2),幅wに対して、以下の座標を作成する。
//
//	edge_p[0][0]	始点から終点を見て、始点の左方向に(w/2)の位置	┬太い線の、左側の枠に相当します。
//	edge_p[0][1]	始点から終点を見て、終点の左方向に(w/2)の位置	┘
//	edge_p[1][0]	始点から終点を見て、始点の右方向に(w/2)の位置	┬太い線の、右側の枠に相当します。
//	edge_p[1][1]	始点から終点を見て、終点の右方向に(w/2)の位置	┘
//
//	図示すると、以下の通りです。
//
//	┌→x+
//	↓
//	y+	edge_p[0][0]×────────────→×edge_p[0][1]
//		　　　　　　↑　　　　　　　　　　　　　↑
//		　　 幅(w/2)│　　　　　　　　　　　　　│幅(w/2)
//		　　　　　　↓　　　　　　　　　　　　　↓
//		 始点(x1,y1)×────────────→×終点(x2,y2)
//		　　　　　　↑　　　　　　　　　　　　　↑
//		　　 幅(w/2)│　　　　　　　　　　　　　│幅(w/2)
//		　　　　　　↓　　　　　　　　　　　　　↓
//		edge_p[1][0]×────────────→×edge_p[1][1]
//
static void calc_thick_polyline_edge_p(int x1, int y1, int x2, int y2, int w, vec2i edge_p[2/*進行方向左側,進行方向右側*/][2/*始点,終点*/]) {
	//始点を表すベクトルを作成する。
	vec2i p1 = {
		x1,
		y1,
	};
	//終点を表すベクトルを作成する。
	vec2i p2 = {
		x2,
		y2,
	};
	//始点から終点へのベクトルを求める。
	vec2i v12 = {
		p2.x - p1.x,
		p2.y - p1.y,
	};
	//v12の長さを求める。
	int m12 = vec2i_mag(&v12);
//{{2016/10/07追加:calc_thick_polyline_edge_p()に、長さ0の線分が指定された場合の安全対策を追加しました。
	//v12の長さが0ならば…
	if(!m12) {
		//全ての座標に同じ座標を格納する。
		edge_p[0][0] = edge_p[1][0] = p1;	//┬ここへ来たという事は(p1==p2)のはずなので、全てp1,又は,全てp2にしても構わないのだが、一応、p1が始点,p2が終点という意味なので、このように使い分けて格納する事にした。
		edge_p[0][1] = edge_p[1][1] = p2;	//┘
		return;	//ここまで
	}
//}}2016/10/07追加:calc_thick_polyline_edge_p()に、長さ0の線分が指定された場合の安全対策を追加しました。
	//v12に垂直なベクトルを求める。
	vec2i w12 = {
		-v12.y,
		 v12.x,
	};
	//始点から、w12のマイナス方向に、距離(w/2)の位置の点を求める。
	vec2i p11 = {
		p1.x - ((w12.x * w) / (m12 * 2)),
		p1.y - ((w12.y * w) / (m12 * 2)),
	};
	//始点から、w12のプラス方向に、距離(w/2)の位置の点を求める。
	// - 前の処理での除算の切り捨て分を吸収するために、始点基準ではなく、p11基準で計算する。
	//   そうしないと、幅がwよりも小さくなってしまう事が生じる。
	vec2i p21 = {
		p11.x + (w12.x * w) / m12,
		p11.y + (w12.y * w) / m12,
	};
	//終点から、w12のマイナス方向に、距離(w/2)の位置の点を求める。
	// - 素直に計算しても構わないのだが、(p11+v12)で計算する方が簡単である。
	vec2i p12 = {
		p11.x + v12.x,
		p11.y + v12.y,
	};
	//終点から、w12のプラス方向に、距離(w/2)の位置の点を求める。
	// - 素直に計算しても構わないのだが、(p12+v12)で計算する方が簡単である。
	vec2i p22 = {
		p21.x + v12.x,
		p21.y + v12.y,
	};
	//結果を格納する。
	edge_p[0][0] = p11;	//始点から、w12のマイナス方向に、距離(w/2)の位置の点を格納する。
	edge_p[0][1] = p12;	//終点から、w12のマイナス方向に、距離(w/2)の位置の点を格納する。
	edge_p[1][0] = p21;	//始点から、w12のプラス方向に、距離(w/2)の位置の点を格納する。
	edge_p[1][1] = p22;	//終点から、w12のプラス方向に、距離(w/2)の位置の点を格納する。
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//draw_grp_thick_polyline()とdraw_spr_thick_polyline()の共通サブルーチン
// - 2016/10/04現在の実装では、接合方法はBevel接合に決め打ちとしており、Round接合やMiter接合は対応していません。
//   Round接合はポリゴンで描画するのはかなり手間が掛かるので、未対応です。
//   Miter接合は、MiterLimit等を考慮する必要が有ってBevel接合よりもやや手間が掛かるし、かなり太い線でない限りMiter接合とBevel接合とでさほど見た目の違いは目立たないので、Bevel接合で十分だと思います。
//   上記の理由により、接合方法はBevel接合に決め打ちとしました。
static void draw_grp_or_spr_thick_polyline(const int xy[/*n*/][2/*x,y*/], const double uv[/*n*/][2/*進行方向左側,進行方向右側*/][2/*u,v*/], int n/*0不可*/, int w, int iGrp_or_iSpr, int rgba, int mode, void (*draw_grp_or_spr_ts)(const int xy[][2], const double uv[][2], int n, int iSpr, int rgba, int mode)) {
	vec2i (*edge_p)[2/*左側,右側*/][2/*始点,終点*/] = (vec2i (*)[2][2])alloca(sizeof(vec2i[2/*左側,右側*/][2/*始点,終点*/]) * (n - 1));
	int    (*ts_xy)[2/*x,y*/] = (int    (*)[2/*x,y*/])alloca(sizeof(   int[2/*x,y*/]) * ((n - 1) * 4));	//┬1線分あたり4頂点に展開するので((n-1)*4)となります。現在は実装の簡単のために交点も二重に登録していますが、交点を二重に登録しないようにすればもっと減らせるはずです。とは言え、最大では((n-1)*4)になり得ます(一直線上に並んでいる場合など)。
	double (*ts_uv)[2/*u,v*/] = (double (*)[2/*u,v*/])alloca(sizeof(double[2/*u,v*/]) * ((n - 1) * 4));	//┘⇒{{2016/10/05コメント追記:よく考えたら上記の方法で頂点数を減らすのは無理でした。左右の順序を逆にすると、その部分でトライアングルストリップの進行方向の辺が変わってしまって、意図したトライアングルストリップにならないからです。それを防ぐには、全体の外枠を生成してからあらためてトライアングルストリップに再構成すれば可能(?)かも知れないのですが、かなり手間が掛かりそうなのでまだ検証していません。動作上は現在のままで問題無いので、現在のままで良いと思います。}}
	int i, ts_i = 0;
	//与えられた始点(x1,y1),終点(x2,y2),幅wに対して、各線分の始点と終点の座標を生成する。
	for(i = 0; i < (n - 1); i++) {
		int x1 = xy[i + 0][0/*x*/];
		int y1 = xy[i + 0][1/*y*/];
		int x2 = xy[i + 1][0/*x*/];
		int y2 = xy[i + 1][1/*y*/];
		calc_thick_polyline_edge_p(x1, y1, x2, y2, w, edge_p[i]);
	}
	//各線分について…
	for(i = 0; i < (n - 1); i++) {	//ここは(n-1)で合っている。この後の判定では(n-2)の箇所が有るので混同しないよう注意しろ。
		vec2i  p11 = edge_p[i][0/*左側*/][0/*始点*/];				//┐
		double u11 = uv[i + 0][0/*左側*/][0/*u*/];				//├左側始点
		double v11 = uv[i + 0][0/*左側*/][1/*v*/];				//┘
		vec2i  p12 = edge_p[i][0/*左側*/][1/*終点*/];				//┐
		double u12 = uv[i + 1][0/*左側*/][0/*u*/];				//├左側終点
		double v12 = uv[i + 1][0/*左側*/][1/*v*/];				//┘
		vec2i  p21 = edge_p[i][1/*右側*/][0/*始点*/];				//┐
		double u21 = uv[i + 0][1/*右側*/][0/*u*/];				//├右側始点
		double v21 = uv[i + 0][1/*右側*/][1/*v*/];				//┘
		vec2i  p22 = edge_p[i][1/*右側*/][1/*終点*/];				//┐
		double u22 = uv[i + 1][1/*右側*/][0/*u*/];				//├右側終点
		double v22 = uv[i + 1][1/*右側*/][1/*v*/];				//┘
		//最初の線分ならば…
		if(!i) {
			//左側の線分の始点を格納する。
			if(ts_i >= ((n - 1) * 4)) { DIE(); }				//バグ
			ts_xy[ts_i][0/*x*/] = p11.x;					//┐
			ts_xy[ts_i][1/*y*/] = p11.y;					//├左側始点
			ts_uv[ts_i][0/*u*/] = u11;					//│
			ts_uv[ts_i][1/*v*/] = v11;					//┘
			ts_i++;
			//右側の線分の始点を格納する。
			if(ts_i >= ((n - 1) * 4)) { DIE(); }				//バグ
			ts_xy[ts_i][0/*x*/] = p21.x;					//┐
			ts_xy[ts_i][1/*y*/] = p21.y;					//├右側始点
			ts_uv[ts_i][0/*u*/] = u21;					//│
			ts_uv[ts_i][1/*v*/] = v21;					//┘
			ts_i++;
		}
		//最後の線分でなければ…(最初の線分である可能性も有る)
		if(i < (n - 2)) {	//'最後の接点'ではなく'最後の線分'を判定するので、(n-1)ではなく(n-2)とする事に注意しろ。
			//次の線分を取得する。
			i++;
			vec2i  next_p11 = edge_p[i][0/*左側*/][0/*始点*/];		//┐
			double next_u11 = uv[i + 0][0/*左側*/][0/*u*/];			//├左側始点
			double next_v11 = uv[i + 0][0/*左側*/][1/*v*/];			//┘
			vec2i  next_p12 = edge_p[i][0/*左側*/][1/*終点*/];		//┐
			double next_u12 = uv[i + 1][0/*左側*/][0/*u*/];			//├左側終点
			double next_v12 = uv[i + 1][0/*左側*/][1/*v*/];			//┘
			vec2i  next_p21 = edge_p[i][1/*右側*/][0/*始点*/];		//┐
			double next_u21 = uv[i + 0][1/*右側*/][0/*u*/];			//├右側始点
			double next_v21 = uv[i + 0][1/*右側*/][1/*v*/];			//┘
			vec2i  next_p22 = edge_p[i][1/*右側*/][1/*終点*/];		//┐
			double next_u22 = uv[i + 1][1/*右側*/][0/*u*/];			//├右側終点
			double next_v22 = uv[i + 1][1/*右側*/][1/*v*/];			//┘
			i--;
			//左側の線分が交差していたら…
			if(vec2i_is_intersected_ls(&p11, &p12, &next_p11, &next_p12)) {
				//左側の線分の交点を求める。
				vec2i tmp_p1 = vec2i_intersection_l(&p11, &p12, &next_p11, &next_p12);
				//右側の線分が交差していたら…
				if(vec2i_is_intersected_ls(&p21, &p22, &next_p21, &next_p22)) {
					//右側の線分の交点を求める。
					vec2i tmp_p2 = vec2i_intersection_l(&p21, &p22, &next_p21, &next_p22);
					//左側の線分の交点を格納する。
					if(ts_i >= ((n - 1) * 4)) { DIE(); }		//バグ
					ts_xy[ts_i][0/*x*/] = tmp_p1.x;			//┬左側交点
					ts_xy[ts_i][1/*y*/] = tmp_p1.y;			//┘
				    //{{2016/10/05変更:draw_grp_or_spr_thick_polyline()にて、テクスチャ座標の取得に無駄な平均計算を行っていたのを修正しました。
				    //	ts_uv[ts_i][0/*u*/] = (u12 + next_u11) / 2;	//┬現在の線分の左側終点と、次の線分の左側始点の平均	これで良いか?要検討	⇒{{2016/10/05コメント追記:動作上は左記でも問題無いが無駄でした。u12とnext_u11は与えられた配列uv[]の同じ箇所から取って来る値なので、u12とnext_u11は必ず同じ値であり、平均計算を行うのは無駄でした。}}
				    //	ts_uv[ts_i][1/*v*/] = (v12 + next_v11) / 2;	//┘
				    //↓2016/10/05変更:draw_grp_or_spr_thick_polyline()にて、テクスチャ座標の取得に無駄な平均計算を行っていたのを修正しました。
					ts_uv[ts_i][0/*u*/] = u12;			//┬現在の線分の左側終点。又は、次の線分の左側始点の値を使っても同じです。
					ts_uv[ts_i][1/*v*/] = v12;			//┘
				    //}}2016/10/05変更:draw_grp_or_spr_thick_polyline()にて、テクスチャ座標の取得に無駄な平均計算を行っていたのを修正しました。
					ts_i++;
					//右側の線分の交点を格納する。
					if(ts_i >= ((n - 1) * 4)) { DIE(); }		//バグ
					ts_xy[ts_i][0/*x*/] = tmp_p2.x;			//┬右側交点
					ts_xy[ts_i][1/*y*/] = tmp_p2.y;			//┘
				    //{{2016/10/05変更:draw_grp_or_spr_thick_polyline()にて、テクスチャ座標の取得に無駄な平均計算を行っていたのを修正しました。
				    //	ts_uv[ts_i][0/*u*/] = (u22 + next_u21) / 2;	//┬現在の線分の右側終点と、次の線分の右側始点の平均	これで良いか?要検討	⇒{{2016/10/05コメント追記:動作上は左記でも問題無いが無駄でした。u22とnext_u21は与えられた配列uv[]の同じ箇所から取って来る値なので、u22とnext_u21は必ず同じ値であり、平均計算を行うのは無駄でした。}}
				    //	ts_uv[ts_i][1/*v*/] = (v22 + next_v21) / 2;	//┘
				    //↓2016/10/05変更:draw_grp_or_spr_thick_polyline()にて、テクスチャ座標の取得に無駄な平均計算を行っていたのを修正しました。
					ts_uv[ts_i][0/*u*/] = u22;			//┬現在の線分の右側終点。又は、次の線分の右側始点の値を使っても同じです。
					ts_uv[ts_i][1/*v*/] = v22;			//┘
				    //}}2016/10/05変更:draw_grp_or_spr_thick_polyline()にて、テクスチャ座標の取得に無駄な平均計算を行っていたのを修正しました。
					ts_i++;
				//右側の線分が交差していなければ…
				} else {
					//左側の線分の交点を格納する。
					if(ts_i >= ((n - 1) * 4)) { DIE(); }		//バグ
					ts_xy[ts_i][0/*x*/] = tmp_p1.x;			//┬左側交点
					ts_xy[ts_i][1/*y*/] = tmp_p1.y;			//┘
				    //{{2016/10/05変更:draw_grp_or_spr_thick_polyline()にて、テクスチャ座標の取得に無駄な平均計算を行っていたのを修正しました。
				    //	ts_uv[ts_i][0/*u*/] = (u12 + next_u11) / 2;	//┬現在の線分の左側終点と、次の線分の左側始点の平均	これで良いか?要検討	⇒{{2016/10/05コメント追記:動作上は左記でも問題無いが無駄でした。u12とnext_u11は与えられた配列uv[]の同じ箇所から取って来る値なので、u12とnext_u11は必ず同じ値であり、平均計算を行うのは無駄でした。}}
				    //	ts_uv[ts_i][1/*v*/] = (v12 + next_v11) / 2;	//┘
				    //↓2016/10/05変更:draw_grp_or_spr_thick_polyline()にて、テクスチャ座標の取得に無駄な平均計算を行っていたのを修正しました。
					ts_uv[ts_i][0/*u*/] = u12;			//┬現在の線分の左側終点。又は、次の線分の左側始点の値を使っても同じです。
					ts_uv[ts_i][1/*v*/] = v12;			//┘
				    //}}2016/10/05変更:draw_grp_or_spr_thick_polyline()にて、テクスチャ座標の取得に無駄な平均計算を行っていたのを修正しました。
					ts_i++;
					//右側の線分の終点を格納する。
					if(ts_i >= ((n - 1) * 4)) { DIE(); }		//バグ
					ts_xy[ts_i][0/*x*/] = p22.x;			//┐
					ts_xy[ts_i][1/*y*/] = p22.y;			//├右側終点
					ts_uv[ts_i][0/*u*/] = u22;			//│
					ts_uv[ts_i][1/*v*/] = v22;			//┘
					ts_i++;
					//左側の線分の交点を格納する。					//交点を二重に登録しないようにして、左右の順序を逆転しろ。	⇒{{2016/10/05コメント追記:よく考えたら無理でした。左右の順序を逆にすると、その部分でトライアングルストリップの進行方向の辺が変わってしまって、意図したトライアングルストリップにならないからです。それを防ぐには、全体の外枠を生成してからあらためてトライアングルストリップに再構成すれば可能(?)かも知れないのですが、かなり手間が掛かりそうなのでまだ検証していません。動作上は現在のままで問題無いので、現在のままで良いと思います。}}
					if(ts_i >= ((n - 1) * 4)) { DIE(); }		//バグ
					ts_xy[ts_i][0/*x*/] = tmp_p1.x;			//┬左側交点
					ts_xy[ts_i][1/*y*/] = tmp_p1.y;			//┘
				    //{{2016/10/05変更:draw_grp_or_spr_thick_polyline()にて、テクスチャ座標の取得に無駄な平均計算を行っていたのを修正しました。
				    //	ts_uv[ts_i][0/*u*/] = (u12 + next_u11) / 2;	//┬現在の線分の左側終点と、次の線分の左側始点の平均	これで良いか?要検討	⇒{{2016/10/05コメント追記:動作上は左記でも問題無いが無駄でした。u12とnext_u11は与えられた配列uv[]の同じ箇所から取って来る値なので、u12とnext_u11は必ず同じ値であり、平均計算を行うのは無駄でした。}}
				    //	ts_uv[ts_i][1/*v*/] = (v12 + next_v11) / 2;	//┘
				    //↓2016/10/05変更:draw_grp_or_spr_thick_polyline()にて、テクスチャ座標の取得に無駄な平均計算を行っていたのを修正しました。
					ts_uv[ts_i][0/*u*/] = u12;			//┬現在の線分の左側終点。又は、次の線分の左側始点の値を使っても同じです。
					ts_uv[ts_i][1/*v*/] = v12;			//┘
				    //}}2016/10/05変更:draw_grp_or_spr_thick_polyline()にて、テクスチャ座標の取得に無駄な平均計算を行っていたのを修正しました。
					ts_i++;
					//次の線分の右側の線分の始点を格納する。
					if(ts_i >= ((n - 1) * 4)) { DIE(); }		//バグ
					ts_xy[ts_i][0/*x*/] = next_p21.x;		//┐
					ts_xy[ts_i][1/*y*/] = next_p21.y;		//├次の線分の右側始点
					ts_uv[ts_i][0/*u*/] = next_u21;			//│
					ts_uv[ts_i][1/*v*/] = next_v21;			//┘
					ts_i++;
				}
			//左側の線分が交差していなければ…
			} else {
				//右側の線分が交差していたら…
				if(vec2i_is_intersected_ls(&p21, &p22, &next_p21, &next_p22)) {
					//右側の線分の交点を求める。
					vec2i tmp_p2 = vec2i_intersection_l(&p21, &p22, &next_p21, &next_p22);
					//左側の線分の終点を格納する。
					if(ts_i >= ((n - 1) * 4)) { DIE(); }		//バグ
					ts_xy[ts_i][0/*x*/] = p12.x;			//┐
					ts_xy[ts_i][1/*y*/] = p12.y;			//├左側終点
					ts_uv[ts_i][0/*u*/] = u12;			//│
					ts_uv[ts_i][1/*v*/] = v12;			//┘
					ts_i++;
					//右側の線分の交点を格納する。
					if(ts_i >= ((n - 1) * 4)) { DIE(); }		//バグ
					ts_xy[ts_i][0/*x*/] = tmp_p2.x;			//┬右側交点
					ts_xy[ts_i][1/*y*/] = tmp_p2.y;			//┘
				    //{{2016/10/05変更:draw_grp_or_spr_thick_polyline()にて、テクスチャ座標の取得に無駄な平均計算を行っていたのを修正しました。
				    //	ts_uv[ts_i][0/*u*/] = (u22 + next_u21) / 2;	//┬現在の線分の右側終点と、次の線分の右側始点の平均	これで良いか?要検討	⇒{{2016/10/05コメント追記:動作上は左記でも問題無いが無駄でした。u22とnext_u21は与えられた配列uv[]の同じ箇所から取って来る値なので、u22とnext_u21は必ず同じ値であり、平均計算を行うのは無駄でした。}}
				    //	ts_uv[ts_i][1/*v*/] = (v22 + next_v21) / 2;	//┘
				    //↓2016/10/05変更:draw_grp_or_spr_thick_polyline()にて、テクスチャ座標の取得に無駄な平均計算を行っていたのを修正しました。
					ts_uv[ts_i][0/*u*/] = u22;			//┬現在の線分の右側終点。又は、次の線分の右側始点の値を使っても同じです。
					ts_uv[ts_i][1/*v*/] = v22;			//┘
				    //}}2016/10/05変更:draw_grp_or_spr_thick_polyline()にて、テクスチャ座標の取得に無駄な平均計算を行っていたのを修正しました。
					ts_i++;
					//次の線分の左側の線分の始点を格納する。
					if(ts_i >= ((n - 1) * 4)) { DIE(); }		//バグ
					ts_xy[ts_i][0/*x*/] = next_p11.x;		//┐
					ts_xy[ts_i][1/*y*/] = next_p11.y;		//├次の線分の左側始点
					ts_uv[ts_i][0/*u*/] = next_u11;			//│
					ts_uv[ts_i][1/*v*/] = next_v11;			//┘
					ts_i++;
					//右側の線分の交点を格納する。					//交点を二重に登録しないようにして、左右の順序を逆転しろ。	⇒{{2016/10/05コメント追記:よく考えたら無理でした。左右の順序を逆にすると、その部分でトライアングルストリップの進行方向の辺が変わってしまって、意図したトライアングルストリップにならないからです。それを防ぐには、全体の外枠を生成してからあらためてトライアングルストリップに再構成すれば可能(?)かも知れないのですが、かなり手間が掛かりそうなのでまだ検証していません。動作上は現在のままで問題無いので、現在のままで良いと思います。}}
					if(ts_i >= ((n - 1) * 4)) { DIE(); }		//バグ
					ts_xy[ts_i][0/*x*/] = tmp_p2.x;			//┬右側交点
					ts_xy[ts_i][1/*y*/] = tmp_p2.y;			//┘
				    //{{2016/10/05変更:draw_grp_or_spr_thick_polyline()にて、テクスチャ座標の取得に無駄な平均計算を行っていたのを修正しました。
				    //	ts_uv[ts_i][0/*u*/] = (u22 + next_u21) / 2;	//┬現在の線分の右側終点と、次の線分の右側始点の平均	これで良いか?要検討	⇒{{2016/10/05コメント追記:動作上は左記でも問題無いが無駄でした。u22とnext_u21は与えられた配列uv[]の同じ箇所から取って来る値なので、u22とnext_u21は必ず同じ値であり、平均計算を行うのは無駄でした。}}
				    //	ts_uv[ts_i][1/*v*/] = (v22 + next_v21) / 2;	//┘
				    //↓2016/10/05変更:draw_grp_or_spr_thick_polyline()にて、テクスチャ座標の取得に無駄な平均計算を行っていたのを修正しました。
					ts_uv[ts_i][0/*u*/] = u22;			//┬現在の線分の右側終点。又は、次の線分の右側始点の値を使っても同じです。
					ts_uv[ts_i][1/*v*/] = v22;			//┘
				    //}}2016/10/05変更:draw_grp_or_spr_thick_polyline()にて、テクスチャ座標の取得に無駄な平均計算を行っていたのを修正しました。
					ts_i++;
				//右側の線分が交差していなければ…
				} else {
					//左側の線分の終点を格納する。
					if(ts_i >= ((n - 1) * 4)) { DIE(); }		//バグ
					ts_xy[ts_i][0/*x*/] = p12.x;			//┐
					ts_xy[ts_i][1/*y*/] = p12.y;			//├左側終点
					ts_uv[ts_i][0/*u*/] = u12;			//│
					ts_uv[ts_i][1/*v*/] = v12;			//┘
					ts_i++;
					//右側の線分の終点を格納する。
					if(ts_i >= ((n - 1) * 4)) { DIE(); }		//バグ
					ts_xy[ts_i][0/*x*/] = p22.x;			//┐
					ts_xy[ts_i][1/*y*/] = p22.y;			//├右側終点
					ts_uv[ts_i][0/*u*/] = u22;			//│
					ts_uv[ts_i][1/*v*/] = v22;			//┘
					ts_i++;
					//次の線分の左側の線分の始点を格納する。
					if(ts_i >= ((n - 1) * 4)) { DIE(); }		//バグ
					ts_xy[ts_i][0/*x*/] = next_p11.x;		//┐
					ts_xy[ts_i][1/*y*/] = next_p11.y;		//├次の線分の左側始点
					ts_uv[ts_i][0/*u*/] = next_u11;			//│
					ts_uv[ts_i][1/*v*/] = next_v11;			//┘
					ts_i++;
					//次の線分の右側の線分の始点を格納する。
					if(ts_i >= ((n - 1) * 4)) { DIE(); }		//バグ
					ts_xy[ts_i][0/*x*/] = next_p21.x;		//┐
					ts_xy[ts_i][1/*y*/] = next_p21.y;		//├次の線分の右側始点
					ts_uv[ts_i][0/*u*/] = next_u21;			//│
					ts_uv[ts_i][1/*v*/] = next_v21;			//┘
					ts_i++;
				}
			}

		//最後の線分ならば…(最初の線分である可能性も有る)
		} else {
			//左側の線分の終点を格納する。
			if(ts_i >= ((n - 1) * 4)) { DIE(); }				//バグ
			ts_xy[ts_i][0/*x*/] = p12.x;					//┐
			ts_xy[ts_i][1/*y*/] = p12.y;					//├左側終点
			ts_uv[ts_i][0/*u*/] = u12;					//│
			ts_uv[ts_i][1/*v*/] = v12;					//┘
			ts_i++;
			//右側の線分の終点を格納する。
			if(ts_i >= ((n - 1) * 4)) { DIE(); }				//バグ
			ts_xy[ts_i][0/*x*/] = p22.x;					//┐
			ts_xy[ts_i][1/*y*/] = p22.y;					//├右側終点
			ts_uv[ts_i][0/*u*/] = u22;					//│
			ts_uv[ts_i][1/*v*/] = v22;					//┘
			ts_i++;
		}
	}
	if(ts_i != ((n - 1) * 4)) { DIE(); }						//バグ
	//トライアングルストリップを描画する。
	(*draw_grp_or_spr_ts)(ts_xy, ts_uv, ts_i, iGrp_or_iSpr, rgba, mode);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//テクスチャを用いて、太いポリラインを描画する。
// - 2016/10/04現在の実装では、長さ0の線分が含まれていると、ゼロ除算例外が発生すると思います。	┬→{{2016/10/07コメント追加:calc_thick_polyline_edge_p()に、長さ0の線分が指定された場合の安全対策を追加しました。}}
//   呼び出し側で対応する事も可能ですが、この程度は当関数側で対応すべきだと思います。		┘
void draw_grp_thick_polyline(const int xy[/*n*/][2/*x,y*/], const double uv[/*n*/][2/*進行方向左側,進行方向右側*/][2/*u,v*/], int n/*0不可*/, int w, int iGrp, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
	draw_grp_or_spr_thick_polyline(xy, uv, n, w, iGrp, rgba, mode, draw_grp_ts);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//スプライトを用いて、太いポリラインを描画する。
// - 2016/10/04現在の実装では、長さ0の線分が含まれていると、ゼロ除算例外が発生すると思います。	┬→{{2016/10/07コメント追加:calc_thick_polyline_edge_p()に、長さ0の線分が指定された場合の安全対策を追加しました。}}
//   呼び出し側で対応する事も可能ですが、この程度は当関数側で対応すべきだと思います。		┘
void draw_spr_thick_polyline(const int xy[/*n*/][2/*x,y*/], const double uv[/*n*/][2/*進行方向左側,進行方向右側*/][2/*u,v*/], int n/*0不可*/, int w, int iSpr, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
	draw_grp_or_spr_thick_polyline(xy, uv, n, w, iSpr, rgba, mode, draw_spr_ts);
}
/*--------------------------------------------------------------------------*/
struct ST_CbkQue_draw_grp_thick_polyline {
	int		n;
	int		w;
	int		iGrp;
	int		rgba;
	int		mode;
    //	int		xy[/*n*/][2/*x,y*/];
    //	double		uv[/*n*/][2/*進行方向左側,進行方向右側*/][2/*u,v*/];
};
static void fn_CbkQue_draw_grp_thick_polyline(ST_CbkQue* pCbkQue, int pri, void* param) {
	struct ST_CbkQue_draw_grp_thick_polyline* p = (struct ST_CbkQue_draw_grp_thick_polyline*)param;
	const int    (*_xy)[2/*x,y*/]                                 = (const int    (*)                                [2/*x,y*/])(  p +    1);
	const double (*_uv)[2/*進行方向左側,進行方向右側*/][2/*u,v*/] = (const double (*)[2/*進行方向左側,進行方向右側*/][2/*u,v*/])(_xy + p->n);
	draw_grp_thick_polyline(_xy, _uv, p->n, p->w, p->iGrp, p->rgba, p->mode);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//□draw_grp_thick_polyline()の優先度付きバージョン
void CbkQue_draw_grp_thick_polyline(ST_CbkQue* pCbkQue, int pri, const int xy[/*n*/][2/*x,y*/], const double uv[/*n*/][2/*進行方向左側,進行方向右側*/][2/*u,v*/], int n/*0不可*/, int w, int iGrp, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
	struct ST_CbkQue_draw_grp_thick_polyline* p = (struct ST_CbkQue_draw_grp_thick_polyline*)CbkQue_AddMem(pCbkQue, pri, fn_CbkQue_draw_grp_thick_polyline, sizeof(struct ST_CbkQue_draw_grp_thick_polyline) + (sizeof(int[2/*x,y*/]) * n) + (sizeof(double[2/*進行方向左側,進行方向右側*/][2/*u,v*/]) * n));
	int    (*_xy)                                [2/*x,y*/] = (int    (*)                                [2/*x,y*/])(  p + 1);
	double (*_uv)[2/*進行方向左側,進行方向右側*/][2/*u,v*/] = (double (*)[2/*進行方向左側,進行方向右側*/][2/*u,v*/])(_xy + n);
	p->n		= n;
	p->w		= w;
	p->iGrp		= iGrp;
	p->rgba		= rgba;
	p->mode		= mode;
	memcpy(_xy, xy, sizeof(   int                                [2/*x,y*/]) * n);
	memcpy(_uv, uv, sizeof(double[2/*進行方向左側,進行方向右側*/][2/*u,v*/]) * n);
}
/*--------------------------------------------------------------------------*/
struct ST_CbkQue_draw_spr_thick_polyline {
	int		n;
	int		w;
	int		iSpr;
	int		rgba;
	int		mode;
    //	int		xy[/*n*/][2/*x,y*/];
    //	double		uv[/*n*/][2/*進行方向左側,進行方向右側*/][2/*u,v*/];
};
static void fn_CbkQue_draw_spr_thick_polyline(ST_CbkQue* pCbkQue, int pri, void* param) {
	struct ST_CbkQue_draw_spr_thick_polyline* p = (struct ST_CbkQue_draw_spr_thick_polyline*)param;
	const int    (*_xy)[2/*x,y*/]                                 = (const int    (*)                                [2/*x,y*/])(  p +    1);
	const double (*_uv)[2/*進行方向左側,進行方向右側*/][2/*u,v*/] = (const double (*)[2/*進行方向左側,進行方向右側*/][2/*u,v*/])(_xy + p->n);
	draw_spr_thick_polyline(_xy, _uv, p->n, p->w, p->iSpr, p->rgba, p->mode);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//□draw_spr_thick_polyline()の優先度付きバージョン
void CbkQue_draw_spr_thick_polyline(ST_CbkQue* pCbkQue, int pri, const int xy[/*n*/][2/*x,y*/], const double uv[/*n*/][2/*進行方向左側,進行方向右側*/][2/*u,v*/], int n/*0不可*/, int w, int iSpr, int rgba /*= -1*/, int mode /*= DX_BLENDMODE_ALPHA*/) {
	struct ST_CbkQue_draw_spr_thick_polyline* p = (struct ST_CbkQue_draw_spr_thick_polyline*)CbkQue_AddMem(pCbkQue, pri, fn_CbkQue_draw_spr_thick_polyline, sizeof(struct ST_CbkQue_draw_spr_thick_polyline) + (sizeof(int[2/*x,y*/]) * n) + (sizeof(double[2/*進行方向左側,進行方向右側*/][2/*u,v*/]) * n));
	int    (*_xy)                                [2/*x,y*/] = (int    (*)                                [2/*x,y*/])(  p + 1);
	double (*_uv)[2/*進行方向左側,進行方向右側*/][2/*u,v*/] = (double (*)[2/*進行方向左側,進行方向右側*/][2/*u,v*/])(_xy + n);
	p->n		= n;
	p->w		= w;
	p->iSpr		= iSpr;
	p->rgba		= rgba;
	p->mode		= mode;
	memcpy(_xy, xy, sizeof(   int                                [2/*x,y*/]) * n);
	memcpy(_uv, uv, sizeof(double[2/*進行方向左側,進行方向右側*/][2/*u,v*/]) * n);
}
/****************************************************************************
 *	DxLib用のムービーグラフィック管理
 ****************************************************************************/
typedef struct _ST_PlayingGrp {
	int	iGrp;		//グラフィック番号	(0=空き)
	int	bPlay;		//今回のフレームで再生したフラグ
} ST_PlayingGrp;
//{{2016/04/07変更:TBL_PlayingGrp[]のサイズを、MAX_MOVIE_NUM⇒16 に変更しました。
//static ST_PlayingGrp TBL_PlayingGrp[MAX_MOVIE_NUM];
//↓2016/04/07変更:TBL_PlayingGrp[]のサイズを、MAX_MOVIE_NUM⇒16 に変更しました。
// * Thu Apr 07 21:58:31 JST 2016 Naoyuki Sawa
// - TBL_PlayingGrp[]のサイズを、MAX_MOVIE_NUM⇒16 に変更しました。
//   変更した理由は、以下の通りです。
//   これまで、TBL_PlayingGrp[]のサイズを、MAX_MOVIE_NUM(=100)としていました。
//   しかし、DxLibの仕様上はMAX_MOVIE_NUM(=100)個のムービーを同時に扱えるはずですが、実際にはムービーサイズに関係無く21個ぐらいでテクスチャが作成出来ないエラーになるようです。(これは前々から気付いていました)
//   従って、TBL_PlayingGrp[]のサイズが100もあっても、ほとんど無駄でした。
//   本日load_grp()が毎回play_grp()を呼び出すように変更した事に伴い、TBL_PlayingGrp[]のサイズが無駄に大きいと全体への負荷が高くなります。(とは言え100要素リニア比較する程度なので、現在のCPUではほとんど問題無いとは思いますが…)
//   そこで、TBL_PlayingGrp[]のサイズを、MAX_MOVIE_NUM⇒16 に変更する事にしました。
//   実際には16個もムービーを同時再生する事は無いと思うので、実質アプリケーションへの影響は無いと思います。
static ST_PlayingGrp TBL_PlayingGrp[16/*調整可*/];
//}}2016/04/07変更:TBL_PlayingGrp[]のサイズを、MAX_MOVIE_NUM⇒16 に変更しました。
/*--------------------------------------------------------------------------*/
//ムービーグラフィックを再生開始,又は,再生継続する。
static void play_grp(int iGrp, int bCont/*0=再生開始,1=再生継続*/) {
	int i;
	//グラフィック番号⇒(グラフィックハンドル+1)を取得する。
	// - play_grp()の中でload_grp()を呼び出すと、無限再帰になってしまうので、load_grp()を呼び出してはいけない。
//{{2016/09/07変更:64ビット対応のための変更を行いました。
//	int gr = (int)ght_get(ht_grp, sizeof iGrp, &iGrp);
//↓2016/09/07変更:64ビット対応のための変更を行いました。
#ifndef _WIN64
	int gr = (int)ght_get(ht_grp, sizeof iGrp, &iGrp);	//32ビット版
#else //_WIN64
	int gr = (intptr_t)ght_get(ht_grp, sizeof iGrp, &iGrp);	//64ビット版
#endif//_WIN64
//}}2016/09/07変更:64ビット対応のための変更を行いました。
	if(!gr) { DIE(); }
	gr--;	//上記の+1と相殺する。
	//初回のload_grp()時にplay_grp(iGrp,0)で再生開始したグラフィック(=ムービーグラフィック)であったか?
	for(i = 0; i < ARRAY_SIZE(TBL_PlayingGrp); i++) {
		if(TBL_PlayingGrp[i].iGrp == iGrp) {			//再生中のグラフィックとして既に登録されていたら…
			TBL_PlayingGrp[i].bPlay = 1;			//今回のフレームで再生したフラグをセットする。
#ifdef  USE_SOFDEC
			//ハッシュテーブルが作成されていたら、Sofdecムービーである可能性が有る。
			if(ht_usm) {
				//Sofdecムービーならば…
				ST_PlayingUsm* pUsm = (ST_PlayingUsm*)ght_get(ht_usm, sizeof iGrp, &iGrp);
				if(pUsm) {
					//デコード済みのフレームが有れば、グラフィックを更新する。
					//デコード済みのフレームが無ければ、グラフィックはそのままとする。
					SofdecHelper_GetFrameYUV(pUsm->iManaPlayer, fnGetFrameYUV, pUsm->TBL_GrHandle);
				}
			}
#endif//USE_SOFDEC
			return;	//ここまで
		}
	}
	//再生継続要求だったならば、ここまで。
	// - 初回のload_grp()時にplay_grp(iGrp,0)で再生開始しなかったグラフィック(=静止画グラフィック)に対しては、play_grp(iGrp,1)は何も行わずダミー処理となる。
	if(bCont) { return; }
	//レジストリから、ループフラグを取得する。
	int bLoop = (REG_get_value_l(stTexInf.pTexDef, iGrp, stTexInf.name_Loop, -1) == 1);	//ループ無しならばレジストリにループフラグは格納されておらず、REG_get_value_l()の戻り値は(-1)である。ループ有りならばレジストリに'Loop=1'が格納されている。従って、REG_get_value_l()の戻り値が1である場合のみbLoopを1とすれば良い。
	for(i = 0; i < ARRAY_SIZE(TBL_PlayingGrp); i++) {
		if(!TBL_PlayingGrp[i].iGrp) {				//空きエントリを見つけたら…
			TBL_PlayingGrp[i].iGrp = iGrp;			//再生中のグラフィックとして登録する。
			TBL_PlayingGrp[i].bPlay = 1;			//今回のフレームで再生したフラグをセットする。
#ifdef  USE_SOFDEC
			//ハッシュテーブルが作成されていたら、Sofdecムービーである可能性が有る。
			if(ht_usm) {
				//Sofdecムービーならば…
				ST_PlayingUsm* pUsm = (ST_PlayingUsm*)ght_get(ht_usm, sizeof iGrp, &iGrp);
				if(pUsm) {
					//再生開始する。
					SofdecHelper_Play(pUsm->iManaPlayer, bLoop);
					//play_grp()で再生開始直後、少なくとも最初のムービーフレームがデコードされるまで待つ事にしました。
					// - 最初のムービーフレームがデコードされる前にこのグラフィックハンドルを使ってDrawGraph()等を呼び出すと、ゴミが描画されてしまう問題が生じるからです。
					while(!SofdecHelper_GetFrameYUV(pUsm->iManaPlayer, fnGetFrameYUV, pUsm->TBL_GrHandle)) {
						SofdecHelper_Exec();	//必要
						Sleep(0);//タイムスライスを放棄する。これは必須ではない。
					}
					return;	//ここまで
				}
			}
#endif//USE_SOFDEC
			if(!bLoop) {
				if(PlayMovieToGraph(gr)) { DIE(); }			//非ループ再生		実際にはここでエラー検出は出来ない。DxLibは通常のテクスチャに対してPlayMovieToGraph()を呼び出しても0(成功)を返すようだ。この特性を利用して、通常のテクスチャに対してもplay_grp()を呼び出せば、自動破棄の仕組みを利用する事が出来る。
			} else {
				if(PlayMovieToGraph(gr, DX_PLAYTYPE_LOOP)) { DIE(); }	//ループ再生		実際にはここでエラー検出は出来ない。DxLibは通常のテクスチャに対してPlayMovieToGraph()を呼び出しても0(成功)を返すようだ。この特性を利用して、通常のテクスチャに対してもplay_grp()を呼び出せば、自動破棄の仕組みを利用する事が出来る。
			}
		    //{{2016/01/26追加:play_grp()で再生開始直後、少なくとも最初のムービーフレームがデコードされるまで待つ事にしました。
			//play_grp()で再生開始直後、少なくとも最初のムービーフレームがデコードされるまで待つ事にしました。
			// - 最初のムービーフレームがデコードされる前にこのグラフィックハンドルを使ってDrawGraph()等を呼び出すと、ゴミが描画されてしまう問題が生じるからです。
			for(;;) {
				int ImageUpdateFlag;
			    //{{2016/05/08変更:DxLib Ver 3.16c(2016/05/05版)で、GetMovieBaseImageToGraph()にImageUpdateFlagSetOnly引数が追加された事に伴い、ImageUpdateFlagSetOnly=TRUEにして呼び出すように変更しました。
			    //	if(!GetMovieBaseImageToGraph(gr, &ImageUpdateFlag)) { DIE(); }
			    //↓2016/05/08変更:DxLib Ver 3.16c(2016/05/05版)で、GetMovieBaseImageToGraph()にImageUpdateFlagSetOnly引数が追加された事に伴い、ImageUpdateFlagSetOnly=TRUEにして呼び出すように変更しました。
			    // * Sun May 08 23:30:59 JST 2016 Naoyuki Sawa
			    // - DxLib Ver 3.16c(2016/05/05版)で、GetMovieBaseImageToGraph()にImageUpdateFlagSetOnly引数が追加された事に伴い、ImageUpdateFlagSetOnly=TRUEにして呼び出すように変更しました。
			    //   □「ＤＸライブラリ質問掲示板 - Ver3.16cでのGetMovieBaseImageToG」(http://dxlib.o.oo7.jp/cgi/patiobbs/patio.cgi?mode=view&no=3820)より引用:
			    //   │因みに、最新のＤＸライブラリではYUVカラーの動画を再生する際のYUVカラーからRGBカラーに変換する処理をGPU(ピクセルシェーダー)を使用して行っているのですが、
			    //   │GetMovieBaseImageToGraphを実行すると強制的にその変換処理をCPUで行ってしまい無駄に処理負荷が高くなってしまいます
			    //   │なので、それを回避するために『戻り値のBASEIMAGE*は使用せず、ImageUpdateFlagの取得だけを行うかどうかを指定する第３の引数 int ImageUpdateFlagSetOnly』を追加しましたので、
			    //   │もし動画画像の更新のみのチェックを行う場合は第三の引数をTRUEにして呼ぶようにしてください
			    // - ImageUpdateFlagSetOnly=TRUEにしてGetMovieBaseImageToGraph()を呼び出した場合、正しくImageUpdateFlagが格納されても、戻り値はNULLになる事も有るようです。
			    //   動画形式によって、戻り値がNULLになるかどうかが異なるようです。(DxLib Ver 3.16c(2016/05/05版)のDxMovie.cppのGetMovieBaseImage()を参照)
			    //   従って、GetMovieBaseImageToGraph()の戻り値でのエラーチェックを行わないようにしました。
			    //   元々、GetMovieBaseImageToGraph()の戻り値は使っておらず、エラーチェックだけにしか使用していなかったので、このように変更しても動作上の問題は有りません。
			    //   唯一危険が有るとすると、上でPlayMovieToGraph(),又は,PlayMovieToGraph()が成功した後、このfor(;;)ループが始まる前,又は,このfor(;;)ループ中に(バグによるメモリ破壊などで)グラフィックハンドルが壊れた場合に、
			    //   このfor(;;)ループ内で異常を検出する事が出来なくなり、無限ループに陥る可能性が有る事です。
			    //   もしプログラムが停止した場合は、このfor(;;)ループ内で無限ループしていないかを疑い、そうだった場合は、バグによるメモリ破壊などでグラフィックハンドルが壊れていないかを調べて下さい。
				/*if(!*/GetMovieBaseImageToGraph(gr, &ImageUpdateFlag, TRUE)/*) { DIE(); }*/;	//戻り値を検査してはいけない。理由は上記のコメントを参照して下さい。
			    //}}2016/05/08変更:DxLib Ver 3.16c(2016/05/05版)で、GetMovieBaseImageToGraph()にImageUpdateFlagSetOnly引数が追加された事に伴い、ImageUpdateFlagSetOnly=TRUEにして呼び出すように変更しました。
				if(ImageUpdateFlag) { break; }
				Sleep(0);//タイムスライスを放棄する。これは必須ではない。
			}
		    //}}2016/01/26追加:play_grp()で再生開始直後、少なくとも最初のムービーフレームがデコードされるまで待つ事にしました。
			return;	//ここまで
		}
	}
	DIE();	//同時再生数が多過ぎる。実際には、先にDxLibでエラーになるはずなので、ここまで到達しないはずです。
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//ムービーグラフィックを明示的に再生停止する。
// - 普通の使い方では、この関数を呼ぶ必要は有りません。
//   今回のフレームで再生しなかったムービーグラフィックは、auto_stop_grp()で自動的に停止するからです。
//   当関数は、再生中に先頭へ巻き戻して再生する場合などの、特殊な場合だけに使用して下さい。
void stop_grp(int iGrp) {
	int i;
	for(i = 0; i < ARRAY_SIZE(TBL_PlayingGrp); i++) {
		if(TBL_PlayingGrp[i].iGrp == iGrp) {			//再生中のグラフィックとして登録したエントリを見つけたら…
			TBL_PlayingGrp[i].iGrp = 0;			//登録解除する。
			TBL_PlayingGrp[i].bPlay = 0;			//今回のフレームで再生したフラグをクリアする。この処理は必須ではないが空きエントリが判り易いようにクリアする事にした。
			free_grp(iGrp);					//ムービーグラフィックのハンドルを削除する。DxLibの仕様上はMAX_MOVIE_NUM(=100)個のムービーを同時に扱えるはずであるが、実際にはムービーサイズに関係無く21個ぐらいでテクスチャが作成出来ないエラーになるようだ。停止したムービーグラフィックはすぐに削除する方が安全だと思う。	※TODO:普通のテクスチャには左記の制限は無いようだが、メモリ使用量が極端に増える事を防ぐためには、今後、通常のテクスチャもしばらく使われなかったら開放するようにしたいと思う。	⇒通常のテクスチャに対してもplay_grp()を呼び出せば、自動破棄の仕組みを利用する事が出来る事に気付いた。play_grp()のコメントを参照せよ。
			return;	//ここまで
		}
	}
	//再生中のグラフィックとして登録されていない。
	//アプリケーションのバグと見なしても良いし、無視しても構わない。
	//現在の所、無視する事にした。
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//今回のフレームでplay_grp()が呼ばれなかったムービーグラフィックを、自動的に停止します。
void auto_stop_grp() {
	int i;
	for(i = 0; i < ARRAY_SIZE(TBL_PlayingGrp); i++) {
		int iGrp = TBL_PlayingGrp[i].iGrp;
		if(iGrp) {						//空きエントリでなければ…
			if(TBL_PlayingGrp[i].bPlay) {			//今回のフレームで再生したフラグがセットされていたら…
				TBL_PlayingGrp[i].bPlay = 0;		//次のフレームのために再生したフラグをクリアしておく。
			} else {					//今回のフレームで再生したフラグがセットされていなければ…
				TBL_PlayingGrp[i].iGrp = 0;		//登録解除する。
				free_grp(iGrp);				//ムービーグラフィックのハンドルを削除する。DxLibの仕様上はMAX_MOVIE_NUM(=100)個のムービーを同時に扱えるはずであるが、実際にはムービーサイズに関係無く21個ぐらいでテクスチャが作成出来ないエラーになるようだ。停止したムービーグラフィックはすぐに削除する方が安全だと思う。	※TODO:普通のテクスチャには左記の制限は無いようだが、メモリ使用量が極端に増える事を防ぐためには、今後、通常のテクスチャもしばらく使われなかったら開放するようにしたいと思う。	⇒通常のテクスチャに対してもplay_grp()を呼び出せば、自動破棄の仕組みを利用する事が出来る事に気付いた。play_grp()のコメントを参照せよ。
			}
		}
	}
}
/*--------------------------------------------------------------------------*/
//ムービーの総時間をミリ秒単位で取得する。
//□参照資料
//│「C言語交流フォーラム ～ mixC++ ～ - C言語何でも質問掲示板 - 動画再生時の総再生時間取得について」
//│(http://dixq.net/forum/viewtopic.php?f=3&t=6938)
int get_grp_total_time(int iGrp) {
	int gr = load_grp(iGrp);
	//↑↓この順番を逆にしないように注意しろ。Sofdecムービーかどうかを判定する前にまずロードする必要が有る。
#ifdef  USE_SOFDEC
	//ハッシュテーブルが作成されていたら、Sofdecムービーである可能性が有る。
	if(ht_usm) {
		//Sofdecムービーならば…
		ST_PlayingUsm* pUsm = (ST_PlayingUsm*)ght_get(ht_usm, sizeof iGrp, &iGrp);
		if(pUsm) {
			double FramerateN  = SofdecHelper_GetFramerateN(pUsm->iManaPlayer);	//有理数形式フレームレート(分子) framerate [x1000] = framerate_n / framerate_d
			double FramerateD  = SofdecHelper_GetFramerateD(pUsm->iManaPlayer);	//有理数形式フレームレート(分母) framerate [x1000] = framerate_n / framerate_d
			double TotalFrames = SofdecHelper_GetTotalFrames(pUsm->iManaPlayer);	//総フレーム数
			return ((FramerateD / FramerateN) * TotalFrames) * 1000.0;	//ここまで
			//      ~~~~~~~~~~~~~~~~~~~~~~~~~[秒/フレーム]
			//     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~[秒]
			//     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~[ミリ秒]
		}
	}
#endif//USE_SOFDEC
	int nTotalFrame = GetMovieTotalFrameToGraph(gr);		//ムービーの総フレーム数を得る(Ogg Theoraでのみ有効)
	//GetMovieTotalFrameToGraph()が成功したら…
	// - このグラフィックがogvから読み込まれたムービーグラフィックならば、GetMovieTotalFrameToGraph()が成功するはず。
	if(nTotalFrame != -1) {
		int nOneFrameTime = GetOneFrameTimeMovieToGraph(gr);	//ムービーの1フレームあたりの時間を得る		//DxLib.hのコメントに説明が無いが、どうやらマイクロ秒単位のようだ。
		if(nOneFrameTime == -1) { DIE(); }
		return (nTotalFrame * nOneFrameTime) / 1000;		//ムービーの総時間をミリ秒単位で取得する。	//マイクロ秒単位⇒ミリ秒単位に変換する。
	//GetMovieTotalFrameToGraph()が失敗したら…
	// - このグラフィックがogv以外(avi等)から読み込まれたムービーグラフィックであるか,又は,ムービーグラフィックでなければ、GetMovieTotalFrameToGraph()が失敗するはず。
	} else {
		//aviファイルと仮定して、AVIメインヘッダーのdwMicroSecPerFrame,dwTotalFramesから時間を算出する。
		//※TODO:2016/03/23現在の所はogvしか使っておらず、まだ必要無いので未実装です。必要になったら上記の参照資料を参照して実装して下さい。
		DIE();
	}
}
/*--------------------------------------------------------------------------*/
//スプライト番号を指定して、stop_grp()を呼び出すユーティリティ関数です。
void stop_spr(int iSpr) {
	ST_SprDef stSprDef;
	get_sprDef(iSpr, &stSprDef);
	stop_grp(stSprDef.iTexNo);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//スプライト番号を指定して、get_grp_total_time()を呼び出すユーティリティ関数です。
int get_spr_total_time(int iSpr) {
	ST_SprDef stSprDef;
	get_sprDef(iSpr, &stSprDef);
	return get_grp_total_time(stSprDef.iTexNo);
}
/*--------------------------------------------------------------------------*/
//グラフィック番号を指定して、ムービーの再生準備を行います。
//Sofdecムービー以外ならば何もしない(エラーにもしない)ので、任意のグラフィック番号に対して呼び出して安全です。
//{{2016/09/19変更:SofdecHelper_Prepare()が戻り値を返すようになった事に伴い、prep_grp(),prep_spr()も戻り値を返すように変更しました。
//void prep_grp(int iGrp) {
//	char ext[MAX_EXT];
//	//レジストリから、グラフィックデータのファイル名を取得する。
//	const char* path = REG_get_string_l(stTexInf.pTexDef, iGrp, stTexInf.name_Path, -1);
//	if(!path) { DIE(); }
//	//ファイル名の拡張子を取得する。
//	splitpath(path, NULL, NULL, NULL, ext);
//#ifdef  USE_SOFDEC
//	//ファイル名の拡張子が".usm"ならば…
//	if(!strcasecmp(ext, ".usm")) {
//		//Sofdecムービーを準備する。
//		SofdecHelper_Prepare(path);
//		return;	//ここまで
//	}
//#endif//USE_SOFDEC
//	//ファイル名の拡張子が".usm"以外ならば…
//	{
//		//今後、DxLibのムービーや静止画の準備にも対応する場合は、ここに処理を追加して下さい。
//		//2016/09/17現在は、何もしません。(エラーにもしません)
//		/** no job **/
//	}
//}
//↓2016/09/19変更:SofdecHelper_Prepare()が戻り値を返すようになった事に伴い、prep_grp(),prep_spr()も戻り値を返すように変更しました。
int prep_grp(int iGrp) {
	char ext[MAX_EXT];
	//レジストリから、グラフィックデータのファイル名を取得する。
	const char* path = REG_get_string_l(stTexInf.pTexDef, iGrp, stTexInf.name_Path, -1);
	if(!path) { DIE(); }
	//ファイル名の拡張子を取得する。
	splitpath(path, NULL, NULL, NULL, ext);
#ifdef  USE_SOFDEC
	//ファイル名の拡張子が".usm"ならば…
	if(!strcasecmp(ext, ".usm")) {
	    //{{特例処理
		//当モジュールの描画処理は、一種類のムービーを複数チャネルで再生する事は無い。
		//既に描画中のムービーが登録されているならば、準備完了しているのと同じである。
		if(ht_usm) {
			ST_PlayingUsm* pUsm = (ST_PlayingUsm*)ght_get(ht_usm, sizeof iGrp, &iGrp);
			if(pUsm) { return 0; }
		}
	    //}}特例処理
		//Sofdecムービーを準備する。
		// - SofdecHelper_Prepare()の戻り値をそのまま返します。
		//   戻り値の仕様は、SofdecHelper_Prepare()のコメントを参照して下さい。
		return SofdecHelper_Prepare(path);
	}
#endif//USE_SOFDEC
	//ファイル名の拡張子が".usm"以外ならば…
	{
		//DxLibの再生準備にはまだ対応していないので、常に再生準備完了として0を返す事にしました。
		//再生遅延が発生する事以外は、アプリケーションの観点からは不可視であるはずです。
		//今後、DxLibの再生準備に対応したら、正確な値を返すように変更して下さい。
		return 0;
	}
}
//}}2016/09/19変更:SofdecHelper_Prepare()が戻り値を返すようになった事に伴い、prep_grp(),prep_spr()も戻り値を返すように変更しました。
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//スプライト番号を指定して、ムービーの再生準備を行います。
//Sofdecムービー以外ならば何もしない(エラーにもしない)ので、任意のスプライト番号に対して呼び出して安全です。
//{{2016/09/19変更:SofdecHelper_Prepare()が戻り値を返すようになった事に伴い、prep_grp(),prep_spr()も戻り値を返すように変更しました。
//void prep_spr(int iSpr) {
//	ST_SprDef stSprDef;
//	get_sprDef(iSpr, &stSprDef);
//	prep_grp(stSprDef.iTexNo);
//}
//↓2016/09/19変更:SofdecHelper_Prepare()が戻り値を返すようになった事に伴い、prep_grp(),prep_spr()も戻り値を返すように変更しました。
int prep_spr(int iSpr) {
	ST_SprDef stSprDef;
	get_sprDef(iSpr, &stSprDef);
	return prep_grp(stSprDef.iTexNo);
}
//}}2016/09/19変更:SofdecHelper_Prepare()が戻り値を返すようになった事に伴い、prep_grp(),prep_spr()も戻り値を返すように変更しました。
/****************************************************************************
 *	DxLib用のサウンド関数ラッパー
 ****************************************************************************/
ST_TapSeq*		pTapSeq;		//TAPシーケンサ
ST_TapMxr*		pTapMxr;		//TAPミキサー
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#ifdef  USE_ADX
static int		use_adx;		//0:DxLibを使う,1:ADXを使う
#endif//USE_ADX
static union {
	//DxLibを使う場合
	struct {
		ght_hash_table_t*	ht_pr;				//フレーズ番号⇒(サウンドハンドル+1)
		uint16_t*		TBL_DevPhr/*[nDevCh]*/;		//各デバイスチャネルで再生中のフレーズ番号	UINT16_MAX=無し(停止中を意味する)	DxLibにはデバイスチャネルの概念が無いので擬似的に管理する必要が有る。
	} dxl;
#ifdef  USE_ADX
	//ADXを使う場合
	struct {
		ght_hash_table_t*	ht_time;			//フレーズ番号⇒総時間(ミリ秒)			2016/05/04時点のSofdecHelper.dllのAdxHelper_GetTotalTime()の実装は効率が悪いのでアプリケーション側で結果をキャッシュする事にした。もし今後AdxHelper_GetTotalTime()の効率を改善したらこのキャッシュは不要になる。
	} adx;
#endif//USE_ADX
} snd;
/*--------------------------------------------------------------------------*/
//レジストリ名⇒サウンドデータを読み込む。
static int load_snd(int iPhr) {
#ifdef  USE_ADX
	//DxLibを使う場合
	if(!use_adx) {
#endif//USE_ADX
		//ハッシュテーブルが未作成ならば、作成する。
		if(!snd.dxl.ht_pr) { ght_set_rehash((snd.dxl.ht_pr = ght_create(0)), 1); }
		//既に読み込まれているか?
//{{2016/09/07変更:64ビット対応のための変更を行いました。
//		int pr = (int)ght_get(snd.dxl.ht_pr, sizeof iPhr, &iPhr);	//フレーズ番号⇒(サウンドハンドル+1)を取得する。
//↓2016/09/07変更:64ビット対応のための変更を行いました。
#ifndef _WIN64
		int pr = (int)ght_get(snd.dxl.ht_pr, sizeof iPhr, &iPhr);	//フレーズ番号⇒(サウンドハンドル+1)を取得する。	//32ビット版
#else //_WIN64
		int pr = (intptr_t)ght_get(snd.dxl.ht_pr, sizeof iPhr, &iPhr);	//フレーズ番号⇒(サウンドハンドル+1)を取得する。	//64ビット版
#endif//_WIN64
//}}2016/09/07変更:64ビット対応のための変更を行いました。
		//まだ読み込まれていなければ…
		if(!pr) {
			//レジストリから、サウンドデータのファイル名を取得する。
			const char* fname = REG_get_string_l(stPhrInf.pPhrDef, iPhr, stPhrInf.name_Path, -1);
			if(!fname) { DIE(); }
		    #ifdef  DEBUG
			syslog(LOG_DEBUG, "LoadSoundMem(\"%s\")\n", fname);
		    #endif//DEBUG
			//アプリケーション定義の関数が指定されていたら、呼び出す。
			// - アプリケーションはiPhrを見て、必要に応じてSetCreateSoundDataType()等を呼び出して下さい。
			//   この下の処理で、LoadSoundMem()によって読み込まれる画像や動画の、読み込み設定を行うためです。
			//   全ての音声に対してそのような設定を行う必要が無ければ、(stPhrInf.fnPreLoadSoundMem=NULL)にしても構いません。
			if(stPhrInf.fnPreLoadSoundMem) { (*stPhrInf.fnPreLoadSoundMem)(iPhr); }
			//サウンドデータを読み込む。
			pr = LoadSoundMem(fname);
			if(pr == -1) { DIE(); }
			pr++;	//ハッシュテーブルに0は登録出来ないので+1して0を避ける。(pr!=-1)だから(pr+1)は必ず0以外になる。
			//フレーズ番号⇒(サウンドハンドル+1)を登録する。
//{{2016/09/07変更:64ビット対応のための変更を行いました。
//			if(ght_insert(snd.dxl.ht_pr, (void*)pr, sizeof iPhr, &iPhr)) { DIE(); }	//バグ
//↓2016/09/07変更:64ビット対応のための変更を行いました。
#ifndef _WIN64
			if(ght_insert(snd.dxl.ht_pr, (void*)pr, sizeof iPhr, &iPhr)) { DIE(); }	//バグ			//32ビット版
#else //_WIN64
			if(ght_insert(snd.dxl.ht_pr, (void*)(intptr_t)pr, sizeof iPhr, &iPhr)) { DIE(); }	//バグ	//64ビット版
#endif//_WIN64
//}}2016/09/07変更:64ビット対応のための変更を行いました。
		}
		pr--;	//上記の+1と相殺する。
		//サウンドデータのハンドルを返す。
		return pr;
#ifdef  USE_ADX
	//ADXを使う場合
	} else {
		DIE();	//ADXを使う場合は当関数が呼び出される事は無いはず。もしここで停止したら当モジュールのバグです。
	}
#endif//USE_ADX
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//既に読み込んだサウンドデータを削除する。
// - 2016/05/16現在の所、free_all_snd()からのみ呼び出されます。
static void free_snd(int iPhr) {
#ifdef  USE_ADX
	//DxLibを使う場合
	if(!use_adx) {
#endif//USE_ADX
		//ハッシュテーブルが未作成ならば、明らかにまだ読み込まれていない。
		if(!snd.dxl.ht_pr) { return; }	//ここまで
		//既に読み込まれているか?
//{{2016/09/07変更:64ビット対応のための変更を行いました。
//		int pr = (int)ght_remove(snd.dxl.ht_pr, sizeof iPhr, &iPhr);	//フレーズ番号⇒(サウンドハンドル+1)を取得する。
//↓2016/09/07変更:64ビット対応のための変更を行いました。
#ifndef _WIN64
		int pr = (int)ght_remove(snd.dxl.ht_pr, sizeof iPhr, &iPhr);	//フレーズ番号⇒(サウンドハンドル+1)を取得する。		//32ビット版
#else //_WIN64
		int pr = (intptr_t)ght_remove(snd.dxl.ht_pr, sizeof iPhr, &iPhr);	//フレーズ番号⇒(サウンドハンドル+1)を取得する。	//64ビット版
#endif//_WIN64
//}}2016/09/07変更:64ビット対応のための変更を行いました。
		//まだ読み込まれていなければ、何もしない。
		if(!pr) { return; }		//ここまで
		pr--;	//上記の+1と相殺する。
	    #ifdef  DEBUG
		const char* fname = REG_get_string_l(stPhrInf.pPhrDef, iPhr, stPhrInf.name_Path, -1);
		if(!fname) { DIE(); }
		syslog(LOG_DEBUG, "DeleteSoundMem(\"%s\")\n", fname);
	    #endif//DEBUG
		//サウンドデータのハンドルを削除する。
		if(DeleteSoundMem(pr)) { DIE(); }
#ifdef  USE_ADX
	//ADXを使う場合
	} else {
		DIE();	//ADXを使う場合は当関数が呼び出される事は無いはず。もしここで停止したら当モジュールのバグです。
	}
#endif//USE_ADX
}
/*--------------------------------------------------------------------------*/
//指定された論理チャネルがデバイスチャネルを獲得していたら、この論理チャネルに適用するミキサーチャネルの合計減衰量を、デバイスチャネルに設定する。
static void set_att(int iLogCh, ST_TapLogCh* pLogCh) {
	//指定された論理チャネルがデバイスチャネルを獲得していたら…
	if(pLogCh->iDevCh != UINT8_MAX) {
		//指定された論理チャネルに適用する合計減衰量を取得し、減衰量⇒音量に変換する。
		int iVol = 255;							//音量の最大値
		iVol -= TapMxr_GetAtt(pTapMxr, stSndInf.TBL_MxrChMask[iLogCh]);	//TAPミキサーの合計減衰量…1次ボリューム相当
		iVol -= pLogCh->iAtt;						//論理チャネルの減衰量   …2次ボリューム相当
		if(iVol < 0) { iVol = 0; }
#ifdef  USE_ADX
		//DxLibを使う場合
		if(!use_adx) {
#endif//USE_ADX
			//変換した音量を、デバイスチャネルに設定する。
			// - DxLibの場合、デバイスチャネルの概念は無く、フレーズに対しての設定となる。
			if(ChangeVolumeSoundMem(iVol, load_snd(snd.dxl.TBL_DevPhr[pLogCh->iDevCh]))) { DIE(); }	//┬どちらでも同じ結果になります。
		//	if(ChangeVolumeSoundMem(iVol, load_snd(pLogCh->iPhr))) { DIE(); }			//┘
#ifdef  USE_ADX
		//ADXを使う場合
		} else {
//{{2016/10/12修正:AdxHelperのプレイヤーidを、正しくは物理チャネル番号に対応付けて決定すべき所、間違って論理チャネル番号に対応付けて決定していたバグを修正しました。
//			int iAtomExPlayer = iLogCh + 1;	//論理チャネル番号(0ベース)⇒プレイヤーid(1ベース)変換
//↓2016/10/12修正:AdxHelperのプレイヤーidを、正しくは物理チャネル番号に対応付けて決定すべき所、間違って論理チャネル番号に対応付けて決定していたバグを修正しました。
			int iAtomExPlayer = pLogCh->iDevCh + 1;	//物理チャネル番号(0ベース)⇒プレイヤーid(1ベース)変換
//}}2016/10/12修正:AdxHelperのプレイヤーidを、正しくは物理チャネル番号に対応付けて決定すべき所、間違って論理チャネル番号に対応付けて決定していたバグを修正しました。
			//変換した音量を、デバイスチャネルに設定する。
			AdxHelper_SetVolume(iAtomExPlayer, iVol / 255.0);
		}
#endif//USE_ADX
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//指定された論理チャネルがデバイスチャネルを獲得しており,かつ,そのデバイスチャネルで再生中のフレーズ番号が有れば、そのフレーズを停止する。
static void stop_phr(int iLogCh, ST_TapLogCh* pLogCh) {
	//指定された論理チャネルがデバイスチャネルを獲得していたら…
	if(pLogCh->iDevCh != UINT8_MAX) {
#ifdef  USE_ADX
		//DxLibを使う場合
		if(!use_adx) {
#endif//USE_ADX
			//このデバイスチャネルで再生中のフレーズ番号が有れば…
			if(snd.dxl.TBL_DevPhr[pLogCh->iDevCh] != UINT16_MAX) {
				//このデバイスチャネルで再生中のフレーズを停止する。
				// - DxLibの場合、デバイスチャネルの概念は無く、フレーズに対しての停止となる。
				if(StopSoundMem(load_snd(snd.dxl.TBL_DevPhr[pLogCh->iDevCh]))) { DIE(); }
				//このデバイスチャネルで再生中のフレーズ番号をクリアする。
				snd.dxl.TBL_DevPhr[pLogCh->iDevCh] = -1/*=UINT16_MAX=無し*/;
			}
#ifdef  USE_ADX
		//ADXを使う場合
		} else {
//{{2016/10/12修正:AdxHelperのプレイヤーidを、正しくは物理チャネル番号に対応付けて決定すべき所、間違って論理チャネル番号に対応付けて決定していたバグを修正しました。
//			int iAtomExPlayer = iLogCh + 1;	//論理チャネル番号(0ベース)⇒プレイヤーid(1ベース)変換
//↓2016/10/12修正:AdxHelperのプレイヤーidを、正しくは物理チャネル番号に対応付けて決定すべき所、間違って論理チャネル番号に対応付けて決定していたバグを修正しました。
			int iAtomExPlayer = pLogCh->iDevCh + 1;	//物理チャネル番号(0ベース)⇒プレイヤーid(1ベース)変換
//}}2016/10/12修正:AdxHelperのプレイヤーidを、正しくは物理チャネル番号に対応付けて決定すべき所、間違って論理チャネル番号に対応付けて決定していたバグを修正しました。
			//デバイスチャネルを停止する。
			// - 既に停止している場合はダミー処理となり安全です。
			AdxHelper_Stop(iAtomExPlayer);
		}
#endif//USE_ADX
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//指定された論理チャネルがデバイスチャネルを獲得していたら、そのデバイスチャネルでフレーズを'非ループ'再生する。
static void play_phr(int iLogCh, ST_TapLogCh* pLogCh) {
	//指定された論理チャネルがデバイスチャネルを獲得していなければ、何もしない。
	if(pLogCh->iDevCh != UINT8_MAX) {
#ifdef  USE_ADX
		//DxLibを使う場合
		if(!use_adx) {
#endif//USE_ADX
			//このデバイスチャネルで再生中のフレーズ番号が有れば、そのフレーズを停止する。
			// - あるデバイスチャネルが、論理チャネルXによってフレーズx再生中の状態から、論理チャネルYの再生開始によって横取りされ、フレーズyのfnPlay()が通知される場合、フレーズxのfnStop()は呼び出されない。
			//   P/ECEならば同一デバイスチャネルに対するフレーズyの再生開始によってフレーズxは自動停止するので問題無いのだが、DxLibにはデバイスチャネルの概念が無いのでフレーズxが自動停止しない問題が生じる。
			//   そこで、fnPlay()によるデバイスチャネルの再生開始を行う前に、そのデバイスチャネルが既に何らかのフレーズを再生中だったかを判断して、再生だったならば古いフレーズを明示的に停止する必要が有る。
			stop_phr(iLogCh, pLogCh);
			//デバイスチャネルを再生する。
			// - DxLibの場合、デバイスチャネルの概念は無く、フレーズに対しての再生となる。
			if(PlaySoundMem(load_snd(pLogCh->iPhr), DX_PLAYTYPE_BACK)) { DIE(); }	//ループ処理はデバイスでなくシーケンサで行う事にした。従ってTapSeq_fnPlay()とTapSeq_fnLoop()で同じ処理をおこなっています。
			//このデバイスチャネルで再生中のフレーズ番号を格納する。			//┐
			snd.dxl.TBL_DevPhr[pLogCh->iDevCh] = pLogCh->iPhr;				//├この順序は必須です。set_att()がsnd.dxl.TBL_DevPhr[pLogCh->iDevCh]を参照するからです。
			//指定された論理チャネルに適用する合計減衰量を、デバイスチャネルに設定する。	//│
			set_att(iLogCh, pLogCh);							//┘
#ifdef  USE_ADX
		//ADXを使う場合
		} else {
//{{2016/10/12修正:AdxHelperのプレイヤーidを、正しくは物理チャネル番号に対応付けて決定すべき所、間違って論理チャネル番号に対応付けて決定していたバグを修正しました。
//			int iAtomExPlayer = iLogCh + 1;	//論理チャネル番号(0ベース)⇒プレイヤーid(1ベース)変換
//↓2016/10/12修正:AdxHelperのプレイヤーidを、正しくは物理チャネル番号に対応付けて決定すべき所、間違って論理チャネル番号に対応付けて決定していたバグを修正しました。
			int iAtomExPlayer = pLogCh->iDevCh + 1;	//物理チャネル番号(0ベース)⇒プレイヤーid(1ベース)変換
//}}2016/10/12修正:AdxHelperのプレイヤーidを、正しくは物理チャネル番号に対応付けて決定すべき所、間違って論理チャネル番号に対応付けて決定していたバグを修正しました。
			//レジストリから、サウンドデータのファイル名を取得する。
			const char* fname = REG_get_string_l(stPhrInf.pPhrDef, pLogCh->iPhr, stPhrInf.name_Path, -1);
			if(!fname) { DIE(); }
			//デバイスチャネルを再生する。
			AdxHelper_Play(iAtomExPlayer, fname);
//{{2016/11/02追加:play_phr()にて、AdxHelper_Play()で再生開始した直後に、set_att()を呼ぶのを忘れていたのを修正しました。
			//指定された論理チャネルに適用する合計減衰量を、デバイスチャネルに設定する。
			set_att(iLogCh, pLogCh);
//}}2016/11/02追加:play_phr()にて、AdxHelper_Play()で再生開始した直後に、set_att()を呼ぶのを忘れていたのを修正しました。
		}
#endif//USE_ADX
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//{{2016/10/13変更:TapSeq_fnPlay,TapSeq_fnStop,TapSeq_fnLoop,TapSeq_fnEnd,TapSeq_fnNext,TapSeq_fnCtrl,TapSeq_fnGetPhrTime,TapSeq_fnGetPhrNextの引数に、pTapSeqを追加しました。
//static void TapSeq_fnPlay(int iLogCh, ST_TapLogCh* pLogCh) {
//↓2016/10/13変更:TapSeq_fnPlay,TapSeq_fnStop,TapSeq_fnLoop,TapSeq_fnEnd,TapSeq_fnNext,TapSeq_fnCtrl,TapSeq_fnGetPhrTime,TapSeq_fnGetPhrNextの引数に、pTapSeqを追加しました。
static void TapSeq_fnPlay(ST_TapSeq* pTapSeq, int iLogCh, ST_TapLogCh* pLogCh) {
//}}2016/10/13変更:TapSeq_fnPlay,TapSeq_fnStop,TapSeq_fnLoop,TapSeq_fnEnd,TapSeq_fnNext,TapSeq_fnCtrl,TapSeq_fnGetPhrTime,TapSeq_fnGetPhrNextの引数に、pTapSeqを追加しました。
    #ifdef  DEBUG
	syslog(LOG_DEBUG, "fnPlay %d %d\n", iLogCh, pLogCh->iPhr);
    #endif//DEBUG
	play_phr(iLogCh, pLogCh);	//ループ処理はデバイスでなくシーケンサで行う事にした。従ってTapSeq_fnPlay()とTapSeq_fnLoop()で同じ処理をおこなっています。
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//{{2016/10/13変更:TapSeq_fnPlay,TapSeq_fnStop,TapSeq_fnLoop,TapSeq_fnEnd,TapSeq_fnNext,TapSeq_fnCtrl,TapSeq_fnGetPhrTime,TapSeq_fnGetPhrNextの引数に、pTapSeqを追加しました。
//static void TapSeq_fnLoop(int iLogCh, ST_TapLogCh* pLogCh) {
//↓2016/10/13変更:TapSeq_fnPlay,TapSeq_fnStop,TapSeq_fnLoop,TapSeq_fnEnd,TapSeq_fnNext,TapSeq_fnCtrl,TapSeq_fnGetPhrTime,TapSeq_fnGetPhrNextの引数に、pTapSeqを追加しました。
static void TapSeq_fnLoop(ST_TapSeq* pTapSeq, int iLogCh, ST_TapLogCh* pLogCh) {
//}}2016/10/13変更:TapSeq_fnPlay,TapSeq_fnStop,TapSeq_fnLoop,TapSeq_fnEnd,TapSeq_fnNext,TapSeq_fnCtrl,TapSeq_fnGetPhrTime,TapSeq_fnGetPhrNextの引数に、pTapSeqを追加しました。
    #ifdef  DEBUG
	syslog(LOG_DEBUG, "fnLoop %d\n", iLogCh);
    #endif//DEBUG
	play_phr(iLogCh, pLogCh);	//ループ処理はデバイスでなくシーケンサで行う事にした。従ってTapSeq_fnPlay()とTapSeq_fnLoop()で同じ処理をおこなっています。
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//{{2016/08/02変更:自然遷移通知を追加しました。
//{{2016/10/13変更:TapSeq_fnPlay,TapSeq_fnStop,TapSeq_fnLoop,TapSeq_fnEnd,TapSeq_fnNext,TapSeq_fnCtrl,TapSeq_fnGetPhrTime,TapSeq_fnGetPhrNextの引数に、pTapSeqを追加しました。
//static void TapSeq_fnNext(int iLogCh, ST_TapLogCh* pLogCh) {
//↓2016/10/13変更:TapSeq_fnPlay,TapSeq_fnStop,TapSeq_fnLoop,TapSeq_fnEnd,TapSeq_fnNext,TapSeq_fnCtrl,TapSeq_fnGetPhrTime,TapSeq_fnGetPhrNextの引数に、pTapSeqを追加しました。
static void TapSeq_fnNext(ST_TapSeq* pTapSeq, int iLogCh, ST_TapLogCh* pLogCh) {
//}}2016/10/13変更:TapSeq_fnPlay,TapSeq_fnStop,TapSeq_fnLoop,TapSeq_fnEnd,TapSeq_fnNext,TapSeq_fnCtrl,TapSeq_fnGetPhrTime,TapSeq_fnGetPhrNextの引数に、pTapSeqを追加しました。
    #ifdef  DEBUG
	syslog(LOG_DEBUG, "fnNext %d\n", iLogCh);
    #endif//DEBUG
	play_phr(iLogCh, pLogCh);
}
//}}2016/08/02変更:自然遷移通知を追加しました。
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//{{2016/10/13変更:TapSeq_fnPlay,TapSeq_fnStop,TapSeq_fnLoop,TapSeq_fnEnd,TapSeq_fnNext,TapSeq_fnCtrl,TapSeq_fnGetPhrTime,TapSeq_fnGetPhrNextの引数に、pTapSeqを追加しました。
//static void TapSeq_fnStop(int iLogCh, ST_TapLogCh* pLogCh) {
//↓2016/10/13変更:TapSeq_fnPlay,TapSeq_fnStop,TapSeq_fnLoop,TapSeq_fnEnd,TapSeq_fnNext,TapSeq_fnCtrl,TapSeq_fnGetPhrTime,TapSeq_fnGetPhrNextの引数に、pTapSeqを追加しました。
static void TapSeq_fnStop(ST_TapSeq* pTapSeq, int iLogCh, ST_TapLogCh* pLogCh) {
//}}2016/10/13変更:TapSeq_fnPlay,TapSeq_fnStop,TapSeq_fnLoop,TapSeq_fnEnd,TapSeq_fnNext,TapSeq_fnCtrl,TapSeq_fnGetPhrTime,TapSeq_fnGetPhrNextの引数に、pTapSeqを追加しました。
    #ifdef  DEBUG
	syslog(LOG_DEBUG, "fnStop %d\n", iLogCh);
    #endif//DEBUG
	stop_phr(iLogCh, pLogCh);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//{{2016/10/13変更:TapSeq_fnPlay,TapSeq_fnStop,TapSeq_fnLoop,TapSeq_fnEnd,TapSeq_fnNext,TapSeq_fnCtrl,TapSeq_fnGetPhrTime,TapSeq_fnGetPhrNextの引数に、pTapSeqを追加しました。
//static void TapSeq_fnEnd(int iLogCh, ST_TapLogCh* pLogCh) {
//↓2016/10/13変更:TapSeq_fnPlay,TapSeq_fnStop,TapSeq_fnLoop,TapSeq_fnEnd,TapSeq_fnNext,TapSeq_fnCtrl,TapSeq_fnGetPhrTime,TapSeq_fnGetPhrNextの引数に、pTapSeqを追加しました。
static void TapSeq_fnEnd(ST_TapSeq* pTapSeq, int iLogCh, ST_TapLogCh* pLogCh) {
//}}2016/10/13変更:TapSeq_fnPlay,TapSeq_fnStop,TapSeq_fnLoop,TapSeq_fnEnd,TapSeq_fnNext,TapSeq_fnCtrl,TapSeq_fnGetPhrTime,TapSeq_fnGetPhrNextの引数に、pTapSeqを追加しました。
    #ifdef  DEBUG
	syslog(LOG_DEBUG, "fnEnd %d\n", iLogCh);
    #endif//DEBUG
	stop_phr(iLogCh, pLogCh);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//{{2016/10/13変更:TapSeq_fnPlay,TapSeq_fnStop,TapSeq_fnLoop,TapSeq_fnEnd,TapSeq_fnNext,TapSeq_fnCtrl,TapSeq_fnGetPhrTime,TapSeq_fnGetPhrNextの引数に、pTapSeqを追加しました。
//static void TapSeq_fnCtrl(int iLogCh, ST_TapLogCh* pLogCh) {
//↓2016/10/13変更:TapSeq_fnPlay,TapSeq_fnStop,TapSeq_fnLoop,TapSeq_fnEnd,TapSeq_fnNext,TapSeq_fnCtrl,TapSeq_fnGetPhrTime,TapSeq_fnGetPhrNextの引数に、pTapSeqを追加しました。
static void TapSeq_fnCtrl(ST_TapSeq* pTapSeq, int iLogCh, ST_TapLogCh* pLogCh) {
//}}2016/10/13変更:TapSeq_fnPlay,TapSeq_fnStop,TapSeq_fnLoop,TapSeq_fnEnd,TapSeq_fnNext,TapSeq_fnCtrl,TapSeq_fnGetPhrTime,TapSeq_fnGetPhrNextの引数に、pTapSeqを追加しました。
    #ifdef  DEBUG
	syslog(LOG_DEBUG, "fnCtrl %d\n", iLogCh);
    #endif//DEBUG
	//指定された論理チャネルに適用する合計減衰量を、デバイスチャネルに設定する。
	set_att(iLogCh, pLogCh);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//{{2016/10/13変更:TapSeq_fnPlay,TapSeq_fnStop,TapSeq_fnLoop,TapSeq_fnEnd,TapSeq_fnNext,TapSeq_fnCtrl,TapSeq_fnGetPhrTime,TapSeq_fnGetPhrNextの引数に、pTapSeqを追加しました。
//static int TapSeq_fnGetPhrTime(int iPhr) {
//↓2016/10/13変更:TapSeq_fnPlay,TapSeq_fnStop,TapSeq_fnLoop,TapSeq_fnEnd,TapSeq_fnNext,TapSeq_fnCtrl,TapSeq_fnGetPhrTime,TapSeq_fnGetPhrNextの引数に、pTapSeqを追加しました。
static int TapSeq_fnGetPhrTime(ST_TapSeq* pTapSeq, int iPhr) {
//}}2016/10/13変更:TapSeq_fnPlay,TapSeq_fnStop,TapSeq_fnLoop,TapSeq_fnEnd,TapSeq_fnNext,TapSeq_fnCtrl,TapSeq_fnGetPhrTime,TapSeq_fnGetPhrNextの引数に、pTapSeqを追加しました。
#ifdef  USE_ADX
	//DxLibを使う場合
	if(!use_adx) {
#endif//USE_ADX
		//サウンドハンドルの音の総時間をミリ秒単位で取得する。
		return GetSoundTotalTime(load_snd(iPhr));
#ifdef  USE_ADX
	//ADXを使う場合
	} else {
		//2016/05/04時点のSofdecHelper.dllのAdxHelper_GetTotalTime()の実装は効率が悪いのでアプリケーション側で結果をキャッシュする事にした。
		//もし今後AdxHelper_GetTotalTime()の効率を改善したらこのキャッシュは不要になる。
		if(!snd.adx.ht_time) {
			snd.adx.ht_time = ght_create(257/*調整可*/);
			ght_set_bounded_buckets(snd.adx.ht_time, 8/*調整可*/, NULL);
		}
		//既に結果がキャッシュされているか?
//{{2016/09/07変更:64ビット対応のための変更を行いました。
//		int t = (int)ght_get(snd.adx.ht_time, sizeof iPhr, &iPhr);	//フレーズ番号⇒総時間(ミリ秒)を取得する。
//↓2016/09/07変更:64ビット対応のための変更を行いました。
#ifndef _WIN64
		int t = (int)ght_get(snd.adx.ht_time, sizeof iPhr, &iPhr);	//フレーズ番号⇒総時間(ミリ秒)を取得する。	//32ビット版
#else //_WIN64
		int t = (intptr_t)ght_get(snd.adx.ht_time, sizeof iPhr, &iPhr);	//フレーズ番号⇒総時間(ミリ秒)を取得する。	//64ビット版
#endif//_WIN64
//}}2016/09/07変更:64ビット対応のための変更を行いました。
		//まだ結果がキャッシュされていなければ…
		if(!t) {
			//レジストリから、サウンドデータのファイル名を取得する。
			const char* fname = REG_get_string_l(stPhrInf.pPhrDef, iPhr, stPhrInf.name_Path, -1);
			if(!fname) { DIE(); }
			//サウンドファイルの音の総時間をミリ秒単位で取得する。
			t = AdxHelper_GetTotalTime(fname);
			if(!t) { t = 1; }	//0はハッシュテーブルに登録出来ないので、0ミリ秒(有り得ないはずだが)ならば1ミリ秒に補正する。
			//結果をキャッシュに登録する。
//{{2016/09/07変更:64ビット対応のための変更を行いました。
//			if(ght_insert(snd.adx.ht_time, (void*)t, sizeof iPhr, &iPhr)) { DIE(); }	//バグ
//↓2016/09/07変更:64ビット対応のための変更を行いました。
#ifndef _WIN64
			if(ght_insert(snd.adx.ht_time, (void*)t, sizeof iPhr, &iPhr)) { DIE(); }	//バグ		//32ビット版
#else //_WIN64
			if(ght_insert(snd.adx.ht_time, (void*)(intptr_t)t, sizeof iPhr, &iPhr)) { DIE(); }	//バグ	//64ビット版
#endif//_WIN64
//}}2016/09/07変更:64ビット対応のための変更を行いました。
		}
		return t;
	}
#endif//USE_ADX
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//{{2016/10/13変更:TapSeq_fnPlay,TapSeq_fnStop,TapSeq_fnLoop,TapSeq_fnEnd,TapSeq_fnNext,TapSeq_fnCtrl,TapSeq_fnGetPhrTime,TapSeq_fnGetPhrNextの引数に、pTapSeqを追加しました。
//static int TapSeq_fnGetPhrNext(int iPhr) {
//↓2016/10/13変更:TapSeq_fnPlay,TapSeq_fnStop,TapSeq_fnLoop,TapSeq_fnEnd,TapSeq_fnNext,TapSeq_fnCtrl,TapSeq_fnGetPhrTime,TapSeq_fnGetPhrNextの引数に、pTapSeqを追加しました。
static int TapSeq_fnGetPhrNext(ST_TapSeq* pTapSeq, int iPhr) {
//}}2016/10/13変更:TapSeq_fnPlay,TapSeq_fnStop,TapSeq_fnLoop,TapSeq_fnEnd,TapSeq_fnNext,TapSeq_fnCtrl,TapSeq_fnGetPhrTime,TapSeq_fnGetPhrNextの引数に、pTapSeqを追加しました。
	//通常フレーズ(-1),中間フレーズ(<>iPhr),ループフレーズ(=iPhr),完全フレーズ(-2)
	return REG_get_value_l(stPhrInf.pPhrDef, iPhr, stPhrInf.name_Next, -1) << 8 >> 8;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static void TapSeq_TimeProc() {
	//TAPシーケンサを実行する。
	TapSeq_Exec(pTapSeq, 10/*調整可*/);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static void TapMxr_TimeProc() {
	int iDevCh, iLogCh;
	//TAPミキサーを実行する。
	TapMxr_Exec(pTapMxr, 50/*調整可*/);
	//各デバイスチャネルについて…
	for(iDevCh = 0; iDevCh < pTapSeq->pInfo->nDevCh; iDevCh++) {
		//このデバイスチャネルを制御している論理チャネルが有れば…
		iLogCh = TapSeq_GetDevCh(pTapSeq, iDevCh)->iLogCh;
		if(iLogCh != UINT8_MAX) {
			//このデバイスチャネルを制御している論理チャネルを取得する。
			ST_TapLogCh* pLogCh = TapSeq_GetLogCh(pTapSeq, iLogCh);
			//この論理チャネルに適用するミキサーチャネルの合計減衰量を、デバイスチャネルに設定する。
			set_att(iLogCh, pLogCh);
		}
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void init_snd(ST_TimMgr* pTimMgr, int _use_adx) {
#ifdef  USE_ADX
	//0:DxLibを使う,又は,1:ADXを使うを格納する。
	use_adx = _use_adx;
#else //USE_ADX
	if(_use_adx) { DIE(); }
#endif//USE_ADX
	{
		//TAPシーケンサを初期化する。
		ST_TapSeqInfo* pInfo = (ST_TapSeqInfo*)calloc(sizeof(ST_TapSeqInfo) + (sizeof(uint8_t*) * stSndInf.nLogCh), 1);
		if(!pInfo) { DIE(); }
		pInfo->nPhr		= stSndInf.nPhr;
		pInfo->nDevCh		= stSndInf.nDevCh;
		pInfo->nLogCh		= stSndInf.nLogCh;
		pInfo->nSeqCh		= stSndInf.nSeqCh;
		pInfo->iCont		= stSndInf.iCont;
		pInfo->nEvt		= stSndInf.nEvt;
		pInfo->iEvtMsk		= stSndInf.iEvtMsk;
		pInfo->fnPlay		= TapSeq_fnPlay;
		pInfo->fnStop		= TapSeq_fnStop;
		pInfo->fnLoop		= TapSeq_fnLoop;
		pInfo->fnEnd		= TapSeq_fnEnd;
		pInfo->fnNext		= TapSeq_fnNext;		//{{2016/08/02変更:自然遷移通知を追加しました。}}
		pInfo->fnCtrl		= TapSeq_fnCtrl;
		pInfo->fnGetPhrTime	= TapSeq_fnGetPhrTime;
		pInfo->fnGetPhrNext	= TapSeq_fnGetPhrNext;
//{{2016/08/19変更:cliptaps.hのST_TapSeqInfo.TBL_TapSet[0]フィールドを廃止した事に伴い、DxlibHelper.cpp内のinit_snd()において、処理で明示的にアドレスを求めるようにしました。フィールドの宣言を廃止しただけで、メモリレイアウトは変わっていません。
//		memcpy(pInfo->TBL_TapSet, stSndInf.TBL_TapSet, (sizeof(uint8_t*) * stSndInf.nLogCh));
//↓2016/08/19変更:cliptaps.hのST_TapSeqInfo.TBL_TapSet[0]フィールドを廃止した事に伴い、DxlibHelper.cpp内のinit_snd()において、処理で明示的にアドレスを求めるようにしました。フィールドの宣言を廃止しただけで、メモリレイアウトは変わっていません。
		const uint8_t** TBL_TapSet = (const uint8_t**)(pInfo + 1);
		memcpy(TBL_TapSet, stSndInf.TBL_TapSet, (sizeof(uint8_t*) * stSndInf.nLogCh));
//}}2016/08/19変更:cliptaps.hのST_TapSeqInfo.TBL_TapSet[0]フィールドを廃止した事に伴い、DxlibHelper.cpp内のinit_snd()において、処理で明示的にアドレスを求めるようにしました。フィールドの宣言を廃止しただけで、メモリレイアウトは変わっていません。
		pTapSeq = TapSeq_New(pInfo);
		//TAPミキサーを初期化する。
		pTapMxr = TapMxr_New(stSndInf.nMxrCh);
		//各デバイスチャネルで再生中のフレーズ番号を管理するための配列を確保する。
		snd.dxl.TBL_DevPhr = (uint16_t*)malloc(sizeof(uint16_t) * stSndInf.nDevCh);
		if(!snd.dxl.TBL_DevPhr) { DIE(); }
		memset(snd.dxl.TBL_DevPhr, -1/*=UINT16_MAX=無し*/, sizeof(uint16_t) * stSndInf.nDevCh);
	}
ENTER_CS;
	//TAPシーケンサを開始する。
	{
		static ST_TimMgrFunc stTimMgrFunc = { (void (*)(void*))TapSeq_TimeProc, NULL, 1/*調整可*/ };
		TimMgr_AddFunc(pTimMgr, TimMgrCh_10ms/*調整可*/, &stTimMgrFunc);
	}
	//TAPミキサーを開始する。
	{
		static ST_TimMgrFunc stTimMgrFunc = { (void (*)(void*))TapMxr_TimeProc, NULL, 5/*調整可*/ };
		TimMgr_AddFunc(pTimMgr, TimMgrCh_10ms/*調整可*/, &stTimMgrFunc);
	}
LEAVE_CS;
}
/*--------------------------------------------------------------------------*/
//フレーズ番号を指定して、ムービーの再生準備を行います。
//ADXを使っていない場合は何もしない(エラーにもしない)ので、use_adxの設定に関係無く呼び出して安全です。
//{{2016/09/19変更:AdxHelper_Prepare()が戻り値を返すようになった事に伴い、prep_phr()も戻り値を返すように変更しました。
//void prep_phr(int iPhr) {
//#ifdef  USE_ADX
//	//DxLibを使う場合
//	if(!use_adx) {
//		/** no job **/
//	//ADXを使う場合
//	} else {
//		//レジストリから、サウンドデータのファイル名を取得する。
//		const char* path = REG_get_string_l(stPhrInf.pPhrDef, iPhr, stPhrInf.name_Path, -1);
//		if(!path) { DIE(); }
//		//再生準備を行う。
//		AdxHelper_Prepare(path);
//	}
//#endif//USE_ADX
//}
//↓2016/09/19変更:AdxHelper_Prepare()が戻り値を返すようになった事に伴い、prep_phr()も戻り値を返すように変更しました。
int prep_phr(int iPhr) {
#ifdef  USE_ADX
	//DxLibを使う場合
	if(!use_adx) {
		//DxLibの再生準備にはまだ対応していないので、常に再生準備完了として0を返す事にしました。
		//再生遅延が発生する事以外は、アプリケーションの観点からは不可視であるはずです。
		//今後、DxLibの再生準備に対応したら、正確な値を返すように変更して下さい。
		return 0;
	//ADXを使う場合
	} else {
	    //{{特例処理
		//prep_grp()と違って、サウンドは、一種類のサウンドを複数チャネルで再生出来るので、特例処理は不要です。
		/** no job **/
	    //}}特例処理
		//レジストリから、サウンドデータのファイル名を取得する。
		const char* path = REG_get_string_l(stPhrInf.pPhrDef, iPhr, stPhrInf.name_Path, -1);
		if(!path) { DIE(); }
		//再生準備を行う。
		// - AdxHelper_Prepare()の戻り値をそのまま返します。
		//   戻り値の仕様は、AdxHelper_Prepare()のコメントを参照して下さい。
		return AdxHelper_Prepare(path);
	}
#endif//USE_ADX
}
//}}2016/09/19変更:AdxHelper_Prepare()が戻り値を返すようになった事に伴い、prep_phr()も戻り値を返すように変更しました。
/****************************************************************************
 *	その他のユーティリティ関数
 ****************************************************************************/
//{{2016/03/09削除:「Microsoft Platform SDK Febuary 2003 (Last version with VC6 support)」を導入した事に伴い、WINVERと_WIN32_WINNTの定義を、0x0400⇒0x0502に変更しました。詳細は、clipstd.h の同日のコメントを参照して下さい。それによって、当モジュール内でEnumDisplayMonitors()を明示的に宣言する必要が無くなり、EnumDisplayMonitors()の明示的な宣言を削除しました。
//#if     (WINVER<0x0500)
//#ifdef  __cplusplus
//extern "C" {
//#endif//__cplusplus
////以下のAPIは、WINVERが0x0500未満だと宣言されない。
////2016/02/17現在は、clipstd.hにてWINVERを0x0400に定義しており、これらの宣言はおこなわれないので、ここで明示的に宣言する事にした。
////尚、WINVERを0x0500にすると、他のヘッダファイルとの整合性が取れず、VC++6.0の環境で大量に警告が発生するので、0x0500には出来ない。
//DECLARE_HANDLE(HMONITOR);	//"\Program Files\Microsoft Visual Studio\VC98\Include\MULTIMON.H"より
//typedef BOOL (CALLBACK* MONITORENUMPROC)(HMONITOR, HDC, LPRECT, LPARAM);	//"\Program Files\Microsoft Visual Studio\VC98\Include\WINUSER.H"より
//WINUSERAPI BOOL WINAPI EnumDisplayMonitors(HDC hdc, LPCRECT lprcClip, MONITORENUMPROC lpfnEnum, LPARAM dwData);	//"\Program Files\Microsoft Visual Studio\VC98\Include\WINUSER.H"より
//#ifdef  __cplusplus
//}//extern "C"
//#endif//__cplusplus
//#endif//(WINVER<0x0500)
//}}2016/03/09削除:「Microsoft Platform SDK Febuary 2003 (Last version with VC6 support)」を導入した事に伴い、WINVERと_WIN32_WINNTの定義を、0x0400⇒0x0502に変更しました。詳細は、clipstd.h の同日のコメントを参照して下さい。それによって、当モジュール内でEnumDisplayMonitors()を明示的に宣言する必要が無くなり、EnumDisplayMonitors()の明示的な宣言を削除しました。
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
	//モニタの左上座標が(0,0)でなければ、サブモニタと判断して、サブモニタの矩形を格納する。
	if(lprcMonitor->left || lprcMonitor->top) { *(LPRECT)dwData = *lprcMonitor; }	//To continue the enumeration, return TRUE.
	//サブモニタを発見したかどうかに関係無く、列挙を継続する。
	// - MonitorEnumProc()がFALSEを返して列挙を打ち切ると、EnumDisplayMonitors()もFALSEを返してエラーと区別出来ないようだ。
	//   最初のサブモニタを発見したら、これ以上列挙を継続しても無駄なのだが、TRUEを返して最後まで列挙を継続せざるを得ない。
	return TRUE;	//To continue the enumeration, return TRUE.
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//サブモニタの矩形を取得する。
//[in]
//	pSubMonRect		サブモニタの矩形を格納する変数へのポインタ。(NULL不可)
//[out]
//	戻り値			マルチモニタ環境ならば、pSubMonRectにサブモニタの矩形を格納し、TRUEを返す。
//				シングルモニタ環境ならば、pSubMonRectは不定(実際にはクリア)で、FALSEを返す。
int GetSubMonitorRect(RECT* pSubMonitorRect) {
	//サブモニタの矩形を、(0,0)-(0,0)に初期化しておく。
	if(!SetRect(pSubMonitorRect, 0, 0, 0, 0)) { DIE(); }
	//サブモニタの矩形(有れば)を取得する。
	if(!EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (LPARAM)pSubMonitorRect)) { DIE(); }
	//サブモニタの矩形が格納されていたら、マルチモニタ環境と判断、TRUEを返す。
	//サブモニタの矩形が格納されていなければ、シングルモニタと判断し、FALSEを返す。
	return pSubMonitorRect->left || pSubMonitorRect->top;
}
/*--------------------------------------------------------------------------*/
//free_all_grp()とfree_all_snd()を両方呼び出すだけのユーティリティー関数です。
//free_all_grp()とfree_all_snd()は同じタイミングで呼び出す事が多いと思うので、アプリケーションの利便性のために用意しておく事にしました。
//グラフィックリソースだけを開放したい場合や、サウンドリソースだけを開放したい場合は、free_all_grp(),又は,free_all_snd()を個別に呼び出して下さい。
void free_all_res() {
	free_all_grp();	//グラフィックリソースを全て開放します。
	free_all_snd();	//サウンドリソースを全て開放します。
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//グラフィックリソースを全て開放します。
// - 当関数の呼び出しは必須ではありません。
//   極端に多くのグラフィックリソースを使用するアプリケーションの場合のみ、時々当関数を呼び出して、蓄積されたグラフィックリソースを開放して下さい。
// - 当関数を呼び出すタイミングは、シーンの切り替わり等で呼び出すのが良いと思います。
//   当関数の呼び出しは、ムービーの停止も伴うからです。
void free_all_grp() {
	ght_iterator_t iterator;
	//auto_stop_grp()を二回呼ぶ事で自動的に、ムービーグラフィックを停止して開放出来ます。
	auto_stop_grp();	//再生中のムービーグラフィックのTBL_PlayingGrp[].bPlayを確実に0にする。
	auto_stop_grp();	//再生中のムービーグラフィックのTBL_PlayingGrp[].iGrpに対してfree_grp()を呼び出す。
    #ifdef  _DEBUG
	//再生中のムービーグラフィックが残っていない事を確認する。
	int i;
	for(i = 0; i < ARRAY_SIZE(TBL_PlayingGrp); i++) {
		if(TBL_PlayingGrp[i].iGrp) { DIE(); }	//もしここで停止したら当モジュールのバグです。
	}
#ifdef  USE_SOFDEC
	//再生中のSofdecムービーが残っていない事を確認する。
	if(ht_usm && ght_size(ht_usm)) { DIE(); }	//もしここで停止したら当モジュールのバグです。
#endif//USE_SOFDEC
    #endif//_DEBUG
    //↑この順序は必須です。
    //↓逆にすると、TBL_PlayingGrp[].iGrpが残ってしまいます。
	//グラフィックハンドルのハッシュテーブルが作成されていたら…
	if(ht_grp) {
		//静止画グラフィックを開放する。
		// - ムービーグラフィックは上の処理で全て開放されたはずなので、ht_grpに残っているのは全て静止画グラフィックであるはずです。
		int gr, *pGrp;
//{{2016/09/07変更:64ビット対応のための変更を行いました。
//		for(gr = (int)ght_first(ht_grp, &iterator, (const void**)&pGrp);
//		    gr;
//		    gr = (int)ght_next( ht_grp, &iterator, (const void**)&pGrp)) {
//↓2016/09/07変更:64ビット対応のための変更を行いました。
#ifndef _WIN64
		for(gr = (int)ght_first(ht_grp, &iterator, (const void**)&pGrp);	//32ビット版
		    gr;
		    gr = (int)ght_next( ht_grp, &iterator, (const void**)&pGrp)) {
#else //_WIN64
		for(gr = (intptr_t)ght_first(ht_grp, &iterator, (const void**)&pGrp);	//64ビット版
		    gr;
		    gr = (intptr_t)ght_next( ht_grp, &iterator, (const void**)&pGrp)) {
#endif//_WIN64
//}}2016/09/07変更:64ビット対応のための変更を行いました。
			free_grp(*pGrp);
		}
	    #ifdef  _DEBUG
		//グラフィックハンドルが残っていない事を確認する。
		if(ght_size(ht_grp)) { DIE(); }	//もしここで停止したら当モジュールのバグです。
	    #endif//_DEBUG
	//不要	ght_finalize(ht_grp);	┬ハッシュテーブルは削除しない事にしました。削除してもまたすぐに作成する事になるだろうからです。
	//不要	ht_grp = NULL;		┘
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//サウンドリソースを全て開放します。
// - 当関数の呼び出しは必須ではありません。
//   極端に多くのサウンドリソースを使用するアプリケーションの場合のみ、時々当関数を呼び出して、蓄積されたサウンドリソースを開放して下さい。
// - 当関数を呼び出すタイミングは、シーンの切り替わり等で呼び出すのが良いと思います。
//   当関数の呼び出しは、サウンドの停止も伴うからです。
// - 当関数は、init_snd()と対になる関数ではありません。
//   当関数を呼び出した後に、再度、init_snd()を呼び出してはいけません。
// - ADXのサウンド機能を使用している場合は、蓄積されるサウンドリソースはほとんど存在しないので、当関数を呼び出す事にあまり意味はありません。
//   ADXのサウンド機能を使用している場合に、当関数を呼び出しても害は有りませんが、呼び出さなくても良いと思います。
void free_all_snd() {
	ght_iterator_t iterator;
	int i;
    //{{この順序で処理すれば、割り込み禁止は不要です。
	//シーケンサチャネルを全て停止する。
	for(i = 0; i < stSndInf.nSeqCh; i++) { TapSeqCh_Stop(pTapSeq, i); }
    //↑この順序は必須です。
    //↓逆にすると、論理チャネルを停止した後に割り込みが発生して、シーケンサによって論理チャネルがまた再生開始されてしまう可能性が有ります。
	//論理チャネルを全て停止する。
	for(i = 0; i < stSndInf.nLogCh; i++) { TapLogCh_Stop(pTapSeq, i); }
    //}}この順序で処理すれば、割り込み禁止は不要です。
#ifdef  USE_ADX
	//DxLibを使う場合
	if(!use_adx) {
#endif//USE_ADX
	    #ifdef  _DEBUG
		//各デバイスチャネルで再生中のフレーズ番号が、無し(停止中)に戻っている事を確認する。
		for(i = 0; i < stSndInf.nDevCh; i++) {
			if(snd.dxl.TBL_DevPhr[i] != UINT16_MAX) { DIE(); }	//もしここで停止したら当モジュールのバグです。
		}
	    #endif//_DEBUG
		//サウンドハンドルのハッシュテーブルが作成されていたら…
		if(snd.dxl.ht_pr) {
			//サウンドハンドルを全て開放する。
			int pr, *pPhr;
//{{2016/09/07変更:64ビット対応のための変更を行いました。
//			for(pr = (int)ght_first(snd.dxl.ht_pr, &iterator, (const void**)&pPhr);
//			    pr;
//			    pr = (int)ght_next( snd.dxl.ht_pr, &iterator, (const void**)&pPhr)) {
//↓2016/09/07変更:64ビット対応のための変更を行いました。
#ifndef _WIN64
			for(pr = (int)ght_first(snd.dxl.ht_pr, &iterator, (const void**)&pPhr);		//32ビット版
			    pr;
			    pr = (int)ght_next( snd.dxl.ht_pr, &iterator, (const void**)&pPhr)) {
#else //_WIN64
			for(pr = (intptr_t)ght_first(snd.dxl.ht_pr, &iterator, (const void**)&pPhr);	//64ビット版
			    pr;
			    pr = (intptr_t)ght_next( snd.dxl.ht_pr, &iterator, (const void**)&pPhr)) {
#endif//_WIN64
//}}2016/09/07変更:64ビット対応のための変更を行いました。
				free_snd(*pPhr);
			}
		    #ifdef  _DEBUG
			//サウンドハンドルが残っていない事を確認する。
			if(ght_size(snd.dxl.ht_pr)) { DIE(); }	//もしここで停止したら当モジュールのバグです。
		    #endif//_DEBUG
		//不要	ght_finalize(snd.dxl.ht_pr);	┬ハッシュテーブルは削除しない事にしました。削除してもまたすぐに作成する事になるだろうからです。
		//不要	snd.dxl.ht_pr = NULL;		┘
		}
#ifdef  USE_ADX
	//ADXを使う場合
	} else {
		//サウンドハンドルの音の総時間の、キャッシュが作成されていたら…
		if(snd.adx.ht_time) {
			//サウンドハンドルの音の総時間の、キャッシュを空に戻す。
			int t, *pPhr;
//{{2016/09/07変更:64ビット対応のための変更を行いました。
//			for(t = (int)ght_first(snd.adx.ht_time, &iterator, (const void**)&pPhr);
//			    t;
//			    t = (int)ght_next( snd.adx.ht_time, &iterator, (const void**)&pPhr)) {
//↓2016/09/07変更:64ビット対応のための変更を行いました。
#ifndef _WIN64
			for(t = (int)ght_first(snd.adx.ht_time, &iterator, (const void**)&pPhr);	//32ビット版
			    t;
			    t = (int)ght_next( snd.adx.ht_time, &iterator, (const void**)&pPhr)) {
#else //_WIN64
			for(t = (intptr_t)ght_first(snd.adx.ht_time, &iterator, (const void**)&pPhr);	//64ビット版
			    t;
			    t = (intptr_t)ght_next( snd.adx.ht_time, &iterator, (const void**)&pPhr)) {
#endif//_WIN64
//}}2016/09/07変更:64ビット対応のための変更を行いました。
				ght_remove(snd.adx.ht_time, sizeof *pPhr, pPhr);
				//                          ~~~~~~~~~~~~'sizeof pPhr'ではなく'sizeof *pPhr'である事に注意!!(結果的にどちらも同じサイズではあるが)
			}
		    #ifdef  _DEBUG
			//キャッシュが残っていない事を確認する。
			if(ght_size(snd.adx.ht_time)) { DIE(); }
		    #endif//_DEBUG
		//不要	ght_finalize(snd.adx.ht_time);	┬ハッシュテーブルは削除しない事にしました。削除してもまたすぐに作成する事になるだろうからです。
		//不要	snd.adx.ht_time = NULL;		┘
		}
	}
#endif//USE_ADX
}
/*--------------------------------------------------------------------------*/
// * Sat Sep 24 21:37:23 JST 2016 Naoyuki Sawa
// - FullyLoad(),FullyLoadText()を追加しました。
//   DxLibのFileRead_fullyLoad()系関数を簡単に使うためのラッパーです。
// - DXアーカイブ内にファイルが有ればそれを読み込み、無ければ通常のファイルを読み込みます。
//   (SetUseDXArchiveFlag(),SetDXArchivePriority()の設定に依存します。)
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//ファイル全体をバイナリ形式で読み込みます。
//読み込みに成功したらメモリブロックのポインタを返します。アプリケーションがfree()で開放して下さい。
//読み込みに失敗したらNULLポインタを返します。
void* FullyLoad(const char* FilePath) {
	void* s = NULL;	//ファイルが開けなかった場合のために、戻り値を仮にNULLとしておく。
	//□参照資料「ＤＸライブラリプログラム＆プログラム質問掲示板(スレッド表示) - マルチスレッドとSetUseASyncLoadFlag」(http://dxlib.o.oo7.jp/cgi/patiobbs/patio.cgi?mode=view&no=3129)
	//│指定のファイルの内容を全てメモリに読み込み、その情報のアクセスに必要なハンドルを返す。(戻り値 -1:エラー -1以外:ハンドル)
	//│使い終わったらハンドルはFileRead_fullyLoad_deleteで削除する必要があります。
	int fh = FileRead_fullyLoad(FilePath);
	if(fh != -1) {	//ファイルが開けたら…
		//│FileRead_fullyLoadで読み込んだファイルの内容を格納したメモリアドレスを取得する。
		const void* image = FileRead_fullyLoad_getImage(fh);
		if(!image) { DIE(); }
		//│FileRead_fullyLoadで読み込んだファイルのサイズを取得する。
		int size = FileRead_fullyLoad_getSize(fh);
		if(size == -1) { DIE(); }
		s = memdup(image, size);	//FileRead_fullyLoadを呼び出す前にメモリを複製する。
		//│FileRead_fullyLoadで読み込んだファイルのハンドルを削除する。
		if(FileRead_fullyLoad_delete(fh)) { DIE(); }
	}
	//読み込みに成功したらメモリブロックのポインタ、読み込みに失敗したらNULLポインタを返す。
	return s;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//ファイル全体をテキスト形式で読み込みます。
//読み込みに成功したら文字列のポインタを返します。アプリケーションがfree()で開放して下さい。
//読み込みに失敗したらNULLポインタを返します。
char* FullyLoadText(const char* FilePath) {
	//ファイル全体をバイナリ形式で読み込む。
	char* s = (char*)FullyLoad(FilePath);
	if(s) {	//読み込みに成功したら…
		//CR+LFをLFに変換して、文字列を作成する。
		char* t = strreplace(s, "\r\n", "\n");
		//変換元のメモリブロックを開放する。
		free(s);
		//変換した文字列を、戻り値に設定する。
		s = t;
	}
	//読み込みに成功したら文字列のポインタ、読み込みに失敗したらNULLポインタを返す。
	return s;
}
