Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 23 additions & 13 deletions AssM.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,30 @@
<Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest>
<Version>1.6.1</Version>
<Version>1.7.0</Version>
<Authors>SubZeroPL</Authors>
<PackageProjectUrl>https://github.com/SubZeroPL/AssM/</PackageProjectUrl>
<AssemblyVersion>1.6.1</AssemblyVersion>
<FileVersion>1.6.1</FileVersion>
<PackageProjectUrl>https://github.com/SubZeroPL/AssM</PackageProjectUrl>
<AssemblyVersion>1.7.0</AssemblyVersion>
<FileVersion>1.7.0</FileVersion>
<Title>AssM</Title>
<PackageLicenseUrl>https://github.com/SubZeroPL/AssM/blob/master/LICENSE</PackageLicenseUrl>
<RepositoryUrl>https://github.com/SubZeroPL/AssM/</RepositoryUrl>
<ApplicationIcon>Assets\assm.ico</ApplicationIcon>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Avalonia" Version="11.3.0" />
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.3.0" />
<PackageReference Include="Avalonia.Desktop" Version="11.3.0" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.0" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.0" />
<PackageReference Include="Avalonia" Version="11.3.9" />
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.3.9" />
<PackageReference Include="Avalonia.Desktop" Version="11.3.9" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.9" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.9" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.3.0" />
<PackageReference Include="BouncyCastle.Cryptography" Version="2.5.1" />
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.3.9" />
<PackageReference Include="BouncyCastle.Cryptography" Version="2.6.2" />
<PackageReference Include="Markdown.Avalonia" Version="11.0.3-a1"/>
<PackageReference Include="MessageBox.Avalonia" Version="3.2.0" />
<PackageReference Include="NLog" Version="5.4.0" />
<PackageReference Include="MessageBox.Avalonia" Version="3.3.0" />
<PackageReference Include="NLog" Version="6.0.6" />
<PackageReference Include="Octokit" Version="14.0.0" />
</ItemGroup>

<ItemGroup>
Expand All @@ -40,6 +44,7 @@

<ItemGroup>
<AvaloniaResource Include="Assets\**"/>
<AvaloniaResource Remove="Updater\**" />
</ItemGroup>

<ItemGroup>
Expand All @@ -53,25 +58,30 @@
</Compile>
<Compile Remove="Avalonia.Labs\**"/>
<Compile Remove="DiscTools\**"/>
<Compile Remove="Updater\**" />
</ItemGroup>

<ItemGroup>
<AvaloniaXaml Remove="Avalonia.Labs\**"/>
<AvaloniaXaml Remove="DiscTools\**"/>
<AvaloniaXaml Remove="Updater\**" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Remove="Avalonia.Labs\**"/>
<EmbeddedResource Remove="DiscTools\**"/>
<EmbeddedResource Remove="Updater\**" />
</ItemGroup>

<ItemGroup>
<AdditionalFiles Remove="Avalonia.Labs\**"/>
<AdditionalFiles Remove="DiscTools\**"/>
<AdditionalFiles Remove="Updater\**" />
</ItemGroup>

<ItemGroup>
<None Remove="Avalonia.Labs\**"/>
<None Remove="DiscTools\**"/>
<None Remove="Updater\**" />
</ItemGroup>
</Project>
Binary file added Assets/assm.ico
Binary file not shown.
10 changes: 9 additions & 1 deletion Classes/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ private static bool IsWindows() =>
[GeneratedRegex(BinFile)]
public static partial Regex BinFileRegex();

private const string BinHash = @"BIN \(TRACK (\d{2})\) MD5\: (.+)";
private const string BinHash = @"TRACK (\d{2}) MD5\: (.+)";
[GeneratedRegex(BinHash)]
public static partial Regex BinHashRegex();
public const string ReadmeGameTitle = "#gameTitle#";
Expand All @@ -53,4 +53,12 @@ private static bool IsWindows() =>
"Generating README",
"Process JSON"
];

public static readonly string[] AddFolderSteps =
[
"",
"Aggregating image files (folder {0} of {1})",
"Adding to the list",
"Loading existing data"
];
}
30 changes: 17 additions & 13 deletions Classes/Functions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public static class Functions

