initial commit
This commit is contained in:
commit
75170c76c9
17 changed files with 974 additions and 0 deletions
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
bin/
|
||||
obj/
|
||||
/packages/
|
||||
riderModule.iml
|
||||
/_ReSharper.Caches/
|
16
PS2-Manager.sln
Normal file
16
PS2-Manager.sln
Normal 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
|
64
PS2_Manager/AddGameWindow.axaml
Normal file
64
PS2_Manager/AddGameWindow.axaml
Normal 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>
|
95
PS2_Manager/AddGameWindow.axaml.cs
Normal file
95
PS2_Manager/AddGameWindow.axaml.cs
Normal 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
11
PS2_Manager/App.axaml
Normal 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
25
PS2_Manager/App.axaml.cs
Normal 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();
|
||||
}
|
||||
}
|
357
PS2_Manager/Core/ConfigUtils.cs
Normal file
357
PS2_Manager/Core/ConfigUtils.cs
Normal 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
103
PS2_Manager/Core/Game.cs
Normal 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"));
|
||||
}
|
||||
}
|
54
PS2_Manager/Core/Globals.cs
Normal file
54
PS2_Manager/Core/Globals.cs
Normal 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
44
PS2_Manager/Core/ISO.cs
Normal 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
25
PS2_Manager/Core/Util.cs
Normal 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();
|
||||
}
|
||||
}
|
BIN
PS2_Manager/Images/missing.png
Normal file
BIN
PS2_Manager/Images/missing.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
39
PS2_Manager/MainWindow.axaml
Normal file
39
PS2_Manager/MainWindow.axaml
Normal 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>
|
69
PS2_Manager/MainWindow.axaml.cs
Normal file
69
PS2_Manager/MainWindow.axaml.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
28
PS2_Manager/PS2_Manager.csproj
Normal file
28
PS2_Manager/PS2_Manager.csproj
Normal 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
21
PS2_Manager/Program.cs
Normal 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
18
PS2_Manager/app.manifest
Normal 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>
|
Loading…
Add table
Reference in a new issue