void RawFileCopy(FileStream file, Xp3Entry xp3entry, Stream output, bool compress) { if (file.Length > uint.MaxValue) { throw new FileSizeException(); } uint unpacked_size = (uint)file.Length; xp3entry.UnpackedSize = (uint)unpacked_size; xp3entry.Size = (uint)unpacked_size; compress = compress && unpacked_size > 0; var segment = new Xp3Segment { IsCompressed = compress, Offset = output.Position, Size = unpacked_size, PackedSize = unpacked_size }; if (compress) { var start = output.Position; using (var zstream = new ZLibStream(output, CompressionMode.Compress, CompressionLevel.Level9, true)) { xp3entry.Hash = CheckedCopy(file, zstream); } segment.PackedSize = (uint)(output.Position - start); xp3entry.Size = segment.PackedSize; } else { xp3entry.Hash = CheckedCopy(file, output); } xp3entry.Segments.Add(segment); }
void EncryptedFileCopy(FileStream file, Xp3Entry xp3entry, Stream output, bool compress) { if (file.Length > int.MaxValue) { throw new FileSizeException(); } using (var map = MemoryMappedFile.CreateFromFile(file, null, 0, MemoryMappedFileAccess.Read, null, HandleInheritability.None, true)) { uint unpacked_size = (uint)file.Length; xp3entry.UnpackedSize = (uint)unpacked_size; xp3entry.Size = (uint)unpacked_size; using (var view = map.CreateViewAccessor(0, unpacked_size, MemoryMappedFileAccess.Read)) { var segment = new Xp3Segment { IsCompressed = compress, Offset = output.Position, Size = unpacked_size, PackedSize = unpacked_size, }; if (compress) { output = new ZLibStream(output, CompressionMode.Compress, CompressionLevel.Level9, true); } unsafe { byte[] read_buffer = new byte[81920]; byte * ptr = view.GetPointer(0); try { var checksum = new Adler32(); bool hash_after_crypt = xp3entry.Cipher.HashAfterCrypt; if (!hash_after_crypt) { xp3entry.Hash = checksum.Update(ptr, (int)unpacked_size); } int offset = 0; int remaining = (int)unpacked_size; while (remaining > 0) { int amount = Math.Min(remaining, read_buffer.Length); remaining -= amount; Marshal.Copy((IntPtr)(ptr + offset), read_buffer, 0, amount); xp3entry.Cipher.Encrypt(xp3entry, offset, read_buffer, 0, amount); if (hash_after_crypt) { checksum.Update(read_buffer, 0, amount); } output.Write(read_buffer, 0, amount); offset += amount; } if (hash_after_crypt) { xp3entry.Hash = checksum.Value; } } finally { view.SafeMemoryMappedViewHandle.ReleasePointer(); if (compress) { var dest = (output as ZLibStream).BaseStream; output.Dispose(); segment.PackedSize = (uint)(dest.Position - segment.Offset); xp3entry.Size = segment.PackedSize; } xp3entry.Segments.Add(segment); } } } } }
public override ArcFile TryOpen(ArcView file) { long base_offset = 0; if (0x5a4d == file.View.ReadUInt16(0)) // 'MZ' { base_offset = SkipExeHeader(file, s_xp3_header); } if (!file.View.BytesEqual(base_offset, s_xp3_header)) { return(null); } long dir_offset = base_offset + file.View.ReadInt64(base_offset + 0x0b); if (dir_offset < 0x13 || dir_offset >= file.MaxOffset) { return(null); } if (0x80 == file.View.ReadUInt32(dir_offset)) { dir_offset = base_offset + file.View.ReadInt64(dir_offset + 9); if (dir_offset < 0x13 || dir_offset >= file.MaxOffset) { return(null); } } int header_type = file.View.ReadByte(dir_offset); if (0 != header_type && 1 != header_type) { return(null); } Stream header_stream; if (0 == header_type) // read unpacked header { long header_size = file.View.ReadInt64(dir_offset + 1); if (header_size > uint.MaxValue) { return(null); } header_stream = file.CreateStream(dir_offset + 9, (uint)header_size); } else // read packed header { long packed_size = file.View.ReadInt64(dir_offset + 1); if (packed_size > uint.MaxValue) { return(null); } long header_size = file.View.ReadInt64(dir_offset + 9); using (var input = file.CreateStream(dir_offset + 17, (uint)packed_size)) header_stream = ZLibCompressor.DeCompress(input); } var crypt_algorithm = new Lazy <ICrypt> (() => QueryCryptAlgorithm(file), false); var dir = new List <Entry>(); dir_offset = 0; using (var header = new BinaryReader(header_stream, Encoding.Unicode)) using (var filename_map = new FilenameMap()) { while (-1 != header.PeekChar()) { uint entry_signature = header.ReadUInt32(); long entry_size = header.ReadInt64(); if (entry_size < 0) { return(null); } dir_offset += 12 + entry_size; if (0x656C6946 == entry_signature) // "File" { var entry = new Xp3Entry(); while (entry_size > 0) { uint section = header.ReadUInt32(); long section_size = header.ReadInt64(); entry_size -= 12; if (section_size > entry_size) { break; } entry_size -= section_size; long next_section_pos = header.BaseStream.Position + section_size; switch (section) { case 0x6f666e69: // "info" if (entry.Size != 0 || !string.IsNullOrEmpty(entry.Name)) { goto NextEntry; // ambiguous entry, ignore } entry.IsEncrypted = 0 != header.ReadUInt32(); long file_size = header.ReadInt64(); long packed_size = header.ReadInt64(); if (file_size >= uint.MaxValue || packed_size > uint.MaxValue || packed_size > file.MaxOffset) { goto NextEntry; } entry.IsPacked = file_size != packed_size; entry.Size = (uint)packed_size; entry.UnpackedSize = (uint)file_size; if (entry.IsEncrypted || ForceEncryptionQuery) { entry.Cipher = crypt_algorithm.Value; } else { entry.Cipher = NoCryptAlgorithm; } var name = entry.Cipher.ReadName(header); if (null == name) { goto NextEntry; } if (entry.Cipher.ObfuscatedIndex && ObfuscatedPathRe.IsMatch(name)) { goto NextEntry; } if (filename_map.Count > 0) { name = filename_map.Get(entry.Hash, name); } if (name.Length > 0x100) { goto NextEntry; } entry.Name = name; entry.Type = FormatCatalog.Instance.GetTypeFromName(name); entry.IsEncrypted = !(entry.Cipher is NoCrypt) && !(entry.Cipher.StartupTjsNotEncrypted && "startup.tjs" == name); break; case 0x6d676573: // "segm" int segment_count = (int)(section_size / 0x1c); if (segment_count > 0) { for (int i = 0; i < segment_count; ++i) { bool compressed = 0 != header.ReadInt32(); long segment_offset = base_offset + header.ReadInt64(); long segment_size = header.ReadInt64(); long segment_packed_size = header.ReadInt64(); if (segment_offset > file.MaxOffset || segment_packed_size > file.MaxOffset) { goto NextEntry; } var segment = new Xp3Segment { IsCompressed = compressed, Offset = segment_offset, Size = (uint)segment_size, PackedSize = (uint)segment_packed_size }; entry.Segments.Add(segment); } entry.Offset = entry.Segments.First().Offset; } break; case 0x726c6461: // "adlr" if (4 == section_size) { entry.Hash = header.ReadUInt32(); } break; default: // unknown section break; } header.BaseStream.Position = next_section_pos; } if (!string.IsNullOrEmpty(entry.Name) && entry.Segments.Any()) { if (entry.Cipher.ObfuscatedIndex) { DeobfuscateEntry(entry); } dir.Add(entry); } } else if (entry_size > 7) { // 0x6E666E68 == entry_signature // "hnfn" // 0x6C696D73 == entry_signature // "smil" // 0x46696C65 == entry_signature // "eliF" // 0x757A7559 == entry_signature // "Yuzu" uint hash = header.ReadUInt32(); int name_size = header.ReadInt16(); if (name_size > 0) { entry_size -= 6; if (name_size * 2 <= entry_size) { var filename = new string (header.ReadChars(name_size)); filename_map.Add(hash, filename); } } } NextEntry: header.BaseStream.Position = dir_offset; } } if (0 == dir.Count) { return(null); } var arc = new ArcFile(file, this, dir); try { if (crypt_algorithm.IsValueCreated) { crypt_algorithm.Value.Init(arc); } return(arc); } catch { arc.Dispose(); throw; } }