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; }