Compare commits

...

52 Commits
3.1.4 ... 3.1.9

Author SHA1 Message Date
Matt Nadareski
f97e293ad2 Bump version 2024-05-19 19:48:51 -04:00
Deterous
2a040effde Prefer PlayStation info from Redumper logs (#702)
* Prefer PlayStation info from Redumper logs

* Restrict parsing PS data to info field

* Typo

* Readd version field
2024-05-19 19:45:25 -04:00
Matt Nadareski
862e676590 Update BinaryObjectScanner to 3.1.12 2024-05-18 22:30:27 -04:00
Deterous
bffa70bcc9 Add Xbox Backup Creator support to MPF.Check (#701)
* Initial XBC support

* Complete but untested XBC support

* Update changelog

* Fix SS recreation bug

* Parse XeMID from DMI

* Nitpicks
2024-05-16 23:11:33 -04:00
Matt Nadareski
bb596c49f4 Use IO implementation of IniFile 2024-05-16 12:35:39 -04:00
Matt Nadareski
917986530b Remove now-unused Hash enum 2024-05-16 12:29:48 -04:00
Matt Nadareski
14bc7609c5 Update BinaryObjectScanner to 3.1.11 2024-05-15 20:44:15 -04:00
Matt Nadareski
a2361c34bc Update RedumpLib and related 2024-05-15 17:17:23 -04:00
Matt Nadareski
3d29eeb3c3 Add site code listing to Check 2024-05-15 15:49:40 -04:00
Matt Nadareski
c908a55ce6 Get volume label from UIC outputs 2024-05-15 11:57:34 -04:00
Deterous
c2b3932363 Trim PIC for XboxOne/XboxSX (#700) 2024-05-14 10:34:11 -04:00
Deterous
b4d47aea37 Fix XboxOne/XboxSX Filename bug (#698) 2024-05-10 09:13:38 -04:00
Deterous
f8d3ae7bc7 Fix CleanRip not pulling info (#697) 2024-05-10 08:09:20 -04:00
Matt Nadareski
9f50277888 Update Redumper to build 325 2024-05-09 09:32:06 -04:00
Matt Nadareski
96f826994a Bump version 2024-05-09 08:56:53 -04:00
Matt Nadareski
eda3c97465 Note 2024-05-08 23:36:27 -04:00
Matt Nadareski
ff380451db Note 2024-05-08 23:35:35 -04:00
Matt Nadareski
a9ee6667d0 Add _PFI.bin support for UIC (fixes #696) 2024-05-07 19:17:48 -04:00
Matt Nadareski
54415241d2 Critical update to BinaryObjectScanner 3.1.10 2024-05-07 09:03:42 -04:00
Matt Nadareski
79d2957ede Omit false positives on formatting protections 2024-05-06 22:58:23 -04:00
Matt Nadareski
0b5d52da7d Note 2024-05-06 22:04:26 -04:00
Matt Nadareski
274ad9fc9a Note 2024-05-06 22:01:39 -04:00
Matt Nadareski
a2217b536b Note 2024-05-06 21:51:14 -04:00
Deterous
43e7883ac9 Option for default Redumper leadin retries (#693)
* Option for default Redumper leadin retries

* Gate custom default leadin retries behind option

* typo

* False by default

* change wording

* whitespace?

* better whitespace?
2024-05-02 23:41:07 -04:00
Matt Nadareski
c37d098eee Bump version 2024-04-28 20:17:48 -04:00
Matt Nadareski
17c2ca6fa8 Critical update to BinaryObjectScanner 3.1.9 2024-04-28 19:57:10 -04:00
Matt Nadareski
4b2d30bc01 Bump version 2024-04-27 19:37:49 -04:00
Matt Nadareski
ec8b65a7fa Update packages 2024-04-26 22:21:41 -04:00
Matt Nadareski
ec5611f5ff Update packages 2024-04-24 18:12:05 -04:00
Deterous
3e350b666b Custom non-redump Redumper options (#691)
* Gate advanced Redumper options in UI, add read method + sector order

* Update changelog

* Fixed-width text/combo boxes
2024-04-16 10:11:09 -04:00
Deterous
e83f69fc3e Define better default categories (#689) 2024-04-08 09:37:31 -04:00
Deterous
6ecbbb6978 Fix parameter parsing for = symbol (#687) 2024-04-05 21:27:57 -04:00
Matt Nadareski
771483ac14 Bump version 2024-04-05 16:03:28 -04:00
Matt Nadareski
ccde878286 Update BinaryObjectScanner to 3.1.5 2024-04-05 15:58:44 -04:00
Matt Nadareski
e0ab3e048b Enable remaining fields for label-side information 2024-04-05 15:35:54 -04:00
Matt Nadareski
cf2ae163c4 Enable label-side mastering SID and toolstamp 2024-04-05 15:30:37 -04:00
Deterous
5025a3e91a Fix CleanRip hash output for Check (#685)
* ClrMamePro format is for CDs, dont assume hashes are in CleanRip logs

* Rename folder

* Rename folder again

* Update changelog

* Don't trust cleanrip log hashes if any are missing
2024-04-05 08:54:07 -04:00
Matt Nadareski
dab774dab3 Fix test project project includes 2024-04-02 22:58:02 -04:00
Matt Nadareski
04c6131d28 Enable Windows targeting for test project 2024-04-02 22:28:10 -04:00
Deterous
47561baee8 Detect Xbox Series X discs (#683)
* Detect Xbox Series X discs via catalog.js deserialization

* Update changelog
2024-04-02 19:02:05 -07:00
Matt Nadareski
a8b1a8342d Update BinaryObjectScanner to 3.1.4 2024-04-02 17:10:49 -04:00
Matt Nadareski
7b8ef00d59 Merge with main 2024-04-02 16:39:42 -04:00
Matt Nadareski
65cc502a94 Update packages 2024-04-02 16:38:51 -04:00
fuzzball
d38b465b08 Fix information pulling for redumper (#682)
* Fix dat pull failure

* Fix c2 error exception

* Update changelist
2024-03-30 09:00:58 -07:00
Matt Nadareski
783c323fd0 Update BinaryObjectScanner to 3.1.3 2024-03-27 12:09:19 -04:00
Matt Nadareski
04af8807e5 Language selections unchecked by default (fixes #681) 2024-03-23 12:17:37 -04:00
Matt Nadareski
1260dfdff2 Fix missing information in README 2024-03-18 12:45:11 -04:00
Matt Nadareski
e5b883fb73 Fix outdated information in README 2024-03-18 12:44:24 -04:00
Matt Nadareski
1c08451487 Add Konami Python 2 system detection (fixes #680) 2024-03-18 12:30:02 -04:00
Matt Nadareski
29b483f805 Read last instance of hash data from Redumper 2024-03-18 12:20:11 -04:00
Matt Nadareski
2eff4a7488 Read C2 error count from Redumper logs (fixes #666) 2024-03-18 12:15:56 -04:00
Matt Nadareski
5e94d02503 Handle .0.physical files from Redumper (fixes #679) 2024-03-17 19:23:25 -04:00
38 changed files with 2019 additions and 656 deletions

View File

@@ -30,10 +30,10 @@ jobs:
- name: Bundle Redumper
run: |
wget https://github.com/superg/redumper/releases/download/build_311/redumper-2024.01.08_build311-win64.zip
unzip redumper-2024.01.08_build311-win64.zip
wget https://github.com/superg/redumper/releases/download/build_325/redumper-2024.05.06_build325-win64.zip
unzip redumper-2024.05.06_build325-win64.zip
mkdir -p MPF/bin/Debug/${{ matrix.framework }}/${{ matrix.runtime }}/publish/Programs/Redumper
mv redumper-2024.01.08_build311-win64/bin/redumper.exe MPF/bin/Debug/${{ matrix.framework }}/${{ matrix.runtime }}/publish/Programs/Redumper/
mv redumper-2024.05.06_build325-win64/bin/redumper.exe MPF/bin/Debug/${{ matrix.framework }}/${{ matrix.runtime }}/publish/Programs/Redumper/
- name: Archive build
run: zip -r ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_debug.zip ${{ matrix.project }}/bin/Debug/${{ matrix.framework }}/${{ matrix.runtime }}/publish/

View File

@@ -1,3 +1,59 @@
### 3.1.9 (2024-05-19)
- Update Redumper to build 325
- Fix CleanRip not pulling info (Deterous)
- Fix XboxOne/XboxSX Filename bug (Deterous)
- Trim PIC for XboxOne/XboxSX (Deterous)
- Get volume label from UIC outputs
- Add site code listing to Check
- Update RedumpLib and related
- Update BinaryObjectScanner to 3.1.11
- Remove now-unused Hash enum
- Use IO implementation of IniFile
- Add Xbox Backup Creator support to MPF.Check (Deterous)
- Update BinaryObjectScanner to 3.1.12
- Prefer PlayStation info from Redumper logs (Deterous)
### 3.1.8 (2024-05-09)
- Option for default Redumper leadin retries (Deterous)
- Omit false positives on formatting protections
- Critical update to BinaryObjectScanner 3.1.10
- Add _PFI.bin support for UIC
### 3.1.7 (2024-04-28)
- Critical update to BinaryObjectScanner 3.1.9
### 3.1.6 (2024-04-27)
- Fix parameter parsing for `=` symbol (Deterous)
- Define better default categories (Deterous)
- Custom non-redump Redumper options (Deterous)
- Update packages
- Update packages
### 3.1.5 (2024-04-05)
- Handle `.0.physical` files from Redumpers
- Read C2 error count from Redumper logs
- Read last instance of hash data from Redumper
- Add Konami Python 2 system detection
- Fix outdated information in README
- Fix missing information in README
- Language selections unchecked by default
- Update BinaryObjectScanner to 3.1.3
- Fix information pulling for redumper (fuzz6001)
- Update packages
- Update BinaryObjectScanner to 3.1.4
- Detect Xbox Series X discs (Deterous)
- Enable Windows targeting for test project
- Fix test project project includes
- Fix CleanRip hash output for Check (Deterous)
- Enable label-side mastering SID and toolstamp
- Enable remaining fields for label-side information
- Update BinaryObjectScanner to 3.1.5
### 3.1.4 (2024-03-16)
- Update BinaryObjectScanner to 3.1.2

View File

@@ -11,7 +11,7 @@
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<VersionPrefix>3.1.4</VersionPrefix>
<VersionPrefix>3.1.9</VersionPrefix>
<!-- Package Properties -->
<Title>MPF Check</Title>
@@ -32,10 +32,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.1.2" GeneratePathProperty="true">
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.1.12" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.6" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.8" />
</ItemGroup>
<ItemGroup>

View File

@@ -99,6 +99,7 @@ namespace MPF.Check
Console.WriteLine();
Console.WriteLine("Standalone Options:");
Console.WriteLine("-h, -? Show this help text");
Console.WriteLine("-lc, --listcodes List supported comment/content site codes");
Console.WriteLine("-lm, --listmedia List supported media types");
Console.WriteLine("-ls, --listsystems List supported system types");
Console.WriteLine("-lp, --listprograms List supported dumping program outputs");

View File

@@ -103,6 +103,7 @@ namespace MPF.Core.Converters
InternalProgram.DCDumper => "DCDumper",
InternalProgram.PS3CFW => "PS3 CFW",
InternalProgram.UmdImageCreator => "UmdImageCreator",
InternalProgram.XboxBackupCreator => "XboxBackupCreator",
#endregion
@@ -111,7 +112,44 @@ namespace MPF.Core.Converters
};
}
#endregion
/// <summary>
/// Get the string representation of the RedumperReadMethod enum values
/// </summary>
/// <param name="method">RedumperReadMethod value to convert</param>
/// <returns>String representing the value, if possible</returns>
public static string LongName(this RedumperReadMethod? method)
{
return (method) switch
{
RedumperReadMethod.D8 => "D8",
RedumperReadMethod.BE => "BE",
RedumperReadMethod.BE_CDDA => "BE_CDDA",
RedumperReadMethod.NONE => "Default",
_ => "Unknown",
};
}
/// <summary>
/// Get the string representation of the RedumperSectorOrder enum values
/// </summary>
/// <param name="order">RedumperSectorOrder value to convert</param>
/// <returns>String representing the value, if possible</returns>
public static string LongName(this RedumperSectorOrder? order)
{
return (order) switch
{
RedumperSectorOrder.DATA_C2_SUB => "DATA_C2_SUB",
RedumperSectorOrder.DATA_SUB_C2 => "DATA_SUB_C2",
RedumperSectorOrder.DATA_SUB => "DATA_SUB",
RedumperSectorOrder.DATA_C2 => "DATA_C2",
RedumperSectorOrder.NONE => "Default",
_ => "Unknown",
};
}
#endregion
#region Convert From String
@@ -151,6 +189,11 @@ namespace MPF.Core.Converters
or "umd"
or "umdcreator"
or "umdimagecreator" => InternalProgram.UmdImageCreator,
"xbc"
or "xbox"
or "xbox360"
or "xboxcreator"
or "xboxbackupcreator" => InternalProgram.XboxBackupCreator,
_ => InternalProgram.NONE,
};
@@ -299,6 +342,56 @@ namespace MPF.Core.Converters
};
}
/// <summary>
/// Get the RedumperReadMethod enum value for a given string
/// </summary>
/// <param name="method">String value to convert</param>
/// <returns>RedumperReadMethod represented by the string, if possible</returns>
public static RedumperReadMethod ToRedumperReadMethod(string? method)
{
return (method?.ToLowerInvariant()) switch
{
"d8" => RedumperReadMethod.D8,
"be" => RedumperReadMethod.BE,
"be_cdda"
or "be cdda"
or "be-cdda"
or "becdda" => RedumperReadMethod.BE_CDDA,
_ => RedumperReadMethod.NONE,
};
}
/// <summary>
/// Get the RedumperSectorOrder enum value for a given string
/// </summary>
/// <param name="order">String value to convert</param>
/// <returns>RedumperSectorOrder represented by the string, if possible</returns>
public static RedumperSectorOrder ToRedumperSectorOrder(string? order)
{
return (order?.ToLowerInvariant()) switch
{
"data_c2_sub"
or "data c2 sub"
or "data-c2-sub"
or "datac2sub" => RedumperSectorOrder.DATA_C2_SUB,
"data_sub_c2"
or "data sub c2"
or "data-sub-c2"
or "datasubc2" => RedumperSectorOrder.DATA_SUB_C2,
"data_sub"
or "data sub"
or "data-sub"
or "datasub" => RedumperSectorOrder.DATA_SUB,
"data_c2"
or "data c2"
or "data-c2"
or "datac2" => RedumperSectorOrder.DATA_C2,
_ => RedumperSectorOrder.NONE,
};
}
#endregion
}
}

View File

@@ -7,6 +7,7 @@ using Microsoft.Management.Infrastructure;
using Microsoft.Management.Infrastructure.Generic;
#endif
using MPF.Core.Converters;
using SabreTools.IO;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Data
@@ -291,6 +292,12 @@ namespace MPF.Core.Data
return RedumpSystem.funworldPhotoPlay;
}
// Konami Python 2
if (Directory.Exists(Path.Combine(this.Name, "PY2.D")))
{
return RedumpSystem.KonamiPython2;
}
#endregion
#region Consoles
@@ -363,17 +370,43 @@ namespace MPF.Core.Data
}
catch { }
// Microsoft Xbox One
// Microsoft Xbox One and Series X
try
{
if (Directory.Exists(Path.Combine(this.Name, "MSXC"))
#if NET20 || NET35
&& Directory.GetFiles(Path.Combine(this.Name, "MSXC")).Any())
#else
&& Directory.EnumerateFiles(Path.Combine(this.Name, "MSXC")).Any())
#endif
if (Directory.Exists(Path.Combine(this.Name, "MSXC")))
{
return RedumpSystem.MicrosoftXboxOne;
try
{
#if NET20 || NET35
string catalogjs = Path.Combine(this.Name, Path.Combine("MSXC", Path.Combine("Metadata", "catalog.js")));
#else
string catalogjs = Path.Combine(this.Name, "MSXC", "Metadata", "catalog.js");
#endif
if (!File.Exists(catalogjs))
return RedumpSystem.MicrosoftXboxOne;
SabreTools.Models.Xbox.Catalog? catalog = SabreTools.Serialization.Deserializers.Catalog.DeserializeFile(catalogjs);
if (catalog != null && catalog.Version != null && catalog.Packages != null)
{
if (!double.TryParse(catalog.Version, out double version))
return RedumpSystem.MicrosoftXboxOne;
if (version < 4)
return RedumpSystem.MicrosoftXboxOne;
foreach (var package in catalog.Packages)
{
if (package.Generation != "9")
return RedumpSystem.MicrosoftXboxOne;
}
return RedumpSystem.MicrosoftXboxSeriesXS;
}
}
catch
{
return RedumpSystem.MicrosoftXboxOne;
}
}
}
catch { }

View File

@@ -1,25 +1,5 @@
namespace MPF.Core.Data
{
/// <summary>
/// Available hashing types
/// </summary>
public enum Hash
{
CRC32,
#if NET6_0_OR_GREATER
CRC64,
#endif
MD5,
SHA1,
SHA256,
SHA384,
SHA512,
#if NET6_0_OR_GREATER
XxHash32,
XxHash64,
#endif
}
/// <summary>
/// Drive type for dumping
/// </summary>
@@ -48,6 +28,32 @@
DCDumper,
PS3CFW,
UmdImageCreator,
XboxBackupCreator,
}
/// <summary>
/// Drive read method option
/// </summary>
public enum RedumperReadMethod
{
NONE = 0,
BE,
D8,
BE_CDDA,
}
/// <summary>
/// Drive sector order option
/// </summary>
public enum RedumperSectorOrder
{
NONE = 0,
DATA_C2_SUB,
DATA_SUB_C2,
DATA_SUB,
DATA_C2,
}
/// <summary>

View File

@@ -1,294 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace MPF.Core.Data
{
public class IniFile : IDictionary<string, string>
{
private Dictionary<string, string> _keyValuePairs = [];
public string this[string key]
{
get
{
_keyValuePairs ??= [];
key = key.ToLowerInvariant();
if (_keyValuePairs.TryGetValue(key, out string? val))
return val;
return string.Empty;
}
set
{
_keyValuePairs ??= [];
key = key.ToLowerInvariant();
_keyValuePairs[key] = value;
}
}
/// <summary>
/// Create an empty INI file
/// </summary>
public IniFile()
{
}
/// <summary>
/// Populate an INI file from path
/// </summary>
public IniFile(string path)
{
this.Parse(path);
}
/// <summary>
/// Populate an INI file from stream
/// </summary>
public IniFile(Stream stream)
{
this.Parse(stream);
}
/// <summary>
/// Add or update a key and value to the INI file
/// </summary>
public void AddOrUpdate(string key, string value)
{
_keyValuePairs[key.ToLowerInvariant()] = value;
}
/// <summary>
/// Remove a key from the INI file
/// </summary>
public void Remove(string key)
{
_keyValuePairs.Remove(key.ToLowerInvariant());
}
/// <summary>
/// Read an INI file based on the path
/// </summary>
public bool Parse(string path)
{
// If we don't have a file, we can't read it
if (!File.Exists(path))
return false;
using var fileStream = File.OpenRead(path);
return Parse(fileStream);
}
/// <summary>
/// Read an INI file from a stream
/// </summary>
public bool Parse(Stream stream)
{
// If the stream is invalid or unreadable, we can't process it
if (stream == null || !stream.CanRead || stream.Position >= stream.Length - 1)
return false;
// Keys are case-insensitive by default
try
{
using var sr = new StreamReader(stream);
string section = string.Empty;
while (!sr.EndOfStream)
{
var line = sr.ReadLine()?.Trim();
// Empty lines are skipped
if (string.IsNullOrEmpty(line))
{
// No-op, we don't process empty lines
}
// Comments start with ';'
else if (line!.StartsWith(";"))
{
// No-op, we don't process comments
}
// Section titles are surrounded by square brackets
else if (line.StartsWith("["))
{
section = line.TrimStart('[').TrimEnd(']');
}
// Valid INI lines are in the format key=value
else if (line.Contains('='))
{
// Split the line by '=' for key-value pairs
string[] data = line.Split('=');
// If the value field contains an '=', we need to put them back in
string key = data[0].Trim();
string value = string.Join("=", data.Skip(1).ToArray()).Trim();
// Section names are prepended to the key with a '.' separating
if (!string.IsNullOrEmpty(section))
key = $"{section}.{key}";
// Set or overwrite keys in the returned dictionary
_keyValuePairs[key.ToLowerInvariant()] = value;
}
// All other lines are ignored
}
}
catch
{
// We don't care what the error was, just catch and return
return false;
}
return true;
}
/// <summary>
/// Write an INI file to a path
/// </summary>
public bool Write(string path)
{
// If we don't have a valid dictionary with values, we can't write out
if (_keyValuePairs == null || _keyValuePairs.Count == 0)
return false;
using var fileStream = File.OpenWrite(path);
return Write(fileStream);
}
/// <summary>
/// Write an INI file to a stream
/// </summary>
public bool Write(Stream stream)
{
// If we don't have a valid dictionary with values, we can't write out
if (_keyValuePairs == null || _keyValuePairs.Count == 0)
return false;
// If the stream is invalid or unwritable, we can't output to it
if (stream == null || !stream.CanWrite || stream.Position >= stream.Length - 1)
return false;
try
{
// Order the dictionary by keys to link sections together
using var sw = new StreamWriter(stream);
var orderedKeyValuePairs = _keyValuePairs.OrderBy(kvp => kvp.Key);
string section = string.Empty;
foreach (var keyValuePair in orderedKeyValuePairs)
{
// Extract the key and value
string key = keyValuePair.Key;
string value = keyValuePair.Value;
// We assume '.' is a section name separator
if (key.Contains('.'))
{
// Split the key by '.'
string[] data = keyValuePair.Key.Split('.');
// If the key contains an '.', we need to put them back in
string newSection = data[0].Trim();
key = string.Join(".", data.Skip(1).ToArray()).Trim();
// If we have a new section, write it out
if (!string.Equals(newSection, section, StringComparison.OrdinalIgnoreCase))
{
sw.WriteLine($"[{newSection}]");
section = newSection;
}
}
// Now write out the key and value in a standardized way
sw.WriteLine($"{key}={value}");
}
}
catch
{
// We don't care what the error was, just catch and return
return false;
}
return true;
}
#region IDictionary Impelementations
public ICollection<string> Keys => ((IDictionary<string, string>)_keyValuePairs).Keys;
public ICollection<string> Values => ((IDictionary<string, string>)_keyValuePairs).Values;
public int Count => ((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Count;
public bool IsReadOnly => ((ICollection<KeyValuePair<string, string>>)_keyValuePairs).IsReadOnly;
public void Add(string key, string value)
{
((IDictionary<string, string>)_keyValuePairs).Add(key.ToLowerInvariant(), value);
}
bool IDictionary<string, string>.Remove(string key)
{
return ((IDictionary<string, string>)_keyValuePairs).Remove(key.ToLowerInvariant());
}
public bool TryGetValue(string key, out string value)
{
bool result = ((IDictionary<string, string>)_keyValuePairs).TryGetValue(key.ToLowerInvariant(), out var temp);
value = temp ?? string.Empty;
return result;
}
public void Add(KeyValuePair<string, string> item)
{
var newItem = new KeyValuePair<string, string>(item.Key.ToLowerInvariant(), item.Value);
((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Add(newItem);
}
public void Clear()
{
((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Clear();
}
public bool Contains(KeyValuePair<string, string> item)
{
var newItem = new KeyValuePair<string, string>(item.Key.ToLowerInvariant(), item.Value);
return ((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Contains(newItem);
}
public bool ContainsKey(string key)
{
return _keyValuePairs.ContainsKey(key.ToLowerInvariant());
}
public void CopyTo(KeyValuePair<string, string>[] array, int arrayIndex)
{
((ICollection<KeyValuePair<string, string>>)_keyValuePairs).CopyTo(array, arrayIndex);
}
public bool Remove(KeyValuePair<string, string> item)
{
var newItem = new KeyValuePair<string, string>(item.Key.ToLowerInvariant(), item.Value);
return ((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Remove(newItem);
}
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
return ((IEnumerable<KeyValuePair<string, string>>)_keyValuePairs).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)_keyValuePairs).GetEnumerator();
}
#endregion
}
}

View File

@@ -316,6 +316,15 @@ namespace MPF.Core.Data
set { Settings["RedumperEnableDebug"] = value.ToString(); }
}
/// <summary>
/// Enable Redumper custom lead-in retries for Plextor drives
/// </summary>
public bool RedumperEnableLeadinRetry
{
get { return GetBooleanSetting(Settings, "RedumperEnableLeadinRetry", false); }
set { Settings["RedumperEnableLeadinRetry"] = value.ToString(); }
}
/// <summary>
/// Enable verbose output while dumping by default
/// </summary>
@@ -326,12 +335,21 @@ namespace MPF.Core.Data
}
/// <summary>
/// Enable BE reading by default with Redumper
/// Default number of redumper Plextor leadin retries
/// </summary>
public bool RedumperUseBEReading
public int RedumperLeadinRetryCount
{
get { return GetBooleanSetting(Settings, "RedumperUseBEReading", false); }
set { Settings["RedumperUseBEReading"] = value.ToString(); }
get { return GetInt32Setting(Settings, "RedumperLeadinRetryCount", 4); }
set { Settings["RedumperLeadinRetryCount"] = value.ToString(); }
}
/// <summary>
/// Enable options incompatible with redump submissions
/// </summary>
public bool RedumperNonRedumpMode
{
get { return GetBooleanSetting(Settings, "RedumperNonRedumpMode", false); }
set { Settings["RedumperNonRedumpMode"] = value.ToString(); }
}
/// <summary>
@@ -343,6 +361,38 @@ namespace MPF.Core.Data
set { Settings["RedumperUseGenericDriveType"] = value.ToString(); }
}
/// <summary>
/// Currently selected default redumper read method
/// </summary>
public RedumperReadMethod RedumperReadMethod
{
get
{
var valueString = GetStringSetting(Settings, "RedumperReadMethod", RedumperReadMethod.NONE.ToString());
return EnumConverter.ToRedumperReadMethod(valueString);
}
set
{
Settings["RedumperReadMethod"] = value.ToString();
}
}
/// <summary>
/// Currently selected default redumper sector order
/// </summary>
public RedumperSectorOrder RedumperSectorOrder
{
get
{
var valueString = GetStringSetting(Settings, "RedumperSectorOrder", RedumperSectorOrder.NONE.ToString());
return EnumConverter.ToRedumperSectorOrder(valueString);
}
set
{
Settings["RedumperSectorOrder"] = value.ToString();
}
}
/// <summary>
/// Default number of rereads
/// </summary>

View File

@@ -150,6 +150,7 @@ namespace MPF.Core
InternalProgram.DCDumper => null, // TODO: Create correct parameter type when supported
InternalProgram.PS3CFW => new Modules.PS3CFW.Parameters(parameters) { ExecutablePath = null },
InternalProgram.UmdImageCreator => new Modules.UmdImageCreator.Parameters(parameters) { ExecutablePath = null },
InternalProgram.XboxBackupCreator => new Modules.XboxBackupCreator.Parameters(parameters) { ExecutablePath = null },
// If no dumping program found, set to null
InternalProgram.NONE => null,

View File

@@ -14,6 +14,7 @@ using MPF.Core.Modules;
using MPF.Core.Utilities;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SabreTools.IO;
using SabreTools.Models.PIC;
using SabreTools.RedumpLib.Data;
using Formatting = Newtonsoft.Json.Formatting;
@@ -168,7 +169,7 @@ namespace MPF.Core
{
try
{
return new SabreTools.Serialization.Files.PIC().Deserialize(pic);
return SabreTools.Serialization.Deserializers.PIC.DeserializeFile(pic);
}
catch
{
@@ -384,6 +385,11 @@ namespace MPF.Core
return di.Units[0]?.Body?.DiscTypeIdentifier;
}
/// <summary>
/// Get the EXE name from a PlayStation disc, if possible
/// </summary>
/// <param name="driveLetter">Drive letter to use to check</param>
/// <returns>Executable name on success, null otherwise</returns>
internal static string? GetPlayStationExecutableName(char? driveLetter)
{
// If there's no drive letter, we can't get exe name
@@ -395,6 +401,11 @@ namespace MPF.Core
return GetPlayStationExecutableName(drivePath);
}
/// <summary>
/// Get the EXE name from a PlayStation disc, if possible
/// </summary>
/// <param name="drivePath">Drive path to use to check</param>
/// <returns>Executable name on success, null otherwise</returns>
internal static string? GetPlayStationExecutableName(string? drivePath)
{
// If there's no drive path, we can't get exe name
@@ -411,7 +422,7 @@ namespace MPF.Core
// Read the CNF file as an INI file
var systemCnf = new IniFile(systemCnfPath);
string bootValue = string.Empty;
string? bootValue = string.Empty;
// PlayStation uses "BOOT" as the key
if (systemCnf.ContainsKey("BOOT"))
@@ -451,7 +462,7 @@ namespace MPF.Core
/// <param name="serial">Internal disc serial, if possible</param>
/// <param name="region">Output region, if possible</param>
/// <param name="date">Output EXE date in "yyyy-mm-dd" format if possible, null on error</param>
/// <returns></returns>
/// <returns>True if information could be determined, false otherwise</returns>
internal static bool GetPlayStationExecutableInfo(char? driveLetter, out string? serial, out Region? region, out string? date)
{
serial = null; region = null; date = null;
@@ -472,7 +483,7 @@ namespace MPF.Core
/// <param name="serial">Internal disc serial, if possible</param>
/// <param name="region">Output region, if possible</param>
/// <param name="date">Output EXE date in "yyyy-mm-dd" format if possible, null on error</param>
/// <returns></returns>
/// <returns>True if information could be determined, false otherwise</returns>
internal static bool GetPlayStationExecutableInfo(string? drivePath, out string? serial, out Region? region, out string? date)
{
serial = null; region = null; date = null;
@@ -1003,7 +1014,7 @@ namespace MPF.Core
}
}
#endregion
#endregion
#region Category Extraction

View File

@@ -10,7 +10,7 @@
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<VersionPrefix>3.1.4</VersionPrefix>
<VersionPrefix>3.1.9</VersionPrefix>
<!-- Package Properties -->
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
@@ -48,16 +48,16 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.1.2" GeneratePathProperty="true">
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.1.12" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="LibIRD" Version="0.9.0" />
<PackageReference Include="LibIRD" Version="0.9.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="psxt001z.Library" Version="0.21.0-beta4" />
<PackageReference Include="SabreTools.Hashing" Version="1.1.4" />
<PackageReference Include="SabreTools.Models" Version="1.4.0" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.6" />
<PackageReference Include="SabreTools.Serialization" Version="1.4.0" />
<PackageReference Include="psxt001z.Library" Version="0.21.0-rc1" />
<PackageReference Include="SabreTools.Hashing" Version="1.2.0" />
<PackageReference Include="SabreTools.Models" Version="1.4.8" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.8" />
<PackageReference Include="SabreTools.Serialization" Version="1.6.5" />
</ItemGroup>
</Project>

View File

@@ -2486,7 +2486,7 @@ namespace MPF.Core.Modules.Aaru
cueSheet.Files = [.. cueFiles];
if (cueSheet != null && cueSheet != default)
{
var ms = new SabreTools.Serialization.Streams.CueSheet().Serialize(cueSheet);
var ms = SabreTools.Serialization.Serializers.CueSheet.SerializeStream(cueSheet);
if (ms == null)
return null;

View File

@@ -994,11 +994,9 @@ namespace MPF.Core.Modules
if (!IsFlagSupported(longFlagString))
return null;
string[] commandParts = parts[i].Split('=');
if (commandParts.Length != 2)
return null;
int loc = parts[i].IndexOf('=');
string valuePart = commandParts[1];
string valuePart = parts[i].Substring(loc + 1);
this[longFlagString] = true;
return valuePart.Trim('"');
@@ -1200,6 +1198,8 @@ namespace MPF.Core.Modules
if (trimLength > -1)
hex = hex.Substring(0, trimLength);
// TODO: Check for non-zero values in discarded PIC
return Regex.Replace(hex, ".{32}", "$0\n", RegexOptions.Compiled);
}
catch
@@ -1209,6 +1209,60 @@ namespace MPF.Core.Modules
}
}
/// <summary>
/// Get a isobuster-formatted PVD from a 2048 byte-per-sector image, if possible
/// </summary>
/// <param name="isoPath">Path to ISO file</param>
/// <param name="pvd">Formatted PVD string, otherwise null</param>
/// <returns>True if PVD was successfully parsed, otherwise false</returns>
protected static bool GetPVD(string isoPath, out string? pvd)
{
pvd = null;
try
{
// Get PVD bytes from ISO file
var buf = new byte[96];
using (FileStream iso = File.OpenRead(isoPath))
{
// TODO: Don't hardcode 0x8320
iso.Seek(0x8320, SeekOrigin.Begin);
int offset = 0;
while (offset < 96)
{
int read = iso.Read(buf, offset, buf.Length - offset);
if (read == 0)
throw new EndOfStreamException();
offset += read;
}
}
// Format PVD to isobuster standard
char[] pvdCharArray = new char[96];
for (int i = 0; i < 96; i++)
{
if (buf[i] >= 0x20 && buf[i] <= 0x7E)
pvdCharArray[i] = (char)buf[i];
else
pvdCharArray[i] = '.';
}
string pvdASCII = new string(pvdCharArray, 0, 96);
pvd = string.Empty;
for (int i = 0; i < 96; i += 16)
{
pvd += $"{(0x0320 + i):X4} : {buf[i]:X2} {buf[i + 1]:X2} {buf[i + 2]:X2} {buf[i + 3]:X2} {buf[i + 4]:X2} {buf[i + 5]:X2} {buf[i + 6]:X2} {buf[i + 7]:X2} " +
$"{buf[i + 8]:X2} {buf[i + 9]:X2} {buf[i + 10]:X2} {buf[i + 11]:X2} {buf[i + 12]:X2} {buf[i + 13]:X2} {buf[i + 14]:X2} {buf[i + 15]:X2} {pvdASCII.Substring(i, 16)}\n";
}
return true;
}
catch
{
// We don't care what the error is
return false;
}
}
#endregion
}
}

View File

@@ -4,6 +4,7 @@ using System.Linq;
using System.Text.RegularExpressions;
using MPF.Core.Converters;
using MPF.Core.Data;
using SabreTools.Hashing;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
@@ -71,8 +72,6 @@ namespace MPF.Core.Modules.CleanRip
// Get the Datafile information
var datafile = GenerateCleanripDatafile(basePath + ".iso", basePath + "-dumpinfo.txt");
// Fill in the hash data
info.TracksAndWriteOffsets!.ClrMameProData = InfoTool.GenerateDatfile(datafile);
// Get the individual hash data, as per internal
@@ -182,6 +181,17 @@ namespace MPF.Core.Modules.CleanRip
sha1 = line.Substring(7);
}
// Ensure all checksums were found in log
if (crc == string.Empty || md5 == string.Empty || sha1 == string.Empty)
{
if (HashTool.GetStandardHashes(iso, out long isoSize, out string? isoCRC, out string? isoMD5, out string? isoSHA1))
{
crc = isoCRC ?? crc;
md5 = isoMD5 ?? md5;
sha1 = isoSHA1 ?? sha1;
}
}
return new Datafile
{
Games =

View File

@@ -5,6 +5,7 @@ using System.Linq;
using System.Text.RegularExpressions;
using MPF.Core.Converters;
using MPF.Core.Data;
using MPF.Core.Utilities;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
@@ -497,6 +498,8 @@ namespace MPF.Core.Modules.DiscImageCreator
int trimLength = -1;
switch (this.System)
{
case RedumpSystem.MicrosoftXboxOne:
case RedumpSystem.MicrosoftXboxSeriesXS:
case RedumpSystem.SonyPlayStation3:
case RedumpSystem.SonyPlayStation4:
case RedumpSystem.SonyPlayStation5:
@@ -558,9 +561,9 @@ namespace MPF.Core.Modules.DiscImageCreator
string xmidString;
if (string.IsNullOrEmpty(outputDirectory))
xmidString = GetXGD1XMID($"{basePath}_DMI.bin");
xmidString = Tools.GetXGD1XMID($"{basePath}_DMI.bin");
else
xmidString = GetXGD1XMID(Path.Combine(outputDirectory, $"{basePath}_DMI.bin"));
xmidString = Tools.GetXGD1XMID(Path.Combine(outputDirectory, $"{basePath}_DMI.bin"));
var xmid = SabreTools.Serialization.Wrappers.XMID.Create(xmidString);
if (xmid != null)
@@ -607,9 +610,9 @@ namespace MPF.Core.Modules.DiscImageCreator
case RedumpSystem.MicrosoftXbox360:
string xemidString;
if (string.IsNullOrEmpty(outputDirectory))
xemidString = GetXGD23XeMID($"{basePath}_DMI.bin");
xemidString = Tools.GetXGD23XeMID($"{basePath}_DMI.bin");
else
xemidString = GetXGD23XeMID(Path.Combine(outputDirectory, $"{basePath}_DMI.bin"));
xemidString = Tools.GetXGD23XeMID(Path.Combine(outputDirectory, $"{basePath}_DMI.bin"));
var xemid = SabreTools.Serialization.Wrappers.XeMID.Create(xemidString);
if (xemid != null)
@@ -3884,50 +3887,6 @@ namespace MPF.Core.Modules.DiscImageCreator
}
}
/// <summary>
/// Get the XGD1 Master ID (XMID) information
/// </summary>
/// <param name="dmi">DMI.bin file location</param>
/// <returns>String representation of the XGD1 DMI information, empty string on error</returns>
private static string GetXGD1XMID(string dmi)
{
if (!File.Exists(dmi))
return string.Empty;
try
{
using var br = new BinaryReader(File.OpenRead(dmi));
br.BaseStream.Seek(8, SeekOrigin.Begin);
return new string(br.ReadChars(8));
}
catch
{
return string.Empty;
}
}
/// <summary>
/// Get the XGD2/3 Master ID (XeMID) information
/// </summary>
/// <param name="dmi">DMI.bin file location</param>
/// <returns>String representation of the XGD2/3 DMI information, empty string on error</returns>
private static string GetXGD23XeMID(string dmi)
{
if (!File.Exists(dmi))
return string.Empty;
try
{
using var br = new BinaryReader(File.OpenRead(dmi));
br.BaseStream.Seek(64, SeekOrigin.Begin);
return new string(br.ReadChars(14));
}
catch
{
return string.Empty;
}
}
#endregion
}
}

View File

@@ -1,7 +1,5 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using MPF.Core.Converters;
using MPF.Core.Data;
@@ -188,60 +186,6 @@ namespace MPF.Core.Modules.PS3CFW
}
}
// TODO: Don't hardcode 0x8320 and move this function to BaseParameters
/// <summary>
/// Get a isobuster-formatted PVD from a PS3 ISO, if possible
/// </summary>
/// <param name="isoPath">Path to ISO file</param>
/// <param name="pvd">Formatted PVD string, otherwise null</param>
/// <returns>True if PVD was successfully parsed, otherwise false</returns>
private static bool GetPVD(string isoPath, out string? pvd)
{
pvd = null;
try
{
// Get PVD bytes from ISO file
var buf = new byte[96];
using (FileStream iso = File.OpenRead(isoPath))
{
iso.Seek(0x8320, SeekOrigin.Begin);
int offset = 0;
while (offset < 96)
{
int read = iso.Read(buf, offset, buf.Length - offset);
if (read == 0)
throw new EndOfStreamException();
offset += read;
}
}
// Format PVD to isobuster standard
char[] pvdCharArray = new char[96];
for (int i = 0; i < 96; i++)
{
if (buf[i] >= 0x20 && buf[i] <= 0x7E)
pvdCharArray[i] = (char)buf[i];
else
pvdCharArray[i] = '.';
}
string pvdASCII = new string(pvdCharArray, 0, 96);
pvd = string.Empty;
for (int i = 0; i < 96; i += 16)
{
pvd += $"{(0x0320+i):X4} : {buf[i]:X2} {buf[i+1]:X2} {buf[i+2]:X2} {buf[i+3]:X2} {buf[i+4]:X2} {buf[i+5]:X2} {buf[i+6]:X2} {buf[i+7]:X2} " +
$"{buf[i+8]:X2} {buf[i+9]:X2} {buf[i+10]:X2} {buf[i+11]:X2} {buf[i+12]:X2} {buf[i+13]:X2} {buf[i+14]:X2} {buf[i+15]:X2} {pvdASCII.Substring(i, 16)}\n";
}
return true;
}
catch
{
// We don't care what the error is
return false;
}
}
/// <summary>
/// Get a formatted datfile from the PS3 CFW output, if possible
/// </summary>

View File

@@ -10,6 +10,7 @@ namespace MPF.Core.Modules.Redumper
public const string DVD = "dvd"; // Synonym for CD
public const string BluRay = "bd"; // Synonym for CD
public const string SACD = "sacd"; // Synonym for CD
public const string New = "new"; // Synonym for CD; Temporary command, to be removed later
public const string Rings = "rings";
public const string Dump = "dump";
public const string DumpNew = "dumpnew"; // Temporary command, to be removed later
@@ -77,6 +78,7 @@ namespace MPF.Core.Modules.Redumper
public const string DumpWriteOffset = "--dump-write-offset";
public const string DumpReadSize = "--dump-read-size";
public const string OverreadLeadout = "--overread-leadout";
public const string ForceUnscrambled = "--force-unscrambled";
public const string LegacySubs = "--legacy-subs";
public const string DisableCDText = "--disable-cdtext";
}

View File

@@ -231,7 +231,7 @@ namespace MPF.Core.Modules.Redumper
missingFiles.Add($"{basePath}.dat");
if (!File.Exists($"{basePath}.manufacturer") && !File.Exists($"{basePath}.1.manufacturer") && !File.Exists($"{basePath}.2.manufacturer"))
missingFiles.Add($"{basePath}.manufacturer");
if (!File.Exists($"{basePath}.physical") && !File.Exists($"{basePath}.1.physical") && !File.Exists($"{basePath}.2.physical"))
if (!File.Exists($"{basePath}.physical") && !File.Exists($"{basePath}.0.physical") && !File.Exists($"{basePath}.1.physical") && !File.Exists($"{basePath}.2.physical"))
missingFiles.Add($"{basePath}.physical");
if (!File.Exists($"{basePath}.state"))
missingFiles.Add($"{basePath}.state");
@@ -256,7 +256,7 @@ namespace MPF.Core.Modules.Redumper
missingFiles.Add($"{basePath}.log");
else if (GetDatfile($"{basePath}.log") == null)
missingFiles.Add($"{basePath}.dat");
if (!File.Exists($"{basePath}.physical") && !File.Exists($"{basePath}.1.physical") && !File.Exists($"{basePath}.2.physical"))
if (!File.Exists($"{basePath}.physical") && !File.Exists($"{basePath}.0.physical") && !File.Exists($"{basePath}.1.physical") && !File.Exists($"{basePath}.2.physical"))
missingFiles.Add($"{basePath}.physical");
if (!File.Exists($"{basePath}.state"))
missingFiles.Add($"{basePath}.state");
@@ -320,8 +320,11 @@ namespace MPF.Core.Modules.Redumper
info.TracksAndWriteOffsets.OtherWriteOffsets = cdWriteOffset;
// Attempt to get the error count
long errorCount = GetErrorCount($"{basePath}.log");
info.CommonDiscInfo.ErrorsCount = (errorCount == -1 ? "Error retrieving error count" : errorCount.ToString());
if (GetErrorCount($"{basePath}.log", out long redumpErrors, out long c2Errors))
{
info.CommonDiscInfo.ErrorsCount = (redumpErrors == -1 ? "Error retrieving error count" : redumpErrors.ToString());
info.DumpingInfo.C2ErrorsCount = (c2Errors == -1 ? "Error retrieving error count" : c2Errors.ToString());
}
// Attempt to get multisession data
string cdMultiSessionInfo = GetMultisessionInformation($"{basePath}.log") ?? string.Empty;
@@ -373,6 +376,8 @@ namespace MPF.Core.Modules.Redumper
int trimLength = -1;
switch (this.System)
{
case RedumpSystem.MicrosoftXboxOne:
case RedumpSystem.MicrosoftXboxSeriesXS:
case RedumpSystem.SonyPlayStation3:
case RedumpSystem.SonyPlayStation4:
case RedumpSystem.SonyPlayStation5:
@@ -385,9 +390,14 @@ namespace MPF.Core.Modules.Redumper
break;
}
info.Extras!.PIC = GetPIC($"{basePath}.physical", trimLength) ?? string.Empty;
info.Extras!.PIC = GetPIC($"{basePath}.physical", trimLength)
?? GetPIC($"{basePath}.0.physical", trimLength)
?? GetPIC($"{basePath}.1.physical", trimLength)
?? string.Empty;
var di = InfoTool.GetDiscInformation($"{basePath}.physical");
var di = InfoTool.GetDiscInformation($"{basePath}.physical")
?? InfoTool.GetDiscInformation($"{basePath}.0.physical")
?? InfoTool.GetDiscInformation($"{basePath}.1.physical");
info.SizeAndChecksums!.PICIdentifier = InfoTool.GetPICIdentifier(di);
}
@@ -413,16 +423,28 @@ namespace MPF.Core.Modules.Redumper
break;
case RedumpSystem.KonamiPython2:
info.CommonDiscInfo!.EXEDateBuildDate = GetEXEDate($"{basePath}.log");
// Get metadata from log if possible
if (GetPlayStationInfo($"{basePath}.log", out string? kp2EXEDate, out string? kp2Serial, out string? kp2Version, out var _))
{
info.CommonDiscInfo!.EXEDateBuildDate = kp2EXEDate;
info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName] = kp2Serial ?? string.Empty;
if (!string.IsNullOrEmpty(info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName]))
info.CommonDiscInfo.Region = InfoTool.GetPlayStationRegion(info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName]);
info.VersionAndEditions!.Version = kp2Version ?? string.Empty;
}
// Get metadata from drive if not available from log
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var pythonTwoSerial, out Region? pythonTwoRegion, out var pythonTwoDate))
{
// Ensure internal serial is pulled from local data
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = pythonTwoSerial ?? string.Empty;
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? pythonTwoRegion;
info.CommonDiscInfo.EXEDateBuildDate ??= pythonTwoDate;
if (string.IsNullOrEmpty(info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName]))
info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName] = pythonTwoSerial ?? string.Empty;
info.CommonDiscInfo.Region ??= pythonTwoRegion;
if (string.IsNullOrEmpty(info.CommonDiscInfo.EXEDateBuildDate))
info.CommonDiscInfo.EXEDateBuildDate = pythonTwoDate;
}
info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
if (string.IsNullOrEmpty(info.VersionAndEditions!.Version))
info.VersionAndEditions.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
break;
case RedumpSystem.MicrosoftXbox:
@@ -478,13 +500,24 @@ namespace MPF.Core.Modules.Redumper
break;
case RedumpSystem.SonyPlayStation:
info.CommonDiscInfo!.EXEDateBuildDate = GetEXEDate($"{basePath}.log");
// Get metadata from log if possible
if (GetPlayStationInfo($"{basePath}.log", out string? psxEXEDate, out string? psxSerial, out var _, out var _))
{
info.CommonDiscInfo!.EXEDateBuildDate = psxEXEDate;
info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName] = psxSerial ?? string.Empty;
if (!string.IsNullOrEmpty(info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName]))
info.CommonDiscInfo.Region = InfoTool.GetPlayStationRegion(info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName]);
}
// Get metadata from drive if not available from log
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationSerial, out Region? playstationRegion, out var playstationDate))
{
// Ensure internal serial is pulled from local data
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = playstationSerial ?? string.Empty;
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationRegion;
info.CommonDiscInfo.EXEDateBuildDate ??= playstationDate;
if (string.IsNullOrEmpty(info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName]))
info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName] = playstationSerial ?? string.Empty;
info.CommonDiscInfo.Region ??= playstationRegion;
if (string.IsNullOrEmpty(info.CommonDiscInfo.EXEDateBuildDate))
info.CommonDiscInfo.EXEDateBuildDate = playstationDate;
}
info.CopyProtection!.AntiModchip = GetPlayStationAntiModchipDetected($"{basePath}.log").ToYesNo();
@@ -494,34 +527,70 @@ namespace MPF.Core.Modules.Redumper
break;
case RedumpSystem.SonyPlayStation2:
info.CommonDiscInfo!.EXEDateBuildDate = GetEXEDate($"{basePath}.log");
// Get metadata from log if possible
if (GetPlayStationInfo($"{basePath}.log", out string? ps2EXEDate, out string? ps2Serial, out var ps2Version, out var _))
{
info.CommonDiscInfo!.EXEDateBuildDate = ps2EXEDate;
info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName] = ps2Serial ?? string.Empty;
if (!string.IsNullOrEmpty(info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName]))
info.CommonDiscInfo.Region = InfoTool.GetPlayStationRegion(info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName]);
info.VersionAndEditions!.Version = ps2Version ?? string.Empty;
}
// Get metadata from drive if not available from log
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationTwoSerial, out Region? playstationTwoRegion, out var playstationTwoDate))
{
// Ensure internal serial is pulled from local data
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = playstationTwoSerial ?? string.Empty;
if (string.IsNullOrEmpty(info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName]))
info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName] = playstationTwoSerial ?? string.Empty;
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationTwoRegion;
info.CommonDiscInfo.EXEDateBuildDate ??= playstationTwoDate;
if (string.IsNullOrEmpty(info.CommonDiscInfo.EXEDateBuildDate))
info.CommonDiscInfo.EXEDateBuildDate ??= playstationTwoDate;
}
info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
if (string.IsNullOrEmpty(info.VersionAndEditions!.Version))
info.VersionAndEditions.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
break;
case RedumpSystem.SonyPlayStation3:
info.VersionAndEditions!.Version = InfoTool.GetPlayStation3Version(drive?.Name) ?? string.Empty;
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation3Serial(drive?.Name) ?? string.Empty;
string? firmwareVersion = InfoTool.GetPlayStation3FirmwareVersion(drive?.Name);
// Get metadata from log if possible
if (GetPlayStationInfo($"{basePath}.log", out var _, out string? ps3Serial, out var ps3Version, out string? firmwareVersion))
{
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = ps3Serial ?? string.Empty;
info.VersionAndEditions!.Version = ps3Version ?? string.Empty;
}
// Get metadata from drive if not available from log
if (string.IsNullOrEmpty(info.VersionAndEditions!.Version))
info.VersionAndEditions.Version = InfoTool.GetPlayStation3Version(drive?.Name) ?? string.Empty;
if (string.IsNullOrEmpty(info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName]))
info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation3Serial(drive?.Name) ?? string.Empty;
firmwareVersion ??= InfoTool.GetPlayStation3FirmwareVersion(drive?.Name);
if (firmwareVersion != null)
info.CommonDiscInfo!.ContentsSpecialFields![SiteCode.Patches] = $"PS3 Firmware {firmwareVersion}";
info.CommonDiscInfo.ContentsSpecialFields![SiteCode.Patches] = $"PS3 Firmware {firmwareVersion}";
break;
case RedumpSystem.SonyPlayStation4:
info.VersionAndEditions!.Version = InfoTool.GetPlayStation4Version(drive?.Name) ?? string.Empty;
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation4Serial(drive?.Name) ?? string.Empty;
if (GetPlayStationInfo($"{basePath}.log", out var _, out string? ps4Serial, out var ps4Version, out var _))
{
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = ps4Serial ?? string.Empty;
info.VersionAndEditions!.Version = ps4Version ?? string.Empty;
}
if (string.IsNullOrEmpty(info.VersionAndEditions!.Version))
info.VersionAndEditions.Version = InfoTool.GetPlayStation4Version(drive?.Name) ?? string.Empty;
if (string.IsNullOrEmpty(info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName]))
info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation4Serial(drive?.Name) ?? string.Empty;
break;
case RedumpSystem.SonyPlayStation5:
info.VersionAndEditions!.Version = InfoTool.GetPlayStation5Version(drive?.Name) ?? string.Empty;
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation5Serial(drive?.Name) ?? string.Empty;
if (GetPlayStationInfo($"{basePath}.log", out var _, out string? ps5Serial, out var ps5Version, out var _))
{
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = ps5Serial ?? string.Empty;
info.VersionAndEditions!.Version = ps5Version ?? string.Empty;
}
if (string.IsNullOrEmpty(info.VersionAndEditions!.Version))
info.VersionAndEditions.Version = InfoTool.GetPlayStation5Version(drive?.Name) ?? string.Empty;
if (string.IsNullOrEmpty(info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName]))
info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation5Serial(drive?.Name) ?? string.Empty;
break;
}
@@ -549,6 +618,8 @@ namespace MPF.Core.Modules.Redumper
info.Artifacts["manufacturer2"] = GetBase64(GetFullFile($"{basePath}.2.manufacturer")) ?? string.Empty;
if (File.Exists($"{basePath}.physical"))
info.Artifacts["physical"] = GetBase64(GetFullFile($"{basePath}.physical")) ?? string.Empty;
if (File.Exists($"{basePath}.0.physical"))
info.Artifacts["physical0"] = GetBase64(GetFullFile($"{basePath}.0.physical")) ?? string.Empty;
if (File.Exists($"{basePath}.1.physical"))
info.Artifacts["physical1"] = GetBase64(GetFullFile($"{basePath}.1.physical")) ?? string.Empty;
if (File.Exists($"{basePath}.2.physical"))
@@ -806,6 +877,10 @@ namespace MPF.Core.Modules.Redumper
if (this[FlagStrings.OverreadLeadout] == true)
parameters.Add(FlagStrings.OverreadLeadout);
// Force Unscrambled
if (this[FlagStrings.ForceUnscrambled] == true)
parameters.Add(FlagStrings.ForceUnscrambled);
// Legacy Subs
if (this[FlagStrings.LegacySubs] == true)
parameters.Add(FlagStrings.LegacySubs);
@@ -874,6 +949,7 @@ namespace MPF.Core.Modules.Redumper
FlagStrings.DumpWriteOffset,
FlagStrings.DumpReadSize,
FlagStrings.OverreadLeadout,
FlagStrings.ForceUnscrambled,
FlagStrings.LegacySubs,
FlagStrings.DisableCDText,
],
@@ -918,7 +994,7 @@ namespace MPF.Core.Modules.Redumper
logFiles.Add($"{basePath}.toc");
// Include .hash and .skeleton for all files in cuesheet
var cueSheet = new SabreTools.Serialization.Files.CueSheet().Deserialize($"{basePath}.cue");
var cueSheet = SabreTools.Serialization.Deserializers.CueSheet.DeserializeFile($"{basePath}.cue");
string? baseDir = Path.GetDirectoryName(basePath);
if (cueSheet?.Files != null && baseDir != null)
{
@@ -958,6 +1034,8 @@ namespace MPF.Core.Modules.Redumper
logFiles.Add($"{basePath}.2.manufacturer");
if (File.Exists($"{basePath}.physical"))
logFiles.Add($"{basePath}.physical");
if (File.Exists($"{basePath}.0.physical"))
logFiles.Add($"{basePath}.0.physical");
if (File.Exists($"{basePath}.1.physical"))
logFiles.Add($"{basePath}.1.physical");
if (File.Exists($"{basePath}.2.physical"))
@@ -976,6 +1054,8 @@ namespace MPF.Core.Modules.Redumper
logFiles.Add($"{basePath}.log");
if (File.Exists($"{basePath}.physical"))
logFiles.Add($"{basePath}.physical");
if (File.Exists($"{basePath}.0.physical"))
logFiles.Add($"{basePath}.0.physical");
if (File.Exists($"{basePath}.1.physical"))
logFiles.Add($"{basePath}.1.physical");
if (File.Exists($"{basePath}.2.physical"))
@@ -998,7 +1078,9 @@ namespace MPF.Core.Modules.Redumper
|| this.BaseCommand?.Contains(CommandStrings.DVD) == true
|| this.BaseCommand?.Contains(CommandStrings.BluRay) == true
|| this.BaseCommand?.Contains(CommandStrings.SACD) == true
|| this.BaseCommand?.Contains(CommandStrings.Dump) == true;
|| this.BaseCommand?.Contains(CommandStrings.New) == true
|| this.BaseCommand?.Contains(CommandStrings.Dump) == true
|| this.BaseCommand?.Contains(CommandStrings.DumpNew) == true;
}
/// <inheritdoc/>
@@ -1084,10 +1166,15 @@ namespace MPF.Core.Modules.Redumper
this[FlagStrings.Verbose] = options.RedumperEnableVerbose;
if (options.RedumperEnableDebug)
this[FlagStrings.Debug] = options.RedumperEnableDebug;
if (options.RedumperUseBEReading)
if (options.RedumperReadMethod != RedumperReadMethod.NONE)
{
this[FlagStrings.DriveReadMethod] = true;
DriveReadMethodValue = "BE_CDDA";
DriveReadMethodValue = options.RedumperReadMethod.ToString();
}
if (options.RedumperSectorOrder != RedumperSectorOrder.NONE)
{
this[FlagStrings.DriveSectorOrder] = true;
DriveSectorOrderValue = options.RedumperSectorOrder.ToString();
}
if (options.RedumperUseGenericDriveType)
{
@@ -1115,6 +1202,12 @@ namespace MPF.Core.Modules.Redumper
this[FlagStrings.Retries] = true;
RetriesValue = options.RedumperRereadCount;
if (options.RedumperEnableLeadinRetry)
{
this[FlagStrings.PlextorLeadinRetries] = true;
PlextorLeadinRetriesValue = options.RedumperLeadinRetryCount;
}
}
/// <inheritdoc/>
@@ -1151,6 +1244,7 @@ namespace MPF.Core.Modules.Redumper
case CommandStrings.DVD:
case CommandStrings.BluRay:
case CommandStrings.SACD:
case CommandStrings.New: // Temporary command, to be removed later
case CommandStrings.Rings:
case CommandStrings.Dump:
case CommandStrings.DumpNew: // Temporary command, to be removed later
@@ -1360,6 +1454,9 @@ namespace MPF.Core.Modules.Redumper
// Overread Leadout
ProcessFlagParameter(parts, FlagStrings.OverreadLeadout, ref i);
// Force Unscrambled
ProcessFlagParameter(parts, FlagStrings.ForceUnscrambled, ref i);
// Legacy Subs
ProcessFlagParameter(parts, FlagStrings.LegacySubs, ref i);
@@ -1429,25 +1526,31 @@ namespace MPF.Core.Modules.Redumper
try
{
// Fast forward to the dat line
using var sr = File.OpenText(log);
while (!sr.EndOfStream && sr.ReadLine()?.TrimStart()?.StartsWith("dat:") == false) ;
if (sr.EndOfStream)
return null;
string? datString = null;
// Now that we're at the relevant entries, read each line in and concatenate
var datString = string.Empty;
var line = sr.ReadLine()?.Trim();
while (line?.StartsWith("<rom") == true)
// Find all occurrences of the hash information
while (!sr.EndOfStream)
{
datString += line + "\n";
// Fast forward to the dat line
while (!sr.EndOfStream && sr.ReadLine()?.TrimStart()?.StartsWith("dat:") == false) ;
if (sr.EndOfStream)
break;
line = sr.ReadLine()?.Trim();
// Now that we're at the relevant entries, read each line in and concatenate
datString = string.Empty;
var line = sr.ReadLine()?.Trim();
while (line?.StartsWith("<rom") == true)
{
datString += line + "\n";
if (sr.EndOfStream)
break;
line = sr.ReadLine()?.Trim();
}
}
return datString.TrimEnd('\n');
return datString?.TrimEnd('\n');
}
catch
{
@@ -1655,86 +1758,54 @@ namespace MPF.Core.Modules.Redumper
}
/// <summary>
/// Get the detected error count from the input files, if possible
/// Get the detected error counts from the input files, if possible
/// </summary>
/// <param name="log">Log file location</param>
/// <returns>Error count if possible, -1 on error</returns>
public static long GetErrorCount(string log)
/// <returns>True if error counts could be retrieved, false otherwise</returns>
public static bool GetErrorCount(string log, out long redumpErrors, out long c2Errors)
{
// Set the default values for error counts
redumpErrors = -1; c2Errors = -1;
// If the file doesn't exist, we can't get info from it
if (!File.Exists(log))
return -1;
return false;
try
{
// Fast forward to the errors lines
using var sr = File.OpenText(log);
while (!sr.EndOfStream && sr.ReadLine()?.Trim()?.StartsWith("CD-ROM [") == false) ;
if (sr.EndOfStream)
return 0;
// Now that we're at the relevant lines, find the error count
// Find the error counts
while (!sr.EndOfStream)
{
// Skip forward to the "REDUMP.ORG" line
var line = string.Empty;
while (!sr.EndOfStream && (line = sr.ReadLine()?.Trim())?.StartsWith("REDUMP.ORG errors") == false) ;
if (string.IsNullOrEmpty(line))
var line = sr.ReadLine()?.Trim();
if (line == null)
break;
// REDUMP.ORG errors: <error count>
string[] parts = line!.Split(' ');
if (long.TryParse(parts[2], out long redump))
return redump;
else
return -1;
}
return -1;
}
catch
{
// We don't care what the exception is right now
return -1;
}
}
/// <summary>
/// Get the EXE Date from the log, if possible
/// </summary>
/// <param name="log">Log file location</param>
/// <returns>EXE date if possible, null otherwise</returns>
public static string? GetEXEDate(string log)
{
// If the file doesn't exist, we can't get the info
if (!File.Exists(log))
return null;
try
{
using var sr = File.OpenText(log);
var line = sr.ReadLine();
while (line != null)
{
// Trim the line for later use
line = line.Trim();
// The exe date is listed in a single line
if (line.StartsWith("EXE date:"))
// C2: <error count>
if (line.StartsWith("C2:"))
{
// exe date: yyyy-MM-dd
return line.Substring("EXE date: ".Length);
string[] parts = line.Split(' ');
if (!long.TryParse(parts[1], out c2Errors))
c2Errors = -1;
}
line = sr.ReadLine();
// REDUMP.ORG errors: <error count>
else if (line.StartsWith("REDUMP.ORG errors:"))
{
string[] parts = line!.Split(' ');
if (!long.TryParse(parts[2], out redumpErrors))
redumpErrors = -1;
}
}
return null;
// If the Redump error count is -1, then an issue occurred
return redumpErrors != -1;
}
catch
{
// We don't care what the exception is right now
return null;
return false;
}
}
@@ -1989,6 +2060,75 @@ namespace MPF.Core.Modules.Redumper
}
}
/// <summary>
/// Get the info from a PlayStation disc, if possible
/// </summary>
/// <param name="log">Log file location</param>
/// <returns>True if section found, null on error</returns>
private static bool GetPlayStationInfo(string log, out string? exeDate, out string? serial, out string? version, out string? firmware)
{
// Set the default values
exeDate = null; serial = null; version = null; firmware = null;
// If the file doesn't exist, we can't get info from it
if (!File.Exists(log))
return false;
try
{
// Fast forward to the PS info line
using var sr = File.OpenText(log);
string? line;
while (!sr.EndOfStream)
{
line = sr.ReadLine()?.TrimStart();
if (line?.StartsWith("PSX [") == true ||
line?.StartsWith("PS2 [") == true ||
line?.StartsWith("PS3 [") == true ||
line?.StartsWith("PS4 [") == true ||
line?.StartsWith("PS5 [") == true)
break;
}
if (sr.EndOfStream)
return false;
while (!sr.EndOfStream)
{
line = sr.ReadLine()?.TrimStart();
if (line == null)
break;
if (line.StartsWith("EXE date:"))
{
exeDate = line.Substring("EXE date: ".Length).Trim();
}
else if (line.StartsWith("serial:"))
{
serial = line.Substring("serial: ".Length).Trim();
}
else if (line.StartsWith("version:"))
{
version = line.Substring("version: ".Length).Trim();
}
else if (line.StartsWith("firmware:"))
{
firmware = line.Substring("firmware: ".Length).Trim();
}
else
{
break;
}
}
return true;
}
catch
{
// We don't care what the exception is right now
return false;
}
}
/// <summary>
/// Get the LibCrypt data from the input file, if possible
/// </summary>

