/* FCE Ultra - NES/Famicom Emulator * * Copyright notice for this file: * Copyright (C) 2002,2003 Xodnizel * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "fceu-types.h" #include "fceu.h" #include "general.h" #include "driver.h" #include "palette.h" #include "palettes/palettes.h" #ifndef M_PI #define M_PI 3.14159265358979323846 #endif static int ntsccol = 0; static int ntsctint = 46 + 10; static int ntschue = 72; /* These are dynamically filled/generated palettes: */ pal palettei[64]; // Custom palette for an individual game. pal palettec[64]; // Custom "global" palette. pal paletten[64]; // Mathematically generated palette. static void CalculatePalette(void); static void ChoosePalette(void); static void WritePalette(void); uint8 pale = 0; pal *palo; static pal *palpoint[8] = { palette, rp2c04001, rp2c04002, rp2c04003, rp2c05004, }; void FCEUI_SetPaletteArray(uint8 *pal) { if (!pal) palpoint[0] = palette; else { int x; palpoint[0] = palettec; for (x = 0; x < 64; x++) { palpoint[0][x].r = *((uint8*)pal + x + x + x); palpoint[0][x].g = *((uint8*)pal + x + x + x + 1); palpoint[0][x].b = *((uint8*)pal + x + x + x + 2); } } FCEU_ResetPalette(); } void FCEUI_SetNTSCTH(int n, int tint, int hue) { ntsctint = tint; ntschue = hue; ntsccol = n; FCEU_ResetPalette(); } static uint8 lastd = 0; void SetNESDeemph(uint8 d, int force) { static uint16 rtmul[7] = { 32768 * 1.239, 32768 * .794, 32768 * 1.019, 32768 * .905, 32768 * 1.023, 32768 * .741, 32768 * .75 }; static uint16 gtmul[7] = { 32768 * .915, 32768 * 1.086, 32768 * .98, 32768 * 1.026, 32768 * .908, 32768 * .987, 32768 * .75 }; static uint16 btmul[7] = { 32768 * .743, 32768 * .882, 32768 * .653, 32768 * 1.277, 32768 * .979, 32768 * .101, 32768 * .75 }; uint32 r, g, b; int x; /* If it's not forced(only forced when the palette changes), don't waste cpu time if the same deemphasis bits are set as the last call. */ if (!force) { if (d == lastd) return; } else { /* Only set this when palette has changed. */ r = rtmul[6]; g = rtmul[6]; b = rtmul[6]; for (x = 0; x < 0x40; x++) { uint32 m, n, o; m = palo[x].r; n = palo[x].g; o = palo[x].b; m = (m * r) >> 15; n = (n * g) >> 15; o = (o * b) >> 15; if (m > 0xff) m = 0xff; if (n > 0xff) n = 0xff; if (o > 0xff) o = 0xff; FCEUD_SetPalette(x | 0xC0, m, n, o); } } if (!d) return; /* No deemphasis, so return. */ r = rtmul[d - 1]; g = gtmul[d - 1]; b = btmul[d - 1]; for (x = 0; x < 0x40; x++) { uint32 m, n, o; m = palo[x].r; n = palo[x].g; o = palo[x].b; m = (m * r) >> 15; n = (n * g) >> 15; o = (o * b) >> 15; if (m > 0xff) m = 0xff; if (n > 0xff) n = 0xff; if (o > 0xff) o = 0xff; FCEUD_SetPalette(x | 0x40, m, n, o); } lastd = d; } /* Converted from Kevin Horton's qbasic palette generator. */ static void CalculatePalette(void) { int x, z; int r, g, b; double s, luma, theta; static uint8 cols[16] = { 0, 24, 21, 18, 15, 12, 9, 6, 3, 0, 33, 30, 27, 0, 0, 0 }; static uint8 br1[4] = { 6, 9, 12, 12 }; static double br2[4] = { .29, .45, .73, .9 }; static double br3[4] = { 0, .24, .47, .77 }; for (x = 0; x <= 3; x++) for (z = 0; z < 16; z++) { s = (double)ntsctint / 128; luma = br2[x]; if (z == 0) { s = 0; luma = ((double)br1[x]) / 12; } if (z >= 13) { s = luma = 0; if (z == 13) luma = br3[x]; } theta = (double)M_PI * (double)(((double)cols[z] * 10 + (((double)ntschue / 2) + 300)) / (double)180); r = (int)((luma + s * sin(theta)) * 256); g = (int)((luma - (double)27 / 53 * s * sin(theta) + (double)10 / 53 * s * cos(theta)) * 256); b = (int)((luma - s * cos(theta)) * 256); if (r > 255) r = 255; if (g > 255) g = 255; if (b > 255) b = 255; if (r < 0) r = 0; if (g < 0) g = 0; if (b < 0) b = 0; paletten[(x << 4) + z].r = r; paletten[(x << 4) + z].g = g; paletten[(x << 4) + z].b = b; } WritePalette(); } static int ipalette = 0; void FCEU_LoadGamePalette(void) { uint8 ptmp[192]; FILE *fp; char *fn; ipalette = 0; fn = FCEU_MakeFName(FCEUMKF_PALETTE, 0, 0); if ((fp = FCEUD_UTF8fopen(fn, "rb"))) { int x; fread(ptmp, 1, 192, fp); fclose(fp); for (x = 0; x < 64; x++) { palettei[x].r = ptmp[x + x + x]; palettei[x].g = ptmp[x + x + x + 1]; palettei[x].b = ptmp[x + x + x + 2]; } ipalette = 1; } free(fn); } void FCEU_ResetPalette(void) { if (GameInfo) { ChoosePalette(); WritePalette(); } } static void ChoosePalette(void) { if (GameInfo->type == GIT_NSF) palo = 0; else if (ipalette) palo = palettei; else if (ntsccol && !PAL && GameInfo->type != GIT_VSUNI) { palo = paletten; CalculatePalette(); } else palo = palpoint[pale]; } void WritePalette(void) { int x; for (x = 0; x < 7; x++) FCEUD_SetPalette(x, unvpalette[x].r, unvpalette[x].g, unvpalette[x].b); if (GameInfo->type == GIT_NSF) { } else { for (x = 0; x < 64; x++) FCEUD_SetPalette(128 + x, palo[x].r, palo[x].g, palo[x].b); SetNESDeemph(lastd, 1); } } void FCEUI_GetNTSCTH(int *tint, int *hue) { *tint = ntsctint; *hue = ntschue; } static int controlselect = 0; static int controllength = 0; void FCEUI_NTSCDEC(void) { if (ntsccol && GameInfo->type != GIT_VSUNI && !PAL && GameInfo->type != GIT_NSF) { int which; if (controlselect) { if (controllength) { which = controlselect == 1 ? ntschue : ntsctint; which--; if (which < 0) which = 0; if (controlselect == 1) ntschue = which; else ntsctint = which; CalculatePalette(); } controllength = 360; } } } void FCEUI_NTSCINC(void) { if (ntsccol && GameInfo->type != GIT_VSUNI && !PAL && GameInfo->type != GIT_NSF) if (controlselect) { if (controllength) { switch (controlselect) { case 1: ntschue++; if (ntschue > 128) ntschue = 128; CalculatePalette(); break; case 2: ntsctint++; if (ntsctint > 128) ntsctint = 128; CalculatePalette(); break; } } controllength = 360; } } void FCEUI_NTSCSELHUE(void) { if (ntsccol && GameInfo->type != GIT_VSUNI && !PAL && GameInfo->type != GIT_NSF) { controlselect = 1; controllength = 360; } } void FCEUI_NTSCSELTINT(void) { if (ntsccol && GameInfo->type != GIT_VSUNI && !PAL && GameInfo->type != GIT_NSF) { controlselect = 2; controllength = 360; } } void FCEU_DrawNTSCControlBars(uint8 *XBuf) { uint8 *XBaf; int which = 0; int x, x2; if (!controllength) return; controllength--; if (!XBuf) return; if (controlselect == 1) { DrawTextTrans(XBuf + 128 - 12 + 180 * 256, 256, (uint8*)"Hue", 0x85); which = ntschue << 1; } else if (controlselect == 2) { DrawTextTrans(XBuf + 128 - 16 + 180 * 256, 256, (uint8*)"Tint", 0x85); which = ntsctint << 1; } XBaf = XBuf + 200 * 256; for (x = 0; x < which; x += 2) { for (x2 = 6; x2 >= -6; x2--) { XBaf[x - 256 * x2] = 0x85; } } for (; x < 256; x += 2) { for (x2 = 2; x2 >= -2; x2--) XBaf[x - 256 * x2] = 0x85; } }