public static string GetChdName(Game game, Configuration configuration) => configuration.GameIdAsChdName
? $"{game.Id.ToUpper()}.chd"
: Path.GetFileName(Path.ChangeExtension(game.CuePath, "chd"));
: Path.GetFileName(Path.ChangeExtension(game.ImagePath, "chd"));

public static void LoadChdManInfo(string chdPath, Game game)
{
Expand Down Expand Up @@ -174,7 +174,7 @@ private static void LoadTrackInfoFromReadme(string readmePath, Game game)
}

var endIndex = Array.FindIndex(lines, line => line.Contains("**Description:**")) - 1;
var hashes = lines.Skip(startIndex).Take(endIndex - startIndex).Where(item => item.StartsWith("BIN"));
var hashes = lines.Skip(startIndex).Take(endIndex - startIndex).Where(item => item.StartsWith("TRACK"));
foreach (var hashLine in hashes)
{
var matches = Constants.BinHashRegex().Matches(hashLine);
Expand All @@ -199,11 +199,12 @@ private static void LoadTrackInfoFromReadme(string readmePath, Game game)
}
}

public static List<string> GetCueFilesInDirectory(string directory)
public static List<string> GetCueIsoFilesInDirectory(string directory)
{
Logger.Debug($"Getting cue files from {directory}");
var result = Directory.GetFiles(directory).Where(d => Path.GetExtension(d) == ".cue").ToList();
Directory.GetDirectories(directory).ToList().ForEach(d => result.AddRange(GetCueFilesInDirectory(d)));
Logger.Debug($"Getting cue/iso files from {directory}");
var result = Directory.GetFiles(directory)
.Where(d => Path.GetExtension(d) == ".cue" || Path.GetExtension(d) == ".iso").ToList();
Directory.GetDirectories(directory).ToList().ForEach(d => result.AddRange(GetCueIsoFilesInDirectory(d)));
return result;
}

Expand All @@ -216,29 +217,30 @@ public static List<string> GetReadmeFilesInDirectory(string directory)
return result;
}

public static Game? AddGameToList(string cuePath, Configuration configuration, ObservableCollection<Game> gameList)
public static Game? AddGameToList(string imagePath, Configuration configuration, ObservableCollection<Game> gameList)
{
Logger.Debug($"Adding game to list from {cuePath}");
var di = DiscInspector.ScanDisc(cuePath);
Logger.Debug($"Adding game to list from {imagePath}");
var di = DiscInspector.ScanDisc(imagePath);
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - apparently there are games that have no Id in image (like SLPS-00018)
if (di.Data.SerialNumber == null)
{
Logger.Error($"Failed to add game to list from {cuePath}{Environment.NewLine}Id not present in image");
Logger.Error($"Failed to add game to list from {imagePath}{Environment.NewLine}Id not present in image");
return null;
}
var title = configuration.GetTitleFromCue ? Path.GetFileNameWithoutExtension(cuePath) : di.Data.GameTitle;

var title = configuration.GetTitleFromCue ? Path.GetFileNameWithoutExtension(imagePath) : di.Data.GameTitle;
var game = new Game
{
Title = title, Id = di.Data.SerialNumber, Platform = di.DetectedDiscType,
CuePath = cuePath
ImagePath = imagePath
};
var existingGame = gameList.FirstOrDefault(g => g.Id == game.Id);
if (existingGame != null)
{
Logger.Debug($"Game already exists: {existingGame.Title}, updating");
existingGame.Title = game.Title;
existingGame.Platform = game.Platform;
existingGame.CuePath = game.CuePath;
existingGame.ImagePath = game.ImagePath;
existingGame.Id = game.Id;
}
else
Expand Down Expand Up @@ -273,4 +275,6 @@ public static void ProcessJson(Configuration configuration, Game game, Action<do
Logger.Debug($"Json processing cancelled: {e}");
}
}