View File

@@ -72,6 +72,10 @@ namespace MPF.Core.Modules.UmdImageCreator
info.DumpingInfo!.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
info.DumpingInfo.DumpingDate = InfoTool.GetFileModifiedDate(basePath + "_disc.txt")?.ToString("yyyy-MM-dd HH:mm:ss");
// Fill in the volume labels
if (GetVolumeLabels($"{basePath}_volDesc.txt", out var volLabels))
VolumeLabels = volLabels;
// Extract info based generically on MediaType
switch (this.Type)
{
@@ -114,16 +118,18 @@ namespace MPF.Core.Modules.UmdImageCreator
{
info.Artifacts ??= [];
if (File.Exists(basePath + "_disc.txt"))
info.Artifacts["disc"] = GetBase64(GetFullFile(basePath + "_disc.txt")) ?? string.Empty;
if (File.Exists(basePath + "_drive.txt"))
info.Artifacts["drive"] = GetBase64(GetFullFile(basePath + "_drive.txt")) ?? string.Empty;
if (File.Exists(basePath + "_mainError.txt"))
info.Artifacts["mainError"] = GetBase64(GetFullFile(basePath + "_mainError.txt")) ?? string.Empty;
if (File.Exists(basePath + "_mainInfo.txt"))
info.Artifacts["mainInfo"] = GetBase64(GetFullFile(basePath + "_mainInfo.txt")) ?? string.Empty;
if (File.Exists(basePath + "_volDesc.txt"))
info.Artifacts["volDesc"] = GetBase64(GetFullFile(basePath + "_volDesc.txt")) ?? string.Empty;
if (File.Exists($"{basePath}_disc.txt"))
info.Artifacts["disc"] = GetBase64(GetFullFile($"{basePath}_disc.txt")) ?? string.Empty;
if (File.Exists($"{basePath}_drive.txt"))
info.Artifacts["drive"] = GetBase64(GetFullFile($"{basePath}_drive.txt")) ?? string.Empty;
if (File.Exists($"{basePath}_mainError.txt"))
info.Artifacts["mainError"] = GetBase64(GetFullFile($"{basePath}_mainError.txt")) ?? string.Empty;
if (File.Exists($"{basePath}_mainInfo.txt"))
info.Artifacts["mainInfo"] = GetBase64(GetFullFile($"{basePath}_mainInfo.txt")) ?? string.Empty;
//if (File.Exists($"{basePath}_PFI.bin"))
// info.Artifacts["pfi"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}_PFI.bin")) ?? string.Empty;
if (File.Exists($"{basePath}_volDesc.txt"))
info.Artifacts["volDesc"] = GetBase64(GetFullFile($"{basePath}_volDesc.txt")) ?? string.Empty;
}
}
@@ -145,6 +151,9 @@ namespace MPF.Core.Modules.UmdImageCreator
if (File.Exists($"{basePath}_volDesc.txt"))
logFiles.Add($"{basePath}_volDesc.txt");
if (File.Exists($"{basePath}_PFI.bin"))
logFiles.Add($"{basePath}_PFI.bin");
break;
}
@@ -237,6 +246,90 @@ namespace MPF.Core.Modules.UmdImageCreator
}
}
/// <summary>
/// Get all Volume Identifiers
/// </summary>
/// <param name="volDesc">_volDesc.txt file location</param>
/// <returns>Volume labels (by type), or null if none present</returns>
/// <remarks>This is a copy of the code from DiscImageCreator and has extrandous checks</remarks>
private static bool GetVolumeLabels(string volDesc, out Dictionary<string, List<string>> volLabels)
{
// If the file doesn't exist, can't get the volume labels
volLabels = [];
if (!File.Exists(volDesc))
return false;
try
{
using var sr = File.OpenText(volDesc);
var line = sr.ReadLine();
string volType = "UNKNOWN";
string label;
while (line != null)
{
// Trim the line for later use
line = line.Trim();
// ISO9660 and extensions section
if (line.StartsWith("Volume Descriptor Type: "))
{
Int32.TryParse(line.Substring("Volume Descriptor Type: ".Length), out int volTypeInt);
volType = volTypeInt switch
{
// 0 => "Boot Record" // Should not not contain a Volume Identifier
1 => "ISO", // ISO9660
2 => "Joliet",
// 3 => "Volume Partition Descriptor" // Should not not contain a Volume Identifier
// 255 => "???" // Should not not contain a Volume Identifier
_ => "UNKNOWN" // Should not contain a Volume Identifier
};
}
// UDF section
else if (line.StartsWith("Primary Volume Descriptor Number:"))
{
volType = "UDF";
}
// Identifier
else if (line.StartsWith("Volume Identifier: "))
{
label = line.Substring("Volume Identifier: ".Length);
// Remove leading non-printable character (unsure why DIC outputs this)
if (Convert.ToUInt32(label[0]) == 0x7F || Convert.ToUInt32(label[0]) < 0x20)
label = label.Substring(1);
// Skip if label is blank
if (label == null || label.Length <= 0)
{
volType = "UNKNOWN";
line = sr.ReadLine();
continue;
}
if (volLabels.ContainsKey(label))
volLabels[label].Add(volType);
else
volLabels.Add(label, [volType]);
// Reset volume type
volType = "UNKNOWN";
}
line = sr.ReadLine();
}
// Return true if a volume label was found
return volLabels.Count > 0;
}
catch
{
// We don't care what the exception is right now
volLabels = [];
return false;
}
}
#endregion
}
}

