private void InitCompress(Stream stream, CompressionLevel level) { int flevel = (int)level; System.IO.Compression.CompressionLevel sys_level; if (0 == flevel) { sys_level = System.IO.Compression.CompressionLevel.NoCompression; } else if (flevel > 5) { sys_level = System.IO.Compression.CompressionLevel.Optimal; flevel = 3; } else { sys_level = System.IO.Compression.CompressionLevel.Fastest; flevel = 1; } int cmf = 0x7800 | flevel << 6; cmf = ((cmf + 30) / 31) * 31; stream.WriteByte((byte)(cmf >> 8)); stream.WriteByte((byte)cmf); m_stream = new DeflateStream(stream, sys_level, true); m_adler = new CheckedStream(m_stream, new Adler32()); m_writing = true; }
public override void Create(Stream output, IEnumerable <Entry> list, ResourceOptions options, EntryCallback callback) { var ypf_options = GetOptions <YpfOptions> (options); if (null == ypf_options) { throw new ArgumentException("Invalid archive creation options", "options"); } if (ypf_options.Key > 0xff) { throw new InvalidEncryptionScheme(arcStrings.MsgCreationKeyRequired); } if (0 == ypf_options.Version) { throw new InvalidFormatException(arcStrings.MsgInvalidVersion); } var scheme = new YpfScheme { SwapTable = GuessSwapTable(ypf_options.Version), Key = (byte)ypf_options.Key }; int callback_count = 0; var encoding = Encodings.cp932.WithFatalFallback(); ChecksumFunc Checksum = data => Crc32.Compute(data, 0, data.Length); uint data_offset = 0x20; var file_table = new List <YpfEntry>(); foreach (var entry in list) { try { string file_name = entry.Name; byte[] name_buf = encoding.GetBytes(file_name); if (name_buf.Length > 0xff) { throw new InvalidFileName(entry.Name, arcStrings.MsgFileNameTooLong); } uint hash = Checksum(name_buf); byte file_type = GetFileType(ypf_options.Version, file_name); for (int i = 0; i < name_buf.Length; ++i) { name_buf[i] = (byte)(name_buf[i] ^ ypf_options.Key); } file_table.Add(new YpfEntry { Name = file_name, IndexName = name_buf, NameHash = hash, FileType = file_type, IsPacked = 0 == file_type, }); data_offset += (uint)(0x17 + name_buf.Length); } catch (EncoderFallbackException X) { throw new InvalidFileName(entry.Name, arcStrings.MsgIllegalCharacters, X); } } file_table.Sort((a, b) => a.NameHash.CompareTo(b.NameHash)); output.Position = data_offset; uint current_offset = data_offset; foreach (var entry in file_table) { if (null != callback) { callback(callback_count++, entry, arcStrings.MsgAddingFile); } entry.Offset = current_offset; using (var input = File.OpenRead(entry.Name)) { var file_size = input.Length; if (file_size > uint.MaxValue || current_offset + file_size > uint.MaxValue) { throw new FileSizeException(); } entry.UnpackedSize = (uint)file_size; using (var checked_stream = new CheckedStream(output, new Adler32())) { if (entry.IsPacked) { var start = output.Position; using (var zstream = new ZLibStream(checked_stream, CompressionMode.Compress, CompressionLevel.Level9, true)) { input.CopyTo(zstream); } entry.Size = (uint)(output.Position - start); } else { input.CopyTo(checked_stream); entry.Size = entry.UnpackedSize; } checked_stream.Flush(); entry.CheckSum = checked_stream.CheckSumValue; current_offset += entry.Size; } } } if (null != callback) { callback(callback_count++, null, arcStrings.MsgWritingIndex); } output.Position = 0; using (var writer = new BinaryWriter(output, encoding, true)) { writer.Write(Signature); writer.Write(ypf_options.Version); writer.Write(file_table.Count); writer.Write(data_offset); writer.BaseStream.Seek(0x20, SeekOrigin.Begin); foreach (var entry in file_table) { writer.Write(entry.NameHash); byte name_len = (byte)~Parser.DecryptLength(scheme.SwapTable, (byte)entry.IndexName.Length); writer.Write(name_len); writer.Write(entry.IndexName); writer.Write(entry.FileType); writer.Write(entry.IsPacked); writer.Write(entry.UnpackedSize); writer.Write(entry.Size); writer.Write((uint)entry.Offset); writer.Write(entry.CheckSum); } } }
// files inside archive are aligned to 0x10 boundary. // to convert DateTime structure into entry time: // entry.FileTime = file_info.CreationTimeUtc.Ticks; // // last two bytes of archive is CRC16 of the whole file public override void Create(Stream output, IEnumerable <Entry> list, ResourceOptions options, EntryCallback callback) { const long data_offset = 0x10; var encoding = Encodings.cp932.WithFatalFallback(); int callback_count = 0; var output_list = new List <OutputEntry> (list.Count()); foreach (var entry in list) { try { string name = Path.GetFileNameWithoutExtension(entry.Name); string ext = Path.GetExtension(entry.Name); byte[] name_buf = new byte[0x15]; byte[] ext_buf = new byte[3]; encoding.GetBytes(name, 0, name.Length, name_buf, 0); if (!string.IsNullOrEmpty(ext)) { ext = ext.TrimStart('.').ToLowerInvariant(); encoding.GetBytes(ext, 0, ext.Length, ext_buf, 0); } var out_entry = new OutputEntry { Name = entry.Name, IndexName = name_buf, IndexExt = ext_buf, }; output_list.Add(out_entry); } catch (EncoderFallbackException X) { throw new InvalidFileName(entry.Name, arcStrings.MsgIllegalCharacters, X); } catch (ArgumentException X) { throw new InvalidFileName(entry.Name, arcStrings.MsgFileNameTooLong, X); } } if (null != callback) { callback(output_list.Count + 2, null, null); } output.Position = data_offset; uint current_offset = 0; foreach (var entry in output_list) { if (null != callback) { callback(callback_count++, entry, arcStrings.MsgAddingFile); } entry.FileTime = File.GetCreationTimeUtc(entry.Name).Ticks; entry.Offset = current_offset; entry.CompressionType = 0; using (var input = File.OpenRead(entry.Name)) { var size = input.Length; if (size > uint.MaxValue || current_offset + size + 0x0f > uint.MaxValue) { throw new FileSizeException(); } entry.Size = (uint)size; entry.UnpackedSize = entry.Size; using (var checked_stream = new CheckedStream(output, new Crc16())) { input.CopyTo(checked_stream); entry.HasCheckSum = true; entry.CheckSum = (ushort)checked_stream.CheckSumValue; } current_offset += (uint)size + 0x0f; current_offset &= ~0x0fu; output.Position = data_offset + current_offset; } } if (null != callback) { callback(callback_count++, null, arcStrings.MsgUpdatingIndex); } // at last, go back to directory and write offset/sizes uint index_offset = current_offset; using (var index = new BinaryWriter(output, encoding, true)) { foreach (var entry in output_list) { index.Write(entry.IndexName); index.Write(entry.IndexExt); index.Write((uint)entry.Offset); index.Write(entry.UnpackedSize); index.Write(entry.Size); index.Write(entry.CompressionType); index.Write(entry.HasCheckSum); index.Write(entry.CheckSum); index.Write(entry.FileTime); } index.BaseStream.Position = 0; index.Write(Signature); index.Write(0x03006b63); index.Write(index_offset); index.Write(output_list.Count); if (null != callback) { callback(callback_count++, null, arcStrings.MsgCalculatingChecksum); } output.Position = 0; using (var checked_stream = new CheckedStream(output, new Crc16())) { checked_stream.CopyTo(Stream.Null); index.Write((ushort)checked_stream.CheckSumValue); } } }