├── .editorconfig
├── .github
└── FUNDING.yml
├── .gitignore
├── .gitmodules
├── BL3SaveEditor.sln
├── BL3SaveEditor
├── App.config
├── App.xaml
├── App.xaml.cs
├── AutoUpdater.xml
├── BL3SaveEditor.csproj
├── Borderlands 3 002 512 x 512 ICO.ico
├── Controls
│ ├── IntegerMessageBox.xaml
│ ├── IntegerMessageBox.xaml.cs
│ ├── ItemBalanceChanger.xaml
│ └── ItemBalanceChanger.xaml.cs
├── Debug
│ ├── DebugConsole.xaml
│ ├── DebugConsole.xaml.cs
│ └── RedirectWriter.cs
├── FodyWeavers.xml
├── Helpers
│ └── Helpers.cs
├── Icons
│ ├── fugue
│ │ ├── clipboard-text.png
│ │ ├── cross-script.png
│ │ ├── disk--plus.png
│ │ ├── disk--purple.png
│ │ ├── disk-black.png
│ │ ├── document-binary.png
│ │ ├── document-copy.png
│ │ ├── documents-stack.png
│ │ ├── documents-text.png
│ │ ├── folder-open-document.png
│ │ └── wrench-screwdriver.png
│ ├── game
│ │ ├── AllModIcons_I10.png
│ │ ├── AllModIcons_I12.png
│ │ ├── AllModIcons_I2C.png
│ │ ├── AllModIcons_I2E.png
│ │ ├── AllModIcons_I30.png
│ │ ├── BITMAP_ICO_Expansion_4k.png
│ │ ├── ECHOComm_IE.png
│ │ ├── MinimapIcon_GalaxyMap.png
│ │ ├── MinimapIcon_LostFoundFull.png
│ │ ├── MinimapIcon_MiniBoss.png
│ │ ├── MinimapIcon_Pinging_Loot.png
│ │ ├── MinimapIcon_QuickChange.png
│ │ ├── MinimapIcon_Respawn.png
│ │ ├── MinimapIcon_SDUVendor.png
│ │ ├── MissionIcon_TurnIn.png
│ │ ├── promptIconDiamondKey.png
│ │ ├── promptIconEridium.png
│ │ └── promptIconGoldenKey.png
│ └── icons
│ │ ├── Borderlands 3 001 512 x 512 ICO.ico
│ │ ├── Borderlands 3 001 512 x 512 PNG.png
│ │ ├── Borderlands 3 002 512 x 512 ICO.ico
│ │ ├── Borderlands 3 002 512 x 512 PNG.png
│ │ ├── Borderlands 3 003 512 x 512 ICO.ico
│ │ ├── Borderlands 3 003 512 x 512 PNG.png
│ │ ├── Borderlands 3 004 512 x 512 ICO.ico
│ │ ├── Borderlands 3 004 512 x 512 PNG.png
│ │ └── README.md
├── MainWindow.xaml
├── MainWindow.xaml.cs
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ ├── Settings.Designer.cs
│ └── Settings.settings
├── Starters
│ └── Zane.sav
└── Styles
│ └── WPFToolkit_Adonis.xaml
├── BL3Tools
├── BL3Tools.cs
├── BL3Tools.csproj
├── Decryption
│ ├── CRC32.cs
│ ├── ProfileBogoCrypt.cs
│ └── SaveBogoCrypt.cs
├── FixProtobufs.ps1
├── GVAS
│ └── GVASSave.cs
├── GameData
│ ├── DataPathTranslations.cs
│ ├── Experience.cs
│ ├── Items
│ │ ├── Borderlands3Serial.cs
│ │ ├── InventoryKeyDB.cs
│ │ ├── InventoryNameDatabase.cs
│ │ ├── InventorySerialDatabase.cs
│ │ └── Mappings
│ │ │ ├── README.md
│ │ │ ├── balance_to_inv_data.json
│ │ │ ├── balance_to_inv_key.json
│ │ │ ├── part_name_mapping.json
│ │ │ ├── prefix_name_mapping.json
│ │ │ ├── valid_generics.json
│ │ │ └── valid_part_database.json
│ ├── Mappings
│ │ └── fast_travel_to_name.json
│ └── SDU.cs
├── Helpers
│ └── Helpers.cs
├── Platform.cs
├── Properties
│ └── AssemblyInfo.cs
├── Protobufs
│ ├── OakProfile.proto
│ ├── OakSave.proto
│ └── OakShared.proto
├── Save.cs
├── Scripts
│ ├── BalanceToInventoryData.py
│ ├── BalanceToInventoryKey.py
│ ├── FastTravelDumper.py
│ ├── PartToName.py
│ ├── README.md
│ └── ValidPartMapper.py
├── TMSUnpack
│ ├── TMSArchive.cs
│ └── TMSUnpacker.cs
└── packages.config
├── CustomizationGenerator
├── CustomizationGenerator.py
└── CustomizationGenerator.pyproj
├── LICENSE
└── README.md
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 |
3 | # Default severity for all analyzer diagnostics
4 | dotnet_analyzer_diagnostic.severity = none
5 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: fromdarkhell
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: fromdarkhell
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/.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 | [Bbin]/[Dd]ebug/
21 | [Bbin]/[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 | [Ll]og/
33 | [Ll]ogs/
34 |
35 | # Visual Studio 2015/2017 cache/options directory
36 | .vs/
37 | # Uncomment if you have tasks that create the project's static files in wwwroot
38 | #wwwroot/
39 |
40 | # Visual Studio 2017 auto generated files
41 | Generated\ Files/
42 |
43 | # MSTest test Results
44 | [Tt]est[Rr]esult*/
45 | [Bb]uild[Ll]og.*
46 |
47 | # NUnit
48 | *.VisualState.xml
49 | TestResult.xml
50 | nunit-*.xml
51 |
52 | # Build Results of an ATL Project
53 | [Dd]ebugPS/
54 | [Rr]eleasePS/
55 | dlldata.c
56 |
57 | # Benchmark Results
58 | BenchmarkDotNet.Artifacts/
59 |
60 | # .NET Core
61 | project.lock.json
62 | project.fragment.lock.json
63 | artifacts/
64 |
65 | # ASP.NET Scaffolding
66 | ScaffoldingReadMe.txt
67 |
68 | # StyleCop
69 | StyleCopReport.xml
70 |
71 | # Files built by Visual Studio
72 | *_i.c
73 | *_p.c
74 | *_h.h
75 | *.ilk
76 | *.meta
77 | *.obj
78 | *.iobj
79 | *.pch
80 | *.pdb
81 | *.ipdb
82 | *.pgc
83 | *.pgd
84 | *.rsp
85 | *.sbr
86 | *.tlb
87 | *.tli
88 | *.tlh
89 | *.tmp
90 | *.tmp_proj
91 | *_wpftmp.csproj
92 | *.log
93 | *.tlog
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 | # Nuget personal access tokens and Credentials
210 | nuget.config
211 |
212 | # Microsoft Azure Build Output
213 | csx/
214 | *.build.csdef
215 |
216 | # Microsoft Azure Emulator
217 | ecf/
218 | rcf/
219 |
220 | # Windows Store app package directories and files
221 | AppPackages/
222 | BundleArtifacts/
223 | Package.StoreAssociation.xml
224 | _pkginfo.txt
225 | *.appx
226 | *.appxbundle
227 | *.appxupload
228 |
229 | # Visual Studio cache files
230 | # files ending in .cache can be ignored
231 | *.[Cc]ache
232 | # but keep track of directories ending in .cache
233 | !?*.[Cc]ache/
234 |
235 | # Others
236 | ClientBin/
237 | ~$*
238 | *~
239 | *.dbmdl
240 | *.dbproj.schemaview
241 | *.jfm
242 | *.pfx
243 | *.publishsettings
244 | orleans.codegen.cs
245 |
246 | # Including strong name files can present a security risk
247 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
248 | #*.snk
249 |
250 | # Since there are multiple workflows, uncomment next line to ignore bower_components
251 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
252 | #bower_components/
253 |
254 | # RIA/Silverlight projects
255 | Generated_Code/
256 |
257 | # Backup & report files from converting an old project file
258 | # to a newer Visual Studio version. Backup files are not needed,
259 | # because we have git ;-)
260 | _UpgradeReport_Files/
261 | Backup*/
262 | UpgradeLog*.XML
263 | UpgradeLog*.htm
264 | ServiceFabricBackup/
265 | *.rptproj.bak
266 |
267 | # SQL Server files
268 | *.mdf
269 | *.ldf
270 | *.ndf
271 |
272 | # Business Intelligence projects
273 | *.rdl.data
274 | *.bim.layout
275 | *.bim_*.settings
276 | *.rptproj.rsuser
277 | *- [Bb]ackup.rdl
278 | *- [Bb]ackup ([0-9]).rdl
279 | *- [Bb]ackup ([0-9][0-9]).rdl
280 |
281 | # Microsoft Fakes
282 | FakesAssemblies/
283 |
284 | # GhostDoc plugin setting file
285 | *.GhostDoc.xml
286 |
287 | # Node.js Tools for Visual Studio
288 | .ntvs_analysis.dat
289 | node_modules/
290 |
291 | # Visual Studio 6 build log
292 | *.plg
293 |
294 | # Visual Studio 6 workspace options file
295 | *.opt
296 |
297 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
298 | *.vbw
299 |
300 | # Visual Studio LightSwitch build output
301 | **/*.HTMLClient/GeneratedArtifacts
302 | **/*.DesktopClient/GeneratedArtifacts
303 | **/*.DesktopClient/ModelManifest.xml
304 | **/*.Server/GeneratedArtifacts
305 | **/*.Server/ModelManifest.xml
306 | _Pvt_Extensions
307 |
308 | # Paket dependency manager
309 | .paket/paket.exe
310 | paket-files/
311 |
312 | # FAKE - F# Make
313 | .fake/
314 |
315 | # CodeRush personal settings
316 | .cr/personal
317 |
318 | # Python Tools for Visual Studio (PTVS)
319 | __pycache__/
320 | *.pyc
321 |
322 | # Cake - Uncomment if you are using it
323 | # tools/**
324 | # !tools/packages.config
325 |
326 | # Tabs Studio
327 | *.tss
328 |
329 | # Telerik's JustMock configuration file
330 | *.jmconfig
331 |
332 | # BizTalk build output
333 | *.btp.cs
334 | *.btm.cs
335 | *.odx.cs
336 | *.xsd.cs
337 |
338 | # OpenCover UI analysis results
339 | OpenCover/
340 |
341 | # Azure Stream Analytics local run output
342 | ASALocalRun/
343 |
344 | # MSBuild Binary and Structured Log
345 | *.binlog
346 |
347 | # NVidia Nsight GPU debugger configuration file
348 | *.nvuser
349 |
350 | # MFractors (Xamarin productivity tool) working folder
351 | .mfractor/
352 |
353 | # Local History for Visual Studio
354 | .localhistory/
355 |
356 | # BeatPulse healthcheck temp database
357 | healthchecksdb
358 |
359 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
360 | MigrationBackup/
361 |
362 | # Ionide (cross platform F# VS Code tools) working folder
363 | .ionide/
364 |
365 | # Fody - auto-generated XML schema
366 | FodyWeavers.xsd
367 |
368 | # VS Code files for those working on multiple tools
369 | .vscode/*
370 | !.vscode/settings.json
371 | !.vscode/tasks.json
372 | !.vscode/launch.json
373 | !.vscode/extensions.json
374 | *.code-workspace
375 |
376 | # Local History for Visual Studio Code
377 | .history/
378 |
379 | # Windows Installer files from build outputs
380 | *.cab
381 | *.msi
382 | *.msix
383 | *.msm
384 | *.msp
385 |
386 | # JetBrains Rider
387 | .idea/
388 | *.sln.iml
389 |
390 |
391 | # Scripts results
392 | CustomizationGenerator/output.txt
393 | CustomizationGenerator/sheetData.xlsx
394 |
395 | BL3Tools/Scripts/Data/
396 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "BL3Tools/Protobufs"]
2 | path = BL3Tools/Protobufs
3 | url = https://github.com/gibbed/Borderlands3Protos
4 | [submodule "BL3Tools/GameData/Items/SerialDB"]
5 | path = BL3Tools/GameData/Items/SerialDB
6 | url = https://github.com/gibbed/Borderlands3Dumps
7 | [submodule "IOTools"]
8 | path = IOTools
9 | url = https://github.com/FromDarkHell/IOTools
10 |
--------------------------------------------------------------------------------
/BL3SaveEditor.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30330.147
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BL3SaveEditor", "BL3SaveEditor\BL3SaveEditor.csproj", "{A363B5AB-89B8-40F7-85A9-6A7341E183EF}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BL3Tools", "BL3Tools\BL3Tools.csproj", "{EEFEAB45-B558-4D33-9C74-D68660E26921}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IOTools", "IOTools\IOTools.csproj", "{B93FF5DB-FFEF-4F81-812E-F5FDF8CDFBBE}"
11 | EndProject
12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{83376407-428A-4A92-B64D-F5956CCEA666}"
13 | ProjectSection(SolutionItems) = preProject
14 | .editorconfig = .editorconfig
15 | .gitignore = .gitignore
16 | README.md = README.md
17 | EndProjectSection
18 | EndProject
19 | Global
20 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
21 | Debug|Any CPU = Debug|Any CPU
22 | Release|Any CPU = Release|Any CPU
23 | Single File|Any CPU = Single File|Any CPU
24 | EndGlobalSection
25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
26 | {A363B5AB-89B8-40F7-85A9-6A7341E183EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {A363B5AB-89B8-40F7-85A9-6A7341E183EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {A363B5AB-89B8-40F7-85A9-6A7341E183EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {A363B5AB-89B8-40F7-85A9-6A7341E183EF}.Release|Any CPU.Build.0 = Release|Any CPU
30 | {A363B5AB-89B8-40F7-85A9-6A7341E183EF}.Single File|Any CPU.ActiveCfg = Single File|Any CPU
31 | {A363B5AB-89B8-40F7-85A9-6A7341E183EF}.Single File|Any CPU.Build.0 = Single File|Any CPU
32 | {EEFEAB45-B558-4D33-9C74-D68660E26921}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {EEFEAB45-B558-4D33-9C74-D68660E26921}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {EEFEAB45-B558-4D33-9C74-D68660E26921}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {EEFEAB45-B558-4D33-9C74-D68660E26921}.Release|Any CPU.Build.0 = Release|Any CPU
36 | {EEFEAB45-B558-4D33-9C74-D68660E26921}.Single File|Any CPU.ActiveCfg = Release|Any CPU
37 | {EEFEAB45-B558-4D33-9C74-D68660E26921}.Single File|Any CPU.Build.0 = Release|Any CPU
38 | {B93FF5DB-FFEF-4F81-812E-F5FDF8CDFBBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39 | {B93FF5DB-FFEF-4F81-812E-F5FDF8CDFBBE}.Debug|Any CPU.Build.0 = Debug|Any CPU
40 | {B93FF5DB-FFEF-4F81-812E-F5FDF8CDFBBE}.Release|Any CPU.ActiveCfg = Release|Any CPU
41 | {B93FF5DB-FFEF-4F81-812E-F5FDF8CDFBBE}.Release|Any CPU.Build.0 = Release|Any CPU
42 | {B93FF5DB-FFEF-4F81-812E-F5FDF8CDFBBE}.Single File|Any CPU.ActiveCfg = Release|Any CPU
43 | {B93FF5DB-FFEF-4F81-812E-F5FDF8CDFBBE}.Single File|Any CPU.Build.0 = Release|Any CPU
44 | EndGlobalSection
45 | GlobalSection(SolutionProperties) = preSolution
46 | HideSolutionNode = FALSE
47 | EndGlobalSection
48 | GlobalSection(ExtensibilityGlobals) = postSolution
49 | SolutionGuid = {BCA67F01-673D-4452-BB34-56C5F207DE42}
50 | EndGlobalSection
51 | EndGlobal
52 |
--------------------------------------------------------------------------------
/BL3SaveEditor/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | True
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/BL3SaveEditor/App.xaml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/BL3SaveEditor/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Configuration;
4 | using System.Data;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using System.Windows;
8 |
9 | namespace BL3SaveEditor {
10 | ///
11 | /// Interaction logic for App.xaml
12 | ///
13 | public partial class App : Application {
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/BL3SaveEditor/AutoUpdater.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 | 1.1.3.0
5 | https://github.com/FromDarkHell/BL3SaveEditor/releases/latest/download/BL3SaveEditor-Portable.zip
6 | https://github.com/FromDarkHell/BL3SaveEditor/releases/latest
7 | false
8 |
9 |
10 |
--------------------------------------------------------------------------------
/BL3SaveEditor/BL3SaveEditor.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {A363B5AB-89B8-40F7-85A9-6A7341E183EF}
8 | WinExe
9 | BL3SaveEditor
10 | BL3SaveEditor
11 | v4.7.2
12 | 512
13 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
14 | 4
15 | true
16 | true
17 |
18 |
19 | AnyCPU
20 | true
21 | full
22 | false
23 | bin\Debug\
24 | DEBUG;TRACE
25 | prompt
26 | 4
27 | true
28 |
29 |
30 | AnyCPU
31 | pdbonly
32 | true
33 | bin\Release\
34 | TRACE
35 | prompt
36 | 4
37 | true
38 |
39 |
40 | Borderlands 3 002 512 x 512 ICO.ico
41 |
42 |
43 | bin\Single File\
44 | SINGLE_FILE
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | 4.0
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | MSBuild:Compile
70 | Designer
71 |
72 |
73 | App.xaml
74 | Code
75 |
76 |
77 | Designer
78 | MSBuild:Compile
79 |
80 |
81 | IntegerMessageBox.xaml
82 |
83 |
84 | Designer
85 | MSBuild:Compile
86 |
87 |
88 | ItemBalanceChanger.xaml
89 |
90 |
91 | MSBuild:Compile
92 | Designer
93 |
94 |
95 | DebugConsole.xaml
96 |
97 |
98 | MSBuild:Compile
99 | Designer
100 |
101 |
102 | MainWindow.xaml
103 |
104 |
105 | Designer
106 | MSBuild:Compile
107 |
108 |
109 |
110 |
111 |
112 |
113 | Code
114 |
115 |
116 | True
117 | True
118 | Resources.resx
119 |
120 |
121 | True
122 | Settings.settings
123 | True
124 |
125 |
126 | ResXFileCodeGenerator
127 | Resources.Designer.cs
128 |
129 |
130 | SettingsSingleFileGenerator
131 | Settings.Designer.cs
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 | {eefeab45-b558-4d33-9c74-d68660e26921}
159 | BL3Tools
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 | 1.17.0
205 |
206 |
207 | 1.17.0
208 |
209 |
210 | 1.7.0
211 |
212 |
213 | 5.4.0
214 | runtime; build; native; contentfiles; analyzers; buildtransitive
215 | all
216 |
217 |
218 | 4.1.0
219 |
220 |
221 | 6.5.2
222 | runtime; build; native; contentfiles; analyzers; buildtransitive
223 | all
224 |
225 |
226 | 13.0.1
227 |
228 |
229 | 3.1.0
230 |
231 |
232 | 3.0.101
233 |
234 |
235 | 4.5.1
236 |
237 |
238 | 1.7.1
239 |
240 |
241 | 4.5.4
242 |
243 |
244 | 4.5.0
245 |
246 |
247 | 4.5.3
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
--------------------------------------------------------------------------------
/BL3SaveEditor/Borderlands 3 002 512 x 512 ICO.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Borderlands 3 002 512 x 512 ICO.ico
--------------------------------------------------------------------------------
/BL3SaveEditor/Controls/IntegerMessageBox.xaml:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 |
17 |
18 |
19 |
21 |
23 |
25 |
28 |
30 |
31 |
--------------------------------------------------------------------------------
/BL3SaveEditor/Controls/IntegerMessageBox.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Windows;
7 | using System.Windows.Controls;
8 | using System.Windows.Data;
9 | using System.Windows.Documents;
10 | using System.Windows.Input;
11 | using System.Windows.Media;
12 | using System.Windows.Media.Imaging;
13 | using System.Windows.Shapes;
14 |
15 | namespace BL3SaveEditor.Controls {
16 | ///
17 | /// Interaction logic for IntegerMessageBox.xaml
18 | ///
19 | public partial class IntegerMessageBox {
20 |
21 | ///
22 | /// Minimum value for the integer message box
23 | ///
24 | public int Minimum { get; set; }
25 |
26 | ///
27 | /// Maximum value for the integer message box
28 | ///
29 | public int Maximum { get; set; }
30 |
31 | ///
32 | /// Whether or not the user agreed to save their inputted value
33 | ///
34 | public bool Succeeded { get; private set; }
35 |
36 | ///
37 | /// The last inputted value; regardless of whether or not the user saved/okayed the message box
38 | ///
39 | public int Result { get; set; }
40 |
41 | ///
42 | /// The message displayed to the users when they open the box
43 | ///
44 | public string Message { get; private set; }
45 |
46 | ///
47 | /// The text labelling the input box
48 | ///
49 | public string Label { get; private set; } = "";
50 |
51 | public IntegerMessageBox(string message, string label, int minimum = int.MinValue, int maximum = int.MaxValue, int defaultValue = 0) {
52 | InitializeComponent();
53 |
54 | this.Message = message;
55 | this.Label = label;
56 | this.Minimum = minimum;
57 | this.Maximum = maximum;
58 | this.Result = defaultValue;
59 |
60 | this.DataContext = null;
61 | this.DataContext = this;
62 | }
63 |
64 | private void OkBtn_Click(object sender, System.Windows.RoutedEventArgs e) {
65 | this.Succeeded = true;
66 | this.Close();
67 | }
68 |
69 | private void ExitBtn_Click(object sender, System.Windows.RoutedEventArgs e) {
70 | this.Succeeded = false;
71 | this.Close();
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/BL3SaveEditor/Controls/ItemBalanceChanger.xaml:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 |
17 |
18 |
19 |
21 |
23 |
25 |
27 |
29 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/BL3SaveEditor/Controls/ItemBalanceChanger.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Windows.Controls;
4 | using System.Windows.Data;
5 | using BL3Tools.GameData.Items;
6 |
7 | namespace BL3SaveEditor.Controls {
8 |
9 |
10 | ///
11 | /// A simple UI which allows the user to change an item balance based off of the type / balance
12 | ///
13 | public partial class ItemBalanceChanger {
14 |
15 | public ListCollectionView ItemTypes {
16 | get {
17 | var value = InventoryKeyDB.ItemTypeToKey.Keys.ToList();
18 | return new ListCollectionView(value);
19 | }
20 | }
21 |
22 | public ListCollectionView Balances {
23 | get {
24 | if (SelectedItemType == null || !InventoryKeyDB.ItemTypeToKey.ContainsKey(SelectedItemType)) return null;
25 | var itemKeys = InventoryKeyDB.ItemTypeToKey[SelectedItemType];
26 | var longNames = InventoryKeyDB.KeyDictionary.Where(x => itemKeys.Contains(x.Value)).Select(x => x.Key).ToList();
27 | var shortNames = longNames.Select(x => InventorySerialDatabase.GetShortNameFromBalance(x)).Where(x => x != null).ToList();
28 |
29 | return new ListCollectionView(shortNames);
30 | }
31 | }
32 |
33 | private string _SelectedItemType = null;
34 | public string SelectedItemType {
35 | get {
36 | return _SelectedItemType;
37 | }
38 | set {
39 | _SelectedItemType = value;
40 | if (!IsStarted) return;
41 |
42 | this.DataContext = null;
43 | this.DataContext = this;
44 | }
45 | }
46 |
47 | public string SelectedBalance { get; set; }
48 | public string SelectedInventoryData { get; private set; }
49 |
50 | public bool IsStarted = false;
51 |
52 | public ItemBalanceChanger() {
53 | InitializeComponent();
54 |
55 | IsStarted = true;
56 | }
57 |
58 | public ItemBalanceChanger(string itemType, string balance) {
59 | InitializeComponent();
60 |
61 | this.SelectedItemType = itemType;
62 | this.SelectedBalance = balance;
63 |
64 | this.DataContext = null;
65 | this.DataContext = this;
66 |
67 | this.IsStarted = true;
68 | }
69 |
70 | private void ExitBtn_Click(object sender, System.Windows.RoutedEventArgs e) {
71 | SelectedItemType = null;
72 | SelectedBalance = null;
73 | SelectedInventoryData = null;
74 | this.Close();
75 | }
76 |
77 | private void SaveBtn_Click(object sender, System.Windows.RoutedEventArgs e) {
78 | string longName = InventorySerialDatabase.GetBalanceFromShortName(SelectedBalance);
79 | SelectedInventoryData = InventorySerialDatabase.GetInventoryDataByBalance(longName);
80 |
81 | this.Close();
82 | }
83 |
84 | private void BalanceBox_Selected(object sender, SelectionChangedEventArgs e) {
85 | if (!IsStarted) return;
86 |
87 | var box = (ComboBox)sender;
88 | if (box.SelectedItem == null && Balances != null) {
89 | box.SelectedIndex = 0;
90 | SelectedBalance = Balances.SourceCollection.OfType().FirstOrDefault();
91 | }
92 | }
93 |
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/BL3SaveEditor/Debug/DebugConsole.xaml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
16 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/BL3SaveEditor/Debug/DebugConsole.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.Windows;
4 | using BL3Tools.TMSUnpack;
5 | using MessageBox = AdonisUI.Controls.MessageBox;
6 | using MessageBoxResult = AdonisUI.Controls.MessageBoxResult;
7 | using MessageBoxButton = AdonisUI.Controls.MessageBoxButton;
8 | using MessageBoxImage = AdonisUI.Controls.MessageBoxImage;
9 | using Ookii.Dialogs.Wpf;
10 | using System.IO;
11 |
12 | namespace BL3SaveEditor.Debug {
13 | ///
14 | /// Interaction logic for DebugConsole.xaml
15 | ///
16 | public partial class DebugConsole : Window {
17 | public ConsoleRedirectWriter consoleRedirectWriter;
18 | public bool bClose = false;
19 |
20 | public DebugConsole() {
21 | this.Hide();
22 |
23 | InitializeComponent();
24 |
25 | consoleRedirectWriter = new ConsoleRedirectWriter(textBoxDebug);
26 | }
27 |
28 | protected override void OnClosing(CancelEventArgs e) {
29 | if(!bClose) {
30 | e.Cancel = true;
31 | this.Hide();
32 | }
33 | }
34 |
35 | private void Button_Click(object sender, RoutedEventArgs e) {
36 | try {
37 | var data = TMSUnpacker.DownloadFromURL();
38 | // Now we write the folders out
39 | var vx = new VistaFolderBrowserDialog() {
40 | SelectedPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location),
41 | ShowNewFolderButton = true
42 | };
43 |
44 | if(vx.ShowDialog() == true) {
45 | foreach(TMSArchive.TMSFile tmsFile in data.Files) {
46 | string filePath = Path.Combine(vx.SelectedPath, tmsFile.FileName);
47 | Directory.CreateDirectory(Path.GetDirectoryName(filePath));
48 |
49 | File.WriteAllBytes(filePath, tmsFile.Contents);
50 | }
51 | }
52 | }
53 | catch(Exception ex) {
54 | MessageBox.Show(string.Format("Error unpacking TMS: {0}", ex.Message));
55 | }
56 |
57 | return;
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/BL3SaveEditor/Debug/RedirectWriter.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.Controls;
8 |
9 | namespace BL3SaveEditor.Debug {
10 | public class RedirectWriter : StringWriter {
11 | public Action OnWrite;
12 |
13 | private void WriteGeneric(T value) { if (OnWrite != null) OnWrite(value.ToString()); }
14 |
15 | public override void Write(char value) { WriteGeneric(value); }
16 | public override void Write(string value) { WriteGeneric(value); }
17 | public override void Write(bool value) { WriteGeneric(value); }
18 | public override void Write(int value) { WriteGeneric(value); }
19 | public override void Write(double value) { WriteGeneric(value); }
20 | public override void Write(long value) { WriteGeneric(value); }
21 |
22 | private void WriteLineGeneric(T value) { if (OnWrite != null) OnWrite(value.ToString() + "\n"); }
23 | public override void WriteLine(char value) { WriteLineGeneric(value); }
24 | public override void WriteLine(string value) { WriteLineGeneric(value); }
25 | public override void WriteLine(bool value) { WriteLineGeneric(value); }
26 | public override void WriteLine(int value) { WriteLineGeneric(value); }
27 | public override void WriteLine(double value) { WriteLineGeneric(value); }
28 | public override void WriteLine(long value) { WriteLineGeneric(value); }
29 |
30 | public override void Write(char[] buffer, int index, int count) {
31 | base.Write(buffer, index, count);
32 | char[] buffer2 = new char[count]; // Ensures large buffers are not a problem
33 | for (int i = 0; i < count; i++) buffer2[i] = buffer[index + i];
34 | WriteGeneric(buffer2);
35 | }
36 |
37 | public override void WriteLine(char[] buffer, int index, int count) {
38 | base.Write(buffer, index, count);
39 | char[] buffer2 = new char[count]; // Ensures large buffers are not a problem
40 | for (int i = 0; i < count; i++) buffer2[i] = buffer[index + i];
41 | WriteLineGeneric(buffer2);
42 | }
43 | }
44 |
45 | public class ConsoleRedirectWriter : RedirectWriter {
46 | TextWriter consoleTextWriter; //keeps Visual Studio console in scope.
47 |
48 | public ConsoleRedirectWriter(TextBox c) {
49 | consoleTextWriter = Console.Out;
50 | this.OnWrite += delegate (string text) { consoleTextWriter.Write(text); c.Text += text; c.ScrollToEnd(); };
51 |
52 | Console.SetOut(this);
53 | }
54 |
55 | public void Release() {
56 | Console.SetOut(consoleTextWriter);
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/BL3SaveEditor/FodyWeavers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/fugue/clipboard-text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/fugue/clipboard-text.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/fugue/cross-script.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/fugue/cross-script.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/fugue/disk--plus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/fugue/disk--plus.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/fugue/disk--purple.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/fugue/disk--purple.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/fugue/disk-black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/fugue/disk-black.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/fugue/document-binary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/fugue/document-binary.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/fugue/document-copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/fugue/document-copy.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/fugue/documents-stack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/fugue/documents-stack.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/fugue/documents-text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/fugue/documents-text.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/fugue/folder-open-document.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/fugue/folder-open-document.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/fugue/wrench-screwdriver.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/fugue/wrench-screwdriver.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/AllModIcons_I10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/game/AllModIcons_I10.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/AllModIcons_I12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/game/AllModIcons_I12.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/AllModIcons_I2C.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/game/AllModIcons_I2C.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/AllModIcons_I2E.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/game/AllModIcons_I2E.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/AllModIcons_I30.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/game/AllModIcons_I30.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/BITMAP_ICO_Expansion_4k.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/game/BITMAP_ICO_Expansion_4k.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/ECHOComm_IE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/game/ECHOComm_IE.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/MinimapIcon_GalaxyMap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/game/MinimapIcon_GalaxyMap.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/MinimapIcon_LostFoundFull.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/game/MinimapIcon_LostFoundFull.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/MinimapIcon_MiniBoss.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/game/MinimapIcon_MiniBoss.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/MinimapIcon_Pinging_Loot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/game/MinimapIcon_Pinging_Loot.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/MinimapIcon_QuickChange.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/game/MinimapIcon_QuickChange.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/MinimapIcon_Respawn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/game/MinimapIcon_Respawn.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/MinimapIcon_SDUVendor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/game/MinimapIcon_SDUVendor.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/MissionIcon_TurnIn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/game/MissionIcon_TurnIn.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/promptIconDiamondKey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/game/promptIconDiamondKey.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/promptIconEridium.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/game/promptIconEridium.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/promptIconGoldenKey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/game/promptIconGoldenKey.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/icons/Borderlands 3 001 512 x 512 ICO.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/icons/Borderlands 3 001 512 x 512 ICO.ico
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/icons/Borderlands 3 001 512 x 512 PNG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/icons/Borderlands 3 001 512 x 512 PNG.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/icons/Borderlands 3 002 512 x 512 ICO.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/icons/Borderlands 3 002 512 x 512 ICO.ico
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/icons/Borderlands 3 002 512 x 512 PNG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/icons/Borderlands 3 002 512 x 512 PNG.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/icons/Borderlands 3 003 512 x 512 ICO.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/icons/Borderlands 3 003 512 x 512 ICO.ico
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/icons/Borderlands 3 003 512 x 512 PNG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/icons/Borderlands 3 003 512 x 512 PNG.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/icons/Borderlands 3 004 512 x 512 ICO.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/icons/Borderlands 3 004 512 x 512 ICO.ico
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/icons/Borderlands 3 004 512 x 512 PNG.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Icons/icons/Borderlands 3 004 512 x 512 PNG.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/icons/README.md:
--------------------------------------------------------------------------------
1 | All of these assets in this folder come courtesy of [BrokenNoah](https://www.deviantart.com/brokennoah/art/Borderlands-3-icons-797030087)
2 | Follow them on DeviantArt and their other platforms for all of their creative work!
--------------------------------------------------------------------------------
/BL3SaveEditor/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("Borderlands 3 Save Editor")]
11 | [assembly: AssemblyDescription("A save and profile editor for Borderlands 3")]
12 | [assembly: AssemblyConfiguration("")]
13 | [assembly: AssemblyCompany("FromDarkHell")]
14 | [assembly: AssemblyProduct("Borderlands 3 Save Editor")]
15 | [assembly: AssemblyCopyright("Copyright © 2021")]
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.1.3.0")]
55 | [assembly: AssemblyFileVersion("1.1.3.0")]
56 | [assembly: NeutralResourcesLanguage("en-US")]
57 |
--------------------------------------------------------------------------------
/BL3SaveEditor/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 BL3SaveEditor.Properties {
12 | using System;
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", "16.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("BL3SaveEditor.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/BL3SaveEditor/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 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/BL3SaveEditor/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 BL3SaveEditor.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.7.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 |
26 | [global::System.Configuration.UserScopedSettingAttribute()]
27 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
28 | [global::System.Configuration.DefaultSettingValueAttribute("True")]
29 | public bool bDarkModeEnabled {
30 | get {
31 | return ((bool)(this["bDarkModeEnabled"]));
32 | }
33 | set {
34 | this["bDarkModeEnabled"] = value;
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/BL3SaveEditor/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | True
7 |
8 |
9 |
--------------------------------------------------------------------------------
/BL3SaveEditor/Starters/Zane.sav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FromDarkHell/BL3SaveEditor/c044e6d6f1fd78b259f2147119fbe2cdbbc7a3ee/BL3SaveEditor/Starters/Zane.sav
--------------------------------------------------------------------------------
/BL3Tools/BL3Tools.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using IOTools;
4 | using ProtoBuf;
5 | using BL3Tools.GVAS;
6 | using BL3Tools.Decryption;
7 | using OakSave;
8 | using System.Linq;
9 | using BL3Tools.GameData.Items;
10 | using System.Collections.Generic;
11 |
12 | namespace BL3Tools {
13 |
14 | public static class BL3Tools {
15 | public class BL3Exceptions {
16 | public class InvalidSaveException : Exception {
17 | public InvalidSaveException() : base("Invalid BL3 Save") { }
18 | public InvalidSaveException(string saveGameType) : base(String.Format("Invalid BL3 Save Game Type: {0}", saveGameType)) { }
19 | public InvalidSaveException(Platform platform) : base(String.Format("Incorrectly decrypted save game using the {0} platform; Are you sure you're using the right one?", platform)) { }
20 | }
21 |
22 |
23 | public class SerialParseException : Exception {
24 | public bool knowCause = false;
25 |
26 | public SerialParseException() : base("Invalid BL3 Serial...") { }
27 | public SerialParseException(string serial) : base(String.Format("Invalid Serial: {0}", serial)) { }
28 | public SerialParseException(string serial, int version) : base(String.Format("Invalid Serial: \"{0}\"; Version: {1}", serial, version)) { knowCause = true; }
29 | public SerialParseException(string serial, int version, uint originalChecksum, uint calculatedChecksum) : base(String.Format("Invalid Serial: \"{0}\"; Serial Version: {1}; Checksum Difference: {2} vs {3}", serial, version, originalChecksum, calculatedChecksum)) { knowCause = true; }
30 |
31 | public SerialParseException(string serial, int version, int databaseVersion) : base(String.Format("Invalid Serial: \"{0}\"; Serial Version: {1}; Database Version: {2}", serial, version, databaseVersion)) { knowCause = true; }
32 |
33 | public SerialParseException(string serial, int version, int databaseVersion, string oddity) : base(String.Format("Invalid Serial: \"{0}\"; Serial Version: {1}; Database Version: {2}; Error: {3}", serial, version, databaseVersion, oddity)) { knowCause = true; }
34 |
35 | }
36 | }
37 |
38 | ///
39 | /// This function writes a UE3Save instance to the drive, deserializes it to the respective classes of BL3Profile or BL3Save
40 | ///
41 | /// A file path for which to load the file from
42 | /// Whether or not to backup the save on reading (Default: False)
43 | /// An instance of the respective type, all subclassed by a UE3Save instance
44 | public static UE3Save LoadFileFromDisk(string filePath, Platform platform = Platform.PC, bool bBackup = false) {
45 | UE3Save saveGame = null;
46 | Console.WriteLine("Reading new file: \"{0}\"", filePath);
47 | FileStream fs = new FileStream(filePath, FileMode.Open);
48 |
49 | IOWrapper io = new IOWrapper(fs, Endian.Little, 0x0000000);
50 | try {
51 | if (bBackup) {
52 | // Gonna use this byte array for backing up the save file
53 | byte[] originalBytes = io.ReadAll();
54 | io.Seek(0);
55 |
56 | // Backup the file
57 | File.WriteAllBytes(filePath + ".bak", originalBytes);
58 | }
59 |
60 | GVASSave saveData = Helpers.ReadGVASSave(io);
61 |
62 | // Throw an exception if the save is null somehow
63 | if(saveData == null) {
64 | throw new BL3Exceptions.InvalidSaveException();
65 | }
66 |
67 | // Read in the save data itself now
68 | string saveGameType = saveData.sgType;
69 | int remainingData = io.ReadInt32();
70 | Console.WriteLine("Length of data: {0}", remainingData);
71 | byte[] buffer = io.ReadBytes(remainingData);
72 |
73 | switch(saveGameType) {
74 | // Decrypt a profile
75 | case "BP_DefaultOakProfile_C":
76 | ProfileBogoCrypt.Decrypt(buffer, 0, remainingData, platform);
77 | saveGame = new BL3Profile(saveData, Serializer.Deserialize(new MemoryStream(buffer)));
78 | (saveGame as BL3Profile).Platform = platform;
79 | break;
80 | // Decrypt a save game
81 | case "OakSaveGame":
82 | SaveBogoCrypt.Decrypt(buffer, 0, remainingData, platform);
83 | saveGame = new BL3Save(saveData, Serializer.Deserialize(new MemoryStream(buffer)));
84 | (saveGame as BL3Save).Platform = platform;
85 | break;
86 | default:
87 | throw new BL3Exceptions.InvalidSaveException(saveGameType);
88 | }
89 | }
90 | catch(ProtoBuf.ProtoException ex) {
91 | // Typically this exception means that the user didn't properly give in the platform for their save
92 | if(ex.Message.StartsWith("Invalid wire-type (7);")) {
93 | throw new BL3Exceptions.InvalidSaveException(platform);
94 | }
95 |
96 | // Raise all other exceptions
97 | throw ex;
98 | }
99 | finally {
100 | // Close the buffer
101 | io.Close();
102 | }
103 | saveGame.filePath = filePath;
104 | return saveGame;
105 | }
106 |
107 | ///
108 | /// Writes a UE3Save instance to disk, serializing it to the respective protobuf type.
109 | ///
110 | /// An instance of a UE3Save for which to write out
111 | /// Whether or not to backup on writing (Default: True)
112 | /// Whether or not the file writing succeeded
113 | public static bool WriteFileToDisk(UE3Save saveGame, bool bBackup = true) {
114 | return WriteFileToDisk(saveGame.filePath, saveGame, bBackup);
115 | }
116 |
117 | ///
118 | /// Writes a UE3Save instance to disk, serializing it to the respective protobuf type.
119 | ///
120 | /// Filepath for which to write the out to
121 | /// An instance of a UE3Save for which to write out
122 | /// Whether or not to backup on writing (Default: True)
123 | /// Whether or not the file writing succeeded
124 |
125 | public static bool WriteFileToDisk(string filePath, UE3Save saveGame, bool bBackup = true) {
126 | Console.WriteLine("Writing file to disk...");
127 | FileStream fs = new FileStream(filePath, FileMode.Create);
128 | IOWrapper io = new IOWrapper(fs, Endian.Little, 0x0000000);
129 | try {
130 | Helpers.WriteGVASSave(io, saveGame.GVASData);
131 | byte[] result;
132 |
133 | Console.WriteLine("Writing profile of type: {0}", saveGame.GVASData.sgType);
134 |
135 | using (var stream = new MemoryStream()) {
136 | switch (saveGame.GVASData.sgType) {
137 | case "BP_DefaultOakProfile_C":
138 | // This is probably a little bit unsafe and costly but *ehh*?
139 | BL3Profile vx = (BL3Profile)saveGame;
140 |
141 | vx.Profile.BankInventoryLists.Clear();
142 | vx.Profile.BankInventoryLists.AddRange(vx.BankItems.Select(x => x.InventoryKey == null ? x.OriginalData.ItemSerialNumber : x.EncryptSerialToBytes()));
143 |
144 | vx.Profile.LostLootInventoryLists.Clear();
145 | vx.Profile.LostLootInventoryLists.AddRange(vx.LostLootItems.Select(x => x.InventoryKey == null ? x.OriginalData.ItemSerialNumber : x.EncryptSerialToBytes()));
146 |
147 | Serializer.Serialize(stream, vx.Profile);
148 | result = stream.ToArray();
149 | ProfileBogoCrypt.Encrypt(result, 0, result.Length, vx.Platform);
150 | break;
151 | case "OakSaveGame":
152 | BL3Save save = (BL3Save)saveGame;
153 | // Now we've got to update the underlying protobuf data's serial...
154 | foreach(Borderlands3Serial serial in save.InventoryItems) {
155 | var protobufItem = save.Character.InventoryItems.FirstOrDefault(x => ReferenceEquals(x, serial.OriginalData));
156 | if(protobufItem == default) {
157 | throw new BL3Exceptions.SerialParseException(serial.EncryptSerial(), serial.SerialVersion, serial.SerialDatabaseVersion);
158 | }
159 | protobufItem.ItemSerialNumber = serial.EncryptSerialToBytes();
160 | }
161 |
162 | Serializer.Serialize(stream, save.Character);
163 | result = stream.ToArray();
164 | SaveBogoCrypt.Encrypt(result, 0, result.Length, save.Platform);
165 | break;
166 | default:
167 | throw new BL3Exceptions.InvalidSaveException(saveGame.GVASData.sgType);
168 | }
169 | }
170 |
171 | io.WriteInt32(result.Length);
172 | io.WriteBytes(result);
173 | }
174 | finally {
175 | io.Close();
176 | }
177 |
178 | Console.WriteLine("Completed writing file...");
179 | return true;
180 | }
181 | }
182 |
183 | }
184 |
--------------------------------------------------------------------------------
/BL3Tools/BL3Tools.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Debug
7 | AnyCPU
8 | {EEFEAB45-B558-4D33-9C74-D68660E26921}
9 | Library
10 | Properties
11 | BL3Tools
12 | BL3Tools
13 | v4.7.2
14 | 512
15 | true
16 |
17 |
18 |
19 |
20 |
21 | true
22 | full
23 | false
24 | bin\Debug\
25 | DEBUG;TRACE
26 | prompt
27 | 4
28 | 8.0
29 |
30 |
31 | pdbonly
32 | true
33 | bin\Release\
34 | TRACE
35 | prompt
36 | 4
37 | 8.0
38 |
39 |
40 | true
41 |
42 |
43 | false
44 |
45 |
46 |
47 |
48 |
49 |
50 | false
51 |
52 |
53 | bin\Single File\
54 |
55 |
56 |
57 | ..\packages\SharpZipLib.1.3.2\lib\net45\ICSharpCode.SharpZipLib.dll
58 |
59 |
60 | ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll
61 |
62 |
63 |
64 | ..\packages\protobuf-net.3.0.101\lib\net461\protobuf-net.dll
65 |
66 |
67 | ..\packages\protobuf-net.Core.3.0.101\lib\net461\protobuf-net.Core.dll
68 |
69 |
70 |
71 | ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll
72 |
73 |
74 | ..\packages\System.Collections.Immutable.1.7.1\lib\net461\System.Collections.Immutable.dll
75 |
76 |
77 |
78 |
79 | ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll
80 |
81 |
82 |
83 | ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll
84 |
85 |
86 | ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.3\lib\net461\System.Runtime.CompilerServices.Unsafe.dll
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 | .editorconfig
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 | {b93ff5db-ffef-4f81-812e-f5fdf8cdfbbe}
154 | IOTools
155 |
156 |
157 |
158 |
159 |
160 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
161 |
162 |
163 |
164 |
165 |
166 |
167 | powershell -executionpolicy bypass -File "$(ProjectDir)FixProtobufs.ps1"
168 |
169 |
--------------------------------------------------------------------------------
/BL3Tools/Decryption/CRC32.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Security.Cryptography;
4 | using System.Globalization;
5 |
6 | namespace BL3Tools.Decryption {
7 | ///
8 | /// Performs 32-bit reversed cyclic redundancy checks.
9 | ///
10 | public class CRC32 {
11 | #region Fields
12 | ///
13 | /// Contains a cache of calculated checksum chunks.
14 | ///
15 | private static readonly uint[] checksumTable = {
16 | 0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9, 0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005,
17 | 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61, 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD,
18 | 0x4C11DB70, 0x48D0C6C7, 0x4593E01E, 0x4152FDA9, 0x5F15ADAC, 0x5BD4B01B, 0x569796C2, 0x52568B75,
19 | 0x6A1936C8, 0x6ED82B7F, 0x639B0DA6, 0x675A1011, 0x791D4014, 0x7DDC5DA3, 0x709F7B7A, 0x745E66CD,
20 | 0x9823B6E0, 0x9CE2AB57, 0x91A18D8E, 0x95609039, 0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5,
21 | 0xBE2B5B58, 0xBAEA46EF, 0xB7A96036, 0xB3687D81, 0xAD2F2D84, 0xA9EE3033, 0xA4AD16EA, 0xA06C0B5D,
22 | 0xD4326D90, 0xD0F37027, 0xDDB056FE, 0xD9714B49, 0xC7361B4C, 0xC3F706FB, 0xCEB42022, 0xCA753D95,
23 | 0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, 0xFF79A6F1, 0xE13EF6F4, 0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D,
24 | 0x34867077, 0x30476DC0, 0x3D044B19, 0x39C556AE, 0x278206AB, 0x23431B1C, 0x2E003DC5, 0x2AC12072,
25 | 0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16, 0x018AEB13, 0x054BF6A4, 0x0808D07D, 0x0CC9CDCA,
26 | 0x7897AB07, 0x7C56B6B0, 0x71159069, 0x75D48DDE, 0x6B93DDDB, 0x6F52C06C, 0x6211E6B5, 0x66D0FB02,
27 | 0x5E9F46BF, 0x5A5E5B08, 0x571D7DD1, 0x53DC6066, 0x4D9B3063, 0x495A2DD4, 0x44190B0D, 0x40D816BA,
28 | 0xACA5C697, 0xA864DB20, 0xA527FDF9, 0xA1E6E04E, 0xBFA1B04B, 0xBB60ADFC, 0xB6238B25, 0xB2E29692,
29 | 0x8AAD2B2F, 0x8E6C3698, 0x832F1041, 0x87EE0DF6, 0x99A95DF3, 0x9D684044, 0x902B669D, 0x94EA7B2A,
30 | 0xE0B41DE7, 0xE4750050, 0xE9362689, 0xEDF73B3E, 0xF3B06B3B, 0xF771768C, 0xFA325055, 0xFEF34DE2,
31 | 0xC6BCF05F, 0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686, 0xD5B88683, 0xD1799B34, 0xDC3ABDED, 0xD8FBA05A,
32 | 0x690CE0EE, 0x6DCDFD59, 0x608EDB80, 0x644FC637, 0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB,
33 | 0x4F040D56, 0x4BC510E1, 0x46863638, 0x42472B8F, 0x5C007B8A, 0x58C1663D, 0x558240E4, 0x51435D53,
34 | 0x251D3B9E, 0x21DC2629, 0x2C9F00F0, 0x285E1D47, 0x36194D42, 0x32D850F5, 0x3F9B762C, 0x3B5A6B9B,
35 | 0x0315D626, 0x07D4CB91, 0x0A97ED48, 0x0E56F0FF, 0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623,
36 | 0xF12F560E, 0xF5EE4BB9, 0xF8AD6D60, 0xFC6C70D7, 0xE22B20D2, 0xE6EA3D65, 0xEBA91BBC, 0xEF68060B,
37 | 0xD727BBB6, 0xD3E6A601, 0xDEA580D8, 0xDA649D6F, 0xC423CD6A, 0xC0E2D0DD, 0xCDA1F604, 0xC960EBB3,
38 | 0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, 0xB07DABA7, 0xAE3AFBA2, 0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B,
39 | 0x9B3660C6, 0x9FF77D71, 0x92B45BA8, 0x9675461F, 0x8832161A, 0x8CF30BAD, 0x81B02D74, 0x857130C3,
40 | 0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640, 0x4E8EE645, 0x4A4FFBF2, 0x470CDD2B, 0x43CDC09C,
41 | 0x7B827D21, 0x7F436096, 0x7200464F, 0x76C15BF8, 0x68860BFD, 0x6C47164A, 0x61043093, 0x65C52D24,
42 | 0x119B4BE9, 0x155A565E, 0x18197087, 0x1CD86D30, 0x029F3D35, 0x065E2082, 0x0B1D065B, 0x0FDC1BEC,
43 | 0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088, 0x2497D08D, 0x2056CD3A, 0x2D15EBE3, 0x29D4F654,
44 | 0xC5A92679, 0xC1683BCE, 0xCC2B1D17, 0xC8EA00A0, 0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB, 0xDBEE767C,
45 | 0xE3A1CBC1, 0xE760D676, 0xEA23F0AF, 0xEEE2ED18, 0xF0A5BD1D, 0xF464A0AA, 0xF9278673, 0xFDE69BC4,
46 | 0x89B8FD09, 0x8D79E0BE, 0x803AC667, 0x84FBDBD0, 0x9ABC8BD5, 0x9E7D9662, 0x933EB0BB, 0x97FFAD0C,
47 | 0xAFB010B1, 0xAB710D06, 0xA6322BDF, 0xA2F33668, 0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4,
48 | };
49 |
50 | #endregion
51 |
52 | #region Methods
53 |
54 | public static uint Get(string assetPath) {
55 | uint hash = 0x00;
56 | foreach (char c in assetPath.ToCharArray()) {
57 | char uc = char.ToUpper(c, CultureInfo.InvariantCulture);
58 | hash = checksumTable[(byte)(hash ^ (uc >> 0))] ^ (hash >> 8);
59 | hash = checksumTable[(byte)(hash ^ (uc >> 8))] ^ (hash >> 8);
60 | }
61 | return hash;
62 | }
63 |
64 | #endregion
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/BL3Tools/Decryption/ProfileBogoCrypt.cs:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2019 Rick (rick 'at' gibbed 'dot' us)
2 | *
3 | * This software is provided 'as-is', without any express or implied
4 | * warranty. In no event will the authors be held liable for any damages
5 | * arising from the use of this software.
6 | *
7 | * Permission is granted to anyone to use this software for any purpose,
8 | * including commercial applications, and to alter it and redistribute it
9 | * freely, subject to the following restrictions:
10 | *
11 | * 1. The origin of this software must not be misrepresented; you must not
12 | * claim that you wrote the original software. If you use this software
13 | * in a product, an acknowledgment in the product documentation would
14 | * be appreciated but is not required.
15 | *
16 | * 2. Altered source versions must be plainly marked as such, and must not
17 | * be misrepresented as being the original software.
18 | *
19 | * 3. This notice may not be removed or altered from any source
20 | * distribution.
21 | */
22 |
23 | using System;
24 | using System.Collections.Generic;
25 |
26 | namespace BL3Tools.Decryption {
27 |
28 | internal static class ProfileBogoCrypt {
29 |
30 | public static Dictionary> EncryptionKeys = new Dictionary>() {
31 | { Platform.PC, new Dictionary() {
32 | { "PrefixMagic", new byte[] {
33 | 0xD8, 0x04, 0xB9, 0x08, 0x5C, 0x4E, 0x2B, 0xC0,
34 | 0x61, 0x9F, 0x7C, 0x8D, 0x5D, 0x34, 0x00, 0x56,
35 | 0xE7, 0x7B, 0x4E, 0xC0, 0xA4, 0xD6, 0xA7, 0x01,
36 | 0x14, 0x15, 0xA9, 0x93, 0x1F, 0x27, 0x2C, 0x8F } },
37 | { "XorMagic", new byte[] {
38 | 0xE8, 0xDC, 0x3A, 0x66, 0xF7, 0xEF, 0x85, 0xE0,
39 | 0xBD, 0x4A, 0xA9, 0x73, 0x57, 0x99, 0x30, 0x8C,
40 | 0x94, 0x63, 0x59, 0xA8, 0xC9, 0xAE, 0xD9, 0x58,
41 | 0x7D, 0x51, 0xB0, 0x1E, 0xBE, 0xD0, 0x77, 0x43 } },
42 | }},
43 | { Platform.PS4, new Dictionary() {
44 | { "PrefixMagic", new byte[] {
45 | 0xAD, 0x1E, 0x60, 0x4E, 0x42, 0x9E, 0xA9, 0x33,
46 | 0xB2, 0xF5, 0x01, 0xE1, 0x02, 0x4D, 0x08, 0x75,
47 | 0xB1, 0xAD, 0x1A, 0x3D, 0xA1, 0x03, 0x6B, 0x1A,
48 | 0x17, 0xE6, 0xEC, 0x0F, 0x60, 0x8D, 0xB4, 0xF9 } },
49 | { "XorMagic", new byte[] {
50 | 0xBA, 0x0E, 0x86, 0x1D, 0x58, 0xE1, 0x92, 0x21,
51 | 0x30, 0xD6, 0xCB, 0xF0, 0xD0, 0x82, 0xD5, 0x58,
52 | 0x36, 0x12, 0xE1, 0xF6, 0x39, 0x44, 0x88, 0xEA,
53 | 0x4E, 0xFB, 0x04, 0x74, 0x07, 0x95, 0x3A, 0xA2 } },
54 | }},
55 | };
56 |
57 | public static void Encrypt(byte[] buffer, int offset, int length, Platform platform = Platform.PC) {
58 | if (buffer == null) throw new ArgumentNullException(nameof(buffer));
59 | if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset));
60 | if (length < 0) throw new ArgumentOutOfRangeException(nameof(length));
61 | if (offset > buffer.Length) throw new ArgumentOutOfRangeException(nameof(offset));
62 | if (offset + length > buffer.Length) throw new ArgumentOutOfRangeException(nameof(length));
63 | if (length == 0) return;
64 |
65 | byte[] _PrefixMagic = EncryptionKeys[platform]["PrefixMagic"];
66 | byte[] _XorMagic = EncryptionKeys[platform]["XorMagic"];
67 |
68 | for (int i = 0, o = offset; i < length; i++, o++) {
69 | byte b = i < 32 ? _PrefixMagic[i] : buffer[o - 32];
70 | b ^= _XorMagic[o % 32];
71 | buffer[o] ^= b;
72 | }
73 | }
74 |
75 | public static void Decrypt(byte[] buffer, int offset, int length, Platform platform = Platform.PC) {
76 | if (buffer == null) throw new ArgumentNullException(nameof(buffer));
77 | if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset));
78 | if (length < 0) throw new ArgumentOutOfRangeException(nameof(length));
79 | if (offset > buffer.Length) throw new ArgumentOutOfRangeException(nameof(offset));
80 | if (offset + length > buffer.Length) throw new ArgumentOutOfRangeException(nameof(length));
81 | if (length == 0) return;
82 |
83 | byte[] _PrefixMagic = EncryptionKeys[platform]["PrefixMagic"];
84 | byte[] _XorMagic = EncryptionKeys[platform]["XorMagic"];
85 |
86 | for (int i = length - 1, o = offset + i; i >= 0; i--, o--) {
87 | byte b = i < 32 ? _PrefixMagic[i] : buffer[o - 32];
88 | b ^= _XorMagic[o % 32];
89 | buffer[o] ^= b;
90 | }
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/BL3Tools/Decryption/SaveBogoCrypt.cs:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2019 Rick (rick 'at' gibbed 'dot' us)
2 | *
3 | * This software is provided 'as-is', without any express or implied
4 | * warranty. In no event will the authors be held liable for any damages
5 | * arising from the use of this software.
6 | *
7 | * Permission is granted to anyone to use this software for any purpose,
8 | * including commercial applications, and to alter it and redistribute it
9 | * freely, subject to the following restrictions:
10 | *
11 | * 1. The origin of this software must not be misrepresented; you must not
12 | * claim that you wrote the original software. If you use this software
13 | * in a product, an acknowledgment in the product documentation would
14 | * be appreciated but is not required.
15 | *
16 | * 2. Altered source versions must be plainly marked as such, and must not
17 | * be misrepresented as being the original software.
18 | *
19 | * 3. This notice may not be removed or altered from any source
20 | * distribution.
21 | */
22 |
23 |
24 | using System;
25 | using System.Collections.Generic;
26 |
27 | namespace BL3Tools.Decryption {
28 | internal static class SaveBogoCrypt {
29 |
30 | public static Dictionary> EncryptionKeys = new Dictionary>() {
31 | { Platform.PC, new Dictionary() {
32 | { "PrefixMagic", new byte[] {
33 | 0x71, 0x34, 0x36, 0xB3, 0x56, 0x63, 0x25, 0x5F,
34 | 0xEA, 0xE2, 0x83, 0x73, 0xF4, 0x98, 0xB8, 0x18,
35 | 0x2E, 0xE5, 0x42, 0x2E, 0x50, 0xA2, 0x0F, 0x49,
36 | 0x87, 0x24, 0xE6, 0x65, 0x9A, 0xF0, 0x7C, 0xD7 } },
37 | { "XorMagic", new byte[] {
38 | 0x7C, 0x07, 0x69, 0x83, 0x31, 0x7E, 0x0C, 0x82,
39 | 0x5F, 0x2E, 0x36, 0x7F, 0x76, 0xB4, 0xA2, 0x71,
40 | 0x38, 0x2B, 0x6E, 0x87, 0x39, 0x05, 0x02, 0xC6,
41 | 0xCD, 0xD8, 0xB1, 0xCC, 0xA1, 0x33, 0xF9, 0xB6 } },
42 | }},
43 | { Platform.PS4, new Dictionary() {
44 | { "PrefixMagic", new byte[] {
45 | 0xD1, 0x7B, 0xBF, 0x75, 0x4C, 0xC1, 0x80, 0x30,
46 | 0x37, 0x92, 0xBD, 0xD0, 0x18, 0x3E, 0x4A, 0x5F,
47 | 0x43, 0xA2, 0x46, 0xA0, 0xED, 0xDB, 0x2d, 0x9F,
48 | 0x56, 0x5F, 0x8B, 0x3D, 0x6E, 0x73, 0xE6, 0xB8 } },
49 | { "XorMagic", new byte[] {
50 | 0xFB, 0xFD, 0xFD, 0x51, 0x3A, 0x5C, 0xDB, 0x20,
51 | 0xBB, 0x5E, 0xC7, 0xAF, 0x66, 0x6F, 0xB6, 0x9A,
52 | 0x9A, 0x52, 0x67, 0x0F, 0x19, 0x5D, 0xD3, 0x84,
53 | 0x15, 0x19, 0xC9, 0x4A, 0x79, 0x67, 0xDA, 0x6D } },
54 | }},
55 | };
56 |
57 | public static void Encrypt(byte[] buffer, int offset, int length, Platform platform = Platform.PC) {
58 | if (buffer == null) throw new ArgumentNullException(nameof(buffer));
59 | if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset));
60 | if (length < 0) throw new ArgumentOutOfRangeException(nameof(length));
61 | if (offset > buffer.Length) throw new ArgumentOutOfRangeException(nameof(offset));
62 | if (offset + length > buffer.Length) throw new ArgumentOutOfRangeException(nameof(length));
63 | if (length == 0) return;
64 |
65 | byte[] _PrefixMagic = EncryptionKeys[platform]["PrefixMagic"];
66 | byte[] _XorMagic = EncryptionKeys[platform]["XorMagic"];
67 |
68 | for (int i = 0, o = offset; i < length; i++, o++) {
69 | byte b = i < 32 ? _PrefixMagic[i] : buffer[o - 32];
70 | b ^= _XorMagic[o % 32];
71 | buffer[o] ^= b;
72 | }
73 | }
74 |
75 | public static void Decrypt(byte[] buffer, int offset, int length, Platform platform = Platform.PC) {
76 | if (buffer == null) throw new ArgumentNullException(nameof(buffer));
77 | if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset));
78 | if (length < 0) throw new ArgumentOutOfRangeException(nameof(length));
79 | if (offset > buffer.Length) throw new ArgumentOutOfRangeException(nameof(offset));
80 | if (offset + length > buffer.Length) throw new ArgumentOutOfRangeException(nameof(length));
81 | if (length == 0) return;
82 |
83 | byte[] _PrefixMagic = EncryptionKeys[platform]["PrefixMagic"];
84 | byte[] _XorMagic = EncryptionKeys[platform]["XorMagic"];
85 |
86 | for (int i = length - 1, o = offset + i; i >= 0; i--, o--) {
87 | byte b = i < 32 ? _PrefixMagic[i] : buffer[o - 32];
88 | b ^= _XorMagic[o % 32];
89 | buffer[o] ^= b;
90 | }
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/BL3Tools/FixProtobufs.ps1:
--------------------------------------------------------------------------------
1 | Write-Host "Fixing protobuf...";
2 |
3 | Get-ChildItem "../../obj/" -Recurse -Filter *.cs |
4 | Foreach-Object {
5 | $fileName = $_.FullName
6 | if($fileName -Match "Protobufs") {
7 | Write-Host ("Parsing file: " + $_.FullName)
8 |
9 | ((Get-Content -path $_.FullName -Raw) -replace '{ get; }','{ get; set; }') | Set-Content -Path $_.FullName
10 | }
11 | }
--------------------------------------------------------------------------------
/BL3Tools/GVAS/GVASSave.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace BL3Tools.GVAS {
4 | public class GVASSave {
5 | public int sg { get; set; }
6 | public int pkg { get; set; }
7 | public short mj { get; set; }
8 | public short mn { get; set; }
9 | public short pa { get; set; }
10 | public uint eng { get; set; }
11 | public string build { get; set; }
12 | public int fmt { get; set; }
13 | public int fmtLength { get; set; }
14 | public Dictionary fmtData { get; set; }
15 | public string sgType { get; set; }
16 |
17 | public GVASSave(int sg, int pkg, short mj, short mn, short pa, uint eng, string build, int fmt, int fmtlength, Dictionary fmtData, string sgType) {
18 | this.sg = sg;
19 | this.pkg = pkg;
20 | this.mj = mj;
21 | this.mn = mn;
22 | this.pa = pa;
23 | this.eng = eng;
24 | this.build = build;
25 | this.fmt = fmt;
26 | this.fmtLength = fmtlength;
27 | this.fmtData = fmtData;
28 | this.sgType = sgType;
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/BL3Tools/GameData/Experience.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 BL3Tools.GameData {
8 |
9 | public static class PlayerXP {
10 |
11 | // PlayerExperienceFormula={Multiplier: 60.0, Power: 2.799999952316284, Offset: 7.329999923706055}
12 | private const float expMultiplier = 60.0f;
13 | private const float expPower = 2.799999952316284f;
14 | private const float expOffset = 7.329999923706055f;
15 |
16 | public static int _XPMaximumLevel { get; } = 72;
17 | public static int _XPMinimumLevel { get; } = 1;
18 | private static readonly int _XPReduction = 0;
19 |
20 | private static Dictionary xpLevelTable = new Dictionary();
21 |
22 | private static int ComputeEXPLevel(int level) {
23 | return (int)Math.Floor((Math.Pow(level, expPower) + expOffset) * expMultiplier);
24 | }
25 |
26 | static PlayerXP() {
27 | _XPReduction = ComputeEXPLevel(_XPMinimumLevel);
28 |
29 | // Add to the dictionary
30 | for (int lvl = 1; lvl <= _XPMaximumLevel; lvl++) {
31 | xpLevelTable.Add(lvl, GetPointsForXPLevel(lvl));
32 | }
33 |
34 | }
35 |
36 | ///
37 | /// Gets the points for the associated XP level
38 | ///
39 | /// EXP Level
40 | /// The amount of EXP points for the associated amount of points
41 | public static int GetPointsForXPLevel(int lvl) {
42 | if (lvl <= _XPMinimumLevel) return 0;
43 |
44 | return ComputeEXPLevel(lvl) - _XPReduction;
45 | }
46 |
47 | ///
48 | /// Gets the respective level for a given amount of XP points
49 | ///
50 | /// Amount of XP Points
51 | /// The level associated with the points
52 | public static int GetLevelForPoints(int points) {
53 | if (points < 0) return _XPMinimumLevel;
54 | if (points >= xpLevelTable.Last().Value) return _XPMaximumLevel;
55 |
56 | // Get the closest level to the point amounts (price is right rules)
57 | return xpLevelTable.First(lv => points < lv.Value).Key - 1;
58 | }
59 | }
60 |
61 | public static class MayhemLevel {
62 | public static readonly int MinimumLevel = 0;
63 | public static readonly int MaximumLevel = 10;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/BL3Tools/GameData/Items/InventoryKeyDB.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Reflection;
4 | using Newtonsoft.Json.Linq;
5 | using System.Collections.Generic;
6 | using Newtonsoft.Json;
7 | using System.Linq;
8 |
9 | namespace BL3Tools.GameData.Items {
10 | public static class InventoryKeyDB {
11 | ///
12 | /// A JObject representing the InventoryKey DB as loaded from JSON
13 | ///
14 | private static JObject KeyDatabase { get; set; } = null;
15 |
16 | ///
17 | /// An easy to use dictionary mapping the balances to a SerialDB key as loaded from the DB.
18 | ///
19 | public static Dictionary KeyDictionary { get; private set; } = null;
20 |
21 | public static Dictionary> ItemTypeToKey { get; private set; } = null;
22 |
23 | private static readonly string embeddedDatabasePath = "BL3Tools.GameData.Items.Mappings.balance_to_inv_key.json";
24 |
25 | static InventoryKeyDB() {
26 | Console.WriteLine("Initializing InventoryKeyDB...");
27 |
28 | Assembly me = typeof(BL3Tools).Assembly;
29 |
30 | using (Stream stream = me.GetManifestResourceStream(embeddedDatabasePath))
31 | using (StreamReader reader = new StreamReader(stream)) {
32 | string result = reader.ReadToEnd();
33 |
34 | LoadInventoryKeyDatabase(result);
35 | }
36 | }
37 |
38 | ///
39 | /// Replace the inventory serial database info with the one specified in this specific string
40 | ///
41 | /// A JSON string representing the InventorySerialDB
42 | /// Whether or not the loading succeeded
43 | public static bool LoadInventoryKeyDatabase(string JSONString) {
44 | var lastDB = KeyDatabase;
45 | try {
46 | JObject db = JObject.FromObject(JsonConvert.DeserializeObject(JSONString));
47 | KeyDictionary = db.ToObject>();
48 | KeyDatabase = db;
49 |
50 | var invKeys = KeyDictionary.Values.Distinct();
51 |
52 | ItemTypeToKey = new Dictionary>() {
53 | { "Grenades", invKeys.Where(x => x.Contains("_GrenadeMod_")).ToList() },
54 | { "Shields", invKeys.Where(x => x.Contains("_Shield_")).ToList() },
55 | { "Class Mods", invKeys.Where(x => x.Contains("_ClassMod_")).ToList() },
56 | { "Artifacts", invKeys.Where(x => x.Contains("_Artifact_")).ToList() },
57 | { "Assault Rifles", invKeys.Where(x => x.Contains("_AR_")).ToList() },
58 | { "Pistols", invKeys.Where(x => x.Contains("_Pistol_") || x.Contains("_PS_")).ToList() },
59 | { "SMGs", invKeys.Where(x => x.Contains("_SM_") || x.Contains("_SMG")).ToList() },
60 | { "Heavy Weapons", invKeys.Where(x => x.Contains("_HW_")).ToList() },
61 | { "Shotguns", invKeys.Where(x => x.Contains("_SG_") || x.Contains("_Shotgun_")).ToList() },
62 | { "Sniper Rifles", invKeys.Where(x => x.Contains("_SR_")).ToList() },
63 | { "Eridian Fabricator", invKeys.Where(x => x.Contains("EridianFabricator")).ToList() },
64 | { "Customizations", invKeys.Where(x => x.Contains("Customization")).ToList() }
65 | };
66 |
67 | return true;
68 | }
69 | catch (Exception) {
70 | KeyDatabase = lastDB;
71 | }
72 |
73 | return false;
74 | }
75 |
76 |
77 | public static string GetKeyForBalance(string balance) {
78 |
79 | // Check if the name exists by default
80 | if (!balance.Contains(".")) balance = $"{balance}.{balance.Split('/').LastOrDefault()}";
81 |
82 | if (KeyDictionary.ContainsKey(balance))
83 | return KeyDictionary[balance];
84 | else if (KeyDictionary.ContainsKey(balance.ToLower()))
85 | return KeyDictionary[balance.ToLower()];
86 |
87 | return null;
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/BL3Tools/GameData/Items/InventoryNameDatabase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Reflection;
4 | using Newtonsoft.Json.Linq;
5 | using System.Collections.Generic;
6 | using Newtonsoft.Json;
7 | using System.Linq;
8 |
9 | namespace BL3Tools.GameData.Items {
10 | public static class InventoryNameDatabase {
11 | ///
12 | /// A JObject representing the Name DB as loaded from JSON
13 | ///
14 | private static JObject NameDatabase { get; set; } = null;
15 |
16 | ///
17 | /// An easy to use dictionary mapping a part to a ""human safe"" name as loaded from the DB.
18 | ///
19 | public static Dictionary NameDictionary { get; private set; } = null;
20 |
21 | ///
22 | /// A JObject representing the prefix DB as loaded from the JSON
23 | ///
24 | private static JObject PrefixDatabase { get; set; } = null;
25 |
26 | ///
27 | /// An easy to use dictionary that maps a part to a given prefix
28 | ///
29 | public static Dictionary PrefixDictionary { get; private set; } = null;
30 |
31 | private static readonly string embeddedNameDatabase = "BL3Tools.GameData.Items.Mappings.part_name_mapping.json";
32 | private static readonly string embeddedPrefixDatabase = "BL3Tools.GameData.Items.Mappings.prefix_name_mapping.json";
33 |
34 | static InventoryNameDatabase() {
35 | Console.WriteLine("Initializing InventoryNameDatabase...");
36 |
37 | Assembly me = typeof(BL3Tools).Assembly;
38 |
39 | using (Stream stream = me.GetManifestResourceStream(embeddedNameDatabase))
40 | using (StreamReader reader = new StreamReader(stream)) {
41 | string result = reader.ReadToEnd();
42 |
43 | LoadInventoryNameDatabase(result);
44 | }
45 |
46 | using (Stream stream = me.GetManifestResourceStream(embeddedPrefixDatabase))
47 | using (StreamReader reader = new StreamReader(stream)) {
48 | string result = reader.ReadToEnd();
49 |
50 | LoadInventoryPrefixDatabase(result);
51 | }
52 | }
53 |
54 | ///
55 | /// Replace the inventory serial database info with the one specified in this specific string
56 | ///
57 | /// A JSON string representing the InventorySerialDB
58 | /// Whether or not the loading succeeded
59 | public static bool LoadInventoryNameDatabase(string JSONString) {
60 | var lastDB = NameDatabase;
61 | try {
62 | JObject db = JObject.FromObject(JsonConvert.DeserializeObject(JSONString));
63 | NameDictionary = db.ToObject>();
64 | NameDatabase = db;
65 |
66 | return true;
67 | }
68 | catch (Exception) {
69 | NameDatabase = lastDB;
70 | }
71 |
72 | return false;
73 | }
74 |
75 | public static bool LoadInventoryPrefixDatabase(string JSONString) {
76 | var lastDB = PrefixDatabase;
77 | try {
78 | JObject db = JObject.FromObject(JsonConvert.DeserializeObject(JSONString));
79 | PrefixDictionary = db.ToObject>();
80 | PrefixDatabase = db;
81 |
82 | return true;
83 | }
84 | catch (Exception) {
85 | PrefixDatabase = lastDB;
86 | }
87 |
88 | return false;
89 | }
90 |
91 | ///
92 | /// Gets the associated ""human safe"" (non code) name for the given set of parts;
93 | /// You CAN include a customization balance in the parts and you will get a customization part instead of calling .
94 | ///
95 | /// A list of parts
96 | /// Whether or not to include prefixes in the result
97 | public static string GetNameForParts(List parts, bool bPrefixes = true) {
98 | string titleName = NameDictionary.Where(x => parts.Contains(x.Key)).Select(x => x.Value).LastOrDefault();
99 | string prefix = !bPrefixes ? null : PrefixDictionary.Where(x => parts.Contains(x.Key)).Select(x => x.Value).LastOrDefault();
100 | if (string.IsNullOrEmpty(titleName)) return null;
101 | else if (!string.IsNullOrEmpty(prefix))
102 | return (prefix + " " + titleName);
103 |
104 | return titleName;
105 | }
106 |
107 | ///
108 | /// Gets the associated ""human safe"" (non-code) name for a customization balance
109 | /// Since customizations don't have parts, this is the function you want to use in the event you need the name of a customization.
110 | ///
111 | /// The balance for the customization
112 | /// The human safe name of the balance
113 | public static string GetCustomizationNameForBalance(string balance) {
114 | string name = NameDictionary.Where(x => x.Key.Equals(balance)).Select(x => x.Value).LastOrDefault();
115 | if (string.IsNullOrEmpty(name)) return null;
116 |
117 | return name;
118 | }
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/BL3Tools/GameData/Items/Mappings/README.md:
--------------------------------------------------------------------------------
1 | This data was generated using custom PythonSDK scripts available in the `Tools` section of this project.
2 | Please see that for more info.
3 |
--------------------------------------------------------------------------------
/BL3Tools/GameData/Mappings/fast_travel_to_name.json:
--------------------------------------------------------------------------------
1 | {"/Game/PatchDLC/Takedown2/GameData/LevelTravel/FTS_TD2DropPod1.FTS_TD2DropPod1": "The Shattered Tribunal - Drop Pod", "/Game/PatchDLC/Takedown2/GameData/LevelTravel/FTS_TD2.FTS_TD2": "Guardian Breach", "/Game/PatchDLC/Takedown2/GameData/LevelTravel/FTS_GuardianTD_SendOnly_2.FTS_GuardianTD_SendOnly_2": "Guardian Breach", "/Game/PatchDLC/Raid1/GameData/FastTravel/LevelTravelData/FTS_Raid1DropPod.FTS_Raid1DropPod": "Midnight's Cairn", "/Game/PatchDLC/Raid1/GameData/FastTravel/LevelTravelData/FTS_Raid1.FTS_Raid1": "Midnight's Cairn", "/Game/PatchDLC/Raid1/GameData/FastTravel/LevelTravelData/FTS_MaliwanTD_SendOnly.FTS_MaliwanTD_SendOnly": "Midnight's Cairn \u2013 The Godsgallows", "/Game/PatchDLC/Ixora2/GameData/FastTravel/LevelTravel/LTS_Ixora2_Eden6_OneWay.LTS_Ixora2_Eden6_OneWay": "Enoch's Grove", "/Game/PatchDLC/Ixora2/GameData/FastTravel/LevelTravel/FTS_Ixora2_SacrificeBoss_One.FTS_Ixora2_SacrificeBoss_One": "Darkthirst Dominion", "/Game/PatchDLC/Ixora2/GameData/FastTravel/LevelTravel/FTS_Ixora2_SacrificeBoss.FTS_Ixora2_SacrificeBoss": "Darkthirst Dominion", "/Game/PatchDLC/Ixora2/GameData/FastTravel/LevelTravel/FTS_Ixora2_Promethea_OneWay.FTS_Ixora2_Promethea_OneWay": "Eschaton Row", "/Game/PatchDLC/Ixora2/GameData/FastTravel/LevelTravel/FTS_Ixora2_PrometheaMystery.FTS_Ixora2_PrometheaMystery": "Eschaton Row", "/Game/PatchDLC/Ixora2/GameData/FastTravel/LevelTravel/FTS_Ixora2_Promethea.FTS_Ixora2_Promethea": "Eschaton Row", "/Game/PatchDLC/Ixora2/GameData/FastTravel/LevelTravel/FTS_Ixora2_PandoraMystery.FTS_Ixora2_PandoraMystery": "Karass Canyon", "/Game/PatchDLC/Ixora2/GameData/FastTravel/LevelTravel/FTS_Ixora2_NekroMystery_OneWay.FTS_Ixora2_NekroMystery_OneWay": "Scryer's Crypt", "/Game/PatchDLC/Ixora2/GameData/FastTravel/LevelTravel/FTS_Ixora2_NekroMystery.FTS_Ixora2_NekroMystery": "Scryer's Crypt", "/Game/PatchDLC/Ixora2/GameData/FastTravel/LevelTravel/FTS_Ixora2_Eden6Mystery.FTS_Ixora2_Eden6Mystery": "Enoch's Grove", "/Game/PatchDLC/Ixora/GameData/FastTravel/LevelTravel/FTS_GearUpMap_SendOnly.FTS_GearUpMap_SendOnly": "Stormblind Complex", "/Game/PatchDLC/Ixora/GameData/FastTravel/LevelTravel/FTS_GearUpMap.FTS_GearUpMap": "Stormblind Complex", "/Game/PatchDLC/Hibiscus/GameData/FastTravel/FTS_DLC2_Woods.FTS_DLC2_Woods": "The Cankerwood", "/Game/PatchDLC/Hibiscus/GameData/FastTravel/FTS_DLC2_Village_NearBar.FTS_DLC2_Village_NearBar": "Cursehaven \u2013 Olmstead Square", "/Game/PatchDLC/Hibiscus/GameData/FastTravel/FTS_DLC2_Village_NearArchives.FTS_DLC2_Village_NearArchives": "Cursehaven \u2013 Withernot Cemetery", "/Game/PatchDLC/Hibiscus/GameData/FastTravel/FTS_DLC2_VillageGardens.FTS_DLC2_VillageGardens": "Cursehaven \u2013 Bleak Terrace", "/Game/PatchDLC/Hibiscus/GameData/FastTravel/FTS_DLC2_VillageDistrict.FTS_DLC2_VillageDistrict": "Cursehaven \u2013 Lantern's Hook", "/Game/PatchDLC/Hibiscus/GameData/FastTravel/FTS_DLC2_Venue_OneWay.FTS_DLC2_Venue_OneWay": "Heart's Desire \u2013 Heart Chamber", "/Game/PatchDLC/Hibiscus/GameData/FastTravel/FTS_DLC2_Venue_MapStart.FTS_DLC2_Venue_MapStart": "Heart's Desire \u2013 Entrance", "/Game/PatchDLC/Hibiscus/GameData/FastTravel/FTS_DLC2_Venue.FTS_DLC2_Venue": "Heart's Desire \u2013 What Beats Beneath", "/Game/PatchDLC/Hibiscus/GameData/FastTravel/FTS_DLC2_Lake_Gondola.FTS_DLC2_Lake_Gondola": "Skittermaw Basin \u2013 Cursehaven Gondola", "/Game/PatchDLC/Hibiscus/GameData/FastTravel/FTS_DLC2_Lake_Excavation.FTS_DLC2_Lake_Excavation": "Skittermaw Basin \u2013 Nethes Mines", "/Game/PatchDLC/Hibiscus/GameData/FastTravel/FTS_DLC2_Lake_DropPod.FTS_DLC2_Lake_DropPod": "Skittermaw Basin", "/Game/PatchDLC/Hibiscus/GameData/FastTravel/FTS_DLC2_Lake_Amourette.FTS_DLC2_Lake_Amourette": "Skittermaw Basin \u2013 Clan Amourette", "/Game/PatchDLC/Hibiscus/GameData/FastTravel/FTS_DLC2_Camp_OneWay.FTS_DLC2_Camp_OneWay": "Negul Neshai \u2013 Xenocardiac Containment", "/Game/PatchDLC/Hibiscus/GameData/FastTravel/FTS_DLC2_Camp.FTS_DLC2_Camp": "Negul Neshai", "/Game/PatchDLC/Hibiscus/GameData/FastTravel/FTS_DLC2_Bar.FTS_DLC2_Bar": "The Lodge", "/Game/PatchDLC/Hibiscus/GameData/FastTravel/FTS_DLC2_Archives_OneWay.FTS_DLC2_Archives_OneWay": "Dustbound Archives \u2013 Founders' Office", "/Game/PatchDLC/Hibiscus/GameData/FastTravel/FTS_DLC2_Archive.FTS_DLC2_Archive": "Dustbound Archives", "/Game/PatchDLC/Geranium/GameData/FastTravel/FTS_Town_DLC3_Pod.FTS_Town_DLC3_Pod": "Vestige \u2013 Drop Pod", "/Game/PatchDLC/Geranium/GameData/FastTravel/FTS_Town_DLC3.FTS_Town_DLC3": "Vestige", "/Game/PatchDLC/Geranium/GameData/FastTravel/FTS_Lodge_DLC3_Mid.FTS_Lodge_DLC3_Mid": "Ashfall Peaks \u2013 Caldera Stronghold", "/Game/PatchDLC/Geranium/GameData/FastTravel/FTS_Lodge_DLC3_Boss.FTS_Lodge_DLC3_Boss": "Ashfall Peaks \u2013 Rose's Private Alcove", "/Game/PatchDLC/Geranium/GameData/FastTravel/FTS_Lodge_DLC3.FTS_Lodge_DLC3": "Ashfall Peaks", "/Game/PatchDLC/Geranium/GameData/FastTravel/FTS_Frontier_DLC3_GasStation.FTS_Frontier_DLC3_GasStation": "The Blastplains \u2013 Pump & Charge", "/Game/PatchDLC/Geranium/GameData/FastTravel/FTS_Frontier_DLC3_Garage.FTS_Frontier_DLC3_Garage": "The Blastplains", "/Game/PatchDLC/Geranium/GameData/FastTravel/FTS_Frontier_DLC3_Fort.FTS_Frontier_DLC3_Fort": "The Blastplains \u2013 Fort Kickwater", "/Game/PatchDLC/Geranium/GameData/FastTravel/FTS_Forest_DLC3_Mid.FTS_Forest_DLC3_Mid": "Obsidian Forest \u2013 Crone's Contentment", "/Game/PatchDLC/Geranium/GameData/FastTravel/FTS_Forest_DLC3.FTS_Forest_DLC3": "Obsidian Forest", "/Game/PatchDLC/Geranium/GameData/FastTravel/FTS_Facility_DLC3_SendOnly.FTS_Facility_DLC3_SendOnly": "Bloodsun Canyon \u2013 Materials Transport", "/Game/PatchDLC/Geranium/GameData/FastTravel/FTS_Facility_DLC3_mid.FTS_Facility_DLC3_Mid": "Bloodsun Canyon \u2013 Presentation Room", "/Game/PatchDLC/Geranium/GameData/FastTravel/FTS_Facility_DLC3.FTS_Facility_DLC3": "Bloodsun Canyon", "/Game/PatchDLC/Geranium/GameData/FastTravel/FTS_CraterBoss_DLC3_Boss.FTS_CraterBoss_DLC3_Boss": "Crater's Edge \u2013 Birth of Ruin", "/Game/PatchDLC/Geranium/GameData/FastTravel/FTS_CraterBoss_DLC3.FTS_CraterBoss_DLC3": "Crater's Edge", "/Game/PatchDLC/Event2/GameData/FastTravel/LevelTravelData/FTS_CartelHideout.FTS_CartelHideout": "Cartel Hideout", "/Game/PatchDLC/Dandelion/GameData/FastTravel/FTS_Trashtown_DLC1_Town.FTS_Trashtown_DLC1_Town": "The Compactor \u2013 Trashlantis", "/Game/PatchDLC/Dandelion/GameData/FastTravel/FTS_Trashtown_DLC1.FTS_Trashtown_DLC1": "The Compactor", "/Game/PatchDLC/Dandelion/GameData/FastTravel/FTS_TowerLair_DLC1_Scrooge.FTS_TowerLair_DLC1_Scrooge": "VIP Tower Stash", "/Game/PatchDLC/Dandelion/GameData/FastTravel/FTS_TowerLair_DLC1_Lobby.FTS_TowerLair_DLC1_Lobby": "VIP Tower Entrance", "/Game/PatchDLC/Dandelion/GameData/FastTravel/FTS_Strip_DLC1_Vice.FTS_Strip_DLC1_Vice": "The Spendopticon \u2013 Vice District", "/Game/PatchDLC/Dandelion/GameData/FastTravel/FTS_Strip_DLC1_TricksyNickArea.FTS_Strip_DLC1_TricksyNickArea": "The Spendopticon \u2013 Tricksy Nick's Hideout", "/Game/PatchDLC/Dandelion/GameData/FastTravel/FTS_Strip_DLC1_Market.FTS_Strip_DLC1_Market": "The Spendopticon \u2013 Market District", "/Game/PatchDLC/Dandelion/GameData/FastTravel/FTS_Strip_DLC1_Hideout.FTS_Strip_DLC1_Hideout": "The Spendopticon \u2013 Casa de Timothy", "/Game/PatchDLC/Dandelion/GameData/FastTravel/FTS_Strip_DLC1_Entrance.FTS_Strip_DLC1_Entrance": "The Spendopticon", "/Game/PatchDLC/Dandelion/GameData/FastTravel/FTS_Impound_DLC1_LowGrav.FTS_Impound_DLC1_LowGrav": "Impound Deluxe \u2013 Beggar's Berth", "/Game/PatchDLC/Dandelion/GameData/FastTravel/FTS_Impound_DLC1_Exit.FTS_Impound_DLC1_Exit": "Impound Deluxe \u2013 La Femme Br\u00fbl\u00e9e", "/Game/PatchDLC/Dandelion/GameData/FastTravel/FTS_Impound_DLC1.FTS_Impound_DLC1": "Impound Deluxe", "/Game/PatchDLC/Dandelion/GameData/FastTravel/FTS_Core_DLC1_Boss.FTS_Core_DLC1_Boss": "Jack's Secret \u2013 Prototype Testing Arena", "/Game/PatchDLC/Dandelion/GameData/FastTravel/FTS_Core_DLC1.FTS_Core_DLC1": "Jack's Secret", "/Game/PatchDLC/Dandelion/GameData/FastTravel/FTS_CasinoIntro_DLC1_Pod.FTS_CasinoIntro_DLC1_Pod": "Grand Opening", "/Game/PatchDLC/BloodyHarvest/GameData/FastTravel/LevelTravelData/FTS_BloodyHarvest.FTS_BloodyHarvest": "Bloody Harvest", "/Game/PatchDLC/Alisma/GameData/FastTravel/FTS_ALI_Sanctum_DropPod.FTS_ALI_Sanctum_DropPod": "The Psychoscape \u2013 The Screaming Meadow", "/Game/PatchDLC/Alisma/GameData/FastTravel/FTS_ALI_Sanctum.FTS_ALI_Sanctum": "The Psychoscape \u2013 Sanity's Sanctum", "/Game/PatchDLC/Alisma/GameData/FastTravel/FTS_ALI_Experiment_Boss.FTS_ALI_Experiment_Boss": "Benediction of Pain \u2013 The Clotted Stage", "/Game/PatchDLC/Alisma/GameData/FastTravel/FTS_ALI_Experiment.FTS_ALI_Experiment": "Benediction of Pain", "/Game/PatchDLC/Alisma/GameData/FastTravel/FTS_ALI_Eldorado_Boss.FTS_ALI_Eldorado_Boss": "Vaulthalla \u2013 Destroyer's Wake", "/Game/PatchDLC/Alisma/GameData/FastTravel/FTS_ALI_Eldorado.FTS_ALI_Eldorado": "Vaulthalla", "/Game/PatchDLC/Alisma/GameData/FastTravel/FTS_ALI_Chase_Boss.FTS_ALI_Chase_Boss": "Sapphire's Run \u2013 Coalheart Lair", "/Game/PatchDLC/Alisma/GameData/FastTravel/FTS_ALI_Chase.FTS_ALI_Chase": "Sapphire's Run", "/Game/PatchDLC/Alisma/GameData/FastTravel/FTS_ALI_Anger_Castle.FTS_ALI_Anger_Castle": "Castle Crimson \u2013 The Walls", "/Game/PatchDLC/Alisma/GameData/FastTravel/FTS_ALI_Anger_Boss.FTS_ALI_Anger_Boss": "Castle Crimson \u2013 Atop the Spire", "/Game/PatchDLC/Alisma/GameData/FastTravel/FTS_ALI_Anger.FTS_ALI_Anger": "Castle Crimson", "/Game/GameData/FastTravel/FTS_ZoneMapTest2.FTS_ZoneMapTest2": "Fast Travel Station B", "/Game/GameData/FastTravel/FTS_ZoneMapTest.FTS_ZoneMapTest": "Fast Travel Station A", "/Game/GameData/FastTravel/FTS_WetlandsVault.FTS_WetlandsVault": "Blackbarrel Cellars", "/Game/GameData/FastTravel/FTS_WetlandsDropPod.FTS_WetlandsDropPod": "Floodmoor Basin \u2013 Drop Pod", "/Game/GameData/FastTravel/FTS_WetlandsBoss_SendOnly.FTS_WetlandsBoss_SendOnly": "The Floating Tomb \u2013 Altar of the Guardians", "/Game/GameData/FastTravel/FTS_WetlandsBoss.FTS_WetlandsBoss": "The Floating Tomb", "/Game/GameData/FastTravel/FTS_Wetlands2.FTS_Wetlands2": "Floodmoor Basin \u2013 Reliance", "/Game/GameData/FastTravel/FTS_Wetlands1.FTS_Wetlands1": "Floodmoor Basin \u2013 Knotty Peak", "/Game/GameData/FastTravel/FTS_Watership_SendOnly.FTS_Watership_SendOnly": "Voracious Canopy \u2013 Bridge of the Jewel", "/Game/GameData/FastTravel/FTS_Watership.FTS_Watership": "Voracious Canopy", "/Game/GameData/FastTravel/FTS_Towers.FTS_Towers": "Lectra City", "/Game/GameData/FastTravel/FTS_TechSlaughterDropPod.FTS_TechSlaughterDropPod": "Slaughterstar 3000 \u2013 Drop Pod", "/Game/GameData/FastTravel/FTS_TechSlaughter.FTS_TechSlaughter": "Slaughterstar 3000", "/Game/GameData/FastTravel/FTS_SanctuaryBridge.FTS_SanctuaryBridge": "Sanctuary \u2013 Bridge", "/Game/GameData/FastTravel/FTS_Sanctuary.FTS_Sanctuary": "Sanctuary", "/Game/GameData/FastTravel/FTS_Sacrifice.FTS_Sacrifice": "Ascension Bluff", "/Game/GameData/FastTravel/FTS_Recruitment.FTS_Recruitment": "Covenant Pass", "/Game/GameData/FastTravel/FTS_Raid.FTS_Raid": "Raid", "/Game/GameData/FastTravel/FTS_ProvingGrounds08_OneWay.FTS_ProvingGrounds08_OneWay": "Proving Grounds \u2013 Instinct", "/Game/GameData/FastTravel/FTS_ProvingGrounds08_Droppod.FTS_ProvingGrounds08_Droppod": "Proving Grounds \u2013 Instinct", "/Game/GameData/FastTravel/FTS_ProvingGrounds08.FTS_ProvingGrounds08": "Proving Grounds \u2013 Instinct", "/Game/GameData/FastTravel/FTS_ProvingGrounds07_OneWay.FTS_ProvingGrounds07_OneWay": "Proving Grounds \u2013 Discipline", "/Game/GameData/FastTravel/FTS_ProvingGrounds07_Droppod.FTS_ProvingGrounds07_Droppod": "Proving Grounds \u2013 Discipline", "/Game/GameData/FastTravel/FTS_ProvingGrounds07.FTS_ProvingGrounds07": "Proving Grounds \u2013 Discipline", "/Game/GameData/FastTravel/FTS_ProvingGrounds06_OneWay.FTS_ProvingGrounds06_OneWay": "Proving Grounds \u2013 Supremacy", "/Game/GameData/FastTravel/FTS_ProvingGrounds06_Droppod.FTS_ProvingGrounds06_Droppod": "Proving Grounds \u2013 Supremacy", "/Game/GameData/FastTravel/FTS_ProvingGrounds06.FTS_ProvingGrounds06": "Proving Grounds \u2013 Supremacy", "/Game/GameData/FastTravel/FTS_ProvingGrounds05_OneWay.FTS_ProvingGrounds05_OneWay": "Proving Grounds \u2013 Cunning", "/Game/GameData/FastTravel/FTS_ProvingGrounds05_Droppod.FTS_ProvingGrounds05_Droppod": "Proving Grounds \u2013 Cunning", "/Game/GameData/FastTravel/FTS_ProvingGrounds05.FTS_ProvingGrounds05": "Proving Grounds \u2013 Cunning", "/Game/GameData/FastTravel/FTS_ProvingGrounds04_OneWay.FTS_ProvingGrounds04_OneWay": "Proving Grounds \u2013 Fervor", "/Game/GameData/FastTravel/FTS_ProvingGrounds04_Droppod.FTS_ProvingGrounds04_Droppod": "Proving Grounds \u2013 Fervor", "/Game/GameData/FastTravel/FTS_ProvingGrounds04.FTS_ProvingGrounds04": "Proving Grounds \u2013 Fervor", "/Game/GameData/FastTravel/FTS_ProvingGrounds03.FTS_ProvingGrounds03": "Proving Grounds 03", "/Game/GameData/FastTravel/FTS_ProvingGrounds02.FTS_ProvingGrounds02": "Proving Grounds 02", "/Game/GameData/FastTravel/FTS_ProvingGrounds01_OneWay.FTS_ProvingGrounds01_OneWay": "Proving Grounds \u2013 Survival", "/Game/GameData/FastTravel/FTS_ProvingGrounds01_Droppod.FTS_ProvingGrounds01_Droppod": "Proving Grounds \u2013 Survival", "/Game/GameData/FastTravel/FTS_ProvingGrounds01.FTS_ProvingGrounds01": "Proving Grounds \u2013 Survival", "/Game/GameData/FastTravel/FTS_ProvingGrounds00.FTS_ProvingGrounds00": "Proving Grounds 00", "/Game/GameData/FastTravel/FTS_Prologue2.FTS_Prologue2": "The Droughts \u2013 Highway", "/Game/GameData/FastTravel/FTS_Prologue.FTS_Prologue": "The Droughts", "/Game/GameData/FastTravel/FTS_Prison_SendOnly.FTS_Prison_SendOnly": "The Anvil \u2013 Ultramax Spire", "/Game/GameData/FastTravel/FTS_Prison.FTS_Prison": "The Anvil", "/Game/GameData/FastTravel/FTS_PlayableIntro_SendOnly.FTS_PlayableIntro_SendOnly": "Playable Intro SendOnly", "/Game/GameData/FastTravel/FTS_OutskirtsDropPod.FTS_OutskirtsDropPod": "Meridian Outskirts \u2013 Drop Pod", "/Game/GameData/FastTravel/FTS_Outskirts.FTS_Outskirts": "Meridian Outskirts", "/Game/GameData/FastTravel/FTS_OrbitalShuttle.FTS_OrbitalShuttle": "Skywell-27 \u2013 Shuttle", "/Game/GameData/FastTravel/FTS_OrbitalPlatform_SendOnly.FTS_OrbitalPlatform_SendOnly": "Skywell-27 \u2013 Laser Control Room", "/Game/GameData/FastTravel/FTS_OrbitalPlatform.FTS_OrbitalPlatform": "Skywell-27", "/Game/GameData/FastTravel/FTS_MotorcadeInterior_SendOnly.FTS_MotorcadeInterior_SendOnly": "Guts of Carnivora \u2013 Agonizer 9000", "/Game/GameData/FastTravel/FTS_MotorcadeInterior.FTS_MotorcadeInterior": "Guts of Carnivora", "/Game/GameData/FastTravel/FTS_MotorcadeFestival.FTS_MotorcadeFestival": "Carnivora", "/Game/GameData/FastTravel/FTS_Motorcade2.FTS_Motorcade2": "The Splinterlands \u2013 Chop Shop", "/Game/GameData/FastTravel/FTS_Motorcade.FTS_Motorcade": "The Splinterlands \u2013 Pitt's Stop", "/Game/GameData/FastTravel/FTS_Monastery_SendOnly.FTS_Monastery_SendOnly": "Athenas \u2013 The Anchorhold", "/Game/GameData/FastTravel/FTS_MonasteryDropPod.FTS_MonasteryDropPod": "Athenas \u2013 Drop Pod", "/Game/GameData/FastTravel/FTS_Monastery.FTS_Monastery": "Athenas", "/Game/GameData/FastTravel/FTS_Mine.FTS_Mine": "Konrad's Hold", "/Game/GameData/FastTravel/FTS_Marshfields_SendOnly.FTS_Marshfields_SendOnly": "Marshfields SendOnly", "/Game/GameData/FastTravel/FTS_MarshfieldsShip.FTS_MarshfieldsShip": "Ambermire \u2013 Rogue's Hollow", "/Game/GameData/FastTravel/FTS_Marshfields.FTS_Marshfields": "Ambermire", "/Game/GameData/FastTravel/FTS_Mansion.FTS_Mansion": "Jakobs Estate", "/Game/GameData/FastTravel/FTS_Grotto.FTS_Grotto": "Grotto", "/Game/GameData/FastTravel/FTS_FinalBoss_SendOnly.FTS_FinalBoss_SendOnly": "Destroyer's Rift \u2013 Crown of Tyrants", "/Game/GameData/FastTravel/FTS_FinalBossPortal.FTS_FinalBossPortal": "FinalBoss Portal", "/Game/GameData/FastTravel/FTS_FinalBoss.FTS_FinalBoss": "Destroyer's Rift", "/Game/GameData/FastTravel/FTS_DesolateDropPod.FTS_DesolateDropPod": "Desolation's Edge \u2013 Drop Pod", "/Game/GameData/FastTravel/FTS_Desolate2.FTS_Desolate2": "Desolation's Edge", "/Game/GameData/FastTravel/FTS_DesertVault.FTS_DesertVault": "Cathedral of the Twin Gods", "/Game/GameData/FastTravel/FTS_DesertBoss_SendOnly.FTS_DesertBoss_SendOnly": "The Great Vault \u2013 Dig Site", "/Game/GameData/FastTravel/FTS_DesertBoss.FTS_DesertBoss": "The Great Vault", "/Game/GameData/FastTravel/FTS_Desert2.FTS_Desert2": "Devil's Razor \u2013 Boomtown", "/Game/GameData/FastTravel/FTS_Desert1.FTS_Desert1": "Devil's Razor \u2013 Roland's Rest", "/Game/GameData/FastTravel/FTS_Crypt.FTS_Crypt": "The Pyre of Stars", "/Game/GameData/FastTravel/FTS_CreatureSlaughter.FTS_CreatureSlaughter": "Cistern of Slaughter", "/Game/GameData/FastTravel/FTS_COVSlaughter.FTS_COVSlaughter": "The Slaughter Shaft", "/Game/GameData/FastTravel/FTS_Convoy.FTS_Convoy": "Sandblast Scar", "/Game/GameData/FastTravel/FTS_CityVault.FTS_CityVault": "Neon Arterial", "/Game/GameData/FastTravel/FTS_CityBoss_SendOnly.FTS_CityBoss_SendOnly": "The Forgotten Basilica", "/Game/GameData/FastTravel/FTS_CityBoss.FTS_CityBoss": "The Forgotten Basilica", "/Game/GameData/FastTravel/FTS_City.FTS_City": "Meridian Metroplex", "/Game/GameData/FastTravel/FTS_Beach_SendOnly.FTS_Beach_SendOnly": "Tazendeer Ruins \u2013 An Eternal Silence", "/Game/GameData/FastTravel/FTS_Beach.FTS_Beach": "Tazendeer Ruins", "/Game/GameData/FastTravel/FTS_AtlasHQ_SendOnly.FTS_AtlasHQ_SendOnly": "Atlas HQ \u2013 Rooftop", "/Game/GameData/FastTravel/FTS_AtlasHQ.FTS_AtlasHQ": "Atlas HQ"}
--------------------------------------------------------------------------------
/BL3Tools/GameData/SDU.cs:
--------------------------------------------------------------------------------
1 | namespace BL3Tools.GameData {
2 | public static class SDU {
3 | public static int MaximumBankSDUs = 23;
4 | public static int MaximumLostLoot = 10;
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/BL3Tools/Helpers/Helpers.cs:
--------------------------------------------------------------------------------
1 | using BL3Tools.GVAS;
2 | using OakSave;
3 | using IOTools;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Runtime.InteropServices;
8 | using System.Text;
9 |
10 | namespace BL3Tools {
11 |
12 | public static partial class Helpers {
13 | private static readonly Encoding Utf8 = new UTF8Encoding(false);
14 |
15 | #region Reading/Writing UE Strings
16 |
17 | public static string ReadUEString(this IOWrapper io) {
18 | if (io.PeekChar() < 0) return null;
19 | int length = io.ReadInt32();
20 | if (length == 0) return null;
21 | if (length == 1) return "";
22 | var valueBytes = io.ReadBytes(length);
23 | return Utf8.GetString(valueBytes, 0, valueBytes.Length - 1);
24 | }
25 |
26 | public static void WriteUEString(this IOWrapper io, string str) {
27 | if (str == null) io.WriteInt32(0);
28 | else if (string.Empty.Equals(str)) io.WriteInt32(1);
29 | else {
30 | byte[] data = Utf8.GetBytes(str + '\0');
31 | io.WriteInt32(data.Length);
32 | io.WriteBytes(data);
33 | }
34 | }
35 |
36 | #endregion
37 |
38 | #region Reading/Writing GVAS Format
39 |
40 | public static GVASSave ReadGVASSave(IOWrapper io) {
41 | string header = io.ReadASCII(4);
42 | if (!header.Equals("GVAS")) return null;
43 | Console.WriteLine("Header: {0}", header);
44 | int sgVersion = io.ReadInt32();
45 | Console.WriteLine("Save Game Version: {0}", sgVersion);
46 | int pkgVersion = io.ReadInt32();
47 | Console.WriteLine("Package version: {0}", pkgVersion);
48 | short major = io.ReadInt16();
49 | short minor = io.ReadInt16();
50 | short patch = io.ReadInt16();
51 | uint engineBuild = io.ReadUInt32();
52 | Console.WriteLine("Engine version: {0}.{1}.{2}.{3}", major, minor, patch, engineBuild);
53 |
54 | string buildId = ReadUEString(io);
55 | Console.WriteLine("Build ID: {0}", buildId);
56 |
57 | int fmtVersion = io.ReadInt32();
58 | Console.WriteLine("Custom Format Version: {0}", fmtVersion);
59 | int fmtCount = io.ReadInt32();
60 | Console.WriteLine("Custom Format Data Count: {0}", fmtCount);
61 | Dictionary keyValuePairs = new Dictionary();
62 | for (int i = 0; i < fmtCount; i++) {
63 | byte[] guid = io.ReadBytes(16);
64 | int entry = io.ReadInt32();
65 | keyValuePairs.Add(guid, entry);
66 | }
67 |
68 | string sgType = ReadUEString(io);
69 | Console.WriteLine("Save Game Type: {0}", sgType);
70 | GVASSave saveData = new GVASSave(sgVersion, pkgVersion, major, minor, patch, engineBuild, buildId, fmtVersion, fmtCount, keyValuePairs, sgType);
71 |
72 | return saveData;
73 | }
74 |
75 | public static void WriteGVASSave(IOWrapper io, GVASSave saveData) {
76 | io.WriteASCII("GVAS");
77 | io.WriteInt32(saveData.sg);
78 | io.WriteInt32(saveData.pkg);
79 | io.WriteInt16(saveData.mj);
80 | io.WriteInt16(saveData.mn);
81 | io.WriteInt16(saveData.pa);
82 | io.WriteUInt32(saveData.eng);
83 | io.WriteUEString(saveData.build);
84 | io.WriteInt32(saveData.fmt);
85 | io.WriteInt32(saveData.fmtLength);
86 | foreach (KeyValuePair entry in saveData.fmtData) {
87 | io.WriteBytes(entry.Key);
88 | io.WriteInt32(entry.Value);
89 | }
90 | io.WriteUEString(saveData.sgType);
91 | }
92 | #endregion
93 |
94 | public static string GetCharacterString(this Character value) {
95 | Dictionary validClasses = BL3Save.ValidClasses;
96 | PlayerClassSaveGameData val = value.PlayerClassData;
97 | string result = validClasses.Where(x => x.Value.PlayerClassPath.Equals(val.PlayerClassPath)).First().Key;
98 | return result;
99 | }
100 |
101 | public static T[] ConcatArrays(params T[][] list) {
102 | var result = new T[list.Sum(a => a.Length)];
103 | int offset = 0;
104 | for (int x = 0; x < list.Length; x++) {
105 | list[x].CopyTo(result, offset);
106 | offset += list[x].Length;
107 | }
108 | return result;
109 | }
110 | }
111 |
112 | ///
113 | /// Contains approximate string matching
114 | ///
115 | static class LevenshteinDistance {
116 |
117 | ///
118 | /// Compute the distance between two strings.
119 | ///
120 | public static int Compute(string s, string t) {
121 | int n = s.Length;
122 | int m = t.Length;
123 | int[,] d = new int[n + 1, m + 1];
124 |
125 | // Step 1
126 | if (n == 0) return m;
127 |
128 | if (m == 0) return n;
129 |
130 | // Step 2
131 | for (int i = 0; i <= n; d[i, 0] = i++) ;
132 |
133 | for (int j = 0; j <= m; d[0, j] = j++) ;
134 |
135 | // Step 3
136 | for (int i = 1; i <= n; i++) {
137 | //Step 4
138 | for (int j = 1; j <= m; j++) {
139 | // Step 5
140 | int cost = (t[j - 1] == s[i - 1]) ? 0 : 1;
141 |
142 | // Step 6
143 | d[i, j] = Math.Min(
144 | Math.Min(d[i - 1, j] + 1, d[i, j - 1] + 1),
145 | d[i - 1, j - 1] + cost);
146 | }
147 | }
148 | // Step 7
149 | return d[n, m];
150 | }
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/BL3Tools/Platform.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 |
3 | namespace BL3Tools {
4 |
5 | ///
6 | /// Defines the platform that a given / can have
7 | /// Particularly used for encryption/decryption of the profile
8 | ///
9 | public enum Platform {
10 | ///
11 | /// A PC save/profile
12 | ///
13 | [Description("PC")]
14 | PC = 0x01,
15 |
16 | ///
17 | /// A decrypted PS4 save.
18 | ///
19 | [Description("PS4")]
20 | PS4 = 0x02
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/BL3Tools/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("BL3Tools")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("BL3Tools")]
13 | [assembly: AssemblyCopyright("Copyright © 2021")]
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("eefeab45-b558-4d33-9c74-d68660e26921")]
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 |
--------------------------------------------------------------------------------
/BL3Tools/Protobufs/OakProfile.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package OakSave;
3 | import "Protobufs/OakShared.proto";
4 | message PlayerInputBinding_Button {
5 | string rebind_data_path = 1;
6 | repeated string key_names = 2;
7 | }
8 | message PlayerInputBinding_Axis_Key {
9 | string key_name = 1;
10 | Vec3 scale_3d = 2;
11 | }
12 | message PlayerInputBinding_Axis {
13 | string rebind_data_path = 1;
14 | repeated PlayerInputBinding_Axis_Key keys = 2;
15 | }
16 | message PlayerInputBinding_Category {
17 | string category_data_path = 1;
18 | string context_data_path = 2;
19 | repeated PlayerInputBinding_Button button_bindings = 3;
20 | repeated PlayerInputBinding_Axis axis_bindings = 4;
21 | }
22 | message PlayerInputBindings {
23 | repeated PlayerInputBinding_Category categories = 1;
24 | }
25 | message OakProfileLastInventoryFilterInfo {
26 | string slot_type_id = 1;
27 | int32 last_filter_index = 2;
28 | }
29 | message OakProfileMenuTutorialInfo {
30 | repeated string seen_tutorials = 1;
31 | bool tutorials_disabled = 2;
32 | bool tutorials_allowed_in_non_game_modes = 3;
33 | }
34 | message OakFriendEncounterData {
35 | uint32 num_encounters = 1;
36 | int64 time_last_encounter = 2;
37 | }
38 | message GearSoldByFriendData {
39 | string gear_serial_number = 1;
40 | int32 player_class_identifier_hash = 2;
41 | string friend_net_id = 3;
42 | }
43 | message GuardianRankRewardSaveGameData {
44 | int32 num_tokens = 1;
45 | string reward_data_path = 2;
46 | }
47 | message GuardianRankProfileData {
48 | int32 available_tokens = 1;
49 | repeated GuardianRankRewardSaveGameData rank_rewards = 2;
50 | int32 guardian_rank = 3;
51 | int32 guardian_experience = 4;
52 | int32 guardian_reward_random_seed = 5;
53 | int64 new_guardian_experience = 6;
54 | }
55 | message RecentlyMetPlayer {
56 | string shift_player_id = 1;
57 | string first_party_player_id = 2;
58 | bool show_shift_player_entry = 3;
59 | }
60 | message Profile {
61 | message FriendEncountersEntry {
62 | string key = 1;
63 | OakFriendEncounterData value = 2;
64 | }
65 | bool enable_aim_assist = 1;
66 | bool gamepad_invert_look = 2;
67 | bool gamepad_invert_turn = 3;
68 | bool gamepad_invert_move = 4;
69 | bool gamepad_invert_strafe = 5;
70 | bool enable_vibration = 6;
71 | bool invert_mouse_pitch = 7;
72 | bool enable_mouse_smoothing = 8;
73 | float mouse_scale = 9;
74 | bool show_damage_numbers = 10;
75 | bool show_damage_number_icons = 11;
76 | bool enable_training_messages = 12;
77 | bool show_text_chat = 13;
78 | bool center_crosshair = 14;
79 | bool toggle_sprint = 15;
80 | bool toggle_crouch = 16;
81 | bool censor_content = 17;
82 | float music_volume = 18;
83 | float sound_effects_volume = 19;
84 | float vo_volume = 20;
85 | float voice_volume = 21;
86 | bool enable_optional_vo = 22;
87 | bool push_to_talk = 23;
88 | bool enable_controller_audio = 24;
89 | float speaker_angle_front = 25;
90 | float speaker_angle_side = 26;
91 | float speaker_angle_back = 27;
92 | uint32 speaker_setup = 28;
93 | bool mute_audio_on_focus_loss = 29;
94 | bool hide_strict_nat_help_dialog = 34;
95 | PlayerInputBindings player_input_bindings = 35;
96 | repeated uint32 news_hashes = 36;
97 | uint32 last_used_savegame_id = 37;
98 | int32 gamepad_hip_sensitivity_level = 38;
99 | int32 gamepad_zoomed_sensitivity_level = 39;
100 | int32 gamepad_vehicle_sensitivity_level = 40;
101 | float gamepad_movement_dead_zone_x = 41;
102 | float gamepad_movement_dead_zone_y = 42;
103 | float gamepad_look_dead_zone_inner_x = 43;
104 | float gamepad_look_dead_zone_outer_x = 44;
105 | float gamepad_look_dead_zone_inner_y = 45;
106 | float gamepad_look_dead_zone_outer_y = 46;
107 | float gamepad_vehicle_movement_dead_zone_x = 47;
108 | float gamepad_vehicle_movement_dead_zone_y = 48;
109 | float gamepad_vehicle_look_dead_zone_inner_x = 49;
110 | float gamepad_vehicle_look_dead_zone_outer_x = 50;
111 | float gamepad_vehicle_look_dead_zone_inner_y = 51;
112 | float gamepad_vehicle_look_dead_zone_outer_y = 52;
113 | float gamepad_left_dead_zone_inner = 53;
114 | float gamepad_left_dead_zone_outer = 54;
115 | float gamepad_right_dead_zone_inner = 55;
116 | float gamepad_right_dead_zone_outer = 56;
117 | float gamepad_look_axial_dead_zone_scale = 57;
118 | float gamepad_move_axial_dead_zone_scale = 58;
119 | bool gamepad_use_advanced_hip_aim_settings = 59;
120 | bool gamepad_use_advanced_zoomed_aim_settings = 60;
121 | bool gamepad_use_advanced_vehicle_aim_settings = 61;
122 | float gamepad_hip_yaw_rate = 62;
123 | float gamepad_hip_pitch_rate = 63;
124 | float gamepad_hip_extra_yaw = 64;
125 | float gamepad_hip_extra_pitch = 65;
126 | float gamepad_hip_ramp_up_time = 66;
127 | float gamepad_hip_ramp_up_delay = 67;
128 | float gamepad_zoomed_yaw_rate = 68;
129 | float gamepad_zoomed_pitch_rate = 69;
130 | float gamepad_zoomed_extra_yaw = 70;
131 | float gamepad_zoomed_extra_pitch = 71;
132 | float gamepad_zoomed_ramp_up_time = 72;
133 | float gamepad_zoomed_ramp_up_delay = 73;
134 | float gamepad_vehicle_yaw_rate = 74;
135 | float gamepad_vehicle_pitch_rate = 75;
136 | float gamepad_vehicle_extra_yaw = 76;
137 | float gamepad_vehicle_extra_pitch = 77;
138 | float gamepad_vehicle_ramp_up_time = 78;
139 | float gamepad_vehicle_ramp_up_delay = 79;
140 | bool ironsight_aim_assist = 80;
141 | uint32 walking_joystick_scheme = 81;
142 | uint32 driving_joystick_scheme = 82;
143 | float mouse_ads_scale = 83;
144 | float mouse_vehicle_scale = 84;
145 | bool mouse_ironsight_aim_assist = 85;
146 | uint32 vehicle_input_mode = 86;
147 | bool weapon_aim_toggle = 87;
148 | bool mantle_requires_button = 88;
149 | bool fixed_minimap_rotation = 89;
150 | bool map_invert_pitch = 90;
151 | bool map_invert_yaw = 91;
152 | uint32 difficulty = 92;
153 | bool swap_dual_wield_controls = 93;
154 | float base_fov = 94;
155 | uint32 crosshair_neutral_color_frame = 95;
156 | uint32 crosshair_enemy_color_frame = 96;
157 | uint32 crosshair_ally_color_frame = 97;
158 | bool enable_subtitles = 98;
159 | bool enable_closed_captions = 99;
160 | string last_status_menu_page = 100;
161 | repeated OakProfileLastInventoryFilterInfo inventory_screen_last_filter = 101;
162 | OakProfileMenuTutorialInfo tutorial_info = 102;
163 | uint32 default_network_type = 103;
164 | uint32 default_invite_type = 104;
165 | string matchmaking_region = 105;
166 | uint32 streaming_service = 106;
167 | int32 max_cached_friend_events = 107;
168 | int32 max_cached_friend_statuses = 108;
169 | repeated string friend_events = 109;
170 | repeated string friend_statuses = 110;
171 | int64 last_whisper_fetch_events_time = 111;
172 | int64 last_whisper_fetch_statuses_time = 112;
173 | uint32 desired_crossplay_state = 113;
174 | repeated FriendEncountersEntry friend_encounters = 133;
175 | int32 max_friend_encounter_size = 134;
176 | repeated GameStatSaveGameData profile_stats_data = 135;
177 | repeated InventoryCategorySaveData bank_inventory_category_list = 136;
178 | repeated bytes bank_inventory_list = 137;
179 | repeated bytes lost_loot_inventory_list = 138;
180 | repeated OakMailItem npc_mail_items = 139;
181 | repeated string mail_guids = 140;
182 | repeated string unread_mail_guids = 141;
183 | repeated GearSoldByFriendData gear_sold_by_friends = 142;
184 | repeated OakSDUSaveGameData profile_sdu_list = 143;
185 | repeated OakCustomizationSaveGameData unlocked_customizations = 144;
186 | repeated OakInventoryCustomizationPartInfo unlocked_inventory_customization_parts = 145;
187 | GuardianRankProfileData guardian_rank = 146;
188 | repeated CrewQuartersDecorationItemSaveGameData unlocked_crew_quarters_decorations = 147;
189 | repeated CrewQuartersRoomItemSaveGameData unlocked_crew_quarters_rooms = 148;
190 | bool enable_mouse_acceleration = 150;
191 | bool enable_gamepad_input = 151;
192 | bool use_classic_gamepad_input = 152;
193 | float master_volume = 153;
194 | uint32 monitor_display_type = 154;
195 | uint32 graphics_mode = 155;
196 | uint32 frame_rate_limit = 156;
197 | float base_vehicle_fov = 157;
198 | uint32 graphics_quality = 158;
199 | uint32 anisotropic_filtering = 159;
200 | uint32 shadow_quality = 160;
201 | uint32 display_performance_stats = 161;
202 | uint32 texture_detail = 162;
203 | uint32 draw_distance = 163;
204 | uint32 clutter = 164;
205 | uint32 tessellation = 165;
206 | uint32 foliage = 166;
207 | bool foliage_shadows = 167;
208 | bool planar_reflections = 168;
209 | uint32 volumetric_fog = 169;
210 | uint32 screen_space_reflections = 170;
211 | uint32 character_texture_detail = 171;
212 | uint32 character_detail = 172;
213 | uint32 ambient_occlusion_quality = 173;
214 | bool object_motion_blur = 174;
215 | bool lens_flare = 175;
216 | bool combat_number_long_format = 176;
217 | bool show_minimap_legendaries = 177;
218 | bool use_player_callouts = 178;
219 | uint32 friend_event_notification_lifetime = 179;
220 | uint32 friend_event_notification_frequency = 180;
221 | uint32 trade_request_reception_type = 181;
222 | float head_bob_scale = 182;
223 | bool has_seen_first_boot = 184;
224 | float subs_cc_size = 189;
225 | float cc_subs_background_opacity = 190;
226 | uint32 walking_button_scheme = 191;
227 | uint32 driving_button_scheme = 192;
228 | uint32 glyph_mode = 193;
229 | bool use_MPH = 194;
230 | repeated RegisteredDownloadableEntitlements registered_downloadable_entitlements = 195;
231 | repeated string seen_news_items = 196;
232 | bool auto_centering_enabled = 197;
233 | bool increased_chance_for_subscribers = 198;
234 | bool rare_chest_event_enabled = 199;
235 | bool badass_event_enabled = 200;
236 | bool pinata_event_enabled = 201;
237 | int32 min_time_between_badass_events = 202;
238 | float hud_scale_multiplier = 203;
239 | bool disable_spatial_audio__or__has_reset_console_fov = 204;
240 | int32 total_playtime_seconds = 205;
241 | bool moxxis_drink_event_enabled = 206;
242 | int32 moxxis_drink_event_bits_product_id = 207;
243 | repeated ChallengeSaveGameData challenge_data = 208;
244 | repeated int32 CitizenScienceLevelProgression = 209;
245 | bool default_dead_zone_inner_updated = 210;
246 | bool disable_event_content = 211;
247 | uint32 desired_friend_sync_state = 212;
248 | bool needs_shift_first_boot = 213;
249 | repeated RecentlyMetPlayer recently_met_players = 214;
250 | int32 CitizenScienceActiveBoosterIndex = 215;
251 | float CitizenScienceActiveBoosterRemainingTime = 216;
252 | float CitizenScienceActiveBoosterTotalTime = 217;
253 | int32 StreamerPrimaryActiveBoosterIndex = 218;
254 | float StreamerPrimaryActiveBoosterRemainingTime = 219;
255 | float StreamerPrimaryActiveBoosterTotalTime = 220;
256 | int32 StreamerSecondaryActiveBoosterIndex = 221;
257 | float StreamerSecondaryActiveBoosterRemainingTime = 222;
258 | float StreamerSecondaryActiveBoosterTotalTime = 223;
259 | int32 StreamerBoosterTier = 224;
260 | int32 CitizenScienceCSBucksAmount = 226;
261 | bool bCitizenScienceHasSeenIntroVideo = 227;
262 | bool bCitizenScienceTutorialDone = 228;
263 | bool enable_trigger_feedback = 229;
264 | bool fixed_initial_zonemap_rotation = 230;
265 | VaultCardSaveGameData vault_card = 231;
266 | uint32 player_selected_league = 232;
267 | bool needs_shift_first_boot_primary = 233;
268 | }
269 |
--------------------------------------------------------------------------------
/BL3Tools/Protobufs/OakSave.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package OakSave;
3 | import "Protobufs/OakShared.proto";
4 | message PlayerClassSaveGameData {
5 | string player_class_path = 1;
6 | uint32 dlc_package_id = 2;
7 | }
8 | message ResourcePoolSavegameData {
9 | float amount = 1;
10 | string resource_path = 2;
11 | }
12 | message RegionSaveGameData {
13 | int32 game_stage = 1;
14 | int32 play_through_idx = 2;
15 | string region_path = 3;
16 | uint32 dlc_package_id = 4;
17 | }
18 | message InventoryBalanceStateInitializationData {
19 | int32 game_stage = 1;
20 | string inventory_data = 2;
21 | string inventory_balance_data = 3;
22 | string manufacturer_data = 4;
23 | repeated string part_list = 5;
24 | repeated string generic_part_list = 6;
25 | bytes additional_data = 7;
26 | repeated string customization_part_list = 8;
27 | }
28 | message OakInventoryItemSaveGameData {
29 | bytes item_serial_number = 1;
30 | int32 pickup_order_index = 2;
31 | int32 flags = 3;
32 | string weapon_skin_path = 4;
33 | InventoryBalanceStateInitializationData development_save_data = 5;
34 | }
35 | message EquippedInventorySaveGameData {
36 | int32 inventory_list_index = 1;
37 | bool enabled = 2;
38 | string slot_data_path = 3;
39 | string trinket_data_path = 4;
40 | }
41 | message OakAbilityTreeItemSaveGameData {
42 | string item_asset_path = 1;
43 | int32 points = 2;
44 | int32 max_points = 3;
45 | int32 tree_identifier = 4;
46 | }
47 | message OakAbilitySlotSaveGameData {
48 | string ability_class_path = 1;
49 | string slot_asset_path = 2;
50 | }
51 | message OakActionAbilityAugmentSaveGameData {
52 | string action_ability_class_path = 1;
53 | string slot_asset_path = 2;
54 | string augment_asset_path = 3;
55 | }
56 | message OakActionAbilityAugmentConfigurationSaveGameData {
57 | string ability_class_path = 1;
58 | string augment_asset_path = 2;
59 | string mod_slot_asset_path = 3;
60 | string mod_asset_path = 4;
61 | }
62 | message OakPlayerAbilitySaveGameData {
63 | int32 ability_points = 1;
64 | repeated OakAbilityTreeItemSaveGameData tree_item_list = 2;
65 | repeated OakAbilitySlotSaveGameData ability_slot_list = 3;
66 | repeated OakActionAbilityAugmentSaveGameData augment_slot_list = 4;
67 | repeated OakActionAbilityAugmentConfigurationSaveGameData augment_configuration_list = 5;
68 | int32 tree_grade = 6;
69 | }
70 | message MissionStatusPlayerSaveGameData {
71 | enum MissionState {
72 | MS_NotStarted = 0;
73 | MS_Active = 1;
74 | MS_Complete = 2;
75 | MS_Failed = 3;
76 | MS_Unknown = 4;
77 | }
78 | MissionState status = 1;
79 | bool has_been_viewed_in_log = 2;
80 | repeated int32 objectives_progress = 3;
81 | string mission_class_path = 4;
82 | string active_objective_set_path = 5;
83 | uint32 dlc_package_id = 6;
84 | bool kickoff_played = 7;
85 | uint32 league_instance = 8;
86 | }
87 | message MissionPlaythroughSaveGameData {
88 | repeated MissionStatusPlayerSaveGameData mission_list = 1;
89 | string tracked_mission_class_path = 2;
90 | }
91 | message ActiveFastTravelSaveData {
92 | string active_travel_station_name = 1;
93 | bool blacklisted = 2;
94 | }
95 | message PlaythroughActiveFastTravelSaveData {
96 | repeated ActiveFastTravelSaveData active_travel_stations = 1;
97 | }
98 | message DiscoveredAreaInfo {
99 | string discovered_area_name = 1;
100 | uint32 discovered_playthroughs = 2;
101 | }
102 | message DiscoveredLevelInfo {
103 | string discovered_level_name = 1;
104 | uint32 discovered_playthroughs = 3;
105 | repeated DiscoveredAreaInfo discovered_area_info = 4;
106 | }
107 | message DiscoveredPlanetInfo {
108 | string discovered_planet = 1;
109 | bool is_new_planet = 2;
110 | }
111 | message DiscoverySaveData {
112 | repeated DiscoveredLevelInfo discovered_level_info = 1;
113 | }
114 | message VehicleUnlockedSaveGameData {
115 | string asset_path = 1;
116 | bool just_unlocked = 2;
117 | }
118 | message OakCARMenuVehicleConfigSaveData {
119 | string loadout_save_name = 1;
120 | string body_asset_path = 2;
121 | string wheel_asset_path = 3;
122 | string armor_asset_path = 4;
123 | string core_mod_asset_path = 5;
124 | string gunner_weapon_asset_path = 6;
125 | string driver_weapon_asset_path = 7;
126 | string ornament_asset_path = 8;
127 | string material_decal_asset_path = 9;
128 | string material_asset_path = 10;
129 | int32 color_index_1 = 11;
130 | int32 color_index_2 = 12;
131 | int32 color_index_3 = 13;
132 | }
133 | message CustomPlayerColorSaveGameData {
134 | string color_parameter = 1;
135 | Vec3 applied_color = 2;
136 | Vec3 split_color = 3;
137 | bool use_default_color = 4;
138 | bool use_default_split_color = 5;
139 | }
140 | message GuardianRankSaveGameData {
141 | int32 guardian_rank = 1;
142 | int32 guardian_experience = 2;
143 | }
144 | message GuardianRankRewardCharacterSaveGameData {
145 | int32 num_tokens = 1;
146 | bool is_enabled = 2;
147 | string reward_data_path = 3;
148 | }
149 | message GuardianRankPerkCharacterSaveGameData {
150 | bool is_enabled = 1;
151 | string perk_data_path = 2;
152 | }
153 | message GuardianRankCharacterSaveGameData {
154 | int32 guardian_available_tokens = 1;
155 | int32 guardian_rank = 2;
156 | int32 guardian_experience = 3;
157 | repeated GuardianRankRewardCharacterSaveGameData rank_rewards = 4;
158 | repeated GuardianRankPerkCharacterSaveGameData rank_perks = 5;
159 | int32 guardian_reward_random_seed = 6;
160 | int64 new_guardian_experience = 7;
161 | bool is_rank_system_enabled = 8;
162 | }
163 | message CrewQuartersDecorationSaveData {
164 | int32 decoration_index = 1;
165 | string decoration_data_path = 2;
166 | }
167 | message CrewQuartersSaveData {
168 | int32 preferred_room_assignment = 1;
169 | repeated CrewQuartersDecorationSaveData decorations = 2;
170 | string room_data_path = 3;
171 | }
172 | message CrewQuartersGunRackItemSaveData {
173 | bytes encrypted_serial_number = 1;
174 | string slot_asset_path = 2;
175 | InventoryBalanceStateInitializationData development_save_data = 3;
176 | }
177 | message CrewQuartersGunRackSaveData {
178 | repeated CrewQuartersGunRackItemSaveData rack_save_data = 1;
179 | }
180 | message EchoLogSaveGameData {
181 | bool has_been_seen_in_log = 1;
182 | string echo_log_path = 2;
183 | }
184 | message MapIDData {
185 | uint32 zone_name_id = 1;
186 | uint32 map_name_id = 2;
187 | }
188 | message GameStateSaveData {
189 | MapIDData last_traveled_map_id = 1;
190 | int32 mayhem_level = 2;
191 | int32 mayhem_random_seed = 3;
192 | }
193 | message ChallengeCategoryProgressSaveData {
194 | bytes category_progress = 1;
195 | }
196 | message OakPlayerCharacterAugmentSaveGameData {
197 | string slot_asset_path = 1;
198 | string augment_asset_path = 2;
199 | }
200 | message OakPlayerCharacterSlotSaveGameData {
201 | repeated OakPlayerCharacterAugmentSaveGameData augment_slot_list = 1;
202 | }
203 | message UITrackingSaveGameData {
204 | bool has_seen_skill_menu_unlock = 1;
205 | bool has_seen_guardian_rank_menu_unlock = 2;
206 | bool has_seen_echo_boot_ammo_bar = 3;
207 | bool has_seen_echo_boot_shield_bar = 4;
208 | bool has_seen_echo_boot_grenades = 5;
209 | int32 highest_thvm_breadcrumb_seen = 6;
210 | repeated string inventory_slot_unlocks_seen = 7;
211 | int32 saved_spin_offset = 8;
212 | }
213 | message PlanetCycleInfo {
214 | string planet_name = 1;
215 | float cycle_length = 2;
216 | float last_cached_time = 3;
217 | }
218 | message TimeOfDaySaveGameData {
219 | repeated PlanetCycleInfo planet_cycle_info = 1;
220 | string planet_cycle = 2;
221 | }
222 | message LevelPersistence_Actor_SaveGameData {
223 | string actor_name = 1;
224 | int32 timer_remaining = 2;
225 | }
226 | message LevelPersistence_Level_SaveGameData {
227 | string level_name = 1;
228 | repeated LevelPersistence_Actor_SaveGameData saved_actors = 2;
229 | }
230 | message GbxZoneMapFODSavedLevelData {
231 | string level_name = 1;
232 | uint32 fod_texture_size = 2;
233 | uint32 num_chunks = 3;
234 | float discovery_percentage = 4;
235 | uint32 data_state = 5;
236 | uint32 data_revision = 6;
237 | bytes fod_data = 7;
238 | }
239 | message GbxZoneMapFODSaveGameData {
240 | repeated GbxZoneMapFODSavedLevelData level_data = 1;
241 | }
242 | message OakProfileCloudData {
243 | repeated GameStatSaveGameData profile_stats_data = 1;
244 | repeated bytes bank_inventory_list = 2;
245 | repeated bytes lost_loot_inventory_list = 3;
246 | repeated OakMailItem npc_mail_items = 4;
247 | repeated OakSDUSaveGameData profile_sdu_list = 5;
248 | repeated OakCustomizationSaveGameData unlocked_customizations = 6;
249 | repeated OakInventoryCustomizationPartInfo unlocked_inventory_customization_parts = 7;
250 | int64 guardian_experience = 8;
251 | repeated CrewQuartersDecorationItemSaveGameData unlocked_crew_quarters_decorations = 9;
252 | repeated CrewQuartersRoomItemSaveGameData unlocked_crew_quarters_rooms = 10;
253 | repeated ChallengeSaveGameData challenge_data = 11;
254 | repeated string mail_guids = 12;
255 | repeated int32 CitizenScienceLevelProgression = 13;
256 | int32 CitizenScienceCSBucksAmount = 14;
257 | VaultCardSaveGameData vault_card = 15;
258 | bool bCitizenScienceHasSeenIntroVideo = 25;
259 | bool bCitizenScienceTutorialDone = 26;
260 | }
261 | message Character {
262 | message NicknameMappingsEntry {
263 | string key = 1;
264 | string value = 2;
265 | }
266 | message ActiveLeagueInstanceForEventEntry {
267 | uint32 key = 1;
268 | uint32 value = 2;
269 | }
270 | uint32 save_game_id = 1;
271 | int64 last_save_timestamp = 2;
272 | uint32 time_played_seconds = 3;
273 | PlayerClassSaveGameData player_class_data = 4;
274 | repeated ResourcePoolSavegameData resource_pools = 5;
275 | repeated RegionSaveGameData saved_regions = 6;
276 | int32 experience_points = 7;
277 | repeated GameStatSaveGameData game_stats_data = 8;
278 | repeated InventoryCategorySaveData inventory_category_list = 9;
279 | repeated OakInventoryItemSaveGameData inventory_items = 10;
280 | repeated EquippedInventorySaveGameData equipped_inventory_list = 11;
281 | repeated int32 active_weapon_list = 12;
282 | OakPlayerAbilitySaveGameData ability_data = 13;
283 | int32 last_play_through_index = 14;
284 | int32 playthroughs_completed = 15;
285 | bool show_new_playthrough_notification = 16;
286 | repeated MissionPlaythroughSaveGameData mission_playthroughs_data = 17;
287 | repeated string active_travel_stations = 21;
288 | DiscoverySaveData discovery_data = 22;
289 | string last_active_travel_station = 23;
290 | repeated VehicleUnlockedSaveGameData vehicles_unlocked_data = 24;
291 | repeated string vehicle_parts_unlocked = 25;
292 | repeated OakCARMenuVehicleConfigSaveData vehicle_loadouts = 26;
293 | int32 vehicle_last_loadout_index = 27;
294 | repeated ChallengeSaveGameData challenge_data = 28;
295 | repeated OakSDUSaveGameData sdu_list = 29;
296 | repeated string selected_customizations = 30;
297 | repeated int32 equipped_emote_customizations = 31;
298 | repeated CustomPlayerColorSaveGameData selected_color_customizations = 32;
299 | GuardianRankSaveGameData guardian_rank = 33;
300 | CrewQuartersSaveData crew_quarters_room = 34;
301 | CrewQuartersGunRackSaveData crew_quarters_gun_rack = 35;
302 | repeated EchoLogSaveGameData unlocked_echo_logs = 36;
303 | bool has_played_special_echo_log_insert_already = 37;
304 | repeated NicknameMappingsEntry nickname_mappings = 38;
305 | MapIDData last_traveled_map_id = 39;
306 | ChallengeCategoryProgressSaveData challenge_category_completion_pcts = 40;
307 | OakPlayerCharacterSlotSaveGameData character_slot_save_game_data = 41;
308 | UITrackingSaveGameData ui_tracking_save_game_data = 42;
309 | string preferred_character_name = 43;
310 | int32 name_character_limit = 44;
311 | uint32 preferred_group_mode = 45;
312 | TimeOfDaySaveGameData time_of_day_save_game_data = 46;
313 | repeated LevelPersistence_Level_SaveGameData level_persistence_data = 47;
314 | uint32 accumulated_level_persistence_reset_timer_seconds = 48;
315 | uint32 mayhem_level = 49;
316 | GbxZoneMapFODSaveGameData gbx_zone_map_fod_save_game_data = 50;
317 | repeated ActiveFastTravelSaveData active_or_blacklisted_travel_stations = 51;
318 | repeated string last_active_travel_station_for_playthrough = 52;
319 | repeated GameStateSaveData game_state_save_data_for_playthrough = 53;
320 | repeated RegisteredDownloadableEntitlements registered_downloadable_entitlements = 54;
321 | repeated PlaythroughActiveFastTravelSaveData active_travel_stations_for_playthrough = 55;
322 | string save_game_guid = 56;
323 | GuardianRankCharacterSaveGameData guardian_rank_character_data = 57;
324 | bool optional_objective_reward_fixup_applied = 58;
325 | bool vehicle_part_rewards_fixup_applied = 59;
326 | uint32 last_active_league = 60;
327 | uint32 last_active_league_instance = 61;
328 | repeated ActiveLeagueInstanceForEventEntry active_league_instance_for_event = 62;
329 | bool levelled_save_vehicle_part_rewards_fixup_applied = 63;
330 | OakProfileCloudData profile_cloud_data = 64;
331 | }
332 |
--------------------------------------------------------------------------------
/BL3Tools/Protobufs/OakShared.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package OakSave;
3 | message Vec3 {
4 | float x = 1;
5 | float y = 2;
6 | float z = 3;
7 | }
8 | message GameStatSaveGameData {
9 | int32 stat_value = 1;
10 | string stat_path = 2;
11 | }
12 | message InventoryCategorySaveData {
13 | uint32 base_category_definition_hash = 1;
14 | int32 quantity = 2;
15 | }
16 | message OakSDUSaveGameData {
17 | int32 sdu_level = 1;
18 | string sdu_data_path = 2;
19 | }
20 | message RegisteredDownloadableEntitlement {
21 | int32 id = 1;
22 | uint32 consumed = 2;
23 | bool registered = 3;
24 | bool seen = 4;
25 | }
26 | message RegisteredDownloadableEntitlements {
27 | string entitlement_source_asset_path = 1;
28 | repeated int64 entitlement_ids = 2;
29 | repeated RegisteredDownloadableEntitlement entitlements = 3;
30 | }
31 | message ChallengeStatSaveGameData {
32 | int32 current_stat_value = 1;
33 | string challenge_stat_path = 2;
34 | }
35 | message OakChallengeRewardSaveGameData {
36 | bool challenge_reward_claimed = 1;
37 | }
38 | message ChallengeSaveGameData {
39 | int32 completed_count = 1;
40 | bool is_active = 2;
41 | bool currently_completed = 3;
42 | int32 completed_progress_level = 4;
43 | int32 progress_counter = 5;
44 | repeated ChallengeStatSaveGameData stat_instance_state = 6;
45 | string challenge_class_path = 7;
46 | repeated OakChallengeRewardSaveGameData challenge_reward_info = 8;
47 | }
48 | message OakMailItem {
49 | uint32 mail_item_type = 1;
50 | string sender_display_name = 2;
51 | string subject = 3;
52 | string body = 4;
53 | string gear_serial_number = 5;
54 | string mail_guid = 6;
55 | int64 date_sent = 7;
56 | int64 expiration_date = 8;
57 | string from_player_id = 9;
58 | bool has_been_read = 10;
59 | }
60 | message OakCustomizationSaveGameData {
61 | bool is_new = 1;
62 | string customization_asset_path = 2;
63 | }
64 | message OakInventoryCustomizationPartInfo {
65 | uint32 customization_part_hash = 1;
66 | bool is_new = 2;
67 | }
68 | message CrewQuartersDecorationItemSaveGameData {
69 | bool is_new = 1;
70 | string decoration_item_asset_path = 2;
71 | }
72 | message CrewQuartersRoomItemSaveGameData {
73 | bool is_new = 1;
74 | string room_item_asset_path = 2;
75 | }
76 | message VaultCardSaveGameData {
77 | uint32 last_active_vault_card_id = 2;
78 | int32 current_day_seed = 3;
79 | int32 current_week_seed = 4;
80 | repeated VaultCardPreviousChallenge vault_card_previous_challenges = 5;
81 | repeated VaultCardRewardList vault_card_claimed_rewards = 6;
82 | }
83 | message VaultCardReward {
84 | int32 column_index = 1;
85 | int32 row_index = 2;
86 | }
87 | message VaultCardGearReward {
88 | int32 gear_index = 1;
89 | uint32 repurchase_count = 2;
90 | }
91 | message VaultCardRewardList {
92 | uint32 vault_card_id = 1;
93 | int64 vault_card_experience = 2;
94 | repeated VaultCardReward unlocked_reward_list = 4;
95 | repeated VaultCardReward redeemed_reward_list = 5;
96 | int32 vault_card_chests = 7;
97 | uint32 vault_card_chests_opened = 8;
98 | uint32 vault_card_keys_spent = 9;
99 | repeated VaultCardGearReward gear_rewards = 10;
100 | }
101 | message VaultCardPreviousChallenge {
102 | int32 previous_challenge_seed = 1;
103 | uint32 previous_challenge_id = 2;
104 | }
105 |
--------------------------------------------------------------------------------
/BL3Tools/Save.cs:
--------------------------------------------------------------------------------
1 | using BL3Tools.GameData.Items;
2 | using BL3Tools.GVAS;
3 | using System.Linq;
4 | using OakSave;
5 | using System.Collections.Generic;
6 |
7 | namespace BL3Tools {
8 |
9 | ///
10 | /// A simple underlying class that's used to store both and
11 | ///
12 | public class UE3Save {
13 | ///
14 | /// The file path associated with this path; does not need to be set
15 | ///
16 | public string filePath { get; set; } = null;
17 |
18 | public GVASSave GVASData { get; set; }
19 | }
20 |
21 |
22 | ///
23 | /// A class that represents a Borderlands 3 save.
24 | /// This stores all of the data about it; Including the underlying protobuf data as well as all of the loaded/serializable items from the save.
25 | ///
26 | public class BL3Save : UE3Save {
27 | public BL3Save(GVASSave saveData, Character character) {
28 | GVASData = saveData;
29 | Character = character;
30 |
31 | InventoryItems = Character.InventoryItems.Select(x => Borderlands3Serial.DecryptSerial(x.ItemSerialNumber)).ToList();
32 |
33 | for(int i = 0; i < InventoryItems.Count; i++) {
34 | InventoryItems[i].OriginalData = Character.InventoryItems[i];
35 | }
36 | }
37 |
38 | // Unlike the profiles, we can't just remove all of the data from the save's inventory and then readd it
39 | // Saves store other data in the items as well so we can't do that
40 |
41 | ///
42 | /// Deletes the given item from the save
43 | ///
44 | /// A object representing the item to delete
45 | public void DeleteItem(Borderlands3Serial serialToDelete) {
46 |
47 | InventoryItems.Remove(serialToDelete);
48 | if (serialToDelete.OriginalData != null) {
49 | Character.InventoryItems.RemoveAll(x => ReferenceEquals(x, serialToDelete.OriginalData));
50 | }
51 | }
52 |
53 | ///
54 | /// Adds the given item to the save
55 | ///
56 | /// A object representing the item to add
57 | public void AddItem(Borderlands3Serial serialToAdd) {
58 | InventoryItems.Add(serialToAdd);
59 | var oakItem = new OakInventoryItemSaveGameData() {
60 | DevelopmentSaveData = null,
61 | Flags = 0x01, // "NEW" Flag
62 | PickupOrderIndex = 8008,
63 | WeaponSkinPath = "", // No skin obviously
64 | ItemSerialNumber = serialToAdd.EncryptSerialToBytes()
65 | };
66 | // Properly add in the item onto the save
67 | Character.InventoryItems.Add(oakItem);
68 | serialToAdd.OriginalData = oakItem;
69 | }
70 |
71 | ///
72 | /// The underlying protobuf data representing this save
73 | ///
74 | public Character Character { get; set; } = null;
75 |
76 | ///
77 | /// The respective platform for the given save
78 | /// Used for encryption/decryption of the save files
79 | ///
80 | public Platform Platform { get; set; } = Platform.PC;
81 |
82 | ///
83 | /// A list containing all of the inventory items of this file
84 | /// If you want to add items to the save, please use
85 | /// If you want to delete items from the save, please use
86 | ///
87 | public List InventoryItems { get; set; } = null;
88 |
89 | public static Dictionary ValidClasses = new Dictionary() {
90 | { "FL4K", new PlayerClassSaveGameData() {
91 | DlcPackageId = 0,
92 | PlayerClassPath="/Game/PlayerCharacters/Beastmaster/PlayerClassId_Beastmaster.PlayerClassId_Beastmaster" } },
93 | { "Moze", new PlayerClassSaveGameData() {
94 | DlcPackageId = 0,
95 | PlayerClassPath="/Game/PlayerCharacters/Gunner/PlayerClassId_Gunner.PlayerClassId_Gunner" } },
96 | { "Zane", new PlayerClassSaveGameData() {
97 | DlcPackageId = 0,
98 | PlayerClassPath="/Game/PlayerCharacters/Operative/PlayerClassId_Operative.PlayerClassId_Operative" } },
99 | { "Amara", new PlayerClassSaveGameData() {
100 | DlcPackageId = 0, PlayerClassPath=
101 | "/Game/PlayerCharacters/SirenBrawler/PlayerClassId_Siren.PlayerClassId_Siren" } },
102 | };
103 |
104 | public static Dictionary CharacterToClassPair = new Dictionary() {
105 | { "FL4K", "Beastmaster" },
106 | { "Moze", "Gunner" },
107 | { "Zane", "Operative" },
108 | { "Amara", "Siren" }
109 | };
110 | }
111 |
112 | ///
113 | /// A simple class that represents the Borderlands 3 profile structure.
114 | ///
115 | public class BL3Profile : UE3Save {
116 | public BL3Profile(GVASSave gvasSave, Profile profile) {
117 | this.GVASData = gvasSave;
118 | Profile = profile;
119 |
120 | BankItems = Profile.BankInventoryLists.Select(x => Borderlands3Serial.DecryptSerial(x)).ToList();
121 | LostLootItems = Profile.LostLootInventoryLists.Select(x => Borderlands3Serial.DecryptSerial(x)).ToList();
122 |
123 | for(int i = 0; i < BankItems.Count - 1; i++) {
124 | Borderlands3Serial item = BankItems[i];
125 | item.OriginalData = new OakInventoryItemSaveGameData() {
126 | DevelopmentSaveData = null,
127 | Flags = 0x00,
128 | ItemSerialNumber = Profile.BankInventoryLists[i],
129 | PickupOrderIndex = -1,
130 | WeaponSkinPath = ""
131 | };
132 | }
133 |
134 | for (int i = 0; i < LostLootItems.Count - 1; i++) {
135 | Borderlands3Serial item = LostLootItems[i];
136 | item.OriginalData = new OakInventoryItemSaveGameData() {
137 | DevelopmentSaveData = null,
138 | Flags = 0x00,
139 | ItemSerialNumber = Profile.LostLootInventoryLists[i],
140 | PickupOrderIndex = -1,
141 | WeaponSkinPath = ""
142 | };
143 | }
144 | }
145 |
146 | ///
147 | /// The underlying protobuf data representing this profile
148 | ///
149 | public Profile Profile { get; set; }
150 |
151 |
152 | ///
153 | /// A list representing all of the items stored in the current profile's bank.
154 | ///
155 | public List BankItems { get; set; } = null;
156 |
157 | ///
158 | /// A list representing all of the items stored in the current profile's lost loot.
159 | ///
160 | public List LostLootItems { get; set; } = null;
161 |
162 |
163 | ///
164 | /// The respective platform for the profile
165 | /// Used for encryption/decryption
166 | ///
167 | public Platform Platform { get; set; } = Platform.PC;
168 | }
169 |
170 | }
171 |
--------------------------------------------------------------------------------
/BL3Tools/Scripts/BalanceToInventoryData.py:
--------------------------------------------------------------------------------
1 | import unrealsdk
2 | from unrealsdk import * # type: ignore[import]
3 | import json
4 | import os
5 |
6 | # This script creates a JSON file containing every balance and their respective inventory data.
7 | # Do know that this data isn't actually a hard-limitation; Balances when saved in serials can have *any* InventoryData
8 |
9 | balances = unrealsdk.FindAll("Class /Script/GbxInventory.InventoryBalanceData", True)[2:]
10 | mapping = {}
11 |
12 |
13 | for balance in balances:
14 | balanceName = balance.GetObjectName()
15 | if "Default__" in balanceName:
16 | continue
17 | inventoryData = balance.InventoryData
18 | if inventoryData == None:
19 | Log(f"Skipping balance: {balanceName}...Missing inventory data")
20 | inventoryData = inventoryData.Name
21 | balanceName = balanceName.split(" ")[-1]
22 | mapping[balanceName] = inventoryData
23 |
24 | with open("./Mods/Tools/Data/balance_to_inv_data.json", "w+") as outFile:
25 | json.dump(mapping, outFile)
26 |
--------------------------------------------------------------------------------
/BL3Tools/Scripts/BalanceToInventoryKey.py:
--------------------------------------------------------------------------------
1 | import os
2 | import json
3 | from unrealsdk import * # type: ignore[import]
4 | import unrealsdk
5 |
6 | balances = unrealsdk.FindAll("Class /Script/GbxInventory.InventoryBalanceData", True)[1:]
7 | mapping = {}
8 | for balance in balances:
9 | balanceName = balance.GetObjectName()
10 | if("Default__" in balanceName):
11 | continue
12 | partSetData = balance.PartSetData
13 | if(partSetData == None):
14 | balanceName = balanceName.split(" ")[-1]
15 | if "Head" in balanceName:
16 | mapping.update({balanceName : "BPInvPart_Customization_Head_C"})
17 | continue
18 | elif "WeaponSkin" in balanceName or "WeaponTrinket" in balanceName or "RoomDeco" in balanceName:
19 | mapping.update({balanceName : "InventoryCustomizationPartData"})
20 | elif "Skin" in balanceName:
21 | mapping.update({balanceName : "BPInvPart_Customization_Skin_C"})
22 | continue
23 | unrealsdk.Log(f"Skipping balance: {str(balance.Name)}...Missing part set")
24 | continue
25 | partDataClass = partSetData.PartDataClass
26 | balanceName = balanceName.split(" ")[-1]
27 |
28 | mapping.update({balanceName : partDataClass.GetName()})
29 |
30 | with open("./Mods/Tools/Data/balance_to_inv_key.json", "w+") as outFile:
31 | json.dump(mapping, outFile)
--------------------------------------------------------------------------------
/BL3Tools/Scripts/FastTravelDumper.py:
--------------------------------------------------------------------------------
1 | import json
2 | from unrealsdk import * # type: ignore[import]
3 |
4 | fastTravelStations = unrealsdk.FindAll("Class /Script/GbxTravelStation.FastTravelStationData", True)[1:]
5 | mapping = {}
6 | for station in fastTravelStations:
7 | mapping.update( {str(station).split(" ")[1] : station.DisplayName} )
8 |
9 | with open("./Mods/Tools/Data/fast_travel_to_name.json", "w+") as outFile:
10 | json.dump(mapping, outFile)
--------------------------------------------------------------------------------
/BL3Tools/Scripts/PartToName.py:
--------------------------------------------------------------------------------
1 | import unrealsdk
2 | import json
3 |
4 | names = {}
5 | prefixes = {}
6 |
7 | # Weapons
8 | for part in unrealsdk.FindAll("InventoryPartData", True):
9 | partObj = part.GetObjectName().split(" ")[-1]
10 |
11 | if part.PrefixPartList is not None:
12 | if len(list(part.PrefixPartList)) != 0:
13 | prefixPart = max([x for x in part.PrefixPartList], key = lambda p: p.Priority)
14 | prefixes[partObj] = prefixPart.PartName
15 |
16 | if part.TitlePartList is not None:
17 | if len(list(part.TitlePartList)) != 0:
18 | titlePart = max([x for x in part.TitlePartList], key = lambda p: p.Priority)
19 | names[partObj] = titlePart.PartName
20 |
21 | # Real simple fix up because lol eridian fabricator
22 | names["/Game/Gear/Weapons/HeavyWeapons/Eridian/_Shared/_Design/Parts/Part_Eridian_Fabricator.Part_Eridian_Fabricator"] = "Eridian Fabricator"
23 |
24 | # Items
25 | for obj in unrealsdk.FindAll("InventoryNamingStrategyData", True):
26 | if obj.SingleNames is None: continue
27 | for entry in obj.SingleNames:
28 | if None in (entry.Part, entry.NamePart):
29 | continue
30 | part = entry.Part.GetObjectName().split(" ")[-1]
31 | names[part] = entry.NamePart.PartName
32 |
33 | # Customizations
34 | for obj in unrealsdk.FindAll("GbxCustomizationData", True):
35 | if obj.BalanceData is None: continue
36 | balanceData = obj.BalanceData
37 | names[balanceData.GetObjectName().split(" ")[-1]] = obj.CustomizationName
38 |
39 | with open("./Mods/Tools/Data/part_name_mapping.json", "w+") as outFile:
40 | json.dump(names, outFile)
41 |
42 | with open("./Mods/Tools/Data/prefix_name_mapping.json", "w+") as outFile:
43 | json.dump(prefixes, outFile)
--------------------------------------------------------------------------------
/BL3Tools/Scripts/README.md:
--------------------------------------------------------------------------------
1 | All of these scripts utilize the BL3 [PythonSDK](https://github.com/bl-sdk/PythonSDK).
2 | It's currently **in development** and available in the [bl3](https://github.com/bl-sdk/PythonSDK/tree/bl3) branch.
3 | You will need to join the Discord in order to get support for it or DM me on Discord (@FromDarkHell#8008).
4 |
--------------------------------------------------------------------------------
/BL3Tools/Scripts/ValidPartMapper.py:
--------------------------------------------------------------------------------
1 | import unrealsdk # type: ignore[import]
2 | from unrealsdk import * # type: ignore[import]
3 | import json
4 | import os
5 | from typing import Dict, List
6 |
7 | # partMapping = {"PART": {"Dependencies": [], "Excluders": []}}
8 | partMapping: Dict[str, Dict[str, List[str]]] = {}
9 |
10 | for part in unrealsdk.FindAll("Class /Script/GbxGameSystemCore.ActorPartData", True)[2:]:
11 | if "Default__" in str(part):
12 | continue
13 | dependencies = [str(x).split(" ")[-1] for x in list(part.Dependencies)]
14 | excluders = [str(x).split(" ")[-1] for x in list(part.Excluders)]
15 | partMapping[str(part).split(" ")[-1]] = {"Dependencies": dependencies, "Excluders": excluders}
16 |
17 | # getall InventoryBalanceData RuntimeGenericPartList
18 | anointmentMapping: Dict[str, List[str]] = {}
19 | for balance in unrealsdk.FindAll("Class /Script/GbxInventory.InventoryBalanceData", True)[2:]:
20 | if "Default__" in str(balance):
21 | continue
22 | partList = balance.RuntimeGenericPartList
23 | anointments = []
24 |
25 | if partList.bEnabled:
26 | anointments = [str(x.PartData).split(" ")[-1] for x in list(partList.PartList) if x.PartData != None]
27 |
28 | anointmentMapping[str(balance).split(" ")[-1]] = anointments
29 |
30 | with open("./Mods/Tools/Data/valid_part_database.json", "w+") as outFile:
31 | json.dump(partMapping, outFile, indent=4, sort_keys=True)
32 |
33 | with open("./Mods/Tools/Data/valid_generics.json", "w+") as outFile:
34 | json.dump(anointmentMapping, outFile, indent=4, sort_keys=True)
35 |
--------------------------------------------------------------------------------
/BL3Tools/TMSUnpack/TMSArchive.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.IO.Compression;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 | using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
10 | using IOTools;
11 |
12 | // A large portion of this code is based off of apocalyptech's OakTMS unpack:
13 | // https://github.com/apocalyptech/pyoaktms
14 |
15 | namespace BL3Tools.TMSUnpack {
16 |
17 | ///
18 | /// A class that represents an OakTMS file and the contents of it.
19 | ///
20 | public class TMSArchive {
21 |
22 | public struct TMSChunk {
23 | public ulong CompressedSize { get; set; }
24 | public ulong DecompressedSize { get; set; }
25 | public byte[] DecompressedData { get; set; }
26 | public byte[] CompressedData { get; set; }
27 | }
28 |
29 | public struct TMSFile {
30 | public string FileName { get; set; }
31 | public byte[] Contents { get; set; }
32 | }
33 |
34 | public string FileName { get; private set; } = null;
35 | public List Chunks { get; private set; } = new List();
36 | public List FooterStrings { get; private set; } = new List();
37 | public uint[] FooterNumbers { get; private set; } = new uint[2];
38 | public List Files { get; private set; } = new List();
39 |
40 | public TMSArchive(byte[] data, string name = "OakTMS.cfg") {
41 | FileName = name;
42 | Process(data);
43 | }
44 |
45 | public void Process(byte[] data) {
46 | IOWrapper helper = new IOWrapper(data, Endian.Little);
47 | Process(helper);
48 | }
49 |
50 | public void Process(IOWrapper wrapper) {
51 | if (wrapper.CurrentEndian != Endian.Little)
52 | wrapper.CurrentEndian = Endian.Little;
53 |
54 | long totalSize = wrapper.Length;
55 |
56 | uint uncompressedSize = wrapper.ReadUInt32();
57 | uint fileCount = wrapper.ReadUInt32();
58 |
59 | ulong magic = wrapper.ReadUInt64();
60 | if (magic != 0x9E2A83C1) throw new Exception("Invalid magic for TMS file...");
61 |
62 | ulong chunkSize = wrapper.ReadUInt64();
63 |
64 | ulong totalCompressedSize = wrapper.ReadUInt64();
65 | ulong totalUncompressedSize = wrapper.ReadUInt64();
66 | if (uncompressedSize != totalUncompressedSize) throw new Exception("Varying decompressed sizes...");
67 |
68 | ulong currentCompressedSize = 0;
69 | ulong currentUncompressedSize = 0;
70 |
71 | while(true) {
72 | ulong chunkCompressedSize = wrapper.ReadUInt64();
73 | ulong chunkUncompressedSize = wrapper.ReadUInt64();
74 |
75 | currentCompressedSize += chunkCompressedSize;
76 | currentUncompressedSize += chunkUncompressedSize;
77 |
78 | Console.WriteLine("Got chunk, Compressed Size: {0}, Uncompressed: {1}", chunkCompressedSize, chunkUncompressedSize);
79 | Chunks.Add(new TMSChunk {
80 | CompressedSize = chunkCompressedSize,
81 | DecompressedSize = chunkUncompressedSize,
82 | DecompressedData = null,
83 | CompressedData = null
84 | });
85 |
86 | if(currentCompressedSize == totalCompressedSize) {
87 | if (currentUncompressedSize != totalUncompressedSize) throw new Exception("Varying decompressed sizes...");
88 | break;
89 | }
90 | }
91 |
92 | for(int i = 0; i < Chunks.Count; i++) {
93 | TMSChunk chunk = Chunks[i];
94 |
95 | //! BAD DECISION HERE
96 | chunk.CompressedData = wrapper.ReadBytes((int)chunk.CompressedSize);
97 | chunk.DecompressedData = new byte[chunk.DecompressedSize + 1];
98 | using(MemoryStream ms = new MemoryStream(chunk.CompressedData))
99 | using(InflaterInputStream inflater = new InflaterInputStream(ms)) {
100 | int sizeRead = inflater.Read(chunk.DecompressedData, 0, (int)chunk.DecompressedSize);
101 | var x = chunk.DecompressedData;
102 | Array.Resize(ref x, sizeRead);
103 | chunk.DecompressedData = x;
104 | }
105 |
106 | Chunks[i] = chunk;
107 | }
108 |
109 | // Remove invalid chunks just to be safe
110 | Chunks.RemoveAll(x => x.DecompressedData == null);
111 | ulong decompressedSize = (ulong)Chunks.Sum(x => x.DecompressedData.Length);
112 |
113 | // Make sure that we did decompression properly.
114 | if (decompressedSize != totalUncompressedSize)
115 | throw new Exception("Incorrect decompression sizes...");
116 |
117 | // Read in some footer info here now that we've read in everything else...
118 | uint numStrs = wrapper.ReadUInt32();
119 | for(int i = 0; i < numStrs; i++) {
120 | uint stringLength = wrapper.ReadUInt32() - 1;
121 | var strBytes = wrapper.ReadBytes((int)stringLength);
122 | if (wrapper.ReadByte() != 0x00) throw new Exception("Incorrect string length, null byte not found...");
123 | string str = Encoding.UTF8.GetString(strBytes);
124 | FooterStrings.Add(str);
125 | }
126 |
127 | FooterNumbers[0] = wrapper.ReadUInt32();
128 | FooterNumbers[1] = wrapper.ReadUInt32();
129 |
130 | if (wrapper.Position != totalSize) throw new Exception("File larger than expected...");
131 |
132 | // Now process all of the decompressed data...
133 |
134 | // Join the data into one big byte array
135 | byte[] data = new byte[0];
136 | foreach(TMSChunk chunk in Chunks)
137 | data = Helpers.ConcatArrays(data, chunk.DecompressedData);
138 |
139 | IOWrapper decompData = new IOWrapper(data, Endian.Little);
140 |
141 | for (int i = 0; i < fileCount; i++) {
142 | uint stringLength = decompData.ReadUInt32() - 1;
143 | var strBytes = decompData.ReadBytes((int)stringLength);
144 | if (decompData.ReadByte() != 0x00) throw new Exception("Incorrect string length, null byte not found...");
145 | string fileName = Encoding.UTF8.GetString(strBytes);
146 |
147 | // Now we read the contents of the file
148 | int contentsLen = (int)decompData.ReadUInt32();
149 | byte[] contents = decompData.ReadBytes(contentsLen);
150 | fileName = fileName.TrimStart();
151 | while (fileName.StartsWith("../"))
152 | fileName = fileName.Substring(3);
153 |
154 | Files.Add(new TMSFile {
155 | FileName = fileName,
156 | Contents = contents
157 | });
158 | }
159 | }
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/BL3Tools/TMSUnpack/TMSUnpacker.cs:
--------------------------------------------------------------------------------
1 | using System.Net;
2 |
3 | namespace BL3Tools.TMSUnpack {
4 | public static class TMSUnpacker {
5 | ///
6 | /// Downloads the given TMS from the gearbox servers to a byte array
7 | ///
8 | /// Platform of the TMS file; Known Values: `pc`
9 | /// Marketplace of the TMS file; Known Values: `epic`/`steam`
10 | /// "Branch" of the TMS file; Known values `prod`/`qa`
11 | /// A representing the downloaded archive
12 | public static TMSArchive DownloadFromURL(string platform = "pc", string marketplace = "steam", string type = "prod") {
13 | return DownloadFromURL(string.Format("http://cdn.services.gearboxsoftware.com/sparktms/oak/{0}/{1}/OakTMS-{2}.cfg", platform, marketplace, type));
14 | }
15 |
16 | ///
17 | /// Downloads the given TMS from the given URL
18 | ///
19 | /// A representing the downloaded archive
20 | public static TMSArchive DownloadFromURL(string url) {
21 | var webClient = new WebClient();
22 | byte[] TMSData = webClient.DownloadData(url);
23 |
24 | return new TMSArchive(TMSData);
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/BL3Tools/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/CustomizationGenerator/CustomizationGenerator.py:
--------------------------------------------------------------------------------
1 | import openpyxl # type: ignore[import]
2 | import requests
3 | from pathlib import Path
4 | from typing import Dict, List
5 |
6 | apocalyptechSheet = "https://docs.google.com/spreadsheets/d/1v-F_3C2ceaFKJae1b6wmbelw_jLjmPPriBLzGTZMqRc/export?format=xlsx&id=1v-F_3C2ceaFKJae1b6wmbelw_jLjmPPriBLzGTZMqRc"
7 | characters = {
8 | "FL4K": "Beastmaster",
9 | "Moze": "Gunner",
10 | "Zane": "Operative",
11 | "Amara": "Siren",
12 | }
13 |
14 | emotes, decos, heads, skins, echos, trinkets, weaponSkins = [], [], [], [], [], [], []
15 |
16 | playerHeads: Dict[str, List[str]] = {x: [] for x, _ in characters.items()}
17 | playerSkins: Dict[str, List[str]] = {x: [] for x, _ in characters.items()}
18 |
19 |
20 | def requestGoogleSheetAsXLSX(url):
21 | print("Requesting google sheet: {}".format(url))
22 | r = requests.get(url, allow_redirects=True)
23 | open("sheetData.xlsx", "wb").write(r.content)
24 | print("Google sheet written...")
25 |
26 |
27 | requestGoogleSheetAsXLSX(apocalyptechSheet)
28 |
29 | xlsxFile = Path("sheetData.xlsx")
30 | workbook = openpyxl.load_workbook(xlsxFile)
31 | sheetNames = workbook.sheetnames[1:]
32 | print("Sheet Names: {}".format(sheetNames))
33 |
34 | for sheetName in sheetNames:
35 | worksheet = workbook[sheetName]
36 |
37 | for row in range(
38 | 2, worksheet.max_row + 1
39 | ): # Iterate through each row of the worksheet
40 | invBalance = worksheet["C{}".format(row)].value
41 | customizationName = worksheet["A{}".format(row)].value
42 |
43 | if invBalance == None:
44 | break
45 |
46 | assetPath = invBalance.replace("InvBal_", "")
47 |
48 | if "Heads" in sheetName:
49 | heads.append((customizationName, assetPath))
50 | print(
51 | f"\t[.] Parsing heads: {customizationName} on sheet: {sheetName} :: Asset Path: {assetPath}"
52 | )
53 | playerHeads[[x for x, y in characters.items() if y in sheetName][0]].append(
54 | assetPath
55 | )
56 | elif "Skins" in sheetName and "Weapon" not in sheetName:
57 | skins.append((customizationName, assetPath))
58 | print(f"\t[..] Parsing skins: {customizationName} on sheet: {sheetName}")
59 | playerSkins[[x for x, y in characters.items() if y in sheetName][0]].append(
60 | assetPath
61 | )
62 | elif "Emotes" in sheetName:
63 | emotes.append((customizationName, assetPath))
64 | elif "Themes" in sheetName:
65 | echos.append((customizationName, assetPath))
66 | elif "Decorations" in sheetName:
67 | decos.append((customizationName, assetPath))
68 | elif "Trinket" in sheetName:
69 | trinkets.append((customizationName, assetPath))
70 | elif "Weapon Skins" in sheetName:
71 | weaponSkins.append((customizationName, assetPath))
72 |
73 | print("Done reading {}".format(sheetName))
74 |
75 | pathFormat = "public static readonly Dictionary {0}AssetPaths = new Dictionary()"
76 |
77 | assetSets = []
78 | nameToList = {
79 | "emotes": emotes,
80 | "deco": decos,
81 | "head": heads,
82 | "skin": skins,
83 | "echo": echos,
84 | "weapon": weaponSkins,
85 | "trinket": trinkets,
86 | }
87 |
88 | for name in nameToList:
89 | list = nameToList[name]
90 | assetSet = pathFormat.format(name) + "{\n"
91 | for name, asset in list:
92 | assetSet += ' {{"{0}", "{1}"}},\n'.format(asset, name)
93 | assetSet = assetSet[:-2] + "\n };\n"
94 | assetSets += [assetSet]
95 |
96 | pathFormat = "public static readonly Dictionary> {0}NamesDictionary = new Dictionary>()"
97 |
98 | assetSet = pathFormat.format("Head") + "{\n"
99 | for char, hx in playerHeads.items():
100 | assetSet += ' {{"{0}", new List() {{ {1} }} }},\n'.format(
101 | char, str(hx)[1:-1].replace("'", '"')
102 | )
103 | assetSet = assetSet[:-2] + "\n };\n"
104 | assetSets += [assetSet]
105 |
106 | assetSet = pathFormat.format("Skin") + "{\n"
107 | for char, hx in playerSkins.items():
108 | assetSet += ' {{"{0}", new List() {{ {1} }} }},\n'.format(
109 | char, str(hx)[1:-1].replace("'", '"')
110 | )
111 | assetSet = assetSet[:-2] + "\n };\n"
112 | assetSets += [assetSet]
113 |
114 | with open("output.txt", "w") as outFile:
115 | completeSet = ""
116 |
117 | for assetSet in assetSets:
118 | completeSet += assetSet + "\n"
119 |
120 | outFile.write(completeSet)
121 |
--------------------------------------------------------------------------------
/CustomizationGenerator/CustomizationGenerator.pyproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Debug
4 | 2.0
5 | 0b16c2f4-e535-456c-9547-06328ed6a585
6 | .
7 | CustomizationGenerator.py
8 |
9 |
10 | .
11 | .
12 | CustomizationGenerator
13 | CustomizationGenerator
14 |
15 |
16 | true
17 | false
18 |
19 |
20 | true
21 | false
22 |
23 |
24 | bin\Single File\
25 |
26 |
27 |
28 |
29 |
30 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Borderlands 3 Save Editor
2 |
3 | A desktop based save _and_ profile editor for [Borderlands 3](borderlands.com/).
4 |
5 |  
6 |
7 | ### Installation / Usage
8 |
9 | 1. Download the exe/zip for your respective OS/setup available on the [Releases](https://github.com/FromDarkHell/BL3SaveEditor/releases) tab.
10 | - _Which one do I download?_ If you don't know which one to download, you probably will want to download `BL3SaveEditor-Portable.zip`[here](https://github.com/FromDarkHell/BL3SaveEditor/releases/latest/download/BL3SaveEditor-Portable.zip). This is a self-contained windows binary. If that one doesn't launch for you, try downloading `BL3SaveEditor.zip`[here](https://github.com/FromDarkHell/BL3SaveEditor/releases/latest/download/BL3SaveEditor.zip).
11 | 2. Then you download / run the exe
12 | 3. Click `Open`, then you can select either a profile (`profile.sav`) or a game save (`[NUMBERS].sav`).
13 | 4. You can now edit your profile or game save to your hearts content!
14 | 5. Once you think you're done editing your saves, you can save your file with either the `Save` button (which will overwrite your loaded file), or use the `Save As` button, allowing you to save to a new file. In either case, it will make backups to help keep your saves protected!
15 |
16 | ### Credits
17 |
18 | - [gibbed](https://github.com/Gibbed) for their [Inventory Serial Number Database](https://github.com/gibbed/Borderlands3Dumps). It's what allows me to know what weapons you've got in your backpack! I also use their [Borderlands3Protos](https://github.com/gibbed/Borderlands3Protos) repository
19 | - [apocalyptech](https://github.com/apocalyptech/) for writing the incredibly helpful [guide](https://github.com/BLCM/BLCMods/wiki/Understanding-BL3-Item-Serial-Numbers) on parsing weapon serials.
20 | - [Benjamin Ruhl](https://github.com/benruehl) for creating [AdonisUI](https://github.com/benruehl/adonis-ui), the WPF toolkit I used for most of my UI development/theming.
21 | - [xceed software](https://github.com/xceedsoftware) for creating the [Extended WPF Toolkit](https://github.com/xceedsoftware/wpftoolkit), allowing me to have a `Raw` tab and number up/downs.
22 | - [Gearbox Software](https://www.gearboxsoftware.com/) of course for creating Borderlands 3.
23 | - Many others who helped test the editor as well as help me get my mind around some of the concepts.
24 |
25 | ### Support
26 |
27 | If you for some reason want to support me financially for this project (or others I make), you can donate to me via [ko-fi](https://ko-fi.com/fromdarkhell) or [Patreon](https://patreon.com/fromdarkhell).
28 |
29 | [](https://ko-fi.com/O4O44GLCD) [](https://patreon.com/fromdarkhell)
30 |
--------------------------------------------------------------------------------