Beispiel #1
0
        private bool LoadFile(DuplicatableStream headerStream, DuplicatableStream contentStream)
        {
            DuplicatableStream infile = headerStream.Duplicate();

            contentFile = contentStream.Duplicate();

            infile.Seek(0x00, SeekOrigin.Begin);
            string magic = infile.ReadAscii(4);

            if (magic != "FPS4")
            {
                Console.WriteLine("Not an FPS4 file!");
                return(false);
            }

            Endian     = Util.Endianness.BigEndian;
            FileCount  = infile.ReadUInt32().FromEndian(Endian);
            HeaderSize = infile.ReadUInt32().FromEndian(Endian);

            // if header seems huge then we probably have assumed the wrong endianness
            if (HeaderSize > 0xFFFF)
            {
                Endian     = Util.Endianness.LittleEndian;
                FileCount  = FileCount.ToEndian(Util.Endianness.BigEndian).FromEndian(Endian);
                HeaderSize = HeaderSize.ToEndian(Util.Endianness.BigEndian).FromEndian(Endian);
            }

            FirstFileStart      = infile.ReadUInt32().FromEndian(Endian);
            EntrySize           = infile.ReadUInt16().FromEndian(Endian);
            ContentBitmask      = new ContentInfo(infile.ReadUInt16().FromEndian(Endian));
            Unknown2            = infile.ReadUInt32().FromEndian(Endian);
            ArchiveNameLocation = infile.ReadUInt32().FromEndian(Endian);
            infile.Position     = ArchiveNameLocation;
            if (ArchiveNameLocation > 0)
            {
                ArchiveName = infile.ReadShiftJisNullterm();
            }

            Alignment = FirstFileStart;

            Console.WriteLine("Content Bitmask: 0x" + ContentBitmask.Value.ToString("X4"));
            if (ContentBitmask.HasUnknownDataTypes)
            {
                Console.WriteLine("WARNING: Bitmask identifies unknown data types, data interpretation will probably be incorrect.");
            }

            Files = new List <FileInfo>((int)FileCount);
            for (uint i = 0; i < FileCount; ++i)
            {
                infile.Position = HeaderSize + (i * EntrySize);
                Files.Add(new FileInfo(infile, i, ContentBitmask, Endian, Util.GameTextEncoding.ASCII));
            }

            FileLocationMultiplier          = CalculateFileLocationMultiplier();
            ShouldGuessFilesizeFromNextFile = !ContentBitmask.ContainsFileSizes && !ContentBitmask.ContainsSectorSizes && CalculateIsLinear();

            infile.Dispose();
            return(true);
        }
Beispiel #2
0
 public override void Dispose()
 {
     if (Stream != null)
     {
         Stream.Close();
         Stream.Dispose();
         Stream = null;
     }
 }
Beispiel #3
0
        private bool disposedValue = false;         // To detect redundant calls

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    infile.Dispose();
                }

                infile           = null;
                toc_string_table = null;
                disposedValue    = true;
            }
        }
Beispiel #4
0
        public static DuplicatableStream CreateConcatenatedStream(List <DuplicatableStream> streams)
        {
            if (streams.Count == 0)
            {
                return(EmptyStream.Instance);
            }

            var  Streams          = new List <DuplicatableStream>(streams.Count);
            var  Offsets          = new List <long>(Streams.Count);
            var  InternalCanRead  = true;
            var  InternalCanSeek  = true;
            var  InternalCanWrite = true;
            long sum = 0;

            for (int i = 0; i < streams.Count; ++i)
            {
                DuplicatableStream s = streams[i].Duplicate();
                s.ReStart();
                long l = s.Length;
                if (l <= 0)
                {
                    s.End();
                    s.Dispose();
                    continue;
                }
                sum             += l;
                InternalCanRead  = InternalCanRead && s.CanRead;
                InternalCanSeek  = InternalCanSeek && s.CanSeek;
                InternalCanWrite = InternalCanWrite && s.CanWrite;
                s.End();
                Streams.Add(s);
                Offsets.Add(sum);
            }

            if (Streams.Count == 0)
            {
                return(EmptyStream.Instance);
            }

            if (Streams.Count == 1)
            {
                return(Streams[0]);
            }

            return(new ConcatenatedStream(Streams, Offsets, InternalCanRead, InternalCanSeek, InternalCanWrite));
        }
