private void Write(BinaryWriterEx bhdWriter, BinaryWriterEx bdtWriter) { BHD.Write(bhdWriter, Files); for (int i = 0; i < Files.Count; i++) { File file = Files[i]; bdtWriter.Pad(0x10); byte[] bytes = file.Bytes; if (file.Flags == 0x03 || file.Flags == 0xC0) { bytes = DCX.Compress(bytes, DCX.Type.DarkSouls1); } if (BHD.Format == 0x3E) { bhdWriter.FillUInt64($"FileOffset{i}", (ulong)bdtWriter.Position); } else { bhdWriter.FillUInt32($"FileOffset{i}", (uint)bdtWriter.Position); } bhdWriter.FillInt64($"FileSize{i}", bytes.LongLength); bdtWriter.WriteBytes(bytes); } }
internal Texture(BinaryReaderEx br, TPFPlatform platform, byte encoding) { int fileOffset = br.ReadInt32(); int fileSize = br.ReadInt32(); Format = br.ReadByte(); Cubemap = br.ReadBoolean(); Mipmaps = br.ReadByte(); Flags1 = br.AssertByte(0, 1, 2, 3); int nameOffset = 0; if (platform == TPFPlatform.PC) { Header = null; nameOffset = br.ReadInt32(); Flags2 = br.AssertInt32(0, 1); } else if (platform == TPFPlatform.PS3) { Header = new TexHeader(); Header.Width = br.ReadInt16(); Header.Height = br.ReadInt16(); Header.Unk1 = br.ReadInt32(); Header.Unk2 = br.AssertInt32(0, 0xAAE4); nameOffset = br.ReadInt32(); Flags2 = br.AssertInt32(0, 1); } else if (platform == TPFPlatform.PS4 || platform == TPFPlatform.Xbone) { Header = new TexHeader(); Header.Width = br.ReadInt16(); Header.Height = br.ReadInt16(); Header.TextureCount = br.AssertByte(1, 6); br.AssertByte(0); br.AssertByte(0); br.AssertByte(0); Header.Unk2 = br.AssertInt32(0xD); nameOffset = br.ReadInt32(); Flags2 = br.AssertInt32(0, 1); Header.DXGIFormat = br.ReadInt32(); } Bytes = br.GetBytes(fileOffset, fileSize); if (Flags1 == 2 || Flags1 == 3) { Bytes = DCX.Decompress(Bytes); } if (encoding == 1) { Name = br.GetUTF16(nameOffset); } else if (encoding == 0 || encoding == 2) { Name = br.GetShiftJIS(nameOffset); } }
internal File(BinaryReaderEx br, BHF4.FileHeader fileHeader) { Name = fileHeader.Name; Flags = fileHeader.Flags; ID = fileHeader.ID; Bytes = br.GetBytes((long)fileHeader.Offset, (int)fileHeader.CompressedSize); if (Flags == 0x03 || Flags == 0xC0) { Bytes = DCX.Decompress(Bytes); } }
/// <summary> /// Writes TPF data to a BinaryWriterEx. /// </summary> internal override void Write(BinaryWriterEx bw) { bw.BigEndian = false; bw.WriteASCII("TPF\0"); bw.ReserveInt32("DataSize"); bw.WriteInt32(Textures.Count); bw.WriteByte((byte)Platform); bw.WriteByte(Flag2); bw.WriteByte(Encoding); bw.WriteByte(0); for (int i = 0; i < Textures.Count; i++) { Textures[i].Write(bw, i, Platform); } bw.Pad(0x10); for (int i = 0; i < Textures.Count; i++) { Texture texture = Textures[i]; bw.FillInt32($"FileName{i}", (int)bw.Position); if (Encoding == 1) { bw.WriteUTF16(texture.Name, true); } else if (Encoding == 0 || Encoding == 2) { bw.WriteShiftJIS(texture.Name, true); } } int dataStart = (int)bw.Position; for (int i = 0; i < Textures.Count; i++) { Texture texture = Textures[i]; if (texture.Bytes.Length > 0) { bw.Pad(0x10); } bw.FillInt32($"FileData{i}", (int)bw.Position); byte[] bytes = texture.Bytes; if (texture.Flags1 == 2 || texture.Flags2 == 3) { bytes = DCX.Compress(bytes, DCX.Type.ACEREDGE); } bw.FillInt32($"FileSize{i}", bytes.Length); bw.WriteBytes(bytes); } bw.FillInt32("DataSize", (int)bw.Position - dataStart); }
/// <summary> /// Decompresses data and returns a new BinaryReaderEx if necessary. /// </summary> public static BinaryReaderEx GetDecompressedBR(BinaryReaderEx br, out DCX.Type compression) { if (DCX.Is(br)) { byte[] bytes = DCX.Decompress(br, out compression); return(new BinaryReaderEx(false, bytes)); } else { compression = DCX.Type.None; return(br); } }
internal void WriteData(BinaryWriterEx bw, int index) { bw.FillUInt32($"FileData{index}", (uint)bw.Position); byte[] bytes = Bytes; if (Flags1 == 2 || Flags1 == 3) { bytes = DCX.Compress(bytes, DCX.Type.DCP_EDGE); } bw.FillInt32($"FileSize{index}", bytes.Length); bw.WriteBytes(bytes); }
/// <summary> /// Writes file data to a BinaryWriterEx, compressing it afterwards if specified. /// </summary> private void Write(BinaryWriterEx bw, DCX.Type compression) { if (compression == DCX.Type.None) { Write(bw); } else { BinaryWriterEx bwUncompressed = new BinaryWriterEx(false); Write(bwUncompressed); byte[] uncompressed = bwUncompressed.FinishBytes(); DCX.Compress(uncompressed, bw, compression); } }
internal BinderFile ReadFileData(BinaryReaderEx br) { byte[] bytes; DCX.Type compressionType = DCX.Type.Zlib; if (IsCompressed(Flags)) { bytes = br.GetBytes(DataOffset, (int)CompressedSize); bytes = DCX.Decompress(bytes, out compressionType); } else { bytes = br.GetBytes(DataOffset, (int)CompressedSize); } return(new BinderFile(Flags, ID, Name, bytes) { CompressionType = compressionType, }); }
private void WriteFileData(BinaryWriterEx bw, byte[] bytes) { if (bytes.LongLength > 0) { bw.Pad(0x10); } DataOffset = bw.Position; UncompressedSize = bytes.LongLength; if (IsCompressed(Flags)) { byte[] compressed = DCX.Compress(bytes, CompressionType); CompressedSize = compressed.LongLength; bw.WriteBytes(compressed); } else { CompressedSize = bytes.LongLength; bw.WriteBytes(bytes); } }
/// <summary> /// Guesses the extension of a file based on its contents. /// </summary> public static string GuessExtension(byte[] bytes, bool bigEndian = false) { bool dcx = false; if (DCX.Is(bytes)) { dcx = true; bytes = DCX.Decompress(bytes); } bool checkMsb(BinaryReaderEx br) { if (br.Length < 8) { return(false); } int offset = br.GetInt32(4); if (offset < 0 || offset >= br.Length - 1) { return(false); } try { return(br.GetASCII(offset) == "MODEL_PARAM_ST"); } catch { return(false); } } bool checkParam(BinaryReaderEx br) { if (br.Length < 0x2C) { return(false); } string param = br.GetASCII(0xC, 0x20); return(Regex.IsMatch(param, "^[^\0]+\0 *$")); } bool checkTdf(BinaryReaderEx br) { if (br.Length < 4) { return(false); } if (br.GetASCII(0, 1) != "\"") { return(false); } for (int i = 1; i < br.Length; i++) { if (br.GetASCII(i, 1) == "\"") { return(i < br.Length - 2 && br.GetASCII(i + 1, 2) == "\r\n"); } } return(false); } string ext = ""; using (var ms = new MemoryStream(bytes)) { var br = new BinaryReaderEx(bigEndian, ms); string magic = null; if (br.Length >= 4) { magic = br.ReadASCII(4); } if (magic == "AISD") { ext = ".aisd"; } else if (magic == "BDF3" || magic == "BDF4") { ext = ".bdt"; } else if (magic == "BHF3" || magic == "BHF4") { ext = ".bhd"; } else if (magic == "BND3" || magic == "BND4") { ext = ".bnd"; } else if (magic == "DDS ") { ext = ".dds"; } // ESD or FFX else if (magic != null && magic.ToUpper() == "DLSE") { ext = ".dlse"; } else if (bigEndian && magic == "\0BRD" || !bigEndian && magic == "DRB\0") { ext = ".drb"; } else if (magic == "EDF\0") { ext = ".edf"; } else if (magic == "ELD\0") { ext = ".eld"; } else if (magic == "ENFL") { ext = ".entryfilelist"; } else if (magic != null && magic.ToUpper() == "FSSL") { ext = ".esd"; } else if (magic == "EVD\0") { ext = ".evd"; } else if (br.Length >= 3 && br.GetASCII(0, 3) == "FEV" || br.Length >= 0x10 && br.GetASCII(8, 8) == "FEV FMT ") { ext = ".fev"; } else if (br.Length >= 6 && br.GetASCII(0, 6) == "FLVER\0") { ext = ".flver"; } else if (br.Length >= 3 && br.GetASCII(0, 3) == "FSB") { ext = ".fsb"; } else if (br.Length >= 3 && br.GetASCII(0, 3) == "GFX") { ext = ".gfx"; } else if (br.Length >= 0x19 && br.GetASCII(0xC, 0xE) == "ITLIMITER_INFO") { ext = ".itl"; } else if (br.Length >= 4 && br.GetASCII(1, 3) == "Lua") { ext = ".lua"; } else if (checkMsb(br)) { ext = ".msb"; } else if (br.Length >= 0x30 && br.GetASCII(0x2C, 4) == "MTD ") { ext = ".mtd"; } else if (magic == "DFPN") { ext = ".nfd"; } else if (checkParam(br)) { ext = ".param"; } else if (br.Length >= 4 && br.GetASCII(1, 3) == "PNG") { ext = ".png"; } else if (br.Length >= 0x2C && br.GetASCII(0x28, 4) == "SIB ") { ext = ".sib"; } else if (magic == "TAE ") { ext = ".tae"; } else if (checkTdf(br)) { ext = ".tdf"; } else if (magic == "TPF\0") { ext = ".tpf"; } else if (magic == "#BOM") { ext = ".txt"; } else if (br.Length >= 5 && br.GetASCII(0, 5) == "<?xml") { ext = ".xml"; } // This is pretty sketchy else if (br.Length >= 0xC && br.GetByte(0) == 0 && br.GetByte(3) == 0 && br.GetInt32(4) == br.Length && br.GetInt16(0xA) == 0) { ext = ".fmg"; } } if (dcx) { return(ext + ".dcx"); } else { return(ext); } }
private static BinderFile ReadFile(BinaryReaderEx br, bool unicode, Binder.Format format) { Binder.FileFlags flags = br.ReadEnum8 <Binder.FileFlags>(); br.AssertByte(0); br.AssertByte(0); br.AssertByte(0); br.AssertInt32(-1); long compressedSize = br.ReadInt64(); if (Binder.HasUncompressedSize(format)) { long uncompressedSize = br.ReadInt64(); } uint fileOffset = br.ReadUInt32(); int id = -1; if (Binder.HasID(format)) { id = br.ReadInt32(); } string name = null; if (Binder.HasName(format)) { uint nameOffset = br.ReadUInt32(); if (unicode) { name = br.GetUTF16(nameOffset); } else { name = br.GetShiftJIS(nameOffset); } } if (format == Binder.Format.x20) { br.AssertInt64(0); } byte[] bytes; if (Binder.IsCompressed(flags)) { if (format == Binder.Format.x2E) { bytes = br.GetBytes(fileOffset, (int)compressedSize); bytes = DCX.Decompress(bytes, out DCX.Type type); if (type != DCX.Type.DemonsSoulsEDGE) { throw null; } } else { br.StepIn(fileOffset); bytes = SFUtil.ReadZlib(br, (int)compressedSize); br.StepOut(); } } else { bytes = br.GetBytes(fileOffset, (int)compressedSize); } return(new BinderFile(flags, id, name, bytes)); }
/// <summary> /// Writes BND4 data to a BinaryWriterEx. /// </summary> internal override void Write(BinaryWriterEx bw) { bw.BigEndian = BigEndian; bw.WriteASCII("BND4"); bw.WriteBoolean(Flag1); bw.WriteBoolean(Flag2); bw.WriteByte(0); bw.WriteByte(0); bw.WriteInt32(0x10000); bw.WriteInt32(Files.Count); bw.WriteInt64(0x40); bw.WriteFixStr(Timestamp, 8); bw.WriteInt64(Binder.FileHeaderSize(Format)); bw.ReserveInt64("DataStart"); bw.WriteBoolean(Unicode); bw.WriteByte((byte)Format); bw.WriteByte(Extended); bw.WriteByte(0); bw.WriteInt32(0); if (Extended == 4) { bw.ReserveInt64("HashGroups"); } else { bw.WriteInt64(0); } for (int i = 0; i < Files.Count; i++) { WriteFile(Files[i], bw, i, Format); } if (Binder.HasName(Format)) { for (int i = 0; i < Files.Count; i++) { BinderFile file = Files[i]; bw.FillUInt32($"FileName{i}", (uint)bw.Position); if (Unicode) { bw.WriteUTF16(file.Name, true); } else { bw.WriteShiftJIS(file.Name, true); } } } if (Extended == 4) { uint groupCount = 0; for (uint p = (uint)Files.Count / 7; p <= 100000; p++) { if (SFUtil.IsPrime(p)) { groupCount = p; break; } } if (groupCount == 0) { throw new InvalidOperationException("Hash group count not determined in BND4."); } var hashLists = new List <PathHash> [groupCount]; for (int i = 0; i < groupCount; i++) { hashLists[i] = new List <PathHash>(); } for (int i = 0; i < Files.Count; i++) { var pathHash = new PathHash(i, Files[i].Name); uint group = pathHash.Hash % groupCount; hashLists[group].Add(pathHash); } for (int i = 0; i < groupCount; i++) { hashLists[i].Sort((ph1, ph2) => ph1.Hash.CompareTo(ph2.Hash)); } var hashGroups = new List <HashGroup>(); var pathHashes = new List <PathHash>(); int count = 0; foreach (List <PathHash> hashList in hashLists) { int index = count; foreach (PathHash pathHash in hashList) { pathHashes.Add(pathHash); count++; } hashGroups.Add(new HashGroup(index, count - index)); } bw.Pad(0x8); bw.FillInt64("HashGroups", bw.Position); bw.ReserveInt64("PathHashes"); bw.WriteUInt32(groupCount); bw.WriteInt32(0x00080810); foreach (HashGroup hashGroup in hashGroups) { hashGroup.Write(bw); } // No padding after section 1 bw.FillInt64("PathHashes", bw.Position); foreach (PathHash pathHash in pathHashes) { pathHash.Write(bw); } } bw.FillInt64("DataStart", bw.Position); for (int i = 0; i < Files.Count; i++) { BinderFile file = Files[i]; if (file.Bytes.LongLength > 0) { bw.Pad(0x10); } bw.FillUInt32($"FileData{i}", (uint)bw.Position); int compressedSize = file.Bytes.Length; if (Binder.IsCompressed(file.Flags)) { if (Format == Binder.Format.x2E) { byte[] bytes = DCX.Compress(file.Bytes, DCX.Type.DemonsSoulsEDGE); bw.WriteBytes(bytes); compressedSize = bytes.Length; } else { compressedSize = SFUtil.WriteZlib(bw, 0x9C, file.Bytes); } } else { bw.WriteBytes(file.Bytes); } bw.FillInt64($"CompressedSize{i}", compressedSize); } }
internal Texture(BinaryReaderEx br, TPFPlatform platform, byte flag2, byte encoding) { uint fileOffset = br.ReadUInt32(); int fileSize = br.ReadInt32(); Format = br.ReadByte(); Type = br.ReadEnum8 <TexType>(); Mipmaps = br.ReadByte(); Flags1 = br.AssertByte(0, 1, 2, 3); if (platform != TPFPlatform.PC) { Header = new TexHeader(); Header.Width = br.ReadInt16(); Header.Height = br.ReadInt16(); if (platform == TPFPlatform.Xbox360) { br.AssertInt32(0); } else if (platform == TPFPlatform.PS3) { Header.Unk1 = br.ReadInt32(); if (flag2 != 0) { Header.Unk2 = br.AssertInt32(0, 0x69E0, 0xAAE4); } } else if (platform == TPFPlatform.PS4 || platform == TPFPlatform.Xbone) { Header.TextureCount = br.AssertInt32(1, 6); Header.Unk2 = br.AssertInt32(0xD); } } uint nameOffset = br.ReadUInt32(); bool hasFloatStruct = br.AssertInt32(0, 1) == 1; if (platform == TPFPlatform.PS4 || platform == TPFPlatform.Xbone) { Header.DXGIFormat = br.ReadInt32(); } if (hasFloatStruct) { FloatStruct = new FloatStruct(br); } Bytes = br.GetBytes(fileOffset, fileSize); if (Flags1 == 2 || Flags1 == 3) { Bytes = DCX.Decompress(Bytes, out DCX.Type type); if (type != DCX.Type.DCP_EDGE) { throw new NotImplementedException($"TPF compression is expected to be DCP_EDGE, but it was {type}"); } } if (encoding == 1) { Name = br.GetUTF16(nameOffset); } else if (encoding == 0 || encoding == 2) { Name = br.GetShiftJIS(nameOffset); } }
internal Texture(BinaryReaderEx br, TPFPlatform platform, byte flag2, byte encoding) { uint fileOffset = br.ReadUInt32(); int fileSize = br.ReadInt32(); Format = br.ReadByte(); Type = br.ReadEnum8 <TexType>(); Mipmaps = br.ReadByte(); Flags1 = br.AssertByte(0, 1, 2, 3); uint nameOffset = uint.MaxValue; if (platform == TPFPlatform.PC) { Header = null; nameOffset = br.ReadUInt32(); Flags2 = br.AssertInt32(0, 1); } else { Header = new TexHeader(); Header.Width = br.ReadInt16(); Header.Height = br.ReadInt16(); if (platform == TPFPlatform.Xbox360) { br.AssertInt32(0); nameOffset = br.ReadUInt32(); br.AssertInt32(0); } else if (platform == TPFPlatform.PS3) { Header.Unk1 = br.ReadInt32(); if (flag2 != 0) { Header.Unk2 = br.AssertInt32(0, 0xAAE4); } nameOffset = br.ReadUInt32(); Flags2 = br.AssertInt32(0, 1); if (Flags2 == 1) { Unk20 = br.ReadInt32(); Unk24 = br.ReadInt32(); Unk28 = br.ReadSingle(); } } else if (platform == TPFPlatform.PS4 || platform == TPFPlatform.Xbone) { Header.TextureCount = br.AssertInt32(1, 6); Header.Unk2 = br.AssertInt32(0xD); nameOffset = br.ReadUInt32(); Flags2 = br.AssertInt32(0, 1); Header.DXGIFormat = br.ReadInt32(); } } Bytes = br.GetBytes(fileOffset, fileSize); if (Flags1 == 2 || Flags1 == 3) { Bytes = DCX.Decompress(Bytes); } if (encoding == 1) { Name = br.GetUTF16(nameOffset); } else if (encoding == 0 || encoding == 2) { Name = br.GetShiftJIS(nameOffset); } }