/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * JPEG image viewer * (This is a real mess if it has to be coded in one single C file) * * File scrolling addition (C) 2005 Alexander Spyridakis * Copyright (C) 2004 J�g Hohensohn aka [IDC]Dragon * Grayscale framework (C) 2004 Jens Arnold * Heavily borrowed from the IJG implementation (C) Thomas G. Lane * Small & fast downscaling IDCT (C) 2002 by Guido Vollbeding JPEGclub.org * * All files in this archive are subject to the GNU General Public License. * See the file COPYING in the source tree root for full license agreement. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "plugin.h" #include "playback_control.h" #include "oldmenuapi.h" #include "helper.h" #ifdef HAVE_LCD_BITMAP #include "gray.h" #include "xlcd.h" #ifdef HAVE_LCD_COLOR #include "lib/configfile.h" #endif PLUGIN_HEADER /* variable button definitions */ #if CONFIG_KEYPAD == RECORDER_PAD #define JPEG_ZOOM_IN BUTTON_PLAY #define JPEG_ZOOM_OUT BUTTON_ON #define JPEG_UP BUTTON_UP #define JPEG_DOWN BUTTON_DOWN #define JPEG_LEFT BUTTON_LEFT #define JPEG_RIGHT BUTTON_RIGHT #define JPEG_NEXT BUTTON_F3 #define JPEG_PREVIOUS BUTTON_F2 #define JPEG_MENU BUTTON_OFF #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD #define JPEG_ZOOM_IN BUTTON_SELECT #define JPEG_ZOOM_OUT BUTTON_ON #define JPEG_UP BUTTON_UP #define JPEG_DOWN BUTTON_DOWN #define JPEG_LEFT BUTTON_LEFT #define JPEG_RIGHT BUTTON_RIGHT #define JPEG_NEXT BUTTON_F3 #define JPEG_PREVIOUS BUTTON_F2 #define JPEG_MENU BUTTON_OFF #elif CONFIG_KEYPAD == ONDIO_PAD #define JPEG_ZOOM_PRE BUTTON_MENU #define JPEG_ZOOM_IN (BUTTON_MENU | BUTTON_REL) #define JPEG_ZOOM_OUT (BUTTON_MENU | BUTTON_DOWN) #define JPEG_UP BUTTON_UP #define JPEG_DOWN BUTTON_DOWN #define JPEG_LEFT BUTTON_LEFT #define JPEG_RIGHT BUTTON_RIGHT #define JPEG_NEXT (BUTTON_MENU | BUTTON_RIGHT) #define JPEG_PREVIOUS (BUTTON_MENU | BUTTON_LEFT) #define JPEG_MENU BUTTON_OFF #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \ (CONFIG_KEYPAD == IRIVER_H300_PAD) #define JPEG_ZOOM_IN BUTTON_SELECT #define JPEG_ZOOM_OUT BUTTON_MODE #define JPEG_UP BUTTON_UP #define JPEG_DOWN BUTTON_DOWN #define JPEG_LEFT BUTTON_LEFT #define JPEG_RIGHT BUTTON_RIGHT #if (CONFIG_KEYPAD == IRIVER_H100_PAD) #define JPEG_NEXT BUTTON_ON #define JPEG_PREVIOUS BUTTON_REC #else #define JPEG_NEXT BUTTON_REC #define JPEG_PREVIOUS BUTTON_ON #endif #define JPEG_MENU BUTTON_OFF #define JPEG_RC_MENU BUTTON_RC_STOP #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \ (CONFIG_KEYPAD == IPOD_1G2G_PAD) #define JPEG_ZOOM_IN BUTTON_SCROLL_FWD #define JPEG_ZOOM_OUT BUTTON_SCROLL_BACK #define JPEG_UP BUTTON_MENU #define JPEG_DOWN BUTTON_PLAY #define JPEG_LEFT BUTTON_LEFT #define JPEG_RIGHT BUTTON_RIGHT #define JPEG_MENU (BUTTON_SELECT | BUTTON_MENU) #define JPEG_NEXT (BUTTON_SELECT | BUTTON_RIGHT) #define JPEG_PREVIOUS (BUTTON_SELECT | BUTTON_LEFT) #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD #define JPEG_ZOOM_PRE BUTTON_SELECT #define JPEG_ZOOM_IN (BUTTON_SELECT | BUTTON_REL) #define JPEG_ZOOM_OUT (BUTTON_SELECT | BUTTON_REPEAT) #define JPEG_UP BUTTON_UP #define JPEG_DOWN BUTTON_DOWN #define JPEG_LEFT BUTTON_LEFT #define JPEG_RIGHT BUTTON_RIGHT #define JPEG_MENU BUTTON_POWER #define JPEG_NEXT BUTTON_PLAY #define JPEG_PREVIOUS BUTTON_REC #elif CONFIG_KEYPAD == GIGABEAT_PAD #define JPEG_ZOOM_IN BUTTON_VOL_UP #define JPEG_ZOOM_OUT BUTTON_VOL_DOWN #define JPEG_UP BUTTON_UP #define JPEG_DOWN BUTTON_DOWN #define JPEG_LEFT BUTTON_LEFT #define JPEG_RIGHT BUTTON_RIGHT #define JPEG_MENU BUTTON_MENU #define JPEG_NEXT (BUTTON_A | BUTTON_RIGHT) #define JPEG_PREVIOUS (BUTTON_A | BUTTON_LEFT) #elif CONFIG_KEYPAD == SANSA_E200_PAD #define JPEG_ZOOM_PRE BUTTON_SELECT #define JPEG_ZOOM_IN (BUTTON_SELECT | BUTTON_REL) #define JPEG_ZOOM_OUT (BUTTON_SELECT | BUTTON_REPEAT) #define JPEG_UP BUTTON_UP #define JPEG_DOWN BUTTON_DOWN #define JPEG_LEFT BUTTON_LEFT #define JPEG_RIGHT BUTTON_RIGHT #define JPEG_MENU BUTTON_POWER #define JPEG_SLIDE_SHOW BUTTON_REC #define JPEG_NEXT BUTTON_SCROLL_DOWN #define JPEG_NEXT_REPEAT (BUTTON_SCROLL_DOWN|BUTTON_REPEAT) #define JPEG_PREVIOUS BUTTON_SCROLL_UP #define JPEG_PREVIOUS_REPEAT (BUTTON_SCROLL_UP|BUTTON_REPEAT) #elif CONFIG_KEYPAD == SANSA_C200_PAD #define JPEG_ZOOM_PRE BUTTON_SELECT #define JPEG_ZOOM_IN (BUTTON_SELECT | BUTTON_REL) #define JPEG_ZOOM_OUT (BUTTON_SELECT | BUTTON_REPEAT) #define JPEG_UP BUTTON_UP #define JPEG_DOWN BUTTON_DOWN #define JPEG_LEFT BUTTON_LEFT #define JPEG_RIGHT BUTTON_RIGHT #define JPEG_MENU BUTTON_POWER #define JPEG_SLIDE_SHOW BUTTON_REC #define JPEG_NEXT BUTTON_VOL_UP #define JPEG_NEXT_REPEAT (BUTTON_VOL_UP|BUTTON_REPEAT) #define JPEG_PREVIOUS BUTTON_VOL_DOWN #define JPEG_PREVIOUS_REPEAT (BUTTON_VOL_DOWN|BUTTON_REPEAT) #elif CONFIG_KEYPAD == IRIVER_H10_PAD #define JPEG_ZOOM_PRE BUTTON_PLAY #define JPEG_ZOOM_IN (BUTTON_PLAY | BUTTON_REL) #define JPEG_ZOOM_OUT (BUTTON_PLAY | BUTTON_REPEAT) #define JPEG_UP BUTTON_SCROLL_UP #define JPEG_DOWN BUTTON_SCROLL_DOWN #define JPEG_LEFT BUTTON_LEFT #define JPEG_RIGHT BUTTON_RIGHT #define JPEG_MENU BUTTON_POWER #define JPEG_NEXT BUTTON_FF #define JPEG_PREVIOUS BUTTON_REW #endif /* different graphics libraries */ #if LCD_DEPTH < 8 #define USEGSLIB #define MYLCD(fn) gray_ub_ ## fn #define MYLCD_UPDATE() #define MYXLCD(fn) gray_ub_ ## fn #else #define MYLCD(fn) rb->lcd_ ## fn #define MYLCD_UPDATE() rb->lcd_update(); #define MYXLCD(fn) xlcd_ ## fn #endif #define MAX_X_SIZE LCD_WIDTH*8 /* Min memory allowing us to use the plugin buffer * and thus not stopping the music * *Very* rough estimation: * Max 10 000 dir entries * 4bytes/entry (char **) = 40000 bytes * + 20k code size = 60 000 * + 50k min for jpeg = 120 000 */ #define MIN_MEM 120000 /* Headings */ #define DIR_PREV 1 #define DIR_NEXT -1 #define DIR_NONE 0 #define PLUGIN_OTHER 10 /* State code for output with return. */ /******************************* Globals ***********************************/ static struct plugin_api* rb; MEM_FUNCTION_WRAPPERS(rb); /* for portability of below JPEG code */ #define MEMSET(p,v,c) rb->memset(p,v,c) #define MEMCPY(d,s,c) rb->memcpy(d,s,c) #define INLINE static inline #define ENDIAN_SWAP16(n) n /* only for poor little endian machines */ static int slideshow_enabled = false; /* run slideshow */ static int running_slideshow = false; /* loading image because of slideshw */ #ifndef SIMULATOR static int immediate_ata_off = false; /* power down disk after loading */ #endif static int button_timeout = HZ*5; #ifdef HAVE_LCD_COLOR /* Persistent configuration - only needed for color displays atm */ #define JPEG_CONFIGFILE "jpeg.cfg" #define JPEG_SETTINGS_MINVERSION 1 #define JPEG_SETTINGS_VERSION 1 enum color_modes { COLOURMODE_COLOUR = 0, COLOURMODE_GRAY, COLOUR_NUM_MODES }; enum dither_modes { DITHER_NONE = 0, /* No dithering */ DITHER_ORDERED, /* Bayer ordered */ DITHER_DIFFUSION, /* Floyd/Steinberg error diffusion */ DITHER_NUM_MODES }; struct jpeg_settings { int colour_mode; int dither_mode; }; static struct jpeg_settings jpeg_settings = { COLOURMODE_COLOUR, DITHER_NONE }; static struct jpeg_settings old_settings; static struct configdata jpeg_config[] = { { TYPE_ENUM, 0, COLOUR_NUM_MODES, &jpeg_settings.colour_mode, "Colour Mode", (char *[]){ "Colour", "Grayscale" }, NULL }, { TYPE_ENUM, 0, DITHER_NUM_MODES, &jpeg_settings.dither_mode, "Dither Mode", (char *[]){ "None", "Ordered", "Diffusion" }, NULL }, }; #endif /* HAVE_LCD_COLOR */ #if LCD_DEPTH > 1 fb_data* old_backdrop; #endif /**************** begin JPEG code ********************/ INLINE unsigned range_limit(int value) { #if CONFIG_CPU == SH7034 unsigned tmp; asm ( /* Note: Uses knowledge that only low byte of result is used */ "mov #-128,%[t] \n" "sub %[t],%[v] \n" /* value -= -128; equals value += 128; */ "extu.b %[v],%[t] \n" "cmp/eq %[v],%[t] \n" /* low byte == whole number ? */ "bt 1f \n" /* yes: no overflow */ "cmp/pz %[v] \n" /* overflow: positive? */ "subc %[v],%[v] \n" /* %[r] now either 0 or 0xffffffff */ "1: \n" : /* outputs */ [v]"+r"(value), [t]"=&r"(tmp) ); return value; #elif defined(CPU_COLDFIRE) asm ( /* Note: Uses knowledge that only the low byte of the result is used */ "add.l #128,%[v] \n" /* value += 128; */ "cmp.l #255,%[v] \n" /* overflow? */ "bls.b 1f \n" /* no: return value */ "spl.b %[v] \n" /* yes: set low byte to appropriate boundary */ "1: \n" : /* outputs */ [v]"+d"(value) ); return value; #elif defined(CPU_ARM) asm ( /* Note: Uses knowledge that only the low byte of the result is used */ "add %[v], %[v], #128 \n" /* value += 128 */ "cmp %[v], #255 \n" /* out of range 0..255? */ "mvnhi %[v], %[v], asr #31 \n" /* yes: set all bits to ~(sign_bit) */ : /* outputs */ [v]"+r"(value) ); return value; #else value += 128; if ((unsigned)value <= 255) return value; if (value < 0) return 0; return 255; #endif } /* IDCT implementation */ #define CONST_BITS 13 #define PASS1_BITS 2 /* Some C compilers fail to reduce "FIX(constant)" at compile time, thus * causing a lot of useless floating-point operations at run time. * To get around this we use the following pre-calculated constants. * If you change CONST_BITS you may want to add appropriate values. * (With a reasonable C compiler, you can just rely on the FIX() macro...) */ #define FIX_0_298631336 2446 /* FIX(0.298631336) */ #define FIX_0_390180644 3196 /* FIX(0.390180644) */ #define FIX_0_541196100 4433 /* FIX(0.541196100) */ #define FIX_0_765366865 6270 /* FIX(0.765366865) */ #define FIX_0_899976223 7373 /* FIX(0.899976223) */ #define FIX_1_175875602 9633 /* FIX(1.175875602) */ #define FIX_1_501321110 12299 /* FIX(1.501321110) */ #define FIX_1_847759065 15137 /* FIX(1.847759065) */ #define FIX_1_961570560 16069 /* FIX(1.961570560) */ #define FIX_2_053119869 16819 /* FIX(2.053119869) */ #define FIX_2_562915447 20995 /* FIX(2.562915447) */ #define FIX_3_072711026 25172 /* FIX(3.072711026) */ /* Multiply an long variable by an long constant to yield an long result. * For 8-bit samples with the recommended scaling, all the variable * and constant values involved are no more than 16 bits wide, so a * 16x16->32 bit multiply can be used instead of a full 32x32 multiply. * For 12-bit samples, a full 32-bit multiplication will be needed. */ #define MULTIPLY16(var,const) (((short) (var)) * ((short) (const))) /* Dequantize a coefficient by multiplying it by the multiplier-table * entry; produce an int result. In this module, both inputs and result * are 16 bits or less, so either int or short multiply will work. */ /* #define DEQUANTIZE(coef,quantval) (((int) (coef)) * (quantval)) */ #define DEQUANTIZE MULTIPLY16 /* Descale and correctly round an int value that's scaled by N bits. * We assume RIGHT_SHIFT rounds towards minus infinity, so adding * the fudge factor is correct for either sign of X. */ #define DESCALE(x,n) (((x) + (1l << ((n)-1))) >> (n)) /* * Perform dequantization and inverse DCT on one block of coefficients, * producing a reduced-size 1x1 output block. */ void idct1x1(unsigned char* p_byte, int* inptr, int* quantptr, int skip_line) { (void)skip_line; /* unused */ *p_byte = range_limit(inptr[0] * quantptr[0] >> 3); } /* * Perform dequantization and inverse DCT on one block of coefficients, * producing a reduced-size 2x2 output block. */ void idct2x2(unsigned char* p_byte, int* inptr, int* quantptr, int skip_line) { int tmp0, tmp1, tmp2, tmp3, tmp4, tmp5; unsigned char* outptr; /* Pass 1: process columns from input, store into work array. */ /* Column 0 */ tmp4 = DEQUANTIZE(inptr[8*0], quantptr[8*0]); tmp5 = DEQUANTIZE(inptr[8*1], quantptr[8*1]); tmp0 = tmp4 + tmp5; tmp2 = tmp4 - tmp5; /* Column 1 */ tmp4 = DEQUANTIZE(inptr[8*0+1], quantptr[8*0+1]); tmp5 = DEQUANTIZE(inptr[8*1+1], quantptr[8*1+1]); tmp1 = tmp4 + tmp5; tmp3 = tmp4 - tmp5; /* Pass 2: process 2 rows, store into output array. */ /* Row 0 */ outptr = p_byte; outptr[0] = range_limit((int) DESCALE(tmp0 + tmp1, 3)); outptr[1] = range_limit((int) DESCALE(tmp0 - tmp1, 3)); /* Row 1 */ outptr = p_byte + skip_line; outptr[0] = range_limit((int) DESCALE(tmp2 + tmp3, 3)); outptr[1] = range_limit((int) DESCALE(tmp2 - tmp3, 3)); } /* * Perform dequantization and inverse DCT on one block of coefficients, * producing a reduced-size 4x4 output block. */ void idct4x4(unsigned char* p_byte, int* inptr, int* quantptr, int skip_line) { int tmp0, tmp2, tmp10, tmp12; int z1, z2, z3; int * wsptr; unsigned char* outptr; int ctr; int workspace[4*4]; /* buffers data between passes */ /* Pass 1: process columns from input, store into work array. */ wsptr = workspace; for (ctr = 0; ctr < 4; ctr++, inptr++, quantptr++, wsptr++) { /* Even part */ tmp0 = DEQUANTIZE(inptr[8*0], quantptr[8*0]); tmp2 = DEQUANTIZE(inptr[8*2], quantptr[8*2]); tmp10 = (tmp0 + tmp2) << PASS1_BITS; tmp12 = (tmp0 - tmp2) << PASS1_BITS; /* Odd part */ /* Same rotation as in the even part of the 8x8 LL&M IDCT */ z2 = DEQUANTIZE(inptr[8*1], quantptr[8*1]); z3 = DEQUANTIZE(inptr[8*3], quantptr[8*3]); z1 = MULTIPLY16(z2 + z3, FIX_0_541196100); tmp0 = DESCALE(z1 + MULTIPLY16(z3, - FIX_1_847759065), CONST_BITS-PASS1_BITS); tmp2 = DESCALE(z1 + MULTIPLY16(z2, FIX_0_765366865), CONST_BITS-PASS1_BITS); /* Final output stage */ wsptr[4*0] = (int) (tmp10 + tmp2); wsptr[4*3] = (int) (tmp10 - tmp2); wsptr[4*1] = (int) (tmp12 + tmp0); wsptr[4*2] = (int) (tmp12 - tmp0); } /* Pass 2: process 4 rows from work array, store into output array. */ wsptr = workspace; for (ctr = 0; ctr < 4; ctr++) { outptr = p_byte + (ctr*skip_line); /* Even part */ tmp0 = (int) wsptr[0]; tmp2 = (int) wsptr[2]; tmp10 = (tmp0 + tmp2) << CONST_BITS; tmp12 = (tmp0 - tmp2) << CONST_BITS; /* Odd part */ /* Same rotation as in the even part of the 8x8 LL&M IDCT */ z2 = (int) wsptr[1]; z3 = (int) wsptr[3]; z1 = MULTIPLY16(z2 + z3, FIX_0_541196100); tmp0 = z1 + MULTIPLY16(z3, - FIX_1_847759065); tmp2 = z1 + MULTIPLY16(z2, FIX_0_765366865); /* Final output stage */ outptr[0] = range_limit((int) DESCALE(tmp10 + tmp2, CONST_BITS+PASS1_BITS+3)); outptr[3] = range_limit((int) DESCALE(tmp10 - tmp2, CONST_BITS+PASS1_BITS+3)); outptr[1] = range_limit((int) DESCALE(tmp12 + tmp0, CONST_BITS+PASS1_BITS+3)); outptr[2] = range_limit((int) DESCALE(tmp12 - tmp0, CONST_BITS+PASS1_BITS+3)); wsptr += 4; /* advance pointer to next row */ } } /* * Perform dequantization and inverse DCT on one block of coefficients. */ void idct8x8(unsigned char* p_byte, int* inptr, int* quantptr, int skip_line) { long tmp0, tmp1, tmp2, tmp3; long tmp10, tmp11, tmp12, tmp13; long z1, z2, z3, z4, z5; int * wsptr; unsigned char* outptr; int ctr; int workspace[64]; /* buffers data between passes */ /* Pass 1: process columns from input, store into work array. */ /* Note results are scaled up by sqrt(8) compared to a true IDCT; */ /* furthermore, we scale the results by 2**PASS1_BITS. */ wsptr = workspace; for (ctr = 8; ctr > 0; ctr--) { /* Due to quantization, we will usually find that many of the input * coefficients are zero, especially the AC terms. We can exploit this * by short-circuiting the IDCT calculation for any column in which all * the AC terms are zero. In that case each output is equal to the * DC coefficient (with scale factor as needed). * With typical images and quantization tables, half or more of the * column DCT calculations can be simplified this way. */ if ((inptr[8*1] | inptr[8*2] | inptr[8*3] | inptr[8*4] | inptr[8*5] | inptr[8*6] | inptr[8*7]) == 0) { /* AC terms all zero */ int dcval = DEQUANTIZE(inptr[8*0], quantptr[8*0]) << PASS1_BITS; wsptr[8*0] = wsptr[8*1] = wsptr[8*2] = wsptr[8*3] = wsptr[8*4] = wsptr[8*5] = wsptr[8*6] = wsptr[8*7] = dcval; inptr++; /* advance pointers to next column */ quantptr++; wsptr++; continue; } /* Even part: reverse the even part of the forward DCT. */ /* The rotator is sqrt(2)*c(-6). */ z2 = DEQUANTIZE(inptr[8*2], quantptr[8*2]); z3 = DEQUANTIZE(inptr[8*6], quantptr[8*6]); z1 = MULTIPLY16(z2 + z3, FIX_0_541196100); tmp2 = z1 + MULTIPLY16(z3, - FIX_1_847759065); tmp3 = z1 + MULTIPLY16(z2, FIX_0_765366865); z2 = DEQUANTIZE(inptr[8*0], quantptr[8*0]); z3 = DEQUANTIZE(inptr[8*4], quantptr[8*4]); tmp0 = (z2 + z3) << CONST_BITS; tmp1 = (z2 - z3) << CONST_BITS; tmp10 = tmp0 + tmp3; tmp13 = tmp0 - tmp3; tmp11 = tmp1 + tmp2; tmp12 = tmp1 - tmp2; /* Odd part per figure 8; the matrix is unitary and hence its transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. */ tmp0 = DEQUANTIZE(inptr[8*7], quantptr[8*7]); tmp1 = DEQUANTIZE(inptr[8*5], quantptr[8*5]); tmp2 = DEQUANTIZE(inptr[8*3], quantptr[8*3]); tmp3 = DEQUANTIZE(inptr[8*1], quantptr[8*1]); z1 = tmp0 + tmp3; z2 = tmp1 + tmp2; z3 = tmp0 + tmp2; z4 = tmp1 + tmp3; z5 = MULTIPLY16(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */ tmp0 = MULTIPLY16(tmp0, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ tmp1 = MULTIPLY16(tmp1, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ tmp2 = MULTIPLY16(tmp2, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ tmp3 = MULTIPLY16(tmp3, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ z1 = MULTIPLY16(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ z2 = MULTIPLY16(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ z3 = MULTIPLY16(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ z4 = MULTIPLY16(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ z3 += z5; z4 += z5; tmp0 += z1 + z3; tmp1 += z2 + z4; tmp2 += z2 + z3; tmp3 += z1 + z4; /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ wsptr[8*0] = (int) DESCALE(tmp10 + tmp3, CONST_BITS-PASS1_BITS); wsptr[8*7] = (int) DESCALE(tmp10 - tmp3, CONST_BITS-PASS1_BITS); wsptr[8*1] = (int) DESCALE(tmp11 + tmp2, CONST_BITS-PASS1_BITS); wsptr[8*6] = (int) DESCALE(tmp11 - tmp2, CONST_BITS-PASS1_BITS); wsptr[8*2] = (int) DESCALE(tmp12 + tmp1, CONST_BITS-PASS1_BITS); wsptr[8*5] = (int) DESCALE(tmp12 - tmp1, CONST_BITS-PASS1_BITS); wsptr[8*3] = (int) DESCALE(tmp13 + tmp0, CONST_BITS-PASS1_BITS); wsptr[8*4] = (int) DESCALE(tmp13 - tmp0, CONST_BITS-PASS1_BITS); inptr++; /* advance pointers to next column */ quantptr++; wsptr++; } /* Pass 2: process rows from work array, store into output array. */ /* Note that we must descale the results by a factor of 8 == 2**3, */ /* and also undo the PASS1_BITS scaling. */ wsptr = workspace; for (ctr = 0; ctr < 8; ctr++) { outptr = p_byte + (ctr*skip_line); /* Rows of zeroes can be exploited in the same way as we did with columns. * However, the column calculation has created many nonzero AC terms, so * the simplification applies less often (typically 5% to 10% of the time). * On machines with very fast multiplication, it's possible that the * test takes more time than it's worth. In that case this section * may be commented out. */ #ifndef NO_ZERO_ROW_TEST if ((wsptr[1] | wsptr[2] | wsptr[3] | wsptr[4] | wsptr[5] | wsptr[6] | wsptr[7]) == 0) { /* AC terms all zero */ unsigned char dcval = range_limit((int) DESCALE((long) wsptr[0], PASS1_BITS+3)); outptr[0] = dcval; outptr[1] = dcval; outptr[2] = dcval; outptr[3] = dcval; outptr[4] = dcval; outptr[5] = dcval; outptr[6] = dcval; outptr[7] = dcval; wsptr += 8; /* advance pointer to next row */ continue; } #endif /* Even part: reverse the even part of the forward DCT. */ /* The rotator is sqrt(2)*c(-6). */ z2 = (long) wsptr[2]; z3 = (long) wsptr[6]; z1 = MULTIPLY16(z2 + z3, FIX_0_541196100); tmp2 = z1 + MULTIPLY16(z3, - FIX_1_847759065); tmp3 = z1 + MULTIPLY16(z2, FIX_0_765366865); tmp0 = ((long) wsptr[0] + (long) wsptr[4]) << CONST_BITS; tmp1 = ((long) wsptr[0] - (long) wsptr[4]) << CONST_BITS; tmp10 = tmp0 + tmp3; tmp13 = tmp0 - tmp3; tmp11 = tmp1 + tmp2; tmp12 = tmp1 - tmp2; /* Odd part per figure 8; the matrix is unitary and hence its * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. */ tmp0 = (long) wsptr[7]; tmp1 = (long) wsptr[5]; tmp2 = (long) wsptr[3]; tmp3 = (long) wsptr[1]; z1 = tmp0 + tmp3; z2 = tmp1 + tmp2; z3 = tmp0 + tmp2; z4 = tmp1 + tmp3; z5 = MULTIPLY16(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */ tmp0 = MULTIPLY16(tmp0, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ tmp1 = MULTIPLY16(tmp1, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ tmp2 = MULTIPLY16(tmp2, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ tmp3 = MULTIPLY16(tmp3, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ z1 = MULTIPLY16(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ z2 = MULTIPLY16(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ z3 = MULTIPLY16(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ z4 = MULTIPLY16(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ z3 += z5; z4 += z5; tmp0 += z1 + z3; tmp1 += z2 + z4; tmp2 += z2 + z3; tmp3 += z1 + z4; /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ outptr[0] = range_limit((int) DESCALE(tmp10 + tmp3, CONST_BITS+PASS1_BITS+3)); outptr[7] = range_limit((int) DESCALE(tmp10 - tmp3, CONST_BITS+PASS1_BITS+3)); outptr[1] = range_limit((int) DESCALE(tmp11 + tmp2, CONST_BITS+PASS1_BITS+3)); outptr[6] = range_limit((int) DESCALE(tmp11 - tmp2, CONST_BITS+PASS1_BITS+3)); outptr[2] = range_limit((int) DESCALE(tmp12 + tmp1, CONST_BITS+PASS1_BITS+3)); outptr[5] = range_limit((int) DESCALE(tmp12 - tmp1, CONST_BITS+PASS1_BITS+3)); outptr[3] = range_limit((int) DESCALE(tmp13 + tmp0, CONST_BITS+PASS1_BITS+3)); outptr[4] = range_limit((int) DESCALE(tmp13 - tmp0, CONST_BITS+PASS1_BITS+3)); wsptr += 8; /* advance pointer to next row */ } } /* JPEG decoder implementation */ #define HUFF_LOOKAHEAD 8 /* # of bits of lookahead */ struct derived_tbl { /* Basic tables: (element [0] of each array is unused) */ long mincode[17]; /* smallest code of length k */ long maxcode[18]; /* largest code of length k (-1 if none) */ /* (maxcode[17] is a sentinel to ensure huff_DECODE terminates) */ int valptr[17]; /* huffval[] index of 1st symbol of length k */ /* Back link to public Huffman table (needed only in slow_DECODE) */ int* pub; /* Lookahead tables: indexed by the next HUFF_LOOKAHEAD bits of the input data stream. If the next Huffman code is no more than HUFF_LOOKAHEAD bits long, we can obtain its length and the corresponding symbol directly from these tables. */ int look_nbits[1<p_entropy_end = p_src + size; while (p_src < p_bytes + size) { if (*p_src++ != 0xFF) /* no marker? */ { p_src--; /* it's image data, put it back */ p_jpeg->p_entropy_data = p_src; break; /* exit marker processing */ } switch (*p_src++) { case 0xFF: /* Fill byte */ ret |= FILL_FF; case 0x00: /* Zero stuffed byte - entropy data */ p_src--; /* put it back */ continue; case 0xC0: /* SOF Huff - Baseline DCT */ { ret |= SOF0; marker_size = *p_src++ << 8; /* Highbyte */ marker_size |= *p_src++; /* Lowbyte */ n = *p_src++; /* sample precision (= 8 or 12) */ if (n != 8) { return(-1); /* Unsupported sample precision */ } p_jpeg->y_size = *p_src++ << 8; /* Highbyte */ p_jpeg->y_size |= *p_src++; /* Lowbyte */ p_jpeg->x_size = *p_src++ << 8; /* Highbyte */ p_jpeg->x_size |= *p_src++; /* Lowbyte */ n = (marker_size-2-6)/3; if (*p_src++ != n || (n != 1 && n != 3)) { return(-2); /* Unsupported SOF0 component specification */ } for (i=0; iframeheader[i].ID = *p_src++; /* Component info */ p_jpeg->frameheader[i].horizontal_sampling = *p_src >> 4; p_jpeg->frameheader[i].vertical_sampling = *p_src++ & 0x0F; p_jpeg->frameheader[i].quanttable_select = *p_src++; if (p_jpeg->frameheader[i].horizontal_sampling > 2 || p_jpeg->frameheader[i].vertical_sampling > 2) return -3; /* Unsupported SOF0 subsampling */ } p_jpeg->blocks = n; } break; case 0xC1: /* SOF Huff - Extended sequential DCT*/ case 0xC2: /* SOF Huff - Progressive DCT*/ case 0xC3: /* SOF Huff - Spatial (sequential) lossless*/ case 0xC5: /* SOF Huff - Differential sequential DCT*/ case 0xC6: /* SOF Huff - Differential progressive DCT*/ case 0xC7: /* SOF Huff - Differential spatial*/ case 0xC8: /* SOF Arith - Reserved for JPEG extensions*/ case 0xC9: /* SOF Arith - Extended sequential DCT*/ case 0xCA: /* SOF Arith - Progressive DCT*/ case 0xCB: /* SOF Arith - Spatial (sequential) lossless*/ case 0xCD: /* SOF Arith - Differential sequential DCT*/ case 0xCE: /* SOF Arith - Differential progressive DCT*/ case 0xCF: /* SOF Arith - Differential spatial*/ { return (-4); /* other DCT model than baseline not implemented */ } case 0xC4: /* Define Huffman Table(s) */ { unsigned char* p_temp; ret |= DHT; marker_size = *p_src++ << 8; /* Highbyte */ marker_size |= *p_src++; /* Lowbyte */ p_temp = p_src; while (p_src < p_temp+marker_size-2-17) /* another table */ { int sum = 0; i = *p_src & 0x0F; /* table index */ if (i > 1) { return (-5); /* Huffman table index out of range */ } else if (*p_src++ & 0xF0) /* AC table */ { for (j=0; j<16; j++) { sum += *p_src; p_jpeg->hufftable[i].huffmancodes_ac[j] = *p_src++; } if(16 + sum > AC_LEN) return -10; /* longer than allowed */ for (; j < 16 + sum; j++) p_jpeg->hufftable[i].huffmancodes_ac[j] = *p_src++; } else /* DC table */ { for (j=0; j<16; j++) { sum += *p_src; p_jpeg->hufftable[i].huffmancodes_dc[j] = *p_src++; } if(16 + sum > DC_LEN) return -11; /* longer than allowed */ for (; j < 16 + sum; j++) p_jpeg->hufftable[i].huffmancodes_dc[j] = *p_src++; } } /* while */ p_src = p_temp+marker_size - 2; /* skip possible residue */ } break; case 0xCC: /* Define Arithmetic coding conditioning(s) */ return(-6); /* Arithmetic coding not supported */ case 0xD8: /* Start of Image */ case 0xD9: /* End of Image */ case 0x01: /* for temp private use arith code */ break; /* skip parameterless marker */ case 0xDA: /* Start of Scan */ { ret |= SOS; marker_size = *p_src++ << 8; /* Highbyte */ marker_size |= *p_src++; /* Lowbyte */ n = (marker_size-2-1-3)/2; if (*p_src++ != n || (n != 1 && n != 3)) { return (-7); /* Unsupported SOS component specification */ } for (i=0; iscanheader[i].ID = *p_src++; p_jpeg->scanheader[i].DC_select = *p_src >> 4; p_jpeg->scanheader[i].AC_select = *p_src++ & 0x0F; } p_src += 3; /* skip spectral information */ } break; case 0xDB: /* Define quantization Table(s) */ { ret |= DQT; marker_size = *p_src++ << 8; /* Highbyte */ marker_size |= *p_src++; /* Lowbyte */ n = (marker_size-2)/(QUANT_TABLE_LENGTH+1); /* # of tables */ for (i=0; i= 4) { return (-8); /* Unsupported quantization table */ } /* Read Quantisation table: */ for (j=0; jquanttable[id][j] = *p_src++; } } break; case 0xDD: /* Define Restart Interval */ { marker_size = *p_src++ << 8; /* Highbyte */ marker_size |= *p_src++; /* Lowbyte */ p_jpeg->restart_interval = *p_src++ << 8; /* Highbyte */ p_jpeg->restart_interval |= *p_src++; /* Lowbyte */ p_src += marker_size-4; /* skip segment */ } break; case 0xDC: /* Define Number of Lines */ case 0xDE: /* Define Hierarchical progression */ case 0xDF: /* Expand Reference Component(s) */ case 0xE0: /* Application Field 0*/ case 0xE1: /* Application Field 1*/ case 0xE2: /* Application Field 2*/ case 0xE3: /* Application Field 3*/ case 0xE4: /* Application Field 4*/ case 0xE5: /* Application Field 5*/ case 0xE6: /* Application Field 6*/ case 0xE7: /* Application Field 7*/ case 0xE8: /* Application Field 8*/ case 0xE9: /* Application Field 9*/ case 0xEA: /* Application Field 10*/ case 0xEB: /* Application Field 11*/ case 0xEC: /* Application Field 12*/ case 0xED: /* Application Field 13*/ case 0xEE: /* Application Field 14*/ case 0xEF: /* Application Field 15*/ case 0xFE: /* Comment */ { marker_size = *p_src++ << 8; /* Highbyte */ marker_size |= *p_src++; /* Lowbyte */ p_src += marker_size-2; /* skip segment */ } break; case 0xF0: /* Reserved for JPEG extensions */ case 0xF1: /* Reserved for JPEG extensions */ case 0xF2: /* Reserved for JPEG extensions */ case 0xF3: /* Reserved for JPEG extensions */ case 0xF4: /* Reserved for JPEG extensions */ case 0xF5: /* Reserved for JPEG extensions */ case 0xF6: /* Reserved for JPEG extensions */ case 0xF7: /* Reserved for JPEG extensions */ case 0xF8: /* Reserved for JPEG extensions */ case 0xF9: /* Reserved for JPEG extensions */ case 0xFA: /* Reserved for JPEG extensions */ case 0xFB: /* Reserved for JPEG extensions */ case 0xFC: /* Reserved for JPEG extensions */ case 0xFD: /* Reserved for JPEG extensions */ case 0x02: /* Reserved */ default: return (-9); /* Unknown marker */ } /* switch */ } /* while */ return (ret); /* return flags with seen markers */ } void default_huff_tbl(struct jpeg* p_jpeg) { static const struct huffman_table luma_table = { { 0x00,0x01,0x05,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B }, { 0x00,0x02,0x01,0x03,0x03,0x02,0x04,0x03,0x05,0x05,0x04,0x04,0x00,0x00,0x01,0x7D, 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07, 0x22,0x71,0x14,0x32,0x81,0x91,0xA1,0x08,0x23,0x42,0xB1,0xC1,0x15,0x52,0xD1,0xF0, 0x24,0x33,0x62,0x72,0x82,0x09,0x0A,0x16,0x17,0x18,0x19,0x1A,0x25,0x26,0x27,0x28, 0x29,0x2A,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x43,0x44,0x45,0x46,0x47,0x48,0x49, 0x4A,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x63,0x64,0x65,0x66,0x67,0x68,0x69, 0x6A,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x83,0x84,0x85,0x86,0x87,0x88,0x89, 0x8A,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7, 0xA8,0xA9,0xAA,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xC2,0xC3,0xC4,0xC5, 0xC6,0xC7,0xC8,0xC9,0xCA,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xE1,0xE2, 0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8, 0xF9,0xFA } }; static const struct huffman_table chroma_table = { { 0x00,0x03,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00, 0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B }, { 0x00,0x02,0x01,0x02,0x04,0x04,0x03,0x04,0x07,0x05,0x04,0x04,0x00,0x01,0x02,0x77, 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71, 0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,0xA1,0xB1,0xC1,0x09,0x23,0x33,0x52,0xF0, 0x15,0x62,0x72,0xD1,0x0A,0x16,0x24,0x34,0xE1,0x25,0xF1,0x17,0x18,0x19,0x1A,0x26, 0x27,0x28,0x29,0x2A,0x35,0x36,0x37,0x38,0x39,0x3A,0x43,0x44,0x45,0x46,0x47,0x48, 0x49,0x4A,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x63,0x64,0x65,0x66,0x67,0x68, 0x69,0x6A,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x8A,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0xA2,0xA3,0xA4,0xA5, 0xA6,0xA7,0xA8,0xA9,0xAA,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xC2,0xC3, 0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA, 0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8, 0xF9,0xFA } }; MEMCPY(&p_jpeg->hufftable[0], &luma_table, sizeof(luma_table)); MEMCPY(&p_jpeg->hufftable[1], &chroma_table, sizeof(chroma_table)); return; } /* Compute the derived values for a Huffman table */ void fix_huff_tbl(int* htbl, struct derived_tbl* dtbl) { int p, i, l, si; int lookbits, ctr; char huffsize[257]; unsigned int huffcode[257]; unsigned int code; dtbl->pub = htbl; /* fill in back link */ /* Figure C.1: make table of Huffman code length for each symbol */ /* Note that this is in code-length order. */ p = 0; for (l = 1; l <= 16; l++) { /* all possible code length */ for (i = 1; i <= (int) htbl[l-1]; i++) /* all codes per length */ huffsize[p++] = (char) l; } huffsize[p] = 0; /* Figure C.2: generate the codes themselves */ /* Note that this is in code-length order. */ code = 0; si = huffsize[0]; p = 0; while (huffsize[p]) { while (((int) huffsize[p]) == si) { huffcode[p++] = code; code++; } code <<= 1; si++; } /* Figure F.15: generate decoding tables for bit-sequential decoding */ p = 0; for (l = 1; l <= 16; l++) { if (htbl[l-1]) { dtbl->valptr[l] = p; /* huffval[] index of 1st symbol of code length l */ dtbl->mincode[l] = huffcode[p]; /* minimum code of length l */ p += htbl[l-1]; dtbl->maxcode[l] = huffcode[p-1]; /* maximum code of length l */ } else { dtbl->maxcode[l] = -1; /* -1 if no codes of this length */ } } dtbl->maxcode[17] = 0xFFFFFL; /* ensures huff_DECODE terminates */ /* Compute lookahead tables to speed up decoding. * First we set all the table entries to 0, indicating "too long"; * then we iterate through the Huffman codes that are short enough and * fill in all the entries that correspond to bit sequences starting * with that code. */ MEMSET(dtbl->look_nbits, 0, sizeof(dtbl->look_nbits)); p = 0; for (l = 1; l <= HUFF_LOOKAHEAD; l++) { for (i = 1; i <= (int) htbl[l-1]; i++, p++) { /* l = current code's length, p = its index in huffcode[] & huffval[]. */ /* Generate left-justified code followed by all possible bit sequences */ lookbits = huffcode[p] << (HUFF_LOOKAHEAD-l); for (ctr = 1 << (HUFF_LOOKAHEAD-l); ctr > 0; ctr--) { dtbl->look_nbits[lookbits] = l; dtbl->look_sym[lookbits] = htbl[16+p]; lookbits++; } } } } /* zag[i] is the natural-order position of the i'th element of zigzag order. * If the incoming data is corrupted, decode_mcu could attempt to * reference values beyond the end of the array. To avoid a wild store, * we put some extra zeroes after the real entries. */ static const int zag[] = { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63, 0, 0, 0, 0, 0, 0, 0, 0, /* extra entries in case k>63 below */ 0, 0, 0, 0, 0, 0, 0, 0 }; void build_lut(struct jpeg* p_jpeg) { int i; fix_huff_tbl(p_jpeg->hufftable[0].huffmancodes_dc, &p_jpeg->dc_derived_tbls[0]); fix_huff_tbl(p_jpeg->hufftable[0].huffmancodes_ac, &p_jpeg->ac_derived_tbls[0]); fix_huff_tbl(p_jpeg->hufftable[1].huffmancodes_dc, &p_jpeg->dc_derived_tbls[1]); fix_huff_tbl(p_jpeg->hufftable[1].huffmancodes_ac, &p_jpeg->ac_derived_tbls[1]); /* build the dequantization tables for the IDCT (De-ZiZagged) */ for (i=0; i<64; i++) { p_jpeg->qt_idct[0][zag[i]] = p_jpeg->quanttable[0][i]; p_jpeg->qt_idct[1][zag[i]] = p_jpeg->quanttable[1][i]; } for (i=0; i<4; i++) p_jpeg->store_pos[i] = i; /* default ordering */ /* assignments for the decoding of blocks */ if (p_jpeg->frameheader[0].horizontal_sampling == 2 && p_jpeg->frameheader[0].vertical_sampling == 1) { /* 4:2:2 */ p_jpeg->blocks = 4; p_jpeg->x_mbl = (p_jpeg->x_size+15) / 16; p_jpeg->x_phys = p_jpeg->x_mbl * 16; p_jpeg->y_mbl = (p_jpeg->y_size+7) / 8; p_jpeg->y_phys = p_jpeg->y_mbl * 8; p_jpeg->mcu_membership[0] = 0; /* Y1=Y2=0, U=1, V=2 */ p_jpeg->mcu_membership[1] = 0; p_jpeg->mcu_membership[2] = 1; p_jpeg->mcu_membership[3] = 2; p_jpeg->tab_membership[0] = 0; /* DC, DC, AC, AC */ p_jpeg->tab_membership[1] = 0; p_jpeg->tab_membership[2] = 1; p_jpeg->tab_membership[3] = 1; p_jpeg->subsample_x[0] = 1; p_jpeg->subsample_x[1] = 2; p_jpeg->subsample_x[2] = 2; p_jpeg->subsample_y[0] = 1; p_jpeg->subsample_y[1] = 1; p_jpeg->subsample_y[2] = 1; } if (p_jpeg->frameheader[0].horizontal_sampling == 1 && p_jpeg->frameheader[0].vertical_sampling == 2) { /* 4:2:2 vertically subsampled */ p_jpeg->store_pos[1] = 2; /* block positions are mirrored */ p_jpeg->store_pos[2] = 1; p_jpeg->blocks = 4; p_jpeg->x_mbl = (p_jpeg->x_size+7) / 8; p_jpeg->x_phys = p_jpeg->x_mbl * 8; p_jpeg->y_mbl = (p_jpeg->y_size+15) / 16; p_jpeg->y_phys = p_jpeg->y_mbl * 16; p_jpeg->mcu_membership[0] = 0; /* Y1=Y2=0, U=1, V=2 */ p_jpeg->mcu_membership[1] = 0; p_jpeg->mcu_membership[2] = 1; p_jpeg->mcu_membership[3] = 2; p_jpeg->tab_membership[0] = 0; /* DC, DC, AC, AC */ p_jpeg->tab_membership[1] = 0; p_jpeg->tab_membership[2] = 1; p_jpeg->tab_membership[3] = 1; p_jpeg->subsample_x[0] = 1; p_jpeg->subsample_x[1] = 1; p_jpeg->subsample_x[2] = 1; p_jpeg->subsample_y[0] = 1; p_jpeg->subsample_y[1] = 2; p_jpeg->subsample_y[2] = 2; } else if (p_jpeg->frameheader[0].horizontal_sampling == 2 && p_jpeg->frameheader[0].vertical_sampling == 2) { /* 4:2:0 */ p_jpeg->blocks = 6; p_jpeg->x_mbl = (p_jpeg->x_size+15) / 16; p_jpeg->x_phys = p_jpeg->x_mbl * 16; p_jpeg->y_mbl = (p_jpeg->y_size+15) / 16; p_jpeg->y_phys = p_jpeg->y_mbl * 16; p_jpeg->mcu_membership[0] = 0; p_jpeg->mcu_membership[1] = 0; p_jpeg->mcu_membership[2] = 0; p_jpeg->mcu_membership[3] = 0; p_jpeg->mcu_membership[4] = 1; p_jpeg->mcu_membership[5] = 2; p_jpeg->tab_membership[0] = 0; p_jpeg->tab_membership[1] = 0; p_jpeg->tab_membership[2] = 0; p_jpeg->tab_membership[3] = 0; p_jpeg->tab_membership[4] = 1; p_jpeg->tab_membership[5] = 1; p_jpeg->subsample_x[0] = 1; p_jpeg->subsample_x[1] = 2; p_jpeg->subsample_x[2] = 2; p_jpeg->subsample_y[0] = 1; p_jpeg->subsample_y[1] = 2; p_jpeg->subsample_y[2] = 2; } else if (p_jpeg->frameheader[0].horizontal_sampling == 1 && p_jpeg->frameheader[0].vertical_sampling == 1) { /* 4:4:4 */ /* don't overwrite p_jpeg->blocks */ p_jpeg->x_mbl = (p_jpeg->x_size+7) / 8; p_jpeg->x_phys = p_jpeg->x_mbl * 8; p_jpeg->y_mbl = (p_jpeg->y_size+7) / 8; p_jpeg->y_phys = p_jpeg->y_mbl * 8; p_jpeg->mcu_membership[0] = 0; p_jpeg->mcu_membership[1] = 1; p_jpeg->mcu_membership[2] = 2; p_jpeg->tab_membership[0] = 0; p_jpeg->tab_membership[1] = 1; p_jpeg->tab_membership[2] = 1; p_jpeg->subsample_x[0] = 1; p_jpeg->subsample_x[1] = 1; p_jpeg->subsample_x[2] = 1; p_jpeg->subsample_y[0] = 1; p_jpeg->subsample_y[1] = 1; p_jpeg->subsample_y[2] = 1; } else { /* error */ } } /* * These functions/macros provide the in-line portion of bit fetching. * Use check_bit_buffer to ensure there are N bits in get_buffer * before using get_bits, peek_bits, or drop_bits. * check_bit_buffer(state,n,action); * Ensure there are N bits in get_buffer; if suspend, take action. * val = get_bits(n); * Fetch next N bits. * val = peek_bits(n); * Fetch next N bits without removing them from the buffer. * drop_bits(n); * Discard next N bits. * The value N should be a simple variable, not an expression, because it * is evaluated multiple times. */ INLINE void check_bit_buffer(struct bitstream* pb, int nbits) { if (pb->bits_left < nbits) { /* nbits is <= 16, so I can always refill 2 bytes in this case */ unsigned char byte; byte = *pb->next_input_byte++; if (byte == 0xFF) /* legal marker can be byte stuffing or RSTm */ { /* simplification: just skip the (one-byte) marker code */ pb->next_input_byte++; } pb->get_buffer = (pb->get_buffer << 8) | byte; byte = *pb->next_input_byte++; if (byte == 0xFF) /* legal marker can be byte stuffing or RSTm */ { /* simplification: just skip the (one-byte) marker code */ pb->next_input_byte++; } pb->get_buffer = (pb->get_buffer << 8) | byte; pb->bits_left += 16; } } INLINE int get_bits(struct bitstream* pb, int nbits) { return ((int) (pb->get_buffer >> (pb->bits_left -= nbits))) & ((1<get_buffer >> (pb->bits_left - nbits))) & ((1<bits_left -= nbits; } /* re-synchronize to entropy data (skip restart marker) */ void search_restart(struct bitstream* pb) { pb->next_input_byte--; /* we may have overread it, taking 2 bytes */ /* search for a non-byte-padding marker, has to be RSTm or EOS */ while (pb->next_input_byte < pb->input_end && (pb->next_input_byte[-2] != 0xFF || pb->next_input_byte[-1] == 0x00)) { pb->next_input_byte++; } pb->bits_left = 0; } /* Figure F.12: extend sign bit. */ #define HUFF_EXTEND(x,s) ((x) < extend_test[s] ? (x) + extend_offset[s] : (x)) static const int extend_test[16] = /* entry n is 2**(n-1) */ { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 }; static const int extend_offset[16] = /* entry n is (-1 << n) + 1 */ { 0, ((-1)<<1) + 1, ((-1)<<2) + 1, ((-1)<<3) + 1, ((-1)<<4) + 1, ((-1)<<5) + 1, ((-1)<<6) + 1, ((-1)<<7) + 1, ((-1)<<8) + 1, ((-1)<<9) + 1, ((-1)<<10) + 1, ((-1)<<11) + 1, ((-1)<<12) + 1, ((-1)<<13) + 1, ((-1)<<14) + 1, ((-1)<<15) + 1 }; /* Decode a single value */ INLINE int huff_decode_dc(struct bitstream* bs, struct derived_tbl* tbl) { int nb, look, s, r; check_bit_buffer(bs, HUFF_LOOKAHEAD); look = peek_bits(bs, HUFF_LOOKAHEAD); if ((nb = tbl->look_nbits[look]) != 0) { drop_bits(bs, nb); s = tbl->look_sym[look]; check_bit_buffer(bs, s); r = get_bits(bs, s); s = HUFF_EXTEND(r, s); } else { /* slow_DECODE(s, HUFF_LOOKAHEAD+1)) < 0); */ long code; nb=HUFF_LOOKAHEAD+1; check_bit_buffer(bs, nb); code = get_bits(bs, nb); while (code > tbl->maxcode[nb]) { code <<= 1; check_bit_buffer(bs, 1); code |= get_bits(bs, 1); nb++; } if (nb > 16) /* error in Huffman */ { s=0; /* fake a zero, this is most safe */ } else { s = tbl->pub[16 + tbl->valptr[nb] + ((int) (code - tbl->mincode[nb])) ]; check_bit_buffer(bs, s); r = get_bits(bs, s); s = HUFF_EXTEND(r, s); } } /* end slow decode */ return s; } INLINE int huff_decode_ac(struct bitstream* bs, struct derived_tbl* tbl) { int nb, look, s; check_bit_buffer(bs, HUFF_LOOKAHEAD); look = peek_bits(bs, HUFF_LOOKAHEAD); if ((nb = tbl->look_nbits[look]) != 0) { drop_bits(bs, nb); s = tbl->look_sym[look]; } else { /* slow_DECODE(s, HUFF_LOOKAHEAD+1)) < 0); */ long code; nb=HUFF_LOOKAHEAD+1; check_bit_buffer(bs, nb); code = get_bits(bs, nb); while (code > tbl->maxcode[nb]) { code <<= 1; check_bit_buffer(bs, 1); code |= get_bits(bs, 1); nb++; } if (nb > 16) /* error in Huffman */ { s=0; /* fake a zero, this is most safe */ } else { s = tbl->pub[16 + tbl->valptr[nb] + ((int) (code - tbl->mincode[nb])) ]; } } /* end slow decode */ return s; } #ifdef HAVE_LCD_COLOR /* JPEG decoder variant for YUV decoding, into 3 different planes */ /* Note: it keeps the original color subsampling, even if resized. */ int jpeg_decode(struct jpeg* p_jpeg, unsigned char* p_pixel[3], int downscale, void (*pf_progress)(int current, int total)) { struct bitstream bs; /* bitstream "object" */ int block[64]; /* decoded DCT coefficients */ int width, height; int skip_line[3]; /* bytes from one line to the next (skip_line) */ int skip_strip[3], skip_mcu[3]; /* bytes to next DCT row / column */ int i, x, y; /* loop counter */ unsigned char* p_line[3] = {p_pixel[0], p_pixel[1], p_pixel[2]}; unsigned char* p_byte[3]; /* bitmap pointer */ void (*pf_idct)(unsigned char*, int*, int*, int); /* selected IDCT */ int k_need; /* AC coefficients needed up to here */ int zero_need; /* init the block with this many zeros */ int last_dc_val[3] = {0, 0, 0}; /* or 128 for chroma? */ int store_offs[4]; /* memory offsets: order of Y11 Y12 Y21 Y22 U V */ int restart = p_jpeg->restart_interval; /* MCUs until restart marker */ /* pick the IDCT we want, determine how to work with coefs */ if (downscale == 1) { pf_idct = idct8x8; k_need = 64; /* all */ zero_need = 63; /* all */ } else if (downscale == 2) { pf_idct = idct4x4; k_need = 25; /* this far in zig-zag to cover 4*4 */ zero_need = 27; /* clear this far in linear order */ } else if (downscale == 4) { pf_idct = idct2x2; k_need = 5; /* this far in zig-zag to cover 2*2 */ zero_need = 9; /* clear this far in linear order */ } else if (downscale == 8) { pf_idct = idct1x1; k_need = 0; /* no AC, not needed */ zero_need = 0; /* no AC, not needed */ } else return -1; /* not supported */ /* init bitstream, fake a restart to make it start */ bs.next_input_byte = p_jpeg->p_entropy_data; bs.bits_left = 0; bs.input_end = p_jpeg->p_entropy_end; width = p_jpeg->x_phys / downscale; height = p_jpeg->y_phys / downscale; for (i=0; i<3; i++) /* calculate some strides */ { skip_line[i] = width / p_jpeg->subsample_x[i]; skip_strip[i] = skip_line[i] * (height / p_jpeg->y_mbl) / p_jpeg->subsample_y[i]; skip_mcu[i] = width/p_jpeg->x_mbl / p_jpeg->subsample_x[i]; } /* prepare offsets about where to store the different blocks */ store_offs[p_jpeg->store_pos[0]] = 0; store_offs[p_jpeg->store_pos[1]] = 8 / downscale; /* to the right */ store_offs[p_jpeg->store_pos[2]] = width * 8 / downscale; /* below */ store_offs[p_jpeg->store_pos[3]] = store_offs[1] + store_offs[2]; /* r+b */ for(y=0; yy_mbl && bs.next_input_byte <= bs.input_end; y++) { for (i=0; i<3; i++) /* scan line init */ { p_byte[i] = p_line[i]; p_line[i] += skip_strip[i]; } for (x=0; xx_mbl; x++) { int blkn; /* Outer loop handles each block in the MCU */ for (blkn = 0; blkn < p_jpeg->blocks; blkn++) { /* Decode a single block's worth of coefficients */ int k = 1; /* coefficient index */ int s, r; /* huffman values */ int ci = p_jpeg->mcu_membership[blkn]; /* component index */ int ti = p_jpeg->tab_membership[blkn]; /* table index */ struct derived_tbl* dctbl = &p_jpeg->dc_derived_tbls[ti]; struct derived_tbl* actbl = &p_jpeg->ac_derived_tbls[ti]; /* Section F.2.2.1: decode the DC coefficient difference */ s = huff_decode_dc(&bs, dctbl); last_dc_val[ci] += s; block[0] = last_dc_val[ci]; /* output it (assumes zag[0] = 0) */ /* coefficient buffer must be cleared */ MEMSET(block+1, 0, zero_need*sizeof(block[0])); /* Section F.2.2.2: decode the AC coefficients */ for (; k < k_need; k++) { s = huff_decode_ac(&bs, actbl); r = s >> 4; s &= 15; if (s) { k += r; check_bit_buffer(&bs, s); r = get_bits(&bs, s); block[zag[k]] = HUFF_EXTEND(r, s); } else { if (r != 15) { k = 64; break; } k += r; } } /* for k */ /* In this path we just discard the values */ for (; k < 64; k++) { s = huff_decode_ac(&bs, actbl); r = s >> 4; s &= 15; if (s) { k += r; check_bit_buffer(&bs, s); drop_bits(&bs, s); } else { if (r != 15) break; k += r; } } /* for k */ if (ci == 0) { /* Y component needs to bother about block store */ pf_idct(p_byte[0]+store_offs[blkn], block, p_jpeg->qt_idct[ti], skip_line[0]); } else { /* chroma */ pf_idct(p_byte[ci], block, p_jpeg->qt_idct[ti], skip_line[ci]); } } /* for blkn */ p_byte[0] += skip_mcu[0]; /* unrolled for (i=0; i<3; i++) loop */ p_byte[1] += skip_mcu[1]; p_byte[2] += skip_mcu[2]; if (p_jpeg->restart_interval && --restart == 0) { /* if a restart marker is due: */ restart = p_jpeg->restart_interval; /* count again */ search_restart(&bs); /* align the bitstream */ last_dc_val[0] = last_dc_val[1] = last_dc_val[2] = 0; /* reset decoder */ } } /* for x */ if (pf_progress != NULL) pf_progress(y, p_jpeg->y_mbl-1); /* notify about decoding progress */ } /* for y */ return 0; /* success */ } #else /* !HAVE_LCD_COLOR */ /* a JPEG decoder specialized in decoding only the luminance (b&w) */ int jpeg_decode(struct jpeg* p_jpeg, unsigned char* p_pixel[1], int downscale, void (*pf_progress)(int current, int total)) { struct bitstream bs; /* bitstream "object" */ int block[64]; /* decoded DCT coefficients */ int width, height; int skip_line; /* bytes from one line to the next (skip_line) */ int skip_strip, skip_mcu; /* bytes to next DCT row / column */ int x, y; /* loop counter */ unsigned char* p_line = p_pixel[0]; unsigned char* p_byte; /* bitmap pointer */ void (*pf_idct)(unsigned char*, int*, int*, int); /* selected IDCT */ int k_need; /* AC coefficients needed up to here */ int zero_need; /* init the block with this many zeros */ int last_dc_val = 0; int store_offs[4]; /* memory offsets: order of Y11 Y12 Y21 Y22 U V */ int restart = p_jpeg->restart_interval; /* MCUs until restart marker */ /* pick the IDCT we want, determine how to work with coefs */ if (downscale == 1) { pf_idct = idct8x8; k_need = 64; /* all */ zero_need = 63; /* all */ } else if (downscale == 2) { pf_idct = idct4x4; k_need = 25; /* this far in zig-zag to cover 4*4 */ zero_need = 27; /* clear this far in linear order */ } else if (downscale == 4) { pf_idct = idct2x2; k_need = 5; /* this far in zig-zag to cover 2*2 */ zero_need = 9; /* clear this far in linear order */ } else if (downscale == 8) { pf_idct = idct1x1; k_need = 0; /* no AC, not needed */ zero_need = 0; /* no AC, not needed */ } else return -1; /* not supported */ /* init bitstream, fake a restart to make it start */ bs.next_input_byte = p_jpeg->p_entropy_data; bs.bits_left = 0; bs.input_end = p_jpeg->p_entropy_end; width = p_jpeg->x_phys / downscale; height = p_jpeg->y_phys / downscale; skip_line = width; skip_strip = skip_line * (height / p_jpeg->y_mbl); skip_mcu = (width/p_jpeg->x_mbl); /* prepare offsets about where to store the different blocks */ store_offs[p_jpeg->store_pos[0]] = 0; store_offs[p_jpeg->store_pos[1]] = 8 / downscale; /* to the right */ store_offs[p_jpeg->store_pos[2]] = width * 8 / downscale; /* below */ store_offs[p_jpeg->store_pos[3]] = store_offs[1] + store_offs[2]; /* r+b */ for(y=0; yy_mbl && bs.next_input_byte <= bs.input_end; y++) { p_byte = p_line; p_line += skip_strip; for (x=0; xx_mbl; x++) { int blkn; /* Outer loop handles each block in the MCU */ for (blkn = 0; blkn < p_jpeg->blocks; blkn++) { /* Decode a single block's worth of coefficients */ int k = 1; /* coefficient index */ int s, r; /* huffman values */ int ci = p_jpeg->mcu_membership[blkn]; /* component index */ int ti = p_jpeg->tab_membership[blkn]; /* table index */ struct derived_tbl* dctbl = &p_jpeg->dc_derived_tbls[ti]; struct derived_tbl* actbl = &p_jpeg->ac_derived_tbls[ti]; /* Section F.2.2.1: decode the DC coefficient difference */ s = huff_decode_dc(&bs, dctbl); if (ci == 0) /* only for Y component */ { last_dc_val += s; block[0] = last_dc_val; /* output it (assumes zag[0] = 0) */ /* coefficient buffer must be cleared */ MEMSET(block+1, 0, zero_need*sizeof(block[0])); /* Section F.2.2.2: decode the AC coefficients */ for (; k < k_need; k++) { s = huff_decode_ac(&bs, actbl); r = s >> 4; s &= 15; if (s) { k += r; check_bit_buffer(&bs, s); r = get_bits(&bs, s); block[zag[k]] = HUFF_EXTEND(r, s); } else { if (r != 15) { k = 64; break; } k += r; } } /* for k */ } /* In this path we just discard the values */ for (; k < 64; k++) { s = huff_decode_ac(&bs, actbl); r = s >> 4; s &= 15; if (s) { k += r; check_bit_buffer(&bs, s); drop_bits(&bs, s); } else { if (r != 15) break; k += r; } } /* for k */ if (ci == 0) { /* only for Y component */ pf_idct(p_byte+store_offs[blkn], block, p_jpeg->qt_idct[ti], skip_line); } } /* for blkn */ p_byte += skip_mcu; if (p_jpeg->restart_interval && --restart == 0) { /* if a restart marker is due: */ restart = p_jpeg->restart_interval; /* count again */ search_restart(&bs); /* align the bitstream */ last_dc_val = 0; /* reset decoder */ } } /* for x */ if (pf_progress != NULL) pf_progress(y, p_jpeg->y_mbl-1); /* notify about decoding progress */ } /* for y */ return 0; /* success */ } #endif /* !HAVE_LCD_COLOR */ /**************** end JPEG code ********************/ /**************** begin Application ********************/ /************************* Types ***************************/ struct t_disp { #ifdef HAVE_LCD_COLOR unsigned char* bitmap[3]; /* Y, Cr, Cb */ int csub_x, csub_y; #else unsigned char* bitmap[1]; /* Y only */ #endif int width; int height; int stride; int x, y; }; /************************* Globals ***************************/ /* decompressed image in the possible sizes (1,2,4,8), wasting the other */ struct t_disp disp[9]; /* my memory pool (from the mp3 buffer) */ char print[32]; /* use a common snprintf() buffer */ unsigned char* buf; /* up to here currently used by image(s) */ /* the remaining free part of the buffer for compressed+uncompressed images */ unsigned char* buf_images; ssize_t buf_size, buf_images_size; /* the root of the images, hereafter are decompresed ones */ unsigned char* buf_root; int root_size; int ds, ds_min, ds_max; /* downscaling and limits */ static struct jpeg jpg; /* too large for stack */ static struct tree_context *tree; /* the current full file name */ static char np_file[MAX_PATH]; int curfile = 0, direction = DIR_NONE, entries = 0; /* list of the jpeg files */ char **file_pt; /* are we using the plugin buffer or the audio buffer? */ bool plug_buf = false; /************************* Implementation ***************************/ #ifdef HAVE_LCD_COLOR /* * Conversion of full 0-255 range YCrCb to RGB: * |R| |1.000000 -0.000001 1.402000| |Y'| * |G| = |1.000000 -0.334136 -0.714136| |Pb| * |B| |1.000000 1.772000 0.000000| |Pr| * Scaled (yields s15-bit output): * |R| |128 0 179| |Y | * |G| = |128 -43 -91| |Cb - 128| * |B| |128 227 0| |Cr - 128| */ #define YFAC 128 #define RVFAC 179 #define GUFAC (-43) #define GVFAC (-91) #define BUFAC 227 #define YUV_WHITE (255*YFAC) #define NODITHER_DELTA (127*YFAC) #define COMPONENT_SHIFT 15 #define MATRIX_SHIFT 7 static inline int clamp_component(int x) { if ((unsigned)x > YUV_WHITE) x = x < 0 ? 0 : YUV_WHITE; return x; } static inline int clamp_component_bits(int x, int bits) { if ((unsigned)x > (1u << bits) - 1) x = x < 0 ? 0 : (1 << bits) - 1; return x; } static inline int component_to_lcd(int x, int bits, int delta) { /* Formula used in core bitmap loader. */ return (((1 << bits) - 1)*x + (x >> (8 - bits)) + delta) >> COMPONENT_SHIFT; } static inline int lcd_to_component(int x, int bits, int delta) { /* Reasonable, approximate reversal to get a full range back from the quantized value. */ return YUV_WHITE*x / ((1 << bits) - 1); (void)delta; } #define RED 0 #define GRN 1 #define BLU 2 struct rgb_err { int16_t errbuf[LCD_WIDTH+2]; /* Error record for line below */ } rgb_err_buffers[3]; fb_data rgb_linebuf[LCD_WIDTH]; /* Line buffer for scrolling when DITHER_DIFFUSION is set */ struct rgb_pixel { int r, g, b; /* Current pixel components in s16.0 */ int inc; /* Current line increment (-1 or 1) */ int row; /* Current row in source image */ int col; /* Current column in source image */ int ce[3]; /* Errors to apply to current pixel */ struct rgb_err *e; /* RED, GRN, BLU */ int epos; /* Current position in error record */ }; struct rgb_pixel *pixel; /** round and truncate to lcd depth **/ static fb_data pixel_to_lcd_colour(void) { struct rgb_pixel *p = pixel; int r, g, b; r = component_to_lcd(p->r, LCD_RED_BITS, NODITHER_DELTA); r = clamp_component_bits(r, LCD_RED_BITS); g = component_to_lcd(p->g, LCD_GREEN_BITS, NODITHER_DELTA); g = clamp_component_bits(g, LCD_GREEN_BITS); b = component_to_lcd(p->b, LCD_BLUE_BITS, NODITHER_DELTA); b = clamp_component_bits(b, LCD_BLUE_BITS); return LCD_RGBPACK_LCD(r, g, b); } /** write a monochrome pixel to the colour LCD **/ static fb_data pixel_to_lcd_gray(void) { int r, g, b; g = clamp_component(pixel->g); r = component_to_lcd(g, LCD_RED_BITS, NODITHER_DELTA); b = component_to_lcd(g, LCD_BLUE_BITS, NODITHER_DELTA); g = component_to_lcd(g, LCD_GREEN_BITS, NODITHER_DELTA); return LCD_RGBPACK_LCD(r, g, b); } /** * Bayer ordered dithering - swiped from the core bitmap loader. */ static fb_data pixel_odither_to_lcd(void) { /* canonical ordered dither matrix */ static const unsigned char dither_matrix[16][16] = { { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 }, {"friend_distance",{&default_distfriend, NULL}, {128, NULL}, 0, 999, def_int, ss_enem, &distfriend, 0}, {"dog_jumping",{&default_dog_jumping, NULL}, {1, NULL}, 0, 1, def_bool, ss_enem, &dog_jumping, 0}, #endif /* End of MBF AI extras */ {"sts_always_red",{&sts_always_red, NULL},{1, NULL},0,1, // no color changes on status bar def_bool,ss_stat, 0, 0}, {"sts_pct_always_gray",{&sts_pct_always_gray, NULL},{0, NULL},0,1, // 2/23/98 chg default def_bool,ss_stat, 0, 0}, // makes percent signs on status bar always gray {"sts_traditional_keys",{&sts_traditional_keys, NULL},{0, NULL},0,1, // killough 2/28/98 def_bool,ss_stat,0,0}, // disables doubled card and skull key display on status bar // {"traditional_menu",{&traditional_menu, NULL},{1, NULL},0,1, // def_bool,ss_none, 0, 0}, // force use of Doom's main menu ordering // killough 4/17/98 {"show_messages",{&showMessages, NULL},{1, NULL},0,1, def_bool,ss_none,0,0}, // enables message display {"autorun",{&autorun, NULL},{0, NULL},0,1, // killough 3/6/98: preserve autorun across games def_bool,ss_none,0,0}, {"Compatibility settings",{NULL, NULL},{0, NULL},UL,UL,def_none,ss_none, 0, 0}, {"comp_zombie",{&default_comp[comp_zombie], NULL},{0, NULL},0,1,def_bool,ss_comp,&comp[comp_zombie], 0}, {"comp_infcheat",{&default_comp[comp_infcheat], NULL},{0, NULL},0,1,def_bool,ss_comp,&comp[comp_infcheat], 0}, {"comp_stairs",{&default_comp[comp_stairs], NULL},{0, NULL},0,1,def_bool,ss_comp,&comp[comp_stairs], 0}, {"comp_telefrag",{&default_comp[comp_telefrag], NULL},{0, NULL},0,1,def_bool,ss_comp,&comp[comp_telefrag], 0}, {"comp_dropoff",{&default_comp[comp_dropoff], NULL},{0, NULL},0,1,def_bool,ss_comp,&comp[comp_dropoff], 0}, {"comp_falloff",{&default_comp[comp_falloff], NULL},{0, NULL},0,1,def_bool,ss_comp,&comp[comp_falloff], 0}, {"comp_staylift",{&default_comp[comp_staylift], NULL},{0, NULL},0,1,def_bool,ss_comp,&comp[comp_staylift], 0}, {"comp_doorstuck",{&default_comp[comp_doorstuck], NULL},{0, NULL},0,1,def_bool,ss_comp,&comp[comp_doorstuck], 0}, {"comp_pursuit",{&default_comp[comp_pursuit], NULL},{0, NULL},0,1,def_bool,ss_comp,&comp[comp_pursuit], 0}, {"comp_vile",{&default_comp[comp_vile], NULL},{0, NULL},0,1,def_bool,ss_comp,&comp[comp_vile], 0}, {"comp_pain",{&default_comp[comp_pain], NULL},{0, NULL},0,1,def_bool,ss_comp,&comp[comp_pain], 0}, {"comp_skull",{&default_comp[comp_skull], NULL},{0, NULL},0,1,def_bool,ss_comp,&comp[comp_skull], 0}, {"comp_blazing",{&default_comp[comp_blazing], NULL},{0, NULL},0,1,def_bool,ss_comp,&comp[comp_blazing], 0}, {"comp_doorlight",{&default_comp[comp_doorlight], NULL},{0, NULL},0,1,def_bool,ss_comp,&comp[comp_doorlight], 0}, {"comp_god",{&default_comp[comp_god], NULL},{0, NULL},0,1,def_bool,ss_comp,&comp[comp_god], 0}, {"comp_skymap",{&default_comp[comp_skymap], NULL},{0, NULL},0,1,def_bool,ss_comp,&comp[comp_skymap], 0}, {"comp_floors",{&default_comp[comp_floors], NULL},{0, NULL},0,1,def_bool,ss_comp,&comp[comp_floors], 0}, {"comp_model",{&default_comp[comp_model], NULL},{0, NULL},0,1,def_bool,ss_comp,&comp[comp_model], 0}, {"comp_zerotags",{&default_comp[comp_zerotags], NULL},{0, NULL},0,1,def_bool,ss_comp,&comp[comp_zerotags], 0}, {"comp_moveblock",{&default_comp[comp_moveblock], NULL},{0, NULL},0,1,def_bool,ss_comp,&comp[comp_moveblock], 0}, {"comp_sound",{&default_comp[comp_sound], NULL},{0, NULL},0,1,def_bool,ss_comp,&comp[comp_sound], 0}, {"Sound settings",{NULL, NULL},{0, NULL},UL,UL,def_none,ss_none, 0, 0}, // {"sound_card",{&snd_card, NULL},{-1, NULL},-1,7, // jff 1/18/98 allow Allegro drivers // def_int,ss_none, 0, 0}, // select sounds driver (DOS), -1 is autodetect, 0 is none; in Linux, non-zero enables sound // {"music_card",{&mus_card, NULL},{-1, NULL},-1,9, // to be set, -1 = autodetect // def_int,ss_none, 0, 0}, // select music driver (DOS), -1 is autodetect, 0 is none"; in Linux, non-zero enables music {"pitched_sounds",{&pitched_sounds, NULL},{0, NULL},0,1, // killough 2/21/98 def_bool,ss_none, 0, 0}, // enables variable pitch in sound effects (from id's original code) // {"samplerate",{&snd_samplerate, NULL},{22050, NULL},11025,48000, def_int,ss_none, 0, 0}, {"enable_sound",{(void *)&enable_sound, NULL},{0, NULL},0,1, def_bool,ss_none, 0, 0}, {"sfx_volume",{&snd_SfxVolume, NULL},{8, NULL},0,15, def_int,ss_none, 0, 0}, {"music_volume",{&snd_MusicVolume, NULL},{8, NULL},0,15, def_int,ss_none, 0, 0}, {"mus_pause_opt",{&mus_pause_opt, NULL},{2, NULL},0,2, // CPhipps - music pausing def_int, ss_none, 0, 0}, // 0 = kill music when paused, 1 = pause music, 2 = let music continue {"sounddev", {NULL,&snd_device}, {0,"/dev/dsp"},UL,UL, def_str,ss_none, 0, 0}, // sound output device (UNIX) {"snd_channels",{&default_numChannels, NULL},{4, NULL},1,32, def_int,ss_none, 0, 0}, // number of audio events simultaneously // killough {"Video settings",{NULL, NULL},{0, NULL},UL,UL,def_none,ss_none, 0, 0}, // CPhipps - default screensize for targets that support high-res /* {"screen_width",{&desired_screenwidth, NULL},{320, NULL}, 320, 1600, def_int,ss_none, 0, 0}, {"screen_height",{&desired_screenheight, NULL},{200, NULL},200,1200, def_int,ss_none, 0, 0},*/ #if(LCD_HEIGHT>LCD_WIDTH) {"rotate_screen",{(void *)&rotate_screen, NULL},{0, NULL},0,1, def_bool,ss_none, 0, 0}, /* kwk - rotate the screen 90 degrees */ #endif {"fake_contrast",{&fake_contrast, NULL},{1, NULL},0,1, def_bool,ss_none, 0, 0}, /* cph - allow crappy fake contrast to be disabled */ // {"use_fullscreen",{&use_fullscreen, NULL},{1, NULL},0,1, /* proff 21/05/2000 */ // def_bool,ss_none, 0, 0}, // {"use_doublebuffer",{&use_doublebuffer, NULL},{1, NULL},0,1, // proff 2001-7-4 // def_bool,ss_none, 0, 0}, // enable doublebuffer to avoid display tearing (fullscreen) {"translucency",{&default_translucency, NULL},{1, NULL},0,1, // phares def_bool,ss_none, 0, 0}, // enables translucency {"tran_filter_pct",{&tran_filter_pct, NULL},{66, NULL},0,100, // killough 2/21/98 def_int,ss_none, 0, 0}, // set percentage of foreground/background translucency mix {"screenblocks",{&screenblocks, NULL},{10, NULL},3,11, def_int,ss_none, 0, 0}, {"usegamma",{&usegamma, NULL},{1, NULL},0,4, //jff 3/6/98 fix erroneous upper limit in range def_int,ss_none, 0, 0}, // gamma correction level // killough 1/18/98 {"X_options",{&X_opt, NULL},{0, NULL},0,3, // CPhipps - misc X options def_hex,ss_none, 0, 0}, // X options, see l_video_x.c {"Mouse settings",{NULL, NULL},{0, NULL},UL,UL,def_none,ss_none, 0, 0}, {"use_mouse",{&usemouse, NULL},{1, NULL},0,1, def_bool,ss_none, 0, 0}, // enables use of mouse with DOOM //jff 4/3/98 allow unlimited sensitivity // {"mouse_sensitivity_horiz",{&mouseSensitivity_horiz, NULL},{10, NULL},0,UL, // def_int,ss_none, 0, 0}, /* adjust horizontal (x) mouse sensitivity killough/mead */ //jff 4/3/98 allow unlimited sensitivity // {"mouse_sensitivity_vert",{&mouseSensitivity_vert, NULL},{10, NULL},0,UL, // def_int,ss_none, 0, 0}, /* adjust vertical (y) mouse sensitivity killough/mead */ //jff 3/8/98 allow -1 in mouse bindings to disable mouse function {"mouseb_fire",{&mousebfire, NULL},{0, NULL},-1,MAX_MOUSEB, def_int,ss_keys, 0, 0}, // mouse button number to use for fire {"mouseb_strafe",{&mousebstrafe, NULL},{1, NULL},-1,MAX_MOUSEB, def_int,ss_keys, 0, 0}, // mouse button number to use for strafing {"mouseb_forward",{&mousebforward, NULL},{2, NULL},-1,MAX_MOUSEB, def_int,ss_keys, 0, 0}, // mouse button number to use for forward motion //jff 3/8/98 end of lower range change for -1 allowed in mouse binding // For key bindings, the values stored in the key_* variables // phares // are the internal Doom Codes. The values stored in the default.cfg // file are the keyboard codes. // CPhipps - now they're the doom codes, so default.cfg can be portable {"Key bindings",{NULL, NULL},{0, NULL},UL,UL,def_none,ss_none, 0, 0}, {"key_right", {&key_right, NULL}, {KEY_RIGHTARROW, NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to turn right {"key_left", {&key_left, NULL}, {KEY_LEFTARROW, NULL} , 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to turn left {"key_up", {&key_up, NULL}, {KEY_UPARROW, NULL} , 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to move forward {"key_down", {&key_down, NULL}, {KEY_DOWNARROW, NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to move backward {"key_menu_right", {&key_menu_right, NULL}, {KEY_RIGHTARROW, NULL},// phares 3/7/98 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to move right in a menu // | {"key_menu_left", {&key_menu_left, NULL}, {KEY_LEFTARROW, NULL} ,// V 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to move left in a menu {"key_menu_up", {&key_menu_up, NULL}, {KEY_UPARROW,NULL} , 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to move up in a menu {"key_menu_down", {&key_menu_down, NULL}, {KEY_DOWNARROW, NULL} , 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to move down in a menu {"key_menu_backspace",{&key_menu_backspace, NULL},{KEY_BACKSPACE, NULL} , 0,MAX_KEY,def_key,ss_keys, 0, 0}, // delete key in a menu {"key_menu_escape", {&key_menu_escape, NULL}, {KEY_ESCAPE, NULL} , 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to leave a menu , // phares 3/7/98 {"key_menu_enter", {&key_menu_enter, NULL}, {KEY_ENTER, NULL} , 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to select from menu {"key_strafeleft", {&key_strafeleft, NULL}, {',', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to strafe left {"key_straferight", {&key_straferight, NULL}, {'.', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to strafe right {"key_fire", {&key_fire, NULL}, {KEY_RCTRL, NULL} , 0,MAX_KEY,def_key,ss_keys, 0, 0}, // duh {"key_use", {&key_use, NULL}, {' ', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to open a door, use a switch {"key_strafe", {&key_strafe, NULL}, {'s', NULL} , 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to use with arrows to strafe {"key_speed", {&key_speed, NULL}, {KEY_RSHIFT, NULL} , 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to run {"key_savegame", {&key_savegame, NULL}, {KEY_F2, NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to save current game {"key_loadgame", {&key_loadgame, NULL}, {KEY_F3, NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to restore from saved games {"key_soundvolume", {&key_soundvolume, NULL}, {KEY_F4, NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to bring up sound controls {"key_hud", {&key_hud, NULL}, {KEY_F5, NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to adjust HUD {"key_quicksave", {&key_quicksave, NULL}, {KEY_F6, NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to to quicksave {"key_endgame", {&key_endgame, NULL}, {KEY_F7, NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to end the game {"key_messages", {&key_messages, NULL}, {KEY_F8, NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to toggle message enable {"key_quickload", {&key_quickload, NULL}, {KEY_F9, NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to load from quicksave {"key_quit", {&key_quit, NULL}, {KEY_F10, NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to quit game {"key_gamma", {&key_gamma, NULL}, {KEY_F11, NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to adjust gamma correction {"key_spy", {&key_spy, NULL}, {KEY_F12, NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to view from another coop player's view {"key_pause", {&key_pause, NULL}, {KEY_PAUSE, NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to pause the game {"key_autorun", {&key_autorun, NULL}, {KEY_CAPSLOCK, NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to toggle always run mode {"key_chat", {&key_chat, NULL}, {'t', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to enter a chat message {"key_backspace", {&key_backspace, NULL}, {KEY_BACKSPACE, NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // backspace key {"key_enter", {&key_enter, NULL}, {0, NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to select from menu or see last message {"key_map", {&key_map, NULL}, {KEY_TAB, NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to toggle automap display {"key_map_right", {&key_map_right, NULL}, {KEY_RIGHTARROW, NULL},// phares 3/7/98 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to shift automap right // | {"key_map_left", {&key_map_left, NULL}, {KEY_LEFTARROW, NULL},// V 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to shift automap left {"key_map_up", {&key_map_up, NULL}, {KEY_UPARROW, NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to shift automap up {"key_map_down", {&key_map_down, NULL}, {KEY_DOWNARROW, NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to shift automap down {"key_map_zoomin", {&key_map_zoomin, NULL}, {'=', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to enlarge automap {"key_map_zoomout", {&key_map_zoomout, NULL}, {'-', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to reduce automap {"key_map_gobig", {&key_map_gobig, NULL}, {'0', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to get max zoom for automap {"key_map_follow", {&key_map_follow, NULL}, {'f', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to toggle follow mode {"key_map_mark", {&key_map_mark, NULL}, {'m', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to drop a marker on automap {"key_map_clear", {&key_map_clear, NULL}, {'c', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to clear all markers on automap {"key_map_grid", {&key_map_grid, NULL}, {'g', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to toggle grid display over automap {"key_map_rotate", {&key_map_rotate, NULL}, {'r', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to toggle rotating the automap to match the player's orientation {"key_map_overlay", {&key_map_overlay, NULL}, {'o', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to toggle overlaying the automap on the rendered display {"key_reverse", {&key_reverse, NULL}, {'/', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to spin 180 instantly {"key_zoomin", {&key_zoomin, NULL}, {'=', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to enlarge display {"key_zoomout", {&key_zoomout, NULL}, {'-', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to reduce display {"key_chatplayer1", {&destination_keys[0], NULL}, {'g', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to chat with player 1 // killough 11/98: fix 'i'/'b' reversal {"key_chatplayer2", {&destination_keys[1], NULL}, {'i', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to chat with player 2 {"key_chatplayer3", {&destination_keys[2], NULL}, {'b', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to chat with player 3 {"key_chatplayer4", {&destination_keys[3], NULL}, {'r', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to chat with player 4 {"key_weapon",{&key_weapon, NULL}, {'w', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to toggle between two most preferred weapons with ammo {"key_weapontoggle",{&key_weapontoggle, NULL}, {'0', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to toggle between two most preferred weapons with ammo {"key_weapon1", {&key_weapon1, NULL}, {'1', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to switch to weapon 1 (fist/chainsaw) {"key_weapon2", {&key_weapon2, NULL}, {'2', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to switch to weapon 2 (pistol) {"key_weapon3", {&key_weapon3, NULL}, {'3', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to switch to weapon 3 (supershotgun/shotgun) {"key_weapon4", {&key_weapon4, NULL}, {'4', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to switch to weapon 4 (chaingun) {"key_weapon5", {&key_weapon5, NULL}, {'5', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to switch to weapon 5 (rocket launcher) {"key_weapon6", {&key_weapon6, NULL}, {'6', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to switch to weapon 6 (plasma rifle) {"key_weapon7", {&key_weapon7, NULL}, {'7', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to switch to weapon 7 (bfg9000) // ^ {"key_weapon8", {&key_weapon8, NULL}, {'8', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to switch to weapon 8 (chainsaw) // | {"key_weapon9", {&key_weapon9, NULL}, {'9', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to switch to weapon 9 (supershotgun) // phares // killough 2/22/98: screenshot key {"key_screenshot", {&key_screenshot, NULL}, {'*', NULL}, 0,MAX_KEY,def_key,ss_keys, 0, 0}, // key to take a screenshot /* {"Joystick settings",{NULL, NULL},{0, NULL},UL,UL,def_none,ss_none, 0, 0}, {"use_joystick",{&usejoystick, NULL},{0, NULL},0,2, def_int,ss_none, 0, 0}, // number of joystick to use (0 for none) {"joy_left",{&joyleft, NULL},{0, NULL}, UL,UL,def_int,ss_none, 0, 0}, {"joy_right",{&joyright, NULL},{0, NULL},UL,UL,def_int,ss_none, 0, 0}, {"joy_up", {&joyup, NULL}, {0, NULL}, UL,UL,def_int,ss_none, 0, 0}, {"joy_down",{&joydown, NULL},{0, NULL}, UL,UL,def_int,ss_none, 0, 0}, {"joyb_fire",{&joybfire, NULL},{0, NULL},0,UL, def_int,ss_keys, 0, 0}, // joystick button number to use for fire {"joyb_strafe",{&joybstrafe, NULL},{1, NULL},0,UL, def_int,ss_keys, 0, 0}, // joystick button number to use for strafing {"joyb_speed",{&joybspeed, NULL},{2, NULL},0,UL, def_int,ss_keys, 0, 0}, // joystick button number to use for running {"joyb_use",{&joybuse, NULL},{3, NULL},0,UL, def_int,ss_keys, 0, 0}, // joystick button number to use for use/open */ {"Chat macros",{NULL, NULL},{0, NULL},UL,UL,def_none,ss_none, 0, 0}, {"chatmacro0", {0,&chat_macros[0]}, {0,HUSTR_CHATMACRO0},UL,UL, def_str,ss_chat, 0, 0}, // chat string associated with 0 key {"chatmacro1", {0,&chat_macros[1]}, {0,HUSTR_CHATMACRO1},UL,UL, def_str,ss_chat, 0, 0}, // chat string associated with 1 key {"chatmacro2", {0,&chat_macros[2]}, {0,HUSTR_CHATMACRO2},UL,UL, def_str,ss_chat, 0, 0}, // chat string associated with 2 key {"chatmacro3", {0,&chat_macros[3]}, {0,HUSTR_CHATMACRO3},UL,UL, def_str,ss_chat, 0, 0}, // chat string associated with 3 key {"chatmacro4", {0,&chat_macros[4]}, {0,HUSTR_CHATMACRO4},UL,UL, def_str,ss_chat, 0, 0}, // chat string associated with 4 key {"chatmacro5", {0,&chat_macros[5]}, {0,HUSTR_CHATMACRO5},UL,UL, def_str,ss_chat, 0, 0}, // chat string associated with 5 key {"chatmacro6", {0,&chat_macros[6]}, {0,HUSTR_CHATMACRO6},UL,UL, def_str,ss_chat, 0, 0}, // chat string associated with 6 key {"chatmacro7", {0,&chat_macros[7]}, {0,HUSTR_CHATMACRO7},UL,UL, def_str,ss_chat, 0, 0}, // chat string associated with 7 key {"chatmacro8", {0,&chat_macros[8]}, {0,HUSTR_CHATMACRO8},UL,UL, def_str,ss_chat, 0, 0}, // chat string associated with 8 key {"chatmacro9", {0,&chat_macros[9]}, {0,HUSTR_CHATMACRO9},UL,UL, def_str,ss_chat, 0, 0}, // chat string associated with 9 key {"Automap settings",{NULL, NULL},{0, NULL},UL,UL,def_none,ss_none, 0, 0}, //jff 1/7/98 defaults for automap colors //jff 4/3/98 remove -1 in lower range, 0 now disables new map features {"mapcolor_back", {&mapcolor_back, NULL}, {247, NULL},0,255, // black //jff 4/6/98 new black def_colour,ss_auto, 0, 0}, // color used as background for automap {"mapcolor_grid", {&mapcolor_grid, NULL}, {104, NULL},0,255, // dk gray def_colour,ss_auto, 0, 0}, // color used for automap grid lines {"mapcolor_wall", {&mapcolor_wall, NULL}, {23, NULL},0,255, // red-brown def_colour,ss_auto, 0, 0}, // color used for one side walls on automap {"mapcolor_fchg", {&mapcolor_fchg, NULL}, {55, NULL},0,255, // lt brown def_colour,ss_auto, 0, 0}, // color used for lines floor height changes across {"mapcolor_cchg", {&mapcolor_cchg, NULL}, {215, NULL},0,255, // orange def_colour,ss_auto, 0, 0}, // color used for lines ceiling height changes across {"mapcolor_clsd", {&mapcolor_clsd, NULL}, {208, NULL},0,255, // white def_colour,ss_auto, 0, 0}, // color used for lines denoting closed doors, objects {"mapcolor_rkey", {&mapcolor_rkey, NULL}, {175, NULL},0,255, // red def_colour,ss_auto, 0, 0}, // color used for red key sprites {"mapcolor_bkey", {&mapcolor_bkey, NULL}, {204, NULL},0,255, // blue def_colour,ss_auto, 0, 0}, // color used for blue key sprites {"mapcolor_ykey", {&mapcolor_ykey, NULL}, {231, NULL},0,255, // yellow def_colour,ss_auto, 0, 0}, // color used for yellow key sprites {"mapcolor_rdor", {&mapcolor_rdor, NULL}, {175, NULL},0,255, // red def_colour,ss_auto, 0, 0}, // color used for closed red doors {"mapcolor_bdor", {&mapcolor_bdor, NULL}, {204, NULL},0,255, // blue def_colour,ss_auto, 0, 0}, // color used for closed blue doors {"mapcolor_ydor", {&mapcolor_ydor, NULL}, {231, NULL},0,255, // yellow def_colour,ss_auto, 0, 0}, // color used for closed yellow doors {"mapcolor_tele", {&mapcolor_tele, NULL}, {119, NULL},0,255, // dk green def_colour,ss_auto, 0, 0}, // color used for teleporter lines {"mapcolor_secr", {&mapcolor_secr, NULL}, {252, NULL},0,255, // purple def_colour,ss_auto, 0, 0}, // color used for lines around secret sectors {"mapcolor_exit", {&mapcolor_exit, NULL}, {0, NULL},0,255, // none def_colour,ss_auto, 0, 0}, // color used for exit lines {"mapcolor_unsn", {&mapcolor_unsn, NULL}, {104, NULL},0,255, // dk gray def_colour,ss_auto, 0, 0}, // color used for lines not seen without computer map {"mapcolor_flat", {&mapcolor_flat, NULL}, {88, NULL},0,255, // lt gray def_colour,ss_auto, 0, 0}, // color used for lines with no height changes {"mapcolor_sprt", {&mapcolor_sprt, NULL}, {112, NULL},0,255, // green def_colour,ss_auto, 0, 0}, // color used as things {"mapcolor_item", {&mapcolor_item, NULL}, {231, NULL},0,255, // yellow def_colour,ss_auto, 0, 0}, // color used for counted items {"mapcolor_hair", {&mapcolor_hair, NULL}, {208, NULL},0,255, // white def_colour,ss_auto, 0, 0}, // color used for dot crosshair denoting center of map {"mapcolor_sngl", {&mapcolor_sngl, NULL}, {208, NULL},0,255, // white def_colour,ss_auto, 0, 0}, // color used for the single player arrow /* {"mapcolor_me", {&mapcolor_me, NULL}, {112, NULL},0,255, // green def_colour,ss_auto, 0, 0}, // your (player) colour*/ {"mapcolor_frnd", {&mapcolor_frnd, NULL}, {112, NULL},0,255, def_colour,ss_auto, 0, 0}, //jff 3/9/98 add option to not show secrets til after found {"map_secret_after", {&map_secret_after, NULL}, {0, NULL},0,1, // show secret after gotten def_bool,ss_auto, 0, 0}, // prevents showing secret sectors till after entered {"map_point_coord", {&map_point_coordinates, NULL}, {0, NULL},0,1, def_bool,ss_auto, 0, 0}, //jff 1/7/98 end additions for automap {"automapmode", {(void*)&automapmode, NULL}, {0, NULL}, 0, 31, // CPhipps - remember automap mode def_hex,ss_none, 0, 0}, // automap mode {"Heads-up display settings",{NULL, NULL},{0, NULL},UL,UL,def_none,ss_none, 0, 0}, //jff 2/16/98 defaults for color ranges in hud and status {"hudcolor_titl", {&hudcolor_titl, NULL}, {5, NULL},0,9, // gold range def_int,ss_auto, 0, 0}, // color range used for automap level title {"hudcolor_xyco", {&hudcolor_xyco, NULL}, {3, NULL},0,9, // green range def_int,ss_auto, 0, 0}, // color range used for automap coordinates {"hudcolor_mesg", {&hudcolor_mesg, NULL}, {6, NULL},0,9, // red range def_int,ss_mess, 0, 0}, // color range used for messages during play {"hudcolor_chat", {&hudcolor_chat, NULL}, {5, NULL},0,9, // gold range def_int,ss_mess, 0, 0}, // color range used for chat messages and entry {"hudcolor_list", {&hudcolor_list, NULL}, {5, NULL},0,9, // gold range //jff 2/26/98 def_int,ss_mess, 0, 0}, // color range used for message review {"hud_msg_lines", {&hud_msg_lines, NULL}, {1, NULL},1,16, // 1 line scrolling window def_int,ss_mess, 0, 0}, // number of messages in review display (1=disable) {"hud_list_bgon", {&hud_list_bgon, NULL}, {0, NULL},0,1, // solid window bg ena //jff 2/26/98 def_bool,ss_mess, 0, 0}, // enables background window behind message review {"hud_distributed",{&hud_distributed, NULL},{0, NULL},0,1, // hud broken up into 3 displays //jff 3/4/98 def_bool,ss_none, 0, 0}, // splits HUD into three 2 line displays {"health_red", {&health_red, NULL}, {25, NULL},0,200, // below is red def_int,ss_stat, 0, 0}, // amount of health for red to yellow transition {"health_yellow", {&health_yellow, NULL}, {50, NULL},0,200, // below is yellow def_int,ss_stat, 0, 0}, // amount of health for yellow to green transition {"health_green", {&health_green, NULL}, {100, NULL},0,200,// below is green, above blue def_int,ss_stat, 0, 0}, // amount of health for green to blue transition {"armor_red", {&armor_red, NULL}, {25, NULL},0,200, // below is red def_int,ss_stat, 0, 0}, // amount of armor for red to yellow transition {"armor_yellow", {&armor_yellow, NULL}, {50, NULL},0,200, // below is yellow def_int,ss_stat, 0, 0}, // amount of armor for yellow to green transition {"armor_green", {&armor_green, NULL}, {100, NULL},0,200,// below is green, above blue def_int,ss_stat, 0, 0}, // amount of armor for green to blue transition {"ammo_red", {&ammo_red, NULL}, {25, NULL},0,100, // below 25% is red def_int,ss_stat, 0, 0}, // percent of ammo for red to yellow transition {"ammo_yellow", {&ammo_yellow, NULL}, {50, NULL},0,100, // below 50% is yellow, above green def_int,ss_stat, 0, 0}, // percent of ammo for yellow to green transition //jff 2/16/98 HUD and status feature controls {"hud_active", {&hud_active, NULL}, {1, NULL},0,2, // 0=off, 1=small, 2=full def_int,ss_none, 0, 0}, // 0 for HUD off, 1 for HUD small, 2 for full HUD //jff 2/23/98 {"hud_displayed", {&hud_displayed, NULL}, {0, NULL},0,1, // whether hud is displayed def_bool,ss_none, 0, 0}, // enables display of HUD {"hud_nosecrets", {&hud_nosecrets, NULL}, {0, NULL},0,1, // no secrets/items/kills HUD line def_bool,ss_stat, 0, 0}, // disables display of kills/items/secrets on HUD {"Weapon preferences",{NULL, NULL},{0, NULL},UL,UL,def_none,ss_none, 0, 0}, // killough 2/8/98: weapon preferences set by user: {"weapon_choice_1", {&weapon_preferences[0][0], NULL}, {6, NULL}, 0,9, def_int,ss_weap, 0, 0}, // first choice for weapon (best) {"weapon_choice_2", {&weapon_preferences[0][1], NULL}, {9, NULL}, 0,9, def_int,ss_weap, 0, 0}, // second choice for weapon {"weapon_choice_3", {&weapon_preferences[0][2], NULL}, {4, NULL}, 0,9, def_int,ss_weap, 0, 0}, // third choice for weapon {"weapon_choice_4", {&weapon_preferences[0][3], NULL}, {3, NULL}, 0,9, def_int,ss_weap, 0, 0}, // fourth choice for weapon {"weapon_choice_5", {&weapon_preferences[0][4], NULL}, {2, NULL}, 0,9, def_int,ss_weap, 0, 0}, // fifth choice for weapon {"weapon_choice_6", {&weapon_preferences[0][5], NULL}, {8, NULL}, 0,9, def_int,ss_weap, 0, 0}, // sixth choice for weapon {"weapon_choice_7", {&weapon_preferences[0][6], NULL}, {5, NULL}, 0,9, def_int,ss_weap, 0, 0}, // seventh choice for weapon {"weapon_choice_8", {&weapon_preferences[0][7], NULL}, {7, NULL}, 0,9, def_int,ss_weap, 0, 0}, // eighth choice for weapon {"weapon_choice_9", {&weapon_preferences[0][8], NULL}, {1, NULL}, 0,9, def_int,ss_weap, 0, 0}, // ninth choice for weapon (worst) /* // cournia - support for arbitrary music file (defaults are mp3) {"Music", {NULL, NULL},{0, NULL},UL,UL,def_none,ss_none, 0, 0}, {"mus_e1m1", {0,&S_music_files[mus_e1m1]}, {0,"e1m1.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_e1m2", {0,&S_music_files[mus_e1m2]}, {0,"e1m2.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_e1m3", {0,&S_music_files[mus_e1m3]}, {0,"e1m3.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_e1m4", {0,&S_music_files[mus_e1m4]}, {0,"e1m4.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_e1m5", {0,&S_music_files[mus_e1m5]}, {0,"e1m5.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_e1m6", {0,&S_music_files[mus_e1m6]}, {0,"e1m6.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_e1m7", {0,&S_music_files[mus_e1m7]}, {0,"e1m7.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_e1m8", {0,&S_music_files[mus_e1m8]}, {0,"e1m8.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_e1m9", {0,&S_music_files[mus_e1m9]}, {0,"e1m9.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_e2m1", {0,&S_music_files[mus_e2m1]}, {0,"e2m1.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_e2m2", {0,&S_music_files[mus_e2m2]}, {0,"e2m2.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_e2m3", {0,&S_music_files[mus_e2m3]}, {0,"e2m3.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_e2m4", {0,&S_music_files[mus_e2m4]}, {0,"e2m4.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_e2m5", {0,&S_music_files[mus_e2m5]}, {0,"e1m7.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_e2m6", {0,&S_music_files[mus_e2m6]}, {0,"e2m6.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_e2m7", {0,&S_music_files[mus_e2m7]}, {0,"e2m7.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_e2m8", {0,&S_music_files[mus_e2m8]}, {0,"e2m8.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_e2m9", {0,&S_music_files[mus_e2m9]}, {0,"e3m1.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_e3m1", {0,&S_music_files[mus_e3m1]}, {0,"e3m1.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_e3m2", {0,&S_music_files[mus_e3m2]}, {0,"e3m2.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_e3m3", {0,&S_music_files[mus_e3m3]}, {0,"e3m3.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_e3m4", {0,&S_music_files[mus_e3m4]}, {0,"e1m8.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_e3m5", {0,&S_music_files[mus_e3m5]}, {0,"e1m7.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_e3m6", {0,&S_music_files[mus_e3m6]}, {0,"e1m6.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_e3m7", {0,&S_music_files[mus_e3m7]}, {0,"e2m7.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_e3m8", {0,&S_music_files[mus_e3m8]}, {0,"e3m8.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_e3m9", {0,&S_music_files[mus_e3m9]}, {0,"e1m9.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_inter", {0,&S_music_files[mus_inter]}, {0,"e2m3.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_intro", {0,&S_music_files[mus_intro]}, {0,"intro.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_bunny", {0,&S_music_files[mus_bunny]}, {0,"bunny.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_victor", {0,&S_music_files[mus_victor]}, {0,"victor.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_introa", {0,&S_music_files[mus_introa]}, {0,"intro.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_runnin", {0,&S_music_files[mus_runnin]}, {0,"runnin.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_stalks", {0,&S_music_files[mus_stalks]}, {0,"stalks.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_countd", {0,&S_music_files[mus_countd]}, {0,"countd.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_betwee", {0,&S_music_files[mus_betwee]}, {0,"betwee.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_doom", {0,&S_music_files[mus_doom]}, {0,"doom.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_the_da", {0,&S_music_files[mus_the_da]}, {0,"the_da.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_shawn", {0,&S_music_files[mus_shawn]}, {0,"shawn.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_ddtblu", {0,&S_music_files[mus_ddtblu]}, {0,"ddtblu.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_in_cit", {0,&S_music_files[mus_in_cit]}, {0,"in_cit.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_dead", {0,&S_music_files[mus_dead]}, {0,"dead.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_stlks2", {0,&S_music_files[mus_stlks2]}, {0,"stalks.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_theda2", {0,&S_music_files[mus_theda2]}, {0,"the_da.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_doom2", {0,&S_music_files[mus_doom2]}, {0,"doom.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_ddtbl2", {0,&S_music_files[mus_ddtbl2]}, {0,"ddtblu.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_runni2", {0,&S_music_files[mus_runni2]}, {0,"runnin.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_dead2", {0,&S_music_files[mus_dead2]}, {0,"dead.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_stlks3", {0,&S_music_files[mus_stlks3]}, {0,"stalks.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_romero", {0,&S_music_files[mus_romero]}, {0,"romero.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_shawn2", {0,&S_music_files[mus_shawn2]}, {0,"shawn.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_messag", {0,&S_music_files[mus_messag]}, {0,"messag.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_count2", {0,&S_music_files[mus_count2]}, {0,"countd.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_ddtbl3", {0,&S_music_files[mus_ddtbl3]}, {0,"ddtblu.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_ampie", {0,&S_music_files[mus_ampie]}, {0,"ampie.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_theda3", {0,&S_music_files[mus_theda3]}, {0,"the_da.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_adrian", {0,&S_music_files[mus_adrian]}, {0,"adrian.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_messg2", {0,&S_music_files[mus_messg2]}, {0,"messag.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_romer2", {0,&S_music_files[mus_romer2]}, {0,"romero.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_tense", {0,&S_music_files[mus_tense]}, {0,"tense.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_shawn3", {0,&S_music_files[mus_shawn3]}, {0,"shawn.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_openin", {0,&S_music_files[mus_openin]}, {0,"openin.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_evil", {0,&S_music_files[mus_evil]}, {0,"evil.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_ultima", {0,&S_music_files[mus_ultima]}, {0,"ultima.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_read_m", {0,&S_music_files[mus_read_m]}, {0,"read_m.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_dm2ttl", {0,&S_music_files[mus_dm2ttl]}, {0,"dm2ttl.mp3"},UL,UL, def_str,ss_none, 0, 0}, {"mus_dm2int", {0,&S_music_files[mus_dm2int]}, {0,"dm2int.mp3"},UL,UL, def_str,ss_none, 0, 0}, */ }; int numdefaults; //static const char* defaultfile; // CPhipps - static, const // // M_SaveDefaults // void M_SaveDefaults (void) { int i,fd; uint32_t magic = DOOM_CONFIG_MAGIC; uint32_t ver = DOOM_CONFIG_VERSION; fd = open (GAMEBASE"default.dfg", O_WRONLY|O_CREAT|O_TRUNC); if (fd<0) return; // can't write the file, but don't complain write(fd,&magic,sizeof(magic)); write(fd,&ver,sizeof(ver)); for (i=0 ; i<numdefaults ; i++) if(defaults[i].location.pi) write(fd,defaults[i].location.pi, sizeof(int)); close (fd); } /* * M_LookupDefault * * cph - mimic MBF function for now. Yes it's crap. */ struct default_s *M_LookupDefault(const char *name) { int i; for (i = 0 ; i < numdefaults ; i++) if ((defaults[i].type != def_none) && !strcmp(name, defaults[i].name)) return &defaults[i]; I_Error("M_LookupDefault: %s not found",name); return NULL; } // // M_LoadDefaults // #define NUMCHATSTRINGS 10 // phares 4/13/98 void M_LoadDefaults (void) { int i; uint32_t magic = 0; uint32_t ver; int fd; // set everything to base values numdefaults = sizeof(defaults)/sizeof(defaults[0]); for (i = 0 ; i < numdefaults ; i++) { if (defaults[i].location.ppsz) *defaults[i].location.ppsz = strdup(defaults[i].defaultvalue.psz); if (defaults[i].location.pi) *defaults[i].location.pi = defaults[i].defaultvalue.i; } fd = open (GAMEBASE"default.dfg", O_RDONLY); if (fd<0) return; // don't have anything to read read(fd,&magic,sizeof(magic)); if (magic != DOOM_CONFIG_MAGIC) { close(fd); return; } read(fd,&ver,sizeof(ver)); if (ver != DOOM_CONFIG_VERSION) { close(fd); return; } for (i=0 ; i<numdefaults ; i++) if(defaults[i].location.pi) read(fd,defaults[i].location.pi, sizeof(int)); close (fd); } // // SCREEN SHOTS // // CPhipps - nasty but better than nothing static boolean screenshot_write_error; // jff 3/30/98 types and data structures for BMP output of screenshots // // killough 5/2/98: // Changed type names to avoid conflicts with endianess functions #define BI_RGB 0L typedef unsigned long dword_t; typedef long long_t; typedef unsigned char ubyte_t; typedef struct tagBITMAPFILEHEADER { unsigned short bfType; dword_t bfSize; unsigned short bfReserved1; unsigned short bfReserved2; dword_t bfOffBits; } PACKEDATTR BITMAPFILEHEADER; typedef struct tagBITMAPINFOHEADER { dword_t biSize; long_t biWidth; long_t biHeight; unsigned short biPlanes; unsigned short biBitCount; dword_t biCompression; dword_t biSizeImage; long_t biXPelsPerMeter; long_t biYPelsPerMeter; dword_t biClrUsed; dword_t biClrImportant; } PACKEDATTR BITMAPINFOHEADER; #if 0 // jff 3/30/98 binary file write with error detection // CPhipps - static, const on parameter static void SafeWrite(const void *data, size_t size, size_t number, int st) { /* if (write(data,size,number,st)<number) screenshot_write_error = true; // CPhipps - made non-fatal*/ } #endif // // WriteBMPfile // jff 3/30/98 Add capability to write a .BMP file (256 color uncompressed) // // CPhipps - static, const on parameters static void WriteBMPfile(const char* filename, const byte* data, const int width, const int height, const byte* palette) { (void)filename; (void)data; (void)width; (void)height; (void)palette; /* int i,wid; BITMAPFILEHEADER bmfh; BITMAPINFOHEADER bmih; int fhsiz,ihsiz; FILE *st; char zero=0; ubyte_t c; fhsiz = sizeof(BITMAPFILEHEADER); ihsiz = sizeof(BITMAPINFOHEADER); wid = 4*((width+3)/4); //jff 4/22/98 add endian macros bmfh.bfType = SHORT(19778); bmfh.bfSize = LONG(fhsiz+ihsiz+256L*4+width*height); bmfh.bfReserved1 = SHORT(0); bmfh.bfReserved2 = SHORT(0); bmfh.bfOffBits = LONG(fhsiz+ihsiz+256L*4); bmih.biSize = LONG(ihsiz); bmih.biWidth = LONG(width); bmih.biHeight = LONG(height); bmih.biPlanes = SHORT(1); bmih.biBitCount = SHORT(8); bmih.biCompression = LONG(BI_RGB); bmih.biSizeImage = LONG(wid*height); bmih.biXPelsPerMeter = LONG(0); bmih.biYPelsPerMeter = LONG(0); bmih.biClrUsed = LONG(256); bmih.biClrImportant = LONG(256); st = fopen(filename,"wb"); if (st!=NULL) { // write the header SafeWrite(&bmfh.bfType,sizeof(bmfh.bfType),1,st); SafeWrite(&bmfh.bfSize,sizeof(bmfh.bfSize),1,st); SafeWrite(&bmfh.bfReserved1,sizeof(bmfh.bfReserved1),1,st); SafeWrite(&bmfh.bfReserved2,sizeof(bmfh.bfReserved2),1,st); SafeWrite(&bmfh.bfOffBits,sizeof(bmfh.bfOffBits),1,st); SafeWrite(&bmih.biSize,sizeof(bmih.biSize),1,st); SafeWrite(&bmih.biWidth,sizeof(bmih.biWidth),1,st); SafeWrite(&bmih.biHeight,sizeof(bmih.biHeight),1,st); SafeWrite(&bmih.biPlanes,sizeof(bmih.biPlanes),1,st); SafeWrite(&bmih.biBitCount,sizeof(bmih.biBitCount),1,st); SafeWrite(&bmih.biCompression,sizeof(bmih.biCompression),1,st); SafeWrite(&bmih.biSizeImage,sizeof(bmih.biSizeImage),1,st); SafeWrite(&bmih.biXPelsPerMeter,sizeof(bmih.biXPelsPerMeter),1,st); SafeWrite(&bmih.biYPelsPerMeter,sizeof(bmih.biYPelsPerMeter),1,st); SafeWrite(&bmih.biClrUsed,sizeof(bmih.biClrUsed),1,st); SafeWrite(&bmih.biClrImportant,sizeof(bmih.biClrImportant),1,st); // write the palette, in blue-green-red order, gamma corrected for (i=0;i<768;i+=3) { c=gammatable[usegamma][palette[i+2]]; SafeWrite(&c,sizeof(char),1,st); c=gammatable[usegamma][palette[i+1]]; SafeWrite(&c,sizeof(char),1,st); c=gammatable[usegamma][palette[i+0]]; SafeWrite(&c,sizeof(char),1,st); SafeWrite(&zero,sizeof(char),1,st); } for (i = 0 ; i < height ; i++) SafeWrite(data+(height-1-i)*width,sizeof(byte),wid,st); fclose(st); }*/ } // // M_ScreenShot // // Modified by Lee Killough so that any number of shots can be taken, // the code is faster, and no annoying "screenshot" message appears. // CPhipps - modified to use its own buffer for the image // - checks for the case where no file can be created (doesn't occur on POSIX systems, would on DOS) // - track errors better // - split into 2 functions // // M_DoScreenShot // Takes a screenshot into the names file void M_DoScreenShot (const char* fname) { byte *linear; #ifndef GL_DOOM const byte *pal; int pplump = W_GetNumForName("PLAYPAL"); #endif screenshot_write_error = false; #ifdef GL_DOOM // munge planar buffer to linear // CPhipps - use a malloc()ed buffer instead of screens[2] gld_ReadScreen(linear = malloc(SCREENWIDTH * SCREENHEIGHT * 3)); // save the bmp file WriteTGAfile (fname, linear, SCREENWIDTH, SCREENHEIGHT); #else // munge planar buffer to linear // CPhipps - use a malloc()ed buffer instead of screens[2] I_ReadScreen(linear = malloc(SCREENWIDTH * SCREENHEIGHT)); // killough 4/18/98: make palette stay around (PU_CACHE could cause crash) pal = W_CacheLumpNum (pplump); // save the bmp file WriteBMPfile (fname, linear, SCREENWIDTH, SCREENHEIGHT, pal); // cph - free the palette W_UnlockLumpNum(pplump); #endif free(linear); // 1/18/98 killough: replace "SCREEN SHOT" acknowledgement with sfx if (screenshot_write_error) doom_printf("M_ScreenShot: Error writing screenshot"); } void M_ScreenShot(void) { static int shot; char lbmname[32]; int startshot; screenshot_write_error = false; if (fileexists(".")) screenshot_write_error = true; startshot = shot; // CPhipps - prevent infinite loop do snprintf(lbmname,sizeof(lbmname),"DOOM%d.BMP", shot++); while (!fileexists(lbmname) && (shot != startshot) && (shot < 10000)); if (!fileexists(lbmname)) screenshot_write_error = true; if (screenshot_write_error) { doom_printf ("M_ScreenShot: Couldn't create a BMP"); // killough 4/18/98 return; } M_DoScreenShot(lbmname); // cph S_StartSound(NULL,gamemode==commercial ? sfx_radio : sfx_tink); }