View File

@@ -0,0 +1,677 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using MPF.Core.Converters;
using MPF.Core.Data;
using MPF.Core.Utilities;
using SabreTools.Hashing;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Modules.XboxBackupCreator
{
/// <summary>
/// Represents a generic set of Xbox Backup Creator parameters
/// </summary>
public class Parameters : BaseParameters
{
#region Metadata
/// <inheritdoc/>
public override InternalProgram InternalProgram => InternalProgram.XboxBackupCreator;
#endregion
/// <inheritdoc/>
public Parameters(string? parameters) : base(parameters) { }
/// <inheritdoc/>
public Parameters(RedumpSystem? system, MediaType? type, string? drivePath, string filename, int? driveSpeed, Options options)
: base(system, type, drivePath, filename, driveSpeed, options)
{
}
#region BaseParameters Implementations
/// <inheritdoc/>
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
{
var missingFiles = new List<string>();
switch (this.Type)
{
case MediaType.DVD:
if (!File.Exists($"{basePath}_logs.zip") || !preCheck)
{
string baseDir = Path.GetDirectoryName(basePath) + Path.DirectorySeparatorChar;
string? logPath = GetLogName(baseDir);
if (string.IsNullOrEmpty(logPath))
missingFiles.Add($"{baseDir}Log.txt");
if (!File.Exists($"{baseDir}DMI.bin"))
missingFiles.Add($"{baseDir}DMI.bin");
if (!File.Exists($"{baseDir}PFI.bin"))
missingFiles.Add($"{baseDir}PFI.bin");
if (!File.Exists($"{baseDir}SS.bin"))
missingFiles.Add($"{baseDir}SS.bin");
// Not required from XBC
//if (!File.Exists($"{basePath}.dvd"))
// missingFiles.Add($"{basePath}.dvd");
}
break;
default:
missingFiles.Add("Media and system combination not supported for XboxBackupCreator");
break;
}
return (!missingFiles.Any(), missingFiles);
}
/// <inheritdoc/>
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive? drive, bool includeArtifacts)
{
// Ensure that required sections exist
info = Builder.EnsureAllSections(info);
// Get base directory
string baseDir = Path.GetDirectoryName(basePath) + Path.DirectorySeparatorChar;
// Get log filename
string? logPath = GetLogName(baseDir);
if (string.IsNullOrEmpty(logPath))
return;
// XBC dump info
info.DumpingInfo!.DumpingProgram = $"{EnumConverter.LongName(this.InternalProgram)} {GetVersion(logPath) ?? "Unknown Version"}";
info.DumpingInfo.DumpingDate = InfoTool.GetFileModifiedDate(logPath)?.ToString("yyyy-MM-dd HH:mm:ss");
info.DumpingInfo.Model = GetDrive(logPath) ?? "Unknown Drive";
// Look for read errors
if (GetReadErrors(logPath, out long readErrors))
info.CommonDiscInfo!.ErrorsCount = readErrors == -1 ? "Error retrieving error count" : readErrors.ToString();
// Extract info based generically on MediaType
switch (this.Type)
{
case MediaType.DVD:
// Get Layerbreak from .dvd file if possible
if (GetLayerbreak($"{basePath}.dvd", out long layerbreak))
info.SizeAndChecksums!.Layerbreak = layerbreak;
// Hash data
if (HashTool.GetStandardHashes(basePath + ".iso", out long filesize, out var crc32, out var md5, out var sha1))
{
// Get the Datafile information
var datafile = new Datafile
{
Games = [new Game { Roms = [new Rom { Name = string.Empty, Size = filesize.ToString(), Crc = crc32, Md5 = md5, Sha1 = sha1, }] }]
};
// Fill in the hash data
info.TracksAndWriteOffsets!.ClrMameProData = InfoTool.GenerateDatfile(datafile);
info.SizeAndChecksums!.Size = filesize;
info.SizeAndChecksums.CRC32 = crc32;
info.SizeAndChecksums.MD5 = md5;
info.SizeAndChecksums.SHA1 = sha1;
}
switch (this.System)
{
case RedumpSystem.MicrosoftXbox:
// Parse DMI.bin
string xmidString = Tools.GetXGD1XMID($"{baseDir}DMI.bin");
var xmid = SabreTools.Serialization.Wrappers.XMID.Create(xmidString);
if (xmid != null)
{
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.XMID] = xmidString?.TrimEnd('\0') ?? string.Empty;
info.CommonDiscInfo.Serial = xmid.Serial ?? string.Empty;
if (!options.EnableRedumpCompatibility)
info.VersionAndEditions!.Version = xmid.Version ?? string.Empty;
info.CommonDiscInfo.Region = InfoTool.GetXGDRegion(xmid.Model.RegionIdentifier);
}
break;
case RedumpSystem.MicrosoftXbox360:
// Get PVD from ISO
if (GetPVD(basePath + ".iso", out string? pvd))
info.Extras!.PVD = pvd;
// Parse Media ID
//string? mediaID = GetMediaID(logPath);
// Parse DMI.bin
string xemidString = Tools.GetXGD23XeMID($"{baseDir}DMI.bin");
var xemid = SabreTools.Serialization.Wrappers.XeMID.Create(xemidString);
if (xemid != null)
{
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.XeMID] = xemidString?.TrimEnd('\0') ?? string.Empty;
info.CommonDiscInfo.Serial = xemid.Serial ?? string.Empty;
if (!options.EnableRedumpCompatibility)
info.VersionAndEditions!.Version = xemid.Version ?? string.Empty;
info.CommonDiscInfo.Region = InfoTool.GetXGDRegion(xemid.Model.RegionIdentifier);
}
break;
}
// Deal with SS.bin
if (File.Exists($"{baseDir}SS.bin"))
{
// Save security sector ranges
string? ranges = Tools.GetSSRanges($"{baseDir}SS.bin");
if (!string.IsNullOrEmpty(ranges))
info.Extras!.SecuritySectorRanges = ranges;
// TODO: Determine SS version?
//info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.SSVersion] =
// Recreate RawSS.bin
RecreateSS(logPath!, $"{baseDir}SS.bin", $"{baseDir}RawSS.bin");
// Run ss_sector_range to get repeatable SS hash
Tools.CleanSS($"{baseDir}SS.bin", $"{baseDir}SS.bin");
}
// DMI/PFI/SS CRC32 hashes
if (File.Exists($"{baseDir}DMI.bin"))
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.DMIHash] = HashTool.GetFileHash($"{baseDir}DMI.bin", HashType.CRC32)?.ToUpperInvariant() ?? string.Empty;
if (File.Exists($"{baseDir}PFI.bin"))
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.PFIHash] = HashTool.GetFileHash($"{baseDir}PFI.bin", HashType.CRC32)?.ToUpperInvariant() ?? string.Empty;
if (File.Exists($"{baseDir}SS.bin"))
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.SSHash] = HashTool.GetFileHash($"{baseDir}SS.bin", HashType.CRC32)?.ToUpperInvariant() ?? string.Empty;
break;
}
// Fill in any artifacts that exist, Base64-encoded, if we need to
if (includeArtifacts)
{
info.Artifacts ??= [];
if (File.Exists(logPath))
info.Artifacts["log"] = GetBase64(GetFullFile(logPath!)) ?? string.Empty;
if (File.Exists($"{basePath}.dvd"))
info.Artifacts["dvd"] = GetBase64(GetFullFile($"{basePath}.dvd")) ?? string.Empty;
//if (File.Exists($"{baseDir}DMI.bin"))
// info.Artifacts["dmi"] = Convert.ToBase64String(File.ReadAllBytes($"{baseDir}DMI.bin")) ?? string.Empty;
// TODO: Include PFI artifact only if the hash doesn't match known PFI hashes
//if (File.Exists($"{baseDir}PFI.bin"))
// info.Artifacts["pfi"] = Convert.ToBase64String(File.ReadAllBytes($"{baseDir}PFI.bin")) ?? string.Empty;
//if (File.Exists($"{baseDir}SS.bin"))
// info.Artifacts["ss"] = Convert.ToBase64String(File.ReadAllBytes($"{baseDir}SS.bin")) ?? string.Empty;
//if (File.Exists($"{baseDir}RawSS.bin"))
// info.Artifacts["rawss"] = Convert.ToBase64String(File.ReadAllBytes($"{baseDir}RawSS.bin")) ?? string.Empty;
}
}
/// <inheritdoc/>
public override List<string> GetLogFilePaths(string basePath)
{
var logFiles = new List<string>();
string baseDir = Path.GetDirectoryName(basePath) + Path.DirectorySeparatorChar;
switch (this.Type)
{
case MediaType.DVD:
string? logPath = GetLogName(baseDir);
if (!string.IsNullOrEmpty(logPath))
logFiles.Add(logPath!);
if (File.Exists($"{basePath}.dvd"))
logFiles.Add($"{basePath}.dvd");
if (File.Exists($"{baseDir}DMI.bin"))
logFiles.Add($"{baseDir}DMI.bin");
if (File.Exists($"{baseDir}PFI.bin"))
logFiles.Add($"{baseDir}PFI.bin");
if (File.Exists($"{baseDir}SS.bin"))
logFiles.Add($"{baseDir}SS.bin");
if (File.Exists($"{baseDir}RawSS.bin"))
logFiles.Add($"{baseDir}RawSS.bin");
break;
}
return logFiles;
}
#endregion
#region Information Extraction Methods
/// <summary>
/// Determines the file path of the XBC log
/// </summary>
/// <param name="baseDir">Base directory to search in</param>
/// <returns>Log path if found, null otherwise</returns>
private static string? GetLogName(string baseDir)
{
if (IsSuccessfulLog($"{baseDir}Log.txt"))
return $"{baseDir}Log.txt";
// Search for a renamed log file (assume there is only one)
string[] files = Directory.GetFiles(baseDir, "*.txt", SearchOption.TopDirectoryOnly);
foreach (string file in files)
{
if (IsSuccessfulLog(file))
return file;
}
return null;
}
/// <summary>
/// Checks if Log file has a successful read in it
/// </summary>
/// <param name="log">Path to log file</param>
/// <returns>True if successful log found, false otherwise</returns>
private static bool IsSuccessfulLog(string log)
{
if (!File.Exists(log))
return false;
// Successful Example:
// Read completed in 00:50:23
// Failed Example:
// Read failed
try
{
// If Version is not found, not a valid log file
if (string.IsNullOrEmpty(GetVersion(log)))
return false;
// Look for " Read completed in " in log file
using var sr = File.OpenText(log);
while (!sr.EndOfStream)
{
string? line = sr.ReadLine();
if (line?.StartsWith(" Read completed in ") == true)
{
return true;
}
}
// We couldn't find a successful dump
return false;
}
catch
{
// We don't care what the exception is right now
return false;
}
}
/// <summary>
/// Get the XBC version if possible
/// </summary>
/// <param name="log">Path to XBC log file</param>
/// <returns>Version if possible, null on error</returns>
private static string? GetVersion(string? log)
{
if (string.IsNullOrEmpty(log) || !File.Exists(log))
return null;
// Sample:
// ====================================================================
// Xbox Backup Creator v2.9 Build:0425 By Redline99
//
try
{
// Assume version is appended after first mention of Xbox Backup Creator
using var sr = File.OpenText(log);
while (!sr.EndOfStream)
{
string? line = sr.ReadLine()?.Trim();
if (line?.StartsWith("Xbox Backup Creator ") == true)
return line.Substring("Xbox Backup Creator ".Length).Trim();
}
// We couldn't detect the version
return null;
}
catch
{
// We don't care what the exception is right now
return null;
}
}
/// <summary>
/// Get the drive model from the log
/// </summary>
/// <param name="log">Path to XBC log file</param>
/// <returns>Drive model if found, null otherwise</returns>
private static string? GetDrive(string? log)
{
if (string.IsNullOrEmpty(log) || !File.Exists(log))
return null;
// Example:
// ========================================
// < --Security Sector Details -->
// Source Drive: SH-D162D
// ----------------------------------------
try
{
// Parse drive model from log file
using var sr = File.OpenText(log);
while (!sr.EndOfStream)
{
string? line = sr.ReadLine()?.Trim();
if (line?.StartsWith("Source Drive: ") == true)
{
return line.Substring("Source Drive: ".Length).Trim();
}
}
// We couldn't detect the drive model
return null;
}
catch
{
// We don't care what the exception is right now
return null;
}
}
/// <summary>
/// Get the Layerbreak value if possible
/// </summary>
/// <param name="dvd">Path to layerbreak file</param>
/// <param name="layerbreak">Layerbreak value if found</param>
/// <returns>True if successful, otherwise false</returns>
/// <returns></returns>
private static bool GetLayerbreak(string? dvd, out long layerbreak)
{
layerbreak = 0;
if (string.IsNullOrEmpty(dvd) || !File.Exists(dvd))
return false;
// Example:
// LayerBreak=1913776
// track.iso
try
{
// Parse Layerbreak value from DVD file
using var sr = File.OpenText(dvd);
while (!sr.EndOfStream)
{
string? line = sr.ReadLine()?.Trim();
if (line?.StartsWith("LayerBreak=") == true)
{
return long.TryParse(line.Substring("LayerBreak=".Length).Trim(), out layerbreak);
}
}
// We couldn't detect the Layerbreak
return false;
}
catch
{
// We don't care what the exception is right now
return false;
}
}
/// <summary>
/// Get the read error count if possible
/// </summary>
/// <param name="log">Path to XBC log file</param>
/// <param name="readErrors">Read error count if found, -1 otherwise</param>
/// <returns>True if sucessful, otherwise false</returns>
private bool GetReadErrors(string? log, out long readErrors)
{
readErrors = -1;
if (string.IsNullOrEmpty(log) || !File.Exists(log))
return false;
// TODO: Logic when more than one dump is in the logs
// Example: (replace [E] with drive letter)
// Creating SplitVid backup image [E]
// ...
// Reading Game Partition
// Setting read speed to 1x
// Unrecovered read error at Partition LBA: 0
// Example: (replace track with base filename)
// Creating Layer Break File
// LayerBreak file saved as: "track.dvd"
// A total of 1 sectors were zeroed out.
// Example: (for Original Xbox)
// A total of 65,536 sectors were zeroed out.
// A total of 31 sectors with read errors were recovered.
try
{
// Parse Layerbreak value from DVD file
using var sr = File.OpenText(log);
while (!sr.EndOfStream)
{
string? line = sr.ReadLine()?.Trim();
if (line?.StartsWith("Creating Layer Break File") == true)
{
// Read error count is two lines below
line = sr.ReadLine()?.Trim();
line = sr.ReadLine()?.Trim();
if (line?.StartsWith("A total of ") == true && line?.EndsWith(" sectors were zeroed out.") == true)
{
string? errorCount = line.Substring("A total of ".Length, line.Length - 36).Replace(",", "").Trim();
bool success = long.TryParse(errorCount, out readErrors);
// Original Xbox should have 65536 read errors when dumping with XBC
if (this.System == RedumpSystem.MicrosoftXbox)
{
if (readErrors == 65536)
readErrors = 0;
else if (readErrors > 65536)
readErrors -= 65536;
}
return success;
}
}
}
// We couldn't detect the read error count
return false;
}
catch
{
// We don't care what the exception is right now
return false;
}
}
/// <summary>
/// Get Xbox360 Media ID from XBC log file
/// </summary>
/// <param name="log">Path to XBC log file</param>
/// <returns>Media ID if Log successfully parsed, null otherwise</returns>
private string? GetMediaID(string? log)
{
if (string.IsNullOrEmpty(log) || !File.Exists(log))
return null;
if (this.System == RedumpSystem.MicrosoftXbox)
return null;
// Example:
// ----------------------------------------
// Media ID
// A76B9983D170EFF8749A892BC-8B62A812
// ----------------------------------------
try
{
// Parse Layerbreak value from DVD file
using var sr = File.OpenText(log);
while (!sr.EndOfStream)
{
string? line = sr.ReadLine()?.Trim();
if (line?.StartsWith("Media ID") == true)
{
line = sr.ReadLine()?.Trim();
return line?.Substring(25).Trim();
}
}
// We couldn't detect the Layerbreak
return null;
}
catch
{
// We don't care what the exception is right now
return null;
}
}
/// <summary>
/// Recreate an SS.bin file from XBC log and write it to a file
/// </summary>
/// <param name="log">Path to XBC log</param>
/// <param name="cleanSS">Path to the clean SS file to read from</param>
/// <param name="rawSS">Path to the raw SS file to write to</param>
/// <returns>True if successful, false otherwise</returns>
private static bool RecreateSS(string log, string cleanSS, string rawSS)
{
if (!File.Exists(log) || !File.Exists(cleanSS))
return false;
byte[] ss = File.ReadAllBytes(cleanSS);
if (ss.Length != 2048)
return false;
if (!RecreateSS(log!, ss))
return false;
File.WriteAllBytes(rawSS, ss);
return true;
}
/// <summary>
/// Recreate an SS.bin byte array from an XBC log.
/// With help from https://github.com/hadzz/SS-Angle-Fixer/
/// </summary>
/// <param name="log">Path to XBC log</param>
/// <param name="ss">Byte array of SS sector</param>
/// <returns>True if successful, false otherwise</returns>
private static bool RecreateSS(string log, byte[] ss)
{
// Log file must exist
if (!File.Exists(log))
return false;
// SS must be complete sector
if (ss.Length != 2048)
return false;
// Ignore XGD1 discs
if (!Tools.GetXGDType(ss, out int xgdType))
return false;
if (xgdType == 0)
return false;
// Don't recreate an already raw SS
// (but do save to file, so return true)
if (!Tools.IsCleanSS(ss))
return true;
// Example replay table:
/*
----------------------------------------
RT CID MOD DATA Drive Response
-- -- -- ------------- -------------------
01 14 00 033100 0340FF B7D8C32A B703590100
03 BE 00 244530 24552F F4B9B528 BE46360500
01 97 00 DBBAD0 DBCACF DD7787F4 484977ED00
03 45 00 FCAF00 FCBEFF FB7A7773 AAB662FC00
05 6B 00 033100 033E7F 0A31252A 0200000200
07 46 00 244530 2452AF F8E77EBC 5B00005B00
05 36 00 DBBAD0 DBC84F F5DFA735 B50000B500
07 A1 00 FCAF00 FCBC7F 6B749DBF 0E01000E01
E0 50 00 42F4E1 00B6F7 00000000 0000000000
--------------------------------------------
*/
try
{
// Parse Replay Table from log
using var sr = File.OpenText(log);
while (!sr.EndOfStream)
{
string? line = sr.ReadLine()?.Trim();
if (line?.StartsWith("RT CID MOD DATA Drive Response") == true)
{
// Ignore next line
line = sr.ReadLine()?.Trim();
if (sr.EndOfStream)
return false;
byte[][] responses = new byte[4][];
// Parse the nine rows from replay table
for (int i = 0; i < 9; i++)
{
line = sr.ReadLine()?.Trim();
// Validate line
if (sr.EndOfStream || string.IsNullOrEmpty(line) || line!.Length < 44)
return false;
// Save useful angle responses
if (i >= 4 && i <= 7)
{
byte[]? angles = Tools.HexStringToByteArray(line!.Substring(34, 10));
if (angles == null || angles.Length != 5)
return false;
responses[i - 4] = angles!;
}
}
int rtOffset = 0x204;
if (xgdType == 3)
rtOffset = 0x24;
// Replace angles
for (int i = 0; i < 4; i++)
{
int offset = rtOffset + (9 * (i + 4));
for (int j = 0; j < 5; j++)
{
// Ignore the middle byte
if (j == 2)
continue;
ss[offset + j] = responses[i][j];
}
}
return true;
}
}
// We couldn't detect the replay table
return false;
}
catch
{
// We don't care what the exception is right now
return false;
}
}
#endregion
}
}

