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); } } }
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; }