C++资源编译工具,用于将任何格式的文件编译成C++代码

简介: resource_maker.zip          linux自带了一个名叫xxd的工具,带参数-i运行时,效果类似,如:xxd -i /bin/ls。 // C++资源编译工具,用于将任何格式的文件编译成C++代码 // 优点:单个.
img_e25d4fb2f8de1caf41a735ec53088516.pngresource_maker.zip          linux自带了一个名叫 xxd的工具,带参数-i运行时,效果类似,如:xxd -i /bin/ls。

  1. // C++资源编译工具,用于将任何格式的文件编译成C++代码
  2. // 优点:单个.cpp文件,无其它依赖,一句编译后即可使用
  3. // 编译:g++ -Wall -g -o resource_maker resource_maker.cpp
  4. //
  5. // 编译后,会生成与资源文件对应的.cpp文件,访.cpp文件包含两个全局变量:
  6. // 1) size变量:存储资源文件的字节数大小,变量名同文件名,但不包含扩展名部分
  7. // 2) 资源文件的内容变量:以十六进制方式表达
  8. // 注意,所有变量总是位于resource名字空间内。
  9. //
  10. // 示例,假设就以resource_maker.cpp为资源文件,则:
  11. // 1) 将resource_maker.cpp编译成C++代码:./resource_maker ./resource_maker.cpp
  12. // 2) 可以看到生成了对应的c++代码文件:res_resource_maker.cpp
  13. // 3) 打开res_resource_maker.cpp文件,可以看到的两个resource名字空间内的全局变量:
  14. // size_t resource_maker_size和unsigned char resource_maker[];
  15. //
  16. // 接下来,就可以根据需求使用以变量的形式在c++代码中以只读的方式访问资源文件了,如:
  17. // namespace resource {
  18. // extern size_t resource_maker_size;
  19. // extern unsigned char resource_maker[];
  20. // }
  21. // int main()
  22. // {
  23. // // 因为resource_maker.cpp是文本格式,所以可以printf,
  24. // // 但如果是图片、音频等二进制格式的文件,显示就不能这样了。
  25. // printf("%s\n", static_castchar*>(resource::resource_maker));
  26. // return 0;
  27. // }
  28. #include error.h>
  29. #include fstream>
  30. #include libgen.h>
  31. #include stdio.h>
  32. #include stdlib.h>
  33. #include string>
  34. #include string.h>
  35. #include sys/types.h>
  36. #include sys/stat.h>
  37. #include unistd.h>

  38. // 从文件路径中扣出不带斜杠结尾的目录路径
  39. static std::string extract_dirpath_without_slash(const std::string& filepath);

  40. // 从文件路径中扣出不带后缀的文件名
  41. static std::string extract_filename_without_suffix(const std::string& filepath);

  42. // 将一个文件内容全读取出来,放到buffer中
  43. static bool file2string(const std::string& filepath, std::string* buffer);

  44. //.h文件
  45. static bool write_h_file(const std::string& resource_h_filepath, const std::string& c_variable_name);

  46. //.cpp文件
  47. static bool write_cpp_file(const std::string& resource_cpp_filepath, const std::string& c_variable_name, const std::string& buffer);

  48. // 将一个十进制值转换成十六进制,并带前缀0x,如果不足位字符宽度,则被0
  49. // 如:a变成0x61,1变成0x31,。。。
  50. static std::string dec2hex(unsigned char c);

  51. // 用法,带2个参数:
  52. // 参数1:resource_maker 资源文件
  53. // 参数2:文件名前缀(可选,默认为res_,如果文件名是骆驼命名风格,建议改为Res)
  54. int main(int argc, char* argv[])
  55. {
  56.     std::string filename_prefix = (3 == argc)? argv[2]: "res_";
  57.     std::string resource_filepath = argv[1];
  58.     std::string resource_dirpath = extract_dirpath_without_slash(resource_filepath);
  59.     std::string filename_without_suffix = extract_filename_without_suffix(resource_filepath);

  60.     std::string resource_h_filepath = resource_dirpath + "/" + filename_prefix + filename_without_suffix + ".h";
  61.     std::string resource_cpp_filepath = resource_dirpath + "/" + filename_prefix + filename_without_suffix + ".cpp";
  62.     std::string buffer; // 用来存储资源文件的内容
  63.     std::string c_variable_name = filename_without_suffix; // 用这个变量来存储编码后的资源文件内容

  64.     fprintf(stdout, "h file: %s\n", resource_h_filepath.c_str());
  65.     fprintf(stdout, "cpp file: %s\n", resource_cpp_filepath.c_str());
  66.     fprintf(stdout, "variable name: %s\n", c_variable_name.c_str());

  67.     // 输入参数检查,
  68.     // 要求带一个参数
  69.     if ((argc != 2) && (argc != 3))
  70.     {
  71.         fprintf(stderr, "usage: %s resouce_filepath \n", basename(argv[0]));
  72.         exit(1);
  73.     }

  74.     if (!file2string(resource_filepath, &buffer))
  75.     {
  76.         exit(1);
  77.     }

  78.     if (!write_h_file(resource_h_filepath, c_variable_name))
  79.     {
  80.         exit(1);
  81.     }

  82.     if (!write_cpp_file(resource_cpp_filepath, c_variable_name, buffer))
  83.     {
  84.         exit(1);
  85.     }

  86.     return 0;
  87. }

  88. std::string extract_dirpath_without_slash(const std::string& filepath)
  89. {
  90.     char* tmp = strdup(filepath.c_str());
  91.     std::string dirpath_without_slash = dirname(tmp);

  92.     free(tmp);
  93.     return dirpath_without_slash;
  94. }

  95. std::string extract_filename_without_suffix(const std::string& filepath)
  96. {
  97.     char* tmp = strdup(filepath.c_str());

  98.     // 去掉目录部分
  99.     std::string filename = basename(tmp);
  100.     std::string::size_type dot_pos = filename.rfind('.');

  101.     free(tmp);
  102.     return (std::string::npos == dot_pos)? filename: filename.substr(0, dot_pos);
  103. }

  104. bool file2string(const std::string& filepath, std::string* buffer)
  105. {
  106.     std::ifstream fs(filepath.c_str());
  107.     if (!fs)
  108.     {
  109.         fprintf(stderr, "open %s error: %m\n", filepath.c_str());
  110.         return false;
  111.     }

  112.     // 得到文件大小
  113.     fs.seekg(0, std::ifstream::end);
  114.     std::streamoff file_size = fs.tellg();

  115.     // 调整buffer大小
  116.     buffer->resize(static_caststd::string::size_type>(file_size + 1));
  117.     (*buffer)[file_size] = '\0';

  118.     // 将整个文件读到buffer中
  119.     fs.seekg(0, std::ifstream::beg);
  120.     fs.read(const_castchar*>(buffer->data()), file_size);

  121.     return true;
  122. }

  123. bool write_h_file(const std::string& resource_h_filepath, const std::string& c_variable_name)
  124. {
  125.     return true;
  126. }

  127. bool write_cpp_file(const std::string& resource_cpp_filepath, const std::string& c_variable_name, const std::string& buffer)
  128. {
  129.     std::string::size_type i = 0;
  130.     std::ofstream fs(resource_cpp_filepath.c_str());

  131.     if (!fs)
  132.     {
  133.         fprintf(stderr, "open %s error: %m\n", resource_cpp_filepath.c_str());
  134.         return false;
  135.     }

  136.     // 写文件头
  137.     fs "// DO NOT EDIT!!!" std::endl;
  138.     fs "// this file is auto generated by resource_maker" std::endl;
  139.     fs "// edit the generator if necessary" std::endl;
  140.     fs "#include // size_t" std::endl;
  141.     fs std::endl;
  142.     fs "namespace resource { // namespace resource BEGIN" std::endl;
  143.     fs std::endl;

  144.     // 记得减去结尾符
  145.     fs " " "size_t " c_variable_name "_size = " buffer.size() - 1 ";" std::endl;
  146.     fs " " "unsigned char " c_variable_name "[] = {" std::endl;

  147.     while (true)
  148.     {
  149.         for (int j=0; j16 && ibuffer.size()-1; ++j, ++i)
  150.         {
  151.             if (0 == j)
  152.             {
  153.                 fs " ";
  154.                 fs " ";
  155.             }

  156.             fs dec2hex(static_castunsigned char>(buffer[i]));
  157.             if (i buffer.size() - 2)
  158.                 fs ",";
  159.         }

  160.         fs std::endl;
  161.         if (i == buffer.size()-1)
  162.         {
  163.             break;
  164.         }
  165.     }

  166.     // 缩进4个空格
  167.     fs " " "};" std::endl;

  168.     // 写文件尾巴
  169.     fs std::endl;
  170.     fs "} // namespace resouce END" std::endl;
  171.     return true;
  172. }

  173. std::string dec2hex(unsigned char c)
  174. {
  175.     char buf[2+2+1]; // 第一个2为前缀0x,第二个2为内容,第三个1为结尾符
  176.     snprintf(buf, sizeof(buf), "0x%02x", c); // 注意c类型如果为char,则需要强制转换成unsigned类型
  177.     return buf;
  178. }

相关文章
|
18天前
|
自然语言处理 编译器 C语言
为什么C/C++编译腰要先完成汇编
C/C++ 编译过程中先生成汇编语言是历史、技术和实践的共同选择。历史上,汇编语言作为成熟的中间表示方式,简化了工具链;技术上,分阶段编译更高效,汇编便于调试和移植;实践中,保留汇编阶段降低了复杂度,增强了可移植性和优化能力。即使在现代编译器中,汇编仍作为重要桥梁,帮助开发者更好地理解和优化代码。
46 25
为什么C/C++编译腰要先完成汇编
|
1月前
|
存储 算法 安全
基于哈希表的文件共享平台 C++ 算法实现与分析
在数字化时代,文件共享平台不可或缺。本文探讨哈希表在文件共享中的应用,包括原理、优势及C++实现。哈希表通过键值对快速访问文件元数据(如文件名、大小、位置等),查找时间复杂度为O(1),显著提升查找速度和用户体验。代码示例展示了文件上传和搜索功能,实际应用中需解决哈希冲突、动态扩容和线程安全等问题,以优化性能。
|
3月前
|
自然语言处理 编译器 Linux
|
3月前
|
算法 安全 C++
提高C/C++代码的可读性
提高C/C++代码的可读性
83 4
|
3月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
239 4
|
3月前
|
自然语言处理 编译器 Linux
告别头文件,编译效率提升 42%!C++ Modules 实战解析 | 干货推荐
本文中,阿里云智能集团开发工程师李泽政以 Alinux 为操作环境,讲解模块相比传统头文件有哪些优势,并通过若干个例子,学习如何组织一个 C++ 模块工程并使用模块封装第三方库或是改造现有的项目。
|
4月前
|
Linux C++
Linux c/c++文件的基本操作
在Linux环境下使用C/C++进行文件的基本操作,包括文件的创建、写入、读取、关闭以及文件描述符的定位。
45 0
Linux c/c++文件的基本操作
|
4月前
|
Linux C++
Linux c/c++文件虚拟内存映射
这篇文章介绍了在Linux环境下,如何使用虚拟内存映射技术来提高文件读写的速度,并通过C/C++代码示例展示了文件映射的整个流程。
90 0
|
4月前
|
Linux C++
Linux c/c++文件移动
这篇文章介绍了在Linux环境下,使用C/C++语言通过命令方式和文件操作方式实现文件移动的方法。
120 0
|
1月前
|
C++ 芯片
【C++面向对象——类与对象】Computer类(头歌实践教学平台习题)【合集】
声明一个简单的Computer类,含有数据成员芯片(cpu)、内存(ram)、光驱(cdrom)等等,以及两个公有成员函数run、stop。只能在类的内部访问。这是一种数据隐藏的机制,用于保护类的数据不被外部随意修改。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。成员可以在派生类(继承该类的子类)中访问。成员,在类的外部不能直接访问。可以在类的外部直接访问。为了完成本关任务,你需要掌握。
68 19