Skip to content

Commit

Permalink
Merge pull request #3 from TheIndra55/refactor
Browse files Browse the repository at this point in the history
Refactor
  • Loading branch information
TheIndra55 authored Jan 25, 2025
2 parents 68d3e25 + 316770f commit da92832
Show file tree
Hide file tree
Showing 38 changed files with 1,231 additions and 983 deletions.
95 changes: 95 additions & 0 deletions Yura.Shared/Archive/ArchiveFile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace Yura.Shared.Archive
{
public abstract class ArchiveFile
{
protected ArchiveOptions Options { get; }

public ArchiveFile(ArchiveOptions options)
{
ArgumentNullException.ThrowIfNull(options);

Options = options;
Records = [];
}

/// <summary>
/// Gets the records in the archive
/// </summary>
public List<ArchiveRecord> Records { get; }

/// <summary>
/// Gets the path of the archive
/// </summary>
public string Name => Options.Path;

/// <summary>
/// Opens the archive
/// </summary>
public abstract void Open();

/// <summary>
/// Reads a file from the archive
/// </summary>
/// <param name="record">The record to read</param>
/// <returns>The contents of the file</returns>
public abstract byte[] Read(ArchiveRecord record);

/// <summary>
/// Gets all files in the specified directory
/// </summary>
/// <param name="path">The path to the directory</param>
/// <returns>The contents of the directory</returns>
public List<ArchiveRecord> GetFiles(string path)
{
var hierachy = Split(path);

// Find all records where the path is equal, removing the file name
return Records.Where(record => record.Name != null && Split(record.Name).SkipLast(1).SequenceEqual(hierachy)).ToList();
}

/// <summary>
/// Gets the file name of a archive part
/// </summary>
/// <param name="part">The part</param>
/// <param name="extension">The extension</param>
/// <returns>The formatted file name</returns>
protected string GetFilePart(int part, string extension = "")
{
var path = Options.Path[..^extension.Length];

var name = Path.GetFileNameWithoutExtension(path);
var directory = Path.GetDirectoryName(path);

return Path.Combine(directory, name + "." + part.ToString("000") + extension);

Check warning on line 68 in Yura.Shared/Archive/ArchiveFile.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'path1' in 'string Path.Combine(string path1, string path2)'.
}

/// <summary>
/// From an offset and the alignment get the physical file and offset
/// </summary>
/// <param name="offset">The offset of the file</param>
/// <returns>The file and offset</returns>
protected (string, long) GetFileAndOffset(long offset)
{
// Check whether the archive is split over multiple files
if (Path.GetExtension(Name) == ".000")
{
var part = offset / Options.Alignment;
var path = GetFilePart((int)part);

return (path, offset % Options.Alignment);
}

return (Name, offset);
}

private static string[] Split(string path)
{
return path.Split('\\', StringSplitOptions.RemoveEmptyEntries);
}
}
}
33 changes: 33 additions & 0 deletions Yura.Shared/Archive/ArchiveOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Yura.Shared.IO;
using Yura.Shared.Util;

namespace Yura.Shared.Archive
{
public class ArchiveOptions
{
/// <summary>
/// Gets or sets the path to the archive
/// </summary>
public string Path { get; set; }

Check warning on line 11 in Yura.Shared/Archive/ArchiveOptions.cs

View workflow job for this annotation

GitHub Actions / build

Non-nullable property 'Path' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.

/// <summary>
/// Gets or sets the archive endianness
/// </summary>
public Endianness Endianness { get; set; }

/// <summary>
/// Gets or sets the archive platform
/// </summary>
public Platform Platform { get; set; }

/// <summary>
/// Gets or sets the archive alignment
/// </summary>
public int Alignment { get; set; }

/// <summary>
/// Gets or sets the file list
/// </summary>
public FileList? FileList { get; set; }
}
}
25 changes: 25 additions & 0 deletions Yura.Shared/Archive/ArchiveRecord.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
namespace Yura.Shared.Archive
{
public class ArchiveRecord
{
/// <summary>
/// Gets or sets the hash of the file name
/// </summary>
public ulong Hash { get; internal set; }

/// <summary>
/// Gets or sets the file name
/// </summary>
public string? Name { get; internal set; }

/// <summary>
/// Gets or sets the file size
/// </summary>
public uint Size { get; internal set; }

/// <summary>
/// Gets the file specialisation mask
/// </summary>
public ulong Specialisation { get; internal set; }
}
}
72 changes: 72 additions & 0 deletions Yura.Shared/Archive/DefianceArchive.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System.IO;
using Yura.Shared.IO;

