├── .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
├── TTWSaveEditor.csproj
└── ttw_ico.ico
├── 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
│ │ │ ├── balance_to_inv_key_redux.json
│ │ │ ├── part_name_mapping.json
│ │ │ ├── part_name_mapping_redux.json
│ │ │ ├── prefix_name_mapping.json
│ │ │ ├── valid_generics.json
│ │ │ └── valid_part_database.json
│ │ ├── SerialDBR
│ │ │ └── Inventory Serial Number Database REDUX.json
│ │ └── WonderlandsSerial.cs
│ ├── Mappings
│ │ └── fast_travel_to_name.json
│ └── SDU.cs
├── Helpers
│ └── Helpers.cs
├── Platform.cs
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Settings.Designer.cs
│ └── Settings.settings
├── 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
├── app.config
└── 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/WonderlandsDumps
7 | [submodule "IOTools"]
8 | path = IOTools
9 | url = https://github.com/FromDarkHell/IOTools
10 |
11 |
--------------------------------------------------------------------------------
/BL3SaveEditor.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.0.32112.339
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TTWSaveEditor", "BL3SaveEditor\TTWSaveEditor.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 = Release|Any CPU
27 | {A363B5AB-89B8-40F7-85A9-6A7341E183EF}.Debug|Any CPU.Build.0 = Release|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 = Release|Any CPU
33 | {EEFEAB45-B558-4D33-9C74-D68660E26921}.Debug|Any CPU.Build.0 = Release|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 = Release|Any CPU
39 | {B93FF5DB-FFEF-4F81-812E-F5FDF8CDFBBE}.Debug|Any CPU.Build.0 = Release|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 | False
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/BL3SaveEditor/App.xaml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/BL3SaveEditor/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | namespace TTWSaveEditor
4 | {
5 | ///
6 | /// Interaction logic for App.xaml
7 | ///
8 | public partial class App : Application
9 | {
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/BL3SaveEditor/AutoUpdater.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 | 0.1.0.0
5 | https://github.com/jokarwent/BL3SaveEditor/releases/latest/download/BL3SaveEditor-Portable.zip
6 | https://github.com/jokarwent/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/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/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 | namespace TTWSaveEditor.Controls
2 | {
3 | ///
4 | /// Interaction logic for IntegerMessageBox.xaml
5 | ///
6 | public partial class IntegerMessageBox
7 | {
8 |
9 | ///
10 | /// Minimum value for the integer message box
11 | ///
12 | public int Minimum { get; set; }
13 |
14 | ///
15 | /// Maximum value for the integer message box
16 | ///
17 | public int Maximum { get; set; }
18 |
19 | ///
20 | /// Whether or not the user agreed to save their inputted value
21 | ///
22 | public bool Succeeded { get; private set; }
23 |
24 | ///
25 | /// The last inputted value; regardless of whether or not the user saved/okayed the message box
26 | ///
27 | public int Result { get; set; }
28 |
29 | ///
30 | /// The message displayed to the users when they open the box
31 | ///
32 | public string Message { get; private set; }
33 |
34 | ///
35 | /// The text labelling the input box
36 | ///
37 | public string Label { get; private set; } = "";
38 |
39 | public IntegerMessageBox(string message, string label, int minimum = int.MinValue, int maximum = int.MaxValue, int defaultValue = 0)
40 | {
41 | InitializeComponent();
42 |
43 | this.Message = message;
44 | this.Label = label;
45 | this.Minimum = minimum;
46 | this.Maximum = maximum;
47 | this.Result = defaultValue;
48 |
49 | this.DataContext = null;
50 | this.DataContext = this;
51 | }
52 |
53 | private void OkBtn_Click(object sender, System.Windows.RoutedEventArgs e)
54 | {
55 | this.Succeeded = true;
56 | this.Close();
57 | }
58 |
59 | private void ExitBtn_Click(object sender, System.Windows.RoutedEventArgs e)
60 | {
61 | this.Succeeded = false;
62 | this.Close();
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/BL3SaveEditor/Controls/ItemBalanceChanger.xaml:
--------------------------------------------------------------------------------
1 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
44 |
45 |
51 |
52 |
53 |
57 |
66 |
67 |
73 |
74 |
75 |
76 |
77 |
85 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/BL3SaveEditor/Controls/ItemBalanceChanger.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Windows.Controls;
3 | using System.Windows.Data;
4 | using BL3Tools.GameData.Items;
5 |
6 | namespace TTWSaveEditor.Controls
7 | {
8 | ///
9 | /// A simple UI which allows the user to change an item balance based off of the type / balance
10 | ///
11 | public partial class ItemBalanceChanger
12 | {
13 |
14 | public ListCollectionView ItemTypes
15 | {
16 | get
17 | {
18 | var value = InventoryKeyDB.ItemTypeToKey.Keys.ToList();
19 | return new ListCollectionView(value);
20 | }
21 | }
22 |
23 | public ListCollectionView Balances
24 | {
25 | get
26 | {
27 | if (SelectedItemType == null || !InventoryKeyDB.ItemTypeToKey.ContainsKey(SelectedItemType)) return null;
28 | var itemKeys = InventoryKeyDB.ItemTypeToKey[SelectedItemType];
29 | var longNames = InventoryKeyDB.KeyDictionary.Where(x => itemKeys.Contains(x.Value)).Select(x => x.Key).ToList();
30 | var shortNames = longNames.Select(x => InventorySerialDatabase.GetShortNameFromBalance(x)).Where(x => x != null).ToList();
31 |
32 | return new ListCollectionView(shortNames);
33 | }
34 | }
35 |
36 | private string _SelectedItemType = null;
37 | public string SelectedItemType
38 | {
39 | get
40 | {
41 | return _SelectedItemType;
42 | }
43 | set
44 | {
45 | _SelectedItemType = value;
46 | if (!IsStarted) return;
47 |
48 | this.DataContext = null;
49 | this.DataContext = this;
50 | }
51 | }
52 |
53 | public string SelectedBalance { get; set; }
54 | public string SelectedInventoryData { get; private set; }
55 |
56 | public bool IsStarted = false;
57 |
58 | public ItemBalanceChanger()
59 | {
60 | InitializeComponent();
61 |
62 | IsStarted = true;
63 | }
64 |
65 | public ItemBalanceChanger(string itemType, string balance)
66 | {
67 | InitializeComponent();
68 |
69 | this.SelectedItemType = itemType;
70 | this.SelectedBalance = balance;
71 |
72 | this.DataContext = null;
73 | this.DataContext = this;
74 |
75 | this.IsStarted = true;
76 | }
77 |
78 | private void ExitBtn_Click(object sender, System.Windows.RoutedEventArgs e)
79 | {
80 | SelectedItemType = null;
81 | SelectedBalance = null;
82 | SelectedInventoryData = null;
83 | this.Close();
84 | }
85 |
86 | private void SaveBtn_Click(object sender, System.Windows.RoutedEventArgs e)
87 | {
88 | string longName = InventorySerialDatabase.GetBalanceFromShortName(SelectedBalance);
89 | SelectedInventoryData = InventorySerialDatabase.GetInventoryDataByBalance(longName);
90 |
91 | this.Close();
92 | }
93 |
94 | private void BalanceBox_Selected(object sender, SelectionChangedEventArgs e)
95 | {
96 | if (!IsStarted) return;
97 |
98 | var box = (ComboBox)sender;
99 | if (box.SelectedItem == null && Balances != null)
100 | {
101 | box.SelectedIndex = 0;
102 | SelectedBalance = Balances.SourceCollection.OfType().FirstOrDefault();
103 | }
104 | }
105 |
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/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 TTWSaveEditor.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 TTWSaveEditor.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/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Icons/fugue/clipboard-text.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/fugue/cross-script.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Icons/fugue/cross-script.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/fugue/disk--plus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Icons/fugue/disk--plus.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/fugue/disk--purple.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Icons/fugue/disk--purple.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/fugue/disk-black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Icons/fugue/disk-black.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/fugue/document-binary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Icons/fugue/document-binary.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/fugue/document-copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Icons/fugue/document-copy.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/fugue/documents-stack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Icons/fugue/documents-stack.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/fugue/documents-text.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Icons/fugue/documents-text.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/fugue/folder-open-document.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Icons/fugue/folder-open-document.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/fugue/wrench-screwdriver.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Icons/fugue/wrench-screwdriver.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/AllModIcons_I10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Icons/game/AllModIcons_I10.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/AllModIcons_I12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Icons/game/AllModIcons_I12.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/AllModIcons_I2C.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Icons/game/AllModIcons_I2C.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/AllModIcons_I2E.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Icons/game/AllModIcons_I2E.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/AllModIcons_I30.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Icons/game/AllModIcons_I30.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/BITMAP_ICO_Expansion_4k.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Icons/game/BITMAP_ICO_Expansion_4k.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/ECHOComm_IE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Icons/game/ECHOComm_IE.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/MinimapIcon_GalaxyMap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Icons/game/MinimapIcon_GalaxyMap.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/MinimapIcon_LostFoundFull.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Icons/game/MinimapIcon_LostFoundFull.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/MinimapIcon_MiniBoss.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Icons/game/MinimapIcon_MiniBoss.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/MinimapIcon_Pinging_Loot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Icons/game/MinimapIcon_Pinging_Loot.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/MinimapIcon_QuickChange.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Icons/game/MinimapIcon_QuickChange.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/MinimapIcon_Respawn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Icons/game/MinimapIcon_Respawn.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/MinimapIcon_SDUVendor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Icons/game/MinimapIcon_SDUVendor.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/MissionIcon_TurnIn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Icons/game/MissionIcon_TurnIn.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/promptIconDiamondKey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Icons/game/promptIconDiamondKey.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/promptIconEridium.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Icons/game/promptIconEridium.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/game/promptIconGoldenKey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Icons/game/promptIconGoldenKey.png
--------------------------------------------------------------------------------
/BL3SaveEditor/Icons/icons/Borderlands 3 001 512 x 512 ICO.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/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/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/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/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/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/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/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/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/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/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/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/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/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/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/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("Tiny Tina's Wonderlands Save Editor")]
11 | [assembly: AssemblyDescription("A save and profile editor for Tiny Tina's Wonderlands based on the FromDarkHell's BL3 save and profile editor.")]
12 | [assembly: AssemblyConfiguration("")]
13 | [assembly: AssemblyCompany("Arwent")]
14 | [assembly: AssemblyProduct("Tiny Tina's Wonderlands 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("0.1.0.0")]
55 | [assembly: AssemblyFileVersion("0.1.0.0")]
56 | [assembly: NeutralResourcesLanguage("en-US")]
57 |
--------------------------------------------------------------------------------
/BL3SaveEditor/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // Ce code a été généré par un outil.
4 | // Version du runtime :4.0.30319.42000
5 | //
6 | // Les modifications apportées à ce fichier peuvent provoquer un comportement incorrect et seront perdues si
7 | // le code est régénéré.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace TTWSaveEditor.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// Une classe de ressource fortement typée destinée, entre autres, à la consultation des chaînes localisées.
17 | ///
18 | // Cette classe a été générée automatiquement par la classe StronglyTypedResourceBuilder
19 | // à l'aide d'un outil, tel que ResGen ou Visual Studio.
20 | // Pour ajouter ou supprimer un membre, modifiez votre fichier .ResX, puis réexécutez ResGen
21 | // avec l'option /str ou régénérez votre projet VS.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.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 | /// Retourne l'instance ResourceManager mise en cache utilisée par cette classe.
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("TTWSaveEditor.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Remplace la propriété CurrentUICulture du thread actuel pour toutes
51 | /// les recherches de ressources à l'aide de cette classe de ressource fortement typée.
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 TTWSaveEditor.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.6.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 | [global::System.Configuration.UserScopedSettingAttribute()]
39 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
40 | [global::System.Configuration.DefaultSettingValueAttribute("False")]
41 | public bool bReduxModeEnabled {
42 | get {
43 | return ((bool)(this["bReduxModeEnabled"]));
44 | }
45 | set {
46 | this["bReduxModeEnabled"] = value;
47 | }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/BL3SaveEditor/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | True
7 |
8 |
9 | False
10 |
11 |
12 |
--------------------------------------------------------------------------------
/BL3SaveEditor/Starters/Zane.sav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/Starters/Zane.sav
--------------------------------------------------------------------------------
/BL3SaveEditor/TTWSaveEditor.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {A363B5AB-89B8-40F7-85A9-6A7341E183EF}
8 | WinExe
9 | TTWSaveEditor
10 | TTWSaveEditor
11 | v4.7.2
12 | 512
13 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
14 | 4
15 | true
16 | true
17 | publish\
18 | true
19 | Disk
20 | false
21 | Foreground
22 | 7
23 | Days
24 | false
25 | false
26 | true
27 | 0
28 | 1.0.0.%2a
29 | false
30 | false
31 | true
32 |
33 |
34 | AnyCPU
35 | true
36 | full
37 | false
38 | bin\Debug\
39 | DEBUG;TRACE
40 | prompt
41 | 4
42 | true
43 |
44 |
45 | AnyCPU
46 | none
47 | true
48 | bin\Release\
49 | TRACE
50 | prompt
51 | 4
52 | true
53 |
54 |
55 | ttw_ico.ico
56 |
57 |
58 | bin\Single File\
59 | SINGLE_FILE
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | 4.0
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | MSBuild:Compile
85 | Designer
86 |
87 |
88 | App.xaml
89 | Code
90 |
91 |
92 | Designer
93 | MSBuild:Compile
94 |
95 |
96 | IntegerMessageBox.xaml
97 |
98 |
99 | Designer
100 | MSBuild:Compile
101 |
102 |
103 | ItemBalanceChanger.xaml
104 |
105 |
106 | MSBuild:Compile
107 | Designer
108 |
109 |
110 | DebugConsole.xaml
111 |
112 |
113 | MSBuild:Compile
114 | Designer
115 |
116 |
117 | MainWindow.xaml
118 |
119 |
120 | Designer
121 | MSBuild:Compile
122 |
123 |
124 |
125 |
126 |
127 |
128 | Code
129 |
130 |
131 | True
132 | True
133 | Resources.resx
134 |
135 |
136 | True
137 | Settings.settings
138 | True
139 |
140 |
141 | ResXFileCodeGenerator
142 | Resources.Designer.cs
143 |
144 |
145 | SettingsSingleFileGenerator
146 | Settings.Designer.cs
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 | {eefeab45-b558-4d33-9c74-d68660e26921}
173 | BL3Tools
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 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 | 1.17.0
219 |
220 |
221 | 1.17.0
222 |
223 |
224 | 1.7.0
225 |
226 |
227 | 5.4.0
228 | runtime; build; native; contentfiles; analyzers; buildtransitive
229 | all
230 |
231 |
232 | 4.1.0
233 |
234 |
235 | 6.5.2
236 | runtime; build; native; contentfiles; analyzers; buildtransitive
237 | all
238 |
239 |
240 | 13.0.1
241 |
242 |
243 | 3.1.0
244 |
245 |
246 | 3.0.101
247 |
248 |
249 | 4.5.1
250 |
251 |
252 | 1.7.1
253 |
254 |
255 | 4.5.4
256 |
257 |
258 | 4.5.0
259 |
260 |
261 | 4.5.3
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 | False
286 | Microsoft .NET Framework 4.7.2 %28x86 and x64%29
287 | true
288 |
289 |
290 | False
291 | .NET Framework 3.5 SP1
292 | false
293 |
294 |
295 |
296 |
--------------------------------------------------------------------------------
/BL3SaveEditor/ttw_ico.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/somefunguy/TTWLSaveEditor/c6d701ec4a5ae1c115017de54d39df4813c035c1/BL3SaveEditor/ttw_ico.ico
--------------------------------------------------------------------------------
/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 | none
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 | True
118 | True
119 | Settings.settings
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 | .editorconfig
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 | SettingsSingleFileGenerator
146 | Settings.Designer.cs
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 | {b93ff5db-ffef-4f81-812e-f5fdf8cdfbbe}
166 | IOTools
167 |
168 |
169 |
170 |
171 |
172 | 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}.
173 |
174 |
175 |
176 |
177 |
178 |
179 | powershell -executionpolicy bypass -File "$(ProjectDir)FixProtobufs.ps1"
180 |
181 |
--------------------------------------------------------------------------------
/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/DataPathTranslations.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Collections.Generic;
4 | using BL3Tools.Decryption;
5 | using System.Reflection;
6 | using System.IO;
7 | using Newtonsoft.Json.Linq;
8 | using BL3Tools.GameData.Items;
9 | using System.Text.RegularExpressions;
10 |
11 | namespace BL3Tools.GameData
12 | {
13 | public static class DataPathTranslations
14 | {
15 | ///
16 | /// A list of all of the hashes of the weapon skins
17 | ///
18 | public static readonly List weaponSkinHashes = new List();
19 |
20 | ///
21 | /// A list of all of the hashes of the weapon trinkets
22 | ///
23 | public static readonly List trinketHashes = new List();
24 |
25 | public static readonly string BankSDUAssetPath = "/Game/Pickups/SDU/SDU_Bank.SDU_Bank";
26 | public static readonly string LLSDUAssetPath = "/Game/Pickups/SDU/SDU_LostLoot.SDU_LostLoot";
27 |
28 | // /-Extract\OakGame\Content\Gear\_Shared\_Design\InventoryCategories
29 | public static readonly string GoldenKeyCurrencyPath = "/Game/Gear/_Shared/_Design/InventoryCategories/InventoryCategory_GoldenKey.InventoryCategory_GoldenKey";
30 | public static readonly string DiamondKeyCurrencyPath = "/Game/Gear/_Shared/_Design/InventoryCategories/InventoryCategory_DiamondKey.InventoryCategory_DiamondKey";
31 | public static readonly string MoneyCurrencyPath = "/Game/Gear/_Shared/_Design/InventoryCategories/InventoryCategory_Money.InventoryCategory_Money";
32 | public static readonly string EridiumCurrencyPath = "/Game/Gear/_Shared/_Design/InventoryCategories/InventoryCategory_Eridium.InventoryCategory_Eridium";
33 | public static readonly string VaultCard1Path = "/Game/Gear/_Shared/_Design/InventoryCategories/InventoryCategory_VaultCard1Key";
34 | public static readonly string VaultCard2Path = "/Game/Gear/_Shared/_Design/InventoryCategories/InventoryCategory_VaultCard2Key";
35 |
36 | public static uint GoldenKeyHash { get; private set; }
37 | public static uint DiamondKeyHash { get; private set; }
38 | public static uint VaultCard1Hash { get; private set; }
39 | public static uint VaultCard2Hash { get; private set; }
40 | public static uint MoneyHash { get; private set; }
41 | public static uint EridiumHash { get; private set; }
42 |
43 | private static readonly string embeddedFastTravelDatabasePath = "BL3Tools.GameData.Mappings.fast_travel_to_name.json";
44 |
45 | public static void Initialize()
46 | {
47 | GoldenKeyHash = CRC32.Get(GoldenKeyCurrencyPath);
48 | DiamondKeyHash = CRC32.Get(DiamondKeyCurrencyPath);
49 | VaultCard1Hash = CRC32.Get(VaultCard1Path);
50 | VaultCard2Hash = CRC32.Get(VaultCard2Path);
51 | MoneyHash = CRC32.Get(MoneyCurrencyPath);
52 | EridiumHash = CRC32.Get(EridiumCurrencyPath);
53 |
54 | Console.WriteLine("Initialized CRC32 hashes of customization & currency data");
55 |
56 | Assembly me = typeof(BL3Tools).Assembly;
57 | using (Stream stream = me.GetManifestResourceStream(embeddedFastTravelDatabasePath))
58 | using (StreamReader reader = new StreamReader(stream))
59 | {
60 | string result = reader.ReadToEnd();
61 | JObject db = JObject.FromObject(Newtonsoft.Json.JsonConvert.DeserializeObject(result));
62 | FastTravelTranslations = db.ToObject>();
63 | }
64 | }
65 |
66 | static DataPathTranslations()
67 | {
68 | Initialize();
69 | }
70 |
71 | #region Translation Dictionaries
72 |
73 | #region Fast Travels
74 |
75 | public static List UnobtainableFastTravels { get; private set; } = new List() {
76 | "/Game/GameData/FastTravel/FTS_OrbitalPlatform.FTS_OrbitalPlatform",
77 | "/Game/PatchDLC/Raid1/GameData/FastTravel/LevelTravelData/FTS_MaliwanTD_SendOnly.FTS_MaliwanTD_SendOnly",
78 | "/Game/GameData/FastTravel/FTS_ProvingGrounds01.FTS_ProvingGrounds01",
79 | "/Game/PatchDLC/Alisma/GameData/FastTravel/FTS_ALI_Chase_Boss.FTS_ALI_Chase_Boss",
80 | "/Game/GameData/FastTravel/FTS_FinalBossPortal.FTS_FinalBossPortal",
81 | "/Game/GameData/FastTravel/FTS_Prison_SendOnly.FTS_Prison_SendOnly",
82 | "/Game/PatchDLC/Ixora2/GameData/FastTravel/LevelTravel/FTS_Ixora2_NekroMystery_OneWay.FTS_Ixora2_NekroMystery_OneWay",
83 | "/Game/GameData/FastTravel/FTS_ProvingGrounds02.FTS_ProvingGrounds02",
84 | "/Game/GameData/FastTravel/FTS_TechSlaughter.FTS_TechSlaughter",
85 | "/Game/PatchDLC/Dandelion/GameData/FastTravel/FTS_Strip_DLC1_TricksyNickArea.FTS_Strip_DLC1_TricksyNickArea",
86 | "/Game/GameData/FastTravel/FTS_ProvingGrounds04.FTS_ProvingGrounds04",
87 | "/Game/PatchDLC/Takedown2/GameData/LevelTravel/FTS_GuardianTD_SendOnly.FTS_GuardianTD_SendOnly",
88 | "/Game/GameData/FastTravel/FTS_AtlasHQ_SendOnly.FTS_AtlasHQ_SendOnly",
89 | "/Game/GameData/FastTravel/FTS_Grotto.FTS_Grotto",
90 | "/Game/PatchDLC/Alisma/GameData/FastTravel/FTS_ALI_Eldorado_Boss.FTS_ALI_Eldorado_Boss",
91 | "/Game/GameData/FastTravel/FTS_Raid.FTS_Raid",
92 | "/Game/PatchDLC/Raid1/GameData/FastTravel/LevelTravelData/FTS_Raid1.FTS_Raid1",
93 | "/Game/GameData/FastTravel/FTS_MotorcadeInterior_SendOnly.FTS_MotorcadeInterior_SendOnly",
94 | "/Game/PatchDLC/Alisma/GameData/FastTravel/FTS_ALI_Experiment_Boss.FTS_ALI_Experiment_Boss",
95 | "/Game/PatchDLC/Ixora/GameData/FastTravel/LevelTravel/FTS_GearUpMap_SendOnly.FTS_GearUpMap_SendOnly",
96 | "/Game/GameData/FastTravel/FTS_ProvingGrounds03.FTS_ProvingGrounds03",
97 | "/Game/PatchDLC/Alisma/GameData/FastTravel/FTS_ALI_Anger_Boss.FTS_ALI_Anger_Boss",
98 | "/Game/PatchDLC/Ixora2/GameData/FastTravel/LevelTravel/FTS_Ixora2_SacrificeBoss_One.FTS_Ixora2_SacrificeBoss_One",
99 | "/Game/GameData/FastTravel/FTS_ProvingGrounds07.FTS_ProvingGrounds07",
100 | "/Game/PatchDLC/Takedown2/GameData/LevelTravel/FTS_GuardianTD_SendOnly_2.FTS_GuardianTD_SendOnly_2",
101 | "/Game/GameData/FastTravel/FTS_ProvingGrounds00.FTS_ProvingGrounds00",
102 | "/Game/GameData/FastTravel/FTS_ProvingGrounds08.FTS_ProvingGrounds08",
103 | "/Game/GameData/FastTravel/FTS_ZoneMapTest.FTS_ZoneMapTest",
104 | "/Game/GameData/FastTravel/FTS_ProvingGrounds05.FTS_ProvingGrounds05",
105 | "/Game/PatchDLC/Hibiscus/GameData/FastTravel/FTS_DLC2_Lake_Excavation.FTS_DLC2_Lake_Excavation",
106 | "/Game/PatchDLC/Ixora2/GameData/FastTravel/LevelTravel/FTS_Ixora2_Promethea.FTS_Ixora2_Promethea",
107 | "/Game/GameData/FastTravel/FTS_ZoneMapTest2.FTS_ZoneMapTest2",
108 | "/Game/PatchDLC/Takedown2/GameData/LevelTravel/FTS_TD2.FTS_TD2",
109 | "/Game/GameData/FastTravel/FTS_Monastery.FTS_Monastery",
110 | "/Game/GameData/FastTravel/FTS_Marshfields_SendOnly.FTS_Marshfields_SendOnly",
111 | "/Game/GameData/FastTravel/FTS_WetlandsBoss_SendOnly.FTS_WetlandsBoss_SendOnly",
112 | "/Game/GameData/FastTravel/FTS_PlayableIntro_SendOnly.FTS_PlayableIntro_SendOnly",
113 | "/Game/GameData/FastTravel/FTS_ProvingGrounds06.FTS_ProvingGrounds06",
114 | "/Game/GameData/FastTravel/FTS_CityBoss_SendOnly.FTS_CityBoss_SendOnly"
115 | };
116 |
117 | public static Dictionary FastTravelTranslations { get; private set; } = new Dictionary();
118 | #endregion
119 |
120 | #region Myth Rank
121 |
122 | public static readonly string[] MythRankAttributes = new string[]
123 | {
124 | "Intelligence",
125 | "Spell Critical Damage",
126 | "Spell Critical Chance",
127 | "Fire Damage",
128 | "Frost Damage",
129 | "Status Effect Chance",
130 | "Spell Damage",
131 | "Strength",
132 | "Constitution",
133 | "Dark Magic Damage",
134 | "Melee Critical Chance",
135 | "Melee Swing Speed",
136 | "Melee Critical Damage",
137 | "Melee Damage",
138 | "Dexterity",
139 | "Gun Handling",
140 | "Move Speed",
141 | "Reload Speed",
142 | "Magazine Size",
143 | "Fire Rate",
144 | "Gun Damage",
145 | "Wisdom",
146 | "Companion Damage",
147 | "Attunement",
148 | "Lightning Damage",
149 | "Loot Luck",
150 | "Poison Damage",
151 | "Ability Damage"
152 | };
153 | #endregion
154 |
155 | #region Items etc
156 | public static Dictionary SlotToPathDictionary = new Dictionary() {
157 | {"Weapon1", "/Game/Gear/Weapons/_Shared/_Design/InventorySlots/BPInvSlot_Weapon1.BPInvSlot_Weapon1" },
158 | {"Weapon2", "/Game/Gear/Weapons/_Shared/_Design/InventorySlots/BPInvSlot_Weapon2.BPInvSlot_Weapon2" },
159 | {"Weapon3", "/Game/Gear/Weapons/_Shared/_Design/InventorySlots/BPInvSlot_Weapon3.BPInvSlot_Weapon3" },
160 | {"Weapon4", "/Game/Gear/Weapons/_Shared/_Design/InventorySlots/BPInvSlot_Weapon4.BPInvSlot_Weapon4" },
161 | {"Amulet", "/Game/Gear/Amulets/_Shared/_Design/A_Data/InvSlot_Amulet.InvSlot_Amulet"},
162 | {"Spell", "/Game/Gear/SpellMods/_Shared/_Design/A_Data/BPInvSlot_SpellMod.BPInvSlot_SpellMod"},
163 | {"SecondSpell", "/Game/Gear/SpellMods/_Shared/_Design/A_Data/BPInvSlot_SecondSpellMod.BPInvSlot_SecondSpellMod"},
164 | {"Ring1", "/Game/Gear/Rings/_Shared/Design/A_Data/InvSlot_Ring_1.InvSlot_Ring_1"},
165 | {"Ring2", "/Game/Gear/Rings/_Shared/Design/A_Data/InvSlot_Ring_2.InvSlot_Ring_2"},
166 | {"Pauldron", "/Game/Gear/Pauldrons/_Shared/_Design/A_Data/InvSlot_Pauldron.InvSlot_Pauldron"}
167 | };
168 | #endregion
169 |
170 | #region Customizations
171 |
172 | #endregion
173 |
174 | #endregion
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/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: 65.0, Power: 2.7999999523162841796875, Offset: 7.3299999237060546875}
12 | // PlayerGuardianRankExperienceFormula={BaseValue: 40.0, BaseMultiplier: 1.0, Multiplier: 60.0, Power: 2.2999999523162841796875, Offset: 12.3299999237060546875}
13 | // expBaseValueXP is calculated from (65*(0+1)^2.3) mythBaseValueXP is calculated from (60*(40+1)^2.3)
14 | private const float expMultiplier = 65.0f;
15 | private const float expBaseValueXP = 65.0f;
16 | private const float expPower = 2.7999999523162841796875f;
17 | private const float expOffset = 7.3299999237060546875f;
18 | private const float mythBaseValue = 40.0f;
19 | private const float mythBaseValueXP = 307294.028457972f;
20 | private const float mythBaseMultiplier = 1.0f;
21 | private const float mythMultiplier = 60.0f;
22 | private const float mythPower = 2.2999999523162841796875f;
23 | private const float mythOffset = 12.3299999237060546875f;
24 |
25 | public static int _XPMaximumLevel { get; } = 40;
26 | public static int _XPMinimumLevel { get; } = 1;
27 | private static readonly int _XPReduction = 0;
28 |
29 | private static Dictionary xpLevelTable = new Dictionary();
30 |
31 | private static int ComputeEXPLevel(int level) {
32 | return (int)Math.Ceiling((Math.Pow(level, expPower) * expMultiplier) - expBaseValueXP);
33 | }
34 |
35 | static PlayerXP() {
36 | _XPReduction = ComputeEXPLevel(_XPMinimumLevel);
37 |
38 | // Add to the dictionary
39 | for (int lvl = 1; lvl <= _XPMaximumLevel; lvl++) {
40 | xpLevelTable.Add(lvl, GetPointsForXPLevel(lvl));
41 | }
42 |
43 | }
44 |
45 | ///
46 | /// Gets the points for the associated XP level
47 | ///
48 | /// EXP Level
49 | /// The amount of EXP points for the associated amount of points
50 | public static int GetPointsForXPLevel(int lvl) {
51 | if (lvl <= _XPMinimumLevel) return 0;
52 |
53 | return ComputeEXPLevel(lvl) - _XPReduction;
54 | }
55 |
56 | ///
57 | /// Gets the respective level for a given amount of XP points
58 | ///
59 | /// Amount of XP Points
60 | /// The level associated with the points
61 | public static int GetLevelForPoints(int points) {
62 | if (points < 0) return _XPMinimumLevel;
63 | if (points >= xpLevelTable.Last().Value) return _XPMaximumLevel;
64 |
65 | // Get the closest level to the point amounts (price is right rules)
66 | return xpLevelTable.First(lv => points < lv.Value).Key - 1;
67 | }
68 |
69 | public static long GetPointsForMythPoints(int points)
70 | {
71 | return (long)Math.Ceiling((Math.Pow(points, mythPower) * mythMultiplier) - mythBaseValueXP);
72 | }
73 | }
74 |
75 | public static class MayhemLevel {
76 | public static readonly int MinimumLevel = 0;
77 | public static readonly int MaximumLevel = 10;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/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 | using System.Windows.Controls;
9 |
10 | namespace BL3Tools.GameData.Items {
11 | public static class InventoryKeyDB {
12 | ///
13 | /// A JObject representing the InventoryKey DB as loaded from JSON
14 | ///
15 | private static JObject KeyDatabase { get; set; } = null;
16 |
17 | ///
18 | /// An easy to use dictionary mapping the balances to a SerialDB key as loaded from the DB.
19 | ///
20 | public static Dictionary KeyDictionary { get; private set; } = null;
21 |
22 | public static Dictionary> ItemTypeToKey { get; private set; } = null;
23 |
24 | private static readonly string embeddedDatabasePath = "BL3Tools.GameData.Items.Mappings.balance_to_inv_key.json";
25 | private static readonly string embeddedReduxDatabasePath = "BL3Tools.GameData.Items.Mappings.balance_to_inv_key_redux.json";
26 |
27 | static InventoryKeyDB() {
28 | Console.WriteLine("Initializing InventoryKeyDB...");
29 |
30 | Assembly me = typeof(BL3Tools).Assembly;
31 |
32 | // set REDUX mode from project settings
33 | bool isRedux = Properties.Settings.Default.bReduxModeEnabled;
34 |
35 | // load either redux database or normal
36 | if (isRedux) {
37 | using (Stream stream = me.GetManifestResourceStream(embeddedReduxDatabasePath))
38 | using (StreamReader reader = new StreamReader(stream))
39 | {
40 | string result = reader.ReadToEnd();
41 |
42 | LoadInventoryKeyDatabase(result);
43 | }
44 | }
45 | else {
46 | using (Stream stream = me.GetManifestResourceStream(embeddedDatabasePath))
47 | using (StreamReader reader = new StreamReader(stream))
48 | {
49 | string result = reader.ReadToEnd();
50 |
51 | LoadInventoryKeyDatabase(result);
52 | }
53 | }
54 |
55 | }
56 |
57 | ///
58 | /// Replace the inventory serial database info with the one specified in this specific string
59 | ///
60 | /// A JSON string representing the InventorySerialDB
61 | /// Whether or not the loading succeeded
62 | public static bool LoadInventoryKeyDatabase(string JSONString) {
63 | var lastDB = KeyDatabase;
64 | try {
65 | JObject db = JObject.FromObject(JsonConvert.DeserializeObject(JSONString));
66 | KeyDictionary = db.ToObject>();
67 | KeyDatabase = db;
68 |
69 | var invKeys = KeyDictionary.Values.Distinct();
70 |
71 | ItemTypeToKey = new Dictionary>() {
72 | { "Amulets", invKeys.Where(x => x.Contains("_Amulet_")).ToList() },
73 | { "Axes", invKeys.Where(x => x.Contains("_Melee_Axe_")).ToList() },
74 | { "Blunts", invKeys.Where(x => x.Contains("_Melee_Blunt_")).ToList() },
75 | { "Sword 1-Handed", invKeys.Where(x => x.Contains("_Melee_Sword_")).ToList() },
76 | { "Sword 2-Handed", invKeys.Where(x => x.Contains("_Melee_Sword_2H_")).ToList() },
77 | { "Pauldrons", invKeys.Where(x => x.Contains("_Pauldron_")).ToList() },
78 | { "Rings", invKeys.Where(x => x.Contains("_Ring_")).ToList() },
79 | { "Spells", invKeys.Where(x => x.Contains("_SpellMod_")).ToList() },
80 | { "Shields", invKeys.Where(x => x.Contains("_Shield_")).ToList() },
81 | { "Assault Rifles", invKeys.Where(x => x.Contains("_AR_")).ToList() },
82 | { "Pistols", invKeys.Where(x => x.Contains("_Pistol_") || x.Contains("_PS_")).ToList() },
83 | { "SMGs", invKeys.Where(x => x.Contains("_SM_") || x.Contains("_SMG")).ToList() },
84 | { "Heavy Weapons", invKeys.Where(x => x.Contains("_HW_")).ToList() },
85 | { "Shotguns", invKeys.Where(x => x.Contains("_SG_") || x.Contains("_Shotgun_")).ToList() },
86 | { "Sniper Rifles", invKeys.Where(x => x.Contains("_SR_")).ToList() },
87 | //{ "Customizations", invKeys.Where(x => x.Contains("Customization")).ToList() },
88 | //test InventoryBalanceData to force population of customization parts
89 | //{ "Customizations", invKeys.Where(x => x.Contains("InventoryBalanceData")).ToList() },
90 | }.OrderBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value);
91 |
92 | return true;
93 | }
94 | catch (Exception) {
95 | KeyDatabase = lastDB;
96 | }
97 |
98 | return false;
99 | }
100 |
101 |
102 | public static string GetKeyForBalance(string balance) {
103 |
104 | // Check if the name exists by default
105 | if (!balance.Contains(".")) balance = $"{balance}.{balance.Split('/').LastOrDefault()}";
106 |
107 | if (KeyDictionary.ContainsKey(balance))
108 | return KeyDictionary[balance];
109 | else if (KeyDictionary.ContainsKey(balance.ToLower()))
110 | return KeyDictionary[balance.ToLower()];
111 |
112 | return null;
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/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 | private static readonly string embeddedReduxNameDatabase = "BL3Tools.GameData.Items.Mappings.part_name_mapping_redux.json";
34 |
35 | static InventoryNameDatabase() {
36 | Console.WriteLine("Initializing InventoryNameDatabase...");
37 |
38 | Assembly me = typeof(BL3Tools).Assembly;
39 |
40 | // set REDUX mode from project settings
41 | bool isRedux = Properties.Settings.Default.bReduxModeEnabled;
42 |
43 | if (isRedux) {
44 | using (Stream stream = me.GetManifestResourceStream(embeddedReduxNameDatabase))
45 |
46 | using (StreamReader reader = new StreamReader(stream))
47 | {
48 | string result = reader.ReadToEnd();
49 |
50 | LoadInventoryNameDatabase(result);
51 | }
52 | }
53 | else {
54 | using (Stream stream = me.GetManifestResourceStream(embeddedNameDatabase))
55 |
56 | using (StreamReader reader = new StreamReader(stream))
57 | {
58 | string result = reader.ReadToEnd();
59 |
60 | LoadInventoryNameDatabase(result);
61 | }
62 | }
63 |
64 |
65 | using (Stream stream = me.GetManifestResourceStream(embeddedPrefixDatabase))
66 | using (StreamReader reader = new StreamReader(stream)) {
67 | string result = reader.ReadToEnd();
68 |
69 | LoadInventoryPrefixDatabase(result);
70 | }
71 | }
72 |
73 | ///
74 | /// Replace the inventory serial database info with the one specified in this specific string
75 | ///
76 | /// A JSON string representing the InventorySerialDB
77 | /// Whether or not the loading succeeded
78 | public static bool LoadInventoryNameDatabase(string JSONString) {
79 | var lastDB = NameDatabase;
80 | try {
81 | JObject db = JObject.FromObject(JsonConvert.DeserializeObject(JSONString));
82 | NameDictionary = db.ToObject>();
83 | NameDatabase = db;
84 |
85 | return true;
86 | }
87 | catch (Exception) {
88 | NameDatabase = lastDB;
89 | }
90 |
91 | return false;
92 | }
93 |
94 | public static bool LoadInventoryPrefixDatabase(string JSONString) {
95 | var lastDB = PrefixDatabase;
96 | try {
97 | JObject db = JObject.FromObject(JsonConvert.DeserializeObject(JSONString));
98 | PrefixDictionary = db.ToObject>();
99 | PrefixDatabase = db;
100 |
101 | return true;
102 | }
103 | catch (Exception) {
104 | PrefixDatabase = lastDB;
105 | }
106 |
107 | return false;
108 | }
109 |
110 | ///
111 | /// Gets the associated ""human safe"" (non code) name for the given set of parts;
112 | /// You CAN include a customization balance in the parts and you will get a customization part instead of calling .
113 | ///
114 | /// A list of parts
115 | /// Whether or not to include prefixes in the result
116 | public static string GetNameForParts(List parts, bool bPrefixes = true) {
117 | string titleName = NameDictionary.Where(x => parts.Contains(x.Key)).Select(x => x.Value).LastOrDefault();
118 | string prefix = !bPrefixes ? null : PrefixDictionary.Where(x => parts.Contains(x.Key)).Select(x => x.Value).LastOrDefault();
119 | if (string.IsNullOrEmpty(titleName)) return null;
120 | else if (!string.IsNullOrEmpty(prefix))
121 | return (prefix + " " + titleName);
122 |
123 | return titleName;
124 | }
125 |
126 | ///
127 | /// Gets the associated ""human safe"" (non-code) name for a customization balance
128 | /// Since customizations don't have parts, this is the function you want to use in the event you need the name of a customization.
129 | ///
130 | /// The balance for the customization
131 | /// The human safe name of the balance
132 | public static string GetCustomizationNameForBalance(string balance) {
133 | string name = NameDictionary.Where(x => x.Key.Equals(balance)).Select(x => x.Value).LastOrDefault();
134 | if (string.IsNullOrEmpty(name)) return null;
135 |
136 | return name;
137 | }
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/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/Items/Mappings/balance_to_inv_data.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/BL3Tools/GameData/Items/Mappings/prefix_name_mapping.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/BL3Tools/GameData/Items/Mappings/valid_generics.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/BL3Tools/GameData/Items/Mappings/valid_part_database.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/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 | /// A decrypted Save Wizard's PS4 save.
24 | ///
25 | [Description("JSON")]
26 | JSON = 0x03
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/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/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 BL3Tools.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.6.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("False")]
29 | public bool bReduxModeEnabled {
30 | get {
31 | return ((bool)(this["bReduxModeEnabled"]));
32 | }
33 | set {
34 | this["bReduxModeEnabled"] = value;
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/BL3Tools/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | False
7 |
8 |
9 |
--------------------------------------------------------------------------------
/BL3Tools/Protobufs/OakProfile.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package OakSave;
3 | import "Protobufs/OakShared.proto";
4 | message PlayerInputBinding_Button {
5 | string rebind_name = 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_name = 1;
14 | repeated PlayerInputBinding_Axis_Key keys = 2;
15 | }
16 | message PlayerInputBinding_Schema {
17 | string schema_name = 1;
18 | repeated PlayerInputBinding_Button button_bindings = 2;
19 | repeated PlayerInputBinding_Axis axis_bindings = 3;
20 | }
21 | message PlayerInputBindings {
22 | repeated PlayerInputBinding_Schema schemas = 1;
23 | }
24 | message OakProfileLastInventoryFilterInfo {
25 | string slot_type_id = 1;
26 | int32 last_filter_index = 2;
27 | }
28 | message OakProfileMenuTutorialInfo {
29 | repeated string seen_tutorials = 1;
30 | bool tutorials_disabled = 2;
31 | bool tutorials_allowed_in_non_game_modes = 3;
32 | }
33 | message OakFriendEncounterData {
34 | uint32 num_encounters = 1;
35 | int64 time_last_encounter = 2;
36 | }
37 | message GearSoldByFriendData {
38 | string gear_serial_number = 1;
39 | int32 player_class_identifier_hash = 2;
40 | string friend_net_id = 3;
41 | }
42 | message PlayerPrestigeProfileData {
43 | int64 prestige_experience = 1;
44 | int32 last_upgrade_tree_index = 2;
45 | repeated int32 points_spent_by_index_order = 3;
46 | bool has_seen_tutorial = 4;
47 | }
48 | message RecentlyMetPlayer {
49 | string shift_player_id = 1;
50 | string first_party_player_id = 2;
51 | bool show_shift_player_entry = 3;
52 | }
53 | message AcceptedOfflineEULA {
54 | int32 id = 1;
55 | string version = 2;
56 | }
57 | message Profile {
58 | message FriendEncountersEntry {
59 | string key = 1;
60 | OakFriendEncounterData value = 2;
61 | }
62 | bool enable_aim_assist = 1;
63 | bool gamepad_invert_look = 2;
64 | bool gamepad_invert_turn = 3;
65 | bool gamepad_invert_move = 4;
66 | bool gamepad_invert_strafe = 5;
67 | bool enable_vibration = 6;
68 | bool invert_mouse_pitch = 7;
69 | bool enable_mouse_smoothing = 8;
70 | float mouse_scale = 9;
71 | bool show_damage_numbers = 10;
72 | bool show_damage_number_icons = 11;
73 | bool enable_training_messages = 12;
74 | bool show_text_chat = 13;
75 | bool center_crosshair = 14;
76 | bool toggle_sprint = 15;
77 | bool toggle_crouch = 16;
78 | bool censor_content = 17;
79 | float music_volume = 18;
80 | float sound_effects_volume = 19;
81 | float vo_volume = 20;
82 | float voice_volume = 21;
83 | bool enable_optional_vo = 22;
84 | bool push_to_talk = 23;
85 | bool enable_controller_audio = 24;
86 | float speaker_angle_front = 25;
87 | float speaker_angle_side = 26;
88 | float speaker_angle_back = 27;
89 | uint32 speaker_setup = 28;
90 | bool mute_audio_on_focus_loss = 29;
91 | repeated ChallengeSaveGameData challenge_data = 30;
92 | bool default_dead_zone_inner_updated = 32;
93 | bool disable_event_content = 33;
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 | repeated FriendEncountersEntry friend_encounters = 133;
174 | int32 max_friend_encounter_size = 134;
175 | repeated GameStatSaveGameData profile_stats_data = 135;
176 | repeated InventoryCategorySaveData bank_inventory_category_list = 136;
177 | uint32 two_player_splitscreen_layout = 137;
178 | repeated bytes lost_loot_inventory_list = 138;
179 | repeated OakMailItem npc_mail_items = 139;
180 | repeated string mail_guids = 140;
181 | repeated string unread_mail_guids = 141;
182 | repeated GearSoldByFriendData gear_sold_by_friends = 142;
183 | repeated OakSDUSaveGameData profile_sdu_list = 143;
184 | repeated OakCustomizationSaveGameData unlocked_customizations = 144;
185 | repeated OakInventoryCustomizationPartInfo unlocked_inventory_customization_parts = 145;
186 | bool fixed_initial_zonemap_rotation = 149;
187 | bool enable_mouse_acceleration = 150;
188 | bool enable_gamepad_input = 151;
189 | bool use_classic_gamepad_input = 152;
190 | float master_volume = 153;
191 | uint32 monitor_display_type = 154;
192 | uint32 graphics_mode = 155;
193 | uint32 frame_rate_limit = 156;
194 | float base_vehicle_fov = 157;
195 | uint32 graphics_quality = 158;
196 | uint32 anisotropic_filtering = 159;
197 | uint32 shadow_quality = 160;
198 | uint32 display_performance_stats = 161;
199 | uint32 texture_detail = 162;
200 | uint32 draw_distance = 163;
201 | uint32 clutter = 164;
202 | uint32 tessellation = 165;
203 | uint32 foliage = 166;
204 | bool foliage_shadows = 167;
205 | bool planar_reflections = 168;
206 | uint32 volumetric_fog = 169;
207 | uint32 screen_space_reflections = 170;
208 | uint32 character_texture_detail = 171;
209 | uint32 character_detail = 172;
210 | uint32 ambient_occlusion_quality = 173;
211 | bool object_motion_blur = 174;
212 | bool lens_flare = 175;
213 | bool combat_number_long_format = 176;
214 | bool show_minimap_legendaries = 177;
215 | bool use_player_callouts = 178;
216 | uint32 friend_event_notification_lifetime = 179;
217 | uint32 friend_event_notification_frequency = 180;
218 | uint32 trade_request_reception_type = 181;
219 | float head_bob_scale = 182;
220 | bool has_reset_console_fov = 183;
221 | bool has_seen_first_boot = 184;
222 | bool badass_event_enabled = 185;
223 | bool pinata_event_enabled = 186;
224 | int32 min_time_between_badass_events = 187;
225 | bool disable_spatial_audio = 188;
226 | float subs_cc_size = 189;
227 | float cc_subs_background_opacity = 190;
228 | uint32 walking_button_scheme = 191;
229 | uint32 driving_button_scheme = 192;
230 | uint32 glyph_mode = 193;
231 | bool use_MPH = 194;
232 | repeated RegisteredDownloadableEntitlements registered_downloadable_entitlements = 195;
233 | repeated string seen_news_items = 196;
234 | bool auto_centering_enabled = 197;
235 | bool increased_chance_for_subscribers = 198;
236 | bool rare_chest_event_enabled = 199;
237 | float hud_scale_multiplier = 200;
238 | int32 total_playtime_seconds = 201;
239 | uint32 desired_crossplay_state = 202;
240 | uint32 desired_friend_sync_state = 203;
241 | bool needs_shift_first_boot = 204;
242 | repeated RecentlyMetPlayer recently_met_players = 205;
243 | bool enable_trigger_feedback = 206;
244 | float camera_shake_scale = 207;
245 | PlayerPrestigeProfileData player_prestige = 208;
246 | bool needs_shift_first_boot_primary = 209;
247 | repeated AcceptedOfflineEULA accepted_offline_eulas = 210;
248 | string override_audio_language = 211;
249 | repeated OakInventoryItemSaveGameData bank_inventory_list = 212;
250 | }
251 |
--------------------------------------------------------------------------------
/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 EquippedInventorySaveGameData {
19 | int32 inventory_list_index = 1;
20 | bool enabled = 2;
21 | string slot_data_path = 3;
22 | }
23 | message OakAbilityTreeItemSaveGameData {
24 | string item_asset_path = 1;
25 | int32 points = 2;
26 | int32 max_points = 3;
27 | int32 tree_identifier = 4;
28 | }
29 | message OakAbilitySlotSaveGameData {
30 | string ability_class_path = 1;
31 | string slot_asset_path = 2;
32 | }
33 | message OakActionAbilityAugmentSaveGameData {
34 | string action_ability_class_path = 1;
35 | string slot_asset_path = 2;
36 | string augment_asset_path = 3;
37 | }
38 | message OakActionAbilityAugmentConfigurationSaveGameData {
39 | string ability_class_path = 1;
40 | string augment_asset_path = 2;
41 | string mod_slot_asset_path = 3;
42 | string mod_asset_path = 4;
43 | }
44 | message OakDualClassSaveGameData {
45 | string primary_branch_path = 1;
46 | repeated string unlocked_secondary_branch_paths = 2;
47 | string slotted_secondary_branch_path = 3;
48 | bool unlocked_class_features = 4;
49 | bool unlocked_initial_secondary_class = 5;
50 | bool unlocked_secondary_class_swapping = 6;
51 | }
52 | message OakPlayerAbilitySaveGameData {
53 | int32 ability_points = 1;
54 | repeated OakAbilityTreeItemSaveGameData tree_item_list = 2;
55 | repeated OakAbilitySlotSaveGameData ability_slot_list = 3;
56 | repeated OakActionAbilityAugmentSaveGameData augment_slot_list = 4;
57 | repeated OakActionAbilityAugmentConfigurationSaveGameData augment_configuration_list = 5;
58 | int32 tree_grade = 6;
59 | OakDualClassSaveGameData dual_class_save_data = 7;
60 | uint32 respec_count = 8;
61 | }
62 | message MissionStatusPlayerSaveGameData {
63 | enum MissionState {
64 | MS_NotStarted = 0;
65 | MS_Active = 1;
66 | MS_Complete = 2;
67 | MS_Failed = 3;
68 | MS_Unknown = 4;
69 | }
70 | MissionState status = 1;
71 | bool has_been_viewed_in_log = 2;
72 | repeated int32 objectives_progress = 3;
73 | string mission_class_path = 4;
74 | string active_objective_set_path = 5;
75 | uint32 dlc_package_id = 6;
76 | bool kickoff_played = 7;
77 | }
78 | message MissionPlaythroughSaveGameData {
79 | repeated MissionStatusPlayerSaveGameData mission_list = 1;
80 | string tracked_mission_class_path = 2;
81 | }
82 | message ActiveFastTravelSaveData {
83 | string active_travel_station_name = 1;
84 | bool blacklisted = 2;
85 | }
86 | message PlaythroughActiveFastTravelSaveData {
87 | repeated ActiveFastTravelSaveData active_travel_stations = 1;
88 | }
89 | message DiscoveredAreaInfo {
90 | string discovered_area_name = 1;
91 | uint32 discovered_playthroughs = 2;
92 | }
93 | message DiscoveredLevelInfo {
94 | string discovered_level_name = 1;
95 | uint32 discovered_playthroughs = 3;
96 | repeated DiscoveredAreaInfo discovered_area_info = 4;
97 | }
98 | message DiscoveredPlanetInfo {
99 | string discovered_planet = 1;
100 | bool is_new_planet = 2;
101 | }
102 | message DiscoverySaveData {
103 | repeated DiscoveredLevelInfo discovered_level_info = 1;
104 | }
105 | message VehicleUnlockedSaveGameData {
106 | string asset_path = 1;
107 | bool just_unlocked = 2;
108 | }
109 | message OakCARMenuVehicleConfigSaveData {
110 | string loadout_save_name = 1;
111 | string body_asset_path = 2;
112 | string wheel_asset_path = 3;
113 | string armor_asset_path = 4;
114 | string core_mod_asset_path = 5;
115 | string gunner_weapon_asset_path = 6;
116 | string driver_weapon_asset_path = 7;
117 | string ornament_asset_path = 8;
118 | string material_decal_asset_path = 9;
119 | string material_asset_path = 10;
120 | int32 color_index_1 = 11;
121 | int32 color_index_2 = 12;
122 | int32 color_index_3 = 13;
123 | }
124 | message CustomPlayerColorSaveGameData {
125 | string color_parameter = 1;
126 | Vec3 applied_color = 2;
127 | Vec3 split_color = 3;
128 | bool use_default_color = 4;
129 | bool use_default_split_color = 5;
130 | float applied_color_alpha = 6;
131 | float split_color_alpha = 7;
132 | }
133 | message CustomFloatCustomizationSaveGameData {
134 | string name = 1;
135 | float value = 2;
136 | }
137 | message EchoLogSaveGameData {
138 | bool has_been_seen_in_log = 1;
139 | string echo_log_path = 2;
140 | }
141 | message MapIDData {
142 | uint32 zone_name_id = 1;
143 | uint32 map_name_id = 2;
144 | }
145 | message GameStateSaveData {
146 | MapIDData last_traveled_map_id = 1;
147 | int32 mayhem_level = 2;
148 | int32 mayhem_random_seed = 3;
149 | int32 mayhem_unlocked_level = 4;
150 | }
151 | message ChallengeCategoryProgressSaveData {
152 | bytes category_progress = 1;
153 | }
154 | message OakPlayerCharacterAugmentSaveGameData {
155 | string slot_asset_path = 1;
156 | string augment_asset_path = 2;
157 | }
158 | message OakPlayerCharacterSlotSaveGameData {
159 | repeated OakPlayerCharacterAugmentSaveGameData augment_slot_list = 1;
160 | }
161 | message UITrackingSaveGameData {
162 | bool has_seen_skill_menu_unlock = 1;
163 | bool has_seen_echo_boot_ammo_bar = 3;
164 | bool has_seen_echo_boot_shield_bar = 4;
165 | bool has_seen_echo_boot_grenades = 5;
166 | int32 highest_thvm_breadcrumb_seen = 6;
167 | repeated string inventory_slot_unlocks_seen = 7;
168 | int32 saved_spin_offset = 8;
169 | }
170 | message PlanetCycleInfo {
171 | string planet_name = 1;
172 | float cycle_length = 2;
173 | float last_cached_time = 3;
174 | }
175 | message TimeOfDaySaveGameData {
176 | repeated PlanetCycleInfo planet_cycle_info = 1;
177 | string planet_cycle = 2;
178 | }
179 | message LevelPersistence_Actor_SaveGameData {
180 | string actor_name = 1;
181 | int32 timer_remaining = 2;
182 | }
183 | message LevelPersistence_Level_SaveGameData {
184 | string level_name = 1;
185 | repeated LevelPersistence_Actor_SaveGameData saved_actors = 2;
186 | }
187 | message GbxZoneMapFODSavedLevelData {
188 | string level_name = 1;
189 | uint32 fod_texture_size = 2;
190 | uint32 num_chunks = 3;
191 | float discovery_percentage = 4;
192 | uint32 data_state = 5;
193 | uint32 data_revision = 6;
194 | bytes fod_data = 7;
195 | }
196 | message GbxZoneMapFODSaveGameData {
197 | repeated GbxZoneMapFODSavedLevelData level_data = 1;
198 | }
199 | message HeroPointsSaveData {
200 | uint32 strength = 1;
201 | uint32 dexterity = 2;
202 | uint32 intelligence = 3;
203 | uint32 wisdom = 4;
204 | uint32 constitution = 5;
205 | uint32 luck = 6;
206 | string player_aspect_data_path = 7;
207 | uint32 respec_count = 8;
208 | }
209 | message GuidSaveData {
210 | uint32 A = 1;
211 | uint32 B = 2;
212 | uint32 C = 3;
213 | uint32 D = 4;
214 | }
215 | message TrackedInteractionSaveData {
216 | string tracked_interaction_data = 1;
217 | repeated GuidSaveData completed_instances = 2;
218 | }
219 | message PlayerVoiceSaveData {
220 | string data = 1;
221 | float pitch = 2;
222 | }
223 | message OakProfileCloudData_PlayerPrestige {
224 | bool has_seen_tutorial = 1;
225 | int64 prestige_experience = 2;
226 | repeated int32 points_spent_by_index_order = 3;
227 | }
228 | message OakProfileCloudData {
229 | repeated GameStatSaveGameData profile_stats_data = 1;
230 | repeated bytes lost_loot_inventory_list = 3;
231 | repeated OakMailItem npc_mail_items = 4;
232 | repeated OakSDUSaveGameData profile_sdu_list = 5;
233 | repeated OakCustomizationSaveGameData unlocked_customizations = 6;
234 | repeated OakInventoryCustomizationPartInfo unlocked_inventory_customization_parts = 7;
235 | repeated ChallengeSaveGameData challenge_data = 11;
236 | repeated string mail_guids = 12;
237 | repeated OakInventoryItemSaveGameData bank_inventory_list = 13;
238 | uint32 difficulty = 14;
239 | OakProfileCloudData_PlayerPrestige player_prestige = 15;
240 | }
241 | message DafTownHubTravelData {
242 | bool is_set = 1;
243 | bool is_active = 2;
244 | Vec3 return_travel_location = 3;
245 | Vec3 return_travel_rotation = 4;
246 | string return_map_name = 5;
247 | string return_station_name = 6;
248 | string return_travel_station = 7;
249 | }
250 | message Character {
251 | message NicknameMappingsEntry {
252 | string key = 1;
253 | string value = 2;
254 | }
255 | uint32 save_game_id = 1;
256 | int64 last_save_timestamp = 2;
257 | uint32 time_played_seconds = 3;
258 | PlayerClassSaveGameData player_class_data = 4;
259 | repeated ResourcePoolSavegameData resource_pools = 5;
260 | repeated RegionSaveGameData saved_regions = 6;
261 | int32 experience_points = 7;
262 | repeated GameStatSaveGameData game_stats_data = 8;
263 | repeated InventoryCategorySaveData inventory_category_list = 9;
264 | repeated OakInventoryItemSaveGameData inventory_items = 10;
265 | repeated EquippedInventorySaveGameData equipped_inventory_list = 11;
266 | repeated int32 active_weapon_list = 12;
267 | OakPlayerAbilitySaveGameData ability_data = 13;
268 | int32 last_play_through_index = 14;
269 | int32 playthroughs_completed = 15;
270 | bool show_new_playthrough_notification = 16;
271 | repeated MissionPlaythroughSaveGameData mission_playthroughs_data = 17;
272 | repeated string last_active_travel_station_for_playthrough = 18;
273 | repeated GameStateSaveData game_state_save_data_for_playthrough = 19;
274 | repeated RegisteredDownloadableEntitlements registered_downloadable_entitlements = 20;
275 | repeated PlaythroughActiveFastTravelSaveData active_travel_stations_for_playthrough = 21;
276 | DiscoverySaveData discovery_data = 22;
277 | string save_game_guid = 23;
278 | repeated VehicleUnlockedSaveGameData vehicles_unlocked_data = 24;
279 | repeated string vehicle_parts_unlocked = 25;
280 | repeated OakCARMenuVehicleConfigSaveData vehicle_loadouts = 26;
281 | int32 vehicle_last_loadout_index = 27;
282 | repeated ChallengeSaveGameData challenge_data = 28;
283 | repeated OakSDUSaveGameData sdu_list = 29;
284 | repeated string selected_customizations = 30;
285 | repeated int32 equipped_emote_customizations = 31;
286 | repeated CustomPlayerColorSaveGameData selected_color_customizations = 32;
287 | GbxZoneMapFODSaveGameData gbx_zone_map_fod_save_game_data = 33;
288 | repeated EchoLogSaveGameData unlocked_echo_logs = 36;
289 | repeated NicknameMappingsEntry nickname_mappings = 38;
290 | uint32 accumulated_level_persistence_reset_timer_seconds = 39;
291 | ChallengeCategoryProgressSaveData challenge_category_completion_pcts = 40;
292 | OakPlayerCharacterSlotSaveGameData character_slot_save_game_data = 41;
293 | UITrackingSaveGameData ui_tracking_save_game_data = 42;
294 | string preferred_character_name = 43;
295 | int32 name_character_limit = 44;
296 | uint32 preferred_group_mode = 45;
297 | TimeOfDaySaveGameData time_of_day_save_game_data = 46;
298 | repeated LevelPersistence_Level_SaveGameData level_persistence_data = 47;
299 | repeated CustomFloatCustomizationSaveGameData custom_float_customizations = 49;
300 | HeroPointsSaveData hero_points_save_data = 50;
301 | repeated TrackedInteractionSaveData tracked_interactions = 51;
302 | string player_pronoun_selection = 52;
303 | PlayerVoiceSaveData player_voice = 53;
304 | repeated string last_overworld_travel_station_for_playthrough = 54;
305 | bool disable_customization_suppression = 55;
306 | repeated OakProfileCustomizationLinkData customization_link_data = 56;
307 | OakProfileCloudData profile_cloud_data = 64;
308 | DafTownHubTravelData townhub_travel_info = 65;
309 | uint32 difficulty = 66;
310 | }
311 |
--------------------------------------------------------------------------------
/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 OakProfileCustomizationLinkData {
69 | string customization_name = 1;
70 | bool active = 2;
71 | }
72 | message InventoryBalanceStateInitializationData {
73 | int32 game_stage = 1;
74 | string inventory_data = 2;
75 | string inventory_balance_data = 3;
76 | string manufacturer_data = 4;
77 | repeated string part_list = 5;
78 | repeated string generic_part_list = 6;
79 | bytes additional_data = 7;
80 | repeated string customization_part_list = 8;
81 | }
82 | message OakInventoryItemSaveGameData {
83 | bytes item_serial_number = 1;
84 | int32 pickup_order_index = 2;
85 | int32 flags = 3;
86 | InventoryBalanceStateInitializationData development_save_data = 5;
87 | }
88 |
--------------------------------------------------------------------------------
/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 | ///
11 | /// A simple underlying class that's used to store both and
12 | ///
13 | public class UE3Save
14 | {
15 | ///
16 | /// The file path associated with this path; does not need to be set
17 | ///
18 | public string filePath { get; set; } = null;
19 |
20 | public GVASSave GVASData { get; set; }
21 | }
22 |
23 |
24 | ///
25 | /// A class that represents a Borderlands 3 save.
26 | /// 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.
27 | ///
28 | public class BL3Save : UE3Save
29 | {
30 | public BL3Save(GVASSave saveData, Character character)
31 | {
32 | GVASData = saveData;
33 | Character = character;
34 |
35 | InventoryItems = Character.InventoryItems.Select(x => WonderlandsSerial.DecryptSerial(x.ItemSerialNumber)).ToList();
36 |
37 | for (int i = 0; i < InventoryItems.Count; i++)
38 | {
39 | InventoryItems[i].OriginalData = Character.InventoryItems[i];
40 | }
41 | }
42 |
43 | // Unlike the profiles, we can't just remove all of the data from the save's inventory and then readd it
44 | // Saves store other data in the items as well so we can't do that
45 |
46 | ///
47 | /// Deletes the given item from the save
48 | ///
49 | /// A object representing the item to delete
50 | public void DeleteItem(WonderlandsSerial serialToDelete)
51 | {
52 |
53 | InventoryItems.Remove(serialToDelete);
54 | if (serialToDelete.OriginalData != null)
55 | {
56 | Character.InventoryItems.RemoveAll(x => ReferenceEquals(x, serialToDelete.OriginalData));
57 | }
58 | }
59 |
60 | ///
61 | /// Adds the given item to the save
62 | ///
63 | /// A object representing the item to add
64 | public void AddItem(WonderlandsSerial serialToAdd)
65 | {
66 | InventoryItems.Add(serialToAdd);
67 | var oakItem = new OakInventoryItemSaveGameData()
68 | {
69 | DevelopmentSaveData = null,
70 | Flags = 0x01, // "NEW" Flag
71 | PickupOrderIndex = 8008,
72 | ItemSerialNumber = serialToAdd.EncryptSerialToBytes()
73 | };
74 | // Properly add in the item onto the save
75 | Character.InventoryItems.Add(oakItem);
76 | serialToAdd.OriginalData = oakItem;
77 | }
78 |
79 | ///
80 | /// The underlying protobuf data representing this save
81 | ///
82 | public Character Character { get; set; } = null;
83 |
84 | ///
85 | /// The respective platform for the given save
86 | /// Used for encryption/decryption of the save files
87 | ///
88 | public Platform Platform { get; set; } = Platform.PC;
89 |
90 | ///
91 | /// A list containing all of the inventory items of this file
92 | /// If you want to add items to the save, please use
93 | /// If you want to delete items from the save, please use
94 | ///
95 | public List InventoryItems { get; set; } = null;
96 |
97 | public static Dictionary ValidClasses = new Dictionary() {
98 | { "Hero", new PlayerClassSaveGameData() {
99 | DlcPackageId = 0,
100 | PlayerClassPath="/Game/PlayerCharacters/PlayerClassId_Player.PlayerClassId_Player" } }
101 | };
102 |
103 | public static Dictionary ValidAbilityBranches = new Dictionary() {
104 | { "None", "" },
105 | /// { "Blightcaller", "/Game/PlayerCharacters/Shaman/_Shared/_Design/SkillTree/AbilityTree_Branch_Shaman.AbilityTree_Branch_Shaman" },
106 | { "Blightcaller", "/Game/PatchDLC/Indigo4/PlayerCharacters/Shaman/_Shared/_Design/SkillTree/AbilityTree_Branch_Shaman.AbilityTree_Branch_Shaman" },
107 | { "Brr-zerker", "/Game/PlayerCharacters/Barbarian/_Shared/_Design/SkillTree/AbilityTree_Branch_Barbarian.AbilityTree_Branch_Barbarian" },
108 | { "Spellshot", "/Game/PlayerCharacters/GunMage/_Shared/_Design/SkillTree/AbilityTree_Branch_GunMage.AbilityTree_Branch_GunMage" },
109 | { "Clawbringer", "/Game/PlayerCharacters/KnightOfTheClaw/_Shared/_Design/SkillTree/AbilityTree_Branch_DragonCleric.AbilityTree_Branch_DragonCleric" },
110 | { "Graveborn", "/Game/PlayerCharacters/Necromancer/_Shared/_Design/SkillTree/AbilityTree_Branch_Necromancer.AbilityTree_Branch_Necromancer" },
111 | { "Spore Warden", "/Game/PlayerCharacters/Ranger/_Shared/_Design/SkillTree/AbilityTree_Branch_Ranger.AbilityTree_Branch_Ranger" },
112 | { "Stabbomancer", "/Game/PlayerCharacters/Rogue/_Shared/_Design/SkillTree/AbilityTree_Branch_Rogue.AbilityTree_Branch_Rogue" }
113 | };
114 |
115 | public static Dictionary CharacterToClassPair = new Dictionary() {
116 | { "FL4K", "Beastmaster" },
117 | { "Moze", "Gunner" },
118 | { "Zane", "Operative" },
119 | { "Amara", "Siren" }
120 | };
121 |
122 | public static Dictionary ValidPlayerAspect = new Dictionary() {
123 | { "None", "" },
124 | { "Village Idiot", "/Game/PlayerCharacters/_Shared/_Design/Aspects/Aspect_01.Aspect_01" },
125 | { "Raised By Elves", "/Game/PlayerCharacters/_Shared/_Design/Aspects/Aspect_02.Aspect_02" },
126 | { "Failed Monk", "/Game/PlayerCharacters/_Shared/_Design/Aspects/Aspect_03.Aspect_03" },
127 | { "Recovering Inventory Hoarder", "/Game/PlayerCharacters/_Shared/_Design/Aspects/Aspect_04.Aspect_04" },
128 | { "Rogue Alchemist", "/Game/PlayerCharacters/_Shared/_Design/Aspects/Aspect_05.Aspect_05" },
129 | { "Nerfed By The Bunker Master", "/Game/PatchDLC/Indigo4/PlayerCharacters/_Shared/_Design/Aspects/Aspect_PLC_1.Aspect_PLC_1" },
130 | { "Clownblood", "/Game/PatchDLC/Indigo4/PlayerCharacters/_Shared/_Design/Aspects/Aspect_PLC_2.Aspect_PLC_2" },
131 | { "Apprentice Barnacle Scraper", "/Game/PatchDLC/Indigo4/PlayerCharacters/_Shared/_Design/Aspects/Aspect_PLC_3.Aspect_PLC_3" },
132 | { "Street Urchin Success Story", "/Game/PatchDLC/Indigo4/PlayerCharacters/_Shared/_Design/Aspects/Aspect_PLC_4.Aspect_PLC_4" }
133 | };
134 |
135 | public static Dictionary ValidPlayerPronouns = new Dictionary() {
136 | { "Neutral", "/Game/PlayerCharacters/_Shared/_Design/PlayerPronouns/PlayerPronouns_Neutral.PlayerPronouns_Neutral" },
137 | { "Masculine", "/Game/PlayerCharacters/_Shared/_Design/PlayerPronouns/PlayerPronouns_Masculine.PlayerPronouns_Masculine" },
138 | { "Feminine", "/Game/PlayerCharacters/_Shared/_Design/PlayerPronouns/PlayerPronouns_Feminine.PlayerPronouns_Feminine" },
139 | };
140 | }
141 |
142 | ///
143 | /// A simple class that represents the Borderlands 3 profile structure.
144 | ///
145 | public class BL3Profile : UE3Save
146 | {
147 | public BL3Profile(GVASSave gvasSave, Profile profile)
148 | {
149 | this.GVASData = gvasSave;
150 | Profile = profile;
151 |
152 | BankItems = Profile.BankInventoryLists.Select(x => WonderlandsSerial.DecryptSerial(x.ItemSerialNumber)).ToList();
153 | LostLootItems = Profile.LostLootInventoryLists.Select(x => WonderlandsSerial.DecryptSerial(x)).ToList();
154 |
155 | for (int i = 0; i < BankItems.Count - 1; i++)
156 | {
157 | WonderlandsSerial item = BankItems[i];
158 | item.OriginalData = new OakInventoryItemSaveGameData()
159 | {
160 | DevelopmentSaveData = null,
161 | Flags = 0x00,
162 | ItemSerialNumber = Profile.BankInventoryLists[i].ItemSerialNumber,
163 | PickupOrderIndex = -1
164 | };
165 | }
166 |
167 | for (int i = 0; i < LostLootItems.Count - 1; i++)
168 | {
169 | WonderlandsSerial item = LostLootItems[i];
170 | item.OriginalData = new OakInventoryItemSaveGameData()
171 | {
172 | DevelopmentSaveData = null,
173 | Flags = 0x00,
174 | ItemSerialNumber = Profile.LostLootInventoryLists[i],
175 | PickupOrderIndex = -1
176 | };
177 | }
178 | }
179 |
180 | ///
181 | /// The underlying protobuf data representing this profile
182 | ///
183 | public Profile Profile { get; set; }
184 |
185 |
186 | ///
187 | /// A list representing all of the items stored in the current profile's bank.
188 | ///
189 | public List BankItems { get; set; } = null;
190 |
191 | ///
192 | /// A list representing all of the items stored in the current profile's lost loot.
193 | ///
194 | public List LostLootItems { get; set; } = null;
195 |
196 |
197 | ///
198 | /// The respective platform for the profile
199 | /// Used for encryption/decryption
200 | ///
201 | public Platform Platform { get; set; } = Platform.PC;
202 | }
203 |
204 | }
205 |
--------------------------------------------------------------------------------
/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/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | False
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/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 | ## Tiny Tina's Wonderlands Save Editor
2 |
3 | A desktop based save _and_ profile editor for [Tiny Tina's Wonderlands](https://playwonderlands.2k.com/).
4 |
5 |  
6 |
7 | ### Installation / Usage
8 |
9 | 1. Download the [7zip](https://7-zip.org) file on the [Releases](https://github.com/somefunguy/TTWLSaveEditor/releases) tab.
10 | 2. Extract the [7zip](https://7-zip.org) file to a folder of your choosing.
11 | 3. Run TTWSaveEditor.exe
12 | 4. Click `Open`, then you can select either a profile (`profile.sav`) or a game save (`[NUMBERS].sav`).
13 | 5. You can now edit your profile or game save to your hearts content!
14 | 6. 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 | - [FromDarkHell](https://github.com/fromdarkhell/) for creating the original BL3 editor, which this project has been entirely built upon and would not be possible without.
19 | - Arwent for transforming the FDH BL3 editor to work with Wonderlands.
20 | - [GreenLamp](https://github.com/Greenlamp2/) for continuing and adding many things to Arwent's work.
21 | - [gibbed](https://github.com/Gibbed) for their [Inventory Serial Number Database](https://github.com/gibbed/WonderlandsDumps). It's what allows me to know what weapons you've got in your backpack! I also use their [WonderlandsProtos](https://github.com/gibbed/WonderlandsProtos) repository
22 | - [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.
23 | - [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.
24 | - [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.
25 | - [Gearbox Software](https://www.gearboxsoftware.com/) of course for creating Wonderlands.
26 | - Many others who helped test the editor as well as help me get my mind around some of the concepts.
27 |
28 | ### Support
29 |
30 | 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).
31 |
32 | [](https://ko-fi.com/O4O44GLCD) [](https://patreon.com/fromdarkhell)
33 |
--------------------------------------------------------------------------------