├── .gitignore
├── CLI
├── CLI.csproj
├── Program.cs
└── Properties
│ ├── PublishProfiles
│ ├── FrameworkDependent.pubxml
│ └── SelfContained.pubxml
│ └── launchSettings.json
├── Core
├── BondSchema
│ ├── ApiManifest.cs
│ ├── BondAsset.cs
│ ├── BondGuid.cs
│ ├── CacheFile.cs
│ ├── CacheMap.cs
│ ├── CustomsManifest.cs
│ ├── EngineGameVariant.cs
│ ├── GameManifest.cs
│ ├── MapVariant.cs
│ └── UgcGameVariant.cs
├── Cache
│ ├── CacheManager.cs
│ └── UserCacheManager.cs
├── Constants.cs
├── Core.csproj
├── GameApi.cs
├── LuaBundle.cs
├── Oodle.cs
├── Serialization
│ ├── BondReader.cs
│ ├── BondWriter.cs
│ ├── MyBinaryReader.cs
│ ├── MyBinaryWriter.cs
│ └── SchemaSerializer.cs
├── Settings
│ ├── SettingsBase.cs
│ └── UserSettings.cs
├── Updater.cs
├── UrlHasher.cs
├── Utils
│ ├── Exceptions.cs
│ ├── ExtensionMethods.cs
│ ├── FileUtil.cs
│ ├── OrderedDictionary.cs
│ └── Util.cs
├── VariantManager.cs
├── Variants
│ └── VariantAsset.cs
├── lib
│ └── oo2core_9_win64.dll
└── resources
│ ├── endpoints-lan.json
│ ├── endpoints-offline.json
│ └── endpoints-online.json
├── Directory.Build.props
├── GUI
├── App.xaml
├── App.xaml.cs
├── AssemblyInfo.cs
├── GUI.csproj
├── Models
│ ├── LanguageModel.cs
│ └── VariantModel.cs
├── Properties
│ └── PublishProfiles
│ │ ├── FrameworkDependent.pubxml
│ │ └── SelfContained.pubxml
├── Utils
│ ├── Converters.cs
│ ├── HyperlinkExtensions.cs
│ ├── IOService.cs
│ ├── ListViewExtensions.cs
│ ├── NavigationService.cs
│ ├── NotifyPropertyChanged.cs
│ ├── RelayCommand.cs
│ ├── WindowExtensions.cs
│ └── WindowManager.cs
├── ViewModels
│ ├── AboutViewModel.cs
│ ├── ExtractViewModel.cs
│ ├── FileActionViewModel.cs
│ ├── HashUrlViewModel.cs
│ ├── MainViewModel.cs
│ ├── ResultViewModel.cs
│ ├── SettingsViewModel.cs
│ ├── UpdaterViewModel.cs
│ ├── VariantViewModel.cs
│ ├── VariantViewModelContext.cs
│ └── ViewModelBase.cs
└── Views
│ ├── AboutWindow.xaml
│ ├── AboutWindow.xaml.cs
│ ├── ErrorWindow.xaml
│ ├── ErrorWindow.xaml.cs
│ ├── ExtractWindow.xaml
│ ├── ExtractWindow.xaml.cs
│ ├── FileActionWindow.xaml
│ ├── FileActionWindow.xaml.cs
│ ├── HashUrlWindow.xaml
│ ├── HashUrlWindow.xaml.cs
│ ├── MainWindow.xaml
│ ├── MainWindow.xaml.cs
│ ├── ResultWindow.xaml
│ ├── ResultWindow.xaml.cs
│ ├── SettingsWindow.xaml
│ ├── SettingsWindow.xaml.cs
│ ├── UpdateWindow.xaml
│ ├── UpdateWindow.xaml.cs
│ ├── VariantListView.xaml
│ └── VariantListView.xaml.cs
├── InfiniteVariantTool.sln
├── LICENSE.md
├── README.md
├── Tests
├── BondFileTests.cs
├── BondSchemaTests.cs
├── CacheFileTests.cs
├── CacheManagerTests.cs
├── CacheMapTests.cs
├── HashUrlTests.cs
├── LuaBundleTests.cs
├── OodleTests.cs
├── Secrets.cs
├── TestUtil.cs
├── Tests.csproj
└── VariantManagerTests.cs
├── icon.ico
├── publish.bat
└── publish.ps1
/CLI/CLI.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6.0
6 | enable
7 | InfiniteVariantToolCLI
8 | ..\icon.ico
9 | InfiniteVariantTool.$(MSBuildProjectName.Replace(" ", "_"))
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/CLI/Properties/PublishProfiles/FrameworkDependent.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Release
5 | Any CPU
6 | ..\publish\framework_dependent
7 | FileSystem
8 | net6.0
9 | win-x64
10 | false
11 | true
12 | true
13 | true
14 |
15 |
--------------------------------------------------------------------------------
/CLI/Properties/PublishProfiles/SelfContained.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Release
5 | Any CPU
6 | ..\publish\self_contained
7 | FileSystem
8 | net6.0
9 | win-x64
10 | true
11 | true
12 | true
13 | true
14 | true
15 |
16 |
--------------------------------------------------------------------------------
/CLI/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "InfiniteVariantTool.CLI": {
4 | "commandName": "Project",
5 | "commandLineArgs": "variants list Map"
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/Core/BondSchema/ApiManifest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics.CodeAnalysis;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Text.Json.Serialization;
7 | using System.Text.RegularExpressions;
8 | using System.Threading.Tasks;
9 |
10 | namespace InfiniteVariantTool.Core.BondSchema
11 | {
12 | [Bond.Schema]
13 | public class ApiManifest
14 | {
15 | #region Schema
16 |
17 | [Bond.Id(0)]
18 | public Dictionary Authorities { get; set; }
19 | [Bond.Id(1)]
20 | public Dictionary RetryPolicies { get; set; }
21 | [Bond.Id(2)]
22 | public Dictionary Settings { get; set; }
23 | [Bond.Id(3)]
24 | public Dictionary Endpoints { get; set; }
25 |
26 | public ApiManifest()
27 | {
28 | Authorities = new();
29 | RetryPolicies = new();
30 | Settings = new();
31 | Endpoints = new();
32 | }
33 |
34 | [Bond.Schema]
35 | public class Authority
36 | {
37 | [Bond.Id(0)]
38 | public string AuthorityId { get; set; }
39 | [Bond.Id(1)]
40 | public int Scheme { get; set; }
41 | [Bond.Id(2), Bond.Type(typeof(Bond.Tag.nullable))]
42 | public string? Hostname { get; set; }
43 | [Bond.Id(3), Bond.Type(typeof(Bond.Tag.nullable))]
44 | public ushort? Port { get; set; }
45 | [Bond.Id(4)]
46 | public HashSet AuthenticationMethods { get; set; }
47 |
48 | public Authority()
49 | {
50 | AuthorityId = "";
51 | AuthenticationMethods = new();
52 | }
53 |
54 | // copy constructor
55 | public Authority(Authority other)
56 | {
57 | AuthorityId = other.AuthorityId;
58 | Scheme = other.Scheme;
59 | Hostname = other.Hostname;
60 | Port = other.Port;
61 | AuthenticationMethods = new(other.AuthenticationMethods);
62 | }
63 | }
64 |
65 | [Bond.Schema]
66 | public class RetryPolicy
67 | {
68 | [Bond.Id(0)]
69 | public string RetryPolicyId { get; set; }
70 | [Bond.Id(1)]
71 | public uint TimeoutMs { get; set; }
72 | [Bond.Id(2), Bond.Type(typeof(Bond.Tag.nullable))]
73 | public RetryOptions? RetryOptions { get; set; }
74 |
75 | public RetryPolicy()
76 | {
77 | RetryPolicyId = "";
78 | }
79 | }
80 |
81 | [Bond.Schema]
82 | public class RetryOptions
83 | {
84 | [Bond.Id(0)]
85 | public byte MaxRetryCount { get; set; }
86 | [Bond.Id(1)]
87 | public uint RetryDelayMs { get; set; }
88 | [Bond.Id(2)]
89 | public float RetryGrowth { get; set; }
90 | [Bond.Id(3)]
91 | public uint RetryJitterMs { get; set; }
92 | [Bond.Id(4)]
93 | public bool RetryIfNotFound { get; set; }
94 |
95 | public RetryOptions()
96 | {
97 | MaxRetryCount = 3;
98 | RetryDelayMs = 2000;
99 | RetryGrowth = 2;
100 | RetryJitterMs = 150;
101 | }
102 | }
103 |
104 | [Bond.Schema]
105 | public class Endpoint
106 | {
107 | [Bond.Id(0)]
108 | public string AuthorityId { get; set; }
109 | [Bond.Id(1)]
110 | public string Path { get; set; }
111 | [Bond.Id(2), Bond.Type(typeof(Bond.Tag.nullable))]
112 | public string? QueryString { get; set; }
113 | [Bond.Id(3)]
114 | public string RetryPolicyId { get; set; }
115 | [Bond.Id(4)]
116 | public string TopicName { get; set; }
117 | [Bond.Id(5)]
118 | public int AcknowledgementTypeId { get; set; }
119 | [Bond.Id(6)]
120 | public bool AuthenticationLifetimeExtensionSupported { get; set; }
121 | [Bond.Id(7)]
122 | public bool ClearanceAware { get; set; }
123 |
124 | public Endpoint()
125 | {
126 | AuthorityId = "";
127 | Path = "";
128 | RetryPolicyId = "";
129 | TopicName = "";
130 | EndpointId = "";
131 | }
132 |
133 | // copy constructor
134 | public Endpoint(Endpoint other)
135 | {
136 | AuthorityId = other.AuthorityId;
137 | Path = other.Path;
138 | QueryString = other.QueryString;
139 | RetryPolicyId = other.RetryPolicyId;
140 | TopicName = other.TopicName;
141 | AcknowledgementTypeId = other.AcknowledgementTypeId;
142 | AuthenticationLifetimeExtensionSupported = other.AuthenticationLifetimeExtensionSupported;
143 | ClearanceAware = other.ClearanceAware;
144 | EndpointId = other.EndpointId;
145 | }
146 |
147 | #region MyExtensions
148 |
149 | [JsonIgnore]
150 | public string EndpointId { get; set; }
151 |
152 | #endregion
153 | }
154 | #endregion
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/Core/BondSchema/BondGuid.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Buffers.Binary;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Text.Json;
7 | using System.Text.Json.Serialization;
8 | using System.Threading.Tasks;
9 |
10 | namespace InfiniteVariantTool.Core.BondSchema
11 | {
12 | [Bond.Schema]
13 | public class BondGuid
14 | {
15 | #region Schema
16 |
17 | [Bond.Id(0)]
18 | public uint Data1 { get; set; }
19 |
20 | [Bond.Id(1)]
21 | public ushort Data2 { get; set; }
22 |
23 | [Bond.Id(2)]
24 | public ushort Data3 { get; set; }
25 |
26 | [Bond.Id(3)]
27 | public ulong Data4 { get; set; }
28 |
29 | public BondGuid()
30 | {
31 |
32 | }
33 |
34 | // copy constructor
35 | public BondGuid(BondGuid other)
36 | {
37 | Data1 = other.Data1;
38 | Data2 = other.Data2;
39 | Data3 = other.Data3;
40 | Data4 = other.Data4;
41 | }
42 |
43 | #endregion
44 |
45 | #region MyExtensions
46 |
47 | public BondGuid(Guid guid)
48 | {
49 | byte[] bytes = guid.ToByteArray();
50 | Data1 = BitConverter.ToUInt32(bytes, 0);
51 | Data2 = BitConverter.ToUInt16(bytes, 4);
52 | Data3 = BitConverter.ToUInt16(bytes, 6);
53 | Data4 = BitConverter.ToUInt64(bytes, 8);
54 | }
55 |
56 | public BondGuid(string guid) : this(Guid.Parse(guid))
57 | {
58 |
59 | }
60 |
61 | public override string ToString()
62 | {
63 | byte[] bytes = new byte[8];
64 | BinaryPrimitives.WriteUInt64LittleEndian(bytes, Data4);
65 | ulong data5 = BinaryPrimitives.ReadUInt64BigEndian(bytes);
66 | ushort data4 = (ushort)(data5 >> 48);
67 | data5 &= 0xffffffffffff;
68 | return $"{Data1:x08}-{Data2:x04}-{Data3:x04}-{data4:x04}-{data5:x012}";
69 | }
70 |
71 | public override bool Equals(object? obj)
72 | {
73 | return obj is BondGuid other
74 | && Data1 == other.Data1
75 | && Data2 == other.Data2
76 | && Data3 == other.Data3
77 | && Data4 == other.Data4;
78 | }
79 |
80 | public override int GetHashCode()
81 | {
82 | return (int)Data1 ^ Data2 ^ (Data3 << 16) ^ (int)(Data4 & 0xffffffff) ^ (int)(Data4 >> 32);
83 | }
84 |
85 | public static explicit operator Guid(BondGuid bondGuid) => Guid.Parse(bondGuid.ToString());
86 | public static explicit operator BondGuid(Guid guid) => new(guid);
87 |
88 | #endregion
89 | }
90 |
91 | public class BondGuidJsonConverter : JsonConverter
92 | {
93 | public override BondGuid Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
94 | {
95 | return new BondGuid(reader.GetString()!);
96 | }
97 |
98 | public override void Write(Utf8JsonWriter writer, BondGuid bondGuid, JsonSerializerOptions options)
99 | {
100 | writer.WriteStringValue(bondGuid.ToString());
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/Core/BondSchema/CacheFile.cs:
--------------------------------------------------------------------------------
1 | using Bond.IO.Safe;
2 | using Bond.Protocols;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace InfiniteVariantTool.Core.BondSchema
11 | {
12 | [Bond.Schema]
13 | public class CacheFile
14 | {
15 | #region Schema
16 |
17 | [Bond.Id(1), Bond.Type(typeof(Bond.Tag.nullable))]
18 | public CacheMap.Metadata? Metadata { get; set; }
19 | [Bond.Id(2)]
20 | public sbyte[] Data { get; set; }
21 |
22 | public CacheFile()
23 | {
24 | Data = Array.Empty();
25 | }
26 |
27 | #endregion
28 |
29 | #region MyExtensions
30 |
31 | public byte[] UData => (byte[])(Array)Data;
32 |
33 | #endregion
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Core/BondSchema/CacheMap.cs:
--------------------------------------------------------------------------------
1 | using Bond.IO.Safe;
2 | using Bond.Protocols;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace InfiniteVariantTool.Core.BondSchema
11 | {
12 | [Bond.Schema]
13 | public class CacheMap
14 | {
15 | #region Schema
16 |
17 | [Bond.Id(0)]
18 | public Dictionary Entries { get; set; }
19 | [Bond.Id(1)]
20 | public sbyte Unk { get; set; }
21 | [Bond.Id(2)]
22 | public string Language { get; set; }
23 |
24 | public CacheMap()
25 | {
26 | Entries = new();
27 | Language = "";
28 | }
29 |
30 | [Bond.Schema]
31 | public class Entry
32 | {
33 | [Bond.Id(0)]
34 | public long CreateTime { get; set; }
35 | [Bond.Id(1)]
36 | public long AccessTime { get; set; }
37 | [Bond.Id(2)]
38 | public long WriteTime { get; set; }
39 | [Bond.Id(3)]
40 | public Metadata Metadata { get; set; }
41 | [Bond.Id(4)]
42 | public ulong Size { get; set; }
43 |
44 | public Entry()
45 | {
46 | Metadata = new();
47 | }
48 | }
49 |
50 | [Bond.Schema]
51 | public class Metadata
52 | {
53 | [Bond.Id(0)]
54 | public string Etag { get; set; }
55 | [Bond.Id(1)]
56 | public ulong Timestamp { get; set; }
57 | [Bond.Id(2)]
58 | public Dictionary Headers { get; set; }
59 | [Bond.Id(3)]
60 | public BondGuid Guid { get; set; }
61 | [Bond.Id(4)]
62 | public string Url { get; set; }
63 |
64 | public Metadata()
65 | {
66 | Etag = "";
67 | Headers = new();
68 | Guid = new();
69 | Url = "";
70 | }
71 | }
72 |
73 | #endregion
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/Core/BondSchema/CustomsManifest.cs:
--------------------------------------------------------------------------------
1 | using InfiniteVariantTool.Core.Variants;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace InfiniteVariantTool.Core.BondSchema
9 | {
10 | [Bond.Schema]
11 | public class CustomsManifest : BondAsset
12 | {
13 | #region Schema
14 |
15 | [Bond.Id(0)]
16 | public CustomData_ CustomData { get; set; }
17 | [Bond.Id(1)]
18 | public List MapLinks { get; set; }
19 | [Bond.Id(2)]
20 | public List PlaylistLinks { get; set; }
21 | [Bond.Id(3)]
22 | public List PrefabLinks { get; set; }
23 | [Bond.Id(4)]
24 | public List UgcGameVariantLinks { get; set; }
25 | [Bond.Id(5)]
26 | public List MapModePairLinks { get; set; }
27 | [Bond.Id(6), Bond.Type(typeof(List))]
28 | public List Tags { get; set; }
29 |
30 | public CustomsManifest()
31 | {
32 | CustomData = new();
33 | MapLinks = new();
34 | PlaylistLinks = new();
35 | PrefabLinks = new();
36 | UgcGameVariantLinks = new();
37 | MapModePairLinks = new();
38 | Tags = new();
39 | }
40 |
41 | [Bond.Schema]
42 | public class CustomData_
43 | {
44 | [Bond.Id(0)]
45 | public int Intent { get; set; }
46 | }
47 |
48 | #endregion
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Core/BondSchema/EngineGameVariant.cs:
--------------------------------------------------------------------------------
1 | using InfiniteVariantTool.Core.Variants;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace InfiniteVariantTool.Core.BondSchema
9 | {
10 | [Bond.Schema, Variant]
11 | public class EngineGameVariant : BondAsset
12 | {
13 | #region Schema
14 |
15 | [Bond.Id(0)]
16 | public CustomData_ CustomData { get; set; }
17 | [Bond.Id(1), Bond.Type(typeof(List))]
18 | public List Tags { get; set; }
19 |
20 | public EngineGameVariant()
21 | {
22 | CustomData = new();
23 | Tags = new();
24 | }
25 |
26 | // partial copy constructor
27 | public EngineGameVariant(BondAsset other) : base(other)
28 | {
29 | CustomData = new();
30 | Tags = new();
31 | }
32 |
33 | [Bond.Schema]
34 | public class CustomData_
35 | {
36 | [Bond.Id(0)]
37 | public SubsetData_ SubsetData { get; set; }
38 | [Bond.Id(1)]
39 | public Dictionary LocalizedData { get; set; }
40 |
41 | public CustomData_()
42 | {
43 | SubsetData = new();
44 | LocalizedData = new();
45 | }
46 | }
47 |
48 | [Bond.Schema]
49 | public class SubsetData_
50 | {
51 | [Bond.Id(0)]
52 | public int StatBucketGameType { get; set; }
53 | [Bond.Id(1)]
54 | public string EngineName { get; set; }
55 | [Bond.Id(2)]
56 | public string VariantName { get; set; }
57 |
58 | public SubsetData_()
59 | {
60 | EngineName = "";
61 | VariantName = "";
62 | }
63 | }
64 |
65 | #endregion
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Core/BondSchema/GameManifest.cs:
--------------------------------------------------------------------------------
1 | using InfiniteVariantTool.Core.Cache;
2 | using InfiniteVariantTool.Core.Variants;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace InfiniteVariantTool.Core.BondSchema
10 | {
11 | [Bond.Schema]
12 | public class GameManifest : BondAsset
13 | {
14 | #region Schema
15 |
16 | [Bond.Id(0)]
17 | public CustomData_ CustomData { get; set; }
18 | [Bond.Id(1), Bond.Type(typeof(List))]
19 | public List Tags { get; set; }
20 | [Bond.Id(2)]
21 | public List MapLinks { get; set; }
22 | [Bond.Id(3)]
23 | public List UgcGameVariantLinks { get; set; }
24 | [Bond.Id(4)]
25 | public List PlaylistLinks { get; set; }
26 | [Bond.Id(5)]
27 | public List EngineGameVariantLinks { get; set; }
28 |
29 | public GameManifest()
30 | {
31 | CustomData = new();
32 | Tags = new();
33 | MapLinks = new();
34 | UgcGameVariantLinks = new();
35 | PlaylistLinks = new();
36 | EngineGameVariantLinks = new();
37 | }
38 |
39 | [Bond.Schema]
40 | public class CustomData_
41 | {
42 | [Bond.Id(0)]
43 | public string BranchName { get; set; }
44 | [Bond.Id(1)]
45 | public string BuildNumber { get; set; }
46 | [Bond.Id(2)]
47 | public int Kind { get; set; }
48 | [Bond.Id(3)]
49 | public string ContentVersion { get; set; }
50 | [Bond.Id(4)]
51 | public BondGuid BuildGuid { get; set; }
52 | [Bond.Id(5)]
53 | public int Visibility { get; set; }
54 |
55 | public CustomData_()
56 | {
57 | BranchName = "";
58 | BuildNumber = "";
59 | ContentVersion = "";
60 | BuildGuid = new();
61 | }
62 | }
63 |
64 | #endregion
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/Core/BondSchema/MapVariant.cs:
--------------------------------------------------------------------------------
1 | using InfiniteVariantTool.Core.Variants;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace InfiniteVariantTool.Core.BondSchema
9 | {
10 | [Bond.Schema, Variant]
11 | public class MapVariant : BondAsset
12 | {
13 | [Bond.Id(0)]
14 | public CustomData_ CustomData { get; set; }
15 | [Bond.Id(1), Bond.Type(typeof(List))]
16 | public List Tags { get; set; }
17 |
18 | public MapVariant()
19 | {
20 | CustomData = new();
21 | Tags = new();
22 | }
23 |
24 | // partial copy constructor
25 | public MapVariant(BondAsset other) : base(other)
26 | {
27 | CustomData = new();
28 | Tags = new();
29 | }
30 |
31 | [Bond.Schema]
32 | public class CustomData_
33 | {
34 | [Bond.Id(0)]
35 | public int NumOfObjectsOnMap { get; set; }
36 | [Bond.Id(1)]
37 | public int TagLevelId { get; set; }
38 | [Bond.Id(2)]
39 | public bool IsBaked { get; set; }
40 | [Bond.Id(3)]
41 | public bool HasNodeGraph { get; set; }
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Core/BondSchema/UgcGameVariant.cs:
--------------------------------------------------------------------------------
1 | using InfiniteVariantTool.Core.Variants;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace InfiniteVariantTool.Core.BondSchema
9 | {
10 | [Bond.Schema, Variant]
11 | public class UgcGameVariant : BondAsset
12 | {
13 | #region schema
14 |
15 | [Bond.Id(0)]
16 | public CustomData_ CustomData { get; set; }
17 | [Bond.Id(1), Bond.Type(typeof(List))]
18 | public List Tags { get; set; }
19 | [Bond.Id(2), Bond.Type(typeof(Bond.Tag.nullable))]
20 | public BondAsset? EngineGameVariantLink { get; set; }
21 |
22 | public UgcGameVariant()
23 | {
24 | CustomData = new();
25 | Tags = new();
26 | }
27 |
28 | // partial copy constructor
29 | public UgcGameVariant(BondAsset other) : base(other)
30 | {
31 | CustomData = new();
32 | Tags = new();
33 | }
34 |
35 | [Bond.Schema]
36 | public class CustomData_
37 | {
38 | [Bond.Id(0)]
39 | public Dictionary KeyValues { get; set; }
40 |
41 | public CustomData_()
42 | {
43 | KeyValues = new();
44 | }
45 | }
46 |
47 | #endregion
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Core/Cache/UserCacheManager.cs:
--------------------------------------------------------------------------------
1 | using InfiniteVariantTool.Core.Variants;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using System.Xml.Linq;
9 |
10 | namespace InfiniteVariantTool.Core.Cache
11 | {
12 | public class UserCacheManager
13 | {
14 | public List Entries { get; private set; }
15 | private string cacheDirectory;
16 |
17 | public UserCacheManager(string cacheDirectory)
18 | {
19 | this.cacheDirectory = cacheDirectory;
20 | Entries = new();
21 | }
22 |
23 | public async Task LoadEntries()
24 | {
25 | Entries.Clear();
26 | foreach (string filePath in VariantAsset.FindVariants(cacheDirectory))
27 | {
28 | Entries.Add(await VariantAsset.Load(filePath, false));
29 | }
30 | }
31 |
32 | public void RemoveVariant(string variantFilePath)
33 | {
34 | Entries.RemoveAll(entry => entry.FilePath == variantFilePath);
35 |
36 | string variantDirectory = Path.GetDirectoryName(variantFilePath)!;
37 | if (Directory.Exists(variantDirectory))
38 | {
39 | Directory.Delete(variantDirectory, true);
40 | }
41 |
42 | // remove empty parent directories
43 | string currentPath = Path.GetFullPath(Path.GetDirectoryName(variantDirectory)!);
44 | string basePath = Path.GetFullPath(cacheDirectory);
45 | while (!currentPath.Equals(basePath, StringComparison.InvariantCultureIgnoreCase) && Directory.Exists(currentPath)
46 | && !Directory.EnumerateFileSystemEntries(currentPath).Any())
47 | {
48 | Directory.Delete(currentPath);
49 | currentPath = Path.GetDirectoryName(currentPath)!;
50 | }
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Core/Constants.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 |
4 | namespace InfiniteVariantTool.Core
5 | {
6 | public static class Constants
7 | {
8 | public static readonly string GameExeName = "HaloInfinite.exe";
9 | public static readonly string OfflineCacheDirectory = Path.Combine("package", "pc");
10 | public static readonly string OnlineCacheDirectory = "disk_cache";
11 | public static readonly string LanCacheDirectory = "server_disk_cache";
12 | public static readonly string CmsDirectory = "__cms__";
13 | public static readonly byte[] pngSignature = new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
14 | public static readonly byte[] jpgSignature = new byte[] { 0xff, 0xd8, 0xff };
15 | public static readonly string AppName = "InfiniteVariantTool";
16 | }
17 |
18 | public enum Game
19 | {
20 | HaloInfinite,
21 | Halo5
22 | }
23 |
24 | public class MimeType
25 | {
26 | public readonly string Value;
27 | private MimeType(string value)
28 | {
29 | Value = value;
30 | }
31 |
32 | public static readonly MimeType OctetStream = new("application/octet-stream");
33 | public static readonly MimeType Bond = new("application/x-bond-compact-binary");
34 | public static readonly MimeType Json = new("application/json");
35 | }
36 |
37 | public class Language
38 | {
39 | public readonly string ShortCode;
40 | public readonly string Code;
41 | public readonly string Name;
42 | private Language(string shortCode, string code, string name)
43 | {
44 | ShortCode = shortCode;
45 | Code = code;
46 | Name = name;
47 | }
48 |
49 | private Language(string shortCode)
50 | {
51 | ShortCode = shortCode;
52 | Code = "";
53 | Name = "";
54 | }
55 |
56 | public static Language? TryFromCode(string code)
57 | {
58 | if (code == "")
59 | {
60 | return null;
61 | }
62 | return Languages.Find(lang => lang.Code == code);
63 | }
64 |
65 | public static Language FromCode(string code)
66 | {
67 | return TryFromCode(code) ?? throw new KeyNotFoundException();
68 | }
69 |
70 | public static Language FromShortCode(string shortCode)
71 | {
72 | return Languages.Find(lang => lang.ShortCode == shortCode) ?? throw new KeyNotFoundException();
73 | }
74 |
75 | public static readonly Language En = new("en", "en-US", "English");
76 | public static readonly Language Jpn = new("jpn", "ja-JP", "Japanese");
77 | public static readonly Language De = new("de", "de-DE", "German");
78 | public static readonly Language Fr = new("fr", "fr-FR", "French");
79 | public static readonly Language Sp = new("sp", "es-ES", "Spanish");
80 | public static readonly Language Mx = new("mx", "es-MX", "Spanish (Mexico)");
81 | public static readonly Language It = new("it", "it-IT", "Italian");
82 | public static readonly Language Kor = new("kor", "ko-KR", "Korean");
83 | public static readonly Language Cht = new("cht", "zh-TW", "Chinese (Traditional)");
84 | public static readonly Language Chs = new("chs", "zh-CN", "Chinese (Simplified)");
85 | public static readonly Language Pl = new("pl", "pl-PL", "Polish");
86 | public static readonly Language Ru = new("ru", "ru-RU", "Russian");
87 | public static readonly Language Nl = new("nl", "nl-NL", "Dutch");
88 | public static readonly Language Br = new("br", "pt-BR", "Portuguese (Brazil)");
89 | public static readonly Language Dk = new("dk");
90 | public static readonly Language Fi = new("fi");
91 | public static readonly Language No = new("no");
92 | public static readonly Language Pt = new("pt");
93 | public static readonly List Languages = new() { En, Jpn, De, Fr, Sp, Mx, It, Kor, Cht, Chs, Pl, Ru, Nl, Br, Dk, Fi, No, Pt };
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/Core/Core.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | enable
6 | InfiniteVariantTool.$(MSBuildProjectName.Replace(" ", "_"))
7 | InfiniteVariantToolCore
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | PreserveNewest
20 | %(Filename)%(Extension)
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/Core/Oodle.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 | using System.Runtime.InteropServices;
4 |
5 | namespace InfiniteVariantTool.Core
6 | {
7 | public static class Oodle
8 | {
9 | public const string DllName = "oo2core_9_win64.dll";
10 |
11 | [DllImport(DllName)]
12 | public static extern long OodleLZ_Decompress(
13 | byte[] compBuf,
14 | long compBufSize,
15 | byte[] rawBuf,
16 | long rawLen,
17 | bool fuzzSafe = true,
18 | bool checkCRC = false,
19 | OodleLZ_Verbosity verbosity = OodleLZ_Verbosity.None,
20 | IntPtr decBufBase = default,
21 | long decBufSize = 0,
22 | OodleDecompressCallback? fpCallback = null,
23 | object? callbackUserData = null,
24 | byte[]? decoderMemory = null,
25 | long decoderMemorySize = 0,
26 | OodleLZ_Decode_ThreadPhase threadPhase = OodleLZ_Decode_ThreadPhase.Unthreaded);
27 |
28 | [DllImport(DllName)]
29 | public static extern long OodleLZ_Compress(
30 | OodleLZ_Compressor compressor,
31 | byte[] rawBuf,
32 | long rawLen,
33 | byte[] compBuf,
34 | OodleLZ_CompressionLevel level,
35 | IntPtr pOptions = default,
36 | IntPtr dictionaryBase = default,
37 | IntPtr lrm = default,
38 | byte[]? scratchMem = null,
39 | long scratchSize = 0);
40 |
41 | [DllImport(DllName)]
42 | public static extern long OodleLZ_GetCompressedBufferSizeNeeded(OodleLZ_Compressor compressor, long rawLen);
43 |
44 | public static bool TryDecompress(byte[] compressedData, long decompressedSize, [MaybeNullWhen(false)] out byte[] decompressedData)
45 | {
46 | byte[] decompressBuffer = new byte[decompressedSize];
47 | if (0 != OodleLZ_Decompress(compressedData, compressedData.Length, decompressBuffer, decompressedSize))
48 | {
49 | decompressedData = decompressBuffer;
50 | return true;
51 | }
52 | decompressedData = null;
53 | return false;
54 | }
55 |
56 | public static byte[] Decompress(byte[] compressedData, long decompressedSize)
57 | {
58 | if (TryDecompress(compressedData, decompressedSize, out byte[]? decompressedData))
59 | {
60 | return decompressedData;
61 | }
62 | else
63 | {
64 | throw new OodleException("Failed to decompress data");
65 | }
66 | }
67 |
68 | public static byte[] Compress(byte[] data, OodleLZ_Compressor compressor, OodleLZ_CompressionLevel level)
69 | {
70 | long compressionBufferSize = OodleLZ_GetCompressedBufferSizeNeeded(compressor, data.Length);
71 | byte[] compressionBuffer = new byte[compressionBufferSize];
72 | long compressedSize = OodleLZ_Compress(compressor, data, data.Length, compressionBuffer, level);
73 | if (compressedSize != 0)
74 | {
75 | return compressionBuffer[..(int)compressedSize];
76 | }
77 | else
78 | {
79 | throw new OodleException("Failed to compress data");
80 | }
81 | }
82 | }
83 |
84 | public delegate OodleDecompressCallbackRet OodleDecompressCallback(object userdata, IntPtr rawBuf, long rawLen, IntPtr compBuf, long compBufferSize , long rawDone, long compUsed);
85 |
86 | public enum OodleLZ_Compressor
87 | {
88 | Invalid = -1,
89 | None = 3,
90 | Kraken = 8,
91 | Leviathan = 13,
92 | Mermaid = 9,
93 | Selkie = 11,
94 | Hydra = 12
95 | }
96 |
97 | public enum OodleLZ_Verbosity
98 | {
99 | None = 0,
100 | Minimal = 1,
101 | Some = 2,
102 | Lots = 3
103 | }
104 |
105 | public enum OodleDecompressCallbackRet
106 | {
107 | Continue = 0,
108 | Cancel = 1,
109 | Invalid = 2
110 | }
111 |
112 | public enum OodleLZ_Decode_ThreadPhase
113 | {
114 | ThreadPhase1 = 1,
115 | ThreadPhase2 = 2,
116 | ThreadPhaseAll = 3,
117 | Unthreaded = ThreadPhaseAll
118 | }
119 |
120 | public enum OodleLZ_CompressionLevel
121 | {
122 | None = 0,
123 | SuperFast = 1,
124 | VeryFast = 2,
125 | Fast = 3,
126 | Normal = 4,
127 | Optimal1 = 5,
128 | Optimal2 = 6,
129 | Optimal3 = 7,
130 | Optimal4 = 8,
131 | Optimal5 = 9,
132 | HyperFast1 = -1,
133 | HyperFast2 = -2,
134 | HyperFast3 = -3,
135 | HyperFast4 = -4,
136 | HyperFast = HyperFast1,
137 | Optimal = Optimal2,
138 | Max = Optimal5,
139 | Min = HyperFast4
140 | }
141 |
142 | public class OodleException : Exception
143 | {
144 | public OodleException() { }
145 | public OodleException(string message) : base(message) { }
146 | public OodleException(string message, Exception innerException) : base(message, innerException) { }
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/Core/Serialization/MyBinaryReader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Buffers.Binary;
4 |
5 | namespace InfiniteVariantTool.Core.Serialization
6 | {
7 | public class MyBinaryReader
8 | {
9 | private readonly byte[] data;
10 | private int cursor;
11 | private bool littleEndian;
12 | private readonly Stack cursorStack;
13 | public MyBinaryReader(byte[] data, bool littleEndian = true)
14 | {
15 | this.data = data;
16 | this.littleEndian = littleEndian;
17 | cursor = 0;
18 | cursorStack = new Stack();
19 | }
20 |
21 | public bool EOF => cursor == data.Length;
22 |
23 | public void SetLittleEndian(bool isLittleEndian)
24 | {
25 | littleEndian = isLittleEndian;
26 | }
27 |
28 | public bool ReadBool()
29 | {
30 | return ReadUInt8() != 0;
31 | }
32 |
33 | public sbyte ReadInt8()
34 | {
35 | return (sbyte)ReadUInt8();
36 | }
37 |
38 | public byte ReadUInt8()
39 | {
40 |
41 | byte ret = data[cursor];
42 | cursor += sizeof(byte);
43 | return ret;
44 | }
45 |
46 | public short ReadInt16()
47 | {
48 | Span span = new Span(data)[cursor..];
49 | short ret = littleEndian ? BinaryPrimitives.ReadInt16LittleEndian(span) : BinaryPrimitives.ReadInt16BigEndian(span);
50 | cursor += sizeof(short);
51 | return ret;
52 | }
53 |
54 | public ushort ReadUInt16()
55 | {
56 | return (ushort)ReadInt16();
57 | }
58 |
59 | public int ReadInt32()
60 | {
61 | Span span = new Span(data)[cursor..];
62 | int ret = littleEndian ? BinaryPrimitives.ReadInt32LittleEndian(span) : BinaryPrimitives.ReadInt32BigEndian(span);
63 | cursor += sizeof(int);
64 | return ret;
65 | }
66 |
67 | public uint ReadUInt32()
68 | {
69 | return (uint)ReadInt32();
70 | }
71 |
72 | public long ReadInt64()
73 | {
74 | Span span = new Span(data)[cursor..];
75 | long ret = littleEndian ? BinaryPrimitives.ReadInt64LittleEndian(span) : BinaryPrimitives.ReadInt64BigEndian(span);
76 | cursor += sizeof(long);
77 | return ret;
78 | }
79 |
80 | public ulong ReadUInt64()
81 | {
82 | return (ulong)ReadInt64();
83 | }
84 |
85 | public float ReadFloat()
86 | {
87 | Span span = new Span(data)[cursor..];
88 | float ret = littleEndian ? BinaryPrimitives.ReadSingleLittleEndian(span) : BinaryPrimitives.ReadSingleBigEndian(span);
89 | cursor += sizeof(float);
90 | return ret;
91 | }
92 |
93 | public double ReadDouble()
94 | {
95 | Span span = new Span(data)[cursor..];
96 | double ret = littleEndian ? BinaryPrimitives.ReadDoubleLittleEndian(span) : BinaryPrimitives.ReadDoubleBigEndian(span);
97 | cursor += sizeof(double);
98 | return ret;
99 | }
100 |
101 | public string ReadString(int length)
102 | {
103 | string ret = System.Text.Encoding.UTF8.GetString(data, cursor, length);
104 | cursor += length;
105 | return ret;
106 | }
107 |
108 | public string ReadCString(int maxLength)
109 | {
110 | int length = 0;
111 | while (length < maxLength && Data[cursor + length] != 0)
112 | {
113 | length++;
114 | }
115 | return ReadString(length);
116 | }
117 |
118 | public string ReadStringBuffer(int size)
119 | {
120 | string ret = ReadCString(size);
121 | cursor += size - ret.Length;
122 | return ret;
123 | }
124 |
125 | public string ReadWString(int length)
126 | {
127 | string ret = System.Text.Encoding.Unicode.GetString(data, cursor, length * 2);
128 | cursor += length * 2;
129 | return ret;
130 | }
131 |
132 | public byte[] ReadBytes(int length)
133 | {
134 | byte[] ret = data[cursor..(cursor + length)];
135 | cursor += length;
136 | return ret;
137 | }
138 |
139 | public long ReadLEB128()
140 | {
141 | ulong unsigned = ReadLEB128U();
142 | return (long)(unsigned >> 1) ^ -(long)(unsigned & 1);
143 | }
144 |
145 | public ulong ReadLEB128U()
146 | {
147 | ulong result = 0;
148 | int shift = 0;
149 | ulong b;
150 | do
151 | {
152 | b = ReadUInt8();
153 | result |= (b & 0x7f) << shift;
154 | shift += 7;
155 | } while ((b & 0x80) != 0);
156 | return result;
157 | }
158 |
159 | public void Skip(int offset)
160 | {
161 | cursor += offset;
162 | }
163 |
164 | public void Seek(int position)
165 | {
166 | cursor = position;
167 | }
168 |
169 | public void PushCursor()
170 | {
171 | cursorStack.Push(cursor);
172 | }
173 |
174 | public int PopCursor()
175 | {
176 | cursor = cursorStack.Pop();
177 | return cursor;
178 | }
179 |
180 | public void Pad(int padding)
181 | {
182 | if (cursor % padding != 0)
183 | {
184 | Skip(padding - (cursor % padding));
185 | }
186 | }
187 |
188 | public int Position => cursor;
189 | public int Size => data.Length;
190 | public byte[] Data => data;
191 | }
192 | }
193 |
--------------------------------------------------------------------------------
/Core/Serialization/SchemaSerializer.cs:
--------------------------------------------------------------------------------
1 | using Bond.IO.Safe;
2 | using Bond.Protocols;
3 | using InfiniteVariantTool.Core.BondSchema;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.IO;
7 | using System.Linq;
8 | using System.Text;
9 | using System.Text.Json;
10 | using System.Threading;
11 | using System.Threading.Tasks;
12 | using System.Xml.Linq;
13 |
14 | namespace InfiniteVariantTool.Core.Serialization
15 | {
16 | public class SchemaSerializer
17 | {
18 | private static readonly JsonSerializerOptions jsonOptions = new()
19 | {
20 | WriteIndented = true,
21 | IgnoreReadOnlyFields = true,
22 | IgnoreReadOnlyProperties = true,
23 | Converters =
24 | {
25 | new BondGuidJsonConverter()
26 | }
27 | };
28 |
29 |
30 | public static T DeserializeBond(byte[] data)
31 | {
32 | InputBuffer input = new(data);
33 | CompactBinaryReader reader = new(input, 2);
34 | return Bond.Deserialize.From(reader);
35 | }
36 |
37 | public static object DeserializeBond(byte[] data, Type type)
38 | {
39 | InputBuffer input = new(data);
40 | CompactBinaryReader reader = new(input, 2);
41 | Bond.Deserializer> deserializer = new(type);
42 | return deserializer.Deserialize(reader);
43 | }
44 |
45 | public static async Task