namespace Yura.Shared.Archive
{
public class DefianceArchive : ArchiveFile
{
public DefianceArchive(ArchiveOptions options) : base(options)
{
}

public override void Open()
{
var stream = File.OpenRead(Options.Path);
var reader = new DataReader(stream, Options.Endianness);

// Read the number of files
var numFiles = reader.ReadUInt16();

stream.Position += 2;

// Read the file name hashes
var hashes = new uint[numFiles];

for (var i = 0; i < numFiles; i++)
{
hashes[i] = reader.ReadUInt32();
}

// Read the file records
for (var i = 0; i < numFiles; i++)
{
var record = new Record
{
Hash = hashes[i],

Size = reader.ReadUInt32(),
Offset = reader.ReadUInt32()
};

reader.Position += 4;

Records.Add(record);
}

stream.Close();
}

public override byte[] Read(ArchiveRecord record)
{
var file = record as Record;

var (path, offset) = GetFileAndOffset(file.Offset);

Check warning on line 53 in Yura.Shared/Archive/DefianceArchive.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.

// Read the file
var stream = File.OpenRead(path);
var data = new byte[file.Size];

stream.Position = offset;
stream.ReadExactly(data);

stream.Close();

return data;
}

private class Record : ArchiveRecord
{
public uint Offset { get; set; }
}
}
}
89 changes: 89 additions & 0 deletions Yura.Shared/Archive/DeusExArchive.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using System;
using System.IO;
using Yura.Shared.IO;

namespace Yura.Shared.Archive
{
public class DeusExArchive : ArchiveFile
{
private uint _alignment;

public DeusExArchive(ArchiveOptions options) : base(options)
{
}

public override void Open()
{
var stream = File.OpenRead(Options.Path);
var reader = new DataReader(stream, Options.Endianness);

// Read the header
_alignment = reader.ReadUInt32();

// Skip over the config name
reader.Position += 64;

// Read the number of files
var numRecords = reader.ReadUInt32();

if (numRecords > 1_000_000)
{
throw new ArgumentException("Bigfile has more than a million files, did you select the right endianness?");
}

// Read the file name hashes
var hashes = new uint[numRecords];

for (var i = 0; i < numRecords; i++)
{
hashes[i] = reader.ReadUInt32();
}

// Read the file records
for (var i = 0; i < numRecords; i++)
{
var record = new Record
{
Hash = hashes[i],

Size = reader.ReadUInt32(),
Offset = reader.ReadUInt32(),
Specialisation = reader.ReadUInt32(),
CompressedSize = reader.ReadUInt32(),
};

Records.Add(record);
}

stream.Close();
}

public override byte[] Read(ArchiveRecord record)
{
var file = record as Record;

// Calculate the location of the file
var offset = (long)file.Offset << 11;

Check warning on line 66 in Yura.Shared/Archive/DeusExArchive.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
var part = offset / _alignment;

var path = GetFilePart((int)part);

// Read the file
var stream = File.OpenRead(path);
var data = new byte[file.Size];

stream.Position = offset % _alignment;
stream.ReadExactly(data);

stream.Close();

return data;
}

private class Record : ArchiveRecord
{
public uint Offset { get; set; }
public uint CompressedSize { get; set; }
}
}
}
Loading

0 comments on commit da92832

Please sign in to comment.