Beispiel #1
0
        /// <summary>
        /// Reads a <see cref="GgpkFreeRecord"/> from the given <see cref="BinaryReader"/>.
        /// </summary>
        /// <param name="marker">The <see cref="GgpkRecordMarker"/> of the record.</param>
        /// <param name="reader">The <see cref="BinaryReader"/> that shall be read.</param>
        /// <returns>A <see cref="GgpkFreeRecord"/>.</returns>
        public static GgpkFreeRecord From(GgpkRecordMarker marker, BinaryReader reader)
        {
            GgpkFreeRecord record = new GgpkFreeRecord
            {
                NextFreeRecordOffset = reader.ReadUInt64(),
                DataLength           = marker.Length - GgpkRecordMarker.Size
            };

            // Skip free space, move to position of next record
            reader.BaseStream.Seek(record.DataLength - Size, SeekOrigin.Current);

            return(record);
        }
Beispiel #2
0
        /// <summary>
        /// Reads a <see cref="GgpkFileRecord"/> from the given <see cref="BinaryReader"/>.
        /// </summary>
        /// <param name="marker">The <see cref="GgpkRecordMarker"/> of the record.</param>
        /// <param name="reader">The <see cref="BinaryReader"/> that shall be read.</param>
        /// <returns>A <see cref="GgpkFileRecord"/>.</returns>
        public static GgpkFileRecord From(GgpkRecordMarker marker, BinaryReader reader)
        {
            uint   fileNameLength         = reader.ReadUInt32();
            string hash                   = Convert.ToBase64String(reader.ReadBytes(32));
            string fileName               = Encoding.Unicode.GetString(reader.ReadBytes((int)fileNameLength * 2)).TrimEnd('\0');
            ulong  fileOffset             = (ulong)reader.BaseStream.Position;
            ulong  fileRecordHeaderLength = fileOffset - marker.Offset;
            ulong  fileLength             = marker.Length - fileRecordHeaderLength;

            GgpkFileRecord record = new GgpkFileRecord
            {
                FileName   = fileName,
                Hash       = hash,
                FileOffset = fileOffset,
                FileLength = fileLength
            };

            // Skip actual file data, move to position of next record
            reader.BaseStream.Seek((long)record.FileLength, SeekOrigin.Current);

            return(record);
        }
Beispiel #3
0
        /// <summary>
        /// Reads the given ggpk archive <paramref name="stream"/> and returns all records.
        /// </summary>
        /// <param name="stream">The ggpk <see cref="Stream"/>.</param>
        /// <returns>All records read from the <see cref="Stream"/>.</returns>
        /// <exception cref="ArgumentNullException"><c>stream</c> is <c>null</c>.</exception>
        /// <exception cref="GgpkException">Error while reading the archive.</exception>
        public static IEnumerable <GgpkRecord> From(Stream stream)
        {
            if (stream is null)
            {
                throw new ArgumentNullException(nameof(stream));
            }

            List <GgpkRecord> records = new List <GgpkRecord>();

            using (BinaryReader ggpkStreamReader = new BinaryReader(stream))
            {
                try
                {
                    while (ggpkStreamReader.BaseStream.Position < ggpkStreamReader.BaseStream.Length)
                    {
                        GgpkRecordMarker recordMarker  = GgpkRecordMarker.From(ggpkStreamReader);
                        GgpkRecord       currentRecord = null;

                        if (recordMarker.Offset + recordMarker.Length > (ulong)ggpkStreamReader.BaseStream.Length)
                        {
                            throw new InvalidDataException($"Invalid record length {recordMarker.Length} at offset {recordMarker.Offset}");
                        }

                        switch (recordMarker.Type)
                        {
                        case "GGPK":
                            currentRecord = GgpkMainRecord.From(ggpkStreamReader);
                            break;

                        case "FREE":
                            currentRecord = GgpkFreeRecord.From(recordMarker, ggpkStreamReader);
                            break;

                        case "FILE":
                            currentRecord = GgpkFileRecord.From(recordMarker, ggpkStreamReader);
                            break;

                        case "PDIR":
                            currentRecord = GgpkDirectoryRecord.From(ggpkStreamReader);
                            break;

                        default:
                            throw new InvalidDataException($"Unknown record type {recordMarker.Type} at offset {recordMarker.Offset}");
                        }

                        currentRecord.Offset = recordMarker.Offset;
                        currentRecord.Length = recordMarker.Length;

                        records.Add(currentRecord);
                    }
                }
                catch (Exception ex)
                {
                    throw new GgpkException($"Error while parsing the ggpk archive file", ex)
                          {
                              Offset = ggpkStreamReader.BaseStream.Position
                          };
                }
            }

            return(records);
        }