animlist support

main
Dominik Madarász 2023-08-26 01:32:43 +02:00
parent 99d6884984
commit 380732c4ad
8 changed files with 982 additions and 889 deletions

View File

@ -15418,8 +15418,8 @@ API const char * xml_string(char *key);
API unsigned xml_count(char *key); API unsigned xml_count(char *key);
API array(char) xml_blob(char *key); API array(char) xml_blob(char *key);
#define xml_string(...) xml_string(va(__VA_ARGS__)) // syntax sugar: string #define xml_string(...) xml_string(va(__VA_ARGS__)) // syntax sugar: string
#define xml_int(...) atoi(xml_string(va(__VA_ARGS__))) // syntax sugar: int #define xml_int(...) atoi(xml_string(__VA_ARGS__)) // syntax sugar: int
#define xml_float(...) atof(xml_string(va(__VA_ARGS__))) // syntax sugar: float #define xml_float(...) atof(xml_string(__VA_ARGS__)) // syntax sugar: float
#define xml_blob(...) xml_blob(va(__VA_ARGS__)) // syntax sugar: base64 blob #define xml_blob(...) xml_blob(va(__VA_ARGS__)) // syntax sugar: base64 blob
#define xml_count(...) xml_count(va(__VA_ARGS__)) // syntax sugar: count nodes #define xml_count(...) xml_count(va(__VA_ARGS__)) // syntax sugar: count nodes
API void xml_pop(); API void xml_pop();
@ -331929,31 +331929,42 @@ const char *COOK_INI = "tools/cook.ini";
static unsigned ART_SKIP_ROOT; // number of chars to skip the base root in ART folder static unsigned ART_SKIP_ROOT; // number of chars to skip the base root in ART folder
static unsigned ART_LEN; // dupe static unsigned ART_LEN; // dupe
typedef struct cook_script_t { typedef struct cook_subscript_t {
char *infile; // free after use char *infile;
char *finalfile; // free after use. can be either infile or a totally different file char *outfile; // can be either infile, or a totally different file
char *script; char *script;
char *outname;
int compress_level; int compress_level;
} cook_subscript_t;
typedef struct cook_script_t {
cook_subscript_t cs[8];
int num_passes;
} cook_script_t; } cook_script_t;
static static
cook_script_t cook_script(const char *rules, const char *infile, const char *outfile) { cook_script_t cook_script(const char *rules, const char *infile, const char *outfile) {
cook_script_t mcs = { 0 };
// pass loop: some asset rules may require multiple cook passes
for( int pass = 0; pass < countof(mcs.cs); ++pass ) {
// by default, assume: // by default, assume:
// - no script is going to be generated (empty script) // - no script is going to be generated (empty script)
// - if no script is going to be generated, output is in fact input file. // - if no script is going to be generated, output is in fact input file.
// - no compression is going to be required. // - no compression is going to be required.
cook_script_t cs = { 0 }; cook_subscript_t cs = { 0 };
// reuse script heap from last call if possible (optimization) // reuse script heap from last call if possible (optimization)
static __thread char *script = 0; static __thread char *script = 0;
if(script) script[0] = 0; if(script) script[0] = 0;
// reuse parsing maps if possible (optimization) // reuse parsing maps if possible (optimization)
static __thread map(char*, char*) symbols = 0; static __thread map(char*, char*) symbols = 0; if(!symbols) map_init_str(symbols);
static __thread map(char*, char*) groups = 0; static __thread map(char*, char*) groups = 0; if(!groups) map_init_str(groups);
static __thread set(char*) passes = 0; if(!passes) set_init_str(passes);
if(!symbols) map_init(symbols, less_str, hash_str); map_clear(symbols);
if(!groups) map_init(groups, less_str, hash_str); map_clear(groups);
map_find_or_add(symbols, "INFILE", STRDUP(infile)); map_find_or_add(symbols, "INFILE", STRDUP(infile));
map_find_or_add(symbols, "INPUT", STRDUP(infile)); map_find_or_add(symbols, "INPUT", STRDUP(infile));
@ -331963,6 +331974,9 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
map_find_or_add(symbols, "EDITOR", STRDUP(EDITOR)); map_find_or_add(symbols, "EDITOR", STRDUP(EDITOR));
map_find_or_add(symbols, "PROGRESS", STRDUP(va("%03d", cook_progress()))); map_find_or_add(symbols, "PROGRESS", STRDUP(va("%03d", cook_progress())));
// clear pass counter
set_clear(passes);
// start parsing. parsing is enabled by default // start parsing. parsing is enabled by default
int enabled = 1; int enabled = 1;
array(char*)lines = strsplit(rules, "\r\n"); array(char*)lines = strsplit(rules, "\r\n");
@ -331997,11 +332011,11 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
if( eos ) *eos = 0; if( eos ) *eos = 0;
// replace all symbols // replace all symbols
char* nl = STRDUP(line); char* nl = STRDUP(line); // @leak
for each_map(symbols, char*, key, char*, val) { for each_map(symbols, char*, key, char*, val) {
strrepl(&nl, key, val); strrepl(&nl, key, val);
} }
lines[i] = line = nl; // @fixme:leak lines[i] = line = nl;
static thread_mutex_t lock, *init = 0; if(!init) thread_mutex_init(init = &lock); static thread_mutex_t lock, *init = 0; if(!init) thread_mutex_init(init = &lock);
thread_mutex_lock( &lock ); thread_mutex_lock( &lock );
@ -332015,9 +332029,9 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
enabled = 1; enabled = 1;
int is_cook = !!strstr(line, "[cook]"); int is_cook = !!strstr(line, "[cook]");
int is_compress = !!strstr(line, "[compress]"); int is_compress = !!strstr(line, "[compress]");
if( !is_cook && !is_compress ) { if( !is_cook && !is_compress ) { // if not a special section...
// remove hint cook tag if present. that's informative only. // remove hint cook tag if present. that's informative only.
if(strbegi(line, "[cook ") ) memcpy(line+1, " ", 4); if(strbegi(line, "[cook ") ) memcpy(line+1, " ", 4); // line += 6;
// start parsing expressions like `[media && !avi && mp3]` // start parsing expressions like `[media && !avi && mp3]`
array(char*) tags = strsplit(line, " []&"); array(char*) tags = strsplit(line, " []&");
@ -332026,8 +332040,9 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
char **INPUT = map_find(symbols, "INPUT"); char **INPUT = map_find(symbols, "INPUT");
bool found_in_set = true; bool found_in_set = true;
for( int i = 0, end = array_count(tags); i < end; ++i) { char *tag = tags[i]; for( int i = 0, end = array_count(tags); i < end; ++i) {
bool negate = false; bool negate = false;
char *tag = tags[i];
while(*tag == '!') negate ^= 1, ++tag; while(*tag == '!') negate ^= 1, ++tag;
// find tag in groups map // find tag in groups map
@ -332037,7 +332052,7 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
char *list = *is_group; char *list = *is_group;
char *INPUT_EXT = file_ext(infile); INPUT_EXT = strrchr(INPUT_EXT, '.'); // .ext1.ext -> .ext char *INPUT_EXT = file_ext(infile); INPUT_EXT = strrchr(INPUT_EXT, '.'); // .ext1.ext -> .ext
char *ext = INPUT_EXT; ext += ext[0] == '.'; // dotless char *ext = INPUT_EXT; ext += ext[0] == '.'; // dotless
bool in_list = strbegi(list, ext) || strstri(list, va(",%s,",ext)) || strendi(list, va(",%s",ext)); bool in_list = strbegi(list, ext) || strendi(list, va(",%s",ext)) || strstri(list, va(",%s,",ext));
if( !in_list ^ negate ) { found_in_set = false; break; } if( !in_list ^ negate ) { found_in_set = false; break; }
} else { } else {
char *ext = va(".%s", tag); char *ext = va(".%s", tag);
@ -332045,6 +332060,13 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
if( !found ^ negate ) { found_in_set = false; break; } if( !found ^ negate ) { found_in_set = false; break; }
} }
} }
if( found_in_set ) {
// inc pass
set_find_or_add(passes, STRDUP(*tags)); // @leak
// check whether we keep searching
int num_passes = set_count(passes);
found_in_set = ( pass == (num_passes-1) );
}
// //
enabled = found_in_set ? 1 : 0; enabled = found_in_set ? 1 : 0;
} }
@ -332085,20 +332107,26 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
int errorlevel = has_errorlevel ? atoi(has_errorlevel + 2) : 0; int errorlevel = has_errorlevel ? atoi(has_errorlevel + 2) : 0;
if( has_errorlevel ) memcpy(has_errorlevel, " ", 3); if( has_errorlevel ) memcpy(has_errorlevel, " ", 3);
// detect if newer extension is present, and thus update OUTPUT if needed // detect if newer extension or filename is present, and thus update OUTPUT if needed
char *newer_extension = strstr(line, "->"); if(newer_extension) { char *newer_extension = strstr(line, "->"); if(newer_extension) {
*newer_extension = 0; *newer_extension = 0;
newer_extension += 2 + strspn(newer_extension + 2, " "); newer_extension += 2 + strspn(newer_extension + 2, " ");
if( strchr(newer_extension, '.') ) {
// newer filename
cs.outname = stringf("%s@%s", cs.outname ? cs.outname : infile, newer_extension); // @leak
newer_extension = NULL;
} else {
strcatf(&*OUTPUT, ".%s", newer_extension); strcatf(&*OUTPUT, ".%s", newer_extension);
} }
}
// replace all symbols // replace all symbols
char* nl = STRDUP(line); char* nl = STRDUP(line); // @leak
for each_map(symbols, char*, key, char*, val) { for each_map(symbols, char*, key, char*, val) {
strrepl(&nl, key, val); strrepl(&nl, key, val);
} }
lines[i] = line = nl; // @fixme:leak lines[i] = line = nl;
// convert slashes // convert slashes
ifdef(win32, ifdef(win32,
@ -332123,43 +332151,41 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
} }
} }
// compression
char* ext = file_ext(infile); ext = strrchr(ext, '.'); ext += ext[0] == '.'; // dotless INPUT_EXT
char** OUTPUT = map_find(symbols, "OUTPUT"); char** OUTPUT = map_find(symbols, "OUTPUT");
int ext_num_groups = 0;
// compression
if( 1 ) {
char* ext = file_ext(infile); ext = strrchr(ext, '.'); ext += ext[0] == '.'; // dotless INPUT_EXT
char* belongs_to = 0; char* belongs_to = 0;
for each_map(groups, char*, key, char*, val) { for each_map(groups, char*, key, char*, val) {
if( !isdigit(key[0]) ) { if( !isdigit(key[0]) ) {
char *comma = va(",%s,", ext); char *comma = va(",%s,", ext);
if( strbegi(val, comma+1) || strstri(val, comma) || strendi(val, va(",%s", ext))) { if( !strcmpi(val,ext) || strbegi(val, comma+1) || strstri(val, comma) || strendi(val, va(",%s", ext))) {
belongs_to = key; belongs_to = key;
//goto break1; // each_map() macro is made of multiple for(;;)s. goto needed; you cant escape with single break. ext_num_groups++;
} }
} }
} }
break1:;
char *compression = 0; char *compression = 0;
for each_map(groups, char*, key, char*, val) { for each_map(groups, char*, key, char*, val) {
if( isdigit(key[0]) ) { if( isdigit(key[0]) ) {
char *comma = va(",%s,", ext); char *comma = va(",%s,", ext);
if( strbegi(val, comma+1) || strstri(val, comma) || strendi(val, va(",%s", ext))) { if( !strcmpi(val,ext) || strbegi(val, comma+1) || strstri(val, comma) || strendi(val, va(",%s", ext))) {
compression = key; compression = key;
//goto break2; // each_map() macro is made of multiple for(;;)s. goto needed; you cant escape with single break.
} }
comma = va(",%s,", belongs_to); comma = va(",%s,", belongs_to);
if( strbegi(val, comma+1) || strstri(val, comma) || strendi(val, va(",%s", ext))) { if( !strcmpi(val,ext) || strbegi(val, comma+1) || strstri(val, comma) || strendi(val, va(",%s", ext))) {
compression = key; compression = key;
//goto break2; // each_map() macro is made of multiple for(;;)s. goto needed; you cant escape with single break.
} }
} }
} }
break2:;
cs.compress_level = 0; cs.compress_level = 0;
if( compression ) { if( compression ) {
// last chance to optionally override the compressor at command-line level // last chance to optionally override the compressor at command-line level
static const char *compressor_override, **init = 0; static const char *compressor_override;
if( !init ) *(init = &compressor_override) = option("--cook-compressor", ""); do_once compressor_override = option("--cook-compressor", "");
if( compressor_override[0] ) compression = (char*)compressor_override; if( compressor_override[0] ) compression = (char*)compressor_override;
/**/ if(strstri(compression, "PPP")) cs.compress_level = atoi(compression) | PPP; /**/ if(strstri(compression, "PPP")) cs.compress_level = atoi(compression) | PPP;
@ -332175,11 +332201,12 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
else if(strstri(compression, "BCM")) cs.compress_level = atoi(compression) | BCM; else if(strstri(compression, "BCM")) cs.compress_level = atoi(compression) | BCM;
else cs.compress_level = isdigit(compression[0]) ? atoi(compression) : 6 /*| DEFL*/; else cs.compress_level = isdigit(compression[0]) ? atoi(compression) : 6 /*| DEFL*/;
} }
}
// if script was generated... // if script was generated...
if( script && script[0]) { if( script && script[0]) {
// update outfile // update outfile
cs.finalfile = *OUTPUT; cs.outfile = *OUTPUT;
// amalgamate script // amalgamate script
array(char*) lines = strsplit(script, "\r\n"); array(char*) lines = strsplit(script, "\r\n");
@ -332202,15 +332229,22 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
} else { } else {
// ... else bypass infile->outfile // ... else bypass infile->outfile
char** INFILE = map_find(symbols, "INFILE"); char** INFILE = map_find(symbols, "INFILE");
cs.finalfile = *INFILE; cs.outfile = *INFILE;
// and return an empty script // and return an empty script
cs.script = ""; cs.script = "";
} }
map_clear(symbols); cs.outname = cs.outname ? cs.outname : (char*)infile;
map_clear(groups);
return cs; ASSERT(mcs.num_passes < countof(mcs.cs));
mcs.cs[mcs.num_passes++] = cs;
bool next_pass_required = mcs.num_passes < ext_num_groups;
if( !next_pass_required ) break;
}
return mcs;
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -332372,56 +332406,53 @@ int cook(void *userdata) {
*progress = ((i+1) == end ? 90 : (i * 90) / end); // (i+i>0) * 100.f / end; *progress = ((i+1) == end ? 90 : (i * 90) / end); // (i+i>0) * 100.f / end;
// start cook // start cook
const char *fname = uncooked[i]; //job->files[j]; const char *infile = uncooked[i]; //job->files[j];
int inlen = file_size(fname); int inlen = file_size(infile);
// generate a cooking script for this asset // generate a cooking script for this asset
cook_script_t cs = cook_script(job->rules, fname, COOK_TMPFILE); cook_script_t mcs = cook_script(job->rules, infile, COOK_TMPFILE);
// puts(cs.script); // puts(cs.script);
for(int pass = 0; pass < mcs.num_passes; ++pass) {
cook_subscript_t cs = mcs.cs[pass];
// log to batch file for forensic purposes, if explicitly requested // log to batch file for forensic purposes, if explicitly requested
static __thread bool logging = 0, *init = 0; if(!init) *(init = &logging) = !!flag("--cook-debug") || cook_debug; static __thread bool logging = 0; do_once logging = !!flag("--cook-debug") || cook_debug;
if( logging ) { if( logging ) {
FILE *logfile = fopen(va("cook%d.cmd",job->threadid), "a+t"); FILE *logfile = fopen(va("cook%d.cmd",job->threadid), "a+t");
if( logfile ) { fprintf(logfile, "@rem %s\n%s\n", fname, cs.script); fclose(logfile); } if( logfile ) { fprintf(logfile, "@rem %s\n%s\n", cs.outname, cs.script); fclose(logfile); }
// maybe log fprintf(logfile, "@rem %*.s\n", 4096, app_exec_output()); ? fprintf(stderr, "%s\n", cs.script);
} }
// invoke cooking script and recap status // invoke cooking script and recap status
const char *rcout = app_exec(cs.script); const char *rc_output = app_exec(cs.script);
int rc = atoi(rcout); int rc = atoi(rc_output);
int outlen = file_size(cs.finalfile); int outlen = file_size(cs.outfile);
int failed = cs.script[0] ? rc || !outlen : 0; int failed = cs.script[0] ? rc || !outlen : 0;
// print errors, or... // print errors, or...
if( failed ) { if( failed ) {
PRINTF("Import failed: %s while executing:\n%s\nReturned:\n%s\n", fname, cs.script, rcout); PRINTF("Import failed: %s while executing:\n%s\nReturned:\n%s\n", cs.outname, cs.script, rc_output);
} }
// ...process only if included. may include optional compression. // ...process only if included. may include optional compression.
else if( cs.compress_level >= 0 ) { else if( cs.compress_level >= 0 ) {
FILE *in = fopen(cs.finalfile, "rb"); FILE *in = fopen(cs.outfile, "rb");
#if 0 #if 0
struct stat st; stat(fname, &st); struct stat st; stat(infile, &st);
struct tm *timeinfo = localtime(&st.st_mtime); struct tm *timeinfo = localtime(&st.st_mtime);
ASSERT(timeinfo); ASSERT(timeinfo);
// pretty (truncated) input (C:/prj/V4K/art/file.wav -> file.wav)
static __thread int artlen = 0; if(!artlen) artlen = strlen(ART);
const char *pretty = fname;
if( !strncmp(pretty, ART, artlen) ) pretty += artlen;
while(pretty[0] == '/') ++pretty;
fname = pretty;
//puts(fname);
#endif #endif
char *comment = va("%d", inlen); char *comment = va("%d", inlen);
if( !zip_append_file/*_timeinfo*/(z, fname, comment, in, cs.compress_level/*, timeinfo*/) ) { if( !zip_append_file/*_timeinfo*/(z, cs.outname, comment, in, cs.compress_level/*, timeinfo*/) ) {
PANIC("failed to add processed file into %s: %s", zipfile, fname); PANIC("failed to add processed file into %s: %s(%s)", zipfile, cs.outname, infile);
} }
fclose(in); fclose(in);
} }
}
} }
zip_close(z); zip_close(z);

