Compare commits

..

33 Commits

Author SHA1 Message Date
Matt Nadareski
fc3ef36fef Attempt to add CD to existing actions 2024-02-23 12:36:55 -05:00
Matt Nadareski
6298487346 Fix link in README 2024-02-23 11:26:45 -05:00
Matt Nadareski
727d9844d5 Fix whitespace that got unwhitespaced 2024-02-23 11:18:36 -05:00
Matt Nadareski
72e7619e2d Remove net35 from MPF... again 2024-02-23 11:17:44 -05:00
Matt Nadareski
24b4647037 Remove now-unnecessary restore step 2024-02-23 11:16:44 -05:00
Matt Nadareski
713b3f0557 Fix net35 build issue 2024-02-23 11:11:48 -05:00
Matt Nadareski
f796a9b131 More tweaks to CI 2024-02-23 10:59:40 -05:00
Matt Nadareski
2cdf92bf92 Rename badges for GHA 2024-02-23 10:26:27 -05:00
Matt Nadareski
ccc1687f1a Add GHA CI status badges 2024-02-23 10:23:18 -05:00
Matt Nadareski
6057ec3a59 Split CI workflow files 2024-02-23 10:20:30 -05:00
Matt Nadareski
2a5e736285 Reorganize solution items 2024-02-23 10:11:39 -05:00
Deterous
010ef9016b Add CI via GitHub Workflows (#657)
* Create ci.yml

* Update ci.yml

* Rename to master

* Update CHANGELIST.md
2024-02-23 07:08:59 -08:00
Deterous
02606318b0 Opt-in automatic IRD creation after PS3 dump (#655)
* Opt-in automatic IRD creation after PS3 dump

* Add tabs before an endregion

* Prevent double checking for existing files

* Spin off IRD creation into new thread
2024-02-22 19:55:10 -08:00
Matt Nadareski
d4f641b122 Update README with current build instructions 2024-02-21 19:28:11 -05:00
Matt Nadareski
a1dd6e2d21 Fix misattributed artifact 2024-02-21 11:14:56 -05:00
Matt Nadareski
d35679d688 Make AppVeyor builds framework-dependent 2024-02-21 11:00:18 -05:00
Matt Nadareski
83f5083ce7 Add x86 builds to AppVeyor 2024-02-21 10:59:30 -05:00
Matt Nadareski
5b6457f4b7 Remove DIC and Aaru bundles from CI 2024-02-21 10:51:05 -05:00
Deterous
c6517d526b Hide unavailable dumping programs (#654) 2024-02-21 07:14:39 -08:00
Matt Nadareski
e35f1fc2ec Readd x86 builds by default 2024-02-21 00:52:40 -05:00
Matt Nadareski
14f4128d4a Fix double git hash version (feat. Deterous) 2024-02-21 00:03:39 -05:00
Matt Nadareski
5465252dc7 Port build script fixes from BOS 2024-02-20 23:16:22 -05:00
Matt Nadareski
2573b47a79 Remove debugging lines from build script 2024-02-20 20:52:49 -05:00
Matt Nadareski
fe20905524 Bump version 2024-02-20 10:34:48 -05:00
Deterous
88f19180a4 Update LibIRD, disable UI elements when creating IRD (#653)
* Update LibIRD, disable UI when creating IRD

* Update changelog
2024-02-20 07:25:03 -08:00
Deterous
de89968a1d Add a GUI for PS3 IRD Creation (#647)
* Create a non-functional Create IRD window

* Add PIC and getkey.log options for IRDs

* Disable IRD creation window for unsupport .NET versions

* Finalise UI and parse inputs

* Add LibIRD package

* Manually define PIC in IRD creation

* Better output file browser

* Bump LibIRD version

* Update changelog

* Custom Disc ID, bump LibIRD version

* Ignore custom Disc ID for BD-50

* Provide a status message when creating IRD

* Better logpath enabled logic

* Nicer PIC UX

* Scrollbar only appears for unusually tall PIC
2024-02-18 17:38:28 -08:00
Matt Nadareski
8fc53c91b0 Limit DVD protection outputs (fixes #651) 2024-02-17 23:23:00 -05:00
Matt Nadareski
1a1fbd4b40 Fix Aaru drive parameter generation (fixes #652) 2024-02-17 23:09:29 -05:00
Matt Nadareski
cac6c3049b Add funworld Photo Play detection (fixes #650) 2024-02-15 12:56:30 -05:00
Matt Nadareski
6a6871e922 Write outputs with UTF-8 2024-02-15 12:00:40 -05:00
Deterous
4a02a3efac Fix DIC log parsing for SS version (#649) 2024-02-12 08:30:50 -08:00
Matt Nadareski
f6eb961af4 Make Redumper the default for new users (fixes #638) 2024-02-06 12:57:26 -05:00
Matt Nadareski
faeaaef02a Remove .NET 6 from auto-builds (fixes #646) 2024-02-06 12:50:01 -05:00
31 changed files with 2793 additions and 262 deletions

52
.github/workflows/build_check.yml vendored Normal file
View File

@@ -0,0 +1,52 @@
name: MPF Check
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
project: [MPF.Check]
runtime: [win-x86, win-x64, linux-x64, osx-x64] #[win-x86, win-x64, win-arm64, linux-x64, linux-arm64, osx-x64]
framework: [net8.0] #[net20, net35, net40, net452, net472, net48, netcoreapp3.1, net5.0, net6.0, net7.0, net8.0]
conf: [Release, Debug]
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet publish ${{ matrix.project }}/${{ matrix.project }}.csproj -f ${{ matrix.framework }} -r ${{ matrix.runtime }} -c ${{ matrix.conf == 'Release' && 'Release -p:DebugType=None -p:DebugSymbols=false' || 'Debug'}} --self-contained true --version-suffix ${{ github.sha }} ${{ (startsWith(matrix.framework, 'net5') || startsWith(matrix.framework, 'net6') || startsWith(matrix.framework, 'net7') || startsWith(matrix.framework, 'net8')) && '-p:PublishSingleFile=true' || ''}}
- name: Upload build
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}
path: ${{ matrix.project }}/bin/${{ matrix.conf }}/${{ matrix.framework }}/${{ matrix.runtime }}/publish/
- name: Upload to rolling
uses: ncipollo/release-action@v1
with:
allowUpdates: True
artifacts: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}
generateReleaseNotes: true
omitBody: True
omitBodyDuringUpdate: True
omitNameDuringUpdate: True
prerelease: True
replacesArtifacts: True
tag: "check-rolling"
updateOnlyUnreleased: True

60
.github/workflows/build_ui.yml vendored Normal file
View File

@@ -0,0 +1,60 @@
name: MPF UI
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
project: [MPF]
runtime: [win-x86, win-x64]
framework: [net8.0-windows] #[net40, net452, net472, net48, netcoreapp3.1, net5.0-windows, net6.0-windows, net7.0-windows, net8.0-windows]
conf: [Release, Debug]
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet publish ${{ matrix.project }}/${{ matrix.project }}.csproj -f ${{ matrix.framework }} -r ${{ matrix.runtime }} -c ${{ matrix.conf == 'Release' && 'Release -p:DebugType=None -p:DebugSymbols=false' || 'Debug'}} --self-contained true --version-suffix ${{ github.sha }} ${{ (startsWith(matrix.framework, 'net5') || startsWith(matrix.framework, 'net6') || startsWith(matrix.framework, 'net7') || startsWith(matrix.framework, 'net8')) && '-p:PublishSingleFile=true' || ''}}
- 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
mkdir -p MPF/bin/${{ matrix.conf }}/${{ matrix.framework }}/${{ matrix.runtime }}/publish/Programs/Redumper
mv redumper-2024.01.08_build311-win64/bin/redumper.exe MPF/bin/${{ matrix.conf }}/${{ matrix.framework }}/${{ matrix.runtime }}/publish/Programs/Redumper/
- name: Upload build
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}
path: ${{ matrix.project }}/bin/${{ matrix.conf }}/${{ matrix.framework }}/${{ matrix.runtime }}/publish/
- name: Upload to rolling
uses: ncipollo/release-action@v1
with:
allowUpdates: True
artifacts: "*.zip"
generateReleaseNotes: true
omitBody: True
omitBodyDuringUpdate: True
omitNameDuringUpdate: True
prerelease: True
removeArtifacts: True
replacesArtifacts: True
tag: "ui-rolling"
updateOnlyUnreleased: True

View File

@@ -1,3 +1,41 @@
### WIP (xxxx-xx-xx)
- Remove debugging lines from build script
- Port build script fixes from BOS
- Fix double git hash version (feat. Deterous)
- Readd x86 builds by default
- Hide unavailable dumping programs (Deterous)
- Remove DIC and Aaru bundles from CI
- Add x86 builds to AppVeyor
- Make AppVeyor builds framework-dependent
- Fix misattributed artifact
- Update README with current build instructions
- Opt-in automatic IRD creation after PS3 dump (Deterous)
- Add CI via Github Workflows (Deterous)
- Reorganize solution items
- Split CI workflow files
- Add GHA CI status badges
- Rename badges for GHA
- More tweaks to CI
- Fix net35 build issue
- Remove now-unnecessary restore step
- Remove net35 from MPF... again
- Fix whitespace that got unwhitespaced
- Fix link in README
- Attempt to add CD to existing actions
### 3.1.1 (2024-02-20)
- Remove .NET 6 from auto-builds
- Make Redumper the default for new users
- Fix DIC log parsing for SS version (Deterous)
- Write outputs with UTF-8
- Add funworld Photo Play detection
- Fix Aaru drive parameter generation
- Limit DVD protection outputs
- Add a GUI for PS3 IRD Creation (Deterous)
- Update LibIRD, disable UI elements when creating IRD (Deterous)
### 3.1.0 (2024-02-06)
- Update RedumpLib
@@ -17,8 +55,9 @@
- Prevent crashing on invalid parameters (Deterous)
- Detect CDTV discs (Deterous)
- Differentiate CD32 from CDTV (Deterous)
- Skip warning line in Redumper log
- Normalise Disc Titles in Submission Info (Deterous)
- Skip warning line in Redumper log
- Add a GUI for MPF.Check (Deterous)
- Fix information pulling for CleanRip and UIC
- Add UMD handling for the disc info window
- Detect Photo CD

View File

@@ -6,11 +6,12 @@
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
<OutputType>Exe</OutputType>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<VersionPrefix>3.1.0</VersionPrefix>
<VersionPrefix>3.1.1</VersionPrefix>
<!-- Package Properties -->
<Title>MPF Check</Title>

View File

@@ -83,7 +83,7 @@ namespace MPF.Core.Data
InfoTool.GetPlayStationExecutableInfo(this.Name, out string? serial, out _, out _);
volumeLabel = serial ?? "track";
break;
default:
volumeLabel = "track";
break;
@@ -282,6 +282,17 @@ namespace MPF.Core.Data
return systemFromLabel;
// Get a list of files for quicker checking
#region Arcade
// funworld Photo Play
if (File.Exists(Path.Combine(this.Name, "PP.INF"))
&& Directory.Exists(Path.Combine(this.Name, "PPINC")))
{
return RedumpSystem.funworldPhotoPlay;
}
#endregion
#region Consoles
// Bandai Playdia Quick Interactive System
@@ -439,7 +450,7 @@ namespace MPF.Core.Data
return RedumpSystem.VTechVFlashVSmilePro;
}
#endregion
#endregion
#region Computers
@@ -530,7 +541,7 @@ namespace MPF.Core.Data
}
catch { }
#endregion
#endregion
// Default return
return defaultValue;
@@ -596,7 +607,7 @@ namespace MPF.Core.Data
this.PopulateFromDriveInfo(driveInfo);
}
#endregion
#endregion
#region Helpers

View File

@@ -56,9 +56,9 @@ namespace MPF.Core.Data
{
get
{
var valueString = GetStringSetting(Settings, "InternalProgram", InternalProgram.DiscImageCreator.ToString());
var valueString = GetStringSetting(Settings, "InternalProgram", InternalProgram.Redumper.ToString());
var valueEnum = EnumConverter.ToInternalProgram(valueString);
return valueEnum == InternalProgram.NONE ? InternalProgram.DiscImageCreator : valueEnum;
return valueEnum == InternalProgram.NONE ? InternalProgram.Redumper : valueEnum;
}
set
{
@@ -500,6 +500,20 @@ namespace MPF.Core.Data
set { Settings["DeleteUnnecessaryFiles"] = value.ToString(); }
}
/// <summary>
/// Create a PS3 IRD file after dumping PS3 BD-ROM discs
/// Always returns false if not compiled with .NET Core 6 or newer
/// </summary>
public bool CreateIRDAfterDumping
{
#if NET6_0_OR_GREATER
get { return GetBooleanSetting(Settings, "CreateIRDAfterDumping", false); }
#else
get { return false; }
#endif
set { Settings["CreateIRDAfterDumping"] = value.ToString(); }
}
#endregion
#region Skip Options

View File

@@ -150,8 +150,11 @@ namespace MPF.Core
InternalProgram.DCDumper => null, // TODO: Create correct parameter type when supported
InternalProgram.UmdImageCreator => new Modules.UmdImageCreator.Parameters(parameters) { ExecutablePath = null },
// If no dumping program found, set to null
InternalProgram.NONE => null,
// This should never happen, but it needs a fallback
_ => new Modules.DiscImageCreator.Parameters(parameters) { ExecutablePath = Options.DiscImageCreatorPath },
_ => new Modules.Redumper.Parameters(parameters) { ExecutablePath = Options.RedumperPath },
};
// Set system and type
@@ -183,12 +186,15 @@ namespace MPF.Core
InternalProgram.DiscImageCreator => new Modules.DiscImageCreator.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options),
InternalProgram.Redumper => new Modules.Redumper.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options),
// If no dumping program found, set to null
InternalProgram.NONE => null,
// This should never happen, but it needs a fallback
_ => new Modules.DiscImageCreator.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options),
_ => new Modules.Redumper.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options),
};
// Generate and return the param string
return Parameters.GenerateParameters();
return Parameters?.GenerateParameters();
}
return null;
@@ -281,6 +287,9 @@ namespace MPF.Core
Func<SubmissionInfo?, (bool?, SubmissionInfo?)>? processUserInfo = null,
SubmissionInfo? seedInfo = null)
{
if (Parameters == null)
return Result.Failure("Error! Current configuration is not supported!");
resultProgress?.Report(Result.Success("Gathering submission information... please wait!"));
// Get the output directory and filename separately
@@ -415,6 +424,19 @@ namespace MPF.Core
resultProgress?.Report(Result.Failure(deleteResult));
}
#if NET6_0_OR_GREATER
// Create PS3 IRD, if required
if (Options.CreateIRDAfterDumping && System == RedumpSystem.SonyPlayStation3 && Type == MediaType.BluRay)
{
resultProgress?.Report(Result.Success("Creating IRD... please wait!"));
(bool deleteSuccess, string deleteResult) = await InfoTool.WriteIRD(OutputPath, submissionInfo?.Extras?.DiscKey, submissionInfo?.Extras?.DiscID, submissionInfo?.Extras?.PIC, submissionInfo?.SizeAndChecksums?.Layerbreak, submissionInfo?.SizeAndChecksums?.CRC32);
if (deleteSuccess)
resultProgress?.Report(Result.Success(deleteResult));
else
resultProgress?.Report(Result.Failure(deleteResult));
}
#endif
resultProgress?.Report(Result.Success("Submission information process complete!"));
return Result.Success();
}

