From 7f9ee69a2bc769042433dba3970137b7be9afa03 Mon Sep 17 00:00:00 2001
From: archshift <admin@archshift.com>
Date: Thu, 26 Feb 2015 19:11:39 -0800
Subject: [PATCH] Added RGBA5551 compatibility in the rasterizer

This allows Virtual Console games to display properly.
---
 src/core/hw/gpu.h             |  2 +-
 src/video_core/color.h        | 19 +++++++++++++++++++
 src/video_core/pica.h         |  1 +
 src/video_core/rasterizer.cpp | 23 +++++++++++++++++++++--
 4 files changed, 42 insertions(+), 3 deletions(-)

diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h
index ab1dcf91d..737b1e968 100644
--- a/src/core/hw/gpu.h
+++ b/src/core/hw/gpu.h
@@ -46,7 +46,7 @@ struct Regs {
                   "Structure size and register block length don't match")
 #endif
 
-    // All of those formats are described in reverse byte order, since the 3DS is little-endian.
+    // Components are laid out in reverse byte order, most significant bits first.
     enum class PixelFormat : u32 {
         RGBA8  = 0,
         RGB8   = 1,
diff --git a/src/video_core/color.h b/src/video_core/color.h
index e86ac1265..f095d8ac5 100644
--- a/src/video_core/color.h
+++ b/src/video_core/color.h
@@ -28,5 +28,24 @@ static inline u8 Convert6To8(u8 value) {
     return (value << 2) | (value >> 4);
 }
 
+/// Convert a 8-bit color component to 1 bit
+static inline u8 Convert8To1(u8 value) {
+    return value >> 7;
+}
+
+/// Convert a 8-bit color component to 4 bit
+static inline u8 Convert8To4(u8 value) {
+    return value >> 4;
+}
+
+/// Convert a 8-bit color component to 5 bit
+static inline u8 Convert8To5(u8 value) {
+    return value >> 3;
+}
+
+/// Convert a 8-bit color component to 6 bit
+static inline u8 Convert8To6(u8 value) {
+    return value >> 2;
+}
 
 } // namespace
diff --git a/src/video_core/pica.h b/src/video_core/pica.h
index d03b811d3..96d0c72fe 100644
--- a/src/video_core/pica.h
+++ b/src/video_core/pica.h
@@ -409,6 +409,7 @@ struct Regs {
     } output_merger;
 
     struct {
+        // Components are laid out in reverse byte order, most significant bits first.
         enum ColorFormat : u32 {
             RGBA8    = 0,
             RGB8     = 1,
diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp
index a7bb0612f..8c370781a 100644
--- a/src/video_core/rasterizer.cpp
+++ b/src/video_core/rasterizer.cpp
@@ -51,6 +51,16 @@ static void DrawPixel(int x, int y, const Math::Vec4<u8>& color) {
         break;
     }
 
+    case registers.framebuffer.RGBA5551:
+    {
+        u16_le* pixel = (u16_le*)(color_buffer + dst_offset);
+        *pixel = (Color::Convert8To5(color.r()) << 11) |
+                 (Color::Convert8To5(color.g()) << 6)  |
+                 (Color::Convert8To5(color.b()) << 1)  |
+                 Color::Convert8To1(color.a());
+        break;
+    }
+
     default:
         LOG_CRITICAL(Render_Software, "Unknown framebuffer color format %x", registers.framebuffer.color_format.Value());
         UNIMPLEMENTED();
@@ -66,11 +76,11 @@ static const Math::Vec4<u8> GetPixel(int x, int y) {
     const u32 coarse_y = y & ~7;
     u32 bytes_per_pixel = GPU::Regs::BytesPerPixel(GPU::Regs::PixelFormat(registers.framebuffer.color_format.Value()));
     u32 src_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * registers.framebuffer.width * bytes_per_pixel;
+    Math::Vec4<u8> ret;
 
     switch (registers.framebuffer.color_format) {
     case registers.framebuffer.RGBA8:
     {
-        Math::Vec4<u8> ret;
         u8* pixel = color_buffer + src_offset;
         ret.r() = pixel[3];
         ret.g() = pixel[2];
@@ -81,7 +91,6 @@ static const Math::Vec4<u8> GetPixel(int x, int y) {
 
     case registers.framebuffer.RGBA4:
     {
-        Math::Vec4<u8> ret;
         u8* pixel = color_buffer + src_offset;
         ret.r() = Color::Convert4To8(pixel[1] >> 4);
         ret.g() = Color::Convert4To8(pixel[1] & 0x0F);
@@ -90,6 +99,16 @@ static const Math::Vec4<u8> GetPixel(int x, int y) {
         return ret;
     }
 
+    case registers.framebuffer.RGBA5551:
+    {
+        u16_le pixel = *(u16_le*)(color_buffer + src_offset);
+        ret.r() = Color::Convert5To8((pixel >> 11) & 0x1F);
+        ret.g() = Color::Convert5To8((pixel >>  6) & 0x1F);
+        ret.b() = Color::Convert5To8((pixel >>  1) & 0x1F);
+        ret.a() = Color::Convert1To8(pixel & 0x1);
+        return ret;
+    }
+
     default:
         LOG_CRITICAL(Render_Software, "Unknown framebuffer color format %x", registers.framebuffer.color_format.Value());
         UNIMPLEMENTED();