SignatureChecker fixes

- Added SignatureTests
- MatchesAsync will only read the file once
- Signatures are ordered in descending order by length
This commit is contained in:
erri120 2020-08-01 11:51:59 +02:00
parent 49b3e603c7
commit be985169d9
No known key found for this signature in database
GPG Key ID: A8C0A18D8D4D3135
2 changed files with 71 additions and 20 deletions

View File

@ -0,0 +1,54 @@
using System.Collections.Generic;
using Wabbajack.Common.FileSignatures;
using Xunit;
namespace Wabbajack.Common.Test
{
public class SignatureTests
{
[Fact]
public async void CanMatchSignatures()
{
await using var tempFile = new TempFile();
var sig = new byte[] {0x00, 0x01, 0x00, 0x00, 0x00};
await tempFile.Path.WriteAllBytesAsync(sig);
var list = new List<Definitions.FileType>
{
Definitions.FileType.TTF, Definitions.FileType.ABA, Definitions.FileType.ACCDB
};
var checker = new SignatureChecker(list.ToArray());
var res = await checker.MatchesAsync(tempFile.Path);
Assert.NotNull(res);
Assert.Equal(Definitions.FileType.TTF, res);
}
[Fact]
public async void CanMatchCorrectSignature()
{
await using var tempFile = new TempFile();
var sig = new byte[] { 0x00, 0x01, 0x00, 0x00, 0x00 };
await tempFile.Path.WriteAllBytesAsync(sig);
var list = new List<Definitions.FileType>
{
Definitions.FileType.TES3,
Definitions.FileType.TTF,
};
var checker = new SignatureChecker(list.ToArray());
var res = await checker.MatchesAsync(tempFile.Path);
Assert.NotNull(res);
Assert.Equal(Definitions.FileType.TTF, res);
}
}
}

View File

@ -1,5 +1,4 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -7,39 +6,37 @@ namespace Wabbajack.Common.FileSignatures
{ {
public class SignatureChecker public class SignatureChecker
{ {
private HashSet<Definitions.FileType> _types; private readonly HashSet<Definitions.FileType> _types;
private (Definitions.FileType, byte[])[] _signatures; private readonly (Definitions.FileType, byte[])[] _signatures;
private readonly int _maxLength;
public SignatureChecker(params Definitions.FileType[] types) public SignatureChecker(params Definitions.FileType[] types)
{ {
_types = new HashSet<Definitions.FileType>(types); _types = new HashSet<Definitions.FileType>(types);
_signatures = Definitions.Signatures.Where(row => _types.Contains(row.Item1)).ToArray(); _signatures = Definitions.Signatures.Where(row => _types.Contains(row.Item1)).OrderByDescending(x => x.Item2.Length).ToArray();
_maxLength = _signatures.First().Item2.Length;
} }
public async Task<Definitions.FileType?> MatchesAsync(AbsolutePath path) public async Task<Definitions.FileType?> MatchesAsync(AbsolutePath path)
{ {
await using var fs = await path.OpenShared(); await using var fs = await path.OpenShared();
foreach (var signature in _signatures) var buffer = new byte[_maxLength];
fs.Position = 0;
await fs.ReadAsync(buffer);
foreach (var (fileType, signature) in _signatures)
{ {
var buffer = new byte[signature.Item2.Length]; if (AreEqual(buffer, signature))
fs.Position = 0; return fileType;
await fs.ReadAsync(buffer);
if (AreEqual(buffer, signature.Item2))
return signature.Item1;
} }
return null; return null;
} }
private static bool AreEqual(byte[] a, byte[] b) private static bool AreEqual(IReadOnlyList<byte> a, IEnumerable<byte> b)
{ {
if (a.Length != b.Length) return false; return !b.Where((t, i) => a[i] != t).Any();
for (var i = 0; i < a.Length; i++)
{
if (a[i] != b[i]) return false;
}
return true;
} }
} }
} }