View File

@ -17,31 +17,42 @@ const char *COOK_INI = "tools/cook.ini";
static unsigned ART_SKIP_ROOT; // number of chars to skip the base root in ART folder static unsigned ART_SKIP_ROOT; // number of chars to skip the base root in ART folder
static unsigned ART_LEN; // dupe static unsigned ART_LEN; // dupe
typedef struct cook_script_t { typedef struct cook_subscript_t {
char *infile; // free after use char *infile;
char *finalfile; // free after use. can be either infile or a totally different file char *outfile; // can be either infile, or a totally different file
char *script; char *script;
char *outname;
int compress_level; int compress_level;
} cook_subscript_t;
typedef struct cook_script_t {
cook_subscript_t cs[8];
int num_passes;
} cook_script_t; } cook_script_t;
static static
cook_script_t cook_script(const char *rules, const char *infile, const char *outfile) { cook_script_t cook_script(const char *rules, const char *infile, const char *outfile) {
cook_script_t mcs = { 0 };
// pass loop: some asset rules may require multiple cook passes
for( int pass = 0; pass < countof(mcs.cs); ++pass ) {
// by default, assume: // by default, assume:
// - no script is going to be generated (empty script) // - no script is going to be generated (empty script)
// - if no script is going to be generated, output is in fact input file. // - if no script is going to be generated, output is in fact input file.
// - no compression is going to be required. // - no compression is going to be required.
cook_script_t cs = { 0 }; cook_subscript_t cs = { 0 };
// reuse script heap from last call if possible (optimization) // reuse script heap from last call if possible (optimization)
static __thread char *script = 0; static __thread char *script = 0;
if(script) script[0] = 0; if(script) script[0] = 0;
// reuse parsing maps if possible (optimization) // reuse parsing maps if possible (optimization)
static __thread map(char*, char*) symbols = 0; static __thread map(char*, char*) symbols = 0; if(!symbols) map_init_str(symbols);
static __thread map(char*, char*) groups = 0; static __thread map(char*, char*) groups = 0; if(!groups) map_init_str(groups);
static __thread set(char*) passes = 0; if(!passes) set_init_str(passes);
if(!symbols) map_init(symbols, less_str, hash_str); map_clear(symbols);
if(!groups) map_init(groups, less_str, hash_str); map_clear(groups);
map_find_or_add(symbols, "INFILE", STRDUP(infile)); map_find_or_add(symbols, "INFILE", STRDUP(infile));
map_find_or_add(symbols, "INPUT", STRDUP(infile)); map_find_or_add(symbols, "INPUT", STRDUP(infile));
@ -51,6 +62,9 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
map_find_or_add(symbols, "EDITOR", STRDUP(EDITOR)); map_find_or_add(symbols, "EDITOR", STRDUP(EDITOR));
map_find_or_add(symbols, "PROGRESS", STRDUP(va("%03d", cook_progress()))); map_find_or_add(symbols, "PROGRESS", STRDUP(va("%03d", cook_progress())));
// clear pass counter
set_clear(passes);
// start parsing. parsing is enabled by default // start parsing. parsing is enabled by default
int enabled = 1; int enabled = 1;
array(char*)lines = strsplit(rules, "\r\n"); array(char*)lines = strsplit(rules, "\r\n");
@ -85,11 +99,11 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
if( eos ) *eos = 0; if( eos ) *eos = 0;
// replace all symbols // replace all symbols
char* nl = STRDUP(line); char* nl = STRDUP(line); // @leak
for each_map(symbols, char*, key, char*, val) { for each_map(symbols, char*, key, char*, val) {
strrepl(&nl, key, val); strrepl(&nl, key, val);
} }
lines[i] = line = nl; // @fixme:leak lines[i] = line = nl;
static thread_mutex_t lock, *init = 0; if(!init) thread_mutex_init(init = &lock); static thread_mutex_t lock, *init = 0; if(!init) thread_mutex_init(init = &lock);
thread_mutex_lock( &lock ); thread_mutex_lock( &lock );
@ -103,9 +117,9 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
enabled = 1; enabled = 1;
int is_cook = !!strstr(line, "[cook]"); int is_cook = !!strstr(line, "[cook]");
int is_compress = !!strstr(line, "[compress]"); int is_compress = !!strstr(line, "[compress]");
if( !is_cook && !is_compress ) { if( !is_cook && !is_compress ) { // if not a special section...
// remove hint cook tag if present. that's informative only. // remove hint cook tag if present. that's informative only.
if(strbegi(line, "[cook ") ) memcpy(line+1, " ", 4); if(strbegi(line, "[cook ") ) memcpy(line+1, " ", 4); // line += 6;
// start parsing expressions like `[media && !avi && mp3]` // start parsing expressions like `[media && !avi && mp3]`
array(char*) tags = strsplit(line, " []&"); array(char*) tags = strsplit(line, " []&");
@ -114,8 +128,9 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
char **INPUT = map_find(symbols, "INPUT"); char **INPUT = map_find(symbols, "INPUT");
bool found_in_set = true; bool found_in_set = true;
for( int i = 0, end = array_count(tags); i < end; ++i) { char *tag = tags[i]; for( int i = 0, end = array_count(tags); i < end; ++i) {
bool negate = false; bool negate = false;
char *tag = tags[i];
while(*tag == '!') negate ^= 1, ++tag; while(*tag == '!') negate ^= 1, ++tag;
// find tag in groups map // find tag in groups map
@ -125,7 +140,7 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
char *list = *is_group; char *list = *is_group;
char *INPUT_EXT = file_ext(infile); INPUT_EXT = strrchr(INPUT_EXT, '.'); // .ext1.ext -> .ext char *INPUT_EXT = file_ext(infile); INPUT_EXT = strrchr(INPUT_EXT, '.'); // .ext1.ext -> .ext
char *ext = INPUT_EXT; ext += ext[0] == '.'; // dotless char *ext = INPUT_EXT; ext += ext[0] == '.'; // dotless
bool in_list = strbegi(list, ext) || strstri(list, va(",%s,",ext)) || strendi(list, va(",%s",ext)); bool in_list = strbegi(list, ext) || strendi(list, va(",%s",ext)) || strstri(list, va(",%s,",ext));
if( !in_list ^ negate ) { found_in_set = false; break; } if( !in_list ^ negate ) { found_in_set = false; break; }
} else { } else {
char *ext = va(".%s", tag); char *ext = va(".%s", tag);
@ -133,6 +148,13 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
if( !found ^ negate ) { found_in_set = false; break; } if( !found ^ negate ) { found_in_set = false; break; }
} }
} }
if( found_in_set ) {
// inc pass
set_find_or_add(passes, STRDUP(*tags)); // @leak
// check whether we keep searching
int num_passes = set_count(passes);
found_in_set = ( pass == (num_passes-1) );
}
// //
enabled = found_in_set ? 1 : 0; enabled = found_in_set ? 1 : 0;
} }
@ -173,20 +195,26 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
int errorlevel = has_errorlevel ? atoi(has_errorlevel + 2) : 0; int errorlevel = has_errorlevel ? atoi(has_errorlevel + 2) : 0;
if( has_errorlevel ) memcpy(has_errorlevel, " ", 3); if( has_errorlevel ) memcpy(has_errorlevel, " ", 3);
// detect if newer extension is present, and thus update OUTPUT if needed // detect if newer extension or filename is present, and thus update OUTPUT if needed
char *newer_extension = strstr(line, "->"); if(newer_extension) { char *newer_extension = strstr(line, "->"); if(newer_extension) {
*newer_extension = 0; *newer_extension = 0;
newer_extension += 2 + strspn(newer_extension + 2, " "); newer_extension += 2 + strspn(newer_extension + 2, " ");
if( strchr(newer_extension, '.') ) {
// newer filename
cs.outname = stringf("%s@%s", cs.outname ? cs.outname : infile, newer_extension); // @leak
newer_extension = NULL;
} else {
strcatf(&*OUTPUT, ".%s", newer_extension); strcatf(&*OUTPUT, ".%s", newer_extension);
} }
}
// replace all symbols // replace all symbols
char* nl = STRDUP(line); char* nl = STRDUP(line); // @leak
for each_map(symbols, char*, key, char*, val) { for each_map(symbols, char*, key, char*, val) {
strrepl(&nl, key, val); strrepl(&nl, key, val);
} }
lines[i] = line = nl; // @fixme:leak lines[i] = line = nl;
// convert slashes // convert slashes
ifdef(win32, ifdef(win32,
@ -211,43 +239,41 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
} }
} }
// compression
char* ext = file_ext(infile); ext = strrchr(ext, '.'); ext += ext[0] == '.'; // dotless INPUT_EXT
char** OUTPUT = map_find(symbols, "OUTPUT"); char** OUTPUT = map_find(symbols, "OUTPUT");
int ext_num_groups = 0;
// compression
if( 1 ) {
char* ext = file_ext(infile); ext = strrchr(ext, '.'); ext += ext[0] == '.'; // dotless INPUT_EXT
char* belongs_to = 0; char* belongs_to = 0;
for each_map(groups, char*, key, char*, val) { for each_map(groups, char*, key, char*, val) {
if( !isdigit(key[0]) ) { if( !isdigit(key[0]) ) {
char *comma = va(",%s,", ext); char *comma = va(",%s,", ext);
if( strbegi(val, comma+1) || strstri(val, comma) || strendi(val, va(",%s", ext))) { if( !strcmpi(val,ext) || strbegi(val, comma+1) || strstri(val, comma) || strendi(val, va(",%s", ext))) {
belongs_to = key; belongs_to = key;
//goto break1; // each_map() macro is made of multiple for(;;)s. goto needed; you cant escape with single break. ext_num_groups++;
} }
} }
} }
break1:;
char *compression = 0; char *compression = 0;
for each_map(groups, char*, key, char*, val) { for each_map(groups, char*, key, char*, val) {
if( isdigit(key[0]) ) { if( isdigit(key[0]) ) {
char *comma = va(",%s,", ext); char *comma = va(",%s,", ext);
if( strbegi(val, comma+1) || strstri(val, comma) || strendi(val, va(",%s", ext))) { if( !strcmpi(val,ext) || strbegi(val, comma+1) || strstri(val, comma) || strendi(val, va(",%s", ext))) {
compression = key; compression = key;
//goto break2; // each_map() macro is made of multiple for(;;)s. goto needed; you cant escape with single break.
} }
comma = va(",%s,", belongs_to); comma = va(",%s,", belongs_to);
if( strbegi(val, comma+1) || strstri(val, comma) || strendi(val, va(",%s", ext))) { if( !strcmpi(val,ext) || strbegi(val, comma+1) || strstri(val, comma) || strendi(val, va(",%s", ext))) {
compression = key; compression = key;
//goto break2; // each_map() macro is made of multiple for(;;)s. goto needed; you cant escape with single break.
} }
} }
} }
break2:;
cs.compress_level = 0; cs.compress_level = 0;
if( compression ) { if( compression ) {
// last chance to optionally override the compressor at command-line level // last chance to optionally override the compressor at command-line level
static const char *compressor_override, **init = 0; static const char *compressor_override;
if( !init ) *(init = &compressor_override) = option("--cook-compressor", ""); do_once compressor_override = option("--cook-compressor", "");
if( compressor_override[0] ) compression = (char*)compressor_override; if( compressor_override[0] ) compression = (char*)compressor_override;
/**/ if(strstri(compression, "PPP")) cs.compress_level = atoi(compression) | PPP; /**/ if(strstri(compression, "PPP")) cs.compress_level = atoi(compression) | PPP;
@ -263,11 +289,12 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
else if(strstri(compression, "BCM")) cs.compress_level = atoi(compression) | BCM; else if(strstri(compression, "BCM")) cs.compress_level = atoi(compression) | BCM;
else cs.compress_level = isdigit(compression[0]) ? atoi(compression) : 6 /*| DEFL*/; else cs.compress_level = isdigit(compression[0]) ? atoi(compression) : 6 /*| DEFL*/;
} }
}
// if script was generated... // if script was generated...
if( script && script[0]) { if( script && script[0]) {
// update outfile // update outfile
cs.finalfile = *OUTPUT; cs.outfile = *OUTPUT;
// amalgamate script // amalgamate script
array(char*) lines = strsplit(script, "\r\n"); array(char*) lines = strsplit(script, "\r\n");
@ -290,15 +317,22 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
} else { } else {
// ... else bypass infile->outfile // ... else bypass infile->outfile
char** INFILE = map_find(symbols, "INFILE"); char** INFILE = map_find(symbols, "INFILE");
cs.finalfile = *INFILE; cs.outfile = *INFILE;
// and return an empty script // and return an empty script
cs.script = ""; cs.script = "";
} }
map_clear(symbols); cs.outname = cs.outname ? cs.outname : (char*)infile;
map_clear(groups);
return cs; ASSERT(mcs.num_passes < countof(mcs.cs));
mcs.cs[mcs.num_passes++] = cs;
bool next_pass_required = mcs.num_passes < ext_num_groups;
if( !next_pass_required ) break;
}
return mcs;
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -460,56 +494,53 @@ int cook(void *userdata) {
*progress = ((i+1) == end ? 90 : (i * 90) / end); // (i+i>0) * 100.f / end; *progress = ((i+1) == end ? 90 : (i * 90) / end); // (i+i>0) * 100.f / end;
// start cook // start cook
const char *fname = uncooked[i]; //job->files[j]; const char *infile = uncooked[i]; //job->files[j];
int inlen = file_size(fname); int inlen = file_size(infile);
// generate a cooking script for this asset // generate a cooking script for this asset
cook_script_t cs = cook_script(job->rules, fname, COOK_TMPFILE); cook_script_t mcs = cook_script(job->rules, infile, COOK_TMPFILE);
// puts(cs.script); // puts(cs.script);
for(int pass = 0; pass < mcs.num_passes; ++pass) {
cook_subscript_t cs = mcs.cs[pass];
// log to batch file for forensic purposes, if explicitly requested // log to batch file for forensic purposes, if explicitly requested
static __thread bool logging = 0, *init = 0; if(!init) *(init = &logging) = !!flag("--cook-debug") || cook_debug; static __thread bool logging = 0; do_once logging = !!flag("--cook-debug") || cook_debug;
if( logging ) { if( logging ) {
FILE *logfile = fopen(va("cook%d.cmd",job->threadid), "a+t"); FILE *logfile = fopen(va("cook%d.cmd",job->threadid), "a+t");
if( logfile ) { fprintf(logfile, "@rem %s\n%s\n", fname, cs.script); fclose(logfile); } if( logfile ) { fprintf(logfile, "@rem %s\n%s\n", cs.outname, cs.script); fclose(logfile); }
// maybe log fprintf(logfile, "@rem %*.s\n", 4096, app_exec_output()); ? fprintf(stderr, "%s\n", cs.script);
} }
// invoke cooking script and recap status // invoke cooking script and recap status
const char *rcout = app_exec(cs.script); const char *rc_output = app_exec(cs.script);
int rc = atoi(rcout); int rc = atoi(rc_output);
int outlen = file_size(cs.finalfile); int outlen = file_size(cs.outfile);
int failed = cs.script[0] ? rc || !outlen : 0; int failed = cs.script[0] ? rc || !outlen : 0;
// print errors, or... // print errors, or...
if( failed ) { if( failed ) {
PRINTF("Import failed: %s while executing:\n%s\nReturned:\n%s\n", fname, cs.script, rcout); PRINTF("Import failed: %s while executing:\n%s\nReturned:\n%s\n", cs.outname, cs.script, rc_output);
} }
// ...process only if included. may include optional compression. // ...process only if included. may include optional compression.
else if( cs.compress_level >= 0 ) { else if( cs.compress_level >= 0 ) {
FILE *in = fopen(cs.finalfile, "rb"); FILE *in = fopen(cs.outfile, "rb");
#if 0 #if 0
struct stat st; stat(fname, &st); struct stat st; stat(infile, &st);
struct tm *timeinfo = localtime(&st.st_mtime); struct tm *timeinfo = localtime(&st.st_mtime);
ASSERT(timeinfo); ASSERT(timeinfo);
// pretty (truncated) input (C:/prj/V4K/art/file.wav -> file.wav)
static __thread int artlen = 0; if(!artlen) artlen = strlen(ART);
const char *pretty = fname;
if( !strncmp(pretty, ART, artlen) ) pretty += artlen;
while(pretty[0] == '/') ++pretty;
fname = pretty;
//puts(fname);
#endif #endif
char *comment = va("%d", inlen); char *comment = va("%d", inlen);
if( !zip_append_file/*_timeinfo*/(z, fname, comment, in, cs.compress_level/*, timeinfo*/) ) { if( !zip_append_file/*_timeinfo*/(z, cs.outname, comment, in, cs.compress_level/*, timeinfo*/) ) {
PANIC("failed to add processed file into %s: %s", zipfile, fname); PANIC("failed to add processed file into %s: %s(%s)", zipfile, cs.outname, infile);
} }
fclose(in); fclose(in);
} }
}
} }
zip_close(z); zip_close(z);

