1 /*
2 * Copyright 2006 Sony Computer Entertainment Inc.
3 *
4 * Licensed under the MIT Open Source License, for details please see license.txt or the website
5 * http://www.opensource.org/licenses/mit-license.php
6 *
7 */
8
9 // The user can choose whether or not to include libxml support in the DOM. Supporting libxml will
10 // require linking against it. By default libxml support is included.
11 #if defined(DOM_INCLUDE_LIBXML)
12
13 // This is a rework of the XML plugin that contains a complete interface to libxml2 "readXML"
14 // This is intended to be a seperate plugin but I'm starting out by rewriting it in daeLIBXMLPlugin
15 // because I'm not sure if all the plugin handling stuff has been tested. Once I get a working
16 // plugin I'll look into renaming it so the old daeLIBXMLPlugin can coexist with it.
17 //
18 #include <string>
19 #include <sstream>
20 #include <modules/daeLIBXMLPlugin.h>
21 #include <dae.h>
22 #include <dom.h>
23 #include <dae/daeDatabase.h>
24 #include <dae/daeMetaElement.h>
25 #include <libxml/xmlreader.h>
26 #include <libxml/xmlwriter.h>
27 #include <libxml/xmlmemory.h>
28 #include <dae/daeErrorHandler.h>
29 #include <dae/daeMetaElementAttribute.h>
30
31 #include <limits>
32 #include <iomanip>
33 using namespace std;
34
35 #include <minizip/zip.h> // for saving compressed files
36 #ifdef _WIN32
37 #include <iowin32.h>
38 #else
39 #include <unistd.h>
40 #endif
41
42 // Some helper functions for working with libxml
43 namespace {
getCurrentLineNumber(xmlTextReaderPtr reader)44 daeInt getCurrentLineNumber(xmlTextReaderPtr reader) {
45 #if LIBXML_VERSION >= 20620
46 return xmlTextReaderGetParserLineNumber(reader);
47 #else
48 return -1;
49 #endif
50 }
51
52 #ifdef _WIN32
53 static const char s_filesep = '\\';
54 #else
55 static const char s_filesep = '/';
56 #endif
57
58 // Return value should be freed by caller with delete[]. Passed in value should not
59 // be null.
utf8ToLatin1(const xmlChar * utf8)60 xmlChar* utf8ToLatin1(const xmlChar* utf8) {
61 int inLen = xmlStrlen(utf8);
62 int outLen = (inLen+1) * 2;
63 xmlChar* latin1 = new xmlChar[outLen];
64 int numBytes = UTF8Toisolat1(latin1, &outLen, utf8, &inLen);
65 if (numBytes < 0) {
66 // Failed. Return an empty string instead.
67 numBytes = 0;
68 }
69 latin1[numBytes] = '\0';
70 return latin1;
71 }
72
73 // Return value should be freed by caller with delete[].
latin1ToUtf8(const string & latin1)74 xmlChar* latin1ToUtf8(const string& latin1) {
75 int inLen = (int)latin1.length();
76 int outLen = (inLen+1) * 2;
77 xmlChar* utf8 = new xmlChar[outLen];
78 int numBytes = isolat1ToUTF8(utf8, &outLen, (xmlChar*)latin1.c_str(), &inLen);
79 if (numBytes < 0)
80 // Failed. Return an empty string instead.
81 numBytes = 0;
82
83 utf8[numBytes] = '\0';
84 return utf8;
85 }
86
87 // wrapper that automatically closes the zip file handle
88 class zipFileHandler
89 {
90 public:
zipFileHandler()91 zipFileHandler() {
92 zf = NULL;
93 }
~zipFileHandler()94 ~zipFileHandler() {
95 if( !!zf ) {
96 int errclose = zipClose(zf,NULL);
97 if (errclose != ZIP_OK) {
98 ostringstream msg;
99 msg << "zipClose error" << errclose << "\n";
100 daeErrorHandler::get()->handleError(msg.str().c_str());
101 }
102 }
103 }
104 zipFile zf;
105 };
106
107 class xmlBufferHandler
108 {
109 public:
xmlBufferHandler()110 xmlBufferHandler() {
111 buf = NULL;
112 }
~xmlBufferHandler()113 ~xmlBufferHandler() {
114 if( !!buf ) {
115 xmlBufferFree(buf);
116 }
117 }
118 xmlBufferPtr buf;
119 };
120
121 typedef pair<daeString, daeString> stringPair;
122
123 // The attributes vector passed in should be empty. If 'encoding' is anything
124 // other than utf8 the caller should free the returned attribute value
125 // strings. The 'freeAttrValues' function is provided for that purpose.
packageCurrentAttributes(xmlTextReaderPtr reader,DAE::charEncoding encoding,vector<stringPair> & attributes)126 void packageCurrentAttributes(xmlTextReaderPtr reader,
127 DAE::charEncoding encoding,
128 /* out */ vector<stringPair>& attributes) {
129 int numAttributes = xmlTextReaderAttributeCount(reader);
130 if (numAttributes == -1 || numAttributes == 0)
131 return;
132 attributes.reserve(numAttributes);
133
134 while (xmlTextReaderMoveToNextAttribute(reader) == 1) {
135 const xmlChar* xmlName = xmlTextReaderConstName(reader);
136 const xmlChar* xmlValue = xmlTextReaderConstValue(reader);
137 if (encoding == DAE::Latin1)
138 attributes.push_back(stringPair((daeString)xmlName, (daeString)utf8ToLatin1(xmlValue)));
139 else
140 attributes.push_back(stringPair((daeString)xmlName, (daeString)xmlValue));
141 }
142 }
143
freeAttrValues(vector<stringPair> & pairs)144 void freeAttrValues(vector<stringPair>& pairs) {
145 for(size_t i=0, size=pairs.size(); i<size; ++i) {
146 delete[] pairs[i].second;
147 pairs[i].second = 0;
148 }
149 }
150 }
151
daeLIBXMLPlugin(DAE & dae)152 daeLIBXMLPlugin::daeLIBXMLPlugin(DAE& dae) : dae(dae), rawRelPath(dae)
153 {
154 supportedProtocols.push_back("*");
155 xmlInitParser();
156 rawFile = NULL;
157 rawByteCount = 0;
158 saveRawFile = false;
159 writer = NULL;
160 }
161
~daeLIBXMLPlugin()162 daeLIBXMLPlugin::~daeLIBXMLPlugin()
163 {
164 if( !writer ) {
165 xmlFreeTextWriter( writer );
166 writer = NULL;
167 }
168 xmlCleanupParser();
169 }
170
setOption(daeString option,daeString value)171 daeInt daeLIBXMLPlugin::setOption( daeString option, daeString value )
172 {
173 if ( strcmp( option, "saveRawBinary" ) == 0 )
174 {
175 if ( strcmp( value, "true" ) == 0 || strcmp( value, "TRUE" ) == 0 )
176 {
177 saveRawFile = true;
178 }
179 else
180 {
181 saveRawFile = false;
182 }
183 return DAE_OK;
184 }
185 return DAE_ERR_INVALID_CALL;
186 }
187
getOption(daeString option)188 daeString daeLIBXMLPlugin::getOption( daeString option )
189 {
190 if ( strcmp( option, "saveRawBinary" ) == 0 )
191 {
192 if ( saveRawFile )
193 {
194 return "true";
195 }
196 return "false";
197 }
198 return NULL;
199 }
200
201 namespace {
libxmlErrorHandler(void * arg,const char * msg,xmlParserSeverities severity,xmlTextReaderLocatorPtr locator)202 void libxmlErrorHandler(void* arg,
203 const char* msg,
204 xmlParserSeverities severity,
205 xmlTextReaderLocatorPtr locator) {
206 if(severity == XML_PARSER_SEVERITY_VALIDITY_WARNING ||
207 severity == XML_PARSER_SEVERITY_WARNING) {
208 daeErrorHandler::get()->handleWarning(msg);
209 }
210 else
211 daeErrorHandler::get()->handleError(msg);
212 }
213 }
214
215 #if LIBXML_VERSION < 20700
216 #ifndef XML_PARSE_HUGE
217 #define XML_PARSE_HUGE 0
218 #endif
219 #endif
220
221 // A simple structure to help alloc/free xmlTextReader objects
222 struct xmlTextReaderHelper {
xmlTextReaderHelperxmlTextReaderHelper223 xmlTextReaderHelper(const daeURI& uri) {
224 if((reader = xmlReaderForFile(cdom::fixUriForLibxml(uri.str()).c_str(), NULL, XML_PARSE_HUGE)))
225 xmlTextReaderSetErrorHandler(reader, libxmlErrorHandler, NULL);
226 }
227
xmlTextReaderHelperxmlTextReaderHelper228 xmlTextReaderHelper(daeString buffer, const daeURI& baseUri) {
229 if((reader = xmlReaderForDoc((xmlChar*)buffer, cdom::fixUriForLibxml(baseUri.str()).c_str(), NULL, XML_PARSE_HUGE)))
230 xmlTextReaderSetErrorHandler(reader, libxmlErrorHandler, NULL);
231 };
232
~xmlTextReaderHelperxmlTextReaderHelper233 ~xmlTextReaderHelper() {
234 if (reader)
235 xmlFreeTextReader(reader);
236 }
237
238 xmlTextReaderPtr reader;
239 };
240
readFromFile(const daeURI & uri)241 daeElementRef daeLIBXMLPlugin::readFromFile(const daeURI& uri) {
242 xmlTextReaderHelper readerHelper(uri);
243 if (!readerHelper.reader) {
244 daeErrorHandler::get()->handleError((string("Failed to open ") + uri.str() +
245 " in daeLIBXMLPlugin::readFromFile\n").c_str());
246 return NULL;
247 }
248 return read(readerHelper.reader);
249 }
250
readFromMemory(daeString buffer,const daeURI & baseUri)251 daeElementRef daeLIBXMLPlugin::readFromMemory(daeString buffer, const daeURI& baseUri) {
252 xmlTextReaderHelper readerHelper(buffer, baseUri);
253 if (!readerHelper.reader) {
254 daeErrorHandler::get()->handleError("Failed to open XML document from memory buffer in "
255 "daeLIBXMLPlugin::readFromMemory\n");
256 return NULL;
257 }
258 return read(readerHelper.reader);
259 }
260
read(_xmlTextReader * reader)261 daeElementRef daeLIBXMLPlugin::read(_xmlTextReader* reader) {
262 // Drop everything up to the first element. In the future, we should try to store header comments somewhere.
263 while(xmlTextReaderNodeType(reader) != XML_READER_TYPE_ELEMENT)
264 {
265 if (xmlTextReaderRead(reader) != 1) {
266 daeErrorHandler::get()->handleError("Error parsing XML in daeLIBXMLPlugin::read\n");
267 return NULL;
268 }
269 }
270
271 int readRetVal = 0;
272 return readElement(reader, NULL, readRetVal);
273 }
274
readElement(_xmlTextReader * reader,daeElement * parentElement,int & readRetVal)275 daeElementRef daeLIBXMLPlugin::readElement(_xmlTextReader* reader,
276 daeElement* parentElement,
277 /* out */ int& readRetVal) {
278 assert(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT);
279 daeString elementName = (daeString)xmlTextReaderConstName(reader);
280 bool empty = xmlTextReaderIsEmptyElement(reader) != 0;
281
282 vector<attrPair> attributes;
283 packageCurrentAttributes(reader, dae.getCharEncoding(), /* out */ attributes);
284
285 daeElementRef element = beginReadElement(parentElement, elementName, attributes, getCurrentLineNumber(reader));
286 if (dae.getCharEncoding() != DAE::Utf8) {
287 freeAttrValues(attributes);
288 }
289
290 if (!element) {
291 // We couldn't create the element. beginReadElement already printed an error message. Just make sure
292 // to skip ahead past the bad element.
293 xmlTextReaderNext(reader);
294 return NULL;
295 }
296
297 if ((readRetVal = xmlTextReaderRead(reader)) == -1) {
298 return NULL;
299 }
300 if (empty) {
301 return element;
302 }
303 int nodeType = xmlTextReaderNodeType(reader);
304 while (readRetVal == 1 && nodeType != XML_READER_TYPE_END_ELEMENT) {
305 if (nodeType == XML_READER_TYPE_ELEMENT) {
306 element->placeElement(readElement(reader, element, readRetVal));
307 }
308 else if (nodeType == XML_READER_TYPE_TEXT) {
309 const xmlChar* xmlText = xmlTextReaderConstValue(reader);
310 if (dae.getCharEncoding() == DAE::Latin1) {
311 xmlText = utf8ToLatin1(xmlText);
312 }
313 readElementText(element, (daeString)xmlText, getCurrentLineNumber(reader));
314 if (dae.getCharEncoding() == DAE::Latin1) {
315 delete[] xmlText;
316 }
317 readRetVal = xmlTextReaderRead(reader);
318 }
319 else {
320 readRetVal = xmlTextReaderRead(reader);
321 }
322 nodeType = xmlTextReaderNodeType(reader);
323 }
324
325 if (nodeType == XML_READER_TYPE_END_ELEMENT) {
326 readRetVal = xmlTextReaderRead(reader);
327 }
328 if (readRetVal == -1) { // Something went wrong (bad xml probably)
329 return NULL;
330 }
331
332 return element;
333 }
334
write(const daeURI & name,daeDocument * document,daeBool replace)335 daeInt daeLIBXMLPlugin::write(const daeURI& name, daeDocument *document, daeBool replace)
336 {
337 // Make sure database and document are both set
338 if (!database) {
339 return DAE_ERR_INVALID_CALL;
340 }
341 if(!document) {
342 return DAE_ERR_COLLECTION_DOES_NOT_EXIST;
343 }
344 // Convert the URI to a file path, to see if we're about to overwrite a file
345 string file = cdom::uriToNativePath(name.str());
346 if (file.empty() && saveRawFile)
347 {
348 daeErrorHandler::get()->handleError( "can't get path in write\n" );
349 return DAE_ERR_BACKEND_IO;
350 }
351
352 // If replace=false, don't replace existing files
353 if(!replace)
354 {
355 // Using "stat" would be better, but it's not available on all platforms
356 FILE *tempfd = fopen(file.c_str(), "r");
357 if(tempfd != NULL)
358 {
359 // File exists, return error
360 fclose(tempfd);
361 return DAE_ERR_BACKEND_FILE_EXISTS;
362 }
363 fclose(tempfd);
364 }
365 if ( saveRawFile )
366 {
367 string rawFilePath = file + ".raw";
368 if ( !replace )
369 {
370 rawFile = fopen(rawFilePath.c_str(), "rb" );
371 if ( rawFile != NULL )
372 {
373 fclose(rawFile);
374 return DAE_ERR_BACKEND_FILE_EXISTS;
375 }
376 fclose(rawFile);
377 }
378 rawFile = fopen(rawFilePath.c_str(), "wb");
379 if ( rawFile == NULL )
380 {
381 return DAE_ERR_BACKEND_IO;
382 }
383 rawRelPath.set(cdom::nativePathToUri(rawFilePath));
384 rawRelPath.makeRelativeTo( &name );
385 }
386
387 std::string fileName = cdom::uriToNativePath(name.str());
388 bool bcompress = fileName.size() >= 4 && fileName[fileName.size()-4] == '.' && ::tolower(fileName[fileName.size()-3]) == 'z' && ::tolower(fileName[fileName.size()-2]) == 'a' && ::tolower(fileName[fileName.size()-1]) == 'e';
389
390 int err=0;
391 xmlBufferHandler bufhandler;
392
393 if( bcompress ) {
394 // taken from http://xmlsoft.org/examples/testWriter.c
395 // Create a new XML buffer, to which the XML document will be written
396 bufhandler.buf = xmlBufferCreate();
397 if (!bufhandler.buf) {
398 ostringstream msg;
399 msg << "daeLIBXMLPlugin::write(" << name.str() << ") testXmlwriterMemory: Error creating the xml buffer\n";
400 daeErrorHandler::get()->handleError(msg.str().c_str());
401 return DAE_ERR_BACKEND_IO;
402 }
403
404 // Create a new XmlWriter for memory, with no compression. Remark: there is no compression for this kind of xmlTextWriter
405 writer = xmlNewTextWriterMemory(bufhandler.buf, 0);
406 }
407 else {
408 // Open the file we will write to
409 writer = xmlNewTextWriterFilename(cdom::fixUriForLibxml(name.str()).c_str(), 0);
410 }
411
412 if (!writer) {
413 ostringstream msg;
414 msg << "daeLIBXMLPlugin::write(" << name.str() << ") Error creating the xml writer\n";
415 daeErrorHandler::get()->handleError(msg.str().c_str());
416 return DAE_ERR_BACKEND_IO;
417 }
418 err = xmlTextWriterSetIndentString( writer, (const xmlChar*)"\t" ); // Don't change this to spaces
419 if( err < 0 ) {
420 }
421 err = xmlTextWriterSetIndent( writer, 1 ); // Turns indentation on
422 if( err < 0 ) {
423 }
424 err = xmlTextWriterStartDocument( writer, "1.0", "UTF-8", NULL );
425 if( err < 0 ) {
426 }
427
428 writeElement( document->getDomRoot() );
429
430 xmlTextWriterEndDocument( writer );
431 xmlTextWriterFlush( writer );
432 xmlFreeTextWriter( writer );
433 writer = NULL; // reset pointer
434
435 if( bcompress ) {
436 std::string savefilenameinzip;
437 size_t namestart = fileName.find_last_of(s_filesep);
438 if( namestart == string::npos ) {
439 namestart = 0;
440 }
441 else {
442 namestart+=1;
443 }
444 if(namestart+4>=fileName.size()) {
445 daeErrorHandler::get()->handleError("invalid fileName when removing zae extension");
446 return DAE_ERR_BACKEND_IO;
447 }
448 savefilenameinzip = fileName.substr(namestart,fileName.size()-namestart-4);
449 savefilenameinzip += ".dae";
450
451 zipFileHandler zfh;
452 #ifdef _WIN32
453 zlib_filefunc64_def ffunc;
454 fill_win32_filefunc64A(&ffunc);
455 zfh.zf = zipOpen2_64(fileName.c_str(),APPEND_STATUS_CREATE,NULL,&ffunc);
456 #else
457 zfh.zf = zipOpen64(fileName.c_str(),APPEND_STATUS_CREATE);
458 #endif
459 if (!zfh.zf) {
460 ostringstream msg;
461 msg << "daeLIBXMLPlugin::write(" << name.str() << ") Error opening zip file for writing\n";
462 daeErrorHandler::get()->handleError(msg.str().c_str());
463 return DAE_ERR_BACKEND_IO;
464 }
465
466 time_t curtime = time(NULL);
467 struct tm* timeofday = localtime(&curtime);
468 zip_fileinfo zi;
469 zi.tmz_date.tm_sec = timeofday->tm_sec;
470 zi.tmz_date.tm_min = timeofday->tm_min;
471 zi.tmz_date.tm_hour = timeofday->tm_hour;
472 zi.tmz_date.tm_mday = timeofday->tm_mday;
473 zi.tmz_date.tm_mon = timeofday->tm_mon;
474 zi.tmz_date.tm_year = timeofday->tm_year;
475 zi.dosDate = 0;
476 zi.internal_fa = 0;
477 zi.external_fa = 0;
478
479 int zip64 = bufhandler.buf->use >= 0xffffffff;
480
481 char* password=NULL;
482 unsigned long crcFile=0;
483 int opt_compress_level = 9;
484 err = zipOpenNewFileInZip3_64(zfh.zf,savefilenameinzip.c_str(),&zi,NULL,0,NULL,0,"collada file generated by collada-dom",Z_DEFLATED, opt_compress_level,0,-MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,password,crcFile, zip64);
485 if (err != ZIP_OK) {
486 ostringstream msg;
487 msg << "daeLIBXMLPlugin::write(" << name.str() << ") zipOpenNewFileInZip3_64 error" << err << "\n";
488 daeErrorHandler::get()->handleError(msg.str().c_str());
489 return DAE_ERR_BACKEND_IO;
490 }
491
492 err = zipWriteInFileInZip (zfh.zf,bufhandler.buf->content, bufhandler.buf->use);
493 if (err<0) {
494 ostringstream msg;
495 msg << "daeLIBXMLPlugin::write(" << name.str() << ") zipWriteInFileInZip error for dae file " << err << "\n";
496 daeErrorHandler::get()->handleError(msg.str().c_str());
497 return DAE_ERR_BACKEND_IO;
498 }
499 err = zipCloseFileInZip(zfh.zf);
500 if (err!=ZIP_OK) {
501 ostringstream msg;
502 msg << "daeLIBXMLPlugin::write(" << name.str() << ") zipCloseFileInZip error for dae file " << err << "\n";
503 daeErrorHandler::get()->handleError(msg.str().c_str());
504 return DAE_ERR_BACKEND_IO;
505 }
506
507 // add the manifest
508 string smanifest = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<dae_root>./";
509 smanifest += savefilenameinzip;
510 smanifest += "</dae_root>\n";
511 err = zipOpenNewFileInZip3_64(zfh.zf,"manifest.xml",&zi,NULL,0,NULL,0,NULL,Z_DEFLATED, opt_compress_level,0,-MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,password,crcFile, zip64);
512 if (err != ZIP_OK) {
513 ostringstream msg;
514 msg << "daeLIBXMLPlugin::write(" << name.str() << ") zipOpenNewFileInZip3_64 error for manifest.xml file " << err << "\n";
515 daeErrorHandler::get()->handleError(msg.str().c_str());
516 return DAE_ERR_BACKEND_IO;
517 }
518
519 err = zipWriteInFileInZip (zfh.zf,&smanifest[0],smanifest.size());
520 if (err != ZIP_OK) {
521 ostringstream msg;
522 msg << "daeLIBXMLPlugin::write(" << name.str() << ") zipWriteInFileInZip error for manifest.xml file " << err << "\n";
523 daeErrorHandler::get()->handleError(msg.str().c_str());
524 return DAE_ERR_BACKEND_IO;
525 }
526
527 err = zipCloseFileInZip(zfh.zf);
528 if (err != ZIP_OK) {
529 ostringstream msg;
530 msg << "daeLIBXMLPlugin::write(" << name.str() << ") zipCloseFileInZip error for manifest.xml file " << err << "\n";
531 daeErrorHandler::get()->handleError(msg.str().c_str());
532 return DAE_ERR_BACKEND_IO;
533 }
534 }
535
536 if ( saveRawFile && rawFile != NULL )
537 {
538 fclose( rawFile );
539 }
540
541 return DAE_OK;
542 }
543
writeToMemory(std::vector<char> & output,daeDocument * document)544 daeInt daeLIBXMLPlugin::writeToMemory(std::vector<char>& output, daeDocument *document)
545 {
546 // Make sure database and document are both set
547 if (!database) {
548 return DAE_ERR_INVALID_CALL;
549 }
550 if(!document) {
551 return DAE_ERR_COLLECTION_DOES_NOT_EXIST;
552 }
553
554 int err=0;
555 xmlBufferHandler bufhandler;
556
557 // taken from http://xmlsoft.org/examples/testWriter.c
558 // Create a new XML buffer, to which the XML document will be written
559 bufhandler.buf = xmlBufferCreate();
560 if (!bufhandler.buf) {
561 ostringstream msg;
562 msg << "daeLIBXMLPlugin::writeToMemory() testXmlwriterMemory: Error creating the xml buffer\n";
563 daeErrorHandler::get()->handleError(msg.str().c_str());
564 return DAE_ERR_BACKEND_IO;
565 }
566
567 // Create a new XmlWriter for memory, with no compression. Remark: there is no compression for this kind of xmlTextWriter
568 writer = xmlNewTextWriterMemory(bufhandler.buf, 0);
569 if (!writer) {
570 ostringstream msg;
571 msg << "daeLIBXMLPlugin::writeToMemory() Error creating the xml writer\n";
572 daeErrorHandler::get()->handleError(msg.str().c_str());
573 return DAE_ERR_BACKEND_IO;
574 }
575 err = xmlTextWriterSetIndentString( writer, (const xmlChar*)"\t" ); // Don't change this to spaces
576 if( err < 0 ) {
577 }
578 err = xmlTextWriterSetIndent( writer, 1 ); // Turns indentation on
579 if( err < 0 ) {
580 }
581 err = xmlTextWriterStartDocument( writer, "1.0", "UTF-8", NULL );
582 if( err < 0 ) {
583 }
584
585 writeElement( document->getDomRoot() );
586
587 xmlTextWriterEndDocument( writer );
588 xmlTextWriterFlush( writer );
589 xmlFreeTextWriter( writer );
590 writer = NULL; // reset pointer
591
592 if( bufhandler.buf->use == 0 ) {
593 return DAE_ERR_BACKEND_IO;
594 }
595
596 output.resize(bufhandler.buf->use);
597 memcpy(&output[0], bufhandler.buf->content, bufhandler.buf->use);
598 return DAE_OK;
599 }
600
writeElement(daeElement * element)601 void daeLIBXMLPlugin::writeElement( daeElement* element )
602 {
603 daeMetaElement* _meta = element->getMeta();
604
605 //intercept <source> elements for special handling
606 if ( saveRawFile )
607 {
608 if ( strcmp( element->getTypeName(), "source" ) == 0 )
609 {
610 daeElementRefArray children;
611 element->getChildren( children );
612 bool validArray = false, teqCommon = false;
613 for ( unsigned int i = 0; i < children.getCount(); i++ )
614 {
615 if ( strcmp( children[i]->getTypeName(), "float_array" ) == 0 ||
616 strcmp( children[i]->getTypeName(), "int_array" ) == 0 )
617 {
618 validArray = true;
619 }
620 else if ( strcmp( children[i]->getTypeName(), "technique_common" ) == 0 )
621 {
622 teqCommon = true;
623 }
624 }
625 if ( validArray && teqCommon )
626 {
627 writeRawSource( element );
628 return;
629 }
630 }
631 }
632
633 if (!_meta->getIsTransparent() ) {
634 xmlTextWriterStartElement(writer, (xmlChar*)element->getElementName());
635 daeMetaAttributeRefArray& attrs = _meta->getMetaAttributes();
636
637 int acnt = (int)attrs.getCount();
638
639 for(int i=0; i<acnt; i++) {
640 writeAttribute(attrs[i], element);
641 }
642 }
643 writeValue(element);
644
645 daeElementRefArray children;
646 element->getChildren( children );
647 for ( size_t x = 0; x < children.getCount(); x++ ) {
648 writeElement( children.get(x) );
649 }
650
651 /*if (_meta->getContents() != NULL) {
652 daeElementRefArray* era = (daeElementRefArray*)_meta->getContents()->getWritableMemory(element);
653 int elemCnt = (int)era->getCount();
654 for(int i = 0; i < elemCnt; i++) {
655 daeElementRef elem = (daeElementRef)era->get(i);
656 if (elem != NULL) {
657 writeElement( elem );
658 }
659 }
660 }
661 else
662 {
663 daeMetaElementAttributeArray& children = _meta->getMetaElements();
664 int cnt = (int)children.getCount();
665 for(int i=0;i<cnt;i++) {
666 daeMetaElement *type = children[i]->getElementType();
667 if ( !type->getIsAbstract() ) {
668 for (int c = 0; c < children[i]->getCount(element); c++ ) {
669 writeElement( *(daeElementRef*)children[i]->get(element,c) );
670 }
671 }
672 }
673 }*/
674 if (!_meta->getIsTransparent() ) {
675 xmlTextWriterEndElement(writer);
676 }
677 }
678
writeAttribute(daeMetaAttribute * attr,daeElement * element)679 void daeLIBXMLPlugin::writeAttribute( daeMetaAttribute* attr, daeElement* element)
680 {
681 ostringstream buffer;
682 #ifdef COLLADA_DOM_DAEFLOAT_IS64
683 buffer << std::setprecision(std::numeric_limits<PLATFORM_FLOAT64>::digits10+1); // set the default precision to daeFloat digit
684 #endif
685 attr->memoryToString(element, buffer);
686 string str = buffer.str();
687
688 // Don't write the attribute if
689 // - The attribute isn't required AND
690 // - The attribute has no default value and the current value is ""
691 // - The attribute has a default value and the current value matches the default
692 if (!attr->getIsRequired()) {
693 if(!attr->getDefaultValue() && str.empty())
694 return;
695 if(attr->getDefaultValue() && attr->compareToDefault(element) == 0)
696 return;
697 }
698
699 xmlTextWriterStartAttribute(writer, (xmlChar*)(daeString)attr->getName());
700 xmlChar* utf8 = (xmlChar*)str.c_str();
701 if (dae.getCharEncoding() == DAE::Latin1)
702 utf8 = latin1ToUtf8(str);
703 xmlTextWriterWriteString(writer, utf8);
704 if (dae.getCharEncoding() == DAE::Latin1)
705 delete[] utf8;
706
707 xmlTextWriterEndAttribute(writer);
708 }
709
writeValue(daeElement * element)710 void daeLIBXMLPlugin::writeValue(daeElement* element) {
711 if (daeMetaAttribute* attr = element->getMeta()->getValueAttribute()) {
712 ostringstream buffer;
713 #ifdef COLLADA_DOM_DAEFLOAT_IS64
714 buffer << std::setprecision(std::numeric_limits<PLATFORM_FLOAT64>::digits10+1); // set the default precision to daeFloat digit
715 #endif
716 attr->memoryToString(element, buffer);
717 string s = buffer.str();
718 if (!s.empty()) {
719 xmlChar* str = (xmlChar*)s.c_str();
720 if (dae.getCharEncoding() == DAE::Latin1)
721 str = latin1ToUtf8(s);
722 xmlTextWriterWriteString(writer, (xmlChar*)s.c_str());
723 if (dae.getCharEncoding() == DAE::Latin1)
724 delete[] str;
725 }
726 }
727 }
728
writeRawSource(daeElement * src)729 void daeLIBXMLPlugin::writeRawSource( daeElement *src )
730 {
731 daeElementRef newSrc = src->clone();
732 daeElementRef array = NULL;
733 daeElement *accessor = NULL;
734 daeElementRefArray children;
735 newSrc->getChildren( children );
736 bool isInt = false;
737 for ( int i = 0; i < (int)children.getCount(); i++ )
738 {
739 if ( strcmp( children[i]->getTypeName(), "float_array" ) == 0 )
740 {
741 array = children[i];
742 newSrc->removeChildElement( array );
743 }
744 else if ( strcmp( children[i]->getTypeName(), "int_array" ) == 0 )
745 {
746 array = children[i];
747 isInt = true;
748 newSrc->removeChildElement( array );
749 }
750 else if ( strcmp( children[i]->getTypeName(), "technique_common" ) == 0 )
751 {
752 children[i]->getChildren( children );
753 }
754 else if ( strcmp( children[i]->getTypeName(), "accessor" ) == 0 )
755 {
756 accessor = children[i];
757 }
758 }
759
760 daeULong *countPtr = (daeULong*)array->getAttributeValue( "count" );
761 daeULong count = countPtr != NULL ? *countPtr : 0;
762
763 daeULong *stridePtr = (daeULong*)accessor->getAttributeValue( "stride" );
764 daeULong stride = stridePtr != NULL ? *stridePtr : 1;
765
766 children.clear();
767 accessor->getChildren( children );
768 if ( children.getCount() > stride ) {
769 *stridePtr = children.getCount();
770 }
771
772 daeFixedName newURI;
773 sprintf( newURI, "%s#%ld", rawRelPath.getOriginalURI(), rawByteCount );
774 accessor->setAttribute( "source", newURI );
775
776 daeArray *valArray = (daeArray*)array->getValuePointer();
777
778 //TODO: pay attention to precision for the array.
779 if ( isInt )
780 {
781 for( size_t i = 0; i < count; i++ )
782 {
783 daeInt tmp = (daeInt)*(daeLong*)(valArray->getRaw(i));
784 rawByteCount += (unsigned long)(fwrite( &tmp, sizeof(daeInt), 1, rawFile ) * sizeof(daeInt));
785 }
786 }
787 else
788 {
789 for( size_t i = 0; i < count; i++ )
790 {
791 daeFloat tmp = (daeFloat)*(daeDouble*)(valArray->getRaw(i));
792 rawByteCount += (unsigned long)(fwrite( &tmp, sizeof(daeFloat), 1, rawFile ) * sizeof(daeFloat));
793 }
794 }
795
796 writeElement( newSrc );
797 }
798
799 #endif // DOM_INCLUDE_LIBXML
800