View File

@@ -296,7 +296,7 @@ namespace MPF.Core
// SafeDisc
if (foundProtections.Any(p => p.StartsWith("SafeDisc")))
{
if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled)))
if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled) && !p.StartsWith("Macrovision Protection File")))
{
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
.Where(p => !p.StartsWith("Macrovision Protection File"))
@@ -307,7 +307,7 @@ namespace MPF.Core
.Where(p => p != "SafeDisc 1/Lite")
.Where(p => p != "SafeDisc 2+");
}
else if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled)))
else if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled) && !p.StartsWith("Macrovision Protection File")))
{
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
.Where(p => !p.StartsWith("Macrovision Protection File"))
@@ -317,7 +317,7 @@ namespace MPF.Core
.Where(p => p != "SafeDisc 1/Lite")
.Where(p => p != "SafeDisc 2+");
}
else if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+", RegexOptions.Compiled)))
else if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+", RegexOptions.Compiled) && !p.StartsWith("Macrovision Protection File")))
{
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
.Where(p => !p.StartsWith("Macrovision Protection File"))

View File

@@ -283,6 +283,12 @@ namespace MPF.Core
info.CopyProtection.FullProtections = fullProtections as Dictionary<string, List<string>?> ?? [];
resultProgress?.Report(Result.Success("Copy protection scan complete!"));
if (system == RedumpSystem.EnhancedCD)
info.CommonDiscInfo!.Category ??= DiscCategory.Audio;
if (system == RedumpSystem.SonyElectronicBook)
info.CommonDiscInfo!.Category ??= DiscCategory.Multimedia;
break;
case RedumpSystem.AudioCD:
@@ -292,68 +298,78 @@ namespace MPF.Core
break;
case RedumpSystem.BandaiPlaydiaQuickInteractiveSystem:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? Region.Japan;
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo.Region ??= info.CommonDiscInfo.Region ?? Region.Japan;
break;
case RedumpSystem.BDVideo:
info.CommonDiscInfo!.Category ??= DiscCategory.BonusDiscs;
info.CopyProtection!.Protection = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
case RedumpSystem.DVDVideo:
case RedumpSystem.HDDVDVideo:
info.CommonDiscInfo!.Category ??= DiscCategory.Video;
info.CopyProtection!.Protection ??= options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
break;
case RedumpSystem.CommodoreAmigaCD:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.CommodoreAmigaCD32:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo.Region ??= Region.Europe;
break;
case RedumpSystem.CommodoreAmigaCDTV:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo.Region ??= Region.Europe;
break;
case RedumpSystem.DVDVideo:
info.CommonDiscInfo!.Category ??= DiscCategory.BonusDiscs;
break;
case RedumpSystem.FujitsuFMTownsseries:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? Region.Japan;
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo.Region ??= Region.Japan;
break;
case RedumpSystem.FujitsuFMTownsMarty:
info.CommonDiscInfo!.Region ??= Region.Japan;
break;
case RedumpSystem.HasbroVideoNow:
case RedumpSystem.HasbroVideoNowColor:
case RedumpSystem.HasbroVideoNowJr:
case RedumpSystem.VideoCD:
info.CommonDiscInfo!.Category ??= DiscCategory.Video;
break;
case RedumpSystem.HasbroVideoNowXP:
case RedumpSystem.PhotoCD:
info.CommonDiscInfo!.Category ??= DiscCategory.Multimedia;
break;
case RedumpSystem.IncredibleTechnologiesEagle:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.KonamieAmusement:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.KonamiFireBeat:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.KonamiSystemGV:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.KonamiSystem573:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.KonamiTwinkle:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.MattelHyperScan:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.MicrosoftXboxOne:
@@ -383,7 +399,7 @@ namespace MPF.Core
break;
case RedumpSystem.NamcoSegaNintendoTriforce:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.NavisoftNaviken21:
@@ -405,23 +421,23 @@ namespace MPF.Core
break;
case RedumpSystem.SegaChihiro:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.SegaDreamcast:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.SegaNaomi:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.SegaNaomi2:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.SegaTitanVideo:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.SharpX68000:
@@ -429,7 +445,7 @@ namespace MPF.Core
break;
case RedumpSystem.SNKNeoGeoCD:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.SonyPlayStation:
@@ -452,7 +468,7 @@ namespace MPF.Core
break;
case RedumpSystem.SonyPlayStation2:
info.CommonDiscInfo!.LanguageSelection = [LanguageSelection.BiosSettings, LanguageSelection.LanguageSelector, LanguageSelection.OptionsMenu];
info.CommonDiscInfo!.LanguageSelection ??= [];
break;
case RedumpSystem.SonyPlayStation3:
@@ -461,11 +477,12 @@ namespace MPF.Core
break;
case RedumpSystem.TomyKissSite:
info.CommonDiscInfo!.Category ??= DiscCategory.Video;
info.CommonDiscInfo!.Region ??= Region.Japan;
break;
case RedumpSystem.ZAPiTGamesGameWaveFamilyEntertainmentSystem:
info.CopyProtection!.Protection = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CopyProtection!.Protection ??= options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
break;
}

