Compare commits

...

23 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
21 changed files with 850 additions and 533 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,29 @@
### 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

View File

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

View File

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

@@ -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,6 +5,7 @@
<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>

View File

@@ -2,9 +2,6 @@
using System.ComponentModel;
using System.IO;
using MPF.Core.Utilities;
#if NET6_0_OR_GREATER
using LibIRD;
#endif
namespace MPF.Core.UI.ViewModels
{
@@ -628,7 +625,7 @@ namespace MPF.Core.UI.ViewModels
PICTextBoxEnabled = false;
LayerbreakTextBoxEnabled = false;
if (ParseLog(LogPath, out byte[]? key, out byte[]? id, out byte[]? pic))
if (Tools.ParseGetKeyLog(LogPath, out byte[]? key, out byte[]? id, out byte[]? pic))
{
Key = key;
DiscID = id;
@@ -678,7 +675,7 @@ namespace MPF.Core.UI.ViewModels
LogPathTextBoxEnabled = false;
LogPathBrowseButtonEnabled = false;
byte[]? id = ParseDiscID(DiscIDString);
byte[]? id = Tools.ParseDiscID(DiscIDString);
if (id != null)
{
DiscID = id;
@@ -717,7 +714,7 @@ namespace MPF.Core.UI.ViewModels
LogPathBrowseButtonEnabled = false;
HexKeyTextBoxEnabled = false;
byte[]? key = ParseKeyFile(KeyPath);
byte[]? key = Tools.ParseKeyFile(KeyPath);
if (key != null)
{
Key = key;
@@ -761,7 +758,7 @@ namespace MPF.Core.UI.ViewModels
KeyPathTextBoxEnabled = false;
KeyPathBrowseButtonEnabled = false;
byte[]? key = ParseHexKey(HexKey);
byte[]? key = Tools.ParseHexKey(HexKey);
if (key != null)
{
Key = key;
@@ -801,7 +798,7 @@ namespace MPF.Core.UI.ViewModels
PICTextBoxEnabled = false;
LayerbreakTextBoxEnabled = false;
PIC = ParsePICFile(PICPath);
PIC = Tools.ParsePICFile(PICPath);
if (PIC != null)
{
PICStatus = $"Using PIC from file: {Path.GetFileName(PICPath)}";
@@ -844,7 +841,7 @@ namespace MPF.Core.UI.ViewModels
PICPathBrowseButtonEnabled = false;
LayerbreakTextBoxEnabled = false;
PIC = ParsePIC(PICString);
PIC = Tools.ParsePIC(PICString);
if (PIC != null)
{
PICStatus = "Using provided PIC";
@@ -884,7 +881,7 @@ namespace MPF.Core.UI.ViewModels
PICPathBrowseButtonEnabled = false;
PICTextBoxEnabled = false;
Layerbreak = ParseLayerbreak(LayerbreakString);
Layerbreak = Tools.ParseLayerbreak(LayerbreakString);
if (Layerbreak != null)
{
PICStatus = $"Will generate a PIC using a Layerbreak of {Layerbreak}";
@@ -1050,8 +1047,8 @@ namespace MPF.Core.UI.ViewModels
try
{
#if NET6_0_OR_GREATER
// Create IRD
ReIRD ird = new(InputPath, Key, Layerbreak);
// Create Redump-style reproducible IRD
LibIRD.ReIRD ird = new(InputPath, Key, Layerbreak);
if (PIC != null)
ird.PIC = PIC;
if (DiscID != null && ird.DiscID[15] != 0x00)
@@ -1071,315 +1068,6 @@ namespace MPF.Core.UI.ViewModels
}
}
/// <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>
private static bool ParseLog(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 = 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 = 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 = 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>
private 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 = 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>
private 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>
private static byte[]? ParseHexKey(string? hexKey)
{
if (string.IsNullOrEmpty(hexKey))
return null;
string cleanHexKey = hexKey!.Trim().Replace("\n", string.Empty);
if (hexKey!.Length != 32)
return null;
// Convert to byte array, null if invalid hex string
byte[]? key = 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>
private 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>
private 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 = HexStringToByteArray(cleanPIC);
return pic;
}
/// <summary>
/// Validates 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>
private static long? ParseLayerbreak(string? inputLayerbreak)
{
if (string.IsNullOrEmpty(inputLayerbreak))
return null;
if (!long.TryParse(inputLayerbreak, out long layerbreak))
return null;
// Check that layerbreak is positive number and smaller than largest disc size (in sectors)
if (layerbreak <= 0 || layerbreak > 24438784)
return null;
return layerbreak;
}
#endregion
#region Helper Functions
/// <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>
private 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
}
}

View File

@@ -721,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.Redumper, InternalProgram.DiscImageCreator, InternalProgram.Aaru };
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();
@@ -1341,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();
@@ -1698,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
@@ -1792,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))
{
@@ -1831,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;
}
}
}
@@ -1900,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

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

View File

@@ -26,7 +26,7 @@ namespace MPF.UI.Core.Windows
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");
@@ -46,7 +46,15 @@ namespace MPF.UI.Core.Windows
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>
@@ -322,9 +330,15 @@ namespace MPF.UI.Core.Windows
/// </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";

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,12 +2,13 @@
<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>

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

@@ -10,63 +10,15 @@ image: Visual Studio 2022
# build step
build_script:
- dotnet restore
# .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

@@ -45,9 +45,9 @@ COMMIT=`git log --pretty=%H -1`
# Create the build matrix arrays
UI_FRAMEWORKS=("net8.0-windows")
UI_RUNTIMES=("win-x64")
UI_RUNTIMES=("win-x86" "win-x64")
CHECK_FRAMEWORKS=("net8.0")
CHECK_RUNTIMES=("win-x64" "linux-x64" "osx-x64")
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
)
@@ -35,9 +35,9 @@ $COMMIT = git log --pretty=format:"%H" -1
# Create the build matrix arrays
$UI_FRAMEWORKS = @('net8.0-windows')
$UI_RUNTIMES = @('win-x64')
$UI_RUNTIMES = @('win-x86', 'win-x64')
$CHECK_FRAMEWORKS = @('net8.0')
$CHECK_RUNTIMES = @('win-x64', 'linux-x64', 'osx-x64')
$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
}