mirror of
https://github.com/SabreTools/MPF.git
synced 2026-02-04 21:30:11 +00:00
Compare commits
71 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
24150c7b3c | ||
|
|
040720a1c2 | ||
|
|
70d30d370f | ||
|
|
a50b99da07 | ||
|
|
2cecbe69ad | ||
|
|
557410660f | ||
|
|
0d75dbaaa2 | ||
|
|
d38f95c73a | ||
|
|
c9d59a90e4 | ||
|
|
fc6a34d987 | ||
|
|
8acabb692f | ||
|
|
61f8871839 | ||
|
|
69d61ad185 | ||
|
|
27391ed31b | ||
|
|
ed56c92101 | ||
|
|
ab8ae1524f | ||
|
|
de7c247583 | ||
|
|
997fd8f7ac | ||
|
|
e39e07246a | ||
|
|
e0b0406d76 | ||
|
|
1efbe9a784 | ||
|
|
fab921c2dd | ||
|
|
56896b4bea | ||
|
|
54eb7d8ecd | ||
|
|
114587b9f6 | ||
|
|
cad33c6c07 | ||
|
|
8154999f1c | ||
|
|
3a30c14085 | ||
|
|
db8b1df480 | ||
|
|
e20cac8a80 | ||
|
|
ce9b4e39f5 | ||
|
|
ddff4b2e58 | ||
|
|
16a470475c | ||
|
|
6e01473e87 | ||
|
|
8682089151 | ||
|
|
f45cb0075f | ||
|
|
afccb48798 | ||
|
|
ce6995fba0 | ||
|
|
3328d1adea | ||
|
|
33d79df9a8 | ||
|
|
4fb64b19d6 | ||
|
|
bf0f495c8b | ||
|
|
5d5f8e8d8c | ||
|
|
91aa248355 | ||
|
|
633c6c1efb | ||
|
|
facb1f673f | ||
|
|
9c8938c7f2 | ||
|
|
17f75f3ce6 | ||
|
|
b99c48afa2 | ||
|
|
9481fada23 | ||
|
|
7efbc5043c | ||
|
|
92880d3148 | ||
|
|
9800f2b8ae | ||
|
|
a5d01604cb | ||
|
|
aaab84f90a | ||
|
|
318a1a303c | ||
|
|
0061de6b2e | ||
|
|
b3db7c547f | ||
|
|
1da29464a8 | ||
|
|
d1d4ff41c6 | ||
|
|
673c2745a9 | ||
|
|
4ad88441cc | ||
|
|
71bb822856 | ||
|
|
2a7789bd12 | ||
|
|
1d0417cc1c | ||
|
|
905c578d90 | ||
|
|
a457c85e53 | ||
|
|
36630fc0ed | ||
|
|
f509ac60da | ||
|
|
bf8aac6f81 | ||
|
|
ceddcc0722 |
27
.github/ISSUE_TEMPLATE/feature-request.md
vendored
Normal file
27
.github/ISSUE_TEMPLATE/feature-request.md
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: For when you know better than me what you want
|
||||
title: "[Request]"
|
||||
labels: enhancement
|
||||
assignees: mnadareski
|
||||
|
||||
---
|
||||
|
||||
**Before You Submit**
|
||||
- Remember to try the [latest WIP build](https://ci.appveyor.com/project/mnadareski/dicui/build/artifacts) to see if the feature already exists.
|
||||
- .NET Core 3.1 has known limitations, so make sure that what you're asking for isn't already in another build.
|
||||
- Check [previous issues](https://github.com/SabreTools/DICUI/issues) to see if any of those are related to what you're about to ask for.
|
||||
|
||||
If none of those apply, then continue...
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
48
.github/ISSUE_TEMPLATE/issue-report.md
vendored
Normal file
48
.github/ISSUE_TEMPLATE/issue-report.md
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
---
|
||||
name: Issue Report
|
||||
about: Tell me what's wrong, seriously
|
||||
title: "[Problem]"
|
||||
labels: bug
|
||||
assignees: mnadareski
|
||||
|
||||
---
|
||||
|
||||
**Before You Submit**
|
||||
- Remember to try the [latest WIP build](https://ci.appveyor.com/project/mnadareski/dicui/build/artifacts) to see if the issue has already been addressed.
|
||||
- .NET Core 3.1 has known issues, please try using another build to reproduce the error
|
||||
- Check multiple discs to help narrow down the issue
|
||||
- Check the Options to see if changing any of those affects your issue.
|
||||
|
||||
If all of those fail, then continue...
|
||||
|
||||
**Version**
|
||||
What version are you using?
|
||||
|
||||
- [ ] Stable release (version here)
|
||||
- [ ] WIP release (version here)
|
||||
|
||||
**Build**
|
||||
What runtime version are you using?
|
||||
|
||||
- [ ] .NET Framework 4.7.2 running on (Operating System)
|
||||
- [ ] .NET Framework 4.8 running on (Operating System)
|
||||
- [ ] .NET Core 3.1 running on (Operating System)
|
||||
|
||||
**Describe the issue**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "DICUI.Library/Aaru/CICMMetadata"]
|
||||
path = DICUI.Library/Aaru/CICMMetadata
|
||||
url = https://github.com/claunia/CICMMetadata
|
||||
@@ -1,3 +1,34 @@
|
||||
### 1.18 (2020-11-10)
|
||||
- Add more information extraction and generation for Aaru
|
||||
- Remove instances of CD Check from copy protection (again, sorry)
|
||||
- Fix multiline submission info outputs
|
||||
- Fix PVD retrieval for multi-session discs
|
||||
- Updated to DIC version 20200921
|
||||
- Add and fix multiple Sega disc header pieces or submission info
|
||||
- Fixed issues in parsing the alternate mainInfo format
|
||||
- Fixed issue with logging clear not working properly
|
||||
- ~~Updated to BurnOutSharp 1.4.1~~
|
||||
- Added split archives for AppVeyor builds
|
||||
- Remove subdump from both UI and run steps
|
||||
- Removed default config file
|
||||
- Fixed copy protect scan using wrong drive when using UI option
|
||||
- Changed default to skip fixed drives
|
||||
- Fixed default media type when skipping type detection
|
||||
- Attempt sector reading for Saturn system detection
|
||||
- Fixed default media type when detection fails
|
||||
- Add option to allow users to select dumping program
|
||||
- ~~Updated to DIC version 20201101~~
|
||||
- Add support for `/ps` DIC flag
|
||||
- Updated to BurnOutSharp 1.5.0
|
||||
- Added HD-DVD-Video detection
|
||||
|
||||
### 1.17.1 (2020-09-14)
|
||||
- Shuffled some shared, internal UI variables
|
||||
- Synced WPF and Avalonia UI internals
|
||||
- Made the disc information window less prone to bugs
|
||||
- Fixed DIC flags based on code (not documentation)
|
||||
- Added support for old(?) DIC flags: `/fix` and `/re`
|
||||
|
||||
### 1.17 (2020-09-12)
|
||||
- Updated to Aaru version 5.1
|
||||
- Updated to BurnOutSharp version 1.4.0
|
||||
|
||||
30
DICUI.Avalonia/ComboBoxItems/InternalProgramComboBoxItem.cs
Normal file
30
DICUI.Avalonia/ComboBoxItems/InternalProgramComboBoxItem.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using DICUI.Data;
|
||||
using DICUI.Utilities;
|
||||
|
||||
namespace DICUI.Avalonia
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a single item in the Internal Program combo box
|
||||
/// </summary>
|
||||
public class InternalProgramComboBoxItem
|
||||
{
|
||||
private object data;
|
||||
|
||||
public InternalProgramComboBoxItem(InternalProgram? internalProgram) => data = internalProgram;
|
||||
|
||||
public static implicit operator InternalProgram? (InternalProgramComboBoxItem item) => item.data as InternalProgram?;
|
||||
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return (data as InternalProgram?).LongName();
|
||||
}
|
||||
}
|
||||
|
||||
public InternalProgram? Value
|
||||
{
|
||||
get { return data as InternalProgram?; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,40 +10,12 @@ namespace DICUI.Avalonia
|
||||
/// </summary>
|
||||
public static class Constants
|
||||
{
|
||||
public const string StartDumping = "Start Dumping";
|
||||
public const string StopDumping = "Stop Dumping";
|
||||
|
||||
public const int LogWindowMarginFromMainWindow = 40;
|
||||
|
||||
// Private lists of known drive speed ranges
|
||||
private static IReadOnlyList<int> cd { get; } = new List<int> { 1, 2, 3, 4, 6, 8, 12, 16, 20, 24, 32, 40, 44, 48, 52, 56, 72 };
|
||||
private static IReadOnlyList<int> dvd { get; } = cd.Where(s => s <= 24).ToList();
|
||||
private static IReadOnlyList<int> bd { get; } = cd.Where(s => s <= 16).ToList();
|
||||
private static IReadOnlyList<int> unknown { get; } = cd; // TODO: All or {1}? Maybe null?
|
||||
|
||||
/// <summary>
|
||||
/// Get list of all drive speeds for a given MediaType
|
||||
/// </summary>
|
||||
/// <param name="type">MediaType? that represents the current item</param>
|
||||
/// <returns>Read-only list of drive speeds</returns>
|
||||
public static IReadOnlyList<int> GetSpeedsForMediaType(MediaType? type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case MediaType.CDROM:
|
||||
case MediaType.GDROM:
|
||||
return cd;
|
||||
case MediaType.DVD:
|
||||
case MediaType.HDDVD:
|
||||
case MediaType.NintendoGameCubeGameDisc:
|
||||
case MediaType.NintendoWiiOpticalDisc:
|
||||
return dvd;
|
||||
case MediaType.BluRay:
|
||||
return bd;
|
||||
default:
|
||||
return unknown;
|
||||
}
|
||||
}
|
||||
|
||||
// Create collections for UI based on known drive speeds
|
||||
public static List<double> SpeedsForCDAsCollection { get; } = GetDoubleCollectionFromIntList(cd);
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2020</Copyright>
|
||||
<RepositoryUrl>https://github.com/SabreTools/DICUI</RepositoryUrl>
|
||||
<Version>1.17.0</Version>
|
||||
<AssemblyVersion>1.17.0</AssemblyVersion>
|
||||
<FileVersion>1.17.0</FileVersion>
|
||||
<Version>1.18</Version>
|
||||
<AssemblyVersion>1.18</AssemblyVersion>
|
||||
<FileVersion>1.18</FileVersion>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
</PropertyGroup>
|
||||
@@ -21,10 +21,16 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="0.9.12" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="0.9.12" />
|
||||
<PackageReference Include="BurnOutSharp" Version="1.4.0" />
|
||||
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="1.5.0" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="4.7.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="$(PkgBurnOutSharp)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\DICUI.Library\DICUI.Library.csproj" />
|
||||
|
||||
@@ -37,14 +37,20 @@ namespace DICUI.Avalonia
|
||||
#endregion
|
||||
|
||||
public DiscInformationWindow()
|
||||
: this(new SubmissionInfo())
|
||||
{
|
||||
}
|
||||
|
||||
public DiscInformationWindow(SubmissionInfo submissionInfo)
|
||||
{
|
||||
this.SubmissionInfo = submissionInfo;
|
||||
this.InitializeComponent();
|
||||
#if DEBUG
|
||||
this.AttachDevTools();
|
||||
#endif
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
private void InitializeComponent()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
|
||||
@@ -62,7 +68,7 @@ namespace DICUI.Avalonia
|
||||
private void DisableFieldsIfNeeded()
|
||||
{
|
||||
// Only disable for single-layer discs
|
||||
if (SubmissionInfo.SizeAndChecksums?.Layerbreak == default(long))
|
||||
if (SubmissionInfo?.SizeAndChecksums?.Layerbreak == default(long))
|
||||
{
|
||||
this.Find<TextBox>("L1MasteringRingTextBox").IsEnabled = false;
|
||||
this.Find<TextBox>("L1MasteringRingTextBox").Background = Brushes.Gray;
|
||||
|
||||
@@ -87,7 +87,7 @@ namespace DICUI.Avalonia
|
||||
|
||||
// TODO: If log window open, "dock" it to the current Window
|
||||
|
||||
if (UIOptions.OpenLogWindowAtStartup)
|
||||
if (UIOptions.Options.OpenLogWindowAtStartup)
|
||||
{
|
||||
//TODO: this should be bound directly to WindowVisible property in two way fashion
|
||||
// we need to study how to properly do it in XAML
|
||||
@@ -125,12 +125,19 @@ namespace DICUI.Avalonia
|
||||
// Get the drive letter from the selected item
|
||||
if (this.Find<ComboBox>("DriveLetterComboBox").SelectedItem is Drive drive)
|
||||
{
|
||||
// Get the current media type
|
||||
if (!UIOptions.SkipMediaTypeDetection)
|
||||
// Get the current media type, if possible
|
||||
if (UIOptions.Options.SkipMediaTypeDetection)
|
||||
{
|
||||
ViewModels.LoggerViewModel.VerboseLog("Media type detection disabled, defaulting to CD-ROM");
|
||||
CurrentMediaType = MediaType.CDROM;
|
||||
}
|
||||
else
|
||||
{
|
||||
ViewModels.LoggerViewModel.VerboseLog("Trying to detect media type for drive {0}.. ", drive.Letter);
|
||||
CurrentMediaType = Validators.GetMediaType(drive);
|
||||
ViewModels.LoggerViewModel.VerboseLogLn(CurrentMediaType == null ? "unable to detect." : ("detected " + CurrentMediaType.LongName() + "."));
|
||||
ViewModels.LoggerViewModel.VerboseLogLn(CurrentMediaType == null ? "unable to detect, defaulting to CD-ROM." : ($"detected {CurrentMediaType.LongName()}."));
|
||||
if (CurrentMediaType == null)
|
||||
CurrentMediaType = MediaType.CDROM;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -210,7 +217,7 @@ namespace DICUI.Avalonia
|
||||
|
||||
// Set the output directory, if we changed drives or it's not already
|
||||
if (driveChanged || string.IsNullOrEmpty(this.Find<TextBox>("OutputDirectoryTextBox").Text))
|
||||
this.Find<TextBox>("OutputDirectoryTextBox").Text = Path.Combine(UIOptions.DefaultOutputPath, drive?.VolumeLabel ?? string.Empty);
|
||||
this.Find<TextBox>("OutputDirectoryTextBox").Text = Path.Combine(UIOptions.Options.DefaultOutputPath, drive?.VolumeLabel ?? string.Empty);
|
||||
|
||||
// Get the extension for the file for the next two statements
|
||||
string extension = Env?.GetExtension(mediaType);
|
||||
@@ -236,7 +243,7 @@ namespace DICUI.Avalonia
|
||||
this.Find<Button>("MediaScanButton").IsEnabled = true;
|
||||
|
||||
// Populate the list of drives and add it to the combo box
|
||||
Drives = Validators.CreateListOfDrives(UIOptions.IgnoreFixedDrives);
|
||||
Drives = Validators.CreateListOfDrives(UIOptions.Options.IgnoreFixedDrives);
|
||||
this.Find<ComboBox>("DriveLetterComboBox").Items = Drives;
|
||||
|
||||
if (Drives.Any())
|
||||
@@ -258,7 +265,7 @@ namespace DICUI.Avalonia
|
||||
this.Find<Button>("CopyProtectScanButton").IsEnabled = true;
|
||||
|
||||
// Get the current media type
|
||||
if (!UIOptions.SkipSystemDetection && index != -1)
|
||||
if (!UIOptions.Options.SkipSystemDetection && index != -1)
|
||||
{
|
||||
ViewModels.LoggerViewModel.VerboseLog("Trying to detect system for drive {0}.. ", Drives[index].Letter);
|
||||
var currentSystem = Validators.GetKnownSystem(Drives[index]);
|
||||
@@ -387,12 +394,15 @@ namespace DICUI.Avalonia
|
||||
/// </summary>
|
||||
private async void ScanAndShowProtection()
|
||||
{
|
||||
// Determine current environment, just in case
|
||||
if (Env == null)
|
||||
Env = DetermineEnvironment();
|
||||
|
||||
if (Env.Drive.Letter != default(char))
|
||||
// Pull the drive letter from the UI directly, just in case
|
||||
var drive = this.Find<ComboBox>("DriveLetterComboBox").SelectedItem as Drive;
|
||||
if (drive.Letter != default(char))
|
||||
{
|
||||
ViewModels.LoggerViewModel.VerboseLogLn("Scanning for copy protection in {0}", Env.Drive.Letter);
|
||||
ViewModels.LoggerViewModel.VerboseLogLn("Scanning for copy protection in {0}", drive.Letter);
|
||||
|
||||
var tempContent = this.Find<TextBlock>("StatusLabel").Text;
|
||||
this.Find<TextBlock>("StatusLabel").Text = "Scanning for copy protection... this might take a while!";
|
||||
@@ -402,7 +412,7 @@ namespace DICUI.Avalonia
|
||||
|
||||
var progress = new Progress<FileProtection>();
|
||||
progress.ProgressChanged += ProgressUpdated;
|
||||
string protections = await Validators.RunProtectionScanOnPath(Env.Drive.Letter + ":\\");
|
||||
string protections = await Validators.RunProtectionScanOnPath(drive.Letter + ":\\");
|
||||
|
||||
// If SmartE is detected on the current disc, remove `/sf` from the flags for DIC only
|
||||
if (Env.InternalProgram == InternalProgram.DiscImageCreator && protections.Contains("SmartE"))
|
||||
@@ -422,7 +432,7 @@ namespace DICUI.Avalonia
|
||||
}.ShowDialog(this);
|
||||
}
|
||||
|
||||
ViewModels.LoggerViewModel.VerboseLog("Detected the following protections in {0}:\r\n\r\n{1}", Env.Drive.Letter, protections);
|
||||
ViewModels.LoggerViewModel.VerboseLog("Detected the following protections in {0}:\r\n\r\n{1}", drive.Letter, protections);
|
||||
|
||||
this.Find<TextBlock>("StatusLabel").Text = tempContent;
|
||||
this.Find<Button>("StartStopButton").IsEnabled = true;
|
||||
@@ -454,7 +464,7 @@ namespace DICUI.Avalonia
|
||||
private void SetSupportedDriveSpeed()
|
||||
{
|
||||
// Set the drive speed list that's appropriate
|
||||
var values = Constants.GetSpeedsForMediaType(CurrentMediaType);
|
||||
var values = Interface.GetSpeedsForMediaType(CurrentMediaType);
|
||||
this.Find<ComboBox>("DriveSpeedComboBox").Items = values;
|
||||
ViewModels.LoggerViewModel.VerboseLogLn("Supported media speeds: {0}", string.Join(",", values));
|
||||
|
||||
@@ -464,6 +474,20 @@ namespace DICUI.Avalonia
|
||||
this.Find<ComboBox>("DriveSpeedComboBox").SelectedItem = speed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show the disc information window
|
||||
/// </summary>
|
||||
/// <param name="submissionInfo">SubmissionInfo object to display and possibly change</param>
|
||||
/// <returns>Dialog open result</returns>
|
||||
private bool? ShowDiscInformationWindow(SubmissionInfo submissionInfo)
|
||||
{
|
||||
var discInformationWindow = new DiscInformationWindow(submissionInfo);
|
||||
discInformationWindow.Load();
|
||||
discInformationWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||
discInformationWindow.ShowDialog(this).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Begin the dumping process using the given inputs
|
||||
/// </summary>
|
||||
@@ -516,7 +540,7 @@ namespace DICUI.Avalonia
|
||||
}
|
||||
}
|
||||
|
||||
this.Find<Button>("StartStopButton").Content = Constants.StopDumping;
|
||||
this.Find<Button>("StartStopButton").Content = Interface.StopDumping;
|
||||
this.Find<Button>("CopyProtectScanButton").IsEnabled = false;
|
||||
this.Find<TextBlock>("StatusLabel").Text = "Beginning dumping process";
|
||||
ViewModels.LoggerViewModel.VerboseLogLn("Starting dumping process..");
|
||||
@@ -535,7 +559,7 @@ namespace DICUI.Avalonia
|
||||
{
|
||||
ViewModels.LoggerViewModel.VerboseLogLn("No dumping command was run, submission information will not be gathered.");
|
||||
this.Find<TextBlock>("StatusLabel").Text = "Execution complete!";
|
||||
this.Find<Button>("StartStopButton").Content = Constants.StartDumping;
|
||||
this.Find<Button>("StartStopButton").Content = Interface.StartDumping;
|
||||
this.Find<Button>("CopyProtectScanButton").IsEnabled = true;
|
||||
return;
|
||||
}
|
||||
@@ -546,16 +570,8 @@ namespace DICUI.Avalonia
|
||||
result = await Env.VerifyAndSaveDumpOutput(resultProgress,
|
||||
protectionProgress,
|
||||
this.Find<CheckBox>("EjectWhenDoneCheckBox").IsChecked,
|
||||
UIOptions.ResetDriveAfterDump,
|
||||
(si) =>
|
||||
{
|
||||
var discInformationWindow = new DiscInformationWindow();
|
||||
discInformationWindow.SubmissionInfo = si;
|
||||
discInformationWindow.Load();
|
||||
discInformationWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||
discInformationWindow.ShowDialog(this).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
return true;
|
||||
}
|
||||
UIOptions.Options.ResetDriveAfterDump,
|
||||
ShowDiscInformationWindow
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -565,7 +581,7 @@ namespace DICUI.Avalonia
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.Find<Button>("StartStopButton").Content = Constants.StartDumping;
|
||||
this.Find<Button>("StartStopButton").Content = Interface.StartDumping;
|
||||
this.Find<Button>("CopyProtectScanButton").IsEnabled = true;
|
||||
}
|
||||
}
|
||||
@@ -712,6 +728,7 @@ namespace DICUI.Avalonia
|
||||
var optionsWindow = new OptionsWindow();
|
||||
optionsWindow.UIOptions = UIOptions;
|
||||
optionsWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen;
|
||||
optionsWindow.Refresh();
|
||||
await optionsWindow.ShowDialog(this);
|
||||
|
||||
// Set any new options
|
||||
@@ -773,11 +790,11 @@ namespace DICUI.Avalonia
|
||||
private void StartStopButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Dump or stop the dump
|
||||
if ((string)this.Find<Button>("StartStopButton").Content == Constants.StartDumping)
|
||||
if ((string)this.Find<Button>("StartStopButton").Content == Interface.StartDumping)
|
||||
{
|
||||
StartDumping();
|
||||
}
|
||||
else if ((string)this.Find<Button>("StartStopButton").Content == Constants.StopDumping)
|
||||
else if ((string)this.Find<Button>("StartStopButton").Content == Interface.StopDumping)
|
||||
{
|
||||
ViewModels.LoggerViewModel.VerboseLogLn("Canceling dumping process...");
|
||||
Env.CancelDumping();
|
||||
@@ -789,7 +806,7 @@ namespace DICUI.Avalonia
|
||||
Env.EjectDisc();
|
||||
}
|
||||
|
||||
if (UIOptions.ResetDriveAfterDump)
|
||||
if (UIOptions.Options.ResetDriveAfterDump)
|
||||
{
|
||||
ViewModels.LoggerViewModel.VerboseLogLn($"Resetting drive {Env.Drive.Letter}");
|
||||
Env.ResetDrive();
|
||||
|
||||
@@ -27,18 +27,21 @@
|
||||
<Button Name="CreatorPathButton" Grid.Row="1" Grid.Column="2" Height="25" Width="25" Content="..." Click="BrowseForPathClick" />
|
||||
|
||||
<!--
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="5" Text="dd Path" />
|
||||
<TextBox Name="DDPathTextBox" Grid.Row="0" Grid.Column="1" Height="25" HorizontalAlignment="Stretch"
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="5" Text="dd Path" />
|
||||
<TextBox Name="DDPathTextBox" Grid.Row="2" Grid.Column="1" Height="25" HorizontalAlignment="Stretch"
|
||||
DataContext="{Binding Source={x:Static local:ViewModels.OptionsViewModel}}"
|
||||
Text="{Binding Path=DDPath}" ScrollViewer.VerticalScrollBarVisibility="Disabled" />
|
||||
<Button Name="DDPathButton" Grid.Row="0" Grid.Column="2" Height="25" Width="25" Content="..." Click="BrowseForPathClick" />
|
||||
<Button Name="DDPathButton" Grid.Row="2" Grid.Column="2" Height="25" Width="25" Content="..." Click="BrowseForPathClick" />
|
||||
-->
|
||||
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="5" Text="Subdump Path" />
|
||||
<TextBox Name="SubDumpPathTextBox" Grid.Row="2" Grid.Column="1" Height="25" HorizontalAlignment="Stretch"
|
||||
DataContext="{Binding Source={x:Static local:ViewModels.OptionsViewModel}}"
|
||||
Text="{Binding Path=SubDumpPath}" ScrollViewer.VerticalScrollBarVisibility="Disabled" />
|
||||
<Button Name="SubDumpPathButton" Grid.Row="2" Grid.Column="2" Height="25" Width="25" Content="..." Click="BrowseForPathClick" />
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="5" Text="Region" />
|
||||
<ComboBox Name="InternalProgramComboBox" Grid.Row="2" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" ScrollViewer.VerticalScrollBarVisibility="Disabled" >
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Path=Name}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="5" Text="Default Output Path" />
|
||||
<TextBox Name="DefaultOutputPathTextBox" Grid.Row="3" Grid.Column="1" Height="25" HorizontalAlignment="Stretch"
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using DICUI.Data;
|
||||
using DICUI.Utilities;
|
||||
using DICUI.Web;
|
||||
|
||||
namespace DICUI.Avalonia
|
||||
@@ -17,6 +20,11 @@ namespace DICUI.Avalonia
|
||||
/// </summary>
|
||||
public UIOptions UIOptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of available internal programs
|
||||
/// </summary>
|
||||
public List<InternalProgramComboBoxItem> InternalPrograms { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
public OptionsWindow()
|
||||
@@ -25,6 +33,8 @@ namespace DICUI.Avalonia
|
||||
#if DEBUG
|
||||
this.AttachDevTools();
|
||||
#endif
|
||||
|
||||
PopulateInternalPrograms();
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
@@ -34,6 +44,34 @@ namespace DICUI.Avalonia
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Get a complete list of internal programs and fill the combo box
|
||||
/// </summary>
|
||||
private void PopulateInternalPrograms()
|
||||
{
|
||||
// We only support certain programs for dumping
|
||||
var internalPrograms = new List<InternalProgram> { InternalProgram.DiscImageCreator, InternalProgram.Aaru, InternalProgram.DD };
|
||||
ViewModels.LoggerViewModel.VerboseLogLn("Populating internal programs, {0} internal programs found.", internalPrograms.Count);
|
||||
|
||||
InternalPrograms = new List<InternalProgramComboBoxItem>();
|
||||
foreach (var internalProgram in internalPrograms)
|
||||
{
|
||||
InternalPrograms.Add(new InternalProgramComboBoxItem(internalProgram));
|
||||
}
|
||||
|
||||
this.Find<ComboBox>("InternalProgramComboBox").Items = InternalPrograms;
|
||||
this.Find<ComboBox>("InternalProgramComboBox").SelectedIndex = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refresh any options-related elements
|
||||
/// </summary>
|
||||
public void Refresh()
|
||||
{
|
||||
// Handle non-bindable fields
|
||||
this.Find<ComboBox>("InternalProgramComboBox").SelectedIndex = InternalPrograms.FindIndex(r => r == Converters.ToInternalProgram(UIOptions.Options.InternalProgram));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find a TextBox by setting name
|
||||
/// </summary>
|
||||
@@ -95,6 +133,9 @@ namespace DICUI.Avalonia
|
||||
/// </summary>
|
||||
private void OnAcceptClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Handle non-bindable fields
|
||||
UIOptions.Options.InternalProgram = (this.Find<ComboBox>("InternalProgramComboBox").SelectedItem as InternalProgramComboBoxItem)?.Name ?? InternalProgram.DiscImageCreator.ToString();
|
||||
|
||||
UIOptions.Save();
|
||||
Hide();
|
||||
}
|
||||
|
||||
@@ -9,18 +9,6 @@ namespace DICUI.Avalonia
|
||||
// TODO: Is there any way that this can be made private?
|
||||
public Options Options { get; set; }
|
||||
|
||||
#region Passthrough readonly values
|
||||
|
||||
// TODO: Can any of these be removed?
|
||||
public string DefaultOutputPath { get { return Options.DefaultOutputPath; } }
|
||||
public bool IgnoreFixedDrives { get { return Options.IgnoreFixedDrives; } }
|
||||
public bool ResetDriveAfterDump { get { return Options.ResetDriveAfterDump; } }
|
||||
public bool SkipMediaTypeDetection { get { return Options.SkipMediaTypeDetection; } }
|
||||
public bool SkipSystemDetection { get { return Options.SkipSystemDetection; } }
|
||||
public bool OpenLogWindowAtStartup { get { return Options.OpenLogWindowAtStartup; } }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
|
||||
@@ -43,12 +43,6 @@ namespace DICUI.Avalonia
|
||||
set { _uiOptions.Options.DefaultOutputPath = value; }
|
||||
}
|
||||
|
||||
public string SubDumpPath
|
||||
{
|
||||
get { return _uiOptions.Options.SubDumpPath; }
|
||||
set { _uiOptions.Options.SubDumpPath = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Dumping Speeds
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2020</Copyright>
|
||||
<RepositoryUrl>https://github.com/SabreTools/DICUI</RepositoryUrl>
|
||||
<Version>1.17.0</Version>
|
||||
<AssemblyVersion>1.17.0</AssemblyVersion>
|
||||
<FileVersion>1.17.0</FileVersion>
|
||||
<Version>1.18</Version>
|
||||
<AssemblyVersion>1.18</AssemblyVersion>
|
||||
<FileVersion>1.18</FileVersion>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
</PropertyGroup>
|
||||
@@ -22,7 +22,13 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BurnOutSharp" Version="1.4.0" />
|
||||
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="1.5.0" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="$(PkgBurnOutSharp)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
1
DICUI.Library/Aaru/CICMMetadata
Submodule
1
DICUI.Library/Aaru/CICMMetadata
Submodule
Submodule DICUI.Library/Aaru/CICMMetadata added at 7944bca8e6
@@ -2,10 +2,12 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml;
|
||||
using System.Xml.Schema;
|
||||
using System.Xml.Serialization;
|
||||
using DICUI.CueSheets;
|
||||
using DICUI.Data;
|
||||
using DICUI.Utilities;
|
||||
using DICUI.Web;
|
||||
@@ -1554,9 +1556,14 @@ namespace DICUI.Aaru
|
||||
{
|
||||
case MediaType.CDROM:
|
||||
// TODO: Can this do GD-ROM?
|
||||
// TODO: Investigate if there's an error count for Aaru
|
||||
info.Extras.PVD = GeneratePVD(sidecar) ?? "Disc has no PVD";
|
||||
|
||||
long errorCount = -1;
|
||||
if (File.Exists(basePath + ".resume.xml"))
|
||||
errorCount = GetErrorCount(basePath + ".resume.xml");
|
||||
|
||||
info.CommonDiscInfo.ErrorsCount = (errorCount == -1 ? "Error retrieving error count" : errorCount.ToString());
|
||||
|
||||
info.TracksAndWriteOffsets.Cuesheet = GenerateCuesheet(sidecar, basePath) ?? "";
|
||||
|
||||
string cdWriteOffset = GetWriteOffset(sidecar) ?? "";
|
||||
@@ -1578,8 +1585,26 @@ namespace DICUI.Aaru
|
||||
info.SizeAndChecksums.SHA1 = sha1;
|
||||
}
|
||||
|
||||
// Deal with the layerbreak
|
||||
string layerbreak = null;
|
||||
if (type == MediaType.DVD)
|
||||
layerbreak = GetLayerbreak(sidecar) ?? "";
|
||||
else if (type == MediaType.BluRay)
|
||||
layerbreak = info.SizeAndChecksums.Size > 25_025_314_816 ? "25025314816" : null;
|
||||
|
||||
// If we have a single-layer disc
|
||||
if (string.IsNullOrWhiteSpace(layerbreak))
|
||||
{
|
||||
info.Extras.PVD = GeneratePVD(sidecar) ?? "Disc has no PVD";
|
||||
}
|
||||
// If we have a dual-layer disc
|
||||
else
|
||||
{
|
||||
info.Extras.PVD = GeneratePVD(sidecar) ?? "Disc has no PVD";
|
||||
info.SizeAndChecksums.Layerbreak = Int64.Parse(layerbreak);
|
||||
}
|
||||
|
||||
// TODO: Investigate XGD disc outputs
|
||||
// TODO: Get layerbreak information
|
||||
// TODO: Investigate BD specifics like PIC
|
||||
|
||||
break;
|
||||
@@ -1587,13 +1612,18 @@ namespace DICUI.Aaru
|
||||
|
||||
switch (system)
|
||||
{
|
||||
// TODO: Can we get DVD protection or SecuROM data out of this?
|
||||
// TODO: Can we get SecuROM data?
|
||||
// TODO: Can we get SS version/ranges?
|
||||
// TODO: Can we get DMI info?
|
||||
// TODO: Can we get Sega Header info?
|
||||
// TODO: Can we get PS1 EDC status?
|
||||
// TODO: Can we get PS1 LibCrypt status?
|
||||
|
||||
case KnownSystem.DVDAudio:
|
||||
case KnownSystem.DVDVideo:
|
||||
info.CopyProtection.Protection = GetDVDProtection(sidecar) ?? "";
|
||||
break;
|
||||
|
||||
case KnownSystem.KonamiPython2:
|
||||
if (GetPlayStationExecutableInfo(drive?.Letter, out string pythonTwoSerial, out Region? pythonTwoRegion, out string pythonTwoDate))
|
||||
{
|
||||
@@ -2162,6 +2192,62 @@ namespace DICUI.Aaru
|
||||
|
||||
#region Information Extraction Methods
|
||||
|
||||
/// <summary>
|
||||
/// Convert the TrackTypeTrackType value to a CueTrackDataType
|
||||
/// </summary>
|
||||
/// <param name="trackType">TrackTypeTrackType to convert</param>
|
||||
/// <param name="bytesPerSector">Sector size to help with specific subtypes</param>
|
||||
/// <returns>CueTrackDataType representing the input data</returns>
|
||||
private CueTrackDataType ConvertToDataType(TrackTypeTrackType trackType, uint bytesPerSector)
|
||||
{
|
||||
switch (trackType)
|
||||
{
|
||||
case TrackTypeTrackType.audio:
|
||||
return CueTrackDataType.AUDIO;
|
||||
|
||||
case TrackTypeTrackType.mode1:
|
||||
if (bytesPerSector == 2048)
|
||||
return CueTrackDataType.MODE1_2048;
|
||||
else
|
||||
return CueTrackDataType.MODE1_2352;
|
||||
|
||||
case TrackTypeTrackType.mode2:
|
||||
case TrackTypeTrackType.m2f1:
|
||||
case TrackTypeTrackType.m2f2:
|
||||
if (bytesPerSector == 2336)
|
||||
return CueTrackDataType.MODE2_2336;
|
||||
else
|
||||
return CueTrackDataType.MODE2_2352;
|
||||
|
||||
default:
|
||||
return CueTrackDataType.MODE1_2352;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert the TrackFlagsType value to a CueTrackFlag
|
||||
/// </summary>
|
||||
/// <param name="trackFlagsType">TrackFlagsType containing flag data</param>
|
||||
/// <returns>CueTrackFlag representing the flags</returns>
|
||||
private CueTrackFlag ConvertToTrackFlag(TrackFlagsType trackFlagsType)
|
||||
{
|
||||
if (trackFlagsType == null)
|
||||
return 0;
|
||||
|
||||
CueTrackFlag flag = 0;
|
||||
|
||||
if (trackFlagsType.CopyPermitted)
|
||||
flag |= CueTrackFlag.DCP;
|
||||
|
||||
if (trackFlagsType.Quadraphonic)
|
||||
flag |= CueTrackFlag.FourCH;
|
||||
|
||||
if (trackFlagsType.PreEmphasis)
|
||||
flag |= CueTrackFlag.PRE;
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a cuesheet string based on CICM sidecar data
|
||||
/// </summary>
|
||||
@@ -2176,7 +2262,9 @@ namespace DICUI.Aaru
|
||||
|
||||
// Required variables
|
||||
uint totalTracks = 0;
|
||||
string cuesheet = string.Empty;
|
||||
CueSheet cueSheet = new CueSheet();
|
||||
cueSheet.Performer = string.Join(", ", cicmSidecar.Performer ?? new string[0]);
|
||||
cueSheet.Files = new List<CueFile>();
|
||||
|
||||
// Only care about OpticalDisc types
|
||||
if (cicmSidecar.OpticalDisc == null || cicmSidecar.OpticalDisc.Length == 0)
|
||||
@@ -2193,83 +2281,79 @@ namespace DICUI.Aaru
|
||||
if (opticalDisc.Track == null || opticalDisc.Track.Length == 0)
|
||||
continue;
|
||||
|
||||
// Get cuesheet-level information
|
||||
cueSheet.Catalog = opticalDisc.MediaCatalogueNumber;
|
||||
|
||||
// Loop through each track
|
||||
foreach (TrackType track in opticalDisc.Track)
|
||||
{
|
||||
uint trackNumber = track.Sequence?.TrackNumber ?? 0;
|
||||
TrackTypeTrackType trackType = track.TrackType1;
|
||||
// Create cue track entry
|
||||
CueTrack cueTrack = new CueTrack();
|
||||
cueTrack.Number = (int)(track.Sequence?.TrackNumber ?? 0);
|
||||
cueTrack.DataType = ConvertToDataType(track.TrackType1, track.BytesPerSector);
|
||||
cueTrack.Flags = ConvertToTrackFlag(track.Flags);
|
||||
cueTrack.ISRC = track.ISRC;
|
||||
|
||||
// Build the track datfile data and append
|
||||
string trackName = basePath;
|
||||
if (totalTracks == 1)
|
||||
trackName = $"{trackName}.bin";
|
||||
else if (totalTracks > 1 && totalTracks < 10)
|
||||
trackName = $"{trackName} (Track {trackNumber}).bin";
|
||||
else
|
||||
trackName = $"{trackName} (Track {trackNumber.ToString().PadLeft(2, '0')}).bin";
|
||||
|
||||
// Handle track flags
|
||||
if (track.Flags != null)
|
||||
{
|
||||
string flagString = "FLAGS";
|
||||
if (track.Flags.CopyPermitted)
|
||||
flagString += " DCP";
|
||||
if (track.Flags.Quadraphonic)
|
||||
flagString += " 4CH";
|
||||
if (track.Flags.PreEmphasis)
|
||||
flagString += " PRE";
|
||||
|
||||
cuesheet += $"{flagString}\n";
|
||||
}
|
||||
|
||||
// Handle ISRC
|
||||
if (track.ISRC != null)
|
||||
cuesheet += $"ISRC {track.ISRC}\n";
|
||||
|
||||
// Add track for each file
|
||||
cuesheet += $"FILE \"{trackName}\" BINARY\n";
|
||||
|
||||
// Add track type
|
||||
string trackTypeString = string.Empty;
|
||||
switch (trackType)
|
||||
{
|
||||
case TrackTypeTrackType.audio:
|
||||
trackTypeString = "AUDIO";
|
||||
break;
|
||||
case TrackTypeTrackType.mode0:
|
||||
trackTypeString = $"MODE0/{track.BytesPerSector}";
|
||||
break;
|
||||
case TrackTypeTrackType.mode1:
|
||||
trackTypeString = $"MODE1/{track.BytesPerSector}";
|
||||
break;
|
||||
case TrackTypeTrackType.mode2:
|
||||
case TrackTypeTrackType.m2f1:
|
||||
case TrackTypeTrackType.m2f2:
|
||||
trackTypeString = $"MODE2/{track.BytesPerSector}";
|
||||
break;
|
||||
}
|
||||
|
||||
cuesheet += $" TRACK {trackNumber.ToString().PadLeft(2, '0')} {trackTypeString}\n";
|
||||
// Create cue file entry
|
||||
CueFile cueFile = new CueFile();
|
||||
cueFile.FileName = GenerateTrackName(basePath, (int)totalTracks, cueTrack.Number, opticalDisc.DiscType);
|
||||
cueFile.FileType = CueFileType.BINARY;
|
||||
cueFile.Tracks = new List<CueTrack>();
|
||||
|
||||
// Add index data
|
||||
if (track.Indexes != null && track.Indexes.Length > 0)
|
||||
{
|
||||
cueTrack.Indices = new List<CueIndex>();
|
||||
|
||||
// Loop through each index
|
||||
foreach (TrackIndexType trackIndex in track.Indexes)
|
||||
{
|
||||
// TODO: Convert Value to timecode
|
||||
cuesheet += $" INDEX {trackIndex.index} {trackIndex.Value}\n";
|
||||
// Get timestamp from frame count
|
||||
int absoluteLength = Math.Abs(trackIndex.Value);
|
||||
int frames = absoluteLength % 75;
|
||||
int seconds = (absoluteLength / 75) % 60;
|
||||
int minutes = (absoluteLength / 75 / 60);
|
||||
string timeString = $"{minutes:D2}:{seconds:D2}:{frames:D2}";
|
||||
|
||||
// Pregap information
|
||||
if (trackIndex.Value < 0)
|
||||
cueTrack.PreGap = new PreGap(timeString);
|
||||
|
||||
// Individual indexes
|
||||
else
|
||||
cueTrack.Indices.Add(new CueIndex(trackIndex.index.ToString(), timeString));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default if index data missing from sidecar
|
||||
cuesheet += " INDEX 01 00:00:00\n";
|
||||
cueTrack.Indices = new List<CueIndex>()
|
||||
{
|
||||
new CueIndex("01", "00:00:00"),
|
||||
};
|
||||
}
|
||||
|
||||
// Add the track to the file
|
||||
cueFile.Tracks.Add(cueTrack);
|
||||
|
||||
// Add the file to the cuesheet
|
||||
cueSheet.Files.Add(cueFile);
|
||||
}
|
||||
}
|
||||
|
||||
return cuesheet;
|
||||
// If we have a cuesheet to write out, do so
|
||||
if (cueSheet != null && cueSheet != default)
|
||||
{
|
||||
MemoryStream ms = new MemoryStream();
|
||||
cueSheet.Write(ms);
|
||||
ms.Position = 0;
|
||||
using (var sr = new StreamReader(ms))
|
||||
{
|
||||
return sr.ReadToEnd();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -2288,7 +2372,7 @@ namespace DICUI.Aaru
|
||||
string datfile = string.Empty;
|
||||
|
||||
// Process OpticalDisc, if possible
|
||||
if (cicmSidecar.OpticalDisc != null || cicmSidecar.OpticalDisc.Length > 0)
|
||||
if (cicmSidecar.OpticalDisc != null && cicmSidecar.OpticalDisc.Length > 0)
|
||||
{
|
||||
// Loop through each OpticalDisc in the metadata
|
||||
foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc)
|
||||
@@ -2333,21 +2417,14 @@ namespace DICUI.Aaru
|
||||
}
|
||||
|
||||
// Build the track datfile data and append
|
||||
string trackName = basePath;
|
||||
if (totalTracks == 1)
|
||||
trackName = $"{trackName}.bin";
|
||||
else if (totalTracks > 1 && totalTracks < 10)
|
||||
trackName = $"{trackName} (Track {trackNumber}).bin";
|
||||
else
|
||||
trackName = $"{trackName} (Track {trackNumber.ToString().PadLeft(2, '0')}).bin";
|
||||
|
||||
string trackName = GenerateTrackName(basePath, (int)totalTracks, (int)trackNumber, opticalDisc.DiscType);
|
||||
datfile += $"<rom name=\"{trackName}\" size=\"{size}\" crc=\"{crc32}\" md5=\"{md5}\" sha1=\"{sha1}\" />\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process BlockMedia, if possible
|
||||
if (cicmSidecar.BlockMedia != null || cicmSidecar.BlockMedia.Length > 0)
|
||||
if (cicmSidecar.BlockMedia != null && cicmSidecar.BlockMedia.Length > 0)
|
||||
{
|
||||
// Loop through each BlockMedia in the metadata
|
||||
foreach (BlockMediaType blockMedia in cicmSidecar.BlockMedia)
|
||||
@@ -2387,12 +2464,36 @@ namespace DICUI.Aaru
|
||||
return datfile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a track name based on current path and tracks
|
||||
/// </summary>
|
||||
/// <param name="basePath">Base path for determining file names</param>
|
||||
/// <param name="totalTracks">Total number of tracks in the media</param>
|
||||
/// <param name="trackNumber">Current track index</param>
|
||||
/// <param name="discType">Current disc type, used for determining extension</param>
|
||||
/// <returns>Formatted string representing the track name according to Redump standards</returns>
|
||||
private static string GenerateTrackName(string basePath, int totalTracks, int trackNumber, string discType)
|
||||
{
|
||||
string extension = "bin";
|
||||
if (discType.Contains("BD") || discType.Contains("DVD"))
|
||||
extension = "iso";
|
||||
|
||||
string trackName = Path.GetFileNameWithoutExtension(basePath);
|
||||
if (totalTracks == 1)
|
||||
trackName = $"{trackName}.{extension}";
|
||||
else if (totalTracks > 1 && totalTracks < 10)
|
||||
trackName = $"{trackName} (Track {trackNumber}).{extension}";
|
||||
else
|
||||
trackName = $"{trackName} (Track {trackNumber:D2}).{extension}";
|
||||
|
||||
return trackName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a Redump-compatible PVD block based on CICM sidecar file
|
||||
/// </summary>
|
||||
/// <param name="cicmSidecar">CICM Sidecar data generated by Aaru</param>
|
||||
/// <returns>String containing the PVD, null on error</returns>
|
||||
/// TODO: Fix this string outputting
|
||||
private static string GeneratePVD(CICMMetadataType cicmSidecar)
|
||||
{
|
||||
// If the object is null, we can't get information from it
|
||||
@@ -2405,139 +2506,20 @@ namespace DICUI.Aaru
|
||||
// Loop through each OpticalDisc in the metadata
|
||||
foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc)
|
||||
{
|
||||
// Required variables
|
||||
DateTime creation = DateTime.MinValue;
|
||||
DateTime modification = DateTime.MinValue;
|
||||
DateTime expiration = DateTime.MinValue;
|
||||
DateTime effective = DateTime.MinValue;
|
||||
byte[] pvdData = GeneratePVDData(opticalDisc);
|
||||
|
||||
// If there are no tracks, we can't get a PVD
|
||||
if (opticalDisc.Track == null || opticalDisc.Track.Length == 0)
|
||||
// If we got a null value, we skip this disc
|
||||
if (pvdData == null)
|
||||
continue;
|
||||
|
||||
// Take the first track only
|
||||
TrackType track = opticalDisc.Track[0];
|
||||
|
||||
// If there are no partitions, we can't get a PVD
|
||||
if (track.FileSystemInformation == null || track.FileSystemInformation.Length == 0)
|
||||
continue;
|
||||
|
||||
// Loop through each Partition
|
||||
foreach (PartitionType partition in track.FileSystemInformation)
|
||||
{
|
||||
// If the partition has no file systems, we can't get a PVD
|
||||
if (partition.FileSystems == null || partition.FileSystems.Length == 0)
|
||||
continue;
|
||||
|
||||
// Loop through each FileSystem until we find a PVD
|
||||
foreach (FileSystemType fileSystem in partition.FileSystems)
|
||||
{
|
||||
// If we don't have a PVD-able filesystem, we can't get a PVD
|
||||
if (!fileSystem.CreationDateSpecified
|
||||
&& !fileSystem.ModificationDateSpecified
|
||||
&& !fileSystem.ExpirationDateSpecified
|
||||
&& !fileSystem.EffectiveDateSpecified)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Creation Date
|
||||
if (fileSystem.CreationDateSpecified)
|
||||
creation = fileSystem.CreationDate;
|
||||
|
||||
// Modification Date
|
||||
if (fileSystem.ModificationDateSpecified)
|
||||
modification = fileSystem.ModificationDate;
|
||||
|
||||
// Expiration Date
|
||||
if (fileSystem.ExpirationDateSpecified)
|
||||
expiration = fileSystem.ExpirationDate;
|
||||
|
||||
// Effective Date
|
||||
if (fileSystem.EffectiveDateSpecified)
|
||||
effective = fileSystem.EffectiveDate;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// If we found a Partition with PVD data, we break
|
||||
if (creation != DateTime.MinValue
|
||||
|| modification != DateTime.MinValue
|
||||
|| expiration != DateTime.MinValue
|
||||
|| effective != DateTime.MinValue)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Needs to look like this on the other side
|
||||
0320 : 20 20 20 20 20 20 20 20 20 20 20 20 20 31 39 39 199
|
||||
0330 : 36 30 39 32 34 31 34 35 34 30 35 30 30 DC 31 39 6092414540500.19
|
||||
0340 : 39 36 30 39 32 34 31 34 35 34 30 35 30 30 DC 30 96092414540500.0
|
||||
0350 : 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 00 000000000000000.
|
||||
0360 : 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 0000000000000000
|
||||
0370 : 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||||
*/
|
||||
|
||||
// TODO: Hundredths of seconds are not part of the output, using 00 `30 30` bytes for now
|
||||
// TODO: Timezones are not part of the output, using UTC `00` byte for now
|
||||
|
||||
// Build each row in consecutive order
|
||||
string pvd = string.Empty;
|
||||
|
||||
// String versions of each DateTime
|
||||
string emptyTime = "0000-00-00T00:00:00.00+00:00";
|
||||
string creationString = emptyTime;
|
||||
string modificationString = emptyTime;
|
||||
string expirationString = emptyTime;
|
||||
string effectiveString = emptyTime;
|
||||
|
||||
// If we don't have default values, set the proper string
|
||||
if (creation != DateTime.MinValue)
|
||||
creationString = creation.ToString("yyyy-MM-ddTHH:mm:ss.ffK");
|
||||
if (modification != DateTime.MinValue)
|
||||
modificationString = emptyTime;
|
||||
if (expiration != DateTime.MinValue)
|
||||
expirationString = emptyTime;
|
||||
if (effective != DateTime.MinValue)
|
||||
effectiveString = emptyTime;
|
||||
|
||||
// Get byte versions of each for the PVD
|
||||
byte[] effectiveBytes = creationString.ToCharArray().Select(c => (byte)c).ToArray();
|
||||
byte[] creationBytes = modificationString.ToCharArray().Select(c => (byte)c).ToArray();
|
||||
byte[] modificationBytes = expirationString.ToCharArray().Select(c => (byte)c).ToArray();
|
||||
byte[] expirationBytes = effectiveString.ToCharArray().Select(c => (byte)c).ToArray();
|
||||
|
||||
// 0320
|
||||
pvd += $"0320 : 20 20 20 20 20 20 20 20";
|
||||
pvd += $" 20 20 20 20 20 {creationBytes[0]:x} {creationBytes[1]:x} {creationBytes[2]:x}";
|
||||
pvd += $" {creationString.Substring(0, 3)}\n";
|
||||
|
||||
// 0330
|
||||
pvd += $"0330 : {creationBytes[3]:x} {creationBytes[5]:x} {creationBytes[6]:x} {creationBytes[8]:x} {creationBytes[9]:x} {creationBytes[11]:x} {creationBytes[12]:x} {creationBytes[14]:x}";
|
||||
pvd += $" {creationBytes[15]:x} {creationBytes[17]:x} {creationBytes[18]:x} 30 30 00 {modificationBytes[0]:x} {modificationBytes[1]:x}";
|
||||
pvd += $" {creationString[3]}{creationString.Substring(5, 2)}{creationString.Substring(8, 2)}{creationString.Substring(11, 2)}{creationString.Substring(14, 2)}{creationString.Substring(17, 2)}00.{modificationString.Substring(0, 2)}\n";
|
||||
|
||||
// 0340
|
||||
pvd += $"0340 : {modificationBytes[2]:x} {modificationBytes[3]:x} {modificationBytes[5]:x} {modificationBytes[6]:x} {modificationBytes[8]:x} {modificationBytes[9]:x} {modificationBytes[11]:x} {modificationBytes[12]:x}";
|
||||
pvd += $" {modificationBytes[14]:x} {modificationBytes[15]:x} {modificationBytes[17]:x} {modificationBytes[18]:x} 30 30 00 {expirationBytes[0]:x}";
|
||||
pvd += $" {modificationString.Substring(2, 2)}{modificationString.Substring(5, 2)}{modificationString.Substring(8, 2)}{modificationString.Substring(11, 2)}{modificationString.Substring(14, 2)}{modificationString.Substring(17, 2)}00.{expirationString[0]}\n";
|
||||
|
||||
// 0350
|
||||
pvd += $"0350 : {expirationBytes[1]:x} {expirationBytes[2]:x} {expirationBytes[3]:x} {expirationBytes[5]:x} {expirationBytes[6]:x} {expirationBytes[8]:x} {expirationBytes[9]:x} {expirationBytes[11]:x}";
|
||||
pvd += $" {expirationBytes[12]:x} {expirationBytes[14]:x} {expirationBytes[15]:x} {expirationBytes[17]:x} {expirationBytes[18]:x} 30 30 00";
|
||||
pvd += $" {expirationString.Substring(1, 3)}{expirationString.Substring(5, 2)}{expirationString.Substring(8, 2)}{expirationString.Substring(11, 2)}{expirationString.Substring(14, 2)}{expirationString.Substring(17, 2)}00.\n";
|
||||
|
||||
// 0360
|
||||
pvd += $"0360 : {effectiveBytes[0]:x} {effectiveBytes[1]:x} {effectiveBytes[2]:x} {effectiveBytes[3]:x} {effectiveBytes[5]:x} {effectiveBytes[6]:x} {effectiveBytes[8]:x} {effectiveBytes[9]:x}";
|
||||
pvd += $" {effectiveBytes[11]:x} {effectiveBytes[12]:x} {effectiveBytes[14]:x} {effectiveBytes[15]:x} {effectiveBytes[17]:x} {effectiveBytes[18]:x} 30 30";
|
||||
pvd += $" {effectiveString.Substring(0, 4)}{effectiveString.Substring(5, 2)}{effectiveString.Substring(8, 2)}{effectiveString.Substring(11, 2)}{effectiveString.Substring(14, 2)}{effectiveString.Substring(17, 2)}00\n";
|
||||
|
||||
// 0370 - TODO: Get the sometimes burner string that appears in this block
|
||||
pvd += $"0370 : 00 01 00 00 00 00 00 00";
|
||||
pvd += $" 00 00 00 00 00 00 00 00";
|
||||
pvd += $" ................\n";
|
||||
pvd += GenerateSectorOutputLine("0320", new ReadOnlySpan<byte>(pvdData, 0, 16));
|
||||
pvd += GenerateSectorOutputLine("0330", new ReadOnlySpan<byte>(pvdData, 16, 16));
|
||||
pvd += GenerateSectorOutputLine("0340", new ReadOnlySpan<byte>(pvdData, 32, 16));
|
||||
pvd += GenerateSectorOutputLine("0350", new ReadOnlySpan<byte>(pvdData, 48, 16));
|
||||
pvd += GenerateSectorOutputLine("0360", new ReadOnlySpan<byte>(pvdData, 64, 16));
|
||||
pvd += GenerateSectorOutputLine("0370", new ReadOnlySpan<byte>(pvdData, 80, 16));
|
||||
|
||||
return pvd;
|
||||
}
|
||||
@@ -2546,6 +2528,165 @@ namespace DICUI.Aaru
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate the byte array representing the current PVD information
|
||||
/// </summary>
|
||||
/// <param name="opticalDisc">OpticalDisc type from CICM Sidecar data</param>
|
||||
/// <returns>Byte array representing the PVD, null on error</returns>
|
||||
private static byte[] GeneratePVDData(OpticalDiscType opticalDisc)
|
||||
{
|
||||
// Required variables
|
||||
DateTime creation = DateTime.MinValue;
|
||||
DateTime modification = DateTime.MinValue;
|
||||
DateTime expiration = DateTime.MinValue;
|
||||
DateTime effective = DateTime.MinValue;
|
||||
|
||||
// If there are no tracks, we can't get a PVD
|
||||
if (opticalDisc.Track == null || opticalDisc.Track.Length == 0)
|
||||
return null;
|
||||
|
||||
// Take the first track only
|
||||
TrackType track = opticalDisc.Track[0];
|
||||
|
||||
// If there are no partitions, we can't get a PVD
|
||||
if (track.FileSystemInformation == null || track.FileSystemInformation.Length == 0)
|
||||
return null;
|
||||
|
||||
// Loop through each Partition
|
||||
foreach (PartitionType partition in track.FileSystemInformation)
|
||||
{
|
||||
// If the partition has no file systems, we can't get a PVD
|
||||
if (partition.FileSystems == null || partition.FileSystems.Length == 0)
|
||||
continue;
|
||||
|
||||
// Loop through each FileSystem until we find a PVD
|
||||
foreach (FileSystemType fileSystem in partition.FileSystems)
|
||||
{
|
||||
// If we don't have a PVD-able filesystem, we can't get a PVD
|
||||
if (!fileSystem.CreationDateSpecified
|
||||
&& !fileSystem.ModificationDateSpecified
|
||||
&& !fileSystem.ExpirationDateSpecified
|
||||
&& !fileSystem.EffectiveDateSpecified)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Creation Date
|
||||
if (fileSystem.CreationDateSpecified)
|
||||
creation = fileSystem.CreationDate;
|
||||
|
||||
// Modification Date
|
||||
if (fileSystem.ModificationDateSpecified)
|
||||
modification = fileSystem.ModificationDate;
|
||||
|
||||
// Expiration Date
|
||||
if (fileSystem.ExpirationDateSpecified)
|
||||
expiration = fileSystem.ExpirationDate;
|
||||
|
||||
// Effective Date
|
||||
if (fileSystem.EffectiveDateSpecified)
|
||||
effective = fileSystem.EffectiveDate;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// If we found a Partition with PVD data, we break
|
||||
if (creation != DateTime.MinValue
|
||||
|| modification != DateTime.MinValue
|
||||
|| expiration != DateTime.MinValue
|
||||
|| effective != DateTime.MinValue)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we found no partitions, we return null
|
||||
if (creation == DateTime.MinValue
|
||||
&& modification == DateTime.MinValue
|
||||
&& expiration == DateTime.MinValue
|
||||
&& effective == DateTime.MinValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Now generate the byte array data
|
||||
List<byte> pvdData = new List<byte>();
|
||||
pvdData.AddRange(new string(' ', 13).ToCharArray().Select(c => (byte)c));
|
||||
pvdData.AddRange(GeneratePVDDateTimeBytes(creation));
|
||||
pvdData.AddRange(GeneratePVDDateTimeBytes(modification));
|
||||
pvdData.AddRange(GeneratePVDDateTimeBytes(expiration));
|
||||
pvdData.AddRange(GeneratePVDDateTimeBytes(effective));
|
||||
pvdData.Add(0x01);
|
||||
pvdData.AddRange(new string((char)0, 14).ToCharArray().Select(c => (byte)c));
|
||||
|
||||
// Return the filled array
|
||||
return pvdData.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate the required bytes from a DateTime object
|
||||
/// </summary>
|
||||
/// <param name="dateTime">DateTime to get representation of</param>
|
||||
/// <returns>Byte array representing the DateTime</returns>
|
||||
private static byte[] GeneratePVDDateTimeBytes(DateTime dateTime)
|
||||
{
|
||||
string emptyTime = "0000000000000000";
|
||||
string dateTimeString = emptyTime;
|
||||
byte timeZoneNumber = 0;
|
||||
|
||||
// If we don't have default values, set the proper string
|
||||
if (dateTime != DateTime.MinValue)
|
||||
{
|
||||
dateTimeString = dateTime.ToString("yyyyMMddHHmmssff");
|
||||
|
||||
// Get timezone offset (0 == GMT, up and down in 15-minute increments)
|
||||
string timeZoneString;
|
||||
try
|
||||
{
|
||||
timeZoneString = dateTime.ToString("zzz");
|
||||
}
|
||||
catch
|
||||
{
|
||||
timeZoneString = "00:00";
|
||||
}
|
||||
|
||||
// Format is hh:mm
|
||||
string[] splitTimeZoneString = timeZoneString.Split(':');
|
||||
if (int.TryParse(splitTimeZoneString[0], out int hours))
|
||||
timeZoneNumber += (byte)(hours * 4);
|
||||
if (int.TryParse(splitTimeZoneString[1], out int minutes))
|
||||
timeZoneNumber += (byte)(minutes / 15);
|
||||
}
|
||||
|
||||
// Get and return the byte array
|
||||
List<byte> dateTimeList = dateTimeString.ToCharArray().Select(c => (byte)c).ToList();
|
||||
dateTimeList.Add(timeZoneNumber);
|
||||
return dateTimeList.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a single 16-byte sector line from a byte array
|
||||
/// </summary>
|
||||
/// <param name="row">Row ID for outputting</param>
|
||||
/// <param name="bytes">Byte span representing the data to write</param>
|
||||
/// <returns>Formatted string representing the sector line</returns>
|
||||
private static string GenerateSectorOutputLine(string row, ReadOnlySpan<byte> bytes)
|
||||
{
|
||||
// If the data isn't correct, return null
|
||||
if (bytes == null || bytes.Length != 16)
|
||||
return null;
|
||||
|
||||
string pvdLine = $"{row} : ";
|
||||
pvdLine += BitConverter.ToString(bytes.Slice(0, 8).ToArray()).Replace("-", " ");
|
||||
pvdLine += " ";
|
||||
pvdLine += BitConverter.ToString(bytes.Slice(8, 8).ToArray().ToArray()).Replace("-", " ");
|
||||
pvdLine += " ";
|
||||
pvdLine += Encoding.ASCII.GetString(bytes.ToArray()).Replace((char)0, '.').Replace('?', '.');
|
||||
pvdLine += "\n";
|
||||
|
||||
return pvdLine;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read the CICM Sidecar as an object
|
||||
/// </summary>
|
||||
@@ -2578,6 +2719,130 @@ namespace DICUI.Aaru
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the DVD protection information, if possible
|
||||
/// </summary>
|
||||
/// <param name="cicmSidecar">CICM Sidecar data generated by Aaru</param>
|
||||
/// <returns>Formatted string representing the DVD protection, null on error</returns>
|
||||
private static string GetDVDProtection(CICMMetadataType cicmSidecar)
|
||||
{
|
||||
// If the object is null, we can't get information from it
|
||||
if (cicmSidecar == null)
|
||||
return null;
|
||||
|
||||
// Only care about OpticalDisc types
|
||||
if (cicmSidecar.OpticalDisc == null || cicmSidecar.OpticalDisc.Length == 0)
|
||||
return null;
|
||||
|
||||
// Get an output for the copyright protection
|
||||
string copyrightProtectionSystemType = string.Empty;
|
||||
|
||||
// Loop through each OpticalDisc in the metadata
|
||||
foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(opticalDisc.CopyProtection))
|
||||
copyrightProtectionSystemType += $", {opticalDisc.CopyProtection}";
|
||||
}
|
||||
|
||||
// Trim the values
|
||||
copyrightProtectionSystemType = copyrightProtectionSystemType.TrimStart(',').Trim();
|
||||
|
||||
// TODO: Note- Most of the below values are not currently captured by Aaru.
|
||||
// At the time of writing, there are open issues to capture more of this
|
||||
// information and store it in the output. For now, only the copyright
|
||||
// protection system can be retrieved.
|
||||
|
||||
// Now we format everything we can
|
||||
string protection = string.Empty;
|
||||
//if (!string.IsNullOrEmpty(region))
|
||||
// protection += $"Region: {region}\n";
|
||||
//if (!string.IsNullOrEmpty(rceProtection))
|
||||
// protection += $"RCE Protection: {rceProtection}\n";
|
||||
if (!string.IsNullOrEmpty(copyrightProtectionSystemType))
|
||||
protection += $"Copyright Protection System Type: {copyrightProtectionSystemType}\n";
|
||||
//if (!string.IsNullOrEmpty(vobKeys))
|
||||
// protection += vobKeys;
|
||||
//if (!string.IsNullOrEmpty(decryptedDiscKey))
|
||||
// protection += $"Decrypted Disc Key: {decryptedDiscKey}\n";
|
||||
|
||||
return protection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the detected error count from the input files, if possible
|
||||
/// </summary>
|
||||
/// <param name="resume">.resume.xml file location</param>
|
||||
/// <returns>Error count if possible, -1 on error</returns>
|
||||
private static long GetErrorCount(string resume)
|
||||
{
|
||||
// If the file doesn't exist, we can't get info from it
|
||||
if (!File.Exists(resume))
|
||||
return -1;
|
||||
|
||||
// Get a total error count for after
|
||||
long? totalErrors = null;
|
||||
|
||||
// Parse the resume XML file
|
||||
using (StreamReader sr = File.OpenText(resume))
|
||||
{
|
||||
try
|
||||
{
|
||||
// Read in the error count whenever we find it
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
string line = sr.ReadLine().Trim();
|
||||
|
||||
// Initialize on seeing the open tag
|
||||
if (line.StartsWith("<BadBlocks>"))
|
||||
totalErrors = 0;
|
||||
else if (line.StartsWith("</BadBlocks>"))
|
||||
return totalErrors ?? -1;
|
||||
else if (line.StartsWith("<Block>") && totalErrors != null)
|
||||
totalErrors++;
|
||||
}
|
||||
|
||||
// If we haven't found anything, return -1
|
||||
return totalErrors ?? -1;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the exception is right now
|
||||
return Int64.MaxValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the layerbreak from the input file, if possible
|
||||
/// </summary>
|
||||
/// <param name="cicmSidecar">CICM Sidecar data generated by Aaru</param>
|
||||
/// <returns>Layerbreak if possible, null on error</returns>
|
||||
private static string GetLayerbreak(CICMMetadataType cicmSidecar)
|
||||
{
|
||||
// If the object is null, we can't get information from it
|
||||
if (cicmSidecar == null)
|
||||
return null;
|
||||
|
||||
// Only care about OpticalDisc types
|
||||
if (cicmSidecar.OpticalDisc == null || cicmSidecar.OpticalDisc.Length == 0)
|
||||
return null;
|
||||
|
||||
// Setup the layerbreak
|
||||
string layerbreak = null;
|
||||
|
||||
// Find and return the layerbreak, if possible
|
||||
foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc)
|
||||
{
|
||||
// If there's no layer information, skip
|
||||
if (opticalDisc.Layers == null)
|
||||
continue;
|
||||
|
||||
// TODO: Determine how to find the layerbreak from the CICM or other outputs
|
||||
}
|
||||
|
||||
return layerbreak;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the write offset from the CICM Sidecar file, if possible
|
||||
/// </summary>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
201
DICUI.Library/CueSheets/CueFile.cs
Normal file
201
DICUI.Library/CueSheets/CueFile.cs
Normal file
@@ -0,0 +1,201 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
/// <remarks>
|
||||
/// Information sourced from http://web.archive.org/web/20070221154246/http://www.goldenhawk.com/download/cdrwin.pdf
|
||||
/// </remarks>
|
||||
namespace DICUI.CueSheets
|
||||
{
|
||||
/// <summary>
|
||||
/// The audio or data file’s filetype
|
||||
/// </summary>
|
||||
public enum CueFileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Intel binary file (least significant byte first). Use for data files.
|
||||
/// </summary>
|
||||
BINARY,
|
||||
|
||||
/// <summary>
|
||||
/// Motorola binary file (most significant byte first). Use for data files.
|
||||
/// </summary>
|
||||
MOTOROLA,
|
||||
|
||||
/// <summary>
|
||||
/// Audio AIFF file (44.1KHz 16-bit stereo)
|
||||
/// </summary>
|
||||
AIFF,
|
||||
|
||||
/// <summary>
|
||||
/// Audio WAVE file (44.1KHz 16-bit stereo)
|
||||
/// </summary>
|
||||
WAVE,
|
||||
|
||||
/// <summary>
|
||||
/// Audio MP3 file (44.1KHz 16-bit stereo)
|
||||
/// </summary>
|
||||
MP3,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a single FILE in a cuesheet
|
||||
/// </summary>
|
||||
public class CueFile
|
||||
{
|
||||
/// <summary>
|
||||
/// filename
|
||||
/// </summary>
|
||||
public string FileName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// filetype
|
||||
/// </summary>
|
||||
public CueFileType FileType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of TRACK in FILE
|
||||
/// </summary>
|
||||
public List<CueTrack> Tracks { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create an empty FILE
|
||||
/// </summary>
|
||||
public CueFile()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fill a FILE from an array of lines
|
||||
/// </summary>
|
||||
/// <param name="fileName">File name to set</param>
|
||||
/// <param name="fileType">File type to set</param>
|
||||
/// <param name="cueLines">Lines array to pull from</param>
|
||||
/// <param name="i">Reference to index in array</param>
|
||||
public CueFile(string fileName, string fileType, string[] cueLines, ref int i)
|
||||
{
|
||||
if (cueLines == null || i < 0 || i > cueLines.Length)
|
||||
return; // TODO: Make this throw an exception
|
||||
|
||||
// Set the current fields
|
||||
this.FileName = fileName.Trim('"');
|
||||
this.FileType = GetFileType(fileType);
|
||||
|
||||
// Increment to start
|
||||
i++;
|
||||
|
||||
for (; i < cueLines.Length; i++)
|
||||
{
|
||||
string line = cueLines[i].Trim();
|
||||
string[] splitLine = line.Split(' ');
|
||||
|
||||
// If we have an empty line, we skip
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
continue;
|
||||
|
||||
switch (splitLine[0])
|
||||
{
|
||||
// Read comments
|
||||
case "REM":
|
||||
// We ignore all comments for now
|
||||
break;
|
||||
|
||||
// Read track information
|
||||
case "TRACK":
|
||||
if (splitLine.Length < 3)
|
||||
continue; // TODO: Make this throw an exception
|
||||
|
||||
if (this.Tracks == null)
|
||||
this.Tracks = new List<CueTrack>();
|
||||
|
||||
var track = new CueTrack(splitLine[1], splitLine[2], cueLines, ref i);
|
||||
if (track == default)
|
||||
continue; // TODO: Make this throw an exception
|
||||
|
||||
this.Tracks.Add(track);
|
||||
break;
|
||||
|
||||
// Default means return
|
||||
default:
|
||||
i--;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the FILE out to a stream
|
||||
/// </summary>
|
||||
/// <param name="sw">StreamWriter to write to</param>
|
||||
public void Write(StreamWriter sw)
|
||||
{
|
||||
// If we don't have any tracks, it's invalid
|
||||
if (this.Tracks == null || this.Tracks.Count == 0)
|
||||
return; // TODO: Make this throw an exception
|
||||
|
||||
sw.WriteLine($"FILE \"{this.FileName}\" {FromFileType(this.FileType)}");
|
||||
|
||||
foreach (var track in Tracks)
|
||||
{
|
||||
track.Write(sw);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the file type from a given string
|
||||
/// </summary>
|
||||
/// <param name="fileType">String to get value from</param>
|
||||
/// <returns>CueFileType, if possible</returns>
|
||||
private CueFileType GetFileType(string fileType)
|
||||
{
|
||||
switch (fileType.ToLowerInvariant())
|
||||
{
|
||||
case "binary":
|
||||
return CueFileType.BINARY;
|
||||
|
||||
case "motorola":
|
||||
return CueFileType.MOTOROLA;
|
||||
|
||||
case "aiff":
|
||||
return CueFileType.AIFF;
|
||||
|
||||
case "wave":
|
||||
return CueFileType.WAVE;
|
||||
|
||||
case "mp3":
|
||||
return CueFileType.MP3;
|
||||
|
||||
default:
|
||||
return CueFileType.BINARY;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the string from a given file type
|
||||
/// </summary>
|
||||
/// <param name="fileType">CueFileType to get value from</param>
|
||||
/// <returns>String, if possible (default BINARY)</returns>
|
||||
private string FromFileType(CueFileType fileType)
|
||||
{
|
||||
switch (fileType)
|
||||
{
|
||||
case CueFileType.BINARY:
|
||||
return "BINARY";
|
||||
|
||||
case CueFileType.MOTOROLA:
|
||||
return "MOTOROLA";
|
||||
|
||||
case CueFileType.AIFF:
|
||||
return "AIFF";
|
||||
|
||||
case CueFileType.WAVE:
|
||||
return "WAVE";
|
||||
|
||||
case CueFileType.MP3:
|
||||
return "MP3";
|
||||
|
||||
default:
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
106
DICUI.Library/CueSheets/CueIndex.cs
Normal file
106
DICUI.Library/CueSheets/CueIndex.cs
Normal file
@@ -0,0 +1,106 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
/// <remarks>
|
||||
/// Information sourced from http://web.archive.org/web/20070221154246/http://www.goldenhawk.com/download/cdrwin.pdf
|
||||
/// </remarks>
|
||||
namespace DICUI.CueSheets
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a single INDEX in a TRACK
|
||||
/// </summary>
|
||||
public class CueIndex
|
||||
{
|
||||
/// <summary>
|
||||
/// INDEX number, between 0 and 99
|
||||
/// </summary>
|
||||
public int Index { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Starting time of INDEX in minutes
|
||||
/// </summary>
|
||||
public int Minutes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Starting time of INDEX in seconds
|
||||
/// </summary>
|
||||
/// <remarks>There are 60 seconds in a minute</remarks>
|
||||
public int Seconds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Starting time of INDEX in frames.
|
||||
/// </summary>
|
||||
/// <remarks>There are 75 frames per second</remarks>
|
||||
public int Frames { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create an empty INDEX
|
||||
/// </summary>
|
||||
public CueIndex()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fill a INDEX from an array of lines
|
||||
/// </summary>
|
||||
/// <param name="index">Index to set</param>
|
||||
/// <param name="startTime">Start time to set</param>
|
||||
public CueIndex(string index, string startTime)
|
||||
{
|
||||
// Set the current fields
|
||||
if (!int.TryParse(index, out int parsedIndex))
|
||||
return; // TODO: Make this throw an exception
|
||||
else if (parsedIndex < 0 || parsedIndex > 99)
|
||||
return; // TODO: Make this throw an exception
|
||||
|
||||
// Ignore empty lines
|
||||
if (string.IsNullOrWhiteSpace(startTime))
|
||||
return; // TODO: Make this throw an exception
|
||||
|
||||
// Ignore lines that don't contain the correct information
|
||||
if (startTime.Length != 8 || startTime.Count(c => c == ':') != 2)
|
||||
return; // TODO: Make this throw an exception
|
||||
|
||||
// Split the line
|
||||
string[] splitTime = startTime.Split(':');
|
||||
if (splitTime.Length != 3)
|
||||
return; // TODO: Make this throw an exception
|
||||
|
||||
// Parse the lengths
|
||||
int[] lengthSegments = new int[3];
|
||||
|
||||
// Minutes
|
||||
if (!int.TryParse(splitTime[0], out lengthSegments[0]))
|
||||
return; // TODO: Make this throw an exception
|
||||
else if (lengthSegments[0] < 0)
|
||||
return; // TODO: Make this throw an exception
|
||||
|
||||
// Seconds
|
||||
if (!int.TryParse(splitTime[1], out lengthSegments[1]))
|
||||
return; // TODO: Make this throw an exception
|
||||
else if (lengthSegments[1] < 0 || lengthSegments[1] > 60)
|
||||
return; // TODO: Make this throw an exception
|
||||
|
||||
// Frames
|
||||
if (!int.TryParse(splitTime[2], out lengthSegments[2]))
|
||||
return; // TODO: Make this throw an exception
|
||||
else if (lengthSegments[2] < 0 || lengthSegments[2] > 75)
|
||||
return; // TODO: Make this throw an exception
|
||||
|
||||
// Set the values
|
||||
this.Index = parsedIndex;
|
||||
this.Minutes = lengthSegments[0];
|
||||
this.Seconds = lengthSegments[1];
|
||||
this.Frames = lengthSegments[2];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the INDEX out to a stream
|
||||
/// </summary>
|
||||
/// <param name="sw">StreamWriter to write to</param>
|
||||
public void Write(StreamWriter sw)
|
||||
{
|
||||
sw.WriteLine($" INDEX {this.Index:D2} {this.Minutes:D2}:{this.Seconds:D2}:{this.Frames:D2}");
|
||||
}
|
||||
}
|
||||
}
|
||||
201
DICUI.Library/CueSheets/CueSheet.cs
Normal file
201
DICUI.Library/CueSheets/CueSheet.cs
Normal file
@@ -0,0 +1,201 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
/// <remarks>
|
||||
/// Information sourced from http://web.archive.org/web/20070221154246/http://www.goldenhawk.com/download/cdrwin.pdf
|
||||
/// </remarks>
|
||||
namespace DICUI.CueSheets
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a single cuesheet
|
||||
/// </summary>
|
||||
public class CueSheet
|
||||
{
|
||||
/// <summary>
|
||||
/// CATALOG
|
||||
/// </summary>
|
||||
public string Catalog { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// CDTEXTFILE
|
||||
/// </summary>
|
||||
public string CdTextFile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// PERFORMER
|
||||
/// </summary>
|
||||
public string Performer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SONGWRITER
|
||||
/// </summary>
|
||||
public string Songwriter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// TITLE
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of FILE in cuesheet
|
||||
/// </summary>
|
||||
public List<CueFile> Files { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create an empty cuesheet
|
||||
/// </summary>
|
||||
public CueSheet()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a cuesheet from a file, if possible
|
||||
/// </summary>
|
||||
/// <param name="filename"></param>
|
||||
public CueSheet(string filename)
|
||||
{
|
||||
// Check that the file exists
|
||||
if (!File.Exists(filename))
|
||||
return;
|
||||
|
||||
// Check the extension
|
||||
string ext = Path.GetExtension(filename).TrimStart('.');
|
||||
if (!string.Equals(ext, "cue", StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.Equals(ext, "txt", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Open the file and begin reading
|
||||
string[] cueLines = File.ReadAllLines(filename);
|
||||
for (int i = 0; i < cueLines.Length; i++)
|
||||
{
|
||||
string line = cueLines[i].Trim();
|
||||
|
||||
// http://stackoverflow.com/questions/554013/regular-expression-to-split-on-spaces-unless-in-quotes
|
||||
string[] splitLine = Regex
|
||||
.Matches(line, @"[^\s""]+|""[^""]*""")
|
||||
.Cast<Match>()
|
||||
.Select(m => m.Groups[0].Value)
|
||||
.ToArray();
|
||||
|
||||
// If we have an empty line, we skip
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
continue;
|
||||
|
||||
switch (splitLine[0])
|
||||
{
|
||||
// Read comments
|
||||
case "REM":
|
||||
// We ignore all comments for now
|
||||
break;
|
||||
|
||||
// Read MCN
|
||||
case "CATALOG":
|
||||
if (splitLine.Length < 2)
|
||||
continue; // TODO: Make this throw an exception
|
||||
|
||||
this.Catalog = splitLine[1];
|
||||
break;
|
||||
|
||||
// Read external CD-Text file path
|
||||
case "CDTEXTFILE":
|
||||
if (splitLine.Length < 2)
|
||||
continue; // TODO: Make this throw an exception
|
||||
|
||||
this.CdTextFile = splitLine[1];
|
||||
break;
|
||||
|
||||
// Read CD-Text enhanced performer
|
||||
case "PERFORMER":
|
||||
if (splitLine.Length < 2)
|
||||
continue; // TODO: Make this throw an exception
|
||||
|
||||
this.Performer = splitLine[1];
|
||||
break;
|
||||
|
||||
// Read CD-Text enhanced songwriter
|
||||
case "SONGWRITER":
|
||||
if (splitLine.Length < 2)
|
||||
continue; // TODO: Make this throw an exception
|
||||
|
||||
this.Songwriter = splitLine[1];
|
||||
break;
|
||||
|
||||
// Read CD-Text enhanced title
|
||||
case "TITLE":
|
||||
if (splitLine.Length < 2)
|
||||
continue; // TODO: Make this throw an exception
|
||||
|
||||
this.Title = splitLine[1];
|
||||
break;
|
||||
|
||||
// Read file information
|
||||
case "FILE":
|
||||
if (splitLine.Length < 3)
|
||||
continue; // TODO: Make this throw an exception
|
||||
|
||||
if (this.Files == null)
|
||||
this.Files = new List<CueFile>();
|
||||
|
||||
var file = new CueFile(splitLine[1], splitLine[2], cueLines, ref i);
|
||||
if (file == default)
|
||||
continue; // TODO: Make this throw an exception
|
||||
|
||||
this.Files.Add(file);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the cuesheet out to a file
|
||||
/// </summary>
|
||||
/// <param name="filename">File path to write to</param>
|
||||
public void Write(string filename)
|
||||
{
|
||||
using (var fs = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
|
||||
{
|
||||
Write(fs);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the cuesheet out to a stream
|
||||
/// </summary>
|
||||
/// <param name="stream">Stream to write to</param>
|
||||
public void Write(Stream stream)
|
||||
{
|
||||
// If we don't have any files, it's invalid
|
||||
if (this.Files == null || this.Files.Count == 0)
|
||||
return; // TODO: Make this throw an exception
|
||||
|
||||
using (var sw = new StreamWriter(stream, Encoding.ASCII, 1024, true))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(this.Catalog))
|
||||
sw.WriteLine($"CATALOG {this.Catalog}");
|
||||
|
||||
if (!string.IsNullOrEmpty(this.CdTextFile))
|
||||
sw.WriteLine($"CDTEXTFILE {this.CdTextFile}");
|
||||
|
||||
if (!string.IsNullOrEmpty(this.Performer))
|
||||
sw.WriteLine($"PERFORMER {this.Performer}");
|
||||
|
||||
if (!string.IsNullOrEmpty(this.Songwriter))
|
||||
sw.WriteLine($"SONGWRITER {this.Songwriter}");
|
||||
|
||||
if (!string.IsNullOrEmpty(this.Title))
|
||||
sw.WriteLine($"TITLE {this.Title}");
|
||||
|
||||
foreach (var file in Files)
|
||||
{
|
||||
file.Write(sw);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
464
DICUI.Library/CueSheets/CueTrack.cs
Normal file
464
DICUI.Library/CueSheets/CueTrack.cs
Normal file
@@ -0,0 +1,464 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
/// <remarks>
|
||||
/// Information sourced from http://web.archive.org/web/20070221154246/http://www.goldenhawk.com/download/cdrwin.pdf
|
||||
/// </remarks>
|
||||
namespace DICUI.CueSheets
|
||||
{
|
||||
/// <summary>
|
||||
/// Track datatype
|
||||
/// </summary>
|
||||
public enum CueTrackDataType
|
||||
{
|
||||
/// <summary>
|
||||
/// AUDIO, Audio/Music (2352)
|
||||
/// </summary>
|
||||
AUDIO,
|
||||
|
||||
/// <summary>
|
||||
/// CDG, Karaoke CD+G (2448)
|
||||
/// </summary>
|
||||
CDG,
|
||||
|
||||
/// <summary>
|
||||
/// MODE1/2048, CD-ROM Mode1 Data (cooked)
|
||||
/// </summary>
|
||||
MODE1_2048,
|
||||
|
||||
/// <summary>
|
||||
/// MODE1/2352 CD-ROM Mode1 Data (raw)
|
||||
/// </summary>
|
||||
MODE1_2352,
|
||||
|
||||
/// <summary>
|
||||
/// MODE2/2336, CD-ROM XA Mode2 Data
|
||||
/// </summary>
|
||||
MODE2_2336,
|
||||
|
||||
/// <summary>
|
||||
/// MODE2/2352, CD-ROM XA Mode2 Data
|
||||
/// </summary>
|
||||
MODE2_2352,
|
||||
|
||||
/// <summary>
|
||||
/// CDI/2336, CD-I Mode2 Data
|
||||
/// </summary>
|
||||
CDI_2336,
|
||||
|
||||
/// <summary>
|
||||
/// CDI/2352, CD-I Mode2 Data
|
||||
/// </summary>
|
||||
CDI_2352,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Special subcode flags within a track
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum CueTrackFlag
|
||||
{
|
||||
/// <summary>
|
||||
/// DCP, Digital copy permitted
|
||||
/// </summary>
|
||||
DCP = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// 4CH, Four channel audio
|
||||
/// </summary>
|
||||
FourCH = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// PRE, Pre-emphasis enabled (audio tracks only)
|
||||
/// </summary>
|
||||
PRE = 1 << 2,
|
||||
|
||||
/// <summary>
|
||||
/// SCMS, Serial Copy Management System (not supported by all recorders)
|
||||
/// </summary>
|
||||
SCMS = 1 << 3,
|
||||
|
||||
/// <summary>
|
||||
/// DATA, set for data files. This flag is set automatically based on the track’s filetype
|
||||
/// </summary>
|
||||
DATA = 1 << 4,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a single TRACK in a FILE
|
||||
/// </summary>
|
||||
public class CueTrack
|
||||
{
|
||||
/// <summary>
|
||||
/// Track number. The range is 1 to 99.
|
||||
/// </summary>
|
||||
public int Number { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Track datatype
|
||||
/// </summary>
|
||||
public CueTrackDataType DataType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// FLAGS
|
||||
/// </summary>
|
||||
public CueTrackFlag Flags { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ISRC
|
||||
/// </summary>
|
||||
/// <remarks>12 characters in length</remarks>
|
||||
public string ISRC { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// PERFORMER
|
||||
/// </summary>
|
||||
public string Performer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SONGWRITER
|
||||
/// </summary>
|
||||
public string Songwriter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// TITLE
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// PREGAP
|
||||
/// </summary>
|
||||
public PreGap PreGap { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of INDEX in TRACK
|
||||
/// </summary>
|
||||
/// <remarks>Must start with 0 or 1 and then sequential</remarks>
|
||||
public List<CueIndex> Indices { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// POSTGAP
|
||||
/// </summary>
|
||||
public PostGap PostGap { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create an empty TRACK
|
||||
/// </summary>
|
||||
public CueTrack()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fill a TRACK from an array of lines
|
||||
/// </summary>
|
||||
/// <param name="number">Number to set</param>
|
||||
/// <param name="dataType">Data type to set</param>
|
||||
/// <param name="cueLines">Lines array to pull from</param>
|
||||
/// <param name="i">Reference to index in array</param>
|
||||
public CueTrack(string number, string dataType, string[] cueLines, ref int i)
|
||||
{
|
||||
if (cueLines == null || i < 0 || i > cueLines.Length)
|
||||
return; // TODO: Make this throw an exception
|
||||
|
||||
// Set the current fields
|
||||
if (!int.TryParse(number, out int parsedNumber))
|
||||
return; // TODO: Make this throw an exception
|
||||
else if (parsedNumber < 1 || parsedNumber > 99)
|
||||
return; // TODO: Make this throw an exception
|
||||
|
||||
this.Number = parsedNumber;
|
||||
this.DataType = GetDataType(dataType);
|
||||
|
||||
// Increment to start
|
||||
i++;
|
||||
|
||||
for (; i < cueLines.Length; i++)
|
||||
{
|
||||
string line = cueLines[i].Trim();
|
||||
string[] splitLine = line.Split(' ');
|
||||
|
||||
// If we have an empty line, we skip
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
continue;
|
||||
|
||||
switch (splitLine[0])
|
||||
{
|
||||
// Read comments
|
||||
case "REM":
|
||||
// We ignore all comments for now
|
||||
break;
|
||||
|
||||
// Read flag information
|
||||
case "FLAGS":
|
||||
if (splitLine.Length < 2)
|
||||
continue; // TODO: Make this throw an exception
|
||||
|
||||
this.Flags = GetFlags(splitLine);
|
||||
break;
|
||||
|
||||
// Read International Standard Recording Code
|
||||
case "ISRC":
|
||||
if (splitLine.Length < 2)
|
||||
continue; // TODO: Make this throw an exception
|
||||
|
||||
this.ISRC = splitLine[1];
|
||||
break;
|
||||
|
||||
// Read CD-Text enhanced performer
|
||||
case "PERFORMER":
|
||||
if (splitLine.Length < 2)
|
||||
continue; // TODO: Make this throw an exception
|
||||
|
||||
this.Performer = splitLine[1];
|
||||
break;
|
||||
|
||||
// Read CD-Text enhanced songwriter
|
||||
case "SONGWRITER":
|
||||
if (splitLine.Length < 2)
|
||||
continue; // TODO: Make this throw an exception
|
||||
|
||||
this.Songwriter = splitLine[1];
|
||||
break;
|
||||
|
||||
// Read CD-Text enhanced title
|
||||
case "TITLE":
|
||||
if (splitLine.Length < 2)
|
||||
continue; // TODO: Make this throw an exception
|
||||
|
||||
this.Title = splitLine[1];
|
||||
break;
|
||||
|
||||
// Read pregap information
|
||||
case "PREGAP":
|
||||
if (splitLine.Length < 2)
|
||||
continue; // TODO: Make this throw an exception
|
||||
|
||||
var pregap = new PreGap(splitLine[1]);
|
||||
if (pregap == default)
|
||||
continue; // TODO: Make this throw an exception
|
||||
|
||||
this.PreGap = pregap;
|
||||
break;
|
||||
|
||||
// Read index information
|
||||
case "INDEX":
|
||||
if (splitLine.Length < 3)
|
||||
continue; // TODO: Make this throw an exception
|
||||
|
||||
if (this.Indices == null)
|
||||
this.Indices = new List<CueIndex>();
|
||||
|
||||
var index = new CueIndex(splitLine[1], splitLine[2]);
|
||||
if (index == default)
|
||||
continue; // TODO: Make this throw an exception
|
||||
|
||||
this.Indices.Add(index);
|
||||
break;
|
||||
|
||||
// Read postgap information
|
||||
case "POSTGAP":
|
||||
if (splitLine.Length < 2)
|
||||
continue; // TODO: Make this throw an exception
|
||||
|
||||
var postgap = new PostGap(splitLine[1]);
|
||||
if (postgap == default)
|
||||
continue; // TODO: Make this throw an exception
|
||||
|
||||
this.PostGap = postgap;
|
||||
break;
|
||||
|
||||
// Default means return
|
||||
default:
|
||||
i--;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the TRACK out to a stream
|
||||
/// </summary>
|
||||
/// <param name="sw">StreamWriter to write to</param>
|
||||
public void Write(StreamWriter sw)
|
||||
{
|
||||
// If we don't have any indices, it's invalid
|
||||
if (this.Indices == null || this.Indices.Count == 0)
|
||||
return; // TODO: Make this throw an exception
|
||||
|
||||
sw.WriteLine($" TRACK {this.Number:D2} {FromDataType(this.DataType)}");
|
||||
|
||||
if (this.Flags != 0)
|
||||
sw.WriteLine($" FLAGS {FromFlags(this.Flags)}");
|
||||
|
||||
if (!string.IsNullOrEmpty(this.ISRC))
|
||||
sw.WriteLine($"ISRC {this.ISRC}");
|
||||
|
||||
if (!string.IsNullOrEmpty(this.Performer))
|
||||
sw.WriteLine($"PERFORMER {this.Performer}");
|
||||
|
||||
if (!string.IsNullOrEmpty(this.Songwriter))
|
||||
sw.WriteLine($"SONGWRITER {this.Songwriter}");
|
||||
|
||||
if (!string.IsNullOrEmpty(this.Title))
|
||||
sw.WriteLine($"TITLE {this.Title}");
|
||||
|
||||
if (this.PreGap != null)
|
||||
this.PreGap.Write(sw);
|
||||
|
||||
foreach (var index in Indices)
|
||||
{
|
||||
index.Write(sw);
|
||||
}
|
||||
|
||||
if (this.PostGap != null)
|
||||
this.PostGap.Write(sw);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the data type from a given string
|
||||
/// </summary>
|
||||
/// <param name="dataType">String to get value from</param>
|
||||
/// <returns>CueTrackDataType, if possible (default AUDIO)</returns>
|
||||
private CueTrackDataType GetDataType(string dataType)
|
||||
{
|
||||
switch (dataType.ToLowerInvariant())
|
||||
{
|
||||
case "audio":
|
||||
return CueTrackDataType.AUDIO;
|
||||
|
||||
case "cdg":
|
||||
return CueTrackDataType.CDG;
|
||||
|
||||
case "mode1/2048":
|
||||
return CueTrackDataType.MODE1_2048;
|
||||
|
||||
case "mode1/2352":
|
||||
return CueTrackDataType.MODE1_2352;
|
||||
|
||||
case "mode2/2336":
|
||||
return CueTrackDataType.MODE2_2336;
|
||||
|
||||
case "mode2/2352":
|
||||
return CueTrackDataType.MODE2_2352;
|
||||
|
||||
case "cdi/2336":
|
||||
return CueTrackDataType.CDI_2336;
|
||||
|
||||
case "cdi/2352":
|
||||
return CueTrackDataType.CDI_2352;
|
||||
|
||||
default:
|
||||
return CueTrackDataType.AUDIO;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the string from a given data type
|
||||
/// </summary>
|
||||
/// <param name="dataType">CueTrackDataType to get value from</param>
|
||||
/// <returns>string, if possible</returns>
|
||||
private string FromDataType(CueTrackDataType dataType)
|
||||
{
|
||||
switch (dataType)
|
||||
{
|
||||
case CueTrackDataType.AUDIO:
|
||||
return "AUDIO";
|
||||
|
||||
case CueTrackDataType.CDG:
|
||||
return "CDG";
|
||||
|
||||
case CueTrackDataType.MODE1_2048:
|
||||
return "MODE1/2048";
|
||||
|
||||
case CueTrackDataType.MODE1_2352:
|
||||
return "MODE1/2352";
|
||||
|
||||
case CueTrackDataType.MODE2_2336:
|
||||
return "MODE2/2336";
|
||||
|
||||
case CueTrackDataType.MODE2_2352:
|
||||
return "MODE2/2352";
|
||||
|
||||
case CueTrackDataType.CDI_2336:
|
||||
return "CDI/2336";
|
||||
|
||||
case CueTrackDataType.CDI_2352:
|
||||
return "CDI/2352";
|
||||
|
||||
default:
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the flag value for an array of strings
|
||||
/// </summary>
|
||||
/// <param name="flagStrings">Possible flags as strings</param>
|
||||
/// <returns>CueTrackFlag value representing the strings, if possible</returns>
|
||||
private CueTrackFlag GetFlags(string[] flagStrings)
|
||||
{
|
||||
CueTrackFlag flag = 0;
|
||||
|
||||
foreach (string flagString in flagStrings)
|
||||
{
|
||||
// TODO: Make default throw an exception
|
||||
switch (flagString.ToLowerInvariant())
|
||||
{
|
||||
case "flags":
|
||||
// No-op since this is the start of the line
|
||||
break;
|
||||
|
||||
case "dcp":
|
||||
flag |= CueTrackFlag.DCP;
|
||||
break;
|
||||
|
||||
case "4ch":
|
||||
flag |= CueTrackFlag.FourCH;
|
||||
break;
|
||||
|
||||
case "pre":
|
||||
flag |= CueTrackFlag.PRE;
|
||||
break;
|
||||
|
||||
case "scms":
|
||||
flag |= CueTrackFlag.SCMS;
|
||||
break;
|
||||
|
||||
case "data":
|
||||
flag |= CueTrackFlag.DATA;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the string value for a set of track flags
|
||||
/// </summary>
|
||||
/// <param name="flags">CueTrackFlag to get value from</param>
|
||||
/// <returns>String value representing the CueTrackFlag, if possible</returns>
|
||||
private string FromFlags(CueTrackFlag flags)
|
||||
{
|
||||
string outputFlagString = string.Empty;
|
||||
|
||||
if (flags.HasFlag(CueTrackFlag.DCP))
|
||||
outputFlagString += "DCP ";
|
||||
|
||||
if (flags.HasFlag(CueTrackFlag.FourCH))
|
||||
outputFlagString += "4CH ";
|
||||
|
||||
if (flags.HasFlag(CueTrackFlag.PRE))
|
||||
outputFlagString += "PRE ";
|
||||
|
||||
if (flags.HasFlag(CueTrackFlag.SCMS))
|
||||
outputFlagString += "SCMS ";
|
||||
|
||||
if (flags.HasFlag(CueTrackFlag.DATA))
|
||||
outputFlagString += "DATA ";
|
||||
|
||||
return outputFlagString.Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
92
DICUI.Library/CueSheets/PostGap.cs
Normal file
92
DICUI.Library/CueSheets/PostGap.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
/// <remarks>
|
||||
/// Information sourced from http://web.archive.org/web/20070221154246/http://www.goldenhawk.com/download/cdrwin.pdf
|
||||
/// </remarks>
|
||||
namespace DICUI.CueSheets
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents POSTGAP information of a track
|
||||
/// </summary>
|
||||
public class PostGap
|
||||
{
|
||||
/// <summary>
|
||||
/// Length of POSTGAP in minutes
|
||||
/// </summary>
|
||||
public int Minutes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Length of POSTGAP in seconds
|
||||
/// </summary>
|
||||
/// <remarks>There are 60 seconds in a minute</remarks>
|
||||
public int Seconds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Length of POSTGAP in frames.
|
||||
/// </summary>
|
||||
/// <remarks>There are 75 frames per second</remarks>
|
||||
public int Frames { get; set; }
|
||||
|
||||
/// Create an empty POSTGAP
|
||||
/// </summary>
|
||||
public PostGap()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a POSTGAP from a mm:ss:ff length
|
||||
/// </summary>
|
||||
/// <param name="length">String to get length information from</param>
|
||||
public PostGap(string length)
|
||||
{
|
||||
// Ignore empty lines
|
||||
if (string.IsNullOrWhiteSpace(length))
|
||||
return; // TODO: Make this throw an exception
|
||||
|
||||
// Ignore lines that don't contain the correct information
|
||||
if (length.Length != 8 || length.Count(c => c == ':') != 2)
|
||||
return; // TODO: Make this throw an exception
|
||||
|
||||
// Split the line
|
||||
string[] splitLength = length.Split(':');
|
||||
if (splitLength.Length != 3)
|
||||
return; // TODO: Make this throw an exception
|
||||
|
||||
// Parse the lengths
|
||||
int[] lengthSegments = new int[3];
|
||||
|
||||
// Minutes
|
||||
if (!int.TryParse(splitLength[0], out lengthSegments[0]))
|
||||
return; // TODO: Make this throw an exception
|
||||
else if (lengthSegments[0] < 0)
|
||||
return; // TODO: Make this throw an exception
|
||||
|
||||
// Seconds
|
||||
if (!int.TryParse(splitLength[1], out lengthSegments[1]))
|
||||
return; // TODO: Make this throw an exception
|
||||
else if (lengthSegments[1] < 0 || lengthSegments[1] > 60)
|
||||
return; // TODO: Make this throw an exception
|
||||
|
||||
// Frames
|
||||
if (!int.TryParse(splitLength[2], out lengthSegments[2]))
|
||||
return; // TODO: Make this throw an exception
|
||||
else if (lengthSegments[2] < 0 || lengthSegments[2] > 75)
|
||||
return; // TODO: Make this throw an exception
|
||||
|
||||
// Set the values
|
||||
this.Minutes = lengthSegments[0];
|
||||
this.Seconds = lengthSegments[1];
|
||||
this.Frames = lengthSegments[2];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the POSTGAP out to a stream
|
||||
/// </summary>
|
||||
/// <param name="sw">StreamWriter to write to</param>
|
||||
public void Write(StreamWriter sw)
|
||||
{
|
||||
sw.WriteLine($" POSTGAP {this.Minutes:D2}:{this.Seconds:D2}:{this.Frames:D2}");
|
||||
}
|
||||
}
|
||||
}
|
||||
93
DICUI.Library/CueSheets/PreGap.cs
Normal file
93
DICUI.Library/CueSheets/PreGap.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
/// <remarks>
|
||||
/// Information sourced from http://web.archive.org/web/20070221154246/http://www.goldenhawk.com/download/cdrwin.pdf
|
||||
/// </remarks>
|
||||
namespace DICUI.CueSheets
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents PREGAP information of a track
|
||||
/// </summary>
|
||||
public class PreGap
|
||||
{
|
||||
/// <summary>
|
||||
/// Length of PREGAP in minutes
|
||||
/// </summary>
|
||||
public int Minutes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Length of PREGAP in seconds
|
||||
/// </summary>
|
||||
/// <remarks>There are 60 seconds in a minute</remarks>
|
||||
public int Seconds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Length of PREGAP in frames.
|
||||
/// </summary>
|
||||
/// <remarks>There are 75 frames per second</remarks>
|
||||
public int Frames { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create an empty PREGAP
|
||||
/// </summary>
|
||||
public PreGap()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a PREGAP from a mm:ss:ff length
|
||||
/// </summary>
|
||||
/// <param name="length">String to get length information from</param>
|
||||
public PreGap(string length)
|
||||
{
|
||||
// Ignore empty lines
|
||||
if (string.IsNullOrWhiteSpace(length))
|
||||
return; // TODO: Make this throw an exception
|
||||
|
||||
// Ignore lines that don't contain the correct information
|
||||
if (length.Length != 8 || length.Count(c => c == ':') != 2)
|
||||
return; // TODO: Make this throw an exception
|
||||
|
||||
// Split the line
|
||||
string[] splitLength = length.Split(':');
|
||||
if (splitLength.Length != 3)
|
||||
return; // TODO: Make this throw an exception
|
||||
|
||||
// Parse the lengths
|
||||
int[] lengthSegments = new int[3];
|
||||
|
||||
// Minutes
|
||||
if (!int.TryParse(splitLength[0], out lengthSegments[0]))
|
||||
return; // TODO: Make this throw an exception
|
||||
else if (lengthSegments[0] < 0)
|
||||
return; // TODO: Make this throw an exception
|
||||
|
||||
// Seconds
|
||||
if (!int.TryParse(splitLength[1], out lengthSegments[1]))
|
||||
return; // TODO: Make this throw an exception
|
||||
else if (lengthSegments[1] < 0 || lengthSegments[1] > 60)
|
||||
return; // TODO: Make this throw an exception
|
||||
|
||||
// Frames
|
||||
if (!int.TryParse(splitLength[2], out lengthSegments[2]))
|
||||
return; // TODO: Make this throw an exception
|
||||
else if (lengthSegments[2] < 0 || lengthSegments[2] > 75)
|
||||
return; // TODO: Make this throw an exception
|
||||
|
||||
// Set the values
|
||||
this.Minutes = lengthSegments[0];
|
||||
this.Seconds = lengthSegments[1];
|
||||
this.Frames = lengthSegments[2];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the PREGAP out to a stream
|
||||
/// </summary>
|
||||
/// <param name="sw">StreamWriter to write to</param>
|
||||
public void Write(StreamWriter sw)
|
||||
{
|
||||
sw.WriteLine($" PREGAP {this.Minutes:D2}:{this.Seconds:D2}:{this.Frames:D2}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,9 @@
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2020</Copyright>
|
||||
<RepositoryUrl>https://github.com/SabreTools/DICUI</RepositoryUrl>
|
||||
<Version>1.17.0</Version>
|
||||
<AssemblyVersion>1.17.0</AssemblyVersion>
|
||||
<FileVersion>1.17.0</FileVersion>
|
||||
<Version>1.18</Version>
|
||||
<AssemblyVersion>1.18</AssemblyVersion>
|
||||
<FileVersion>1.18</FileVersion>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
@@ -42,10 +42,41 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BurnOutSharp" Version="1.4.0" />
|
||||
<Compile Remove="Aaru\CICMMetadata\CICMMetadataEditor\**" />
|
||||
<Compile Remove="Aaru\CICMMetadata\java\**" />
|
||||
<Compile Remove="Aaru\CICMMetadata\samples\**" />
|
||||
<EmbeddedResource Remove="Aaru\CICMMetadata\CICMMetadataEditor\**" />
|
||||
<EmbeddedResource Remove="Aaru\CICMMetadata\java\**" />
|
||||
<EmbeddedResource Remove="Aaru\CICMMetadata\samples\**" />
|
||||
<None Remove="Aaru\CICMMetadata\CICMMetadataEditor\**" />
|
||||
<None Remove="Aaru\CICMMetadata\java\**" />
|
||||
<None Remove="Aaru\CICMMetadata\samples\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Aaru\CICMMetadata\.editorconfig" />
|
||||
<None Remove="Aaru\CICMMetadata\.git" />
|
||||
<None Remove="Aaru\CICMMetadata\.gitignore" />
|
||||
<None Remove="Aaru\CICMMetadata\.project" />
|
||||
<None Remove="Aaru\CICMMetadata\build.sh" />
|
||||
<None Remove="Aaru\CICMMetadata\cicm.xml" />
|
||||
<None Remove="Aaru\CICMMetadata\cicm.xsd" />
|
||||
<None Remove="Aaru\CICMMetadata\CICMMetadata.iml" />
|
||||
<None Remove="Aaru\CICMMetadata\dotnet\cicm.vb" />
|
||||
<None Remove="Aaru\CICMMetadata\README.md" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="1.5.0" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="System.Management" Version="4.7.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="$(PkgBurnOutSharp)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Management" />
|
||||
|
||||
@@ -1,5 +1,51 @@
|
||||
namespace DICUI.Data
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace DICUI.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// Constant values for UI
|
||||
/// </summary>
|
||||
public static class Interface
|
||||
{
|
||||
// Button values
|
||||
public const string StartDumping = "Start Dumping";
|
||||
public const string StopDumping = "Stop Dumping";
|
||||
|
||||
// Byte arrays for signatures
|
||||
public static readonly byte[] SaturnSectorZeroStart = new byte[] { 0x53, 0x45, 0x47, 0x41, 0x20, 0x53, 0x45, 0x47, 0x41, 0x53, 0x41, 0x54, 0x55, 0x52, 0x4E, 0x20 };
|
||||
|
||||
// Private lists of known drive speed ranges
|
||||
private static IReadOnlyList<int> cd { get; } = new List<int> { 1, 2, 3, 4, 6, 8, 12, 16, 20, 24, 32, 40, 44, 48, 52, 56, 72 };
|
||||
private static IReadOnlyList<int> dvd { get; } = cd.Where(s => s <= 24).ToList();
|
||||
private static IReadOnlyList<int> bd { get; } = cd.Where(s => s <= 16).ToList();
|
||||
private static IReadOnlyList<int> unknown { get; } = new List<int> { 1 };
|
||||
|
||||
/// <summary>
|
||||
/// Get list of all drive speeds for a given MediaType
|
||||
/// </summary>
|
||||
/// <param name="type">MediaType? that represents the current item</param>
|
||||
/// <returns>Read-only list of drive speeds</returns>
|
||||
public static IReadOnlyList<int> GetSpeedsForMediaType(MediaType? type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case MediaType.CDROM:
|
||||
case MediaType.GDROM:
|
||||
return cd;
|
||||
case MediaType.DVD:
|
||||
case MediaType.HDDVD:
|
||||
case MediaType.NintendoGameCubeGameDisc:
|
||||
case MediaType.NintendoWiiOpticalDisc:
|
||||
return dvd;
|
||||
case MediaType.BluRay:
|
||||
return bd;
|
||||
default:
|
||||
return unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Template field values for submission info
|
||||
/// </summary>
|
||||
|
||||
@@ -44,12 +44,6 @@ namespace DICUI.Data
|
||||
set { _settings["DefaultOutputPath"] = value; }
|
||||
}
|
||||
|
||||
public string SubDumpPath
|
||||
{
|
||||
get { return GetStringSetting(_settings, "SubDumpPath", "Programs\\Subdump\\subdump.exe"); }
|
||||
set { _settings["SubDumpPath"] = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Dumping Speeds
|
||||
@@ -114,7 +108,7 @@ namespace DICUI.Data
|
||||
|
||||
public bool IgnoreFixedDrives
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "IgnoreFixedDrives", false); }
|
||||
get { return GetBooleanSetting(_settings, "IgnoreFixedDrives", true); }
|
||||
set { _settings["IgnoreFixedDrives"] = value.ToString(); }
|
||||
}
|
||||
|
||||
@@ -184,6 +178,8 @@ namespace DICUI.Data
|
||||
this._settings = settings ?? new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Get a Boolean setting from a settings, dictionary
|
||||
/// </summary>
|
||||
@@ -243,6 +239,8 @@ namespace DICUI.Data
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDictionary implementations
|
||||
|
||||
public ICollection<string> Keys => _settings.Keys;
|
||||
|
||||
@@ -45,6 +45,7 @@ namespace DICUI.DiscImageCreator
|
||||
public const string D8Opcode = "/d8";
|
||||
public const string DisableBeep = "/q";
|
||||
public const string ExtractMicroSoftCabFile = "/mscf";
|
||||
public const string Fix = "/fix";
|
||||
public const string ForceUnitAccess = "/f";
|
||||
public const string MultiSession = "/ms";
|
||||
public const string NoFixSubP = "/np";
|
||||
@@ -53,7 +54,9 @@ namespace DICUI.DiscImageCreator
|
||||
public const string NoFixSubRtoW = "/nr";
|
||||
public const string NoFixSubQSecuROM = "/ns";
|
||||
public const string NoSkipSS = "/nss";
|
||||
public const string PadSector = "/ps";
|
||||
public const string Raw = "/raw";
|
||||
public const string Resume = "/re";
|
||||
public const string Reverse = "/r";
|
||||
public const string ScanAntiMod = "/am";
|
||||
public const string ScanFileProtect = "/sf";
|
||||
|
||||
@@ -214,6 +214,8 @@ namespace DICUI.DiscImageCreator
|
||||
return FlagStrings.DisableBeep;
|
||||
case Flag.ExtractMicroSoftCabFile:
|
||||
return FlagStrings.ExtractMicroSoftCabFile;
|
||||
case Flag.Fix:
|
||||
return FlagStrings.Fix;
|
||||
case Flag.ForceUnitAccess:
|
||||
return FlagStrings.ForceUnitAccess;
|
||||
case Flag.MultiSession:
|
||||
@@ -230,8 +232,12 @@ namespace DICUI.DiscImageCreator
|
||||
return FlagStrings.NoFixSubQSecuROM;
|
||||
case Flag.NoSkipSS:
|
||||
return FlagStrings.NoSkipSS;
|
||||
case Flag.PadSector:
|
||||
return FlagStrings.PadSector;
|
||||
case Flag.Raw:
|
||||
return FlagStrings.Raw;
|
||||
case Flag.Resume:
|
||||
return FlagStrings.Resume;
|
||||
case Flag.Reverse:
|
||||
return FlagStrings.Reverse;
|
||||
case Flag.ScanAntiMod:
|
||||
|
||||
@@ -47,6 +47,7 @@ namespace DICUI.DiscImageCreator
|
||||
D8Opcode,
|
||||
DisableBeep,
|
||||
ExtractMicroSoftCabFile,
|
||||
Fix,
|
||||
ForceUnitAccess,
|
||||
MultiSession,
|
||||
NoFixSubP,
|
||||
@@ -55,7 +56,9 @@ namespace DICUI.DiscImageCreator
|
||||
NoFixSubRtoW,
|
||||
NoFixSubQSecuROM,
|
||||
NoSkipSS,
|
||||
PadSector,
|
||||
Raw,
|
||||
Resume,
|
||||
Reverse,
|
||||
ScanAntiMod,
|
||||
ScanFileProtect,
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using BurnOutSharp.External.psxt001z;
|
||||
using DICUI.CueSheets;
|
||||
using DICUI.Data;
|
||||
using DICUI.Utilities;
|
||||
using DICUI.Web;
|
||||
@@ -77,7 +78,7 @@ namespace DICUI.DiscImageCreator
|
||||
#region Flag Values
|
||||
|
||||
/// <summary>
|
||||
/// Manual offset for Audio CD
|
||||
/// Manual offset for Audio CD (default 0)
|
||||
/// </summary>
|
||||
public int? AddOffsetValue { get; set; }
|
||||
|
||||
@@ -85,17 +86,23 @@ namespace DICUI.DiscImageCreator
|
||||
/// 0xbe opcode value for dumping
|
||||
/// Possible values: raw (default), pack
|
||||
/// </summary>
|
||||
/// TODO: Make this an enum
|
||||
public string BEOpcodeValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// C2 reread options for dumping
|
||||
/// [0] - Reread value
|
||||
/// [0] - Reread value (default 4000)
|
||||
/// [1] - 0 reread issue sector (default), 1 reread all
|
||||
/// [2] - First LBA to reread (default 0)
|
||||
/// [3] - Last LBA to reread (default EOS)
|
||||
/// </summary>
|
||||
public int?[] C2OpcodeValue { get; set; } = new int?[4];
|
||||
|
||||
/// <summary>
|
||||
/// End LBA for fixing
|
||||
/// </summary>
|
||||
public int? FixValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set the force unit access flag value (default 1)
|
||||
/// </summary>
|
||||
@@ -106,13 +113,28 @@ namespace DICUI.DiscImageCreator
|
||||
/// </summary>
|
||||
public int? NoSkipSecuritySectorValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set the pad sector flag value (default 0)
|
||||
/// </summary>
|
||||
public int? PadSectorValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set the reverse End LBA value (required for DVD)
|
||||
/// </summary>
|
||||
public int? ReverseEndLBAValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set the reverse Start LBA value (required for DVD)
|
||||
/// </summary>
|
||||
public int? ReverseStartLBAValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set scan file timeout value (default 60)
|
||||
/// </summary>
|
||||
public int? ScanFileProtectValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Beginning and ending sectors to skip for physical protection
|
||||
/// Beginning and ending sectors to skip for physical protection (both default 0)
|
||||
/// </summary>
|
||||
public int?[] SkipSectorValue { get; set; } = new int?[2];
|
||||
|
||||
@@ -123,7 +145,7 @@ namespace DICUI.DiscImageCreator
|
||||
public int? SubchannelReadLevelValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set number of empty bytes to insert at the head of first track for VideoNow
|
||||
/// Set number of empty bytes to insert at the head of first track for VideoNow (default 0)
|
||||
/// </summary>
|
||||
public int? VideoNowValue { get; set; }
|
||||
|
||||
@@ -275,8 +297,6 @@ namespace DICUI.DiscImageCreator
|
||||
parameters.Add(Converters.LongName(Flag.AddOffset));
|
||||
if (AddOffsetValue != null)
|
||||
parameters.Add(AddOffsetValue.ToString());
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,7 +342,9 @@ namespace DICUI.DiscImageCreator
|
||||
if (C2OpcodeValue[1] != null)
|
||||
{
|
||||
if (C2OpcodeValue[1] == 0)
|
||||
{
|
||||
parameters.Add(C2OpcodeValue[1].ToString());
|
||||
}
|
||||
else if (C2OpcodeValue[1] == 1)
|
||||
{
|
||||
parameters.Add(C2OpcodeValue[1].ToString());
|
||||
@@ -334,11 +356,15 @@ namespace DICUI.DiscImageCreator
|
||||
parameters.Add(C2OpcodeValue[3].ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -364,6 +390,26 @@ namespace DICUI.DiscImageCreator
|
||||
parameters.Add(Converters.LongName(Flag.DisableBeep));
|
||||
}
|
||||
|
||||
// Extract MicroSoftCabFile
|
||||
if (GetSupportedCommands(Flag.ExtractMicroSoftCabFile).Contains(BaseCommand))
|
||||
{
|
||||
if (this[Flag.ExtractMicroSoftCabFile] == true)
|
||||
parameters.Add(Converters.LongName(Flag.ExtractMicroSoftCabFile));
|
||||
}
|
||||
|
||||
// Fix
|
||||
if (GetSupportedCommands(Flag.Fix).Contains(BaseCommand))
|
||||
{
|
||||
if (this[Flag.Fix] == true)
|
||||
{
|
||||
parameters.Add(Converters.LongName(Flag.Fix));
|
||||
if (FixValue != null)
|
||||
parameters.Add(FixValue.ToString());
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Force Unit Access
|
||||
if (GetSupportedCommands(Flag.ForceUnitAccess).Contains(BaseCommand))
|
||||
{
|
||||
@@ -375,13 +421,6 @@ namespace DICUI.DiscImageCreator
|
||||
}
|
||||
}
|
||||
|
||||
// Extract MicroSoftCabFile
|
||||
if (GetSupportedCommands(Flag.ExtractMicroSoftCabFile).Contains(BaseCommand))
|
||||
{
|
||||
if (this[Flag.ExtractMicroSoftCabFile] == true)
|
||||
parameters.Add(Converters.LongName(Flag.ExtractMicroSoftCabFile));
|
||||
}
|
||||
|
||||
// Multi-Session
|
||||
if (GetSupportedCommands(Flag.MultiSession).Contains(BaseCommand))
|
||||
{
|
||||
@@ -435,6 +474,17 @@ namespace DICUI.DiscImageCreator
|
||||
}
|
||||
}
|
||||
|
||||
// Pad sectors
|
||||
if (GetSupportedCommands(Flag.PadSector).Contains(BaseCommand))
|
||||
{
|
||||
if (this[Flag.PadSector] == true)
|
||||
{
|
||||
parameters.Add(Converters.LongName(Flag.PadSector));
|
||||
if (PadSectorValue != null)
|
||||
parameters.Add(PadSectorValue.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
// Raw read (2064 byte/sector)
|
||||
if (GetSupportedCommands(Flag.Raw).Contains(BaseCommand))
|
||||
{
|
||||
@@ -442,11 +492,29 @@ namespace DICUI.DiscImageCreator
|
||||
parameters.Add(Converters.LongName(Flag.Raw));
|
||||
}
|
||||
|
||||
// Resume
|
||||
if (GetSupportedCommands(Flag.Resume).Contains(BaseCommand))
|
||||
{
|
||||
if (this[Flag.Resume] == true)
|
||||
parameters.Add(Converters.LongName(Flag.Resume));
|
||||
}
|
||||
|
||||
// Reverse read
|
||||
if (GetSupportedCommands(Flag.Reverse).Contains(BaseCommand))
|
||||
{
|
||||
if (this[Flag.Reverse] == true)
|
||||
{
|
||||
parameters.Add(Converters.LongName(Flag.Reverse));
|
||||
|
||||
if (BaseCommand == Command.DigitalVideoDisc)
|
||||
{
|
||||
if (ReverseStartLBAValue == null || ReverseEndLBAValue == null)
|
||||
return null;
|
||||
|
||||
parameters.Add(ReverseStartLBAValue.ToString());
|
||||
parameters.Add(ReverseEndLBAValue.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scan PlayStation anti-mod strings
|
||||
@@ -491,19 +559,19 @@ namespace DICUI.DiscImageCreator
|
||||
{
|
||||
if (this[Flag.SkipSector] == true)
|
||||
{
|
||||
if (SkipSectorValue[0] != null && SkipSectorValue[1] != null)
|
||||
parameters.Add(Converters.LongName(Flag.SkipSector));
|
||||
if (SkipSectorValue[0] != null)
|
||||
{
|
||||
parameters.Add(Converters.LongName(Flag.SkipSector));
|
||||
if (SkipSectorValue[0] >= 0 && SkipSectorValue[1] >= 0)
|
||||
{
|
||||
if (SkipSectorValue[0] > 0)
|
||||
parameters.Add(SkipSectorValue[0].ToString());
|
||||
parameters.Add(SkipSectorValue[1].ToString());
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
else
|
||||
return null;
|
||||
if (SkipSectorValue[1] != null)
|
||||
{
|
||||
if (SkipSectorValue[1] == 0)
|
||||
parameters.Add(SkipSectorValue[1].ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -643,6 +711,7 @@ namespace DICUI.DiscImageCreator
|
||||
AddOffsetValue = null;
|
||||
BEOpcodeValue = null;
|
||||
C2OpcodeValue = new int?[4];
|
||||
FixValue = null;
|
||||
ForceUnitAccessValue = null;
|
||||
NoSkipSecuritySectorValue = null;
|
||||
ScanFileProtectValue = null;
|
||||
@@ -1171,11 +1240,23 @@ namespace DICUI.DiscImageCreator
|
||||
{
|
||||
case FlagStrings.AddOffset:
|
||||
if (!GetSupportedCommands(Flag.AddOffset).Contains(BaseCommand))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!DoesExist(parts, i + 1))
|
||||
return false;
|
||||
{
|
||||
this[Flag.AddOffset] = true;
|
||||
break;
|
||||
}
|
||||
else if (IsFlag(parts[i + 1]))
|
||||
{
|
||||
this[Flag.AddOffset] = true;
|
||||
break;
|
||||
}
|
||||
else if (!IsValidInt32(parts[i + 1]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this[Flag.AddOffset] = true;
|
||||
AddOffsetValue = Int32.Parse(parts[i + 1]);
|
||||
@@ -1198,7 +1279,9 @@ namespace DICUI.DiscImageCreator
|
||||
|
||||
case FlagStrings.BEOpcode:
|
||||
if (!GetSupportedCommands(Flag.BEOpcode).Contains(BaseCommand))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!DoesExist(parts, i + 1))
|
||||
{
|
||||
this[Flag.BEOpcode] = true;
|
||||
@@ -1209,11 +1292,14 @@ namespace DICUI.DiscImageCreator
|
||||
this[Flag.BEOpcode] = true;
|
||||
break;
|
||||
}
|
||||
else if (parts[i + 1] != "raw" && (parts[i + 1] != "pack"))
|
||||
else if (!string.Equals(parts[i + 1], "raw", StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.Equals(parts[i + 1], "pack", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this[Flag.BEOpcode] = true;
|
||||
BEOpcodeValue = parts[i + 1];
|
||||
BEOpcodeValue = parts[i + 1].ToLowerInvariant();
|
||||
i++;
|
||||
break;
|
||||
|
||||
@@ -1225,11 +1311,17 @@ namespace DICUI.DiscImageCreator
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
if (!DoesExist(parts, i + 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (IsFlag(parts[i + 1]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (!IsValidInt32(parts[i + 1], lowerBound: 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
C2OpcodeValue[j] = Int32.Parse(parts[i + 1]);
|
||||
@@ -1267,9 +1359,24 @@ namespace DICUI.DiscImageCreator
|
||||
this[Flag.ExtractMicroSoftCabFile] = true;
|
||||
break;
|
||||
|
||||
case FlagStrings.Fix:
|
||||
if (!GetSupportedCommands(Flag.Fix).Contains(BaseCommand))
|
||||
return false;
|
||||
else if (!DoesExist(parts, i + 1))
|
||||
return false;
|
||||
else if (!IsValidInt32(parts[i + 1], lowerBound: 0))
|
||||
return false;
|
||||
|
||||
this[Flag.Fix] = true;
|
||||
FixValue = Int32.Parse(parts[i + 1]);
|
||||
i++;
|
||||
break;
|
||||
|
||||
case FlagStrings.ForceUnitAccess:
|
||||
if (!GetSupportedCommands(Flag.ForceUnitAccess).Contains(BaseCommand))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!DoesExist(parts, i + 1))
|
||||
{
|
||||
this[Flag.ForceUnitAccess] = true;
|
||||
@@ -1281,7 +1388,9 @@ namespace DICUI.DiscImageCreator
|
||||
break;
|
||||
}
|
||||
else if (!IsValidInt32(parts[i + 1], lowerBound: 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this[Flag.ForceUnitAccess] = true;
|
||||
ForceUnitAccessValue = Int32.Parse(parts[i + 1]);
|
||||
@@ -1316,13 +1425,6 @@ namespace DICUI.DiscImageCreator
|
||||
this[Flag.NoFixSubQLibCrypt] = true;
|
||||
break;
|
||||
|
||||
case FlagStrings.NoFixSubRtoW:
|
||||
if (!GetSupportedCommands(Flag.NoFixSubRtoW).Contains(BaseCommand))
|
||||
return false;
|
||||
|
||||
this[Flag.NoFixSubRtoW] = true;
|
||||
break;
|
||||
|
||||
case FlagStrings.NoFixSubQSecuROM:
|
||||
if (!GetSupportedCommands(Flag.NoFixSubQSecuROM).Contains(BaseCommand))
|
||||
return false;
|
||||
@@ -1330,9 +1432,18 @@ namespace DICUI.DiscImageCreator
|
||||
this[Flag.NoFixSubQSecuROM] = true;
|
||||
break;
|
||||
|
||||
case FlagStrings.NoFixSubRtoW:
|
||||
if (!GetSupportedCommands(Flag.NoFixSubRtoW).Contains(BaseCommand))
|
||||
return false;
|
||||
|
||||
this[Flag.NoFixSubRtoW] = true;
|
||||
break;
|
||||
|
||||
case FlagStrings.NoSkipSS:
|
||||
if (!GetSupportedCommands(Flag.NoSkipSS).Contains(BaseCommand))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!DoesExist(parts, i + 1))
|
||||
{
|
||||
this[Flag.NoSkipSS] = true;
|
||||
@@ -1344,10 +1455,37 @@ namespace DICUI.DiscImageCreator
|
||||
break;
|
||||
}
|
||||
else if (!IsValidInt32(parts[i + 1], lowerBound: 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this[Flag.NoSkipSS] = true;
|
||||
ForceUnitAccessValue = Int32.Parse(parts[i + 1]);
|
||||
NoSkipSecuritySectorValue = Int32.Parse(parts[i + 1]);
|
||||
i++;
|
||||
break;
|
||||
|
||||
case FlagStrings.PadSector:
|
||||
if (!GetSupportedCommands(Flag.PadSector).Contains(BaseCommand))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!DoesExist(parts, i + 1))
|
||||
{
|
||||
this[Flag.PadSector] = true;
|
||||
break;
|
||||
}
|
||||
else if (IsFlag(parts[i + 1]))
|
||||
{
|
||||
this[Flag.PadSector] = true;
|
||||
break;
|
||||
}
|
||||
else if (!IsValidInt32(parts[i + 1], lowerBound: 0, upperBound: 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this[Flag.PadSector] = true;
|
||||
PadSectorValue = Int32.Parse(parts[i + 1]);
|
||||
i++;
|
||||
break;
|
||||
|
||||
@@ -1358,10 +1496,30 @@ namespace DICUI.DiscImageCreator
|
||||
this[Flag.Raw] = true;
|
||||
break;
|
||||
|
||||
case FlagStrings.Resume:
|
||||
if (!GetSupportedCommands(Flag.Resume).Contains(BaseCommand))
|
||||
return false;
|
||||
|
||||
this[Flag.Resume] = true;
|
||||
break;
|
||||
|
||||
case FlagStrings.Reverse:
|
||||
if (!GetSupportedCommands(Flag.Reverse).Contains(BaseCommand))
|
||||
return false;
|
||||
|
||||
// DVD specifically requires StartLBA and EndLBA
|
||||
if (BaseCommand == Command.DigitalVideoDisc)
|
||||
{
|
||||
if (!DoesExist(parts, i + 1) || !DoesExist(parts, i + 2))
|
||||
return false;
|
||||
else if (!IsValidInt32(parts[i + 1], lowerBound: 0) || !IsValidInt32(parts[i + 2], lowerBound: 0))
|
||||
return false;
|
||||
|
||||
ReverseStartLBAValue = Int32.Parse(parts[i + 1]);
|
||||
ReverseEndLBAValue = Int32.Parse(parts[i + 2]);
|
||||
i += 2;
|
||||
}
|
||||
|
||||
this[Flag.Reverse] = true;
|
||||
break;
|
||||
|
||||
@@ -1374,7 +1532,9 @@ namespace DICUI.DiscImageCreator
|
||||
|
||||
case FlagStrings.ScanFileProtect:
|
||||
if (!GetSupportedCommands(Flag.ScanFileProtect).Contains(BaseCommand))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!DoesExist(parts, i + 1))
|
||||
{
|
||||
this[Flag.ScanFileProtect] = true;
|
||||
@@ -1386,7 +1546,9 @@ namespace DICUI.DiscImageCreator
|
||||
break;
|
||||
}
|
||||
else if (!IsValidInt32(parts[i + 1], lowerBound: 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this[Flag.ScanFileProtect] = true;
|
||||
ScanFileProtectValue = Int32.Parse(parts[i + 1]);
|
||||
@@ -1410,22 +1572,36 @@ namespace DICUI.DiscImageCreator
|
||||
case FlagStrings.SkipSector:
|
||||
if (!GetSupportedCommands(Flag.SkipSector).Contains(BaseCommand))
|
||||
return false;
|
||||
else if (!DoesExist(parts, i + 1) || !DoesExist(parts, i + 2))
|
||||
return false;
|
||||
else if (IsFlag(parts[i + 1]) || IsFlag(parts[i + 2]))
|
||||
return false;
|
||||
else if (!IsValidInt32(parts[i + 1], lowerBound: 0) || !IsValidInt32(parts[i + 2], lowerBound: 0))
|
||||
return false;
|
||||
|
||||
this[Flag.SkipSector] = true;
|
||||
SkipSectorValue[0] = Int32.Parse(parts[i + 1]);
|
||||
SkipSectorValue[1] = Int32.Parse(parts[i + 2]);
|
||||
i += 2;
|
||||
for (int j = 0; j < 2; j++)
|
||||
{
|
||||
if (!DoesExist(parts, i + 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (IsFlag(parts[i + 1]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (!IsValidInt32(parts[i + 1], lowerBound: 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
SkipSectorValue[j] = Int32.Parse(parts[i + 1]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case FlagStrings.SubchannelReadLevel:
|
||||
if (!GetSupportedCommands(Flag.SubchannelReadLevel).Contains(BaseCommand))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!DoesExist(parts, i + 1))
|
||||
{
|
||||
this[Flag.SubchannelReadLevel] = true;
|
||||
@@ -1437,7 +1613,9 @@ namespace DICUI.DiscImageCreator
|
||||
break;
|
||||
}
|
||||
else if (!IsValidInt32(parts[i + 1], lowerBound: 0, upperBound: 2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this[Flag.SubchannelReadLevel] = true;
|
||||
SubchannelReadLevelValue = Int32.Parse(parts[i + 1]);
|
||||
@@ -1453,7 +1631,9 @@ namespace DICUI.DiscImageCreator
|
||||
|
||||
case FlagStrings.VideoNow:
|
||||
if (!GetSupportedCommands(Flag.VideoNow).Contains(BaseCommand))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (!DoesExist(parts, i + 1))
|
||||
{
|
||||
this[Flag.VideoNow] = true;
|
||||
@@ -1465,7 +1645,9 @@ namespace DICUI.DiscImageCreator
|
||||
break;
|
||||
}
|
||||
else if (!IsValidInt32(parts[i + 1], lowerBound: 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this[Flag.VideoNow] = true;
|
||||
VideoNowValue = Int32.Parse(parts[i + 1]);
|
||||
@@ -1685,6 +1867,7 @@ namespace DICUI.DiscImageCreator
|
||||
}
|
||||
|
||||
info.TracksAndWriteOffsets.Cuesheet = GetFullFile(basePath + ".cue") ?? "";
|
||||
var cueSheet = new CueSheet(basePath + ".cue"); // TODO: Do something with this
|
||||
|
||||
string cdWriteOffset = GetWriteOffset(basePath + "_disc.txt") ?? "";
|
||||
info.CommonDiscInfo.RingWriteOffset = cdWriteOffset;
|
||||
@@ -1711,7 +1894,7 @@ namespace DICUI.DiscImageCreator
|
||||
if (type == MediaType.DVD)
|
||||
layerbreak = GetLayerbreak(basePath + "_disc.txt", xgd) ?? "";
|
||||
else if (type == MediaType.BluRay)
|
||||
layerbreak = (info.SizeAndChecksums.Size > 25025314816 ? "25025314816" : null);
|
||||
layerbreak = info.SizeAndChecksums.Size > 25_025_314_816 ? "25025314816" : null;
|
||||
|
||||
// If we have a single-layer disc
|
||||
if (string.IsNullOrWhiteSpace(layerbreak))
|
||||
@@ -1803,8 +1986,21 @@ namespace DICUI.DiscImageCreator
|
||||
|
||||
case KnownSystem.NamcoSegaNintendoTriforce:
|
||||
if (type == MediaType.CDROM)
|
||||
{
|
||||
info.Extras.Header = GetSegaHeader(basePath + "_mainInfo.txt") ?? "";
|
||||
|
||||
// Take only the first 16 lines for GD-ROM
|
||||
if (!string.IsNullOrEmpty(info.Extras.Header))
|
||||
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16));
|
||||
|
||||
if (GetGDROMBuildInfo(info.Extras.Header, out string gdSerial, out string gdVersion, out string gdDate))
|
||||
{
|
||||
info.CommonDiscInfo.Comments += $"Internal Serial: {gdSerial ?? ""}";
|
||||
info.VersionAndEditions.Version = gdVersion ?? "";
|
||||
info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case KnownSystem.SegaCDMegaCD:
|
||||
@@ -1816,7 +2012,7 @@ namespace DICUI.DiscImageCreator
|
||||
|
||||
if (GetSegaCDBuildInfo(info.Extras.Header, out string scdSerial, out string fixedDate))
|
||||
{
|
||||
info.CommonDiscInfo.Serial = scdSerial ?? "";
|
||||
info.CommonDiscInfo.Comments += $"Internal Serial: {scdSerial ?? ""}";
|
||||
info.CommonDiscInfo.EXEDateBuildDate = fixedDate ?? "";
|
||||
}
|
||||
|
||||
@@ -1824,26 +2020,78 @@ namespace DICUI.DiscImageCreator
|
||||
|
||||
case KnownSystem.SegaChihiro:
|
||||
if (type == MediaType.CDROM)
|
||||
{
|
||||
info.Extras.Header = GetSegaHeader(basePath + "_mainInfo.txt") ?? "";
|
||||
|
||||
// Take only the first 16 lines for GD-ROM
|
||||
if (!string.IsNullOrEmpty(info.Extras.Header))
|
||||
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16));
|
||||
|
||||
if (GetGDROMBuildInfo(info.Extras.Header, out string gdSerial, out string gdVersion, out string gdDate))
|
||||
{
|
||||
info.CommonDiscInfo.Comments += $"Internal Serial: {gdSerial ?? ""}";
|
||||
info.VersionAndEditions.Version = gdVersion ?? "";
|
||||
info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case KnownSystem.SegaDreamcast:
|
||||
if (type == MediaType.CDROM)
|
||||
{
|
||||
info.Extras.Header = GetSegaHeader(basePath + "_mainInfo.txt") ?? "";
|
||||
|
||||
// Take only the first 16 lines for GD-ROM
|
||||
if (!string.IsNullOrEmpty(info.Extras.Header))
|
||||
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16));
|
||||
|
||||
if (GetGDROMBuildInfo(info.Extras.Header, out string gdSerial, out string gdVersion, out string gdDate))
|
||||
{
|
||||
info.CommonDiscInfo.Comments += $"Internal Serial: {gdSerial ?? ""}";
|
||||
info.VersionAndEditions.Version = gdVersion ?? "";
|
||||
info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case KnownSystem.SegaNaomi:
|
||||
if (type == MediaType.CDROM)
|
||||
{
|
||||
info.Extras.Header = GetSegaHeader(basePath + "_mainInfo.txt") ?? "";
|
||||
|
||||
// Take only the first 16 lines for GD-ROM
|
||||
if (!string.IsNullOrEmpty(info.Extras.Header))
|
||||
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16));
|
||||
|
||||
if (GetGDROMBuildInfo(info.Extras.Header, out string gdSerial, out string gdVersion, out string gdDate))
|
||||
{
|
||||
info.CommonDiscInfo.Comments += $"Internal Serial: {gdSerial ?? ""}";
|
||||
info.VersionAndEditions.Version = gdVersion ?? "";
|
||||
info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case KnownSystem.SegaNaomi2:
|
||||
if (type == MediaType.CDROM)
|
||||
{
|
||||
info.Extras.Header = GetSegaHeader(basePath + "_mainInfo.txt") ?? "";
|
||||
|
||||
// Take only the first 16 lines for GD-ROM
|
||||
if (!string.IsNullOrEmpty(info.Extras.Header))
|
||||
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16));
|
||||
|
||||
if (GetGDROMBuildInfo(info.Extras.Header, out string gdSerial, out string gdVersion, out string gdDate))
|
||||
{
|
||||
info.CommonDiscInfo.Comments += $"Internal Serial: {gdSerial ?? ""}";
|
||||
info.VersionAndEditions.Version = gdVersion ?? "";
|
||||
info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case KnownSystem.SegaSaturn:
|
||||
@@ -1855,7 +2103,7 @@ namespace DICUI.DiscImageCreator
|
||||
|
||||
if (GetSaturnBuildInfo(info.Extras.Header, out string saturnSerial, out string saturnVersion, out string buildDate))
|
||||
{
|
||||
info.CommonDiscInfo.Serial = saturnSerial ?? "";
|
||||
info.CommonDiscInfo.Comments += $"Internal Serial: {saturnSerial ?? ""}";
|
||||
info.VersionAndEditions.Version = saturnVersion ?? "";
|
||||
info.CommonDiscInfo.EXEDateBuildDate = buildDate ?? "";
|
||||
}
|
||||
@@ -1865,7 +2113,7 @@ namespace DICUI.DiscImageCreator
|
||||
case KnownSystem.SonyPlayStation:
|
||||
if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationSerial, out Region? playstationRegion, out string playstationDate))
|
||||
{
|
||||
info.CommonDiscInfo.Comments += $"Internal Disc Serial: {playstationSerial}\n";
|
||||
info.CommonDiscInfo.Comments += $"Internal Serial: {playstationSerial ?? ""}\n";
|
||||
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationRegion;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = playstationDate;
|
||||
}
|
||||
@@ -1986,22 +2234,35 @@ namespace DICUI.DiscImageCreator
|
||||
commands.Add(Command.SACD);
|
||||
commands.Add(Command.Swap);
|
||||
commands.Add(Command.XBOX);
|
||||
commands.Add(Command.XBOXSwap);
|
||||
commands.Add(Command.XGD2Swap);
|
||||
commands.Add(Command.XGD3Swap);
|
||||
break;
|
||||
case Flag.ExtractMicroSoftCabFile:
|
||||
commands.Add(Command.CompactDisc);
|
||||
break;
|
||||
case Flag.Fix:
|
||||
commands.Add(Command.DigitalVideoDisc);
|
||||
break;
|
||||
case Flag.ForceUnitAccess:
|
||||
commands.Add(Command.Audio);
|
||||
commands.Add(Command.BluRay);
|
||||
commands.Add(Command.CompactDisc);
|
||||
commands.Add(Command.Data);
|
||||
commands.Add(Command.DigitalVideoDisc);
|
||||
commands.Add(Command.GDROM);
|
||||
commands.Add(Command.SACD);
|
||||
commands.Add(Command.Swap);
|
||||
commands.Add(Command.XBOX);
|
||||
commands.Add(Command.XBOXSwap);
|
||||
commands.Add(Command.XGD2Swap);
|
||||
commands.Add(Command.XGD3Swap);
|
||||
break;
|
||||
case Flag.MultiSession:
|
||||
commands.Add(Command.Audio);
|
||||
commands.Add(Command.CompactDisc);
|
||||
commands.Add(Command.Data);
|
||||
commands.Add(Command.Swap);
|
||||
break;
|
||||
case Flag.NoFixSubP:
|
||||
commands.Add(Command.Audio);
|
||||
@@ -2018,17 +2279,11 @@ namespace DICUI.DiscImageCreator
|
||||
commands.Add(Command.Swap);
|
||||
break;
|
||||
case Flag.NoFixSubQLibCrypt:
|
||||
commands.Add(Command.Audio);
|
||||
commands.Add(Command.CompactDisc);
|
||||
commands.Add(Command.Data);
|
||||
commands.Add(Command.GDROM);
|
||||
commands.Add(Command.Swap);
|
||||
break;
|
||||
case Flag.NoFixSubQSecuROM:
|
||||
commands.Add(Command.Audio);
|
||||
commands.Add(Command.CompactDisc);
|
||||
commands.Add(Command.Data);
|
||||
commands.Add(Command.GDROM);
|
||||
commands.Add(Command.Swap);
|
||||
break;
|
||||
case Flag.NoFixSubRtoW:
|
||||
@@ -2039,22 +2294,31 @@ namespace DICUI.DiscImageCreator
|
||||
commands.Add(Command.Swap);
|
||||
break;
|
||||
case Flag.NoSkipSS:
|
||||
commands.Add(Command.BluRay);
|
||||
commands.Add(Command.XBOX);
|
||||
commands.Add(Command.XBOXSwap);
|
||||
commands.Add(Command.XGD2Swap);
|
||||
commands.Add(Command.XGD3Swap);
|
||||
break;
|
||||
case Flag.PadSector:
|
||||
commands.Add(Command.DigitalVideoDisc);
|
||||
break;
|
||||
case Flag.Raw:
|
||||
commands.Add(Command.DigitalVideoDisc);
|
||||
break;
|
||||
case Flag.Resume:
|
||||
commands.Add(Command.DigitalVideoDisc);
|
||||
break;
|
||||
case Flag.Reverse:
|
||||
commands.Add(Command.CompactDisc);
|
||||
commands.Add(Command.Audio);
|
||||
commands.Add(Command.Data);
|
||||
commands.Add(Command.DigitalVideoDisc);
|
||||
break;
|
||||
case Flag.ScanAntiMod:
|
||||
commands.Add(Command.Audio);
|
||||
commands.Add(Command.CompactDisc);
|
||||
commands.Add(Command.Data);
|
||||
commands.Add(Command.Swap);
|
||||
break;
|
||||
case Flag.ScanFileProtect:
|
||||
commands.Add(Command.Audio);
|
||||
@@ -2064,14 +2328,17 @@ namespace DICUI.DiscImageCreator
|
||||
commands.Add(Command.Swap);
|
||||
break;
|
||||
case Flag.ScanSectorProtect:
|
||||
commands.Add(Command.Audio);
|
||||
commands.Add(Command.CompactDisc);
|
||||
commands.Add(Command.Data);
|
||||
commands.Add(Command.Swap);
|
||||
break;
|
||||
case Flag.SeventyFour:
|
||||
commands.Add(Command.CompactDisc);
|
||||
commands.Add(Command.Swap);
|
||||
break;
|
||||
case Flag.SkipSector:
|
||||
commands.Add(Command.Audio);
|
||||
commands.Add(Command.Data);
|
||||
break;
|
||||
case Flag.SubchannelReadLevel:
|
||||
@@ -2088,12 +2355,15 @@ namespace DICUI.DiscImageCreator
|
||||
break;
|
||||
case Flag.VideoNow:
|
||||
commands.Add(Command.CompactDisc);
|
||||
commands.Add(Command.Swap);
|
||||
break;
|
||||
case Flag.VideoNowColor:
|
||||
commands.Add(Command.CompactDisc);
|
||||
commands.Add(Command.Swap);
|
||||
break;
|
||||
case Flag.VideoNowXP:
|
||||
commands.Add(Command.CompactDisc);
|
||||
commands.Add(Command.Swap);
|
||||
break;
|
||||
|
||||
case Flag.NONE:
|
||||
@@ -2195,14 +2465,14 @@ namespace DICUI.DiscImageCreator
|
||||
sr.ReadLine(); // <description>Plextor</description>
|
||||
|
||||
// Now that we're at the relevant entries, read each line in and concatenate
|
||||
string pvd = "", line = sr.ReadLine().Trim();
|
||||
string datString = "", line = sr.ReadLine().Trim();
|
||||
while (line.StartsWith("<rom"))
|
||||
{
|
||||
pvd += line + "\n";
|
||||
datString += line + "\n";
|
||||
line = sr.ReadLine().Trim();
|
||||
}
|
||||
|
||||
return pvd.TrimEnd('\n');
|
||||
return datString.TrimEnd('\n');
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -2366,6 +2636,38 @@ namespace DICUI.DiscImageCreator
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the build info from a GD-ROM LD area, if possible
|
||||
/// </summary>
|
||||
/// <<param name="segaHeader">String representing a formatter variant of the GD-ROM header</param>
|
||||
/// <returns>True on successful extraction of info, false otherwise</returns>
|
||||
private static bool GetGDROMBuildInfo(string segaHeader, out string serial, out string version, out string date)
|
||||
{
|
||||
serial = null; version = null; date = null;
|
||||
|
||||
// If the input header is null, we can't do a thing
|
||||
if (string.IsNullOrWhiteSpace(segaHeader))
|
||||
return false;
|
||||
|
||||
// Now read it in cutting it into lines for easier parsing
|
||||
try
|
||||
{
|
||||
string[] header = segaHeader.Split('\n');
|
||||
string serialLine = header[2].Substring(58);
|
||||
string versionLine = header[4].Substring(58);
|
||||
string dateLine = header[5].Substring(58);
|
||||
serial = serialLine.Substring(0, 4);
|
||||
version = versionLine.Substring(10, 6).TrimStart('V', 'v');
|
||||
date = dateLine.Substring(0, 8);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error is
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the layerbreak from the input file, if possible
|
||||
/// </summary>
|
||||
@@ -2567,11 +2869,41 @@ namespace DICUI.DiscImageCreator
|
||||
{
|
||||
try
|
||||
{
|
||||
// Make sure we're in the right sector
|
||||
while (!sr.ReadLine().StartsWith("========== LBA[000016, 0x00010]: Main Channel ==========")) ;
|
||||
// If we're in a new mainInfo, the location of the header changed
|
||||
string line = sr.ReadLine();
|
||||
if (line.StartsWith("========== OpCode"))
|
||||
{
|
||||
// Scrambled LBA0, instance 1
|
||||
while (!(line = sr.ReadLine()).StartsWith("========== LBA[000000, 0000000]: Main Channel ==========")) ;
|
||||
|
||||
// Scrambled LBA0, instance 2
|
||||
while (!(line = sr.ReadLine()).StartsWith("========== LBA[000000, 0000000]: Main Channel ==========")) ;
|
||||
|
||||
// Scrambled LBA0, instance 3
|
||||
while (!(line = sr.ReadLine()).StartsWith("========== LBA[000000, 0000000]: Main Channel ==========")) ;
|
||||
|
||||
// Start of second OpCode area
|
||||
while (!(line = sr.ReadLine()).StartsWith("========== OpCode")) ;
|
||||
|
||||
// Read the next line so the search goes properly
|
||||
line = sr.ReadLine();
|
||||
}
|
||||
|
||||
// Make sure we're in the area
|
||||
if (!line.StartsWith("========== LBA"))
|
||||
while (!(line = sr.ReadLine()).StartsWith("========== LBA")) ;
|
||||
|
||||
// If we have a Sega disc, skip sector 0
|
||||
if (line.StartsWith("========== LBA[000000, 0000000]: Main Channel =========="))
|
||||
while (!(line = sr.ReadLine()).StartsWith("========== LBA")) ;
|
||||
|
||||
// If we have a PlayStation disc, skip sector 4
|
||||
if (line.StartsWith("========== LBA[000004, 0x00004]: Main Channel =========="))
|
||||
while (!(line = sr.ReadLine()).StartsWith("========== LBA")) ;
|
||||
|
||||
// We assume the first non-LBA0/4 sector listed is the proper one
|
||||
// Fast forward to the PVD
|
||||
while (!sr.ReadLine().StartsWith("0310")) ;
|
||||
while (!(line = sr.ReadLine()).StartsWith("0310")) ;
|
||||
|
||||
// Now that we're at the PVD, read each line in and concatenate
|
||||
string pvd = "";
|
||||
@@ -2608,7 +2940,7 @@ namespace DICUI.DiscImageCreator
|
||||
string serialVersionLine = header[2].Substring(58);
|
||||
string dateLine = header[3].Substring(58);
|
||||
serial = serialVersionLine.Substring(0, 8);
|
||||
version = serialVersionLine.Substring(10, 6);
|
||||
version = serialVersionLine.Substring(10, 6).TrimStart('V', 'v');
|
||||
date = dateLine.Substring(0, 8);
|
||||
return true;
|
||||
}
|
||||
@@ -2718,11 +3050,32 @@ namespace DICUI.DiscImageCreator
|
||||
{
|
||||
try
|
||||
{
|
||||
// If we're in a new mainInfo, the location of the header changed
|
||||
string line = sr.ReadLine();
|
||||
if (line.StartsWith("========== OpCode"))
|
||||
{
|
||||
// Scrambled LBA0, instance 1
|
||||
while (!(line = sr.ReadLine()).StartsWith("========== LBA[000000, 0000000]: Main Channel ==========")) ;
|
||||
|
||||
// Scrambled LBA0, instance 2
|
||||
while (!(line = sr.ReadLine()).StartsWith("========== LBA[000000, 0000000]: Main Channel ==========")) ;
|
||||
|
||||
// Scrambled LBA0, instance 3
|
||||
while (!(line = sr.ReadLine()).StartsWith("========== LBA[000000, 0000000]: Main Channel ==========")) ;
|
||||
|
||||
// Start of second OpCode area
|
||||
while (!(line = sr.ReadLine()).StartsWith("========== OpCode")) ;
|
||||
|
||||
// Read the next line so the search goes properly
|
||||
line = sr.ReadLine();
|
||||
}
|
||||
|
||||
// Make sure we're in the right sector
|
||||
while (!sr.ReadLine().StartsWith("========== LBA[000000, 0000000]: Main Channel ==========")) ;
|
||||
if (!line.StartsWith("========== LBA[000000, 0000000]: Main Channel =========="))
|
||||
while (!(line = sr.ReadLine()).StartsWith("========== LBA[000000, 0000000]: Main Channel ==========")) ;
|
||||
|
||||
// Fast forward to the header
|
||||
while (!sr.ReadLine().Trim().StartsWith("+0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F")) ;
|
||||
while (!(line = sr.ReadLine()).Trim().StartsWith("+0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F")) ;
|
||||
|
||||
// Now that we're at the Header, read each line in and concatenate
|
||||
string header = "";
|
||||
|
||||
@@ -61,5 +61,42 @@ namespace DICUI.Utilities
|
||||
this.InternalDriveType = driveType;
|
||||
this.DriveInfo = driveInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a sector with a specified size from the drive
|
||||
/// </summary>
|
||||
/// <param name="num">Sector number, non-negative</param>
|
||||
/// <param name="size">Size of a sector in bytes</param>
|
||||
/// <returns>Byte array representing the sector, null on error</returns>
|
||||
public byte[] ReadSector(long num, int size = 2048)
|
||||
{
|
||||
// Missing drive leter is not supported
|
||||
if (string.IsNullOrEmpty(this.DriveInfo?.Name))
|
||||
return null;
|
||||
|
||||
// We don't support negative sectors
|
||||
if (num < 0)
|
||||
return null;
|
||||
|
||||
// Wrap the following in case of device access errors
|
||||
try
|
||||
{
|
||||
// Open the drive as a device
|
||||
var fs = File.OpenRead($"\\\\?\\{this.Letter}:");
|
||||
|
||||
// Seek to the start of the sector, if possible
|
||||
long start = num * size;
|
||||
fs.Seek(start, SeekOrigin.Begin);
|
||||
|
||||
// Read and return the sector
|
||||
byte[] buffer = new byte[size];
|
||||
fs.Read(buffer, 0, size);
|
||||
return buffer;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,11 +26,6 @@ namespace DICUI.Utilities
|
||||
/// <remarks>DiscImageCreator is both used as an internal tool and an extra tool</remarks>
|
||||
public string DiscImageCreatorPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Path to Subdump executable
|
||||
/// </summary>
|
||||
public string SubdumpPath { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Output paths
|
||||
@@ -149,7 +144,6 @@ namespace DICUI.Utilities
|
||||
{
|
||||
// Tool paths
|
||||
this.DiscImageCreatorPath = options.CreatorPath;
|
||||
this.SubdumpPath = options.SubDumpPath;
|
||||
|
||||
// Output paths
|
||||
this.OutputDirectory = outputDirectory;
|
||||
@@ -587,17 +581,6 @@ namespace DICUI.Utilities
|
||||
/// <returns>Result instance with the outcome</returns>
|
||||
private Result ExecuteAdditionalTools()
|
||||
{
|
||||
// Special cases
|
||||
switch (System)
|
||||
{
|
||||
case KnownSystem.SegaSaturn:
|
||||
if (!File.Exists(SubdumpPath))
|
||||
return Result.Success("Could not find subdump! Skipping execution...");
|
||||
|
||||
ExecuteSubdump();
|
||||
return Result.Success("subdump has finished!");
|
||||
}
|
||||
|
||||
return Result.Success("No external tools needed!");
|
||||
}
|
||||
|
||||
@@ -638,26 +621,6 @@ namespace DICUI.Utilities
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Execute subdump for a (potential) Sega Saturn dump
|
||||
/// </summary>
|
||||
private async void ExecuteSubdump()
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
Process childProcess = new Process()
|
||||
{
|
||||
StartInfo = new ProcessStartInfo()
|
||||
{
|
||||
FileName = SubdumpPath,
|
||||
Arguments = "-i " + Drive.Letter + ": -f " + Path.Combine(OutputDirectory, Path.GetFileNameWithoutExtension(OutputFilename) + "_subdump.sub") + "-mode 6 -rereadnum 25 -fix 2",
|
||||
},
|
||||
};
|
||||
childProcess.Start();
|
||||
childProcess.WaitForExit();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract all of the possible information from a given input combination
|
||||
/// </summary>
|
||||
@@ -675,10 +638,6 @@ namespace DICUI.Utilities
|
||||
// Sanitize the output filename to strip off any potential extension
|
||||
string outputFilename = Path.GetFileNameWithoutExtension(OutputFilename);
|
||||
|
||||
// TODO: Notes about Aaru outputs:
|
||||
// - `image convert` might be able to help generate CUE, CCD, SUB?
|
||||
// - NEED CONFIRMATION OF TRACK SPLITTING ON CD
|
||||
|
||||
// Check that all of the relevant files are there
|
||||
if (!FoundAllFiles())
|
||||
return null;
|
||||
@@ -1192,6 +1151,7 @@ namespace DICUI.Utilities
|
||||
prefix += "\t";
|
||||
|
||||
// If the value contains a newline
|
||||
value = value.Replace("\r\n", "\n");
|
||||
if (value.Contains("\n"))
|
||||
{
|
||||
output.Add(prefix + key + ":"); output.Add("");
|
||||
|
||||
@@ -4,8 +4,74 @@ using DICUI.Web;
|
||||
|
||||
namespace DICUI.Utilities
|
||||
{
|
||||
public class Tools
|
||||
public static class Tools
|
||||
{
|
||||
#region Byte Arrays
|
||||
|
||||
/// <summary>
|
||||
/// Search for a byte array in another array
|
||||
/// </summary>
|
||||
public static bool Contains(this byte[] stack, byte[] needle, out int position, int start = 0, int end = -1)
|
||||
{
|
||||
// Initialize the found position to -1
|
||||
position = -1;
|
||||
|
||||
// If either array is null or empty, we can't do anything
|
||||
if (stack == null || stack.Length == 0 || needle == null || needle.Length == 0)
|
||||
return false;
|
||||
|
||||
// If the needle array is larger than the stack array, it can't be contained within
|
||||
if (needle.Length > stack.Length)
|
||||
return false;
|
||||
|
||||
// If start or end are not set properly, set them to defaults
|
||||
if (start < 0)
|
||||
start = 0;
|
||||
if (end < 0)
|
||||
end = stack.Length - needle.Length;
|
||||
|
||||
for (int i = start; i < end; i++)
|
||||
{
|
||||
if (stack.EqualAt(needle, i))
|
||||
{
|
||||
position = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See if a byte array starts with another
|
||||
/// </summary>
|
||||
public static bool StartsWith(this byte[] stack, byte[] needle)
|
||||
{
|
||||
return stack.Contains(needle, out int _, start: 0, end: 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get if a stack at a certain index is equal to a needle
|
||||
/// </summary>
|
||||
private static bool EqualAt(this byte[] stack, byte[] needle, int index)
|
||||
{
|
||||
// If we're too close to the end of the stack, return false
|
||||
if (needle.Length >= stack.Length - index)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < needle.Length; i++)
|
||||
{
|
||||
if (stack[i + index] != needle[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Versioning
|
||||
|
||||
/// <summary>
|
||||
/// Check for a new DICUI version
|
||||
/// </summary>
|
||||
@@ -44,5 +110,6 @@ namespace DICUI.Utilities
|
||||
return $"{assemblyVersion.Major}.{assemblyVersion.Minor}" + (assemblyVersion.Build != 0 ? $".{assemblyVersion.Build}" : string.Empty);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -932,6 +932,13 @@ namespace DICUI.Utilities
|
||||
return KnownSystem.DVDVideo;
|
||||
}
|
||||
|
||||
// HD-DVD-Video
|
||||
if (Directory.Exists(Path.Combine(drivePath, "HVDVD_TS"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "HVDVD_TS")).Count() > 0)
|
||||
{
|
||||
return KnownSystem.HDDVDVideo;
|
||||
}
|
||||
|
||||
// Sega Dreamcast
|
||||
if (File.Exists(Path.Combine(drivePath, "IP.BIN")))
|
||||
{
|
||||
@@ -947,6 +954,18 @@ namespace DICUI.Utilities
|
||||
return KnownSystem.SegaCDMegaCD;
|
||||
}
|
||||
|
||||
// Sega Saturn
|
||||
try
|
||||
{
|
||||
byte[] sector = drive?.ReadSector(0);
|
||||
if (sector != null)
|
||||
{
|
||||
if (sector.StartsWith(Interface.SaturnSectorZeroStart))
|
||||
return KnownSystem.SegaSaturn;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Sony PlayStation and Sony PlayStation 2
|
||||
string psxExePath = Path.Combine(drivePath, "PSX.EXE");
|
||||
string systemCnfPath = Path.Combine(drivePath, "SYSTEM.CNF");
|
||||
@@ -1032,27 +1051,32 @@ namespace DICUI.Utilities
|
||||
/// <returns>Copy protection detected in the envirionment, if any</returns>
|
||||
public static async Task<string> RunProtectionScanOnPath(string path, IProgress<FileProtection> progress = null)
|
||||
{
|
||||
#if NET_FRAMEWORK
|
||||
try
|
||||
{
|
||||
var found = await Task.Run(() =>
|
||||
{
|
||||
return ProtectionFind.Scan(path, includePosition: false, progress: progress);
|
||||
var scanner = new Scanner(progress)
|
||||
{
|
||||
IncludePosition = false, // Debug flag to include protection position in file
|
||||
ScanAllFiles = false, // Forces all files to be scanned as executables
|
||||
ScanArchives = true, // Allows archives to have internals extracted and scanned
|
||||
ScanPackers = false, // Allows executable packers to be detected
|
||||
};
|
||||
return scanner.GetProtections(path);
|
||||
});
|
||||
|
||||
if (found == null || found.Count == 0)
|
||||
return "None found";
|
||||
|
||||
// Strip out "CD Check" instances due to false positives
|
||||
return string.Join("\n", found.Where(kvp => !kvp.Value.Equals("CD Check", StringComparison.OrdinalIgnoreCase)).Select(kvp => kvp.Key + ": " + kvp.Value).ToArray());
|
||||
// Join the output protections for writing
|
||||
return string.Join("\n", found
|
||||
.Where(kvp => kvp.Value != null && kvp.Value.Any())
|
||||
.Select(kvp => $"{kvp.Key}: {string.Join(", ", kvp.Value)}"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return $"Path could not be scanned! {ex}";
|
||||
}
|
||||
#else
|
||||
return "Copy protection scanning is not available on .NET Core builds";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ namespace DICUI.Web
|
||||
/// <summary>
|
||||
/// Regex matching individual WIP disc links on a results page
|
||||
/// </summary>
|
||||
private readonly Regex newDiscRegex = new Regex(@"<a href=""/newdisc/(\d+)/"">");
|
||||
private readonly Regex newDiscRegex = new Regex(@"<a (style=.*)?href=""/newdisc/(\d+)/"">");
|
||||
|
||||
/// <summary>
|
||||
/// Regex matching the "partial match" ID list from a WIP disc page
|
||||
@@ -254,8 +254,9 @@ namespace DICUI.Web
|
||||
|
||||
#region URL Extensions
|
||||
|
||||
private const string changesExt = "/changes/";
|
||||
private const string changesExt = "changes/";
|
||||
private const string cueExt = "cue/";
|
||||
private const string editExt = "edit/";
|
||||
private const string gdiExt = "gdi/";
|
||||
private const string keyExt = "key/";
|
||||
private const string lsdExt = "lsd/";
|
||||
@@ -328,30 +329,38 @@ namespace DICUI.Web
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the current token from the login page
|
||||
var loginPage = DownloadString(loginUrl);
|
||||
string token = this.tokenRegex.Match(loginPage).Groups[1].Value;
|
||||
|
||||
// Construct the login request
|
||||
Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
|
||||
Encoding = Encoding.UTF8;
|
||||
var response = UploadString(loginUrl, $"form_sent=1&redirect_url=&csrf_token={token}&req_username={username}&req_password={password}&save_pass=0");
|
||||
|
||||
if (response.Contains("Incorrect username and/or password."))
|
||||
try
|
||||
{
|
||||
Console.WriteLine("Invalid credentials entered, continuing without logging in...");
|
||||
// Get the current token from the login page
|
||||
var loginPage = DownloadString(loginUrl);
|
||||
string token = this.tokenRegex.Match(loginPage).Groups[1].Value;
|
||||
|
||||
// Construct the login request
|
||||
Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
|
||||
Encoding = Encoding.UTF8;
|
||||
var response = UploadString(loginUrl, $"form_sent=1&redirect_url=&csrf_token={token}&req_username={username}&req_password={password}&save_pass=0");
|
||||
|
||||
if (response.Contains("Incorrect username and/or password."))
|
||||
{
|
||||
Console.WriteLine("Invalid credentials entered, continuing without logging in...");
|
||||
return false;
|
||||
}
|
||||
|
||||
// The user was able to be logged in
|
||||
Console.WriteLine("Credentials accepted! Logged into Redump...");
|
||||
LoggedIn = true;
|
||||
|
||||
// If the user is a moderator or staff, set accordingly
|
||||
if (response.Contains("http://forum.redump.org/forum/9/staff/"))
|
||||
IsStaff = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"An exception occurred while trying to log in: {ex}");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Credentials accepted! Logged into Redump...");
|
||||
}
|
||||
|
||||
// If the user is a moderator or staff, set accordingly
|
||||
if (response.Contains("http://forum.redump.org/forum/9/staff/"))
|
||||
IsStaff = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -891,7 +900,8 @@ namespace DICUI.Web
|
||||
match = commentsRegex.Match(discData);
|
||||
if (match.Success)
|
||||
{
|
||||
info.CommonDiscInfo.Comments = WebUtility.HtmlDecode(match.Groups[1].Value)
|
||||
info.CommonDiscInfo.Comments += (string.IsNullOrEmpty(info.CommonDiscInfo.Comments) ? string.Empty : "\n")
|
||||
+ WebUtility.HtmlDecode(match.Groups[1].Value)
|
||||
.Replace("<br />", "\n")
|
||||
.Replace("<b>ISBN</b>", "[T:ISBN]") + "\n";
|
||||
}
|
||||
@@ -1107,7 +1117,7 @@ namespace DICUI.Web
|
||||
{
|
||||
try
|
||||
{
|
||||
if (int.TryParse(match.Groups[1].Value, out int value))
|
||||
if (int.TryParse(match.Groups[2].Value, out int value))
|
||||
ids.Add(value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -1141,7 +1151,7 @@ namespace DICUI.Web
|
||||
{
|
||||
try
|
||||
{
|
||||
if (int.TryParse(match.Groups[1].Value, out int value))
|
||||
if (int.TryParse(match.Groups[2].Value, out int value))
|
||||
{
|
||||
bool downloaded = DownloadSingleWIPID(value, outDir, false);
|
||||
if (!downloaded && failOnSingle)
|
||||
@@ -1291,18 +1301,18 @@ namespace DICUI.Web
|
||||
// Create ID subdirectory
|
||||
Directory.CreateDirectory(paddedIdDir);
|
||||
|
||||
// HTML
|
||||
using (var discStreamWriter = File.CreateText(Path.Combine(paddedIdDir, "disc.html")))
|
||||
{
|
||||
discStreamWriter.Write(discPage);
|
||||
}
|
||||
|
||||
DownloadFile(string.Format(discPageUrl, +id) + changesExt, Path.Combine(paddedIdDir, "changes.html"));
|
||||
// View Edit History
|
||||
if (discPage.Contains($"<a href=\"/disc/{id}/changes/\""))
|
||||
DownloadFile(string.Format(discPageUrl, +id) + changesExt, Path.Combine(paddedIdDir, "changes.html"));
|
||||
|
||||
// CUE
|
||||
if (discPage.Contains($"<a href=\"/disc/{id}/cue/\""))
|
||||
DownloadFile(string.Format(discPageUrl, +id) + cueExt, Path.Combine(paddedIdDir, paddedId + ".cue"));
|
||||
|
||||
// Edit disc
|
||||
if (discPage.Contains($"<a href=\"/disc/{id}/edit/\""))
|
||||
DownloadFile(string.Format(discPageUrl, +id) + editExt, Path.Combine(paddedIdDir, "edit.html"));
|
||||
|
||||
// GDI
|
||||
if (discPage.Contains($"<a href=\"/disc/{id}/gdi/\""))
|
||||
DownloadFile(string.Format(discPageUrl, +id) + gdiExt, Path.Combine(paddedIdDir, paddedId + ".gdi"));
|
||||
@@ -1319,6 +1329,13 @@ namespace DICUI.Web
|
||||
if (discPage.Contains($"<a href=\"/disc/{id}/md5/\""))
|
||||
DownloadFile(string.Format(discPageUrl, +id) + md5Ext, Path.Combine(paddedIdDir, paddedId + ".md5"));
|
||||
|
||||
// Review WIP entry
|
||||
if (newDiscRegex.IsMatch(discPage))
|
||||
{
|
||||
var match = newDiscRegex.Match(discPage);
|
||||
DownloadFile(string.Format(wipDiscPageUrl, match.Groups[2].Value), Path.Combine(paddedIdDir, "newdisc.html"));
|
||||
}
|
||||
|
||||
// SBI
|
||||
if (discPage.Contains($"<a href=\"/disc/{id}/sbi/\""))
|
||||
DownloadFile(string.Format(discPageUrl, +id) + sbiExt, Path.Combine(paddedIdDir, paddedId + ".sbi"));
|
||||
@@ -1331,6 +1348,12 @@ namespace DICUI.Web
|
||||
if (discPage.Contains($"<a href=\"/disc/{id}/sha1/\""))
|
||||
DownloadFile(string.Format(discPageUrl, +id) + sha1Ext, Path.Combine(paddedIdDir, paddedId + ".sha1"));
|
||||
|
||||
// HTML (Last in case of errors)
|
||||
using (var discStreamWriter = File.CreateText(Path.Combine(paddedIdDir, "disc.html")))
|
||||
{
|
||||
discStreamWriter.Write(discPage);
|
||||
}
|
||||
|
||||
Console.WriteLine($"ID {paddedId} has been successfully downloaded");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -10,11 +10,11 @@ namespace DICUI.Test.Data
|
||||
[InlineData(MediaType.CDROM, 72)]
|
||||
[InlineData(MediaType.DVD, 24)]
|
||||
[InlineData(MediaType.BluRay, 16)]
|
||||
[InlineData(MediaType.LaserDisc, 72)] // TODO: Update when fully determined
|
||||
[InlineData(null, 72)] // TODO: Update when fully determined
|
||||
[InlineData(MediaType.LaserDisc, 1)]
|
||||
[InlineData(null, 1)]
|
||||
public void GetAllowedDriveSpeedForMediaTypeTest(MediaType? mediaType, int maxExpected)
|
||||
{
|
||||
var actual = Constants.GetSpeedsForMediaType(mediaType);
|
||||
var actual = Interface.GetSpeedsForMediaType(mediaType);
|
||||
Assert.Equal(maxExpected, actual.Last());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<appSettings>
|
||||
<add key="AaruPath" value="Programs\Aaru\Aaru.exe"/>
|
||||
<add key="CreatorPath" value="Programs\Creator\DiscImageCreator.exe"/>
|
||||
<add key="DDPath" value="Programs\DD\dd.exe"/>
|
||||
<add key="SubDumpPath" value="Programs\Subdump\subdump.exe"/>
|
||||
<add key="DefaultOutputPath" value="ISO"/>
|
||||
<add key="InternalProgram" value="DiscImageCreator"/>
|
||||
|
||||
<add key="PreferredDumpSpeedCD" value="48"/>
|
||||
<add key="PreferredDumpSpeedDVD" value="24"/>
|
||||
<add key="PreferredDumpSpeedBD" value="16"/>
|
||||
|
||||
<add key="QuietMode" value="false"/>
|
||||
<add key="ParanoidMode" value="false"/>
|
||||
<add key="ScanForProtection" value="true"/>
|
||||
<add key="SkipMediaTypeDetection" value="false"/>
|
||||
<add key="SkipSystemDetection" value="false"/>
|
||||
<add key="RereadAmountForC2" value="20"/>
|
||||
<add key="VerboseLogging" value="true"/>
|
||||
<add key="OpenLogWindowAtStartup" value="true"/>
|
||||
<add key="AddPlaceholders" value="true"/>
|
||||
<add key="PromptForDiscInformation" value="true"/>
|
||||
<add key="IgnoreFixedDrives" value="false"/>
|
||||
<add key="ResetDriveAfterDump" value="false"/>
|
||||
|
||||
<add key="Username" value=""/>
|
||||
<add key="Password" value=""/>
|
||||
</appSettings>
|
||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/></startup></configuration>
|
||||
30
DICUI/ComboBoxItems/InternalProgramComboBoxItem.cs
Normal file
30
DICUI/ComboBoxItems/InternalProgramComboBoxItem.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using DICUI.Data;
|
||||
using DICUI.Utilities;
|
||||
|
||||
namespace DICUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a single item in the Internal Program combo box
|
||||
/// </summary>
|
||||
public class InternalProgramComboBoxItem
|
||||
{
|
||||
private object data;
|
||||
|
||||
public InternalProgramComboBoxItem(InternalProgram? internalProgram) => data = internalProgram;
|
||||
|
||||
public static implicit operator InternalProgram? (InternalProgramComboBoxItem item) => item.data as InternalProgram?;
|
||||
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return (data as InternalProgram?).LongName();
|
||||
}
|
||||
}
|
||||
|
||||
public InternalProgram? Value
|
||||
{
|
||||
get { return data as InternalProgram?; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows.Media;
|
||||
using DICUI.Data;
|
||||
|
||||
namespace DICUI
|
||||
{
|
||||
@@ -11,40 +10,12 @@ namespace DICUI
|
||||
/// </summary>
|
||||
public static class Constants
|
||||
{
|
||||
public const string StartDumping = "Start Dumping";
|
||||
public const string StopDumping = "Stop Dumping";
|
||||
|
||||
public const int LogWindowMarginFromMainWindow = 10;
|
||||
|
||||
// Private lists of known drive speed ranges
|
||||
private static IReadOnlyList<int> cd { get; } = new List<int> { 1, 2, 3, 4, 6, 8, 12, 16, 20, 24, 32, 40, 44, 48, 52, 56, 72 };
|
||||
private static IReadOnlyList<int> dvd { get; } = cd.Where(s => s <= 24).ToList();
|
||||
private static IReadOnlyList<int> bd { get; } = cd.Where(s => s <= 16).ToList();
|
||||
private static IReadOnlyList<int> unknown { get; } = cd; // TODO: All or {1}? Maybe null?
|
||||
|
||||
/// <summary>
|
||||
/// Get list of all drive speeds for a given MediaType
|
||||
/// </summary>
|
||||
/// <param name="type">MediaType? that represents the current item</param>
|
||||
/// <returns>Read-only list of drive speeds</returns>
|
||||
public static IReadOnlyList<int> GetSpeedsForMediaType(MediaType? type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case MediaType.CDROM:
|
||||
case MediaType.GDROM:
|
||||
return cd;
|
||||
case MediaType.DVD:
|
||||
case MediaType.HDDVD:
|
||||
case MediaType.NintendoGameCubeGameDisc:
|
||||
case MediaType.NintendoWiiOpticalDisc:
|
||||
return dvd;
|
||||
case MediaType.BluRay:
|
||||
return bd;
|
||||
default:
|
||||
return unknown;
|
||||
}
|
||||
}
|
||||
|
||||
// Create collections for UI based on known drive speeds
|
||||
public static DoubleCollection SpeedsForCDAsCollection { get; } = GetDoubleCollectionFromIntList(cd);
|
||||
|
||||
@@ -13,19 +13,22 @@
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2020</Copyright>
|
||||
<RepositoryUrl>https://github.com/SabreTools/DICUI</RepositoryUrl>
|
||||
<Version>1.17.0</Version>
|
||||
<AssemblyVersion>1.17.0</AssemblyVersion>
|
||||
<FileVersion>1.17.0</FileVersion>
|
||||
<Version>1.18</Version>
|
||||
<AssemblyVersion>1.18</AssemblyVersion>
|
||||
<FileVersion>1.18</FileVersion>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="1.5.0" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="4.7.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="4.7.0" />
|
||||
<Content Include="$(PkgBurnOutSharp)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -9,18 +9,6 @@ namespace DICUI
|
||||
// TODO: Is there any way that this can be made private?
|
||||
public Options Options { get; set; }
|
||||
|
||||
#region Passthrough readonly values
|
||||
|
||||
// TODO: Can any of these be removed?
|
||||
public string DefaultOutputPath { get { return Options.DefaultOutputPath; } }
|
||||
public bool IgnoreFixedDrives { get { return Options.IgnoreFixedDrives; } }
|
||||
public bool ResetDriveAfterDump { get { return Options.ResetDriveAfterDump; } }
|
||||
public bool SkipMediaTypeDetection { get { return Options.SkipMediaTypeDetection; } }
|
||||
public bool SkipSystemDetection { get { return Options.SkipSystemDetection; } }
|
||||
public bool OpenLogWindowAtStartup { get { return Options.OpenLogWindowAtStartup; } }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
|
||||
@@ -44,12 +44,6 @@ namespace DICUI
|
||||
set { _uiOptions.Options.DefaultOutputPath = value; }
|
||||
}
|
||||
|
||||
public string SubDumpPath
|
||||
{
|
||||
get { return _uiOptions.Options.SubDumpPath; }
|
||||
set { _uiOptions.Options.SubDumpPath = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Dumping Speeds
|
||||
|
||||
@@ -12,6 +12,8 @@ namespace DICUI.Windows
|
||||
/// </summary>
|
||||
public partial class DiscInformationWindow : Window
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// List of available disc categories
|
||||
/// </summary>
|
||||
@@ -32,10 +34,12 @@ namespace DICUI.Windows
|
||||
/// </summary>
|
||||
public List<LanguageComboBoxItem> Languages { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
public DiscInformationWindow(SubmissionInfo submissionInfo)
|
||||
{
|
||||
this.SubmissionInfo = submissionInfo;
|
||||
InitializeComponent();
|
||||
SubmissionInfo = submissionInfo;
|
||||
|
||||
PopulateCategories();
|
||||
PopulateRegions();
|
||||
@@ -49,7 +53,7 @@ namespace DICUI.Windows
|
||||
private void DisableFieldsIfNeeded()
|
||||
{
|
||||
// Only disable for single-layer discs
|
||||
if (SubmissionInfo.SizeAndChecksums.Layerbreak == default(long))
|
||||
if (SubmissionInfo?.SizeAndChecksums?.Layerbreak == default(long))
|
||||
{
|
||||
L1MasteringRingTextBox.IsEnabled = false;
|
||||
L1MasteringRingTextBox.Background = Brushes.Gray;
|
||||
|
||||
@@ -25,8 +25,8 @@ namespace DICUI.Windows
|
||||
|
||||
private readonly MainWindow _mainWindow;
|
||||
|
||||
private readonly FlowDocument _document;
|
||||
private readonly Paragraph _paragraph;
|
||||
private FlowDocument _document;
|
||||
private Paragraph _paragraph;
|
||||
private readonly List<Matcher> _matchers;
|
||||
|
||||
volatile Process _process;
|
||||
@@ -362,6 +362,8 @@ namespace DICUI.Windows
|
||||
private void OnClearButton(object sender, EventArgs e)
|
||||
{
|
||||
output.Document.Blocks.Clear();
|
||||
_paragraph = new Paragraph();
|
||||
_document.Blocks.Add(_paragraph);
|
||||
}
|
||||
|
||||
private void OnSaveButton(object sender, EventArgs e)
|
||||
|
||||
@@ -56,21 +56,11 @@ namespace DICUI.Windows
|
||||
/// </summary>
|
||||
private bool alreadyShown;
|
||||
|
||||
/// <summary>
|
||||
/// Current attached DiscInformationWindow
|
||||
/// </summary>
|
||||
private DiscInformationWindow discInformationWindow;
|
||||
|
||||
/// <summary>
|
||||
/// Current attached LogWindow
|
||||
/// </summary>
|
||||
private readonly LogWindow logWindow;
|
||||
|
||||
/// <summary>
|
||||
/// Currently attached OptionsWindow
|
||||
/// </summary>
|
||||
private OptionsWindow optionsWindow;
|
||||
|
||||
#endregion
|
||||
|
||||
public MainWindow()
|
||||
@@ -89,7 +79,7 @@ namespace DICUI.Windows
|
||||
MediaScanButton.IsEnabled = false;
|
||||
CopyProtectScanButton.IsEnabled = false;
|
||||
|
||||
if (UIOptions.OpenLogWindowAtStartup)
|
||||
if (UIOptions.Options.OpenLogWindowAtStartup)
|
||||
{
|
||||
this.WindowStartupLocation = WindowStartupLocation.Manual;
|
||||
double combinedHeight = this.Height + logWindow.Height + Constants.LogWindowMarginFromMainWindow;
|
||||
@@ -124,12 +114,19 @@ namespace DICUI.Windows
|
||||
// Get the drive letter from the selected item
|
||||
if (DriveLetterComboBox.SelectedItem is Drive drive)
|
||||
{
|
||||
// Get the current media type
|
||||
if (!UIOptions.SkipMediaTypeDetection)
|
||||
// Get the current media type, if possible
|
||||
if (UIOptions.Options.SkipMediaTypeDetection)
|
||||
{
|
||||
ViewModels.LoggerViewModel.VerboseLog("Media type detection disabled, defaulting to CD-ROM");
|
||||
CurrentMediaType = MediaType.CDROM;
|
||||
}
|
||||
else
|
||||
{
|
||||
ViewModels.LoggerViewModel.VerboseLog("Trying to detect media type for drive {0}.. ", drive.Letter);
|
||||
CurrentMediaType = Validators.GetMediaType(drive);
|
||||
ViewModels.LoggerViewModel.VerboseLogLn(CurrentMediaType == null ? "unable to detect." : ("detected " + CurrentMediaType.LongName() + "."));
|
||||
ViewModels.LoggerViewModel.VerboseLogLn(CurrentMediaType == null ? "unable to detect, defaulting to CD-ROM." : ($"detected {CurrentMediaType.LongName()}."));
|
||||
if (CurrentMediaType == null)
|
||||
CurrentMediaType = MediaType.CDROM;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -209,7 +206,7 @@ namespace DICUI.Windows
|
||||
|
||||
// Set the output directory, if we changed drives or it's not already
|
||||
if (driveChanged || string.IsNullOrEmpty(OutputDirectoryTextBox.Text))
|
||||
OutputDirectoryTextBox.Text = Path.Combine(UIOptions.DefaultOutputPath, drive?.VolumeLabel ?? string.Empty);
|
||||
OutputDirectoryTextBox.Text = Path.Combine(UIOptions.Options.DefaultOutputPath, drive?.VolumeLabel ?? string.Empty);
|
||||
|
||||
// Get the extension for the file for the next two statements
|
||||
string extension = Env.GetExtension(mediaType);
|
||||
@@ -235,7 +232,7 @@ namespace DICUI.Windows
|
||||
MediaScanButton.IsEnabled = true;
|
||||
|
||||
// Populate the list of drives and add it to the combo box
|
||||
Drives = Validators.CreateListOfDrives(UIOptions.IgnoreFixedDrives);
|
||||
Drives = Validators.CreateListOfDrives(UIOptions.Options.IgnoreFixedDrives);
|
||||
DriveLetterComboBox.ItemsSource = Drives;
|
||||
|
||||
if (DriveLetterComboBox.Items.Count > 0)
|
||||
@@ -257,7 +254,7 @@ namespace DICUI.Windows
|
||||
CopyProtectScanButton.IsEnabled = true;
|
||||
|
||||
// Get the current media type
|
||||
if (!UIOptions.SkipSystemDetection && index != -1)
|
||||
if (!UIOptions.Options.SkipSystemDetection && index != -1)
|
||||
{
|
||||
ViewModels.LoggerViewModel.VerboseLog("Trying to detect system for drive {0}.. ", Drives[index].Letter);
|
||||
var currentSystem = Validators.GetKnownSystem(Drives[index]);
|
||||
@@ -386,12 +383,15 @@ namespace DICUI.Windows
|
||||
/// </summary>
|
||||
private async void ScanAndShowProtection()
|
||||
{
|
||||
// Determine current environment, just in case
|
||||
if (Env == null)
|
||||
Env = DetermineEnvironment();
|
||||
|
||||
if (Env.Drive.Letter != default(char))
|
||||
// Pull the drive letter from the UI directly, just in case
|
||||
var drive = DriveLetterComboBox.SelectedItem as Drive;
|
||||
if (drive.Letter != default(char))
|
||||
{
|
||||
ViewModels.LoggerViewModel.VerboseLogLn("Scanning for copy protection in {0}", Env.Drive.Letter);
|
||||
ViewModels.LoggerViewModel.VerboseLogLn("Scanning for copy protection in {0}", drive.Letter);
|
||||
|
||||
var tempContent = StatusLabel.Content;
|
||||
StatusLabel.Content = "Scanning for copy protection... this might take a while!";
|
||||
@@ -401,7 +401,7 @@ namespace DICUI.Windows
|
||||
|
||||
var progress = new Progress<FileProtection>();
|
||||
progress.ProgressChanged += ProgressUpdated;
|
||||
string protections = await Validators.RunProtectionScanOnPath(Env.Drive.Letter + ":\\", progress);
|
||||
string protections = await Validators.RunProtectionScanOnPath(drive.Letter + ":\\", progress);
|
||||
|
||||
// If SmartE is detected on the current disc, remove `/sf` from the flags for DIC only
|
||||
if (Env.InternalProgram == InternalProgram.DiscImageCreator && protections.Contains("SmartE"))
|
||||
@@ -412,7 +412,8 @@ namespace DICUI.Windows
|
||||
|
||||
if (!ViewModels.LoggerViewModel.WindowVisible)
|
||||
MessageBox.Show(protections, "Detected Protection", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
ViewModels.LoggerViewModel.VerboseLog("Detected the following protections in {0}:\r\n\r\n{1}", Env.Drive.Letter, protections);
|
||||
|
||||
ViewModels.LoggerViewModel.VerboseLog("Detected the following protections in {0}:\r\n\r\n{1}", drive.Letter, protections);
|
||||
|
||||
StatusLabel.Content = tempContent;
|
||||
StartStopButton.IsEnabled = true;
|
||||
@@ -444,7 +445,7 @@ namespace DICUI.Windows
|
||||
private void SetSupportedDriveSpeed()
|
||||
{
|
||||
// Set the drive speed list that's appropriate
|
||||
var values = Constants.GetSpeedsForMediaType(CurrentMediaType);
|
||||
var values = Interface.GetSpeedsForMediaType(CurrentMediaType);
|
||||
DriveSpeedComboBox.ItemsSource = values;
|
||||
ViewModels.LoggerViewModel.VerboseLogLn("Supported media speeds: {0}", string.Join(",", values));
|
||||
|
||||
@@ -454,6 +455,20 @@ namespace DICUI.Windows
|
||||
DriveSpeedComboBox.SelectedValue = speed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show the disc information window
|
||||
/// </summary>
|
||||
/// <param name="submissionInfo">SubmissionInfo object to display and possibly change</param>
|
||||
/// <returns>Dialog open result</returns>
|
||||
private bool? ShowDiscInformationWindow(SubmissionInfo submissionInfo)
|
||||
{
|
||||
var discInformationWindow = new DiscInformationWindow(submissionInfo);
|
||||
discInformationWindow.Owner = this;
|
||||
discInformationWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||
discInformationWindow.Load();
|
||||
return discInformationWindow.ShowDialog();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Begin the dumping process using the given inputs
|
||||
/// </summary>
|
||||
@@ -504,7 +519,7 @@ namespace DICUI.Windows
|
||||
}
|
||||
}
|
||||
|
||||
StartStopButton.Content = Constants.StopDumping;
|
||||
StartStopButton.Content = Interface.StopDumping;
|
||||
CopyProtectScanButton.IsEnabled = false;
|
||||
StatusLabel.Content = "Beginning dumping process";
|
||||
ViewModels.LoggerViewModel.VerboseLogLn("Starting dumping process..");
|
||||
@@ -523,7 +538,7 @@ namespace DICUI.Windows
|
||||
{
|
||||
ViewModels.LoggerViewModel.VerboseLogLn("No dumping command was run, submission information will not be gathered.");
|
||||
StatusLabel.Content = "Execution complete!";
|
||||
StartStopButton.Content = Constants.StartDumping;
|
||||
StartStopButton.Content = Interface.StartDumping;
|
||||
CopyProtectScanButton.IsEnabled = true;
|
||||
return;
|
||||
}
|
||||
@@ -534,24 +549,8 @@ namespace DICUI.Windows
|
||||
result = await Env.VerifyAndSaveDumpOutput(resultProgress,
|
||||
protectionProgress,
|
||||
EjectWhenDoneCheckBox.IsChecked,
|
||||
UIOptions.ResetDriveAfterDump,
|
||||
(si) =>
|
||||
{
|
||||
// lazy initialization
|
||||
if (discInformationWindow == null)
|
||||
{
|
||||
discInformationWindow = new DiscInformationWindow(si);
|
||||
discInformationWindow.Closed += delegate
|
||||
{
|
||||
discInformationWindow = null;
|
||||
};
|
||||
}
|
||||
|
||||
discInformationWindow.Owner = this;
|
||||
discInformationWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||
discInformationWindow.Load();
|
||||
return discInformationWindow.ShowDialog();
|
||||
}
|
||||
UIOptions.Options.ResetDriveAfterDump,
|
||||
ShowDiscInformationWindow
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -561,7 +560,7 @@ namespace DICUI.Windows
|
||||
}
|
||||
finally
|
||||
{
|
||||
StartStopButton.Content = Constants.StartDumping;
|
||||
StartStopButton.Content = Interface.StartDumping;
|
||||
CopyProtectScanButton.IsEnabled = true;
|
||||
}
|
||||
}
|
||||
@@ -711,7 +710,7 @@ namespace DICUI.Windows
|
||||
|
||||
alreadyShown = true;
|
||||
|
||||
if (UIOptions.OpenLogWindowAtStartup)
|
||||
if (UIOptions.Options.OpenLogWindowAtStartup)
|
||||
{
|
||||
//TODO: this should be bound directly to WindowVisible property in two way fashion
|
||||
// we need to study how to properly do it in XAML
|
||||
@@ -742,21 +741,13 @@ namespace DICUI.Windows
|
||||
/// <summary>
|
||||
/// Handler for OptionsMenuItem Click event
|
||||
/// </summary>
|
||||
/// TODO: Re-evaluate this based on Avalonia code
|
||||
private void OptionsMenuItemClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// lazy initialization
|
||||
if (optionsWindow == null)
|
||||
{
|
||||
optionsWindow = new OptionsWindow(this, UIOptions);
|
||||
optionsWindow.Closed += delegate
|
||||
{
|
||||
optionsWindow = null;
|
||||
};
|
||||
}
|
||||
|
||||
// Show the window and wait for the response
|
||||
var optionsWindow = new OptionsWindow();
|
||||
optionsWindow.UIOptions = UIOptions;
|
||||
optionsWindow.Owner = this;
|
||||
optionsWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||
optionsWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen;
|
||||
optionsWindow.Refresh();
|
||||
optionsWindow.Show();
|
||||
}
|
||||
@@ -811,11 +802,11 @@ namespace DICUI.Windows
|
||||
private void StartStopButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Dump or stop the dump
|
||||
if ((string)StartStopButton.Content == Constants.StartDumping)
|
||||
if ((string)StartStopButton.Content == Interface.StartDumping)
|
||||
{
|
||||
StartDumping();
|
||||
}
|
||||
else if ((string)StartStopButton.Content == Constants.StopDumping)
|
||||
else if ((string)StartStopButton.Content == Interface.StopDumping)
|
||||
{
|
||||
ViewModels.LoggerViewModel.VerboseLogLn("Canceling dumping process...");
|
||||
Env.CancelDumping();
|
||||
@@ -827,7 +818,7 @@ namespace DICUI.Windows
|
||||
Env.EjectDisc();
|
||||
}
|
||||
|
||||
if (UIOptions.ResetDriveAfterDump)
|
||||
if (UIOptions.Options.ResetDriveAfterDump)
|
||||
{
|
||||
ViewModels.LoggerViewModel.VerboseLogLn($"Resetting drive {Env.Drive.Letter}");
|
||||
Env.ResetDrive();
|
||||
|
||||
@@ -45,18 +45,21 @@
|
||||
<Button x:Name="CreatorPathButton" Grid.Row="1" Grid.Column="2" Height="22" Width="22" Content="..." Click="BrowseForPathClick" />
|
||||
|
||||
<!--
|
||||
<Label Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="DD Path" />
|
||||
<TextBox x:Name="DDPathTextBox" Grid.Row="1" Grid.Column="1" Height="22" HorizontalAlignment="Stretch"
|
||||
<Label Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="DD Path" />
|
||||
<TextBox x:Name="DDPathTextBox" Grid.Row="2" Grid.Column="1" Height="22" HorizontalAlignment="Stretch"
|
||||
DataContext="{Binding Source={x:Static local:ViewModels.OptionsViewModel}}"
|
||||
Text="{Binding Path=DDPath}" />
|
||||
<Button x:Name="DDPathButton" Grid.Row="1" Grid.Column="2" Height="22" Width="22" Content="..." Click="BrowseForPathClick" />
|
||||
<Button x:Name="DDPathButton" Grid.Row="2" Grid.Column="2" Height="22" Width="22" Content="..." Click="BrowseForPathClick" />
|
||||
-->
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="subdump Path" />
|
||||
<TextBox x:Name="SubDumpPathTextBox" Grid.Row="2" Grid.Column="1" Height="22" HorizontalAlignment="Stretch"
|
||||
DataContext="{Binding Source={x:Static local:ViewModels.OptionsViewModel}}"
|
||||
Text="{Binding Path=SubDumpPath}" />
|
||||
<Button x:Name="SubDumpPathButton" Grid.Row="2" Grid.Column="2" Height="22" Width="22" Content="..." Click="BrowseForPathClick"/>
|
||||
<Label Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Dumping Program" />
|
||||
<ComboBox x:Name="InternalProgramComboBox" Grid.Row="2" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" >
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Path=Name}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<Label Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Default Output Path" />
|
||||
<TextBox x:Name="DefaultOutputPathTextBox" Grid.Row="3" Grid.Column="1" Height="22" HorizontalAlignment="Stretch"
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Forms;
|
||||
using DICUI.Data;
|
||||
using DICUI.Utilities;
|
||||
using DICUI.Web;
|
||||
using Button = System.Windows.Controls.Button;
|
||||
using TextBox = System.Windows.Controls.TextBox;
|
||||
@@ -13,14 +16,33 @@ namespace DICUI.Windows
|
||||
/// </summary>
|
||||
public partial class OptionsWindow : Window
|
||||
{
|
||||
private readonly MainWindow _mainWindow;
|
||||
private readonly UIOptions _uiOptions;
|
||||
#region Fields
|
||||
|
||||
public OptionsWindow(MainWindow mainWindow, UIOptions options)
|
||||
/// <summary>
|
||||
/// Current UI options
|
||||
/// </summary>
|
||||
public UIOptions UIOptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of available internal programs
|
||||
/// </summary>
|
||||
public List<InternalProgramComboBoxItem> InternalPrograms { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
public OptionsWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
_mainWindow = mainWindow;
|
||||
_uiOptions = options;
|
||||
|
||||
PopulateInternalPrograms();
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
||||
private FolderBrowserDialog CreateFolderBrowserDialog()
|
||||
{
|
||||
FolderBrowserDialog dialog = new FolderBrowserDialog();
|
||||
return dialog;
|
||||
}
|
||||
|
||||
private OpenFileDialog CreateOpenFileDialog()
|
||||
@@ -35,17 +57,52 @@ namespace DICUI.Windows
|
||||
return dialog;
|
||||
}
|
||||
|
||||
private FolderBrowserDialog CreateFolderBrowserDialog()
|
||||
/// <summary>
|
||||
/// Get a complete list of internal programs and fill the combo box
|
||||
/// </summary>
|
||||
private void PopulateInternalPrograms()
|
||||
{
|
||||
FolderBrowserDialog dialog = new FolderBrowserDialog();
|
||||
return dialog;
|
||||
// We only support certain programs for dumping
|
||||
var internalPrograms = new List<InternalProgram> { InternalProgram.DiscImageCreator, InternalProgram.Aaru, InternalProgram.DD };
|
||||
ViewModels.LoggerViewModel.VerboseLogLn("Populating internal programs, {0} internal programs found.", internalPrograms.Count);
|
||||
|
||||
InternalPrograms = new List<InternalProgramComboBoxItem>();
|
||||
foreach (var internalProgram in internalPrograms)
|
||||
{
|
||||
InternalPrograms.Add(new InternalProgramComboBoxItem(internalProgram));
|
||||
}
|
||||
|
||||
InternalProgramComboBox.ItemsSource = InternalPrograms;
|
||||
InternalProgramComboBox.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refresh any options-related elements
|
||||
/// </summary>
|
||||
public void Refresh()
|
||||
{
|
||||
// Handle non-bindable fields
|
||||
InternalProgramComboBox.SelectedIndex = InternalPrograms.FindIndex(r => r == Converters.ToInternalProgram(UIOptions.Options.InternalProgram));
|
||||
RedumpPasswordBox.Password = UIOptions.Options.Password;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find a TextBox by setting name
|
||||
/// </summary>
|
||||
/// <param name="name">Setting name to find</param>
|
||||
/// <returns>TextBox for that setting</returns>
|
||||
private TextBox TextBoxForPathSetting(string name)
|
||||
{
|
||||
return FindName(name + "TextBox") as TextBox;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
/// <summary>
|
||||
/// Handler for generic Click event
|
||||
/// </summary>
|
||||
private void BrowseForPathClick(object sender, EventArgs e)
|
||||
{
|
||||
Button button = sender as Button;
|
||||
@@ -93,25 +150,24 @@ namespace DICUI.Windows
|
||||
}
|
||||
}
|
||||
|
||||
public void Refresh()
|
||||
{
|
||||
// Handle non-bindable fields
|
||||
RedumpPasswordBox.Password = _uiOptions.Options.Password;
|
||||
}
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
/// <summary>
|
||||
/// Handler for AcceptButton Click event
|
||||
/// </summary>
|
||||
private void OnAcceptClick(object sender, EventArgs e)
|
||||
{
|
||||
// Handle non-bindable fields
|
||||
_uiOptions.Options.Password = RedumpPasswordBox.Password;
|
||||
UIOptions.Options.InternalProgram = (InternalProgramComboBox.SelectedItem as InternalProgramComboBoxItem)?.Name ?? InternalProgram.DiscImageCreator.ToString();
|
||||
UIOptions.Options.Password = RedumpPasswordBox.Password;
|
||||
|
||||
_uiOptions.Save();
|
||||
UIOptions.Save();
|
||||
Hide();
|
||||
|
||||
_mainWindow.OnOptionsUpdated();
|
||||
(Owner as MainWindow).OnOptionsUpdated();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for CancelButton Click event
|
||||
/// </summary>
|
||||
private void OnCancelClick(object sender, EventArgs e)
|
||||
{
|
||||
// just hide the window and don't care
|
||||
|
||||
@@ -18,7 +18,7 @@ For those who like to test the newest features, download the latest AppVeyor WIP
|
||||
Even though this is written in C#, this program can only be used on Windows systems due to one of the base programs, DiscImageCreator, being Windows-only. There is some preliminary support for Linux underway, and we will try to integrate with that when the time comes.
|
||||
|
||||
- Windows 7 (newest version of Windows recommended) or Mono-compatible Linux environment (DICUI.Check only)
|
||||
- .NET Framework 4.7.2, .NET Framework 4.8, or .NET Core 3.1 Runtimes (.NET Core 3.1 is only partially functional due to a dependency, use at your own risk)
|
||||
- .NET Framework 4.7.2, .NET Framework 4.8, or .NET Core 3.1 Runtimes (.NET Core 3.1 is mostly functional due to a dependency issues but may be unstable in some situations)
|
||||
- 1 GB of free RAM
|
||||
- As much hard drive space as the amount of discs you will be dumping (20+ GB recommended)
|
||||
|
||||
@@ -30,7 +30,7 @@ For all additional information, including information about the individual compo
|
||||
|
||||
## Changelist
|
||||
|
||||
A list of all changes in each stable release can now be found [here](https://github.com/SabreTools/DICUI/blob/master/CHANGELIST.md).
|
||||
A list of all changes in each stable release and current WIP builds can now be found [here](https://github.com/SabreTools/DICUI/blob/master/CHANGELIST.md).
|
||||
|
||||
## External Libraries
|
||||
|
||||
|
||||
62
appveyor.yml
62
appveyor.yml
@@ -1,5 +1,5 @@
|
||||
# version format
|
||||
version: 1.17-{build}
|
||||
version: 1.18-{build}
|
||||
|
||||
# pull request template
|
||||
pull_requests:
|
||||
@@ -21,6 +21,8 @@ configuration:
|
||||
# install dependencies
|
||||
install:
|
||||
- ps: appveyor DownloadFile https://dist.nuget.org/win-x86-commandline/latest/nuget.exe
|
||||
- cd %APPVEYOR_BUILD_FOLDER%
|
||||
- git submodule update --init --recursive
|
||||
|
||||
# pre-build script
|
||||
before_build:
|
||||
@@ -29,12 +31,13 @@ before_build:
|
||||
# build step
|
||||
build:
|
||||
verbosity: minimal
|
||||
project: DICUI.sln
|
||||
|
||||
# post-build step
|
||||
after_build:
|
||||
- ps: appveyor DownloadFile https://github.com/aaru-dps/Aaru/releases/download/v5.1.0.3214/aaru-5.1.0.3214-1_windows_x64.zip
|
||||
- ps: appveyor DownloadFile http://www.chrysocome.net/downloads/8ab730cd2a29e76ddd89be1f99357942/dd-0.6beta3.zip
|
||||
- ps: appveyor DownloadFile https://github.com/saramibreak/DiscImageCreator/files/4931645/DiscImageCreator_20200716.zip
|
||||
- ps: appveyor DownloadFile https://github.com/saramibreak/DiscImageCreator/files/5253259/DiscImageCreator_20200921.zip
|
||||
- ps: appveyor DownloadFile https://archive.org/download/subdump_fua_0x28/subdump_fua_0x28.zip
|
||||
|
||||
- 7z e aaru-5.1.0.3214-1_windows_x64.zip -oDICUI\bin\Debug\net472\Programs\Aaru *
|
||||
@@ -51,12 +54,12 @@ after_build:
|
||||
- 7z e dd-0.6beta3.zip -oDICUI.Avalonia\bin\Debug\net48\Programs\DD *
|
||||
- 7z e dd-0.6beta3.zip -oDICUI.Avalonia\bin\Debug\netcoreapp3.1\Programs\DD *
|
||||
|
||||
- 7z e DiscImageCreator_20200716.zip -oDICUI\bin\Debug\net472\Programs\Creator Release_ANSI\*
|
||||
- 7z e DiscImageCreator_20200716.zip -oDICUI\bin\Debug\net48\Programs\Creator Release_ANSI\*
|
||||
- 7z e DiscImageCreator_20200716.zip -oDICUI\bin\Debug\netcoreapp3.1\Programs\Creator Release_ANSI\*
|
||||
- 7z e DiscImageCreator_20200716.zip -oDICUI.Avalonia\bin\Debug\net472\Programs\Creator Release_ANSI\*
|
||||
- 7z e DiscImageCreator_20200716.zip -oDICUI.Avalonia\bin\Debug\net48\Programs\Creator Release_ANSI\*
|
||||
- 7z e DiscImageCreator_20200716.zip -oDICUI.Avalonia\bin\Debug\netcoreapp3.1\Programs\Creator Release_ANSI\*
|
||||
- 7z e DiscImageCreator_20200921.zip -oDICUI\bin\Debug\net472\Programs\Creator Release_ANSI\*
|
||||
- 7z e DiscImageCreator_20200921.zip -oDICUI\bin\Debug\net48\Programs\Creator Release_ANSI\*
|
||||
- 7z e DiscImageCreator_20200921.zip -oDICUI\bin\Debug\netcoreapp3.1\Programs\Creator Release_ANSI\*
|
||||
- 7z e DiscImageCreator_20200921.zip -oDICUI.Avalonia\bin\Debug\net472\Programs\Creator Release_ANSI\*
|
||||
- 7z e DiscImageCreator_20200921.zip -oDICUI.Avalonia\bin\Debug\net48\Programs\Creator Release_ANSI\*
|
||||
- 7z e DiscImageCreator_20200921.zip -oDICUI.Avalonia\bin\Debug\netcoreapp3.1\Programs\Creator Release_ANSI\*
|
||||
|
||||
- 7z e subdump_fua_0x28.zip -oDICUI\bin\Debug\net472 *
|
||||
- mkdir DICUI\bin\Debug\net472\Programs\Subdump
|
||||
@@ -77,15 +80,40 @@ after_build:
|
||||
- mkdir DICUI.Avalonia\bin\Debug\netcoreapp3.1\Programs\Subdump
|
||||
- mv DICUI.Avalonia\bin\Debug\netcoreapp3.1\subdump_fua_0x28.exe DICUI.Avalonia\bin\Debug\netcoreapp3.1\Programs\Subdump\subdump.exe
|
||||
|
||||
- 7z a DICUI.zip DICUI\bin\Debug\*
|
||||
- 7z a DICUI-Avalonia.zip DICUI.Avalonia\bin\Debug\*
|
||||
- 7z a DICUI-Check.zip DICUI.Check\bin\Debug\*
|
||||
- cd DICUI\bin\Debug
|
||||
- 7z a DICUI_net472.zip net472\*
|
||||
- 7z a DICUI_net48.zip net48\*
|
||||
- 7z a DICUI_netcoreapp3.1.zip netcoreapp3.1\*
|
||||
|
||||
- cd ..\..\..\DICUI.Avalonia\bin\Debug
|
||||
- 7z a DICUI-Avalonia_net472.zip net472\*
|
||||
- 7z a DICUI-Avalonia_net48.zip net48\*
|
||||
- 7z a DICUI-Avalonia_netcoreapp3.1.zip netcoreapp3.1\*
|
||||
|
||||
- cd ..\..\..\DICUI.Check\bin\Debug
|
||||
- 7z a DICUI-Check_net472.zip net472\*
|
||||
- 7z a DICUI-Check_net48.zip net48\*
|
||||
- 7z a DICUI-Check_netcoreapp3.1.zip netcoreapp3.1\*
|
||||
|
||||
# artifact linking
|
||||
artifacts:
|
||||
- path: DICUI.zip
|
||||
name: DICUI (WPF UI)
|
||||
- path: DICUI-Avalonia.zip
|
||||
name: DICUI (Avalonia UI)
|
||||
- path: DICUI-Check.zip
|
||||
name: DICUI Check
|
||||
- path: DICUI\bin\Debug\DICUI_net472.zip
|
||||
name: DICUI (WPF UI) (.NET Framework 4.7.2)
|
||||
- path: DICUI\bin\Debug\DICUI_net48.zip
|
||||
name: DICUI (WPF UI) (.NET Framework 4.8)
|
||||
- path: DICUI\bin\Debug\DICUI_netcoreapp3.1.zip
|
||||
name: DICUI (WPF UI) (.NET Core 3.1)
|
||||
|
||||
- path: DICUI.Avalonia\bin\Debug\DICUI-Avalonia_net472.zip
|
||||
name: DICUI (Avalonia UI) (.NET Framework 4.7.2)
|
||||
- path: DICUI.Avalonia\bin\Debug\DICUI-Avalonia_net48.zip
|
||||
name: DICUI (Avalonia UI) (.NET Framework 4.8)
|
||||
- path: DICUI.Avalonia\bin\Debug\DICUI-Avalonia_netcoreapp3.1.zip
|
||||
name: DICUI (Avalonia UI) (.NET Core 3.1)
|
||||
|
||||
- path: DICUI.Check\bin\Debug\DICUI-Check_net472.zip
|
||||
name: DICUI Check (.NET Framework 4.7.2)
|
||||
- path: DICUI.Check\bin\Debug\DICUI-Check_net48.zip
|
||||
name: DICUI Check (.NET Framework 4.8)
|
||||
- path: DICUI.Check\bin\Debug\DICUI-Check_netcoreapp3.1.zip
|
||||
name: DICUI Check (.NET Core 3.1)
|
||||
Reference in New Issue
Block a user