Example #1
0
bool appendAtoB(const FS_NAMESPACE::path & destinationPath, const FS_NAMESPACE::path & sourcePath)
{
	// try opening the output file.
	std::fstream outStream;
	outStream.open(destinationPath.string(), std::ofstream::out | std::ofstream::binary | std::ofstream::app);
	if (outStream.is_open() && outStream.good())
	{
		if (beVerbose)
		{
			std::cout << std::endl << "Opened output file " << destinationPath << std::endl;
		}
		// seek to the end
		outStream.seekg(0, std::ios::end);
		// open input file
		std::ifstream inStream;
		inStream.open(sourcePath.string(), std::ifstream::in | std::ifstream::binary);
		if (inStream.is_open() && inStream.good())
		{
			if (beVerbose)
			{
				std::cout << "Opened input file \"" << sourcePath << "\". Appending data to output." << std::endl;
			}
			// copy data from input to output file
			while (!inStream.eof() && inStream.good())
			{
				unsigned char buffer[1024];
				std::streamsize readSize = sizeof(buffer);
				try
				{
					// try reading data from input file
					inStream.read(reinterpret_cast<char *>(&buffer), sizeof(buffer));
				}
				catch (std::ios_base::failure) { /*ignore read failure. salvage what we can.*/ }
				// store how many bytes were actually read
				readSize = inStream.gcount();
				// write to output file
				outStream.write(reinterpret_cast<const char *>(&buffer), readSize);
			}
			// close input file
			inStream.close();
		}
		else
		{
			std::cout << "Error: Failed to open input file \"" << sourcePath.string() << "\" for reading!" << std::endl;
			outStream.close();
			return false;
		}
		// close output file
		outStream.close();
		return true;
	}
	else
	{
		std::cout << "Error: Failed to open output file \"" << destinationPath.string() << "\" for writing!" << std::endl;
	}
	return false;
}
Example #2
0
int main(int argc, const char * argv[])
{
	printVersion();
	// check number of arguments and if all arguments can be read
	if (argc < 3 || !readArguments(argc, argv))
	{
		printUsage();
		return -1;
	}
	// check if the input path exist
	if (!FS_NAMESPACE::exists(inFilePath))
	{
		std::cout << "Error: Invalid input file/directory \"" << inFilePath.string() << "\"!" << std::endl;
		return -2;
	}
	if (createBinary)
	{
		// check if argument 2 is a file
		if (FS_NAMESPACE::is_directory(outFilePath))
		{
			std::cout << "Error: Output must be a file if -b is used!" << std::endl;
			return -2;
		}
	}
	else if (appendFile)
	{
		// check if argument 2 is a file
		if (FS_NAMESPACE::is_directory(outFilePath))
		{
			std::cout << "Error: Output must be a file if -a is used!" << std::endl;
			return -2;
		}
	}
	else if (FS_NAMESPACE::is_directory(inFilePath) != FS_NAMESPACE::is_directory(outFilePath))
	{
		// check if output directory exists
		if (FS_NAMESPACE::is_directory(outFilePath) && !FS_NAMESPACE::exists(outFilePath))
		{
			std::cout << "Error: Invalid output directory \"" << outFilePath.string() << "\"!" << std::endl;
			return -2;
		}
		// check if arguments 1 and 2 are both files or both directories
		std::cout << "Error: Input and output file must be both either a file or a directory!" << std::endl;
		return -2;
	}
	if (appendFile)
	{
		// append file a to b
		if (!appendAtoB(outFilePath, inFilePath))
		{
			std::cout << "Error: Failed to append data to executable!" << std::endl;
			return -3;
		}
	}
	else
	{
		// build list of files to process
		std::vector<FileData> fileList;
		if (FS_NAMESPACE::is_directory(inFilePath) && FS_NAMESPACE::is_directory(inFilePath))
		{
			// both files are directories, build file ist
			fileList = getFileDataFrom(inFilePath, outFilePath, inFilePath, useRecursion);
			if (fileList.empty())
			{
				std::cout << "Error: No files to convert!" << std::endl;
				return -3;
			}
		}
		else
		{
			// just add single input/output file
			FileData temp;
			temp.inPath = inFilePath;
			temp.outPath = outFilePath;
			temp.internalName = inFilePath.filename().string(); // remove all, but the file name and extension
			if (beVerbose)
			{
				std::cout << "Found input file " << inFilePath << std::endl;
				std::cout << "Internal name will be \"" << temp.internalName << "\"" << std::endl;
				std::cout << "Output path is " << temp.outPath << std::endl;
			}
			// get file size
			try
			{
				temp.size = static_cast<uint64_t>(FS_NAMESPACE::file_size(inFilePath));
				if (beVerbose)
				{
					std::cout << "Size is " << temp.size << " bytes." << std::endl;
				}
			}
			catch (...)
			{
				std::cout << "Error: Failed to get size of " << inFilePath << "!" << std::endl;
				temp.size = 0;
			}
			fileList.push_back(temp);
		}

		// does the user want an binary file?
		if (createBinary)
		{
			// yes. build it.
			if (!createBlob(fileList, outFilePath))
			{
				std::cout << "Error: Failed to convert to binary file!" << std::endl;
				return -4;
			}
		}
		else
		{
			// no. convert files to .c/.cpp. loop through list, converting files
			for (auto fdIt = fileList.begin(); fdIt != fileList.cend(); ++fdIt)
			{
				if (!convertFile(*fdIt, commonHeaderFilePath))
				{
					std::cout << "Error: Failed to convert all files. Aborting!" << std::endl;
					return -4;
				}
			}
			// do we need to write a header file?
			if (!commonHeaderFilePath.empty())
			{
				if (!createCommonHeader(fileList, commonHeaderFilePath, !utilitiesFilePath.empty(), useC))
				{
					return -5;
				}
				// do we need to create utilities?
				if (!utilitiesFilePath.empty())
				{
					if (!createUtilities(fileList, utilitiesFilePath, commonHeaderFilePath, useC, combineResults))
					{
						return -6;
					}
				}
			}
		}
	} // if (!appendFile) {
	// profit!!!
	std::cout << "res2h succeeded." << std::endl;
	return 0;
}
Example #3
0
bool createUtilities(std::vector<FileData> & fileList, const FS_NAMESPACE::path & utilitiesPath, const FS_NAMESPACE::path & commonHeaderPath, bool useCConstructs = false, bool addFileData = false)
{
	// try opening the output file. truncate it when it exists
	std::ofstream outStream;
	outStream.open(utilitiesPath.generic_string(), std::ofstream::out | std::ofstream::trunc);
	if (outStream.is_open() && outStream.good())
	{
		if (beVerbose)
		{
			std::cout << std::endl << "Creating utilities file " << utilitiesPath;
		}
		// add message
		outStream << "// this file was auto-generated by res2h" << std::endl << std::endl;
		// create path to include file RELATIVE to this file
		FS_NAMESPACE::path relativePath = naiveUncomplete(commonHeaderPath, utilitiesPath);
		// include header file
		outStream << "#include \"" << relativePath.string() << "\"" << std::endl << std::endl;
		// if the data should go to this file too, add it
		if (addFileData)
		{
			for (auto fdIt = fileList.begin(); fdIt != fileList.cend(); ++fdIt)
			{
				if (!convertFile(*fdIt, commonHeaderFilePath, outStream, false))
				{
					std::cout << "Error: Failed to convert all files. Aborting!" << std::endl;
					outStream.close();
					return false;
				}
			}
		}
		// begin data arrays. switch depending whether C or C++
		outStream << "const uint32_t res2hNrOfFiles = " << fileList.size() << ";" << std::endl;
		// add files
		outStream << "const Res2hEntry res2hFiles[res2hNrOfFiles] = {" << std::endl;
		outStream << "    "; // first indent
		for (auto fdIt = fileList.cbegin(); fdIt != fileList.cend();)
		{
			outStream << "{\"" << fdIt->internalName << "\", " << fdIt->sizeVariableName << ", " << fdIt->dataVariableName << "}";
			// was this the last entry?
			++fdIt;
			if (fdIt != fileList.cend())
			{
				// no. add comma.
				outStream << ",";
				// add break after every entry and add indent again
				outStream << std::endl << "    ";
			}
		}
		outStream << std::endl << "};" << std::endl;
		if (!useCConstructs)
		{
			// add files to map
			outStream << std::endl << "res2hMapType::value_type mapTemp[] = {" << std::endl;
			outStream << "    ";
			for (auto fdIt = fileList.cbegin(); fdIt != fileList.cend();)
			{
				outStream << "std::make_pair(\"" << fdIt->internalName << "\", res2hFiles[" << (fdIt - fileList.cbegin()) << "])";
				// was this the last entry?
				++fdIt;
				if (fdIt != fileList.cend())
				{
					// no. add comma.
					outStream << ",";
					// add break after every entry and add indent again
					outStream << std::endl << "    ";
				}
			}
			outStream << std::endl << "};" << std::endl << std::endl;
			// create map
			outStream << "res2hMapType res2hMap(mapTemp, mapTemp + sizeof mapTemp / sizeof mapTemp[0]);" << std::endl;
		}
		// close file
		outStream.close();
		if (beVerbose)
		{
			std::cout << " - succeeded." << std::endl;
		}
		return true;
	}
	else
	{
		std::cout << "Error: Failed to open file \"" << utilitiesPath << "\" for writing!" << std::endl;
	}
	return true;
}
Example #4
0
// Blob archive file format:
// Offset               | Type                | Description
// ---------------------+---------------------+-------------------------------------------
// START OF DATA        | char[8]             | magic number string "res2hbin"
// 08                   | uint32_t            | file format version number (currently 2)
// 12                   | uint32_t            | format flags. The lower 8 bit state the bit depth of the archive (32/64)
// 16                   | uint32_t / uint64_t | size of whole archive including checksum in bytes
// 20/24                | uint32_t            | number of directory and file entries following
// Then follows the directory:
// 24/28 + 00           | uint16_t            | file entry #0, size of internal name WITHOUT null-terminating character
// 24/28 + 02           | char[]              | file entry #0, internal name (NOT null-terminated)
// 24/28 + 02 + name    | uint32_t            | file entry #0, format flags for entry (currently 0)
// 24/28 + 06 + name    | uint32_t / uint64_t | file entry #0, size of data
// 24/28 + 10/14 + name | uint32_t / uint64_t | file entry #0, absolute offset of data in file
// 24/28 + 14/22 + name | uint32_t / uint64_t | file entry #0, Fletcher32/64 checksum of data
// Then follow other directory entries. There is some redundant information here, but that's for reading stuff faster.
// Directly after the directory the data blocks begin.
// END - 04/08       | uint32_t / uint64_t | Fletcher32/64 checksum of whole file up to this point
// Obviously with a 32bit archive you're limited to ~4GB for the whole binary file and ~4GB per data entry.
// Res2h will automagically create a 32bit archive, if data permits it, or a 64bit archive if needed.
bool createBlob(const std::vector<FileData> & fileList, const FS_NAMESPACE::path & filePath)
{
	// try opening the output file. truncate it when it exists
	std::fstream outStream;
	outStream.open(filePath.string(), std::ofstream::out | std::ofstream::binary | std::ofstream::trunc);
	if (outStream.is_open() && outStream.good())
	{
		const uint32_t nrOfEntries = static_cast<uint32_t>(fileList.size());
		// check if a 64bit archive is needed, or 32bit suffice
		uint64_t directorySize = 0;
		uint64_t maxDataSize = 0;
		uint64_t dataSize = 0;
		for (const auto & file : fileList)
		{
			dataSize += file.size;
			maxDataSize = maxDataSize < file.size ? file.size : maxDataSize;
			directorySize += file.internalName.size();
		}
		// now take worst case header and fixed directory size into account and check if we need 32 or 64 bit
		const bool mustUse64Bit = maxDataSize > UINT32_MAX || (RES2H_HEADER_SIZE_64 + directorySize + nrOfEntries * RES2H_DIRECTORY_SIZE_64 + dataSize + sizeof(uint64_t)) > UINT32_MAX;
		if (beVerbose)
		{
			std::cout << std::endl << "Creating binary " << (mustUse64Bit ? "64" : "32") << "bit archive " << filePath << std::endl;
		}
		// now that we know how many bits, add the correct amount of data for the fixed directory entries to the variable
		directorySize += nrOfEntries * (mustUse64Bit ? RES2H_DIRECTORY_SIZE_64 : RES2H_DIRECTORY_SIZE_32);
		// add magic number to file
		const unsigned char magicBytes[9] = RES2H_MAGIC_BYTES;
		outStream.write(reinterpret_cast<const char *>(&magicBytes), sizeof(magicBytes) - 1);
		// add version and format flag to file
		const uint32_t fileVersion = RES2H_ARCHIVE_VERSION;
		const uint32_t fileFlags = mustUse64Bit ? 64 : 32;
		outStream.write(reinterpret_cast<const char *>(&fileVersion), sizeof(uint32_t));
		outStream.write(reinterpret_cast<const char *>(&fileFlags), sizeof(uint32_t));
		// add dummy archive size to file
		uint64_t archiveSize = 0;
		outStream.write(reinterpret_cast<const char *>(&archiveSize), (mustUse64Bit ? sizeof(uint64_t) : sizeof(uint32_t)));
		// add number of directory entries to file
		outStream.write(reinterpret_cast<const char *>(&nrOfEntries), sizeof(uint32_t));
		// calculate data start offset behind directory
		uint64_t dataStart = mustUse64Bit ? RES2H_HEADER_SIZE_64 : RES2H_HEADER_SIZE_32;
		dataStart += directorySize;
		// add directory for all files
		for (const auto & file : fileList)
		{
			// add size of name
			if (file.internalName.size() > UINT16_MAX)
			{
				std::cout << "Error: File name \"" << file.internalName << "\" is too long!" << std::endl;
				outStream.close();
				return false;
			}
			const uint16_t nameSize = static_cast<uint16_t>(file.internalName.size());
			outStream.write(reinterpret_cast<const char *>(&nameSize), sizeof(uint16_t));
			// add name
			outStream.write(reinterpret_cast<const char *>(&file.internalName[0]), nameSize);
			// add flags
			const uint32_t entryFlags = 0;
			outStream.write(reinterpret_cast<const char *>(&entryFlags), sizeof(uint32_t));
			uint64_t fileChecksum = mustUse64Bit ? calculateFletcher<uint64_t>(file.inPath.string()) : calculateFletcher<uint32_t>(file.inPath.string());
			// add data size, offset from file start to start of data and checksum
			outStream.write(reinterpret_cast<const char *>(&file.size), (mustUse64Bit ? sizeof(uint64_t) : sizeof(uint32_t)));
			outStream.write(reinterpret_cast<const char *>(&dataStart), (mustUse64Bit ? sizeof(uint64_t) : sizeof(uint32_t)));
			outStream.write(reinterpret_cast<const char *>(&fileChecksum), (mustUse64Bit ? sizeof(uint64_t) : sizeof(uint32_t)));
			if (beVerbose)
			{
				std::cout << "Creating directory entry for \"" << file.internalName << "\"" << std::endl;
				std::cout << "Data starts at " << std::dec << std::showbase << dataStart << " bytes" << std::endl;
				std::cout << "Size is " << std::dec << file.size << " bytes" << std::endl;
				std::cout << "Fletcher" << (mustUse64Bit ? "64" : "32") << " checksum is " << std::hex << std::showbase << fileChecksum << std::endl;
			}
			// now add size of this entries data to start offset for next data block
			dataStart += file.size;
		}
		// add data for all files		
		for (const auto & file : fileList)
		{
			// try to open file
			std::ifstream inStream;
			inStream.open(file.inPath.string(), std::ifstream::in | std::ifstream::binary);
			if (inStream.is_open() && inStream.good())
			{
				if (beVerbose)
				{
					std::cout << "Adding data for \"" << file.internalName << "\"" << std::endl;
				}
				std::streamsize overallDataSize = 0;
				// copy data from input to output file
				while (!inStream.eof() && inStream.good())
				{
					unsigned char buffer[4096];
					std::streamsize readSize = sizeof(buffer);
					try
					{
						// try reading data from input file
						inStream.read(reinterpret_cast<char *>(&buffer), sizeof(buffer));
					}
					catch (std::ios_base::failure) { /*ignore read failure. salvage what we can.*/ }
					// store how many bytes were actually read
					readSize = inStream.gcount();
					// write to output file
					outStream.write(reinterpret_cast<const char *>(&buffer), readSize);
					// increase size of overall data read
					overallDataSize += readSize;
				}
				// close input file
				inStream.close();
				// check if the file was completely read
				if (overallDataSize != file.size)
				{
					std::cout << "Error: Failed to completely copy file \"" << file.inPath.string() << "\" to binary data!" << std::endl;
					outStream.close();
					return false;
				}
			}
			else
			{
				std::cout << "Error: Failed to open file \"" << file.inPath.string() << "\" for reading!" << std::endl;
				outStream.close();
				return false;
			}
		}
		// final archive size is current size + checksum. write size to the header now
		archiveSize = static_cast<uint64_t>(outStream.tellg()) + (mustUse64Bit ? sizeof(uint64_t) : sizeof(uint32_t));
		outStream.seekg(RES2H_OFFSET_ARCHIVE_SIZE);
		outStream.write(reinterpret_cast<const char *>(&archiveSize), (mustUse64Bit ? sizeof(uint64_t) : sizeof(uint32_t)));
		// close file
		outStream.close();
		if (beVerbose)
		{
			std::cout << "Binary archive creation succeeded." << std::endl;
			std::cout << "Archive has " << std::dec << archiveSize << " bytes." << std::endl;
		}
		// calculate checksum of whole file
		const uint64_t checksum = mustUse64Bit ? calculateFletcher<uint64_t>(filePath.string()) : calculateFletcher<uint32_t>(filePath.string());
		// open file again, move to end of file and append checksum
		outStream.open(filePath.string(), std::ofstream::out | std::ofstream::binary | std::ofstream::app);
		if (outStream.is_open() && outStream.good())
		{
			outStream.seekg(0, std::ios::end);
			outStream.write(reinterpret_cast<const char *>(&checksum), (mustUse64Bit ? sizeof(uint64_t) : sizeof(uint32_t)));
			// close file
			outStream.close();
		}
		else
		{
			std::cout << "Error: Failed to open file \"" << filePath.string() << "\" for writing!" << std::endl;
			return false;
		}
		if (beVerbose)
		{
			std::cout << "Archive Fletcher" << (mustUse64Bit ? "64" : "32") << " checksum is " << std::hex << std::showbase << checksum << "." << std::endl;
		}
		return true;
	}
	else
	{
		std::cout << "Error: Failed to open file \"" << filePath.string() << "\" for writing!" << std::endl;
		return false;
	}
	return false;
}
Example #5
0
std::vector<FileData> getFileDataFrom(const FS_NAMESPACE::path & inPath, const FS_NAMESPACE::path & outPath, const FS_NAMESPACE::path & parentDir, const bool recurse)
{
	// get all files from directory
	std::vector<FileData> files;
	// check for infinite symlinks
	if (FS_NAMESPACE::is_symlink(inPath))
	{
		// check if the symlink points somewhere in the path. this would recurse
		if (inPath.string().find(FS_NAMESPACE::canonical(inPath).string()) == 0)
		{
			std::cout << "Warning: Path " << inPath << " contains recursive symlink! Skipping." << std::endl;
			return files;
		}
	}
	// iterate through source directory searching for files
	const FS_NAMESPACE::directory_iterator dirEnd;
	for (FS_NAMESPACE::directory_iterator fileIt(inPath); fileIt != dirEnd; ++fileIt)
	{
		FS_NAMESPACE::path filePath = (*fileIt).path();
		if (!FS_NAMESPACE::is_directory(filePath))
		{
			if (beVerbose)
			{
				std::cout << "Found input file " << filePath << std::endl;
			}
			// add file to list
			FileData temp;
			temp.inPath = filePath;
			// replace dots in file name with '_' and add a .c/.cpp extension
			std::string newFileName = filePath.filename().generic_string();
			std::replace(newFileName.begin(), newFileName.end(), '.', '_');
			if (useC)
			{
				newFileName.append(".c");
			}
			else
			{
				newFileName.append(".cpp");
			}
			// remove parent directory of file from path for internal name. This could surely be done in a safer way
			FS_NAMESPACE::path subPath(filePath.generic_string().substr(parentDir.generic_string().size() + 1));
			// add a ":/" before the name to mark internal resources (Yes. Hello Qt!)
			temp.internalName = ":/" + subPath.generic_string();
			// add subdir below parent path to name to enable multiple files with the same name
			std::string subDirString(subPath.remove_filename().generic_string());
			if (!subDirString.empty())
			{
				// replace dir separators by underscores
				std::replace(subDirString.begin(), subDirString.end(), '/', '_');
				// add in front of file name
				newFileName = subDirString + "_" + newFileName;
			}
			// build new output file name
			temp.outPath = outPath / newFileName;
			if (beVerbose)
			{
				std::cout << "Internal name will be \"" << temp.internalName << "\"" << std::endl;
				std::cout << "Output path is " << temp.outPath << std::endl;
			}
			// get file size
			try
			{
				temp.size = static_cast<uint64_t>(FS_NAMESPACE::file_size(filePath));
				if (beVerbose)
				{
					std::cout << "Size is " << temp.size << " bytes." << std::endl;
				}
			}
			catch (...)
			{
				std::cout << "Error: Failed to get size of " << filePath << "!" << std::endl;
				temp.size = 0;
			}
			// add file to list
			files.push_back(temp);
		}
	}
	// does the user want subdirectories?
	if (recurse)
	{
		// iterate through source directory again searching for directories
		for (FS_NAMESPACE::directory_iterator dirIt(inPath); dirIt != dirEnd; ++dirIt)
		{
			FS_NAMESPACE::path dirPath = (*dirIt).path();
			if (FS_NAMESPACE::is_directory(dirPath))
			{
				if (beVerbose)
				{
					std::cout << "Found subdirectory " << dirPath << std::endl;
				}
				// subdirectory found. recurse.
				std::vector<FileData> subFiles = getFileDataFrom(dirPath, outPath, parentDir, recurse);
				// add returned result to file list
				files.insert(files.end(), subFiles.cbegin(), subFiles.cend());
			}
		}
	}
	// return result
	return files;
}