Exemplo n.º 1
0
        private void LoadEntry(int id)
        {
            if (id < 0 || id > _files.Count - 1)
            {
                throw new Exception("Tried to read CRAF entry out of bounds");
            }
            if (_inputStream is null)
            {
                throw new Exception("Tried to load CRAF entry after reader was closed");
            }

            using (BinaryReader bin = new BinaryReader(_inputStream, Encoding.Default, true))
            {
                _inputStream.Seek(_files[id].dataOffset, SeekOrigin.Begin);

                if (_files[id].UseCompression)
                {
                    var chunkCount = ChunkCount(_files[id].uncompressedSize);

                    _files[id].chunks = new CrafChunk[chunkCount];

                    for (var j = 0; j < chunkCount; j++)
                    {
                        _files[id].chunks[j] = new CrafChunk();
                        var compressedSize = bin.ReadInt32();
                        _files[id].chunks[j].uncompressedSize = bin.ReadInt32();
                        if (MetadataEncrypted && j == 0)
                        {
                            Int64 chunkKey            = (MasterChunkKey1 * _files[id].chunkKey) + MasterChunkKey2;
                            int   compressedSizeKey   = (int)(chunkKey >> 32);
                            int   uncompressedSizeKey = (int)(chunkKey & 0xFFFFFFFF);
                            compressedSize ^= compressedSizeKey;
                            _files[id].chunks[j].uncompressedSize ^= uncompressedSizeKey;
                        }
                        _files[id].chunks[j].compressedData = bin.ReadBytes(compressedSize);
                    }
                }
                else if (_files[id].UseDataEncryption)
                {
                    var decryptor = _aes.CreateDecryptor(AESKey, _files[id].IV);
                    using (var decryptorStream = new ReuseCryptoStream(_inputStream, decryptor, CryptoStreamMode.Read))
                    {
                        _files[id].data = new byte[_files[id].uncompressedSize];
                        decryptorStream.Read(_files[id].data, 0, _files[id].uncompressedSize);
                    }
                }
                else
                {
                    _files[id].data = bin.ReadBytes(_files[id].uncompressedSize);
                }
            }
        }
