Compare commits

...

49 Commits
2.0 ... 2.1

Author SHA1 Message Date
Matt Nadareski
7f4921a47c Bump version to 2.1 2021-07-22 21:02:32 -07:00
Matt Nadareski
07359d2a63 Tweak Linq statement 2021-07-22 20:59:08 -07:00
Matt Nadareski
e5ba670c04 Update to BurnOutSharp 1.7.0 2021-07-22 11:19:58 -07:00
Matt Nadareski
3096c369d3 Update changelog 2021-07-19 22:47:25 -07:00
Matt Nadareski
333368675a Add DMI/PFI/SS to zipfile (fixes #296) 2021-07-19 13:38:16 -07:00
Wilson
d78d744ae2 Fixed log window background turning white after some "time". (#295)
* Fixed launching MPF in Windows 7.

* Potential fix for log window background 'random' color change.

* Updated the color to use hex digits.
2021-07-17 20:35:38 -07:00
Wilson
a8efe056c1 Fixed launching MPF in Windows 7. (#292) 2021-07-12 16:46:30 -07:00
Matt Nadareski
9b2475a854 Add BD to list of supported types for PC (fixes #291) 2021-07-05 22:31:58 -07:00
Matt Nadareski
33e2b27e6d Add new header for mainInfo 2021-07-05 13:26:23 -07:00
Matt Nadareski
91fe9d9bec Support /mr value parameter 2021-07-02 22:57:07 -07:00
Matt Nadareski
b1b9b3134f Update to DIC 20210701 2021-07-01 14:19:25 -07:00
Matt Nadareski
ca5386b529 Update changelog 2021-06-20 11:23:30 -07:00
Matt Nadareski
347beca874 Handle Redump 503s / Timeouts (fixes #289) 2021-06-20 11:21:43 -07:00
Matt Nadareski
27ca4fd1af Check for updates logs as well 2021-06-19 20:47:41 -07:00
Matt Nadareski
6d57a05f8c Encode params for login 2021-06-19 16:12:42 -07:00
Matt Nadareski
4d81449cfa Minor updates 2021-06-19 15:51:18 -07:00
Matt Nadareski
76fcb3ae10 Separate "Include Artifacts" into a separate setting (fixes #287) 2021-06-19 15:41:46 -07:00
Matt Nadareski
b913f5f9ae Update Xbox disc detection (fixes #288) 2021-06-19 15:25:43 -07:00
Matt Nadareski
68fa1eecfb Replace dumping program output processing 2021-06-15 10:24:19 -07:00
Matt Nadareski
dc84f17ac5 Set the matcher for carriage returns 2021-06-15 10:23:14 -07:00
Matt Nadareski
c91137130b Fix newline processing, add changelog 2021-06-15 10:20:13 -07:00
Matt Nadareski
972b07551f Add Logging utility class 2021-06-15 09:55:14 -07:00
Matt Nadareski
61562fe995 Handle carriage returns even without fancy formatting 2021-06-15 09:35:11 -07:00
Matt Nadareski
01b6c3cc98 Minor formatting change in ProcessingQueue 2021-06-14 22:37:47 -07:00
Matt Nadareski
b461dc76b9 Fix incorrect normalization for Aaru 2021-06-14 22:15:36 -07:00
Matt Nadareski
03eeed4113 Update nuget packages 2021-06-03 22:39:50 -07:00
Matt Nadareski
07615cb336 Update nuget packages 2021-06-03 22:19:03 -07:00
Matt Nadareski
9a9a977cc4 Simplify flag validation
Instead of trying to determine if something is a flag, just check if it's one of the supported flags. This should reduce the amount of potential issues that come along with making assumptions about what a parameter could look like.
2021-06-03 22:13:50 -07:00
Matt Nadareski
ab5869331b Add throwability to cuesheet code 2021-06-03 21:58:52 -07:00
Matt Nadareski
1c9ebfd703 TODO additions 2021-06-03 21:20:45 -07:00
Matt Nadareski
a9bc3587a6 Update to DIC 20210601 2021-06-03 21:19:33 -07:00
BadAd84
b1af2b6061 Fix for high cpu usage, issue with loop. (#286) 2021-05-30 12:58:08 -07:00
Matt Nadareski
2fccd53098 Ensure CSSKey file included in log zipping 2021-05-27 12:07:21 -07:00
Matt Nadareski
a7ad3e41d9 Change offset value for HVN discs (fixes #285) 2021-05-27 11:49:24 -07:00
Matt Nadareski
1fc6476f71 Be smarter about checking for zipped logs 2021-05-27 11:46:47 -07:00
Matt Nadareski
53e5a1b1b1 Check for the zipped logs for dealing with overwrites (fixes #277) 2021-05-27 11:20:48 -07:00
Matt Nadareski
119e9dc4cc Always check for all DIC log files, just in case (fixes #281) 2021-05-27 11:00:56 -07:00
Matt Nadareski
9263769906 Fix negative offsets (fixes #282) 2021-05-27 10:41:25 -07:00
Matt Nadareski
ebaf9539b2 Allow users to customize protection scanning 2021-05-06 21:47:57 -07:00
Matt Nadareski
818fb5ab34 Add right click menu with theming 2021-04-28 11:07:54 -07:00
Matt Nadareski
2f514082a9 Replace WindowChrome with dragable titles instead 2021-04-28 10:08:18 -07:00
Matt Nadareski
5c5379e972 Update ScrollViewer style 2021-04-27 11:07:07 -07:00
Matt Nadareski
c39894d9fc Update ProgressBar style 2021-04-27 10:51:32 -07:00
Matt Nadareski
2c6cf0e736 Custom UI elements v2 2021-04-26 23:08:21 -07:00
Matt Nadareski
7e96bbee27 Custom UI elements v1
This enables full-window dark mode to be a reality. The problem is that my design skills are very low
2021-04-26 22:32:05 -07:00
Matt Nadareski
f7019546d1 Add experimental dark mode (fixes #272) 2021-04-26 12:07:21 -07:00
Matt Nadareski
086f54abf0 Topsy-turvy (fixes #274) 2021-04-25 14:08:00 -07:00
Matt Nadareski
143a4149bd Parametrics (#276)
* Begin reducing complexity of parameters classes

* Make the method virtual, not the field

* Consolidate helper methods

* Fix "empty" commands; Fix DD generation

* Handle all sorts of numerical values

* Rip out remaining unneeded enum
2021-04-25 14:03:55 -07:00
Whovian9369
20aed68d58 Fix README formatting in "Notable Testers" Section (#275) 2021-04-25 10:19:15 -07:00
53 changed files with 5805 additions and 3613 deletions

View File

@@ -1,3 +1,32 @@
### 2.1 (2021-07-22)
- Enum, no more
- Sony works backward
- Add experimental dark mode
- Allow users to customize protection scanning
- Fix negative offsets for `/a` flag
- Always check for all DIC log files, just in case
- Check for the zipped logs for dealing with overwrites
- Be smarter about checking for zipped logs
- Change offset value for HVN discs
- Update to DIC 20210601
- Fix Aaru command normalization
- Handle carriage returns better
- Add logging helper class
- Set matcher on carriage return for log formatting
- Replace dumping program output processing
- Fix volume name detection for XBOX discs
- Add new setting for including artifacts in serialzied JSON
- Fix logging in to Redump for verifications
- Gracefully handle timeouts during login
- Update to DIC 20210701
- Support `/mr` value parameter
- Support new mainInfo header
- Add BD to IBM-PC supported discs
- Fix launching MPF on Windows 7
- Fix log window background turning white
- Add DMI/PFI/SS to log zipfile
- Update to BurnOutSharp 1.7.0
### 2.0 (2021-04-23)
- Rename DICUI to Media Preservation Frontend (MPF)
- Add handling for BEh drive _mainInfo.txt changes

View File

@@ -2,15 +2,15 @@
<PropertyGroup>
<TargetFrameworks>net48;netcoreapp3.1</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<OutputType>Exe</OutputType>
<Prefer32Bit>true</Prefer32Bit>
<Title>MPF Check</Title>
<AssemblyName>MPF.Check</AssemblyName>
<Description>Validator for various dumping programs</Description>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2021</Copyright>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<Version>2.0</Version>
<Version>2.1</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<FileVersion>$(Version)</FileVersion>
<IncludeSource>true</IncludeSource>
@@ -28,10 +28,13 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="1.6.1" GeneratePathProperty="true">
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="1.7.0" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.0" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>

View File

@@ -5,6 +5,8 @@ namespace MPF.Aaru
/// </summary>
public static class CommandStrings
{
public const string NONE = "";
// Database Family
public const string DatabasePrefixShort = "db";
public const string DatabasePrefixLong = "database";

View File

@@ -18,407 +18,5 @@ namespace MPF.Aaru
}
#endregion
#region Convert to Long Name
/// <summary>
/// Get the string representation of the Command enum values
/// </summary>
/// <param name="command">Command value to convert</param>
/// <returns>String representing the value, if possible</returns>
public static string LongName(Command command)
{
switch (command)
{
// Database Family
case Command.DatabaseStats:
return $"{CommandStrings.DatabasePrefixLong} {CommandStrings.DatabaseStats}";
case Command.DatabaseUpdate:
return $"{CommandStrings.DatabasePrefixLong} {CommandStrings.DatabaseUpdate}";
// Device Family
case Command.DeviceInfo:
return $"{CommandStrings.DevicePrefixLong} {CommandStrings.DeviceInfo}";
case Command.DeviceList:
return $"{CommandStrings.DevicePrefixLong} {CommandStrings.DeviceList}";
case Command.DeviceReport:
return $"{CommandStrings.DevicePrefixLong} {CommandStrings.DeviceReport}";
// Filesystem Family
case Command.FilesystemExtract:
return $"{CommandStrings.FilesystemPrefixLong} {CommandStrings.FilesystemExtract}";
case Command.FilesystemList:
return $"{CommandStrings.FilesystemPrefixLong} {CommandStrings.FilesystemListLong}";
case Command.FilesystemOptions:
return $"{CommandStrings.FilesystemPrefixLong} {CommandStrings.FilesystemOptions}";
// Image Family
case Command.ImageAnalyze:
return $"{CommandStrings.ImagePrefixLong} {CommandStrings.ImageAnalyze}";
case Command.ImageChecksum:
return $"{CommandStrings.ImagePrefixLong} {CommandStrings.ImageChecksumLong}";
case Command.ImageCompare:
return $"{CommandStrings.ImagePrefixLong} {CommandStrings.ImageCompareLong}";
case Command.ImageConvert:
return $"{CommandStrings.ImagePrefixLong} {CommandStrings.ImageConvert}";
case Command.ImageCreateSidecar:
return $"{CommandStrings.ImagePrefixLong} {CommandStrings.ImageCreateSidecar}";
case Command.ImageDecode:
return $"{CommandStrings.ImagePrefixLong} {CommandStrings.ImageDecode}";
case Command.ImageEntropy:
return $"{CommandStrings.ImagePrefixLong} {CommandStrings.ImageEntropy}";
case Command.ImageInfo:
return $"{CommandStrings.ImagePrefixLong} {CommandStrings.ImageInfo}";
case Command.ImageOptions:
return $"{CommandStrings.ImagePrefixLong} {CommandStrings.ImageOptions}";
case Command.ImagePrint:
return $"{CommandStrings.ImagePrefixLong} {CommandStrings.ImagePrint}";
case Command.ImageVerify:
return $"{CommandStrings.ImagePrefixLong} {CommandStrings.ImageVerify}";
// Media Family
case Command.MediaDump:
return $"{CommandStrings.MediaPrefixLong} {CommandStrings.MediaDump}";
case Command.MediaInfo:
return $"{CommandStrings.MediaPrefixLong} {CommandStrings.MediaInfo}";
case Command.MediaScan:
return $"{CommandStrings.MediaPrefixLong} {CommandStrings.MediaScan}";
// Standalone Commands
case Command.Configure:
return CommandStrings.Configure;
case Command.Formats:
return CommandStrings.Formats;
case Command.ListEncodings:
return CommandStrings.ListEncodings;
case Command.ListNamespaces:
return CommandStrings.ListNamespaces;
case Command.Remote:
return CommandStrings.Remote;
case Command.NONE:
default:
return "";
}
}
/// <summary>
/// Get the string representation of the Flag enum values
/// </summary>
/// <param name="command">Flag value to convert</param>
/// <returns>String representing the value, if possible</returns>
public static string LongName(Flag flag)
{
switch (flag)
{
// Boolean flags
case Flag.Adler32:
return FlagStrings.Adler32Long;
case Flag.Clear:
return FlagStrings.ClearLong;
case Flag.ClearAll:
return FlagStrings.ClearAllLong;
case Flag.CRC16:
return FlagStrings.CRC16Long;
case Flag.CRC32:
return FlagStrings.CRC32Long;
case Flag.CRC64:
return FlagStrings.CRC64Long;
case Flag.Debug:
return FlagStrings.DebugLong;
case Flag.DiskTags:
return FlagStrings.DiskTagsLong;
case Flag.DuplicatedSectors:
return FlagStrings.DuplicatedSectorsLong;
case Flag.Eject:
return FlagStrings.EjectLong;
case Flag.ExtendedAttributes:
return FlagStrings.ExtendedAttributesLong;
case Flag.Filesystems:
return FlagStrings.FilesystemsLong;
case Flag.FirstPregap:
return FlagStrings.FirstPregapLong;
case Flag.FixOffset:
return FlagStrings.FixOffsetLong;
case Flag.FixSubchannel:
return FlagStrings.FixSubchannelLong;
case Flag.FixSubchannelCrc:
return FlagStrings.FixSubchannelCrcLong;
case Flag.FixSubchannelPosition:
return FlagStrings.FixSubchannelPositionLong;
case Flag.Fletcher16:
return FlagStrings.Fletcher16Long;
case Flag.Fletcher32:
return FlagStrings.Fletcher32Long;
case Flag.Force:
return FlagStrings.ForceLong;
case Flag.GenerateSubchannels:
return FlagStrings.GenerateSubchannelsLong;
case Flag.Help:
return FlagStrings.HelpLong;
case Flag.LongFormat:
return FlagStrings.LongFormatLong;
case Flag.LongSectors:
return FlagStrings.LongSectorsLong;
case Flag.MD5:
return FlagStrings.MD5Long;
case Flag.Metadata:
return FlagStrings.MetadataLong;
case Flag.Partitions:
return FlagStrings.PartitionsLong;
case Flag.Persistent:
return FlagStrings.PersistentLong;
case Flag.Private:
return FlagStrings.PrivateLong;
case Flag.Resume:
return FlagStrings.ResumeLong;
case Flag.RetrySubchannel:
return FlagStrings.RetrySubchannelLong;
case Flag.SectorTags:
return FlagStrings.SectorTagsLong;
case Flag.SeparatedTracks:
return FlagStrings.SeparatedTracksLong;
case Flag.SHA1:
return FlagStrings.SHA1Long;
case Flag.SHA256:
return FlagStrings.SHA256Long;
case Flag.SHA384:
return FlagStrings.SHA384Long;
case Flag.SHA512:
return FlagStrings.SHA512Long;
case Flag.SkipCdiReadyHole:
return FlagStrings.SkipCdiReadyHoleLong;
case Flag.SpamSum:
return FlagStrings.SpamSumLong;
case Flag.StopOnError:
return FlagStrings.StopOnErrorLong;
case Flag.Tape:
return FlagStrings.TapeLong;
case Flag.Trim:
return FlagStrings.TrimLong;
case Flag.Verbose:
return FlagStrings.VerboseLong;
case Flag.VerifyDisc:
return FlagStrings.VerifyDiscLong;
case Flag.VerifySectors:
return FlagStrings.VerifySectorsLong;
case Flag.Version:
return FlagStrings.VersionLong;
case Flag.WholeDisc:
return FlagStrings.WholeDiscLong;
// Int8 flags
case Flag.Speed:
return FlagStrings.SpeedLong;
// Int16 flags
case Flag.RetryPasses:
return FlagStrings.RetryPassesLong;
case Flag.Width:
return FlagStrings.WidthLong;
// Int32 flags
case Flag.BlockSize:
return FlagStrings.BlockSizeLong;
case Flag.Count:
return FlagStrings.CountLong;
case Flag.MediaLastSequence:
return FlagStrings.MediaLastSequenceLong;
case Flag.MediaSequence:
return FlagStrings.MediaSequenceLong;
case Flag.Skip:
return FlagStrings.SkipLong;
// Int64 flags
case Flag.Length:
return FlagStrings.LengthLong;
case Flag.Start:
return FlagStrings.StartLong;
// String flags
case Flag.Comments:
return FlagStrings.CommentsLong;
case Flag.Creator:
return FlagStrings.CreatorLong;
case Flag.DriveManufacturer:
return FlagStrings.DriveManufacturerLong;
case Flag.DriveModel:
return FlagStrings.DriveModelLong;
case Flag.DriveRevision:
return FlagStrings.DriveRevisionLong;
case Flag.DriveSerial:
return FlagStrings.DriveSerialLong;
case Flag.Encoding:
return FlagStrings.EncodingLong;
case Flag.FormatConvert:
return FlagStrings.FormatConvertLong;
case Flag.FormatDump:
return FlagStrings.FormatDumpLong;
case Flag.ImgBurnLog:
return FlagStrings.ImgBurnLogLong;
case Flag.MediaBarcode:
return FlagStrings.MediaBarcodeLong;
case Flag.MediaManufacturer:
return FlagStrings.MediaManufacturerLong;
case Flag.MediaModel:
return FlagStrings.MediaModelLong;
case Flag.MediaPartNumber:
return FlagStrings.MediaPartNumberLong;
case Flag.MediaSerial:
return FlagStrings.MediaSerialLong;
case Flag.MediaTitle:
return FlagStrings.MediaTitleLong;
case Flag.MHDDLog:
return FlagStrings.MHDDLogLong;
case Flag.Namespace:
return FlagStrings.NamespaceLong;
case Flag.Options:
return FlagStrings.OptionsLong;
case Flag.OutputPrefix:
return FlagStrings.OutputPrefixLong;
case Flag.ResumeFile:
return FlagStrings.ResumeFileLong;
case Flag.Subchannel:
return FlagStrings.SubchannelLong;
case Flag.XMLSidecar:
return FlagStrings.XMLSidecarLong;
case Flag.NONE:
default:
return "";
}
}
#endregion
#region Convert From String
/// <summary>
/// Get the Command enum value for a given string
/// </summary>
/// <param name="commandOne">First part of String value to convert</param>
/// <param name="commandTwo">Second part of String value to convert</param>
/// <param name="useSecond">Output bool if the second command was used</param>
/// <returns>Command represented by the string(s), if possible</returns>
public static Command StringToCommand(string commandOne, string commandTwo, out bool useSecond)
{
useSecond = false;
switch (commandOne)
{
// Database Family
case CommandStrings.DatabasePrefixShort:
case CommandStrings.DatabasePrefixLong:
useSecond = true;
switch (commandTwo)
{
case CommandStrings.DatabaseStats:
return Command.DatabaseStats;
case CommandStrings.DatabaseUpdate:
return Command.DatabaseUpdate;
}
break;
// Device Family
case CommandStrings.DevicePrefixShort:
case CommandStrings.DevicePrefixLong:
useSecond = true;
switch (commandTwo)
{
case CommandStrings.DeviceInfo:
return Command.DeviceInfo;
case CommandStrings.DeviceList:
return Command.DeviceList;
case CommandStrings.DeviceReport:
return Command.DeviceReport;
}
break;
// Filesystem Family
case CommandStrings.FilesystemPrefixShort:
case CommandStrings.FilesystemPrefixShortAlt:
case CommandStrings.FilesystemPrefixLong:
useSecond = true;
switch (commandTwo)
{
case CommandStrings.FilesystemExtract:
return Command.FilesystemExtract;
case CommandStrings.FilesystemListShort:
case CommandStrings.FilesystemListLong:
return Command.FilesystemList;
case CommandStrings.DatabaseStats:
return Command.FilesystemOptions;
}
break;
// Image Family
case CommandStrings.ImagePrefixShort:
case CommandStrings.ImagePrefixLong:
useSecond = true;
switch (commandTwo)
{
case CommandStrings.ImageAnalyze:
return Command.ImageAnalyze;
case CommandStrings.ImageChecksumShort:
case CommandStrings.ImageChecksumLong:
return Command.ImageChecksum;
case CommandStrings.ImageCompareShort:
case CommandStrings.ImageCompareLong:
return Command.ImageCompare;
case CommandStrings.ImageConvert:
return Command.ImageConvert;
case CommandStrings.ImageCreateSidecar:
return Command.ImageCreateSidecar;
case CommandStrings.ImageDecode:
return Command.ImageDecode;
case CommandStrings.ImageEntropy:
return Command.ImageEntropy;
case CommandStrings.ImageInfo:
return Command.ImageInfo;
case CommandStrings.ImageOptions:
return Command.ImageOptions;
case CommandStrings.ImagePrint:
return Command.ImagePrint;
case CommandStrings.ImageVerify:
return Command.ImageVerify;
}
break;
// Media Family
case CommandStrings.MediaPrefixShort:
case CommandStrings.MediaPrefixLong:
useSecond = true;
switch (commandTwo)
{
case CommandStrings.MediaDump:
return Command.MediaDump;
case CommandStrings.MediaInfo:
return Command.MediaInfo;
case CommandStrings.MediaScan:
return Command.MediaScan;
}
break;
// Standalone Commands
case CommandStrings.Configure:
return Command.Configure;
case CommandStrings.Formats:
return Command.Formats;
case CommandStrings.ListEncodings:
return Command.ListEncodings;
case CommandStrings.ListNamespaces:
return Command.ListNamespaces;
case CommandStrings.Remote:
return Command.Remote;
}
return Command.NONE;
}
#endregion
}
}

View File

@@ -1,149 +0,0 @@
namespace MPF.Aaru
{
/// <summary>
/// Supported Aaru commands
/// </summary>
public enum Command : int
{
NONE = 0,
// Database Family
DatabaseStats,
DatabaseUpdate,
// Device Family
DeviceInfo,
DeviceList,
DeviceReport,
// Filesystem Family
FilesystemExtract,
FilesystemList,
FilesystemOptions,
// Image Family
ImageAnalyze,
ImageChecksum,
ImageCompare,
ImageConvert,
ImageCreateSidecar,
ImageDecode,
ImageEntropy,
ImageInfo,
ImageOptions,
ImagePrint,
ImageVerify,
// Media Family
MediaDump,
MediaInfo,
MediaScan,
// Standalone Commands
Configure,
Formats,
ListEncodings,
ListNamespaces,
Remote,
}
/// <summary>
/// Supported Aaru flags
/// </summary>
public enum Flag : int
{
NONE = 0,
// Boolean flags
Adler32,
Clear,
ClearAll,
CRC16,
CRC32,
CRC64,
Debug,
DiskTags,
DuplicatedSectors,
Eject,
ExtendedAttributes,
Filesystems,
FirstPregap,
FixOffset,
FixSubchannel,
FixSubchannelCrc,
FixSubchannelPosition,
Fletcher16,
Fletcher32,
Force,
GenerateSubchannels,
Help,
LongFormat,
LongSectors,
MD5,
Metadata,
Partitions,
Persistent,
Private,
Resume,
RetrySubchannel,
SectorTags,
SeparatedTracks,
SHA1,
SHA256,
SHA384,
SHA512,
SkipCdiReadyHole,
SpamSum,
StopOnError,
Tape,
Trim,
Verbose,
VerifyDisc,
VerifySectors,
Version,
WholeDisc,
// Int8 flags
Speed,
// Int16 flags
RetryPasses,
Width,
// Int32 flags
BlockSize,
Count,
MediaLastSequence,
MediaSequence,
Skip,
// Int64 flags
Length,
Start,
// String flags
Comments,
Creator,
DriveManufacturer,
DriveModel,
DriveRevision,
DriveSerial,
Encoding,
FormatConvert,
FormatDump,
ImgBurnLog,
MediaBarcode,
MediaManufacturer,
MediaModel,
MediaPartNumber,
MediaSerial,
MediaTitle,
MHDDLog,
Namespace,
Options,
OutputPrefix,
ResumeFile,
Subchannel,
XMLSidecar,
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,6 @@ using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using MPF.Data;
using MPF.Utilities;
namespace MPF.CleanRip
{
@@ -31,7 +30,7 @@ namespace MPF.CleanRip
#region BaseParameters Implementations
/// <inheritdoc/>
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath)
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
{
List<string> missingFiles = new List<string>();
switch (this.Type)
@@ -39,10 +38,13 @@ namespace MPF.CleanRip
case MediaType.DVD: // Only added here to help users; not strictly correct
case MediaType.NintendoGameCubeGameDisc:
case MediaType.NintendoWiiOpticalDisc:
if (!File.Exists($"{basePath}-dumpinfo.txt"))
missingFiles.Add($"{basePath}-dumpinfo.txt");
if (!File.Exists($"{basePath}.bca"))
missingFiles.Add($"{basePath}.bca");
if (!File.Exists($"{basePath}_logs.zip") || !preCheck)
{
if (!File.Exists($"{basePath}-dumpinfo.txt"))
missingFiles.Add($"{basePath}-dumpinfo.txt");
if (!File.Exists($"{basePath}.bca"))
missingFiles.Add($"{basePath}.bca");
}
break;

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.IO;
/// <remarks>
@@ -71,10 +72,23 @@ namespace MPF.CueSheets
/// <param name="fileType">File type to set</param>
/// <param name="cueLines">Lines array to pull from</param>
/// <param name="i">Reference to index in array</param>
public CueFile(string fileName, string fileType, string[] cueLines, ref int i)
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
public CueFile(string fileName, string fileType, string[] cueLines, ref int i, bool throwOnError = false)
{
if (cueLines == null || i < 0 || i > cueLines.Length)
return; // TODO: Make this throw an exception
if (cueLines == null)
{
if (throwOnError)
throw new ArgumentNullException(nameof(cueLines));
return;
}
else if (i < 0 || i > cueLines.Length)
{
if (throwOnError)
throw new IndexOutOfRangeException();
return;
}
// Set the current fields
this.FileName = fileName.Trim('"');
@@ -102,14 +116,24 @@ namespace MPF.CueSheets
// Read track information
case "TRACK":
if (splitLine.Length < 3)
continue; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"TRACK line malformed: {line}");
continue;
}
if (this.Tracks == null)
this.Tracks = new List<CueTrack>();
var track = new CueTrack(splitLine[1], splitLine[2], cueLines, ref i);
if (track == default)
continue; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"TRACK line malformed: {line}");
continue;
}
this.Tracks.Add(track);
break;
@@ -126,11 +150,24 @@ namespace MPF.CueSheets
/// Write the FILE out to a stream
/// </summary>
/// <param name="sw">StreamWriter to write to</param>
public void Write(StreamWriter sw)
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
public void Write(StreamWriter sw, bool throwOnError = false)
{
// If we don't have any tracks, it's invalid
if (this.Tracks == null || this.Tracks.Count == 0)
return; // TODO: Make this throw an exception
if (this.Tracks == null)
{
if (throwOnError)
throw new ArgumentNullException(nameof(this.Tracks));
return;
}
else if (this.Tracks.Count == 0)
{
if (throwOnError)
throw new ArgumentException("No tracks provided to write");
return;
}
sw.WriteLine($"FILE \"{this.FileName}\" {FromFileType(this.FileType)}");

View File

@@ -1,4 +1,5 @@
using System.IO;
using System;
using System.IO;
using System.Linq;
/// <remarks>
@@ -45,47 +46,103 @@ namespace MPF.CueSheets
/// </summary>
/// <param name="index">Index to set</param>
/// <param name="startTime">Start time to set</param>
public CueIndex(string index, string startTime)
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
public CueIndex(string index, string startTime, bool throwOnError = false)
{
// Set the current fields
if (!int.TryParse(index, out int parsedIndex))
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new ArgumentException($"Index was not a number: {index}");
return;
}
else if (parsedIndex < 0 || parsedIndex > 99)
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new IndexOutOfRangeException($"Index must be between 0 and 99: {parsedIndex}");
return;
}
// Ignore empty lines
if (string.IsNullOrWhiteSpace(startTime))
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new ArgumentException("Start time was null or whitespace");
return;
}
// Ignore lines that don't contain the correct information
if (startTime.Length != 8 || startTime.Count(c => c == ':') != 2)
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"Start time was not in a recognized format: {startTime}");
return;
}
// Split the line
string[] splitTime = startTime.Split(':');
if (splitTime.Length != 3)
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"Start time was not in a recognized format: {startTime}");
return;
}
// Parse the lengths
int[] lengthSegments = new int[3];
// Minutes
if (!int.TryParse(splitTime[0], out lengthSegments[0]))
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"Minutes segment was not a number: {splitTime[0]}");
return;
}
else if (lengthSegments[0] < 0)
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new IndexOutOfRangeException($"Minutes segment must be 0 or greater: {lengthSegments[0]}");
return;
}
// Seconds
if (!int.TryParse(splitTime[1], out lengthSegments[1]))
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"Seconds segment was not a number: {splitTime[1]}");
return;
}
else if (lengthSegments[1] < 0 || lengthSegments[1] > 60)
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new IndexOutOfRangeException($"Seconds segment must be between 0 and 60: {lengthSegments[1]}");
return;
}
// Frames
if (!int.TryParse(splitTime[2], out lengthSegments[2]))
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"Frames segment was not a number: {splitTime[2]}");
return;
}
else if (lengthSegments[2] < 0 || lengthSegments[2] > 75)
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new IndexOutOfRangeException($"Frames segment must be between 0 and 75: {lengthSegments[2]}");
return;
}
// Set the values
this.Index = parsedIndex;

View File

