Source

lrc / src / compiler / Collector.cxx

Full commit
//      Collector.cxx
//
//      Copyright 2011, 2012 Andreas Tscharner <andy@vis.ethz.ch>
//
//      This program is free software; you can redistribute it and/or modify
//      it under the terms of the GNU Lesser General Public License as
//      published by the Free Software Foundation; either version 3 of the
//      License, or (at your option) any later version.
//
//      This program is distributed in the hope that it will be useful,
//      but WITHOUT ANY WARRANTY; without even the implied warranty of
//      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//      GNU General Public License for more details.
//
//      You should have received a copy of the GNU Lesser General Public
//      License along with this program; if not, write to the Free Software
//      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
//      MA 02110-1301, USA.


#include <vector>
#include <iostream>
#include <cstring>
#include <cstdio>
#include "../Utils.hxx"
#include "../lrcExceptions.hxx"
#include "../ResourceData.hxx"
#include "../Factories.hxx"
#include "../StatusCodes.hxx"
#include "Collector.hxx"


int Collector::are_resIDs_unique(std::vector<ResourceData *> p_entries,
                                 inFilePosition &p_doubleIDPos,
                                 char **p_doubleID)
{
	int len, entrySize;


	entrySize = p_entries.size();
	DEBUG_PRINT(("are_resIDs_unique: Number of entries in vector: %d\n", entrySize))
	if ((! p_doubleID) || (entrySize < 0)) {
		return ERROR_INVALID_PARAMETER;
	};

	p_doubleIDPos = std::make_tuple(-1, -1);
	*p_doubleID = nullptr;

	if (entrySize < 2) {
		return NO_ERROR;
	};

	for (int i=0; i<entrySize; i++) {
		for (int j=0; j<entrySize; j++) {
			if (i == j) {
				continue;
			};
			if (strcmp(p_entries[i]->get_ID(), p_entries[j]->get_ID()) == 0) {
				len = strlen(p_entries[i]->get_ID());
				*p_doubleID = new char[len+1];
				memset(*p_doubleID, 0, (len+1));
				strncpy(*p_doubleID, p_entries[i]->get_ID(), len);
				if (i > j) {
					p_doubleIDPos = p_entries[i]->get_rc_position();
				} else {
					p_doubleIDPos = p_entries[j]->get_rc_position();
				};
				if (std::get<1>(p_doubleIDPos) == -1) {
					std::get<1>(p_doubleIDPos) = 1;
				};
				return(WARNING_DOUBLE_RESOURCE_ID);
			};
		};
	};

	return(NO_ERROR);
}

void Collector::show_resource_data_error(int p_errorCode, ResourceData *p_resData)
{
	const int MAX_ERR_TEXT_LEN = 255;
	char errorText[MAX_ERR_TEXT_LEN];
	char baseErrTxt[MAX_ERR_TEXT_LEN];
	char *resError;
	inFilePosition rcFPos;


	if (!is_error(p_errorCode)) {
		return;
	};

	rcFPos = p_resData->get_rc_position();
	memset(errorText, 0, MAX_ERR_TEXT_LEN);
	memset(baseErrTxt, 0, MAX_ERR_TEXT_LEN);
	snprintf(baseErrTxt, MAX_ERR_TEXT_LEN, "%s:%u:%d: error:", m_rcName,
	         std::get<0>(rcFPos), std::get<1>(rcFPos));
	DEBUG_PRINT(("Base error text: %s\n", baseErrTxt))

	switch (p_errorCode) {
		case ERROR_FILE_NOT_FOUND:
			snprintf(errorText, MAX_ERR_TEXT_LEN, "%s File \"%s\" not found.\n",
			         baseErrTxt, p_resData->get_file());
			break;
		case ERROR_COMPRESSION_NOT_AVAILABLE:
			snprintf(errorText, MAX_ERR_TEXT_LEN, "%s Compression type %d not available.\n",
			         baseErrTxt, p_resData->get_compression());
			break;
		case ERROR_ENCRYPTION_NOT_AVAILABLE:
			resError = p_resData->get_error_msg();
			if (resError) {
				snprintf(errorText, MAX_ERR_TEXT_LEN, "%s %s\n", baseErrTxt, resError);
				delete[] resError;
			} else {
				snprintf(errorText, MAX_ERR_TEXT_LEN, "%s Encryption type %d not available.\n",
			             baseErrTxt, p_resData->get_encryption());
			};
			break;
		case ERROR_FILE_OPEN:
			snprintf(errorText, MAX_ERR_TEXT_LEN, "%s Could not open file \"%s\".\n",
			         baseErrTxt, p_resData->get_file());
			break;
		case ERROR_FILE_READ:
			snprintf(errorText, MAX_ERR_TEXT_LEN, "%s File read error on file \"%s\".\n",
			         baseErrTxt, p_resData->get_file());
			break;
		case ERROR_COMPRESSION_COMPRESS:
			snprintf(errorText, MAX_ERR_TEXT_LEN, "%s Error while compressing data.\n",
			         baseErrTxt);
			break;
		case ERROR_ENCRYPTION_ENCRYPT:
			snprintf(errorText, MAX_ERR_TEXT_LEN, "%s Error while encrypting data.\n",
			         baseErrTxt);
			break;
		default:
			break;      // Don't show an error, will be displayed later on
	};
	if (strlen(errorText) > 0) {
		printf("%s", errorText);
	};
}

