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