@@ -56,7 +56,8 @@ namespace MPF.CueSheets
/// Create a cuesheet from a file, if possible
/// </summary>
/// <param name="filename"></param>
public CueSheet(string filename)
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
public CueSheet(string filename, bool throwOnError = false)
{
// Check that the file exists
if (!File.Exists(filename))
@@ -97,7 +98,12 @@ namespace MPF.CueSheets
// Read MCN
case "CATALOG":
if (splitLine.Length < 2)
continue; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"CATALOG line malformed: {line}");
continue;
}
this.Catalog = splitLine[1];
break;
@@ -105,7 +111,12 @@ namespace MPF.CueSheets
// Read external CD-Text file path
case "CDTEXTFILE":
if (splitLine.Length < 2)
continue; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"CDTEXTFILE line malformed: {line}");
continue;
}
this.CdTextFile = splitLine[1];
break;
@@ -113,7 +124,12 @@ namespace MPF.CueSheets
// Read CD-Text enhanced performer
case "PERFORMER":
if (splitLine.Length < 2)
continue; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"PERFORMER line malformed: {line}");
continue;
}
this.Performer = splitLine[1];
break;
@@ -121,7 +137,12 @@ namespace MPF.CueSheets
// Read CD-Text enhanced songwriter
case "SONGWRITER":
if (splitLine.Length < 2)
continue; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"SONGWRITER line malformed: {line}");
continue;
}
this.Songwriter = splitLine[1];
break;
@@ -129,7 +150,12 @@ namespace MPF.CueSheets
// Read CD-Text enhanced title
case "TITLE":
if (splitLine.Length < 2)
continue; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"TITLE line malformed: {line}");
continue;
}
this.Title = splitLine[1];
break;
@@ -137,14 +163,24 @@ namespace MPF.CueSheets
// Read file information
case "FILE":
if (splitLine.Length < 3)
continue; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"FILE line malformed: {line}");
continue;
}
if (this.Files == null)
this.Files = new List<CueFile>();
var file = new CueFile(splitLine[1], splitLine[2], cueLines, ref i);
if (file == default)
continue; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"FILE line malformed: {line}");
continue;
}
this.Files.Add(file);
break;
@@ -168,11 +204,24 @@ namespace MPF.CueSheets
/// Write the cuesheet out to a stream
/// </summary>
/// <param name="stream">Stream to write to</param>
public void Write(Stream stream)
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
public void Write(Stream stream, bool throwOnError = false)
{
// If we don't have any files, it's invalid
if (this.Files == null || this.Files.Count == 0)
return; // TODO: Make this throw an exception
if (this.Files == null)
{
if (throwOnError)
throw new ArgumentNullException(nameof(this.Files));
return;
}
else if (this.Files.Count == 0)
{
if (throwOnError)
throw new ArgumentException("No files provided to write");
return;
}
using (var sw = new StreamWriter(stream, Encoding.ASCII, 1024, true))
{

View File

@@ -156,16 +156,39 @@ namespace MPF.CueSheets
/// <param name="dataType">Data type to set</param>
/// <param name="cueLines">Lines array to pull from</param>
/// <param name="i">Reference to index in array</param>
public CueTrack(string number, string dataType, string[] cueLines, ref int i)
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
public CueTrack(string number, string dataType, string[] cueLines, ref int i, bool throwOnError = false)
{
if (cueLines == null || i < 0 || i > cueLines.Length)
return; // TODO: Make this throw an exception
if (cueLines == null)
{
if (throwOnError)
throw new ArgumentNullException(nameof(cueLines));
return;
}
else if (i < 0 || i > cueLines.Length)
{
if (throwOnError)
throw new IndexOutOfRangeException();
return;
}
// Set the current fields
if (!int.TryParse(number, out int parsedNumber))
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new ArgumentException($"Number was not a number: {number}");
return;
}
else if (parsedNumber < 1 || parsedNumber > 99)
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new IndexOutOfRangeException($"Index must be between 1 and 99: {parsedNumber}");
return;
}
this.Number = parsedNumber;
this.DataType = GetDataType(dataType);
@@ -192,7 +215,12 @@ namespace MPF.CueSheets
// Read flag information
case "FLAGS":
if (splitLine.Length < 2)
continue; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"FLAGS line malformed: {line}");
continue;
}
this.Flags = GetFlags(splitLine);
break;
@@ -200,7 +228,12 @@ namespace MPF.CueSheets
// Read International Standard Recording Code
case "ISRC":
if (splitLine.Length < 2)
continue; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"ISRC line malformed: {line}");
continue;
}
this.ISRC = splitLine[1];
break;
@@ -208,7 +241,12 @@ namespace MPF.CueSheets
// Read CD-Text enhanced performer
case "PERFORMER":
if (splitLine.Length < 2)
continue; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"PERFORMER line malformed: {line}");
continue;
}
this.Performer = splitLine[1];
break;
@@ -216,7 +254,12 @@ namespace MPF.CueSheets
// Read CD-Text enhanced songwriter
case "SONGWRITER":
if (splitLine.Length < 2)
continue; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"SONGWRITER line malformed: {line}");
continue;
}
this.Songwriter = splitLine[1];
break;
@@ -224,7 +267,12 @@ namespace MPF.CueSheets
// Read CD-Text enhanced title
case "TITLE":
if (splitLine.Length < 2)
continue; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"TITLE line malformed: {line}");
continue;
}
this.Title = splitLine[1];
break;
@@ -232,11 +280,21 @@ namespace MPF.CueSheets
// Read pregap information
case "PREGAP":
if (splitLine.Length < 2)
continue; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"PREGAP line malformed: {line}");
continue;
}
var pregap = new PreGap(splitLine[1]);
if (pregap == default)
continue; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"PREGAP line malformed: {line}");
continue;
}
this.PreGap = pregap;
break;
@@ -244,14 +302,24 @@ namespace MPF.CueSheets
// Read index information
case "INDEX":
if (splitLine.Length < 3)
continue; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"INDEX line malformed: {line}");
continue;
}
if (this.Indices == null)
this.Indices = new List<CueIndex>();
var index = new CueIndex(splitLine[1], splitLine[2]);
if (index == default)
continue; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"INDEX line malformed: {line}");
continue;
}
this.Indices.Add(index);
break;
@@ -259,11 +327,21 @@ namespace MPF.CueSheets
// Read postgap information
case "POSTGAP":
if (splitLine.Length < 2)
continue; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"POSTGAP line malformed: {line}");
continue;
}
var postgap = new PostGap(splitLine[1]);
if (postgap == default)
continue; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"POSTGAP line malformed: {line}");
continue;
}
this.PostGap = postgap;
break;
@@ -279,12 +357,25 @@ namespace MPF.CueSheets
/// <summary>
/// Write the TRACK out to a stream
/// </summary>
/// <param name="sw">StreamWriter to write to</param>
public void Write(StreamWriter sw)
/// <param name="sw">StreamWriter to write to</param
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
public void Write(StreamWriter sw, bool throwOnError = false)
{
// If we don't have any indices, it's invalid
if (this.Indices == null || this.Indices.Count == 0)
return; // TODO: Make this throw an exception
if (this.Indices == null)
{
if (throwOnError)
throw new ArgumentNullException(nameof(this.Indices));
return;
}
else if (this.Indices.Count == 0)
{
if (throwOnError)
throw new ArgumentException("No indices provided to write");
return;
}
sw.WriteLine($" TRACK {this.Number:D2} {FromDataType(this.DataType)}");
@@ -402,7 +493,6 @@ namespace MPF.CueSheets
foreach (string flagString in flagStrings)
{
// TODO: Make default throw an exception
switch (flagString.ToLowerInvariant())
{
case "flags":

View File

@@ -1,4 +1,5 @@
using System.IO;
using System;
using System.IO;
using System.Linq;
/// <remarks>
@@ -38,41 +39,87 @@ namespace MPF.CueSheets
/// Create a POSTGAP from a mm:ss:ff length
/// </summary>
/// <param name="length">String to get length information from</param>
public PostGap(string length)
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
public PostGap(string length, bool throwOnError = false)
{
// Ignore empty lines
if (string.IsNullOrWhiteSpace(length))
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new ArgumentException("Length was null or whitespace");
return;
}
// Ignore lines that don't contain the correct information
if (length.Length != 8 || length.Count(c => c == ':') != 2)
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"Length was not in a recognized format: {length}");
return;
}
// Split the line
string[] splitLength = length.Split(':');
if (splitLength.Length != 3)
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"Length was not in a recognized format: {length}");
return;
}
// Parse the lengths
int[] lengthSegments = new int[3];
// Minutes
if (!int.TryParse(splitLength[0], out lengthSegments[0]))
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"Minutes segment was not a number: {splitLength[0]}");
return;
}
else if (lengthSegments[0] < 0)
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new IndexOutOfRangeException($"Minutes segment must be 0 or greater: {lengthSegments[0]}");
return;
}
// Seconds
if (!int.TryParse(splitLength[1], out lengthSegments[1]))
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"Seconds segment was not a number: {splitLength[1]}");
return;
}
else if (lengthSegments[1] < 0 || lengthSegments[1] > 60)
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new IndexOutOfRangeException($"Seconds segment must be between 0 and 60: {lengthSegments[1]}");
return;
}
// Frames
if (!int.TryParse(splitLength[2], out lengthSegments[2]))
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"Frames segment was not a number: {splitLength[2]}");
return;
}
else if (lengthSegments[2] < 0 || lengthSegments[2] > 75)
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new IndexOutOfRangeException($"Frames segment must be between 0 and 75: {lengthSegments[2]}");
return;
}
// Set the values
this.Minutes = lengthSegments[0];

View File

@@ -1,4 +1,5 @@
using System.IO;
using System;
using System.IO;
using System.Linq;
/// <remarks>
@@ -39,41 +40,87 @@ namespace MPF.CueSheets
/// Create a PREGAP from a mm:ss:ff length
/// </summary>
/// <param name="length">String to get length information from</param>
public PreGap(string length)
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
public PreGap(string length, bool throwOnError = false)
{
// Ignore empty lines
if (string.IsNullOrWhiteSpace(length))
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new ArgumentException("Length was null or whitespace");
return;
}
// Ignore lines that don't contain the correct information
if (length.Length != 8 || length.Count(c => c == ':') != 2)
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"Length was not in a recognized format: {length}");
return;
}
// Split the line
string[] splitLength = length.Split(':');
if (splitLength.Length != 3)
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"Length was not in a recognized format: {length}");
return;
}
// Parse the lengths
int[] lengthSegments = new int[3];
// Minutes
if (!int.TryParse(splitLength[0], out lengthSegments[0]))
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"Minutes segment was not a number: {splitLength[0]}");
return;
}
else if (lengthSegments[0] < 0)
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new IndexOutOfRangeException($"Minutes segment must be 0 or greater: {lengthSegments[0]}");
return;
}
// Seconds
if (!int.TryParse(splitLength[1], out lengthSegments[1]))
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"Seconds segment was not a number: {splitLength[1]}");
return;
}
else if (lengthSegments[1] < 0 || lengthSegments[1] > 60)
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new IndexOutOfRangeException($"Seconds segment must be between 0 and 60: {lengthSegments[1]}");
return;
}
// Frames
if (!int.TryParse(splitLength[2], out lengthSegments[2]))
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new FormatException($"Frames segment was not a number: {splitLength[2]}");
return;
}
else if (lengthSegments[2] < 0 || lengthSegments[2] > 75)
return; // TODO: Make this throw an exception
{
if (throwOnError)
throw new IndexOutOfRangeException($"Frames segment must be between 0 and 75: {lengthSegments[2]}");
return;
}
// Set the values
this.Minutes = lengthSegments[0];

View File

@@ -5,6 +5,7 @@ namespace MPF.DD
/// </summary>
public static class CommandStrings
{
public const string NONE = "";
public const string List = "--list";
}

View File

@@ -18,87 +18,5 @@ namespace MPF.DD
}
#endregion
#region Convert to Long Name
/// <summary>
/// Get the string representation of the Command enum values
/// </summary>
/// <param name="command">Command value to convert</param>
/// <returns>String representing the value, if possible</returns>
public static string LongName(Command command)
{
switch (command)
{
case Command.List:
return CommandStrings.List;
case Command.NONE:
default:
return "";
}
}
/// <summary>
/// Get the string representation of the Flag enum values
/// </summary>
/// <param name="command">Flag value to convert</param>
/// <returns>String representing the value, if possible</returns>
public static string LongName(Flag flag)
{
switch (flag)
{
// Boolean flags
case Flag.Progress:
return FlagStrings.Progress;
case Flag.Size:
return FlagStrings.Size;
// Int64 flags
case Flag.BlockSize:
return FlagStrings.BlockSize;
case Flag.Count:
return FlagStrings.Count;
case Flag.Seek:
return FlagStrings.Seek;
case Flag.Skip:
return FlagStrings.Skip;
// String flags
case Flag.Filter:
return FlagStrings.Filter;
case Flag.InputFile:
return FlagStrings.InputFile;
case Flag.OutputFile:
return FlagStrings.OutputFile;
case Flag.NONE:
default:
return "";
}
}
#endregion
#region Convert From String
/// <summary>
/// Get the Command enum value for a given string
/// </summary>
/// <param name="command">String value to convert</param>
/// <returns>Command represented by the string(s), if possible</returns>
public static Command StringToCommand(string command)
{
switch (command)
{
case CommandStrings.List:
return Command.List;
default:
return Command.NONE;
}
}
#endregion
}
}

View File

@@ -1,34 +0,0 @@
namespace MPF.DD
{
/// <summary>
/// Supported DD commands
/// </summary>
public enum Command: int
{
NONE = 0, // For DD, this represents a normal dump
List,
}
/// <summary>
/// Supported DD flags
/// </summary>
public enum Flag : int
{
NONE = 0,
// Boolean flags
Progress,
Size,
// Int64 flags
BlockSize,
Count,
Seek,
Skip,
// String flags
Filter,
InputFile,
OutputFile,
}
}

View File

