Example #1
0
        public RPF7File(Stream inputStream, String filname = "", String filePath = null)
        {
            if (!inputStream.CanRead)
            {
                throw new RPFParsingException("Stream isn't readable");
            }
            this.Filename = filname;
            this.FilePath = filePath;
            Stream        = inputStream;

            try
            {
                Info = new Structs.RPF7Header(Stream);
            }
            catch (Exception e)
            {
                throw new RPFParsingException(String.Format("Failed to read header: {0}", e.Message));
            }

            if (new string(Info.Magic) != "RPF7")
            {
                throw new RPFParsingException("Invalid RPF Magic");
            }

            if (GlobalOptions.Platform == Platform.PlatformType.PLAYSTATION3)
            {
                if (Info.PlatformBit != 0)
                {
                    throw new RPFParsingException("Invalid platform bit (Are you sure that this RPF is for PlayStation 3?)");
                }
            }
            else if (GlobalOptions.Platform == Platform.PlatformType.XBOX360)
            {
                if (Info.PlatformBit != 1)
                {
                    throw new RPFParsingException("Invalid platform bit (Are you sure that this RPF is for Xbox 360?)");
                }
            }

            if (Info.EntriesCount == 0)
            {
                throw new RPFParsingException("Empty RPF - no root directory");
            }

            sixteenRoundsDecrypt = (Info.Flag >> 28) == 0xf;

            if (sixteenRoundsDecrypt)
            {
                throw new Exception("Needed to be tested first");
            }

            using (BinaryReader binaryStream = new BinaryReader(AES.DecryptStream(new StreamKeeper(this.Stream), sixteenRoundsDecrypt)))
            {
                MemoryStream    entriesInfo = new MemoryStream(binaryStream.ReadBytes(0x10 * Info.EntriesCount));
                MemoryStream    filenames   = new MemoryStream(binaryStream.ReadBytes(Info.EntriesNamesLength));
                Range <Boolean> fileUsage   = new Range <Boolean>(Stream.Length);
                // Just mark the root entry info and the header as used
                fileUsage.AddItem(0, 0x10 + 0x10, true);
                this.Root = CreateFromHeader(new Structs.RPF7EntryInfoTemplate(entriesInfo), entriesInfo, filenames, fileUsage) as DirectoryEntry;
            }


            if (this.Root == null)
            {
                throw new RPFParsingException("Expected root to be a directory.");
            }
        }
Example #2
0
        private Entry CreateFromHeader(Structs.RPF7EntryInfoTemplate entryInfo, MemoryStream entriesInfo, MemoryStream filenames, Range <Boolean> fileUsage)
        {
            // Todo: check sizes and offsets
            bool isResource     = entryInfo.Field1 == 1;
            long offset         = (long)entryInfo.Field2;
            int  compressedSize = (int)entryInfo.Field3;
            int  filenameOffset = (int)entryInfo.Field4;

            filenameOffset <<= Info.ShiftNameAccessBy;

            if (filenameOffset < 0 || filenameOffset >= filenames.Length)
            {
                throw new RPFEntryParsingException("Reading entry name: Invalid offst");
            }
            filenames.Seek(filenameOffset, SeekOrigin.Begin);

            String filename = "";
            // Read null-terminated filename
            int currentChar;

            while ((currentChar = filenames.ReadByte()) != 0)
            {
                if (currentChar == -1)
                {
                    throw new RPFEntryParsingException("Reading entry name: Unexpected EOF");
                }
                filename += (char)currentChar;
            }
            // There may be duplicate names usage, so don't check it
            if (!fileUsage.AddItem(0x10 + entriesInfo.Length + filenameOffset, filename.Length + 1, true))
            {
                throw new RPFEntryParsingException("Reading entry entry: Invalid duplicate usage of a name");
            }

            if (offset == 0x7FFFFF)
            {
                // Is a Directory
                if (isResource)
                {
                    throw new RPFEntryParsingException("Invalid entry type (directory and resource)");
                }
                int subentriesStartIndex = (int)entryInfo.Field5;
                int subentriesCount      = (int)entryInfo.Field6;
                if (subentriesStartIndex < 0 || (subentriesStartIndex + subentriesCount) * 0x10 > entriesInfo.Length)
                {
                    throw new RPFEntryParsingException("Invalid directory entry: Invalid subentries info");
                }

                List <Entry> entries = new List <Entry>();
                // This will prevent recursion..
                if (!fileUsage.AddItem(0x10 * (1 + subentriesStartIndex), 0x10 * subentriesCount, true, false))
                {
                    throw new RPFEntryParsingException("Reading directory entry: Duplicate usage of an entries - may lead to recursion");
                }

                for (int i = 0; i < subentriesCount; ++i)
                {
                    entriesInfo.Seek(0x10 * (i + subentriesStartIndex), SeekOrigin.Begin);
                    entries.Add(CreateFromHeader(new Structs.RPF7EntryInfoTemplate(entriesInfo), entriesInfo, filenames, fileUsage));
                }
                return(new DirectoryEntry(filename, entries));
            }

            offset <<= 9;

            if (offset < 0 || offset > Stream.Length)
            {
                throw new RPFEntryParsingException("Invalid entry info: Invalid data offset");
            }

            if (isResource)
            {
                if (compressedSize == 0xFFFFFF)
                {
                    throw new RPFEntryParsingException("Resource with size -1, not supported (Contact developr if seen)");
                }
                if (!fileUsage.AddItem(offset, compressedSize, true, false)) // Can't use the same data for two files
                {
                    throw new RPFEntryParsingException("Reading entry data: Data position is wierd");
                }
                uint systemFlag   = entryInfo.Field5;
                uint graphicsFlag = entryInfo.Field6;
                return(new ResourceEntry(filename, new ResourceStreamCreator(Stream, offset, compressedSize, systemFlag, graphicsFlag, Path.GetExtension(filename).Substring(2)), systemFlag, graphicsFlag));
            }

            // Regular file
            int uncompressedSize = (int)entryInfo.Field5;
            int isEncrypted      = (int)entryInfo.Field6;

            if (compressedSize == 0)
            {
                // Uncompressed file
                if (isEncrypted != 0)
                {
                    throw new RPFEntryParsingException("Unexcepted file - compressed but unencrypted (Contact developr if seen)");
                }
                if (!fileUsage.AddItem(offset, uncompressedSize, true, false)) // Can't use the same data for two files
                {
                    throw new RPFEntryParsingException("Reading entry data: Data position is wierd");
                }
                return(new RegularFileEntry(filename, new FileStreamCreator(Stream, offset, uncompressedSize), false));
            }
            else
            {
                if (!fileUsage.AddItem(offset, compressedSize, true, false)) // Can't use the same data for two files
                {
                    throw new RPFEntryParsingException("Reading entry data: Data position is wierd");
                }
                // Compressed file
                return(new RegularFileEntry(filename, new CompressedFileStreamCreator(Stream, offset, compressedSize, uncompressedSize, isEncrypted != 0), true));
            }
        }