mirror of
https://github.com/SabreTools/BinaryObjectScanner.git
synced 2026-02-06 05:35:20 +00:00
Compare commits
210 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aeb379f307 | ||
|
|
cae0edfb49 | ||
|
|
e97558db1f | ||
|
|
06ecc1a571 | ||
|
|
adcf9ee27e | ||
|
|
a8c759be55 | ||
|
|
ecf97de439 | ||
|
|
f134de6609 | ||
|
|
d91ce3100b | ||
|
|
20de40014d | ||
|
|
a7f9a99d10 | ||
|
|
4bdfbabaab | ||
|
|
5465c57d96 | ||
|
|
5082c8f705 | ||
|
|
509fff4684 | ||
|
|
9d2c14f809 | ||
|
|
b023549b87 | ||
|
|
0983073538 | ||
|
|
815acf2ce0 | ||
|
|
6b8596466b | ||
|
|
1e5bb7df64 | ||
|
|
e920bfc69c | ||
|
|
a85c6f4028 | ||
|
|
a75bc15f29 | ||
|
|
659aa30fb3 | ||
|
|
54a11916d2 | ||
|
|
47f423d092 | ||
|
|
5c5e68e31d | ||
|
|
9e917e2bb9 | ||
|
|
4bd4d2f395 | ||
|
|
6406248840 | ||
|
|
3d8134bbd3 | ||
|
|
15310a6c47 | ||
|
|
450a8cd5bd | ||
|
|
088f99942f | ||
|
|
3e97bd8d2d | ||
|
|
866051c975 | ||
|
|
0be437f3cf | ||
|
|
11f3dec65d | ||
|
|
912d151164 | ||
|
|
863678f850 | ||
|
|
b82a6a8c5d | ||
|
|
ab2daf2a80 | ||
|
|
f734e3a58a | ||
|
|
212981fda9 | ||
|
|
c64e138ba7 | ||
|
|
c43353d126 | ||
|
|
d439ba9592 | ||
|
|
b7f06f0b59 | ||
|
|
60d666f8be | ||
|
|
4ff4c2afef | ||
|
|
655b8385f9 | ||
|
|
439c141c2c | ||
|
|
95755b930d | ||
|
|
9cf54c1f2d | ||
|
|
e118418a23 | ||
|
|
a3567d6eb2 | ||
|
|
a359143bc7 | ||
|
|
77b7a24d85 | ||
|
|
338b2593d5 | ||
|
|
ded5e27355 | ||
|
|
ee1b005d96 | ||
|
|
cd2673d1ba | ||
|
|
41d77085ae | ||
|
|
67059b2e43 | ||
|
|
f6157ef79b | ||
|
|
6e5d517e82 | ||
|
|
1dca51988a | ||
|
|
3c064bad55 | ||
|
|
d60a3ce05a | ||
|
|
f68438ff8c | ||
|
|
d351f1d08e | ||
|
|
70bdb8f37d | ||
|
|
3cd713e078 | ||
|
|
454655de5a | ||
|
|
268ed1a6a6 | ||
|
|
a42ce601b8 | ||
|
|
756b37ef6c | ||
|
|
ab7e708e02 | ||
|
|
326d916c0b | ||
|
|
db09bd931b | ||
|
|
d1e9eb90f1 | ||
|
|
20a5c4c78d | ||
|
|
499f9888b1 | ||
|
|
7bb3364b43 | ||
|
|
473cbc5694 | ||
|
|
e32b24c9f6 | ||
|
|
777fdc14c8 | ||
|
|
ffbb01c25c | ||
|
|
47380c2c1c | ||
|
|
51e9121a6b | ||
|
|
94f51d518d | ||
|
|
8fdc17b239 | ||
|
|
42bb29185f | ||
|
|
05ae0f4e80 | ||
|
|
092374a143 | ||
|
|
3cfb60430a | ||
|
|
370cc68fa4 | ||
|
|
8fe5046c19 | ||
|
|
37e7604441 | ||
|
|
7651b34855 | ||
|
|
4bf89b1d5f | ||
|
|
0287284909 | ||
|
|
a8453b3f21 | ||
|
|
2552564953 | ||
|
|
0d4d19559a | ||
|
|
52f4132ccb | ||
|
|
cb6440662b | ||
|
|
6293895611 | ||
|
|
f564fb6e9e | ||
|
|
3f2adfcf62 | ||
|
|
2c979f291e | ||
|
|
7e7b2ee64a | ||
|
|
87108405a8 | ||
|
|
9fb055cbff | ||
|
|
e690f0137e | ||
|
|
87c08d6fbd | ||
|
|
8c164d776e | ||
|
|
964271b4e1 | ||
|
|
e99ba48f07 | ||
|
|
62b1627b04 | ||
|
|
3a54997d42 | ||
|
|
7d95a43b4b | ||
|
|
23ea8710c0 | ||
|
|
0b62a52991 | ||
|
|
1143c8a8b7 | ||
|
|
a5b66caae6 | ||
|
|
b0b87d05fd | ||
|
|
cb3c666f64 | ||
|
|
12fdae7944 | ||
|
|
e76bc70ec6 | ||
|
|
e78bb8cb41 | ||
|
|
5153e73f42 | ||
|
|
7f36ff8a2b | ||
|
|
99a8a39dda | ||
|
|
adbf983e65 | ||
|
|
d7639495ac | ||
|
|
fbe09d9082 | ||
|
|
70468b72c3 | ||
|
|
90f4af1121 | ||
|
|
aa37449bbf | ||
|
|
c835e04722 | ||
|
|
29b999b8ed | ||
|
|
9ddd6cc317 | ||
|
|
3a694f0e31 | ||
|
|
080cbda588 | ||
|
|
fd066e8aae | ||
|
|
0fd0cf689a | ||
|
|
f85adda24c | ||
|
|
2d1e8e02aa | ||
|
|
371fbee7a4 | ||
|
|
a5bb95e7c1 | ||
|
|
53b5a443fe | ||
|
|
a230871f75 | ||
|
|
f560ce17e8 | ||
|
|
b96329bd33 | ||
|
|
913f7802de | ||
|
|
a9f61ed51e | ||
|
|
04c0835228 | ||
|
|
af7ff05ecf | ||
|
|
4ccf80189e | ||
|
|
b417229ee6 | ||
|
|
61457582b3 | ||
|
|
ecc1613f49 | ||
|
|
5ea89eefe8 | ||
|
|
661808826a | ||
|
|
342f78ffd0 | ||
|
|
cc6a65d5e4 | ||
|
|
068ee76983 | ||
|
|
b980b33019 | ||
|
|
4327ce7848 | ||
|
|
2c813e7b3d | ||
|
|
d69746f7ef | ||
|
|
ce8f73d30d | ||
|
|
d6602ac8a8 | ||
|
|
6478af03e7 | ||
|
|
f086e63914 | ||
|
|
6fbb6bd8dc | ||
|
|
008629b61f | ||
|
|
b7704dbe57 | ||
|
|
5aff2d0b1b | ||
|
|
771bbeed6a | ||
|
|
53dd1e9aa5 | ||
|
|
6b7ed456ac | ||
|
|
5e2185dffd | ||
|
|
b5d318013b | ||
|
|
07a926e50c | ||
|
|
78bbb63c11 | ||
|
|
1fd613c2b2 | ||
|
|
792833ebc8 | ||
|
|
7392fce770 | ||
|
|
6c621c743d | ||
|
|
50a7883958 | ||
|
|
3882db6fc6 | ||
|
|
c91691f79b | ||
|
|
17fbf1163d | ||
|
|
9a1bbd7e0d | ||
|
|
bf6f3bad46 | ||
|
|
b99b2a53cf | ||
|
|
1fec5c15d4 | ||
|
|
a8b13e60b6 | ||
|
|
4c0c44de6b | ||
|
|
af0623beea | ||
|
|
761b418d21 | ||
|
|
0316edb8cb | ||
|
|
48cf417d60 | ||
|
|
83af2926aa | ||
|
|
ddfa820004 | ||
|
|
80986978cb | ||
|
|
68283554e9 |
4
.gitmodules
vendored
4
.gitmodules
vendored
@@ -1,3 +1,3 @@
|
||||
[submodule "BurnOutSharp/External/stormlibsharp"]
|
||||
path = BurnOutSharp/External/stormlibsharp
|
||||
[submodule "BinaryObjectScanner.Compression/_EXTERNAL/stormlibsharp"]
|
||||
path = BinaryObjectScanner.Compression/_EXTERNAL/stormlibsharp
|
||||
url = https://github.com/robpaveza/stormlibsharp.git
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BurnOutSharp.ASN1
|
||||
namespace BinaryObjectScanner.ASN1
|
||||
{
|
||||
/// <summary>
|
||||
/// ASN.1 Parser
|
||||
@@ -1,17 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;net6.0</TargetFrameworks>
|
||||
<TargetFrameworks>net48;net6.0;net7.0</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win-x86;win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<Title>BurnOutSharp.ASN1</Title>
|
||||
<AssemblyName>BurnOutSharp.ASN1</AssemblyName>
|
||||
<Title>BinaryObjectScanner.ASN1</Title>
|
||||
<AssemblyName>BinaryObjectScanner.ASN1</AssemblyName>
|
||||
<Authors>Matt Nadareski</Authors>
|
||||
<Product>BurnOutSharp</Product>
|
||||
<Copyright>Copyright (c)2022 Matt Nadareski</Copyright>
|
||||
<RepositoryUrl>https://github.com/mnadareski/BurnOutSharp</RepositoryUrl>
|
||||
<Version>2.6</Version>
|
||||
<AssemblyVersion>2.6</AssemblyVersion>
|
||||
<FileVersion>2.6</FileVersion>
|
||||
<Version>2.8</Version>
|
||||
<AssemblyVersion>2.8</AssemblyVersion>
|
||||
<FileVersion>2.8</FileVersion>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
</PropertyGroup>
|
||||
@@ -21,7 +21,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BurnOutSharp.Utilities\BurnOutSharp.Utilities.csproj" />
|
||||
<ProjectReference Include="..\BinaryObjectScanner.Utilities\BinaryObjectScanner.Utilities.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace BurnOutSharp.ASN1
|
||||
namespace BinaryObjectScanner.ASN1
|
||||
{
|
||||
/// <summary>
|
||||
/// ASN.1 type indicators
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace BurnOutSharp.ASN1
|
||||
namespace BinaryObjectScanner.ASN1
|
||||
{
|
||||
#pragma warning disable IDE0011
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BurnOutSharp.ASN1
|
||||
namespace BinaryObjectScanner.ASN1
|
||||
{
|
||||
#pragma warning disable IDE0011
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BurnOutSharp.ASN1
|
||||
namespace BinaryObjectScanner.ASN1
|
||||
{
|
||||
#pragma warning disable IDE0011
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BurnOutSharp.ASN1
|
||||
namespace BinaryObjectScanner.ASN1
|
||||
{
|
||||
/// <summary>
|
||||
/// Methods related to Object Identifiers (OID)
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace BurnOutSharp.ASN1
|
||||
namespace BinaryObjectScanner.ASN1
|
||||
{
|
||||
#pragma warning disable IDE0011
|
||||
|
||||
@@ -3,9 +3,9 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using BurnOutSharp.Utilities;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
|
||||
namespace BurnOutSharp.ASN1
|
||||
namespace BinaryObjectScanner.ASN1
|
||||
{
|
||||
/// <summary>
|
||||
/// ASN.1 type/length/value class that all types are based on
|
||||
470
BinaryObjectScanner.Builders/AACS.cs
Normal file
470
BinaryObjectScanner.Builders/AACS.cs
Normal file
@@ -0,0 +1,470 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BinaryObjectScanner.Models.AACS;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
|
||||
namespace BinaryObjectScanner.Builders
|
||||
{
|
||||
public class AACS
|
||||
{
|
||||
#region Byte Data
|
||||
|
||||
/// <summary>
|
||||
/// Parse a byte array into an AACS media key block
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array to parse</param>
|
||||
/// <param name="offset">Offset into the byte array</param>
|
||||
/// <returns>Filled archive on success, null on error</returns>
|
||||
public static MediaKeyBlock ParseMediaKeyBlock(byte[] data, int offset)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
// If the offset is out of bounds
|
||||
if (offset < 0 || offset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Create a memory stream and parse that
|
||||
MemoryStream dataStream = new MemoryStream(data, offset, data.Length - offset);
|
||||
return ParseMediaKeyBlock(dataStream);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Stream Data
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an AACS media key block
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled cmedia key block on success, null on error</returns>
|
||||
public static MediaKeyBlock ParseMediaKeyBlock(Stream data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
|
||||
return null;
|
||||
|
||||
// If the offset is out of bounds
|
||||
if (data.Position < 0 || data.Position >= data.Length)
|
||||
return null;
|
||||
|
||||
// Cache the current offset
|
||||
int initialOffset = (int)data.Position;
|
||||
|
||||
// Create a new media key block to fill
|
||||
var mediaKeyBlock = new MediaKeyBlock();
|
||||
|
||||
#region Records
|
||||
|
||||
// Create the records list
|
||||
var records = new List<Record>();
|
||||
|
||||
// Try to parse the records
|
||||
while (data.Position < data.Length)
|
||||
{
|
||||
// Try to parse the record
|
||||
var record = ParseRecord(data);
|
||||
if (record == null)
|
||||
return null;
|
||||
|
||||
// Add the record
|
||||
records.Add(record);
|
||||
|
||||
// If we have an end of media key block record
|
||||
if (record.RecordType == RecordType.EndOfMediaKeyBlock)
|
||||
break;
|
||||
|
||||
// Align to the 4-byte boundary if we're not at the end
|
||||
if (data.Position != data.Length)
|
||||
{
|
||||
while ((data.Position % 4) != 0)
|
||||
_ = data.ReadByteValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the records
|
||||
mediaKeyBlock.Records = records.ToArray();
|
||||
|
||||
#endregion
|
||||
|
||||
return mediaKeyBlock;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a record
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled record on success, null on error</returns>
|
||||
private static Record ParseRecord(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
|
||||
// The first 4 bytes make up the type and length
|
||||
byte[] typeAndLength = data.ReadBytes(4);
|
||||
RecordType type = (RecordType)typeAndLength[0];
|
||||
|
||||
// Remove the first byte and parse as big-endian
|
||||
typeAndLength[0] = 0x00;
|
||||
Array.Reverse(typeAndLength);
|
||||
uint length = BitConverter.ToUInt32(typeAndLength, 0);
|
||||
|
||||
// Create a record based on the type
|
||||
switch (type)
|
||||
{
|
||||
// Recognized record types
|
||||
case RecordType.EndOfMediaKeyBlock: return ParseEndOfMediaKeyBlockRecord(data, type, length);
|
||||
case RecordType.ExplicitSubsetDifference: return ParseExplicitSubsetDifferenceRecord(data, type, length);
|
||||
case RecordType.MediaKeyData: return ParseMediaKeyDataRecord(data, type, length);
|
||||
case RecordType.SubsetDifferenceIndex: return ParseSubsetDifferenceIndexRecord(data, type, length);
|
||||
case RecordType.TypeAndVersion: return ParseTypeAndVersionRecord(data, type, length);
|
||||
case RecordType.DriveRevocationList: return ParseDriveRevocationListRecord(data, type, length);
|
||||
case RecordType.HostRevocationList: return ParseHostRevocationListRecord(data, type, length);
|
||||
case RecordType.VerifyMediaKey: return ParseVerifyMediaKeyRecord(data, type, length);
|
||||
case RecordType.Copyright: return ParseCopyrightRecord(data, type, length);
|
||||
|
||||
// Unrecognized record type
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an end of media key block record
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled end of media key block record on success, null on error</returns>
|
||||
private static EndOfMediaKeyBlockRecord ParseEndOfMediaKeyBlockRecord(Stream data, RecordType type, uint length)
|
||||
{
|
||||
// Verify we're calling the right parser
|
||||
if (type != RecordType.EndOfMediaKeyBlock)
|
||||
return null;
|
||||
|
||||
// TODO: Use marshalling here instead of building
|
||||
var record = new EndOfMediaKeyBlockRecord();
|
||||
|
||||
record.RecordType = type;
|
||||
record.RecordLength = length;
|
||||
if (length > 4)
|
||||
record.SignatureData = data.ReadBytes((int)(length - 4));
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an explicit subset-difference record
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled explicit subset-difference record on success, null on error</returns>
|
||||
private static ExplicitSubsetDifferenceRecord ParseExplicitSubsetDifferenceRecord(Stream data, RecordType type, uint length)
|
||||
{
|
||||
// Verify we're calling the right parser
|
||||
if (type != RecordType.ExplicitSubsetDifference)
|
||||
return null;
|
||||
|
||||
// TODO: Use marshalling here instead of building
|
||||
var record = new ExplicitSubsetDifferenceRecord();
|
||||
|
||||
record.RecordType = type;
|
||||
record.RecordLength = length;
|
||||
|
||||
// Cache the current offset
|
||||
long initialOffset = data.Position - 4;
|
||||
|
||||
// Create the subset difference list
|
||||
var subsetDifferences = new List<SubsetDifference>();
|
||||
|
||||
// Try to parse the subset differences
|
||||
while (data.Position < initialOffset + length - 5)
|
||||
{
|
||||
var subsetDifference = new SubsetDifference();
|
||||
|
||||
subsetDifference.Mask = data.ReadByteValue();
|
||||
subsetDifference.Number = data.ReadUInt32BE();
|
||||
|
||||
subsetDifferences.Add(subsetDifference);
|
||||
}
|
||||
|
||||
// Set the subset differences
|
||||
record.SubsetDifferences = subsetDifferences.ToArray();
|
||||
|
||||
// If there's any data left, discard it
|
||||
if (data.Position < initialOffset + length)
|
||||
_ = data.ReadBytes((int)(initialOffset + length - data.Position));
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a media key data record
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled media key data record on success, null on error</returns>
|
||||
private static MediaKeyDataRecord ParseMediaKeyDataRecord(Stream data, RecordType type, uint length)
|
||||
{
|
||||
// Verify we're calling the right parser
|
||||
if (type != RecordType.MediaKeyData)
|
||||
return null;
|
||||
|
||||
// TODO: Use marshalling here instead of building
|
||||
var record = new MediaKeyDataRecord();
|
||||
|
||||
record.RecordType = type;
|
||||
record.RecordLength = length;
|
||||
|
||||
// Cache the current offset
|
||||
long initialOffset = data.Position - 4;
|
||||
|
||||
// Create the media key list
|
||||
var mediaKeys = new List<byte[]>();
|
||||
|
||||
// Try to parse the media keys
|
||||
while (data.Position < initialOffset + length)
|
||||
{
|
||||
byte[] mediaKey = data.ReadBytes(0x10);
|
||||
mediaKeys.Add(mediaKey);
|
||||
}
|
||||
|
||||
// Set the media keys
|
||||
record.MediaKeyData = mediaKeys.ToArray();
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a subset-difference index record
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled subset-difference index record on success, null on error</returns>
|
||||
private static SubsetDifferenceIndexRecord ParseSubsetDifferenceIndexRecord(Stream data, RecordType type, uint length)
|
||||
{
|
||||
// Verify we're calling the right parser
|
||||
if (type != RecordType.SubsetDifferenceIndex)
|
||||
return null;
|
||||
|
||||
// TODO: Use marshalling here instead of building
|
||||
var record = new SubsetDifferenceIndexRecord();
|
||||
|
||||
record.RecordType = type;
|
||||
record.RecordLength = length;
|
||||
|
||||
// Cache the current offset
|
||||
long initialOffset = data.Position - 4;
|
||||
|
||||
record.Span = data.ReadUInt32BE();
|
||||
|
||||
// Create the offset list
|
||||
var offsets = new List<uint>();
|
||||
|
||||
// Try to parse the offsets
|
||||
while (data.Position < initialOffset + length)
|
||||
{
|
||||
uint offset = data.ReadUInt32BE();
|
||||
offsets.Add(offset);
|
||||
}
|
||||
|
||||
// Set the offsets
|
||||
record.Offsets = offsets.ToArray();
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a type and version record
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled type and version record on success, null on error</returns>
|
||||
private static TypeAndVersionRecord ParseTypeAndVersionRecord(Stream data, RecordType type, uint length)
|
||||
{
|
||||
// Verify we're calling the right parser
|
||||
if (type != RecordType.TypeAndVersion)
|
||||
return null;
|
||||
|
||||
// TODO: Use marshalling here instead of building
|
||||
var record = new TypeAndVersionRecord();
|
||||
|
||||
record.RecordType = type;
|
||||
record.RecordLength = length;
|
||||
record.MediaKeyBlockType = (MediaKeyBlockType)data.ReadUInt32BE();
|
||||
record.VersionNumber = data.ReadUInt32BE();
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a drive revocation list record
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled drive revocation list record on success, null on error</returns>
|
||||
private static DriveRevocationListRecord ParseDriveRevocationListRecord(Stream data, RecordType type, uint length)
|
||||
{
|
||||
// Verify we're calling the right parser
|
||||
if (type != RecordType.DriveRevocationList)
|
||||
return null;
|
||||
|
||||
// TODO: Use marshalling here instead of building
|
||||
var record = new DriveRevocationListRecord();
|
||||
|
||||
record.RecordType = type;
|
||||
record.RecordLength = length;
|
||||
|
||||
// Cache the current offset
|
||||
long initialOffset = data.Position - 4;
|
||||
|
||||
record.TotalNumberOfEntries = data.ReadUInt32BE();
|
||||
|
||||
// Create the signature blocks list
|
||||
var blocks = new List<DriveRevocationSignatureBlock>();
|
||||
|
||||
// Try to parse the signature blocks
|
||||
int entryCount = 0;
|
||||
while (entryCount < record.TotalNumberOfEntries && data.Position < initialOffset + length)
|
||||
{
|
||||
var block = new DriveRevocationSignatureBlock();
|
||||
|
||||
block.NumberOfEntries = data.ReadUInt32BE();
|
||||
block.EntryFields = new DriveRevocationListEntry[block.NumberOfEntries];
|
||||
for (int i = 0; i < block.EntryFields.Length; i++)
|
||||
{
|
||||
var entry = new DriveRevocationListEntry();
|
||||
|
||||
entry.Range = data.ReadUInt16BE();
|
||||
entry.DriveID = data.ReadBytes(6);
|
||||
|
||||
block.EntryFields[i] = entry;
|
||||
entryCount++;
|
||||
}
|
||||
|
||||
blocks.Add(block);
|
||||
|
||||
// If we have an empty block
|
||||
if (block.NumberOfEntries == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// Set the signature blocks
|
||||
record.SignatureBlocks = blocks.ToArray();
|
||||
|
||||
// If there's any data left, discard it
|
||||
if (data.Position < initialOffset + length)
|
||||
_ = data.ReadBytes((int)(initialOffset + length - data.Position));
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a host revocation list record
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled host revocation list record on success, null on error</returns>
|
||||
private static HostRevocationListRecord ParseHostRevocationListRecord(Stream data, RecordType type, uint length)
|
||||
{
|
||||
// Verify we're calling the right parser
|
||||
if (type != RecordType.HostRevocationList)
|
||||
return null;
|
||||
|
||||
// TODO: Use marshalling here instead of building
|
||||
var record = new HostRevocationListRecord();
|
||||
|
||||
record.RecordType = type;
|
||||
record.RecordLength = length;
|
||||
|
||||
// Cache the current offset
|
||||
long initialOffset = data.Position - 4;
|
||||
|
||||
record.TotalNumberOfEntries = data.ReadUInt32BE();
|
||||
|
||||
// Create the signature blocks list
|
||||
var blocks = new List<HostRevocationSignatureBlock>();
|
||||
|
||||
// Try to parse the signature blocks
|
||||
int entryCount = 0;
|
||||
while (entryCount < record.TotalNumberOfEntries && data.Position < initialOffset + length)
|
||||
{
|
||||
var block = new HostRevocationSignatureBlock();
|
||||
|
||||
block.NumberOfEntries = data.ReadUInt32BE();
|
||||
block.EntryFields = new HostRevocationListEntry[block.NumberOfEntries];
|
||||
for (int i = 0; i < block.EntryFields.Length; i++)
|
||||
{
|
||||
var entry = new HostRevocationListEntry();
|
||||
|
||||
entry.Range = data.ReadUInt16BE();
|
||||
entry.HostID = data.ReadBytes(6);
|
||||
|
||||
block.EntryFields[i] = entry;
|
||||
entryCount++;
|
||||
}
|
||||
|
||||
blocks.Add(block);
|
||||
|
||||
// If we have an empty block
|
||||
if (block.NumberOfEntries == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// Set the signature blocks
|
||||
record.SignatureBlocks = blocks.ToArray();
|
||||
|
||||
// If there's any data left, discard it
|
||||
if (data.Position < initialOffset + length)
|
||||
_ = data.ReadBytes((int)(initialOffset + length - data.Position));
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a verify media key record
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled verify media key record on success, null on error</returns>
|
||||
private static VerifyMediaKeyRecord ParseVerifyMediaKeyRecord(Stream data, RecordType type, uint length)
|
||||
{
|
||||
// Verify we're calling the right parser
|
||||
if (type != RecordType.VerifyMediaKey)
|
||||
return null;
|
||||
|
||||
// TODO: Use marshalling here instead of building
|
||||
var record = new VerifyMediaKeyRecord();
|
||||
|
||||
record.RecordType = type;
|
||||
record.RecordLength = length;
|
||||
record.CiphertextValue = data.ReadBytes(0x10);
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a copyright record
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled copyright record on success, null on error</returns>
|
||||
private static CopyrightRecord ParseCopyrightRecord(Stream data, RecordType type, uint length)
|
||||
{
|
||||
// Verify we're calling the right parser
|
||||
if (type != RecordType.Copyright)
|
||||
return null;
|
||||
|
||||
// TODO: Use marshalling here instead of building
|
||||
var record = new CopyrightRecord();
|
||||
|
||||
record.RecordType = type;
|
||||
record.RecordLength = length;
|
||||
if (length > 4)
|
||||
{
|
||||
byte[] copyright = data.ReadBytes((int)(length - 4));
|
||||
record.Copyright = Encoding.ASCII.GetString(copyright).TrimEnd('\0');
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
95
BinaryObjectScanner.Builders/BDPlus.cs
Normal file
95
BinaryObjectScanner.Builders/BDPlus.cs
Normal file
@@ -0,0 +1,95 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BinaryObjectScanner.Models.BDPlus;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
using static BinaryObjectScanner.Models.BDPlus.Constants;
|
||||
|
||||
namespace BinaryObjectScanner.Builders
|
||||
{
|
||||
public class BDPlus
|
||||
{
|
||||
#region Byte Data
|
||||
|
||||
/// <summary>
|
||||
/// Parse a byte array into a BD+ SVM
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array to parse</param>
|
||||
/// <param name="offset">Offset into the byte array</param>
|
||||
/// <returns>Filled BD+ SVM on success, null on error</returns>
|
||||
public static SVM ParseSVM(byte[] data, int offset)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
// If the offset is out of bounds
|
||||
if (offset < 0 || offset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Create a memory stream and parse that
|
||||
MemoryStream dataStream = new MemoryStream(data, offset, data.Length - offset);
|
||||
return ParseSVM(dataStream);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Stream Data
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an BD+ SVM
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled BD+ SVM on success, null on error</returns>
|
||||
public static SVM ParseSVM(Stream data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
|
||||
return null;
|
||||
|
||||
// If the offset is out of bounds
|
||||
if (data.Position < 0 || data.Position >= data.Length)
|
||||
return null;
|
||||
|
||||
// Cache the current offset
|
||||
int initialOffset = (int)data.Position;
|
||||
|
||||
// Try to parse the SVM
|
||||
return ParseSVMData(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an SVM
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled SVM on success, null on error</returns>
|
||||
private static SVM ParseSVMData(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
var svm = new SVM();
|
||||
|
||||
byte[] signature = data.ReadBytes(8);
|
||||
svm.Signature = Encoding.ASCII.GetString(signature);
|
||||
if (svm.Signature != SignatureString)
|
||||
return null;
|
||||
|
||||
svm.Unknown1 = data.ReadBytes(5);
|
||||
svm.Year = data.ReadUInt16BE();
|
||||
svm.Month = data.ReadByteValue();
|
||||
if (svm.Month < 1 || svm.Month > 12)
|
||||
return null;
|
||||
|
||||
svm.Day = data.ReadByteValue();
|
||||
if (svm.Day < 1 || svm.Day > 31)
|
||||
return null;
|
||||
|
||||
svm.Unknown2 = data.ReadBytes(4);
|
||||
svm.Length = data.ReadUInt32();
|
||||
// if (svm.Length > 0)
|
||||
// svm.Data = data.ReadBytes((int)svm.Length);
|
||||
|
||||
return svm;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using BurnOutSharp.Models.BFPK;
|
||||
using BurnOutSharp.Utilities;
|
||||
using static BurnOutSharp.Models.BFPK.Constants;
|
||||
using BinaryObjectScanner.Models.BFPK;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
using static BinaryObjectScanner.Models.BFPK.Constants;
|
||||
|
||||
namespace BurnOutSharp.Builders
|
||||
namespace BinaryObjectScanner.Builders
|
||||
{
|
||||
public class BFPK
|
||||
{
|
||||
@@ -1,11 +1,11 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using BurnOutSharp.Models.BSP;
|
||||
using BurnOutSharp.Utilities;
|
||||
using static BurnOutSharp.Models.BSP.Constants;
|
||||
using BinaryObjectScanner.Models.BSP;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
using static BinaryObjectScanner.Models.BSP.Constants;
|
||||
|
||||
namespace BurnOutSharp.Builders
|
||||
namespace BinaryObjectScanner.Builders
|
||||
{
|
||||
public static class BSP
|
||||
{
|
||||
@@ -0,0 +1,28 @@
|
||||
<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.Builders</Title>
|
||||
<AssemblyName>BinaryObjectScanner.Builders</AssemblyName>
|
||||
<Authors>Matt Nadareski</Authors>
|
||||
<Product>BurnOutSharp</Product>
|
||||
<Copyright>Copyright (c)2022 Matt Nadareski</Copyright>
|
||||
<RepositoryUrl>https://github.com/mnadareski/BurnOutSharp</RepositoryUrl>
|
||||
<Version>2.8</Version>
|
||||
<AssemblyVersion>2.8</AssemblyVersion>
|
||||
<FileVersion>2.8</FileVersion>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BinaryObjectScanner.Models\BinaryObjectScanner.Models.csproj" />
|
||||
<ProjectReference Include="..\BinaryObjectScanner.Utilities\BinaryObjectScanner.Utilities.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
391
BinaryObjectScanner.Builders/CFB.cs
Normal file
391
BinaryObjectScanner.Builders/CFB.cs
Normal file
@@ -0,0 +1,391 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BinaryObjectScanner.Models.CFB;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
using static BinaryObjectScanner.Models.CFB.Constants;
|
||||
|
||||
namespace BinaryObjectScanner.Builders
|
||||
{
|
||||
public class CFB
|
||||
{
|
||||
#region Byte Data
|
||||
|
||||
/// <summary>
|
||||
/// Parse a byte array into a Compound File Binary
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array to parse</param>
|
||||
/// <param name="offset">Offset into the byte array</param>
|
||||
/// <returns>Filled Compound File Binary on success, null on error</returns>
|
||||
public static Binary ParseBinary(byte[] data, int offset)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
// If the offset is out of bounds
|
||||
if (offset < 0 || offset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Create a memory stream and parse that
|
||||
MemoryStream dataStream = new MemoryStream(data, offset, data.Length - offset);
|
||||
return ParseBinary(dataStream);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Stream Data
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Compound File Binary
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Compound File Binary on success, null on error</returns>
|
||||
public static Binary ParseBinary(Stream data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
|
||||
return null;
|
||||
|
||||
// If the offset is out of bounds
|
||||
if (data.Position < 0 || data.Position >= data.Length)
|
||||
return null;
|
||||
|
||||
// Cache the current offset
|
||||
int initialOffset = (int)data.Position;
|
||||
|
||||
// Create a new binary to fill
|
||||
var binary = new Binary();
|
||||
|
||||
#region Header
|
||||
|
||||
// Try to parse the file header
|
||||
var fileHeader = ParseFileHeader(data);
|
||||
if (fileHeader == null)
|
||||
return null;
|
||||
|
||||
// Set the file header
|
||||
binary.Header = fileHeader;
|
||||
|
||||
#endregion
|
||||
|
||||
#region DIFAT Sector Numbers
|
||||
|
||||
// Create a DIFAT sector table
|
||||
var difatSectors = new List<SectorNumber>();
|
||||
|
||||
// Add the sectors from the header
|
||||
difatSectors.AddRange(fileHeader.DIFAT);
|
||||
|
||||
// Loop through and add the DIFAT sectors
|
||||
SectorNumber currentSector = (SectorNumber)fileHeader.FirstDIFATSectorLocation;
|
||||
for (int i = 0; i < fileHeader.NumberOfDIFATSectors; i++)
|
||||
{
|
||||
// If we have a readable sector
|
||||
if (currentSector <= SectorNumber.MAXREGSECT)
|
||||
{
|
||||
// Get the new next sector information
|
||||
long sectorOffset = (long)((long)(currentSector + 1) * Math.Pow(2, fileHeader.SectorShift));
|
||||
if (sectorOffset < 0 || sectorOffset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Seek to the next sector
|
||||
data.Seek(sectorOffset, SeekOrigin.Begin);
|
||||
|
||||
// Try to parse the sectors
|
||||
var sectorNumbers = ParseSectorNumbers(data, fileHeader.SectorShift);
|
||||
if (sectorNumbers == null)
|
||||
return null;
|
||||
|
||||
// Add the sector shifts
|
||||
difatSectors.AddRange(sectorNumbers);
|
||||
}
|
||||
|
||||
// Get the next sector from the DIFAT
|
||||
currentSector = difatSectors[i];
|
||||
}
|
||||
|
||||
// Assign the DIFAT sectors table
|
||||
binary.DIFATSectorNumbers = difatSectors.ToArray();
|
||||
|
||||
#endregion
|
||||
|
||||
#region FAT Sector Numbers
|
||||
|
||||
// Create a FAT sector table
|
||||
var fatSectors = new List<SectorNumber>();
|
||||
|
||||
// Loop through and add the FAT sectors
|
||||
currentSector = binary.DIFATSectorNumbers[0];
|
||||
for (int i = 0; i < fileHeader.NumberOfFATSectors; i++)
|
||||
{
|
||||
// If we have a readable sector
|
||||
if (currentSector <= SectorNumber.MAXREGSECT)
|
||||
{
|
||||
// Get the new next sector information
|
||||
long sectorOffset = (long)((long)(currentSector + 1) * Math.Pow(2, fileHeader.SectorShift));
|
||||
if (sectorOffset < 0 || sectorOffset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Seek to the next sector
|
||||
data.Seek(sectorOffset, SeekOrigin.Begin);
|
||||
|
||||
// Try to parse the sectors
|
||||
var sectorNumbers = ParseSectorNumbers(data, fileHeader.SectorShift);
|
||||
if (sectorNumbers == null)
|
||||
return null;
|
||||
|
||||
// Add the sector shifts
|
||||
fatSectors.AddRange(sectorNumbers);
|
||||
}
|
||||
|
||||
// Get the next sector from the DIFAT
|
||||
currentSector = binary.DIFATSectorNumbers[i];
|
||||
}
|
||||
|
||||
// Assign the FAT sectors table
|
||||
binary.FATSectorNumbers = fatSectors.ToArray();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Mini FAT Sector Numbers
|
||||
|
||||
// Create a mini FAT sector table
|
||||
var miniFatSectors = new List<SectorNumber>();
|
||||
|
||||
// Loop through and add the mini FAT sectors
|
||||
currentSector = (SectorNumber)fileHeader.FirstMiniFATSectorLocation;
|
||||
for (int i = 0; i < fileHeader.NumberOfMiniFATSectors; i++)
|
||||
{
|
||||
// If we have a readable sector
|
||||
if (currentSector <= SectorNumber.MAXREGSECT)
|
||||
{
|
||||
// Get the new next sector information
|
||||
long sectorOffset = (long)((long)(currentSector + 1) * Math.Pow(2, fileHeader.SectorShift));
|
||||
if (sectorOffset < 0 || sectorOffset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Seek to the next sector
|
||||
data.Seek(sectorOffset, SeekOrigin.Begin);
|
||||
|
||||
// Try to parse the sectors
|
||||
var sectorNumbers = ParseSectorNumbers(data, fileHeader.SectorShift);
|
||||
if (sectorNumbers == null)
|
||||
return null;
|
||||
|
||||
// Add the sector shifts
|
||||
miniFatSectors.AddRange(sectorNumbers);
|
||||
}
|
||||
|
||||
// Get the next sector from the DIFAT
|
||||
currentSector = binary.DIFATSectorNumbers[i];
|
||||
}
|
||||
|
||||
// Assign the mini FAT sectors table
|
||||
binary.MiniFATSectorNumbers = miniFatSectors.ToArray();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Directory Entries
|
||||
|
||||
// Get the offset of the first directory sector
|
||||
long firstDirectoryOffset = (long)(fileHeader.FirstDirectorySectorLocation * Math.Pow(2, fileHeader.SectorShift));
|
||||
if (firstDirectoryOffset < 0 || firstDirectoryOffset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Seek to the first directory sector
|
||||
data.Seek(firstDirectoryOffset, SeekOrigin.Begin);
|
||||
|
||||
// Create a directory sector table
|
||||
var directorySectors = new List<DirectoryEntry>();
|
||||
|
||||
// Get the number of directory sectors
|
||||
uint directorySectorCount = 0;
|
||||
switch (fileHeader.MajorVersion)
|
||||
{
|
||||
case 3:
|
||||
directorySectorCount = int.MaxValue;
|
||||
break;
|
||||
case 4:
|
||||
directorySectorCount = fileHeader.NumberOfDirectorySectors;
|
||||
break;
|
||||
}
|
||||
|
||||
// Loop through and add the directory sectors
|
||||
currentSector = (SectorNumber)fileHeader.FirstDirectorySectorLocation;
|
||||
for (int i = 0; i < directorySectorCount; i++)
|
||||
{
|
||||
// If we have an end of chain
|
||||
if (currentSector == SectorNumber.ENDOFCHAIN)
|
||||
break;
|
||||
|
||||
// If we have a readable sector
|
||||
if (currentSector <= SectorNumber.MAXREGSECT)
|
||||
{
|
||||
// Get the new next sector information
|
||||
long sectorOffset = (long)((long)(currentSector + 1) * Math.Pow(2, fileHeader.SectorShift));
|
||||
if (sectorOffset < 0 || sectorOffset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Seek to the next sector
|
||||
data.Seek(sectorOffset, SeekOrigin.Begin);
|
||||
|
||||
// Try to parse the sectors
|
||||
var directoryEntries = ParseDirectoryEntries(data, fileHeader.SectorShift, fileHeader.MajorVersion);
|
||||
if (directoryEntries == null)
|
||||
return null;
|
||||
|
||||
// Add the sector shifts
|
||||
directorySectors.AddRange(directoryEntries);
|
||||
}
|
||||
|
||||
// Get the next sector from the DIFAT
|
||||
currentSector = binary.DIFATSectorNumbers[i];
|
||||
}
|
||||
|
||||
// Assign the Directory sectors table
|
||||
binary.DirectoryEntries = directorySectors.ToArray();
|
||||
|
||||
#endregion
|
||||
|
||||
return binary;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a file header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled file header on success, null on error</returns>
|
||||
private static FileHeader ParseFileHeader(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
FileHeader header = new FileHeader();
|
||||
|
||||
header.Signature = data.ReadUInt64();
|
||||
if (header.Signature != SignatureUInt64)
|
||||
return null;
|
||||
|
||||
header.CLSID = data.ReadGuid();
|
||||
header.MinorVersion = data.ReadUInt16();
|
||||
header.MajorVersion = data.ReadUInt16();
|
||||
header.ByteOrder = data.ReadUInt16();
|
||||
if (header.ByteOrder != 0xFFFE)
|
||||
return null;
|
||||
|
||||
header.SectorShift = data.ReadUInt16();
|
||||
if (header.MajorVersion == 3 && header.SectorShift != 0x0009)
|
||||
return null;
|
||||
else if (header.MajorVersion == 4 && header.SectorShift != 0x000C)
|
||||
return null;
|
||||
|
||||
header.MiniSectorShift = data.ReadUInt16();
|
||||
header.Reserved = data.ReadBytes(6);
|
||||
header.NumberOfDirectorySectors = data.ReadUInt32();
|
||||
if (header.MajorVersion == 3 && header.NumberOfDirectorySectors != 0)
|
||||
return null;
|
||||
|
||||
header.NumberOfFATSectors = data.ReadUInt32();
|
||||
header.FirstDirectorySectorLocation = data.ReadUInt32();
|
||||
header.TransactionSignatureNumber = data.ReadUInt32();
|
||||
header.MiniStreamCutoffSize = data.ReadUInt32();
|
||||
if (header.MiniStreamCutoffSize != 0x00001000)
|
||||
return null;
|
||||
|
||||
header.FirstMiniFATSectorLocation = data.ReadUInt32();
|
||||
header.NumberOfMiniFATSectors = data.ReadUInt32();
|
||||
header.FirstDIFATSectorLocation = data.ReadUInt32();
|
||||
header.NumberOfDIFATSectors = data.ReadUInt32();
|
||||
header.DIFAT = new SectorNumber[109];
|
||||
for (int i = 0; i < header.DIFAT.Length; i++)
|
||||
{
|
||||
header.DIFAT[i] = (SectorNumber)data.ReadUInt32();
|
||||
}
|
||||
|
||||
// Skip rest of sector for version 4
|
||||
if (header.MajorVersion == 4)
|
||||
_ = data.ReadBytes(3584);
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a sector full of sector numbers
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="sectorShift">Sector shift from the header</param>
|
||||
/// <returns>Filled sector full of sector numbers on success, null on error</returns>
|
||||
private static SectorNumber[] ParseSectorNumbers(Stream data, ushort sectorShift)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
int sectorCount = (int)(Math.Pow(2, sectorShift) / sizeof(uint));
|
||||
SectorNumber[] sectorNumbers = new SectorNumber[sectorCount];
|
||||
|
||||
for (int i = 0; i < sectorNumbers.Length; i++)
|
||||
{
|
||||
sectorNumbers[i] = (SectorNumber)data.ReadUInt32();
|
||||
}
|
||||
|
||||
return sectorNumbers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a sector full of directory entries
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="sectorShift">Sector shift from the header</param>
|
||||
/// <param name="majorVersion">Major version from the header</param>
|
||||
/// <returns>Filled sector full of directory entries on success, null on error</returns>
|
||||
private static DirectoryEntry[] ParseDirectoryEntries(Stream data, ushort sectorShift, ushort majorVersion)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
const int directoryEntrySize = 64 + 2 + 1 + 1 + 4 + 4 + 4 + 16 + 4 + 8 + 8 + 4 + 8;
|
||||
int sectorCount = (int)(Math.Pow(2, sectorShift) / directoryEntrySize);
|
||||
DirectoryEntry[] directoryEntries = new DirectoryEntry[sectorCount];
|
||||
|
||||
for (int i = 0; i < directoryEntries.Length; i++)
|
||||
{
|
||||
var directoryEntry = ParseDirectoryEntry(data, majorVersion);
|
||||
if (directoryEntry == null)
|
||||
return null;
|
||||
|
||||
directoryEntries[i] = directoryEntry;
|
||||
}
|
||||
|
||||
return directoryEntries;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a directory entry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="majorVersion">Major version from the header</param>
|
||||
/// <returns>Filled directory entry on success, null on error</returns>
|
||||
private static DirectoryEntry ParseDirectoryEntry(Stream data, ushort majorVersion)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
DirectoryEntry directoryEntry = new DirectoryEntry();
|
||||
|
||||
byte[] name = data.ReadBytes(64);
|
||||
directoryEntry.Name = Encoding.Unicode.GetString(name).TrimEnd('\0');
|
||||
directoryEntry.NameLength = data.ReadUInt16();
|
||||
directoryEntry.ObjectType = (ObjectType)data.ReadByteValue();
|
||||
directoryEntry.ColorFlag = (ColorFlag)data.ReadByteValue();
|
||||
directoryEntry.LeftSiblingID = (StreamID)data.ReadUInt32();
|
||||
directoryEntry.RightSiblingID = (StreamID)data.ReadUInt32();
|
||||
directoryEntry.ChildID = (StreamID)data.ReadUInt32();
|
||||
directoryEntry.CLSID = data.ReadGuid();
|
||||
directoryEntry.StateBits = data.ReadUInt32();
|
||||
directoryEntry.CreationTime = data.ReadUInt64();
|
||||
directoryEntry.ModifiedTime = data.ReadUInt64();
|
||||
directoryEntry.StartingSectorLocation = data.ReadUInt32();
|
||||
directoryEntry.StreamSize = data.ReadUInt64();
|
||||
if (majorVersion == 3)
|
||||
directoryEntry.StreamSize &= 0x0000FFFF;
|
||||
|
||||
return directoryEntry;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,9 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Serialization;
|
||||
using BurnOutSharp.Utilities;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
|
||||
namespace BurnOutSharp.Builders
|
||||
namespace BinaryObjectScanner.Builders
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
@@ -1228,6 +1228,7 @@ namespace BurnOutSharp.Builders
|
||||
int currentOffset = offset;
|
||||
|
||||
offset += 6;
|
||||
|
||||
string nextKey = entry.Data.ReadString(ref offset, Encoding.Unicode);
|
||||
offset = currentOffset;
|
||||
|
||||
@@ -1278,12 +1279,18 @@ namespace BurnOutSharp.Builders
|
||||
{
|
||||
var stringFileInfo = new Models.PortableExecutable.StringFileInfo();
|
||||
|
||||
// Cache the initial offset
|
||||
int currentOffset = offset;
|
||||
|
||||
stringFileInfo.Length = data.ReadUInt16(ref offset);
|
||||
stringFileInfo.ValueLength = data.ReadUInt16(ref offset);
|
||||
stringFileInfo.ResourceType = (Models.PortableExecutable.VersionResourceType)data.ReadUInt16(ref offset);
|
||||
stringFileInfo.Key = data.ReadString(ref offset, Encoding.Unicode);
|
||||
if (stringFileInfo.Key != "StringFileInfo")
|
||||
{
|
||||
offset -= 6 + ((stringFileInfo.Key.Length + 1) * 2);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Align to the DWORD boundary if we're not at the end
|
||||
if (offset != data.Length)
|
||||
@@ -1293,7 +1300,7 @@ namespace BurnOutSharp.Builders
|
||||
}
|
||||
|
||||
var stringFileInfoChildren = new List<Models.PortableExecutable.StringTable>();
|
||||
while (offset < stringFileInfo.Length)
|
||||
while ((offset - currentOffset) < stringFileInfo.Length)
|
||||
{
|
||||
var stringTable = new Models.PortableExecutable.StringTable();
|
||||
|
||||
@@ -1310,7 +1317,7 @@ namespace BurnOutSharp.Builders
|
||||
}
|
||||
|
||||
var stringTableChildren = new List<Models.PortableExecutable.StringData>();
|
||||
while (offset < stringTable.Length)
|
||||
while ((offset - currentOffset) < stringTable.Length)
|
||||
{
|
||||
var stringData = new Models.PortableExecutable.StringData();
|
||||
|
||||
@@ -1362,6 +1369,9 @@ namespace BurnOutSharp.Builders
|
||||
{
|
||||
var varFileInfo = new Models.PortableExecutable.VarFileInfo();
|
||||
|
||||
// Cache the initial offset
|
||||
int initialOffset = offset;
|
||||
|
||||
varFileInfo.Length = data.ReadUInt16(ref offset);
|
||||
varFileInfo.ValueLength = data.ReadUInt16(ref offset);
|
||||
varFileInfo.ResourceType = (Models.PortableExecutable.VersionResourceType)data.ReadUInt16(ref offset);
|
||||
@@ -1377,7 +1387,7 @@ namespace BurnOutSharp.Builders
|
||||
}
|
||||
|
||||
var varFileInfoChildren = new List<Models.PortableExecutable.VarData>();
|
||||
while (offset < varFileInfo.Length)
|
||||
while ((offset - initialOffset) < varFileInfo.Length)
|
||||
{
|
||||
var varData = new Models.PortableExecutable.VarData();
|
||||
|
||||
@@ -1386,7 +1396,10 @@ namespace BurnOutSharp.Builders
|
||||
varData.ResourceType = (Models.PortableExecutable.VersionResourceType)data.ReadUInt16(ref offset);
|
||||
varData.Key = data.ReadString(ref offset, Encoding.Unicode);
|
||||
if (varData.Key != "Translation")
|
||||
{
|
||||
offset -= 6 + ((varData.Key.Length + 1) * 2);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Align to the DWORD boundary if we're not at the end
|
||||
if (offset != data.Length)
|
||||
@@ -1395,8 +1408,11 @@ namespace BurnOutSharp.Builders
|
||||
varData.Padding = data.ReadByte(ref offset);
|
||||
}
|
||||
|
||||
// Cache the current offset
|
||||
int currentOffset = offset;
|
||||
|
||||
var varDataValue = new List<uint>();
|
||||
while (offset < (varData.ValueLength * sizeof(ushort)))
|
||||
while ((offset - currentOffset) < varData.ValueLength)
|
||||
{
|
||||
uint languageAndCodeIdentifierPair = data.ReadUInt32(ref offset);
|
||||
varDataValue.Add(languageAndCodeIdentifierPair);
|
||||
@@ -1,10 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BurnOutSharp.Models.GCF;
|
||||
using BurnOutSharp.Utilities;
|
||||
using BinaryObjectScanner.Models.GCF;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
|
||||
namespace BurnOutSharp.Builders
|
||||
namespace BinaryObjectScanner.Builders
|
||||
{
|
||||
public static class GCF
|
||||
{
|
||||
@@ -1,11 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BurnOutSharp.Models.InstallShieldCabinet;
|
||||
using BurnOutSharp.Utilities;
|
||||
using static BurnOutSharp.Models.InstallShieldCabinet.Constants;
|
||||
using BinaryObjectScanner.Models.InstallShieldCabinet;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
using static BinaryObjectScanner.Models.InstallShieldCabinet.Constants;
|
||||
|
||||
namespace BurnOutSharp.Builders
|
||||
namespace BinaryObjectScanner.Builders
|
||||
{
|
||||
// TODO: Add multi-cabinet reading
|
||||
public class InstallShieldCabinet
|
||||
@@ -18,7 +18,7 @@ namespace BurnOutSharp.Builders
|
||||
/// <param name="data">Byte array to parse</param>
|
||||
/// <param name="offset">Offset into the byte array</param>
|
||||
/// <returns>Filled cabinet on success, null on error</returns>
|
||||
public static Header ParseCabinet(byte[] data, int offset)
|
||||
public static Cabinet ParseCabinet(byte[] data, int offset)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null)
|
||||
@@ -42,7 +42,7 @@ namespace BurnOutSharp.Builders
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled cabinet on success, null on error</returns>
|
||||
public static Header ParseCabinet(Stream data)
|
||||
public static Cabinet ParseCabinet(Stream data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
|
||||
@@ -56,7 +56,7 @@ namespace BurnOutSharp.Builders
|
||||
int initialOffset = (int)data.Position;
|
||||
|
||||
// Create a new cabinet to fill
|
||||
var header = new Header();
|
||||
var cabinet = new Cabinet();
|
||||
|
||||
#region Common Header
|
||||
|
||||
@@ -66,34 +66,46 @@ namespace BurnOutSharp.Builders
|
||||
return null;
|
||||
|
||||
// Set the cabinet header
|
||||
header.CommonHeader = commonHeader;
|
||||
cabinet.CommonHeader = commonHeader;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cabinet Descriptor
|
||||
#region Volume Header
|
||||
|
||||
// Get the cabinet descriptor offset
|
||||
uint cabinetDescriptorOffset = commonHeader.CabDescriptorOffset;
|
||||
if (cabinetDescriptorOffset < 0 || cabinetDescriptorOffset >= data.Length)
|
||||
// Try to parse the volume header
|
||||
var volumeHeader = ParseVolumeHeader(data, GetMajorVersion(commonHeader));
|
||||
if (volumeHeader == null)
|
||||
return null;
|
||||
|
||||
// Seek to the cabinet descriptor
|
||||
data.Seek(cabinetDescriptorOffset, SeekOrigin.Begin);
|
||||
// Set the volume header
|
||||
cabinet.VolumeHeader = volumeHeader;
|
||||
|
||||
// Try to parse the cabinet descriptor
|
||||
var cabinetDescriptor = ParseCabinetDescriptor(data);
|
||||
if (cabinetDescriptor == null)
|
||||
#endregion
|
||||
|
||||
#region Descriptor
|
||||
|
||||
// Get the descriptor offset
|
||||
uint descriptorOffset = commonHeader.DescriptorOffset;
|
||||
if (descriptorOffset < 0 || descriptorOffset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Set the cabinet descriptor
|
||||
header.CabinetDescriptor = cabinetDescriptor;
|
||||
// Seek to the descriptor
|
||||
data.Seek(descriptorOffset, SeekOrigin.Begin);
|
||||
|
||||
// Try to parse the descriptor
|
||||
var descriptor = ParseDescriptor(data);
|
||||
if (descriptor == null)
|
||||
return null;
|
||||
|
||||
// Set the descriptor
|
||||
cabinet.Descriptor = descriptor;
|
||||
|
||||
#endregion
|
||||
|
||||
#region File Descriptor Offsets
|
||||
|
||||
// Get the file table offset
|
||||
uint fileTableOffset = commonHeader.CabDescriptorOffset + cabinetDescriptor.FileTableOffset;
|
||||
uint fileTableOffset = commonHeader.DescriptorOffset + descriptor.FileTableOffset;
|
||||
if (fileTableOffset < 0 || fileTableOffset >= data.Length)
|
||||
return null;
|
||||
|
||||
@@ -102,16 +114,16 @@ namespace BurnOutSharp.Builders
|
||||
|
||||
// Get the number of file table items
|
||||
uint fileTableItems;
|
||||
if (header.MajorVersion <= 5)
|
||||
fileTableItems = cabinetDescriptor.DirectoryCount + cabinetDescriptor.FileCount;
|
||||
if (GetMajorVersion(commonHeader) <= 5)
|
||||
fileTableItems = descriptor.DirectoryCount + descriptor.FileCount;
|
||||
else
|
||||
fileTableItems = cabinetDescriptor.DirectoryCount;
|
||||
fileTableItems = descriptor.DirectoryCount;
|
||||
|
||||
// Create and fill the file table
|
||||
header.FileDescriptorOffsets = new uint[fileTableItems];
|
||||
for (int i = 0; i < header.FileDescriptorOffsets.Length; i++)
|
||||
cabinet.FileDescriptorOffsets = new uint[fileTableItems];
|
||||
for (int i = 0; i < cabinet.FileDescriptorOffsets.Length; i++)
|
||||
{
|
||||
header.FileDescriptorOffsets[i] = data.ReadUInt32();
|
||||
cabinet.FileDescriptorOffsets[i] = data.ReadUInt32();
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -119,13 +131,13 @@ namespace BurnOutSharp.Builders
|
||||
#region Directory Descriptors
|
||||
|
||||
// Create and fill the directory descriptors
|
||||
header.DirectoryDescriptors = new FileDescriptor[cabinetDescriptor.DirectoryCount];
|
||||
for (int i = 0; i < cabinetDescriptor.DirectoryCount; i++)
|
||||
cabinet.DirectoryNames = new string[descriptor.DirectoryCount];
|
||||
for (int i = 0; i < descriptor.DirectoryCount; i++)
|
||||
{
|
||||
// Get the directory descriptor offset
|
||||
uint offset = cabinetDescriptorOffset
|
||||
+ cabinetDescriptor.FileTableOffset
|
||||
+ header.FileDescriptorOffsets[i];
|
||||
uint offset = descriptorOffset
|
||||
+ descriptor.FileTableOffset
|
||||
+ cabinet.FileDescriptorOffsets[i];
|
||||
|
||||
// If we have an invalid offset
|
||||
if (offset < 0 || offset >= data.Length)
|
||||
@@ -135,8 +147,8 @@ namespace BurnOutSharp.Builders
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
// Create and add the file descriptor
|
||||
FileDescriptor directoryDescriptor = ParseDirectoryDescriptor(data, header.MajorVersion);
|
||||
header.DirectoryDescriptors[i] = directoryDescriptor;
|
||||
string directoryName = ParseDirectoryName(data, GetMajorVersion(commonHeader));
|
||||
cabinet.DirectoryNames[i] = directoryName;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -144,22 +156,22 @@ namespace BurnOutSharp.Builders
|
||||
#region File Descriptors
|
||||
|
||||
// Create and fill the file descriptors
|
||||
header.FileDescriptors = new FileDescriptor[cabinetDescriptor.FileCount];
|
||||
for (int i = 0; i < cabinetDescriptor.FileCount; i++)
|
||||
cabinet.FileDescriptors = new FileDescriptor[descriptor.FileCount];
|
||||
for (int i = 0; i < descriptor.FileCount; i++)
|
||||
{
|
||||
// Get the file descriptor offset
|
||||
uint offset;
|
||||
if (header.MajorVersion <= 5)
|
||||
if (GetMajorVersion(commonHeader) <= 5)
|
||||
{
|
||||
offset = cabinetDescriptorOffset
|
||||
+ cabinetDescriptor.FileTableOffset
|
||||
+ header.FileDescriptorOffsets[cabinetDescriptor.DirectoryCount + i];
|
||||
offset = descriptorOffset
|
||||
+ descriptor.FileTableOffset
|
||||
+ cabinet.FileDescriptorOffsets[descriptor.DirectoryCount + i];
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = cabinetDescriptorOffset
|
||||
+ cabinetDescriptor.FileTableOffset
|
||||
+ cabinetDescriptor.FileTableOffset2
|
||||
offset = descriptorOffset
|
||||
+ descriptor.FileTableOffset
|
||||
+ descriptor.FileTableOffset2
|
||||
+ (uint)(i * 0x57);
|
||||
}
|
||||
|
||||
@@ -171,8 +183,8 @@ namespace BurnOutSharp.Builders
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
// Create and add the file descriptor
|
||||
FileDescriptor fileDescriptor = ParseFileDescriptor(data, header.MajorVersion, cabinetDescriptorOffset + cabinetDescriptor.FileTableOffset);
|
||||
header.FileDescriptors[i] = fileDescriptor;
|
||||
FileDescriptor fileDescriptor = ParseFileDescriptor(data, GetMajorVersion(commonHeader), descriptorOffset + descriptor.FileTableOffset);
|
||||
cabinet.FileDescriptors[i] = fileDescriptor;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -180,16 +192,16 @@ namespace BurnOutSharp.Builders
|
||||
#region File Group Offsets
|
||||
|
||||
// Create and fill the file group offsets
|
||||
header.FileGroupOffsets = new Dictionary<long, OffsetList>();
|
||||
for (int i = 0; i < cabinetDescriptor.FileGroupOffsets.Length; i++)
|
||||
cabinet.FileGroupOffsets = new Dictionary<long, OffsetList>();
|
||||
for (int i = 0; i < descriptor.FileGroupOffsets.Length; i++)
|
||||
{
|
||||
// Get the file group offset
|
||||
uint offset = cabinetDescriptor.FileGroupOffsets[i];
|
||||
uint offset = descriptor.FileGroupOffsets[i];
|
||||
if (offset == 0)
|
||||
continue;
|
||||
|
||||
// Adjust the file group offset
|
||||
offset += commonHeader.CabDescriptorOffset;
|
||||
offset += commonHeader.DescriptorOffset;
|
||||
if (offset < 0 || offset >= data.Length)
|
||||
continue;
|
||||
|
||||
@@ -197,22 +209,22 @@ namespace BurnOutSharp.Builders
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
// Create and add the offset
|
||||
OffsetList offsetList = ParseOffsetList(data, header.MajorVersion, cabinetDescriptorOffset);
|
||||
header.FileGroupOffsets[cabinetDescriptor.FileGroupOffsets[i]] = offsetList;
|
||||
OffsetList offsetList = ParseOffsetList(data, GetMajorVersion(commonHeader), descriptorOffset);
|
||||
cabinet.FileGroupOffsets[descriptor.FileGroupOffsets[i]] = offsetList;
|
||||
|
||||
// If we have a nonzero next offset
|
||||
uint nextOffset = offsetList.NextOffset;
|
||||
while (nextOffset != 0)
|
||||
{
|
||||
// Get the next offset to read
|
||||
uint internalOffset = nextOffset + commonHeader.CabDescriptorOffset;
|
||||
uint internalOffset = nextOffset + commonHeader.DescriptorOffset;
|
||||
|
||||
// Seek to the file group offset
|
||||
data.Seek(internalOffset, SeekOrigin.Begin);
|
||||
|
||||
// Create and add the offset
|
||||
offsetList = ParseOffsetList(data, header.MajorVersion, cabinetDescriptorOffset);
|
||||
header.FileGroupOffsets[nextOffset] = offsetList;
|
||||
offsetList = ParseOffsetList(data, GetMajorVersion(commonHeader), descriptorOffset);
|
||||
cabinet.FileGroupOffsets[nextOffset] = offsetList;
|
||||
|
||||
// Set the next offset
|
||||
nextOffset = offsetList.NextOffset;
|
||||
@@ -223,43 +235,55 @@ namespace BurnOutSharp.Builders
|
||||
|
||||
#region File Groups
|
||||
|
||||
// Create the file groups array
|
||||
cabinet.FileGroups = new FileGroup[cabinet.FileGroupOffsets.Count];
|
||||
|
||||
// Create and fill the file groups
|
||||
List<FileGroup> fileGroups = new List<FileGroup>();
|
||||
foreach (var kvp in header.FileGroupOffsets)
|
||||
int fileGroupId = 0;
|
||||
foreach (var kvp in cabinet.FileGroupOffsets)
|
||||
{
|
||||
// Get the offset
|
||||
OffsetList list = kvp.Value;
|
||||
if (list == null)
|
||||
{
|
||||
fileGroupId++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we have an invalid offset
|
||||
if (list.DescriptorOffset <= 0)
|
||||
{
|
||||
fileGroupId++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/// Seek to the file group
|
||||
data.Seek(list.DescriptorOffset + cabinetDescriptorOffset, SeekOrigin.Begin);
|
||||
data.Seek(list.DescriptorOffset + descriptorOffset, SeekOrigin.Begin);
|
||||
|
||||
// Try to parse the file group
|
||||
FileGroup fileGroup = ParseFileGroup(data, header.MajorVersion, cabinetDescriptorOffset);
|
||||
var fileGroup = ParseFileGroup(data, GetMajorVersion(commonHeader), descriptorOffset);
|
||||
if (fileGroup == null)
|
||||
return null;
|
||||
|
||||
// Add the file group
|
||||
fileGroups.Add(fileGroup);
|
||||
cabinet.FileGroups[fileGroupId++] = fileGroup;
|
||||
}
|
||||
|
||||
// Set the file groups
|
||||
header.FileGroups = fileGroups.ToArray();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Component Offsets
|
||||
|
||||
// Create and fill the component offsets
|
||||
header.ComponentOffsets = new Dictionary<long, OffsetList>();
|
||||
for (int i = 0; i < cabinetDescriptor.ComponentOffsets.Length; i++)
|
||||
cabinet.ComponentOffsets = new Dictionary<long, OffsetList>();
|
||||
for (int i = 0; i < descriptor.ComponentOffsets.Length; i++)
|
||||
{
|
||||
// Get the component offset
|
||||
uint offset = cabinetDescriptor.ComponentOffsets[i];
|
||||
uint offset = descriptor.ComponentOffsets[i];
|
||||
if (offset == 0)
|
||||
continue;
|
||||
|
||||
// Adjust the component offset
|
||||
offset += commonHeader.CabDescriptorOffset;
|
||||
offset += commonHeader.DescriptorOffset;
|
||||
if (offset < 0 || offset >= data.Length)
|
||||
continue;
|
||||
|
||||
@@ -267,22 +291,22 @@ namespace BurnOutSharp.Builders
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
// Create and add the offset
|
||||
OffsetList offsetList = ParseOffsetList(data, header.MajorVersion, cabinetDescriptorOffset);
|
||||
header.ComponentOffsets[cabinetDescriptor.ComponentOffsets[i]] = offsetList;
|
||||
OffsetList offsetList = ParseOffsetList(data, GetMajorVersion(commonHeader), descriptorOffset);
|
||||
cabinet.ComponentOffsets[descriptor.ComponentOffsets[i]] = offsetList;
|
||||
|
||||
// If we have a nonzero next offset
|
||||
uint nextOffset = offsetList.NextOffset;
|
||||
while (nextOffset != 0)
|
||||
{
|
||||
// Get the next offset to read
|
||||
uint internalOffset = nextOffset + commonHeader.CabDescriptorOffset;
|
||||
uint internalOffset = nextOffset + commonHeader.DescriptorOffset;
|
||||
|
||||
// Seek to the file group offset
|
||||
data.Seek(internalOffset, SeekOrigin.Begin);
|
||||
|
||||
// Create and add the offset
|
||||
offsetList = ParseOffsetList(data, header.MajorVersion, cabinetDescriptorOffset);
|
||||
header.ComponentOffsets[nextOffset] = offsetList;
|
||||
offsetList = ParseOffsetList(data, GetMajorVersion(commonHeader), descriptorOffset);
|
||||
cabinet.ComponentOffsets[nextOffset] = offsetList;
|
||||
|
||||
// Set the next offset
|
||||
nextOffset = offsetList.NextOffset;
|
||||
@@ -293,31 +317,45 @@ namespace BurnOutSharp.Builders
|
||||
|
||||
#region Components
|
||||
|
||||
// Create the components array
|
||||
cabinet.Components = new Component[cabinet.ComponentOffsets.Count];
|
||||
|
||||
// Create and fill the components
|
||||
List<Component> components = new List<Component>();
|
||||
foreach (KeyValuePair<long, OffsetList> kvp in header.ComponentOffsets)
|
||||
int componentId = 0;
|
||||
foreach (KeyValuePair<long, OffsetList> kvp in cabinet.ComponentOffsets)
|
||||
{
|
||||
// Get the offset
|
||||
OffsetList list = kvp.Value;
|
||||
if (list == null)
|
||||
{
|
||||
componentId++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we have an invalid offset
|
||||
if (list.DescriptorOffset <= 0)
|
||||
{
|
||||
componentId++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Seek to the component
|
||||
data.Seek(list.DescriptorOffset + cabinetDescriptorOffset, SeekOrigin.Begin);
|
||||
data.Seek(list.DescriptorOffset + descriptorOffset, SeekOrigin.Begin);
|
||||
|
||||
// Try to parse the component
|
||||
Component component = ParseComponent(data, header.MajorVersion, cabinetDescriptorOffset);
|
||||
var component = ParseComponent(data, GetMajorVersion(commonHeader), descriptorOffset);
|
||||
if (component == null)
|
||||
return null;
|
||||
|
||||
// Add the component
|
||||
components.Add(component);
|
||||
cabinet.Components[componentId++] = component;
|
||||
}
|
||||
|
||||
// Set the components
|
||||
header.Components = components.ToArray();
|
||||
|
||||
#endregion
|
||||
|
||||
return header;
|
||||
// TODO: Parse setup types
|
||||
|
||||
return cabinet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -336,45 +374,105 @@ namespace BurnOutSharp.Builders
|
||||
|
||||
commonHeader.Version = data.ReadUInt32();
|
||||
commonHeader.VolumeInfo = data.ReadUInt32();
|
||||
commonHeader.CabDescriptorOffset = data.ReadUInt32();
|
||||
commonHeader.CabDescriptorSize = data.ReadUInt32();
|
||||
commonHeader.DescriptorOffset = data.ReadUInt32();
|
||||
commonHeader.DescriptorSize = data.ReadUInt32();
|
||||
|
||||
return commonHeader;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a cabinet descriptor
|
||||
/// Parse a Stream into a volume header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled cabinet descriptor on success, null on error</returns>
|
||||
private static CabDescriptor ParseCabinetDescriptor(Stream data)
|
||||
/// <param name="majorVersion">Major version of the cabinet</param>
|
||||
/// <returns>Filled volume header on success, null on error</returns>
|
||||
private static VolumeHeader ParseVolumeHeader(Stream data, int majorVersion)
|
||||
{
|
||||
CabDescriptor cabDescriptor = new CabDescriptor();
|
||||
VolumeHeader volumeHeader = new VolumeHeader();
|
||||
|
||||
cabDescriptor.Reserved0 = data.ReadBytes(0x0C);
|
||||
cabDescriptor.FileTableOffset = data.ReadUInt32();
|
||||
cabDescriptor.Reserved1 = data.ReadBytes(0x04);
|
||||
cabDescriptor.FileTableSize = data.ReadUInt32();
|
||||
cabDescriptor.FileTableSize2 = data.ReadUInt32();
|
||||
cabDescriptor.DirectoryCount = data.ReadUInt32();
|
||||
cabDescriptor.Reserved2 = data.ReadBytes(0x08);
|
||||
cabDescriptor.FileCount = data.ReadUInt32();
|
||||
cabDescriptor.FileTableOffset2 = data.ReadUInt32();
|
||||
cabDescriptor.Reserved3 = data.ReadBytes(0x0E);
|
||||
|
||||
cabDescriptor.FileGroupOffsets = new uint[MAX_FILE_GROUP_COUNT];
|
||||
for (int i = 0; i < cabDescriptor.FileGroupOffsets.Length; i++)
|
||||
// Read the descriptor based on version
|
||||
if (majorVersion <= 5)
|
||||
{
|
||||
cabDescriptor.FileGroupOffsets[i] = data.ReadUInt32();
|
||||
volumeHeader.DataOffset = data.ReadUInt32();
|
||||
_ = data.ReadBytes(0x04); // Skip 0x04 bytes, unknown data?
|
||||
volumeHeader.FirstFileIndex = data.ReadUInt32();
|
||||
volumeHeader.LastFileIndex = data.ReadUInt32();
|
||||
volumeHeader.FirstFileOffset = data.ReadUInt32();
|
||||
volumeHeader.FirstFileSizeExpanded = data.ReadUInt32();
|
||||
volumeHeader.FirstFileSizeCompressed = data.ReadUInt32();
|
||||
volumeHeader.LastFileOffset = data.ReadUInt32();
|
||||
volumeHeader.LastFileSizeExpanded = data.ReadUInt32();
|
||||
volumeHeader.LastFileSizeCompressed = data.ReadUInt32();
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Should standard and high values be combined?
|
||||
volumeHeader.DataOffset = data.ReadUInt32();
|
||||
volumeHeader.DataOffsetHigh = data.ReadUInt32();
|
||||
volumeHeader.FirstFileIndex = data.ReadUInt32();
|
||||
volumeHeader.LastFileIndex = data.ReadUInt32();
|
||||
volumeHeader.FirstFileOffset = data.ReadUInt32();
|
||||
volumeHeader.FirstFileOffsetHigh = data.ReadUInt32();
|
||||
volumeHeader.FirstFileSizeExpanded = data.ReadUInt32();
|
||||
volumeHeader.FirstFileSizeExpandedHigh = data.ReadUInt32();
|
||||
volumeHeader.FirstFileSizeCompressed = data.ReadUInt32();
|
||||
volumeHeader.FirstFileSizeCompressedHigh = data.ReadUInt32();
|
||||
volumeHeader.LastFileOffset = data.ReadUInt32();
|
||||
volumeHeader.LastFileOffsetHigh = data.ReadUInt32();
|
||||
volumeHeader.LastFileSizeExpanded = data.ReadUInt32();
|
||||
volumeHeader.LastFileSizeExpandedHigh = data.ReadUInt32();
|
||||
volumeHeader.LastFileSizeCompressed = data.ReadUInt32();
|
||||
volumeHeader.LastFileSizeCompressedHigh = data.ReadUInt32();
|
||||
}
|
||||
|
||||
cabDescriptor.ComponentOffsets = new uint[MAX_COMPONENT_COUNT];
|
||||
for (int i = 0; i < cabDescriptor.ComponentOffsets.Length; i++)
|
||||
return volumeHeader;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a descriptor
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled descriptor on success, null on error</returns>
|
||||
private static Descriptor ParseDescriptor(Stream data)
|
||||
{
|
||||
Descriptor descriptor = new Descriptor();
|
||||
|
||||
descriptor.StringsOffset = data.ReadUInt32();
|
||||
descriptor.Reserved0 = data.ReadBytes(4);
|
||||
descriptor.ComponentListOffset = data.ReadUInt32();
|
||||
descriptor.FileTableOffset = data.ReadUInt32();
|
||||
descriptor.Reserved1 = data.ReadBytes(4);
|
||||
descriptor.FileTableSize = data.ReadUInt32();
|
||||
descriptor.FileTableSize2 = data.ReadUInt32();
|
||||
descriptor.DirectoryCount = data.ReadUInt16();
|
||||
descriptor.Reserved2 = data.ReadBytes(4);
|
||||
descriptor.Reserved3 = data.ReadBytes(2);
|
||||
descriptor.Reserved4 = data.ReadBytes(4);
|
||||
descriptor.FileCount = data.ReadUInt32();
|
||||
descriptor.FileTableOffset2 = data.ReadUInt32();
|
||||
descriptor.ComponentTableInfoCount = data.ReadUInt16();
|
||||
descriptor.ComponentTableOffset = data.ReadUInt32();
|
||||
descriptor.Reserved5 = data.ReadBytes(4);
|
||||
descriptor.Reserved6 = data.ReadBytes(4);
|
||||
|
||||
descriptor.FileGroupOffsets = new uint[MAX_FILE_GROUP_COUNT];
|
||||
for (int i = 0; i < descriptor.FileGroupOffsets.Length; i++)
|
||||
{
|
||||
cabDescriptor.ComponentOffsets[i] = data.ReadUInt32();
|
||||
descriptor.FileGroupOffsets[i] = data.ReadUInt32();
|
||||
}
|
||||
|
||||
return cabDescriptor;
|
||||
descriptor.ComponentOffsets = new uint[MAX_COMPONENT_COUNT];
|
||||
for (int i = 0; i < descriptor.ComponentOffsets.Length; i++)
|
||||
{
|
||||
descriptor.ComponentOffsets[i] = data.ReadUInt32();
|
||||
}
|
||||
|
||||
descriptor.SetupTypesOffset = data.ReadUInt32();
|
||||
descriptor.SetupTableOffset = data.ReadUInt32();
|
||||
descriptor.Reserved7 = data.ReadBytes(4);
|
||||
descriptor.Reserved8 = data.ReadBytes(4);
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -423,14 +521,33 @@ namespace BurnOutSharp.Builders
|
||||
|
||||
fileGroup.NameOffset = data.ReadUInt32();
|
||||
|
||||
// Skip bytes based on the version
|
||||
if (majorVersion <= 5)
|
||||
_ = data.ReadBytes(0x48);
|
||||
else
|
||||
_ = data.ReadBytes(0x12);
|
||||
fileGroup.ExpandedSize = data.ReadUInt32();
|
||||
fileGroup.Reserved0 = data.ReadBytes(4);
|
||||
fileGroup.CompressedSize = data.ReadUInt32();
|
||||
fileGroup.Reserved1 = data.ReadBytes(4);
|
||||
fileGroup.Reserved2 = data.ReadBytes(2);
|
||||
fileGroup.Attribute1 = data.ReadUInt16();
|
||||
fileGroup.Attribute2 = data.ReadUInt16();
|
||||
|
||||
fileGroup.FirstFile = data.ReadUInt16();
|
||||
// TODO: Figure out what data lives in this area for V5 and below
|
||||
if (majorVersion <= 5)
|
||||
data.Seek(0x36, SeekOrigin.Current);
|
||||
|
||||
fileGroup.FirstFile = data.ReadUInt32();
|
||||
fileGroup.LastFile = data.ReadUInt32();
|
||||
fileGroup.UnknownOffset = data.ReadUInt32();
|
||||
fileGroup.Var4Offset = data.ReadUInt32();
|
||||
fileGroup.Var1Offset = data.ReadUInt32();
|
||||
fileGroup.HTTPLocationOffset = data.ReadUInt32();
|
||||
fileGroup.FTPLocationOffset = data.ReadUInt32();
|
||||
fileGroup.MiscOffset = data.ReadUInt32();
|
||||
fileGroup.Var2Offset = data.ReadUInt32();
|
||||
fileGroup.TargetDirectoryOffset = data.ReadUInt32();
|
||||
fileGroup.Reserved3 = data.ReadBytes(2);
|
||||
fileGroup.Reserved4 = data.ReadBytes(2);
|
||||
fileGroup.Reserved5 = data.ReadBytes(2);
|
||||
fileGroup.Reserved6 = data.ReadBytes(2);
|
||||
fileGroup.Reserved7 = data.ReadBytes(2);
|
||||
|
||||
// Cache the current position
|
||||
long currentPosition = data.Position;
|
||||
@@ -465,20 +582,64 @@ namespace BurnOutSharp.Builders
|
||||
{
|
||||
Component component = new Component();
|
||||
|
||||
component.IdentifierOffset = data.ReadUInt32();
|
||||
component.DescriptorOffset = data.ReadUInt32();
|
||||
component.DisplayNameOffset = data.ReadUInt32();
|
||||
component.Reserved0 = data.ReadBytes(2);
|
||||
component.ReservedOffset0 = data.ReadUInt32();
|
||||
component.ReservedOffset1 = data.ReadUInt32();
|
||||
component.ComponentIndex = data.ReadUInt16();
|
||||
component.NameOffset = data.ReadUInt32();
|
||||
|
||||
// Skip bytes based on the version
|
||||
if (majorVersion <= 5)
|
||||
_ = data.ReadBytes(0x6C);
|
||||
else
|
||||
_ = data.ReadBytes(0x6B);
|
||||
|
||||
component.ReservedOffset2 = data.ReadUInt32();
|
||||
component.ReservedOffset3 = data.ReadUInt32();
|
||||
component.ReservedOffset4 = data.ReadUInt32();
|
||||
component.Reserved1 = data.ReadBytes(32);
|
||||
component.CLSIDOffset = data.ReadUInt32();
|
||||
component.Reserved2 = data.ReadBytes(28);
|
||||
component.Reserved3 = data.ReadBytes(majorVersion <= 5 ? 2 : 1);
|
||||
component.DependsCount = data.ReadUInt16();
|
||||
component.DependsOffset = data.ReadUInt32();
|
||||
component.FileGroupCount = data.ReadUInt16();
|
||||
component.FileGroupTableOffset = data.ReadUInt32();
|
||||
component.FileGroupNamesOffset = data.ReadUInt32();
|
||||
component.X3Count = data.ReadUInt16();
|
||||
component.X3Offset = data.ReadUInt32();
|
||||
component.SubComponentsCount = data.ReadUInt16();
|
||||
component.SubComponentsOffset = data.ReadUInt32();
|
||||
component.NextComponentOffset = data.ReadUInt32();
|
||||
component.ReservedOffset5 = data.ReadUInt32();
|
||||
component.ReservedOffset6 = data.ReadUInt32();
|
||||
component.ReservedOffset7 = data.ReadUInt32();
|
||||
component.ReservedOffset8 = data.ReadUInt32();
|
||||
|
||||
// Cache the current position
|
||||
long currentPosition = data.Position;
|
||||
|
||||
// Read the identifier, if possible
|
||||
if (component.IdentifierOffset != 0)
|
||||
{
|
||||
// Seek to the identifier
|
||||
data.Seek(component.IdentifierOffset + descriptorOffset, SeekOrigin.Begin);
|
||||
|
||||
// Read the string
|
||||
if (majorVersion >= 17)
|
||||
component.Identifier = data.ReadString(Encoding.Unicode);
|
||||
else
|
||||
component.Identifier = data.ReadString(Encoding.ASCII);
|
||||
}
|
||||
|
||||
// Read the display name, if possible
|
||||
if (component.DisplayNameOffset != 0)
|
||||
{
|
||||
// Seek to the name
|
||||
data.Seek(component.DisplayNameOffset + descriptorOffset, SeekOrigin.Begin);
|
||||
|
||||
// Read the string
|
||||
if (majorVersion >= 17)
|
||||
component.DisplayName = data.ReadString(Encoding.Unicode);
|
||||
else
|
||||
component.DisplayName = data.ReadString(Encoding.ASCII);
|
||||
}
|
||||
|
||||
// Read the name, if possible
|
||||
if (component.NameOffset != 0)
|
||||
{
|
||||
@@ -492,20 +653,42 @@ namespace BurnOutSharp.Builders
|
||||
component.Name = data.ReadString(Encoding.ASCII);
|
||||
}
|
||||
|
||||
// Read the file group table, if possible
|
||||
if (component.FileGroupCount != 0 && component.FileGroupTableOffset != 0)
|
||||
// Read the CLSID, if possible
|
||||
if (component.CLSIDOffset != 0)
|
||||
{
|
||||
// Seek to the CLSID
|
||||
data.Seek(component.CLSIDOffset + descriptorOffset, SeekOrigin.Begin);
|
||||
|
||||
// Read the GUID
|
||||
component.CLSID = data.ReadGuid();
|
||||
}
|
||||
|
||||
// Read the file group names, if possible
|
||||
if (component.FileGroupCount != 0 && component.FileGroupNamesOffset != 0)
|
||||
{
|
||||
// Seek to the file group table offset
|
||||
data.Seek(component.FileGroupTableOffset + descriptorOffset, SeekOrigin.Begin);
|
||||
data.Seek(component.FileGroupNamesOffset + descriptorOffset, SeekOrigin.Begin);
|
||||
|
||||
// Read the file group table
|
||||
// Read the file group names table
|
||||
component.FileGroupNames = new string[component.FileGroupCount];
|
||||
for (int j = 0; j < component.FileGroupCount; j++)
|
||||
{
|
||||
// Get the name offset
|
||||
uint nameOffset = data.ReadUInt32();
|
||||
|
||||
// Cache the current offset
|
||||
long preNameOffset = data.Position;
|
||||
|
||||
// Seek to the name offset
|
||||
data.Seek(nameOffset + descriptorOffset, SeekOrigin.Begin);
|
||||
|
||||
if (majorVersion >= 17)
|
||||
component.FileGroupNames[j] = data.ReadString(Encoding.Unicode);
|
||||
else
|
||||
component.FileGroupNames[j] = data.ReadString(Encoding.ASCII);
|
||||
|
||||
// Seek back to the original position
|
||||
data.Seek(preNameOffset, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,22 +699,18 @@ namespace BurnOutSharp.Builders
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a directory descriptor
|
||||
/// Parse a Stream into a directory name
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="majorVersion">Major version of the cabinet</param>
|
||||
/// <returns>Filled directory descriptor on success, null on error</returns>
|
||||
private static FileDescriptor ParseDirectoryDescriptor(Stream data, int majorVersion)
|
||||
/// <returns>Filled directory name on success, null on error</returns>
|
||||
private static string ParseDirectoryName(Stream data, int majorVersion)
|
||||
{
|
||||
FileDescriptor fileDescriptor = new FileDescriptor();
|
||||
|
||||
// Read the string
|
||||
if (majorVersion >= 17)
|
||||
fileDescriptor.Name = data.ReadString(Encoding.Unicode);
|
||||
return data.ReadString(Encoding.Unicode);
|
||||
else
|
||||
fileDescriptor.Name = data.ReadString(Encoding.ASCII);
|
||||
|
||||
return fileDescriptor;
|
||||
return data.ReadString(Encoding.ASCII);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -599,51 +778,29 @@ namespace BurnOutSharp.Builders
|
||||
return fileDescriptor;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a volume header
|
||||
/// Get the major version of the cabinet
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="majorVersion">Major version of the cabinet</param>
|
||||
/// <returns>Filled volume header on success, null on error</returns>
|
||||
private static VolumeHeader ParseVolumeHeader(Stream data, int majorVersion)
|
||||
/// <remarks>This should live in the wrapper but is needed during parsing</remarks>
|
||||
private static int GetMajorVersion(CommonHeader commonHeader)
|
||||
{
|
||||
VolumeHeader volumeHeader = new VolumeHeader();
|
||||
|
||||
// Read the descriptor based on version
|
||||
if (majorVersion <= 5)
|
||||
uint majorVersion = commonHeader.Version;
|
||||
if (majorVersion >> 24 == 1)
|
||||
{
|
||||
volumeHeader.DataOffset = data.ReadUInt32();
|
||||
_ = data.ReadBytes(0x04); // Skip 0x04 bytes, unknown data?
|
||||
volumeHeader.FirstFileIndex = data.ReadUInt32();
|
||||
volumeHeader.LastFileIndex = data.ReadUInt32();
|
||||
volumeHeader.FirstFileOffset = data.ReadUInt32();
|
||||
volumeHeader.FirstFileSizeExpanded = data.ReadUInt32();
|
||||
volumeHeader.FirstFileSizeCompressed = data.ReadUInt32();
|
||||
volumeHeader.LastFileOffset = data.ReadUInt32();
|
||||
volumeHeader.LastFileSizeExpanded = data.ReadUInt32();
|
||||
volumeHeader.LastFileSizeCompressed = data.ReadUInt32();
|
||||
majorVersion = (majorVersion >> 12) & 0x0F;
|
||||
}
|
||||
else
|
||||
else if (majorVersion >> 24 == 2 || majorVersion >> 24 == 4)
|
||||
{
|
||||
volumeHeader.DataOffset = data.ReadUInt32();
|
||||
volumeHeader.DataOffsetHigh = data.ReadUInt32();
|
||||
volumeHeader.FirstFileIndex = data.ReadUInt32();
|
||||
volumeHeader.LastFileIndex = data.ReadUInt32();
|
||||
volumeHeader.FirstFileOffset = data.ReadUInt32();
|
||||
volumeHeader.FirstFileOffsetHigh = data.ReadUInt32();
|
||||
volumeHeader.FirstFileSizeExpanded = data.ReadUInt32();
|
||||
volumeHeader.FirstFileSizeExpandedHigh = data.ReadUInt32();
|
||||
volumeHeader.FirstFileSizeCompressed = data.ReadUInt32();
|
||||
volumeHeader.FirstFileSizeCompressedHigh = data.ReadUInt32();
|
||||
volumeHeader.LastFileOffset = data.ReadUInt32();
|
||||
volumeHeader.LastFileOffsetHigh = data.ReadUInt32();
|
||||
volumeHeader.LastFileSizeExpanded = data.ReadUInt32();
|
||||
volumeHeader.LastFileSizeExpandedHigh = data.ReadUInt32();
|
||||
volumeHeader.LastFileSizeCompressed = data.ReadUInt32();
|
||||
volumeHeader.LastFileSizeCompressedHigh = data.ReadUInt32();
|
||||
majorVersion = majorVersion & 0xFFFF;
|
||||
if (majorVersion != 0)
|
||||
majorVersion /= 100;
|
||||
}
|
||||
|
||||
return volumeHeader;
|
||||
return (int)majorVersion;
|
||||
}
|
||||
|
||||
#endregion
|
||||
943
BinaryObjectScanner.Builders/LinearExecutable.cs
Normal file
943
BinaryObjectScanner.Builders/LinearExecutable.cs
Normal file
@@ -0,0 +1,943 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BinaryObjectScanner.Models.LinearExecutable;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
using static BinaryObjectScanner.Models.LinearExecutable.Constants;
|
||||
|
||||
namespace BinaryObjectScanner.Builders
|
||||
{
|
||||
public static class LinearExecutable
|
||||
{
|
||||
#region Byte Data
|
||||
|
||||
/// <summary>
|
||||
/// Parse a byte array into a Linear Executable
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array to parse</param>
|
||||
/// <param name="offset">Offset into the byte array</param>
|
||||
/// <returns>Filled executable on success, null on error</returns>
|
||||
public static Executable ParseExecutable(byte[] data, int offset)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
// If the offset is out of bounds
|
||||
if (offset < 0 || offset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Create a memory stream and parse that
|
||||
MemoryStream dataStream = new MemoryStream(data, offset, data.Length - offset);
|
||||
return ParseExecutable(dataStream);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Stream Data
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Linear Executable
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled executable on success, null on error</returns>
|
||||
public static Executable ParseExecutable(Stream data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
|
||||
return null;
|
||||
|
||||
// If the offset is out of bounds
|
||||
if (data.Position < 0 || data.Position >= data.Length)
|
||||
return null;
|
||||
|
||||
// Cache the current offset
|
||||
int initialOffset = (int)data.Position;
|
||||
|
||||
// Create a new executable to fill
|
||||
var executable = new Executable();
|
||||
|
||||
#region MS-DOS Stub
|
||||
|
||||
// Parse the MS-DOS stub
|
||||
var stub = MSDOS.ParseExecutable(data);
|
||||
if (stub?.Header == null || stub.Header.NewExeHeaderAddr == 0)
|
||||
return null;
|
||||
|
||||
// Set the MS-DOS stub
|
||||
executable.Stub = stub;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Information Block
|
||||
|
||||
// Try to parse the executable header
|
||||
data.Seek(initialOffset + stub.Header.NewExeHeaderAddr, SeekOrigin.Begin);
|
||||
var informationBlock = ParseInformationBlock(data);
|
||||
if (informationBlock == null)
|
||||
return null;
|
||||
|
||||
// Set the executable header
|
||||
executable.InformationBlock = informationBlock;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Object Table
|
||||
|
||||
// Get the object table offset
|
||||
long offset = informationBlock.ObjectTableOffset + stub.Header.NewExeHeaderAddr;
|
||||
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
|
||||
{
|
||||
// Seek to the object table
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
// Create the object table
|
||||
executable.ObjectTable = new ObjectTableEntry[informationBlock.ObjectTableCount];
|
||||
|
||||
// Try to parse the object table
|
||||
for (int i = 0; i < executable.ObjectTable.Length; i++)
|
||||
{
|
||||
var entry = ParseObjectTableEntry(data);
|
||||
if (entry == null)
|
||||
return null;
|
||||
|
||||
executable.ObjectTable[i] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Object Page Map
|
||||
|
||||
// Get the object page map offset
|
||||
offset = informationBlock.ObjectPageMapOffset + stub.Header.NewExeHeaderAddr;
|
||||
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
|
||||
{
|
||||
// Seek to the object page map
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
// Create the object page map
|
||||
executable.ObjectPageMap = new ObjectPageMapEntry[informationBlock.ObjectTableCount];
|
||||
|
||||
// Try to parse the object page map
|
||||
for (int i = 0; i < executable.ObjectPageMap.Length; i++)
|
||||
{
|
||||
var entry = ParseObjectPageMapEntry(data);
|
||||
if (entry == null)
|
||||
return null;
|
||||
|
||||
executable.ObjectPageMap[i] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Object Iterate Data Map
|
||||
|
||||
offset = informationBlock.ObjectIterateDataMapOffset + stub.Header.NewExeHeaderAddr;
|
||||
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
|
||||
{
|
||||
// Seek to the object page map
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
// TODO: Implement when model found
|
||||
// No model has been found in the documentation about what
|
||||
// each of the entries looks like for this map.
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Resource Table
|
||||
|
||||
// Get the resource table offset
|
||||
offset = informationBlock.ResourceTableOffset + stub.Header.NewExeHeaderAddr;
|
||||
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
|
||||
{
|
||||
// Seek to the resource table
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
// Create the resource table
|
||||
executable.ResourceTable = new ResourceTableEntry[informationBlock.ResourceTableCount];
|
||||
|
||||
// Try to parse the resource table
|
||||
for (int i = 0; i < executable.ResourceTable.Length; i++)
|
||||
{
|
||||
var entry = ParseResourceTableEntry(data);
|
||||
if (entry == null)
|
||||
return null;
|
||||
|
||||
executable.ResourceTable[i] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Resident Names Table
|
||||
|
||||
// Get the resident names table offset
|
||||
offset = informationBlock.ResidentNamesTableOffset + stub.Header.NewExeHeaderAddr;
|
||||
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
|
||||
{
|
||||
// Seek to the resident names table
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
// Create the resident names table
|
||||
var residentNamesTable = new List<ResidentNamesTableEntry>();
|
||||
|
||||
// Try to parse the resident names table
|
||||
while (true)
|
||||
{
|
||||
var entry = ParseResidentNamesTableEntry(data);
|
||||
residentNamesTable.Add(entry);
|
||||
|
||||
// If we have a 0-length entry
|
||||
if (entry.Length == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// Assign the resident names table
|
||||
executable.ResidentNamesTable = residentNamesTable.ToArray();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Entry Table
|
||||
|
||||
// Get the entry table offset
|
||||
offset = informationBlock.EntryTableOffset + stub.Header.NewExeHeaderAddr;
|
||||
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
|
||||
{
|
||||
// Seek to the entry table
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
// Create the entry table
|
||||
var entryTable = new List<EntryTableBundle>();
|
||||
|
||||
// Try to parse the entry table
|
||||
while (true)
|
||||
{
|
||||
var bundle = ParseEntryTableBundle(data);
|
||||
entryTable.Add(bundle);
|
||||
|
||||
// If we have a 0-length entry
|
||||
if (bundle.Entries == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// Assign the entry table
|
||||
executable.EntryTable = entryTable.ToArray();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Module Format Directives Table
|
||||
|
||||
// Get the module format directives table offset
|
||||
offset = informationBlock.ModuleDirectivesTableOffset + stub.Header.NewExeHeaderAddr;
|
||||
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
|
||||
{
|
||||
// Seek to the module format directives table
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
// Create the module format directives table
|
||||
executable.ModuleFormatDirectivesTable = new ModuleFormatDirectivesTableEntry[informationBlock.ModuleDirectivesCount];
|
||||
|
||||
// Try to parse the module format directives table
|
||||
for (int i = 0; i < executable.ModuleFormatDirectivesTable.Length; i++)
|
||||
{
|
||||
var entry = ParseModuleFormatDirectivesTableEntry(data);
|
||||
if (entry == null)
|
||||
return null;
|
||||
|
||||
executable.ModuleFormatDirectivesTable[i] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Verify Record Directive Table
|
||||
|
||||
// TODO: Figure out where the offset to this table is stored
|
||||
// The documentation suggests it's either part of or immediately following
|
||||
// the Module Format Directives Table
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fix-up Page Table
|
||||
|
||||
// Get the fix-up page table offset
|
||||
offset = informationBlock.FixupPageTableOffset + stub.Header.NewExeHeaderAddr;
|
||||
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
|
||||
{
|
||||
// Seek to the fix-up page table
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
// Create the fix-up page table
|
||||
executable.FixupPageTable = new FixupPageTableEntry[executable.ObjectPageMap.Length + 1];
|
||||
|
||||
// Try to parse the fix-up page table
|
||||
for (int i = 0; i < executable.FixupPageTable.Length; i++)
|
||||
{
|
||||
var entry = ParseFixupPageTableEntry(data);
|
||||
if (entry == null)
|
||||
return null;
|
||||
|
||||
executable.FixupPageTable[i] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fix-up Record Table
|
||||
|
||||
// Get the fix-up record table offset
|
||||
offset = informationBlock.FixupRecordTableOffset + stub.Header.NewExeHeaderAddr;
|
||||
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
|
||||
{
|
||||
// Seek to the fix-up record table
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
// Create the fix-up record table
|
||||
executable.FixupRecordTable = new FixupRecordTableEntry[executable.ObjectPageMap.Length + 1];
|
||||
|
||||
// Try to parse the fix-up record table
|
||||
for (int i = 0; i < executable.FixupRecordTable.Length; i++)
|
||||
{
|
||||
var entry = ParseFixupRecordTableEntry(data);
|
||||
if (entry == null)
|
||||
return null;
|
||||
|
||||
executable.FixupRecordTable[i] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Imported Module Name Table
|
||||
|
||||
// Get the imported module name table offset
|
||||
offset = informationBlock.ImportedModulesNameTableOffset + stub.Header.NewExeHeaderAddr;
|
||||
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
|
||||
{
|
||||
// Seek to the imported module name table
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
// Create the imported module name table
|
||||
executable.ImportModuleNameTable = new ImportModuleNameTableEntry[informationBlock.ImportedModulesCount];
|
||||
|
||||
// Try to parse the imported module name table
|
||||
for (int i = 0; i < executable.ImportModuleNameTable.Length; i++)
|
||||
{
|
||||
var entry = ParseImportModuleNameTableEntry(data);
|
||||
if (entry == null)
|
||||
return null;
|
||||
|
||||
executable.ImportModuleNameTable[i] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Imported Module Procedure Name Table
|
||||
|
||||
// Get the imported module procedure name table offset
|
||||
offset = informationBlock.ImportProcedureNameTableOffset + stub.Header.NewExeHeaderAddr;
|
||||
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
|
||||
{
|
||||
// Seek to the imported module procedure name table
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
// Get the size of the imported module procedure name table
|
||||
long tableSize = informationBlock.FixupPageTableOffset
|
||||
+ informationBlock.FixupSectionSize
|
||||
- informationBlock.ImportProcedureNameTableOffset;
|
||||
|
||||
// Create the imported module procedure name table
|
||||
var importModuleProcedureNameTable = new List<ImportModuleProcedureNameTableEntry>();
|
||||
|
||||
// Try to parse the imported module procedure name table
|
||||
while (data.Position < offset + tableSize)
|
||||
{
|
||||
var entry = ParseImportModuleProcedureNameTableEntry(data);
|
||||
if (entry == null)
|
||||
return null;
|
||||
|
||||
importModuleProcedureNameTable.Add(entry);
|
||||
}
|
||||
|
||||
// Assign the resident names table
|
||||
executable.ImportModuleProcedureNameTable = importModuleProcedureNameTable.ToArray();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Per-Page Checksum Table
|
||||
|
||||
// Get the per-page checksum table offset
|
||||
offset = informationBlock.PerPageChecksumTableOffset + stub.Header.NewExeHeaderAddr;
|
||||
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
|
||||
{
|
||||
// Seek to the per-page checksum name table
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
// Create the per-page checksum name table
|
||||
executable.PerPageChecksumTable = new PerPageChecksumTableEntry[informationBlock.ModuleNumberPages];
|
||||
|
||||
// Try to parse the per-page checksum name table
|
||||
for (int i = 0; i < executable.PerPageChecksumTable.Length; i++)
|
||||
{
|
||||
var entry = ParsePerPageChecksumTableEntry(data);
|
||||
if (entry == null)
|
||||
return null;
|
||||
|
||||
executable.PerPageChecksumTable[i] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Non-Resident Names Table
|
||||
|
||||
// Get the non-resident names table offset
|
||||
offset = informationBlock.NonResidentNamesTableOffset + stub.Header.NewExeHeaderAddr;
|
||||
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
|
||||
{
|
||||
// Seek to the non-resident names table
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
// Create the non-resident names table
|
||||
var nonResidentNamesTable = new List<NonResidentNamesTableEntry>();
|
||||
|
||||
// Try to parse the non-resident names table
|
||||
while (true)
|
||||
{
|
||||
var entry = ParseNonResidentNameTableEntry(data);
|
||||
nonResidentNamesTable.Add(entry);
|
||||
|
||||
// If we have a 0-length entry
|
||||
if (entry.Length == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// Assign the non-resident names table
|
||||
executable.NonResidentNamesTable = nonResidentNamesTable.ToArray();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Debug Information
|
||||
|
||||
// Get the debug information offset
|
||||
offset = informationBlock.DebugInformationOffset + stub.Header.NewExeHeaderAddr;
|
||||
if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length)
|
||||
{
|
||||
// Seek to the debug information
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
// Try to parse the debug information
|
||||
var debugInformation = ParseDebugInformation(data, informationBlock.DebugInformationLength);
|
||||
if (debugInformation == null)
|
||||
return null;
|
||||
|
||||
// Set the debug information
|
||||
executable.DebugInformation = debugInformation;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
return executable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an information block
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled information block on success, null on error</returns>
|
||||
private static InformationBlock ParseInformationBlock(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
var informationBlock = new InformationBlock();
|
||||
|
||||
byte[] magic = data.ReadBytes(2);
|
||||
informationBlock.Signature = Encoding.ASCII.GetString(magic);
|
||||
if (informationBlock.Signature != LESignatureString && informationBlock.Signature != LXSignatureString)
|
||||
return null;
|
||||
|
||||
informationBlock.ByteOrder = (ByteOrder)data.ReadByteValue();
|
||||
informationBlock.WordOrder = (WordOrder)data.ReadByteValue();
|
||||
informationBlock.ExecutableFormatLevel = data.ReadUInt32();
|
||||
informationBlock.CPUType = (CPUType)data.ReadUInt16();
|
||||
informationBlock.ModuleOS = (OperatingSystem)data.ReadUInt16();
|
||||
informationBlock.ModuleVersion = data.ReadUInt32();
|
||||
informationBlock.ModuleTypeFlags = (ModuleFlags)data.ReadUInt32();
|
||||
informationBlock.ModuleNumberPages = data.ReadUInt32();
|
||||
informationBlock.InitialObjectCS = data.ReadUInt32();
|
||||
informationBlock.InitialEIP = data.ReadUInt32();
|
||||
informationBlock.InitialObjectSS = data.ReadUInt32();
|
||||
informationBlock.InitialESP = data.ReadUInt32();
|
||||
informationBlock.MemoryPageSize = data.ReadUInt32();
|
||||
informationBlock.BytesOnLastPage = data.ReadUInt32();
|
||||
informationBlock.FixupSectionSize = data.ReadUInt32();
|
||||
informationBlock.FixupSectionChecksum = data.ReadUInt32();
|
||||
informationBlock.LoaderSectionSize = data.ReadUInt32();
|
||||
informationBlock.LoaderSectionChecksum = data.ReadUInt32();
|
||||
informationBlock.ObjectTableOffset = data.ReadUInt32();
|
||||
informationBlock.ObjectTableCount = data.ReadUInt32();
|
||||
informationBlock.ObjectPageMapOffset = data.ReadUInt32();
|
||||
informationBlock.ObjectIterateDataMapOffset = data.ReadUInt32();
|
||||
informationBlock.ResourceTableOffset = data.ReadUInt32();
|
||||
informationBlock.ResourceTableCount = data.ReadUInt32();
|
||||
informationBlock.ResidentNamesTableOffset = data.ReadUInt32();
|
||||
informationBlock.EntryTableOffset = data.ReadUInt32();
|
||||
informationBlock.ModuleDirectivesTableOffset = data.ReadUInt32();
|
||||
informationBlock.ModuleDirectivesCount = data.ReadUInt32();
|
||||
informationBlock.FixupPageTableOffset = data.ReadUInt32();
|
||||
informationBlock.FixupRecordTableOffset = data.ReadUInt32();
|
||||
informationBlock.ImportedModulesNameTableOffset = data.ReadUInt32();
|
||||
informationBlock.ImportedModulesCount = data.ReadUInt32();
|
||||
informationBlock.ImportProcedureNameTableOffset = data.ReadUInt32();
|
||||
informationBlock.PerPageChecksumTableOffset = data.ReadUInt32();
|
||||
informationBlock.DataPagesOffset = data.ReadUInt32();
|
||||
informationBlock.PreloadPageCount = data.ReadUInt32();
|
||||
informationBlock.NonResidentNamesTableOffset = data.ReadUInt32();
|
||||
informationBlock.NonResidentNamesTableLength = data.ReadUInt32();
|
||||
informationBlock.NonResidentNamesTableChecksum = data.ReadUInt32();
|
||||
informationBlock.AutomaticDataObject = data.ReadUInt32();
|
||||
informationBlock.DebugInformationOffset = data.ReadUInt32();
|
||||
informationBlock.DebugInformationLength = data.ReadUInt32();
|
||||
informationBlock.PreloadInstancePagesNumber = data.ReadUInt32();
|
||||
informationBlock.DemandInstancePagesNumber = data.ReadUInt32();
|
||||
informationBlock.ExtraHeapAllocation = data.ReadUInt32();
|
||||
|
||||
return informationBlock;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an object table entry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled object table entry on success, null on error</returns>
|
||||
private static ObjectTableEntry ParseObjectTableEntry(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
var entry = new ObjectTableEntry();
|
||||
|
||||
entry.VirtualSegmentSize = data.ReadUInt32();
|
||||
entry.RelocationBaseAddress = data.ReadUInt32();
|
||||
entry.ObjectFlags = (ObjectFlags)data.ReadUInt16();
|
||||
entry.PageTableIndex = data.ReadUInt32();
|
||||
entry.PageTableEntries = data.ReadUInt32();
|
||||
entry.Reserved = data.ReadUInt32();
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an object page map entry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled object page map entry on success, null on error</returns>
|
||||
private static ObjectPageMapEntry ParseObjectPageMapEntry(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
var entry = new ObjectPageMapEntry();
|
||||
|
||||
entry.PageDataOffset = data.ReadUInt32();
|
||||
entry.DataSize = data.ReadUInt16();
|
||||
entry.Flags = (ObjectPageFlags)data.ReadUInt16();
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a resource table entry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled resource table entry on success, null on error</returns>
|
||||
private static ResourceTableEntry ParseResourceTableEntry(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
var entry = new ResourceTableEntry();
|
||||
|
||||
entry.TypeID = (ResourceTableEntryType)data.ReadUInt32();
|
||||
entry.NameID = data.ReadUInt16();
|
||||
entry.ResourceSize = data.ReadUInt32();
|
||||
entry.ObjectNumber = data.ReadUInt16();
|
||||
entry.Offset = data.ReadUInt32();
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a resident names table entry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled resident names table entry on success, null on error</returns>
|
||||
private static ResidentNamesTableEntry ParseResidentNamesTableEntry(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
var entry = new ResidentNamesTableEntry();
|
||||
|
||||
entry.Length = data.ReadByteValue();
|
||||
if (entry.Length > 0)
|
||||
{
|
||||
byte[] name = data.ReadBytes(entry.Length);
|
||||
entry.Name = Encoding.ASCII.GetString(name).TrimEnd('\0');
|
||||
}
|
||||
entry.OrdinalNumber = data.ReadUInt16();
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an entry table bundle
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled entry table bundle on success, null on error</returns>
|
||||
private static EntryTableBundle ParseEntryTableBundle(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
var bundle = new EntryTableBundle();
|
||||
|
||||
bundle.Entries = data.ReadByteValue();
|
||||
if (bundle.Entries == 0)
|
||||
return bundle;
|
||||
|
||||
bundle.BundleType = (BundleType)data.ReadByteValue();
|
||||
bundle.TableEntries = new EntryTableEntry[bundle.Entries];
|
||||
for (int i = 0; i < bundle.Entries; i++)
|
||||
{
|
||||
var entry = new EntryTableEntry();
|
||||
|
||||
switch (bundle.BundleType & ~BundleType.ParameterTypingInformationPresent)
|
||||
{
|
||||
case BundleType.UnusedEntry:
|
||||
// Empty entry with no information
|
||||
break;
|
||||
|
||||
case BundleType.SixteenBitEntry:
|
||||
entry.SixteenBitObjectNumber = data.ReadUInt16();
|
||||
entry.SixteenBitEntryFlags = (EntryFlags)data.ReadByteValue();
|
||||
entry.SixteenBitOffset = data.ReadUInt16();
|
||||
break;
|
||||
|
||||
case BundleType.TwoEightySixCallGateEntry:
|
||||
entry.TwoEightySixObjectNumber = data.ReadUInt16();
|
||||
entry.TwoEightySixEntryFlags = (EntryFlags)data.ReadByteValue();
|
||||
entry.TwoEightySixOffset = data.ReadUInt16();
|
||||
entry.TwoEightySixCallgate = data.ReadUInt16();
|
||||
break;
|
||||
|
||||
case BundleType.ThirtyTwoBitEntry:
|
||||
entry.ThirtyTwoBitObjectNumber = data.ReadUInt16();
|
||||
entry.ThirtyTwoBitEntryFlags = (EntryFlags)data.ReadByteValue();
|
||||
entry.ThirtyTwoBitOffset = data.ReadUInt32();
|
||||
break;
|
||||
|
||||
case BundleType.ForwarderEntry:
|
||||
entry.ForwarderReserved = data.ReadUInt16();
|
||||
entry.ForwarderFlags = (ForwarderFlags)data.ReadByteValue();
|
||||
entry.ForwarderModuleOrdinalNumber = data.ReadUInt16();
|
||||
entry.ProcedureNameOffset = data.ReadUInt32();
|
||||
entry.ImportOrdinalNumber = data.ReadUInt32();
|
||||
break;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
bundle.TableEntries[i] = entry;
|
||||
}
|
||||
|
||||
return bundle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a module format directives table entry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled module format directives table entry on success, null on error</returns>
|
||||
private static ModuleFormatDirectivesTableEntry ParseModuleFormatDirectivesTableEntry(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
var entry = new ModuleFormatDirectivesTableEntry();
|
||||
|
||||
entry.DirectiveNumber = (DirectiveNumber)data.ReadUInt16();
|
||||
entry.DirectiveDataLength = data.ReadUInt16();
|
||||
entry.DirectiveDataOffset = data.ReadUInt32();
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a verify record directive table entry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled verify record directive table entry on success, null on error</returns>
|
||||
private static VerifyRecordDirectiveTableEntry ParseVerifyRecordDirectiveTableEntry(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
var entry = new VerifyRecordDirectiveTableEntry();
|
||||
|
||||
entry.EntryCount = data.ReadUInt16();
|
||||
entry.OrdinalIndex = data.ReadUInt16();
|
||||
entry.Version = data.ReadUInt16();
|
||||
entry.ObjectEntriesCount = data.ReadUInt16();
|
||||
entry.ObjectNumberInModule = data.ReadUInt16();
|
||||
entry.ObjectLoadBaseAddress = data.ReadUInt16();
|
||||
entry.ObjectVirtualAddressSize = data.ReadUInt16();
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a fix-up page table entry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled fix-up page table entry on success, null on error</returns>
|
||||
private static FixupPageTableEntry ParseFixupPageTableEntry(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
var entry = new FixupPageTableEntry();
|
||||
|
||||
entry.Offset = data.ReadUInt32();
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a fix-up record table entry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled fix-up record table entry on success, null on error</returns>
|
||||
private static FixupRecordTableEntry ParseFixupRecordTableEntry(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
var entry = new FixupRecordTableEntry();
|
||||
|
||||
entry.SourceType = (FixupRecordSourceType)data.ReadByteValue();
|
||||
entry.TargetFlags = (FixupRecordTargetFlags)data.ReadByteValue();
|
||||
|
||||
// Source list flag
|
||||
if (entry.SourceType.HasFlag(FixupRecordSourceType.SourceListFlag))
|
||||
entry.SourceOffsetListCount = data.ReadByteValue();
|
||||
else
|
||||
entry.SourceOffset = data.ReadUInt16();
|
||||
|
||||
// OBJECT / TRGOFF
|
||||
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.InternalReference))
|
||||
{
|
||||
// 16-bit Object Number/Module Ordinal Flag
|
||||
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag))
|
||||
entry.TargetObjectNumberWORD = data.ReadUInt16();
|
||||
else
|
||||
entry.TargetObjectNumberByte = data.ReadByteValue();
|
||||
|
||||
// 16-bit Selector fixup
|
||||
if (!entry.SourceType.HasFlag(FixupRecordSourceType.SixteenBitSelectorFixup))
|
||||
{
|
||||
// 32-bit Target Offset Flag
|
||||
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag))
|
||||
entry.TargetOffsetDWORD = data.ReadUInt32();
|
||||
else
|
||||
entry.TargetOffsetWORD = data.ReadUInt16();
|
||||
}
|
||||
}
|
||||
|
||||
// MOD ORD# / IMPORT ORD / ADDITIVE
|
||||
else if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ImportedReferenceByOrdinal))
|
||||
{
|
||||
// 16-bit Object Number/Module Ordinal Flag
|
||||
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag))
|
||||
entry.OrdinalIndexImportModuleNameTableWORD = data.ReadUInt16();
|
||||
else
|
||||
entry.OrdinalIndexImportModuleNameTableByte = data.ReadByteValue();
|
||||
|
||||
// 8-bit Ordinal Flag & 32-bit Target Offset Flag
|
||||
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.EightBitOrdinalFlag))
|
||||
entry.ImportedOrdinalNumberByte = data.ReadByteValue();
|
||||
else if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag))
|
||||
entry.ImportedOrdinalNumberDWORD = data.ReadUInt32();
|
||||
else
|
||||
entry.ImportedOrdinalNumberWORD = data.ReadUInt16();
|
||||
|
||||
// Additive Fixup Flag
|
||||
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.AdditiveFixupFlag))
|
||||
{
|
||||
// 32-bit Additive Flag
|
||||
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag))
|
||||
entry.AdditiveFixupValueDWORD = data.ReadUInt32();
|
||||
else
|
||||
entry.AdditiveFixupValueWORD = data.ReadUInt16();
|
||||
}
|
||||
}
|
||||
|
||||
// MOD ORD# / PROCEDURE NAME OFFSET / ADDITIVE
|
||||
else if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ImportedReferenceByName))
|
||||
{
|
||||
// 16-bit Object Number/Module Ordinal Flag
|
||||
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag))
|
||||
entry.OrdinalIndexImportModuleNameTableWORD = data.ReadUInt16();
|
||||
else
|
||||
entry.OrdinalIndexImportModuleNameTableByte = data.ReadByteValue();
|
||||
|
||||
// 32-bit Target Offset Flag
|
||||
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag))
|
||||
entry.OffsetImportProcedureNameTableDWORD = data.ReadUInt32();
|
||||
else
|
||||
entry.OffsetImportProcedureNameTableWORD = data.ReadUInt16();
|
||||
|
||||
// Additive Fixup Flag
|
||||
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.AdditiveFixupFlag))
|
||||
{
|
||||
// 32-bit Additive Flag
|
||||
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag))
|
||||
entry.AdditiveFixupValueDWORD = data.ReadUInt32();
|
||||
else
|
||||
entry.AdditiveFixupValueWORD = data.ReadUInt16();
|
||||
}
|
||||
}
|
||||
|
||||
// ORD # / ADDITIVE
|
||||
else if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.InternalReferenceViaEntryTable))
|
||||
{
|
||||
// 16-bit Object Number/Module Ordinal Flag
|
||||
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag))
|
||||
entry.OrdinalIndexImportModuleNameTableWORD = data.ReadUInt16();
|
||||
else
|
||||
entry.OrdinalIndexImportModuleNameTableByte = data.ReadByteValue();
|
||||
|
||||
// Additive Fixup Flag
|
||||
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.AdditiveFixupFlag))
|
||||
{
|
||||
// 32-bit Additive Flag
|
||||
if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag))
|
||||
entry.AdditiveFixupValueDWORD = data.ReadUInt32();
|
||||
else
|
||||
entry.AdditiveFixupValueWORD = data.ReadUInt16();
|
||||
}
|
||||
}
|
||||
|
||||
// No other top-level flags recognized
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
#region SCROFFn
|
||||
|
||||
if (entry.SourceType.HasFlag(FixupRecordSourceType.SourceListFlag))
|
||||
{
|
||||
entry.SourceOffsetList = new ushort[entry.SourceOffsetListCount];
|
||||
for (int i = 0; i < entry.SourceOffsetList.Length; i++)
|
||||
{
|
||||
entry.SourceOffsetList[i] = data.ReadUInt16();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a import module name table entry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled import module name table entry on success, null on error</returns>
|
||||
private static ImportModuleNameTableEntry ParseImportModuleNameTableEntry(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
var entry = new ImportModuleNameTableEntry();
|
||||
|
||||
entry.Length = data.ReadByteValue();
|
||||
if (entry.Length > 0)
|
||||
{
|
||||
byte[] name = data.ReadBytes(entry.Length);
|
||||
entry.Name = Encoding.ASCII.GetString(name).TrimEnd('\0');
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a import module name table entry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled import module name table entry on success, null on error</returns>
|
||||
private static ImportModuleProcedureNameTableEntry ParseImportModuleProcedureNameTableEntry(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
var entry = new ImportModuleProcedureNameTableEntry();
|
||||
|
||||
entry.Length = data.ReadByteValue();
|
||||
if (entry.Length > 0)
|
||||
{
|
||||
byte[] name = data.ReadBytes(entry.Length);
|
||||
entry.Name = Encoding.ASCII.GetString(name).TrimEnd('\0');
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a per-page checksum table entry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled per-page checksum table entry on success, null on error</returns>
|
||||
private static PerPageChecksumTableEntry ParsePerPageChecksumTableEntry(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
var entry = new PerPageChecksumTableEntry();
|
||||
|
||||
entry.Checksum = data.ReadUInt32();
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a non-resident names table entry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled non-resident names table entry on success, null on error</returns>
|
||||
private static NonResidentNamesTableEntry ParseNonResidentNameTableEntry(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
var entry = new NonResidentNamesTableEntry();
|
||||
|
||||
entry.Length = data.ReadByteValue();
|
||||
if (entry.Length > 0)
|
||||
{
|
||||
byte[] name = data.ReadBytes(entry.Length);
|
||||
entry.Name = Encoding.ASCII.GetString(name).TrimEnd('\0');
|
||||
}
|
||||
entry.OrdinalNumber = data.ReadUInt16();
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a debug information
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="size">Total size of the debug information</param>
|
||||
/// <returns>Filled debug information on success, null on error</returns>
|
||||
private static DebugInformation ParseDebugInformation(Stream data, long size)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
var debugInformation = new DebugInformation();
|
||||
|
||||
byte[] signature = data.ReadBytes(3);
|
||||
debugInformation.Signature = Encoding.ASCII.GetString(signature);
|
||||
if (debugInformation.Signature != DebugInformationSignatureString)
|
||||
return null;
|
||||
|
||||
debugInformation.FormatType = (DebugFormatType)data.ReadByteValue();
|
||||
debugInformation.DebuggerData = data.ReadBytes((int)(size - 4));
|
||||
|
||||
return debugInformation;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BurnOutSharp.Models.MSDOS;
|
||||
using BurnOutSharp.Utilities;
|
||||
using static BurnOutSharp.Models.MSDOS.Constants;
|
||||
using BinaryObjectScanner.Models.MSDOS;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
using static BinaryObjectScanner.Models.MSDOS.Constants;
|
||||
|
||||
namespace BurnOutSharp.Builders
|
||||
namespace BinaryObjectScanner.Builders
|
||||
{
|
||||
public static class MSDOS
|
||||
{
|
||||
@@ -1,10 +1,10 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BurnOutSharp.Models.MicrosoftCabinet;
|
||||
using BurnOutSharp.Utilities;
|
||||
using static BurnOutSharp.Models.MicrosoftCabinet.Constants;
|
||||
using BinaryObjectScanner.Models.MicrosoftCabinet;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
using static BinaryObjectScanner.Models.MicrosoftCabinet.Constants;
|
||||
|
||||
namespace BurnOutSharp.Builders
|
||||
namespace BinaryObjectScanner.Builders
|
||||
{
|
||||
// TODO: Add multi-cabinet reading
|
||||
public class MicrosoftCabinet
|
||||
@@ -2,11 +2,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BurnOutSharp.Models.MoPaQ;
|
||||
using BurnOutSharp.Utilities;
|
||||
using static BurnOutSharp.Models.MoPaQ.Constants;
|
||||
using BinaryObjectScanner.Models.MoPaQ;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
using static BinaryObjectScanner.Models.MoPaQ.Constants;
|
||||
|
||||
namespace BurnOutSharp.Builders
|
||||
namespace BinaryObjectScanner.Builders
|
||||
{
|
||||
public class MoPaQ
|
||||
{
|
||||
1223
BinaryObjectScanner.Builders/N3DS.cs
Normal file
1223
BinaryObjectScanner.Builders/N3DS.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BurnOutSharp.Models.NCF;
|
||||
using BurnOutSharp.Utilities;
|
||||
using BinaryObjectScanner.Models.NCF;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
|
||||
namespace BurnOutSharp.Builders
|
||||
namespace BinaryObjectScanner.Builders
|
||||
{
|
||||
public static class NCF
|
||||
{
|
||||
@@ -2,11 +2,11 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using BurnOutSharp.Models.NewExecutable;
|
||||
using BurnOutSharp.Utilities;
|
||||
using static BurnOutSharp.Models.NewExecutable.Constants;
|
||||
using BinaryObjectScanner.Models.NewExecutable;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
using static BinaryObjectScanner.Models.NewExecutable.Constants;
|
||||
|
||||
namespace BurnOutSharp.Builders
|
||||
namespace BinaryObjectScanner.Builders
|
||||
{
|
||||
public static class NewExecutable
|
||||
{
|
||||
393
BinaryObjectScanner.Builders/Nitro.cs
Normal file
393
BinaryObjectScanner.Builders/Nitro.cs
Normal file
@@ -0,0 +1,393 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BinaryObjectScanner.Models.Nitro;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
|
||||
namespace BinaryObjectScanner.Builders
|
||||
{
|
||||
public class Nitro
|
||||
{
|
||||
#region Byte Data
|
||||
|
||||
/// <summary>
|
||||
/// Parse a byte array into a NDS cart image
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array to parse</param>
|
||||
/// <param name="offset">Offset into the byte array</param>
|
||||
/// <returns>Filled cart image on success, null on error</returns>
|
||||
public static Cart ParseCart(byte[] data, int offset)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
// If the offset is out of bounds
|
||||
if (offset < 0 || offset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Create a memory stream and parse that
|
||||
MemoryStream dataStream = new MemoryStream(data, offset, data.Length - offset);
|
||||
return ParseCart(dataStream);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Stream Data
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a NDS cart image
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled cart image on success, null on error</returns>
|
||||
public static Cart ParseCart(Stream data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
|
||||
return null;
|
||||
|
||||
// If the offset is out of bounds
|
||||
if (data.Position < 0 || data.Position >= data.Length)
|
||||
return null;
|
||||
|
||||
// Cache the current offset
|
||||
int initialOffset = (int)data.Position;
|
||||
|
||||
// Create a new cart image to fill
|
||||
var cart = new Cart();
|
||||
|
||||
#region Header
|
||||
|
||||
// Try to parse the header
|
||||
var header = ParseCommonHeader(data);
|
||||
if (header == null)
|
||||
return null;
|
||||
|
||||
// Set the cart image header
|
||||
cart.CommonHeader = header;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extended DSi Header
|
||||
|
||||
// If we have a DSi-compatible cartridge
|
||||
if (header.UnitCode == Unitcode.NDSPlusDSi || header.UnitCode == Unitcode.DSi)
|
||||
{
|
||||
var extendedDSiHeader = ParseExtendedDSiHeader(data);
|
||||
if (extendedDSiHeader == null)
|
||||
return null;
|
||||
|
||||
cart.ExtendedDSiHeader = extendedDSiHeader;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Secure Area
|
||||
|
||||
// Try to get the secure area offset
|
||||
long secureAreaOffset = 0x4000;
|
||||
if (secureAreaOffset > data.Length)
|
||||
return null;
|
||||
|
||||
// Seek to the secure area
|
||||
data.Seek(secureAreaOffset, SeekOrigin.Begin);
|
||||
|
||||
// Read the secure area without processing
|
||||
cart.SecureArea = data.ReadBytes(0x800);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Name Table
|
||||
|
||||
// Try to get the name table offset
|
||||
long nameTableOffset = header.FileNameTableOffset;
|
||||
if (nameTableOffset < 0 || nameTableOffset > data.Length)
|
||||
return null;
|
||||
|
||||
// Seek to the name table
|
||||
data.Seek(nameTableOffset, SeekOrigin.Begin);
|
||||
|
||||
// Try to parse the name table
|
||||
var nameTable = ParseNameTable(data);
|
||||
if (nameTable == null)
|
||||
return null;
|
||||
|
||||
// Set the name table
|
||||
cart.NameTable = nameTable;
|
||||
|
||||
#endregion
|
||||
|
||||
#region File Allocation Table
|
||||
|
||||
// Try to get the file allocation table offset
|
||||
long fileAllocationTableOffset = header.FileAllocationTableOffset;
|
||||
if (fileAllocationTableOffset < 0 || fileAllocationTableOffset > data.Length)
|
||||
return null;
|
||||
|
||||
// Seek to the file allocation table
|
||||
data.Seek(fileAllocationTableOffset, SeekOrigin.Begin);
|
||||
|
||||
// Create the file allocation table
|
||||
var fileAllocationTable = new List<FileAllocationTableEntry>();
|
||||
|
||||
// Try to parse the file allocation table
|
||||
while (data.Position - fileAllocationTableOffset < header.FileAllocationTableLength)
|
||||
{
|
||||
var entry = ParseFileAllocationTableEntry(data);
|
||||
fileAllocationTable.Add(entry);
|
||||
}
|
||||
|
||||
// Set the file allocation table
|
||||
cart.FileAllocationTable = fileAllocationTable.ToArray();
|
||||
|
||||
#endregion
|
||||
|
||||
// TODO: Read and optionally parse out the other areas
|
||||
// Look for offsets and lengths in the header pieces
|
||||
|
||||
return cart;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a common header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled common header on success, null on error</returns>
|
||||
private static CommonHeader ParseCommonHeader(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
CommonHeader commonHeader = new CommonHeader();
|
||||
|
||||
byte[] gameTitle = data.ReadBytes(12);
|
||||
commonHeader.GameTitle = Encoding.ASCII.GetString(gameTitle).TrimEnd('\0');
|
||||
commonHeader.GameCode = data.ReadUInt32();
|
||||
byte[] makerCode = data.ReadBytes(2);
|
||||
commonHeader.MakerCode = Encoding.ASCII.GetString(bytes: makerCode).TrimEnd('\0');
|
||||
commonHeader.UnitCode = (Unitcode)data.ReadByteValue();
|
||||
commonHeader.EncryptionSeedSelect = data.ReadByteValue();
|
||||
commonHeader.DeviceCapacity = data.ReadByteValue();
|
||||
commonHeader.Reserved1 = data.ReadBytes(7);
|
||||
commonHeader.GameRevision = data.ReadUInt16();
|
||||
commonHeader.RomVersion = data.ReadByteValue();
|
||||
commonHeader.InternalFlags = data.ReadByteValue();
|
||||
commonHeader.ARM9RomOffset = data.ReadUInt32();
|
||||
commonHeader.ARM9EntryAddress = data.ReadUInt32();
|
||||
commonHeader.ARM9LoadAddress = data.ReadUInt32();
|
||||
commonHeader.ARM9Size = data.ReadUInt32();
|
||||
commonHeader.ARM7RomOffset = data.ReadUInt32();
|
||||
commonHeader.ARM7EntryAddress = data.ReadUInt32();
|
||||
commonHeader.ARM7LoadAddress = data.ReadUInt32();
|
||||
commonHeader.ARM7Size = data.ReadUInt32();
|
||||
commonHeader.FileNameTableOffset = data.ReadUInt32();
|
||||
commonHeader.FileNameTableLength = data.ReadUInt32();
|
||||
commonHeader.FileAllocationTableOffset = data.ReadUInt32();
|
||||
commonHeader.FileAllocationTableLength = data.ReadUInt32();
|
||||
commonHeader.ARM9OverlayOffset = data.ReadUInt32();
|
||||
commonHeader.ARM9OverlayLength = data.ReadUInt32();
|
||||
commonHeader.ARM7OverlayOffset = data.ReadUInt32();
|
||||
commonHeader.ARM7OverlayLength = data.ReadUInt32();
|
||||
commonHeader.NormalCardControlRegisterSettings = data.ReadUInt32();
|
||||
commonHeader.SecureCardControlRegisterSettings = data.ReadUInt32();
|
||||
commonHeader.IconBannerOffset = data.ReadUInt32();
|
||||
commonHeader.SecureAreaCRC = data.ReadUInt16();
|
||||
commonHeader.SecureTransferTimeout = data.ReadUInt16();
|
||||
commonHeader.ARM9Autoload = data.ReadUInt32();
|
||||
commonHeader.ARM7Autoload = data.ReadUInt32();
|
||||
commonHeader.SecureDisable = data.ReadBytes(8);
|
||||
commonHeader.NTRRegionRomSize = data.ReadUInt32();
|
||||
commonHeader.HeaderSize = data.ReadUInt32();
|
||||
commonHeader.Reserved2 = data.ReadBytes(56);
|
||||
commonHeader.NintendoLogo = data.ReadBytes(156);
|
||||
commonHeader.NintendoLogoCRC = data.ReadUInt16();
|
||||
commonHeader.HeaderCRC = data.ReadUInt16();
|
||||
commonHeader.DebuggerReserved = data.ReadBytes(0x20);
|
||||
|
||||
return commonHeader;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an extended DSi header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled extended DSi header on success, null on error</returns>
|
||||
private static ExtendedDSiHeader ParseExtendedDSiHeader(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
ExtendedDSiHeader extendedDSiHeader = new ExtendedDSiHeader();
|
||||
|
||||
extendedDSiHeader.GlobalMBK15Settings = new uint[5];
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
extendedDSiHeader.GlobalMBK15Settings[i] = data.ReadUInt32();
|
||||
}
|
||||
extendedDSiHeader.LocalMBK68SettingsARM9 = new uint[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
extendedDSiHeader.LocalMBK68SettingsARM9[i] = data.ReadUInt32();
|
||||
}
|
||||
extendedDSiHeader.LocalMBK68SettingsARM7 = new uint[3];
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
extendedDSiHeader.LocalMBK68SettingsARM7[i] = data.ReadUInt32();
|
||||
}
|
||||
extendedDSiHeader.GlobalMBK9Setting = data.ReadUInt32();
|
||||
extendedDSiHeader.RegionFlags = data.ReadUInt32();
|
||||
extendedDSiHeader.AccessControl = data.ReadUInt32();
|
||||
extendedDSiHeader.ARM7SCFGEXTMask = data.ReadUInt32();
|
||||
extendedDSiHeader.ReservedFlags = data.ReadUInt32();
|
||||
extendedDSiHeader.ARM9iRomOffset = data.ReadUInt32();
|
||||
extendedDSiHeader.Reserved3 = data.ReadUInt32();
|
||||
extendedDSiHeader.ARM9iLoadAddress = data.ReadUInt32();
|
||||
extendedDSiHeader.ARM9iSize = data.ReadUInt32();
|
||||
extendedDSiHeader.ARM7iRomOffset = data.ReadUInt32();
|
||||
extendedDSiHeader.Reserved4 = data.ReadUInt32();
|
||||
extendedDSiHeader.ARM7iLoadAddress = data.ReadUInt32();
|
||||
extendedDSiHeader.ARM7iSize = data.ReadUInt32();
|
||||
extendedDSiHeader.DigestNTRRegionOffset = data.ReadUInt32();
|
||||
extendedDSiHeader.DigestNTRRegionLength = data.ReadUInt32();
|
||||
extendedDSiHeader.DigestTWLRegionOffset = data.ReadUInt32();
|
||||
extendedDSiHeader.DigestTWLRegionLength = data.ReadUInt32();
|
||||
extendedDSiHeader.DigestSectorHashtableRegionOffset = data.ReadUInt32();
|
||||
extendedDSiHeader.DigestSectorHashtableRegionLength = data.ReadUInt32();
|
||||
extendedDSiHeader.DigestBlockHashtableRegionOffset = data.ReadUInt32();
|
||||
extendedDSiHeader.DigestBlockHashtableRegionLength = data.ReadUInt32();
|
||||
extendedDSiHeader.DigestSectorSize = data.ReadUInt32();
|
||||
extendedDSiHeader.DigestBlockSectorCount = data.ReadUInt32();
|
||||
extendedDSiHeader.IconBannerSize = data.ReadUInt32();
|
||||
extendedDSiHeader.Unknown1 = data.ReadUInt32();
|
||||
extendedDSiHeader.ModcryptArea1Offset = data.ReadUInt32();
|
||||
extendedDSiHeader.ModcryptArea1Size = data.ReadUInt32();
|
||||
extendedDSiHeader.ModcryptArea2Offset = data.ReadUInt32();
|
||||
extendedDSiHeader.ModcryptArea2Size = data.ReadUInt32();
|
||||
extendedDSiHeader.TitleID = data.ReadBytes(8);
|
||||
extendedDSiHeader.DSiWarePublicSavSize = data.ReadUInt32();
|
||||
extendedDSiHeader.DSiWarePrivateSavSize = data.ReadUInt32();
|
||||
extendedDSiHeader.ReservedZero = data.ReadBytes(176);
|
||||
extendedDSiHeader.Unknown2 = data.ReadBytes(0x10);
|
||||
extendedDSiHeader.ARM9WithSecureAreaSHA1HMACHash = data.ReadBytes(20);
|
||||
extendedDSiHeader.ARM7SHA1HMACHash = data.ReadBytes(20);
|
||||
extendedDSiHeader.DigestMasterSHA1HMACHash = data.ReadBytes(20);
|
||||
extendedDSiHeader.BannerSHA1HMACHash = data.ReadBytes(20);
|
||||
extendedDSiHeader.ARM9iDecryptedSHA1HMACHash = data.ReadBytes(20);
|
||||
extendedDSiHeader.ARM7iDecryptedSHA1HMACHash = data.ReadBytes(20);
|
||||
extendedDSiHeader.Reserved5 = data.ReadBytes(40);
|
||||
extendedDSiHeader.ARM9NoSecureAreaSHA1HMACHash = data.ReadBytes(20);
|
||||
extendedDSiHeader.Reserved6 = data.ReadBytes(2636);
|
||||
extendedDSiHeader.ReservedAndUnchecked = data.ReadBytes(0x180);
|
||||
extendedDSiHeader.RSASignature = data.ReadBytes(0x80);
|
||||
|
||||
return extendedDSiHeader;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a name table
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled name table on success, null on error</returns>
|
||||
private static NameTable ParseNameTable(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
NameTable nameTable = new NameTable();
|
||||
|
||||
// Create a variable-length table
|
||||
var folderAllocationTable = new List<FolderAllocationTableEntry>();
|
||||
int entryCount = int.MaxValue;
|
||||
while (entryCount > 0)
|
||||
{
|
||||
var entry = ParseFolderAllocationTableEntry(data);
|
||||
folderAllocationTable.Add(entry);
|
||||
|
||||
// If we have the root entry
|
||||
if (entryCount == int.MaxValue)
|
||||
entryCount = (entry.Unknown << 8) | entry.ParentFolderIndex;
|
||||
|
||||
// Decrement the entry count
|
||||
entryCount--;
|
||||
}
|
||||
|
||||
// Assign the folder allocation table
|
||||
nameTable.FolderAllocationTable = folderAllocationTable.ToArray();
|
||||
|
||||
// Create a variable-length table
|
||||
var nameList = new List<NameListEntry>();
|
||||
while (true)
|
||||
{
|
||||
var entry = ParseNameListEntry(data);
|
||||
if (entry == null)
|
||||
break;
|
||||
|
||||
nameList.Add(entry);
|
||||
}
|
||||
|
||||
// Assign the name list
|
||||
nameTable.NameList = nameList.ToArray();
|
||||
|
||||
return nameTable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a folder allocation table entry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled folder allocation table entry on success, null on error</returns>
|
||||
private static FolderAllocationTableEntry ParseFolderAllocationTableEntry(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
FolderAllocationTableEntry entry = new FolderAllocationTableEntry();
|
||||
|
||||
entry.StartOffset = data.ReadUInt32();
|
||||
entry.FirstFileIndex = data.ReadUInt16();
|
||||
entry.ParentFolderIndex = data.ReadByteValue();
|
||||
entry.Unknown = data.ReadByteValue();
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a name list entry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled name list entry on success, null on error</returns>
|
||||
private static NameListEntry ParseNameListEntry(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
NameListEntry entry = new NameListEntry();
|
||||
|
||||
byte flagAndSize = data.ReadByteValue();
|
||||
if (flagAndSize == 0xFF)
|
||||
return null;
|
||||
|
||||
entry.Folder = (flagAndSize & 0x80) != 0;
|
||||
|
||||
byte size = (byte)(flagAndSize & ~0x80);
|
||||
if (size > 0)
|
||||
{
|
||||
byte[] name = data.ReadBytes(size);
|
||||
entry.Name = Encoding.UTF8.GetString(name);
|
||||
}
|
||||
|
||||
if (entry.Folder)
|
||||
entry.Index = data.ReadUInt16();
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a name list entry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled name list entry on success, null on error</returns>
|
||||
private static FileAllocationTableEntry ParseFileAllocationTableEntry(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
FileAllocationTableEntry entry = new FileAllocationTableEntry();
|
||||
|
||||
entry.StartOffset = data.ReadUInt32();
|
||||
entry.EndOffset = data.ReadUInt32();
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BurnOutSharp.Models.PAK;
|
||||
using BurnOutSharp.Utilities;
|
||||
using static BurnOutSharp.Models.PAK.Constants;
|
||||
using BinaryObjectScanner.Models.PAK;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
using static BinaryObjectScanner.Models.PAK.Constants;
|
||||
|
||||
namespace BurnOutSharp.Builders
|
||||
namespace BinaryObjectScanner.Builders
|
||||
{
|
||||
public static class PAK
|
||||
{
|
||||
211
BinaryObjectScanner.Builders/PFF.cs
Normal file
211
BinaryObjectScanner.Builders/PFF.cs
Normal file
@@ -0,0 +1,211 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BinaryObjectScanner.Models.PFF;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
using static BinaryObjectScanner.Models.PFF.Constants;
|
||||
|
||||
namespace BinaryObjectScanner.Builders
|
||||
{
|
||||
public class PFF
|
||||
{
|
||||
#region Byte Data
|
||||
|
||||
/// <summary>
|
||||
/// Parse a byte array into a PFF archive
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array to parse</param>
|
||||
/// <param name="offset">Offset into the byte array</param>
|
||||
/// <returns>Filled archive on success, null on error</returns>
|
||||
public static Archive ParseArchive(byte[] data, int offset)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
// If the offset is out of bounds
|
||||
if (offset < 0 || offset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Create a memory stream and parse that
|
||||
MemoryStream dataStream = new MemoryStream(data, offset, data.Length - offset);
|
||||
return ParseArchive(dataStream);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Stream Data
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a PFF archive
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled archive on success, null on error</returns>
|
||||
public static Archive ParseArchive(Stream data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
|
||||
return null;
|
||||
|
||||
// If the offset is out of bounds
|
||||
if (data.Position < 0 || data.Position >= data.Length)
|
||||
return null;
|
||||
|
||||
// Cache the current offset
|
||||
int initialOffset = (int)data.Position;
|
||||
|
||||
// Create a new archive to fill
|
||||
var archive = new Archive();
|
||||
|
||||
#region Header
|
||||
|
||||
// Try to parse the header
|
||||
var header = ParseHeader(data);
|
||||
if (header == null)
|
||||
return null;
|
||||
|
||||
// Set the archive header
|
||||
archive.Header = header;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Segments
|
||||
|
||||
// Get the segments
|
||||
long offset = header.FileListOffset;
|
||||
if (offset < 0 || offset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Seek to the segments
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
// Create the segments array
|
||||
archive.Segments = new Segment[header.NumberOfFiles];
|
||||
|
||||
// Read all segments in turn
|
||||
for (int i = 0; i < header.NumberOfFiles; i++)
|
||||
{
|
||||
var file = ParseSegment(data, header.FileSegmentSize);
|
||||
if (file == null)
|
||||
return null;
|
||||
|
||||
archive.Segments[i] = file;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Footer
|
||||
|
||||
// Get the footer offset
|
||||
offset = header.FileListOffset + (header.FileSegmentSize * header.NumberOfFiles);
|
||||
if (offset < 0 || offset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Seek to the footer
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
// Try to parse the footer
|
||||
var footer = ParseFooter(data);
|
||||
if (footer == null)
|
||||
return null;
|
||||
|
||||
// Set the archive footer
|
||||
archive.Footer = footer;
|
||||
|
||||
#endregion
|
||||
|
||||
return archive;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled header on success, null on error</returns>
|
||||
private static Header ParseHeader(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
Header header = new Header();
|
||||
|
||||
header.HeaderSize = data.ReadUInt32();
|
||||
byte[] signature = data.ReadBytes(4);
|
||||
header.Signature = Encoding.ASCII.GetString(signature);
|
||||
header.NumberOfFiles = data.ReadUInt32();
|
||||
header.FileSegmentSize = data.ReadUInt32();
|
||||
switch (header.Signature)
|
||||
{
|
||||
case Version0SignatureString:
|
||||
if (header.FileSegmentSize != Version0HSegmentSize)
|
||||
return null;
|
||||
break;
|
||||
|
||||
case Version2SignatureString:
|
||||
if (header.FileSegmentSize != Version2SegmentSize)
|
||||
return null;
|
||||
break;
|
||||
|
||||
// Version 3 can sometimes have Version 2 segment sizes
|
||||
case Version3SignatureString:
|
||||
if (header.FileSegmentSize != Version2SegmentSize && header.FileSegmentSize != Version3SegmentSize)
|
||||
return null;
|
||||
break;
|
||||
|
||||
case Version4SignatureString:
|
||||
if (header.FileSegmentSize != Version4SegmentSize)
|
||||
return null;
|
||||
break;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
header.FileListOffset = data.ReadUInt32();
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a footer
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled footer on success, null on error</returns>
|
||||
private static Footer ParseFooter(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
Footer footer = new Footer();
|
||||
|
||||
footer.SystemIP = data.ReadUInt32();
|
||||
footer.Reserved = data.ReadUInt32();
|
||||
byte[] kingTag = data.ReadBytes(4);
|
||||
footer.KingTag = Encoding.ASCII.GetString(kingTag);
|
||||
|
||||
return footer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a file entry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="segmentSize">PFF segment size</param>
|
||||
/// <returns>Filled file entry on success, null on error</returns>
|
||||
private static Segment ParseSegment(Stream data, uint segmentSize)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
Segment segment = new Segment();
|
||||
|
||||
segment.Deleted = data.ReadUInt32();
|
||||
segment.FileLocation = data.ReadUInt32();
|
||||
segment.FileSize = data.ReadUInt32();
|
||||
segment.PackedDate = data.ReadUInt32();
|
||||
byte[] fileName = data.ReadBytes(0x10);
|
||||
segment.FileName = Encoding.ASCII.GetString(fileName).TrimEnd('\0');
|
||||
if (segmentSize > Version2SegmentSize)
|
||||
segment.ModifiedDate = data.ReadUInt32();
|
||||
if (segmentSize > Version3SegmentSize)
|
||||
segment.CompressionLevel = data.ReadUInt32();
|
||||
|
||||
return segment;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
463
BinaryObjectScanner.Builders/PlayJ.cs
Normal file
463
BinaryObjectScanner.Builders/PlayJ.cs
Normal file
@@ -0,0 +1,463 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BinaryObjectScanner.Models.PlayJ;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
using static BinaryObjectScanner.Models.PlayJ.Constants;
|
||||
|
||||
namespace BinaryObjectScanner.Builders
|
||||
{
|
||||
public class PlayJ
|
||||
{
|
||||
#region Byte Data
|
||||
|
||||
/// <summary>
|
||||
/// Parse a byte array into a PlayJ playlist
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array to parse</param>
|
||||
/// <param name="offset">Offset into the byte array</param>
|
||||
/// <returns>Filled playlist on success, null on error</returns>
|
||||
public static Playlist ParsePlaylist(byte[] data, int offset)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
// If the offset is out of bounds
|
||||
if (offset < 0 || offset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Create a memory stream and parse that
|
||||
MemoryStream dataStream = new MemoryStream(data, offset, data.Length - offset);
|
||||
return ParsePlaylist(dataStream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a byte array into a PlayJ audio file
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array to parse</param>
|
||||
/// <param name="offset">Offset into the byte array</param>
|
||||
/// <returns>Filled audio file on success, null on error</returns>
|
||||
public static AudioFile ParseAudioFile(byte[] data, int offset)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
// If the offset is out of bounds
|
||||
if (offset < 0 || offset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Create a memory stream and parse that
|
||||
MemoryStream dataStream = new MemoryStream(data, offset, data.Length - offset);
|
||||
return ParseAudioFile(dataStream);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Stream Data
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a PlayJ playlist
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled playlist on success, null on error</returns>
|
||||
public static Playlist ParsePlaylist(Stream data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
|
||||
return null;
|
||||
|
||||
// If the offset is out of bounds
|
||||
if (data.Position < 0 || data.Position >= data.Length)
|
||||
return null;
|
||||
|
||||
// Cache the current offset
|
||||
int initialOffset = (int)data.Position;
|
||||
|
||||
// Create a new playlist to fill
|
||||
var playlist = new Playlist();
|
||||
|
||||
#region Playlist Header
|
||||
|
||||
// Try to parse the playlist header
|
||||
var playlistHeader = ParsePlaylistHeader(data);
|
||||
if (playlistHeader == null)
|
||||
return null;
|
||||
|
||||
// Set the playlist header
|
||||
playlist.Header = playlistHeader;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Audio Files
|
||||
|
||||
// Create the audio files array
|
||||
playlist.AudioFiles = new AudioFile[playlistHeader.TrackCount];
|
||||
|
||||
// Try to parse the audio files
|
||||
for (int i = 0; i < playlist.AudioFiles.Length; i++)
|
||||
{
|
||||
long currentOffset = data.Position;
|
||||
var entryHeader = ParseAudioFile(data, currentOffset);
|
||||
if (entryHeader == null)
|
||||
return null;
|
||||
|
||||
playlist.AudioFiles[i] = entryHeader;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
return playlist;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a PlayJ audio file
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="adjust">Offset to adjust all seeking by</param>
|
||||
/// <returns>Filled audio file on success, null on error</returns>
|
||||
public static AudioFile ParseAudioFile(Stream data, long adjust = 0)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
|
||||
return null;
|
||||
|
||||
// If the offset is out of bounds
|
||||
if (data.Position < 0 || data.Position >= data.Length)
|
||||
return null;
|
||||
|
||||
// Cache the current offset
|
||||
int initialOffset = (int)data.Position;
|
||||
|
||||
// Create a new audio file to fill
|
||||
var audioFile = new AudioFile();
|
||||
|
||||
#region Audio Header
|
||||
|
||||
// Try to parse the audio header
|
||||
var audioHeader = ParseAudioHeader(data);
|
||||
if (audioHeader == null)
|
||||
return null;
|
||||
|
||||
// Set the audio header
|
||||
audioFile.Header = audioHeader;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unknown Block 1
|
||||
|
||||
uint unknownOffset1 = (audioHeader.Version == 0x00000000)
|
||||
? (audioHeader as AudioHeaderV1).UnknownOffset1
|
||||
: (audioHeader as AudioHeaderV2).UnknownOffset1 + 0x54;
|
||||
|
||||
// If we have an unknown block 1 offset
|
||||
if (unknownOffset1 > 0)
|
||||
{
|
||||
// Get the unknown block 1 offset
|
||||
long offset = unknownOffset1 + adjust;
|
||||
if (offset < 0 || offset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Seek to the unknown block 1
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
// Try to parse the unknown block 1
|
||||
var unknownBlock1 = ParseUnknownBlock1(data);
|
||||
if (unknownBlock1 == null)
|
||||
return null;
|
||||
|
||||
// Set the unknown block 1
|
||||
audioFile.UnknownBlock1 = unknownBlock1;
|
||||
|
||||
#endregion
|
||||
|
||||
#region V1 Only
|
||||
|
||||
// If we have a V1 file
|
||||
if (audioHeader.Version == 0x00000000)
|
||||
{
|
||||
#region Unknown Value 2
|
||||
|
||||
// Get the V1 unknown offset 2
|
||||
uint? unknownOffset2 = (audioHeader as AudioHeaderV1)?.UnknownOffset2;
|
||||
|
||||
// If we have an unknown value 2 offset
|
||||
if (unknownOffset2 != null && unknownOffset2 > 0)
|
||||
{
|
||||
// Get the unknown value 2 offset
|
||||
long offset = unknownOffset2.Value + adjust;
|
||||
if (offset < 0 || offset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Seek to the unknown value 2
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
// Set the unknown value 2
|
||||
audioFile.UnknownValue2 = data.ReadUInt32();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unknown Block 3
|
||||
|
||||
// Get the V1 unknown offset 3
|
||||
uint? unknownOffset3 = (audioHeader as AudioHeaderV1)?.UnknownOffset3;
|
||||
|
||||
// If we have an unknown block 3 offset
|
||||
if (unknownOffset3 != null && unknownOffset3 > 0)
|
||||
{
|
||||
// Get the unknown block 3 offset
|
||||
long offset = unknownOffset3.Value + adjust;
|
||||
if (offset < 0 || offset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Seek to the unknown block 3
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
// Try to parse the unknown block 3
|
||||
var unknownBlock3 = ParseUnknownBlock3(data);
|
||||
if (unknownBlock3 == null)
|
||||
return null;
|
||||
|
||||
// Set the unknown block 3
|
||||
audioFile.UnknownBlock3 = unknownBlock3;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region V2 Only
|
||||
|
||||
// If we have a V2 file
|
||||
if (audioHeader.Version == 0x0000000A)
|
||||
{
|
||||
#region Data Files Count
|
||||
|
||||
// Set the data files count
|
||||
audioFile.DataFilesCount = data.ReadUInt32();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Data Files
|
||||
|
||||
// Create the data files array
|
||||
audioFile.DataFiles = new DataFile[audioFile.DataFilesCount];
|
||||
|
||||
// Try to parse the data files
|
||||
for (int i = 0; i < audioFile.DataFiles.Length; i++)
|
||||
{
|
||||
var dataFile = ParseDataFile(data);
|
||||
if (dataFile == null)
|
||||
return null;
|
||||
|
||||
audioFile.DataFiles[i] = dataFile;
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
return audioFile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a playlist header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled playlist header on success, null on error</returns>
|
||||
private static PlaylistHeader ParsePlaylistHeader(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
PlaylistHeader playlistHeader = new PlaylistHeader();
|
||||
|
||||
playlistHeader.TrackCount = data.ReadUInt32();
|
||||
playlistHeader.Data = data.ReadBytes(52);
|
||||
|
||||
return playlistHeader;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an audio header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled audio header on success, null on error</returns>
|
||||
private static AudioHeader ParseAudioHeader(Stream data)
|
||||
{
|
||||
// Cache the current offset
|
||||
long initialOffset = data.Position;
|
||||
|
||||
// TODO: Use marshalling here instead of building
|
||||
AudioHeader audioHeader;
|
||||
|
||||
// Get the common header pieces
|
||||
uint signature = data.ReadUInt32();
|
||||
if (signature != SignatureUInt32)
|
||||
return null;
|
||||
|
||||
uint version = data.ReadUInt32();
|
||||
|
||||
// Build the header according to version
|
||||
uint unknownOffset1;
|
||||
switch (version)
|
||||
{
|
||||
// Version 1
|
||||
case 0x00000000:
|
||||
AudioHeaderV1 v1 = new AudioHeaderV1();
|
||||
|
||||
v1.Signature = signature;
|
||||
v1.Version = version;
|
||||
v1.TrackID = data.ReadUInt32();
|
||||
v1.UnknownOffset1 = data.ReadUInt32();
|
||||
v1.UnknownOffset2 = data.ReadUInt32();
|
||||
v1.UnknownOffset3 = data.ReadUInt32();
|
||||
v1.Unknown1 = data.ReadUInt32();
|
||||
v1.Unknown2 = data.ReadUInt32();
|
||||
v1.Year = data.ReadUInt32();
|
||||
v1.TrackNumber = data.ReadByteValue();
|
||||
v1.Subgenre = (Subgenre)data.ReadByteValue();
|
||||
v1.Duration = data.ReadUInt32();
|
||||
|
||||
audioHeader = v1;
|
||||
unknownOffset1 = v1.UnknownOffset1;
|
||||
break;
|
||||
|
||||
// Version 2
|
||||
case 0x0000000A:
|
||||
AudioHeaderV2 v2 = new AudioHeaderV2();
|
||||
|
||||
v2.Signature = signature;
|
||||
v2.Version = version;
|
||||
v2.Unknown1 = data.ReadUInt32();
|
||||
v2.Unknown2 = data.ReadUInt32();
|
||||
v2.Unknown3 = data.ReadUInt32();
|
||||
v2.Unknown4 = data.ReadUInt32();
|
||||
v2.Unknown5 = data.ReadUInt32();
|
||||
v2.Unknown6 = data.ReadUInt32();
|
||||
v2.UnknownOffset1 = data.ReadUInt32();
|
||||
v2.Unknown7 = data.ReadUInt32();
|
||||
v2.Unknown8 = data.ReadUInt32();
|
||||
v2.Unknown9 = data.ReadUInt32();
|
||||
v2.UnknownOffset2 = data.ReadUInt32();
|
||||
v2.Unknown10 = data.ReadUInt32();
|
||||
v2.Unknown11 = data.ReadUInt32();
|
||||
v2.Unknown12 = data.ReadUInt32();
|
||||
v2.Unknown13 = data.ReadUInt32();
|
||||
v2.Unknown14 = data.ReadUInt32();
|
||||
v2.Unknown15 = data.ReadUInt32();
|
||||
v2.Unknown16 = data.ReadUInt32();
|
||||
v2.Unknown17 = data.ReadUInt32();
|
||||
v2.TrackID = data.ReadUInt32();
|
||||
v2.Year = data.ReadUInt32();
|
||||
v2.TrackNumber = data.ReadUInt32();
|
||||
v2.Unknown18 = data.ReadUInt32();
|
||||
|
||||
audioHeader = v2;
|
||||
unknownOffset1 = v2.UnknownOffset1 + 0x54;
|
||||
break;
|
||||
|
||||
// No other version are recognized
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
audioHeader.TrackLength = data.ReadUInt16();
|
||||
byte[] track = data.ReadBytes(audioHeader.TrackLength);
|
||||
if (track != null)
|
||||
audioHeader.Track = Encoding.ASCII.GetString(track);
|
||||
|
||||
audioHeader.ArtistLength = data.ReadUInt16();
|
||||
byte[] artist = data.ReadBytes(audioHeader.ArtistLength);
|
||||
if (artist != null)
|
||||
audioHeader.Artist = Encoding.ASCII.GetString(artist);
|
||||
|
||||
audioHeader.AlbumLength = data.ReadUInt16();
|
||||
byte[] album = data.ReadBytes(audioHeader.AlbumLength);
|
||||
if (album != null)
|
||||
audioHeader.Album = Encoding.ASCII.GetString(album);
|
||||
|
||||
audioHeader.WriterLength = data.ReadUInt16();
|
||||
byte[] writer = data.ReadBytes(audioHeader.WriterLength);
|
||||
if (writer != null)
|
||||
audioHeader.Writer = Encoding.ASCII.GetString(writer);
|
||||
|
||||
audioHeader.PublisherLength = data.ReadUInt16();
|
||||
byte[] publisher = data.ReadBytes(audioHeader.PublisherLength);
|
||||
if (publisher != null)
|
||||
audioHeader.Publisher = Encoding.ASCII.GetString(publisher);
|
||||
|
||||
audioHeader.LabelLength = data.ReadUInt16();
|
||||
byte[] label = data.ReadBytes(audioHeader.LabelLength);
|
||||
if (label != null)
|
||||
audioHeader.Label = Encoding.ASCII.GetString(label);
|
||||
|
||||
if (data.Position - initialOffset < unknownOffset1)
|
||||
{
|
||||
audioHeader.CommentsLength = data.ReadUInt16();
|
||||
byte[] comments = data.ReadBytes(audioHeader.CommentsLength);
|
||||
if (comments != null)
|
||||
audioHeader.Comments = Encoding.ASCII.GetString(comments);
|
||||
}
|
||||
|
||||
return audioHeader;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an unknown block 1
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled unknown block 1 on success, null on error</returns>
|
||||
private static UnknownBlock1 ParseUnknownBlock1(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
UnknownBlock1 unknownBlock1 = new UnknownBlock1();
|
||||
|
||||
unknownBlock1.Length = data.ReadUInt32();
|
||||
unknownBlock1.Data = data.ReadBytes((int)unknownBlock1.Length);
|
||||
|
||||
return unknownBlock1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an unknown block 3
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled unknown block 3 on success, null on error</returns>
|
||||
private static UnknownBlock3 ParseUnknownBlock3(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
UnknownBlock3 unknownBlock3 = new UnknownBlock3();
|
||||
|
||||
// No-op because we don't even know the length
|
||||
|
||||
return unknownBlock3;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a data file
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled data file on success, null on error</returns>
|
||||
private static DataFile ParseDataFile(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
DataFile dataFile = new DataFile();
|
||||
|
||||
dataFile.FileNameLength = data.ReadUInt16();
|
||||
byte[] fileName = data.ReadBytes(dataFile.FileNameLength);
|
||||
if (fileName != null)
|
||||
dataFile.FileName = Encoding.ASCII.GetString(fileName);
|
||||
|
||||
dataFile.DataLength = data.ReadUInt32();
|
||||
dataFile.Data = data.ReadBytes((int)dataFile.DataLength);
|
||||
|
||||
return dataFile;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,11 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using BurnOutSharp.Models.PortableExecutable;
|
||||
using BurnOutSharp.Utilities;
|
||||
using static BurnOutSharp.Models.PortableExecutable.Constants;
|
||||
using BinaryObjectScanner.Models.PortableExecutable;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
using static BinaryObjectScanner.Models.PortableExecutable.Constants;
|
||||
|
||||
namespace BurnOutSharp.Builders
|
||||
namespace BinaryObjectScanner.Builders
|
||||
{
|
||||
public static class PortableExecutable
|
||||
{
|
||||
@@ -1,10 +1,10 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BurnOutSharp.Models.Quantum;
|
||||
using BurnOutSharp.Utilities;
|
||||
using static BurnOutSharp.Models.Quantum.Constants;
|
||||
using BinaryObjectScanner.Models.Quantum;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
using static BinaryObjectScanner.Models.Quantum.Constants;
|
||||
|
||||
namespace BurnOutSharp.Builders
|
||||
namespace BinaryObjectScanner.Builders
|
||||
{
|
||||
public class Quantum
|
||||
{
|
||||
@@ -1,11 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BurnOutSharp.Models.SGA;
|
||||
using BurnOutSharp.Utilities;
|
||||
using static BurnOutSharp.Models.SGA.Constants;
|
||||
using BinaryObjectScanner.Models.SGA;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
using static BinaryObjectScanner.Models.SGA.Constants;
|
||||
|
||||
namespace BurnOutSharp.Builders
|
||||
namespace BinaryObjectScanner.Builders
|
||||
{
|
||||
public static class SGA
|
||||
{
|
||||
@@ -1,10 +1,10 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BurnOutSharp.Models.VBSP;
|
||||
using BurnOutSharp.Utilities;
|
||||
using static BurnOutSharp.Models.VBSP.Constants;
|
||||
using BinaryObjectScanner.Models.VBSP;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
using static BinaryObjectScanner.Models.VBSP.Constants;
|
||||
|
||||
namespace BurnOutSharp.Builders
|
||||
namespace BinaryObjectScanner.Builders
|
||||
{
|
||||
public static class VBSP
|
||||
{
|
||||
@@ -1,11 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BurnOutSharp.Models.VPK;
|
||||
using BurnOutSharp.Utilities;
|
||||
using static BurnOutSharp.Models.VPK.Constants;
|
||||
using BinaryObjectScanner.Models.VPK;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
using static BinaryObjectScanner.Models.VPK.Constants;
|
||||
|
||||
namespace BurnOutSharp.Builders
|
||||
namespace BinaryObjectScanner.Builders
|
||||
{
|
||||
public static class VPK
|
||||
{
|
||||
@@ -1,10 +1,10 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BurnOutSharp.Models.WAD;
|
||||
using BurnOutSharp.Utilities;
|
||||
using static BurnOutSharp.Models.WAD.Constants;
|
||||
using BinaryObjectScanner.Models.WAD;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
using static BinaryObjectScanner.Models.WAD.Constants;
|
||||
|
||||
namespace BurnOutSharp.Builders
|
||||
namespace BinaryObjectScanner.Builders
|
||||
{
|
||||
public static class WAD
|
||||
{
|
||||
@@ -1,10 +1,10 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BurnOutSharp.Models.XZP;
|
||||
using BurnOutSharp.Utilities;
|
||||
using static BurnOutSharp.Models.XZP.Constants;
|
||||
using BinaryObjectScanner.Models.XZP;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
using static BinaryObjectScanner.Models.XZP.Constants;
|
||||
|
||||
namespace BurnOutSharp.Builders
|
||||
namespace BinaryObjectScanner.Builders
|
||||
{
|
||||
public static class XZP
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace BurnOutSharp.Compression.ADPCM
|
||||
namespace BinaryObjectScanner.Compression.ADPCM
|
||||
{
|
||||
/// <see href="https://github.com/ladislav-zezula/StormLib/blob/master/src/adpcm/adpcm.cpp"/>
|
||||
public unsafe struct ADPCM_DATA
|
||||
@@ -1,7 +1,7 @@
|
||||
using static BurnOutSharp.Compression.ADPCM.Constants;
|
||||
using static BurnOutSharp.Compression.ADPCM.Helper;
|
||||
using static BinaryObjectScanner.Compression.ADPCM.Constants;
|
||||
using static BinaryObjectScanner.Compression.ADPCM.Helper;
|
||||
|
||||
namespace BurnOutSharp.Compression.ADPCM
|
||||
namespace BinaryObjectScanner.Compression.ADPCM
|
||||
{
|
||||
public unsafe class Compressor
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace BurnOutSharp.Compression.ADPCM
|
||||
namespace BinaryObjectScanner.Compression.ADPCM
|
||||
{
|
||||
/// <see href="https://github.com/ladislav-zezula/StormLib/blob/master/src/adpcm/adpcm.h"/>
|
||||
public static class Constants
|
||||
@@ -1,7 +1,7 @@
|
||||
using static BurnOutSharp.Compression.ADPCM.Constants;
|
||||
using static BurnOutSharp.Compression.ADPCM.Helper;
|
||||
using static BinaryObjectScanner.Compression.ADPCM.Constants;
|
||||
using static BinaryObjectScanner.Compression.ADPCM.Helper;
|
||||
|
||||
namespace BurnOutSharp.Compression.ADPCM
|
||||
namespace BinaryObjectScanner.Compression.ADPCM
|
||||
{
|
||||
public unsafe class Decompressor
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using static BurnOutSharp.Compression.ADPCM.Constants;
|
||||
using static BinaryObjectScanner.Compression.ADPCM.Constants;
|
||||
|
||||
namespace BurnOutSharp.Compression.ADPCM
|
||||
namespace BinaryObjectScanner.Compression.ADPCM
|
||||
{
|
||||
/// <see href="https://github.com/ladislav-zezula/StormLib/blob/master/src/adpcm/adpcm.cpp"/>
|
||||
internal static unsafe class Helper
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace BurnOutSharp.Compression.ADPCM
|
||||
namespace BinaryObjectScanner.Compression.ADPCM
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class for writing output ADPCM data
|
||||
@@ -0,0 +1,43 @@
|
||||
<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.Compression</Title>
|
||||
<AssemblyName>BinaryObjectScanner.Compression</AssemblyName>
|
||||
<Authors>Matt Nadareski</Authors>
|
||||
<Product>BurnOutSharp</Product>
|
||||
<Copyright>Copyright (c)2022 Matt Nadareski</Copyright>
|
||||
<RepositoryUrl>https://github.com/mnadareski/BurnOutSharp</RepositoryUrl>
|
||||
<Version>2.8</Version>
|
||||
<AssemblyVersion>2.8</AssemblyVersion>
|
||||
<FileVersion>2.8</FileVersion>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- These are needed for dealing with submodules -->
|
||||
<PropertyGroup>
|
||||
<DefaultItemExcludes>
|
||||
$(DefaultItemExcludes);
|
||||
**\AssemblyInfo.cs;
|
||||
External\stormlibsharp\lib\**;
|
||||
External\stormlibsharp\TestConsole\**
|
||||
</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SharpCompress" Version="0.32.2" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.4.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BinaryObjectScanner.Models\BinaryObjectScanner.Models.csproj" />
|
||||
<ProjectReference Include="..\BinaryObjectScanner.Utilities\BinaryObjectScanner.Utilities.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,11 +1,11 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using BurnOutSharp.Models.Compression.LZ;
|
||||
using BurnOutSharp.Utilities;
|
||||
using static BurnOutSharp.Models.Compression.LZ.Constants;
|
||||
using BinaryObjectScanner.Models.Compression.LZ;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
using static BinaryObjectScanner.Models.Compression.LZ.Constants;
|
||||
|
||||
namespace BurnOutSharp.Compression
|
||||
namespace BinaryObjectScanner.Compression
|
||||
{
|
||||
/// <see href="https://github.com/wine-mirror/wine/blob/master/dlls/kernel32/lzexpand.c"/>
|
||||
public class LZ
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace BurnOutSharp.Compression.LZX
|
||||
namespace BinaryObjectScanner.Compression.LZX
|
||||
{
|
||||
/// <see href="https://github.com/wine-mirror/wine/blob/master/dlls/cabinet/cabinet.h"/>
|
||||
public class Bits
|
||||
@@ -1,9 +1,9 @@
|
||||
using System;
|
||||
using BurnOutSharp.Models.Compression.LZX;
|
||||
using static BurnOutSharp.Models.Compression.LZX.Constants;
|
||||
using static BurnOutSharp.Models.MicrosoftCabinet.Constants;
|
||||
using BinaryObjectScanner.Compression.LZX;
|
||||
using static BinaryObjectScanner.Models.Compression.LZX.Constants;
|
||||
using static BinaryObjectScanner.Models.MicrosoftCabinet.Constants;
|
||||
|
||||
namespace BurnOutSharp.Compression.LZX
|
||||
namespace BinaryObjectScanner.Compression.LZX
|
||||
{
|
||||
/// <see href="https://github.com/wine-mirror/wine/blob/master/dlls/cabinet/fdi.c"/>
|
||||
public class Decompressor
|
||||
@@ -1,6 +1,6 @@
|
||||
using static BurnOutSharp.Models.Compression.LZX.Constants;
|
||||
using static BinaryObjectScanner.Models.Compression.LZX.Constants;
|
||||
|
||||
namespace BurnOutSharp.Compression.LZX
|
||||
namespace BinaryObjectScanner.Compression.LZX
|
||||
{
|
||||
/// <see href="https://github.com/wine-mirror/wine/blob/master/dlls/cabinet/cabinet.h"/>
|
||||
public class State
|
||||
@@ -1,9 +1,9 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using BurnOutSharp.Models.Compression.MSZIP;
|
||||
using static BurnOutSharp.Models.Compression.MSZIP.Constants;
|
||||
using BinaryObjectScanner.Models.Compression.MSZIP;
|
||||
using static BinaryObjectScanner.Models.Compression.MSZIP.Constants;
|
||||
|
||||
namespace BurnOutSharp.Compression.MSZIP
|
||||
namespace BinaryObjectScanner.Compression.MSZIP
|
||||
{
|
||||
/// <see href="https://github.com/wine-mirror/wine/blob/master/dlls/cabinet/fdi.c"/>
|
||||
public unsafe class Decompressor
|
||||
@@ -21,7 +21,7 @@ namespace BurnOutSharp.Compression.MSZIP
|
||||
return false;
|
||||
|
||||
// CK = Chris Kirmse, official Microsoft purloiner
|
||||
if (state.inpos[0] != 0x43 || state.inpos[1] != 0x48)
|
||||
if (state.inpos[0] != 0x43 || state.inpos[1] != 0x4B)
|
||||
return false;
|
||||
|
||||
state.inpos += 2;
|
||||
@@ -191,6 +191,7 @@ namespace BurnOutSharp.Compression.MSZIP
|
||||
b = state.bb;
|
||||
k = state.bk;
|
||||
|
||||
state.ll = new uint[288 + 32];
|
||||
fixed (uint* ll = state.ll)
|
||||
{
|
||||
/* read in table lengths */
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace BurnOutSharp.Compression.MSZIP
|
||||
namespace BinaryObjectScanner.Compression.MSZIP
|
||||
{
|
||||
public unsafe struct HuffmanNode
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using static BurnOutSharp.Models.Compression.MSZIP.Constants;
|
||||
using static BinaryObjectScanner.Models.Compression.MSZIP.Constants;
|
||||
|
||||
namespace BurnOutSharp.Compression.MSZIP
|
||||
namespace BinaryObjectScanner.Compression.MSZIP
|
||||
{
|
||||
/// <see href="https://github.com/wine-mirror/wine/blob/master/dlls/cabinet/cabinet.h"/>
|
||||
public unsafe class State
|
||||
@@ -1,9 +1,9 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using BurnOutSharp.Models.Compression.Quantum;
|
||||
using BurnOutSharp.Models.MicrosoftCabinet;
|
||||
using BinaryObjectScanner.Models.Compression.Quantum;
|
||||
using BinaryObjectScanner.Models.MicrosoftCabinet;
|
||||
|
||||
namespace BurnOutSharp.Compression.Quantum
|
||||
namespace BinaryObjectScanner.Compression.Quantum
|
||||
{
|
||||
/// <see href="https://github.com/wine-mirror/wine/blob/master/dlls/cabinet/cabinet.h"/>
|
||||
/// <see href="https://github.com/wine-mirror/wine/blob/master/dlls/cabinet/fdi.c"/>
|
||||
@@ -1,6 +1,6 @@
|
||||
using BurnOutSharp.Models.Compression.Quantum;
|
||||
using BinaryObjectScanner.Models.Compression.Quantum;
|
||||
|
||||
namespace BurnOutSharp.Compression.Quantum
|
||||
namespace BinaryObjectScanner.Compression.Quantum
|
||||
{
|
||||
/// <see href="https://github.com/kyz/libmspack/blob/master/libmspack/mspack/qtmd.c"/>
|
||||
/// <see href="https://github.com/wine-mirror/wine/blob/master/dlls/cabinet/cabinet.h"/>
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace BurnOutSharp.Compression.bzip2
|
||||
namespace BinaryObjectScanner.Compression.bzip2
|
||||
{
|
||||
/// <see href="https://github.com/ladislav-zezula/StormLib/blob/master/src/bzip2/bzlib.h"/>
|
||||
/// <see href="https://github.com/ladislav-zezula/StormLib/blob/master/src/bzip2/bzlib_private.h"/>
|
||||
@@ -1,6 +1,6 @@
|
||||
using static BurnOutSharp.Compression.bzip2.Constants;
|
||||
using static BinaryObjectScanner.Compression.bzip2.Constants;
|
||||
|
||||
namespace BurnOutSharp.Compression.bzip2
|
||||
namespace BinaryObjectScanner.Compression.bzip2
|
||||
{
|
||||
/// <summary>
|
||||
/// Structure holding all the decompression-side stuff.
|
||||
@@ -1,6 +1,6 @@
|
||||
using static BurnOutSharp.Compression.bzip2.Constants;
|
||||
using static BinaryObjectScanner.Compression.bzip2.Constants;
|
||||
|
||||
namespace BurnOutSharp.Compression.bzip2
|
||||
namespace BinaryObjectScanner.Compression.bzip2
|
||||
{
|
||||
/// <summary>
|
||||
/// Structure holding all the compression-side stuff.
|
||||
@@ -1,6 +1,6 @@
|
||||
using static BurnOutSharp.Compression.bzip2.Constants;
|
||||
using static BinaryObjectScanner.Compression.bzip2.Constants;
|
||||
|
||||
namespace BurnOutSharp.Compression.bzip2
|
||||
namespace BinaryObjectScanner.Compression.bzip2
|
||||
{
|
||||
/// <summary>
|
||||
/// Huffman coding low-level stuff
|
||||
@@ -1,6 +1,6 @@
|
||||
using static BurnOutSharp.Compression.bzip2.Constants;
|
||||
using static BinaryObjectScanner.Compression.bzip2.Constants;
|
||||
|
||||
namespace BurnOutSharp.Compression.bzip2
|
||||
namespace BinaryObjectScanner.Compression.bzip2
|
||||
{
|
||||
/// <summary>
|
||||
/// Block sorting machinery
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace BurnOutSharp.Compression.bzip2
|
||||
namespace BinaryObjectScanner.Compression.bzip2
|
||||
{
|
||||
/// <see href="https://github.com/ladislav-zezula/StormLib/blob/master/src/bzip2/bzlib.h"/>
|
||||
public unsafe struct bz_stream
|
||||
@@ -1,8 +1,8 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using static BurnOutSharp.Compression.bzip2.Constants;
|
||||
using static BurnOutSharp.Compression.bzip2.Huffman;
|
||||
using static BinaryObjectScanner.Compression.bzip2.Constants;
|
||||
using static BinaryObjectScanner.Compression.bzip2.Huffman;
|
||||
|
||||
namespace BurnOutSharp.Compression.bzip2
|
||||
namespace BinaryObjectScanner.Compression.bzip2
|
||||
{
|
||||
/// <see href="https://github.com/ladislav-zezula/StormLib/blob/master/src/bzip2/decompress.c"/>
|
||||
internal static unsafe class decompress
|
||||
51
BinaryObjectScanner.FileType/AACSMediaKeyBlock.cs
Normal file
51
BinaryObjectScanner.FileType/AACSMediaKeyBlock.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// AACS media key block
|
||||
/// </summary>
|
||||
public class AACSMediaKeyBlock : IDetectable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string Detect(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Detect(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
// If the MKB file itself fails
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
Wrappers.AACSMediaKeyBlock mkb = Wrappers.AACSMediaKeyBlock.Create(stream);
|
||||
if (mkb == null)
|
||||
return null;
|
||||
|
||||
// Derive the version, if possible
|
||||
var typeAndVersion = mkb.Records.FirstOrDefault(r => r.RecordType == Models.AACS.RecordType.TypeAndVersion);
|
||||
if (typeAndVersion == null)
|
||||
return "AACS (Unknown Version)";
|
||||
else
|
||||
return $"AACS {(typeAndVersion as Models.AACS.TypeAndVersionRecord).VersionNumber}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
49
BinaryObjectScanner.FileType/BDPlusSVM.cs
Normal file
49
BinaryObjectScanner.FileType/BDPlusSVM.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// BD+ SVM
|
||||
/// </summary>
|
||||
public class BDPlusSVM : IDetectable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string Detect(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Detect(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
// If the BD+ file itself fails
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
Wrappers.BDPlusSVM svm = Wrappers.BDPlusSVM.Create(stream);
|
||||
if (svm == null)
|
||||
return null;
|
||||
|
||||
// Format the date
|
||||
string date = $"{svm.Year:0000}/{svm.Month:00}/{svm.Day:00}";
|
||||
|
||||
// Return the formatted value
|
||||
return $"BD+ {date}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
50
BinaryObjectScanner.FileType/BFPK.cs
Normal file
50
BinaryObjectScanner.FileType/BFPK.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// BFPK custom archive format
|
||||
/// </summary>
|
||||
public class BFPK : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
Wrappers.BFPK bfpk = Wrappers.BFPK.Create(stream);
|
||||
if (bfpk == null)
|
||||
return null;
|
||||
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
// Extract all files
|
||||
bfpk.ExtractAll(tempPath);
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
51
BinaryObjectScanner.FileType/BSP.cs
Normal file
51
BinaryObjectScanner.FileType/BSP.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Half-Life Level
|
||||
/// </summary>
|
||||
public class BSP : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
Wrappers.BSP bsp = Wrappers.BSP.Create(stream);
|
||||
if (bsp == null)
|
||||
return null;
|
||||
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
// Loop through and extract all files
|
||||
bsp.ExtractAllLumps(tempPath);
|
||||
bsp.ExtractAllTextures(tempPath);
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
53
BinaryObjectScanner.FileType/BZip2.cs
Normal file
53
BinaryObjectScanner.FileType/BZip2.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SharpCompress.Compressors;
|
||||
using SharpCompress.Compressors.BZip2;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// bzip2 archive
|
||||
/// </summary>
|
||||
public class BZip2 : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
using (BZip2Stream bz2File = new BZip2Stream(stream, CompressionMode.Decompress, true))
|
||||
{
|
||||
string tempFile = Path.Combine(tempPath, Guid.NewGuid().ToString());
|
||||
using (FileStream fs = File.OpenWrite(tempFile))
|
||||
{
|
||||
bz2File.CopyTo(fs);
|
||||
}
|
||||
}
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<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.FileType</Title>
|
||||
<AssemblyName>BinaryObjectScanner.FileType</AssemblyName>
|
||||
<Authors>Matt Nadareski</Authors>
|
||||
<Product>BurnOutSharp</Product>
|
||||
<Copyright>Copyright (c)2022 Matt Nadareski</Copyright>
|
||||
<RepositoryUrl>https://github.com/mnadareski/BurnOutSharp</RepositoryUrl>
|
||||
<Version>2.8</Version>
|
||||
<AssemblyVersion>2.8</AssemblyVersion>
|
||||
<FileVersion>2.8</FileVersion>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<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" />
|
||||
<ProjectReference Include="..\BinaryObjectScanner.Protection\BinaryObjectScanner.Protection.csproj" />
|
||||
<ProjectReference Include="..\BinaryObjectScanner.Wrappers\BinaryObjectScanner.Wrappers.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="OpenMcdf" Version="2.2.1.12" />
|
||||
<PackageReference Include="SharpCompress" Version="0.32.2" />
|
||||
<PackageReference Include="UnshieldSharp" Version="1.6.9" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,37 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BurnOutSharp.Interfaces;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using OpenMcdf;
|
||||
using static BurnOutSharp.Utilities.Dictionary;
|
||||
|
||||
namespace BurnOutSharp.FileType
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Microsoft installation package
|
||||
/// Compound File Binary
|
||||
/// </summary>
|
||||
public class MSI : IScannable
|
||||
public class CFB : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
public string Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add stream opening support
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
// If the MSI file itself fails
|
||||
try
|
||||
{
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
@@ -39,61 +36,51 @@ namespace BurnOutSharp.FileType
|
||||
{
|
||||
msi.RootStorage.VisitEntries((e) =>
|
||||
{
|
||||
if (!e.IsStream)
|
||||
return;
|
||||
|
||||
var str = msi.RootStorage.GetStream(e.Name);
|
||||
if (str == null)
|
||||
return;
|
||||
|
||||
byte[] strData = str.GetData();
|
||||
if (strData == null)
|
||||
return;
|
||||
|
||||
string decoded = DecodeStreamName(e.Name).TrimEnd('\0');
|
||||
byte[] nameBytes = Encoding.UTF8.GetBytes(e.Name);
|
||||
|
||||
// UTF-8 encoding of 0x4840.
|
||||
if (nameBytes[0] == 0xe4 && nameBytes[1] == 0xa1 && nameBytes[2] == 0x80)
|
||||
decoded = decoded.Substring(3);
|
||||
|
||||
foreach (char c in Path.GetInvalidFileNameChars())
|
||||
try
|
||||
{
|
||||
decoded = decoded.Replace(c, '_');
|
||||
if (!e.IsStream)
|
||||
return;
|
||||
|
||||
var str = msi.RootStorage.GetStream(e.Name);
|
||||
if (str == null)
|
||||
return;
|
||||
|
||||
byte[] strData = str.GetData();
|
||||
if (strData == null)
|
||||
return;
|
||||
|
||||
string decoded = DecodeStreamName(e.Name).TrimEnd('\0');
|
||||
byte[] nameBytes = Encoding.UTF8.GetBytes(e.Name);
|
||||
|
||||
// UTF-8 encoding of 0x4840.
|
||||
if (nameBytes[0] == 0xe4 && nameBytes[1] == 0xa1 && nameBytes[2] == 0x80)
|
||||
decoded = decoded.Substring(3);
|
||||
|
||||
foreach (char c in Path.GetInvalidFileNameChars())
|
||||
{
|
||||
decoded = decoded.Replace(c, '_');
|
||||
}
|
||||
|
||||
string filename = Path.Combine(tempPath, decoded);
|
||||
using (Stream fs = File.OpenWrite(filename))
|
||||
{
|
||||
fs.Write(strData, 0, strData.Length);
|
||||
}
|
||||
}
|
||||
|
||||
string filename = Path.Combine(tempPath, decoded);
|
||||
using (Stream fs = File.OpenWrite(filename))
|
||||
catch (Exception ex)
|
||||
{
|
||||
fs.Write(strData, 0, strData.Length);
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}, recursive: true);
|
||||
}
|
||||
|
||||
// Collect and format all found protections
|
||||
var protections = scanner.GetProtections(tempPath);
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
// Remove temporary path references
|
||||
StripFromKeys(protections, tempPath);
|
||||
|
||||
return protections;
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <remarks>Adapted from LibMSI</remarks>
|
||||
437
BinaryObjectScanner.FileType/Executable.cs
Normal file
437
BinaryObjectScanner.FileType/Executable.cs
Normal file
@@ -0,0 +1,437 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using BinaryObjectScanner.Utilities;
|
||||
using BinaryObjectScanner.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Executable or library
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Due to the complexity of executables, all extraction handling
|
||||
/// another class that is used by the scanner
|
||||
/// </remarks>
|
||||
public class Executable : IDetectable
|
||||
{
|
||||
#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>
|
||||
public bool IncludePackers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IContentCheck types
|
||||
/// </summary>
|
||||
public static IEnumerable<IContentCheck> ContentCheckClasses
|
||||
{
|
||||
get
|
||||
{
|
||||
if (contentCheckClasses == null)
|
||||
contentCheckClasses = InitCheckClasses<IContentCheck>();
|
||||
|
||||
return contentCheckClasses;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all ILinearExecutableCheck types
|
||||
/// </summary>
|
||||
public static IEnumerable<ILinearExecutableCheck> LinearExecutableCheckClasses
|
||||
{
|
||||
get
|
||||
{
|
||||
if (linearExecutableCheckClasses == null)
|
||||
linearExecutableCheckClasses = InitCheckClasses<ILinearExecutableCheck>();
|
||||
|
||||
return linearExecutableCheckClasses;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IMSDOSExecutableCheck types
|
||||
/// </summary>
|
||||
public static IEnumerable<IMSDOSExecutableCheck> MSDOSExecutableCheckClasses
|
||||
{
|
||||
get
|
||||
{
|
||||
if (msdosExecutableCheckClasses == null)
|
||||
msdosExecutableCheckClasses = InitCheckClasses<IMSDOSExecutableCheck>();
|
||||
|
||||
return msdosExecutableCheckClasses;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all INewExecutableCheck types
|
||||
/// </summary>
|
||||
public static IEnumerable<INewExecutableCheck> NewExecutableCheckClasses
|
||||
{
|
||||
get
|
||||
{
|
||||
if (newExecutableCheckClasses == null)
|
||||
newExecutableCheckClasses = InitCheckClasses<INewExecutableCheck>();
|
||||
|
||||
return newExecutableCheckClasses;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IPortableExecutableCheck types
|
||||
/// </summary>
|
||||
public static IEnumerable<IPortableExecutableCheck> PortableExecutableCheckClasses
|
||||
{
|
||||
get
|
||||
{
|
||||
if (portableExecutableCheckClasses == null)
|
||||
portableExecutableCheckClasses = InitCheckClasses<IPortableExecutableCheck>();
|
||||
|
||||
return portableExecutableCheckClasses;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Instances
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IContentCheck types
|
||||
/// </summary>
|
||||
private static IEnumerable<IContentCheck> contentCheckClasses;
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all ILinearExecutableCheck types
|
||||
/// </summary>
|
||||
private static IEnumerable<ILinearExecutableCheck> linearExecutableCheckClasses;
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IMSDOSExecutableCheck types
|
||||
/// </summary>
|
||||
private static IEnumerable<IMSDOSExecutableCheck> msdosExecutableCheckClasses;
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all INewExecutableCheck types
|
||||
/// </summary>
|
||||
private static IEnumerable<INewExecutableCheck> newExecutableCheckClasses;
|
||||
|
||||
/// <summary>
|
||||
/// Cache for all IPortableExecutableCheck types
|
||||
/// </summary>
|
||||
private static IEnumerable<IPortableExecutableCheck> portableExecutableCheckClasses;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Detect(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Detect(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
// Try to create a wrapper for the proper executable type
|
||||
var wrapper = WrapperFactory.CreateExecutableWrapper(stream);
|
||||
if (wrapper == null)
|
||||
return null;
|
||||
|
||||
// Create the internal queue
|
||||
var protections = new ConcurrentQueue<string>();
|
||||
|
||||
// Only use generic content checks if we're in debug mode
|
||||
if (includeDebug)
|
||||
{
|
||||
var subProtections = RunContentChecks(file, stream, includeDebug);
|
||||
if (subProtections != null)
|
||||
protections.AddRange(subProtections.Values.ToArray());
|
||||
}
|
||||
|
||||
if (wrapper is MSDOS mz)
|
||||
{
|
||||
var subProtections = RunMSDOSExecutableChecks(file, stream, mz, includeDebug);
|
||||
if (subProtections != null)
|
||||
protections.AddRange(subProtections.Values.ToArray());
|
||||
}
|
||||
else if (wrapper is LinearExecutable lex)
|
||||
{
|
||||
var subProtections = RunLinearExecutableChecks(file, stream, lex, includeDebug);
|
||||
if (subProtections != null)
|
||||
protections.AddRange(subProtections.Values.ToArray());
|
||||
}
|
||||
else if (wrapper is NewExecutable nex)
|
||||
{
|
||||
var subProtections = RunNewExecutableChecks(file, stream, nex, includeDebug);
|
||||
if (subProtections != null)
|
||||
protections.AddRange(subProtections.Values.ToArray());
|
||||
}
|
||||
else if (wrapper is PortableExecutable pex)
|
||||
{
|
||||
var subProtections = RunPortableExecutableChecks(file, stream, pex, includeDebug);
|
||||
if (subProtections != null)
|
||||
protections.AddRange(subProtections.Values.ToArray());
|
||||
}
|
||||
|
||||
return string.Join(";", protections);
|
||||
}
|
||||
|
||||
#region Check Runners
|
||||
|
||||
/// <summary>
|
||||
/// Handle a single file based on all content check implementations
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the source file of the stream, for tracking</param>
|
||||
/// <param name="stream">Stream to scan the contents of</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Set of protections in file, null on error</returns>
|
||||
public ConcurrentDictionary<IContentCheck, string> RunContentChecks(string file, Stream stream, bool includeDebug)
|
||||
{
|
||||
// If we have an invalid file
|
||||
if (string.IsNullOrWhiteSpace(file))
|
||||
return null;
|
||||
else if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
// Read the file contents
|
||||
byte[] fileContent = null;
|
||||
try
|
||||
{
|
||||
using (BinaryReader br = new BinaryReader(stream, Encoding.Default, true))
|
||||
{
|
||||
fileContent = br.ReadBytes((int)stream.Length);
|
||||
if (fileContent == null)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create the output dictionary
|
||||
var protections = new ConcurrentDictionary<IContentCheck, string>();
|
||||
|
||||
// Iterate through all checks
|
||||
Parallel.ForEach(ContentCheckClasses, checkClass =>
|
||||
{
|
||||
// Get the protection for the class, if possible
|
||||
string protection = checkClass.CheckContents(file, fileContent, includeDebug);
|
||||
if (string.IsNullOrWhiteSpace(protection))
|
||||
return;
|
||||
|
||||
// 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);
|
||||
});
|
||||
|
||||
return protections;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle a single file based on all linear executable check implementations
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the source file of the executable, for tracking</param>
|
||||
/// <param name="lex">Executable to scan</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Set of protections in file, null on error</returns>
|
||||
public ConcurrentDictionary<ILinearExecutableCheck, string> RunLinearExecutableChecks(string file, Stream stream, LinearExecutable lex, bool includeDebug)
|
||||
{
|
||||
// Create the output dictionary
|
||||
var protections = new ConcurrentDictionary<ILinearExecutableCheck, string>();
|
||||
|
||||
// Iterate through all checks
|
||||
Parallel.ForEach(LinearExecutableCheckClasses, checkClass =>
|
||||
{
|
||||
// Get the protection for the class, if possible
|
||||
string protection = checkClass.CheckLinearExecutable(file, lex, includeDebug);
|
||||
if (string.IsNullOrWhiteSpace(protection))
|
||||
return;
|
||||
|
||||
// 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);
|
||||
});
|
||||
|
||||
return protections;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle a single file based on all MS-DOS executable check implementations
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the source file of the executable, for tracking</param>
|
||||
/// <param name="mz">Executable to scan</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Set of protections in file, null on error</returns>
|
||||
public ConcurrentDictionary<IMSDOSExecutableCheck, string> RunMSDOSExecutableChecks(string file, Stream stream, MSDOS mz, bool includeDebug)
|
||||
{
|
||||
// Create the output dictionary
|
||||
var protections = new ConcurrentDictionary<IMSDOSExecutableCheck, string>();
|
||||
|
||||
// Iterate through all checks
|
||||
Parallel.ForEach(MSDOSExecutableCheckClasses, checkClass =>
|
||||
{
|
||||
// Get the protection for the class, if possible
|
||||
string protection = checkClass.CheckMSDOSExecutable(file, mz, includeDebug);
|
||||
if (string.IsNullOrWhiteSpace(protection))
|
||||
return;
|
||||
|
||||
// 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);
|
||||
});
|
||||
|
||||
return protections;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle a single file based on all new executable check implementations
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the source file of the executable, for tracking</param>
|
||||
/// <param name="nex">Executable to scan</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Set of protections in file, null on error</returns>
|
||||
public ConcurrentDictionary<INewExecutableCheck, string> RunNewExecutableChecks(string file, Stream stream, NewExecutable nex, bool includeDebug)
|
||||
{
|
||||
// Create the output dictionary
|
||||
var protections = new ConcurrentDictionary<INewExecutableCheck, string>();
|
||||
|
||||
// Iterate through all checks
|
||||
Parallel.ForEach(NewExecutableCheckClasses, checkClass =>
|
||||
{
|
||||
// Get the protection for the class, if possible
|
||||
string protection = checkClass.CheckNewExecutable(file, nex, includeDebug);
|
||||
if (string.IsNullOrWhiteSpace(protection))
|
||||
return;
|
||||
|
||||
// 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);
|
||||
});
|
||||
|
||||
return protections;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle a single file based on all portable executable check implementations
|
||||
/// </summary>
|
||||
/// <param name="file">Name of the source file of the executable, for tracking</param>
|
||||
/// <param name="pex">Executable to scan</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Set of protections in file, null on error</returns>
|
||||
public ConcurrentDictionary<IPortableExecutableCheck, string> RunPortableExecutableChecks(string file, Stream stream, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Create the output dictionary
|
||||
var protections = new ConcurrentDictionary<IPortableExecutableCheck, string>();
|
||||
|
||||
// Iterate through all checks
|
||||
Parallel.ForEach(PortableExecutableCheckClasses, checkClass =>
|
||||
{
|
||||
// Get the protection for the class, if possible
|
||||
string protection = checkClass.CheckPortableExecutable(file, pex, includeDebug);
|
||||
if (string.IsNullOrWhiteSpace(protection))
|
||||
return;
|
||||
|
||||
// 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);
|
||||
});
|
||||
|
||||
return protections;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Initializers
|
||||
|
||||
/// <summary>
|
||||
/// Initialize all implementations of a type
|
||||
/// </summary>
|
||||
private static IEnumerable<T> InitCheckClasses<T>()
|
||||
=> 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
|
||||
/// </summary>
|
||||
private static IEnumerable<T> InitCheckClasses<T>(Assembly assembly)
|
||||
{
|
||||
return assembly.GetTypes()
|
||||
.Where(t => t.IsClass && t.GetInterface(typeof(T).Name) != null)
|
||||
.Select(t => (T)Activator.CreateInstance(t));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#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>
|
||||
/// <param name="impl">Implementation that was last used to check</param>
|
||||
private static bool CheckIfPacker(object impl)
|
||||
{
|
||||
return impl.GetType().Namespace.ToLowerInvariant().Contains("packer");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
50
BinaryObjectScanner.FileType/GCF.cs
Normal file
50
BinaryObjectScanner.FileType/GCF.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Half-Life Game Cache File
|
||||
/// </summary>
|
||||
public class GCF : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
Wrappers.GCF gcf = Wrappers.GCF.Create(stream);
|
||||
if (gcf == null)
|
||||
return null;
|
||||
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
// Loop through and extract all files
|
||||
gcf.ExtractAll(tempPath);
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Interfaces;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.GZip;
|
||||
using static BurnOutSharp.Utilities.Dictionary;
|
||||
|
||||
namespace BurnOutSharp.FileType
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// gzip archive
|
||||
/// </summary>
|
||||
public class GZIP : IScannable
|
||||
public class GZIP : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
public string Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
// If the gzip file itself fails
|
||||
try
|
||||
{
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
@@ -38,7 +36,6 @@ namespace BurnOutSharp.FileType
|
||||
{
|
||||
foreach (var entry in zipFile.Entries)
|
||||
{
|
||||
// If an individual entry fails
|
||||
try
|
||||
{
|
||||
// If we have a directory, skip it
|
||||
@@ -50,35 +47,18 @@ namespace BurnOutSharp.FileType
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collect and format all found protections
|
||||
var protections = scanner.GetProtections(tempPath);
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
// Remove temporary path references
|
||||
StripFromKeys(protections, tempPath);
|
||||
|
||||
return protections;
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using BurnOutSharp.Interfaces;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using UnshieldSharp.Archive;
|
||||
using static BurnOutSharp.Utilities.Dictionary;
|
||||
|
||||
namespace BurnOutSharp.FileType
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// InstallShield archive v3
|
||||
/// </summary>
|
||||
public class InstallShieldArchiveV3 : IScannable
|
||||
public class InstallShieldArchiveV3 : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
public string Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add stream opening support
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
// If the archive itself fails
|
||||
try
|
||||
{
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
UnshieldSharp.Archive.InstallShieldArchiveV3 archive = new UnshieldSharp.Archive.InstallShieldArchiveV3(file);
|
||||
foreach (CompressedFile cfile in archive.Files.Select(kvp => kvp.Value))
|
||||
{
|
||||
// If an individual entry fails
|
||||
try
|
||||
{
|
||||
string tempFile = Path.Combine(tempPath, cfile.FullPath);
|
||||
@@ -57,34 +52,17 @@ namespace BurnOutSharp.FileType
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
// Collect and format all found protections
|
||||
var protections = scanner.GetProtections(tempPath);
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
// Remove temporary path references
|
||||
StripFromKeys(protections, tempPath);
|
||||
|
||||
return protections;
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
87
BinaryObjectScanner.FileType/InstallShieldCAB.cs
Normal file
87
BinaryObjectScanner.FileType/InstallShieldCAB.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using UnshieldSharp.Cabinet;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// InstallShield cabinet file
|
||||
/// </summary>
|
||||
public class InstallShieldCAB : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
// Get the name of the first cabinet file or header
|
||||
string directory = Path.GetDirectoryName(file);
|
||||
string noExtension = Path.GetFileNameWithoutExtension(file);
|
||||
string filenamePattern = Path.Combine(directory, noExtension);
|
||||
filenamePattern = new Regex(@"\d+$").Replace(filenamePattern, string.Empty);
|
||||
|
||||
bool cabinetHeaderExists = File.Exists(Path.Combine(directory, filenamePattern + "1.hdr"));
|
||||
bool shouldScanCabinet = cabinetHeaderExists
|
||||
? file.Equals(Path.Combine(directory, filenamePattern + "1.hdr"), StringComparison.OrdinalIgnoreCase)
|
||||
: file.Equals(Path.Combine(directory, filenamePattern + "1.cab"), StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
// If we have anything but the first file
|
||||
if (!shouldScanCabinet)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
InstallShieldCabinet cabfile = InstallShieldCabinet.Open(file);
|
||||
for (int i = 0; i < cabfile.FileCount; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Check if the file is valid first
|
||||
if (!cabfile.FileIsValid(i))
|
||||
continue;
|
||||
|
||||
string tempFile;
|
||||
try
|
||||
{
|
||||
string filename = cabfile.FileName(i);
|
||||
tempFile = Path.Combine(tempPath, filename);
|
||||
}
|
||||
catch
|
||||
{
|
||||
tempFile = Path.Combine(tempPath, $"BAD_FILENAME{i}");
|
||||
}
|
||||
|
||||
cabfile.FileSave(i, tempFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
44
BinaryObjectScanner.FileType/LDSCRYPT.cs
Normal file
44
BinaryObjectScanner.FileType/LDSCRYPT.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using BinaryObjectScanner.Matching;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Link Data Security encrypted file
|
||||
/// </summary>
|
||||
public class LDSCRYPT : IDetectable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string Detect(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Detect(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] magic = new byte[16];
|
||||
stream.Read(magic, 0, 16);
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x4C, 0x44, 0x53, 0x43, 0x52, 0x59, 0x50, 0x54 }))
|
||||
return "Link Data Security encrypted file";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Interfaces;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
#if NET48
|
||||
using StormLibSharp;
|
||||
#endif
|
||||
using static BurnOutSharp.Utilities.Dictionary;
|
||||
|
||||
namespace BurnOutSharp.FileType
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// MoPaQ game data archive
|
||||
/// </summary>
|
||||
public class MPQ : IScannable
|
||||
public class MPQ : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
public string Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add stream opening support
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
// Not supported for .NET 6.0 due to Windows DLL requirements
|
||||
return null;
|
||||
#else
|
||||
// If the MPQ file itself fails
|
||||
try
|
||||
{
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
@@ -45,7 +43,7 @@ namespace BurnOutSharp.FileType
|
||||
// Try to open the listfile
|
||||
string listfile = null;
|
||||
MpqFileStream listStream = mpqArchive.OpenFile("(listfile)");
|
||||
|
||||
|
||||
// If we can't read the listfile, we just return
|
||||
if (!listStream.CanRead)
|
||||
return null;
|
||||
@@ -62,7 +60,6 @@ namespace BurnOutSharp.FileType
|
||||
// Loop over each entry
|
||||
foreach (string sub in listfileLines)
|
||||
{
|
||||
// If an individual entry fails
|
||||
try
|
||||
{
|
||||
string tempFile = Path.Combine(tempPath, sub);
|
||||
@@ -71,35 +68,18 @@ namespace BurnOutSharp.FileType
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collect and format all found protections
|
||||
var protections = scanner.GetProtections(tempPath);
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
// Remove temporary path references
|
||||
StripFromKeys(protections, tempPath);
|
||||
|
||||
return protections;
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
58
BinaryObjectScanner.FileType/MicrosoftCAB.cs
Normal file
58
BinaryObjectScanner.FileType/MicrosoftCAB.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using BinaryObjectScanner.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Microsoft cabinet file
|
||||
/// </summary>
|
||||
/// <remarks>Specification available at <see href="http://download.microsoft.com/download/5/0/1/501ED102-E53F-4CE0-AA6B-B0F93629DDC6/Exchange/%5BMS-CAB%5D.pdf"/></remarks>
|
||||
/// <see href="https://github.com/wine-mirror/wine/tree/master/dlls/cabinet"/>
|
||||
public class MicrosoftCAB : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO: Fix/re-enable/do ANYTHING to get this working again
|
||||
return null;
|
||||
|
||||
// Open the cab file
|
||||
var cabFile = MicrosoftCabinet.Create(stream);
|
||||
if (cabFile == null)
|
||||
return null;
|
||||
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
// If entry extraction fails
|
||||
bool success = cabFile.ExtractAll(tempPath);
|
||||
if (!success)
|
||||
return null;
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Compression;
|
||||
using BurnOutSharp.Interfaces;
|
||||
using static BurnOutSharp.Utilities.Dictionary;
|
||||
using BinaryObjectScanner.Compression;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BurnOutSharp.FileType
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Microsoft LZ-compressed Files (LZ32)
|
||||
/// </summary>
|
||||
/// <remarks>This is treated like an archive type due to the packing style</remarks>
|
||||
public class MicrosoftLZ : IScannable
|
||||
public class MicrosoftLZ : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
public string Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
// If the LZ file itself fails
|
||||
try
|
||||
{
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
@@ -56,30 +54,13 @@ namespace BurnOutSharp.FileType
|
||||
tempStream.Write(data, 0, data.Length);
|
||||
}
|
||||
|
||||
// Collect and format all found protections
|
||||
var protections = scanner.GetProtections(tempPath);
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
// Remove temporary path references
|
||||
StripFromKeys(protections, tempPath);
|
||||
|
||||
return protections;
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
50
BinaryObjectScanner.FileType/PAK.cs
Normal file
50
BinaryObjectScanner.FileType/PAK.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Half-Life Package File
|
||||
/// </summary>
|
||||
public class PAK : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
Wrappers.PAK pak = Wrappers.PAK.Create(stream);
|
||||
if (pak == null)
|
||||
return null;
|
||||
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
// Loop through and extract all files
|
||||
pak.ExtractAll(tempPath);
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
50
BinaryObjectScanner.FileType/PFF.cs
Normal file
50
BinaryObjectScanner.FileType/PFF.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// NovaLogic Game Archive Format
|
||||
/// </summary>
|
||||
public class PFF : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
Wrappers.PFF pff = Wrappers.PFF.Create(stream);
|
||||
if (pff == null)
|
||||
return null;
|
||||
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
// Extract all files
|
||||
pff.ExtractAll(tempPath);
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Interfaces;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Zip;
|
||||
using static BurnOutSharp.Utilities.Dictionary;
|
||||
|
||||
namespace BurnOutSharp.FileType
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// PKWARE ZIP archive and derivatives
|
||||
/// </summary>
|
||||
public class PKZIP : IScannable
|
||||
public class PKZIP : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
public string Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
// If the zip file itself fails
|
||||
try
|
||||
{
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
@@ -38,7 +36,6 @@ namespace BurnOutSharp.FileType
|
||||
{
|
||||
foreach (var entry in zipFile.Entries)
|
||||
{
|
||||
// If an individual entry fails
|
||||
try
|
||||
{
|
||||
// If we have a directory, skip it
|
||||
@@ -51,35 +48,18 @@ namespace BurnOutSharp.FileType
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collect and format all found protections
|
||||
var protections = scanner.GetProtections(tempPath);
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
// Remove temporary path references
|
||||
StripFromKeys(protections, tempPath);
|
||||
|
||||
return protections;
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
44
BinaryObjectScanner.FileType/PLJ.cs
Normal file
44
BinaryObjectScanner.FileType/PLJ.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using BinaryObjectScanner.Matching;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// PlayJ audio file
|
||||
/// </summary>
|
||||
public class PLJ : IDetectable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string Detect(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Detect(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] magic = new byte[16];
|
||||
stream.Read(magic, 0, 16);
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0xFF, 0x9D, 0x53, 0x4B }))
|
||||
return "PlayJ Audio File";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Interfaces;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Rar;
|
||||
using static BurnOutSharp.Utilities.Dictionary;
|
||||
|
||||
namespace BurnOutSharp.FileType
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// RAR archive
|
||||
/// </summary>
|
||||
public class RAR : IScannable
|
||||
public class RAR : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
public string Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
// If the rar file itself fails
|
||||
try
|
||||
{
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
@@ -38,7 +36,6 @@ namespace BurnOutSharp.FileType
|
||||
{
|
||||
foreach (var entry in rarFile.Entries)
|
||||
{
|
||||
// If an individual entry fails
|
||||
try
|
||||
{
|
||||
// If we have a directory, skip it
|
||||
@@ -50,35 +47,18 @@ namespace BurnOutSharp.FileType
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collect and format all found protections
|
||||
var protections = scanner.GetProtections(tempPath);
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
// Remove temporary path references
|
||||
StripFromKeys(protections, tempPath);
|
||||
|
||||
return protections;
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
63
BinaryObjectScanner.FileType/SFFS.cs
Normal file
63
BinaryObjectScanner.FileType/SFFS.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using BinaryObjectScanner.Matching;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// StarForce Filesystem file
|
||||
/// </summary>
|
||||
/// <see href="https://forum.xentax.com/viewtopic.php?f=21&t=2084"/>
|
||||
public class SFFS : IExtractable, IDetectable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string Detect(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Detect(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] magic = new byte[16];
|
||||
stream.Read(magic, 0, 16);
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x53, 0x46, 0x46, 0x53 }))
|
||||
return "StarForce Filesystem Container";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
50
BinaryObjectScanner.FileType/SGA.cs
Normal file
50
BinaryObjectScanner.FileType/SGA.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// SGA game archive
|
||||
/// </summary>
|
||||
public class SGA : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
Wrappers.SGA sga = Wrappers.SGA.Create(stream);
|
||||
if (sga == null)
|
||||
return null;
|
||||
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
// Loop through and extract all files
|
||||
sga.ExtractAll(tempPath);
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Interfaces;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.SevenZip;
|
||||
using static BurnOutSharp.Utilities.Dictionary;
|
||||
|
||||
namespace BurnOutSharp.FileType
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// 7-zip archive
|
||||
/// </summary>
|
||||
public class SevenZip : IScannable
|
||||
public class SevenZip : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
public string Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
// If the 7-zip file itself fails
|
||||
try
|
||||
{
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
@@ -38,7 +36,6 @@ namespace BurnOutSharp.FileType
|
||||
{
|
||||
foreach (var entry in sevenZipFile.Entries)
|
||||
{
|
||||
// If an individual entry fails
|
||||
try
|
||||
{
|
||||
// If we have a directory, skip it
|
||||
@@ -50,35 +47,18 @@ namespace BurnOutSharp.FileType
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
// Collect and format all found protections
|
||||
var protections = scanner.GetProtections(tempPath);
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
// Remove temporary path references
|
||||
StripFromKeys(protections, tempPath);
|
||||
|
||||
return protections;
|
||||
}
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using BurnOutSharp.Interfaces;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.Tar;
|
||||
using static BurnOutSharp.Utilities.Dictionary;
|
||||
|
||||
namespace BurnOutSharp.FileType
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Tape archive
|
||||
/// </summary>
|
||||
public class TapeArchive : IScannable
|
||||
public class TapeArchive : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
public string Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
// If the tar file itself fails
|
||||
try
|
||||
{
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
@@ -38,7 +36,6 @@ namespace BurnOutSharp.FileType
|
||||
{
|
||||
foreach (var entry in tarFile.Entries)
|
||||
{
|
||||
// If an individual entry fails
|
||||
try
|
||||
{
|
||||
// If we have a directory, skip it
|
||||
@@ -50,35 +47,18 @@ namespace BurnOutSharp.FileType
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collect and format all found protections
|
||||
var protections = scanner.GetProtections(tempPath);
|
||||
|
||||
// If temp directory cleanup fails
|
||||
try
|
||||
{
|
||||
Directory.Delete(tempPath, true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
// Remove temporary path references
|
||||
StripFromKeys(protections, tempPath);
|
||||
|
||||
return protections;
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,34 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using BurnOutSharp.Interfaces;
|
||||
using static BurnOutSharp.Utilities.Dictionary;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BurnOutSharp.FileType
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Various generic textfile formats
|
||||
/// </summary>
|
||||
public class Textfile : IScannable
|
||||
public class Textfile : IDetectable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, string file)
|
||||
public string Detect(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Scan(scanner, fs, file);
|
||||
return Detect(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ConcurrentDictionary<string, ConcurrentQueue<string>> Scan(Scanner scanner, Stream stream, string file)
|
||||
public string Detect(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
// Files can be protected in multiple ways
|
||||
var protections = new ConcurrentDictionary<string, ConcurrentQueue<string>>();
|
||||
var protections = new List<string>();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -42,57 +41,60 @@ namespace BurnOutSharp.FileType
|
||||
// AegiSoft License Manager
|
||||
// Found in "setup.ins" (Redump entry 73521/IA item "Nova_HoyleCasino99USA").
|
||||
if (fileContent.Contains("Failed to load the AegiSoft License Manager install program."))
|
||||
AppendToDictionary(protections, file, "AegiSoft License Manager");
|
||||
protections.Add("AegiSoft License Manager");
|
||||
|
||||
// CD-Key
|
||||
if (fileContent.Contains("a valid serial number is required"))
|
||||
AppendToDictionary(protections, file, "CD-Key / Serial");
|
||||
protections.Add("CD-Key / Serial");
|
||||
else if (fileContent.Contains("serial number is located"))
|
||||
AppendToDictionary(protections, file, "CD-Key / Serial");
|
||||
protections.Add("CD-Key / Serial");
|
||||
// Found in "Setup.Ins" ("Word Point 2002" in IA item "advantage-language-french-beginner-langmaster-2005").
|
||||
else if (fileContent.Contains("Please enter a valid registration number"))
|
||||
protections.Add("CD-Key / Serial");
|
||||
|
||||
// Freelock
|
||||
// Found in "FILE_ID.DIZ" distributed with Freelock.
|
||||
if (fileContent.Contains("FREELOCK 1.0"))
|
||||
AppendToDictionary(protections, file, "Freelock 1.0");
|
||||
protections.Add("Freelock 1.0");
|
||||
else if (fileContent.Contains("FREELOCK 1.2"))
|
||||
AppendToDictionary(protections, file, "Freelock 1.2");
|
||||
protections.Add("Freelock 1.2");
|
||||
else if (fileContent.Contains("FREELOCK 1.2a"))
|
||||
AppendToDictionary(protections, file, "Freelock 1.2a");
|
||||
protections.Add("Freelock 1.2a");
|
||||
else if (fileContent.Contains("FREELOCK 1.3"))
|
||||
AppendToDictionary(protections, file, "Freelock 1.3");
|
||||
protections.Add("Freelock 1.3");
|
||||
else if (fileContent.Contains("FREELOCK"))
|
||||
AppendToDictionary(protections, file, "Freelock");
|
||||
protections.Add("Freelock");
|
||||
|
||||
// MediaCloQ
|
||||
if (fileContent.Contains("SunnComm MediaCloQ"))
|
||||
AppendToDictionary(protections, file, "MediaCloQ");
|
||||
protections.Add("MediaCloQ");
|
||||
else if (fileContent.Contains("http://download.mediacloq.com/"))
|
||||
AppendToDictionary(protections, file, "MediaCloQ");
|
||||
protections.Add("MediaCloQ");
|
||||
else if (fileContent.Contains("http://www.sunncomm.com/mediacloq/"))
|
||||
AppendToDictionary(protections, file, "MediaCloQ");
|
||||
protections.Add("MediaCloQ");
|
||||
|
||||
// MediaMax
|
||||
if (fileContent.Contains("MediaMax technology"))
|
||||
AppendToDictionary(protections, file, "MediaMax CD-3");
|
||||
protections.Add("MediaMax CD-3");
|
||||
else if (fileContent.Contains("exclusive Cd3 technology"))
|
||||
AppendToDictionary(protections, file, "MediaMax CD-3");
|
||||
protections.Add("MediaMax CD-3");
|
||||
else if (fileContent.Contains("<PROTECTION-VENDOR>MediaMAX</PROTECTION-VENDOR>"))
|
||||
AppendToDictionary(protections, file, "MediaMax CD-3");
|
||||
protections.Add("MediaMax CD-3");
|
||||
else if (fileContent.Contains("MediaMax(tm)"))
|
||||
AppendToDictionary(protections, file, "MediaMax CD-3");
|
||||
protections.Add("MediaMax CD-3");
|
||||
|
||||
// phenoProtect
|
||||
if (fileContent.Contains("phenoProtect"))
|
||||
AppendToDictionary(protections, file, "phenoProtect");
|
||||
protections.Add("phenoProtect");
|
||||
|
||||
// Rainbow Sentinel
|
||||
// Found in "SENTW95.HLP" and "SENTINEL.HLP" in BA entry "Autodesk AutoCAD LT 98 (1998) (CD) [English] [Dutch]".
|
||||
if (fileContent.Contains("Rainbow Sentinel Driver Help"))
|
||||
AppendToDictionary(protections, file, "Rainbow Sentinel");
|
||||
protections.Add("Rainbow Sentinel");
|
||||
|
||||
// Found in "OEMSETUP.INF" in BA entry "Autodesk AutoCAD LT 98 (1998) (CD) [English] [Dutch]".
|
||||
if (fileContent.Contains("Sentinel Driver Disk"))
|
||||
AppendToDictionary(protections, file, "Rainbow Sentinel");
|
||||
protections.Add("Rainbow Sentinel");
|
||||
|
||||
// The full line from a sample is as follows:
|
||||
//
|
||||
@@ -102,22 +104,22 @@ namespace BurnOutSharp.FileType
|
||||
|
||||
// SecuROM
|
||||
if (fileContent.Contains("SecuROM protected application"))
|
||||
AppendToDictionary(protections, file, "SecuROM");
|
||||
protections.Add("SecuROM");
|
||||
|
||||
// Steam
|
||||
if (fileContent.Contains("All use of the Program is governed by the terms of the Steam Agreement as described below."))
|
||||
AppendToDictionary(protections, file, "Steam");
|
||||
protections.Add("Steam");
|
||||
|
||||
// XCP
|
||||
if (fileContent.Contains("http://cp.sonybmg.com/xcp/"))
|
||||
AppendToDictionary(protections, file, "XCP");
|
||||
protections.Add("XCP");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (scanner.IncludeDebug) Console.WriteLine(ex);
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
return protections;
|
||||
return string.Join(";", protections);
|
||||
}
|
||||
}
|
||||
}
|
||||
50
BinaryObjectScanner.FileType/VBSP.cs
Normal file
50
BinaryObjectScanner.FileType/VBSP.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Half-Life 2 Level
|
||||
/// </summary>
|
||||
public class VBSP : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
Wrappers.VBSP vbsp = Wrappers.VBSP.Create(stream);
|
||||
if (vbsp == null)
|
||||
return null;
|
||||
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
// Loop through and extract all files
|
||||
vbsp.ExtractAllLumps(tempPath);
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex.ToString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
50
BinaryObjectScanner.FileType/VPK.cs
Normal file
50
BinaryObjectScanner.FileType/VPK.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Valve Package File
|
||||
/// </summary>
|
||||
public class VPK : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
Wrappers.VPK vpk = Wrappers.VPK.Create(stream);
|
||||
if (vpk == null)
|
||||
return null;
|
||||
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
// Loop through and extract all files
|
||||
vpk.ExtractAll(tempPath);
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
50
BinaryObjectScanner.FileType/WAD.cs
Normal file
50
BinaryObjectScanner.FileType/WAD.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Half-Life Texture Package File
|
||||
/// </summary>
|
||||
public class WAD : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
Wrappers.WAD wad = Wrappers.WAD.Create(stream);
|
||||
if (wad == null)
|
||||
return null;
|
||||
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
// Loop through and extract all files
|
||||
wad.ExtractAllLumps(tempPath);
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
52
BinaryObjectScanner.FileType/XZ.cs
Normal file
52
BinaryObjectScanner.FileType/XZ.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using SharpCompress.Compressors.Xz;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// xz archive
|
||||
/// </summary>
|
||||
public class XZ : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
using (XZStream xzFile = new XZStream(stream))
|
||||
{
|
||||
string tempFile = Path.Combine(tempPath, Guid.NewGuid().ToString());
|
||||
using (FileStream fs = File.OpenWrite(tempFile))
|
||||
{
|
||||
xzFile.CopyTo(fs);
|
||||
}
|
||||
}
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
50
BinaryObjectScanner.FileType/XZP.cs
Normal file
50
BinaryObjectScanner.FileType/XZP.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
|
||||
namespace BinaryObjectScanner.FileType
|
||||
{
|
||||
/// <summary>
|
||||
/// XBox Package File
|
||||
/// </summary>
|
||||
public class XZP : IExtractable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string Extract(string file, bool includeDebug)
|
||||
{
|
||||
if (!File.Exists(file))
|
||||
return null;
|
||||
|
||||
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return Extract(fs, file, includeDebug);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Extract(Stream stream, string file, bool includeDebug)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Create the wrapper
|
||||
Wrappers.XZP xzp = Wrappers.XZP.Create(stream);
|
||||
if (xzp == null)
|
||||
return null;
|
||||
|
||||
// Create a temp output directory
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
||||
Directory.CreateDirectory(tempPath);
|
||||
|
||||
// Loop through and extract all files
|
||||
xzp.ExtractAll(tempPath);
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.8</Version>
|
||||
<AssemblyVersion>2.8</AssemblyVersion>
|
||||
<FileVersion>2.8</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>
|
||||
40
BinaryObjectScanner.GameEngine/RenderWare.cs
Normal file
40
BinaryObjectScanner.GameEngine/RenderWare.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using BinaryObjectScanner.Interfaces;
|
||||
using BinaryObjectScanner.Wrappers;
|
||||
|
||||
namespace BinaryObjectScanner.GameEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// RenderWare (https://web.archive.org/web/20070214132346/http://www.renderware.com/) is an API and graphics engine created by Criterion in 1993.
|
||||
/// It appears that version 4.X was exclusively used by EA internally, with version 3.X being the final public version (https://sigmaco.org/3782-renderware/).
|
||||
/// It was available to use on many different platforms, with it being particularly useful for the PS2 (https://en.wikipedia.org/wiki/RenderWare).
|
||||
///
|
||||
/// Additional resources and documentation:
|
||||
/// RenderWare interview: https://web.archive.org/web/20031208124348/http://www.homelanfed.com/index.php?id=9856
|
||||
/// RenderWare V2.1 API reference: http://www.tnlc.com/rw/api/rwdoc.htm
|
||||
/// RenderWare 2 official docs: https://github.com/electronicarts/RenderWare3Docs
|
||||
/// RenderWare 3.7 SDK: https://github.com/sigmaco/rwsdk-v37-pc
|
||||
/// Wikipedia list of RenderWare games: https://en.wikipedia.org/wiki/Category:RenderWare_games
|
||||
/// </summary>
|
||||
public class RenderWare : IPortableExecutableCheck
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string CheckPortableExecutable(string file, PortableExecutable pex, bool includeDebug)
|
||||
{
|
||||
// Get the sections from the executable, if possible
|
||||
var sections = pex?.SectionTable;
|
||||
if (sections == null)
|
||||
return null;
|
||||
|
||||
// Found in Redump entries 20138, 55823, and 102493.
|
||||
bool rwcsegSection = pex.ContainsSection("_rwcseg", exact: true);
|
||||
// Found in Redump entry 20138.
|
||||
bool rwdsegSection = pex.ContainsSection("_rwdseg", exact: true);
|
||||
|
||||
// TODO: Check if this indicates a specific version, or if these sections are present in multiple.
|
||||
if (rwcsegSection || rwdsegSection)
|
||||
return "RenderWare";
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
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 { }
|
||||
}
|
||||
@@ -1,17 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;net6.0</TargetFrameworks>
|
||||
<TargetFrameworks>net48;net6.0;net7.0</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win-x86;win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<Title>BurnOutSharp.Builders</Title>
|
||||
<AssemblyName>BurnOutSharp.Builders</AssemblyName>
|
||||
<Title>BinaryObjectScanner.Interfaces</Title>
|
||||
<AssemblyName>BinaryObjectScanner.Interfaces</AssemblyName>
|
||||
<Authors>Matt Nadareski</Authors>
|
||||
<Product>BurnOutSharp</Product>
|
||||
<Copyright>Copyright (c)2022 Matt Nadareski</Copyright>
|
||||
<RepositoryUrl>https://github.com/mnadareski/BurnOutSharp</RepositoryUrl>
|
||||
<Version>2.6</Version>
|
||||
<AssemblyVersion>2.6</AssemblyVersion>
|
||||
<FileVersion>2.6</FileVersion>
|
||||
<Version>2.8</Version>
|
||||
<AssemblyVersion>2.8</AssemblyVersion>
|
||||
<FileVersion>2.8</FileVersion>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
</PropertyGroup>
|
||||
@@ -21,8 +21,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BurnOutSharp.Models\BurnOutSharp.Models.csproj" />
|
||||
<ProjectReference Include="..\BurnOutSharp.Utilities\BurnOutSharp.Utilities.csproj" />
|
||||
<ProjectReference Include="..\BinaryObjectScanner.Wrappers\BinaryObjectScanner.Wrappers.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace BurnOutSharp.Interfaces
|
||||
namespace BinaryObjectScanner.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Check a generic file for protection
|
||||
28
BinaryObjectScanner.Interfaces/IDetectable.cs
Normal file
28
BinaryObjectScanner.Interfaces/IDetectable.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System.IO;
|
||||
|
||||
namespace BinaryObjectScanner.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Mark a file type as being able to be detected
|
||||
/// </summary>
|
||||
public interface IDetectable
|
||||
{
|
||||
/// <summary>
|
||||
/// Check if a file is detected as this file type
|
||||
/// </summary>
|
||||
/// <param name="file">Path to the input file</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Detected file or protection type, null on error</returns>
|
||||
/// <remarks>Ideally, this should just point to the other detect implementation.</remarks>
|
||||
string Detect(string file, bool includeDebug);
|
||||
|
||||
/// <summary>
|
||||
/// Check if a stream is detected as this file type
|
||||
/// </summary>
|
||||
/// <param name="stream">Stream representing the input file</param>
|
||||
/// <param name="file">Path to the input file</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Detected file or protection type, null on error</returns>
|
||||
string Detect(Stream stream, string file, bool includeDebug);
|
||||
}
|
||||
}
|
||||
30
BinaryObjectScanner.Interfaces/IExtractable.cs
Normal file
30
BinaryObjectScanner.Interfaces/IExtractable.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System.IO;
|
||||
|
||||
namespace BinaryObjectScanner.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Mark a file type as being able to be extracted
|
||||
/// </summary>
|
||||
/// TODO: Change to have output directory passed in
|
||||
/// TODO: Change to return a bool
|
||||
public interface IExtractable
|
||||
{
|
||||
/// <summary>
|
||||
/// Extract a file to a temporary path, if possible
|
||||
/// </summary>
|
||||
/// <param name="file">Path to the input file</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Path to extracted files, null on error</returns>
|
||||
/// <remarks>Ideally, this should just point to the other extract implementation.</remarks>
|
||||
string Extract(string file, bool includeDebug);
|
||||
|
||||
/// <summary>
|
||||
/// Extract a stream to a temporary path, if possible
|
||||
/// </summary>
|
||||
/// <param name="stream">Stream representing the input file</param>
|
||||
/// <param name="file">Path to the input file</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Path to extracted files, null on error</returns>
|
||||
string Extract(Stream stream, string file, bool includeDebug);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user