256 lines
6.9 KiB
C++
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
|