View File

@@ -1304,7 +1304,7 @@ namespace MPF.Core
else if (!string.IsNullOrEmpty(outputDirectory) && !string.IsNullOrEmpty(filenameSuffix))
path = Path.Combine(outputDirectory, $"!submissionInfo_{filenameSuffix}.txt");
using var sw = new StreamWriter(File.Open(path, FileMode.Create, FileAccess.Write));
using var sw = new StreamWriter(File.Open(path, FileMode.Create, FileAccess.Write), Encoding.UTF8);
foreach (string line in lines)
{
sw.WriteLine(line);
@@ -1411,7 +1411,7 @@ namespace MPF.Core
else if (!string.IsNullOrEmpty(outputDirectory) && !string.IsNullOrEmpty(filenameSuffix))
path = Path.Combine(outputDirectory, $"!protectionInfo{filenameSuffix}.txt");
using var sw = new StreamWriter(File.Open(path, FileMode.Create, FileAccess.Write));
using var sw = new StreamWriter(File.Open(path, FileMode.Create, FileAccess.Write), Encoding.UTF8);
List<string> sortedKeys = [.. info.CopyProtection.FullProtections.Keys.OrderBy(k => k)];
foreach (string key in sortedKeys)
@@ -1495,6 +1495,58 @@ namespace MPF.Core
return files;
}
#if NET6_0_OR_GREATER
/// <summary>
/// Create an IRD and write it to the specified output directory with optional filename suffix
/// </summary>
/// <param name="outputDirectory">Output folder to write to</param>
/// <param name="filenameSuffix">Optional suffix to append to the filename</param>
/// <param name="outputFilename">Output filename to use as the base path</param>
/// <returns>True on success, false on error</returns>
public static async Task<(bool, string)> WriteIRD(string isoPath, string? discKeyString, string? discIDString, string? picString, long? layerbreak, string? crc32)
{
try
{
// Output IRD file path
string irdPath = Path.ChangeExtension(isoPath, ".ird");
// Parse disc key from submission info (Required)
byte[]? discKey = Tools.ParseHexKey(discKeyString);
if (discKey == null)
return (false, "Failed to create IRD: No key provided");
// Parse Disc ID from submission info (Optional)
byte[]? discID = Tools.ParseDiscID(discIDString);
// Parse PIC from submission info (Optional)
byte[]? pic = Tools.ParsePIC(picString);
// Parse CRC32 strings into ISO hash for Unique ID field (Optional)
uint? uid = Tools.ParseCRC32(crc32);
// Ensure layerbreak value is valid (Optional)
layerbreak = Tools.ParseLayerbreak(layerbreak);
// Create Redump-style reproducible IRD
LibIRD.ReIRD ird = await Task.Run(() => new LibIRD.ReIRD(isoPath, discKey, layerbreak, uid));
if (pic != null)
ird.PIC = pic;
if (discID != null && ird.DiscID[15] != 0x00)
ird.DiscID = discID;
// Write IRD to file
ird.Write(irdPath);
return (true, "IRD created!");
}
catch (Exception)
{
// We don't care what the error is
return (false, "Failed to create IRD");
}
}
#endif
#endregion
#region Normalization

View File

