2 using System.Collections.Generic;
\r
4 using System.Diagnostics;
\r
5 using System.Runtime.InteropServices;
\r
7 using System.Threading;
\r
8 using System.Runtime.Serialization.Formatters.Binary;
\r
10 using SlimDX.Direct3D9;
\r
12 using SampleFramework;
\r
16 internal class CEnumSongs // #27060 2011.2.7 yyagi 曲リストを取得するクラス
\r
17 { // ファイルキャッシュ(songslist.db)からの取得と、ディスクからの取得を、この一つのクラスに集約。
\r
19 public CSongs管理 Songs管理 // 曲の探索結果はこのSongs管理に読み込まれる
\r
25 public bool IsSongListEnumCompletelyDone // 曲リスト探索と、実際の曲リストへの反映が完了した?
\r
29 return ( this.state == DTXEnumState.CompletelyDone );
\r
32 public bool IsEnumerating
\r
36 if ( thDTXFileEnumerate == null )
\r
40 return thDTXFileEnumerate.IsAlive;
\r
43 public bool IsSongListEnumerated // 曲リスト探索が完了したが、実際の曲リストへの反映はまだ?
\r
47 return ( this.state == DTXEnumState.Enumeratad );
\r
50 public bool IsSongListEnumStarted // 曲リスト探索開始後?(探索完了も含む)
\r
54 return ( this.state != DTXEnumState.None );
\r
57 public void SongListEnumCompletelyDone()
\r
59 this.state = DTXEnumState.CompletelyDone;
\r
60 this.Songs管理 = null; // GCはOSに任せる
\r
62 public bool IsSlowdown // #PREMOVIE再生中は検索負荷を落とす
\r
66 return this.Songs管理.bIsSlowdown;
\r
70 this.Songs管理.bIsSlowdown = value;
\r
74 public void ChangeEnumeratePriority( ThreadPriority tp )
\r
76 if ( this.thDTXFileEnumerate != null && this.thDTXFileEnumerate.IsAlive == true )
\r
78 this.thDTXFileEnumerate.Priority = tp;
\r
81 private readonly string strPathSongsDB = CDTXMania.Instance.strEXEのあるフォルダ + "songs.db";
\r
82 private readonly string strPathSongList = CDTXMania.Instance.strEXEのあるフォルダ + "songlist.db";
\r
84 public Thread thDTXFileEnumerate
\r
89 private enum DTXEnumState
\r
94 Enumeratad, // 探索完了、現在の曲リストに未反映
\r
95 CompletelyDone // 探索完了、現在の曲リストに反映完了
\r
97 private DTXEnumState state = DTXEnumState.None;
\r
103 public CEnumSongs()
\r
105 this.Songs管理 = new CSongs管理();
\r
108 public void Init( List<Cスコア> ls, int n )
\r
110 if ( state == DTXEnumState.None )
\r
112 this.Songs管理.listSongsDB = ls;
\r
113 this.Songs管理.nSongsDBから取得できたスコア数 = n;
\r
118 /// 曲リストのキャッシュ(songlist.db)取得スレッドの開始
\r
120 public void StartEnumFromCache()
\r
122 this.thDTXFileEnumerate = new Thread( new ThreadStart( this.t曲リストの構築1 ) );
\r
123 this.thDTXFileEnumerate.Name = "曲リストの構築";
\r
124 this.thDTXFileEnumerate.IsBackground = true;
\r
125 this.thDTXFileEnumerate.Start();
\r
131 public delegate void AsyncDelegate();
\r
136 public void StartEnumFromDisk()
\r
138 if ( state == DTXEnumState.None || state == DTXEnumState.CompletelyDone )
\r
140 Trace.TraceInformation( "★曲データ検索スレッドを起動しました。" );
\r
143 state = DTXEnumState.Ongoing;
\r
145 // this.autoReset = new AutoResetEvent( true );
\r
147 if ( this.Songs管理 == null ) // Enumerating Songs完了後、CONFIG画面から再スキャンしたときにこうなる
\r
149 this.Songs管理 = new CSongs管理();
\r
151 this.thDTXFileEnumerate = new Thread( new ThreadStart( this.t曲リストの構築2 ) );
\r
152 this.thDTXFileEnumerate.Name = "曲リストの構築";
\r
153 this.thDTXFileEnumerate.IsBackground = true;
\r
154 this.thDTXFileEnumerate.Priority = System.Threading.ThreadPriority.Lowest;
\r
155 this.thDTXFileEnumerate.Start();
\r
163 public void Suspend()
\r
165 if ( this.state != DTXEnumState.CompletelyDone &&
\r
166 ( ( thDTXFileEnumerate.ThreadState & ( System.Threading.ThreadState.Background ) ) != 0 ) )
\r
168 // this.thDTXFileEnumerate.Suspend(); // obsoleteにつき使用中止
\r
169 this.Songs管理.bIsSuspending = true;
\r
170 this.state = DTXEnumState.Suspended;
\r
171 Trace.TraceInformation( "★曲データ検索スレッドを中断しました。" );
\r
178 public void Resume()
\r
180 if ( this.state == DTXEnumState.Suspended )
\r
182 if ( ( this.thDTXFileEnumerate.ThreadState & ( System.Threading.ThreadState.WaitSleepJoin | System.Threading.ThreadState.StopRequested ) ) != 0 ) //
\r
184 // this.thDTXFileEnumerate.Resume(); // obsoleteにつき使用中止
\r
185 this.Songs管理.bIsSuspending = false;
\r
186 this.Songs管理.AutoReset.Set();
\r
187 this.state = DTXEnumState.Ongoing;
\r
188 Trace.TraceInformation( "★曲データ検索スレッドを再開しました。" );
\r
194 /// 曲探索スレッドにサスペンド指示を出してから、本当にサスペンド状態に遷移するまでの間、ブロックする
\r
195 /// 500ms * 10回=5秒でタイムアウトし、サスペンド完了して無くてもブロック解除する
\r
197 public void WaitUntilSuspended()
\r
200 for ( int i = 0; i < 10; i++ )
\r
202 if ( this.state == DTXEnumState.CompletelyDone ||
\r
203 ( thDTXFileEnumerate.ThreadState & ( System.Threading.ThreadState.WaitSleepJoin | System.Threading.ThreadState.Background | System.Threading.ThreadState.Stopped ) ) != 0 )
\r
207 Trace.TraceInformation( "★曲データ検索スレッドの中断待ちです: {0}", this.thDTXFileEnumerate.ThreadState.ToString() );
\r
208 Thread.Sleep( 500 );
\r
216 public void Abort()
\r
218 if ( thDTXFileEnumerate != null )
\r
220 thDTXFileEnumerate.Abort();
\r
221 thDTXFileEnumerate = null;
\r
222 this.state = DTXEnumState.None;
\r
224 this.Songs管理 = null; // Songs管理を再初期化する (途中まで作った曲リストの最後に、一から重複して追記することにならないようにする。)
\r
225 this.Songs管理 = new CSongs管理();
\r
232 /// songlist.dbからの曲リスト構築
\r
234 public void t曲リストの構築1()
\r
237 // 本メソッドは別スレッドで動作するが、プラグイン側でカレントディレクトリを変更しても大丈夫なように、
\r
238 // すべてのファイルアクセスは「絶対パス」で行うこと。(2010.9.16)
\r
239 // 構築が完了したら、DTXEnumerateState state を DTXEnumerateState.Done にすること。(2012.2.9)
\r
240 DateTime now = DateTime.Now;
\r
244 #region [ 0) システムサウンドの構築 ]
\r
245 //-----------------------------
\r
246 CDTXMania.Instance.stage起動.eフェーズID = CStage.Eフェーズ.起動0_システムサウンドを構築;
\r
248 Trace.TraceInformation( "0) システムサウンドを構築します。" );
\r
253 CDTXMania.Instance.Skin.bgm起動画面.t再生する();
\r
254 for ( int i = 0; i < CDTXMania.Instance.Skin.nシステムサウンド数; i++ )
\r
256 if ( !CDTXMania.Instance.Skin[ i ].b排他 ) // BGM系以外のみ読み込む。(BGM系は必要になったときに読み込む)
\r
258 CSkin.Cシステムサウンド cシステムサウンド = CDTXMania.Instance.Skin[ i ];
\r
259 if ( !CDTXMania.Instance.bコンパクトモード || cシステムサウンド.bCompact対象 )
\r
264 Trace.TraceInformation( "システムサウンドを読み込みました。({0})", cシステムサウンド.strファイル名 );
\r
265 //if ( ( cシステムサウンド == CDTXMania.Instance.Skin.bgm起動画面 ) && cシステムサウンド.b読み込み成功 )
\r
267 // cシステムサウンド.t再生する();
\r
270 catch ( FileNotFoundException )
\r
272 Trace.TraceWarning( "システムサウンドが存在しません。({0})", cシステムサウンド.strファイル名 );
\r
274 catch ( Exception e )
\r
276 Trace.TraceError( e.Message );
\r
277 Trace.TraceWarning( "システムサウンドの読み込みに失敗しました。({0})", cシステムサウンド.strファイル名 );
\r
282 lock ( CDTXMania.Instance.stage起動.list進行文字列 )
\r
284 CDTXMania.Instance.stage起動.list進行文字列.Add( "Loading system sounds ... OK " );
\r
291 //-----------------------------
\r
294 if ( CDTXMania.Instance.bコンパクトモード )
\r
296 Trace.TraceInformation( "コンパクトモードなので残りの起動処理は省略します。" );
\r
300 #region [ 00) songlist.dbの読み込みによる曲リストの構築 ]
\r
301 //-----------------------------
\r
302 CDTXMania.Instance.stage起動.eフェーズID = CStage.Eフェーズ.起動00_songlistから曲リストを作成する;
\r
304 Trace.TraceInformation( "1) songlist.dbを読み込みます。" );
\r
309 if ( !CDTXMania.Instance.ConfigIni.bConfigIniがないかDTXManiaのバージョンが異なる )
\r
311 CSongs管理 s = new CSongs管理();
\r
312 s = Deserialize( strPathSongList ); // 直接this.Songs管理にdeserialize()結果を代入するのは避ける。nullにされてしまうことがあるため。
\r
318 int scores = this.Songs管理.n検索されたスコア数;
\r
319 Trace.TraceInformation( "songlist.db の読み込みを完了しました。[{0}スコア]", scores );
\r
320 lock ( CDTXMania.Instance.stage起動.list進行文字列 )
\r
322 CDTXMania.Instance.stage起動.list進行文字列.Add( "Loading songlist.db ... OK" );
\r
327 Trace.TraceInformation( "初回の起動であるかまたはDTXManiaのバージョンが上がったため、songlist.db の読み込みをスキップします。" );
\r
328 lock ( CDTXMania.Instance.stage起動.list進行文字列 )
\r
330 CDTXMania.Instance.stage起動.list進行文字列.Add( "Loading songlist.db ... Skip" );
\r
341 #region [ 1) songs.db の読み込み ]
\r
342 //-----------------------------
\r
343 CDTXMania.Instance.stage起動.eフェーズID = CStage.Eフェーズ.起動1_SongsDBからスコアキャッシュを構築;
\r
345 Trace.TraceInformation( "2) songs.db を読み込みます。" );
\r
350 if ( !CDTXMania.Instance.ConfigIni.bConfigIniがないかDTXManiaのバージョンが異なる )
\r
354 this.Songs管理.tSongsDBを読み込む( strPathSongsDB );
\r
358 Trace.TraceError( "songs.db の読み込みに失敗しました。" );
\r
361 int scores = ( this.Songs管理 == null ) ? 0 : this.Songs管理.nSongsDBから取得できたスコア数; // 読み込み途中でアプリ終了した場合など、CDTXMania.Instance.Songs管理 がnullの場合があるので注意
\r
362 Trace.TraceInformation( "songs.db の読み込みを完了しました。[{0}スコア]", scores );
\r
363 lock ( CDTXMania.Instance.stage起動.list進行文字列 )
\r
365 CDTXMania.Instance.stage起動.list進行文字列.Add( "Loading songs.db ... OK" );
\r
370 Trace.TraceInformation( "初回の起動であるかまたはDTXManiaのバージョンが上がったため、songs.db の読み込みをスキップします。" );
\r
371 lock ( CDTXMania.Instance.stage起動.list進行文字列 )
\r
373 CDTXMania.Instance.stage起動.list進行文字列.Add( "Loading songs.db ... Skip" );
\r
381 //-----------------------------
\r
387 CDTXMania.Instance.stage起動.eフェーズID = CStage.Eフェーズ.起動7_完了;
\r
388 TimeSpan span = (TimeSpan) ( DateTime.Now - now );
\r
389 Trace.TraceInformation( "起動所要時間: {0}", span.ToString() );
\r
390 lock ( this ) // #28700 2012.6.12 yyagi; state change must be in finally{} for exiting as of compact mode.
\r
392 state = DTXEnumState.CompletelyDone;
\r
399 /// 起動してタイトル画面に遷移した後にバックグラウンドで発生させる曲検索
\r
400 /// #27060 2012.2.6 yyagi
\r
402 private void t曲リストの構築2()
\r
405 // 本メソッドは別スレッドで動作するが、プラグイン側でカレントディレクトリを変更しても大丈夫なように、
\r
406 // すべてのファイルアクセスは「絶対パス」で行うこと。(2010.9.16)
\r
407 // 構築が完了したら、DTXEnumerateState state を DTXEnumerateState.Done にすること。(2012.2.9)
\r
409 DateTime now = DateTime.Now;
\r
410 bool bIsAvailableSongList = false;
\r
411 bool bIsAvailableSongsDB = false;
\r
412 bool bSucceededFastBoot = false;
\r
417 #region [ 2) 曲データの検索 ]
\r
418 //-----------------------------
\r
419 // base.eフェーズID = CStage.Eフェーズ.起動2_曲を検索してリストを作成する;
\r
421 Trace.TraceInformation( "enum2) 曲データを検索します。" );
\r
426 if ( !string.IsNullOrEmpty( CDTXMania.Instance.ConfigIni.str曲データ検索パス ) )
\r
428 string[] strArray = CDTXMania.Instance.ConfigIni.str曲データ検索パス.Split( new char[] { ';' } );
\r
429 if ( strArray.Length > 0 )
\r
432 foreach ( string str in strArray )
\r
435 if ( !Path.IsPathRooted( path ) )
\r
437 path = CDTXMania.Instance.strEXEのあるフォルダ + str; // 相対パスの場合、絶対パスに直す(2010.9.16)
\r
440 if ( !string.IsNullOrEmpty( path ) )
\r
442 Trace.TraceInformation( "検索パス: " + path );
\r
447 this.Songs管理.t曲を検索してリストを作成する( path, true );
\r
449 catch ( Exception e )
\r
451 Trace.TraceError( e.Message );
\r
452 Trace.TraceError( e.StackTrace );
\r
453 Trace.TraceError( "例外が発生しましたが処理を継続します。" );
\r
465 Trace.TraceWarning( "曲データの検索パス(DTXPath)の指定がありません。" );
\r
470 Trace.TraceInformation( "曲データの検索を完了しました。[{0}曲{1}スコア]", this.Songs管理.n検索された曲ノード数, this.Songs管理.n検索されたスコア数 );
\r
473 // lock ( this.list進行文字列 )
\r
475 // this.list進行文字列.Add( string.Format( "{0} ... {1} scores ({2} songs)", "Enumerating songs", this..Songs管理_裏読.n検索されたスコア数, this.Songs管理_裏読.n検索された曲ノード数 ) );
\r
477 //-----------------------------
\r
479 #region [ 3) songs.db 情報の曲リストへの反映 ]
\r
480 //-----------------------------
\r
481 // base.eフェーズID = CStage.Eフェーズ.起動3_スコアキャッシュをリストに反映する;
\r
482 Trace.TraceInformation( "enum3) songs.db の情報を曲リストへ反映します。" );
\r
487 if ( this.Songs管理.listSongsDB != null )
\r
489 this.Songs管理.tスコアキャッシュを曲リストに反映する();
\r
492 catch ( Exception e )
\r
494 Trace.TraceError( e.Message );
\r
495 Trace.TraceError( e.StackTrace );
\r
496 Trace.TraceError( "例外が発生しましたが処理を継続します。" );
\r
500 Trace.TraceInformation( "曲リストへの反映を完了しました。[{0}/{1}スコア]", this.Songs管理.nスコアキャッシュから反映できたスコア数, this.Songs管理.n検索されたスコア数 );
\r
503 // lock ( this.list進行文字列 )
\r
505 // this.list進行文字列.Add( string.Format( "{0} ... {1}/{2}", "Loading score properties from songs.db", CDTXMania.Instance.Songs管理_裏読.nスコアキャッシュから反映できたスコア数, cs.n検索されたスコア数 ) );
\r
507 //-----------------------------
\r
509 #region [ 4) songs.db になかった曲データをファイルから読み込んで反映 ]
\r
510 //-----------------------------
\r
511 // base.eフェーズID = CStage.Eフェーズ.起動4_スコアキャッシュになかった曲をファイルから読み込んで反映する;
\r
513 int num2 = this.Songs管理.n検索されたスコア数 - this.Songs管理.nスコアキャッシュから反映できたスコア数;
\r
515 Trace.TraceInformation( "{0}, {1}", this.Songs管理.n検索されたスコア数, this.Songs管理.nスコアキャッシュから反映できたスコア数 );
\r
516 Trace.TraceInformation( "enum4) songs.db になかった曲データ[{0}スコア]の情報をファイルから読み込んで反映します。", num2 );
\r
521 this.Songs管理.tSongsDBになかった曲をファイルから読み込んで反映する();
\r
523 catch ( Exception e )
\r
525 Trace.TraceError( e.Message );
\r
526 Trace.TraceError( e.StackTrace );
\r
527 Trace.TraceError( "例外が発生しましたが処理を継続します。" );
\r
531 Trace.TraceInformation( "曲データへの反映を完了しました。[{0}/{1}スコア]", this.Songs管理.nファイルから反映できたスコア数, num2 );
\r
534 // lock ( this.list進行文字列 )
\r
536 // this.list進行文字列.Add( string.Format( "{0} ... {1}/{2}", "Loading score properties from files", CDTXMania.Instance.Songs管理_裏読.nファイルから反映できたスコア数, CDTXMania.Instance.Songs管理_裏読.n検索されたスコア数 - cs.nスコアキャッシュから反映できたスコア数 ) );
\r
538 //-----------------------------
\r
540 #region [ 5) 曲リストへの後処理の適用 ]
\r
541 //-----------------------------
\r
542 // base.eフェーズID = CStage.Eフェーズ.起動5_曲リストへ後処理を適用する;
\r
544 Trace.TraceInformation( "enum5) 曲リストへの後処理を適用します。" );
\r
549 this.Songs管理.t曲リストへ後処理を適用する();
\r
551 catch ( Exception e )
\r
553 Trace.TraceError( e.Message );
\r
554 Trace.TraceError( e.StackTrace );
\r
555 Trace.TraceError( "例外が発生しましたが処理を継続します。" );
\r
559 Trace.TraceInformation( "曲リストへの後処理を完了しました。" );
\r
562 // lock ( this.list進行文字列 )
\r
564 // this.list進行文字列.Add( string.Format( "{0} ... OK", "Building songlists" ) );
\r
566 //-----------------------------
\r
568 #region [ 6) songs.db への保存 ]
\r
569 //-----------------------------
\r
570 // base.eフェーズID = CStage.Eフェーズ.起動6_スコアキャッシュをSongsDBに出力する;
\r
572 Trace.TraceInformation( "enum6) 曲データの情報を songs.db へ出力します。" );
\r
577 this.Songs管理.tスコアキャッシュをSongsDBに出力する( strPathSongsDB );
\r
579 catch ( Exception e )
\r
581 Trace.TraceError( e.Message );
\r
582 Trace.TraceError( e.StackTrace );
\r
583 Trace.TraceError( "例外が発生しましたが処理を継続します。" );
\r
587 Trace.TraceInformation( "songs.db への出力を完了しました。[{0}スコア]", this.Songs管理.nSongsDBへ出力できたスコア数 );
\r
590 // lock ( this.list進行文字列 )
\r
592 // this.list進行文字列.Add( string.Format( "{0} ... OK", "Saving songs.db" ) );
\r
596 // if ( !bSucceededFastBoot ) // songs2.db読み込みに成功したなら、songs2.dbを新たに作らない
\r
597 #region [ 7) songs2.db への保存 ] // #27060 2012.1.26 yyagi
\r
598 Trace.TraceInformation( "enum7) 曲データの情報を songlist.db へ出力します。" );
\r
601 SerializeSongList( this.Songs管理, strPathSongList );
\r
602 Trace.TraceInformation( "songlist.db への出力を完了しました。[{0}スコア]", this.Songs管理.nSongsDBへ出力できたスコア数 );
\r
604 //-----------------------------
\r
611 // base.eフェーズID = CStage.Eフェーズ.起動7_完了;
\r
612 TimeSpan span = (TimeSpan) ( DateTime.Now - now );
\r
613 Trace.TraceInformation( "曲探索所要時間: {0}", span.ToString() );
\r
617 // state = DTXEnumState.Done; // DoneにするのはCDTXMania.Instance.cs側にて。
\r
618 state = DTXEnumState.Enumeratad;
\r
627 private static void SerializeSongList( CSongs管理 cs, string strPathSongList )
\r
629 bool bSucceededSerialize = true;
\r
630 Stream output = null;
\r
633 output = File.Create( strPathSongList );
\r
634 BinaryFormatter formatter = new BinaryFormatter();
\r
635 formatter.Serialize( output, cs );
\r
637 catch ( Exception e )
\r
639 bSucceededSerialize = false;
\r
640 Trace.TraceError( e.Message );
\r
641 Trace.TraceError( e.StackTrace );
\r
642 Trace.TraceError( "例外が発生しましたが処理を継続します。" );
\r
647 if ( !bSucceededSerialize )
\r
651 File.Delete( strPathSongList ); // serializeに失敗したら、songs2.dbファイルを消しておく
\r
653 catch ( Exception )
\r
662 /// 曲リストのdeserialize
\r
664 /// <param name="songs管理"></param>
\r
665 /// <param name="strPathSongList"></param>
\r
666 private CSongs管理 Deserialize( string strPathSongList )
\r
668 CSongs管理 songs管理 = null;
\r
671 #region [ SongListDB(songlist.db)を読み込む ]
\r
672 // byte[] buf = File.ReadAllBytes( SongListDBファイル名 ); // 一旦メモリにまとめ読みしてからdeserializeした方が高速かと思ったら全く変わらなかったので削除
\r
673 // using ( MemoryStream input = new MemoryStream(buf, false) )
\r
674 using ( Stream input = File.OpenRead( strPathSongList ) )
\r
678 BinaryFormatter formatter = new BinaryFormatter();
\r
679 songs管理 = (CSongs管理) formatter.Deserialize( input );
\r
681 catch ( Exception )
\r
690 Trace.TraceError( "songlist.db の読み込みに失敗しました。" );
\r