@@ -1,10 +1,8 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using MPF.Data;
using MPF.Utilities;
namespace MPF.DD
{
@@ -16,7 +14,7 @@ namespace MPF.DD
#region Generic Dumping Information
/// <inheritdoc/>
public override string InputPath => InputFileValue;
public override string InputPath => InputFileValue?.TrimStart('\\', '?');
/// <inheritdoc/>
public override string OutputPath => OutputFileValue;
@@ -33,36 +31,11 @@ namespace MPF.DD
#region Metadata
/// <summary>
/// Base command to run
/// </summary>
public Command BaseCommand { get; set; }
/// <inheritdoc/>
public override InternalProgram InternalProgram => InternalProgram.DD;
#endregion
/// <summary>
/// Set of flags to pass to the executable
/// </summary>
protected Dictionary<Flag, bool?> _flags = new Dictionary<Flag, bool?>();
public bool? this[Flag key]
{
get
{
if (_flags.ContainsKey(key))
return _flags[key];
return null;
}
set
{
_flags[key] = value;
}
}
protected internal IEnumerable<Flag> Keys => _flags.Keys;
#region Flag Values
public long? BlockSizeValue { get; set; }
@@ -94,7 +67,7 @@ namespace MPF.DD
#region BaseParameters Implementations
/// <inheritdoc/>
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath)
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
{
// TODO: Figure out what sort of output files are expected... just `.bin`?
return (true, new List<string>());
@@ -161,23 +134,26 @@ namespace MPF.DD
{
List<string> parameters = new List<string>();
if (BaseCommand != Command.NONE)
parameters.Add(Converters.LongName(BaseCommand));
if (BaseCommand == null)
BaseCommand = CommandStrings.NONE;
if (!string.IsNullOrEmpty(BaseCommand))
parameters.Add(BaseCommand);
#region Boolean flags
// Progress
if (GetSupportedCommands(Flag.Progress).Contains(BaseCommand))
if (IsFlagSupported(FlagStrings.Progress))
{
if (this[Flag.Progress] == true)
parameters.Add($"{this[Flag.Progress]}");
if (this[FlagStrings.Progress] == true)
parameters.Add($"{FlagStrings.Progress}");
}
// Size
if (GetSupportedCommands(Flag.Size).Contains(BaseCommand))
if (IsFlagSupported(FlagStrings.Size))
{
if (this[Flag.Size] == true)
parameters.Add($"{this[Flag.Size]}");
if (this[FlagStrings.Size] == true)
parameters.Add($"{FlagStrings.Size}");
}
#endregion
@@ -185,31 +161,31 @@ namespace MPF.DD
#region Int64 flags
// Block Size
if (GetSupportedCommands(Flag.BlockSize).Contains(BaseCommand))
if (IsFlagSupported(FlagStrings.BlockSize))
{
if (this[Flag.BlockSize] == true && BlockSizeValue != null)
parameters.Add($"{Converters.LongName(Flag.BlockSize)}={BlockSizeValue}");
if (this[FlagStrings.BlockSize] == true && BlockSizeValue != null)
parameters.Add($"{FlagStrings.BlockSize}={BlockSizeValue}");
}
// Count
if (GetSupportedCommands(Flag.Count).Contains(BaseCommand))
if (IsFlagSupported(FlagStrings.Count))
{
if (this[Flag.Count] == true && CountValue != null)
parameters.Add($"{Converters.LongName(Flag.Count)}={CountValue}");
if (this[FlagStrings.Count] == true && CountValue != null)
parameters.Add($"{FlagStrings.Count}={CountValue}");
}
// Seek
if (GetSupportedCommands(Flag.Seek).Contains(BaseCommand))
if (IsFlagSupported(FlagStrings.Seek))
{
if (this[Flag.Seek] == true && SeekValue != null)
parameters.Add($"{Converters.LongName(Flag.Seek)}={SeekValue}");
if (this[FlagStrings.Seek] == true && SeekValue != null)
parameters.Add($"{FlagStrings.Seek}={SeekValue}");
}
// Skip
if (GetSupportedCommands(Flag.Skip).Contains(BaseCommand))
if (IsFlagSupported(FlagStrings.Skip))
{
if (this[Flag.Skip] == true && SkipValue != null)
parameters.Add($"{Converters.LongName(Flag.Skip)}={SkipValue}");
if (this[FlagStrings.Skip] == true && SkipValue != null)
parameters.Add($"{FlagStrings.Skip}={SkipValue}");
}
#endregion
@@ -217,33 +193,57 @@ namespace MPF.DD
#region String flags
// Filter
if (GetSupportedCommands(Flag.Filter).Contains(BaseCommand))
if (IsFlagSupported(FlagStrings.Filter))
{
if (this[Flag.Filter] == true && FilterValue != null)
parameters.Add($"{Converters.LongName(Flag.Filter)}={FilterValue}");
if (this[FlagStrings.Filter] == true && FilterValue != null)
parameters.Add($"{FlagStrings.Filter}={FilterValue}");
}
// Input File
if (GetSupportedCommands(Flag.InputFile).Contains(BaseCommand))
if (IsFlagSupported(FlagStrings.InputFile))
{
if (this[Flag.InputFile] == true && InputFileValue != null)
parameters.Add($"{Converters.LongName(Flag.InputFile)}={InputFileValue}");
if (this[FlagStrings.InputFile] == true && InputFileValue != null)
parameters.Add($"{FlagStrings.InputFile}=\"{InputFileValue}\"");
else
return null;
}
// Output File
if (GetSupportedCommands(Flag.OutputFile).Contains(BaseCommand))
if (IsFlagSupported(FlagStrings.OutputFile))
{
if (this[Flag.OutputFile] == true && OutputFileValue != null)
parameters.Add($"{Converters.LongName(Flag.OutputFile)}={OutputFileValue}");
if (this[FlagStrings.OutputFile] == true && OutputFileValue != null)
parameters.Add($"{FlagStrings.OutputFile}=\"{OutputFileValue}\"");
else
return null;
}
#endregion
return string.Empty;
return string.Join(" ", parameters);
}
/// <inheritdoc/>
public override Dictionary<string, List<string>> GetCommandSupport()
{
return new Dictionary<string, List<string>>()
{
[CommandStrings.NONE] = new List<string>()
{
FlagStrings.BlockSize,
FlagStrings.Count,
FlagStrings.Filter,
FlagStrings.InputFile,
FlagStrings.OutputFile,
FlagStrings.Progress,
FlagStrings.Seek,
FlagStrings.Size,
FlagStrings.Skip,
},
[CommandStrings.List] = new List<string>()
{
},
};
}
/// <inheritdoc/>
@@ -254,7 +254,7 @@ namespace MPF.DD
{
switch (this.BaseCommand)
{
case Command.List:
case CommandStrings.List:
return false;
default:
return true;
@@ -264,9 +264,9 @@ namespace MPF.DD
/// <inheritdoc/>
protected override void ResetValues()
{
BaseCommand = Command.NONE;
BaseCommand = CommandStrings.NONE;
_flags = new Dictionary<Flag, bool?>();
flags = new Dictionary<string, bool?>();
BlockSizeValue = null;
CountValue = null;
@@ -279,16 +279,16 @@ namespace MPF.DD
/// <inheritdoc/>
protected override void SetDefaultParameters(char driveLetter, string filename, int? driveSpeed, Options options)
{
BaseCommand = Command.NONE;
BaseCommand = CommandStrings.NONE;
this[Flag.InputFile] = true;
this[FlagStrings.InputFile] = true;
InputFileValue = $"\\\\?\\{driveLetter}:";
this[Flag.OutputFile] = true;
this[FlagStrings.OutputFile] = true;
OutputFileValue = filename;
// TODO: Add more common block sizes
this[Flag.BlockSize] = true;
this[FlagStrings.BlockSize] = true;
switch (this.Type)
{
case MediaType.FloppyDisk:
@@ -300,13 +300,15 @@ namespace MPF.DD
break;
}
this[Flag.Progress] = true;
this[Flag.Size] = true;
this[FlagStrings.Progress] = true;
this[FlagStrings.Size] = true;
}
/// <inheritdoc/>
protected override bool ValidateAndSetParameters(string parameters)
{
BaseCommand = CommandStrings.NONE;
// The string has to be valid by itself first
if (string.IsNullOrWhiteSpace(parameters))
return false;
@@ -321,13 +323,14 @@ namespace MPF.DD
// Determine what the commandline should look like given the first item
int start = 0;
BaseCommand = Converters.StringToCommand(parts[0]);
if (BaseCommand != Command.NONE)
if (parts[0] == CommandStrings.List)
{
BaseCommand = parts[0];
start = 1;
}
// Loop through all auxilary flags, if necessary
int i = 0;
for (i = start; i < parts.Count; i++)
for (int i = start; i < parts.Count; i++)
{
// Flag read-out values
long? longValue = null;
@@ -339,41 +342,33 @@ namespace MPF.DD
#region Boolean flags
// Progress
ProcessBooleanParameter(parts, FlagStrings.Progress, Flag.Progress, ref i);
ProcessFlagParameter(parts, FlagStrings.Progress, ref i);
// Size
ProcessBooleanParameter(parts, FlagStrings.Size, Flag.Size, ref i);
ProcessFlagParameter(parts, FlagStrings.Size, ref i);
#endregion
#region Int64 flags
// Block Size
longValue = ProcessInt64Parameter(parts, FlagStrings.BlockSize, Flag.BlockSize, ref i);
if (longValue == Int64.MinValue)
return false;
else if (longValue != null)
longValue = ProcessInt64Parameter(parts, FlagStrings.BlockSize, ref i);
if (longValue != null)
BlockSizeValue = longValue;
// Count
longValue = ProcessInt64Parameter(parts, FlagStrings.Count, Flag.Count, ref i);
if (longValue == Int64.MinValue)
return false;
else if (longValue != null)
longValue = ProcessInt64Parameter(parts, FlagStrings.Count, ref i);
if (longValue != null)
CountValue = longValue;
// Seek
longValue = ProcessInt64Parameter(parts, FlagStrings.Seek, Flag.Seek, ref i);
if (longValue == Int64.MinValue)
return false;
else if (longValue != null)
longValue = ProcessInt64Parameter(parts, FlagStrings.Seek, ref i);
if (longValue != null)
SeekValue = longValue;
// Skip
longValue = ProcessInt64Parameter(parts, FlagStrings.Skip, Flag.Skip, ref i);
if (longValue == Int64.MinValue)
return false;
else if (longValue != null)
longValue = ProcessInt64Parameter(parts, FlagStrings.Skip, ref i);
if (longValue != null)
SkipValue = longValue;
#endregion
@@ -381,22 +376,18 @@ namespace MPF.DD
#region String flags
// Filter (fixed, removable, disk, partition)
stringValue = ProcessStringParameter(parts, FlagStrings.Filter, Flag.Filter, ref i);
stringValue = ProcessStringParameter(parts, FlagStrings.Filter, ref i);
if (!string.IsNullOrEmpty(stringValue))
FilterValue = stringValue;
// Input File
stringValue = ProcessStringParameter(parts, FlagStrings.InputFile, Flag.InputFile, ref i);
if (string.Equals(stringValue, string.Empty))
return false;
else if (stringValue != null)
stringValue = ProcessStringParameter(parts, FlagStrings.InputFile, ref i);
if (!string.IsNullOrEmpty(stringValue))
InputFileValue = stringValue;
// Output File
stringValue = ProcessStringParameter(parts, FlagStrings.OutputFile, Flag.OutputFile, ref i);
if (string.Equals(stringValue, string.Empty))
return false;
else if (stringValue != null)
stringValue = ProcessStringParameter(parts, FlagStrings.OutputFile, ref i);
if (!string.IsNullOrEmpty(stringValue))
OutputFileValue = stringValue;
#endregion
@@ -406,212 +397,5 @@ namespace MPF.DD
}
#endregion
#region Private Extra Methods
/// <summary>
/// Get the list of commands that use a given flag
/// </summary>
/// <param name="flag">Flag value to get commands for</param>
/// <returns>List of Commands, if possible</returns>
private static List<Command> GetSupportedCommands(Flag flag)
{
var commands = new List<Command>();
switch (flag)
{
#region Boolean flags
case Flag.Progress:
commands.Add(Command.NONE);
break;
case Flag.Size:
commands.Add(Command.NONE);
break;
#endregion
#region Int64 flags
case Flag.BlockSize:
commands.Add(Command.NONE);
break;
case Flag.Count:
commands.Add(Command.NONE);
break;
case Flag.Seek:
commands.Add(Command.NONE);
break;
case Flag.Skip:
commands.Add(Command.NONE);
break;
#endregion
#region String flags
case Flag.Filter:
commands.Add(Command.NONE);
break;
case Flag.InputFile:
commands.Add(Command.NONE);
break;
case Flag.OutputFile:
commands.Add(Command.NONE);
break;
#endregion
case Flag.NONE:
default:
return commands;
}
return commands;
}
#endregion
#region Process Parameter Helpers
/// <summary>
/// Process a boolean parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="flagString">Flag string to check</param>
/// <param name="flag">Flag value corresponding to the flag</param>
/// <param name="i">Reference to the position in the parts</param>
/// <returns>True if the parameter was processed successfully or skipped, false otherwise</returns>
private bool ProcessBooleanParameter(List<string> parts, string flagString, Flag flag, ref int i)
{
if (parts == null)
return false;
if (parts[i] == flagString)
{
if (!GetSupportedCommands(flag).Contains(BaseCommand))
return false;
this[flag] = true;
}
return true;
}
/// <summary>
/// Process an Int64 parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="flagString">Flag string to check</param>
/// <param name="flag">Flag value corresponding to the flag</param>
/// <param name="i">Reference to the position in the parts</param>
/// <returns>Int64 value if success, Int64.MinValue if skipped, null on error/returns>
private long? ProcessInt64Parameter(List<string> parts, string flagString, Flag flag, ref int i)
{
if (parts == null)
return null;
if (parts[i].StartsWith(flagString))
{
if (!GetSupportedCommands(flag).Contains(BaseCommand))
return null;
string[] commandParts = parts[i].Split('=');
if (commandParts.Length != 2)
return null;
string valuePart = commandParts[1];
long factor = 1;
// Characters
if (valuePart.EndsWith("c", StringComparison.Ordinal))
{
factor = 1;
valuePart.TrimEnd('c');
}
// Words
else if (valuePart.EndsWith("w", StringComparison.Ordinal))
{
factor = 2;
valuePart.TrimEnd('w');
}
// Double Words
else if (valuePart.EndsWith("d", StringComparison.Ordinal))
{
factor = 4;
valuePart.TrimEnd('d');
}
// Quad Words
else if (valuePart.EndsWith("q", StringComparison.Ordinal))
{
factor = 8;
valuePart.TrimEnd('q');
}
// Kilobytes
else if (valuePart.EndsWith("k", StringComparison.Ordinal))
{
factor = 1024;
valuePart.TrimEnd('k');
}
// Megabytes
else if (valuePart.EndsWith("M", StringComparison.Ordinal))
{
factor = 1024 * 1024;
valuePart.TrimEnd('M');
}
// Gigabytes
else if (valuePart.EndsWith("G", StringComparison.Ordinal))
{
factor = 1024 * 1024 * 1024;
valuePart.TrimEnd('G');
}
if (!IsValidInt64(valuePart))
return null;
this[flag] = true;
return long.Parse(valuePart) * factor;
}
return Int64.MinValue;
}
/// <summary>
/// Process a string parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="flagString">Flag string to check</param>
/// <param name="flag">Flag value corresponding to the flag</param>
/// <param name="i">Reference to the position in the parts</param>
/// <returns>String value if possible, string.Empty on missing, null on error</returns>
private string ProcessStringParameter(List<string> parts, string flagString, Flag flag, ref int i)
{
if (parts == null)
return null;
if (parts[i] == flagString)
{
if (!GetSupportedCommands(flag).Contains(BaseCommand))
return null;
string[] commandParts = parts[i].Split('=');
if (commandParts.Length != 2)
return null;
string valuePart = commandParts[1];
this[flag] = true;
return valuePart.Trim('"');
}
return string.Empty;
}
#endregion
}
}

View File

@@ -9,6 +9,7 @@ using System.Threading.Tasks;
using BurnOutSharp.ProtectionType;
using Compress.ThreadReaders;
using MPF.Hashing;
using MPF.Utilities;
namespace MPF.Data
{
@@ -22,18 +23,53 @@ namespace MPF.Data
/// <param name="message">String value to report</param>
public EventHandler<string> ReportStatus;
/// <summary>
/// Event handler for data returned from a process
/// </summary>
private void OutputToLog(object proc, DataReceivedEventArgs args)
{
ReportStatus.Invoke(this, args.Data);
}
#endregion
#region Generic Dumping Information
/// <summary>
/// Base command to run
/// </summary>
public string BaseCommand { get; set; }
/// <summary>
/// Set of flags to pass to the executable
/// </summary>
protected Dictionary<string, bool?> flags = new Dictionary<string, bool?>();
protected internal IEnumerable<string> Keys => flags.Keys;
/// <summary>
/// Safe access to currently set flags
/// </summary>
public bool? this[string key]
{
get
{
if (flags.ContainsKey(key))
return flags[key];
return null;
}
set
{
flags[key] = value;
}
}
/// <summary>
/// Process to track external program
/// </summary>
private Process process;
#endregion
#region Virtual Dumping Information
/// <summary>
/// Command to flag support mappings
/// </summary>
public Dictionary<string, List<string>> CommandSupport => GetCommandSupport();
/// <summary>
/// Input path for operations
/// </summary>
@@ -50,11 +86,6 @@ namespace MPF.Data
/// </summary>
public virtual int? Speed { get; set; } = null;
/// <summary>
/// Process to track external program
/// </summary>
private Process process;
#endregion
#region Metadata
@@ -114,8 +145,9 @@ namespace MPF.Data
/// Validate if all required output files exist
/// </summary>
/// <param name="basePath">Base filename and path to use for checking</param>
/// <param name="preCheck">True if this is a check done before a dump, false if done after</param>
/// <returns>Tuple of true if all required files exist, false otherwise and a list representing missing files</returns>
public abstract (bool, List<string>) CheckAllOutputFilesExist(string basePath);
public abstract (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck);
/// <summary>
/// Generate a SubmissionInfo for the output files
@@ -130,6 +162,12 @@ namespace MPF.Data
#region Virtual Methods
/// <summary>
/// Get all commands mapped to the supported flags
/// </summary>
/// <returns>Mappings from command to supported flags</returns>
public virtual Dictionary<string, List<string>> GetCommandSupport() => null;
/// <summary>
/// Blindly generate a parameter string based on the inputs
/// </summary>
@@ -162,6 +200,22 @@ namespace MPF.Data
/// <returns>True if it's a dumping command, false otherwise</returns>
public virtual bool IsDumpingCommand() => true;
/// <summary>
/// Gets if the flag is supported by the current command
/// </summary>
/// <param name="flag">Flag value to check</param>
/// <returns>True if the flag value is supported, false otherwise</returns>
public virtual bool IsFlagSupported(string flag)
{
if (CommandSupport == null)
return false;
if (this.BaseCommand == null)
return false;
if (!CommandSupport.ContainsKey(this.BaseCommand))
return false;
return CommandSupport[this.BaseCommand].Contains(flag);
}
/// <summary>
/// Returns if the current Parameter object is valid
/// </summary>
@@ -212,22 +266,15 @@ namespace MPF.Data
// Create the new process
process = new Process() { StartInfo = startInfo };
// Add event handlers, if necessary
if (!separateWindow)
{
process.OutputDataReceived += OutputToLog;
process.ErrorDataReceived += OutputToLog;
}
// Start the process
process.Start();
// Begin reading the outputs, if necessary
// Start processing tasks, if necessary
if (!separateWindow)
{
process.BeginOutputReadLine();
process.BeginErrorReadLine();
Logging.OutputToLog(process.StandardOutput, this, ReportStatus);
Logging.OutputToLog(process.StandardError, this, ReportStatus);
}
process.WaitForExit();
@@ -304,19 +351,6 @@ namespace MPF.Data
return string.Join("\n", File.ReadAllLines(filename));
}
/// <summary>
/// Returns whether a string is a flag (starts with '/')
/// </summary>
/// <param name="parameter">String value to check</param>
/// <returns>True if it's a flag, false otherwise</returns>
protected static bool IsFlag(string parameter)
{
if (parameter.Trim('\"').StartsWith("/"))
return true;
return false;
}
/// <summary>
/// Returns whether a string is a valid drive letter
/// </summary>
@@ -349,7 +383,8 @@ namespace MPF.Data
/// <returns>True if it's a valid byte, false otherwise</returns>
protected static bool IsValidInt8(string parameter, sbyte lowerBound = -1, sbyte upperBound = -1)
{
if (!sbyte.TryParse(parameter, out sbyte temp))
(string value, long _) = ExtractFactorFromValue(parameter);
if (!sbyte.TryParse(value, out sbyte temp))
return false;
else if (lowerBound != -1 && temp < lowerBound)
return false;
@@ -368,7 +403,8 @@ namespace MPF.Data
/// <returns>True if it's a valid Int16, false otherwise</returns>
protected static bool IsValidInt16(string parameter, short lowerBound = -1, short upperBound = -1)
{
if (!short.TryParse(parameter, out short temp))
(string value, long _) = ExtractFactorFromValue(parameter);
if (!short.TryParse(value, out short temp))
return false;
else if (lowerBound != -1 && temp < lowerBound)
return false;
@@ -387,7 +423,8 @@ namespace MPF.Data
/// <returns>True if it's a valid Int32, false otherwise</returns>
protected static bool IsValidInt32(string parameter, int lowerBound = -1, int upperBound = -1)
{
if (!int.TryParse(parameter, out int temp))
(string value, long _) = ExtractFactorFromValue(parameter);
if (!int.TryParse(value, out int temp))
return false;
else if (lowerBound != -1 && temp < lowerBound)
return false;
@@ -406,7 +443,8 @@ namespace MPF.Data
/// <returns>True if it's a valid Int64, false otherwise</returns>
protected static bool IsValidInt64(string parameter, long lowerBound = -1, long upperBound = -1)
{
if (!long.TryParse(parameter, out long temp))
(string value, long _) = ExtractFactorFromValue(parameter);
if (!long.TryParse(value, out long temp))
return false;
else if (lowerBound != -1 && temp < lowerBound)
return false;
@@ -416,6 +454,571 @@ namespace MPF.Data
return true;
}
/// <summary>
/// Process a flag parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="flagString">Flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <returns>True if the parameter was processed successfully or skipped, false otherwise</returns>
protected bool ProcessFlagParameter(List<string> parts, string flagString, ref int i)
{
return ProcessFlagParameter(parts, null, flagString, ref i);
}
/// <summary>
/// Process a flag parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="shortFlagString">Short flag string, if available</param>
/// <param name="longFlagString">Long flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <returns>True if the parameter was processed successfully or skipped, false otherwise</returns>
protected bool ProcessFlagParameter(List<string> parts, string shortFlagString, string longFlagString, ref int i)
{
if (parts == null)
return false;
if (parts[i] == shortFlagString || parts[i] == longFlagString)
{
if (!IsFlagSupported(longFlagString))
return false;
this[longFlagString] = true;
}
return true;
}
/// <summary>
/// Process a boolean parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="flagString">Flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>True if the parameter was processed successfully or skipped, false otherwise</returns>
protected bool ProcessBooleanParameter(List<string> parts, string flagString, ref int i, bool missingAllowed = false)
{
return ProcessBooleanParameter(parts, null, flagString, ref i, missingAllowed);
}
/// <summary>
/// Process a boolean parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="shortFlagString">Short flag string, if available</param>
/// <param name="longFlagString">Long flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>True if the parameter was processed successfully or skipped, false otherwise</returns>
protected bool ProcessBooleanParameter(List<string> parts, string shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
{
if (parts == null)
return false;
if (parts[i] == shortFlagString || parts[i] == longFlagString)
{
if (!IsFlagSupported(longFlagString))
{
return false;
}
else if (!DoesExist(parts, i + 1))
{
if (missingAllowed)
{
this[longFlagString] = true;
return true;
}
else
{
return false;
}
}
else if (IsFlagSupported(parts[i + 1]))
{
if (missingAllowed)
{
this[longFlagString] = true;
return true;
}
else
{
return false;
}
}
else if (!IsValidBool(parts[i + 1]))
{
if (missingAllowed)
{
this[longFlagString] = true;
return true;
}
else
{
return false;
}
}
this[longFlagString] = bool.Parse(parts[i + 1]);
i++;
}
return true;
}
/// <summary>
/// Process a sbyte parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="flagString">Flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>SByte value if success, SByte.MinValue if skipped, null on error/returns>
protected sbyte? ProcessInt8Parameter(List<string> parts, string flagString, ref int i, bool missingAllowed = false)
{
return ProcessInt8Parameter(parts, null, flagString, ref i, missingAllowed);
}
/// <summary>
/// Process an sbyte parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="shortFlagString">Short flag string, if available</param>
/// <param name="longFlagString">Long flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>SByte value if success, SByte.MinValue if skipped, null on error/returns>
protected sbyte? ProcessInt8Parameter(List<string> parts, string shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
{
if (parts == null)
return null;
if (parts[i] == shortFlagString || parts[i] == longFlagString)
{
if (!IsFlagSupported(longFlagString))
{
return null;
}
else if (!DoesExist(parts, i + 1))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
else if (IsFlagSupported(parts[i + 1]))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
else if (!IsValidInt8(parts[i + 1]))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
this[longFlagString] = true;
i++;
(string value, long factor) = ExtractFactorFromValue(parts[i]);
return (sbyte)(sbyte.Parse(value) * factor);
}
else if (parts[i].StartsWith(shortFlagString + "=") || parts[i].StartsWith(longFlagString + "="))
{
if (!IsFlagSupported(longFlagString))
return null;
string[] commandParts = parts[i].Split('=');
if (commandParts.Length != 2)
return null;
string valuePart = commandParts[1];
(string value, long factor) = ExtractFactorFromValue(valuePart);
return (sbyte)(sbyte.Parse(value) * factor);
}
return SByte.MinValue;
}
/// <summary>
/// Process an Int16 parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="flagString">Flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>Int16 value if success, Int16.MinValue if skipped, null on error/returns>
protected short? ProcessInt16Parameter(List<string> parts, string flagString, ref int i, bool missingAllowed = false)
{
return ProcessInt16Parameter(parts, null, flagString, ref i, missingAllowed);
}
/// <summary>
/// Process an Int16 parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="shortFlagString">Short flag string, if available</param>
/// <param name="longFlagString">Long flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>Int16 value if success, Int16.MinValue if skipped, null on error/returns>
protected short? ProcessInt16Parameter(List<string> parts, string shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
{
if (parts == null)
return null;
if (parts[i] == shortFlagString || parts[i] == longFlagString)
{
if (!IsFlagSupported(longFlagString))
{
return null;
}
else if (!DoesExist(parts, i + 1))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
else if (IsFlagSupported(parts[i + 1]))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
else if (!IsValidInt16(parts[i + 1]))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
this[longFlagString] = true;
i++;
(string value, long factor) = ExtractFactorFromValue(parts[i]);
return (short)(short.Parse(value) * factor);
}
else if (parts[i].StartsWith(shortFlagString + "=") || parts[i].StartsWith(longFlagString + "="))
{
if (!IsFlagSupported(longFlagString))
return null;
string[] commandParts = parts[i].Split('=');
if (commandParts.Length != 2)
return null;
string valuePart = commandParts[1];
(string value, long factor) = ExtractFactorFromValue(valuePart);
return (short)(short.Parse(value) * factor);
}
return Int16.MinValue;
}
/// <summary>
/// Process an Int32 parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="flagString">Flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>Int32 value if success, Int32.MinValue if skipped, null on error/returns>
protected int? ProcessInt32Parameter(List<string> parts, string flagString, ref int i, bool missingAllowed = false)
{
return ProcessInt32Parameter(parts, null, flagString, ref i, missingAllowed);
}
/// <summary>
/// Process an Int32 parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="shortFlagString">Short flag string, if available</param>
/// <param name="longFlagString">Long flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>Int32 value if success, Int32.MinValue if skipped, null on error/returns>
protected int? ProcessInt32Parameter(List<string> parts, string shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
{
if (parts == null)
return null;
if (parts[i] == shortFlagString || parts[i] == longFlagString)
{
if (!IsFlagSupported(longFlagString))
{
return null;
}
else if (!DoesExist(parts, i + 1))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
else if (IsFlagSupported(parts[i + 1]))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
else if (!IsValidInt32(parts[i + 1]))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
this[longFlagString] = true;
i++;
(string value, long factor) = ExtractFactorFromValue(parts[i]);
return (int)(int.Parse(value) * factor);
}
else if (parts[i].StartsWith(shortFlagString + "=") || parts[i].StartsWith(longFlagString + "="))
{
if (!IsFlagSupported(longFlagString))
return null;
string[] commandParts = parts[i].Split('=');
if (commandParts.Length != 2)
return null;
string valuePart = commandParts[1];
(string value, long factor) = ExtractFactorFromValue(valuePart);
return (int)(int.Parse(value) * factor);
}
return Int32.MinValue;
}
/// <summary>
/// Process an Int64 parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="flagString">Flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>Int64 value if success, Int64.MinValue if skipped, null on error/returns>
protected long? ProcessInt64Parameter(List<string> parts, string flagString, ref int i, bool missingAllowed = false)
{
return ProcessInt64Parameter(parts, null, flagString, ref i, missingAllowed);
}
/// <summary>
/// Process an Int64 parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="shortFlagString">Short flag string, if available</param>
/// <param name="longFlagString">Long flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>Int64 value if success, Int64.MinValue if skipped, null on error/returns>
protected long? ProcessInt64Parameter(List<string> parts, string shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
{
if (parts == null)
return null;
if (parts[i] == shortFlagString || parts[i] == longFlagString)
{
if (!IsFlagSupported(longFlagString))
{
return null;
}
else if (!DoesExist(parts, i + 1))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
else if (IsFlagSupported(parts[i + 1]))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
else if (!IsValidInt64(parts[i + 1]))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
this[longFlagString] = true;
i++;
(string value, long factor) = ExtractFactorFromValue(parts[i]);
return long.Parse(value) * factor;
}
else if (parts[i].StartsWith(shortFlagString + "=") || parts[i].StartsWith(longFlagString + "="))
{
if (!IsFlagSupported(longFlagString))
return null;
string[] commandParts = parts[i].Split('=');
if (commandParts.Length != 2)
return null;
string valuePart = commandParts[1];
(string value, long factor) = ExtractFactorFromValue(valuePart);
return long.Parse(value) * factor;
}
return Int64.MinValue;
}
/// <summary>
/// Process an Int64 parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="flagString">Flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>String value if possible, string.Empty on missing, null on error</returns>
protected string ProcessStringParameter(List<string> parts, string flagString, ref int i, bool missingAllowed = false)
{
return ProcessStringParameter(parts, null, flagString, ref i, missingAllowed);
}
/// <summary>
/// Process a string parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="shortFlagString">Short flag string, if available</param>
/// <param name="longFlagString">Long flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>String value if possible, string.Empty on missing, null on error</returns>
protected string ProcessStringParameter(List<string> parts, string shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
{
if (parts == null)
return null;
if (parts[i] == shortFlagString || parts[i] == longFlagString)
{
if (!IsFlagSupported(longFlagString))
{
return null;
}
else if (!DoesExist(parts, i + 1))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
else if (IsFlagSupported(parts[i + 1]))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
else if (string.IsNullOrWhiteSpace(parts[i + 1]))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
this[longFlagString] = true;
i++;
return parts[i].Trim('"');
}
else if (parts[i].StartsWith(shortFlagString + "=") || parts[i].StartsWith(longFlagString + "="))
{
if (!IsFlagSupported(longFlagString))
return null;
string[] commandParts = parts[i].Split('=');
if (commandParts.Length != 2)
return null;
string valuePart = commandParts[1];
this[longFlagString] = true;
return valuePart.Trim('"');
}
return string.Empty;
}
/// <summary>
/// Get yhe trimmed value and multiplication factor from a value
/// </summary>
/// <param name="value">String value to treat as suffixed number</param>
/// <returns>Trimmed value and multiplication factor</returns>
private static (string trimmed, long factor) ExtractFactorFromValue(string value)
{
value = value.Trim('"');
long factor = 1;
// Characters
if (value.EndsWith("c", StringComparison.Ordinal))
{
factor = 1;
value = value.TrimEnd('c');
}
// Words
else if (value.EndsWith("w", StringComparison.Ordinal))
{
factor = 2;
value = value.TrimEnd('w');
}
// Double Words
else if (value.EndsWith("d", StringComparison.Ordinal))
{
factor = 4;
value = value.TrimEnd('d');
}
// Quad Words
else if (value.EndsWith("q", StringComparison.Ordinal))
{
factor = 8;
value = value.TrimEnd('q');
}
// Kilobytes
else if (value.EndsWith("k", StringComparison.Ordinal))
{
factor = 1024;
value = value.TrimEnd('k');
}
// Megabytes
else if (value.EndsWith("M", StringComparison.Ordinal))
{
factor = 1024 * 1024;
value = value.TrimEnd('M');
}
// Gigabytes
else if (value.EndsWith("G", StringComparison.Ordinal))
{
factor = 1024 * 1024 * 1024;
value = value.TrimEnd('G');
}
return (value, factor);
}
#endregion
#region Common Information Extraction
@@ -691,7 +1294,7 @@ namespace MPF.Data
var systemCnf = new IniFile(systemCnfPath);
if (systemCnf.ContainsKey("VER"))
return systemCnf["VER"];
// If "VER" can't be found, we can't do much
return null;
}

View File

@@ -176,9 +176,9 @@ namespace MPF.Data
/// <summary>
/// Ensures that all required output files have been created
/// </summary>
/// <param name="progress">Optional result progress callback</param>
/// <param name="preCheck">True if this is a check done before a dump, false if done after</param>
/// <returns>Tuple of true if all required files exist, false otherwise and a list representing missing files</returns>
public (bool, List<string>) FoundAllFiles()
public (bool, List<string>) FoundAllFiles(bool preCheck)
{
// First, sanitized the output filename to strip off any potential extension
string outputFilename = Path.GetFileNameWithoutExtension(OutputFilename);
@@ -187,7 +187,7 @@ namespace MPF.Data
string basePath = Path.Combine(OutputDirectory, outputFilename);
// Finally, let the parameters say if all files exist
return Parameters.CheckAllOutputFilesExist(basePath);
return Parameters.CheckAllOutputFilesExist(basePath, preCheck);
}
/// <summary>
@@ -320,7 +320,7 @@ namespace MPF.Data
var parameters = new DiscImageCreator.Parameters(string.Empty)
{
BaseCommand = DiscImageCreator.Command.Eject,
BaseCommand = DiscImageCreator.CommandStrings.Eject,
DriveLetter = Drive.Letter.ToString(),
ExecutablePath = Options.DiscImageCreatorPath,
};
@@ -350,7 +350,7 @@ namespace MPF.Data
DiscImageCreator.Parameters parameters = new DiscImageCreator.Parameters(string.Empty)
{
BaseCommand = DiscImageCreator.Command.Reset,
BaseCommand = DiscImageCreator.CommandStrings.Reset,
DriveLetter = Drive.Letter.ToString(),
ExecutablePath = Options.DiscImageCreatorPath,
};
@@ -412,7 +412,7 @@ namespace MPF.Data
resultProgress?.Report(Result.Success("Gathering submission information... please wait!"));
// Check to make sure that the output had all the correct files
(bool foundFiles, List<string> missingFiles) = FoundAllFiles();
(bool foundFiles, List<string> missingFiles) = FoundAllFiles(false);
if (!foundFiles)
{
resultProgress.Report(Result.Failure($"There were files missing from the output:\n{string.Join("\n", missingFiles)}"));
@@ -568,7 +568,7 @@ namespace MPF.Data
string outputFilename = Path.GetFileNameWithoutExtension(OutputFilename);
// Check that all of the relevant files are there
(bool foundFiles, List<string> missingFiles) = FoundAllFiles();
(bool foundFiles, List<string> missingFiles) = FoundAllFiles(false);
if (!foundFiles)
{
resultProgress.Report(Result.Failure($"There were files missing from the output:\n{string.Join("\n", missingFiles)}"));
@@ -603,7 +603,7 @@ namespace MPF.Data
};
// Get specific tool output handling
Parameters.GenerateSubmissionInfo(info, combinedBase, this.Drive, Options.OutputSubmissionJSON);
Parameters.GenerateSubmissionInfo(info, combinedBase, this.Drive, Options.IncludeArtifacts);
// Get a list of matching IDs for each line in the DAT
if (!string.IsNullOrEmpty(info.TracksAndWriteOffsets.ClrMameProData) && Options.HasRedumpLogin)
@@ -968,6 +968,13 @@ namespace MPF.Data
try
{
// Sony-printed discs have layers in the opposite order
var system = info.CommonDiscInfo.System;
bool reverseOrder = (system == KnownSystem.SonyPlayStation2
|| system == KnownSystem.SonyPlayStation3
|| system == KnownSystem.SonyPlayStation4
|| system == KnownSystem.SonyPlayStation5);
// Common Disc Info section
List<string> output = new List<string> { "Common Disc Info:" };
AddIfExists(output, Template.TitleField, info.CommonDiscInfo.Title, 1);
@@ -995,9 +1002,9 @@ namespace MPF.Data
// If we have a triple-layer disc
if (info.SizeAndChecksums.Layerbreak3 != default)
{
AddIfExists(output, "Layer 0 (Inner) " + Template.MasteringRingField, info.CommonDiscInfo.Layer0MasteringRing, 2);
AddIfExists(output, "Layer 0 (Inner) " + Template.MasteringSIDField, info.CommonDiscInfo.Layer0MasteringSID, 2);
AddIfExists(output, "Layer 0 (Inner) " + Template.ToolstampField, info.CommonDiscInfo.Layer0ToolstampMasteringCode, 2);
AddIfExists(output, (reverseOrder ? "Layer 0 (Outer) " : "Layer 0 (Inner) ") + Template.MasteringRingField, info.CommonDiscInfo.Layer0MasteringRing, 2);
AddIfExists(output, (reverseOrder ? "Layer 0 (Outer) " : "Layer 0 (Inner) ") + Template.MasteringSIDField, info.CommonDiscInfo.Layer0MasteringSID, 2);
AddIfExists(output, (reverseOrder ? "Layer 0 (Outer) " : "Layer 0 (Inner) ") + Template.ToolstampField, info.CommonDiscInfo.Layer0ToolstampMasteringCode, 2);
AddIfExists(output, "Data Side " + Template.MouldSIDField, info.CommonDiscInfo.Layer0MouldSID, 2);
AddIfExists(output, "Data Side " + Template.AdditionalMouldField, info.CommonDiscInfo.Layer0AdditionalMould, 2);
@@ -1011,16 +1018,16 @@ namespace MPF.Data
AddIfExists(output, "Layer 2 " + Template.MasteringSIDField, info.CommonDiscInfo.Layer2MasteringSID, 2);
AddIfExists(output, "Layer 2 " + Template.ToolstampField, info.CommonDiscInfo.Layer2ToolstampMasteringCode, 2);
AddIfExists(output, "Layer 3 (Outer) " + Template.MasteringRingField, info.CommonDiscInfo.Layer3MasteringRing, 2);
AddIfExists(output, "Layer 3 (Outer) " + Template.MasteringSIDField, info.CommonDiscInfo.Layer3MasteringSID, 2);
AddIfExists(output, "Layer 3 (Outer) " + Template.ToolstampField, info.CommonDiscInfo.Layer3ToolstampMasteringCode, 2);
AddIfExists(output, (reverseOrder ? "Layer 3 (Inner) " : "Layer 3 (Outer) ") + Template.MasteringRingField, info.CommonDiscInfo.Layer3MasteringRing, 2);
AddIfExists(output, (reverseOrder ? "Layer 3 (Inner) " : "Layer 3 (Outer) ") + Template.MasteringSIDField, info.CommonDiscInfo.Layer3MasteringSID, 2);
AddIfExists(output, (reverseOrder ? "Layer 3 (Inner) " : "Layer 3 (Outer) ") + Template.ToolstampField, info.CommonDiscInfo.Layer3ToolstampMasteringCode, 2);
}
// If we have a triple-layer disc
else if (info.SizeAndChecksums.Layerbreak2 != default)
{
AddIfExists(output, "Layer 0 (Inner) " + Template.MasteringRingField, info.CommonDiscInfo.Layer0MasteringRing, 2);
AddIfExists(output, "Layer 0 (Inner) " + Template.MasteringSIDField, info.CommonDiscInfo.Layer0MasteringSID, 2);
AddIfExists(output, "Layer 0 (Inner) " + Template.ToolstampField, info.CommonDiscInfo.Layer0ToolstampMasteringCode, 2);
AddIfExists(output, (reverseOrder ? "Layer 0 (Outer) " : "Layer 0 (Inner) ") + Template.MasteringRingField, info.CommonDiscInfo.Layer0MasteringRing, 2);
AddIfExists(output, (reverseOrder ? "Layer 0 (Outer) " : "Layer 0 (Inner) ") + Template.MasteringSIDField, info.CommonDiscInfo.Layer0MasteringSID, 2);
AddIfExists(output, (reverseOrder ? "Layer 0 (Outer) " : "Layer 0 (Inner) ") + Template.ToolstampField, info.CommonDiscInfo.Layer0ToolstampMasteringCode, 2);
AddIfExists(output, "Data Side " + Template.MouldSIDField, info.CommonDiscInfo.Layer0MouldSID, 2);
AddIfExists(output, "Data Side " + Template.AdditionalMouldField, info.CommonDiscInfo.Layer0AdditionalMould, 2);
@@ -1030,22 +1037,22 @@ namespace MPF.Data
AddIfExists(output, "Label Side " + Template.MouldSIDField, info.CommonDiscInfo.Layer1MouldSID, 2);
AddIfExists(output, "Label Side " + Template.AdditionalMouldField, info.CommonDiscInfo.Layer1AdditionalMould, 2);
AddIfExists(output, "Layer 2 (Outer) " + Template.MasteringRingField, info.CommonDiscInfo.Layer2MasteringRing, 2);
AddIfExists(output, "Layer 2 (Outer) " + Template.MasteringSIDField, info.CommonDiscInfo.Layer2MasteringSID, 2);
AddIfExists(output, "Layer 2 (Outer) " + Template.ToolstampField, info.CommonDiscInfo.Layer2ToolstampMasteringCode, 2);
AddIfExists(output, (reverseOrder ? "Layer 2 (Inner) " : "Layer 2 (Outer) ") + Template.MasteringRingField, info.CommonDiscInfo.Layer2MasteringRing, 2);
AddIfExists(output, (reverseOrder ? "Layer 2 (Inner) " : "Layer 2 (Outer) ") + Template.MasteringSIDField, info.CommonDiscInfo.Layer2MasteringSID, 2);
AddIfExists(output, (reverseOrder ? "Layer 2 (Inner) " : "Layer 2 (Outer) ") + Template.ToolstampField, info.CommonDiscInfo.Layer2ToolstampMasteringCode, 2);
}
// If we have a dual-layer disc
else if (info.SizeAndChecksums.Layerbreak != default)
{
AddIfExists(output, "Layer 0 (Inner) " + Template.MasteringRingField, info.CommonDiscInfo.Layer0MasteringRing, 2);
AddIfExists(output, "Layer 0 (Inner) " + Template.MasteringSIDField, info.CommonDiscInfo.Layer0MasteringSID, 2);
AddIfExists(output, "Layer 0 (Inner) " + Template.ToolstampField, info.CommonDiscInfo.Layer0ToolstampMasteringCode, 2);
AddIfExists(output, (reverseOrder ? "Layer 0 (Outer) " : "Layer 0 (Inner) ") + Template.MasteringRingField, info.CommonDiscInfo.Layer0MasteringRing, 2);
AddIfExists(output, (reverseOrder ? "Layer 0 (Outer) " : "Layer 0 (Inner) ") + Template.MasteringSIDField, info.CommonDiscInfo.Layer0MasteringSID, 2);
AddIfExists(output, (reverseOrder ? "Layer 0 (Outer) " : "Layer 0 (Inner) ") + Template.ToolstampField, info.CommonDiscInfo.Layer0ToolstampMasteringCode, 2);
AddIfExists(output, "Data Side " + Template.MouldSIDField, info.CommonDiscInfo.Layer0MouldSID, 2);
AddIfExists(output, "Data Side " + Template.AdditionalMouldField, info.CommonDiscInfo.Layer0AdditionalMould, 2);
AddIfExists(output, "Layer 1 (Outer) " + Template.MasteringRingField, info.CommonDiscInfo.Layer1MasteringRing, 2);
AddIfExists(output, "Layer 1 (Outer) " + Template.MasteringSIDField, info.CommonDiscInfo.Layer1MasteringSID, 2);
AddIfExists(output, "Layer 1 (Outer) " + Template.ToolstampField, info.CommonDiscInfo.Layer1ToolstampMasteringCode, 2);
AddIfExists(output, (reverseOrder ? "Layer 1 (Inner) " : "Layer 1 (Outer) ") + Template.MasteringRingField, info.CommonDiscInfo.Layer1MasteringRing, 2);
AddIfExists(output, (reverseOrder ? "Layer 1 (Inner) " : "Layer 1 (Outer) ") + Template.MasteringSIDField, info.CommonDiscInfo.Layer1MasteringSID, 2);
AddIfExists(output, (reverseOrder ? "Layer 1 (Inner) " : "Layer 1 (Outer) ") + Template.ToolstampField, info.CommonDiscInfo.Layer1ToolstampMasteringCode, 2);
AddIfExists(output, "Label Side " + Template.MouldSIDField, info.CommonDiscInfo.Layer1MouldSID, 2);
AddIfExists(output, "Label Side " + Template.AdditionalMouldField, info.CommonDiscInfo.Layer1AdditionalMould, 2);
}
@@ -1356,7 +1363,7 @@ namespace MPF.Data
{
if (Options.ScanForProtection)
{
(bool success, string output) = await Validators.RunProtectionScanOnPath($"{Drive.Letter}:\\", progress);
(bool success, string output) = await Validators.RunProtectionScanOnPath($"{Drive.Letter}:\\", this.Options, progress);
if (success)
return output;
else

View File

@@ -60,6 +60,15 @@ namespace MPF.Data
#region UI Defaults
/// <summary>
/// Enable dark mode for UI elements
/// </summary>
public bool EnableDarkMode
{
get { return GetBooleanSetting(_settings, "EnableDarkMode", false); }
set { _settings["EnableDarkMode"] = value.ToString(); }
}
/// <summary>
/// Default output path for dumps
/// </summary>
@@ -287,6 +296,15 @@ namespace MPF.Data
set { _settings["OutputSubmissionJSON"] = value.ToString(); }
}
/// <summary>
/// Include log files in serialized JSON data
/// </summary>
public bool IncludeArtifacts
{
get { return GetBooleanSetting(_settings, "IncludeArtifacts", false); }
set { _settings["IncludeArtifacts"] = value.ToString(); }
}
/// <summary>
/// Compress output log files to reduce space
/// </summary>
@@ -320,6 +338,46 @@ namespace MPF.Data
#endregion
#region Protection Scanning Options
/// <summary>
/// Scan archive contents during protection scanning
/// </summary>
public bool ScanArchivesForProtection
{
get { return GetBooleanSetting(_settings, "ScanArchivesForProtection", true); }
set { _settings["ScanArchivesForProtection"] = value.ToString(); }
}
/// <summary>
/// Scan for executable packers during protection scanning
/// </summary>
public bool ScanPackersForProtection
{
get { return GetBooleanSetting(_settings, "ScanPackersForProtection", false); }
set { _settings["ScanPackersForProtection"] = value.ToString(); }
}
/// <summary>
/// Force scanning all files for protection
/// </summary>
public bool ForceScanningForProtection
{
get { return GetBooleanSetting(_settings, "ForceScanningForProtection", false); }
set { _settings["ForceScanningForProtection"] = value.ToString(); }
}
/// <summary>
/// Include debug information with scan results
/// </summary>
public bool IncludeDebugProtectionInformation
{
get { return GetBooleanSetting(_settings, "IncludeDebugProtectionInformation", false); }
set { _settings["IncludeDebugProtectionInformation"] = value.ToString(); }
}
#endregion
#region Logging Options
/// <summary>

View File

@@ -62,6 +62,7 @@ namespace MPF.Data
if (this.TokenSource.IsCancellationRequested)
break;
Thread.Sleep(10);
continue;
}

View File

@@ -5,6 +5,7 @@ namespace MPF.DiscImageCreator
/// </summary>
public static class CommandStrings
{
public const string NONE = "";
public const string Audio = "audio";
public const string BluRay = "bd";
public const string Close = "close";

View File

@@ -11,31 +11,31 @@ namespace MPF.DiscImageCreator
/// </summary>
/// <param name="baseCommand">Command value to check</param>
/// <returns>KnownSystem if possible, null on error</returns>
public static KnownSystem? ToKnownSystem(Command baseCommand)
public static KnownSystem? ToKnownSystem(string baseCommand)
{
switch (baseCommand)
{
case Command.Audio:
case CommandStrings.Audio:
return KnownSystem.AudioCD;
case Command.CompactDisc:
case Command.Data:
case Command.DigitalVideoDisc:
case Command.Disk:
case Command.Floppy:
case Command.Tape:
case CommandStrings.CompactDisc:
case CommandStrings.Data:
case CommandStrings.DigitalVideoDisc:
case CommandStrings.Disk:
case CommandStrings.Floppy:
case CommandStrings.Tape:
return KnownSystem.IBMPCCompatible;
case Command.GDROM:
case Command.Swap:
case CommandStrings.GDROM:
case CommandStrings.Swap:
return KnownSystem.SegaDreamcast;
case Command.BluRay:
case CommandStrings.BluRay:
return KnownSystem.SonyPlayStation3;
case Command.SACD:
case CommandStrings.SACD:
return KnownSystem.SuperAudioCD;
case Command.XBOX:
case Command.XBOXSwap:
case CommandStrings.XBOX:
case CommandStrings.XBOXSwap:
return KnownSystem.MicrosoftXBOX;
case Command.XGD2Swap:
case Command.XGD3Swap:
case CommandStrings.XGD2Swap:
case CommandStrings.XGD3Swap:
return KnownSystem.MicrosoftXBOX360;
default:
return null;
@@ -48,33 +48,33 @@ namespace MPF.DiscImageCreator
/// <param name="baseCommand">Command value to check</param>
/// <returns>MediaType if possible, null on error</returns>
/// <remarks>This takes the "safe" route by assuming the larger of any given format</remarks>
public static MediaType? ToMediaType(Command baseCommand)
public static MediaType? ToMediaType(string baseCommand)
{
switch (baseCommand)
{
case Command.Audio:
case Command.CompactDisc:
case Command.Data:
case Command.SACD:
case CommandStrings.Audio:
case CommandStrings.CompactDisc:
case CommandStrings.Data:
case CommandStrings.SACD:
return MediaType.CDROM;
case Command.GDROM:
case Command.Swap:
case CommandStrings.GDROM:
case CommandStrings.Swap:
return MediaType.GDROM;
case Command.DigitalVideoDisc:
case Command.XBOX:
case Command.XBOXSwap:
case Command.XGD2Swap:
case Command.XGD3Swap:
case CommandStrings.DigitalVideoDisc:
case CommandStrings.XBOX:
case CommandStrings.XBOXSwap:
case CommandStrings.XGD2Swap:
case CommandStrings.XGD3Swap:
return MediaType.DVD;
case Command.BluRay:
case CommandStrings.BluRay:
return MediaType.BluRay;
// Non-optical
case Command.Floppy:
case CommandStrings.Floppy:
return MediaType.FloppyDisk;
case Command.Disk:
case CommandStrings.Disk:
return MediaType.HardDisk;
case Command.Tape:
case CommandStrings.Tape:
return MediaType.DataCartridge;
default:
return null;
@@ -120,222 +120,5 @@ namespace MPF.DiscImageCreator
}
#endregion
#region Convert to Long Name
/// <summary>
/// Get the string representation of the Command enum values
/// </summary>
/// <param name="command">Command value to convert</param>
/// <returns>String representing the value, if possible</returns>
public static string LongName(Command command)
{
switch (command)
{
case Command.Audio:
return CommandStrings.Audio;
case Command.BluRay:
return CommandStrings.BluRay;
case Command.Close:
return CommandStrings.Close;
case Command.CompactDisc:
return CommandStrings.CompactDisc;
case Command.Data:
return CommandStrings.Data;
case Command.DigitalVideoDisc:
return CommandStrings.DigitalVideoDisc;
case Command.Disk:
return CommandStrings.Disk;
case Command.DriveSpeed:
return CommandStrings.DriveSpeed;
case Command.Eject:
return CommandStrings.Eject;
case Command.Floppy:
return CommandStrings.Floppy;
case Command.GDROM:
return CommandStrings.GDROM;
case Command.MDS:
return CommandStrings.MDS;
case Command.Merge:
return CommandStrings.Merge;
case Command.Reset:
return CommandStrings.Reset;
case Command.SACD:
return CommandStrings.SACD;
case Command.Start:
return CommandStrings.Start;
case Command.Stop:
return CommandStrings.Stop;
case Command.Sub:
return CommandStrings.Sub;
case Command.Swap:
return CommandStrings.Swap;
case Command.Tape:
return CommandStrings.Tape;
case Command.XBOX:
return CommandStrings.XBOX;
case Command.XBOXSwap:
return CommandStrings.XBOXSwap;
case Command.XGD2Swap:
return CommandStrings.XGD2Swap;
case Command.XGD3Swap:
return CommandStrings.XGD3Swap;
case Command.NONE:
default:
return "";
}
}
/// <summary>
/// Get the string representation of the Flag enum values
/// </summary>
/// <param name="command">Flag value to convert</param>
/// <returns>String representing the value, if possible</returns>
public static string LongName(Flag flag)
{
switch (flag)
{
case Flag.AddOffset:
return FlagStrings.AddOffset;
case Flag.AMSF:
return FlagStrings.AMSF;
case Flag.AtariJaguar:
return FlagStrings.AtariJaguar;
case Flag.BEOpcode:
return FlagStrings.BEOpcode;
case Flag.C2Opcode:
return FlagStrings.C2Opcode;
case Flag.CopyrightManagementInformation:
return FlagStrings.CopyrightManagementInformation;
case Flag.D8Opcode:
return FlagStrings.D8Opcode;
case Flag.DisableBeep:
return FlagStrings.DisableBeep;
case Flag.ExtractMicroSoftCabFile:
return FlagStrings.ExtractMicroSoftCabFile;
case Flag.Fix:
return FlagStrings.Fix;
case Flag.ForceUnitAccess:
return FlagStrings.ForceUnitAccess;
case Flag.MultiSectorRead:
return FlagStrings.MultiSectorRead;
case Flag.MultiSession:
return FlagStrings.MultiSession;
case Flag.NoFixSubP:
return FlagStrings.NoFixSubP;
case Flag.NoFixSubQ:
return FlagStrings.NoFixSubQ;
case Flag.NoFixSubQLibCrypt:
return FlagStrings.NoFixSubQLibCrypt;
case Flag.NoFixSubRtoW:
return FlagStrings.NoFixSubRtoW;
case Flag.NoFixSubQSecuROM:
return FlagStrings.NoFixSubQSecuROM;
case Flag.NoSkipSS:
return FlagStrings.NoSkipSS;
case Flag.PadSector:
return FlagStrings.PadSector;
case Flag.Raw:
return FlagStrings.Raw;
case Flag.Resume:
return FlagStrings.Resume;
case Flag.Reverse:
return FlagStrings.Reverse;
case Flag.ScanAntiMod:
return FlagStrings.ScanAntiMod;
case Flag.ScanFileProtect:
return FlagStrings.ScanFileProtect;
case Flag.ScanSectorProtect:
return FlagStrings.ScanSectorProtect;
case Flag.SeventyFour:
return FlagStrings.SeventyFour;
case Flag.SkipSector:
return FlagStrings.SkipSector;
case Flag.SubchannelReadLevel:
return FlagStrings.SubchannelReadLevel;
case Flag.UseAnchorVolumeDescriptorPointer:
return FlagStrings.UseAnchorVolumeDescriptorPointer;
case Flag.VideoNow:
return FlagStrings.VideoNow;
case Flag.VideoNowColor:
return FlagStrings.VideoNowColor;
case Flag.VideoNowXP:
return FlagStrings.VideoNowXP;
case Flag.NONE:
default:
return "";
}
}
#endregion
#region Convert From String
/// <summary>
/// Get the Command enum value for a given string
/// </summary>
/// <param name="command">String value to convert</param>
/// <returns>Command represented by the string(s), if possible</returns>
public static Command StringToCommand(string command)
{
switch (command)
{
case CommandStrings.Audio:
return Command.Audio;
case CommandStrings.BluRay:
return Command.BluRay;
case CommandStrings.Close:
return Command.Close;
case CommandStrings.CompactDisc:
return Command.CompactDisc;
case CommandStrings.Data:
return Command.Data;
case CommandStrings.DigitalVideoDisc:
return Command.DigitalVideoDisc;
case CommandStrings.Disk:
return Command.Disk;
case CommandStrings.DriveSpeed:
return Command.DriveSpeed;
case CommandStrings.Eject:
return Command.Eject;
case CommandStrings.Floppy:
return Command.Floppy;
case CommandStrings.GDROM:
return Command.GDROM;
case CommandStrings.MDS:
return Command.MDS;
case CommandStrings.Merge:
return Command.Merge;
case CommandStrings.Reset:
return Command.Reset;
case CommandStrings.SACD:
return Command.SACD;
case CommandStrings.Start:
return Command.Start;
case CommandStrings.Stop:
return Command.Stop;
case CommandStrings.Sub:
return Command.Sub;
case CommandStrings.Swap:
return Command.Swap;
case CommandStrings.Tape:
return Command.Tape;
case CommandStrings.XBOX:
return Command.XBOX;
case CommandStrings.XBOXSwap:
return Command.XBOXSwap;
case CommandStrings.XGD2Swap:
return Command.XGD2Swap;
case CommandStrings.XGD3Swap:
return Command.XGD3Swap;
default:
return Command.NONE;
}
}
#endregion
}
}

View File

@@ -1,75 +0,0 @@
namespace MPF.DiscImageCreator
{
/// <summary>
/// Supported DiscImageCreator commands
/// </summary>
public enum Command : int
{
NONE = 0,
Audio,
BluRay,
Close,
CompactDisc,
Data,
DigitalVideoDisc,
Disk,
DriveSpeed,
Eject,
Floppy,
GDROM,
MDS,
Merge,
Reset,
SACD,
Start,
Stop,
Sub,
Swap,
Tape,
XBOX,
XBOXSwap,
XGD2Swap,
XGD3Swap,
}
/// <summary>
/// Supported DiscImageCreator flags
/// </summary>
public enum Flag : int
{
NONE = 0,
AddOffset,
AMSF,
AtariJaguar,
BEOpcode,
C2Opcode,
CopyrightManagementInformation,
D8Opcode,
DisableBeep,
ExtractMicroSoftCabFile,
Fix,
ForceUnitAccess,
MultiSectorRead,
MultiSession,
NoFixSubP,
NoFixSubQ,
NoFixSubQLibCrypt,
NoFixSubRtoW,
NoFixSubQSecuROM,
NoSkipSS,
PadSector,
Raw,
Resume,
Reverse,
ScanAntiMod,
ScanFileProtect,
ScanSectorProtect,
SeventyFour,
SkipSector,
SubchannelReadLevel,
UseAnchorVolumeDescriptorPointer,
VideoNow,
VideoNowColor,
VideoNowXP,
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,13 +2,14 @@
<PropertyGroup>
<TargetFrameworks>net48;netcoreapp3.1</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<Title>MPF Library</Title>
<AssemblyName>MPF.Library</AssemblyName>
<Description>Library code for MPF and MPF.Check</Description>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2021</Copyright>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<Version>2.0</Version>
<Version>2.1</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<FileVersion>$(Version)</FileVersion>
<IncludeSource>true</IncludeSource>
@@ -73,13 +74,16 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="1.6.1" GeneratePathProperty="true">
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="1.7.0" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
<PackageReference Include="System.Management" Version="6.0.0-preview.3.21201.4" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.0" />
<PackageReference Include="System.Management" Version="6.0.0-preview.6.21352.12" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>

View File

@@ -299,6 +299,7 @@ namespace MPF.Redump
if (request is HttpWebRequest webRequest)
{
webRequest.CookieContainer = m_container;
webRequest.Timeout = 5 * 1000;
}
return request;
@@ -336,16 +337,26 @@ namespace MPF.Redump
var loginPage = DownloadString(loginUrl);
string token = this.tokenRegex.Match(loginPage).Groups[1].Value;
// Encode the values
token = WebUtility.UrlEncode(token);
username = WebUtility.UrlEncode(username);
password = WebUtility.UrlEncode(password);
// Construct the login request
Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
Encoding = Encoding.UTF8;
var response = UploadString(loginUrl, $"form_sent=1&redirect_url=&csrf_token={token}&req_username={username}&req_password={password}&save_pass=0");
var response = UploadString(loginUrl, $"form_sent=1&redirect_url=&csrf_token={token}&req_username={username}&req_password={password}&save_pass=0&login=Login");
if (response.Contains("Incorrect username and/or password."))
if (response.Contains("Incorrect username and/or password"))
{
Console.WriteLine("Invalid credentials entered, continuing without logging in...");
return false;
}
else if (response.Contains("503 Service Unavailable"))
{
Console.WriteLine("Redump is not currently responding, continuing without logging in...");
return null;
}
// The user was able to be logged in
Console.WriteLine("Credentials accepted! Logged into Redump...");

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using MPF.Data;
using MPF.Utilities;
namespace MPF.UmdImageCreator
{
@@ -31,20 +30,23 @@ namespace MPF.UmdImageCreator
#region BaseParameters Implementations
/// <inheritdoc/>
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath)
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
{
List<string> missingFiles = new List<string>();
switch (this.Type)
{
case MediaType.UMD:
if (!File.Exists($"{basePath}_disc.txt"))
missingFiles.Add($"{basePath}_disc.txt");
if (!File.Exists($"{basePath}_mainError.txt"))
missingFiles.Add($"{basePath}_mainError.txt");
if (!File.Exists($"{basePath}_mainInfo.txt"))
missingFiles.Add($"{basePath}_mainInfo.txt");
if (!File.Exists($"{basePath}_volDesc.txt"))
missingFiles.Add($"{basePath}_volDesc.txt");
if (!File.Exists($"{basePath}_logs.zip") || !preCheck)
{
if (!File.Exists($"{basePath}_disc.txt"))
missingFiles.Add($"{basePath}_disc.txt");
if (!File.Exists($"{basePath}_mainError.txt"))
missingFiles.Add($"{basePath}_mainError.txt");
if (!File.Exists($"{basePath}_mainInfo.txt"))
missingFiles.Add($"{basePath}_mainInfo.txt");
if (!File.Exists($"{basePath}_volDesc.txt"))
missingFiles.Add($"{basePath}_volDesc.txt");
}
break;

View File

@@ -0,0 +1,122 @@
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MPF.Utilities
{
internal static class Logging
{
/// <summary>
/// Process a chunk of text and send it to a handler
/// </summary>
/// <param name="reader">TextReader representing the input</param>
/// <param name="baseClass">Invoking class, passed on to the event handler</param>
/// <param name="handler">Event handler to be invoked to write to log</param>
public static async Task OutputToLog(TextReader reader, object baseClass, EventHandler<string> handler)
{
// Initialize the required variables
char[] buffer = new char[256];
int read = 0;
var sb = new StringBuilder();
try
{
while (true)
{
// Try to read the next chunk of characters
read = await reader.ReadAsync(buffer, 0, buffer.Length);
if (read == 0)
{
Thread.Sleep(10);
continue;
}
// Convert the characters into a string
string line = new string(buffer, 0, read);
// If we have no newline characters, store in the string builder
if (!line.Contains('\r') && !line.Contains('\n'))
sb.Append(line);
// If we have a newline, append and log
else if (line.Contains('\n') || line.Contains("\r\n"))
ProcessNewLines(sb, line, baseClass, handler);
// If we have a carriage return only, append and log first and last instances
else if (line.Contains('\r'))
ProcessCarriageReturns(sb, line, baseClass, handler);
}
}
catch { }
finally
{
handler?.Invoke(baseClass, sb.ToString());
}
}
/// <summary>
/// Process a chunk that contains newlines
/// </summary>
/// <param name="sb">StringBuilder to write from and append to</param>
/// <param name="line">Current line to process</param>
/// <param name="baseClass">Invoking class, passed on to the event handler</param>
/// <param name="handler">Event handler to be invoked to write to log</param>
private static void ProcessNewLines(StringBuilder sb, string line, object baseClass, EventHandler<string> handler)
{
line = line.Replace("\r\n", "\n");
var split = line.Split('\n');
for (int i = 0; i < split.Length; i++)
{
// If the chunk contains a carriage return, handle it like a separate line
if (split[i].Contains('\r'))
{
ProcessCarriageReturns(sb, split[i], baseClass, handler);
continue;
}
// For the first item, append to anything existing and then write out
if (i == 0)
{
sb.Append(split[i]);
handler?.Invoke(baseClass, sb.ToString());
sb.Clear();
}
// For the last item, just append so it's dealt with the next time
else if (i == split.Length - 1)
{
sb.Append(split[i]);
}
// For everything else, directly write out
else
{
handler?.Invoke(baseClass, split[i]);
}
}
}
/// <summary>
/// Process a chunk that contains carriage returns
/// </summary>
/// <param name="sb">StringBuilder to write from and append to</param>
/// <param name="line">Current line to process</param>
/// <param name="baseClass">Invoking class, passed on to the event handler</param>
/// <param name="handler">Event handler to be invoked to write to log</param>
private static void ProcessCarriageReturns(StringBuilder sb, string line, object baseClass, EventHandler<string> handler)
{
var split = line.Split('\r');
// Append and log the first
sb.Append(split[0]);
handler?.Invoke(baseClass, sb.ToString());
// Append the last
sb.Clear();
sb.Append($"\r{split[split.Length - 1]}");
}
}
}

View File

@@ -272,6 +272,7 @@ namespace MPF.Utilities
case KnownSystem.IBMPCCompatible:
types.Add(MediaType.CDROM);
types.Add(MediaType.DVD);
types.Add(MediaType.BluRay);
types.Add(MediaType.FloppyDisk);
types.Add(MediaType.HardDisk);
types.Add(MediaType.DataCartridge);
@@ -889,7 +890,7 @@ namespace MPF.Utilities
&& Directory.EnumerateFiles(Path.Combine(drivePath, "VIDEO_TS")).Count() > 0)
{
// TODO: Maybe add video track hashes to compare for Xbox and X360?
if (string.Equals(drive.VolumeLabel, "SEP13011042072", StringComparison.OrdinalIgnoreCase))
if (drive.VolumeLabel.StartsWith("SEP13011042", StringComparison.OrdinalIgnoreCase))
return KnownSystem.MicrosoftXBOX;
return KnownSystem.DVDVideo;
@@ -1023,9 +1024,10 @@ namespace MPF.Utilities
/// Run protection scan on a given dump environment
/// </summary>
/// <param name="path">Path to scan for protection</param>
/// <param name="options">Options object that determines what to scan</param>
/// <param name="progress">Optional progress callback</param>
/// <returns>TCopy protection detected in the envirionment, if any</returns>
public static async Task<(bool, string)> RunProtectionScanOnPath(string path, IProgress<ProtectionProgress> progress = null)
public static async Task<(bool, string)> RunProtectionScanOnPath(string path, Options options, IProgress<ProtectionProgress> progress = null)
{
try
{
@@ -1033,10 +1035,10 @@ namespace MPF.Utilities
{
var scanner = new Scanner(progress)
{
IncludePosition = false, // Debug flag to include protection position in file
ScanAllFiles = false, // Forces all files to be scanned as executables
ScanArchives = true, // Allows archives to have internals extracted and scanned
ScanPackers = false, // Allows executable packers to be detected
IncludePosition = options.IncludeDebugProtectionInformation,
ScanAllFiles = options.ForceScanningForProtection,
ScanArchives = options.ScanArchivesForProtection,
ScanPackers = options.ScanPackersForProtection,
};
return scanner.GetProtections(path);
});
@@ -1048,7 +1050,8 @@ namespace MPF.Utilities
string protections = string.Join(", ", found
.Where(kvp => kvp.Value != null && kvp.Value.Any())
.SelectMany(kvp => kvp.Value)
.Distinct());
.Distinct()
.OrderBy(p => p));
return (true, protections);
}
catch (Exception ex)

View File

@@ -2,6 +2,7 @@
<PropertyGroup>
<TargetFrameworks>net48;netcoreapp3.1</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<IsPackable>false</IsPackable>
</PropertyGroup>
@@ -17,8 +18,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeCoverage" Version="16.9.0-preview-20210106-01" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.0-preview-20210106-01" />
<PackageReference Include="Microsoft.CodeCoverage" Version="16.11.0-release-20210626-04" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0-release-20210626-04" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
<PackageReference Include="xunit.analyzers" Version="0.10.0" />

View File

@@ -15,48 +15,48 @@ namespace MPF.Test.Utilities
public static IEnumerable<object[]> KnownSystems = KnownSystemComboBoxItem.GenerateElements().Select(e => new object[] { e });
[Theory]
[InlineData(DiscImageCreator.Command.Audio, MediaType.CDROM)]
[InlineData(DiscImageCreator.Command.BluRay, MediaType.BluRay)]
[InlineData(DiscImageCreator.Command.Close, null)]
[InlineData(DiscImageCreator.Command.CompactDisc, MediaType.CDROM)]
[InlineData(DiscImageCreator.Command.Data, MediaType.CDROM)]
[InlineData(DiscImageCreator.Command.DigitalVideoDisc, MediaType.DVD)]
[InlineData(DiscImageCreator.Command.Eject, null)]
[InlineData(DiscImageCreator.Command.Floppy, MediaType.FloppyDisk)]
[InlineData(DiscImageCreator.Command.GDROM, MediaType.GDROM)]
[InlineData(DiscImageCreator.Command.MDS, null)]
[InlineData(DiscImageCreator.Command.Reset, null)]
[InlineData(DiscImageCreator.Command.SACD, MediaType.CDROM)]
[InlineData(DiscImageCreator.Command.Start, null)]
[InlineData(DiscImageCreator.Command.Stop, null)]
[InlineData(DiscImageCreator.Command.Sub, null)]
[InlineData(DiscImageCreator.Command.Swap, MediaType.GDROM)]
[InlineData(DiscImageCreator.Command.XBOX, MediaType.DVD)]
public void BaseCommandToMediaTypeTest(DiscImageCreator.Command command, MediaType? expected)
[InlineData(DiscImageCreator.CommandStrings.Audio, MediaType.CDROM)]
[InlineData(DiscImageCreator.CommandStrings.BluRay, MediaType.BluRay)]
[InlineData(DiscImageCreator.CommandStrings.Close, null)]
[InlineData(DiscImageCreator.CommandStrings.CompactDisc, MediaType.CDROM)]
[InlineData(DiscImageCreator.CommandStrings.Data, MediaType.CDROM)]
[InlineData(DiscImageCreator.CommandStrings.DigitalVideoDisc, MediaType.DVD)]
[InlineData(DiscImageCreator.CommandStrings.Eject, null)]
[InlineData(DiscImageCreator.CommandStrings.Floppy, MediaType.FloppyDisk)]
[InlineData(DiscImageCreator.CommandStrings.GDROM, MediaType.GDROM)]
[InlineData(DiscImageCreator.CommandStrings.MDS, null)]
[InlineData(DiscImageCreator.CommandStrings.Reset, null)]
[InlineData(DiscImageCreator.CommandStrings.SACD, MediaType.CDROM)]
[InlineData(DiscImageCreator.CommandStrings.Start, null)]
[InlineData(DiscImageCreator.CommandStrings.Stop, null)]
[InlineData(DiscImageCreator.CommandStrings.Sub, null)]
[InlineData(DiscImageCreator.CommandStrings.Swap, MediaType.GDROM)]
[InlineData(DiscImageCreator.CommandStrings.XBOX, MediaType.DVD)]
public void BaseCommandToMediaTypeTest(string command, MediaType? expected)
{
MediaType? actual = DiscImageCreator.Converters.ToMediaType(command);
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(DiscImageCreator.Command.Audio, KnownSystem.AudioCD)]
[InlineData(DiscImageCreator.Command.BluRay, KnownSystem.SonyPlayStation3)]
[InlineData(DiscImageCreator.Command.Close, null)]
[InlineData(DiscImageCreator.Command.CompactDisc, KnownSystem.IBMPCCompatible)]
[InlineData(DiscImageCreator.Command.Data, KnownSystem.IBMPCCompatible)]
[InlineData(DiscImageCreator.Command.DigitalVideoDisc, KnownSystem.IBMPCCompatible)]
[InlineData(DiscImageCreator.Command.Eject, null)]
[InlineData(DiscImageCreator.Command.Floppy, KnownSystem.IBMPCCompatible)]
[InlineData(DiscImageCreator.Command.GDROM, KnownSystem.SegaDreamcast)]
[InlineData(DiscImageCreator.Command.MDS, null)]
[InlineData(DiscImageCreator.Command.Reset, null)]
[InlineData(DiscImageCreator.Command.SACD, KnownSystem.SuperAudioCD)]
[InlineData(DiscImageCreator.Command.Start, null)]
[InlineData(DiscImageCreator.Command.Stop, null)]
[InlineData(DiscImageCreator.Command.Sub, null)]
[InlineData(DiscImageCreator.Command.Swap, KnownSystem.SegaDreamcast)]
[InlineData(DiscImageCreator.Command.XBOX, KnownSystem.MicrosoftXBOX)]
public void BaseCommandToKnownSystemTest(DiscImageCreator.Command command, KnownSystem? expected)
[InlineData(DiscImageCreator.CommandStrings.Audio, KnownSystem.AudioCD)]
[InlineData(DiscImageCreator.CommandStrings.BluRay, KnownSystem.SonyPlayStation3)]
[InlineData(DiscImageCreator.CommandStrings.Close, null)]
[InlineData(DiscImageCreator.CommandStrings.CompactDisc, KnownSystem.IBMPCCompatible)]
[InlineData(DiscImageCreator.CommandStrings.Data, KnownSystem.IBMPCCompatible)]
[InlineData(DiscImageCreator.CommandStrings.DigitalVideoDisc, KnownSystem.IBMPCCompatible)]
[InlineData(DiscImageCreator.CommandStrings.Eject, null)]
[InlineData(DiscImageCreator.CommandStrings.Floppy, KnownSystem.IBMPCCompatible)]
[InlineData(DiscImageCreator.CommandStrings.GDROM, KnownSystem.SegaDreamcast)]
[InlineData(DiscImageCreator.CommandStrings.MDS, null)]
[InlineData(DiscImageCreator.CommandStrings.Reset, null)]
[InlineData(DiscImageCreator.CommandStrings.SACD, KnownSystem.SuperAudioCD)]
[InlineData(DiscImageCreator.CommandStrings.Start, null)]
[InlineData(DiscImageCreator.CommandStrings.Stop, null)]
[InlineData(DiscImageCreator.CommandStrings.Sub, null)]
[InlineData(DiscImageCreator.CommandStrings.Swap, KnownSystem.SegaDreamcast)]
[InlineData(DiscImageCreator.CommandStrings.XBOX, KnownSystem.MicrosoftXBOX)]
public void BaseCommandToKnownSystemTest(string command, KnownSystem? expected)
{
KnownSystem? actual = DiscImageCreator.Converters.ToKnownSystem(command);
Assert.Equal(expected, actual);

View File

@@ -10,13 +10,13 @@ namespace MPF.Test.Utilities
public class ParametersTest
{
[Theory]
[InlineData(KnownSystem.MicrosoftXBOX, MediaType.CDROM, Command.CompactDisc)]
[InlineData(KnownSystem.MicrosoftXBOX, MediaType.DVD, Command.XBOX)]
[InlineData(KnownSystem.MicrosoftXBOX, MediaType.LaserDisc, Command.NONE)]
[InlineData(KnownSystem.SegaNu, MediaType.BluRay, Command.BluRay)]
[InlineData(KnownSystem.AppleMacintosh, MediaType.FloppyDisk, Command.Floppy)]
[InlineData(KnownSystem.RawThrillsVarious, MediaType.GDROM, Command.NONE)]
public void ParametersFromSystemAndTypeTest(KnownSystem? knownSystem, MediaType? mediaType, Command expected)
[InlineData(KnownSystem.MicrosoftXBOX, MediaType.CDROM, CommandStrings.CompactDisc)]
[InlineData(KnownSystem.MicrosoftXBOX, MediaType.DVD, CommandStrings.XBOX)]
[InlineData(KnownSystem.MicrosoftXBOX, MediaType.LaserDisc, null)]
[InlineData(KnownSystem.SegaNu, MediaType.BluRay, CommandStrings.BluRay)]
[InlineData(KnownSystem.AppleMacintosh, MediaType.FloppyDisk, CommandStrings.Floppy)]
[InlineData(KnownSystem.RawThrillsVarious, MediaType.GDROM, null)]
public void ParametersFromSystemAndTypeTest(KnownSystem? knownSystem, MediaType? mediaType, string expected)
{
var options = new Options { };
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
@@ -25,26 +25,26 @@ namespace MPF.Test.Utilities
[Theory]
[InlineData(KnownSystem.AppleMacintosh, MediaType.LaserDisc, true, 20, null, null)]
[InlineData(KnownSystem.NintendoGameCube, MediaType.NintendoGameCubeGameDisc, false, 20, null, new Flag[] { Flag.Raw })]
[InlineData(KnownSystem.IBMPCCompatible, MediaType.DVD, false, 20, null, new Flag[] { Flag.CopyrightManagementInformation, Flag.ScanFileProtect })]
[InlineData(KnownSystem.NintendoGameCube, MediaType.NintendoGameCubeGameDisc, false, 20, null, new string[] { FlagStrings.Raw })]
[InlineData(KnownSystem.IBMPCCompatible, MediaType.DVD, false, 20, null, new string[] { FlagStrings.CopyrightManagementInformation, FlagStrings.ScanFileProtect })]
/* paranoid mode tests */
[InlineData(KnownSystem.IBMPCCompatible, MediaType.CDROM, true, 1000, 2, new Flag[] { Flag.C2Opcode, Flag.NoFixSubQSecuROM, Flag.ScanFileProtect, Flag.ScanSectorProtect, Flag.SubchannelReadLevel })]
[InlineData(KnownSystem.AppleMacintosh, MediaType.CDROM, false, 20, null, new Flag[] { Flag.C2Opcode, Flag.NoFixSubQSecuROM, Flag.ScanFileProtect, Flag.ScanSectorProtect, Flag.SubchannelReadLevel })]
[InlineData(KnownSystem.IBMPCCompatible, MediaType.DVD, true, 500, null, new Flag[] { Flag.CopyrightManagementInformation, Flag.ScanFileProtect })]
[InlineData(KnownSystem.HDDVDVideo, MediaType.HDDVD, true, 500, null, new Flag[] { Flag.CopyrightManagementInformation })]
[InlineData(KnownSystem.IBMPCCompatible, MediaType.DVD, false, 500, null, new Flag[] { Flag.CopyrightManagementInformation, Flag.ScanFileProtect })]
[InlineData(KnownSystem.HDDVDVideo, MediaType.HDDVD, false, 500, null, new Flag[] { Flag.CopyrightManagementInformation })]
[InlineData(KnownSystem.IBMPCCompatible, MediaType.CDROM, true, 1000, 2, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.ScanFileProtect, FlagStrings.ScanSectorProtect, FlagStrings.SubchannelReadLevel })]
[InlineData(KnownSystem.AppleMacintosh, MediaType.CDROM, false, 20, null, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.ScanFileProtect, FlagStrings.ScanSectorProtect, FlagStrings.SubchannelReadLevel })]
[InlineData(KnownSystem.IBMPCCompatible, MediaType.DVD, true, 500, null, new string[] { FlagStrings.CopyrightManagementInformation, FlagStrings.ScanFileProtect })]
[InlineData(KnownSystem.HDDVDVideo, MediaType.HDDVD, true, 500, null, new string[] { FlagStrings.CopyrightManagementInformation })]
[InlineData(KnownSystem.IBMPCCompatible, MediaType.DVD, false, 500, null, new string[] { FlagStrings.CopyrightManagementInformation, FlagStrings.ScanFileProtect })]
[InlineData(KnownSystem.HDDVDVideo, MediaType.HDDVD, false, 500, null, new string[] { FlagStrings.CopyrightManagementInformation })]
/* reread c2 */
[InlineData(KnownSystem.SegaDreamcast, MediaType.GDROM, false, 1000, null, new Flag[] { Flag.C2Opcode })]
[InlineData(KnownSystem.SegaDreamcast, MediaType.GDROM, false, -1, null, new Flag[] { Flag.C2Opcode })]
[InlineData(KnownSystem.SegaDreamcast, MediaType.GDROM, false, 1000, null, new string[] { FlagStrings.C2Opcode })]
[InlineData(KnownSystem.SegaDreamcast, MediaType.GDROM, false, -1, null, new string[] { FlagStrings.C2Opcode })]
public void ParametersFromOptionsTest(KnownSystem? knownSystem, MediaType? mediaType, bool paranoid, int rereadC2, int? subchannelLevel, Flag[] expected)
public void ParametersFromOptionsTest(KnownSystem? knownSystem, MediaType? mediaType, bool paranoid, int rereadC2, int? subchannelLevel, string[] expected)
{
var options = new Options { DICParanoidMode = paranoid, DICRereadCount = rereadC2 };
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
HashSet<Flag> expectedSet = new HashSet<Flag>(expected ?? new Flag[0]);
HashSet<Flag> actualSet = new HashSet<Flag>(actual.Keys.Cast<Flag>() ?? new Flag[0]);
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
HashSet<string> actualSet = new HashSet<string>(actual.Keys.Cast<string>() ?? new string[0]);
Assert.Equal(expectedSet, actualSet);
if (rereadC2 == -1 || !Validators.GetValidMediaTypes(knownSystem).Contains(mediaType))
Assert.Null(actual.C2OpcodeValue[0]);

View File

@@ -1,9 +1,772 @@
<Application x:Class="MPF.App"
<Application
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MPF"
xmlns:Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero" x:Class="MPF.App"
StartupUri="Windows\MainWindow.xaml">
<Application.Resources>
<!-- Button -->
<SolidColorBrush x:Key="Button.Static.Background" Color="#FFDDDDDD"/>
<SolidColorBrush x:Key="Button.Static.Border" Color="#FF707070"/>
<SolidColorBrush x:Key="Button.MouseOver.Background" Color="#FFBEE6FD"/>
<SolidColorBrush x:Key="Button.MouseOver.Border" Color="#FF3C7FB1"/>
<SolidColorBrush x:Key="Button.Pressed.Background" Color="#FFC4E5F6"/>
<SolidColorBrush x:Key="Button.Pressed.Border" Color="#FF2C628B"/>
<SolidColorBrush x:Key="Button.Disabled.Background" Color="#FFF4F4F4"/>
<SolidColorBrush x:Key="Button.Disabled.Border" Color="#FFADB2B5"/>
<SolidColorBrush x:Key="Button.Disabled.Foreground" Color="#FF838383"/>
<Style x:Key="CustomButtonStyle" TargetType="{x:Type Button}">
<Setter Property="FocusVisualStyle" Value="{DynamicResource FocusVisual}"/>
<Setter Property="Background" Value="{DynamicResource Button.Static.Background}"/>
<Setter Property="BorderBrush" Value="{DynamicResource Button.Static.Border}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
<ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsDefaulted" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" TargetName="border" Value="{DynamicResource Button.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource Button.MouseOver.Border}"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Background" TargetName="border" Value="{DynamicResource Button.Pressed.Background}"/>
<Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource Button.Pressed.Border}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="border" Value="{DynamicResource Button.Disabled.Background}"/>
<Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource Button.Disabled.Border}"/>
<Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{DynamicResource Button.Disabled.Foreground}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- ComboBox -->
<Style x:Key="FocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<LinearGradientBrush x:Key="ComboBox.Static.Background" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFF0F0F0" Offset="0.0"/>
<GradientStop Color="#FFE5E5E5" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="ComboBox.Static.Border" Color="#FFACACAC"/>
<SolidColorBrush x:Key="ComboBox.Static.Editable.Background" Color="#FFFFFFFF"/>
<SolidColorBrush x:Key="ComboBox.Static.Editable.Border" Color="#FFABADB3"/>
<SolidColorBrush x:Key="ComboBox.Static.Editable.Button.Background" Color="Transparent"/>
<SolidColorBrush x:Key="ComboBox.Static.Editable.Button.Border" Color="Transparent"/>
<SolidColorBrush x:Key="ComboBox.MouseOver.Glyph" Color="#FF000000"/>
<LinearGradientBrush x:Key="ComboBox.MouseOver.Background" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFECF4FC" Offset="0.0"/>
<GradientStop Color="#FFDCECFC" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="ComboBox.MouseOver.Border" Color="#FF7EB4EA"/>
<SolidColorBrush x:Key="ComboBox.MouseOver.Editable.Background" Color="#FFFFFFFF"/>
<SolidColorBrush x:Key="ComboBox.MouseOver.Editable.Border" Color="#FF7EB4EA"/>
<LinearGradientBrush x:Key="ComboBox.MouseOver.Editable.Button.Background" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFEBF4FC" Offset="0.0"/>
<GradientStop Color="#FFDCECFC" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="ComboBox.MouseOver.Editable.Button.Border" Color="#FF7EB4EA"/>
<SolidColorBrush x:Key="ComboBox.Pressed.Glyph" Color="#FF000000"/>
<LinearGradientBrush x:Key="ComboBox.Pressed.Background" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFDAECFC" Offset="0.0"/>
<GradientStop Color="#FFC4E0FC" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="ComboBox.Pressed.Border" Color="#FF569DE5"/>
<SolidColorBrush x:Key="ComboBox.Pressed.Editable.Background" Color="#FFFFFFFF"/>
<SolidColorBrush x:Key="ComboBox.Pressed.Editable.Border" Color="#FF569DE5"/>
<LinearGradientBrush x:Key="ComboBox.Pressed.Editable.Button.Background" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFDAEBFC" Offset="0.0"/>
<GradientStop Color="#FFC4E0FC" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="ComboBox.Pressed.Editable.Button.Border" Color="#FF569DE5"/>
<SolidColorBrush x:Key="ComboBox.Disabled.Glyph" Color="#FFBFBFBF"/>
<SolidColorBrush x:Key="ComboBox.Disabled.Background" Color="#FFF0F0F0"/>
<SolidColorBrush x:Key="ComboBox.Disabled.Border" Color="#FFD9D9D9"/>
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Background" Color="#FFFFFFFF"/>
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Border" Color="#FFBFBFBF"/>
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Button.Background" Color="Transparent"/>
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Button.Border" Color="Transparent"/>
<SolidColorBrush x:Key="ComboBox.Static.Glyph" Color="#FF606060"/>
<Style x:Key="ComboBoxToggleButton" TargetType="{x:Type ToggleButton}">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="IsTabStop" Value="false"/>
<Setter Property="Focusable" Value="false"/>
<Setter Property="ClickMode" Value="Press"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border x:Name="templateRoot" BorderBrush="{DynamicResource ComboBox.Static.Border}" BorderThickness="{TemplateBinding BorderThickness}" Background="{DynamicResource ComboBox.Static.Background}" SnapsToDevicePixels="true">
<Border x:Name="splitBorder" BorderBrush="Transparent" BorderThickness="1" HorizontalAlignment="Right" Margin="0" SnapsToDevicePixels="true" Width="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}">
<Path x:Name="arrow" Data="F1 M 0,0 L 2.667,2.66665 L 5.3334,0 L 5.3334,-1.78168 L 2.6667,0.88501 L0,-1.78168 L0,0 Z" Fill="{DynamicResource ComboBox.Static.Glyph}" HorizontalAlignment="Center" Margin="0" VerticalAlignment="Center"/>
</Border>
</Border>
<ControlTemplate.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="true"/>
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding IsPressed, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="true"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="templateRoot" Value="{DynamicResource ComboBox.Static.Editable.Background}"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="{DynamicResource ComboBox.Static.Editable.Border}"/>
<Setter Property="Background" TargetName="splitBorder" Value="{DynamicResource ComboBox.Static.Editable.Button.Background}"/>
<Setter Property="BorderBrush" TargetName="splitBorder" Value="{DynamicResource ComboBox.Static.Editable.Button.Border}"/>
</MultiDataTrigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Fill" TargetName="arrow" Value="{DynamicResource ComboBox.MouseOver.Glyph}"/>
</Trigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="false"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="templateRoot" Value="{DynamicResource ComboBox.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="{DynamicResource ComboBox.MouseOver.Border}"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="true"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="templateRoot" Value="{DynamicResource ComboBox.MouseOver.Editable.Background}"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="{DynamicResource ComboBox.MouseOver.Editable.Border}"/>
<Setter Property="Background" TargetName="splitBorder" Value="{DynamicResource ComboBox.MouseOver.Editable.Button.Background}"/>
<Setter Property="BorderBrush" TargetName="splitBorder" Value="{DynamicResource ComboBox.MouseOver.Editable.Button.Border}"/>
</MultiDataTrigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Fill" TargetName="arrow" Value="{DynamicResource ComboBox.Pressed.Glyph}"/>
</Trigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsPressed, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="false"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="templateRoot" Value="{DynamicResource ComboBox.Pressed.Background}"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="{DynamicResource ComboBox.Pressed.Border}"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsPressed, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="true"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="templateRoot" Value="{DynamicResource ComboBox.Pressed.Editable.Background}"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="{DynamicResource ComboBox.Pressed.Editable.Border}"/>
<Setter Property="Background" TargetName="splitBorder" Value="{DynamicResource ComboBox.Pressed.Editable.Button.Background}"/>
<Setter Property="BorderBrush" TargetName="splitBorder" Value="{DynamicResource ComboBox.Pressed.Editable.Button.Border}"/>
</MultiDataTrigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Fill" TargetName="arrow" Value="{DynamicResource ComboBox.Disabled.Glyph}"/>
</Trigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="false"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="templateRoot" Value="{DynamicResource ComboBox.Disabled.Background}"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="{DynamicResource ComboBox.Disabled.Border}"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="true"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="templateRoot" Value="{DynamicResource ComboBox.Disabled.Editable.Background}"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="{DynamicResource ComboBox.Disabled.Editable.Border}"/>
<Setter Property="Background" TargetName="splitBorder" Value="{DynamicResource ComboBox.Disabled.Editable.Button.Background}"/>
<Setter Property="BorderBrush" TargetName="splitBorder" Value="{DynamicResource ComboBox.Disabled.Editable.Button.Border}"/>
</MultiDataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ControlTemplate x:Key="ComboBoxTemplate" TargetType="{x:Type ComboBox}">
<Grid x:Name="templateRoot" SnapsToDevicePixels="true">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Width="0"/>
</Grid.ColumnDefinitions>
<Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Margin="1" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
<Themes:SystemDropShadowChrome x:Name="shadow" Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=templateRoot}">
<Border x:Name="dropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
<ScrollViewer x:Name="DropDownScrollViewer">
<Grid x:Name="grid" RenderOptions.ClearTypeHint="Enabled">
<Canvas x:Name="canvas" HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
<Rectangle x:Name="opaqueRect" Fill="{Binding Background, ElementName=dropDownBorder}" Height="{Binding ActualHeight, ElementName=dropDownBorder}" Width="{Binding ActualWidth, ElementName=dropDownBorder}"/>
</Canvas>
<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Grid>
</ScrollViewer>
</Border>
</Themes:SystemDropShadowChrome>
</Popup>
<ToggleButton x:Name="toggleButton" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.ColumnSpan="2" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Style="{DynamicResource ComboBoxToggleButton}"/>
<ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" Content="{TemplateBinding SelectionBoxItem}" ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" IsHitTestVisible="false" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="true">
<Setter Property="Margin" TargetName="shadow" Value="0,0,5,5"/>
<Setter Property="Color" TargetName="shadow" Value="#71000000"/>
</Trigger>
<Trigger Property="HasItems" Value="false">
<Setter Property="Height" TargetName="dropDownBorder" Value="95"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsGrouping" Value="true"/>
<Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</MultiTrigger>
<Trigger Property="ScrollViewer.CanContentScroll" SourceName="DropDownScrollViewer" Value="false">
<Setter Property="Canvas.Top" TargetName="opaqueRect" Value="{Binding VerticalOffset, ElementName=DropDownScrollViewer}"/>
<Setter Property="Canvas.Left" TargetName="opaqueRect" Value="{Binding HorizontalOffset, ElementName=DropDownScrollViewer}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<SolidColorBrush x:Key="TextBox.Static.Background" Color="#FFFFFFFF"/>
<Style x:Key="ComboBoxEditableTextBox" TargetType="{x:Type TextBox}">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="MinWidth" Value="0"/>
<Setter Property="MinHeight" Value="0"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<ScrollViewer x:Name="PART_ContentHost" Background="Transparent" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ControlTemplate x:Key="ComboBoxEditableTemplate" TargetType="{x:Type ComboBox}">
<Grid x:Name="templateRoot" SnapsToDevicePixels="true">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Width="0"/>
</Grid.ColumnDefinitions>
<Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
<Themes:SystemDropShadowChrome x:Name="shadow" Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=templateRoot}">
<Border x:Name="dropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
<ScrollViewer x:Name="DropDownScrollViewer">
<Grid x:Name="grid" RenderOptions.ClearTypeHint="Enabled">
<Canvas x:Name="canvas" HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
<Rectangle x:Name="opaqueRect" Fill="{Binding Background, ElementName=dropDownBorder}" Height="{Binding ActualHeight, ElementName=dropDownBorder}" Width="{Binding ActualWidth, ElementName=dropDownBorder}"/>
</Canvas>
<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Grid>
</ScrollViewer>
</Border>
</Themes:SystemDropShadowChrome>
</Popup>
<ToggleButton x:Name="toggleButton" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.ColumnSpan="2" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Style="{DynamicResource ComboBoxToggleButton}"/>
<Border x:Name="border" Background="{DynamicResource TextBox.Static.Background}" Margin="{TemplateBinding BorderThickness}">
<TextBox x:Name="PART_EditableTextBox" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" IsReadOnly="{Binding IsReadOnly, RelativeSource={RelativeSource TemplatedParent}}" Margin="{TemplateBinding Padding}" Style="{DynamicResource ComboBoxEditableTextBox}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" TargetName="border" Value="0.56"/>
</Trigger>
<Trigger Property="IsKeyboardFocusWithin" Value="true">
<Setter Property="Foreground" Value="Black"/>
</Trigger>
<Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="true">
<Setter Property="Margin" TargetName="shadow" Value="0,0,5,5"/>
<Setter Property="Color" TargetName="shadow" Value="#71000000"/>
</Trigger>
<Trigger Property="HasItems" Value="false">
<Setter Property="Height" TargetName="dropDownBorder" Value="95"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsGrouping" Value="true"/>
<Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</MultiTrigger>
<Trigger Property="ScrollViewer.CanContentScroll" SourceName="DropDownScrollViewer" Value="false">
<Setter Property="Canvas.Top" TargetName="opaqueRect" Value="{Binding VerticalOffset, ElementName=DropDownScrollViewer}"/>
<Setter Property="Canvas.Left" TargetName="opaqueRect" Value="{Binding HorizontalOffset, ElementName=DropDownScrollViewer}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style x:Key="CustomComboBoxStyle" TargetType="{x:Type ComboBox}">
<Setter Property="FocusVisualStyle" Value="{DynamicResource FocusVisual}"/>
<Setter Property="Background" Value="{DynamicResource ComboBox.Static.Background}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ComboBox.Static.Border}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="Padding" Value="6,3,5,3"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
<Setter Property="ScrollViewer.PanningMode" Value="Both"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="Template" Value="{DynamicResource ComboBoxTemplate}"/>
<Style.Triggers>
<Trigger Property="IsEditable" Value="true">
<Setter Property="IsTabStop" Value="false"/>
<Setter Property="Padding" Value="2"/>
<Setter Property="Template" Value="{DynamicResource ComboBoxEditableTemplate}"/>
</Trigger>
</Style.Triggers>
</Style>
<!-- ContextMenu -->
<SolidColorBrush x:Key="ContextMenu.Static.Border" Color="#FF888888"/>
<Style x:Key="CustomContextMenuStyle" TargetType="{x:Type ContextMenu}">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Grid.IsSharedSizeScope" Value="true" />
<Setter Property="HasDropShadow" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContextMenu}">
<Border x:Name="Border" Background="{Binding Background}" BorderBrush="{DynamicResource ContextMenu.Static.Border}" BorderThickness="1">
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Cycle" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="HasDropShadow" Value="true">
<Setter TargetName="Border" Property="Padding" Value="0,3,0,3" />
<Setter TargetName="Border" Property="CornerRadius" Value="4" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- CustomMessageBox -->
<SolidColorBrush x:Key="CustomMessageBox.Static.Background" Color="#FFE5E5E5"/>
<!-- MenuItem -->
<SolidColorBrush x:Key="MenuItem.SubMenu.Background" Color="#FFF0F0F0"/>
<SolidColorBrush x:Key="MenuItem.SubMenu.Border" Color="#FF999999"/>
<SolidColorBrush x:Key="MenuItem.SubMenu.HoverRectangle" Color="Transparent"/>
<ControlTemplate x:Key="CustomMenuItemTemplate" TargetType="{x:Type MenuItem}">
<Border x:Name="templateRoot" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<Grid VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<ContentPresenter x:Name="Icon" Content="{TemplateBinding Icon}" ContentSource="Icon" HorizontalAlignment="Center" Height="16" Margin="3" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center" Width="16"/>
<Path x:Name="GlyphPanel" Data="F1M10,1.2L4.7,9.1 4.5,9.1 0,5.2 1.3,3.5 4.3,6.1 8.3,0 10,1.2z" Fill="{TemplateBinding Foreground}" FlowDirection="LeftToRight" Margin="3" Visibility="Collapsed" VerticalAlignment="Center"/>
<ContentPresenter ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" Grid.Column="1" ContentStringFormat="{TemplateBinding HeaderStringFormat}" ContentSource="Header" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
<Popup x:Name="PART_Popup" AllowsTransparency="True" Focusable="False" IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}" Placement="Bottom">
<Border x:Name="SubMenuBorder" BorderBrush="{DynamicResource MenuItem.SubMenu.Border}" BorderThickness="1" Background="{DynamicResource MenuItem.SubMenu.Background}" Padding="2">
<ScrollViewer x:Name="SubMenuScrollViewer" Style="{DynamicResource {ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}}">
<Grid RenderOptions.ClearTypeHint="Enabled">
<Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
<Rectangle x:Name="OpaqueRect" Fill="{Binding Background, ElementName=SubMenuBorder}" Height="{Binding ActualHeight, ElementName=SubMenuBorder}" Width="{Binding ActualWidth, ElementName=SubMenuBorder}"/>
</Canvas>
<Rectangle Fill="{DynamicResource MenuItem.SubMenu.HoverRectangle}" HorizontalAlignment="Left" Margin="29,2,0,2" Width="1"/>
<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Cycle" Grid.IsSharedSizeScope="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" KeyboardNavigation.TabNavigation="Cycle"/>
</Grid>
</ScrollViewer>
</Border>
</Popup>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSuspendingPopupAnimation" Value="True">
<Setter Property="PopupAnimation" TargetName="PART_Popup" Value="None"/>
</Trigger>
<Trigger Property="Icon" Value="{x:Null}">
<Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Visibility" TargetName="GlyphPanel" Value="Visible"/>
<Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
</Trigger>
<Trigger Property="IsHighlighted" Value="True">
<Setter Property="Background" TargetName="templateRoot" Value="#3D26A0DA"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="#FF26A0DA"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="#FF707070"/>
<Setter Property="Fill" TargetName="GlyphPanel" Value="#FF707070"/>
</Trigger>
<Trigger Property="CanContentScroll" SourceName="SubMenuScrollViewer" Value="False">
<Setter Property="Canvas.Top" TargetName="OpaqueRect" Value="{Binding VerticalOffset, ElementName=SubMenuScrollViewer}"/>
<Setter Property="Canvas.Left" TargetName="OpaqueRect" Value="{Binding HorizontalOffset, ElementName=SubMenuScrollViewer}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<!-- ProgressBar -->
<SolidColorBrush x:Key="ProgressBar.Progress" Color="#FF06B025"/>
<SolidColorBrush x:Key="ProgressBar.Background" Color="#FFE6E6E6"/>
<SolidColorBrush x:Key="ProgressBar.Border" Color="#FFBCBCBC"/>
<Style x:Key="CustomProgressBarStyle" TargetType="{x:Type ProgressBar}">
<Setter Property="Foreground" Value="{DynamicResource ProgressBar.Progress}"/>
<Setter Property="Background" Value="{DynamicResource ProgressBar.Background}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ProgressBar.Border}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ProgressBar}">
<Grid x:Name="TemplateRoot">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Determinate"/>
<VisualState x:Name="Indeterminate">
<Storyboard RepeatBehavior="Forever">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)" Storyboard.TargetName="Animation">
<EasingDoubleKeyFrame KeyTime="0" Value="0.25"/>
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="0.25"/>
<EasingDoubleKeyFrame KeyTime="0:0:2" Value="0.25"/>
</DoubleAnimationUsingKeyFrames>
<PointAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransformOrigin)" Storyboard.TargetName="Animation">
<EasingPointKeyFrame KeyTime="0" Value="-0.5,0.5"/>
<EasingPointKeyFrame KeyTime="0:0:1" Value="0.5,0.5"/>
<EasingPointKeyFrame KeyTime="0:0:2" Value="1.5,0.5"/>
</PointAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}"/>
<Rectangle x:Name="PART_Track"/>
<Grid x:Name="PART_Indicator" ClipToBounds="true" HorizontalAlignment="Left">
<Rectangle x:Name="Indicator" Fill="{TemplateBinding Foreground}"/>
<Rectangle x:Name="Animation" Fill="{TemplateBinding Foreground}" RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
</Grid>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="Orientation" Value="Vertical">
<Setter Property="LayoutTransform" TargetName="TemplateRoot">
<Setter.Value>
<RotateTransform Angle="-90"/>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsIndeterminate" Value="true">
<Setter Property="Visibility" TargetName="Indicator" Value="Collapsed"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- ScrollViewer -->
<SolidColorBrush x:Key="ScrollViewer.ScrollBar.Background" Color="LightGray"/>
<SolidColorBrush x:Key="ScrollViewer.ScrollBar.Foreground" Color="DarkGray"/>
<ControlTemplate x:Key="CustomScrollViewerControlStyle" TargetType="{x:Type ScrollViewer}">
<Grid x:Name="Grid" Background="{TemplateBinding Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Rectangle x:Name="Corner" Grid.Column="1" Fill="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" Grid.Row="1"/>
<ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanContentScroll="{TemplateBinding CanContentScroll}" CanHorizontallyScroll="False" CanVerticallyScroll="False" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Grid.Column="0" Margin="{TemplateBinding Padding}" Grid.Row="0"/>
<ScrollBar x:Name="PART_VerticalScrollBar" AutomationProperties.AutomationId="VerticalScrollBar" Cursor="Arrow" Grid.Column="1" Maximum="{TemplateBinding ScrollableHeight}" Minimum="0" Grid.Row="0" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportHeight}" Background="{DynamicResource ScrollViewer.ScrollBar.Background}" Foreground="{DynamicResource ScrollViewer.ScrollBar.Foreground}"/>
<ScrollBar x:Name="PART_HorizontalScrollBar" AutomationProperties.AutomationId="HorizontalScrollBar" Cursor="Arrow" Grid.Column="0" Maximum="{TemplateBinding ScrollableWidth}" Minimum="0" Orientation="Horizontal" Grid.Row="1" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportWidth}" Background="{DynamicResource ScrollViewer.ScrollBar.Background}" Foreground="{DynamicResource ScrollViewer.ScrollBar.Foreground}"/>
</Grid>
</ControlTemplate>
<!-- TabControl -->
<SolidColorBrush x:Key="TabItem.Selected.Background" Color="#FFFFFF"/>
<SolidColorBrush x:Key="TabItem.Selected.Border" Color="#ACACAC"/>
<Style x:Key="CustomTabControlStyle" TargetType="{x:Type TabControl}">
<Setter Property="Padding" Value="2"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Background" Value="{DynamicResource TabItem.Selected.Background}"/>
<Setter Property="BorderBrush" Value="{DynamicResource TabItem.Selected.Border}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid x:Name="templateRoot" ClipToBounds="true" SnapsToDevicePixels="true" KeyboardNavigation.TabNavigation="Local">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="ColumnDefinition0"/>
<ColumnDefinition x:Name="ColumnDefinition1" Width="0"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition x:Name="RowDefinition0" Height="Auto"/>
<RowDefinition x:Name="RowDefinition1" Height="*"/>
</Grid.RowDefinitions>
<TabPanel x:Name="headerPanel" Background="Transparent" Grid.Column="0" IsItemsHost="true" Margin="2,2,2,0" Grid.Row="0" KeyboardNavigation.TabIndex="1" Panel.ZIndex="1"/>
<Border x:Name="contentPanel" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Column="0" KeyboardNavigation.DirectionalNavigation="Contained" Grid.Row="1" KeyboardNavigation.TabIndex="2" KeyboardNavigation.TabNavigation="Local">
<ContentPresenter x:Name="PART_SelectedContentHost" ContentSource="SelectedContent" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="TabStripPlacement" Value="Bottom">
<Setter Property="Grid.Row" TargetName="headerPanel" Value="1"/>
<Setter Property="Grid.Row" TargetName="contentPanel" Value="0"/>
<Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
<Setter Property="Height" TargetName="RowDefinition1" Value="Auto"/>
<Setter Property="Margin" TargetName="headerPanel" Value="2,0,2,2"/>
</Trigger>
<Trigger Property="TabStripPlacement" Value="Left">
<Setter Property="Grid.Row" TargetName="headerPanel" Value="0"/>
<Setter Property="Grid.Row" TargetName="contentPanel" Value="0"/>
<Setter Property="Grid.Column" TargetName="headerPanel" Value="0"/>
<Setter Property="Grid.Column" TargetName="contentPanel" Value="1"/>
<Setter Property="Width" TargetName="ColumnDefinition0" Value="Auto"/>
<Setter Property="Width" TargetName="ColumnDefinition1" Value="*"/>
<Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
<Setter Property="Height" TargetName="RowDefinition1" Value="0"/>
<Setter Property="Margin" TargetName="headerPanel" Value="2,2,0,2"/>
</Trigger>
<Trigger Property="TabStripPlacement" Value="Right">
<Setter Property="Grid.Row" TargetName="headerPanel" Value="0"/>
<Setter Property="Grid.Row" TargetName="contentPanel" Value="0"/>
<Setter Property="Grid.Column" TargetName="headerPanel" Value="1"/>
<Setter Property="Grid.Column" TargetName="contentPanel" Value="0"/>
<Setter Property="Width" TargetName="ColumnDefinition0" Value="*"/>
<Setter Property="Width" TargetName="ColumnDefinition1" Value="Auto"/>
<Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
<Setter Property="Height" TargetName="RowDefinition1" Value="0"/>
<Setter Property="Margin" TargetName="headerPanel" Value="0,2,2,2"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- TabItem -->
<LinearGradientBrush x:Key="TabItem.Static.Background" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#F0F0F0" Offset="0.0"/>
<GradientStop Color="#E5E5E5" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="TabItem.Static.Border" Color="#ACACAC"/>
<LinearGradientBrush x:Key="TabItem.MouseOver.Background" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#ECF4FC" Offset="0.0"/>
<GradientStop Color="#DCECFC" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="TabItem.MouseOver.Border" Color="#7EB4EA"/>
<SolidColorBrush x:Key="TabItem.Disabled.Background" Color="#F0F0F0"/>
<SolidColorBrush x:Key="TabItem.Disabled.Border" Color="#D9D9D9"/>
<Style x:Key="CustomTabItemStyle" TargetType="{x:Type TabItem}">
<Setter Property="FocusVisualStyle" Value="{DynamicResource FocusVisual}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"/>
<Setter Property="Background" Value="{DynamicResource TabItem.Static.Background}"/>
<Setter Property="BorderBrush" Value="{DynamicResource TabItem.Static.Border}"/>
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="6,2,6,2"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid x:Name="templateRoot" SnapsToDevicePixels="true">
<Border x:Name="mainBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1,1,1,0" Background="{TemplateBinding Background}" Margin="0">
<Border x:Name="innerBorder" BorderBrush="{DynamicResource TabItem.Selected.Border}" BorderThickness="1,1,1,0" Background="{DynamicResource TabItem.Selected.Background}" Margin="-1" Opacity="0"/>
</Border>
<ContentPresenter x:Name="contentPresenter" ContentSource="Header" Focusable="False" HorizontalAlignment="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
</Grid>
<ControlTemplate.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Left"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="mainBorder" Value="{DynamicResource TabItem.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="mainBorder" Value="{DynamicResource TabItem.MouseOver.Border}"/>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="1,1,0,1"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="1,1,0,1"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Bottom"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="mainBorder" Value="{DynamicResource TabItem.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="mainBorder" Value="{DynamicResource TabItem.MouseOver.Border}"/>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="1,0,1,1"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="1,0,1,1"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Right"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="mainBorder" Value="{DynamicResource TabItem.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="mainBorder" Value="{DynamicResource TabItem.MouseOver.Border}"/>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="0,1,1,1"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="0,1,1,1"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Top"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="mainBorder" Value="{DynamicResource TabItem.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="mainBorder" Value="{DynamicResource TabItem.MouseOver.Border}"/>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="1,1,1,0"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="1,1,1,0"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Left"/>
</MultiDataTrigger.Conditions>
<Setter Property="Opacity" TargetName="contentPresenter" Value="0.56"/>
<Setter Property="Background" TargetName="mainBorder" Value="{DynamicResource TabItem.Disabled.Background}"/>
<Setter Property="BorderBrush" TargetName="mainBorder" Value="{DynamicResource TabItem.Disabled.Border}"/>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="1,1,0,1"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="1,1,0,1"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Bottom"/>
</MultiDataTrigger.Conditions>
<Setter Property="Opacity" TargetName="contentPresenter" Value="0.56"/>
<Setter Property="Background" TargetName="mainBorder" Value="{DynamicResource TabItem.Disabled.Background}"/>
<Setter Property="BorderBrush" TargetName="mainBorder" Value="{DynamicResource TabItem.Disabled.Border}"/>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="1,0,1,1"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="1,0,1,1"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Right"/>
</MultiDataTrigger.Conditions>
<Setter Property="Opacity" TargetName="contentPresenter" Value="0.56"/>
<Setter Property="Background" TargetName="mainBorder" Value="{DynamicResource TabItem.Disabled.Background}"/>
<Setter Property="BorderBrush" TargetName="mainBorder" Value="{DynamicResource TabItem.Disabled.Border}"/>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="0,1,1,1"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="0,1,1,1"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Top"/>
</MultiDataTrigger.Conditions>
<Setter Property="Opacity" TargetName="contentPresenter" Value="0.56"/>
<Setter Property="Background" TargetName="mainBorder" Value="{DynamicResource TabItem.Disabled.Background}"/>
<Setter Property="BorderBrush" TargetName="mainBorder" Value="{DynamicResource TabItem.Disabled.Border}"/>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="1,1,1,0"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="1,1,1,0"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Left"/>
</MultiDataTrigger.Conditions>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="1,1,0,1"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="1,1,0,1"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Left"/>
</MultiDataTrigger.Conditions>
<Setter Property="Panel.ZIndex" Value="1"/>
<Setter Property="Margin" Value="-2,-2,0,-2"/>
<Setter Property="Opacity" TargetName="innerBorder" Value="1"/>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="1,1,0,1"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="1,1,0,1"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Bottom"/>
</MultiDataTrigger.Conditions>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="1,0,1,1"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="1,0,1,1"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Bottom"/>
</MultiDataTrigger.Conditions>
<Setter Property="Panel.ZIndex" Value="1"/>
<Setter Property="Margin" Value="-2,0,-2,-2"/>
<Setter Property="Opacity" TargetName="innerBorder" Value="1"/>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="1,0,1,1"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="1,0,1,1"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Right"/>
</MultiDataTrigger.Conditions>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="0,1,1,1"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="0,1,1,1"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Right"/>
</MultiDataTrigger.Conditions>
<Setter Property="Panel.ZIndex" Value="1"/>
<Setter Property="Margin" Value="0,-2,-2,-2"/>
<Setter Property="Opacity" TargetName="innerBorder" Value="1"/>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="0,1,1,1"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="0,1,1,1"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Top"/>
</MultiDataTrigger.Conditions>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="1,1,1,0"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="1,1,1,0"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Top"/>
</MultiDataTrigger.Conditions>
<Setter Property="Panel.ZIndex" Value="1"/>
<Setter Property="Margin" Value="-2,-2,-2,0"/>
<Setter Property="Opacity" TargetName="innerBorder" Value="1"/>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="1,1,1,0"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="1,1,1,0"/>
</MultiDataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
</Application>

View File

@@ -0,0 +1,596 @@
// -----------------------------------------------------------------------
// <copyright file="MessageBox.cs">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace WPFCustomMessageBox
{
using System;
using System.Windows;
/// <summary>
/// Displays a message box.
/// </summary>
public static class CustomMessageBox
{
/// <summary>
/// Global parameter to enable (true) or disable (false) the removal of the title bar icon.
/// If you are using a custom window style, the icon removal may cause issues like displaying two title bar (the default windows one and the custom one).
/// </summary>
public static bool RemoveTitleBarIcon = true;
/// <summary>
/// Displays a message box that has a message and returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult Show(string messageBoxText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(null, messageBoxText, string.Empty, null, null, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message and a title bar caption; and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult Show(string messageBoxText, string caption, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(null, messageBoxText, caption, null, null, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box in front of the specified window. The message box displays a message and returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult Show(Window owner, string messageBoxText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(owner, messageBoxText, string.Empty, null, null, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box in front of the specified window. The message box displays a message and title bar caption; and it returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult Show(Window owner, string messageBoxText, string caption, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(owner, messageBoxText, caption, null, null, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, title bar caption, and button; and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="button">A System.Windows.MessageBoxButton value that specifies which button or buttons to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
switch (button)
{
case MessageBoxButton.YesNo:
case MessageBoxButton.YesNoCancel:
return ShowYesNoMessage(null, messageBoxText, caption, null, null, null, null, timeout, timeoutResult);
default:
return ShowOKMessage(null, messageBoxText, caption, null, null, null, timeout, timeoutResult);
}
}
/// <summary>
/// Displays a message box that has a message, title bar caption, and button; and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="button">A System.Windows.MessageBoxButton value that specifies which button or buttons to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult Show(Window owner, string messageBoxText, string caption, MessageBoxButton button, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
switch (button)
{
case MessageBoxButton.YesNo:
case MessageBoxButton.YesNoCancel:
return ShowYesNoMessage(owner, messageBoxText, caption, null, null, null, null, timeout, timeoutResult);
default:
return ShowOKMessage(owner, messageBoxText, caption, null, null, null, timeout, timeoutResult);
}
}
/// <summary>
/// Displays a message box that has a message, title bar caption, button, and icon; and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="button">A System.Windows.MessageBoxButton value that specifies which button or buttons to display.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
switch (button)
{
case MessageBoxButton.YesNo:
case MessageBoxButton.YesNoCancel:
return ShowYesNoMessage(null, messageBoxText, caption, null, null, null, icon, timeout, timeoutResult);
default:
return ShowOKMessage(null, messageBoxText, caption, null, null, icon, timeout, timeoutResult);
}
}
/// <summary>
/// Displays a message box that has a message, title bar caption, button, and icon; and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="button">A System.Windows.MessageBoxButton value that specifies which button or buttons to display.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult Show(Window owner, string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
switch (button)
{
case MessageBoxButton.YesNo:
case MessageBoxButton.YesNoCancel:
return ShowYesNoMessage(owner, messageBoxText, caption, null, null, null, icon, timeout, timeoutResult);
default:
return ShowOKMessage(owner, messageBoxText, caption, null, null, icon, timeout, timeoutResult);
}
}
/// <summary>
/// Displays a message box that has a message, title bar caption, and OK button with a custom System.String value for the button's text; and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="okButtonText">A System.String that specifies the text to display within the OK button.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowOK(string messageBoxText, string caption, string okButtonText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(null, messageBoxText, caption, okButtonText, null, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, title bar caption, and OK button with a custom System.String value for the button's text; and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="okButtonText">A System.String that specifies the text to display within the OK button.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowOK(Window owner, string messageBoxText, string caption, string okButtonText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(owner, messageBoxText, caption, okButtonText, null, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, title bar caption, OK button with a custom System.String value for the button's text, and icon; and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="okButtonText">A System.String that specifies the text to display within the OK button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowOK(string messageBoxText, string caption, string okButtonText, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(null, messageBoxText, caption, okButtonText, null, icon, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, title bar caption, OK button with a custom System.String value for the button's text, and icon; and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="okButtonText">A System.String that specifies the text to display within the OK button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowOK(Window owner, string messageBoxText, string caption, string okButtonText, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(owner, messageBoxText, caption, okButtonText, null, icon, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, and OK/Cancel buttons with custom System.String values for the buttons' text;
/// and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="okButtonText">A System.String that specifies the text to display within the OK button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowOKCancel(string messageBoxText, string caption, string okButtonText, string cancelButtonText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(null, messageBoxText, caption, okButtonText, cancelButtonText, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, and OK/Cancel buttons with custom System.String values for the buttons' text;
/// and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="okButtonText">A System.String that specifies the text to display within the OK button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowOKCancel(Window owner, string messageBoxText, string caption, string okButtonText, string cancelButtonText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(owner, messageBoxText, caption, okButtonText, cancelButtonText, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, OK/Cancel buttons with custom System.String values for the buttons' text, and icon;
/// and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="okButtonText">A System.String that specifies the text to display within the OK button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowOKCancel(string messageBoxText, string caption, string okButtonText, string cancelButtonText, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(null, messageBoxText, caption, okButtonText, cancelButtonText, icon, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, OK/Cancel buttons with custom System.String values for the buttons' text, and icon;
/// and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="okButtonText">A System.String that specifies the text to display within the OK button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowOKCancel(Window owner, string messageBoxText, string caption, string okButtonText, string cancelButtonText, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(owner, messageBoxText, caption, okButtonText, cancelButtonText, icon, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, and Yes/No buttons with custom System.String values for the buttons' text;
/// and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="yesButtonText">A System.String that specifies the text to display within the Yes button.</param>
/// <param name="noButtonText">A System.String that specifies the text to display within the No button.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowYesNo(string messageBoxText, string caption, string yesButtonText, string noButtonText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowYesNoMessage(null, messageBoxText, caption, yesButtonText, noButtonText, null, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, and Yes/No buttons with custom System.String values for the buttons' text;
/// and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="yesButtonText">A System.String that specifies the text to display within the Yes button.</param>
/// <param name="noButtonText">A System.String that specifies the text to display within the No button.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowYesNo(Window owner, string messageBoxText, string caption, string yesButtonText, string noButtonText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowYesNoMessage(owner, messageBoxText, caption, yesButtonText, noButtonText, null, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, Yes/No buttons with custom System.String values for the buttons' text, and icon;
/// and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="yesButtonText">A System.String that specifies the text to display within the Yes button.</param>
/// <param name="noButtonText">A System.String that specifies the text to display within the No button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowYesNo(string messageBoxText, string caption, string yesButtonText, string noButtonText, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowYesNoMessage(null, messageBoxText, caption, yesButtonText, noButtonText, null, icon, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, Yes/No buttons with custom System.String values for the buttons' text, and icon;
/// and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="yesButtonText">A System.String that specifies the text to display within the Yes button.</param>
/// <param name="noButtonText">A System.String that specifies the text to display within the No button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowYesNo(Window owner, string messageBoxText, string caption, string yesButtonText, string noButtonText, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowYesNoMessage(owner, messageBoxText, caption, yesButtonText, noButtonText, null, icon, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, and Yes/No/Cancel buttons with custom System.String values for the buttons' text;
/// and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="yesButtonText">A System.String that specifies the text to display within the Yes button.</param>
/// <param name="noButtonText">A System.String that specifies the text to display within the No button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowYesNoCancel(string messageBoxText, string caption, string yesButtonText, string noButtonText, string cancelButtonText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowYesNoMessage(null, messageBoxText, caption, yesButtonText, noButtonText, cancelButtonText, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, and Yes/No/Cancel buttons with custom System.String values for the buttons' text;
/// and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="yesButtonText">A System.String that specifies the text to display within the Yes button.</param>
/// <param name="noButtonText">A System.String that specifies the text to display within the No button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowYesNoCancel(Window owner, string messageBoxText, string caption, string yesButtonText, string noButtonText, string cancelButtonText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowYesNoMessage(owner, messageBoxText, caption, yesButtonText, noButtonText, cancelButtonText, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, Yes/No/Cancel buttons with custom System.String values for the buttons' text, and icon;
/// and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="yesButtonText">A System.String that specifies the text to display within the Yes button.</param>
/// <param name="noButtonText">A System.String that specifies the text to display within the No button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowYesNoCancel(string messageBoxText, string caption, string yesButtonText, string noButtonText, string cancelButtonText, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowYesNoMessage(null, messageBoxText, caption, yesButtonText, noButtonText, cancelButtonText, icon, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, Yes/No/Cancel buttons with custom System.String values for the buttons' text, and icon;
/// and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="yesButtonText">A System.String that specifies the text to display within the Yes button.</param>
/// <param name="noButtonText">A System.String that specifies the text to display within the No button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowYesNoCancel(Window owner, string messageBoxText, string caption, string yesButtonText, string noButtonText, string cancelButtonText, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowYesNoMessage(owner, messageBoxText, caption, yesButtonText, noButtonText, cancelButtonText, icon, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, OK/Cancel buttons with custom System.String values for the buttons' text, and icon;
/// and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="okButtonText">A System.String that specifies the text to display within the OK button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">The message box will close automatically after given milliseconds</param>
/// <param name="timeoutResult">If the message box closes automatically due to <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
private static MessageBoxResult ShowOKMessage(Window owner, string messageBoxText, string caption, string okButtonText, string cancelButtonText, MessageBoxImage? icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
MessageBoxButton buttonLayout = string.IsNullOrEmpty(cancelButtonText) ? MessageBoxButton.OK : MessageBoxButton.OKCancel;
CustomMessageBoxWindow msg = new CustomMessageBoxWindow(owner, messageBoxText, caption, buttonLayout, icon, RemoveTitleBarIcon);
if (!string.IsNullOrEmpty(okButtonText))
msg.OkButtonText = okButtonText;
if (!string.IsNullOrEmpty(cancelButtonText))
msg.CancelButtonText = cancelButtonText;
ShowDialog(msg, timeout, timeoutResult);
return msg.Result;
}
/// <summary>
/// Displays a message box that has a message, caption, Yes/No/Cancel buttons with custom System.String values for the buttons' text, and icon;
/// and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="yesButtonText">A System.String that specifies the text to display within the Yes button.</param>
/// <param name="noButtonText">A System.String that specifies the text to display within the No button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">The message box will close automatically after given milliseconds</param>
/// <param name="timeoutResult">If the message box closes automatically due to <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
private static MessageBoxResult ShowYesNoMessage(Window owner, string messageBoxText, string caption, string yesButtonText, string noButtonText, string cancelButtonText, MessageBoxImage? icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
MessageBoxButton buttonLayout = string.IsNullOrEmpty(cancelButtonText) ? MessageBoxButton.YesNo : MessageBoxButton.YesNoCancel;
CustomMessageBoxWindow msg = new CustomMessageBoxWindow(owner, messageBoxText, caption, buttonLayout, icon, RemoveTitleBarIcon);
if (!string.IsNullOrEmpty(yesButtonText))
msg.YesButtonText = yesButtonText;
if (!string.IsNullOrEmpty(noButtonText))
msg.NoButtonText = noButtonText;
if (!string.IsNullOrEmpty(cancelButtonText))
msg.CancelButtonText = cancelButtonText;
ShowDialog(msg, timeout, timeoutResult);
return msg.Result;
}
private static void ShowDialog(CustomMessageBoxWindow dialog, int? timeout, MessageBoxResult timeoutResult)
{
if (timeout.HasValue && timeout.Value <= 0)
{
throw new ArgumentOutOfRangeException("timeout", string.Format("Timeout must be greater than 0."));
}
if (timeout.HasValue)
{
//System.Threading.Timer timer = null;
//timer = new System.Threading.Timer(s => { msg.Close(); timer.Dispose(); }, null, timeout.Value, System.Threading.Timeout.Infinite);
System.Timers.Timer timer = new System.Timers.Timer(timeout.Value) { AutoReset = false };
timer.Elapsed += delegate {
Application.Current.Dispatcher.Invoke(new Action(() =>
{
dialog.Result = timeoutResult;
dialog.Close();
//timer.Stop();
//timer.Dispose();
//timer = null;
}));
};
timer.Start();
dialog.ShowDialog();
timer.Stop();
timer.Dispose();
}
else
dialog.ShowDialog();
}
}
}

View File

@@ -0,0 +1,84 @@
<Window x:Class="WPFCustomMessageBox.CustomMessageBoxWindow"
x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
WindowStartupLocation="CenterScreen"
WindowStyle="None"
ShowInTaskbar="False"
ResizeMode="NoResize" SizeToContent="WidthAndHeight"
TextOptions.TextFormattingMode="Display" TextOptions.TextRenderingMode="ClearType" UseLayoutRounding="True"
Title="" MinHeight="155" MaxWidth="470" MinWidth="154"
BorderBrush="DarkGray" BorderThickness="2">
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
</WindowChrome.WindowChrome>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Margin="0,10,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="115"/>
<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" Content="{Binding Path=Title,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"/>
</Grid>
<!-- Make the window width fit the title by embedding the title invisibly -->
<TextBlock Text="{Binding Path=Title,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"
Visibility="Hidden" Height="0" Margin="50 0 0 0" />
<Grid Grid.Row="1" Background="{DynamicResource CustomMessageBox.Static.Background}" MinHeight="69">
<DockPanel>
<Image Name="Image_MessageBox" Width="32" Height="32" HorizontalAlignment="Left" DockPanel.Dock="Left" Margin="30,0,0,0" Visibility="Collapsed"/>
<TextBlock Name="TextBlock_Message" TextWrapping="Wrap" MaxWidth="500" Width="Auto"
VerticalAlignment="Center" Margin="12,20,41,15" />
</DockPanel>
</Grid>
<Grid Grid.Row="2" Background="{DynamicResource CustomMessageBox.Static.Background}" MinHeight="49">
<DockPanel Margin="5,0">
<!-- Cancel Button -->
<Button Name="Button_Cancel" MinWidth="88" MaxWidth="160" Height="26" Margin="5,0" HorizontalAlignment="Right" Visibility="Collapsed" IsCancel="True"
DockPanel.Dock="Right" Click="Button_Cancel_Click" Style="{DynamicResource CustomButtonStyle}">
<Label Name="Label_Cancel" Padding="0" Margin="10,0">_Cancel</Label>
</Button>
<!-- End Cancel Button -->
<!-- No Button -->
<Button Name="Button_No" MinWidth="88" MaxWidth="160" Height="26" Margin="5,0" HorizontalAlignment="Right" Visibility="Collapsed"
DockPanel.Dock="Right" Click="Button_No_Click" Style="{DynamicResource CustomButtonStyle}">
<Label Name="Label_No" Padding="0" Margin="10,0">_No</Label>
</Button>
<!-- End No Button -->
<!-- Yes Button -->
<Button Name="Button_Yes" MinWidth="88" MaxWidth="160" Height="26" Margin="35,0,5,0" HorizontalAlignment="Right" Visibility="Collapsed"
DockPanel.Dock="Right" Click="Button_Yes_Click" Style="{DynamicResource CustomButtonStyle}">
<Label Name="Label_Yes" Padding="0" Margin="10,0">_Yes</Label>
</Button>
<!-- End Yes Button -->
<!-- OK Button -->
<Button Name="Button_OK" MinWidth="88" MaxWidth="160" Margin="35,0,5,0" HorizontalAlignment="Right" Height="26"
Click="Button_OK_Click" Style="{DynamicResource CustomButtonStyle}">
<Label Name="Label_Ok" Padding="0" Margin="10,0">_OK</Label>
</Button>
<!-- End OK Button -->
</DockPanel>
</Grid>
</Grid>
</Window>

View File

@@ -0,0 +1,223 @@
using System;
using System.Drawing;
using System.Windows;
using System.Windows.Input;
namespace WPFCustomMessageBox
{
/// <summary>
/// Interaction logic for ModalDialog.xaml
/// </summary>
internal partial class CustomMessageBoxWindow : Window
{
private bool _removeTitleBarIcon = true;
public string Caption
{
get
{
return Title;
}
set
{
Title = value;
}
}
public string Message
{
get
{
return TextBlock_Message.Text;
}
set
{
TextBlock_Message.Text = value;
}
}
public string OkButtonText
{
get
{
return Label_Ok.Content.ToString();
}
set
{
Label_Ok.Content = value.TryAddKeyboardAccellerator();
}
}
public string CancelButtonText
{
get
{
return Label_Cancel.Content.ToString();
}
set
{
Label_Cancel.Content = value.TryAddKeyboardAccellerator();
}
}
public string YesButtonText
{
get
{
return Label_Yes.Content.ToString();
}
set
{
Label_Yes.Content = value.TryAddKeyboardAccellerator();
}
}
public string NoButtonText
{
get
{
return Label_No.Content.ToString();
}
set
{
Label_No.Content = value.TryAddKeyboardAccellerator();
}
}
public MessageBoxResult Result { get; set; }
internal CustomMessageBoxWindow(Window owner, string message, string caption = null, MessageBoxButton? button = null, MessageBoxImage? image = null, bool removeTitleBarIcon = true)
{
InitializeComponent();
_removeTitleBarIcon = removeTitleBarIcon;
if (owner != null)
{
Owner = owner;
WindowStartupLocation = WindowStartupLocation.CenterOwner;
}
Message = message;
Caption = caption;
DisplayButtons(button ?? MessageBoxButton.OK);
if (image.HasValue)
DisplayImage(image.Value);
else
Image_MessageBox.Visibility = System.Windows.Visibility.Collapsed;
}
protected override void OnSourceInitialized(EventArgs e)
{
if (_removeTitleBarIcon)
Util.RemoveIcon(this);
base.OnSourceInitialized(e);
}
private void DisplayButtons(MessageBoxButton button)
{
switch (button)
{
case MessageBoxButton.OKCancel:
// Hide all but OK, Cancel
Button_OK.Visibility = System.Windows.Visibility.Visible;
Button_OK.Focus();
Button_Cancel.Visibility = System.Windows.Visibility.Visible;
Button_Yes.Visibility = System.Windows.Visibility.Collapsed;
Button_No.Visibility = System.Windows.Visibility.Collapsed;
break;
case MessageBoxButton.YesNo:
// Hide all but Yes, No
Button_Yes.Visibility = System.Windows.Visibility.Visible;
Button_Yes.Focus();
Button_No.Visibility = System.Windows.Visibility.Visible;
Button_OK.Visibility = System.Windows.Visibility.Collapsed;
Button_Cancel.Visibility = System.Windows.Visibility.Collapsed;
break;
case MessageBoxButton.YesNoCancel:
// Hide only OK
Button_Yes.Visibility = System.Windows.Visibility.Visible;
Button_Yes.Focus();
Button_No.Visibility = System.Windows.Visibility.Visible;
Button_Cancel.Visibility = System.Windows.Visibility.Visible;
Button_OK.Visibility = System.Windows.Visibility.Collapsed;
break;
default:
// Hide all but OK
Button_OK.Visibility = System.Windows.Visibility.Visible;
Button_OK.Focus();
Button_Yes.Visibility = System.Windows.Visibility.Collapsed;
Button_No.Visibility = System.Windows.Visibility.Collapsed;
Button_Cancel.Visibility = System.Windows.Visibility.Collapsed;
break;
}
}
private void DisplayImage(MessageBoxImage image)
{
Icon icon;
switch (image)
{
case MessageBoxImage.Exclamation: // Enumeration value 48 - also covers "Warning"
icon = SystemIcons.Exclamation;
break;
case MessageBoxImage.Error: // Enumeration value 16, also covers "Hand" and "Stop"
icon = SystemIcons.Hand;
break;
case MessageBoxImage.Information: // Enumeration value 64 - also covers "Asterisk"
icon = SystemIcons.Information;
break;
case MessageBoxImage.Question:
icon = SystemIcons.Question;
break;
default:
icon = SystemIcons.Information;
break;
}
Image_MessageBox.Source = icon.ToImageSource();
Image_MessageBox.Visibility = System.Windows.Visibility.Visible;
}
private void Button_OK_Click(object sender, RoutedEventArgs e)
{
Result = MessageBoxResult.OK;
Close();
}
private void Button_Cancel_Click(object sender, RoutedEventArgs e)
{
Result = MessageBoxResult.Cancel;
Close();
}
private void Button_Yes_Click(object sender, RoutedEventArgs e)
{
Result = MessageBoxResult.Yes;
Close();
}
private void Button_No_Click(object sender, RoutedEventArgs e)
{
Result = MessageBoxResult.No;
Close();
}
/// <summary>
/// Handler for Title MouseDown event
/// </summary>
private void TitleMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
this.DragMove();
}
}
}

View File

@@ -0,0 +1,23 @@
MIT License
Copyright (c) 2021 Thomas Absenger
Copyright (c) 2013 Evan Wondrasek / Apricity Software LLC
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,92 @@
// -----------------------------------------------------------------------
// <copyright file="Util.cs">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace WPFCustomMessageBox
{
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
/// <summary>
/// TODO: Update summary.
/// </summary>
internal static class Util
{
[DllImport("user32.dll")]
static extern int GetWindowLong(IntPtr hwnd, int index);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter, int x, int y, int width, int height, uint flags);
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam);
const int GWL_EXSTYLE = -20;
const int WS_EX_DLGMODALFRAME = 0x0001;
const int SWP_NOSIZE = 0x0001;
const int SWP_NOMOVE = 0x0002;
const int SWP_NOZORDER = 0x0004;
const int SWP_FRAMECHANGED = 0x0020;
const uint WM_SETICON = 0x0080;
internal static ImageSource ToImageSource(this Icon icon)
{
ImageSource imageSource = Imaging.CreateBitmapSourceFromHIcon(
icon.Handle,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
return imageSource;
}
/// <summary>
/// Keyboard Accellerators are used in Windows to allow easy shortcuts to controls like Buttons and
/// MenuItems. These allow users to press the Alt key, and a shortcut key will be highlighted on the
/// control. If the user presses that key, that control will be activated.
/// This method checks a string if it contains a keyboard accellerator. If it doesn't, it adds one to the
/// beginning of the string. If there are two strings with the same accellerator, Windows handles it.
/// The keyboard accellerator character for WPF is underscore (_). It will not be visible.
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
internal static string TryAddKeyboardAccellerator(this string input)
{
if (input == null)
return input;
const string accellerator = "_"; // This is the default WPF accellerator symbol - used to be & in WinForms
// If it already contains an accellerator, do nothing
if (input.Contains(accellerator)) return input;
return accellerator + input;
}
/// <summary>
/// Removes the icon from the given window.
/// See https://stackoverflow.com/a/18581096
/// </summary>
/// <param name="window">The window to remove the icon from.</param>
internal static void RemoveIcon(Window window)
{
// Get this window's handle
IntPtr hwnd = new WindowInteropHelper(window).Handle;
// Change the extended window style to not show a window icon
int extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_DLGMODALFRAME);
// Update the window's non-client area to reflect the changes
SetWindowPos(hwnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
}
}
}

View File

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 71 KiB

View File

@@ -2,18 +2,18 @@
<PropertyGroup>
<TargetFrameworks>net48;netcoreapp3.1</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<OutputType>WinExe</OutputType>
<UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF>
<ApplicationIcon>Icon.ico</ApplicationIcon>
<Prefer32Bit>true</Prefer32Bit>
<ApplicationIcon>Images\Icon.ico</ApplicationIcon>
<Title>MPF</Title>
<AssemblyName>MPF</AssemblyName>
<Description>Frontend for various dumping programs</Description>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2021</Copyright>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<Version>2.0</Version>
<Version>2.1</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<FileVersion>$(Version)</FileVersion>
<IncludeSource>true</IncludeSource>
@@ -27,11 +27,14 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="1.6.1" GeneratePathProperty="true">
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="1.7.0" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0-preview.3.21201.4" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.0" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0-preview.6.21352.12" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
@@ -39,6 +42,7 @@
</ItemGroup>
<ItemGroup>
<Resource Include="Images\Icon.ico" />
<Resource Include="Images\ring-code-guide.png" />
</ItemGroup>
@@ -49,4 +53,8 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Reference Include="PresentationFramework.Aero" />
</ItemGroup>
</Project>

View File

@@ -9,21 +9,21 @@
<Grid>
<StackPanel Orientation="Vertical">
<Grid Height="22" Margin="10 10 10 0">
<ProgressBar x:Name="ProgressBar" />
<ProgressBar x:Name="ProgressBar" Style="{DynamicResource CustomProgressBarStyle}" />
<TextBlock x:Name="ProgressLabel" Grid.Row="0" Height="22" HorizontalAlignment="Center" VerticalAlignment="Center" Padding="0 2 0 0" />
</Grid>
<Border Height="180" Background="White" BorderBrush="Gainsboro" BorderThickness="1" Margin="10">
<ScrollViewer Name="OutputViewer" SizeChanged="OutputViewerSizeChanged">
<RichTextBox Name="Output" FontFamily="Consolas" Background="#202020" IsReadOnly="true" TextChanged="OnTextChanged" />
<ScrollViewer Name="OutputViewer" SizeChanged="OutputViewerSizeChanged" Template="{DynamicResource CustomScrollViewerControlStyle}">
<RichTextBox Name="Output" FontFamily="Consolas" Background="#FF202020" IsReadOnly="true" TextChanged="OnTextChanged" />
</ScrollViewer>
</Border>
<GroupBox Grid.Row="2" 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="ClearButton" Height="25" Width="80" Content="Clear" Click="OnClearButton" />
<Button Name="SaveButton" Height="25" Width="80" Content="Save" Click="OnSaveButton" />
<Button Name="ClearButton" Height="25" Width="80" Content="Clear" Click="OnClearButton" Style="{DynamicResource CustomButtonStyle}" />
<Button Name="SaveButton" Height="25" Width="80" Content="Save" Click="OnSaveButton" Style="{DynamicResource CustomButtonStyle}" />
<Label/> <!-- Empty label for padding -->
</UniformGrid>
</GroupBox>

View File

@@ -48,11 +48,15 @@ namespace MPF.UserControls
InitializeComponent();
DataContext = this;
var document = new FlowDocument();
var document = new FlowDocument()
{
Background = new SolidColorBrush(Color.FromArgb(0xFF, 0x20, 0x20, 0x20))
};
_paragraph = new Paragraph();
document.Blocks.Add(_paragraph);
Output.Document = document;
// TODO: Can we dynamically add matchers *only* during dumping?
_matchers = new List<Matcher?>();
AddAaruMatchers();
AddDiscImageCreatorMatchers();
@@ -359,7 +363,7 @@ namespace MPF.UserControls
// If we have verbose logs but not enabled, ignore
if (logLevel == LogLevel.VERBOSE && !ViewModels.OptionsViewModel.VerboseLogging)
return;
return;
// Enqueue the text
logQueue.Enqueue(new LogLine(text, logLevel));
@@ -381,7 +385,11 @@ namespace MPF.UserControls
// If we're not processing log formatting, just append and continue
if (!ViewModels.OptionsViewModel.EnableLogFormatting)
{
AppendToTextBox(nextLogLine);
if (nextText.StartsWith("\r"))
ReplaceLastLine(nextLogLine);
else
AppendToTextBox(nextLogLine);
return;
}
@@ -398,6 +406,7 @@ namespace MPF.UserControls
else if (nextText.StartsWith("\r"))
{
ReplaceLastLine(nextLogLine);
lastUsedMatcher = _matchers.FirstOrDefault(m => m?.Matches(nextText.TrimStart('\r')) == true);
}
// If we have a cached matcher and we match
else if (lastUsedMatcher?.Matches(nextText) == true)
@@ -499,7 +508,7 @@ namespace MPF.UserControls
_paragraph.Inlines.Clear();
ResetProgressBar();
}
private void OnSaveButton(object sender, EventArgs e)
{
using (StreamWriter tw = new StreamWriter(File.OpenWrite("console.log")))

View File

@@ -6,12 +6,67 @@
xmlns:local="clr-namespace:MPF"
xmlns:controls="clr-namespace:MPF.UserControls"
mc:Ignorable="d"
Title="Disc Information" Width="515" ResizeMode="NoResize" SizeToContent="Height">
Title="Disc Information" Width="515" WindowStyle="None"
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
BorderBrush="DarkGray" BorderThickness="2">
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
</WindowChrome.WindowChrome>
<Grid>
<StackPanel Orientation="Vertical" Width="500">
<TabControl>
<TabItem x:Name="CommonInfo" Header="Common Info">
<Grid Margin="0,10,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="115"/>
<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>
<TextBlock TextAlignment="Center"><Bold>Disc Information</Bold></TextBlock>
</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}}"
BorderBrush="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}"/>
<MenuItem Header="Close" Click="CloseButtonClick" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
BorderBrush="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
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>
<TabControl Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Style="{DynamicResource CustomTabControlStyle}">
<TabItem x:Name="CommonInfo" Header="Common Info" Style="{DynamicResource CustomTabItemStyle}">
<StackPanel Orientation="Vertical">
<Expander Margin="5" Padding="5" BorderThickness="1" BorderBrush="LightGray" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Common Disc Information" IsExpanded="True">
<StackPanel Orientation="Vertical">
@@ -27,7 +82,8 @@
</Grid.ColumnDefinitions>
<Label x:Name="CategoryLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Category" />
<ComboBox x:Name="CategoryComboBox" Grid.Row="0" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" ItemsSource="{Binding Path=Categories}" SelectedIndex="0" />
<ComboBox x:Name="CategoryComboBox" Grid.Row="0" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" ItemsSource="{Binding Categories}"
SelectedIndex="0" Style="{DynamicResource CustomComboBoxStyle}" />
</Grid>
<Grid>
@@ -37,7 +93,8 @@
</Grid.ColumnDefinitions>
<Label x:Name="RegionLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Region" />
<ComboBox x:Name="RegionComboBox" Grid.Row="0" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" ItemsSource="{Binding Path=Regions}" SelectedIndex="0" />
<ComboBox x:Name="RegionComboBox" Grid.Row="0" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" ItemsSource="{Binding Regions}"
SelectedIndex="0" Style="{DynamicResource CustomComboBoxStyle}" />
</Grid>
<Grid>
@@ -47,10 +104,11 @@
</Grid.ColumnDefinitions>
<Label x:Name="LanguagesLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Languages" />
<ComboBox x:Name="LanguagesComboBox" Grid.Row="0" Grid.Column="1" Height="24" HorizontalAlignment="Stretch" ItemsSource="{Binding Path=Languages}" SelectedIndex="0">
<ComboBox x:Name="LanguagesComboBox" Grid.Row="0" Grid.Column="1" Height="24" HorizontalAlignment="Stretch" ItemsSource="{Binding Languages}"
SelectedIndex="0" Style="{DynamicResource CustomComboBoxStyle}">
<ComboBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Path=Name}" IsChecked="{Binding IsChecked}" />
<CheckBox Content="{Binding Name}" IsChecked="{Binding IsChecked}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
@@ -63,10 +121,11 @@
</Grid.ColumnDefinitions>
<Label x:Name="LanguageSelectionLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Language Selection Via" />
<ComboBox x:Name="LanguageSelectionComboBox" Grid.Row="0" Grid.Column="1" Height="24" HorizontalAlignment="Stretch" ItemsSource="{Binding Path=LanguageSelections}" SelectedIndex="0">
<ComboBox x:Name="LanguageSelectionComboBox" Grid.Row="0" Grid.Column="1" Height="24" HorizontalAlignment="Stretch" ItemsSource="{Binding LanguageSelections}"
SelectedIndex="0" Style="{DynamicResource CustomComboBoxStyle}">
<ComboBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Path=Name}" IsChecked="{Binding IsChecked}" />
<CheckBox Content="{Binding Name}" IsChecked="{Binding IsChecked}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
@@ -86,8 +145,8 @@
</Expander>
</StackPanel>
</TabItem>
<TabItem x:Name="L0Info" Header="Data/L0 Info">
<TabItem x:Name="L0Info" Header="Data/L0 Info" Style="{DynamicResource CustomTabItemStyle}">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="L0MasteringRing" Label="Data/L0 Mastering Ring" Tab="True"/>
<controls:UserInput x:Name="L0MasteringSID" Label="Data/L0 Mastering SID"/>
@@ -96,8 +155,8 @@
<controls:UserInput x:Name="L0AdditionalMould" Label="Data/L0 Additional Mould"/>
</StackPanel>
</TabItem>
<TabItem x:Name="L1Info" Header="Label/L1 Info">
<TabItem x:Name="L1Info" Header="Label/L1 Info" Style="{DynamicResource CustomTabItemStyle}">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="L1MasteringRing" Label="Label/L1 Mastering Ring" Tab="True"/>
<controls:UserInput x:Name="L1MasteringSID" Label="Label/L1 Mastering SID"/>
@@ -107,7 +166,7 @@
</StackPanel>
</TabItem>
<TabItem x:Name="L2Info" Header="L2 Info" Visibility="Collapsed">
<TabItem x:Name="L2Info" Header="L2 Info" Visibility="Collapsed" Style="{DynamicResource CustomTabItemStyle}">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="L2MasteringRing" Label="L2 Mastering Ring" Tab="True"/>
<controls:UserInput x:Name="L2MasteringSID" Label="L2 Mastering SID"/>
@@ -115,7 +174,7 @@
</StackPanel>
</TabItem>
<TabItem x:Name="L3Info" Header="L3 Info" Visibility="Collapsed">
<TabItem x:Name="L3Info" Header="L3 Info" Visibility="Collapsed" Style="{DynamicResource CustomTabItemStyle}">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="L3MasteringRing" Label="L3 Mastering Ring" Tab="True"/>
<controls:UserInput x:Name="L3MasteringSID" Label="L3 Mastering SID"/>
@@ -127,9 +186,12 @@
<!-- Accept / Cancel -->
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<UniformGrid Columns="3" Margin="5,5,5,5" Height="28">
<Button Name="AcceptButton" Grid.Column="0" Height="25" Width="120" IsDefault="True" Content="Accept" Click="OnAcceptClick" />
<Button Name="CancelButton" Grid.Column="1" Height="25" Width="120" IsCancel="True" Content="Cancel" Click="OnCancelClick" />
<Button Name="RingCodeGuideButton" Grid.Column="2" Height="25" Width="120" Content="Ring Code Guide" Click="OnRingCodeGuideClick" />
<Button Name="AcceptButton" Grid.Column="0" Height="25" Width="120" IsDefault="True" Content="Accept"
Click="OnAcceptClick" Style="{DynamicResource CustomButtonStyle}" />
<Button Name="CancelButton" Grid.Column="1" Height="25" Width="120" IsCancel="True" Content="Cancel"
Click="OnCancelClick" Style="{DynamicResource CustomButtonStyle}" />
<Button Name="RingCodeGuideButton" Grid.Column="2" Height="25" Width="120" Content="Ring Code Guide"
Click="OnRingCodeGuideClick" Style="{DynamicResource CustomButtonStyle}" />
</UniformGrid>
</GroupBox>
</StackPanel>

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using MPF.Data;
namespace MPF.Windows
@@ -55,6 +56,13 @@ namespace MPF.Windows
/// </summary>
private void ManipulateFields()
{
// Sony-printed discs have layers in the opposite order
var system = SubmissionInfo?.CommonDiscInfo?.System;
bool reverseOrder = (system == KnownSystem.SonyPlayStation2
|| system == KnownSystem.SonyPlayStation3
|| system == KnownSystem.SonyPlayStation4
|| system == KnownSystem.SonyPlayStation5);
// Different media types mean different fields available
switch (SubmissionInfo?.CommonDiscInfo?.Media)
{
@@ -87,7 +95,7 @@ namespace MPF.Windows
L2Info.Visibility = Visibility.Visible;
L3Info.Visibility = Visibility.Visible;
L0Info.Header = "Layer 0 (Inner)";
L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
L0MasteringRing.Label = "Mastering Ring";
L0MasteringSID.Label = "Mastering SID";
L0Toolstamp.Label = "Toolstamp/Mastering Code";
@@ -106,7 +114,7 @@ namespace MPF.Windows
L2MasteringSID.Label = "Mastering SID";
L2Toolstamp.Label = "Toolstamp/Mastering Code";
L3Info.Header = "Layer 3 (Outer)";
L3Info.Header = reverseOrder ? "Layer 3 (Inner)" : "Layer 3 (Outer)";
L3MasteringRing.Label = "Mastering Ring";
L3MasteringSID.Label = "Mastering SID";
L3Toolstamp.Label = "Toolstamp/Mastering Code";
@@ -117,7 +125,7 @@ namespace MPF.Windows
{
L2Info.Visibility = Visibility.Visible;
L0Info.Header = "Layer 0 (Inner)";
L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
L0MasteringRing.Label = "Mastering Ring";
L0MasteringSID.Label = "Mastering SID";
L0Toolstamp.Label = "Toolstamp/Mastering Code";
@@ -131,7 +139,7 @@ namespace MPF.Windows
L1MouldSID.Label = "Label Side Mould SID";
L1AdditionalMould.Label = "Label Side Additional Mould";
L2Info.Header = "Layer 2 (Outer)";
L2Info.Header = reverseOrder ? "Layer 2 (Inner)" : "Layer 2 (Outer)";
L2MasteringRing.Label = "Mastering Ring";
L2MasteringSID.Label = "Mastering SID";
L2Toolstamp.Label = "Toolstamp/Mastering Code";
@@ -140,14 +148,14 @@ namespace MPF.Windows
// Double-layer discs
else if (SubmissionInfo?.SizeAndChecksums?.Layerbreak != default(long))
{
L0Info.Header = "Layer 0 (Inner)";
L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
L0MasteringRing.Label = "Mastering Ring";
L0MasteringSID.Label = "Mastering SID";
L0Toolstamp.Label = "Toolstamp/Mastering Code";
L0MouldSID.Label = "Data Side Mould SID";
L0AdditionalMould.Label = "Data Side Additional Mould";
L1Info.Header = "Layer 1 (Outer)";
L1Info.Header = reverseOrder ? "Layer 1 (Inner)" : "Layer 1 (Outer)";
L1MasteringRing.Label = "Mastering Ring";
L1MasteringSID.Label = "Mastering SID";
L1Toolstamp.Label = "Toolstamp/Mastering Code";
@@ -185,7 +193,7 @@ namespace MPF.Windows
}
// Different systems mean different fields available
switch (SubmissionInfo?.CommonDiscInfo?.System)
switch (system)
{
case KnownSystem.SonyPlayStation2:
LanguageSelectionGrid.Visibility = Visibility.Visible;
@@ -284,6 +292,23 @@ namespace MPF.Windows
#region Event Handlers
/// <summary>
/// Handler for CloseButton Click event
/// </summary>
private void CloseButtonClick(object sender, RoutedEventArgs e)
{
this.DialogResult = false;
Close();
}
/// <summary>
/// Handler for MinimizeButton Click event
/// </summary>
private void MinimizeButtonClick(object sender, RoutedEventArgs e)
{
this.WindowState = WindowState.Minimized;
}
/// <summary>
/// Handler for AcceptButton Click event
/// </summary>
@@ -316,6 +341,15 @@ namespace MPF.Windows
ringCodeGuideWindow.Show();
}
/// <summary>
/// Handler for Title MouseDown event
/// </summary>
private void TitleMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
this.DragMove();
}
#endregion
}
}

View File

@@ -6,24 +6,97 @@
xmlns:local="clr-namespace:MPF"
xmlns:controls="clr-namespace:MPF.UserControls"
mc:Ignorable="d"
Title="Media Preservation Frontend" Width="600" WindowStartupLocation="CenterScreen" ResizeMode="CanMinimize" SizeToContent="Height">
Title="Media Preservation Frontend" Width="600" WindowStyle="None"
WindowStartupLocation="CenterScreen" ResizeMode="CanMinimize" SizeToContent="Height"
BorderBrush="DarkGray" BorderThickness="2">
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
</WindowChrome.WindowChrome>
<Grid>
<StackPanel Orientation="Vertical">
<StackPanel VerticalAlignment="Top" Grid.ColumnSpan="4">
<Menu Width="Auto" Height="20" >
<MenuItem Header="_File">
<MenuItem x:Name="AppExit" Header="E_xit" HorizontalAlignment="Left" Width="185" Click="AppExitClick" />
</MenuItem>
<MenuItem Header="_Tools">
<MenuItem x:Name="OptionsMenuItem" Header="_Options" HorizontalAlignment="Left" Width="185" Click="OptionsMenuItemClick" />
</MenuItem>
<MenuItem Header="_Help">
<MenuItem x:Name="About" Header="_About" HorizontalAlignment="Left" Width="185" Click="AboutClick" />
<MenuItem x:Name="CheckForUpdatesMenuItem" Header="_Check for Updates" HorizontalAlignment="Left" Width="185" Click="CheckForUpdatesClick" />
</MenuItem>
</Menu>
</StackPanel>
<Grid Margin="0,2,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="115"/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="/Images/Icon.ico" Height="20" Width="20" Margin="1" />
<StackPanel Panel.ZIndex="1" Grid.Column="1" VerticalAlignment="Center" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
<Menu x:Name="TopMenuBar" Width="Auto" Height="20"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}">
<MenuItem x:Name="FileMenuItem" Header="_File"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}">
<MenuItem x:Name="AppExitMenuItem" Header="E_xit" HorizontalAlignment="Left" Width="185" Click="AppExitClick"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}" />
</MenuItem>
<MenuItem x:Name="ToolsMenuItem" Header="_Tools"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}">
<MenuItem x:Name="OptionsMenuItem" Header="_Options" HorizontalAlignment="Left" Width="185" Click="OptionsMenuItemClick"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}" />
</MenuItem>
<MenuItem x:Name="HelpMenuItem" Header="_Help"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}">
<MenuItem x:Name="AboutMenuItem" Header="_About" HorizontalAlignment="Left" Width="185" Click="AboutClick"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}" />
<MenuItem x:Name="CheckForUpdatesMenuItem" Header="_Check for Updates" HorizontalAlignment="Left" Width="185" Click="CheckForUpdatesClick"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}" />
</MenuItem>
</Menu>
</StackPanel>
<Label Panel.ZIndex="0" Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" MouseDown="TitleMouseDown">
<Label.Content>
<TextBlock TextAlignment="Center"><Bold>Media Preservation Frontend</Bold></TextBlock>
</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="Settings">
<Grid Margin="5,5,5,5">
@@ -40,40 +113,41 @@
<RowDefinition/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="System/Media Type" />
<ComboBox x:Name="SystemTypeComboBox" Grid.Row="0" Grid.Column="1" Height="22" Width="250" HorizontalAlignment="Left" ItemsSource="{Binding Path=Systems}" SelectedIndex="0">
<Label x:Name="SystemMediaTypeLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="System/Media Type" />
<ComboBox x:Name="SystemTypeComboBox" Grid.Row="0" Grid.Column="1" Height="22" Width="250" HorizontalAlignment="Left" ItemsSource="{Binding Systems}" SelectedIndex="0" Style="{DynamicResource CustomComboBoxStyle}">
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsHeader}" Value="True">
<DataTrigger Binding="{Binding IsHeader}" Value="True">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
<ComboBox x:Name="MediaTypeComboBox" Grid.Row="0" Grid.Column="1" Height="22" Width="140" HorizontalAlignment="Right" />
<ComboBox x:Name="MediaTypeComboBox" Grid.Row="0" Grid.Column="1" Height="22" Width="140" HorizontalAlignment="Right" Style="{DynamicResource CustomComboBoxStyle}" />
<Label Grid.Row="1" Grid.Column="0" VerticalAlignment="Center">Output Filename</Label>
<Label x:Name="OutputFilenameLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Content="Output Filename"/>
<TextBox x:Name="OutputFilenameTextBox" Grid.Row="1" Grid.Column="1" Height="22" VerticalContentAlignment="Center" />
<Label Grid.Row="2" Grid.Column="0" VerticalAlignment="Center">Output Directory</Label>
<Label x:Name="OutputDirectoryLabel" Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Content="Output Directory"/>
<TextBox x:Name="OutputDirectoryTextBox" Grid.Row="2" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center" />
<Button x:Name="OutputDirectoryBrowseButton" Grid.Row="2" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse" Click="OutputDirectoryBrowseButtonClick"/>
<Button x:Name="OutputDirectoryBrowseButton" Grid.Row="2" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
Click="OutputDirectoryBrowseButtonClick" Style="{DynamicResource CustomButtonStyle}"/>
<Label Grid.Row="3" Grid.Column="0" VerticalAlignment="Center">Drive Letter</Label>
<ComboBox x:Name="DriveLetterComboBox" Grid.Row="3" Grid.Column="1" Height="22" Width="60" HorizontalAlignment="Left">
<Label x:Name="DriveLetterLabel" Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Content="Drive Letter"/>
<ComboBox x:Name="DriveLetterComboBox" Grid.Row="3" Grid.Column="1" Height="22" Width="60" HorizontalAlignment="Left" Style="{DynamicResource CustomComboBoxStyle}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Letter}" />
<TextBlock Text="{Binding Letter}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Label Grid.Row="4" Grid.Column="0" VerticalAlignment="Center">Drive Speed</Label>
<ComboBox x:Name="DriveSpeedComboBox" Grid.Row="4" Grid.Column="1" Height="22" Width="60" HorizontalAlignment="Left" />
<Label x:Name="DriveSpeedLabel" Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" Content="Drive Speed"/>
<ComboBox x:Name="DriveSpeedComboBox" Grid.Row="4" Grid.Column="1" Height="22" Width="60" HorizontalAlignment="Left" Style="{DynamicResource CustomComboBoxStyle}" />
<Label Grid.Row="5" Grid.Column="0" VerticalAlignment="Center">Parameters</Label>
<Label x:Name="ParametersLabel" Grid.Row="5" Grid.Column="0" VerticalAlignment="Center" Content="Parameters"/>
<TextBox x:Name="ParametersTextBox" Grid.Row="5" Grid.Column="1" Height="22" Width="370" HorizontalAlignment="Left" IsEnabled="False" VerticalContentAlignment="Center" />
<CheckBox x:Name="EnableParametersCheckBox" Grid.Row="5" Grid.Column="1" Height="22" HorizontalAlignment="Right" IsChecked="False" Click="EnableParametersCheckBoxClick" />
</Grid>
@@ -81,9 +155,12 @@
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" Header="Controls">
<UniformGrid Columns="3" Margin="5,5,5,5">
<Button x:Name="StartStopButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" IsDefault="True" Content="Start Dumping" Click="StartStopButtonClick" IsEnabled="False" />
<Button x:Name="MediaScanButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Scan for disks" Click="MediaScanButtonClick" />
<Button x:Name="CopyProtectScanButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Scan for protection" Click="CopyProtectScanButtonClick" />
<Button x:Name="StartStopButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" IsDefault="True" Content="Start Dumping"
Click="StartStopButtonClick" IsEnabled="False" Style="{DynamicResource CustomButtonStyle}" />
<Button x:Name="MediaScanButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Scan for disks"
Click="MediaScanButtonClick" Style="{DynamicResource CustomButtonStyle}" />
<Button x:Name="CopyProtectScanButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Scan for protection"
Click="CopyProtectScanButtonClick" Style="{DynamicResource CustomButtonStyle}" />
</UniformGrid>
</GroupBox>

