diff --git a/WiiLink-Patcher-CLI/WiiLink Patcher.csproj b/WiiLink-Patcher-CLI/WiiLink Patcher.csproj index 2181033..e2bb219 100644 --- a/WiiLink-Patcher-CLI/WiiLink Patcher.csproj +++ b/WiiLink-Patcher-CLI/WiiLink Patcher.csproj @@ -30,6 +30,7 @@ + diff --git a/WiiLink-Patcher-CLI/WiiLink_Patcher.cs b/WiiLink-Patcher-CLI/WiiLink_Patcher.cs index 8fee0d7..009b5b7 100644 --- a/WiiLink-Patcher-CLI/WiiLink_Patcher.cs +++ b/WiiLink-Patcher-CLI/WiiLink_Patcher.cs @@ -3,81 +3,94 @@ using System.Runtime.InteropServices; using Spectre.Console; using libWiiSharp; +using System.Net; +using Newtonsoft.Json.Linq; + +// Author: PablosCorner and WiiLink Team +// Project: WiiLink Patcher (CLI Version) +// Description: WiiLink Patcher (CLI Version) is a command-line interface to patch and revive the exclusive Japanese Wii Channels that were shut down, along with some WiiConnect24 Channels. class WiiLink_Patcher { - /*###### Build Info ######*/ - static readonly string version = "v1.1.2h2"; + //// Build Info //// + static readonly string version = "v1.2.0"; static readonly string copyrightYear = DateTime.Now.Year.ToString(); - static readonly string lastBuild = "June 29nd, 2023"; - static readonly string at = "1:37 PM"; + static readonly string buildDate = "July 19th, 2023"; + static readonly string buildTime = "7:40 PM"; static string? sdcard = DetectSDCard(); - static readonly string wiiLinkPatcherUrl = "https://patcher.wiilink24.com"; - /*########################*/ - - /*###### Setup Info ######*/ - // Core Channel variables + //////////////////// + + //// Setup Info //// + // Express Install variables static public string reg = ""; static public string lang = ""; static public string demae_version = ""; - - // WiiConnect24 variables + static public bool installWC24 = false; static public string nc_reg = ""; static public string forecast_reg = ""; - - // Misc. setup variables static public string platformType = ""; - static public bool sd_connected = false; + static public bool installKirbyTV = false; + static Dictionary patchingProgress_express = new Dictionary(); + + // Custom Install variables + static List coreChannels_selection = new List(); + static List wiiConnect24Channels_selection = new List(); + static string spdVersion_custom = ""; + static bool inCompatabilityMode = false; + static Dictionary patchingProgress_custom = new Dictionary(); + // Misc. variables static public string task = ""; static public string curCmd = ""; static public int exitCode = -1; static readonly string curDir = Directory.GetCurrentDirectory(); - static string[] patching_progress = new string[7]; + static readonly string tempDir = Path.Join(Path.GetTempPath(), "WiiLink_Patcher"); + static bool DEBUG_MODE = false; + static public string localizeLang = "en"; // English by default + static JObject? localizedText = null; // Get current console window size static int console_width = 0; static int console_height = 0; - - static bool DEBUG_MODE = false; - /*########################*/ + //////////////////// static void PrintHeader() { - // Clear console Console.Clear(); - string borderChar = "="; - string borderLine = ""; - int columns = Console.WindowWidth; - - AnsiConsole.MarkupLine($"[bold]WiiLink Patcher {version} - (c) {copyrightYear} WiiLink[/] (Updated on {lastBuild} at {at} EST)"); - - for (int i = 0; i < columns; i++) + string headerText = $"WiiLink Patcher {version} - (c) {copyrightYear} WiiLink"; + if (localizeLang != "en") { - borderLine += borderChar; + headerText = $"{localizedText?["Header"]}" + .Replace("{version}", version) + .Replace("{copyrightYear}", copyrightYear); } - AnsiConsole.WriteLine(borderLine); - AnsiConsole.WriteLine(); + AnsiConsole.MarkupLine(headerText); + + string borderChar = "="; + string borderLine = new string(borderChar[0], Console.WindowWidth); + + AnsiConsole.MarkupLine($"{borderLine}\n"); } - // Discord Announcement - static void PrintAnnouncement() + static void PrintNotice() { - string markupTitle = "[bold lime]( Announcement )[/]"; - var markupText = new Markup("[bold]If you have any issues with the patcher or services offered by WiiLink, please report them here:[/]\n[link bold lime]https://discord.gg/WiiLink[/] - Thank you."); + string title = $"[bold lime] Notice [/]"; + string text = inCompatabilityMode ? "[bold]If you have any issues with the patcher or services offered by WiiLink, please report them on our [bold]Discord Server[/]:[/]\n[bold link]https://discord.gg/WiiLink[/]" : $"[bold]If you have any issues with the patcher or services offered by WiiLink, please report them on our [lime link=https://discord.gg/WiiLink]Discord Server[/]![/]"; - var panel = new Panel(markupText) + var panel = new Panel(text) { - Header = new PanelHeader(markupTitle, Justify.Center), - Border = BoxBorder.Rounded, - BorderStyle = new Style(Color.Green), + Header = new PanelHeader(title, Justify.Center), + Border = inCompatabilityMode ? BoxBorder.Ascii : BoxBorder.Heavy, + BorderStyle = new Style(Color.Lime), Expand = true, }; + AnsiConsole.Write(panel); - Console.WriteLine(); + AnsiConsole.WriteLine(); + } static string? DetectSDCard() @@ -94,39 +107,94 @@ static void PrintAnnouncement() // User choice static int UserChoose(string choices) { + var choose_localizedText = new Dictionary { + {"en", "Choose: "}, + {"es", "Elija: "}, + {"fr", "Choisir: "}, + {"jp", "選択: "} + }; + ConsoleKeyInfo keyPressed; do { - Console.Write("Choose: "); + Console.Write($"{choose_localizedText[localizeLang]}"); keyPressed = Console.ReadKey(intercept: true); - if (choices.Contains(keyPressed.KeyChar)) - { - Console.WriteLine(); - return choices.IndexOf(keyPressed.KeyChar) + 1; - } - else + + switch (keyPressed.Key) { - return -1; + case ConsoleKey.Escape: + return -1; + case ConsoleKey.Enter: + Console.WriteLine(); + return 0; + case ConsoleKey.Backspace: + if (Console.CursorLeft > 0) + Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop); + else + { + Console.SetCursorPosition(0, Console.CursorTop - 1); + Console.Write(new string(' ', Console.WindowWidth)); + Console.SetCursorPosition(0, Console.CursorTop - 1); + } + break; + default: + if (choices.Contains(keyPressed.KeyChar)) + { + Console.WriteLine(); + return choices.IndexOf(keyPressed.KeyChar) + 1; + } + else + { + if (Console.CursorLeft > 8) + Console.SetCursorPosition(Console.CursorLeft - 8, Console.CursorTop); + else + Console.SetCursorPosition(0, Console.CursorTop); + } + break; } - } while (keyPressed.Key != ConsoleKey.Escape); + } while (true); } // Credits function - static void Credits() + static void CreditsScreen() { - { - PrintHeader(); - AnsiConsole.MarkupLine("[bold lime]Credits[/]:\n"); - AnsiConsole.MarkupLine(" - [bold]Sketch:[/] WiiLink Founder\n"); - AnsiConsole.MarkupLine(" - [bold]PablosCorner:[/] WiiLink Patcher Maintainer\n"); - AnsiConsole.MarkupLine(" - [bold]TheShadowEevee:[/] Sharpii-NetCore Developer\n"); - AnsiConsole.MarkupLine(" - [bold]Joshua MacDonald:[/] Xdelta3\n"); - AnsiConsole.MarkupLine(" - [bold]leathl and WiiDatabase:[/] libWiiSharp developers\n"); - AnsiConsole.MarkupLine(" - [bold]SnowflakePowered:[/] VCDiff\n"); - AnsiConsole.MarkupLine("[bold lime]WiiLink[/] [bold lime]website:[/] https://wiilink24.com\n"); - AnsiConsole.MarkupLine("[bold]Press any key to go back to the main menu[/]"); - Console.ReadKey(); - } + PrintHeader(); + + // Build info + AnsiConsole.MarkupLine($"This build was compiled on [bold lime]{buildDate}[/] at [bold lime]{buildTime}[/].\n"); + + // Credits table + var creditTable = new Table().Border(inCompatabilityMode ? TableBorder.None : TableBorder.DoubleEdge); + creditTable.AddColumn(new TableColumn($"[bold lime]Credits[/]").Centered()); + + // Credits grid + var creditGrid = new Grid().AddColumn().AddColumn(); + + creditGrid.AddRow(new Text("Sketch", new Style(Color.Lime, null, Decoration.Bold)).RightJustified(), new Text("WiiLink Founder", new Style(null, null, Decoration.Bold))); + creditGrid.AddRow(new Text("PablosCorner", new Style(Color.Lime, null, Decoration.Bold)).RightJustified(), new Text("WiiLink Patcher Developer", new Style(null, null, Decoration.Bold))); + creditGrid.AddRow(new Text("leathl and WiiDatabase", new Style(Color.Lime, null, Decoration.Bold)).RightJustified(), new Text("libWiiSharp developers", new Style(null, null, Decoration.Bold))); + creditGrid.AddRow(new Text("SnowflakePowered", new Style(Color.Lime, null, Decoration.Bold)).RightJustified(), new Text("VCDiff", new Style(null, null, Decoration.Bold))); + + // Add the grid to the table + creditTable.AddRow(creditGrid).Centered(); + AnsiConsole.Write(creditTable); + + // Special thanks grid + AnsiConsole.MarkupLine("\n[bold lime]Special thanks to:[/]\n"); + + var specialThanksGrid = new Grid().AddColumn().AddColumn(); + + specialThanksGrid.AddRow(" ● [bold]TheShadowEevee[/]", "- Pointing me in the right direction with implementing libWiiSharp!"); + specialThanksGrid.AddRow(" ● [bold]Our Testers[/]", "- For testing the patcher and reporting bugs/anomalies!"); + specialThanksGrid.AddRow(" ● [bold]You![/]", "- For your continued support of WiiLink!"); + + AnsiConsole.Write(specialThanksGrid); + + AnsiConsole.MarkupLine("\n[bold lime]WiiLink website:[/] [link]https://wiilink24.com[/]"); + AnsiConsole.MarkupLine("[bold lime]Github repository:[/] [link]https://github.com/WiiLink24/WiiLink24-Patcher[/]\n"); + + AnsiConsole.MarkupLine("[bold]Press any key to go back to settings...[/]"); + Console.ReadKey(); } static void DownloadFile(string URL, string dest, string name) @@ -136,6 +204,8 @@ static void DownloadFile(string URL, string dest, string name) { task = $"Downloading {name}"; curCmd = $"DownloadFile({URL}, {dest}, {name})"; + if (DEBUG_MODE) + AnsiConsole.MarkupLine($"[lime]Downloading [bold]{name}[/] from [bold]{URL}[/] to [bold]{dest}[/][/]..."); try { // Create a new HttpClient instance to handle the download. @@ -156,13 +226,15 @@ static void DownloadFile(string URL, string dest, string name) else { int statusCode = (int)response.StatusCode; - ErrorScreen(statusCode, $"Failed to download {name} from {URL} to {dest}"); + ErrorScreen(statusCode, $"Failed to download [bold]{name}[/] from [bold]{URL}[/] to [bold]{dest}[/]"); } } } catch (Exception e) { AnsiConsole.MarkupLine($"[bold red]ERROR:[/] {e.Message}"); + AnsiConsole.MarkupLine("Press any key to try again..."); + Console.ReadKey(true); } } } @@ -180,7 +252,7 @@ static string DownloadNUS(string titleID, string outputDir, string? appVer = nul // Check that the title ID is the correct length. if (titleID.Length != 16) { - ErrorScreen(-1, "Title ID must be 16 characters long"); + ErrorScreen(16, "Title ID must be 16 characters long"); return ""; } @@ -242,8 +314,8 @@ static void PackWAD(string unpackPath, string outputWADDir) static void DownloadPatch(string folderName, string patchInput, string patchOutput, string channelName) { string patchUrl = $"{wiiLinkPatcherUrl}/{folderName.ToLower()}/{patchInput}"; - string patchDestinationPath = Path.Join("WiiLink_Patcher", folderName, patchOutput); - + string patchDestinationPath = Path.Join(tempDir, "Patches", folderName, patchOutput); + if (DEBUG_MODE) { AnsiConsole.MarkupLine($"[bold yellow]URL:[/] {patchUrl}"); @@ -252,9 +324,9 @@ static void DownloadPatch(string folderName, string patchInput, string patchOutp Console.ReadKey(true); } - // If WiiLink_Patcher/{folderName} doesn't exist, make it - if (!Directory.Exists(Path.Join("WiiLink_Patcher", folderName))) - Directory.CreateDirectory(Path.Join("WiiLink_Patcher", folderName)); + // If tempDir/Patches/{folderName} doesn't exist, make it + if (!Directory.Exists(Path.Join(tempDir, "Patches", folderName))) + Directory.CreateDirectory(Path.Join(tempDir, "Patches", folderName)); DownloadFile(patchUrl, patchDestinationPath, channelName); } @@ -283,7 +355,7 @@ static void ApplyPatch(FileStream original, FileStream patch, FileStream output) } } - static void DownloadSPD() + static void DownloadSPD(string platformType = "") { string spdUrl = ""; string spdDestinationPath = ""; @@ -309,13 +381,13 @@ static void DownloadSPD() // Patches the Japanese-exclusive channels - static void PatchCoreChannel(string channelName, string channelTitle, string titleID, string[] patchFiles, string[] appFiles, string? appVer = null) + static void PatchCoreChannel(string channelName, string channelTitle, string titleID, List> patchFilesDict, string? appVer = null, string? lang = null) { // Set up folder paths and file names - string titleFolder = Path.Join("unpack"); - string tempFolder = Path.Join("unpack-patched"); - string patchFolder = Path.Join("WiiLink_Patcher", channelName); - string outputChannel = Path.Join("WAD", $"{channelTitle} ({lang}).wad"); + string titleFolder = Path.Join(tempDir, "Unpack"); + string tempFolder = Path.Join(tempDir, "Unpack_Patched"); + string patchFolder = Path.Join(tempDir, "Patches", channelName); + string outputChannel = lang == null ? Path.Join("WAD", $"{channelTitle}.wad") : Path.Join("WAD", $"{channelTitle} ({lang}).wad"); string urlSubdir = channelName.ToLower(); // Create unpack and unpack-patched folders @@ -332,18 +404,19 @@ static void PatchCoreChannel(string channelName, string channelTitle, string tit task = $"Downloading patched TMD file for {channelTitle}"; DownloadFile($"{wiiLinkPatcherUrl}/{urlSubdir}/{channelName}.tmd", Path.Join(titleFolder, $"{titleID}.tmd"), channelTitle); - // Apply delta patches to the app files + //// Apply delta patches to the app files //// task = $"Applying delta patches for {channelTitle}"; + // First delta patch if (reg == "EN" || channelName == "Dominos") - ApplyPatch(File.OpenRead(Path.Join(titleFolder, appFiles[0] + ".app")), File.OpenRead(Path.Join(patchFolder, patchFiles[0] + ".delta")), File.OpenWrite(Path.Join(tempFolder, appFiles[0] + ".app"))); + ApplyPatch(File.OpenRead(Path.Join(titleFolder, $"{patchFilesDict[0].Value}.app")), File.OpenRead(Path.Join(patchFolder, $"{patchFilesDict[0].Key}.delta")), File.OpenWrite(Path.Join(tempFolder, $"{patchFilesDict[0].Value}.app"))); // Second delta patch - ApplyPatch(File.OpenRead(Path.Join(titleFolder, appFiles[1] + ".app")), File.OpenRead(Path.Join(patchFolder, patchFiles[1] + ".delta")), File.OpenWrite(Path.Join(tempFolder, appFiles[1] + ".app"))); + ApplyPatch(File.OpenRead(Path.Join(titleFolder, $"{patchFilesDict[1].Value}.app")), File.OpenRead(Path.Join(patchFolder, $"{patchFilesDict[1].Key}.delta")), File.OpenWrite(Path.Join(tempFolder, $"{patchFilesDict[1].Value}.app"))); // Third delta patch if (reg == "EN" || channelName == "Dominos" || channelName == "WiinoMa") - ApplyPatch(File.OpenRead(Path.Join(titleFolder, appFiles[2] + ".app")), File.OpenRead(Path.Join(patchFolder, patchFiles[2] + ".delta")), File.OpenWrite(Path.Join(tempFolder, appFiles[2] + ".app"))); + ApplyPatch(File.OpenRead(Path.Join(titleFolder, $"{patchFilesDict[2].Value}.app")), File.OpenRead(Path.Join(patchFolder, $"{patchFilesDict[2].Key}.delta")), File.OpenWrite(Path.Join(tempFolder, $"{patchFilesDict[2].Value}.app"))); // Copy patched files to unpack folder task = $"Copying patched files for {channelTitle}"; @@ -359,59 +432,84 @@ static void PatchCoreChannel(string channelName, string channelTitle, string tit } // This function patches the WiiConnect24 channels - static void PatchWC24Channel(string channelName, string channelTitle, int channelVersion, string channelRegion, string titleID, string patchFile, string appFile) + static void PatchWC24Channel(string channelName, string channelTitle, int channelVersion, string? channelRegion, string titleID, string patchFile, string appFile) { // Define the necessary paths and filenames - string titleFolder = Path.Join("unpack"); - string tempFolder = Path.Join("unpack-patched"); - string patchFolder = Path.Join("WiiLink_Patcher", channelName); - string outputWad = Path.Join("WAD", $"{channelTitle} [{channelRegion}] (WiiLink).wad"); + string titleFolder = Path.Join(tempDir, "Unpack"); + string tempFolder = Path.Join(tempDir, "Unpack_Patched"); + string patchFolder = Path.Join(tempDir, "Patches", channelName); + + // Name the output WAD file + string outputWad; + if (string.IsNullOrEmpty(channelRegion)) + outputWad = Path.Join("WAD", $"{channelTitle} (WiiLink).wad"); + else + outputWad = Path.Join("WAD", $"{channelTitle} [{channelRegion}] (WiiLink).wad"); // Create unpack and unpack-patched folders Directory.CreateDirectory(titleFolder); Directory.CreateDirectory(tempFolder); - // Using Discord links to temporarily host the Certs and Tiks till I can find a more permanent host - var tempLinks = new Dictionary>{ + // Define the URLs for the necessary files + var discordURLs = new Dictionary>{ // Nintendo Channel Certs and Tiks {"000100014841544a", new Dictionary { {"cert", "https://cdn.discordapp.com/attachments/253286648291393536/1123709388641800263/000100014841544a.cert"}, + {"tmd", ""}, {"tik", "https://cdn.discordapp.com/attachments/253286648291393536/1123709425149038612/000100014841544a.tik"} }}, {"0001000148415445", new Dictionary { {"cert", "https://cdn.discordapp.com/attachments/253286648291393536/1123709388998324235/0001000148415445.cert"}, + {"tmd", ""}, {"tik", "https://cdn.discordapp.com/attachments/253286648291393536/1123709425518129173/0001000148415445.tik"} }}, {"0001000148415450", new Dictionary { {"cert", "https://cdn.discordapp.com/attachments/253286648291393536/1123709389329678417/0001000148415450.cert"}, + {"tmd", ""}, {"tik", "https://cdn.discordapp.com/attachments/253286648291393536/1123709425950130236/0001000148415450.tik"} }}, // Forecast Channel Certs {"000100024841464a", new Dictionary { {"cert", "https://cdn.discordapp.com/attachments/253286648291393536/1123709479372980326/000100024841464a.cert"}, + {"tmd", ""}, {"tik", ""} }}, {"0001000248414645", new Dictionary { {"cert", "https://cdn.discordapp.com/attachments/253286648291393536/1123709478697709638/0001000248414645.cert"}, + {"tmd", ""}, {"tik", ""} }}, {"0001000248414650", new Dictionary { {"cert", "https://cdn.discordapp.com/attachments/253286648291393536/1123709479016484967/0001000248414650.cert"}, + {"tmd", ""}, {"tik", ""} }}, + // Kirby TV Channel Cert, TMD, and TIK + {"0001000148434d50", new Dictionary { + {"cert", "https://cdn.discordapp.com/attachments/253286648291393536/1123828090754314261/0001000148434d50.cert"}, + {"tmd", "https://cdn.discordapp.com/attachments/253286648291393536/1123828527918231563/0001000148434d50.tmd"}, + {"tik", "https://cdn.discordapp.com/attachments/253286648291393536/1123828811860017203/0001000148434d50.tik"} + }} }; - //// Download the necessary files for the channel + //// Download the necessary files for the channel //// task = $"Downloading necessary files for {channelTitle}"; - DownloadFile(tempLinks[titleID]["cert"], Path.Join(titleFolder, $"{titleID}.cert"), $"{channelTitle} cert"); + + // Download the cetk file + DownloadFile(discordURLs[titleID]["cert"], Path.Join(titleFolder, $"{titleID}.cert"), $"{channelTitle} cert"); + // Download the tik file if it exists - if (tempLinks[titleID]["tik"] != "") - DownloadFile(tempLinks[titleID]["tik"], Path.Join(titleFolder, "cetk"), $"{channelTitle} tik"); + if (discordURLs[titleID]["tik"] != "") + DownloadFile(discordURLs[titleID]["tik"], Path.Join(titleFolder, "cetk"), $"{channelTitle} tik"); // Extract the necessary files for the channel task = $"Extracting stuff for {channelTitle}"; DownloadNUS(titleID, titleFolder, channelVersion.ToString(), true); + // Download the TMD file if it exists + if (discordURLs[titleID]["tmd"] != "") + DownloadFile(discordURLs[titleID]["tmd"], Path.Join(titleFolder, $"tmd.{channelVersion}"), $"{channelTitle} TMD"); + // Rename the extracted files task = $"Renaming files for {channelTitle}"; File.Move(Path.Join(titleFolder, $"tmd.{channelVersion}"), Path.Join(titleFolder, $"{titleID}.tmd")); @@ -421,11 +519,27 @@ static void PatchWC24Channel(string channelName, string channelTitle, int channe task = $"Applying delta patch for {channelTitle}"; ApplyPatch(File.OpenRead(Path.Join(titleFolder, $"{appFile}.app")), File.OpenRead(Path.Join(patchFolder, $"{patchFile}.delta")), File.OpenWrite(Path.Join(tempFolder, $"{appFile}.app"))); + // Apply the special second Forecast patch if the channel is Forecast + if (channelName == "forecast") + { + task = $"Applying Forecast Channel Fix"; + // Delete existing 0000000f.app and replace it with this one, then rename it to 0000000f.app + File.Delete(Path.Join(titleFolder, "0000000f.app")); + + // Since the patch doesn't seem to work correctly, I'm using this as a workaround + DownloadFile("https://cdn.discordapp.com/attachments/1061653146872582204/1127695471666810950/banner_rso.arc", Path.Join(tempFolder, "banner_rso.arc"), "Forecast Channel Fix"); + + File.Move(Path.Join(tempFolder, "banner_rso.arc"), Path.Join(titleFolder, "0000000f.app")); + } + // Copy the patched files to the unpack folder task = $"Copying patched files for {channelTitle}"; - try { + try + { CopyFolder(tempFolder, titleFolder); - } catch (Exception e) { + } + catch (Exception e) + { ErrorScreen(e.HResult, e.Message); } @@ -439,26 +553,26 @@ static void PatchWC24Channel(string channelName, string channelTitle, int channe } - // Install Choose + // Install Choose (Express Install) static void CoreChannel_LangSetup() { while (true) { PrintHeader(); - Console.WriteLine("\u001b[1;32mExpress Install\u001b[0m"); - Console.WriteLine("\nHello {0}! Welcome to the Express Installation of WiiLink!", Environment.UserName); - Console.WriteLine("The patcher will download any files that are required to run the patcher."); - Console.WriteLine(); - Console.WriteLine("\u001b[1mStep 1: Choose core channel language\u001b[0m"); - Console.WriteLine(); - Console.WriteLine("For \u001b[1mWii Room\u001b[0m, \u001b[1mDigicam Print Channel\u001b[0m, and \u001b[1mFood Channel\u001b[0m, which language would you like to select?"); - Console.WriteLine(); - Console.WriteLine("1. English Translation"); - Console.WriteLine("2. Japanese"); - Console.WriteLine(); - Console.WriteLine("3. Go Back to Main Menu"); - Console.WriteLine(); + AnsiConsole.MarkupLine("[bold lime]Express Install[/]\n"); + + AnsiConsole.MarkupLine($"Hello [bold lime]{Environment.UserName}[/]! Welcome to the Express Installation of WiiLink!"); + AnsiConsole.MarkupLine("The patcher will download any files that are required to run the patcher.\n"); + + AnsiConsole.MarkupLine("[bold]Step 1: Choose core channel language[/]\n"); + + AnsiConsole.MarkupLine("For [bold]Wii Room[/], [bold]Digicam Print Channel[/], and [bold]Food Channel[/], which language would you like to select?\n"); + + AnsiConsole.MarkupLine("1. English Translation"); + AnsiConsole.MarkupLine("2. Japanese\n"); + + AnsiConsole.MarkupLine("3. Go Back to Main Menu\n"); int choice = UserChoose("123"); switch (choice) @@ -475,8 +589,7 @@ static void CoreChannel_LangSetup() NCSetup(); break; case 3: - // Go back to main menu - MainMenu(); + MainMenu(); // Go back to main menu return; default: break; @@ -485,43 +598,82 @@ static void CoreChannel_LangSetup() } - // Configure Demae Channel (if English was selected) + // Configure Demae Channel (if English was selected) [Express Install] static void DemaeConfiguration() { while (true) { PrintHeader(); - AnsiConsole.MarkupLine("[bold lime]Express Install[/]"); - Console.WriteLine(); - AnsiConsole.MarkupLine("[bold]Step 1B: Choose Food Channel version[/]"); - Console.WriteLine(); - AnsiConsole.MarkupLine("For [bold]Food Channel[/], which version would you like to install?"); - Console.WriteLine(); + AnsiConsole.MarkupLine("[bold lime]Express Install[/]\n"); + + AnsiConsole.MarkupLine("[bold]Step 1B: Choose Food Channel version[/]\n"); + + AnsiConsole.MarkupLine("For [bold]Food Channel[/], which version would you like to install?\n"); + AnsiConsole.MarkupLine("1. Standard [bold](Fake Ordering)[/]"); AnsiConsole.MarkupLine("2. Domino's [bold](US and Canada only)[/]"); - AnsiConsole.MarkupLine("3. Deliveroo [bold](Select EU countries only)[/]"); - Console.WriteLine(); - Console.WriteLine("4. Go Back to Main Menu"); - Console.WriteLine(); + AnsiConsole.MarkupLine("3. Deliveroo [bold](Select EU countries only)[/]\n"); + + Console.WriteLine("4. Go Back to Main Menu\n"); int choice = UserChoose("1234"); switch (choice) { case 1: demae_version = "standard"; - NCSetup(); + WiiConnect24Setup(); break; case 2: demae_version = "dominos"; - NCSetup(); + WiiConnect24Setup(); break; case 3: demae_version = "deliveroo"; + WiiConnect24Setup(); + break; + case 4: // Go back to main menu + MainMenu(); + break; + default: + break; + } + } + } + + // Ask user if they want to install WiiLink's WiiConnect24 services (Express Install) + static void WiiConnect24Setup() + { + while (true) + { + PrintHeader(); + + AnsiConsole.MarkupLine("[bold lime]Express Install[/]\n"); + + AnsiConsole.MarkupLine("Would you like to install [bold]WiiLink's WiiConnect24 services[/]?\n"); + + AnsiConsole.MarkupLine("Services that would be installed:\n"); + + AnsiConsole.MarkupLine(" ● Nintendo Channel"); + AnsiConsole.MarkupLine(" ● Forecast Channel\n"); + + Console.WriteLine("1. Yes"); + Console.WriteLine("2. No\n"); + + Console.WriteLine("3. Go Back to Main Menu\n"); + + int choice = UserChoose("123"); + switch (choice) + { + case 1: + installWC24 = true; NCSetup(); break; - case 4: - // Go back to main menu + case 2: + installWC24 = false; + ChoosePlatform(); + break; + case 3: // Go back to main menu MainMenu(); break; default: @@ -530,23 +682,23 @@ static void DemaeConfiguration() } } - // Configure Nintendo Channel + // Configure Nintendo Channel (Express Install) static void NCSetup() { while (true) { PrintHeader(); - AnsiConsole.MarkupLine("[bold lime]Express Install[/]"); - Console.WriteLine(); - AnsiConsole.MarkupLine("[bold]Step 2: Choose Nintendo Channel region[/]"); - Console.WriteLine(); - AnsiConsole.MarkupLine("For [bold]Nintendo Channel[/], which region would you like to install?"); - Console.WriteLine(); + AnsiConsole.MarkupLine("[bold lime]Express Install[/]\n"); + + AnsiConsole.MarkupLine("[bold]Step 2: Choose Nintendo Channel region[/]\n"); + + AnsiConsole.MarkupLine("For [bold]Nintendo Channel[/], which region would you like to install?\n"); + Console.WriteLine("1. North America"); Console.WriteLine("2. PAL"); - Console.WriteLine("3. Japan"); - Console.WriteLine(); + Console.WriteLine("3. Japan\n"); + Console.WriteLine("4. Go Back to Main Menu"); Console.WriteLine(); @@ -565,8 +717,7 @@ static void NCSetup() nc_reg = "Japan"; ForecastSetup(); break; - case 4: - // Go back to main menu + case 4: // Go back to main menu MainMenu(); break; default: @@ -576,43 +727,82 @@ static void NCSetup() } - // Configure Forecast Channel + // Configure Forecast Channel (Express Install) static void ForecastSetup() { while (true) { PrintHeader(); - AnsiConsole.MarkupLine("[bold lime]Express Install[/]"); - Console.WriteLine(); - AnsiConsole.MarkupLine("[bold]Step 3: Choose Forecast Channel region[/]"); - Console.WriteLine(); - AnsiConsole.MarkupLine("For [bold]Forecast Channel[/], which region would you like to install?"); - Console.WriteLine(); + AnsiConsole.MarkupLine("[bold lime]Express Install[/]\n"); + + AnsiConsole.MarkupLine("[bold]Step 3: Choose Forecast Channel region[/]\n"); + + AnsiConsole.MarkupLine("For [bold]Forecast Channel[/], which region would you like to install?\n"); + Console.WriteLine("1. North America"); Console.WriteLine("2. PAL"); - Console.WriteLine("3. Japan"); - Console.WriteLine(); - Console.WriteLine("4. Go Back to Main Menu"); - Console.WriteLine(); + Console.WriteLine("3. Japan\n"); + + Console.WriteLine("4. Go Back to Main Menu\n"); int choice = UserChoose("1234"); switch (choice) { case 1: forecast_reg = "USA"; - ChoosePlatform(); + KirbyTVSetup(); break; case 2: forecast_reg = "PAL"; - ChoosePlatform(); + KirbyTVSetup(); break; case 3: forecast_reg = "Japan"; + KirbyTVSetup(); + break; + case 4: // Go back to main menu + MainMenu(); + break; + default: + break; + } + } + } + + + // Ask user if they want to install Kirby TV Channel (Express Install) + static void KirbyTVSetup() + { + string stepNum = !installWC24 ? "Step 2" : "Step 4"; + + while (true) + { + PrintHeader(); + + AnsiConsole.MarkupLine("[bold lime]Express Install[/]\n"); + + AnsiConsole.MarkupLine($"[bold]{stepNum}: Choose to install Kirby TV Channel[/]\n"); + + AnsiConsole.MarkupLine("Would you like to install [bold]Kirby TV Channel[/]?\n"); + + Console.WriteLine("1. Yes"); + Console.WriteLine("2. No\n"); + + Console.WriteLine("3. Go Back to Main Menu\n"); + + int choice = UserChoose("123"); + switch (choice) + { + case 1: + installKirbyTV = true; ChoosePlatform(); break; - case 4: - // Go back to main menu + case 2: + installKirbyTV = false; + ChoosePlatform(); + break; + case 3: // Go back to main menu MainMenu(); break; default: @@ -622,24 +812,25 @@ static void ForecastSetup() } - // Choose console platformType (Wii [Dolphin Emulator] or vWii [Wii U]) + // Choose console platformType (Wii [Dolphin Emulator] or vWii [Wii U]) [Express Install] static void ChoosePlatform() { + string stepNum = !installWC24 ? "Step 3" : "Step 5"; + while (true) { PrintHeader(); - AnsiConsole.MarkupLine("[bold lime]Express Install[/]"); - Console.WriteLine(); - AnsiConsole.MarkupLine("[bold]Step 4: Choose console platform[/]"); - Console.WriteLine(); - Console.WriteLine("Which Wii version are you installing to?"); - Console.WriteLine(); + AnsiConsole.MarkupLine("[bold lime]Express Install[/]\n"); + + AnsiConsole.MarkupLine($"[bold]{stepNum}: Choose console platform[/]\n"); + + Console.WriteLine("Which Wii version are you installing to?\n"); + AnsiConsole.MarkupLine("1. Wii [bold](or Dolphin Emulator)[/]"); - AnsiConsole.MarkupLine("2. vWii [bold](Wii U)[/]"); - Console.WriteLine(); - Console.WriteLine("3. Go Back to Main Menu"); - Console.WriteLine(); + AnsiConsole.MarkupLine("2. vWii [bold](Wii U)[/]\n"); + + Console.WriteLine("3. Go Back to Main Menu\n"); int choice = UserChoose("123"); switch (choice) @@ -652,8 +843,7 @@ static void ChoosePlatform() platformType = "vWii"; SDSetup(); break; - case 3: - // Go back to main menu + case 3: // Go back to main menu MainMenu(); break; default: @@ -664,51 +854,41 @@ static void ChoosePlatform() // SD card setup - static void SDSetup() + static void SDSetup(bool isCustomSetup = false) { - string start_btn = "Start without SD card"; - - if (sdcard != null) - start_btn = "[bold]Start[/]"; while (true) { PrintHeader(); - AnsiConsole.MarkupLine("[bold lime]Express Install[/]"); - Console.WriteLine(); - AnsiConsole.MarkupLine("[bold]Step 5: Insert SD Card (if applicable)[/]"); - Console.WriteLine(); - Console.WriteLine("After passing this step, any user interaction won't be needed, so sit back and relax!"); - Console.WriteLine(); - Console.WriteLine("If you have your Wii SD card inserted, everything can download straight to it, but if not, everything will be downloaded to where this patcher is located on your computer."); - Console.WriteLine(); - AnsiConsole.MarkupLine($"1. {start_btn}"); - Console.WriteLine("2. Manually Select SD card"); + string stepNum = isCustomSetup ? "Step 4" : (!installWC24 ? "Step 4" : "Step 6"); + string installType = isCustomSetup ? "Custom Install" : "Express Install"; - Console.WriteLine(); + AnsiConsole.MarkupLine($"[bold lime]{installType}[/]\n"); + + AnsiConsole.MarkupLine($"[bold]{stepNum}: Insert SD Card / USB Drive (if applicable)[/]\n"); + + Console.WriteLine("After passing this step, any user interaction won't be needed, so sit back and relax!\n"); + + Console.WriteLine($"You can download everything directly to your Wii SD Card / USB Drive if you insert it before starting the patching\nprocess. Otherwise, everything will be saved in the same folder as this patcher on your computer.\n"); + + AnsiConsole.MarkupLine(sdcard != null ? $"1. Start" : $"1. Start without SD Card / USB Drive"); + AnsiConsole.MarkupLine("2. Manually Select SD Card / USB Drive Path\n"); if (sdcard != null) - { - AnsiConsole.MarkupLine($"[[SD card detected: [bold lime]{sdcard}[/]]]"); - Console.WriteLine(); - } + AnsiConsole.MarkupLine($"[[SD card detected: [bold lime]{sdcard}[/]]]\n"); Console.WriteLine("3. Go Back to Main Menu\n"); int choice = UserChoose("123"); switch (choice) { - case 1: - // Start - WADFolderCheck(); + case 1: // Check if WAD folder exists + WADFolderCheck(isCustomSetup); break; - case 2: - // Manually select SD card + case 2: // Manually select SD card SDCardSelect(); break; - - case 3: - // Go back to main menu + case 3: // Go back to main menu MainMenu(); break; default: @@ -718,13 +898,16 @@ static void SDSetup() } - // WAD folder check - static void WADFolderCheck() + // WAD folder check (Express Install) + static void WADFolderCheck(bool isCustomSetup = false) { - // If WAD folder doesn't exist, go to PatchingProgress(), otherwise, ask user if they want to delete it (use AnsiConsole.MarkupLine for formatting) + // Start patching process if WAD folder doesn't exist if (!Directory.Exists("WAD")) { - PatchingProgress(); + if (!isCustomSetup) + PatchingProgress_Express(); + else + PatchingProgress_Custom(); } else { @@ -732,23 +915,25 @@ static void WADFolderCheck() { PrintHeader(); - AnsiConsole.MarkupLine("[bold lime]Express Install[/]"); - Console.WriteLine(); - AnsiConsole.MarkupLine("[bold]Step 6: WAD folder detected[/]"); - Console.WriteLine(); - AnsiConsole.MarkupLine("A [bold]WAD[/] folder has been detected in the current directory. This folder is used to store the WAD files that are downloaded during the patching process. If you choose to delete this folder, it will be recreated when you start the patching process again."); - Console.WriteLine(); + // Change header depending on if it's Express Install or Custom Install + string installType = isCustomSetup ? "Custom Install" : "Express Install"; + string stepNum = isCustomSetup ? "Step 5" : (!installWC24 ? "Step 5" : "Step 7"); + + AnsiConsole.MarkupLine($"[bold lime]{installType}[/]\n"); + + AnsiConsole.MarkupLine($"[bold]{stepNum}: WAD folder detected[/]\n"); + + AnsiConsole.MarkupLine("A [bold]WAD[/] folder has been detected in the current directory. This folder is used to store the WAD files that are downloaded during the patching process. If you choose to delete this folder, it will be recreated when you start the patching process again.\n"); + AnsiConsole.MarkupLine("1. [bold]Delete[/] WAD folder"); - AnsiConsole.MarkupLine("2. [bold]Keep[/] WAD folder"); - Console.WriteLine(); - AnsiConsole.MarkupLine("3. [bold]Go Back to Main Menu[/]"); - Console.WriteLine(); + AnsiConsole.MarkupLine("2. [bold]Keep[/] WAD folder\n"); + + AnsiConsole.MarkupLine("3. [bold]Go Back to Main Menu[/]\n"); int choice = UserChoose("123"); switch (choice) { - case 1: - // Delete WAD folder in a try catch block + case 1: // Delete WAD folder in a try catch block try { Directory.Delete("WAD", true); @@ -761,14 +946,19 @@ static void WADFolderCheck() Console.ReadKey(); WADFolderCheck(); } - PatchingProgress(); + + if (!isCustomSetup) + PatchingProgress_Express(); + else + PatchingProgress_Custom(); break; - case 2: - // Keep WAD folder - PatchingProgress(); + case 2: // Keep WAD folder + if (!isCustomSetup) + PatchingProgress_Express(); + else + PatchingProgress_Custom(); break; - case 3: - // Go back to main menu + case 3: // Go back to main menu MainMenu(); break; default: @@ -776,185 +966,258 @@ static void WADFolderCheck() } } } - } - // Patching progress function - static void PatchingProgress() + // Patching progress function (Express Install) + static void PatchingProgress_Express() { int counter_done = 0; int percent = 0; - string demae_prog_msg = ""; - - // Make WiiLink_Patcher folder in current directory if it doesn't exist - if (!Directory.Exists("WiiLink_Patcher")) - Directory.CreateDirectory("WiiLink_Patcher"); + // Make sure the temp folder exists + if (!Directory.Exists(tempDir)) + Directory.CreateDirectory(tempDir); - // Set patching progress - patching_progress[0] = "downloading:in_progress"; - patching_progress[1] = "wiiroom:not_started"; - patching_progress[2] = "digicam:not_started"; - patching_progress[3] = "demae:not_started"; - patching_progress[4] = "nc:not_started"; - patching_progress[5] = "forecast:not_started"; - patching_progress[6] = "finishing:not_started"; - - // Change Demae progress message depending on version - switch (demae_version) + // Dictionary version of the above switch statement + Dictionary demaeProgMsg_dict = new Dictionary() { - case "standard": - demae_prog_msg = "(Standard)"; - break; - case "dominos": - demae_prog_msg = "(Domino's)"; - break; - case "deliveroo": - demae_prog_msg = "(Deliveroo)"; - break; - } + {"standard", "[[Standard]]"}, + {"dominos", "[[Domino's]]"}, + {"deliveroo", "[[Deliveroo]]"} + }; - // Set channel titles based on region - string demae_title; - string wiiroom_title; - switch (reg) + // Define a dictionary to store the different channel titles + Dictionary> corechannel_titles_dict = new Dictionary>() { - case "EN": - demae_title = "Food Channel"; - wiiroom_title = "Wii Room"; - break; - default: - demae_title = "Demae Channel"; - wiiroom_title = "Wii no Ma"; - break; - } + {"EN", new Dictionary() + { + {"demae", $"Food Channel [bold](English)[/] [bold]{demaeProgMsg_dict[demae_version]}[/]"}, + {"wiiroom", "Wii Room [bold](English)[/]"}, + {"digicam", "Digicam Print Channel [bold](English)[/]"} + } + }, + {"JP", new Dictionary() + { + {"demae", $"Demae Channel [bold](Japanese)[/] [bold]{demaeProgMsg_dict[demae_version]}[/]"}, + {"wiiroom", "Wii no Ma [bold](Japanese)[/]"}, + {"digicam", "Digicam Print Channel [bold](Japanese)[/]"} + } + } + }; - // Set Nintendo Channel title based on region - string nc_title; - switch (nc_reg) - { - case "Japan": - nc_title = "Minna no Nintendo Channel"; - break; - default: - nc_title = "Nintendo Channel"; - break; - } + // Set the channel titles based on the region + string demae_title = corechannel_titles_dict[reg]["demae"]; + string wiiroom_title = corechannel_titles_dict[reg]["wiiroom"]; + string digicam_title = corechannel_titles_dict[reg]["digicam"]; - // Progress messages - string[] progress_messages = new string[] + // Define a dictionary to store the different titles + Dictionary NCTitles_dict = new Dictionary() { - "Downloading files\n\n[bold]Patching Core Channels:[/]", - $"{wiiroom_title}", - "Digicam Print Channel", - $"{demae_title} {demae_prog_msg}\n\n[bold]Patching WiiConnect24 Channels:[/]", - $"{nc_title}", - "Forecast Channel\n\n[bold]Post-Patching:[/]", - "Finishing up!" + {"Japan", "Minna no Nintendo Channel [bold](Japan)[/]"}, + {"USA", "Nintendo Channel [bold](USA)[/]"}, + {"PAL", "Nintendo Channel [bold](PAL)[/]"}, }; - // Setup patching process arrays - Action[] patching_functions = new Action[] - { - DownloadAllPatches, - WiiRoom_Patch, - Digicam_Patch, - Demae_Patch, - NC_Patch, - Forecast_Patch, - FinishSDCopy - }; + // Set the Nintendo Channel title based on the region + string NCTitle = NCTitles_dict[nc_reg]; - while (patching_progress[6] != "finishing:done") + // Define a dictionary to store the different titles for the Forecast Channel + Dictionary> forecastTitles_dict = new Dictionary>() { - // Progress bar and completion display - switch (percent) - { - case 0: - counter_done = 1; - break; - case 1: - counter_done = 2; - break; - case 2: - counter_done = 4; - break; - case 3: - counter_done = 6; - break; - case 4: - counter_done = 8; - break; - case 5: - counter_done = 9; - break; - case 6: - counter_done = 10; - break; + {"Wii", new Dictionary() + { + {"USA", "Forecast Channel [bold](USA)[/] [bold grey][[Wii]][/]"}, + {"PAL", "Forecast Channel [bold](PAL)[/] [bold grey][[Wii]][/]"}, + {"Japan", "Forecast Channel [bold](Japan)[/] [bold grey][[Wii]][/]"}, + } + }, + {"vWii", new Dictionary() + { + {"USA", "Forecast Channel [bold](USA)[/] [bold deepskyblue1][[vWii]][/]"}, + {"PAL", "Forecast Channel [bold](PAL)[/] [bold deepskyblue1][[vWii]][/]"}, + {"Japan", "Forecast Channel [bold](Japan)[/] [bold deepskyblue1][[vWii]][/]"}, + } } + }; - PrintHeader(); + // Set the forecast channel title based on the platform type and region + string forecastTitle = forecastTitles_dict[platformType][forecast_reg]; - AnsiConsole.MarkupLine("[bold][[*]] Patching... this can take some time depending on the processing speed (CPU) of your computer.[/]\n"); + // Define the channel_messages dictionary + Dictionary channelMessages = new Dictionary() + { + { "wiiroom", wiiroom_title }, + { "digicam", digicam_title }, + { "demae", demae_title }, + { "nc", NCTitle }, + { "forecast", forecastTitle } + }; + + //// Setup patching process list //// + List patching_functions = new List(); + + patching_functions.Add(() => DownloadAllPatches()); + + patching_functions.Add(() => WiiRoom_Patch(reg, lang)); + patching_functions.Add(() => Digicam_Patch(reg, lang)); + patching_functions.Add(() => Demae_Patch(reg, demae_version, lang)); + + // Add Kirby TV Channel patching function if applicable + if (installKirbyTV) + patching_functions.Add(() => KirbyTV_Patch()); + + // Add WiiConnect24 patching functions if applicable + if (installWC24) + { + patching_functions.Add(() => NC_Patch(nc_reg)); + patching_functions.Add(() => Forecast_Patch(forecast_reg)); + } + + patching_functions.Add(() => FinishSDCopy()); + + //// Set up patching progress dictionary //// + // Flush dictionary and downloading patches + patchingProgress_express.Clear(); + patchingProgress_express.Add("downloading", "in_progress"); + + // Patching core channels + foreach (string channel in new string[] { "wiiroom", "digicam", "demae" }) + patchingProgress_express.Add(channel, "not_started"); + // Patching Kirby TV Channel (if applicable) + if (installKirbyTV) + patchingProgress_express.Add("kirbytv", "not_started"); + + // Patching WiiConnect24 channels + if (installWC24) + { + foreach (string channel in new string[] { "nc", "forecast" }) + patchingProgress_express.Add(channel, "not_started"); + } + + // Finishing up + patchingProgress_express.Add("finishing", "not_started"); + + + // While the patching process is not finished + while (patchingProgress_express["finishing"] != "done") + { + PrintHeader(); + + AnsiConsole.MarkupLine("[bold][[*]] Patching... this can take some time depending on the processing speed (CPU) of your computer.[/]\n"); Console.Write(" Progress: "); - //Progress bar - switch (counter_done) + //// Progress bar and completion display //// + // Calculate percentage based on how many channels are completed + int percentage = (int)((float)patchingProgress_express.Where(x => x.Value == "done").Count() / (float)patchingProgress_express.Count * 100.0f); + + // Calculate progress bar + counter_done = (int)((float)percentage / 10.0f); + StringBuilder progressBar = new StringBuilder("[["); + for (int i = 0; i < counter_done; i++) { - case 1: - AnsiConsole.Markup("[[[bold lime]■ [/]]]"); - break; - case 2: - AnsiConsole.Markup("[[[bold lime]■■ [/]]]"); - break; - case 4: - AnsiConsole.Markup("[[[bold lime]■■■■ [/]]]"); - break; - case 6: - AnsiConsole.Markup("[[[bold lime]■■■■■■ [/]]]"); - break; - case 8: - AnsiConsole.Markup("[[[bold lime]■■■■■■■■ [/]]]"); - break; - case 9: - AnsiConsole.Markup("[[[bold lime]■■■■■■■■■ [/]]]"); + progressBar.Append("[bold lime]■[/]"); + } + for (int i = counter_done; i < 10; i++) + { + progressBar.Append(" "); + } + progressBar.Append("]]"); + AnsiConsole.Markup(progressBar.ToString()); + + AnsiConsole.Markup($" [bold]{percentage}%[/] completed\n\n"); + AnsiConsole.MarkupLine("Please wait while the patching process is in progress...\n"); + + //// Display progress for each channel //// + + // Pre-Patching Section: Downloading files + AnsiConsole.MarkupLine("[bold]Pre-Patching:[/]"); + switch (patchingProgress_express["downloading"]) + { + case "not_started": + AnsiConsole.MarkupLine($"○ Downloading files..."); break; - case 10: - AnsiConsole.Markup("[[[bold lime]■■■■■■■■■■[/]]]"); + case "in_progress": + AnsiConsole.MarkupLine($"[slowblink yellow]●[/] Downloading files..."); break; - default: - AnsiConsole.Write("[ ]"); + case "done": + AnsiConsole.MarkupLine($"[bold lime]●[/] Downloading files..."); break; } - // Calculate percentage - int percent_done = (counter_done * 100 / 11); - - AnsiConsole.Markup($" [bold]{percent_done}% completed[/]\n\n"); - Console.WriteLine("Please wait while the patching process is in progress...\n"); - - Console.WriteLine("\u001b[1mPre-Patching:\u001b[0m"); - // Show progress of each channel - for (int i = 0; i < patching_progress.Length; i++) + // Patching Section: Patching core channels + AnsiConsole.MarkupLine("\n[bold]Patching Core Channels:[/]"); + foreach (string channel in new string[] { "wiiroom", "digicam", "demae" }) { - string[] progress = patching_progress[i].Split(':'); - string progress_status = progress[1]; + switch (patchingProgress_express[channel]) + { + case "not_started": + AnsiConsole.MarkupLine($"○ {channelMessages[channel]}"); + break; + case "in_progress": + AnsiConsole.MarkupLine($"[slowblink yellow]●[/] {channelMessages[channel]}"); + break; + case "done": + AnsiConsole.MarkupLine($"[bold lime]●[/] {channelMessages[channel]}"); + break; + } + } - switch (progress_status) + // Patching Kirby TV Channel (if applicable) in Core Channels section + if (installKirbyTV) + { + switch (patchingProgress_express["kirbytv"]) { case "not_started": - AnsiConsole.MarkupLine($"○ {progress_messages[i]}"); + AnsiConsole.MarkupLine($"○ Kirby TV Channel"); break; case "in_progress": - AnsiConsole.MarkupLine($"[slowblink yellow]●[/] {progress_messages[i]}"); + AnsiConsole.MarkupLine($"[slowblink yellow]●[/] Kirby TV Channel"); break; case "done": - AnsiConsole.MarkupLine($"[lime]●[/] {progress_messages[i]}"); + AnsiConsole.MarkupLine($"[bold lime]●[/] Kirby TV Channel"); break; } } + + // Patching Section: Patching WiiConnect24 channels (if applicable) + if (installWC24) + { + AnsiConsole.MarkupLine("\n[bold]Patching WiiConnect24 Channels:[/]"); + foreach (string channel in new string[] { "nc", "forecast" }) + { + switch (patchingProgress_express[channel]) + { + case "not_started": + AnsiConsole.MarkupLine($"○ {channelMessages[channel]}"); + break; + case "in_progress": + AnsiConsole.MarkupLine($"[slowblink yellow]●[/] {channelMessages[channel]}"); + break; + case "done": + AnsiConsole.MarkupLine($"[bold lime]●[/] {channelMessages[channel]}"); + break; + } + } + } + + + + // Post-Patching Section: Finishing up + AnsiConsole.MarkupLine("\n[bold]Post-Patching:[/]"); + switch (patchingProgress_express["finishing"]) + { + case "not_started": + AnsiConsole.MarkupLine($"○ Finishing up..."); + break; + case "in_progress": + AnsiConsole.MarkupLine($"[slowblink yellow]●[/] Finishing up..."); + break; + case "done": + AnsiConsole.MarkupLine($"[bold lime]●[/] Finishing up..."); + break; + } Console.WriteLine(); patching_functions[percent](); @@ -964,8 +1227,233 @@ static void PatchingProgress() } // After all the channels are patched, we're done! + Thread.Sleep(2000); + Finished(); + } + + // Patching progress function (Custom Install) + static void PatchingProgress_Custom() + { + task = "Patching channels..."; + int counter_done = 0; + int partCompleted = 0; + + // List of channels to patch, based on coreChannels_selection and wiiConnect24Channels_selection + List channelsToPatch = new List(); + foreach (string channel in coreChannels_selection) + channelsToPatch.Add(channel); + foreach (string channel in wiiConnect24Channels_selection) + channelsToPatch.Add(channel); + /* foreach (string channel in miscChannels_selection) + channelsToPatch.Add(channel); */ + + // Set up patching progress dictionary + patchingProgress_custom.Clear(); // Flush dictionary + patchingProgress_custom.Add("downloading", "in_progress"); // Downloading patches + foreach (string channel in channelsToPatch) // Patching channels + patchingProgress_custom.Add(channel, "not_started"); + patchingProgress_custom.Add("finishing", "not_started"); // Finishing up + + // Give each core channel a proper name + Dictionary channelMap = new Dictionary() + { + { "wiiroom_en", "Wii Room [bold](English)[/]" }, + { "wiinoma_jp", "Wii no Ma [bold](Japanese)[/]" }, + { "digicam_en", "Digicam Print Channel [bold](English)[/]" }, + { "digicam_jp", "Digicam Print Channel [bold](Japanese)[/]" }, + { "food_en", "Food Channel [bold](Standard) [[English]][/]" }, + { "demae_jp", "Demae Channel [bold](Standard) [[Japanese]][/]" }, + { "food_dominos", "Food Channel [bold](Dominos) [[English]][/]" }, + { "food_deliveroo", "Food Channel [bold](Deliveroo) [[English]][/]" }, + { "nc_us", "Nintendo Channel [bold](USA)[/]" }, + { "nc_eu", "Nintendo Channel [bold](Europe)[/]" }, + { "mnnc_jp", "Minna no Nintendo Channel [bold](Japan)[/]" }, + { "forecast_us", "Forecast Channel [bold](USA)[/]" }, + { "forecast_eu", "Forecast Channel [bold](Europe)[/]" }, + { "forecast_jp", "Forecast Channel [bold](Japan)[/]" }, + { "kirbytv", "Kirby TV Channel" } + }; + + // Setup patching process arrays based on the selected channels + Dictionary channelPatchingFunctions = new Dictionary() + { + { "wiiroom_en", () => WiiRoom_Patch("EN", "English") }, + { "wiinoma_jp", () => WiiRoom_Patch("JP", "Japanese") }, + { "digicam_en", () => Digicam_Patch("EN", "English") }, + { "digicam_jp", () => Digicam_Patch("JP", "Japanese") }, + { "food_en", () => Demae_Patch("EN", "standard", "English") }, + { "demae_jp", () => Demae_Patch("JP", "standard", "Japanese") }, + { "food_dominos", () => Demae_Patch("EN", "dominos", "English") }, + { "food_deliveroo", () => Demae_Patch("EN", "deliveroo", "English") }, + { "kirbytv", () => KirbyTV_Patch() }, + { "nc_us", () => NC_Patch("USA") }, + { "nc_eu", () => NC_Patch("PAL") }, + { "mnnc_jp", () => NC_Patch("Japan") }, + { "forecast_us", () => Forecast_Patch("USA") }, + { "forecast_eu", () => Forecast_Patch("PAL") }, + { "forecast_jp", () => Forecast_Patch("Japan") } + + }; + + // Create a list of patching functions to execute + List selectedPatchingFunctions = new List(); + + // Add the patching functions to the list + selectedPatchingFunctions.Add(() => DownloadCustomPatches(channelsToPatch)); + + foreach (string selectedChannel in channelsToPatch) + selectedPatchingFunctions.Add(channelPatchingFunctions[selectedChannel]); + + selectedPatchingFunctions.Add(() => FinishSDCopy()); + + // Start patching + int totalChannels = channelsToPatch.Count; + while (patchingProgress_custom["finishing"] != "done") + { + PrintHeader(); + + AnsiConsole.MarkupLine("[bold][[*]] Patching... this can take some time depending on the processing speed (CPU) of your computer.[/]\n"); + Console.Write(" Progress: "); + + //// Progress bar and completion display + // Calculate percentage based on how many channels are selected + int percentage = (int)((float)partCompleted / (float)(totalChannels + 2) * 100); + + // Calculate progress bar + counter_done = (int)((float)percentage / 10.0f); + + // Display progress bar + StringBuilder progressBar = new StringBuilder("[["); + for (int i = 0; i < counter_done; i++) + progressBar.Append("[bold lime]■[/]"); + for (int i = counter_done; i < 10; i++) + progressBar.Append(" "); + progressBar.Append("]]"); + + AnsiConsole.Markup(progressBar.ToString()); + + // Display percentage + AnsiConsole.Markup($" [bold]{percentage}%[/] completed\n\n"); + AnsiConsole.MarkupLine("Please wait while the patching process is in progress...\n"); + + //// Display progress for each channel //// + + // Pre-Patching Section: Downloading files + AnsiConsole.MarkupLine("[bold]Pre-Patching:[/]"); + switch (patchingProgress_custom["downloading"]) + { + case "not_started": + AnsiConsole.MarkupLine($"○ Downloading files..."); + break; + case "in_progress": + AnsiConsole.MarkupLine($"[slowblink yellow]●[/] Downloading files..."); + break; + case "done": + AnsiConsole.MarkupLine($"[bold lime]●[/] Downloading files..."); + break; + } + + // Patching Section: Patching core channels + if (coreChannels_selection.Count > 0) + { + AnsiConsole.MarkupLine("\n[bold]Patching Core Channels:[/]"); + foreach (string coreChannel in channelsToPatch) + { + List coreChannels = new List { "wiiroom_en", "wiinoma_jp", "digicam_en", "digicam_jp", "food_en", "demae_jp", "food_dominos", "food_deliveroo", "kirbytv" }; + if (coreChannels.Contains(coreChannel)) + { + switch (patchingProgress_custom[coreChannel]) + { + case "not_started": + AnsiConsole.MarkupLine($"○ {channelMap[coreChannel]}"); + break; + case "in_progress": + AnsiConsole.MarkupLine($"[slowblink yellow]●[/] {channelMap[coreChannel]}"); + break; + case "done": + AnsiConsole.MarkupLine($"[bold lime]●[/] {channelMap[coreChannel]}"); + break; + } + } + } + } + + // Patching Section: Patching WiiConnect24 channels + if (wiiConnect24Channels_selection.Count > 0) + { + AnsiConsole.MarkupLine("\n[bold]Patching WiiConnect24 Channels:[/]"); + foreach (string wiiConnect24Channel in channelsToPatch) + { + List wiiConnect24Channels = new List { "nc_us", "nc_eu", "mnnc_jp", "forecast_us", "forecast_eu", "forecast_jp" }; + if (wiiConnect24Channels.Contains(wiiConnect24Channel)) + { + switch (patchingProgress_custom[wiiConnect24Channel]) + { + case "not_started": + AnsiConsole.MarkupLine($"○ {channelMap[wiiConnect24Channel]}"); + break; + case "in_progress": + AnsiConsole.MarkupLine($"[slowblink yellow]●[/] {channelMap[wiiConnect24Channel]}"); + break; + case "done": + AnsiConsole.MarkupLine($"[bold lime]●[/] {channelMap[wiiConnect24Channel]}"); + break; + } + } + } + } + + // Post-Patching Section: Finishing up + AnsiConsole.MarkupLine("\n[bold]Post-Patching:[/]"); + switch (patchingProgress_custom["finishing"]) + { + case "not_started": + AnsiConsole.MarkupLine($"○ Finishing up..."); + break; + case "in_progress": + AnsiConsole.MarkupLine($"[slowblink yellow]●[/] Finishing up..."); + break; + case "done": + AnsiConsole.MarkupLine($"[bold lime]●[/] Finishing up..."); + break; + } + Console.WriteLine(); + + // Execute the next patching function + selectedPatchingFunctions[partCompleted](); + + + // Increment the percentage + task = "Moving to next patch"; + partCompleted++; + + switch (partCompleted) + { + case 1: + // If we're on the first channel, mark downloading as done and the first channel as in progress + patchingProgress_custom["downloading"] = "done"; + patchingProgress_custom[channelsToPatch[0]] = "in_progress"; + break; + case int n when (n > 1 && n < totalChannels + 1): + // If we're on a channel that's not the first or last, mark the previous channel as done and the current channel as in progress + patchingProgress_custom[channelsToPatch[partCompleted - 2]] = "done"; + patchingProgress_custom[channelsToPatch[partCompleted - 1]] = "in_progress"; + break; + case int n when (n == totalChannels + 1): + // If we're on the last channel, mark the previous channel as done and finishing as in progress + patchingProgress_custom[channelsToPatch[partCompleted - 2]] = "done"; + patchingProgress_custom["finishing"] = "in_progress"; + break; + case int n when (n == totalChannels + 2): + // If we're done patching, mark finishing as done + patchingProgress_custom["finishing"] = "done"; + break; + } + } + + // We're finally done patching! + Thread.Sleep(2000); Finished(); - return; } @@ -975,11 +1463,15 @@ static void DownloadAllPatches() // Download SPD if English is selected if (reg == "EN") - DownloadSPD(); + DownloadSPD(platformType); else - Directory.CreateDirectory("WAD"); + { + if (!Directory.Exists("WAD")) + Directory.CreateDirectory("WAD"); + } + + //// Downloading All Channel Patches //// - ///// Downloading All Channel Patches ///// // Wii no Ma (Wii Room) if (reg == "EN") DownloadPatch("WiinoMa", $"WiinoMa_0_{lang}.delta", "WiinoMa_0.delta", "Wii no Ma"); @@ -993,194 +1485,717 @@ static void DownloadAllPatches() if (reg == "EN") DownloadPatch("Digicam", $"Digicam_2_{lang}.delta", "Digicam_2.delta", "Digicam Print Channel"); - // Demae Channel (Standard or Dominos) - if (demae_version == "standard") + // Demae Channel + switch (demae_version) + { + case "standard": + if (reg == "EN") + DownloadPatch("Demae", $"Demae_0_{lang}.delta", "Demae_0.delta", "Demae Channel (Standard)"); + DownloadPatch("Demae", $"Demae_1_{lang}.delta", "Demae_1.delta", "Demae Channel (Standard)"); + if (reg == "EN") + DownloadPatch("Demae", $"Demae_2_{lang}.delta", "Demae_2.delta", "Demae Channel (Standard)"); + break; + case "dominos": + DownloadPatch("Dominos", $"Dominos_0.delta", "Dominos_0.delta", "Demae Channel (Dominos)"); + DownloadPatch("Dominos", $"Dominos_1.delta", "Dominos_1.delta", "Demae Channel (Dominos)"); + DownloadPatch("Dominos", $"Dominos_2.delta", "Dominos_2.delta", "Demae Channel (Dominos)"); + break; + case "deliveroo": + DownloadPatch("Deliveroo", $"Deliveroo_0.delta", "Deliveroo_0.delta", "Demae Channel (Deliveroo)"); + DownloadPatch("Deliveroo", $"Deliveroo_1.delta", "Deliveroo_1.delta", "Demae Channel (Deliveroo)"); + DownloadPatch("Deliveroo", $"Deliveroo_2.delta", "Deliveroo_2.delta", "Demae Channel (Deliveroo)"); + break; + } + + // If /apps/WiiModLite folder doesn't exist, create it + if (!Directory.Exists(Path.Join("apps", "WiiModLite"))) + Directory.CreateDirectory(Path.Join("apps", "WiiModLite")); + + // Downloading Wii Mod Lite + task = "Downloading Wii Mod Lite"; + DownloadFile($"https://hbb1.oscwii.org/unzipped_apps/WiiModLite/apps/WiiModLite/boot.dol", Path.Join("apps", "WiiModLite", "boot.dol"), "Wii Mod Lite"); + DownloadFile($"https://hbb1.oscwii.org/unzipped_apps/WiiModLite/apps/WiiModLite/meta.xml", Path.Join("apps", "WiiModLite", "meta.xml"), "Wii Mod Lite"); + DownloadFile($"https://hbb1.oscwii.org/hbb/WiiModLite.png", Path.Join("apps", "WiiModLite", "icon.png"), "Wii Mod Lite"); + + // Downloading Get Console ID (for Dominos Demae Channel) + if (demae_version == "dominos" || demae_version == "deliveroo") + { + task = "Downloading Get Console ID"; + + // If /apps/GetConsoleID folder doesn't exist, create it + if (!Directory.Exists(Path.Join("apps", "GetConsoleID"))) + Directory.CreateDirectory(Path.Join("apps", "GetConsoleID")); + + DownloadFile($"https://hbb1.oscwii.org/unzipped_apps/GetConsoleID/apps/GetConsoleID/boot.dol", Path.Join("apps", "GetConsoleID", "boot.dol"), "Get Console ID"); + DownloadFile($"https://hbb1.oscwii.org/unzipped_apps/GetConsoleID/apps/GetConsoleID/meta.xml", Path.Join("apps", "GetConsoleID", "meta.xml"), "Get Console ID"); + DownloadFile($"https://hbb1.oscwii.org/hbb/GetConsoleID.png", Path.Join("apps", "GetConsoleID", "icon.png"), "Get Console ID"); + } + + // Nintendo Channel + DownloadPatch("nc", $"NC_1_{nc_reg}.delta", "NC_1.delta", "Nintendo Channel"); + + // Forecast Channel + DownloadPatch("forecast", $"Forecast_1.delta", "Forecast_1.delta", "Forecast Channel"); + + // Kirby TV Channel (only if user chose to install it) + if (installKirbyTV) + DownloadPatch("ktv", $"ktv_2.delta", "KirbyTV_2.delta", "Kirby TV Channel"); + + // Downloading stuff is finished! + patchingProgress_express["downloading"] = "done"; + patchingProgress_express["wiiroom"] = "in_progress"; + } + + // Custom Install (Part 1 - Select core channels) + static void CustomInstall_CoreChannel_Setup() + { + task = "Custom Install (Part 1 - Select core channels)"; + + // Flush the list of selected channels (in case the user goes back to the previous menu) + coreChannels_selection.Clear(); + wiiConnect24Channels_selection.Clear(); + + // List of selected channels + HashSet selectedChannels = new HashSet(); + + // Define a dictionary to map channel names to easy-to-read format + Dictionary channelMap = new Dictionary() + { + { "Wii Room [bold](English)[/]", "wiiroom_en" }, + { "Wii no Ma [bold](Japanese)[/]", "wiinoma_jp" }, + { "Digicam Print Channel [bold](English)[/]", "digicam_en" }, + { "Digicam Print Channel [bold](Japanese)[/]", "digicam_jp" }, + { "Food Channel [bold](Standard) [[English]][/]", "food_en" }, + { "Demae Channel [bold](Standard) [[Japanese]][/]", "demae_jp" }, + { "Food Channel [bold](Dominos) [[English]][/]", "food_dominos" }, + { "Food Channel [bold](Deliveroo) [[English]][/]", "food_deliveroo" }, + { "Kirby TV Channel", "kirbytv" } + }; + + // Initialize selection array to "Not selected" using LINQ + string[] selected = channelMap.Values.Select(_ => "[grey]Not selected[/]").ToArray(); + + while (true) + { + PrintHeader(); + + // Print title + AnsiConsole.MarkupLine("[bold lime]Custom Install[/]"); + Console.WriteLine(); + AnsiConsole.MarkupLine("[bold]Step 1:[/] Select core channel(s) to install\n"); + + // Display core channel selection menu + AnsiConsole.MarkupLine("[bold]Select core channel(s) to install:[/]\n"); + var grid = new Grid(); + + // Add channels to grid + grid.AddColumn(); + grid.AddColumn(); + + // Display list of channels + for (int i = 1; i <= channelMap.Count; i++) + { + KeyValuePair channel = channelMap.ElementAt(i - 1); + grid.AddRow($"[bold]{i}.[/] {channel.Key}", selected[i - 1]); + } + + AnsiConsole.Write(grid); + Console.WriteLine(); + + // Print instructions + AnsiConsole.MarkupLine("[grey]< Press [bold white]a number[/] to select/deselect a channel, [bold white]ENTER[/] to continue, [bold white]Backspace[/] to go back, [bold white]ESC[/] to go back to exit setup >[/]\n"); + + int choice = UserChoose("123456789"); + + // Handle user input + switch (choice) + { + case -1: // Escape + coreChannels_selection.Clear(); + MainMenu(); + break; + case 0: // Enter + // Save selected channels to global variable if any are selected + if (selectedChannels.Count != 0) + { + foreach (string channel in selectedChannels) + coreChannels_selection.Add(channel); + } + + CustomInstall_WiiConnect24_Setup(); + break; + case -2: // Backspace + coreChannels_selection.Clear(); + MainMenu(); + break; + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + string channelName = channelMap.Values.ElementAt(choice - 1); + if (selectedChannels.Contains(channelName)) + { + selectedChannels.Remove(channelName); + selected[choice - 1] = "[grey]Not selected[/]"; + } + else + { + selectedChannels.Add(channelName); + selected[choice - 1] = "[bold lime]Selected[/]"; + } + break; + + + default: + break; + } + } + } + + // Custom Install (Part 2 - Select WiiConnect24 channels) + static void CustomInstall_WiiConnect24_Setup() + { + task = "Custom Install (Part 2 - Select WiiConnect24 channels)"; + + // List of selected channels + HashSet selectedChannels = new HashSet(); + + // Define a dictionary to map channel names to easy-to-read format + Dictionary channelMap = new Dictionary() + { + { "Nintendo Channel [bold](USA)[/]", "nc_us" }, + { "Nintendo Channel [bold](Europe)[/]", "nc_eu" }, + { "Minna no Nintendo Channel [bold](Japan)[/]", "mnnc_jp" }, + { "Forecast Channel [bold](USA)[/]", "forecast_us" }, + { "Forecast Channel [bold](Europe)[/]", "forecast_eu" }, + { "Forecast Channel [bold](Japan)[/]", "forecast_jp" } + }; + + // Initialize selection array to "Not selected" using LINQ + string[] selected = channelMap.Values.Select(_ => "[grey]Not selected[/]").ToArray(); + + while (true) + { + PrintHeader(); + + // Print title + AnsiConsole.MarkupLine("[bold lime]Custom Install[/]"); + Console.WriteLine(); + AnsiConsole.MarkupLine("[bold]Step 2:[/] Select WiiConnect24 channel(s)\n"); + + + // Display WC24 channel selection menu + AnsiConsole.MarkupLine("[bold]Select WiiConnect24 channel(s) to install:[/]\n"); + var grid = new Grid(); + + // Add channels to grid + grid.AddColumn(); + grid.AddColumn(); + + // Display list of channels + for (int i = 1; i <= channelMap.Count; i++) + { + KeyValuePair channel = channelMap.ElementAt(i - 1); + grid.AddRow($"[bold]{i}.[/] {channel.Key}", selected[i - 1]); + } + + AnsiConsole.Write(grid); + Console.WriteLine(); + + // Print instructions + AnsiConsole.MarkupLine("[grey]< Press [bold white]a number[/] to select/deselect a channel, [bold white]ENTER[/] to continue, [bold white]Backspace[/] to go back, [bold white]ESC[/] to go back to exit setup >[/]\n"); + + int choice = UserChoose("123456789"); + + // Handle user input + switch (choice) + { + case -1: // Escape + wiiConnect24Channels_selection.Clear(); + coreChannels_selection.Clear(); + MainMenu(); + break; + case 0: // Enter + // Save selected channels to global variable if any are selected + if (selectedChannels.Count != 0) + { + foreach (string channel in selectedChannels) + wiiConnect24Channels_selection.Add(channel); + } + + // If both coreChannels_selection and wiiConnect24Channels_selection are empty, error out + if (coreChannels_selection.Count == 0 && wiiConnect24Channels_selection.Count == 0) + { + AnsiConsole.MarkupLine("\n[bold red]ERROR:[/] You must select at least one channel to proceed!"); + Thread.Sleep(3000); + continue; + } + + // If any selected core channels have "_en" in their name, go to SPD setup + if (coreChannels_selection.Any(channel => channel.Contains("_en")) || coreChannels_selection.Contains("food_dominos") || coreChannels_selection.Contains("food_deliveroo")) + CustomInstall_SPD_Setup(); + else + CustomInstall_SummaryScreen(); + break; + case -2: // Backspace + wiiConnect24Channels_selection.Clear(); + coreChannels_selection.Clear(); + CustomInstall_CoreChannel_Setup(); + break; + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + string channelName = channelMap.Values.ElementAt(choice - 1); + if (selectedChannels.Contains(channelName)) + { + selectedChannels.Remove(channelName); + selected[choice - 1] = "[grey]Not selected[/]"; + } + else + { + selectedChannels.Add(channelName); + selected[choice - 1] = "[bold lime]Selected[/]"; + } + break; + default: + break; + } + } + } + + // Custom Install (Part 3 - Select SPD version) + static void CustomInstall_SPD_Setup() + { + task = "Custom Install (Part 3 - Select SPD version)"; + while (true) { - if (reg == "EN") - DownloadPatch("Demae", $"Demae_0_{lang}.delta", "Demae_0.delta", "Demae Channel (Standard)"); - DownloadPatch("Demae", $"Demae_1_{lang}.delta", "Demae_1.delta", "Demae Channel (Standard)"); - if (reg == "EN") - DownloadPatch("Demae", $"Demae_2_{lang}.delta", "Demae_2.delta", "Demae Channel (Standard)"); + PrintHeader(); + + // Print title + AnsiConsole.MarkupLine("[bold lime]Custom Install[/]"); + Console.WriteLine(); + AnsiConsole.MarkupLine("[bold]Step 3:[/] Select WiiLink SPD version\n"); + + // Display SPD version selection menu + AnsiConsole.MarkupLine("[bold]Select WiiLink SPD version to install:[/]\n"); + AnsiConsole.MarkupLine("[bold]1.[/] WiiLink SPD [bold grey][[Wii]][/]"); + AnsiConsole.MarkupLine("[bold]2.[/] WiiLink SPD [bold deepskyblue1][[vWii]][/]"); + Console.WriteLine(); + + // Prompt user to select SPD version (Press a number to select a version, Backspace to go back a step, ESC to go back to the main menu) + AnsiConsole.MarkupLine("[grey]< Press [bold white]a number[/] to select a version, [bold white]Backspace[/] to go back, [bold white]ESC[/] to go back to exit setup >[/]\n"); + + int choice = UserChoose("12"); + + // Use a switch statement to handle user's SPD version selection + switch (choice) + { + case -1: // Escape + wiiConnect24Channels_selection.Clear(); + coreChannels_selection.Clear(); + MainMenu(); + break; + case -2: // Backspace + wiiConnect24Channels_selection.Clear(); + CustomInstall_WiiConnect24_Setup(); + break; + case 1: + spdVersion_custom = "Wii"; + CustomInstall_SummaryScreen(showSPD: true); + break; + case 2: + spdVersion_custom = "vWii"; + CustomInstall_SummaryScreen(showSPD: true); + break; + default: + break; + } } - else if (demae_version == "dominos") + } + + + // Custom Install (Part 4 - Show summary of selected channels to be installed) + static void CustomInstall_SummaryScreen(bool showSPD = false) + { + task = "Custom Install (Part 4 - Show summary of selected channels to be installed)"; + // Convert core channel names to proper names + Dictionary coreChannelMap = new Dictionary() + { + { "wiiroom_en", "● Wii Room [bold](English)[/]" }, + { "wiinoma_jp", "● Wii no Ma [bold](Japanese)[/]" }, + { "digicam_en", "● Digicam Print Channel [bold](English)[/]" }, + { "digicam_jp", "● Digicam Print Channel [bold](Japanese)[/]" }, + { "food_en", "● Food Channel [bold](Standard) [[English]][/]" }, + { "demae_jp", "● Demae Channel [bold](Standard) [[Japanese]][/]" }, + { "food_dominos", "● Food Channel [bold](Dominos) [[English]][/]" }, + { "food_deliveroo", "● Food Channel [bold](Deliveroo) [[English]][/]" }, + { "kirbytv", "● Kirby TV Channel" } + }; + List selectedCoreChannels = new List(); + if (coreChannels_selection.Count > 0) { - DownloadPatch("Dominos", $"Dominos_0.delta", "Dominos_0.delta", "Demae Channel (Dominos)"); - DownloadPatch("Dominos", $"Dominos_1.delta", "Dominos_1.delta", "Demae Channel (Dominos)"); - DownloadPatch("Dominos", $"Dominos_2.delta", "Dominos_2.delta", "Demae Channel (Dominos)"); + foreach (string channel in coreChannels_selection) + { + if (coreChannelMap.TryGetValue(channel, out string? modifiedChannel)) + selectedCoreChannels.Add(modifiedChannel); + } } - else if (demae_version == "deliveroo") + else { - DownloadPatch("Deliveroo", $"Deliveroo_0.delta", "Deliveroo_0.delta", "Demae Channel (Deliveroo)"); - DownloadPatch("Deliveroo", $"Deliveroo_1.delta", "Deliveroo_1.delta", "Demae Channel (Deliveroo)"); - DownloadPatch("Deliveroo", $"Deliveroo_2.delta", "Deliveroo_2.delta", "Demae Channel (Deliveroo)"); + selectedCoreChannels.Add("● [grey]N/A[/]"); } - // If /apps/WiiModLite folder doesn't exist, create it - if (!Directory.Exists(Path.Join("apps", "WiiModLite"))) - Directory.CreateDirectory(Path.Join("apps", "WiiModLite")); + // Convert WiiConnect24 channel names to proper names + Dictionary wiiConnect24ChannelMap = new Dictionary() + { + { "nc_us", "● Nintendo Channel [bold](USA)[/]" }, + { "nc_eu", "● Nintendo Channel [bold](Europe)[/]" }, + { "mnnc_jp", "● Minna no Nintendo Channel [bold](Japan)[/]" }, + { "forecast_us", "● Forecast Channel [bold](USA)[/]" }, + { "forecast_eu", "● Forecast Channel [bold](Europe)[/]"}, + { "forecast_jp", "● Forecast Channel [bold](Japan)[/]"} + }; + List selectedWiiConnect24Channels = new List(); + if (wiiConnect24Channels_selection.Count > 0) + { + foreach (string channel in wiiConnect24Channels_selection) + { + if (wiiConnect24ChannelMap.TryGetValue(channel, out string? modifiedChannel)) + selectedWiiConnect24Channels.Add(modifiedChannel); + } + } + else + { + selectedWiiConnect24Channels.Add("● [grey]N/A[/]"); + } + + while (true) + { + PrintHeader(); + + // Print title + AnsiConsole.MarkupLine("[bold lime]Custom Install[/]\n"); + AnsiConsole.MarkupLine("[bold]Summary of selected channels to be installed:[/]\n"); + + // Display summary of selected channels in two columns using a grid + var grid = new Grid(); + grid.AddColumn(); + grid.AddColumn(); + + if (showSPD) + { + grid.AddColumn(); + grid.AddRow("[bold lime]Core Channels:[/]", "[bold lime]WiiConnect24 Channels:[/]", "[bold lime]SPD Version:[/]"); + grid.AddRow(string.Join("\n", selectedCoreChannels), string.Join("\n", selectedWiiConnect24Channels), spdVersion_custom == "Wii" ? "● [bold grey]Wii[/]" : "● [bold deepskyblue1]vWii[/]"); + } + else + { + grid.AddRow("[bold lime]Core Channels:[/]", "[bold lime]WiiConnect24 Channels:[/]"); + grid.AddRow(string.Join("\n", selectedCoreChannels), string.Join("\n", selectedWiiConnect24Channels)); + } + AnsiConsole.Write(grid); + + // User confirmation prompt + AnsiConsole.MarkupLine("\n[bold]Are you sure you want to install these selected channels?[/]\n"); + AnsiConsole.MarkupLine("1. Yes"); + AnsiConsole.MarkupLine("2. No, start over\n"); + AnsiConsole.MarkupLine("3. No, go back to main menu\n"); + var choice = UserChoose("123"); + + // Handle user confirmation choice + switch (choice) + { + case 1: // Yes + SDSetup(isCustomSetup: true); + break; + case 2: // No, start over + coreChannels_selection.Clear(); + wiiConnect24Channels_selection.Clear(); + CustomInstall_CoreChannel_Setup(); + break; + case 3: // No, go back to main menu + coreChannels_selection.Clear(); + wiiConnect24Channels_selection.Clear(); + MainMenu(); + break; + default: + break; + } + } + } + + // Download respective patches for selected core and WiiConnect24 channels (and SPD if English is selected for core channels) + static void DownloadCustomPatches(List channelSelection) + { + task = "Downloading selected patches"; + + // Download SPD if English is selected for core channels + if (channelSelection.Any(x => x.Contains("_en")) || channelSelection.Contains("food_dominos") || channelSelection.Contains("food_deliveroo")) + DownloadSPD(spdVersion_custom); + else + Directory.CreateDirectory("WAD"); + // Download patches for selected core channels + foreach (string channel in channelSelection) + { + switch (channel) + { + case "wiiroom_en": + task = "Downloading Wii Room (English)"; + DownloadPatch("WiinoMa", $"WiinoMa_0_English.delta", "WiinoMa_0.delta", "Wii Room"); + DownloadPatch("WiinoMa", $"WiinoMa_1_English.delta", "WiinoMa_1.delta", "Wii Room"); + DownloadPatch("WiinoMa", $"WiinoMa_2_English.delta", "WiinoMa_2.delta", "Wii Room"); + break; + case "wiinoma_jp": + task = "Downloading Wii no Ma (Japan)"; + DownloadPatch("WiinoMa", $"WiinoMa_1_Japan.delta", "WiinoMa_1.delta", "Wii no Ma"); + DownloadPatch("WiinoMa", $"WiinoMa_2_Japan.delta", "WiinoMa_2.delta", "Wii no Ma"); + break; + case "digicam_en": + task = "Downloading Digicam Print Channel (English)"; + DownloadPatch("Digicam", $"Digicam_0_English.delta", "Digicam_0.delta", "Digicam Print Channel"); + DownloadPatch("Digicam", $"Digicam_1_English.delta", "Digicam_1.delta", "Digicam Print Channel"); + DownloadPatch("Digicam", $"Digicam_2_English.delta", "Digicam_2.delta", "Digicam Print Channel"); + break; + case "digicam_jp": + task = "Downloading Digicam Print Channel (Japan)"; + DownloadPatch("Digicam", $"Digicam_1_Japan.delta", "Digicam_1.delta", "Digicam Print Channel"); + break; + case "food_en": + task = "Downloading Food Channel (English)"; + DownloadPatch("Demae", $"Demae_0_English.delta", "Demae_0.delta", "Food Channel (Standard)"); + DownloadPatch("Demae", $"Demae_1_English.delta", "Demae_1.delta", "Food Channel (Standard)"); + DownloadPatch("Demae", $"Demae_2_English.delta", "Demae_2.delta", "Food Channel (Standard)"); + break; + case "demae_jp": + task = "Downloading Demae Channel (Japan)"; + DownloadPatch("Demae", $"Demae_1_Japan.delta", "Demae_1.delta", "Demae Channel"); + break; + case "food_dominos": + task = "Downloading Food Channel (Domino's)"; + DownloadPatch("Dominos", $"Dominos_0.delta", "Dominos_0.delta", "Food Channel (Domino's)"); + DownloadPatch("Dominos", $"Dominos_1.delta", "Dominos_1.delta", "Food Channel (Domino's)"); + DownloadPatch("Dominos", $"Dominos_2.delta", "Dominos_2.delta", "Food Channel (Domino's)"); + + task = "Downloading Get Console ID"; + if (!Directory.Exists(Path.Join("apps", "GetConsoleID"))) + Directory.CreateDirectory(Path.Join("apps", "GetConsoleID")); + DownloadFile($"https://hbb1.oscwii.org/unzipped_apps/GetConsoleID/apps/GetConsoleID/boot.dol", Path.Join("apps", "GetConsoleID", "boot.dol"), "Get Console ID"); + DownloadFile($"https://hbb1.oscwii.org/unzipped_apps/GetConsoleID/apps/GetConsoleID/meta.xml", Path.Join("apps", "GetConsoleID", "meta.xml"), "Get Console ID"); + DownloadFile($"https://hbb1.oscwii.org/hbb/GetConsoleID.png", Path.Join("apps", "GetConsoleID", "icon.png"), "Get Console ID"); + break; + case "food_deliveroo": + task = "Downloading Food Channel (Deliveroo)"; + DownloadPatch("Deliveroo", $"Deliveroo_0.delta", "Deliveroo_0.delta", "Food Channel (Deliveroo)"); + DownloadPatch("Deliveroo", $"Deliveroo_1.delta", "Deliveroo_1.delta", "Food Channel (Deliveroo)"); + DownloadPatch("Deliveroo", $"Deliveroo_2.delta", "Deliveroo_2.delta", "Food Channel (Deliveroo)"); + + task = "Downloading Get Console ID"; + if (!Directory.Exists(Path.Join("apps", "GetConsoleID"))) + Directory.CreateDirectory(Path.Join("apps", "GetConsoleID")); + DownloadFile($"https://hbb1.oscwii.org/unzipped_apps/GetConsoleID/apps/GetConsoleID/boot.dol", Path.Join("apps", "GetConsoleID", "boot.dol"), "Get Console ID"); + DownloadFile($"https://hbb1.oscwii.org/unzipped_apps/GetConsoleID/apps/GetConsoleID/meta.xml", Path.Join("apps", "GetConsoleID", "meta.xml"), "Get Console ID"); + DownloadFile($"https://hbb1.oscwii.org/hbb/GetConsoleID.png", Path.Join("apps", "GetConsoleID", "icon.png"), "Get Console ID"); + break; + case "nc_us": + task = "Downloading Nintendo Channel (USA)"; + DownloadPatch("nc", $"NC_1_USA.delta", "NC_1.delta", "Nintendo Channel"); + break; + case "mnnc_jp": + task = "Downloading Nintendo Channel (Japan)"; + DownloadPatch("nc", $"NC_1_Japan.delta", "NC_1.delta", "Nintendo Channel"); + break; + case "nc_eu": + task = "Downloading Nintendo Channel (Europe)"; + DownloadPatch("nc", $"NC_1_PAL.delta", "NC_1.delta", "Nintendo Channel"); + break; + case "forecast_us": // Forecast Patch works for all regions now + case "forecast_jp": + case "forecast_eu": + task = "Downloading Forecast Channel"; + DownloadPatch("forecast", $"Forecast_1.delta", "Forecast_1.delta", "Forecast Channel"); + break; + case "kirbytv": + task = "Downloading Kirby TV Channel"; + DownloadPatch("ktv", $"ktv_2.delta", "KirbyTV_2.delta", "Kirby TV Channel"); + break; + } + } // Downloading Wii Mod Lite task = "Downloading Wii Mod Lite"; + if (!Directory.Exists(Path.Join("apps", "WiiModLite"))) + Directory.CreateDirectory(Path.Join("apps", "WiiModLite")); DownloadFile($"https://hbb1.oscwii.org/unzipped_apps/WiiModLite/apps/WiiModLite/boot.dol", Path.Join("apps", "WiiModLite", "boot.dol"), "Wii Mod Lite"); DownloadFile($"https://hbb1.oscwii.org/unzipped_apps/WiiModLite/apps/WiiModLite/meta.xml", Path.Join("apps", "WiiModLite", "meta.xml"), "Wii Mod Lite"); DownloadFile($"https://hbb1.oscwii.org/hbb/WiiModLite.png", Path.Join("apps", "WiiModLite", "icon.png"), "Wii Mod Lite"); - - // Downloading Get Console ID (for Dominos Demae Channel) - if (demae_version == "dominos" || demae_version == "deliveroo") - { - task = "Downloading Get Console ID"; - - // If /apps/GetConsoleID folder doesn't exist, create it - if (!Directory.Exists(Path.Join("apps", "GetConsoleID"))) - Directory.CreateDirectory(Path.Join("apps", "GetConsoleID")); - DownloadFile($"https://hbb1.oscwii.org/unzipped_apps/GetConsoleID/apps/GetConsoleID/boot.dol", Path.Join("apps", "GetConsoleID", "boot.dol"), "Get Console ID"); - DownloadFile($"https://hbb1.oscwii.org/unzipped_apps/GetConsoleID/apps/GetConsoleID/meta.xml", Path.Join("apps", "GetConsoleID", "meta.xml"), "Get Console ID"); - DownloadFile($"https://hbb1.oscwii.org/hbb/GetConsoleID.png", Path.Join("apps", "GetConsoleID", "icon.png"), "Get Console ID"); - } - - // Nintendo Channel - DownloadPatch("nc", $"NC_1_{nc_reg}.delta", "NC_1.delta", "Nintendo Channel"); - - // Forecast Channel - DownloadPatch("forecast", $"Forecast_1_{platformType}_{forecast_reg}.delta", "Forecast_1.delta", "Forecast Channel"); - - // Downloading stuff is finished! - patching_progress[0] = "downloading:done"; - patching_progress[1] = "wiinoma:in_progress"; } // Patching Wii no Ma - static void WiiRoom_Patch() + static void WiiRoom_Patch(string reg = "", string lang = "") { task = "Patching Wii no Ma"; - string[] wiiroom_patches = { "WiinoMa_0", "WiinoMa_1", "WiinoMa_2" }; - string[] wiiroom_apps = { "00000000", "00000001", "00000002" }; + // Dictionary for which files to patch + var wiiroom_patch_list = new List>() + { + new KeyValuePair("WiinoMa_0", "00000000"), + new KeyValuePair("WiinoMa_1", "00000001"), + new KeyValuePair("WiinoMa_2", "00000002") + }; // If English, change channel title to "Wii Room" - string wiiroom_title = "Wii no Ma"; - if (reg == "EN") - wiiroom_title = "Wii Room"; + string wiiroom_title = reg == "EN" ? "Wii Room" : "Wii no Ma"; - PatchCoreChannel("WiinoMa", wiiroom_title, "000100014843494a", wiiroom_patches, wiiroom_apps); + PatchCoreChannel("WiinoMa", wiiroom_title, "000100014843494a", wiiroom_patch_list, lang: lang); // Finished patching Wii no Ma - patching_progress[1] = "wiinoma:done"; - patching_progress[2] = "digicam:in_progress"; + patchingProgress_express["wiiroom"] = "done"; + patchingProgress_express["digicam"] = "in_progress"; } // Patching Digicam Print Channel - static void Digicam_Patch() + static void Digicam_Patch(string reg = "", string lang = "") { task = "Patching Digicam Print Channel"; - string[] digicam_patches = { "Digicam_0", "Digicam_1", "Digicam_2" }; - string[] digicam_apps = { "00000000", "00000001", "00000002" }; + // Dictionary for which files to patch + var digicam_patch_list = new List>() + { + new KeyValuePair("Digicam_0", "00000000"), + new KeyValuePair("Digicam_1", "00000001"), + new KeyValuePair("Digicam_2", "00000002") + }; - PatchCoreChannel("Digicam", "Digicam Print Channel", "000100014843444a", digicam_patches, digicam_apps); + PatchCoreChannel("Digicam", "Digicam Print Channel", "000100014843444a", digicam_patch_list, lang: lang); // Finished patching Digicam Print Channel - patching_progress[2] = "digicam:done"; - patching_progress[3] = "demae:in_progress"; + patchingProgress_express["digicam"] = "done"; + patchingProgress_express["demae"] = "in_progress"; } // Patching Demae Channel - static void Demae_Patch() + static void Demae_Patch(string reg = "", string demae_version = "", string lang = "") { task = "Patching Demae Channel"; - string demae_folder = ""; - string demae_ver = ""; - - string[] demae_patches = new string[3]; // If reg is EN, change channel title to "Food Channel", else "Demae Channel" - string demae_title = "Demae Channel"; - if (reg == "EN") - demae_title = "Food Channel"; - - switch (demae_version) + string demae_title = reg == "EN" ? "Food Channel" : "Demae Channel"; + + // Map demae_version to corresponding patch list + var patchLists = new Dictionary>>() { - case "dominos": - demae_patches = new string[] { "Dominos_0", "Dominos_1", "Dominos_2" }; - demae_folder = "Dominos"; - demae_ver = "Dominos"; - break; - case "deliveroo": - demae_patches = new string[] { "Deliveroo_0", "Deliveroo_1", "Deliveroo_2" }; - demae_folder = "Deliveroo"; - demae_ver = "Deliveroo"; - break; - default: - demae_patches = new string[] { "Demae_0", "Demae_1", "Demae_2" }; - demae_folder = "Demae"; - demae_ver = "Standard"; - break; - } + ["dominos"] = new List>() + { + new KeyValuePair("Dominos_0", "00000000"), + new KeyValuePair("Dominos_1", "00000001"), + new KeyValuePair("Dominos_2", "00000002") + }, + ["deliveroo"] = new List>() + { + new KeyValuePair("Deliveroo_0", "00000000"), + new KeyValuePair("Deliveroo_1", "00000001"), + new KeyValuePair("Deliveroo_2", "00000002") + }, + ["standard"] = new List>() + { + new KeyValuePair("Demae_0", "00000000"), + new KeyValuePair("Demae_1", "00000001"), + new KeyValuePair("Demae_2", "00000002") + } + }; + + // Get patch list based on demae_version + var demae_patch_list = patchLists.TryGetValue(demae_version, out var patchList) ? patchList : patchLists[""]; - string[] demae_apps = { "00000000", "00000001", "00000002" }; + // Dictionary to properly name demae folders and versions + var demaeNameVer_dict = new Dictionary + { + // (Demae folder name, Demae version) + ["dominos"] = ("Dominos", "Dominos"), + ["deliveroo"] = ("Deliveroo", "Deliveroo"), + ["standard"] = ("Demae", "Standard") + }; - PatchCoreChannel(demae_folder, $"{demae_title} ({demae_ver})", "000100014843484a", demae_patches, demae_apps); + // Set demae_folder and demae_ver based on demae_version + if (demaeNameVer_dict.TryGetValue(demae_version, out var demaeInfo)) + { + var (demaeFolder, demaeVer) = demaeInfo; + PatchCoreChannel(demaeFolder, $"{demae_title} ({demaeVer})", "000100014843484a", demae_patch_list, lang: lang); + } // Finished patching Demae Channel - patching_progress[3] = "demae:done"; - patching_progress[4] = "nc:in_progress"; + patchingProgress_express["demae"] = "done"; + patchingProgress_express[!installKirbyTV ? "nc" : "kirbytv"] = "in_progress"; + } + + // Patching Kirby TV Channel (if applicable) + static void KirbyTV_Patch() + { + task = "Patching Kirby TV Channel"; + + PatchWC24Channel("ktv", "Kirby TV Channel", 257, null, "0001000148434d50", "KirbyTV_2", "0000000e"); + + // Finished patching Kirby TV Channel + patchingProgress_express["kirbytv"] = "done"; + patchingProgress_express["nc"] = "in_progress"; } + // Patching Nintendo Channel - static void NC_Patch() + static void NC_Patch(string nc_reg = "") { task = "Patching Nintendo Channel"; - string NC_titleID = ""; - string appNum = ""; - string channel_title = "Nintendo Channel"; - switch (nc_reg) - { - case "USA": - NC_titleID = "0001000148415445"; - appNum = "0000002c"; - break; - case "PAL": - NC_titleID = "0001000148415450"; - appNum = "0000002d"; - break; - case "Japan": - NC_titleID = "000100014841544a"; - appNum = "0000003e"; - channel_title = "Minna no Nintendo Channel"; - break; - } - PatchWC24Channel("nc", $"{channel_title}", 1792, nc_reg, NC_titleID, "NC_1", appNum); + // Properly set Nintendo Channel titleID, appNum, and channel_title + var ncRegData_dict = new Dictionary + { + {"USA", ("0001000148415445", "0000002c", "Nintendo Channel")}, + {"PAL", ("0001000148415450", "0000002d", "Nintendo Channel")}, + {"Japan", ("000100014841544a", "0000003e", "Minna no Nintendo Channel")} + }; + + if (ncRegData_dict.TryGetValue(nc_reg, out var data)) + { + var (NC_titleID, appNum, channel_title) = data; + PatchWC24Channel("nc", $"{channel_title}", 1792, nc_reg, NC_titleID, "NC_1", appNum); + } // Finished patching Nintendo Channel - patching_progress[4] = "nc:done"; - patching_progress[5] = "forecast:in_progress"; + patchingProgress_express["nc"] = "done"; + patchingProgress_express["forecast"] = "in_progress"; } // Patching Forecast Channel - static void Forecast_Patch() + static void Forecast_Patch(string forecast_reg = "") { task = "Patching Forecast Channel"; - string Forecast_titleID = ""; - switch (forecast_reg) + + // Properly set Forecast Channel titleID + var forecastRegData_dict = new Dictionary { - case "USA": - Forecast_titleID = "0001000248414645"; - break; - case "PAL": - Forecast_titleID = "0001000248414650"; - break; - case "Japan": - Forecast_titleID = "000100024841464a"; - break; - } + {"USA", "0001000248414645"}, + {"PAL", "0001000248414650"}, + {"Japan", "000100024841464a"} + }; - PatchWC24Channel("forecast", $"Forecast Channel [{platformType}]", 7, forecast_reg, Forecast_titleID, "Forecast_1", "0000000d"); + if (forecastRegData_dict.TryGetValue(forecast_reg, out var forecastTitleID)) + PatchWC24Channel("forecast", $"Forecast Channel", 7, forecast_reg, forecastTitleID, "Forecast_1", "0000000d"); // Finished patching Forecast Channel - patching_progress[5] = "forecast:done"; - patching_progress[6] = "finishing:in_progress"; + patchingProgress_express["forecast"] = "done"; + patchingProgress_express["finishing"] = "in_progress"; } // Finish SD Copy @@ -1196,8 +2211,8 @@ static void FinishSDCopy() try { // Copy apps and WAD folder to SD card - CopyFolder("apps", Path.Combine(sdcard, "apps")); - CopyFolder("WAD", Path.Combine(sdcard, "WAD")); + CopyFolder("apps", Path.Join(sdcard, "apps")); + CopyFolder("WAD", Path.Join(sdcard, "WAD")); } catch (Exception e) { @@ -1219,7 +2234,7 @@ static void FinishSDCopy() Directory.Delete("WiiLink_Patcher", true); // Finished patching - patching_progress[6] = "finishing:done"; + patchingProgress_express["finishing"] = "done"; } static void Finished() @@ -1230,50 +2245,32 @@ static void Finished() AnsiConsole.MarkupLine("[bold slowblink lime]Patching Completed![/]\n"); if (sdcard != null) - { - Console.WriteLine("Every file is in it's place on your SD card!\n"); - } + Console.WriteLine("Every file is in it's place on your SD Card / USB Drive!\n"); else { - Console.WriteLine("Please connect your Wii SD card and copy the \u001b[1mWAD\u001b[0m and \u001b[1mapps\u001b[0m folders to the root (main folder) of your SD card."); - Console.WriteLine($"You can find these folders in the \u001b[1m{curDir}\u001b[0m folder of your computer.\n"); + AnsiConsole.MarkupLine("[bold]Please connect your Wii SD Card / USB Drive and copy the [u]WAD[/] and [u]apps[/] folders to the root (main folder) of your\nSD Card / USB Drive.[/]\n"); + + AnsiConsole.MarkupLine($"You can find these folders in the [u]{curDir}[/] folder of your computer.\n"); } - AnsiConsole.Markup("Please proceed with the tutorial that you can find on [bold lime link]https://wii.guide/wiilink[/]\n"); + AnsiConsole.MarkupLine("Please proceed with the tutorial that you can find on [bold lime link]https://wii.guide/wiilink[/]\n"); - Console.WriteLine("What would you like to do now?\n"); - if (sdcard != null) - Console.WriteLine("1. Open the SD card folder"); - else - Console.WriteLine("1. Open the folder"); - Console.WriteLine("2. Go back to the main menu"); - Console.WriteLine("3. Exit the program\n"); + AnsiConsole.MarkupLine("[bold]What would you like to do now?[/]\n"); + + AnsiConsole.MarkupLine(sdcard != null ? "1. Open the SD Card / USB Drive folder" : "1. Open the folder"); + AnsiConsole.MarkupLine("2. Go back to the main menu"); + AnsiConsole.MarkupLine("3. Exit the program\n"); int choice = UserChoose("123"); switch (choice) { case 1: if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - if (sdcard != null) - Process.Start(@"explorer.exe", sdcard); - else - Process.Start(@"explorer.exe", curDir); - } + Process.Start(@"explorer.exe", sdcard != null ? sdcard : curDir); else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - if (sdcard != null) - Process.Start("xdg-open", sdcard); - else - Process.Start("xdg-open", curDir); - } + Process.Start("xdg-open", sdcard != null ? sdcard : curDir); else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - if (sdcard != null) - Process.Start("open", sdcard); - else - Process.Start("open", curDir); - } + Process.Start("open", sdcard != null ? sdcard : curDir); break; case 2: MainMenu(); @@ -1299,21 +2296,21 @@ static void SDCardSelect() { PrintHeader(); - Console.WriteLine("\u001b[1;32mManually Select SD Card\u001b[0m\n"); + AnsiConsole.MarkupLine("[bold lime]Manually Select SD Card / USB Drive Path[/]\n"); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - Console.WriteLine("Please enter the drive letter of your SD card (e.g. E)"); + Console.WriteLine("Please enter the drive letter of your SD card/USB drive (e.g. E)"); else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - Console.WriteLine("Please enter the mount name of your SD card (e.g. /media/username/Wii)"); + Console.WriteLine("Please enter the mount name of your SD card/USB drive (e.g. /media/username/Wii)"); else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - Console.WriteLine("Please enter the volume name of your SD card (e.g. /Volumes/Wii)"); + Console.WriteLine("Please enter the volume name of your SD card/USB drive (e.g. /Volumes/Wii)"); - Console.WriteLine("(Type \u001b[1mEXIT\u001b[0m to go back to the previous menu)\n"); + AnsiConsole.MarkupLine("(Type [bold]EXIT[/] to go back to the previous menu)\n"); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - Console.Write("New SD card drive: "); + Console.Write("New SD card/USB drive: "); else - Console.Write("New SD card volume: "); + Console.Write("New SD card/USB drive volume: "); // Get user input, if user presses ESC (without needing to press ENTER), go back to previous menu sdcard_new = Console.ReadLine(); @@ -1333,7 +2330,7 @@ static void SDCardSelect() // Error if drive letter is more than 1 character if (sdcard_new?.Length > 1) { - Console.WriteLine("\u001b[1;31mDrive letter must be 1 character!\u001b[0m"); + AnsiConsole.MarkupLine("[bold red]Drive letter must be 1 character![/]"); System.Threading.Thread.Sleep(2000); continue; } @@ -1350,29 +2347,17 @@ static void SDCardSelect() } // Prevent user from selecting boot drive - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - if (sdcard_new == "C:\\") - { - Console.WriteLine("\u001b[1;31mYou cannot select your boot drive!\u001b[0m"); - System.Threading.Thread.Sleep(2000); - continue; - } - } - else + if (Path.GetPathRoot(sdcard_new) == Path.GetPathRoot(Path.GetPathRoot(Environment.SystemDirectory))) { - if (sdcard_new == "/") - { - Console.WriteLine("\u001b[1;31mYou cannot select your boot drive!\u001b[0m"); - System.Threading.Thread.Sleep(2000); - continue; - } + AnsiConsole.MarkupLine("[bold red]You cannot select your boot drive![/]"); + System.Threading.Thread.Sleep(2000); + continue; } // Check if new SD card path is the same as the old one if (sdcard_new == sdcard) { - Console.WriteLine("\u001b[1;31mYou have already selected this SD card!\u001b[0m"); + AnsiConsole.MarkupLine("[bold red]You have already selected this SD card/USB drive![/]"); System.Threading.Thread.Sleep(2000); continue; } @@ -1381,16 +2366,16 @@ static void SDCardSelect() if (!Directory.Exists(sdcard_new)) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - Console.WriteLine("\u001b[1;31mDrive does not exist!\u001b[0m"); + AnsiConsole.MarkupLine("[bold red]Drive does not exist![/]"); else - Console.WriteLine("\u001b[1;31mVolume does not exist!\u001b[0m"); + AnsiConsole.MarkupLine("[bold red]Volume does not exist![/]"); System.Threading.Thread.Sleep(2000); continue; } // Check if SD card has /apps folder (using PathCombine) - if (Directory.Exists(Path.Combine(sdcard_new, "apps"))) + if (Directory.Exists(Path.Join(sdcard_new, "apps"))) { // SD card is valid sdcard = sdcard_new; @@ -1399,7 +2384,7 @@ static void SDCardSelect() else { // SD card is invalid - Console.WriteLine("\n\u001b[1mDrive detected, but no /apps folder found!\u001b[0m"); + AnsiConsole.MarkupLine("\n[bold]Drive detected, but no /apps folder found![/]"); Console.WriteLine("Please create it first and then try again.\n"); // Press any key to continue @@ -1409,63 +2394,197 @@ static void SDCardSelect() } } + // Language Menu function, supports English, Spanish, French, and Japanese + static void LanguageMenu() + { + while (true) + { + PrintHeader(); + PrintNotice(); + + List languages = new List(); + languages.Add("English"); + + AnsiConsole.MarkupLine($"[bold]Notice:[/] This feature is a work in progress, so only English is available for now.\n"); + + AnsiConsole.MarkupLine($"[bold lime]Choose a Language[/]\n"); + + // Display languages + for (int i = 0; i < languages.Count; i++) + AnsiConsole.MarkupLine($"{i + 1}. {languages[i]}"); + + AnsiConsole.MarkupLine($"\n{languages.Count + 1}. Go back to Settings\n"); + + + var languageCodes = new Dictionary(); + languageCodes.Add(1, "EN"); + + // Choices (build from languages + 1 sequentially) + string choices = ""; + for (int i = 1; i <= languages.Count + 1; i++) + choices += i.ToString(); + + int choice = UserChoose(choices); + + // Map choice to language code + if (languageCodes.TryGetValue(choice, out var langCode)) + { + // Set programLang to chosen language code + localizeLang = langCode.ToLower(); + + // Since English is hardcoded, there's no language pack for it + if (localizeLang == "en") + { + SettingsMenu(); + break; + } + + // Download language pack if it doesn't exist + if (!File.Exists(Path.Join(tempDir, "LanguagePack", $"LocalizedText.{langCode}.json"))) + DownloadLanguagePack(langCode); + + // Set localizedText to use the language pack + localizedText = JObject.Parse(File.ReadAllText(Path.Join(tempDir, "LanguagePack", $"LocalizedText.{langCode}.json"))); + + SettingsMenu(); + } + else if (choice == languages.Count + 1) + { + SettingsMenu(); + } + } + } + + static void DownloadLanguagePack(string languageCode) + { + string URL = "http://pabloscorner.akawah.net/WL24-Patcher/TextLocalization"; + + AnsiConsole.MarkupLine($"\n[bold lime]Downloading Language Pack ({languageCode})[/]\n"); + + // Create LanguagePack folder if it doesn't exist + var languagePackDir = Path.Join(tempDir, "LanguagePack"); + if (!Directory.Exists(languagePackDir)) + Directory.CreateDirectory(languagePackDir); + + // Download language file + var languageFileUrl = $"{URL}/LocalizedText.{languageCode.ToLower()}.json"; + var languageFilePath = Path.Join(languagePackDir, $"LocalizedText.{languageCode.ToLower()}.json"); + + DownloadFile(languageFileUrl, languageFilePath, $"Language Pack ({languageCode})"); + } + + static void SettingsMenu() + { + while (true) + { + PrintHeader(); + PrintNotice(); + + AnsiConsole.MarkupLine($"[bold lime]Settings[/]\n"); + + if (!inCompatabilityMode) + { + Console.WriteLine($"1. Change Language"); + Console.WriteLine($"2. Credits\n"); + + Console.WriteLine($"3. Go back to Main Menu\n"); + } + else + { + Console.WriteLine("1. Credits\n"); + + Console.WriteLine("2. Go back to Main Menu\n"); + } + + int choice = UserChoose("123"); + switch (choice) + { + case 1 when !inCompatabilityMode: + LanguageMenu(); + break; + case 1 when inCompatabilityMode: + CreditsScreen(); + break; + case 2 when !inCompatabilityMode: + CreditsScreen(); + break; + case 2 when inCompatabilityMode: + MainMenu(); + break; + case 3 when !inCompatabilityMode: + MainMenu(); + break; + default: + break; + } + } + } + // Main Menu function static void MainMenu() { - // Delete temp folder if it exists - if (Directory.Exists(Path.Combine(curDir, "WiiLink_Patcher"))) - Directory.Delete(Path.Combine(curDir, "WiiLink_Patcher"), true); - if (Directory.Exists(Path.Combine(curDir, "unpack"))) - Directory.Delete(Path.Combine(curDir, "unpack"), true); - if (Directory.Exists(Path.Combine(curDir, "unpack-patched"))) - Directory.Delete(Path.Combine(curDir, "unpack-patched"), true); - + // Delete specific folders in temp folder + string tempPath = Path.Join(tempDir); + if (Directory.Exists(tempPath)) + { + string[] foldersToDelete = { "Patches", "Unpack", "Unpack_Patched" }; + foreach (string folder in foldersToDelete) + { + string folderPath = Path.Join(tempPath, folder); + if (Directory.Exists(folderPath)) + Directory.Delete(folderPath, true); + } + } + while (true) { - // Call the PrintHeader() method + // Display header and notice PrintHeader(); - PrintAnnouncement(); + PrintNotice(); - AnsiConsole.MarkupLine("[bold]Welcome to the WiiLink Patcher![/]"); - Console.WriteLine(); - Console.WriteLine("1. Start"); - Console.WriteLine("2. Credits"); - Console.WriteLine(); - Console.WriteLine("3. Exit Patcher"); - Console.WriteLine(); + // Main Menu text + string welcomeMessage = localizeLang == "en" + ? "Welcome to the WiiLink Patcher!\n" + : $"{localizedText?["MainMenu"]?["welcomeMessage"]}\n"; + AnsiConsole.MarkupLine(welcomeMessage); - if (sdcard != null) - AnsiConsole.MarkupLine("[bold lime]Detected SD Card:[/] " + sdcard); - else - AnsiConsole.MarkupLine("[bold red]Could not detect your SD Card.[/]"); + AnsiConsole.MarkupLine($"{(localizeLang == "en" ? "1. Start Express Install Setup" : localizedText?["MainMenu"]?["startExpressSetup"])} [bold lime](Recommended)[/]"); + AnsiConsole.MarkupLine($"{(localizeLang == "en" ? "2. Start Custom Install Setup" : localizedText?["MainMenu"]?["startCustomSetup"])} [bold](Advanced)[/]"); + AnsiConsole.MarkupLine($"{(localizeLang == "en" ? "3. Settings" : localizedText?["MainMenu"]?["settings"])}\n"); + AnsiConsole.MarkupLine($"{(localizeLang == "en" ? "4. Exit Patcher" : localizedText?["MainMenu"]?["exitPatcher"])}\n"); - Console.WriteLine("M. Manually select SD Card path"); - Console.WriteLine(); + string SDDetectedOrNot = sdcard != null + ? $"[bold lime]{(localizeLang == "en" ? "Detected SD Card / USB Drive:" : localizedText?["MainMenu"]?["sdCardDetected"])}[/] {sdcard}" + : $"[bold red]{(localizeLang == "en" ? "Could not detect your SD Card / USB Drive!" : localizedText?["MainMenu"]?["noSDCard"])}[/]"; + AnsiConsole.MarkupLine(SDDetectedOrNot); + + AnsiConsole.MarkupLine($"{(localizeLang == "en" ? "R. Automatically detect SD Card / USB Drive" : localizedText?["MainMenu"]?["automaticDetection"])}"); + AnsiConsole.MarkupLine($"{(localizeLang == "en" ? "M. Manually select SD Card / USB Drive path" : localizedText?["MainMenu"]?["manualSelection"])}\n"); // User chooses an option - int choice = UserChoose("123Mm"); + int choice = UserChoose("1234RrMm"); switch (choice) { - case 1: - // Start (1) + case 1: // Start Express Install CoreChannel_LangSetup(); break; - case 2: - // Credits (2) - Credits(); + case 2: // Start Custom Install + CustomInstall_CoreChannel_Setup(); break; - case 3: - // Clear console and Exit app (3) + case 3: // Settings + SettingsMenu(); + break; + case 4: // Clear console and Exit app Console.Clear(); ExitApp(); break; - case 4: - // Manually select SD Card path (O) - SDCardSelect(); - break; case 5: - // Manually select SD Card path (o) + case 6: // Automatically detect SD Card path (R/r) + sdcard = DetectSDCard(); + break; + case 7: + case 8: // Manually select SD Card path (M/m) SDCardSelect(); break; default: @@ -1475,44 +2594,67 @@ static void MainMenu() } // Check if server is up - static async System.Threading.Tasks.Task CheckServerAsync(string serverURL) + static async Task<(System.Net.HttpStatusCode, string)> CheckServerAsync(string serverURL, int maxRetries = 3, int retryDelayMs = 1000) { + // Use the following URL to check if the server is up string url = $"{serverURL}/wiinoma/WiinoMa_1_English.delta"; - bool isServerUp = false; var httpClient = new HttpClient(); - try + PrintHeader(); + Console.WriteLine($"Checking server status..."); + + for (int i = 0; i < maxRetries; i++) { - PrintHeader(); - Console.WriteLine("Checking server status..."); + try + { + using (var response = await httpClient.GetAsync(url)) + { + if (response.IsSuccessStatusCode) + { + AnsiConsole.MarkupLine("[bold lime]Successfully connected to server![/]"); + await Task.Delay(1000); // Wait for 1 second + return (System.Net.HttpStatusCode.OK, "Successfully connected to server!"); + } + else + { + return (response.StatusCode == default(HttpStatusCode) ? System.Net.HttpStatusCode.InternalServerError : response.StatusCode, response.ReasonPhrase ?? "Unknown error"); + } + } + } + catch (HttpRequestException ex) + { + if (i == maxRetries - 1) + { + AnsiConsole.MarkupLine("[bold red]Connection to server failed![/]\n"); + return (ex.StatusCode ?? System.Net.HttpStatusCode.InternalServerError, ex.Message); + } + } - using (var response = await httpClient.GetAsync(url)) + if (i < maxRetries - 1) { - isServerUp = response.IsSuccessStatusCode; - Console.WriteLine("\u001b[1;32mServer is up!\u001b[0m"); - System.Threading.Thread.Sleep(1000); + AnsiConsole.MarkupLine($"Retrying in [bold]{retryDelayMs / 1000}[/] seconds...\n"); + await Task.Delay(retryDelayMs); } } - catch - { - isServerUp = false; - Console.WriteLine("\u001b[1;31mServer is down!\u001b[0m"); - System.Threading.Thread.Sleep(1000); - } - return isServerUp; + return (System.Net.HttpStatusCode.ServiceUnavailable, "Connection to server failed!"); } - static void ServerDown() + static void ConnectionFailed(System.Net.HttpStatusCode statusCode, string msg) { PrintHeader(); - Console.WriteLine("The WiiLink server is currently down!\n"); - Console.WriteLine("It seems that our server is currently down. We're trying to get it back up as soon as possible.\n"); - Console.WriteLine("Stay tuned on our Discord server for updates:"); - AnsiConsole.MarkupLine("[link bold lime]https://discord.gg/WiiLink\u001b[/]\n"); + AnsiConsole.MarkupLine("[bold blink red]Connection to server failed![/]\n"); + + Console.WriteLine("Connection to the server failed. Please check your internet connection and try again.\n"); + Console.WriteLine("It seems that either the server is down or your internet connection is not working.\n"); + AnsiConsole.MarkupLine("If you are sure that your internet connection is working, please join our [link=https://discord.gg/WiiLink bold lime]Discord Server[/] and report this issue.\n"); + + AnsiConsole.MarkupLine("[bold]Status code:[/] " + statusCode); + AnsiConsole.MarkupLine("[bold]Message:[/] " + msg); + + AnsiConsole.MarkupLine("\n[bold yellow]Press any key to exit...[/]"); - Console.Write("Press any key to exit..."); Console.ReadKey(); ExitApp(); } @@ -1545,7 +2687,7 @@ public static async Task CheckForUpdates(string currentVersion) string latestVersion = updateInfo.Split('\n')[0].Trim(); // Map operating system names to executable names - Dictionary executables = new Dictionary + var executables = new Dictionary { { "Windows", $"WiiLink_Patcher_Windows_{latestVersion}.exe" }, { "Linux", RuntimeInformation.ProcessArchitecture == Architecture.Arm64 ? $"WiiLink_Patcher_Linux-arm64_{latestVersion}" : $"WiiLink_Patcher_Linux-x64_{latestVersion}" }, @@ -1585,7 +2727,7 @@ public static async Task CheckForUpdates(string currentVersion) switch (choice) { - case 1: + case 1: // Download the latest version // Determine the operating system name string? osName = System.Runtime.InteropServices.RuntimeInformation .IsOSPlatform(OSPlatform.Windows) ? "Windows" : @@ -1630,7 +2772,7 @@ public static async Task CheckForUpdates(string currentVersion) // Exit the program ExitApp(); return; - case 2: + case 2: // Don't download the latest version return; default: break; @@ -1645,80 +2787,69 @@ public static async Task CheckForUpdates(string currentVersion) } } - // Error detected! - static void ErrorScreen(int exitCode, string? msg) + static void ErrorScreen(int exitCode, string msg = "") { PrintHeader(); - Console.WriteLine("\u001b[5;31mAn error has occurred.\u001b[0m\n"); + AnsiConsole.MarkupLine("[bold red]An error has occurred.[/]\n"); - Console.WriteLine("\u001b[1mERROR DETAILS:\u001b[0m"); - Console.WriteLine($" * \u001b[1mTask:\u001b[0m {task}"); - if (msg == null) - Console.WriteLine($" * \u001b[1mCommand:\u001b[0m {curCmd}"); - else - Console.WriteLine($" * \u001b[1mMessage:\u001b[0m {msg}"); - Console.WriteLine($" * \u001b[1mExit code:\u001b[0m {exitCode}\n"); + AnsiConsole.MarkupLine("[bold]ERROR DETAILS:[/]\n"); + AnsiConsole.MarkupLine($" * [bold]Task:[/] {task}"); + AnsiConsole.MarkupLine(msg == null ? $" * [bold]Command:[/] {curCmd}" : $" * [bold]Message:[/] {msg}"); + AnsiConsole.MarkupLine($" * [bold]Exit code:[/] {exitCode}\n"); AnsiConsole.MarkupLine("Please open an issue on our GitHub page ([link bold lime]https://github.com/WiiLink24/WiiLink24-Patcher/issues[/]) and describe the"); - Console.WriteLine("issue you're having."); + AnsiConsole.MarkupLine("error you encountered. Please include the error details above in your issue.\n"); // Press any key to go back to the main menu - Console.WriteLine("\n\u001b[1mPress any key to go back to the main menu...\u001b[0m"); + AnsiConsole.MarkupLine("\n[bold]Press any key to go back to the main menu...[/]"); Console.ReadKey(); // Go back to the main menu MainMenu(); } - public static void ExecuteProcess(string programName, params string[] args) + private static void CopyFolder(string sourcePath, string destinationPath) { + DirectoryInfo source = new DirectoryInfo(sourcePath); + DirectoryInfo destination = new DirectoryInfo(destinationPath); - curCmd = $"{programName} {string.Join(" ", args)}"; - - Process process = new Process(); - process.StartInfo.FileName = programName; - process.StartInfo.Arguments = string.Join(" ", args); - process.StartInfo.RedirectStandardError = true; - process.Start(); - - // Wait for the process to exit before accessing its information - process.WaitForExit(); + // If the destination folder doesn't exist, create it. + if (!destination.Exists) + destination.Create(); - // Get the exit code after the process has exited - exitCode = process.ExitCode; - string exitMessage = process.StandardError.ReadToEnd(); + // Copy each file to the destination folder. + foreach (var file in source.GetFiles()) + file.CopyTo(Path.Combine(destination.FullName, file.Name), true); - // If the exit code is not 0, then an error has occurred - if (exitCode != 0) - ErrorScreen(exitCode, exitMessage); + // Recursively copy each subdirectory to the destination folder. + foreach (var subfolder in source.GetDirectories()) + CopyFolder(subfolder.FullName, Path.Combine(destination.FullName, subfolder.Name)); } - private static void CopyFolder(string sourceFolder, string destinationFolder) + public static void WinCompatWarning() { - if (!Directory.Exists(destinationFolder)) - Directory.CreateDirectory(destinationFolder); + PrintHeader(); - string[] files = Directory.GetFiles(sourceFolder); + AnsiConsole.MarkupLine("[bold red]WARNING:[/] Older version of Windows detected!\n"); - foreach (string file in files) - { - string fileName = Path.GetFileName(file); - string destFile = Path.Combine(destinationFolder, fileName); + AnsiConsole.MarkupLine("You are running the WiiLink Patcher on an older version of Windows."); + AnsiConsole.MarkupLine("While the patcher may work, it is not guaranteed to work on this version of Windows.\n"); - using (FileStream sourceStream = new FileStream(file, FileMode.Open, FileAccess.Read)) - { - using (FileStream destinationStream = new FileStream(destFile, FileMode.Create, FileAccess.Write)) - { - sourceStream.CopyTo(destinationStream); - } - } - } + AnsiConsole.MarkupLine("If you encounter any issues while running the patcher, we will most likely not be able to help you.\n"); + + AnsiConsole.MarkupLine("Please consider upgrading to Windows 10 or above before continuing, or use the macOS/Linux patcher instead.\n"); - string[] subFolders = Directory.GetDirectories(sourceFolder); + AnsiConsole.MarkupLine("Otherwise, for the best visual experience, set your console font to [lime]Consolas[/] with a size of [lime]16[/]."); + AnsiConsole.MarkupLine("Also, set your console window and buffer size to [lime]120x30[/].\n"); - foreach (string subfolder in subFolders) - CopyFolder(subfolder, Path.Combine(destinationFolder, Path.GetFileName(subfolder))); + AnsiConsole.MarkupLine("\n[bold yellow]Press ESC to quit, or any other key to proceed at your own risk...[/]"); + + ConsoleKeyInfo keyInfo = Console.ReadKey(); + if (keyInfo.Key == ConsoleKey.Escape) + ExitApp(); + + inCompatabilityMode = true; } // Exit console app @@ -1728,6 +2859,9 @@ static void ExitApp() if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) Console.Write($"\u001b[8;{console_height};{console_width}t"); + // Clear console + Console.Clear(); + Environment.Exit(0); } @@ -1740,28 +2874,33 @@ static async System.Threading.Tasks.Task Main(string[] args) console_width = Console.WindowWidth; console_height = Console.WindowHeight; - // Set console window size to 120x30 on macOS and Linux - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - Console.Write("\u001b[8;30;120t"); - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - // If console size is less than 100x25, then resize it - if (console_width < 100 || console_height < 25) - Console.Write("\u001b[8;30;120t"); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Console.Title = $"WiiLink Patcher {version}"; + if (DEBUG_MODE) Console.Title += $" [DEBUG MODE]"; + if (version.Contains("T")) Console.Title += $" (Test Build)"; + } - // Change Windows console title - Console.Title = $"WiiLink Patcher {version}"; + // Set console window size to 120x30 on macOS and Linux and on Windows, check for Windows version + switch (RuntimeInformation.OSDescription) + { + case string os when os.Contains("macOS"): + Console.Write("\u001b[8;30;120t"); + break; + case string os when os.Contains("Linux"): + if (console_width < 100 || console_height < 25) + Console.Write("\u001b[8;30;120t"); + break; + case string os when os.Contains("Windows"): + if (Environment.OSVersion.Version.Major < 10) + WinCompatWarning(); + break; + } // Check if the server is up - if (!await CheckServerAsync(wiiLinkPatcherUrl)) - ServerDown(); - - // Delete temp folders - if (Directory.Exists("WiiLink_Patcher")) - Directory.Delete(Path.Join("WiiLink_Patcher"), true); - if (Directory.Exists("unpack")) - Directory.Delete(Path.Join("unpack"), true); - if (Directory.Exists("unpack-patched")) - Directory.Delete(Path.Join("unpack-patched"), true); + var result = await CheckServerAsync(wiiLinkPatcherUrl); + if (result != (System.Net.HttpStatusCode.OK, "Successfully connected to server!")) + ConnectionFailed(result.Item1, result.Item2); // Check latest version if not on a test build if (!version.Contains("T"))