Exemplo n.º 1
0
 public void Visit(UInt32Type type) => GenerateArray(new UInt32Array.Builder(), x => (uint)x);
Exemplo n.º 2
0
        /// <summary>
        /// Loads a NeFS item with the specified id from the archive.
        /// </summary>
        /// <param name="file">The file stream to load from.</param>
        /// <param name="archive">The NeFS archive this item is in.</param>
        /// <param name="id">The id of the item in the archive to load.</param>
        public NefsItem(FileStream file, NefsArchive archive, UInt32 id)
        {
            /* Validate inputs */
            if (file == null)
            {
                throw new ArgumentNullException("File stream required to load NeFS item.");
            }

            if (archive == null)
            {
                throw new ArgumentNullException("NeFS archive object required to load an item.");
            }

            _archive = archive;
            _id      = id;

            /* Get header entries related to this item */
            _pt1Entry = archive.Header.Part1.GetEntry(id);
            _pt2Entry = archive.Header.Part2.GetEntry(_pt1Entry);
            _pt5Entry = archive.Header.Part5.GetEntry(id);
            try
            {
                _pt6Entry = archive.Header.Part6.GetEntry(id);
            }
            catch (Exception ex)
            {
                try
                {
                    /* Some items share a part 2 entry, so try to find the corresponding part 6 entry */
                    _pt6Entry = archive.Header.Part6.GetEntry(_pt2Entry.Id);
                }
                catch (Exception ex2)
                {
                    // TODO : Handle when an item doesn't have a part 6 entry
                    _pt6Entry = archive.Header.Part6.GetEntry(0);
                }
            }

            /* Determine item type */
            _type = (_pt1Entry.OffsetToData == 0)
                ? NefsItemType.Directory
                : NefsItemType.File;

            /* Get the filename */
            _fileName = archive.Header.Part3.GetFilename(_pt2Entry.FilenameOffset);

            /* Hash the filename */
            _fileNameHash = FilePathHelper.HashStringMD5(_fileName);

            /* Get offsets */
            _dataOffset       = _pt1Entry.OffsetToData;
            _offsetIntoPt2Raw = _pt1Entry.OffsetIntoPt2Raw;
            _offsetIntoPt4Raw = _pt1Entry.OffsetIntoPt4Raw;

            /* Get extracted size */
            _extractedSize = _pt2Entry.ExtractedSize;

            /*
             * Build the file path inside this archive
             * for example: "rootDir/childDir/file.xml".
             */
            _filePathInArchive = _fileName;
            var currentItem = _pt2Entry;

            /* The root directory's id is equal to its parent directory id */
            while (currentItem.Id != currentItem.DirectoryId)
            {
                var pt1Entry = archive.Header.Part1.GetEntry(currentItem.DirectoryId);
                var dir      = archive.Header.Part2.GetEntry(pt1Entry);
                var dirName  = archive.Header.Part3.GetFilename(dir.FilenameOffset);
                _filePathInArchive = Path.Combine(dirName, _filePathInArchive);

                currentItem = dir;
            }

            /* Hash the file path in archive */
            _filePathInArchiveHash = FilePathHelper.HashStringMD5(_filePathInArchive);

            //
            // Get the compressed file chunk offsets
            //
            if (_pt1Entry.OffsetIntoPt4Raw == 0xFFFFFFFF)
            {
                // TODO : Not sure exactly what this value means yet
                // For now, just set compressed size as extracted size with not compressed chunk sizes
                _compressedSize = ExtractedSize;
            }
            else
            {
                var numChunks = (UInt32)Math.Ceiling(ExtractedSize / (double)CHUNK_SIZE);
                if (numChunks > 0)
                {
                    var        firstChunkSizeEntry = _archive.Header.Part4.Offset + _pt1Entry.OffsetIntoPt4;
                    UInt32Type chunkOffset;

                    for (int i = 0; i < numChunks; i++)
                    {
                        chunkOffset = new UInt32Type(i * 4);
                        chunkOffset.Read(file, firstChunkSizeEntry);
                        _chunkSizes.Add(chunkOffset.Value);
                    }

                    _compressedSize = _chunkSizes.Last();
                }
            }
        }
Exemplo n.º 3
0
 public void Visit(UInt32Type type) => CreateIntType(type);
Exemplo n.º 4
0
 public void Write(UInt32Type uInt32Type, object value) => writer.Write(Convert.ToUInt32(value));
Exemplo n.º 5
0
 public object Read(UInt32Type uInt32Type) => reader.ReadUInt32();
Exemplo n.º 6
0
 public void Visit(UInt32Type type) => _array    = new UInt32Array(_data);
Exemplo n.º 7
0
        public void Size_4bytes()
        {
            var data = new UInt32Type(0);

            Assert.Equal((uint)4, data.Size);
        }
Exemplo n.º 8
0
 public void Visit(UInt32Type type) => CreateNumberArray <uint>(type);