View File

@@ -5,10 +5,13 @@ using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using WinForms = System.Windows.Forms;
using BurnOutSharp;
using MPF.Data;
using MPF.Utilities;
using WPFCustomMessageBox;
namespace MPF.Windows
{
@@ -73,6 +76,12 @@ namespace MPF.Windows
StartStopButton.IsEnabled = false;
MediaScanButton.IsEnabled = false;
CopyProtectScanButton.IsEnabled = false;
// Set the UI color scheme according to the options
if (Options.EnableDarkMode)
EnableDarkMode();
else
DisableDarkMode();
}
#region Population
@@ -168,6 +177,12 @@ namespace MPF.Windows
// Disable the dumping button
StartStopButton.IsEnabled = false;
// Set the UI color scheme according to the options
if (Options.EnableDarkMode)
EnableDarkMode();
else
DisableDarkMode();
// Remove event handlers to ensure ordering
if (removeEventHandlers)
RemoveEventHandlers();
@@ -281,6 +296,140 @@ namespace MPF.Windows
CopyProtectScanButton.IsEnabled = true;
}
/// <summary>
/// Recolor all UI elements back to normal values
/// </summary>
private void DisableDarkMode()
{
// Handle application-wide resources
Application.Current.Resources[SystemColors.ActiveBorderBrushKey] = null;
Application.Current.Resources[SystemColors.ControlBrushKey] = null;
Application.Current.Resources[SystemColors.ControlTextBrushKey] = null;
Application.Current.Resources[SystemColors.GrayTextBrushKey] = null;
Application.Current.Resources[SystemColors.WindowBrushKey] = null;
Application.Current.Resources[SystemColors.WindowTextBrushKey] = null;
// Handle Button-specific resources
Application.Current.Resources["Button.Disabled.Background"] = new SolidColorBrush(Color.FromArgb(0xFF, 0xF4, 0xF4, 0xF4));
Application.Current.Resources["Button.MouseOver.Background"] = new SolidColorBrush(Color.FromArgb(0xFF, 0xBE, 0xE6, 0xFD));
Application.Current.Resources["Button.Pressed.Background"] = new SolidColorBrush(Color.FromArgb(0xFF, 0xC4, 0xE5, 0xF6));
Application.Current.Resources["Button.Static.Background"] = new SolidColorBrush(Color.FromArgb(0xFF, 0xDD, 0xDD, 0xDD));
// Handle ComboBox-specific resources
Application.Current.Resources["ComboBox.Disabled.Background"] = new SolidColorBrush(Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0));
Application.Current.Resources["ComboBox.Disabled.Editable.Background"] = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
Application.Current.Resources["ComboBox.Disabled.Editable.Button.Background"] = Brushes.Transparent;
Application.Current.Resources["ComboBox.MouseOver.Background"] = new LinearGradientBrush(
Color.FromArgb(0xFF, 0xEC, 0xF4, 0xFC),
Color.FromArgb(0xFF, 0xDC, 0xEC, 0xFC),
new Point(0, 0),
new Point(0, 1));
Application.Current.Resources["ComboBox.MouseOver.Editable.Background"] = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
Application.Current.Resources["ComboBox.MouseOver.Editable.Button.Background"] = new LinearGradientBrush(
Color.FromArgb(0xFF, 0xEB, 0xF4, 0xFC),
Color.FromArgb(0xFF, 0xDC, 0xEC, 0xFC),
new Point(0, 0),
new Point(0, 1));
Application.Current.Resources["ComboBox.Pressed.Background"] = new LinearGradientBrush(
Color.FromArgb(0xFF, 0xDA, 0xEC, 0xFC),
Color.FromArgb(0xFF, 0xC4, 0xE0, 0xFC),
new Point(0, 0),
new Point(0, 1));
Application.Current.Resources["ComboBox.Pressed.Editable.Background"] = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
Application.Current.Resources["ComboBox.Pressed.Editable.Button.Background"] = new LinearGradientBrush(
Color.FromArgb(0xFF, 0xDA, 0xEB, 0xFC),
Color.FromArgb(0xFF, 0xC4, 0xE0, 0xFC),
new Point(0, 0),
new Point(0, 1));
Application.Current.Resources["ComboBox.Static.Background"] = new LinearGradientBrush(
Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0),
Color.FromArgb(0xFF, 0xE5, 0xE5, 0xE5),
new Point(0, 0),
new Point(0, 1));
Application.Current.Resources["ComboBox.Static.Editable.Background"] = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
Application.Current.Resources["ComboBox.Static.Editable.Button.Background"] = Brushes.Transparent;
Application.Current.Resources["TextBox.Static.Background"] = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
// Handle CustomMessageBox-specific resources
Application.Current.Resources["CustomMessageBox.Static.Background"] = null;
// Handle MenuItem-specific resources
Application.Current.Resources["MenuItem.SubMenu.Background"] = new SolidColorBrush(Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0));
Application.Current.Resources["MenuItem.SubMenu.Border"] = Brushes.DarkGray;
// Handle ProgressBar-specific resources
Application.Current.Resources["ProgressBar.Background"] = new SolidColorBrush(Color.FromArgb(0xFF, 0xE6, 0xE6, 0xE6));
// Handle ScrollViewer-specific resources
Application.Current.Resources["ScrollViewer.ScrollBar.Background"] = Brushes.LightGray;
// Handle TabItem-specific resources
Application.Current.Resources["TabItem.Selected.Background"] = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
Application.Current.Resources["TabItem.Static.Background"] = new LinearGradientBrush(
Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0),
Color.FromArgb(0xFF, 0xE5, 0xE5, 0xE5),
new Point(0, 0),
new Point(0, 1));
Application.Current.Resources["TabItem.Static.Border"] = Brushes.DarkGray;
}
/// <summary>
/// Recolor all UI elements for dark mode
/// </summary>
private void EnableDarkMode()
{
// Setup needed brushes
var darkModeBrush = new SolidColorBrush();
darkModeBrush.Color = Color.FromArgb(0xff, 0x20, 0x20, 0x20);
// Handle application-wide resources
Application.Current.Resources[SystemColors.ActiveBorderBrushKey] = Brushes.Black;
Application.Current.Resources[SystemColors.ControlBrushKey] = darkModeBrush;
Application.Current.Resources[SystemColors.ControlTextBrushKey] = Brushes.White;
Application.Current.Resources[SystemColors.GrayTextBrushKey] = Brushes.DarkGray;
Application.Current.Resources[SystemColors.WindowBrushKey] = darkModeBrush;
Application.Current.Resources[SystemColors.WindowTextBrushKey] = Brushes.White;
// Handle Button-specific resources
Application.Current.Resources["Button.Disabled.Background"] = darkModeBrush;
Application.Current.Resources["Button.MouseOver.Background"] = darkModeBrush;
Application.Current.Resources["Button.Pressed.Background"] = darkModeBrush;
Application.Current.Resources["Button.Static.Background"] = darkModeBrush;
// Handle ComboBox-specific resources
Application.Current.Resources["ComboBox.Disabled.Background"] = darkModeBrush;
Application.Current.Resources["ComboBox.Disabled.Editable.Background"] = darkModeBrush;
Application.Current.Resources["ComboBox.Disabled.Editable.Button.Background"] = darkModeBrush;
Application.Current.Resources["ComboBox.MouseOver.Background"] = darkModeBrush;
Application.Current.Resources["ComboBox.MouseOver.Editable.Background"] = darkModeBrush;
Application.Current.Resources["ComboBox.MouseOver.Editable.Button.Background"] = darkModeBrush;
Application.Current.Resources["ComboBox.Pressed.Background"] = darkModeBrush;
Application.Current.Resources["ComboBox.Pressed.Editable.Background"] = darkModeBrush;
Application.Current.Resources["ComboBox.Pressed.Editable.Button.Background"] = darkModeBrush;
Application.Current.Resources["ComboBox.Static.Background"] = darkModeBrush;
Application.Current.Resources["ComboBox.Static.Editable.Background"] = darkModeBrush;
Application.Current.Resources["ComboBox.Static.Editable.Button.Background"] = darkModeBrush;
Application.Current.Resources["TextBox.Static.Background"] = darkModeBrush;
// Handle CustomMessageBox-specific resources
Application.Current.Resources["CustomMessageBox.Static.Background"] = darkModeBrush;
// Handle MenuItem-specific resources
Application.Current.Resources["MenuItem.SubMenu.Background"] = darkModeBrush;
Application.Current.Resources["MenuItem.SubMenu.Border"] = Brushes.DarkGray;
// Handle ProgressBar-specific resources
Application.Current.Resources["ProgressBar.Background"] = darkModeBrush;
// Handle ScrollViewer-specific resources
Application.Current.Resources["ScrollViewer.ScrollBar.Background"] = darkModeBrush;
// Handle TabItem-specific resources
Application.Current.Resources["TabItem.Selected.Background"] = darkModeBrush;
Application.Current.Resources["TabItem.Static.Background"] = darkModeBrush;
Application.Current.Resources["TabItem.Static.Border"] = Brushes.DarkGray;
}
#endregion
#region Helpers
@@ -593,21 +742,21 @@ namespace MPF.Windows
var progress = new Progress<ProtectionProgress>();
progress.ProgressChanged += ProgressUpdated;
(bool success, string output) = await Validators.RunProtectionScanOnPath(drive.Letter + ":\\", progress);
(bool success, string output) = await Validators.RunProtectionScanOnPath(drive.Letter + ":\\", this.Options, progress);
// If SmartE is detected on the current disc, remove `/sf` from the flags for DIC only
if (Env.Options.InternalProgram == InternalProgram.DiscImageCreator && output.Contains("SmartE"))
{
((DiscImageCreator.Parameters)Env.Parameters)[DiscImageCreator.Flag.ScanFileProtect] = false;
((DiscImageCreator.Parameters)Env.Parameters)[DiscImageCreator.FlagStrings.ScanFileProtect] = false;
LogOutput.VerboseLogLn($"SmartE detected, removing {DiscImageCreator.FlagStrings.ScanFileProtect} from parameters");
}
if (!LogPanel.IsExpanded)
{
if (success)
MessageBox.Show(output, "Detected Protection(s)", MessageBoxButton.OK, MessageBoxImage.Information);
CustomMessageBox.Show(output, "Detected Protection(s)", MessageBoxButton.OK, MessageBoxImage.Information);
else
MessageBox.Show("An exception occurred, see the log for details", "Error!", MessageBoxButton.OK, MessageBoxImage.Error);
CustomMessageBox.Show("An exception occurred, see the log for details", "Error!", MessageBoxButton.OK, MessageBoxImage.Error);
}
if (success)
@@ -712,7 +861,7 @@ namespace MPF.Windows
// If still in custom parameter mode, check that users meant to continue or not
if (EnableParametersCheckBox.IsChecked == true)
{
MessageBoxResult result = MessageBox.Show("It looks like you have custom parameters that have not been saved. Would you like to apply those changes before starting to dump?", "Custom Changes", MessageBoxButton.YesNoCancel, MessageBoxImage.Question);
MessageBoxResult result = CustomMessageBox.Show("It looks like you have custom parameters that have not been saved. Would you like to apply those changes before starting to dump?", "Custom Changes", MessageBoxButton.YesNoCancel, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
EnableParametersCheckBox.IsChecked = false;
@@ -729,7 +878,7 @@ namespace MPF.Windows
// Validate that the user explicitly wants an inactive drive to be considered for dumping
if (!Env.Drive.MarkedActive)
{
MessageBoxResult mbresult = MessageBox.Show("The currently selected drive does not appear to contain a disc! Are you sure you want to continue?", "Missing Disc", MessageBoxButton.YesNo, MessageBoxImage.Exclamation);
MessageBoxResult mbresult = CustomMessageBox.Show("The currently selected drive does not appear to contain a disc! Are you sure you want to continue?", "Missing Disc", MessageBoxButton.YesNo, MessageBoxImage.Exclamation);
if (mbresult == MessageBoxResult.No || mbresult == MessageBoxResult.Cancel || mbresult == MessageBoxResult.None)
{
LogOutput.LogLn("Dumping aborted!");
@@ -738,10 +887,10 @@ namespace MPF.Windows
}
// If a complete dump already exists
(bool foundFiles, List<string> _) = Env.FoundAllFiles();
(bool foundFiles, List<string> _) = Env.FoundAllFiles(true);
if (foundFiles)
{
MessageBoxResult mbresult = MessageBox.Show("A complete dump already exists! Are you sure you want to overwrite?", "Overwrite?", MessageBoxButton.YesNo, MessageBoxImage.Exclamation);
MessageBoxResult mbresult = CustomMessageBox.Show("A complete dump already exists! Are you sure you want to overwrite?", "Overwrite?", MessageBoxButton.YesNo, MessageBoxImage.Exclamation);
if (mbresult == MessageBoxResult.No || mbresult == MessageBoxResult.Cancel || mbresult == MessageBoxResult.None)
{
LogOutput.LogLn("Dumping aborted!");
@@ -825,7 +974,7 @@ namespace MPF.Windows
+ $"{Environment.NewLine}Version {Tools.GetCurrentVersion()}";
LogOutput.SecretLogLn(aboutText);
MessageBox.Show(aboutText, "About", MessageBoxButton.OK, MessageBoxImage.Information);
CustomMessageBox.Show(this, aboutText, "About", MessageBoxButton.OK, MessageBoxImage.Information);
}
/// <summary>
@@ -847,7 +996,16 @@ namespace MPF.Windows
if (different)
Clipboard.SetText(url);
MessageBox.Show(message, "Version Update Check", MessageBoxButton.OK, different ? MessageBoxImage.Exclamation : MessageBoxImage.Information);
LogOutput.SecretLogLn(message);
CustomMessageBox.Show(this, message, "Version Update Check", MessageBoxButton.OK, different ? MessageBoxImage.Exclamation : MessageBoxImage.Information);
}
/// <summary>
/// Handler for CloseButton Click event
/// </summary>
private void CloseButtonClick(object sender, RoutedEventArgs e)
{
Application.Current.Shutdown();
}
/// <summary>
@@ -913,6 +1071,14 @@ namespace MPF.Windows
InitializeUIValues(removeEventHandlers: true, rescanDrives: true);
}
/// <summary>
/// Handler for MinimizeButton Click event
/// </summary>
private void MinimizeButtonClick(object sender, RoutedEventArgs e)
{
this.WindowState = WindowState.Minimized;
}
/// <summary>
/// Handler for MainWindow OnContentRendered event
/// </summary>
@@ -953,11 +1119,7 @@ namespace MPF.Windows
/// </summary>
private void OptionsMenuItemClick(object sender, RoutedEventArgs e)
{
var optionsWindow = new OptionsWindow(Options)
{
Owner = this,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
};
var optionsWindow = new OptionsWindow(Options) { Owner = this };
optionsWindow.Closed += OnOptionsUpdated;
optionsWindow.Show();
}
@@ -1074,6 +1236,15 @@ namespace MPF.Windows
EnsureDiscInformation();
}
/// <summary>
/// Handler for Title MouseDown event
/// </summary>
private void TitleMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
this.DragMove();
}
#endregion
}
}

