2021-09-29 11:48:37 -07:00
using System ;
using System.Collections.Generic ;
2021-09-29 12:17:34 -07:00
using System.IO ;
2021-09-29 11:48:37 -07:00
using System.Linq ;
using System.Text.RegularExpressions ;
using System.Threading.Tasks ;
2022-08-25 10:45:22 -07:00
using psxt001z ;
2021-09-29 11:48:37 -07:00
2023-11-06 23:06:11 -05:00
#pragma warning disable SYSLIB1045 // Convert to 'GeneratedRegexAttribute'.
2023-10-06 20:39:59 -04:00
namespace MPF.Core
2021-09-29 11:48:37 -07:00
{
public static class Protection
{
/// <summary>
2022-04-04 22:44:15 -07:00
/// Run protection scan on a given path
2021-09-29 11:48:37 -07:00
/// </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>
2022-04-04 22:06:56 -07:00
/// <returns>Set of all detected copy protections with an optional error string</returns>
2023-11-14 23:09:55 -05:00
public static async Task < ( Dictionary < string , List < string > > ? , string? ) > RunProtectionScanOnPath ( string path , Data . Options options , IProgress < BinaryObjectScanner . ProtectionProgress > ? progress = null )
2021-09-29 11:48:37 -07:00
{
try
{
2023-11-22 23:38:59 -05:00
#if NET40
2023-11-20 16:46:54 -05:00
var found = await Task . Factory . StartNew ( ( ) = >
2023-11-20 13:15:06 -05:00
{
var scanner = new BinaryObjectScanner . Scanner (
options . ScanArchivesForProtection ,
scanContents : true , // Hardcoded value to avoid issues
scanGameEngines : false , // Hardcoded value to avoid issues
options . ScanPackersForProtection ,
scanPaths : true , // Hardcoded value to avoid issues
options . IncludeDebugProtectionInformation ,
progress ) ;
return scanner . GetProtections ( path ) ;
} ) ;
#else
2021-09-29 11:48:37 -07:00
var found = await Task . Run ( ( ) = >
{
2023-11-14 23:09:55 -05:00
var scanner = new BinaryObjectScanner . Scanner (
2022-08-25 11:54:55 -07:00
options . ScanArchivesForProtection ,
2023-01-06 13:54:24 -08:00
scanContents : true , // Hardcoded value to avoid issues
2023-07-14 12:07:44 -04:00
scanGameEngines : false , // Hardcoded value to avoid issues
2022-08-25 11:54:55 -07:00
options . ScanPackersForProtection ,
2023-01-06 13:54:24 -08:00
scanPaths : true , // Hardcoded value to avoid issues
2022-08-25 11:54:55 -07:00
options . IncludeDebugProtectionInformation ,
progress ) ;
2021-09-29 11:48:37 -07:00
return scanner . GetProtections ( path ) ;
} ) ;
2023-11-20 13:15:06 -05:00
#endif
2021-09-29 11:48:37 -07:00
2022-03-08 22:37:12 -08:00
// If nothing was returned, return
2023-11-22 16:26:31 -05:00
#if NET20 | | NET35
if ( found = = null | | found . Count = = 0 )
#else
2023-11-16 15:33:37 -05:00
if ( found = = null | | found . IsEmpty )
2023-11-22 16:26:31 -05:00
#endif
2022-04-04 22:06:56 -07:00
return ( null , null ) ;
2022-03-08 22:37:12 -08:00
// Filter out any empty protections
var filteredProtections = found
2023-11-22 16:26:31 -05:00
#if NET20 | | NET35
. Where ( kvp = > kvp . Value ! = null & & kvp . Value . Count > 0 )
#else
2023-11-16 15:33:37 -05:00
. Where ( kvp = > kvp . Value ! = null & & ! kvp . Value . IsEmpty )
2023-11-22 16:26:31 -05:00
#endif
2022-04-04 22:44:15 -07:00
. ToDictionary (
kvp = > kvp . Key ,
kvp = > kvp . Value . OrderBy ( s = > s ) . ToList ( ) ) ;
2022-03-08 22:37:12 -08:00
2022-04-04 22:06:56 -07:00
// Return the filtered set of protections
return ( filteredProtections , null ) ;
2021-09-29 11:48:37 -07:00
}
catch ( Exception ex )
{
2022-04-04 22:06:56 -07:00
return ( null , ex . ToString ( ) ) ;
2021-09-29 11:48:37 -07:00
}
}
2022-04-04 22:06:56 -07:00
/// <summary>
/// Format found protections to a deduplicated, ordered string
/// </summary>
/// <param name="protections">Dictionary of file to list of protection mappings</param>
/// <returns>Detected protections, if any</returns>
2023-10-07 01:02:21 -04:00
public static string? FormatProtections ( Dictionary < string , List < string > > ? protections )
2022-04-04 22:06:56 -07:00
{
2022-04-10 16:14:13 -07:00
// If the filtered list is empty in some way, return
if ( protections = = null | | ! protections . Any ( ) )
2023-10-04 12:51:29 -04:00
return "None found [OMIT FROM SUBMISSION]" ;
2022-04-04 22:06:56 -07:00
// Get an ordered list of distinct found protections
var orderedDistinctProtections = protections
. SelectMany ( kvp = > kvp . Value )
. Distinct ( )
. OrderBy ( p = > p ) ;
// Sanitize and join protections for writing
string protectionString = SanitizeFoundProtections ( orderedDistinctProtections ) ;
2023-11-22 15:56:43 -05:00
if ( string . IsNullOrEmpty ( protectionString ) )
2023-10-04 12:51:29 -04:00
return "None found [OMIT FROM SUBMISSION]" ;
2022-04-04 22:06:56 -07:00
return protectionString ;
}
2021-09-29 12:17:34 -07:00
/// <summary>
2021-11-04 15:23:40 -07:00
/// Get the existence of an anti-modchip string from a PlayStation disc, if possible
2021-09-29 12:17:34 -07:00
/// </summary>
/// <param name="path">Path to scan for anti-modchip strings</param>
2021-11-04 15:23:40 -07:00
/// <returns>Anti-modchip existence if possible, false on error</returns>
2023-10-18 01:47:42 -04:00
public static async Task < bool > GetPlayStationAntiModchipDetected ( string? path )
2021-09-29 12:17:34 -07:00
{
2023-10-18 01:47:42 -04:00
// If there is no valid path
if ( string . IsNullOrEmpty ( path ) )
return false ;
2023-11-22 23:38:59 -05:00
#if NET40
2023-11-20 16:46:54 -05:00
return await Task . Factory . StartNew ( ( ) = >
2023-11-20 13:15:06 -05:00
{
try
{
var antiModchip = new BinaryObjectScanner . Protection . PSXAntiModchip ( ) ;
foreach ( string file in Directory . EnumerateFiles ( path , "*" , SearchOption . AllDirectories ) )
{
try
{
byte [ ] fileContent = File . ReadAllBytes ( file ) ;
var protection = antiModchip . CheckContents ( file , fileContent , false ) ;
2023-11-22 15:56:43 -05:00
if ( ! string . IsNullOrEmpty ( protection ) )
2023-11-20 13:15:06 -05:00
return true ;
}
catch { }
}
}
catch { }
return false ;
} ) ;
#else
2021-09-29 12:17:34 -07:00
return await Task . Run ( ( ) = >
{
2021-11-04 15:23:40 -07:00
try
2021-09-29 12:17:34 -07:00
{
2023-11-14 23:09:55 -05:00
var antiModchip = new BinaryObjectScanner . Protection . PSXAntiModchip ( ) ;
2023-11-22 16:26:31 -05:00
#if NET20 | | NET35
foreach ( string file in Directory . GetFiles ( path , "*" , SearchOption . AllDirectories ) )
#else
2021-11-04 15:23:40 -07:00
foreach ( string file in Directory . EnumerateFiles ( path , "*" , SearchOption . AllDirectories ) )
2023-11-22 16:26:31 -05:00
#endif
2021-09-29 12:17:34 -07:00
{
2021-11-04 15:23:40 -07:00
try
{
byte [ ] fileContent = File . ReadAllBytes ( file ) ;
2023-10-26 01:00:09 -04:00
var protection = antiModchip . CheckContents ( file , fileContent , false ) ;
2023-11-22 15:56:43 -05:00
if ( ! string . IsNullOrEmpty ( protection ) )
2021-11-04 15:23:40 -07:00
return true ;
}
catch { }
2021-09-29 12:17:34 -07:00
}
}
2021-11-04 15:23:40 -07:00
catch { }
2021-09-29 12:17:34 -07:00
return false ;
} ) ;
2023-11-20 13:15:06 -05:00
#endif
2021-09-29 12:17:34 -07:00
}
2021-09-29 14:56:15 -07:00
/// <summary>
/// Get if LibCrypt data is detected in the subchannel file, if possible
/// </summary>
/// <param name="sub">.sub file location</param>
/// <returns>Status of the LibCrypt data, if possible</returns>
public static bool? GetLibCryptDetected ( string sub )
{
// If the file doesn't exist, we can't get info from it
if ( ! File . Exists ( sub ) )
return null ;
2023-11-22 15:56:43 -05:00
return LibCrypt . DetectLibCrypt ( [ sub ] ) ;
2021-09-29 14:56:15 -07:00
}
2021-09-29 11:48:37 -07:00
/// <summary>
/// Sanitize unnecessary protection duplication from output
/// </summary>
/// <param name="foundProtections">Enumerable of found protections</param>
2021-11-19 10:09:34 -08:00
public static string SanitizeFoundProtections ( IEnumerable < string > foundProtections )
2021-09-29 11:48:37 -07:00
{
2023-10-04 12:51:29 -04:00
// EXCEPTIONS
if ( foundProtections . Any ( p = > p . StartsWith ( "[Exception opening file" ) ) )
{
foundProtections = foundProtections . Where ( p = > ! p . StartsWith ( "[Exception opening file" ) ) ;
2023-11-22 15:56:43 -05:00
#if NET20 | | NET35 | | NET40 | | NET452 | | NET462
2023-11-14 23:09:55 -05:00
var tempList = new List < string > { "Exception occurred while scanning [RESCAN NEEDED]" } ;
tempList . AddRange ( foundProtections ) ;
foundProtections = tempList . OrderBy ( p = > p ) ;
#else
2023-10-04 12:51:29 -04:00
foundProtections = foundProtections
. Prepend ( "Exception occurred while scanning [RESCAN NEEDED]" )
. OrderBy ( p = > p ) ;
2023-11-14 23:09:55 -05:00
#endif
2023-10-04 12:51:29 -04:00
}
2021-09-29 11:48:37 -07:00
// ActiveMARK
if ( foundProtections . Any ( p = > p = = "ActiveMARK 5" ) & & foundProtections . Any ( p = > p = = "ActiveMARK" ) )
foundProtections = foundProtections . Where ( p = > p ! = "ActiveMARK" ) ;
// Cactus Data Shield
2023-10-30 16:31:50 -04:00
if ( foundProtections . Any ( p = > Regex . IsMatch ( p , @"Cactus Data Shield [0-9]{3} .+" , RegexOptions . Compiled ) ) & & foundProtections . Any ( p = > p = = "Cactus Data Shield 200" ) )
2021-09-29 11:48:37 -07:00
foundProtections = foundProtections . Where ( p = > p ! = "Cactus Data Shield 200" ) ;
// CD-Check
foundProtections = foundProtections . Where ( p = > p ! = "Executable-Based CD Check" ) ;
// CD-Cops
if ( foundProtections . Any ( p = > p = = "CD-Cops" ) & & foundProtections . Any ( p = > p . StartsWith ( "CD-Cops" ) & & p . Length > "CD-Cops" . Length ) )
foundProtections = foundProtections . Where ( p = > p ! = "CD-Cops" ) ;
// CD-Key / Serial
foundProtections = foundProtections . Where ( p = > p ! = "CD-Key / Serial" ) ;
// Electronic Arts
if ( foundProtections . Any ( p = > p = = "EA CdKey Registration Module" ) & & foundProtections . Any ( p = > p . StartsWith ( "EA CdKey Registration Module" ) & & p . Length > "EA CdKey Registration Module" . Length ) )
foundProtections = foundProtections . Where ( p = > p ! = "EA CdKey Registration Module" ) ;
if ( foundProtections . Any ( p = > p = = "EA DRM Protection" ) & & foundProtections . Any ( p = > p . StartsWith ( "EA DRM Protection" ) & & p . Length > "EA DRM Protection" . Length ) )
foundProtections = foundProtections . Where ( p = > p ! = "EA DRM Protection" ) ;
// Games for Windows LIVE
if ( foundProtections . Any ( p = > p = = "Games for Windows LIVE" ) & & foundProtections . Any ( p = > p . StartsWith ( "Games for Windows LIVE" ) & & ! p . Contains ( "Zero Day Piracy Protection" ) & & p . Length > "Games for Windows LIVE" . Length ) )
foundProtections = foundProtections . Where ( p = > p ! = "Games for Windows LIVE" ) ;
// Impulse Reactor
if ( foundProtections . Any ( p = > p . StartsWith ( "Impulse Reactor Core Module" ) ) & & foundProtections . Any ( p = > p = = "Impulse Reactor" ) )
foundProtections = foundProtections . Where ( p = > p ! = "Impulse Reactor" ) ;
// JoWood X-Prot
if ( foundProtections . Any ( p = > p . StartsWith ( "JoWood X-Prot" ) ) )
{
2023-10-30 16:31:50 -04:00
if ( foundProtections . Any ( p = > Regex . IsMatch ( p , @"JoWood X-Prot [0-9]\.[0-9]\.[0-9]\.[0-9]{2}" , RegexOptions . Compiled ) ) )
2021-09-29 11:48:37 -07:00
{
2021-11-19 10:09:34 -08:00
foundProtections = foundProtections . Where ( p = > p ! = "JoWood X-Prot" )
. Where ( p = > p ! = "JoWood X-Prot v1.0-v1.3" )
. Where ( p = > p ! = "JoWood X-Prot v1.4+" )
. Where ( p = > p ! = "JoWood X-Prot v2" ) ;
2021-09-29 11:48:37 -07:00
}
else if ( foundProtections . Any ( p = > p = = "JoWood X-Prot v2" ) )
{
2021-11-19 10:09:34 -08:00
foundProtections = foundProtections . Where ( p = > p ! = "JoWood X-Prot" )
. Where ( p = > p ! = "JoWood X-Prot v1.0-v1.3" )
. Where ( p = > p ! = "JoWood X-Prot v1.4+" ) ;
2021-09-29 11:48:37 -07:00
}
else if ( foundProtections . Any ( p = > p = = "JoWood X-Prot v1.4+" ) )
{
2021-11-19 10:09:34 -08:00
foundProtections = foundProtections . Where ( p = > p ! = "JoWood X-Prot" )
. Where ( p = > p ! = "JoWood X-Prot v1.0-v1.3" ) ;
2021-09-29 11:48:37 -07:00
}
else if ( foundProtections . Any ( p = > p = = "JoWood X-Prot v1.0-v1.3" ) )
{
foundProtections = foundProtections . Where ( p = > p ! = "JoWood X-Prot" ) ;
}
}
// LaserLok
// TODO: Figure this one out
2021-11-19 10:09:34 -08:00
// Online Registration
foundProtections = foundProtections . Where ( p = > ! p . StartsWith ( "Executable-Based Online Registration" ) ) ;
2021-09-29 11:48:37 -07:00
// ProtectDISC / VOB ProtectCD/DVD
// TODO: Figure this one out
// SafeCast
// TODO: Figure this one out
2022-10-16 14:27:26 -07:00
// Cactus Data Shield / SafeDisc
if ( foundProtections . Any ( p = > p = = "Cactus Data Shield 300 (Confirm presence of other CDS-300 files)" ) )
{
foundProtections = foundProtections
. Where ( p = > p ! = "Cactus Data Shield 300 (Confirm presence of other CDS-300 files)" ) ;
if ( foundProtections . Any ( p = > ! p . StartsWith ( "SafeDisc" ) ) )
2023-11-14 23:09:55 -05:00
{
2023-11-22 15:56:43 -05:00
#if NET20 | | NET35 | | NET40 | | NET452 | | NET462
2023-11-14 23:09:55 -05:00
var tempList = new List < string > ( ) ;
tempList . AddRange ( foundProtections ) ;
tempList . Add ( "Cactus Data Shield 300" ) ;
foundProtections = tempList ;
#else
2022-10-16 14:27:26 -07:00
foundProtections = foundProtections . Append ( "Cactus Data Shield 300" ) ;
2023-11-14 23:09:55 -05:00
#endif
}
2022-10-16 14:27:26 -07:00
}
2021-09-29 11:48:37 -07:00
// SafeDisc
if ( foundProtections . Any ( p = > p . StartsWith ( "SafeDisc" ) ) )
{
2023-10-30 16:31:50 -04:00
if ( foundProtections . Any ( p = > Regex . IsMatch ( p , @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}" , RegexOptions . Compiled ) ) )
2021-09-29 11:48:37 -07:00
{
2023-08-15 10:18:01 -06:00
foundProtections = foundProtections . Where ( p = > ! p . StartsWith ( "Macrovision Protected Application" ) )
. Where ( p = > ! p . StartsWith ( "Macrovision Protection File" ) )
. Where ( p = > ! p . StartsWith ( "Macrovision Security Driver" ) )
. Where ( p = > p ! = "SafeDisc" )
2023-10-30 16:31:50 -04:00
. Where ( p = > ! ( Regex . IsMatch ( p , @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}" , RegexOptions . Compiled ) ) )
. Where ( p = > ! ( Regex . IsMatch ( p , @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+" , RegexOptions . Compiled ) ) )
2021-11-19 10:09:34 -08:00
. Where ( p = > p ! = "SafeDisc 1/Lite" )
2023-08-15 10:18:01 -06:00
. Where ( p = > p ! = "SafeDisc 2+" ) ;
2021-09-29 11:48:37 -07:00
}
2023-10-30 16:31:50 -04:00
else if ( foundProtections . Any ( p = > Regex . IsMatch ( p , @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}" , RegexOptions . Compiled ) ) )
2021-09-29 11:48:37 -07:00
{
2023-08-15 10:18:01 -06:00
foundProtections = foundProtections . Where ( p = > ! p . StartsWith ( "Macrovision Protected Application" ) )
. Where ( p = > ! p . StartsWith ( "Macrovision Protection File" ) )
. Where ( p = > ! p . StartsWith ( "Macrovision Security Driver" ) )
. Where ( p = > p ! = "SafeDisc" )
2023-10-30 16:31:50 -04:00
. Where ( p = > ! ( Regex . IsMatch ( p , @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+" , RegexOptions . Compiled ) ) )
2021-11-19 10:09:34 -08:00
. Where ( p = > p ! = "SafeDisc 1/Lite" )
2023-08-15 10:18:01 -06:00
. Where ( p = > p ! = "SafeDisc 2+" ) ;
2021-09-29 11:48:37 -07:00
}
2023-10-30 16:31:50 -04:00
else if ( foundProtections . Any ( p = > Regex . IsMatch ( p , @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+" , RegexOptions . Compiled ) ) )
2021-09-29 11:48:37 -07:00
{
2023-08-15 10:18:01 -06:00
foundProtections = foundProtections . Where ( p = > ! p . StartsWith ( "Macrovision Protected Application" ) )
. Where ( p = > ! p . StartsWith ( "Macrovision Protection File" ) )
. Where ( p = > ! p . StartsWith ( "Macrovision Security Driver" ) )
. Where ( p = > p ! = "SafeDisc" )
2021-11-19 10:09:34 -08:00
. Where ( p = > p ! = "SafeDisc 1/Lite" )
2023-08-15 10:18:01 -06:00
. Where ( p = > p ! = "SafeDisc 2+" ) ;
2021-09-29 11:48:37 -07:00
}
2023-08-15 10:18:01 -06:00
else if ( foundProtections . Any ( p = > p . StartsWith ( "Macrovision Security Driver" ) ) )
2021-09-29 11:48:37 -07:00
{
2023-08-15 10:18:01 -06:00
foundProtections = foundProtections . Where ( p = > ! p . StartsWith ( "Macrovision Protected Application" ) )
. Where ( p = > ! p . StartsWith ( "Macrovision Protection File" ) )
. Where ( p = > p ! = "SafeDisc" )
2021-11-19 10:09:34 -08:00
. Where ( p = > p ! = "SafeDisc 1/Lite" )
2023-08-15 10:18:01 -06:00
. Where ( p = > p ! = "SafeDisc 2+" ) ;
2021-09-29 11:48:37 -07:00
}
2023-08-15 10:18:01 -06:00
else if ( foundProtections . Any ( p = > p = = "SafeDisc 2+" ) )
2021-09-29 11:48:37 -07:00
{
2023-08-15 10:18:01 -06:00
foundProtections = foundProtections . Where ( p = > ! p . StartsWith ( "Macrovision Protected Application" ) )
. Where ( p = > ! p . StartsWith ( "Macrovision Protection File" ) )
. Where ( p = > p ! = "SafeDisc" ) ;
2021-09-29 11:48:37 -07:00
}
else if ( foundProtections . Any ( p = > p = = "SafeDisc 1/Lite" ) )
{
2023-08-15 10:18:01 -06:00
foundProtections = foundProtections . Where ( p = > ! p . StartsWith ( "Macrovision Protected Application" ) )
. Where ( p = > ! p . StartsWith ( "Macrovision Protection File" ) )
. Where ( p = > p ! = "SafeDisc" ) ;
2021-09-29 11:48:37 -07:00
}
}
// SecuROM
// TODO: Figure this one out
// SolidShield
// TODO: Figure this one out
// StarForce
2021-11-19 11:03:24 -08:00
if ( foundProtections . Any ( p = > p . StartsWith ( "StarForce" ) ) )
{
2023-10-30 16:31:50 -04:00
if ( foundProtections . Any ( p = > Regex . IsMatch ( p , @"StarForce [0-9]+\..+" , RegexOptions . Compiled ) ) )
2021-11-19 11:03:24 -08:00
{
foundProtections = foundProtections . Where ( p = > p ! = "StarForce" )
. Where ( p = > p ! = "StarForce 3-5" )
. Where ( p = > p ! = "StarForce 5" )
. Where ( p = > p ! = "StarForce 5 [Protected Module]" ) ;
}
else if ( foundProtections . Any ( p = > p = = "StarForce 5 [Protected Module]" ) )
{
foundProtections = foundProtections . Where ( p = > p ! = "StarForce" )
. Where ( p = > p ! = "StarForce 3-5" )
. Where ( p = > p ! = "StarForce 5" ) ;
}
else if ( foundProtections . Any ( p = > p = = "StarForce 5" ) )
{
foundProtections = foundProtections . Where ( p = > p ! = "StarForce" )
. Where ( p = > p ! = "StarForce 3-5" ) ;
}
else if ( foundProtections . Any ( p = > p = = "StarForce 3-5" ) )
{
foundProtections = foundProtections . Where ( p = > p ! = "StarForce" ) ;
}
}
2021-09-29 11:48:37 -07:00
// Sysiphus
if ( foundProtections . Any ( p = > p = = "Sysiphus" ) & & foundProtections . Any ( p = > p . StartsWith ( "Sysiphus" ) & & p . Length > "Sysiphus" . Length ) )
foundProtections = foundProtections . Where ( p = > p ! = "Sysiphus" ) ;
// TAGES
// TODO: Figure this one out
// XCP
if ( foundProtections . Any ( p = > p = = "XCP" ) & & foundProtections . Any ( p = > p . StartsWith ( "XCP" ) & & p . Length > "XCP" . Length ) )
foundProtections = foundProtections . Where ( p = > p ! = "XCP" ) ;
2023-11-22 16:26:31 -05:00
return string . Join ( ", " , foundProtections . ToArray ( ) ) ;
2021-09-29 11:48:37 -07:00
}
}
}