From 737a28334e7b0461c39cda6e83d2cebef9b622a9 Mon Sep 17 00:00:00 2001 From: Elie Rodrigue Date: Tue, 9 May 2017 15:24:10 -0400 Subject: Game detail update --- MainForm.Designer.cs | 202 +-- MainForm.cs | 294 +--- MainForm.resx | 565 +------- Manager/GameManager.cs | 148 +- Properties/app.manifest | 106 +- UI/Components/GameDetail.Designer.cs | 8 + UI/Components/GameDetail.cs | 31 + UI/Components/GameDetail.resx | 3 + UI/Components/GameSelecter.Designer.cs | 20 +- UI/Components/GameSelecter.cs | 96 -- WorkerForm.cs | 2380 ++++++++++++++++---------------- hakchi_gui.csproj | 17 +- 12 files changed, 1526 insertions(+), 2344 deletions(-) diff --git a/MainForm.Designer.cs b/MainForm.Designer.cs index f5a02e49..1da05612 100644 --- a/MainForm.Designer.cs +++ b/MainForm.Designer.cs @@ -108,27 +108,6 @@ this.fAQToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.checkedListBoxGames = new System.Windows.Forms.CheckedListBox(); - this.groupBoxOptions = new System.Windows.Forms.GroupBox(); - this.buttonShowGameGenieDatabase = new System.Windows.Forms.Button(); - this.maskedTextBoxReleaseDate = new System.Windows.Forms.MaskedTextBox(); - this.label1 = new System.Windows.Forms.Label(); - this.textBoxGameGenie = new System.Windows.Forms.TextBox(); - this.label7 = new System.Windows.Forms.Label(); - this.label6 = new System.Windows.Forms.Label(); - this.radioButtonTwoSim = new System.Windows.Forms.RadioButton(); - this.buttonGoogle = new System.Windows.Forms.Button(); - this.buttonBrowseImage = new System.Windows.Forms.Button(); - this.pictureBoxArt = new System.Windows.Forms.PictureBox(); - this.label4 = new System.Windows.Forms.Label(); - this.textBoxArguments = new System.Windows.Forms.TextBox(); - this.label3 = new System.Windows.Forms.Label(); - this.textBoxPublisher = new System.Windows.Forms.TextBox(); - this.label2 = new System.Windows.Forms.Label(); - this.radioButtonTwo = new System.Windows.Forms.RadioButton(); - this.radioButtonOne = new System.Windows.Forms.RadioButton(); - this.textBoxName = new System.Windows.Forms.TextBox(); - this.labelName = new System.Windows.Forms.Label(); - this.labelID = new System.Windows.Forms.Label(); this.label5 = new System.Windows.Forms.Label(); this.buttonAddGames = new System.Windows.Forms.Button(); this.statusStrip = new System.Windows.Forms.StatusStrip(); @@ -145,9 +124,8 @@ this.buttonStart = new System.Windows.Forms.Button(); this.timerCalculateGames = new System.Windows.Forms.Timer(this.components); this.timerConnectionCheck = new System.Windows.Forms.Timer(this.components); + this.gameDetail1 = new com.clusterrr.hakchi_gui.UI.Components.GameDetail(); this.menuStrip.SuspendLayout(); - this.groupBoxOptions.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.pictureBoxArt)).BeginInit(); this.statusStrip.SuspendLayout(); this.contextMenuStrip.SuspendLayout(); this.SuspendLayout(); @@ -717,152 +695,6 @@ this.checkedListBoxGames.KeyDown += new System.Windows.Forms.KeyEventHandler(this.checkedListBoxGames_KeyDown); this.checkedListBoxGames.MouseDown += new System.Windows.Forms.MouseEventHandler(this.checkedListBoxGames_MouseDown); // - // groupBoxOptions - // - resources.ApplyResources(this.groupBoxOptions, "groupBoxOptions"); - this.groupBoxOptions.Controls.Add(this.buttonShowGameGenieDatabase); - this.groupBoxOptions.Controls.Add(this.maskedTextBoxReleaseDate); - this.groupBoxOptions.Controls.Add(this.label1); - this.groupBoxOptions.Controls.Add(this.textBoxGameGenie); - this.groupBoxOptions.Controls.Add(this.label7); - this.groupBoxOptions.Controls.Add(this.label6); - this.groupBoxOptions.Controls.Add(this.radioButtonTwoSim); - this.groupBoxOptions.Controls.Add(this.buttonGoogle); - this.groupBoxOptions.Controls.Add(this.buttonBrowseImage); - this.groupBoxOptions.Controls.Add(this.pictureBoxArt); - this.groupBoxOptions.Controls.Add(this.label4); - this.groupBoxOptions.Controls.Add(this.textBoxArguments); - this.groupBoxOptions.Controls.Add(this.label3); - this.groupBoxOptions.Controls.Add(this.textBoxPublisher); - this.groupBoxOptions.Controls.Add(this.label2); - this.groupBoxOptions.Controls.Add(this.radioButtonTwo); - this.groupBoxOptions.Controls.Add(this.radioButtonOne); - this.groupBoxOptions.Controls.Add(this.textBoxName); - this.groupBoxOptions.Controls.Add(this.labelName); - this.groupBoxOptions.Controls.Add(this.labelID); - this.groupBoxOptions.Name = "groupBoxOptions"; - this.groupBoxOptions.TabStop = false; - // - // buttonShowGameGenieDatabase - // - resources.ApplyResources(this.buttonShowGameGenieDatabase, "buttonShowGameGenieDatabase"); - this.buttonShowGameGenieDatabase.Name = "buttonShowGameGenieDatabase"; - this.buttonShowGameGenieDatabase.UseVisualStyleBackColor = true; - this.buttonShowGameGenieDatabase.Click += new System.EventHandler(this.buttonShowGameGenieDatabase_Click); - // - // maskedTextBoxReleaseDate - // - resources.ApplyResources(this.maskedTextBoxReleaseDate, "maskedTextBoxReleaseDate"); - this.maskedTextBoxReleaseDate.Name = "maskedTextBoxReleaseDate"; - this.maskedTextBoxReleaseDate.TextChanged += new System.EventHandler(this.maskedTextBoxReleaseDate_TextChanged); - // - // label1 - // - resources.ApplyResources(this.label1, "label1"); - this.label1.Name = "label1"; - // - // textBoxGameGenie - // - resources.ApplyResources(this.textBoxGameGenie, "textBoxGameGenie"); - this.textBoxGameGenie.Name = "textBoxGameGenie"; - this.textBoxGameGenie.TextChanged += new System.EventHandler(this.textBoxGameGenie_TextChanged); - // - // label7 - // - resources.ApplyResources(this.label7, "label7"); - this.label7.Name = "label7"; - // - // label6 - // - resources.ApplyResources(this.label6, "label6"); - this.label6.Name = "label6"; - // - // radioButtonTwoSim - // - resources.ApplyResources(this.radioButtonTwoSim, "radioButtonTwoSim"); - this.radioButtonTwoSim.Name = "radioButtonTwoSim"; - this.radioButtonTwoSim.UseVisualStyleBackColor = true; - this.radioButtonTwoSim.CheckedChanged += new System.EventHandler(this.radioButtonOne_CheckedChanged); - // - // buttonGoogle - // - resources.ApplyResources(this.buttonGoogle, "buttonGoogle"); - this.buttonGoogle.Name = "buttonGoogle"; - this.buttonGoogle.UseVisualStyleBackColor = true; - this.buttonGoogle.Click += new System.EventHandler(this.buttonGoogle_Click); - // - // buttonBrowseImage - // - resources.ApplyResources(this.buttonBrowseImage, "buttonBrowseImage"); - this.buttonBrowseImage.Name = "buttonBrowseImage"; - this.buttonBrowseImage.UseVisualStyleBackColor = true; - this.buttonBrowseImage.Click += new System.EventHandler(this.buttonBrowseImage_Click); - // - // pictureBoxArt - // - resources.ApplyResources(this.pictureBoxArt, "pictureBoxArt"); - this.pictureBoxArt.Name = "pictureBoxArt"; - this.pictureBoxArt.TabStop = false; - // - // label4 - // - resources.ApplyResources(this.label4, "label4"); - this.label4.Name = "label4"; - // - // textBoxArguments - // - resources.ApplyResources(this.textBoxArguments, "textBoxArguments"); - this.textBoxArguments.Name = "textBoxArguments"; - this.textBoxArguments.TextChanged += new System.EventHandler(this.textBoxArguments_TextChanged); - // - // label3 - // - resources.ApplyResources(this.label3, "label3"); - this.label3.Name = "label3"; - // - // textBoxPublisher - // - resources.ApplyResources(this.textBoxPublisher, "textBoxPublisher"); - this.textBoxPublisher.Name = "textBoxPublisher"; - this.textBoxPublisher.TextChanged += new System.EventHandler(this.textBoxPublisher_TextChanged); - // - // label2 - // - resources.ApplyResources(this.label2, "label2"); - this.label2.Name = "label2"; - // - // radioButtonTwo - // - resources.ApplyResources(this.radioButtonTwo, "radioButtonTwo"); - this.radioButtonTwo.Name = "radioButtonTwo"; - this.radioButtonTwo.UseVisualStyleBackColor = true; - this.radioButtonTwo.CheckedChanged += new System.EventHandler(this.radioButtonOne_CheckedChanged); - // - // radioButtonOne - // - resources.ApplyResources(this.radioButtonOne, "radioButtonOne"); - this.radioButtonOne.Checked = true; - this.radioButtonOne.Name = "radioButtonOne"; - this.radioButtonOne.TabStop = true; - this.radioButtonOne.UseVisualStyleBackColor = true; - this.radioButtonOne.CheckedChanged += new System.EventHandler(this.radioButtonOne_CheckedChanged); - // - // textBoxName - // - resources.ApplyResources(this.textBoxName, "textBoxName"); - this.textBoxName.Name = "textBoxName"; - this.textBoxName.TextChanged += new System.EventHandler(this.textBoxName_TextChanged); - // - // labelName - // - resources.ApplyResources(this.labelName, "labelName"); - this.labelName.Name = "labelName"; - // - // labelID - // - resources.ApplyResources(this.labelID, "labelID"); - this.labelID.Name = "labelID"; - // // label5 // resources.ApplyResources(this.label5, "label5"); @@ -972,18 +804,23 @@ this.timerConnectionCheck.Interval = 500; this.timerConnectionCheck.Tick += new System.EventHandler(this.timerConnectionCheck_Tick); // + // gameDetail1 + // + resources.ApplyResources(this.gameDetail1, "gameDetail1"); + this.gameDetail1.Name = "gameDetail1"; + // // MainForm // this.AllowDrop = true; resources.ApplyResources(this, "$this"); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.gameDetail1); this.Controls.Add(this.buttonStart); this.Controls.Add(this.statusStrip); this.Controls.Add(this.buttonAddGames); this.Controls.Add(this.label5); this.Controls.Add(this.checkedListBoxGames); this.Controls.Add(this.menuStrip); - this.Controls.Add(this.groupBoxOptions); this.Cursor = System.Windows.Forms.Cursors.Default; this.KeyPreview = true; this.MaximizeBox = false; @@ -996,9 +833,6 @@ this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.MainForm_KeyDown); this.menuStrip.ResumeLayout(false); this.menuStrip.PerformLayout(); - this.groupBoxOptions.ResumeLayout(false); - this.groupBoxOptions.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.pictureBoxArt)).EndInit(); this.statusStrip.ResumeLayout(false); this.statusStrip.PerformLayout(); this.contextMenuStrip.ResumeLayout(false); @@ -1013,22 +847,6 @@ private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem addMoreGamesToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem; - private System.Windows.Forms.GroupBox groupBoxOptions; - private System.Windows.Forms.RadioButton radioButtonTwo; - private System.Windows.Forms.RadioButton radioButtonOne; - private System.Windows.Forms.TextBox textBoxName; - private System.Windows.Forms.Label labelName; - private System.Windows.Forms.Label labelID; - private System.Windows.Forms.TextBox textBoxPublisher; - private System.Windows.Forms.Label label2; - private System.Windows.Forms.MaskedTextBox maskedTextBoxReleaseDate; - private System.Windows.Forms.Label label1; - private System.Windows.Forms.TextBox textBoxArguments; - private System.Windows.Forms.Label label3; - private System.Windows.Forms.PictureBox pictureBoxArt; - private System.Windows.Forms.Label label4; - private System.Windows.Forms.Button buttonGoogle; - private System.Windows.Forms.Button buttonBrowseImage; private System.Windows.Forms.Label label5; private System.Windows.Forms.Button buttonAddGames; private System.Windows.Forms.StatusStrip statusStrip; @@ -1042,8 +860,6 @@ private System.Windows.Forms.ToolStripMenuItem dumpKernelToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem flashCustomKernelToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem flashOriginalKernelToolStripMenuItem; - private System.Windows.Forms.RadioButton radioButtonTwoSim; - private System.Windows.Forms.Label label6; private System.Windows.Forms.ToolStripMenuItem settingsToolStripMenuItem; private System.Windows.Forms.Timer timerCalculateGames; private System.Windows.Forms.ToolStripMenuItem selectAllToolStripMenuItem; @@ -1060,8 +876,6 @@ private System.Windows.Forms.ToolStripSeparator toolStripMenuItem2; protected internal System.Windows.Forms.CheckedListBox checkedListBoxGames; private System.Windows.Forms.ToolStripMenuItem searchToolStripMenuItem; - private System.Windows.Forms.TextBox textBoxGameGenie; - private System.Windows.Forms.Label label7; private System.Windows.Forms.ToolStripMenuItem epilepsyProtectionToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem cloverconHackToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem resetUsingCombinationOfButtonsToolStripMenuItem; @@ -1080,7 +894,6 @@ private System.Windows.Forms.ToolStripMenuItem automaticOriginalToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem foldersSplitByFirstLetterToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem customToolStripMenuItem; - private System.Windows.Forms.Button buttonShowGameGenieDatabase; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem3; private System.Windows.Forms.ToolStripMenuItem pagesOriginalToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem foldersOriginalToolStripMenuItem; @@ -1124,6 +937,7 @@ private System.Windows.Forms.ToolStripMenuItem languageToolStripMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem8; private System.Windows.Forms.ToolStripMenuItem takeScreenshotToolStripMenuItem; + private UI.Components.GameDetail gameDetail1; } } diff --git a/MainForm.cs b/MainForm.cs index 13b604e1..1831c555 100644 --- a/MainForm.cs +++ b/MainForm.cs @@ -53,9 +53,13 @@ namespace com.clusterrr.hakchi_gui { try { + KernelDump = Path.Combine(Path.Combine(Program.BaseDirectoryExternal, "dump"), "kernel.img"); InternalMods = from m in Directory.GetFiles(Path.Combine(Program.BaseDirectoryInternal, "mods/hmods")) select Path.GetFileNameWithoutExtension(m); - LoadGames(); + Manager.GameManager.GetInstance().GamesRemoved += MainForm_GamesRemoved; ; + Manager.GameManager.GetInstance().NewGamesAdded += MainForm_NewGamesAdded; + Manager.GameManager.GetInstance().LoadLibrary(); + // LoadGames(); LoadPresets(); LoadLanguages(); @@ -105,14 +109,7 @@ namespace com.clusterrr.hakchi_gui max90toolStripMenuItem.Checked = ConfigIni.MaxGamesPerFolder == 90; max100toolStripMenuItem.Checked = ConfigIni.MaxGamesPerFolder == 100; - // Little tweak for easy translation - var tbl = textBoxName.Left; - textBoxName.Left = labelName.Left + labelName.Width; - textBoxName.Width -= textBoxName.Left - tbl; - maskedTextBoxReleaseDate.Left = label1.Left + label1.Width + 3; - tbl = textBoxPublisher.Left; - textBoxPublisher.Left = label2.Left + label2.Width; - textBoxPublisher.Width -= textBoxPublisher.Left - tbl; + // Tweeks for message boxes MessageBoxManager.Yes = MessageBoxManager.Retry = Resources.Yes; @@ -182,66 +179,32 @@ namespace com.clusterrr.hakchi_gui } } - public void LoadGames() + + + private void MainForm_NewGamesAdded(List e) { - /*Make sure its initialised*/ - Manager.GameManager.GetInstance(); - - foreach(var game in Manager.GameManager.GetInstance().getAllGames()) + foreach (var game in e.OrderBy(o=>o.Name)) { checkedListBoxGames.Items.Add(game, game.Selected); } - RecalculateSelectedGames(); + ShowStats(); ShowSelected(); } - public void ShowSelected() + private void MainForm_GamesRemoved(List e) { - var selected = checkedListBoxGames.SelectedItem; - if (selected == null) + foreach (var game in e) { - - groupBoxOptions.Visible = true; - groupBoxOptions.Enabled = false; - labelID.Text = "ID: "; - textBoxName.Text = ""; - radioButtonOne.Checked = true; - radioButtonTwo.Checked = false; - radioButtonTwoSim.Checked = false; - maskedTextBoxReleaseDate.Text = ""; - textBoxPublisher.Text = ""; - textBoxArguments.Text = ""; - pictureBoxArt.Image = null; - } - else - { - var app = selected as NesMiniApplication; - - groupBoxOptions.Visible = true; - labelID.Text = "ID: " + app.Code; - textBoxName.Text = app.Name; - if (app.Simultaneous && app.Players == 2) - radioButtonTwoSim.Checked = true; - else if (app.Players == 2) - radioButtonTwo.Checked = true; - else - radioButtonOne.Checked = true; - maskedTextBoxReleaseDate.Text = app.ReleaseDate; - textBoxPublisher.Text = app.Publisher; - if (app is NesGame) - textBoxArguments.Text = (app as NesGame).Args; - else if (app is FdsGame) - textBoxArguments.Text = (app as FdsGame).Args; - else - textBoxArguments.Text = app.Command; - if (File.Exists(app.IconPath)) - pictureBoxArt.Image = NesMiniApplication.LoadBitmap(app.IconPath); - else - pictureBoxArt.Image = null; - buttonShowGameGenieDatabase.Enabled = textBoxGameGenie.Enabled = app is NesGame; - textBoxGameGenie.Text = (app is NesGame) ? (app as NesGame).GameGenie : ""; - groupBoxOptions.Enabled = true; + checkedListBoxGames.Items.Remove(e); } + ShowStats(); + ShowSelected(); + } + + public void ShowSelected() + { + NesMiniApplication selected = (NesMiniApplication)checkedListBoxGames.SelectedItem; + gameDetail1.SetGame(selected); } @@ -350,92 +313,12 @@ namespace com.clusterrr.hakchi_gui ShowSelected(); } - void SetImageForSelectedGame(string fileName) - { - var selected = checkedListBoxGames.SelectedItem; - if (selected == null || !(selected is NesMiniApplication)) return; - var game = (selected as NesMiniApplication); - game.Image = NesMiniApplication.LoadBitmap(fileName); - ShowSelected(); - timerCalculateGames.Enabled = true; - } - - private void buttonBrowseImage_Click(object sender, EventArgs e) - { - openFileDialogImage.Filter = Resources.Images + " (*.bmp;*.png;*.jpg;*.jpeg;*.gif)|*.bmp;*.png;*.jpg;*.jpeg;*.gif|" + Resources.AllFiles + "|*.*"; - if (openFileDialogImage.ShowDialog() == System.Windows.Forms.DialogResult.OK) - { - SetImageForSelectedGame(openFileDialogImage.FileName); - } - } - private void buttonGoogle_Click(object sender, EventArgs e) - { - var selected = checkedListBoxGames.SelectedItem; - if (selected == null || !(selected is NesMiniApplication)) return; - var game = (selected as NesMiniApplication); - var googler = new ImageGooglerForm(game); - if (googler.ShowDialog() == System.Windows.Forms.DialogResult.OK) - { - game.Image = googler.Result; - ShowSelected(); - timerCalculateGames.Enabled = true; - } - } - private void textBoxName_TextChanged(object sender, EventArgs e) - { - var selected = checkedListBoxGames.SelectedItem; - if (selected == null || !(selected is NesMiniApplication)) return; - var game = (selected as NesMiniApplication); - game.Name = textBoxName.Text; - } - - private void radioButtonOne_CheckedChanged(object sender, EventArgs e) - { - var selected = checkedListBoxGames.SelectedItem; - if (selected == null || !(selected is NesMiniApplication)) return; - var game = (selected as NesMiniApplication); - game.Players = (byte)(radioButtonOne.Checked ? 1 : 2); - game.Simultaneous = radioButtonTwoSim.Checked; - } - - private void textBoxPublisher_TextChanged(object sender, EventArgs e) - { - var selected = checkedListBoxGames.SelectedItem; - if (selected == null || !(selected is NesMiniApplication)) return; - var game = (selected as NesMiniApplication); - game.Publisher = textBoxPublisher.Text.ToUpper(); - } - - private void textBoxArguments_TextChanged(object sender, EventArgs e) - { - var selected = checkedListBoxGames.SelectedItem; - if (selected == null || !(selected is NesMiniApplication)) return; - var game = (selected as NesMiniApplication); - if (game is NesGame) - (game as NesGame).Args = textBoxArguments.Text; - else if (game is FdsGame) - (game as FdsGame).Args = textBoxArguments.Text; - else - game.Command = textBoxArguments.Text; - } + - private void maskedTextBoxReleaseDate_TextChanged(object sender, EventArgs e) - { - var selected = checkedListBoxGames.SelectedItem; - if (selected == null || !(selected is NesMiniApplication)) return; - var game = (selected as NesMiniApplication); - game.ReleaseDate = maskedTextBoxReleaseDate.Text; - } - private void textBoxGameGenie_TextChanged(object sender, EventArgs e) - { - var selected = checkedListBoxGames.SelectedItem; - if (selected == null || !(selected is NesGame)) return; - var game = (selected as NesGame); - game.GameGenie = textBoxGameGenie.Text; - } + private void SaveConfig() @@ -453,21 +336,18 @@ namespace com.clusterrr.hakchi_gui } private void MainForm_FormClosed(object sender, FormClosedEventArgs e) { - Process.GetCurrentProcess().Kill(); // Suicide! Just easy and dirty way to kill all threads. + + //Process.GetCurrentProcess().Kill(); // Suicide! Just easy and dirty way to kill all threads. } - struct CountResult - { - public int Count; - public long Size; - } + void RecalculateSelectedGamesThread() { try { - var stats = RecalculateSelectedGames(); - showStats(stats); + + ShowStats(); } catch { @@ -475,38 +355,27 @@ namespace com.clusterrr.hakchi_gui timerCalculateGames.Enabled = true; } } - CountResult RecalculateSelectedGames() + void ShowStats() { - CountResult stats; - stats.Count = 0; - stats.Size = 0; - foreach (var game in checkedListBoxGames.CheckedItems) - { - if (game is NesMiniApplication) - { - stats.Count++; - stats.Size += (game as NesMiniApplication).Size(); - } - - } - return stats; + showStats(Manager.GameManager.GetInstance().GetStatistics()); } - void showStats(CountResult stats) + void showStats(Manager.GameManager.CountResult stats) { try { if (InvokeRequired) { - Invoke(new Action(showStats), new object[] { stats }); + Invoke(new Action(showStats), new object[] { stats }); return; } + var maxGamesSize = DefaultMaxGamesSize * 1024 * 1024; if (WorkerForm.NandCTotal > 0) maxGamesSize = (WorkerForm.NandCFree + WorkerForm.WritedGamesSize) - WorkerForm.ReservedMemory * 1024 * 1024; - toolStripStatusLabelSelected.Text = stats.Count + " " + Resources.GamesSelected; - toolStripStatusLabelSize.Text = string.Format("{0:F1}MB / {1:F1}MB", stats.Size / 1024.0 / 1024.0, maxGamesSize / 1024.0 / 1024.0); + toolStripStatusLabelSelected.Text = stats.SelectedCount + " " + Resources.GamesSelected; + toolStripStatusLabelSize.Text = string.Format("{0:F1}MB / {1:F1}MB", stats.SelectedSize / 1024.0 / 1024.0, maxGamesSize / 1024.0 / 1024.0); toolStripProgressBar.Maximum = (int)maxGamesSize; - toolStripProgressBar.Value = Math.Min((int)stats.Size, toolStripProgressBar.Maximum); + toolStripProgressBar.Value = Math.Min((int)stats.SelectedSize, toolStripProgressBar.Maximum); toolStripStatusLabelSize.ForeColor = (toolStripProgressBar.Value < toolStripProgressBar.Maximum) ? SystemColors.ControlText : @@ -535,16 +404,6 @@ namespace com.clusterrr.hakchi_gui } } - private void checkedListBoxDefaultGames_MouseDown(object sender, MouseEventArgs e) - { - if (e.Button == System.Windows.Forms.MouseButtons.Right) - { - var i = checkedListBoxGames.IndexFromPoint(e.X, e.Y); - selectAllToolStripMenuItem.Tag = unselectAllToolStripMenuItem.Tag = 1; - deleteGameToolStripMenuItem.Enabled = false; - contextMenuStrip.Show(sender as Control, e.X, e.Y); - } - } DialogResult RequireKernelDump() { @@ -583,8 +442,8 @@ namespace com.clusterrr.hakchi_gui { SaveConfig(); - var stats = RecalculateSelectedGames(); - if (stats.Count == 0) + var stats = Manager.GameManager.GetInstance().GetStatistics(); + if (stats.SelectedCount == 0) { MessageBox.Show(Resources.SelectAtLeast, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error); return; @@ -658,8 +517,7 @@ namespace com.clusterrr.hakchi_gui void AddGames(IEnumerable files) { - SaveConfig(); - ICollection addedApps; + List addedApps; var workerForm = new WorkerForm(); workerForm.Text = Resources.LoadingGames; workerForm.Task = WorkerForm.Tasks.AddGames; @@ -667,47 +525,12 @@ namespace com.clusterrr.hakchi_gui workerForm.Start(); addedApps = workerForm.addedApplications; - if (addedApps != null) - { - // Add games, only new ones - var newApps = addedApps.Distinct(new NesMiniApplication.NesMiniAppEqualityComparer()); - var newCodes = from app in newApps select app.Code; - var oldAppsReplaced = from app in checkedListBoxGames.Items.Cast().ToArray() - where (app is NesMiniApplication) && newCodes.Contains((app as NesMiniApplication).Code) - select app; - foreach (var replaced in oldAppsReplaced) - checkedListBoxGames.Items.Remove(replaced); - checkedListBoxGames.Items.AddRange(newApps.ToArray()); - var first = checkedListBoxGames.Items[0]; - bool originalChecked = (checkedListBoxGames.CheckedItems.Contains(first)); - checkedListBoxGames.Items.Remove(first); - checkedListBoxGames.Sorted = true; - checkedListBoxGames.Sorted = false; - checkedListBoxGames.Items.Insert(0, first); - checkedListBoxGames.SetItemChecked(0, originalChecked); - } - else - { - // Reload all games (maybe process was terminated?) - LoadGames(); - } - if (addedApps != null) // if added only one game select it + foreach (NesMiniApplication g in addedApps) { - bool first = true; - foreach (var addedApp in addedApps) - { - for (int i = 0; i < checkedListBoxGames.Items.Count; i++) - if ((checkedListBoxGames.Items[i] is NesMiniApplication) && - (checkedListBoxGames.Items[i] as NesMiniApplication).Code == addedApp.Code) - { - if (first) - checkedListBoxGames.SelectedIndex = i; - first = false; - checkedListBoxGames.SetItemChecked(i, true); - break; - } - } + g.Selected = true; } + Manager.GameManager.GetInstance().AddGames(addedApps); + // Schedule recalculation timerCalculateGames.Enabled = false; timerCalculateGames.Enabled = true; @@ -771,9 +594,9 @@ namespace com.clusterrr.hakchi_gui workerForm.Text = Resources.DownloadAllCoversTitle; workerForm.Task = WorkerForm.Tasks.DownloadAllCovers; workerForm.Games = new NesMenuCollection(); - foreach (var game in checkedListBoxGames.Items) + foreach (var game in Manager.GameManager.GetInstance().getAllGames()) { - if (game is NesMiniApplication) + if (game.GetType() != typeof(NesDefaultGame)) workerForm.Games.Add(game as NesMiniApplication); } return workerForm.Start() == DialogResult.OK; @@ -968,8 +791,7 @@ namespace com.clusterrr.hakchi_gui var game = checkedListBoxGames.Items[pos] as NesMiniApplication; if (MessageBox.Show(this, string.Format(Resources.DeleteGame, game.Name), Resources.AreYouSure, MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == System.Windows.Forms.DialogResult.Yes) { - Directory.Delete(game.GamePath, true); - checkedListBoxGames.Items.RemoveAt(pos); + Manager.GameManager.GetInstance().DeleteGames(new List() { game }); } } catch (Exception ex) @@ -985,8 +807,14 @@ namespace com.clusterrr.hakchi_gui private void selectAllToolStripMenuItem_Click(object sender, EventArgs e) { if ((int)(sender as ToolStripMenuItem).Tag == 0) + { for (int i = 0; i < checkedListBoxGames.Items.Count; i++) + { checkedListBoxGames.SetItemChecked(i, true); + var game = checkedListBoxGames.Items[i] as NesMiniApplication; + game.Selected = true; + } + } } @@ -994,7 +822,11 @@ namespace com.clusterrr.hakchi_gui { if ((int)(sender as ToolStripMenuItem).Tag == 0) for (int i = 0; i < checkedListBoxGames.Items.Count; i++) + { checkedListBoxGames.SetItemChecked(i, false); + var game = checkedListBoxGames.Items[i] as NesMiniApplication; + game.Selected = false; + } } @@ -1015,7 +847,8 @@ namespace com.clusterrr.hakchi_gui var ext = Path.GetExtension(files[0]).ToLower(); if (ext == ".jpg" || ext == ".png") { - SetImageForSelectedGame(files[0]); + gameDetail1.SetImageForSelectedGame(files[0]); + return; } } @@ -1127,14 +960,7 @@ namespace com.clusterrr.hakchi_gui ConfigIni.Compress = compressGamesIfPossibleToolStripMenuItem.Checked; } - private void buttonShowGameGenieDatabase_Click(object sender, EventArgs e) - { - if (!(checkedListBoxGames.SelectedItem is NesGame)) return; - NesGame nesGame = checkedListBoxGames.SelectedItem as NesGame; - GameGenieCodeForm lFrm = new GameGenieCodeForm(nesGame); - if (lFrm.ShowDialog() == System.Windows.Forms.DialogResult.OK) - textBoxGameGenie.Text = nesGame.GameGenie; - } + private void pagesModefoldersToolStripMenuItem_Click(object sender, EventArgs e) { diff --git a/MainForm.resx b/MainForm.resx index b78b2cf2..2806eae8 100644 --- a/MainForm.resx +++ b/MainForm.resx @@ -144,7 +144,7 @@ $this - 6 + 7 37, 20 @@ -645,542 +645,8 @@ $this - 5 - - - None - - - 252, 286 - - - 25, 20 - - - 12 - - - + - - - buttonShowGameGenieDatabase - - - System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - groupBoxOptions - - - 0 - - - 210, 154 - - - 0000-00-00 - - - 65, 20 - - - 6 - - - maskedTextBoxReleaseDate - - - System.Windows.Forms.MaskedTextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - groupBoxOptions - - - 1 - - - True - - - 15, 157 - - - 150, 13 - - - 5 - - - Release date (YYYY-MM-DD): - - - label1 - - - System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - groupBoxOptions - - - 2 - - - 19, 286 - - - 227, 20 - - - 11 - - - textBoxGameGenie - - - System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - groupBoxOptions - - - 3 - - - True - - - NoControl - - - 16, 269 - - - 194, 13 - - - 17 - - - Game Genie codes (comma separated): - - - label7 - - - System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - groupBoxOptions - - - 4 - - - 15, 76 - - - 82, 63 - - - 16 - - - Max players: - - - MiddleLeft - - - label6 - - - System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - groupBoxOptions - - - 5 - - - True - - - NoControl - - - 103, 122 - - - 156, 17 - - - 5 - - - Two players, simultaneously - - - radioButtonTwoSim - - - System.Windows.Forms.RadioButton, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - groupBoxOptions - - 6 - - 219, 427 - - - 61, 23 - - - 15 - - - Google - - - buttonGoogle - - - System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - groupBoxOptions - - - 7 - - - 219, 384 - - - 61, 23 - - - 13 - - - Browse - - - buttonBrowseImage - - - System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - groupBoxOptions - - - 8 - - - 71, 316 - - - 140, 204 - - - Zoom - - - 12 - - - pictureBoxArt - - - System.Windows.Forms.PictureBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - groupBoxOptions - - - 9 - - - 15, 316 - - - 53, 204 - - - 11 - - - Box art: - - - MiddleCenter - - - label4 - - - System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - groupBoxOptions - - - 10 - - - 18, 237 - - - 257, 20 - - - 10 - - - textBoxArguments - - - System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - groupBoxOptions - - - 11 - - - True - - - 15, 220 - - - 253, 13 - - - 9 - - - Command line arguments (for advanced users only!): - - - label3 - - - System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - groupBoxOptions - - - 12 - - - 71, 186 - - - 204, 20 - - - 8 - - - textBoxPublisher - - - System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - groupBoxOptions - - - 13 - - - True - - - 15, 189 - - - 53, 13 - - - 7 - - - Publisher: - - - label2 - - - System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - groupBoxOptions - - - 14 - - - True - - - 103, 99 - - - 174, 17 - - - 4 - - - Two players, not simultaneously - - - radioButtonTwo - - - System.Windows.Forms.RadioButton, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - groupBoxOptions - - - 15 - - - True - - - 103, 76 - - - 76, 17 - - - 3 - - - One player - - - radioButtonOne - - - System.Windows.Forms.RadioButton, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - groupBoxOptions - - - 16 - - - 59, 46 - - - 216, 20 - - - 2 - - - textBoxName - - - System.Windows.Forms.TextBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - groupBoxOptions - - - 17 - - - True - - - 15, 49 - - - 38, 13 - - - 1 - - - Name: - - - labelName - - - System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - groupBoxOptions - - - 18 - - - True - - - 15, 21 - - - 21, 13 - - - 0 - - - ID: - - - labelID - - - System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - groupBoxOptions - - - 19 - - - 306, 33 - - - 293, 529 - - - 4 - - - Game options - - - groupBoxOptions - - - System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 7 - None @@ -1209,7 +675,7 @@ $this - 4 + 5 None @@ -1236,7 +702,7 @@ $this - 3 + 4 109, 17 @@ -1263,7 +729,7 @@ $this - 2 + 3 False @@ -1356,7 +822,7 @@ $this - 1 + 2 473, 17 @@ -1364,6 +830,27 @@ 775, 17 + + 306, 18 + + + 299, 538 + + + 7 + + + gameDetail1 + + + com.clusterrr.hakchi_gui.UI.Components.GameDetail, hakchi, Version=2.0.17.14, Culture=neutral, PublicKeyToken=null + + + $this + + + 0 + True diff --git a/Manager/GameManager.cs b/Manager/GameManager.cs index 28183241..e7bff70a 100644 --- a/Manager/GameManager.cs +++ b/Manager/GameManager.cs @@ -10,6 +10,16 @@ namespace com.clusterrr.hakchi_gui.Manager { public class GameManager { + public struct CountResult + { + public int SelectedCount; + public long SelectedSize; + public int TotalCount; + public long TotalSize; + } + public delegate void GameListEventHandler(List e); + public event GameListEventHandler NewGamesAdded; + public event GameListEventHandler GamesRemoved; static NesDefaultGame[] defaultNesGames = new NesDefaultGame[] { new NesDefaultGame { Code = "CLV-P-NAAAE", Name = "Super Mario Bros.", Size = 571031 }, new NesDefaultGame { Code = "CLV-P-NAACE", Name = "Super Mario Bros. 3", Size = 1163285 }, @@ -87,12 +97,94 @@ namespace com.clusterrr.hakchi_gui.Manager private List gameLibrary = new List(); private GameManager() { - LoadLibrary(); + + } + public CountResult GetStatistics() + { + CountResult stats = new CountResult(); + stats.SelectedCount = 0; + stats.SelectedSize = 0; + stats.TotalCount = 0; + stats.TotalSize = 0; + + foreach (var game in gameLibrary) + { + if (game is NesMiniApplication) + { + long size = game.Size(); + stats.TotalSize += size; + stats.TotalCount++; + if(game.Selected) + { + stats.SelectedCount++; + stats.SelectedSize += size; + } + + } + + } + return stats; } public IOrderedEnumerable getAllGames() { return gameLibrary.OrderBy(o => o.Name); } + public void DeleteGames(List toDelete) + { + foreach(NesMiniApplication game in toDelete) + { + try + { + + Directory.Delete(game.GamePath, true); + gameLibrary.Remove(game); + AppTypeCollection.AppInfo inf = AppTypeCollection.GetAppByClass(game.GetType()); + if (systemClassifiedGames.ContainsKey(inf)) + { + systemClassifiedGames[inf].Remove(game); + } + + + } + catch (Exception ex) + { + Debug.WriteLine(ex.Message + ex.StackTrace); + MessageBox.Show(ex.Message, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + if (GamesRemoved != null) + { + if (toDelete.Count() > 0) + { + GamesRemoved(toDelete); + } + } + } + public void AddGames(List newGames) + { + List reallyAdded = new List(); + foreach(NesMiniApplication g in newGames) + { + if (!gameLibrary.Contains(g)) + { + gameLibrary.Add(g); + reallyAdded.Add(g); + AppTypeCollection.AppInfo inf = AppTypeCollection.GetAppByClass(g.GetType()); + if (!systemClassifiedGames.ContainsKey(inf)) + { + systemClassifiedGames[inf] = new List(); + } + systemClassifiedGames[inf].Add(g); + } + } + if (NewGamesAdded != null) + { + if (reallyAdded.Count() > 0) + { + NewGamesAdded(reallyAdded); + } + } + } public void SaveChanges() { var selected = new List(); @@ -124,7 +216,7 @@ namespace com.clusterrr.hakchi_gui.Manager public void ReloadDefault() { string[] selectedGames = ConfigIni.SelectedGames.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); - List toremove = new List(); + List toremove = new List(); foreach (var game in gameLibrary) { if (game.GetType() == typeof(NesDefaultGame)) @@ -136,29 +228,39 @@ namespace com.clusterrr.hakchi_gui.Manager { gameLibrary.Remove(tr); } + if(GamesRemoved!=null) + { + if(toremove.Count() > 0) + { + GamesRemoved(toremove); + } + } AppTypeCollection.AppInfo inf = AppTypeCollection.GetAppByClass(typeof(NesDefaultGame)); if (systemClassifiedGames.ContainsKey(inf)) { systemClassifiedGames[inf].Clear(); } - + List toAdd = new List(); foreach (var game in new List(ConfigIni.ConsoleType == 0 ? defaultNesGames : defaultFamicomGames).OrderBy(o => o.Name)) { game.Selected = selectedGames.Contains(game.Code); - - gameLibrary.Add(game); - - AppTypeCollection.AppInfo inf2 = AppTypeCollection.GetAppByClass(game.GetType()); - if (!systemClassifiedGames.ContainsKey(inf2)) - { - systemClassifiedGames[inf2] = new List(); - } - systemClassifiedGames[inf2].Add(game); + toAdd.Add(game); + } + if(toAdd.Count()>0) + { + AddGames(toAdd); } + } - private void LoadLibrary() + public void LoadLibrary() { - + if (GamesRemoved != null) + { + if (gameLibrary.Count() > 0) + { + GamesRemoved(gameLibrary); + } + } gameLibrary.Clear(); ReloadDefault(); string[] selectedGames = ConfigIni.SelectedGames.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); @@ -167,7 +269,7 @@ namespace com.clusterrr.hakchi_gui.Manager Directory.CreateDirectory(NesMiniApplication.GamesDirectory); } var gameDirs = Directory.GetDirectories(NesMiniApplication.GamesDirectory); - + List toAdd = new List(); foreach (var gameDir in gameDirs) { try @@ -177,15 +279,8 @@ namespace com.clusterrr.hakchi_gui.Manager { var game = NesMiniApplication.FromDirectory(gameDir); game.Selected = selectedGames.Contains(game.Code); - - gameLibrary.Add(game); - - AppTypeCollection.AppInfo inf = AppTypeCollection.GetAppByClass(game.GetType()); - if(!systemClassifiedGames.ContainsKey(inf)) - { - systemClassifiedGames[inf] = new List(); - } - systemClassifiedGames[inf].Add(game); + toAdd.Add(game); + } catch (FileNotFoundException ex) // Remove bad directories if any { @@ -200,6 +295,11 @@ namespace com.clusterrr.hakchi_gui.Manager continue; } } + if(toAdd.Count() >0) + { + AddGames(toAdd); + } + } } } diff --git a/Properties/app.manifest b/Properties/app.manifest index c18985e1..7776278c 100644 --- a/Properties/app.manifest +++ b/Properties/app.manifest @@ -1,54 +1,54 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/UI/Components/GameDetail.Designer.cs b/UI/Components/GameDetail.Designer.cs index 7f46a324..860fdb1e 100644 --- a/UI/Components/GameDetail.Designer.cs +++ b/UI/Components/GameDetail.Designer.cs @@ -49,6 +49,7 @@ this.textBoxName = new System.Windows.Forms.TextBox(); this.labelName = new System.Windows.Forms.Label(); this.labelID = new System.Windows.Forms.Label(); + this.openFileDialogImage = new System.Windows.Forms.OpenFileDialog(); this.groupBoxOptions.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.pictureBoxArt)).BeginInit(); this.SuspendLayout(); @@ -161,6 +162,7 @@ this.buttonGoogle.TabIndex = 15; this.buttonGoogle.Text = "Google"; this.buttonGoogle.UseVisualStyleBackColor = true; + this.buttonGoogle.Click += new System.EventHandler(this.buttonGoogle_Click); // // buttonBrowseImage // @@ -171,6 +173,7 @@ this.buttonBrowseImage.TabIndex = 13; this.buttonBrowseImage.Text = "Browse"; this.buttonBrowseImage.UseVisualStyleBackColor = true; + this.buttonBrowseImage.Click += new System.EventHandler(this.buttonBrowseImage_Click); // // pictureBoxArt // @@ -281,6 +284,10 @@ this.labelID.TabIndex = 0; this.labelID.Text = "ID:"; // + // openFileDialogImage + // + this.openFileDialogImage.FileName = "openFileDialog1"; + // // GameDetail // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -318,5 +325,6 @@ private System.Windows.Forms.TextBox textBoxName; private System.Windows.Forms.Label labelName; private System.Windows.Forms.Label labelID; + private System.Windows.Forms.OpenFileDialog openFileDialogImage; } } diff --git a/UI/Components/GameDetail.cs b/UI/Components/GameDetail.cs index ad278430..397f37b6 100644 --- a/UI/Components/GameDetail.cs +++ b/UI/Components/GameDetail.cs @@ -138,5 +138,36 @@ namespace com.clusterrr.hakchi_gui.UI.Components if (lFrm.ShowDialog() == System.Windows.Forms.DialogResult.OK) textBoxGameGenie.Text = nesGame.GameGenie; } + + public void SetImageForSelectedGame(string fileName) + { + + if (currentApp == null || !(currentApp is NesMiniApplication)) return; + var game = (currentApp as NesMiniApplication); + game.Image = NesMiniApplication.LoadBitmap(fileName); + SetGame(game); + + } + + private void buttonBrowseImage_Click(object sender, EventArgs e) + { + openFileDialogImage.Filter = Properties.Resources.Images + " (*.bmp;*.png;*.jpg;*.jpeg;*.gif)|*.bmp;*.png;*.jpg;*.jpeg;*.gif|" + Properties.Resources.AllFiles + "|*.*"; + if (openFileDialogImage.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + SetImageForSelectedGame(openFileDialogImage.FileName); + } + } + + private void buttonGoogle_Click(object sender, EventArgs e) + { + if (currentApp == null || !(currentApp is NesMiniApplication)) return; + var game = (currentApp as NesMiniApplication); + var googler = new ImageGooglerForm(game); + if (googler.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + game.Image = googler.Result; + SetGame(game); + } + } } } diff --git a/UI/Components/GameDetail.resx b/UI/Components/GameDetail.resx index 7080a7d1..14700763 100644 --- a/UI/Components/GameDetail.resx +++ b/UI/Components/GameDetail.resx @@ -117,4 +117,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 17, 17 + \ No newline at end of file diff --git a/UI/Components/GameSelecter.Designer.cs b/UI/Components/GameSelecter.Designer.cs index d10f7a79..9d2b7f17 100644 --- a/UI/Components/GameSelecter.Designer.cs +++ b/UI/Components/GameSelecter.Designer.cs @@ -28,23 +28,23 @@ /// private void InitializeComponent() { - this.tvGameSelecter = new System.Windows.Forms.TreeView(); + this.checkedListBox1 = new System.Windows.Forms.CheckedListBox(); this.SuspendLayout(); // - // tvGameSelecter + // checkedListBox1 // - this.tvGameSelecter.CheckBoxes = true; - this.tvGameSelecter.Dock = System.Windows.Forms.DockStyle.Fill; - this.tvGameSelecter.Location = new System.Drawing.Point(0, 0); - this.tvGameSelecter.Name = "tvGameSelecter"; - this.tvGameSelecter.Size = new System.Drawing.Size(495, 607); - this.tvGameSelecter.TabIndex = 0; + this.checkedListBox1.Dock = System.Windows.Forms.DockStyle.Fill; + this.checkedListBox1.FormattingEnabled = true; + this.checkedListBox1.Location = new System.Drawing.Point(0, 0); + this.checkedListBox1.Name = "checkedListBox1"; + this.checkedListBox1.Size = new System.Drawing.Size(495, 607); + this.checkedListBox1.TabIndex = 0; // // GameSelecter // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.Controls.Add(this.tvGameSelecter); + this.Controls.Add(this.checkedListBox1); this.Name = "GameSelecter"; this.Size = new System.Drawing.Size(495, 607); this.ResumeLayout(false); @@ -53,6 +53,6 @@ #endregion - private System.Windows.Forms.TreeView tvGameSelecter; + private System.Windows.Forms.CheckedListBox checkedListBox1; } } diff --git a/UI/Components/GameSelecter.cs b/UI/Components/GameSelecter.cs index 749dfde6..605ad074 100644 --- a/UI/Components/GameSelecter.cs +++ b/UI/Components/GameSelecter.cs @@ -18,102 +18,6 @@ namespace com.clusterrr.hakchi_gui.UI.Components InitializeComponent(); } - public void RefreshGames() - { - string[] selectedGames = ConfigIni.SelectedGames.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); - if(!System.IO.Directory.Exists(NesMiniApplication.GamesDirectory)) - { - Directory.CreateDirectory(NesMiniApplication.GamesDirectory); - } - var gameDirs = Directory.GetDirectories(NesMiniApplication.GamesDirectory); - var games = new List(); - foreach (var gameDir in gameDirs) - { - try - { - // Removing empty directories without errors - try - { - var game = NesMiniApplication.FromDirectory(gameDir); - games.Add(game); - } - catch (FileNotFoundException ex) // Remove bad directories if any - { - Debug.WriteLine(ex.Message + ex.StackTrace); - Directory.Delete(gameDir, true); - } - } - catch (Exception ex) - { - Debug.WriteLine(ex.Message + ex.StackTrace); - MessageBox.Show(this, ex.Message, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error); - continue; - } - } - var gamesSorted = games.OrderBy(o => o.Name); - - tvGameSelecter.Nodes.Clear(); - TreeNode tn = new TreeNode(Resources.Default30games); - tn.Checked = selectedGames.Contains("default"); - tvGameSelecter.Nodes.Add(tn); - Dictionary consolesTreeNode = new Dictionary(); - foreach (var game in gamesSorted) - { - if(!consolesTreeNode.ContainsKey(AppTypeCollection.GetAppByClass(game.GetType()).SystemName)) - { - AppTypeCollection.AppInfo inf = AppTypeCollection.GetAppByClass(game.GetType()); - TreeNode systemNode = new TreeNode(inf.SystemName); - systemNode.Tag = inf; - consolesTreeNode[inf.SystemName]= systemNode; - tvGameSelecter.Nodes.Add(systemNode); - - } - TreeNode gameNode = new TreeNode(game.Name); - gameNode.Tag = game; - gameNode.Checked = selectedGames.Contains(game.Code); - consolesTreeNode[AppTypeCollection.GetAppByClass(game.GetType()).SystemName].Nodes.Add(gameNode); - } - // RecalculateSelectedGames(); - // ShowSelected(); - } - /* Debug.WriteLine("Loading games"); - var selected = ConfigIni.SelectedGames.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); - Directory.CreateDirectory(NesMiniApplication.GamesDirectory); - var gameDirs = Directory.GetDirectories(NesMiniApplication.GamesDirectory); - var games = new List(); - foreach (var gameDir in gameDirs) - { - try - { - // Removing empty directories without errors - try - { - var game = NesMiniApplication.FromDirectory(gameDir); - games.Add(game); - } - catch (FileNotFoundException ex) // Remove bad directories if any - { - Debug.WriteLine(ex.Message + ex.StackTrace); - Directory.Delete(gameDir, true); - } - } - catch (Exception ex) - { - Debug.WriteLine(ex.Message + ex.StackTrace); - MessageBox.Show(this, ex.Message, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error); - continue; - } - } - - var gamesSorted = games.OrderBy(o => o.Name); - checkedListBoxGames.Items.Clear(); - checkedListBoxGames.Items.Add(Resources.Default30games, selected.Contains("default")); - foreach (var game in gamesSorted) - { - checkedListBoxGames.Items.Add(game, selected.Contains(game.Code)); - } - RecalculateSelectedGames(); - ShowSelected();*/ } } diff --git a/WorkerForm.cs b/WorkerForm.cs index de17240f..b0a64f13 100644 --- a/WorkerForm.cs +++ b/WorkerForm.cs @@ -1,1190 +1,1190 @@ -using com.clusterrr.Famicom; -using com.clusterrr.FelLib; -using com.clusterrr.hakchi_gui.Properties; -using com.clusterrr.util; -using SevenZip; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Drawing; -using System.Drawing.Imaging; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using System.Windows.Forms; - -namespace com.clusterrr.hakchi_gui -{ - public partial class WorkerForm : Form - { - public enum Tasks { DumpKernel, FlashKernel, Memboot, UploadGames, DownloadAllCovers, AddGames }; - public Tasks Task; - //public string UBootDump; - public string KernelDump; - public string Mod = null; - public Dictionary Config = null; - public NesMenuCollection Games; - public IEnumerable hmodsInstall; - public IEnumerable hmodsUninstall; - public IEnumerable GamesToAdd; - public NesMenuCollection.SplitStyle FoldersMode = NesMenuCollection.SplitStyle.Auto; - public int MaxGamesPerFolder = 35; - public MainForm MainForm; - Thread thread = null; - Fel fel = null; - - const UInt16 vid = 0x1F3A; - const UInt16 pid = 0xEFE8; - - readonly string baseDirectoryInternal; - readonly string baseDirectoryExternal; - readonly string fes1Path; - readonly string ubootPath; - readonly string tempDirectory; - readonly string kernelDirectory; - readonly string initramfs_cpio; - readonly string initramfs_cpioPatched; - readonly string ramfsDirectory; - readonly string hakchiDirectory; - readonly string modsDirectory; - readonly string[] hmodDirectories; - readonly string toolsDirectory; - readonly string kernelPatched; - readonly string ramdiskPatched; - readonly string tempHmodsDirectory; - readonly string cloverconDriverPath; - readonly string argumentsFilePath; - readonly string transferDirectory; - string tempGamesDirectory; - //string originalGamesConfigDirectory; - //string hiddenPath; - string[] correctKernels; - const long maxCompressedsRamfsSize = 30 * 1024 * 1024; - string selectedFile = null; - public NesMiniApplication[] addedApplications; - public static int NandCTotal, NandCUsed, NandCFree, WritedGamesSize, SaveStatesSize; - public const long ReservedMemory = 10; - - public WorkerForm() - { - InitializeComponent(); - DialogResult = DialogResult.None; - baseDirectoryInternal = Program.BaseDirectoryInternal; - baseDirectoryExternal = Program.BaseDirectoryExternal; - fes1Path = Path.Combine(Path.Combine(baseDirectoryInternal, "data"), "fes1.bin"); - ubootPath = Path.Combine(Path.Combine(baseDirectoryInternal, "data"), "uboot.bin"); -#if DEBUG - tempDirectory = Path.Combine(baseDirectoryInternal, "temp"); -#else - tempDirectory = Path.Combine(Path.GetTempPath(), "hakchi-temp"); -#endif - kernelDirectory = Path.Combine(tempDirectory, "kernel"); - initramfs_cpio = Path.Combine(kernelDirectory, "initramfs.cpio"); - initramfs_cpioPatched = Path.Combine(kernelDirectory, "initramfs_mod.cpio"); - ramfsDirectory = Path.Combine(kernelDirectory, "initramfs"); - hakchiDirectory = Path.Combine(ramfsDirectory, "hakchi"); - modsDirectory = Path.Combine(baseDirectoryInternal, "mods"); - hmodDirectories = new string[]{ - Path.Combine(baseDirectoryExternal, "user_mods"), - Path.Combine(modsDirectory, "hmods") - }; - toolsDirectory = Path.Combine(baseDirectoryInternal, "tools"); - kernelPatched = Path.Combine(kernelDirectory, "patched_kernel.img"); - ramdiskPatched = Path.Combine(kernelDirectory, "kernel.img-ramdisk_mod.gz"); - cloverconDriverPath = Path.Combine(hakchiDirectory, "clovercon.ko"); - argumentsFilePath = Path.Combine(hakchiDirectory, "extra_args"); - transferDirectory = Path.Combine(hakchiDirectory, "transfer"); - tempHmodsDirectory = Path.Combine(transferDirectory, "hmod"); - correctKernels - = new string[] { - "5cfdca351484e7025648abc3b20032ff", "07bfb800beba6ef619c29990d14b5158", // NES Mini - "ac8144c3ea4ab32e017648ee80bdc230" // Famicom Mini - }; - } - - public DialogResult Start() - { - SetProgress(0, 1); - thread = new Thread(StartThread); - thread.IsBackground = true; - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - return ShowDialog(); - } - - DialogResult WaitForFelFromThread() - { - if (InvokeRequired) - { - return (DialogResult)Invoke(new Func(WaitForFelFromThread)); - } - SetStatus(Resources.WaitingForDevice); - if (fel != null) - fel.Close(); - TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Paused); - var result = WaitingFelForm.WaitForDevice(vid, pid, this); - if (result) - { - fel = new Fel(); - if (!File.Exists(fes1Path)) throw new FileNotFoundException(fes1Path + " not found"); - if (!File.Exists(ubootPath)) throw new FileNotFoundException(ubootPath + " not found"); - fel.Fes1Bin = File.ReadAllBytes(fes1Path); - fel.UBootBin = File.ReadAllBytes(ubootPath); - fel.Open(vid, pid); - SetStatus(Resources.UploadingFes1); - fel.InitDram(true); - TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Normal); - return DialogResult.OK; - } - TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Normal); - return DialogResult.Abort; - } - DialogResult WaitForClovershellFromThread() - { - if (InvokeRequired) - { - return (DialogResult)Invoke(new Func(WaitForClovershellFromThread)); - } - SetStatus(Resources.WaitingForDevice); - TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Paused); - var result = WaitingClovershellForm.WaitForDevice(this); - TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Normal); - if (result) - return DialogResult.OK; - else return DialogResult.Abort; - } - - private delegate DialogResult MessageBoxFromThreadDelegate(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, - MessageBoxIcon icon, MessageBoxDefaultButton defaultButton, bool tweak); - DialogResult MessageBoxFromThread(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, - MessageBoxIcon icon, MessageBoxDefaultButton defaultButton, bool tweak) - { - if (InvokeRequired) - { - return (DialogResult)Invoke(new MessageBoxFromThreadDelegate(MessageBoxFromThread), - new object[] { owner, text, caption, buttons, icon, defaultButton, tweak }); - } - TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Paused); - if (tweak) MessageBoxManager.Register(); // Tweak button names - var result = MessageBox.Show(owner, text, caption, buttons, icon, defaultButton); - if (tweak) MessageBoxManager.Unregister(); - TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Normal); - return result; - } - - DialogResult FoldersManagerFromThread(NesMenuCollection collection) - { - if (InvokeRequired) - { - return (DialogResult)Invoke(new Func(FoldersManagerFromThread), new object[] { collection }); - } - var constructor = new FoldersManagerForm(collection, MainForm); - TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Paused); - var result = constructor.ShowDialog(); - TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Normal); - return result; - } - - DialogResult SelectFileFromThread(string[] files) - { - if (InvokeRequired) - { - return (DialogResult)Invoke(new Func(SelectFileFromThread), new object[] { files }); - } - var form = new SelectFileForm(files); - TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Paused); - var result = form.ShowDialog(); - TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Normal); - if (form.listBoxFiles.SelectedItem != null) - selectedFile = form.listBoxFiles.SelectedItem.ToString(); - else - selectedFile = null; - TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Normal); - return result; - } - - public void StartThread() - { - Thread.CurrentThread.CurrentUICulture = new CultureInfo(ConfigIni.Language); - SetProgress(0, 1); - try - { - DialogResult = DialogResult.None; - Debug.WriteLine("Executing task: " + Task.ToString()); - switch (Task) - { - case Tasks.DumpKernel: - DoKernelDump(); - break; - case Tasks.FlashKernel: - FlashKernel(); - break; - case Tasks.UploadGames: - UploadGames(); - break; - case Tasks.Memboot: - Memboot(); - break; - case Tasks.DownloadAllCovers: - DownloadAllCovers(); - break; - case Tasks.AddGames: - AddGames(GamesToAdd); - break; - } - if (DialogResult == DialogResult.None) - DialogResult = DialogResult.OK; - } - catch (ThreadAbortException) { } - catch (Exception ex) - { - ShowError(ex); - } - finally - { - thread = null; - if (fel != null) - { - fel.Close(); - fel = null; - } - GC.Collect(); - } - } - - void SetStatus(string status) - { - if (Disposing) return; - try - { - if (InvokeRequired) - { - Invoke(new Action(SetStatus), new object[] { status }); - return; - } - labelStatus.Text = status; - } - catch { } - } - - void SetProgress(int value, int max) - { - if (Disposing) return; - try - { - if (InvokeRequired) - { - Invoke(new Action(SetProgress), new object[] { value, max }); - if (value == max) - Thread.Sleep(1000); - return; - } - if (value > max) value = max; - progressBar.Maximum = max; - progressBar.Value = value; - TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Normal); - TaskbarProgress.SetValue(this, value, max); - } - catch { } - } - - void ShowError(Exception ex, bool dontStop = false) - { - if (Disposing) return; - try - { - if (InvokeRequired) - { - Invoke(new Action(ShowError), new object[] { ex, dontStop }); - return; - } - TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Error); - var message = ex.Message; -#if DEBUG - message += ex.StackTrace; -#endif - Debug.WriteLine(ex.Message + ex.StackTrace); - //if (ex is MadWizard.WinUSBNet.USBException) // TODO - // MessageBox.Show(this, message + "\r\n" + Resources.PleaseTryAgainUSB, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error); - //else - MessageBox.Show(this, message, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error); - TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Normal); - if (!dontStop) - { - thread = null; - Close(); - } - } - catch { } - } - - void ShowMessage(string text, string title) - { - if (Disposing) return; - try - { - if (InvokeRequired) - { - Invoke(new Action(ShowMessage), new object[] { text, title }); - return; - } - TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Paused); - MessageBox.Show(this, text, title, MessageBoxButtons.OK, MessageBoxIcon.Information); - TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Normal); - } - catch { } - } - - public void DoKernelDump() - { - int progress = 0; - const int maxProgress = 80; - if (WaitForFelFromThread() != DialogResult.OK) - { - DialogResult = DialogResult.Abort; - return; - } - progress += 5; - SetProgress(progress, maxProgress); - - SetStatus(Resources.DumpingKernel); - var kernel = fel.ReadFlash(Fel.kernel_base_f, Fel.sector_size * 0x20, - delegate(Fel.CurrentAction action, string command) - { - switch (action) - { - case Fel.CurrentAction.RunningCommand: - SetStatus(Resources.ExecutingCommand + " " + command); - break; - case Fel.CurrentAction.ReadingMemory: - SetStatus(Resources.DumpingKernel); - break; - } - progress++; - SetProgress(progress, maxProgress); - } - ); - - var size = CalcKernelSize(kernel); - if (size == 0 || size > Fel.kernel_max_size) - throw new Exception(Resources.InvalidKernelSize + " " + size); - if (kernel.Length > size) - { - var sm_kernel = new byte[size]; - Array.Copy(kernel, 0, sm_kernel, 0, size); - kernel = sm_kernel; - } - - SetProgress(maxProgress, maxProgress); - SetStatus(Resources.Done); - - var md5 = System.Security.Cryptography.MD5.Create(); - var hash = BitConverter.ToString(md5.ComputeHash(kernel)).Replace("-", "").ToLower(); - if (!correctKernels.Contains(hash)) - { - if (MessageBoxFromThread(this, Resources.MD5Failed + " " + hash + "\r\n" + Resources.MD5Failed2 + - "\r\n" + Resources.DoYouWantToContinue, Resources.Warning, MessageBoxButtons.YesNo, - MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1, false) - == DialogResult.No) - { - DialogResult = DialogResult.Abort; - return; - } - } - - Directory.CreateDirectory(Path.GetDirectoryName(KernelDump)); - File.WriteAllBytes(KernelDump, kernel); - } - - public void FlashKernel() - { - int progress = 0; - int maxProgress = 115 + (string.IsNullOrEmpty(Mod) ? 0 : 30) + - ((hmodsInstall != null && hmodsInstall.Count() > 0) ? 75 : 0); - var hmods = hmodsInstall; - hmodsInstall = null; - if (WaitForFelFromThread() != DialogResult.OK) - { - DialogResult = DialogResult.Abort; - return; - } - progress += 5; - SetProgress(progress, maxProgress); - - byte[] kernel; - if (!string.IsNullOrEmpty(Mod)) - { - kernel = CreatePatchedKernel(); - progress += 5; - SetProgress(progress, maxProgress); - } - else - kernel = File.ReadAllBytes(KernelDump); - var size = CalcKernelSize(kernel); - if (size > kernel.Length || size > Fel.kernel_max_size) - throw new Exception(Resources.InvalidKernelSize + " " + size); - - size = (size + Fel.sector_size - 1) / Fel.sector_size; - size = size * Fel.sector_size; - if (kernel.Length != size) - { - var newK = new byte[size]; - Array.Copy(kernel, newK, kernel.Length); - kernel = newK; - } - - fel.WriteFlash(Fel.kernel_base_f, kernel, - delegate(Fel.CurrentAction action, string command) - { - switch (action) - { - case Fel.CurrentAction.RunningCommand: - SetStatus(Resources.ExecutingCommand + " " + command); - break; - case Fel.CurrentAction.WritingMemory: - SetStatus(Resources.UploadingKernel); - break; - } - progress++; - SetProgress(progress, maxProgress); - } - ); - var r = fel.ReadFlash((UInt32)Fel.kernel_base_f, (UInt32)kernel.Length, - delegate(Fel.CurrentAction action, string command) - { - switch (action) - { - case Fel.CurrentAction.RunningCommand: - SetStatus(Resources.ExecutingCommand + " " + command); - break; - case Fel.CurrentAction.ReadingMemory: - SetStatus(Resources.Verifying); - break; - } - progress++; - SetProgress(progress, maxProgress); - } - ); - if (!kernel.SequenceEqual(r)) - throw new Exception(Resources.VerifyFailed); - - hmodsInstall = hmods; - if (hmodsInstall != null && hmodsInstall.Count() > 0) - { - Memboot(maxProgress, progress); // Lets install some mods - } - else - { - var shutdownCommand = string.Format("shutdown", Fel.kernel_base_m); - SetStatus(Resources.ExecutingCommand + " " + shutdownCommand); - fel.RunUbootCmd(shutdownCommand, true); - SetStatus(Resources.Done); - SetProgress(maxProgress, maxProgress); - } - } - - public static void GetMemoryStats() - { - var clovershell = MainForm.Clovershell; - var nandc = clovershell.ExecuteSimple("df /dev/nandc | tail -n 1 | awk '{ print $2 \" | \" $3 \" | \" $4 }'", 500, true).Split('|'); - WritedGamesSize = int.Parse(clovershell.ExecuteSimple("mkdir -p /var/lib/hakchi/rootfs/usr/share/games/ && du -s /var/lib/hakchi/rootfs/usr/share/games/ | awk '{ print $1 }'", 1000, true)) * 1024; - SaveStatesSize = int.Parse(clovershell.ExecuteSimple("mkdir -p /var/lib/clover/profiles/0/ && du -s /var/lib/clover/profiles/0/ | awk '{ print $1 }'", 1000, true)) * 1024; - NandCTotal = int.Parse(nandc[0]) * 1024; - NandCUsed = int.Parse(nandc[1]) * 1024; - NandCFree = int.Parse(nandc[2]) * 1024; - Debug.WriteLine(string.Format("NANDC size: {0:F1}MB, used: {1:F1}MB, free: {2:F1}MB", NandCTotal / 1024.0 / 1024.0, NandCUsed / 1024.0 / 1024.0, NandCFree / 1024.0 / 1024.0)); - Debug.WriteLine(string.Format("Used by games: {0:F1}MB", WritedGamesSize / 1024.0 / 1024.0)); - Debug.WriteLine(string.Format("Used by save-states: {0:F1}MB", SaveStatesSize / 1024.0 / 1024.0)); - Debug.WriteLine(string.Format("Used by other files (mods, configs, etc.): {0:F1}MB", (NandCUsed - WritedGamesSize - SaveStatesSize) / 1024.0 / 1024.0)); - Debug.WriteLine(string.Format("Available for games: {0:F1}MB", (NandCFree + WritedGamesSize) / 1024.0 / 1024.0)); - } - - public static void ShowSplashScreen() - { - var clovershell = MainForm.Clovershell; - var splashScreenPath = Path.Combine(Path.Combine(Program.BaseDirectoryInternal, "data"), "splash.gz"); - clovershell.ExecuteSimple("pkill -KILL clover-mcp"); - clovershell.ExecuteSimple("pkill -KILL ReedPlayer-Clover"); - clovershell.ExecuteSimple("pkill -KILL kachikachi"); - if (File.Exists(splashScreenPath)) - { - using (var splash = new FileStream(splashScreenPath, FileMode.Open)) - { - clovershell.Execute("gunzip -c - > /dev/fb0", splash, null, null, 3000); - } - } - } - - public void UploadGames() - { - const string gamesPath = "/usr/share/games/nes/kachikachi"; - const string rootFsPath = "/var/lib/hakchi/rootfs"; - const string installPath = "/var/lib/hakchi"; - int progress = 0; - int maxProgress = 400; - if (Games == null || Games.Count == 0) - throw new Exception("there are no games"); - SetStatus(Resources.BuildingFolders); - if (FoldersMode == NesMenuCollection.SplitStyle.Custom) - { - if (FoldersManagerFromThread(Games) != System.Windows.Forms.DialogResult.OK) - { - DialogResult = DialogResult.Abort; - return; - } - Games.AddBack(); - } - else Games.Split(FoldersMode, MaxGamesPerFolder); - progress += 5; - SetProgress(progress, maxProgress); - - var clovershell = MainForm.Clovershell; - try - { - if (WaitForClovershellFromThread() != DialogResult.OK) - { - DialogResult = DialogResult.Abort; - return; - } - progress += 5; - SetProgress(progress, maxProgress); - - ShowSplashScreen(); - - SetStatus(Resources.BuildingFolders); - if (Directory.Exists(tempDirectory)) - Directory.Delete(tempDirectory, true); - Directory.CreateDirectory(tempDirectory); - // Games! - tempGamesDirectory = Path.Combine(tempDirectory, "games"); - Directory.CreateDirectory(tempDirectory); - Directory.CreateDirectory(tempGamesDirectory); - Dictionary originalGames = new Dictionary(); - var stats = new GamesTreeStats(); - AddMenu(Games, originalGames, stats); - progress += 5; - SetProgress(progress, maxProgress); - - GetMemoryStats(); - var maxGamesSize = (NandCFree + WritedGamesSize) - ReservedMemory * 1024 * 1024; - if (stats.TotalSize > maxGamesSize) - { - throw new Exception(string.Format(Resources.MemoryFull, stats.TotalSize / 1024 / 1024) + "\r\n\r\n" + - string.Format(Resources.MemoryStats.Replace("|", "\r\n"), - NandCTotal / 1024.0 / 1024.0, - (NandCFree + WritedGamesSize - ReservedMemory * 1024 * 1024) / 1024 / 1024, - SaveStatesSize / 1024.0 / 1024.0, - (NandCUsed - WritedGamesSize - SaveStatesSize) / 1024.0 / 1024.0)); - } - - int startProgress = progress; - using (var gamesTar = new TarStream(tempGamesDirectory)) - { - maxProgress = (int)(gamesTar.Length / 1024 / 1024 + 20 + originalGames.Count() * 2); - SetProgress(progress, maxProgress); - - clovershell.ExecuteSimple(string.Format("umount {0}", gamesPath)); - clovershell.ExecuteSimple(string.Format("rm -rf {0}{1}/CLV-* {0}{1}/??? {2}/menu", rootFsPath, gamesPath, installPath), 5000, true); - - if (gamesTar.Length > 0) - { - gamesTar.OnReadProgress += delegate(long pos, long len) - { - progress = (int)(startProgress + pos / 1024 / 1024); - SetProgress(progress, maxProgress); - }; - - SetStatus(Resources.UploadingGames); - clovershell.Execute(string.Format("tar -xvC {0}{1}", rootFsPath, gamesPath), gamesTar, null, null, 30000, true); - } - } - - SetStatus(Resources.UploadingOriginalGames); - startProgress = progress; - foreach (var originalCode in originalGames.Keys) - { - clovershell.ExecuteSimple(string.Format(@"mkdir -p ""{2}{3}/{1}/{0}/"" && rsync -ac ""{3}/{0}/"" ""{2}{3}/{1}/{0}/"" && sed -i -e 's/\/usr\/bin\/clover-kachikachi/\/bin\/clover-kachikachi-wr/g' ""{2}{3}/{1}/{0}/{0}.desktop""", originalCode, originalGames[originalCode], rootFsPath, gamesPath), 5000, true); - progress += 2; - SetProgress(progress, maxProgress); - }; - - SetStatus(Resources.UploadingConfig); - SyncConfig(Config); -#if !DEBUG - Directory.Delete(tempDirectory, true); -#endif - SetStatus(Resources.Done); - SetProgress(maxProgress, maxProgress); - } - finally - { - try - { - if (clovershell.IsOnline) - clovershell.ExecuteSimple("reboot", 100); - } - catch { } - } - } - - public static void SyncConfig(Dictionary Config, bool reboot = false) - { - var clovershell = MainForm.Clovershell; - - // Writing config - var config = new MemoryStream(); - if (Config != null && Config.Count > 0) - { - foreach (var key in Config.Keys) - { - var data = Encoding.UTF8.GetBytes(string.Format("cfg_{0}='{1}'\n", key, Config[key].Replace(@"'", @"\'"))); - config.Write(data, 0, data.Length); - } - } - clovershell.Execute("cat > /tmp/config", config, null, null, 1000, true); - clovershell.Execute("source /etc/preinit && script_init && source /tmp/config && source $preinit.d/pffff_config", null, null, null, 30000, true); - config.Dispose(); - if (reboot) - { - try - { - clovershell.ExecuteSimple("reboot", 100); - } - catch { } - } - } - - public static Image TakeScreenshot() - { - var clovershell = MainForm.Clovershell; - var screenshot = new Bitmap(1280, 720, System.Drawing.Imaging.PixelFormat.Format24bppRgb); - var rawStream = new MemoryStream(); - clovershell.Execute("cat /dev/fb0", null, rawStream, null, 1000, true); - var raw = rawStream.ToArray(); - BitmapData data = screenshot.LockBits( - new Rectangle(0, 0, screenshot.Width, screenshot.Height), - ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); - - int rawOffset = 0; - unsafe - { - for (int y = 0; y < screenshot.Height; ++y) - { - byte* row = (byte*)data.Scan0 + (y * data.Stride); - int columnOffset = 0; - for (int x = 0; x < screenshot.Width; ++x) - { - row[columnOffset] = raw[rawOffset]; - row[columnOffset + 1] = raw[rawOffset + 1]; - row[columnOffset + 2] = raw[rawOffset + 2]; - - columnOffset += 3; - rawOffset += 4; - } - } - } - screenshot.UnlockBits(data); - return screenshot; - } - - public void Memboot(int maxProgress = -1, int progress = 0) - { - SetProgress(progress, maxProgress < 0 ? 1000 : maxProgress); - // Connecting to NES Mini - if (WaitForFelFromThread() != DialogResult.OK) - { - DialogResult = DialogResult.Abort; - return; - } - progress += 5; - SetProgress(progress, maxProgress > 0 ? maxProgress : 1000); - - byte[] kernel; - if (!string.IsNullOrEmpty(Mod)) - kernel = CreatePatchedKernel(); - else - kernel = File.ReadAllBytes(KernelDump); - var size = CalcKernelSize(kernel); - if (size > kernel.Length || size > Fel.kernel_max_size) - throw new Exception(Resources.InvalidKernelSize + " " + size); - size = (size + Fel.sector_size - 1) / Fel.sector_size; - size = size * Fel.sector_size; - if (kernel.Length != size) - { - var newK = new byte[size]; - Array.Copy(kernel, newK, kernel.Length); - kernel = newK; - } - progress += 5; - if (maxProgress < 0) - maxProgress = (int)((double)kernel.Length / (double)67000 + 20); - SetProgress(progress, maxProgress); - - SetStatus(Resources.UploadingKernel); - fel.WriteMemory(Fel.flash_mem_base, kernel, - delegate(Fel.CurrentAction action, string command) - { - switch (action) - { - case Fel.CurrentAction.WritingMemory: - SetStatus(Resources.UploadingKernel); - break; - } - progress++; - SetProgress(progress, maxProgress); - } - ); - - var bootCommand = string.Format("boota {0:x}", Fel.kernel_base_m); - SetStatus(Resources.ExecutingCommand + " " + bootCommand); - fel.RunUbootCmd(bootCommand, true); - Thread.Sleep(7000); - SetStatus(Resources.Done); - SetProgress(maxProgress, maxProgress); - } - - private byte[] CreatePatchedKernel() - { - SetStatus(Resources.BuildingCustom); - if (Directory.Exists(tempDirectory)) - Directory.Delete(tempDirectory, true); - Directory.CreateDirectory(tempDirectory); - Directory.CreateDirectory(kernelDirectory); - Directory.CreateDirectory(ramfsDirectory); - if (!ExecuteTool("unpackbootimg.exe", string.Format("-i \"{0}\" -o \"{1}\"", KernelDump, kernelDirectory))) - throw new Exception("Can't unpack kernel image"); - if (!ExecuteTool("lzop.exe", string.Format("-d \"{0}\" -o \"{1}\"", - Path.Combine(kernelDirectory, "kernel.img-ramdisk.gz"), initramfs_cpio))) - throw new Exception("Can't unpack ramdisk"); - ExecuteTool("cpio.exe", string.Format("-imd --no-preserve-owner --quiet -I \"{0}\"", - @"..\initramfs.cpio"), ramfsDirectory); - if (!File.Exists(Path.Combine(ramfsDirectory, "init"))) // cpio.exe fails on Windows XP for some reason. But working! - throw new Exception("Can't unpack ramdisk 2"); - if (Directory.Exists(hakchiDirectory)) Directory.Delete(hakchiDirectory, true); - NesMiniApplication.DirectoryCopy(Path.Combine(modsDirectory, Mod), ramfsDirectory, true); - var ramfsFiles = Directory.GetFiles(ramfsDirectory, "*.*", SearchOption.AllDirectories); - foreach (var file in ramfsFiles) - { - var fInfo = new FileInfo(file); - if (fInfo.Length > 10 && fInfo.Length < 100 && ((fInfo.Attributes & FileAttributes.System) == 0) && - (Encoding.ASCII.GetString(File.ReadAllBytes(file), 0, 10)) == "!") - fInfo.Attributes |= FileAttributes.System; - } - - if (hmodsInstall != null && hmodsInstall.Count() > 0) - { - Directory.CreateDirectory(tempHmodsDirectory); - foreach (var hmod in hmodsInstall) - { - var modName = hmod + ".hmod"; - foreach (var dir in hmodDirectories) - { - if (Directory.Exists(Path.Combine(dir, modName))) - { - NesMiniApplication.DirectoryCopy(Path.Combine(dir, modName), Path.Combine(tempHmodsDirectory, modName), true); - break; - } - if (File.Exists(Path.Combine(dir, modName))) - { - File.Copy(Path.Combine(dir, modName), Path.Combine(tempHmodsDirectory, modName)); - break; - } - } - } - } - if (hmodsUninstall != null && hmodsUninstall.Count() > 0) - { - Directory.CreateDirectory(tempHmodsDirectory); - var mods = new StringBuilder(); - foreach (var hmod in hmodsUninstall) - mods.AppendFormat("{0}.hmod\n", hmod); - File.WriteAllText(Path.Combine(tempHmodsDirectory, "uninstall"), mods.ToString()); - } - - // Building image - byte[] ramdisk; - if (!ExecuteTool("mkbootfs.exe", string.Format("\"{0}\"", ramfsDirectory), out ramdisk)) - throw new Exception("Can't repack ramdisk"); - File.WriteAllBytes(initramfs_cpioPatched, ramdisk); - var argCmdline = File.ReadAllText(Path.Combine(kernelDirectory, "kernel.img-cmdline")).Trim(); - var argBoard = File.ReadAllText(Path.Combine(kernelDirectory, "kernel.img-board")).Trim(); - var argBase = File.ReadAllText(Path.Combine(kernelDirectory, "kernel.img-base")).Trim(); - var argPagesize = File.ReadAllText(Path.Combine(kernelDirectory, "kernel.img-pagesize")).Trim(); - var argKerneloff = File.ReadAllText(Path.Combine(kernelDirectory, "kernel.img-kerneloff")).Trim(); - var argRamdiscoff = File.ReadAllText(Path.Combine(kernelDirectory, "kernel.img-ramdiskoff")).Trim(); - var argTagsoff = File.ReadAllText(Path.Combine(kernelDirectory, "kernel.img-tagsoff")).Trim(); - if (!ExecuteTool("lzop.exe", string.Format("--best -f -o \"{0}\" \"{1}\"", - ramdiskPatched, initramfs_cpioPatched))) - throw new Exception("Can't repack ramdisk 2"); - if (!ExecuteTool("mkbootimg.exe", string.Format("--kernel \"{0}\" --ramdisk \"{1}\" --cmdline \"{2}\" --board \"{3}\" --base \"{4}\" --pagesize \"{5}\" --kernel_offset \"{6}\" --ramdisk_offset \"{7}\" --tags_offset \"{8}\" -o \"{9}\"", - Path.Combine(kernelDirectory, "kernel.img-zImage"), ramdiskPatched, argCmdline, argBoard, argBase, argPagesize, argKerneloff, argRamdiscoff, argTagsoff, kernelPatched))) - throw new Exception("Can't rebuild kernel"); - - var result = File.ReadAllBytes(kernelPatched); -#if !DEBUG - Directory.Delete(tempDirectory, true); -#endif - return result; - } - - void DownloadAllCovers() - { - if (Games == null) return; - int i = 0; - foreach (NesMiniApplication game in Games) - { - SetStatus(Resources.GooglingFor + " " + game.Name); - string[] urls = null; - for (int tries = 0; tries < 5; tries++) - { - if (urls == null) - { - try - { - urls = ImageGooglerForm.GetImageUrls(game); - break; - } - catch (Exception ex) - { - SetStatus(Resources.Error + ": " + ex.Message); - Thread.Sleep(1500); - continue; - } - } - } - if (urls != null && urls.Length == 0) - SetStatus(Resources.NotFound + " " + game.Name); - for (int tries = 0; urls != null && tries < 5 && tries < urls.Length; tries++) - { - try - { - var cover = ImageGooglerForm.DownloadImage(urls[tries]); - game.Image = cover; - break; - } - catch (Exception ex) - { - SetStatus(Resources.Error + ": " + ex.Message); - Thread.Sleep(1500); - continue; - } - } - SetProgress(++i, Games.Count); - Thread.Sleep(500); // not so fast, Google don't like it - } - } - - private class GamesTreeStats - { - public List allMenus = new List(); - public int TotalGames = 0; - public long TotalSize = 0; - public long TransferSize = 0; - } - - private void AddMenu(NesMenuCollection menuCollection, Dictionary originalGames, GamesTreeStats stats = null) - { - if (stats == null) - stats = new GamesTreeStats(); - if (!stats.allMenus.Contains(menuCollection)) - stats.allMenus.Add(menuCollection); - int menuIndex = stats.allMenus.IndexOf(menuCollection); - string targetDirectory; - if (menuIndex == 0) - targetDirectory = tempGamesDirectory; - else - targetDirectory = Path.Combine(tempGamesDirectory, string.Format("{0:D3}", menuIndex)); - foreach (var element in menuCollection) - { - if (element is NesMiniApplication) - { - stats.TotalGames++; - var game = element as NesMiniApplication; - var gameSize = game.Size(); - Debug.WriteLine(string.Format("Processing {0} ('{1}'), size: {2}KB", game.Code, game.Name, gameSize / 1024)); - var gameCopy = game.CopyTo(targetDirectory); - stats.TotalSize += gameSize; - stats.TransferSize += gameSize; - stats.TotalGames++; - try - { - if (gameCopy is NesGame && File.Exists((gameCopy as NesGame).GameGeniePath)) - { - (gameCopy as NesGame).ApplyGameGenie(); - File.Delete((gameCopy as NesGame).GameGeniePath); - } - } - catch (GameGenieFormatException ex) - { - ShowError(new Exception(string.Format(Resources.GameGenieFormatError, ex.Code, game.Name)), dontStop: true); - } - catch (GameGenieNotFoundException ex) - { - ShowError(new Exception(string.Format(Resources.GameGenieNotFound, ex.Code, game.Name)), dontStop: true); - } - } - if (element is NesMenuFolder) - { - var folder = element as NesMenuFolder; - if (!stats.allMenus.Contains(folder.ChildMenuCollection)) - { - stats.allMenus.Add(folder.ChildMenuCollection); - AddMenu(folder.ChildMenuCollection, originalGames, stats); - } - folder.ChildIndex = stats.allMenus.IndexOf(folder.ChildMenuCollection); - var folderDir = Path.Combine(targetDirectory, folder.Code); - var folderSize = folder.Save(folderDir); - stats.TotalSize += folderSize; - stats.TransferSize += folderSize; - - } - if (element is NesDefaultGame) - { - var game = element as NesDefaultGame; - stats.TotalSize += game.Size; - originalGames[game.Code] = menuIndex == 0 ? "." : string.Format("{0:D3}", menuIndex); - } - } - } - - private bool ExecuteTool(string tool, string args, string directory = null, bool external = false) - { - byte[] output; - return ExecuteTool(tool, args, out output, directory, external); - } - - private bool ExecuteTool(string tool, string args, out byte[] output, string directory = null, bool external = false) - { - var process = new Process(); - var appDirectory = baseDirectoryInternal; - var fileName = !external ? Path.Combine(toolsDirectory, tool) : tool; - process.StartInfo.FileName = fileName; - process.StartInfo.Arguments = args; - if (string.IsNullOrEmpty(directory)) - directory = appDirectory; - process.StartInfo.WorkingDirectory = directory; - process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; - process.StartInfo.UseShellExecute = false; - process.StartInfo.CreateNoWindow = true; - process.StartInfo.StandardOutputEncoding = Encoding.GetEncoding(866); - process.StartInfo.RedirectStandardInput = true; - process.StartInfo.RedirectStandardOutput = true; - process.StartInfo.RedirectStandardError = true; - Debug.WriteLine("Executing: " + fileName); - Debug.WriteLine("Arguments: " + args); - Debug.WriteLine("Directory: " + directory); - process.Start(); - string outputStr = process.StandardOutput.ReadToEnd(); - string errorStr = process.StandardError.ReadToEnd(); - process.WaitForExit(); - output = Encoding.GetEncoding(866).GetBytes(outputStr); - Debug.WriteLineIf(outputStr.Length > 0 && outputStr.Length < 300, "Output:\r\n" + outputStr); - Debug.WriteLineIf(errorStr.Length > 0, "Errors:\r\n" + errorStr); - Debug.WriteLine("Exit code: " + process.ExitCode); - return process.ExitCode == 0; - } - - static UInt32 CalcKernelSize(byte[] header) - { - if (Encoding.ASCII.GetString(header, 0, 8) != "ANDROID!") throw new Exception(Resources.InvalidKernelHeader); - UInt32 kernel_size = (UInt32)(header[8] | (header[9] * 0x100) | (header[10] * 0x10000) | (header[11] * 0x1000000)); - UInt32 kernel_addr = (UInt32)(header[12] | (header[13] * 0x100) | (header[14] * 0x10000) | (header[15] * 0x1000000)); - UInt32 ramdisk_size = (UInt32)(header[16] | (header[17] * 0x100) | (header[18] * 0x10000) | (header[19] * 0x1000000)); - UInt32 ramdisk_addr = (UInt32)(header[20] | (header[21] * 0x100) | (header[22] * 0x10000) | (header[23] * 0x1000000)); - UInt32 second_size = (UInt32)(header[24] | (header[25] * 0x100) | (header[26] * 0x10000) | (header[27] * 0x1000000)); - UInt32 second_addr = (UInt32)(header[28] | (header[29] * 0x100) | (header[30] * 0x10000) | (header[31] * 0x1000000)); - UInt32 tags_addr = (UInt32)(header[32] | (header[33] * 0x100) | (header[34] * 0x10000) | (header[35] * 0x1000000)); - UInt32 page_size = (UInt32)(header[36] | (header[37] * 0x100) | (header[38] * 0x10000) | (header[39] * 0x1000000)); - UInt32 dt_size = (UInt32)(header[40] | (header[41] * 0x100) | (header[42] * 0x10000) | (header[43] * 0x1000000)); - UInt32 pages = 1; - pages += (kernel_size + page_size - 1) / page_size; - pages += (ramdisk_size + page_size - 1) / page_size; - pages += (second_size + page_size - 1) / page_size; - pages += (dt_size + page_size - 1) / page_size; - return pages * page_size; - } - - - bool YesForAllPatches = false; - public ICollection AddGames(IEnumerable files, Form parentForm = null) - { - var apps = new List(); - addedApplications = null; - //bool NoForAllUnsupportedMappers = false; - bool YesForAllUnsupportedMappers = false; - YesForAllPatches = false; - int count = 0; - SetStatus(Resources.AddingGames); - foreach (var sourceFileName in files) - { - NesMiniApplication app = null; - try - { - var fileName = sourceFileName; - var ext = Path.GetExtension(sourceFileName).ToLower(); - bool? needPatch = YesForAllPatches ? (bool?)true : null; - byte[] rawData = null; - string tmp = null; - if (ext == ".7z" || ext == ".zip" || ext == ".rar") - { - SevenZipExtractor.SetLibraryPath(Path.Combine(baseDirectoryInternal, IntPtr.Size == 8 ? @"tools\7z64.dll" : @"tools\7z.dll")); - using (var szExtractor = new SevenZipExtractor(sourceFileName)) - { - var filesInArchive = new List(); - var gameFilesInArchive = new List(); - foreach (var f in szExtractor.ArchiveFileNames) - { - var e = Path.GetExtension(f).ToLower(); - if (e == ".desktop" || AppTypeCollection.GetAppByExtension(e) != null) - gameFilesInArchive.Add(f); - filesInArchive.Add(f); - } - if (gameFilesInArchive.Count == 1) // Only one NES file (or app) - { - fileName = gameFilesInArchive[0]; - } - else if (gameFilesInArchive.Count > 1) // Many NES files, need to select - { - var r = SelectFileFromThread(gameFilesInArchive.ToArray()); - if (r == DialogResult.OK) - fileName = selectedFile; - else if (r == DialogResult.Ignore) - fileName = sourceFileName; - else continue; - } - else if (filesInArchive.Count == 1) // No NES files but only one another file - { - fileName = filesInArchive[0]; - } - else // Need to select - { - var r = SelectFileFromThread(filesInArchive.ToArray()); - if (r == DialogResult.OK) - fileName = selectedFile; - else if (r == DialogResult.Ignore) - fileName = sourceFileName; - else continue; - } - if (fileName != sourceFileName) - { - var o = new MemoryStream(); - if (Path.GetExtension(fileName).ToLower() == ".desktop" // App in archive, need the whole directory - || szExtractor.ArchiveFileNames.Contains(Path.GetFileNameWithoutExtension(fileName) + ".jpg") // Or it has cover in archive - || szExtractor.ArchiveFileNames.Contains(Path.GetFileNameWithoutExtension(fileName) + ".png")) - { - tmp = Path.Combine(Path.GetTempPath(), fileName); - Directory.CreateDirectory(tmp); - szExtractor.ExtractArchive(tmp); - fileName = Path.Combine(tmp, fileName); - } - else - { - szExtractor.ExtractFile(fileName, o); - rawData = new byte[o.Length]; - o.Seek(0, SeekOrigin.Begin); - o.Read(rawData, 0, (int)o.Length); - } - } - } - } - if (Path.GetExtension(fileName).ToLower() == ".nes") - { - try - { - app = NesGame.Import(fileName, sourceFileName, YesForAllUnsupportedMappers ? (bool?)true : null, ref needPatch, needPatchCallback, this, rawData); - - // Trying to import Game Genie codes - var lGameGeniePath = Path.Combine(Path.GetDirectoryName(fileName), Path.GetFileNameWithoutExtension(fileName) + ".xml"); - if (File.Exists(lGameGeniePath)) - { - GameGenieDataBase lGameGenieDataBase = new GameGenieDataBase(app); - lGameGenieDataBase.ImportCodes(lGameGeniePath, true); - lGameGenieDataBase.Save(); - } - } - catch (Exception ex) - { - if (ex is UnsupportedMapperException || ex is UnsupportedFourScreenException) - { - var r = MessageBoxFromThread(this, - (ex is UnsupportedMapperException) - ? string.Format(Resources.MapperNotSupported, Path.GetFileName(fileName), (ex as UnsupportedMapperException).ROM.Mapper) - : string.Format(Resources.FourScreenNotSupported, Path.GetFileName(fileName)), - Resources.AreYouSure, - files.Count() <= 1 ? MessageBoxButtons.YesNo : MessageBoxButtons.AbortRetryIgnore, - MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2, true); - if (r == DialogResult.Abort) - YesForAllUnsupportedMappers = true; - if (r == DialogResult.Yes || r == DialogResult.Abort || r == DialogResult.Retry) - app = NesGame.Import(fileName, sourceFileName, true, ref needPatch, needPatchCallback, this, rawData); - else - continue; - } - else throw ex; - } - } - else - { - app = NesMiniApplication.Import(fileName, sourceFileName, rawData); - } - if (!string.IsNullOrEmpty(tmp) && Directory.Exists(tmp)) Directory.Delete(tmp, true); - ConfigIni.SelectedGames += ";" + app.Code; - } - catch (Exception ex) - { - if (ex is ThreadAbortException) return null; - Debug.WriteLine(ex.Message + ex.StackTrace); - ShowError(ex, true); - } - if (app != null) - apps.Add(app); - SetProgress(++count, files.Count()); - } - addedApplications = apps.ToArray(); - return apps; // Added games/apps - } - - private bool needPatchCallback(Form parentForm, string nesFileName) - { - if (GamesToAdd == null || GamesToAdd.Count() <= 1) - { - return MessageBoxFromThread(parentForm, - string.Format(Resources.PatchQ, Path.GetFileName(nesFileName)), - Resources.PatchAvailable, - MessageBoxButtons.YesNo, - MessageBoxIcon.Question, - MessageBoxDefaultButton.Button1, false) == DialogResult.Yes; - } - else - { - var r = MessageBoxFromThread(parentForm, - string.Format(Resources.PatchQ, Path.GetFileName(nesFileName)), - Resources.PatchAvailable, - MessageBoxButtons.AbortRetryIgnore, - MessageBoxIcon.Question, - MessageBoxDefaultButton.Button2, true); - if (r == DialogResult.Abort) - YesForAllPatches = true; - return r != DialogResult.Ignore; - } - } - - private void WorkerForm_FormClosing(object sender, FormClosingEventArgs e) - { - if ((thread != null) && (e.CloseReason == CloseReason.UserClosing)) - { - if (MessageBox.Show(this, Resources.DoYouWantCancel, Resources.AreYouSure, MessageBoxButtons.YesNo, MessageBoxIcon.Warning) - == System.Windows.Forms.DialogResult.No) - { - e.Cancel = true; - return; - } - if (thread != null) thread.Abort(); - TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.NoProgress); - TaskbarProgress.SetValue(this, 0, 1); - } - } - } -} +using com.clusterrr.Famicom; +using com.clusterrr.FelLib; +using com.clusterrr.hakchi_gui.Properties; +using com.clusterrr.util; +using SevenZip; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Imaging; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Windows.Forms; + +namespace com.clusterrr.hakchi_gui +{ + public partial class WorkerForm : Form + { + public enum Tasks { DumpKernel, FlashKernel, Memboot, UploadGames, DownloadAllCovers, AddGames }; + public Tasks Task; + //public string UBootDump; + public string KernelDump; + public string Mod = null; + public Dictionary Config = null; + public NesMenuCollection Games; + public IEnumerable hmodsInstall; + public IEnumerable hmodsUninstall; + public IEnumerable GamesToAdd; + public NesMenuCollection.SplitStyle FoldersMode = NesMenuCollection.SplitStyle.Auto; + public int MaxGamesPerFolder = 35; + public MainForm MainForm; + Thread thread = null; + Fel fel = null; + + const UInt16 vid = 0x1F3A; + const UInt16 pid = 0xEFE8; + + readonly string baseDirectoryInternal; + readonly string baseDirectoryExternal; + readonly string fes1Path; + readonly string ubootPath; + readonly string tempDirectory; + readonly string kernelDirectory; + readonly string initramfs_cpio; + readonly string initramfs_cpioPatched; + readonly string ramfsDirectory; + readonly string hakchiDirectory; + readonly string modsDirectory; + readonly string[] hmodDirectories; + readonly string toolsDirectory; + readonly string kernelPatched; + readonly string ramdiskPatched; + readonly string tempHmodsDirectory; + readonly string cloverconDriverPath; + readonly string argumentsFilePath; + readonly string transferDirectory; + string tempGamesDirectory; + //string originalGamesConfigDirectory; + //string hiddenPath; + string[] correctKernels; + const long maxCompressedsRamfsSize = 30 * 1024 * 1024; + string selectedFile = null; + public List addedApplications; + public static int NandCTotal, NandCUsed, NandCFree, WritedGamesSize, SaveStatesSize; + public const long ReservedMemory = 10; + + public WorkerForm() + { + InitializeComponent(); + DialogResult = DialogResult.None; + baseDirectoryInternal = Program.BaseDirectoryInternal; + baseDirectoryExternal = Program.BaseDirectoryExternal; + fes1Path = Path.Combine(Path.Combine(baseDirectoryInternal, "data"), "fes1.bin"); + ubootPath = Path.Combine(Path.Combine(baseDirectoryInternal, "data"), "uboot.bin"); +#if DEBUG + tempDirectory = Path.Combine(baseDirectoryInternal, "temp"); +#else + tempDirectory = Path.Combine(Path.GetTempPath(), "hakchi-temp"); +#endif + kernelDirectory = Path.Combine(tempDirectory, "kernel"); + initramfs_cpio = Path.Combine(kernelDirectory, "initramfs.cpio"); + initramfs_cpioPatched = Path.Combine(kernelDirectory, "initramfs_mod.cpio"); + ramfsDirectory = Path.Combine(kernelDirectory, "initramfs"); + hakchiDirectory = Path.Combine(ramfsDirectory, "hakchi"); + modsDirectory = Path.Combine(baseDirectoryInternal, "mods"); + hmodDirectories = new string[]{ + Path.Combine(baseDirectoryExternal, "user_mods"), + Path.Combine(modsDirectory, "hmods") + }; + toolsDirectory = Path.Combine(baseDirectoryInternal, "tools"); + kernelPatched = Path.Combine(kernelDirectory, "patched_kernel.img"); + ramdiskPatched = Path.Combine(kernelDirectory, "kernel.img-ramdisk_mod.gz"); + cloverconDriverPath = Path.Combine(hakchiDirectory, "clovercon.ko"); + argumentsFilePath = Path.Combine(hakchiDirectory, "extra_args"); + transferDirectory = Path.Combine(hakchiDirectory, "transfer"); + tempHmodsDirectory = Path.Combine(transferDirectory, "hmod"); + correctKernels + = new string[] { + "5cfdca351484e7025648abc3b20032ff", "07bfb800beba6ef619c29990d14b5158", // NES Mini + "ac8144c3ea4ab32e017648ee80bdc230" // Famicom Mini + }; + } + + public DialogResult Start() + { + SetProgress(0, 1); + thread = new Thread(StartThread); + thread.IsBackground = true; + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + return ShowDialog(); + } + + DialogResult WaitForFelFromThread() + { + if (InvokeRequired) + { + return (DialogResult)Invoke(new Func(WaitForFelFromThread)); + } + SetStatus(Resources.WaitingForDevice); + if (fel != null) + fel.Close(); + TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Paused); + var result = WaitingFelForm.WaitForDevice(vid, pid, this); + if (result) + { + fel = new Fel(); + if (!File.Exists(fes1Path)) throw new FileNotFoundException(fes1Path + " not found"); + if (!File.Exists(ubootPath)) throw new FileNotFoundException(ubootPath + " not found"); + fel.Fes1Bin = File.ReadAllBytes(fes1Path); + fel.UBootBin = File.ReadAllBytes(ubootPath); + fel.Open(vid, pid); + SetStatus(Resources.UploadingFes1); + fel.InitDram(true); + TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Normal); + return DialogResult.OK; + } + TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Normal); + return DialogResult.Abort; + } + DialogResult WaitForClovershellFromThread() + { + if (InvokeRequired) + { + return (DialogResult)Invoke(new Func(WaitForClovershellFromThread)); + } + SetStatus(Resources.WaitingForDevice); + TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Paused); + var result = WaitingClovershellForm.WaitForDevice(this); + TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Normal); + if (result) + return DialogResult.OK; + else return DialogResult.Abort; + } + + private delegate DialogResult MessageBoxFromThreadDelegate(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, + MessageBoxIcon icon, MessageBoxDefaultButton defaultButton, bool tweak); + DialogResult MessageBoxFromThread(IWin32Window owner, string text, string caption, MessageBoxButtons buttons, + MessageBoxIcon icon, MessageBoxDefaultButton defaultButton, bool tweak) + { + if (InvokeRequired) + { + return (DialogResult)Invoke(new MessageBoxFromThreadDelegate(MessageBoxFromThread), + new object[] { owner, text, caption, buttons, icon, defaultButton, tweak }); + } + TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Paused); + if (tweak) MessageBoxManager.Register(); // Tweak button names + var result = MessageBox.Show(owner, text, caption, buttons, icon, defaultButton); + if (tweak) MessageBoxManager.Unregister(); + TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Normal); + return result; + } + + DialogResult FoldersManagerFromThread(NesMenuCollection collection) + { + if (InvokeRequired) + { + return (DialogResult)Invoke(new Func(FoldersManagerFromThread), new object[] { collection }); + } + var constructor = new FoldersManagerForm(collection, MainForm); + TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Paused); + var result = constructor.ShowDialog(); + TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Normal); + return result; + } + + DialogResult SelectFileFromThread(string[] files) + { + if (InvokeRequired) + { + return (DialogResult)Invoke(new Func(SelectFileFromThread), new object[] { files }); + } + var form = new SelectFileForm(files); + TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Paused); + var result = form.ShowDialog(); + TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Normal); + if (form.listBoxFiles.SelectedItem != null) + selectedFile = form.listBoxFiles.SelectedItem.ToString(); + else + selectedFile = null; + TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Normal); + return result; + } + + public void StartThread() + { + Thread.CurrentThread.CurrentUICulture = new CultureInfo(ConfigIni.Language); + SetProgress(0, 1); + try + { + DialogResult = DialogResult.None; + Debug.WriteLine("Executing task: " + Task.ToString()); + switch (Task) + { + case Tasks.DumpKernel: + DoKernelDump(); + break; + case Tasks.FlashKernel: + FlashKernel(); + break; + case Tasks.UploadGames: + UploadGames(); + break; + case Tasks.Memboot: + Memboot(); + break; + case Tasks.DownloadAllCovers: + DownloadAllCovers(); + break; + case Tasks.AddGames: + AddGames(GamesToAdd); + break; + } + if (DialogResult == DialogResult.None) + DialogResult = DialogResult.OK; + } + catch (ThreadAbortException) { } + catch (Exception ex) + { + ShowError(ex); + } + finally + { + thread = null; + if (fel != null) + { + fel.Close(); + fel = null; + } + GC.Collect(); + } + } + + void SetStatus(string status) + { + if (Disposing) return; + try + { + if (InvokeRequired) + { + Invoke(new Action(SetStatus), new object[] { status }); + return; + } + labelStatus.Text = status; + } + catch { } + } + + void SetProgress(int value, int max) + { + if (Disposing) return; + try + { + if (InvokeRequired) + { + Invoke(new Action(SetProgress), new object[] { value, max }); + if (value == max) + Thread.Sleep(1000); + return; + } + if (value > max) value = max; + progressBar.Maximum = max; + progressBar.Value = value; + TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Normal); + TaskbarProgress.SetValue(this, value, max); + } + catch { } + } + + void ShowError(Exception ex, bool dontStop = false) + { + if (Disposing) return; + try + { + if (InvokeRequired) + { + Invoke(new Action(ShowError), new object[] { ex, dontStop }); + return; + } + TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Error); + var message = ex.Message; +#if DEBUG + message += ex.StackTrace; +#endif + Debug.WriteLine(ex.Message + ex.StackTrace); + //if (ex is MadWizard.WinUSBNet.USBException) // TODO + // MessageBox.Show(this, message + "\r\n" + Resources.PleaseTryAgainUSB, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error); + //else + MessageBox.Show(this, message, Resources.Error, MessageBoxButtons.OK, MessageBoxIcon.Error); + TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Normal); + if (!dontStop) + { + thread = null; + Close(); + } + } + catch { } + } + + void ShowMessage(string text, string title) + { + if (Disposing) return; + try + { + if (InvokeRequired) + { + Invoke(new Action(ShowMessage), new object[] { text, title }); + return; + } + TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Paused); + MessageBox.Show(this, text, title, MessageBoxButtons.OK, MessageBoxIcon.Information); + TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.Normal); + } + catch { } + } + + public void DoKernelDump() + { + int progress = 0; + const int maxProgress = 80; + if (WaitForFelFromThread() != DialogResult.OK) + { + DialogResult = DialogResult.Abort; + return; + } + progress += 5; + SetProgress(progress, maxProgress); + + SetStatus(Resources.DumpingKernel); + var kernel = fel.ReadFlash(Fel.kernel_base_f, Fel.sector_size * 0x20, + delegate(Fel.CurrentAction action, string command) + { + switch (action) + { + case Fel.CurrentAction.RunningCommand: + SetStatus(Resources.ExecutingCommand + " " + command); + break; + case Fel.CurrentAction.ReadingMemory: + SetStatus(Resources.DumpingKernel); + break; + } + progress++; + SetProgress(progress, maxProgress); + } + ); + + var size = CalcKernelSize(kernel); + if (size == 0 || size > Fel.kernel_max_size) + throw new Exception(Resources.InvalidKernelSize + " " + size); + if (kernel.Length > size) + { + var sm_kernel = new byte[size]; + Array.Copy(kernel, 0, sm_kernel, 0, size); + kernel = sm_kernel; + } + + SetProgress(maxProgress, maxProgress); + SetStatus(Resources.Done); + + var md5 = System.Security.Cryptography.MD5.Create(); + var hash = BitConverter.ToString(md5.ComputeHash(kernel)).Replace("-", "").ToLower(); + if (!correctKernels.Contains(hash)) + { + if (MessageBoxFromThread(this, Resources.MD5Failed + " " + hash + "\r\n" + Resources.MD5Failed2 + + "\r\n" + Resources.DoYouWantToContinue, Resources.Warning, MessageBoxButtons.YesNo, + MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1, false) + == DialogResult.No) + { + DialogResult = DialogResult.Abort; + return; + } + } + + Directory.CreateDirectory(Path.GetDirectoryName(KernelDump)); + File.WriteAllBytes(KernelDump, kernel); + } + + public void FlashKernel() + { + int progress = 0; + int maxProgress = 115 + (string.IsNullOrEmpty(Mod) ? 0 : 30) + + ((hmodsInstall != null && hmodsInstall.Count() > 0) ? 75 : 0); + var hmods = hmodsInstall; + hmodsInstall = null; + if (WaitForFelFromThread() != DialogResult.OK) + { + DialogResult = DialogResult.Abort; + return; + } + progress += 5; + SetProgress(progress, maxProgress); + + byte[] kernel; + if (!string.IsNullOrEmpty(Mod)) + { + kernel = CreatePatchedKernel(); + progress += 5; + SetProgress(progress, maxProgress); + } + else + kernel = File.ReadAllBytes(KernelDump); + var size = CalcKernelSize(kernel); + if (size > kernel.Length || size > Fel.kernel_max_size) + throw new Exception(Resources.InvalidKernelSize + " " + size); + + size = (size + Fel.sector_size - 1) / Fel.sector_size; + size = size * Fel.sector_size; + if (kernel.Length != size) + { + var newK = new byte[size]; + Array.Copy(kernel, newK, kernel.Length); + kernel = newK; + } + + fel.WriteFlash(Fel.kernel_base_f, kernel, + delegate(Fel.CurrentAction action, string command) + { + switch (action) + { + case Fel.CurrentAction.RunningCommand: + SetStatus(Resources.ExecutingCommand + " " + command); + break; + case Fel.CurrentAction.WritingMemory: + SetStatus(Resources.UploadingKernel); + break; + } + progress++; + SetProgress(progress, maxProgress); + } + ); + var r = fel.ReadFlash((UInt32)Fel.kernel_base_f, (UInt32)kernel.Length, + delegate(Fel.CurrentAction action, string command) + { + switch (action) + { + case Fel.CurrentAction.RunningCommand: + SetStatus(Resources.ExecutingCommand + " " + command); + break; + case Fel.CurrentAction.ReadingMemory: + SetStatus(Resources.Verifying); + break; + } + progress++; + SetProgress(progress, maxProgress); + } + ); + if (!kernel.SequenceEqual(r)) + throw new Exception(Resources.VerifyFailed); + + hmodsInstall = hmods; + if (hmodsInstall != null && hmodsInstall.Count() > 0) + { + Memboot(maxProgress, progress); // Lets install some mods + } + else + { + var shutdownCommand = string.Format("shutdown", Fel.kernel_base_m); + SetStatus(Resources.ExecutingCommand + " " + shutdownCommand); + fel.RunUbootCmd(shutdownCommand, true); + SetStatus(Resources.Done); + SetProgress(maxProgress, maxProgress); + } + } + + public static void GetMemoryStats() + { + var clovershell = MainForm.Clovershell; + var nandc = clovershell.ExecuteSimple("df /dev/nandc | tail -n 1 | awk '{ print $2 \" | \" $3 \" | \" $4 }'", 500, true).Split('|'); + WritedGamesSize = int.Parse(clovershell.ExecuteSimple("mkdir -p /var/lib/hakchi/rootfs/usr/share/games/ && du -s /var/lib/hakchi/rootfs/usr/share/games/ | awk '{ print $1 }'", 1000, true)) * 1024; + SaveStatesSize = int.Parse(clovershell.ExecuteSimple("mkdir -p /var/lib/clover/profiles/0/ && du -s /var/lib/clover/profiles/0/ | awk '{ print $1 }'", 1000, true)) * 1024; + NandCTotal = int.Parse(nandc[0]) * 1024; + NandCUsed = int.Parse(nandc[1]) * 1024; + NandCFree = int.Parse(nandc[2]) * 1024; + Debug.WriteLine(string.Format("NANDC size: {0:F1}MB, used: {1:F1}MB, free: {2:F1}MB", NandCTotal / 1024.0 / 1024.0, NandCUsed / 1024.0 / 1024.0, NandCFree / 1024.0 / 1024.0)); + Debug.WriteLine(string.Format("Used by games: {0:F1}MB", WritedGamesSize / 1024.0 / 1024.0)); + Debug.WriteLine(string.Format("Used by save-states: {0:F1}MB", SaveStatesSize / 1024.0 / 1024.0)); + Debug.WriteLine(string.Format("Used by other files (mods, configs, etc.): {0:F1}MB", (NandCUsed - WritedGamesSize - SaveStatesSize) / 1024.0 / 1024.0)); + Debug.WriteLine(string.Format("Available for games: {0:F1}MB", (NandCFree + WritedGamesSize) / 1024.0 / 1024.0)); + } + + public static void ShowSplashScreen() + { + var clovershell = MainForm.Clovershell; + var splashScreenPath = Path.Combine(Path.Combine(Program.BaseDirectoryInternal, "data"), "splash.gz"); + clovershell.ExecuteSimple("pkill -KILL clover-mcp"); + clovershell.ExecuteSimple("pkill -KILL ReedPlayer-Clover"); + clovershell.ExecuteSimple("pkill -KILL kachikachi"); + if (File.Exists(splashScreenPath)) + { + using (var splash = new FileStream(splashScreenPath, FileMode.Open)) + { + clovershell.Execute("gunzip -c - > /dev/fb0", splash, null, null, 3000); + } + } + } + + public void UploadGames() + { + const string gamesPath = "/usr/share/games/nes/kachikachi"; + const string rootFsPath = "/var/lib/hakchi/rootfs"; + const string installPath = "/var/lib/hakchi"; + int progress = 0; + int maxProgress = 400; + if (Games == null || Games.Count == 0) + throw new Exception("there are no games"); + SetStatus(Resources.BuildingFolders); + if (FoldersMode == NesMenuCollection.SplitStyle.Custom) + { + if (FoldersManagerFromThread(Games) != System.Windows.Forms.DialogResult.OK) + { + DialogResult = DialogResult.Abort; + return; + } + Games.AddBack(); + } + else Games.Split(FoldersMode, MaxGamesPerFolder); + progress += 5; + SetProgress(progress, maxProgress); + + var clovershell = MainForm.Clovershell; + try + { + if (WaitForClovershellFromThread() != DialogResult.OK) + { + DialogResult = DialogResult.Abort; + return; + } + progress += 5; + SetProgress(progress, maxProgress); + + ShowSplashScreen(); + + SetStatus(Resources.BuildingFolders); + if (Directory.Exists(tempDirectory)) + Directory.Delete(tempDirectory, true); + Directory.CreateDirectory(tempDirectory); + // Games! + tempGamesDirectory = Path.Combine(tempDirectory, "games"); + Directory.CreateDirectory(tempDirectory); + Directory.CreateDirectory(tempGamesDirectory); + Dictionary originalGames = new Dictionary(); + var stats = new GamesTreeStats(); + AddMenu(Games, originalGames, stats); + progress += 5; + SetProgress(progress, maxProgress); + + GetMemoryStats(); + var maxGamesSize = (NandCFree + WritedGamesSize) - ReservedMemory * 1024 * 1024; + if (stats.TotalSize > maxGamesSize) + { + throw new Exception(string.Format(Resources.MemoryFull, stats.TotalSize / 1024 / 1024) + "\r\n\r\n" + + string.Format(Resources.MemoryStats.Replace("|", "\r\n"), + NandCTotal / 1024.0 / 1024.0, + (NandCFree + WritedGamesSize - ReservedMemory * 1024 * 1024) / 1024 / 1024, + SaveStatesSize / 1024.0 / 1024.0, + (NandCUsed - WritedGamesSize - SaveStatesSize) / 1024.0 / 1024.0)); + } + + int startProgress = progress; + using (var gamesTar = new TarStream(tempGamesDirectory)) + { + maxProgress = (int)(gamesTar.Length / 1024 / 1024 + 20 + originalGames.Count() * 2); + SetProgress(progress, maxProgress); + + clovershell.ExecuteSimple(string.Format("umount {0}", gamesPath)); + clovershell.ExecuteSimple(string.Format("rm -rf {0}{1}/CLV-* {0}{1}/??? {2}/menu", rootFsPath, gamesPath, installPath), 5000, true); + + if (gamesTar.Length > 0) + { + gamesTar.OnReadProgress += delegate(long pos, long len) + { + progress = (int)(startProgress + pos / 1024 / 1024); + SetProgress(progress, maxProgress); + }; + + SetStatus(Resources.UploadingGames); + clovershell.Execute(string.Format("tar -xvC {0}{1}", rootFsPath, gamesPath), gamesTar, null, null, 30000, true); + } + } + + SetStatus(Resources.UploadingOriginalGames); + startProgress = progress; + foreach (var originalCode in originalGames.Keys) + { + clovershell.ExecuteSimple(string.Format(@"mkdir -p ""{2}{3}/{1}/{0}/"" && rsync -ac ""{3}/{0}/"" ""{2}{3}/{1}/{0}/"" && sed -i -e 's/\/usr\/bin\/clover-kachikachi/\/bin\/clover-kachikachi-wr/g' ""{2}{3}/{1}/{0}/{0}.desktop""", originalCode, originalGames[originalCode], rootFsPath, gamesPath), 5000, true); + progress += 2; + SetProgress(progress, maxProgress); + }; + + SetStatus(Resources.UploadingConfig); + SyncConfig(Config); +#if !DEBUG + Directory.Delete(tempDirectory, true); +#endif + SetStatus(Resources.Done); + SetProgress(maxProgress, maxProgress); + } + finally + { + try + { + if (clovershell.IsOnline) + clovershell.ExecuteSimple("reboot", 100); + } + catch { } + } + } + + public static void SyncConfig(Dictionary Config, bool reboot = false) + { + var clovershell = MainForm.Clovershell; + + // Writing config + var config = new MemoryStream(); + if (Config != null && Config.Count > 0) + { + foreach (var key in Config.Keys) + { + var data = Encoding.UTF8.GetBytes(string.Format("cfg_{0}='{1}'\n", key, Config[key].Replace(@"'", @"\'"))); + config.Write(data, 0, data.Length); + } + } + clovershell.Execute("cat > /tmp/config", config, null, null, 1000, true); + clovershell.Execute("source /etc/preinit && script_init && source /tmp/config && source $preinit.d/pffff_config", null, null, null, 30000, true); + config.Dispose(); + if (reboot) + { + try + { + clovershell.ExecuteSimple("reboot", 100); + } + catch { } + } + } + + public static Image TakeScreenshot() + { + var clovershell = MainForm.Clovershell; + var screenshot = new Bitmap(1280, 720, System.Drawing.Imaging.PixelFormat.Format24bppRgb); + var rawStream = new MemoryStream(); + clovershell.Execute("cat /dev/fb0", null, rawStream, null, 1000, true); + var raw = rawStream.ToArray(); + BitmapData data = screenshot.LockBits( + new Rectangle(0, 0, screenshot.Width, screenshot.Height), + ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); + + int rawOffset = 0; + unsafe + { + for (int y = 0; y < screenshot.Height; ++y) + { + byte* row = (byte*)data.Scan0 + (y * data.Stride); + int columnOffset = 0; + for (int x = 0; x < screenshot.Width; ++x) + { + row[columnOffset] = raw[rawOffset]; + row[columnOffset + 1] = raw[rawOffset + 1]; + row[columnOffset + 2] = raw[rawOffset + 2]; + + columnOffset += 3; + rawOffset += 4; + } + } + } + screenshot.UnlockBits(data); + return screenshot; + } + + public void Memboot(int maxProgress = -1, int progress = 0) + { + SetProgress(progress, maxProgress < 0 ? 1000 : maxProgress); + // Connecting to NES Mini + if (WaitForFelFromThread() != DialogResult.OK) + { + DialogResult = DialogResult.Abort; + return; + } + progress += 5; + SetProgress(progress, maxProgress > 0 ? maxProgress : 1000); + + byte[] kernel; + if (!string.IsNullOrEmpty(Mod)) + kernel = CreatePatchedKernel(); + else + kernel = File.ReadAllBytes(KernelDump); + var size = CalcKernelSize(kernel); + if (size > kernel.Length || size > Fel.kernel_max_size) + throw new Exception(Resources.InvalidKernelSize + " " + size); + size = (size + Fel.sector_size - 1) / Fel.sector_size; + size = size * Fel.sector_size; + if (kernel.Length != size) + { + var newK = new byte[size]; + Array.Copy(kernel, newK, kernel.Length); + kernel = newK; + } + progress += 5; + if (maxProgress < 0) + maxProgress = (int)((double)kernel.Length / (double)67000 + 20); + SetProgress(progress, maxProgress); + + SetStatus(Resources.UploadingKernel); + fel.WriteMemory(Fel.flash_mem_base, kernel, + delegate(Fel.CurrentAction action, string command) + { + switch (action) + { + case Fel.CurrentAction.WritingMemory: + SetStatus(Resources.UploadingKernel); + break; + } + progress++; + SetProgress(progress, maxProgress); + } + ); + + var bootCommand = string.Format("boota {0:x}", Fel.kernel_base_m); + SetStatus(Resources.ExecutingCommand + " " + bootCommand); + fel.RunUbootCmd(bootCommand, true); + Thread.Sleep(7000); + SetStatus(Resources.Done); + SetProgress(maxProgress, maxProgress); + } + + private byte[] CreatePatchedKernel() + { + SetStatus(Resources.BuildingCustom); + if (Directory.Exists(tempDirectory)) + Directory.Delete(tempDirectory, true); + Directory.CreateDirectory(tempDirectory); + Directory.CreateDirectory(kernelDirectory); + Directory.CreateDirectory(ramfsDirectory); + if (!ExecuteTool("unpackbootimg.exe", string.Format("-i \"{0}\" -o \"{1}\"", KernelDump, kernelDirectory))) + throw new Exception("Can't unpack kernel image"); + if (!ExecuteTool("lzop.exe", string.Format("-d \"{0}\" -o \"{1}\"", + Path.Combine(kernelDirectory, "kernel.img-ramdisk.gz"), initramfs_cpio))) + throw new Exception("Can't unpack ramdisk"); + ExecuteTool("cpio.exe", string.Format("-imd --no-preserve-owner --quiet -I \"{0}\"", + @"..\initramfs.cpio"), ramfsDirectory); + if (!File.Exists(Path.Combine(ramfsDirectory, "init"))) // cpio.exe fails on Windows XP for some reason. But working! + throw new Exception("Can't unpack ramdisk 2"); + if (Directory.Exists(hakchiDirectory)) Directory.Delete(hakchiDirectory, true); + NesMiniApplication.DirectoryCopy(Path.Combine(modsDirectory, Mod), ramfsDirectory, true); + var ramfsFiles = Directory.GetFiles(ramfsDirectory, "*.*", SearchOption.AllDirectories); + foreach (var file in ramfsFiles) + { + var fInfo = new FileInfo(file); + if (fInfo.Length > 10 && fInfo.Length < 100 && ((fInfo.Attributes & FileAttributes.System) == 0) && + (Encoding.ASCII.GetString(File.ReadAllBytes(file), 0, 10)) == "!") + fInfo.Attributes |= FileAttributes.System; + } + + if (hmodsInstall != null && hmodsInstall.Count() > 0) + { + Directory.CreateDirectory(tempHmodsDirectory); + foreach (var hmod in hmodsInstall) + { + var modName = hmod + ".hmod"; + foreach (var dir in hmodDirectories) + { + if (Directory.Exists(Path.Combine(dir, modName))) + { + NesMiniApplication.DirectoryCopy(Path.Combine(dir, modName), Path.Combine(tempHmodsDirectory, modName), true); + break; + } + if (File.Exists(Path.Combine(dir, modName))) + { + File.Copy(Path.Combine(dir, modName), Path.Combine(tempHmodsDirectory, modName)); + break; + } + } + } + } + if (hmodsUninstall != null && hmodsUninstall.Count() > 0) + { + Directory.CreateDirectory(tempHmodsDirectory); + var mods = new StringBuilder(); + foreach (var hmod in hmodsUninstall) + mods.AppendFormat("{0}.hmod\n", hmod); + File.WriteAllText(Path.Combine(tempHmodsDirectory, "uninstall"), mods.ToString()); + } + + // Building image + byte[] ramdisk; + if (!ExecuteTool("mkbootfs.exe", string.Format("\"{0}\"", ramfsDirectory), out ramdisk)) + throw new Exception("Can't repack ramdisk"); + File.WriteAllBytes(initramfs_cpioPatched, ramdisk); + var argCmdline = File.ReadAllText(Path.Combine(kernelDirectory, "kernel.img-cmdline")).Trim(); + var argBoard = File.ReadAllText(Path.Combine(kernelDirectory, "kernel.img-board")).Trim(); + var argBase = File.ReadAllText(Path.Combine(kernelDirectory, "kernel.img-base")).Trim(); + var argPagesize = File.ReadAllText(Path.Combine(kernelDirectory, "kernel.img-pagesize")).Trim(); + var argKerneloff = File.ReadAllText(Path.Combine(kernelDirectory, "kernel.img-kerneloff")).Trim(); + var argRamdiscoff = File.ReadAllText(Path.Combine(kernelDirectory, "kernel.img-ramdiskoff")).Trim(); + var argTagsoff = File.ReadAllText(Path.Combine(kernelDirectory, "kernel.img-tagsoff")).Trim(); + if (!ExecuteTool("lzop.exe", string.Format("--best -f -o \"{0}\" \"{1}\"", + ramdiskPatched, initramfs_cpioPatched))) + throw new Exception("Can't repack ramdisk 2"); + if (!ExecuteTool("mkbootimg.exe", string.Format("--kernel \"{0}\" --ramdisk \"{1}\" --cmdline \"{2}\" --board \"{3}\" --base \"{4}\" --pagesize \"{5}\" --kernel_offset \"{6}\" --ramdisk_offset \"{7}\" --tags_offset \"{8}\" -o \"{9}\"", + Path.Combine(kernelDirectory, "kernel.img-zImage"), ramdiskPatched, argCmdline, argBoard, argBase, argPagesize, argKerneloff, argRamdiscoff, argTagsoff, kernelPatched))) + throw new Exception("Can't rebuild kernel"); + + var result = File.ReadAllBytes(kernelPatched); +#if !DEBUG + Directory.Delete(tempDirectory, true); +#endif + return result; + } + + void DownloadAllCovers() + { + if (Games == null) return; + int i = 0; + foreach (NesMiniApplication game in Games) + { + SetStatus(Resources.GooglingFor + " " + game.Name); + string[] urls = null; + for (int tries = 0; tries < 5; tries++) + { + if (urls == null) + { + try + { + urls = ImageGooglerForm.GetImageUrls(game); + break; + } + catch (Exception ex) + { + SetStatus(Resources.Error + ": " + ex.Message); + Thread.Sleep(1500); + continue; + } + } + } + if (urls != null && urls.Length == 0) + SetStatus(Resources.NotFound + " " + game.Name); + for (int tries = 0; urls != null && tries < 5 && tries < urls.Length; tries++) + { + try + { + var cover = ImageGooglerForm.DownloadImage(urls[tries]); + game.Image = cover; + break; + } + catch (Exception ex) + { + SetStatus(Resources.Error + ": " + ex.Message); + Thread.Sleep(1500); + continue; + } + } + SetProgress(++i, Games.Count); + Thread.Sleep(500); // not so fast, Google don't like it + } + } + + private class GamesTreeStats + { + public List allMenus = new List(); + public int TotalGames = 0; + public long TotalSize = 0; + public long TransferSize = 0; + } + + private void AddMenu(NesMenuCollection menuCollection, Dictionary originalGames, GamesTreeStats stats = null) + { + if (stats == null) + stats = new GamesTreeStats(); + if (!stats.allMenus.Contains(menuCollection)) + stats.allMenus.Add(menuCollection); + int menuIndex = stats.allMenus.IndexOf(menuCollection); + string targetDirectory; + if (menuIndex == 0) + targetDirectory = tempGamesDirectory; + else + targetDirectory = Path.Combine(tempGamesDirectory, string.Format("{0:D3}", menuIndex)); + foreach (var element in menuCollection) + { + if (element is NesMiniApplication) + { + stats.TotalGames++; + var game = element as NesMiniApplication; + var gameSize = game.Size(); + Debug.WriteLine(string.Format("Processing {0} ('{1}'), size: {2}KB", game.Code, game.Name, gameSize / 1024)); + var gameCopy = game.CopyTo(targetDirectory); + stats.TotalSize += gameSize; + stats.TransferSize += gameSize; + stats.TotalGames++; + try + { + if (gameCopy is NesGame && File.Exists((gameCopy as NesGame).GameGeniePath)) + { + (gameCopy as NesGame).ApplyGameGenie(); + File.Delete((gameCopy as NesGame).GameGeniePath); + } + } + catch (GameGenieFormatException ex) + { + ShowError(new Exception(string.Format(Resources.GameGenieFormatError, ex.Code, game.Name)), dontStop: true); + } + catch (GameGenieNotFoundException ex) + { + ShowError(new Exception(string.Format(Resources.GameGenieNotFound, ex.Code, game.Name)), dontStop: true); + } + } + if (element is NesMenuFolder) + { + var folder = element as NesMenuFolder; + if (!stats.allMenus.Contains(folder.ChildMenuCollection)) + { + stats.allMenus.Add(folder.ChildMenuCollection); + AddMenu(folder.ChildMenuCollection, originalGames, stats); + } + folder.ChildIndex = stats.allMenus.IndexOf(folder.ChildMenuCollection); + var folderDir = Path.Combine(targetDirectory, folder.Code); + var folderSize = folder.Save(folderDir); + stats.TotalSize += folderSize; + stats.TransferSize += folderSize; + + } + if (element is NesDefaultGame) + { + var game = element as NesDefaultGame; + stats.TotalSize += game.Size; + originalGames[game.Code] = menuIndex == 0 ? "." : string.Format("{0:D3}", menuIndex); + } + } + } + + private bool ExecuteTool(string tool, string args, string directory = null, bool external = false) + { + byte[] output; + return ExecuteTool(tool, args, out output, directory, external); + } + + private bool ExecuteTool(string tool, string args, out byte[] output, string directory = null, bool external = false) + { + var process = new Process(); + var appDirectory = baseDirectoryInternal; + var fileName = !external ? Path.Combine(toolsDirectory, tool) : tool; + process.StartInfo.FileName = fileName; + process.StartInfo.Arguments = args; + if (string.IsNullOrEmpty(directory)) + directory = appDirectory; + process.StartInfo.WorkingDirectory = directory; + process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; + process.StartInfo.UseShellExecute = false; + process.StartInfo.CreateNoWindow = true; + process.StartInfo.StandardOutputEncoding = Encoding.GetEncoding(866); + process.StartInfo.RedirectStandardInput = true; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.RedirectStandardError = true; + Debug.WriteLine("Executing: " + fileName); + Debug.WriteLine("Arguments: " + args); + Debug.WriteLine("Directory: " + directory); + process.Start(); + string outputStr = process.StandardOutput.ReadToEnd(); + string errorStr = process.StandardError.ReadToEnd(); + process.WaitForExit(); + output = Encoding.GetEncoding(866).GetBytes(outputStr); + Debug.WriteLineIf(outputStr.Length > 0 && outputStr.Length < 300, "Output:\r\n" + outputStr); + Debug.WriteLineIf(errorStr.Length > 0, "Errors:\r\n" + errorStr); + Debug.WriteLine("Exit code: " + process.ExitCode); + return process.ExitCode == 0; + } + + static UInt32 CalcKernelSize(byte[] header) + { + if (Encoding.ASCII.GetString(header, 0, 8) != "ANDROID!") throw new Exception(Resources.InvalidKernelHeader); + UInt32 kernel_size = (UInt32)(header[8] | (header[9] * 0x100) | (header[10] * 0x10000) | (header[11] * 0x1000000)); + UInt32 kernel_addr = (UInt32)(header[12] | (header[13] * 0x100) | (header[14] * 0x10000) | (header[15] * 0x1000000)); + UInt32 ramdisk_size = (UInt32)(header[16] | (header[17] * 0x100) | (header[18] * 0x10000) | (header[19] * 0x1000000)); + UInt32 ramdisk_addr = (UInt32)(header[20] | (header[21] * 0x100) | (header[22] * 0x10000) | (header[23] * 0x1000000)); + UInt32 second_size = (UInt32)(header[24] | (header[25] * 0x100) | (header[26] * 0x10000) | (header[27] * 0x1000000)); + UInt32 second_addr = (UInt32)(header[28] | (header[29] * 0x100) | (header[30] * 0x10000) | (header[31] * 0x1000000)); + UInt32 tags_addr = (UInt32)(header[32] | (header[33] * 0x100) | (header[34] * 0x10000) | (header[35] * 0x1000000)); + UInt32 page_size = (UInt32)(header[36] | (header[37] * 0x100) | (header[38] * 0x10000) | (header[39] * 0x1000000)); + UInt32 dt_size = (UInt32)(header[40] | (header[41] * 0x100) | (header[42] * 0x10000) | (header[43] * 0x1000000)); + UInt32 pages = 1; + pages += (kernel_size + page_size - 1) / page_size; + pages += (ramdisk_size + page_size - 1) / page_size; + pages += (second_size + page_size - 1) / page_size; + pages += (dt_size + page_size - 1) / page_size; + return pages * page_size; + } + + + bool YesForAllPatches = false; + public ICollection AddGames(IEnumerable files, Form parentForm = null) + { + var apps = new List(); + addedApplications = null; + //bool NoForAllUnsupportedMappers = false; + bool YesForAllUnsupportedMappers = false; + YesForAllPatches = false; + int count = 0; + SetStatus(Resources.AddingGames); + foreach (var sourceFileName in files) + { + NesMiniApplication app = null; + try + { + var fileName = sourceFileName; + var ext = Path.GetExtension(sourceFileName).ToLower(); + bool? needPatch = YesForAllPatches ? (bool?)true : null; + byte[] rawData = null; + string tmp = null; + if (ext == ".7z" || ext == ".zip" || ext == ".rar") + { + SevenZipExtractor.SetLibraryPath(Path.Combine(baseDirectoryInternal, IntPtr.Size == 8 ? @"tools\7z64.dll" : @"tools\7z.dll")); + using (var szExtractor = new SevenZipExtractor(sourceFileName)) + { + var filesInArchive = new List(); + var gameFilesInArchive = new List(); + foreach (var f in szExtractor.ArchiveFileNames) + { + var e = Path.GetExtension(f).ToLower(); + if (e == ".desktop" || AppTypeCollection.GetAppByExtension(e) != null) + gameFilesInArchive.Add(f); + filesInArchive.Add(f); + } + if (gameFilesInArchive.Count == 1) // Only one NES file (or app) + { + fileName = gameFilesInArchive[0]; + } + else if (gameFilesInArchive.Count > 1) // Many NES files, need to select + { + var r = SelectFileFromThread(gameFilesInArchive.ToArray()); + if (r == DialogResult.OK) + fileName = selectedFile; + else if (r == DialogResult.Ignore) + fileName = sourceFileName; + else continue; + } + else if (filesInArchive.Count == 1) // No NES files but only one another file + { + fileName = filesInArchive[0]; + } + else // Need to select + { + var r = SelectFileFromThread(filesInArchive.ToArray()); + if (r == DialogResult.OK) + fileName = selectedFile; + else if (r == DialogResult.Ignore) + fileName = sourceFileName; + else continue; + } + if (fileName != sourceFileName) + { + var o = new MemoryStream(); + if (Path.GetExtension(fileName).ToLower() == ".desktop" // App in archive, need the whole directory + || szExtractor.ArchiveFileNames.Contains(Path.GetFileNameWithoutExtension(fileName) + ".jpg") // Or it has cover in archive + || szExtractor.ArchiveFileNames.Contains(Path.GetFileNameWithoutExtension(fileName) + ".png")) + { + tmp = Path.Combine(Path.GetTempPath(), fileName); + Directory.CreateDirectory(tmp); + szExtractor.ExtractArchive(tmp); + fileName = Path.Combine(tmp, fileName); + } + else + { + szExtractor.ExtractFile(fileName, o); + rawData = new byte[o.Length]; + o.Seek(0, SeekOrigin.Begin); + o.Read(rawData, 0, (int)o.Length); + } + } + } + } + if (Path.GetExtension(fileName).ToLower() == ".nes") + { + try + { + app = NesGame.Import(fileName, sourceFileName, YesForAllUnsupportedMappers ? (bool?)true : null, ref needPatch, needPatchCallback, this, rawData); + + // Trying to import Game Genie codes + var lGameGeniePath = Path.Combine(Path.GetDirectoryName(fileName), Path.GetFileNameWithoutExtension(fileName) + ".xml"); + if (File.Exists(lGameGeniePath)) + { + GameGenieDataBase lGameGenieDataBase = new GameGenieDataBase(app); + lGameGenieDataBase.ImportCodes(lGameGeniePath, true); + lGameGenieDataBase.Save(); + } + } + catch (Exception ex) + { + if (ex is UnsupportedMapperException || ex is UnsupportedFourScreenException) + { + var r = MessageBoxFromThread(this, + (ex is UnsupportedMapperException) + ? string.Format(Resources.MapperNotSupported, Path.GetFileName(fileName), (ex as UnsupportedMapperException).ROM.Mapper) + : string.Format(Resources.FourScreenNotSupported, Path.GetFileName(fileName)), + Resources.AreYouSure, + files.Count() <= 1 ? MessageBoxButtons.YesNo : MessageBoxButtons.AbortRetryIgnore, + MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2, true); + if (r == DialogResult.Abort) + YesForAllUnsupportedMappers = true; + if (r == DialogResult.Yes || r == DialogResult.Abort || r == DialogResult.Retry) + app = NesGame.Import(fileName, sourceFileName, true, ref needPatch, needPatchCallback, this, rawData); + else + continue; + } + else throw ex; + } + } + else + { + app = NesMiniApplication.Import(fileName, sourceFileName, rawData); + } + if (!string.IsNullOrEmpty(tmp) && Directory.Exists(tmp)) Directory.Delete(tmp, true); + ConfigIni.SelectedGames += ";" + app.Code; + } + catch (Exception ex) + { + if (ex is ThreadAbortException) return null; + Debug.WriteLine(ex.Message + ex.StackTrace); + ShowError(ex, true); + } + if (app != null) + apps.Add(app); + SetProgress(++count, files.Count()); + } + addedApplications = apps; + return apps; // Added games/apps + } + + private bool needPatchCallback(Form parentForm, string nesFileName) + { + if (GamesToAdd == null || GamesToAdd.Count() <= 1) + { + return MessageBoxFromThread(parentForm, + string.Format(Resources.PatchQ, Path.GetFileName(nesFileName)), + Resources.PatchAvailable, + MessageBoxButtons.YesNo, + MessageBoxIcon.Question, + MessageBoxDefaultButton.Button1, false) == DialogResult.Yes; + } + else + { + var r = MessageBoxFromThread(parentForm, + string.Format(Resources.PatchQ, Path.GetFileName(nesFileName)), + Resources.PatchAvailable, + MessageBoxButtons.AbortRetryIgnore, + MessageBoxIcon.Question, + MessageBoxDefaultButton.Button2, true); + if (r == DialogResult.Abort) + YesForAllPatches = true; + return r != DialogResult.Ignore; + } + } + + private void WorkerForm_FormClosing(object sender, FormClosingEventArgs e) + { + if ((thread != null) && (e.CloseReason == CloseReason.UserClosing)) + { + if (MessageBox.Show(this, Resources.DoYouWantCancel, Resources.AreYouSure, MessageBoxButtons.YesNo, MessageBoxIcon.Warning) + == System.Windows.Forms.DialogResult.No) + { + e.Cancel = true; + return; + } + if (thread != null) thread.Abort(); + TaskbarProgress.SetState(this, TaskbarProgress.TaskbarStates.NoProgress); + TaskbarProgress.SetValue(this, 0, 1); + } + } + } +} diff --git a/hakchi_gui.csproj b/hakchi_gui.csproj index 3f8c4f83..b27318a0 100644 --- a/hakchi_gui.csproj +++ b/hakchi_gui.csproj @@ -442,6 +442,12 @@ FoldersManagerForm.cs + + UserControl + + + GameDetail.cs + UserControl @@ -958,6 +964,9 @@ FoldersManagerForm.cs + + GameDetail.cs + GameSelecter.cs @@ -1652,22 +1661,22 @@ False - Include - True + Include + True File False - Include - True + Include + True File -- cgit v1.2.3