diff --git a/BizHawk.Emulation/BizHawk.Emulation.csproj b/BizHawk.Emulation/BizHawk.Emulation.csproj index 0c716e1f10..b02eb1a1b9 100644 --- a/BizHawk.Emulation/BizHawk.Emulation.csproj +++ b/BizHawk.Emulation/BizHawk.Emulation.csproj @@ -212,6 +212,7 @@ + diff --git a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/GBColors.cs b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/GBColors.cs new file mode 100644 index 0000000000..be05f3e920 --- /dev/null +++ b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/GBColors.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BizHawk.Emulation.Consoles.GB +{ + public class GBColors + { + /* + * The GBC uses a RGB555 color space, but it most definately does not resemble sRGB at all. + * To make matters worse, because of the reflective screen, the visible colors depend greatly + * on the viewing environment. + * + * All of these algorithms convert from GBC RGB555 to sRGB RGB888 + */ + public struct Triple + { + public int r; + public int g; + public int b; + public Triple(int r, int g, int b) + { + this.r = r; + this.g = g; + this.b = b; + } + + public Triple Bit5to8Bad() + { + Triple ret; + ret.r = r * 8; + ret.g = g * 8; + ret.b = b * 8; + return ret; + } + + public Triple Bit5to8Good() + { + Triple ret; + ret.r = (r * 255 + 15) / 31; + ret.g = (g * 255 + 15) / 31; + ret.b = (b * 255 + 15) / 31; + return ret; + } + + public int ToARGB32() + { + return b | g << 8 | r << 16 | 255 << 24; + } + } + + // the version of gambatte in bizhawk + public static Triple GambatteColor(Triple c) + { + Triple ret; + ret.r = (c.r * 13 + c.g * 2 + c.b) >> 1; + ret.g = (c.g * 3 + c.b) << 1; + ret.b = (c.r * 3 + c.g * 2 + c.b * 11) >> 1; + return ret; + } + + // vba's default mode + public static Triple VividColor(Triple c) + { + return c.Bit5to8Bad(); + } + + // "gameboy colors" mode on older versions of VBA + static int gbGetValue(int min, int max, int v) + { + return (int)(min + (float)(max - min) * (2.0 * (v / 31.0) - (v / 31.0) * (v / 31.0))); + } + public static Triple OldVBAColor(Triple c) + { + Triple ret; + ret.r = gbGetValue(gbGetValue(4, 14, c.g), + gbGetValue(24, 29, c.g), c.r) - 4; + ret.g = gbGetValue(gbGetValue(4 + gbGetValue(0, 5, c.r), + 14 + gbGetValue(0, 3, c.r), c.b), + gbGetValue(24 + gbGetValue(0, 3, c.r), + 29 + gbGetValue(0, 1, c.r), c.b), c.g) - 4; + ret.b = gbGetValue(gbGetValue(4 + gbGetValue(0, 5, c.r), + 14 + gbGetValue(0, 3, c.r), c.g), + gbGetValue(24 + gbGetValue(0, 3, c.r), + 29 + gbGetValue(0, 1, c.r), c.g), c.b) - 4; + return ret.Bit5to8Bad(); + } + + // "gameboy colors" mode on newer versions of VBA + public static Triple NewVBAColor(Triple c) + { + Triple ret; + ret.r = (c.r * 13 + c.g * 2 + c.b * 1 + 8) >> 4; + ret.g = (c.r * 1 + c.g * 12 + c.b * 3 + 8) >> 4; + ret.b = (c.r * 2 + c.g * 2 + c.b * 12 + 8) >> 4; + return ret.Bit5to8Bad(); + } + } +} diff --git a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs index 82f6a940e1..6856a50ffb 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/Gambatte.cs @@ -50,7 +50,8 @@ namespace BizHawk.Emulation.Consoles.GB // set real default colors (before anyone mucks with them at all) ChangeDMGColors(new int[] { 10798341, 8956165, 1922333, 337157, 10798341, 8956165, 1922333, 337157, 10798341, 8956165, 1922333, 337157 }); - + SetCGBColors(); + InitSound(); Frame = 0; @@ -706,6 +707,21 @@ namespace BizHawk.Emulation.Consoles.GB LibGambatte.gambatte_setdmgpalettecolor(GambatteState, (LibGambatte.PalType)(i / 4), (uint)i % 4, (uint)colors[i]); } + void SetCGBColors() + { + int[] lut = new int[32768]; + int i = 0; + for (int b = 0; b < 32; b++) + for (int g = 0; g < 32; g++) + for (int r = 0; r < 32; r++) + lut[i++] = GBColors.GambatteColor(new GBColors.Triple(r, g, b)).ToARGB32(); + unsafe + { + fixed (int* p = &lut[0]) + LibGambatte.gambatte_setcgbpalette(GambatteState, (IntPtr)p); + } + } + #endregion #region ISoundProvider diff --git a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs index 3877502c0d..73b17e0085 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/Gameboy/LibGambatte.cs @@ -98,6 +98,14 @@ namespace BizHawk.Emulation.Consoles.GB [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] public static extern void gambatte_setdmgpalettecolor(IntPtr core, PalType palnum, uint colornum, uint rgb32); + /// + /// set cgb palette lookup + /// + /// opaque state pointer + /// uint32[32768], input color (r,g,b) is at lut[r | g << 5 | b << 10] + [DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)] + public static extern void gambatte_setcgbpalette(IntPtr core, IntPtr lut); + /// /// combination of button flags used by the input callback /// diff --git a/BizHawk.MultiClient/output/dll/libgambatte.dll b/BizHawk.MultiClient/output/dll/libgambatte.dll index 43f431c686..b1c0538605 100644 Binary files a/BizHawk.MultiClient/output/dll/libgambatte.dll and b/BizHawk.MultiClient/output/dll/libgambatte.dll differ diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h index be67840072..3d75d41d6c 100644 --- a/libgambatte/include/gambatte.h +++ b/libgambatte/include/gambatte.h @@ -80,6 +80,8 @@ public: */ void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned rgb32); + void setCgbPalette(unsigned *lut); + /** Sets the callback used for getting input state. */ void setInputGetter(InputGetter *getInput); diff --git a/libgambatte/src/cinterface.cpp b/libgambatte/src/cinterface.cpp index 0858a6304d..1a473c06f7 100644 --- a/libgambatte/src/cinterface.cpp +++ b/libgambatte/src/cinterface.cpp @@ -45,6 +45,12 @@ __declspec(dllexport) void gambatte_setdmgpalettecolor(void *core, unsigned paln g->setDmgPaletteColor(palnum, colornum, rgb32); } +__declspec(dllexport) void gambatte_setcgbpalette(void *core, unsigned *lut) +{ + GB *g = (GB *) core; + g->setCgbPalette(lut); +} + class CInputGetter: public InputGetter { public: diff --git a/libgambatte/src/cinterface.h b/libgambatte/src/cinterface.h index 810347fc53..2c3a24d5a6 100644 --- a/libgambatte/src/cinterface.h +++ b/libgambatte/src/cinterface.h @@ -16,6 +16,8 @@ extern "C" __declspec(dllexport) void gambatte_setdmgpalettecolor(void *core, unsigned palnum, unsigned colornum, unsigned rgb32); + __declspec(dllexport) void gambatte_setcgbpalette(void *core, unsigned *lut); + __declspec(dllexport) void gambatte_setinputgetter(void *core, unsigned (*getinput)(void)); __declspec(dllexport) void gambatte_setreadcallback(void *core, void (*callback)(unsigned)); diff --git a/libgambatte/src/cpu.h b/libgambatte/src/cpu.h index 13711eef00..2bba796af1 100644 --- a/libgambatte/src/cpu.h +++ b/libgambatte/src/cpu.h @@ -110,6 +110,10 @@ public: void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned rgb32) { memory.setDmgPaletteColor(palNum, colorNum, rgb32); } + + void setCgbPalette(unsigned *lut) { + memory.setCgbPalette(lut); + } void setGameGenie(const std::string &codes) { memory.setGameGenie(codes); } void setGameShark(const std::string &codes) { memory.setGameShark(codes); } diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp index 5c295fbd98..f6dad17c48 100644 --- a/libgambatte/src/gambatte.cpp +++ b/libgambatte/src/gambatte.cpp @@ -182,6 +182,10 @@ void GB::setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned rgb32) p_->cpu.setDmgPaletteColor(palNum, colorNum, rgb32); } +void GB::setCgbPalette(unsigned *lut) { + p_->cpu.setCgbPalette(lut); +} + bool GB::loadState(std::istream &file) { if (p_->cpu.loaded()) { // p_->cpu.saveSavedata(); diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index 4aaa934cf7..e32d0de4bb 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -1031,6 +1031,10 @@ void Memory::setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned lon display.setDmgPaletteColor(palNum, colorNum, rgb32); } +void Memory::setCgbPalette(unsigned *lut) { + display.setCgbPalette(lut); +} + bool Memory::getMemoryArea(int which, unsigned char **data, int *length) { if (!data || !length) return false; diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h index 9b01cbf8b9..43a5cba486 100644 --- a/libgambatte/src/memory.h +++ b/libgambatte/src/memory.h @@ -173,6 +173,7 @@ public: } void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32); + void setCgbPalette(unsigned *lut); void setGameGenie(const std::string &codes) { cart.setGameGenie(codes); } void setGameShark(const std::string &codes) { interrupter.setGameShark(codes); } }; diff --git a/libgambatte/src/video.cpp b/libgambatte/src/video.cpp index d581304c57..f5fe80d8ad 100644 --- a/libgambatte/src/video.cpp +++ b/libgambatte/src/video.cpp @@ -30,12 +30,20 @@ void LCD::setDmgPalette(unsigned long *const palette, const unsigned long *const palette[3] = dmgColors[data >> 6 & 3]; } -static unsigned long gbcToRgb32(const unsigned bgr15) { +void LCD::setCgbPalette(unsigned *lut) { + for (int i = 0; i < 32768; i++) + cgbColorsRgb32[i] = lut[i]; +} + +unsigned long LCD::gbcToRgb32(const unsigned bgr15) { + /* const unsigned long r = bgr15 & 0x1F; const unsigned long g = bgr15 >> 5 & 0x1F; const unsigned long b = bgr15 >> 10 & 0x1F; return ((r * 13 + g * 2 + b) >> 1) << 16 | (g * 3 + b) << 9 | (r * 3 + g * 2 + b * 11) >> 1; + */ + return cgbColorsRgb32[bgr15 & 0x7FFF]; } /*static unsigned long gbcToRgb16(const unsigned bgr15) { @@ -357,7 +365,7 @@ bool LCD::cgbpAccessible(const unsigned long cycleCounter) { || cycleCounter >= m0TimeOfCurrentLine(cycleCounter) + 3 - isDoubleSpeed(); } -static void doCgbColorChange(unsigned char *const pdata, +void LCD::doCgbColorChange(unsigned char *const pdata, unsigned long *const palette, unsigned index, const unsigned data) { pdata[index] = data; index >>= 1; diff --git a/libgambatte/src/video.h b/libgambatte/src/video.h index b0cfca8560..897c172281 100644 --- a/libgambatte/src/video.h +++ b/libgambatte/src/video.h @@ -120,6 +120,7 @@ class LCD { PPU ppu; unsigned long dmgColorsRgb32[3 * 4]; + unsigned long cgbColorsRgb32[32768]; unsigned char bgpData[8 * 8]; unsigned char objpData[8 * 8]; @@ -137,6 +138,9 @@ class LCD { static void setDmgPalette(unsigned long *palette, const unsigned long *dmgColors, unsigned data); void setDmgPaletteColor(unsigned index, unsigned long rgb32); + unsigned long gbcToRgb32(const unsigned bgr15); + void doCgbColorChange(unsigned char *const pdata, unsigned long *const palette, unsigned index, const unsigned data); + void refreshPalettes(); void setDBuffer(); @@ -160,6 +164,7 @@ public: void saveState(SaveState &state) const; void loadState(const SaveState &state, const unsigned char *oamram); void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32); + void setCgbPalette(unsigned *lut); void setVideoBuffer(uint_least32_t *videoBuf, int pitch); void setOsdElement(std::auto_ptr osdElement) { this->osdElement = osdElement; }