Exemplo n.º 9
0
 public void Visit(UInt32Type type) => GenerateArray <uint, UInt32Array>((v, n, c, nc, o) => new UInt32Array(v, n, c, nc, o));
Exemplo n.º 10
0
        /// <summary>
        /// Reads the header intro from an input stream. Returns a new stream that contains the
        /// header data. This stream must be disposed by the caller. If the header is encrypted, the
        /// header data is decrypted before being placed in the new stream.
        /// </summary>
        /// <param name="stream">The stream to read from.</param>
        /// <param name="offset">The offset to the header intro from the beginning of the stream.</param>
        /// <param name="p">Progress info.</param>
        /// <returns>The loaded header intro and the stream to use for the rest of the header.</returns>
        internal async Task <(NefsHeaderIntro Intro, Stream HeaderStream)> ReadHeaderIntroAsync(
            Stream stream,
            ulong offset,
            NefsProgress p)
        {
            // The decrypted stream will need to be disposed by the caller
            var             decryptedStream = new MemoryStream();
            NefsHeaderIntro intro;

            // Read magic number (first four bytes)
            stream.Seek((long)offset, SeekOrigin.Begin);
            var magicNum = new UInt32Type(0);
            await magicNum.ReadAsync(stream, offset, p);

            // Reset stream position
            stream.Seek((long)offset, SeekOrigin.Begin);

            // Check magic number
            if (magicNum.Value == NefsHeaderIntro.NefsMagicNumber)
            {
                // This is a non-encrypted NeFS header
                intro = new NefsHeaderIntro();
                await FileData.ReadDataAsync(stream, offset, intro, p);

                // Copy the entire header to the decrypted stream (nothing to decrypt)
                stream.Seek((long)offset, SeekOrigin.Begin);
                await stream.CopyPartialAsync(decryptedStream, intro.HeaderSize, p.CancellationToken);
            }
            else
            {
                // Magic number is incorrect, assume file is encrpyted
                Log.LogInformation("Header magic number mismatch, assuming header is encrypted.");

                // Encrypted headers:
                // - Headers are "encrypted" in a two-step process. RSA-1024. No padding is used.
                // - First 0x80 bytes are signed with an RSA private key (data -> decrypt ->
                // scrambled data).
                // - Must use an RSA 1024-bit public key to unscramble the data (scrambled data ->
                // encrypt -> data).
                // - For DiRT Rally 2 this public key is stored in the main executable.
                byte[] encryptedHeader = new byte[NefsHeaderIntro.Size + 1]; // TODO : Why the +1?
                await stream.ReadAsync(encryptedHeader, 0, (int)NefsHeaderIntro.Size, p.CancellationToken);

                encryptedHeader[NefsHeaderIntro.Size] = 0;

                // Use big integers instead of RSA since the c# implementation forces the use of padding.
                var n = new BigInteger(this.RsaPublicKey);
                var e = new BigInteger(this.RsaExponent);
                var m = new BigInteger(encryptedHeader);

                // Decrypt the header intro
                byte[] decrypted = BigInteger.ModPow(m, e, n).ToByteArray();
                decryptedStream.Write(decrypted, 0, decrypted.Length);

                // Fill any leftover space with zeros
                if (decrypted.Length != NefsHeaderIntro.Size)
                {
                    for (int i = 0; i < (NefsHeaderIntro.Size - decrypted.Length); i++)
                    {
                        decryptedStream.WriteByte(0);
                    }
                }

                // Read header intro data from decrypted stream
                intro = new NefsHeaderIntro(isEncrpyted: true);
                await FileData.ReadDataAsync(decryptedStream, 0, intro, p);

                // The rest of the header is encrypted using AES-256, decrypt using the key from the
                // header intro
                byte[] key        = intro.GetAesKey();
                var    headerSize = intro.HeaderSize;

                // Decrypt the rest of the header
                using (var rijAlg = new RijndaelManaged())
                {
                    rijAlg.KeySize   = 256;
                    rijAlg.Key       = key;
                    rijAlg.Mode      = CipherMode.ECB;
                    rijAlg.BlockSize = 128;
                    rijAlg.Padding   = PaddingMode.Zeros;

                    var decryptor = rijAlg.CreateDecryptor();
                    decryptedStream.Seek(0, SeekOrigin.End);

                    // Decrypt the data - make sure to leave open the base stream
                    using (var cryptoStream = new CryptoStream(stream, decryptor, CryptoStreamMode.Read, true))
                    {
                        // Decrypt data from input stream and copy to the decrypted stream
                        await cryptoStream.CopyPartialAsync(decryptedStream, headerSize, p.CancellationToken);
                    }
                }
            }

            return(intro, decryptedStream);
        }
Exemplo n.º 11
0
 public void Visit(UInt32Type type)
 {
     ColumnDecoder = new UInt32Decoder();
 }