X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~yarrgweb/git?p=ypp-sc-tools.db-test.git;a=blobdiff_plain;f=pctb%2Fconvert.c;h=8101fb920a40adafad02ab92ed4efd5181c2ae44;hp=80bc894559656714c48e6c90b2c193a44309d57a;hb=9334b48a221df6f96a88933be8fde35e2ef41b35;hpb=0051984495aa2a5ed61224a7618f5bbd04311f7a diff --git a/pctb/convert.c b/pctb/convert.c index 80bc894..8101fb9 100644 --- a/pctb/convert.c +++ b/pctb/convert.c @@ -1,75 +1,438 @@ -#include -#include -#include -#include -#include - -#define eassert assert - -typedef struct { - unsigned long rgb; /* on screen */ - char c; /* canonical */ -} CanonColourInfo; - -static const CanonColourInfo canoncolourinfos[]= { - { 0x475A5E, '*' }, /* edge */ - { 0x7D9094, '+' }, /* interbox */ - { 0xBDC5BF, ' ' }, /* background - pale */ - { 0xADB5AF, ' ' }, /* background - dark */ - { 0x000000, 'o' }, /* foreground */ - { 0xD4B356, ' ' }, /* background (cursor) */ - { 0xFFFFFF, 'o' }, /* foreground (cursor) */ - { 0,0 } +/* + * 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) { + sysassert(!ferror(debug)); + sysassert(!fflush(debug)); +} + +const char *get_vardir(void) { return "."; } +const char *get_libdir(void) { return "."; } + + +enum mode { + mf_findwindow= 00001, + mf_screenshot= 00010, + mf_readscreenshot= 00020, + mf_analyse= 00100, + mfm_special= 07000, + + mode_findwindow= 00001, + mode_screenshot= 00011, + mode_analyse= 00120, + mode_showcharset= 01000, + + mode_all= 00111, +}; + +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 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)); +} + +static void run_analysis(void) { + FILE *tf; + + sysassert( tf= tmpfile() ); + progress("running recognition..."); + analyse(tf); + + if (o_flags & ff_upload) { + if (npages<=1) + fatal("Recognition successful, but refusing to upload partial data\n" + " (--single-page specified). Specify an output mode?"); + } + + sysassert( fseek(tf,0,SEEK_SET) == 0); + + 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); + } + + waitpid_check_exitstatus(processor, "output processor/uploader"); + fclose(tf); + progress_log("all complete."); +} + +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); + if (DEBUGP(rsync)) + fprintf(stderr,"executing rsync to fetch %s to %s\n",remote,local); + execlp(rsync, "rsync", + DEBUGP(rsync) ? "-zvLt" : "-zLt", + "--",remote,local,(char*)0); + sysassert(!"exec rsync failed"); + } + + waitpid_check_exitstatus(fetcher, "rsync"); +} + +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; } + + if (userspecified) + value= userspecified; + else if ((value= getenv(envname))) + ; + else if (o_flags & ff_testservers) + value= defvalue_test; + else + value= defvalue; + + if (value[0]=='/' || (value[0]=='.' && value[1]=='/')) + /* absolute or relative pathname - or anyway, something with no hostname */ + goto ok; + + const char *colon= strchr(value, ':'); + const char *slash= strchr(value, '/'); + + 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); + + value= masprintf("%s%s%s", defprotocol, value, + vallen && value[vallen-1]=='/' ? "" : "/"); + + ok: + sysassert(! setenv(envname,value,1) ); +} + +int main(int argc, char **argv) { + const char *arg; + + sysassert( setlocale(LC_MESSAGES,"") ); + sysassert( setlocale(LC_CTYPE,"en_GB.UTF-8") || + setlocale(LC_CTYPE,"en.UTF-8") ); + +#define ARGVAL ((*++argv) ? *argv : \ + (badusage("missing value for option %s",arg),(char*)0)) + +#define IS(s) (!strcmp(arg,(s))) + + while ((arg=*++argv)) { + if (IS("--find-window-only")) o_mode= mode_findwindow; + else if (IS("--screenshot-only")) o_mode= mode_screenshot; + else if (IS("--show-charset")) o_mode= mode_showcharset; + else if (IS("--analyse-only") || + IS("--same")) o_mode= mode_analyse; + else if (IS("--everything")) o_mode= mode_all; + else if (IS("--find-island")) o_flags |= ffs_printisland; + else if (IS("--single-page")) o_flags |= ff_singlepage; + else if (IS("--quiet")) o_quiet= 1; + else if (IS("--edit-charset")) o_flags |= ff_editcharset; + else if (IS("--test-servers")) o_flags |= ff_testservers; + else if (IS("--dict-local-only")) o_flags &= ~ffs_dict; + else if (IS("--dict-read-only")) o_flags &= (~ffs_dict | ff_dict_fetch); + else if (IS("--dict-anon")) o_flags &= ~ff_dict_pirate; + else if (IS("--dict-submit")) o_flags |= ff_dict_fetch|ff_dict_submit; + else if (IS("--raw-tsv")) o_outmode_kind= omk_raw; + else if (IS("--upload")) o_outmode_kind= omk_upload; + else if (IS("--arbitrage") || + IS("--tsv") || + IS("--best-prices")) o_outmode_kind=omk_str, + o_outmode_str=arg+2; + + else if (IS("--screenshot-file")) o_screenshot_fn= ARGVAL; + else if (IS("--pctb-server")) o_serv_pctb= ARGVAL; + else if (IS("--dict-submit-server")) o_serv_dict_submit= ARGVAL; + else if (IS("--dict-update-server")) o_serv_dict_fetch= ARGVAL; + else if (IS("--ocean")) o_ocean= ARGVAL; + else if (IS("--pirate")) o_pirate= ARGVAL; +#define DF(f) \ + else if (IS("-D" #f)) \ + debug_flags |= dbg_##f; + DEBUG_FLAG_LIST +#undef DF + else if (IS("--window-id")) { + char *ep; + unsigned long windowid= strtoul(ARGVAL,&ep,0); + if (*ep) badusage("invalid window id"); + set_yppclient_window(windowid); + } else + badusage("unknown option `%s'",arg); + } -static int height, width; -static char *image; - -static void load_image_and_canonify(void) { - struct pam inpam; - unsigned char rgb[3]; - int x,y,r; - const CanonColourInfo *cci; - - pnm_readpaminit(stdin, &inpam, sizeof(inpam)); - height= inpam.height; - width= inpam.width; - eassert(inpam.maxval == 255); - eassert(inpam.bytes_per_sample == 1); - - image= malloc(width*height); - eassert(image); - memset(image,'?',width*height); - - for (y=0; yc; cci++) - if (cci->rgb == rgb_l) { - image[y*width + x]= cci->c; - break; - } - if (y==234 && x==82) { - printf("y=%d/%d x=%d/%d rgb=%d,%d,%d rgb_l=%lx c=%c\n", - y,height,x,width, rgb[0],rgb[1],rgb[2], rgb_l, image[y*width+x]); + /* Consequential changes to options */ + + if (o_mode & mf_analyse) { + if (!o_outmode_kind) { + if (o_flags & ff_printisland) { + o_outmode_kind= omk_none; + o_flags |= ff_singlepage; + } else { + o_outmode_kind= omk_upload; } } - r= fwrite(image + y*width, 1,width, stdout); eassert(r==width); - putchar('\n'); + + if (o_outmode_kind==omk_upload) { + o_flags |= ffs_upload; + o_outmode_str= "upload"; + } } - eassert(!fflush(stdout)); - eassert(!ferror(stdout)); -} - -int main(void) { - load_image_and_canonify(); - /* - find_main_rectangle(); - repeatedly_find_top_thing(); - */ + + /* Defaults */ + + set_server("YPPSC_PCTB_PCTB", + "http://", "pctb.ilk.org" /*pctb.crabdance.com*/, + "pctb.ilk.org", + o_serv_pctb, o_flags & (ff_needisland|ff_upload)); + + set_server("YPPSC_PCTB_DICT_UPDATE", + "rsync://", "rsync.pctb.chiark.greenend.org.uk/pctb", + "rsync.pctb.chiark.greenend.org.uk/pctb/test", + o_serv_dict_fetch, o_flags & ff_dict_fetch); + + set_server("YPPSC_PCTB_DICT_SUBMIT", + "http://", "dictup.pctb.chiark.greenend.org.uk", + "dictup.pctb.chiark.greenend.org.uk/test", + o_serv_dict_submit, o_flags & ff_dict_submit); + + if (!o_screenshot_fn) + o_screenshot_fn= masprintf("%s/#pages#.ppm",get_vardir()); + + /* Actually do the work */ + + if (o_mode & mf_findwindow) { + screenshot_startup(); + find_yppclient_window(); + } + if (!ocean) ocean= o_ocean; + if (!pirate) pirate= o_pirate; + + if (o_flags & ff_needisland) + if (!ocean) + badusage("need --ocean option when not using actual YPP client window" + " (consider supplying --pirate too)"); + if (ocean) + sysassert(! setenv("YPPSC_OCEAN",ocean,1) ); + if (pirate && (o_flags & ff_dict_pirate)) + sysassert(! setenv("YPPSC_PIRATE",pirate,1) ); + + switch (o_mode & mfm_special) { + case 0: break; + case mode_showcharset: ocr_showcharsets(); exit(0); + default: abort(); + } + + if (o_mode & mf_screenshot) { + open_screenshot_file("w"); + if (o_flags & ff_singlepage) take_one_screenshot(); + else take_screenshots(); + progress_log("OK for you to move the mouse now."); + } + if (o_mode & mf_readscreenshot) { + if ((o_flags & ff_upload) && !(o_flags & ff_testservers)) + badusage("must not reuse screenshots for upload to live PCTB database"); + open_screenshot_file("r"); + if (o_flags & ff_singlepage) read_one_screenshot(); + else read_screenshots(); + } + if (o_mode & mf_analyse) { + if (o_flags & ff_needisland) { + find_islandname(page_images[0]->rgb); + if (o_flags & ff_printisland) + printf("%s, %s\n", archipelago, island); + sysassert(! setenv("YPPSC_ISLAND",island,1) ); + } + switch (o_outmode_kind) { + case omk_upload: case omk_str: run_analysis(); break; + case omk_raw: analyse(stdout); break; + case omk_none: break; + default: abort(); + } + } + progress_log("Finished."); return 0; } + + + + +DEFINE_VWRAPPERF(, progress, ) +DEFINE_VWRAPPERF(, progress_log, ) +DEFINE_VWRAPPERF(, progress_spinner, ) +DEFINE_VWRAPPERF(, warning, ) +DEFINE_VWRAPPERF(, fatal, NORET) + +static int last_progress_len; + +static void vprogress_core(int spinner, const char *fmt, va_list al) { + int r; + + if (o_quiet) return; + if (!isatty(2)) return; + + if (last_progress_len) + putc('\r',stderr); + + r= vfprintf(stderr,fmt,al); + + if (spinner) { + putc(spinner,stderr); + r++; + } + + if (r < last_progress_len) { + fprintf(stderr,"%*s", last_progress_len - r, ""); + if (!r) putc('\r', stderr); + else while (last_progress_len-- > 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; +}