View File

@@ -402,7 +402,7 @@ namespace MPF.Core.UI.ViewModels
InternalProgram internalProgram = this.Options.InternalProgram;
// Create a static list of supported Check programs, not everything
var internalPrograms = new List<InternalProgram> { InternalProgram.Redumper, InternalProgram.Aaru, InternalProgram.DiscImageCreator, InternalProgram.CleanRip, InternalProgram.PS3CFW, InternalProgram.UmdImageCreator };
var internalPrograms = new List<InternalProgram> { InternalProgram.Redumper, InternalProgram.Aaru, InternalProgram.DiscImageCreator, InternalProgram.CleanRip, InternalProgram.PS3CFW, InternalProgram.UmdImageCreator, InternalProgram.XboxBackupCreator };
InternalPrograms = internalPrograms.Select(ip => new Element<InternalProgram>(ip)).ToList();
// Select the current default dumping program

View File

@@ -51,6 +51,16 @@ namespace MPF.Core.UI.ViewModels
/// </summary>
public static List<Element<InternalProgram>> InternalPrograms => PopulateInternalPrograms();
/// <summary>
/// Current list of supported Redumper read methods
/// </summary>
public static List<Element<RedumperReadMethod>> RedumperReadMethods => PopulateRedumperReadMethods();
/// <summary>
/// Current list of supported Redumper sector orders
/// </summary>
public static List<Element<RedumperSectorOrder>> RedumperSectorOrders => PopulateRedumperSectorOrders();
/// <summary>
/// Current list of supported system profiles
/// </summary>
@@ -77,7 +87,7 @@ namespace MPF.Core.UI.ViewModels
#region Population
/// <summary>
/// Get a complete list of supported internal programs
/// Get a complete list of supported internal programs
/// </summary>
private static List<Element<InternalProgram>> PopulateInternalPrograms()
{
@@ -85,6 +95,24 @@ namespace MPF.Core.UI.ViewModels
return internalPrograms.Select(ip => new Element<InternalProgram>(ip)).ToList();
}
/// <summary>
/// Get a complete list of supported redumper drive read methods
/// </summary>
private static List<Element<RedumperReadMethod>> PopulateRedumperReadMethods()
{
var readMethods = new List<RedumperReadMethod> { RedumperReadMethod.NONE, RedumperReadMethod.D8, RedumperReadMethod.BE, RedumperReadMethod.BE_CDDA };
return readMethods.Select(rm => new Element<RedumperReadMethod>(rm)).ToList();
}
/// <summary>
/// Get a complete list of supported redumper drive sector orders
/// </summary>
private static List<Element<RedumperSectorOrder>> PopulateRedumperSectorOrders()
{
var sectorOrders = new List<RedumperSectorOrder> { RedumperSectorOrder.NONE, RedumperSectorOrder.DATA_C2_SUB, RedumperSectorOrder.DATA_SUB_C2, RedumperSectorOrder.DATA_SUB, RedumperSectorOrder.DATA_C2 };
return sectorOrders.Select(so => new Element<RedumperSectorOrder>(so)).ToList();
}
#endregion
#region UI Commands
@@ -107,6 +135,17 @@ namespace MPF.Core.UI.ViewModels
#endif
}
/// <summary>
/// Reset Redumper non-redump options (Read Method, Sector Order, Drive Type)
/// </summary>
public void NonRedumpModeUnChecked()
{
Options.RedumperReadMethod = RedumperReadMethod.NONE;
Options.RedumperSectorOrder = RedumperSectorOrder.NONE;
Options.RedumperUseGenericDriveType = false;
TriggerPropertyChanged(nameof(Options));
}
#endregion
#region Property Updates

