private static void WriteEncodeBody(BinaryReader br, BinaryWriter bw, PsbStreamContext context, PsbHeader header) { bw.Write ( context.Encode ( br.ReadBytes((int)(header.OffsetChunkOffsets - header.OffsetNames)) ) ); WriteToEnd(br, bw); }
public static PsbHeader Load(BinaryReader br, uint key) { PsbHeader header = new PsbHeader { Signature = br.ReadChars(4), Version = br.ReadUInt16(), HeaderEncrypt = br.ReadUInt16() }; var sig = new string(header.Signature).ToUpperInvariant(); if (sig.StartsWith("MDF")) { throw new PsbBadFormatException(PsbBadFormatReason.IsMdf, "Maybe a MDF file"); } if (!sig.StartsWith("PSB")) { throw new PsbBadFormatException(PsbBadFormatReason.Header, "Not a valid PSB file"); } PsbStreamContext context = new PsbStreamContext(key); header.HeaderLength = context.ReadUInt32(br); header.OffsetNames = context.ReadUInt32(br); header.OffsetStrings = context.ReadUInt32(br); header.OffsetStringsData = context.ReadUInt32(br); header.OffsetChunkOffsets = context.ReadUInt32(br); header.OffsetChunkLengths = context.ReadUInt32(br); header.OffsetChunkData = context.ReadUInt32(br); header.OffsetEntries = context.ReadUInt32(br); if (header.Version > 2) { header.Checksum = context.ReadUInt32(br); } if (header.Version > 3) { header.OffsetExtraChunkOffsets = context.ReadUInt32(br); header.OffsetExtraChunkLengths = context.ReadUInt32(br); header.OffsetExtraChunkData = context.ReadUInt32(br); } return(header); }
public static PsbHeader Load(BinaryReader br, uint key) { PsbHeader header = new PsbHeader { Signature = br.ReadChars(4), Version = br.ReadUInt16(), HeaderEncrypt = br.ReadUInt16() }; if (!new string(header.Signature).ToUpperInvariant().StartsWith("PSB")) { throw new BadImageFormatException("Not a valid PSB file"); } PsbStreamContext context = new PsbStreamContext(key); header.HeaderLength = context.ReadUInt32(br); header.OffsetNames = context.ReadUInt32(br); header.OffsetStrings = context.ReadUInt32(br); header.OffsetStringsData = context.ReadUInt32(br); header.OffsetChunkOffsets = context.ReadUInt32(br); header.OffsetChunkLengths = context.ReadUInt32(br); header.OffsetChunkData = context.ReadUInt32(br); header.OffsetEntries = context.ReadUInt32(br); if (header.Version > 2) { header.Checksum = context.ReadUInt32(br); } if (header.Version > 3) { header.OffsetUnknown1 = context.ReadUInt32(br); header.OffsetUnknown2 = context.ReadUInt32(br); header.OffsetResourceOffsets = context.ReadUInt32(br); } return(header); }
/// <summary> /// Encode (Encrypt/Decrypt) PSB file /// </summary> /// <param name="key"></param> /// <param name="mode"></param> /// <param name="position"></param> /// <param name="input"></param> /// <param name="output"></param> /// <returns>Unencrypted Header for reference. Usually you shouldn't use it.</returns> public static PsbHeader Encode(uint key, EncodeMode mode, EncodePosition position, Stream input, Stream output) { input.Seek(0, SeekOrigin.Begin); output.Seek(0, SeekOrigin.Begin); PsbHeader header = new PsbHeader(); PsbStreamContext context = new PsbStreamContext(key); BinaryReader br = new BinaryReader(input); BinaryWriter bw = new BinaryWriter(output); header.Signature = br.ReadChars(4); header.Version = br.ReadUInt16(); bw.Write(header.Signature); //Signature bw.Write(header.Version); //Version header.HeaderEncrypt = br.ReadUInt16(); //headerEncrypt, sometimes we don't believe it when encoding header.HeaderLength = br.ReadUInt32(); header.OffsetNames = br.ReadUInt32(); br.BaseStream.Seek(-8, SeekOrigin.Current); void WriteOriginal() { bw.Write(header.HeaderEncrypt); WriteOriginalPartialHeader(br, bw, header); WriteOriginalBody(br, bw); } switch (position) { case EncodePosition.Auto: bool headerEnc = TestHeaderEncrypted(br.BaseStream, header); bool bodyEnc = TestBodyEncrypted(br, header); if (headerEnc && bodyEnc) //MARK: is this possible? { mode = EncodeMode.Decrypt; } if (!headerEnc && !bodyEnc) { mode = EncodeMode.Encrypt; } switch (mode) { case EncodeMode.Encrypt: if (header.Version > 2) //Header Encrypted; Body Clean { bw.Write((ushort)1); if (headerEnc) { WriteOriginalPartialHeader(br, bw, header); WriteOriginalBody(br, bw); break; } WriteEncryptPartialHeader(br, bw, context, header); WriteOriginalBody(br, bw); break; } else //Header Clean; Body Encrpyted { bw.Write((ushort)0); // if (headerEnc) { WriteDecryptPartialHeader(br, bw, context, header); context = new PsbStreamContext(key); WriteEncodeBody(br, bw, context, header); } else { WriteOriginalPartialHeader(br, bw, header); WriteEncodeBody(br, bw, context, header); } } break; case EncodeMode.Decrypt: bw.Write((ushort)0); // if (headerEnc) { WriteDecryptPartialHeader(br, bw, context, header); } else { WriteOriginalPartialHeader(br, bw, header); } if (bodyEnc) { WriteEncodeBody(br, bw, context, header); } else { WriteOriginalBody(br, bw); } break; default: WriteOriginal(); break; } break; case EncodePosition.Body: switch (mode) { case EncodeMode.Encrypt: case EncodeMode.Decrypt: bw.Write(header.HeaderEncrypt); //We believe file is clean so write original header but encrypt body WriteOriginalPartialHeader(br, bw, header); WriteEncodeBody(br, bw, context, header); break; default: WriteOriginal(); break; } break; case EncodePosition.Header: switch (mode) { case EncodeMode.Encrypt: bw.Write((ushort)1); WriteEncryptPartialHeader(br, bw, context, header); WriteOriginalBody(br, bw); break; case EncodeMode.Decrypt: bw.Write((ushort)0); // WriteDecryptPartialHeader(br, bw, context, header); WriteOriginalBody(br, bw); break; default: WriteOriginal(); break; } break; case EncodePosition.Full: switch (mode) { case EncodeMode.Encrypt: bw.Write((ushort)1); WriteEncryptPartialHeader(br, bw, context, header); WriteEncodeBody(br, bw, context, header); break; case EncodeMode.Decrypt: bw.Write((ushort)1); // WriteDecryptPartialHeader(br, bw, context, header); WriteEncodeBody(br, bw, context, header); break; default: WriteOriginal(); break; } break; default: WriteOriginal(); break; } bw.Flush(); output.Seek(0, SeekOrigin.Begin); return(header); }
/// <summary> /// Assume header is clean /// </summary> /// <param name="br"></param> /// <param name="bw"></param> /// <param name="context"></param> /// <param name="header"></param> private static void WriteEncryptPartialHeader(BinaryReader br, BinaryWriter bw, PsbStreamContext context, PsbHeader header) { var checksumStartPosition = br.BaseStream.Position; header.HeaderLength = br.ReadUInt32(); header.OffsetNames = br.ReadUInt32(); if (header.HeaderLength == 0) { header.HeaderLength = header.OffsetNames; } header.OffsetStrings = br.ReadUInt32(); header.OffsetStringsData = br.ReadUInt32(); header.OffsetChunkOffsets = br.ReadUInt32(); header.OffsetChunkLengths = br.ReadUInt32(); header.OffsetChunkData = br.ReadUInt32(); header.OffsetEntries = br.ReadUInt32(); if (header.Version > 2) { header.Checksum = br.ReadUInt32(); } if (header.Version > 3) { header.OffsetUnknownOffsets = br.ReadUInt32(); header.OffsetUnknownLengths = br.ReadUInt32(); header.OffsetUnknownData = br.ReadUInt32(); } var checksumEndPosition = br.BaseStream.Position; context.Write(header.HeaderLength, bw); context.Write(header.OffsetNames, bw); context.Write(header.OffsetStrings, bw); context.Write(header.OffsetStringsData, bw); context.Write(header.OffsetChunkOffsets, bw); context.Write(header.OffsetChunkLengths, bw); context.Write(header.OffsetChunkData, bw); context.Write(header.OffsetEntries, bw); if (header.Version > 2) { int checkLength = 32; br.BaseStream.Seek(checksumStartPosition, SeekOrigin.Begin); var checkBuffer = new byte[checkLength]; br.BaseStream.Read(checkBuffer, 0, checkLength); br.BaseStream.Seek(checksumEndPosition, SeekOrigin.Begin); //Jump back Adler32 adler32 = new Adler32(); adler32.Update(checkBuffer); header.Checksum = (uint)adler32.Checksum; if (header.Version == 3) { context.Write(header.Checksum, bw); } else //PSBv4 { checkBuffer = BitConverter.GetBytes(header.OffsetUnknownOffsets) .Concat(BitConverter.GetBytes(header.OffsetUnknownLengths)) .Concat(BitConverter.GetBytes(header.OffsetUnknownData)).ToArray(); adler32.Update(checkBuffer); header.Checksum = (uint)adler32.Checksum; context.Write(header.Checksum, bw); context.Write(header.OffsetUnknownOffsets, bw); context.Write(header.OffsetUnknownLengths, bw); context.Write(header.OffsetUnknownData, bw); } } }
/// <summary> /// Assume header is encrypted /// </summary> /// <param name="br"></param> /// <param name="bw"></param> /// <param name="context"></param> /// <param name="header"></param> private static void WriteDecryptPartialHeader(BinaryReader br, BinaryWriter bw, PsbStreamContext context, PsbHeader header) { header.HeaderLength = context.ReadUInt32(br); header.OffsetNames = context.ReadUInt32(br); header.OffsetStrings = context.ReadUInt32(br); header.OffsetStringsData = context.ReadUInt32(br); header.OffsetChunkOffsets = context.ReadUInt32(br); header.OffsetChunkLengths = context.ReadUInt32(br); header.OffsetChunkData = context.ReadUInt32(br); header.OffsetEntries = context.ReadUInt32(br); if (header.Version > 2) { header.Checksum = context.ReadUInt32(br); } if (header.Version > 3) { header.OffsetUnknownOffsets = context.ReadUInt32(br); header.OffsetUnknownLengths = context.ReadUInt32(br); header.OffsetUnknownData = context.ReadUInt32(br); } //var checksumStartPosition = bw.BaseStream.Position; bw.Write(header.HeaderLength); bw.Write(header.OffsetNames); bw.Write(header.OffsetStrings); bw.Write(header.OffsetStringsData); bw.Write(header.OffsetChunkOffsets); bw.Write(header.OffsetChunkLengths); bw.Write(header.OffsetChunkData); bw.Write(header.OffsetEntries); //var checksumPosition = bw.BaseStream.Position; if (header.Version > 2) { int checkLength = 32; bw.BaseStream.Seek(-checkLength, SeekOrigin.Current); var checkBuffer = new byte[checkLength]; bw.BaseStream.Read(checkBuffer, 0, checkLength); Adler32 adler32 = new Adler32(); adler32.Update(checkBuffer); header.Checksum = (uint)adler32.Checksum; if (header.Version == 3) { bw.Write(header.Checksum); } else //PSBv4 { checkBuffer = BitConverter.GetBytes(header.OffsetUnknownOffsets) .Concat(BitConverter.GetBytes(header.OffsetUnknownLengths)) .Concat(BitConverter.GetBytes(header.OffsetUnknownData)).ToArray(); adler32.Update(checkBuffer); header.Checksum = (uint)adler32.Checksum; bw.Write(header.Checksum); bw.Write(header.OffsetUnknownOffsets); bw.Write(header.OffsetUnknownLengths); bw.Write(header.OffsetUnknownData); } } }
/// <summary> /// Encode a value and write using <see cref="BinaryWriter"/>. /// </summary> /// <param name="context"></param> /// <param name="value"></param> /// <param name="bw"></param> public static void Write(this PsbStreamContext context, ushort value, BinaryWriter bw) { bw.Write(context.Encode(BitConverter.GetBytes(value))); }
/// <summary> /// Read a <see cref="ushort"/> from <see cref="BinaryReader"/>, and then encode using <see cref="PsbStreamContext"/>. /// </summary> /// <param name="context"></param> /// <param name="br"></param> /// <returns></returns> public static ushort ReadUInt16(this PsbStreamContext context, BinaryReader br) { return(BitConverter.ToUInt16(context.Encode(br.ReadBytes(2)), 0)); }
/// <summary> /// Read bytes from <see cref="BinaryReader"/>, and then encode using <see cref="PsbStreamContext"/>. /// </summary> /// <param name="context"></param> /// <param name="br"></param> /// <param name="count"></param> /// <returns></returns> public static byte[] ReadBytes(this PsbStreamContext context, BinaryReader br, int count) { return(context.Encode(br.ReadBytes(count))); }