View File

@@ -6,12 +6,75 @@
xmlns:local="clr-namespace:MPF"
xmlns:controls="clr-namespace:MPF.UserControls"
mc:Ignorable="d"
Title="Options" Width="515.132" ResizeMode="NoResize" SizeToContent="Height">
Title="Options" Width="515.132" WindowStyle="None"
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
BorderBrush="DarkGray" BorderThickness="2">
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
</WindowChrome.WindowChrome>
<Grid>
<StackPanel Orientation="Vertical">
<TabControl Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
<TabItem Header="Paths">
<Grid Margin="0,10,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="115"/>
<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>
<TextBlock TextAlignment="Center"><Bold>Options</Bold></TextBlock>
</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>
<TabControl Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Style="{DynamicResource CustomTabControlStyle}">
<TabItem Header="User Interface" Style="{DynamicResource CustomTabItemStyle}">
<UniformGrid Columns="2" Rows="3">
<CheckBox VerticalAlignment="Center" Content="Enable Dark Mode"
IsChecked="{Binding Options.EnableDarkMode}"
ToolTip="(Experimental) Enable dark mode across the entire application" Margin="0,4"
/>
</UniformGrid>
</TabItem>
<TabItem Header="Paths" Style="{DynamicResource CustomTabItemStyle}">
<Grid Margin="5,5,5,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
@@ -27,46 +90,51 @@
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Aaru Path" />
<TextBox x:Name="AaruPathTextBox" Grid.Row="0" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" Text="{Binding Path=Options.AaruPath}" VerticalContentAlignment="Center" />
<Button x:Name="AaruPathButton" Grid.Row="0" Grid.Column="2" Height="22" Width="22" Content="..." Click="BrowseForPathClick" />
<TextBox x:Name="AaruPathTextBox" Grid.Row="0" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" Text="{Binding Options.AaruPath}" VerticalContentAlignment="Center" />
<Button x:Name="AaruPathButton" Grid.Row="0" Grid.Column="2" Height="22" Width="22" Content="..."
Click="BrowseForPathClick" Style="{DynamicResource CustomButtonStyle}" />
<Label Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="DiscImageCreator Path" />
<TextBox x:Name="DiscImageCreatorPathTextBox" Grid.Row="1" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" Text="{Binding Path=Options.DiscImageCreatorPath}" VerticalContentAlignment="Center" />
<Button x:Name="DiscImageCreatorPathButton" Grid.Row="1" Grid.Column="2" Height="22" Width="22" Content="..." Click="BrowseForPathClick" />
<TextBox x:Name="DiscImageCreatorPathTextBox" Grid.Row="1" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" Text="{Binding Options.DiscImageCreatorPath}" VerticalContentAlignment="Center" />
<Button x:Name="DiscImageCreatorPathButton" Grid.Row="1" Grid.Column="2" Height="22" Width="22" Content="..."
Click="BrowseForPathClick" Style="{DynamicResource CustomButtonStyle}" />
<Label Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="DD Path" />
<TextBox x:Name="DDPathTextBox" Grid.Row="2" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" Text="{Binding Path=Options.DDPath}" VerticalContentAlignment="Center" />
<Button x:Name="DDPathButton" Grid.Row="2" Grid.Column="2" Height="22" Width="22" Content="..." Click="BrowseForPathClick" />
<TextBox x:Name="DDPathTextBox" Grid.Row="2" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" Text="{Binding Options.DDPath}" VerticalContentAlignment="Center" />
<Button x:Name="DDPathButton" Grid.Row="2" Grid.Column="2" Height="22" Width="22" Content="..."
Click="BrowseForPathClick" Style="{DynamicResource CustomButtonStyle}" />
<Label Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Dumping Program" />
<ComboBox x:Name="InternalProgramComboBox" Grid.Row="3" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" ItemsSource="{Binding Path=InternalPrograms}" />
<ComboBox x:Name="InternalProgramComboBox" Grid.Row="3" Grid.Column="1" Height="22" HorizontalAlignment="Stretch"
ItemsSource="{Binding InternalPrograms}" Style="{DynamicResource CustomComboBoxStyle}" />
<Label Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Default Output Path" />
<TextBox x:Name="DefaultOutputPathTextBox" Grid.Row="4" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" Text="{Binding Path=Options.DefaultOutputPath}" VerticalContentAlignment="Center" />
<Button x:Name="DefaultOutputPathButton" Grid.Row="4" Grid.Column="2" Height="22" Width="22" Content="..." Click="BrowseForPathClick" />
<TextBox x:Name="DefaultOutputPathTextBox" Grid.Row="4" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" Text="{Binding Options.DefaultOutputPath}" VerticalContentAlignment="Center" />
<Button x:Name="DefaultOutputPathButton" Grid.Row="4" Grid.Column="2" Height="22" Width="22" Content="..."
Click="BrowseForPathClick" Style="{DynamicResource CustomButtonStyle}" />
</Grid>
</TabItem>
<TabItem Header="Dumping">
<TabItem Header="Dumping" Style="{DynamicResource CustomTabItemStyle}">
<StackPanel>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Dumping">
<UniformGrid Columns="2" Rows="6">
<UniformGrid Columns="2" Rows="7">
<CheckBox VerticalAlignment="Center" Content="Skip Type Detect"
IsChecked="{Binding Path=Options.SkipMediaTypeDetection}"
IsChecked="{Binding Options.SkipMediaTypeDetection}"
ToolTip="Disable trying to guess media type inserted (may improve performance at startup)" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Skip System Detect"
IsChecked="{Binding Path=Options.SkipSystemDetection}"
IsChecked="{Binding Options.SkipSystemDetection}"
ToolTip="Disable trying to guess system (may improve performance at startup)" Margin="0,4"
/>
<Label VerticalAlignment="Center" Content="Default System:" HorizontalAlignment="Right" />
<ComboBox x:Name="DefaultSystemComboBox" Height="22" Width="200" HorizontalAlignment="Left" ItemsSource="{Binding Path=Systems}" SelectedIndex="0">
<ComboBox x:Name="DefaultSystemComboBox" Height="22" Width="200" HorizontalAlignment="Left" ItemsSource="{Binding Systems}" SelectedIndex="0" Style="{DynamicResource CustomComboBoxStyle}">
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsHeader}" Value="True">
<DataTrigger Binding="{Binding IsHeader}" Value="True">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
@@ -75,42 +143,47 @@
</ComboBox>
<CheckBox VerticalAlignment="Center" Content="No Fixed Drives"
IsChecked="{Binding Path=Options.IgnoreFixedDrives}"
IsChecked="{Binding Options.IgnoreFixedDrives}"
ToolTip="Ignore hard drives and other fixed drives" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Show Separate Window"
IsChecked="{Binding Path=Options.ToolsInSeparateWindow}"
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"
/>
<CheckBox VerticalAlignment="Center" Content="Protection Scan"
IsChecked="{Binding Path=Options.ScanForProtection}"
IsChecked="{Binding Options.ScanForProtection}"
ToolTip="Enable automatic checking for copy protection on dumped media" Margin="0,4,0,0"
/>
<CheckBox VerticalAlignment="Center" Content="Eject After Dump"
IsChecked="{Binding Path=Options.EjectAfterDump}"
IsChecked="{Binding Options.EjectAfterDump}"
ToolTip="Eject the disc from the drive after dumping has completed" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Show Disc Info"
IsChecked="{Binding Path=Options.PromptForDiscInformation}"
IsChecked="{Binding Options.PromptForDiscInformation}"
ToolTip="Enable showing the disc information output after dumping" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Add Placeholders"
IsChecked="{Binding Path=Options.AddPlaceholders}"
IsChecked="{Binding Options.AddPlaceholders}"
ToolTip="Enable adding placeholder text in the submissioninfo output for required and optional fields" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Output Submission JSON"
IsChecked="{Binding Path=Options.OutputSubmissionJSON}"
IsChecked="{Binding Options.OutputSubmissionJSON}"
ToolTip="Enable outputting a compressed JSON version of the submission info" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Include Artifacts"
IsChecked="{Binding Options.IncludeArtifacts}" IsEnabled="{Binding Options.OutputSubmissionJSON}"
ToolTip="Include log files in serialized JSON data" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Compress Log Files"
IsChecked="{Binding Path=Options.CompressLogFiles}"
IsChecked="{Binding Options.CompressLogFiles}"
ToolTip="Compress output log files to reduce space" Margin="0,4"
/>
</UniformGrid>
@@ -133,21 +206,21 @@
<Label Grid.Row="0" Grid.Column="0" Content="CD-ROM" />
<Slider x:Name="DumpSpeedCDSlider" Grid.Row="0" Grid.Column="1" Minimum="1" Maximum="72" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
Ticks="{Binding Source={x:Static local:Constants.SpeedsForCDAsCollection}}"
Value="{Binding Path=Options.PreferredDumpSpeedCD}" />
Value="{Binding Options.PreferredDumpSpeedCD}" />
<TextBox x:Name="DumpSpeedCDTextBox" Grid.Row="0" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
Text="{Binding ElementName=DumpSpeedCDSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" Background="LightGray" Foreground="Gray"/>
<Label Grid.Row="1" Grid.Column="0" Content="DVD-ROM" />
<Slider x:Name="DumpSpeedDVDSlider" Grid.Row="1" Grid.Column="1" Minimum="1" Maximum="24" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
Ticks="{Binding Source={x:Static local:Constants.SpeedsForDVDAsCollection}}"
Value="{Binding Path=Options.PreferredDumpSpeedDVD}" />
Value="{Binding Options.PreferredDumpSpeedDVD}" />
<TextBox x:Name="DumpSpeedDVDTextBox" Grid.Row="1" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
Text="{Binding ElementName=DumpSpeedDVDSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" Background="LightGray" Foreground="Gray"/>
<Label Grid.Row="2" Grid.Column="0" Content="BD-ROM" />
<Slider x:Name="DumpSpeedBDSlider" Grid.Row="2" Grid.Column="1" Minimum="1" Maximum="16" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
Ticks="{Binding Source={x:Static local:Constants.SpeedsForBDAsCollection}}"
Value="{Binding Path=Options.PreferredDumpSpeedBD}" />
Value="{Binding Options.PreferredDumpSpeedBD}" />
<TextBox x:Name="DumpSpeedBDTextBox" Grid.Row="2" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
Text="{Binding ElementName=DumpSpeedBDSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" Background="LightGray" Foreground="Gray"/>
</Grid>
@@ -155,102 +228,129 @@
</StackPanel>
</TabItem>
<TabItem Header="Aaru">
<TabItem Header="Protection Scanning" Style="{DynamicResource CustomTabItemStyle}">
<StackPanel>
<UniformGrid Columns="2" Rows="2">
<CheckBox VerticalAlignment="Center" Content="Scan Archive Contents"
IsChecked="{Binding Options.ScanArchivesForProtection}"
ToolTip="Enable scanning archive contents during protection scanning (may drastically increase scanning time but is more accurate)" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Include Executable Packers"
IsChecked="{Binding Options.ScanPackersForProtection}"
ToolTip="Include executable packers in outputted protections" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Force Scanning All Files"
IsChecked="{Binding Options.ForceScanningForProtection}"
ToolTip="Force scanning all files even if they're not executables" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Include Debug Information"
IsChecked="{Binding Options.IncludeDebugProtectionInformation}"
ToolTip="Include debug information during protection scans" Margin="0,4"
/>
</UniformGrid>
</StackPanel>
</TabItem>
<TabItem Header="Aaru" Style="{DynamicResource CustomTabItemStyle}">
<UniformGrid Columns="2" Rows="3">
<CheckBox VerticalAlignment="Center" Content="Enable Debug Output"
IsChecked="{Binding Path=Options.AaruEnableDebug}"
IsChecked="{Binding Options.AaruEnableDebug}"
ToolTip="Enable debug output in logs" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Enable Verbose Output"
IsChecked="{Binding Path=Options.AaruEnableVerbose}"
IsChecked="{Binding Options.AaruEnableVerbose}"
ToolTip="Enable verbose output in logs" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Force Dumping"
IsChecked="{Binding Path=Options.AaruForceDumping}"
IsChecked="{Binding Options.AaruForceDumping}"
ToolTip="Enable forcing dump even if there are issues" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Strip Personal Data"
IsChecked="{Binding Path=Options.AaruStripPersonalData}"
IsChecked="{Binding Options.AaruStripPersonalData}"
ToolTip="Enable stripping of personally identifiable information from metadata" Margin="0,4,0,0"
/>
<Label Content="Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
Text="{Binding Path=Options.AaruRereadCount}"
Text="{Binding Options.AaruRereadCount}"
ToolTip="Specifies how many rereads are attempted for sector and subchannel errors"
/>
</UniformGrid>
</TabItem>
<TabItem Header="DiscImageCreator">
<TabItem Header="DiscImageCreator" Style="{DynamicResource CustomTabItemStyle}">
<UniformGrid Columns="2" Rows="3">
<CheckBox VerticalAlignment="Center" Content="Quiet Mode"
IsChecked="{Binding Path=Options.DICQuietMode}"
IsChecked="{Binding Options.DICQuietMode}"
ToolTip="Disable sounds (beeps) during and after operations" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Paranoid Mode"
IsChecked="{Binding Path=Options.DICParanoidMode}"
IsChecked="{Binding Options.DICParanoidMode}"
ToolTip="Enable pedantic and super-safe flags" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Use CMI Flag"
IsChecked="{Binding Path=Options.DICUseCMIFlag}"
IsChecked="{Binding Options.DICUseCMIFlag}"
ToolTip="Enable the CMI flag for supported disc types (DVD/HD-DVD only)" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Reset After Dump"
IsChecked="{Binding Path=Options.DICResetDriveAfterDump}"
IsChecked="{Binding Options.DICResetDriveAfterDump}"
ToolTip="Reset disc drives after dumping; useful for some older machines" Margin="0,4"
/>
<Label Content="Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
Text="{Binding Path=Options.DICRereadCount}"
Text="{Binding Options.DICRereadCount}"
ToolTip="Specifies how many rereads are attempted on C2 error"
/>
</UniformGrid>
</TabItem>
<TabItem Header="Logging">
<TabItem Header="Logging" Style="{DynamicResource CustomTabItemStyle}">
<UniformGrid Columns="2">
<CheckBox VerticalAlignment="Center" Content="Verbose Logging"
IsChecked="{Binding Path=Options.VerboseLogging}"
IsChecked="{Binding Options.VerboseLogging}"
ToolTip="Display all logging statements" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Auto-Open Log"
IsChecked="{Binding Path=Options.OpenLogWindowAtStartup}"
IsChecked="{Binding Options.OpenLogWindowAtStartup}"
ToolTip="Open the log panel when the program launches" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Enable Log Formatting"
IsChecked="{Binding Path=Options.EnableLogFormatting}"
IsChecked="{Binding Options.EnableLogFormatting}"
ToolTip="Format log lines written to the log, including overwriting previous lines on match. Disable this if you have a weaker system as there is an increased processing load otherwise." Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Enable Progress Processing"
IsChecked="{Binding Path=Options.EnableProgressProcessing}" IsEnabled="{Binding Path=Options.EnableLogFormatting}"
IsChecked="{Binding Options.EnableProgressProcessing}" IsEnabled="{Binding Options.EnableLogFormatting}"
ToolTip="Process lines written to the log to update the progress bar. Disable this if you have a weaker system as there is an increased processing load otherwise." Margin="0,4"
/>
</UniformGrid>
</TabItem>
<TabItem Header="Login Info">
<TabItem Header="Login Info" Style="{DynamicResource CustomTabItemStyle}">
<StackPanel>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Redump Credentials">
<UniformGrid Columns="5" Rows="1">
<Label VerticalAlignment="Center" HorizontalAlignment="Right" Content="Username" />
<TextBox x:Name="RedumpUsernameTextBox" Height="22" HorizontalAlignment="Stretch"
Text="{Binding Path=Options.RedumpUsername}" />
Text="{Binding Options.RedumpUsername}" />
<Label VerticalAlignment="Center" HorizontalAlignment="Right" Content="Password" />
<PasswordBox x:Name="RedumpPasswordBox" Height="22" HorizontalAlignment="Stretch" PasswordChar="*" />
<Button x:Name="RedumpLoginTestButton" Height="22" Width="80" Content="Test Login" Click="OnRedumpTestClick" />
<Button x:Name="RedumpLoginTestButton" Height="22" Width="80" Content="Test Login"
Click="OnRedumpTestClick" Style="{DynamicResource CustomButtonStyle}" />
</UniformGrid>
</GroupBox>
</StackPanel>
@@ -261,8 +361,10 @@
<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="AcceptButton" Height="25" Width="80" IsDefault="True" Content="Accept" Click="OnAcceptClick" />
<Button Name="CancelButton" Height="25" Width="80" IsCancel="True" Content="Cancel" Click="OnCancelClick" />
<Button Name="AcceptButton" Height="25" Width="80" IsDefault="True" Content="Accept"
Click="OnAcceptClick" Style="{DynamicResource CustomButtonStyle}" />
<Button Name="CancelButton" Height="25" Width="80" IsCancel="True" Content="Cancel"
Click="OnCancelClick" Style="{DynamicResource CustomButtonStyle}" />
<Label/> <!-- Empty label for padding -->
</UniformGrid>
</GroupBox>