View File

@@ -26,7 +26,17 @@ namespace MPF.Core.Utilities
return false;
// List options
if (args[0] == "-lm" || args[0] == "--listmedia")
if (args[0] == "-lc" || args[0] == "--listcodes")
{
Console.WriteLine("Supported Site Codes:");
foreach (string siteCode in Extensions.ListSiteCodes())
{
Console.WriteLine(siteCode);
}
Console.ReadLine();
return true;
}
else if (args[0] == "-lm" || args[0] == "--listmedia")
{
Console.WriteLine("Supported Media Types:");
foreach (string mediaType in Extensions.ListMediaTypes())

View File

@@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using MPF.Core.Data;
@@ -642,5 +643,409 @@ namespace MPF.Core.Utilities
}
#endregion
#region Xbox/Xbox360 specific tools
/// <summary>
/// Get the XGD1 Master ID (XMID) information
/// </summary>
/// <param name="dmi">DMI.bin file location</param>
/// <returns>String representation of the XGD1 DMI information, empty string on error</returns>
public static string GetXGD1XMID(string dmi)
{
if (!File.Exists(dmi))
return string.Empty;
try
{
using var br = new BinaryReader(File.OpenRead(dmi));
br.BaseStream.Seek(8, SeekOrigin.Begin);
return new string(br.ReadChars(8));
}
catch
{
return string.Empty;
}
}
/// <summary>
/// Get the XGD2/3 Master ID (XeMID) information
/// </summary>
/// <param name="dmi">DMI.bin file location</param>
/// <returns>String representation of the XGD2/3 DMI information, empty string on error</returns>
public static string GetXGD23XeMID(string dmi)
{
if (!File.Exists(dmi))
return string.Empty;
try
{
using var br = new BinaryReader(File.OpenRead(dmi));
br.BaseStream.Seek(64, SeekOrigin.Begin);
return new string(br.ReadChars(14));
}
catch
{
return string.Empty;
}
}
/// <summary>
/// Get XGD type from SS.bin file
/// </summary>
/// <param name="ss"></param>
/// <param name="xgdType"></param>
/// <returns></returns>
public static bool GetXGDType(string? ss, out int xgdType)
{
xgdType = 0;
if (string.IsNullOrEmpty(ss) || !File.Exists(ss))
return false;
using FileStream fs = File.OpenRead(ss);
byte[] buf = new byte[3];
int numBytes = fs.Read(buf, 13, 16);
if (numBytes != 3)
return false;
return GetXGDType(buf, out xgdType);
}
/// <summary>
/// Get XGD type from SS.bin sector
/// </summary>
/// <param name="ss">Byte array of SS.bin sector</param>
/// <param name="xgdType">XGD type</param>
/// <returns>True if successful, false otherwise</returns>
public static bool GetXGDType(byte[] ss, out int xgdType)
{
xgdType = 0;
// Concatenate the last three values
long lastThree = (((ss[13] << 8) | ss[14]) << 8) | ss[15];
// Return XGD type based on value
switch (lastThree)
{
case 0x2033AF:
xgdType = 1;
return true;
case 0x20339F:
xgdType = 2;
return true;
case 0x238E0F:
xgdType = 3;
return true;
default:
return false;
}
}
/// <summary>
/// Determine if a given SS has already been cleaned
/// </summary>
/// <param name="ss">Byte array of SS sector</param>
/// <returns>True if SS is clean, false otherwise</returns>
public static bool IsCleanSS(byte[] ss)
{
if (ss.Length != 2048)
return false;
if (!GetXGDType(ss, out int xgdType))
return false;
if (xgdType == 3 && ss.Skip(32).Take(72).All(x => x == 0))
{
// Check for a cleaned SSv2
int rtOffset = 0x24;
if (ss[rtOffset + 36] != 0x01)
return false;
if (ss[rtOffset + 37] != 0x00)
return false;
if (ss[rtOffset + 39] != 0x01)
return false;
if (ss[rtOffset + 40] != 0x00)
return false;
if (ss[rtOffset + 45] != 0x5B)
return false;
if (ss[rtOffset + 46] != 0x00)
return false;
if (ss[rtOffset + 48] != 0x5B)
return false;
if (ss[rtOffset + 49] != 0x00)
return false;
if (ss[rtOffset + 54] != 0xB5)
return false;
if (ss[rtOffset + 55] != 0x00)
return false;
if (ss[rtOffset + 57] != 0xB5)
return false;
if (ss[rtOffset + 58] != 0x00)
return false;
if (ss[rtOffset + 63] != 0x0F)
return false;
if (ss[rtOffset + 64] != 0x01)
return false;
if (ss[rtOffset + 66] != 0x0F)
return false;
if (ss[rtOffset + 67] != 0x01)
return false;
}
else
{
// Check for a cleaned SSv1
int rtOffset = 0x204;
if (ss[rtOffset + 36] != 0x01)
return false;
if (ss[rtOffset + 37] != 0x00)
return false;
if (xgdType == 2 && ss[rtOffset + 39] != 0x00)
return false;
if (xgdType == 2 && ss[rtOffset + 40] != 0x00)
return false;
if (ss[rtOffset + 45] != 0x5B)
return false;
if (ss[rtOffset + 46] != 0x00)
return false;
if (xgdType == 2 && ss[rtOffset + 48] != 0x00)
return false;
if (xgdType == 2 && ss[rtOffset + 49] != 0x00)
return false;
if (ss[rtOffset + 54] != 0xB5)
return false;
if (ss[rtOffset + 55] != 0x00)
return false;
if (xgdType == 2 && ss[rtOffset + 57] != 0x00)
return false;
if (xgdType == 2 && ss[rtOffset + 58] != 0x00)
return false;
if (ss[rtOffset + 63] != 0x0F)
return false;
if (ss[rtOffset + 64] != 0x01)
return false;
if (xgdType == 2 && ss[rtOffset + 66] != 0x00)
return false;
if (xgdType == 2 && ss[rtOffset + 67] != 0x00)
return false;
}
// All angles are as expected, it is clean
return true;
}
/// <summary>
/// Clean a rawSS.bin file and write it to a file
/// </summary>
/// <param name="rawSS">Path to the raw SS file to read from</param>
/// <param name="cleanSS">Path to the clean SS file to write to</param>
/// <returns>True if successful, false otherwise</returns>
public static bool CleanSS(string rawSS, string cleanSS)
{
if (!File.Exists(rawSS))
return false;
byte[] ss = File.ReadAllBytes(rawSS);
if (ss.Length != 2048)
return false;
if (!CleanSS(ss))
return false;
File.WriteAllBytes(cleanSS, ss);
return true;
}
/// <summary>
/// Fix a SS sector to its predictable clean form.
/// With help from ss_sector_range
/// </summary>
/// <param name="ss">Byte array of raw SS sector</param>
/// <returns>True if successful, false otherwise</returns>
public static bool CleanSS(byte[] ss)
{
// Must be entire sector
if (ss.Length != 2048)
return false;
// Determine XGD type
if (!GetXGDType(ss, out int xgdType))
return false;
switch (xgdType)
{
case 1:
// Leave Original Xbox SS.bin unchanged
return true;
case 2:
// Fix standard SSv1 ss.bin
ss[552] = 1; // 0x01
ss[553] = 0; // 0x00
ss[555] = 0; // 0x00
ss[556] = 0; // 0x00
ss[561] = 91; // 0x5B
ss[562] = 0; // 0x00
ss[564] = 0; // 0x00
ss[565] = 0; // 0x00
ss[570] = 181; // 0xB5
ss[571] = 0; // 0x00
ss[573] = 0; // 0x00
ss[574] = 0; // 0x00
ss[579] = 15; // 0x0F
ss[580] = 1; // 0x01
ss[582] = 0; // 0x00
ss[583] = 0; // 0x00
return true;
case 3:
// Determine if XGD3 SS.bin is SSv1 (Kreon) or SSv2 (0800)
bool ssv2 = ss.Skip(32).Take(72).All(x => x == 0);
if (ssv2)
{
ss[72] = 1; // 0x01
ss[73] = 0; // 0x00
ss[75] = 1; // 0x01
ss[76] = 0; // 0x00
ss[81] = 91; // 0x5B
ss[82] = 0; // 0x00
ss[84] = 91; // 0x5B
ss[85] = 0; // 0x00
ss[90] = 181; // 0xB5
ss[91] = 0; // 0x00
ss[93] = 181; // 0xB5
ss[94] = 0; // 0x00
ss[99] = 15; // 0x0F
ss[100] = 1; // 0x01
ss[102] = 15; // 0x0F
ss[103] = 1; // 0x01
}
else
{
ss[552] = 1; // 0x01
ss[553] = 0; // 0x00
ss[561] = 91; // 0x5B
ss[562] = 0; // 0x00
ss[570] = 181; // 0xB5
ss[571] = 0; // 0x00
ss[579] = 15; // 0x0F
ss[580] = 1; // 0x01
}
return true;
default:
// Unknown XGD type
return false;
}
}
/// <summary>
/// Get Security Sector ranges from SS.bin
/// </summary>
/// <param name="ssBin">Path to SS.bin file</param>
/// <returns>Sector ranges if found, null otherwise</returns>
public static string? GetSSRanges(string ssBin)
{
if (!File.Exists(ssBin))
return null;
byte[] ss = File.ReadAllBytes(ssBin);
if (ss.Length != 2048)
return null;
return GetSSRanges(ss);
}
/// <summary>
/// Get Security Sector ranges from SS sector.
/// With help from ss_sector_range
/// </summary>
/// <param name="ss">Byte array of SS sector</param>
/// <returns>Sector ranges if found, null otherwise</returns>
public static string? GetSSRanges(byte[] ss)
{
if (ss.Length != 2048)
return null;
if (!GetXGDType(ss, out int xgdType))
return null;
//uint numRanges = ss[1632];
uint numRanges;
if (xgdType == 1)
numRanges = 16;
else
numRanges = 4;
uint[] startLBA = new uint[numRanges];
uint[] endLBA = new uint[numRanges];
for (uint i = 0; i < numRanges; i++)
{
// Determine range Physical Sector Number
uint startPSN = (uint)((((ss[i * 9 + 1636] << 8) | ss[i * 9 + 1637]) << 8) | ss[i * 9 + 1638]);
uint endPSN = (uint)((((ss[i * 9 + 1639] << 8) | ss[i * 9 + 1640]) << 8) | ss[i * 9 + 1641]);
// Determine range Logical Sector Number
if (xgdType == 1 && startPSN >= (1913776 + 0x030000))
{
// Layer 1 of XGD1
startLBA[i] = (1913776 + 0x030000) * 2 - (startPSN ^ 0xFFFFFF) - 0x030000 - 1;
endLBA[i] = (1913776 + 0x030000) * 2 - (endPSN ^ 0xFFFFFF) - 0x030000 - 1;
}
else if (xgdType > 1 && startPSN >= (1913760 + 0x030000))
{
// Layer 1 of XGD2 or XGD3
startLBA[i] = (1913760 + 0x030000) * 2 - (startPSN ^ 0xFFFFFF) - 0x030000 - 1;
endLBA[i] = (1913760 + 0x030000) * 2 - (endPSN ^ 0xFFFFFF) - 0x030000 - 1;
}
else
{
// Layer 0
startLBA[i] = startPSN - 0x030000;
endLBA[i] = endPSN - 0x030000;
}
}
// Sort ranges for XGD1
if (xgdType == 1)
Array.Sort(startLBA, endLBA);
// Represent ranges as string
string? ranges = null;
if (xgdType == 1)
{
for (int i = 0; i < 16; i++)
{
ranges += $"{startLBA[i]}-{endLBA[i]}";
if (i != numRanges - 1)
ranges += "\r\n";
}
}
else
{
ranges = $"{startLBA[0]}-{endLBA[0]}\r\n{startLBA[3]}-{endLBA[3]}";
}
return ranges;
}
#endregion
}
}

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0-windows;net8.0-windows</TargetFrameworks>
<TargetFrameworks>net8.0</TargetFrameworks>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
@@ -10,26 +10,26 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MPF\MPF.csproj" />
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeCoverage" Version="17.10.0-preview-24080-01" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0-preview-24080-01" />
<PackageReference Include="Microsoft.CodeCoverage" Version="17.10.0-release-24177-07" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0-release-24177-07" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.6" />
<PackageReference Include="xunit" Version="2.7.0" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.8" />
<PackageReference Include="xunit" Version="2.8.0" />
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
<PackageReference Include="xunit.analyzers" Version="1.11.0" />
<PackageReference Include="xunit.assert" Version="2.7.0" />
<PackageReference Include="xunit.core" Version="2.7.0" />
<PackageReference Include="xunit.extensibility.core" Version="2.7.0" />
<PackageReference Include="xunit.extensibility.execution" Version="2.7.0" />
<PackageReference Include="xunit.runner.console" Version="2.7.0">
<PackageReference Include="xunit.analyzers" Version="1.13.0" />
<PackageReference Include="xunit.assert" Version="2.8.0" />
<PackageReference Include="xunit.core" Version="2.8.0" />
<PackageReference Include="xunit.extensibility.core" Version="2.8.0" />
<PackageReference Include="xunit.extensibility.execution" Version="2.8.0" />
<PackageReference Include="xunit.runner.console" Version="2.8.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.7">
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@@ -16,6 +16,8 @@ namespace MPF.UI.Core
DiscCategory discCategory => new Element<DiscCategory>(discCategory),
InternalProgram internalProgram => new Element<InternalProgram>(internalProgram),
MediaType mediaType => new Element<MediaType>(mediaType),
RedumperReadMethod readMethod => new Element<RedumperReadMethod>(readMethod),
RedumperSectorOrder sectorOrder => new Element<RedumperSectorOrder>(sectorOrder),
RedumpSystem redumpSystem => new RedumpSystemComboBoxItem(redumpSystem),
Region region => new Element<Region>(region),
@@ -35,6 +37,8 @@ namespace MPF.UI.Core
Element<DiscCategory> dcElement => dcElement.Value,
Element<InternalProgram> ipElement => ipElement.Value,
Element<MediaType> mtElement => mtElement.Value,
Element<RedumperReadMethod> rmElement => rmElement.Value,
Element<RedumperSectorOrder> soElement => soElement.Value,
RedumpSystemComboBoxItem rsElement => rsElement.Value,
Element<Region> reValue => reValue.Value,
_ => null,

