1 #ifndef __UCF_Handler_hpp__ 2 #define __UCF_Handler_hpp__ 1 3 4 // ================================================================================================= 5 // ADOBE SYSTEMS INCORPORATED 6 // Copyright 2002-2008 Adobe Systems Incorporated 7 // All Rights Reserved 8 // 9 // NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms 10 // of the Adobe license agreement accompanying it. 11 // ================================================================================================= 12 13 #include "XMPFiles_Impl.hpp" 14 15 // ================================================================================================= 16 /// \file UCF_Handler.hpp 17 // 18 // underlying math: 19 // __ 0 ______ 0 ______ __ 20 // | A | | A | 21 // | | | | 22 // al | | (a2l)| | 23 // x |------| b2 |------| 24 // xl | X | | B |_ 25 // b |------| (b2l)| | | 26 // | B | x2 |------| | B2 could also be 27 // bl | | x2l | X2 | | _after_ X2 28 // cd |------| cd2 |------|<' 29 // |//CD//| |//CD2/| 30 // cdx |------| cdx2 |------| 31 // cdxl|------| cdx2l|------| 32 // cdl|//////| cd2l|//////| 33 // z |------| z2|------| 34 // | | | | 35 // [zl]| Z | z2l | Z2 | 36 // h |------| h2 |------| 37 // fl | H | | H2 | f2l 38 // __ hl |______| (h2l)|______| __ 39 // 40 // fl file length pre (2 = post) 41 // numCf number of content files prior to injection 42 // numCf2 " post 43 // 44 // 45 // l length variable, all else offset 46 // [ ] variable is not needed 47 // ( ) variable is identical to left 48 // a content files prior to xmp (possibly: all) 49 // b content files behind xmp (possibly: 0) 50 // x xmp packet (possibly: 0) 51 // cd central directory 52 // h end of central directory 53 // 54 // z zip64 record and locator (if existing) 55 // 56 // general rules: 57 // the bigger A, the less rewrite effort. 58 // (also within the CD) 59 // putting XMP at the end maximizes A. 60 // 61 // bool previousXMP == x!=0 62 // 63 // (x==0) == (cdx==0) == (xl==0) == (cdxl==0) 64 // 65 // std::vector<XMP_Uns32> cdOffsetsPre; 66 // 67 // ----------------- 68 // asserts: 69 //( 1) a == a2 == 0, making these variables obsolete 70 //( 2) a2l == al, this block is not touched 71 //( 3) b2 <= b, b is only moved closer to the beginning of file 72 //( 4) b2l == bl, b does not change in size 73 //( 5) x2 >= x, b is only moved further down in the file 74 // 75 //( 6) x != 0, x2l != 0, cd != 0, cdl != 0 76 // none of these blocks is at the beginning ('mimetype' by spec), 77 // nor is any of them zero byte long 78 //( 7) h!=0, hl >= 22 header is not at the beginning, minimum size 22 79 // 80 // file size computation: 81 //( 8) al + bl + xl +cdl +hl = fl 82 //( 9) al + bl + x2l+cd2l+hl = fl2 83 // 84 //(10) ( x==0 ) <=> ( cdx == 0 ) 85 // if there's a packet in the pre-file, or there isn't 86 //(11) (x==0) => xl=0 87 //(12) (cdx==0)=> cdx=0 88 // 89 //(13) x==0 ==> b,bl,b2,b2l==0 90 // if there is no pre-xmp, B does not exist 91 //(14) x!=0 ==> al:=x, b:=x+xl, bl:=cd-b 92 // 93 // zip 64: 94 //(15) zl and z2l are basically equal, except _one_ of them is 0 : 95 // 96 //(16) b2l is indeed never different t 97 // 98 // FIXED_SIZE means the fixed (minimal) portion of a struct 99 // TOTAL_SIZE indicates, that this struct indeed has a fixed, known total length 100 // 101 // ================================================================================================= 102 103 extern XMPFileHandler* UCF_MetaHandlerCTor ( XMPFiles * parent ); 104 105 extern bool UCF_CheckFormat ( XMP_FileFormat format, 106 XMP_StringPtr filePath, 107 LFA_FileRef fileRef, 108 XMPFiles * parent ); 109 110 static const XMP_OptionBits kUCF_HandlerFlags = ( 111 kXMPFiles_CanInjectXMP | 112 kXMPFiles_CanExpand | 113 kXMPFiles_CanRewrite | 114 /* kXMPFiles_PrefersInPlace | removed, only reasonable for formats where difference is significant */ 115 kXMPFiles_AllowsOnlyXMP | 116 kXMPFiles_ReturnsRawPacket | 117 // *** kXMPFiles_AllowsSafeUpdate | 118 kXMPFiles_NeedsReadOnlyPacket //UCF/zip has checksums... 119 ); 120 121 enum { // data descriptor 122 // may or may not have a signature: 0x08074b50 123 kUCF_DD_crc32 = 0, 124 kUCF_DD_sizeCompressed = 4, 125 kUCF_DD_sizeUncompressed = 8, 126 }; 127 128 class UCF_MetaHandler : public XMPFileHandler 129 { 130 public: 131 UCF_MetaHandler ( XMPFiles * _parent ); 132 ~UCF_MetaHandler(); 133 134 void CacheFileData(); 135 void ProcessXMP(); 136 137 void UpdateFile ( bool doSafeUpdate ); 138 void WriteFile ( LFA_FileRef sourceRef, const std::string & sourcePath ); 139 140 protected: 141 const static XMP_Uns16 xmpFilenameLen = 21; 142 const static char* xmpFilename; 143 144 private: 145 class Zip64EndOfDirectory { 146 private: 147 148 public: 149 const static XMP_Uns16 o_sig = 0; // 0x06064b50 150 const static XMP_Uns16 o_size = 4; // of this, excluding leading 12 bytes 151 // == FIXED_SIZE -12, since we're never creating the extensible data sector... 152 const static XMP_Uns16 o_VersionMade = 12; 153 const static XMP_Uns16 o_VersionNeededExtr = 14; 154 const static XMP_Uns16 o_numDisk = 16; // force 0 155 const static XMP_Uns16 o_numCDDisk = 20; // force 0 156 const static XMP_Uns16 o_numCFsThisDisk = 24; 157 const static XMP_Uns16 o_numCFsTotal = 32; // force equal 158 const static XMP_Uns16 o_sizeOfCD = 40; // (regular one, not Z64) 159 const static XMP_Uns16 o_offsetCD = 48; // " 160 161 const static XMP_Int32 FIXED_SIZE = 56; 162 char fields[FIXED_SIZE]; 163 164 const static XMP_Uns32 ID = 0x06064b50; 165 Zip64EndOfDirectory(XMP_Int64 offsetCD,XMP_Int64 sizeOfCD,XMP_Uns64 numCFs)166 Zip64EndOfDirectory( XMP_Int64 offsetCD, XMP_Int64 sizeOfCD, XMP_Uns64 numCFs ) 167 { 168 memset(fields,'\0',FIXED_SIZE); 169 170 PutUns32LE(ID ,&fields[o_sig] ); 171 PutUns64LE(FIXED_SIZE - 12, &fields[o_size] ); //see above 172 PutUns16LE( 45 ,&fields[o_VersionMade] ); 173 PutUns16LE( 45 ,&fields[o_VersionNeededExtr] ); 174 // fine at 0: o_numDisk 175 // fine at 0: o_numCDDisk 176 PutUns64LE( numCFs, &fields[o_numCFsThisDisk] ); 177 PutUns64LE( numCFs, &fields[o_numCFsTotal] ); 178 PutUns64LE( sizeOfCD, &fields[o_sizeOfCD] ); 179 PutUns64LE( offsetCD, &fields[o_offsetCD] ); 180 } 181 write(LFA_FileRef file)182 void write(LFA_FileRef file) 183 { 184 XMP_Validate( ID == GetUns32LE( &this->fields[o_sig] ), "invalid header on write", kXMPErr_BadFileFormat ); 185 LFA_Write( file , fields , FIXED_SIZE ); 186 } 187 188 }; 189 190 class Zip64Locator { 191 public: 192 const static XMP_Uns16 o_sig = 0; // 0x07064b50 193 const static XMP_Uns16 o_numDiskZ64CD = 4; // force 0 194 const static XMP_Uns16 o_offsZ64EOD = 8; 195 const static XMP_Uns16 o_numDisks = 16; // set 1, tolerate 0 196 197 const static XMP_Int32 TOTAL_SIZE = 20; 198 char fields[TOTAL_SIZE]; 199 200 const static XMP_Uns32 ID = 0x07064b50; 201 Zip64Locator(XMP_Int64 offsetZ64EOD)202 Zip64Locator( XMP_Int64 offsetZ64EOD ) 203 { 204 memset(fields,'\0',TOTAL_SIZE); 205 PutUns32LE(ID, &fields[Zip64Locator::o_sig] ); 206 PutUns32LE(0, &fields[Zip64Locator::o_numDiskZ64CD] ); 207 PutUns64LE(offsetZ64EOD, &fields[Zip64Locator::o_offsZ64EOD] ); 208 PutUns32LE(1, &fields[Zip64Locator::o_numDisks] ); 209 } 210 211 // writes structure to file (starting at current position) write(LFA_FileRef file)212 void write(LFA_FileRef file) 213 { 214 XMP_Validate( ID == GetUns32LE( &this->fields[o_sig] ), "invalid header on write", kXMPErr_BadFileFormat ); 215 LFA_Write( file , fields , TOTAL_SIZE ); 216 } 217 }; 218 219 struct EndOfDirectory { 220 public: 221 const static XMP_Int32 FIXED_SIZE = 22; //32 bit type is important to not overrun on maxcomment 222 const static XMP_Uns32 ID = 0x06054b50; 223 const static XMP_Int32 COMMENT_MAX = 0xFFFF; 224 //offsets 225 const static XMP_Int32 o_CentralDirectorySize = 12; 226 const static XMP_Int32 o_CentralDirectoryOffset = 16; 227 }; 228 229 class FileHeader { 230 private: 231 //TODO intergrate in clear() release()232 void release() // avoid terminus free() since subject to a #define (mem-leak-check) 233 { 234 if (filename) delete filename; 235 if (extraField) delete extraField; 236 filename=0; 237 extraField=0; 238 } 239 240 public: 241 const static XMP_Uns32 SIG = 0x04034b50; 242 const static XMP_Uns16 kdataDescriptorFlag = 0x8; 243 244 const static XMP_Uns16 o_sig = 0; 245 const static XMP_Uns16 o_extractVersion = 4; 246 const static XMP_Uns16 o_flags = 6; 247 const static XMP_Uns16 o_compression = 8; 248 const static XMP_Uns16 o_lastmodTime = 10; 249 const static XMP_Uns16 o_lastmodDate = 12; 250 const static XMP_Uns16 o_crc32 = 14; 251 const static XMP_Uns16 o_sizeCompressed = 18; 252 const static XMP_Uns16 o_sizeUncompressed = 22; 253 const static XMP_Uns16 o_fileNameLength = 26; 254 const static XMP_Uns16 o_extraFieldLength = 28; 255 // total 30 256 257 const static int FIXED_SIZE = 30; 258 char fields[FIXED_SIZE]; 259 260 char* filename; 261 char* extraField; 262 XMP_Uns16 filenameLen; 263 XMP_Uns16 extraFieldLen; 264 clear()265 void clear() 266 { 267 this->release(); 268 memset(fields,'\0',FIXED_SIZE); 269 //arm with minimal default values: 270 PutUns32LE(0x04034b50, &fields[FileHeader::o_sig] ); 271 PutUns16LE(0x14, &fields[FileHeader::o_extractVersion] ); 272 } 273 FileHeader()274 FileHeader() : filename(0),filenameLen(0),extraField(0),extraFieldLen(0) 275 { 276 clear(); 277 }; 278 279 // reads entire *FileHeader* structure from file (starting at current position) read(LFA_FileRef file)280 void read(LFA_FileRef file) 281 { 282 this->release(); 283 284 LFA_Read( file , fields , FIXED_SIZE , true ); 285 286 XMP_Uns32 tmp32 = GetUns32LE( &this->fields[FileHeader::o_sig] ); 287 XMP_Validate( SIG == tmp32, "invalid header", kXMPErr_BadFileFormat ); 288 filenameLen = GetUns16LE( &this->fields[FileHeader::o_fileNameLength] ); 289 extraFieldLen = GetUns16LE( &this->fields[FileHeader::o_extraFieldLength] ); 290 291 // nb unlike the CDFileHeader the FileHeader will in practice never have 292 // extra fields. Reasoning: File headers never carry (their own) offsets, 293 // (un)compressed size of XMP will hardly ever reach 4 GB 294 295 if (filenameLen) { 296 filename = new char[filenameLen]; 297 LFA_Read(file,filename,filenameLen,true); 298 } 299 if (extraFieldLen) { 300 extraField = new char[extraFieldLen]; 301 LFA_Read(file,extraField,extraFieldLen,true); 302 // *** NB: this WOULD need parsing for content files that are 303 // compressed or uncompressed >4GB (VERY unlikely for XMP) 304 } 305 } 306 307 // writes structure to file (starting at current position) write(LFA_FileRef file)308 void write(LFA_FileRef file) 309 { 310 XMP_Validate( SIG == GetUns32LE( &this->fields[FileHeader::o_sig] ), "invalid header on write", kXMPErr_BadFileFormat ); 311 312 filenameLen = GetUns16LE( &this->fields[FileHeader::o_fileNameLength] ); 313 extraFieldLen = GetUns16LE( &this->fields[FileHeader::o_extraFieldLength] ); 314 315 LFA_Write( file , fields , FIXED_SIZE ); 316 if (filenameLen) LFA_Write( file, filename, filenameLen ); 317 if (extraFieldLen) LFA_Write( file, extraField,extraFieldLen ); 318 } 319 transfer(const FileHeader & orig)320 void transfer(const FileHeader &orig) 321 { 322 memcpy(fields,orig.fields,FIXED_SIZE); 323 if (orig.extraField) 324 { 325 extraFieldLen=orig.extraFieldLen; 326 extraField = new char[extraFieldLen]; 327 memcpy(extraField,orig.extraField,extraFieldLen); 328 } 329 if (orig.filename) 330 { 331 filenameLen=orig.filenameLen; 332 filename = new char[filenameLen]; 333 memcpy(filename,orig.filename,filenameLen); 334 } 335 }; 336 setXMPFilename()337 void setXMPFilename() 338 { 339 // only needed for fresh structs, thus enforcing rather than catering to memory issues 340 XMP_Enforce( (filenameLen==0) && (extraFieldLen == 0) ); 341 filenameLen = xmpFilenameLen; 342 PutUns16LE(filenameLen, &fields[FileHeader::o_fileNameLength] ); 343 filename = new char[xmpFilenameLen]; 344 memcpy(filename,"META-INF/metadata.xml",xmpFilenameLen); 345 } 346 sizeHeader()347 XMP_Uns32 sizeHeader() 348 { 349 return this->FIXED_SIZE + this->filenameLen + this->extraFieldLen; 350 } 351 sizeTotalCF()352 XMP_Uns32 sizeTotalCF() 353 { 354 //*** not zip64 bit safe yet, use only for non-large xmp packet 355 return this->sizeHeader() + GetUns32LE( &fields[FileHeader::o_sizeCompressed] ); 356 } 357 ~FileHeader()358 ~FileHeader() 359 { 360 this->release(); 361 }; 362 363 }; //class FileHeader 364 365 ////// yes, this needs an own class 366 ////// offsets must be extracted, added, modified, 367 ////// come&go depending on being >0xffffff 368 ////class extraField { 369 //// private: 370 371 372 class CDFileHeader { 373 private: release()374 void release() //*** needed or can go? 375 { 376 if (filename) delete filename; 377 if (extraField) delete extraField; 378 if (comment) delete comment; 379 filename=0; filenameLen=0; 380 extraField=0; extraFieldLen=0; 381 comment=0; commentLen=0; 382 } 383 384 const static XMP_Uns32 SIG = 0x02014b50; 385 386 public: 387 const static XMP_Uns16 o_sig = 0; //0x02014b50 388 const static XMP_Uns16 o_versionMadeBy = 4; 389 const static XMP_Uns16 o_extractVersion = 6; 390 const static XMP_Uns16 o_flags = 8; 391 const static XMP_Uns16 o_compression = 10; 392 const static XMP_Uns16 o_lastmodTime = 12; 393 const static XMP_Uns16 o_lastmodDate = 14; 394 const static XMP_Uns16 o_crc32 = 16; 395 const static XMP_Uns16 o_sizeCompressed = 20; // 16bit stub 396 const static XMP_Uns16 o_sizeUncompressed = 24; // 16bit stub 397 const static XMP_Uns16 o_fileNameLength = 28; 398 const static XMP_Uns16 o_extraFieldLength = 30; 399 const static XMP_Uns16 o_commentLength = 32; 400 const static XMP_Uns16 o_diskNo = 34; 401 const static XMP_Uns16 o_internalAttribs = 36; 402 const static XMP_Uns16 o_externalAttribs = 38; 403 const static XMP_Uns16 o_offsetLocalHeader = 42; // 16bit stub 404 // total size is 4+12+12+10+8=46 405 406 const static int FIXED_SIZE = 46; 407 char fields[FIXED_SIZE]; 408 409 // do not bet on any zero-freeness, 410 // certainly no zero termination (pascal strings), 411 // treat as data blocks 412 char* filename; 413 char* extraField; 414 char* comment; 415 XMP_Uns16 filenameLen; 416 XMP_Uns16 extraFieldLen; 417 XMP_Uns16 commentLen; 418 419 // full, real, parsed 64 bit values 420 XMP_Int64 sizeUncompressed; 421 XMP_Int64 sizeCompressed; 422 XMP_Int64 offsetLocalHeader; 423 CDFileHeader()424 CDFileHeader() : filename(0),extraField(0),comment(0),filenameLen(0), 425 extraFieldLen(0),commentLen(0),sizeUncompressed(0),sizeCompressed(0),offsetLocalHeader(0) 426 { 427 memset(fields,'\0',FIXED_SIZE); 428 //already arm with appropriate values where applicable: 429 PutUns32LE(0x02014b50, &fields[CDFileHeader::o_sig] ); 430 PutUns16LE(0x14, &fields[CDFileHeader::o_extractVersion] ); 431 }; 432 433 // copy constructor CDFileHeader(const CDFileHeader & orig)434 CDFileHeader(const CDFileHeader& orig) : filename(0),extraField(0),comment(0),filenameLen(0), 435 extraFieldLen(0),commentLen(0),sizeUncompressed(0),sizeCompressed(0),offsetLocalHeader(0) 436 { 437 memcpy(fields,orig.fields,FIXED_SIZE); 438 if (orig.extraField) 439 { 440 extraFieldLen=orig.extraFieldLen; 441 extraField = new char[extraFieldLen]; 442 memcpy(extraField , orig.extraField , extraFieldLen); 443 } 444 if (orig.filename) 445 { 446 filenameLen=orig.filenameLen; 447 filename = new char[filenameLen]; 448 memcpy(filename , orig.filename , filenameLen); 449 } 450 if (orig.comment) 451 { 452 commentLen=orig.commentLen; 453 comment = new char[commentLen]; 454 memcpy(comment , orig.comment , commentLen); 455 } 456 457 filenameLen = orig.filenameLen; 458 extraFieldLen = orig.extraFieldLen; 459 commentLen = orig.commentLen; 460 461 sizeUncompressed = orig.sizeUncompressed; 462 sizeCompressed = orig.sizeCompressed; 463 offsetLocalHeader = orig.offsetLocalHeader; 464 } 465 466 // Assignment operator operator =(const CDFileHeader & obj)467 CDFileHeader& operator=(const CDFileHeader& obj) 468 { 469 XMP_Throw("not supported",kXMPErr_Unimplemented); 470 } 471 472 // reads entire structure from file (starting at current position) read(LFA_FileRef file)473 void read(LFA_FileRef file) 474 { 475 this->release(); 476 477 LFA_Read(file,fields,FIXED_SIZE,true); 478 XMP_Validate( SIG == GetUns32LE( &this->fields[CDFileHeader::o_sig] ), "invalid header", kXMPErr_BadFileFormat ); 479 480 filenameLen = GetUns16LE( &this->fields[CDFileHeader::o_fileNameLength] ); 481 extraFieldLen = GetUns16LE( &this->fields[CDFileHeader::o_extraFieldLength] ); 482 commentLen = GetUns16LE( &this->fields[CDFileHeader::o_commentLength] ); 483 484 if (filenameLen) { 485 filename = new char[filenameLen]; 486 LFA_Read(file,filename,filenameLen,true); 487 } 488 if (extraFieldLen) { 489 extraField = new char[extraFieldLen]; 490 LFA_Read(file,extraField,extraFieldLen,true); 491 } 492 if (commentLen) { 493 comment = new char[commentLen]; 494 LFA_Read(file,comment,commentLen,true); 495 } 496 497 ////// GET ACTUAL 64 BIT VALUES ////////////////////////////////////////////// 498 // get 32bit goodies first, correct later 499 sizeUncompressed = GetUns32LE( &fields[o_sizeUncompressed] ); 500 sizeCompressed = GetUns32LE( &fields[o_sizeCompressed] ); 501 offsetLocalHeader = GetUns32LE( &fields[o_offsetLocalHeader] ); 502 503 XMP_Int32 offset = 0; 504 while ( offset < extraFieldLen ) 505 { 506 XMP_Validate( (extraFieldLen - offset) >= 4, "need 4 bytes for next header ID+len", kXMPErr_BadFileFormat); 507 XMP_Uns16 headerID = GetUns16LE( &extraField[offset] ); 508 XMP_Uns16 dataSize = GetUns16LE( &extraField[offset+2] ); 509 offset += 4; 510 511 XMP_Validate( (extraFieldLen - offset) <= dataSize, 512 "actual field lenght not given", kXMPErr_BadFileFormat); 513 if ( headerID == 0x1 ) //we only care about "Zip64 extended information extra field" 514 { 515 XMP_Validate( offset < extraFieldLen, "extra field too short", kXMPErr_BadFileFormat); 516 if (sizeUncompressed == 0xffffffff) 517 { 518 sizeUncompressed = GetUns64LE( &extraField[offset] ); 519 offset += 8; 520 } 521 if (sizeCompressed == 0xffffffff) 522 { 523 sizeCompressed = GetUns64LE( &extraField[offset] ); 524 offset += 8; 525 } 526 if (offsetLocalHeader == 0xffffffff) 527 { 528 offsetLocalHeader = GetUns64LE( &extraField[offset] ); 529 offset += 8; 530 } 531 } 532 else 533 { 534 offset += dataSize; 535 } // if 536 } // while 537 } // read() 538 539 // writes structure to file (starting at current position) write(LFA_FileRef file)540 void write(LFA_FileRef file) 541 { 542 //// WRITE BACK REAL 64 BIT VALUES, CREATE EXTRA FIELD /////////////// 543 //may only wipe extra field after obtaining all Info from it 544 if (extraField) delete extraField; 545 extraFieldLen=0; 546 547 if ( ( sizeUncompressed > 0xffffffff ) || 548 ( sizeCompressed > 0xffffffff ) || 549 ( offsetLocalHeader > 0xffffffff ) ) 550 { 551 extraField = new char[64]; // actual maxlen is 32 552 extraFieldLen = 4; //first fields are for ID, size 553 if ( sizeUncompressed > 0xffffffff ) 554 { 555 PutUns64LE( sizeUncompressed, &extraField[extraFieldLen] ); 556 extraFieldLen += 8; 557 sizeUncompressed = 0xffffffff; 558 } 559 if ( sizeCompressed > 0xffffffff ) 560 { 561 PutUns64LE( sizeCompressed, &extraField[extraFieldLen] ); 562 extraFieldLen += 8; 563 sizeCompressed = 0xffffffff; 564 } 565 if ( offsetLocalHeader > 0xffffffff ) 566 { 567 PutUns64LE( offsetLocalHeader, &extraField[extraFieldLen] ); 568 extraFieldLen += 8; 569 offsetLocalHeader = 0xffffffff; 570 } 571 572 //write ID, dataSize 573 PutUns16LE( 0x0001, &extraField[0] ); 574 PutUns16LE( extraFieldLen-4, &extraField[2] ); 575 //extraFieldSize 576 PutUns16LE( extraFieldLen, &this->fields[CDFileHeader::o_extraFieldLength] ); 577 } 578 579 // write out 32-bit ('ff-stubs' or not) 580 PutUns32LE( (XMP_Uns32)sizeUncompressed, &fields[o_sizeUncompressed] ); 581 PutUns32LE( (XMP_Uns32)sizeCompressed, &fields[o_sizeCompressed] ); 582 PutUns32LE( (XMP_Uns32)offsetLocalHeader, &fields[o_offsetLocalHeader] ); 583 584 /// WRITE ///////////////////////////////////////////////////////////////// 585 XMP_Enforce( SIG == GetUns32LE( &this->fields[CDFileHeader::o_sig] ) ); 586 587 LFA_Write( file , fields , FIXED_SIZE ); 588 if (filenameLen) LFA_Write( file, filename , filenameLen ); 589 if (extraFieldLen) LFA_Write( file, extraField , extraFieldLen ); 590 if (commentLen) LFA_Write( file, extraField , extraFieldLen ); 591 } 592 setXMPFilename()593 void setXMPFilename() 594 { 595 if (filename) delete filename; 596 filenameLen = xmpFilenameLen; 597 filename = new char[xmpFilenameLen]; 598 PutUns16LE(filenameLen, &fields[CDFileHeader::o_fileNameLength] ); 599 memcpy(filename,"META-INF/metadata.xml",xmpFilenameLen); 600 } 601 size()602 XMP_Int64 size() 603 { 604 XMP_Int64 r = this->FIXED_SIZE + this->filenameLen + this->commentLen; 605 // predict serialization size 606 if ( (sizeUncompressed > 0xffffffff)||(sizeCompressed > 0xffffffff)||(offsetLocalHeader>0xffffffff) ) 607 { 608 r += 4; //extra fields necessary 609 if (sizeUncompressed > 0xffffffff) r += 8; 610 if (sizeCompressed > 0xffffffff) r += 8; 611 if (offsetLocalHeader > 0xffffffff) r += 8; 612 } 613 return r; 614 } 615 ~CDFileHeader()616 ~CDFileHeader() 617 { 618 this->release(); 619 }; 620 }; // class CDFileHeader 621 622 class EndOfCD { 623 private: 624 const static XMP_Uns32 SIG = 0x06054b50; UCFECD_Free()625 void UCFECD_Free() 626 { 627 if(commentLen) delete comment; 628 commentLen = 0; 629 } 630 public: 631 const static XMP_Int32 o_Sig = 0; 632 const static XMP_Int32 o_CdNumEntriesDisk = 8; // same-same for UCF, since single-volume 633 const static XMP_Int32 o_CdNumEntriesTotal = 10;// must update both 634 const static XMP_Int32 o_CdSize = 12; 635 const static XMP_Int32 o_CdOffset = 16; 636 const static XMP_Int32 o_CommentLen = 20; 637 638 const static int FIXED_SIZE = 22; 639 char fields[FIXED_SIZE]; 640 641 char* comment; 642 XMP_Uns16 commentLen; 643 EndOfCD()644 EndOfCD() : comment(0), commentLen(0) 645 { 646 //nothing 647 }; 648 read(LFA_FileRef file)649 void read (LFA_FileRef file) 650 { 651 UCFECD_Free(); 652 653 LFA_Read(file,fields,FIXED_SIZE,true); 654 XMP_Validate( this->SIG == GetUns32LE( &this->fields[o_Sig] ), "invalid header", kXMPErr_BadFileFormat ); 655 656 commentLen = GetUns16LE( &this->fields[o_CommentLen] ); 657 if(commentLen) 658 { 659 comment = new char[commentLen]; 660 LFA_Read(file,comment,commentLen,true); 661 } 662 }; 663 write(LFA_FileRef file)664 void write(LFA_FileRef file) 665 { 666 XMP_Enforce( this->SIG == GetUns32LE( &this->fields[o_Sig] ) ); 667 commentLen = GetUns16LE( &this->fields[o_CommentLen] ); 668 LFA_Write( file , fields , FIXED_SIZE ); 669 if (commentLen) 670 LFA_Write ( file, comment, commentLen ); 671 } 672 ~EndOfCD()673 ~EndOfCD() 674 { 675 if (comment) delete comment; 676 }; 677 }; //class EndOfCD 678 679 //////////////////////////////////////////////////////////////////////////////////// 680 // EMBEDDING MATH 681 // 682 // a = content files before xmp (always 0 thus ommited) 683 // b/b2 = content files behind xmp (before/after injection) 684 // x/x2 = offset xmp content header + content file (before/after injection) 685 // cd/cd = central directory 686 // h/h2 = end of central directory record 687 XMP_Int64 b,b2,x,x2,cd,cd2,cdx,cdx2,z,z2,h,h2, 688 // length thereof ('2' only where possibly different) 689 // using XMP_Int64 here also for length (not XMP_Int32), 690 // to be prepared for zip64, our LFA functions might need things in multiple chunks... 691 al,bl,xl,x2l,cdl,cd2l,cdxl,cdx2l,z2l,hl,fl,f2l; 692 XMP_Uns16 numCF,numCF2; 693 694 bool wasCompressed; // ..before, false if no prior xmp 695 bool compressXMP; // compress this time? 696 bool inPlacePossible; 697 /* bool isZip64; <=> z2 != 0 */ 698 699 FileHeader xmpFileHeader; 700 CDFileHeader xmpCDHeader; 701 702 XMP_StringPtr uncomprPacketStr; 703 XMP_StringLen uncomprPacketLen; 704 XMP_StringPtr finalPacketStr; 705 XMP_StringLen finalPacketLen; 706 std::vector<CDFileHeader> cdEntries; 707 EndOfCD endOfCD; 708 void writeOut( LFA_FileRef sourceFile, LFA_FileRef targetFile, bool isRewrite, bool isInPlace); 709 710 }; // UCF_MetaHandler 711 712 // ================================================================================================= 713 714 #endif /* __UCF_Handler_hpp__ */ 715 716 717