#ifndef NO_ZAE
#include <fstream>
#include <dae.h>
#include <dae/daeErrorHandler.h>
#include <dae/daeZAEUncompressHandler.h>
//-----------------------------------------------------------------
const std::string daeZAEUncompressHandler::MANIFEST_FILE_NAME("manifest.xml");
const std::string daeZAEUncompressHandler::MANIFEST_FILE_ROOT_ELEMENT_NAME("dae_root");
const int daeZAEUncompressHandler::CASE_INSENSITIVE = 2;
const int daeZAEUncompressHandler::BUFFER_SIZE = 1024;
const std::string daeZAEUncompressHandler::EMPTY_STRING = "";
//-----------------------------------------------------------------
daeZAEUncompressHandler::daeZAEUncompressHandler( const daeURI& zaeFile )
: mZipFile(NULL)
, mZipFileURI(zaeFile)
, mValidZipFile(false)
, mRootFilePath("")
{
std::string zipFilePath = cdom::uriToNativePath(zaeFile.getURI());
mZipFile = unzOpen(zipFilePath.c_str());
mValidZipFile = mZipFile != NULL;
mTmpDir = cdom::getSafeTmpDir() + cdom::getRandomFileName() +
cdom::getFileSeparator() + mZipFileURI.pathFile() + cdom::getFileSeparator();
}
//-----------------------------------------------------------------
daeZAEUncompressHandler::~daeZAEUncompressHandler()
{
if (mZipFile != NULL)
unzClose(mZipFile);
}
//-----------------------------------------------------------------
const std::string& daeZAEUncompressHandler::obtainRootFilePath()
{
if (!isZipFile())
return EMPTY_STRING;
if (boost::filesystem::create_directories(mTmpDir))
{
if (extractArchive(mZipFile, mTmpDir))
{
if (retrieveRootURIFromManifest(mTmpDir))
{
return mRootFilePath;
}
else
{
// TODO find root file without manifest
}
}
else
{
daeErrorHandler::get()->handleError("Error extracting archive in daeZAEUncompressHandler::obtainRootFilePath\n");
}
}
else
{
daeErrorHandler::get()->handleError("Error creating tmp dir in daeZAEUncompressHandler::obtainRootFilePath\n");
}
boost::filesystem::remove_all(this->getTmpDir());
return EMPTY_STRING;
}
//-----------------------------------------------------------------
bool daeZAEUncompressHandler::retrieveRootURIFromManifest(const std::string& tmpDir)
{
// extract via libxml.
bool error = false;
xmlTextReaderPtr xmlReader = xmlReaderForFile(
(tmpDir + MANIFEST_FILE_NAME).c_str(),
NULL,
0
);
if (xmlReader)
{
if (findManifestRootElement(xmlReader))
{
if (xmlTextReaderRead(xmlReader))
{
if (xmlTextReaderNodeType(xmlReader) == XML_READER_TYPE_TEXT) {
const xmlChar* xmlText = xmlTextReaderConstValue(xmlReader);
// copy xmlText.
std::string rootFilePath((daeString)xmlText);
// destroy xmlText.
xmlTextReaderRead(xmlReader);
cdom::trimWhitespaces(rootFilePath);
mRootFilePath = cdom::nativePathToUri(tmpDir + rootFilePath);
}
else
{
error = true;
}
}
else
{
error = true;
}
}
else
{
error = true;
}
}
else
{
error = true;
}
if (xmlReader)
xmlFreeTextReader(xmlReader);
if (error)
{
daeErrorHandler::get()->handleError("Error parsing manifest.xml in daeZAEUncompressHandler::retrieveRootURIFromManifest\n");
return false;
}
return true;
}
//-----------------------------------------------------------------
bool daeZAEUncompressHandler::findManifestRootElement( xmlTextReaderPtr xmlReader )
{
while(xmlTextReaderNodeType(xmlReader) != XML_READER_TYPE_ELEMENT)
{
if (xmlTextReaderRead(xmlReader) != 1) {
return false;
}
}
daeString elementName = (daeString)xmlTextReaderConstName(xmlReader);
if (strcmp(elementName, MANIFEST_FILE_ROOT_ELEMENT_NAME.c_str()) == 0)
{
return true;
}
return findManifestRootElement(xmlReader);
}
//-----------------------------------------------------------------
bool daeZAEUncompressHandler::extractArchive( unzFile zipFile, const std::string& destDir )
{
bool error = false;
unz_global_info globalZipInfo;
if (unzGetGlobalInfo (zipFile, &globalZipInfo) == UNZ_OK)
{
for (unsigned int i=0; i<globalZipInfo.number_entry; ++i)
{
if (!extractFile(zipFile, destDir))
{
error = true;
break;
}
if ((i+1)<globalZipInfo.number_entry)
{
if (unzGoToNextFile(zipFile) != UNZ_OK)
{
daeErrorHandler::get()->handleError("Error moving to next file in zip archive in daeZAEUncompressHandler::extractArchive\n");
error = true;
break;
}
}
}
}
else
{
daeErrorHandler::get()->handleError("Error getting info for zip archive in daeZAEUncompressHandler::extractArchive\n");
error = true;
}
return !error;
}
//-----------------------------------------------------------------
bool daeZAEUncompressHandler::extractFile( unzFile zipFile, const std::string& destDir )
{
bool error = false;
unz_file_info fileInfo;
char currentFileName[256]; // ARGH !!!
int fileInfoResult = unzGetCurrentFileInfo(zipFile, &fileInfo, currentFileName, sizeof(currentFileName), 0, 0, 0, 0);
if (fileInfoResult == UNZ_OK)
{
if ( currentFileName[ strlen(currentFileName)-1 ] == '/')
{
if (!boost::filesystem::create_directories(destDir + currentFileName))
{
daeErrorHandler::get()->handleError("Error creating dir from zip archive in daeZAEUncompressHandler::extractFile\n");
error = true;
}
}
else
{
if (unzOpenCurrentFile(zipFile) == UNZ_OK)
{
char* buffer = 0;
int readBytes = 1;
buffer = new char[ BUFFER_SIZE ];
std::string currentOutFilePath(destDir + std::string(currentFileName));
std::ofstream outFile(currentOutFilePath.c_str(), std::ios::binary);
while (readBytes > 0)
{
readBytes = unzReadCurrentFile(zipFile, buffer, BUFFER_SIZE);
outFile.write(buffer, readBytes);
}
delete[] buffer;
outFile.close();
if (readBytes >= 0)
{
if (unzCloseCurrentFile(zipFile) == UNZ_CRCERROR)
{
daeErrorHandler::get()->handleError("CRC error while opening file in zip archive in daeZAEUncompressHandler::extractFile\n");
error = true;
}
else
{
if (!checkAndExtractInternalArchive(currentOutFilePath))
{
error = true;
}
}
}
else
{
daeErrorHandler::get()->handleError("Error reading file in zip archive in daeZAEUncompressHandler::extractFile\n");
error = true;
}
}
else
{
daeErrorHandler::get()->handleError("Error opening file in zip archive in daeZAEUncompressHandler::extractFile\n");
error = true;
}
}
}
else
{
daeErrorHandler::get()->handleError("Error getting info for file in zip archive in daeZAEUncompressHandler::extractFile\n");
error = true;
}
return !error;
}
//-----------------------------------------------------------------
bool daeZAEUncompressHandler::checkAndExtractInternalArchive( const std::string& filePath )
{
unzFile zipFile = unzOpen(filePath.c_str());
if (zipFile == NULL)
{
// TODO check for other compression formats.
return true;
}
bool error = false;
boost::filesystem::path archivePath(filePath);
std::string dir = archivePath.branch_path().string();
const std::string& randomSegment = cdom::getRandomFileName();
std::string tmpDir = dir + cdom::getFileSeparator() + randomSegment + cdom::getFileSeparator();
if (boost::filesystem::create_directory(tmpDir))
{
if (!extractArchive(zipFile, tmpDir))
{
daeErrorHandler::get()->handleError("Could not extract internal zip archive in daeZAEUncompressHandler::checkAndExtractInternalArchive\n");
error = true;
}
}
else
{
daeErrorHandler::get()->handleError("Could not create temporary directory for extracting internal zip archive in daeZAEUncompressHandler::checkAndExtractInternalArchive\n");
error = true;
}
unzClose(zipFile);
if (!error)
{
if (boost::filesystem::remove(archivePath))
{
boost::filesystem::rename(tmpDir, archivePath);
}
else
{
daeErrorHandler::get()->handleError("Could not remove internal zip archive in daeZAEUncompressHandler::checkAndExtractInternalArchive\n");
error = true;
}
}
return !error;
}
#endif //NO_ZAE