diff --git a/Core/Code/CppMicroServices/documentation/doxygen/standalone/BuildInstructions.md b/Core/Code/CppMicroServices/documentation/doxygen/standalone/BuildInstructions.md index c904ee8165..949489d93b 100644 --- a/Core/Code/CppMicroServices/documentation/doxygen/standalone/BuildInstructions.md +++ b/Core/Code/CppMicroServices/documentation/doxygen/standalone/BuildInstructions.md @@ -1,80 +1,83 @@ Build Instructions {#BuildInstructions} ================== The C++ Micro Services library provides [CMake][cmake] build scripts which allow the generation of platform and IDE specific project files. The library should compile on many different platforms. Below is a list of tested compiler/OS combinations: - GCC 4.5 (Ubuntu 11.04 and MacOS X 10.6) - Visual Studio 2008 and 2010 - Clang 3.0 (Ubuntu 11.04 and MacOS X 10.6) Prerequisites ------------- - [CMake][cmake] 2.8 (Visual Studio 2010 users should use the latest CMake version available) Configuring the Build --------------------- When building the C++ Micro Services library, you have a few configuration options at hand. ### General build options - **US_BUILD_SHARED_LIBS** Specify if the library should be build shared or static. See \ref MicroServices_StaticModules for detailed information about static CppMicroServices modules. - **US_BUILD_TESTING** Build unit tests and code snippets. - **US_ENABLE_AUTOLOADING_SUPPORT** Enable auto-loading of modules located in special sup-directories. See \ref MicroServices_AutoLoading for detailed information about this feature. - **US_ENABLE_THREADING_SUPPORT** Enable the use of synchronization primitives (atomics and pthread mutexes or Windows primitives) to make the API thread-safe. If you are application is not multi-threaded, turn this option OFF to get maximum performance. - **US_USE_C++11 (advanced)** Enable the usage of C++11 constructs +- **US_ENABLE_RESOURCE_COMPRESSION** + Enable compression of embedded resources. See \ref MicroServices_Resources for detailed information + about the resource system. ### Customizing naming conventions - **US_NAMESPACE** The default namespace is `us` but you may override this at will. - **US_HEADER_PREFIX** By default, all public headers have a "us" prefix. You may specify an arbitrary prefix to match your naming conventions. The above options are mainly useful when embedding the C++ Micro Services source code in your own library and you want to make it look like native source code. ### Configure the service base class All service implementations must inherit from the same base class. The C++ Micro Services library provides a trivial class called `us::Base` for that purpose. However, most applications already have a special base class and you can configure the C++ Micro Services library to use that class. - **US_BASECLASS_NAME** The fully-qualified name of the base class, e.g. `my::OwnBaseClass`. If you don't need support for service factories (see below) and don't want to enable US_BUILD_TESTING, this is all you need. - **US_ENABLE_SERVICE_FACTORY_SUPPORT (advanced)** If you want support for service factories (they allow the customization of service objects for individual modules, see OSGi Service Platform Core Specification Release 4, Version 4.3, Section 5.6), switch this option to ON. If you also provided a custom US_BASECLASS_NAME, you need to set the US_BASECLASS_HEADER variable. - **US_BASECLASS_HEADER (advanced)** The name of the header file containing the declaration for the base class specified in US_BASECLASS_NAME, e.g. `myOwnBaseClass.h`. You will also have to set US_BASECLASS_PACKAGE or US_BASECLASS_INCLUDE_DIRS and US_BASECLASS_LIBRARIES. - **US_BASECLASS_PACKAGE (advanced)** If your specified a custom base class from a library which can be found via a CMake `find_package` command, enter the package name here. Most of the time, this will correctly set the values for US_BASECLASS_INCLUDE_DIRS, US_BASECLASS_LIBRARIES, and US_BASECLASS_LIBRARY_DIRS. - **US_BASECLASS_INCLUDE_DIRS (advanced)** A list of include dirs needed to include the base class header file as specified in US_BASECLASS_HEADER. - **US_BASECLASS_LIBRARIES (advanced)** A list of libraries to link the C++ Micro Services library against for resolving symbols needed for a custom base class. - **US_BASECLASS_LIBRARY_DIRS (advanced)** A list of directories to look for the libraries specified in US_BASECLASS_LIBRARIES [cmake]: http://www.cmake.org diff --git a/Core/Code/CppMicroServices/src/service/usServiceEvent.h b/Core/Code/CppMicroServices/src/service/usServiceEvent.h index d1047b5fab..1d181deed2 100644 --- a/Core/Code/CppMicroServices/src/service/usServiceEvent.h +++ b/Core/Code/CppMicroServices/src/service/usServiceEvent.h @@ -1,179 +1,191 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #ifndef USSERVICEEVENT_H #define USSERVICEEVENT_H +#ifdef REGISTERED +#ifdef _WIN32 +#error The REGISTERED preprocessor define clashes with the ServiceEvent::REGISTERED\ + enum type. Try to reorder your includes, compile with WIN32_LEAN_AND_MEAN, or undef\ + the REGISTERED macro befor including this header. +#else +#error The REGISTERED preprocessor define clashes with the ServiceEvent::REGISTERED\ + enum type. Try to reorder your includes or undef the REGISTERED macro befor including\ + this header. +#endif +#endif + #include "usSharedData.h" #include "usServiceReference.h" #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4251) #endif US_BEGIN_NAMESPACE class ServiceEventData; /** * \ingroup MicroServices * * An event from the Micro Services framework describing a service lifecycle change. *

* ServiceEvent objects are delivered to * listeners connected via ModuleContext::AddServiceListener() when a * change occurs in this service's lifecycle. A type code is used to identify * the event type for future extendability. */ class US_EXPORT ServiceEvent { SharedDataPointer d; public: enum Type { /** * This service has been registered. *

* This event is delivered after the service * has been registered with the framework. * * @see ModuleContext#RegisterService() */ REGISTERED = 0x00000001, /** * The properties of a registered service have been modified. *

* This event is delivered after the service * properties have been modified. * * @see ServiceRegistration#SetProperties */ MODIFIED = 0x00000002, /** * This service is in the process of being unregistered. *

* This event is delivered before the service * has completed unregistering. * *

* If a module is using a service that is UNREGISTERING, the * module should release its use of the service when it receives this event. * If the module does not release its use of the service when it receives * this event, the framework will automatically release the module's use of * the service while completing the service unregistration operation. * * @see ServiceRegistration#Unregister * @see ModuleContext#UngetService */ UNREGISTERING = 0x00000004, /** * The properties of a registered service have been modified and the new * properties no longer match the listener's filter. *

* This event is delivered after the service * properties have been modified. This event is only delivered to listeners * which were added with a non-empty filter where the filter * matched the service properties prior to the modification but the filter * does not match the modified service properties. * * @see ServiceRegistration#SetProperties */ MODIFIED_ENDMATCH = 0x00000008 }; /** * Creates an invalid instance. */ ServiceEvent(); ~ServiceEvent(); /** * Can be used to check if this ServiceEvent instance is valid, * or if it has been constructed using the default constructor. * * @return true if this event object is valid, * false otherwise. */ bool IsNull() const; /** * Creates a new service event object. * * @param type The event type. * @param reference A ServiceReference object to the service * that had a lifecycle change. */ ServiceEvent(Type type, const ServiceReference& reference); ServiceEvent(const ServiceEvent& other); ServiceEvent& operator=(const ServiceEvent& other); /** * Returns a reference to the service that had a change occur in its * lifecycle. *

* This reference is the source of the event. * * @return Reference to the service that had a lifecycle change. */ ServiceReference GetServiceReference() const; /** * Returns the type of event. The event type values are: *

* * @return Type of service lifecycle change. */ Type GetType() const; }; US_END_NAMESPACE #ifdef _MSC_VER #pragma warning(pop) #endif /** * \ingroup MicroServices * @{ */ US_EXPORT std::ostream& operator<<(std::ostream& os, const US_PREPEND_NAMESPACE(ServiceEvent::Type)& type); US_EXPORT std::ostream& operator<<(std::ostream& os, const US_PREPEND_NAMESPACE(ServiceEvent)& event); /** @}*/ #endif // USSERVICEEVENT_H diff --git a/Core/Code/CppMicroServices/tools/usResourceCompiler.cpp b/Core/Code/CppMicroServices/tools/usResourceCompiler.cpp index 34aef6b1b2..443cd9c46f 100644 --- a/Core/Code/CppMicroServices/tools/usResourceCompiler.cpp +++ b/Core/Code/CppMicroServices/tools/usResourceCompiler.cpp @@ -1,817 +1,827 @@ /*============================================================================= Library: CppMicroServices Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =============================================================================*/ #include "usConfig.h" #include #include #include #include #include #include #include #include #include #include #include "stdint_p.h" #include "usConfig.h" #ifdef US_ENABLE_RESOURCE_COMPRESSION extern "C" { const char* us_resource_compressor_error(); unsigned char* us_resource_compressor(FILE*, long, int level, long* out_size); } #endif // US_ENABLE_RESOURCE_COMPRESSION #ifdef US_PLATFORM_WINDOWS static const char DIR_SEP = '\\'; #else static const char DIR_SEP = '/'; #endif class ResourceWriter; class Resource { public: enum Flags { NoFlags = 0x00, Directory = 0x01, Compressed = 0x02 }; Resource(const std::string& name, const std::string& path = std::string(), unsigned int flags = NoFlags); ~Resource(); std::string GetResourcePath() const; int64_t WriteName(ResourceWriter& writer, int64_t offset); void WriteTreeInfo(ResourceWriter& writer); int64_t WritePayload(ResourceWriter& writer, int64_t offset, std::string* errorMessage); std::string name; std::string path; unsigned int flags; Resource* parent; std::map children; std::map sortedChildren; int64_t nameOffset; int64_t dataOffset; int64_t childOffset; }; class ResourceWriter { public: ResourceWriter(const std::string& fileName, const std::string& libName, int compressionLevel, int compressionThreshold); ~ResourceWriter(); bool AddFiles(const std::vector& files, const std::string& basePath); bool Write(); private: friend class Resource; bool AddFile(const std::string& alias, const Resource& file); bool WriteHeader(); bool WritePayloads(); bool WriteNames(); bool WriteDataTree(); bool WriteRegistrationCode(); void WriteString(const std::string& str); void WriteChar(char c); void WriteHex(uint8_t tmp); void WriteNumber2(uint16_t number); void WriteNumber4(uint32_t number); std::ofstream out; std::vector files; std::string libName; std::string fileName; int compressionLevel; int compressionThreshold; Resource* root; }; Resource::Resource(const std::string& name, const std::string& path, unsigned int flags) : name(name) , path(path) , flags(flags) , parent(NULL) , nameOffset(0) , dataOffset(0) , childOffset(0) { } Resource::~Resource() { for (std::map::iterator i = children.begin(); i != children.end(); ++i) { delete i->second; } } std::string Resource::GetResourcePath() const { std::string resource = name; for (Resource* p = parent; p; p = p->parent) { resource = resource.insert(0, p->name + '/'); } return resource; } int64_t Resource::WriteName(ResourceWriter& writer, int64_t offset) { // capture the offset nameOffset = offset; // write the resource name as a comment writer.WriteString(" // "); writer.WriteString(name); writer.WriteString("\n "); // write the length of the name writer.WriteNumber2(static_cast(name.size())); writer.WriteString("\n "); offset += 2; // write the hash value writer.WriteNumber4(static_cast(US_HASH_FUNCTION_NAMESPACE::US_HASH_FUNCTION(std::string, name))); writer.WriteString("\n "); offset += 4; // write the name itself for (std::size_t i = 0; i < name.length(); ++i) { writer.WriteHex(name[i]); if (i != 0 && i % 32 == 0) writer.WriteString("\n "); } offset += name.length(); // done writer.WriteString("\n "); return offset; } void Resource::WriteTreeInfo(ResourceWriter& writer) { // write the resource path as a comment writer.WriteString(" // "); writer.WriteString(GetResourcePath()); writer.WriteString("\n "); if (flags & Directory) { // name offset (in the us_resource_name array) writer.WriteNumber4(static_cast(nameOffset)); // flags writer.WriteNumber2(flags); // child count writer.WriteNumber4(static_cast(children.size())); // first child offset (in the us_resource_tree array) writer.WriteNumber4(static_cast(childOffset)); } else { // name offset writer.WriteNumber4(static_cast(nameOffset)); // flags writer.WriteNumber2(flags); // padding (not used) writer.WriteNumber4(0); // data offset writer.WriteNumber4(static_cast(dataOffset)); } writer.WriteChar('\n'); } int64_t Resource::WritePayload(ResourceWriter& writer, int64_t offset, std::string* errorMessage) { // capture the offset dataOffset = offset; // open the resource file on the file system FILE* file = fopen(path.c_str(), "rb"); if (!file) { *errorMessage = "File could not be opened: " + path; return 0; } // get the file size fseek(file, 0, SEEK_END); const long fileSize = ftell(file); fseek(file, 0, SEEK_SET); unsigned char* fileBuffer = NULL; long fileBufferSize = 0; #ifdef US_ENABLE_RESOURCE_COMPRESSION // try compression if (writer.compressionLevel != 0 && fileSize != 0) { long compressedSize = 0; unsigned char* compressedBuffer = us_resource_compressor(file, fileSize, writer.compressionLevel, &compressedSize); if (compressedBuffer == NULL) { *errorMessage = us_resource_compressor_error(); return 0; } int compressRatio = static_cast((100.0 * (fileSize - compressedSize)) / fileSize); if (compressRatio >= writer.compressionThreshold) { fileBuffer = compressedBuffer; fileBufferSize = compressedSize; flags |= Compressed; } else { free(compressedBuffer); } } if (!(flags & Compressed)) #endif // US_ENABLE_RESOURCE_COMPRESSION { fileBuffer = static_cast(malloc(sizeof(unsigned char)*fileSize)); if (fileBuffer == NULL) { *errorMessage = "Could not allocate memory buffer for resource file " + path; return 0; } if (fseek(file, 0, SEEK_SET) != 0) { free(fileBuffer); *errorMessage = "Could not set stream position for resource file " + path; return 0; } if (fread(fileBuffer, 1, fileSize, file) != static_cast(fileSize)) { free(fileBuffer); *errorMessage = "Error reading resource file " + path; return 0; } fileBufferSize = fileSize; } if (fclose(file)) { *errorMessage = "Error closing resource file " + path; free(fileBuffer); return 0; } // write the full path of the resource in the file system as a comment writer.WriteString(" // "); writer.WriteString(path); writer.WriteString("\n "); // write the length writer.WriteNumber4(static_cast(fileBufferSize)); writer.WriteString("\n "); offset += 4; // write the actual payload int charsLeft = 16; for (long i = 0; i < fileBufferSize; ++i) { --charsLeft; writer.WriteHex(static_cast(fileBuffer[i])); if (charsLeft == 0) { writer.WriteString("\n "); charsLeft = 16; } } offset += fileBufferSize; free(fileBuffer); // done writer.WriteString("\n "); return offset; } ResourceWriter::ResourceWriter(const std::string& fileName, const std::string& libName, int compressionLevel, int compressionThreshold) : libName(libName) , fileName(fileName) , compressionLevel(compressionLevel) , compressionThreshold(compressionThreshold) , root(NULL) { out.exceptions(std::ofstream::goodbit); out.open(fileName.c_str()); } ResourceWriter::~ResourceWriter() { delete root; } bool ResourceWriter::AddFiles(const std::vector& files, const std::string& basePath) { bool success = true; for (std::size_t i = 0; i < files.size(); ++i) { const std::string& file = files[i]; if (file.size() <= basePath.size() || file.substr(0, basePath.size()) != basePath) { std::cerr << "File " << file << " is not an absolute path starting with " << basePath << std::endl; success = false; } else { const std::string relativePath = file.substr(basePath.size()); std::string name = relativePath; std::size_t index = relativePath.find_last_of(DIR_SEP); if (index != std::string::npos) { name = relativePath.substr(index+1); } success &= AddFile(relativePath, Resource(name, file)); } } return success; } bool ResourceWriter::Write() { if (!WriteHeader()) { std::cerr << "Could not write header." << std::endl; return false; } if (!WritePayloads()) { std::cerr << "Could not write data blobs." << std::endl; return false; } if (!WriteNames()) { std::cerr << "Could not write file names." << std::endl; return false; } if (!WriteDataTree()) { std::cerr << "Could not write data tree." << std::endl; return false; } if (!WriteRegistrationCode()) { std::cerr << "Could not write footer" << std::endl; return false; } return true; } bool ResourceWriter::AddFile(const std::string& alias, const Resource& file) { std::ifstream in(file.path.c_str(), std::ifstream::in | std::ifstream::binary); if (!in) { std::cerr << "File could not be opened: " << file.path << std::endl; return false; } in.seekg(0, std::ifstream::end); std::ifstream::pos_type size = in.tellg(); in.close(); if (size > 0xffffffff) { std::cerr << "File too big: " << file.path << std::endl; return false; } if (!root) { root = new Resource(std::string(), std::string(), Resource::Directory); } Resource* parent = root; std::stringstream ss(alias); std::vector nodes; { std::string node; while (std::getline(ss, node, DIR_SEP)) { if (node.empty()) continue; nodes.push_back(node); } } for(std::size_t i = 0; i < nodes.size()-1; ++i) { const std::string& node = nodes[i]; if (parent->children.find(node) == parent->children.end()) { Resource* s = new Resource(node, std::string(), Resource::Directory); s->parent = parent; parent->children.insert(std::make_pair(node, s)); parent->sortedChildren.insert(std::make_pair(static_cast(US_HASH_FUNCTION_NAMESPACE::US_HASH_FUNCTION(std::string, node)), s)); parent = s; } else { parent = parent->children[node]; } } const std::string filename = nodes.back(); Resource* s = new Resource(file); s->parent = parent; parent->children.insert(std::make_pair(filename, s)); parent->sortedChildren.insert(std::make_pair(static_cast(US_HASH_FUNCTION_NAMESPACE::US_HASH_FUNCTION(std::string, filename)), s)); return true; } bool ResourceWriter::WriteHeader() { std::stringstream ss; std::time_t now = time(0); ss << std::ctime(&now); WriteString("/*=============================================================================\n"); WriteString(" Resource object code\n"); WriteString("\n"); WriteString(" Created: "); WriteString(ss.str()); WriteString(" by: The Resource Compiler for CppMicroServices version "); WriteString(CppMicroServices_VERSION_STR); WriteString("\n\n"); WriteString(" WARNING! All changes made in this file will be lost!\n"); WriteString( "=============================================================================*/\n\n"); WriteString("#include \n"); WriteString("#include \n\n"); return true; } bool ResourceWriter::WritePayloads() { if (!root) return false; WriteString("static const unsigned char us_resource_data[] = {\n"); std::stack pending; pending.push(root); int64_t offset = 0; std::string errorMessage; while (!pending.empty()) { Resource* file = pending.top(); pending.pop(); for (std::map::iterator i = file->children.begin(); i != file->children.end(); ++i) { Resource* child = i->second; if (child->flags & Resource::Directory) { pending.push(child); } else { offset = child->WritePayload(*this, offset, &errorMessage); if (offset == 0) { std::cerr << errorMessage << std::endl; return false; } } } } WriteString("\n};\n\n"); return true; } bool ResourceWriter::WriteNames() { if (!root) return false; WriteString("static const unsigned char us_resource_name[] = {\n"); std::map names; std::stack pending; pending.push(root); int64_t offset = 0; while (!pending.empty()) { Resource* file = pending.top(); pending.pop(); for (std::map::iterator it = file->children.begin(); it != file->children.end(); ++it) { Resource* child = it->second; if (child->flags & Resource::Directory) { pending.push(child); } if (names.find(child->name) != names.end()) { child->nameOffset = names[child->name]; } else { names.insert(std::make_pair(child->name, offset)); offset = child->WriteName(*this, offset); } } } WriteString("\n};\n\n"); return true; } bool ResourceWriter::WriteDataTree() { if (!root) return false; WriteString("static const unsigned char us_resource_tree[] = {\n"); std::stack pending; // calculate the child offsets in the us_resource_tree array pending.push(root); int offset = 1; while (!pending.empty()) { Resource* file = pending.top(); pending.pop(); file->childOffset = offset; // calculate the offset now for (std::map::iterator i = file->sortedChildren.begin(); i != file->sortedChildren.end(); ++i) { Resource* child = i->second; ++offset; if (child->flags & Resource::Directory) { pending.push(child); } } } // write the tree structure pending.push(root); root->WriteTreeInfo(*this); while (!pending.empty()) { Resource *file = pending.top(); pending.pop(); // write the actual data now for (std::map::iterator i = file->sortedChildren.begin(); i != file->sortedChildren.end(); ++i) { Resource *child = i->second; child->WriteTreeInfo(*this); if (child->flags & Resource::Directory) { pending.push(child); } } } WriteString("\n};\n\n"); return true; } bool ResourceWriter::WriteRegistrationCode() { WriteString("US_BEGIN_NAMESPACE\n\n"); WriteString("extern US_EXPORT bool RegisterResourceData(int, ModuleInfo*, ModuleInfo::ModuleResourceData, ModuleInfo::ModuleResourceData, ModuleInfo::ModuleResourceData);\n\n"); WriteString("US_END_NAMESPACE\n\n"); WriteString(std::string("extern \"C\" US_ABI_EXPORT int _us_init_resources_") + libName + "(US_PREPEND_NAMESPACE(ModuleInfo)* moduleInfo)\n"); WriteString("{\n"); WriteString(" US_PREPEND_NAMESPACE(RegisterResourceData)(0x01, moduleInfo, us_resource_tree, us_resource_name, us_resource_data);\n"); WriteString(" return 1;\n"); WriteString("}\n"); return true; } void ResourceWriter::WriteString(const std::string& str) { out << str; } void ResourceWriter::WriteChar(char c) { out << c; } void ResourceWriter::WriteHex(uint8_t tmp) { const char* const digits = "0123456789abcdef"; WriteChar('0'); WriteChar('x'); if (tmp < 16) { WriteChar(digits[tmp]); } else { WriteChar(digits[tmp >> 4]); WriteChar(digits[tmp & 0xf]); } WriteChar(','); } void ResourceWriter::WriteNumber2(uint16_t number) { WriteHex(number >> 8); WriteHex(static_cast(number)); } void ResourceWriter::WriteNumber4(uint32_t number) { WriteHex(number >> 24); WriteHex(number >> 16); WriteHex(number >> 8); WriteHex(number); } #ifdef US_PLATFORM_POSIX #include std::string GetCurrentDir() { char currDir[512]; if (!getcwd(currDir, sizeof(currDir))) { std::cerr << "Getting the current directory failed." << std::endl; exit(EXIT_FAILURE); } return std::string(currDir); } bool IsAbsolutePath(const std::string& path) { return path.find_first_of('/') == 0; } #else #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include std::string GetCurrentDir() { TCHAR currDir[512]; DWORD dwRet; dwRet = GetCurrentDirectory(sizeof(currDir), currDir); if( dwRet == 0 || dwRet > 512) { std::cerr << "Getting the current directory failed." << std::endl; exit(EXIT_FAILURE); } return std::string(currDir); } bool IsAbsolutePath(const std::string& path) { return !PathIsRelative(path.c_str()); } #endif int main(int argc, char** argv) { if (argc < 4) { std::cout << US_RCC_EXECUTABLE_NAME " - A resource compiler for C++ Micro Services modules\n" "\n" - "Usage: " US_RCC_EXECUTABLE_NAME " LIBNAME OUTPUT INPUT... [-c COMPRESSION_LEVEL] [-t COMPRESSION_THRESHOLD] [-d ROOT_DIR INPUT [INPUT]]...\n" - "Convert all INPUT files into hex code embedded in C funtions written to OUTPUT.\n"; + "Usage: " US_RCC_EXECUTABLE_NAME " LIBNAME OUTPUT INPUT... " + "[-c COMPRESSION_LEVEL] [-t COMPRESSION_THRESHOLD] [-d ROOT_DIR INPUT...]...\n\n" + "Convert all INPUT files into hex code written to OUTPUT.\n" + "\n" + " LIBNAME The modules library name as it is specified in the US_INITIALIZE_MODULE macro\n" + " OUTPUT Absolute path for the generated source file\n" + " INPUT Path to the resource file, relative to the current working directory or" + " the preceeding ROOT_DIR argument\n" + " -c The Zip compression level (0-9) or -1 [defaults to -1, the default level]\n" + " -t Size reduction threshold (0-100) to trigger compression [defaults to 30]\n" + " -d Absolute path to a directory containing resource files. All following INPUT" + " files must be relative to this root path\n"; exit(EXIT_SUCCESS); } std::string libName(argv[1]); std::string fileName(argv[2]); // default zlib compression level int compressionLevel = -1; // use compressed data if 30% reduction or better int compressionThreshold = 30; std::map > inputFiles; inputFiles.insert(std::make_pair(GetCurrentDir(), std::vector())); std::vector* currFiles = &inputFiles.begin()->second; std::string currRootDir = inputFiles.begin()->first; for (int i = 3; i < argc; i++) { if (std::strcmp(argv[i], "-d") == 0) { if (i == argc-1) { std::cerr << "No argument after -d given." << std::endl; exit(EXIT_FAILURE); } currRootDir = argv[++i]; inputFiles.insert(std::make_pair(currRootDir, std::vector())); currFiles = &inputFiles[currRootDir]; } else if(std::strcmp(argv[i], "-c") == 0) { if (i == argc-1) { std::cerr << "No argument after -c given." << std::endl; exit(EXIT_FAILURE); } compressionLevel = atoi(argv[++i]); if (compressionLevel < -1 || compressionLevel > 10) { compressionLevel = -1; } } else if(std::strcmp(argv[i], "-t") == 0) { if (i == argc-1) { std::cerr << "No argument after -t given." << std::endl; exit(EXIT_FAILURE); } compressionThreshold = atoi(argv[++i]); } else { const std::string inputFile = argv[i]; if (IsAbsolutePath(inputFile)) { currFiles->push_back(inputFile); } else { currFiles->push_back(currRootDir + DIR_SEP + inputFile); } } } ResourceWriter writer(fileName, libName, compressionLevel, compressionThreshold); for(std::map >::iterator i = inputFiles.begin(); i != inputFiles.end(); ++i) { if (i->second.empty()) continue; if (!writer.AddFiles(i->second, i->first)) { return EXIT_FAILURE; } } return writer.Write() ? EXIT_SUCCESS : EXIT_FAILURE; }