]>
git.saurik.com Git - android/aapt.git/blob - ZipEntry.cpp
a0b54c2cf78d992d2e5e17155d6d837ea57c3602
   2  * Copyright (C) 2006 The Android Open Source Project 
   4  * Licensed under the Apache License, Version 2.0 (the "License"); 
   5  * you may not use this file except in compliance with the License. 
   6  * You may obtain a copy of the License at 
   8  *      http://www.apache.org/licenses/LICENSE-2.0 
  10  * Unless required by applicable law or agreed to in writing, software 
  11  * distributed under the License is distributed on an "AS IS" BASIS, 
  12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13  * See the License for the specific language governing permissions and 
  14  * limitations under the License. 
  18 // Access to entries in a Zip archive. 
  24 #include <utils/Log.h> 
  30 using namespace android
; 
  33  * Initialize a new ZipEntry structure from a FILE* positioned at a 
  34  * CentralDirectoryEntry. 
  36  * On exit, the file pointer will be at the start of the next CDE or 
  39 status_t 
ZipEntry::initFromCDE(FILE* fp
) 
  45     //LOGV("initFromCDE ---\n"); 
  48     result 
= mCDE
.read(fp
); 
  49     if (result 
!= NO_ERROR
) { 
  50         LOGD("mCDE.read failed\n"); 
  56     /* using the info in the CDE, go load up the LFH */ 
  58     if (fseek(fp
, mCDE
.mLocalHeaderRelOffset
, SEEK_SET
) != 0) { 
  59         LOGD("local header seek failed (%ld)\n", 
  60             mCDE
.mLocalHeaderRelOffset
); 
  64     result 
= mLFH
.read(fp
); 
  65     if (result 
!= NO_ERROR
) { 
  66         LOGD("mLFH.read failed\n"); 
  70     if (fseek(fp
, posn
, SEEK_SET
) != 0) 
  76      * We *might* need to read the Data Descriptor at this point and 
  77      * integrate it into the LFH.  If this bit is set, the CRC-32, 
  78      * compressed size, and uncompressed size will be zero.  In practice 
  79      * these seem to be rare. 
  81     hasDD 
= (mLFH
.mGPBitFlag 
& kUsesDataDescr
) != 0; 
  83         // do something clever 
  84         //LOGD("+++ has data descriptor\n"); 
  88      * Sanity-check the LFH.  Note that this will fail if the "kUsesDataDescr" 
  89      * flag is set, because the LFH is incomplete.  (Not a problem, since we 
  90      * prefer the CDE values.) 
  92     if (!hasDD 
&& !compareHeaders()) { 
  93         LOGW("warning: header mismatch\n"); 
  98      * If the mVersionToExtract is greater than 20, we may have an 
  99      * issue unpacking the record -- could be encrypted, compressed 
 100      * with something we don't support, or use Zip64 extensions.  We 
 101      * can defer worrying about that to when we're extracting data. 
 108  * Initialize a new entry.  Pass in the file name and an optional comment. 
 110  * Initializes the CDE and the LFH. 
 112 void ZipEntry::initNew(const char* fileName
, const char* comment
) 
 114     assert(fileName 
!= NULL 
&& *fileName 
!= '\0');  // name required 
 116     /* most fields are properly initialized by constructor */ 
 117     mCDE
.mVersionMadeBy 
= kDefaultMadeBy
; 
 118     mCDE
.mVersionToExtract 
= kDefaultVersion
; 
 119     mCDE
.mCompressionMethod 
= kCompressStored
; 
 120     mCDE
.mFileNameLength 
= strlen(fileName
); 
 122         mCDE
.mFileCommentLength 
= strlen(comment
); 
 123     mCDE
.mExternalAttrs 
= 0x81b60020;   // matches what WinZip does 
 125     if (mCDE
.mFileNameLength 
> 0) { 
 126         mCDE
.mFileName 
= new unsigned char[mCDE
.mFileNameLength
+1]; 
 127         strcpy((char*) mCDE
.mFileName
, fileName
); 
 129     if (mCDE
.mFileCommentLength 
> 0) { 
 130         /* TODO: stop assuming null-terminated ASCII here? */ 
 131         mCDE
.mFileComment 
= new unsigned char[mCDE
.mFileCommentLength
+1]; 
 132         strcpy((char*) mCDE
.mFileComment
, comment
); 
 139  * Initialize a new entry, starting with the ZipEntry from a different 
 142  * Initializes the CDE and the LFH. 
 144 status_t 
ZipEntry::initFromExternal(const ZipFile
* pZipFile
, 
 145     const ZipEntry
* pEntry
) 
 148      * Copy everything in the CDE over, then fix up the hairy bits. 
 150     memcpy(&mCDE
, &pEntry
->mCDE
, sizeof(mCDE
)); 
 152     if (mCDE
.mFileNameLength 
> 0) { 
 153         mCDE
.mFileName 
= new unsigned char[mCDE
.mFileNameLength
+1]; 
 154         if (mCDE
.mFileName 
== NULL
) 
 156         strcpy((char*) mCDE
.mFileName
, (char*)pEntry
->mCDE
.mFileName
); 
 158     if (mCDE
.mFileCommentLength 
> 0) { 
 159         mCDE
.mFileComment 
= new unsigned char[mCDE
.mFileCommentLength
+1]; 
 160         if (mCDE
.mFileComment 
== NULL
) 
 162         strcpy((char*) mCDE
.mFileComment
, (char*)pEntry
->mCDE
.mFileComment
); 
 164     if (mCDE
.mExtraFieldLength 
> 0) { 
 165         /* we null-terminate this, though it may not be a string */ 
 166         mCDE
.mExtraField 
= new unsigned char[mCDE
.mExtraFieldLength
+1]; 
 167         if (mCDE
.mExtraField 
== NULL
) 
 169         memcpy(mCDE
.mExtraField
, pEntry
->mCDE
.mExtraField
, 
 170             mCDE
.mExtraFieldLength
+1); 
 173     /* construct the LFH from the CDE */ 
 177      * The LFH "extra" field is independent of the CDE "extra", so we 
 180     assert(mLFH
.mExtraField 
== NULL
); 
 181     mLFH
.mExtraFieldLength 
= pEntry
->mLFH
.mExtraFieldLength
; 
 182     if (mLFH
.mExtraFieldLength 
> 0) { 
 183         mLFH
.mExtraField 
= new unsigned char[mLFH
.mExtraFieldLength
+1]; 
 184         if (mLFH
.mExtraField 
== NULL
) 
 186         memcpy(mLFH
.mExtraField
, pEntry
->mLFH
.mExtraField
, 
 187             mLFH
.mExtraFieldLength
+1); 
 194  * Insert pad bytes in the LFH by tweaking the "extra" field.  This will 
 195  * potentially confuse something that put "extra" data in here earlier, 
 196  * but I can't find an actual problem. 
 198 status_t 
ZipEntry::addPadding(int padding
) 
 201         return INVALID_OPERATION
; 
 203     //LOGI("HEY: adding %d pad bytes to existing %d in %s\n", 
 204     //    padding, mLFH.mExtraFieldLength, mCDE.mFileName); 
 206     if (mLFH
.mExtraFieldLength 
> 0) { 
 207         /* extend existing field */ 
 208         unsigned char* newExtra
; 
 210         newExtra 
= new unsigned char[mLFH
.mExtraFieldLength 
+ padding
]; 
 211         if (newExtra 
== NULL
) 
 213         memset(newExtra 
+ mLFH
.mExtraFieldLength
, 0, padding
); 
 214         memcpy(newExtra
, mLFH
.mExtraField
, mLFH
.mExtraFieldLength
); 
 216         delete[] mLFH
.mExtraField
; 
 217         mLFH
.mExtraField 
= newExtra
; 
 218         mLFH
.mExtraFieldLength 
+= padding
; 
 220         /* create new field */ 
 221         mLFH
.mExtraField 
= new unsigned char[padding
]; 
 222         memset(mLFH
.mExtraField
, 0, padding
); 
 223         mLFH
.mExtraFieldLength 
= padding
; 
 230  * Set the fields in the LFH equal to the corresponding fields in the CDE. 
 232  * This does not touch the LFH "extra" field. 
 234 void ZipEntry::copyCDEtoLFH(void) 
 236     mLFH
.mVersionToExtract  
= mCDE
.mVersionToExtract
; 
 237     mLFH
.mGPBitFlag         
= mCDE
.mGPBitFlag
; 
 238     mLFH
.mCompressionMethod 
= mCDE
.mCompressionMethod
; 
 239     mLFH
.mLastModFileTime   
= mCDE
.mLastModFileTime
; 
 240     mLFH
.mLastModFileDate   
= mCDE
.mLastModFileDate
; 
 241     mLFH
.mCRC32             
= mCDE
.mCRC32
; 
 242     mLFH
.mCompressedSize    
= mCDE
.mCompressedSize
; 
 243     mLFH
.mUncompressedSize  
= mCDE
.mUncompressedSize
; 
 244     mLFH
.mFileNameLength    
= mCDE
.mFileNameLength
; 
 245     // the "extra field" is independent 
 247     delete[] mLFH
.mFileName
; 
 248     if (mLFH
.mFileNameLength 
> 0) { 
 249         mLFH
.mFileName 
= new unsigned char[mLFH
.mFileNameLength
+1]; 
 250         strcpy((char*) mLFH
.mFileName
, (const char*) mCDE
.mFileName
); 
 252         mLFH
.mFileName 
= NULL
; 
 257  * Set some information about a file after we add it. 
 259 void ZipEntry::setDataInfo(long uncompLen
, long compLen
, unsigned long crc32
, 
 260     int compressionMethod
) 
 262     mCDE
.mCompressionMethod 
= compressionMethod
; 
 264     mCDE
.mCompressedSize 
= compLen
; 
 265     mCDE
.mUncompressedSize 
= uncompLen
; 
 266     mCDE
.mCompressionMethod 
= compressionMethod
; 
 267     if (compressionMethod 
== kCompressDeflated
) { 
 268         mCDE
.mGPBitFlag 
|= 0x0002;      // indicates maximum compression used 
 274  * See if the data in mCDE and mLFH match up.  This is mostly useful for 
 275  * debugging these classes, but it can be used to identify damaged 
 278  * Returns "false" if they differ. 
 280 bool ZipEntry::compareHeaders(void) const 
 282     if (mCDE
.mVersionToExtract 
!= mLFH
.mVersionToExtract
) { 
 283         LOGV("cmp: VersionToExtract\n"); 
 286     if (mCDE
.mGPBitFlag 
!= mLFH
.mGPBitFlag
) { 
 287         LOGV("cmp: GPBitFlag\n"); 
 290     if (mCDE
.mCompressionMethod 
!= mLFH
.mCompressionMethod
) { 
 291         LOGV("cmp: CompressionMethod\n"); 
 294     if (mCDE
.mLastModFileTime 
!= mLFH
.mLastModFileTime
) { 
 295         LOGV("cmp: LastModFileTime\n"); 
 298     if (mCDE
.mLastModFileDate 
!= mLFH
.mLastModFileDate
) { 
 299         LOGV("cmp: LastModFileDate\n"); 
 302     if (mCDE
.mCRC32 
!= mLFH
.mCRC32
) { 
 303         LOGV("cmp: CRC32\n"); 
 306     if (mCDE
.mCompressedSize 
!= mLFH
.mCompressedSize
) { 
 307         LOGV("cmp: CompressedSize\n"); 
 310     if (mCDE
.mUncompressedSize 
!= mLFH
.mUncompressedSize
) { 
 311         LOGV("cmp: UncompressedSize\n"); 
 314     if (mCDE
.mFileNameLength 
!= mLFH
.mFileNameLength
) { 
 315         LOGV("cmp: FileNameLength\n"); 
 318 #if 0       // this seems to be used for padding, not real data 
 319     if (mCDE
.mExtraFieldLength 
!= mLFH
.mExtraFieldLength
) { 
 320         LOGV("cmp: ExtraFieldLength\n"); 
 324     if (mCDE
.mFileName 
!= NULL
) { 
 325         if (strcmp((char*) mCDE
.mFileName
, (char*) mLFH
.mFileName
) != 0) { 
 326             LOGV("cmp: FileName\n"); 
 336  * Convert the DOS date/time stamp into a UNIX time stamp. 
 338 time_t ZipEntry::getModWhen(void) const 
 342     parts
.tm_sec 
= (mCDE
.mLastModFileTime 
& 0x001f) << 1; 
 343     parts
.tm_min 
= (mCDE
.mLastModFileTime 
& 0x07e0) >> 5; 
 344     parts
.tm_hour 
= (mCDE
.mLastModFileTime 
& 0xf800) >> 11; 
 345     parts
.tm_mday 
= (mCDE
.mLastModFileDate 
& 0x001f); 
 346     parts
.tm_mon 
= ((mCDE
.mLastModFileDate 
& 0x01e0) >> 5) -1; 
 347     parts
.tm_year 
= ((mCDE
.mLastModFileDate 
& 0xfe00) >> 9) + 80; 
 348     parts
.tm_wday 
= parts
.tm_yday 
= 0; 
 349     parts
.tm_isdst 
= -1;        // DST info "not available" 
 351     return mktime(&parts
); 
 355  * Set the CDE/LFH timestamp from UNIX time. 
 357 void ZipEntry::setModWhen(time_t when
) 
 359 #ifdef HAVE_LOCALTIME_R 
 363     unsigned short zdate
, ztime
; 
 367     /* round up to an even number of seconds */ 
 368     even 
= (time_t)(((unsigned long)(when
) + 1) & (~1)); 
 371 #ifdef HAVE_LOCALTIME_R 
 372     ptm 
= localtime_r(&even
, &tmResult
); 
 374     ptm 
= localtime(&even
); 
 382     zdate 
= (year 
- 80) << 9 | (ptm
->tm_mon
+1) << 5 | ptm
->tm_mday
; 
 383     ztime 
= ptm
->tm_hour 
<< 11 | ptm
->tm_min 
<< 5 | ptm
->tm_sec 
>> 1; 
 385     mCDE
.mLastModFileTime 
= mLFH
.mLastModFileTime 
= ztime
; 
 386     mCDE
.mLastModFileDate 
= mLFH
.mLastModFileDate 
= zdate
; 
 391  * =========================================================================== 
 392  *      ZipEntry::LocalFileHeader 
 393  * =========================================================================== 
 397  * Read a local file header. 
 399  * On entry, "fp" points to the signature at the start of the header. 
 400  * On exit, "fp" points to the start of data. 
 402 status_t 
ZipEntry::LocalFileHeader::read(FILE* fp
) 
 404     status_t result 
= NO_ERROR
; 
 405     unsigned char buf
[kLFHLen
]; 
 407     assert(mFileName 
== NULL
); 
 408     assert(mExtraField 
== NULL
); 
 410     if (fread(buf
, 1, kLFHLen
, fp
) != kLFHLen
) { 
 411         result 
= UNKNOWN_ERROR
; 
 415     if (ZipEntry::getLongLE(&buf
[0x00]) != kSignature
) { 
 416         LOGD("whoops: didn't find expected signature\n"); 
 417         result 
= UNKNOWN_ERROR
; 
 421     mVersionToExtract 
= ZipEntry::getShortLE(&buf
[0x04]); 
 422     mGPBitFlag 
= ZipEntry::getShortLE(&buf
[0x06]); 
 423     mCompressionMethod 
= ZipEntry::getShortLE(&buf
[0x08]); 
 424     mLastModFileTime 
= ZipEntry::getShortLE(&buf
[0x0a]); 
 425     mLastModFileDate 
= ZipEntry::getShortLE(&buf
[0x0c]); 
 426     mCRC32 
= ZipEntry::getLongLE(&buf
[0x0e]); 
 427     mCompressedSize 
= ZipEntry::getLongLE(&buf
[0x12]); 
 428     mUncompressedSize 
= ZipEntry::getLongLE(&buf
[0x16]); 
 429     mFileNameLength 
= ZipEntry::getShortLE(&buf
[0x1a]); 
 430     mExtraFieldLength 
= ZipEntry::getShortLE(&buf
[0x1c]); 
 432     // TODO: validate sizes 
 435     if (mFileNameLength 
!= 0) { 
 436         mFileName 
= new unsigned char[mFileNameLength
+1]; 
 437         if (mFileName 
== NULL
) { 
 441         if (fread(mFileName
, 1, mFileNameLength
, fp
) != mFileNameLength
) { 
 442             result 
= UNKNOWN_ERROR
; 
 445         mFileName
[mFileNameLength
] = '\0'; 
 448     /* grab extra field */ 
 449     if (mExtraFieldLength 
!= 0) { 
 450         mExtraField 
= new unsigned char[mExtraFieldLength
+1]; 
 451         if (mExtraField 
== NULL
) { 
 455         if (fread(mExtraField
, 1, mExtraFieldLength
, fp
) != mExtraFieldLength
) { 
 456             result 
= UNKNOWN_ERROR
; 
 459         mExtraField
[mExtraFieldLength
] = '\0'; 
 467  * Write a local file header. 
 469 status_t 
ZipEntry::LocalFileHeader::write(FILE* fp
) 
 471     unsigned char buf
[kLFHLen
]; 
 473     ZipEntry::putLongLE(&buf
[0x00], kSignature
); 
 474     ZipEntry::putShortLE(&buf
[0x04], mVersionToExtract
); 
 475     ZipEntry::putShortLE(&buf
[0x06], mGPBitFlag
); 
 476     ZipEntry::putShortLE(&buf
[0x08], mCompressionMethod
); 
 477     ZipEntry::putShortLE(&buf
[0x0a], mLastModFileTime
); 
 478     ZipEntry::putShortLE(&buf
[0x0c], mLastModFileDate
); 
 479     ZipEntry::putLongLE(&buf
[0x0e], mCRC32
); 
 480     ZipEntry::putLongLE(&buf
[0x12], mCompressedSize
); 
 481     ZipEntry::putLongLE(&buf
[0x16], mUncompressedSize
); 
 482     ZipEntry::putShortLE(&buf
[0x1a], mFileNameLength
); 
 483     ZipEntry::putShortLE(&buf
[0x1c], mExtraFieldLength
); 
 485     if (fwrite(buf
, 1, kLFHLen
, fp
) != kLFHLen
) 
 486         return UNKNOWN_ERROR
; 
 489     if (mFileNameLength 
!= 0) { 
 490         if (fwrite(mFileName
, 1, mFileNameLength
, fp
) != mFileNameLength
) 
 491             return UNKNOWN_ERROR
; 
 494     /* write "extra field" */ 
 495     if (mExtraFieldLength 
!= 0) { 
 496         if (fwrite(mExtraField
, 1, mExtraFieldLength
, fp
) != mExtraFieldLength
) 
 497             return UNKNOWN_ERROR
; 
 505  * Dump the contents of a LocalFileHeader object. 
 507 void ZipEntry::LocalFileHeader::dump(void) const 
 509     LOGD(" LocalFileHeader contents:\n"); 
 510     LOGD("  versToExt=%u gpBits=0x%04x compression=%u\n", 
 511         mVersionToExtract
, mGPBitFlag
, mCompressionMethod
); 
 512     LOGD("  modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n", 
 513         mLastModFileTime
, mLastModFileDate
, mCRC32
); 
 514     LOGD("  compressedSize=%lu uncompressedSize=%lu\n", 
 515         mCompressedSize
, mUncompressedSize
); 
 516     LOGD("  filenameLen=%u extraLen=%u\n", 
 517         mFileNameLength
, mExtraFieldLength
); 
 518     if (mFileName 
!= NULL
) 
 519         LOGD("  filename: '%s'\n", mFileName
); 
 524  * =========================================================================== 
 525  *      ZipEntry::CentralDirEntry 
 526  * =========================================================================== 
 530  * Read the central dir entry that appears next in the file. 
 532  * On entry, "fp" should be positioned on the signature bytes for the 
 533  * entry.  On exit, "fp" will point at the signature word for the next 
 534  * entry or for the EOCD. 
 536 status_t 
ZipEntry::CentralDirEntry::read(FILE* fp
) 
 538     status_t result 
= NO_ERROR
; 
 539     unsigned char buf
[kCDELen
]; 
 542     assert(mFileName 
== NULL
); 
 543     assert(mExtraField 
== NULL
); 
 544     assert(mFileComment 
== NULL
); 
 546     if (fread(buf
, 1, kCDELen
, fp
) != kCDELen
) { 
 547         result 
= UNKNOWN_ERROR
; 
 551     if (ZipEntry::getLongLE(&buf
[0x00]) != kSignature
) { 
 552         LOGD("Whoops: didn't find expected signature\n"); 
 553         result 
= UNKNOWN_ERROR
; 
 557     mVersionMadeBy 
= ZipEntry::getShortLE(&buf
[0x04]); 
 558     mVersionToExtract 
= ZipEntry::getShortLE(&buf
[0x06]); 
 559     mGPBitFlag 
= ZipEntry::getShortLE(&buf
[0x08]); 
 560     mCompressionMethod 
= ZipEntry::getShortLE(&buf
[0x0a]); 
 561     mLastModFileTime 
= ZipEntry::getShortLE(&buf
[0x0c]); 
 562     mLastModFileDate 
= ZipEntry::getShortLE(&buf
[0x0e]); 
 563     mCRC32 
= ZipEntry::getLongLE(&buf
[0x10]); 
 564     mCompressedSize 
= ZipEntry::getLongLE(&buf
[0x14]); 
 565     mUncompressedSize 
= ZipEntry::getLongLE(&buf
[0x18]); 
 566     mFileNameLength 
= ZipEntry::getShortLE(&buf
[0x1c]); 
 567     mExtraFieldLength 
= ZipEntry::getShortLE(&buf
[0x1e]); 
 568     mFileCommentLength 
= ZipEntry::getShortLE(&buf
[0x20]); 
 569     mDiskNumberStart 
= ZipEntry::getShortLE(&buf
[0x22]); 
 570     mInternalAttrs 
= ZipEntry::getShortLE(&buf
[0x24]); 
 571     mExternalAttrs 
= ZipEntry::getLongLE(&buf
[0x26]); 
 572     mLocalHeaderRelOffset 
= ZipEntry::getLongLE(&buf
[0x2a]); 
 574     // TODO: validate sizes and offsets 
 577     if (mFileNameLength 
!= 0) { 
 578         mFileName 
= new unsigned char[mFileNameLength
+1]; 
 579         if (mFileName 
== NULL
) { 
 583         if (fread(mFileName
, 1, mFileNameLength
, fp
) != mFileNameLength
) { 
 584             result 
= UNKNOWN_ERROR
; 
 587         mFileName
[mFileNameLength
] = '\0'; 
 590     /* read "extra field" */ 
 591     if (mExtraFieldLength 
!= 0) { 
 592         mExtraField 
= new unsigned char[mExtraFieldLength
+1]; 
 593         if (mExtraField 
== NULL
) { 
 597         if (fread(mExtraField
, 1, mExtraFieldLength
, fp
) != mExtraFieldLength
) { 
 598             result 
= UNKNOWN_ERROR
; 
 601         mExtraField
[mExtraFieldLength
] = '\0'; 
 605     /* grab comment, if any */ 
 606     if (mFileCommentLength 
!= 0) { 
 607         mFileComment 
= new unsigned char[mFileCommentLength
+1]; 
 608         if (mFileComment 
== NULL
) { 
 612         if (fread(mFileComment
, 1, mFileCommentLength
, fp
) != mFileCommentLength
) 
 614             result 
= UNKNOWN_ERROR
; 
 617         mFileComment
[mFileCommentLength
] = '\0'; 
 625  * Write a central dir entry. 
 627 status_t 
ZipEntry::CentralDirEntry::write(FILE* fp
) 
 629     unsigned char buf
[kCDELen
]; 
 631     ZipEntry::putLongLE(&buf
[0x00], kSignature
); 
 632     ZipEntry::putShortLE(&buf
[0x04], mVersionMadeBy
); 
 633     ZipEntry::putShortLE(&buf
[0x06], mVersionToExtract
); 
 634     ZipEntry::putShortLE(&buf
[0x08], mGPBitFlag
); 
 635     ZipEntry::putShortLE(&buf
[0x0a], mCompressionMethod
); 
 636     ZipEntry::putShortLE(&buf
[0x0c], mLastModFileTime
); 
 637     ZipEntry::putShortLE(&buf
[0x0e], mLastModFileDate
); 
 638     ZipEntry::putLongLE(&buf
[0x10], mCRC32
); 
 639     ZipEntry::putLongLE(&buf
[0x14], mCompressedSize
); 
 640     ZipEntry::putLongLE(&buf
[0x18], mUncompressedSize
); 
 641     ZipEntry::putShortLE(&buf
[0x1c], mFileNameLength
); 
 642     ZipEntry::putShortLE(&buf
[0x1e], mExtraFieldLength
); 
 643     ZipEntry::putShortLE(&buf
[0x20], mFileCommentLength
); 
 644     ZipEntry::putShortLE(&buf
[0x22], mDiskNumberStart
); 
 645     ZipEntry::putShortLE(&buf
[0x24], mInternalAttrs
); 
 646     ZipEntry::putLongLE(&buf
[0x26], mExternalAttrs
); 
 647     ZipEntry::putLongLE(&buf
[0x2a], mLocalHeaderRelOffset
); 
 649     if (fwrite(buf
, 1, kCDELen
, fp
) != kCDELen
) 
 650         return UNKNOWN_ERROR
; 
 653     if (mFileNameLength 
!= 0) { 
 654         if (fwrite(mFileName
, 1, mFileNameLength
, fp
) != mFileNameLength
) 
 655             return UNKNOWN_ERROR
; 
 658     /* write "extra field" */ 
 659     if (mExtraFieldLength 
!= 0) { 
 660         if (fwrite(mExtraField
, 1, mExtraFieldLength
, fp
) != mExtraFieldLength
) 
 661             return UNKNOWN_ERROR
; 
 665     if (mFileCommentLength 
!= 0) { 
 666         if (fwrite(mFileComment
, 1, mFileCommentLength
, fp
) != mFileCommentLength
) 
 667             return UNKNOWN_ERROR
; 
 674  * Dump the contents of a CentralDirEntry object. 
 676 void ZipEntry::CentralDirEntry::dump(void) const 
 678     LOGD(" CentralDirEntry contents:\n"); 
 679     LOGD("  versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n", 
 680         mVersionMadeBy
, mVersionToExtract
, mGPBitFlag
, mCompressionMethod
); 
 681     LOGD("  modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n", 
 682         mLastModFileTime
, mLastModFileDate
, mCRC32
); 
 683     LOGD("  compressedSize=%lu uncompressedSize=%lu\n", 
 684         mCompressedSize
, mUncompressedSize
); 
 685     LOGD("  filenameLen=%u extraLen=%u commentLen=%u\n", 
 686         mFileNameLength
, mExtraFieldLength
, mFileCommentLength
); 
 687     LOGD("  diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n", 
 688         mDiskNumberStart
, mInternalAttrs
, mExternalAttrs
, 
 689         mLocalHeaderRelOffset
); 
 691     if (mFileName 
!= NULL
) 
 692         LOGD("  filename: '%s'\n", mFileName
); 
 693     if (mFileComment 
!= NULL
) 
 694         LOGD("  comment: '%s'\n", mFileComment
);