#include <stdio.h> #include <string.h> #include <ctype.h> #include <stdlib.h> #define MAXFIELDS 100 #define LINECHARS 10000 typedef struct LINE_s { char *text; char *fields[MAXFIELDS]; int n_fields; int alloc; } LINE; typedef struct CLUE_s { int num; char *arrival_code; char *gc_name; char *title; char *arrival_text; char *solution; char *confirm_code; char *confirm_text; int conf_cnt; int is_override; struct CLUE_s *next; } CLUE; CLUE *g_clue_head = 0; CLUE **g_clue_tail = &g_clue_head; int g_num_clues = 0; char *g_infile = "clue_messages.stxt"; char *g_outfile = 0; int g_debug = 0; int g_override_cnt = 0; char *g_override_list[100]; #define ASSERT(cond) ASSERT1(cond,__LINE__) #define ASSERT1(cond,l) { if (!(cond)) { assert_failed(#cond,l); }} void assert_failed(char *cond, int line) { fprintf(stderr,"ASSERT FAILED (line %d): %s\n",line,cond); exit(1); } LINE *line_new(void) { LINE *line = (LINE*)malloc(sizeof(LINE)); line->alloc = LINECHARS; line->text = (char*)malloc(line->alloc); line->n_fields = 0; return line; } void line_delete(LINE *line) { if (line->text) { free(line->text); } free(line); } int line_read(LINE *line, FILE *fp) { enum { STARTFIELD, NOQUOTE, NOQUOTE_BSLASH, DQUOTE, DQUOTE_BSLASH, ENDLINE } state = STARTFIELD; int escape = 0; int dquote = 0; int c = getc(fp); char *s = line->text; line->n_fields = 0; if (c == EOF) { *s = 0; return 0; } ungetc(c,fp); while(state != ENDLINE) { ASSERT(s-line->text < line->alloc); c = getc(fp); switch(state) { case STARTFIELD: ASSERT(line->n_fields < MAXFIELDS); line->fields[line->n_fields++] = s; state = NOQUOTE; case NOQUOTE: switch(c) { case ',': *(s++) = 0; state = STARTFIELD; break; case '"': state = DQUOTE; break; case '\\': state = NOQUOTE_BSLASH; break; case '\n': *(s++) = 0; state = ENDLINE; break; default: *(s++) = c; break; } break; case NOQUOTE_BSLASH: case DQUOTE_BSLASH: switch(c) { case '\n': break; case 'n': *(s++) = '\n'; break; default: *(s++) = c; break; } state--; break; case DQUOTE: switch(c) { case '"': c = getc(fp); if (c == '"') { (*s++) = '"'; } else { ungetc(c,fp); state = NOQUOTE; } break; case '\\': state = NOQUOTE_BSLASH; break; default: *(s++) = c; break; } break; default: fprintf(stderr,"BAD STATE %d\n",state); exit(1); } } if (g_debug) { int j; static char *fname[] = { "arrival_code", "gc_name", "title", "arrival_text", "confirm_code", "confirm_text", }; fprintf(stderr,"===============================================\n"); for (j=0; j<line->n_fields; j++) { fprintf(stderr," field[%3d] %12s = '%s'\n",j,fname[j],line->fields[j]); } } return 1; } char *to_printable_nocr(char *s) { static char buf[1000]; char *d = buf; while(*s) { if (isprint(*s) && *s != '\n') { *d = *s; } else { *d = '@'; } d++; s++; } *d = 0; return buf; } char *code_dup(char *code) { char *nc = malloc(strlen(code)+1); char *s = code; char *d = nc; while(*s) { *d = toupper(*s); if (isupper(*d) || isdigit(*d)) d++; s++; } *d = 0; return nc; } void add_clue( char *arrival_code, char *gc_name, char *title, char *arrival_text, char *solution, char *confirm_code, char *confirm_text) { char buf[1000]; CLUE *clue = (CLUE*)malloc(sizeof(CLUE)); clue->num = g_num_clues++; clue->is_override = 0; if (*arrival_code == 0 || *arrival_code == '?') { arrival_code = "X"; } clue->arrival_code = code_dup(arrival_code); if (*gc_name == 0 || *gc_name == '?') { gc_name = buf; sprintf(buf,"clue %d",clue->num); } clue->gc_name = strdup(to_printable_nocr(gc_name)); if (*title == 0 || *title == '?') { title = clue->gc_name; } clue->title = strdup(to_printable_nocr(title)); if (*confirm_code == 0 || *confirm_code == '?') { static char *extra_confirms[] = { "DIAMONDS", "CROCODILE", "ROUTER", "ANIMATOR", "FOXHUNT", "TABLETOP", "BANANA", 0 }; static char **extra = extra_confirms; confirm_code = *extra; extra++; ASSERT(confirm_code); } clue->confirm_code = code_dup(confirm_code); if (!strcmp(clue->arrival_code,"OVERRIDE")) { clue->is_override = 1; clue->confirm_code = strdup("X"); } if (*arrival_text == 0 || *arrival_text == '?') { arrival_text = "X"; if (!clue->is_override) { arrival_text = title; } } clue->arrival_text = strdup(arrival_text); if (*solution == 0 || *solution == '?') { solution = "X"; } clue->solution = strdup(solution); if (*confirm_text == 0 || *confirm_text == '?') { confirm_text = ":My antenna must be damaged. Call Duct Tape Man at 650-743-7203 for instructions."; } clue->confirm_text = strdup(confirm_text); if (g_debug) { fprintf(stderr,"Add clue[%d]: '%s'\n",clue->num, clue->title); } clue->conf_cnt = 0; clue->next = 0; *g_clue_tail = clue; g_clue_tail = &(clue->next); } void get_messages(FILE *in) { int linenum = 0; int num = 0; LINE *line = line_new(); while(line_read(line, in)) { linenum++; if (line->n_fields < 1) continue; if (num == 0 && line->fields[0][0] == 0) continue; if (num == 0 && !strcmp(line->fields[0],"ARRIVAL CODE")) continue; if (line->n_fields != 7) { fprintf(stderr,"ERROR: line %d has %d fields (should have 7)\n", linenum,line->n_fields); continue; } num++; add_clue( line->fields[0], // arrival code line->fields[1], // gc name line->fields[2], // title line->fields[3], // mission briefing/arrival text line->fields[6], // solution line->fields[4], // confirm code line->fields[5]); // confirm text } line_delete(line); } void output_text(FILE *out, char *text) { char *str = strdup(text); char *s = str; char *e; int line = 0; while(*s) { // find 20 characters (find wordbreaks) for (e=s; *e != 0 && *e != '\n' && e-s < 20; e++); if (e-s >= 20) { char *e1 = e; while(1) { if (e == s) { e = e1; break; } if (*e == '\n') break; if (*e == ' ') break; if (*e == 0) break; if (e[-1] == '-') break; e--; } while(s != e && (e[-1] == ' ' || e[-1] == '\t')) e--; } // output 1 line putc('"',out); while(s != e) { if (*s == '\t') { fprintf(stderr,"ERROR: TAB is not supported! (using space)\n"); fprintf(stderr," TAB in string '%s'\n",text); *s = ' '; } if (*s == '"') { putc('\\',out); } putc(*s,out); s++; } putc('"',out); putc('\n',out); line++; // skip leading spaces (unless explicit carriage return) switch(*e) { case ' ': case '\t': while (*e == ' ' || *e == '\t') e++; break; case '\n': e++; break; default: break; } s = e; } while(line < 2) { fprintf(out,"\" \"\n"); line++; } free(str); } void output_message(FILE *out, CLUE *clue, char *text, char *basename, char *sfx) { fprintf(out,"TEXT mode_%s_%d%s POP\n", basename, clue->num, sfx); output_text(out,text); fprintf(out,"END\n"); fprintf(out,"\n"); } // // parse time string and output valid time hh:mm // char *time_string(char *in) { static char buf[100]; char *s = in; int hh=0, mm=0; if (s == 0) return "00:00"; if (*s && isdigit(*s)) do { char *e = 0; hh = strtol(s,&e,10); if (!e || e==s) { s++; continue; } if (*e != ':') { s = e; continue; } s = e+1; if (!isdigit(*s)) continue; mm = strtol(s,&e,10); if (!e || e==s) { s++; continue; } if (*e == ' ' || *e == '\t' || *e==0) { if (mm >= 60 || hh >= 48) { fprintf(stderr,"Error: invalid time: '%s'\n",in); return "00:00"; } sprintf(buf,"%02d:%02d",hh,mm); return buf; } } while(0); fprintf(stderr,"Error: could not parse time: '%s'\n",in); return "00:00"; } typedef struct CONFIRM_s { char *text; char *earliest_time; } CONFIRM; void output_confirms(FILE *out, CLUE *clue) { CONFIRM conf[100]; int cnt = 0; char *text = strdup(clue->confirm_text); char *s = text; int is_override = 0; conf[0].text = 0; conf[0].earliest_time = 0; if (!strcmp(text,"X")) { free(text); return; } while(*s) { char *s1 = s; while(isspace(*s)) s++; if (*s == 0) break; if (clue->is_override) { while(*s && *s != ':') s++; } if (*s == '>' || *s == '<') { int digit = 0; int state = 1; char dir = *s; char *t = 0; s++; while(isspace(*s)) s++; if (*s == '=') s++; while(isspace(*s)) s++; //t = s; while(1) { if (*s == 0) break; if (*s == '\n') break; switch(state) { case 0: // look for space if (isspace(*s)) state=1; case 1: // look for hour1 or space if (isdigit(*s)) { state=2; t=s; } else if (isspace(*s)) state=1; else state=0; break; case 2: // look for colon or hour2 if (isdigit(*s)) state=3; else if (*s==':') state=4; else state=100; break; case 3: // look for colon if (*s==':') state=4; else state=100; break; case 4: // look for minutes1 if (isdigit(*s)) state=5; else state=100; break; case 5: // look for minutes2 if (isdigit(*s)) state=6; else state=100; break; case 6: // look for space following time if (isspace(*s)) { *s = 0; state=90; } else state=100; break; case 90: // time found case 100: // no time found default: break; } if (*s == ':' && !digit) break; digit = isdigit(*s); s++; } if (*s != ':' || state!=90) { fprintf(stderr,"Error parsing time %d (%.*s) in clue %d:\n%s\n", cnt, s-s1,s1, clue->num, clue->confirm_text); } else { *s = 0; s++; while(isspace(*s)) s++; if (*s == '>' || *s == '<') { fprintf(stderr,"Error parsing confirm %d in clue %d:\n" "\tzero length (or starts with '>' or '<')\n%s\n", cnt, clue->num, clue->confirm_text); } else if (dir == '>') { if (cnt == 0) cnt++; conf[cnt].earliest_time = t; conf[cnt].text = s; cnt++; } else if (conf[cnt].text) { fprintf(stderr, "Error in clue %d: 2 earliest time (or default) confirms\n%s\n", clue->num, clue->confirm_text); } else { conf[0].text = s; if (cnt == 0) cnt++; } } } else if (conf[0].text) { fprintf(stderr, "Error in clue %d: 2 default (or earliest time) confirms\n%s\n", clue->num, clue->confirm_text); } else { char *s3 = s; int digit=0; while (*s3 && *s3 != '\n' && (*s3 != ':' || digit)) { digit = isdigit(*s3); s3++; } if (*s3 == ':') { s = s3+1; } else { fprintf(stderr,"ERROR: no colon found in '%s'\n", clue->confirm_text); } while(isspace(*s) && *s != '\n') *s++; conf[0].text = s; if (cnt == 0) cnt++; } while(*s) { if (*s == '\n') s1 = s+1; if (s == s1 && (*s =='>' || *s == '<')) { ASSERT(s[-1] == '\n'); s[-1] = 0; break; } s++; } } if (cnt < 1) { fprintf(stderr,"ERROR: no confirm messages in clue %d\n%s\n", clue->num, clue->confirm_text); conf[0].earliest_time = 0; conf[0].text = strdup(" "); cnt++; } else if (conf[0].text == 0) { if (cnt == 2 && !strcmp(clue->arrival_code,"OVERRIDE")) { is_override = 1; } else { fprintf(stderr,"ERROR: no default (zero-time) confirm message " "in clue %d:\n%s\n", clue->num, clue->confirm_text); } } //fprintf(stderr,"IMPLEMENT CONFIRM SORT!!\n"); if (!is_override) { int i; int offset = 0; fprintf(out,"CONFIRMS clue_%d\n",clue->num); for (i=0; i<cnt; i++) { char *time; if (conf[i].text == 0) { offset++; continue; } time = (i-offset == 0) ? time_string(0) : time_string(conf[i].earliest_time), fprintf(out,"confirm_%d_t%d\t\t\"%s\"\t# after %s\n", clue->num, i-offset, time, time); } fprintf(out,"END\n"); fprintf(out,"\n"); } if (is_override) { char buf[100]; ASSERT(cnt == 2 && conf[1].text && conf[1].earliest_time); sprintf(buf,"_t%d",g_override_cnt); g_override_list[g_override_cnt] = strdup(conf[1].earliest_time); g_override_cnt++; output_message(out, clue, conf[1].text, "override",buf); cnt = 0; } else { int i; int offset = 0; for (i=0; i<cnt; i++) { char buf[100]; if (conf[i].text == 0) { offset++; continue; } sprintf(buf,"_t%d",i-offset); output_message(out, clue, conf[i].text, "confirm",buf); } } clue->conf_cnt = cnt; free(text); } void output_overrides(FILE *out) { CLUE *clue; int i; int cnt; int cnt2; #if 0 fprintf(stderr,"%d time overrides found\n",g_override_cnt); ASSERT(g_override_cnt == 2); fprintf(out,"CONFIRMS overrides\n"); for (i=0; i<g_override_cnt; i++) { fprintf(out,"override_t%d\t\t\"%s\"\n", i, time_string(g_override_list[i])); } fprintf(out,"END\n"); fprintf(out,"\n"); #endif fprintf(out,"CONFIRMS clue_1000\n"); for(clue = g_clue_head; clue; clue=clue->next) { for (i=0; i<clue->conf_cnt; i++) { fprintf(out, "confirm_%d_t%d\t\t\"00:00\"\n", clue->num, i); } } fprintf(out,"END\n"); fprintf(out,"\n"); fprintf(out,"MENU mode_conf_test \"Confirm Menu\"\n"); cnt=0; cnt2 = 1; for(clue = g_clue_head; clue; clue=clue->next) { if (cnt >= 18) { cnt = 0; cnt2++; fprintf(out, "SET mode_conf_test%d \"Next...\"\n",cnt2); fprintf(out, "END\n"); fprintf(out,"MENU mode_conf_test%d \"Confirm Menu (cont)\"\n",cnt2); } for (i=0; i<clue->conf_cnt; i++) { cnt++; fprintf(out, "mode_confirm_%d_t%d\t\t\"%.17s v%d\"\n", clue->num, i, clue->title, i); } } fprintf(out, "POP \"Debug Menu\"\n"); fprintf(out,"END\n"); fprintf(out,"\n"); fprintf(out,"MENU mode_arr_test \"Arrival Menu\"\n"); cnt=0; for(clue = g_clue_head; clue; clue=clue->next) { cnt++; if (cnt == 18) { fprintf(out, "SET mode_arr_test2 \"Next...\"\n"); fprintf(out, "END\n"); fprintf(out,"MENU mode_arr_test2 \"Arrival Menu (cont)\"\n"); } if (clue->is_override) continue; fprintf(out, "mode_arrival_%d\t\t\"%.20s\"\n", clue->num, clue->title); } fprintf(out, "POP \"Debug Menu\"\n"); fprintf(out,"END\n"); fprintf(out,"\n"); fprintf(out,"MENU mode_sol_test \"Solution Menu\"\n"); cnt=0; for(clue = g_clue_head; clue; clue=clue->next) { cnt++; if (cnt == 18) { fprintf(out, "SET mode_sol_test2 \"Next...\"\n"); fprintf(out, "END\n"); fprintf(out,"MENU mode_sol_test2 \"Solution Menu cont.\"\n"); } if (clue->is_override) continue; fprintf(out, "mode_solution_%d\t\t\"%.20s\"\n", clue->num, clue->title); } fprintf(out, "POP \"Debug Menu\"\n"); fprintf(out,"END\n"); fprintf(out,"\n"); fprintf(out,"TEXT mode_ccode_test POP\n"); for(clue = g_clue_head; clue; clue=clue->next) { fprintf(out, "\"%2d:%.17s\"\n", clue->num, clue->title); fprintf(out, "\" %.17s\"\n", clue->confirm_code); } fprintf(out,"END\n"); fprintf(out,"\n"); } void output_clue(FILE *out, CLUE *clue) { char buf[1000]; if (g_debug) { fprintf(stderr,"=============== %d\n",clue->num); fprintf(stderr,"arrival: '%s'\n",clue->arrival_code); fprintf(stderr,"confirm: '%s'\n",clue->confirm_code); } fprintf(out,"# CLUE %d - '%s' (%s)\n", clue->num, clue->title, clue->gc_name); #if 0 if (!clue->is_override) { // // title string // fprintf(out, "str_clue%-4d\t\t\"%s\"\n", clue->num, clue->title); } #endif // // CODES // if (!clue->is_override && strcmp(clue->confirm_code,"NA") && strcmp(clue->confirm_code,"X") && strcmp(clue->confirm_code,"") && strcmp(clue->arrival_code,"OVERRIDE")) { fprintf(out,"CODE \"%s\" func_fn_confirm puz%d\t\t# confirm code for %s (%s)\n", clue->confirm_code, clue->num, clue->title, clue->gc_name); fprintf(out,"CODECHECK \"%s\"\t\t# confirm\n",clue->confirm_code); } if (!clue->is_override && strcmp(clue->arrival_code,"NA") && strcmp(clue->arrival_code,"X") && strcmp(clue->arrival_code,"OVERRIDE")) { #if 0 fprintf(out,"CODE \"%s\" func_fn_arrival2 parmval_%d parmval_30\t\t# arrival code for %s (%s)\n", clue->arrival_code, clue->num, clue->title, clue->gc_name); #endif fprintf(out,"CODECHECK \"%s\" clue_%d\t\t# arrival\n", clue->arrival_code, clue->num); } // // arrival message // fprintf(out,"\n"); if (!clue->is_override && strcmp(clue->arrival_text,"X")) { output_message(out, clue, clue->arrival_text, "arrival",""); } // // confirm message(s) // fprintf(out,"\n"); //output_message(out, clue, clue->confirm_text, "confirm",""); output_confirms(out, clue); // // solution message // fprintf(out,"\n"); if (!clue->is_override) { sprintf(buf,"%s\nSolution: '%s'\nCode: '%s'\nJU Internal name: '%s'", clue->title, clue->solution, clue->confirm_code, clue->gc_name); output_message(out, clue, buf, "solution",""); } fprintf(out,"\n"); fprintf(out,"\n"); } void output(FILE *out) { CLUE *clue = g_clue_head; fprintf(out,"# Justice Unlimited Gadget Microcode\n"); fprintf(out,"# (C) 2004 Nathan (Acorn) Pooley\n"); fprintf(out,"#\n"); fprintf(out,"# %s\n",g_outfile?g_outfile:"stdout"); fprintf(out,"#\n"); fprintf(out,"#@DOC@ Clue Messages \n"); fprintf(out,"#\n"); fprintf(out,"# DO NOT EDIT THIS FILE!!! - autogenerated by cmconvert,c\n"); fprintf(out,"#\n"); fprintf(out,"\n"); for(; clue; clue=clue->next) { output_clue(out,clue); } // // overrides // fprintf(out,"\n"); output_overrides(out); } char *get_parm(int *n, int argc, char *argv[]) { if (argv[*n][2] == 0) { if (*n >= argc-1) { fprintf(stderr,"Option '%s' must be followed by an argument\n", argv[*n]); exit(1); } (*n)++; return argv[*n]; } else { return &argv[*n][2]; } } int main(int argc, char *argv[]) { FILE *fp; int i; for (i=1; i<argc; i++) { if (argv[i][0] == '-') { switch(argv[i][1]) { case 'i': g_infile = strdup(get_parm(&i, argc, argv)); break; case 'o': g_outfile = strdup(get_parm(&i, argc, argv)); break; case 'd': g_debug = 1; default: fprintf(stderr,"Unknown option '%s'\n",argv[i]); } } else { fprintf(stderr,"Unknown argument %s",argv[i]); exit(1); } } fp = fopen(g_infile, "r"); if (!fp) { fprintf(stderr,"ERROR: Could not read infile '%s'\n",g_infile); exit(1); } get_messages(fp); fclose(fp); if (g_outfile) { fp = fopen(g_outfile,"w"); if (!fp) { fprintf(stderr,"ERROR: Could not write outfile '%s'\n",g_outfile); exit(1); } } else { fp = stdout; } output(fp); if (g_outfile) { fclose(fp); } return 0; } |
This file Copyright (C) 2004 by Nathan (Acorn) Pooley
Go to DRUID Development page
Go to DRUID page
Go to JU Gadgets page
Go to Justice Unlimited homepage
Go to Acorn's personal webpage
Contact Acorn
See comments from others
Post your own comments
File created by do_doc at Wed Aug 4 18:04:42 2004