Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions TACTLib/Client/HandlerArgs/ClientCreateArgs_Tank.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,27 @@ public class ClientCreateArgs_Tank : IHandlerArgs {
/// Load manifest files. Flag here to allow disabling for development purposes
/// </summary>
public bool LoadManifest { get; set; } = true;

/// <summary>
/// Load bundles for lookup. Flag here to allow disabling for development purposes
/// </summary>
public bool LoadBundlesForLookup { get; set; } = true;

public const string REGION_DEV = "DEV";
public const string REGION_CN = "CN";
public const string REGION_KR = "KR";
public const string REGION_TW = "TW";
public const string REGION_SG = "SG";
public const string REGION_EU = "EU";
public const string REGION_US = "US";

public const string PLATFORM_WIN = "Win";
public const string PLATFORM_WINPRISM = "WinPrism";
public const string PLATFORM_XBOXONE = "XB1";
public const string PLATFORM_XBOXSERIES = "XSX";
public const string PLATFORM_PLAYSTATION4 = "PS4";
public const string PLATFORM_PLAYSTATION5 = "PS5";
public const string PLATFORM_NINTENDOSWITCH = "NX";
public const string PLATFORM_NINTENDOSWITCH2 = "NX2";
}
}
122 changes: 83 additions & 39 deletions TACTLib/Config/BuildConfig.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Text;

