├── TACTLib
├── Core
│ ├── Product
│ │ ├── IProductHandler.cs
│ │ ├── Fenris
│ │ │ ├── SnoFile.cs
│ │ │ ├── SnoType.cs
│ │ │ ├── SnoChild.cs
│ │ │ ├── SnoHandle.cs
│ │ │ ├── EncryptedNameDict.cs
│ │ │ ├── EncryptedSnos.cs
│ │ │ ├── ReplacedSnos.cs
│ │ │ ├── SharedPayloadsMapping.cs
│ │ │ ├── SnoManifest.cs
│ │ │ └── CoreTOC.cs
│ │ ├── MNDX
│ │ │ ├── CASCSearchPhase.cs
│ │ │ ├── TRIPLET.cs
│ │ │ ├── MARInfo.cs
│ │ │ ├── MNDXHeader.cs
│ │ │ ├── CASC_ROOT_ENTRY_MNDX.cs
│ │ │ ├── NAME_FRAG.cs
│ │ │ ├── PATH_STOP.cs
│ │ │ ├── Extensions.cs
│ │ │ ├── MNDXSearchResult.cs
│ │ │ ├── TBitEntryArray.cs
│ │ │ └── SearchBuffer.cs
│ │ ├── WorldOfWarcraftV6
│ │ │ ├── CASWarcraftV6Record.cs
│ │ │ ├── ContentFlags.cs
│ │ │ ├── CASWarcraftV6.cs
│ │ │ └── ProductHandler_WorldOfWarcraftV6.cs
│ │ ├── ProductHandlerAttribute.cs
│ │ ├── Tank
│ │ │ ├── IManifestCrypto.cs
│ │ │ ├── Bundle.cs
│ │ │ ├── ProductHandler_Tank.Bundles.cs
│ │ │ ├── CMF
│ │ │ │ ├── ProCMF_37703.cs
│ │ │ │ ├── ProCMF_38459.cs
│ │ │ │ ├── ProCMF_39823.cs
│ │ │ │ ├── ProCMF_37865.cs
│ │ │ │ ├── ProCMF_42539.cs
│ │ │ │ ├── ProCMF_39028.cs
│ │ │ │ ├── ProCMF_35455.cs
│ │ │ │ ├── ProCMF_35780.cs
│ │ │ │ ├── ProCMF_38125.cs
│ │ │ │ ├── ProCMF_41713.cs
│ │ │ │ ├── ProCMF_42210.cs
│ │ │ │ ├── ProCMF_63778.cs
│ │ │ │ ├── ProCMF_67664.cs
│ │ │ │ ├── ProCMF_38248.cs
│ │ │ │ ├── ProCMF_38170.cs
│ │ │ │ ├── ProCMF_39083.cs
│ │ │ │ ├── ProCMF_63869.cs
│ │ │ │ ├── ProCMF_43515.cs
│ │ │ │ ├── ProCMF_63568.cs
│ │ │ │ ├── ProCMF_81410.cs
│ │ │ │ └── ProCMF_62065.cs
│ │ │ └── TRG
│ │ │ │ ├── ProTRG_71213.cs
│ │ │ │ └── ProTRG_81410.cs
│ │ ├── Locale.cs
│ │ ├── D2
│ │ │ └── ProductHandler_D2R.cs
│ │ ├── ProductHandlerFactory.cs
│ │ └── CommonV2
│ │ │ └── RootFile.cs
│ ├── VFS
│ │ ├── VFSCFile.cs
│ │ ├── VFSFile.cs
│ │ └── VFSFileTree.cs
│ ├── Key
│ │ ├── CASCKeyComparer.cs
│ │ ├── TruncatedKey.cs
│ │ └── FullKey.cs
│ └── ClientPatchManifest.cs
├── Client
│ ├── IHandlerArgs.cs
│ ├── HandlerArgs
│ │ ├── ClientCreateArgs_Fenris.cs
│ │ ├── ClientCreateArgs_WorldOfWarcraftV6.cs
│ │ └── ClientCreateArgs_Tank.cs
│ └── ConfigHandler.cs
├── GlobalUsings.cs
├── Config
│ ├── PatchConfig.cs
│ ├── CDNConfig.cs
│ ├── InstallationInfoFile.cs
│ ├── Config.cs
│ ├── BuildConfig.cs
│ └── InstallationInfo.cs
├── Container
│ ├── IContainerHandler.cs
│ └── StaticContainerHandler.cs
├── Utils.cs
├── Exceptions
│ ├── BLTEKeyException.cs
│ ├── UnsupportedBuildVersionException.cs
│ └── BLTEDecoderException.cs
├── Helpers
│ ├── UInt32BE.cs
│ ├── UInt16BE.cs
│ ├── UInt24BE.cs
│ ├── UInt40BE.cs
│ ├── PerfCounter.cs
│ ├── SpanHelper.cs
│ └── Extensions.cs
├── Agent
│ └── AgentDatabase.cs
├── Protocol
│ ├── ICDNClient.cs
│ ├── NGDP
│ │ ├── RibbitHttpClient.cs
│ │ ├── NGDPClient.cs
│ │ └── RibbitTcpClient.cs
│ └── HttpCDNClient.cs
├── Logger.cs
└── TACTLib.csproj
├── TACTLibTest
├── TACTLibTest.csproj
└── Program.cs
├── .github
├── dependabot.yml
└── workflows
│ └── dotnet.yml
├── LICENSE
├── TACTLib.sln
└── README.md
/TACTLib/Core/Product/IProductHandler.cs:
--------------------------------------------------------------------------------
1 | namespace TACTLib.Core.Product {
2 | public interface IProductHandler {
3 | }
4 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Fenris/SnoFile.cs:
--------------------------------------------------------------------------------
1 | namespace TACTLib.Core.Product.Fenris;
2 |
3 | public record SnoFile(uint Id, SnoType Type, uint SubId = 0);
--------------------------------------------------------------------------------
/TACTLib/Core/VFS/VFSCFile.cs:
--------------------------------------------------------------------------------
1 | namespace TACTLib.Core.VFS {
2 | public class VFSCFile : VFSFile {
3 | public CKey CKey;
4 | }
5 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Fenris/SnoType.cs:
--------------------------------------------------------------------------------
1 | namespace TACTLib.Core.Product.Fenris;
2 |
3 | public enum SnoType {
4 | Meta,
5 | Child,
6 | Payload,
7 | Paymid,
8 | Paylow,
9 | }
--------------------------------------------------------------------------------
/TACTLib/Client/IHandlerArgs.cs:
--------------------------------------------------------------------------------
1 | namespace TACTLib.Client {
2 | ///
3 | /// Dummy shim
4 | ///
5 | public interface IHandlerArgs {
6 |
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/TACTLib/GlobalUsings.cs:
--------------------------------------------------------------------------------
1 | global using CKey=TACTLib.Core.Key.FullKey;
2 | global using FullEKey=TACTLib.Core.Key.FullKey;
3 | global using MD5Key=TACTLib.Core.Key.FullKey;
4 | global using EKey=TACTLib.Core.Key.TruncatedKey;
5 |
--------------------------------------------------------------------------------
/TACTLib/Config/PatchConfig.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace TACTLib.Config {
4 | public class PatchConfig : Config {
5 | public PatchConfig(Stream? stream) : base(stream) {
6 |
7 | }
8 | }
9 | }
--------------------------------------------------------------------------------
/TACTLib/Core/VFS/VFSFile.cs:
--------------------------------------------------------------------------------
1 | namespace TACTLib.Core.VFS {
2 | public class VFSFile {
3 | public string? Name;
4 | public int ContentSize;
5 | public CKey EKey;
6 | public int Offset;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/TACTLib/Container/IContainerHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TACTLib.Container {
4 | public interface IContainerHandler {
5 | ArraySegment? OpenEKey(FullEKey ekey, int eSize);
6 | bool CheckResidency(FullEKey ekey);
7 | }
8 | }
--------------------------------------------------------------------------------
/TACTLib/Utils.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TACTLib {
4 | public static class Utils {
5 | public static byte[] StringToByteArray(ReadOnlySpan str) {
6 | // todo: span
7 | return Convert.FromHexString(str);
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Fenris/SnoChild.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 |
3 | namespace TACTLib.Core.Product.Fenris;
4 |
5 | [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x8)]
6 | public record struct SnoChild {
7 | public uint SnoId;
8 | public uint SubId;
9 | }
10 |
--------------------------------------------------------------------------------
/TACTLibTest/TACTLibTest.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net9.0
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/TACTLib/Client/HandlerArgs/ClientCreateArgs_Fenris.cs:
--------------------------------------------------------------------------------
1 | namespace TACTLib.Client.HandlerArgs;
2 |
3 | public class ClientCreateArgs_Fenris : IHandlerArgs {
4 | ///
5 | /// Language to use for base language overrides.
6 | ///
7 | public string? BaseLanguage { get; set; }
8 | }
9 |
--------------------------------------------------------------------------------
/TACTLib/Config/CDNConfig.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 |
4 | namespace TACTLib.Config {
5 | public class CDNConfig : Config {
6 | public CDNConfig(Stream? stream) : base(stream) { }
7 |
8 | public List Archives => Values["archives"];
9 | }
10 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/MNDX/CASCSearchPhase.cs:
--------------------------------------------------------------------------------
1 | // https://raw.githubusercontent.com/WoW-Tools/CascLib/master/CascLib/RootHandlers/MNDXRootHandler.cs
2 |
3 | namespace TACTLib.Core.Product.MNDX
4 | {
5 | internal enum CASCSearchPhase
6 | {
7 | Initializing = 0,
8 | Searching = 2,
9 | Finished = 4
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/TACTLib/Core/Product/MNDX/TRIPLET.cs:
--------------------------------------------------------------------------------
1 | // https://raw.githubusercontent.com/WoW-Tools/CascLib/master/CascLib/RootHandlers/MNDXRootHandler.cs
2 |
3 | namespace TACTLib.Core.Product.MNDX
4 | {
5 | internal struct TRIPLET
6 | {
7 | public int BaseValue;
8 | public int Value2;
9 | public int Value3;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/TACTLib/Core/Product/WorldOfWarcraftV6/CASWarcraftV6Record.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 |
3 | namespace TACTLib.Core.Product.WorldOfWarcraftV6 {
4 |
5 | [StructLayout(LayoutKind.Sequential, Pack = 4)]
6 | public struct CASWarcraftV6Record {
7 | public CKey ContentKey;
8 | public ulong LookupHash;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/TACTLib/Core/Product/ProductHandlerAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TACTLib.Core.Product {
4 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
5 | public class ProductHandlerAttribute : Attribute {
6 | public TACTProduct Product;
7 |
8 | public ProductHandlerAttribute(TACTProduct product) {
9 | Product = product;
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/TACTLib/Core/Product/MNDX/MARInfo.cs:
--------------------------------------------------------------------------------
1 | // https://raw.githubusercontent.com/WoW-Tools/CascLib/master/CascLib/RootHandlers/MNDXRootHandler.cs
2 |
3 | namespace TACTLib.Core.Product.MNDX
4 | {
5 | internal struct MARInfo
6 | {
7 | public int MarIndex;
8 | public int MarDataSize;
9 | public int MarDataSizeHi;
10 | public int MarDataOffset;
11 | public int MarDataOffsetHi;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/TACTLib/Exceptions/BLTEKeyException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TACTLib.Exceptions {
4 | /// Thrown when the BLTE reader is missing a key
5 | [Serializable]
6 | public class BLTEKeyException : Exception {
7 | public ulong MissingKey;
8 |
9 | public BLTEKeyException(ulong key) : base($"unknown key {key:X16}") {
10 | MissingKey = key;
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/TACTLib/Core/Product/MNDX/MNDXHeader.cs:
--------------------------------------------------------------------------------
1 | // https://raw.githubusercontent.com/WoW-Tools/CascLib/master/CascLib/RootHandlers/MNDXRootHandler.cs
2 |
3 | namespace TACTLib.Core.Product.MNDX
4 | {
5 | internal struct MNDXHeader
6 | {
7 | public int Signature; // 'MNDX'
8 | public int HeaderVersion; // Must be <= 2
9 | public int FormatVersion;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Tank/IManifestCrypto.cs:
--------------------------------------------------------------------------------
1 | namespace TACTLib.Core.Product.Tank {
2 | public interface IManifestCrypto {
3 | byte[] Key(T header, int length);
4 | byte[] IV(T header, byte[] digest, int length);
5 | }
6 |
7 | public interface ICMFEncryptionProc : IManifestCrypto { }
8 |
9 | public interface ITRGEncryptionProc : IManifestCrypto { }
10 | }
--------------------------------------------------------------------------------
/TACTLib/Config/InstallationInfoFile.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 |
4 | namespace TACTLib.Config {
5 | public class InstallationInfoFile {
6 | public readonly List> Values;
7 |
8 | public InstallationInfoFile(string path) {
9 | using var reader = new StreamReader(path);
10 | Values = InstallationInfo.ParseToDict(reader);
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/TACTLib/Client/HandlerArgs/ClientCreateArgs_WorldOfWarcraftV6.cs:
--------------------------------------------------------------------------------
1 | namespace TACTLib.Client.HandlerArgs {
2 | ///
3 | ///
4 | /// Args specific to WorldOfWarcraftV6
5 | ///
6 | public class ClientCreateArgs_WorldOfWarcraftV6 : IHandlerArgs {
7 | ///
8 | /// Lookup list file to load
9 | ///
10 | public string? ListFile { get; set; } = null;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Fenris/SnoHandle.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 |
3 | namespace TACTLib.Core.Product.Fenris;
4 |
5 | [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x8)]
6 | public record struct SnoHandle {
7 | public uint Group; // 0xFFFFFFFF if none
8 | public uint Id; // 0 if none
9 | public static SnoHandle Invalid { get; } = new() { Group = uint.MaxValue };
10 | public bool IsInvalid => Group == uint.MaxValue && Id == 0;
11 | }
12 |
--------------------------------------------------------------------------------
/TACTLib/Exceptions/UnsupportedBuildVersionException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TACTLib.Exceptions {
4 | /// Thrown when we don't support specific build version
5 | [Serializable]
6 | public class UnsupportedBuildVersionException : Exception {
7 | public UnsupportedBuildVersionException(string message) : base(message) { }
8 | public UnsupportedBuildVersionException(string message, Exception inner) : base(message, inner) { }
9 | }
10 | }
--------------------------------------------------------------------------------
/TACTLib/Helpers/UInt32BE.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers.Binary;
3 | using System.Runtime.InteropServices;
4 |
5 | namespace TACTLib.Helpers {
6 | [StructLayout(LayoutKind.Sequential)]
7 | public struct UInt32BE
8 | {
9 | private uint m_data;
10 |
11 | public readonly uint ToInt()
12 | {
13 | if (BitConverter.IsLittleEndian) return BinaryPrimitives.ReverseEndianness(m_data);
14 | return m_data;
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/TACTLib/Helpers/UInt16BE.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers.Binary;
3 | using System.Runtime.InteropServices;
4 |
5 | namespace TACTLib.Helpers {
6 | [StructLayout(LayoutKind.Sequential)]
7 | public struct UInt16BE
8 | {
9 | private ushort m_data;
10 |
11 | public readonly ushort ToInt()
12 | {
13 | if (BitConverter.IsLittleEndian) return BinaryPrimitives.ReverseEndianness(m_data);
14 | return m_data;
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "nuget" # See documentation for possible values
9 | directory: "/" # Location of package manifests
10 | schedule:
11 | interval: "daily"
12 |
--------------------------------------------------------------------------------
/TACTLib/Core/Product/MNDX/CASC_ROOT_ENTRY_MNDX.cs:
--------------------------------------------------------------------------------
1 |
2 |
3 | // https://raw.githubusercontent.com/WoW-Tools/CascLib/master/CascLib/RootHandlers/MNDXRootHandler.cs
4 |
5 | namespace TACTLib.Core.Product.MNDX
6 | {
7 | internal class CASC_ROOT_ENTRY_MNDX
8 | {
9 | public CKey MD5; // Encoding key for the file
10 | public int Flags; // High 8 bits: Flags, low 24 bits: package index
11 | public int FileSize; // Uncompressed file size, in bytes
12 | public CASC_ROOT_ENTRY_MNDX? Next;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/TACTLib/Core/Product/MNDX/NAME_FRAG.cs:
--------------------------------------------------------------------------------
1 | // https://raw.githubusercontent.com/WoW-Tools/CascLib/master/CascLib/RootHandlers/MNDXRootHandler.cs
2 |
3 | namespace TACTLib.Core.Product.MNDX
4 | {
5 | internal struct NAME_FRAG
6 | {
7 | public int ItemIndex; // Back index to various tables
8 | public int NextIndex; // The following item index
9 | public int FragOffs; // Higher 24 bits are 0xFFFFFF00 --> A single matching character
10 | // Otherwise --> Offset to the name fragment table
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/TACTLib/Helpers/UInt24BE.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace TACTLib.Helpers
5 | {
6 | [StructLayout(LayoutKind.Sequential)]
7 | public struct UInt24BE
8 | {
9 | private byte m_a;
10 | private byte m_b;
11 | private byte m_c;
12 |
13 | public readonly int ToInt()
14 | {
15 | if (BitConverter.IsLittleEndian)
16 | {
17 | return m_c | (m_b << 8) | (m_a << 16);
18 | }
19 | throw new NotImplementedException();
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/TACTLib/Exceptions/BLTEDecoderException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TACTLib.Exceptions {
4 | /// Thrown when the BLTE reader encounters invalid data
5 | [Serializable]
6 | public class BLTEDecoderException : Exception {
7 | public readonly byte[]? BLTEData;
8 |
9 | public BLTEDecoderException(byte[]? data, string message) : base(message) {
10 | BLTEData = data;
11 | }
12 |
13 | public BLTEDecoderException(byte[]? data, string fmt, params object[] args) : this(data, string.Format(fmt, args)) { }
14 |
15 | public byte[]? GetBLTEData() => BLTEData;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/TACTLib/Core/Product/WorldOfWarcraftV6/ContentFlags.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TACTLib.Core.Product.WorldOfWarcraftV6 {
4 | [Flags]
5 | public enum ContentFlags : uint {
6 | None = 0,
7 | F00000001 = 0x1,
8 | F00000002 = 0x2,
9 | F00000004 = 0x4,
10 | F00000008 = 0x8, // added in 7.2.0.23436
11 | F00000010 = 0x10, // added in 7.2.0.23436
12 | LowViolence = 0x80, // many models have this flag
13 | F10000000 = 0x10000000,
14 | F20000000 = 0x20000000, // added in 21737
15 | Bundle = 0x40000000,
16 | NoCompression = 0x80000000 // sounds have this flag
17 | }
18 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/MNDX/PATH_STOP.cs:
--------------------------------------------------------------------------------
1 | // https://raw.githubusercontent.com/WoW-Tools/CascLib/master/CascLib/RootHandlers/MNDXRootHandler.cs
2 |
3 | namespace TACTLib.Core.Product.MNDX
4 | {
5 | internal class PATH_STOP
6 | {
7 | public int ItemIndex { get; set; }
8 | public int Field_4 { get; set; }
9 | public int Field_8 { get; set; }
10 | public int Field_C { get; set; }
11 | public int Field_10 { get; set; }
12 |
13 | public PATH_STOP()
14 | {
15 | ItemIndex = 0;
16 | Field_4 = 0;
17 | Field_8 = 0;
18 | Field_C = -1;
19 | Field_10 = -1;
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/TACTLibTest/Program.cs:
--------------------------------------------------------------------------------
1 | using TACTLib;
2 | using TACTLib.Client;
3 |
4 | namespace TACTLibTest {
5 | internal static class Program {
6 | public static void Main(string[] args) {
7 | //const string path = @"C:\ow\game\Overwatch\";
8 | //const string path = @"D:\Games\Call of Duty Black Ops 4";
9 |
10 | Logger.RegisterBasic();
11 |
12 | ClientHandler onlineClientHandler = new ClientHandler(args[0], new ClientCreateArgs {
13 | Product = "pro",
14 | UseContainer = true,
15 | LoadSupportKeyring = true
16 | });
17 | Logger.Info("LOAD", onlineClientHandler.Product.ToString("G"));
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/TACTLib/Helpers/UInt40BE.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers.Binary;
3 | using System.Runtime.InteropServices;
4 |
5 | namespace TACTLib.Helpers {
6 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
7 | public struct UInt40BE
8 | {
9 | private byte m_high;
10 | private uint m_low;
11 | //private fixed byte val[5];
12 |
13 | public readonly ulong ToInt() {
14 | //return val[4] | (ulong)val[3] << 8 | (ulong)val[2] << 16 | (ulong)val[1] << 24 | (ulong)val[0] << 32;
15 | if (BitConverter.IsLittleEndian) {
16 | return (ulong) m_high << 32 | BinaryPrimitives.ReverseEndianness(m_low);
17 | }
18 | throw new NotImplementedException();
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Locale.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 |
4 | namespace TACTLib.Core.Product {
5 | [Flags]
6 | [SuppressMessage("ReSharper", "InconsistentNaming")]
7 | public enum Locale : uint {
8 | All = 0xFFFFFFFF,
9 | None = 0,
10 | Internal = 0x1,
11 | enUS = 0x2,
12 | koKR = 0x4,
13 | Unused = 0x8,
14 | frFR = 0x10,
15 | deDE = 0x20,
16 | zhCN = 0x40,
17 | esES = 0x80,
18 | zhTW = 0x100,
19 | enGB = 0x200,
20 | enCN = 0x400,
21 | enTW = 0x800,
22 | esMX = 0x1000,
23 | ruRU = 0x2000,
24 | ptBR = 0x4000,
25 | itIT = 0x8000,
26 | ptPT = 0x10000,
27 | jaJP = 0x20000
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/TACTLib/Helpers/PerfCounter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 |
4 | namespace TACTLib.Helpers {
5 | /// Performance tracker
6 | public sealed class PerfCounter : IDisposable {
7 | private readonly Stopwatch _sw;
8 | private readonly string _name;
9 |
10 | public PerfCounter(string name) {
11 | _name = name;
12 | _sw = Stopwatch.StartNew();
13 | }
14 |
15 | public void Dispose() {
16 | _sw.Stop();
17 | Logger.Debug("Perf", $"[{_name}] {_sw.ElapsedMilliseconds}ms {_sw.ElapsedTicks}t");
18 | if (Debugger.IsAttached) {
19 | Debugger.Log(1, "Perf", $"[{_name}] {_sw.ElapsedMilliseconds}ms {_sw.ElapsedTicks}t\n");
20 | }
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/D2/ProductHandler_D2R.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using TACTLib.Client;
3 | using TACTLib.Core.Product.CommonV2;
4 |
5 | namespace TACTLib.Core.Product.D2
6 | {
7 | // ReSharper disable ClassNeverInstantiated.Global
8 | // ReSharper disable once InconsistentNaming
9 | [ProductHandler(TACTProduct.Diablo2)]
10 | public class ProductHandler_D2R : IProductHandler
11 | {
12 | private ClientHandler m_client { get; }
13 |
14 | public RootFile[] m_rootFiles { get; }
15 | public ProductHandler_D2R(ClientHandler client, Stream stream)
16 | {
17 | m_client = client;
18 |
19 | using (var reader = new StreamReader(stream))
20 | {
21 | m_rootFiles = RootFile.ParseList(reader).ToArray();
22 | }
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/TACTLib/Core/Product/MNDX/Extensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Runtime.InteropServices;
4 |
5 | namespace TACTLib.Core.Product.MNDX
6 | {
7 | public static class Extensions
8 | {
9 | ///
10 | /// Read array of structs from a BinaryReader
11 | ///
12 | /// Source reader
13 | /// Struct to read
14 | /// Array of read structs
15 | public static T[] ReadArray(this BinaryReader reader) where T : unmanaged
16 | {
17 | var numBytes = (int)reader.ReadInt64();
18 | if (numBytes == 0)
19 | {
20 | return Array.Empty();
21 | }
22 |
23 | var result = reader.ReadBytes(numBytes);
24 |
25 | reader.BaseStream.Position += (0 - numBytes) & 0x07;
26 | return MemoryMarshal.Cast(result).ToArray();
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/WorldOfWarcraftV6/CASWarcraftV6.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using TACTLib.Helpers;
3 |
4 | // ReSharper disable UnusedAutoPropertyAccessor.Global
5 | namespace TACTLib.Core.Product.WorldOfWarcraftV6 {
6 | public class CASWarcraftV6 {
7 | public int RecordCount { get; }
8 | public ContentFlags Flags { get; }
9 | public Locale Locale { get; }
10 | public int[] Delta { get; }
11 | public CASWarcraftV6Record[] Records { get; }
12 |
13 | public CASWarcraftV6(BinaryReader reader) {
14 | RecordCount = reader.ReadInt32();
15 | Flags = (ContentFlags)reader.ReadUInt32();
16 | Locale = (Locale)reader.ReadUInt32();
17 | Delta = reader.ReadArray(RecordCount);
18 | Records = reader.ReadArray(RecordCount);
19 | }
20 |
21 | public int FileId(int index) {
22 | return index == 0 ? Delta[index] : FileId(index - 1) + 1 + Delta[index];
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/TACTLib/Agent/AgentDatabase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using Google.Protobuf;
4 | using TACTLib.Agent.Protobuf;
5 |
6 | namespace TACTLib.Agent {
7 | public class AgentDatabase {
8 | public string? FilePath { get; }
9 | public Database Data { get; }
10 |
11 | public AgentDatabase() : this(null) { }
12 |
13 | public AgentDatabase(string? path) {
14 | FilePath = path;
15 | if (string.IsNullOrWhiteSpace(FilePath)) {
16 | FilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Battle.net", "Agent", "product.db");
17 | }
18 |
19 | var bytes = File.ReadAllBytes(FilePath);
20 | try {
21 | Data = Database.Parser.ParseFrom(bytes);
22 | } catch (InvalidProtocolBufferException) {
23 | Data = new Database {
24 | ProductInstall = {ProductInstall.Parser.ParseFrom(bytes)}
25 | };
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 overtools
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Fenris/EncryptedNameDict.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Text;
5 | using TACTLib.Helpers;
6 |
7 | namespace TACTLib.Core.Product.Fenris;
8 |
9 | public class EncryptedNameDict {
10 | public Dictionary Files { get; } = [];
11 |
12 | public EncryptedNameDict(Stream stream) {
13 | var magic = stream.Read();
14 | if (magic != 0xABCD4567) {
15 | throw new InvalidDataException("Not an EncryptedNameDict");
16 | }
17 |
18 | var count = stream.Read();
19 | var present = stream.ReadArray(count);
20 |
21 | Span stringBuffer = stackalloc byte[0xFF];
22 | foreach (var handle in present)
23 | {
24 | var tmp = stream.Position;
25 |
26 | if (stream.Read(stringBuffer) > 0) { // todo: verify usage of stream.Read here
27 | Files[handle] = Encoding.ASCII.GetString(stringBuffer[..stringBuffer.IndexOf((byte) 0)]);
28 | stream.Position = tmp + stringBuffer.IndexOf((byte) 0) + 1;
29 | }
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/TACTLib/Core/Key/CASCKeyComparer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace TACTLib.Core.Key {
5 | public class CASCKeyComparer : IEqualityComparer, IEqualityComparer {
6 | /// Static instance
7 | public static readonly CASCKeyComparer Instance = new CASCKeyComparer();
8 |
9 | public bool Equals(TruncatedKey x, TruncatedKey y) {
10 | return CASCKeyComparer.Equals(x, y);
11 | }
12 |
13 | public bool Equals(FullKey x, FullKey y) {
14 | return CASCKeyComparer.Equals(x, y);
15 | }
16 |
17 | private static bool Equals(ReadOnlySpan spanA, ReadOnlySpan spanB) {
18 | return spanA.SequenceEqual(spanB);
19 | }
20 |
21 | public int GetHashCode(TruncatedKey obj) {
22 | var h = new HashCode();
23 | h.AddBytes(obj);
24 | return h.ToHashCode();
25 | }
26 |
27 | public int GetHashCode(FullKey obj) {
28 | var h = new HashCode();
29 | h.AddBytes(obj);
30 | return h.ToHashCode();
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Fenris/EncryptedSnos.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Runtime.InteropServices;
6 | using TACTLib.Helpers;
7 |
8 | namespace TACTLib.Core.Product.Fenris;
9 |
10 | public class EncryptedSnos {
11 | [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x10)]
12 | public record struct EncryptedSno {
13 | public SnoHandle Sno;
14 | public ulong KeyId;
15 | }
16 |
17 | public EncryptedSno[] Entries { get; }
18 | public Dictionary Lookup = [];
19 |
20 | public EncryptedSnos(Stream? stream) {
21 | using var _ = new PerfCounter("EncryptedSnos::cctor`Stream");
22 | if (stream == null) {
23 | Entries = [];
24 | return;
25 | }
26 |
27 | var magic = stream.Read();
28 | if (magic != 0x4CBF334D) {
29 | throw new InvalidDataException("Not an EncryptedSNOs.dat file");
30 | }
31 |
32 | var count = stream.Read();
33 | Entries = stream.ReadArray(count);
34 | Lookup = Entries.ToDictionary(x => x.Sno, x => x.KeyId);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/TACTLib/Protocol/ICDNClient.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using TACTLib.Client;
3 |
4 | namespace TACTLib.Protocol
5 | {
6 | public interface ICDNClient
7 | {
8 | void SetClientHandler(ClientHandler handler);
9 |
10 | public byte[]? FetchData(string key, Range? range=null, string? suffix=null)
11 | {
12 | return Fetch("data", key.ToLowerInvariant(), range, suffix);
13 | }
14 |
15 | public byte[]? FetchLooseData(CKey key)
16 | {
17 | return FetchData(key.ToHexString());
18 | }
19 |
20 | public byte[]? FetchIndexFile(string archiveKey)
21 | {
22 | return FetchData(archiveKey, null, ".index");
23 | }
24 |
25 | public byte[]? FetchIndexEntry(string archiveKey, IndexEntry indexEntry)
26 | {
27 | return FetchData(archiveKey, new Range((int)indexEntry.Offset, (int)indexEntry.Offset + (int)indexEntry.Size - 1));
28 | }
29 |
30 | public byte[]? FetchConfig(string key)
31 | {
32 | return Fetch("config", key.ToLowerInvariant());
33 | }
34 |
35 | byte[]? Fetch(string type, string key, Range? range=null, string? suffix=null);
36 | }
37 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Fenris/ReplacedSnos.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Runtime.InteropServices;
6 | using TACTLib.Helpers;
7 |
8 | namespace TACTLib.Core.Product.Fenris;
9 |
10 | public class ReplacedSnos {
11 | [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x10)]
12 | public struct ReplacedSno {
13 | public SnoHandle Sno;
14 | public ulong Hash; // ??
15 | public SnoHandle ReplacementSno;
16 | }
17 |
18 | public ReplacedSno[] Entries { get; }
19 | public Dictionary Lookup = [];
20 |
21 | public ReplacedSnos(Stream? stream) {
22 | using var _ = new PerfCounter("ReplacedSnos::cctor`Stream");
23 | if (stream == null) {
24 | Entries = [];
25 | return;
26 | }
27 |
28 | var magic = stream.Read();
29 | if (magic != 0xABBA0003) {
30 | throw new InvalidDataException("Not an CoreTOCReplacedSNOs.dat file");
31 | }
32 |
33 | var count = stream.Read();
34 | Entries = stream.ReadArray(count);
35 | Lookup = Entries.ToDictionary(x => x.Sno.Id, x => x.ReplacementSno.Id);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/TACTLib/Core/Product/MNDX/MNDXSearchResult.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | // https://raw.githubusercontent.com/WoW-Tools/CascLib/master/CascLib/RootHandlers/MNDXRootHandler.cs
4 |
5 | namespace TACTLib.Core.Product.MNDX
6 | {
7 | internal class MNDXSearchResult
8 | {
9 | private string szSearchMask;
10 | public string SearchMask // Search mask without wildcards
11 | {
12 | get { return szSearchMask; }
13 | set
14 | {
15 | Buffer.Init();
16 |
17 | szSearchMask = value ?? throw new ArgumentNullException(nameof(SearchMask));
18 | }
19 | }
20 | public string FoundPath { get; private set; } // Found path name
21 | public int FileNameIndex { get; private set; } // Index of the file name
22 | public SearchBuffer Buffer { get; private set; }
23 |
24 | public MNDXSearchResult()
25 | {
26 | Buffer = new SearchBuffer();
27 |
28 | SearchMask = string.Empty;
29 | }
30 |
31 | public void SetFindResult(string szFoundPath, int dwFileNameIndex)
32 | {
33 | FoundPath = szFoundPath;
34 | FileNameIndex = dwFileNameIndex;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/TACTLib/Config/Config.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 |
6 | namespace TACTLib.Config {
7 | public class Config {
8 | public readonly Dictionary> Values;
9 |
10 | protected Config(Stream? stream) {
11 | Values = new Dictionary>();
12 | if (stream == null) return;
13 |
14 | using (var reader = new StreamReader(stream)) {
15 | Read(reader);
16 | }
17 | }
18 |
19 | private void Read(TextReader reader) {
20 | string? line;
21 | while ((line = reader.ReadLine()) != null) {
22 | if (string.IsNullOrWhiteSpace(line) || line.StartsWith('#')) continue;
23 | string[] tokens = line.Split('=', StringSplitOptions.RemoveEmptyEntries);
24 |
25 | if (tokens.Length != 2)
26 | throw new Exception("Config: tokens.Length != 2");
27 |
28 | string[] values = tokens[1].Trim().Split(' ', StringSplitOptions.RemoveEmptyEntries);
29 | List valuesList = values.ToList();
30 | Values.Add(tokens[0].Trim(), valuesList);
31 | }
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/TACTLib.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TACTLib", "TACTLib\TACTLib.csproj", "{CC681FAC-B05D-483F-9FA1-D8DEC4871B99}"
4 | EndProject
5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TACTLibTest", "TACTLibTest\TACTLibTest.csproj", "{B6FB166C-41CF-4A63-9346-F073BE43464C}"
6 | EndProject
7 | Global
8 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
9 | Debug|Any CPU = Debug|Any CPU
10 | Release|Any CPU = Release|Any CPU
11 | EndGlobalSection
12 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
13 | {CC681FAC-B05D-483F-9FA1-D8DEC4871B99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
14 | {CC681FAC-B05D-483F-9FA1-D8DEC4871B99}.Debug|Any CPU.Build.0 = Debug|Any CPU
15 | {CC681FAC-B05D-483F-9FA1-D8DEC4871B99}.Release|Any CPU.ActiveCfg = Release|Any CPU
16 | {CC681FAC-B05D-483F-9FA1-D8DEC4871B99}.Release|Any CPU.Build.0 = Release|Any CPU
17 | {B6FB166C-41CF-4A63-9346-F073BE43464C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
18 | {B6FB166C-41CF-4A63-9346-F073BE43464C}.Debug|Any CPU.Build.0 = Debug|Any CPU
19 | {B6FB166C-41CF-4A63-9346-F073BE43464C}.Release|Any CPU.ActiveCfg = Release|Any CPU
20 | {B6FB166C-41CF-4A63-9346-F073BE43464C}.Release|Any CPU.Build.0 = Release|Any CPU
21 | EndGlobalSection
22 | EndGlobal
23 |
--------------------------------------------------------------------------------
/TACTLib/Logger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TACTLib {
4 | public static class Logger {
5 | public delegate void LogEvent(string category, string message);
6 |
7 | public static event LogEvent? OnInfo;
8 | public static event LogEvent? OnDebug;
9 | public static event LogEvent? OnWarn;
10 | public static event LogEvent? OnError;
11 |
12 | public static void Info(string category, string message) {
13 | OnInfo?.Invoke(category, message);
14 | }
15 |
16 | public static void Debug(string category, string message) {
17 | OnDebug?.Invoke(category, message);
18 | }
19 |
20 | public static void Warn(string category, string message) {
21 | OnWarn?.Invoke(category, message);
22 | }
23 |
24 | public static void Error(string category, string message) {
25 | OnError?.Invoke(category, message);
26 | }
27 |
28 | public static void RegisterBasic() {
29 | OnInfo += LogBasic;
30 | OnDebug += LogBasic;
31 | OnWarn += LogBasic;
32 | OnError += LogBasic;
33 | }
34 |
35 | private static void LogBasic(string category, string message) {
36 | Console.Out.WriteLine($"[{category}] {message}");
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/TACTLib/Client/HandlerArgs/ClientCreateArgs_Tank.cs:
--------------------------------------------------------------------------------
1 | namespace TACTLib.Client.HandlerArgs {
2 | ///
3 | ///
4 | /// Args specific to Tank
5 | ///
6 | public class ClientCreateArgs_Tank : IHandlerArgs {
7 | ///
8 | /// Cache parsed APM file data
9 | ///
10 | public bool CacheAPM { get; set; } = true;
11 |
12 | public string ManifestPlatform { get; set; } = PLATFORM_WIN;
13 |
14 | ///
15 | /// Manifest Region declaration. Only two valid values are RDEV and RCN.
16 | ///
17 | public string ManifestRegion { get; set; } = REGION_DEV;
18 |
19 | ///
20 | /// Load manifest files. Flag here to allow disabling for development purposes
21 | ///
22 | public bool LoadManifest { get; set; } = true;
23 |
24 | ///
25 | /// Load bundles for lookup. Flag here to allow disabling for development purposes
26 | ///
27 | public bool LoadBundlesForLookup { get; set; } = true;
28 |
29 | public const string REGION_DEV = "DEV";
30 | public const string REGION_CN = "CN";
31 |
32 | public const string PLATFORM_WIN = "Win";
33 | public const string PLATFORM_WINPRISM = "WinPrism";
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Fenris/SharedPayloadsMapping.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using TACTLib.Helpers;
4 |
5 | namespace TACTLib.Core.Product.Fenris;
6 |
7 | public class SharedPayloadsMapping {
8 | public Dictionary SharedPayloads { get; set; } = [];
9 | public Dictionary SharedChildren { get; set; } = [];
10 |
11 | public SharedPayloadsMapping(Stream? stream) {
12 | using var _ = new PerfCounter("SharedPayloadsMapping::cctor`Stream");
13 | if (stream == null) {
14 | return;
15 | }
16 |
17 | var magic = stream.Read();
18 | if (magic != 0xABBA0003) {
19 | throw new InvalidDataException("Not an CoreTOCSharedPayloadsMapping.dat file");
20 | }
21 |
22 | var count = stream.Read();
23 | {
24 | var entries = stream.ReadArray((int)count * 2);
25 |
26 | for (var i = 0; i < count; ++i) {
27 | SharedPayloads[entries[i << 1]] = entries[(i << 1) + 1];
28 | }
29 | }
30 |
31 | count = stream.Read();
32 | {
33 | var entries = stream.ReadArray((int)count * 2);
34 |
35 | for (var i = 0; i < count; ++i) {
36 | SharedChildren[entries[i << 1]] = entries[(i << 1) + 1];
37 | }
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/.github/workflows/dotnet.yml:
--------------------------------------------------------------------------------
1 | name: TACTLib
2 | on:
3 | push:
4 | branches: [ master ]
5 | pull_request:
6 | branches: [ master ]
7 | jobs:
8 | build:
9 | runs-on: windows-latest
10 | steps:
11 | - name: Checkout
12 | uses: actions/checkout@v4
13 | with:
14 | fetch-depth: 0
15 | submodules: 'recursive'
16 | - name: Install .NET 9
17 | uses: actions/setup-dotnet@v4
18 | with:
19 | dotnet-version: 9.0.x
20 | - name: Restore project
21 | run: dotnet restore TACTLib.sln --verbosity m -r win-x64
22 | - name: Build Release
23 | run: dotnet publish -f net9.0 --configuration Release -r win-x64 --self-contained false -o dist/Release
24 | - name: Upload Release
25 | uses: actions/upload-artifact@v4
26 | with:
27 | name: TACTLib-release
28 | path: dist/Release
29 | - name: Build Debug
30 | run: dotnet publish -f net9.0 --configuration Debug -r win-x64 --self-contained false -o dist/Debug
31 | - name: Upload Debug
32 | uses: actions/upload-artifact@v4
33 | with:
34 | name: TACTLib-debug
35 | path: dist/Debug
36 |
--------------------------------------------------------------------------------
/TACTLib/TACTLib.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Library
4 | latest
5 | net9.0
6 | true
7 | 1.0.0.0
8 | enable
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | broken
24 |
25 |
26 |
27 |
28 | develop
29 | $(GITHUB_RUN_NUMBER)+$(SourceRevisionIdGit)
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/TACTLib/Core/Product/ProductHandlerFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics.CodeAnalysis;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Reflection;
7 | using TACTLib.Client;
8 | using TACTLib.Helpers;
9 |
10 | namespace TACTLib.Core.Product {
11 | public static class ProductHandlerFactory {
12 | private static readonly Dictionary _handlers = new Dictionary();
13 |
14 | public static IProductHandler? GetHandler(TACTProduct product, ClientHandler client, Stream? root) {
15 | var handlerType = GetHandlerType(product);
16 | if (handlerType == null) return null;
17 |
18 | using (var _ = new PerfCounter($"{handlerType.Name}::ctor`ClientHandler`Stream"))
19 | return (IProductHandler)Activator.CreateInstance(handlerType, client, root)!;
20 | }
21 |
22 | public static Type? GetHandlerType(TACTProduct product) {
23 | if (!_handlers.TryGetValue(product, out var type)) {
24 | type = Assembly.GetExecutingAssembly().GetTypes().FirstOrDefault(x => typeof(IProductHandler).IsAssignableFrom(x) && x.GetCustomAttributes().Any(i => i.Product == product));
25 | }
26 | return type;
27 | }
28 |
29 | public static void SetHandler(TACTProduct product, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type) {
30 | _handlers[product] = type;
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/TACTLib/Core/Product/MNDX/TBitEntryArray.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 |
4 | // https://raw.githubusercontent.com/WoW-Tools/CascLib/master/CascLib/RootHandlers/MNDXRootHandler.cs
5 |
6 | namespace TACTLib.Core.Product.MNDX
7 | {
8 | internal class TBitEntryArray : List
9 | {
10 | private int BitsPerEntry;
11 | private int EntryBitMask;
12 | private int TotalEntries;
13 |
14 | public new int this[int index]
15 | {
16 | get
17 | {
18 | var dwItemIndex = (index * BitsPerEntry) >> 0x05;
19 | var dwStartBit = (index * BitsPerEntry) & 0x1F;
20 | var dwEndBit = dwStartBit + BitsPerEntry;
21 | int dwResult;
22 |
23 | // If the end bit index is greater than 32,
24 | // we also need to load from the next 32-bit item
25 | if (dwEndBit > 0x20)
26 | {
27 | dwResult = (base[dwItemIndex + 1] << (0x20 - dwStartBit)) | (int)((uint)base[dwItemIndex] >> dwStartBit);
28 | }
29 | else
30 | {
31 | dwResult = base[dwItemIndex] >> dwStartBit;
32 | }
33 |
34 | // Now we also need to mask the result by the bit mask
35 | return dwResult & EntryBitMask;
36 | }
37 | }
38 |
39 | public TBitEntryArray(BinaryReader reader) : base(reader.ReadArray())
40 | {
41 | BitsPerEntry = reader.ReadInt32();
42 | EntryBitMask = reader.ReadInt32();
43 | TotalEntries = (int)reader.ReadInt64();
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/TACTLib/Protocol/NGDP/RibbitHttpClient.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Http;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 |
5 | namespace TACTLib.Protocol.NGDP
6 | {
7 | public class RibbitHttpClient : NGDPClientBase
8 | {
9 | private readonly HttpClient m_httpClient;
10 | private readonly string m_host;
11 |
12 | public RibbitHttpClient(string host, HttpClient? httpClient=null)
13 | {
14 | m_httpClient = httpClient ?? new HttpClient();
15 | m_host = host;
16 | }
17 |
18 | public override string Get(string query)
19 | {
20 | Logger.Info(nameof(RibbitHttpClient), $"Fetching Ribbit {query}");
21 | using var response = m_httpClient.Send(new HttpRequestMessage(HttpMethod.Get, $"{m_host}/{query}"));
22 | return response.Content.ReadAsStringAsync().Result; // todo: async over sync
23 | }
24 |
25 | public override async Task GetAsync(string query, CancellationToken cancellationToken = default)
26 | {
27 | Logger.Info(nameof(RibbitHttpClient), $"Fetching Ribbit {query}");
28 | using var response = await m_httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, $"{m_host}/{query}"), cancellationToken);
29 | return await response.Content.ReadAsStringAsync(cancellationToken);
30 | }
31 |
32 | public override string GetSummaryQuery() => "v2/summary";
33 | public override string GetVersionsQuery(string product) => $"v2/products/{product}/versions";
34 | public override string GetCDNsQuery(string product) => $"v2/products/{product}/cdns";
35 | public override string GetBGDLsQuery(string product) => $"v2/products/{product}/bgdl";
36 | }
37 | }
--------------------------------------------------------------------------------
/TACTLib/Protocol/NGDP/NGDPClient.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net.Http;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 |
6 | namespace TACTLib.Protocol.NGDP {
7 | public class NGDPClient : NGDPClientBase
8 | {
9 | private readonly HttpClient m_httpClient;
10 | private readonly string m_host;
11 |
12 | public NGDPClient(string host, HttpClient? httpClient=null)
13 | {
14 | m_httpClient = httpClient ?? new HttpClient();
15 | m_host = host;
16 | }
17 |
18 | public override string Get(string query)
19 | {
20 | Logger.Info(nameof(NGDPClient), $"Fetching NGDP {query}");
21 | using var response = m_httpClient.Send(new HttpRequestMessage(HttpMethod.Get, $"{m_host}/{query}"));
22 | return response.Content.ReadAsStringAsync().Result; // todo: async over sync
23 | }
24 |
25 | public override async Task GetAsync(string query, CancellationToken cancellationToken = default)
26 | {
27 | Logger.Info(nameof(NGDPClient), $"Fetching NGDP {query}");
28 | using var response = await m_httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, $"{m_host}/{query}"), cancellationToken);
29 | return await response.Content.ReadAsStringAsync(cancellationToken);
30 | }
31 |
32 | public override string GetSummaryQuery() => throw new NotSupportedException("Legacy patch endpoint does have a summary");
33 | public override string GetVersionsQuery(string product) => $"{product}/versions";
34 | public override string GetCDNsQuery(string product) => $"{product}/cdns";
35 | public override string GetBGDLsQuery(string product) => $"{product}/bgdl";
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/TACTLib/Helpers/SpanHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace TACTLib.Helpers
5 | {
6 | public static class SpanHelper
7 | {
8 | public static Span Advance(scoped ref Span input, int numEntries)
9 | {
10 | var result = input.Slice(0, numEntries);
11 | input = input.Slice(numEntries);
12 |
13 | return result;
14 | }
15 |
16 | public static ReadOnlySpan Advance(scoped ref ReadOnlySpan input, int numEntries)
17 | {
18 | var result = input.Slice(0, numEntries);
19 | input = input.Slice(numEntries);
20 |
21 | return result;
22 | }
23 |
24 | public static unsafe ref T ReadStruct(scoped ref Span input) where T : unmanaged
25 | {
26 | ref var result = ref MemoryMarshal.AsRef(input);
27 | input = input.Slice(sizeof(T));
28 | return ref result;
29 | }
30 |
31 | public static unsafe T ReadStruct(scoped ref ReadOnlySpan input) where T : unmanaged
32 | {
33 | var result = MemoryMarshal.Read(input);
34 | input = input.Slice(sizeof(T));
35 | return result;
36 | }
37 |
38 | public static unsafe ReadOnlySpan ReadArray(scoped ref ReadOnlySpan input, int count) where T : unmanaged
39 | {
40 | var resultBytes = Advance(ref input, sizeof(T) * count);
41 | return MemoryMarshal.Cast(resultBytes);
42 | }
43 |
44 | public static byte ReadByte(scoped ref Span input)
45 | {
46 | var result = input[0];
47 | input = input.Slice(1);
48 | return result;
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/WorldOfWarcraftV6/ProductHandler_WorldOfWarcraftV6.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Concurrent;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using TACTLib.Client;
5 | using TACTLib.Client.HandlerArgs;
6 |
7 | // ReSharper disable NotAccessedField.Global
8 |
9 | namespace TACTLib.Core.Product.WorldOfWarcraftV6 {
10 | [ProductHandler(TACTProduct.WorldOfWarcraft)]
11 | public class ProductHandler_WorldOfWarcraftV6 : IProductHandler {
12 | public CASWarcraftV6[] Blocks;
13 |
14 | public ConcurrentDictionary Assets = new ConcurrentDictionary();
15 |
16 | public struct Asset {
17 | public int BlockIdx;
18 | public int RecordIdx;
19 |
20 | public Asset(int blockIdx, int recordIdx) {
21 | BlockIdx = blockIdx;
22 | RecordIdx = recordIdx;
23 | }
24 | }
25 |
26 | public ProductHandler_WorldOfWarcraftV6(ClientHandler client, Stream stream) {
27 | var clientArgs = client.CreateArgs.HandlerArgs as ClientCreateArgs_WorldOfWarcraftV6 ?? new ClientCreateArgs_WorldOfWarcraftV6();
28 |
29 | List blocks = new List();
30 |
31 | using (BinaryReader reader = new BinaryReader(stream)) {
32 | while (stream.Length - stream.Position > 0) {
33 | CASWarcraftV6 block = new CASWarcraftV6(reader);
34 | blocks.Add(block);
35 |
36 | var blockIdx = blocks.Count - 1;
37 |
38 | for (var i = 0; i < block.Records.Length; ++i) {
39 | if (Assets.ContainsKey(block.Records[i].LookupHash)) continue;
40 | Assets[block.Records[i].LookupHash] = new Asset(blockIdx, i);
41 | }
42 | }
43 | }
44 |
45 | Blocks = blocks.ToArray();
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/TACTLib/Core/ClientPatchManifest.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 | using TACTLib.Helpers;
3 |
4 | namespace TACTLib.Core {
5 | public class ClientPatchManifest {
6 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
7 | public struct Header {
8 | ///
9 | /// Patch signature, "PA"
10 | ///
11 | public short Signature;
12 | public byte Version;
13 | public byte CKeySize;
14 | public byte OKeySize;
15 | public byte PatchKeySize;
16 | public byte BlockSize;
17 | public UInt16BE BlockCount; // uint16-BE
18 | public byte Flags;
19 | public CKey CKey;
20 | public EKey EKey;
21 | public UInt32BE DecodedSize; // uint32-BE
22 | public UInt32BE EncodedSize; // uint32-BE
23 | public byte ESpecLength;
24 | // char encoding_format[ESpecLength];
25 | }
26 |
27 | public struct BlockHeader {
28 | public CKey LastFileCKey;
29 | public CKey BlockMD5; // maybe shouldn't be CKey
30 | public UInt32BE BlockOffset; // uint32-BE
31 | }
32 |
33 | // at BlockOffset
34 | // count unspecified: read until the next file num_patches would be 0 or block would exceed max block size
35 | public struct BlockFile {
36 | public byte NumPatches;
37 | public CKey TargetCKey;
38 | public UInt40BE DecodedSize; // uint40-BE
39 | }
40 |
41 | public struct BlockFilePatch {
42 | public EKey SourceEKey;
43 | public UInt40BE DecodedSize; // uint40-BE
44 | public EKey PatchEKey; // todo: check size (patch_ekey[header.patch_key_size])
45 | public UInt32BE PatchSize; // uint32-BE
46 | public byte Unknown; // some sort of patch index number. first entry seems to always be 1
47 | }
48 | }
49 | }
--------------------------------------------------------------------------------
/TACTLib/Core/VFS/VFSFileTree.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 | using TACTLib.Client;
8 |
9 | namespace TACTLib.Core.VFS {
10 | public class VFSFileTree {
11 | private readonly ClientHandler _client;
12 |
13 | private readonly Dictionary _files;
14 | private readonly VFSManifestReader.Manifest _manifest;
15 |
16 | public readonly ReadOnlyCollection Files;
17 |
18 | public VFSFileTree(ClientHandler client, Stream stream) {
19 | _client = client;
20 | using BinaryReader reader = new BinaryReader(stream, Encoding.ASCII);
21 | //using (Stream file = File.OpenWrite("vfs.hex")) {
22 | // stream.CopyTo(file);
23 | // stream.Position = 0;
24 | //}
25 |
26 | _manifest = VFSManifestReader.Read(reader);
27 |
28 | _files = new Dictionary(_manifest.Files.Count);
29 | foreach (VFSFile file in _manifest.Files) {
30 | if (file.Name != null) {
31 | _files[file.Name] = file;
32 | }
33 | }
34 |
35 | Files = Array.AsReadOnly(_files.Keys.ToArray());
36 | }
37 |
38 | ///
39 | /// Open file by path
40 | ///
41 | ///
42 | ///
43 | ///
44 | public Stream? Open(string file) {
45 | if (_files.TryGetValue(file, out var vfsFile)) {
46 | if (vfsFile is VFSCFile cFile) {
47 | return _client.OpenCKey(cFile.CKey);
48 | }
49 |
50 | if (_client.IsStaticContainer && vfsFile.ContentSize == 0) {
51 | throw new NotImplementedException("where esize?");
52 | }
53 |
54 | return _client.OpenEKey(vfsFile.EKey, vfsFile.ContentSize == 0 ? _client.EncodingHandler!.GetEncodedSize(vfsFile.EKey) : vfsFile.ContentSize);
55 | }
56 | throw new FileNotFoundException(file);
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/TACTLib/Core/Product/MNDX/SearchBuffer.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text;
3 |
4 | // https://raw.githubusercontent.com/WoW-Tools/CascLib/master/CascLib/RootHandlers/MNDXRootHandler.cs
5 |
6 | namespace TACTLib.Core.Product.MNDX
7 | {
8 | internal class SearchBuffer
9 | {
10 | private List SearchResult = new List();
11 | private List PathStops = new List(); // Array of path checkpoints
12 |
13 | public int ItemIndex { get; set; } = 0;// Current name fragment: Index to various tables
14 | public int CharIndex { get; set; } = 0;
15 | public int ItemCount { get; set; } = 0;
16 | public CASCSearchPhase SearchPhase { get; private set; } = CASCSearchPhase.Initializing; // 0 = initializing, 2 = searching, 4 = finished
17 |
18 | public string Result
19 | {
20 | get { return Encoding.ASCII.GetString(SearchResult.ToArray()); }
21 | }
22 |
23 | public int NumBytesFound
24 | {
25 | get { return SearchResult.Count; }
26 | }
27 |
28 | public int NumPathStops
29 | {
30 | get { return PathStops.Count; }
31 | }
32 |
33 | public void Add(byte value)
34 | {
35 | SearchResult.Add(value);
36 | }
37 |
38 | public void RemoveRange(int index)
39 | {
40 | SearchResult.RemoveRange(index, SearchResult.Count - index);
41 | }
42 |
43 | public void AddPathStop(PATH_STOP item)
44 | {
45 | PathStops.Add(item);
46 | }
47 |
48 | public PATH_STOP GetPathStop(int index)
49 | {
50 | return PathStops[index];
51 | }
52 |
53 | public void Init()
54 | {
55 | SearchPhase = CASCSearchPhase.Initializing;
56 | }
57 |
58 | public void Finish()
59 | {
60 | SearchPhase = CASCSearchPhase.Finished;
61 | }
62 |
63 | public void InitSearchBuffers()
64 | {
65 | SearchResult.Clear();
66 | PathStops.Clear();
67 |
68 | ItemIndex = 0;
69 | CharIndex = 0;
70 | ItemCount = 0;
71 | SearchPhase = CASCSearchPhase.Searching;
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Fenris/SnoManifest.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using TACTLib.Helpers;
4 |
5 | namespace TACTLib.Core.Product.Fenris;
6 |
7 | // Both for convenience, and also as a priority order.
8 | public enum SnoManifestRole {
9 | Base,
10 | Core,
11 | Speech,
12 | Text,
13 | }
14 |
15 | public class SnoManifest {
16 | public Locale Locale { get; set; }
17 | public SnoManifestRole Role { get; set; }
18 | public HashSet SnoIds { get; }
19 | public HashSet SnoIds2 { get; }
20 | public HashSet ChildIds { get; }
21 | public HashSet ChildIds2 { get; }
22 |
23 | public SnoManifest(Stream? stream) { // used to assist lookups, i think.
24 | using var _ = new PerfCounter("SnoManifest::cctor`Stream");
25 | if (stream == null) {
26 | SnoIds = [];
27 | ChildIds = [];
28 | SnoIds2 = [];
29 | ChildIds2 = [];
30 | return;
31 | }
32 |
33 | var magic = stream.Read();
34 | if (magic != 0xEAF1FE90) {
35 | throw new InvalidDataException("Not a SNO Manifest file");
36 | }
37 |
38 | var count = stream.Read();
39 | var snoIds = stream.ReadArray(count);
40 | SnoIds = new HashSet(snoIds);
41 |
42 | count = stream.Read();
43 | var childIds = stream.ReadArray(count);
44 | ChildIds = new HashSet(childIds);
45 |
46 | if (stream.Position == stream.Length) {
47 | SnoIds2 = [];
48 | ChildIds2 = [];
49 | return;
50 | }
51 |
52 | count = stream.Read();
53 | var childIds2 = stream.ReadArray(count);
54 | ChildIds2 = new HashSet(childIds2);
55 |
56 | count = stream.Read();
57 | var snoIds2 = stream.ReadArray(count);
58 | SnoIds2 = new HashSet(snoIds2);
59 | }
60 |
61 | public bool ContainsChild(uint id, uint subId) =>
62 | ChildIds.Contains(new SnoChild {
63 | SnoId = id,
64 | SubId = subId,
65 | }) || ChildIds2.Contains(new SnoChild {
66 | SnoId = id,
67 | SubId = subId,
68 | });
69 |
70 | public bool Contains(uint id) => SnoIds.Contains(id) || SnoIds2.Contains(id);
71 | }
72 |
--------------------------------------------------------------------------------
/TACTLib/Protocol/HttpCDNClient.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using System.Net.Http;
4 | using System.Net.Http.Headers;
5 | using TACTLib.Client;
6 |
7 | namespace TACTLib.Protocol {
8 | public class HttpCDNClient : ICDNClient
9 | {
10 | private static readonly HttpClientHandler s_httpClientHandler = new HttpClientHandler
11 | {
12 | // unlikely to be supported by cdn but...
13 | AutomaticDecompression = DecompressionMethods.All
14 | };
15 |
16 | private readonly HttpClient m_httpClient;
17 | private ClientHandler m_client;
18 |
19 | public HttpCDNClient(HttpClient? httpClient)
20 | {
21 | m_httpClient = httpClient ?? new HttpClient(s_httpClientHandler);
22 | }
23 |
24 | public virtual void SetClientHandler(ClientHandler handler)
25 | {
26 | m_client = handler;
27 | }
28 |
29 | public virtual byte[]? Fetch(string type, string key, Range? range=null, string? suffix=null)
30 | {
31 | key = key.ToLowerInvariant();
32 |
33 | var hosts = m_client.InstallationInfo.Values["CDNHosts"].Split(' ');
34 | foreach (var host in hosts)
35 | {
36 | if (host.EndsWith("cdn.blizzard.com"))
37 | {
38 | continue; // these still dont work very well..
39 | }
40 |
41 | var url = $"http://{host}/{m_client.InstallationInfo.Values["CDNPath"]}/{type}/{key.AsSpan(0, 2)}/{key.AsSpan(2, 2)}/{key}{suffix}";
42 | Logger.Info("CDN", $"Fetching file {url}");
43 |
44 | var requestMessage = new HttpRequestMessage(HttpMethod.Get, url);
45 | if (range != null)
46 | {
47 | requestMessage.Headers.Range = new RangeHeaderValue(range.Value.Start.Value, range.Value.End.Value);
48 | }
49 |
50 | try
51 | {
52 | using var response = m_httpClient.Send(requestMessage, HttpCompletionOption.ResponseHeadersRead);
53 | if (!response.IsSuccessStatusCode)
54 | {
55 | continue;
56 | }
57 |
58 | var result = response.Content.ReadAsByteArrayAsync().Result; // todo: async over sync
59 | return result;
60 | } catch (Exception e) {
61 | // ignored
62 | Logger.Debug("CDN", $"Error fetching {url}: {e}");
63 | }
64 | }
65 |
66 | return null;
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/TACTLib/Config/BuildConfig.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 |
5 | namespace TACTLib.Config {
6 | public class BuildConfig : Config {
7 | public FileRecord Root;
8 | public FileRecord? Install;
9 | public FileRecord? Patch;
10 | public FileRecord? Download;
11 | public FileRecord Encoding;
12 | public SizeRecord? EncodingSize;
13 | public FileRecord? VFSRoot;
14 |
15 | public string GetBuildName() => Values["build-name"][0];
16 |
17 | public BuildConfig(Stream? stream) : base(stream) {
18 | GetFileRecord("root", out var root);
19 | GetFileRecord("install", out Install);
20 | GetFileRecord("patch", out Patch);
21 | GetFileRecord("download", out Download);
22 | GetFileRecord("encoding", out var encoding);
23 | GetSizeRecord("encoding-size", out EncodingSize);
24 | GetFileRecord("vfs-root", out VFSRoot);
25 |
26 | if (root == null) throw new NullReferenceException(nameof(root));
27 | Root = root;
28 |
29 | if (encoding == null) throw new NullReferenceException(nameof(encoding));
30 | Encoding = encoding;
31 | }
32 |
33 | private void GetFileRecord(string key, out FileRecord? @out) {
34 | if (!Values.TryGetValue(key, out var list)) {
35 | @out = null;
36 | return;
37 | }
38 | @out = GetFileRecord(list);
39 | }
40 |
41 | private void GetSizeRecord(string key, out SizeRecord? @out) {
42 | if (!Values.TryGetValue(key, out var list)) {
43 | @out = null;
44 | return;
45 | }
46 | @out = new SizeRecord {
47 | ContentSize = int.Parse(list[0]),
48 | EncodedSize = int.Parse(list[1])
49 | };
50 | }
51 |
52 | private static FileRecord GetFileRecord(IReadOnlyList vals) {
53 | FileRecord record = new FileRecord();
54 |
55 | if (vals.Count > 0) {
56 | record.ContentKey = CKey.FromString(vals[0]);
57 | }
58 |
59 | if (vals.Count > 1) {
60 | record.EncodingKey = FullEKey.FromString(vals[1]);
61 | }
62 |
63 | return record;
64 | }
65 |
66 | public class FileRecord {
67 | public CKey ContentKey;
68 | public FullEKey EncodingKey;
69 | }
70 |
71 | public class SizeRecord {
72 | public int ContentSize;
73 | public int EncodedSize;
74 | }
75 | }
76 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Fenris/CoreTOC.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Runtime.InteropServices;
6 | using System.Text;
7 | using TACTLib.Helpers;
8 |
9 | namespace TACTLib.Core.Product.Fenris;
10 |
11 | public class CoreTOC {
12 | [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0xC)]
13 | public record struct TOCEntry {
14 | public SnoHandle Sno;
15 | public int NameOffset;
16 | }
17 |
18 | public int[] GroupCounts { get; }
19 | public int[] GroupOffsets { get; }
20 | public int[] GroupCounts2 { get; }
21 | public uint[] GroupFormats { get; }
22 | public uint PrimaryId { get; }
23 | public Dictionary Files { get; } = [];
24 |
25 | public CoreTOC(Stream? stream, EncryptedSnos encrypted) {
26 | using var _ = new PerfCounter("CoreTOC::cctor`Stream`EncryptedSnos");
27 | if (stream == null) {
28 | throw new ArgumentNullException(nameof(stream));
29 | }
30 |
31 | var magic = stream.Read();
32 | var isNew = magic == 0xBCDE6611;
33 | if (!isNew) {
34 | stream.Position = 0;
35 | }
36 |
37 | var count = stream.Read();
38 | GroupCounts = stream.ReadArray(count);
39 | GroupOffsets = stream.ReadArray(count);
40 | GroupCounts2 = stream.ReadArray(count);
41 | GroupFormats = isNew ? stream.ReadArray(count) : [];
42 |
43 | PrimaryId = stream.Read();
44 |
45 | var baseOffset = stream.Position;
46 |
47 | Span stringBuffer = stackalloc byte[0x1FF];
48 | for (var i = 0; i < count; ++i) {
49 | // I'm assuming that GroupCounts2 is the count but with replaced files included.
50 | Debug.Assert(GroupCounts[i] == GroupCounts2[i]);
51 | stream.Position = baseOffset + GroupOffsets[i];
52 |
53 | var stringOffset = baseOffset + GroupOffsets[i] + 0xC * GroupCounts[i];
54 | for (var j = 0; j < GroupCounts[i]; ++j) {
55 | var entry = stream.Read();
56 |
57 | if (encrypted.Lookup.ContainsKey(entry.Sno)) {
58 | continue;
59 | }
60 |
61 | var tmp = stream.Position;
62 | stream.Position = stringOffset + entry.NameOffset;
63 |
64 | if (stream.Read(stringBuffer) > 0) { // todo: verify usage of stream.Read here
65 | Files[entry.Sno] = Encoding.ASCII.GetString(stringBuffer[..stringBuffer.IndexOf((byte) 0)]);
66 | }
67 |
68 | stream.Position = tmp;
69 | }
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/TACTLib/Client/ConfigHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 | using System.IO;
4 | using TACTLib.Config;
5 |
6 | namespace TACTLib.Client {
7 | public class ConfigHandler {
8 | /// Build config
9 | public readonly BuildConfig BuildConfig;
10 |
11 | /// CDN config
12 | public readonly CDNConfig CDNConfig;
13 |
14 | /// Keyring config
15 | public readonly Keyring Keyring;
16 |
17 | private ConfigHandler(ClientHandler client) {
18 | LoadFromInstallationInfo(client, "BuildKey", out BuildConfig? buildConfig);
19 | LoadFromInstallationInfo(client, "CDNKey", out CDNConfig? cdnConfig);
20 | LoadFromInstallationInfo(client, "Keyring", out Keyring? keyring);
21 |
22 | if (buildConfig == null) throw new NullReferenceException(nameof(buildConfig));
23 | BuildConfig = buildConfig;
24 |
25 | if (cdnConfig == null) throw new NullReferenceException(nameof(cdnConfig));
26 | CDNConfig = cdnConfig;
27 |
28 | if (keyring == null) {
29 | keyring = new Keyring(null);
30 | }
31 | Keyring = keyring;
32 | Keyring.LoadSupportKeyrings(client);
33 | }
34 |
35 | private ConfigHandler(ClientHandler client, BuildConfig buildConfig) {
36 | BuildConfig = buildConfig;
37 |
38 | CDNConfig = new CDNConfig(null);
39 |
40 | Keyring = new Keyring(null);
41 | Keyring.LoadSupportKeyrings(client);
42 | }
43 |
44 | private static void LoadFromInstallationInfo<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(ClientHandler client, string name, out T? @out) where T : Config.Config {
45 | if (client.InstallationInfo.Values.TryGetValue(name, out var key) && !string.IsNullOrWhiteSpace(key)) {
46 | using (var stream = client.OpenConfigKey(key)) {
47 | LoadConfig(stream, out @out);
48 | }
49 | } else {
50 | @out = null;
51 | }
52 | }
53 |
54 | private static void LoadConfig<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(Stream? stream, out T @out) {
55 | // hmm
56 | @out = (T) Activator.CreateInstance(typeof(T), stream)!;
57 | }
58 |
59 | public static ConfigHandler ForDynamicContainer(ClientHandler client) {
60 | return new ConfigHandler(client);
61 | }
62 |
63 | public static ConfigHandler ForStaticContainer(ClientHandler client, BuildConfig buildConfig) {
64 | return new ConfigHandler(client, buildConfig);
65 | }
66 | }
67 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/CommonV2/RootFile.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 |
6 | // ReSharper disable file NotAccessedField.Global
7 | namespace TACTLib.Core.Product.CommonV2 {
8 | [DebuggerDisplay("{FileName}")]
9 | public class RootFile {
10 | public string? FileID;
11 | public CKey MD5;
12 | public byte ChunkID;
13 | public byte Priority;
14 | public byte MPriority;
15 | public string? FileName;
16 | public string? InstallPath;
17 |
18 | public RootFile() { }
19 |
20 | private RootFile(ReadOnlySpan columns, string row) {
21 | Span ranges = stackalloc Range[columns.Length];
22 | MemoryExtensions.Split(row, ranges, '|');
23 |
24 | for (var i = 0; i < columns.Length; i++) {
25 | var valueSpan = row.AsSpan(ranges[i]);
26 |
27 | switch (columns[i]) {
28 | case "FILEID": {
29 | FileID = row[ranges[i]];
30 | break;
31 | }
32 | case "MD5": {
33 | MD5 = CKey.FromString(valueSpan);
34 | break;
35 | }
36 | case "CHUNK_ID": {
37 | ChunkID = byte.Parse(valueSpan);
38 | break;
39 | }
40 | case "PRIORITY": {
41 | Priority = byte.Parse(valueSpan);
42 | break;
43 | }
44 | case "MPRIORITY": {
45 | MPriority = byte.Parse(valueSpan);
46 | break;
47 | }
48 | case "FILENAME": {
49 | FileName = row[ranges[i]];
50 | break;
51 | }
52 | case "INSTALLPATH": {
53 | InstallPath = row[ranges[i]];
54 | break;
55 | }
56 | default: {
57 | Logger.Debug("RootFile", $"Unknown column {columns[i]}");
58 | break;
59 | }
60 | }
61 | }
62 | }
63 |
64 | public static List ParseList(StreamReader reader) {
65 | var header = reader.ReadLine()!;
66 | if (header[0] != '#') throw new InvalidDataException($"bad header: \"{header}\"");
67 |
68 | var files = new List();
69 |
70 | var columns = header.Substring(1).Split('|');
71 | while (reader.ReadLine() is { } row) {
72 | var rootFile = new RootFile(columns, row);
73 | files.Add(rootFile);
74 | }
75 |
76 | return files;
77 | }
78 | }
79 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Tank/Bundle.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Linq;
4 | using System.Runtime.InteropServices;
5 | using TACTLib.Helpers;
6 |
7 | namespace TACTLib.Core.Product.Tank {
8 | public class Bundle {
9 | public readonly HeaderData148 Header;
10 | public readonly Entry4[] Entries;
11 |
12 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
13 | public struct HeaderData {
14 | public int EntryCount;
15 | public int Flags;
16 | public byte OffsetSize;
17 |
18 | public HeaderData148 To148() => new HeaderData148 {
19 | EntryCount = EntryCount,
20 | OffsetSize = OffsetSize
21 | };
22 | }
23 |
24 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
25 | public struct HeaderData148 {
26 | public int EntryCount;
27 | public byte OffsetSize;
28 | }
29 |
30 | [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 9)]
31 | public struct Entry1 {
32 | public ulong GUID;
33 | public byte Offset; // 1
34 | }
35 |
36 | [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 10)]
37 | public struct Entry2 {
38 | public ulong GUID;
39 | public ushort Offset; // 2
40 | }
41 |
42 | [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 12)]
43 | public struct Entry4 {
44 | public ulong GUID;
45 | public uint Offset; // 4
46 | }
47 |
48 | public Bundle(Stream? stream, bool is148) {
49 | if (stream == null) {
50 | Entries = Array.Empty();
51 | return;
52 | }
53 |
54 | using (BinaryReader reader = new BinaryReader(stream)) {
55 | if (is148) {
56 | Header = reader.Read();
57 | } else {
58 | Header = reader.Read().To148();
59 | }
60 |
61 | // todo: maybe don't use Select then convert. bit of a hack
62 | if (Header.OffsetSize == 1)
63 | Entries = reader.ReadArray(Header.EntryCount).Select(x => new Entry4 {
64 | GUID = x.GUID,
65 | Offset = x.Offset
66 | }).ToArray();
67 | else if (Header.OffsetSize == 2) {
68 | Entries = reader.ReadArray(Header.EntryCount).Select(x => new Entry4 {
69 | GUID = x.GUID,
70 | Offset = x.Offset
71 | }).ToArray();
72 | } else if (Header.OffsetSize == 4) {
73 | Entries = reader.ReadArray(Header.EntryCount);
74 | } else {
75 | throw new Exception($"unknown bundle offset size {Header.OffsetSize}. contact the devs");
76 | // this should never happen
77 | }
78 | }
79 | }
80 | }
81 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TACTLib [](https://github.com/overtools/TACTLib/actions/workflows/dotnet.yml)
2 |
3 | ### License: MIT
4 |
5 | ----
6 |
7 | ### Usage:
8 | #### Creating a ClientHandler
9 | The ClientHandler is the base object that controls the CAS(C).
10 |
11 | The path passed to ClientHandler should be the *base path* of the game install. E.g. where the game executables are. Upon creation, the client handler will load everything required for CASC operation.
12 | ```cs
13 | string path = @"C:\ow\game\Overwatch";
14 | ClientHandler clientHandler = new ClientHandler(path);
15 | ```
16 |
17 | ### Logging:
18 | Logging is handled through the TACTLib.Logger class. It has events that are triggered by TACTLib during runtime.
19 | Basic logging can be enabled by using TACTLib.Logger.RegisterBasic. That method also serves as an example of how to do custom logging. (see [Logger.cs](https://github.com/overtools/TACTLib/blob/master/TACTLib/Logger.cs))
20 |
21 | ```cs
22 | // enables the default basic logger. should be called *before* creating the client
23 | Logger.RegisterBasic();
24 | ```
25 |
26 | #### Product specific:
27 | (none of this is true yet)
28 |
29 | ##### VFS: (Black Ops 4)
30 | ```cs
31 | ClientHandler client = new ClientHandler(path);
32 | if (client.VFS == null) {
33 | // invalid install
34 | return;
35 | }
36 | VFSFileTree vfs = client.VFS;
37 | using (Stream stream = vfs.Open(@"zone\base.xpak")) {
38 | // do whatever
39 | }
40 | foreach (string fileName in vfs.Files.Where(x => x.StartsWith(@"zone\"))) {
41 | using(Stream stream = vfs.Open(fileName)) {
42 | // could do this too
43 | }
44 | }
45 | ```
46 | ##### Tank: (Overwatch)
47 | TACTLib is used internally in TankLib/DataTool.
48 | ```cs
49 | ClientHandler client = new ClientHandler(path);
50 | ProductHandler_Tank tankHandler = client.ProductHandler as ProdcuctHandler_Tank;
51 | if (tankHandler == null) {
52 | // not a valid overwatch install
53 | return;
54 | }
55 | using (Stream stream = tankHandler.OpenFile(0xE00000000000895)) { // open any asset you want
56 | // in this case, parse the material
57 | }
58 | ```
59 | ##### WorldOfWarcraftV6: (World of Warcraft)
60 | Not all features are implemented yet. Anything below is a concept.
61 | ```cs
62 | ClientHandler client = new ClientHandler(path, new ClientCreateArgs {
63 | HandlerArgs = new ClientCreateArgs_WorldOfWarcraft {
64 | ListFile = "https://raw.githubusercontent.com/wowdev/wow-listfile/master/listfile.txt"
65 | }
66 | });
67 | ProductHandler_WorldOfWarcraftV6 wowHandler = client.ProductHandler as ProductHandler_WorldOfWarcraftV6;
68 | if (wowHandler == null) {
69 | // not a valid warcraft install
70 | return;
71 | }
72 | using (Stream stream = wowHandler.OpenFile("world/maps/azuremyst isle (7.3 intro)/azuremyst isle (7.3 intro).wdt")) { // open any asset you want
73 | // in this case, parse wdt
74 | }
75 | foreach(string file in wowHandler.GetFiles("world")) {
76 | // will be empty if listfile is invalid
77 | }
78 | foreach(string dir in wowHandler.GetDirectories("world")) {
79 | // will be empty if listfile is invalid
80 | }
81 | ```
82 |
83 | ## We can't wait to see the amazing things you will do with it
84 |
--------------------------------------------------------------------------------
/TACTLib/Protocol/NGDP/RibbitTcpClient.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Net.Sockets;
4 | using System.Text;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 | using MimeKit;
8 | using MimeKit.Text;
9 |
10 | namespace TACTLib.Protocol.NGDP
11 | {
12 | [Obsolete("Blizzard's Ribbit TCP server is no longer online")]
13 | public class RibbitTcpClient : NGDPClientBase
14 | {
15 | public readonly Uri m_uri;
16 |
17 | public RibbitTcpClient(string host)
18 | {
19 | if (!Uri.TryCreate(host, UriKind.RelativeOrAbsolute, out var uri)) {
20 | throw new Exception($"unable to create uri from ribbit host: \"{host}\"");
21 | }
22 | m_uri = uri;
23 | }
24 |
25 | public override string Get(string query)
26 | {
27 | Logger.Info(nameof(RibbitTcpClient), $"Fetching Ribbit {query}");
28 |
29 | using var client = new TcpClient();
30 | client.Connect(m_uri.Host, m_uri.Port);
31 |
32 | using var stream = client.GetStream();
33 | using (var writer = new StreamWriter(stream, Encoding.ASCII, leaveOpen: true))
34 | {
35 | writer.WriteLine(query);
36 | writer.Flush();
37 | }
38 |
39 | var message = MimeMessage.Load(stream);
40 | return GetTextBody(message);
41 | }
42 |
43 | public override async Task GetAsync(string query, CancellationToken cancellationToken=default)
44 | {
45 | Logger.Info(nameof(RibbitTcpClient), $"Fetching Ribbit {query}");
46 |
47 | using var client = new TcpClient();
48 | await client.ConnectAsync(m_uri.Host, m_uri.Port, cancellationToken);
49 |
50 | await using var stream = client.GetStream();
51 | await using (var writer = new StreamWriter(stream, Encoding.ASCII, leaveOpen: true))
52 | {
53 | await writer.WriteLineAsync(query.AsMemory(), cancellationToken); // todo: why can't pass string + cancellationtoken?
54 | await writer.FlushAsync(cancellationToken);
55 | }
56 |
57 | var message = await MimeMessage.LoadAsync(stream, cancellationToken);
58 | return GetTextBody(message);
59 | }
60 |
61 | private static string GetTextBody(MimeMessage message)
62 | {
63 | // return message.GetTextBody(TextFormat.Text);
64 | // todo: using this (^) pulls in HtmlEntityDecoder which is about 500kb in size
65 | // mimekit needs an overload where the encoding can be specified instead of auto-detecting
66 |
67 | var body = (MultipartAlternative)message.Body;
68 | if (!body.TryGetValue(TextFormat.Plain, out var textPart))
69 | {
70 | throw new InvalidDataException("ribbit message had no text part");
71 | }
72 | return textPart.GetText(Encoding.UTF8);
73 | }
74 |
75 | public override string GetSummaryQuery() => "v1/summary";
76 | public override string GetVersionsQuery(string product) => $"v1/products/{product}/versions";
77 | public override string GetCDNsQuery(string product) => $"v1/products/{product}/cdns";
78 | public override string GetBGDLsQuery(string product) => $"v1/products/{product}/bgdl";
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/TACTLib/Config/InstallationInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 |
6 | namespace TACTLib.Config {
7 | public class InstallationInfo {
8 | public readonly Dictionary Values;
9 |
10 | public InstallationInfo(Dictionary values) {
11 | Values = values;
12 | }
13 |
14 | public InstallationInfo(List> vals, string product) {
15 | var activeProducts = vals.Where(x => x["Active"] == "1").ToArray();
16 | if (activeProducts.Length == 0) {
17 | Logger.Error("InstallationInfo", "Found no Active products in installation info. Searching all.");
18 | activeProducts = vals.ToArray();
19 | }
20 |
21 | if (activeProducts.Length == 0) {
22 | throw new Exception($"Installation info contains 0 products. Requested product: {product}");
23 | }
24 |
25 | var valuesMatchingRequestedProduct = activeProducts.Where(x => {
26 | if (!x.TryGetValue("Product", out var foundProduct)) return false;
27 | return foundProduct.Equals(product, StringComparison.OrdinalIgnoreCase);
28 | }).SingleOrDefault();
29 |
30 | if (valuesMatchingRequestedProduct != null) {
31 | Values = valuesMatchingRequestedProduct;
32 | return;
33 | }
34 |
35 | // fenris has empty product field
36 | var containsProductField = activeProducts.Any(x => x.TryGetValue("Product", out var foundProduct) && !string.IsNullOrWhiteSpace(foundProduct));
37 | if (!containsProductField) {
38 | if (activeProducts.Length > 1) throw new Exception($"Installation info didn't contain (useful) Product field but >1 product was found. Requested product: {product}");
39 | Values = activeProducts.Single();
40 | return;
41 | }
42 |
43 | throw new Exception($"Failed to find installation info for requested product {product} (found [{string.Join(", ", vals.Select(x => {
44 | x.TryGetValue("Product", out var foundProduct);
45 | foundProduct ??= "null";
46 | return foundProduct;
47 | }))}])");
48 | }
49 |
50 | public static List> ParseToDict(TextReader reader) {
51 | string[]? keys = null;
52 | var valueDicts = new List>();
53 |
54 | string? line;
55 | while ((line = reader.ReadLine()?.Trim()) != null) {
56 | if (line.Length == 0)
57 | {
58 | continue;
59 | }
60 | if (line.StartsWith("##"))
61 | {
62 | continue;
63 | }
64 |
65 | var tokens = line.Split('|');
66 |
67 | if (keys == null) {
68 | keys = new string[tokens.Length];
69 |
70 | for (var j = 0; j < tokens.Length; j++) {
71 | keys[j] = tokens[j].Split('!')[0].Replace(" ", "");
72 | }
73 | } else {
74 | var values = new Dictionary();
75 | for (var j = 0; j < tokens.Length; ++j) {
76 | values[keys[j]] = tokens[j];
77 | }
78 |
79 | valueDicts.Add(values);
80 | }
81 | }
82 |
83 | return valueDicts;
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/TACTLib/Core/Key/TruncatedKey.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers.Binary;
3 | using System.Diagnostics;
4 | using System.Diagnostics.CodeAnalysis;
5 | using System.Runtime.CompilerServices;
6 | using System.Runtime.InteropServices;
7 | using TACTLib.Helpers;
8 | using static TACTLib.Utils;
9 |
10 | namespace TACTLib.Core.Key {
11 | ///
12 | /// Encoding Key
13 | ///
14 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
15 | [InlineArray(CASC_TRUNCATED_KEY_SIZE)]
16 | [DebuggerDisplay("{ToHexString()}")]
17 | [SuppressMessage("ReSharper", "UseSymbolAlias")]
18 | public struct TruncatedKey : IComparable, IEquatable {
19 | // ReSharper disable once InconsistentNaming
20 | /// Encoding Key size, in bytes
21 | public const int CASC_TRUNCATED_KEY_SIZE = 9;
22 |
23 | private byte _first;
24 |
25 | ///
26 | /// Convert to a hex string
27 | ///
28 | /// Hex string
29 | public readonly string ToHexString() {
30 | return Extensions.ToHexString(this);
31 | }
32 |
33 | ///
34 | /// Create from a hex string
35 | ///
36 | /// Source string
37 | /// Created EKey
38 | public static TruncatedKey FromString(ReadOnlySpan @string) {
39 | return FromByteArray(StringToByteArray(@string));
40 | }
41 |
42 | ///
43 | /// Create from a byte array
44 | ///
45 | /// Source array
46 | ///
47 | /// Array length !=
48 | public static TruncatedKey FromByteArray(ReadOnlySpan array) {
49 | if (array.Length < CASC_TRUNCATED_KEY_SIZE)
50 | throw new ArgumentException($"array size < {CASC_TRUNCATED_KEY_SIZE}");
51 |
52 | return MemoryMarshal.Read(array);
53 | }
54 |
55 | public readonly int CompareTo(TruncatedKey other) {
56 | return TruncatedKeyCompare(this, other);
57 | }
58 |
59 | public static int TruncatedKeyCompare(TruncatedKey left, TruncatedKey right) {
60 | var leftSpan = (ReadOnlySpan) left;
61 | var rightSpan = (ReadOnlySpan) right;
62 |
63 | var leftU0 = BinaryPrimitives.ReadUInt64BigEndian(leftSpan);
64 | var rightU0 = BinaryPrimitives.ReadUInt64BigEndian(rightSpan);
65 |
66 | var compareA = leftU0.CompareTo(rightU0);
67 | if (compareA != 0) return compareA;
68 |
69 | var leftU1 = MemoryMarshal.Read(leftSpan.Slice(8));
70 | var rightU1 = MemoryMarshal.Read(rightSpan.Slice(8));
71 | return leftU1.CompareTo(rightU1);
72 | }
73 |
74 | public bool Equals(TruncatedKey other) {
75 | return TruncatedKeyCompare(this, other) == 0;
76 | }
77 |
78 | public override bool Equals(object? obj) => obj is TruncatedKey other && Equals(other);
79 |
80 | public static bool operator ==(TruncatedKey left, TruncatedKey right) => left.Equals(right);
81 | public static bool operator !=(TruncatedKey left, TruncatedKey right) => !(left == right);
82 |
83 | public override int GetHashCode() {
84 | var h = new HashCode();
85 | h.AddBytes(this);
86 | return h.ToHashCode();
87 | }
88 | }
89 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Key/FullKey.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers.Binary;
3 | using System.Diagnostics;
4 | using System.Diagnostics.CodeAnalysis;
5 | using System.Runtime.CompilerServices;
6 | using System.Runtime.InteropServices;
7 | using TACTLib.Helpers;
8 | using static TACTLib.Utils;
9 |
10 | namespace TACTLib.Core.Key {
11 | ///
12 | /// Content Key
13 | ///
14 | [StructLayout(LayoutKind.Sequential, Pack = 1)]
15 | [InlineArray(CASC_FULL_KEY_SIZE)]
16 | [DebuggerDisplay("{ToHexString()}")]
17 | [SuppressMessage("ReSharper", "UseSymbolAlias")]
18 | public struct FullKey : IComparable, IEquatable {
19 | // ReSharper disable once InconsistentNaming
20 | /// Content Key size, in bytes
21 | public const int CASC_FULL_KEY_SIZE = 16;
22 |
23 | private byte _first;
24 |
25 | ///
26 | /// Convert to a hex string
27 | ///
28 | /// Hex string
29 | public readonly string ToHexString() {
30 | return Extensions.ToHexString(this);
31 | }
32 |
33 | ///
34 | /// Create from a hex string
35 | ///
36 | /// Source string
37 | /// Created FullKey
38 | public static FullKey FromString(ReadOnlySpan @string) {
39 | return FromByteArray(StringToByteArray(@string));
40 | }
41 |
42 | ///
43 | /// Create from a byte array
44 | ///
45 | /// Source array
46 | ///
47 | /// Array length !=
48 | public static FullKey FromByteArray(ReadOnlySpan array) {
49 | if (array.Length < CASC_FULL_KEY_SIZE)
50 | throw new ArgumentException($"array size < {CASC_FULL_KEY_SIZE}");
51 |
52 | return MemoryMarshal.Read(array);
53 | }
54 |
55 | public readonly TruncatedKey AsTruncated() {
56 | return MemoryMarshal.Read(this);
57 | }
58 |
59 | public readonly int CompareTo(FullKey other) {
60 | return FullKeyCompare(this, other);
61 | }
62 |
63 | public static int FullKeyCompare(FullKey left, FullKey right) {
64 | var leftSpan = (ReadOnlySpan) left;
65 | var rightSpan = (ReadOnlySpan) right;
66 |
67 | var leftU0 = BinaryPrimitives.ReadUInt64BigEndian(leftSpan);
68 | var rightU0 = BinaryPrimitives.ReadUInt64BigEndian(rightSpan);
69 |
70 | var compareA = leftU0.CompareTo(rightU0);
71 | if (compareA != 0) return compareA;
72 |
73 | var leftU1 = BinaryPrimitives.ReadUInt64BigEndian(leftSpan.Slice(8));
74 | var rightU1 = BinaryPrimitives.ReadUInt64BigEndian(rightSpan.Slice(8));
75 | return leftU1.CompareTo(rightU1);
76 | }
77 |
78 | public bool Equals(FullKey other) {
79 | return FullKeyCompare(this, other) == 0;
80 | }
81 |
82 | public override bool Equals(object? obj) => obj is FullKey other && Equals(other);
83 |
84 | public static bool operator ==(FullKey left, FullKey right) => left.Equals(right);
85 | public static bool operator !=(FullKey left, FullKey right) => !(left == right);
86 |
87 | public override int GetHashCode() {
88 | var h = new HashCode();
89 | h.AddBytes(this);
90 | return h.ToHashCode();
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Tank/ProductHandler_Tank.Bundles.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using TACTLib.Exceptions;
6 | using TACTLib.Helpers;
7 |
8 | namespace TACTLib.Core.Product.Tank {
9 | public partial class ProductHandler_Tank {
10 | private class BundleCache {
11 | public Dictionary m_offsets;
12 | public Memory m_buffer;
13 | }
14 |
15 | private readonly Dictionary m_bundleCache = new Dictionary();
16 |
17 | private readonly Dictionary? m_hackedBundleLookup;
18 | private readonly HashSet? m_hackedLookedUpBundles;
19 |
20 | private void DoBundleLookupHack() {
21 | if (!m_usingResourceGraph) return;
22 |
23 | foreach (var asset in m_assets) {
24 | if ((asset.Key & 0xFFF000000000000ul) != 0x0D90000000000000) continue; // bundles only
25 | if (m_hackedLookedUpBundles!.Contains(asset.Key)) continue; // already done
26 |
27 | if (!TryGetHashData(asset.Key, out _)) {
28 | Logger.Debug("TRG", $"bundle {asset.Key:X16} doesn't exist???");
29 | continue;
30 | }
31 |
32 | Bundle bundle;
33 | try {
34 | bundle = OpenBundle(asset.Key);
35 | } catch (BLTEKeyException e) {
36 | Logger.Debug("TRG", $"can't load bundle {asset.Key:X16} because key {e.MissingKey:X16} is missing from the keyring.");
37 | continue;
38 | }
39 |
40 | if (bundle.Header.OffsetSize == 0) {
41 | throw new InvalidDataException($"failed to load bundle {asset.Key:X16}");
42 | }
43 |
44 | foreach (var valuePair in bundle.Entries) {
45 | m_hackedBundleLookup![valuePair.GUID] = asset.Key;
46 | }
47 |
48 | m_hackedLookedUpBundles.Add(asset.Key);
49 | }
50 | }
51 |
52 | private BundleCache GetBundleCache(ulong bundleGuid) {
53 | lock (m_bundleCache) {
54 | if (m_bundleCache.TryGetValue(bundleGuid, out var cache)) return cache;
55 |
56 | Bundle bundle;
57 | byte[] buf;
58 | using (var bundleStream = OpenFile(bundleGuid)) {
59 | buf = new byte[(int) bundleStream.Length];
60 | bundleStream.DefinitelyRead(buf);
61 | bundleStream.Position = 0;
62 |
63 | //using (Stream outStr = File.OpenWrite($"{bundleGuid:X16}.bndl")) {
64 | // bundleStream.CopyTo(outStr);
65 | //}
66 |
67 | bundle = new Bundle(bundleStream, m_usingResourceGraph);
68 | }
69 |
70 | var offsetMap = bundle.Entries.ToDictionary(x => x.GUID, x => x.Offset);
71 |
72 | cache = new BundleCache {
73 | m_buffer = buf,
74 | m_offsets = offsetMap
75 | };
76 |
77 | m_bundleCache[bundleGuid] = cache;
78 | return cache;
79 | }
80 | }
81 |
82 | private Bundle OpenBundle(ulong bundleGuid) {
83 | using (var bundleStream = OpenFile(bundleGuid))
84 | return new Bundle(bundleStream, m_usingResourceGraph);
85 | }
86 |
87 | private Stream? OpenFileFromBundle(ulong bundleGuid, ulong guid) {
88 | var cache = GetBundleCache(bundleGuid);
89 | if (!cache.m_offsets.TryGetValue(guid, out var offset)) return null;
90 |
91 | var hashData = GetHashData(guid);
92 | var slice = cache.m_buffer.Slice((int) offset, (int) hashData.Size);
93 | return new MemoryStream(slice.ToArray());
94 | }
95 |
96 | ///
97 | /// Clears bundle cache
98 | ///
99 | public void WipeBundleCache() {
100 | lock (m_bundleCache) {
101 | m_bundleCache.Clear();
102 | }
103 | }
104 | }
105 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Tank/CMF/ProCMF_37703.cs:
--------------------------------------------------------------------------------
1 | using static TACTLib.Core.Product.Tank.ManifestCryptoHandler;
2 | using static TACTLib.Core.Product.Tank.ContentManifestFile;
3 |
4 | namespace TACTLib.Core.Product.Tank.CMF
5 | {
6 | [ManifestCrypto(AutoDetectVersion = true, Product = TACTProduct.Overwatch)]
7 | public class ProCMF_37703 : ICMFEncryptionProc
8 | {
9 | public byte[] Key(CMFHeader header, int length)
10 | {
11 | byte[] buffer = new byte[length];
12 |
13 | uint kidx = Constrain(length * header.m_buildVersion);
14 | for (int i = 0; i != length; ++i)
15 | {
16 | buffer[i] = Keytable[kidx % 512];
17 | kidx += (uint)header.m_entryCount;
18 | }
19 |
20 | return buffer;
21 | }
22 |
23 | public byte[] IV(CMFHeader header, byte[] digest, int length)
24 | {
25 | byte[] buffer = new byte[length];
26 |
27 | for (int i = 0; i != length; ++i) buffer[i] = 0;
28 |
29 | return buffer;
30 | }
31 |
32 | private static readonly byte[] Keytable =
33 | {
34 | 0x62, 0x3A, 0x4A, 0xDE, 0xF1, 0x3A, 0x80, 0xF1, 0xFE, 0x4B, 0xBB, 0xBD, 0x4C, 0x27, 0x90, 0x9C,
35 | 0xB4, 0x29, 0xD2, 0xC7, 0x8B, 0x11, 0xEA, 0x53, 0x85, 0xA8, 0x32, 0x38, 0x38, 0xBB, 0x56, 0xB7,
36 | 0x52, 0xD3, 0xD6, 0xBF, 0xC0, 0x75, 0x1B, 0xA2, 0xD9, 0xEF, 0x29, 0x3C, 0x3D, 0x8A, 0xC4, 0xCC,
37 | 0x2F, 0x6D, 0x8C, 0x34, 0xA5, 0x32, 0xDC, 0x1E, 0x12, 0xC8, 0x89, 0x8D, 0xB4, 0x03, 0xDA, 0x11,
38 | 0x17, 0x9F, 0xDC, 0x49, 0x30, 0xEB, 0x95, 0x0E, 0x0E, 0xC0, 0x95, 0xAD, 0xF7, 0x72, 0x22, 0x02,
39 | 0xA3, 0x31, 0x15, 0x41, 0x5D, 0x5B, 0x9A, 0x45, 0xEB, 0x4B, 0x7E, 0x78, 0x86, 0xA1, 0xD3, 0xF2,
40 | 0x1D, 0x39, 0xC8, 0x76, 0x7C, 0x17, 0xEC, 0xC3, 0x5B, 0x38, 0x9E, 0xF7, 0xCE, 0x05, 0x63, 0x8B,
41 | 0x6A, 0x53, 0xDE, 0xCC, 0x25, 0x63, 0xC9, 0x4C, 0x49, 0xDB, 0x60, 0x7D, 0xB6, 0x44, 0xCF, 0xE3,
42 | 0xB2, 0x53, 0xB1, 0xBF, 0x03, 0xE6, 0x65, 0x26, 0x1E, 0x23, 0xB3, 0x23, 0x4F, 0xFE, 0x3E, 0xD9,
43 | 0xBB, 0xE4, 0x13, 0x73, 0x56, 0x2E, 0x83, 0xB8, 0x02, 0x6B, 0x1C, 0x4E, 0x8D, 0x9C, 0x86, 0x22,
44 | 0x1B, 0x52, 0x43, 0x0E, 0x1D, 0xC0, 0xC2, 0x7B, 0x15, 0xE5, 0x4E, 0xE8, 0x68, 0xA9, 0x92, 0xC5,
45 | 0x86, 0x2E, 0x1F, 0x3E, 0xD2, 0xB6, 0x52, 0xCC, 0xA6, 0xB4, 0x42, 0x42, 0x21, 0xD6, 0x96, 0x74,
46 | 0x60, 0xB8, 0xF4, 0x21, 0xD0, 0xD3, 0x07, 0xB7, 0x95, 0xD9, 0xE8, 0xF8, 0x8D, 0xBE, 0x26, 0x66,
47 | 0x6B, 0x7E, 0xE6, 0xC0, 0x93, 0x33, 0xF7, 0x18, 0xC1, 0xF9, 0xB4, 0x5F, 0xC6, 0x77, 0xB4, 0x21,
48 | 0x77, 0x49, 0x8C, 0x1C, 0x10, 0x3F, 0x6A, 0xC6, 0xF9, 0xAC, 0x16, 0x46, 0x1B, 0x2E, 0x39, 0x8B,
49 | 0xB7, 0xEA, 0xCF, 0x20, 0x6C, 0x4E, 0xB0, 0x95, 0x5B, 0xF9, 0x12, 0x57, 0x3C, 0x40, 0x08, 0x55,
50 | 0x2A, 0x35, 0xC2, 0x17, 0x2F, 0x83, 0x92, 0xCD, 0x10, 0x9F, 0x7C, 0x98, 0x23, 0x46, 0xA6, 0xF3,
51 | 0x91, 0x44, 0x33, 0xCF, 0x9A, 0xE0, 0x04, 0x4A, 0x17, 0xC4, 0x6A, 0xA3, 0xF1, 0x45, 0x22, 0x66,
52 | 0x0D, 0xF7, 0x4A, 0xD9, 0x0C, 0xD2, 0xE2, 0x44, 0xD0, 0xF4, 0xD2, 0x8E, 0x02, 0x59, 0xF8, 0xB6,
53 | 0x22, 0xD7, 0x12, 0x07, 0xA2, 0x44, 0x45, 0x63, 0x44, 0x5D, 0x54, 0x0B, 0x0A, 0xD0, 0x94, 0x71,
54 | 0x25, 0xBF, 0xA4, 0x67, 0x4F, 0xC8, 0xB5, 0xAF, 0x5F, 0xF3, 0x42, 0x97, 0x04, 0xE2, 0x74, 0xEF,
55 | 0xEA, 0x35, 0x7F, 0x75, 0x05, 0xF9, 0x06, 0x54, 0x23, 0xEF, 0x8D, 0x1C, 0x84, 0xF0, 0x0F, 0x67,
56 | 0x1E, 0x2F, 0x05, 0xA4, 0xDA, 0x44, 0x95, 0xBF, 0xAA, 0x1B, 0x38, 0xEF, 0xE0, 0x18, 0x60, 0xA8,
57 | 0x82, 0xC0, 0xE4, 0x8E, 0x89, 0x0D, 0x11, 0x48, 0xB5, 0x71, 0x4E, 0x33, 0x4C, 0xF9, 0xE6, 0xD7,
58 | 0x1C, 0xEF, 0x73, 0x31, 0x64, 0x5F, 0x3D, 0x6A, 0x97, 0xB1, 0x01, 0xB2, 0x10, 0x7B, 0x24, 0xB7,
59 | 0x18, 0x86, 0x76, 0xCD, 0x6B, 0x4B, 0x6C, 0x7B, 0x43, 0x04, 0x6F, 0x94, 0xFC, 0x3D, 0x36, 0xEE,
60 | 0x3A, 0x32, 0xF9, 0xDE, 0x00, 0x70, 0x29, 0xB4, 0xA7, 0xDA, 0xD3, 0xC7, 0x2D, 0x79, 0xC3, 0xBB,
61 | 0x5B, 0x13, 0xFE, 0xA4, 0x36, 0xF4, 0x21, 0x9B, 0x62, 0x85, 0x6A, 0x0F, 0x65, 0xB4, 0x00, 0x6C,
62 | 0x48, 0x68, 0xA1, 0x49, 0x07, 0x84, 0x5A, 0xD4, 0x94, 0x2B, 0x79, 0xB9, 0x46, 0x5C, 0xD3, 0x2D,
63 | 0x3E, 0x34, 0xA8, 0x15, 0x61, 0x24, 0x42, 0x62, 0x47, 0xF9, 0x38, 0x9D, 0x60, 0x26, 0xCC, 0x23,
64 | 0x2C, 0xDD, 0x4D, 0x23, 0x69, 0x5B, 0x1B, 0x7D, 0x86, 0x55, 0xDE, 0x04, 0xD4, 0xD6, 0x44, 0xF8,
65 | 0x74, 0xA7, 0x9C, 0x92, 0xE9, 0xE7, 0x23, 0x33, 0xB3, 0xB1, 0x71, 0x2D, 0x6D, 0x73, 0x0B, 0xB9
66 | };
67 | }
68 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Tank/CMF/ProCMF_38459.cs:
--------------------------------------------------------------------------------
1 | using static TACTLib.Core.Product.Tank.ManifestCryptoHandler;
2 | using static TACTLib.Core.Product.Tank.ContentManifestFile;
3 |
4 | namespace TACTLib.Core.Product.Tank.CMF
5 | {
6 | [ManifestCrypto(AutoDetectVersion = true, Product = TACTProduct.Overwatch)]
7 | public class ProCMF_38459 : ICMFEncryptionProc
8 | {
9 | public byte[] Key(CMFHeader header, int length)
10 | {
11 | byte[] buffer = new byte[length];
12 |
13 | uint kidx = Keytable[length + 256];
14 | for (int i = 0; i != length; ++i)
15 | {
16 | buffer[i] = Keytable[kidx % 512];
17 | kidx += (uint)header.m_entryCount;
18 | }
19 |
20 | return buffer;
21 | }
22 |
23 | public byte[] IV(CMFHeader header, byte[] digest, int length)
24 | {
25 | byte[] buffer = new byte[length];
26 |
27 | uint kidx = Keytable[(2 * digest[13] - length) % 512];
28 | for (int i = 0; i != length; ++i)
29 | {
30 | buffer[i] = Keytable[kidx % 512];
31 | buffer[i] = digest[(i + kidx) % SHA1_DIGESTSIZE];
32 | }
33 |
34 | return buffer;
35 | }
36 |
37 | private static readonly byte[] Keytable =
38 | {
39 | 0xA9, 0xD9, 0x35, 0x16, 0x46, 0xF4, 0x84, 0x2A, 0x2E, 0xD6, 0x9D, 0x01, 0x2F, 0xBD, 0x2E, 0xAD,
40 | 0xE0, 0x1C, 0xD7, 0x63, 0xD8, 0x69, 0x9A, 0x8E, 0xC0, 0x0D, 0x65, 0x7F, 0x72, 0x44, 0xEB, 0x8E,
41 | 0x66, 0x12, 0xBC, 0xC0, 0x5E, 0x9F, 0x29, 0x3C, 0xD5, 0xFF, 0x8A, 0x1B, 0x40, 0x50, 0x52, 0xDE,
42 | 0xC1, 0xA5, 0x29, 0xE4, 0xD2, 0x77, 0x8E, 0x8B, 0xB3, 0xBF, 0x7B, 0x09, 0x39, 0xBB, 0x26, 0xAC,
43 | 0x44, 0x62, 0x2A, 0x00, 0x00, 0x97, 0x71, 0x1D, 0x56, 0xE6, 0xF0, 0xEF, 0x18, 0x24, 0xC1, 0xB1,
44 | 0x7B, 0x37, 0xF9, 0xBB, 0xE3, 0x21, 0xE7, 0xC7, 0xBB, 0x8F, 0x98, 0xBF, 0xB9, 0xD1, 0xAF, 0xA9,
45 | 0xD8, 0x04, 0xB4, 0xEC, 0x30, 0x8D, 0x13, 0xCA, 0xC0, 0xF3, 0xEF, 0x09, 0x6C, 0x2C, 0x2C, 0xD8,
46 | 0x9C, 0x20, 0x36, 0xC0, 0x92, 0xAE, 0xEB, 0x78, 0xC6, 0xF5, 0x79, 0xD9, 0x93, 0x35, 0x2D, 0x19,
47 | 0xC5, 0xE1, 0x29, 0xC1, 0x76, 0xA2, 0x85, 0x08, 0x51, 0x2F, 0x52, 0x23, 0x7B, 0x27, 0x0A, 0x6E,
48 | 0xF2, 0x45, 0xB7, 0x60, 0xB9, 0xE0, 0xBA, 0x57, 0xCC, 0x59, 0x59, 0x38, 0x30, 0x1B, 0xA3, 0x39,
49 | 0x8A, 0xD6, 0x38, 0x2D, 0x4A, 0xE8, 0x9C, 0x6C, 0xEF, 0xC8, 0x7D, 0x0B, 0xD5, 0xF6, 0xFF, 0x5D,
50 | 0x20, 0xCE, 0xA7, 0xA9, 0x4A, 0x33, 0x60, 0xDB, 0xBC, 0xC4, 0x1F, 0x8E, 0x2C, 0x27, 0xBA, 0x7A,
51 | 0xBB, 0x5C, 0x66, 0x2C, 0x81, 0x13, 0x30, 0xD0, 0xC4, 0x8A, 0xCC, 0x18, 0x55, 0xA6, 0x32, 0xBB,
52 | 0x10, 0x0D, 0xA0, 0x30, 0xEE, 0x9D, 0x77, 0x1A, 0xC7, 0xCC, 0x9C, 0x7E, 0xE4, 0x9B, 0xFD, 0x94,
53 | 0x9E, 0xFD, 0x8B, 0x09, 0x1B, 0x27, 0x0C, 0x85, 0x38, 0x28, 0x6A, 0x58, 0x8C, 0xB6, 0x24, 0x5A,
54 | 0xD0, 0x07, 0xE8, 0x9A, 0xE7, 0x20, 0x5D, 0xCE, 0xD4, 0x64, 0x01, 0xF9, 0xCF, 0xD3, 0x72, 0xA0,
55 | 0x4C, 0x6A, 0x33, 0x80, 0x89, 0xBC, 0x52, 0x55, 0x26, 0xE8, 0x71, 0xA4, 0x83, 0x12, 0x54, 0x19,
56 | 0x53, 0xB9, 0xF5, 0x25, 0x6C, 0x8D, 0x38, 0x63, 0x5A, 0xD7, 0xF3, 0xE9, 0x14, 0x68, 0x33, 0xD4,
57 | 0x9C, 0xE1, 0xF4, 0x5B, 0x2E, 0x22, 0x9A, 0x08, 0x73, 0xEF, 0xE6, 0x86, 0xC5, 0xA0, 0x76, 0x6E,
58 | 0x0D, 0x4A, 0x13, 0xF4, 0xB2, 0x6D, 0xB2, 0xE8, 0xBB, 0xC3, 0x10, 0xAA, 0xBF, 0x03, 0x03, 0x7A,
59 | 0x62, 0x49, 0x31, 0x9A, 0x3D, 0xA7, 0xEC, 0x7D, 0xCC, 0x4A, 0xC2, 0x8A, 0xB9, 0x65, 0x68, 0xBC,
60 | 0x09, 0x96, 0x0D, 0x6A, 0xAC, 0x0C, 0x49, 0x34, 0x9D, 0xD8, 0xF6, 0xC1, 0x18, 0xEB, 0xD3, 0x80,
61 | 0x38, 0xC2, 0xC8, 0x91, 0x27, 0x67, 0xC7, 0x47, 0xCD, 0x74, 0xF4, 0x09, 0x38, 0xA4, 0x1B, 0x35,
62 | 0xDD, 0x97, 0xBF, 0x7A, 0x40, 0xEC, 0x8B, 0xC0, 0xA3, 0xF8, 0xFE, 0x1A, 0x7A, 0x54, 0x45, 0x51,
63 | 0x15, 0xD7, 0x9B, 0xCE, 0x23, 0xB4, 0x33, 0x90, 0x5B, 0x63, 0xD2, 0xF9, 0xCE, 0xF0, 0x9A, 0xC1,
64 | 0x79, 0xFD, 0xCF, 0x53, 0x50, 0x1C, 0x43, 0x70, 0x6D, 0xF2, 0xBF, 0x45, 0xD0, 0xFA, 0xE8, 0xCA,
65 | 0xA8, 0x49, 0xC6, 0x2A, 0xBD, 0xBA, 0x78, 0x37, 0x5B, 0x82, 0xCC, 0x9D, 0x55, 0x57, 0x44, 0x9A,
66 | 0xDE, 0xAA, 0xF7, 0xB9, 0x15, 0x2C, 0xA4, 0x32, 0x4C, 0xFD, 0xE2, 0xC2, 0xE3, 0xBE, 0x72, 0x30,
67 | 0x97, 0x1B, 0xDD, 0xDF, 0xA8, 0x8D, 0x8F, 0x35, 0x8F, 0x23, 0xE8, 0xA8, 0x9E, 0x84, 0xE1, 0x78,
68 | 0x87, 0xD6, 0x7D, 0x57, 0xF9, 0x00, 0x32, 0xE7, 0x85, 0xAA, 0x84, 0x17, 0xBE, 0xCD, 0x55, 0x77,
69 | 0x57, 0x98, 0x6B, 0xB5, 0xF0, 0x20, 0x99, 0x7D, 0x05, 0xB5, 0x21, 0x74, 0x45, 0x03, 0x05, 0x7A,
70 | 0x87, 0x1B, 0x81, 0xFA, 0x90, 0x98, 0xFB, 0xA8, 0x2A, 0xD9, 0xBD, 0x4C, 0x21, 0xC5, 0xA5, 0xDE
71 | };
72 | }
73 | }
--------------------------------------------------------------------------------
/TACTLib/Helpers/Extensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Runtime.InteropServices;
4 | using CommunityToolkit.HighPerformance;
5 |
6 | namespace TACTLib.Helpers {
7 | public static class Extensions {
8 | #region BinaryReader
9 |
10 | public static void DefinitelyRead(this Stream stream, Span buffer)
11 | {
12 | stream.ReadExactly(buffer);
13 | }
14 |
15 | public static void DefinitelyRead(this BinaryReader reader, Span buffer)
16 | {
17 | DefinitelyRead(reader.BaseStream, buffer);
18 | }
19 |
20 | ///
21 | /// Read struct from a BinaryReader
22 | ///
23 | /// Source reader
24 | /// Struct to read
25 | /// Read struct
26 | public static T Read(this BinaryReader reader) where T : unmanaged
27 | {
28 | return reader.BaseStream.Read();
29 | }
30 |
31 | public static unsafe T Read(this Stream stream) where T : unmanaged
32 | {
33 | var result = default(T);
34 | stream.DefinitelyRead(new Span(&result, sizeof(T)));
35 | return result;
36 | }
37 |
38 | ///
39 | /// Read array of structs from a reader
40 | ///
41 | /// Target reader
42 | /// Count
43 | /// Struct to read
44 | /// Stuct array
45 | public static T[] ReadArray(this BinaryReader reader, int count) where T : unmanaged
46 | {
47 | return reader.BaseStream.ReadArray(count);
48 | }
49 |
50 | public static T[] ReadArray(this Stream stream, int count) where T : unmanaged
51 | {
52 | if (count == 0) return Array.Empty();
53 |
54 | var result = new T[count];
55 | stream.DefinitelyRead(result.AsSpan().AsBytes());
56 | return result;
57 | }
58 |
59 | ///
60 | /// Write a struct to a BinaryWriter
61 | ///
62 | /// Target writer
63 | /// Struct instance to write
64 | /// Struct type
65 | public static void Write(this BinaryWriter writer, T @struct) where T : unmanaged
66 | {
67 | var bytes = MemoryMarshal.CreateReadOnlySpan(ref @struct, 1).AsBytes();
68 | writer.Write(bytes);
69 | }
70 |
71 | ///
72 | /// Write an array of structs to a BinaryWriter
73 | ///
74 | /// Target write
75 | /// Struct array to write
76 | /// Struct type
77 | public static void WriteStructArray(this BinaryWriter writer, T[] @struct) where T : unmanaged
78 | {
79 | var bytes = @struct.AsSpan().AsBytes();
80 | writer.Write(bytes);
81 | }
82 |
83 | /// Read a big endian 32-bit int
84 | // ReSharper disable once InconsistentNaming
85 | public static int ReadInt32BE(this BinaryReader reader)
86 | {
87 | var s = reader.Read();
88 | return (int)s.ToInt();
89 | }
90 | /// Read a big endian 16-bit int
91 | // ReSharper disable once InconsistentNaming
92 | public static short ReadInt16BE(this BinaryReader reader)
93 | {
94 | return (short)ReadUInt16BE(reader);
95 | }
96 |
97 | /// Read a big endian 16-bit uint
98 | // ReSharper disable once InconsistentNaming
99 | public static ushort ReadUInt16BE(this BinaryReader reader)
100 | {
101 | var s = reader.Read();
102 | return s.ToInt();
103 | }
104 |
105 | /// Read a big-endian 24-bit int
106 | // ReSharper disable once InconsistentNaming
107 | public static int ReadInt24BE(this BinaryReader reader)
108 | {
109 | var s = reader.Read();
110 | return s.ToInt();
111 | }
112 | #endregion
113 |
114 | /// Convert to a hexadecimal string
115 | public static string ToHexString(this ReadOnlySpan data)
116 | {
117 | return Convert.ToHexString(data);
118 | }
119 | }
120 | }
--------------------------------------------------------------------------------
/TACTLib/Container/StaticContainerHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers.Binary;
3 | using System.IO;
4 | using System.Linq;
5 | using CommunityToolkit.HighPerformance.Helpers;
6 | using TACTLib.Client;
7 | using TACTLib.Helpers;
8 |
9 | namespace TACTLib.Container {
10 | public class StaticContainerHandler : IContainerHandler {
11 | private readonly ClientHandler m_client;
12 | private readonly string m_containerDirectory;
13 |
14 | private readonly byte m_keyLayoutIndexBits;
15 | private readonly KeyLayout[] m_keyLayouts;
16 |
17 | private struct KeyLayout {
18 | public byte m_chunkBits;
19 | public byte m_archiveBits;
20 | public byte m_offsetBits;
21 | }
22 |
23 | public StaticContainerHandler(ClientHandler client) {
24 | m_client = client;
25 | if (client.BasePath == null) throw new Exception("no 'BasePath' specified");
26 | m_containerDirectory = Path.Combine(client.BasePath, GetContainerDirectory(client.Product));
27 |
28 | m_keyLayoutIndexBits = byte.Parse(client.ConfigHandler.BuildConfig.Values["key-layout-index-bits"][0]);
29 | m_keyLayouts = new KeyLayout[Math.Max((int)Math.Pow(2, m_keyLayoutIndexBits), 1)];
30 |
31 | foreach (var keyLayoutPair in client.ConfigHandler.BuildConfig.Values
32 | .Where(static x => x.Key.StartsWith("key-layout-") && x.Key != "key-layout-index-bits")) {
33 |
34 | var layoutIndex = byte.Parse(keyLayoutPair.Key.AsSpan("key-layout-".Length));
35 |
36 | m_keyLayouts[layoutIndex] = new KeyLayout {
37 | m_chunkBits = byte.Parse(keyLayoutPair.Value[0]),
38 | m_archiveBits = byte.Parse(keyLayoutPair.Value[1]),
39 | m_offsetBits = byte.Parse(keyLayoutPair.Value[2])
40 | };
41 | }
42 | }
43 |
44 | private void ExtractStorageLocation(FullEKey ekey, out ulong chunk, out ulong archive, out ulong offset) {
45 | //var chunk = 0ul;
46 | //var archive = 0ul;
47 | //var offset = 0ul;
48 | //var alignment = 0ul;
49 |
50 | var ekeySpan = (ReadOnlySpan)ekey;
51 | var ekeyHiUl = BinaryPrimitives.ReadUInt64BigEndian(ekeySpan.Slice(8));
52 |
53 | //Console.Out.WriteLine($"{ekeyHiUl}");
54 | var keyLayoutBitCount = m_keyLayoutIndexBits;
55 | var keyLayoutIndexBitOffset = 56-keyLayoutBitCount;
56 | var keyLayoutIndex = BitHelper.ExtractRange(ekeyHiUl, (byte)keyLayoutIndexBitOffset, keyLayoutBitCount);
57 | var keyLayout = m_keyLayouts[keyLayoutIndex];
58 |
59 | var chunkBitCount = keyLayout.m_chunkBits;
60 | var chunkBitOffset = keyLayoutIndexBitOffset-chunkBitCount;
61 | chunk = BitHelper.ExtractRange(ekeyHiUl, (byte)chunkBitOffset, chunkBitCount);
62 |
63 | var archiveBitCount = keyLayout.m_archiveBits;
64 | var archiveBitOffset = chunkBitOffset-archiveBitCount;
65 | archive = BitHelper.ExtractRange(ekeyHiUl, (byte)archiveBitOffset, archiveBitCount);
66 |
67 | var offsetBitCount = keyLayout.m_offsetBits;
68 | var offsetBitOffset = archiveBitOffset-offsetBitCount;
69 | offset = BitHelper.ExtractRange(ekeyHiUl, (byte)offsetBitOffset, offsetBitCount);
70 | }
71 |
72 | private static string GetFileName(ulong chunk, ulong archive) {
73 | return $"data.{chunk:D3}.{archive:D3}";
74 | }
75 |
76 | private string GetFilePath(ulong chunk, ulong archive) {
77 | return Path.Combine(m_containerDirectory, GetFileName(chunk, archive));
78 | }
79 |
80 | public ArraySegment? OpenEKey(FullEKey ekey, int eSize) {
81 | ExtractStorageLocation(ekey, out var chunk, out var archive, out var offset);
82 |
83 | using var stream = File.OpenRead(GetFilePath(chunk, archive));
84 | stream.Position = (long)offset;
85 | var data = GC.AllocateUninitializedArray(eSize);
86 | stream.DefinitelyRead(data);
87 |
88 | return data;
89 | }
90 |
91 | public bool CheckResidency(FullEKey ekey) {
92 | ExtractStorageLocation(ekey, out var chunk, out var archive, out _);
93 | return File.Exists(GetFilePath(chunk, archive));
94 | }
95 |
96 | private static string GetContainerDirectory(TACTProduct product) {
97 | if (product == TACTProduct.Overwatch)
98 | return Path.Combine("data");
99 |
100 | throw new NotImplementedException($"Product \"{product}\" is not supported.");
101 | }
102 | }
103 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Tank/CMF/ProCMF_39823.cs:
--------------------------------------------------------------------------------
1 | using static TACTLib.Core.Product.Tank.ManifestCryptoHandler;
2 | using static TACTLib.Core.Product.Tank.ContentManifestFile;
3 |
4 | namespace TACTLib.Core.Product.Tank.CMF
5 | {
6 | [ManifestCrypto(AutoDetectVersion = true, Product = TACTProduct.Overwatch)]
7 | public class ProCMF_39823 : ICMFEncryptionProc
8 | {
9 | public byte[] Key(CMFHeader header, int length)
10 | {
11 | byte[] buffer = new byte[length];
12 |
13 | uint kidx = header.m_buildVersion * (uint)length;
14 | for (int i = 0; i != length; ++i)
15 | {
16 | buffer[i] = Keytable[kidx % 512];
17 | kidx += 3;
18 | }
19 |
20 | return buffer;
21 | }
22 |
23 | public byte[] IV(CMFHeader header, byte[] digest, int length)
24 | {
25 | byte[] buffer = new byte[length];
26 |
27 | uint kidx = header.m_buildVersion * (uint)length;
28 | for (int i = 0; i != length; ++i)
29 | {
30 | buffer[i] = Keytable[kidx % 512];
31 | kidx = header.m_buildVersion - kidx;
32 | buffer[i] ^= digest[(i + kidx) % SHA1_DIGESTSIZE];
33 | }
34 |
35 | return buffer;
36 | }
37 |
38 | private static readonly byte[] Keytable =
39 | {
40 | 0x91, 0x27, 0x29, 0xDA, 0x64, 0x19, 0x01, 0x37, 0x28, 0x19, 0x83, 0xEA, 0xA8, 0x0A, 0xDE, 0x5B,
41 | 0x77, 0xC2, 0xAD, 0xFA, 0xD6, 0x84, 0x04, 0xBC, 0x2D, 0xC1, 0x69, 0xC7, 0x9E, 0x32, 0xC3, 0x92,
42 | 0xD4, 0xFD, 0xDE, 0x4C, 0x07, 0x75, 0x17, 0xE1, 0x79, 0x86, 0xEB, 0x74, 0xF7, 0xF7, 0x0A, 0xC3,
43 | 0x11, 0xD9, 0xBF, 0xCC, 0x34, 0xF5, 0xB4, 0xA6, 0x56, 0xA5, 0x05, 0x45, 0x52, 0x10, 0xA0, 0xEA,
44 | 0xDC, 0x25, 0x24, 0xA3, 0xAE, 0x13, 0x93, 0x6F, 0xAD, 0x44, 0x7D, 0x3D, 0xB4, 0x6B, 0x15, 0xAC,
45 | 0x33, 0xD3, 0x11, 0x65, 0xA2, 0xCD, 0x29, 0xAB, 0x76, 0xDA, 0x04, 0xD9, 0x10, 0x44, 0x49, 0x82,
46 | 0xA9, 0x1D, 0xF9, 0xD7, 0x3F, 0xD1, 0x60, 0x8C, 0x7B, 0x89, 0x26, 0x77, 0x76, 0x29, 0xAC, 0x1B,
47 | 0xBB, 0xED, 0x99, 0xD6, 0x1C, 0xC8, 0xFC, 0x39, 0x05, 0x62, 0x9A, 0x5A, 0x14, 0xE8, 0x4D, 0x6C,
48 | 0x06, 0xA5, 0x91, 0xD5, 0x9C, 0x53, 0x4A, 0x62, 0x5D, 0x65, 0x9A, 0x4E, 0x8A, 0xE8, 0x6F, 0x15,
49 | 0x51, 0xE5, 0xCF, 0xDA, 0x7E, 0x78, 0xAF, 0xC2, 0x48, 0x23, 0xF2, 0x0C, 0x9F, 0x1B, 0x64, 0xA5,
50 | 0x59, 0x44, 0x92, 0xDF, 0x5D, 0xFA, 0xDE, 0x23, 0x37, 0x19, 0x47, 0xA1, 0xE7, 0x5C, 0x8C, 0x0E,
51 | 0xDF, 0xA7, 0x71, 0xAC, 0xF2, 0xF3, 0xD5, 0x53, 0xFC, 0xA3, 0x54, 0xE8, 0x1A, 0xBE, 0x91, 0x50,
52 | 0x61, 0x2B, 0x0D, 0x96, 0xDB, 0x99, 0xA3, 0xE9, 0x34, 0x86, 0x24, 0x09, 0xBF, 0x20, 0x09, 0xA2,
53 | 0x71, 0x2E, 0xAE, 0x44, 0xCA, 0x51, 0xCB, 0x6E, 0xE5, 0xD7, 0xAF, 0xD3, 0x5F, 0x45, 0x0F, 0xEC,
54 | 0x8C, 0xD9, 0x83, 0x5F, 0x0E, 0x8D, 0xA6, 0x83, 0x1A, 0x9C, 0x10, 0x73, 0x2E, 0x3C, 0x1A, 0xA6,
55 | 0x41, 0xCE, 0x40, 0x88, 0xC4, 0x2D, 0x73, 0x79, 0x9F, 0x84, 0x34, 0xA9, 0xF0, 0x1A, 0x41, 0x94,
56 | 0xFB, 0xFE, 0xDD, 0xAC, 0x37, 0xBE, 0x70, 0x1D, 0x72, 0xD9, 0xE8, 0xE9, 0xF7, 0x1D, 0x4A, 0xDA,
57 | 0xD8, 0x40, 0xA5, 0xA8, 0xBB, 0x80, 0x25, 0x7B, 0x76, 0x0C, 0xA6, 0x4F, 0xC8, 0x2B, 0xA7, 0x29,
58 | 0x71, 0xCB, 0x37, 0x03, 0x64, 0x36, 0xC1, 0x05, 0x6B, 0xD3, 0x63, 0x86, 0xFD, 0x69, 0x57, 0x01,
59 | 0x6A, 0xBE, 0x00, 0x25, 0x92, 0xDF, 0xA4, 0x88, 0xB5, 0x8C, 0x9D, 0xF1, 0xE4, 0x89, 0x43, 0x39,
60 | 0xA6, 0x5D, 0xDF, 0x4E, 0x2C, 0xCA, 0xAC, 0x56, 0xAB, 0x84, 0x11, 0x89, 0xFA, 0xD0, 0xD4, 0x74,
61 | 0xB2, 0x05, 0xCD, 0x69, 0x4E, 0x08, 0x11, 0xEB, 0xDB, 0xAF, 0x80, 0xD0, 0xF5, 0x85, 0xE9, 0x50,
62 | 0x4B, 0xFB, 0x9F, 0x75, 0xCC, 0xDA, 0x27, 0xE7, 0xC1, 0x6A, 0x93, 0xBE, 0xDA, 0x21, 0x8B, 0xF8,
63 | 0x58, 0x81, 0xE7, 0x55, 0xDE, 0x63, 0xB8, 0xA2, 0xAB, 0xD6, 0x69, 0x0B, 0x59, 0xA7, 0x4F, 0x2A,
64 | 0x87, 0xA1, 0x2F, 0x93, 0xE1, 0x0C, 0xFD, 0x96, 0xD9, 0xEB, 0x2D, 0x1C, 0x9A, 0x8F, 0x3F, 0x0B,
65 | 0x08, 0xA4, 0x98, 0x72, 0xA4, 0x10, 0x6F, 0x34, 0x05, 0x2E, 0x14, 0x4F, 0xA0, 0x9A, 0x50, 0x8D,
66 | 0x64, 0xDB, 0x86, 0xF5, 0xB5, 0x12, 0xC0, 0x96, 0xF2, 0x54, 0xE7, 0x60, 0xC2, 0xD4, 0x61, 0x75,
67 | 0xFB, 0xE5, 0x2D, 0x8C, 0x6A, 0x2B, 0xF1, 0xFC, 0xAB, 0x97, 0xA9, 0x6C, 0x8D, 0xEC, 0x04, 0xB7,
68 | 0x80, 0x88, 0x3E, 0x8C, 0xA1, 0x0E, 0x15, 0x44, 0x3D, 0x29, 0xAD, 0x32, 0xB1, 0xF5, 0xB2, 0x6F,
69 | 0xC7, 0x71, 0xCD, 0xC0, 0xD4, 0xC4, 0x3C, 0x5D, 0x9B, 0x80, 0xC3, 0x67, 0xE6, 0x21, 0xB5, 0x01,
70 | 0x6B, 0x9C, 0x4F, 0xA1, 0x5B, 0x03, 0xB2, 0x17, 0x28, 0xE6, 0xA6, 0x69, 0x39, 0xEC, 0xA0, 0x8E,
71 | 0xF0, 0xD7, 0x12, 0x3F, 0xF7, 0x5C, 0xB6, 0xA5, 0x4B, 0xAD, 0x71, 0xFC, 0x9C, 0x20, 0x2B, 0xB9
72 | };
73 | }
74 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Tank/CMF/ProCMF_37865.cs:
--------------------------------------------------------------------------------
1 | using static TACTLib.Core.Product.Tank.ManifestCryptoHandler;
2 | using static TACTLib.Core.Product.Tank.ContentManifestFile;
3 |
4 | namespace TACTLib.Core.Product.Tank.CMF
5 | {
6 | [ManifestCrypto(AutoDetectVersion = true, Product = TACTProduct.Overwatch)]
7 | public class ProCMF_37865 : ICMFEncryptionProc
8 | {
9 | public byte[] Key(CMFHeader header, int length)
10 | {
11 | byte[] buffer = new byte[length];
12 |
13 | uint kidx = Keytable[header.m_buildVersion & 511];
14 | for (int i = 0; i != length; ++i)
15 | {
16 | buffer[i] = Keytable[kidx % 512];
17 | kidx -= 489;
18 | }
19 |
20 | return buffer;
21 | }
22 |
23 | public byte[] IV(CMFHeader header, byte[] digest, int length)
24 | {
25 | byte[] buffer = new byte[length];
26 |
27 | uint kidx = Keytable[header.m_dataCount & 511];
28 | for (int i = 0; i != length; ++i)
29 | {
30 | buffer[i] = Keytable[kidx % 512];
31 | kidx = header.m_buildVersion - kidx;
32 | buffer[i] ^= digest[(i + kidx) % SHA1_DIGESTSIZE];
33 | }
34 |
35 | return buffer;
36 | }
37 |
38 | private static readonly byte[] Keytable =
39 | {
40 | 0x29, 0x02, 0x18, 0x1D, 0xA1, 0xA3, 0x43, 0x4F, 0xDC, 0xF7, 0x5A, 0x9E, 0x86, 0x8D, 0xA9, 0xF8,
41 | 0x7F, 0xFE, 0x83, 0x5C, 0x1F, 0x08, 0xC9, 0x35, 0x16, 0x26, 0xE4, 0x64, 0xB7, 0x14, 0x34, 0x93,
42 | 0x64, 0x76, 0xF1, 0xDE, 0x8F, 0x38, 0xDB, 0xC7, 0xBA, 0x2B, 0xC2, 0x7F, 0xD3, 0x3C, 0xFA, 0x19,
43 | 0xA4, 0x01, 0x7E, 0x19, 0x08, 0x9B, 0xD6, 0xA3, 0x02, 0xE1, 0xC9, 0xF0, 0x88, 0xE2, 0x98, 0xED,
44 | 0xA3, 0x9B, 0xFC, 0x28, 0x22, 0xB3, 0x37, 0xFA, 0xAA, 0x99, 0x79, 0x75, 0xA0, 0x82, 0x94, 0x53,
45 | 0x7C, 0x66, 0xDF, 0xC8, 0x83, 0xC3, 0xD1, 0x15, 0xAF, 0xDF, 0x6A, 0xA7, 0x9A, 0xE2, 0x1D, 0x61,
46 | 0xF8, 0xF7, 0x34, 0xBA, 0x70, 0xA5, 0x9C, 0x88, 0xC2, 0x64, 0x3A, 0x1D, 0x1D, 0x88, 0x7A, 0x78,
47 | 0x0F, 0x4F, 0x54, 0xF0, 0x63, 0x4B, 0x55, 0x4F, 0x22, 0x56, 0xDD, 0xBD, 0xC9, 0x89, 0x0D, 0xA7,
48 | 0xD2, 0xE7, 0xE9, 0xC3, 0x2B, 0x16, 0xD2, 0xA8, 0x38, 0x07, 0x7C, 0x56, 0xA3, 0x24, 0x4F, 0x53,
49 | 0x92, 0x1F, 0xEF, 0xD5, 0xC3, 0xE5, 0x53, 0xF1, 0x21, 0x57, 0x75, 0x62, 0x34, 0x37, 0x50, 0xF2,
50 | 0xC3, 0xDE, 0x1A, 0x67, 0x41, 0x0E, 0x13, 0xAC, 0x31, 0xE8, 0x06, 0x89, 0x74, 0xAF, 0x33, 0xF6,
51 | 0xC2, 0x78, 0x68, 0x75, 0x1F, 0xD2, 0xA0, 0xE1, 0xEF, 0xCD, 0x7D, 0x18, 0x5D, 0xA4, 0x9B, 0x3A,
52 | 0x79, 0x51, 0xAB, 0x7F, 0x65, 0xC9, 0xCA, 0x78, 0x40, 0x12, 0x27, 0xE0, 0xEA, 0x24, 0xA1, 0x41,
53 | 0x60, 0x21, 0x49, 0x07, 0x81, 0x18, 0x08, 0xB6, 0x82, 0xDF, 0x0D, 0x33, 0x55, 0xB6, 0xC4, 0x26,
54 | 0x9A, 0xB1, 0xA0, 0xEF, 0xA9, 0xA1, 0x66, 0xEF, 0xAB, 0x8E, 0x5B, 0x30, 0x69, 0x4F, 0x0E, 0xFB,
55 | 0xAC, 0x3A, 0xC7, 0x12, 0xE0, 0x83, 0x97, 0xD9, 0x46, 0xE5, 0x6F, 0x20, 0xEF, 0x26, 0x79, 0xEC,
56 | 0x4B, 0x76, 0xB7, 0xC5, 0xE5, 0x32, 0x82, 0xE3, 0xB3, 0x82, 0xAC, 0xA4, 0xDB, 0x2A, 0x30, 0x35,
57 | 0x58, 0x1A, 0xDC, 0x41, 0xD8, 0x87, 0x88, 0x0B, 0x86, 0x0A, 0xEA, 0x80, 0x1F, 0x46, 0x47, 0xC5,
58 | 0xC1, 0xE5, 0x02, 0x88, 0xCA, 0xA8, 0x09, 0xFE, 0xEE, 0x0C, 0xA2, 0x9D, 0x61, 0xDE, 0x53, 0x87,
59 | 0xCC, 0xC3, 0x86, 0x6D, 0x91, 0xED, 0x46, 0x61, 0xCA, 0x74, 0xF2, 0x53, 0x0D, 0x6F, 0xF2, 0x3D,
60 | 0xB6, 0x0F, 0xCC, 0x26, 0x90, 0x66, 0xFD, 0xAF, 0xEE, 0x96, 0xF4, 0x11, 0x32, 0xAE, 0x02, 0x7B,
61 | 0x26, 0x08, 0x4D, 0x3C, 0x52, 0x5F, 0x4E, 0x7B, 0xEA, 0xAE, 0x7C, 0xA7, 0x63, 0xED, 0x05, 0x88,
62 | 0xED, 0x84, 0x0E, 0x70, 0x50, 0x0B, 0xD7, 0xF4, 0x83, 0x2A, 0xFC, 0xC9, 0x01, 0xD8, 0xC5, 0x1C,
63 | 0x4F, 0x0B, 0x50, 0x94, 0x1F, 0x5C, 0xD0, 0x1C, 0x8B, 0x50, 0x27, 0xF6, 0x9A, 0xE9, 0x4E, 0x2C,
64 | 0xF7, 0x32, 0x72, 0x7B, 0x46, 0xE3, 0x78, 0x02, 0xED, 0x31, 0x70, 0xA2, 0x02, 0x75, 0x71, 0xC5,
65 | 0x65, 0x45, 0x83, 0x7D, 0x1E, 0xB5, 0x9D, 0x5A, 0x2A, 0x9D, 0x4C, 0xD4, 0xF8, 0x2D, 0xDA, 0xAF,
66 | 0xCD, 0x24, 0x70, 0xB5, 0x78, 0xAD, 0xD6, 0x19, 0x43, 0xF5, 0x49, 0x5D, 0xFD, 0x79, 0xA8, 0x1A,
67 | 0xD1, 0x5D, 0x96, 0xD6, 0x93, 0x39, 0x1B, 0xB6, 0x61, 0x10, 0x4D, 0xE6, 0x99, 0x05, 0xBA, 0xEA,
68 | 0xEA, 0xAD, 0x3D, 0x61, 0x5B, 0xE3, 0x7D, 0x1B, 0x8C, 0x47, 0xD9, 0x7D, 0x82, 0xFB, 0x5C, 0x49,
69 | 0xE3, 0x5E, 0xFD, 0xF1, 0x5B, 0x2E, 0x97, 0x73, 0xDA, 0x4C, 0xA4, 0xBF, 0x0A, 0x95, 0xA5, 0xAA,
70 | 0xDB, 0x70, 0x52, 0x6E, 0xBC, 0xE5, 0xEF, 0x14, 0x4E, 0x4B, 0x1D, 0xE9, 0xCE, 0x26, 0x1F, 0xDB,
71 | 0x02, 0x1D, 0x81, 0x38, 0xF3, 0xCD, 0xFF, 0xB4, 0x6A, 0x5E, 0x19, 0xD4, 0x6B, 0xA6, 0xB9, 0xA1
72 | };
73 | }
74 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Tank/CMF/ProCMF_42539.cs:
--------------------------------------------------------------------------------
1 | using static TACTLib.Core.Product.Tank.ManifestCryptoHandler;
2 | using static TACTLib.Core.Product.Tank.ContentManifestFile;
3 |
4 | namespace TACTLib.Core.Product.Tank.CMF
5 | {
6 | [ManifestCrypto(AutoDetectVersion = true, Product = TACTProduct.Overwatch)]
7 | public class ProCMF_42539 : ICMFEncryptionProc
8 | {
9 | public byte[] Key(CMFHeader header, int length)
10 | {
11 | byte[] buffer = new byte[length];
12 |
13 | uint kidx = Keytable[length + 256];
14 | for (int i = 0; i != length; ++i)
15 | {
16 | buffer[i] = Keytable[SignedMod(kidx, 512)];
17 | kidx += 3;
18 | }
19 |
20 | return buffer;
21 | }
22 |
23 | public byte[] IV(CMFHeader header, byte[] digest, int length)
24 | {
25 | byte[] buffer = new byte[length];
26 |
27 | uint kidx = (uint)length * header.m_buildVersion;
28 | for (int i = 0; i != length; ++i)
29 | {
30 | buffer[i] = Keytable[SignedMod(kidx, 512)];
31 | kidx -= 43;
32 | buffer[i] ^= digest[SignedMod(kidx + header.m_buildVersion, SHA1_DIGESTSIZE)];
33 | }
34 |
35 | return buffer;
36 | }
37 |
38 | private static readonly byte[] Keytable =
39 | {
40 | 0x7F, 0x81, 0xB1, 0x26, 0x5F, 0xCC, 0x7B, 0x70, 0xD6, 0x8F, 0x1E, 0x4E, 0x37, 0xFF, 0x9C, 0x31,
41 | 0xBD, 0xC7, 0x03, 0x04, 0x16, 0x14, 0x8E, 0xA0, 0x1A, 0x66, 0xE0, 0x23, 0xF8, 0xAB, 0x3C, 0xEC,
42 | 0x99, 0x0D, 0x9C, 0x35, 0x52, 0xB2, 0x76, 0x6A, 0x7A, 0x66, 0x89, 0xA2, 0xFF, 0x1D, 0xC1, 0x90,
43 | 0xD7, 0x22, 0x2C, 0x64, 0xD9, 0x58, 0x36, 0x96, 0x10, 0xD2, 0x26, 0x36, 0xFB, 0xB1, 0x74, 0x41,
44 | 0x9C, 0x6C, 0x2E, 0x30, 0x58, 0x32, 0x99, 0xCC, 0x8C, 0x42, 0x7B, 0xFF, 0x93, 0x66, 0x45, 0x5C,
45 | 0x86, 0xF4, 0x68, 0x6F, 0xAD, 0x47, 0xFF, 0xA4, 0xBA, 0x63, 0xF4, 0xED, 0xCF, 0x1E, 0x77, 0x2C,
46 | 0x2B, 0x6B, 0x64, 0x2B, 0x30, 0xEC, 0x14, 0x32, 0x9D, 0xAF, 0xF7, 0xB6, 0x51, 0xAF, 0x0A, 0xA0,
47 | 0xF3, 0x1E, 0x30, 0x23, 0xBD, 0x69, 0x09, 0x86, 0x17, 0xE4, 0xCC, 0xF0, 0x18, 0x06, 0xC3, 0x7D,
48 | 0x2D, 0x6D, 0x46, 0xE8, 0xA4, 0xDE, 0x77, 0x51, 0xAE, 0xEC, 0x2F, 0x98, 0x4C, 0x7A, 0x79, 0x21,
49 | 0x1D, 0x94, 0x11, 0x01, 0x68, 0xD9, 0x59, 0x21, 0xE3, 0x73, 0x4C, 0xD3, 0xA4, 0xC7, 0x23, 0x7C,
50 | 0x11, 0x67, 0x2F, 0x3B, 0x50, 0xB1, 0x7C, 0x00, 0xD2, 0x65, 0xDB, 0xFA, 0x0B, 0x3F, 0x3D, 0xF7,
51 | 0xB1, 0x46, 0x23, 0x7F, 0x35, 0x29, 0x06, 0xB4, 0x09, 0xA8, 0xA5, 0x44, 0x68, 0x1A, 0x5D, 0x20,
52 | 0xD7, 0x0A, 0x9A, 0x87, 0xBC, 0x4B, 0x38, 0x23, 0x56, 0x1D, 0xF5, 0x04, 0x83, 0xC4, 0xDF, 0x0F,
53 | 0x74, 0xF3, 0xF7, 0x1B, 0xCA, 0x17, 0xE3, 0x45, 0xFD, 0x6A, 0x41, 0xCF, 0x16, 0x71, 0x95, 0x08,
54 | 0x2E, 0xBD, 0x00, 0x04, 0x73, 0xC0, 0xEC, 0xCD, 0x5F, 0x63, 0xC4, 0xA5, 0xAE, 0x42, 0xEA, 0x33,
55 | 0x81, 0x3B, 0x17, 0xB0, 0x93, 0xAB, 0x6F, 0x25, 0xB7, 0xE8, 0x5F, 0xAA, 0xF3, 0x57, 0xB9, 0xF9,
56 | 0x4D, 0xA0, 0xCC, 0x6E, 0x9F, 0x89, 0x9F, 0x53, 0xE1, 0xB5, 0xE3, 0xB4, 0xCC, 0x91, 0xAE, 0x88,
57 | 0x66, 0x98, 0xF7, 0xD6, 0x2C, 0x24, 0x26, 0x41, 0x75, 0xD0, 0x6F, 0xAB, 0xAC, 0xA2, 0x85, 0xD6,
58 | 0x25, 0xC6, 0x06, 0xEE, 0xE9, 0x49, 0xDC, 0xE2, 0x56, 0x97, 0x6B, 0xB3, 0x11, 0x8C, 0xCC, 0x3F,
59 | 0x8B, 0x44, 0x91, 0x34, 0x07, 0x25, 0x14, 0x56, 0x78, 0x4F, 0x22, 0xC1, 0x00, 0x90, 0x47, 0xC1,
60 | 0xC2, 0xBD, 0xF7, 0x74, 0xE8, 0x84, 0x59, 0x43, 0x2D, 0xCB, 0xDF, 0xDA, 0x56, 0x6D, 0xC0, 0xEA,
61 | 0xE3, 0x93, 0xC6, 0x4B, 0x02, 0xBA, 0xB9, 0x1A, 0xBC, 0xD4, 0x18, 0x07, 0xDF, 0x42, 0x99, 0xAF,
62 | 0xD3, 0xB9, 0x47, 0x6B, 0x15, 0x15, 0x0F, 0x74, 0x57, 0xF8, 0x9A, 0x8D, 0x89, 0x99, 0x4D, 0x07,
63 | 0xDD, 0x1E, 0x52, 0xE8, 0xC0, 0x68, 0x4B, 0x22, 0x8E, 0xA0, 0x4A, 0x57, 0x1E, 0x2D, 0x18, 0xDA,
64 | 0x93, 0xB7, 0xFE, 0x36, 0xF7, 0x28, 0x86, 0x2B, 0x7E, 0xBD, 0x7C, 0x0C, 0xE9, 0xDA, 0x2F, 0x2A,
65 | 0xD7, 0x1C, 0x55, 0xC3, 0xD1, 0x4C, 0x96, 0xD2, 0x07, 0x5D, 0x8D, 0x1A, 0xB7, 0x3D, 0x7E, 0x8E,
66 | 0xD2, 0x5D, 0xC6, 0x90, 0x00, 0xF3, 0x23, 0xDE, 0x36, 0xCE, 0xA6, 0x88, 0x1C, 0x77, 0xAD, 0x25,
67 | 0xCD, 0x92, 0x46, 0x7D, 0x97, 0x7A, 0xB6, 0x97, 0x36, 0xF9, 0xBC, 0x0B, 0xD3, 0x6A, 0x01, 0x0D,
68 | 0x7D, 0x0B, 0x49, 0x84, 0xA4, 0x29, 0x34, 0x68, 0x09, 0x31, 0x1C, 0x3C, 0xEB, 0xCA, 0x2A, 0x25,
69 | 0x2E, 0xCB, 0xD0, 0xAE, 0x35, 0x9C, 0xBA, 0xEA, 0x8A, 0x87, 0x6C, 0x9B, 0x8B, 0xC8, 0x68, 0xAA,
70 | 0x87, 0x4B, 0xFF, 0xEC, 0xAB, 0x0B, 0x6F, 0xD6, 0x94, 0x95, 0xC4, 0x61, 0x48, 0x65, 0x62, 0x56,
71 | 0x1E, 0x4D, 0x25, 0x5F, 0xB4, 0x34, 0xFA, 0x86, 0xDC, 0x43, 0x2C, 0x1F, 0x77, 0xF6, 0x53, 0x6D
72 | };
73 | }
74 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Tank/TRG/ProTRG_71213.cs:
--------------------------------------------------------------------------------
1 | using static TACTLib.Core.Product.Tank.ManifestCryptoHandler;
2 | using static TACTLib.Core.Product.Tank.ResourceGraph;
3 |
4 | namespace TACTLib.Core.Product.Tank.TRG
5 | {
6 | [ManifestCrypto(AutoDetectVersion = true, Product = TACTProduct.Overwatch)]
7 | public class ProTRG_71213 : ITRGEncryptionProc
8 | {
9 | public byte[] Key(TRGHeader header, int length)
10 | {
11 | byte[] buffer = new byte[length];
12 | uint kidx = (uint)length * header.m_buildVersion;
13 | for (uint i = 0; i != length; ++i)
14 | {
15 | buffer[i] = Keytable[SignedMod(kidx, 512)];
16 | kidx -= header.m_buildVersion & 511;
17 | }
18 | return buffer;
19 | }
20 |
21 | public byte[] IV(TRGHeader header, byte[] digest, int length)
22 | {
23 | byte[] buffer = new byte[length];
24 | uint kidx = Keytable[header.m_skinCount & 511];
25 | for (int i = 0; i != length; ++i)
26 | {
27 | buffer[i] = Keytable[SignedMod(kidx, 512)];
28 | kidx -= 43;
29 | buffer[i] ^= digest[SignedMod(kidx + header.m_skinCount, SHA1_DIGESTSIZE)];
30 | }
31 | return buffer;
32 | }
33 |
34 | private static readonly byte[] Keytable =
35 | {
36 | 0xDC, 0x16, 0xEC, 0x6D, 0x4C, 0xFC, 0x14, 0xDC, 0x6E, 0xB8, 0x10, 0x25, 0x0A, 0xDB, 0x3D, 0x1B,
37 | 0xB0, 0xED, 0x18, 0xB4, 0xF5, 0x57, 0xDC, 0x91, 0x53, 0xF6, 0x22, 0x60, 0xFE, 0x2B, 0xB9, 0x62,
38 | 0x3D, 0x84, 0x7B, 0x1F, 0x83, 0x9E, 0x63, 0xDE, 0x88, 0xD5, 0x2C, 0x0C, 0xE2, 0x39, 0x2F, 0xE2,
39 | 0xBC, 0x20, 0x71, 0xCE, 0x8D, 0x13, 0x7B, 0xE2, 0x06, 0x49, 0x5F, 0xCF, 0x78, 0x72, 0x9E, 0x7E,
40 | 0x64, 0x6E, 0x04, 0x02, 0xC6, 0x23, 0x37, 0x90, 0xB9, 0xBE, 0x14, 0xEC, 0x3E, 0x8E, 0x94, 0x5F,
41 | 0x6A, 0x9A, 0x17, 0xCA, 0xDC, 0x57, 0x0E, 0x75, 0xE8, 0xF3, 0xC0, 0x60, 0xE5, 0xB6, 0xD1, 0x5D,
42 | 0xCF, 0x81, 0xAD, 0x18, 0x47, 0xAD, 0x36, 0x39, 0xCB, 0xBD, 0x5E, 0x96, 0x07, 0xC0, 0x0D, 0xBB,
43 | 0x54, 0x81, 0xA8, 0x00, 0xE2, 0x32, 0x2E, 0x19, 0x4A, 0x4F, 0x51, 0xD1, 0x46, 0x3B, 0x5B, 0x8E,
44 | 0x21, 0x28, 0xDD, 0x8E, 0xA3, 0x8E, 0x55, 0xAC, 0x88, 0x45, 0x16, 0x87, 0xF4, 0xF3, 0x9E, 0x82,
45 | 0xA0, 0x3D, 0x2C, 0x57, 0x8B, 0xD9, 0x94, 0xAE, 0xF7, 0x53, 0xE8, 0xD7, 0xF8, 0xB9, 0x55, 0xD8,
46 | 0x7A, 0x0F, 0x98, 0xAF, 0x1C, 0x47, 0x52, 0x00, 0x9E, 0xC8, 0x64, 0xD1, 0xDE, 0xC8, 0x1A, 0x46,
47 | 0x15, 0xF4, 0x12, 0xD1, 0x7E, 0x8E, 0xEB, 0x70, 0x35, 0x98, 0x21, 0xEF, 0xDA, 0x40, 0x8D, 0xDF,
48 | 0x44, 0xD7, 0x25, 0x0B, 0x97, 0x71, 0x6A, 0xED, 0xBA, 0x14, 0x0C, 0xA1, 0x31, 0x7F, 0x73, 0x96,
49 | 0xEF, 0x82, 0xD2, 0xBD, 0x27, 0x09, 0x29, 0x7E, 0x17, 0xB4, 0xB5, 0xCC, 0x62, 0x4E, 0x51, 0xC6,
50 | 0x3A, 0xBC, 0x0B, 0x00, 0x9F, 0x75, 0x75, 0x14, 0xB6, 0x13, 0x1E, 0xB2, 0x66, 0x46, 0x30, 0x94,
51 | 0x34, 0x48, 0xCA, 0x69, 0x1E, 0xD3, 0x76, 0x95, 0x3F, 0x6F, 0x3C, 0x66, 0x8A, 0x87, 0xB9, 0x6D,
52 | 0xB9, 0xEA, 0x46, 0x54, 0xD0, 0x7C, 0xCF, 0xBD, 0x6A, 0xAC, 0x5E, 0xD4, 0x72, 0xD5, 0x74, 0xE2,
53 | 0xB4, 0xBC, 0xC7, 0xB8, 0x23, 0xE8, 0x68, 0xC7, 0x95, 0x87, 0xE8, 0xCB, 0xE2, 0xA8, 0x97, 0x89,
54 | 0xE7, 0xFA, 0x9D, 0xD3, 0x53, 0x1D, 0xCB, 0x10, 0x5B, 0x3B, 0x05, 0x79, 0x3A, 0x09, 0x9C, 0xD2,
55 | 0xBA, 0x64, 0xB7, 0xE3, 0x55, 0xD1, 0xB7, 0xF7, 0xAC, 0x8B, 0x5D, 0x05, 0xD6, 0xF8, 0x5C, 0x1E,
56 | 0xB5, 0x9D, 0xAC, 0x48, 0x15, 0x16, 0x21, 0xA7, 0x6D, 0x89, 0x8C, 0x60, 0x35, 0x2E, 0x8A, 0x95,
57 | 0x6B, 0xB1, 0x98, 0x91, 0x61, 0x88, 0x74, 0x2C, 0x13, 0xDB, 0x9C, 0x0A, 0xE1, 0x98, 0xEC, 0xF5,
58 | 0xAB, 0xBA, 0x9E, 0x76, 0x78, 0xF3, 0x60, 0x4C, 0xFD, 0x43, 0x37, 0x36, 0x6C, 0x1D, 0xFC, 0xC8,
59 | 0x17, 0xF1, 0x66, 0x18, 0x1A, 0xD0, 0x09, 0x1F, 0xFA, 0x60, 0xB9, 0xC5, 0xFC, 0x63, 0x45, 0x6C,
60 | 0x83, 0xA7, 0x2B, 0x2A, 0xE2, 0xE1, 0x18, 0x4A, 0xA4, 0x6F, 0xA1, 0xA9, 0xD2, 0xDC, 0x26, 0xC7,
61 | 0xD6, 0xD7, 0x71, 0xBD, 0xF6, 0xAA, 0x8F, 0x7A, 0x89, 0x28, 0x69, 0xEB, 0x41, 0x45, 0xAE, 0x9F,
62 | 0x8E, 0x75, 0xDD, 0xCE, 0x10, 0xC4, 0x91, 0x2D, 0x0E, 0x4D, 0x6D, 0x45, 0x56, 0xF5, 0xAB, 0xA1,
63 | 0xBF, 0x6D, 0x7D, 0xE0, 0x37, 0xB4, 0xBA, 0x55, 0x4F, 0xB9, 0x12, 0x67, 0xAB, 0x18, 0xCA, 0xA3,
64 | 0xB2, 0xDE, 0xEE, 0x3B, 0x19, 0x42, 0xCF, 0x0F, 0x91, 0x92, 0xFE, 0x24, 0x81, 0x77, 0xC1, 0xE6,
65 | 0xC7, 0xA3, 0x07, 0xC1, 0xF2, 0x43, 0x28, 0x34, 0x61, 0x9B, 0x70, 0x5F, 0x64, 0x29, 0xF3, 0x93,
66 | 0x20, 0x29, 0x06, 0x24, 0x9D, 0x99, 0x4D, 0xED, 0xAF, 0x39, 0x04, 0x0D, 0x09, 0x09, 0x0F, 0xB4,
67 | 0x62, 0x04, 0xD4, 0x6E, 0x60, 0x28, 0x23, 0xB2, 0x77, 0xFE, 0x64, 0xAA, 0x65, 0x8E, 0xB9, 0xFE
68 | };
69 | }
70 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Tank/CMF/ProCMF_39028.cs:
--------------------------------------------------------------------------------
1 | using static TACTLib.Core.Product.Tank.ManifestCryptoHandler;
2 | using static TACTLib.Core.Product.Tank.ContentManifestFile;
3 |
4 | namespace TACTLib.Core.Product.Tank.CMF
5 | {
6 | [ManifestCrypto(AutoDetectVersion = true, Product = TACTProduct.Overwatch)]
7 | public class ProCMF_39028 : ICMFEncryptionProc
8 | {
9 | public byte[] Key(CMFHeader header, int length)
10 | {
11 | byte[] buffer = new byte[length];
12 |
13 | uint kidx = Constrain(length * header.m_buildVersion);
14 | for (int i = 0; i != length; ++i)
15 | {
16 | buffer[i] = Keytable[kidx % 512];
17 | kidx = Constrain(header.m_buildVersion - kidx);
18 | }
19 |
20 | return buffer;
21 | }
22 |
23 | public byte[] IV(CMFHeader header, byte[] digest, int length)
24 | {
25 | byte[] buffer = new byte[length];
26 |
27 | uint kidx = Keytable[header.m_buildVersion & 511];
28 | for (int i = 0; i != length; ++i)
29 | {
30 | buffer[i] = Keytable[kidx % 512];
31 | kidx += 3;
32 | buffer[i] ^= digest[(kidx - i) % SHA1_DIGESTSIZE];
33 | }
34 |
35 | return buffer;
36 | }
37 |
38 | private static readonly byte[] Keytable =
39 | {
40 | 0x21, 0xC3, 0x55, 0x59, 0x94, 0x08, 0xC7, 0x70, 0xFB, 0x09, 0x4D, 0xD7, 0xB0, 0x71, 0x1D, 0x48,
41 | 0xB5, 0xC8, 0x1A, 0xB5, 0xE2, 0xC7, 0x2F, 0xA4, 0x0E, 0x59, 0x97, 0x9F, 0x94, 0xA6, 0x43, 0x26,
42 | 0x6A, 0xB9, 0xA9, 0x18, 0xE4, 0xC8, 0x64, 0x29, 0x93, 0x8A, 0x4C, 0x8B, 0x4F, 0xA3, 0x50, 0xC7,
43 | 0x36, 0x26, 0x34, 0xA8, 0xC4, 0x88, 0x16, 0xF2, 0x42, 0x43, 0x6D, 0x74, 0x05, 0x59, 0xC1, 0xF1,
44 | 0x96, 0x22, 0xBA, 0xC0, 0x05, 0x07, 0x06, 0x22, 0x0F, 0xBB, 0xD3, 0xC9, 0xC0, 0xDE, 0xE7, 0x99,
45 | 0xF7, 0xBE, 0x82, 0x32, 0x09, 0x5D, 0xEC, 0xD2, 0x7D, 0xC2, 0x73, 0x9C, 0x87, 0x1F, 0x7D, 0x75,
46 | 0xFD, 0x4C, 0x79, 0x35, 0x98, 0xA6, 0x20, 0x37, 0x68, 0x27, 0xE1, 0x7B, 0x44, 0xBF, 0x50, 0x98,
47 | 0xED, 0x1B, 0x15, 0x6C, 0xAD, 0x31, 0x14, 0x0E, 0x4B, 0x81, 0xF2, 0xEE, 0x8E, 0xF3, 0x3A, 0x07,
48 | 0xF3, 0x03, 0x66, 0xC7, 0x0A, 0xCD, 0xCC, 0x7B, 0xD9, 0xB8, 0xC1, 0x4B, 0x17, 0x68, 0xD0, 0x8D,
49 | 0xC7, 0x5F, 0x28, 0xCA, 0xC3, 0xC4, 0xAC, 0xA2, 0xFF, 0x75, 0xE8, 0xF5, 0x0F, 0xD8, 0xE6, 0x3A,
50 | 0x40, 0xF3, 0x8C, 0x0A, 0xED, 0xBC, 0x97, 0xDE, 0x13, 0x6D, 0x04, 0x75, 0x3F, 0xFE, 0x71, 0x08,
51 | 0xC0, 0x28, 0x87, 0x11, 0x9C, 0xC1, 0x75, 0x43, 0x8E, 0x24, 0xCB, 0xEE, 0x79, 0x45, 0x85, 0xD5,
52 | 0x34, 0xF2, 0x6B, 0x75, 0xE7, 0x3D, 0xD8, 0x20, 0x6F, 0x86, 0x13, 0xF9, 0x4E, 0x6D, 0x66, 0xDA,
53 | 0x40, 0x57, 0xCF, 0xA3, 0xA4, 0x4D, 0xAC, 0x9C, 0xA0, 0xA7, 0x25, 0xD6, 0xC7, 0x4F, 0xC7, 0xD6,
54 | 0x7D, 0x23, 0x21, 0xA5, 0xA3, 0x34, 0x62, 0xD0, 0x63, 0x0B, 0xCF, 0x1A, 0xE3, 0x95, 0xED, 0x0C,
55 | 0x12, 0xD0, 0xD4, 0x1A, 0x14, 0x8F, 0x15, 0x44, 0x49, 0xE1, 0x69, 0xF9, 0x2D, 0x0C, 0x47, 0x0E,
56 | 0x83, 0x63, 0xA5, 0x95, 0x19, 0x07, 0x77, 0x10, 0x6A, 0x5A, 0x8C, 0x35, 0x34, 0xBC, 0x35, 0x76,
57 | 0x0C, 0x2C, 0x0E, 0xFB, 0x42, 0x20, 0x8C, 0x93, 0xE8, 0x02, 0x25, 0x9A, 0x66, 0x90, 0x0A, 0x2F,
58 | 0x9D, 0xB7, 0x5C, 0x40, 0x98, 0x82, 0x86, 0xF9, 0xEA, 0x45, 0xF9, 0xC1, 0x31, 0xC2, 0x39, 0x16,
59 | 0xFD, 0xE7, 0x61, 0xE9, 0xB9, 0x88, 0xD3, 0x4D, 0x48, 0x5D, 0x33, 0x12, 0x83, 0x6C, 0xDF, 0x92,
60 | 0x26, 0xE5, 0x17, 0x42, 0xD2, 0x4C, 0xC7, 0x83, 0xC9, 0x0E, 0x7D, 0x60, 0x9A, 0xCF, 0x77, 0x5A,
61 | 0x12, 0xB5, 0x86, 0xB4, 0xCE, 0xA2, 0xC9, 0xBD, 0xED, 0x94, 0xA4, 0xA5, 0x84, 0x80, 0xBC, 0xCB,
62 | 0x54, 0x9F, 0x55, 0xCA, 0xA0, 0x04, 0x18, 0x63, 0x56, 0x12, 0xC1, 0x9C, 0xF7, 0xF7, 0x57, 0x65,
63 | 0xF9, 0x7A, 0xDE, 0x7E, 0xC9, 0xE5, 0xFD, 0x65, 0x1E, 0x03, 0x0F, 0x55, 0x8D, 0xBA, 0x16, 0xBE,
64 | 0xF4, 0x25, 0x9F, 0xA0, 0x16, 0x10, 0xAB, 0x91, 0xFA, 0xFD, 0xB3, 0xC0, 0x70, 0x7A, 0x89, 0xD0,
65 | 0x65, 0x52, 0x63, 0xD2, 0x33, 0xA1, 0xD2, 0xD8, 0xFD, 0x2F, 0x4E, 0xC9, 0x19, 0xDD, 0x86, 0xE9,
66 | 0x7B, 0x9A, 0xCD, 0x82, 0x8B, 0x00, 0x33, 0xA5, 0x3F, 0x0D, 0x1C, 0xB7, 0x71, 0xAA, 0xFE, 0xF6,
67 | 0x09, 0xE3, 0x1B, 0x71, 0x6B, 0x95, 0x35, 0xFB, 0x4D, 0x3F, 0x04, 0x00, 0x09, 0x17, 0x36, 0xD9,
68 | 0x80, 0x46, 0x39, 0x4B, 0x32, 0x51, 0x0E, 0x52, 0xD7, 0x92, 0x46, 0x20, 0x23, 0x8A, 0x2E, 0xAC,
69 | 0xF0, 0xED, 0xBB, 0xFE, 0x4E, 0x58, 0xD7, 0xE9, 0x50, 0xA3, 0x4C, 0x6D, 0xB4, 0xE5, 0x41, 0x58,
70 | 0x33, 0xDD, 0xDF, 0xEE, 0xAE, 0x96, 0xFA, 0xCE, 0x7A, 0x23, 0xC3, 0x3F, 0x22, 0x85, 0x68, 0xBC,
71 | 0x36, 0x0B, 0x9D, 0x4E, 0x04, 0x62, 0x9D, 0xCD, 0xC3, 0x5E, 0x9F, 0xDE, 0xB2, 0xDD, 0xCA, 0xF7
72 | };
73 | }
74 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Tank/CMF/ProCMF_35455.cs:
--------------------------------------------------------------------------------
1 | using static TACTLib.Core.Product.Tank.ManifestCryptoHandler;
2 | using static TACTLib.Core.Product.Tank.ContentManifestFile;
3 |
4 | namespace TACTLib.Core.Product.Tank.CMF
5 | {
6 | [ManifestCrypto(AutoDetectVersion = true, Product = TACTProduct.Overwatch)]
7 | public class ProCMF_35455 : ICMFEncryptionProc
8 | {
9 | public byte[] Key(CMFHeader header, int length)
10 | {
11 | byte[] buffer = new byte[length];
12 |
13 | uint kidx = 193;
14 | const uint increment = 319;
15 | for (int i = 0; i != length; ++i)
16 | {
17 | buffer[i] = Keytable[kidx % 512];
18 | kidx -= increment;
19 | }
20 |
21 | return buffer;
22 | }
23 |
24 | public byte[] IV(CMFHeader header, byte[] digest, int length)
25 | {
26 | byte[] buffer = new byte[length];
27 |
28 | uint kidx = Keytable[header.m_dataCount & 511];
29 | for (int i = 0; i != length; ++i)
30 | {
31 | kidx += (uint)header.m_entryCount + digest[header.m_entryCount % SHA1_DIGESTSIZE];
32 | buffer[i] = digest[kidx % SHA1_DIGESTSIZE];
33 | }
34 |
35 | return buffer;
36 | }
37 |
38 | private static readonly byte[] Keytable =
39 | {
40 | 0x80, 0x8A, 0x26, 0xFC, 0x2E, 0xCC, 0x67, 0xEC, 0x9D, 0xEC, 0x33, 0xEC, 0xCA, 0xA8, 0x86, 0x38,
41 | 0x54, 0x3F, 0x9E, 0xD3, 0x5A, 0xC4, 0xDC, 0x67, 0xA5, 0xE0, 0xB5, 0x06, 0x8D, 0xD7, 0xED, 0x5F,
42 | 0x4C, 0xE9, 0xF3, 0x28, 0x05, 0x19, 0x2E, 0xAF, 0x55, 0x15, 0x85, 0x34, 0x82, 0x82, 0xEA, 0xC3,
43 | 0xD6, 0x34, 0xC0, 0x74, 0xD2, 0xDE, 0x40, 0x8A, 0x56, 0x04, 0x7A, 0x50, 0x7F, 0x65, 0x68, 0xF2,
44 | 0x00, 0x0C, 0x6F, 0xEB, 0x11, 0x20, 0xE8, 0xDE, 0x03, 0x9E, 0xF4, 0x54, 0x09, 0xF5, 0xC1, 0x1A,
45 | 0x4A, 0x8C, 0x90, 0xD6, 0x2B, 0x39, 0xD6, 0x4A, 0x8B, 0x0A, 0x77, 0x7C, 0x40, 0x6C, 0xED, 0xD1,
46 | 0x19, 0xE0, 0x40, 0x48, 0x40, 0x6B, 0xF9, 0x25, 0x48, 0xD5, 0xCC, 0x99, 0x21, 0x95, 0x90, 0x9B,
47 | 0x81, 0xD6, 0xD9, 0x4D, 0xB4, 0xF2, 0xAD, 0x65, 0xE9, 0x21, 0x81, 0x33, 0x7D, 0x99, 0x3F, 0x96,
48 | 0xEE, 0x66, 0x15, 0xBB, 0x6E, 0x2D, 0xE8, 0xE7, 0x68, 0x7A, 0xA2, 0x47, 0x8B, 0x65, 0xC4, 0x38,
49 | 0xC1, 0xDE, 0x17, 0x75, 0x3F, 0x9A, 0x4F, 0x4F, 0x4C, 0x26, 0xDD, 0x45, 0x26, 0x7D, 0x46, 0x9D,
50 | 0x92, 0x54, 0xDE, 0x22, 0x39, 0xDA, 0x7A, 0x50, 0x46, 0x78, 0x80, 0x4A, 0x12, 0x2E, 0x2C, 0x4D,
51 | 0x5F, 0x50, 0x8A, 0xB3, 0x2D, 0x7D, 0x74, 0x55, 0x8C, 0xF7, 0x69, 0xC0, 0x6C, 0x3E, 0x97, 0x38,
52 | 0xED, 0x20, 0x42, 0x2C, 0xA3, 0x00, 0xFF, 0xB7, 0x33, 0x0F, 0xF7, 0xB2, 0x84, 0x74, 0x7C, 0x31,
53 | 0xC3, 0x0B, 0x61, 0xCF, 0x68, 0x85, 0x8A, 0x59, 0x27, 0x1A, 0x6D, 0x5A, 0x21, 0xB5, 0x1D, 0x4B,
54 | 0x74, 0xD9, 0x5D, 0x86, 0x24, 0x03, 0x03, 0x61, 0x3E, 0x26, 0x06, 0x4C, 0xEA, 0xC8, 0xB1, 0x95,
55 | 0x03, 0xEE, 0xA7, 0x63, 0x87, 0x76, 0x6C, 0x87, 0xAF, 0xDC, 0xFB, 0x8C, 0x1A, 0xFC, 0x9A, 0xF0,
56 | 0xFF, 0x20, 0x47, 0x0B, 0xEC, 0xE1, 0x53, 0x81, 0x4B, 0xCD, 0xCE, 0x3C, 0x80, 0xC3, 0x1F, 0x57,
57 | 0xFC, 0xDB, 0x63, 0x9D, 0x5E, 0x53, 0x3F, 0xAC, 0x45, 0xA7, 0xB0, 0x13, 0xE2, 0x4D, 0x8B, 0x0F,
58 | 0xB1, 0xC3, 0x67, 0x3F, 0x80, 0xD0, 0xFF, 0xE6, 0x7A, 0x13, 0xEE, 0x87, 0x74, 0xC3, 0x31, 0xCF,
59 | 0x85, 0xF1, 0x46, 0x52, 0x3D, 0x5B, 0x1E, 0xB6, 0x7C, 0xBB, 0x57, 0x58, 0x23, 0x01, 0x9D, 0xC1,
60 | 0x39, 0xD0, 0xC5, 0xD7, 0x02, 0x2E, 0x53, 0xBD, 0xAB, 0x22, 0x75, 0x78, 0x80, 0xAE, 0xAD, 0x42,
61 | 0xED, 0xBB, 0x74, 0xF4, 0x09, 0x3F, 0x60, 0x3E, 0x54, 0xF8, 0xA1, 0x12, 0xA4, 0xE2, 0xE1, 0x14,
62 | 0xD7, 0x2E, 0x78, 0x9F, 0xB2, 0x33, 0x80, 0x08, 0xFA, 0x76, 0xAB, 0x1C, 0xEE, 0x8E, 0x1F, 0x04,
63 | 0xD2, 0x01, 0xAF, 0x9A, 0x0E, 0xF1, 0xC5, 0x1F, 0x26, 0x0F, 0x11, 0xF4, 0x23, 0xD6, 0x1F, 0xB5,
64 | 0x79, 0xF7, 0x5D, 0x54, 0xC6, 0x85, 0xE0, 0xDE, 0x08, 0x5A, 0x62, 0x4B, 0x7B, 0x04, 0xB6, 0x1A,
65 | 0x3A, 0x65, 0xEB, 0xC2, 0xD2, 0x1E, 0xAE, 0x98, 0x30, 0x0E, 0xB7, 0x8A, 0x7A, 0xE2, 0x5A, 0x89,
66 | 0x9C, 0x9D, 0x57, 0x4D, 0xB0, 0x68, 0x97, 0xB5, 0x73, 0x42, 0x63, 0xA1, 0x38, 0xF7, 0xBE, 0x50,
67 | 0xF3, 0xFF, 0x29, 0xE9, 0x5A, 0x0B, 0x88, 0x94, 0x19, 0x39, 0xD2, 0xEE, 0xEF, 0x82, 0xE0, 0x83,
68 | 0xCA, 0xFB, 0x39, 0xD9, 0xFF, 0x2B, 0x1F, 0xC9, 0x24, 0x3F, 0xAB, 0xAE, 0xA7, 0x59, 0x92, 0x58,
69 | 0x78, 0xB3, 0xB1, 0x52, 0x28, 0xF1, 0x50, 0x4A, 0x49, 0x53, 0x95, 0xDF, 0x0F, 0x2A, 0xF4, 0xAF,
70 | 0x00, 0x89, 0x6D, 0xA7, 0xEA, 0xA8, 0x97, 0x98, 0x05, 0x35, 0x01, 0xAF, 0xB4, 0x33, 0xF6, 0xCF,
71 | 0xC7, 0x7F, 0x18, 0xC3, 0x27, 0x3F, 0xC0, 0x36, 0x15, 0xE2, 0x29, 0x31, 0x99, 0x12, 0x44, 0x06
72 | };
73 | }
74 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Tank/CMF/ProCMF_35780.cs:
--------------------------------------------------------------------------------
1 | using static TACTLib.Core.Product.Tank.ManifestCryptoHandler;
2 | using static TACTLib.Core.Product.Tank.ContentManifestFile;
3 |
4 | namespace TACTLib.Core.Product.Tank.CMF
5 | {
6 | [ManifestCrypto(AutoDetectVersion = true, Product = TACTProduct.Overwatch)]
7 | public class ProCMF_35780 : ICMFEncryptionProc
8 | {
9 | public byte[] Key(CMFHeader header, int length)
10 | {
11 | byte[] buffer = new byte[length];
12 |
13 | uint kidx = 193;
14 | const uint increment = 319;
15 | for (int i = 0; i != length; ++i)
16 | {
17 | buffer[i] = Keytable[kidx % 512];
18 | kidx -= increment;
19 | }
20 |
21 | return buffer;
22 | }
23 |
24 | public byte[] IV(CMFHeader header, byte[] digest, int length)
25 | {
26 | byte[] buffer = new byte[length];
27 |
28 | uint kidx = Keytable[header.m_dataCount & 511];
29 | for (int i = 0; i != length; ++i)
30 | {
31 | kidx += (uint)header.m_entryCount + digest[header.m_entryCount % SHA1_DIGESTSIZE];
32 | buffer[i] = digest[kidx % SHA1_DIGESTSIZE];
33 | }
34 |
35 | return buffer;
36 | }
37 |
38 | private static readonly byte[] Keytable =
39 | {
40 | 0x80, 0x8A, 0x26, 0xFC, 0x2E, 0xCC, 0x67, 0xEC, 0x9D, 0xEC, 0x33, 0xEC, 0xCA, 0xA8, 0x86, 0x38,
41 | 0x54, 0x3F, 0x9E, 0xD3, 0x5A, 0xC4, 0xDC, 0x67, 0xA5, 0xE0, 0xB5, 0x06, 0x8D, 0xD7, 0xED, 0x5F,
42 | 0x4C, 0xE9, 0xF3, 0x28, 0x05, 0x19, 0x2E, 0xAF, 0x55, 0x15, 0x85, 0x34, 0x82, 0x82, 0xEA, 0xC3,
43 | 0xD6, 0x34, 0xC0, 0x74, 0xD2, 0xDE, 0x40, 0x8A, 0x56, 0x04, 0x7A, 0x50, 0x7F, 0x65, 0x68, 0xF2,
44 | 0x00, 0x0C, 0x6F, 0xEB, 0x11, 0x20, 0xE8, 0xDE, 0x03, 0x9E, 0xF4, 0x54, 0x09, 0xF5, 0xC1, 0x1A,
45 | 0x4A, 0x8C, 0x90, 0xD6, 0x2B, 0x39, 0xD6, 0x4A, 0x8B, 0x0A, 0x77, 0x7C, 0x40, 0x6C, 0xED, 0xD1,
46 | 0x19, 0xE0, 0x40, 0x48, 0x40, 0x6B, 0xF9, 0x25, 0x48, 0xD5, 0xCC, 0x99, 0x21, 0x95, 0x90, 0x9B,
47 | 0x81, 0xD6, 0xD9, 0x4D, 0xB4, 0xF2, 0xAD, 0x65, 0xE9, 0x21, 0x81, 0x33, 0x7D, 0x99, 0x3F, 0x96,
48 | 0xEE, 0x66, 0x15, 0xBB, 0x6E, 0x2D, 0xE8, 0xE7, 0x68, 0x7A, 0xA2, 0x47, 0x8B, 0x65, 0xC4, 0x38,
49 | 0xC1, 0xDE, 0x17, 0x75, 0x3F, 0x9A, 0x4F, 0x4F, 0x4C, 0x26, 0xDD, 0x45, 0x26, 0x7D, 0x46, 0x9D,
50 | 0x92, 0x54, 0xDE, 0x22, 0x39, 0xDA, 0x7A, 0x50, 0x46, 0x78, 0x80, 0x4A, 0x12, 0x2E, 0x2C, 0x4D,
51 | 0x5F, 0x50, 0x8A, 0xB3, 0x2D, 0x7D, 0x74, 0x55, 0x8C, 0xF7, 0x69, 0xC0, 0x6C, 0x3E, 0x97, 0x38,
52 | 0xED, 0x20, 0x42, 0x2C, 0xA3, 0x00, 0xFF, 0xB7, 0x33, 0x0F, 0xF7, 0xB2, 0x84, 0x74, 0x7C, 0x31,
53 | 0xC3, 0x0B, 0x61, 0xCF, 0x68, 0x85, 0x8A, 0x59, 0x27, 0x1A, 0x6D, 0x5A, 0x21, 0xB5, 0x1D, 0x4B,
54 | 0x74, 0xD9, 0x5D, 0x86, 0x24, 0x03, 0x03, 0x61, 0x3E, 0x26, 0x06, 0x4C, 0xEA, 0xC8, 0xB1, 0x95,
55 | 0x03, 0xEE, 0xA7, 0x63, 0x87, 0x76, 0x6C, 0x87, 0xAF, 0xDC, 0xFB, 0x8C, 0x1A, 0xFC, 0x9A, 0xF0,
56 | 0xFF, 0x20, 0x47, 0x0B, 0xEC, 0xE1, 0x53, 0x81, 0x4B, 0xCD, 0xCE, 0x3C, 0x80, 0xC3, 0x1F, 0x57,
57 | 0xFC, 0xDB, 0x63, 0x9D, 0x5E, 0x53, 0x3F, 0xAC, 0x45, 0xA7, 0xB0, 0x13, 0xE2, 0x4D, 0x8B, 0x0F,
58 | 0xB1, 0xC3, 0x67, 0x3F, 0x80, 0xD0, 0xFF, 0xE6, 0x7A, 0x13, 0xEE, 0x87, 0x74, 0xC3, 0x31, 0xCF,
59 | 0x85, 0xF1, 0x46, 0x52, 0x3D, 0x5B, 0x1E, 0xB6, 0x7C, 0xBB, 0x57, 0x58, 0x23, 0x01, 0x9D, 0xC1,
60 | 0x39, 0xD0, 0xC5, 0xD7, 0x02, 0x2E, 0x53, 0xBD, 0xAB, 0x22, 0x75, 0x78, 0x80, 0xAE, 0xAD, 0x42,
61 | 0xED, 0xBB, 0x74, 0xF4, 0x09, 0x3F, 0x60, 0x3E, 0x54, 0xF8, 0xA1, 0x12, 0xA4, 0xE2, 0xE1, 0x14,
62 | 0xD7, 0x2E, 0x78, 0x9F, 0xB2, 0x33, 0x80, 0x08, 0xFA, 0x76, 0xAB, 0x1C, 0xEE, 0x8E, 0x1F, 0x04,
63 | 0xD2, 0x01, 0xAF, 0x9A, 0x0E, 0xF1, 0xC5, 0x1F, 0x26, 0x0F, 0x11, 0xF4, 0x23, 0xD6, 0x1F, 0xB5,
64 | 0x79, 0xF7, 0x5D, 0x54, 0xC6, 0x85, 0xE0, 0xDE, 0x08, 0x5A, 0x62, 0x4B, 0x7B, 0x04, 0xB6, 0x1A,
65 | 0x3A, 0x65, 0xEB, 0xC2, 0xD2, 0x1E, 0xAE, 0x98, 0x30, 0x0E, 0xB7, 0x8A, 0x7A, 0xE2, 0x5A, 0x89,
66 | 0x9C, 0x9D, 0x57, 0x4D, 0xB0, 0x68, 0x97, 0xB5, 0x73, 0x42, 0x63, 0xA1, 0x38, 0xF7, 0xBE, 0x50,
67 | 0xF3, 0xFF, 0x29, 0xE9, 0x5A, 0x0B, 0x88, 0x94, 0x19, 0x39, 0xD2, 0xEE, 0xEF, 0x82, 0xE0, 0x83,
68 | 0xCA, 0xFB, 0x39, 0xD9, 0xFF, 0x2B, 0x1F, 0xC9, 0x24, 0x3F, 0xAB, 0xAE, 0xA7, 0x59, 0x92, 0x58,
69 | 0x78, 0xB3, 0xB1, 0x52, 0x28, 0xF1, 0x50, 0x4A, 0x49, 0x53, 0x95, 0xDF, 0x0F, 0x2A, 0xF4, 0xAF,
70 | 0x00, 0x89, 0x6D, 0xA7, 0xEA, 0xA8, 0x97, 0x98, 0x05, 0x35, 0x01, 0xAF, 0xB4, 0x33, 0xF6, 0xCF,
71 | 0xC7, 0x7F, 0x18, 0xC3, 0x27, 0x3F, 0xC0, 0x36, 0x15, 0xE2, 0x29, 0x31, 0x99, 0x12, 0x44, 0x06
72 | };
73 | }
74 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Tank/CMF/ProCMF_38125.cs:
--------------------------------------------------------------------------------
1 | using static TACTLib.Core.Product.Tank.ManifestCryptoHandler;
2 | using static TACTLib.Core.Product.Tank.ContentManifestFile;
3 |
4 | namespace TACTLib.Core.Product.Tank.CMF
5 | {
6 | [ManifestCrypto(AutoDetectVersion = true, Product = TACTProduct.Overwatch)]
7 | public class ProCMF_38125 : ICMFEncryptionProc
8 | {
9 | public byte[] Key(CMFHeader header, int length)
10 | {
11 | byte[] buffer = new byte[length];
12 |
13 | uint kidx = Keytable[header.m_buildVersion & 511];
14 | for (int i = 0; i != length; ++i)
15 | {
16 | buffer[i] = Keytable[kidx % 512];
17 | kidx -= header.m_buildVersion;
18 | }
19 |
20 | return buffer;
21 | }
22 |
23 | public byte[] IV(CMFHeader header, byte[] digest, int length)
24 | {
25 | byte[] buffer = new byte[length];
26 |
27 | uint kidx = Keytable[header.m_buildVersion & 511];
28 | for (int i = 0; i != length; ++i)
29 | {
30 | buffer[i] = Keytable[kidx % 512];
31 | kidx -= 43;
32 | buffer[i] ^= (byte)(digest[(kidx + header.m_dataCount) % SHA1_DIGESTSIZE] % 0xFF);
33 | }
34 |
35 | return buffer;
36 | }
37 |
38 | private static readonly byte[] Keytable =
39 | {
40 | 0x38, 0xBD, 0xA8, 0x9E, 0x49, 0xC2, 0xBD, 0x0F, 0x21, 0x9E, 0x44, 0x7A, 0x8E, 0xD2, 0x22, 0x4E,
41 | 0x36, 0x75, 0xEC, 0x49, 0x91, 0xEB, 0x73, 0x17, 0x3C, 0x02, 0x1D, 0x14, 0x9B, 0x18, 0xDF, 0x23,
42 | 0xD5, 0x70, 0x41, 0x12, 0x0C, 0x98, 0x9B, 0x3E, 0x63, 0x5B, 0x67, 0x26, 0x5F, 0x4C, 0x95, 0x25,
43 | 0x46, 0x24, 0x07, 0x48, 0x4E, 0xEF, 0x82, 0x6D, 0xBA, 0xDA, 0xCD, 0x0A, 0xEA, 0xF0, 0x43, 0x60,
44 | 0x24, 0xEA, 0xDD, 0x39, 0x86, 0x0E, 0x2C, 0xB2, 0xE1, 0xB3, 0x59, 0xA2, 0xB6, 0x4B, 0xC6, 0x9F,
45 | 0xB6, 0x18, 0xF2, 0xFC, 0xEE, 0xA2, 0x42, 0x72, 0x4C, 0x82, 0xCA, 0xC2, 0x6D, 0x55, 0xEF, 0x05,
46 | 0x2D, 0x0C, 0x6B, 0x66, 0x51, 0xFC, 0xB3, 0xC9, 0x92, 0xB3, 0x19, 0xDE, 0xAA, 0x4E, 0x3D, 0x26,
47 | 0xE3, 0x1A, 0x2C, 0xA1, 0xA0, 0x7B, 0x5F, 0xF4, 0xF5, 0xF8, 0x43, 0x61, 0x10, 0x31, 0xDE, 0x45,
48 | 0xEE, 0xA4, 0xA9, 0x92, 0x9E, 0x89, 0x66, 0x1C, 0x4F, 0x52, 0x03, 0x21, 0xA5, 0x81, 0x7C, 0x66,
49 | 0x4A, 0x28, 0xF4, 0x22, 0x2C, 0xC6, 0xBC, 0x0C, 0xFF, 0x26, 0x15, 0x19, 0xBC, 0x88, 0xC9, 0x60,
50 | 0x52, 0x71, 0x89, 0x1B, 0xFD, 0xB0, 0x59, 0x37, 0x7B, 0xE7, 0xD5, 0xCD, 0x78, 0x4B, 0x76, 0xF7,
51 | 0x56, 0x81, 0x64, 0x22, 0x64, 0xA7, 0xFA, 0x18, 0x13, 0x50, 0x54, 0x03, 0x71, 0xD2, 0x58, 0xE4,
52 | 0x7E, 0xA6, 0x86, 0x44, 0x5E, 0x94, 0x6D, 0x86, 0xD2, 0xCC, 0x10, 0xC8, 0xE3, 0xF4, 0x23, 0xA5,
53 | 0x53, 0x26, 0x35, 0x68, 0xD4, 0x67, 0x05, 0xC1, 0x69, 0x4D, 0x68, 0x8F, 0x6B, 0x8D, 0x7F, 0x23,
54 | 0xB8, 0x3A, 0x85, 0x8A, 0x19, 0x36, 0x17, 0xCE, 0xC8, 0xBA, 0x1D, 0x84, 0x2F, 0xBD, 0x4E, 0xCF,
55 | 0x10, 0x3B, 0x16, 0x35, 0x13, 0xD2, 0x31, 0x15, 0x0E, 0x68, 0x0C, 0x27, 0xE5, 0x68, 0x43, 0x1A,
56 | 0x6A, 0x45, 0x43, 0x08, 0x63, 0xF8, 0x6F, 0xB7, 0x7D, 0x56, 0xD0, 0x48, 0x87, 0xAF, 0xE8, 0xDE,
57 | 0xAE, 0x57, 0x86, 0x87, 0x66, 0x2C, 0xC2, 0xD2, 0xBA, 0xFB, 0x47, 0x99, 0x64, 0xD6, 0x8A, 0x9D,
58 | 0xFD, 0x59, 0x5C, 0x5D, 0x9A, 0xC7, 0xB8, 0xB4, 0xB2, 0x5D, 0x16, 0x39, 0x02, 0x6B, 0x58, 0x1E,
59 | 0x7C, 0x35, 0xC9, 0x2A, 0xBB, 0xF0, 0xCE, 0x1D, 0x03, 0x15, 0x16, 0xE4, 0x76, 0x8E, 0x1F, 0xE9,
60 | 0xC3, 0x87, 0x5D, 0xC0, 0x3A, 0x4F, 0x71, 0x24, 0xF6, 0xA3, 0xBA, 0xA6, 0x11, 0xDC, 0x2E, 0x84,
61 | 0x52, 0xFF, 0x1A, 0x5F, 0x22, 0x22, 0x79, 0x0A, 0x71, 0xD1, 0x75, 0xD5, 0x3D, 0xA1, 0x5C, 0x53,
62 | 0x76, 0x6D, 0x5F, 0x32, 0xCC, 0xB6, 0x01, 0x1F, 0xD2, 0x54, 0x9F, 0xB3, 0xB6, 0x9D, 0x1F, 0x1D,
63 | 0x28, 0xBD, 0x10, 0xE1, 0x4C, 0x56, 0x4F, 0x12, 0xFB, 0x1A, 0xA5, 0x5C, 0xAA, 0x04, 0x84, 0x3C,
64 | 0xB1, 0x56, 0x5C, 0xD7, 0xB4, 0xDF, 0x9C, 0xEC, 0xD1, 0x22, 0x58, 0x34, 0x88, 0x03, 0x44, 0x61,
65 | 0x21, 0x6A, 0xD2, 0xB5, 0xDE, 0xBD, 0x73, 0x3C, 0xBB, 0xC6, 0x60, 0x33, 0x94, 0x71, 0x86, 0x37,
66 | 0xC2, 0xF5, 0x50, 0x33, 0xE0, 0x5C, 0xBD, 0x4D, 0xE5, 0xA1, 0xF0, 0x09, 0xF0, 0xEC, 0x34, 0x1A,
67 | 0xAB, 0xA2, 0x2A, 0x08, 0xF4, 0xA8, 0x66, 0xAD, 0x07, 0xB4, 0x59, 0x00, 0xA4, 0x2A, 0x4A, 0x02,
68 | 0x15, 0x47, 0x96, 0x9C, 0xFE, 0x21, 0xA1, 0xB2, 0xD8, 0xE1, 0x93, 0x39, 0xD1, 0x7C, 0x65, 0x7A,
69 | 0xB1, 0x46, 0x20, 0x25, 0xCB, 0xD8, 0xEC, 0xF1, 0x2D, 0x3C, 0x02, 0x43, 0x0D, 0xC8, 0xF0, 0xA0,
70 | 0xEF, 0xDA, 0x81, 0x50, 0xAD, 0x56, 0x7B, 0x5C, 0x00, 0x13, 0x2A, 0xE5, 0x72, 0xBC, 0x6A, 0xDE,
71 | 0x31, 0xB9, 0x06, 0xE5, 0x44, 0x87, 0xE9, 0x14, 0xD6, 0xD2, 0xA1, 0x7A, 0x80, 0x32, 0xC7, 0x19
72 | };
73 | }
74 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Tank/CMF/ProCMF_41713.cs:
--------------------------------------------------------------------------------
1 | using static TACTLib.Core.Product.Tank.ManifestCryptoHandler;
2 | using static TACTLib.Core.Product.Tank.ContentManifestFile;
3 |
4 | namespace TACTLib.Core.Product.Tank.CMF
5 | {
6 | [ManifestCrypto(AutoDetectVersion = true, Product = TACTProduct.Overwatch)]
7 | public class ProCMF_41713 : ICMFEncryptionProc
8 | {
9 | public byte[] Key(CMFHeader header, int length)
10 | {
11 | byte[] buffer = new byte[length];
12 |
13 | uint kidx = (uint)length * header.m_buildVersion;
14 | for (int i = 0; i != length; ++i)
15 | {
16 | buffer[i] = Keytable[kidx % 512];
17 | kidx += 3;
18 | }
19 |
20 | return buffer;
21 | }
22 |
23 | public byte[] IV(CMFHeader header, byte[] digest, int length)
24 | {
25 | byte[] buffer = new byte[length];
26 |
27 | uint kidx = (uint)length * header.m_buildVersion;
28 | uint increment = (uint)header.m_entryCount + digest[header.m_entryCount % SHA1_DIGESTSIZE];
29 | for (int i = 0; i != length; ++i)
30 | {
31 | kidx += increment;
32 | buffer[i] = digest[kidx % SHA1_DIGESTSIZE];
33 | }
34 |
35 | return buffer;
36 | }
37 |
38 | private static readonly byte[] Keytable =
39 | {
40 | 0x32, 0x76, 0x49, 0x28, 0xFD, 0x60, 0x40, 0xC4, 0x1A, 0x70, 0x33, 0x87, 0xA6, 0x8A, 0x70, 0xB5,
41 | 0x64, 0xE5, 0xC2, 0xC7, 0xA0, 0x15, 0xFE, 0x9D, 0x87, 0xCA, 0x61, 0x6D, 0xC2, 0x12, 0x29, 0x6D,
42 | 0xCC, 0xB1, 0x5B, 0xCA, 0x43, 0x57, 0x55, 0xF5, 0x15, 0xBF, 0x5F, 0x99, 0x3A, 0xD3, 0x67, 0xF9,
43 | 0xD1, 0xD1, 0x94, 0x63, 0xA3, 0xDB, 0xEA, 0xE8, 0x40, 0xCD, 0xE0, 0x1C, 0x4D, 0xBC, 0x55, 0xE1,
44 | 0x60, 0xA5, 0x09, 0x73, 0x98, 0xAF, 0x85, 0xEE, 0xA7, 0x2D, 0x67, 0xF1, 0xDC, 0x25, 0x30, 0x1C,
45 | 0x86, 0xEF, 0x16, 0x01, 0x38, 0x4E, 0xCA, 0x64, 0xE3, 0x2E, 0xF8, 0xF9, 0xA8, 0x48, 0xD4, 0x2F,
46 | 0x9C, 0x95, 0xC3, 0x23, 0x0D, 0x84, 0x87, 0xE8, 0x05, 0xFB, 0xE5, 0xA7, 0xBF, 0xA9, 0x87, 0xE4,
47 | 0xA9, 0x11, 0x56, 0x74, 0x30, 0x8A, 0xDF, 0x7F, 0xA1, 0x02, 0xD4, 0xCA, 0xF3, 0xCF, 0x48, 0x23,
48 | 0x5A, 0x5E, 0xC2, 0x59, 0x3F, 0xA7, 0x2D, 0x5D, 0xF2, 0xBF, 0x7A, 0x96, 0xC8, 0x7F, 0x1D, 0x00,
49 | 0xF7, 0x36, 0x8E, 0x15, 0x78, 0x17, 0x52, 0xBA, 0xB9, 0x7F, 0xA2, 0x24, 0xE5, 0x71, 0xBD, 0x85,
50 | 0x65, 0x12, 0x9C, 0xB9, 0x97, 0xDA, 0x29, 0x26, 0x06, 0x0F, 0x5C, 0xB7, 0xAC, 0x5F, 0x11, 0x42,
51 | 0xEF, 0xB2, 0xF0, 0xB2, 0x61, 0xFC, 0xAA, 0xFD, 0x2A, 0x69, 0x2D, 0x58, 0x3B, 0x25, 0x50, 0x84,
52 | 0x97, 0xD8, 0xAC, 0x35, 0x52, 0x67, 0x68, 0x8A, 0xAC, 0xAF, 0x8D, 0x28, 0x69, 0x74, 0x5C, 0xD5,
53 | 0x5A, 0xB5, 0x4B, 0xB8, 0xB9, 0x27, 0xE5, 0x81, 0xD9, 0xF2, 0xCE, 0x62, 0x00, 0x8A, 0xE8, 0x58,
54 | 0x02, 0xCD, 0x83, 0x07, 0x07, 0xFF, 0x78, 0x5C, 0x8C, 0x49, 0xE6, 0x12, 0x93, 0xBD, 0xB8, 0xE3,
55 | 0x1F, 0xF4, 0xF6, 0x2E, 0xCD, 0x5E, 0x7E, 0x69, 0xF2, 0x0B, 0xF9, 0xE5, 0x7D, 0x17, 0x0E, 0xFD,
56 | 0x3A, 0x33, 0x69, 0x25, 0x00, 0x74, 0x91, 0xAA, 0x40, 0x71, 0x96, 0xB4, 0x82, 0x66, 0xD2, 0x77,
57 | 0xE9, 0x3E, 0x34, 0x6A, 0xC2, 0x5A, 0x73, 0xE9, 0x6C, 0x5D, 0xC1, 0x2C, 0x06, 0xEB, 0xBF, 0x64,
58 | 0x15, 0xC1, 0x64, 0xA6, 0x59, 0x4A, 0x5E, 0x8A, 0xDA, 0x46, 0xFB, 0x9F, 0x09, 0xF7, 0xF1, 0x1F,
59 | 0x41, 0xE6, 0x98, 0x50, 0x62, 0xC9, 0xE5, 0x2A, 0x09, 0x44, 0x47, 0xDE, 0x82, 0x80, 0x90, 0x8A,
60 | 0x51, 0x3A, 0xCE, 0xAE, 0x0A, 0x62, 0x47, 0x77, 0xE8, 0xB1, 0xE1, 0x34, 0x60, 0xDD, 0xDF, 0x7B,
61 | 0x5E, 0x60, 0x25, 0xC0, 0x47, 0x97, 0x74, 0x12, 0x07, 0x66, 0xC4, 0x8F, 0x39, 0x00, 0x5E, 0x3E,
62 | 0x8E, 0xB6, 0x66, 0x1B, 0x4A, 0x84, 0x33, 0xF1, 0x0A, 0xD0, 0xB6, 0x03, 0x65, 0x0F, 0xA7, 0xB8,
63 | 0x98, 0xDB, 0xF3, 0xC1, 0x96, 0xF5, 0xA0, 0xD6, 0x59, 0x03, 0x4B, 0x57, 0xD9, 0x60, 0xB0, 0xB6,
64 | 0xCE, 0xE5, 0x0A, 0xAF, 0xBF, 0x94, 0x19, 0x17, 0xF4, 0xAA, 0xB3, 0x56, 0xE2, 0xA5, 0x66, 0xD2,
65 | 0xB4, 0x8A, 0x27, 0x82, 0x14, 0xD9, 0xDA, 0xCC, 0x6E, 0x26, 0xB8, 0x8B, 0x95, 0x73, 0x19, 0x0A,
66 | 0x33, 0x1C, 0xD6, 0x86, 0x66, 0xF3, 0xE4, 0x27, 0x76, 0xC8, 0x27, 0xF7, 0x9D, 0x55, 0x52, 0x80,
67 | 0x1D, 0x14, 0x23, 0x2F, 0x72, 0xF4, 0xF8, 0x56, 0x05, 0xAA, 0x17, 0x2B, 0xF4, 0x21, 0xC5, 0x96,
68 | 0xEE, 0x7D, 0x74, 0x7E, 0x2F, 0xA8, 0xC0, 0x0D, 0x32, 0x57, 0x66, 0xA4, 0x0A, 0x4F, 0xB0, 0x4C,
69 | 0x4A, 0x8F, 0x51, 0x79, 0x58, 0x9D, 0xE0, 0x68, 0x92, 0x80, 0x9E, 0x16, 0x06, 0x4A, 0xA3, 0xA2,
70 | 0xBE, 0xD0, 0xEA, 0x32, 0x47, 0xBA, 0x93, 0x79, 0xDA, 0x29, 0xCE, 0x05, 0x81, 0xDC, 0xEE, 0x54,
71 | 0x39, 0x34, 0x7A, 0x4C, 0x0E, 0x68, 0xD3, 0xC9, 0x23, 0xDF, 0x38, 0x52, 0xC8, 0xA4, 0x4F, 0xE4
72 | };
73 | }
74 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Tank/CMF/ProCMF_42210.cs:
--------------------------------------------------------------------------------
1 | using static TACTLib.Core.Product.Tank.ManifestCryptoHandler;
2 | using static TACTLib.Core.Product.Tank.ContentManifestFile;
3 |
4 | namespace TACTLib.Core.Product.Tank.CMF
5 | {
6 | [ManifestCrypto(AutoDetectVersion = true, Product = TACTProduct.Overwatch)]
7 | public class ProCMF_42210 : ICMFEncryptionProc
8 | {
9 | public byte[] Key(CMFHeader header, int length)
10 | {
11 | byte[] buffer = new byte[length];
12 |
13 | uint kidx = Keytable[header.m_buildVersion & 511];
14 | for (int i = 0; i != length; ++i)
15 | {
16 | buffer[i] = Keytable[SignedMod(kidx, 512)];
17 | kidx += 3;
18 | }
19 |
20 | return buffer;
21 | }
22 |
23 | public byte[] IV(CMFHeader header, byte[] digest, int length)
24 | {
25 | byte[] buffer = new byte[length];
26 |
27 | uint kidx = (uint)length * header.m_buildVersion;
28 | for (int i = 0; i != length; ++i)
29 | {
30 | buffer[i] = Keytable[SignedMod(kidx, 512)];
31 | kidx += header.m_buildVersion - kidx;
32 | buffer[i] ^= digest[SignedMod(i + kidx, SHA1_DIGESTSIZE)];
33 | }
34 |
35 | return buffer;
36 | }
37 |
38 | private static readonly byte[] Keytable =
39 | {
40 | 0x99, 0x9D, 0x00, 0x78, 0xBA, 0x76, 0xED, 0xF1, 0xBB, 0xCA, 0x05, 0x72, 0xA5, 0x29, 0x7D, 0x87,
41 | 0x11, 0x80, 0xD4, 0x52, 0x4A, 0x4D, 0x7F, 0x83, 0x9C, 0x05, 0xBB, 0x84, 0xCF, 0xC4, 0xC9, 0x98,
42 | 0x25, 0x12, 0x08, 0x5B, 0x72, 0x47, 0xC9, 0x37, 0xBC, 0x5D, 0x76, 0x94, 0xB4, 0x10, 0x89, 0x69,
43 | 0xB9, 0xFB, 0x81, 0xCE, 0xC3, 0xFD, 0x7A, 0xAD, 0xDB, 0x19, 0x3C, 0x75, 0x8B, 0x76, 0xEA, 0x34,
44 | 0x8F, 0x6A, 0x31, 0xB0, 0x2C, 0x10, 0xAD, 0x13, 0x7F, 0xFA, 0xCB, 0x79, 0x4C, 0xC9, 0x64, 0x3E,
45 | 0x40, 0xD5, 0x26, 0xD3, 0x88, 0xCE, 0x7A, 0x35, 0xB5, 0x78, 0xE9, 0x38, 0xC2, 0x66, 0xF7, 0x0D,
46 | 0x99, 0x39, 0xB7, 0x6E, 0xCC, 0x4D, 0xA3, 0xA8, 0xB6, 0x72, 0xC2, 0x6F, 0x51, 0xBD, 0x7F, 0xC6,
47 | 0x70, 0x5C, 0xB0, 0xE8, 0xE4, 0xA3, 0x65, 0xF2, 0x7F, 0x51, 0x3D, 0x0D, 0x3A, 0x66, 0xFD, 0x5F,
48 | 0xC8, 0x13, 0x5A, 0x1A, 0xE8, 0x53, 0xA2, 0x4B, 0x6C, 0x27, 0x2A, 0xE7, 0xAB, 0x31, 0x52, 0x14,
49 | 0x6A, 0x04, 0xC5, 0xDA, 0x4D, 0xFB, 0x40, 0xD4, 0xD1, 0x9D, 0xF0, 0x70, 0x30, 0x54, 0x85, 0x59,
50 | 0xC5, 0x79, 0xE1, 0x2C, 0x51, 0x35, 0x43, 0xBE, 0x41, 0x5F, 0x42, 0x21, 0x3F, 0xFB, 0x1C, 0x30,
51 | 0x96, 0x1A, 0xB7, 0x01, 0x3A, 0x0B, 0x46, 0x05, 0x82, 0xD9, 0xF3, 0xC0, 0x6F, 0xDD, 0x77, 0x86,
52 | 0x38, 0xCC, 0x34, 0xA9, 0x45, 0x17, 0x01, 0x17, 0x9E, 0xA0, 0x1A, 0x9E, 0x6E, 0x1A, 0x70, 0xEE,
53 | 0x47, 0xBF, 0x56, 0x3A, 0xAA, 0x01, 0xEB, 0x67, 0x75, 0x75, 0x39, 0x7B, 0x22, 0x30, 0x40, 0x3A,
54 | 0xBC, 0xAA, 0x4B, 0xA9, 0x1D, 0xAB, 0xD1, 0x41, 0xEA, 0x46, 0x0C, 0x52, 0xEF, 0xD9, 0x5F, 0xFF,
55 | 0x8A, 0x53, 0x5C, 0xAE, 0x1D, 0xCE, 0x61, 0x76, 0x10, 0xBA, 0x87, 0x07, 0xCE, 0x6C, 0x19, 0x04,
56 | 0x3D, 0x85, 0x77, 0xF0, 0xDE, 0x60, 0x7F, 0xF7, 0x06, 0x2E, 0x91, 0xEB, 0x05, 0x59, 0x1C, 0x13,
57 | 0xD0, 0xFB, 0xD2, 0x5A, 0xA7, 0x60, 0x39, 0x12, 0xF0, 0x51, 0xD7, 0x4C, 0x6D, 0x4E, 0x01, 0xB7,
58 | 0x5C, 0x3F, 0xFA, 0x10, 0x69, 0x93, 0xEF, 0x05, 0xCA, 0x76, 0x61, 0x8A, 0x4B, 0x31, 0x18, 0x8F,
59 | 0x6C, 0xF8, 0x02, 0x3A, 0x56, 0x5C, 0x72, 0xC1, 0x9C, 0x02, 0x29, 0xB6, 0xE9, 0x57, 0x3F, 0xC1,
60 | 0xAB, 0x3F, 0x7C, 0xEC, 0xF4, 0xCD, 0x99, 0x01, 0x13, 0xCE, 0xD9, 0xE9, 0xEB, 0xDC, 0x9C, 0xE4,
61 | 0x3B, 0x44, 0x72, 0xC2, 0x81, 0xB2, 0x10, 0xF5, 0xC3, 0x81, 0x7A, 0xF5, 0xF3, 0x3D, 0x03, 0x4E,
62 | 0xD4, 0xAE, 0xD8, 0x41, 0xB7, 0xF7, 0x2B, 0x54, 0x51, 0x80, 0xD3, 0xD1, 0x19, 0x24, 0x6D, 0x0B,
63 | 0xB1, 0x77, 0xBB, 0x1F, 0xBA, 0x23, 0x50, 0xE7, 0x18, 0x05, 0x6E, 0xC8, 0xBE, 0x0B, 0x09, 0x32,
64 | 0x1C, 0xD4, 0xC0, 0x75, 0xB2, 0x45, 0x85, 0x3B, 0x3B, 0x97, 0x7B, 0x4A, 0x73, 0x3E, 0xAD, 0x90,
65 | 0x9B, 0x27, 0x2C, 0x3A, 0xC9, 0xFA, 0x91, 0x45, 0x27, 0x20, 0x69, 0x44, 0x13, 0x06, 0x70, 0x12,
66 | 0x5E, 0x0A, 0x72, 0xBC, 0x5C, 0x88, 0xF0, 0x9F, 0xF3, 0xCB, 0x8E, 0x54, 0xF2, 0x82, 0x5A, 0x85,
67 | 0xC6, 0xAB, 0xF6, 0xED, 0x14, 0xAA, 0xFE, 0xA6, 0xD1, 0x01, 0xEA, 0xA9, 0x4F, 0xE4, 0x55, 0xBB,
68 | 0xDC, 0xDB, 0xC5, 0x5E, 0x81, 0xF2, 0xC7, 0xDE, 0x3E, 0x66, 0x0F, 0x70, 0xD2, 0xF2, 0x98, 0xCB,
69 | 0x0D, 0x1E, 0x42, 0x85, 0xF3, 0x1C, 0x52, 0x8D, 0x59, 0x83, 0xC0, 0x48, 0xFC, 0x31, 0xE3, 0xBB,
70 | 0x44, 0x63, 0x0E, 0x66, 0xB8, 0x86, 0x9C, 0x81, 0x74, 0x05, 0xEB, 0x70, 0x59, 0x81, 0x04, 0x4F,
71 | 0xEF, 0x12, 0x0B, 0x1B, 0xD7, 0xE9, 0x63, 0xBC, 0x3C, 0x52, 0xB0, 0x7A, 0xED, 0xB3, 0xFE, 0xD6
72 | };
73 | }
74 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Tank/CMF/ProCMF_63778.cs:
--------------------------------------------------------------------------------
1 | using static TACTLib.Core.Product.Tank.ManifestCryptoHandler;
2 | using static TACTLib.Core.Product.Tank.ContentManifestFile;
3 |
4 | namespace TACTLib.Core.Product.Tank.CMF
5 | {
6 | [ManifestCrypto(AutoDetectVersion = true, Product = TACTProduct.Overwatch)]
7 | public class ProCMF_63778 : ICMFEncryptionProc
8 | {
9 | public byte[] Key(CMFHeader header, int length)
10 | {
11 | byte[] buffer = new byte[length];
12 | uint kidx = header.m_buildVersion * (uint)length;
13 | for (uint i = 0; i != length; ++i)
14 | {
15 | buffer[i] = Keytable[SignedMod(kidx, 512)];
16 | kidx += (uint)header.m_entryCount;
17 | }
18 |
19 | return buffer;
20 | }
21 |
22 | public byte[] IV(CMFHeader header, byte[] digest, int length)
23 | {
24 | byte[] buffer = new byte[length];
25 | uint kidx = Keytable[SignedMod((2 * digest[13]) - length, 512)];
26 | for (int i = 0; i != length; ++i)
27 | {
28 | buffer[i] = Keytable[SignedMod(kidx, 512)];
29 | kidx += 3;
30 | buffer[i] ^= digest[SignedMod(kidx - i, SHA1_DIGESTSIZE)];
31 | }
32 |
33 | return buffer;
34 | }
35 |
36 | private static readonly byte[] Keytable =
37 | {
38 | 0xBD, 0x5E, 0x95, 0x73, 0x20, 0x4F, 0x5B, 0xB9, 0xC4, 0xDC, 0x48, 0xCC, 0x51, 0xC2, 0x92, 0x09,
39 | 0xA4, 0xB5, 0x7A, 0x06, 0x89, 0x97, 0xA2, 0x46, 0x40, 0xFE, 0x97, 0x23, 0x10, 0xAE, 0x46, 0xDE,
40 | 0x98, 0x1A, 0x11, 0x88, 0xEE, 0x30, 0xA0, 0x0A, 0x90, 0xCC, 0x99, 0xCA, 0x1C, 0xD9, 0xC6, 0xF5,
41 | 0x1F, 0xA0, 0x01, 0xDB, 0x3A, 0xBF, 0x88, 0x48, 0x66, 0xB5, 0x89, 0x5A, 0x2F, 0x6F, 0xBB, 0xBF,
42 | 0x0E, 0x8E, 0x07, 0x9E, 0x4C, 0xD1, 0x0E, 0x8A, 0x43, 0x0D, 0x80, 0x26, 0x82, 0xA8, 0x56, 0xD6,
43 | 0x76, 0x67, 0x62, 0xA0, 0x0B, 0xBF, 0xC3, 0x81, 0x1A, 0x85, 0xB5, 0xAA, 0x0B, 0x3F, 0x45, 0x79,
44 | 0x97, 0xB0, 0x10, 0x5C, 0xF6, 0x33, 0x20, 0x08, 0xE9, 0x46, 0x54, 0xA6, 0x6A, 0x05, 0x6C, 0x00,
45 | 0x2A, 0x34, 0x60, 0xB3, 0xCC, 0x19, 0xC7, 0xD2, 0x89, 0x87, 0x5C, 0x87, 0xAF, 0x72, 0xC2, 0x7A,
46 | 0x06, 0x84, 0xEB, 0x45, 0x8A, 0x4C, 0x4E, 0x79, 0x2E, 0xE2, 0xD2, 0x40, 0x06, 0x3E, 0x71, 0x79,
47 | 0xC9, 0xEC, 0x50, 0xFD, 0x68, 0x2C, 0xC3, 0xFC, 0x9E, 0xD1, 0xA9, 0x27, 0xD4, 0x22, 0x9B, 0x67,
48 | 0xD9, 0xDF, 0xB3, 0x40, 0x0A, 0xA9, 0xDD, 0xA1, 0xEC, 0x35, 0x35, 0xBC, 0xA9, 0x7D, 0x17, 0x9B,
49 | 0x5D, 0x29, 0xB6, 0x2D, 0xB4, 0x63, 0xE4, 0xF9, 0x54, 0x24, 0x75, 0x4A, 0xB1, 0xA2, 0xB5, 0x0D,
50 | 0xC9, 0xD1, 0x99, 0xD7, 0x05, 0xC7, 0x37, 0xD1, 0x6A, 0x89, 0x07, 0x1E, 0xA3, 0x24, 0x8A, 0x95,
51 | 0x7D, 0xE6, 0x07, 0xCD, 0x7E, 0x43, 0xAF, 0x61, 0x22, 0xC2, 0x56, 0x3A, 0x5A, 0x62, 0x6F, 0x61,
52 | 0xDC, 0x28, 0xA4, 0xDD, 0x72, 0xE1, 0xA6, 0x83, 0x4B, 0x70, 0x4F, 0x0F, 0x03, 0xEF, 0x01, 0x51,
53 | 0xBD, 0xF0, 0x07, 0x08, 0x66, 0x8B, 0x38, 0x1F, 0x32, 0xFF, 0xFA, 0x12, 0x63, 0xD0, 0x95, 0x5B,
54 | 0x1D, 0xA4, 0x33, 0x63, 0x92, 0x4C, 0x26, 0x02, 0x36, 0x6A, 0xFE, 0x2A, 0xCD, 0x8D, 0x7E, 0x63,
55 | 0x87, 0x30, 0xFB, 0x54, 0x69, 0xC0, 0xFB, 0xDE, 0x80, 0x0E, 0xF2, 0x34, 0x79, 0x9A, 0x27, 0x18,
56 | 0xFF, 0x3D, 0xCF, 0x25, 0x59, 0xDA, 0xA1, 0x7A, 0xC0, 0x99, 0xA3, 0x9F, 0x70, 0x28, 0xFD, 0x1A,
57 | 0xCC, 0x9D, 0xA7, 0x9B, 0x43, 0x75, 0x22, 0xDA, 0x30, 0x6C, 0x1B, 0x99, 0x57, 0x11, 0xA8, 0x1B,
58 | 0x5E, 0x4F, 0x89, 0x8B, 0x5B, 0xE9, 0x81, 0xC4, 0xEA, 0x0E, 0x9D, 0xFD, 0x71, 0x3D, 0xE9, 0x25,
59 | 0x30, 0xA8, 0x80, 0x36, 0xAD, 0xF8, 0x16, 0x4C, 0x21, 0xEC, 0x4A, 0xE9, 0x57, 0x36, 0x49, 0x23,
60 | 0x82, 0xEF, 0x86, 0xCA, 0x72, 0x3C, 0x15, 0x9E, 0xF0, 0x74, 0x8E, 0x7D, 0x29, 0x23, 0xE6, 0x0B,
61 | 0xD4, 0x24, 0xDE, 0x85, 0xAE, 0x85, 0xC6, 0x1B, 0x5C, 0xFC, 0xC8, 0x2F, 0x64, 0x36, 0xA4, 0x18,
62 | 0xEF, 0xE8, 0x37, 0x36, 0x84, 0x66, 0xDC, 0x9D, 0xDF, 0x1E, 0xA0, 0x06, 0x18, 0x52, 0xC5, 0xB4,
63 | 0x14, 0x9D, 0xFB, 0x64, 0x64, 0xC2, 0x09, 0x47, 0x68, 0x98, 0xD2, 0xBD, 0xE7, 0x04, 0x5C, 0xA3,
64 | 0x40, 0xE1, 0x24, 0xC9, 0x4A, 0x90, 0x7B, 0x18, 0x71, 0x1B, 0xF9, 0xF9, 0x17, 0x16, 0x7F, 0x9C,
65 | 0xCB, 0xBB, 0x33, 0x1A, 0x96, 0xC9, 0x3E, 0xCF, 0xE7, 0xDB, 0xBF, 0x6C, 0x56, 0x51, 0xAE, 0xF8,
66 | 0x9D, 0x71, 0x38, 0xA3, 0x28, 0x11, 0x94, 0x65, 0xD8, 0xAA, 0xBE, 0x93, 0x9E, 0x38, 0x8F, 0x05,
67 | 0xF3, 0xC8, 0x94, 0x3A, 0xF6, 0xDF, 0x47, 0x4B, 0xD1, 0x89, 0xCE, 0x18, 0x6A, 0x10, 0x58, 0xE8,
68 | 0x62, 0x13, 0x95, 0x42, 0x2C, 0x84, 0x0C, 0xB1, 0xBF, 0x8D, 0x31, 0x10, 0xB5, 0x6C, 0x4D, 0xE8,
69 | 0x10, 0x46, 0x9E, 0xF5, 0x9C, 0x61, 0xE3, 0xB5, 0xD7, 0xAE, 0xAD, 0x1A, 0xF1, 0xC3, 0x81, 0x33
70 | };
71 | }
72 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Tank/CMF/ProCMF_67664.cs:
--------------------------------------------------------------------------------
1 | using static TACTLib.Core.Product.Tank.ManifestCryptoHandler;
2 | using static TACTLib.Core.Product.Tank.ContentManifestFile;
3 |
4 | namespace TACTLib.Core.Product.Tank.CMF
5 | {
6 | [ManifestCrypto(AutoDetectVersion = true, Product = TACTProduct.Overwatch)]
7 | public class ProCMF_67664 : ICMFEncryptionProc
8 | {
9 | public byte[] Key(CMFHeader header, int length)
10 | {
11 | byte[] buffer = new byte[length];
12 | uint kidx = Keytable[length + 256];
13 | for (uint i = 0; i != length; ++i)
14 | {
15 | buffer[i] = Keytable[SignedMod(kidx, 512)];
16 | kidx -= header.m_buildVersion & 511;
17 | }
18 |
19 | return buffer;
20 | }
21 |
22 | public byte[] IV(CMFHeader header, byte[] digest, int length)
23 | {
24 | byte[] buffer = new byte[length];
25 | uint kidx = Keytable[(uint)header.m_dataCount & 511];
26 | for (int i = 0; i != length; ++i)
27 | {
28 | buffer[i] = Keytable[SignedMod(kidx, 512)];
29 | kidx = header.m_buildVersion - kidx;
30 | buffer[i] ^= digest[SignedMod(kidx + i, SHA1_DIGESTSIZE)];
31 | }
32 |
33 | return buffer;
34 | }
35 |
36 | private static readonly byte[] Keytable =
37 | {
38 | 0x55, 0x90, 0xF6, 0x22, 0xA1, 0xA9, 0xAA, 0xFF, 0xC0, 0xE2, 0x73, 0x23, 0xFF, 0x29, 0xA7, 0x02,
39 | 0x0B, 0xB5, 0xC1, 0x93, 0x20, 0x2C, 0x27, 0x04, 0xF8, 0x6D, 0x2B, 0x58, 0x66, 0x86, 0x4D, 0x2B,
40 | 0x90, 0xBC, 0x60, 0x4B, 0xA7, 0x19, 0xE3, 0x28, 0x73, 0x7C, 0xC9, 0xCB, 0xCD, 0xF0, 0xF0, 0x1A,
41 | 0x3B, 0x96, 0x31, 0xB3, 0x2C, 0x98, 0x57, 0xCB, 0x1D, 0xD2, 0x48, 0x0A, 0x35, 0xE7, 0x3E, 0xA2,
42 | 0x3B, 0x0C, 0x09, 0x73, 0x0F, 0xEE, 0x74, 0x18, 0xD1, 0x48, 0xD1, 0x76, 0x48, 0x01, 0x19, 0xA2,
43 | 0x50, 0x14, 0x1E, 0xF9, 0xF9, 0xF2, 0xE1, 0x9D, 0xFE, 0xAA, 0xB9, 0x14, 0xD5, 0xCB, 0x4C, 0x05,
44 | 0xE8, 0x0F, 0x8A, 0x48, 0x61, 0x6F, 0xD8, 0x0C, 0xAD, 0x53, 0xF7, 0xD3, 0x09, 0xE3, 0x5C, 0xDF,
45 | 0x16, 0x38, 0x75, 0x26, 0x09, 0x73, 0x9F, 0xE3, 0x34, 0x5C, 0x3C, 0x08, 0xDE, 0x45, 0x72, 0x52,
46 | 0x9D, 0x30, 0xF4, 0x59, 0x50, 0xBE, 0xEA, 0x25, 0x58, 0x00, 0xC5, 0x4B, 0xD6, 0x57, 0x47, 0xAE,
47 | 0x47, 0x8B, 0xC4, 0x0A, 0xC1, 0x51, 0x52, 0xE8, 0x32, 0xFD, 0x0D, 0x3E, 0xF0, 0x81, 0xDA, 0x70,
48 | 0x13, 0xA3, 0x2A, 0x57, 0x46, 0xA5, 0x98, 0xEF, 0xE9, 0x68, 0xBD, 0xBA, 0x4C, 0x98, 0x48, 0x99,
49 | 0x99, 0x6A, 0xA2, 0xA6, 0x69, 0x9E, 0x3E, 0xF8, 0x67, 0x5E, 0x64, 0x12, 0x15, 0xA5, 0xFE, 0xCC,
50 | 0x40, 0x04, 0x62, 0x0E, 0xCD, 0xDE, 0x2A, 0xA8, 0xFA, 0x5C, 0x97, 0xD0, 0xA4, 0x68, 0xC8, 0x91,
51 | 0xA3, 0x13, 0xA3, 0xAE, 0x4F, 0xE3, 0xE9, 0x4B, 0x9B, 0xF0, 0x60, 0x3D, 0x5A, 0xC6, 0xDE, 0xC5,
52 | 0xA2, 0xFF, 0x08, 0xAE, 0xC6, 0xAB, 0x60, 0xC9, 0x0C, 0xDA, 0x2D, 0x05, 0x5F, 0x71, 0x9D, 0x22,
53 | 0xB3, 0x84, 0x51, 0xD2, 0x0D, 0x66, 0x9D, 0xAB, 0x97, 0xB5, 0xEF, 0x28, 0x06, 0x43, 0x6A, 0x7B,
54 | 0x30, 0x52, 0x22, 0x78, 0x9E, 0x60, 0xFA, 0xB9, 0xDD, 0xD1, 0xB3, 0x20, 0x3C, 0xD8, 0xEA, 0xC3,
55 | 0xAB, 0xAB, 0x83, 0x0C, 0x8A, 0xDD, 0xBD, 0x7C, 0xD5, 0x91, 0x55, 0xDE, 0x9F, 0x23, 0x3F, 0x82,
56 | 0xE0, 0x08, 0xC9, 0x72, 0x99, 0x54, 0x11, 0xB4, 0x1F, 0x55, 0x35, 0x8F, 0xF0, 0x7C, 0x4B, 0x4F,
57 | 0xE8, 0x54, 0x4C, 0x3D, 0xC5, 0xAC, 0xB6, 0x2A, 0x06, 0x0F, 0xDE, 0x1B, 0x0C, 0xAD, 0x0D, 0x49,
58 | 0x59, 0x43, 0xA5, 0x76, 0x0E, 0x20, 0x37, 0xB3, 0xF1, 0x2C, 0x86, 0x3B, 0x3A, 0xDF, 0x46, 0xFB,
59 | 0xCF, 0x5D, 0x6C, 0xFA, 0x17, 0x41, 0xDA, 0x54, 0x34, 0xD2, 0x9B, 0xDA, 0x04, 0xB9, 0xA0, 0x91,
60 | 0xFB, 0x03, 0x47, 0x22, 0x76, 0xC2, 0xA7, 0x42, 0x2F, 0xC5, 0x86, 0x8C, 0xF4, 0xD8, 0x52, 0x45,
61 | 0xF0, 0x60, 0x72, 0x05, 0x6D, 0x9E, 0x06, 0xC8, 0xF6, 0xD6, 0x10, 0xAD, 0xA9, 0xA0, 0xFF, 0x3C,
62 | 0xDD, 0x42, 0x5F, 0x23, 0x07, 0x9A, 0xE3, 0xE3, 0x3E, 0x1C, 0xD2, 0x29, 0x7E, 0xAC, 0xAD, 0x92,
63 | 0x3F, 0xC5, 0xC8, 0x07, 0x92, 0xB2, 0x32, 0x38, 0x27, 0x73, 0x6C, 0x73, 0xC7, 0x39, 0xCF, 0xE5,
64 | 0xFD, 0x8B, 0x3B, 0xB4, 0x32, 0xD0, 0x41, 0x0D, 0xB1, 0x00, 0x61, 0xC9, 0x1A, 0x4F, 0x03, 0xDC,
65 | 0x59, 0x7C, 0x2E, 0x02, 0x2F, 0x36, 0x52, 0x25, 0x9C, 0x3C, 0x6D, 0x84, 0xE1, 0x91, 0xBF, 0x62,
66 | 0x1F, 0x93, 0xCD, 0x4F, 0x27, 0x5C, 0x53, 0x49, 0xDE, 0xE4, 0xBC, 0x0B, 0xD9, 0x6C, 0x88, 0xA6,
67 | 0x66, 0x35, 0xB5, 0x5B, 0x32, 0xFA, 0x9A, 0xC1, 0x19, 0xCF, 0x5B, 0xA7, 0x6E, 0x5A, 0xA2, 0x5C,
68 | 0x1E, 0xBD, 0x9F, 0x09, 0xED, 0xDE, 0xDE, 0x28, 0xAA, 0x9E, 0x73, 0x26, 0xD2, 0xAE, 0xFA, 0xF4,
69 | 0x87, 0xA8, 0xC8, 0x4D, 0xB2, 0x6F, 0x18, 0xAB, 0xD9, 0x78, 0x4A, 0xDC, 0x99, 0x91, 0x8B, 0xCF
70 | };
71 | }
72 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Tank/CMF/ProCMF_38248.cs:
--------------------------------------------------------------------------------
1 | using static TACTLib.Core.Product.Tank.ManifestCryptoHandler;
2 | using static TACTLib.Core.Product.Tank.ContentManifestFile;
3 |
4 | namespace TACTLib.Core.Product.Tank.CMF
5 | {
6 | [ManifestCrypto(AutoDetectVersion = true, Product = TACTProduct.Overwatch)]
7 | public class ProCMF_38248 : ICMFEncryptionProc
8 | {
9 | public byte[] Key(CMFHeader header, int length)
10 | {
11 | byte[] buffer = new byte[length];
12 |
13 | uint kidx = Constrain(length * header.m_buildVersion);
14 | for (int i = 0; i != length; ++i)
15 | {
16 | buffer[i] = Keytable[kidx % 512];
17 | kidx += 3;
18 | }
19 |
20 | return buffer;
21 | }
22 |
23 | public byte[] IV(CMFHeader header, byte[] digest, int length)
24 | {
25 | byte[] buffer = new byte[length];
26 |
27 | uint kidx = Constrain(length * header.m_buildVersion);
28 | uint increment = (uint)header.m_entryCount + digest[header.m_entryCount % SHA1_DIGESTSIZE];
29 | for (int i = 0; i != length; ++i)
30 | {
31 | kidx += increment;
32 | buffer[i] = digest[kidx % SHA1_DIGESTSIZE];
33 | }
34 |
35 | return buffer;
36 | }
37 |
38 | private static readonly byte[] Keytable =
39 | {
40 | 0xDE, 0xC2, 0xB8, 0x91, 0x1E, 0xFA, 0x66, 0xCB, 0xE9, 0x45, 0x0A, 0xF6, 0x0E, 0x6F, 0x9A, 0xF3,
41 | 0x41, 0xEC, 0xA6, 0xC7, 0x84, 0x05, 0x5B, 0xD3, 0x6B, 0x7B, 0x4C, 0xA4, 0x55, 0x18, 0x26, 0xF3,
42 | 0xBA, 0xA8, 0x41, 0x9A, 0x7E, 0xAA, 0x99, 0xE3, 0xA4, 0x11, 0x31, 0xED, 0x3A, 0xDF, 0x44, 0xCC,
43 | 0x28, 0x3C, 0xB7, 0xFE, 0x95, 0x4F, 0xCD, 0xA9, 0x66, 0x7E, 0x58, 0x11, 0x99, 0xC8, 0x25, 0x93,
44 | 0x3E, 0xD0, 0xCF, 0x9B, 0xEA, 0xF2, 0xB1, 0x1E, 0x69, 0x4A, 0x72, 0x73, 0xDD, 0x4D, 0x39, 0x56,
45 | 0xD4, 0x25, 0x88, 0x66, 0x2F, 0x41, 0x1C, 0x93, 0x50, 0x3C, 0x06, 0x80, 0xB0, 0x16, 0xB1, 0x10,
46 | 0xE8, 0xFD, 0xE2, 0x6A, 0xC7, 0xFE, 0x51, 0x38, 0x54, 0xBD, 0xF3, 0x5A, 0x61, 0xBB, 0xF1, 0x7E,
47 | 0x5B, 0x73, 0x9E, 0xB7, 0x79, 0xFB, 0xAC, 0x44, 0x01, 0xD0, 0x59, 0x90, 0xB5, 0xE0, 0xAD, 0xC0,
48 | 0x71, 0x52, 0x5C, 0x3C, 0xCF, 0x6E, 0xD6, 0x46, 0x0B, 0xE9, 0x15, 0x8C, 0xD6, 0x44, 0xB6, 0x40,
49 | 0x8A, 0xFB, 0x37, 0x9D, 0x7E, 0x81, 0x00, 0x03, 0xB3, 0x66, 0x08, 0x60, 0xB2, 0xDC, 0x93, 0x8F,
50 | 0x39, 0x13, 0x62, 0xAE, 0xBB, 0x33, 0x59, 0xA3, 0xD3, 0xAE, 0x1C, 0x23, 0xDB, 0x17, 0x04, 0xC2,
51 | 0x84, 0x64, 0xF0, 0x1A, 0xF7, 0x7C, 0x0F, 0xA1, 0x73, 0xEE, 0x9B, 0x72, 0x42, 0x50, 0x13, 0x1C,
52 | 0xC0, 0xEC, 0x01, 0x99, 0x5B, 0xE4, 0x50, 0x6A, 0x80, 0x1F, 0x8D, 0xAB, 0x1F, 0x15, 0xB1, 0x86,
53 | 0x45, 0x12, 0x0B, 0x3D, 0xD0, 0x73, 0xD8, 0xF4, 0xC0, 0x83, 0x66, 0x4E, 0xD6, 0xD8, 0xEE, 0xA1,
54 | 0xAB, 0xB2, 0x28, 0xAA, 0x77, 0xC1, 0xDC, 0x8D, 0x12, 0x98, 0x1A, 0x5D, 0x54, 0x69, 0x60, 0x6F,
55 | 0x95, 0x99, 0x21, 0x39, 0xD5, 0xDC, 0xDD, 0xF4, 0x2C, 0x38, 0xE0, 0xD6, 0x30, 0x71, 0x1C, 0xEF,
56 | 0x07, 0x9E, 0xF8, 0x51, 0xBB, 0x0F, 0x0E, 0x46, 0xC9, 0x95, 0x61, 0xDA, 0xA0, 0xBF, 0x6D, 0xE6,
57 | 0x6E, 0x23, 0x47, 0x4E, 0x19, 0xE3, 0x38, 0x74, 0x27, 0xC1, 0xEA, 0xCE, 0x17, 0x68, 0x39, 0xA0,
58 | 0xBF, 0x3C, 0xF9, 0x6F, 0xCA, 0xE3, 0x74, 0xBB, 0xC8, 0x6F, 0x43, 0x79, 0x8A, 0x60, 0xDE, 0xFB,
59 | 0xE4, 0xA1, 0x03, 0xD6, 0xB7, 0x74, 0x0C, 0xCF, 0xF4, 0x40, 0xC6, 0xA3, 0x73, 0x5F, 0xD3, 0x2A,
60 | 0x1E, 0xBC, 0xF6, 0xF6, 0xCC, 0x62, 0x4F, 0x38, 0xA0, 0x60, 0x65, 0x78, 0x34, 0x96, 0x60, 0x14,
61 | 0xA4, 0x75, 0x13, 0x87, 0x0F, 0x70, 0x87, 0x96, 0x74, 0xA9, 0xF3, 0xE9, 0x50, 0xF2, 0x25, 0xBE,
62 | 0x18, 0xBD, 0x59, 0xF3, 0x80, 0x6A, 0x15, 0xFB, 0xC2, 0xD1, 0x35, 0xBB, 0x39, 0xA9, 0x20, 0xC5,
63 | 0x3D, 0xC9, 0x12, 0xBB, 0x30, 0x55, 0xE6, 0x47, 0x2F, 0x38, 0x0B, 0xDA, 0x36, 0xED, 0x05, 0xB1,
64 | 0x9F, 0x27, 0xB7, 0xFA, 0x84, 0x42, 0x20, 0xDC, 0x45, 0x23, 0x38, 0x68, 0x6B, 0x1F, 0xF7, 0xAD,
65 | 0x31, 0xC5, 0x2E, 0x05, 0x00, 0x36, 0x4C, 0x74, 0x29, 0x5C, 0x58, 0x60, 0xCE, 0x0E, 0xAE, 0x2E,
66 | 0x51, 0xBD, 0xBA, 0x2A, 0x08, 0x71, 0xDA, 0x4E, 0x76, 0x29, 0xD1, 0x1C, 0x93, 0xC4, 0xB9, 0x05,
67 | 0xEF, 0x4E, 0x2B, 0x0A, 0xC9, 0x1F, 0x9D, 0x16, 0xDD, 0xF3, 0xF9, 0x4A, 0xAE, 0xE5, 0xD6, 0x90,
68 | 0xFC, 0xAA, 0xF3, 0xBF, 0xA0, 0xEB, 0xFD, 0x50, 0xB0, 0x22, 0xA2, 0x4D, 0x38, 0x82, 0x74, 0x94,
69 | 0x4C, 0x2E, 0x3E, 0xC2, 0x1C, 0x8C, 0x05, 0x64, 0x6C, 0x73, 0x15, 0x3A, 0x8D, 0xF1, 0x77, 0x59,
70 | 0xAE, 0xFC, 0xFB, 0x41, 0x01, 0xC0, 0xB8, 0x27, 0x9B, 0x38, 0xB3, 0x8F, 0x55, 0x77, 0x68, 0x15,
71 | 0x91, 0x34, 0xEE, 0xDA, 0x4C, 0x82, 0x8B, 0xC6, 0xF4, 0x54, 0x9A, 0x73, 0xE6, 0xBF, 0xF1, 0x95
72 | };
73 | }
74 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Tank/TRG/ProTRG_81410.cs:
--------------------------------------------------------------------------------
1 | using static TACTLib.Core.Product.Tank.ManifestCryptoHandler;
2 | using static TACTLib.Core.Product.Tank.ResourceGraph;
3 |
4 | namespace TACTLib.Core.Product.Tank.TRG
5 | {
6 | [ManifestCrypto(AutoDetectVersion = true, Product = TACTProduct.Overwatch)]
7 | public class ProTRG_81410 : ITRGEncryptionProc
8 | {
9 | public byte[] Key(TRGHeader header, int length)
10 | {
11 | byte[] buffer = new byte[length];
12 | uint kidx;
13 | kidx = Keytable[header.m_buildVersion & 511];
14 | for (uint i = 0; i != length; ++i)
15 | {
16 | buffer[i] = Keytable[SignedMod(kidx, 512)];
17 | kidx += 3;
18 | }
19 | return buffer;
20 | }
21 |
22 | public byte[] IV(TRGHeader header, byte[] digest, int length)
23 | {
24 | byte[] buffer = new byte[length];
25 | uint kidx;
26 | kidx = (uint)(length * header.m_buildVersion);
27 | for (int i = 0; i != length; ++i)
28 | {
29 | buffer[i] = Keytable[SignedMod(kidx, 512)];
30 | kidx -= 43;
31 | buffer[i] ^= digest[SignedMod(kidx + header.m_skinCount, SHA1_DIGESTSIZE)];
32 | }
33 | return buffer;
34 | }
35 |
36 | private static readonly byte[] Keytable =
37 | {
38 | 0xB3, 0x59, 0x6F, 0x0B, 0xE5, 0xA9, 0x6C, 0x21, 0xCD, 0x91, 0x44, 0x6E, 0x60, 0xBB, 0x13, 0xA0,
39 | 0x74, 0x25, 0xEC, 0x52, 0x04, 0xBD, 0xA3, 0x61, 0x8B, 0x95, 0x29, 0xFE, 0x91, 0x5C, 0x31, 0x2A,
40 | 0xA8, 0xCC, 0xEA, 0x5E, 0x46, 0x5D, 0xD1, 0xD9, 0xE6, 0x77, 0x11, 0xEB, 0x7D, 0x94, 0xF3, 0x2C,
41 | 0xD0, 0xA8, 0x66, 0x23, 0xEB, 0xC1, 0x96, 0x0F, 0xA2, 0xEB, 0x83, 0x21, 0x56, 0x30, 0x80, 0x8B,
42 | 0xEB, 0xEB, 0x1F, 0xA5, 0xC9, 0x6D, 0x89, 0x19, 0xA1, 0xFC, 0x8A, 0x09, 0x24, 0xA5, 0x47, 0xC3,
43 | 0x65, 0x30, 0x59, 0xB1, 0x30, 0x64, 0x02, 0xB8, 0xD9, 0x32, 0x2F, 0x6B, 0x56, 0x39, 0xB9, 0x08,
44 | 0xF5, 0x8F, 0x90, 0xFC, 0x69, 0x1E, 0x55, 0x1A, 0x89, 0x19, 0x50, 0xAD, 0xD5, 0xC2, 0x3B, 0xFB,
45 | 0xC1, 0xFF, 0xA7, 0xBF, 0xFF, 0x93, 0x95, 0x44, 0xE3, 0x82, 0x28, 0x8B, 0x86, 0x3D, 0x45, 0x31,
46 | 0x71, 0xFC, 0xC2, 0x43, 0x5A, 0xA2, 0x3E, 0x30, 0x1B, 0xA1, 0x35, 0xA4, 0x6F, 0x62, 0x2A, 0x96,
47 | 0x6D, 0xE8, 0xA0, 0x6D, 0x06, 0xF3, 0xB0, 0xA0, 0xFA, 0x53, 0x85, 0x95, 0x53, 0x84, 0xCE, 0x74,
48 | 0x61, 0xA7, 0x0D, 0x29, 0x63, 0x90, 0x39, 0x59, 0x48, 0x32, 0x07, 0x57, 0x1A, 0x33, 0x73, 0x91,
49 | 0x0B, 0x9F, 0x3D, 0x2C, 0xE0, 0xF2, 0x22, 0x78, 0x88, 0x6E, 0x36, 0x12, 0x4F, 0x4B, 0x4C, 0x7F,
50 | 0x58, 0x0C, 0xAD, 0x3B, 0xB5, 0x57, 0x90, 0x76, 0x36, 0x1C, 0x1A, 0x09, 0xA7, 0x2A, 0xDD, 0xE7,
51 | 0xE3, 0x2D, 0x97, 0xFC, 0xA0, 0x04, 0x99, 0xC1, 0xA6, 0xA6, 0xB2, 0x61, 0xF4, 0x67, 0xDF, 0x6E,
52 | 0x02, 0x7B, 0xB1, 0xC8, 0xF9, 0x76, 0x72, 0x1D, 0xC9, 0xA7, 0xBA, 0x57, 0x45, 0xBC, 0x56, 0xFF,
53 | 0x6B, 0xC8, 0x67, 0xFA, 0x26, 0x44, 0x72, 0x3A, 0x9D, 0xD9, 0x12, 0x36, 0xBC, 0xFB, 0x8D, 0x59,
54 | 0x0F, 0x45, 0x88, 0x71, 0x23, 0x4B, 0x60, 0x88, 0x1F, 0x93, 0x80, 0xD7, 0x55, 0x2E, 0x0D, 0x81,
55 | 0xC8, 0x56, 0x4A, 0x93, 0x1C, 0xE7, 0x85, 0x41, 0x36, 0x37, 0xC7, 0xD5, 0xB0, 0x9F, 0x1C, 0xF3,
56 | 0x67, 0x0C, 0xD7, 0x3E, 0x18, 0x9F, 0xB9, 0xB9, 0x3E, 0x54, 0x03, 0x4D, 0x23, 0xE9, 0x19, 0x18,
57 | 0xF9, 0x59, 0x8E, 0x42, 0x38, 0x89, 0xDC, 0xD7, 0x66, 0xB1, 0x2F, 0x23, 0x7D, 0x6C, 0x36, 0xA6,
58 | 0x04, 0x19, 0x75, 0xB8, 0xD5, 0xB9, 0x45, 0x5D, 0x36, 0xA5, 0xA4, 0x80, 0x96, 0x27, 0xDB, 0x14,
59 | 0xBC, 0xFF, 0x28, 0x6B, 0xE8, 0x6E, 0xD9, 0xC3, 0xCE, 0x85, 0x42, 0x0F, 0xEB, 0x05, 0x47, 0x0E,
60 | 0xE7, 0x37, 0x76, 0x04, 0x69, 0xB5, 0x4C, 0xDC, 0xA1, 0x76, 0x3E, 0x31, 0x9F, 0x5D, 0xCB, 0x7F,
61 | 0x51, 0xD9, 0x17, 0x22, 0xD3, 0x9E, 0xA0, 0x9A, 0xE6, 0x2B, 0xF1, 0x76, 0x85, 0xE4, 0x20, 0x7F,
62 | 0xCC, 0xF0, 0x2D, 0x48, 0xB9, 0x50, 0x33, 0x93, 0x16, 0x58, 0xBF, 0x62, 0xBC, 0xF9, 0xBF, 0x04,
63 | 0xDE, 0x53, 0xA9, 0x8F, 0xA9, 0x27, 0xFE, 0x00, 0x03, 0xDC, 0x43, 0xAA, 0x1D, 0xDF, 0x75, 0x6A,
64 | 0x56, 0xB5, 0xC1, 0x46, 0xF5, 0x3D, 0x31, 0x2B, 0x97, 0x29, 0x64, 0x62, 0x14, 0xCF, 0xCE, 0x76,
65 | 0xCC, 0xF8, 0x39, 0x2F, 0x07, 0x62, 0x9F, 0xCC, 0x4F, 0xC7, 0x13, 0x86, 0x60, 0x8F, 0x3C, 0xBE,
66 | 0x6F, 0xEA, 0x69, 0xA9, 0x2A, 0xDD, 0x26, 0xAA, 0x9C, 0x1D, 0x6E, 0xCE, 0x86, 0x1D, 0x91, 0x16,
67 | 0x8C, 0x50, 0xD4, 0x4F, 0x4F, 0x84, 0x7B, 0x5B, 0xE4, 0x34, 0x02, 0x41, 0x3B, 0xBE, 0x71, 0x0E,
68 | 0x31, 0xC6, 0x9A, 0xC1, 0xCB, 0x4D, 0xDE, 0x1D, 0xC8, 0x3E, 0x5C, 0x9E, 0xBC, 0x21, 0x16, 0x06,
69 | 0x38, 0xB1, 0x42, 0x70, 0xFC, 0x30, 0xBF, 0xF0, 0x02, 0xB5, 0x2C, 0x3E, 0xC8, 0x02, 0x21, 0x53
70 | };
71 | }
72 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Tank/CMF/ProCMF_38170.cs:
--------------------------------------------------------------------------------
1 | using static TACTLib.Core.Product.Tank.ManifestCryptoHandler;
2 | using static TACTLib.Core.Product.Tank.ContentManifestFile;
3 |
4 | namespace TACTLib.Core.Product.Tank.CMF
5 | {
6 | [ManifestCrypto(AutoDetectVersion = true, Product = TACTProduct.Overwatch)]
7 | public class ProCMF_38170 : ICMFEncryptionProc
8 | {
9 | public byte[] Key(CMFHeader header, int length)
10 | {
11 | byte[] buffer = new byte[length];
12 |
13 | uint kidx = Keytable[header.m_buildVersion & 511];
14 | uint increment = kidx % 61;
15 | for (int i = 0; i != length; ++i)
16 | {
17 | buffer[i] = Keytable[kidx % 512];
18 | kidx += increment;
19 | }
20 |
21 | return buffer;
22 | }
23 |
24 | public byte[] IV(CMFHeader header, byte[] digest, int length)
25 | {
26 | byte[] buffer = new byte[length];
27 |
28 | uint kidx = (uint)(digest[7] + (ushort)header.m_dataCount) & 511;
29 | for (int i = 0; i != length; ++i)
30 | {
31 | buffer[i] = Keytable[kidx % 512];
32 | kidx += 3;
33 | buffer[i] ^= digest[(kidx - i) % SHA1_DIGESTSIZE];
34 | }
35 |
36 | return buffer;
37 | }
38 |
39 | private static readonly byte[] Keytable =
40 | {
41 | 0x78, 0x3F, 0xEC, 0x15, 0xEB, 0xDA, 0xF2, 0xB0, 0x62, 0xD7, 0x0D, 0xCD, 0x15, 0x06, 0x62, 0x94,
42 | 0xA3, 0x25, 0x6E, 0x17, 0x0C, 0x6F, 0x80, 0xCB, 0xEA, 0x6D, 0x88, 0x1E, 0x28, 0xA5, 0x9D, 0xD5,
43 | 0x33, 0x0C, 0x0C, 0xC3, 0x3D, 0x1F, 0x26, 0x0F, 0x65, 0x7C, 0x43, 0xEA, 0x1F, 0xAE, 0x1F, 0x14,
44 | 0xE8, 0x6B, 0xEE, 0x04, 0xEE, 0x62, 0xB7, 0xDA, 0xDF, 0x8C, 0x9C, 0x34, 0x2F, 0x93, 0x77, 0xD6,
45 | 0xAE, 0x8C, 0x13, 0x3A, 0x74, 0x4E, 0xB4, 0x26, 0xBA, 0x89, 0xA2, 0x53, 0xA9, 0x33, 0xCF, 0x50,
46 | 0x51, 0x9E, 0xC4, 0x01, 0xB6, 0x7C, 0x77, 0x7B, 0x9B, 0x31, 0x90, 0xA5, 0x1B, 0xF0, 0x1D, 0x29,
47 | 0xEE, 0x35, 0x38, 0xE7, 0x9F, 0x13, 0xD5, 0x10, 0x2C, 0xF3, 0x95, 0xE4, 0xB0, 0xAA, 0xD0, 0x76,
48 | 0x64, 0x3A, 0xDB, 0x5A, 0x37, 0xF4, 0xEB, 0x8E, 0x81, 0x7B, 0x24, 0x35, 0x81, 0x30, 0xD7, 0x43,
49 | 0x5B, 0x0C, 0xAD, 0xCB, 0x78, 0x50, 0xFD, 0xAF, 0xE0, 0x51, 0xB2, 0xB4, 0x4A, 0x72, 0xC0, 0x3B,
50 | 0xF8, 0xA3, 0x5A, 0x38, 0x49, 0x5E, 0x04, 0x25, 0xC8, 0xB8, 0xA5, 0xA4, 0x30, 0x0F, 0x7E, 0x47,
51 | 0xFF, 0xA2, 0x19, 0x9A, 0xD0, 0x69, 0xC5, 0x98, 0xD8, 0xAC, 0xE3, 0xB7, 0x22, 0x96, 0x92, 0xF1,
52 | 0x0E, 0x00, 0x9F, 0x23, 0xCD, 0x4B, 0x5A, 0xA8, 0xC5, 0x36, 0x2B, 0x77, 0x21, 0x57, 0xEC, 0x9A,
53 | 0x48, 0xC4, 0xED, 0x13, 0x13, 0x06, 0x09, 0xE2, 0x08, 0x56, 0x38, 0x98, 0xA5, 0x50, 0x9B, 0x68,
54 | 0x3D, 0xA4, 0x9A, 0x18, 0xCE, 0x99, 0x83, 0x69, 0xE9, 0x3D, 0xC9, 0xE8, 0x42, 0x60, 0xF8, 0x10,
55 | 0xA8, 0xBB, 0x49, 0xE2, 0x6A, 0x85, 0x5B, 0x42, 0x36, 0x5D, 0x07, 0xFA, 0xED, 0xFE, 0x59, 0xDB,
56 | 0x80, 0x11, 0x7F, 0xE0, 0x7B, 0xF3, 0x4A, 0x28, 0xDA, 0xF9, 0x8C, 0x7B, 0x4A, 0x33, 0x42, 0xB3,
57 | 0xFC, 0xC8, 0xB0, 0x4F, 0x74, 0xB8, 0x3D, 0xFE, 0xED, 0x65, 0x1B, 0x9D, 0x89, 0xB2, 0x68, 0x94,
58 | 0x48, 0x86, 0x3D, 0x16, 0x9F, 0xE6, 0x4C, 0x80, 0xB9, 0x6A, 0xA6, 0xDA, 0xA1, 0x8A, 0x7D, 0x8D,
59 | 0xB6, 0x3B, 0x7D, 0x81, 0x38, 0xEE, 0xEB, 0x95, 0xBF, 0x57, 0x4A, 0xCB, 0xE6, 0x8A, 0x34, 0x2A,
60 | 0xAB, 0x05, 0xE5, 0xB8, 0x23, 0xF2, 0x62, 0x55, 0x0D, 0xDE, 0xF0, 0x81, 0x1C, 0xF3, 0xAF, 0xE3,
61 | 0x04, 0x65, 0xBD, 0xA4, 0xA1, 0xA2, 0x52, 0x00, 0x38, 0x54, 0xE4, 0x8D, 0xFB, 0x61, 0x4C, 0x52,
62 | 0xCB, 0x1E, 0xAE, 0x22, 0xB6, 0xB7, 0x77, 0x40, 0xA4, 0xA4, 0xAA, 0xC8, 0x93, 0xA9, 0xDB, 0xF7,
63 | 0xF3, 0xF3, 0x61, 0xB7, 0xAA, 0x58, 0x0A, 0x42, 0xC2, 0x29, 0x12, 0x1B, 0xC6, 0xE1, 0xD6, 0x27,
64 | 0xF8, 0xB7, 0x83, 0x4C, 0x6D, 0xBD, 0xB2, 0xC2, 0xA1, 0xCE, 0x29, 0xFC, 0xE5, 0xE2, 0xC5, 0xE0,
65 | 0x6D, 0x39, 0x26, 0x8C, 0x16, 0xB4, 0x69, 0xA8, 0xEA, 0xE1, 0xC7, 0x45, 0x0E, 0x0D, 0xF4, 0xDB,
66 | 0x9C, 0xAC, 0x3E, 0xB9, 0x0D, 0x08, 0x75, 0x8E, 0x5F, 0x8A, 0xC5, 0x18, 0xA8, 0xD5, 0x34, 0xB2,
67 | 0x00, 0xC3, 0x27, 0x56, 0x49, 0xA2, 0x9E, 0x46, 0x48, 0x25, 0xFE, 0x27, 0x13, 0x8A, 0x7C, 0x64,
68 | 0x94, 0x25, 0xCD, 0xA9, 0xEC, 0xDB, 0xE6, 0x4B, 0x39, 0x8D, 0xAE, 0x1F, 0x31, 0x2C, 0x5F, 0x37,
69 | 0xE6, 0x08, 0x24, 0xC6, 0x43, 0xDD, 0xF8, 0x48, 0x5D, 0x87, 0x1A, 0x20, 0xBD, 0x12, 0xFE, 0x57,
70 | 0x7E, 0x12, 0x1D, 0x91, 0x90, 0x7C, 0x90, 0xFB, 0x80, 0x3D, 0xCA, 0x37, 0xF9, 0xDA, 0x7A, 0x75,
71 | 0xC6, 0x30, 0x2B, 0x62, 0x61, 0xCA, 0xE1, 0xA9, 0xB8, 0x03, 0xB1, 0xF5, 0xBD, 0xA1, 0x76, 0xA0,
72 | 0x77, 0xDE, 0xEA, 0xDC, 0xEC, 0x80, 0x69, 0x27, 0x52, 0xC2, 0x34, 0x33, 0x17, 0xDC, 0x1E, 0x97
73 | };
74 | }
75 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Tank/CMF/ProCMF_39083.cs:
--------------------------------------------------------------------------------
1 | using static TACTLib.Core.Product.Tank.ManifestCryptoHandler;
2 | using static TACTLib.Core.Product.Tank.ContentManifestFile;
3 |
4 | namespace TACTLib.Core.Product.Tank.CMF
5 | {
6 | [ManifestCrypto(AutoDetectVersion = true, Product = TACTProduct.Overwatch)]
7 | public class ProCMF_39083 : ICMFEncryptionProc
8 | {
9 | public byte[] Key(CMFHeader header, int length)
10 | {
11 | byte[] buffer = new byte[length];
12 |
13 | uint kidx = Constrain(length * header.m_buildVersion);
14 | uint decrement = header.m_buildVersion & 511;
15 | for (int i = 0; i != length; ++i)
16 | {
17 | buffer[i] = Keytable[kidx % 512];
18 | kidx -= decrement;
19 | }
20 |
21 | return buffer;
22 | }
23 |
24 | public byte[] IV(CMFHeader header, byte[] digest, int length)
25 | {
26 | byte[] buffer = new byte[length];
27 | uint kidx = Keytable[header.m_dataCount & 511];
28 | for (int i = 0; i != length; ++i)
29 | {
30 | buffer[i] = Keytable[kidx % 512];
31 | kidx += 3;
32 | buffer[i] ^= digest[(kidx - i) % SHA1_DIGESTSIZE];
33 | }
34 |
35 | return buffer;
36 | }
37 |
38 | private static readonly byte[] Keytable =
39 | {
40 | 0x16, 0x7A, 0x47, 0x4E, 0x71, 0x7B, 0x99, 0x57, 0x74, 0xB8, 0xD8, 0xBA, 0x67, 0x00, 0x3F, 0x7D,
41 | 0xED, 0x88, 0xC1, 0x6A, 0x70, 0x8F, 0xA0, 0x2C, 0x67, 0x19, 0x1F, 0xE0, 0xAE, 0xDF, 0xB2, 0x5A,
42 | 0xEE, 0x7C, 0x54, 0x40, 0x6C, 0xEE, 0x0D, 0x26, 0x13, 0xBB, 0x3F, 0x90, 0xC4, 0x5E, 0x66, 0x7F,
43 | 0xC6, 0x04, 0x8B, 0x98, 0xC0, 0x71, 0x99, 0x60, 0xE9, 0x33, 0x28, 0xC0, 0x1E, 0xB3, 0x25, 0xD4,
44 | 0xD2, 0x8B, 0x43, 0xE8, 0x28, 0x7A, 0x50, 0x08, 0x45, 0xD3, 0xC2, 0xC1, 0xDE, 0x3B, 0xD2, 0xFF,
45 | 0x26, 0x7F, 0x2E, 0xF3, 0xB1, 0x39, 0xA5, 0x82, 0x97, 0x32, 0x20, 0xC4, 0x8E, 0xDE, 0xA0, 0xCC,
46 | 0x67, 0x3D, 0x16, 0x2C, 0x85, 0x8E, 0xCA, 0xE4, 0x44, 0xC8, 0x07, 0x6D, 0xF5, 0xAF, 0x9B, 0x67,
47 | 0xE9, 0xBA, 0x90, 0x16, 0xF8, 0xE0, 0xF9, 0x49, 0x18, 0xB6, 0x3B, 0xC8, 0x10, 0x7B, 0x0C, 0xF2,
48 | 0x0F, 0x00, 0x3A, 0xC3, 0xD8, 0x19, 0xDD, 0xC0, 0x39, 0x29, 0xAD, 0x5F, 0xDB, 0x95, 0x31, 0xF7,
49 | 0x44, 0xC3, 0x84, 0x8E, 0x51, 0xFA, 0x94, 0x87, 0x5A, 0xF2, 0x9C, 0xA9, 0x69, 0x53, 0x28, 0xD7,
50 | 0xA0, 0xF8, 0xF4, 0x8D, 0x04, 0xB4, 0xC7, 0xC4, 0xE8, 0xF0, 0x91, 0x6E, 0x16, 0x0E, 0xAF, 0x6B,
51 | 0x2E, 0xB7, 0x85, 0x88, 0x58, 0x95, 0xC0, 0x21, 0xE9, 0x13, 0x56, 0x5B, 0x88, 0xA1, 0x72, 0x71,
52 | 0xCB, 0x54, 0x37, 0x28, 0xBB, 0xCF, 0x2D, 0xD8, 0x68, 0xB8, 0x15, 0xF3, 0x30, 0xB4, 0x45, 0xA5,
53 | 0x95, 0x56, 0x69, 0x96, 0x74, 0x39, 0xE2, 0x9A, 0xA4, 0x55, 0xD1, 0x8D, 0x02, 0x5B, 0x91, 0x25,
54 | 0xFC, 0x4A, 0xB3, 0xE0, 0x9E, 0x72, 0x73, 0x00, 0xCA, 0x03, 0x41, 0xBF, 0x5D, 0x19, 0x8F, 0x4E,
55 | 0x59, 0xA4, 0x40, 0x23, 0x50, 0x12, 0xDE, 0xB1, 0x4B, 0xC3, 0x6C, 0x8A, 0x2C, 0xF8, 0xCF, 0x5E,
56 | 0xED, 0x0C, 0x7D, 0xBE, 0x68, 0xE8, 0xCC, 0x46, 0xDC, 0xA1, 0xC7, 0x37, 0xCA, 0x36, 0x62, 0x7F,
57 | 0x5B, 0xBD, 0x1A, 0xAE, 0xAC, 0x8C, 0x32, 0xFE, 0x7A, 0xD5, 0x72, 0xD4, 0x7C, 0x98, 0x9A, 0xB4,
58 | 0xBB, 0x0C, 0xA2, 0x23, 0x45, 0x6B, 0x2A, 0xFC, 0xAE, 0xE6, 0xF2, 0x85, 0x06, 0x21, 0x98, 0x33,
59 | 0x59, 0xD5, 0x06, 0x1B, 0x4B, 0xA9, 0x61, 0xCD, 0x66, 0x35, 0x56, 0xD8, 0x22, 0xEF, 0x7E, 0xAB,
60 | 0x1C, 0x58, 0xE9, 0x98, 0xB5, 0xE0, 0x85, 0xEC, 0xCB, 0x3A, 0xA8, 0x6D, 0x87, 0x96, 0xA8, 0x84,
61 | 0xA2, 0x6C, 0xDD, 0x4F, 0x06, 0x5C, 0xE4, 0x03, 0x47, 0xAF, 0x19, 0x00, 0x4B, 0x2B, 0xE4, 0x1A,
62 | 0x60, 0x68, 0x2A, 0x70, 0xC5, 0x48, 0x68, 0xA7, 0x41, 0xE2, 0xE2, 0xC3, 0x51, 0xF4, 0xED, 0x58,
63 | 0xCF, 0x63, 0x44, 0x6B, 0x3C, 0xC8, 0x18, 0x46, 0xCC, 0x89, 0x5D, 0x01, 0x3C, 0x42, 0x6F, 0x3B,
64 | 0xDE, 0xAA, 0xCC, 0x51, 0x09, 0xCC, 0xF9, 0x74, 0x10, 0x86, 0xB8, 0x96, 0xD5, 0x69, 0xD3, 0x42,
65 | 0xB1, 0x4B, 0xD9, 0x05, 0x8E, 0x15, 0x77, 0xC2, 0x09, 0x50, 0xA6, 0x6F, 0xD0, 0xC2, 0xB6, 0xA6,
66 | 0xC6, 0xC6, 0x02, 0x70, 0xAD, 0xBA, 0x3B, 0x67, 0x89, 0xF5, 0xFC, 0x93, 0xE8, 0x52, 0xCC, 0x0C,
67 | 0x62, 0x1C, 0x14, 0x10, 0x6F, 0x6D, 0x1D, 0x98, 0xB6, 0xDF, 0x82, 0x3F, 0x01, 0x33, 0x16, 0x67,
68 | 0xF6, 0xFE, 0x8B, 0x8F, 0x57, 0x9B, 0x7D, 0x7B, 0x00, 0x2E, 0xE2, 0xEC, 0x85, 0x60, 0x93, 0xD9,
69 | 0x21, 0x1D, 0x95, 0xFE, 0x41, 0x1E, 0x14, 0xB0, 0x3F, 0xD1, 0x55, 0x5E, 0x1E, 0xA9, 0xC0, 0x86,
70 | 0x93, 0xAD, 0x36, 0x5D, 0x23, 0xBA, 0x83, 0x30, 0x78, 0x4C, 0x2F, 0x29, 0x14, 0xBE, 0x4A, 0xEF,
71 | 0xDE, 0x54, 0xAE, 0xB9, 0x97, 0xAA, 0x9B, 0x8A, 0xE9, 0xE0, 0xAB, 0xCE, 0x22, 0x0D, 0x0C, 0xE2
72 | };
73 | }
74 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Tank/CMF/ProCMF_63869.cs:
--------------------------------------------------------------------------------
1 | using static TACTLib.Core.Product.Tank.ManifestCryptoHandler;
2 | using static TACTLib.Core.Product.Tank.ContentManifestFile;
3 |
4 | namespace TACTLib.Core.Product.Tank.CMF
5 | {
6 | [ManifestCrypto(AutoDetectVersion = true, Product = TACTProduct.Overwatch)]
7 | public class ProCMF_63869 : ICMFEncryptionProc
8 | {
9 | public byte[] Key(CMFHeader header, int length)
10 | {
11 | byte[] buffer = new byte[length];
12 | uint kidx = Keytable[length + 256];
13 | for (uint i = 0; i != length; ++i)
14 | {
15 | buffer[i] = Keytable[SignedMod(kidx, 512)];
16 | kidx += (uint)header.m_entryCount;
17 | }
18 |
19 | return buffer;
20 | }
21 |
22 | public byte[] IV(CMFHeader header, byte[] digest, int length)
23 | {
24 | byte[] buffer = new byte[length];
25 | uint kidx = Keytable[SignedMod((2 * digest[13]) - length, 512)];
26 | for (int i = 0; i != length; ++i)
27 | {
28 | buffer[i] = Keytable[SignedMod(kidx, 512)];
29 | kidx = header.m_buildVersion - kidx;
30 | buffer[i] ^= digest[SignedMod(kidx + i, SHA1_DIGESTSIZE)];
31 | }
32 |
33 | return buffer;
34 | }
35 |
36 | private static readonly byte[] Keytable =
37 | {
38 | 0x9E, 0x7F, 0x61, 0x85, 0x44, 0x1D, 0x18, 0xAF, 0xE3, 0x7E, 0x63, 0x97, 0xBF, 0x3C, 0x88, 0x3A,
39 | 0x4F, 0xB8, 0xFC, 0x0C, 0x82, 0xA1, 0x9B, 0x63, 0x2A, 0x91, 0xAC, 0x85, 0x96, 0xAE, 0x89, 0xDF,
40 | 0x06, 0x62, 0xF5, 0x78, 0xB9, 0x19, 0xFB, 0xCB, 0xA5, 0xF9, 0x9D, 0x69, 0x5A, 0x30, 0xC2, 0xE6,
41 | 0x45, 0x31, 0x9B, 0xAC, 0x2D, 0x1C, 0x7C, 0x40, 0x44, 0x94, 0x7B, 0xF7, 0xAE, 0x47, 0x50, 0xF2,
42 | 0xFB, 0x30, 0x6A, 0xFE, 0xCB, 0x30, 0x6A, 0x40, 0x08, 0x1D, 0x88, 0x01, 0x57, 0xF9, 0x62, 0x87,
43 | 0xEE, 0x76, 0x3D, 0xA9, 0x20, 0x3D, 0x01, 0xA4, 0x43, 0xC5, 0x35, 0x40, 0xA2, 0x40, 0x71, 0x5D,
44 | 0x25, 0xE1, 0x9C, 0x91, 0xCE, 0xC8, 0xC6, 0x03, 0xEA, 0x0E, 0x23, 0x66, 0x8C, 0x7D, 0x90, 0xB9,
45 | 0x59, 0xBA, 0x80, 0xCD, 0xF2, 0xB2, 0xA0, 0x6D, 0x96, 0x8E, 0x18, 0x5A, 0x42, 0x26, 0x87, 0x29,
46 | 0x39, 0x6C, 0x1D, 0x4C, 0x67, 0xD0, 0xDA, 0xEC, 0x9D, 0xEB, 0xC3, 0xB0, 0x40, 0x86, 0xD4, 0xB3,
47 | 0xC8, 0xB4, 0x00, 0x78, 0x82, 0x74, 0x1C, 0x79, 0x78, 0x90, 0x1E, 0x87, 0xC1, 0x37, 0x78, 0x17,
48 | 0x44, 0x3A, 0x10, 0x3D, 0xC1, 0x27, 0xFE, 0x71, 0x23, 0x83, 0xD8, 0xAD, 0xF9, 0x9D, 0x9C, 0x30,
49 | 0xAD, 0x87, 0xFC, 0x83, 0x46, 0x65, 0x26, 0xEC, 0xF1, 0x1A, 0x8C, 0x50, 0x7A, 0x1D, 0xD7, 0x62,
50 | 0x20, 0xDC, 0xC1, 0xCF, 0xC7, 0x89, 0x6A, 0xCA, 0x27, 0x93, 0xA3, 0xC3, 0xAB, 0xB7, 0xC1, 0x3C,
51 | 0x41, 0x6C, 0xDC, 0x37, 0xA6, 0x20, 0x4D, 0x35, 0x19, 0x28, 0x9B, 0x22, 0x54, 0xCA, 0x9F, 0xAD,
52 | 0xF0, 0xA4, 0x3D, 0x5E, 0xD4, 0x59, 0xE4, 0xF6, 0xC4, 0x78, 0x78, 0x3D, 0x01, 0x27, 0xE1, 0xE4,
53 | 0xFB, 0x44, 0xD9, 0xAB, 0xDB, 0x26, 0xE2, 0x3C, 0x76, 0x60, 0x8F, 0x05, 0xBD, 0x17, 0xE3, 0x66,
54 | 0x97, 0x33, 0x2F, 0x68, 0x78, 0xAF, 0x7B, 0x7F, 0x00, 0xCA, 0xA2, 0xA6, 0x62, 0x7D, 0xE3, 0x8E,
55 | 0xCF, 0xE2, 0xF7, 0x43, 0x55, 0xFC, 0x8A, 0xA8, 0xAC, 0xC1, 0xA1, 0xF2, 0x4C, 0x00, 0x85, 0x53,
56 | 0xDA, 0xCE, 0x7E, 0x83, 0x49, 0x6F, 0x45, 0x19, 0xFB, 0x99, 0xD7, 0x77, 0x69, 0xB2, 0xEF, 0xA8,
57 | 0x7C, 0x71, 0x88, 0x43, 0x0D, 0x58, 0x17, 0xD2, 0x47, 0x32, 0x27, 0x9F, 0x05, 0xCB, 0x01, 0x72,
58 | 0xC7, 0x5C, 0x95, 0x8D, 0xF0, 0xB2, 0xFB, 0x43, 0x04, 0x9F, 0xF2, 0x22, 0x4C, 0xBF, 0xC9, 0xFF,
59 | 0x35, 0x96, 0x35, 0x29, 0xC7, 0x25, 0xCA, 0xFF, 0x35, 0x60, 0x2F, 0x0C, 0x52, 0xCD, 0x1C, 0x12,
60 | 0x31, 0xC5, 0xA2, 0xC5, 0x1C, 0xFC, 0x03, 0x2A, 0xAD, 0xC0, 0xFD, 0xFB, 0x03, 0x24, 0x17, 0x07,
61 | 0xCD, 0x79, 0xC4, 0x27, 0x45, 0x08, 0x0B, 0xD8, 0x25, 0x79, 0xE0, 0xFB, 0xB4, 0x4C, 0xBA, 0x2A,
62 | 0x01, 0xD2, 0x63, 0x79, 0x10, 0xD5, 0x9C, 0x75, 0x03, 0x4A, 0xC5, 0xA9, 0x2E, 0xE6, 0x8F, 0x2D,
63 | 0x4E, 0x40, 0x30, 0xF3, 0x6F, 0x3A, 0x8D, 0x4C, 0xAF, 0x96, 0x8F, 0x81, 0x9B, 0xEC, 0xCA, 0x84,
64 | 0xF0, 0x4B, 0xC6, 0xD1, 0x26, 0xBE, 0x1F, 0xFD, 0x30, 0x38, 0xC2, 0x16, 0x85, 0xF7, 0x8B, 0xAF,
65 | 0x72, 0x53, 0xC2, 0x2F, 0x2D, 0xDD, 0xA6, 0x8C, 0xCD, 0x8F, 0x9E, 0x23, 0x35, 0x09, 0x32, 0xD3,
66 | 0x2A, 0xF7, 0xA6, 0x1D, 0xE4, 0xF9, 0xE8, 0x31, 0x67, 0x2A, 0x42, 0x5A, 0xC2, 0xF3, 0xC9, 0x40,
67 | 0x1C, 0x7B, 0xB4, 0xE1, 0xF1, 0x47, 0xB2, 0xF2, 0x1F, 0x74, 0x10, 0x4D, 0xB1, 0xF3, 0x3F, 0xB0,
68 | 0xB2, 0x35, 0x68, 0xF3, 0xD3, 0xF0, 0xA1, 0x33, 0xDD, 0x39, 0x32, 0x08, 0xAB, 0xAF, 0x46, 0x36,
69 | 0x0B, 0xE5, 0xDD, 0xC1, 0xBB, 0x2E, 0x15, 0x13, 0x27, 0x6C, 0x03, 0x8C, 0x79, 0x6B, 0x44, 0xE4
70 | };
71 | }
72 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Tank/CMF/ProCMF_43515.cs:
--------------------------------------------------------------------------------
1 | using static TACTLib.Core.Product.Tank.ManifestCryptoHandler;
2 | using static TACTLib.Core.Product.Tank.ContentManifestFile;
3 |
4 | namespace TACTLib.Core.Product.Tank.CMF
5 | {
6 | [ManifestCrypto(AutoDetectVersion = true, Product = TACTProduct.Overwatch)]
7 | public class ProCMF_43515 : ICMFEncryptionProc
8 | {
9 | public byte[] Key(CMFHeader header, int length)
10 | {
11 | byte[] buffer = new byte[length];
12 |
13 | uint kidx = Keytable[header.m_buildVersion & 511];
14 | for (int i = 0; i != length; ++i)
15 | {
16 | buffer[i] = Keytable[SignedMod(kidx, 512)];
17 | kidx = header.m_buildVersion - kidx;
18 | }
19 |
20 | return buffer;
21 | }
22 |
23 | public byte[] IV(CMFHeader header, byte[] digest, int length)
24 | {
25 | byte[] buffer = new byte[length];
26 |
27 | uint kidx = Keytable[header.m_buildVersion & 511];
28 | for (int i = 0; i != length; ++i)
29 | {
30 | buffer[i] = Keytable[SignedMod(kidx, 512)];
31 | kidx -= 0x2B;
32 | buffer[i] ^= digest[SignedMod(kidx + header.m_dataCount, SHA1_DIGESTSIZE)];
33 | }
34 |
35 | return buffer;
36 | }
37 |
38 | private static readonly byte[] Keytable =
39 | {
40 | 0xEE, 0xF8, 0x0D, 0x93, 0x26, 0x31, 0xE9, 0x0D, 0xFF, 0x07, 0xE7, 0xFA, 0xE8, 0xFD, 0x3E, 0x05,
41 | 0xA0, 0xA7, 0x49, 0xC7, 0x5C, 0xCC, 0x24, 0x78, 0x4D, 0x3C, 0xDD, 0x4B, 0x6A, 0xBF, 0xDF, 0x79,
42 | 0xA9, 0x73, 0xA4, 0x7B, 0x9B, 0x50, 0x6C, 0xFF, 0xA1, 0x2C, 0xFA, 0x6C, 0x96, 0x79, 0x8F, 0x31,
43 | 0x6C, 0x97, 0x74, 0x94, 0x20, 0x47, 0x70, 0x09, 0x37, 0x28, 0x62, 0x46, 0x4A, 0xA3, 0x83, 0xD3,
44 | 0xD4, 0xE4, 0x59, 0xAA, 0x85, 0x13, 0x67, 0x7E, 0xD6, 0x59, 0x0A, 0x84, 0x06, 0x7C, 0x19, 0xB1,
45 | 0x8A, 0xE6, 0xEC, 0x2A, 0xAE, 0x01, 0xBB, 0x23, 0x15, 0x50, 0x2C, 0x0D, 0xF0, 0x25, 0x68, 0x1C,
46 | 0x15, 0xCC, 0x06, 0xB5, 0x88, 0xEF, 0x55, 0xF4, 0xD0, 0xE9, 0x6D, 0x16, 0x58, 0x68, 0x0F, 0xD4,
47 | 0xDF, 0x53, 0xE7, 0xC3, 0x9D, 0x13, 0xEE, 0x64, 0x56, 0xE8, 0x58, 0xAB, 0x10, 0xFA, 0xDC, 0x2A,
48 | 0xC4, 0x9B, 0xE4, 0x5F, 0xDD, 0x9F, 0x28, 0xCA, 0x82, 0x9B, 0xC8, 0x4E, 0x37, 0x19, 0x1F, 0x34,
49 | 0x13, 0x06, 0x00, 0xE1, 0x31, 0xA7, 0xD4, 0x7D, 0x6E, 0xBE, 0x95, 0xD6, 0x45, 0xC9, 0x05, 0x0B,
50 | 0xB8, 0xBD, 0x07, 0x98, 0x5C, 0x48, 0x46, 0x26, 0xCF, 0x1B, 0x9E, 0x2C, 0x96, 0xB3, 0x5F, 0x9E,
51 | 0x36, 0x07, 0xA5, 0x61, 0x87, 0x49, 0x4D, 0x0A, 0x79, 0xA8, 0x8F, 0x0F, 0x9D, 0x5E, 0xEB, 0x07,
52 | 0x0D, 0x7E, 0xF8, 0x1B, 0xC8, 0x9F, 0x7B, 0x2A, 0x1D, 0x69, 0x24, 0x81, 0x4E, 0x09, 0xC7, 0xD1,
53 | 0x54, 0x00, 0x20, 0x2E, 0xE8, 0x60, 0x4C, 0x71, 0x04, 0x96, 0x03, 0x4E, 0xA5, 0x9B, 0x41, 0xB5,
54 | 0x02, 0x91, 0x0B, 0x3F, 0x84, 0x77, 0x2F, 0xCD, 0x19, 0x00, 0xDB, 0x23, 0xB7, 0xBC, 0x47, 0xBD,
55 | 0x62, 0x36, 0x54, 0xCD, 0x99, 0x87, 0x0F, 0x63, 0xD9, 0x74, 0xD1, 0xD1, 0xE2, 0x5E, 0x2B, 0xC6,
56 | 0xDA, 0x96, 0x57, 0x28, 0x9C, 0x54, 0xF4, 0x2C, 0xB9, 0xE1, 0x2F, 0x15, 0xCF, 0x04, 0x2E, 0xE1,
57 | 0xFD, 0x44, 0x08, 0xC3, 0xED, 0xE1, 0x58, 0xD4, 0x40, 0x6F, 0x26, 0x11, 0x64, 0xC8, 0x5D, 0x13,
58 | 0x63, 0xAE, 0xCD, 0x4C, 0xC0, 0x1D, 0x73, 0xF2, 0xF9, 0x4D, 0x2D, 0x1C, 0x9E, 0xFB, 0xF5, 0xEF,
59 | 0x8D, 0x78, 0x51, 0xB3, 0x54, 0x23, 0xC5, 0x66, 0x48, 0xF6, 0xCA, 0xD0, 0xF0, 0xFE, 0x18, 0x97,
60 | 0x88, 0x78, 0x55, 0x22, 0xA5, 0x7D, 0x33, 0xCA, 0xBD, 0x91, 0x21, 0x2D, 0xEC, 0x10, 0x42, 0xEF,
61 | 0x5C, 0xC0, 0x5E, 0x34, 0x71, 0x0A, 0x9A, 0x14, 0xBA, 0x9B, 0xA4, 0xAE, 0x3D, 0xA2, 0xC4, 0x5F,
62 | 0x02, 0xD2, 0x67, 0x8D, 0xC7, 0x2C, 0xFE, 0xAE, 0x34, 0xD7, 0xA7, 0x36, 0x3B, 0x67, 0x35, 0x36,
63 | 0x3A, 0xE4, 0xDA, 0x40, 0x83, 0x60, 0xDF, 0x24, 0x0E, 0x36, 0x43, 0x29, 0xB8, 0xDA, 0x22, 0xBA,
64 | 0xA7, 0x73, 0x7F, 0xE3, 0x9A, 0xFE, 0xBE, 0x7C, 0xF9, 0xA0, 0xF7, 0x92, 0xCB, 0xDA, 0x03, 0xA7,
65 | 0x76, 0x82, 0xA2, 0x9C, 0x39, 0x40, 0x86, 0xD6, 0x02, 0x4E, 0x8B, 0xDB, 0x11, 0xBE, 0x82, 0x81,
66 | 0x27, 0x97, 0x53, 0x32, 0x63, 0x14, 0x47, 0xC8, 0x7D, 0x3E, 0x21, 0x04, 0xBB, 0x4A, 0x26, 0x24,
67 | 0xF5, 0x84, 0x13, 0x7C, 0xBD, 0x6D, 0x1F, 0xB4, 0x91, 0xF1, 0x2E, 0x72, 0x1C, 0xCA, 0xCC, 0xA1,
68 | 0x22, 0x36, 0x5F, 0x66, 0xC0, 0x16, 0xFB, 0x94, 0x0E, 0xD7, 0x82, 0x0D, 0x95, 0xEF, 0x94, 0x12,
69 | 0x97, 0xC0, 0xBE, 0xBE, 0x75, 0x79, 0x9B, 0x35, 0x22, 0x77, 0xBA, 0x85, 0x93, 0xF0, 0x2F, 0x2E,
70 | 0x3E, 0xFC, 0x18, 0x25, 0x0F, 0xC3, 0xE5, 0x9E, 0x8B, 0xA9, 0x6D, 0xDB, 0xA1, 0x7E, 0x7B, 0x2E,
71 | 0x37, 0x54, 0xFA, 0x11, 0xEC, 0x7B, 0x83, 0x0E, 0x1F, 0x28, 0xED, 0x1A, 0x75, 0x1C, 0xB9, 0x6A
72 | };
73 | }
74 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Tank/CMF/ProCMF_63568.cs:
--------------------------------------------------------------------------------
1 | using static TACTLib.Core.Product.Tank.ManifestCryptoHandler;
2 | using static TACTLib.Core.Product.Tank.ContentManifestFile;
3 |
4 | namespace TACTLib.Core.Product.Tank.CMF
5 | {
6 | [ManifestCrypto(AutoDetectVersion = true, Product = TACTProduct.Overwatch)]
7 | public class ProCMF_63568 : ICMFEncryptionProc
8 | {
9 | public byte[] Key(CMFHeader header, int length)
10 | {
11 | byte[] buffer = new byte[length];
12 | uint kidx = (uint)(header.m_buildVersion * length);
13 | for (uint i = 0; i != length; ++i)
14 | {
15 | buffer[i] = Keytable[SignedMod(kidx, 512)];
16 | kidx += (uint)header.m_entryCount;
17 | }
18 |
19 | return buffer;
20 | }
21 |
22 | public byte[] IV(CMFHeader header, byte[] digest, int length)
23 | {
24 | byte[] buffer = new byte[length];
25 | uint kidx = Keytable[SignedMod((2 * digest[13]) - length, 512)];
26 | for (int i = 0; i != length; ++i)
27 | {
28 | buffer[i] = Keytable[SignedMod(kidx, 512)];
29 | kidx -= 43;
30 | buffer[i] ^= digest[SignedMod(kidx + header.m_dataCount, SHA1_DIGESTSIZE)];
31 | }
32 |
33 | return buffer;
34 | }
35 |
36 | private static readonly byte[] Keytable =
37 | {
38 | 0x69, 0x3A, 0xD8, 0xEF, 0x4E, 0x52, 0xD6, 0xB3, 0x65, 0x70, 0x91, 0x4C, 0x14, 0x67, 0xAC, 0x74,
39 | 0x57, 0x81, 0x0C, 0x43, 0xE4, 0xCA, 0xD3, 0x39, 0xFA, 0x6A, 0xE6, 0xC8, 0xDA, 0x49, 0xAC, 0x39,
40 | 0x48, 0xFD, 0x52, 0x6D, 0x5A, 0x88, 0xD7, 0x24, 0x63, 0x9E, 0x78, 0x9D, 0x72, 0x26, 0x8A, 0xAB,
41 | 0x29, 0x03, 0x9B, 0xDA, 0xF4, 0x96, 0xA9, 0x16, 0x04, 0x58, 0x7F, 0x65, 0x1D, 0x33, 0xA7, 0x1D,
42 | 0xFC, 0x4B, 0x1C, 0x66, 0x31, 0x99, 0xE4, 0x18, 0x42, 0x01, 0x93, 0x18, 0x31, 0xFF, 0x5F, 0x88,
43 | 0xD5, 0x72, 0x2A, 0x07, 0x40, 0x25, 0x1E, 0xB1, 0x12, 0x93, 0x37, 0xDC, 0xB0, 0xDA, 0xF2, 0x04,
44 | 0x8E, 0x61, 0x14, 0xA6, 0x76, 0x42, 0xA7, 0xB6, 0x26, 0xE8, 0xBB, 0x32, 0x66, 0xC7, 0xDB, 0x39,
45 | 0xA6, 0x57, 0x88, 0x66, 0xA9, 0x18, 0x87, 0x93, 0xE5, 0x27, 0x89, 0x13, 0x46, 0xCA, 0xF8, 0xDB,
46 | 0xA6, 0x55, 0xF3, 0x28, 0x4C, 0x8B, 0x75, 0x50, 0x14, 0x8A, 0x65, 0x53, 0x4E, 0x32, 0x74, 0x7E,
47 | 0x63, 0x71, 0xC3, 0xE2, 0x05, 0x8C, 0x9A, 0x1E, 0x73, 0xBB, 0x8F, 0xE4, 0x12, 0x7E, 0x4A, 0x1F,
48 | 0x56, 0x40, 0xAC, 0x8A, 0x96, 0x05, 0x11, 0xD5, 0x02, 0x23, 0x7F, 0x39, 0x2E, 0xF7, 0x56, 0xBE,
49 | 0xF9, 0xED, 0x0C, 0xA4, 0x61, 0xC7, 0xD1, 0x9E, 0x31, 0x70, 0x78, 0xA8, 0x0C, 0x8B, 0x35, 0x6C,
50 | 0x77, 0x4F, 0xF7, 0x0F, 0xCD, 0xA7, 0x10, 0xAB, 0x3E, 0x97, 0xC8, 0xE1, 0x61, 0xC7, 0x8C, 0xBF,
51 | 0xAF, 0x59, 0x48, 0x14, 0x9A, 0x1D, 0xA7, 0x49, 0xD6, 0x50, 0xD3, 0xA0, 0x63, 0xF4, 0x7A, 0x06,
52 | 0x06, 0xF8, 0x98, 0xCA, 0x51, 0x40, 0xB4, 0x0A, 0x0C, 0x29, 0x56, 0x76, 0x09, 0x2D, 0x65, 0xF0,
53 | 0x13, 0x61, 0x13, 0xA2, 0xC5, 0xB2, 0xED, 0xA2, 0x58, 0x6C, 0x58, 0x9C, 0xCF, 0xE9, 0x3C, 0xB3,
54 | 0x9D, 0xFA, 0x36, 0x35, 0x6E, 0x06, 0x8F, 0x05, 0xE3, 0x2D, 0x95, 0x71, 0x31, 0x87, 0x12, 0xF1,
55 | 0xB2, 0x7A, 0x33, 0x5B, 0xE2, 0xB7, 0x61, 0xDA, 0x90, 0xC3, 0x9B, 0x56, 0xCA, 0xF8, 0x0B, 0xE8,
56 | 0x62, 0xD2, 0x46, 0x33, 0x1F, 0x7E, 0x9F, 0x10, 0x52, 0x75, 0x05, 0x88, 0xB1, 0xC0, 0x4D, 0x7A,
57 | 0x07, 0x2A, 0x68, 0x94, 0x53, 0xA3, 0x37, 0x80, 0xC8, 0x8F, 0x53, 0xF8, 0xB8, 0x0F, 0x22, 0xD2,
58 | 0xE9, 0x06, 0x41, 0x3E, 0x4F, 0x21, 0x23, 0x4D, 0x9E, 0x74, 0x0F, 0x38, 0x6A, 0xF4, 0x0B, 0x0F,
59 | 0x79, 0x20, 0x01, 0xF2, 0x54, 0xB9, 0x42, 0x78, 0xB3, 0x77, 0xBD, 0x71, 0xB6, 0xA4, 0xD0, 0x01,
60 | 0xB5, 0xFA, 0x4E, 0xEC, 0x8A, 0x51, 0x57, 0x24, 0xED, 0xC1, 0x23, 0x80, 0x6C, 0xAB, 0x0D, 0x85,
61 | 0xCC, 0xD7, 0xE4, 0xE2, 0x40, 0x99, 0xEB, 0xB3, 0xCE, 0x58, 0x21, 0x5A, 0xC8, 0xD8, 0xC4, 0x47,
62 | 0x69, 0xB4, 0xDC, 0x90, 0xAA, 0xEA, 0x83, 0xCD, 0xE8, 0xE9, 0x89, 0xAA, 0x80, 0x87, 0xCB, 0x5B,
63 | 0xF4, 0x83, 0x3A, 0xF3, 0x07, 0x6F, 0x27, 0xCF, 0x4A, 0xEF, 0x67, 0x18, 0xAD, 0x00, 0xF3, 0xAD,
64 | 0x36, 0xD7, 0x79, 0xB0, 0xE5, 0x85, 0x20, 0x6E, 0xAA, 0x6C, 0xB0, 0x5B, 0x20, 0xD9, 0xF0, 0xBA,
65 | 0xC0, 0x59, 0x9F, 0xEC, 0x69, 0xD8, 0x0A, 0xB1, 0x01, 0xEF, 0xD3, 0x17, 0x55, 0xF5, 0x18, 0xC0,
66 | 0x3F, 0xB1, 0x9F, 0xD1, 0xEE, 0xBB, 0x9A, 0x4E, 0x30, 0xB9, 0xF2, 0x6C, 0x13, 0xED, 0x3D, 0x47,
67 | 0xF5, 0x04, 0xBE, 0xCB, 0xAF, 0x86, 0x16, 0xEC, 0x21, 0x2D, 0xAA, 0x56, 0xDB, 0x1E, 0xFC, 0x5D,
68 | 0x72, 0xF9, 0xBB, 0xB3, 0x96, 0x9B, 0x67, 0x24, 0x1F, 0x49, 0xBD, 0x66, 0xFB, 0xEE, 0x60, 0xC3,
69 | 0x10, 0xE3, 0x4D, 0x98, 0xA8, 0xBB, 0xB8, 0x20, 0x33, 0x03, 0x36, 0xE1, 0xF8, 0x59, 0x74, 0xD4
70 | };
71 | }
72 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Tank/CMF/ProCMF_81410.cs:
--------------------------------------------------------------------------------
1 | using static TACTLib.Core.Product.Tank.ManifestCryptoHandler;
2 | using static TACTLib.Core.Product.Tank.ContentManifestFile;
3 |
4 | namespace TACTLib.Core.Product.Tank.CMF
5 | {
6 | [ManifestCrypto(AutoDetectVersion = true, Product = TACTProduct.Overwatch)]
7 | public class ProCMF_81410 : ICMFEncryptionProc
8 | {
9 | public byte[] Key(CMFHeader header, int length)
10 | {
11 | byte[] buffer = new byte[length];
12 | uint kidx;
13 | kidx = Keytable[header.m_buildVersion & 511];
14 | for (uint i = 0; i != length; ++i)
15 | {
16 | buffer[i] = Keytable[SignedMod(kidx, 512)];
17 | kidx += 3;
18 | }
19 | return buffer;
20 | }
21 |
22 | public byte[] IV(CMFHeader header, byte[] digest, int length)
23 | {
24 | byte[] buffer = new byte[length];
25 | uint kidx;
26 | kidx = (uint)(length * header.m_buildVersion);
27 | for (int i = 0; i != length; ++i)
28 | {
29 | buffer[i] = Keytable[SignedMod(kidx, 512)];
30 | kidx -= 43;
31 | buffer[i] ^= digest[SignedMod(kidx + header.m_dataCount, SHA1_DIGESTSIZE)];
32 | }
33 | return buffer;
34 | }
35 |
36 | private static readonly byte[] Keytable =
37 | {
38 | 0xB3, 0x59, 0x6F, 0x0B, 0xE5, 0xA9, 0x6C, 0x21, 0xCD, 0x91, 0x44, 0x6E, 0x60, 0xBB, 0x13, 0xA0,
39 | 0x74, 0x25, 0xEC, 0x52, 0x04, 0xBD, 0xA3, 0x61, 0x8B, 0x95, 0x29, 0xFE, 0x91, 0x5C, 0x31, 0x2A,
40 | 0xA8, 0xCC, 0xEA, 0x5E, 0x46, 0x5D, 0xD1, 0xD9, 0xE6, 0x77, 0x11, 0xEB, 0x7D, 0x94, 0xF3, 0x2C,
41 | 0xD0, 0xA8, 0x66, 0x23, 0xEB, 0xC1, 0x96, 0x0F, 0xA2, 0xEB, 0x83, 0x21, 0x56, 0x30, 0x80, 0x8B,
42 | 0xEB, 0xEB, 0x1F, 0xA5, 0xC9, 0x6D, 0x89, 0x19, 0xA1, 0xFC, 0x8A, 0x09, 0x24, 0xA5, 0x47, 0xC3,
43 | 0x65, 0x30, 0x59, 0xB1, 0x30, 0x64, 0x02, 0xB8, 0xD9, 0x32, 0x2F, 0x6B, 0x56, 0x39, 0xB9, 0x08,
44 | 0xF5, 0x8F, 0x90, 0xFC, 0x69, 0x1E, 0x55, 0x1A, 0x89, 0x19, 0x50, 0xAD, 0xD5, 0xC2, 0x3B, 0xFB,
45 | 0xC1, 0xFF, 0xA7, 0xBF, 0xFF, 0x93, 0x95, 0x44, 0xE3, 0x82, 0x28, 0x8B, 0x86, 0x3D, 0x45, 0x31,
46 | 0x71, 0xFC, 0xC2, 0x43, 0x5A, 0xA2, 0x3E, 0x30, 0x1B, 0xA1, 0x35, 0xA4, 0x6F, 0x62, 0x2A, 0x96,
47 | 0x6D, 0xE8, 0xA0, 0x6D, 0x06, 0xF3, 0xB0, 0xA0, 0xFA, 0x53, 0x85, 0x95, 0x53, 0x84, 0xCE, 0x74,
48 | 0x61, 0xA7, 0x0D, 0x29, 0x63, 0x90, 0x39, 0x59, 0x48, 0x32, 0x07, 0x57, 0x1A, 0x33, 0x73, 0x91,
49 | 0x0B, 0x9F, 0x3D, 0x2C, 0xE0, 0xF2, 0x22, 0x78, 0x88, 0x6E, 0x36, 0x12, 0x4F, 0x4B, 0x4C, 0x7F,
50 | 0x58, 0x0C, 0xAD, 0x3B, 0xB5, 0x57, 0x90, 0x76, 0x36, 0x1C, 0x1A, 0x09, 0xA7, 0x2A, 0xDD, 0xE7,
51 | 0xE3, 0x2D, 0x97, 0xFC, 0xA0, 0x04, 0x99, 0xC1, 0xA6, 0xA6, 0xB2, 0x61, 0xF4, 0x67, 0xDF, 0x6E,
52 | 0x02, 0x7B, 0xB1, 0xC8, 0xF9, 0x76, 0x72, 0x1D, 0xC9, 0xA7, 0xBA, 0x57, 0x45, 0xBC, 0x56, 0xFF,
53 | 0x6B, 0xC8, 0x67, 0xFA, 0x26, 0x44, 0x72, 0x3A, 0x9D, 0xD9, 0x12, 0x36, 0xBC, 0xFB, 0x8D, 0x59,
54 | 0x0F, 0x45, 0x88, 0x71, 0x23, 0x4B, 0x60, 0x88, 0x1F, 0x93, 0x80, 0xD7, 0x55, 0x2E, 0x0D, 0x81,
55 | 0xC8, 0x56, 0x4A, 0x93, 0x1C, 0xE7, 0x85, 0x41, 0x36, 0x37, 0xC7, 0xD5, 0xB0, 0x9F, 0x1C, 0xF3,
56 | 0x67, 0x0C, 0xD7, 0x3E, 0x18, 0x9F, 0xB9, 0xB9, 0x3E, 0x54, 0x03, 0x4D, 0x23, 0xE9, 0x19, 0x18,
57 | 0xF9, 0x59, 0x8E, 0x42, 0x38, 0x89, 0xDC, 0xD7, 0x66, 0xB1, 0x2F, 0x23, 0x7D, 0x6C, 0x36, 0xA6,
58 | 0x04, 0x19, 0x75, 0xB8, 0xD5, 0xB9, 0x45, 0x5D, 0x36, 0xA5, 0xA4, 0x80, 0x96, 0x27, 0xDB, 0x14,
59 | 0xBC, 0xFF, 0x28, 0x6B, 0xE8, 0x6E, 0xD9, 0xC3, 0xCE, 0x85, 0x42, 0x0F, 0xEB, 0x05, 0x47, 0x0E,
60 | 0xE7, 0x37, 0x76, 0x04, 0x69, 0xB5, 0x4C, 0xDC, 0xA1, 0x76, 0x3E, 0x31, 0x9F, 0x5D, 0xCB, 0x7F,
61 | 0x51, 0xD9, 0x17, 0x22, 0xD3, 0x9E, 0xA0, 0x9A, 0xE6, 0x2B, 0xF1, 0x76, 0x85, 0xE4, 0x20, 0x7F,
62 | 0xCC, 0xF0, 0x2D, 0x48, 0xB9, 0x50, 0x33, 0x93, 0x16, 0x58, 0xBF, 0x62, 0xBC, 0xF9, 0xBF, 0x04,
63 | 0xDE, 0x53, 0xA9, 0x8F, 0xA9, 0x27, 0xFE, 0x00, 0x03, 0xDC, 0x43, 0xAA, 0x1D, 0xDF, 0x75, 0x6A,
64 | 0x56, 0xB5, 0xC1, 0x46, 0xF5, 0x3D, 0x31, 0x2B, 0x97, 0x29, 0x64, 0x62, 0x14, 0xCF, 0xCE, 0x76,
65 | 0xCC, 0xF8, 0x39, 0x2F, 0x07, 0x62, 0x9F, 0xCC, 0x4F, 0xC7, 0x13, 0x86, 0x60, 0x8F, 0x3C, 0xBE,
66 | 0x6F, 0xEA, 0x69, 0xA9, 0x2A, 0xDD, 0x26, 0xAA, 0x9C, 0x1D, 0x6E, 0xCE, 0x86, 0x1D, 0x91, 0x16,
67 | 0x8C, 0x50, 0xD4, 0x4F, 0x4F, 0x84, 0x7B, 0x5B, 0xE4, 0x34, 0x02, 0x41, 0x3B, 0xBE, 0x71, 0x0E,
68 | 0x31, 0xC6, 0x9A, 0xC1, 0xCB, 0x4D, 0xDE, 0x1D, 0xC8, 0x3E, 0x5C, 0x9E, 0xBC, 0x21, 0x16, 0x06,
69 | 0x38, 0xB1, 0x42, 0x70, 0xFC, 0x30, 0xBF, 0xF0, 0x02, 0xB5, 0x2C, 0x3E, 0xC8, 0x02, 0x21, 0x53
70 | };
71 | }
72 | }
--------------------------------------------------------------------------------
/TACTLib/Core/Product/Tank/CMF/ProCMF_62065.cs:
--------------------------------------------------------------------------------
1 | using static TACTLib.Core.Product.Tank.ManifestCryptoHandler;
2 | using static TACTLib.Core.Product.Tank.ContentManifestFile;
3 |
4 | namespace TACTLib.Core.Product.Tank.CMF
5 | {
6 | [ManifestCrypto(AutoDetectVersion = true, Product = TACTProduct.Overwatch)]
7 | public class ProCMF_62065 : ICMFEncryptionProc
8 | {
9 | public byte[] Key(CMFHeader header, int length)
10 | {
11 | byte[] buffer = new byte[length];
12 | uint kidx = Keytable[header.m_buildVersion & 511];
13 | for (uint i = 0; i != length; ++i)
14 | {
15 | buffer[i] = Keytable[SignedMod(kidx, 512)];
16 | kidx = header.m_buildVersion - kidx;
17 | }
18 |
19 | return buffer;
20 | }
21 |
22 | public byte[] IV(CMFHeader header, byte[] digest, int length)
23 | {
24 | byte[] buffer = new byte[length];
25 | uint kidx = Keytable[header.m_buildVersion & 511];
26 |
27 | for (int i = 0; i != length; ++i)
28 | {
29 | buffer[i] = Keytable[SignedMod(kidx, 512)];
30 | kidx = header.m_buildVersion - kidx;
31 | buffer[i] ^= digest[SignedMod(kidx + i, SHA1_DIGESTSIZE)];
32 | }
33 |
34 | return buffer;
35 | }
36 |
37 | private static readonly byte[] Keytable =
38 | {
39 | 0x6F, 0x7F, 0xDF, 0xF0, 0x90, 0x83, 0x31, 0x29, 0xDF, 0x8B, 0x67, 0x77, 0xE1, 0x2B, 0xFE, 0xDF,
40 | 0x13, 0xFF, 0x06, 0x52, 0xBC, 0x43, 0x61, 0x26, 0xB1, 0xC0, 0x5B, 0x0D, 0xD0, 0x7A, 0x09, 0x01,
41 | 0x66, 0xB5, 0x22, 0xF0, 0x9D, 0x2B, 0x6B, 0x87, 0xF7, 0x56, 0x5F, 0x6E, 0x3B, 0xBB, 0x59, 0x87,
42 | 0x04, 0xEA, 0x10, 0xE8, 0x90, 0x30, 0x36, 0xDF, 0xB4, 0x47, 0x98, 0x19, 0xE1, 0x12, 0x5D, 0x54,
43 | 0x62, 0xE2, 0x52, 0x3B, 0xC8, 0x7D, 0x1E, 0x58, 0x0E, 0xC7, 0x5C, 0xE2, 0xF3, 0x19, 0x92, 0xFB,
44 | 0x86, 0xD7, 0xFB, 0xF1, 0xA6, 0x42, 0x7B, 0xD0, 0x98, 0x02, 0xDB, 0xC4, 0x88, 0x36, 0xD3, 0x89,
45 | 0x96, 0xE6, 0x40, 0x46, 0x16, 0x64, 0x98, 0xF2, 0x5E, 0xE8, 0x60, 0x55, 0x76, 0x90, 0x6D, 0xF5,
46 | 0x9B, 0x6B, 0xD3, 0xA5, 0x80, 0x59, 0x08, 0x76, 0x5D, 0x59, 0xCA, 0xC7, 0x70, 0x9E, 0xDB, 0x8C,
47 | 0x0A, 0x31, 0x75, 0x97, 0x81, 0x3A, 0xAF, 0x74, 0x42, 0x2E, 0xD8, 0x11, 0x4C, 0xC9, 0xA1, 0xC6,
48 | 0x1A, 0x1C, 0x40, 0xDB, 0xA9, 0xA5, 0xA8, 0x9D, 0xF2, 0xCB, 0x9E, 0x79, 0xAD, 0x82, 0x3B, 0x46,
49 | 0xF6, 0xD2, 0x34, 0xC0, 0x3B, 0x14, 0x7D, 0xAD, 0x2D, 0x52, 0x31, 0x1B, 0xCC, 0x0A, 0xE1, 0x23,
50 | 0xD3, 0xBE, 0x3B, 0x6D, 0x1A, 0xE4, 0x03, 0xDE, 0x8C, 0xDA, 0x13, 0x56, 0xF6, 0x89, 0x70, 0x6D,
51 | 0x1E, 0xA6, 0xC8, 0xA0, 0xF0, 0xBE, 0x59, 0xA7, 0xEB, 0x27, 0x0C, 0x12, 0x28, 0xBF, 0xE2, 0x96,
52 | 0x23, 0x07, 0x50, 0xCB, 0xFF, 0x5F, 0x69, 0x4D, 0xA7, 0x3C, 0x86, 0x54, 0x41, 0x06, 0x7E, 0x0C,
53 | 0x92, 0xA4, 0x71, 0xAA, 0x13, 0xB4, 0x0B, 0x2D, 0x0B, 0xC2, 0xA4, 0x83, 0x55, 0x92, 0x1F, 0x44,
54 | 0x5D, 0xBD, 0x82, 0x6E, 0x4E, 0xB6, 0xE7, 0xED, 0x09, 0x8C, 0x19, 0x6A, 0xAD, 0x70, 0x51, 0x29,
55 | 0x44, 0xB6, 0xF8, 0xBC, 0x08, 0x33, 0x95, 0x84, 0xB5, 0x19, 0x44, 0xB6, 0x52, 0x40, 0xA4, 0xC9,
56 | 0x8B, 0xB7, 0xF8, 0x72, 0x9A, 0x71, 0xB1, 0x0A, 0xB0, 0xB5, 0x25, 0x7E, 0x4B, 0xFD, 0x28, 0x0C,
57 | 0xA4, 0xA7, 0xE8, 0x77, 0x57, 0x22, 0xF2, 0xFA, 0x8E, 0x38, 0x0E, 0xA7, 0xDF, 0x52, 0xA0, 0x64,
58 | 0xAA, 0xCC, 0xFD, 0xCB, 0x31, 0x13, 0x5B, 0x51, 0xAD, 0xCB, 0x23, 0x2B, 0xBC, 0xBF, 0x5B, 0x35,
59 | 0xE8, 0x25, 0xE6, 0x3F, 0x82, 0x0A, 0xC2, 0x6A, 0x84, 0x68, 0xC3, 0xC4, 0xFC, 0x46, 0x2E, 0xA3,
60 | 0x8B, 0x92, 0x1C, 0xAF, 0x5C, 0xCC, 0x7B, 0x21, 0x20, 0xF8, 0x4F, 0x75, 0x27, 0xBB, 0xC8, 0x1B,
61 | 0xE4, 0x79, 0x87, 0xF4, 0x6E, 0x00, 0xC4, 0xE9, 0x4E, 0xC3, 0x5A, 0x9D, 0x56, 0x10, 0x8D, 0xD6,
62 | 0x24, 0xFC, 0xDE, 0x3C, 0x23, 0x74, 0x1F, 0xD9, 0x6C, 0x92, 0xBE, 0x45, 0x22, 0x85, 0xE1, 0xF7,
63 | 0xCC, 0x36, 0x10, 0x68, 0xA1, 0x33, 0x2E, 0x7E, 0xD0, 0xBE, 0xA5, 0xE7, 0x40, 0x33, 0xAF, 0xB4,
64 | 0x97, 0xA6, 0xB8, 0x58, 0xB4, 0x01, 0x82, 0x21, 0x90, 0x62, 0x33, 0x8F, 0x57, 0x29, 0xA7, 0x77,
65 | 0x1F, 0x5D, 0xAB, 0xC6, 0xE1, 0x57, 0xD9, 0x11, 0x08, 0x52, 0x4F, 0x93, 0x00, 0x34, 0x05, 0x05,
66 | 0x55, 0x1A, 0x03, 0xE7, 0x19, 0xDC, 0x75, 0x71, 0x13, 0x8A, 0x54, 0x3C, 0x04, 0x32, 0x7E, 0x56,
67 | 0x70, 0x38, 0xBE, 0x92, 0x24, 0xD0, 0x54, 0x14, 0x4F, 0x45, 0x71, 0xB3, 0xAE, 0xBE, 0x21, 0x6A,
68 | 0xA2, 0x0E, 0xEB, 0xBD, 0x8E, 0xF9, 0x99, 0x83, 0xA8, 0x39, 0xDA, 0xB9, 0x26, 0x0B, 0x4E, 0x9F,
69 | 0x89, 0x1B, 0x28, 0x77, 0x59, 0xD7, 0x80, 0x46, 0x82, 0xFB, 0xF0, 0x5D, 0x86, 0xA7, 0xE6, 0xF3,
70 | 0xE4, 0x0F, 0x8F, 0x8A, 0x98, 0x48, 0x63, 0xD7, 0x46, 0x80, 0x0B, 0xD3, 0xB9, 0xDB, 0x9A, 0xD5
71 | };
72 | }
73 | }
--------------------------------------------------------------------------------