├── .editorconfig
├── .gitattributes
├── .gitignore
├── ACDBackend
├── ACDBackend.csproj
├── ACDEncryption.cs
├── ACDWorker.cs
├── CustomReader.cs
├── CustomWriter.cs
└── Properties
│ └── AssemblyInfo.cs
├── AssettoTools.sln
├── AssettoTools
├── App.config
├── App.xaml
├── App.xaml.cs
├── AssettoTools.csproj
├── Core
│ ├── Helper
│ │ ├── Logger.cs
│ │ └── Utilities.cs
│ ├── Interfaces
│ │ ├── ICarExplorer.cs
│ │ ├── IConfigReader.cs
│ │ └── IFileExplorer.cs
│ ├── Models
│ │ └── CarObject.cs
│ └── Services
│ │ ├── CarExplorer.cs
│ │ ├── Config
│ │ ├── Config.cs
│ │ └── ConfigReader.cs
│ │ └── FileExplorer.cs
├── FodyWeavers.xml
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ ├── Settings.Designer.cs
│ └── Settings.settings
├── Resources
│ └── INIDefinition.xshd
├── ViewModels
│ └── MainViewModel.cs
├── Views
│ └── Windows
│ │ ├── MainWindow.xaml
│ │ └── MainWindow.xaml.cs
├── assetto-tools-icon.ico
└── assetto-tools-icon.png
└── README.md
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 |
3 | # CA1416: Validate platform compatibility
4 | dotnet_diagnostic.CA1416.severity = none
5 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Ww][Ii][Nn]32/
27 | [Aa][Rr][Mm]/
28 | [Aa][Rr][Mm]64/
29 | bld/
30 | [Bb]in/
31 | [Oo]bj/
32 | [Oo]ut/
33 | [Ll]og/
34 | [Ll]ogs/
35 |
36 | # Visual Studio 2015/2017 cache/options directory
37 | .vs/
38 | # Uncomment if you have tasks that create the project's static files in wwwroot
39 | #wwwroot/
40 |
41 | # Visual Studio 2017 auto generated files
42 | Generated\ Files/
43 |
44 | # MSTest test Results
45 | [Tt]est[Rr]esult*/
46 | [Bb]uild[Ll]og.*
47 |
48 | # NUnit
49 | *.VisualState.xml
50 | TestResult.xml
51 | nunit-*.xml
52 |
53 | # Build Results of an ATL Project
54 | [Dd]ebugPS/
55 | [Rr]eleasePS/
56 | dlldata.c
57 |
58 | # Benchmark Results
59 | BenchmarkDotNet.Artifacts/
60 |
61 | # .NET Core
62 | project.lock.json
63 | project.fragment.lock.json
64 | artifacts/
65 |
66 | # ASP.NET Scaffolding
67 | ScaffoldingReadMe.txt
68 |
69 | # StyleCop
70 | StyleCopReport.xml
71 |
72 | # Files built by Visual Studio
73 | *_i.c
74 | *_p.c
75 | *_h.h
76 | *.ilk
77 | *.meta
78 | *.obj
79 | *.iobj
80 | *.pch
81 | *.pdb
82 | *.ipdb
83 | *.pgc
84 | *.pgd
85 | *.rsp
86 | *.sbr
87 | *.tlb
88 | *.tli
89 | *.tlh
90 | *.tmp
91 | *.tmp_proj
92 | *_wpftmp.csproj
93 | *.log
94 | *.vspscc
95 | *.vssscc
96 | .builds
97 | *.pidb
98 | *.svclog
99 | *.scc
100 |
101 | # Chutzpah Test files
102 | _Chutzpah*
103 |
104 | # Visual C++ cache files
105 | ipch/
106 | *.aps
107 | *.ncb
108 | *.opendb
109 | *.opensdf
110 | *.sdf
111 | *.cachefile
112 | *.VC.db
113 | *.VC.VC.opendb
114 |
115 | # Visual Studio profiler
116 | *.psess
117 | *.vsp
118 | *.vspx
119 | *.sap
120 |
121 | # Visual Studio Trace Files
122 | *.e2e
123 |
124 | # TFS 2012 Local Workspace
125 | $tf/
126 |
127 | # Guidance Automation Toolkit
128 | *.gpState
129 |
130 | # ReSharper is a .NET coding add-in
131 | _ReSharper*/
132 | *.[Rr]e[Ss]harper
133 | *.DotSettings.user
134 |
135 | # TeamCity is a build add-in
136 | _TeamCity*
137 |
138 | # DotCover is a Code Coverage Tool
139 | *.dotCover
140 |
141 | # AxoCover is a Code Coverage Tool
142 | .axoCover/*
143 | !.axoCover/settings.json
144 |
145 | # Coverlet is a free, cross platform Code Coverage Tool
146 | coverage*.json
147 | coverage*.xml
148 | coverage*.info
149 |
150 | # Visual Studio code coverage results
151 | *.coverage
152 | *.coveragexml
153 |
154 | # NCrunch
155 | _NCrunch_*
156 | .*crunch*.local.xml
157 | nCrunchTemp_*
158 |
159 | # MightyMoose
160 | *.mm.*
161 | AutoTest.Net/
162 |
163 | # Web workbench (sass)
164 | .sass-cache/
165 |
166 | # Installshield output folder
167 | [Ee]xpress/
168 |
169 | # DocProject is a documentation generator add-in
170 | DocProject/buildhelp/
171 | DocProject/Help/*.HxT
172 | DocProject/Help/*.HxC
173 | DocProject/Help/*.hhc
174 | DocProject/Help/*.hhk
175 | DocProject/Help/*.hhp
176 | DocProject/Help/Html2
177 | DocProject/Help/html
178 |
179 | # Click-Once directory
180 | publish/
181 |
182 | # Publish Web Output
183 | *.[Pp]ublish.xml
184 | *.azurePubxml
185 | # Note: Comment the next line if you want to checkin your web deploy settings,
186 | # but database connection strings (with potential passwords) will be unencrypted
187 | *.pubxml
188 | *.publishproj
189 |
190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
191 | # checkin your Azure Web App publish settings, but sensitive information contained
192 | # in these scripts will be unencrypted
193 | PublishScripts/
194 |
195 | # NuGet Packages
196 | *.nupkg
197 | # NuGet Symbol Packages
198 | *.snupkg
199 | # The packages folder can be ignored because of Package Restore
200 | **/[Pp]ackages/*
201 | # except build/, which is used as an MSBuild target.
202 | !**/[Pp]ackages/build/
203 | # Uncomment if necessary however generally it will be regenerated when needed
204 | #!**/[Pp]ackages/repositories.config
205 | # NuGet v3's project.json files produces more ignorable files
206 | *.nuget.props
207 | *.nuget.targets
208 |
209 | # Microsoft Azure Build Output
210 | csx/
211 | *.build.csdef
212 |
213 | # Microsoft Azure Emulator
214 | ecf/
215 | rcf/
216 |
217 | # Windows Store app package directories and files
218 | AppPackages/
219 | BundleArtifacts/
220 | Package.StoreAssociation.xml
221 | _pkginfo.txt
222 | *.appx
223 | *.appxbundle
224 | *.appxupload
225 |
226 | # Visual Studio cache files
227 | # files ending in .cache can be ignored
228 | *.[Cc]ache
229 | # but keep track of directories ending in .cache
230 | !?*.[Cc]ache/
231 |
232 | # Others
233 | ClientBin/
234 | ~$*
235 | *~
236 | *.dbmdl
237 | *.dbproj.schemaview
238 | *.jfm
239 | *.pfx
240 | *.publishsettings
241 | orleans.codegen.cs
242 |
243 | # Including strong name files can present a security risk
244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
245 | #*.snk
246 |
247 | # Since there are multiple workflows, uncomment next line to ignore bower_components
248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
249 | #bower_components/
250 |
251 | # RIA/Silverlight projects
252 | Generated_Code/
253 |
254 | # Backup & report files from converting an old project file
255 | # to a newer Visual Studio version. Backup files are not needed,
256 | # because we have git ;-)
257 | _UpgradeReport_Files/
258 | Backup*/
259 | UpgradeLog*.XML
260 | UpgradeLog*.htm
261 | ServiceFabricBackup/
262 | *.rptproj.bak
263 |
264 | # SQL Server files
265 | *.mdf
266 | *.ldf
267 | *.ndf
268 |
269 | # Business Intelligence projects
270 | *.rdl.data
271 | *.bim.layout
272 | *.bim_*.settings
273 | *.rptproj.rsuser
274 | *- [Bb]ackup.rdl
275 | *- [Bb]ackup ([0-9]).rdl
276 | *- [Bb]ackup ([0-9][0-9]).rdl
277 |
278 | # Microsoft Fakes
279 | FakesAssemblies/
280 |
281 | # GhostDoc plugin setting file
282 | *.GhostDoc.xml
283 |
284 | # Node.js Tools for Visual Studio
285 | .ntvs_analysis.dat
286 | node_modules/
287 |
288 | # Visual Studio 6 build log
289 | *.plg
290 |
291 | # Visual Studio 6 workspace options file
292 | *.opt
293 |
294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
295 | *.vbw
296 |
297 | # Visual Studio LightSwitch build output
298 | **/*.HTMLClient/GeneratedArtifacts
299 | **/*.DesktopClient/GeneratedArtifacts
300 | **/*.DesktopClient/ModelManifest.xml
301 | **/*.Server/GeneratedArtifacts
302 | **/*.Server/ModelManifest.xml
303 | _Pvt_Extensions
304 |
305 | # Paket dependency manager
306 | .paket/paket.exe
307 | paket-files/
308 |
309 | # FAKE - F# Make
310 | .fake/
311 |
312 | # CodeRush personal settings
313 | .cr/personal
314 |
315 | # Python Tools for Visual Studio (PTVS)
316 | __pycache__/
317 | *.pyc
318 |
319 | # Cake - Uncomment if you are using it
320 | # tools/**
321 | # !tools/packages.config
322 |
323 | # Tabs Studio
324 | *.tss
325 |
326 | # Telerik's JustMock configuration file
327 | *.jmconfig
328 |
329 | # BizTalk build output
330 | *.btp.cs
331 | *.btm.cs
332 | *.odx.cs
333 | *.xsd.cs
334 |
335 | # OpenCover UI analysis results
336 | OpenCover/
337 |
338 | # Azure Stream Analytics local run output
339 | ASALocalRun/
340 |
341 | # MSBuild Binary and Structured Log
342 | *.binlog
343 |
344 | # NVidia Nsight GPU debugger configuration file
345 | *.nvuser
346 |
347 | # MFractors (Xamarin productivity tool) working folder
348 | .mfractor/
349 |
350 | # Local History for Visual Studio
351 | .localhistory/
352 |
353 | # BeatPulse healthcheck temp database
354 | healthchecksdb
355 |
356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
357 | MigrationBackup/
358 |
359 | # Ionide (cross platform F# VS Code tools) working folder
360 | .ionide/
361 |
362 | # Fody - auto-generated XML schema
363 | FodyWeavers.xsd
364 | /config.json
365 |
--------------------------------------------------------------------------------
/ACDBackend/ACDBackend.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net6.0
4 | Library
5 | false
6 | true
7 |
8 |
9 |
10 |
11 |
12 |
13 | all
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/ACDBackend/ACDEncryption.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace ACDBackend
9 | {
10 | public class ACDEncryption
11 | {
12 | public static string key = "";
13 |
14 | public static void encrypt(string data, byte[] result)
15 | {
16 | encrypt(Encoding.ASCII.GetBytes(data), result);
17 | }
18 |
19 | public static void encrypt(byte[] data, byte[] result)
20 | {
21 | int num2 = 0;
22 |
23 | for (int i = 0; i < data.Length; i++)
24 | {
25 | int num4 = (char)data[i] + key[num2];
26 |
27 | result[i * 4] = (byte)((num4 >= 256) ? (num4 - 256) : num4);
28 |
29 | if (num2 == key.Length - 1)
30 | {
31 | num2 = 0;
32 | }
33 | else
34 | {
35 | num2++;
36 | }
37 | }
38 | }
39 |
40 |
41 | public static void decrypt(byte[] data)
42 | {
43 | int num2 = 0;
44 |
45 | for(int i = 0; i < data.Length; i++)
46 | {
47 | int num4 = ((char)data[i] - key[num2]);
48 |
49 | data[i] = (byte)((num4 < 0) ? (num4 + 256) : num4);
50 |
51 | if (num2 == key.Length - 1)
52 | {
53 | num2 = 0;
54 | }
55 | else
56 | {
57 | num2++;
58 | }
59 | }
60 | }
61 |
62 | public static void setupEncryption(string folderName)
63 | {
64 | folderName = folderName.ToLower();
65 |
66 | int aggregateSeed = folderName.Aggregate(0, (int current, char t) =>
67 | {
68 | return current + t;
69 | });
70 |
71 | /* Octet 1 */
72 | byte octet1 = intToByte(aggregateSeed);
73 |
74 | /* Octet 2 */
75 | int num = 0;
76 | for (int i = 0; i < folderName.Length - 1; i += 2)
77 | {
78 | num = (num * folderName[i]) - folderName[i + 1];
79 | }
80 | byte octet2 = intToByte(num);
81 | //-----------------------
82 |
83 | /* Octet 3 */
84 | int num2 = 0;
85 | for (int j = 1; j < folderName.Length - 3; j += 3)
86 | {
87 | num2 = ((num2 * folderName[j]) / (folderName[j + 1] + 27)) - 27 - folderName[j - 1];
88 | }
89 | byte octet3 = intToByte(num2);
90 | //-----------------------
91 |
92 | /* Octet 4 */
93 | int num3 = 5763;
94 | for (int k = 1; k < folderName.Length; k++)
95 | {
96 | num3 -= folderName[k];
97 | }
98 | byte octet4 = intToByte(num3);
99 | //-----------------------
100 |
101 | /* Octet 5 */
102 | int num4 = 66;
103 | for (int l = 1; l < folderName.Length - 4; l += 4)
104 | {
105 | num4 = (folderName[l] + 15) * num4 * (folderName[l - 1] + 15) + 22;
106 | }
107 | byte octet5 = intToByte(num4);
108 | //-----------------------
109 |
110 | /* Octet 6 */
111 | int num5 = 101;
112 | for (int m = 0; m < folderName.Length - 2; m += 2)
113 | {
114 | num5 -= folderName[m];
115 | }
116 | byte octet6 = intToByte(num5);
117 | //-----------------------
118 |
119 | /* Octet 7 */
120 | int num6 = 171;
121 | for (int n = 0; n < folderName.Length - 2; n += 2)
122 | {
123 | num6 %= folderName[n];
124 | }
125 | byte octet7 = intToByte(num6);
126 | //-----------------------
127 |
128 | /* Octet 8 */
129 | int num7 = 171;
130 | for (int num8 = 0; num8 < folderName.Length - 1; num8++)
131 | {
132 | num7 = num7 / folderName[num8] + folderName[num8 + 1];
133 | }
134 | byte octet8 = intToByte(num7);
135 | //-----------------------
136 |
137 | //Concatenating the key
138 | key = string.Join("-", new object[]
139 | {
140 | octet1,
141 | octet2,
142 | octet3,
143 | octet4,
144 | octet5,
145 | octet6,
146 | octet7,
147 | octet8
148 | });
149 |
150 | Console.WriteLine($"[ACD Encryption]: Got key - {key}");
151 | }
152 |
153 | private static byte intToByte(int value)
154 | {
155 | return (byte)((value % 256 + 256) % 256);
156 | }
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/ACDBackend/ACDWorker.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Reflection.PortableExecutable;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace ACDBackend
11 | {
12 | public class ACDWorker
13 | {
14 | private CustomReader reader = new();
15 | private CustomWriter writer = new();
16 |
17 | public string dataPrefix { get; set; } = "\\data.acd";
18 |
19 | public string dataFolderPrefix { get; set; } = "\\data";
20 |
21 | public void saveEntries(string filePath, List entries, bool hasACD)
22 | {
23 | if(hasACD)
24 | {
25 | //Running this again for saveEntries just ensures that we have the write key.
26 | setupEncryption(filePath);
27 |
28 | if (entries == null)
29 | {
30 | return;
31 | }
32 |
33 | writer.saveACD($"{filePath}\\{dataPrefix}", entries);
34 | }
35 | else
36 | {
37 | Logger("No ACD, writing directly to \\data.");
38 |
39 | //If there is no ACD, just write the files directly to the \\data folder.
40 | foreach(FileObject obj in entries)
41 | {
42 | File.WriteAllText($"{filePath}\\{dataFolderPrefix}\\{obj.name}", obj.fileData);
43 | Logger($"Writing file: {obj.name}");
44 | }
45 | }
46 | }
47 |
48 |
49 | ///
50 | /// Retrieves the entries from a specified file path.
51 | ///
52 | /// The path to the file or directory containing the ACD file or the data folder.
53 | ///
54 | /// A tuple containing a list of ACDEntry objects and a boolean flag indicating how the entry data was collected.
55 | ///
56 | public List getEntries(string filePath)
57 | {
58 | //If the data folder already exists, just read the files from the folder.
59 | if(Directory.Exists($"{filePath}{dataFolderPrefix}"))
60 | {
61 | Logger($"Data folder for: {filePath} already exists, reading...");
62 |
63 | List entries = new();
64 |
65 | foreach (FileInfo file in new DirectoryInfo($"{filePath}{dataFolderPrefix}").GetFiles())
66 | {
67 | entries.Add(new FileObject() {
68 | name = file.Name,
69 | fileData = File.ReadAllText(file.FullName),
70 | fileType = parseType(file.Name)
71 | });
72 | }
73 |
74 | return entries;
75 | }
76 | else
77 | {
78 | //Ensure the ACD actually exists before attempting to read it.
79 |
80 | if (File.Exists($"{filePath}{dataPrefix}"))
81 | {
82 | setupEncryption(filePath);
83 |
84 | //Setup reader
85 | reader.prepareReader($"{filePath}{dataPrefix}");
86 |
87 | //Read data into array
88 | return reader.getEntries();
89 | }
90 | else
91 | {
92 | Logger($"{filePath} contains no ACD or Data folder, skipping...");
93 |
94 | return null;
95 | }
96 | }
97 | }
98 |
99 | public static FileTypes parseType(string name)
100 | {
101 | FileTypes returnType = FileTypes.OTHER;
102 |
103 | try
104 | {
105 | //Get past the dot.
106 | returnType = (FileTypes)Enum.Parse(typeof(FileTypes), name.Trim().Split('.')[1], true);
107 | }
108 | catch (Exception) { }
109 |
110 | //If it shits itself, just return OTHER.
111 |
112 | return returnType;
113 | }
114 |
115 | public void setupEncryption(string filePath)
116 | {
117 | //Setup worker
118 | string folderName = getFolderName(filePath);
119 |
120 | Logger($"Got folder name: {folderName}");
121 |
122 | //Setup encryption, using folder name as encryption key.
123 | ACDEncryption.setupEncryption(folderName);
124 | }
125 |
126 | public string getFolderName(string acdFilename)
127 | {
128 | var name = Path.GetFileName(acdFilename) ?? "";
129 | return name.StartsWith("data", StringComparison.OrdinalIgnoreCase) ? Path.GetFileName(Path.GetDirectoryName(acdFilename)) : name;
130 | }
131 |
132 | public void Logger(object data)
133 | {
134 | Trace.WriteLine($"[ACDWorker]: {data}");
135 | }
136 | }
137 |
138 | public class FileObject
139 | {
140 | public string name { get; set; }
141 |
142 | public FileTypes fileType { get; set; }
143 |
144 | public string fileData { get; set; }
145 | }
146 |
147 | public enum FileTypes
148 | {
149 | INI,
150 | LUT,
151 | OTHER
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/ACDBackend/CustomReader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Runtime.CompilerServices;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 | using System.Xml.Linq;
10 |
11 | namespace ACDBackend
12 | {
13 | /// Author: Danny
14 | ///
15 | /// Date: 25/02/2023
16 | ///
17 | ///
18 | /// Reads the bytes within the array and keeps track of position.
19 | ///
20 | public class CustomReader
21 | {
22 |
23 | private byte[] data { get; set; }
24 |
25 | private string filePath { get; set; }
26 |
27 | private int position { get; set; } = 0;
28 |
29 | public void prepareReader(string filePath)
30 | {
31 | this.filePath = filePath;
32 |
33 | readFile();
34 |
35 | //They do this in content manager, not sure why, but I might aswell do it too lmao
36 | if (readInt() == -1111)
37 | readInt();
38 | else
39 | resetPosition();
40 | }
41 |
42 | public void readFile()
43 | {
44 | data = File.ReadAllBytes(filePath);
45 |
46 | Logger($"Read in {data.Length} bytes, with path: {filePath}");
47 | }
48 |
49 | public List getEntries()
50 | {
51 | List entryList = new();
52 |
53 | //Ensure we are not hitting EOF and still trying to read
54 | while (position < data.Length)
55 | {
56 | string entryName = readString();
57 |
58 | entryList.Add(new FileObject() {
59 | name = entryName,
60 | fileData = readEncryptedBytes(),
61 | fileType = ACDWorker.parseType(entryName)
62 | });
63 | }
64 |
65 | Logger($"EOF, returning: {entryList.Count} entries.");
66 |
67 | cleanUp();
68 |
69 | return entryList;
70 | }
71 |
72 | public void cleanUp()
73 | {
74 | resetPosition();
75 |
76 | data = null;
77 |
78 | filePath = "";
79 | }
80 |
81 | public void resetPosition()
82 | {
83 | Logger($"Resetting position: {position}");
84 |
85 | position = 0;
86 | }
87 |
88 | public string readEncryptedBytes()
89 | {
90 | //Logger($"Current position: {position}");
91 |
92 | int length = readInt();
93 |
94 | //Logger($"Size of entry: {length}");
95 |
96 | byte[] _buffer = new byte[length];
97 |
98 | for (int i = 0; i < length; i++)
99 | {
100 | _buffer[i] = readByte();
101 | skip(3);
102 | }
103 |
104 | ACDEncryption.decrypt(_buffer);
105 |
106 | return Encoding.Default.GetString(_buffer);
107 | }
108 |
109 | public int readInt()
110 | {
111 | return BitConverter.ToInt32(readBytes(4), 0);
112 | }
113 |
114 | public byte readByte()
115 | {
116 | byte returnData = data[position];
117 | addPosition();
118 | return returnData;
119 | }
120 |
121 | public byte[] readBytes(int count)
122 | {
123 | //Make space
124 | byte[] _buffer = new byte[count];
125 |
126 | //Checking endianess
127 | if (BitConverter.IsLittleEndian)
128 | Array.Reverse(_buffer);
129 |
130 | //Copying the bytes into the buffer
131 | Array.Copy(data, position, _buffer, 0, count);
132 |
133 | //Add the position to cursor
134 | addPosition(count);
135 |
136 | return _buffer;
137 | }
138 |
139 |
140 | public string readString()
141 | {
142 | //Length of string
143 | int length = readInt();
144 |
145 | //Logger($"Length of string: {length}");
146 |
147 | //Logger($"Return string: {returnString}");
148 |
149 | return Encoding.Default.GetString(readBytes(length));
150 | }
151 |
152 | public void skip(int amount)
153 | {
154 | position += amount;
155 | }
156 |
157 | public void addPosition(int amount = 1)
158 | {
159 | position += amount;
160 | }
161 |
162 | public void Logger(object data)
163 | {
164 | Trace.WriteLine($"[CustomReader]: {data}");
165 | }
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/ACDBackend/CustomWriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace ACDBackend
10 | {
11 | public class CustomWriter
12 | {
13 | public void saveACD(string path, List entries)
14 | {
15 | //Get size of entries array.
16 |
17 | using (MemoryStream stream = new MemoryStream())
18 | {
19 | foreach (FileObject entry in entries)
20 | {
21 | writeString(entry.name, stream); //Write the name of the file.
22 | writeInt(entry.fileData.Length, stream); //Write the size of the file data.
23 |
24 | //Encrypt bytes and write.
25 | byte[] result = new byte[entry.fileData.Length * 4];
26 | ACDEncryption.encrypt(entry.fileData, result);
27 |
28 | //Write to the stream
29 | stream.Write(result, 0, result.Length);
30 | }
31 |
32 | Logger($"Stream has been written, size: {stream.Length}, writing .acd to: {path}");
33 |
34 | File.WriteAllBytes(path, stream.ToArray());
35 |
36 | Logger($"File successfully written.");
37 | }
38 | }
39 |
40 | public int getEntriesSize(List entries)
41 | {
42 | int returnSize = 0;
43 |
44 | foreach (FileObject entry in entries)
45 | {
46 | returnSize += entry.name.Length + entry.fileData.Length;
47 | }
48 |
49 | Logger($"Calculated entries size: {returnSize}");
50 |
51 | return returnSize;
52 | }
53 |
54 | public void writeInt(int value, Stream stream)
55 | {
56 | byte[] intByte = BitConverter.GetBytes(value);
57 |
58 | stream.Write(intByte, 0, intByte.Length);
59 | }
60 |
61 | public void writeString(string value, Stream stream)
62 | {
63 | //Write the size of the string first.
64 | writeInt(value.Length, stream);
65 |
66 | //Get the string bytes
67 | byte[] stringBytes = Encoding.ASCII.GetBytes(value);
68 |
69 | //Write to the stream
70 | stream.Write(stringBytes, 0, stringBytes.Length);
71 | }
72 |
73 | public CustomWriter()
74 | {
75 | Logger("CustomWriter created.");
76 | }
77 |
78 | public void Logger(object data)
79 | {
80 | Trace.WriteLine($"[CustomWriter]: {data}");
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/ACDBackend/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("ACDBackend")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("ACDBackend")]
13 | [assembly: AssemblyCopyright("Copyright © 2023")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("ea3778f9-544d-410a-bf9d-a0830adc8559")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/AssettoTools.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.4.33205.214
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ACDBackend", "ACDBackend\ACDBackend.csproj", "{EA3778F9-544D-410A-BF9D-A0830ADC8559}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AssettoTools", "AssettoTools\AssettoTools.csproj", "{30705971-E7AA-4F4A-9C9A-9A6BA57EC8E4}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{05FCA456-25F0-47FD-AC1E-A3D75518E32A}"
11 | ProjectSection(SolutionItems) = preProject
12 | .editorconfig = .editorconfig
13 | EndProjectSection
14 | EndProject
15 | Global
16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
17 | Debug|Any CPU = Debug|Any CPU
18 | Release|Any CPU = Release|Any CPU
19 | EndGlobalSection
20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
21 | {EA3778F9-544D-410A-BF9D-A0830ADC8559}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
22 | {EA3778F9-544D-410A-BF9D-A0830ADC8559}.Debug|Any CPU.Build.0 = Debug|Any CPU
23 | {EA3778F9-544D-410A-BF9D-A0830ADC8559}.Release|Any CPU.ActiveCfg = Release|Any CPU
24 | {EA3778F9-544D-410A-BF9D-A0830ADC8559}.Release|Any CPU.Build.0 = Release|Any CPU
25 | {30705971-E7AA-4F4A-9C9A-9A6BA57EC8E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
26 | {30705971-E7AA-4F4A-9C9A-9A6BA57EC8E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
27 | {30705971-E7AA-4F4A-9C9A-9A6BA57EC8E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
28 | {30705971-E7AA-4F4A-9C9A-9A6BA57EC8E4}.Release|Any CPU.Build.0 = Release|Any CPU
29 | EndGlobalSection
30 | GlobalSection(SolutionProperties) = preSolution
31 | HideSolutionNode = FALSE
32 | EndGlobalSection
33 | GlobalSection(ExtensibilityGlobals) = postSolution
34 | SolutionGuid = {91A4495D-C580-4BCA-A48F-A7562C960D77}
35 | EndGlobalSection
36 | EndGlobal
37 |
--------------------------------------------------------------------------------
/AssettoTools/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/AssettoTools/App.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/AssettoTools/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using AssettoTools.Core.Interfaces;
2 | using AssettoTools.Core.Services;
3 | using AssettoTools.Core.Services.Config;
4 | using AssettoTools.ViewModels;
5 | using AssettoTools.Views.Windows;
6 | using Microsoft.Extensions.DependencyInjection;
7 | using Microsoft.Extensions.Hosting;
8 | using System;
9 | using System.Diagnostics;
10 | using System.Windows;
11 | using System.Windows.Forms.Design;
12 |
13 | namespace AssettoTools
14 | {
15 | public partial class App : Application
16 | {
17 | public static IHost AppHost { get; private set; }
18 |
19 | public App()
20 | {
21 | AppHost = Host.CreateDefaultBuilder()
22 | .ConfigureServices((hostContext, services) =>
23 | {
24 | services.AddSingleton();
25 |
26 | services.AddSingleton();
27 |
28 | services.AddSingleton();
29 | services.AddSingleton();
30 | services.AddSingleton();
31 | })
32 | .Build();
33 | }
34 |
35 | protected override async void OnStartup(StartupEventArgs e)
36 | {
37 | await AppHost!.StartAsync();
38 |
39 | var mainWindow = AppHost.Services.GetRequiredService();
40 |
41 | mainWindow.Show();
42 |
43 | base.OnStartup(e);
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/AssettoTools/AssettoTools.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net6.0-windows
4 | WinExe
5 | false
6 | true
7 | true
8 | assetto-tools-icon.ico
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | all
23 | runtime; build; native; contentfiles; analyzers; buildtransitive
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | all
33 |
34 |
35 |
36 |
37 |
38 | Always
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/AssettoTools/Core/Helper/Logger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Runtime.CompilerServices;
6 | using System.Text;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 |
10 | namespace AssettoTools.Core.Helper
11 | {
12 | public class Logger
13 | {
14 | public static void log(object message, [CallerMemberName] string callerMemberName = null)
15 | {
16 | Trace.WriteLine($"[{DateTime.Now.ToString("hh:mm:ss tt")}][{Thread.CurrentThread.Name}][{callerMemberName}]: {message}");
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/AssettoTools/Core/Helper/Utilities.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using System.Windows;
8 |
9 | namespace AssettoTools.Core.Helper
10 | {
11 | public class Utilities
12 | {
13 | public static string fixFilePath(string name)
14 | {
15 | string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
16 |
17 | string fullName = System.IO.Path.Combine(desktopPath, name);
18 |
19 | return fullName;
20 | }
21 |
22 | public static string getACDFolderName(string fileName)
23 | {
24 | var name = Path.GetFileName(fileName) ?? "";
25 | return name.StartsWith("data", StringComparison.OrdinalIgnoreCase) ? Path.GetFileName(Path.GetDirectoryName(fileName)) : name;
26 | }
27 |
28 | public static StreamReader stringToReader(string data)
29 | {
30 | return new StreamReader(new MemoryStream(Encoding.ASCII.GetBytes(data)));
31 | }
32 |
33 | public static bool isDirectoryEmpty(string path)
34 | {
35 | return !Directory.EnumerateFileSystemEntries(path).Any();
36 | }
37 |
38 | public static AdonisUI.Controls.MessageBoxResult showMessageBox(string messageBoxText)
39 | {
40 | return AdonisUI.Controls.MessageBox.Show(messageBoxText, "Assetto Tools", AdonisUI.Controls.MessageBoxButton.OK, AdonisUI.Controls.MessageBoxImage.Warning, AdonisUI.Controls.MessageBoxResult.Yes);
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/AssettoTools/Core/Interfaces/ICarExplorer.cs:
--------------------------------------------------------------------------------
1 | using AssettoTools.Core.Models;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Collections.ObjectModel;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace AssettoTools.Core.Interfaces
10 | {
11 | public interface ICarExplorer
12 | {
13 | public Task> populateList(string filePath);
14 | public CarObject parseFolder(string carFolder);
15 | public string[] getImages(string carFolder);
16 | public string getUIJSON(string carFolder);
17 | public string getCarName(string carFolder);
18 | public bool precautionChecks(string carsPath);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/AssettoTools/Core/Interfaces/IConfigReader.cs:
--------------------------------------------------------------------------------
1 | using AssettoTools.Core.Services.Config;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace AssettoTools.Core.Interfaces
9 | {
10 | public interface IConfigReader
11 | {
12 | ConfigModel ConfigModel { get; }
13 |
14 | public void readConfig();
15 | public void saveConfig();
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/AssettoTools/Core/Interfaces/IFileExplorer.cs:
--------------------------------------------------------------------------------
1 | using ACDBackend;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Collections.ObjectModel;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace AssettoTools.Core.Interfaces
10 | {
11 | public interface IFileExplorer
12 | {
13 | public void saveEntries(string filePath, List objects, bool hasACD);
14 | public ObservableCollection getEntries(string filePath);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/AssettoTools/Core/Models/CarObject.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace AssettoTools.Core.Models
8 | {
9 | public class CarObject
10 | {
11 | public string carName { get; set; }
12 | public string folderName { get; set; }
13 | public string fullPath { get; set; }
14 | public string[] previewImages { get; set; }
15 | public bool hasACD { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/AssettoTools/Core/Services/CarExplorer.cs:
--------------------------------------------------------------------------------
1 | using AssettoTools.Core.Helper;
2 | using AssettoTools.Core.Interfaces;
3 | using AssettoTools.Core.Models;
4 | using AssettoTools.ViewModels;
5 | using Newtonsoft.Json;
6 | using Newtonsoft.Json.Linq;
7 | using System;
8 | using System.Collections.Generic;
9 | using System.Collections.ObjectModel;
10 | using System.Diagnostics;
11 | using System.IO;
12 | using System.Linq;
13 | using System.Runtime.Caching;
14 | using System.Text;
15 | using System.Threading;
16 | using System.Threading.Tasks;
17 |
18 | namespace AssettoTools.Core.Services
19 | {
20 | public class CarExplorer : ICarExplorer
21 | {
22 | private readonly string carsPrefix = "\\content\\cars";
23 | private readonly string uiPrefix = "\\ui";
24 | private readonly string skinsPrefix = "\\skins";
25 |
26 | //Cache this for later, in case we need it again
27 | public string carsPath = "";
28 |
29 | public CarExplorer()
30 | {
31 | Logger.log("CarExplorer created.");
32 | }
33 |
34 | public async Task> populateList(string filePath)
35 | {
36 | return await Task.Run(() =>
37 | {
38 | if (string.IsNullOrEmpty(filePath))
39 | {
40 | Logger.log("File path is empty, config not filled in?");
41 |
42 | //Utilities.showMessageBox("Please set your Assetto Corsa path, located at the bottom portion of the window.");
43 | return null;
44 | }
45 |
46 | carsPath = $"{filePath}{carsPrefix}";
47 |
48 | if (!precautionChecks(carsPath))
49 | {
50 | Logger.log("Precaution checks returned false, returning.");
51 | return null;
52 | }
53 |
54 | Logger.log($"Cars path is {carsPath}");
55 |
56 | ObservableCollection carObjects = new();
57 |
58 | foreach (string carFolder in Directory.GetDirectories(carsPath))
59 | {
60 | CarObject carObject = parseFolder(carFolder);
61 |
62 | if (carObject != null)
63 | {
64 | carObjects.Add(carObject);
65 | }
66 | }
67 |
68 | return carObjects;
69 | });
70 | }
71 |
72 | public CarObject parseFolder(string carFolder)
73 | {
74 | //Ensure the folder actually has something in it.
75 | if (Utilities.isDirectoryEmpty(carFolder))
76 | {
77 | Logger.log($"Car: {carFolder} has nothing in it, skipping...");
78 | return null;
79 | }
80 |
81 | CarObject returnObject = new();
82 |
83 | returnObject.fullPath = carFolder;
84 | returnObject.folderName = carFolder.Replace($"{carsPath}\\", "");
85 | returnObject.hasACD = File.Exists($"{carFolder}\\data.acd");
86 |
87 | returnObject.previewImages = getImages(carFolder);
88 |
89 | string carName = getCarName(carFolder);
90 |
91 | returnObject.carName = carName == null ? returnObject.folderName : carName;
92 |
93 | Logger.log($"[{returnObject.carName}] adding, hasACD: {returnObject.hasACD}.");
94 |
95 | return returnObject;
96 | }
97 |
98 | public string[] getImages(string carFolder)
99 | {
100 | if (!Directory.Exists($"{carFolder}{skinsPrefix}"))
101 | {
102 | Logger.log($"No skins for: {carFolder} returning...");
103 | return null;
104 | }
105 |
106 | //Get the car preview images.
107 | string[] folders = Directory.GetDirectories($"{carFolder}{skinsPrefix}");
108 |
109 | for (int i = 0; i < folders.Length; i++)
110 | {
111 | string imageLoc = $"{folders[i]}\\preview.jpg";
112 |
113 | if (File.Exists(imageLoc))
114 | {
115 | folders[i] = imageLoc;
116 | }
117 | else
118 | {
119 | Logger.log($"[{carFolder}] image number [{i}][{imageLoc}] does not exist.");
120 | }
121 | }
122 |
123 | Logger.log($"Fetched {folders.Length} images for: {carFolder}");
124 |
125 | return folders;
126 | }
127 |
128 | public string getUIJSON(string carFolder)
129 | {
130 | if (!Directory.Exists($"{carFolder}{uiPrefix}"))
131 | {
132 | Logger.log($"UI folder does not exist: {carFolder}");
133 |
134 | return null;
135 | }
136 |
137 | string[] jsonFiles = Directory.GetFiles($"{carFolder}{uiPrefix}", "*ui_car.json", SearchOption.TopDirectoryOnly);
138 |
139 | if (jsonFiles.Length > 0)
140 | {
141 | //Grab the first file & hope for the best (:
142 | return jsonFiles[0];
143 | }
144 | else
145 | {
146 | Logger.log($"UI folder is empty: {carFolder}");
147 |
148 | return null;
149 | }
150 | }
151 |
152 | public string getCarName(string carFolder)
153 | {
154 | //Find the JSON file.
155 |
156 | string uiCarsFile = getUIJSON(carFolder);
157 |
158 | if (uiCarsFile == null)
159 | {
160 | return null;
161 | }
162 |
163 | string jsonContents = File.ReadAllText(uiCarsFile);
164 |
165 | try
166 | {
167 | return ((dynamic)JObject.Parse(jsonContents)).name;
168 | }
169 | catch (JsonReaderException ex)
170 | {
171 | Logger.log($"UI cars for: {carFolder}, could not be parsed: {ex.Message}");
172 |
173 | return null;
174 | }
175 | }
176 |
177 | public bool precautionChecks(string carsPath)
178 | {
179 | //Precautionary checks, ensure directory actually exists.
180 |
181 | return Directory.Exists(carsPath);
182 | }
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/AssettoTools/Core/Services/Config/Config.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace AssettoTools.Core.Services.Config
8 | {
9 | public class ConfigModel
10 | {
11 | public string ACDDirectory { get; set; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/AssettoTools/Core/Services/Config/ConfigReader.cs:
--------------------------------------------------------------------------------
1 | using AssettoTools.Core.Helper;
2 | using AssettoTools.Core.Interfaces;
3 | using Newtonsoft.Json;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.IO;
7 | using System.Linq;
8 | using System.Text;
9 | using System.Threading.Tasks;
10 |
11 | namespace AssettoTools.Core.Services.Config
12 | {
13 | public class ConfigReader : IConfigReader
14 | {
15 | private ConfigModel _configModel = new();
16 |
17 | private string configPath = "config.json";
18 |
19 | public ConfigReader()
20 | {
21 | Logger.log("ConfigReader created.");
22 | }
23 |
24 | public ConfigModel ConfigModel
25 | {
26 | get { return _configModel; }
27 | private set { _configModel = value; }
28 | }
29 |
30 | public void readConfig()
31 | {
32 | if (!File.Exists(configPath))
33 | {
34 | Logger.log("Config does not exist, creating...");
35 |
36 | File.WriteAllText(configPath, JsonConvert.SerializeObject(_configModel));
37 | }
38 | else
39 | {
40 | try
41 | {
42 | _configModel = JsonConvert.DeserializeObject(File.ReadAllText(configPath));
43 |
44 | Logger.log("Config has successfully been read.");
45 | }
46 | catch (Exception ex)
47 | {
48 | Logger.log($"There was an error reading config file: {ex.Message}");
49 | }
50 | }
51 | }
52 |
53 | public void saveConfig()
54 | {
55 | try
56 | {
57 | File.WriteAllText(configPath, JsonConvert.SerializeObject(_configModel));
58 |
59 | Logger.log("Successfully saved config.");
60 | }
61 | catch (Exception ex)
62 | {
63 | Logger.log($"Config could not save: {ex.Message}");
64 | }
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/AssettoTools/Core/Services/FileExplorer.cs:
--------------------------------------------------------------------------------
1 | using ACDBackend;
2 | using AssettoTools.Core.Helper;
3 | using AssettoTools.Core.Models;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.IO;
7 | using System.Linq;
8 | using System.Text;
9 | using System.Threading.Tasks;
10 | using Microsoft.VisualBasic;
11 | using AssettoTools.ViewModels;
12 | using System.Collections.ObjectModel;
13 | using AssettoTools.Core.Interfaces;
14 |
15 | namespace AssettoTools.Core.Services
16 | {
17 | public class FileExplorer : IFileExplorer
18 | {
19 | private ACDWorker acdWorker = new();
20 |
21 | public void saveEntries(string filePath, List objects, bool hasACD)
22 | {
23 | acdWorker.saveEntries(filePath, objects, hasACD);
24 | }
25 |
26 | public ObservableCollection getEntries(string filePath)
27 | {
28 | return new ObservableCollection(acdWorker.getEntries(filePath));
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/AssettoTools/FodyWeavers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/AssettoTools/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Resources;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 | using System.Windows;
6 |
7 | // General Information about an assembly is controlled through the following
8 | // set of attributes. Change these attribute values to modify the information
9 | // associated with an assembly.
10 | [assembly: AssemblyTitle("AssettoTools")]
11 | [assembly: AssemblyDescription("")]
12 | [assembly: AssemblyConfiguration("")]
13 | [assembly: AssemblyCompany("")]
14 | [assembly: AssemblyProduct("AssettoTools")]
15 | [assembly: AssemblyCopyright("Copyright © 2023")]
16 | [assembly: AssemblyTrademark("")]
17 | [assembly: AssemblyCulture("")]
18 |
19 | // Setting ComVisible to false makes the types in this assembly not visible
20 | // to COM components. If you need to access a type in this assembly from
21 | // COM, set the ComVisible attribute to true on that type.
22 | [assembly: ComVisible(false)]
23 |
24 | //In order to begin building localizable applications, set
25 | //CultureYouAreCodingWith in your .csproj file
26 | //inside a . For example, if you are using US english
27 | //in your source files, set the to en-US. Then uncomment
28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in
29 | //the line below to match the UICulture setting in the project file.
30 |
31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
32 |
33 |
34 | [assembly: ThemeInfo(
35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
36 | //(used if a resource is not found in the page,
37 | // or application resource dictionaries)
38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
39 | //(used if a resource is not found in the page,
40 | // app, or any theme specific resource dictionaries)
41 | )]
42 |
43 |
44 | // Version information for an assembly consists of the following four values:
45 | //
46 | // Major Version
47 | // Minor Version
48 | // Build Number
49 | // Revision
50 | //
51 | // You can specify all the values or you can default the Build and Revision Numbers
52 | // by using the '*' as shown below:
53 | // [assembly: AssemblyVersion("1.0.*")]
54 | [assembly: AssemblyVersion("1.0.0.0")]
55 | [assembly: AssemblyFileVersion("1.0.0.0")]
56 |
--------------------------------------------------------------------------------
/AssettoTools/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace AssettoTools.Properties
12 | {
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources
26 | {
27 |
28 | private static global::System.Resources.ResourceManager resourceMan;
29 |
30 | private static global::System.Globalization.CultureInfo resourceCulture;
31 |
32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
33 | internal Resources()
34 | {
35 | }
36 |
37 | ///
38 | /// Returns the cached ResourceManager instance used by this class.
39 | ///
40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
41 | internal static global::System.Resources.ResourceManager ResourceManager
42 | {
43 | get
44 | {
45 | if ((resourceMan == null))
46 | {
47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AssettoTools.Properties.Resources", typeof(Resources).Assembly);
48 | resourceMan = temp;
49 | }
50 | return resourceMan;
51 | }
52 | }
53 |
54 | ///
55 | /// Overrides the current thread's CurrentUICulture property for all
56 | /// resource lookups using this strongly typed resource class.
57 | ///
58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
59 | internal static global::System.Globalization.CultureInfo Culture
60 | {
61 | get
62 | {
63 | return resourceCulture;
64 | }
65 | set
66 | {
67 | resourceCulture = value;
68 | }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/AssettoTools/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/AssettoTools/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace AssettoTools.Properties
12 | {
13 |
14 |
15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
18 | {
19 |
20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
21 |
22 | public static Settings Default
23 | {
24 | get
25 | {
26 | return defaultInstance;
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/AssettoTools/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/AssettoTools/Resources/INIDefinition.xshd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | (.*?)=
24 |
25 |
26 |
27 | =(.*?)
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/AssettoTools/ViewModels/MainViewModel.cs:
--------------------------------------------------------------------------------
1 | using ACDBackend;
2 | using AssettoTools.Core;
3 | using AssettoTools.Core.Helper;
4 | using AssettoTools.Core.Interfaces;
5 | using AssettoTools.Core.Models;
6 | using AssettoTools.Core.Services;
7 | using AssettoTools.Views.Windows;
8 | using CommunityToolkit.Mvvm.ComponentModel;
9 | using CommunityToolkit.Mvvm.Input;
10 | using ICSharpCode.AvalonEdit.Highlighting;
11 | using ICSharpCode.AvalonEdit.Highlighting.Xshd;
12 | using System;
13 | using System.Collections.Generic;
14 | using System.Collections.ObjectModel;
15 | using System.ComponentModel;
16 | using System.Diagnostics;
17 | using System.Linq;
18 | using System.Runtime.Versioning;
19 | using System.Text;
20 | using System.Xml;
21 | using System.Threading.Tasks;
22 | using System.Windows.Controls;
23 | using System.Windows.Forms;
24 |
25 | namespace AssettoTools.ViewModels
26 | {
27 | public partial class MainViewModel : ObservableObject
28 | {
29 | /* Services */
30 | public IConfigReader configService { get; set; }
31 | public IFileExplorer fileService { get; set; }
32 | public ICarExplorer carService { get; set; }
33 |
34 | /* Editor Tab */
35 | [ObservableProperty]
36 | public ObservableCollection carItems = new();
37 |
38 | [ObservableProperty]
39 | public ObservableCollection fileItems = new();
40 |
41 | [ObservableProperty]
42 | public string currentPath = "Current Path: ";
43 |
44 | [ObservableProperty]
45 | public CarObject currentCarObject = new();
46 |
47 | [ObservableProperty]
48 | public FileObject currentFileObject = new();
49 |
50 | [ObservableProperty]
51 | public string imagePath = "";
52 |
53 | public MainViewModel(IConfigReader _configService, IFileExplorer _fileService, ICarExplorer _carService)
54 | {
55 | configService = _configService;
56 | fileService = _fileService;
57 | carService = _carService;
58 |
59 | initWindow();
60 | }
61 |
62 | public async void initWindow()
63 | {
64 | Logger.log("AssettoTools created.");
65 |
66 | //Read the config into model.
67 | configService.readConfig();
68 |
69 | CarItems = await carService.populateList(configService.ConfigModel.ACDDirectory);
70 |
71 | CurrentPath = configService.ConfigModel.ACDDirectory;
72 |
73 | loadSyntaxHighlighting();
74 | }
75 |
76 | public void loadSyntaxHighlighting()
77 | {
78 | //Load syntax highlighting for avalonEdit.
79 | using (XmlTextReader reader = new XmlTextReader("Resources\\INIDefinition.xshd"))
80 | {
81 | AvalonDefinition = HighlightingLoader.Load(reader, HighlightingManager.Instance);
82 | }
83 | }
84 |
85 | partial void OnCurrentFileObjectChanging(FileObject value)
86 | {
87 | if(value != null)
88 | {
89 | if (CurrentFileObject != null && !string.IsNullOrEmpty(CurrentFileObject.name))
90 | {
91 | Logger.log($"Saving the last ACD: {CurrentFileObject.name}");
92 | saveACDEntry(CurrentFileObject);
93 | }
94 |
95 | Logger.log($"ACD entry changed to: {value.name}.");
96 |
97 | MainWindow.windowInstance.setEditorContent(value.fileData);
98 | }
99 | }
100 |
101 | partial void OnCurrentCarObjectChanged(CarObject value)
102 | {
103 | Logger.log($"Car object changed to: {value.carName}");
104 |
105 | //Clear editor
106 | MainWindow.windowInstance.setEditorContent("");
107 |
108 | //Clear file items
109 | FileItems.Clear();
110 |
111 | ObservableCollection entries = fileService.getEntries(value.fullPath);
112 |
113 | if (entries != null)
114 | {
115 | //Ask the File Explorer to populate the INI list.
116 |
117 | //Setup Editor Tab
118 | FileItems = entries;
119 |
120 | ImagePath = value.previewImages[0];
121 | }
122 | else
123 | {
124 | Utilities.showMessageBox($"{value.carName} does not have any data.");
125 |
126 | Logger.log($"Skipping {value.carName} entries null.");
127 | }
128 | }
129 |
130 | public void saveACDEntry(FileObject entry)
131 | {
132 | if(entry == null || FileItems.Count <= 0)
133 | {
134 | return;
135 | }
136 |
137 | entry.fileData = MainWindow.windowInstance.getEditorContent();
138 | }
139 |
140 |
141 |
142 | [RelayCommand]
143 | public void saveACD()
144 | {
145 | Logger.log($"Saving ACD for: {CurrentCarObject.carName}");
146 |
147 | //Save the current file if we haven't already.
148 | saveACDEntry(CurrentFileObject);
149 |
150 | fileService.saveEntries(CurrentCarObject.fullPath, FileItems.ToList(), CurrentCarObject.hasACD);
151 |
152 | Utilities.showMessageBox("Successfully saved data.acd file.");
153 | }
154 |
155 | public void OnWindowClosing(object sender, CancelEventArgs e)
156 | {
157 | configService.saveConfig();
158 | }
159 |
160 | [RelayCommand]
161 | public void setAssettoPath()
162 | {
163 | using (var dialog = new FolderBrowserDialog())
164 | {
165 | DialogResult result = dialog.ShowDialog();
166 |
167 | Logger.log($"Got Assetto root path: {dialog.SelectedPath}");
168 |
169 | CurrentPath = $"Current Path: {dialog.SelectedPath}";
170 |
171 | configService.ConfigModel.ACDDirectory = dialog.SelectedPath;
172 |
173 | carService.populateList(dialog.SelectedPath);
174 | }
175 | }
176 |
177 | [ObservableProperty]
178 | public IHighlightingDefinition avalonDefinition = null;
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/AssettoTools/Views/Windows/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
--------------------------------------------------------------------------------
/AssettoTools/Views/Windows/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using AdonisUI.Controls;
2 | using AssettoTools.ViewModels;
3 | using ICSharpCode.AvalonEdit.Highlighting.Xshd;
4 | using System.Xml;
5 | using ICSharpCode.AvalonEdit.Highlighting;
6 | using System;
7 | using System.Diagnostics;
8 |
9 | namespace AssettoTools.Views.Windows
10 | {
11 | public partial class MainWindow : AdonisWindow
12 | {
13 | public static MainWindow windowInstance;
14 |
15 | public MainWindow(MainViewModel _viewModel)
16 | {
17 | InitializeComponent();
18 |
19 | DataContext = _viewModel;
20 |
21 | windowInstance = this;
22 |
23 | // Subscribe to Closing event
24 | Closing += _viewModel.OnWindowClosing;
25 | }
26 |
27 | public string getEditorContent()
28 | {
29 | return avalonEditor.Text;
30 | }
31 |
32 | public void setEditorContent(string text)
33 | {
34 | avalonEditor.Text = text;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/AssettoTools/assetto-tools-icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0danny/AssettoTools/3af26ad8f985714cebec55a364ea1e7d63e7fad1/AssettoTools/assetto-tools-icon.ico
--------------------------------------------------------------------------------
/AssettoTools/assetto-tools-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0danny/AssettoTools/3af26ad8f985714cebec55a364ea1e7d63e7fad1/AssettoTools/assetto-tools-icon.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
Assetto Tools
8 |
9 |
10 |
11 |
12 | Assetto Corsa car modifier.
13 |
14 | - Able to modifiy all INI files from a desktop application.
15 | - Comes with in-house ACD extractor and decryptor, custom wrote. Please see ACDBackend project.
16 | - Text editor is AvalonEdit, custom made syntax highlighting so it may not be perfect. Still working on it.
17 | - Work flow is fast, side menu containing all files enables quick and easy modification of all a cars attributes/settings, see image below.
18 | - Changes are auto-saved between files. However, when you go to a different car, the changes for the previous are lost unless you save the ACD/data.
19 |
20 | **Release 0.1 is available in the release section of this repository.**
21 |
22 | ## Pictures
23 |
24 | 
25 |
26 | ## TODO
27 |
28 | - ~~Add a save button to re-encrypt ACD.~~
29 | - ~~Add an area to set your Assetto path (Settings tab?)~~
30 | - Dark/Light mode switch.
31 | - ~~Fix saving for cars that don't have a ACD & just data folder.~~
32 | - Add syntax highlighting for the .LUT files.
33 | - It is kind of broken with brackets (look at preview nodes)
34 |
35 | # ACD Extractor
36 |
37 | Below is an explanation on how the CreateKey & Decryption functions work. Assetto utilises a simple ROT algorithim for the encryption/decryption.
38 |
39 | ## Helper Functions
40 |
41 | ### Int To Byte
42 |
43 | ```csharp
44 | (byte)((value % 256 + 256) % 256);
45 | ```
46 |
47 | ## CreateKey()
48 |
49 | ### Params
50 | * string - folderName
51 |
52 | Assetto uses the folder name of the car to create an encryption/decryption key. Once the .acd is packed with the key, if the folder name is changed it will no longer be decryptable. In the example below the folder name we are using is:
53 |
54 | #### ks_audi_sport_quattro
55 |
56 | ### Key Structure
57 |
58 | The key is structured as follows:
59 |
60 | 000-000-000-000-000-000-000-000
61 |
62 | Each octet is concatenated with a "-" delimiter.
63 |
64 | - - - -
65 |
66 | ### Octet 1
67 |
68 | ```csharp
69 | folderName = folderName.ToLower();
70 |
71 | int aggregateSeed = folderName.Aggregate(0, (int current, char t) =>
72 | {
73 | return current + t;
74 | });
75 | ```
76 |
77 | Firstly the folderName is converted to lower case and its characters represented as its unicode counterpart are summed together, this gives:
78 |
79 | #### 2278
80 |
81 | The process of aggregation is shown below.
82 |
83 | ```
84 | Current: 0, T: k, T(NUM): 107
85 | Current: 107, T: s, T(NUM): 115
86 | Current: 222, T: _, T(NUM): 95
87 | Current: 317, T: a, T(NUM): 97
88 | Current: 414, T: u, T(NUM): 117
89 | Current: 531, T: d, T(NUM): 100
90 | Current: 631, T: i, T(NUM): 105
91 | Current: 736, T: _, T(NUM): 95
92 | Current: 831, T: s, T(NUM): 115
93 | Current: 946, T: p, T(NUM): 112
94 | Current: 1058, T: o, T(NUM): 111
95 | Current: 1169, T: r, T(NUM): 114
96 | Current: 1283, T: t, T(NUM): 116
97 | Current: 1399, T: _, T(NUM): 95
98 | Current: 1494, T: q, T(NUM): 113
99 | Current: 1607, T: u, T(NUM): 117
100 | Current: 1724, T: a, T(NUM): 97
101 | Current: 1821, T: t, T(NUM): 116
102 | Current: 1937, T: t, T(NUM): 116
103 | Current: 2053, T: r, T(NUM): 114
104 | Current: 2167, T: o, T(NUM): 111
105 | ```
106 |
107 | The result of **2278** is then converted to a single byte represention using the helper function **intToByte** shown above which yields:
108 |
109 | #### 230
110 |
111 | This is the first octet of the key.
112 |
113 | - - - -
114 |
115 | ### Global Variables
116 |
117 | **a** = folderName Length
118 |
119 | **b** = folderName Array
120 |
121 | ### Octet 2
122 |
123 | For convenience the rest of the calculations will be represented as a math/programming expression. Treat summation as a simple loop.
124 |
125 | **temp** = 0
126 |
127 | 
128 |
129 | ### Octet 3
130 |
131 | **temp** = 0
132 |
133 | 
134 |
135 | ### Octet 4
136 |
137 | **temp** = 5763
138 |
139 | 
140 |
141 | ### Octet 5
142 |
143 | **temp** = 66
144 |
145 | 
146 |
147 | ### Octet 6
148 |
149 | **temp** = 101
150 |
151 | 
152 |
153 | ### Octet 7
154 |
155 | **temp** = 171
156 |
157 | 
158 |
159 | ### Octet 8
160 |
161 | **temp** = 171
162 |
163 | 
164 |
165 | ### Touches
166 |
167 | All octets are ran through the IntToByte function and their product is concatenated using a "-" delimiter as seen below.
168 |
169 | ```csharp
170 | key = string.Join("-", new object[]
171 | {
172 | octet1,
173 | octet2,
174 | octet3,
175 | octet4,
176 | octet5,
177 | octet6,
178 | octet7,
179 | octet8
180 | });
181 | ```
182 |
183 | I understand the above probably looks like gibberish and it's most likely easier to just read the code, however I thought I might aswell include it anyways.
184 |
185 | ## Decrypt()
186 |
187 | ### Params
188 | * byte[] - array
189 |
190 | The decrypt & encrypt functions both make use of the key created above to decrypt the values within the ACD file.
191 |
192 | ## Encrypt()
193 |
194 | ### Params
195 | * byte[] - data
196 | * byte[] - result (pointer)
197 |
--------------------------------------------------------------------------------