├── .gitattributes
├── .gitignore
├── .nuget
└── NuGet.Config
├── BinderTool.Core
├── Bdf4
│ └── Bdf4FileStream.cs
├── Bdt5
│ └── Bdt5FileStream.cs
├── Bhd5
│ ├── Bhd5AesKey.cs
│ ├── Bhd5Bucket.cs
│ ├── Bhd5BucketEntry.cs
│ ├── Bhd5File.cs
│ ├── Bhd5FileReadException.cs
│ ├── Bhd5Range.cs
│ └── Bhd5SaltedShaHash.cs
├── Bhf4
│ ├── Bhf4Entry.cs
│ └── Bhf4File.cs
├── BinderTool.Core.csproj
├── Bnd4
│ ├── Bnd4File.cs
│ └── Bnd4FileEntry.cs
├── CryptographyUtility.cs
├── Dcx
│ ├── DcxCompression.cs
│ ├── DcxFile.cs
│ └── DeflateCompression.cs
├── Dds
│ ├── DdsFile.cs
│ ├── DdsFileHeader.cs
│ ├── DdsFileHeaderDx10.cs
│ ├── DdsPixelFormat.cs
│ └── Enum
│ │ ├── D3D10ResourceDimension.cs
│ │ ├── DdsCaps2Flags.cs
│ │ ├── DdsFileHeaderFlags.cs
│ │ ├── DdsPixelFormatFlag.cs
│ │ ├── DdsSurfaceFlags.cs
│ │ └── DxgiFormat.cs
├── Enc
│ └── EncFile.cs
├── Enfl
│ └── EntryFileListFile.cs
├── ExtensionMethods.cs
├── Fmg
│ ├── FmgFile.cs
│ ├── FmgFileEntry.cs
│ └── FmgIdRange.cs
├── GameVersion.cs
├── IO
│ └── BigEndianBinaryReader.cs
├── Param
│ ├── ParamEntry.cs
│ └── ParamFile.cs
├── Properties
│ └── AssemblyInfo.cs
├── Sl2
│ ├── Sl2File.cs
│ └── Sl2UserData.cs
├── Tpf
│ ├── TpfFile.cs
│ └── TpfFileEntry.cs
└── packages.config
├── BinderTool.sln
├── BinderTool
├── App.config
├── BinderTool.csproj
├── DecryptionKeys.cs
├── DictionaryDS2.csv
├── DictionaryDS3.csv
├── FileNameDictionary.cs
├── FileType.cs
├── Options.cs
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
└── packages.config
├── LICENSE
└── README.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.sln.docstates
8 |
9 | .vs/
10 |
11 | # Build results
12 | [Dd]ebug/
13 | [Dd]ebugPublic/
14 | [Rr]elease/
15 | [Rr]eleases/
16 | x64/
17 | x86/
18 | build/
19 | bld/
20 | [Bb]in/
21 | [Oo]bj/
22 |
23 | # Roslyn cache directories
24 | *.ide/
25 |
26 | # MSTest test Results
27 | [Tt]est[Rr]esult*/
28 | [Bb]uild[Ll]og.*
29 |
30 | #NUNIT
31 | *.VisualState.xml
32 | TestResult.xml
33 |
34 | # Build Results of an ATL Project
35 | [Dd]ebugPS/
36 | [Rr]eleasePS/
37 | dlldata.c
38 |
39 | *_i.c
40 | *_p.c
41 | *_i.h
42 | *.ilk
43 | *.meta
44 | *.obj
45 | *.pch
46 | *.pdb
47 | *.pgc
48 | *.pgd
49 | *.rsp
50 | *.sbr
51 | *.tlb
52 | *.tli
53 | *.tlh
54 | *.tmp
55 | *.tmp_proj
56 | *.log
57 | *.vspscc
58 | *.vssscc
59 | .builds
60 | *.pidb
61 | *.svclog
62 | *.scc
63 |
64 | # Chutzpah Test files
65 | _Chutzpah*
66 |
67 | # Visual C++ cache files
68 | ipch/
69 | *.aps
70 | *.ncb
71 | *.opensdf
72 | *.sdf
73 | *.cachefile
74 |
75 | # Visual Studio profiler
76 | *.psess
77 | *.vsp
78 | *.vspx
79 |
80 | # TFS 2012 Local Workspace
81 | $tf/
82 |
83 | # Guidance Automation Toolkit
84 | *.gpState
85 |
86 | # ReSharper is a .NET coding add-in
87 | _ReSharper*/
88 | *.[Rr]e[Ss]harper
89 | *.DotSettings.user
90 |
91 | # JustCode is a .NET coding addin-in
92 | .JustCode
93 |
94 | # TeamCity is a build add-in
95 | _TeamCity*
96 |
97 | # DotCover is a Code Coverage Tool
98 | *.dotCover
99 |
100 | # NCrunch
101 | _NCrunch_*
102 | .*crunch*.local.xml
103 |
104 | # MightyMoose
105 | *.mm.*
106 | AutoTest.Net/
107 |
108 | # Web workbench (sass)
109 | .sass-cache/
110 |
111 | # Installshield output folder
112 | [Ee]xpress/
113 |
114 | # DocProject is a documentation generator add-in
115 | DocProject/buildhelp/
116 | DocProject/Help/*.HxT
117 | DocProject/Help/*.HxC
118 | DocProject/Help/*.hhc
119 | DocProject/Help/*.hhk
120 | DocProject/Help/*.hhp
121 | DocProject/Help/Html2
122 | DocProject/Help/html
123 |
124 | # Click-Once directory
125 | publish/
126 |
127 | # Publish Web Output
128 | *.[Pp]ublish.xml
129 | *.azurePubxml
130 | # TODO: Comment the next line if you want to checkin your web deploy settings
131 | # but database connection strings (with potential passwords) will be unencrypted
132 | *.pubxml
133 | *.publishproj
134 |
135 | # NuGet Packages
136 | *.nupkg
137 | # The packages folder can be ignored because of Package Restore
138 | **/packages/*
139 | # except build/, which is used as an MSBuild target.
140 | !**/packages/build/
141 | # If using the old MSBuild-Integrated Package Restore, uncomment this:
142 | #!**/packages/repositories.config
143 |
144 | # Windows Azure Build Output
145 | csx/
146 | *.build.csdef
147 |
148 | # Windows Store app package directory
149 | AppPackages/
150 |
151 | # Others
152 | sql/
153 | *.Cache
154 | ClientBin/
155 | [Ss]tyle[Cc]op.*
156 | ~$*
157 | *~
158 | *.dbmdl
159 | *.dbproj.schemaview
160 | *.pfx
161 | *.publishsettings
162 | node_modules/
163 |
164 | # RIA/Silverlight projects
165 | Generated_Code/
166 |
167 | # Backup & report files from converting an old project file
168 | # to a newer Visual Studio version. Backup files are not needed,
169 | # because we have git ;-)
170 | _UpgradeReport_Files/
171 | Backup*/
172 | UpgradeLog*.XML
173 | UpgradeLog*.htm
174 |
175 | # SQL Server files
176 | *.mdf
177 | *.ldf
178 |
179 | # Business Intelligence projects
180 | *.rdl.data
181 | *.bim.layout
182 | *.bim_*.settings
183 |
184 | # Microsoft Fakes
185 | FakesAssemblies/
186 |
--------------------------------------------------------------------------------
/.nuget/NuGet.Config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/BinderTool.Core/Bdf4/Bdf4FileStream.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text;
4 |
5 | namespace BinderTool.Core.Bdf4
6 | {
7 | public class Bdf4FileStream : IDisposable
8 | {
9 | private readonly Stream _inputStream;
10 | public string Version { get; set; }
11 |
12 | public Bdf4FileStream(Stream inputStream)
13 | {
14 | _inputStream = inputStream;
15 | }
16 |
17 | public static Bdf4FileStream OpenFile(string inputPath, FileMode mode, FileAccess access)
18 | {
19 | FileStream inputStream = new FileStream(inputPath, mode, access);
20 | Bdf4FileStream bdfStream = new Bdf4FileStream(inputStream);
21 | bdfStream.ReadHeader();
22 | return bdfStream;
23 | }
24 |
25 | private void ReadHeader()
26 | {
27 | BinaryReader reader = new BinaryReader(_inputStream, Encoding.ASCII, true);
28 | string signature = reader.ReadString(4); // BDF4
29 | int unknown1 = reader.ReadInt32(); // Always 00 00 00 00?
30 | int unknown2 = reader.ReadInt32(); // Always 00 00 01 00?
31 | int unknown3 = reader.ReadInt32();
32 | int unknown4 = reader.ReadInt32(); // Always 48?
33 | int unknown5 = reader.ReadInt32();
34 | Version = reader.ReadString(8);
35 | }
36 |
37 | public MemoryStream Read(long fileOffset, long fileSize)
38 | {
39 | if (fileOffset + fileSize > _inputStream.Length)
40 | throw new EndOfStreamException();
41 | _inputStream.Seek(fileOffset, SeekOrigin.Begin);
42 |
43 | byte[] buffer = new byte[fileSize];
44 | _inputStream.Read(buffer, 0, (int)fileSize);
45 | return new MemoryStream(buffer);
46 | }
47 |
48 | public void Dispose()
49 | {
50 | _inputStream.Dispose();
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/BinderTool.Core/Bdt5/Bdt5FileStream.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace BinderTool.Core.Bdt5
5 | {
6 | public class Bdt5FileStream : IDisposable
7 | {
8 | private readonly Stream _inputStream;
9 |
10 | public Bdt5FileStream(Stream inputStream)
11 | {
12 | _inputStream = inputStream;
13 | }
14 |
15 | public MemoryStream Read(long fileOffset, long fileSize)
16 | {
17 | if (fileOffset + fileSize > _inputStream.Length)
18 | throw new EndOfStreamException();
19 | _inputStream.Seek(fileOffset, SeekOrigin.Begin);
20 |
21 | byte[] buffer = new byte[fileSize];
22 | _inputStream.Read(buffer, 0, (int) fileSize);
23 | return new MemoryStream(buffer);
24 | }
25 |
26 | public static Bdt5FileStream OpenFile(string path, FileMode mode, FileAccess access)
27 | {
28 | FileStream bdtStream = new FileStream(path, mode, access);
29 | return new Bdt5FileStream(bdtStream);
30 | }
31 |
32 | public void Dispose()
33 | {
34 | _inputStream.Dispose();
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/BinderTool.Core/Bhd5/Bhd5AesKey.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace BinderTool.Core.Bhd5
4 | {
5 | public class Bhd5AesKey
6 | {
7 | public byte[] Key { get; private set; }
8 | public Bhd5Range[] Ranges { get; private set; }
9 |
10 | public static Bhd5AesKey Read(BinaryReader reader)
11 | {
12 | Bhd5AesKey result = new Bhd5AesKey();
13 |
14 | result.Key = reader.ReadBytes(16);
15 | int rangeCount = reader.ReadInt32();
16 | Bhd5Range[] ranges = new Bhd5Range[rangeCount];
17 | for (int i = 0; i < rangeCount; i++)
18 | {
19 | ranges[i] = Bhd5Range.Read(reader);
20 | }
21 | result.Ranges = ranges;
22 |
23 | return result;
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/BinderTool.Core/Bhd5/Bhd5Bucket.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using System.Linq;
4 |
5 | namespace BinderTool.Core.Bhd5
6 | {
7 | public class Bhd5Bucket
8 | {
9 | private readonly List _entries;
10 |
11 | public Bhd5Bucket()
12 | {
13 | _entries = new List();
14 | }
15 |
16 | public IEnumerable GetEntries()
17 | {
18 | return _entries.AsEnumerable();
19 | }
20 |
21 | public static Bhd5Bucket Read(BinaryReader reader, GameVersion version)
22 | {
23 | Bhd5Bucket result = new Bhd5Bucket();
24 |
25 | int bucketEntryCount = reader.ReadInt32();
26 | int bucketOffset = reader.ReadInt32();
27 |
28 | long currentPosition = reader.GetPosition();
29 | reader.Seek(bucketOffset, SeekOrigin.Begin);
30 |
31 | for (int i = 0; i < bucketEntryCount; i++)
32 | {
33 | result._entries.Add(Bhd5BucketEntry.Read(reader, version));
34 | }
35 |
36 | reader.Seek(currentPosition, SeekOrigin.Begin);
37 | return result;
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/BinderTool.Core/Bhd5/Bhd5BucketEntry.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace BinderTool.Core.Bhd5
4 | {
5 | public class Bhd5BucketEntry
6 | {
7 | public uint FileNameHash { get; private set; }
8 | public long FileSize { get; set; }
9 | public long PaddedFileSize { get; set; }
10 | public long FileOffset { get; private set; }
11 | public Bhd5AesKey AesKey { get; private set; }
12 | public Bhd5SaltedShaHash ShaHash { get; private set; }
13 | public bool IsEncrypted => AesKey != null;
14 |
15 | public static Bhd5BucketEntry Read(BinaryReader reader, GameVersion version)
16 | {
17 | Bhd5BucketEntry result = new Bhd5BucketEntry();
18 | result.FileNameHash = reader.ReadUInt32();
19 | result.PaddedFileSize = reader.ReadUInt32();
20 | result.FileOffset = reader.ReadInt64();
21 | long saltedHashOffset = reader.ReadInt64();
22 | long aesKeyOffset = reader.ReadInt64();
23 |
24 | switch (version)
25 | {
26 | case GameVersion.DarkSouls3:
27 | result.FileSize = reader.ReadInt64();
28 | break;
29 | default:
30 | result.FileSize = result.PaddedFileSize;
31 | break;
32 | }
33 |
34 | if (saltedHashOffset != 0)
35 | {
36 | long currentPosition = reader.GetPosition();
37 | reader.Seek(saltedHashOffset, SeekOrigin.Begin);
38 | result.ShaHash = Bhd5SaltedShaHash.Read(reader);
39 | reader.Seek(currentPosition, SeekOrigin.Begin);
40 | }
41 |
42 | if (aesKeyOffset != 0)
43 | {
44 | long currentPosition = reader.GetPosition();
45 | reader.Seek(aesKeyOffset, SeekOrigin.Begin);
46 | result.AesKey = Bhd5AesKey.Read(reader);
47 | reader.Seek(currentPosition, SeekOrigin.Begin);
48 | }
49 |
50 | return result;
51 | }
52 |
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/BinderTool.Core/Bhd5/Bhd5File.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace BinderTool.Core.Bhd5
7 | {
8 | public class Bhd5File
9 | {
10 | private const string Bhd5Signature = "BHD5";
11 | private const byte Bhd5UnknownConstant1 = 255;
12 | private const int Bh5UnknownConstant2 = 1;
13 | private readonly List _buckets = new List();
14 |
15 | public IEnumerable GetBuckets()
16 | {
17 | return _buckets.AsEnumerable();
18 | }
19 |
20 | public static Bhd5File Read(Stream inputStream, GameVersion version)
21 | {
22 | Bhd5File result = new Bhd5File();
23 |
24 | BinaryReader reader = new BinaryReader(inputStream, Encoding.ASCII, true);
25 |
26 | string signature = new string(reader.ReadChars(4));
27 | if (signature != Bhd5Signature)
28 | throw new Bhd5FileReadException("Invalid signature");
29 | int bhdVersion = reader.ReadInt32(); // 511
30 | int unknown = reader.ReadInt32(); // 1
31 | int size = reader.ReadInt32(); // excluding sizeof(signature)
32 | int bucketDirectoryEntryCount = reader.ReadInt32();
33 | int bucketDirectoryOffset = reader.ReadInt32();
34 | int saltLength = reader.ReadInt32();
35 | string salt = new string(reader.ReadChars(saltLength));
36 |
37 | for (int i = 0; i < bucketDirectoryEntryCount; i++)
38 | {
39 | result._buckets.Add(Bhd5Bucket.Read(reader, version));
40 | }
41 |
42 | return result;
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/BinderTool.Core/Bhd5/Bhd5FileReadException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace BinderTool.Core.Bhd5
4 | {
5 | internal class Bhd5FileReadException : Exception
6 | {
7 | public Bhd5FileReadException(string message) : base(message)
8 | {
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/BinderTool.Core/Bhd5/Bhd5Range.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace BinderTool.Core.Bhd5
4 | {
5 | public class Bhd5Range
6 | {
7 | public long StartOffset { get; set; }
8 | public long EndOffset { get; set; }
9 |
10 | public static Bhd5Range Read(BinaryReader reader)
11 | {
12 | Bhd5Range result = new Bhd5Range();
13 | result.StartOffset = reader.ReadInt64();
14 | result.EndOffset = reader.ReadInt64();
15 | return result;
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/BinderTool.Core/Bhd5/Bhd5SaltedShaHash.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace BinderTool.Core.Bhd5
4 | {
5 | public class Bhd5SaltedShaHash
6 | {
7 | public byte[] Hash { get; set; }
8 | public Bhd5Range[] Ranges { get; private set; }
9 |
10 | public static Bhd5SaltedShaHash Read(BinaryReader reader)
11 | {
12 | Bhd5SaltedShaHash result = new Bhd5SaltedShaHash();
13 |
14 | result.Hash = reader.ReadBytes(32);
15 | int rangeCount = reader.ReadInt32();
16 | Bhd5Range[] ranges = new Bhd5Range[rangeCount];
17 | for (int i = 0; i < rangeCount; i++)
18 | {
19 | ranges[i] = Bhd5Range.Read(reader);
20 | }
21 | result.Ranges = ranges;
22 |
23 | return result;
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/BinderTool.Core/Bhf4/Bhf4Entry.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text;
3 |
4 | namespace BinderTool.Core.Bhf4
5 | {
6 | public class Bhf4Entry
7 | {
8 | public string FileName { get; set; }
9 | public int FileNameOffset { get; set; }
10 | public int FileOffset { get; set; }
11 | public int FileSize { get; set; }
12 |
13 | public static Bhf4Entry ReadBhf4Entry(Stream inputStream)
14 | {
15 | Bhf4Entry bhf4Entry = new Bhf4Entry();
16 | bhf4Entry.Read(inputStream);
17 | return bhf4Entry;
18 | }
19 |
20 | public void Read(Stream inputStream)
21 | {
22 | BinaryReader reader = new BinaryReader(inputStream, Encoding.ASCII, true);
23 | int unknown1 = reader.ReadInt32(); // Always 64? Endianess? Encoding?
24 | uint unknown2 = reader.ReadUInt32(); // Always -1?
25 | FileSize = reader.ReadInt32();
26 | int unknown4 = reader.ReadInt32();
27 | int fileSize2 = reader.ReadInt32();
28 | int unknown6 = reader.ReadInt32();
29 | FileOffset = reader.ReadInt32();
30 | int unknown8 = reader.ReadInt32();
31 | FileNameOffset = reader.ReadInt32();
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/BinderTool.Core/Bhf4/Bhf4File.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using System.Text;
5 |
6 | namespace BinderTool.Core.Bhf4
7 | {
8 | public class Bhf4File
9 | {
10 | public Bhf4File()
11 | {
12 | Entries = new List();
13 | }
14 |
15 | public List Entries { get; private set; }
16 |
17 | public string Version { get; private set; }
18 |
19 | public static Bhf4File OpenBhf4File(string bhf4FilePath)
20 | {
21 | using (FileStream inputStream = new FileStream(bhf4FilePath, FileMode.Open))
22 | {
23 | Bhf4File bhf4File = new Bhf4File();
24 | bhf4File.Read(inputStream);
25 | return bhf4File;
26 | }
27 | }
28 |
29 | public void Read(Stream inputStream)
30 | {
31 | BinaryReader reader = new BinaryReader(inputStream, Encoding.ASCII, true);
32 | string signature = reader.ReadString(4); // BHF4
33 | int unknown1 = reader.ReadInt32(); // Always 00 00 00 00?
34 | int unknown2 = reader.ReadInt32(); // Always 00 00 01 00?
35 | int numberFiles = reader.ReadInt32();
36 | int unknown3 = reader.ReadInt32(); // Always 64?
37 | int unknown4 = reader.ReadInt32();
38 | Version = reader.ReadString(8);
39 | int directoryEntrySize = reader.ReadInt32(); // Always 36?
40 | int unknown5 = reader.ReadInt32();
41 | int unknown6 = reader.ReadInt32();
42 | int unknown7 = reader.ReadInt32();
43 |
44 | byte encoding = reader.ReadByte();
45 | byte unknown8 = reader.ReadByte();
46 | byte unknown9 = reader.ReadByte();
47 | byte unknown10 = reader.ReadByte();
48 |
49 | int unknown11 = reader.ReadInt32();
50 | int unknown12 = reader.ReadInt32();
51 | int unknown13 = reader.ReadInt32();
52 |
53 | for (int i = 0; i < numberFiles; i++)
54 | {
55 | Entries.Add(Bhf4Entry.ReadBhf4Entry(inputStream));
56 | }
57 |
58 | long endPosition = inputStream.Position;
59 |
60 | switch (encoding)
61 | {
62 | case 0:
63 | break;
64 | case 1:
65 | reader = new BinaryReader(inputStream, Encoding.Unicode, true);
66 | break;
67 | default:
68 | Debug.WriteLine("Unknown encoding " + encoding);
69 | break;
70 | }
71 |
72 | foreach (var entry in Entries)
73 | {
74 | inputStream.Position = entry.FileNameOffset;
75 | entry.FileName = reader.ReadNullTerminatedString();
76 | }
77 |
78 | inputStream.Position = endPosition;
79 | }
80 |
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/BinderTool.Core/BinderTool.Core.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {D8F8A776-F5B5-46EF-8578-390B988D0D0B}
8 | Library
9 | Properties
10 | BinderTool.Core
11 | BinderTool.Core
12 | v4.5.2
13 | 512
14 | ..\
15 | true
16 |
17 |
18 |
19 | true
20 | full
21 | false
22 | bin\Debug\
23 | DEBUG;TRACE
24 | prompt
25 | 4
26 |
27 |
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 | ..\packages\BouncyCastle.1.8.1\lib\BouncyCastle.Crypto.dll
38 |
39 |
40 | ..\packages\ICSharpCode.SharpZipLib.dll.0.85.4.369\lib\net20\ICSharpCode.SharpZipLib.dll
41 |
42 |
43 |
44 |
45 | ..\packages\System.ValueTuple.4.4.0\lib\netstandard1.0\System.ValueTuple.dll
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
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 |
109 |
--------------------------------------------------------------------------------
/BinderTool.Core/Bnd4/Bnd4File.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Text;
6 |
7 | namespace BinderTool.Core.Bnd4
8 | {
9 | public class Bnd4File
10 | {
11 | private const string Bnd4Signature = "BND4";
12 | private readonly List _entries;
13 |
14 | public Bnd4File()
15 | {
16 | _entries = new List();
17 | }
18 |
19 | public List Entries => _entries;
20 |
21 | public static Bnd4File ReadBnd4File(Stream inputStream)
22 | {
23 | Bnd4File bnd4File = new Bnd4File();
24 | bnd4File.Read(inputStream);
25 | return bnd4File;
26 | }
27 |
28 | private void Read(Stream inputStream)
29 | {
30 | BinaryReader reader = new BinaryReader(inputStream, Encoding.ASCII, true);
31 | string signature = reader.ReadString(4);
32 | if (signature != Bnd4Signature)
33 | throw new Exception("Unknown signature");
34 | reader.Skip(8);
35 | int fileCount = reader.ReadInt32();
36 | reader.Skip(8);
37 | string version = reader.ReadString(8);
38 | int directoryEntrySize = reader.ReadInt32();
39 | reader.Skip(4);
40 | int dataOffset = reader.ReadInt32();
41 | reader.Skip(4);
42 | byte encoding = reader.ReadByte();
43 | reader.Skip(15);
44 |
45 | switch (encoding)
46 | {
47 | case 0:
48 | break;
49 | case 1:
50 | reader = new BinaryReader(inputStream, Encoding.Unicode, true);
51 | break;
52 | default:
53 | Debug.WriteLine("Unknown encoding " + encoding);
54 | break;
55 | }
56 |
57 | // Directory section
58 | for (int i = 0; i < fileCount; i++)
59 | {
60 | int fileEntryOffset;
61 | int fileNameOffset;
62 |
63 | reader.Skip(8);
64 | int fileEntrySize = reader.ReadInt32();
65 | reader.Skip(4);
66 | if (directoryEntrySize == 36)
67 | {
68 | reader.Skip(8);
69 | fileEntryOffset = reader.ReadInt32();
70 | reader.Skip(4);
71 | fileNameOffset = reader.ReadInt32();
72 | }
73 | else
74 | {
75 | fileEntryOffset = reader.ReadInt32();
76 | fileNameOffset = reader.ReadInt32();
77 | }
78 |
79 | long position = reader.GetPosition();
80 | string fileName = "";
81 | if (fileNameOffset > 0)
82 | {
83 | reader.Seek(fileNameOffset);
84 | fileName = reader.ReadNullTerminatedString();
85 | }
86 |
87 | reader.Seek(fileEntryOffset);
88 | _entries.Add(Bnd4FileEntry.Read(inputStream, fileEntrySize, fileName));
89 | reader.Seek(position);
90 | }
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/BinderTool.Core/Bnd4/Bnd4FileEntry.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text;
3 |
4 | namespace BinderTool.Core.Bnd4
5 | {
6 | public class Bnd4FileEntry
7 | {
8 | public byte[] EntryData { get; private set; }
9 | public string FileName { get; private set; }
10 |
11 | public static Bnd4FileEntry Read(Stream inputStream, int fileSize, string fileName)
12 | {
13 | Bnd4FileEntry result = new Bnd4FileEntry();
14 | BinaryReader reader = new BinaryReader(inputStream, Encoding.UTF8, true);
15 | result.FileName = fileName;
16 | result.EntryData = reader.ReadBytes(fileSize);
17 | return result;
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/BinderTool.Core/CryptographyUtility.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using BinderTool.Core.Bhd5;
4 | using Org.BouncyCastle.Crypto;
5 | using Org.BouncyCastle.Crypto.Engines;
6 | using Org.BouncyCastle.Crypto.Modes;
7 | using Org.BouncyCastle.Crypto.Parameters;
8 | using Org.BouncyCastle.OpenSsl;
9 |
10 | namespace BinderTool.Core
11 | {
12 | public static class CryptographyUtility
13 | {
14 | public static MemoryStream DecryptAesEcb(Stream inputStream, byte[] key)
15 | {
16 | var cipher = CreateAesEcbCipher(key);
17 | return DecryptAes(inputStream, cipher, inputStream.Length);
18 | }
19 |
20 | public static MemoryStream DecryptAesCbc(Stream inputStream, byte[] key, byte[] iv)
21 | {
22 | AesEngine engine = new AesEngine();
23 | KeyParameter keyParameter = new KeyParameter(key);
24 | ICipherParameters parameters = new ParametersWithIV(keyParameter, iv);
25 |
26 | BufferedBlockCipher cipher = new BufferedBlockCipher(new CbcBlockCipher(engine));
27 | cipher.Init(false, parameters);
28 | return DecryptAes(inputStream, cipher, inputStream.Length);
29 | }
30 |
31 | public static MemoryStream DecryptAesCtr(Stream inputStream, byte[] key, byte[] iv)
32 | {
33 | AesEngine engine = new AesEngine();
34 | KeyParameter keyParameter = new KeyParameter(key);
35 | ICipherParameters parameters = new ParametersWithIV(keyParameter, iv);
36 |
37 | BufferedBlockCipher cipher = new BufferedBlockCipher(new SicBlockCipher(engine));
38 | cipher.Init(false, parameters);
39 | return DecryptAes(inputStream, cipher, inputStream.Length);
40 | }
41 |
42 | private static BufferedBlockCipher CreateAesEcbCipher(byte[] key)
43 | {
44 | AesEngine engine = new AesEngine();
45 | KeyParameter parameter = new KeyParameter(key);
46 | BufferedBlockCipher cipher = new BufferedBlockCipher(engine);
47 | cipher.Init(false, parameter);
48 | return cipher;
49 | }
50 |
51 | private static MemoryStream DecryptAes(Stream inputStream, BufferedBlockCipher cipher, long length)
52 | {
53 | int blockSize = cipher.GetBlockSize();
54 | int inputLength = (int)length;
55 | int paddedLength = inputLength;
56 | if (paddedLength % blockSize > 0)
57 | {
58 | paddedLength += blockSize - paddedLength % blockSize;
59 | }
60 |
61 | byte[] input = new byte[paddedLength];
62 | byte[] output = new byte[cipher.GetOutputSize(paddedLength)];
63 |
64 | inputStream.Read(input, 0, inputLength);
65 | int len = cipher.ProcessBytes(input, 0, input.Length, output, 0);
66 | cipher.DoFinal(output, len);
67 |
68 | MemoryStream outputStream = new MemoryStream();
69 | outputStream.Write(output, 0, inputLength);
70 | outputStream.Seek(0, SeekOrigin.Begin);
71 | return outputStream;
72 | }
73 |
74 | ///
75 | /// Decrypts a file with a provided decryption key.
76 | ///
77 | /// An encrypted file
78 | /// The RSA key in PEM format
79 | /// When the argument filePath is null
80 | /// When the argument keyPath is null
81 | /// A memory stream with the decrypted file
82 | public static MemoryStream DecryptRsa(string filePath, string key)
83 | {
84 | if (filePath == null)
85 | {
86 | throw new ArgumentNullException(nameof(filePath));
87 | }
88 |
89 | if (key == null)
90 | {
91 | throw new ArgumentNullException(nameof(key));
92 | }
93 |
94 | AsymmetricKeyParameter keyParameter = GetKeyOrDefault(key);
95 | RsaEngine engine = new RsaEngine();
96 | engine.Init(false, keyParameter);
97 |
98 | MemoryStream outputStream = new MemoryStream();
99 | using (FileStream inputStream = File.OpenRead(filePath))
100 | {
101 |
102 | int inputBlockSize = engine.GetInputBlockSize();
103 | int outputBlockSize = engine.GetOutputBlockSize();
104 | byte[] inputBlock = new byte[inputBlockSize];
105 | while (inputStream.Read(inputBlock, 0, inputBlock.Length) > 0)
106 | {
107 | byte[] outputBlock = engine.ProcessBlock(inputBlock, 0, inputBlockSize);
108 |
109 | int requiredPadding = outputBlockSize - outputBlock.Length;
110 | if (requiredPadding > 0)
111 | {
112 | byte[] paddedOutputBlock = new byte[outputBlockSize];
113 | outputBlock.CopyTo(paddedOutputBlock, requiredPadding);
114 | outputBlock = paddedOutputBlock;
115 | }
116 |
117 | outputStream.Write(outputBlock, 0, outputBlock.Length);
118 | }
119 | }
120 |
121 | outputStream.Seek(0, SeekOrigin.Begin);
122 | return outputStream;
123 | }
124 |
125 | public static AsymmetricKeyParameter GetKeyOrDefault(string key)
126 | {
127 | try
128 | {
129 | PemReader pemReader = new PemReader(new StringReader(key));
130 | return (AsymmetricKeyParameter)pemReader.ReadObject();
131 | }
132 | catch
133 | {
134 | return null;
135 | }
136 | }
137 |
138 | public static void DecryptAesEcb(MemoryStream inputStream, byte[] key, Bhd5Range[] ranges)
139 | {
140 | var cipher = CreateAesEcbCipher(key);
141 |
142 | foreach (var range in ranges)
143 | {
144 | if (range.StartOffset == -1 || range.EndOffset == -1)
145 | {
146 | continue;
147 | }
148 |
149 | inputStream.Position = range.StartOffset;
150 | long length = range.EndOffset - range.StartOffset;
151 | MemoryStream decryptedStream = DecryptAes(inputStream, cipher, length);
152 | inputStream.Position = range.StartOffset;
153 | decryptedStream.WriteTo(inputStream);
154 | }
155 | }
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/BinderTool.Core/Dcx/DcxCompression.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace BinderTool.Core.Dcx
4 | {
5 | public abstract class DcxCompression
6 | {
7 | public abstract MemoryStream CompressData(byte[] uncompressedData);
8 | public abstract MemoryStream DecompressData(byte[] compressedData);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/BinderTool.Core/Dcx/DcxFile.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text;
4 | using BinderTool.Core.IO;
5 |
6 | namespace BinderTool.Core.Dcx
7 | {
8 | public class DcxFile
9 | {
10 | public const string DcxSignature = "DCX\0";
11 | public const int DcxSize = 76;
12 |
13 | private const int DcxHeaderSize = 24;
14 | private const string DcsSignature = "DCS\0";
15 | private const string DcpSignature = "DCP\0";
16 | private const string DcaSignature = "DCA\0";
17 | private const int DcaHeaderSize = 8;
18 |
19 | public DcxFile()
20 | {
21 | }
22 |
23 | private DcxFile(DcxCompression compression, byte[] compressedData)
24 | {
25 | Compression = compression;
26 | CompressedData = compressedData;
27 | }
28 |
29 | private DcxCompression Compression { get; set; }
30 | private int CompressedSize { get; set; }
31 | private int UncompressedSize { get; set; }
32 | public byte[] CompressedData { get; private set; }
33 |
34 | public static DcxFile Read(Stream inputStream)
35 | {
36 | DcxFile result = new DcxFile();
37 | BigEndianBinaryReader reader = new BigEndianBinaryReader(inputStream, Encoding.UTF8, true);
38 | result.ReadCommonHeader(reader);
39 | result.ReadCompressionHeader(reader);
40 | result.CompressedData = reader.ReadBytes(result.CompressedSize);
41 | return result;
42 | }
43 |
44 | public static int ReadCompressedSize(Stream inputStream)
45 | {
46 | DcxFile result = new DcxFile();
47 | BigEndianBinaryReader reader = new BigEndianBinaryReader(inputStream, Encoding.UTF8, true);
48 | result.ReadCommonHeader(reader);
49 | return result.CompressedSize;
50 | }
51 |
52 | private void ReadCommonHeader(BinaryReader reader)
53 | {
54 | string signature = reader.ReadString(4);
55 | if (signature != DcxSignature)
56 | throw new Exception("Signature was not DCX");
57 | reader.Skip(4);
58 | int dcxHeaderSize = reader.ReadInt32();
59 | if (dcxHeaderSize != DcxHeaderSize)
60 | throw new Exception("Unsupported DCX header size.");
61 | reader.Skip(12);
62 |
63 | signature = reader.ReadString(4);
64 | if (signature != DcsSignature)
65 | throw new Exception("Signature was not DCS");
66 |
67 | UncompressedSize = reader.ReadInt32();
68 | CompressedSize = reader.ReadInt32();
69 | }
70 |
71 | private void ReadCompressionHeader(BinaryReader reader)
72 | {
73 | string signature = reader.ReadString(4);
74 | if (signature != DcpSignature)
75 | throw new Exception("Signature was not DCP");
76 | signature = reader.ReadString(4);
77 | if (signature != DeflateCompression.DeflateSignature)
78 | throw new NotImplementedException($"Compression not implemented ({signature}) ");
79 |
80 | Compression = DeflateCompression.Read(reader);
81 |
82 | signature = reader.ReadString(4);
83 | if (signature != DcaSignature)
84 | throw new Exception("Signature was not DCA");
85 | int dcaHeaderSize = reader.ReadInt32();
86 | if (dcaHeaderSize != DcaHeaderSize)
87 | throw new Exception("Unsupported DCA header size.");
88 | }
89 |
90 | public byte[] Decompress()
91 | {
92 | return Compression.DecompressData(CompressedData).ToArray();
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/BinderTool.Core/Dcx/DeflateCompression.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
3 |
4 | namespace BinderTool.Core.Dcx
5 | {
6 | public class DeflateCompression : DcxCompression
7 | {
8 | public const string DeflateSignature = "DFLT";
9 | public int Level { get; private set; }
10 |
11 | public override MemoryStream CompressData(byte[] uncompressedData)
12 | {
13 | MemoryStream compressedBufferStream = new MemoryStream();
14 | using (DeflaterOutputStream deflaterStream = new DeflaterOutputStream(compressedBufferStream))
15 | {
16 | deflaterStream.Write(uncompressedData, 0, uncompressedData.Length);
17 | }
18 | return compressedBufferStream;
19 | }
20 |
21 | public override MemoryStream DecompressData(byte[] compressedData)
22 | {
23 | InflaterInputStream inflaterStream = new InflaterInputStream(new MemoryStream(compressedData));
24 | MemoryStream outputStream = new MemoryStream();
25 | inflaterStream.CopyTo(outputStream);
26 | outputStream.Position = 0;
27 | return outputStream;
28 | }
29 |
30 | public static DeflateCompression Read(BinaryReader reader)
31 | {
32 | DeflateCompression result = new DeflateCompression();
33 | int headerSize = reader.ReadInt32();
34 | result.Level = reader.ReadInt32();
35 | reader.Skip(16);
36 | return result;
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/BinderTool.Core/Dds/DdsFile.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text;
4 | using BinderTool.Core.Dds.Enum;
5 |
6 | namespace BinderTool.Core.Dds
7 | {
8 | public class DdsFile
9 | {
10 | private const int MagicNumber = 0x20534444;
11 |
12 | public DdsFileHeader Header { get; set; }
13 |
14 | public DdsFileHeaderDx10 HeaderDx10 { get; set; }
15 |
16 | public byte[] Data { get; set; }
17 |
18 | public void Write(Stream outputStream)
19 | {
20 | BinaryWriter writer = new BinaryWriter(outputStream, Encoding.Default, true);
21 | writer.Write(MagicNumber);
22 | Header.Write(outputStream);
23 | if (Header.IsDx10())
24 | {
25 | HeaderDx10.Write(outputStream);
26 | }
27 |
28 | writer.Write(Data);
29 | }
30 |
31 | public static byte[] ConvertData(byte[] sourceBuffer, int height, int width, DxgiFormat dxgiFormat)
32 | {
33 | if (sourceBuffer == null)
34 | {
35 | return null;
36 | }
37 |
38 | var inputStream = new MemoryStream(sourceBuffer);
39 | byte[] targetBuffer = new byte[sourceBuffer.Length];
40 | byte[] blockBuffer = new byte[64];
41 | int heightBlock = height / 4;
42 | int widthBlock = width / 4;
43 | int bytesPerPixel = DdsPixelFormat.DxgiToBytesPerPixel(dxgiFormat) * 2;
44 | for (int y = 0; y < heightBlock; y++)
45 | {
46 | for (int x = 0; x < widthBlock; x++)
47 | {
48 | int mx = x;
49 | int my = y;
50 | if (widthBlock > 1 && heightBlock > 1)
51 | {
52 | MapBlockPosition(x, y, widthBlock, 2, out mx, out my);
53 | }
54 |
55 | if (widthBlock > 2 && heightBlock > 2)
56 | {
57 | MapBlockPosition(mx, my, widthBlock, 4, out mx, out my);
58 | }
59 |
60 | if (widthBlock > 4 && heightBlock > 4)
61 | {
62 | MapBlockPosition(mx, my, widthBlock, 8, out mx, out my);
63 | }
64 |
65 | inputStream.Read(blockBuffer, 0, bytesPerPixel);
66 | int destinationIndex = bytesPerPixel * (my * widthBlock + mx);
67 | Array.Copy(blockBuffer, 0, targetBuffer, destinationIndex, bytesPerPixel);
68 | }
69 | }
70 |
71 | return targetBuffer;
72 | }
73 |
74 | private static void MapBlockPosition(int x, int y, int w, int bx, out int mx, out int my)
75 | {
76 | int num1 = bx / 2;
77 | int num2 = x / bx;
78 | int num3 = y / num1;
79 | int num4 = x % bx;
80 | int num5 = y % num1;
81 | int num6 = w / bx;
82 | int num7 = 2 * num6;
83 | int num8 = num2 + num3 * num6;
84 | int num9 = num8 % num7;
85 | int num10 = num9 / 2 + num9 % 2 * num6;
86 | int num11 = num8 / num7 * num7 + num10;
87 | int num12 = num11 % num6;
88 | int num13 = num11 / num6;
89 |
90 | mx = num12 * bx + num4;
91 | my = num13 * num1 + num5;
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/BinderTool.Core/Dds/DdsFileHeader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text;
4 | using BinderTool.Core.Dds.Enum;
5 |
6 | namespace BinderTool.Core.Dds
7 | {
8 | public class DdsFileHeader
9 | {
10 | public const int DefaultHeaderSize = 124;
11 |
12 | public int Size { get; set; } = DefaultHeaderSize;
13 |
14 | public DdsFileHeaderFlags Flags { get; set; }
15 |
16 | public int Height { get; set; }
17 |
18 | public int Width { get; set; }
19 |
20 | public int PitchOrLinearSize { get; set; }
21 |
22 | public int Depth { get; set; }
23 |
24 | public int MipMapCount { get; set; }
25 |
26 | public DdsPixelFormat PixelFormat { get; set; }
27 |
28 | public DdsSurfaceFlags Caps { get; set; }
29 |
30 | public DdsCaps2Flags Caps2 { get; set; }
31 |
32 | public int Caps3 { get; set; }
33 |
34 | public int Caps4 { get; set; }
35 |
36 | public void Write(Stream outputStream)
37 | {
38 | BinaryWriter writer = new BinaryWriter(outputStream, Encoding.Default, true);
39 | writer.Write(Size);
40 | writer.Write(Convert.ToInt32(Flags));
41 | writer.Write(Height);
42 | writer.Write(Width);
43 | writer.Write(PitchOrLinearSize);
44 | writer.Write(Depth);
45 | writer.Write(MipMapCount);
46 | // int Reserved1[11];
47 | writer.WriteZeros(44);
48 | PixelFormat.Write(outputStream);
49 | writer.Write(Convert.ToInt32(Caps));
50 | writer.Write(Convert.ToInt32(Caps2));
51 | writer.Write(Caps3);
52 | writer.Write(Caps4);
53 | // int Reserved2
54 | writer.WriteZeros(4);
55 | }
56 |
57 | public bool IsDx10()
58 | {
59 | return PixelFormat != null &&
60 | PixelFormat.Flags.HasFlag(DdsPixelFormatFlag.FourCc) &&
61 | PixelFormat.FourCc == DdsPixelFormat.Dx10FourCc;
62 | }
63 |
64 | public override string ToString()
65 | {
66 | return $"Size: {Size}, Flags: {Flags}, Height: {Height}, Width: {Width}," +
67 | $" PitchOrLinearSize: {PitchOrLinearSize}, Depth: {Depth}, MipMapCount: {MipMapCount}," +
68 | $" PixelFormat: {PixelFormat}, Caps: {Caps}, Caps2: {Caps2}, Caps3: {Caps3}, " + $"Caps4: {Caps4}";
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/BinderTool.Core/Dds/DdsFileHeaderDx10.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text;
4 | using BinderTool.Core.Dds.Enum;
5 |
6 | namespace BinderTool.Core.Dds
7 | {
8 | public class DdsFileHeaderDx10
9 | {
10 | public DxgiFormat Format { get; set; }
11 |
12 | public D3D10ResourceDimension ResourceDimension { get; set; }
13 |
14 | public uint MiscFlag { get; set; }
15 |
16 | public uint ArraySize { get; set; }
17 |
18 | public uint MiscFlags2 { get; set; }
19 |
20 | public void Write(Stream outputStream)
21 | {
22 | BinaryWriter writer = new BinaryWriter(outputStream, Encoding.Default, true);
23 | writer.Write(Convert.ToUInt32(Format));
24 | writer.Write(Convert.ToInt32(ResourceDimension));
25 | writer.Write(MiscFlag);
26 | writer.Write(ArraySize);
27 | writer.Write(MiscFlags2);
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/BinderTool.Core/Dds/DdsPixelFormat.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text;
4 | using BinderTool.Core.Dds.Enum;
5 |
6 | namespace BinderTool.Core.Dds
7 | {
8 | public class DdsPixelFormat
9 | {
10 | private const int DefaultSize = 32;
11 | internal const int Dxt1FourCc = 0x31545844;
12 | internal const int Dxt2FourCc = 0x32545844;
13 | internal const int Dxt3FourCc = 0x33545844;
14 | internal const int Dtx4FourCc = 0x34545844;
15 | internal const int Dtx5FourCc = 0x35545844;
16 | internal const int Dx10FourCc = 0x30315844;
17 | internal const int Ati1FourCc = 0x31495441;
18 | internal const int Ati2FourCc = 0x32495441;
19 | public int Size { get; set; }
20 | public DdsPixelFormatFlag Flags { get; set; }
21 | public int FourCc { get; set; }
22 | public int RgbBitCount { get; set; }
23 | public uint RBitMask { get; set; }
24 | public uint GBitMask { get; set; }
25 | public uint BBitMask { get; set; }
26 | public uint ABitMask { get; set; }
27 |
28 | public void Write(Stream outputStream)
29 | {
30 | BinaryWriter writer = new BinaryWriter(outputStream, Encoding.Default, true);
31 | writer.Write(Size);
32 | writer.Write(Convert.ToUInt32(Flags));
33 | writer.Write(FourCc);
34 | writer.Write(RgbBitCount);
35 | writer.Write(RBitMask);
36 | writer.Write(GBitMask);
37 | writer.Write(BBitMask);
38 | writer.Write(ABitMask);
39 | }
40 |
41 | public static int DxgiToBytesPerPixel(DxgiFormat dxgiFormat)
42 | {
43 | switch (dxgiFormat)
44 | {
45 | case DxgiFormat.Unknown:
46 | return 0;
47 | case DxgiFormat.R32G32B32A32Typeless:
48 | case DxgiFormat.R32G32B32A32Float:
49 | case DxgiFormat.R32G32B32A32Uint:
50 | case DxgiFormat.R32G32B32A32Sint:
51 | return 128;
52 | case DxgiFormat.R32G32B32Typeless:
53 | case DxgiFormat.R32G32B32Float:
54 | case DxgiFormat.R32G32B32Uint:
55 | case DxgiFormat.R32G32B32Sint:
56 | return 96;
57 | case DxgiFormat.R16G16B16A16Typeless:
58 | case DxgiFormat.R16G16B16A16Float:
59 | case DxgiFormat.R16G16B16A16Unorm:
60 | case DxgiFormat.R16G16B16A16Uint:
61 | case DxgiFormat.R16G16B16A16Snorm:
62 | case DxgiFormat.R16G16B16A16Sint:
63 | case DxgiFormat.R32G32Typeless:
64 | case DxgiFormat.R32G32Float:
65 | case DxgiFormat.R32G32Uint:
66 | case DxgiFormat.R32G32Sint:
67 | case DxgiFormat.R32G8X24Typeless:
68 | case DxgiFormat.D32FloatS8X24Uint:
69 | case DxgiFormat.R32FloatX8X24Typeless:
70 | case DxgiFormat.X32TypelessG8X24Uint:
71 | return 64;
72 | case DxgiFormat.R10G10B10A2Typeless:
73 | case DxgiFormat.R10G10B10A2Unorm:
74 | case DxgiFormat.R10G10B10A2Uint:
75 | case DxgiFormat.R11G11B10Float:
76 | case DxgiFormat.R8G8B8A8Typeless:
77 | case DxgiFormat.R8G8B8A8Unorm:
78 | case DxgiFormat.R8G8B8A8UnormSrgb:
79 | case DxgiFormat.R8G8B8A8Uint:
80 | case DxgiFormat.R8G8B8A8Snorm:
81 | case DxgiFormat.R8G8B8A8Sint:
82 | case DxgiFormat.R16G16Typeless:
83 | case DxgiFormat.R16G16Float:
84 | case DxgiFormat.R16G16Unorm:
85 | case DxgiFormat.R16G16Uint:
86 | case DxgiFormat.R16G16Snorm:
87 | case DxgiFormat.R16G16Sint:
88 | case DxgiFormat.R32Typeless:
89 | case DxgiFormat.D32Float:
90 | case DxgiFormat.R32Float:
91 | case DxgiFormat.R32Uint:
92 | case DxgiFormat.R32Sint:
93 | case DxgiFormat.R24G8Typeless:
94 | case DxgiFormat.D24UnormS8Uint:
95 | case DxgiFormat.R24UnormX8Typeless:
96 | case DxgiFormat.X24TypelessG8Uint:
97 | return 32;
98 | case DxgiFormat.R8G8Typeless:
99 | case DxgiFormat.R8G8Unorm:
100 | case DxgiFormat.R8G8Uint:
101 | case DxgiFormat.R8G8Snorm:
102 | case DxgiFormat.R8G8Sint:
103 | case DxgiFormat.R16Typeless:
104 | case DxgiFormat.R16Float:
105 | case DxgiFormat.D16Unorm:
106 | case DxgiFormat.R16Unorm:
107 | case DxgiFormat.R16Uint:
108 | case DxgiFormat.R16Snorm:
109 | case DxgiFormat.R16Sint:
110 | return 16;
111 | case DxgiFormat.R8Typeless:
112 | case DxgiFormat.R8Unorm:
113 | case DxgiFormat.R8Uint:
114 | case DxgiFormat.R8Snorm:
115 | case DxgiFormat.R8Sint:
116 | case DxgiFormat.A8Unorm:
117 | return 8;
118 | case DxgiFormat.R1Unorm:
119 | return 1;
120 | case DxgiFormat.R9G9B9E5Sharedexp:
121 | case DxgiFormat.R8G8B8G8Unorm:
122 | case DxgiFormat.G8R8G8B8Unorm:
123 | return 32;
124 | case DxgiFormat.Bc1Typeless:
125 | case DxgiFormat.Bc1Unorm:
126 | case DxgiFormat.Bc1UnormSrgb:
127 | return 4;
128 | case DxgiFormat.Bc2Typeless:
129 | case DxgiFormat.Bc2Unorm:
130 | case DxgiFormat.Bc2UnormSrgb:
131 | case DxgiFormat.Bc3Typeless:
132 | case DxgiFormat.Bc3Unorm:
133 | case DxgiFormat.Bc3UnormSrgb:
134 | return 8;
135 | case DxgiFormat.Bc4Typeless:
136 | case DxgiFormat.Bc4Unorm:
137 | case DxgiFormat.Bc4Snorm:
138 | return 4;
139 | case DxgiFormat.Bc5Typeless:
140 | case DxgiFormat.Bc5Unorm:
141 | case DxgiFormat.Bc5Snorm:
142 | return 8;
143 | case DxgiFormat.B5G6R5Unorm:
144 | case DxgiFormat.B5G5R5A1Unorm:
145 | return 16;
146 | case DxgiFormat.B8G8R8A8Unorm:
147 | case DxgiFormat.B8G8R8X8Unorm:
148 | case DxgiFormat.R10G10B10XrBiasA2Unorm:
149 | case DxgiFormat.B8G8R8A8Typeless:
150 | case DxgiFormat.B8G8R8A8UnormSrgb:
151 | case DxgiFormat.B8G8R8X8Typeless:
152 | case DxgiFormat.B8G8R8X8UnormSrgb:
153 | return 32;
154 | case DxgiFormat.Bc6HTypeless:
155 | case DxgiFormat.Bc6HUf16:
156 | case DxgiFormat.Bc6HSf16:
157 | case DxgiFormat.Bc7Typeless:
158 | case DxgiFormat.Bc7Unorm:
159 | case DxgiFormat.Bc7UnormSrgb:
160 | return 8;
161 | case DxgiFormat.Ayuv:
162 | case DxgiFormat.Y410:
163 | case DxgiFormat.Y416:
164 | case DxgiFormat.Nv12:
165 | case DxgiFormat.P010:
166 | case DxgiFormat.P016:
167 | case DxgiFormat.Opaque420:
168 | case DxgiFormat.Yuy2:
169 | case DxgiFormat.Y210:
170 | case DxgiFormat.Y216:
171 | case DxgiFormat.Nv11:
172 | case DxgiFormat.Ai44:
173 | case DxgiFormat.Ia44:
174 | case DxgiFormat.P8:
175 | case DxgiFormat.A8P8:
176 | return 0;
177 | case DxgiFormat.B4G4R4A4Unorm:
178 | return 16;
179 | default:
180 | return 0;
181 | }
182 | }
183 |
184 | public static DdsPixelFormat DxgiToDdsPixelFormat(DxgiFormat dxgiFormat)
185 | {
186 | DdsPixelFormat pixelFormat;
187 | switch (dxgiFormat)
188 | {
189 | case DxgiFormat.Bc1Unorm:
190 | case DxgiFormat.Bc1UnormSrgb:
191 | pixelFormat = DdsPfDxt1();
192 | break;
193 | case DxgiFormat.Bc2Unorm:
194 | pixelFormat = DdsPfDxt3();
195 | break;
196 | case DxgiFormat.Bc3Unorm:
197 | pixelFormat = DdsPfDxt5();
198 | break;
199 | case DxgiFormat.Bc4Unorm:
200 | pixelFormat = DdsPfAti1();
201 | break;
202 | case DxgiFormat.Bc5Unorm:
203 | pixelFormat = DdsPfAti2();
204 | break;
205 | default:
206 | pixelFormat = DdsPfDx10();
207 | break;
208 | }
209 |
210 | return pixelFormat;
211 | }
212 |
213 | private static int DxgiToFourCc(DxgiFormat dxgiFormat)
214 | {
215 | int fourCc;
216 | switch (dxgiFormat)
217 | {
218 | case DxgiFormat.Bc1Unorm:
219 | case DxgiFormat.Bc1UnormSrgb:
220 | fourCc = Dxt1FourCc; // DXT1
221 | break;
222 | case DxgiFormat.Bc2Unorm:
223 | fourCc = Dxt3FourCc; // DXT3
224 | break;
225 | case DxgiFormat.Bc3Unorm:
226 | fourCc = Dtx5FourCc; // DXT5
227 | break;
228 | case DxgiFormat.Bc4Unorm:
229 | fourCc = Ati1FourCc; // ATI1
230 | break;
231 | case DxgiFormat.Bc5Unorm:
232 | fourCc = Ati2FourCc; // ATI2
233 | break;
234 | default:
235 | fourCc = Dx10FourCc; // DX10
236 | break;
237 | }
238 |
239 | return fourCc;
240 | }
241 |
242 | public static DdsPixelFormat DdsPfDxt1()
243 | {
244 | return DdsPfDx(Dxt1FourCc); // DXT1
245 | }
246 |
247 | public static DdsPixelFormat DdsPfDxt2()
248 | {
249 | return DdsPfDx(Dxt2FourCc); // DXT2
250 | }
251 |
252 | public static DdsPixelFormat DdsPfDxt3()
253 | {
254 | return DdsPfDx(Dxt3FourCc); // DXT3
255 | }
256 |
257 | public static DdsPixelFormat DdsPfDxt4()
258 | {
259 | return DdsPfDx(Dtx4FourCc); // DXT4
260 | }
261 |
262 | public static DdsPixelFormat DdsPfDxt5()
263 | {
264 | return DdsPfDx(Dtx5FourCc); // DXT5
265 | }
266 |
267 | public static DdsPixelFormat DdsPfAti1()
268 | {
269 | return DdsPfDx(Ati1FourCc); // ATI1
270 | }
271 |
272 | public static DdsPixelFormat DdsPfAti2()
273 | {
274 | return DdsPfDx(Ati2FourCc); // ATI2
275 | }
276 |
277 | public static DdsPixelFormat DdsPfDx10()
278 | {
279 | return DdsPfDx(Dx10FourCc); // DX10
280 | }
281 |
282 | private static DdsPixelFormat DdsPfDx(int fourCc)
283 | {
284 | DdsPixelFormat pixelFormat = new DdsPixelFormat
285 | {
286 | Size = DefaultSize,
287 | Flags = DdsPixelFormatFlag.FourCc,
288 | FourCc = fourCc
289 | };
290 | return pixelFormat;
291 | }
292 |
293 | protected bool Equals(DdsPixelFormat other)
294 | {
295 | return Size == other.Size &&
296 | Flags == other.Flags &&
297 | FourCc == other.FourCc &&
298 | RgbBitCount == other.RgbBitCount &&
299 | RBitMask == other.RBitMask &&
300 | GBitMask == other.GBitMask &&
301 | BBitMask == other.BBitMask &&
302 | ABitMask == other.ABitMask;
303 | }
304 |
305 | public override bool Equals(object obj)
306 | {
307 | if (ReferenceEquals(null, obj)) return false;
308 | if (ReferenceEquals(this, obj)) return true;
309 | if (obj.GetType() != GetType()) return false;
310 | return Equals((DdsPixelFormat) obj);
311 | }
312 |
313 | public override int GetHashCode()
314 | {
315 | unchecked
316 | {
317 | var hashCode = Size;
318 | hashCode = (hashCode*397) ^ (int) Flags;
319 | hashCode = (hashCode*397) ^ FourCc;
320 | hashCode = (hashCode*397) ^ RgbBitCount;
321 | hashCode = (hashCode*397) ^ (int) RBitMask;
322 | hashCode = (hashCode*397) ^ (int) GBitMask;
323 | hashCode = (hashCode*397) ^ (int) BBitMask;
324 | hashCode = (hashCode*397) ^ (int) ABitMask;
325 | return hashCode;
326 | }
327 | }
328 |
329 | public override string ToString()
330 | {
331 | return $"Size: {Size}, Flags: {Flags}, FourCc: {FourCc}, RgbBitCount: {RgbBitCount}," +
332 | $" RBitMask: {RBitMask}, GBitMask: {GBitMask}, BBitMask: {BBitMask}, ABitMask: {ABitMask}";
333 | }
334 | }
335 | }
336 |
--------------------------------------------------------------------------------
/BinderTool.Core/Dds/Enum/D3D10ResourceDimension.cs:
--------------------------------------------------------------------------------
1 | namespace BinderTool.Core.Dds.Enum
2 | {
3 | public enum D3D10ResourceDimension
4 | {
5 | Unknown = 0,
6 | Buffer = 1,
7 | Texture1D = 2,
8 | Texture2D = 3,
9 | Texture3D = 4
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/BinderTool.Core/Dds/Enum/DdsCaps2Flags.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace BinderTool.Core.Dds.Enum
4 | {
5 | [Flags]
6 | public enum DdsCaps2Flags
7 | {
8 | ///
9 | /// DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX
10 | ///
11 | PositiveX = 0x00000600,
12 |
13 | ///
14 | /// DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEX
15 | ///
16 | NegativeX = 0x00000a00,
17 |
18 | ///
19 | /// DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEY
20 | ///
21 | PositiveY = 0x00001200,
22 |
23 | ///
24 | /// DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEY
25 | ///
26 | NegativeY = 0x00002200,
27 |
28 | ///
29 | /// DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEZ
30 | ///
31 | PositiveZ = 0x00004200,
32 |
33 | ///
34 | /// DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEZ
35 | ///
36 | NegativeZ = 0x00008200,
37 |
38 | ///
39 | /// DDSCAPS2_CUBEMAP |
40 | /// DDSCAPS2_CUBEMAP_POSITIVEX | DDSCAPS2_CUBEMAP_NEGATIVEX |
41 | /// DDSCAPS2_CUBEMAP_POSITIVEY | DDSCAPS2_CUBEMAP_POSITIVEY |
42 | /// DDSCAPS2_CUBEMAP_POSITIVEZ | DDSCAPS2_CUBEMAP_NEGATIVEZ
43 | ///
44 | AllFaces = PositiveX | NegativeX | PositiveY | NegativeY | PositiveZ | NegativeZ,
45 |
46 | ///
47 | /// DDSCAPS2_VOLUME
48 | ///
49 | Volume = 0x00200000
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/BinderTool.Core/Dds/Enum/DdsFileHeaderFlags.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace BinderTool.Core.Dds.Enum
4 | {
5 | [Flags]
6 | public enum DdsFileHeaderFlags
7 | {
8 | ///
9 | /// DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT
10 | ///
11 | Texture = 0x00001007,
12 |
13 | ///
14 | /// DDSD_MIPMAPCOUNT
15 | ///
16 | MipMap = 0x00020000,
17 |
18 | ///
19 | /// DDSD_DEPTH
20 | ///
21 | Volume = 0x00800000,
22 |
23 | ///
24 | /// DDSD_PITCH
25 | ///
26 | Pitch = 0x00000008,
27 |
28 | ///
29 | /// DDSD_LINEARSIZE
30 | ///
31 | LinearSize = 0x00080000
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/BinderTool.Core/Dds/Enum/DdsPixelFormatFlag.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace BinderTool.Core.Dds.Enum
4 | {
5 | [Flags]
6 | public enum DdsPixelFormatFlag : uint
7 | {
8 | ///
9 | /// DDPF_ALPHA
10 | ///
11 | Alpha = 0x00000002,
12 |
13 | ///
14 | /// DDPF_FOURCC
15 | ///
16 | FourCc = 0x00000004,
17 |
18 | ///
19 | /// DDPF_RGB
20 | ///
21 | Rgb = 0x00000040,
22 |
23 | ///
24 | /// DDPF_RGB | DDPF_ALPHAPIXELS
25 | ///
26 | Rgba = 0x00000041,
27 |
28 | ///
29 | /// DDPF_LUMINANCE
30 | ///
31 | Luminance = 0x00020000,
32 |
33 | ///
34 | /// Nvidia custom DDPF_NORMA
35 | ///
36 | Normal = 0x80000000
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/BinderTool.Core/Dds/Enum/DdsSurfaceFlags.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace BinderTool.Core.Dds.Enum
4 | {
5 | [Flags]
6 | public enum DdsSurfaceFlags
7 | {
8 | ///
9 | /// DDSCAPS_TEXTURE
10 | ///
11 | Texture = 0x00001000,
12 |
13 | ///
14 | /// DDSCAPS_COMPLEX | DDSCAPS_MIPMAP
15 | ///
16 | MipMap = 0x00400008,
17 |
18 | ///
19 | /// DDSCAPS_COMPLEX
20 | ///
21 | CubeMap = 0x00000008
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/BinderTool.Core/Dds/Enum/DxgiFormat.cs:
--------------------------------------------------------------------------------
1 | namespace BinderTool.Core.Dds.Enum
2 | {
3 | public enum DxgiFormat : uint
4 | {
5 | Unknown = 0,
6 | R32G32B32A32Typeless = 1,
7 | R32G32B32A32Float = 2,
8 | R32G32B32A32Uint = 3,
9 | R32G32B32A32Sint = 4,
10 | R32G32B32Typeless = 5,
11 | R32G32B32Float = 6,
12 | R32G32B32Uint = 7,
13 | R32G32B32Sint = 8,
14 | R16G16B16A16Typeless = 9,
15 | R16G16B16A16Float = 10,
16 | R16G16B16A16Unorm = 11,
17 | R16G16B16A16Uint = 12,
18 | R16G16B16A16Snorm = 13,
19 | R16G16B16A16Sint = 14,
20 | R32G32Typeless = 15,
21 | R32G32Float = 16,
22 | R32G32Uint = 17,
23 | R32G32Sint = 18,
24 | R32G8X24Typeless = 19,
25 | D32FloatS8X24Uint = 20,
26 | R32FloatX8X24Typeless = 21,
27 | X32TypelessG8X24Uint = 22,
28 | R10G10B10A2Typeless = 23,
29 | R10G10B10A2Unorm = 24,
30 | R10G10B10A2Uint = 25,
31 | R11G11B10Float = 26,
32 | R8G8B8A8Typeless = 27,
33 | R8G8B8A8Unorm = 28,
34 | R8G8B8A8UnormSrgb = 29,
35 | R8G8B8A8Uint = 30,
36 | R8G8B8A8Snorm = 31,
37 | R8G8B8A8Sint = 32,
38 | R16G16Typeless = 33,
39 | R16G16Float = 34,
40 | R16G16Unorm = 35,
41 | R16G16Uint = 36,
42 | R16G16Snorm = 37,
43 | R16G16Sint = 38,
44 | R32Typeless = 39,
45 | D32Float = 40,
46 | R32Float = 41,
47 | R32Uint = 42,
48 | R32Sint = 43,
49 | R24G8Typeless = 44,
50 | D24UnormS8Uint = 45,
51 | R24UnormX8Typeless = 46,
52 | X24TypelessG8Uint = 47,
53 | R8G8Typeless = 48,
54 | R8G8Unorm = 49,
55 | R8G8Uint = 50,
56 | R8G8Snorm = 51,
57 | R8G8Sint = 52,
58 | R16Typeless = 53,
59 | R16Float = 54,
60 | D16Unorm = 55,
61 | R16Unorm = 56,
62 | R16Uint = 57,
63 | R16Snorm = 58,
64 | R16Sint = 59,
65 | R8Typeless = 60,
66 | R8Unorm = 61,
67 | R8Uint = 62,
68 | R8Snorm = 63,
69 | R8Sint = 64,
70 | A8Unorm = 65,
71 | R1Unorm = 66,
72 | R9G9B9E5Sharedexp = 67,
73 | R8G8B8G8Unorm = 68,
74 | G8R8G8B8Unorm = 69,
75 | Bc1Typeless = 70,
76 | Bc1Unorm = 71,
77 | Bc1UnormSrgb = 72,
78 | Bc2Typeless = 73,
79 | Bc2Unorm = 74,
80 | Bc2UnormSrgb = 75,
81 | Bc3Typeless = 76,
82 | Bc3Unorm = 77,
83 | Bc3UnormSrgb = 78,
84 | Bc4Typeless = 79,
85 | Bc4Unorm = 80,
86 | Bc4Snorm = 81,
87 | Bc5Typeless = 82,
88 | Bc5Unorm = 83,
89 | Bc5Snorm = 84,
90 | B5G6R5Unorm = 85,
91 | B5G5R5A1Unorm = 86,
92 | B8G8R8A8Unorm = 87,
93 | B8G8R8X8Unorm = 88,
94 | R10G10B10XrBiasA2Unorm = 89,
95 | B8G8R8A8Typeless = 90,
96 | B8G8R8A8UnormSrgb = 91,
97 | B8G8R8X8Typeless = 92,
98 | B8G8R8X8UnormSrgb = 93,
99 | Bc6HTypeless = 94,
100 | Bc6HUf16 = 95,
101 | Bc6HSf16 = 96,
102 | Bc7Typeless = 97,
103 | Bc7Unorm = 98,
104 | Bc7UnormSrgb = 99,
105 | Ayuv = 100,
106 | Y410 = 101,
107 | Y416 = 102,
108 | Nv12 = 103,
109 | P010 = 104,
110 | P016 = 105,
111 | Opaque420 = 106, // 420_OPAQUE
112 | Yuy2 = 107,
113 | Y210 = 108,
114 | Y216 = 109,
115 | Nv11 = 110,
116 | Ai44 = 111,
117 | Ia44 = 112,
118 | P8 = 113,
119 | A8P8 = 114,
120 | B4G4R4A4Unorm = 115
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/BinderTool.Core/Enc/EncFile.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text;
3 | using BinderTool.Core.IO;
4 |
5 | namespace BinderTool.Core.Enc
6 | {
7 | public class EncFile
8 | {
9 | private EncFile(byte[] key)
10 | {
11 | _key = key;
12 | }
13 |
14 | private const int EncryptionIvCbcSize = 16;
15 |
16 | private const int EncryptionIvCtrSize = 32;
17 |
18 | private readonly byte[] _key;
19 |
20 | private readonly byte[] _iv = new byte[EncryptionIvCbcSize];
21 |
22 | public MemoryStream Data { get; private set; }
23 |
24 | public static EncFile ReadEncFile(Stream inputStream, byte[] key, GameVersion version = GameVersion.Common)
25 | {
26 | EncFile encFile = new EncFile(key);
27 |
28 | if (version == GameVersion.DarkSouls2)
29 | {
30 | encFile.ReadCtr(inputStream);
31 | }
32 | else
33 | {
34 | encFile.ReadCbc(inputStream);
35 | }
36 |
37 | return encFile;
38 | }
39 |
40 | private void ReadCbc(Stream inputStream)
41 | {
42 | BigEndianBinaryReader reader = new BigEndianBinaryReader(inputStream, Encoding.ASCII, true);
43 | reader.Read(_iv, 0, EncryptionIvCbcSize);
44 | byte[] encryptedData = new byte[inputStream.Length - EncryptionIvCbcSize];
45 | reader.Read(encryptedData, 0, encryptedData.Length);
46 | Data = CryptographyUtility.DecryptAesCbc(new MemoryStream(encryptedData), _key, _iv);
47 | }
48 |
49 |
50 |
51 | private void ReadCtr(Stream inputStream)
52 | {
53 | BigEndianBinaryReader reader = new BigEndianBinaryReader(inputStream, Encoding.ASCII, true);
54 | _iv[00] = 0x80;
55 | for (int i = 1; i <= 11; i++)
56 | {
57 | _iv[i] = reader.ReadByte();
58 | }
59 | _iv[12] = 0x00;
60 | _iv[13] = 0x00;
61 | _iv[14] = 0x00;
62 | _iv[15] = 0x01;
63 | inputStream.Seek(EncryptionIvCtrSize, SeekOrigin.Begin);
64 | byte[] encryptedData = reader.ReadBytes((int)inputStream.Length - EncryptionIvCtrSize);
65 | Data = CryptographyUtility.DecryptAesCtr(new MemoryStream(encryptedData), _key, _iv);
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/BinderTool.Core/Enfl/EntryFileListFile.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text;
3 | using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
4 |
5 | namespace BinderTool.Core.Enfl
6 | {
7 | public class EntryFileListFile
8 | {
9 | private class EntryFileListEntry1
10 | {
11 | public short Unknown1 { get; set; }
12 |
13 | public short Unknown2 { get; set; }
14 |
15 | public static EntryFileListEntry1 ReadEntryFileListEntry1(BinaryReader reader)
16 | {
17 | EntryFileListEntry1 entry1 = new EntryFileListEntry1();
18 | entry1.Unknown1 = reader.ReadInt16();
19 | entry1.Unknown2 = reader.ReadInt16();
20 | return entry1;
21 | }
22 | }
23 |
24 | private class EntryFileListEntry2
25 | {
26 | public uint Unknown1 { get; set; }
27 |
28 | public int Unknown2 { get; set; }
29 |
30 | public string EntryFileName { get; set; }
31 |
32 | public static EntryFileListEntry2 ReadEntryFileListEntry2(BinaryReader reader)
33 | {
34 | EntryFileListEntry2 entry = new EntryFileListEntry2();
35 | entry.Unknown1 = reader.ReadUInt32();
36 | entry.Unknown2 = reader.ReadInt32();
37 | return entry;
38 | }
39 | }
40 |
41 | public static EntryFileListFile ReadEntryFileListFile(Stream inputStream)
42 | {
43 | EntryFileListFile entryFileListFile = new EntryFileListFile();
44 | entryFileListFile.Read(inputStream);
45 | return entryFileListFile;
46 | }
47 |
48 | private void Read(Stream inputStream)
49 | {
50 | MemoryStream dataStream = Decompress(inputStream);
51 | BinaryReader reader = new BinaryReader(dataStream, Encoding.Unicode, true);
52 |
53 | int unknown1 = reader.ReadInt32(); // 0
54 | int count1 = reader.ReadInt32();
55 | int count2 = reader.ReadInt32();
56 | int unknown2 = reader.ReadInt32(); // 0
57 |
58 | EntryFileListEntry1[] array1 = new EntryFileListEntry1[count1];
59 | for (int i = 0; i < count1; i++)
60 | {
61 | array1[i] = EntryFileListEntry1.ReadEntryFileListEntry1(reader);
62 | }
63 |
64 | reader.Align(16);
65 |
66 | EntryFileListEntry2[] array2 = new EntryFileListEntry2[count2];
67 | for (int i = 0; i < count2; i++)
68 | {
69 | array2[i] = EntryFileListEntry2.ReadEntryFileListEntry2(reader);
70 | }
71 |
72 | reader.Align(16);
73 |
74 | short padding = reader.ReadInt16(); // Might as well be an empty string.
75 |
76 | for (int i = 0; i < count2; i++)
77 | {
78 | array2[i].EntryFileName = reader.ReadNullTerminatedString();
79 | }
80 | }
81 |
82 | private MemoryStream Decompress(Stream inputStream)
83 | {
84 | BinaryReader reader = new BinaryReader(inputStream, Encoding.ASCII, true);
85 | string signature = reader.ReadString(4);
86 | int version = reader.ReadInt32();
87 | int compressedSize = reader.ReadInt32();
88 | int uncompressedSize = reader.ReadInt32();
89 |
90 | byte[] data = reader.ReadBytes(compressedSize);
91 | InflaterInputStream inflaterStream = new InflaterInputStream(new MemoryStream(data));
92 | MemoryStream decompressedStream = new MemoryStream();
93 | inflaterStream.CopyTo(decompressedStream);
94 | decompressedStream.Position = 0;
95 | return decompressedStream;
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/BinderTool.Core/ExtensionMethods.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text;
3 |
4 | namespace BinderTool.Core
5 | {
6 | internal static class ExtensionMethods
7 | {
8 | internal static void Skip(this BinaryReader reader, int count)
9 | {
10 | reader.BaseStream.Position += count;
11 | }
12 |
13 | internal static void WriteZeros(this BinaryWriter writer, int count)
14 | {
15 | byte[] zeros = new byte[count];
16 | writer.Write(zeros);
17 | }
18 |
19 | internal static string ReadString(this BinaryReader reader, int count)
20 | {
21 | return new string(reader.ReadChars(count));
22 | }
23 |
24 | internal static string ReadNullTerminatedString(this BinaryReader reader)
25 | {
26 | StringBuilder builder = new StringBuilder();
27 | char nextCharacter;
28 | while ((nextCharacter = reader.ReadChar()) != 0x00)
29 | {
30 | builder.Append(nextCharacter);
31 | }
32 | return builder.ToString();
33 | }
34 |
35 | internal static long GetPosition(this BinaryReader reader)
36 | {
37 | return reader.BaseStream.Position;
38 | }
39 |
40 | internal static void Seek(this BinaryReader reader, long offset)
41 | {
42 | reader.BaseStream.Seek(offset, SeekOrigin.Begin);
43 | }
44 |
45 | internal static void Seek(this BinaryReader reader, long offset, SeekOrigin origin)
46 | {
47 | reader.BaseStream.Seek(offset, origin);
48 | }
49 |
50 | internal static void Align(this BinaryReader reader, int alignment)
51 | {
52 | long alignmentRequired = reader.BaseStream.Position % alignment;
53 | if (alignmentRequired > 0)
54 | {
55 | reader.BaseStream.Position += alignment - alignmentRequired;
56 | }
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/BinderTool.Core/Fmg/FmgFile.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using System.Text;
4 |
5 | namespace BinderTool.Core.Fmg
6 | {
7 | public class FmgFile
8 | {
9 | public List Entries { get; set; }
10 |
11 | public static FmgFile ReadFmgFile(Stream inputStream)
12 | {
13 | FmgFile fmgFile = new FmgFile();
14 | fmgFile.Read(inputStream);
15 | return fmgFile;
16 | }
17 |
18 | public void Read(Stream inputStream)
19 | {
20 | BinaryReader reader = new BinaryReader(inputStream, Encoding.Unicode, true);
21 |
22 | int unknown1 = reader.ReadInt32(); // 131072
23 | int fileSize = reader.ReadInt32();
24 | int unknown2 = reader.ReadInt32(); // 1
25 | int idRangeCount = reader.ReadInt32();
26 | int stringOffsetCount = reader.ReadInt32();
27 | int unknown3 = reader.ReadInt32(); // 255
28 | long stringOffsetSectionOffset = reader.ReadInt64();
29 | int unknown4 = reader.ReadInt32(); // 0
30 | int unknown5 = reader.ReadInt32(); // 0
31 |
32 | FmgIdRange[] idRanges = new FmgIdRange[idRangeCount];
33 | for (int i = 0; i < idRangeCount; i++)
34 | {
35 | idRanges[i] = new FmgIdRange();
36 | idRanges[i].Read(reader);
37 | }
38 |
39 | inputStream.Seek(stringOffsetSectionOffset, SeekOrigin.Begin);
40 | long[] offsets = new long[stringOffsetCount];
41 | for (int i = 0; i < stringOffsetCount; i++)
42 | {
43 | offsets[i] = reader.ReadInt64();
44 | }
45 |
46 | List entries = new List();
47 | foreach (FmgIdRange idRange in idRanges)
48 | {
49 | for (int i = 0; i < idRange.IdCount; i++)
50 | {
51 | FmgFileEntry entry = new FmgFileEntry();
52 | entry.Id = idRange.FirstId + i;
53 |
54 | long offset = offsets[idRange.OffsetIndex + i];
55 | if (offset > 0)
56 | {
57 | reader.Seek(offset, SeekOrigin.Begin);
58 | entry.Value = reader.ReadNullTerminatedString();
59 | }
60 | else
61 | {
62 | entry.Value = string.Empty;
63 | }
64 |
65 | entries.Add(entry);
66 | }
67 | }
68 |
69 | Entries = entries;
70 | }
71 | }
72 | }
--------------------------------------------------------------------------------
/BinderTool.Core/Fmg/FmgFileEntry.cs:
--------------------------------------------------------------------------------
1 | namespace BinderTool.Core.Fmg
2 | {
3 | public class FmgFileEntry
4 | {
5 | public int Id { get; set; }
6 | public string Value { get; set; }
7 | }
8 | }
--------------------------------------------------------------------------------
/BinderTool.Core/Fmg/FmgIdRange.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace BinderTool.Core.Fmg
4 | {
5 | public class FmgIdRange
6 | {
7 |
8 | public int OffsetIndex { get; set; }
9 |
10 | public int FirstId { get; set; }
11 |
12 | public int LastId { get; set; }
13 |
14 | public int Unknown { get; set; }
15 |
16 | public int IdCount => LastId - FirstId + 1;
17 |
18 | public void Read(BinaryReader reader)
19 | {
20 | OffsetIndex = reader.ReadInt32();
21 | FirstId = reader.ReadInt32();
22 | LastId = reader.ReadInt32();
23 | Unknown = reader.ReadInt32();
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/BinderTool.Core/GameVersion.cs:
--------------------------------------------------------------------------------
1 | namespace BinderTool.Core
2 | {
3 | public enum GameVersion
4 | {
5 | Common,
6 | DarkSouls2,
7 | DarkSouls3,
8 | Bloodborne
9 | }
10 | }
--------------------------------------------------------------------------------
/BinderTool.Core/IO/BigEndianBinaryReader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text;
4 |
5 | namespace BinderTool.Core.IO
6 | {
7 | internal class BigEndianBinaryReader : BinaryReader
8 | {
9 | private const byte DecimalSignBit = 128;
10 |
11 | public BigEndianBinaryReader(Stream inputStream) : base(inputStream)
12 | {
13 | }
14 |
15 | public BigEndianBinaryReader(Stream inputStream, Encoding encoding) : base(inputStream, encoding)
16 | {
17 | }
18 |
19 | public BigEndianBinaryReader(Stream inputStream, Encoding encoding, bool leaveOpen) : base(inputStream, encoding, leaveOpen)
20 | {
21 | }
22 |
23 | private byte[] Reverse(byte[] buffer)
24 | {
25 | Array.Reverse(buffer);
26 | return buffer;
27 | }
28 |
29 | public override char ReadChar()
30 | {
31 | return base.ReadChar();
32 | }
33 |
34 | public override char[] ReadChars(int count)
35 | {
36 | return base.ReadChars(count);
37 | }
38 |
39 | public override decimal ReadDecimal()
40 | {
41 | byte[] src = Reverse(ReadBytes(sizeof (decimal)));
42 | return new decimal(
43 | BitConverter.ToInt32(src, 0),
44 | BitConverter.ToInt32(src, 4),
45 | BitConverter.ToInt32(src, 8),
46 | src[15] == DecimalSignBit,
47 | src[14]);
48 | }
49 |
50 | public override double ReadDouble()
51 | {
52 | return BitConverter.ToDouble(Reverse(ReadBytes(sizeof (double))), 0);
53 | }
54 |
55 | public override short ReadInt16()
56 | {
57 | return BitConverter.ToInt16(Reverse(ReadBytes(sizeof (short))), 0);
58 | }
59 |
60 | public override int ReadInt32()
61 | {
62 | return BitConverter.ToInt32(Reverse(ReadBytes(sizeof (int))), 0);
63 | }
64 |
65 | public override long ReadInt64()
66 | {
67 | return BitConverter.ToInt64(Reverse(ReadBytes(sizeof (long))), 0);
68 | }
69 |
70 | public override float ReadSingle()
71 | {
72 | return BitConverter.ToSingle(Reverse(ReadBytes(sizeof (float))), 0);
73 | }
74 |
75 | public override string ReadString()
76 | {
77 | int size = ReadInt32();
78 | return new string(ReadChars(size));
79 | }
80 |
81 | public override ushort ReadUInt16()
82 | {
83 | return BitConverter.ToUInt16(Reverse(ReadBytes(sizeof (ushort))), 0);
84 | }
85 |
86 | public override uint ReadUInt32()
87 | {
88 | return BitConverter.ToUInt32(Reverse(ReadBytes(sizeof (uint))), 0);
89 | }
90 |
91 | public override ulong ReadUInt64()
92 | {
93 | return BitConverter.ToUInt64(Reverse(ReadBytes(sizeof (ulong))), 0);
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/BinderTool.Core/Param/ParamEntry.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace BinderTool.Core.Param
4 | {
5 | public class ParamEntry
6 | {
7 | public long Id { get; set; }
8 | public long Offset { get; set; }
9 | public long Size { get; set; }
10 | public byte[] Data { get; set; }
11 |
12 | public void Read(BinaryReader reader)
13 | {
14 | Id = reader.ReadInt64();
15 | Offset = reader.ReadInt64();
16 | Size = reader.ReadInt64();
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/BinderTool.Core/Param/ParamFile.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text;
3 |
4 | namespace BinderTool.Core.Param
5 | {
6 | public class ParamFile
7 | {
8 | public string StructName { get; set; }
9 | public int Version { get; set; }
10 | public int EntrySize { get; set; }
11 | public ParamEntry[] Entries { get; set; }
12 | public short Type1 { get; set; }
13 | public short Type2 { get; set; }
14 |
15 | public static ParamFile ReadParamFile(Stream inputStream)
16 | {
17 | ParamFile paramFile = new ParamFile();
18 | paramFile.Read(inputStream);
19 | return paramFile;
20 | }
21 |
22 | public void Read(Stream inputStream)
23 | {
24 | BinaryReader reader = new BinaryReader(inputStream, Encoding.ASCII, true);
25 | int fileSize = reader.ReadInt32();
26 | short unknown1 = reader.ReadInt16(); // 0
27 | short type1 = reader.ReadInt16(); // 0-2
28 | short type2 = reader.ReadInt16(); // 0-10
29 | short count = reader.ReadInt16();
30 | int unknown4 = reader.ReadInt32();
31 | int fileSize2 = reader.ReadInt32();
32 | int unknown6 = reader.ReadInt32();
33 | int unknown7 = reader.ReadInt32();
34 | int unknown8 = reader.ReadInt32();
35 | int unknown9 = reader.ReadInt32();
36 | int unknown10 = reader.ReadInt32();
37 | int unknown13 = reader.ReadInt32();
38 | int version = reader.ReadInt32();
39 | int dataOffset = reader.ReadInt32();
40 | int unknown14 = reader.ReadInt32();
41 | int unknown15 = reader.ReadInt32();
42 | int unknown16 = reader.ReadInt32();
43 |
44 | ParamEntry[] entries = new ParamEntry[count];
45 | for (int i = 0; i < count; i++)
46 | {
47 | entries[i] = new ParamEntry();
48 | entries[i].Read(reader);
49 | }
50 |
51 | const int headerSize = 64;
52 | const int dictionarySize = 24;
53 |
54 | int entrySize = (fileSize - headerSize - count * dictionarySize) / count;
55 | for (int i = 0; i < count; i++)
56 | {
57 | entries[i].Data = reader.ReadBytes(entrySize);
58 | }
59 |
60 | string paramName = reader.ReadNullTerminatedString();
61 |
62 | StructName = paramName;
63 | Version = version;
64 | Type1 = type1;
65 | Type2 = type2;
66 | EntrySize = entrySize;
67 | Entries = entries;
68 | }
69 | }
70 | }
--------------------------------------------------------------------------------
/BinderTool.Core/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | [assembly: AssemblyTitle("BinderTool.Core")]
5 | [assembly: AssemblyDescription("")]
6 | [assembly: AssemblyConfiguration("")]
7 | [assembly: AssemblyCompany("")]
8 | [assembly: AssemblyProduct("BinderTool.Core")]
9 | [assembly: AssemblyCopyright("Copyright © 2018 Atvaark")]
10 | [assembly: AssemblyTrademark("")]
11 | [assembly: AssemblyCulture("")]
12 | [assembly: ComVisible(false)]
13 | [assembly: Guid("615e60b8-3eb0-4f54-ad9b-4fa1a9fe0df0")]
14 | [assembly: AssemblyVersion("0.5.2.0")]
15 | [assembly: AssemblyFileVersion("0.5.2.0")]
16 |
--------------------------------------------------------------------------------
/BinderTool.Core/Sl2/Sl2File.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Text;
5 |
6 | namespace BinderTool.Core.Sl2
7 | {
8 | public class Sl2File
9 | {
10 | private const string Sl2Signature = "BND4";
11 |
12 | private readonly byte[] _key;
13 |
14 | private readonly List _userData;
15 |
16 | public Sl2File(byte[] key)
17 | {
18 | _key = key;
19 | _userData = new List();
20 | }
21 |
22 | public List UserData => _userData;
23 |
24 | public static Sl2File ReadSl2File(Stream inputStream, byte[] key)
25 | {
26 | Sl2File sl2File = new Sl2File(key);
27 | sl2File.Read(inputStream);
28 | return sl2File;
29 | }
30 |
31 | public void Read(Stream inputStream)
32 | {
33 | BinaryReader reader = new BinaryReader(inputStream, Encoding.UTF8, true);
34 | BinaryReader unicodeReader = new BinaryReader(inputStream, Encoding.Unicode, true);
35 | string signature = reader.ReadString(4);
36 | if (signature != Sl2Signature)
37 | throw new Exception("Unknown signature");
38 | reader.Skip(8);
39 | int userDataCount = reader.ReadInt32();
40 | reader.Skip(8);
41 | string version = reader.ReadString(8);
42 | int directoryEntrySize = reader.ReadInt32();
43 | reader.Skip(4);
44 | int dataOffset = reader.ReadInt32();
45 | reader.Skip(20);
46 |
47 | // Directory section
48 | for (int i = 0; i < userDataCount; i++)
49 | {
50 | reader.Skip(8);
51 | int userDataSize = reader.ReadInt32();
52 | reader.Skip(4);
53 |
54 | int userDataOffset = reader.ReadInt32();
55 | int userDataNameOffset = reader.ReadInt32();
56 | reader.Skip(8);
57 |
58 |
59 | long position = reader.GetPosition();
60 | string fileName = "";
61 | if (userDataNameOffset > 0)
62 | {
63 | reader.Seek(userDataNameOffset);
64 | fileName = unicodeReader.ReadNullTerminatedString();
65 | }
66 | reader.Seek(userDataOffset);
67 | _userData.Add(Sl2UserData.ReadSl2UserData(inputStream, _key, userDataSize, fileName));
68 | reader.Seek(position);
69 | }
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/BinderTool.Core/Sl2/Sl2UserData.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text;
3 |
4 | namespace BinderTool.Core.Sl2
5 | {
6 | public class Sl2UserData
7 | {
8 | private const int UserDataIvSize = 16;
9 |
10 | private readonly byte[] _key;
11 |
12 | private readonly byte[] _iv;
13 |
14 | public Sl2UserData(byte[] key)
15 | {
16 | _key = key;
17 | _iv = new byte[UserDataIvSize];
18 | }
19 |
20 | public string Name { get; set; }
21 |
22 | public byte[] EncryptedUserData { get; private set; }
23 |
24 | public byte[] DecryptedUserData => CryptographyUtility.DecryptAesCbc(new MemoryStream(EncryptedUserData), _key, _iv).ToArray();
25 |
26 | public static Sl2UserData ReadSl2UserData(Stream inputStream, byte[] key, int size, string name)
27 | {
28 | Sl2UserData sl2UserData = new Sl2UserData(key);
29 | sl2UserData.Name = name;
30 | sl2UserData.Read(inputStream, size);
31 | return sl2UserData;
32 | }
33 |
34 | private void Read(Stream inputStream, int userDataSize)
35 | {
36 | BinaryReader reader = new BinaryReader(inputStream, Encoding.ASCII, true);
37 | reader.Read(_iv, 0, UserDataIvSize);
38 | EncryptedUserData = reader.ReadBytes(userDataSize - UserDataIvSize);
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/BinderTool.Core/Tpf/TpfFile.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using System.Text;
5 |
6 | namespace BinderTool.Core.Tpf
7 | {
8 | public class TpfFile
9 | {
10 | public List Entries { get; private set; }
11 |
12 | public static TpfFile OpenTpfFile(Stream inputStream)
13 | {
14 | TpfFile tpfFile = new TpfFile();
15 | tpfFile.Read(inputStream);
16 | return tpfFile;
17 | }
18 |
19 | private void Read(Stream inputStream)
20 | {
21 | BinaryReader reader = new BinaryReader(inputStream, Encoding.ASCII, true);
22 | string signature = reader.ReadString(4);
23 | int sizeSum = reader.ReadInt32();
24 | int entryCount = reader.ReadInt32();
25 |
26 | byte versionFlag = reader.ReadByte(); // 00 DS / 02 DeS / 04 BB
27 | byte flag2 = reader.ReadByte(); // 03
28 | byte encoding = reader.ReadByte();
29 | byte flag3 = reader.ReadByte(); // 00
30 |
31 | switch (encoding)
32 | {
33 | case 1: // Unicode
34 | reader = new BinaryReader(inputStream, Encoding.Unicode, true);
35 | break;
36 | case 2: // ASCII
37 | break;
38 | default:
39 | reader = new BinaryReader(inputStream, Encoding.Unicode, true);
40 | Debug.WriteLine($"Unknown encoding {encoding}");
41 | break;
42 | }
43 |
44 | // TODO: Verify that versionFlag is actually different between DS and BB
45 | GameVersion gameVersion = versionFlag == 0x04
46 | ? GameVersion.Bloodborne
47 | : GameVersion.Common;
48 |
49 | List entries = new List(entryCount);
50 | for (int i = 0; i < entryCount; i++)
51 | {
52 | entries.Add(TpfFileEntry.Read(reader, gameVersion));
53 | }
54 |
55 | Entries = entries;
56 | }
57 | }
58 | }
--------------------------------------------------------------------------------
/BinderTool.Core/Tpf/TpfFileEntry.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using BinderTool.Core.Dds;
3 | using BinderTool.Core.Dds.Enum;
4 |
5 | namespace BinderTool.Core.Tpf
6 | {
7 | public class TpfFileEntry
8 | {
9 | public GameVersion GameVersion { get; set; }
10 |
11 | public byte Format { get; set; }
12 |
13 | public byte Type { get; set; }
14 |
15 | public byte MipMapCount { get; set; }
16 |
17 | public byte Flags { get; set; }
18 |
19 | public int Unknown { get; set; }
20 |
21 | public int DxgiFormat { get; set; }
22 |
23 | public short Height { get; set; }
24 |
25 | public short Width { get; set; }
26 |
27 | public byte[] Data { get; set; }
28 |
29 | public string FileName { get; set; }
30 |
31 | public static TpfFileEntry Read(BinaryReader reader, GameVersion gameVersion)
32 | {
33 | TpfFileEntry result = new TpfFileEntry();
34 | result.GameVersion = gameVersion;
35 | int dataOffset = reader.ReadInt32();
36 | int dataSize = reader.ReadInt32();
37 |
38 | // 0 = ?
39 | // 1 = ?
40 | // 5 = ?
41 | // 6 = ?
42 | // 9 = ?
43 | // 10 = ?
44 | // 100 = ?
45 | // 102 = ?
46 | // 103 = ?
47 | // 104 = ?
48 | // 105 = ?
49 | // 106 = ?
50 | // 107 = ?
51 | // 108 = ?
52 | // 109 = ?
53 | // 113 = ?
54 | result.Format = reader.ReadByte();
55 | result.Type = reader.ReadByte(); // 0 = texture, 1 = cubemap
56 |
57 | result.MipMapCount = reader.ReadByte();
58 | result.Flags = reader.ReadByte();
59 |
60 | int fileNameOffset;
61 | if (gameVersion == GameVersion.Bloodborne)
62 | {
63 | result.Width = reader.ReadInt16();
64 | result.Height = reader.ReadInt16();
65 | int unknown1 = reader.ReadInt32(); // 1
66 | int unknown2 = reader.ReadInt32(); // 13
67 | fileNameOffset = reader.ReadInt32();
68 | int unknown3 = reader.ReadInt32(); // 0
69 | result.DxgiFormat = reader.ReadInt32();
70 | }
71 | else
72 | {
73 | fileNameOffset = reader.ReadInt32();
74 | result.Unknown = reader.ReadInt32(); // 0 = ?, 1 = ?
75 | }
76 |
77 | long position = reader.GetPosition();
78 | if (fileNameOffset > 0)
79 | {
80 | reader.Seek(fileNameOffset);
81 | result.FileName = reader.ReadNullTerminatedString();
82 | result.FileName += ".dds";
83 | }
84 |
85 | if (dataOffset > 0)
86 | {
87 | reader.Seek(dataOffset);
88 | result.Data = reader.ReadBytes(dataSize);
89 | }
90 |
91 | reader.Seek(position);
92 |
93 | return result;
94 | }
95 |
96 | public void Write(Stream outputStream)
97 | {
98 | if (Data == null)
99 | {
100 | return;
101 | }
102 |
103 | byte[] data = Data;
104 | if (GameVersion == GameVersion.Bloodborne)
105 | {
106 | DxgiFormat dxgiFormat = (DxgiFormat) DxgiFormat;
107 | DdsPixelFormat pixelFormat = DdsPixelFormat.DxgiToDdsPixelFormat(dxgiFormat);
108 | DdsFile ddsFile = new DdsFile
109 | {
110 | Header = new DdsFileHeader
111 | {
112 | Flags = DdsFileHeaderFlags.Texture | (MipMapCount > 1 ? DdsFileHeaderFlags.MipMap : 0),
113 | Height = Height,
114 | Width = Width,
115 | Depth = 0,
116 | PitchOrLinearSize = Data.Length,
117 | MipMapCount = MipMapCount,
118 | PixelFormat = pixelFormat,
119 | Caps = DdsSurfaceFlags.Texture | (MipMapCount > 1 ? DdsSurfaceFlags.MipMap : 0)
120 | },
121 | HeaderDx10 = pixelFormat.FourCc != DdsPixelFormat.Dx10FourCc
122 | ? null
123 | : new DdsFileHeaderDx10
124 | {
125 | Format = dxgiFormat,
126 | ResourceDimension = D3D10ResourceDimension.Texture2D,
127 | MiscFlag = 0,
128 | ArraySize = 1,
129 | MiscFlags2 = 0
130 | },
131 | Data = DdsFile.ConvertData(data, Height, Width, dxgiFormat)
132 | };
133 | ddsFile.Write(outputStream);
134 | }
135 | else
136 | {
137 | outputStream.Write(data, 0, data.Length);
138 | }
139 | }
140 | }
141 | }
--------------------------------------------------------------------------------
/BinderTool.Core/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/BinderTool.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26730.3
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BinderTool", "BinderTool\BinderTool.csproj", "{1566BC27-029D-45AC-B16B-FAA2ACFB26D3}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BinderTool.Core", "BinderTool.Core\BinderTool.Core.csproj", "{D8F8A776-F5B5-46EF-8578-390B988D0D0B}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {1566BC27-029D-45AC-B16B-FAA2ACFB26D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {1566BC27-029D-45AC-B16B-FAA2ACFB26D3}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {1566BC27-029D-45AC-B16B-FAA2ACFB26D3}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {1566BC27-029D-45AC-B16B-FAA2ACFB26D3}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {D8F8A776-F5B5-46EF-8578-390B988D0D0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {D8F8A776-F5B5-46EF-8578-390B988D0D0B}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {D8F8A776-F5B5-46EF-8578-390B988D0D0B}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {D8F8A776-F5B5-46EF-8578-390B988D0D0B}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {27C26559-06D8-49DA-B120-C0FAF836B633}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/BinderTool/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/BinderTool/BinderTool.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {1566BC27-029D-45AC-B16B-FAA2ACFB26D3}
8 | Exe
9 | Properties
10 | BinderTool
11 | BinderTool
12 | v4.5.2
13 | 512
14 | ..\
15 | true
16 |
17 |
18 |
19 | AnyCPU
20 | true
21 | full
22 | false
23 | bin\Debug\
24 | DEBUG;TRACE
25 | prompt
26 | 4
27 |
28 |
29 | AnyCPU
30 | pdbonly
31 | true
32 | bin\Release\
33 | TRACE
34 | prompt
35 | 4
36 |
37 |
38 |
39 |
40 |
41 | ..\packages\System.ValueTuple.4.4.0\lib\netstandard1.0\System.ValueTuple.dll
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | PreserveNewest
61 |
62 |
63 |
64 |
65 |
66 | {d8f8a776-f5b5-46ef-8578-390b988d0d0b}
67 | BinderTool.Core
68 |
69 |
70 |
71 |
72 | PreserveNewest
73 |
74 |
75 |
76 |
83 |
--------------------------------------------------------------------------------
/BinderTool/DecryptionKeys.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace BinderTool
6 | {
7 | internal static class DecryptionKeys
8 | {
9 | private static readonly Dictionary RsaKeyDictionary;
10 |
11 | private static readonly Dictionary AesKeyDictionary;
12 |
13 | static DecryptionKeys()
14 | {
15 | RsaKeyDictionary = new Dictionary(StringComparer.InvariantCultureIgnoreCase)
16 | {
17 | { "Data1.bhd", Data1Key },
18 | { "Data2.bhd", Data2Key },
19 | { "Data3.bhd", Data3Key },
20 | { "Data4.bhd", Data4Key },
21 | { "Data5.bhd", Data5Key },
22 | { "DLC1.bhd", Dlc1Key },
23 | { "DLC2.bhd", Dlc2Key },
24 | };
25 |
26 | AesKeyDictionary = new Dictionary(StringComparer.InvariantCultureIgnoreCase)
27 | {
28 | { "regulation.regbnd.dcx.enc", RegulationFileKeyDs3 },
29 | { "enc_regulation.bnd.dcx", RegulationFileKeyDs2 },
30 | };
31 | }
32 |
33 | public static bool TryGetRsaFileKey(string file, out string key)
34 | {
35 | return RsaKeyDictionary.TryGetValue(file, out key);
36 | }
37 |
38 | public static bool TryGetAesFileKey(string file, out byte[] key)
39 | {
40 | return AesKeyDictionary.TryGetValue(file, out key);
41 | }
42 |
43 | ///
44 | /// The key can be read by setting a breakpoint on the "create_serialCipherKey" method of the SerialKeyGeneratorSPI class.
45 | /// Signature: SerialCipherKey *__fastcall SerialKeyGeneratorSPI::create_serialCipherKey(
46 | /// SerialKeyGeneratorSPI *this,
47 | /// const void *pKeyType,
48 | /// const void *pKey,
49 | /// unsigned int keylen,
50 | /// const void *pHeapAllocator)
51 | ///
52 | /// These are the DarkSoulsIII.exe 1.3.1.0 offsets:
53 | /// Address (vtable): 0000000142AECBB8 (DLCR::DLSerialKeyGeneratorSPI::vftable + 0x08)
54 | /// Address (method): 0000000141790180
55 | ///
56 | public static readonly byte[] UserDataKeyDs3 =
57 | {
58 | 0xFD, 0x46, 0x4D, 0x69, 0x5E, 0x69, 0xA3, 0x9A,
59 | 0x10, 0xE3, 0x19, 0xA7, 0xAC, 0xE8, 0xB7, 0xFA
60 | };
61 |
62 | public static readonly byte[] UserDataKeyDs2 =
63 | {
64 | 0xB7, 0xFD, 0x46, 0x3E, 0x4A, 0x9C, 0x11, 0x02,
65 | 0xDF, 0x17, 0x39, 0xE5, 0xF3, 0xB2, 0xA5, 0x0F
66 | };
67 |
68 | ///
69 | ///
70 | ///
71 | public static readonly byte[] RegulationFileKeyDs3 = Encoding.ASCII.GetBytes("ds3#jn/8_7(rsY9pg55GFN7VFL#+3n/)");
72 |
73 | public static readonly byte[] RegulationFileKeyDs2 =
74 | {
75 | 0x40, 0x17, 0x81, 0x30, 0xDF, 0x0A, 0x94, 0x54,
76 | 0x33, 0x09, 0xE1, 0x71, 0xEC, 0xBF, 0x25, 0x4C
77 | };
78 |
79 | ///
80 | ///
81 | ///
82 | public static readonly byte[] NetworkSessionKeyDs3 = Encoding.ASCII.GetBytes("ds3vvhes09djxwcj");
83 |
84 | private const string Data1Key =
85 | @"-----BEGIN RSA PUBLIC KEY-----
86 | MIIBCwKCAQEA05hqyboW/qZaJ3GBIABFVt1X1aa0/sKINklvpkTRC+5Ytbxvp18L
87 | M1gN6gjTgSJiPUgdlaMbptVa66MzvilEk60aHyVVEhtFWy+HzUZ3xRQm6r/2qsK3
88 | 8wXndgEU5JIT2jrBXZcZfYDCkUkjsGVkYqjBNKfp+c5jlnNwbieUihWTSEO+DA8n
89 | aaCCzZD3e7rKhDQyLCkpdsGmuqBvl02Ou7QeehbPPno78mOYs2XkP6NGqbFFGQwa
90 | swyyyXlQ23N15ZaFGRRR0xYjrX4LSe6OJ8Mx/Zkec0o7L28CgwCTmcD2wO8TEATE
91 | AUbbV+1Su9uq2+wQxgnsAp+xzhn9og9hmwIEC35bSQ==
92 | -----END RSA PUBLIC KEY-----";
93 |
94 | private const string Data2Key =
95 | @"-----BEGIN RSA PUBLIC KEY-----
96 | MIIBCwKCAQEAvCZAK9UfPdk5JaTlG7n1r0LSVzIan3h0BSLaMXQHOwO7tTGpvtdX
97 | m2ZLY9y8SVmOxWTQqRq14aVGLTKDyH87hPuKd47Y0E5K5erTqBbXW6AD4El1eir2
98 | VJz/pwHt73FVziOlAnao1A5MsAylZ9B5QJyzHJQG+LxzMzmWScyeXlQLOKudfiIG
99 | 0qFw/xhRMLNAI+iypkzO5NKblYIySUV5Dx7649XdsZ5UIwJUhxONsKuGS+MbeTFB
100 | mTMehtNj5EwPxGdT4CBPAWdeyPhpoHJHCbgrtnN9akwQmpwdBBxT/sTD16Adn9B+
101 | TxuGDQQALed4S4KvM+fadx27pQz8pP9VLwIEL67iCQ==
102 | -----END RSA PUBLIC KEY-----";
103 |
104 | private const string Data3Key =
105 | @"-----BEGIN RSA PUBLIC KEY-----
106 | MIIBCwKCAQEAqLytWD20TSXPeAA1RGDwPW18nJwe2rBX+0HPtdzFmQc/KmQlWrP+
107 | 94k6KClK5f7m0xUHwT8+yFGLxPdRvUPyOhBEnRA6tkObVDSxij5y0Jh4h4ilAO73
108 | I8VMcmscS71UKkck4444+eR4vVd+SPlzIu8VgqLefvEn/sX/pAevDp7w+gD0NgvO
109 | e9U6iWEXKwTOPB97X+Y2uB03gSSognmV8h2dtUFJ4Ryn5jrpWmsuUbdvGp0CWBKH
110 | CFruNXnfsG0hlf9LqbVmEzbFl/MhjBmbVjjtelorZsoLPK+OiPTHW5EcwwnPh1vH
111 | FFGM7qRMc0yvHqJnniEWDsSz8Bvg+GxpgQIEC8XNVw==
112 | -----END RSA PUBLIC KEY-----";
113 |
114 | private const string Data4Key =
115 | @"-----BEGIN RSA PUBLIC KEY-----
116 | MIIBCwKCAQEArfUaZWjYAUaZ0q+5znpX55GeyepawCZ5NnsMjIW9CA3vrOgUGRkh
117 | 6aAU9frlafQ81LQMRgAznOnQGE7K3ChfySDpq6b47SKm4bWPqd7Ulh2DTxIgi6QP
118 | qm4UUJL2dkLaCnuoya/pGMOOvhT1LD/0CKo/iKwfBcYf/OAnwSnxMRC3SNRugyvF
119 | ylCet9DEdL5L8uBEa4sV4U288ZxZSZLg2tB10xy5SHAsm1VNP4Eqw5iJbqHEDKZW
120 | n2LJP5t5wpEJvV2ACiA4U5fyjQLDzRwtCKzeK7yFkKiZI95JJhU/3DnVvssjIxku
121 | gYZkS9D3k9m+tkNe0VVrd4mBEmqVxg+V9wIEL6Y6tw==
122 | -----END RSA PUBLIC KEY-----";
123 |
124 | private const string Data5Key =
125 | @"-----BEGIN RSA PUBLIC KEY-----
126 | MIIBCwKCAQEAvKTlU3nka4nQesRnYg1NWovCCTLhEBAnjmXwI69lFYfc4lvZsTrQ
127 | E0Y25PtoP0ZddA3nzflJNz1rBwAkqfBRGTeeTCAyoNp/iel3EAkid/pKOt3JEkHx
128 | rojRuWYSQ0EQawcBbzCfdLEjizmREepRKHIUSDWgu0HTmwSFHHeCFbpBA1h99L2X
129 | izH5XFTOu0UIcUmBLsK6DYsIj5QGrWaxwwXcTJN/X+/syJ/TbQK9W/TCGaGiirGM
130 | 1u2wvZXSZ7uVM3CHwgNhAMiqLvqORygcDeNqxgq+dXDTxka43j7iPJWdHs8b25fy
131 | aH3kbUxKlDGaEENNNyZQcQrgz8Q76jIE0QIEFUsz9w==
132 | -----END RSA PUBLIC KEY-----";
133 |
134 | private const string Dlc1Key =
135 | @"-----BEGIN RSA PUBLIC KEY-----
136 | MIIBCwKCAQEAsCGM9dFwzaIOUIin3DXy7xrmI2otKGLZJQyKi5X3znKhSTywpcFc
137 | KoW6hgjeh4fJW24jhzwBosG6eAzDINm+K02pHCG8qZ/D/hIbu+ui0ENDKqrVyFhn
138 | QtX5/QJkVQtj8M4a0FIfdtE3wkxaKtP6IXWIy4DesSdGWONVWLfi2eq62A5ts5MF
139 | qMoSV3XjTYuCgXqZQ6eOE+NIBQRqpZxLNFSzbJwWXpAg2kBMkpy5+ywOByjmWzUw
140 | jnIFl1T17R8DpTU/93ojx+/q1p+b1o5is5KcoP7QwjOqzjHJH8bTytzRbgmRcDMW
141 | 3ahxgI070d45TMXK2YwRzI6/JbM1P29anQIEFezyYw==
142 | -----END RSA PUBLIC KEY-----";
143 |
144 | private const string Dlc2Key =
145 | @"-----BEGIN RSA PUBLIC KEY-----
146 | MIIBCwKCAQEAtCXU9a/GBMVoqtpQox9p0/5sWPaIvDp8avLFnIBhN7vkgTwulZHi
147 | u64vZAiUAdVeFX4F+Qtk+5ivK488Mu2CzAMJcz5RvyMQJtOQXuDDqzIv21Tr5zuu
148 | sswoErHxxP8TZNxkHm7Ram7Oqtn7LQnMTYxsBgZZ34yJkRtAmZnGoCu5YaUR5euk
149 | 8lF75idi97ssczUNV212tLzIMa1YOV7sxOb7+gc0VTIqs3pa+OXLPI/bMfwUc/KN
150 | jur5aLDDntQHGx5zuNtc78gMGwlmPqDhgTusKPO4VyKvoL0kITYvukoXJATaa1HI
151 | WVUjhLm+/uj8r8PNgolerDeS+8FM5Bpe9QIEHwCZLw==
152 | -----END RSA PUBLIC KEY-----";
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/BinderTool/FileNameDictionary.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Reflection;
6 | using BinderTool.Core;
7 |
8 | namespace BinderTool
9 | {
10 | public class FileNameDictionary
11 | {
12 | private static readonly string[] VirtualRoots = {
13 | @"N:\SPRJ\data\",
14 | @"N:\FDP\data\"
15 | };
16 |
17 | private static readonly string[] PhysicalRootsDs3 = {
18 | "capture",
19 | "data1",
20 | "data2",
21 | "data3",
22 | "data4",
23 | "data5",
24 | "system",
25 | "temp",
26 | "config",
27 | "debug",
28 | "debugdata",
29 | "dbgai",
30 | "parampatch",
31 |
32 | "chrhkx",
33 | "chrflver",
34 | "tpfbnd",
35 | "hkxbnd",
36 | };
37 |
38 | private static readonly Dictionary SubstitutionMapDs2 = new Dictionary
39 | {
40 | { "chr", "gamedata:" },
41 | { "chrhq", "hqchr:" },
42 | { "dlc_data", "gamedata:" },
43 | { "dlc_menu", "gamedata:" },
44 | { "eventmaker", "gamedata:" },
45 | { "ezstate", "gamedata:" },
46 | { "gamedata", "gamedata:" },
47 | { "gamedata_patch", "gamedata:" },
48 | { "icon", "gamedata:" },
49 | { "map", "gamedata:" },
50 | { "maphq", "hqmap:" },
51 | { "menu", "gamedata:" },
52 | { "obj", "gamedata:" },
53 | { "objhq", "hqobj:" },
54 | { "parts", "gamedata:" },
55 | { "partshq", "hqparts:" },
56 | { "text", "gamedata:" }
57 | };
58 |
59 | ///
60 | /// 1. gparam:/m_template.gparam.dcx
61 | /// 2. data1:/param/drawparam/m_template.gparam.dcx
62 | /// 3. /param/drawparam/m_template.gparam.dcx
63 | ///
64 | private static readonly Dictionary SubstitutionMapDs3 = new Dictionary
65 | {
66 | { "cap_breakobj", "capture:/breakobj" },
67 | { "cap_dbgsaveload", "capture:/dbgsaveload" },
68 | { "cap_debugmenu", "capture:/debugmenu" },
69 | { "cap_entryfilelist", "capture:/entryfilelist" },
70 | { "cap_envmap", "capture:/envmap" },
71 | { "cap_report", "capture:/fdp_report" },
72 | { "cap_gparam", "capture:/gparam" },
73 | { "cap_havok", "capture:/havok" },
74 | { "cap_log", "capture:/log" },
75 | { "cap_mapstudio", "capture:/mapstudio" },
76 | { "cap_memdump", "capture:/memdump" },
77 | { "cap_param", "capture:/param" },
78 | { "cap_screenshot", "capture:/screenshot" },
79 |
80 | { "title", "data1:/" },
81 | { "event", "data1:/event" },
82 | { "facegen", "data1:/facegen" },
83 | { "font", "data1:/font" },
84 | { "menu", "data1:/menu" },
85 | { "menuesd_dlc", "data1:/menu" },
86 | { "menutexture", "data1:/menu" },
87 | { "movie", "data1:/movie" },
88 | { "msg", "data1:/msg" },
89 | { "mtd", "data1:/mtd" },
90 | { "other", "data1:/other" },
91 | { "param", "data1:/param" },
92 | { "gparam", "data1:/param/drawparam" },
93 | { "regulation", "data1:/param/regulation" },
94 | { "paramdef", "data1:/paramdef" },
95 | { "remo", "data1:/remo" },
96 | { "aiscript", "data1:/script" },
97 | { "luascriptpatch", "data1:/script" },
98 | { "script", "data1:/script" },
99 | { "talkscript", "data1:/script/talk" },
100 | { "patch_sfxbnd", "data1:/sfx" },
101 | { "sfx", "data1:/sfx" },
102 | { "sfxbnd", "data1:/sfx" },
103 | { "shader", "data1:/shader" },
104 | { "fmod", "data1:/sound" },
105 | { "sndchr", "data1:/sound" },
106 | { "sound", "data1:/sound" },
107 | { "stayparamdef", "data1:/stayparamdef" },
108 | { "testdata", "data1:/testdata" },
109 |
110 | { "parts", "data2:/parts" },
111 |
112 | { "action", "data3:/action" },
113 | { "actscript", "data3:/action/script" },
114 | { "chr", "data3:/chr" },
115 | { "chranibnd", "data3:/chr" },
116 | { "chranibnd_dlc", "data3:/chr" },
117 | { "chrbnd", "data3:/chr" },
118 | { "chresd", "data3:/chr" },
119 | { "chresdpatch", "data3:/chr" },
120 | { "chrtpf", "data3:/chr" },
121 |
122 | { "obj", "data4:/obj" },
123 | { "objbnd", "data4:/obj" },
124 |
125 | { "map", "data5:/map" },
126 | { "maphkx", "data5:/map" },
127 | { "maptpf", "data5:/map" },
128 | { "patch_maptpf", "data5:/map" },
129 | { "breakobj", "data5:/map/breakobj" },
130 | { "entryfilelist", "data5:/map/entryfilelist" },
131 | { "mapstudio", "data5:/map/mapstudio" },
132 | { "onav", "data5:/map/onav" },
133 | { "sndmap", "data5:/sound" },
134 | { "sndremo", "data5:/sound" },
135 |
136 | { "adhoc", "debugdata:/adhoc" }
137 | };
138 |
139 | private readonly Dictionary>> _dictionary;
140 | private readonly Dictionary _substitutionMap;
141 | private readonly string[] _physicalRoots;
142 |
143 | public FileNameDictionary(GameVersion version)
144 | {
145 | _dictionary = new Dictionary>>();
146 |
147 | string[] physicalRoots;
148 | Dictionary substitutionMap;
149 | switch (version)
150 | {
151 | case GameVersion.DarkSouls2:
152 | substitutionMap = SubstitutionMapDs2;
153 | physicalRoots = new string[0];
154 | break;
155 | case GameVersion.DarkSouls3:
156 | substitutionMap = SubstitutionMapDs3;
157 | physicalRoots = PhysicalRootsDs3;
158 | break;
159 | default:
160 | substitutionMap = new Dictionary();
161 | physicalRoots = new string[0];
162 | break;
163 | }
164 | _substitutionMap = substitutionMap;
165 | _physicalRoots = physicalRoots;
166 |
167 | }
168 |
169 | public bool TryGetFileName(ulong hash, string archiveName, out string fileName)
170 | {
171 | fileName = "";
172 | Dictionary> archiveDictionary;
173 | if (_dictionary.TryGetValue(archiveName, out archiveDictionary))
174 | {
175 | List fileNames;
176 | if (archiveDictionary.TryGetValue(hash, out fileNames))
177 | {
178 | if (fileNames.Count > 1)
179 | {
180 | return false;
181 | }
182 |
183 | fileName = NormalizeFileName(fileNames.First());
184 | return true;
185 | }
186 | }
187 |
188 | return false;
189 | }
190 |
191 | public bool TryGetFileName(ulong hash, string archiveName, string extension, out string fileName)
192 | {
193 | fileName = "";
194 | Dictionary> archiveDictionary;
195 | if (_dictionary.TryGetValue(archiveName, out archiveDictionary))
196 | {
197 | List fileNames;
198 | if (archiveDictionary.TryGetValue(hash, out fileNames))
199 | {
200 | //if (fileNames.Count > 1)
201 | //{
202 | // Debug.WriteLine($"Hashcollision: {hash}\t{archiveName}\t{fileNames.Count}\t{string.Join("\t", fileNames)}");
203 | //}
204 |
205 | fileName = fileNames.FirstOrDefault(e => e.EndsWith(extension)) ?? fileNames.First();
206 | fileName = NormalizeFileName(fileName);
207 | return true;
208 | }
209 | }
210 |
211 | return false;
212 | }
213 |
214 | private bool TrySplitFileName(string file, out string archiveName, out string fileName)
215 | {
216 | archiveName = null;
217 | fileName = null;
218 |
219 | int i = file.IndexOf(":/", StringComparison.Ordinal);
220 | if (i == -1)
221 | {
222 | return false;
223 | }
224 |
225 | archiveName = file.Substring(0, i);
226 | fileName = file.Substring(i + 2, file.Length - i - 2);
227 |
228 | return true;
229 | }
230 |
231 | private void Add(string file)
232 | {
233 | string archiveName;
234 | string fileName;
235 | if (!TrySplitFileName(file, out archiveName, out fileName))
236 | {
237 | return;
238 | }
239 |
240 | string substitutionArchiveName;
241 | if (!_substitutionMap.TryGetValue(archiveName, out substitutionArchiveName))
242 | {
243 | if (!_physicalRoots.Contains(archiveName))
244 | {
245 | return;
246 | }
247 |
248 | substitutionArchiveName = archiveName + ":";
249 | }
250 |
251 | file = substitutionArchiveName + "/" + fileName;
252 | if (!TrySplitFileName(file, out archiveName, out fileName))
253 | {
254 | return;
255 | }
256 |
257 | string hashablePath = "/" + fileName;
258 | uint hash = GetHashCode(hashablePath);
259 |
260 | Dictionary> archiveDictionary;
261 | if (_dictionary.TryGetValue(archiveName, out archiveDictionary) == false)
262 | {
263 | archiveDictionary = new Dictionary>();
264 | _dictionary.Add(archiveName, archiveDictionary);
265 | }
266 |
267 | List fileNameList;
268 | if (archiveDictionary.TryGetValue(hash, out fileNameList) == false)
269 | {
270 | fileNameList = new List();
271 | archiveDictionary.Add(hash, fileNameList);
272 | }
273 |
274 | if (fileNameList.Contains(fileName) == false)
275 | {
276 | fileNameList.Add(fileName);
277 | }
278 | }
279 |
280 | public static FileNameDictionary OpenFromFile(GameVersion version)
281 | {
282 | string dictionaryDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) ?? string.Empty;
283 | string dictionaryName = "Dictionary.csv";
284 | switch (version)
285 | {
286 | case GameVersion.DarkSouls2:
287 | dictionaryName = "DictionaryDS2.csv";
288 | break;
289 | case GameVersion.DarkSouls3:
290 | dictionaryName = "DictionaryDS3.csv";
291 | break;
292 | }
293 | string dictionaryPath = Path.Combine(dictionaryDirectory, dictionaryName);
294 | return OpenFromFile(dictionaryPath, version);
295 | }
296 |
297 | public static FileNameDictionary OpenFromFile(string dictionaryPath, GameVersion version)
298 | {
299 | var dictionary = new FileNameDictionary(version);
300 |
301 | string[] lines = File.ReadAllLines(dictionaryPath);
302 | foreach (string line in lines)
303 | {
304 | dictionary.Add(line);
305 | }
306 |
307 | return dictionary;
308 | }
309 |
310 | private static uint GetHashCode(string filePath, uint prime = 37u)
311 | {
312 | if (string.IsNullOrEmpty(filePath))
313 | return 0u;
314 | return filePath.Replace('\\', '/')
315 | .ToLowerInvariant()
316 | .Aggregate(0u, (i, c) => i * prime + c);
317 | }
318 |
319 | public static string NormalizeFileName(string fileName)
320 | {
321 | foreach (var virtualRoot in VirtualRoots)
322 | {
323 | if (fileName.StartsWith(virtualRoot))
324 | {
325 | fileName = fileName.Substring(virtualRoot.Length);
326 | break;
327 | }
328 | }
329 |
330 | return fileName.Replace('/', '\\').TrimStart('\\');
331 | }
332 | }
333 | }
--------------------------------------------------------------------------------
/BinderTool/FileType.cs:
--------------------------------------------------------------------------------
1 | namespace BinderTool
2 | {
3 | internal enum FileType
4 | {
5 | Unknown,
6 | Regulation,
7 | Dcx,
8 | EncryptedBdt,
9 | EncryptedBhd,
10 | Bdt,
11 | Bhd,
12 | Bnd,
13 | Savegame,
14 | Tpf,
15 | Param,
16 | Fmg
17 | }
18 | }
--------------------------------------------------------------------------------
/BinderTool/Options.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text.RegularExpressions;
4 | using BinderTool.Core;
5 |
6 | namespace BinderTool
7 | {
8 | internal class Options
9 | {
10 | public GameVersion InputGameVersion { get; set; }
11 |
12 | public FileType InputType { get; private set; }
13 |
14 | public string InputPath { get; private set; }
15 |
16 | public string OutputPath { get; private set; }
17 |
18 | internal static Options Parse(string[] args)
19 | {
20 | Options options = new Options();
21 | if (args.Length == 0)
22 | {
23 | throw new FormatException("Missing arguments");
24 | }
25 |
26 | options.InputPath = args[0];
27 | if (File.Exists(options.InputPath) == false)
28 | {
29 | throw new FormatException("Input file not found");
30 | }
31 |
32 | (FileType type, GameVersion version) fileType = GetFileType(Path.GetFileName(options.InputPath));
33 | options.InputType = fileType.type;
34 | options.InputGameVersion = fileType.version;
35 |
36 | if (options.InputType == FileType.Unknown)
37 | {
38 | throw new FormatException("Unsupported input file format");
39 | }
40 |
41 | if (args.Length >= 2)
42 | {
43 | options.OutputPath = args[1];
44 | }
45 | else
46 | {
47 | options.OutputPath = Path.Combine(
48 | Path.GetDirectoryName(options.InputPath),
49 | Path.GetFileNameWithoutExtension(options.InputPath));
50 | switch (options.InputType)
51 | {
52 | case FileType.EncryptedBhd:
53 | options.OutputPath += "_decrypted.bhd";
54 | break;
55 | case FileType.Dcx:
56 | case FileType.Fmg:
57 | break;
58 | }
59 | }
60 |
61 | return options;
62 | }
63 |
64 | private static (FileType, GameVersion) GetFileType(string fileName)
65 | {
66 | if (fileName == null)
67 | {
68 | throw new ArgumentNullException(nameof(fileName));
69 | }
70 |
71 | if (fileName == "Data0.bdt")
72 | {
73 | return (FileType.Regulation, GameVersion.DarkSouls3);
74 | }
75 |
76 | if (fileName == "enc_regulation.bnd.dcx")
77 | {
78 | return (FileType.Regulation, GameVersion.DarkSouls2);
79 | }
80 |
81 | // file.dcx
82 | // file.bnd.dcx
83 | if (fileName.EndsWith(".dcx", StringComparison.InvariantCultureIgnoreCase))
84 | {
85 | return (FileType.Dcx, GameVersion.Common);
86 | }
87 |
88 | // .anibnd
89 | // .chrbnd
90 | // .chrtpfbhd
91 | // .mtdbnd
92 | // .shaderbnd
93 | // .objbnd
94 | // .partsbnd
95 | // .rumblebnd
96 | // .hkxbhd
97 | // .tpfbhd
98 | // .shaderbdle
99 | // .shaderbdledebug
100 | if (fileName.EndsWith("bnd", StringComparison.InvariantCultureIgnoreCase)
101 | || fileName.EndsWith("bdle", StringComparison.InvariantCultureIgnoreCase)
102 | || fileName.EndsWith("bdledebug", StringComparison.InvariantCultureIgnoreCase))
103 | {
104 | return (FileType.Bnd, GameVersion.Common);
105 | }
106 |
107 | // DS30000.sl2
108 | if (Regex.IsMatch(fileName, @"^DS3\d+.*\.sl2", RegexOptions.IgnoreCase))
109 | {
110 | return (FileType.Savegame, GameVersion.DarkSouls3);
111 | }
112 |
113 | // DARKSII0000.sl2
114 | if (Regex.IsMatch(fileName, @"^DARKSII\d+.*\.sl2", RegexOptions.IgnoreCase))
115 | {
116 | return (FileType.Savegame, GameVersion.DarkSouls2);
117 | }
118 |
119 | if (Regex.IsMatch(fileName, @"^(?:Data|DLC)\d\.bdt$", RegexOptions.IgnoreCase))
120 | {
121 | return (FileType.EncryptedBdt, GameVersion.DarkSouls3);
122 | }
123 |
124 | if (Regex.IsMatch(fileName, @"^[^\W_]+Ebl\.bdt$", RegexOptions.IgnoreCase))
125 | {
126 | return (FileType.EncryptedBdt, GameVersion.DarkSouls2);
127 | }
128 |
129 | if (Regex.IsMatch(fileName, @"^(?:Data|DLC|)\d\.bhd$", RegexOptions.IgnoreCase))
130 | {
131 | return (FileType.EncryptedBhd, GameVersion.DarkSouls3);
132 | }
133 |
134 | if (Regex.IsMatch(fileName, @"^[^\W_]+Ebl\.bhd$", RegexOptions.IgnoreCase))
135 | {
136 | return (FileType.EncryptedBhd, GameVersion.DarkSouls2);
137 | }
138 |
139 | // file.bdt
140 | // file.hkxbdt
141 | // file.tpfbdt
142 | if (fileName.EndsWith("bdt", StringComparison.InvariantCultureIgnoreCase))
143 | {
144 | return (FileType.Bdt, GameVersion.Common);
145 | }
146 |
147 | // file.bhd
148 | // file.hkxbhd
149 | // file.tpfbhd
150 | if (fileName.EndsWith("bhd", StringComparison.InvariantCultureIgnoreCase))
151 | {
152 | return (FileType.Bhd, GameVersion.Common);
153 | }
154 |
155 | if (fileName.EndsWith(".tpf", StringComparison.InvariantCultureIgnoreCase))
156 | {
157 | return (FileType.Tpf, GameVersion.Common);
158 | }
159 |
160 | if (fileName.EndsWith(".param", StringComparison.InvariantCultureIgnoreCase))
161 | {
162 | return (FileType.Param, GameVersion.Common);
163 | }
164 |
165 | if (fileName.EndsWith(".fmg", StringComparison.InvariantCultureIgnoreCase))
166 | {
167 | return (FileType.Fmg, GameVersion.Common);
168 | }
169 |
170 | return (FileType.Unknown, GameVersion.Common);
171 | }
172 | }
173 | }
--------------------------------------------------------------------------------
/BinderTool/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using System.Text;
5 | using System.Text.RegularExpressions;
6 | using BinderTool.Core;
7 | using BinderTool.Core.Bdf4;
8 | using BinderTool.Core.Bdt5;
9 | using BinderTool.Core.Bhd5;
10 | using BinderTool.Core.Bhf4;
11 | using BinderTool.Core.Bnd4;
12 | using BinderTool.Core.Dcx;
13 | using BinderTool.Core.Enc;
14 | using BinderTool.Core.Fmg;
15 | using BinderTool.Core.Param;
16 | using BinderTool.Core.Sl2;
17 | using BinderTool.Core.Tpf;
18 |
19 | namespace BinderTool
20 | {
21 | public static class Program
22 | {
23 | private static void Main(string[] args)
24 | {
25 | if (args.Length == 0)
26 | {
27 | ShowUsageInfo();
28 | return;
29 | }
30 |
31 | Options options;
32 | try
33 | {
34 | options = Options.Parse(args);
35 | }
36 | catch (FormatException e)
37 | {
38 | Console.WriteLine(e.Message);
39 | ShowUsageInfo();
40 | return;
41 | }
42 |
43 | switch (options.InputType)
44 | {
45 | // These files have a single output file.
46 | case FileType.EncryptedBhd:
47 | case FileType.Bhd:
48 | case FileType.Dcx:
49 | case FileType.Fmg:
50 | break;
51 | default:
52 | Directory.CreateDirectory(options.OutputPath);
53 | break;
54 | }
55 |
56 | switch (options.InputType)
57 | {
58 | case FileType.Regulation:
59 | UnpackRegulationFile(options);
60 | break;
61 | case FileType.Dcx:
62 | UnpackDcxFile(options);
63 | break;
64 | case FileType.EncryptedBdt:
65 | UnpackBdtFile(options);
66 | break;
67 | case FileType.EncryptedBhd:
68 | UnpackBhdFile(options);
69 | break;
70 | case FileType.Bdt:
71 | UnpackBdf4File(options);
72 | break;
73 | case FileType.Bhd:
74 | UnpackBhf4File(options);
75 | break;
76 | case FileType.Bnd:
77 | UnpackBndFile(options);
78 | break;
79 | case FileType.Savegame:
80 | UnpackSl2File(options);
81 | break;
82 | case FileType.Tpf:
83 | UnpackTpfFile(options);
84 | break;
85 | case FileType.Param:
86 | UnpackParamFile(options);
87 | break;
88 | case FileType.Fmg:
89 | UnpackFmgFile(options);
90 | break;
91 | default:
92 | throw new ArgumentOutOfRangeException($"Unable to handle type '{options.InputType}'");
93 | }
94 | }
95 |
96 | private static void ShowUsageInfo()
97 | {
98 | Console.WriteLine(
99 | "BinderTool by Atvaark\n" +
100 | " A tool for unpacking Dark Souls II/III/Bloodborne Bdt, Bhd, Dcx, Sl2, Tpf, Param and Fmg files\n" +
101 | "Usage:\n" +
102 | " BinderTool file_path [output_path]\n" +
103 | "Examples:\n" +
104 | " BinderTool data1.bdt\n" +
105 | " BinderTool data1.bdt data1");
106 | }
107 |
108 | private static void UnpackBdtFile(Options options)
109 | {
110 | FileNameDictionary dictionary = FileNameDictionary.OpenFromFile(options.InputGameVersion);
111 | string fileNameWithoutExtension = Path.GetFileName(options.InputPath).Replace("Ebl.bdt", "").Replace(".bdt", "");
112 | string archiveName = fileNameWithoutExtension.ToLower();
113 |
114 | using (Bdt5FileStream bdtStream = Bdt5FileStream.OpenFile(options.InputPath, FileMode.Open, FileAccess.Read))
115 | {
116 | Bhd5File bhdFile = Bhd5File.Read(
117 | inputStream: DecryptBhdFile(
118 | filePath: Path.ChangeExtension(options.InputPath, "bhd"),
119 | version: options.InputGameVersion),
120 | version: options.InputGameVersion
121 | );
122 | foreach (var bucket in bhdFile.GetBuckets())
123 | {
124 | foreach (var entry in bucket.GetEntries())
125 | {
126 | MemoryStream data;
127 | if (entry.FileSize == 0)
128 | {
129 | long fileSize;
130 | if (!TryReadFileSize(entry, bdtStream, out fileSize))
131 | {
132 | Console.WriteLine($"Unable to determine the length of file '{entry.FileNameHash:D10}'");
133 | continue;
134 | }
135 |
136 | entry.FileSize = fileSize;
137 | }
138 |
139 | if (entry.IsEncrypted)
140 | {
141 | data = bdtStream.Read(entry.FileOffset, entry.PaddedFileSize);
142 | CryptographyUtility.DecryptAesEcb(data, entry.AesKey.Key, entry.AesKey.Ranges);
143 | data.Position = 0;
144 | data.SetLength(entry.FileSize);
145 | }
146 | else
147 | {
148 | data = bdtStream.Read(entry.FileOffset, entry.FileSize);
149 | }
150 |
151 | string fileName;
152 | string dataExtension = GetDataExtension(data);
153 | bool fileNameFound = dictionary.TryGetFileName(entry.FileNameHash, archiveName, out fileName);
154 | if (!fileNameFound)
155 | {
156 | fileNameFound = dictionary.TryGetFileName(entry.FileNameHash, archiveName, dataExtension, out fileName);
157 | }
158 |
159 | string extension;
160 | if (fileNameFound)
161 | {
162 | extension = Path.GetExtension(fileName);
163 |
164 | if (dataExtension == ".dcx" && extension != ".dcx")
165 | {
166 | extension = ".dcx";
167 | fileName += ".dcx";
168 | }
169 | }
170 | else
171 | {
172 | extension = dataExtension;
173 | fileName = $"{entry.FileNameHash:D10}_{fileNameWithoutExtension}{extension}";
174 | }
175 |
176 | if (extension == ".enc")
177 | {
178 | byte[] decryptionKey;
179 | if (DecryptionKeys.TryGetAesFileKey(Path.GetFileName(fileName), out decryptionKey))
180 | {
181 | EncFile encFile = EncFile.ReadEncFile(data, decryptionKey);
182 | data = encFile.Data;
183 |
184 | fileName = Path.Combine(Path.GetDirectoryName(fileName), Path.GetFileNameWithoutExtension(fileName));
185 | extension = Path.GetExtension(fileName);
186 | }
187 | else
188 | {
189 | Debug.WriteLine($"No decryption key for file \'{fileName}\' found.");
190 | }
191 | }
192 |
193 | if (extension == ".dcx")
194 | {
195 | DcxFile dcxFile = DcxFile.Read(data);
196 | data = new MemoryStream(dcxFile.Decompress());
197 |
198 | fileName = Path.Combine(Path.GetDirectoryName(fileName), Path.GetFileNameWithoutExtension(fileName));
199 |
200 | if (fileNameFound)
201 | {
202 | extension = Path.GetExtension(fileName);
203 | }
204 | else
205 | {
206 | extension = GetDataExtension(data);
207 | fileName += extension;
208 | }
209 | }
210 |
211 | Debug.WriteLine(
212 | "{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}\t{7}\t{8}",
213 | fileNameWithoutExtension,
214 | fileName,
215 | extension,
216 | entry.FileNameHash,
217 | entry.FileOffset,
218 | entry.FileSize,
219 | entry.PaddedFileSize,
220 | entry.IsEncrypted,
221 | fileNameFound);
222 |
223 | string newFileNamePath = Path.Combine(options.OutputPath, fileName);
224 | Directory.CreateDirectory(Path.GetDirectoryName(newFileNamePath));
225 | File.WriteAllBytes(newFileNamePath, data.ToArray());
226 | }
227 | }
228 | }
229 | }
230 |
231 | private static bool TryReadFileSize(Bhd5BucketEntry entry, Bdt5FileStream bdtStream, out long fileSize)
232 | {
233 | fileSize = 0;
234 |
235 | const int sampleLength = 48;
236 | MemoryStream data = bdtStream.Read(entry.FileOffset, sampleLength);
237 |
238 | if (entry.IsEncrypted)
239 | {
240 | data = CryptographyUtility.DecryptAesEcb(data, entry.AesKey.Key);
241 | }
242 |
243 | string sampleSignature;
244 | if (!TryGetAsciiSignature(data, 4, out sampleSignature)
245 | || sampleSignature != DcxFile.DcxSignature)
246 | {
247 | return false;
248 | }
249 |
250 | fileSize = DcxFile.DcxSize + DcxFile.ReadCompressedSize(data);
251 | return true;
252 | }
253 |
254 | private static string GetDataExtension(MemoryStream data)
255 | {
256 | string signature;
257 | string extension;
258 |
259 | if (TryGetAsciiSignature(data, 4, out signature)
260 | && TryGetFileExtension(signature, out extension))
261 | {
262 | return extension;
263 | }
264 |
265 | if (TryGetUnicodeSignature(data, 4, out signature)
266 | && TryGetFileExtension(signature, out extension))
267 | {
268 | return extension;
269 | }
270 |
271 | if (TryGetAsciiSignature(data, 26, out signature)
272 | && TryGetFileExtension(signature.Substring(12, 14), out extension))
273 | {
274 | return extension;
275 | }
276 |
277 | //Debug.WriteLine($"Unknown signature: '{BitConverter.ToString(Encoding.ASCII.GetBytes(signature)).Replace("-", " ")}'");
278 | return ".bin";
279 | }
280 |
281 | private static bool TryGetAsciiSignature(MemoryStream stream, int signatureLength, out string signature)
282 | {
283 | const int asciiBytesPerChar = 1;
284 | return TryGetSignature(stream, Encoding.ASCII, asciiBytesPerChar, signatureLength, out signature);
285 | }
286 |
287 | private static bool TryGetUnicodeSignature(MemoryStream stream, int signatureLength, out string signature)
288 | {
289 | const int unicodeBytesPerChar = 2;
290 | return TryGetSignature(stream, Encoding.Unicode, unicodeBytesPerChar, signatureLength, out signature);
291 | }
292 |
293 | private static bool TryGetSignature(MemoryStream stream, Encoding encoding, int bytesPerChar, int signatureLength, out string signature)
294 | {
295 | signature = null;
296 |
297 | long startPosition = stream.Position;
298 | if (stream.Length - startPosition < bytesPerChar * signatureLength)
299 | {
300 | return false;
301 | }
302 |
303 | BinaryReader reader = new BinaryReader(stream, encoding, true);
304 | signature = new string(reader.ReadChars(signatureLength));
305 | stream.Position = startPosition;
306 |
307 | return true;
308 | }
309 |
310 | private static bool TryGetFileExtension(string signature, out string extension)
311 | {
312 | switch (signature)
313 | {
314 | case "BND4":
315 | extension = ".bnd";
316 | return true;
317 | case "BHF4":
318 | extension = ".bhd";
319 | return true;
320 | case "BDF4":
321 | extension = ".bdt";
322 | return true;
323 | case "DCX\0":
324 | extension = ".dcx";
325 | return true;
326 | case "DDS ":
327 | extension = ".dds";
328 | return true;
329 | case "TAE ":
330 | extension = ".tae";
331 | return true;
332 | case "FSB5":
333 | extension = ".fsb";
334 | return true;
335 | case "fsSL":
336 | case "fSSL":
337 | extension = ".esd";
338 | return true;
339 | case "TPF\0":
340 | extension = ".tpf";
341 | return true;
342 | case "PFBB":
343 | extension = ".pfbbin";
344 | return true;
345 | case "OBJB":
346 | extension = ".breakobj";
347 | return true;
348 | case "filt":
349 | extension = ".fltparam"; // DS II
350 | //extension = ".gparam"; // DS III
351 | return true;
352 | case "VSDF":
353 | extension = ".vsd";
354 | return true;
355 | case "NVG2":
356 | extension = ".ngp";
357 | return true;
358 | case "#BOM":
359 | extension = ".txt";
360 | return true;
361 | case "\x1BLua":
362 | extension = ".lua"; // or .hks
363 | return true;
364 | case "RIFF":
365 | extension = ".fev";
366 | return true;
367 | case "GFX\v":
368 | extension = ".gfx";
369 | return true;
370 | case "SMD\0":
371 | extension = ".metaparam";
372 | return true;
373 | case "SMDD":
374 | extension = ".metadebug";
375 | return true;
376 | case "CLM2":
377 | extension = ".clm2";
378 | return true;
379 | case "FLVE":
380 | extension = ".flver";
381 | return true;
382 | case "F2TR":
383 | extension = ".flver2tri";
384 | return true;
385 | case "FRTR":
386 | extension = ".tri";
387 | return true;
388 | case "FXR\0":
389 | extension = ".fxr";
390 | return true;
391 | case "ITLIMITER_INFO":
392 | extension = ".itl";
393 | return true;
394 | case "EVD\0":
395 | extension = ".emevd";
396 | return true;
397 | case "ENFL":
398 | extension = ".entryfilelist";
399 | return true;
400 | case "NVMA":
401 | extension = ".nvma"; // ?
402 | return true;
403 | case "MSB ":
404 | extension = ".msb"; // ?
405 | return true;
406 | case "BJBO":
407 | extension = ".bjbo"; // ?
408 | return true;
409 | case "ONAV":
410 | extension = ".onav"; // ?
411 | return true;
412 | default:
413 | extension = ".bin";
414 | return false;
415 | }
416 | }
417 |
418 | private static void UnpackBhdFile(Options options)
419 | {
420 | using (var inputStream = DecryptBhdFile(options.InputPath, options.InputGameVersion))
421 | using (var outputStream = File.OpenWrite(options.OutputPath))
422 | {
423 | inputStream.WriteTo(outputStream);
424 | }
425 | }
426 |
427 | private static void UnpackBndFile(Options options)
428 | {
429 | using (FileStream inputStream = new FileStream(options.InputPath, FileMode.Open, FileAccess.Read))
430 | {
431 | UnpackBndFile(inputStream, options.OutputPath);
432 | }
433 | }
434 |
435 | private static void UnpackBndFile(Stream inputStream, string outputPath)
436 | {
437 | Bnd4File file = Bnd4File.ReadBnd4File(inputStream);
438 |
439 | foreach (var entry in file.Entries)
440 | {
441 | string fileName = FileNameDictionary.NormalizeFileName(entry.FileName);
442 | string outputFilePath = Path.Combine(outputPath, fileName);
443 |
444 | Directory.CreateDirectory(Path.GetDirectoryName(outputFilePath));
445 | File.WriteAllBytes(outputFilePath, entry.EntryData);
446 | }
447 | }
448 |
449 | private static void UnpackSl2File(Options options)
450 | {
451 | using (FileStream inputStream = new FileStream(options.InputPath, FileMode.Open, FileAccess.Read))
452 | {
453 | byte[] key = GetSavegameKey(options.InputGameVersion);
454 | Sl2File sl2File = Sl2File.ReadSl2File(inputStream, key);
455 | foreach (var userData in sl2File.UserData)
456 | {
457 | string outputFilePath = Path.Combine(options.OutputPath, userData.Name);
458 | File.WriteAllBytes(outputFilePath, userData.DecryptedUserData);
459 | }
460 | }
461 | }
462 |
463 | private static byte[] GetSavegameKey(GameVersion version)
464 | {
465 | byte[] key;
466 | switch (version)
467 | {
468 | case GameVersion.DarkSouls2:
469 | key = DecryptionKeys.UserDataKeyDs2;
470 | break;
471 | case GameVersion.DarkSouls3:
472 | key = DecryptionKeys.UserDataKeyDs3;
473 | break;
474 | default:
475 | key = new byte[16];
476 | break;
477 | }
478 |
479 | return key;
480 | }
481 |
482 | private static void UnpackRegulationFile(Options options)
483 | {
484 | using (FileStream inputStream = new FileStream(options.InputPath, FileMode.Open, FileAccess.Read))
485 | {
486 | byte[] key = GetRegulationKey(options.InputGameVersion);
487 | EncFile encryptedFile = EncFile.ReadEncFile(inputStream, key, options.InputGameVersion);
488 | DcxFile compressedRegulationFile = DcxFile.Read(encryptedFile.Data);
489 | UnpackBndFile(new MemoryStream(compressedRegulationFile.Decompress()), options.OutputPath);
490 | }
491 | }
492 |
493 | private static byte[] GetRegulationKey(GameVersion version)
494 | {
495 | byte[] key;
496 | switch (version)
497 | {
498 | case GameVersion.DarkSouls2:
499 | key = DecryptionKeys.RegulationFileKeyDs2;
500 | break;
501 | case GameVersion.DarkSouls3:
502 | key = DecryptionKeys.RegulationFileKeyDs3;
503 | break;
504 | default:
505 | key = new byte[16];
506 | break;
507 | }
508 |
509 | return key;
510 | }
511 |
512 | private static void UnpackDcxFile(Options options)
513 | {
514 | string unpackedFileName = Path.GetFileNameWithoutExtension(options.InputPath);
515 | string outputFilePath = options.OutputPath;
516 | bool hasExtension = Path.GetExtension(unpackedFileName) != "";
517 |
518 | using (FileStream inputStream = new FileStream(options.InputPath, FileMode.Open, FileAccess.Read))
519 | {
520 | DcxFile dcxFile = DcxFile.Read(inputStream);
521 | byte[] decompressedData = dcxFile.Decompress();
522 |
523 | if (!hasExtension)
524 | {
525 | string extension = GetDataExtension(new MemoryStream(decompressedData));
526 | if (extension != ".dcx")
527 | {
528 | outputFilePath += extension;
529 | }
530 | }
531 |
532 | File.WriteAllBytes(outputFilePath, decompressedData);
533 | }
534 | }
535 |
536 | private static void UnpackBdf4File(Options options)
537 | {
538 | string bdfDirectoryPath = Path.GetDirectoryName(options.InputPath);
539 | string bhf4Extension = Path.GetExtension(options.InputPath).Replace("bdt", "bhd");
540 | string bhf4FilePath = Path.Combine(bdfDirectoryPath, Path.GetFileNameWithoutExtension(options.InputPath) + bhf4Extension);
541 | if (!File.Exists(bhf4FilePath))
542 | {
543 | // HACK: Adding 132 to a hash of a text that ends with XXX.bdt will give you the hash of XXX.bhd.
544 | string[] split = Path.GetFileNameWithoutExtension(options.InputPath).Split('_');
545 | uint hash;
546 | if (uint.TryParse(split[0], out hash))
547 | {
548 | hash += 132;
549 | split[0] = hash.ToString("D10");
550 | bhf4FilePath = Path.Combine(bdfDirectoryPath, string.Join("_", split) + ".bhd");
551 | }
552 | }
553 |
554 | using (Bdf4FileStream bdf4InputStream = Bdf4FileStream.OpenFile(options.InputPath, FileMode.Open, FileAccess.Read))
555 | {
556 | Bhf4File bhf4File = Bhf4File.OpenBhf4File(bhf4FilePath);
557 | foreach (var entry in bhf4File.Entries)
558 | {
559 | MemoryStream data = bdf4InputStream.Read(entry.FileOffset, entry.FileSize);
560 |
561 | string fileName = entry.FileName;
562 | string fileExtension = Path.GetExtension(fileName);
563 | if (fileExtension == ".dcx")
564 | {
565 | DcxFile dcxFile = DcxFile.Read(data);
566 | data = new MemoryStream(dcxFile.Decompress());
567 | fileName = Path.Combine(Path.GetDirectoryName(fileName), Path.GetFileNameWithoutExtension(fileName));
568 | }
569 |
570 | string outputFilePath = Path.Combine(options.OutputPath, fileName);
571 | Directory.CreateDirectory(Path.GetDirectoryName(outputFilePath));
572 | File.WriteAllBytes(outputFilePath, data.ToArray());
573 | }
574 | }
575 | }
576 |
577 | private static void UnpackTpfFile(Options options)
578 | {
579 | using (FileStream inputStream = new FileStream(options.InputPath, FileMode.Open, FileAccess.Read))
580 | {
581 | TpfFile tpfFile = TpfFile.OpenTpfFile(inputStream);
582 | foreach (var entry in tpfFile.Entries)
583 | {
584 | string outputFilePath = Path.Combine(options.OutputPath, entry.FileName);
585 | Directory.CreateDirectory(Path.GetDirectoryName(outputFilePath));
586 | using (var outputStream = File.Create(outputFilePath))
587 | {
588 | entry.Write(outputStream);
589 | }
590 | }
591 | }
592 | }
593 |
594 | private static void UnpackBhf4File(Options options)
595 | {
596 | Console.WriteLine($"The file : \'{options.InputPath}\' is already decrypted.");
597 | }
598 |
599 | private static MemoryStream DecryptBhdFile(string filePath, GameVersion version)
600 | {
601 | string fileDirectory = Path.GetDirectoryName(filePath) ?? string.Empty;
602 | string fileName = Path.GetFileName(filePath) ?? string.Empty;
603 | string key = null;
604 | switch (version)
605 | {
606 | case GameVersion.DarkSouls2:
607 | string keyFileName = Regex.Replace(fileName, @"Ebl\.bhd$", "KeyCode.pem", RegexOptions.IgnoreCase);
608 | string keyFilePath = Path.Combine(fileDirectory, keyFileName);
609 | if (File.Exists(keyFilePath))
610 | {
611 | key = File.ReadAllText(keyFilePath);
612 | }
613 | break;
614 | case GameVersion.DarkSouls3:
615 | DecryptionKeys.TryGetRsaFileKey(fileName, out key);
616 | break;
617 | }
618 |
619 | if (key == null)
620 | {
621 | throw new ApplicationException($"Missing decryption key for file \'{fileName}\'");
622 | }
623 |
624 | return CryptographyUtility.DecryptRsa(filePath, key);
625 | }
626 |
627 | private static void UnpackParamFile(Options options)
628 | {
629 | using (FileStream inputStream = new FileStream(options.InputPath, FileMode.Open, FileAccess.Read))
630 | {
631 | ParamFile paramFile = ParamFile.ReadParamFile(inputStream);
632 | foreach (var entry in paramFile.Entries)
633 | {
634 | string entryName = $"{entry.Id:D10}.{paramFile.StructName}";
635 | string outputFilePath = Path.Combine(options.OutputPath, entryName);
636 | Directory.CreateDirectory(Path.GetDirectoryName(outputFilePath));
637 | File.WriteAllBytes(outputFilePath, entry.Data);
638 | }
639 | }
640 | }
641 |
642 | private static void UnpackFmgFile(Options options)
643 | {
644 | using (FileStream inputStream = new FileStream(options.InputPath, FileMode.Open, FileAccess.Read))
645 | {
646 | FmgFile fmgFile = FmgFile.ReadFmgFile(inputStream);
647 |
648 | StringBuilder builder = new StringBuilder();
649 | foreach (var entry in fmgFile.Entries)
650 | {
651 | string value = entry.Value
652 | .Replace("\r", "\\r")
653 | .Replace("\n", "\\n")
654 | .Replace("\t", "\\t");
655 | builder.AppendLine($"{entry.Id}\t{value}");
656 | }
657 |
658 | string outputPath = options.OutputPath + ".txt";
659 | File.WriteAllText(outputPath, builder.ToString());
660 | }
661 | }
662 | }
663 | }
664 |
--------------------------------------------------------------------------------
/BinderTool/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | [assembly: AssemblyTitle("BinderTool")]
5 | [assembly: AssemblyDescription("")]
6 | [assembly: AssemblyConfiguration("")]
7 | [assembly: AssemblyCompany("")]
8 | [assembly: AssemblyProduct("BinderTool")]
9 | [assembly: AssemblyCopyright("Copyright © 2018 Atvaark")]
10 | [assembly: AssemblyTrademark("")]
11 | [assembly: AssemblyCulture("")]
12 | [assembly: ComVisible(false)]
13 | [assembly: Guid("5d388695-6078-4404-8f5a-0bb1a94df97b")]
14 | [assembly: AssemblyVersion("0.5.2.0")]
15 | [assembly: AssemblyFileVersion("0.5.2.0")]
16 |
--------------------------------------------------------------------------------
/BinderTool/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Atvaark
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # BinderTool
2 | A Dark Souls II/III bdt, bhd, bnd, dcx and sl2 unpacking tool
3 |
4 | [](https://ci.appveyor.com/project/Atvaark/bindertool)
5 |
6 | Binaries can be downloaded under [releases](https://github.com/Atvaark/BinderTool/releases).
7 |
8 | ### DS III Dictionary Progress
9 |
10 | | archive | found names | total names | found percentage |
11 | | :--- | ---: | ---: | ---: |
12 | | data1 | 2067 | 2110 | 97,96% |
13 | | data2 | 2140 | 2140 | 100,00% |
14 | | data3 | 671 | 673 | 99,70% |
15 | | data4 | 951 | 951 | 100,00% |
16 | | data5 | 6301 | 6755 | 93,28% |
17 | | dlc1 | 0 | 775 | 0% |
18 | | dlc2 | 0 | 1264 | 0% |
19 | | **total** | **12130** | **14668** | **82%** |
20 |
21 | ## Requirements
22 | ```
23 | Microsoft .NET Framework 4.5.2
24 | ```
25 |
26 | ## Usage
27 | ```
28 | BinderTool input_file_path [output_folder_path]
29 | ```
30 | If no output folder path is specified then the files are unpacked in a folder called after the archive that is getting unpacked.
31 |
32 | ## Examples
33 |
34 | Unpacking an encrypted bdt file. This requires the corresponding .bhd files to be in the same folder.
35 | ```
36 | BinderTool Data1.bdt
37 | BinderTool DLC1.bdt
38 | ```
39 |
40 | Unpacking an unencrypted bdt file. This requires the corresponding bhd file to be in the same folder.
41 | ```
42 | BinderTool t10_23_00_00.tpfbdt
43 | ```
44 |
45 | Unpacking an encrypted bhd file. This will only work for files with known decryption keys such as Data1.bhd-Data5.bhd.
46 | ```
47 | BinderTool Data1.bhd
48 | BinderTool DLC1.bhd
49 | ```
50 |
51 | Unpacking a bnd file
52 | ```
53 | BinderTool c0001.bnd
54 | ```
55 |
56 | Unpacking a dcx file
57 | ```
58 | BinderTool 01.febnd.dcx
59 | ```
60 |
--------------------------------------------------------------------------------