Ejemplo n.º 1
0
        public IList <IArchiveFileInfo> Load(Stream dataStream, Stream matStream)
        {
            using var br    = new BinaryReaderX(dataStream, true);
            using var matBr = new BinaryReaderX(matStream);

            // Read entries
            var entries = matBr.ReadMultiple <SarEntry>((int)(matStream.Length / EntrySize));

            // Read header
            _header = br.ReadType <SarContainerHeader>();

            // Add files
            var result = new List <IArchiveFileInfo>();

            for (var i = 0; i < entries.Count; i++)
            {
                var entry = entries[i];
                dataStream.Position = entry.offset;

                // Read compression header
                var compHeader = br.ReadType <SarContainerHeader>();
                if (compHeader.magic != "cmp ")
                {
                    result.Add(new SarArchiveFileInfo(new SubStream(dataStream, entry.offset, entry.size), $"{i:00000000}.bin"));
                    continue;
                }

                compHeader = br.ReadType <SarContainerHeader>();
                if (compHeader.magic != "lz7 ")
                {
                    throw new InvalidOperationException($"Unknown compression container detected for file at index {i}.");
                }

                var fileStream = new SubStream(dataStream, entry.offset + 0x18, SarSupport.GetCompressedSize(dataStream, entry.offset + 0x18, compHeader.data2));
                var name       = $"{i:00000000}.bin";

                var compMethod = NintendoCompressor.PeekCompressionMethod(fileStream);
                result.Add(new SarArchiveFileInfo(fileStream, name, NintendoCompressor.GetConfiguration(compMethod), NintendoCompressor.PeekDecompressedSize(fileStream)));
            }

            return(result);
        }
Ejemplo n.º 2
0
        public void Save(Stream dataStream, Stream matStream, IList <IArchiveFileInfo> files)
        {
            long endPos;

            using var bw    = new BinaryWriterX(dataStream);
            using var matBw = new BinaryWriterX(matStream);

            // Calculate offsets
            var mbrOffset  = HeaderSize;
            var dataOffset = mbrOffset + HeaderSize;

            // Write files
            var entries = new List <SarEntry>();

            var dataPosition = dataOffset;

            foreach (var file in files.Cast <SarArchiveFileInfo>())
            {
                // Write file data1
                dataStream.Position = dataPosition;
                if (file.UsesCompression)
                {
                    dataStream.Position += HeaderSize * 2;
                }

                var streamToWrite = file.GetFinalStream();
                streamToWrite.CopyTo(dataStream);
                var alignedSize = (streamToWrite.Length + 3) & ~3;

                // Write compression headers
                if (file.UsesCompression)
                {
                    endPos = dataStream.Position;
                    dataStream.Position = dataPosition;

                    bw.WriteType(new SarContainerHeader {
                        magic = "cmp ", data1 = 0x00010002, data2 = (int)(alignedSize + HeaderSize * 2 + 8)
                    });
                    // Divide bit count by 8; this may omit the remainder and is intended in the size calculation
                    // This recreates a buggy behaviour by the developers, who missed to account for the remainder properly, which can lead to the compressed size being off by 1
                    bw.WriteType(new SarContainerHeader {
                        magic = "lz7 ", data1 = (int)(alignedSize + HeaderSize + 4), data2 = SarSupport.CalculateBits(streamToWrite) / 8
                    });

                    dataStream.Position = endPos;
                    bw.WriteString("~lz7", Encoding.ASCII, false, false);
                    bw.WriteAlignment(4);
                    bw.WriteString("~cmp", Encoding.ASCII, false, false);
                }

                // Add entry
                entries.Add(new SarEntry {
                    offset = dataPosition - dataOffset, size = (int)(dataStream.Position - dataPosition)
                });

                dataPosition = (int)dataStream.Position;
            }

            // Write mbr header
            endPos = dataStream.Position;

            dataStream.Position = mbrOffset;
            bw.WriteType(new SarContainerHeader {
                magic = "mbr ", data1 = (int)(dataStream.Length - HeaderSize + 4), data2 = files.Count
            });

            dataStream.Position = endPos;
            bw.WriteString("~mbr", Encoding.ASCII, false, false);

            // Write entries
            var mifOffset = dataStream.Position;

            dataStream.Position += HeaderSize;
            bw.WriteMultiple(entries);

            // Write mif header
            endPos = dataStream.Position;

            dataStream.Position = mifOffset;
            bw.WriteType(new SarContainerHeader {
                magic = "mif ", data1 = (int)(dataStream.Length - mifOffset + 4), data2 = files.Count
            });

            dataStream.Position = endPos;
            bw.WriteString("~mif", Encoding.ASCII, false, false);

            // Write sar header
            bw.WriteString("~sar", Encoding.ASCII, false, false);

            dataStream.Position = 0;
            bw.WriteType(new SarContainerHeader {
                magic = "sar ", data1 = _header.data1, data2 = (int)dataStream.Length
            });

            // Write mat content
            foreach (var entry in entries)
            {
                entry.offset += dataOffset; // Offsets in .mat are absolute to the .sar
            }
            matBw.WriteMultiple(entries);
        }