initial commit

This commit is contained in:
WeeXnes 2025-04-19 22:30:22 +02:00
commit 75170c76c9
17 changed files with 974 additions and 0 deletions

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
bin/
obj/
/packages/
riderModule.iml
/_ReSharper.Caches/

16
PS2-Manager.sln Normal file
View file

@ -0,0 +1,16 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PS2_Manager", "PS2_Manager\PS2_Manager.csproj", "{05475C7F-17C8-4110-A381-3D21D6B3C85E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{05475C7F-17C8-4110-A381-3D21D6B3C85E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{05475C7F-17C8-4110-A381-3D21D6B3C85E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{05475C7F-17C8-4110-A381-3D21D6B3C85E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{05475C7F-17C8-4110-A381-3D21D6B3C85E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,64 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
Height="349"
Width="598"
CanResize="False"
x:Class="PS2_Manager.AddGameWindow"
Title="Install Game"
Loaded="Control_OnLoaded"
SizeChanged="Control_OnSizeChanged"
Background="#201c29"
SystemDecorations="None">
<Grid RowDefinitions="20, *">
<Grid Grid.Row="0">
<Border Background="#35313d"
PointerPressed="WindowDrag">
<Grid ColumnDefinitions="*, 20">
<TextBlock Name="WindowTitle" FontSize="12" Text="Install Game" Padding="8,0,0,0" VerticalAlignment="Center"/>
<Border Grid.Column="1" Background="#4b4753" PointerPressed="WindowClose">
<TextBlock FontSize="12" Text="×" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</Grid>
</Border>
</Grid>
<Grid Grid.Row="1" ColumnDefinitions="Auto,Auto,*" Margin="20">
<!-- PS2 Cover -->
<Border Width="205" Height="292" Background="Black" CornerRadius="5" HorizontalAlignment="Left">
<Image Name="CoverImage" Source="Images/missing.png" Stretch="UniformToFill"/>
</Border>
<!-- Spacer -->
<Rectangle Width="30" Grid.Column="1"/>
<!-- Form, centered vertically -->
<Grid Grid.Column="2">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="1" Width="300" Spacing="10" HorizontalAlignment="Left">
<TextBlock Text="Game Name"/>
<TextBox Name="GameNameBox" MaxLength="20" TextChanged="GameNameBox_OnTextChanged"/>
<TextBlock Text="Serial Number"/>
<TextBox Name="SerialBox" IsEnabled="False"/>
<TextBlock Text="ISO Path"/>
<TextBox Name="IsoPathBox" IsEnabled="False"/>
<Button Name="InstallButton" Content="Install Game" HorizontalAlignment="Center"
Background="#aa4de8"
Foreground="Black"
Click="InstallButton_OnClick"/>
<ProgressBar Name="InstallationProgressBar"/>
</StackPanel>
</Grid>
</Grid>
</Grid>
</Window>

View file

@ -0,0 +1,95 @@
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.Media.Imaging;
using PS2_Manager.Core;
namespace PS2_Manager;
public partial class AddGameWindow : Window
{
public string Gamepath { get; set; }
public Game newGame { get; set; }
public AddGameWindow(string filePath)
{
this.newGame = new Game(filePath);
InitializeComponent();
}
private void Control_OnLoaded(object? sender, RoutedEventArgs e)
{
IsoPathBox.Text = this.newGame.GamePath;
SerialBox.Text = this.newGame.GameID;
CoverImage.Source = this.newGame.Cover;
}
private void Control_OnSizeChanged(object? sender, SizeChangedEventArgs e)
{
var newSize = e.NewSize;
Console.WriteLine($"[SizeChanged] New size: {newSize.Width} x {newSize.Height}");
}
public void LoadPs2Cover(string gameId, Image imageControl)
{
//gameId = "SCPS-15110";
string url = $"https://github.com/xlenore/ps2-covers/blob/main/covers/default/{gameId}.jpg?raw=true";
try
{
using HttpClient client = new();
byte[] data = client.GetByteArrayAsync(url).GetAwaiter().GetResult();
using MemoryStream stream = new(data);
var bitmap = new Bitmap(stream);
imageControl.Source = bitmap;
}
catch (Exception ex)
{
Console.WriteLine($"[Error] Could not load cover for {gameId}: {ex.Message}");
}
}
private void WindowDrag(object? sender, PointerPressedEventArgs e)
{
if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
{
BeginMoveDrag(e);
}
}
private void WindowClose(object? sender, PointerPressedEventArgs e)
{
this.Close();
}
private void InstallButton_OnClick(object? sender, RoutedEventArgs e)
{
InstallButton.IsEnabled = false;
GameNameBox.IsEnabled = false;
InstallButton.Content = "Installing...";
newGame.Install();
newGame.InstallProgress.ValueChanged += () =>
{
InstallationProgressBar.Value = newGame.InstallProgress.Value;
};
newGame.InstallationFinished += (o, args) =>
{
Console.WriteLine("Installation finished");
this.Close();
};
}
private void GameNameBox_OnTextChanged(object? sender, TextChangedEventArgs e)
{
this.newGame.Name = GameNameBox.Text ?? "";
}
}

11
PS2_Manager/App.axaml Normal file
View file

@ -0,0 +1,11 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="PS2_Manager.App"
RequestedThemeVariant="Default"
>
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
<Application.Styles>
<FluentTheme />
</Application.Styles>
</Application>

25
PS2_Manager/App.axaml.cs Normal file
View file

@ -0,0 +1,25 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using PS2_Manager.Core;
namespace PS2_Manager;
public partial class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
Globals.LoadSettings();
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new MainWindow();
}
base.OnFrameworkInitializationCompleted();
}
}

View file

@ -0,0 +1,357 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Nocksoft.IO.ConfigFiles;
public static class ConfigUtils
{
private static INIFile config;
public static void InitConfig(string path = "config.ini")
{
config = new INIFile(path, true);
}
public static void SetValue<T>(this Enum _type, T _value)
{
if (!checkIfInitialized())
return;
config.SetValue(getStrType(_type), getStrName(_type), _value.ToString());
}
public static T GetValue<T>(this Enum _type)
{
if (!checkIfInitialized())
return default(T);
try
{
var value = config.GetValue(getStrType(_type), getStrName(_type));
return (T)Convert.ChangeType(value, typeof(T));
}
catch (Exception ex)
{
Console.WriteLine("ConfigManager Exception on " + getStrType(_type) + "." + getStrName(_type) + ": " + ex.Message);
return default(T);
}
}
private static string getStrType(Enum _enum)
{
return _enum.GetType().Name;
}
private static string getStrName(Enum _enum)
{
return Enum.GetName(_enum.GetType(), _enum);
}
private static bool checkIfInitialized()
{
if (config == null)
{
Console.WriteLine("Config not initialized");
return false;
}
else
{
return true;
}
}
}
/**
* Copyright by Nocksoft
* https://www.nocksoft.de
* https://nocksoft.de/tutorials/visual-c-sharp-arbeiten-mit-ini-dateien/
* https://github.com/Nocksoft/INIFile.cs
* -----------------------------------
* Author: Rafael Nockmann @ Nocksoft
* Updated: 2022-01-09
* Version: 1.0.3
*
* Language: Visual C#
*
* License: MIT license
* License URL: https://github.com/Nocksoft/INIFile.cs/blob/master/LICENSE
*
* Description:
* Provides basic functions for working with INI files.
*
* ##############################################################################################
*/
namespace Nocksoft.IO.ConfigFiles
{
public class INIFile
{
private string _File;
/// <summary>
/// Call the constructor creates a new object of the INIFile class to work with INI files.
/// </summary>
/// <param name="file">Name of INI file, which you want to access.</param>
/// <param name="createFile">Specifies whether the INI file should be created if it does not exist.</param>
public INIFile(string file, bool createFile = false)
{
if (createFile == true && File.Exists(file) == false)
{
FileInfo fileInfo = new FileInfo(file);
FileStream fileStream = fileInfo.Create();
fileStream.Close();
}
_File = file;
}
#region Public Methods
/// <summary>
/// Removes all comments and empty lines from a complete section and returns the sections.
/// This method is not case-sensitive.
/// The return value does not contain any spaces at the beginning or at the end of a line.
/// </summary>
/// <param name="section">Name of the requested section.</param>
/// <param name="includeComments">Specifies whether comments should also be returned.</param>
/// <returns>Returns the whole section.</returns>
public List<string> GetSection(string section, bool includeComments = false)
{
section = CheckSection(section);
List<string> completeSection = new List<string>();
bool sectionStart = false;
string[] fileArray = File.ReadAllLines(_File);
foreach (var item in fileArray)
{
if (item.Length <= 0) continue;
// Beginning of section.
if (item.Replace(" ", "").ToLower() == section)
{
sectionStart = true;
}
// Beginning of next section.
if (sectionStart == true && item.Replace(" ", "").ToLower() != section && item.Replace(" ", "").Substring(0, 1) == "[" && item.Replace(" ", "").Substring(item.Length - 1, 1) == "]")
{
break;
}
if (sectionStart == true)
{
// Add the entry to the List<string> completeSection, if it is not a comment or an empty entry.
if (includeComments == false
&& item.Replace(" ", "").Substring(0, 1) != ";" && !string.IsNullOrWhiteSpace(item))
{
completeSection.Add(ReplaceSpacesAtStartAndEnd(item));
}
if (includeComments == true && !string.IsNullOrWhiteSpace(item))
{
completeSection.Add(ReplaceSpacesAtStartAndEnd(item));
}
}
}
return completeSection;
}
/// <summary>
/// The method returns a value for the associated key.
/// This method is not case-sensitive.
/// </summary>
/// <param name="section">Name of the requested section.</param>
/// <param name="key">Name of the requested key.</param>
/// <param name="convertValueToLower">If "true" is passed, the value will be returned in lowercase letters.</param>
/// <returns>Returns the value for the specified key in the specified section, if available, otherwise null.</returns>
public string GetValue(string section, string key, bool convertValueToLower = false)
{
section = CheckSection(section);
key = key.ToLower();
List<string> completeSection = GetSection(section);
foreach (var item in completeSection)
{
// Continue if entry is no key.
if (!item.Contains("=") && item.Contains("[") && item.Contains("]")) continue;
string[] keyAndValue = item.Split(new string[] { "=" }, StringSplitOptions.RemoveEmptyEntries);
if (keyAndValue[0].ToLower() == key && keyAndValue.Count() > 1)
{
if (convertValueToLower == true)
{
keyAndValue[1] = keyAndValue[1].ToLower();
}
return keyAndValue[1];
}
}
return null;
}
/// <summary>
/// Set or add a value of the associated key in the specified section.
/// This method is not case-sensitive.
/// </summary>
/// <param name="section">Name of the section.</param>
/// <param name="key">Name of the key.</param>
/// <param name="value">Value to save.</param>
/// <param name="convertValueToLower">If "true" is passed, the value will be saved in lowercase letters.</param>
public void SetValue(string section, string key, string value, bool convertValueToLower = false)
{
section = CheckSection(section, false);
string sectionToLower = section.ToLower();
bool sectionFound = false;
List<string> iniFileContent = new List<string>();
string[] fileLines = File.ReadAllLines(_File);
// Creates a new INI file if none exists.
if (fileLines.Length <= 0)
{
iniFileContent = AddSection(iniFileContent, section, key, value, convertValueToLower);
WriteFile(iniFileContent);
return;
}
for (int i = 0; i < fileLines.Length; i++)
{
// Possibility 1: The desired section has not (yet) been found.
if (fileLines[i].Replace(" ", "").ToLower() != sectionToLower)
{
iniFileContent.Add(fileLines[i]);
// If a section does not exist, the section will be created.
if (i == fileLines.Length - 1 && fileLines[i].Replace(" ", "").ToLower() != sectionToLower && sectionFound == false)
{
iniFileContent.Add(null);
iniFileContent = AddSection(iniFileContent, section, key, value, convertValueToLower);
break;
}
continue;
}
// Possibility 2 -> Desired section was found.
sectionFound = true;
// Get the complete section in which the target key may be.
List<string> targetSection = GetSection(sectionToLower, true);
for (int x = 0; x < targetSection.Count; x++)
{
string[] targetKey = targetSection[x].Split(new string[] { "=" }, StringSplitOptions.None);
// When the target key is found.
if (targetKey[0].ToLower() == key.ToLower())
{
if (convertValueToLower == true)
{
iniFileContent.Add(key + "=" + value.ToLower());
}
else
{
iniFileContent.Add(key + "=" + value);
}
i = i + x;
break;
}
else
{
iniFileContent.Add(targetSection[x]);
// If the target key is not found, it will be created.
if (x == targetSection.Count - 1 && targetKey[0].ToLower() != key.ToLower())
{
if (convertValueToLower == true)
{
iniFileContent.Add(key + "=" + value.ToLower());
}
else
{
iniFileContent.Add(key + "=" + value);
}
i = i + x;
break;
}
}
}
}
WriteFile(iniFileContent);
}
#endregion
#region Private Methods
/// <summary>
/// Ensures that a section is always in the following format: [section].
/// </summary>
/// <param name="section">Section to be checked for correct format.</param>
/// <param name="convertToLower">Specifies whether the section should be vonverted in lower case letters.</param>
/// <returns>Returns section in this form: [section].</returns>
private string CheckSection(string section, bool convertToLower = true)
{
if (convertToLower == true)
{
section = section.ToLower();
}
if (!section.StartsWith("[") && !section.EndsWith("]"))
{
section = "[" + section + "]";
}
return section;
}
/// <summary>
/// Removes leading and trailing spaces from sections, keys and values.
/// </summary>
/// <param name="item">String to be trimmed.</param>
/// <returns>Returns the trimmed string.</returns>
private string ReplaceSpacesAtStartAndEnd(string item)
{
// If the string has a key and a value.
if (item.Contains("=") && !item.Contains("[") && !item.Contains("]"))
{
string[] keyAndValue = item.Split(new string[] { "=" }, StringSplitOptions.None);
return keyAndValue[0].Trim() + "=" + keyAndValue[1].Trim();
}
return item.Trim();
}
/// <summary>
/// Adds a new section with key value pair.
/// </summary>
/// <param name="iniFileContent">List iniFileContent from SetValue.</param>
/// <param name="section">Section to be created.</param>
/// <param name="key">Key to be added.</param>
/// <param name="value">Value to be added.</param>
/// <param name="convertValueToLower">Specifies whether the key and value should be saved in lower case letters.</param>
/// <returns>Returns the new created section with key value pair.</returns>
private List<string> AddSection(List<string> iniFileContent, string section, string key, string value, bool convertValueToLower)
{
if (convertValueToLower == true)
{
value = value.ToLower();
}
iniFileContent.Add(section);
iniFileContent.Add($"{key}={value}");
return iniFileContent;
}
private void WriteFile(List<string> content)
{
StreamWriter writer = new StreamWriter(_File);
foreach (var item in content)
{
writer.WriteLine(item);
}
writer.Close();
}
#endregion
}
}

103
PS2_Manager/Core/Game.cs Normal file
View file

@ -0,0 +1,103 @@
using System;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Media.Imaging;
using Avalonia.Threading;
namespace PS2_Manager.Core;
public class Game
{
public string Name { get; set; }
public string GameID { get; set; }
public string GamePath { get; set; }
public Bitmap Cover { get; set; }
public EventHandler? InstallationFinished { get; set; }
public UpdateVar<double> InstallProgress { get; private set; }
public Game(string isoPath)
{
this.GamePath = isoPath;
this.GameID = ISO.GetSerial(isoPath);
this.Cover = this.GetCover();
}
public async void Install()
{
this.InstallProgress = new UpdateVar<double>();
await this.CopyIsoWithProgressAsync();
}
private Bitmap GetCover()
{
Bitmap cover = null;
string url = $"https://github.com/xlenore/ps2-covers/blob/main/covers/default/{this.GameID.Replace(".", "").Replace("_", "-")}.jpg?raw=true";
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.WriteLine($"[Error] Could not load cover for {this.GameID}: {ex.Message}");
}
return cover;
}
public async Task CopyIsoWithProgressAsync()
{
string targetDirectory = settings.library_path.GetValue<string>();
if(!Directory.Exists(Path.Combine(targetDirectory, "DVD")))
Directory.CreateDirectory(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;
}
}
this.InstallCover();
this.InstallationFinished?.Invoke(this, EventArgs.Empty);
Console.WriteLine($"Copied ISO to: {destPath}");
}
public void InstallCover()
{
string targetDirectory = settings.library_path.GetValue<string>();
if(!Directory.Exists(Path.Combine(targetDirectory, "ART")))
Directory.CreateDirectory(Path.Combine(targetDirectory, "ART"));
this.Cover.Save(Path.Combine(Path.Combine(targetDirectory, "ART"), this.GameID + "_COV.png"));
}
}

