diff --git a/.gitignore b/.gitignore index a72f3ddc..16d25d89 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,13 @@ ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +Assets/ +Cores/ +Platforms/ +Saves/ +Spiritualized_*_readme.txt + + # User-specific files *.rsuser *.suo diff --git a/Program.cs b/Program.cs index 2e6a2198..c554c534 100644 --- a/Program.cs +++ b/Program.cs @@ -1,15 +1,33 @@ using pannella.analoguepocket; using System.Net.Http.Headers; using System.Text.Json; +using CommandLine; internal class Program { - private const string VERSION = "1.3.0"; + private const string VERSION = "1.4.0"; private const string API_URL = "https://api.github.com/repos/mattpannella/pocket_core_autoupdate_net/releases"; private const string REMOTE_CORES_FILE = "https://raw.githubusercontent.com/mattpannella/pocket_core_autoupdate_net/main/auto_update.json"; private static async Task Main(string[] args) { + bool autoUpdate = false; + string location = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName; + string path = Path.GetDirectoryName(location); + + Parser.Default.ParseArguments(args) + .WithParsed(o => + { + if(o.Update) { + autoUpdate = true; + } + if(o.InstallPath != null && o.InstallPath != "") { + Console.WriteLine("path: " + o.InstallPath); + path = o.InstallPath; + } + } + ); + ConsoleKey response; Console.WriteLine("Analogue Pocket Core Updater v" + VERSION); @@ -26,27 +44,20 @@ private static async Task Main(string[] args) } } - //string path = "/Users/matt/pocket-test"; - //string cores = "/Users/matt/development/c#/pocket_updater/auto_update.json"; + //path = "/Users/mattpannella/pocket-test"; + //string cores = "/Users/mattpannella/development/c#/pocket_updater/auto_update.json"; - string location = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName; - string path = Path.GetDirectoryName(location); + string cores = path + "/auto_update.json"; - Console.WriteLine("Download master cores list file from github? (This will overwrite your current file) [y/n]:"); - response = Console.ReadKey(false).Key; - if (response == ConsoleKey.Y) { - try { - Console.WriteLine("Downloading..."); - await HttpHelper.DownloadFileAsync(REMOTE_CORES_FILE, cores); - Console.WriteLine("Download complete:"); - Console.WriteLine(cores); - } catch (UnauthorizedAccessException e) { - Console.WriteLine("Unable to save file."); - Console.WriteLine(e.Message); - Console.ReadLine(); - Environment.Exit(1); + if(!autoUpdate) { + Console.WriteLine("Download master cores list file from github? (This will overwrite your current file) [y/n]:"); + response = Console.ReadKey(false).Key; + if (response == ConsoleKey.Y) { + await DownloadCoresFile(cores); } + } else { + await DownloadCoresFile(cores); } PocketCoreUpdater updater = new PocketCoreUpdater(path, cores); @@ -61,6 +72,21 @@ private static async Task Main(string[] args) Console.ReadLine(); //wait for input so the console doesn't auto close in windows } + async static Task DownloadCoresFile(string file) + { + try { + Console.WriteLine("Downloading cores file..."); + await HttpHelper.DownloadFileAsync(REMOTE_CORES_FILE, file); + Console.WriteLine("Download complete:"); + Console.WriteLine(file); + } catch (UnauthorizedAccessException e) { + Console.WriteLine("Unable to save file."); + Console.WriteLine(e.Message); + Console.ReadLine(); + Environment.Exit(1); + } + } + static void updater_StatusUpdated(object sender, StatusUpdatedEventArgs e) { Console.WriteLine(e.Message); @@ -96,4 +122,13 @@ async static Task CheckVersion() return false; } } +} + +public class Options +{ + [Option ('u', "update", Required = false, HelpText = "Automatically download newest core list without asking.")] + public bool Update {get; set; } + + [Option('p', "path", HelpText = "Absolute path to install location", Required = false)] + public string? InstallPath { get; set; } } \ No newline at end of file diff --git a/Updater.cs b/Updater.cs index 9fff8f71..cfb12113 100644 --- a/Updater.cs +++ b/Updater.cs @@ -2,6 +2,7 @@ using System.IO.Compression; using System.Text.Json; using System.Net.Http.Headers; +using System.Text.RegularExpressions; namespace pannella.analoguepocket; @@ -9,7 +10,15 @@ public class PocketCoreUpdater { private const string GITHUB_API_URL = "https://api.github.com/repos/{0}/{1}/releases"; private static readonly string[] ZIP_TYPES = {"application/x-zip-compressed", "application/zip"}; + private const string FIRMWARE_URL = "https://www.analogue.co/support/pocket/firmware/latest"; private const string ZIP_FILE_NAME = "core.zip"; + private static readonly Regex BIN_REGEX = new Regex(@"(?inx) + ]* + href \s* = \s* + (? ['""] ) + (? [^'""]*\.bin ) + \k + [^>]* >"); private bool _installBios = false; private string _githubApiKey; @@ -24,6 +33,8 @@ public class PocketCoreUpdater /// public string CoresFile { get; set; } + public string SettingsFile { get; set; } + /// /// Constructor /// @@ -41,6 +52,8 @@ public PocketCoreUpdater(string updateDirectory, string coresFile = null) throw new FileNotFoundException("Cores json file not found: " + coresFile); } } + + SettingsFile = Path.Combine(updateDirectory, "pocket_updater_settings.json"); } /// @@ -66,6 +79,8 @@ public void InstallBiosFiles(bool set) /// public async Task RunUpdates() { + await UpdateFirmware(); + if(CoresFile == null) { throw new Exception("No Cores file has been set"); } @@ -267,6 +282,45 @@ protected virtual void OnStatusUpdated(StatusUpdatedEventArgs e) } } + public async Task UpdateFirmware() + { + _writeMessage("Checking for firmware updates..."); + string html = await HttpHelper.GetHTML(FIRMWARE_URL); + + MatchCollection matches = BIN_REGEX.Matches(html); + if(matches.Count != 1) { + _writeMessage("cant find it"); + return; + } + + string firmwareUrl = matches[0].Groups["url"].ToString(); + string[] parts = firmwareUrl.Split("/"); + string filename = parts[parts.Length-1]; + + Settings coresList = new Settings(); + if(File.Exists(SettingsFile)) { + string json = File.ReadAllText(SettingsFile); + coresList = JsonSerializer.Deserialize(json); + } + if(coresList.firmware.version != filename) { + _writeMessage("Firmware update found. Downloading..."); + await HttpHelper.DownloadFileAsync(firmwareUrl, Path.Combine(UpdateDirectory, filename)); + _writeMessage("Download Complete"); + _writeMessage(Path.Combine(UpdateDirectory, filename)); + var oldfile = Path.Combine(UpdateDirectory, coresList.firmware.version); + if(File.Exists(oldfile)) { + _writeMessage("Deleting old firmware file..."); + File.Delete(oldfile); + } + coresList.firmware.version = filename; + var options = new JsonSerializerOptions { WriteIndented = true }; + File.WriteAllText(SettingsFile, JsonSerializer.Serialize(coresList, options)); + _writeMessage("To install firmware, restart your Pocket."); + } else { + _writeMessage("Firmware up to date."); + } + } + /// /// Event is raised every time the updater prints a progress update /// diff --git a/helpers/HttpHelper.cs b/helpers/HttpHelper.cs index e0943761..3a01f445 100644 --- a/helpers/HttpHelper.cs +++ b/helpers/HttpHelper.cs @@ -4,8 +4,7 @@ public static class HttpHelper { private static readonly HttpClient _httpClient = new HttpClient(); - public static async Task DownloadFileAsync(string uri - , string outputPath) + public static async Task DownloadFileAsync(string uri, string outputPath) { Uri uriResult; @@ -15,4 +14,16 @@ public static async Task DownloadFileAsync(string uri byte[] fileBytes = await _httpClient.GetByteArrayAsync(uri); File.WriteAllBytes(outputPath, fileBytes); } + + public static async Task GetHTML(string uri) + { + Uri uriResult; + + if (!Uri.TryCreate(uri, UriKind.Absolute, out uriResult)) + throw new InvalidOperationException("URI is invalid."); + + string html = await _httpClient.GetStringAsync(uri); + + return html; + } } \ No newline at end of file diff --git a/models/Settings/Config.cs b/models/Settings/Config.cs new file mode 100644 index 00000000..fdacb897 --- /dev/null +++ b/models/Settings/Config.cs @@ -0,0 +1,7 @@ +namespace pannella.analoguepocket; + +public class Config +{ + public bool download_bios { get; set; } + public bool download_firmware { get; set; } +} \ No newline at end of file diff --git a/models/Settings/CoreSettings.cs b/models/Settings/CoreSettings.cs new file mode 100644 index 00000000..93abc7c7 --- /dev/null +++ b/models/Settings/CoreSettings.cs @@ -0,0 +1,7 @@ +namespace pannella.analoguepocket; + +public class CoreSettings +{ + public bool skip { get; set; } + public Bios bios { get; set; } +} \ No newline at end of file diff --git a/models/Settings/Firmware.cs b/models/Settings/Firmware.cs new file mode 100644 index 00000000..3e8c37ec --- /dev/null +++ b/models/Settings/Firmware.cs @@ -0,0 +1,11 @@ +namespace pannella.analoguepocket; + +public class Firmware +{ + public string version { get; set; } + + public Firmware() + { + version = ""; + } +} \ No newline at end of file diff --git a/models/Settings/Settings.cs b/models/Settings/Settings.cs new file mode 100644 index 00000000..b7fd4f0a --- /dev/null +++ b/models/Settings/Settings.cs @@ -0,0 +1,13 @@ +namespace pannella.analoguepocket; + +public class Settings +{ + public Firmware firmware { get; set; } + public Config config { get; set; } + public Dictionary coreSettings { get; set; } + + public Settings() + { + firmware = new Firmware(); + } +} \ No newline at end of file diff --git a/pocket_updater.csproj b/pocket_updater.csproj index 40c60dd4..3e33ae75 100644 --- a/pocket_updater.csproj +++ b/pocket_updater.csproj @@ -1,10 +1,11 @@ - - - - Exe - net6.0 - enable - enable - - - + + + Exe + net6.0 + enable + enable + + + + + \ No newline at end of file diff --git a/pocket_updater_settings.json b/pocket_updater_settings.json new file mode 100644 index 00000000..bdba37c7 --- /dev/null +++ b/pocket_updater_settings.json @@ -0,0 +1,52 @@ +{ + "firmware": { + "version": "" + }, + "config": { + "download_bios": true, + "download_firmware": true + }, + "coreSettings": { + "GBA": { + "skip": true, + "bios": { + "location": "assets/gba/common/", + "files": [ + { + "url": "https://archive.org/download/mister-console-bios-pack_theypsilon/GBA.zip/gba_bios.bin", + "file_name": "gba_bios.bin", + "zip": false, + "zip_file": null, + "overrideLocation": null + } + ] + } + }, + "NeoGeo": { + "skip": false, + "bios": { + "location": "assets/ng/common", + "files": [ + { + "url": "http://unibios.free.fr/download/uni-bios-40.zip", + "file_name": "uni-bios_1_0.rom", + "zip": true, + "zip_file": "uni-bios.rom", + "overrideLocation": null + }, + { + "url": "https://archive.org/download/mister-console-bios-pack_theypsilon/NeoGeo.zip/000-lo.lo", + "file_name": "000-lo.lo", + "zip": false, + "zip_file": null, + "overrideLocation": null + } + ] + } + }, + "SG-1000": { + "skip": true, + "bios": null + } + } +} \ No newline at end of file