public void Visit(UInt32Type type) => GenerateArray(new UInt32Array.Builder(), x => (uint)x);
/// <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(); } } }
public void Visit(UInt32Type type) => CreateIntType(type);
public void Write(UInt32Type uInt32Type, object value) => writer.Write(Convert.ToUInt32(value));
public object Read(UInt32Type uInt32Type) => reader.ReadUInt32();
public void Visit(UInt32Type type) => _array = new UInt32Array(_data);
public void Size_4bytes() { var data = new UInt32Type(0); Assert.Equal((uint)4, data.Size); }
public void Visit(UInt32Type type) => CreateNumberArray <uint>(type);
public void Visit(UInt32Type type) => GenerateArray <uint, UInt32Array>((v, n, c, nc, o) => new UInt32Array(v, n, c, nc, o));
/// <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); }
public void Visit(UInt32Type type) { ColumnDecoder = new UInt32Decoder(); }