View file

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Platform.Storage;
namespace PS2_Manager.Core;
public static class Globals
{
public static List<Game> Games { get; set; }
public static void LoadSettings()
{
Games = new List<Game>();
ConfigUtils.InitConfig("settings.ini");
}
public static class FileDialogTypes
{
public static FilePickerFileType PS2Game { get; } = new("PS2 Game")
{
Patterns = new[] { "*.iso" }
};
}
}
public enum settings
{
library_path
}
public class UpdateVar<T>
{
private T _value;
public Action ValueChanged;
public T Value
{
get => _value;
set
{
_value = value;
OnValueChanged();
}
}
protected virtual void OnValueChanged() => ValueChanged?.Invoke();
}

44
PS2_Manager/Core/ISO.cs Normal file
View file

@ -0,0 +1,44 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using DiscUtils.Iso9660;
public static class ISO
{
public static string GetSerial(string isoPath)
{
using (FileStream isoStream = File.OpenRead(isoPath))
{
CDReader cd = new CDReader(isoStream, true);
if (cd.FileExists("SYSTEM.CNF"))
{
using (Stream fileStream = cd.OpenFile("SYSTEM.CNF", FileMode.Open))
using (StreamReader reader = new StreamReader(fileStream))
{
string line;
while ((line = reader.ReadLine()) != null)
{
line = line.Trim();
if (line.StartsWith("BOOT2") || line.StartsWith("BOOT"))
{
int pathStart = line.IndexOf(@"\");
int pathEnd = line.IndexOf(";");
if (pathStart != -1 && pathEnd != -1 && pathEnd > pathStart)
{
string bootPath = line.Substring(pathStart + 1, pathEnd - pathStart - 1);
return bootPath;
}
}
}
}
}
}
return null;
}
}

25
PS2_Manager/Core/Util.cs Normal file
View file

@ -0,0 +1,25 @@
using System.Collections.Generic;
using System.Linq;
using Avalonia.Controls;
namespace PS2_Manager.Core;
public static class Util
{
public static string? OpenFileDialogSync(Window parent)
{
var dialog = new OpenFileDialog
{
Title = "Select PS2 ISO",
AllowMultiple = false,
Filters = new List<FileDialogFilter>
{
new FileDialogFilter { Name = "PS2 ISO", Extensions = { "iso", "bin" } },
new FileDialogFilter { Name = "All Files", Extensions = { "*" } }
}
};
var result = dialog.ShowAsync(parent).GetAwaiter().GetResult();
return result?.FirstOrDefault();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -0,0 +1,39 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="PS2_Manager.MainWindow"
Title="PS2_Manager"
Background="#201c29"
SystemDecorations="None"
Width="880"
Height="520"
Loaded="Control_OnLoaded">
<Grid RowDefinitions="20, *">
<Grid Grid.Row="0">
<Border Background="#35313d"
PointerPressed="WindowDrag">
<Grid ColumnDefinitions="*, 20">
<TextBlock Name="WindowTitle" FontSize="12" Padding="8,0,0,0" VerticalAlignment="Center"/>
<Border Grid.Column="1" Background="#625f69" PointerPressed="WindowClose">
<TextBlock FontSize="12" Text="×" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</Grid>
</Border>
</Grid>
<Grid Grid.Row="1">
<Border Background="Red" CornerRadius="0" Margin="8">
<Grid ColumnDefinitions="300, *">
<Border Background="Aqua">
<ListBox Name="GamesList"></ListBox>
</Border>
<Border Grid.Column="1" Background="Aqua">
<Button Click="OpenFileButton_Clicked" Content="Install Game"></Button>
</Border>
</Grid>
</Border>
</Grid>
</Grid>
</Window>

View file

@ -0,0 +1,69 @@
using System;
using System.IO;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Platform.Storage;
using PS2_Manager.Core;
namespace PS2_Manager;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void WindowDrag(object? sender, PointerPressedEventArgs e)
{
if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
{
BeginMoveDrag(e);
}
}
private void WindowClose(object? sender, PointerPressedEventArgs e)
{
this.Close();
}
private async void OpenFileButton_Clicked(object sender, RoutedEventArgs args)
{
var topLevel = TopLevel.GetTopLevel(this);
var files = await topLevel.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
{
Title = "Open Text File",
AllowMultiple = false,
FileTypeFilter = new []
{
Globals.FileDialogTypes.PS2Game
}
});
if (files.Count >= 1)
{
//Console.WriteLine(ISO.GetSerial());
new AddGameWindow(files[0].Path.LocalPath).Show();
}
}
private async void Control_OnLoaded(object? sender, RoutedEventArgs e)
{
WindowTitle.Text = "PS2 Games Manager";
if (String.IsNullOrEmpty(settings.library_path.GetValue<string>()))
{
var topLevel = TopLevel.GetTopLevel(this);
var path = await topLevel.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
{
Title = "Open Game Library",
AllowMultiple = false
});
Console.WriteLine(path[0].Path.LocalPath);
if(!String.IsNullOrEmpty(path[0].Path.LocalPath))
settings.library_path.SetValue(path[0].Path.LocalPath);
}
}
}

View file

@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.2.7" />
<PackageReference Include="Avalonia.Desktop" Version="11.2.7" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.2.7" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.2.7" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Include="Avalonia.Diagnostics" Version="11.2.7">
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
</PackageReference>
<PackageReference Include="DiscUtils.Iso9660" Version="0.16.13" />
</ItemGroup>
<ItemGroup>
<None Remove="Images\missing.png" />
<AvaloniaResource Include="Images\missing.png" />
</ItemGroup>
</Project>

21
PS2_Manager/Program.cs Normal file
View file

@ -0,0 +1,21 @@
using Avalonia;
using System;
namespace PS2_Manager;
class Program
{
// Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
[STAThread]
public static void Main(string[] args) => BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.UsePlatformDetect()
.WithInterFont()
.LogToTrace();
}

18
PS2_Manager/app.manifest Normal file
View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<!-- This manifest is used on Windows only.
Don't remove it as it might cause problems with window transparency and embedded controls.
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
<assemblyIdentity version="1.0.0.0" name="PS2_Manager.Desktop"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
</assembly>