Compare commits

..

71 Commits
1.17 ... 1.18

Author SHA1 Message Date
Matt Nadareski
24150c7b3c Bump version to 1.18 2020-11-10 10:55:29 -08:00
Matt Nadareski
040720a1c2 Detect HD-DVD-Video 2020-11-08 13:40:30 -08:00
Matt Nadareski
70d30d370f Update to BurnOutSharp 1.5.0 2020-11-03 21:49:59 -08:00
Matt Nadareski
a50b99da07 Use older version of DIC for now 2020-11-01 16:08:57 -08:00
Matt Nadareski
2cecbe69ad Support the /ps command for DIC 2020-10-31 23:05:11 -07:00
Matt Nadareski
557410660f Update to DIC 20201101 2020-10-31 22:55:07 -07:00
Matt Nadareski
0d75dbaaa2 Add dumping program selection finally 2020-10-11 22:06:40 -07:00
Matt Nadareski
d38f95c73a Check sector 0 for Saturn, if possible (fixes #241) 2020-10-11 21:15:35 -07:00
Matt Nadareski
c9d59a90e4 Default to CD-ROM when detection fails as well 2020-10-11 21:07:28 -07:00
Matt Nadareski
fc6a34d987 Use CD-ROM as default when no type detection (fixes #237) 2020-10-11 20:46:24 -07:00
Matt Nadareski
8acabb692f Remove default config file (fixes #238) 2020-10-11 20:36:12 -07:00
Matt Nadareski
61f8871839 Change default for fixed drive detection (fixes #233) 2020-10-11 20:35:39 -07:00
Matt Nadareski
69d61ad185 Use selected drive in UI for copy protect scan (fixes #235) 2020-10-11 20:31:27 -07:00
Matt Nadareski
27391ed31b Remove subdump (fixes #234)
Note that it still downloaads the subdump executable and puts it in the same place. This won't be changing anytime soon since it's useful. It also is flat out removing the option in the menu without replacing or reshaping the menu. This will be addressed at a later date.
2020-10-11 20:24:41 -07:00
Matt Nadareski
ed56c92101 Clean up sector read method a bit more (nw) 2020-10-08 21:21:11 -07:00
Matt Nadareski
ab8ae1524f Actually use the drive letter (nw) 2020-10-08 14:18:23 -07:00
Matt Nadareski
de7c247583 Add initial version of sector reading to library 2020-10-08 12:26:28 -07:00
Matt Nadareski
997fd8f7ac Why do I keep forgetting changelist? 2020-10-01 11:46:55 -07:00
Matt Nadareski
e39e07246a Fix archive naming 2020-10-01 11:34:27 -07:00
Matt Nadareski
e0b0406d76 Let's see how badly this goes... 2020-10-01 11:28:39 -07:00
Matt Nadareski
1efbe9a784 Update BurnOutSharp version; fix autobuild 2020-10-01 09:44:49 -07:00
Matt Nadareski
fab921c2dd Added feature request template 2020-09-29 13:01:28 -07:00
Matt Nadareski
56896b4bea Update issue templates 2020-09-29 12:57:19 -07:00
Matt Nadareski
54eb7d8ecd Forgot the changelog 2020-09-28 11:26:47 -07:00
Matt Nadareski
114587b9f6 Reset paragraph state on clear (fixes #231) 2020-09-28 11:22:40 -07:00
Matt Nadareski
cad33c6c07 Don't lose automatic comments 2020-09-24 15:25:56 -07:00
Matt Nadareski
8154999f1c Fix alternate mainInfo parsing 2020-09-24 15:08:17 -07:00
Matt Nadareski
3a30c14085 Make this apparent on the main page 2020-09-24 11:42:28 -07:00
Matt Nadareski
db8b1df480 Add rolling changelog section 2020-09-24 11:35:16 -07:00
Matt Nadareski
e20cac8a80 Sync 2020-09-24 10:57:09 -07:00
Matt Nadareski
ce9b4e39f5 Add more internal info for GD-ROM LD 2020-09-24 10:49:27 -07:00
Matt Nadareski
ddff4b2e58 SCD has internal serial 2020-09-24 10:26:51 -07:00
Matt Nadareski
16a470475c Remember, no Disc 2020-09-24 10:21:35 -07:00
Matt Nadareski
6e01473e87 Saturn serial -> internal serial, strip 'V' from version 2020-09-24 10:19:34 -07:00
Matt Nadareski
8682089151 Update to DIC 20200921 2020-09-23 10:11:38 -07:00
Matt Nadareski
f45cb0075f Remove superflous check for PVD retrieval (fixes #229) 2020-09-19 14:31:17 -07:00
Matt Nadareski
afccb48798 Specify solution file 2020-09-19 13:40:36 -07:00
Matt Nadareski
ce6995fba0 It was seriously a directory marker issue 2020-09-17 22:04:31 -07:00
Matt Nadareski
3328d1adea Try without the git extension 2020-09-17 22:00:51 -07:00
Matt Nadareski
33d79df9a8 Update AppVeyor 2020-09-17 21:59:07 -07:00
Matt Nadareski
4fb64b19d6 Remove weird csproj stuff 2020-09-17 21:57:00 -07:00
Matt Nadareski
bf0f495c8b Ignore all files except the compiled cicm.cs 2020-09-17 21:54:50 -07:00
Matt Nadareski
5d5f8e8d8c Use official repo as submodule for CICM 2020-09-17 21:52:40 -07:00
Matt Nadareski
91aa248355 A little more cleanup and safety 2020-09-17 21:38:45 -07:00
Matt Nadareski
633c6c1efb Slight tweaks, mostly naming 2020-09-17 21:25:22 -07:00
Matt Nadareski
facb1f673f Wrap login call with try/catch 2020-09-17 15:52:29 -07:00
Matt Nadareski
9c8938c7f2 Get some more layerbreak stuff 2020-09-17 13:30:35 -07:00
Matt Nadareski
17f75f3ce6 First parts of layerbreak info 2020-09-17 10:37:04 -07:00
Matt Nadareski
b99c48afa2 First part of DVD protection extraction for Aaru 2020-09-16 20:45:07 -07:00
Matt Nadareski
9481fada23 Fix writing of valid indexes for Aaru cuesheet 2020-09-16 17:13:15 -07:00
Matt Nadareski
7efbc5043c Add error count for Aaru outputs 2020-09-16 17:00:11 -07:00
Matt Nadareski
92880d3148 Fix discs with ISO extension 2020-09-16 16:25:33 -07:00
Matt Nadareski
9800f2b8ae Use spans instead of Linq (and accidental commit) 2020-09-16 16:09:34 -07:00
Matt Nadareski
a5d01604cb Strip out hard-to-find CD Check instances 2020-09-16 16:01:16 -07:00
Matt Nadareski
aaab84f90a Fix multiple things in Aaru
Addresses overlooked issue with DatFile generation, consolidates common code into methods, fixes PVD generation and write
2020-09-16 15:59:11 -07:00
Matt Nadareski
318a1a303c Fix multiline outputs 2020-09-16 15:58:02 -07:00
Matt Nadareski
0061de6b2e Fix cuesheet write-to-stream 2020-09-16 15:57:22 -07:00
Matt Nadareski
b3db7c547f Generate cuesheet using new classes for Aaru 2020-09-16 14:31:29 -07:00
Matt Nadareski
1da29464a8 Add writing of cuesheets 2020-09-16 14:09:02 -07:00
Matt Nadareski
d1d4ff41c6 Fix reading (assumes reasonably sized cuesheets) 2020-09-16 13:41:00 -07:00
Matt Nadareski
673c2745a9 Fix index check 2020-09-16 13:00:42 -07:00
Matt Nadareski
4ad88441cc Add parsing in of cuesheets 2020-09-16 12:53:28 -07:00
Matt Nadareski
71bb822856 Add cuesheet structures 2020-09-16 11:22:33 -07:00
Matt Nadareski
2a7789bd12 Bump version to 1.17.1 2020-09-15 10:03:39 -07:00
Matt Nadareski
1d0417cc1c Fix DiscInformationWindow issues 2020-09-14 13:51:26 -07:00
Matt Nadareski
905c578d90 Fix DIC flags based on the code 2020-09-14 13:30:14 -07:00
Matt Nadareski
a457c85e53 Better internal handling of Options window 2020-09-13 21:01:53 -07:00
Matt Nadareski
36630fc0ed Fix the tests I broke 2020-09-13 20:52:08 -07:00
Matt Nadareski
f509ac60da Make DiscInformationWindow more robust 2020-09-13 20:41:23 -07:00
Matt Nadareski
bf8aac6f81 Move some constants around 2020-09-13 20:41:09 -07:00
Matt Nadareski
ceddcc0722 Remove useless passthrough vars 2020-09-12 14:11:55 -07:00
49 changed files with 2827 additions and 11297 deletions

View 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
View 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
View File

@@ -0,0 +1,3 @@
[submodule "DICUI.Library/Aaru/CICMMetadata"]
path = DICUI.Library/Aaru/CICMMetadata
url = https://github.com/claunia/CICMMetadata

View File

@@ -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

View 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?; }
}
}
}

View File

@@ -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);

View File

@@ -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" />

View File

@@ -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;

View File

@@ -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();

View File

@@ -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"

View File

@@ -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();
}

View File

@@ -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>

View File

@@ -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

View File

@@ -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>

View File

@@ -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

View 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 files 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;
}
}
}
}

View 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}");
}
}
}

View 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);
}
}
}
}
}

View 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 tracks 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();
}
}
}

View 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}");
}
}
}

View 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}");
}
}
}

View File

@@ -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" />

View File

@@ -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>

View File

@@ -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;

View File

@@ -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";

View File

@@ -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:

View File

@@ -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,

View File

@@ -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 = "";

View File

@@ -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;
}
}
}
}

View File

@@ -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("");

View File

@@ -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
}
}

View File

@@ -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
}
}
}

View File

@@ -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;
}

View File

@@ -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());
}
}

View File

@@ -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>

View 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?; }
}
}
}

View File

@@ -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);

View File

@@ -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>

View File

@@ -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>

View File

@@ -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

View File

@@ -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;

View File

@@ -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)

View File

@@ -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();

View File

@@ -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"

View File

@@ -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

View File

@@ -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

View File

@@ -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)