View File

@@ -14,7 +14,7 @@
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF>
<VersionPrefix>3.1.4</VersionPrefix>
<VersionPrefix>3.1.9</VersionPrefix>
<!-- Package Properties -->
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
@@ -43,7 +43,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.6" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.8" />
</ItemGroup>
<ItemGroup>

View File

@@ -172,6 +172,8 @@
<controls:UserInput x:Name="CommentsTextBox" Label="Comments"
Text="{Binding SubmissionInfo.CommonDiscInfo.Comments, Mode=TwoWay}" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top" />
<controls:UserInput x:Name="CompatibleOSTextBox" Label="Compatible OS"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)CompatibleOS], Mode=TwoWay}"/>
<controls:UserInput x:Name="DiscKeyTextBox" Label="Disc Key" Visibility="Collapsed"
Text="{Binding Path=SubmissionInfo.Extras.DiscKey, Mode=TwoWay}"/>
<controls:UserInput x:Name="DiscIDTextBox" Label="Disc ID" Visibility="Collapsed"
@@ -235,8 +237,12 @@
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)ActivisionID], Mode=TwoWay}"/>
<controls:UserInput x:Name="BandaiIDTextBox" Label="Bandai ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)BandaiID], Mode=TwoWay}"/>
<controls:UserInput x:Name="BethesdaIDTextBox" Label="Bandai ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)BethesdaID], Mode=TwoWay}"/>
<controls:UserInput x:Name="CDProjektIDTextBox" Label="CD Projekt ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)CDProjektID], Mode=TwoWay}"/>
<controls:UserInput x:Name="EidosIDTextBox" Label="Eidos ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)EidosID], Mode=TwoWay}"/>
<controls:UserInput x:Name="ElectronicArtsIDTextBox" Label="Electronic Arts ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)ElectronicArtsID], Mode=TwoWay}"/>
<controls:UserInput x:Name="FoxInteractiveIDTextBox" Label="Fox Interactive ID"
@@ -290,6 +296,11 @@
Text="{Binding SubmissionInfo.CommonDiscInfo.Contents, Mode=TwoWay}" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
<!-- Applications -->
<controls:UserInput x:Name="ApplicationsTextBox" Label="Applications" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)Applications], Mode=TwoWay}"/>
<!-- Games -->
<controls:UserInput x:Name="GamesTextBox" Label="Games" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"