View File

@ -27,8 +27,8 @@ API const char * xml_string(char *key);
API unsigned xml_count(char *key); API unsigned xml_count(char *key);
API array(char) xml_blob(char *key); API array(char) xml_blob(char *key);
#define xml_string(...) xml_string(va(__VA_ARGS__)) // syntax sugar: string #define xml_string(...) xml_string(va(__VA_ARGS__)) // syntax sugar: string
#define xml_int(...) atoi(xml_string(va(__VA_ARGS__))) // syntax sugar: int #define xml_int(...) atoi(xml_string(__VA_ARGS__)) // syntax sugar: int
#define xml_float(...) atof(xml_string(va(__VA_ARGS__))) // syntax sugar: float #define xml_float(...) atof(xml_string(__VA_ARGS__)) // syntax sugar: float
#define xml_blob(...) xml_blob(va(__VA_ARGS__)) // syntax sugar: base64 blob #define xml_blob(...) xml_blob(va(__VA_ARGS__)) // syntax sugar: base64 blob
#define xml_count(...) xml_count(va(__VA_ARGS__)) // syntax sugar: count nodes #define xml_count(...) xml_count(va(__VA_ARGS__)) // syntax sugar: count nodes
API void xml_pop(); API void xml_pop();

View File

@ -2942,31 +2942,42 @@ const char *COOK_INI = "tools/cook.ini";
static unsigned ART_SKIP_ROOT; // number of chars to skip the base root in ART folder static unsigned ART_SKIP_ROOT; // number of chars to skip the base root in ART folder
static unsigned ART_LEN; // dupe static unsigned ART_LEN; // dupe
typedef struct cook_script_t { typedef struct cook_subscript_t {
char *infile; // free after use char *infile;
char *finalfile; // free after use. can be either infile or a totally different file char *outfile; // can be either infile, or a totally different file
char *script; char *script;
char *outname;
int compress_level; int compress_level;
} cook_subscript_t;
typedef struct cook_script_t {
cook_subscript_t cs[8];
int num_passes;
} cook_script_t; } cook_script_t;
static static
cook_script_t cook_script(const char *rules, const char *infile, const char *outfile) { cook_script_t cook_script(const char *rules, const char *infile, const char *outfile) {
cook_script_t mcs = { 0 };
// pass loop: some asset rules may require multiple cook passes
for( int pass = 0; pass < countof(mcs.cs); ++pass ) {
// by default, assume: // by default, assume:
// - no script is going to be generated (empty script) // - no script is going to be generated (empty script)
// - if no script is going to be generated, output is in fact input file. // - if no script is going to be generated, output is in fact input file.
// - no compression is going to be required. // - no compression is going to be required.
cook_script_t cs = { 0 }; cook_subscript_t cs = { 0 };
// reuse script heap from last call if possible (optimization) // reuse script heap from last call if possible (optimization)
static __thread char *script = 0; static __thread char *script = 0;
if(script) script[0] = 0; if(script) script[0] = 0;
// reuse parsing maps if possible (optimization) // reuse parsing maps if possible (optimization)
static __thread map(char*, char*) symbols = 0; static __thread map(char*, char*) symbols = 0; if(!symbols) map_init_str(symbols);
static __thread map(char*, char*) groups = 0; static __thread map(char*, char*) groups = 0; if(!groups) map_init_str(groups);
static __thread set(char*) passes = 0; if(!passes) set_init_str(passes);
if(!symbols) map_init(symbols, less_str, hash_str); map_clear(symbols);
if(!groups) map_init(groups, less_str, hash_str); map_clear(groups);
map_find_or_add(symbols, "INFILE", STRDUP(infile)); map_find_or_add(symbols, "INFILE", STRDUP(infile));
map_find_or_add(symbols, "INPUT", STRDUP(infile)); map_find_or_add(symbols, "INPUT", STRDUP(infile));
@ -2976,6 +2987,9 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
map_find_or_add(symbols, "EDITOR", STRDUP(EDITOR)); map_find_or_add(symbols, "EDITOR", STRDUP(EDITOR));
map_find_or_add(symbols, "PROGRESS", STRDUP(va("%03d", cook_progress()))); map_find_or_add(symbols, "PROGRESS", STRDUP(va("%03d", cook_progress())));
// clear pass counter
set_clear(passes);
// start parsing. parsing is enabled by default // start parsing. parsing is enabled by default
int enabled = 1; int enabled = 1;
array(char*)lines = strsplit(rules, "\r\n"); array(char*)lines = strsplit(rules, "\r\n");
@ -3010,11 +3024,11 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
if( eos ) *eos = 0; if( eos ) *eos = 0;
// replace all symbols // replace all symbols
char* nl = STRDUP(line); char* nl = STRDUP(line); // @leak
for each_map(symbols, char*, key, char*, val) { for each_map(symbols, char*, key, char*, val) {
strrepl(&nl, key, val); strrepl(&nl, key, val);
} }
lines[i] = line = nl; // @fixme:leak lines[i] = line = nl;
static thread_mutex_t lock, *init = 0; if(!init) thread_mutex_init(init = &lock); static thread_mutex_t lock, *init = 0; if(!init) thread_mutex_init(init = &lock);
thread_mutex_lock( &lock ); thread_mutex_lock( &lock );
@ -3028,9 +3042,9 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
enabled = 1; enabled = 1;
int is_cook = !!strstr(line, "[cook]"); int is_cook = !!strstr(line, "[cook]");
int is_compress = !!strstr(line, "[compress]"); int is_compress = !!strstr(line, "[compress]");
if( !is_cook && !is_compress ) { if( !is_cook && !is_compress ) { // if not a special section...
// remove hint cook tag if present. that's informative only. // remove hint cook tag if present. that's informative only.
if(strbegi(line, "[cook ") ) memcpy(line+1, " ", 4); if(strbegi(line, "[cook ") ) memcpy(line+1, " ", 4); // line += 6;
// start parsing expressions like `[media && !avi && mp3]` // start parsing expressions like `[media && !avi && mp3]`
array(char*) tags = strsplit(line, " []&"); array(char*) tags = strsplit(line, " []&");
@ -3039,8 +3053,9 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
char **INPUT = map_find(symbols, "INPUT"); char **INPUT = map_find(symbols, "INPUT");
bool found_in_set = true; bool found_in_set = true;
for( int i = 0, end = array_count(tags); i < end; ++i) { char *tag = tags[i]; for( int i = 0, end = array_count(tags); i < end; ++i) {
bool negate = false; bool negate = false;
char *tag = tags[i];
while(*tag == '!') negate ^= 1, ++tag; while(*tag == '!') negate ^= 1, ++tag;
// find tag in groups map // find tag in groups map
@ -3050,7 +3065,7 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
char *list = *is_group; char *list = *is_group;
char *INPUT_EXT = file_ext(infile); INPUT_EXT = strrchr(INPUT_EXT, '.'); // .ext1.ext -> .ext char *INPUT_EXT = file_ext(infile); INPUT_EXT = strrchr(INPUT_EXT, '.'); // .ext1.ext -> .ext
char *ext = INPUT_EXT; ext += ext[0] == '.'; // dotless char *ext = INPUT_EXT; ext += ext[0] == '.'; // dotless
bool in_list = strbegi(list, ext) || strstri(list, va(",%s,",ext)) || strendi(list, va(",%s",ext)); bool in_list = strbegi(list, ext) || strendi(list, va(",%s",ext)) || strstri(list, va(",%s,",ext));
if( !in_list ^ negate ) { found_in_set = false; break; } if( !in_list ^ negate ) { found_in_set = false; break; }
} else { } else {
char *ext = va(".%s", tag); char *ext = va(".%s", tag);
@ -3058,6 +3073,13 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
if( !found ^ negate ) { found_in_set = false; break; } if( !found ^ negate ) { found_in_set = false; break; }
} }
} }
if( found_in_set ) {
// inc pass
set_find_or_add(passes, STRDUP(*tags)); // @leak
// check whether we keep searching
int num_passes = set_count(passes);
found_in_set = ( pass == (num_passes-1) );
}
// //
enabled = found_in_set ? 1 : 0; enabled = found_in_set ? 1 : 0;
} }
@ -3098,20 +3120,26 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
int errorlevel = has_errorlevel ? atoi(has_errorlevel + 2) : 0; int errorlevel = has_errorlevel ? atoi(has_errorlevel + 2) : 0;
if( has_errorlevel ) memcpy(has_errorlevel, " ", 3); if( has_errorlevel ) memcpy(has_errorlevel, " ", 3);
// detect if newer extension is present, and thus update OUTPUT if needed // detect if newer extension or filename is present, and thus update OUTPUT if needed
char *newer_extension = strstr(line, "->"); if(newer_extension) { char *newer_extension = strstr(line, "->"); if(newer_extension) {
*newer_extension = 0; *newer_extension = 0;
newer_extension += 2 + strspn(newer_extension + 2, " "); newer_extension += 2 + strspn(newer_extension + 2, " ");
if( strchr(newer_extension, '.') ) {
// newer filename
cs.outname = stringf("%s@%s", cs.outname ? cs.outname : infile, newer_extension); // @leak
newer_extension = NULL;
} else {
strcatf(&*OUTPUT, ".%s", newer_extension); strcatf(&*OUTPUT, ".%s", newer_extension);
} }
}
// replace all symbols // replace all symbols
char* nl = STRDUP(line); char* nl = STRDUP(line); // @leak
for each_map(symbols, char*, key, char*, val) { for each_map(symbols, char*, key, char*, val) {
strrepl(&nl, key, val); strrepl(&nl, key, val);
} }
lines[i] = line = nl; // @fixme:leak lines[i] = line = nl;
// convert slashes // convert slashes
ifdef(win32, ifdef(win32,
@ -3136,43 +3164,41 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
} }
} }
// compression
char* ext = file_ext(infile); ext = strrchr(ext, '.'); ext += ext[0] == '.'; // dotless INPUT_EXT
char** OUTPUT = map_find(symbols, "OUTPUT"); char** OUTPUT = map_find(symbols, "OUTPUT");
int ext_num_groups = 0;
// compression
if( 1 ) {
char* ext = file_ext(infile); ext = strrchr(ext, '.'); ext += ext[0] == '.'; // dotless INPUT_EXT
char* belongs_to = 0; char* belongs_to = 0;
for each_map(groups, char*, key, char*, val) { for each_map(groups, char*, key, char*, val) {
if( !isdigit(key[0]) ) { if( !isdigit(key[0]) ) {
char *comma = va(",%s,", ext); char *comma = va(",%s,", ext);
if( strbegi(val, comma+1) || strstri(val, comma) || strendi(val, va(",%s", ext))) { if( !strcmpi(val,ext) || strbegi(val, comma+1) || strstri(val, comma) || strendi(val, va(",%s", ext))) {
belongs_to = key; belongs_to = key;
//goto break1; // each_map() macro is made of multiple for(;;)s. goto needed; you cant escape with single break. ext_num_groups++;
} }
} }
} }
break1:;
char *compression = 0; char *compression = 0;
for each_map(groups, char*, key, char*, val) { for each_map(groups, char*, key, char*, val) {
if( isdigit(key[0]) ) { if( isdigit(key[0]) ) {
char *comma = va(",%s,", ext); char *comma = va(",%s,", ext);
if( strbegi(val, comma+1) || strstri(val, comma) || strendi(val, va(",%s", ext))) { if( !strcmpi(val,ext) || strbegi(val, comma+1) || strstri(val, comma) || strendi(val, va(",%s", ext))) {
compression = key; compression = key;
//goto break2; // each_map() macro is made of multiple for(;;)s. goto needed; you cant escape with single break.
} }
comma = va(",%s,", belongs_to); comma = va(",%s,", belongs_to);
if( strbegi(val, comma+1) || strstri(val, comma) || strendi(val, va(",%s", ext))) { if( !strcmpi(val,ext) || strbegi(val, comma+1) || strstri(val, comma) || strendi(val, va(",%s", ext))) {
compression = key; compression = key;
//goto break2; // each_map() macro is made of multiple for(;;)s. goto needed; you cant escape with single break.
} }
} }
} }
break2:;
cs.compress_level = 0; cs.compress_level = 0;
if( compression ) { if( compression ) {
// last chance to optionally override the compressor at command-line level // last chance to optionally override the compressor at command-line level
static const char *compressor_override, **init = 0; static const char *compressor_override;
if( !init ) *(init = &compressor_override) = option("--cook-compressor", ""); do_once compressor_override = option("--cook-compressor", "");
if( compressor_override[0] ) compression = (char*)compressor_override; if( compressor_override[0] ) compression = (char*)compressor_override;
/**/ if(strstri(compression, "PPP")) cs.compress_level = atoi(compression) | PPP; /**/ if(strstri(compression, "PPP")) cs.compress_level = atoi(compression) | PPP;
@ -3188,11 +3214,12 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
else if(strstri(compression, "BCM")) cs.compress_level = atoi(compression) | BCM; else if(strstri(compression, "BCM")) cs.compress_level = atoi(compression) | BCM;
else cs.compress_level = isdigit(compression[0]) ? atoi(compression) : 6 /*| DEFL*/; else cs.compress_level = isdigit(compression[0]) ? atoi(compression) : 6 /*| DEFL*/;
} }
}
// if script was generated... // if script was generated...
if( script && script[0]) { if( script && script[0]) {
// update outfile // update outfile
cs.finalfile = *OUTPUT; cs.outfile = *OUTPUT;
// amalgamate script // amalgamate script
array(char*) lines = strsplit(script, "\r\n"); array(char*) lines = strsplit(script, "\r\n");
@ -3215,15 +3242,22 @@ cook_script_t cook_script(const char *rules, const char *infile, const char *out
} else { } else {
// ... else bypass infile->outfile // ... else bypass infile->outfile
char** INFILE = map_find(symbols, "INFILE"); char** INFILE = map_find(symbols, "INFILE");
cs.finalfile = *INFILE; cs.outfile = *INFILE;
// and return an empty script // and return an empty script
cs.script = ""; cs.script = "";
} }
map_clear(symbols); cs.outname = cs.outname ? cs.outname : (char*)infile;
map_clear(groups);
return cs; ASSERT(mcs.num_passes < countof(mcs.cs));
mcs.cs[mcs.num_passes++] = cs;
bool next_pass_required = mcs.num_passes < ext_num_groups;
if( !next_pass_required ) break;
}
return mcs;
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -3385,56 +3419,53 @@ int cook(void *userdata) {
*progress = ((i+1) == end ? 90 : (i * 90) / end); // (i+i>0) * 100.f / end; *progress = ((i+1) == end ? 90 : (i * 90) / end); // (i+i>0) * 100.f / end;
// start cook // start cook
const char *fname = uncooked[i]; //job->files[j]; const char *infile = uncooked[i]; //job->files[j];
int inlen = file_size(fname); int inlen = file_size(infile);
// generate a cooking script for this asset // generate a cooking script for this asset
cook_script_t cs = cook_script(job->rules, fname, COOK_TMPFILE); cook_script_t mcs = cook_script(job->rules, infile, COOK_TMPFILE);
// puts(cs.script); // puts(cs.script);
for(int pass = 0; pass < mcs.num_passes; ++pass) {
cook_subscript_t cs = mcs.cs[pass];
// log to batch file for forensic purposes, if explicitly requested // log to batch file for forensic purposes, if explicitly requested
static __thread bool logging = 0, *init = 0; if(!init) *(init = &logging) = !!flag("--cook-debug") || cook_debug; static __thread bool logging = 0; do_once logging = !!flag("--cook-debug") || cook_debug;
if( logging ) { if( logging ) {
FILE *logfile = fopen(va("cook%d.cmd",job->threadid), "a+t"); FILE *logfile = fopen(va("cook%d.cmd",job->threadid), "a+t");
if( logfile ) { fprintf(logfile, "@rem %s\n%s\n", fname, cs.script); fclose(logfile); } if( logfile ) { fprintf(logfile, "@rem %s\n%s\n", cs.outname, cs.script); fclose(logfile); }
// maybe log fprintf(logfile, "@rem %*.s\n", 4096, app_exec_output()); ? fprintf(stderr, "%s\n", cs.script);
} }
// invoke cooking script and recap status // invoke cooking script and recap status
const char *rcout = app_exec(cs.script); const char *rc_output = app_exec(cs.script);
int rc = atoi(rcout); int rc = atoi(rc_output);
int outlen = file_size(cs.finalfile); int outlen = file_size(cs.outfile);
int failed = cs.script[0] ? rc || !outlen : 0; int failed = cs.script[0] ? rc || !outlen : 0;
// print errors, or... // print errors, or...
if( failed ) { if( failed ) {
PRINTF("Import failed: %s while executing:\n%s\nReturned:\n%s\n", fname, cs.script, rcout); PRINTF("Import failed: %s while executing:\n%s\nReturned:\n%s\n", cs.outname, cs.script, rc_output);
} }
// ...process only if included. may include optional compression. // ...process only if included. may include optional compression.
else if( cs.compress_level >= 0 ) { else if( cs.compress_level >= 0 ) {
FILE *in = fopen(cs.finalfile, "rb"); FILE *in = fopen(cs.outfile, "rb");
#if 0 #if 0
struct stat st; stat(fname, &st); struct stat st; stat(infile, &st);
struct tm *timeinfo = localtime(&st.st_mtime); struct tm *timeinfo = localtime(&st.st_mtime);
ASSERT(timeinfo); ASSERT(timeinfo);
// pretty (truncated) input (C:/prj/V4K/art/file.wav -> file.wav)
static __thread int artlen = 0; if(!artlen) artlen = strlen(ART);
const char *pretty = fname;
if( !strncmp(pretty, ART, artlen) ) pretty += artlen;
while(pretty[0] == '/') ++pretty;
fname = pretty;
//puts(fname);
#endif #endif
char *comment = va("%d", inlen); char *comment = va("%d", inlen);
if( !zip_append_file/*_timeinfo*/(z, fname, comment, in, cs.compress_level/*, timeinfo*/) ) { if( !zip_append_file/*_timeinfo*/(z, cs.outname, comment, in, cs.compress_level/*, timeinfo*/) ) {
PANIC("failed to add processed file into %s: %s", zipfile, fname); PANIC("failed to add processed file into %s: %s(%s)", zipfile, cs.outname, infile);
} }
fclose(in); fclose(in);
} }
}
} }
zip_close(z); zip_close(z);

