X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~yarrgweb/git?p=ypp-sc-tools.db-live.git;a=blobdiff_plain;f=pctb%2Fconvert.c;h=3bc7554525e9299968c26196bb19ad1f23b420d8;hp=4784c653994e8e4bd0c675389075cca391b23cf4;hb=49e1be1a94ba3dc9d951056ebec2784286d3e928;hpb=5d58a08423953756871493042a9cfff032b66e18 diff --git a/pctb/convert.c b/pctb/convert.c index 4784c65..3bc7554 100644 --- a/pctb/convert.c +++ b/pctb/convert.c @@ -1,306 +1,424 @@ - -#include "ocr.h" - -static CanonImage *cim; +/* + * ypp-commodities main program: argument parsing etc. + */ +/* + * This is part of ypp-sc-tools, a set of third-party tools for assisting + * players of Yohoho Puzzle Pirates. + * + * Copyright (C) 2009 Ian Jackson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Yohoho and Puzzle Pirates are probably trademarks of Three Rings and + * are used without permission. This program is not endorsed or + * sponsored by Three Rings. + */ + +#include "convert.h" void debug_flush(void) { - eassert(!fflush(debug)); - eassert(!ferror(debug)); + sysassert(!ferror(debug)); + sysassert(!fflush(debug)); } -typedef struct { - int x, y; -} Point; - -typedef struct { /* both inclusive */ - Point tl; - Point br; -} Rect; - -static inline char get(int x, int y) { return cim->d[y * cim->w + x]; } -static inline char get_p(Point p) { return get(p.x,p.y); } - - -#define START_MAIN {200,200} -#define MIN_COLUMNS 6 -#define INTERESTING_COLUMNS 6 -#define TEXT_COLUMNS 2 -#define MAX_COLUMNS 7 - -static Rect mainr = { START_MAIN,START_MAIN }; -static int commbasey, comminty; -static int colrightx[INTERESTING_COLUMNS]; -static int text_h; -static OcrReader *rd; - -const CanonColourInfo canoncolourinfos[]= { - { 0x475A5E, '*' }, /* edge */ - { 0x2C5F7A, '*' }, /* edge just under box heading shadow */ - { 0x7D9094, '+' }, /* interbox */ - - { 0xBDC5BF, ' ' }, /* background - pale Sugar cane, etc. */ - { 0xADB5AF, ' ' }, /* background - dark */ - { 0xC7E1C3, ' ' }, /* background - pale Swill, etc. */ - { 0xB5CFB1, ' ' }, /* background - dark */ - { 0xD6CEB0, ' ' }, /* background - pale Madder, etc. */ - { 0xC8C0A2, ' ' }, /* background - dark */ - { 0xE0E1D3, ' ' }, /* background - pale Lorandite, etc. */ - { 0xD0D1C3, ' ' }, /* background - dark */ - { 0xE5E6C1, ' ' }, /* background - pale Cloth */ - { 0xD7D8B3, ' ' }, /* background - dark */ - { 0xEDDED9, ' ' }, /* background - pale Dye */ - { 0xDACBC6, ' ' }, /* background - dark */ - { 0xD3DEDF, ' ' }, /* background - pale Paint */ - { 0xC5D0D1, ' ' }, /* background - dark */ - { 0xDCD1CF, ' ' }, /* background - pale Enamel */ - { 0xCEC3C1, ' ' }, /* background - dark */ - { 0xF3F6F5, ' ' }, /* background - pale fruit */ - { 0xE2E7E5, ' ' }, /* background - dark */ - - { 0x000000, 'o' }, /* foreground */ - { 0xD4B356, ' ' }, /* background (cursor) */ - { 0xFFFFFF, 'o' }, /* foreground (cursor) */ - - { 0x5B93BF, '_' }, /* selector dropdown background */ - { 0xD7C94F, 'X' }, /* selector dropdown foreground */ - { 0,0 } +const char *get_vardir(void) { return "."; } +const char *get_libdir(void) { return "."; } + + +enum mode { + mf_findwindow= 0001, + mf_screenshot= 0010, + mf_readscreenshot= 0020, + mf_analyse= 0100, + + mode_findwindow= 0001, + mode_screenshot= 0011, + mode_analyse= 0120, + + mode_all= 0111, }; -static void require_rectangle(int tlx, int tly, int brx, int bry, - const char *ok) { - int x,y; - for (x=tlx; x<=brx; x++) - for (y=tly; y<=bry; y++) { - int c= get(x,y); - assert(strchr(ok,c)); - } -} -static void require_rectangle_r(Rect rr, const char *ok) { - require_rectangle(rr.tl.x,rr.tl.y, rr.br.x,rr.br.y, ok); +enum outmodekind { + omk_unset, omk_upload, omk_str, omk_raw, omk_none +}; +static enum outmodekind o_outmode_kind; +static const char *o_outmode_str= 0; + +static enum mode o_mode= mode_all; +static char *o_screenshot_fn; +static const char *o_serv_pctb, *o_serv_dict_fetch, *o_serv_dict_submit; + +const char *o_resolver= "./dictionary-manager"; +FILE *screenshot_file; +const char *o_ocean, *o_pirate; +int o_quiet; + +enum flags o_flags= ff_dict_fetch|ff_dict_submit|ff_dict_pirate; + +static void vbadusage(const char *fmt, va_list) FMT(1,0) NORET; +static void vbadusage(const char *fmt, va_list al) { + fputs("bad usage: ",stderr); + vfprintf(stderr,fmt,al); + fputc('\n',stderr); + exit(12); } +DEFINE_VWRAPPERF(static, badusage, NORET); -static void debug_rect(const char *what, int whati, Rect rr) { -#ifdef DEBUG_RECTANGLES - int y,r,w; - fprintf(debug, "%s %d: %d,%d..%d,%d:\n", what, whati, - rr.tl.x,rr.tl.y, rr.br.x,rr.br.y); - w= rr.br.x - rr.tl.x + 1; - for (y=rr.tl.y; y<=rr.br.y; y++) { - fprintf(debug, "%4d%*s|", y, rr.tl.x,""); - r= fwrite(cim->d + y*cim->w + rr.tl.x, 1, w, debug); - eassert(r==w); - fputc('|',debug); - fputc('\n',debug); - } -#endif - debug_flush(); +static void open_screenshot_file(const char *mode) { + screenshot_file= fopen(o_screenshot_fn, mode); + if (!screenshot_file) + fatal("could not open screenshots file `%s': %s", + o_screenshot_fn, strerror(errno)); } -#define WALK_UNTIL(point,coord,increm,last,edge) \ - for (;;) { \ - if ((point).coord == (last)+(increm)) break; \ - if (get_p((point)) == (edge)) { (point).coord -= (increm); break; } \ - (point).coord += (increm); \ - } +static void run_analysis(void) { + FILE *tf; -#define WALK_UNTIL_MUST(point,coord,increm,last,edge) \ - do { \ - WALK_UNTIL(point,coord,increm,last,edge); \ - eassert((point).coord != (last)+(increm)); \ - } while(0) - -static void find_structure(void) { - Rect whole = { {0,0}, {cim->w-1,cim->h-1} }; - - WALK_UNTIL_MUST(mainr.tl, x,-1, whole.tl.x, '*'); - WALK_UNTIL_MUST(mainr.tl, y,-1, whole.tl.y, '*'); - WALK_UNTIL_MUST(mainr.br, x,+1, whole.br.x, '*'); - WALK_UNTIL_MUST(mainr.br, y,+1, whole.br.y, '*'); - - require_rectangle(mainr.tl.x-1, mainr.tl.y, mainr.tl.x-1, mainr.br.y, "*"); - require_rectangle(mainr.br.x+1, mainr.tl.y, mainr.br.x+1, mainr.br.y, "*"); - require_rectangle(mainr.tl.x, mainr.tl.y-1, mainr.br.x, mainr.tl.y-1, "*"); - require_rectangle(mainr.tl.x, mainr.br.y+1, mainr.br.x, mainr.br.y+1, "*"); - -#define CHECK_STRIP_BORDER(tlbr,xy,increm) \ - do { \ - Point csb_p; \ - Rect csb_r; \ - csb_p= mainr.tl; \ - csb_p.xy= mainr.tlbr.xy; \ - if (get_p(csb_p)=='+') { \ - csb_r= mainr; \ - csb_r.tl.xy= csb_p.xy; \ - csb_r.br.xy= csb_p.xy; \ - require_rectangle_r(csb_r, "+"); \ - mainr.tlbr.xy += increm; \ - } \ - } while(0) - - debug_rect("mainr",0, mainr); - - CHECK_STRIP_BORDER(tl,x,+1); - CHECK_STRIP_BORDER(tl,y,+1); - CHECK_STRIP_BORDER(br,x,-1); - CHECK_STRIP_BORDER(br,y,-1); - - debug_rect("mainr",1, mainr); - - Point up = START_MAIN; - WALK_UNTIL_MUST(up, y,-1, mainr.tl.y, '+'); - - Point down = START_MAIN; - down.y++; - WALK_UNTIL_MUST(down, y,+1, mainr.br.y, '+'); - -#ifdef DEBUG_RECTANGLES - int xscaleunit, y,x; - for (y=0, xscaleunit=1; y<4; y++, xscaleunit*=10) { - fprintf(debug," "); - for (x=0; x<=cim->w; x++) { - if (x % xscaleunit) fputc(' ',debug); - else fprintf(debug,"%d",(x / xscaleunit)%10); - } - fputc('\n',debug); + sysassert( tf= tmpfile() ); + progress("running recognition..."); + analyse(tf); + + if (o_flags & ff_upload) { + if (o_flags & ff_singlepage) + fatal("Recognition successful, but refusing to upload partial data\n" + " (--single-page specified). Specify an output mode?"); } -#endif - commbasey= up.y; - comminty= down.y - up.y + 2; + sysassert( fseek(tf,0,SEEK_SET) == 0); - Point across= { mainr.tl.x, commbasey }; - int colno=0; - for (;;) { - eassert(get_p(across) != '+'); - WALK_UNTIL(across, x,+1, mainr.br.x, '+'); - eassert(colno < MAX_COLUMNS); - int colrx= across.x; - if (colrx > mainr.br.x) colrx= mainr.br.x; - if (colno < INTERESTING_COLUMNS) - colrightx[colno]= colrx; - - colno++; - - if (across.x >= mainr.br.x-1) - break; - - across.x++; - require_rectangle(across.x,mainr.tl.y, across.x,mainr.br.y, "+"); - across.x++; + progress_log("processing results (--%s)...", o_outmode_str); + pid_t processor; + sysassert( (processor= fork()) != -1 ); + + if (!processor) { + sysassert( dup2(fileno(tf),0) ==0 ); + EXECLP_HELPER("commod-results-processor", o_outmode_str, (char*)0); } - eassert(colno >= MIN_COLUMNS); - - text_h = comminty - 1; -} - -static void find_commodity(int offset, Rect *rr) { - /* rr->tl.x==-1 if offset out of range */ - rr->tl.y= commbasey - offset*comminty; - rr->br.y= rr->tl.y + comminty-2; - if (rr->tl.y < mainr.tl.y || rr->br.y > mainr.br.y) { rr->tl.x=-1; return; } - if (rr->tl.y > mainr.tl.y) - require_rectangle(rr->tl.x,rr->tl.y-1, rr->br.x,rr->tl.y-1, "+"); - if (rr->br.y < mainr.tl.y) - require_rectangle(rr->tl.x,rr->br.y+1, rr->br.x,rr->br.y+1, "+"); - - rr->tl.x= mainr.tl.x; - rr->br.x= mainr.br.x; -} -static void find_table_entry(Rect commod, int colno, Rect *cellr) { - cellr->tl.y= commod.tl.y; - cellr->br.y= commod.br.y; - cellr->tl.x= !colno ? commod.tl.x : colrightx[colno-1]+2; - cellr->br.x= colrightx[colno]; - debug_rect("cell", colno, *cellr); - require_rectangle_r(*cellr, " o"); + waitpid_check_exitstatus(processor, "output processor/uploader"); + fclose(tf); + progress_log("all complete."); } -CanonImage *alloc_canon_image(int w, int h) { - CanonImage *im= malloc(sizeof(CanonImage) + w*h); - eassert(im); - im->w= w; - im->h= h; - memset(im->d,'?',w*h); - return im; +void fetch_with_rsync(const char *stem) { + pid_t fetcher; + + sysassert( (fetcher= fork()) != -1 ); + if (!fetcher) { + const char *rsync= getenv("YPPSC_PCTB_RSYNC"); + if (!rsync) rsync= "rsync"; + + const char *src= getenv("YPPSC_PCTB_DICT_UPDATE"); + char *remote= masprintf("%s/master-%s.txt", src, stem); + char *local= masprintf("#master-%s#.txt", stem); + execlp(rsync, "rsync", + DEBUGP(rsync) ? "-vLt" : "-Lt", + "--",remote,local,(char*)0); + sysassert(!"exec rsync failed"); + } + + waitpid_check_exitstatus(fetcher, "rsync"); } -CanonImage *file_read_image(FILE *f) { - struct pam inpam; - unsigned char rgb_buf[3]; - CanonImage *im; +static void set_server(const char *envname, const char *defprotocol, + const char *defvalue, const char *defvalue_test, + const char *userspecified, + int enable) { + const char *value; + + if (!enable) { value= "0"; goto ok; } - pnm_readpaminit(f, &inpam, sizeof(inpam)); - eassert(inpam.maxval == 255); - eassert(inpam.bytes_per_sample == 1); + if (userspecified) + value= userspecified; + else if ((value= getenv(envname))) + ; + else if (o_flags & ff_testservers) + value= defvalue_test; + else + value= defvalue; - CANONICALISE_IMAGE(im, inpam.width, inpam.height, { - r= fread(&rgb_buf,1,3,f); eassert(r==3); + if (value[0]=='/' || (value[0]=='.' && value[1]=='/')) + /* absolute or relative pathname - or anyway, something with no hostname */ + goto ok; - rgb= - ((unsigned long)rgb_buf[0]<<16) | - ((unsigned long)rgb_buf[1]<<8) | - (rgb_buf[2]); - }); + const char *colon= strchr(value, ':'); + const char *slash= strchr(value, '/'); - return im; -} + if (colon && (!slash || colon < slash)) + /* colon before the first slash, if any */ + /* rsync :: protocol specification - anyway, adding scheme:// won't help */ + goto ok; + + int vallen= strlen(value); -static void load_image_and_canonify(void) { - cim= file_read_image(stdin); + value= masprintf("%s%s%s", defprotocol, value, + vallen && value[vallen-1]=='/' ? "" : "/"); + + ok: + sysassert(! setenv(envname,value,1) ); } -static void ocr_rectangle(Rect r, const OcrCellType ct) { - OcrResultGlyph *results, *res; - - int w= r.br.x - r.tl.x + 1; - Pixcol cols[w+1]; - int x,y; - for (x=0; xs; res++) - printf("%s",res->s); - printf("\"\n"); - eassert(!ferror(stdout)); - eassert(!fflush(stdout)); -} -int main_test(void) { - Rect thisr, entryr; - int tryrect, colno; - - load_image_and_canonify(); - find_structure(); - rd= ocr_init(text_h); - - for (tryrect= +cim->h; tryrect >= -cim->h; tryrect--) { - find_commodity(tryrect, &thisr); - if (thisr.tl.x < 0) - continue; - debug_rect("commod",tryrect, thisr); - - for (colno=0; colno r) putc('\b',stderr); + } + last_progress_len= r; + + if (ferror(stderr) || fflush(stderr)) _exit(16); +} + +void vprogress(const char *fmt, va_list al) { vprogress_core(0,fmt,al); } +void vprogress_spinner(const char *fmt, va_list al) { + static const char spinchars[]="/-\\"; + static int spinner; + + vprogress_core(spinchars[spinner],fmt,al); + spinner++; + spinner %= (sizeof(spinchars)-1); +} + +void vprogress_log(const char *fmt, va_list al) { + if (o_quiet) return; + + progress(""); + vfprintf(stderr,fmt,al); + putc('\n',stderr); + fflush(stderr); +} + +void vwarning(const char *fmt, va_list al) { + progress(""); + fputs("Warning: ",stderr); + vfprintf(stderr,fmt,al); + fputs("\n",stderr); + fflush(stderr); +} + +void vfatal(const char *fmt, va_list al) { + progress(""); + fputs("\n\nFatal error: ",stderr); + vfprintf(stderr,fmt,al); + fflush(stderr); + fputs("\n\n",stderr); + _exit(4); +} + +void sysassert_fail(const char *file, int line, const char *what) { + int e= errno; + progress(""); + fprintf(stderr, + "\nfatal operational error:\n" + " unsuccessful execution of: %s\n" + " %s:%d: %s\n\n", + what, file,line, strerror(e)); + _exit(16); +} + +void waitpid_check_exitstatus(pid_t pid, const char *what) { + pid_t got; + int st; + for (;;) { + got= waitpid(pid, &st, 0); + if (pid==-1) { sysassert(errno==EINTR); continue; } + break; + } + sysassert( got==pid ); + + if (WIFEXITED(st)) { + if (WEXITSTATUS(st)) + fatal("%s failed with nonzero exit status %d", + what, WEXITSTATUS(st)); + } else if (WIFSIGNALED(st)) { + fatal("%s died due to signal %s%s", what, + strsignal(WTERMSIG(st)), WCOREDUMP(st)?" (core dumped)":""); + } else { + fatal("%s gave strange wait status %d", what, st); + } +} + +char *masprintf(const char *fmt, ...) { + char *r; + va_list al; + va_start(al,fmt); + sysassert( vasprintf(&r,fmt,al) >= 0); + sysassert(r); + va_end(al); + return r; +}