memoserv/subprojects/memcard/src/block.cpp

256 lines
6.9 KiB
C++

/* Copyright 2020, Michele Santullo
* This file is part of memoserv.
*
* Memoserv 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 3 of the License, or
* (at your option) any later version.
*
* Memoserv 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 Memoserv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "memcard/block.hpp"
#include "memcard/memorycard.hpp"
#include "shiftjis_to_utf8.hpp"
#include <cassert>
#include <iomanip>
#include <iostream>
#include <algorithm>
#include <cstring>
#define FLOAT_PALETTE_CONV
namespace mc::psx {
namespace {
uint8_t g_dummy_data[Frame::size()] = {0};
#if defined(FLOAT_PALETTE_CONV)
uint8_t _5bit_to_8bit(int n) {
return static_cast<uint8_t>(static_cast<float>(n) / 0x1F * 255.0f + 0.5f);
}
#endif
std::vector<uint8_t> extract_palette (const ConstFrame& frame) {
std::vector<uint8_t> retval;
retval.reserve(16 * 4);
auto b = frame.cbegin();
const auto e = frame.cend();
{
if (e - b < 96)
throw std::runtime_error("Not enough data to advance and extract an icon palette");
b += 96;
}
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 <bool Const>
BasicBlock<Const>::BasicBlock (data_type* blk, std::size_t index) :
BasicBlock(blk, BasicFrame<Const>(g_dummy_data, 0), index)
{
}
template <bool Const>
BasicBlock<Const>::BasicBlock (data_type* blk, BasicFrame<Const> toc_entry, std::size_t index) :
m_toc_entry(toc_entry),
m_index(index),
m_begin(blk)
{
assert(m_begin);
}
template <bool Const>
BasicBlock<Const>::~BasicBlock() = default;
template <bool Const>
auto BasicBlock<Const>::begin() -> iterator {
return iterator{this, 0};
}
template <bool Const>
auto BasicBlock<Const>::end() -> iterator {
return iterator{this, size()};
}
template <bool Const>
auto BasicBlock<Const>::cbegin() const -> const_iterator {
return const_iterator{this, size()};
}
template <bool Const>
auto BasicBlock<Const>::begin() const -> const_iterator {
return const_iterator{this, size()};
}
template <bool Const>
auto BasicBlock<Const>::cend() const -> const_iterator {
return const_iterator{this, size()};
}
template <bool Const>
auto BasicBlock<Const>::end() const -> const_iterator {
return const_iterator{this, size()};
}
template <bool Const>
BasicFrame<Const> BasicBlock<Const>::frame(unsigned int idx) {
assert(idx < size());
return {this->data() + Frame::Size * idx, idx};
}
template <bool Const>
ConstFrame BasicBlock<Const>::frame(unsigned int idx) const {
assert(idx < size());
return {this->data() + Frame::Size * idx, idx};
}
template <bool Const>
std::vector<uint8_t> BasicBlock<Const>::icon_palette() const {
return extract_palette(frame(0));
}
template <bool Const>
bool BasicBlock<Const>::has_magic() const {
ContentInfo cinfo(frame(0));
return cinfo.magic == cinfo.SCMagic;
}
template <bool Const>
IconDisplayFlag BasicBlock<Const>::icon_display_flag() const {
const uint8_t val = frame(0)[2];
return static_cast<IconDisplayFlag>(val);
}
template <bool Const>
int BasicBlock<Const>::block_count() const {
return frame(0)[3];
}
template <bool Const>
std::string BasicBlock<Const>::title() const {
char mem[64 + 1];
std::strncpy(mem, reinterpret_cast<const char*>(frame(0).data() + 4), sizeof(mem) / sizeof(mem[0]));
std::string_view temp(mem, std::strlen(mem));
auto name = full_to_halfwidth_ascii(shiftjis_to_utf8(temp));
//trim spaces at the front
{
const auto pos = name.find_first_not_of(' ');
const auto non_space_it = (pos == name.npos ? name.end() : name.begin() + pos);
name.erase(name.begin(), non_space_it);
}
//trim spaces at the end
{
auto non_space_it = std::find_if(name.rbegin(), name.rend(), [](char c){return c != ' ';});
name.resize(name.size() - (non_space_it - name.rbegin()));
}
//remove repeated spaces
{
std::size_t start = 0;
while ((start = name.find(' ', start)) != name.npos) {
const std::size_t end = name.find_first_not_of(' ', start);
name.erase(start + 1, end - start - 1);
++start;
};
}
return name;
}
template <bool Const>
std::size_t BasicBlock<Const>::available_blocks() const {
return m_toc_entry.data()[0];
}
template <bool Const>
uint32_t BasicBlock<Const>::use_byte() const {
return read_as<uint32_t>(m_toc_entry, 0x04);
}
template <bool Const>
uint16_t BasicBlock<Const>::link_order() const {
return read_as<uint16_t>(m_toc_entry, 0x08);
}
template <bool Const>
CountryCode BasicBlock<Const>::country_code() const {
const auto cc = read_as<uint16_t>(m_toc_entry, 0x0A);
return to_country_code(cc);
}
template <bool Const>
std::string BasicBlock<Const>::product_code() const {
std::string retval{reinterpret_cast<const char*>(m_toc_entry.data() + 0x0C), 10};
retval[4] = '-';
return retval;
}
template <bool Const>
bool BasicBlock<Const>::has_pocket_station_content() const {
return (m_toc_entry.data()[0x0C + 4] == 'P');
}
template <bool Const>
std::string_view BasicBlock<Const>::identifier() const {
return {reinterpret_cast<const char*>(m_toc_entry.data() + 0x16), 8};
}
template <bool Const>
uint8_t BasicBlock<Const>::toc_checksum() const {
return m_toc_entry.data()[0xFF - 0x80];
}
template <bool Const>
uint8_t BasicBlock<Const>::calculate_toc_checksum() const {
uint8_t retval = 0;
for (std::size_t z = 0; z < m_toc_entry.size() - 1; ++z) {
retval ^= m_toc_entry[z];
}
return retval;
}
template class BasicBlock<true>;
template class BasicBlock<false>;
} //namespace mc::psx