Exemplo n.º 2
0
        public Task SaveAsync(string Path, IProgress <int> progress)
        {
            if (!_loaded)
            {
                throw new Exception("Tried to save CRAF before loading data");
            }
            if (_inputStream != null)
            {
                throw new Exception("Tried to save CRAF while still in read mode");
            }

            return(Task.Run(() =>
            {
                // Create mode is slow
                if (File.Exists(Path))
                {
                    File.Delete(Path);
                }
                using (FileStream file = File.OpenWrite(Path))
                {
                    using (BinaryWriter bin = new BinaryWriter(file))
                    {
                        var magic = new char[] { 'C', 'R', 'A', 'F' };
                        bin.Write(magic);
                        bin.Write(_versionMinor);
                        bin.Write(_versionMajor);
                        bin.Write(_metadataEncryption);
                        bin.Write(_files.Count);
                        bin.Write(_unk0);
                        file.Seek(0x20, SeekOrigin.Begin);
                        bin.Write(_flags);
                        bin.Write(_unk1);
                        bin.Write(_archiveKey);
                        bin.Write(_unk2);

                        uint firstEntryOffset = (uint)file.Position;

                        file.Seek(firstEntryOffset + 0x28 * _files.Count, SeekOrigin.Begin);
                        file.Seek(AlignTo(file.Position, 16), SeekOrigin.Begin);
                        // don't ask me why, but it's normally padded like that
                        file.Seek(8, SeekOrigin.Current);
                        for (var i = 0; i < _files.Count; i++)
                        {
                            var vfsPathOffset = AlignTo(file.Position, 8);
                            file.Seek(vfsPathOffset, SeekOrigin.Begin);
                            bin.WriteNullTerminatedString(_files[i].vfsPath);
                            _files[i].vfsPathOffset = (uint)vfsPathOffset;
                        }

                        // don't ask me why, but it's normally padded like that
                        file.Seek(AlignTo(file.Position, 16), SeekOrigin.Begin);
                        file.Seek(8, SeekOrigin.Current);

                        for (var i = 0; i < _files.Count; i++)
                        {
                            var pathOffset = AlignTo(file.Position, 8);
                            file.Seek(pathOffset, SeekOrigin.Begin);
                            bin.WriteNullTerminatedString(_files[i].path);
                            _files[i].pathOffset = (uint)pathOffset;
                        }

                        for (var i = 0; i < _files.Count; i++)
                        {
                            var dataOffset = AlignTo(file.Position, 0x200);
                            file.Seek(dataOffset, SeekOrigin.Begin);
                            if (_files[i].UseCompression)
                            {
                                for (var j = 0; j < _files[i].chunks.Length; j++)
                                {
                                    var compressedSize = _files[i].chunks[j].compressedData.Length;
                                    var uncompressedSize = _files[i].chunks[j].uncompressedSize;
                                    if (MetadataEncrypted && j == 0)
                                    {
                                        Int64 chunkKey = (MasterChunkKey1 * _files[i].chunkKey) + MasterChunkKey2;
                                        int compressedSizeKey = (int)(chunkKey >> 32);
                                        int uncompressedSizeKey = (int)(chunkKey & 0xFFFFFFFF);
                                        compressedSize ^= compressedSizeKey;
                                        uncompressedSize ^= uncompressedSizeKey;
                                    }
                                    bin.Write(compressedSize);
                                    bin.Write(uncompressedSize);
                                    bin.Write(_files[i].chunks[j].compressedData);
                                }
                                file.Seek(dataOffset + _files[i].totalCompressedSize, SeekOrigin.Begin); // compressed files are padded;
                            }
                            else if (_files[i].UseDataEncryption)
                            {
                                var encryptor = _aes.CreateEncryptor(AESKey, _files[i].IV);
                                using (var encryptorStream = new ReuseCryptoStream(file, encryptor, CryptoStreamMode.Write))
                                {
                                    encryptorStream.Write(_files[i].data, 0, _files[i].data.Length);
                                }
                                bin.Write(_files[i].IV);
                                for (var j = 0; j < 16; j++)
                                {
                                    bin.Write((byte)0x00);
                                }
                                bin.Write((byte)0x01);
                            }
                            else
                            {
                                bin.Write(_files[i].data);
                            }
                            _files[i].dataOffset = (uint)dataOffset;

                            // files should not directly follow one another
                            file.Seek(1, SeekOrigin.Current);

                            progress.Report(i + 1);
                        }
                        // original files are padded at the end
                        if (AlignTo(file.Position, 0x200) != file.Position)
                        {
                            file.Seek(AlignTo(file.Position, 0x200) - 1, SeekOrigin.Begin);
                            // force filesize increase
                            file.WriteByte(0);
                        }

                        file.Seek(0x10, SeekOrigin.Begin);
                        bin.Write(firstEntryOffset);
                        bin.Write(_files[0].vfsPathOffset);
                        bin.Write(_files[0].pathOffset);
                        bin.Write((int)(_files[0].dataOffset));

                        file.Seek(firstEntryOffset, SeekOrigin.Begin);
                        Int64 rollingKey = MasterArchiveKey ^ _archiveKey;
                        for (var i = 0; i < _files.Count; i++)
                        {
                            var uncompressedSize = _files[i].uncompressedSize;
                            var totalCompressedSize = _files[i].totalCompressedSize;
                            var dataOffset = _files[i].dataOffset;
                            var entryKey = _files[i].entryKey;
                            if (MetadataEncrypted)
                            {
                                Int64 fileSizeKey = (rollingKey * MasterEntryKey) ^ entryKey;
                                int uncompressedSizeKey = (int)(fileSizeKey >> 32);
                                int compressedSizeKey = (int)(fileSizeKey & 0xFFFFFFFF);
                                uncompressedSize ^= uncompressedSizeKey;
                                totalCompressedSize ^= compressedSizeKey;
                                Int64 dataOffsetKey = (fileSizeKey * MasterEntryKey) ^ ~(entryKey);
                                dataOffset ^= dataOffsetKey;

                                rollingKey = dataOffsetKey;
                            }

                            bin.Write(entryKey);
                            bin.Write(uncompressedSize);
                            bin.Write(totalCompressedSize);
                            bin.Write(_files[i].flags);
                            bin.Write(_files[i].vfsPathOffset);
                            bin.Write(dataOffset);
                            bin.Write(_files[i].pathOffset);
                            bin.Write(_files[i].unk0);
                            bin.Write(_files[i].chunkKey);
                        }
                    }
                }
            }));
        }