diff --git a/CHANGES b/CHANGES index a48c8ca..892bec6 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,8 @@ Changes for 3.1.0: +* New feature: Support for generating C code. * Fixed issue #29: Create arguments to filter files found with '--dir' argument. +* Fixed issue #53: Generate only C code. * Fixed issue #63: Context class redesign. Move arguments handling into a Context class. * Fixed issue #64: Remove build artifacts of samples from installation packages. * Fixed issue #72: Move the code FileManager class generation code into its own IGenerator implementation. diff --git a/README.md b/README.md index d714cb0..8b44bb0 100644 --- a/README.md +++ b/README.md @@ -88,12 +88,12 @@ The following section shows how to use bin2cpp with code examples: ``` bin2cpp --file= --output= [--headerfile=] [--identifier=] [--generator=] [--encoding=] [--chunksize=] [--namespace=] - [--baseclass=] [--managerfile=] [--registerfile] - [--reportedfilepath=] [--override] [--noheader] [--quiet] + [--baseclass=] [--managerfile=] [--registerfile] [--code] + [--reportedfilepath=] [--plainoutput] [--override] [--noheader] [--quiet] bin2cpp --dir= --output= [--keepdirs] [--generator=] [--encoding=] [--chunksize=] [--namespace=] - [--baseclass=] [--managerfile=] [--registerfile] - [--dirincludefilter=] [--direxcludefilter=] + [--baseclass=] [--managerfile=] [--registerfile] [--code] + [--dirincludefilter=] [--direxcludefilter=] [--override] [--noheader] [--quiet] bin2cpp --help bin2cpp --version @@ -111,8 +111,8 @@ bin2cpp --version | --generator=<name> | Name of the generator to use. Possible values are 'segment', 'string', 'array' and 'win32'.
[default: segment] | | --encoding=<name> | Name of the binary to string literal encoding to use. Possible values are 'oct' and 'hex'.
[default: oct] | | --chunksize=<value> | Size in bytes of each string segments (bytes per LoC).
[default: 200] | -| --baseclass=<name> | The name of the interface for embedded files.
[default: File] | -| --namespace=<name> | The namespace of the generated source code.
[default: bin2cpp] | +| --baseclass=<name> | The name of the interface for embedded files. [default: File]
For C generated code, this parameter is for naming the File structure. [default: Bin2cFile] | +| --namespace=<name> | The namespace of the generated source code. [default: bin2cpp]
For C generated code, this parameter is for setting the prefix of all function names. [default: bin2c] | | --reportedfilepath=<path> | The relative reported path of the File. Path returned when calling method getFilePath() of the File class. Automatically calculated when --dir mode is used.
ie: images/DCIM/IMG_0001.jpg | | --managerfile=<path> | File name or relative path of the generated C++ header file for the FileManager class.
ie: FileManager.h. | | --registerfile | Register the generated file to the FileManager class. This flags is automatically set when parameter 'managerfile' is specified. | @@ -120,6 +120,7 @@ bin2cpp --version | --direxcludefilter=<value>| Set a negative filter on the input directory to skip files matching the filter. Wildcard characters are accepted. Separate each filter with the character ':'. Valid only when --dir is used. See wildcard characters definition below. The exclude filter has precedence over the include filter. | | --keepdirs | Keep the directory structure. Forces the output files to have the same directory structure as the input files. Valid only when --dir is used. | | --plainoutput | Print the encoded string in plain format to stdout. Useful for scripts and integration with third party application. | +| --code | Define the programming language output for code generation. Supported values are `c`, `cpp` or `c++`. | | --override | Tells bin2cpp to overwrite the destination files. | | --noheader | Do not print program header to standard output. | | --quiet | Do not log any message to standard output. | diff --git a/src/bin2cpp/ArrayGenerator.cpp b/src/bin2cpp/ArrayGenerator.cpp index 0ed05f0..b43bfa2 100644 --- a/src/bin2cpp/ArrayGenerator.cpp +++ b/src/bin2cpp/ArrayGenerator.cpp @@ -131,7 +131,7 @@ namespace bin2cpp delete[] buffer; buffer = NULL; - //write cpp file footer + //write cpp source file footer fprintf(cpp, " return (const char *)buffer;\n"); fprintf(cpp, " }\n"); fprintf(cpp, "%s", getSaveMethodTemplate().c_str()); @@ -139,7 +139,7 @@ namespace bin2cpp fprintf(cpp, " const %s & %s() { static %s _instance; return _instance; }\n", getContext().baseClass.c_str(), getterFunctionName.c_str(), className.c_str()); if (mContext.registerFiles) { - std::string fileManagerTemplate = getFileManagerRegistrationTemplate(); + std::string fileManagerTemplate = getCppFileManagerRegistrationImplementationTemplate(); fprintf(cpp, "%s", fileManagerTemplate.c_str()); } fprintf(cpp, "}; //%s\n", getContext().codeNamespace.c_str()); @@ -192,4 +192,166 @@ namespace bin2cpp return true; } + bool ArrayGenerator::createCSourceFile(const char* file_path) + { + //check if input file exists + FILE* input = fopen(mContext.inputFilePath.c_str(), "rb"); + if ( !input ) + return false; + + //Lowercase function identifier + std::string functionIdentifier = ra::strings::Lowercase(mContext.functionIdentifier); + + //Build header and cpp file path + std::string headerPath = getHeaderFilePath(file_path); + std::string sourcePath = file_path; + + //create c source file + FILE* fout = fopen(sourcePath.c_str(), "w"); + if ( !fout ) + { + fclose(input); + return false; + } + + //determine file properties + uint32_t fileSize = ra::filesystem::GetFileSize(input); + std::string filename = ra::filesystem::GetFilename(mContext.inputFilePath.c_str()); + //long lastSegmentSize = fileSize%chunk_size; + //size_t numSegments = fileSize/chunk_size + (lastSegmentSize == 0 ? 0 : 1); + + //Build class name + std::string className = getClassName(); + + //Build function + std::string getterFunctionName = getGetterFunctionName(); + + //Build FileManager class template + std::string manager = mContext.managerHeaderFilename; + + //write c file heading + fprintf(fout, "%s", getHeaderTemplate().c_str()); + fprintf(fout, "#if defined(_WIN32) && !defined(_CRT_SECURE_NO_WARNINGS)\n"); + fprintf(fout, "#define _CRT_SECURE_NO_WARNINGS\n"); + fprintf(fout, "#endif\n"); + fprintf(fout, "#include \"%s\"\n", mContext.headerFilename.c_str()); + fprintf(fout, "#include // for malloc\n"); + fprintf(fout, "#include // for memset\n"); + fprintf(fout, "#include // for fopen\n"); + + fprintf(fout, "static %s %s_file = { 0 };\n", mContext.baseClass.c_str(), functionIdentifier.c_str()); + fprintf(fout, "static bool %s_initialized = false;\n", functionIdentifier.c_str()); + fprintf(fout, "\n"); + + // File registration predeclaration code + fprintf(fout, "%s", getCFileManagerRegistrationPredeclarationTemplate().c_str()); + + fprintf(fout, "bool %s_load()\n", functionIdentifier.c_str()); + fprintf(fout, "{\n"); + fprintf(fout, " if ( %s_file.buffer )\n", functionIdentifier.c_str()); + fprintf(fout, " return true;\n"); + fprintf(fout, " static const unsigned char static_buffer[] = {\n"); + + //create buffer for each chunks from input buffer + int numLinePrinted = 0; + unsigned char* buffer = new unsigned char[getContext().chunkSize]; + while ( !feof(input) ) + { + //read a chunk of the file + size_t readSize = fread(buffer, 1, getContext().chunkSize, input); + + bool isLastChunk = !(readSize == getContext().chunkSize); + + if ( readSize > 0 ) + { + if ( numLinePrinted > 0 ) + { + //end previous line + fprintf(fout, ",\n"); + } + + //output + fprintf(fout, " %s", ra::code::cpp::ToCppCharactersArray(buffer, readSize).c_str()); + numLinePrinted++; + } + + //end the array. all the file content is printed + if ( isLastChunk ) + { + fprintf(fout, "\n"); + fprintf(fout, " };\n"); + } + } + delete[] buffer; + buffer = NULL; + + //write c source file footer + fprintf(fout, "\n"); + fprintf(fout, " %s_file.buffer = static_buffer;\n", functionIdentifier.c_str()); + fprintf(fout, " return true;\n"); + fprintf(fout, "}\n"); + + fprintf(fout, "\n"); + + fprintf(fout, "void %s_free()\n", functionIdentifier.c_str()); + fprintf(fout, "{\n"); + fprintf(fout, " %s_file.buffer = NULL;\n", functionIdentifier.c_str()); + fprintf(fout, "}\n"); + fprintf(fout, "\n"); + fprintf(fout, "bool %s_save(const char* path)\n", functionIdentifier.c_str()); + fprintf(fout, "{\n"); + fprintf(fout, " if ( !%s_file.buffer )\n", functionIdentifier.c_str()); + fprintf(fout, " return false;\n"); + fprintf(fout, " FILE* f = fopen(path, \"wb\");\n"); + fprintf(fout, " if ( !f )\n"); + fprintf(fout, " return false;\n"); + fprintf(fout, " size_t write_size = fwrite(%s_file.buffer, 1, %s_file.size, f);\n", functionIdentifier.c_str(), functionIdentifier.c_str()); + fprintf(fout, " fclose(f);\n"); + fprintf(fout, " if ( write_size != %s_file.size )\n", functionIdentifier.c_str()); + fprintf(fout, " return false;\n"); + fprintf(fout, " return true;\n"); + fprintf(fout, "}\n"); + fprintf(fout, "\n"); + fprintf(fout, "static inline void %s_init()\n", functionIdentifier.c_str()); + fprintf(fout, "{\n"); + fprintf(fout, " // remember we already initialized\n"); + fprintf(fout, " if ( %s_initialized )\n", functionIdentifier.c_str()); + fprintf(fout, " return;\n"); + fprintf(fout, " %s_initialized = true;\n", functionIdentifier.c_str()); + fprintf(fout, "\n"); + fprintf(fout, " // initialize\n"); + fprintf(fout, " %s* file = &%s_file;\n", mContext.baseClass.c_str(), functionIdentifier.c_str()); + fprintf(fout, " file->size = %uULL;\n", fileSize); + fprintf(fout, " file->file_name = \"%s\";\n", getFileClassFileName().c_str()); + fprintf(fout, " file->file_path = \"%s\";\n", getFileClassFilePath().c_str()); + fprintf(fout, " file->buffer = NULL;\n"); + fprintf(fout, " file->load = %s_load;\n", functionIdentifier.c_str()); + fprintf(fout, " file->unload = %s_free;\n", functionIdentifier.c_str()); + fprintf(fout, " file->save = %s_save;\n", functionIdentifier.c_str()); + fprintf(fout, "\n"); + fprintf(fout, " // load file by default on init as in c++ implementation"); + fprintf(fout, " file->load();\n"); + if ( mContext.registerFiles ) + { + fprintf(fout, " \n"); + fprintf(fout, " // register when loaded if static initialisation does not work\n"); + fprintf(fout, " %s_filemanager_register_file(file);\n", mContext.codeNamespace.c_str()); + } + fprintf(fout, "}\n"); + fprintf(fout, "\n"); + fprintf(fout, "%s* %s(void)\n", mContext.baseClass.c_str(), getGetterFunctionName().c_str()); + fprintf(fout, "{\n"); + fprintf(fout, " %s_init();\n", functionIdentifier.c_str()); + fprintf(fout, " return &%s_file;\n", functionIdentifier.c_str()); + fprintf(fout, "}\n"); + + // File registration implementation code + fprintf(fout, "%s", getCFileManagerRegistrationImplementationTemplate().c_str()); + + fclose(input); + fclose(fout); + + return true; + } + }; //bin2cpp \ No newline at end of file diff --git a/src/bin2cpp/ArrayGenerator.h b/src/bin2cpp/ArrayGenerator.h index a0f15d5..46ec570 100644 --- a/src/bin2cpp/ArrayGenerator.h +++ b/src/bin2cpp/ArrayGenerator.h @@ -41,6 +41,7 @@ namespace bin2cpp virtual ~ArrayGenerator(); virtual const char * getName() const; virtual bool createCppSourceFile(const char * cpp_file_path); + virtual bool createCSourceFile(const char * cpp_file_path); virtual bool printFileContent(); }; diff --git a/src/bin2cpp/BaseGenerator.cpp b/src/bin2cpp/BaseGenerator.cpp index 46d0c19..980fefa 100755 --- a/src/bin2cpp/BaseGenerator.cpp +++ b/src/bin2cpp/BaseGenerator.cpp @@ -61,13 +61,33 @@ namespace bin2cpp std::string BaseGenerator::getGetterFunctionName() { - //Uppercase function identifier - std::string functionIdentifier = ra::strings::CapitalizeFirstCharacter(mContext.functionIdentifier); std::string getter; - getter.append("get"); - getter.append(functionIdentifier); - getter.append("File"); + switch ( mContext.code ) + { + default: + case CODE_GENERATION_CPP: + { + //Uppercase function identifier + std::string functionIdentifier = ra::strings::CapitalizeFirstCharacter(mContext.functionIdentifier); + + getter.append("get"); + getter.append(functionIdentifier); + getter.append("File"); + } + break; + case CODE_GENERATION_C: + { + //Uppercase function identifier + std::string functionIdentifier = ra::strings::Lowercase(mContext.functionIdentifier); + + getter.append(mContext.codeNamespace); + getter.append("_get_file_"); + getter.append(functionIdentifier); + } + break; + }; + return getter; } @@ -75,7 +95,17 @@ namespace bin2cpp { //Build header file path std::string headerPath = cpp_file_path; - ra::strings::Replace(headerPath, ".cpp", ".h"); + switch ( mContext.code ) + { + default: + case CODE_GENERATION_CPP: + ra::strings::Replace(headerPath, ".cpp", ".h"); + break; + case CODE_GENERATION_C: + ra::strings::Replace(headerPath, ".c", ".h"); + break; + }; + return headerPath; } @@ -83,7 +113,17 @@ namespace bin2cpp { //Build header file path std::string cppPath = header_file_path; - ra::strings::Replace(cppPath, ".cpp", ".h"); + switch ( mContext.code ) + { + default: + case CODE_GENERATION_CPP: + ra::strings::Replace(cppPath, ".cpp", ".h"); + break; + case CODE_GENERATION_C: + ra::strings::Replace(cppPath, ".c", ".h"); + break; + }; + return cppPath; } @@ -127,7 +167,7 @@ namespace bin2cpp return output; } - std::string BaseGenerator::getFileManagerRegistrationTemplate() + std::string BaseGenerator::getCppFileManagerRegistrationImplementationTemplate() { if (!mContext.registerFiles) return std::string(); @@ -142,6 +182,41 @@ namespace bin2cpp return output; } + std::string BaseGenerator::getCFileManagerRegistrationPredeclarationTemplate() + { + if ( !mContext.registerFiles ) + return std::string(); + + std::string output; + output << "extern bool " << mContext.codeNamespace << "_filemanager_register_file(" << mContext.baseClass << " * file); \n"; + output << "\n"; + return output; + } + + std::string BaseGenerator::getCFileManagerRegistrationImplementationTemplate() + { + if ( !mContext.registerFiles ) + return std::string(); + + //Lowercase function identifier + std::string functionIdentifier = ra::strings::Lowercase(mContext.functionIdentifier); + + std::string output; + output << "#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__) // GCC 4.0+ required, Clang supports it by default\n"; + output << "__attribute__((constructor))\n"; + output << "#endif\n"; + output << "void " << mContext.codeNamespace << "_register_file_static_init_" << functionIdentifier << "(void)\n"; + output << "{\n"; + output << " " << mContext.baseClass << "* this_file = " << mContext.codeNamespace << "_get_file_" << functionIdentifier << "();\n"; + output << " " << mContext.codeNamespace << "_filemanager_register_file(this_file);\n"; + output << "}\n"; + output << "#if _MSC_VER >= 1920 // Visual Studio 2019 or later\n"; + output << "#pragma section(\".CRT$XCU\", read)\n"; + output << "__declspec(allocate(\".CRT$XCU\")) void (*init_ptr_" << functionIdentifier << ")(void) = " << mContext.codeNamespace << "_register_file_static_init_" << functionIdentifier << ";\n"; + output << "#endif\n"; + return output; + } + std::string BaseGenerator::getClassName() { std::string functionIdentifier = ra::strings::CapitalizeFirstCharacter(mContext.functionIdentifier); @@ -225,6 +300,50 @@ namespace bin2cpp return output; } + std::string BaseGenerator::getFileClassFileName() + { + std::string output; + + std::string inputFileName = ra::filesystem::GetFilename(mContext.inputFilePath.c_str()); + + //return default implementation + output += inputFileName; + return output; + } + + std::string BaseGenerator::getFileClassFilePath() + { + std::string output; + + //convert mReportedFilePath string to c++ + std::string path = mContext.reportedFilePath; +#ifdef _WIN32 + //escape backslash characters for c++ + static const std::string BACKSLASH = "\\"; + static const std::string BACKSLASH_ESCAPED = "\\\\"; + ra::strings::Replace(path, BACKSLASH, BACKSLASH_ESCAPED); +#endif + + //is there a reported path specified ? + const char * reported_path = mContext.reportedFilePath.c_str(); + if (reported_path != NULL && reported_path[0] != '\0') + { + output += path; + return output; + } + else + { + //if reported path is not specified ? + //report the same as getFileName() + output = getFileClassFileName(); + return output; + } + + //return default implementation + output += path; + return output; + } + bool BaseGenerator::createCppHeaderFile(const char * header_file_path) { FILE * header = fopen(header_file_path, "w"); @@ -320,4 +439,56 @@ namespace bin2cpp return true; } + bool BaseGenerator::createCHeaderFile(const char* file_path) + { + FILE* header = fopen(file_path, "w"); + if ( !header ) + return false; + + //define macro guard matching the filename + std::string macroGuard = getCppIncludeGuardMacroName(file_path); + + std::string classMacroGuardPrefix = getClassMacroGuardPrefix(); + std::string fileHeader = getHeaderTemplate(); + + fprintf(header, "%s", fileHeader.c_str()); + fprintf(header, "#ifndef %s\n", macroGuard.c_str()); + fprintf(header, "#define %s\n", macroGuard.c_str()); + fprintf(header, "\n"); + fprintf(header, "#include \n"); + fprintf(header, "#include \n"); + fprintf(header, "\n"); + fprintf(header, "#ifndef %s_EMBEDDEDFILE_STRUCT\n", classMacroGuardPrefix.c_str()); + fprintf(header, "#define %s_EMBEDDEDFILE_STRUCT\n", classMacroGuardPrefix.c_str()); + fprintf(header, "typedef struct %s %s;\n", mContext.baseClass.c_str(), mContext.baseClass.c_str()); + fprintf(header, "typedef bool(*%s_load_func)();\n", mContext.codeNamespace.c_str()); + fprintf(header, "typedef void(*%s_free_func)();\n", mContext.codeNamespace.c_str()); + fprintf(header, "typedef bool(*%s_save_func)(const char*);\n", mContext.codeNamespace.c_str()); + fprintf(header, "typedef struct %s\n", mContext.baseClass.c_str()); + fprintf(header, "{\n"); + fprintf(header, " size_t size;\n"); + fprintf(header, " const char* file_name;\n"); + fprintf(header, " const char* file_path;\n"); + fprintf(header, " const unsigned char* buffer;\n"); + fprintf(header, " %s_load_func load;\n", mContext.codeNamespace.c_str()); + fprintf(header, " %s_free_func unload;\n", mContext.codeNamespace.c_str()); + fprintf(header, " %s_save_func save;\n", mContext.codeNamespace.c_str()); + fprintf(header, "} %s;\n", mContext.baseClass.c_str()); + fprintf(header, "typedef %s* %sPtr;\n", mContext.baseClass.c_str(), mContext.baseClass.c_str()); + fprintf(header, "#endif //%s_EMBEDDEDFILE_STRUCT\n", classMacroGuardPrefix.c_str()); + fprintf(header, "%s* %s(void);\n", mContext.baseClass.c_str(), getGetterFunctionName().c_str()); + fprintf(header, "\n"); + fprintf(header, "#endif //%s\n", macroGuard.c_str()); + + fclose(header); + + return true; + } + + bool BaseGenerator::createCSourceFile(const char* file_path) + { + // not supported yet + return false; + } + }; //bin2cpp \ No newline at end of file diff --git a/src/bin2cpp/BaseGenerator.h b/src/bin2cpp/BaseGenerator.h index 0fcfd6e..31473df 100644 --- a/src/bin2cpp/BaseGenerator.h +++ b/src/bin2cpp/BaseGenerator.h @@ -46,6 +46,8 @@ namespace bin2cpp //same header file for all generators virtual bool createCppHeaderFile(const char * header_file_path); virtual bool printFileContent(); + virtual bool createCHeaderFile(const char* file_path); + virtual bool createCSourceFile(const char* file_path); protected: @@ -55,11 +57,15 @@ namespace bin2cpp virtual std::string getHeaderTemplate(); virtual std::string getHeaderTemplate(bool include_source_file); virtual std::string getSaveMethodTemplate(); - virtual std::string getFileManagerRegistrationTemplate(); + virtual std::string getCppFileManagerRegistrationImplementationTemplate(); + virtual std::string getCFileManagerRegistrationPredeclarationTemplate(); + virtual std::string getCFileManagerRegistrationImplementationTemplate(); virtual std::string getClassName(); virtual std::string getClassMacroGuardPrefix(); virtual std::string getImplOfGetFileName(); virtual std::string getImplOfGetFilePath(); + virtual std::string getFileClassFileName(); + virtual std::string getFileClassFilePath(); //attributes Context mContext; diff --git a/src/bin2cpp/CMakeLists.txt b/src/bin2cpp/CMakeLists.txt index 324d89c..326f03b 100644 --- a/src/bin2cpp/CMakeLists.txt +++ b/src/bin2cpp/CMakeLists.txt @@ -21,6 +21,9 @@ add_executable(bin2cpp crc32.h enums.h IGenerator.h + INameProvider.h + LegacyNameProvider.cpp + LegacyNameProvider.h main.cpp ManagerGenerator.cpp ManagerGenerator.h @@ -28,6 +31,7 @@ add_executable(bin2cpp SegmentGenerator.h StringGenerator.cpp StringGenerator.h + types.h wildcard.cpp wildcard.h Win32ResourceGenerator.cpp diff --git a/src/bin2cpp/Context.cpp b/src/bin2cpp/Context.cpp index fcd5e6a..dee7bbc 100644 --- a/src/bin2cpp/Context.cpp +++ b/src/bin2cpp/Context.cpp @@ -69,6 +69,7 @@ namespace bin2cpp this->generatorName = other.generatorName ; this->directoryIncludeFilters = other.directoryIncludeFilters ; this->directoryExcludeFilters = other.directoryExcludeFilters ; + this->code = other.code ; } return *this; } @@ -101,6 +102,7 @@ namespace bin2cpp generatorName.clear(); directoryIncludeFilters.clear(); directoryExcludeFilters.clear(); + code = CodeGenerationEnum::CODE_GENERATION_CPP; } diff --git a/src/bin2cpp/Context.h b/src/bin2cpp/Context.h index 315d32f..3262713 100644 --- a/src/bin2cpp/Context.h +++ b/src/bin2cpp/Context.h @@ -72,6 +72,7 @@ namespace bin2cpp std::string generatorName; std::vector directoryIncludeFilters; std::vector directoryExcludeFilters; + CodeGenerationEnum code; void reset(); diff --git a/src/bin2cpp/IGenerator.h b/src/bin2cpp/IGenerator.h index d7b487e..5b459cb 100644 --- a/src/bin2cpp/IGenerator.h +++ b/src/bin2cpp/IGenerator.h @@ -61,6 +61,20 @@ namespace bin2cpp ///Returns true when the file was created. Returns false otherwise. virtual bool createCppSourceFile(const char * cpp_file_path) = 0; + /// + ///Creates a header file for embedding a given file into C source code. + /// + ///The path of the header file (*.h) to generate. + ///Returns true when the file was created. Returns false otherwise. + virtual bool createCHeaderFile(const char* file_path) = 0; + + /// + ///Creates a C source file for embedding a given file into C source code. + /// + ///The path of the source file (*.c) to generate. + ///Returns true when the file was created. Returns false otherwise. + virtual bool createCSourceFile(const char* file_path) = 0; + /// ///Print the encoded file content to stdout /// diff --git a/src/bin2cpp/INameProvider.h b/src/bin2cpp/INameProvider.h new file mode 100644 index 0000000..173e233 --- /dev/null +++ b/src/bin2cpp/INameProvider.h @@ -0,0 +1,53 @@ +/********************************************************************************** + * MIT License + * + * Copyright (c) 2018 Antoine Beauchamp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *********************************************************************************/ + +#ifndef INAME_PROVIDER_H +#define INAME_PROVIDER_H + +#include "types.h" + +namespace bin2cpp +{ + + class INameProvider + { + public: + + /// + ///Get the name of a function identifier based on a given file path. + /// + ///The new context. + virtual std::string getDefaultFunctionIdentifier(const std::string & path, Dictionary & dict) = 0; + + /// + ///Get the name of a function identifier based on a given file path. + /// + ///The new context. + virtual std::string getDefaultHeaderFile(const std::string& path) = 0; + + }; + +}; //bin2cpp + +#endif //INAME_PROVIDER_H diff --git a/src/bin2cpp/LegacyNameProvider.cpp b/src/bin2cpp/LegacyNameProvider.cpp new file mode 100644 index 0000000..5020242 --- /dev/null +++ b/src/bin2cpp/LegacyNameProvider.cpp @@ -0,0 +1,64 @@ +/********************************************************************************** + * MIT License + * + * Copyright (c) 2018 Antoine Beauchamp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *********************************************************************************/ + +#include "LegacyNameProvider.h" + +#include "common.h" + +#include "rapidassist/strings.h" +#include "rapidassist/filesystem.h" + +namespace bin2cpp +{ + LegacyNameProvider::LegacyNameProvider() + { + } + + LegacyNameProvider::~LegacyNameProvider() + { + } + + std::string LegacyNameProvider::getDefaultFunctionIdentifier(const std::string& path, Dictionary& dict) + { + std::string output; + + //use the file name without extension as 'identifier'. + output = getUniqueFunctionIdentifierFromPath(path.c_str(), dict); + output = ra::strings::CapitalizeFirstCharacter(output); + + return output; + } + + std::string LegacyNameProvider::getDefaultHeaderFile(const std::string& path) + { + std::string output; + + //use the file name without extension as 'headerfile'. + output = ra::filesystem::GetFilenameWithoutExtension(path.c_str()); + output += ".h"; + + return output; + } + +}; //bin2cpp \ No newline at end of file diff --git a/src/bin2cpp/LegacyNameProvider.h b/src/bin2cpp/LegacyNameProvider.h new file mode 100644 index 0000000..29a1bd0 --- /dev/null +++ b/src/bin2cpp/LegacyNameProvider.h @@ -0,0 +1,48 @@ +/********************************************************************************** + * MIT License + * + * Copyright (c) 2018 Antoine Beauchamp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *********************************************************************************/ + +#ifndef LEGACYNAMEPROVIDER_H +#define LEGACYNAMEPROVIDER_H + +#include "INameProvider.h" + +namespace bin2cpp +{ + /// + ///This is the legacy name generator which generates names for version 3.0.1 and before. + /// + class LegacyNameProvider : public virtual INameProvider + { + public: + LegacyNameProvider(); + virtual ~LegacyNameProvider(); + + //INameProvider methods + virtual std::string getDefaultFunctionIdentifier(const std::string& path, Dictionary& dict); + virtual std::string getDefaultHeaderFile(const std::string& path); + }; + +}; //bin2cpp + +#endif //LEGACYNAMEPROVIDER_H diff --git a/src/bin2cpp/ManagerGenerator.cpp b/src/bin2cpp/ManagerGenerator.cpp index 72a8126..fa38a8f 100644 --- a/src/bin2cpp/ManagerGenerator.cpp +++ b/src/bin2cpp/ManagerGenerator.cpp @@ -239,6 +239,233 @@ namespace bin2cpp return true; } + bool ManagerGenerator::createCHeaderFile(const char* file_path) + { + FILE* fout = fopen(file_path, "w"); + if ( !fout ) + return false; + + //define macro guard a macro matching the filename + std::string macroGuard; + macroGuard += getCppIncludeGuardMacroName(mContext.codeNamespace.c_str()); //prefix the custom namespace for the file manager + if ( !macroGuard.empty() ) + macroGuard += "_"; + macroGuard += getCppIncludeGuardMacroName(file_path); + + std::string classMacroGuardPrefix = getClassMacroGuardPrefix(); + std::string fileHeader = getHeaderTemplate(false); + + fprintf(fout, "%s", fileHeader.c_str()); + fprintf(fout, "#ifndef %s\n", macroGuard.c_str()); + fprintf(fout, "#define %s\n", macroGuard.c_str()); + fprintf(fout, "\n"); + fprintf(fout, "#include \n"); + fprintf(fout, "#include \n"); + fprintf(fout, "\n"); + fprintf(fout, "#ifndef %s_EMBEDDEDFILE_STRUCT\n", classMacroGuardPrefix.c_str()); + fprintf(fout, "#define %s_EMBEDDEDFILE_STRUCT\n", classMacroGuardPrefix.c_str()); + fprintf(fout, "typedef struct %s %s;\n", mContext.baseClass.c_str(), mContext.baseClass.c_str()); + fprintf(fout, "typedef bool(*%s_load_func)();\n", mContext.codeNamespace.c_str()); + fprintf(fout, "typedef void(*%s_free_func)();\n", mContext.codeNamespace.c_str()); + fprintf(fout, "typedef bool(*%s_save_func)(const char*);\n", mContext.codeNamespace.c_str()); + fprintf(fout, "typedef struct %s\n", mContext.baseClass.c_str()); + fprintf(fout, "{\n"); + fprintf(fout, " size_t size;\n"); + fprintf(fout, " const char* file_name;\n"); + fprintf(fout, " const char* file_path;\n"); + fprintf(fout, " const unsigned char* buffer;\n"); + fprintf(fout, " %s_load_func load;\n", mContext.codeNamespace.c_str()); + fprintf(fout, " %s_free_func unload;\n", mContext.codeNamespace.c_str()); + fprintf(fout, " %s_save_func save;\n", mContext.codeNamespace.c_str()); + fprintf(fout, "} %s;\n", mContext.baseClass.c_str()); + fprintf(fout, "typedef %s* %sPtr;\n", mContext.baseClass.c_str(), mContext.baseClass.c_str()); + fprintf(fout, "#endif //%s_EMBEDDEDFILE_STRUCT\n", classMacroGuardPrefix.c_str()); + fprintf(fout, "\n"); + fprintf(fout, "size_t %s_filemanager_get_file_count();\n", mContext.codeNamespace.c_str()); + fprintf(fout, "bool %s_filemanager_register_file(%s* file);\n", mContext.codeNamespace.c_str(), mContext.baseClass.c_str()); + fprintf(fout, "const %s* %s_filemanager_get_file(size_t index);\n", mContext.baseClass.c_str(), mContext.codeNamespace.c_str()); + fprintf(fout, "bool %s_filemanager_save_files(const char* directory);\n", mContext.codeNamespace.c_str()); + fprintf(fout, "\n"); + fprintf(fout, "#endif //%s\n", macroGuard.c_str()); + + fclose(fout); + + return true; + } + + bool ManagerGenerator::createCSourceFile(const char* file_path) + { + FILE* fout = fopen(file_path, "w"); + if ( !fout ) + return false; + + //Build header and cpp file path + std::string headerPath = getHeaderFilePath(file_path); + std::string sourcePath = file_path; + + std::string fileHeader = getHeaderTemplate(false); + + fprintf(fout, "%s", fileHeader.c_str()); + fprintf(fout, "#if defined(_WIN32) && !defined(_CRT_SECURE_NO_WARNINGS)\n"); + fprintf(fout, "#define _CRT_SECURE_NO_WARNINGS\n"); + fprintf(fout, "#endif\n"); + fprintf(fout, "\n"); + fprintf(fout, "#include \"%s\"\n", mContext.managerHeaderFilename.c_str()); + fprintf(fout, "#include // for malloc\n"); + fprintf(fout, "#include // for snprintf()\n"); + fprintf(fout, "#include // strlen\n"); + fprintf(fout, "#include // stat\n"); + fprintf(fout, "#include // errno, EEXIST\n"); + fprintf(fout, "#if defined(_WIN32)\n"); + fprintf(fout, "#include // _mkdir\n"); + fprintf(fout, "#endif\n"); + fprintf(fout, "#if defined(_WIN32)\n"); + fprintf(fout, "#define portable_stat _stat\n"); + fprintf(fout, "#define portable_mkdir(path) _mkdir(path)\n"); + fprintf(fout, "#define PATH_SEPARATOR_CHAR '\\\\'\n"); + fprintf(fout, "#define PATH_SEPARATOR_STR \"\\\\\"\n"); + fprintf(fout, "#else\n"); + fprintf(fout, "#define portable_stat stat\n"); + fprintf(fout, "#define portable_mkdir(path) mkdir(path, 0755)\n"); + fprintf(fout, "#define PATH_SEPARATOR_CHAR '/'\n"); + fprintf(fout, "#define PATH_SEPARATOR_STR \"/\"\n"); + fprintf(fout, "#endif\n"); + fprintf(fout, "\n"); + fprintf(fout, "#define BIN2C_MAX_PATH 32767\n"); + fprintf(fout, "\n"); + fprintf(fout, "static %s** registered_files = NULL;\n", mContext.baseClass.c_str()); + fprintf(fout, "static size_t registered_files_count = 0;\n"); + fprintf(fout, "\n"); + fprintf(fout, "size_t %s_filemanager_get_file_count()\n", mContext.codeNamespace.c_str()); + fprintf(fout, "{\n"); + fprintf(fout, " return registered_files_count;\n"); + fprintf(fout, "}\n"); + fprintf(fout, "\n"); + fprintf(fout, "bool %s_filemanager_register_file(%s* file)\n", mContext.codeNamespace.c_str(), mContext.baseClass.c_str()); + fprintf(fout, "{\n"); + fprintf(fout, " // check if already registered\n"); + fprintf(fout, " if ( registered_files_count && registered_files )\n"); + fprintf(fout, " {\n"); + fprintf(fout, " for ( size_t i = 0; i < registered_files_count; i++ )\n"); + fprintf(fout, " {\n"); + fprintf(fout, " const %s* existing_file = registered_files[i];\n", mContext.baseClass.c_str()); + fprintf(fout, " if ( existing_file == file )\n"); + fprintf(fout, " return true; // nothing to do\n"); + fprintf(fout, " }\n"); + fprintf(fout, " }\n"); + fprintf(fout, " \n"); + fprintf(fout, " // allocate ram\n"); + fprintf(fout, " size_t new_ram_size = sizeof(%s**) * (registered_files_count + 1);\n", mContext.baseClass.c_str()); + fprintf(fout, " %s** tmp = NULL;\n", mContext.baseClass.c_str()); + fprintf(fout, " if ( registered_files == NULL )\n"); + fprintf(fout, " tmp = (%s**)malloc(new_ram_size);\n", mContext.baseClass.c_str()); + fprintf(fout, " else\n"); + fprintf(fout, " tmp = (%s**)realloc(registered_files, new_ram_size);\n", mContext.baseClass.c_str()); + fprintf(fout, " if ( tmp == NULL )\n"); + fprintf(fout, " return false;\n"); + fprintf(fout, " \n"); + fprintf(fout, " registered_files = tmp;\n"); + fprintf(fout, " registered_files_count++;\n"); + fprintf(fout, " \n"); + fprintf(fout, " // insert\n"); + fprintf(fout, " registered_files[registered_files_count - 1] = file;\n"); + fprintf(fout, " \n"); + fprintf(fout, " return true;\n"); + fprintf(fout, "}\n"); + fprintf(fout, "\n"); + fprintf(fout, "const %s* %s_filemanager_get_file(size_t index)\n", mContext.baseClass.c_str(), mContext.codeNamespace.c_str()); + fprintf(fout, "{\n"); + fprintf(fout, " if ( index >= registered_files_count )\n"); + fprintf(fout, " return NULL;\n"); + fprintf(fout, " return registered_files[index];\n"); + fprintf(fout, "}\n"); + fprintf(fout, "\n"); + fprintf(fout, "static inline bool %s_filemanager_is_root_directory(const char* path)\n", mContext.codeNamespace.c_str()); + fprintf(fout, "{\n"); + fprintf(fout, " if ( path == NULL && path[0] == '\\0' )\n"); + fprintf(fout, " return false;\n"); + fprintf(fout, "#if defined(_WIN32)\n"); + fprintf(fout, " bool is_drive_letter = ((path[0] >= 'a' && path[0] <= 'z') || (path[0] >= 'A' && path[0] <= 'Z'));\n"); + fprintf(fout, " if ( (is_drive_letter && path[1] == ':' && path[2] == '\\0') || // test for C:\n"); + fprintf(fout, " (is_drive_letter && path[1] == ':' && path[2] == PATH_SEPARATOR_CHAR && path[3] == '\\0') ) // test for C:\\ \n"); + fprintf(fout, " return true;\n"); + fprintf(fout, "#else\n"); + fprintf(fout, " if ( path[0] == PATH_SEPARATOR_CHAR )\n"); + fprintf(fout, " return true;\n"); + fprintf(fout, "#endif\n"); + fprintf(fout, " return false;\n"); + fprintf(fout, "}\n"); + fprintf(fout, "\n"); + fprintf(fout, "bool %s_filemanager_create_parent_directories(const char* file_path)\n", mContext.codeNamespace.c_str()); + fprintf(fout, "{\n"); + fprintf(fout, " if ( file_path == NULL )\n"); + fprintf(fout, " return false;\n"); + fprintf(fout, " char* accumulator = (char*)malloc(BIN2C_MAX_PATH);\n"); + fprintf(fout, " if ( accumulator == NULL )\n"); + fprintf(fout, " return false;\n"); + fprintf(fout, " accumulator[0] = '\\0';\n"); + fprintf(fout, " size_t length = strlen(file_path);\n"); + fprintf(fout, " for ( size_t i = 0; i < length; i++ )\n"); + fprintf(fout, " {\n"); + fprintf(fout, " if ( file_path[i] == PATH_SEPARATOR_CHAR && !(accumulator[0] == '\\0') && !%s_filemanager_is_root_directory(accumulator) )\n", mContext.codeNamespace.c_str()); + fprintf(fout, " {\n"); + fprintf(fout, " int ret = portable_mkdir(accumulator);\n"); + fprintf(fout, " if ( ret != 0 && errno != EEXIST )\n"); + fprintf(fout, " {\n"); + fprintf(fout, " free(accumulator);\n"); + fprintf(fout, " return false;\n"); + fprintf(fout, " }\n"); + fprintf(fout, " }\n"); + fprintf(fout, " \n"); + fprintf(fout, " // append\n"); + fprintf(fout, " char tmp[] = { file_path[i], '\\0' };\n"); + fprintf(fout, " strcat(accumulator, tmp);\n"); + fprintf(fout, " }\n"); + fprintf(fout, " free(accumulator);\n"); + fprintf(fout, " return true;\n"); + fprintf(fout, "}\n"); + fprintf(fout, "\n"); + fprintf(fout, "bool %s_filemanager_save_files(const char * directory)\n", mContext.codeNamespace.c_str()); + fprintf(fout, "{\n"); + fprintf(fout, " if (directory == NULL)\n"); + fprintf(fout, " return false;\n"); + fprintf(fout, " char* path = (char*)malloc(BIN2C_MAX_PATH);\n"); + fprintf(fout, " if ( path == NULL )\n"); + fprintf(fout, " return false;\n"); + fprintf(fout, " path[0] = '\\0';\n"); + fprintf(fout, " for(size_t i=0; i< registered_files_count; i++)\n"); + fprintf(fout, " {\n"); + fprintf(fout, " const %s* f = %s_filemanager_get_file(i);\n", mContext.baseClass.c_str(), mContext.codeNamespace.c_str()); + fprintf(fout, " if ( !f )\n"); + fprintf(fout, " {\n"); + fprintf(fout, " free(path);\n"); + fprintf(fout, " return false;\n"); + fprintf(fout, " }\n"); + fprintf(fout, " \n"); + fprintf(fout, " snprintf(path, sizeof(path), \"%%s%%c%%s\", directory, PATH_SEPARATOR_CHAR, f->file_path);\n"); + fprintf(fout, " \n"); + fprintf(fout, " if (!%s_filemanager_create_parent_directories(path))\n", mContext.codeNamespace.c_str()); + fprintf(fout, " {\n"); + fprintf(fout, " free(path);\n"); + fprintf(fout, " return false;\n"); + fprintf(fout, " }\n"); + fprintf(fout, " bool saved = f->save(path);\n"); + fprintf(fout, " if (!saved)\n"); + fprintf(fout, " {\n"); + fprintf(fout, " free(path);\n"); + fprintf(fout, " return false;\n"); + fprintf(fout, " }\n"); + fprintf(fout, " }\n"); + fprintf(fout, " free(path);\n"); + fprintf(fout, " return true;\n"); + fprintf(fout, "}\n"); + fprintf(fout, "\n"); + + fclose(fout); + + return true; + } + bool ManagerGenerator::printFileContent() { return false; diff --git a/src/bin2cpp/ManagerGenerator.h b/src/bin2cpp/ManagerGenerator.h index 101e412..a6d7981 100644 --- a/src/bin2cpp/ManagerGenerator.h +++ b/src/bin2cpp/ManagerGenerator.h @@ -41,6 +41,8 @@ namespace bin2cpp virtual const char * getName() const; virtual bool createCppHeaderFile(const char* header_file_path); virtual bool createCppSourceFile(const char * cpp_file_path); + virtual bool createCHeaderFile(const char* file_path); + virtual bool createCSourceFile(const char* file_path); virtual bool printFileContent(); }; diff --git a/src/bin2cpp/SegmentGenerator.cpp b/src/bin2cpp/SegmentGenerator.cpp index 8099bbd..071365d 100755 --- a/src/bin2cpp/SegmentGenerator.cpp +++ b/src/bin2cpp/SegmentGenerator.cpp @@ -61,7 +61,7 @@ namespace bin2cpp std::string headerPath = getHeaderFilePath(cpp_file_path); std::string cppPath = cpp_file_path; - //create cpp file + //create cpp source file FILE * cpp = fopen(cppPath.c_str(), "w"); if (!cpp) { @@ -140,7 +140,7 @@ namespace bin2cpp delete[] buffer; buffer = NULL; - //write cpp file footer + //write cpp source file footer fprintf(cpp, " }\n"); fprintf(cpp, "%s", getSaveMethodTemplate().c_str()); fprintf(cpp, " private:\n"); @@ -149,7 +149,7 @@ namespace bin2cpp fprintf(cpp, " const %s & %s() { static %s _instance; return _instance; }\n", getContext().baseClass.c_str(), getterFunctionName.c_str(), className.c_str()); if (mContext.registerFiles) { - std::string fileManagerTemplate = getFileManagerRegistrationTemplate(); + std::string fileManagerTemplate = getCppFileManagerRegistrationImplementationTemplate(); fprintf(cpp, "%s", fileManagerTemplate.c_str()); } fprintf(cpp, "}; //%s\n", getContext().codeNamespace.c_str()); @@ -160,4 +160,171 @@ namespace bin2cpp return true; } + bool SegmentGenerator::createCSourceFile(const char* file_path) + { + //check if input file exists + FILE* input = fopen(mContext.inputFilePath.c_str(), "rb"); + if ( !input ) + return false; + + //Lowercase function identifier + std::string functionIdentifier = ra::strings::Lowercase(mContext.functionIdentifier); + + //Build header and cpp file path + std::string headerPath = getHeaderFilePath(file_path); + std::string sourcePath = file_path; + + //create c source file + FILE* fout = fopen(sourcePath.c_str(), "w"); + if ( !fout ) + { + fclose(input); + return false; + } + + //determine file properties + uint32_t fileSize = ra::filesystem::GetFileSize(input); + std::string filename = ra::filesystem::GetFilename(mContext.inputFilePath.c_str()); + //long lastSegmentSize = fileSize%chunk_size; + //size_t numSegments = fileSize/chunk_size + (lastSegmentSize == 0 ? 0 : 1); + + //Build class name + std::string className = getClassName(); + + //Build function + std::string getterFunctionName = getGetterFunctionName(); + + //Build FileManager class template + std::string manager = mContext.managerHeaderFilename; + + //write c file heading + fprintf(fout, "%s", getHeaderTemplate().c_str()); + fprintf(fout, "#if defined(_WIN32) && !defined(_CRT_SECURE_NO_WARNINGS)\n"); + fprintf(fout, "#define _CRT_SECURE_NO_WARNINGS\n"); + fprintf(fout, "#endif\n"); + fprintf(fout, "#include \"%s\"\n", mContext.headerFilename.c_str()); + fprintf(fout, "#include // for malloc\n"); + fprintf(fout, "#include // for memset\n"); + fprintf(fout, "#include // for fopen\n"); + + fprintf(fout, "static %s %s_file = { 0 };\n", mContext.baseClass.c_str(), functionIdentifier.c_str()); + fprintf(fout, "static bool %s_initialized = false;\n", functionIdentifier.c_str()); + fprintf(fout, "\n"); + + // File registration predeclaration code + fprintf(fout, "%s", getCFileManagerRegistrationPredeclarationTemplate().c_str()); + + fprintf(fout, "bool %s_load()\n", functionIdentifier.c_str()); + fprintf(fout, "{\n"); + fprintf(fout, " if ( %s_file.buffer )\n", functionIdentifier.c_str()); + fprintf(fout, " return true;\n"); + fprintf(fout, "\n"); + fprintf(fout, " unsigned char* local_buffer = (unsigned char*)malloc(%s_file.size);\n", functionIdentifier.c_str()); + fprintf(fout, " if ( local_buffer == NULL )\n"); + fprintf(fout, " return false;\n"); + fprintf(fout, "\n"); + fprintf(fout, " unsigned char* next = local_buffer;\n"); + + //create buffer for each chunks from input buffer + unsigned char* buffer = new unsigned char[getContext().chunkSize]; + while ( !feof(input) ) + { + //read a chunk of the file + size_t readSize = fread(buffer, 1, getContext().chunkSize, input); + + //bool isLastChunk = !(readSize == chunk_size); + + if ( readSize == 0 ) + continue; //nothing to output if nothing was read + + //convert to cpp string + std::string cppEncoder; + switch ( getContext().cppEncoder ) + { + case CPP_ENCODER_HEX: + cppEncoder = ra::code::cpp::ToHexString(buffer, readSize); + break; + case CPP_ENCODER_OCT: + default: + cppEncoder = ra::code::cpp::ToOctString(buffer, readSize, false); + break; + }; + + //output + fprintf(fout, " memcpy(next, \"%s\", %s); next += %s; \n", cppEncoder.c_str(), ra::strings::ToString(readSize).c_str(), ra::strings::ToString(readSize).c_str()); + } + delete[] buffer; + buffer = NULL; + + //write c source file footer + fprintf(fout, "\n"); + fprintf(fout, " %s_file.buffer = local_buffer;\n", functionIdentifier.c_str()); + fprintf(fout, " return true;\n"); + fprintf(fout, "}\n"); + + fprintf(fout, "\n"); + + fprintf(fout, "void %s_free()\n", functionIdentifier.c_str()); + fprintf(fout, "{\n"); + fprintf(fout, " if ( %s_file.buffer )\n", functionIdentifier.c_str()); + fprintf(fout, " free((unsigned char*)%s_file.buffer);\n", functionIdentifier.c_str()); + fprintf(fout, " %s_file.buffer = NULL;\n", functionIdentifier.c_str()); + fprintf(fout, "}\n"); + fprintf(fout, "\n"); + fprintf(fout, "bool %s_save(const char* path)\n", functionIdentifier.c_str()); + fprintf(fout, "{\n"); + fprintf(fout, " if ( !%s_file.buffer )\n", functionIdentifier.c_str()); + fprintf(fout, " return false;\n"); + fprintf(fout, " FILE* f = fopen(path, \"wb\");\n"); + fprintf(fout, " if ( !f )\n"); + fprintf(fout, " return false;\n"); + fprintf(fout, " size_t write_size = fwrite(%s_file.buffer, 1, %s_file.size, f);\n", functionIdentifier.c_str(), functionIdentifier.c_str()); + fprintf(fout, " fclose(f);\n"); + fprintf(fout, " if ( write_size != %s_file.size )\n", functionIdentifier.c_str()); + fprintf(fout, " return false;\n"); + fprintf(fout, " return true;\n"); + fprintf(fout, "}\n"); + fprintf(fout, "\n"); + fprintf(fout, "static inline void %s_init()\n", functionIdentifier.c_str()); + fprintf(fout, "{\n"); + fprintf(fout, " // remember we already initialized\n"); + fprintf(fout, " if ( %s_initialized )\n", functionIdentifier.c_str()); + fprintf(fout, " return;\n"); + fprintf(fout, " %s_initialized = true;\n", functionIdentifier.c_str()); + fprintf(fout, "\n"); + fprintf(fout, " // initialize\n"); + fprintf(fout, " %s* file = &%s_file;\n", mContext.baseClass.c_str(), functionIdentifier.c_str()); + fprintf(fout, " file->size = %uULL;\n", fileSize); + fprintf(fout, " file->file_name = \"%s\";\n", getFileClassFileName().c_str()); + fprintf(fout, " file->file_path = \"%s\";\n", getFileClassFilePath().c_str()); + fprintf(fout, " file->buffer = NULL;\n"); + fprintf(fout, " file->load = %s_load;\n", functionIdentifier.c_str()); + fprintf(fout, " file->unload = %s_free;\n", functionIdentifier.c_str()); + fprintf(fout, " file->save = %s_save;\n", functionIdentifier.c_str()); + fprintf(fout, "\n"); + fprintf(fout, " // load file by default on init as in c++ implementation"); + fprintf(fout, " file->load();\n"); + if ( mContext.registerFiles ) + { + fprintf(fout, " \n"); + fprintf(fout, " // register when loaded if static initialisation does not work\n"); + fprintf(fout, " %s_filemanager_register_file(file);\n", mContext.codeNamespace.c_str()); + } + fprintf(fout, "}\n"); + fprintf(fout, "\n"); + fprintf(fout, "%s* %s(void)\n", mContext.baseClass.c_str(), getGetterFunctionName().c_str()); + fprintf(fout, "{\n"); + fprintf(fout, " %s_init();\n", functionIdentifier.c_str()); + fprintf(fout, " return &%s_file;\n", functionIdentifier.c_str()); + fprintf(fout, "}\n"); + + // File registration implementation code + fprintf(fout, "%s", getCFileManagerRegistrationImplementationTemplate().c_str()); + + fclose(input); + fclose(fout); + + return true; + } + }; //bin2cpp \ No newline at end of file diff --git a/src/bin2cpp/SegmentGenerator.h b/src/bin2cpp/SegmentGenerator.h index 92757db..767eeaa 100644 --- a/src/bin2cpp/SegmentGenerator.h +++ b/src/bin2cpp/SegmentGenerator.h @@ -41,6 +41,7 @@ namespace bin2cpp virtual ~SegmentGenerator(); virtual const char * getName() const; virtual bool createCppSourceFile(const char * cpp_file_path); + virtual bool createCSourceFile(const char* file_path); }; }; //bin2cpp diff --git a/src/bin2cpp/StringGenerator.cpp b/src/bin2cpp/StringGenerator.cpp index 77aaa0a..b3fe3af 100755 --- a/src/bin2cpp/StringGenerator.cpp +++ b/src/bin2cpp/StringGenerator.cpp @@ -143,7 +143,7 @@ namespace bin2cpp delete[] buffer; buffer = NULL; - //write cpp file footer + //write cpp source file footer fprintf(cpp, " return buffer;\n"); fprintf(cpp, " }\n"); fprintf(cpp, "%s", getSaveMethodTemplate().c_str()); @@ -151,7 +151,7 @@ namespace bin2cpp fprintf(cpp, " const %s & %s() { static %s _instance; return _instance; }\n", getContext().baseClass.c_str(), getterFunctionName.c_str(), className.c_str()); if (mContext.registerFiles) { - std::string fileManagerTemplate = getFileManagerRegistrationTemplate(); + std::string fileManagerTemplate = getCppFileManagerRegistrationImplementationTemplate(); fprintf(cpp, "%s", fileManagerTemplate.c_str()); } fprintf(cpp, "}; //%s\n", getContext().codeNamespace.c_str()); @@ -162,4 +162,181 @@ namespace bin2cpp return true; } + bool StringGenerator::createCSourceFile(const char* file_path) + { + //check if input file exists + FILE* input = fopen(mContext.inputFilePath.c_str(), "rb"); + if ( !input ) + return false; + + //Uppercase function identifier + std::string functionIdentifier = ra::strings::Lowercase(mContext.functionIdentifier); + + //Build header and cpp file path + std::string headerPath = getHeaderFilePath(file_path); + std::string sourcePath = file_path; + + //create c source file + FILE* fout = fopen(sourcePath.c_str(), "w"); + if ( !fout ) + { + fclose(input); + return false; + } + + //determine file properties + uint32_t fileSize = ra::filesystem::GetFileSize(input); + std::string filename = ra::filesystem::GetFilename(mContext.inputFilePath.c_str()); + //long lastSegmentSize = fileSize%chunk_size; + //size_t numSegments = fileSize/chunk_size + (lastSegmentSize == 0 ? 0 : 1); + + //Build class name + std::string className = getClassName(); + + //Build function + std::string getterFunctionName = getGetterFunctionName(); + + //Build FileManager class template + std::string manager = mContext.managerHeaderFilename; + + //write c file heading + fprintf(fout, "%s", getHeaderTemplate().c_str()); + fprintf(fout, "#if defined(_WIN32) && !defined(_CRT_SECURE_NO_WARNINGS)\n"); + fprintf(fout, "#define _CRT_SECURE_NO_WARNINGS\n"); + fprintf(fout, "#endif\n"); + fprintf(fout, "#include \"%s\"\n", mContext.headerFilename.c_str()); + fprintf(fout, "#include // for malloc\n"); + fprintf(fout, "#include // for memset\n"); + fprintf(fout, "#include // for fopen\n"); + + fprintf(fout, "static %s %s_file = { 0 };\n", mContext.baseClass.c_str(), functionIdentifier.c_str()); + fprintf(fout, "static bool %s_initialized = false;\n", functionIdentifier.c_str()); + fprintf(fout, "\n"); + + // File registration predeclaration code + fprintf(fout, "%s", getCFileManagerRegistrationPredeclarationTemplate().c_str()); + + fprintf(fout, "bool %s_load()\n", functionIdentifier.c_str()); + fprintf(fout, "{\n"); + fprintf(fout, " if ( %s_file.buffer )\n", functionIdentifier.c_str()); + fprintf(fout, " return true;\n"); + fprintf(fout, "\n"); + fprintf(fout, " static const char * static_buffer = ""\n"); + + //create buffer for each chunks from input buffer + int numLinePrinted = 0; + unsigned char* buffer = new unsigned char[getContext().chunkSize]; + while ( !feof(input) ) + { + //read a chunk of the file + size_t readSize = fread(buffer, 1, getContext().chunkSize, input); + + bool isLastChunk = !(readSize == getContext().chunkSize); + + if ( readSize > 0 ) + { + if ( numLinePrinted > 0 ) + { + //end previous line + fprintf(fout, "\n"); + } + + //convert to cpp string + std::string cppEncoder; + switch ( getContext().cppEncoder ) + { + case CPP_ENCODER_HEX: + cppEncoder = ra::code::cpp::ToHexString(buffer, readSize); + break; + case CPP_ENCODER_OCT: + default: + cppEncoder = ra::code::cpp::ToOctString(buffer, readSize, false); + break; + }; + + //output + fprintf(fout, " \"%s\"", cppEncoder.c_str()); + numLinePrinted++; + } + + //end the string if last chunk printed + if ( isLastChunk ) + { + fprintf(fout, ";\n"); + } + } + delete[] buffer; + buffer = NULL; + + //write c source file footer + fprintf(fout, "\n"); + fprintf(fout, " %s_file.buffer = static_buffer;\n", functionIdentifier.c_str()); + fprintf(fout, " return true;\n"); + fprintf(fout, "}\n"); + + fprintf(fout, "\n"); + + fprintf(fout, "void %s_free()\n", functionIdentifier.c_str()); + fprintf(fout, "{\n"); + fprintf(fout, " %s_file.buffer = NULL;\n", functionIdentifier.c_str()); + fprintf(fout, "}\n"); + fprintf(fout, "\n"); + fprintf(fout, "bool %s_save(const char* path)\n", functionIdentifier.c_str()); + fprintf(fout, "{\n"); + fprintf(fout, " if ( !%s_file.buffer )\n", functionIdentifier.c_str()); + fprintf(fout, " return false;\n"); + fprintf(fout, " FILE* f = fopen(path, \"wb\");\n"); + fprintf(fout, " if ( !f )\n"); + fprintf(fout, " return false;\n"); + fprintf(fout, " size_t write_size = fwrite(%s_file.buffer, 1, %s_file.size, f);\n", functionIdentifier.c_str(), functionIdentifier.c_str()); + fprintf(fout, " fclose(f);\n"); + fprintf(fout, " if ( write_size != %s_file.size )\n", functionIdentifier.c_str()); + fprintf(fout, " return false;\n"); + fprintf(fout, " return true;\n"); + fprintf(fout, "}\n"); + fprintf(fout, "\n"); + fprintf(fout, "static inline void %s_init()\n", functionIdentifier.c_str()); + fprintf(fout, "{\n"); + fprintf(fout, " // remember we already initialized\n"); + fprintf(fout, " if ( %s_initialized )\n", functionIdentifier.c_str()); + fprintf(fout, " return;\n"); + fprintf(fout, " %s_initialized = true;\n", functionIdentifier.c_str()); + fprintf(fout, "\n"); + fprintf(fout, " // initialize\n"); + fprintf(fout, " %s* file = &%s_file;\n", mContext.baseClass.c_str(), functionIdentifier.c_str()); + fprintf(fout, " file->size = %uULL;\n", fileSize); + fprintf(fout, " file->file_name = \"%s\";\n", getFileClassFileName().c_str()); + fprintf(fout, " file->file_path = \"%s\";\n", getFileClassFilePath().c_str()); + fprintf(fout, " file->buffer = NULL;\n"); + fprintf(fout, " file->load = %s_load;\n", functionIdentifier.c_str()); + fprintf(fout, " file->unload = %s_free;\n", functionIdentifier.c_str()); + fprintf(fout, " file->save = %s_save;\n", functionIdentifier.c_str()); + fprintf(fout, "\n"); + fprintf(fout, " // load file by default on init as in c++ implementation"); + fprintf(fout, " file->load();\n"); + + if ( mContext.registerFiles ) + { + fprintf(fout, " \n"); + fprintf(fout, " // register\n"); + fprintf(fout, " %s_filemanager_register_file(file);\n", mContext.codeNamespace.c_str()); + } + + fprintf(fout, "}\n"); + fprintf(fout, "\n"); + fprintf(fout, "%s* %s(void)\n", mContext.baseClass.c_str(), getGetterFunctionName().c_str()); + fprintf(fout, "{\n"); + fprintf(fout, " %s_init();\n", functionIdentifier.c_str()); + fprintf(fout, " return &%s_file;\n", functionIdentifier.c_str()); + fprintf(fout, "}\n"); + + // File registration implementation code + fprintf(fout, "%s", getCFileManagerRegistrationImplementationTemplate().c_str()); + + fclose(input); + fclose(fout); + + return true; + } + }; //bin2cpp \ No newline at end of file diff --git a/src/bin2cpp/StringGenerator.h b/src/bin2cpp/StringGenerator.h index 9d84551..f3dda2f 100644 --- a/src/bin2cpp/StringGenerator.h +++ b/src/bin2cpp/StringGenerator.h @@ -40,6 +40,7 @@ namespace bin2cpp virtual ~StringGenerator(); virtual const char * getName() const; virtual bool createCppSourceFile(const char * cpp_file_path); + virtual bool createCSourceFile(const char* file_path); }; }; //bin2cpp diff --git a/src/bin2cpp/Win32ResourceGenerator.cpp b/src/bin2cpp/Win32ResourceGenerator.cpp index 5db6091..574a8f5 100755 --- a/src/bin2cpp/Win32ResourceGenerator.cpp +++ b/src/bin2cpp/Win32ResourceGenerator.cpp @@ -90,6 +90,10 @@ namespace bin2cpp //write cpp file heading fprintf(cpp, "%s", getHeaderTemplate().c_str()); fprintf(cpp, "#include \"%s\"\n", mContext.headerFilename.c_str() ); + fprintf(cpp, "#if defined(_WIN32) && !defined(_CRT_SECURE_NO_WARNINGS)\n"); + fprintf(cpp, "#define _CRT_SECURE_NO_WARNINGS\n"); + fprintf(cpp, "#endif\n"); + fprintf(cpp, "\n"); fprintf(cpp, "#include \n"); fprintf(cpp, "#include \n"); fprintf(cpp, "#include //for ofstream\n"); @@ -134,7 +138,7 @@ namespace bin2cpp fprintf(cpp, " if ( EnumProcessModules( hProcess, &hModule, sizeof(hModule), &cbNeeded) )\n"); fprintf(cpp, " {\n"); fprintf(cpp, " //Retrieve the resource\n"); - fprintf(cpp, " hResourceInfoBlock = FindResource(hModule, \"%s\", \"CUSTOM\");\n", getRandomIdentifier(mContext.inputFilePath.c_str()).c_str()); + fprintf(cpp, " hResourceInfoBlock = FindResourceA(hModule, \"%s\", \"CUSTOM\");\n", getRandomIdentifier(mContext.inputFilePath.c_str()).c_str()); fprintf(cpp, " if (hResourceInfoBlock)\n"); fprintf(cpp, " {\n"); fprintf(cpp, " hResHandle = LoadResource(hModule, hResourceInfoBlock);\n"); @@ -176,7 +180,7 @@ namespace bin2cpp fprintf(cpp, " const %s & %s() { static %s _instance; return _instance; }\n", getContext().baseClass.c_str(), getterFunctionName.c_str(), className.c_str()); if (mContext.registerFiles) { - std::string fileManagerTemplate = getFileManagerRegistrationTemplate(); + std::string fileManagerTemplate = getCppFileManagerRegistrationImplementationTemplate(); fprintf(cpp, "%s", fileManagerTemplate.c_str()); } fprintf(cpp, "}; //%s\n", getContext().codeNamespace.c_str()); @@ -187,11 +191,212 @@ namespace bin2cpp return true; } + bool Win32ResourceGenerator::createCSourceFile(const char* file_path) + { + bool resourceFileSuccess = createResourceFile(file_path); + if ( !resourceFileSuccess ) + return false; + + //check if input file exists + FILE* input = fopen(mContext.inputFilePath.c_str(), "rb"); + if ( !input ) + return false; + + //Uppercase function identifier + std::string functionIdentifier = ra::strings::Lowercase(mContext.functionIdentifier); + + //Build header and cpp file path + std::string headerPath = getHeaderFilePath(file_path); + std::string sourcePath = file_path; + + //create c source file + FILE* fout = fopen(sourcePath.c_str(), "w"); + if ( !fout ) + { + fclose(input); + return false; + } + + //determine file properties + uint32_t fileSize = ra::filesystem::GetFileSize(input); + std::string filename = ra::filesystem::GetFilename(mContext.inputFilePath.c_str()); + //long lastSegmentSize = fileSize%chunk_size; + //size_t numSegments = fileSize/chunk_size + (lastSegmentSize == 0 ? 0 : 1); + + //Build class name + std::string className = getClassName(); + + //Build function + std::string getterFunctionName = getGetterFunctionName(); + + //Build FileManager class template + std::string manager = mContext.managerHeaderFilename; + + //write c file heading + fprintf(fout, "%s", getHeaderTemplate().c_str()); + fprintf(fout, "#if defined(_WIN32) && !defined(_CRT_SECURE_NO_WARNINGS)\n"); + fprintf(fout, "#define _CRT_SECURE_NO_WARNINGS\n"); + fprintf(fout, "#endif\n"); + fprintf(fout, "\n"); + fprintf(fout, "#include \"%s\"\n", mContext.headerFilename.c_str()); + fprintf(fout, "#include // for malloc\n"); + fprintf(fout, "#include // for memset\n"); + fprintf(fout, "#include // for fopen\n"); + fprintf(fout, "\n"); + fprintf(fout, "#ifndef WIN32_LEAN_AND_MEAN\n"); + fprintf(fout, "#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers\n"); + fprintf(fout, "#endif\n"); + fprintf(fout, "#include \n"); + fprintf(fout, "\n"); + fprintf(fout, "#include //for EnumProcessModules()\n"); + fprintf(fout, "#pragma comment( lib, \"psapi.lib\" )\n"); + fprintf(fout, "\n"); + fprintf(fout, "static %s %s_file = { 0 };\n", mContext.baseClass.c_str(), functionIdentifier.c_str()); + fprintf(fout, "static bool %s_initialized = false;\n", functionIdentifier.c_str()); + fprintf(fout, "typedef struct %s\n", getLocalInfoStructName().c_str()); + fprintf(fout, "{\n"); + fprintf(fout, " HANDLE hProcess;\n"); + fprintf(fout, " HMODULE hModule;\n"); + fprintf(fout, " HRSRC hResourceInfoBlock;\n"); + fprintf(fout, " HGLOBAL hResHandle;\n"); + fprintf(fout, " DWORD dwBufferSize;\n"); + fprintf(fout, "} %s;\n", getLocalInfoStructName().c_str()); + fprintf(fout, "static %s %s_info = { 0 };\n", getLocalInfoStructName().c_str(), functionIdentifier.c_str()); + fprintf(fout, "\n"); + + // File registration predeclaration code + fprintf(fout, "%s", getCFileManagerRegistrationPredeclarationTemplate().c_str()); + + fprintf(fout, "bool %s_load()\n", functionIdentifier.c_str()); + fprintf(fout, "{\n"); + fprintf(fout, " if ( %s_file.buffer )\n", functionIdentifier.c_str()); + fprintf(fout, " return true;\n"); + fprintf(fout, "\n"); + fprintf(fout, " %s* info = &%s_info;\n", getLocalInfoStructName().c_str(), functionIdentifier.c_str()); + + fprintf(fout, " //Get a handle to this process\n"); + fprintf(fout, " info->hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, GetCurrentProcessId() );\n"); + fprintf(fout, " if (info->hProcess)\n"); + fprintf(fout, " {\n"); + fprintf(fout, " //Find the main HMODULE of the process\n"); + fprintf(fout, " DWORD cbNeeded;\n"); + fprintf(fout, " if ( EnumProcessModules( info->hProcess, &info->hModule, sizeof(info->hModule), &cbNeeded) )\n"); + fprintf(fout, " {\n"); + fprintf(fout, " //Retrieve the resource\n"); + fprintf(fout, " info->hResourceInfoBlock = FindResourceA(info->hModule, \"%s\", \"CUSTOM\");\n", getRandomIdentifier(mContext.inputFilePath.c_str()).c_str()); + fprintf(fout, " if (info->hResourceInfoBlock)\n"); + fprintf(fout, " {\n"); + fprintf(fout, " info->hResHandle = LoadResource(info->hModule, info->hResourceInfoBlock);\n"); + fprintf(fout, " if (info->hResHandle)\n"); + fprintf(fout, " {\n"); + fprintf(fout, " %s_file.buffer = (const unsigned char *)LockResource(info->hResHandle);\n", functionIdentifier.c_str()); + fprintf(fout, " info->dwBufferSize = SizeofResource(info->hModule, info->hResourceInfoBlock);\n"); + fprintf(fout, " return true;\n"); + fprintf(fout, " }\n"); + fprintf(fout, " }\n"); + fprintf(fout, " }\n"); + fprintf(fout, " }\n"); + fprintf(fout, " \n"); + fprintf(fout, " return false;\n"); + fprintf(fout, "}\n"); + fprintf(fout, "\n"); + + fprintf(fout, "void %s_free()\n", functionIdentifier.c_str()); + fprintf(fout, "{\n"); + fprintf(fout, " if ( %s_file.buffer == NULL)\n", functionIdentifier.c_str()); + fprintf(fout, " return;\n"); + fprintf(fout, " %s* info = &%s_info;\n", getLocalInfoStructName().c_str(), functionIdentifier.c_str()); + fprintf(fout, " if (info->hResHandle)\n"); + fprintf(fout, " {\n"); + fprintf(fout, " FreeResource(info->hResHandle);\n"); + fprintf(fout, " info->hResHandle = NULL;\n"); + fprintf(fout, " %s_file.buffer = NULL;\n", functionIdentifier.c_str()); + fprintf(fout, " info->dwBufferSize = 0;\n"); + fprintf(fout, " }\n"); + fprintf(fout, " info->hResourceInfoBlock = NULL;\n"); + fprintf(fout, " info->hModule = NULL;\n"); + fprintf(fout, " if (info->hProcess)\n"); + fprintf(fout, " {\n"); + fprintf(fout, " CloseHandle(info->hProcess);\n"); + fprintf(fout, " info->hProcess = NULL;\n"); + fprintf(fout, " }\n"); + fprintf(fout, "}\n"); + fprintf(fout, "\n"); + + fprintf(fout, "bool %s_save(const char* path)\n", functionIdentifier.c_str()); + fprintf(fout, "{\n"); + fprintf(fout, " if ( !%s_file.buffer )\n", functionIdentifier.c_str()); + fprintf(fout, " return false;\n"); + fprintf(fout, " FILE* f = fopen(path, \"wb\");\n"); + fprintf(fout, " if ( !f )\n"); + fprintf(fout, " return false;\n"); + fprintf(fout, " size_t write_size = fwrite(%s_file.buffer, 1, %s_file.size, f);\n", functionIdentifier.c_str(), functionIdentifier.c_str()); + fprintf(fout, " fclose(f);\n"); + fprintf(fout, " if ( write_size != %s_file.size )\n", functionIdentifier.c_str()); + fprintf(fout, " return false;\n"); + fprintf(fout, " return true;\n"); + fprintf(fout, "}\n"); + fprintf(fout, "\n"); + + fprintf(fout, "static inline void %s_init()\n", functionIdentifier.c_str()); + fprintf(fout, "{\n"); + fprintf(fout, " // remember we already initialized\n"); + fprintf(fout, " if ( %s_initialized )\n", functionIdentifier.c_str()); + fprintf(fout, " return;\n"); + fprintf(fout, " %s_initialized = true;\n", functionIdentifier.c_str()); + fprintf(fout, "\n"); + fprintf(fout, " // initialize\n"); + fprintf(fout, " %s* file = &%s_file;\n", mContext.baseClass.c_str(), functionIdentifier.c_str()); + fprintf(fout, " file->size = %uULL;\n", fileSize); + fprintf(fout, " file->file_name = \"%s\";\n", getFileClassFileName().c_str()); + fprintf(fout, " file->file_path = \"%s\";\n", getFileClassFilePath().c_str()); + fprintf(fout, " file->buffer = NULL;\n"); + fprintf(fout, " file->load = %s_load;\n", functionIdentifier.c_str()); + fprintf(fout, " file->unload = %s_free;\n", functionIdentifier.c_str()); + fprintf(fout, " file->save = %s_save;\n", functionIdentifier.c_str()); + fprintf(fout, "\n"); + fprintf(fout, " // load file by default on init as in c++ implementation"); + fprintf(fout, " file->load();\n"); + + if ( mContext.registerFiles ) + { + fprintf(fout, " \n"); + fprintf(fout, " // register\n"); + fprintf(fout, " %s_filemanager_register_file(file);\n", mContext.codeNamespace.c_str()); + } + + fprintf(fout, "}\n"); + fprintf(fout, "\n"); + + fprintf(fout, "%s* %s(void)\n", mContext.baseClass.c_str(), getGetterFunctionName().c_str()); + fprintf(fout, "{\n"); + fprintf(fout, " %s_init();\n", functionIdentifier.c_str()); + fprintf(fout, " return &%s_file;\n", functionIdentifier.c_str()); + fprintf(fout, "}\n"); + + // File registration implementation code + fprintf(fout, "%s", getCFileManagerRegistrationImplementationTemplate().c_str()); + + fclose(input); + fclose(fout); + + return true; + } + std::string Win32ResourceGenerator::getResourceFilePath(const char * cpp_file_path) { //Build header file path std::string resourcePath = cpp_file_path; - ra::strings::Replace(resourcePath, ".cpp", ".rc"); + switch ( mContext.code ) + { + default: + case CODE_GENERATION_CPP: + ra::strings::Replace(resourcePath, ".cpp", ".rc"); + break; + case CODE_GENERATION_C: + ra::strings::Replace(resourcePath, ".c", ".rc"); + break; + }; return resourcePath; } @@ -247,4 +452,11 @@ namespace bin2cpp return false; // not supported } + std::string Win32ResourceGenerator::getLocalInfoStructName() + { + std::string name = ra::strings::Lowercase(mContext.functionIdentifier); + name = ra::strings::Uppercase(name) + "_INFO"; + return name; + } + }; //bin2cpp \ No newline at end of file diff --git a/src/bin2cpp/Win32ResourceGenerator.h b/src/bin2cpp/Win32ResourceGenerator.h index 68b4a0d..a5e29ce 100644 --- a/src/bin2cpp/Win32ResourceGenerator.h +++ b/src/bin2cpp/Win32ResourceGenerator.h @@ -40,11 +40,13 @@ namespace bin2cpp virtual ~Win32ResourceGenerator(); virtual const char * getName() const; virtual bool createCppSourceFile(const char * cpp_file_path); + virtual bool createCSourceFile(const char* file_path); virtual bool printFileContent(); protected: virtual std::string getResourceFilePath(const char * cpp_file_path); virtual bool createResourceFile(const char * cpp_file_path); virtual std::string getRandomIdentifier(const char * cpp_file_path); + virtual std::string getLocalInfoStructName(); }; }; //bin2cpp diff --git a/src/bin2cpp/bin2cpp.samples.txt b/src/bin2cpp/bin2cpp.samples.txt index 88c7520..1f9d63f 100644 --- a/src/bin2cpp/bin2cpp.samples.txt +++ b/src/bin2cpp/bin2cpp.samples.txt @@ -1,8 +1,44 @@ +# C code examples: + +Test all generators +--file=..\..\test\bin2cpp_unittest\generated_files\testHtml100000\testHtml100000.bin --output=..\..\test\bin2cpp_unittest\generated_files\testHtml100000 --headerfile=_testHtml100000_C.h --override --code=c --generator=array +--file=..\..\test\bin2cpp_unittest\generated_files\testHtml100000\testHtml100000.bin --output=..\..\test\bin2cpp_unittest\generated_files\testHtml100000 --headerfile=_testHtml100000_C.h --override --code=c --generator=segment +--file=..\..\test\bin2cpp_unittest\generated_files\testHtml100000\testHtml100000.bin --output=..\..\test\bin2cpp_unittest\generated_files\testHtml100000 --headerfile=_testHtml100000_C.h --override --code=c --generator=string +--file=..\..\test\bin2cpp_unittest\generated_files\testHtml100000\testHtml100000.bin --output=..\..\test\bin2cpp_unittest\generated_files\testHtml100000 --headerfile=_testHtml100000_C.h --override --code=c --generator=win32 + +Test for: + * Segment C generator + * filemanager + * registerfile +--file=..\..\test\bin2cpp_unittest\generated_files\testFileManager_C\testFileManager_C.1.bin --output=..\..\test\bin2cpp_unittest\generated_files\testFileManager_C --headerfile=_testFileManager_C.1.h --identifier=testFileManager1_c --managerfile=filemanager.h --override --code=c +--file=..\..\test\bin2cpp_unittest\generated_files\testFileManager_C\testFileManager_C.2.bin --output=..\..\test\bin2cpp_unittest\generated_files\testFileManager_C --headerfile=_testFileManager_C.2.h --identifier=testFileManager2_c --registerfile --override --code=c + +Test directories: +--dir=..\..\..\samples\demo_website\www --output=..\..\temp --code=c +--dir=..\..\..\samples\demo_website\www --output=..\..\temp --code=c --managerfile=www-file-manager.h + +Test for namespace: +--file=..\..\test\bin2cpp_unittest\generated_files\testNamespace_C\testNamespace_C.bin --output=..\..\test\bin2cpp_unittest\generated_files\testNamespace_C --headerfile=testNamespace_C.h --identifier=testNamespace_C --managerfile=filemanager.h --override --code=c --namespace=foobar + +Test for baseclass: +--file=..\..\test\bin2cpp_unittest\generated_files\testBaseClass_C\testBaseClass_C.bin --output=..\..\test\bin2cpp_unittest\generated_files\testBaseClass_C --headerfile=testBaseClass_C.h --identifier=testBaseClass_C --managerfile=filemanager.h --override --code=c --baseclass=Resource + + +# CPP code examples: +--dir=..\..\..\samples\demo_website\www --output=..\..\temp --managerfile=PagesFileManager.h --namespace=myblog --keepdirs +--dir=..\..\..\samples\demo_website\www --output=..\..\temp --dirincludefilter="*\static\*.css:*.jpg" +--dir=..\..\..\samples\demo_website\www --output=..\..\temp --dirincludefilter="*\static\*.css:*.jpg" --direxcludefilter="*\light-mode.css" +--dir=..\..\..\samples\demo_website\www --output=..\..\temp --direxcludefilter="*.html" + +--file=..\..\test\bin2cpp_unittest\generated_files\testText1000\testText1000.bin --output=..\..\temp --override +--noheader --dir=..\..\test\bin2cpp_unittest\generated_files\testIssue56a\input_files --output=..\..\test\bin2cpp_unittest\generated_files\testIssue56a\compiled_sources --chunksize=200 --managerfile=FileManager56a.h --registerfile --namespace=issue56a --override +--plainoutput --chunksize=9999 --file=..\..\test\bin2cpp_unittest\generated_files\testSequential1000\testSequential1000.bin + + + +# old code examples: --file=.\generated_files\testText1000\testText1000.bin --output=.\generated_files\testText1000 --headerfile=_testText1000.h --identifier=testText1000 --chunksize=450 --override --file=.\generated_files\testSequential1000\testSequential1000.bin --output=.\generated_files\testSequential1000 --headerfile=_testSequential1000.h --identifier=testSequential1000 --chunksize=180 --override ---file=C:\Temp\foobar.bin --output=C:\Temp --headerfile=_testFoo.h --identifier=testFoo --override ---file=C:\Temp\foobar.bin --output=C:\Temp --headerfile=_testFoo.h --identifier=testFoo --override --noheader ---dir=D:\Temp\foobar --output=C:\Temp\foobar --override –noheader --file=..\..\test\bin2cpp_unittest\generated_files\testIssue28\testIssue28.bin --output=..\..\test\bin2cpp_unittest\generated_files\testIssue28 --headerfile=_testIssue28.h --identifier=testIssue28 --managerfile=FileManager.h --override --file=..\..\test\bin2cpp_unittest\generated_files\testIssue28\testIssue28.bin --output=..\..\test\bin2cpp_unittest\generated_files\testIssue28 --headerfile=_testIssue28.h --identifier=testIssue28 --managerfile=FileManager.h --override --file=..\..\test\bin2cpp_unittest\generated_files\testSequential1000\testSequential1000.bin --headerfile="generated/foo-data.h" --output=. --identifier=TestLongPath @@ -17,25 +53,8 @@ --dir=..\..\test\bin2cpp_unittest\generated_files\testIssue56b\input_files\www --output=..\..\test\bin2cpp_unittest\generated_files\testIssue56b\generated_sources --namespace=issue56b --override --dir=..\..\test\bin2cpp_unittest\generated_files\testKeepDirectories\input_files\www --output=..\..\test\bin2cpp_unittest\generated_files\testKeepDirectories\generated_sources --override --keepdirs ---dir=..\..\..\samples\demo_website\www --output=..\..\temp --managerfile=PagesFileManager.h --namespace=myblog --keepdirs ---dir=..\..\..\samples\demo_website\www --output=..\..\temp --dirincludefilter="*\static\*.css:*.jpg" ---dir=..\..\..\samples\demo_website\www --output=..\..\temp --dirincludefilter="*\static\*.css:*.jpg" --direxcludefilter="*\light-mode.css" ---dir=..\..\..\samples\demo_website\www --output=..\..\temp --direxcludefilter="*.html" - -Bug: ---dir=..\..\..\samples\demo_website\www --output=..\..\temp --dirincludefilter="*.old" ---file=..\..\..\samples\demo_website\www\index.html.old --output=..\..\temp - - --file=D:\Temp\bin2cpp\issue51\input_files\IMG_0001.jpg --output=D:\Temp\bin2cpp\issue51\generated_sources --headerfile="IMG_0001.h" --identifier=testIssue51 --namespace=testIssue51 --managerfile=FileManager51.h --override --reportedfilepath=foo\bar\IMG_0001.h --dir=D:\Temp\bin2cpp\testIssue56b\input_files\www --output=D:\Temp\bin2cpp\testIssue56b\generated_sources --namespace=testIssue51 --managerfile=FileManager51.h --override --file=..\..\test\bin2cpp_unittest\generated_files\testSequential1000\testSequential1000.bin --plainoutput --chunksize=50 --managerfile=MyManager.h --output=generated_files --override - ---file=..\..\test\bin2cpp_unittest\generated_files\testText1000\testText1000.bin --output=. --override - ---noheader --dir=..\..\test\bin2cpp_unittest\generated_files\testIssue56a\input_files --output=..\..\test\bin2cpp_unittest\generated_files\testIssue56a\compiled_sources --chunksize=200 --managerfile=FileManager56a.h --registerfile --namespace=issue56a --override - - ---plainoutput --chunksize=9999 --file=..\..\test\bin2cpp_unittest\generated_files\testSequential1000\testSequential1000.bin diff --git a/src/bin2cpp/common.cpp b/src/bin2cpp/common.cpp index a94f6fd..796a38e 100755 --- a/src/bin2cpp/common.cpp +++ b/src/bin2cpp/common.cpp @@ -94,6 +94,22 @@ namespace bin2cpp return false; } + bool isCHeaderFile(const std::string & path) + { + std::string extension = ra::strings::Uppercase(ra::filesystem::GetFileExtention(path)); + if (extension == "H") + return true; + return false; + } + + bool isCSourceFile(const std::string & path) + { + std::string extension = ra::strings::Uppercase(ra::filesystem::GetFileExtention(path)); + if (extension == "C") + return true; + return false; + } + std::string getCppIncludeGuardMacroName(const std::string & path) { static const std::string EMPTY_STRING; @@ -364,4 +380,32 @@ namespace bin2cpp return output; } + CodeGenerationEnum parseCode(const std::string& value) + { + std::string value_upper = ra::strings::Uppercase(value); + if ( value_upper == "C" ) + return CodeGenerationEnum::CODE_GENERATION_C; + if ( value_upper == "CPP" || value_upper == "C++" ) + return CodeGenerationEnum::CODE_GENERATION_CPP; + return CodeGenerationEnum::CODE_GENERATION_UNKNOW; + } + + const std::string& getDefaultCodeSourceFileExtension(CodeGenerationEnum code) + { + static const std::string EMPTY = ""; + static const std::string CPP = "cpp"; + static const std::string C = "c"; + switch ( code ) + { + case CODE_GENERATION_UNKNOW: + return EMPTY; + case CODE_GENERATION_CPP: + return CPP; + case CODE_GENERATION_C: + return C; + default: + return EMPTY; + }; + } + }; //bin2cpp diff --git a/src/bin2cpp/common.h b/src/bin2cpp/common.h index cec0925..48aca46 100644 --- a/src/bin2cpp/common.h +++ b/src/bin2cpp/common.h @@ -31,9 +31,11 @@ #include #include +#include "types.h" +#include "enums.h" + namespace bin2cpp { - typedef std::set Dictionary; /// ///Returns the application version number. @@ -62,6 +64,20 @@ namespace bin2cpp ///Returns true if path is a c++ source file. Returns false otherwise. bool isCppSourceFile(const std::string & path); + /// + ///Determine if a given path is a C header file. + /// + ///An valid file path. + ///Returns true if path is a C header file. Returns false otherwise. + bool isCHeaderFile(const std::string & path); + + /// + ///Determine if a given path is a C source file. + /// + ///An valid file path. + ///Returns true if path is a C source file. Returns false otherwise. + bool isCSourceFile(const std::string & path); + /// ///Determine the appropriate macro name for the include guard of the given c++ header file. ///See also https://en.wikipedia.org/wiki/Include_guard @@ -141,6 +157,20 @@ namespace bin2cpp ///Returns the combined string. std::string strJoin(const std::vector& values, char separator); + /// + ///Parse a CodeGenerationEnum from a string. + /// + ///The string value representing of the code language. + ///Returns a valid CodeGenerationEnum value. Returns CODE_GENERATION_UNKNOW on parsing error. + CodeGenerationEnum parseCode(const std::string& value); + + /// + ///Get the default source file extension for the given CodeGenerationEnum. + /// + ///The code generation language. + ///Returns a valid file extension valu that matches the given code. Returns an empty string otherwise. + const std::string & getDefaultCodeSourceFileExtension(CodeGenerationEnum code); + }; //bin2cpp #endif //BIN2CPP_COMMON_H diff --git a/src/bin2cpp/enums.h b/src/bin2cpp/enums.h index a5398aa..82a7539 100644 --- a/src/bin2cpp/enums.h +++ b/src/bin2cpp/enums.h @@ -24,23 +24,29 @@ #ifndef ENUMS_H #define ENUMS_H - -#include -#include "Context.h" namespace bin2cpp { /// - ///Defines the different type of cpp encoding. - ///See setCppEncoder() and getCppEncoder() functions. + ///Defines the different types of cpp encoding. /// enum CppEncoderEnum { CPP_ENCODER_OCT, CPP_ENCODER_HEX, }; + + /// + ///Defines the different types of programming language code output. + /// + enum CodeGenerationEnum + { + CODE_GENERATION_UNKNOW, + CODE_GENERATION_CPP, + CODE_GENERATION_C, + }; }; //bin2cpp -#endif //IGENERATOR_H +#endif //ENUMS_H diff --git a/src/bin2cpp/main.cpp b/src/bin2cpp/main.cpp index 4678988..d5763f0 100755 --- a/src/bin2cpp/main.cpp +++ b/src/bin2cpp/main.cpp @@ -31,6 +31,8 @@ #include "Win32ResourceGenerator.h" #include "ManagerGenerator.h" #include "Context.h" +#include "INameProvider.h" +#include "LegacyNameProvider.h" #include #include @@ -57,7 +59,8 @@ enum APP_ERROR_CODES APP_ERROR_TOOMANYARGUMENTS, APP_ERROR_INPUTDIRNOTFOUND, AAP_ERROR_NOTSUPPORTED, - APP_ERROR_OPERATIONHASFAILED + APP_ERROR_OPERATIONHASFAILED, + APP_ERROR_INVALIDVALUE, }; enum FILE_UPDATE_MODE @@ -70,9 +73,12 @@ enum FILE_UPDATE_MODE //default values static const size_t DEFAULT_CHUNK_SIZE = 200; -static const char * DEFAULT_NAMESPACE = "bin2cpp"; -static const char * DEFAULT_BASECLASSNAME = "File"; +static const char * DEFAULT_NAMESPACE_CPP = "bin2cpp"; +static const char * DEFAULT_NAMESPACE_C = "bin2c"; +static const char * DEFAULT_BASECLASS_NAME_CPP = "File"; +static const char * DEFAULT_BASECLASS_NAME_C = "Bin2cFile"; static const CppEncoderEnum DEFAULT_ENCODING = CPP_ENCODER_OCT; +static const CodeGenerationEnum DEFAULT_CODE_GENERATION = CODE_GENERATION_CPP; static Dictionary identifiers_dictionary; // unique values for identifiers static Dictionary output_files_dictionary; // unique values for output file names #define DIRECTORY_FILTER_SEPARATOR_STR ":" @@ -106,6 +112,9 @@ const char * getErrorCodeDescription(const APP_ERROR_CODES & error_code) case APP_ERROR_OPERATIONHASFAILED: return "Operation has failed"; break; + case APP_ERROR_INVALIDVALUE: + return "Invalid value"; + break; default: return "Unknown error"; }; @@ -138,13 +147,11 @@ struct ARGUMENTS }; //pre-declarations -bool generateFile(const Context & c, const std::string & output_file_path, bin2cpp::IGenerator * generator); +bool generateOutputFile(const Context & c, const std::string & output_file_path, bin2cpp::IGenerator * generator); APP_ERROR_CODES processInputFile(const Context & c, bin2cpp::IGenerator * generator); -APP_ERROR_CODES processInputDirectory(const Context & c, bin2cpp::IGenerator * generator); +APP_ERROR_CODES processInputDirectory(const Context & c, bin2cpp::INameProvider* nameProvider, bin2cpp::IGenerator * generator); APP_ERROR_CODES processManagerFiles(const Context & c); APP_ERROR_CODES processPlainOutput(const Context & c, bin2cpp::IGenerator * generator); -std::string getDefaultFunctionIdentifier(const Context & c, Dictionary & identifiers_dictionary); -std::string getDefaultHeaderFile(const Context & c); void printHeader() { @@ -184,7 +191,9 @@ void printUsage() " --encoding= Name of the binary to string literal encoding to use. Possible values are 'oct' and 'hex'. [default: oct]\n" " --chunksize= Size in bytes of each string segments (bytes per LoC). [default: 200]\n" " --baseclass= The name of the interface for embedded files. [default: File]\n" + " For C generated code, this parameter is for naming the File structure. [default: Bin2cFile]\n" " --namespace= The namespace of the generated source code. [default: bin2cpp]\n" + " For C generated code, this parameter is for setting the prefix of all function names. [default: bin2c]\n" " --reportedfilepath= The relative reported path of the File. Path returned when calling method getFilePath() of the File class. ie: images" SEPARATOR "DCIM" SEPARATOR "IMG_0001.jpg.\n" " Automatically calculated when --dir mode is used.\n" " --managerfile= File name or relative path of the generated C++ header file for the FileManager class. ie: FileManager.h\n" @@ -198,6 +207,7 @@ void printUsage() " --keepdirs Keep the directory structure. Forces the output files to have the same\n" " directory structure as the input files. Valid only when --dir is used.\n" " --plainoutput Print the encoded string in plain format to stdout. Useful for scripts and integration with third party application.\n" + " --code Define the programming language output for code generation. Supported values are 'c', 'cpp', 'c++'.\n" " --override Tells bin2cpp to overwrite the destination files.\n" " --noheader Do not print program header to standard output.\n" " --quiet Do not log any message to standard output.\n" @@ -213,11 +223,10 @@ void printUsage() " [a-zA-Z0-9] Matches any single letter (uppercase or lowercase) or digit.\n" "\n" " For example:\n" - " 'ker*##.???' would match files that starts with 'ker', and ends with 2 digits, a dot and then 3 characters." + " 'ker*##.\?\?\?' would match files that starts with 'ker', and ends with 2 digits, a dot and then 3 characters.\n" " --dir-include-filter=\"*.jpg:*.png\" would include all files whose file extension is 'jpg' or 'png'.\n" " --dir-exclude-filter=\"*.bak\" would exclude all backup files.\n" - " --dir-include-filter=\"*.log\" --dir-exclude-filter=\"debug.log\" would include all log files but not the one named 'debug.log'." - "\n"; + " --dir-include-filter=\"*.log\" --dir-exclude-filter=\"debug.log\" would include all log files but not the one named 'debug.log'.\n"; printf("%s", usage); } @@ -230,7 +239,8 @@ int main(int argc, char* argv[]) args.version = false; Context c; - + LegacyNameProvider legacyNameProvider; + INameProvider& nameProvider = legacyNameProvider; std::string dummy; //help @@ -338,20 +348,39 @@ int main(int argc, char* argv[]) //optional arguments + std::string codeStr; + if ( ra::cli::ParseArgument("code", codeStr, argc, argv) ) + { + CodeGenerationEnum codeTmp = parseCode(codeStr); + if ( codeTmp == CodeGenerationEnum::CODE_GENERATION_UNKNOW ) + { + APP_ERROR_CODES error = APP_ERROR_INVALIDVALUE; + ra::logging::Log(ra::logging::LOG_ERROR, "%s (code)", getErrorCodeDescription(error)); + printUsage(); + return error; + } + + c.code = codeTmp; + } + else + { + c.code = DEFAULT_CODE_GENERATION; + } + if (c.hasInputFile) { //identifier if (!ra::cli::ParseArgument("identifier", c.functionIdentifier, argc, argv)) { //identifier is not manually specified. - c.functionIdentifier = getDefaultFunctionIdentifier(c, identifiers_dictionary); + c.functionIdentifier = nameProvider.getDefaultFunctionIdentifier(c.inputFilePath, identifiers_dictionary); } //headerfile if (!ra::cli::ParseArgument("headerfile", c.headerFilename, argc, argv)) { //use the file name without extension as 'headerfile'. - c.headerFilename = getDefaultHeaderFile(c); + c.headerFilename = nameProvider.getDefaultHeaderFile(c.inputFilePath); } } @@ -366,12 +395,31 @@ int main(int argc, char* argv[]) if (!ra::cli::ParseArgument("namespace", c.codeNamespace, argc, argv)) { - c.codeNamespace = DEFAULT_NAMESPACE; + switch ( c.code ) + { + default: + case CODE_GENERATION_CPP: + c.codeNamespace = DEFAULT_NAMESPACE_CPP; + break; + case CODE_GENERATION_C: + c.codeNamespace = DEFAULT_NAMESPACE_C; + break; + }; + } if (!ra::cli::ParseArgument("baseclass", c.baseClass, argc, argv)) { - c.baseClass = DEFAULT_BASECLASSNAME; + switch ( c.code ) + { + default: + case CODE_GENERATION_CPP: + c.baseClass = DEFAULT_BASECLASS_NAME_CPP; + break; + case CODE_GENERATION_C: + c.baseClass = DEFAULT_BASECLASS_NAME_C; + break; + }; } c.registerFiles = ra::cli::ParseArgument("registerfile", dummy, argc, argv); @@ -417,7 +465,7 @@ int main(int argc, char* argv[]) c.cppEncoder = CPP_ENCODER_HEX; else { - APP_ERROR_CODES error = APP_ERROR_MISSINGARGUMENTS; + APP_ERROR_CODES error = APP_ERROR_INVALIDVALUE; ra::logging::Log(ra::logging::LOG_ERROR, "%s (encoding)", getErrorCodeDescription(error)); printUsage(); return error; @@ -499,7 +547,7 @@ int main(int argc, char* argv[]) } else if (c.hasInputDir) { - APP_ERROR_CODES error = processInputDirectory(c, generator); + APP_ERROR_CODES error = processInputDirectory(c, &nameProvider, generator); if (error != APP_ERROR_SUCCESS) { ra::logging::Log(ra::logging::LOG_ERROR, "%s.", getErrorCodeDescription(error)); @@ -521,28 +569,6 @@ int main(int argc, char* argv[]) return APP_ERROR_SUCCESS; } -std::string getDefaultFunctionIdentifier(const Context & c, Dictionary & identifiers_dictionary) -{ - std::string output; - - //use the file name without extension as 'identifier'. - output = getUniqueFunctionIdentifierFromPath(c.inputFilePath.c_str(), identifiers_dictionary); - output = ra::strings::CapitalizeFirstCharacter(output); - - return output; -} - -std::string getDefaultHeaderFile(const Context & c) -{ - std::string output; - - //use the file name without extension as 'headerfile'. - output = ra::filesystem::GetFilenameWithoutExtension(c.inputFilePath.c_str()); - output += ".h"; - - return output; -} - APP_ERROR_CODES processInputFile(const Context & c, bin2cpp::IGenerator * generator) { // printing info @@ -564,8 +590,9 @@ APP_ERROR_CODES processInputFile(const Context & c, bin2cpp::IGenerator * genera return APP_ERROR_INPUTFILENOTFOUND; //prepare output files path - std::string headerExtention = ra::filesystem::GetFileExtention(c.headerFilename); - std::string cppFilename = c.headerFilename.substr(0, c.headerFilename.size() - headerExtention.size()) + "cpp"; // strip out header file's extension and add 'cpp'. + const std::string headerExtention = ra::filesystem::GetFileExtention(c.headerFilename); + const std::string & sourceExtension = getDefaultCodeSourceFileExtension(c.code); + std::string sourceFilename = c.headerFilename.substr(0, c.headerFilename.size() - headerExtention.size()) + sourceExtension; // strip out header file's extension and add 'cpp'. //create a copy of the context. //we may have already generated files from a previous call to processInputFile(). @@ -574,11 +601,11 @@ APP_ERROR_CODES processInputFile(const Context & c, bin2cpp::IGenerator * genera //build unique output relative file paths cCopy.headerFilename = bin2cpp::getUniqueFilePath(cCopy.headerFilename, output_files_dictionary); - cppFilename = bin2cpp::getUniqueFilePath(cppFilename, output_files_dictionary); + sourceFilename = bin2cpp::getUniqueFilePath(sourceFilename, output_files_dictionary); //build full absolute paths std::string outputHeaderPath = cCopy.outputDirPath + ra::filesystem::GetPathSeparatorStr() + cCopy.headerFilename; - std::string outputCppPath = cCopy.outputDirPath + ra::filesystem::GetPathSeparatorStr() + cppFilename; + std::string outputSourcePath = cCopy.outputDirPath + ra::filesystem::GetPathSeparatorStr() + sourceFilename; //configure the generator generator->setContext(cCopy); @@ -600,11 +627,11 @@ APP_ERROR_CODES processInputFile(const Context & c, bin2cpp::IGenerator * genera } //process files - bool headerResult = generateFile(c, outputHeaderPath, generator); + bool headerResult = generateOutputFile(c, outputHeaderPath, generator); if (!headerResult) return APP_ERROR_UNABLETOCREATEOUTPUTFILES; - bool cppResult = generateFile(c, outputCppPath, generator); + bool cppResult = generateOutputFile(c, outputSourcePath, generator); if (!cppResult) return APP_ERROR_UNABLETOCREATEOUTPUTFILES; @@ -612,7 +639,7 @@ APP_ERROR_CODES processInputFile(const Context & c, bin2cpp::IGenerator * genera return APP_ERROR_SUCCESS; } -APP_ERROR_CODES processInputDirectory(const Context& c, bin2cpp::IGenerator * generator) +APP_ERROR_CODES processInputDirectory(const Context& c, bin2cpp::INameProvider * nameProvider, bin2cpp::IGenerator * generator) { //check if input dir exists if (!ra::filesystem::DirectoryExists(c.inputDirPath.c_str())) @@ -680,10 +707,10 @@ APP_ERROR_CODES processInputDirectory(const Context& c, bin2cpp::IGenerator * ge cCopy.inputFilePath = file; //use the file name without extension as 'headerfile'. - cCopy.headerFilename = getDefaultHeaderFile(cCopy); + cCopy.headerFilename = nameProvider->getDefaultHeaderFile(cCopy.inputFilePath); //use the file name without extension as 'identifier'. - cCopy.functionIdentifier = getDefaultFunctionIdentifier(cCopy, identifiers_dictionary); + cCopy.functionIdentifier = nameProvider->getDefaultFunctionIdentifier(cCopy.inputFilePath, identifiers_dictionary); //build a relative file path std::string relative_file_path = file; @@ -745,7 +772,7 @@ FILE_UPDATE_MODE getFileUpdateMode(const std::string & input_file_path, const st return UPDATING; } -bool generateFile(const Context & c, const std::string & output_file_path, bin2cpp::IGenerator * generator) +bool generateOutputFile(const Context & c, const std::string & output_file_path, bin2cpp::IGenerator * generator) { FILE_UPDATE_MODE mode = getFileUpdateMode(c.inputFilePath, output_file_path, c.overrideExistingFiles); @@ -757,16 +784,26 @@ bool generateFile(const Context & c, const std::string & output_file_path, bin2c //generate file bool result = false; - if (isCppHeaderFile(output_file_path)) + if (c.code == CODE_GENERATION_CPP && isCppHeaderFile(output_file_path)) { - //generate header + //generate C++ header result = generator->createCppHeaderFile(output_file_path.c_str()); } - else + else if ( c.code == CODE_GENERATION_CPP) { - //generate cpp + //generate C++ source result = generator->createCppSourceFile(output_file_path.c_str()); } + else if ( c.code == CODE_GENERATION_C && isCHeaderFile(output_file_path) ) + { + //generate C header + result = generator->createCHeaderFile(output_file_path.c_str()); + } + else if ( c.code == CODE_GENERATION_C ) + { + //generate C source + result = generator->createCSourceFile(output_file_path.c_str()); + } if (!result) { //there was an error generating file @@ -786,12 +823,14 @@ APP_ERROR_CODES processManagerFiles(const Context & c) info << "..."; ra::logging::Log(ra::logging::LOG_INFO, info.c_str()); + const std::string& sourceFileExtension = "." + getDefaultCodeSourceFileExtension(c.code); + //prepare output files path std::string cppFilename = c.managerHeaderFilename; - ra::strings::Replace(cppFilename, ".hpp", ".cpp"); - ra::strings::Replace(cppFilename, ".h", ".cpp"); + ra::strings::Replace(cppFilename, ".hpp", sourceFileExtension); + ra::strings::Replace(cppFilename, ".h", sourceFileExtension); std::string outputHeaderPath = c.outputDirPath + ra::filesystem::GetPathSeparatorStr() + c.managerHeaderFilename; - std::string outputCppPath = c.outputDirPath + ra::filesystem::GetPathSeparatorStr() + cppFilename; + std::string outputSourcePath = c.outputDirPath + ra::filesystem::GetPathSeparatorStr() + cppFilename; ManagerGenerator generator; @@ -799,11 +838,11 @@ APP_ERROR_CODES processManagerFiles(const Context & c) generator.setContext(c); //process files - bool headerResult = generateFile(c, outputHeaderPath, &generator); + bool headerResult = generateOutputFile(c, outputHeaderPath, &generator); if (!headerResult) return APP_ERROR_UNABLETOCREATEOUTPUTFILES; - bool cppResult = generateFile(c, outputCppPath, &generator); + bool cppResult = generateOutputFile(c, outputSourcePath, &generator); if (!cppResult) return APP_ERROR_UNABLETOCREATEOUTPUTFILES; diff --git a/src/bin2cpp/types.h b/src/bin2cpp/types.h new file mode 100644 index 0000000..5f3bc09 --- /dev/null +++ b/src/bin2cpp/types.h @@ -0,0 +1,41 @@ +/********************************************************************************** + * MIT License + * + * Copyright (c) 2018 Antoine Beauchamp + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *********************************************************************************/ + +#ifndef TYPES_H +#define TYPES_H + +#include +#include + +namespace bin2cpp +{ + + /// + ///A set of names stored as a Distionnary + /// + typedef std::set Dictionary; + +}; //bin2cpp + +#endif //TYPES_H diff --git a/test/bin2cpp_unittest/CMakeLists.txt b/test/bin2cpp_unittest/CMakeLists.txt index 3e6760c..ac6c4ba 100644 --- a/test/bin2cpp_unittest/CMakeLists.txt +++ b/test/bin2cpp_unittest/CMakeLists.txt @@ -7,15 +7,21 @@ include_directories(${GENERATED_TEST_FILES_DIR}) # for TestExtraction.cpp # These are required because each generated cpp file is including its header file # without using the parent directory. include_directories(${GENERATED_TEST_FILES_DIR}/testBaseClass) +include_directories(${GENERATED_TEST_FILES_DIR}/testBaseClass_C) include_directories(${GENERATED_TEST_FILES_DIR}/testDir01/sources) include_directories(${GENERATED_TEST_FILES_DIR}/testEncodingHex) include_directories(${GENERATED_TEST_FILES_DIR}/testEncodingOct) include_directories(${GENERATED_TEST_FILES_DIR}/testFileManager) +include_directories(${GENERATED_TEST_FILES_DIR}/testFileManager_C) include_directories(${GENERATED_TEST_FILES_DIR}/testGeneratorArray10000) +include_directories(${GENERATED_TEST_FILES_DIR}/testGeneratorArray10000_C) include_directories(${GENERATED_TEST_FILES_DIR}/testGeneratorSegment10000) +include_directories(${GENERATED_TEST_FILES_DIR}/testGeneratorSegment10000_C) include_directories(${GENERATED_TEST_FILES_DIR}/testGeneratorString10000) +include_directories(${GENERATED_TEST_FILES_DIR}/testGeneratorString10000_C) if (WIN32) include_directories(${GENERATED_TEST_FILES_DIR}/testGeneratorWin32) + include_directories(${GENERATED_TEST_FILES_DIR}/testGeneratorWin32_C) endif() include_directories(${GENERATED_TEST_FILES_DIR}/testHtml100000) include_directories(${GENERATED_TEST_FILES_DIR}/testIssue12) @@ -28,6 +34,7 @@ include_directories(${GENERATED_TEST_FILES_DIR}/testIssue56b/generated_sources) include_directories(${GENERATED_TEST_FILES_DIR}/testIssue56c/generated_sources) include_directories(${GENERATED_TEST_FILES_DIR}/testKeepDirectories) include_directories(${GENERATED_TEST_FILES_DIR}/testNamespace) +include_directories(${GENERATED_TEST_FILES_DIR}/testNamespace_C) include_directories(${GENERATED_TEST_FILES_DIR}/testRandom1) include_directories(${GENERATED_TEST_FILES_DIR}/testRandom2) include_directories(${GENERATED_TEST_FILES_DIR}/testRandom3) @@ -48,6 +55,8 @@ include_directories(${GENERATED_TEST_FILES_DIR}/testText100000) set(GENERATED_TEST_FILES ${GENERATED_TEST_FILES_DIR}/testBaseClass/_testBaseClass.h ${GENERATED_TEST_FILES_DIR}/testBaseClass/_testBaseClass.cpp + ${GENERATED_TEST_FILES_DIR}/testBaseClass_C/_testBaseClass_C.h + ${GENERATED_TEST_FILES_DIR}/testBaseClass_C/_testBaseClass_C.c ${GENERATED_TEST_FILES_DIR}/testEncodingHex/_testEncodingHex.h ${GENERATED_TEST_FILES_DIR}/testEncodingHex/_testEncodingHex.cpp ${GENERATED_TEST_FILES_DIR}/testEncodingOct/_testEncodingOct.h @@ -58,6 +67,12 @@ set(GENERATED_TEST_FILES ${GENERATED_TEST_FILES_DIR}/testGeneratorSegment10000/_testGeneratorSegment10000.cpp ${GENERATED_TEST_FILES_DIR}/testGeneratorString10000/_testGeneratorString10000.h ${GENERATED_TEST_FILES_DIR}/testGeneratorString10000/_testGeneratorString10000.cpp + ${GENERATED_TEST_FILES_DIR}/testGeneratorArray10000_C/_testGeneratorArray10000_C.h + ${GENERATED_TEST_FILES_DIR}/testGeneratorArray10000_C/_testGeneratorArray10000_C.c + ${GENERATED_TEST_FILES_DIR}/testGeneratorSegment10000_C/_testGeneratorSegment10000_C.h + ${GENERATED_TEST_FILES_DIR}/testGeneratorSegment10000_C/_testGeneratorSegment10000_C.c + ${GENERATED_TEST_FILES_DIR}/testGeneratorString10000_C/_testGeneratorString10000_C.h + ${GENERATED_TEST_FILES_DIR}/testGeneratorString10000_C/_testGeneratorString10000_C.c ${GENERATED_TEST_FILES_DIR}/testHtml100000/_testHtml100000.h ${GENERATED_TEST_FILES_DIR}/testHtml100000/_testHtml100000.cpp ${GENERATED_TEST_FILES_DIR}/testIssue12/_testIssue12.h @@ -110,6 +125,8 @@ set(GENERATED_TEST_FILES ${GENERATED_TEST_FILES_DIR}/testIssue56c/generated_sources/index_5.h ${GENERATED_TEST_FILES_DIR}/testNamespace/_testNamespace.h ${GENERATED_TEST_FILES_DIR}/testNamespace/_testNamespace.cpp + ${GENERATED_TEST_FILES_DIR}/testNamespace_C/_testNamespace_C.h + ${GENERATED_TEST_FILES_DIR}/testNamespace_C/_testNamespace_C.c ${GENERATED_TEST_FILES_DIR}/testRandom1/_testRandom1.h ${GENERATED_TEST_FILES_DIR}/testRandom1/_testRandom1.cpp ${GENERATED_TEST_FILES_DIR}/testRandom2/_testRandom2.h @@ -142,6 +159,12 @@ set(GENERATED_TEST_FILES ${GENERATED_TEST_FILES_DIR}/testFileManager/_testFileManager.2.cpp ${GENERATED_TEST_FILES_DIR}/testFileManager/FileManager.h ${GENERATED_TEST_FILES_DIR}/testFileManager/FileManager.cpp + ${GENERATED_TEST_FILES_DIR}/testFileManager_C/_testFileManager_C.1.h + ${GENERATED_TEST_FILES_DIR}/testFileManager_C/_testFileManager_C.1.c + ${GENERATED_TEST_FILES_DIR}/testFileManager_C/_testFileManager_C.2.h + ${GENERATED_TEST_FILES_DIR}/testFileManager_C/_testFileManager_C.2.c + ${GENERATED_TEST_FILES_DIR}/testFileManager_C/filemanager.h + ${GENERATED_TEST_FILES_DIR}/testFileManager_C/filemanager.c ${GENERATED_TEST_FILES_DIR}/testDir01/sources/_img0001.h ${GENERATED_TEST_FILES_DIR}/testDir01/sources/_img0002.h ${GENERATED_TEST_FILES_DIR}/testDir01/sources/_img0003.h @@ -177,14 +200,34 @@ if (WIN32) ${GENERATED_TEST_FILES_DIR}/testGeneratorWin32/_testGeneratorWin32.h ${GENERATED_TEST_FILES_DIR}/testGeneratorWin32/_testGeneratorWin32.cpp ${GENERATED_TEST_FILES_DIR}/testGeneratorWin32/_testGeneratorWin32.rc + ${GENERATED_TEST_FILES_DIR}/testGeneratorWin32_C/_testGeneratorWin32_C.h + ${GENERATED_TEST_FILES_DIR}/testGeneratorWin32_C/_testGeneratorWin32_C.c + ${GENERATED_TEST_FILES_DIR}/testGeneratorWin32_C/_testGeneratorWin32_C.rc ) endif() set(TEMPLATE_SCRIPT_FILE ${BIN2CPP_UNITTEST_SOURCE_DIR}/generate_test_files.${SCRIPT_FILE_EXTENSION}.in) -# https://stackoverflow.com/questions/18427877/add-custom-build-step-in-cmake +# Define a dummy file to act as a stamp for file generation completion +set(GENERATION_STAMP ${GENERATED_TEST_FILES_DIR}/generated_files.stamp) + +# The script generates multiple files from a single command, but if I try to list all of them as OUTPUT of the add_custom_command it won't work. +# CMake assumes each output is independently produced, so it starts compiling as soon as any one is generated, not necessarily all. +# +# CMake tracks outputs individually, so even if I have `OUTPUT ${GENERATED_TEST_FILES}`, it sees each output as possibly ready at different times. +# When one appears on disk (even partially), it may trigger compilation of bin2cpp_unittest, before the script finishes generating all files. +# +# To work around this behavior, is using a stamp file which is created after the generation script has run which indicates to CMake we are ready to compile bin2cpp_unittest. +# Required steps: +# Only the stamp file is listed as OUTPUT. This prevents CMake from checking each generated file independently. +# The actual source files are listed as BYPRODUCTS, so CMake knows they will appear but doesn't rely on their timestamps individually. +# add_dependencies() ensures the executable only starts compiling after the stamp file is created, i.e., after the script finishes. +# +# To force CMake to regenerate the files, delete the stamp file. +# if (WIN32) - add_custom_command( OUTPUT ${GENERATED_TEST_FILES} + add_custom_command( OUTPUT ${GENERATION_STAMP} + BYPRODUCTS ${GENERATED_TEST_FILES} # Execute prebuild copy COMMAND ${CMAKE_COMMAND} -DBIN2CPP_TARGET_FILE=$ @@ -197,10 +240,17 @@ if (WIN32) # Execute generator script COMMAND echo Calling 'generate_test_files.bat' script... COMMAND cd /d ${CMAKE_CURRENT_BINARY_DIR} && call generate_test_files.bat + + # Tell CMake that we generated the last file of the series + COMMAND ${CMAKE_COMMAND} -E touch ${GENERATION_STAMP} + + COMMENT "Generating test files" + VERBATIM ) else() # Linux build - add_custom_command( OUTPUT ${GENERATED_TEST_FILES} + add_custom_command( OUTPUT ${GENERATION_STAMP} + BYPRODUCTS ${GENERATED_TEST_FILES} # Execute prebuild copy COMMAND ${CMAKE_COMMAND} -DBIN2CPP_TARGET_FILE=$ @@ -209,21 +259,23 @@ else() -DBIN2CPP_UNITTEST_PROJECT_DIR=${CMAKE_CURRENT_BINARY_DIR} -DBIN2CPP_UNITTEST_OUTPUT_DIR=${CMAKE_BINARY_DIR}/bin -P ${CMAKE_CURRENT_SOURCE_DIR}/generate_test_files.cmake - + # Execute generator script COMMAND echo Calling 'generate_test_files.sh' script... COMMAND cd ${CMAKE_CURRENT_BINARY_DIR} && ./generate_test_files.sh + + # Tell CMake that we generated the last file of the series + COMMAND ${CMAKE_COMMAND} -E touch ${GENERATION_STAMP} + + COMMENT "Generating test files" + VERBATIM ) endif() -# target build_test_files is always built -add_custom_target(build_test_files ALL - COMMAND echo "Building unit test files..." - - # If the files exists, then commands related to these files won't be executed - # DONOT let other target depends on the same OUTPUT as current target, - # or it may be bad when doing parallel make - DEPENDS ${GENERATED_TEST_FILES} +# Create a custom target that depends on the generated files +add_custom_target(generate_test_files + DEPENDS ${GENERATION_STAMP} + COMMENT "Ensuring file generation stamp exist" ) # Show all generated files in a common folder @@ -235,6 +287,9 @@ source_group("External Files" FILES ${CMAKE_SOURCE_DIR}/src/bin2cpp/wildcard.h ) +# Ensure source files are properly recognized as generated +set_source_files_properties(${GENERATED_TEST_FILES} PROPERTIES GENERATED TRUE) + add_executable(bin2cpp_unittest ${BIN2CPP_VERSION_HEADER} ${BIN2CPP_CONFIG_HEADER} @@ -262,7 +317,8 @@ add_executable(bin2cpp_unittest add_dependencies(bin2cpp_unittest bin2cpp) add_dependencies(bin2cpp_unittest testfilegenerator) -add_dependencies(bin2cpp_unittest build_test_files) +add_dependencies(bin2cpp_unittest generate_test_files) + # Unit test projects requires to link with pthread if also linking with gtest if(NOT WIN32) diff --git a/test/bin2cpp_unittest/TestCLI.cpp b/test/bin2cpp_unittest/TestCLI.cpp index 5a6ca0b..1ce7046 100755 --- a/test/bin2cpp_unittest/TestCLI.cpp +++ b/test/bin2cpp_unittest/TestCLI.cpp @@ -48,12 +48,15 @@ extern const std::string & gGeneratedFilesDir; enum APP_ERROR_CODES { - APP_ERROR_SUCCESS, + APP_ERROR_SUCCESS = 0, APP_ERROR_MISSINGARGUMENTS, APP_ERROR_INPUTFILENOTFOUND, APP_ERROR_UNABLETOCREATEOUTPUTFILES, APP_ERROR_TOOMANYARGUMENTS, - APP_ERROR_INPUTDIRNOTFOUND + APP_ERROR_INPUTDIRNOTFOUND, + AAP_ERROR_NOTSUPPORTED, + APP_ERROR_OPERATIONHASFAILED, + APP_ERROR_INVALIDVALUE, }; namespace TestCLIUtils @@ -1224,7 +1227,7 @@ TEST_F(TestCLI, testAutomaticIdentifierHeaderfile) ASSERT_TRUE(deleteFile(cppFilePath.c_str())); } -TEST_F(TestCLI, testErrorMissingArgumentEncoding) +TEST_F(TestCLI, testErrorInvalidArgumentEncoding) { static const std::string expectedFilePath = getExpectedFilePath(); static const std::string outputFilePath = getActualFilePath(); @@ -1258,7 +1261,7 @@ TEST_F(TestCLI, testErrorMissingArgumentEncoding) #if defined(__linux__) || defined(__APPLE__) returnCode = WEXITSTATUS(returnCode); #endif - ASSERT_EQ(APP_ERROR_MISSINGARGUMENTS, returnCode) << "The command line '" << cmdline.c_str() << "' returned " << returnCode; + ASSERT_EQ(APP_ERROR_INVALIDVALUE, returnCode) << "The command line '" << cmdline.c_str() << "' returned " << returnCode; //load output file ra::strings::StringVector lines; @@ -1266,7 +1269,7 @@ TEST_F(TestCLI, testErrorMissingArgumentEncoding) ASSERT_TRUE(loaded); //assert standard output log - ASSERT_TEXT_IN_FILE(true, outputFilePath.c_str(), "Error: Missing arguments (encoding)"); + ASSERT_TEXT_IN_FILE(true, outputFilePath.c_str(), "Error: Invalid value (encoding)"); //cleanup ASSERT_TRUE(deleteFile(outputFilePath.c_str())); diff --git a/test/bin2cpp_unittest/TestExtraction.cpp b/test/bin2cpp_unittest/TestExtraction.cpp index c35d71b..dbde3b8 100755 --- a/test/bin2cpp_unittest/TestExtraction.cpp +++ b/test/bin2cpp_unittest/TestExtraction.cpp @@ -69,6 +69,18 @@ #undef BIN2CPP_EMBEDDEDFILE_CLASS #include "testBaseClass/_testBaseClass.h" +extern "C" +{ + #include "testGeneratorArray10000_C/_testGeneratorArray10000_C.h" + #include "testGeneratorSegment10000_C/_testGeneratorSegment10000_C.h" + #include "testGeneratorString10000_C/_testGeneratorString10000_C.h" + #ifdef _WIN32 + #include "testGeneratorWin32_C/_testGeneratorWin32_C.h" + #endif + #include "testNamespace_C/_testNamespace_C.h" + #include "testBaseClass_C/_testBaseClass_C.h" +} + extern const std::string & gGeneratedFilesDir; namespace TestExtractionUtils @@ -330,6 +342,21 @@ TEST_F(TestExtraction, testGeneratorArray10000) ASSERT_TRUE(equal) << reason.c_str(); } +TEST_F(TestExtraction, testGeneratorArray10000_C) +{ + static const std::string expectedFilePath = getExpectedFilePath(); + static const std::string outputFilePath = getActualFilePath(); + + Bin2cFile* file = bin2c_get_file_testgeneratorarray10000_c(); + bool extractSuccess = file->save(outputFilePath.c_str()); + ASSERT_TRUE(extractSuccess); + + //assert binary content is the same + std::string reason; + bool equal = ra::testing::IsFileEquals(expectedFilePath.c_str(), outputFilePath.c_str(), reason); + ASSERT_TRUE(equal) << reason.c_str(); +} + TEST_F(TestExtraction, testGeneratorSegment10000) { static const std::string expectedFilePath = getExpectedFilePath(); @@ -345,6 +372,21 @@ TEST_F(TestExtraction, testGeneratorSegment10000) ASSERT_TRUE(equal) << reason.c_str(); } +TEST_F(TestExtraction, testGeneratorSegment10000_C) +{ + static const std::string expectedFilePath = getExpectedFilePath(); + static const std::string outputFilePath = getActualFilePath(); + + const Bin2cFile* file = bin2c_get_file_testgeneratorsegment10000_c(); + bool extractSuccess = file->save(outputFilePath.c_str()); + ASSERT_TRUE(extractSuccess); + + //assert binary content is the same + std::string reason; + bool equal = ra::testing::IsFileEquals(expectedFilePath.c_str(), outputFilePath.c_str(), reason); + ASSERT_TRUE(equal) << reason.c_str(); +} + TEST_F(TestExtraction, testGeneratorString10000) { static const std::string expectedFilePath = getExpectedFilePath(); @@ -360,6 +402,21 @@ TEST_F(TestExtraction, testGeneratorString10000) ASSERT_TRUE(equal) << reason.c_str(); } +TEST_F(TestExtraction, testGeneratorString10000_C) +{ + static const std::string expectedFilePath = getExpectedFilePath(); + static const std::string outputFilePath = getActualFilePath(); + + const Bin2cFile* file = bin2c_get_file_testgeneratorstring10000_c(); + bool extractSuccess = file->save(outputFilePath.c_str()); + ASSERT_TRUE(extractSuccess); + + //assert binary content is the same + std::string reason; + bool equal = ra::testing::IsFileEquals(expectedFilePath.c_str(), outputFilePath.c_str(), reason); + ASSERT_TRUE(equal) << reason.c_str(); +} + #ifdef _WIN32 TEST_F(TestExtraction, testGeneratorWin32) { @@ -375,6 +432,21 @@ TEST_F(TestExtraction, testGeneratorWin32) bool equal = ra::testing::IsFileEquals(expectedFilePath.c_str(), outputFilePath.c_str(), reason); ASSERT_TRUE(equal) << reason.c_str(); } + +TEST_F(TestExtraction, testGeneratorWin32_C) +{ + static const std::string expectedFilePath = getExpectedFilePath(); + static const std::string outputFilePath = getActualFilePath(); + + const Bin2cFile* file = bin2c_get_file_testgeneratorwin32_c(); + bool extractSuccess = file->save(outputFilePath.c_str()); + ASSERT_TRUE(extractSuccess); + + //assert binary content is the same + std::string reason; + bool equal = ra::testing::IsFileEquals(expectedFilePath.c_str(), outputFilePath.c_str(), reason); + ASSERT_TRUE(equal) << reason.c_str(); +} #endif TEST_F(TestExtraction, testEncodingOct) @@ -422,6 +494,21 @@ TEST_F(TestExtraction, testNamespace) ASSERT_TRUE(equal) << reason.c_str(); } +TEST_F(TestExtraction, testNamespace_C) +{ + static const std::string expectedFilePath = getExpectedFilePath(); + static const std::string outputFilePath = getActualFilePath(); + + const Bin2cFile2* file = foobar_get_file_testnamespace_c(); + bool extractSuccess = file->save(outputFilePath.c_str()); + ASSERT_TRUE(extractSuccess); + + //assert binary content is the same + std::string reason; + bool equal = ra::testing::IsFileEquals(expectedFilePath.c_str(), outputFilePath.c_str(), reason); + ASSERT_TRUE(equal) << reason.c_str(); +} + TEST_F(TestExtraction, testBaseClass) { static const std::string expectedFilePath = getExpectedFilePath(); @@ -437,6 +524,21 @@ TEST_F(TestExtraction, testBaseClass) ASSERT_TRUE(equal) << reason.c_str(); } +TEST_F(TestExtraction, testBaseClass_C) +{ + static const std::string expectedFilePath = getExpectedFilePath(); + static const std::string outputFilePath = getActualFilePath(); + + const Resource* file = tbc_get_file_testbaseclass_c(); + bool extractSuccess = file->save(outputFilePath.c_str()); + ASSERT_TRUE(extractSuccess); + + //assert binary content is the same + std::string reason; + bool equal = ra::testing::IsFileEquals(expectedFilePath.c_str(), outputFilePath.c_str(), reason); + ASSERT_TRUE(equal) << reason.c_str(); +} + TEST_F(TestExtraction, testIssue12) { static const std::string expectedFilePath = getExpectedFilePath(); diff --git a/test/bin2cpp_unittest/generate_test_files.bat.in b/test/bin2cpp_unittest/generate_test_files.bat.in index d30366f..13708da 100644 --- a/test/bin2cpp_unittest/generate_test_files.bat.in +++ b/test/bin2cpp_unittest/generate_test_files.bat.in @@ -121,6 +121,13 @@ mkdir %OUTDIR% 1>NUL 2>NUL @BIN2CPP_TARGET_FILE@ --noheader --file=%OUTDIR%\%TEST_NAME%.bin --output=%OUTDIR% --headerfile=_%TEST_NAME%.h --identifier=%TEST_NAME% --chunksize=450 --generator=array --override if %errorlevel% neq 0 exit /b %errorlevel% +set TEST_NAME=testGeneratorArray10000_C +set OUTDIR=.\generated_files\%TEST_NAME% +mkdir %OUTDIR% 1>NUL 2>NUL +@TESTFILEGENERATOR_TARGET_FILE@ --file=%OUTDIR%\%TEST_NAME%.bin --size=10000 --fill=sequential +@BIN2CPP_TARGET_FILE@ --noheader --file=%OUTDIR%\%TEST_NAME%.bin --output=%OUTDIR% --headerfile=_%TEST_NAME%.h --identifier=%TEST_NAME% --chunksize=450 --generator=array --override --code=c +if %errorlevel% neq 0 exit /b %errorlevel% + set TEST_NAME=testGeneratorString10000 set OUTDIR=.\generated_files\%TEST_NAME% mkdir %OUTDIR% 1>NUL 2>NUL @@ -128,6 +135,13 @@ mkdir %OUTDIR% 1>NUL 2>NUL @BIN2CPP_TARGET_FILE@ --noheader --file=%OUTDIR%\%TEST_NAME%.bin --output=%OUTDIR% --headerfile=_%TEST_NAME%.h --identifier=%TEST_NAME% --chunksize=450 --generator=string --override if %errorlevel% neq 0 exit /b %errorlevel% +set TEST_NAME=testGeneratorString10000_C +set OUTDIR=.\generated_files\%TEST_NAME% +mkdir %OUTDIR% 1>NUL 2>NUL +@TESTFILEGENERATOR_TARGET_FILE@ --file=%OUTDIR%\%TEST_NAME%.bin --size=10000 --fill=sequential +@BIN2CPP_TARGET_FILE@ --noheader --file=%OUTDIR%\%TEST_NAME%.bin --output=%OUTDIR% --headerfile=_%TEST_NAME%.h --identifier=%TEST_NAME% --chunksize=450 --generator=string --override --code=c +if %errorlevel% neq 0 exit /b %errorlevel% + set TEST_NAME=testGeneratorSegment10000 set OUTDIR=.\generated_files\%TEST_NAME% mkdir %OUTDIR% 1>NUL 2>NUL @@ -135,6 +149,13 @@ mkdir %OUTDIR% 1>NUL 2>NUL @BIN2CPP_TARGET_FILE@ --noheader --file=%OUTDIR%\%TEST_NAME%.bin --output=%OUTDIR% --headerfile=_%TEST_NAME%.h --identifier=%TEST_NAME% --chunksize=450 --generator=segment --override if %errorlevel% neq 0 exit /b %errorlevel% +set TEST_NAME=testGeneratorSegment10000_C +set OUTDIR=.\generated_files\%TEST_NAME% +mkdir %OUTDIR% 1>NUL 2>NUL +@TESTFILEGENERATOR_TARGET_FILE@ --file=%OUTDIR%\%TEST_NAME%.bin --size=10000 --fill=sequential +@BIN2CPP_TARGET_FILE@ --noheader --file=%OUTDIR%\%TEST_NAME%.bin --output=%OUTDIR% --headerfile=_%TEST_NAME%.h --identifier=%TEST_NAME% --chunksize=450 --generator=segment --override --code=c +if %errorlevel% neq 0 exit /b %errorlevel% + set TEST_NAME=testGeneratorWin32 set OUTDIR=.\generated_files\%TEST_NAME% mkdir %OUTDIR% 1>NUL 2>NUL @@ -142,6 +163,13 @@ mkdir %OUTDIR% 1>NUL 2>NUL @BIN2CPP_TARGET_FILE@ --noheader --file=%OUTDIR%\%TEST_NAME%.bin --output=%OUTDIR% --headerfile=_%TEST_NAME%.h --identifier=%TEST_NAME% --chunksize=450 --generator=win32 --override if %errorlevel% neq 0 exit /b %errorlevel% +set TEST_NAME=testGeneratorWin32_C +set OUTDIR=.\generated_files\%TEST_NAME% +mkdir %OUTDIR% 1>NUL 2>NUL +@TESTFILEGENERATOR_TARGET_FILE@ --file=%OUTDIR%\%TEST_NAME%.bin --size=10000 --fill=sequential +@BIN2CPP_TARGET_FILE@ --noheader --file=%OUTDIR%\%TEST_NAME%.bin --output=%OUTDIR% --headerfile=_%TEST_NAME%.h --identifier=%TEST_NAME% --chunksize=450 --generator=win32 --override --code=c +if %errorlevel% neq 0 exit /b %errorlevel% + set TEST_NAME=testNamespace set OUTDIR=.\generated_files\%TEST_NAME% mkdir %OUTDIR% 1>NUL 2>NUL @@ -149,6 +177,13 @@ mkdir %OUTDIR% 1>NUL 2>NUL @BIN2CPP_TARGET_FILE@ --noheader --file=%OUTDIR%\%TEST_NAME%.bin --output=%OUTDIR% --headerfile=_%TEST_NAME%.h --identifier=%TEST_NAME% --chunksize=450 --generator=segment --override --namespace=foobar if %errorlevel% neq 0 exit /b %errorlevel% +set TEST_NAME=testNamespace_C +set OUTDIR=.\generated_files\%TEST_NAME% +mkdir %OUTDIR% 1>NUL 2>NUL +@TESTFILEGENERATOR_TARGET_FILE@ --file=%OUTDIR%\%TEST_NAME%.bin --size=10000 --fill=sequential +@BIN2CPP_TARGET_FILE@ --noheader --file=%OUTDIR%\%TEST_NAME%.bin --output=%OUTDIR% --headerfile=_%TEST_NAME%.h --identifier=%TEST_NAME% --chunksize=450 --generator=segment --override --baseclass=Bin2cFile2 --namespace=foobar --code=c +if %errorlevel% neq 0 exit /b %errorlevel% + set TEST_NAME=testBaseClass set OUTDIR=.\generated_files\%TEST_NAME% mkdir %OUTDIR% 1>NUL 2>NUL @@ -156,6 +191,13 @@ mkdir %OUTDIR% 1>NUL 2>NUL @BIN2CPP_TARGET_FILE@ --noheader --file=%OUTDIR%\%TEST_NAME%.bin --output=%OUTDIR% --headerfile=_%TEST_NAME%.h --identifier=%TEST_NAME% --chunksize=450 --generator=segment --override --baseclass=Resource if %errorlevel% neq 0 exit /b %errorlevel% +set TEST_NAME=testBaseClass_C +set OUTDIR=.\generated_files\%TEST_NAME% +mkdir %OUTDIR% 1>NUL 2>NUL +@TESTFILEGENERATOR_TARGET_FILE@ --file=%OUTDIR%\%TEST_NAME%.bin --size=10000 --fill=sequential +@BIN2CPP_TARGET_FILE@ --noheader --file=%OUTDIR%\%TEST_NAME%.bin --output=%OUTDIR% --headerfile=_%TEST_NAME%.h --identifier=%TEST_NAME% --chunksize=450 --generator=segment --override --baseclass=Resource --namespace=tbc --code=c +if %errorlevel% neq 0 exit /b %errorlevel% + set TEST_NAME=testEncodingOct set OUTDIR=.\generated_files\%TEST_NAME% mkdir %OUTDIR% 1>NUL 2>NUL @@ -201,6 +243,15 @@ mkdir %OUTDIR% 1>NUL 2>NUL @BIN2CPP_TARGET_FILE@ --noheader --file=%OUTDIR%\%TEST_NAME%.2.bin --output=%OUTDIR% --headerfile=_%TEST_NAME%.2.h --identifier=%TEST_NAME%2 --registerfile --override if %errorlevel% neq 0 exit /b %errorlevel% +set TEST_NAME=testFileManager_C +set OUTDIR=.\generated_files\%TEST_NAME% +mkdir %OUTDIR% 1>NUL 2>NUL +@TESTFILEGENERATOR_TARGET_FILE@ --file=%OUTDIR%\%TEST_NAME%.1.bin --size=1000 --fill=random --seed=1 +@TESTFILEGENERATOR_TARGET_FILE@ --file=%OUTDIR%\%TEST_NAME%.2.bin --size=1000 --fill=random --seed=2 +@BIN2CPP_TARGET_FILE@ --noheader --file=%OUTDIR%\%TEST_NAME%.1.bin --output=%OUTDIR% --headerfile=_%TEST_NAME%.1.h --identifier=%TEST_NAME%1 --managerfile=filemanager.h --override --code=c +@BIN2CPP_TARGET_FILE@ --noheader --file=%OUTDIR%\%TEST_NAME%.2.bin --output=%OUTDIR% --headerfile=_%TEST_NAME%.2.h --identifier=%TEST_NAME%2 --registerfile --override --code=c +if %errorlevel% neq 0 exit /b %errorlevel% + set TEST_NAME=testDir01 set OUTDIR=.\generated_files\%TEST_NAME% mkdir %OUTDIR% 1>NUL 2>NUL diff --git a/test/bin2cpp_unittest/generate_test_files.sh.in b/test/bin2cpp_unittest/generate_test_files.sh.in index 30f8f16..f808d55 100755 --- a/test/bin2cpp_unittest/generate_test_files.sh.in +++ b/test/bin2cpp_unittest/generate_test_files.sh.in @@ -105,36 +105,72 @@ mkdir -p ${OUTDIR} @TESTFILEGENERATOR_TARGET_FILE@ --file=$OUTDIR/$TEST_NAME.bin --size=10000 --fill=sequential @BIN2CPP_TARGET_FILE@ --noheader --file=$OUTDIR/$TEST_NAME.bin --output=$OUTDIR --headerfile=_$TEST_NAME.h --identifier=$TEST_NAME --chunksize=450 --generator=array --override +export TEST_NAME=testGeneratorArray10000_C +export OUTDIR=./generated_files/$TEST_NAME +mkdir -p ${OUTDIR} +@TESTFILEGENERATOR_TARGET_FILE@ --file=$OUTDIR/$TEST_NAME.bin --size=10000 --fill=sequential +@BIN2CPP_TARGET_FILE@ --noheader --file=$OUTDIR/$TEST_NAME.bin --output=$OUTDIR --headerfile=_$TEST_NAME.h --identifier=$TEST_NAME --chunksize=450 --generator=array --override --code=c + export TEST_NAME=testGeneratorString10000 export OUTDIR=./generated_files/$TEST_NAME mkdir -p ${OUTDIR} @TESTFILEGENERATOR_TARGET_FILE@ --file=$OUTDIR/$TEST_NAME.bin --size=10000 --fill=sequential @BIN2CPP_TARGET_FILE@ --noheader --file=$OUTDIR/$TEST_NAME.bin --output=$OUTDIR --headerfile=_$TEST_NAME.h --identifier=$TEST_NAME --chunksize=450 --generator=string --override +export TEST_NAME=testGeneratorString10000_C +export OUTDIR=./generated_files/$TEST_NAME +mkdir -p ${OUTDIR} +@TESTFILEGENERATOR_TARGET_FILE@ --file=$OUTDIR/$TEST_NAME.bin --size=10000 --fill=sequential +@BIN2CPP_TARGET_FILE@ --noheader --file=$OUTDIR/$TEST_NAME.bin --output=$OUTDIR --headerfile=_$TEST_NAME.h --identifier=$TEST_NAME --chunksize=450 --generator=string --override --code=c + export TEST_NAME=testGeneratorSegment10000 export OUTDIR=./generated_files/$TEST_NAME mkdir -p ${OUTDIR} @TESTFILEGENERATOR_TARGET_FILE@ --file=$OUTDIR/$TEST_NAME.bin --size=10000 --fill=sequential @BIN2CPP_TARGET_FILE@ --noheader --file=$OUTDIR/$TEST_NAME.bin --output=$OUTDIR --headerfile=_$TEST_NAME.h --identifier=$TEST_NAME --chunksize=450 --generator=segment --override +export TEST_NAME=testGeneratorSegment10000_C +export OUTDIR=./generated_files/$TEST_NAME +mkdir -p ${OUTDIR} +@TESTFILEGENERATOR_TARGET_FILE@ --file=$OUTDIR/$TEST_NAME.bin --size=10000 --fill=sequential +@BIN2CPP_TARGET_FILE@ --noheader --file=$OUTDIR/$TEST_NAME.bin --output=$OUTDIR --headerfile=_$TEST_NAME.h --identifier=$TEST_NAME --chunksize=450 --generator=segment --override --code=c + export TEST_NAME=testGeneratorWin32 export OUTDIR=./generated_files/$TEST_NAME mkdir -p ${OUTDIR} @TESTFILEGENERATOR_TARGET_FILE@ --file=$OUTDIR/$TEST_NAME.bin --size=10000 --fill=sequential @BIN2CPP_TARGET_FILE@ --noheader --file=$OUTDIR/$TEST_NAME.bin --output=$OUTDIR --headerfile=_$TEST_NAME.h --identifier=$TEST_NAME --chunksize=450 --generator=win32 --override +export TEST_NAME=testGeneratorWin32_C +export OUTDIR=./generated_files/$TEST_NAME +mkdir -p ${OUTDIR} +@TESTFILEGENERATOR_TARGET_FILE@ --file=$OUTDIR/$TEST_NAME.bin --size=10000 --fill=sequential +@BIN2CPP_TARGET_FILE@ --noheader --file=$OUTDIR/$TEST_NAME.bin --output=$OUTDIR --headerfile=_$TEST_NAME.h --identifier=$TEST_NAME --chunksize=450 --generator=win32 --override --code=c + export TEST_NAME=testNamespace export OUTDIR=./generated_files/$TEST_NAME mkdir -p ${OUTDIR} @TESTFILEGENERATOR_TARGET_FILE@ --file=$OUTDIR/$TEST_NAME.bin --size=10000 --fill=sequential @BIN2CPP_TARGET_FILE@ --noheader --file=$OUTDIR/$TEST_NAME.bin --output=$OUTDIR --headerfile=_$TEST_NAME.h --identifier=$TEST_NAME --chunksize=450 --generator=segment --override --namespace=foobar +export TEST_NAME=testNamespace_C +export OUTDIR=./generated_files/$TEST_NAME +mkdir -p ${OUTDIR} +@TESTFILEGENERATOR_TARGET_FILE@ --file=$OUTDIR/$TEST_NAME.bin --size=10000 --fill=sequential +@BIN2CPP_TARGET_FILE@ --noheader --file=$OUTDIR/$TEST_NAME.bin --output=$OUTDIR --headerfile=_$TEST_NAME.h --identifier=$TEST_NAME --chunksize=450 --generator=segment --override --baseclass=Bin2cFile2 --namespace=foobar --code=c + export TEST_NAME=testBaseClass export OUTDIR=./generated_files/$TEST_NAME mkdir -p ${OUTDIR} @TESTFILEGENERATOR_TARGET_FILE@ --file=$OUTDIR/$TEST_NAME.bin --size=10000 --fill=sequential @BIN2CPP_TARGET_FILE@ --noheader --file=$OUTDIR/$TEST_NAME.bin --output=$OUTDIR --headerfile=_$TEST_NAME.h --identifier=$TEST_NAME --chunksize=450 --generator=segment --override --baseclass=Resource +export TEST_NAME=testBaseClass_C +export OUTDIR=./generated_files/$TEST_NAME +mkdir -p ${OUTDIR} +@TESTFILEGENERATOR_TARGET_FILE@ --file=$OUTDIR/$TEST_NAME.bin --size=10000 --fill=sequential +@BIN2CPP_TARGET_FILE@ --noheader --file=$OUTDIR/$TEST_NAME.bin --output=$OUTDIR --headerfile=_$TEST_NAME.h --identifier=$TEST_NAME --chunksize=450 --generator=segment --override --baseclass=Resource --namespace=tbc --code=c + export TEST_NAME=testEncodingOct export OUTDIR=./generated_files/$TEST_NAME mkdir -p ${OUTDIR} @@ -174,6 +210,14 @@ mkdir -p ${OUTDIR} @BIN2CPP_TARGET_FILE@ --noheader --file=$OUTDIR/$TEST_NAME.1.bin --output=$OUTDIR --headerfile=_$TEST_NAME.1.h --identifier=testFileManager1 --managerfile=FileManager.h --override @BIN2CPP_TARGET_FILE@ --noheader --file=$OUTDIR/$TEST_NAME.2.bin --output=$OUTDIR --headerfile=_$TEST_NAME.2.h --identifier=testFileManager2 --registerfile --override +export TEST_NAME=testFileManager_C +export OUTDIR=./generated_files/$TEST_NAME +mkdir -p ${OUTDIR} +@TESTFILEGENERATOR_TARGET_FILE@ --file=$OUTDIR/$TEST_NAME.1.bin --size=1000 --fill=random --seed=1 +@TESTFILEGENERATOR_TARGET_FILE@ --file=$OUTDIR/$TEST_NAME.2.bin --size=1000 --fill=random --seed=2 +@BIN2CPP_TARGET_FILE@ --noheader --file=$OUTDIR/$TEST_NAME.1.bin --output=$OUTDIR --headerfile=_$TEST_NAME.1.h --identifier=testFileManager1 --managerfile=filemanager.h --override --code=c +@BIN2CPP_TARGET_FILE@ --noheader --file=$OUTDIR/$TEST_NAME.2.bin --output=$OUTDIR --headerfile=_$TEST_NAME.2.h --identifier=testFileManager2 --registerfile --override --code=c + export TEST_NAME=testDir01 export OUTDIR=./generated_files/$TEST_NAME mkdir -p ${OUTDIR}