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