Skip to content

Commit

Permalink
Added globbing pattern support (Currently untested).
Browse files Browse the repository at this point in the history
  • Loading branch information
jasoncouture committed Dec 11, 2018
1 parent 4493292 commit ba0e89d
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 37 deletions.
16 changes: 16 additions & 0 deletions Source/Nexus.Archive.Tests/UnitTest1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,23 @@ public void FileLookupTest()
var entry = enIndex.FindEntry("ClientDataEN\\en-US.bin");
Assert.NotNull(entry);
}
[Fact]
public void FileTypeHeaderChecks()
{
var clientDataArchive = LoadArchiveFromPath(Path.Combine("Patch", "ClientData.archive"), true);

}

private ArchiveFile LoadArchiveFromPath(string path, bool required = false)
{
var realPath = Path.Combine(GamePath, path);
if (!File.Exists(realPath))
{
if (required) throw new FileNotFoundException("File not found", realPath);
return null;
}
return (ArchiveFile)ArchiveFileBase.FromFile(realPath);
}
private IndexFile LoadIndexFromPath(string path, bool required = false)
{
var realPath = Path.Combine(GamePath, path);
Expand Down
2 changes: 1 addition & 1 deletion Source/Nexus.Archive.sln
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nexus.Archive", "Nexus.Arch
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nexus.Archive.Tests", "Nexus.Archive.Tests\Nexus.Archive.Tests.csproj", "{758DE6FD-18C4-4153-B3B1-AA08D000C3C1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TableExtractor", "TableExtractor\TableExtractor.csproj", "{3985642C-5636-467A-A71D-3DB6B7712701}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TableExtractor", "TableExtractor\TableExtractor.csproj", "{3985642C-5636-467A-A71D-3DB6B7712701}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down
54 changes: 26 additions & 28 deletions Source/Nexus.Archive/ArchiveFileBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,32 @@ namespace Nexus.Archive
{
public abstract class ArchiveFileBase : IDisposable
{
private static readonly Dictionary<ArchiveType, MemberInfo> TypeHandlers =
new Dictionary<ArchiveType, MemberInfo>();
private delegate ArchiveFileBase ArchiveFactory(string filePath, MemoryMappedFile file, ArchiveHeader header, BlockInfoHeader[] blockTable, RootIndexBlock rootBlock);
private static readonly Dictionary<ArchiveType, ArchiveFactory> TypeHandlers =
new Dictionary<ArchiveType, ArchiveFactory>();

static ArchiveFileBase()
{
foreach (var type in typeof(ArchiveFileBase).Assembly.GetTypes()
.Where(i => typeof(ArchiveFileBase).IsAssignableFrom(i) && !i.IsAbstract))
{
var attribute = type.GetCustomAttribute<ArchiveFileTypeAttribute>();
if (attribute == null) continue;
var argumentTypes = new[]
{
typeof(string), typeof(MemoryMappedFile), typeof(ArchiveHeader), typeof(BlockInfoHeader[]),
typeof(RootIndexBlock)
};
MemberInfo constructor = type.GetConstructor(argumentTypes);
MemberInfo method = type.GetMethod("FromFile", BindingFlags.Static, null, argumentTypes, null);
if (!(method is MethodInfo methodInfo) ||
!typeof(ArchiveFileBase).IsAssignableFrom(methodInfo.ReturnType))
method = null;
if (constructor == null && method == null) continue;
TypeHandlers[attribute.Type] = method ?? constructor;
}
TypeHandlers.Add(ArchiveType.Index, (filePath, file, header, blockTable, rootIndexBlock) => new IndexFile(filePath, file, header, blockTable, rootIndexBlock));
TypeHandlers.Add(ArchiveType.Archive, (filePath, file, header, blockTable, rootIndexBlock) => new ArchiveFile(filePath, file, header, blockTable, rootIndexBlock));
//foreach (var type in typeof(ArchiveFileBase).Assembly.GetTypes()
// .Where(i => typeof(ArchiveFileBase).IsAssignableFrom(i) && !i.IsAbstract))
//{
// var attribute = type.GetCustomAttribute<ArchiveFileTypeAttribute>();
// if (attribute == null) continue;
// var argumentTypes = new[]
// {
// typeof(string), typeof(MemoryMappedFile), typeof(ArchiveHeader), typeof(BlockInfoHeader[]),
// typeof(RootIndexBlock)
// };
// MemberInfo constructor = type.GetConstructor(argumentTypes);
// MemberInfo method = type.GetMethod("FromFile", BindingFlags.Static, null, argumentTypes, null);
// if (!(method is MethodInfo methodInfo) ||
// !typeof(ArchiveFileBase).IsAssignableFrom(methodInfo.ReturnType))
// method = null;
// if (constructor == null && method == null) continue;
// TypeHandlers[attribute.Type] = method ?? constructor;
//}
}

protected ArchiveFileBase(string fileName, MemoryMappedFile file, ArchiveHeader header,
Expand Down Expand Up @@ -81,7 +84,7 @@ internal Stream GetBlockView(int index)
private static Stream GetBlockView(BlockInfoHeader blockInfo, MemoryMappedFile file)
{
if (blockInfo.Size == 0) return null;
return file.CreateViewStream((long) blockInfo.Offset, (long) blockInfo.Size);
return file.CreateViewStream((long)blockInfo.Offset, (long)blockInfo.Size);
}

protected Stream GetBlockView(BlockInfoHeader blockInfo)
Expand All @@ -99,7 +102,7 @@ private static (int rootDescriptorIndex, BlockInfoHeader[] blockPointers) ReadBl
var startPosition = header.DataHeader.BlockTableOffset;
var length = header.DataHeader.BlockCount * Marshal.SizeOf<BlockInfoHeader>();
var archiveDescriptorIndex = -1;
using (var reader = new BinaryReader(file.CreateViewStream((long) startPosition, length)))
using (var reader = new BinaryReader(file.CreateViewStream((long)startPosition, length)))
{
for (var x = 0; x < header.DataHeader.BlockCount; x++)
{
Expand Down Expand Up @@ -140,12 +143,7 @@ public static ArchiveFileBase FromFile(string fileName)
blockPointerInfo.blockPointers[blockPointerInfo.rootDescriptorIndex]);
if (!TypeHandlers.TryGetValue(rootBlock.ArchiveType, out var creator))
throw new InvalidOperationException($"Unknown archive type: {rootBlock.ArchiveType:G}");
var creatorArguments = new object[] {fileName, file, header, blockPointerInfo.blockPointers, rootBlock};
if (creator is MethodInfo method)
return (ArchiveFileBase) method.Invoke(null, creatorArguments);
var constructorInfo = creator as ConstructorInfo;
Debug.Assert(constructorInfo != null);
return (ArchiveFileBase) constructorInfo.Invoke(creatorArguments);
return creator(fileName, file, header, blockPointerInfo.blockPointers, rootBlock);
}
catch
{
Expand Down
15 changes: 12 additions & 3 deletions Source/Nexus.Archive/DataHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,16 @@ namespace Nexus.Archive
{
public struct DataHeader
{
private const int UnknownDataSize = 28;
private const int UnknownDataSize = 24;
public ulong Unknown1;
public ulong FileSize;
public ulong Unknown2;
public byte UnknownFileIdentifier;
// Always 2 for on disk files.
public byte Unknown2;
public ushort Unknown3;
public uint Unknown4;
//public ulong Unknown2; // Seems to denote file type, 0x60 XX XX XX for on disk files, other values for others.
// Remaining 3 bytes unknown.
public ulong BlockTableOffset;
public uint BlockCount;

Expand All @@ -21,7 +27,10 @@ public static DataHeader ReadFrom(BinaryReader binaryReader)
{
Unknown1 = binaryReader.ReadUInt64(),
FileSize = binaryReader.ReadUInt64(),
Unknown2 = binaryReader.ReadUInt64(),
UnknownFileIdentifier = binaryReader.ReadByte(),
Unknown2 = binaryReader.ReadByte(),
Unknown3 = binaryReader.ReadUInt16(),
Unknown4 = binaryReader.ReadUInt32(),
BlockTableOffset = binaryReader.ReadUInt64(),
BlockCount = binaryReader.ReadUInt32(),
UnknownData = binaryReader.ReadBytes(UnknownDataSize)
Expand Down
51 changes: 51 additions & 0 deletions Source/Nexus.Archive/IndexFile.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Linq;
using System.Text;
using DotNet.Globbing;

namespace Nexus.Archive
{
Expand All @@ -17,6 +19,55 @@ public IndexFile(string fileName, MemoryMappedFile file, ArchiveHeader header,
new BinaryReader(GetBlockView(rootIndex.BlockIndex), Encoding.UTF8));
}

public IEnumerable<IArchiveFilesystemEntry> GetFilesystemEntries()
{
return RootFolder.EnumerateChildren(true);
}
public IEnumerable<IArchiveFilesystemEntry> GetFilesystemEntries(string searchPattern)
{
return SearchWithGlob(GetFilesystemEntries(), searchPattern);
}

public IEnumerable<IArchiveFolderEntry> GetFolders()
{
return RootFolder.EnumerateFolders(true);
}

public IEnumerable<IArchiveFolderEntry> GetFolders(string searchPattern)
{
return SearchWithGlob(GetFolders(), searchPattern);
}

public IEnumerable<IArchiveFileEntry> GetFiles()
{
return RootFolder.EnumerateFiles(true);
}

public IEnumerable<IArchiveFileEntry> GetFiles(string searchPattern)
{
return SearchWithGlob(GetFiles(), searchPattern);
}

private static IEnumerable<T> SearchWithGlob<T>(IEnumerable<T> items, string searchPattern)
where T : IArchiveFilesystemEntry
{
var glob = ParseGlob(searchPattern);
foreach(var item in items.Where(i => glob.IsMatch(i.Path)))
yield return item;
}

private static Glob ParseGlob(string glob)
{
var options = new GlobOptions()
{
Evaluation =
{
CaseInsensitive = true
}
};
return Glob.Parse(glob, options);
}

public IArchiveFolderEntry RootFolder { get; }

public IArchiveFilesystemEntry FindEntry(string archivePath)
Expand Down
5 changes: 5 additions & 0 deletions Source/Nexus.Archive/Nexus.Archive.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="DotNet.Glob" Version="2.1.1" />
<PackageReference Include="SharpCompress" Version="0.22.0" />
</ItemGroup>

<ItemGroup>
<Folder Include="Writer\" />
</ItemGroup>

</Project>
22 changes: 18 additions & 4 deletions Source/Nexus.Archive/RootIndexBlock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,31 @@ public struct RootIndexBlock
public ArchiveType ArchiveType;
public uint Version;
public uint BlockCount;
public uint BuildNumber;
public int BlockIndex;

public static RootIndexBlock FromReader(BinaryReader reader)
{
return new RootIndexBlock
var ret = new RootIndexBlock()
{
ArchiveType = (ArchiveType) reader.ReadUInt32(),
ArchiveType = (ArchiveType)reader.ReadUInt32(),
Version = reader.ReadUInt32(),
BlockCount = reader.ReadUInt32(),
BlockIndex = reader.ReadInt32()
};
if (ret.Version == 1)
{
ret.BuildNumber = reader.ReadUInt32();
}
else if (ret.Version == 2)
{
ret.BlockCount = reader.ReadUInt32();
}
else
{
throw new InvalidDataException($"Unknown file version {ret.Version} for archive type {ret.ArchiveType}");
}

ret.BlockIndex = reader.ReadInt32();
return ret;
}
}
}
4 changes: 3 additions & 1 deletion Source/TableExtractor/TableExtractor.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.DotNet.ILCompiler" Version="1.0.0-alpha-27208-01" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Nexus.Archive\Nexus.Archive.csproj" />
</ItemGroup>
Expand Down
9 changes: 9 additions & 0 deletions Source/TableExtractor/nuget.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<!--To inherit the global NuGet package sources remove the <clear/> line below -->
<clear />
<add key="nuget" value="https://api.nuget.org/v3/index.json" />
<add key="ilcompiler" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />
</packageSources>
</configuration>

0 comments on commit ba0e89d

Please sign in to comment.