View File

@@ -477,16 +477,16 @@ namespace MPF.UI.Core.Windows
#if NET35
_L1Info!.Header = "Label Side";
_L1MasteringRing!.Visibility = Visibility.Collapsed;
_L1MasteringSID!.Visibility = Visibility.Collapsed;
_L1Toolstamp!.Visibility = Visibility.Collapsed;
_L1MasteringRing!.Label = "Mastering Ring";
_L1MasteringSID!.Label = "Mastering SID";
_L1Toolstamp!.Label = "Mastering SID";
_L1MouldSID!.Label = "Mould SID";
_L1AdditionalMould!.Label = "Additional Mould";
#else
L1Info.Header = "Label Side";
L1MasteringRing.Visibility = Visibility.Collapsed;
L1MasteringSID.Visibility = Visibility.Collapsed;
L1Toolstamp.Visibility = Visibility.Collapsed;
L1MasteringRing.Label = "Mastering Ring";
L1MasteringSID.Label = "Mastering SID";
L1Toolstamp.Label = "Mastering SID";
L1MouldSID.Label = "Mould SID";
L1AdditionalMould.Label = "Additional Mould";
#endif
@@ -687,16 +687,16 @@ namespace MPF.UI.Core.Windows
#if NET35
_L1Info!.Header = "Label Side";
_L1MasteringRing!.Visibility = Visibility.Collapsed;
_L1MasteringSID!.Visibility = Visibility.Collapsed;
_L1Toolstamp!.Visibility = Visibility.Collapsed;
_L1MasteringRing!.Label = "Mastering Ring";
_L1MasteringSID!.Label = "Mastering SID";
_L1Toolstamp!.Label = "Toolstamp/Mastering Code";
_L1MouldSID!.Label = "Mould SID";
_L1AdditionalMould!.Label = "Additional Mould";
#else
L1Info.Header = "Label Side";
L1MasteringRing.Visibility = Visibility.Collapsed;
L1MasteringSID.Visibility = Visibility.Collapsed;
L1Toolstamp.Visibility = Visibility.Collapsed;
L1MasteringRing.Label = "Mastering Ring";
L1MasteringSID.Label = "Mastering SID";
L1Toolstamp.Label = "Toolstamp/Mastering Code";
L1MouldSID.Label = "Mould SID";
L1AdditionalMould.Label = "Additional Mould";
#endif

View File

@@ -370,7 +370,7 @@
/>
<Label Content="Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center" Width="200" HorizontalAlignment="Left"
Text="{Binding Options.AaruRereadCount}"
ToolTip="Specifies how many rereads are attempted for sector and subchannel errors"
/>
@@ -403,22 +403,23 @@
IsChecked="{Binding Options.DICMultiSectorRead}"
ToolTip="Enable the /mr flag for BD drive dumping" Margin="0,4"
/>
<Label/> <!-- Empty label for padding -->
<Label/>
<!-- Empty label for padding -->
<Label Content="Multi-Sector Read Value:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center" Width="200" HorizontalAlignment="Left"
Text="{Binding Options.DICMultiSectorReadValue}" IsEnabled="{Binding Options.DICMultiSectorRead}"
ToolTip="Set the default value for the /mr flag"
/>
<Label Content="CD Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center" Width="200" HorizontalAlignment="Left"
Text="{Binding Options.DICRereadCount}"
ToolTip="Specifies how many rereads are attempted on C2 error [CD only]"
/>
<Label Content="DVD/HD-DVD/BD Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center" Width="200" HorizontalAlignment="Left"
Text="{Binding Options.DICDVDRereadCount}"
ToolTip="Specifies how many rereads are attempted on read error [DVD/HD-DVD/BD only]"
/>
@@ -426,7 +427,7 @@
</GroupBox>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Redumper">
<UniformGrid Columns="2" Rows="3">
<UniformGrid Columns="2" Rows="7">
<CheckBox VerticalAlignment="Center" Content="Enable Debug Output"
IsChecked="{Binding Options.RedumperEnableDebug}"
ToolTip="Enable debug output in logs" Margin="0,4"
@@ -437,20 +438,45 @@
ToolTip="Enable verbose output in logs" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Enable BE Drive Reading"
IsChecked="{Binding Options.RedumperUseBEReading}"
ToolTip="Enable setting drive read method to BE_CDDA by default" Margin="0,4"
<Label Content="Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center" Width="200" HorizontalAlignment="Left"
Text="{Binding Options.RedumperRereadCount}"
ToolTip="Specifies how many rereads are attempted on read error"
/>
<CheckBox VerticalAlignment="Center" Content="Set Default Plextor Lead-in Retries"
IsChecked="{Binding Options.RedumperEnableLeadinRetry}"
ToolTip="Enable custom lead-in retries for Plextor drives" Margin="0,4"
/>
<Label/>
<!-- Empty label for padding -->
<Label Content="Plextor Lead-in Retries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center" Width="200" HorizontalAlignment="Left"
Text="{Binding Options.RedumperLeadinRetryCount}" IsEnabled ="{Binding Options.RedumperEnableLeadinRetry}"
ToolTip="Specifies how many retries are attempted for lead-in on Plextor drives"
/>
<CheckBox VerticalAlignment="Center" Content="Non-Redump Options" Click="NonRedumpModeClicked"
IsChecked="{Binding Options.RedumperNonRedumpMode}"
ToolTip="Enable non-redump options" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Set Generic Drive Type"
IsChecked="{Binding Options.RedumperUseGenericDriveType}"
IsChecked="{Binding Options.RedumperUseGenericDriveType}" IsEnabled="{Binding Options.RedumperNonRedumpMode}"
ToolTip="Enable setting drive type to Generic by default" Margin="0,4"
/>
<Label Content="Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
Text="{Binding Options.RedumperRereadCount}"
ToolTip="Specifies how many rereads are attempted on read error"
<Label VerticalAlignment="Center" Content="Default Read Method:" HorizontalAlignment="Right" />
<ComboBox x:Name="DefaultRedumperReadMethodComboBox" Height="22" Width="200" HorizontalAlignment="Left"
ItemsSource="{Binding RedumperReadMethods}" SelectedItem="{Binding Options.RedumperReadMethod, Converter={StaticResource ElementConverter}, Mode=TwoWay}"
Style="{DynamicResource CustomComboBoxStyle}" IsEnabled="{Binding Options.RedumperNonRedumpMode}"
/>
<Label VerticalAlignment="Center" Content="Default Sector Order:" HorizontalAlignment="Right" />
<ComboBox x:Name="DefaultRedumperSectorOrderComboBox" Height="22" Width="200" HorizontalAlignment="Left"
ItemsSource="{Binding RedumperSectorOrders}" SelectedItem="{Binding Options.RedumperSectorOrder, Converter={StaticResource ElementConverter}, Mode=TwoWay}"
Style="{DynamicResource CustomComboBoxStyle}" IsEnabled="{Binding Options.RedumperNonRedumpMode}"
/>
</UniformGrid>
</GroupBox>

View File

@@ -214,7 +214,7 @@ namespace MPF.UI.Core.Windows
CustomMessageBox.Show(this, message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
#endregion
#endregion
#region Event Handlers
@@ -224,6 +224,17 @@ namespace MPF.UI.Core.Windows
private void BrowseForPathClick(object sender, EventArgs e) =>
BrowseForPath(this, sender as System.Windows.Controls.Button);
/// <summary>
/// Alert user of non-redump mode implications
/// </summary>
private void NonRedumpModeClicked(object sender, EventArgs e)
{
if (OptionsViewModel.Options.RedumperNonRedumpMode)
CustomMessageBox.Show(this, "All logs generated with these options will not be acceptable for Redump submission", "Warning", MessageBoxButton.OK, MessageBoxImage.Warning);
else
OptionsViewModel.NonRedumpModeUnChecked();
}
/// <summary>
/// Handler for AcceptButton Click event
/// </summary>

View File

@@ -35,6 +35,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{
ProjectSection(SolutionItems) = preProject
.github\workflows\build_check.yml = .github\workflows\build_check.yml
.github\workflows\build_ui.yml = .github\workflows\build_ui.yml
.github\workflows\check_pr.yml = .github\workflows\check_pr.yml
EndProjectSection
EndProject
Global

View File

@@ -16,7 +16,7 @@
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF>
<VersionPrefix>3.1.4</VersionPrefix>
<VersionPrefix>3.1.9</VersionPrefix>
<!-- Package Properties -->
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
@@ -70,10 +70,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.1.2" GeneratePathProperty="true">
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.1.12" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.6" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.8" />
</ItemGroup>
<ItemGroup>

View File

@@ -26,6 +26,11 @@ The main UI has some known limitations that are documented in code and in some p
- MAUI is not a viable alternative due to lack of out-of-box support for Linux
- Avalonia is being heavily considered as an alternative
- For those who need .NET Framework 4.8, there is an official fork: [MPF Legacy](https://github.com/Deterous/MPF-Legacy)
- For those who require broader archive/installer compatibility for protection scanning (Windows-only), please use the x86 builds as there are some specific scanning libraries that only work with that build
- This is actively being worked on as part of [Binary Object Scanner](https://github.com/SabreTools/BinaryObjectScanner)
- Please consider contributing if you have experience in dealing with multiple archive and installer types
- Consider using a third-party scanning tool, such as Protection ID, if this is not sufficient for your needs
- See [Compatibility Notes](https://github.com/SabreTools/BinaryObjectScanner?tab=readme-ov-file#compatibility-notes) for more details
## Media Preservation Frontend Checker (MPF.Check)
@@ -56,16 +61,14 @@ dotnet build MPF/MPF.csproj --framework net8.0-windows --runtime [win-x86|win-x6
dotnet build MPF.Check/MPF.Check.csproj --framework net8.0 --runtime [win-x86|win-x64|linux-x64|osx-x64]
```
Choose one of `win-x64`, `linux-x64`, or `osx-x64` depending on the machine you are targeting.
Choose one of `win-x86`, `win-x64`, `linux-x64`, or `osx-x64` depending on the machine you are targeting.
### Build Scripts
Windows users may run `publish-win.ps1` and Linux users may run `publish-nix.sh` to perform a full release build.
Windows users may run `publish-win.ps1` and Linux users may run `publish-nix.sh` to perform a full release build. Both scripts will build and package all variants of MPF UI and MPF.Check with commandline switches to control what is included.
- `publish-win.ps1` will build and package all variants of MPF UI and MPF.Check
- The script requires [7-zip commandline](https://www.7-zip.org/download.html) and [Git for Windows](https://git-scm.com/downloads) to be installed and in `PATH`
- `publish-nix.sh` will _only_ build and package all variants of MPF.Check
- The script requires `zip` and `git` to be installed and in `PATH`
- `publish-win.ps1` requires [7-zip commandline](https://www.7-zip.org/download.html) and [Git for Windows](https://git-scm.com/downloads) to be installed and in `PATH`
- `publish-nix.sh` requires `zip` and `git` to be installed and in `PATH`
## Information
@@ -79,7 +82,7 @@ A list of all changes in each stable release and current WIP builds can now be f
MPF uses some external libraries to assist with additional information gathering after the dumping process.
- **BinaryObjectScanner** - Protection scanning - [GitHub](https://github.com/SabreTools/BinaryObjectScanner)
- **Binary Object Scanner** - Protection scanning - [GitHub](https://github.com/SabreTools/BinaryObjectScanner)
- **WPFCustomMessageBox.thabse** - Custom message boxes in UI - [GitHub](https://github.com/thabse/WPFCustomMessageBox)
## Contributors

View File

@@ -1,5 +1,5 @@
# version format
version: 3.1.4-{build}
version: 3.1.9-{build}
# pull request template
pull_requests: