resource_embedder/resource_embedder.c
2024-08-07 18:03:40 +03:00

297 lines
9.5 KiB
C

#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
// the program writes generated text to stdout
// other messages must be sent to stderr
#define printf USE_FPRINTF!!!
#define exit_with_error(FORMAT, ARGS...) ({ \
fprintf(stderr, "\e[91m\nerror: "FORMAT"\n", ## ARGS); \
exit(4); \
})
#define argis(STR) streq(arg, STR) || \
streq(arg, "-"STR) || \
streq(arg, "--"STR)
////////////////////////////////////////////////
// STR FUNCTIONS //
////////////////////////////////////////////////
bool streq(const char* key0,const char* key1){
if(!key0) return key1 ? false : true;
if(!key1) return false;
while(*key0&&*key1)
if(*key0++ != *key1++)
return false;
return true;
}
char* basedir_exclude(const char* path, const char* basename){
int len=strlen(path);
int base_len=strlen(basename);
int i=0;
if(len>0 && base_len>0){
// remove trailing path separator
if(basename[base_len-1]=='/' || basename[base_len-1]=='\\')
base_len--;
if(len>base_len){
while(i<base_len){
if(path[i]!=basename[i])
break;
i++;
}
// skip leading path separator
if(path[i]=='/' || path[i]=='\\')
i++;
}
}
int rzlt_len=len-i;
char* rzlt=malloc(rzlt_len+1);
memcpy(rzlt, path+i, rzlt_len);
rzlt[rzlt_len]=0;
fprintf(stderr, "path without base: %s\n", rzlt);
return rzlt;
}
///@returns ptr to string of <count> spaces
char* mult_space(int count){
char* _mult_space_buf=malloc(count+1);
_mult_space_buf[count]=0;
memset(_mult_space_buf, ' ', count);
return _mult_space_buf;
}
void print_line(bool print_lines, bool start_new_line){
if(!print_lines)
return;
fflush(stdout);
if(start_new_line)
fprintf(stderr, "\n");
fprintf(stderr, "------------------------------------------------------------\n");
fflush(stderr);
}
////////////////////////////////////////////////
// WRITE CODE //
////////////////////////////////////////////////
bool _first_file_processed=false;
unsigned int _input_files_n=0;
void write_header(FILE* out_file, const char* out_file_path){
int onml=strlen(out_file_path);
if(onml>47)
onml=0;
char* _sp1=mult_space(47-onml);
fprintf(out_file,
"////////////////////////////////////////////////////////////////\n"
"// This file was generated by resource_embedder //\n"
"// https://timerix.ddns.net:3322/Timerix/resource_embedder //\n"
"////////////////////////////////////////////////////////////////\n"
"// USAGE: //\n"
"// Put it in a SOURCE file to define variables //\n"
"// #define EMBEDDED_RESOURCE_DEFINITION //\n"
"// #define EMBEDDED_RESOURCE_POSTFIX your_postfix //\n"
"// #include \"%s\"%s//\n"
"// //\n"
"// Put it in a HEADER file to declare external variables //\n"
"// #define EMBEDDED_RESOURCE_POSTFIX your_postfix //\n"
"// #include \"%s\"%s//\n"
"// //\n"
"// Then you can access embedded files through //\n"
"// EmbeddedResource_table_your_postfix. You can get table //\n"
"// content by index and put it into a hashtable or a map. //\n"
"////////////////////////////////////////////////////////////////\n"
"\n",
out_file_path, _sp1,
out_file_path, _sp1);
free(_sp1);
fprintf(out_file,
"#pragma once\n"
"#if __cplusplus\n"
"extern \"C\" {\n"
"#endif\n"
"\n"
"#include <stdint.h>\n"
"#include <stddef.h>\n"
"\n"
"typedef struct {\n"
" const char* path;\n"
" const char* data;\n"
" size_t size;\n"
"} EmbeddedResource;\n"
"\n"
"#define RSCAT(A,B,C...) A##B##C"
"\n"
"#ifdef EMBEDDED_RESOURCE_POSTFIX\n"
" #define _EmbeddedResource_table(P) \\\n"
" RSCAT(EmbeddedResource_table_, P)\n"
" #define _EmbeddedResource_table_count(P) \\\n"
" RSCAT(EmbeddedResource_table_, P, _count)\n"
"#else\n"
" #define _EmbeddedResource_table(P) \\\n"
" EmbeddedResource_table\n"
" #define _EmbeddedResource_table_count(P) \\\n"
" EmbeddedResource_table_count\n"
"#endif\n"
"extern const EmbeddedResource _EmbeddedResource_table(EMBEDDED_RESOURCE_POSTFIX)[];\n"
"extern const int _EmbeddedResource_table_count(EMBEDDED_RESOURCE_POSTFIX);\n"
"\n"
"#ifdef EMBEDDED_RESOURCE_DEFINITION\n"
"const EmbeddedResource _EmbeddedResource_table(EMBEDDED_RESOURCE_POSTFIX)[]={\n"
);
}
void write_footer(FILE* out_file){
fprintf(out_file,
"\n};\n\n"
"const int _EmbeddedResource_table_count(EMBEDDED_RESOURCE_POSTFIX)=%u;\n"
"#endif // EMBEDDED_RESOURCE_DEFINITION\n"
"\n"
"#undef _EmbeddedResource_table\n"
"#undef _EmbeddedResource_table_count\n"
"#undef EMBEDDED_RESOURCE_POSTFIX\n"
"#undef RSCAT\n"
"\n"
"#if __cplusplus\n"
"}\n"
"#endif\n",
_input_files_n
);
}
void process_file(const char* input_file_path,
const char* embedded_file_path,
FILE* out_file,
const char* out_file_path)
{
bool print_lines=streq(out_file_path, "stdout");
fprintf(stderr, "reading file %s\n", input_file_path);
print_line(print_lines, false);
FILE* input_file=fopen(input_file_path, "rb");
if(input_file==NULL)
exit_with_error("can't open file %s", input_file_path);
if(_first_file_processed)
fprintf(out_file, ",\n");
// writing input file code header
fprintf(out_file,
" {\n"
" .path=\"%s\",\n"
" .data=(const char[]){",
embedded_file_path);
// writing input file content
int byte=0;
long long unsigned int size=0;
while( (byte=fgetc(input_file)) != EOF ){
if(size%16==0)
fprintf(out_file, "\n ");
fprintf(out_file, "0x%02X,", byte);
size++;
}
// trailing zero to use file.data as cstring
fprintf(out_file, "0x0");
fprintf(out_file,
"\n },\n"
" .size=%llu\n"
" }",
size);
fflush(out_file);
print_line(print_lines,true);
if(ferror(input_file)){
perror("unexpected end of stream: ");
exit_with_error("input read error");
}
fclose(input_file);
fprintf(stderr, "read %llu bytes\n", size);
_first_file_processed=true;
_input_files_n++;
}
////////////////////////////////////////////////
// MAIN //
////////////////////////////////////////////////
int main(const int argc, const char * const* argv){
FILE* out_file=stdout;
const char* out_file_path="stdout";
const char* basedir="";
for(int i=1; i<argc; i++){
const char* arg=argv[i];
print_line(true, false);
fprintf(stderr, "arg: %s\n", arg);
if(argis("h") || argis("help")){
fprintf(stderr, "Usage: resource_embedder [options] [file_options] -i input_file [[file_options] -i input_file]...\n"
"Options:\n"
" -o output file path (default=stdout)\n"
"File options:\n"
" -d base dir path that will be removed from compiled strings\n");
return 0;
}
else if(argis("o")){
if (i + 1 >= argc)
exit_with_error("expected an argument after -o");
const char* file_path=argv[++i];
out_file_path=file_path;
out_file=fopen(out_file_path, "wb");
if(out_file==NULL)
exit_with_error("can't open file %s", out_file_path);
fprintf(stderr, "output file has been set to %s\n", out_file_path);
bool print_lines=streq(out_file_path, "stdout");
print_line(print_lines, false);
write_header(out_file, out_file_path);
print_line(print_lines, false);
}
else if(argis("d")){
if (i + 1 >= argc)
exit_with_error("expected an argument after -d");
basedir=argv[++i];
fprintf(stderr, "basedir is set to: %s\n", basedir);
}
else if(argis("i")){
if (i + 1 >= argc)
exit_with_error("expected an argument after -i");
const char* input_file_path=argv[++i];
char* embedded_path=basedir_exclude(input_file_path, basedir);
fprintf(stderr, "input file path: %s\n", arg);
fprintf(stderr, "embedded path: %s\n", embedded_path);
process_file(input_file_path, embedded_path, out_file, out_file_path);
free(embedded_path);
}
else exit_with_error("unknown argument: %s", arg);
}
if(out_file_path!=NULL){
bool out_file_set=!streq(out_file_path, "stdout");
if(!out_file_set)
print_line(true, false);
write_footer(out_file);
if(!out_file_set)
fclose(out_file);
print_line(true, false);
}
return 0;
}