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