@@ -5,11 +5,12 @@
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<VersionPrefix>3.1.0</VersionPrefix>
<VersionPrefix>3.1.1</VersionPrefix>
<!-- Package Properties -->
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
@@ -60,4 +61,8 @@
<PackageReference Include="SabreTools.Serialization" Version="1.3.1" />
</ItemGroup>
<ItemGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`))">
<PackageReference Include="LibIRD" Version="0.6.0" />
</ItemGroup>
</Project>

View File

@@ -1067,10 +1067,8 @@ namespace MPF.Core.Modules.Aaru
// Handle filenames based on command, if necessary
switch (BaseCommand)
{
// Input value only
// Input value only (file path)
case CommandStrings.ArchivePrefixLong + " " + CommandStrings.ArchiveInfo:
case CommandStrings.DevicePrefixLong + " " + CommandStrings.DeviceInfo:
case CommandStrings.DevicePrefixLong + " " + CommandStrings.DeviceReport:
case CommandStrings.FilesystemPrefixLong + " " + CommandStrings.FilesystemInfo:
case CommandStrings.FilesystemPrefixLong + " " + CommandStrings.FilesystemListLong:
case CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageChecksumLong:
@@ -1080,12 +1078,21 @@ namespace MPF.Core.Modules.Aaru
case CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageInfo:
case CommandStrings.ImagePrefixLong + " " + CommandStrings.ImagePrint:
case CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageVerify:
if (string.IsNullOrEmpty(InputValue))
return null;
parameters.Add($"\"{InputValue}\"");
break;
// Input value only (device path)
case CommandStrings.DevicePrefixLong + " " + CommandStrings.DeviceInfo:
case CommandStrings.DevicePrefixLong + " " + CommandStrings.DeviceReport:
case CommandStrings.MediaPrefixLong + " " + CommandStrings.MediaInfo:
case CommandStrings.MediaPrefixLong + " " + CommandStrings.MediaScan:
if (string.IsNullOrEmpty(InputValue))
return null;
parameters.Add($"\"{InputValue}\"");
parameters.Add(InputValue!.TrimEnd('\\'));
break;
// Two input values
@@ -1097,10 +1104,9 @@ namespace MPF.Core.Modules.Aaru
parameters.Add($"\"{Input2Value}\"");
break;
// Input and Output value
// Input and Output value (file path)
case CommandStrings.FilesystemPrefixLong + " " + CommandStrings.FilesystemExtract:
case CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageConvert:
case CommandStrings.MediaPrefixLong + " " + CommandStrings.MediaDump:
if (string.IsNullOrEmpty(InputValue) || string.IsNullOrEmpty(OutputValue))
return null;
@@ -1108,6 +1114,15 @@ namespace MPF.Core.Modules.Aaru
parameters.Add($"\"{OutputValue}\"");
break;
// Input and Output value (device path)
case CommandStrings.MediaPrefixLong + " " + CommandStrings.MediaDump:
if (string.IsNullOrEmpty(InputValue) || string.IsNullOrEmpty(OutputValue))
return null;
parameters.Add(InputValue!.TrimEnd('\\'));
parameters.Add($"\"{OutputValue}\"");
break;
// Remote host value only
case CommandStrings.DevicePrefixLong + " " + CommandStrings.DeviceList:
case CommandStrings.Remote:

View File

@@ -531,13 +531,13 @@ namespace MPF.Core.Modules.DiscImageCreator
}
// Needed for some odd copy protections
info.CopyProtection!.Protection = GetDVDProtection($"{basePath}_CSSKey.txt", $"{basePath}_disc.txt") ?? string.Empty;
info.CopyProtection!.Protection = GetDVDProtection($"{basePath}_CSSKey.txt", $"{basePath}_disc.txt", false) ?? string.Empty;
break;
case RedumpSystem.DVDAudio:
case RedumpSystem.DVDVideo:
info.CopyProtection!.Protection = GetDVDProtection($"{basePath}_CSSKey.txt", $"{basePath}_disc.txt") ?? string.Empty;
info.CopyProtection!.Protection = GetDVDProtection($"{basePath}_CSSKey.txt", $"{basePath}_disc.txt", true) ?? string.Empty;
break;
case RedumpSystem.KonamiPython2:
@@ -2820,8 +2820,9 @@ namespace MPF.Core.Modules.DiscImageCreator
/// </summary>
/// <param name="cssKey">_CSSKey.txt file location</param>
/// <param name="disc">_disc.txt file location</param>
/// <param name="includeAlways">Indicates whether region and protection type are always included</param>
/// <returns>Formatted string representing the DVD protection, null on error</returns>
private static string? GetDVDProtection(string cssKey, string disc)
private static string? GetDVDProtection(string cssKey, string disc, bool includeAlways)
{
// If one of the files doesn't exist, we can't get info from them
if (!File.Exists(disc))
@@ -2903,6 +2904,15 @@ namespace MPF.Core.Modules.DiscImageCreator
catch { }
}
// Filter out if we're not always including information
if (!includeAlways)
{
if (region == "1 2 3 4 5 6 7 8")
region = null;
if (copyrightProtectionSystemType == "No")
copyrightProtectionSystemType = null;
}
// Now we format everything we can
string protection = string.Empty;
if (!string.IsNullOrEmpty(region))
@@ -3720,6 +3730,9 @@ namespace MPF.Core.Modules.DiscImageCreator
// This flag is needed because recent versions of DIC include security data twice
bool foundSecuritySectors = false;
// SS version for all Kreon DIC dumps is v1
ssver = "01";
try
{
using var sr = File.OpenText(disc);
@@ -3729,11 +3742,13 @@ namespace MPF.Core.Modules.DiscImageCreator
if (line == null)
break;
// Security Sector version
// XGD version (1 = Xbox, 2 = Xbox360)
/*
if (line.StartsWith("Version of challenge table"))
{
ssver = line.Split(' ')[4]; // "Version of challenge table: <VER>"
xgdver = line.Split(' ')[4]; // "Version of challenge table: <VER>"
}
*/
// Security Sector ranges
else if (line.StartsWith("Number of security sector ranges:") && !foundSecuritySectors)
@@ -3808,6 +3823,9 @@ namespace MPF.Core.Modules.DiscImageCreator
// This flag is needed because recent versions of DIC include security data twice
bool foundSecuritySectors = false;
// SS version for all Kreon DIC dumps is v1
ssver = "01";
try
{
using var sr = File.OpenText(disc);
@@ -3817,11 +3835,13 @@ namespace MPF.Core.Modules.DiscImageCreator
if (line == null)
break;
// Security Sector version
// XGD version (1 = Xbox, 2 = Xbox360)
/*
if (line.StartsWith("Version of challenge table"))
{
ssver = line.Split(' ')[4]; // "Version of challenge table: <VER>"
xgdver = line.Split(' ')[4]; // "Version of challenge table: <VER>"
}
*/
// Security Sector ranges
else if (line.StartsWith("Number of security sector ranges:") && !foundSecuritySectors)

View File

@@ -404,12 +404,12 @@ namespace MPF.Core.Modules.Redumper
info.CopyProtection!.SecuROMData = GetSecuROMData($"{basePath}.log") ?? string.Empty;
// Needed for some odd copy protections
info.CopyProtection!.Protection = GetDVDProtection($"{basePath}.log") ?? string.Empty;
info.CopyProtection!.Protection = GetDVDProtection($"{basePath}.log", false) ?? string.Empty;
break;
case RedumpSystem.DVDAudio:
case RedumpSystem.DVDVideo:
info.CopyProtection!.Protection = GetDVDProtection($"{basePath}.log") ?? string.Empty;
info.CopyProtection!.Protection = GetDVDProtection($"{basePath}.log", true) ?? string.Empty;
break;
case RedumpSystem.KonamiPython2:
@@ -1557,8 +1557,9 @@ namespace MPF.Core.Modules.Redumper
/// Get the DVD protection information, if possible
/// </summary>
/// <param name="log">Log file location</param>
/// <param name="includeAlways">Indicates whether region and protection type are always included</param>
/// <returns>Formatted string representing the DVD protection, null on error</returns>
private static string? GetDVDProtection(string log)
private static string? GetDVDProtection(string log, bool includeAlways)
{
// If one of the files doesn't exist, we can't get info from them
if (!File.Exists(log))
@@ -1628,6 +1629,15 @@ namespace MPF.Core.Modules.Redumper
catch { }
}
// Filter out if we're not always including information
if (!includeAlways)
{
if (region == "1 2 3 4 5 6 7 8")
region = null;
if (copyrightProtectionSystemType == "No")
copyrightProtectionSystemType = null;
}
// Now we format everything we can
string protection = string.Empty;
if (!string.IsNullOrEmpty(region))

View File

@@ -354,7 +354,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.DiscImageCreator, InternalProgram.Aaru, InternalProgram.CleanRip, InternalProgram.Redumper, InternalProgram.UmdImageCreator };
var internalPrograms = new List<InternalProgram> { InternalProgram.Redumper, InternalProgram.DiscImageCreator, InternalProgram.Aaru, InternalProgram.CleanRip, InternalProgram.UmdImageCreator };
InternalPrograms = internalPrograms.Select(ip => new Element<InternalProgram>(ip)).ToList();
// Select the current default dumping program

File diff suppressed because it is too large Load Diff

View File

@@ -76,6 +76,20 @@ namespace MPF.Core.UI.ViewModels
#region Properties
/// <summary>
/// Indicates the inability to create IRDs
/// </summary>
public bool CannotCreateIRD
{
get => _cannotCreateIRD;
set
{
_cannotCreateIRD = value;
TriggerPropertyChanged(nameof(CannotCreateIRD));
}
}
private bool _cannotCreateIRD;
/// <summary>
/// Indicates the status of the check dump menu item
/// </summary>
@@ -90,6 +104,20 @@ namespace MPF.Core.UI.ViewModels
}
private bool _checkDumpMenuItemEnabled;
/// <summary>
/// Indicates the status of the create IRD menu item
/// </summary>
public bool CreateIRDMenuItemEnabled
{
get => _createIRDMenuItemEnabled;
set
{
_createIRDMenuItemEnabled = value;
TriggerPropertyChanged(nameof(CreateIRDMenuItemEnabled));
}
}
private bool _createIRDMenuItemEnabled;
/// <summary>
/// Indicates the status of the options menu item
/// </summary>
@@ -522,6 +550,13 @@ namespace MPF.Core.UI.ViewModels
OptionsMenuItemEnabled = true;
CheckDumpMenuItemEnabled = true;
#if NET6_0_OR_GREATER
CreateIRDMenuItemEnabled = true;
CannotCreateIRD = false;
#else
CreateIRDMenuItemEnabled = false;
CannotCreateIRD = true;
#endif
SystemTypeComboBoxEnabled = true;
MediaTypeComboBoxEnabled = true;
OutputPathTextBoxEnabled = true;
@@ -686,16 +721,23 @@ namespace MPF.Core.UI.ViewModels
bool cachedCanExecuteSelectionChanged = CanExecuteSelectionChanged;
DisableEventHandlers();
// Create a static list of supported programs, not everything
InternalPrograms = Enum.GetValues(typeof(InternalProgram)).Cast<InternalProgram>().Where(ip => InternalProgramExists(ip)).Select(ip => new Element<InternalProgram>(ip)).ToList();
// Get the current internal program
InternalProgram internalProgram = this.Options.InternalProgram;
// Create a static list of supported programs, not everything
var internalPrograms = new List<InternalProgram> { InternalProgram.DiscImageCreator, InternalProgram.Aaru, InternalProgram.Redumper };
InternalPrograms = internalPrograms.Select(ip => new Element<InternalProgram>(ip)).ToList();
// Select the current default dumping program
int currentIndex = InternalPrograms.FindIndex(m => m == internalProgram);
this.CurrentProgram = (currentIndex > -1 ? InternalPrograms[currentIndex].Value : InternalPrograms[0].Value);
if (InternalPrograms.Count == 0)
{
// If no programs are found, default to InternalProgram.NONE
this.CurrentProgram = InternalProgram.NONE;
}
else
{
int currentIndex = InternalPrograms.FindIndex(m => m == internalProgram);
this.CurrentProgram = (currentIndex > -1 ? InternalPrograms[currentIndex].Value : InternalPrograms[0].Value);
}
// Reenable event handlers, if necessary
if (cachedCanExecuteSelectionChanged) EnableEventHandlers();
@@ -1254,6 +1296,10 @@ namespace MPF.Core.UI.ViewModels
public void DisableAllUIElements()
{
OptionsMenuItemEnabled = false;
CheckDumpMenuItemEnabled = false;
#if NET6_0_OR_GREATER
CreateIRDMenuItemEnabled = false;
#endif
SystemTypeComboBoxEnabled = false;
MediaTypeComboBoxEnabled = false;
OutputPathTextBoxEnabled = false;
@@ -1274,6 +1320,10 @@ namespace MPF.Core.UI.ViewModels
public void EnableAllUIElements()
{
OptionsMenuItemEnabled = true;
CheckDumpMenuItemEnabled = true;
#if NET6_0_OR_GREATER
CreateIRDMenuItemEnabled = true;
#endif
SystemTypeComboBoxEnabled = true;
MediaTypeComboBoxEnabled = true;
OutputPathTextBoxEnabled = true;
@@ -1298,7 +1348,10 @@ namespace MPF.Core.UI.ViewModels
// Get the status to write out
Result result = Tools.GetSupportStatus(_environment.System, _environment.Type);
this.Status = result.Message;
if (this.CurrentProgram == InternalProgram.NONE || _environment.Parameters == null)
this.Status = "No dumping program found";
else
this.Status = result.Message;
// Enable or disable the button
this.StartStopButtonEnabled = result && ShouldEnableDumpingButton();
@@ -1655,8 +1708,8 @@ namespace MPF.Core.UI.ViewModels
_environment.Drive?.RefreshDrive();
// Output to the label and log
this.Status = "Starting dumping process... Please wait!";
LogLn("Starting dumping process... Please wait!");
this.Status = "Starting dumping process... please wait!";
LogLn("Starting dumping process... please wait!");
if (this.Options.ToolsInSeparateWindow)
LogLn("Look for the separate command window for more details");
else
@@ -1749,6 +1802,9 @@ namespace MPF.Core.UI.ViewModels
/// <returns>True if dumping should start, false otherwise</returns>
private bool ValidateBeforeDumping()
{
if (Parameters == null)
return false;
// Validate that we have an output path of any sort
if (string.IsNullOrEmpty(_environment?.OutputPath))
{
@@ -1788,49 +1844,51 @@ namespace MPF.Core.UI.ViewModels
return false;
}
}
// If a complete dump exists from a different program
InternalProgram? programFound = null;
if (programFound == null && _environment.InternalProgram != InternalProgram.Aaru)
else
{
Modules.Aaru.Parameters parameters = new("")
// If a complete dump exists from a different program
InternalProgram? programFound = null;
if (programFound == null && _environment.InternalProgram != InternalProgram.Aaru)
{
Type = _environment.Type,
System = _environment.System
};
(bool foundOtherFiles, _) = parameters.FoundAllFiles(outputDirectory, outputFilename, true);
if (foundOtherFiles)
programFound = InternalProgram.Aaru;
}
if (programFound == null && _environment.InternalProgram != InternalProgram.DiscImageCreator)
{
Modules.DiscImageCreator.Parameters parameters = new("")
Modules.Aaru.Parameters parameters = new("")
{
Type = _environment.Type,
System = _environment.System
};
(bool foundOtherFiles, _) = parameters.FoundAllFiles(outputDirectory, outputFilename, true);
if (foundOtherFiles)
programFound = InternalProgram.Aaru;
}
if (programFound == null && _environment.InternalProgram != InternalProgram.DiscImageCreator)
{
Type = _environment.Type,
System = _environment.System
};
(bool foundOtherFiles, _) = parameters.FoundAllFiles(outputDirectory, outputFilename, true);
if (foundOtherFiles)
programFound = InternalProgram.DiscImageCreator;
}
if (programFound == null && _environment.InternalProgram != InternalProgram.Redumper)
{
Modules.Redumper.Parameters parameters = new("")
Modules.DiscImageCreator.Parameters parameters = new("")
{
Type = _environment.Type,
System = _environment.System
};
(bool foundOtherFiles, _) = parameters.FoundAllFiles(outputDirectory, outputFilename, true);
if (foundOtherFiles)
programFound = InternalProgram.DiscImageCreator;
}
if (programFound == null && _environment.InternalProgram != InternalProgram.Redumper)
{
Type = _environment.Type,
System = _environment.System
};
(bool foundOtherFiles, _) = parameters.FoundAllFiles(outputDirectory, outputFilename, true);
if (foundOtherFiles)
programFound = InternalProgram.Redumper;
}
if (programFound != null && _displayUserMessage != null)
{
bool? mbresult = _displayUserMessage("Overwrite?", $"A complete dump from {programFound} already exists! Dumping here may cause issues. Are you sure you want to overwrite?", 2, true);
if (mbresult != true)
Modules.Redumper.Parameters parameters = new("")
{
Type = _environment.Type,
System = _environment.System
};
(bool foundOtherFiles, _) = parameters.FoundAllFiles(outputDirectory, outputFilename, true);
if (foundOtherFiles)
programFound = InternalProgram.Redumper;
}
if (programFound != null && _displayUserMessage != null)
{
LogLn("Dumping aborted!");
return false;
bool? mbresult = _displayUserMessage("Overwrite?", $"A complete dump from {programFound} already exists! Dumping here may cause issues. Are you sure you want to overwrite?", 2, true);
if (mbresult != true)
{
LogLn("Dumping aborted!");
return false;
}
}
}
@@ -1857,7 +1915,30 @@ namespace MPF.Core.UI.ViewModels
return true;
}
#endregion
/// <summary>
/// Checks whether a internal program is found in its path
/// </summary>
/// <param name="program">Program to check for</param>
/// <returns>True if the program is found, false otherwise</returns>
private bool InternalProgramExists(InternalProgram program)
{
try
{
return program switch
{
InternalProgram.Redumper => File.Exists(this.Options.RedumperPath),
InternalProgram.Aaru => File.Exists(this.Options.AaruPath),
InternalProgram.DiscImageCreator => File.Exists(this.Options.DiscImageCreatorPath),
_ => false,
};
}
catch
{
return false;
}
}
#endregion
#region Progress Reporting

View File

@@ -42,6 +42,19 @@ namespace MPF.Core.UI.ViewModels
/// <inheritdoc/>
public event PropertyChangedEventHandler? PropertyChanged;
/// <summary>
/// Used to indicate whether LibIRD is supported
/// Whether compiled with .NET Core 6 or greater
/// </summary>
public static bool CreateIRDSupported
{
#if NET6_0_OR_GREATER
get { return true; }
#else
get { return false; }
#endif
}
#endregion
#region Lists
@@ -81,7 +94,7 @@ namespace MPF.Core.UI.ViewModels
/// </summary>
private static List<Element<InternalProgram>> PopulateInternalPrograms()
{
var internalPrograms = new List<InternalProgram> { InternalProgram.DiscImageCreator, InternalProgram.Aaru, InternalProgram.Redumper };
var internalPrograms = new List<InternalProgram> { InternalProgram.Redumper, InternalProgram.DiscImageCreator, InternalProgram.Aaru };
return internalPrograms.Select(ip => new Element<InternalProgram>(ip)).ToList();
}

View File

@@ -1,4 +1,5 @@
using System;
using System.IO;
using System.Net;
using System.Reflection;
using MPF.Core.Data;
@@ -71,6 +72,36 @@ namespace MPF.Core.Utilities
return true;
}
/// <summary>
/// Converts a hex string into a byte array
/// </summary>
/// <param name="hex">Hex string</param>
/// <returns>Converted byte array, or null if invalid hex string</returns>
public static byte[]? HexStringToByteArray(string? hexString)
{
// Valid hex string must be an even number of characters
if (string.IsNullOrEmpty(hexString) || hexString!.Length % 2 == 1)
return null;
// Convert ASCII to byte via lookup table
int[] hexLookup = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F];
byte[] byteArray = new byte[hexString.Length / 2];
for (int i = 0; i < hexString.Length; i += 2)
{
// Convert next two chars to ASCII value relative to '0'
int a = Char.ToUpper(hexString[i]) - '0';
int b = Char.ToUpper(hexString[i + 1]) - '0';
// Ensure hex string only has '0' through '9' and 'A' through 'F' (case insensitive)
if ((a < 0 || b < 0 || a > 22 || b > 22) || (a > 10 && a < 17) || (b > 10 && b < 17))
return null;
byteArray[i / 2] = (byte)(hexLookup[a] << 4 | hexLookup[b]);
}
return byteArray;
}
#endregion
#region Support
@@ -261,5 +292,328 @@ namespace MPF.Core.Utilities
}
#endregion
#region PlayStation 3
/// <summary>
/// Validates a getkey log to check for presence of valid PS3 key
/// </summary>
/// <param name="logPath">Path to getkey log file</param>
/// <param name="key">Output 16 byte key, null if not valid</param>
/// <returns>True if path to log file contains valid key, false otherwise</returns>
public static bool ParseGetKeyLog(string? logPath, out byte[]? key, out byte[]? id, out byte[]? pic)
{
key = null;
id = null;
pic = null;
if (string.IsNullOrEmpty(logPath))
return false;
try
{
if (!File.Exists(logPath))
return false;
// Protect from attempting to read from really long files
FileInfo logFile = new(logPath);
if (logFile.Length > 65536)
return false;
// Read from .getkey.log file
using StreamReader sr = File.OpenText(logPath);
// Determine whether GetKey was successful
string? line;
while ((line = sr.ReadLine()) != null && line.Trim().StartsWith("get_dec_key succeeded!") == false) ;
if (line == null)
return false;
// Look for Disc Key in log
while ((line = sr.ReadLine()) != null && line.Trim().StartsWith("disc_key = ") == false) ;
// If end of file reached, no key found
if (line == null)
return false;
// Get Disc Key from log
string discKeyStr = line.Substring("disc_key = ".Length);
// Validate Disc Key from log
if (discKeyStr.Length != 32)
return false;
// Convert Disc Key to byte array
key = Tools.HexStringToByteArray(discKeyStr);
if (key == null)
return false;
// Read Disc ID
while ((line = sr.ReadLine()) != null && line.Trim().StartsWith("disc_id = ") == false) ;
// If end of file reached, no ID found
if (line == null)
return false;
// Get Disc ID from log
string discIDStr = line.Substring("disc_id = ".Length);
// Validate Disc ID from log
if (discIDStr.Length != 32)
return false;
// Replace X's in Disc ID with 00000001
discIDStr = discIDStr.Substring(0, 24) + "00000001";
// Convert Disc ID to byte array
id = Tools.HexStringToByteArray(discIDStr);
if (id == null)
return false;
// Look for PIC in log
while ((line = sr.ReadLine()) != null && line.Trim().StartsWith("PIC:") == false) ;
// If end of file reached, no PIC found
if (line == null)
return false;
// Get PIC from log
string discPICStr = "";
for (int i = 0; i < 8; i++)
discPICStr += sr.ReadLine();
if (discPICStr == null)
return false;
// Validate PIC from log
if (discPICStr.Length != 256)
return false;
// Convert PIC to byte array
pic = Tools.HexStringToByteArray(discPICStr.Substring(0, 230));
if (pic == null)
return false;
// Double check for warnings in .getkey.log
while ((line = sr.ReadLine()) != null)
{
string t = line.Trim();
if (t.StartsWith("WARNING"))
return false;
else if (t.StartsWith("SUCCESS"))
return true;
}
}
catch
{
// We are not concerned with the error
return false;
}
return true;
}
/// <summary>
/// Validates a hexadecimal disc ID
/// </summary>
/// <param name="discID">String representing hexadecimal disc ID</param>
/// <returns>True if string is a valid disc ID, false otherwise</returns>
public static byte[]? ParseDiscID(string? discID)
{
if (string.IsNullOrEmpty(discID))
return null;
string cleandiscID = discID!.Trim().Replace("\n", string.Empty);
if (discID!.Length != 32)
return null;
// Censor last 4 bytes by replacing with 0x00000001
cleandiscID = cleandiscID.Substring(0, 24) + "00000001";
// Convert to byte array, null if invalid hex string
byte[]? id = Tools.HexStringToByteArray(cleandiscID);
return id;
}
/// <summary>
/// Validates a key file to check for presence of valid PS3 key
/// </summary>
/// <param name="keyPath">Path to key file</param>
/// <returns>Output 16 byte key, null if not valid</returns>
public static byte[]? ParseKeyFile(string? keyPath)
{
if (string.IsNullOrEmpty(keyPath))
return null;
// Try read from key file
try
{
if (!File.Exists(keyPath))
return null;
// Key file must be exactly 16 bytes long
FileInfo keyFile = new(keyPath);
if (keyFile.Length != 16)
return null;
byte[] key = new byte[16];
// Read 16 bytes from Key file
using FileStream fs = new(keyPath, FileMode.Open, FileAccess.Read);
using BinaryReader reader = new(fs);
int numBytes = reader.Read(key, 0, 16);
if (numBytes != 16)
return null;
return key;
}
catch
{
// Not concerned with error
return null;
}
}
/// <summary>
/// Validates a hexadecimal key
/// </summary>
/// <param name="hexKey">String representing hexadecimal key</param>
/// <returns>Output 16 byte key, null if not valid</returns>
public static byte[]? ParseHexKey(string? hexKey)
{
if (string.IsNullOrEmpty(hexKey))
return null;
string cleanHexKey = hexKey!.Trim().Replace("\n", string.Empty);
if (cleanHexKey.Length != 32)
return null;
// Convert to byte array, null if invalid hex string
byte[]? key = Tools.HexStringToByteArray(cleanHexKey);
return key;
}
/// <summary>
/// Validates a PIC file path
/// </summary>
/// <param name="picPath">Path to PIC file</param>
/// <returns>Output PIC byte array, null if not valid</returns>
public static byte[]? ParsePICFile(string? picPath)
{
if (string.IsNullOrEmpty(picPath))
return null;
// Try read from PIC file
try
{
if (!File.Exists(picPath))
return null;
// PIC file must be at least 115 bytes long
FileInfo picFile = new(picPath);
if (picFile.Length < 115)
return null;
byte[] pic = new byte[115];
// Read 115 bytes from PIC file
using FileStream fs = new(picPath, FileMode.Open, FileAccess.Read);
using BinaryReader reader = new(fs);
int numBytes = reader.Read(pic, 0, 115);
if (numBytes != 115)
return null;
// Validate that a PIC was read by checking first 6 bytes
if (pic[0] != 0x10 ||
pic[1] != 0x02 ||
pic[2] != 0x00 ||
pic[3] != 0x00 ||
pic[4] != 0x44 ||
pic[5] != 0x49)
return null;
return pic;
}
catch
{
// Not concerned with error
return null;
}
}
/// <summary>
/// Validates a PIC
/// </summary>
/// <param name="inputPIC">String representing PIC</param>
/// <returns>Output PIC byte array, null if not valid</returns>
public static byte[]? ParsePIC(string? inputPIC)
{
if (string.IsNullOrEmpty(inputPIC))
return null;
string cleanPIC = inputPIC!.Trim().Replace("\n", string.Empty);
if (cleanPIC.Length < 230)
return null;
// Convert to byte array, null if invalid hex string
byte[]? pic = Tools.HexStringToByteArray(cleanPIC.Substring(0, 230));
return pic;
}
/// <summary>
/// Validates a string representing a layerbreak value (in sectors)
/// </summary>
/// <param name="inputLayerbreak">String representing layerbreak value</param>
/// <param name="layerbreak">Output layerbreak value, null if not valid</param>
/// <returns>True if layerbreak is valid, false otherwise</returns>
public static long? ParseLayerbreak(string? inputLayerbreak)
{
if (string.IsNullOrEmpty(inputLayerbreak))
return null;
if (!long.TryParse(inputLayerbreak, out long layerbreak))
return null;
return ParseLayerbreak(layerbreak);
}
/// <summary>
/// Validates a layerbreak value (in sectors)
/// </summary>
/// <param name="inputLayerbreak">Number representing layerbreak value</param>
/// <param name="layerbreak">Output layerbreak value, null if not valid</param>
/// <returns>True if layerbreak is valid, false otherwise</returns>
public static long? ParseLayerbreak(long? layerbreak)
{
// Check that layerbreak is positive number and smaller than largest disc size (in sectors)
if (layerbreak <= 0 || layerbreak > 24438784)
return null;
return layerbreak;
}
/// <summary>
/// Converts a CRC32 hash hex string into uint32 representation
/// </summary>
/// <param name="inputLayerbreak">Hex string representing CRC32 hash</param>
/// <param name="layerbreak">Output CRC32 value, null if not valid</param>
/// <returns>True if CRC32 hash string is valid, false otherwise</returns>
public static uint? ParseCRC32(string? inputCRC32)
{
if (string.IsNullOrEmpty(inputCRC32))
return null;
byte[]? crc32 = Tools.HexStringToByteArray(inputCRC32);
if (crc32 == null || crc32.Length != 4)
return null;
return (uint)(0x01000000 * crc32[0] + 0x00010000 * crc32[1] + 0x00000100 * crc32[2] + 0x00000001 * crc32[3]);
}
#endregion
}
}

View File

@@ -6,12 +6,13 @@
<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<ImportFrameworkWinFXTargets Condition="$(TargetFramework.StartsWith(`net3`))">true</ImportFrameworkWinFXTargets>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<VersionPrefix>3.1.0</VersionPrefix>
<VersionPrefix>3.1.1</VersionPrefix>
<UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF>

View File

@@ -75,9 +75,6 @@
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label x:Name="InputPathLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="Input Path"/>

View File

@@ -132,6 +132,7 @@ namespace MPF.UI.Core.Windows
WinForms.FileDialog fileDialog = new WinForms.OpenFileDialog
{
InitialDirectory = directory,
Filter = "Disc Images|*.iso;*.cue;*.aaruf|All Files|*.*",
};
WinForms.DialogResult result = fileDialog.ShowDialog();

View File

@@ -0,0 +1,209 @@
<coreWindows:WindowBase x:Class="MPF.UI.Core.Windows.CreateIRDWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:core="clr-namespace:MPF.UI.Core"
xmlns:coreWindows="clr-namespace:MPF.UI.Core.Windows"
xmlns:viewModels="clr-namespace:MPF.Core.UI.ViewModels;assembly=MPF.Core"
mc:Ignorable="d"
Title="Create PS3 IRD" Width="600" WindowStyle="None"
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
BorderBrush="DarkGray" BorderThickness="2">
<Window.DataContext>
<viewModels:CreateIRDViewModel/>
</Window.DataContext>
<Window.Resources>
<core:ElementConverter x:Key="ElementConverter" />
</Window.Resources>
<Grid>
<StackPanel Orientation="Vertical">
<Grid Margin="0,2,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="/Images/Icon.ico" Height="20" Width="20" Margin="1" />
<Label Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" MouseDown="TitleMouseDown">
<Label.Content>
<Run FontWeight="Bold" Text="Create PS3 IRD" />
</Label.Content>
<Label.ContextMenu>
<ContextMenu Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Style="{DynamicResource CustomContextMenuStyle}">
<MenuItem Header="Minimize" Click="MinimizeButtonClick" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}"/>
<MenuItem Header="Close" Click="CloseButtonClick" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}"/>
</ContextMenu>
</Label.ContextMenu>
</Label>
<Grid Grid.Column="5">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button x:Name="MinimizeButton" Grid.Column="0" BorderThickness="0" Background="Transparent" Style="{DynamicResource CustomButtonStyle}" Click="MinimizeButtonClick">
<Path Data="M 0,0 L 10,0" Stroke="{Binding Path=Foreground,RelativeSource={RelativeSource AncestorType={x:Type Button}}}" StrokeThickness="1"/>
</Button>
<Button x:Name="CloseButton" Grid.Column="1" BorderThickness="0" Background="Transparent" Style="{DynamicResource CustomButtonStyle}" Click="CloseButtonClick">
<Path Data="M 0,0 L 12,12 M 0,12 L 12,0" Stroke="{Binding Path=Foreground,RelativeSource={RelativeSource AncestorType={x:Type Button}}}" StrokeThickness="1"/>
</Button>
</Grid>
</Grid>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" Header="Input">
<Grid Margin="5,5,5,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2.5*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label x:Name="InputPathLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="PS3 ISO Path"/>
<TextBox x:Name="InputPathTextBox" Grid.Row="0" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
Text="{Binding InputPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding InputPathTextBoxEnabled}" />
<Button x:Name="InputPathBrowseButton" Grid.Row="0" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
IsEnabled="{Binding InputPathBrowseButtonEnabled}" Style="{DynamicResource CustomButtonStyle}"/>
<Label x:Name="LogPathLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Content="*.getkey.log File Path"/>
<TextBox x:Name="LogPathTextBox" Grid.Row="1" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
Text="{Binding LogPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding LogPathTextBoxEnabled}" />
<Button x:Name="LogPathBrowseButton" Grid.Row="1" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
IsEnabled="{Binding LogPathBrowseButtonEnabled}" Style="{DynamicResource CustomButtonStyle}"/>
</Grid>
</GroupBox>
<Expander Name="KeyExpander" BorderThickness="1" BorderBrush="#D5DFE5" Margin="5,5,5,5" HorizontalAlignment="Stretch"
IsEnabled="{Binding LogPathNotProvided}" IsExpanded="False" Header="Manually define PS3 Disc Encryption Key">
<Grid Margin="5,5,5,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2.5*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label x:Name="KeyLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="Hexadecimal Key"/>
<TextBox x:Name="KeyTextBox" Grid.Row="0" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
Text="{Binding HexKey, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding HexKeyTextBoxEnabled}" />
<Label x:Name="KeyPathLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center"
ToolTip="Typically a *.key file" Content="Key File Path"/>
<TextBox x:Name="KeyPathTextBox" Grid.Row="1" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
Text="{Binding KeyPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding KeyPathTextBoxEnabled}" />
<Button x:Name="KeyPathBrowseButton" Grid.Row="1" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
IsEnabled="{Binding KeyPathBrowseButtonEnabled}" Style="{DynamicResource CustomButtonStyle}"/>
<Label x:Name="KeyStatusLabel" Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Content="Key Status"/>
<Label x:Name="KeyStatusText" Grid.Row="2" Grid.Column="1" VerticalAlignment="Center" Content="{Binding KeyStatus}"/>
</Grid>
</Expander>
<Expander Name="DiscIDExpander" BorderThickness="1" BorderBrush="#D5DFE5" Margin="5,5,5,5" HorizontalAlignment="Stretch"
IsEnabled="{Binding LogPathNotProvided}" IsExpanded="False" Header="Manually define PS3 Disc ID">
<Grid Margin="5,5,5,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2.5*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label x:Name="DiscIDLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="Disc ID"/>
<TextBox x:Name="DiscIDTextBox" Grid.Row="0" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
Text="{Binding DiscIDString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding DiscIDTextBoxEnabled}" />
<Label x:Name="DiscIDStatusLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Content="DiscID Status"/>
<Label x:Name="DiscIDStatusText" Grid.Row="1" Grid.Column="1" VerticalAlignment="Center" Content="{Binding DiscIDStatus}"/>
</Grid>
</Expander>
<Expander Name="PICExpander" BorderThickness="1" BorderBrush="#D5DFE5" Margin="5,5,5,5" HorizontalAlignment="Stretch"
IsEnabled="{Binding LogPathNotProvided}" IsExpanded="False" Header="Manually define Permanent Information &#x26; Control (PIC)">
<Grid Margin="5,5,5,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2.5*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label x:Name="LayerbreakLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="Layerbreak (sectors)"/>
<TextBox x:Name="LayerbreakTextBox" Grid.Row="0" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
Text="{Binding LayerbreakString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding LayerbreakTextBoxEnabled}" />
<Label x:Name="PICLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Content="PIC"/>
<TextBox x:Name="PICTextBox" Grid.Row="1" Grid.Column="1" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Top"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto"
MinHeight="55" MaxHeight="146" AcceptsReturn="True"
Text="{Binding PICString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding PICTextBoxEnabled}" />
<Label x:Name="PICPathLabel" Grid.Row="2" Grid.Column="0" VerticalAlignment="Center"
ToolTip="Typically a *.physical or *_PIC.bin file" Content="PIC File Path"/>
<TextBox x:Name="PICPathTextBox" Grid.Row="2" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
Text="{Binding PICPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding PICPathTextBoxEnabled}" />
<Button x:Name="PICPathBrowseButton" Grid.Row="2" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
IsEnabled="{Binding PICPathBrowseButtonEnabled}" Style="{DynamicResource CustomButtonStyle}"/>
<Label x:Name="PICStatusLabel" Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Content="PIC Status"/>
<Label x:Name="PICStatusText" Grid.Row="3" Grid.Column="1" VerticalAlignment="Center" Content="{Binding PICStatus}"/>
</Grid>
</Expander>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" Header="Status">
<UniformGrid Margin="5,5,5,5" Grid.ColumnSpan="2">
<TextBlock x:Name="StatusLabel" VerticalAlignment="Center" HorizontalAlignment="Center"
Text="{Binding CreateIRDStatus, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</UniformGrid>
</GroupBox>
<!-- Create IRD / Cancel -->
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<UniformGrid Columns="4" Margin="5,5,5,5" Height="28">
<Label/>
<!-- Empty label for padding -->
<Button Name="CreateIRDButton" Height="25" Width="80" IsDefault="True" Content="Create IRD"
IsEnabled="{Binding CreateIRDButtonEnabled}" Style="{DynamicResource CustomButtonStyle}" />
<Button Name="CancelButton" Height="25" Width="80" IsCancel="True" Content="Cancel"
IsEnabled="{Binding CancelButtonEnabled}" Style="{DynamicResource CustomButtonStyle}" />
<Label/>
<!-- Empty label for padding -->
</UniformGrid>
</GroupBox>
</StackPanel>
</Grid>
</coreWindows:WindowBase>

View File

@@ -0,0 +1,489 @@
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using MPF.Core.UI.ViewModels;
using WPFCustomMessageBox;
using WinForms = System.Windows.Forms;
#pragma warning disable IDE1006 // Naming Styles
namespace MPF.UI.Core.Windows
{
/// <summary>
/// Interaction logic for CreateIRDWindow.xaml
/// </summary>
public partial class CreateIRDWindow : WindowBase
{
/// <summary>
/// Read-only access to the current check dump view model
/// </summary>
public CreateIRDViewModel CreateIRDViewModel => DataContext as CreateIRDViewModel ?? new CreateIRDViewModel();
#if NET35
#region Settings
private Button? _InputPathBrowseButton => ItemHelper.FindChild<Button>(this, "InputPathBrowseButton");
private TextBox? _InputPathTextBox => ItemHelper.FindChild<TextBox>(this, "InputPathTextBox");
private Button? _LogPathBrowseButton => ItemHelper.FindChild<Button>(this, "LogPathBrowseButton");
private TextBox? _LogPathTextBox => ItemHelper.FindChild<TextBox>(this, "LogPathTextBox");
private Button? _KeyPathBrowseButton => ItemHelper.FindChild<Button>(this, "KeyPathBrowseButton");
private TextBox? _KeyPathTextBox => ItemHelper.FindChild<TextBox>(this, "KeyPathTextBox");
private TextBox? _KeyTextBox => ItemHelper.FindChild<TextBox>(this, "KeyTextBox");
private TextBox? _DiscIDTextBox => ItemHelper.FindChild<TextBox>(this, "DiscIDTextBox");
private Button? _PICPathBrowseButton => ItemHelper.FindChild<Button>(this, "PICPathBrowseButton");
private TextBox? _PICPathTextBox => ItemHelper.FindChild<TextBox>(this, "PICPathTextBox");
private TextBox? _PICTextBox => ItemHelper.FindChild<TextBox>(this, "PICTextBox");
private TextBox? _LayerbreakTextBox => ItemHelper.FindChild<TextBox>(this, "LayerbreakTextBox");
#endregion
#region Controls
private System.Windows.Controls.Button? _CreateIRDButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "CreateIRDButton");
private System.Windows.Controls.Button? _CancelButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "CancelButton");
#endregion
#region Expanders
private Expander? _KeyExpander => ItemHelper.FindChild<Expander>(this, "KeyExpander");
private Expander? _DiscIDExpander => ItemHelper.FindChild<Expander>(this, "DiscIDExpander");
private Expander? _PICExpander => ItemHelper.FindChild<Expander>(this, "PICExpander");
#endregion
#endif
/// <summary>
/// Constructor
/// </summary>
public CreateIRDWindow(MainWindow parent)
{
#if NET40_OR_GREATER || NETCOREAPP
InitializeComponent();
#endif
#if NET452_OR_GREATER || NETCOREAPP
var chrome = new System.Windows.Shell.WindowChrome
{
CaptionHeight = 0,
ResizeBorderThickness = new Thickness(0),
};
System.Windows.Shell.WindowChrome.SetWindowChrome(this, chrome);
#endif
}
/// <summary>
/// Handler for CheckDumpWindow OnContentRendered event
/// </summary>
protected override void OnContentRendered(EventArgs e)
{
base.OnContentRendered(e);
// Add the click handlers to the UI
AddEventHandlers();
}
#region UI Functionality
/// <summary>
/// Add all event handlers
/// </summary>
public void AddEventHandlers()
{
// Main buttons
#if NET35
_CreateIRDButton!.Click += OnCreateIRDClick;
_CancelButton!.Click += OnCancelClick;
#else
CreateIRDButton.Click += OnCreateIRDClick;
CancelButton.Click += OnCancelClick;
#endif
// User Area Click
#if NET35
_InputPathBrowseButton!.Click += InputPathBrowseButtonClick;
_LogPathBrowseButton!.Click += LogPathBrowseButtonClick;
_KeyPathBrowseButton!.Click += KeyPathBrowseButtonClick;
_PICPathBrowseButton!.Click += PICPathBrowseButtonClick;
#else
InputPathBrowseButton.Click += InputPathBrowseButtonClick;
LogPathBrowseButton.Click += LogPathBrowseButtonClick;
KeyPathBrowseButton.Click += KeyPathBrowseButtonClick;
PICPathBrowseButton.Click += PICPathBrowseButtonClick;
#endif
// User Area TextChanged
#if NET35
_InputPathTextBox!.TextChanged += InputPathTextBoxTextChanged;
_LogPathTextBox!.TextChanged += LogPathTextBoxTextChanged;
_KeyPathTextBox!.TextChanged += KeyPathTextBoxTextChanged;
_KeyTextBox!.TextChanged += KeyTextBoxTextChanged;
_DiscIDTextBox!.TextChanged += DiscIDTextBoxTextChanged;
_PICPathTextBox!.TextChanged += PICPathTextBoxTextChanged;
_PICTextBox!.TextChanged += PICTextBoxTextChanged;
_LayerbreakTextBox!.TextChanged += LayerbreakTextBoxTextChanged;
#else
InputPathTextBox.TextChanged += InputPathTextBoxTextChanged;
LogPathTextBox.TextChanged += LogPathTextBoxTextChanged;
KeyPathTextBox.TextChanged += KeyPathTextBoxTextChanged;
KeyTextBox.TextChanged += KeyTextBoxTextChanged;
DiscIDTextBox.TextChanged += DiscIDTextBoxTextChanged;
PICPathTextBox.TextChanged += PICPathTextBoxTextChanged;
PICTextBox.TextChanged += PICTextBoxTextChanged;
LayerbreakTextBox.TextChanged += LayerbreakTextBoxTextChanged;
#endif
}
/// <summary>
/// Browse for an input ISO file path
/// </summary>
public void BrowseISOFile()
{
// Get the current path, if possible
string? currentPath = CreateIRDViewModel.InputPath;
if (string.IsNullOrEmpty(currentPath) && !string.IsNullOrEmpty(CreateIRDViewModel.Options.DefaultOutputPath))
currentPath = CreateIRDViewModel.Options.DefaultOutputPath!;
if (string.IsNullOrEmpty(currentPath))
currentPath = AppDomain.CurrentDomain.BaseDirectory!;
// Get the full directory
var directory = Path.GetDirectoryName(Path.GetFullPath(currentPath));
WinForms.FileDialog fileDialog = new WinForms.OpenFileDialog
{
InitialDirectory = directory,
Filter = "ISO|*.iso|All Files|*.*",
};
WinForms.DialogResult result = fileDialog.ShowDialog();
if (result == WinForms.DialogResult.OK)
{
CreateIRDViewModel.InputPath = fileDialog.FileName;
}
}
/// <summary>
/// Browse for an .getkey.log file path
/// </summary>
public void BrowseLogFile()
{
// Get the current path, if possible
string? currentPath = CreateIRDViewModel.LogPath;
if (string.IsNullOrEmpty(currentPath) && !string.IsNullOrEmpty(CreateIRDViewModel.Options.DefaultOutputPath))
currentPath = CreateIRDViewModel.Options.DefaultOutputPath!;
if (string.IsNullOrEmpty(currentPath))
currentPath = AppDomain.CurrentDomain.BaseDirectory!;
// Get the full directory
var directory = Path.GetDirectoryName(Path.GetFullPath(currentPath));
WinForms.FileDialog fileDialog = new WinForms.OpenFileDialog
{
InitialDirectory = directory,
Filter = "GetKey Log|*.getkey.log|All Files|*.*",
};
WinForms.DialogResult result = fileDialog.ShowDialog();
if (result == WinForms.DialogResult.OK)
{
CreateIRDViewModel.LogPath = fileDialog.FileName;
}
}
/// <summary>
/// Browse for an key file path
/// </summary>
public void BrowseKeyFile()
{
// Get the current path, if possible
string? currentPath = CreateIRDViewModel.LogPath;
if (string.IsNullOrEmpty(currentPath) && !string.IsNullOrEmpty(CreateIRDViewModel.Options.DefaultOutputPath))
currentPath = CreateIRDViewModel.Options.DefaultOutputPath!;
if (string.IsNullOrEmpty(currentPath))
currentPath = AppDomain.CurrentDomain.BaseDirectory!;
// Get the full directory
var directory = Path.GetDirectoryName(Path.GetFullPath(currentPath));
WinForms.FileDialog fileDialog = new WinForms.OpenFileDialog
{
InitialDirectory = directory,
Filter = "Key|*.key|All Files|*.*",
};
WinForms.DialogResult result = fileDialog.ShowDialog();
if (result == WinForms.DialogResult.OK)
{
CreateIRDViewModel.KeyPath = fileDialog.FileName;
}
}
/// <summary>
/// Browse for an IRD output path
/// </summary>
/// <returns>Output path if provided, else null</returns>
public string? BrowseOutputFile()
{
// Get the current path, if possible
string? currentPath = CreateIRDViewModel.InputPath;
if (string.IsNullOrEmpty(currentPath) && !string.IsNullOrEmpty(CreateIRDViewModel.Options.DefaultOutputPath))
currentPath = Path.Combine(CreateIRDViewModel.Options.DefaultOutputPath, "game.ird");
else if (string.IsNullOrEmpty(currentPath))
currentPath = "game.ird";
if (string.IsNullOrEmpty(currentPath))
currentPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory!, "game.ird");
// Get the full path
currentPath = Path.GetFullPath(currentPath);
// Get the directory
var directory = Path.GetDirectoryName(currentPath);
// Get the filename
string filename = Path.ChangeExtension(Path.GetFileName(currentPath), ".ird");
WinForms.FileDialog fileDialog = new WinForms.SaveFileDialog
{
FileName = filename,
InitialDirectory = directory,
Filter = "IRD File|*.ird|All Files|*.*"
};
WinForms.DialogResult result = fileDialog.ShowDialog();
if (result == WinForms.DialogResult.OK)
return fileDialog.FileName;
else
return null;
}
/// <summary>
/// Browse for an PIC file path
/// </summary>
public void BrowsePICFile()
{
// Get the current path, if possible
string? currentPath = CreateIRDViewModel.LogPath;
if (string.IsNullOrEmpty(currentPath) && !string.IsNullOrEmpty(CreateIRDViewModel.Options.DefaultOutputPath))
currentPath = CreateIRDViewModel.Options.DefaultOutputPath!;
if (string.IsNullOrEmpty(currentPath))
currentPath = AppDomain.CurrentDomain.BaseDirectory!;
// Get the full directory
var directory = Path.GetDirectoryName(Path.GetFullPath(currentPath));
WinForms.FileDialog fileDialog = new WinForms.OpenFileDialog
{
InitialDirectory = directory,
Filter = "PIC|*.physical;*_PIC.bin;*.PIC|All Files|*.*",
};
WinForms.DialogResult result = fileDialog.ShowDialog();
if (result == WinForms.DialogResult.OK)
{
CreateIRDViewModel.PICPath = fileDialog.FileName;
}
}
/// <summary>
/// Display a user message using a CustomMessageBox
/// </summary>
/// <param name="title">Title to display to the user</param>
/// <param name="message">Message to display to the user</param>
/// <param name="optionCount">Number of options to display</param>
/// <param name="flag">true for inquiry, false otherwise</param>
/// <returns>true for positive, false for negative, null for neutral</returns>
public bool? DisplayUserMessage(string title, string message, int optionCount, bool flag)
{
// Set the correct button style
var button = optionCount switch
{
1 => MessageBoxButton.OK,
2 => MessageBoxButton.YesNo,
3 => MessageBoxButton.YesNoCancel,
// This should not happen, but default to "OK"
_ => MessageBoxButton.OK,
};
// Set the correct icon
MessageBoxImage image = flag ? MessageBoxImage.Question : MessageBoxImage.Exclamation;
// Display and get the result
MessageBoxResult result = CustomMessageBox.Show(this, message, title, button, image);
return result switch
{
MessageBoxResult.OK or MessageBoxResult.Yes => true,
MessageBoxResult.No => false,
_ => null,
};
}
#endregion
#region Event Handlers
/// <summary>
/// Handler for CreateIRDButton Click event
/// </summary>
private void OnCreateIRDClick(object sender, EventArgs e)
{
#if NET35
if (_KeyExpander != null) _KeyExpander.IsExpanded = false;
if (_DiscIDExpander != null) _DiscIDExpander.IsExpanded = false;
if (_PICExpander != null) _PICExpander.IsExpanded = false;
#else
KeyExpander.IsExpanded = false;
DiscIDExpander.IsExpanded = false;
PICExpander.IsExpanded = false;
#endif
string tempStatus = CreateIRDViewModel.CreateIRDStatus;
bool[] enabledFields = CreateIRDViewModel.DisableUIFields();
CreateIRDViewModel.CreateIRDStatus = "Creating IRD... Please Wait";
string? outputPath = BrowseOutputFile();
string? errorMessage = "Please provide an output path";
if (outputPath != null)
{
errorMessage = CreateIRDViewModel.CreateIRD(outputPath);
}
if (string.IsNullOrEmpty(errorMessage))
{
bool? checkAgain = DisplayUserMessage("IRD Create", "An IRD has been created successfully! Would you like to create another IRD?", 2, false);
if (checkAgain == false)
Close();
else
CreateIRDViewModel.ResetFields();
}
else
{
DisplayUserMessage("Failed to create IRD", errorMessage!, 1, false);
CreateIRDViewModel.ReenableUIFields(enabledFields);
CreateIRDViewModel.CreateIRDStatus = tempStatus;
}
}
/// <summary>
/// Handler for CancelButtom Click event
/// </summary>
private void OnCancelClick(object sender, EventArgs e)
{
Close();
}
/// <summary>
/// Handler for DiscIDTextBox TextChanged event
/// </summary>
public void DiscIDTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangeDiscID();
}
/// <summary>
/// Handler for InputPathBrowseButton Click event
/// </summary>
public void InputPathBrowseButtonClick(object sender, RoutedEventArgs e)
{
BrowseISOFile();
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangeInputPath();
}
/// <summary>
/// Handler for InputPathTextBox TextChanged event
/// </summary>
public void InputPathTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangeInputPath();
}
/// <summary>
/// Handler for LogPathBrowseButton Click event
/// </summary>
public void LogPathBrowseButtonClick(object sender, RoutedEventArgs e)
{
BrowseLogFile();
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangeLogPath();
}
/// <summary>
/// Handler for LogPathTextBox TextChanged event
/// </summary>
public void LogPathTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangeLogPath();
}
/// <summary>
/// Handler for KeyPathBrowseButton Click event
/// </summary>
public void KeyPathBrowseButtonClick(object sender, RoutedEventArgs e)
{
BrowseKeyFile();
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangeKeyPath();
}
/// <summary>
/// Handler for KeyPathTextBox TextChanged event
/// </summary>
public void KeyPathTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangeKeyPath();
}
/// <summary>
/// Handler for KeyTextBox TextChanged event
/// </summary>
public void KeyTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangeKey();
}
/// <summary>
/// Handler for PICPathBrowseButton Click event
/// </summary>
public void PICPathBrowseButtonClick(object sender, RoutedEventArgs e)
{
BrowsePICFile();
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangePICPath();
}
/// <summary>
/// Handler for PICPathTextBox TextChanged event
/// </summary>
public void PICPathTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangePICPath();
}
/// <summary>
/// Handler for PICTextBox TextChanged event
/// </summary>
public void PICTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangePIC();
}
/// <summary>
/// Handler for LayerbreakTextBox TextChanged event
/// </summary>
public void LayerbreakTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangeLayerbreak();
}
#endregion
}
}

View File

@@ -54,6 +54,15 @@
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}"
IsEnabled="{Binding CheckDumpMenuItemEnabled}"/>
<MenuItem x:Name="CreateIRDMenuItem" Header="_Create PS3 IRD" HorizontalAlignment="Left" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}"
ToolTip="IRD Creation is only available on .NET Core 6 or newer."
ToolTipService.InitialShowDelay="0"
ToolTipService.IsEnabled="{Binding CannotCreateIRD}"
ToolTipService.ShowOnDisabled="True"
IsEnabled="{Binding CreateIRDMenuItemEnabled}"/>
<MenuItem x:Name="OptionsMenuItem" Header="_Options" HorizontalAlignment="Left" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"

View File

@@ -29,6 +29,7 @@ namespace MPF.UI.Core.Windows
private MenuItem? _CheckForUpdatesMenuItem => ItemHelper.FindChild<MenuItem>(this, "CheckForUpdatesMenuItem");
private MenuItem? _DebugViewMenuItem => ItemHelper.FindChild<MenuItem>(this, "DebugViewMenuItem");
private MenuItem? _CheckDumpMenuItem => ItemHelper.FindChild<MenuItem>(this, "CheckDumpMenuItem");
private MenuItem? _CreateIRDMenuItem => ItemHelper.FindChild<MenuItem>(this, "CreateIRDMenuItem");
private MenuItem? _OptionsMenuItem => ItemHelper.FindChild<MenuItem>(this, "OptionsMenuItem");
#endregion
@@ -141,6 +142,7 @@ namespace MPF.UI.Core.Windows
_CheckForUpdatesMenuItem!.Click += CheckForUpdatesClick;
_DebugViewMenuItem!.Click += DebugViewClick;
_CheckDumpMenuItem!.Click += CheckDumpMenuItemClick;
_CreateIRDMenuItem!.Click += CreateIRDMenuItemClick;
_OptionsMenuItem!.Click += OptionsMenuItemClick;
#else
AboutMenuItem.Click += AboutClick;
@@ -148,6 +150,7 @@ namespace MPF.UI.Core.Windows
CheckForUpdatesMenuItem.Click += CheckForUpdatesClick;
DebugViewMenuItem.Click += DebugViewClick;
CheckDumpMenuItem.Click += CheckDumpMenuItemClick;
CreateIRDMenuItem.Click += CreateIRDMenuItemClick;
OptionsMenuItem.Click += OptionsMenuItemClick;
#endif
@@ -341,6 +344,31 @@ namespace MPF.UI.Core.Windows
checkDumpWindow.Show();
}
/// <summary>
/// Show the Create IRD window
/// </summary>
public void ShowCreateIRDWindow()
{
// Hide MainWindow while Create IRD UI is open
this.Hide();
var createIRDWindow = new CreateIRDWindow(this)
{
Focusable = true,
Owner = this,
ShowActivated = true,
ShowInTaskbar = true,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
};
createIRDWindow.Closed += delegate {
// Unhide Main window after Create IRD window has been closed
this.Show();
this.Activate();
};
createIRDWindow.Show();
}
/// <summary>
/// Show the Options window
/// </summary>
@@ -420,6 +448,12 @@ namespace MPF.UI.Core.Windows
public void CheckDumpMenuItemClick(object sender, RoutedEventArgs e) =>
ShowCheckDumpWindow();
/// <summary>
/// Handler for CreateIRDMenuItem Click event
/// </summary>
public void CreateIRDMenuItemClick(object sender, RoutedEventArgs e) =>
ShowCreateIRDWindow();
/// <summary>
/// Handler for CheckForUpdatesMenuItem Click event
/// </summary>

View File

@@ -191,7 +191,7 @@
<TabItem Header="Dumping" Style="{DynamicResource CustomTabItemStyle}">
<StackPanel>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Dumping">
<UniformGrid Columns="2" Rows="7">
<UniformGrid Columns="2" Rows="8">
<CheckBox VerticalAlignment="Center" Content="Show Separate Window"
IsChecked="{Binding Options.ToolsInSeparateWindow}"
ToolTip="Show program output in separate command window instead of in the log. Enable this if you have a weaker system as there is an increased processing load otherwise." Margin="0,4"
@@ -261,6 +261,11 @@
IsChecked="{Binding Options.DeleteUnnecessaryFiles}"
ToolTip="Delete unnecesary output files to reduce space" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Create PS3 IRD After Dumping"
IsChecked="{Binding Options.CreateIRDAfterDumping}" IsEnabled="{Binding CreateIRDSupported}"
ToolTip="Automatically creates an IRD file after dumping a PS3 disc" Margin="0,4"
/>
</UniformGrid>
</GroupBox>

18
MPF.sln
View File

@@ -13,9 +13,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
ProjectSection(SolutionItems) = preProject
appveyor.yml = appveyor.yml
CHANGELIST.md = CHANGELIST.md
.github\ISSUE_TEMPLATE\feature-request.md = .github\ISSUE_TEMPLATE\feature-request.md
.github\ISSUE_TEMPLATE\informational.md = .github\ISSUE_TEMPLATE\informational.md
.github\ISSUE_TEMPLATE\issue-report.md = .github\ISSUE_TEMPLATE\issue-report.md
publish-nix.sh = publish-nix.sh
publish-win.ps1 = publish-win.ps1
README.md = README.md
@@ -27,6 +24,19 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF.UI.Core", "MPF.UI.Core\
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MPF", "MPF", "{4160167D-681D-480B-ABC6-06AC869E5769}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ISSUE_TEMPLATE", "ISSUE_TEMPLATE", "{ADC5FDD7-F43F-4F9C-B222-19AA1D64D3D4}"
ProjectSection(SolutionItems) = preProject
.github\ISSUE_TEMPLATE\feature-request.md = .github\ISSUE_TEMPLATE\feature-request.md
.github\ISSUE_TEMPLATE\informational.md = .github\ISSUE_TEMPLATE\informational.md
.github\ISSUE_TEMPLATE\issue-report.md = .github\ISSUE_TEMPLATE\issue-report.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{440776E0-E6E2-46C6-8E85-09E64A8FC7D6}"
ProjectSection(SolutionItems) = preProject
.github\workflows\build_check.yml = .github\workflows\build_check.yml
.github\workflows\build_ui.yml = .github\workflows\build_ui.yml
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -63,6 +73,8 @@ Global
{8CFDE289-E171-4D49-A40D-5293265C1253} = {4160167D-681D-480B-ABC6-06AC869E5769}
{70B1265D-FE49-472A-A83D-0B462152D37A} = {4160167D-681D-480B-ABC6-06AC869E5769}
{EA3768DB-694A-4653-82E4-9FF71B8963F3} = {4160167D-681D-480B-ABC6-06AC869E5769}
{ADC5FDD7-F43F-4F9C-B222-19AA1D64D3D4} = {4D1DCF5A-F0B0-4E81-A05B-F1A7D37C9D9D}
{440776E0-E6E2-46C6-8E85-09E64A8FC7D6} = {4D1DCF5A-F0B0-4E81-A05B-F1A7D37C9D9D}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {73C62E6A-6584-4D93-83B5-ECB1FBDB469B}

View File

@@ -2,18 +2,19 @@
<PropertyGroup>
<!-- Assembly Properties -->
<TargetFrameworks>net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0-windows;net6.0-windows;net7.0-windows;net8.0-windows</TargetFrameworks>
<TargetFrameworks>net40;net452;net462;net472;net48;netcoreapp3.1;net5.0-windows;net6.0-windows;net7.0-windows;net8.0-windows</TargetFrameworks>
<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
<OutputType>WinExe</OutputType>
<ApplicationIcon>Images\Icon.ico</ApplicationIcon>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<ImportFrameworkWinFXTargets Condition="$(TargetFramework.StartsWith(`net3`))">true</ImportFrameworkWinFXTargets>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<VersionPrefix>3.1.0</VersionPrefix>
<VersionPrefix>3.1.1</VersionPrefix>
<UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF>

View File

@@ -3,6 +3,8 @@
Redumper/Aaru/DiscImageCreator UI in C#
[![Build status](https://ci.appveyor.com/api/projects/status/3ldav3v0c373jeqa?svg=true)](https://ci.appveyor.com/project/mnadareski/MPF/build/artifacts)
[![UI Build](https://github.com/SabreTools/MPF/actions/workflows/build_ui.yml/badge.svg)](https://github.com/SabreTools/MPF/actions/workflows/build_ui.yml)
[![Check Build](https://github.com/SabreTools/MPF/actions/workflows/build_check.yml/badge.svg)](https://github.com/SabreTools/MPF/actions/workflows/build_check.yml)
This is a community project, so if you have some time and knowledge to give, we'll be glad to add you as a contributor to this project. If you have any suggestions, issues, bugs, or crashes, please look at the [Issues](https://github.com/SabreTools/MPF/issues) page first to see if it has been reported before and try out the latest AppVeyor WIP build below to see if it has already been addressed. If it hasn't, please open an issue that's as descriptive as you can be. Help me make this a better program for everyone :)
@@ -10,7 +12,7 @@ This is a community project, so if you have some time and knowledge to give, we'
For the most recent stable build, download the latest release here: [Releases Page](https://github.com/SabreTools/MPF/releases)
For the latest AppVeyor WIP build here: [AppVeyor](https://ci.appveyor.com/project/mnadareski/MPF/build/artifacts)
For the latest GitHub Actions WIP build here: [GitHub Actions](https://github.com/SabreTools/MPF/actions)
## Media Preservation Frontend UI (MPF)
@@ -33,8 +35,6 @@ MPF.Check is a commandline-only program that allows users to generate submission
Both MPF UI and MPF.Check have the same system requirements for running, with the exception that MPF UI is Windows-only.
- [Supported OS versions for .NET 6](https://github.com/dotnet/core/blob/main/release-notes/6.0/supported-os.md)
- Requires [.NET 6.0 Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) if built without bundled runtime
- [Supported OS versions for .NET 8](https://github.com/dotnet/core/blob/main/release-notes/8.0/supported-os.md)
- Requires [.NET 8.0 Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) if built without bundled runtime
@@ -42,18 +42,18 @@ Ensure that your operating system and runtimes are as up-to-date as possible, si
## Build Instructions
To build for .NET 6.0 or .NET 8.0, ensure that the [.NET 8.0 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) (or later) is installed and included in your `PATH`. Then, run the following commands from command prompt, Powershell, Terminal, or shell:
To build for .NET 8.0, ensure that the [.NET 8.0 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) (or later) is installed and included in your `PATH`. Then, run the following commands from command prompt, Powershell, Terminal, or shell:
**MPF UI (Windows only):**
```bash
dotnet build MPF/MPF.csproj --framework [net6.0-windows|net8.0-windows] --runtime win-x64
dotnet build MPF/MPF.csproj --framework net8.0-windows --runtime [win-x86|win-x64]
```
**MPF.Check (Windows, OSX, Linux):**
```bash
dotnet build MPF.Check/MPF.Check.csproj --framework [net6.0|net8.0] --runtime [win-x64|linux-x64|osx-x64]
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.

