public void WriteUInt32BE_WithUInt32_WritesData() { using MemoryStream ms = new MemoryStream(new byte[4]); using BinaryWriter bw = new BinaryWriter(ms); EndianUtility.WriteUInt32BE(bw, 12345678u); ms.Seek(0, SeekOrigin.Begin); using BinaryReader br = new BinaryReader(ms, new ASCIIEncoding()); Assert.Equal(12345678u, EndianUtility.ReadUInt32BE(br)); }
public RpxFile(string rpxFilePath) { Console.WriteLine("Decompressing RPX file..."); path = rpxFilePath; decompressedPath = path + ".extract"; // Remove the temp file if it exists if (File.Exists(decompressedPath)) { File.Delete(decompressedPath); } crcDataOffset = 0; header = new RpxHeader(path); using (FileStream fs = new FileStream(decompressedPath, FileMode.OpenOrCreate, FileAccess.Write)) { using (BinaryWriter bw = new BinaryWriter(fs, new ASCIIEncoding())) { bw.Write(header.Identity); EndianUtility.WriteUInt16BE(bw, header.Type); EndianUtility.WriteUInt16BE(bw, header.Machine); EndianUtility.WriteUInt32BE(bw, header.Version); EndianUtility.WriteUInt32BE(bw, header.EntryPoint); EndianUtility.WriteUInt32BE(bw, header.PhOffset); EndianUtility.WriteUInt32BE(bw, header.SectionHeaderOffset); EndianUtility.WriteUInt32BE(bw, header.Flags); EndianUtility.WriteUInt16BE(bw, header.EhSize); EndianUtility.WriteUInt16BE(bw, header.PhEntSize); EndianUtility.WriteUInt16BE(bw, header.PhNum); EndianUtility.WriteUInt16BE(bw, header.ShEntSize); EndianUtility.WriteUInt16BE(bw, header.SectionHeaderCount); EndianUtility.WriteUInt16BE(bw, header.ShStrIndex); EndianUtility.WriteUInt32BE(bw, 0x00000000); EndianUtility.WriteUInt32BE(bw, 0x00000000); EndianUtility.WriteUInt32BE(bw, 0x00000000); while ((ulong)bw.BaseStream.Position < header.SectionHeaderDataElfOffset) { bw.Write((byte)0); } while (bw.BaseStream.Position % 0x40 != 0) { bw.Write((byte)0); header.SectionHeaderDataElfOffset++; } } } sectionHeaderIndices = new List <RpxSectionHeaderSort>(); sectionHeaders = new List <RpxSectionHeader>(header.SectionHeaderCount); crcs = new List <uint>(header.SectionHeaderCount); // TODO: Add debug flag with this output //Console.WriteLine(header.ToString()); using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read)) { using (BinaryReader br = new BinaryReader(fs, new ASCIIEncoding())) { // Seek to the Section Header Offset in the file br.BaseStream.Seek(header.SectionHeaderOffset, SeekOrigin.Begin); // Read in all of the section headers for (UInt32 i = 0; i < header.SectionHeaderCount; i++) { crcs.Add(0); // Read in the bytes for the section header byte[] buffer = br.ReadBytes(RpxSectionHeader.SECTION_HEADER_LENGTH); // Create a new section header and add it to the list RpxSectionHeader newSectionHeader = new RpxSectionHeader(buffer); sectionHeaders.Add(newSectionHeader); if (newSectionHeader.Offset != 0) { RpxSectionHeaderSort sectionHeaderIndex = new RpxSectionHeaderSort(); sectionHeaderIndex.index = i; sectionHeaderIndex.offset = newSectionHeader.Offset; sectionHeaderIndices.Add(sectionHeaderIndex); // TODO: Add debug flag with this output //Console.WriteLine(sectionHeaderIndex.ToString()); } // TODO: Add debug flag with this output //Console.WriteLine(newSectionHeader.ToString()); } } } sectionHeaderIndices.Sort(); // Iterate through all of the section header indices for (int i = 0; i < sectionHeaderIndices.Count; i++) { // Seek to the correct part of the file RpxSectionHeader currentSectionHeader = sectionHeaders[(int)sectionHeaderIndices[i].index]; UInt64 position = currentSectionHeader.Offset; using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read)) { using (BinaryReader br = new BinaryReader(fs, new ASCIIEncoding())) { br.BaseStream.Seek((long)position, SeekOrigin.Begin); currentSectionHeader.Offset = (uint)br.BaseStream.Position; if ((currentSectionHeader.Flags & RpxSectionHeader.SECTION_HEADER_RPL_ZLIB) == RpxSectionHeader.SECTION_HEADER_RPL_ZLIB) { UInt32 dataSize = currentSectionHeader.Size - 4; currentSectionHeader.Size = EndianUtility.ReadUInt32BE(br); UInt32 blockSize = RpxSectionHeader.CHUNK_SIZE; UInt32 have; byte[] bufferIn = new byte[RpxSectionHeader.CHUNK_SIZE]; byte[] bufferOut = new byte[RpxSectionHeader.CHUNK_SIZE]; ZlibCodec compressor = new ZlibCodec(); compressor.InitializeInflate(true); compressor.AvailableBytesIn = 0; compressor.NextIn = 0; while (dataSize > 0) { blockSize = RpxSectionHeader.CHUNK_SIZE; if (dataSize < blockSize) { blockSize = dataSize; } dataSize -= blockSize; bufferIn = br.ReadBytes((int)blockSize); compressor.NextIn = 0; compressor.InputBuffer = bufferIn; compressor.AvailableBytesIn = bufferIn.Length; compressor.OutputBuffer = bufferOut; do { compressor.AvailableBytesOut = (int)RpxSectionHeader.CHUNK_SIZE; compressor.NextOut = 0; compressor.Inflate(FlushType.None); have = RpxSectionHeader.CHUNK_SIZE - (uint)compressor.AvailableBytesOut; // write the data using (FileStream outFs = new FileStream(decompressedPath, FileMode.Append)) { using (BinaryWriter bw = new BinaryWriter(outFs, new ASCIIEncoding())) { bw.Write(bufferOut, 0, (int)have); } } crcs[(int)sectionHeaderIndices[i].index] = Crc32Rpx(crcs[(int)sectionHeaderIndices[i].index], bufferOut, have); } while (compressor.AvailableBytesOut == 0); currentSectionHeader.Flags &= ~RpxSectionHeader.SECTION_HEADER_RPL_ZLIB; } } else { UInt32 dataSize = currentSectionHeader.Size; UInt32 blockSize = RpxSectionHeader.CHUNK_SIZE; while (dataSize > 0) { byte[] data = new byte[RpxSectionHeader.CHUNK_SIZE]; blockSize = RpxSectionHeader.CHUNK_SIZE; if (dataSize < blockSize) { blockSize = dataSize; } dataSize -= blockSize; data = br.ReadBytes((int)blockSize); // Write out the section bytes using (FileStream outFs = new FileStream(decompressedPath, FileMode.Append)) { using (BinaryWriter bw = new BinaryWriter(outFs, new ASCIIEncoding())) { bw.Write(data); } } crcs[(int)sectionHeaderIndices[i].index] = Crc32Rpx(crcs[(int)sectionHeaderIndices[i].index], data, blockSize); } } // Pad out the section on a 0x40 byte boundary using (FileStream outFs = new FileStream(decompressedPath, FileMode.Append)) { using (BinaryWriter bw = new BinaryWriter(outFs, new ASCIIEncoding())) { while (bw.BaseStream.Position % 0x40 != 0) { bw.Write((byte)0); } } } if ((currentSectionHeader.Type & RpxSectionHeader.SECTION_HEADER_RPL_CRCS) == RpxSectionHeader.SECTION_HEADER_RPL_CRCS) { crcs[(int)sectionHeaderIndices[i].index] = 0; crcDataOffset = currentSectionHeader.Offset; } } } sectionHeaders[(int)sectionHeaderIndices[i].index] = currentSectionHeader; } // Fix the output headers // TODO: This is not currently accurate vs. wiiurpx tool so may need to investigate using (FileStream outFs = new FileStream(decompressedPath, FileMode.Open, FileAccess.Write)) { using (BinaryWriter bw = new BinaryWriter(outFs, new ASCIIEncoding())) { bw.Seek((int)header.SectionHeaderOffset, SeekOrigin.Begin); for (UInt32 i = 0; i < header.SectionHeaderCount; i++) { EndianUtility.WriteUInt32BE(bw, sectionHeaders[(int)i].Name); EndianUtility.WriteUInt32BE(bw, sectionHeaders[(int)i].Type); EndianUtility.WriteUInt32BE(bw, sectionHeaders[(int)i].Flags); EndianUtility.WriteUInt32BE(bw, sectionHeaders[(int)i].Address); EndianUtility.WriteUInt32BE(bw, sectionHeaders[(int)i].Offset); EndianUtility.WriteUInt32BE(bw, sectionHeaders[(int)i].Size); EndianUtility.WriteUInt32BE(bw, sectionHeaders[(int)i].Link); EndianUtility.WriteUInt32BE(bw, sectionHeaders[(int)i].Info); EndianUtility.WriteUInt32BE(bw, sectionHeaders[(int)i].AddrAlign); EndianUtility.WriteUInt32BE(bw, sectionHeaders[(int)i].EntSize); } using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read)) { using (BinaryReader br = new BinaryReader(fs, new ASCIIEncoding())) { // Seek to the Section Header Offset in the file br.BaseStream.Seek((long)crcDataOffset, SeekOrigin.Begin); for (UInt32 i = 0; i < header.SectionHeaderCount; i++) { EndianUtility.WriteUInt32BE(bw, crcs[(int)i]); } } } } } Console.WriteLine("Decompression complete."); }
/// <summary> /// Initializes a new instance of the <see cref="RpxFile"/> class. /// </summary> /// <param name="rpxFilePath">path to the RPX file.</param> /// <param name="verbose">whether to provide verbose output.</param> public RpxFile(string rpxFilePath, bool verbose = false) { Console.WriteLine("Decompressing RPX file..."); this.path = rpxFilePath; this.decompressedPath = this.path + ".extract"; // Remove the temp file if it exists if (File.Exists(this.decompressedPath)) { if (verbose) { Console.WriteLine("Removing file " + System.IO.Path.GetFullPath(this.decompressedPath)); } File.Delete(this.decompressedPath); } this.crcDataOffset = 0; this.header = new RpxHeader(this.path); using (FileStream fs = new FileStream(this.decompressedPath, FileMode.OpenOrCreate, FileAccess.Write)) { using BinaryWriter bw = new BinaryWriter(fs, new ASCIIEncoding()); bw.Write(this.header.Identity); EndianUtility.WriteUInt16BE(bw, this.header.Type); EndianUtility.WriteUInt16BE(bw, this.header.Machine); EndianUtility.WriteUInt32BE(bw, this.header.Version); EndianUtility.WriteUInt32BE(bw, this.header.EntryPoint); EndianUtility.WriteUInt32BE(bw, this.header.PhOffset); EndianUtility.WriteUInt32BE(bw, this.header.SectionHeaderOffset); EndianUtility.WriteUInt32BE(bw, this.header.Flags); EndianUtility.WriteUInt16BE(bw, this.header.EhSize); EndianUtility.WriteUInt16BE(bw, this.header.PhEntSize); EndianUtility.WriteUInt16BE(bw, this.header.PhNum); EndianUtility.WriteUInt16BE(bw, this.header.ShEntSize); EndianUtility.WriteUInt16BE(bw, this.header.SectionHeaderCount); EndianUtility.WriteUInt16BE(bw, this.header.ShStrIndex); EndianUtility.WriteUInt32BE(bw, 0x00000000); EndianUtility.WriteUInt32BE(bw, 0x00000000); EndianUtility.WriteUInt32BE(bw, 0x00000000); while ((ulong)bw.BaseStream.Position < this.header.SectionHeaderDataElfOffset) { bw.Write((byte)0); } while (bw.BaseStream.Position % 0x40 != 0) { bw.Write((byte)0); this.header.SectionHeaderDataElfOffset++; } } this.sectionHeaderIndices = new List <RpxSectionHeaderSort>(); this.sectionHeaders = new List <RpxSectionHeader>(this.header.SectionHeaderCount); this.crcs = new List <uint>(this.header.SectionHeaderCount); if (verbose) { Console.WriteLine(this.header.ToString()); } using (FileStream fs = new FileStream(this.path, FileMode.Open, FileAccess.Read)) { using BinaryReader br = new BinaryReader(fs, new ASCIIEncoding()); // Seek to the Section Header Offset in the file br.BaseStream.Seek(this.header.SectionHeaderOffset, SeekOrigin.Begin); // Read in all of the section headers for (uint i = 0; i < this.header.SectionHeaderCount; i++) { this.crcs.Add(0); // Read in the bytes for the section header byte[] buffer = br.ReadBytes(RpxSectionHeader.SectionHeaderLength); // Create a new section header and add it to the list RpxSectionHeader newSectionHeader = new RpxSectionHeader(buffer); this.sectionHeaders.Add(newSectionHeader); if (newSectionHeader.Offset != 0) { RpxSectionHeaderSort sectionHeaderIndex = new RpxSectionHeaderSort { Index = i, Offset = newSectionHeader.Offset, }; this.sectionHeaderIndices.Add(sectionHeaderIndex); if (verbose) { Console.WriteLine(sectionHeaderIndex.ToString()); } } if (verbose) { Console.WriteLine(newSectionHeader.ToString()); } } } this.sectionHeaderIndices.Sort(); // Iterate through all of the section header indices for (int i = 0; i < this.sectionHeaderIndices.Count; i++) { if (verbose) { Console.WriteLine(this.sectionHeaderIndices[i].ToString()); } else { Console.Write("."); } // Seek to the correct part of the file RpxSectionHeader currentSectionHeader = this.sectionHeaders[(int)this.sectionHeaderIndices[i].Index]; ulong position = currentSectionHeader.Offset; using (FileStream fs = new FileStream(this.path, FileMode.Open, FileAccess.Read)) { using BinaryReader br = new BinaryReader(fs, new ASCIIEncoding()); br.BaseStream.Seek((long)position, SeekOrigin.Begin); currentSectionHeader.Offset = (uint)br.BaseStream.Position; if ((currentSectionHeader.Flags & RpxSectionHeader.SectionHeaderRplZlib) == RpxSectionHeader.SectionHeaderRplZlib) { uint dataSize = currentSectionHeader.Size - 4; currentSectionHeader.Size = EndianUtility.ReadUInt32BE(br); uint blockSize = RpxSectionHeader.ChunkSize; uint have; byte[] bufferIn = new byte[RpxSectionHeader.ChunkSize]; byte[] bufferOut = new byte[RpxSectionHeader.ChunkSize]; ZlibCodec compressor = new ZlibCodec(); compressor.InitializeInflate(true); compressor.AvailableBytesIn = 0; compressor.NextIn = 0; while (dataSize > 0) { blockSize = RpxSectionHeader.ChunkSize; if (dataSize < blockSize) { blockSize = dataSize; } dataSize -= blockSize; bufferIn = br.ReadBytes((int)blockSize); compressor.NextIn = 0; compressor.InputBuffer = bufferIn; compressor.AvailableBytesIn = bufferIn.Length; compressor.OutputBuffer = bufferOut; do { compressor.AvailableBytesOut = (int)RpxSectionHeader.ChunkSize; compressor.NextOut = 0; compressor.Inflate(FlushType.None); have = RpxSectionHeader.ChunkSize - (uint)compressor.AvailableBytesOut; // write the data using (FileStream outFs = new FileStream(this.decompressedPath, FileMode.Append)) { using BinaryWriter bw = new BinaryWriter(outFs, new ASCIIEncoding()); bw.Write(bufferOut, 0, (int)have); } this.crcs[(int)this.sectionHeaderIndices[i].Index] = this.Crc32Rpx(this.crcs[(int)this.sectionHeaderIndices[i].Index], bufferOut, have); }while (compressor.AvailableBytesOut == 0); currentSectionHeader.Flags &= ~RpxSectionHeader.SectionHeaderRplZlib; } } else { uint dataSize = currentSectionHeader.Size; uint blockSize = RpxSectionHeader.ChunkSize; while (dataSize > 0) { byte[] data = new byte[RpxSectionHeader.ChunkSize]; blockSize = RpxSectionHeader.ChunkSize; if (dataSize < blockSize) { blockSize = dataSize; } dataSize -= blockSize; data = br.ReadBytes((int)blockSize); // Write out the section bytes using (FileStream outFs = new FileStream(this.decompressedPath, FileMode.Append)) { using BinaryWriter bw = new BinaryWriter(outFs, new ASCIIEncoding()); bw.Write(data); } this.crcs[(int)this.sectionHeaderIndices[i].Index] = this.Crc32Rpx(this.crcs[(int)this.sectionHeaderIndices[i].Index], data, blockSize); } } // Pad out the section on a 0x40 byte boundary using (FileStream outFs = new FileStream(this.decompressedPath, FileMode.Append)) { using BinaryWriter bw = new BinaryWriter(outFs, new ASCIIEncoding()); while (bw.BaseStream.Position % 0x40 != 0) { bw.Write((byte)0); } } if ((currentSectionHeader.Type & RpxSectionHeader.SectionHeaderRplCrcs) == RpxSectionHeader.SectionHeaderRplCrcs) { this.crcs[(int)this.sectionHeaderIndices[i].Index] = 0; this.crcDataOffset = currentSectionHeader.Offset; } } this.sectionHeaders[(int)this.sectionHeaderIndices[i].Index] = currentSectionHeader; } // Fix the output headers // TODO: This is not currently accurate vs. wiiurpx tool so may need to investigate using (FileStream outFs = new FileStream(this.decompressedPath, FileMode.Open, FileAccess.Write)) { using BinaryWriter bw = new BinaryWriter(outFs, new ASCIIEncoding()); bw.Seek((int)this.header.SectionHeaderOffset, SeekOrigin.Begin); for (uint i = 0; i < this.header.SectionHeaderCount; i++) { EndianUtility.WriteUInt32BE(bw, this.sectionHeaders[(int)i].Name); EndianUtility.WriteUInt32BE(bw, this.sectionHeaders[(int)i].Type); EndianUtility.WriteUInt32BE(bw, this.sectionHeaders[(int)i].Flags); EndianUtility.WriteUInt32BE(bw, this.sectionHeaders[(int)i].Address); EndianUtility.WriteUInt32BE(bw, this.sectionHeaders[(int)i].Offset); EndianUtility.WriteUInt32BE(bw, this.sectionHeaders[(int)i].Size); EndianUtility.WriteUInt32BE(bw, this.sectionHeaders[(int)i].Link); EndianUtility.WriteUInt32BE(bw, this.sectionHeaders[(int)i].Info); EndianUtility.WriteUInt32BE(bw, this.sectionHeaders[(int)i].AddrAlign); EndianUtility.WriteUInt32BE(bw, this.sectionHeaders[(int)i].EntSize); } using FileStream fs = new FileStream(this.path, FileMode.Open, FileAccess.Read); using BinaryReader br = new BinaryReader(fs, new ASCIIEncoding()); // Seek to the Section Header Offset in the file br.BaseStream.Seek((long)this.crcDataOffset, SeekOrigin.Begin); for (uint i = 0; i < this.header.SectionHeaderCount; i++) { EndianUtility.WriteUInt32BE(bw, this.crcs[(int)i]); } } Console.WriteLine(); Console.WriteLine("Decompression complete."); }