using System; using System.ComponentModel; using System.IO; using System.Linq; using System.Net.Http; using System.Text.Json; using System.Threading.Tasks; using Avalonia; using Avalonia.Controls; using Avalonia.Media.Imaging; using Avalonia.Threading; using Microsoft.VisualBasic.CompilerServices; using Nocksoft.IO.ConfigFiles; namespace PS2_Manager.Core; public class Game { public string Name { get; set; } public string GameID { get; set; } public string GamePath { get; set; } public Bitmap? ArtworkFront { get; set; } public Bitmap? ArtworkBack { get; set; } public Bitmap? ArtworkDVD { get; set; } public EventHandler? InstallationFinished { get; set; } public UpdateVar InstallProgress { get; private set; } public Game(string isoPath, bool installed = false) { this.GamePath = isoPath; this.GameID = ISO.GetSerial(isoPath); if (!installed) { this.Name = this.GetGameTitle(); this.ArtworkFront = this.DownloadCover(Artwork.Type.Front); this.ArtworkBack = this.DownloadCover(Artwork.Type.Back); this.ArtworkDVD = this.DownloadCover(Artwork.Type.Disc); } else { this.Name = ParseFormattedFilename(Path.GetFileName(isoPath)); this.ArtworkFront = this.LoadCover(Artwork.Type.Front); this.ArtworkBack = this.LoadCover(Artwork.Type.Back); this.ArtworkDVD = this.LoadCover(Artwork.Type.Disc); } } public void ChangeName(string newName) { Console.Info("Changing name of " + this.Name + " to " + newName); try { this.Name = newName; string targetDirectory = settings.library_path.GetValue(); string newFileName = $"{this.GameID}.{this.Name}.iso"; string destPath = Path.Combine(Path.Combine(targetDirectory, "DVD"), newFileName); File.Move(this.GamePath, destPath); Console.Success(this.Name + " renamed successfully"); } catch (Exception e) { Console.Error(e.Message); } } public string GetGameTitle() { string url = $"https://weexnes.dev:7722/search/{this.GameID}"; try { using HttpClient client = new(); string json = client.GetStringAsync(url).GetAwaiter().GetResult(); Console.WriteLine(json); GameInfoApi? game = JsonSerializer.Deserialize(json); string title = game?.title ?? "Title not found"; if (title.Length > 32) { title = title.Substring(0, 32); } return title; } catch (Exception ex) { Console.Error($"{ex.Message}"); return ""; } } private string ParseFormattedFilename(string filename) { string[] parts = filename.Split('.'); string parsedName = parts[^2]; Console.Info(parsedName + " parsed from " + filename); return parsedName; } public async void Install() { this.InstallProgress = new UpdateVar(); await this.CopyIsoWithProgressAsync(); } private Bitmap DownloadCover(Artwork.Type type) { Console.Info("Downloading " + type + " Artwork for " + this.GameID); Bitmap cover = null; string url = ""; switch (type) { case Artwork.Type.Front: url = $"https://weexnes.dev:7722/art/{this.GameID}/{this.GameID}_COV.png"; break; case Artwork.Type.Back: url = $"https://weexnes.dev:7722/art/{this.GameID}/{this.GameID}_COV2.png"; break; case Artwork.Type.Disc: url = $"https://weexnes.dev:7722/art/{this.GameID}/{this.GameID}_ICO.png"; break; } try { using HttpClient client = new(); byte[] data = client.GetByteArrayAsync(url).GetAwaiter().GetResult(); using MemoryStream stream = new(data); var bitmap = new Bitmap(stream); cover = bitmap; } catch (Exception ex) { Console.Error($"Could not load cover for {this.GameID}: {ex.Message}"); } Console.Success(type + " Artwork for " + this.GameID + " downloaded successfully"); return cover; } public void Uninstall() { File.Delete(this.GamePath); } public async Task CopyIsoWithProgressAsync() { Console.Info("Copying ISO file for " + this + "..."); string targetDirectory = settings.library_path.GetValue(); Util.CheckDir(Path.Combine(targetDirectory, "DVD")); string newFileName = $"{this.GameID}.{this.Name}.iso"; string destPath = Path.Combine(Path.Combine(targetDirectory, "DVD"), newFileName); const int bufferSize = 1024 * 1024; byte[] buffer = new byte[bufferSize]; long totalBytes = new FileInfo(this.GamePath).Length; long bytesCopied = 0; using (FileStream source = new FileStream(this.GamePath, FileMode.Open, FileAccess.Read)) using (FileStream destination = new FileStream(destPath, FileMode.Create, FileAccess.Write)) { int bytesRead; while ((bytesRead = await source.ReadAsync(buffer.AsMemory(0, bufferSize))) > 0) { await destination.WriteAsync(buffer.AsMemory(0, bytesRead)); bytesCopied += bytesRead; this.InstallProgress.Value = (double)bytesCopied / totalBytes * 100; } } Console.Success("ISO File copied successfully for " + this); this.SaveCover(Artwork.Type.Front); this.SaveCover(Artwork.Type.Back); this.SaveCover(Artwork.Type.Disc); this.InstallationFinished?.Invoke(this, EventArgs.Empty); MainWindow.RefreshGamesListTrigger?.Invoke(this, EventArgs.Empty); } public Bitmap? LoadCover(Artwork.Type artworkType) { Console.Info("Loading " + artworkType + " Artwork for " + this.GameID); string targetDirectory = settings.library_path.GetValue(); Util.CheckDir(Path.Combine(targetDirectory, "ART")); try { switch (artworkType) { case Artwork.Type.Front: return new Bitmap(Path.Combine(Path.Combine(targetDirectory, "ART"), this.GameID + "_COV.png")); break; case Artwork.Type.Back: return new Bitmap(Path.Combine(Path.Combine(targetDirectory, "ART"), this.GameID + "_COV2.png")); break; case Artwork.Type.Disc: return new Bitmap(Path.Combine(Path.Combine(targetDirectory, "ART"), this.GameID + "_ICO.png")); break; default: return null; break; } } catch { return null; } } public void SaveCover(Artwork.Type artworkType) { string targetDirectory = settings.library_path.GetValue(); Util.CheckDir(Path.Combine(targetDirectory, "ART")); switch (artworkType) { case Artwork.Type.Front: this.ArtworkFront?.CreateScaledBitmap( new PixelSize(353, 500), BitmapInterpolationMode.HighQuality ).Save(Path.Combine(Path.Combine(targetDirectory, "ART"), this.GameID + "_COV.png")); break; case Artwork.Type.Back: this.ArtworkBack?.CreateScaledBitmap( new PixelSize(353, 500), BitmapInterpolationMode.HighQuality ).Save(Path.Combine(Path.Combine(targetDirectory, "ART"), this.GameID + "_COV2.png")); break; case Artwork.Type.Disc: this.ArtworkDVD?.CreateScaledBitmap( new PixelSize(353, 353), BitmapInterpolationMode.HighQuality ).Save(Path.Combine(Path.Combine(targetDirectory, "ART"), this.GameID + "_ICO.png")); break; } Console.Success("Saved " + artworkType + " Artwork for " + this.GameID); } public override string ToString() { return this.Name + " -> " + this.GameID; } } public class GameConfig { public string MemoryCard1 { get; set; } public string MemoryCard2 { get; set; } public bool Mode1 { get; set; } public bool Mode2 { get; set; } public bool Mode3 { get; set; } public bool Mode4 { get; set; } public bool Mode5 { get; set; } public bool Mode6 { get; set; } public override string ToString() { return $"VMCs: {MemoryCard1} - {MemoryCard2}\n" + $"Mode 1: {Mode1}\n" + $"Mode 2: {Mode2}\n" + $"Mode 3: {Mode3}\n" + $"Mode 4: {Mode4}\n" + $"Mode 5: {Mode5}\n" + $"Mode 6: {Mode6}\n"; } private void FromCompatibility(int value) { Mode1 = (value & (1 << 0)) != 0; Mode2 = (value & (1 << 1)) != 0; Mode3 = (value & (1 << 2)) != 0; Mode4 = (value & (1 << 3)) != 0; Mode5 = (value & (1 << 4)) != 0; Mode6 = (value & (1 << 5)) != 0; } private int ToCompatibility() { int value = 0; if (Mode1) value |= (1 << 0); if (Mode2) value |= (1 << 1); if (Mode3) value |= (1 << 2); if (Mode4) value |= (1 << 3); if (Mode5) value |= (1 << 4); if (Mode6) value |= (1 << 5); return value; } public void ParseConfig(string path) { if (!File.Exists(path)) throw new FileNotFoundException("Config file not found.", path); var lines = File.ReadAllLines(path); foreach (var rawLine in lines) { var line = rawLine.Trim(); // Skip empty or comment lines if (string.IsNullOrWhiteSpace(line) || line.StartsWith("#") || line.StartsWith("//")) continue; var parts = line.Split('=', 2); if (parts.Length != 2) continue; var key = parts[0].Trim(); var value = parts[1].Trim(); switch (key) { case "$Compatibility": if (int.TryParse(value, out int compat)) this.FromCompatibility(compat); break; case "$VMC_0": this.MemoryCard1 = value; break; case "$VMC_1": this.MemoryCard2 = value; break; case "$ConfigSource": // Optional: You can store/use this if needed break; } } } } public static class Artwork { public enum Type { Front, Back, Disc } public static Type NextType(Type _type) { return (Type)(((int)_type + 1) % Enum.GetValues(typeof(Type)).Length); } public static Type PrevType(Type _type) { return (Type)(((int)_type - 1 + Enum.GetValues(typeof(Type)).Length) % Enum.GetValues(typeof(Type)).Length); } }