├── .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 | [![Build status](https://ci.appveyor.com/api/projects/status/t6tf7uuggto1827a?svg=true)](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 | --------------------------------------------------------------------------------