Collector::Collector(char *p_rcName, char *p_rdfName, bool p_overwriteAllow) throw(lrcFileExistsException)
{
	size_t sLen;


	if ((!p_overwriteAllow) && file_exists(p_rdfName)) {
		throw lrcFileExistsException(p_rdfName);
	};

	sLen = strlen(p_rcName)+1;
	m_rcName = new char[sLen];
	memset(m_rcName, 0, sLen);
	strncpy(m_rcName, p_rcName, (sLen-1));

	sLen = strlen(p_rdfName)+1;
	m_rdfName = new char[sLen];
	memset(m_rdfName, 0, sLen);
	strncpy(m_rdfName, p_rdfName, (sLen-1));
}

Collector::~Collector(void)
{
	delete[] m_rdfName;
	delete[] m_rcName;
}

int Collector::collect(std::vector<ResourceData *> *p_resEntries,
                       lrc::CompressionType p_compress,
                       lrc::EncryptionType p_encrypt,
                       const unsigned char *p_key)
{
	const char WHOLE_FILE[] = "Whole file";

	size_t entriesCount = p_resEntries->size();
	unsigned int currentOffset = 0, currentEntry = 0;
	resEntry *resEntries;
	unsigned char *writeBuffer = nullptr;
	unsigned char *bufferPos = nullptr;
	unsigned char *resData = nullptr;
	unsigned char *compressedData;
	unsigned char *encryptedData;
	unsigned char **resourceDataList;
	int retVal, writtenItems;
	size_t resDataSize, totalSize, finalSize;
	FILE *rdfFile;
	inFilePosition warnErr;
	char *doubleID;
	lrc::CompressDecompress *compressor;
	lrc::EncryptDecrypt *encryptor;


	retVal = this->are_resIDs_unique(*p_resEntries, warnErr, &doubleID);
	DEBUG_PRINT(("Checking for entries with the same resource ID returned %d\n", retVal));
	if (is_error(retVal)) {
		return(ERROR_INVALID_PARAMETER);
	};
	if ((is_warning(retVal)) && (retVal == WARNING_DOUBLE_RESOURCE_ID)) {
		std::cout << m_rcName << ":" << std::get<0>(warnErr) << ":";
		std::cout << std::get<1>(warnErr) << ": warning: resource ID \"";
		std::cout << doubleID << "\" appears twice. Only the first named entry will be found." << std::endl;
		delete[] doubleID;
	};

	DEBUG_PRINT(("Allocating memory for %d entries\n", entriesCount))
	resEntries = new resEntry[entriesCount];
	resourceDataList = new unsigned char *[entriesCount];
	for(ResourceData *entry : *p_resEntries) {
		retVal = entry->prepare_resource_from_file(&resData, resDataSize);
		DEBUG_PRINT(("1st turn: Calculate size. Current offset: %d; New size: %d (retVal = %d)\n", currentOffset, resDataSize, retVal))
		if (!no_error(retVal)) {
			this->show_resource_data_error(retVal, entry);

			delete[] resEntries;

			return retVal;
		};
		resourceDataList[currentEntry] = resData;

		DEBUG_PRINT(("Store data for current entry: %d\n", currentEntry))
		memset(resEntries[currentEntry].resID, 0, (sizeof(char)*MAX_ID_LEN));
		strncpy(resEntries[currentEntry].resID, entry->get_ID(), ((sizeof(char)*MAX_ID_LEN)-1));
		resEntries[currentEntry].startOffset = currentOffset;
		resEntries[currentEntry].resSize = resDataSize;
		resEntries[currentEntry].encType = entry->get_encryption();
		resEntries[currentEntry].compType = entry->get_compression();
		currentOffset += resDataSize;
		currentEntry++;
	};

	totalSize = sizeof(unsigned int) +            // Number of entries
	            entriesCount * sizeof(resEntry) + // Size for all entries
	            currentOffset;                    // Data size of all entries
	DEBUG_PRINT(("Total size for all data: %d\n", totalSize))

	DEBUG_PRINT(("Allocating and clearing memory\n"))
	writeBuffer = new unsigned char[totalSize];
	memset(writeBuffer, 0, totalSize);
	bufferPos = writeBuffer;

	DEBUG_PRINT(("Copying number of entries into write buffer\n"))
	memcpy(bufferPos, &currentEntry, sizeof(unsigned int));
	bufferPos += sizeof(unsigned int);

	DEBUG_PRINT(("Copying %d entries into write buffer   ", entriesCount))
	for (int i=0; i<entriesCount; i++) {
		memcpy(bufferPos, &resEntries[i], sizeof(resEntry));
		bufferPos += sizeof(resEntry);
		DEBUG_PRINT(("."))
	};
	DEBUG_PRINT(("done.\n"))

	DEBUG_PRINT(("Copying data of %d entries into write buffer (total: %d)   ", entriesCount, currentOffset))
	for (int i=0; i<entriesCount; i++) {
		memcpy(bufferPos, resourceDataList[i], (sizeof(unsigned char)*resEntries[i].resSize));
		bufferPos += (sizeof(unsigned char)*resEntries[i].resSize);
		DEBUG_PRINT(("."))
	};
	DEBUG_PRINT(("done.\n"))

	delete[] resEntries;
	delete_list(resourceDataList, entriesCount);
	resourceDataList = nullptr;

	DEBUG_PRINT(("Compress the data with compressor type %d.", p_compress))
	compressor = CompressionFactory::get_compression_class(p_compress);
	retVal = compressor->compress(writeBuffer, totalSize, &compressedData, finalSize);
	delete compressor;
	delete[] writeBuffer;
	if (! success(retVal)) {
		DEBUG_PRINT((" Compression failed with error code: %d\n", retVal))

		return ERROR_COMPRESSION_COMPRESS;
	};
	DEBUG_PRINT((" Old size: %d; new size: %d.\n", totalSize, finalSize))

	DEBUG_PRINT(("Encrypt the data with encryption type %d.", p_encrypt))
	try {
		encryptor = EncryptionFactory::get_encryption_class(p_encrypt, const_cast<char *>(WHOLE_FILE));
	} catch (lrcEncryptionDisabledException const &encDisEx) {
		delete[] compressedData;
		return ERROR_ENCRYPTION_NOT_AVAILABLE;
	};
	retVal = encryptor->encrypt(p_key, compressedData, finalSize, &encryptedData, finalSize);
	delete encryptor;
	if (! success(retVal)) {
		DEBUG_PRINT(("Encryption failed with error code: %d\n", retVal))

		return ERROR_ENCRYPTION_ENCRYPT;
	};
	DEBUG_PRINT(("\n"))

	rdfFile = fopen(m_rdfName, "wb");
	if (!rdfFile) {
		return ERROR_FILE_OPEN;
	};

	DEBUG_PRINT(("Writing data to file (%s)\n", m_rdfName))
	writtenItems = fwrite(encryptedData, sizeof(unsigned char), finalSize, rdfFile);
	fclose(rdfFile);
	delete[] encryptedData;

	if (writtenItems != finalSize) {
		DEBUG_PRINT(("Error writing to file (written: %d; total: %d)\n", writtenItems, finalSize))
		remove(m_rdfName);

		return ERROR_FILE_WRITE;
	};

	return NO_ERROR;
}