View File

@@ -1,5 +1,5 @@
# version format
version: 3.1.0-{build}
version: 3.1.1-{build}
# pull request template
pull_requests:
@@ -10,75 +10,15 @@ image: Visual Studio 2022
# build step
build_script:
- dotnet restore
# .NET 6.0 Debug
- dotnet publish MPF\MPF.csproj -f net6.0-windows -r win-x64 -c Debug --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true
- dotnet publish MPF.Check\MPF.Check.csproj -f net6.0 -r win-x64 -c Debug --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true
- dotnet publish MPF.Check\MPF.Check.csproj -f net6.0 -r linux-x64 -c Debug --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true
- dotnet publish MPF.Check\MPF.Check.csproj -f net6.0 -r osx-x64 -c Debug --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true
# .NET 6.0 Release
- dotnet publish MPF\MPF.csproj -f net6.0-windows -r win-x64 -c Release --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
- dotnet publish MPF.Check\MPF.Check.csproj -f net6.0 -r win-x64 -c Release --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
- dotnet publish MPF.Check\MPF.Check.csproj -f net6.0 -r linux-x64 -c Release --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
- dotnet publish MPF.Check\MPF.Check.csproj -f net6.0 -r osx-x64 -c Release --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
# .NET 8.0 Debug
- dotnet publish MPF\MPF.csproj -f net8.0-windows -r win-x64 -c Debug --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true
- dotnet publish MPF.Check\MPF.Check.csproj -f net8.0 -r win-x64 -c Debug --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true
- dotnet publish MPF.Check\MPF.Check.csproj -f net8.0 -r linux-x64 -c Debug --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true
- dotnet publish MPF.Check\MPF.Check.csproj -f net8.0 -r osx-x64 -c Debug --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true
# .NET 8.0 Release
- dotnet publish MPF\MPF.csproj -f net8.0-windows -r win-x64 -c Release --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
- dotnet publish MPF.Check\MPF.Check.csproj -f net8.0 -r win-x64 -c Release --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
- dotnet publish MPF.Check\MPF.Check.csproj -f net8.0 -r linux-x64 -c Release --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
- dotnet publish MPF.Check\MPF.Check.csproj -f net8.0 -r osx-x64 -c Release --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
- dotnet build
# post-build step
after_build:
# Aaru
- ps: appveyor DownloadFile https://github.com/aaru-dps/Aaru/releases/download/v5.3.2/aaru-5.3.2_windows_x64.zip
- 7z x aaru-5.3.2_windows_x64.zip -oMPF\bin\Debug\net8.0-windows\win-x64\publish\Programs\Aaru *
# DiscImageCreator
- ps: appveyor DownloadFile https://github.com/saramibreak/DiscImageCreator/files/11660558/DiscImageCreator_20230606.zip
- 7z e DiscImageCreator_20230606.zip -oMPF\bin\Debug\net8.0-windows\win-x64\publish\Programs\Creator Release_ANSI\*
# Redumper
- ps: appveyor DownloadFile https://github.com/superg/redumper/releases/download/build_311/redumper-2024.01.08_build311-win64.zip
- 7z e redumper-2024.01.08_build311-win64.zip -oMPF\bin\Debug\net8.0-windows\win-x64\publish\Programs\Redumper redumper-2024.01.08_build311-win64\bin\*
# Create MPF Debug archives
- cd %APPVEYOR_BUILD_FOLDER%\MPF\bin\Debug\net8.0-windows\win-x64\publish\
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF_%APPVEYOR_BUILD_NUMBER%_net8.0_win-x64_debug.zip *
# Create MPF.Check Debug archives
- cd %APPVEYOR_BUILD_FOLDER%\MPF.Check\bin\Debug\net8.0\win-x64\publish\
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF.Check_%APPVEYOR_BUILD_NUMBER%_net8.0_win-x64_debug.zip *
- cd %APPVEYOR_BUILD_FOLDER%\MPF.Check\bin\Debug\net8.0\linux-x64\publish\
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF.Check_%APPVEYOR_BUILD_NUMBER%_net8.0_linux-x64_debug.zip *
- cd %APPVEYOR_BUILD_FOLDER%\MPF.Check\bin\Debug\net8.0\osx-x64\publish\
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF.Check_%APPVEYOR_BUILD_NUMBER%_net8.0_osx-x64_debug.zip *
# success/failure tracking
on_success:
- ps: Invoke-RestMethod https://raw.githubusercontent.com/DiscordHooks/appveyor-discord-webhook/master/send.ps1 -o send.ps1
- ps: ./send.ps1 success $env:WEBHOOK_URL
on_failure:
- ps: Invoke-RestMethod https://raw.githubusercontent.com/DiscordHooks/appveyor-discord-webhook/master/send.ps1 -o send.ps1
- ps: ./send.ps1 failure $env:WEBHOOK_URL
# artifact linking
artifacts:
- path: MPF_%APPVEYOR_BUILD_NUMBER%_net8.0_win-x64_debug.zip
name: MPF (.NET 8.0, Debug, Windows x64)
- path: MPF.Check_%APPVEYOR_BUILD_NUMBER%_net8.0_win-x64_debug.zip
name: MPF.Check (.NET 8.0, Debug, Windows x64)
- path: MPF.Check_%APPVEYOR_BUILD_NUMBER%_net8.0_linux-x64_debug.zip
name: MPF.Check (.NET 8.0, Debug, Linux x64)
- path: MPF.Check_%APPVEYOR_BUILD_NUMBER%_net8.0_osx-x64_debug.zip
name: MPF.Check (.NET 8.0, Debug, OSX x64)
- ps: ./send.ps1 failure $env:WEBHOOK_URL