View File

@@ -4,8 +4,10 @@ using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Input;
using MPF.Data;
using MPF.Redump;
using WPFCustomMessageBox;
using Button = System.Windows.Controls.Button;
using TextBox = System.Windows.Controls.TextBox;
@@ -155,7 +157,7 @@ namespace MPF.Windows
}
else
{
System.Windows.MessageBox.Show(
CustomMessageBox.Show(
"Specified path doesn't exists!",
"Error",
MessageBoxButton.OK,
@@ -166,6 +168,22 @@ namespace MPF.Windows
}
}
/// <summary>
/// Handler for CloseButton Click event
/// </summary>
private void CloseButtonClick(object sender, RoutedEventArgs e)
{
Close();
}
/// <summary>
/// Handler for MinimizeButton Click event
/// </summary>
private void MinimizeButtonClick(object sender, RoutedEventArgs e)
{
this.WindowState = WindowState.Minimized;
}
/// <summary>
/// Handler for AcceptButton Click event
/// </summary>
@@ -192,14 +210,23 @@ namespace MPF.Windows
{
bool? loggedIn = wc.Login(RedumpUsernameTextBox.Text, RedumpPasswordBox.Password);
if (loggedIn == true)
System.Windows.MessageBox.Show("Redump login credentials accepted!", "Success", MessageBoxButton.OK, MessageBoxImage.Information);
CustomMessageBox.Show(this, "Redump login credentials accepted!", "Success", MessageBoxButton.OK, MessageBoxImage.Information);
else if (loggedIn == false)
System.Windows.MessageBox.Show("Redump login credentials denied!", "Failure", MessageBoxButton.OK, MessageBoxImage.Error);
CustomMessageBox.Show(this, "Redump login credentials denied!", "Failure", MessageBoxButton.OK, MessageBoxImage.Error);
else
System.Windows.MessageBox.Show("Error validating credentials!", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
CustomMessageBox.Show(this, "Error validating credentials!", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// Handler for Title MouseDown event
/// </summary>
private void TitleMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
this.DragMove();
}
#endregion
}
}

View File

@@ -6,9 +6,61 @@
xmlns:local="clr-namespace:MPF"
xmlns:controls="clr-namespace:MPF.UserControls"
mc:Ignorable="d"
Title="Ring Code Guide" Width="500" ResizeMode="NoResize" SizeToContent="WidthAndHeight">
Title="Ring Code Guide" Width="500" WindowStyle="None"
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
BorderBrush="DarkGray" BorderThickness="2">
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
</WindowChrome.WindowChrome>
<Grid>
<StackPanel Orientation="Vertical">
<Grid Margin="0,10,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="115"/>
<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>
<TextBlock TextAlignment="Center"><Bold>Ring Code Guide</Bold></TextBlock>
</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>
<Grid Margin="5,5,5,5">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
@@ -42,7 +94,7 @@
</Label>
<Label Grid.Row="4">
<Label.Content>
<TextBlock><Bold Foreground="Blue">4. Mould SID:</Bold> IFPI 94V1</TextBlock>
<TextBlock><Bold Foreground="LightBlue">4. Mould SID:</Bold> IFPI 94V1</TextBlock>
</Label.Content>
</Label>
</Grid>

View File

@@ -1,4 +1,5 @@
using System.Windows;
using System.Windows.Input;
namespace MPF.Windows
{
@@ -11,5 +12,34 @@ namespace MPF.Windows
{
InitializeComponent();
}
#region Event Handlers
/// <summary>
/// Handler for CloseButton Click event
/// </summary>
private void CloseButtonClick(object sender, RoutedEventArgs e)
{
Close();
}
/// <summary>
/// Handler for MinimizeButton Click event
/// </summary>
private void MinimizeButtonClick(object sender, RoutedEventArgs e)
{
this.WindowState = WindowState.Minimized;
}
/// <summary>
/// Handler for Title MouseDown event
/// </summary>
private void TitleMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
this.DragMove();
}
#endregion
}
}

