diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index a11b114..85c9752 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -3,6 +3,8 @@ project(gui CXX) add_executable(${PROJECT_NAME} main.cpp memorycard.cpp + block.cpp + icon_fetch.cpp ) target_link_libraries(${PROJECT_NAME} diff --git a/src/gui/block.cpp b/src/gui/block.cpp new file mode 100644 index 0000000..2e9a9af --- /dev/null +++ b/src/gui/block.cpp @@ -0,0 +1,79 @@ +#include "block.hpp" +#include "memorycard.hpp" +#include +#include +#include + +#define FLOAT_PALETTE_CONV + +namespace { +#if defined(FLOAT_PALETTE_CONV) + uint8_t _5bit_to_8bit(int n) { + return static_cast(static_cast(n) / 0x1F * 255.0f + 0.5f); + } +#endif + + std::vector extract_palette (const uint8_t* b, const uint8_t* e) { + std::vector retval; + retval.reserve(16 * 4); + + { + int advance = 96; + while (b != e and advance) { + --advance; + ++b; + } + if (advance) + throw std::runtime_error("Not enough data to advance and extract an icon palette"); + } + + int entries_left = 16; + while (b != e and entries_left) { + --entries_left; + const auto lo = *b; + ++b; + if (b == e) + throw std::runtime_error("Not enough data to extract an icon palette entry of 2 bytes"); + const auto hi = *b; + ++b; + +#if defined(FLOAT_PALETTE_CONV) + const uint16_t col = (hi << 8) | lo; + const uint8_t red = _5bit_to_8bit(col & 0x1F); + const uint8_t green = _5bit_to_8bit((col & 0x3E0) >> 5); + const uint8_t blue = _5bit_to_8bit((col & 0x7C00) >> 10); + const uint8_t black_flag = (col & 0x8000) >> 15; +#else + const uint8_t red = (lo & 0x1F) << 3; + const uint8_t green = ((hi & 0x3) << 6) | ((lo & 0xE0) >> 2); + const uint8_t blue = ((hi & 0x7C) << 1); + const uint8_t black_flag = (hi & 0x80) >> 7; +#endif + const uint8_t alpha = (blue | green | red | black_flag ? 0xFF : 0x00); + + std::cout << "RGBA: " << std::setw(3) << (int)red << '\t' << (int)green << '\t' << (int)blue << '\t' << (int)alpha << '\t' << (int)black_flag << std::setw(0) << '\n'; + retval.push_back(blue); + retval.push_back(green); + retval.push_back(red); + retval.push_back(alpha); + } + if (entries_left) + throw std::runtime_error("Not enough entries in the icon palette"); + + return retval; + } +} //unnamed namespace + +template +BasicBlock::BasicBlock (data_type* beg) : + m_icon_palette(extract_palette(beg, beg + size())), + m_begin(beg) +{ + assert(m_begin); +} + +template +BasicBlock::~BasicBlock() = default; + +template class BasicBlock; +template class BasicBlock; diff --git a/src/gui/block.hpp b/src/gui/block.hpp new file mode 100644 index 0000000..b5c50bd --- /dev/null +++ b/src/gui/block.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include + +template +class BasicBlock { +public: + using data_type = typename std::conditional::type; + + explicit BasicBlock (data_type* beg); + ~BasicBlock(); + + data_type* begin() { return m_begin; } + data_type* end() { return m_begin + size(); } + const data_type* cbegin() const { return m_begin; } + const data_type* cend() const { return m_begin + size(); } + const data_type* begin() const { return m_begin; } + const data_type* end() const { return m_begin + size(); } + + static constexpr std::size_t size() { return 8192; } + + const std::vector& palette() const { return m_icon_palette; } + +private: + std::vector m_icon_palette; + data_type* m_begin; +}; + +using Block = BasicBlock; +using ConstBlock = BasicBlock; diff --git a/src/gui/icon_fetch.cpp b/src/gui/icon_fetch.cpp new file mode 100644 index 0000000..904aae6 --- /dev/null +++ b/src/gui/icon_fetch.cpp @@ -0,0 +1,84 @@ +#include "icon_fetch.hpp" +#include +#include +#include + +namespace { + namespace bmp { + const uint16_t g_signature = 'B' bitor (static_cast('M') << 8); + + struct FileHeader { + uint32_t bfSize = 0; + uint16_t bfReserved1 = 0; + uint16_t bfReserved2 = 0; + uint32_t bfOffBits = 0; + }; + + struct ImageHeader { + uint32_t biSize = 40; + int32_t biWidth = 0; + int32_t biHeight = 0; + uint16_t biPlanes = 1; + uint16_t biBitCount = 24; + uint32_t biCompression = 0; + uint32_t biSizeImage = 0; + uint32_t biXPelsPerMeter = 0; + uint32_t biYPelsPerMeter = 0; + uint32_t biClrUsed = 0; + uint32_t biClrImportant = 0; + }; + } //namespace bmp +} //unnamed namespace + +std::vector> icon_fetch (const ConstBlock& block) { + const int32_t h = 16, w = 16; + + const auto slot_begin = block.cbegin(); + const auto slot_end = block.cend(); + const std::vector& palette = block.palette(); + assert(palette.size() == 16 * 4); + const int32_t palette_padded_size = ((palette.size() * CHAR_BIT + 31) bitand ~31) / CHAR_BIT; + assert(palette.size() == palette_padded_size); + + const int32_t bpp = 4; + const int32_t scanline_size = ((w * bpp + 31) bitand ~31) / CHAR_BIT; + + const std::size_t data_offset = sizeof(bmp::g_signature) + sizeof(bmp::FileHeader) + sizeof(bmp::ImageHeader) + palette_padded_size; + const std::size_t bmp_byte_size = data_offset + scanline_size * h; + + bmp::FileHeader fhead; + bmp::ImageHeader ihead; + fhead.bfSize = static_cast(bmp_byte_size); + fhead.bfOffBits = static_cast(data_offset); + ihead.biWidth = h; + ihead.biHeight = w; + ihead.biBitCount = static_cast(bpp); + ihead.biClrUsed = static_cast(palette.size() / 4); + ihead.biClrImportant = ihead.biClrUsed; + ihead.biSizeImage = scanline_size * h; + + std::vector> retval(3); + int32_t icon_num = 0; + for (auto& frame : retval) { + frame.reserve(bmp_byte_size); + + std::copy(reinterpret_cast(&bmp::g_signature), reinterpret_cast(&bmp::g_signature + 1), std::back_inserter(frame)); + std::copy(reinterpret_cast(&fhead), reinterpret_cast(&fhead + 1), std::back_inserter(frame)); + std::copy(reinterpret_cast(&ihead), reinterpret_cast(&ihead + 1), std::back_inserter(frame)); + std::copy(palette.begin(), palette.end(), std::back_inserter(frame)); + assert(palette_padded_size >= palette.size()); + std::fill_n(std::back_inserter(frame), palette_padded_size - palette.size(), 0); + + assert(frame.size() == data_offset); + frame.resize(data_offset + scanline_size * h); + for (int32_t y = 0; y < h; ++y) { + for (int32_t x = 0; x < w / 2; ++x) { + const uint8_t curr = *(slot_begin + 128 + (w / 2 * y) + 128 * icon_num + x); + frame[data_offset + (h - 1 - y) * scanline_size + x] = + static_cast(((curr & 0x0f) << 4) | ((curr & 0xf0) >> 4)); + } + //std::copy_n(slot_begin + 128 + h / 2 * y, scanline_size, frame.begin() + data_offset + (h - 1 - y) * scanline_size); + } + } + return retval; +} diff --git a/src/gui/icon_fetch.hpp b/src/gui/icon_fetch.hpp new file mode 100644 index 0000000..6ed6a09 --- /dev/null +++ b/src/gui/icon_fetch.hpp @@ -0,0 +1,6 @@ +#pragma once + +#include "block.hpp" +#include + +std::vector> icon_fetch (const ConstBlock& block); diff --git a/src/gui/main.cpp b/src/gui/main.cpp index bcea14b..3807168 100644 --- a/src/gui/main.cpp +++ b/src/gui/main.cpp @@ -2,6 +2,8 @@ #include "nana/gui/animation.hpp" #include "nana/gui/widgets/panel.hpp" #include "memorycard.hpp" +#include "block.hpp" +#include "icon_fetch.hpp" #include #include #include @@ -61,14 +63,14 @@ int main() { std::cout << "Hello world\n"; frm.show(); - MemoryCard mc(std::ifstream("/home/michele/emu/psx/WipEout 3 - Special Edition (Europe) (En,Fr,De,Es,It).srm", std::ios::binary)); + const MemoryCard mc(std::ifstream("/home/michele/emu/psx/WipEout 3 - Special Edition (Europe) (En,Fr,De,Es,It).srm", std::ios::binary)); //MemoryCard mc(std::ifstream("/run/media/michele/roms/michele_epsxe000.mcr", std::ios::binary)); //MemoryCard mc(std::ifstream("/home/michele/emu/psx/Rapid Racer (Europe) (En,Fr,De,Es,It).srm", std::ios::binary)); std::array icons { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }; for (int z = 0; z < 15; ++z) { nana::frameset fset; - for (const auto& frame : mc.bitmap_icon(z)) { + for (const auto& frame : icon_fetch(mc[z])) { nana::paint::image img; img.open(frame.data(), frame.size()); fset.push_back(img); diff --git a/src/gui/memorycard.cpp b/src/gui/memorycard.cpp index 87fbd27..cb5ffe6 100644 --- a/src/gui/memorycard.cpp +++ b/src/gui/memorycard.cpp @@ -1,94 +1,8 @@ #include "memorycard.hpp" +#include "block.hpp" #include #include #include -#include -#include - -#define FLOAT_PALETTE_CONV - -namespace { - namespace bmp { - const uint16_t g_signature = 'B' bitor (static_cast('M') << 8); - - struct FileHeader { - uint32_t bfSize = 0; - uint16_t bfReserved1 = 0; - uint16_t bfReserved2 = 0; - uint32_t bfOffBits = 0; - }; - - struct ImageHeader { - uint32_t biSize = 40; - int32_t biWidth = 0; - int32_t biHeight = 0; - uint16_t biPlanes = 1; - uint16_t biBitCount = 24; - uint32_t biCompression = 0; - uint32_t biSizeImage = 0; - uint32_t biXPelsPerMeter = 0; - uint32_t biYPelsPerMeter = 0; - uint32_t biClrUsed = 0; - uint32_t biClrImportant = 0; - }; - } //namespace bmp - -#if defined(FLOAT_PALETTE_CONV) - uint8_t _5bit_to_8bit(int n) { - return static_cast(static_cast(n) / 0x1F * 255.0f + 0.5f); - } -#endif - - std::vector extract_palette (std::vector::const_iterator b, std::vector::const_iterator e) { - std::vector retval; - retval.reserve(16 * 4); - - { - int advance = 96; - while (b != e and advance) { - --advance; - ++b; - } - if (advance) - throw std::runtime_error("Not enough data to advance and extract an icon palette"); - } - - int entries_left = 16; - while (b != e and entries_left) { - --entries_left; - const auto lo = *b; - ++b; - if (b == e) - throw std::runtime_error("Not enough data to extract an icon palette entry of 2 bytes"); - const auto hi = *b; - ++b; - -#if defined(FLOAT_PALETTE_CONV) - const uint16_t col = (hi << 8) | lo; - const uint8_t red = _5bit_to_8bit(col & 0x1F); - const uint8_t green = _5bit_to_8bit((col & 0x3E0) >> 5); - const uint8_t blue = _5bit_to_8bit((col & 0x7C00) >> 10); - const uint8_t black_flag = (col & 0x8000) >> 15; -#else - const uint8_t red = (lo & 0x1F) << 3; - const uint8_t green = ((hi & 0x3) << 6) | ((lo & 0xE0) >> 2); - const uint8_t blue = ((hi & 0x7C) << 1); - const uint8_t black_flag = (hi & 0x80) >> 7; -#endif - const uint8_t alpha = (blue | green | red | black_flag ? 0xFF : 0x00); - - std::cout << "RGBA: " << std::setw(3) << (int)red << '\t' << (int)green << '\t' << (int)blue << '\t' << (int)alpha << '\t' << (int)black_flag << std::setw(0) << '\n'; - retval.push_back(blue); - retval.push_back(green); - retval.push_back(red); - retval.push_back(alpha); - } - if (entries_left) - throw std::runtime_error("Not enough entries in the icon palette"); - - return retval; - } -} //unnamed namespace MemoryCard::MemoryCard() : m_data(8192 * 16) @@ -110,56 +24,12 @@ MemoryCard::MemoryCard (std::istream&& raw_mc) : MemoryCard::~MemoryCard() = default; -std::array, 3> MemoryCard::bitmap_icon (int slot) const { - const int32_t h = 16, w = 16; - - assert(slot < 15); - const auto slot_begin = m_data.begin() + (slot + 1) * 8192; - const auto slot_end = m_data.begin() + (slot + 1) * 8192 + 8192; - auto palette = extract_palette(slot_begin, slot_end); - assert(palette.size() == 16 * 4); - const int32_t palette_padded_size = ((palette.size() * CHAR_BIT + 31) bitand ~31) / CHAR_BIT; - assert(palette.size() == palette_padded_size); - - const int32_t bpp = 4; - const int32_t scanline_size = ((w * bpp + 31) bitand ~31) / CHAR_BIT; - - const std::size_t data_offset = sizeof(bmp::g_signature) + sizeof(bmp::FileHeader) + sizeof(bmp::ImageHeader) + palette_padded_size; - const std::size_t bmp_byte_size = data_offset + scanline_size * h; - - bmp::FileHeader fhead; - bmp::ImageHeader ihead; - fhead.bfSize = static_cast(bmp_byte_size); - fhead.bfOffBits = static_cast(data_offset); - ihead.biWidth = h; - ihead.biHeight = w; - ihead.biBitCount = static_cast(bpp); - ihead.biClrUsed = static_cast(palette.size() / 4); - ihead.biClrImportant = ihead.biClrUsed; - ihead.biSizeImage = scanline_size * h; - - std::array, 3> retval; - int32_t icon_num = 0; - for (auto& frame : retval) { - frame.reserve(bmp_byte_size); - - std::copy(reinterpret_cast(&bmp::g_signature), reinterpret_cast(&bmp::g_signature + 1), std::back_inserter(frame)); - std::copy(reinterpret_cast(&fhead), reinterpret_cast(&fhead + 1), std::back_inserter(frame)); - std::copy(reinterpret_cast(&ihead), reinterpret_cast(&ihead + 1), std::back_inserter(frame)); - std::copy(palette.begin(), palette.end(), std::back_inserter(frame)); - assert(palette_padded_size >= palette.size()); - std::fill_n(std::back_inserter(frame), palette_padded_size - palette.size(), 0); - - assert(frame.size() == data_offset); - frame.resize(data_offset + scanline_size * h); - for (int32_t y = 0; y < h; ++y) { - for (int32_t x = 0; x < w / 2; ++x) { - const uint8_t curr = *(slot_begin + 128 + (w / 2 * y) + 128 * icon_num + x); - frame[data_offset + (h - 1 - y) * scanline_size + x] = - static_cast(((curr & 0x0f) << 4) | ((curr & 0xf0) >> 4)); - } - //std::copy_n(slot_begin + 128 + h / 2 * y, scanline_size, frame.begin() + data_offset + (h - 1 - y) * scanline_size); - } - } - return retval; +Block MemoryCard::operator[] (std::size_t index) { + assert(index < 15); + return Block(m_data.data() + (index + 1) * Block::size()); +} + +ConstBlock MemoryCard::operator[] (std::size_t index) const { + assert(index < 15); + return ConstBlock(m_data.data() + (index + 1) * Block::size()); } diff --git a/src/gui/memorycard.hpp b/src/gui/memorycard.hpp index f80c276..25eb0cc 100644 --- a/src/gui/memorycard.hpp +++ b/src/gui/memorycard.hpp @@ -1,9 +1,11 @@ #pragma once +#include "block.hpp" #include #include #include #include +#include class MemoryCard { public: @@ -11,7 +13,9 @@ public: explicit MemoryCard (std::istream&& raw_mc); ~MemoryCard(); - std::array, 3> bitmap_icon (int slot) const; + Block operator[] (std::size_t index); + ConstBlock operator[] (std::size_t index) const; + private: std::vector m_data; };