#include #include #include #include #include #include #include #include #include #include static size_t size; static unsigned char *data; static unsigned char add=0; static unsigned pos; static int X, Y; static int LINES,COLS; static struct { unsigned char s[40]; unsigned len; } Search[2]; static int breaks=0; static int fullhex=0; static int pokemonset=1; static char merktab[4][4][4][4]; static void initmerktab(void) { FILE *fp = fopen("/usr/lib/kbd/consolefonts/cp437-8x8", "rb"); unsigned char Fontti[256][8]; int a,b,c,d; static struct {int a,b,c,d; } mrk[256]; fread(&Fontti, 8, 256, fp); fclose(fp); memset(&merktab, sizeof(merktab), 0); memset(&mrk, sizeof(mrk), 0); for(c=0; c<256; c++) { int y, x; for(y=0; y<8; y++) for(x=0; x<8; x++) if(Fontti[c][y] & (1 << (7-x))) { if(y<4)if(x<4)mrk[c].a++;else mrk[c].b++; else if(x<4)mrk[c].c++;else mrk[c].d++; } mrk[c].a = (mrk[c].a+3)*3/16; mrk[c].b = (mrk[c].b+3)*3/16; mrk[c].c = (mrk[c].c+3)*3/16; mrk[c].d = (mrk[c].d+3)*3/16; // printf("Merkki %d: %d,%d,%d,%d\n", c, mrk[c].a, mrk[c].b, mrk[c].c, mrk[c].d); } // exit(0); for(a=0; a<4; a++) for(b=0; b<4; b++) for(c=0; c<4; c++) for(d=0; d<4; d++) { int ch, be = INT_MAX, n=0; for(ch=0; ch<255; ch++) { int ad = mrk[ch].a-a; int bd = mrk[ch].b-b; int cd = mrk[ch].c-c; int dd = mrk[ch].d-d; int e = (ad<0?-ad:ad) +(bd<0?-bd:bd) +(cd<0?-cd:cd) +(dd<0?-dd:dd); /* ad*ad+bd*bd+cd*cd+dd*dd; */ if(e=176&&ch<=178)||(ch>=219&&ch<=223)))) n=ch, be=e; } merktab[a][b][c][d] = n; } } static void display(void) { int fd = open("/dev/vcsa0", O_WRONLY); int x, y; unsigned char *s; int xe = COLS - 4 * ((COLS-X)/4); static char Buf[2]; int searchtype=0; s = data + pos; for(y=0; y> (7-cx))&1) | (((b2 >> (7-cx))&1)<<1); } for(cy=0; cy<4; cy++) { lseek(fd, 2 * (2 + COLS*(1+cy+y*4) + xe + x*4), SEEK_SET); for(cx=0; cx<4; cx++) { /* Buf[0] = merktab[y&3][x&3][cy][cx]; */ Buf[0] = merktab[tab[cy*2][cx*2]] [tab[cy*2][cx*2+1]] [tab[cy*2+1][cx*2]] [tab[cy*2+1][cx*2+1]]; write(fd, Buf, 2); } } } s = data + pos; x=0, y=0; lseek(fd, 2 * (COLS + 2), SEEK_SET); for(;;) { static const char set1[256] = "\r%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%" // 0x00 "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%" // 0x20 "'\"gÚÄ¿³³Ã ´Ã´%\t%;%%%%%%%%%%%%%%." // 0x40 "$%%IN%%%%%%%%%%%%%%%%%%%%%%%%%% " // 0x60 "ABCDEFGHIJKLMNOPQRSTUVWXYZ();:[]" // 0x80 "abcdefghijklmnopqrstuvwxyz‚'''''" // 0xA0 " " // 0xC0 "'PM-''?!.%%%%%%-$*./,+0123456789"; // 0xE0 static const char set2[256] = " " " " " ; " " do " " " " dlstv" " " " kn rm "; #define writ() do{\ write(fd,Buf,2); \ if(++x>=X){lseek(fd,(COLS-x)*2,SEEK_CUR);/*while(x=Y-3)goto Done;}}while(0) \ switch(searchtype) { case 0: { unsigned a, b = (sizeof Search) / (sizeof Search[0]); for(a=0; a= data+size) { Buf[0] = "EOF "[x%4]; Buf[1] = 0x10; writ(); continue; } Buf[1] = searchtype?14:7; Buf[0] = pokemonset?set1[(unsigned char)(*s+add)]:(*s+add); if((*s && fullhex) || fullhex==2)goto Hex; if(pokemonset && set2[*s] != ' ') { if(set2[*s] != ';') { Buf[1] = searchtype?10:2; writ(); Buf[0] = set2[*s]; } else { Buf[1] = searchtype?15:9; } } if(pokemonset && *s==0x54) { Buf[1] = searchtype?10:2; Buf[0] = 'P'; writ(); Buf[0] = 'O'; writ(); Buf[0] = 'K'; writ(); Buf[0] = '‚'; } if((breaks==1 && !*s) || (breaks==2 && (*s=='\n'))) { if(s[1]!=*s) { s++; writ(); Buf[0]=' '; while(x!=0)writ(); continue; } } if(pokemonset && Buf[0]=='%') { Hex: Buf[1] = searchtype?11:3; Buf[0]="0123456789ABCDEF"[(unsigned char)*s>>4]; writ(); Buf[0]="0123456789ABCDEF"[*s&15]; } writ(); s++; } Done: close(fd); } static struct termio back; static int tctl(int wait) { struct termio term = back; term.c_lflag &= ~ECHO & ~ICANON; term.c_cc[VMIN] = wait; /* when set to 0, getch() will return */ return ioctl(0, TCSETA, &term); /* immediately, if no key was pressed */ /* (return value is then EOF) */ } static int LastNap = -1; static int getch(void) { if(LastNap >= 0) { int temp = LastNap; LastNap = -1; return temp; } tctl(1); return getchar(); } static int kbhit(void) { int c; if(LastNap >= 0)return 1; tctl(0); c = getchar(); if(c==EOF) { LastNap = -1; return 0; } LastNap = c; return 1; } static void Upd(void) { printf("\33[H\33[0;1;44mAdd:%4d Pos:%08X \33[K", add, pos); if(Search[0].len) { unsigned char *s = Search[0].s; unsigned l = Search[0].len; printf(" (Searched: "); for(;l;l--)printf("%02X", *s++); printf(")"); } else { printf(" Usage: viewer "); } printf("\33[1;%dHB%d H%d P%d X%d", COLS-12, breaks,fullhex,pokemonset, X); printf("\33[m"); fflush(stdout); } static void DoSearch(unsigned char *what, int len) { static size_t oldfound; size_t p=0, plen = size-len-1; if(!len) { len = Search[0].len; what = Search[0].s; p = oldfound+1; } else { memmove(&Search[1], &Search[0], sizeof(Search) - sizeof(Search[0])); memcpy(Search[0].s, what, Search[0].len=len); } for(; pX*Y/3 ? p-X*Y/3 : 0; return; } } } static void GetWinSize(void) { struct winsize ws; /* buffer for TIOCSWINSZ */ if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0) { LINES = ws.ws_row; COLS = ws.ws_col; } if(LINES==0 || COLS==0) { printf("Unknown window size (%d,%d), setting to 80x24\n",COLS,LINES); LINES=24; COLS=80; } Y=LINES, X=COLS; } int main(int argc, const char *const *argv) { FILE *fp; const char *fn = argc>1?argv[argc-1]:NULL; initmerktab(); ioctl(0, TCGETA, &back); tctl(1); GetWinSize(); Restart: fp = fn?fopen(fn, "rb"):NULL; if(!fp) { if(fn) { printf("\33[%d;1H\33[m\33[K\n", LINES); printf("Can't open %s: %s\n", fn, strerror(errno)); } printf("\n\n" "A file viewer, designed specially for investigating Game Boy Pokémon-games.\n" "Copyright (C) 1992,2000 Bisqwit (http://iki.fi/bisqwit/)\n" "Usage: viewer \n\n"); goto RealEnd; } fseek(fp, 0, SEEK_END); size = ftell(fp); rewind(fp); data = malloc(size); fread(data, size, 1, fp); fclose(fp); Upd(); printf("\33[%d;1H\33[0;30;46m\33[K" "\33[Kq=quit .=pgdn ,=pgup a,s=-+wide /=find f=file\n" "\33[K+-=add b=toglbreaks h=toglhex p=toglpokemontrans", LINES-1); for(;;) { if(!kbhit())display(); switch(getch()) { case 'q': goto End; case '-': add--; Upd(); break; case '+': add++; Upd(); break; case '': if(pos>0)pos--; Upd(); break; case '': pos++; Upd(); break; case 'B': case '': pos+=X; Upd(); break; case 'A': case '': if(pos>X)pos-=X;else pos=0; Upd(); break; case '': case '.': case ' ': pos += X*Y; Upd(); break; case '': case ',': if(pos>X*Y)pos -= X*Y;else pos=0; Upd(); break; case 'a': if(X>10)X--; Upd(); break; case 's': if(X<900)X++; Upd(); break; case 'b': breaks=((breaks+1)%3); Upd(); break; case 'h': fullhex=((fullhex+1)%3); Upd(); break; case 'p': pokemonset^=1; Upd(); break; case 'f': { char Buf[256]; printf("\33[1;1H\33[mLoad file: \33[44m\33[K"); ioctl(0, TCSETA, &back); fgets(Buf, 255, stdin); if(Buf[0]!='\n') { strtok(Buf, "\n"); if(access(Buf, R_OK)) { printf("\33[1;1H\33[0;1;31mError: %s - press key\n", strerror(errno)); getch(); } else { free(data); fn = strdup(Buf); /* memory leak, don't care... who is going */ /* to load 100000 times a different file anyway. */ goto Restart; } } } case '/': { char Buf[40], *s, *t; printf("\33[1;23H\33[1;33;44mSearch (hex,hex,..): \33[37m\33[K"); ioctl(0, TCSETA, &back); fgets(Buf,39,stdin); for(s=t=Buf; *s&&*s!='\n'; s++) { int i = strtol(s, &s, 16); *t++ = i; if(*s!=',')break; } DoSearch(Buf, t-Buf); Upd(); tctl(1); } } } End: printf("\33[%d;1H\33[m\33[K\n", LINES); RealEnd: ioctl(0, TCSETA, &back); return 0; }