View File

@@ -37,6 +37,7 @@ A list of all changes in each stable release and current WIP builds can now be f
MPF uses some external libraries to assist with additional information gathering after the dumping process.
- **BurnOutSharp** - Protection scanning - [GitHub](https://github.com/mnadareski/BurnOutSharp)
- **WPFCustomMessageBox.thabse** - Custom message boxes in UI - [GitHub](https://github.com/thabse/WPFCustomMessageBox)
- **UnshieldSharp** - Protection scanning - [GitHub](https://github.com/mnadareski/UnshieldSharp)
## Contributors
@@ -47,7 +48,7 @@ Here are the talented people who have contributed to the project so far:
- **ReignStumble** - Former Project Lead / UI Design
- **Jakz** - Primary Feature Contributor
- **NHellFire** - Feature Contributor
- **Shadów** - UI Support
- **Shadów** - UI Support
## Notable Testers
@@ -57,7 +58,7 @@ These are the tireless individuals who have dedicated countless hours to help te
- **Kludge**
- **ajshell1**
- **Whovian**
**Gameboi64**
- **Gameboi64**
- **silasqwerty**
## Community Shout-Outs
@@ -65,4 +66,4 @@ These are the tireless individuals who have dedicated countless hours to help te
Thanks to these communities for their use, testing, and feedback. I can't even hope to be able to thank everyone individually.
- **VGPC Discord** - Fast feedback and a lot of testing
- **Redump Community** - Near-daily use to assist with metadata gathering
- **Redump Community** - Near-daily use to assist with metadata gathering

View File

@@ -1,5 +1,5 @@
# version format
version: 2.0-{build}
version: 2.1-{build}
# pull request template
pull_requests:
@@ -37,7 +37,7 @@ build:
after_build:
- ps: appveyor DownloadFile https://github.com/aaru-dps/Aaru/releases/download/v5.2.0.3330/aaru-5.2.0.3330-1_windows_x64.zip
- ps: appveyor DownloadFile http://www.chrysocome.net/downloads/8ab730cd2a29e76ddd89be1f99357942/dd-0.6beta3.zip
- ps: appveyor DownloadFile https://github.com/saramibreak/DiscImageCreator/files/6241075/DiscImageCreator_20210401.zip
- ps: appveyor DownloadFile https://github.com/saramibreak/DiscImageCreator/files/6748071/DiscImageCreator_20210701.zip
- ps: appveyor DownloadFile https://archive.org/download/subdump_fua_0x28/subdump_fua_0x28.zip
- 7z e aaru-5.2.0.3330-1_windows_x64.zip -oMPF\bin\Debug\net48\Programs\Aaru *
@@ -46,8 +46,8 @@ after_build:
- 7z e dd-0.6beta3.zip -oMPF\bin\Debug\net48\Programs\DD *
- 7z e dd-0.6beta3.zip -oMPF\bin\Debug\netcoreapp3.1\Programs\DD *
- 7z e DiscImageCreator_20210401.zip -oMPF\bin\Debug\net48\Programs\Creator Release_ANSI\*
- 7z e DiscImageCreator_20210401.zip -oMPF\bin\Debug\netcoreapp3.1\Programs\Creator Release_ANSI\*
- 7z e DiscImageCreator_20210701.zip -oMPF\bin\Debug\net48\Programs\Creator Release_ANSI\*
- 7z e DiscImageCreator_20210701.zip -oMPF\bin\Debug\netcoreapp3.1\Programs\Creator Release_ANSI\*
- 7z e subdump_fua_0x28.zip -oMPF\bin\Debug\net48 *
- mkdir MPF\bin\Debug\net48\Programs\Subdump