View File

@@ -12,7 +12,7 @@
# Optional parameters
USE_ALL=false
INCLUDE_PROGRAMS=false # TODO: Figure out how to exclude folders with `zip`
INCLUDE_PROGRAMS=false
NO_BUILD=false
NO_ARCHIVE=false
while getopts "upba" OPTION
@@ -44,10 +44,10 @@ BUILD_FOLDER=$PWD
COMMIT=`git log --pretty=%H -1`
# Create the build matrix arrays
UI_FRAMEWORKS=("net6.0-windows" "net8.0-windows")
UI_RUNTIMES=("win-x64")
CHECK_FRAMEWORKS=("net6.0" "net8.0")
CHECK_RUNTIMES=("win-x64" "linux-x64" "osx-x64")
UI_FRAMEWORKS=("net8.0-windows")
UI_RUNTIMES=("win-x86" "win-x64")
CHECK_FRAMEWORKS=("net8.0")
CHECK_RUNTIMES=("win-x86" "win-x64" "linux-x64" "osx-x64")
# Use expanded lists, if requested
if [ $USE_ALL = true ]
@@ -75,6 +75,15 @@ then
do
for RUNTIME in "${UI_RUNTIMES[@]}"
do
# If we have an invalid combination of framework and runtime
if [[ ! $(echo ${VALID_CROSS_PLATFORM_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]
then
if [[ $(echo ${VALID_CROSS_PLATFORM_RUNTIMES[@]} | fgrep -w $RUNTIME) ]]
then
continue
fi
fi
# Only .NET 5 and above can publish to a single file
if [[ $(echo ${SINGLE_FILE_CAPABLE[@]} | fgrep -w $FRAMEWORK) ]]
then
@@ -120,12 +129,9 @@ then
# Create UI archives
for FRAMEWORK in "${UI_FRAMEWORKS[@]}"
do
echo "Framework: $FRAMEWORK"
for RUNTIME in "${UI_RUNTIMES[@]}"
do
echo "Runtime: $RUNTIME"
cd $BUILD_FOLDER/MPF/bin/Debug/${FRAMEWORK}/${RUNTIME}/publish/
echo "PWD: $PWD"
if [ $INCLUDE_PROGRAMS = true ]
then
zip -r $BUILD_FOLDER/MPF_${FRAMEWORK}_${RUNTIME}_debug.zip .
@@ -133,7 +139,6 @@ then
zip -r $BUILD_FOLDER/MPF_${FRAMEWORK}_${RUNTIME}_debug.zip . -x 'Programs/\*'
fi
cd $BUILD_FOLDER/MPF/bin/Release/${FRAMEWORK}/${RUNTIME}/publish/
echo "PWD: $PWD"
if [ $INCLUDE_PROGRAMS = true ]
then
zip -r $BUILD_FOLDER/MPF_${FRAMEWORK}_${RUNTIME}_release.zip .

View File

@@ -14,15 +14,15 @@ param(
[Alias("UseAll")]
[switch]$USE_ALL,
[Parameter(Mandatory = $false)]
[Parameter(Mandatory = $false)]
[Alias("IncludePrograms")]
[switch]$INCLUDE_PROGRAMS,
[Parameter(Mandatory = $false)]
[Parameter(Mandatory = $false)]
[Alias("NoBuild")]
[switch]$NO_BUILD,
[Parameter(Mandatory = $false)]
[Parameter(Mandatory = $false)]
[Alias("NoArchive")]
[switch]$NO_ARCHIVE
)
@@ -34,10 +34,10 @@ $BUILD_FOLDER = $PSScriptRoot
$COMMIT = git log --pretty=format:"%H" -1
# Create the build matrix arrays
$UI_FRAMEWORKS = @('net6.0-windows', 'net8.0-windows')
$UI_RUNTIMES = @('win-x64')
$CHECK_FRAMEWORKS = @('net6.0', 'net8.0')
$CHECK_RUNTIMES = @('win-x64', 'linux-x64', 'osx-x64')
$UI_FRAMEWORKS = @('net8.0-windows')
$UI_RUNTIMES = @('win-x86', 'win-x64')
$CHECK_FRAMEWORKS = @('net8.0')
$CHECK_RUNTIMES = @('win-x86', 'win-x64', 'linux-x64', 'osx-x64')
# Use expanded lists, if requested
if ($USE_ALL.IsPresent)
@@ -56,102 +56,108 @@ $VALID_CROSS_PLATFORM_RUNTIMES = @('win-arm64', 'linux-x64', 'linux-arm64', 'osx
# Only build if requested
if (!$NO_BUILD.IsPresent)
{
# Restore Nuget packages for all builds
Write-Host "Restoring Nuget packages"
dotnet restore
# Restore Nuget packages for all builds
Write-Host "Restoring Nuget packages"
dotnet restore
# Build UI
foreach ($FRAMEWORK in $UI_FRAMEWORKS)
{
foreach ($RUNTIME in $UI_RUNTIMES)
{
# Only .NET 5 and above can publish to a single file
if ($SINGLE_FILE_CAPABLE -contains $FRAMEWORK)
{
dotnet publish MPF\MPF.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
dotnet publish MPF\MPF.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
}
else
{
dotnet publish MPF\MPF.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT
dotnet publish MPF\MPF.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:DebugType=None -p:DebugSymbols=false
}
}
}
# Build UI
foreach ($FRAMEWORK in $UI_FRAMEWORKS)
{
foreach ($RUNTIME in $UI_RUNTIMES)
{
# If we have an invalid combination of framework and runtime
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME)
{
continue
}
# Build Check
foreach ($FRAMEWORK in $CHECK_FRAMEWORKS)
{
foreach ($RUNTIME in $CHECK_RUNTIMES)
{
# If we have an invalid combination of framework and runtime
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME)
{
continue
}
# Only .NET 5 and above can publish to a single file
if ($SINGLE_FILE_CAPABLE -contains $FRAMEWORK)
{
dotnet publish MPF\MPF.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
dotnet publish MPF\MPF.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
}
else
{
dotnet publish MPF\MPF.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT
dotnet publish MPF\MPF.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:DebugType=None -p:DebugSymbols=false
}
}
}
# Only .NET 5 and above can publish to a single file
if ($SINGLE_FILE_CAPABLE -contains $FRAMEWORK)
{
dotnet publish MPF.Check\MPF.Check.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
dotnet publish MPF.Check\MPF.Check.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
}
else
{
dotnet publish MPF.Check\MPF.Check.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT
dotnet publish MPF.Check\MPF.Check.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:DebugType=None -p:DebugSymbols=false
}
}
}
# Build Check
foreach ($FRAMEWORK in $CHECK_FRAMEWORKS)
{
foreach ($RUNTIME in $CHECK_RUNTIMES)
{
# If we have an invalid combination of framework and runtime
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME)
{
continue
}
# Only .NET 5 and above can publish to a single file
if ($SINGLE_FILE_CAPABLE -contains $FRAMEWORK)
{
dotnet publish MPF.Check\MPF.Check.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
dotnet publish MPF.Check\MPF.Check.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
}
else
{
dotnet publish MPF.Check\MPF.Check.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT
dotnet publish MPF.Check\MPF.Check.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:DebugType=None -p:DebugSymbols=false
}
}
}
}
# Only create archives if requested
if (!$NO_ARCHIVE.IsPresent)
{
# Create UI archives
foreach ($FRAMEWORK in $UI_FRAMEWORKS)
{
foreach ($RUNTIME in $UI_RUNTIMES)
{
Set-Location -Path $BUILD_FOLDER\MPF\bin\Debug\${FRAMEWORK}\${RUNTIME}\publish\
if ($INCLUDE_PROGRAMS.IsPresent)
{
7z a -tzip $BUILD_FOLDER\MPF_${FRAMEWORK}_${RUNTIME}_debug.zip *
}
else
{
7z a -tzip -x!Programs\* $BUILD_FOLDER\MPF_${FRAMEWORK}_${RUNTIME}_debug.zip *
}
Set-Location -Path $BUILD_FOLDER\MPF\bin\Release\${FRAMEWORK}\${RUNTIME}\publish\
if ($INCLUDE_PROGRAMS.IsPresent)
{
7z a -tzip $BUILD_FOLDER\MPF_${FRAMEWORK}_${RUNTIME}_release.zip *
}
else
{
7z a -tzip -x!Programs\* $BUILD_FOLDER\MPF_${FRAMEWORK}_${RUNTIME}_release.zip *
}
}
}
# Create UI archives
foreach ($FRAMEWORK in $UI_FRAMEWORKS)
{
foreach ($RUNTIME in $UI_RUNTIMES)
{
Set-Location -Path $BUILD_FOLDER\MPF\bin\Debug\${FRAMEWORK}\${RUNTIME}\publish\
if ($INCLUDE_PROGRAMS.IsPresent)
{
7z a -tzip $BUILD_FOLDER\MPF_${FRAMEWORK}_${RUNTIME}_debug.zip *
}
else
{
7z a -tzip -x!Programs\* $BUILD_FOLDER\MPF_${FRAMEWORK}_${RUNTIME}_debug.zip *
}
Set-Location -Path $BUILD_FOLDER\MPF\bin\Release\${FRAMEWORK}\${RUNTIME}\publish\
if ($INCLUDE_PROGRAMS.IsPresent)
{
7z a -tzip $BUILD_FOLDER\MPF_${FRAMEWORK}_${RUNTIME}_release.zip *
}
else
{
7z a -tzip -x!Programs\* $BUILD_FOLDER\MPF_${FRAMEWORK}_${RUNTIME}_release.zip *
}
}
}
# Create Check archives
foreach ($FRAMEWORK in $CHECK_FRAMEWORKS)
{
foreach ($RUNTIME in $CHECK_RUNTIMES)
{
# If we have an invalid combination of framework and runtime
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME)
{
continue
}
# Create Check archives
foreach ($FRAMEWORK in $CHECK_FRAMEWORKS)
{
foreach ($RUNTIME in $CHECK_RUNTIMES)
{
# If we have an invalid combination of framework and runtime
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME)
{
continue
}
Set-Location -Path $BUILD_FOLDER\MPF.Check\bin\Debug\${FRAMEWORK}\${RUNTIME}\publish\
7z a -tzip $BUILD_FOLDER\MPF.Check_${FRAMEWORK}_${RUNTIME}_debug.zip *
Set-Location -Path $BUILD_FOLDER\MPF.Check\bin\Release\${FRAMEWORK}\${RUNTIME}\publish\
7z a -tzip $BUILD_FOLDER\MPF.Check_${FRAMEWORK}_${RUNTIME}_release.zip *
}
}
Set-Location -Path $BUILD_FOLDER\MPF.Check\bin\Debug\${FRAMEWORK}\${RUNTIME}\publish\
7z a -tzip $BUILD_FOLDER\MPF.Check_${FRAMEWORK}_${RUNTIME}_debug.zip *
Set-Location -Path $BUILD_FOLDER\MPF.Check\bin\Release\${FRAMEWORK}\${RUNTIME}\publish\
7z a -tzip $BUILD_FOLDER\MPF.Check_${FRAMEWORK}_${RUNTIME}_release.zip *
}
}
# Reset the directory
Set-Location -Path $PSScriptRoot
# Reset the directory
Set-Location -Path $PSScriptRoot
}