1 /*! \file	helper.cpp
2  *	\brief	Verious helper functions
3  *
4  *	\version $Id: helper.cpp,v 1.20 2008/03/25 17:05:33 matt-beard Exp $
5  *
6  */
7 /*
8  *	Copyright (c) 2003, Matt Beard
9  *
10  *	This software is provided 'as-is', without any express or implied warranty.
11  *	In no event will the authors be held liable for any damages arising from
12  *	the use of this software.
13  *
14  *	Permission is granted to anyone to use this software for any purpose,
15  *	including commercial applications, and to alter it and redistribute it
16  *	freely, subject to the following restrictions:
17  *
18  *	  1. The origin of this software must not be misrepresented; you must
19  *	     not claim that you wrote the original software. If you use this
20  *	     software in a product, an acknowledgment in the product
21  *	     documentation would be appreciated but is not required.
22  *
23  *	  2. Altered source versions must be plainly marked as such, and must
24  *	     not be misrepresented as being the original software.
25  *
26  *	  3. This notice may not be removed or altered from any source
27  *	     distribution.
28  */
29 
30 #include <mxflib/mxflib.h>
31 
32 using namespace mxflib;
33 
34 // Define the features bitmap - turn on those features set by compile time switch
35 UInt64 mxflib::Features = MXFLIB_FEATURE_DEFAULT & MXFLIB_FEATURE_MASK;
36 
37 
38 namespace
39 {
40 	//! Data bytes for the null UL used as a magic number when no UL is specified for some function parameters
41 	const UInt8 Null_UL_Data[16] = { 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0 };
42 }
43 //! Define the null UL used as a magic number when no UL is specified for some function parameters
44 const UL mxflib::Null_UL(Null_UL_Data);
45 
46 
47 //! Build a BER length
48 /*! \param Data		A pointer to the buffer to receive the length
49  *	\param MaxSize	The maximum length that can be written to the buffer
50  *	\param Length	The length to be converted to BER
51  *	\param Size		The total number of bytes to use for BER length (or 0 for auto)
52  *	\return The number of bytes written
53  *	\note If the size is specified it will be overridden for lengths that will not fit in Size,
54  *        <b>providing</b> they will fit in MaxSize. However an error message will be produced.
55  */
MakeBER(UInt8 * Data,int MaxSize,UInt64 Length,UInt32 Size)56 UInt32 mxflib::MakeBER(UInt8 *Data, int MaxSize, UInt64 Length, UInt32 Size /*=0*/)
57 {
58 	// Mask showing forbidden bits for various sizes
59 	static const UInt64 Masks[9] = { UINT64_C(0xffffffffffffff80), UINT64_C(0xffffffffffffff00),
60 									 UINT64_C(0xffffffffffff0000), UINT64_C(0xffffffffff000000),
61 									 UINT64_C(0xffffffff00000000), UINT64_C(0xffffff0000000000),
62 									 UINT64_C(0xffff000000000000), UINT64_C(0xff00000000000000), 0 };
63 	if(Size > 9)
64 	{
65 		error("Maximum BER size is 9 bytes, however %d bytes specified in call to MakeBER()\n", Size);
66 		Size = 9;
67 	}
68 
69 	// Validate size
70 	if(Size)
71 	{
72 		if(Length & Masks[Size-1])
73 		{
74 			error("BER size specified in call to MakeBER() is %d, however length 0x%s will not fit in that size\n",
75 				  Size, Int64toHexString(Length).c_str());
76 
77 			// Force a new size to be chosen
78 			Size = 0;
79 		}
80 	}
81 
82 	// Determine the best BER size
83 	if(Size == 0)
84 	{
85 		if(Length < 0x01000000) Size = 4;
86 		else if(Length < UINT64_C(0x0100000000000000)) Size = 8;
87 		else Size = 9;
88 	}
89 
90 	if(Size >(UInt32) MaxSize)
91 	{
92 		error("Buffer size given to MakeBER() is %d, however length 0x%s will not fit in that size\n",
93 			  MaxSize, Int64toHexString(Length).c_str());
94 
95 		// This will produce an invalid size!!!!
96 		Size = MaxSize;
97 	}
98 
99 	// Shortform encoding
100 	if(Size == 1)
101 	{
102 		Data[0] =(UInt8) Length;
103 		return 1;
104 	}
105 
106 	// Buffer for building BER
107 	Data[0] = 0x80 + (Size-1);
108 
109 	// Subscript to write next byte
110 	int i = Size-1;
111 
112 	// More speed efficient to write backwards as no need to locate the start
113 	while(i)
114 	{
115 		Data[i] = (UInt8)Length & 0xff;
116 		Length >>= 8;
117 		i--;
118 	}
119 
120 	// Return the number of bytes written
121 	return Size;
122 }
123 
124 
125 //! Read a BER length
126 /*! \param Data is a pointer to a pointer to the data so that the pointer will be updated to point to the first byte <b>after</b> the length.
127  *  \param MaxSize is the maximum number of bytes available to read the BER length. This function never reads from more than 9 bytes as SMPTE 377M forbids vast BER lengths.
128  *  \return The length, or -1 if the data was not a valid BER length
129  *  \note MaxSize is signed to allow calling code to end up with -ve available bytes!
130  */
ReadBER(UInt8 ** Data,int MaxSize)131 Length mxflib::ReadBER(UInt8 **Data, int MaxSize)
132 {
133 	if(MaxSize <= 0) return -1;
134 
135 	// Read the short-form length, or number of bytes for long-form
136 	int Bytes = *((*Data)++);
137 
138 	// Return short-form length
139 	if(Bytes < 128) return (Length)Bytes;
140 
141 	// 0x80 is not valid for MXF lengths (it is "length not specified" in BER)
142 	if(Bytes == 128) return -1;
143 
144 	// Now we have the byte count
145 	Bytes -= 128;
146 
147 	// Don't read passed the end of the available data!
148 	// (We use >= not > as we have already processed one byte)
149 	if(Bytes >= MaxSize) return -1;
150 
151 	// Read in each byte
152 	Length Ret = 0;
153 	while(Bytes--)
154 	{
155 		Ret <<= 8;
156 		Ret += *((*Data)++);
157 	}
158 
159 	return Ret;
160 }
161 
162 
163 //! Encode a UInt64 as a BER OID subid (7 bits per byte)
164 //! length > 0: length is maximum length of subid
165 //! length == 0: as long as necessary
166 //! length < 0: -length is EXACT length of subid
167 //! returns number of bytes UNUSED (-ve is error)
EncodeOID(UInt8 * presult,UInt64 subid,int length)168 int mxflib::EncodeOID( UInt8* presult, UInt64 subid, int length )
169 {
170 	UInt8 rev[10];			// intermediate result (reverse byte order)
171 	UInt8 *prev = rev;
172 	int count = 0;			// bytes required to represent
173 
174 	do
175 	{
176 		*prev++ = (UInt8)(subid & 0x7f) | 0x80; // set msb of every byte
177 		subid >>= 7;
178 		count++;
179 	}
180 	while( subid );
181 
182 	rev[0] &= 0x7f; // clear msb of least significant byte
183 
184 	if( length>0 && count<=length )
185 	{
186 		do *presult++ = *--prev; while( --count );		// copy result
187 		return length-count;
188 	}
189 	else if( length<0 )
190 	{
191 		int cm = count - (-length);
192 		if( cm<0 ) return cm;							// error
193 		while( cm-- ) *presult++ = 0x80;				// pad
194 		do *presult++ = *--prev; while( --count );		// copy result
195 		return 0;										// i.e. none unused
196 	}
197 	else // any length
198 	{
199 		do *presult++ = *--prev; while( --count );		// copy result
200 		return 0;
201 	}
202 }
203 
204 
205 //! Build a new UMID
MakeUMID(int Type,const UUIDPtr AssetID)206 UMIDPtr mxflib::MakeUMID(int Type, const UUIDPtr AssetID)
207 {
208 	static const UInt8 UMIDBase[10] = { 0x06, 0x0a, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 };
209 	UInt8 Buffer[32];
210 
211 	// Set the non-varying base of the UMID
212 	memcpy(Buffer, UMIDBase, 10);
213 
214 	// Correct to v5 dictionary for new (330M-2003) types
215 	if( Type > 4 ) Buffer[7] = 5;
216 
217 	// Set the type
218 	Buffer[10] = Type;
219 
220 	// We are using a GUID for material number, and no defined instance method
221 	Buffer[11] = 0x20;
222 
223 	// Length of UMID "Value" is 19 bytes
224 	Buffer[12] = 0x13;
225 
226 	// Set instance number to zero as this is the first instance of this material
227 	Buffer[13] = 0;
228 	Buffer[14] = 0;
229 	Buffer[15] = 0;
230 
231 	/* Fill the material number with a UUID (no swapping) */
232 
233 	// If no valid AssetID is provided, create a new one
234 	if( ( !AssetID ) || ( AssetID->Size() != 16 ) )
235 	{
236 		UInt8 UUIDbuffer[16];
237 		MakeUUID(UUIDbuffer);
238 		memcpy( &Buffer[16], &UUIDbuffer[0], 16 );
239 	}
240 	else
241 	{
242 		memcpy( &Buffer[16], AssetID->GetValue(), AssetID->Size() );
243 	}
244 
245 	return new UMID(Buffer);
246 }
247 
248 
249 //! Read a "Chunk" from a non-MXF file
FileReadChunk(FileHandle InFile,size_t Size)250 DataChunkPtr mxflib::FileReadChunk(FileHandle InFile, size_t Size)
251 {
252 	DataChunkPtr Ret = new DataChunk;
253 	Ret->Resize(Size);
254 
255 	// Read the data (and shrink chunk to fit)
256 	size_t Bytes = FileRead(InFile, Ret->Data, Size);
257 	if(Bytes == static_cast<size_t>(-1)) Bytes = 0;
258 	Ret->Resize(Bytes);
259 
260 	return Ret;
261 }
262 
263 
264 //! Set a data chunk from a hex string
Hex2DataChunk(std::string Hex)265 DataChunkPtr mxflib::Hex2DataChunk(std::string Hex)
266 {
267 	// Build the result chunk
268 	DataChunkPtr Ret = new DataChunk();
269 
270 	// Use a granularity of 16 as most hex strings are likely to be 16 or 32 bytes
271 	// DRAGONS: We may want to revise this later
272 	Ret->SetGranularity(16);
273 
274 	// Index the hex string
275 	char const *p = Hex.c_str();
276 
277 	int Size = 0;
278 	int Value = -1;
279 
280 	// During this loop Value = -1 when no digits of a number are mid-process
281 	// This stops a double space being regarded as a small zero in between two spaces
282 	// It also stops a trailing zero being appended to the data if the last character
283 	// before the terminating byte is not a hex digit.
284 	do
285 	{
286 		int digit;
287 		if(*p >= '0' && *p <='9') digit = (*p) - '0';
288 		else if(*p >= 'a' && *p <= 'f') digit = (*p) - 'a' + 10;
289 		else if(*p >= 'A' && *p <= 'F') digit = (*p) - 'A' + 10;
290 		else if(Value == -1)
291 		{
292 			// Skip second or subsiquent non-digit
293 			continue;
294 		}
295 		else
296 		{
297 			Size++;
298 			Ret->Resize(Size);
299 			Ret->Data[Size-1] = Value;
300 
301 			Value = -1;
302 			continue;
303 		}
304 
305 		if(Value == -1) Value = 0; else Value <<=4;
306 		Value += digit;
307 
308 	// Note that the loop test is done in this way to force
309 	// a final cycle of the loop with *p == 0 to allow the last
310 	// number to be processed
311 	} while(*(p++));
312 
313 	return Ret;
314 }
315 
316 
317 namespace mxflib
318 {
319 	//! The search path - note that "~" is used as a token for "not yet initialized"
320 	std::string DictionaryPath = "~";
321 }
322 
323 //! Set the search path to be used for dictionary files
SetDictionaryPath(std::string NewPath)324 void mxflib::SetDictionaryPath(std::string NewPath)
325 {
326 	DictionaryPath = NewPath;
327 }
328 
329 //! Search for a file of a specified name in the current dictionary search path
330 /*! If the filename is either absolute, or relative to "." or ".." then the
331  *  paths are not searched - just the location specified by that filename.
332  *  \return the full path and name of the file, or "" if not found
333  */
LookupDictionaryPath(const char * Filename)334 std::string mxflib::LookupDictionaryPath(const char *Filename)
335 {
336 	if(DictionaryPath == "~")
337 	{
338 
339 #ifdef MXFDATADIR
340 		// environment variable name may be specified at build time
341 		char *env = getenv( MXFDATADIR );
342 #else
343 		// default environment variable name is MXFLIB_DATA_DIR
344 		char *env = getenv( "MXFLIB_DATA_DIR" );
345 #endif
346 
347 		// if environment variable not specified, use the platform default
348 		if( !env ) DictionaryPath = std::string( DEFAULT_DICT_PATH );
349 		else DictionaryPath = std::string(env);
350 	}
351 
352 	return SearchPath(DictionaryPath, Filename);
353 }
354 
355 
356 //! Search a path list for a specified file
357 /*! If the filname is either absolute, or relative to "." or ".." then the
358  *  paths are not searched - just the location specified by that filename.
359  *  \return the full path and name of the file, or "" if not found
360  */
SearchPath(const char * Path,const char * Filename)361 std::string mxflib::SearchPath(const char *Path, const char *Filename)
362 {
363 	// First check to see if the filename is either relative to . (or ..)
364 	// or absolute in which case we don't search via the path
365 
366 	bool NonPath = false;
367 
368 	if(*Filename == '.')
369 	{
370 		if(Filename[1] == DIR_SEPARATOR) NonPath = true;
371 		else if((Filename[1] == '.') && (Filename[2] == DIR_SEPARATOR)) NonPath = true;
372 	}
373 	else if(IsAbsolutePath(Filename)) NonPath = true;
374 
375 	// Check the file without path if we should
376 	if((!(*Path)) || NonPath)
377 	{
378 		if(FileExists(Filename)) return std::string(Filename);
379 		return "";
380 	}
381 
382 	// Buffer bug enough for the full path, directory seperator, filename and a terminating zero
383 	char *Buffer = new char[strlen(Path) + strlen(Filename) + 2];
384 
385 	// Start searching all paths
386 	const char *p = Path;
387 	while(p && *p)
388 	{
389 		const char *sep = strchr(p, PATH_SEPARATOR);
390 
391 		// No more path separators - this is the last path to check
392 		if(!sep)
393 		{
394 			// Copy the whole of the remaining path
395 			strcpy(Buffer, p );
396 
397 			// Force the loop to stop at the end of this iteration
398 			p = NULL;
399 		}
400 		else
401 		{
402 			// Copy the section until the next separator
403 			strncpy(Buffer, p, sep - p);
404 			Buffer[sep-p]='\0';
405 
406 			// Advance the pointer to the character following the separator
407 			p = sep;
408 			p++;
409 		}
410 
411 		// Establish the length of this path
412 		size_t len = strlen(Buffer);
413 
414 		// Don't search a null path
415 		if(len == 0) continue;
416 
417 		// Add a directory separator if required
418 		if(Buffer[len-1] != DIR_SEPARATOR)
419 		{
420 			Buffer[len++] = DIR_SEPARATOR;
421 			Buffer[len] = '\0';
422 		}
423 
424 		// Add the filename
425 		strcat(Buffer, Filename);
426 
427 		if(FileExists(Buffer))
428 		{
429 			std::string Ret = std::string(Buffer);
430 			delete[] Buffer;
431 			return Ret;
432 		}
433 	}
434 
435 	// File not found in any of the paths supplied
436 	delete[] Buffer;
437 	return "";
438 }
439 
440 
441 // Is a given sequence of bytes a partition pack key?
442 // We first check if byte 13 == 1 which will be true for all partition packs,
443 // but is false for all GC sets and packs. Once this matches we can do a full memcmp.
IsPartitionKey(const UInt8 * Key)444 bool mxflib::IsPartitionKey(const UInt8 *Key)
445 {
446 	if(Key[12] != 1) return false;
447 
448 	// DRAGONS: This has version 1 hard coded as byte 8
449 	const UInt8 DegeneratePartition[13] = { 0x06, 0x0E, 0x2B, 0x34, 0x02, 0x05, 0x01, 0x01, 0x0d, 0x01, 0x02, 0x01, 0x01 };
450 	if( memcmp(Key, DegeneratePartition, 13) == 0 )
451 	{
452 		// Treat all matches as partition packs EXCEPT the RIP
453 		return Key[13] != 0x11;
454 	}
455 
456 	return false;
457 }
458 
459 
460 //! Does a given std::string contain a "wide" string in UTF8?
461 /*! \note This currently only checks if any bytes contain >127 so it is only safe to test strings that are either 7-bit ASCII or UTF-8 */
IsWideString(std::string & String)462 bool mxflib::IsWideString(std::string &String)
463 {
464 	size_t Len = String.size();
465 	char *Buffer = new char[Len];
466 	memcpy(Buffer, String.c_str(), Len);
467 
468 	// Look for any bytes > 127
469 	size_t i;
470 	char *p = Buffer;
471 	for(i=0; i<Len; i++)
472 	{
473 		if((*p++) & 0x80)
474 		{
475 			delete[] Buffer;
476 			return true;
477 		}
478 	}
479 	delete[] Buffer;
480 	return false;
481 }
482 
483 
484 //! Read an IFF chunk header (from an open file)
485 /*! The Chunk ID is read as a big-endian UInt32 and returned as the first
486  *	part of the returned pair. The chunk size is read as a specified-endian
487  *	number and returned as the second part of the returned pair
488  *	\return <0,0> if the header counld't be read
489  */
ReadIFFHeader(FileHandle InFile,bool BigEndian)490 U32Pair mxflib::ReadIFFHeader(FileHandle InFile, bool BigEndian /*=true*/)
491 {
492 	U32Pair Ret;
493 
494 	UInt8 Buffer[8];
495 	if(FileRead(InFile, Buffer, 8) < 8)
496 	{
497 		Ret.first = 0;
498 		Ret.second = 0;
499 		return Ret;
500 	}
501 
502 	Ret.first = GetU32(Buffer);
503 
504 	if(BigEndian)
505 		Ret.second = GetU32(&Buffer[4]);
506 	else
507 		Ret.second = GetU32_LE(&Buffer[4]);
508 
509 	return Ret;
510 }
511 
512 
513 //! Read a QuickTime Atom header (from an open file)
514 /*! The Atom Type ID is read as a big-endian UInt32 and returned as the first
515  *	part of the returned pair. The Atom size is read as a big-endian
516  *	number and returned as the second part of the returned pair.
517  *  Extended sizes are automatically read if used.
518  *  If SkipWide is omitted (or true) any "wide" atoms are read and skipped automatically.
519  *	\return <0,0> if the header counld't be read
520  */
ReadAtomHeader(FileHandle InFile,bool SkipWide)521 std::pair<UInt32, Length> mxflib::ReadAtomHeader(FileHandle InFile, bool SkipWide /*=true*/)
522 {
523 	static UInt32 WideID = 'w' << 24 | 'i' << 16 | 'd' << 8 | 'e';
524 
525 	std::pair<UInt32, Length> Ret;
526 
527 	UInt8 Buffer[8];
528 	if(FileRead(InFile, Buffer, 8) < 8)
529 	{
530 		Ret.first = 0;
531 		Ret.second = 0;
532 		return Ret;
533 	}
534 
535 	Ret.second = GetU32(Buffer);
536 	Ret.first = GetU32(&Buffer[4]);
537 
538 	// Skip wide atoms if requested
539 	if(SkipWide && (Ret.first == WideID) && (Ret.second == 8))
540 	{
541 		return ReadAtomHeader(InFile, true);
542 	}
543 
544 	// Read the extended length, if used
545 	if(Ret.second == 1)
546 	{
547 		if(FileRead(InFile, Buffer, 8) < 8)
548 		{
549 			Ret.first = 0;
550 			Ret.second = 0;
551 			return Ret;
552 		}
553 
554 		// DRAGONS: We read as signed as MXF uses signed lengths - this is only a problem for chunks > 2^63 bytes!
555 		Ret.second = GetI64(Buffer);
556 	}
557 
558 	return Ret;
559 }
560 
561 
562 //! Read hex values separated by any of 'Sep'
563 /*! \return number of values read */
ReadHexString(const char ** Source,int Max,UInt8 * Dest,const char * Sep)564 int mxflib::ReadHexString(const char **Source, int Max, UInt8 *Dest, const char *Sep)
565 {
566 	/* DRAGONS: - Pointer to pointer used for Source
567 	**		  This allows the caller's pointer to be updated to
568 	**		  point to the first character after the hex string
569 	**
570 	**		  **Source = character value in input data
571 	**		  *Source  = pointer to source data
572 	*/
573 
574 	int Count = 0;
575 	UInt8 current = 0;
576 	int Started = 0;
577 
578 	/* Skip leading whitespace (Abort if end of string) */
579 	while((**Source==' ') || (**Source=='\t'))
580 	{
581 		if((**Source)=='\0') return 0;
582 		(*Source)++;
583 	}
584 
585 
586 	// Lets see if this is a urn:x-ul: format definition
587 	// If so we skip over the lead-in
588 	if(**Source == 'u')
589 	{
590 		const char *Pattern = "urn:x-ul:";
591 
592 		const char *pSrc = *Source;
593 		const char *pPat = Pattern;
594 
595 		// Scan the start of the string until the end of the pattern
596 		while(*pPat)
597 		{
598 			// Abort if we find a mismatch
599 			if((*pSrc) != tolower(*pPat)) break;
600 			pSrc++;
601 			pPat++;
602 		}
603 
604 		// If we reached the end of the pattern then we have a match, so
605 		if(!(*pPat)) *Source = pSrc;
606 	}
607 
608 	int CharCount = 0;
609 	while(**Source != 0)
610 	{
611 		char c = **Source;
612 
613 		if((c>='0') && (c<='9'))
614 		{
615 			/* Update current value with next hex digit */
616 			current *= 16;
617 			current += (c - '0');
618 			Started = 1;
619 			CharCount++;
620 		}
621 		else if((c>='a') && (c<='f'))
622 		{
623 			/* Update current value with next hex digit */
624 			current *= 16;
625 			current += (c - 'a') + 10;
626 			Started = 1;
627 			CharCount++;
628 		}
629 		else if((c>='A') && (c<='F'))
630 		{
631 			/* Update current value with next hex digit */
632 			current *= 16;
633 			current += (c - 'A') + 10;
634 			Started = 1;
635 			CharCount++;
636 		}
637 		else
638 		{
639 			CharCount = 0;
640 
641 			int separator = 0;
642 			const char *p = Sep;
643 
644 			if(p == NULL)
645 			{
646 				if((c==' ') || (c=='\t')) separator = 1;
647 			}
648 			else
649 			{
650 				while(*p)
651 				{
652 					if(*(p++))
653 					{
654 						separator = 1;
655 						break;
656 					}
657 				}
658 			}
659 
660 			/* Valid separator found? */
661 			if(separator)
662 			{
663 				/* Update the output data if not full */
664 				if(Started && (Count <= Max))
665 				{
666 					*Dest++ = current;
667 					Count++;
668 				}
669 
670 				/* Reset current value */
671 				current = 0;
672 				Started = 0;
673 			}
674 			else
675 			{
676 				/* Run out of valid characters - exit loop */
677 				break;
678 			}
679 		}
680 
681 		// Move after 2 digits, even if no separator
682 		if(CharCount == 2)
683 		{
684 			CharCount = 0;
685 
686 			/* Update the output data if not full */
687 			if(Started && (Count <= Max))
688 			{
689 				*Dest++ = current;
690 				Count++;
691 			}
692 
693 			/* Reset current value */
694 			current = 0;
695 			Started = 0;
696 		}
697 
698 		/* Move to next character */
699 		(*Source)++;
700 	}
701 
702 	/* Update the output data with last value      */
703 	/* If we are working on one and there is space */
704 	if(Started)
705 	{
706 		if(Count <= Max)
707 		{
708 			*Dest++ = current;
709 			Count++;
710 		}
711 	}
712 
713 	return Count;
714 }
715 
716 
717 //! Build a UL from a character string, writing the bytes into a 16-byte buffer
718 /*! \return true if a full 16 bytes were read into the buffer, else false
719  */
StringToUL(UInt8 * Data,std::string Val)720 bool mxflib::StringToUL(UInt8 *Data, std::string Val)
721 {
722 	// Make a safe copy of the value that will not be cleaned-up by string manipulation
723 	const int VALBUFF_SIZE = 256;
724 	char ValueBuff[VALBUFF_SIZE];
725 	strncpy(ValueBuff, Val.c_str(), VALBUFF_SIZE -1);
726 	ValueBuff[VALBUFF_SIZE-1] = 0;
727 	const char *p = ValueBuff;
728 
729 	int Count = 16;
730 	int Value = -1;
731 	UInt8 *pD = Data;
732 
733 	// Is this a UUID than needs to be end-swapped
734 	bool EndSwap = false;
735 
736 	// Is this an OID format, which will need converting
737 	bool OIDFormat = false;
738 
739 	// Check for URN format
740 	if((tolower(*p) == 'u') && (tolower(p[1]) == 'r') && (tolower(p[2]) == 'n') && (tolower(p[3]) == ':'))
741 	{
742 		if(strcasecmp(Val.substr(0,9).c_str(), "urn:uuid:") == 0)
743 		{
744 			EndSwap = true;
745 		}
746 		else if(strcasecmp(Val.substr(0,8).c_str(), "urn:oid:") == 0)
747 		{
748 			OIDFormat = true;
749 		}
750 
751 		p += Val.rfind(':') + 1;
752 	}
753 
754 	// During this loop Value = -1 when no digits of a number are mid-process
755 	// This stops a double space being regarded as a small zero in between two spaces
756 	int DigitCount = 0;
757 	while(Count)
758 	{
759 		int Digit;
760 
761 		if((*p == 0) && (Value == -1)) Value = 0;
762 
763 		if(*p >= '0' && *p <='9') Digit = (*p) - '0';
764 		else if(*p >= 'a' && *p <= 'f') Digit = (*p) - 'a' + 10;
765 		else if(*p >= 'A' && *p <= 'F') Digit = (*p) - 'A' + 10;
766 		else
767 		{
768 			// If we meet "{" before any digits, this as a UUID - which will need to be end-swapped
769 			if((*p == '{') && (Count == 16) && (Value == -1))
770 			{
771 				EndSwap = true;
772 			}
773 
774 			if(Value == -1)
775 			{
776 				// Skip second or subsiquent non-digit
777 				p++;
778 				continue;
779 			}
780 			else
781 			{
782 				*pD = Value;
783 				*pD++;
784 
785 				Count--;
786 
787 				if(*p) p++;
788 
789 				Value = -1;
790 				DigitCount = 0;
791 
792 				continue;
793 			}
794 		}
795 
796 		if(Value == -1) Value = 0; else if(OIDFormat) Value *= 10; else Value <<=4;
797 		Value += Digit;
798 		p++;
799 
800 		if(!DigitCount)
801 			DigitCount++;
802 		else
803 		{
804 			*pD = Value;
805 			*pD++;
806 
807 			Count--;
808 
809 			Value = -1;
810 			DigitCount = 0;
811 		}
812 	}
813 
814 	// DRAGONS: oids can be encoded ULs
815 	if(OIDFormat)
816 	{
817 		if((Data[0] == 1) && (Data[1] == 3) && (Data[2] == 52))
818 		{
819 			// Shift the last 12 bytes of the UL forwards 1 byte (note that the oid is 1 byte shorter than a UL)
820 			memmove(&Data[4], &Data[3], 12);
821 
822 			// Set the first 4 bytes of a standard UL
823 			Data[0] = 0x06;
824 			Data[1] = 0x0e;
825 			Data[2] = 0x2b;
826 			Data[3] = 0x34;
827 		}
828 	}
829 
830 	// If the value was a UUID, end-swap it
831 	if(EndSwap)
832 	{
833 		UInt8 Temp[8];
834 		memcpy(Temp, &Data[8], 8);
835 		memcpy(&Data[8], Data, 8);
836 		memcpy(Data, Temp, 8);
837 	}
838 
839 	// Return true if we read 16-bytes worth of data
840 	return (Count == 0);
841 }
842 
843 
844 
845 /*
846 //! Safe printf
847 
848 inline std::string SafePrintf(const char *Fmt, ...)
849 {
850 	va_list args;
851 	va_start(args, Fmt);
852 	std::string Ret = SafePrintfInternal(Fmt, args);
853 	va_end(args);
854 }
855 */
856 //! Safe printf
857 /*! \note This implementation assumes that adding single characters to a std::string is efficient
858 */
859 /*
860 std::string mxflib::SafePrintf(const char *Fmt, va_list args)
861 {
862 	std::string Ret;
863 
864 	while(*Fmt)
865 	{
866 		if(ParsingPhase == ParsingFlag)
867 		{
868 			switch(*Fmt)
869 			{
870 			case '-':
871 				Flag |= FlagMinus;
872 				break;
873 			case '+':
874 				Flag |= FlagPlus;
875 				break;
876 			case '0':
877 				Flag |= FlagZero;
878 				break;
879 			case ' ':
880 				Flag |= FlagBlank;
881 				break;
882 			case '#':
883 				Flag |= FlagHash;
884 				break;
885 			case '.':
886 				ParsingPhase = ParsingPrecision;
887 				break;
888 
889 			default:
890 				if((*Fmt < '0') || (*Fmt > '9'))
891 				{
892 					ParsingPhase = ParsingType;
893 					continue;
894 				}
895 
896 				// Not a valid argument so we flip back to non-parsing
897 				ParsingPhase = ParsingIdle;
898 				continue;
899 			}
900 		}
901 		else
902 		{
903 			// Parsing starts on '%' but not "%%"
904 			if((*Fmt == '%') && (Fmt[1] != '%'))
905 			{
906 				ParsingItem = ParsingFlag;
907 
908 				Flags = 0;
909 				Width = 0;
910 				Precision = 0;
911 
912 				Fmt++;
913 				continue;
914 			}
915 
916 			// Add any non-field characters to the output string
917 			Ret += *Fmt;
918 
919 			// If we are processing "%%" skip an extra byte
920 			if(*Fmt == '%') Fmt++;
921 		}
922 
923 		Fmt++;
924 	}
925 
926 	return Ret;
927 }
928 */
929 
930