Fix: SE048 Problem: The distribution format for tiles (.TXT) is limited to 62 colours. In order to include Mitsuhiro Itakura's 32x32 tile set (which uses 234 colours) we need to extend this. In fact, I have extended the file format to 4096 colours, although MAXCOLORMAPSIZE is still set at 256, so tile sets are still limited by this. While this could be increased, it would probably mean that we should rewrite the storage of tiles to be dynamic so that vast amounts of space are not wasted when a 16-colour tile set is in use. I've also fixed a couple of silly bugs in ppmwrite.c which took me far too long to find and added an xpm reader for easy conversion of XPM tile sets (I found that GIMP's XPM->GIF conversion re-coded the palette which made the resultant .TXT file much less readable). We're still not quite ready to include Mitsuhiro's tile set. Not all the Slash'EM tiles are included so we need a means of generating these from the 16x16 set as they are found to be missing by tile2x11 or whatever. We may also have a problem with the win32 interface which currently assumes 16-colour tiles (and therefore breaking the guidelines laid down in tile.doc). The programmer (M. Allison?) obviously was aware of this so it may not be easy to fix. I guess a routine to select 16 colours and re-colour the tiles to the new palette would be required. We might be able to get away with requiring an 8-bit visual if a 256-colour tile set is loaded. Compatible with: Slash'EM 0.0.6E0F1 Author: J. Ali Harlow, ali@avrc.city.ac.uk Date: 4 Feb 2000 *** sys/unix/Makefile.utl.orig Tue Feb 1 16:45:06 2000 --- sys/unix/Makefile.utl Fri Feb 4 16:30:56 2000 *************** *** 257,268 **** # TEXT_IO = tiletext.o tiletxt.o $(NAMEOBJS) GIFREADERS = gifread.o ../src/alloc.o panic.o PPMWRITERS = ppmwrite.o ../src/alloc.o panic.o ! tileutils: tilemap gif2txt txt2ppm tile2x11 gif2txt: $(GIFREADERS) $(TEXT_IO) $(CC) $(LFLAGS) -o gif2txt $(GIFREADERS) $(TEXT_IO) txt2ppm: $(PPMWRITERS) $(TEXT_IO) $(CC) $(LFLAGS) -o txt2ppm $(PPMWRITERS) $(TEXT_IO) --- 257,271 ---- # TEXT_IO = tiletext.o tiletxt.o $(NAMEOBJS) GIFREADERS = gifread.o ../src/alloc.o panic.o + XPMREADERS = xpmread.o ../src/alloc.o panic.o PPMWRITERS = ppmwrite.o ../src/alloc.o panic.o ! tileutils: tilemap gif2txt xpm2txt txt2ppm tile2x11 gif2txt: $(GIFREADERS) $(TEXT_IO) $(CC) $(LFLAGS) -o gif2txt $(GIFREADERS) $(TEXT_IO) + xpm2txt: $(XPMREADERS) $(TEXT_IO) + $(CC) $(LFLAGS) -o xpm2txt $(XPMREADERS) $(TEXT_IO) -lXpm -lX11 txt2ppm: $(PPMWRITERS) $(TEXT_IO) $(CC) $(LFLAGS) -o txt2ppm $(PPMWRITERS) $(TEXT_IO) *************** *** 284,289 **** --- 287,294 ---- gifread.o: ../win/share/gifread.c ../include/config.h ../include/tile.h $(CC) $(CFLAGS) -c ../win/share/gifread.c + xpmread.o: ../win/share/xpmread.c ../include/config.h ../include/tile.h + $(CC) $(CFLAGS) -c ../win/share/xpmread.c ppmwrite.o: ../win/share/ppmwrite.c ../include/config.h ../include/tile.h $(CC) $(CFLAGS) -c ../win/share/ppmwrite.c *************** *** 333,336 **** -rm -f ../include/lev_comp.h ../include/dgn_comp.h -rm -f ../include/tile.h -rm -f makedefs lev_comp dgn_comp recover dlb ! -rm -f gif2txt txt2ppm tile2x11 tilemap --- 338,341 ---- -rm -f ../include/lev_comp.h ../include/dgn_comp.h -rm -f ../include/tile.h -rm -f makedefs lev_comp dgn_comp recover dlb ! -rm -f gif2txt xpm2txt txt2ppm tile2x11 tilemap *** win/X11/tile2x11.c.orig Fri Jan 28 18:10:19 2000 --- win/X11/tile2x11.c Fri Feb 4 16:11:24 2000 *************** *** 123,132 **** FILE *fp; { int i,j,x,y; unsigned char *bytes; ! if (header.ncolors > 64) { ! Fprintf(stderr, "Sorry, only configured for up to 64 colors\n"); exit(1); /* All you need to do is add more char per color - below */ } --- 123,133 ---- FILE *fp; { int i,j,x,y; + char c[3]="?"; unsigned char *bytes; ! if (header.ncolors > 4096) { ! Fprintf(stderr, "Sorry, only configured for up to 4096 colors\n"); exit(1); /* All you need to do is add more char per color - below */ } *************** *** 137,149 **** header.tile_width*TILES_PER_ROW, header.tile_height*TILES_PER_COL, header.ncolors, ! 1 /* char per color */); ! for (i = 0; i < header.ncolors; i++) ! fprintf(fp, "\"%c c #%02x%02x%02x\",\n", ! i+'0', /* just one char per color */ x11_colormap[i][0], x11_colormap[i][1], x11_colormap[i][2]); for (j = 0; j < TILES_PER_COL; j++) for (y = 0; y < header.tile_height; y++) { --- 138,157 ---- header.tile_width*TILES_PER_ROW, header.tile_height*TILES_PER_COL, header.ncolors, ! header.ncolors > 64 ? 2 : 1 /* char per color */); ! for (i = 0; i < header.ncolors; i++) { ! if (header.ncolors > 64) { ! /* two chars per color */ ! c[0] = i / 64 + '0'; ! c[1] = i % 64 + '0'; ! } ! else ! c[0] = i + '0'; /* just one char per color */ ! fprintf(fp, "\"%s c #%02x%02x%02x\",\n", c, x11_colormap[i][0], x11_colormap[i][1], x11_colormap[i][2]); + } for (j = 0; j < TILES_PER_COL; j++) for (y = 0; y < header.tile_height; y++) { *************** *** 152,159 **** fprintf(fp, "\""); for (i = 0; i < TILES_PER_ROW; i++) { for (x = 0; x < header.tile_width; x++) { ! /* just one char per color */ ! fputc(bytes[x]+'0', fp); } bytes+=header.tile_height*header.tile_width; } --- 160,173 ---- fprintf(fp, "\""); for (i = 0; i < TILES_PER_ROW; i++) { for (x = 0; x < header.tile_width; x++) { ! if (header.ncolors > 64) { ! /* two chars per color */ ! c[0] = bytes[x] / 64 + '0'; ! c[1] = bytes[x] % 64 + '0'; ! } ! else ! c[0] = bytes[x] + '0'; /* just one char per color */ ! fputs(c, fp); } bytes+=header.tile_height*header.tile_width; } *** win/share/ppmwrite.c.orig Wed Jan 26 18:00:42 2000 --- win/share/ppmwrite.c Fri Feb 4 16:36:36 2000 *************** *** 1,5 **** /* this produces a raw ppm file, with a 15-character header of ! * "P6 3-digit-width 3-digit-height 255\n" */ #include "config.h" --- 1,5 ---- /* this produces a raw ppm file, with a 15-character header of ! * "P6 4-digit-width 4-digit-height 255\n" */ #include "config.h" *************** *** 22,32 **** static void FDECL(write_header, ()); static void FDECL(WriteTileStrip, ()); ! static void write_header() { ! (void) fprintf(ppm_file, "P6 %03d %03d 255\n", PpmScreen.Width, PpmScreen.Height); } static void --- 22,38 ---- static void FDECL(write_header, ()); static void FDECL(WriteTileStrip, ()); ! static int write_header() { ! if (PpmScreen.Width > 9999 || PpmScreen.Height > 9999) { ! /* Just increase the number of digits written to solve */ ! Fprintf(stderr, "PPM dimensions too large\n"); ! return FALSE; ! } ! (void) fprintf(ppm_file, "P6 %04d %04d 255\n", PpmScreen.Width, PpmScreen.Height); + return TRUE; } static void *************** *** 68,81 **** return FALSE; } - // tiles_across = 20; curr_tiles_across = 0; PpmScreen.Width = tiles_across * TILE_X; tiles_down = 0; PpmScreen.Height = 0; /* will be rewritten later */ ! write_header(); image = (pixel **)alloc(TILE_Y * sizeof(pixel *)); for (i = 0; i < TILE_Y; i++) { --- 74,87 ---- return FALSE; } curr_tiles_across = 0; PpmScreen.Width = tiles_across * TILE_X; tiles_down = 0; PpmScreen.Height = 0; /* will be rewritten later */ ! if (!write_header()) ! return FALSE; image = (pixel **)alloc(TILE_Y * sizeof(pixel *)); for (i = 0; i < TILE_Y; i++) { *************** *** 135,141 **** PpmScreen.Height = tiles_down * TILE_Y; rewind(ppm_file); ! write_header(); /* update size */ return(fclose(ppm_file)); } --- 141,148 ---- PpmScreen.Height = tiles_down * TILE_Y; rewind(ppm_file); ! if (!write_header()) /* update size */ ! return -1; return(fclose(ppm_file)); } *************** *** 153,174 **** tiles_across = 20; ! if (strcmp(argv[1],"-w")==0) { tiles_across=atoi(argv[2]); - // fprintf (stderr, "tiles across: %d\n",tiles_across); fileargs+=2; } if (argc-fileargs < 2) { ! Fprintf(stderr, "usage: txt2ppm [-w tiles-across] ppmfile txtfile ... \n"); exit(EXIT_FAILURE); } ppmfile=argv[fileargs++]; - // fprintf (stderr, "ppmfile = %s\n",ppmfile); while (fileargs < argc) { - // fprintf (stderr, "opening file %s\n",argv[fileargs]); if (!fopen_text_file(argv[fileargs++], RDTMODE)) exit(EXIT_FAILURE); --- 160,179 ---- tiles_across = 20; ! if (argc > 1 && !strcmp(argv[1],"-w")) { tiles_across=atoi(argv[2]); fileargs+=2; } if (argc-fileargs < 2) { ! Fprintf(stderr, ! "usage: txt2ppm [-w tiles-across] ppmfile txtfile ... \n"); exit(EXIT_FAILURE); } ppmfile=argv[fileargs++]; while (fileargs < argc) { if (!fopen_text_file(argv[fileargs++], RDTMODE)) exit(EXIT_FAILURE); *************** *** 185,192 **** (void) fclose_text_file(); } ! (void) fclose_ppm_file(); ! exit(EXIT_SUCCESS); /*NOTREACHED*/ return 0; } --- 190,199 ---- (void) fclose_text_file(); } ! if (fclose_ppm_file()) ! exit(EXIT_FAILURE); ! else ! exit(EXIT_SUCCESS); /*NOTREACHED*/ return 0; } *** win/share/thintile.c.orig Wed Jan 26 18:00:42 2000 --- win/share/thintile.c Fri Feb 4 16:41:58 2000 *************** *** 11,17 **** #include #endif ! static char pixels[TILE_Y][TILE_X]; static char *tilefiles[] = { "../win/share/monsters.txt", "../win/share/objects.txt", --- 11,17 ---- #include #endif ! static char pixels[TILE_Y][TILE_X][3]; static char *tilefiles[] = { "../win/share/monsters.txt", "../win/share/objects.txt", *************** *** 30,40 **** copy_colormap() { int i, r, g, b; ! char c[2]; ! while (fscanf(infile, "%[A-Za-z0-9] = (%d, %d, %d) ", c, &r, &g, &b) == 4) { ! Fprintf(outfile, "%c = (%d, %d, %d)\n", c[0], r, g, b); } } --- 30,40 ---- copy_colormap() { int i, r, g, b; ! char c[3]; ! while (fscanf(infile, "%2[_A-Za-z0-9$] = (%d, %d, %d) ", c, &r, &g, &b) == 4) { ! Fprintf(outfile, "%s = (%d, %d, %d)\n", c, r, g, b); } } *************** *** 43,50 **** { int i, j, k; char buf[BUFSZ]; ! const char *p; ! char c[2]; if (fscanf(infile, "# tile %d (%[^)])", &i, buf) <= 0) --- 43,50 ---- { int i, j, k; char buf[BUFSZ]; ! const char *p, *fmt_string; ! char c[3]; if (fscanf(infile, "# tile %d (%[^)])", &i, buf) <= 0) *************** *** 61,73 **** Fprintf(stderr, "didn't find expected '{'\n"); return FALSE; } for (j = 0; j < TILE_Y; j++) { for (i = 0; i < TILE_X; i++) { ! if (fscanf(infile, "%1s", c) < 0) { Fprintf(stderr, "unexpected EOF\n"); return FALSE; } - pixels[j][i] = c[0]; } } if (fscanf(infile, "%1s ", c) < 0) { --- 61,73 ---- Fprintf(stderr, "didn't find expected '{'\n"); return FALSE; } + fmt_string = num_colors > 64 ? "%2s" : "%1s"; for (j = 0; j < TILE_Y; j++) { for (i = 0; i < TILE_X; i++) { ! if (fscanf(infile, fmt_string, pixels[j][i]) < 0) { Fprintf(stderr, "unexpected EOF\n"); return FALSE; } } } if (fscanf(infile, "%1s ", c) < 0) { *************** *** 93,99 **** for (j = 0; j < TILE_Y; j++) { Fprintf(outfile, " "); for (i = 0; i < TILE_X; i += 2) { ! (void) fputc(pixels[j][i], outfile); } Fprintf(outfile, "\n"); } --- 93,99 ---- for (j = 0; j < TILE_Y; j++) { Fprintf(outfile, " "); for (i = 0; i < TILE_X; i += 2) { ! (void) fputs(pixels[j][i], outfile); } Fprintf(outfile, "\n"); } *** win/share/tile.doc.orig Wed Jan 26 18:00:42 2000 --- win/share/tile.doc Fri Feb 4 14:05:22 2000 *************** *** 19,24 **** --- 19,28 ---- ... } + Tile sets which require more than 62 colors can use two character color + keys, giving up to 4096 colors. All keys in any one file must be the + same size. + Each port can convert these .txt files to whatever format it wants the game executable to use, probably providing only one merged output file. See the tilemap.c discussion at the bottom for more hints on adding tiles. *************** *** 35,44 **** its own for reading and/or writing another format. The important global variables implement a colormap shared between tiletext.c and the other half of utilities. As an example of conversion utilities, we provide ! txt2ppm (tiletext.c + ppmwrite.c) and gif2txt (tiletext.c + gifread.c). ! (Sorry, we're not paying Unisys patent royalties for the right to provide ! you with a gifwrite.c, which would necessarily use the LZW compression ! algorithm they claim.) The text I/O routines are: --- 39,48 ---- its own for reading and/or writing another format. The important global variables implement a colormap shared between tiletext.c and the other half of utilities. As an example of conversion utilities, we provide ! txt2ppm (tiletext.c + ppmwrite.c), xpm2txt (tiletext.c + xpmread.c) ! and gif2txt (tiletext.c + gifread.c). (Sorry, we're not paying Unisys ! patent royalties for the right to provide you with a gifwrite.c, which ! would necessarily use the LZW compression algorithm they claim.) The text I/O routines are: *************** *** 68,74 **** file with a single common colormap, you may need to open each source file and merge their colormaps into a common colormap before processing any tiles. ! Although there are expected to be only 16 colors in the distribution tiles, conversion programs should be prepared to accept up to MAXCOLORMAPSIZE colors and map them to a smaller number if their port requires it. --- 72,78 ---- file with a single common colormap, you may need to open each source file and merge their colormaps into a common colormap before processing any tiles. ! Although there are expected to be only 256 colors in the distribution tiles, conversion programs should be prepared to accept up to MAXCOLORMAPSIZE colors and map them to a smaller number if their port requires it. *************** *** 112,117 **** --- 116,136 ---- remaining tiles are "blank"), otherwise TRUE and insert the tile in the provided array + The xpm I/O routines are: + + boolean fopen_xpm_file(const char *filename, const char *type); + select file for subsequent tile I/O + "type" a la fopen + returns FALSE if file not opened, otherwise reads xpm file + (including colormap) and sets up to decode tiles + int fclose_xpm_file(); + tear down decode mechanism + release resources + boolean read_xpm_tile(pixel[TILE_Y][TILE_X]); + returns FALSE if no next tile in current file (including when any + remaining tiles are "blank"), + otherwise TRUE and insert the tile in the provided array + Array provided by shared code for NetHack use, by compiling and running tilemap.c to form tile.c: *************** *** 124,131 **** tilemap.c (shudder) accounts for things disappearing due to compilation options -- there should be a tile for everything appearing under any supported option, but under some options some tiles won't be referenced. ! Therefore, tilemap.c has the knowledge to provide the comments for gif2txt ! and is compiled with GIF2TXT to link in there, along with the various strings for things that are compiled in (monst.o etc.). If you add monsters/objects/other things to NetHack and need to add tiles --- 143,150 ---- tilemap.c (shudder) accounts for things disappearing due to compilation options -- there should be a tile for everything appearing under any supported option, but under some options some tiles won't be referenced. ! Therefore, tilemap.c has the knowledge to provide the comments for xxx2txt ! and is compiled with TILETEXT to link in there, along with the various strings for things that are compiled in (monst.o etc.). If you add monsters/objects/other things to NetHack and need to add tiles *** win/share/tile.h.orig Wed Jan 26 18:00:42 2000 --- win/share/tile.h Fri Feb 4 16:26:31 2000 *************** *** 10,15 **** --- 10,17 ---- #define CM_GREEN 1 #define CM_BLUE 2 + #define DEFAULT_BACKGROUND { 71, 108, 108 } /* For transparancy */ + /* shared between reader and writer */ extern pixval ColorMap[3][MAXCOLORMAPSIZE]; extern int colorsinmap; *************** *** 20,29 **** --- 22,36 ---- #include "dlb.h" /* for MODEs */ /* size of tiles */ + #ifdef BIGTILE + #define TILE_X 32 + #define TILE_Y 32 + #else #ifndef TILE_X #define TILE_X 16 #endif #define TILE_Y 16 + #endif #define Fprintf (void) fprintf *** win/share/tilemap.c.orig Mon Jan 31 16:37:44 2000 --- win/share/tilemap.c Fri Feb 4 15:36:33 2000 *************** *** 22,27 **** --- 22,28 ---- #define MON_GLYPH 1 #define OBJ_GLYPH 2 #define OTH_GLYPH 3 /* fortunately unnecessary */ + #define COM_GLYPH 4 /* combined glyphs (for importing tile sets) */ /* note that the ifdefs here should be the opposite sense from monst.c/ * objects.c/rm.h *************** *** 197,202 **** --- 198,204 ---- int set, entry; { int i, j, condnum, tilenum; + int in_set; static char buf[BUFSZ]; /* Note: these initializers don't do anything except guarantee that *************** *** 208,233 **** condnum = tilenum = 0; for (i = 0; i < NUMMONS; i++) { ! if (set == MON_GLYPH && tilenum == entry) return mons[i].mname; tilenum++; while (conditionals[condnum].sequence == MON_GLYPH && conditionals[condnum].predecessor == i) { ! if (set == MON_GLYPH && tilenum == entry) return conditionals[condnum].name; condnum++; tilenum++; } } ! if (set == MON_GLYPH && tilenum == entry) return "invisible monster"; ! tilenum = 0; /* set-relative number */ for (i = 0; i < NUM_OBJECTS; i++) { /* prefer to give the description - that's all the tile's * appearance should reveal */ ! if (set == OBJ_GLYPH && tilenum == entry) { if ( !obj_descr[i].oc_descr ) return obj_descr[i].oc_name; if ( !obj_descr[i].oc_name ) --- 210,239 ---- condnum = tilenum = 0; + in_set = set == MON_GLYPH || set == COM_GLYPH; for (i = 0; i < NUMMONS; i++) { ! if (in_set && tilenum == entry) return mons[i].mname; tilenum++; while (conditionals[condnum].sequence == MON_GLYPH && conditionals[condnum].predecessor == i) { ! if (in_set && tilenum == entry) return conditionals[condnum].name; condnum++; tilenum++; } } ! if (in_set && tilenum == entry) return "invisible monster"; + tilenum++; ! if (set != COM_GLYPH) ! tilenum = 0; /* set-relative number */ ! in_set = set == OBJ_GLYPH || set == COM_GLYPH; for (i = 0; i < NUM_OBJECTS; i++) { /* prefer to give the description - that's all the tile's * appearance should reveal */ ! if (in_set && tilenum == entry) { if ( !obj_descr[i].oc_descr ) return obj_descr[i].oc_name; if ( !obj_descr[i].oc_name ) *************** *** 242,257 **** tilenum++; while (conditionals[condnum].sequence == OBJ_GLYPH && conditionals[condnum].predecessor == i) { ! if (set == OBJ_GLYPH && tilenum == entry) return conditionals[condnum].name; condnum++; tilenum++; } } ! tilenum = 0; /* set-relative number */ for (i = 0; i < MAXPCHARS; i++) { ! if (set == OTH_GLYPH && tilenum == entry) if (*defsyms[i].explanation) return defsyms[i].explanation; else { --- 248,265 ---- tilenum++; while (conditionals[condnum].sequence == OBJ_GLYPH && conditionals[condnum].predecessor == i) { ! if (in_set && tilenum == entry) return conditionals[condnum].name; condnum++; tilenum++; } } ! if (set != COM_GLYPH) ! tilenum = 0; /* set-relative number */ ! in_set = set == OTH_GLYPH || set == COM_GLYPH; for (i = 0; i < MAXPCHARS; i++) { ! if (in_set && tilenum == entry) if (*defsyms[i].explanation) return defsyms[i].explanation; else { *************** *** 274,280 **** tilenum++; while (conditionals[condnum].sequence == OTH_GLYPH && conditionals[condnum].predecessor == i) { ! if (set == OTH_GLYPH && tilenum == entry) return conditionals[condnum].name; condnum++; tilenum++; --- 282,288 ---- tilenum++; while (conditionals[condnum].sequence == OTH_GLYPH && conditionals[condnum].predecessor == i) { ! if (in_set && tilenum == entry) return conditionals[condnum].name; condnum++; tilenum++; *************** *** 283,289 **** i = entry - tilenum; if (i < (NUM_ZAP << 2)) { ! if (set == OTH_GLYPH) { Sprintf(buf, "zap %d %d", i/4, i%4); return buf; } --- 291,297 ---- i = entry - tilenum; if (i < (NUM_ZAP << 2)) { ! if (in_set) { Sprintf(buf, "zap %d %d", i/4, i%4); return buf; } *************** *** 293,299 **** for (i = 0; i < SIZE(substitutes); i++) { j = entry - tilenum; if (j <= substitutes[i].last_glyph - substitutes[i].first_glyph) { ! if (set == OTH_GLYPH) { Sprintf(buf, "sub %s %d", substitutes[i].sub_name, j); return buf; } --- 301,307 ---- for (i = 0; i < SIZE(substitutes); i++) { j = entry - tilenum; if (j <= substitutes[i].last_glyph - substitutes[i].first_glyph) { ! if (in_set) { Sprintf(buf, "sub %s %d", substitutes[i].sub_name, j); return buf; } *** win/share/tiletext.c.orig Wed Jan 26 18:00:42 2000 --- win/share/tiletext.c Fri Feb 4 14:45:07 2000 *************** *** 4,27 **** #include "config.h" #include "tile.h" pixval ColorMap[3][MAXCOLORMAPSIZE]; int colorsinmap; pixval MainColorMap[3][MAXCOLORMAPSIZE]; int colorsinmainmap; ! static short color_index[MAXCOLORMAPSIZE]; static int num_colors; ! static char charcolors[MAXCOLORMAPSIZE]; static int placeholder_init = 0; static pixel placeholder[TILE_Y][TILE_X]; static FILE *tile_file; static int tile_set, tile_set_indx; #if (TILE_X==8) ! static char *text_sets[] = { "monthin.txt", "objthin.txt", "oththin.txt" }; #else ! static char *text_sets[] = { "monsters.txt", "objects.txt", "other.txt" }; #endif extern const char *FDECL(tilename, (int, int)); static void FDECL(read_text_colormap, (FILE *)); --- 4,49 ---- #include "config.h" #include "tile.h" + /* + * TEXTCOLORMAPSPACE + * + * This is the maximum number of possible unique colours in a .TXT file + * file. MAXCOLORMAPSIZE may be set (in tile.h) to be smaller than this + * in which case only that many unique colours may be present in a .TXT + * file (and in all merged .TXT files). MAXCOLORMAPSIZE may be larger + * than this without penalty. A value of n*TEXTCOLORMAPSPACE where n is + * the number of .TXT files that are going to be merged (currently three; + * monsters, objects & others) produces maximum generality. Increasing + * MAXCOLORMAPSIZE has performance issues for the game (TEXTCOLORMAPSPACE + * does not). Windowing systems are required to cope with MAXCOLORMAPSIZE + * colours and map them onto their possible palette (see tile.doc) so this + * also introduces an additional burden. + */ + + #define TEXTCOLORMAPSPACE 4096 + pixval ColorMap[3][MAXCOLORMAPSIZE]; int colorsinmap; pixval MainColorMap[3][MAXCOLORMAPSIZE]; int colorsinmainmap; ! static short color_index[TEXTCOLORMAPSPACE]; static int num_colors; ! static char charcolors[MAXCOLORMAPSIZE][3]; static int placeholder_init = 0; static pixel placeholder[TILE_Y][TILE_X]; static FILE *tile_file; static int tile_set, tile_set_indx; + static char *text_sets[] = { #if (TILE_X==8) ! "monthin.txt", "objthin.txt", "oththin.txt", "comthin.txt" ! #elif (TILE_X==32 && TILE_Y==32) ! "mon32.txt", "obj32.txt", "oth32.txt", "com32.txt" #else ! "monsters.txt", "objects.txt", "other.txt", "combined.txt" #endif + }; extern const char *FDECL(tilename, (int, int)); static void FDECL(read_text_colormap, (FILE *)); *************** *** 29,51 **** static boolean FDECL(read_txttile, (FILE *, pixel(*)[TILE_X])); static void FDECL(write_txttile, (FILE *, pixel(*)[TILE_X])); /* Ugh. DICE doesn't like %[A-Z], so we have to spell it out... */ #define FORMAT_STRING \ ! "%[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789] = (%d, %d, %d) " static void read_text_colormap(txtfile) FILE *txtfile; { ! int i, r, g, b; ! char c[2]; ! for (i = 0; i < MAXCOLORMAPSIZE; i++) color_index[i] = -1; num_colors = 0; while (fscanf(txtfile, FORMAT_STRING, c, &r, &g, &b) == 4) { ! color_index[(int) c[0]] = num_colors; ColorMap[CM_RED][num_colors] = r; ColorMap[CM_GREEN][num_colors] = g; ColorMap[CM_BLUE][num_colors] = b; --- 51,134 ---- static boolean FDECL(read_txttile, (FILE *, pixel(*)[TILE_X])); static void FDECL(write_txttile, (FILE *, pixel(*)[TILE_X])); + /* + * ALI + * + * Support for 4096 colours. + * + * Issues: + * - NetHack 3.2/3.3 .TXT readers have a bug which causes them + * to overflow the "c" array in read_text_colormap if more than + * one character is encountered in the set A-Za-z0-9 where it + * is expecting a colour key. This is because no maximum field + * width has been specified for the %[...] scan format. + * + * Design goals: + * - Capable of reading and writing 62/4096 colour files + * - Writes 62 files which NetHack 3.2/3.3 can read. + * - Writes 4096 files which NetHack 3.2/3.3 fails gracefully on. + * + * We achieve this by introducing two new legal characters "_" and "$". + * This brings the number of legal characters to 64 which is a nice round + * number and also means that as long as we arrange for the first colour + * key in a 4096 file to start with "_", 3.2/3.3 readers will fail without + * crashing. Instead they will see no valid colour map. The error message + * thus generated (no colormap set yet) is admittedly not very informative. + */ + /* Ugh. DICE doesn't like %[A-Z], so we have to spell it out... */ #define FORMAT_STRING \ ! "%2[_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789$] = (%d, %d, %d) " ! ! static char bysx2char(i) ! int i; ! { ! char c; ! if (!i) c = '_'; ! else if (i < 27) c = 'A' + i - 1; ! else if (i < 53) c = 'a' + i - 27; ! else if (i < 63) c = '0' + i - 53; ! else c = '$'; ! return c; ! } ! ! static int char2bysx(c) ! char c; ! { ! int i; ! if ( c == '_' ) i = 0; ! else if (c >= 'A' && c <= 'Z') i = c - 'A' + 1; ! else if (c >= 'a' && c <= 'z') i = c - 'a' + 27; ! else if (c >= '0' && c <= '9') i = c - '0' + 53; ! else if ( c == '$' ) i = 63; ! else i = -1; ! return i; ! } static void read_text_colormap(txtfile) FILE *txtfile; { ! int i, n, r, g, b; ! char c[3]; ! for (i = 0; i < TEXTCOLORMAPSPACE; i++) color_index[i] = -1; num_colors = 0; while (fscanf(txtfile, FORMAT_STRING, c, &r, &g, &b) == 4) { ! if (c[1]) ! n=char2bysx(c[0]) * 64 + char2bysx(c[1]); ! else ! n=char2bysx(c[0]); ! if (n >= 0 && n < TEXTCOLORMAPSPACE) ! color_index[n] = num_colors; ! else ! { ! Fprintf(stderr, "error: Illegal color in colormap %s\n", ! c); ! continue; ! } ColorMap[CM_RED][num_colors] = r; ColorMap[CM_GREEN][num_colors] = g; ColorMap[CM_BLUE][num_colors] = b; *************** *** 61,80 **** FILE *txtfile; { int i; ! char c; num_colors = colorsinmainmap; ! if (num_colors > 62) { Fprintf(stderr, "too many colors (%d)\n", num_colors); return FALSE; } for (i = 0; i < num_colors; i++) { ! if (i < 26) c = 'A' + i; ! else if (i < 52) c = 'a' + i - 26; ! else c = '0' + i - 52; ! charcolors[i] = c; ! Fprintf(txtfile, "%c = (%d, %d, %d)\n", c, (int)MainColorMap[CM_RED][i], (int)MainColorMap[CM_GREEN][i], (int)MainColorMap[CM_BLUE][i]); --- 144,167 ---- FILE *txtfile; { int i; ! char c[3]="?"; num_colors = colorsinmainmap; ! if (num_colors > MAXCOLORMAPSIZE) { Fprintf(stderr, "too many colors (%d)\n", num_colors); return FALSE; } for (i = 0; i < num_colors; i++) { ! if (num_colors > 62) ! { ! c[0] = bysx2char(i / 64); ! c[1] = bysx2char(i % 64); ! } ! else ! c[0] = bysx2char(i + 1); ! strcpy(charcolors[i], c); ! Fprintf(txtfile, "%s = (%d, %d, %d)\n", c, (int)MainColorMap[CM_RED][i], (int)MainColorMap[CM_GREEN][i], (int)MainColorMap[CM_BLUE][i]); *************** *** 87,96 **** FILE *txtfile; pixel (*pixels)[TILE_X]; { ! int ph, i, j, k; char buf[BUFSZ], ttype[BUFSZ]; ! const char *p; ! char c[2]; if (fscanf(txtfile, "# %s %d (%[^)])", ttype, &i, buf) <= 0) return FALSE; --- 174,184 ---- FILE *txtfile; pixel (*pixels)[TILE_X]; { ! int ph, i, j, k, n; ! int tile_no; char buf[BUFSZ], ttype[BUFSZ]; ! const char *p, *fmt_string; ! char c[3]; if (fscanf(txtfile, "# %s %d (%[^)])", ttype, &i, buf) <= 0) return FALSE; *************** *** 114,119 **** --- 202,208 ---- buf, p); } } + tile_no=i; tile_set_indx++; /* look for non-whitespace at each stage */ *************** *** 125,140 **** Fprintf(stderr, "didn't find expected '{'\n"); return FALSE; } for (j = 0; j < TILE_Y; j++) { for (i = 0; i < TILE_X; i++) { ! if (fscanf(txtfile, "%1s", c) < 0) { Fprintf(stderr, "unexpected EOF\n"); return FALSE; } ! k = color_index[(int) c[0]]; if (k == -1) Fprintf(stderr, ! "color %c not in colormap!\n", c[0]); else { pixels[j][i].r = ColorMap[CM_RED][k]; pixels[j][i].g = ColorMap[CM_GREEN][k]; --- 214,237 ---- Fprintf(stderr, "didn't find expected '{'\n"); return FALSE; } + fmt_string = num_colors > 64 ? "%2s" : "%1s"; for (j = 0; j < TILE_Y; j++) { for (i = 0; i < TILE_X; i++) { ! if (fscanf(txtfile, fmt_string, c) < 0) { Fprintf(stderr, "unexpected EOF\n"); return FALSE; } ! if (c[1]) ! n=char2bysx(c[0]) * 64 + char2bysx(c[1]); ! else ! n=char2bysx(c[0]); ! if (n >= 0 && n < TEXTCOLORMAPSPACE) ! k = color_index[n]; ! else ! k = -1; if (k == -1) Fprintf(stderr, ! "color %s not in colormap!\n", c); else { pixels[j][i].r = ColorMap[CM_RED][k]; pixels[j][i].g = ColorMap[CM_GREEN][k]; *************** *** 202,208 **** } if (k >= num_colors) Fprintf(stderr, "color not in colormap!\n"); ! (void) fputc(charcolors[k], txtfile); } Fprintf(txtfile, "\n"); } --- 299,305 ---- } if (k >= num_colors) Fprintf(stderr, "color not in colormap!\n"); ! (void) fputs(charcolors[k], txtfile); } Fprintf(txtfile, "\n"); } *** win/share/xpmread.c.orig Thu Jan 1 01:00:00 1970 --- win/share/xpmread.c Fri Feb 4 16:27:21 2000 *************** *** 0 **** --- 1,202 ---- + /* NetHack may be freely redistributed. See license for details. */ + + #include + #include "config.h" + #include "tile.h" + + #ifndef MONITOR_HEAP + extern long *FDECL(alloc, (unsigned int)); + #endif + + static int tiles_across, tiles_down, curr_tiles_across, curr_tiles_down; + static XpmImage image; + static XpmInfo info; + + /* Maybe these should be in xpm.h, but there isn't one. */ + boolean FDECL(fopen_xpm_file, (char *, char *)); + boolean FDECL(read_xpm_tile, (pixel(*)[])); + int NDECL(fclose_xpm_file); + + boolean + fopen_xpm_file(filename, type) + char *filename; + char *type; + { + int i,n; + int errorcode; + char fmt[20]; + unsigned long r, g, b; + const pixel bg = DEFAULT_BACKGROUND; + + if (strcmp(type, RDBMODE)) { + Fprintf(stderr, "using reading routine for non-reading?\n"); + return FALSE; + } + errorcode = XpmReadFileToXpmImage(filename, &image, &info); + if (errorcode != XpmSuccess) { + Fprintf(stderr, "cannot open xpm file %s: %s\n", filename, + XpmGetErrorString(errorcode)); + return FALSE; + } + + if (image.width % TILE_X) { + Fprintf(stderr, "error: width %d not divisible by %d\n", + image.width, TILE_X); + exit(EXIT_FAILURE); + } + tiles_across = image.width / TILE_X; + curr_tiles_across = 0; + if (image.height % TILE_Y) { + Fprintf(stderr, "error: height %d not divisible by %d\n", + image.height, TILE_Y); + /* exit(EXIT_FAILURE) */; + } + tiles_down = image.height / TILE_Y; + curr_tiles_down = 0; + + for (i = 0; i < image.ncolors; ++i) { + if (!strcmp(image.colorTable[i].c_color, "None")) + { + ColorMap[CM_RED][i] = bg.r; + ColorMap[CM_GREEN][i] = bg.g; + ColorMap[CM_BLUE][i] = bg.b; + } + else if (image.colorTable[i].c_color[0] == '#') + { + n = strlen(image.colorTable[i].c_color + 1); + if (n % 3 || n > 12 || n < 3) + { + Fprintf(stderr, "error: Unknown color defn for %s (%s)\n", + image.colorTable[i].string, image.colorTable[i].c_color); + exit(EXIT_FAILURE); + } + n /= 3; + sprintf(fmt, "%%0%dlx%%0%dlx%%0%dlx", n, n, n); + if (sscanf(image.colorTable[i].c_color + 1, fmt, &r, &g, &b) != 3) + { + Fprintf(stderr, "error: Unknown color defn for %s (%s)\n", + image.colorTable[i].string, image.colorTable[i].c_color); + exit(EXIT_FAILURE); + } + if (n>=2) + { + ColorMap[CM_RED][i]=r>>(n*4-8); + ColorMap[CM_GREEN][i]=g>>(n*4-8); + ColorMap[CM_BLUE][i]=b>>(n*4-8); + } + else + { + ColorMap[CM_RED][i]=r<<4; + ColorMap[CM_GREEN][i]=g<<4; + ColorMap[CM_BLUE][i]=b<<4; + } + } + else + { + Fprintf(stderr, "error: Unknown color defn for %s (%s)\n", + image.colorTable[i].string, image.colorTable[i].c_color); + exit(EXIT_FAILURE); + } + } + colorsinmap = image.ncolors; + + return TRUE; + } + + /* Read a tile. Returns FALSE when there are no more tiles */ + boolean + read_xpm_tile(pixels) + pixel (*pixels)[TILE_X]; + { + int i, j; + unsigned int *src; + + if (curr_tiles_down >= tiles_down) return FALSE; + if (curr_tiles_across == tiles_across) { + curr_tiles_across = 0; + curr_tiles_down++; + if (curr_tiles_down >= tiles_down) return FALSE; + } + src = image.data + curr_tiles_down * TILE_Y * image.width + + curr_tiles_across * TILE_X; + for (j = 0; j < TILE_Y; j++) { + for (i = 0; i < TILE_X; i++) { + pixels[j][i].r = ColorMap[CM_RED][src[j * image.width + i]]; + pixels[j][i].g = ColorMap[CM_GREEN][src[j * image.width + i]]; + pixels[j][i].b = ColorMap[CM_BLUE][src[j * image.width + i]]; + } + } + curr_tiles_across++; + + /* check for "filler" tile */ + for (j = 0; j < TILE_Y; j++) { + for (i = 0; i < TILE_X && i < 4; i += 2) { + if (pixels[j][i].r != ColorMap[CM_RED][0] || + pixels[j][i].g != ColorMap[CM_GREEN][0] || + pixels[j][i].b != ColorMap[CM_BLUE][0] || + pixels[j][i+1].r != ColorMap[CM_RED][1] || + pixels[j][i+1].g != ColorMap[CM_GREEN][1] || + pixels[j][i+1].b != ColorMap[CM_BLUE][1]) + return TRUE; + } + } + return FALSE; + } + + int + fclose_xpm_file() + { + int i; + + XpmFreeXpmImage(&image); + XpmFreeXpmInfo(&info); + + return FALSE; + } + + #ifndef AMIGA + static char *std_args[] = { "tilemap", /* dummy argv[0] */ + "monsters.xpm", "monsters.txt", + "objects.xpm", "objects.txt", + "other.xpm", "other.txt" }; + + int + main(argc, argv) + int argc; + char *argv[]; + { + pixel pixels[TILE_Y][TILE_X]; + + if (argc == 1) { + argc = SIZE(std_args); + argv = std_args; + } else if (argc != 3) { + Fprintf(stderr, "usage: xpm2txt xpmfile txtfile\n"); + exit(EXIT_FAILURE); + } + + while (argc > 1) { + if (!fopen_xpm_file(argv[1], RDBMODE)) + exit(EXIT_FAILURE); + + init_colormap(); + + if (!fopen_text_file(argv[2], WRTMODE)) { + (void) fclose_xpm_file(); + exit(EXIT_FAILURE); + } + + while (read_xpm_tile(pixels)) + (void) write_text_tile(pixels); + + (void) fclose_xpm_file(); + (void) fclose_text_file(); + + argc -= 2; + argv += 2; + } + exit(EXIT_SUCCESS); + /*NOTREACHED*/ + return 0; + } + #endif