namespace TACTLib.Config {
public class BuildConfig : Config {
Expand All @@ -10,67 +13,108 @@ public class BuildConfig : Config {
public FileRecord? Download;
public FileRecord Encoding;
public SizeRecord? EncodingSize;

public FileRecord? VFSRoot;

public SizeRecord? VFSRootSize;
public IReadOnlyList<FileRecord>? VFSManifests;
public IReadOnlyList<SizeRecord>? VFSManifestsSize;

public string GetBuildName() => Values["build-name"][0];

public BuildConfig(Stream? stream) : base(stream) {
GetFileRecord("root", out var root);
GetFileRecord("install", out Install);
GetFileRecord("patch", out Patch);
GetFileRecord("download", out Download);
GetFileRecord("encoding", out var encoding);
GetSizeRecord("encoding-size", out EncodingSize);
GetFileRecord("vfs-root", out VFSRoot);

if (root == null) throw new NullReferenceException(nameof(root));
Root = root;

if (encoding == null) throw new NullReferenceException(nameof(encoding));
Encoding = encoding;
GetRecord("root", out Root);
TryGetRecord("install", out Install);
TryGetRecord("patch", out Patch);
TryGetRecord("download", out Download);
GetRecord("encoding", out Encoding);
TryGetRecord("encoding-size", out EncodingSize);

TryGetRecord("vfs-root", out VFSRoot);
TryGetRecord("vfs-root-size", out VFSRootSize);
TryGetRecords("vfs-{0}", 1, out VFSManifests);
TryGetRecords("vfs-{0}-size", 1, out VFSManifestsSize);
}

private void GetFileRecord(string key, out FileRecord? @out) {
if (!Values.TryGetValue(key, out var list)) {
@out = null;
return;
private void GetRecord<T>(string key, out T record) where T : IBuildConfigRecord<T> {
if (!TryGetRecord(key, out T? res)) {
throw new KeyNotFoundException($"Failed to find \"{key}\" in the build config.");
}
@out = GetFileRecord(list);

record = res;
}
private void GetSizeRecord(string key, out SizeRecord? @out) {
if (!Values.TryGetValue(key, out var list)) {
@out = null;
return;

private bool TryGetRecord<T>(string key, [NotNullWhen(true)] out T? record) where T : IBuildConfigRecord<T> {
if (!Values.TryGetValue(key, out var vals)) {
record = default;
return false;
}
@out = new SizeRecord {
ContentSize = int.Parse(list[0]),
EncodedSize = int.Parse(list[1])
};

record = T.Decode(vals);
return true;
}

private void GetRecords<T>(string key, int baseIter, out IReadOnlyList<T> record) where T : IBuildConfigRecord<T> {
if (!TryGetRecords(key, baseIter, out IReadOnlyList<T>? res)) {
throw new KeyNotFoundException($"Failed to find any \"{key}\" build config records.");
}

record = res;
}

private static FileRecord GetFileRecord(IReadOnlyList<string> vals) {
FileRecord record = new FileRecord();
private bool TryGetRecords<T>(string key, int baseIter, [MaybeNullWhen(false)] out IReadOnlyList<T> record) where T : IBuildConfigRecord<T> {
Debug.Assert(key.Contains("{0}"));

if (vals.Count > 0) {
record.ContentKey = CKey.FromString(vals[0]);
if (!Values.TryGetValue(string.Format(key, baseIter++), out var baseVals)) {
record = null;
return false;
}

if (vals.Count > 1) {
record.EncodingKey = FullEKey.FromString(vals[1]);
var values = new List<T> { T.Decode(baseVals) };
record = values;
while (true) {
if (!Values.TryGetValue(string.Format(key, baseIter++), out var vals)) {
break;
}

values.Add(T.Decode(vals));
}

return record;

return true;
}

private interface IBuildConfigRecord<out T> where T : IBuildConfigRecord<T> {
abstract static T Decode(List<string> vals);
}

public class FileRecord {
public class FileRecord : IBuildConfigRecord<FileRecord> {
public CKey ContentKey;
public FullEKey EncodingKey;

public static FileRecord Decode(List<string> vals) {
FileRecord record = new FileRecord();

if (vals.Count > 0) {
record.ContentKey = CKey.FromString(vals[0]);
}

if (vals.Count > 1) {
record.EncodingKey = FullEKey.FromString(vals[1]);
}

return record;
}
}

public class SizeRecord {
public class SizeRecord : IBuildConfigRecord<SizeRecord> {
public int ContentSize;
public int EncodedSize;

public static SizeRecord Decode(List<string> vals) {
return new SizeRecord {
ContentSize = int.Parse(vals[0]),
EncodedSize = int.Parse(vals[1])
};
}
}
}
}
1 change: 1 addition & 0 deletions TACTLib/Core/VFS/VFSFile.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace TACTLib.Core.VFS {
public class VFSFile {
public string? Name;
public int EncodedSize;
public int ContentSize;
public CKey EKey;
public int Offset;
Expand Down
2 changes: 2 additions & 0 deletions TACTLib/Core/VFS/VFSManifestReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -363,8 +363,10 @@ private static VFSFile ReadVfsSpanEntries(Manifest manifest, BinaryReader reader

reader.BaseStream.Position = cftFileEntry;
var eKey = reader.Read<CKey>();
var encSize = reader.ReadInt32BE();
VFSFile file = new VFSFile {
Offset = fileOffset,
EncodedSize = encSize,
ContentSize = spanSize,
Name = null,
EKey = eKey
Expand Down
38 changes: 28 additions & 10 deletions TACTLib/Helpers/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
namespace TACTLib.Helpers {
public static class Extensions {
#region BinaryReader

public static void DefinitelyRead(this Stream stream, Span<byte> buffer)
{
stream.ReadExactly(buffer);
Expand All @@ -16,7 +16,7 @@ public static void DefinitelyRead(this BinaryReader reader, Span<byte> buffer)
{
DefinitelyRead(reader.BaseStream, buffer);
}

/// <summary>
/// Read struct from a BinaryReader
/// </summary>
Expand All @@ -27,14 +27,14 @@ public static T Read<T>(this BinaryReader reader) where T : unmanaged
{
return reader.BaseStream.Read<T>();
}

public static unsafe T Read<T>(this Stream stream) where T : unmanaged
{
var result = default(T);
stream.DefinitelyRead(new Span<byte>(&result, sizeof(T)));
return result;
}

/// <summary>
/// Read array of structs from a reader
/// </summary>
Expand All @@ -46,16 +46,34 @@ public static T[] ReadArray<T>(this BinaryReader reader, int count) where T : un
{
return reader.BaseStream.ReadArray<T>(count);
}

public static T[] ReadArray<T>(this Stream stream, int count) where T : unmanaged
{
if (count == 0) return Array.Empty<T>();

var result = new T[count];
stream.DefinitelyRead(result.AsSpan().AsBytes());
return result;
}

/// <summary>
/// Peek a value from the stream without incrementing the position
/// </summary>
/// <param name="reader">Target reader</param>
/// <typeparam name="T">Struct to read</typeparam>
/// <returns>Read struct</returns>
public static T Peek<T>(this BinaryReader reader) where T : unmanaged
{
return reader.BaseStream.Peek<T>();
}

public static unsafe T Peek<T>(this Stream stream) where T : unmanaged
{
var value = stream.Read<T>();
stream.Position -= sizeof(T);
return value;
}

/// <summary>
/// Write a struct to a BinaryWriter
/// </summary>
Expand All @@ -67,7 +85,7 @@ public static void Write<T>(this BinaryWriter writer, T @struct) where T : unman
var bytes = MemoryMarshal.CreateReadOnlySpan(ref @struct, 1).AsBytes();
writer.Write(bytes);
}

/// <summary>
/// Write an array of structs to a BinaryWriter
/// </summary>
Expand All @@ -79,7 +97,7 @@ public static void WriteStructArray<T>(this BinaryWriter writer, T[] @struct) wh
var bytes = @struct.AsSpan().AsBytes();
writer.Write(bytes);
}

/// <summary>Read a big endian 32-bit int</summary>
// ReSharper disable once InconsistentNaming
public static int ReadInt32BE(this BinaryReader reader)
Expand All @@ -93,7 +111,7 @@ public static short ReadInt16BE(this BinaryReader reader)
{
return (short)ReadUInt16BE(reader);
}

/// <summary>Read a big endian 16-bit uint</summary>
// ReSharper disable once InconsistentNaming
public static ushort ReadUInt16BE(this BinaryReader reader)
Expand All @@ -110,7 +128,7 @@ public static int ReadInt24BE(this BinaryReader reader)
return s.ToInt();
}
#endregion

/// <summary>Convert <see cref="Span{T}"/> to a hexadecimal string</summary>
public static string ToHexString(this ReadOnlySpan<byte> data)
{
Expand Down
Loading