mirror of
https://github.com/SabreTools/BinaryObjectScanner.git
synced 2026-07-09 02:16:46 +00:00
Add game engine detection skeleton
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BinaryObjectScanner.Compression\BinaryObjectScanner.Compression.csproj" />
|
||||
<ProjectReference Include="..\BinaryObjectScanner.GameEngine\BinaryObjectScanner.GameEngine.csproj" />
|
||||
<ProjectReference Include="..\BinaryObjectScanner.Interfaces\BinaryObjectScanner.Interfaces.csproj" />
|
||||
<ProjectReference Include="..\BinaryObjectScanner.Matching\BinaryObjectScanner.Matching.csproj" />
|
||||
<ProjectReference Include="..\BinaryObjectScanner.Packer\BinaryObjectScanner.Packer.csproj" />
|
||||
|
||||
@@ -23,6 +23,11 @@ namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Determines if game engines are counted as detected protections or not
|
||||
/// </summary>
|
||||
public bool IncludeGameEngines { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if packers are counted as detected protections or not
|
||||
/// </summary>
|
||||
@@ -233,8 +238,12 @@ namespace BinaryObjectScanner.FileType
|
||||
if (string.IsNullOrWhiteSpace(protection))
|
||||
return;
|
||||
|
||||
// If we are filtering the output of the check
|
||||
if (!CheckIfPacker(checkClass) || !IncludePackers)
|
||||
// If we are filtering on game engines
|
||||
if (CheckIfGameEngine(checkClass) && !IncludeGameEngines)
|
||||
return;
|
||||
|
||||
// If we are filtering on packers
|
||||
if (CheckIfPacker(checkClass) && !IncludePackers)
|
||||
return;
|
||||
|
||||
protections.TryAdd(checkClass, protection);
|
||||
@@ -263,8 +272,12 @@ namespace BinaryObjectScanner.FileType
|
||||
if (string.IsNullOrWhiteSpace(protection))
|
||||
return;
|
||||
|
||||
// If we are filtering the output of the check
|
||||
if (!CheckIfPacker(checkClass) || !IncludePackers)
|
||||
// If we are filtering on game engines
|
||||
if (CheckIfGameEngine(checkClass) && !IncludeGameEngines)
|
||||
return;
|
||||
|
||||
// If we are filtering on packers
|
||||
if (CheckIfPacker(checkClass) && !IncludePackers)
|
||||
return;
|
||||
|
||||
protections.TryAdd(checkClass, protection);
|
||||
@@ -293,8 +306,12 @@ namespace BinaryObjectScanner.FileType
|
||||
if (string.IsNullOrWhiteSpace(protection))
|
||||
return;
|
||||
|
||||
// If we are filtering the output of the check
|
||||
if (!CheckIfPacker(checkClass) || !IncludePackers)
|
||||
// If we are filtering on game engines
|
||||
if (CheckIfGameEngine(checkClass) && !IncludeGameEngines)
|
||||
return;
|
||||
|
||||
// If we are filtering on packers
|
||||
if (CheckIfPacker(checkClass) && !IncludePackers)
|
||||
return;
|
||||
|
||||
protections.TryAdd(checkClass, protection);
|
||||
@@ -323,8 +340,12 @@ namespace BinaryObjectScanner.FileType
|
||||
if (string.IsNullOrWhiteSpace(protection))
|
||||
return;
|
||||
|
||||
// If we are filtering the output of the check
|
||||
if (!CheckIfPacker(checkClass) || !IncludePackers)
|
||||
// If we are filtering on game engines
|
||||
if (CheckIfGameEngine(checkClass) && !IncludeGameEngines)
|
||||
return;
|
||||
|
||||
// If we are filtering on packers
|
||||
if (CheckIfPacker(checkClass) && !IncludePackers)
|
||||
return;
|
||||
|
||||
protections.TryAdd(checkClass, protection);
|
||||
@@ -353,8 +374,12 @@ namespace BinaryObjectScanner.FileType
|
||||
if (string.IsNullOrWhiteSpace(protection))
|
||||
return;
|
||||
|
||||
// If we are filtering the output of the check
|
||||
if (!CheckIfPacker(checkClass) || !IncludePackers)
|
||||
// If we are filtering on game engines
|
||||
if (CheckIfGameEngine(checkClass) && !IncludeGameEngines)
|
||||
return;
|
||||
|
||||
// If we are filtering on packers
|
||||
if (CheckIfPacker(checkClass) && !IncludePackers)
|
||||
return;
|
||||
|
||||
protections.TryAdd(checkClass, protection);
|
||||
@@ -371,8 +396,9 @@ namespace BinaryObjectScanner.FileType
|
||||
/// Initialize all implementations of a type
|
||||
/// </summary>
|
||||
private static IEnumerable<T> InitCheckClasses<T>()
|
||||
=> InitCheckClasses<T>(typeof(BinaryObjectScanner.Packer._DUMMY).Assembly)
|
||||
.Concat(InitCheckClasses<T>(typeof(BinaryObjectScanner.Protection._DUMMY).Assembly));
|
||||
=> InitCheckClasses<T>(typeof(GameEngine._DUMMY).Assembly)
|
||||
.Concat(InitCheckClasses<T>(typeof(Packer._DUMMY).Assembly))
|
||||
.Concat(InitCheckClasses<T>(typeof(Protection._DUMMY).Assembly));
|
||||
|
||||
/// <summary>
|
||||
/// Initialize all implementations of a type
|
||||
@@ -388,6 +414,15 @@ namespace BinaryObjectScanner.FileType
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Check to see if an implementation is a game engine using reflection
|
||||
/// </summary>
|
||||
/// <param name="impl">Implementation that was last used to check</param>
|
||||
private static bool CheckIfGameEngine(object impl)
|
||||
{
|
||||
return impl.GetType().Namespace.ToLowerInvariant().Contains("gameengine");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check to see if an implementation is a packer using reflection
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;net6.0;net7.0</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win-x86;win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<Title>BinaryObjectScanner.GameEngine</Title>
|
||||
<AssemblyName>BinaryObjectScanner.GameEngine</AssemblyName>
|
||||
<Authors>Matt Nadareski</Authors>
|
||||
<Product>BurnOutSharp</Product>
|
||||
<Copyright>Copyright (c)2022 Matt Nadareski</Copyright>
|
||||
<RepositoryUrl>https://github.com/mnadareski/BurnOutSharp</RepositoryUrl>
|
||||
<Version>2.7</Version>
|
||||
<AssemblyVersion>2.7</AssemblyVersion>
|
||||
<FileVersion>2.7</FileVersion>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BinaryObjectScanner.Interfaces\BinaryObjectScanner.Interfaces.csproj" />
|
||||
<ProjectReference Include="..\BinaryObjectScanner.Matching\BinaryObjectScanner.Matching.csproj" />
|
||||
<ProjectReference Include="..\BinaryObjectScanner.Wrappers\BinaryObjectScanner.Wrappers.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
7
BinaryObjectScanner.GameEngine/_DUMMY.cs
Normal file
7
BinaryObjectScanner.GameEngine/_DUMMY.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace BinaryObjectScanner.GameEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// This class exists for reflection purposes and should not be used
|
||||
/// </summary>
|
||||
public sealed class _DUMMY { }
|
||||
}
|
||||
@@ -38,7 +38,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BinaryObjectScanner.Protect
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BinaryObjectScanner.Packer", "BinaryObjectScanner.Packer\BinaryObjectScanner.Packer.csproj", "{59B3DE26-9399-4B5A-B39B-6239DAF1A017}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BinaryObjectScanner.FileType", "BinaryObjectScanner.FileType\BinaryObjectScanner.FileType.csproj", "{6733368B-63D4-4921-AE13-FD1A53C5180C}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BinaryObjectScanner.FileType", "BinaryObjectScanner.FileType\BinaryObjectScanner.FileType.csproj", "{6733368B-63D4-4921-AE13-FD1A53C5180C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BinaryObjectScanner.GameEngine", "BinaryObjectScanner.GameEngine\BinaryObjectScanner.GameEngine.csproj", "{8AFC9C4C-CC57-4382-8C32-F6B4C46E321A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@@ -102,6 +104,10 @@ Global
|
||||
{6733368B-63D4-4921-AE13-FD1A53C5180C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6733368B-63D4-4921-AE13-FD1A53C5180C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{6733368B-63D4-4921-AE13-FD1A53C5180C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8AFC9C4C-CC57-4382-8C32-F6B4C46E321A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8AFC9C4C-CC57-4382-8C32-F6B4C46E321A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8AFC9C4C-CC57-4382-8C32-F6B4C46E321A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8AFC9C4C-CC57-4382-8C32-F6B4C46E321A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -15,6 +15,11 @@
|
||||
/// </summary>
|
||||
public bool ScanContents { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if game engines are counted as detected protections or not
|
||||
/// </summary>
|
||||
public bool ScanGameEngines { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if packers are counted as detected protections or not
|
||||
/// </summary>
|
||||
|
||||
@@ -23,10 +23,13 @@ namespace BurnOutSharp
|
||||
/// <inheritdoc cref="Options.ScanContents"/>
|
||||
public bool ScanContents => options?.ScanContents ?? false;
|
||||
|
||||
/// <inheritdoc cref="Options.ScanPaths"/>
|
||||
/// <inheritdoc cref="Options.ScanGameEngines"/>
|
||||
public bool ScanGameEngines => options?.ScanGameEngines ?? false;
|
||||
|
||||
/// <inheritdoc cref="Options.ScanPackers"/>
|
||||
public bool ScanPackers => options?.ScanPackers ?? false;
|
||||
|
||||
/// <inheritdoc cref="Options.ScanArchives"/>
|
||||
/// <inheritdoc cref="Options.ScanPaths"/>
|
||||
public bool ScanPaths => options?.ScanPaths ?? false;
|
||||
|
||||
/// <inheritdoc cref="Options.IncludeDebug"/>
|
||||
@@ -49,16 +52,18 @@ namespace BurnOutSharp
|
||||
/// </summary>
|
||||
/// <param name="scanArchives">Enable scanning archive contents</param>
|
||||
/// <param name="scanContents">Enable including content detections in output</param>
|
||||
/// <param name="scanGameEngines">Enable including game engines in output</param>
|
||||
/// <param name="scanPackers">Enable including packers in output</param>
|
||||
/// <param name="scanPaths">Enable including path detections in output</param>
|
||||
/// <param name="includeDebug">Enable including debug information</param>
|
||||
/// <param name="fileProgress">Optional progress callback</param>
|
||||
public Scanner(bool scanArchives, bool scanContents, bool scanPackers, bool scanPaths, bool includeDebug, IProgress<ProtectionProgress> fileProgress = null)
|
||||
public Scanner(bool scanArchives, bool scanContents, bool scanGameEngines, bool scanPackers, bool scanPaths, bool includeDebug, IProgress<ProtectionProgress> fileProgress = null)
|
||||
{
|
||||
this.options = new Options
|
||||
{
|
||||
ScanArchives = scanArchives,
|
||||
ScanContents = scanContents,
|
||||
ScanGameEngines = scanGameEngines,
|
||||
ScanPackers = scanPackers,
|
||||
ScanPaths = scanPaths,
|
||||
IncludeDebug = includeDebug,
|
||||
@@ -300,6 +305,7 @@ namespace BurnOutSharp
|
||||
// If we have an executable, it needs to bypass normal handling
|
||||
if (detectable is Executable executable)
|
||||
{
|
||||
executable.IncludeGameEngines = ScanGameEngines;
|
||||
executable.IncludePackers = ScanPackers;
|
||||
var subProtections = ProcessExecutable(executable, fileName, stream);
|
||||
if (subProtections != null)
|
||||
|
||||
@@ -325,6 +325,7 @@ This section contains information on project and class organization principles t
|
||||
| `BinaryObjectScanner.Builders` | One file per executable type. |
|
||||
| `BinaryObjectScanner.Compression` | One directory per compression type. |
|
||||
| `BinaryObjectScanner.FileType` | One file per file type. |
|
||||
| `BinaryObjectScanner.GameEngine` | At least one file per game engine. Partial classes allowed. |
|
||||
| `BinaryObjectScanner.Interfaces` | One file per interface. |
|
||||
| `BinaryObjectScanner.Matching` | Flat directory structure. Include interfaces and base classes. |
|
||||
| `BinaryObjectScanner.Models` | One directory per executable type. One file per object model. |
|
||||
@@ -344,19 +345,19 @@ This section contains information on in-code organization principles that depend
|
||||
| Project | Description |
|
||||
| --- | --- |
|
||||
| `BurnOutSharp` | Varies from file to file. |
|
||||
| `BurnOutSharp/FileType` | `IDetectable` implementations, `IExtractable` implementations, `IScannable` implementations, helper methods. |
|
||||
| `BurnOutSharp/Tools` | Methods grouped by function. Regions ordered alphabetically. |
|
||||
| `BinaryObjectScanner.ASN1` | Partial classes suggested for different implmentations. |
|
||||
| `BinaryObjectScanner.Builders` | Two copies of each non-generic method: one for byte arrays and one for Streams. |
|
||||
| `BinaryObjectScanner.Compression` | Varies from file to file. |
|
||||
| `BinaryObjectScanner.FileType` | `IDetectable` implementations, `IExtractable` implementations, helper methods. |
|
||||
| `BinaryObjectScanner.GameEngine` | `IContentCheck` implementations, `ILinearExecutableCheck` implementations, `INewExecutableCheck` implementations, `IPortableExecutableCheck` implementations, `IPathCheck` implementations, `IExtractable` implementations, helper methods. |
|
||||
| `BinaryObjectScanner.Interfaces` | Methods ordered alphabetically. |
|
||||
| `BinaryObjectScanner.Matching` | Varies from file to file. |
|
||||
| `BinaryObjectScanner.Models` | No methods at all, just properties. |
|
||||
| `BinaryObjectScanner.Packer` | `IContentCheck` implementations, `ILinearExecutableCheck` implementations, `INewExecutableCheck` implementations, `IPortableExecutableCheck` implementations, `IPathCheck` implementations, `IExtractable` implementations, helper methods. |
|
||||
| `BinaryObjectScanner.Protection` | `IContentCheck` implementations, `ILinearExecutableCheck` implementations, `INewExecutableCheck` implementations, `IPortableExecutableCheck` implementations, `IPathCheck` implementations, `IExtractable` implementations, helper methods. |
|
||||
| `BinaryObjectScanner.Utilities` | Varies from file to file. |
|
||||
| `BurnOutSharp.Wrappers` | Follow region and method grouping from existing wrappers. |
|
||||
| `BinaryObjectScanner.Wrappers` | Follow region and method grouping from existing wrappers. |
|
||||
| `psxt001z` | Varies from file to file. |
|
||||
| `Test` | New functionality should be added as a combination of a flag with a long and a short form, a new line in the help text, and a new method (if necessary). |
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ This is a guide for any developers who wish to research protections, implement n
|
||||
| `BinaryObjectScanner.Builder` | Library containing classes that assist in populating the various object models defined in `BinaryObjectScanner.Models`. Builders can work with either byte arrays or streams for input. At the time of writing, the following executable types have builders: **MS-DOS**, **New Executable**, **Portable Executable**. |
|
||||
| `BinaryObjectScanner.Compression` | Library containing classes that deal with different compression formats. This library is used extensively by the wrappers in `BinaryObjectScanner.Wrappers`. |
|
||||
| `BinaryObjectScanner.FileType` | Library containing file type definitions specific to scanning. |
|
||||
| `BinaryObjectScanner.GameEngine` | Library containing game engine scanning definitions. |
|
||||
| `BinaryObjectScanner.Interfaces` | Library containing interface definitions for scanning and detection. |
|
||||
| `BinaryObjectScanner.Matching` | Library containing models and logic for generic searching and matching. This library is used extensively by the packer and protection checks in `BurnOutSharp`. |
|
||||
| `BinaryObjectScanner.Models` | Library containing object models that represent various pieces of known executable formats. At the time of writing, the following executable types have models: **MS-DOS**, **New Executable**, **Linear Executable (partial)**, **Portable Executable**. |
|
||||
@@ -91,6 +92,8 @@ Adding a new checker or format should happen in a few distinct steps:
|
||||
|
||||
- If it is a new supported file type (such as an archive format), create the file in `BinaryObjectScanner.FileType`. By default, you will need to implement `BurnOutSharp.Interfaces.IDetectable` or `BinaryObjectScanner.Interfaces.IExtractable`. Do not implement any other interfaces. Please consider asking project maintainers before doing this work, especially if there are external dependencies.
|
||||
|
||||
- If it is a new supported game engine or standard library, create the file in `BinaryObjectScanner.GameEngine`. By default, you will need to implement at least one of: `BinaryObjectScanner.Interfaces.ILinearExecutableCheck`, `BinaryObjectScanner.Interfaces.INewExecutableCheck`, and `BinaryObjectScanner.Interfaces.IPortableExecutableCheck`. It is exceptionally rare to need to implement `BinaryObjectScanner.Interfaces.IPathCheck`.
|
||||
|
||||
- If it is a new supported executable packer, compressor, or installer format, create the file in `BinaryObjectScanner.Packer`. By default, you will need to implement `BinaryObjectScanner.Interfaces.IExtractable` as well as at least one of: `BinaryObjectScanner.Interfaces.ILinearExecutableCheck`, `BinaryObjectScanner.Interfaces.INewExecutableCheck`, and `BinaryObjectScanner.Interfaces.IPortableExecutableCheck`. It is exceptionally rare to need to implement `BinaryObjectScanner.Interfaces.IPathCheck`.
|
||||
|
||||
- If it is a new supported DRM scheme, copy protection, or obfuscator, create the file in `BinaryObjectScanner.Protection`. By default, you will need to implement at least one of:`BinaryObjectScanner.Interfaces.ILinearExecutableCheck`, `BinaryObjectScanner.Interfaces.INewExecutableCheck`, `BinaryObjectScanner.Interfaces.IPortableExecutableCheck`, and `BinaryObjectScanner.Interfaces.IPathCheck`. It is exceptionally rare to need to implement `BinaryObjectScanner.Interfaces.Extractable`.
|
||||
|
||||
@@ -68,6 +68,11 @@ namespace Test
|
||||
/// </summary>
|
||||
public bool ScanContents { get; private set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Scan game engines during protection scanning
|
||||
/// </summary>
|
||||
public bool ScanGameEngines { get; private set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Scan packers during protection scanning
|
||||
/// </summary>
|
||||
@@ -180,6 +185,11 @@ namespace Test
|
||||
options.ScanContents = false;
|
||||
break;
|
||||
|
||||
case "-ng":
|
||||
case "--no-game-engines":
|
||||
options.ScanGameEngines = false;
|
||||
break;
|
||||
|
||||
case "-np":
|
||||
case "--no-packers":
|
||||
options.ScanPackers = false;
|
||||
@@ -230,27 +240,28 @@ namespace Test
|
||||
Console.WriteLine("test.exe <features> <options> file|directory ...");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Features:");
|
||||
Console.WriteLine("-x, --extract Extract archive formats");
|
||||
Console.WriteLine("-i, --info Print executable info");
|
||||
Console.WriteLine("-s, --scan Enable protection scanning (default if none)");
|
||||
Console.WriteLine("-x, --extract Extract archive formats");
|
||||
Console.WriteLine("-i, --info Print executable info");
|
||||
Console.WriteLine("-s, --scan Enable protection scanning (default if none)");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Common options:");
|
||||
Console.WriteLine("-?, -h, --help Display this help text and quit");
|
||||
Console.WriteLine("-d, --debug Enable debug mode");
|
||||
Console.WriteLine("-?, -h, --help Display this help text and quit");
|
||||
Console.WriteLine("-d, --debug Enable debug mode");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Extraction options:");
|
||||
Console.WriteLine("-o, --outdir [PATH] Set output path for extraction (required)");
|
||||
Console.WriteLine("-o, --outdir [PATH] Set output path for extraction (required)");
|
||||
#if NET6_0_OR_GREATER
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Information options:");
|
||||
Console.WriteLine("-j, --json Print executable info as JSON");
|
||||
Console.WriteLine("-j, --json Print executable info as JSON");
|
||||
#endif
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Scanning options:");
|
||||
Console.WriteLine("-nc, --no-contents Disable scanning for content checks");
|
||||
Console.WriteLine("-na, --no-archives Disable scanning archives");
|
||||
Console.WriteLine("-np, --no-packers Disable scanning for packers");
|
||||
Console.WriteLine("-ns, --no-paths Disable scanning for path checks");
|
||||
Console.WriteLine("-nc, --no-contents Disable scanning for content checks");
|
||||
Console.WriteLine("-na, --no-archives Disable scanning archives");
|
||||
Console.WriteLine("-ng, --no-game-engines Disable scanning for game engines");
|
||||
Console.WriteLine("-np, --no-packers Disable scanning for packers");
|
||||
Console.WriteLine("-ns, --no-paths Disable scanning for path checks");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace Test
|
||||
var scanner = new Scanner(
|
||||
options.ScanArchives,
|
||||
options.ScanContents,
|
||||
options.ScanGameEngines,
|
||||
options.ScanPackers,
|
||||
options.ScanPaths,
|
||||
options.Debug,
|
||||
|
||||
Reference in New Issue
Block a user