├── 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 [![TACTLib](https://github.com/overtools/TACTLib/actions/workflows/dotnet.yml/badge.svg)](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 | } --------------------------------------------------------------------------------