Mes documents/Visual Studio 2005/Projects/TES4ModTranslator/TES4ModTranslator/Zip/ZipOutputStream.cs

Go to the documentation of this file.
00001 // ZipOutputStream.cs
00002 //
00003 // Copyright (C) 2001 Mike Krueger
00004 // Copyright (C) 2004 John Reilly
00005 //
00006 // This file was translated from java, it was part of the GNU Classpath
00007 // Copyright (C) 2001 Free Software Foundation, Inc.
00008 //
00009 // This program is free software; you can redistribute it and/or
00010 // modify it under the terms of the GNU General Public License
00011 // as published by the Free Software Foundation; either version 2
00012 // of the License, or (at your option) any later version.
00013 //
00014 // This program is distributed in the hope that it will be useful,
00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017 // GNU General Public License for more details.
00018 //
00019 // You should have received a copy of the GNU General Public License
00020 // along with this program; if not, write to the Free Software
00021 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00022 //
00023 // Linking this library statically or dynamically with other modules is
00024 // making a combined work based on this library.  Thus, the terms and
00025 // conditions of the GNU General Public License cover the whole
00026 // combination.
00027 // 
00028 // As a special exception, the copyright holders of this library give you
00029 // permission to link this library with independent modules to produce an
00030 // executable, regardless of the license terms of these independent
00031 // modules, and to copy and distribute the resulting executable under
00032 // terms of your choice, provided that you also meet, for each linked
00033 // independent module, the terms and conditions of the license of that
00034 // module.  An independent module is a module which is not derived from
00035 // or based on this library.  If you modify this library, you may extend
00036 // this exception to your version of the library, but you are not
00037 // obligated to do so.  If you do not wish to do so, delete this
00038 // exception statement from your version.
00039 
00040 using System;
00041 using System.IO;
00042 using System.Collections;
00043 using System.Text;
00044 
00045 using ICSharpCode.SharpZipLib.Checksums;
00046 using ICSharpCode.SharpZipLib.Zip.Compression;
00047 using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
00048 
00049 namespace ICSharpCode.SharpZipLib.Zip
00050 {
00099         public class ZipOutputStream : DeflaterOutputStream
00100         {
00101                 private ArrayList entries  = new ArrayList();
00102                 private Crc32     crc      = new Crc32();
00103                 private ZipEntry  curEntry = null;
00104                 
00105                 int defaultCompressionLevel = Deflater.DEFAULT_COMPRESSION;
00106                 CompressionMethod curMethod = CompressionMethod.Deflated;
00107 
00108                 
00109                 private long size;
00110                 private long offset = 0;
00111                 
00112                 private byte[] zipComment = new byte[0];
00113                 
00118                 public bool IsFinished {
00119                         get {
00120                                 return entries == null;
00121                         }
00122                 }
00123 
00130                 public ZipOutputStream(Stream baseOutputStream) : base(baseOutputStream, new Deflater(Deflater.DEFAULT_COMPRESSION, true))
00131                 {
00132                 }
00133                 
00143                 public void SetComment(string comment)
00144                 {
00145                         byte[] commentBytes = ZipConstants.ConvertToArray(comment);
00146                         if (commentBytes.Length > 0xffff) {
00147                                 throw new ArgumentOutOfRangeException("comment");
00148                         }
00149                         zipComment = commentBytes;
00150                 }
00151                 
00160                 public void SetLevel(int level)
00161                 {
00162                         defaultCompressionLevel = level;
00163                         def.SetLevel(level);
00164                 }
00165                 
00170                 public int GetLevel()
00171                 {
00172                         return def.GetLevel();
00173                 }
00174                 
00178                 private  void WriteLeShort(int value)
00179                 {
00180                         baseOutputStream.WriteByte((byte)(value & 0xff));
00181                         baseOutputStream.WriteByte((byte)((value >> 8) & 0xff));
00182                 }
00183                 
00187                 private void WriteLeInt(int value)
00188                 {
00189                         WriteLeShort(value);
00190                         WriteLeShort(value >> 16);
00191                 }
00192                 
00196                 private void WriteLeLong(long value)
00197                 {
00198                         WriteLeInt((int)value);
00199                         WriteLeInt((int)(value >> 32));
00200                 }
00201                 
00202                 
00203                 bool patchEntryHeader = false;
00204                 
00205                 long headerPatchPos   = -1;
00206 
00228                 public void PutNextEntry(ZipEntry entry)
00229                 {
00230                         if (entries == null) {
00231                                 throw new InvalidOperationException("ZipOutputStream was finished");
00232                         }
00233                         
00234                         if (curEntry != null) {
00235                                 CloseEntry();
00236                         }
00237 
00238                         if (entries.Count >= 0xffff) {
00239                                 throw new ZipException("Too many entries for Zip file");
00240                         }
00241                         
00242                         CompressionMethod method = entry.CompressionMethod;
00243                         int compressionLevel = defaultCompressionLevel;
00244                         
00245                         entry.Flags = 0;
00246                         patchEntryHeader = false;
00247                         bool headerInfoAvailable = true;
00248                         
00249                         if (method == CompressionMethod.Stored) {
00250                                 if (entry.CompressedSize >= 0) {
00251                                         if (entry.Size < 0) {
00252                                                 entry.Size = entry.CompressedSize;
00253                                         } else if (entry.Size != entry.CompressedSize) {
00254                                                 throw new ZipException("Method STORED, but compressed size != size");
00255                                         }
00256                                 } else {
00257                                         if (entry.Size >= 0) {
00258                                                 entry.CompressedSize = entry.Size;
00259                                         }
00260                                 }
00261                                         
00262                                 if (entry.Size < 0 || entry.Crc < 0) {
00263                                         if (CanPatchEntries == true) {
00264                                                 headerInfoAvailable = false;
00265                                         }
00266                                         else {
00267                   // Cant patch entries so storing is not possible.
00268                                                 method = CompressionMethod.Deflated;
00269                                                 compressionLevel = 0;
00270                                         }
00271                                 }
00272                         }
00273                                 
00274                         if (method == CompressionMethod.Deflated) {
00275                                 if (entry.Size == 0) {
00276                // No need to compress - no data.
00277                                         entry.CompressedSize = entry.Size;
00278                                         entry.Crc = 0;
00279                                         method = CompressionMethod.Stored;
00280                                 } else if (entry.CompressedSize < 0 || entry.Size < 0 || entry.Crc < 0) {
00281                                         headerInfoAvailable = false;
00282                                 }
00283                         }
00284                         
00285                         if (headerInfoAvailable == false) {
00286                                 if (CanPatchEntries == false) {
00287                                         entry.Flags |= 8;
00288                                 } else {
00289                                         patchEntryHeader = true;
00290                                 }
00291                         }
00292                         
00293                         if (Password != null) {
00294                                 entry.IsCrypted = true;
00295                                 if (entry.Crc < 0) {
00296                // Need to append data descriptor as crc is used for encryption and its not known.
00297                                         entry.Flags |= 8;
00298                                 }
00299                         }
00300                         entry.Offset = (int)offset;
00301                         entry.CompressionMethod = (CompressionMethod)method;
00302                         
00303                         curMethod    = method;
00304                         
00305                         // Write the local file header
00306                         WriteLeInt(ZipConstants.LOCSIG);
00307                         
00308                         WriteLeShort(entry.Version);
00309                         WriteLeShort(entry.Flags);
00310                         WriteLeShort((byte)method);
00311                         WriteLeInt((int)entry.DosTime);
00312                         if (headerInfoAvailable == true) {
00313                                 WriteLeInt((int)entry.Crc);
00314                                 WriteLeInt(entry.IsCrypted ? (int)entry.CompressedSize + ZipConstants.CRYPTO_HEADER_SIZE : (int)entry.CompressedSize);
00315                                 WriteLeInt((int)entry.Size);
00316                         } else {
00317                                 if (patchEntryHeader == true) {
00318                                         headerPatchPos = baseOutputStream.Position;
00319                                 }
00320                                 WriteLeInt(0);  // Crc
00321                                 WriteLeInt(0);  // Compressed size
00322                                 WriteLeInt(0);  // Uncompressed size
00323                         }
00324                         
00325                         byte[] name = ZipConstants.ConvertToArray(entry.Name);
00326                         
00327                         if (name.Length > 0xFFFF) {
00328                                 throw new ZipException("Entry name too long.");
00329                         }
00330 
00331                         byte[] extra = entry.ExtraData;
00332                         if (extra == null) {
00333                                 extra = new byte[0];
00334                         }
00335 
00336                         if (extra.Length > 0xFFFF) {
00337                                 throw new ZipException("Extra data too long.");
00338                         }
00339                         
00340                         WriteLeShort(name.Length);
00341                         WriteLeShort(extra.Length);
00342                         baseOutputStream.Write(name, 0, name.Length);
00343                         baseOutputStream.Write(extra, 0, extra.Length);
00344                         
00345                         offset += ZipConstants.LOCHDR + name.Length + extra.Length;
00346                         
00347                         // Activate the entry.
00348                         curEntry = entry;
00349                         crc.Reset();
00350                         if (method == CompressionMethod.Deflated) {
00351                                 def.Reset();
00352                                 def.SetLevel(compressionLevel);
00353                         }
00354                         size = 0;
00355                         
00356                         if (entry.IsCrypted == true) {
00357                                 if (entry.Crc < 0) {                    // so testing Zip will says its ok
00358                                         WriteEncryptionHeader(entry.DosTime << 16);
00359                                 } else {
00360                                         WriteEncryptionHeader(entry.Crc);
00361                                 }
00362                         }
00363                 }
00364                 
00374                 public void CloseEntry()
00375                 {
00376                         if (curEntry == null) {
00377                                 throw new InvalidOperationException("No open entry");
00378                         }
00379                         
00380                         // First finish the deflater, if appropriate
00381                         if (curMethod == CompressionMethod.Deflated) {
00382                                 base.Finish();
00383                         }
00384                         
00385                         long csize = curMethod == CompressionMethod.Deflated ? def.TotalOut : size;
00386                         
00387                         if (curEntry.Size < 0) {
00388                                 curEntry.Size = size;
00389                         } else if (curEntry.Size != size) {
00390                                 throw new ZipException("size was " + size + ", but I expected " + curEntry.Size);
00391                         }
00392                         
00393                         if (curEntry.CompressedSize < 0) {
00394                                 curEntry.CompressedSize = csize;
00395                         } else if (curEntry.CompressedSize != csize) {
00396                                 throw new ZipException("compressed size was " + csize + ", but I expected " + curEntry.CompressedSize);
00397                         }
00398                         
00399                         if (curEntry.Crc < 0) {
00400                                 curEntry.Crc = crc.Value;
00401                         } else if (curEntry.Crc != crc.Value) {
00402                                 throw new ZipException("crc was " + crc.Value + ", but I expected " + curEntry.Crc);
00403                         }
00404                         
00405                         offset += csize;
00406 
00407                         if (offset > 0xffffffff) {
00408                                 throw new ZipException("Maximum Zip file size exceeded");
00409                         }
00410                                 
00411                         if (curEntry.IsCrypted == true) {
00412                                 curEntry.CompressedSize += ZipConstants.CRYPTO_HEADER_SIZE;
00413                         }
00414                                 
00415                         // Patch the header if possible
00416                         if (patchEntryHeader == true) {
00417                                 long curPos = baseOutputStream.Position;
00418                                 baseOutputStream.Seek(headerPatchPos, SeekOrigin.Begin);
00419                                 WriteLeInt((int)curEntry.Crc);
00420                                 WriteLeInt((int)curEntry.CompressedSize);
00421                                 WriteLeInt((int)curEntry.Size);
00422                                 baseOutputStream.Seek(curPos, SeekOrigin.Begin);
00423                                 patchEntryHeader = false;
00424                         }
00425 
00426                         // Add data descriptor if flagged as required
00427                         if ((curEntry.Flags & 8) != 0) {
00428                                 WriteLeInt(ZipConstants.EXTSIG);
00429                                 WriteLeInt((int)curEntry.Crc);
00430                                 WriteLeInt((int)curEntry.CompressedSize);
00431                                 WriteLeInt((int)curEntry.Size);
00432                                 offset += ZipConstants.EXTHDR;
00433                         }
00434                         
00435                         entries.Add(curEntry);
00436                         curEntry = null;
00437                 }
00438                 
00439                 void WriteEncryptionHeader(long crcValue)
00440                 {
00441                         offset += ZipConstants.CRYPTO_HEADER_SIZE;
00442                         
00443                         InitializePassword(Password);
00444                         
00445                         byte[] cryptBuffer = new byte[ZipConstants.CRYPTO_HEADER_SIZE];
00446                         Random rnd = new Random();
00447                         rnd.NextBytes(cryptBuffer);
00448                         cryptBuffer[11] = (byte)(crcValue >> 24);
00449                         
00450                         EncryptBlock(cryptBuffer, 0, cryptBuffer.Length);
00451                         baseOutputStream.Write(cryptBuffer, 0, cryptBuffer.Length);
00452                 }
00453                 
00463                 public override void Write(byte[] b, int off, int len)
00464                 {
00465                         if (curEntry == null) {
00466                                 throw new InvalidOperationException("No open entry.");
00467                         }
00468                         
00469                         if (len <= 0)
00470                                 return;
00471                         
00472                         crc.Update(b, off, len);
00473                         size += len;
00474                         
00475                         if (size > 0xffffffff || size < 0) {
00476                                 throw new ZipException("Maximum entry size exceeded");
00477                         }
00478                                 
00479 
00480                         switch (curMethod) {
00481                                 case CompressionMethod.Deflated:
00482                                         base.Write(b, off, len);
00483                                         break;
00484                                 
00485                                 case CompressionMethod.Stored:
00486                                         if (Password != null) {
00487                                                 byte[] buf = new byte[len];
00488                                                 Array.Copy(b, off, buf, 0, len);
00489                                                 EncryptBlock(buf, 0, len);
00490                                                 baseOutputStream.Write(buf, off, len);
00491                                         } else {
00492                                                 baseOutputStream.Write(b, off, len);
00493                                         }
00494                                         break;
00495                         }
00496                 }
00497                 
00512                 public override void Finish()
00513                 {
00514                         if (entries == null)  {
00515                                 return;
00516                         }
00517                         
00518                         if (curEntry != null) {
00519                                 CloseEntry();
00520                         }
00521                         
00522                         int numEntries = 0;
00523                         int sizeEntries = 0;
00524                         
00525                         foreach (ZipEntry entry in entries) {
00526                                 CompressionMethod method = entry.CompressionMethod;
00527                                 WriteLeInt(ZipConstants.CENSIG); 
00528                                 WriteLeShort(ZipConstants.VERSION_MADE_BY);
00529                                 WriteLeShort(entry.Version);
00530                                 WriteLeShort(entry.Flags);
00531                                 WriteLeShort((short)method);
00532                                 WriteLeInt((int)entry.DosTime);
00533                                 WriteLeInt((int)entry.Crc);
00534                                 WriteLeInt((int)entry.CompressedSize);
00535                                 WriteLeInt((int)entry.Size);
00536                                 
00537                                 byte[] name = ZipConstants.ConvertToArray(entry.Name);
00538                                 
00539                                 if (name.Length > 0xffff) {
00540                                         throw new ZipException("Name too long.");
00541                                 }
00542                                 
00543                                 byte[] extra = entry.ExtraData;
00544                                 if (extra == null) {
00545                                         extra = new byte[0];
00546                                 }
00547                                 
00548                                 byte[] entryComment = entry.Comment != null ? ZipConstants.ConvertToArray(entry.Comment) : new byte[0];
00549                                 if (entryComment.Length > 0xffff) {
00550                                         throw new ZipException("Comment too long.");
00551                                 }
00552                                 
00553                                 WriteLeShort(name.Length);
00554                                 WriteLeShort(extra.Length);
00555                                 WriteLeShort(entryComment.Length);
00556                                 WriteLeShort(0);        // disk number
00557                                 WriteLeShort(0);        // internal file attr
00558                                                                         // external file attribute
00559 
00560                                 if (entry.ExternalFileAttributes != -1) {
00561                                         WriteLeInt(entry.ExternalFileAttributes);
00562                                 } else {
00563                                         if (entry.IsDirectory) {                         // mark entry as directory (from nikolam.AT.perfectinfo.com)
00564                                                 WriteLeInt(16);
00565                                         } else {
00566                                                 WriteLeInt(0);
00567                                         }
00568                                 }
00569 
00570                                 WriteLeInt(entry.Offset);
00571                                 
00572                                 baseOutputStream.Write(name,    0, name.Length);
00573                                 baseOutputStream.Write(extra,   0, extra.Length);
00574                                 baseOutputStream.Write(entryComment, 0, entryComment.Length);
00575                                 ++numEntries;
00576                                 sizeEntries += ZipConstants.CENHDR + name.Length + extra.Length + entryComment.Length;
00577                         }
00578                         
00579                         WriteLeInt(ZipConstants.ENDSIG);
00580                         WriteLeShort(0);                    // number of this disk
00581                         WriteLeShort(0);                    // no of disk with start of central dir
00582                         WriteLeShort(numEntries);           // entries in central dir for this disk
00583                         WriteLeShort(numEntries);           // total entries in central directory
00584                         WriteLeInt(sizeEntries);            // size of the central directory
00585                         WriteLeInt((int)offset);            // offset of start of central dir
00586                         WriteLeShort(zipComment.Length);
00587                         baseOutputStream.Write(zipComment, 0, zipComment.Length);
00588                         baseOutputStream.Flush();
00589                         entries = null;
00590                 }
00591         }
00592 }

Generated on Fri Jun 23 21:50:05 2006 for OblivionModTranslator by  doxygen 1.4.6-NO