Beispiel #5
0
        public HyoutaArchiveChunk(DuplicatableStream duplicatableStream, out ulong chunkLength)
        {
            using (DuplicatableStream data = duplicatableStream.Duplicate()) {
                data.Position = 0;

                // header
                ulong extraMagic = data.ReadUInt64(EndianUtils.Endianness.LittleEndian);
                ulong magic      = extraMagic & 0x00fffffffffffffful;
                if (magic != 0x6b6e7568636168)
                {
                    throw new Exception("wrong magic");
                }
                byte extra                  = (byte)((extraMagic >> 56) & 0xffu);
                byte packedAlignment        = (byte)(extra & 0x1fu);
                long unpackedAlignment      = 1L << packedAlignment;
                bool hasMetadata            = (extra & 0x20) != 0;
                bool isCompressed           = (extra & 0x40) != 0;
                bool isBigEndian            = (extra & 0x80) != 0;
                EndianUtils.Endianness e    = isBigEndian ? EndianUtils.Endianness.BigEndian : EndianUtils.Endianness.LittleEndian;
                ulong endOfFileOffset       = data.ReadUInt64(e) << packedAlignment;
                ulong tableOfContentsOffset = data.ReadUInt64(e) << packedAlignment;
                ulong filecount             = data.ReadUInt64(e);
                chunkLength = endOfFileOffset;

                if (hasMetadata)
                {
                    // just skip past this for now
                    ulong metadataLength = data.ReadUInt64(e);
                    data.DiscardBytes(metadataLength);
                }

                DuplicatableStream dataBlockStream;
                if (isCompressed)
                {
                    ushort compressionInfoLengthRaw       = data.ReadUInt16(e);
                    uint   compressionInfoLength          = compressionInfoLengthRaw & 0xfffcu;
                    int    compressionInfoAlignmentPacked = (compressionInfoLengthRaw & 0x3) + 1;
                    data.ReadAlign(1u << compressionInfoAlignmentPacked);
                    Compression.IHyoutaArchiveCompressionInfo?compressionInfo = HyoutaArchiveCompression.Deserialize(data, compressionInfoLength == 0 ? 0x10000u : compressionInfoLength, e);
                    if (compressionInfo == null)
                    {
                        throw new Exception("File is indicated to be compressed, but no decompressor found.");
                    }
                    dataBlockStream = compressionInfo.Decompress(data);
                }
                else
                {
                    data.ReadAlign(unpackedAlignment);
                    dataBlockStream = new PartialStream(data, data.Position, (long)(endOfFileOffset - (ulong)data.Position));
                }

                try {
                    data.Dispose();

                    dataBlockStream.Position = (long)tableOfContentsOffset;
                    uint offsetToFirstFileInfo = ReadContentLength(dataBlockStream, e);

                    // decode content bitfield(s)
                    long   numberOfUnknownBits = 0;
                    ushort contentBitfield1    = dataBlockStream.ReadUInt16(e);
                    bool   hasDummyContent     = (contentBitfield1 & 0x0001u) != 0;
                    bool   hasFilename         = (contentBitfield1 & 0x0002u) != 0;
                    bool   hasCompression      = (contentBitfield1 & 0x0004u) != 0;
                    bool   hasBpsPatch         = (contentBitfield1 & 0x0008u) != 0;
                    bool   hasCrc32            = (contentBitfield1 & 0x0010u) != 0;
                    bool   hasMd5  = (contentBitfield1 & 0x0020u) != 0;
                    bool   hasSha1 = (contentBitfield1 & 0x0040u) != 0;
                    numberOfUnknownBits += (contentBitfield1 & 0x0080u) != 0 ? 1 : 0;
                    numberOfUnknownBits += (contentBitfield1 & 0x0100u) != 0 ? 1 : 0;
                    numberOfUnknownBits += (contentBitfield1 & 0x0200u) != 0 ? 1 : 0;
                    numberOfUnknownBits += (contentBitfield1 & 0x0400u) != 0 ? 1 : 0;
                    numberOfUnknownBits += (contentBitfield1 & 0x0800u) != 0 ? 1 : 0;
                    numberOfUnknownBits += (contentBitfield1 & 0x1000u) != 0 ? 1 : 0;
                    numberOfUnknownBits += (contentBitfield1 & 0x2000u) != 0 ? 1 : 0;
                    numberOfUnknownBits += (contentBitfield1 & 0x4000u) != 0 ? 1 : 0;
                    ushort currentBitfield = contentBitfield1;
                    while ((currentBitfield & 0x8000u) != 0)
                    {
                        // more bitfields, though we don't understand them since only the first handful of bits are defined at the moment, so just count and skip them
                        currentBitfield      = dataBlockStream.ReadUInt16(e);
                        numberOfUnknownBits += (currentBitfield & 0x0001u) != 0 ? 1 : 0;
                        numberOfUnknownBits += (currentBitfield & 0x0002u) != 0 ? 1 : 0;
                        numberOfUnknownBits += (currentBitfield & 0x0004u) != 0 ? 1 : 0;
                        numberOfUnknownBits += (currentBitfield & 0x0008u) != 0 ? 1 : 0;
                        numberOfUnknownBits += (currentBitfield & 0x0010u) != 0 ? 1 : 0;
                        numberOfUnknownBits += (currentBitfield & 0x0020u) != 0 ? 1 : 0;
                        numberOfUnknownBits += (currentBitfield & 0x0040u) != 0 ? 1 : 0;
                        numberOfUnknownBits += (currentBitfield & 0x0080u) != 0 ? 1 : 0;
                        numberOfUnknownBits += (currentBitfield & 0x0100u) != 0 ? 1 : 0;
                        numberOfUnknownBits += (currentBitfield & 0x0200u) != 0 ? 1 : 0;
                        numberOfUnknownBits += (currentBitfield & 0x0400u) != 0 ? 1 : 0;
                        numberOfUnknownBits += (currentBitfield & 0x0800u) != 0 ? 1 : 0;
                        numberOfUnknownBits += (currentBitfield & 0x1000u) != 0 ? 1 : 0;
                        numberOfUnknownBits += (currentBitfield & 0x2000u) != 0 ? 1 : 0;
                        numberOfUnknownBits += (currentBitfield & 0x4000u) != 0 ? 1 : 0;
                    }
                    uint dummyContentLength   = hasDummyContent ? ReadContentLength(dataBlockStream, e) : 0;
                    uint filenameLength       = hasFilename ? ReadContentLength(dataBlockStream, e) : 0;
                    uint compressionLength    = hasCompression ? ReadContentLength(dataBlockStream, e) : 0;
                    uint bpspatchLength       = hasBpsPatch ? ReadContentLength(dataBlockStream, e) : 0;
                    uint crc32Length          = hasCrc32 ? ReadContentLength(dataBlockStream, e) : 0;
                    uint md5Length            = hasMd5 ? ReadContentLength(dataBlockStream, e) : 0;
                    uint sha1Length           = hasSha1 ? ReadContentLength(dataBlockStream, e) : 0;
                    long unknownContentLength = 0;
                    for (long i = 0; i < numberOfUnknownBits; ++i)
                    {
                        unknownContentLength += ReadContentLength(dataBlockStream, e);
                    }

                    dataBlockStream.Position = (long)(tableOfContentsOffset + offsetToFirstFileInfo);
                    List <HyoutaArchiveFileInfo> files = new List <HyoutaArchiveFileInfo>((int)filecount);
                    for (ulong i = 0; i < filecount; ++i)
                    {
                        ulong offset             = dataBlockStream.ReadUInt64(e) << packedAlignment;
                        ulong filesize           = dataBlockStream.ReadUInt64(e);
                        HyoutaArchiveFileInfo fi = new HyoutaArchiveFileInfo();
                        if (hasDummyContent)
                        {
                            fi.DummyContent = dataBlockStream.ReadBytes(dummyContentLength);
                        }
                        if (hasFilename)
                        {
                            fi.Filename = ReadString(dataBlockStream, filenameLength, e);
                        }
                        if (hasCompression)
                        {
                            fi.CompressionInfo    = HyoutaArchiveCompression.Deserialize(dataBlockStream, compressionLength, e);
                            fi.StreamIsCompressed = true;
                        }
                        if (hasBpsPatch)
                        {
                            fi.BpsPatchInfo     = HyoutaArchiveBpsPatchInfo.Deserialize(dataBlockStream, bpspatchLength, e, i, this);
                            fi.StreamIsBpsPatch = fi.BpsPatchInfo != null;
                        }
                        if (hasCrc32)
                        {
                            if (crc32Length >= 4)
                            {
                                fi.crc32 = new CRC32(dataBlockStream.ReadUInt32(EndianUtils.Endianness.BigEndian));
                                dataBlockStream.DiscardBytes(crc32Length - 4);
                            }
                            else
                            {
                                dataBlockStream.DiscardBytes(crc32Length);
                            }
                        }
                        if (hasMd5)
                        {
                            if (md5Length >= 16)
                            {
                                ulong a = dataBlockStream.ReadUInt64(EndianUtils.Endianness.BigEndian);
                                ulong b = dataBlockStream.ReadUInt64(EndianUtils.Endianness.BigEndian);
                                fi.md5 = new MD5(a, b);
                                dataBlockStream.DiscardBytes(md5Length - 16);
                            }
                            else
                            {
                                dataBlockStream.DiscardBytes(md5Length);
                            }
                        }
                        if (hasSha1)
                        {
                            if (sha1Length >= 20)
                            {
                                ulong a = dataBlockStream.ReadUInt64(EndianUtils.Endianness.BigEndian);
                                ulong b = dataBlockStream.ReadUInt64(EndianUtils.Endianness.BigEndian);
                                uint  c = dataBlockStream.ReadUInt32(EndianUtils.Endianness.BigEndian);
                                fi.sha1 = new SHA1(a, b, c);
                                dataBlockStream.DiscardBytes(sha1Length - 20);
                            }
                            else
                            {
                                dataBlockStream.DiscardBytes(sha1Length);
                            }
                        }
                        dataBlockStream.DiscardBytes(unknownContentLength);

                        fi.Data = new PartialStream(dataBlockStream, (long)offset, (long)filesize);
                        files.Add(fi);
                    }

                    Files = files;
                } finally {
                    dataBlockStream.Dispose();
                }
            }
        }
Beispiel #6
0
 public override void Dispose()
 {
     Stream.Dispose();
 }