Skip to content

Commit 94b115a

Browse files
committed
Initial refactor
1 parent 68d3e25 commit 94b115a

19 files changed

+822
-23
lines changed

Yura.Shared/Archive/ArchiveFile.cs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
6+
namespace Yura.Shared.Archive
7+
{
8+
public abstract class ArchiveFile
9+
{
10+
protected ArchiveOptions Options { get; }
11+
12+
public ArchiveFile(ArchiveOptions options)
13+
{
14+
ArgumentNullException.ThrowIfNull(options);
15+
16+
Options = options;
17+
Records = [];
18+
}
19+
20+
/// <summary>
21+
/// Gets the records in the archive
22+
/// </summary>
23+
public List<ArchiveRecord> Records { get; }
24+
25+
/// <summary>
26+
/// Gets the path of the archive
27+
/// </summary>
28+
public string Name => Options.Path;
29+
30+
/// <summary>
31+
/// Opens the archive
32+
/// </summary>
33+
public abstract void Open();
34+
35+
/// <summary>
36+
/// Reads a file from the archive
37+
/// </summary>
38+
/// <param name="record">The record to read</param>
39+
/// <returns>The contents of the file</returns>
40+
public abstract byte[] Read(ArchiveRecord record);
41+
42+
/// <summary>
43+
/// Gets all files in the specified directory
44+
/// </summary>
45+
/// <param name="path">The path to the directory</param>
46+
/// <returns>The contents of the directory</returns>
47+
public List<ArchiveRecord> GetFiles(string path)
48+
{
49+
var hierachy = Split(path);
50+
51+
// Find all records where the path is equal, removing the file name
52+
return Records.Where(record => record.Name != null && Split(record.Name).SkipLast(1).SequenceEqual(hierachy)).ToList();
53+
}
54+
55+
/// <summary>
56+
/// Gets the file name of a archive part
57+
/// </summary>
58+
/// <param name="part">The part</param>
59+
/// <param name="extension">The extension</param>
60+
/// <returns>The formatted file name</returns>
61+
protected string GetFilePart(int part, string extension = "")
62+
{
63+
var path = Options.Path[..^extension.Length];
64+
65+
var name = Path.GetFileNameWithoutExtension(path);
66+
var directory = Path.GetDirectoryName(path);
67+
68+
return Path.Combine(directory, name + "." + part.ToString("000") + extension);
69+
}
70+
71+
private static string[] Split(string path)
72+
{
73+
return path.Split('\\', StringSplitOptions.RemoveEmptyEntries);
74+
}
75+
}
76+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using Yura.Shared.IO;
2+
using Yura.Shared.Util;
3+
4+
namespace Yura.Shared.Archive
5+
{
6+
public class ArchiveOptions
7+
{
8+
/// <summary>
9+
/// Gets or sets the path to the archive
10+
/// </summary>
11+
public string Path { get; set; }
12+
13+
/// <summary>
14+
/// Gets or sets the archive endianness
15+
/// </summary>
16+
public Endianness Endianness { get; set; }
17+
18+
/// <summary>
19+
/// Gets or sets the archive alignment
20+
/// </summary>
21+
public int Alignment { get; set; }
22+
23+
/// <summary>
24+
/// Gets or sets the file list
25+
/// </summary>
26+
public FileList? FileList { get; set; }
27+
}
28+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
namespace Yura.Shared.Archive
2+
{
3+
public class ArchiveRecord
4+
{
5+
/// <summary>
6+
/// Gets or sets the hash of the file name
7+
/// </summary>
8+
public ulong Hash { get; internal set; }
9+
10+
/// <summary>
11+
/// Gets or sets the file name
12+
/// </summary>
13+
public string? Name { get; internal set; }
14+
15+
/// <summary>
16+
/// Gets or sets the file size
17+
/// </summary>
18+
public uint Size { get; internal set; }
19+
20+
/// <summary>
21+
/// Gets the file specialisation mask
22+
/// </summary>
23+
public ulong Specialisation { get; internal set; }
24+
}
25+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace Yura.Shared.Archive
8+
{
9+
public class DefianceArchive : ArchiveFile
10+
{
11+
public DefianceArchive(ArchiveOptions options) : base(options)
12+
{
13+
}
14+
15+
public override void Open()
16+
{
17+
throw new NotImplementedException();
18+
}
19+
20+
public override byte[] Read(ArchiveRecord record)
21+
{
22+
throw new NotImplementedException();
23+
}
24+
}
25+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace Yura.Shared.Archive
8+
{
9+
public class DeusExArchive : ArchiveFile
10+
{
11+
public DeusExArchive(ArchiveOptions options) : base(options)
12+
{
13+
}
14+
15+
public override void Open()
16+
{
17+
throw new NotImplementedException();
18+
}
19+
20+
public override byte[] Read(ArchiveRecord record)
21+
{
22+
throw new NotImplementedException();
23+
}
24+
}
25+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
using Yura.Shared.IO;
8+
9+
namespace Yura.Shared.Archive
10+
{
11+
public class LegendArchive : ArchiveFile
12+
{
13+
public LegendArchive(ArchiveOptions options) : base(options)
14+
{
15+
}
16+
17+
public override void Open()
18+
{
19+
var stream = File.OpenRead(Options.Path);
20+
var reader = new DataReader(stream, Options.Endianness);
21+
22+
// Read the number of files
23+
var numRecords = reader.ReadUInt32();
24+
25+
if (numRecords > 1_000_000)
26+
{
27+
throw new ArgumentException("Bigfile has more than a million files, did you select the right endianness?");
28+
}
29+
30+
// Read the file hashes
31+
var hashes = new uint[numRecords];
32+
33+
for (var i = 0; i < numRecords; i++)
34+
{
35+
hashes[i] = reader.ReadUInt32();
36+
}
37+
38+
// Read the file records
39+
for (var i = 0; i < numRecords; i++)
40+
{
41+
var record = new Record
42+
{
43+
Hash = hashes[i],
44+
45+
Size = reader.ReadUInt32(),
46+
Offset = reader.ReadUInt32(),
47+
Specialisation = reader.ReadUInt32(),
48+
CompressedSize = reader.ReadUInt32(),
49+
};
50+
51+
Records.Add(record);
52+
}
53+
54+
stream.Close();
55+
}
56+
57+
public override byte[] Read(ArchiveRecord record)
58+
{
59+
var file = record as Record;
60+
61+
// Calculate the location of the file
62+
var offset = (long)file.Offset << 11;
63+
var part = offset / Options.Alignment;
64+
65+
var path = GetFilePart((int)part);
66+
67+
// Read the file
68+
var stream = File.OpenRead(path);
69+
var data = new byte[file.Size];
70+
71+
stream.Position = offset % Options.Alignment;
72+
stream.ReadExactly(data);
73+
74+
stream.Close();
75+
76+
return data;
77+
}
78+
79+
private class Record : ArchiveRecord
80+
{
81+
public uint Offset { get; set; }
82+
public uint CompressedSize { get; set; }
83+
}
84+
}
85+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
using System;
2+
using System.IO;
3+
using Yura.Shared.IO;
4+
using Yura.Shared.Util;
5+
6+
namespace Yura.Shared.Archive
7+
{
8+
public class TigerArchive : ArchiveFile
9+
{
10+
public TigerArchive(ArchiveOptions options) : base(options)
11+
{
12+
}
13+
14+
public override void Open()
15+
{
16+
var stream = File.OpenRead(Options.Path);
17+
var reader = new DataReader(stream, Options.Endianness);
18+
19+
// Read the magic
20+
var magic = reader.ReadUInt32();
21+
22+
if (magic != 0x53464154 && magic != 0x54414653)
23+
{
24+
throw new Exception("File is not a tiger archive file");
25+
}
26+
27+
var version = reader.ReadUInt32();
28+
29+
// Check for version 3, 4 or 5
30+
if (version < 3 || version > 5)
31+
{
32+
throw new NotImplementedException($"Tiger archive version {version} is not supported");
33+
}
34+
35+
// Load the file list with the correct hashing algorithm
36+
Options.FileList?.Load(version < 5 ? HashAlgorithm.Crc32 : HashAlgorithm.Fnv1a);
37+
38+
var numArchives = reader.ReadUInt32();
39+
var numRecords = reader.ReadUInt32();
40+
41+
reader.Position += 4;
42+
43+
// Skip over the config name
44+
reader.Position += 32;
45+
46+
// Read the file records
47+
for (var i = 0; i < numRecords; i++)
48+
{
49+
var record = new Record();
50+
51+
if (version < 5)
52+
{
53+
record.Hash = reader.ReadUInt32();
54+
record.Specialisation = reader.ReadUInt32();
55+
record.Size = reader.ReadUInt32();
56+
}
57+
else
58+
{
59+
record.Hash = reader.ReadUInt64();
60+
record.Specialisation = reader.ReadUInt64();
61+
record.Size = reader.ReadUInt32();
62+
}
63+
64+
// Skip in TR2 and later
65+
if (version >= 4)
66+
{
67+
reader.Position += 4;
68+
}
69+
70+
// TRAS
71+
if (version == 3)
72+
{
73+
var packedOffset = reader.ReadUInt32();
74+
75+
record.Index = packedOffset & 0xF;
76+
record.Offset = packedOffset & 0xFFFFF800;
77+
}
78+
// TR2
79+
else if (version == 4)
80+
{
81+
var packedOffset = reader.ReadUInt64();
82+
83+
record.Index = packedOffset & 0xFFFF;
84+
record.Offset = (packedOffset >> 32) & 0xFFFFFFFF;
85+
}
86+
// TR11
87+
else if (version == 5)
88+
{
89+
var packedOffset = reader.ReadUInt64();
90+
91+
record.Index = packedOffset & 0xFFFF;
92+
record.Offset = (packedOffset >> 32) & 0xFFFFFFFF;
93+
}
94+
95+
Records.Add(record);
96+
}
97+
98+
stream.Close();
99+
}
100+
101+
public override byte[] Read(ArchiveRecord record)
102+
{
103+
var file = record as Record;
104+
105+
var path = GetFilePart((int)file.Index, ".tiger");
106+
107+
// Read the file
108+
var stream = File.OpenRead(path);
109+
var data = new byte[file.Size];
110+
111+
stream.Position = (long)file.Offset;
112+
stream.ReadExactly(data);
113+
114+
stream.Close();
115+
116+
return data;
117+
}
118+
119+
private class Record : ArchiveRecord
120+
{
121+
public ulong Index { get; set; }
122+
public ulong Offset { get; set; }
123+
}
124+
}
125+
}

0 commit comments

Comments
 (0)