/// <summary> /// Loads an archive into a <see cref="VirtualFilesystemDirectory"/>, automatically de-compressing the archive if required. /// /// </summary> /// <param name="filePath">Filepath of file to (optionally) decompress and load.</param> /// <returns><see cref="VirtualFilesystemDirectory"/> containing the contents, or null if filepath is not a valid archive.</returns> public static VirtualFilesystemDirectory LoadArchive(string filePath) { if (string.IsNullOrEmpty(filePath)) { throw new ArgumentNullException("filePath", "Cannot load archive from empty file path!"); } if (!File.Exists(filePath)) { throw new ArgumentException("Cannot load archive from non-existant file!", "filePath"); } MemoryStream decompressedFile = null; using (EndianBinaryReader fileReader = new EndianBinaryReader(File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), Endian.Big)) { // Read the first 4 bytes to see if it's a compressed file (Yaz0) or a plain RARC file. uint fileMagic = fileReader.ReadUInt32(); fileReader.BaseStream.Position = 0L; // Reset to the start so that the next thing to read it is at the start like it expects. switch (fileMagic) { case 0x59617A30: // Yaz0 Compression decompressedFile = Yaz0.Decode(fileReader); break; case 0x59617930: // Yay0 Compression decompressedFile = Yay0.Decode(fileReader); break; case 0x52415243: // RARC - Uncompressed decompressedFile = new MemoryStream((int)fileReader.BaseStream.Length); fileReader.BaseStream.CopyTo(decompressedFile); // Copying modifies the decompressedFile's read head (places it at new location) so we rewind. decompressedFile.Position = 0L; break; default: throw new NotImplementedException(string.Format("Unknown magic: {0}. If this is a Nintendo archive, open an Issue on GitHub!", fileMagic.ToString("X8"))); } } // Not an archive we know how to handle. if (decompressedFile == null) { return(null); } // Decompress the archive into the folder. It'll generate a sub-folder with the Archive's ROOT name. Archive rarc = new Archive(); using (EndianBinaryReader reader = new EndianBinaryReader(decompressedFile, Endian.Big)) { return(rarc.ReadFile(reader)); } }
/// <summary> /// Loads a file into a <see cref="EndianBinaryReader"/>, automatically de-compressing the file if required. /// Throws an exception if the filepath to the file is not valid. /// </summary> /// <param name="filePath">Filepath of file to (optionally) decompress and load.</param> /// <returns><see cref="EndianBinaryReader"/> containing the contents.</returns> public static EndianBinaryReader LoadFile(string filePath) { if (string.IsNullOrEmpty(filePath)) { throw new ArgumentNullException("filePath", "Cannot load archive from empty file path!"); } if (!File.Exists(filePath)) { throw new ArgumentException("Cannot load archive from non-existant file!", "filePath"); } MemoryStream decompressedFile = null; using (EndianBinaryReader fileReader = new EndianBinaryReader(File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), Endian.Big)) { // Read the first 4 bytes to see if it's a compressed file (Yaz0, Yay0, etc.) uint fileMagic = fileReader.ReadUInt32(); fileReader.BaseStream.Position = 0L; // Reset to the start so that the next thing to read it is at the start like it expects. switch (fileMagic) { case 0x59617A30: // Yaz0 Compression decompressedFile = Yaz0.Decode(fileReader); break; case 0x59617930: // Yay0 Compression decompressedFile = Yay0.Decode(fileReader); break; default: // Uncompressed decompressedFile = new MemoryStream((int)fileReader.BaseStream.Length); fileReader.BaseStream.CopyTo(decompressedFile); // CopyTo modifies the decompressedFile's read head (places it at new location) so we rewind. decompressedFile.Position = 0L; break; } } // Return the decompressed file return(new EndianBinaryReader(decompressedFile, Endian.Big)); }
static void Test(byte[] bytes, Method method) { var bytes2 = new byte[bytes.Length]; switch (method) { case Method.RLE: bytes2 = RLE.Decompress(new MemoryStream(RLE.Compress(new MemoryStream(bytes))), bytes.Length); Assert.IsTrue(bytes.SequenceEqual(bytes2)); break; case Method.Yaz0: bytes2 = Yaz0.Decompress(new MemoryStream(Yaz0.Compress(new MemoryStream(bytes)))); Assert.IsTrue(bytes.SequenceEqual(bytes2)); break; case Method.Yay0: bytes2 = Yay0.Decompress(new MemoryStream(Yay0.Compress(new MemoryStream(bytes)))); Assert.IsTrue(bytes.SequenceEqual(bytes2)); break; } }
public static void Compress(object sender, EventArgs e) { var tsi = sender as ToolStripMenuItem; if (!Shared.PrepareFiles("Open a decompressed " + tsi?.Tag + "file...", "Save your compressed file...", ".decomp", out var openFile, out var saveFile, true)) { return; } try { using (openFile) using (var outFs = new BinaryWriterX(saveFile)) switch (tsi?.Tag) { case Compression.L5LZ10: outFs.Write(Level5.Compress(openFile, Level5.Method.LZ10)); break; case Compression.L5Huff4: outFs.Write(Level5.Compress(openFile, Level5.Method.Huffman4Bit)); break; case Compression.L5Huff8: outFs.Write(Level5.Compress(openFile, Level5.Method.Huffman8Bit)); break; case Compression.L5RLE: outFs.Write(Level5.Compress(openFile, Level5.Method.RLE)); break; case Compression.NLZ10: outFs.Write(Nintendo.Compress(openFile, Nintendo.Method.LZ10)); break; case Compression.NLZ11: outFs.Write(Nintendo.Compress(openFile, Nintendo.Method.LZ11)); break; case Compression.NLZ60: outFs.Write(Nintendo.Compress(openFile, Nintendo.Method.LZ60)); break; case Compression.NHuff4: outFs.Write(Nintendo.Compress(openFile, Nintendo.Method.Huff4)); break; case Compression.NHuff8: outFs.Write(Nintendo.Compress(openFile, Nintendo.Method.Huff8)); break; case Compression.NRLE: outFs.Write(Nintendo.Compress(openFile, Nintendo.Method.RLE)); break; case Compression.LZ77: outFs.Write(LZ77.Compress(openFile)); break; case Compression.RevLZ77: outFs.Write(RevLZ77.Compress(openFile)); break; case Compression.LZOvl: outFs.Write(LZOvl.Compress(openFile)); break; case Compression.LZ4: outFs.Write(Kontract.Compression.LZ4.Compress(openFile)); break; case Compression.MIO0LE: outFs.Write(MIO0.Compress(openFile, ByteOrder.LittleEndian)); break; case Compression.MIO0BE: outFs.Write(MIO0.Compress(openFile, ByteOrder.BigEndian)); break; case Compression.Yay0LE: outFs.Write(Yay0.Compress(openFile, ByteOrder.LittleEndian)); break; case Compression.Yay0BE: outFs.Write(Yay0.Compress(openFile, ByteOrder.BigEndian)); break; case Compression.Yaz0LE: outFs.Write(Yaz0.Compress(openFile, ByteOrder.LittleEndian)); break; case Compression.Yaz0BE: outFs.Write(Yaz0.Compress(openFile, ByteOrder.BigEndian)); break; case Compression.GZip: outFs.Write(GZip.Compress(openFile)); break; case Compression.ZLib: outFs.Write(ZLib.Compress(openFile)); break; case Compression.PSVSpikeChun: outFs.Write(PSVSpikeChun.Compress(openFile)); break; } } catch (Exception ex) { MessageBox.Show(ex.ToString(), tsi?.Text, MessageBoxButtons.OK, MessageBoxIcon.Error); return; } MessageBox.Show($"Successfully compressed {Path.GetFileName(openFile.Name)}.", tsi.Text, MessageBoxButtons.OK, MessageBoxIcon.Information); }
public static void Decompress(object sender, EventArgs e) { var tsi = sender as ToolStripMenuItem; if (!Shared.PrepareFiles("Open a " + tsi?.Tag + " compressed file...", "Save your decompressed file...", ".decomp", out var openFile, out var saveFile)) { return; } try { using (openFile) using (var outFs = new BinaryWriterX(saveFile)) switch (tsi?.Tag) { case Compression.Level5: outFs.Write(Level5.Decompress(openFile)); break; case Compression.Nintendo: outFs.Write(Nintendo.Decompress(openFile)); break; case Compression.LZ77: outFs.Write(LZ77.Decompress(openFile)); break; case Compression.RevLZ77: outFs.Write(RevLZ77.Decompress(openFile)); break; case Compression.LZOvl: outFs.Write(LZOvl.Decompress(openFile)); break; case Compression.LZ4: outFs.Write(Kontract.Compression.LZ4.Decompress(openFile)); break; case Compression.MIO0LE: outFs.Write(MIO0.Decompress(openFile, ByteOrder.LittleEndian)); break; case Compression.MIO0BE: outFs.Write(MIO0.Decompress(openFile, ByteOrder.BigEndian)); break; case Compression.Yay0LE: outFs.Write(Yay0.Decompress(openFile, ByteOrder.LittleEndian)); break; case Compression.Yay0BE: outFs.Write(Yay0.Decompress(openFile, ByteOrder.BigEndian)); break; case Compression.Yaz0LE: outFs.Write(Yaz0.Decompress(openFile, ByteOrder.LittleEndian)); break; case Compression.Yaz0BE: outFs.Write(Yaz0.Decompress(openFile, ByteOrder.BigEndian)); break; case Compression.LZECD: outFs.Write(LZECD.Decompress(openFile)); break; case Compression.LZ10VLE: outFs.Write(LZSSVLE.Decompress(openFile)); break; case Compression.GZip: outFs.Write(GZip.Decompress(openFile)); break; case Compression.ZLib: outFs.Write(ZLib.Decompress(openFile)); break; case Compression.PSVSpikeChun: outFs.Write(PSVSpikeChun.Decompress(openFile)); break; } } catch (Exception ex) { MessageBox.Show(ex.ToString(), tsi?.Text, MessageBoxButtons.OK, MessageBoxIcon.Error); return; } MessageBox.Show($"Successfully decompressed {Path.GetFileName(openFile.Name)}.", tsi.Text, MessageBoxButtons.OK, MessageBoxIcon.Information); }
public static VirtDirectory LoadRarc(byte[] data) { if (Yay0.IsCompressed(data)) { data = Yay0.Decompress(data); } DhBinaryReader br = new DhBinaryReader(data, DhEndian.Big); if (br.ReadStr(4) != "RARC") { throw new InvalidDataException("No valid RARC signature was found!"); } List <RarcNode> nodes = new List <RarcNode>(); List <RarcEntry> entries = new List <RarcEntry>(); // read header // RARC here br.Skip(4); br.Skip(4); var dataOffset = br.ReadU32() + 0x20; br.Skip(4); br.Skip(4); br.Skip(4); br.Skip(4); // read infoblock var NodeCount = br.ReadU32(); var NodeOffset = br.ReadU32() + 0x20; var EntryCount = br.ReadU32(); var EntryOffset = br.ReadU32() + 0x20; br.Skip(4); var StringTableOffset = br.ReadU32() + 0x20; br.Skip(2); br.Skip(2); br.Skip(4); br.Goto(EntryOffset); // read entries for (int i = 0; i < EntryCount; i++) { RarcEntry entry = new RarcEntry() { Id = br.ReadU16(), NameHash = br.ReadU16(), Type = br.ReadU16(), NameOffset = br.ReadU16(), DataOffset = br.ReadU32(), DataLength = br.ReadU32() }; if (entry.Type == 0x1100) { entry.Data = br.ReadAt(dataOffset + entry.DataOffset, (int)entry.DataLength); } entry.MemoryPointer = br.ReadU32(); entry.Name = br.ReadStrAt(StringTableOffset + entry.NameOffset); entries.Add(entry); } br.Goto(NodeOffset); // read nodes for (int i = 0; i < NodeCount; i++) { RarcNode rarcNode = new RarcNode() { Id = br.ReadStr(4), NameOffset = br.ReadU32(), NameHash = br.ReadU16(), EntryCount = br.ReadU16(), FirstEntryIndex = br.ReadU32() }; rarcNode.Name = br.ReadStrAt(StringTableOffset + rarcNode.NameOffset); rarcNode.Entries = entries.GetRange((int)rarcNode.FirstEntryIndex, (int)rarcNode.EntryCount); nodes.Add(rarcNode); } List <VirtDirectory> virtDirectories = new List <VirtDirectory>(nodes.Count); foreach (RarcNode rarcNode in nodes) { virtDirectories.Add(new VirtDirectory(rarcNode.Name, Guid.Empty)); } for (int i = 0; i < nodes.Count; i++) { RarcNode rarcNode = nodes[i]; for (int y = 0; y < nodes[i].Entries.Count; y++) { if (rarcNode.Entries[y].Name == "." || rarcNode.Entries[y].Name == "..") { continue; } if (rarcNode.Entries[y].Type == (ushort)NodeType.Directory) { var virtDirectory = virtDirectories[(int)rarcNode.Entries[y].DataOffset]; virtDirectory.ParentGuid = virtDirectories[i].Guid; virtDirectory.Name = rarcNode.Entries[y].Name; virtDirectories[i].Children.Add(virtDirectory); } else { VirtFile virtFile = new VirtFile(rarcNode.Entries[y].Name, virtDirectories[i].Guid, rarcNode.Entries[y].Data); virtDirectories[i].Children.Add(virtFile); } } } return(virtDirectories.Count > 0 ? virtDirectories[0] : null); }
private static void ExtractArchive(string outputFolder, string filePath) { if (!File.Exists(filePath)) { Console.WriteLine("Warning: Tried to extract archive from filePath \"{0}\" but not a file!", filePath); return; } if (m_verboseOutput) { Console.Write("Extracting archive {0}... ", Path.GetFileName(filePath)); } try { MemoryStream decompressedFile = null; using (EndianBinaryReader fileReader = new EndianBinaryReader(File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), Endian.Big)) { // Read the first 4 bytes to see if it's a compressed file (Yaz0) or a plain RARC file. uint fileMagic = fileReader.ReadUInt32(); fileReader.BaseStream.Position = 0L; // Reset to the start so that the next thing to read it is at the start like it expects. if (fileMagic == 0x59617A30) // Yaz0 { if (m_verboseOutput) { Console.Write("Archive compressed with Yaz0, decompressing... "); } decompressedFile = Yaz0.Decode(fileReader); } if (fileMagic == 0x59617930) // Yay0 { if (m_verboseOutput) { Console.Write("Archive compressed with Yay0, decompressing... "); } decompressedFile = Yay0.Decode(fileReader); } else if (fileMagic == 0x52415243) // RARC { // Copy the fileReader stream to a new memorystream. decompressedFile = new MemoryStream((int)fileReader.BaseStream.Length); fileReader.BaseStream.CopyTo(decompressedFile); decompressedFile.Position = 0L; } } if (decompressedFile == null) { if (m_verboseOutput) { Console.WriteLine("Skipping archive, not a Yaz0 or RARC file."); } return; } // Decompress the archive into the folder. It'll generate a sub-folder with the Archive's ROOT name. Archive rarc = new Archive(); using (EndianBinaryReader reader = new EndianBinaryReader(decompressedFile, Endian.Big)) { VirtualFilesystemDirectory root = rarc.ReadFile(reader); if (m_printFS) { PrintFileSystem(root); } // Many archives use the same internal root name, which causes a conflict when they export. // To solve this, we use the file name of the file as the root name, instead of the internal // name. if (!m_useInternalNames) { root.Name = Path.GetFileNameWithoutExtension(filePath); } // Write it to disk. root.ExportToDisk(outputFolder); } if (m_verboseOutput) { Console.WriteLine("Completed."); } } catch (Exception ex) { Console.WriteLine("Caught Exception: " + ex.ToString()); m_wasError = true; } }