View File

@ -1501,8 +1501,8 @@ API const char * xml_string(char *key);
API unsigned xml_count(char *key); API unsigned xml_count(char *key);
API array(char) xml_blob(char *key); API array(char) xml_blob(char *key);
#define xml_string(...) xml_string(va(__VA_ARGS__)) // syntax sugar: string #define xml_string(...) xml_string(va(__VA_ARGS__)) // syntax sugar: string
#define xml_int(...) atoi(xml_string(va(__VA_ARGS__))) // syntax sugar: int #define xml_int(...) atoi(xml_string(__VA_ARGS__)) // syntax sugar: int
#define xml_float(...) atof(xml_string(va(__VA_ARGS__))) // syntax sugar: float #define xml_float(...) atof(xml_string(__VA_ARGS__)) // syntax sugar: float
#define xml_blob(...) xml_blob(va(__VA_ARGS__)) // syntax sugar: base64 blob #define xml_blob(...) xml_blob(va(__VA_ARGS__)) // syntax sugar: base64 blob
#define xml_count(...) xml_count(va(__VA_ARGS__)) // syntax sugar: count nodes #define xml_count(...) xml_count(va(__VA_ARGS__)) // syntax sugar: count nodes
API void xml_pop(); API void xml_pop();

View File

@ -596,7 +596,7 @@ details > summary::-webkit-details-marker {
|Version: | 2023.7 | |Version: | 2023.7 |
|:--------------|:------------| |:--------------|:------------|
|Branch: | main | |Branch: | main |
|Commit: | 62 | |Commit: | 63 |
<!--| Documentation last modified | { {LAST_MODIFIED} } |--> <!--| Documentation last modified | { {LAST_MODIFIED} } |-->
# [V·4·K 2023.7 ](https://dev.v4.games/zaklaus/v4k) # [V·4·K 2023.7 ](https://dev.v4.games/zaklaus/v4k)
@ -8667,7 +8667,7 @@ Other documentation examples: [dll](#dll), [strsplit](#strsplit), [strjoin](#str
## shaders ## shaders
<a name=""></a> <a name="ned"></a>
<details><summary><code lang=C> extern const char* const fs_0_0_shadowmap_lit;</code></summary> <details><summary><code lang=C> extern const char* const fs_0_0_shadowmap_lit;</code></summary>
Under construction. Yet to be documented. Under construction. Yet to be documented.
@ -8676,7 +8676,7 @@ Other documentation examples: [dll](#dll), [strsplit](#strsplit), [strjoin](#str
</details> </details>
<a name=""></a> <a name="ned"></a>
<details><summary><code lang=C> extern const char* const fs_0_0_shadowmap_unlit;</code></summary> <details><summary><code lang=C> extern const char* const fs_0_0_shadowmap_unlit;</code></summary>
Under construction. Yet to be documented. Under construction. Yet to be documented.
@ -8685,7 +8685,7 @@ Other documentation examples: [dll](#dll), [strsplit](#strsplit), [strjoin](#str
</details> </details>
<a name=""></a> <a name="ned"></a>
<details><summary><code lang=C> extern const char* const fs_24_4_sprite;</code></summary> <details><summary><code lang=C> extern const char* const fs_24_4_sprite;</code></summary>
Under construction. Yet to be documented. Under construction. Yet to be documented.
@ -8694,7 +8694,7 @@ Other documentation examples: [dll](#dll), [strsplit](#strsplit), [strjoin](#str
</details> </details>
<a name=""></a> <a name="ned"></a>
<details><summary><code lang=C> extern const char* const fs_2_4_preamble;</code></summary> <details><summary><code lang=C> extern const char* const fs_2_4_preamble;</code></summary>
Under construction. Yet to be documented. Under construction. Yet to be documented.
@ -8703,7 +8703,7 @@ Other documentation examples: [dll](#dll), [strsplit](#strsplit), [strjoin](#str
</details> </details>
<a name=""></a> <a name="ned"></a>
<details><summary><code lang=C> extern const char* const fs_2_4_texel_inv_gamma;</code></summary> <details><summary><code lang=C> extern const char* const fs_2_4_texel_inv_gamma;</code></summary>
Under construction. Yet to be documented. Under construction. Yet to be documented.
@ -8712,7 +8712,7 @@ Other documentation examples: [dll](#dll), [strsplit](#strsplit), [strjoin](#str
</details> </details>
<a name=""></a> <a name="ned"></a>
<details><summary><code lang=C> extern const char* const fs_2_4_texel_ycbr_gamma_saturation;</code></summary> <details><summary><code lang=C> extern const char* const fs_2_4_texel_ycbr_gamma_saturation;</code></summary>
Under construction. Yet to be documented. Under construction. Yet to be documented.
@ -8721,7 +8721,7 @@ Other documentation examples: [dll](#dll), [strsplit](#strsplit), [strjoin](#str
</details> </details>
<a name=""></a> <a name="ned"></a>
<details><summary><code lang=C> extern const char* const fs_32_4_model;</code></summary> <details><summary><code lang=C> extern const char* const fs_32_4_model;</code></summary>
Under construction. Yet to be documented. Under construction. Yet to be documented.
@ -8730,7 +8730,7 @@ Other documentation examples: [dll](#dll), [strsplit](#strsplit), [strjoin](#str
</details> </details>
<a name=""></a> <a name="ned"></a>
<details><summary><code lang=C> extern const char* const fs_32_4_model_basic;</code></summary> <details><summary><code lang=C> extern const char* const fs_32_4_model_basic;</code></summary>
Under construction. Yet to be documented. Under construction. Yet to be documented.
@ -8739,7 +8739,7 @@ Other documentation examples: [dll](#dll), [strsplit](#strsplit), [strjoin](#str
</details> </details>
<a name=""></a> <a name="ned"></a>
<details><summary><code lang=C> extern const char* const fs_3_4_skybox;</code></summary> <details><summary><code lang=C> extern const char* const fs_3_4_skybox;</code></summary>
Under construction. Yet to be documented. Under construction. Yet to be documented.
@ -8748,7 +8748,7 @@ Other documentation examples: [dll](#dll), [strsplit](#strsplit), [strjoin](#str
</details> </details>
<a name=""></a> <a name="ned"></a>
<details><summary><code lang=C> extern const char* const fs_3_4_skybox_rayleigh;</code></summary> <details><summary><code lang=C> extern const char* const fs_3_4_skybox_rayleigh;</code></summary>
Under construction. Yet to be documented. Under construction. Yet to be documented.
@ -8757,7 +8757,7 @@ Other documentation examples: [dll](#dll), [strsplit](#strsplit), [strjoin](#str
</details> </details>
<a name=""></a> <a name="ned"></a>
<details><summary><code lang=C> extern const char* const fs_main_shadertoy;</code></summary> <details><summary><code lang=C> extern const char* const fs_main_shadertoy;</code></summary>
Under construction. Yet to be documented. Under construction. Yet to be documented.
@ -8766,7 +8766,7 @@ Other documentation examples: [dll](#dll), [strsplit](#strsplit), [strjoin](#str
</details> </details>
<a name=""></a> <a name="ned"></a>
<details><summary><code lang=C> extern const char* const vs_0_2_fullscreen_quad_A;</code></summary> <details><summary><code lang=C> extern const char* const vs_0_2_fullscreen_quad_A;</code></summary>
Under construction. Yet to be documented. Under construction. Yet to be documented.
@ -8775,7 +8775,7 @@ Other documentation examples: [dll](#dll), [strsplit](#strsplit), [strjoin](#str
</details> </details>
<a name=""></a> <a name="ned"></a>
<details><summary><code lang=C> extern const char* const vs_0_2_fullscreen_quad_B;</code></summary> <details><summary><code lang=C> extern const char* const vs_0_2_fullscreen_quad_B;</code></summary>
Under construction. Yet to be documented. Under construction. Yet to be documented.
@ -8784,7 +8784,7 @@ Other documentation examples: [dll](#dll), [strsplit](#strsplit), [strjoin](#str
</details> </details>
<a name=""></a> <a name="ned"></a>
<details><summary><code lang=C> extern const char* const vs_0_2_fullscreen_quad_B_flipped;</code></summary> <details><summary><code lang=C> extern const char* const vs_0_2_fullscreen_quad_B_flipped;</code></summary>
Under construction. Yet to be documented. Under construction. Yet to be documented.
@ -8793,7 +8793,7 @@ Other documentation examples: [dll](#dll), [strsplit](#strsplit), [strjoin](#str
</details> </details>
<a name=""></a> <a name="ned"></a>
<details><summary><code lang=C> extern const char* const vs_323444143_16_332_model;</code></summary> <details><summary><code lang=C> extern const char* const vs_323444143_16_332_model;</code></summary>
Under construction. Yet to be documented. Under construction. Yet to be documented.
@ -8802,7 +8802,7 @@ Other documentation examples: [dll](#dll), [strsplit](#strsplit), [strjoin](#str
</details> </details>
<a name=""></a> <a name="ned"></a>
<details><summary><code lang=C> extern const char* const vs_324_24_sprite;</code></summary> <details><summary><code lang=C> extern const char* const vs_324_24_sprite;</code></summary>
Under construction. Yet to be documented. Under construction. Yet to be documented.
@ -8811,7 +8811,7 @@ Other documentation examples: [dll](#dll), [strsplit](#strsplit), [strjoin](#str
</details> </details>
<a name=""></a> <a name="ned"></a>
<details><summary><code lang=C> extern const char* const vs_332_32;</code></summary> <details><summary><code lang=C> extern const char* const vs_332_32;</code></summary>
Under construction. Yet to be documented. Under construction. Yet to be documented.
@ -8820,7 +8820,7 @@ Other documentation examples: [dll](#dll), [strsplit](#strsplit), [strjoin](#str
</details> </details>
<a name=""></a> <a name="ned"></a>
<details><summary><code lang=C> extern const char* const vs_3_3_skybox;</code></summary> <details><summary><code lang=C> extern const char* const vs_3_3_skybox;</code></summary>
Under construction. Yet to be documented. Under construction. Yet to be documented.

Binary file not shown.

View File

@ -1,4 +1,4 @@
; this is where you specify and configure the FWK pipeline. ; this is where you specify and configure the V4K pipeline.
; tweak the pipeline and add new importers just by editing this file. ; tweak the pipeline and add new importers just by editing this file.
; there is no flow control in this script file: lines are parsed and evaluated, from top to bottom. ; there is no flow control in this script file: lines are parsed and evaluated, from top to bottom.
@ -6,11 +6,11 @@
; let's create a symbol. symbols are uppercase words always. ; let's create a symbol. symbols are uppercase words always.
; syntax: symbols are defined in KEY=value form, as seen below. ; syntax: symbols are defined in KEY=value form, as seen below.
TOOLS=./ ; folder where our pipeline tools are located
ART=../demos/art/,../engine/art/ ; comma-separated folder(s) that store all our asset files ART=../demos/art/,../engine/art/ ; comma-separated folder(s) that store all our asset files
TOOLS=./ ; where our pipeline tools are located
; lines starting with @windows, @linux or @osx will be processed only where OS matches. ; lines starting with @windows, @linux or @osx will be processed only where OS matches.
; we are defining here some symbols differently on each platform. ; we are defining here some symbols differently for each platform.
; syntax: lines starting with @keyword. valid keywords are win/dows, lin/ux, and osx. ; syntax: lines starting with @keyword. valid keywords are win/dows, lin/ux, and osx.
@linux NUL=/dev/null @linux NUL=/dev/null
@ -23,7 +23,7 @@ TOOLS=./ ; where our pipeline tools are located
; you can invoke shell commands directly with `command` at anytime. ; you can invoke shell commands directly with `command` at anytime.
; also, once a symbol is found, it is replaced by its value always. ; also, once a symbol is found, it is replaced by its value always.
; PROGRESS (percent), INPUT (input filename), OUTPUT (output filename) and PRETTY (clean input filename) are some predefined symbols. ; some predefined symbols: INPUT (input filename), OUTPUT (output filename), PRETTY (clean input filename), PROGRESS (cook progress).
@windows `echo Cooking PROGRESS% PRETTY...` @windows `echo Cooking PROGRESS% PRETTY...`
@linux `echo "Cooking PROGRESS% PRETTY..."` @linux `echo "Cooking PROGRESS% PRETTY..."`
@ -35,16 +35,16 @@ TOOLS=./ ; where our pipeline tools are located
; syntax: group=ext1,ext2[...] ; syntax: group=ext1,ext2[...]
[cook] [cook]
icons=ico icon=ico
image=jpg,png,bmp,psd,pic,pnm,hdr image=jpg,jpeg,png,bmp,psd,pic,pnm,hdr
texture=pvr,ktx,ktx2,dds,astc,basis,tga texture=pvr,ktx,ktx2,dds,astc,basis,tga
anim=fbx
model=iqm,iqe,gltf,gltf2,glb,fbx,obj,dae,blend,md3,md5,ms3d,smd,x,3ds,bvh,dxf,lwo model=iqm,iqe,gltf,gltf2,glb,fbx,obj,dae,blend,md3,md5,ms3d,smd,x,3ds,bvh,dxf,lwo
anims=anim
audio=wav,flac,ogg,mp1,mp3,mid,sfxr ; ,mod,xm audio=wav,flac,ogg,mp1,mp3,mid,sfxr ; ,mod,xm
audio-modules=mod,xm,s3m,it audio-module=mod,xm,s3m,it
audio-furnace=fur audio-furnace=fur
font=ttf,ttc,otf font=ttf,ttc,otf
text=json,xml,csv,ini,cfg,doc,txt,md,c,h,lua,inl,cpp,hpp,htm,html text=json,xml,csv,ini,cfg,doc,txt,md,c,h,inl,cpp,hpp,htm,html
shader=hlsl,fx,dxil,dxbc,glsl,vert,frag,geom,tese,tesc,comp,vs,fs,gs,ts,cs,spirv,spv,slang shader=hlsl,fx,dxil,dxbc,glsl,vert,frag,geom,tese,tesc,comp,vs,fs,gs,ts,cs,spirv,spv,slang
script=lua,tl script=lua,tl
video=mp4,ogv,avi,mkv,wmv,mpg,mpeg video=mp4,ogv,avi,mkv,wmv,mpg,mpeg
@ -58,7 +58,7 @@ tiled=tmx,tsx
; hint: the ->ogg and ->wav parts below do signal the pipeline that the commands we are about ; hint: the ->ogg and ->wav parts below do signal the pipeline that the commands we are about
; to execute are performing a data conversion (from flac to ogg for example). ; to execute are performing a data conversion (from flac to ogg for example).
[cook audio-modules] [cook audio-module]
TOOLS/mod2wav.EXE INPUT OUTPUT -> wav TOOLS/mod2wav.EXE INPUT OUTPUT -> wav
TOOLS/ffmpeg.EXE -hide_banner -nostdin -loglevel fatal -y -i INPUT -f ogg -b:a 192k OUTPUT -> ogg ; -stats TOOLS/ffmpeg.EXE -hide_banner -nostdin -loglevel fatal -y -i INPUT -f ogg -b:a 192k OUTPUT -> ogg ; -stats
@ -164,16 +164,12 @@ TOOLS/cuttlefish.EXE -q -m -i INPUT -o OUTPUT -f BC1_RGBA -> ktx
; ------------------------------------------------------------------------------ ; ------------------------------------------------------------------------------
; finally, let's cook all models. the logic here is: ; finally, let's cook all models. the logic here is:
; 1. export animation list from models using ass2iqe -L ; 1. cook all models into iqe (ass2iqe), then into iqm (iqe2iqm): any -> iqe -> iqm
; 2. cook all models into iqe (ass2iqe), then into iqm (iqe2iqm): any -> iqe -> iqm ; 2. unless input is iqe. these models will run iqe2iqm only (no ass2iqe): iqe -> iqm.
; 3. unless input is iqe. these models will run iqe2iqm only (no ass2iqe): iqe -> iqm. ; 3. unless input is iqm. these models will not run any conversion at all: iqm.
; 4. unless input is iqm. these models will not run any conversion at all: iqm. ; 4. also, dae models need to flip their UVs coordinates (see -U flag below).
; 5. also, dae models need to flip their UVs coordinates (see -U flag below).
[cook anims] ; process all models to extract animlist [cook model && dae] ; pass dae, reject iqm,iqe or any other model
TOOLS/ass2iqe.EXE -L -o OUTPUT INPUT 2> NUL
[cook model && dae &&] ; pass dae, reject iqm,iqe or any other model
FLAGS= FLAGS=
TOOLS/ass2iqe.EXE FLAGS -o OUTPUT INPUT -U 2> NUL -> iqe TOOLS/ass2iqe.EXE FLAGS -o OUTPUT INPUT -U 2> NUL -> iqe
@ -184,6 +180,10 @@ TOOLS/ass2iqe.EXE FLAGS -o OUTPUT INPUT 2> NUL -> iqe
[cook model && !iqm] [cook model && !iqm]
TOOLS/iqe2iqm.EXE OUTPUT INPUT > NUL -> iqm TOOLS/iqe2iqm.EXE OUTPUT INPUT > NUL -> iqm
[cook anim]
FLAGS=
TOOLS/ass2iqe.EXE FLAGS -L -o OUTPUT INPUT 2> NUL -> animlist.txt
; ------------------------------------------------------------------------------ ; ------------------------------------------------------------------------------
; cook localization files ; cook localization files
@ -204,5 +204,5 @@ TOOLS/iqe2iqm.EXE OUTPUT INPUT > NUL -> iqm
; hint: use plain `0` to exclude those files we would like to directly stream within the final zipfile (flac,mp3,adpcm wav,...) ; hint: use plain `0` to exclude those files we would like to directly stream within the final zipfile (flac,mp3,adpcm wav,...)
[compress] [compress]
0|ULZ=texture,image,model,audio,font,text,shader,script,animlist 0|ULZ=texture,image,model,audio,font,text,shader,script
0=video,flac,ogg,wav,mp1,mp3,jpg,png 0=video,flac,ogg,wav,mp1,mp3,jpg,png