/// <summary> /// 指定ゴーストのシェルを同期的に読み込む /// </summary> protected void LoadShellAndFaceImage(ExplorerGhost ghost, bool reload = false) { // リロードフラグONの場合は既存情報を破棄 if (reload) { Shells[ghost.DirPath] = null; if (FaceImages.ContainsKey(ghost.DirPath)) { if (FaceImages[ghost.DirPath] != null) { FaceImages[ghost.DirPath].Dispose(); } FaceImages.Remove(ghost.DirPath); } imgListFace.Images.RemoveByKey(ghost.DirPath); } // メイン読み込み処理。シェルをまだ読み込んでいない場合、もしくはリロードフラグON時のみ実行 if (reload || !Shells.ContainsKey(ghost.DirPath) || Shells[ghost.DirPath] == null) { // シェル読み込み LoadShell(ghost); // 顔画像読み込み LoadFaceImageIfShellLoaded(ghost); // UI側へ反映 ReflectFaceImageToUIIfShellLoaded(ghost); } }
/// <summary> /// 全ゴーストの顔画像の取得・変換を行う (キャッシュ処理も行う) /// </summary> /// <returns>ゴーストフォルダパスをキー、顔画像 (Bitmap) を値とするDictionary</returns> public virtual Bitmap GetFaceImage(ExplorerGhost ghost, ExplorerShell shell, Size faceSize) { var cacheDir = Util.GetCacheDirPath(); // キャッシュフォルダが存在しなければ作成 if (!Directory.Exists(cacheDir)) { Directory.CreateDirectory(cacheDir); } try { Bitmap face = null; var cachePath = Path.Combine(cacheDir, string.Format("{0}_face.png", Path.GetFileName(ghost.DirPath))); // 顔画像のキャッシュがあり、更新日時がシェルの更新日以降なら、キャッシュを使用 if (File.Exists(cachePath) && File.GetLastWriteTime(cachePath) >= shell.LastModified) { face = new Bitmap(cachePath); } else { // キャッシュがない場合、サーフェス0から顔画像を生成 (サーフェスを読み込めている場合のみ) if (shell.SakuraSurfaceModel != null) { using (var faceImg = shell.DrawFaceImage(shell.SakuraSurfaceModel, faceSize.Width, faceSize.Height)) { face = faceImg.ToBitmap(); } if (face != null) { // 顔画像のキャッシュを保存 face.Save(cachePath); } } } // 画像を返す return(face); } catch (InvalidDescriptException ex) { MessageBox.Show(string.Format("{0} の explorer2\\descript.txt に不正な記述があります。\n{1}", ghost.Name, ex.Message), "エラー", MessageBoxButtons.OK, MessageBoxIcon.Warning); Debug.WriteLine(ex.ToString()); return(null); } catch (Exception ex) { Debug.WriteLine(ex.ToString()); return(null); } }
/// <summary> /// 指定ゴーストの顔画像を読み込む (シェルの読み込みに成功していれば) /// </summary> protected virtual void LoadFaceImageIfShellLoaded(ExplorerGhost ghost) { // ロック取得 (同期処理と非同期処理が競合しないように) lock (GhostManager) { // シェルの読み込みに成功している場合のみ if (Shells.ContainsKey(ghost.DirPath) && Shells[ghost.DirPath] != null) { // まだ読み込んでいなければ、ゴーストの顔画像を変換・取得 if (!FaceImages.ContainsKey(ghost.DirPath)) { FaceImages[ghost.DirPath] = GhostManager.GetFaceImage(ghost, Shells[ghost.DirPath], imgListFace.ImageSize); Debug.WriteLine(string.Format("<{0}> faceImage loaded: {1}", Thread.CurrentThread.ManagedThreadId, ghost.Name)); } } } }
/// <summary> /// 対象ゴーストのシェル情報読み込み /// </summary> public virtual ExplorerShell LoadShell(ExplorerGhost ghost) { // シェルが1つも存在しない場合はエラーとする if (ghost.CurrentShellRelDirPath == null) { throw new ShellNotFoundException(string.Format(@"有効なシェルが1つも存在しません。", ghost.DefaultShellDirName)); } var shellDir = Path.Combine(ghost.DirPath, ghost.CurrentShellRelDirPath); // descript.txtが存在しない場合はエラーとする if (!ExplorerShell.IsShellDir(shellDir)) { throw new ShellDescriptNotFoundException("シェルフォルダの中に descript.txt が存在しません。"); } return(ExplorerShell.Load(shellDir, ghost.SakuraDefaultSurfaceId, ghost.KeroDefaultSurfaceId)); }
/// <summary> /// 指定ゴーストの顔画像をFormへ反映 /// </summary> protected virtual void ReflectFaceImageToUIIfShellLoaded(ExplorerGhost ghost) { // ロック取得 (同期処理と非同期処理が競合しないように) lock (GhostManager) { // シェルの読み込みに成功している場合のみ if (Shells.ContainsKey(ghost.DirPath) && Shells[ghost.DirPath] != null) { // 顔画像を正常に読み込めていれば、イメージリストに追加 if (FaceImages[ghost.DirPath] != null) { imgListFace.Images.Add(ghost.DirPath, FaceImages[ghost.DirPath]); } // 顔画像表示を更新 UpdateFaceImageKey(ghost); } } }
/// <summary> /// 指定ゴーストの顔画像を更新 (不在かどうかに応じて処理を分ける) /// </summary> protected void UpdateFaceImageKey(ExplorerGhost ghost) { // リスト内に項目がなければ何もしない if (!lstGhost.Items.ContainsKey(ghost.DirPath)) { return; } // 不在判定 if (AbsenceInfo.ContainsKey(ghost.DirPath)) { // いない lstGhost.Items[ghost.DirPath].ImageKey = AbsenceInfo[ghost.DirPath]; } else { // いる lstGhost.Items[ghost.DirPath].ImageKey = ghost.DirPath; } }
/// <summary> /// 指定ゴーストのシェルを読み込む /// </summary> protected virtual void LoadShell(ExplorerGhost ghost) { // ロック取得 (同期処理と非同期処理が競合しないように) lock (GhostManager) { try { Shells[ghost.DirPath] = GhostManager.LoadShell(ghost); Debug.WriteLine(string.Format("<{0}> shell loaded: {1}", Thread.CurrentThread.ManagedThreadId, ghost.Name)); } catch (UnhandlableShellException ex) { // 処理不可能なシェル if (!ErrorMessagesOnShellLoading.ContainsKey(ghost.DirPath)) { ErrorMessagesOnShellLoading[ghost.DirPath] = new List <string>(); } ErrorMessagesOnShellLoading[ghost.DirPath].Add(ex.FriendlyMessage); } } }
/// <summary> /// サーフェス画像を取得 (element, MAYUNAの合成も行う。またキャッシュがあればキャッシュから取得) /// </summary> /// <returns>サーフェス画像を取得できた場合はその画像。取得に失敗した場合はnull</returns> protected virtual Bitmap DrawSurfaceInternal(ExplorerGhost ghost, ExplorerShell shell, Shell.SurfaceModel surfaceModel, int surfaceId) { var cacheDir = Util.GetCacheDirPath(); if (surfaceModel == null) { return(null); } // キャッシュフォルダが存在しなければ作成 if (!Directory.Exists(cacheDir)) { Directory.CreateDirectory(cacheDir); } // 立ち絵画像のキャッシュがあり、更新日時がシェルの更新日以降なら、キャッシュを使用 var cachePath = Path.Combine(cacheDir, string.Format("{0}_s{1}.png", Path.GetFileName(ghost.DirPath), surfaceId)); if (File.Exists(cachePath) && File.GetLastWriteTime(cachePath) >= shell.LastModified) { return(new Bitmap(cachePath)); } else { // 立ち絵サーフェス画像を生成 var surface = shell.DrawSurface(surfaceModel); // キャッシュとして保存 surface.Write(cachePath); // サーフェス画像をBitmap形式に変換 var surfaceBmp = surface.ToBitmap(); // 元画像を即時破棄 surface.Dispose(); // サーフェス画像をBitmap形式に変換して返す return(surfaceBmp); } }
/// <summary> /// シェル情報一括読み込み /// </summary> protected virtual void LoadShells() { var appDirPath = Util.GetAppDirPath(); // 既存情報クリア lstShell.Clear(); imgListFace.Images.Clear(); // FMO情報更新 UpdateFMOInfo(); // コマンドライン引数を解釈 var args = Environment.GetCommandLineArgs().ToList(); args.RemoveAt(0); // 先頭はexe名のため削除 var caller = args.First(); // 呼び出し元ゴースト (id or "unspecified") args.RemoveAt(0); // 先頭削除 var ghostDirPath = args.First().TrimEnd('\\'); // ゴーストフォルダパス SakuraFMOData target = null; var matched = Regex.Match(caller, @"^id:(.+?)\z"); if (matched.Success) { // idが指定された場合、そのidと一致するゴースト情報を探す var id = matched.Groups[1].Value.Trim(); target = FMOGhostList.FirstOrDefault(g => g.Id == id); } if (caller == "unspecified") { // "unspecified" が指定された場合、現在起動しているゴーストのうち、ランダムに1体を呼び出し元とする (デバッグ用) var rand = new Random(); target = FMOGhostList[rand.Next(0, FMOGhostList.Count - 1)]; } if (target == null) { throw new Exception(string.Format("指定されたゴーストが見つかりませんでした。 ({0})", caller)); } // 呼び出し元の情報をプロパティにセット CallerId = target.Id; CallerSakuraName = target.Name; CallerKeroName = target.KeroName; CallerHWnd = (IntPtr)target.HWnd; // チェックボックスの位置移動 ChkCloseAfterChange.Left = BtnChange.Left + 1; // ゴースト情報読み込み var ghost = ExplorerGhost.Load(ghostDirPath); // シェル情報読み込み ShellManager = ShellManager.Load(ghostDirPath, ghost.SakuraDefaultSurfaceId, ghost.KeroDefaultSurfaceId); // シェルが1件も取得できなかった場合はエラー表示 if (!ShellManager.ListItems.Any()) { MessageBox.Show("有効なシェルフォルダが1件も見つかりませんでした。" , "エラー" , MessageBoxButtons.OK , MessageBoxIcon.Error); Application.Exit(); return; } // 最終起動時の記録があり、かつ最終起動時とバージョンが異なる場合は、キャッシュをすべて破棄 if (CurrentProfile.LastBootVersion != null && Util.GetVersion() != CurrentProfile.LastBootVersion) { Directory.Delete(Util.GetCacheBaseDirPath(), recursive: true); } // 最終起動情報をセットして、Profileを保存 CurrentProfile.LastBootVersion = Util.GetVersion(); Util.SaveProfile(CurrentProfile); // ゴーストの顔画像を変換・取得 var faceImages = ShellManager.GetFaceImages(imgListFace.ImageSize); // リストビュー構築処理 foreach (var shellItem in ShellManager.ListItems) { // 顔画像を正常に読み込めていれば、イメージリストに追加 if (faceImages.ContainsKey(shellItem.DirPath)) { imgListFace.Images.Add(shellItem.DirPath, faceImages[shellItem.DirPath]); } // リスト項目追加 var item = lstShell.Items.Add(key: shellItem.DirPath, text: shellItem.Name ?? "", imageKey: shellItem.DirPath); item.Tag = shellItem.DirPath; } // 読み込み中表示を消す lblLoading.Hide(); // ゴースト情報リストの更新に伴う画面表示更新 UpdateUIOnFMOChanged(); // ボタン等表示状態を更新 UpdateUIState(); // 現在の使用シェルを選択 var currentShellFolderName = Path.GetFileName(ghost.CurrentShellRelDirPath); foreach (ListViewItem item in lstShell.Items) { var shellFolderName = Path.GetFileName((string)item.Tag); if (shellFolderName == currentShellFolderName) { item.Focused = true; item.Selected = true; break; } } // 選択対象のシェルを判別できなかった場合は、1件目を選択 if (SelectedShellListItem == null) { lstShell.Items[0].Focused = true; lstShell.Items[0].Selected = true; } // スクロール var selectedIndex = lstShell.SelectedItems[0].Index; lstShell.EnsureVisible(selectedIndex); }
/// <summary> /// ゴースト情報の一括読み込み (シェルはまだ読み込まない) /// </summary> public virtual void Load() { // 既存の値はクリア Ghosts.Clear(); // ゴーストフォルダのサブフォルダを列挙 foreach (var subDir in Directory.GetDirectories(GhostDirPath)) { // 隠しフォルダの場合はスキップ var dirInfo = new DirectoryInfo(subDir); if ((dirInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) { continue; } // ゴーストフォルダでなければスキップ if (!ExplorerGhost.IsGhostDir(subDir)) { continue; } // ゴーストの基本情報を読み込み var ghost = ExplorerGhost.Load(subDir); // キーワードが指定されており、かつキーワードに合致しなければスキップ if (!string.IsNullOrWhiteSpace(FilterWord)) { if (!(ghost.Name.Contains(FilterWord) || ghost.SakuraName.Contains(FilterWord) || ghost.KeroName.Contains(FilterWord))) { continue; } } // リストに追加 Ghosts.Add(ghost); } // ゴースト別の使用頻度情報を抽出 var totalBootTimes = new Dictionary <string, long>(); var lastBootSeconds = new Dictionary <string, long>(); foreach (var ghost in Ghosts) { Realize2Text.Record rec = null; if (Realize2Text != null) { rec = Realize2Text.GhostRecords.FirstOrDefault(r => r.Name == ghost.Name); } if (rec != null) { totalBootTimes[ghost.Name] = rec.TotalBootByMinute; lastBootSeconds[ghost.Name] = rec.LastBootSecond; } else { // 情報が見つからなければ0扱い totalBootTimes[ghost.Name] = 0; lastBootSeconds[ghost.Name] = 0; } } // ゴースト別のインストール日付情報を取得 (インストール日時順でソートされた場合のみ) var installSeconds = new Dictionary <string, long>(); if (SortType == Const.SortType.ByRecentInstall) { foreach (var ghost in Ghosts) { // 初期値は0 installSeconds[ghost.DirPath] = 0; // profile\var.txt が存在すれば、その中からインストール時刻を取得 var varPath = Path.Combine(ghost.DirPath, @"ghost\master\profile\var.txt"); if (File.Exists(varPath)) { try { var lines = File.ReadAllLines(varPath); foreach (var line in lines) { if (line.StartsWith("install,")) { var tokens = line.TrimEnd().Split(','); installSeconds[ghost.DirPath] = long.Parse(tokens[1]); break; } } } catch (Exception ex) { Debug.WriteLine(ex); } } } } // 最後にソート switch (SortType) { case Const.SortType.ByBootTime: Ghosts = Ghosts.OrderByDescending(g => totalBootTimes[g.Name]).ToList(); break; case Const.SortType.ByRecentBoot: Ghosts = Ghosts.OrderByDescending(g => lastBootSeconds[g.Name]).ToList(); break; case Const.SortType.ByRecentInstall: Ghosts = Ghosts.OrderByDescending(g => installSeconds[g.DirPath]).ToList(); break; default: // ゴースト名+フォルダパス順 Ghosts = Ghosts.OrderBy(g => Tuple.Create(g.Name, g.DirPath)).ToList(); break; } }
/// <summary> /// kero側サーフェス画像を取得 (element, MAYUNAの合成も行う。またキャッシュがあればキャッシュから取得) /// </summary> /// <returns>サーフェス画像を取得できた場合はその画像。取得に失敗した場合はnull</returns> public virtual Bitmap DrawKeroSurface(ExplorerGhost ghost, ExplorerShell shell) { return(DrawSurfaceInternal(ghost, shell, shell.KeroSurfaceModel, shell.KeroSurfaceId)); }