public static bool IsIso(Game game) => game.ImagePath.EndsWith(".iso", StringComparison.OrdinalIgnoreCase);
}
4 changes: 2 additions & 2 deletions Data/Game.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class Game
{
public string Title { get; set; } = string.Empty;
public string Id { get; set; } = string.Empty;
public string CuePath { get; set; } = string.Empty;
public string ImagePath { get; set; } = string.Empty;
public DetectedDiscType Platform { get; set; } = DetectedDiscType.UnknownFormat;
public bool ReadmeCreated { get; set; }
public bool ChdCreated { get; set; }
Expand All @@ -29,6 +29,6 @@ public class ChdData
public string GetTrackInfo()
{
return string.Join(Environment.NewLine,
TrackInfo.Select(ti => $"BIN (TRACK {ti.TrackNo,2:D2}) MD5: {ti.TrackMD5}{Environment.NewLine}")).Trim();
TrackInfo.Select(ti => $"TRACK {ti.TrackNo,2:D2} MD5: {ti.TrackMD5}{Environment.NewLine}")).Trim();
}
}
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
It is a piece of software that assists in creating entries for [Arkadyzja savestates repository](https://github.com/ActionPL/duckstation_openbios_savestates). It allows creating and editing README files for games that will have savestates uploaded to the repository. Those savestates will then be available for [Arkadyzja](https://arkadyzja.honmaru.pl/).

## ⚠️ Requirements
For the application to work [.NET framework 8](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) is required.
For the application to work, [.NET Framework 8](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) is required.

Application is developed and tested on Windows 11 OS. In theory should also work on Linux/Mac hovewer it was not tested on those platforms (only a bit on Linux and not at all on Mac) and currently only Win and Linux builds are released (see below).
Application is developed and tested on Windows 11 OS. It should also work on Linux/Mac hovewer it was not tested (extensively) on those platforms (only a bit on Linux and not at all on Mac) and currently only Win and Linux builds are released (see below).

## 📦 Releases
Each release starting with 1.4.0 has four variants:
Expand All @@ -23,12 +23,12 @@ Clicking it will open default browser on the latest release page.

## 🪧 Usage
[![Last Release](https://img.shields.io/github/v/release/SubZeroPL/AssM?logo=github&label=latest)](https://github.com/SubZeroPL/AssM/releases)
- get latest version of the application from [Releases](https://github.com/SubZeroPL/AssM/releases) page
- get the latest version of the application from the [Releases](https://github.com/SubZeroPL/AssM/releases) page
- unpack to some empty directory
- run the `AssM.exe` file (or `AssM` for Linux)

![Application main window](.github/assets/app.png "Application main window")
First you should select Output directory, where the created README files will be stored. The files are created in a specific directory structure:
First, you should select the Output directory, where the created README files will be stored. The files are created in a specific directory structure:
`OutputPath\Platform\GameID`.\
The output directory is saved between sessions, so when you start the application next time it will be automatically read and existing entries inserted into the table.\
**You should not modify existing directory structure if you plan to use the output directory again later!**
Expand All @@ -40,8 +40,8 @@ When you click `Start processing` the application will go through the list and p

## ⚙️ Configuration
![Configuration options](.github/assets/config.png "Configuration options")
- Get game title from CUE file name - by default game name is extracted from cue/bin image, this option uses CUE file name instead
- Use Game ID as name for CHD file - by default game title (as set by previous option) is used for CHD file name, this option uses Game ID instead
- Get game title from CUE file name - by default, game name is extracted from cue/bin image; this option uses CUE file name instead
- Use Game ID as the name for CHD file by default, game title (as set by a previous option) is used for CHD file name; this option uses Game ID instead

Other configuration options should be self-explanatory.

Expand Down
8 changes: 4 additions & 4 deletions Windows/AddFolderProgressWindow.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:gif="clr-namespace:Avalonia.Labs.Gif;assembly=Avalonia.Labs.Gif"
mc:Ignorable="d" d:DesignWidth="300"
mc:Ignorable="d" d:DesignWidth="400"
x:Class="AssM.Windows.AddFolderProgressWindow"
Closing="Window_OnClosing"
Width="300" WindowStartupLocation="CenterOwner" SizeToContent="Height" ShowInTaskbar="False" CanResize="False"
Width="400" WindowStartupLocation="CenterOwner" SizeToContent="Height" ShowInTaskbar="False" CanResize="False"
Title="Adding folder contents">
<StackPanel>
<StackPanel Orientation="Horizontal">
<Label>Processing folder:</Label>
<Label Name="LabelFolderName"></Label>
<Label Name="LabelStep">Processing folder (0 of 0):</Label>
</StackPanel>
<Label Name="LabelFolderName">folder name</Label>
<Panel HorizontalAlignment="Center">
<gif:GifImage Source="avares://AssM/Assets/loading.gif" />
</Panel>
Expand Down
45 changes: 37 additions & 8 deletions Windows/AddFolderProgressWindow.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@

namespace AssM.Windows;

internal class AddFolderProgressUpdateObject
{
public string? Dir { get; init; }
public int? Count { get; init; }
public int? Index { get; init; }
public int Step { get; init; }
}

public partial class AddFolderProgressWindow : Window
{
private readonly BackgroundWorker _worker;
Expand All @@ -24,43 +32,64 @@ public AddFolderProgressWindow()

private void WorkerOnProgressChanged(object? sender, ProgressChangedEventArgs e)
{
if (e.UserState is string dir)
LabelFolderName.Content = dir;
if (e.UserState is not AddFolderProgressUpdateObject updateObject) return;
LabelStep.Content = Constants.AddFolderSteps[updateObject.Step];
if (updateObject.Step == 1)
{
LabelStep.Content = string.Format(Constants.AddFolderSteps[updateObject.Step], updateObject.Index,
updateObject.Count);
LabelFolderName.Content = updateObject.Dir;
}
}

public void Process(List<string> dirs, ObservableCollection<Game> gameList, Configuration configuration, Action<List<string>> finishedCallback)
public void Process(List<string> dirs, ObservableCollection<Game> gameList, Configuration configuration,
Action<List<string>> finishedCallback)
{
var cueFiles = new List<string>();
var errors = new List<string>();
_worker.DoWork += (_, _) =>
{
var index = 0;
foreach (var dir in dirs)
{
if (_worker.CancellationPending) return;
_worker.ReportProgress(0, dir);
cueFiles.AddRange(Functions.GetCueFilesInDirectory(dir));
var progress = index / dirs.Count * 100;
_worker.ReportProgress(progress,
new AddFolderProgressUpdateObject { Dir = dir, Index = index++, Count = dirs.Count, Step = 1 });
cueFiles.AddRange(Functions.GetCueIsoFilesInDirectory(dir));
}

index = 0;
foreach (var cueFile in cueFiles)
{
if (_worker.CancellationPending) return;
var progress = index / cueFiles.Count * 100;
_worker.ReportProgress(progress,
new AddFolderProgressUpdateObject
{ Dir = cueFile, Index = index++, Count = cueFiles.Count, Step = 2 });
var game = Functions.AddGameToList(cueFile, configuration, gameList);
if (game == null)
{
errors.Add($"Failed to add game to list from {cueFile}{Environment.NewLine}Id not present in image");
errors.Add(
$"Failed to add game to list from {cueFile}{Environment.NewLine}Id not present in image");
}
}

index = 0;
if (string.IsNullOrWhiteSpace(configuration.OutputDirectory)) return;
foreach (var game in gameList)
{
if (_worker.CancellationPending) return;
var progress = index / gameList.Count * 100;
_worker.ReportProgress(progress,
new AddFolderProgressUpdateObject
{ Dir = game.Title, Index = index++, Count = gameList.Count, Step = 3 });
Functions.LoadExistingData(game, configuration);
}
};

_worker.RunWorkerCompleted += (_, _) => finishedCallback.Invoke(errors);

_worker.RunWorkerAsync();
}

Expand Down
2 changes: 1 addition & 1 deletion Windows/MainWindow.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
</MenuItem>
<CheckBox Name="CheckBoxProcessModified" IsThreeState="False" Click="CheckBox_OnClick">Process only modified enties</CheckBox>
<CheckBox Name="CheckBoxOverwriteReadme" IsThreeState="False" Click="CheckBox_OnClick">Overwrite existing READMEs</CheckBox>
<CheckBox Name="CheckBoxGetTitleFromCue" IsThreeState="False" Click="CheckBox_OnClick">Get game title from CUE file name</CheckBox>
<CheckBox Name="CheckBoxGetTitleFromCue" IsThreeState="False" Click="CheckBox_OnClick">Get game title from file name</CheckBox>
<CheckBox Name="CheckBoxGameIdAsChdName" IsThreeState="False" Click="CheckBox_OnClick">Use Game ID as name for CHD file</CheckBox>
</MenuFlyout>
</Button.Flyout>
Expand Down
Loading
Loading