diff --git a/subprojects/memcard/include/memcard/alignment.hpp b/subprojects/memcard/include/memcard/alignment.hpp
new file mode 100644
index 0000000..08319da
--- /dev/null
+++ b/subprojects/memcard/include/memcard/alignment.hpp
@@ -0,0 +1,37 @@
+/* Copyright 2020-2025, 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 .
+ */
+
+#pragma once
+
+#include
+#include
+
+namespace dhandy {
+template
+class PackedPointer;
+} //namespace dhandy
+
+namespace mc::psx {
+std::uint8_t* to_block_aligned (std::uint8_t* ptr, std::size_t index=0, std::ptrdiff_t base_offs=0);
+const std::uint8_t* to_block_aligned (const std::uint8_t* ptr, std::size_t index=0, std::ptrdiff_t base_offs=0);
+bool is_block_aligned (const std::uint8_t* ptr);
+
+template
+bool is_block_aligned (const dhandy::PackedPointer& ptr);
+
+std::unique_ptr make_unique_for_block_aligned(std::size_t size);
+} //namespace mc::psx
diff --git a/subprojects/memcard/include/memcard/block.hpp b/subprojects/memcard/include/memcard/block.hpp
index dc46be0..dc61719 100644
--- a/subprojects/memcard/include/memcard/block.hpp
+++ b/subprojects/memcard/include/memcard/block.hpp
@@ -20,6 +20,7 @@
#include "memcard/frame.hpp"
#include "memcard/frame_iterator.hpp"
#include "memcard/country_code.hpp"
+#include "duckhandy/packed_pointer.hpp"
#include
#include
#include
@@ -44,17 +45,21 @@ public:
using data_type = typename std::conditional::type;
typedef FrameIterator iterator;
typedef FrameIterator const_iterator;
+ typedef BasicFrame FrameType;
static const constexpr std::size_t FrameCount = 64;
static const constexpr std::size_t TOCBlockIndex = 0xFF;
static const constexpr uint16_t LastLink = 0xFFFF;
- static constexpr std::size_t BlockByteSize = BasicFrame::Size * FrameCount;
+ static constexpr std::size_t BlockByteSize = FrameType::Size * FrameCount;
+ static constexpr auto DataAlignment = FrameType::DataAlignment;
+ static constexpr bool IsConst = Const;
+ static_assert(BlockByteSize % DataAlignment == 0);
BasicBlock() = default;
constexpr BasicBlock (const BasicBlock& other);
BasicBlock(BasicBlock&&) = default;
BasicBlock (data_type* blk, std::size_t index);
- BasicBlock (data_type* blk, BasicFrame toc_entry, std::size_t index);
+ BasicBlock (data_type* blk, FrameType toc_entry, std::size_t index);
template BasicBlock(const BasicBlock& other);
~BasicBlock();
@@ -68,9 +73,9 @@ public:
data_type* data() { return m_begin; }
const data_type* data() const { return m_begin; }
- BasicFrame frame(unsigned int idx);
+ FrameType frame(unsigned int idx);
ConstFrame frame(unsigned int idx) const;
- BasicFrame toc();
+ FrameType toc();
ConstFrame toc() const;
static constexpr std::size_t size() { return FrameCount; }
@@ -79,7 +84,7 @@ public:
bool has_magic() const;
IconDisplayFlag icon_display_flag() const;
std::size_t block_count() const;
- std::size_t index() const { return m_index; }
+ std::size_t index() const { return dhandy::get<0>(m_begin); }
std::string title() const;
std::size_t available_blocks() const;
uint32_t use_byte() const;
@@ -95,9 +100,8 @@ public:
BasicBlock& operator= (BasicBlock&&) = default;
private:
- BasicFrame m_toc_entry;
- std::size_t m_index;
- data_type* m_begin;
+ FrameType m_toc_entry;
+ dhandy::PackedPointer m_begin;
};
[[gnu::const]]
@@ -122,7 +126,6 @@ inline int32_t calc_icon_count (IconDisplayFlag idf) {
template
constexpr BasicBlock::BasicBlock (const BasicBlock& other) :
m_toc_entry(other.m_toc_entry),
- m_index(other.m_index),
m_begin(other.m_begin)
{
}
@@ -131,7 +134,6 @@ template
template
inline BasicBlock::BasicBlock(const BasicBlock& other) :
m_toc_entry(other.m_toc_entry),
- m_index(other.m_index),
m_begin(other.m_begin)
{
}
diff --git a/subprojects/memcard/include/memcard/frame.hpp b/subprojects/memcard/include/memcard/frame.hpp
index 5717802..7191513 100644
--- a/subprojects/memcard/include/memcard/frame.hpp
+++ b/subprojects/memcard/include/memcard/frame.hpp
@@ -17,6 +17,7 @@
#pragma once
+#include "duckhandy/packed_pointer.hpp"
#include
#include
#include
@@ -29,6 +30,11 @@ class BasicFrame {
using data_type = typename std::conditional::type;
public:
static const constexpr unsigned int Size = 128;
+ //Standard memory cards can hold up to 15 blocks so we need 4 bits in the
+ //data pointer field to use for index storing. This means alignment of the
+ //supplied block should be at least 16
+ static constexpr std::size_t DataAlignment = 16;
+ static_assert(Size % DataAlignment == 0);
BasicFrame() = default;
BasicFrame (data_type* data, std::size_t index);
@@ -60,8 +66,7 @@ public:
private:
void extract (std::size_t offset, std::size_t sz, uint8_t* out);
- std::size_t m_index;
- data_type* m_data;
+ dhandy::PackedPointer m_data;
};
template
@@ -78,7 +83,6 @@ inline T read_as (const BasicFrame& frame, std::size_t offset) {
template
template
inline BasicFrame::BasicFrame (const BasicFrame& other) {
- m_index = other.m_index;
m_data = other.m_data;
}
diff --git a/subprojects/memcard/include/memcard/memorycard.hpp b/subprojects/memcard/include/memcard/memorycard.hpp
index af64be3..14e4a60 100644
--- a/subprojects/memcard/include/memcard/memorycard.hpp
+++ b/subprojects/memcard/include/memcard/memorycard.hpp
@@ -58,7 +58,9 @@ public:
const_iterator end() const;
private:
- std::vector m_data;
+ const std::uint8_t* data_ptr() const;
+
+ std::vector m_data;
};
std::vector block_group (const MemoryCard& mc, std::size_t index);
diff --git a/subprojects/memcard/lib/duckhandy/include/duckhandy/bitfield_pack.hpp b/subprojects/memcard/lib/duckhandy/include/duckhandy/bitfield_pack.hpp
new file mode 100644
index 0000000..9af11ab
--- /dev/null
+++ b/subprojects/memcard/lib/duckhandy/include/duckhandy/bitfield_pack.hpp
@@ -0,0 +1,180 @@
+/* Copyright 2016-2025 Michele Santullo
+ * This file is part of "duckhandy".
+ *
+ * "duckhandy" 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.
+ *
+ * "duckhandy" 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 "duckhandy". If not, see .
+ */
+
+#ifndef id895441FC389F481EB824A809A0BF5004
+#define id895441FC389F481EB824A809A0BF5004
+
+#include
+#include
+#include
+
+namespace dhandy {
+ namespace implem {
+ template
+ class BitfieldData;
+
+ template
+ class BitfieldData : public BitfieldData {
+ protected:
+ BitfieldData (T init) : BitfieldData(init), m_value(init) {}
+ public:
+ T& value() { return m_value; }
+ T value() const { return m_value; }
+ private:
+ T m_value;
+ };
+
+ template
+ class BitfieldData {
+ protected:
+ BitfieldData (T init) : m_value(init) {}
+ public:
+ T& value() { return m_value; }
+ T value() const { return m_value; }
+ private:
+ T m_value;
+ };
+
+ template
+ class BitfieldData;
+
+ struct SizeAndOffs {
+ std::size_t size, offset;
+ };
+ struct IndexAndSize {
+ std::size_t index, size;
+ };
+
+ template
+ constexpr inline SizeAndOffs size_and_offset_impl (std::size_t) {
+ throw std::out_of_range("Requested pack index out of range");
+ }
+ template
+ constexpr inline SizeAndOffs size_and_offset_impl (std::size_t index) {
+ return (index ? size_and_offset_impl(index - 1) : SizeAndOffs{Sz, Sum});
+ }
+ template
+ constexpr inline SizeAndOffs size_and_offset (std::size_t index) {
+ return size_and_offset_impl<0, Sizes...>(index);
+ }
+
+ template
+ constexpr inline IndexAndSize index_and_size_impl() {
+ throw std::runtime_error("Should not be reached");
+ }
+ template
+ constexpr inline IndexAndSize index_and_size_impl() {
+ return (Idx ? index_and_size_impl<
+ (CurrMax > Sz ? CurrMax : Sz),
+ (CurrMax > Sz ? MaxIdx : Idx),
+ Idx - 1,
+ Sizes...
+ >() : IndexAndSize{(CurrMax > Sz ? MaxIdx : Idx) - 1, CurrMax > Sz ? CurrMax : Sz});
+ }
+ template
+ constexpr inline IndexAndSize index_and_size() {
+ return index_and_size_impl();
+ }
+
+ template
+ struct BitfieldFacts {
+ static const constexpr std::size_t Sum = size_and_offset(sizeof...(Sizes) - 1).size + size_and_offset(sizeof...(Sizes) - 1).offset;
+ static const constexpr std::size_t ElementCount = (Sum + CHAR_BIT * sizeof(T) - 1) / (CHAR_BIT * sizeof(T));
+ static const constexpr std::size_t LargestIndex = index_and_size().index;
+ static const constexpr std::size_t LargestSize = index_and_size().size;
+ };
+
+ template
+ [[gnu::const]]
+ constexpr inline T make_mask (std::size_t len) noexcept {
+ return (T{1} << len) - 1;
+ }
+ } //namespace implem
+
+ template
+ class BitfieldPack;
+
+ template
+ T get (const BitfieldPack&);
+ template
+ void set (BitfieldPack&, V);
+
+ template
+ class BitfieldPack : private implem::BitfieldData::ElementCount> {
+ typedef implem::BitfieldFacts Facts;
+ template friend U get (const BitfieldPack&);
+ template friend void set (BitfieldPack&, V);
+ typedef implem::BitfieldData base_type;
+ static_assert(Facts::LargestSize <= sizeof(T) * CHAR_BIT, "Bitfield size can't be larger than the data type itself");
+ public:
+ template
+ static constexpr T max_at_index = implem::make_mask(implem::size_and_offset(Idx).size);
+ static constexpr std::size_t element_count = sizeof...(Sizes);
+ BitfieldPack (T init=T{}) : base_type(init) {}
+ };
+
+ template
+ T get (const BitfieldPack& pack) {
+ using implem::size_and_offset;
+ using implem::BitfieldData;
+ using implem::make_mask;
+
+ static_assert(Idx < sizeof...(Sizes), "Index out of bounds");
+ const constexpr auto so = size_and_offset(Idx);
+ const constexpr std::size_t index = so.offset / (sizeof(T) * CHAR_BIT);
+ const constexpr std::size_t reloffs = so.offset % (sizeof(T) * CHAR_BIT);
+ const constexpr bool bleeds_into_next = reloffs + so.size > sizeof(T) * CHAR_BIT;
+
+ const T bottom = static_cast&>(pack).value() >> reloffs;
+ if constexpr (bleeds_into_next) {
+ const std::size_t safe_index = index + 2;
+ const T val = static_cast&>(pack).value();
+ const T top = val << (sizeof(T) * CHAR_BIT - reloffs);
+ return static_cast(top | bottom) & make_mask(so.size);
+ }
+ else {
+ return bottom & make_mask(so.size);
+ }
+ }
+
+ template
+ void set (BitfieldPack& pack, V val) {
+ using implem::size_and_offset;
+ using implem::BitfieldData;
+ using implem::make_mask;
+
+ static_assert(Idx < sizeof...(Sizes), "Index out of bounds");
+ const constexpr auto so = size_and_offset(Idx);
+ const constexpr std::size_t index = so.offset / (sizeof(T) * CHAR_BIT);
+ const constexpr std::size_t reloffs = so.offset % (sizeof(T) * CHAR_BIT);
+ const constexpr bool bleeds_into_next = reloffs + so.size > sizeof(T) * CHAR_BIT;
+
+ T& bottom = static_cast&>(pack).value();
+ bottom &= ~(make_mask(so.size) << reloffs);
+ bottom |= static_cast((val & make_mask(so.size)) << reloffs);
+
+ if (bleeds_into_next) {
+ const std::size_t safe_index = index + 2 - not bleeds_into_next;
+ T& top = static_cast&>(pack).value();
+ const constexpr std::size_t shift = sizeof(T) * CHAR_BIT - reloffs;
+ top &= ~(make_mask(so.size) >> shift);
+ top |= val >> shift;
+ }
+ }
+} //namespace dhandy
+
+#endif
diff --git a/subprojects/memcard/lib/duckhandy/include/duckhandy/packed_pointer.hpp b/subprojects/memcard/lib/duckhandy/include/duckhandy/packed_pointer.hpp
new file mode 100644
index 0000000..364f913
--- /dev/null
+++ b/subprojects/memcard/lib/duckhandy/include/duckhandy/packed_pointer.hpp
@@ -0,0 +1,306 @@
+/* Copyright 2016-2025 Michele Santullo
+ * This file is part of "duckhandy".
+ *
+ * "duckhandy" 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.
+ *
+ * "duckhandy" 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 "duckhandy". If not, see .
+ */
+
+#ifndef id83CE9FAB90684250B98EA584C548E13F
+#define id83CE9FAB90684250B98EA584C548E13F
+
+#include "bitfield_pack.hpp"
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace dhandy {
+namespace detail {
+
+//This monster expression is just the one-liner version of this
+//implementation of log2:
+//unsigned int v; // 32-bit value to find the log2 of
+//register unsigned int r; // result of log2(v) will go here
+//register unsigned int shift;
+//
+//r = (v > 0xFFFF) << 4; v >>= r;
+//shift = (v > 0xFF ) << 3; v >>= shift; r |= shift;
+//shift = (v > 0xF ) << 2; v >>= shift; r |= shift;
+//shift = (v > 0x3 ) << 1; v >>= shift; r |= shift;
+// r |= (v >> 1);
+//see https://graphics.stanford.edu/~seander/bithacks.html#IntegerLog for
+//an explanation of that.
+//This is a shorter version of it but it only works if the input is a power
+//of 2:
+//(((((NUM&0xAAAAAAAA)!=0)|((NUM&0xFFFF0000)!=0)<<4)|((NUM&0xFF00FF00)!=0)<<
+//3)|((NUM&0xF0F0F0F0)!=0)<<2)|((NUM&0xCCCCCCCC)!=0)<<1
+//Source for that is also available at the page linked above.
+template constexpr std::size_t pointer_unused_bit_count =
+ ((((((NUM > 0xFFFF) << 4) | (((NUM >> ((NUM > 0xFFFF) << 4)) > 0xFF) <<
+ 3)) | ((((NUM >> ((NUM > 0xFFFF) << 4)) >> (((NUM >> ((NUM > 0xFFFF) <<
+ 4)) > 0xFF) << 3)) > 0xF) << 2)) | (((((NUM >> ((NUM > 0xFFFF) << 4)) >>
+ (((NUM >> ((NUM > 0xFFFF) << 4)) > 0xFF) << 3)) >> ((((NUM >> ((NUM >
+ 0xFFFF) << 4)) >> (((NUM >> ((NUM > 0xFFFF) << 4)) > 0xFF) << 3)) > 0xF)
+ << 2)) > 0x3) << 1)) | (((((NUM >> ((NUM > 0xFFFF) << 4)) >> (((NUM >> (
+ (NUM > 0xFFFF) << 4)) > 0xFF) << 3)) >> ((((NUM >> ((NUM > 0xFFFF) << 4)
+ ) >> (((NUM >> ((NUM > 0xFFFF) << 4)) > 0xFF) << 3)) > 0xF) << 2)) >> ((
+ (((NUM >> ((NUM > 0xFFFF) << 4)) >> (((NUM >> ((NUM > 0xFFFF) << 4)) >
+ 0xFF) << 3)) >> ((((NUM >> ((NUM > 0xFFFF) << 4)) >> (((NUM >> ((NUM >
+ 0xFFFF) << 4)) > 0xFF) << 3)) > 0xF) << 2)) > 0x3) << 1)) >> 1))
+;
+
+template
+BitfieldPack<
+ std::uintptr_t,
+ sizeof(std::uintptr_t) * CHAR_BIT - sizeof...(S), (S+1)/(S+1)...
+>
+guess_bitfield_type (std::index_sequence);
+
+template
+using BaseBitfieldPackForPackedPointer = decltype(
+ guess_bitfield_type(
+ std::make_index_sequence>()
+ )
+);
+
+template
+using BaseIntPackForPackedPointer = BitfieldPack<
+ std::uintptr_t,
+ sizeof(std::uintptr_t) * CHAR_BIT - pointer_unused_bit_count,
+ pointer_unused_bit_count
+>;
+
+template
+using BaseBitfieldType = std::conditional_t, BaseIntPackForPackedPointer
+>;
+
+template
+struct PackedPointerSetterGetter {
+ template
+ static void set (dhandy::BitfieldPack& out, I val) {
+ static_assert(0 == Idx);
+ dhandy::set<1>(out, val);
+ }
+ template
+ static I get (const dhandy::BitfieldPack& in) {
+ static_assert(0 == Idx);
+ return dhandy::get<1>(in);
+ }
+};
+
+template
+struct PackedPointerSetterGetter {
+ template
+ static void set (dhandy::BitfieldPack& out, bool flag) {
+ static_assert(Idx < sizeof...(S) - 1, "Index out of range");
+ dhandy::set(out, (flag ? 1u : 0u));
+ }
+ template
+ static bool get (const dhandy::BitfieldPack& in) {
+ static_assert(Idx < sizeof...(S) - 1, "Index out of range");
+ return static_cast(dhandy::get(in));
+ }
+};
+} //namespace detail
+
+template
+class PackedPointer : detail::BaseBitfieldType::value> {
+ static_assert(A >= 2, "Not enough space to pack anything");
+ static_assert(sizeof(T*) == sizeof(std::uintptr_t), "Mismatching int/pointer size");
+ typedef detail::BaseBitfieldType::value> parent_type;
+ static_assert(sizeof(parent_type) == sizeof(T*));
+
+ template friend J get (const PackedPointer&);
+ template friend void set (PackedPointer&, J);
+public:
+ static constexpr std::size_t FlagsCount = parent_type::element_count - 1;
+ static constexpr auto max_payload =
+ static_cast, unsigned int, I>>(
+ parent_type::template max_at_index<1>
+ );
+ typedef I int_type;
+
+ PackedPointer();
+ PackedPointer (T* ptr);
+ PackedPointer (const PackedPointer& other);
+ PackedPointer (PackedPointer&& other);
+ ~PackedPointer() = default;
+
+ operator T*();
+ operator const T*() const;
+ operator std::uintptr_t() const;
+ operator bool() const;
+
+ PackedPointer& operator= (const PackedPointer& other);
+ PackedPointer& operator= (PackedPointer&& other);
+ PackedPointer& operator= (T* ptr);
+ T* operator+ (std::integral auto diff);
+ const T* operator+ (std::integral auto diff) const;
+ T* operator- (std::integral auto diff);
+ const T* operator- (std::integral auto diff) const;
+ T* operator->();
+ const T* operator->() const;
+ T& operator*();
+ const T& operator*() const;
+ bool operator== (const PackedPointer& other) const;
+ bool operator== (T* other) const;
+ bool operator!= (const PackedPointer& other) const;
+ bool operator!= (T* other) const;
+
+private:
+ //static const constexpr uintptr_t PtrMask = static_cast(-1) << FlagsCount;
+};
+
+template
+inline void set (PackedPointer& obj, I value) {
+ assert(value < (1u << A));
+ detail::PackedPointerSetterGetter::template set(obj, value);
+}
+
+template
+inline I get (const PackedPointer& obj) {
+ return detail::PackedPointerSetterGetter::template get(obj);
+}
+
+template const constexpr std::size_t PackedPointer::FlagsCount;
+//template const constexpr std::uintptr_t PackedPointer::PtrMask;
+
+template
+PackedPointer::PackedPointer() :
+ PackedPointer(nullptr)
+{
+}
+
+template
+PackedPointer::PackedPointer (T* ptr) :
+ parent_type(0)
+{
+ (*this) = ptr;
+}
+
+template
+PackedPointer::PackedPointer (const PackedPointer& other) :
+ parent_type(other)
+{
+}
+
+template
+PackedPointer::PackedPointer (PackedPointer&& other) :
+ parent_type(std::move(other))
+{
+}
+
+template
+PackedPointer::operator T*() {
+ return reinterpret_cast(get<0>(*static_cast(this)) << FlagsCount);
+}
+
+template
+PackedPointer::operator const T*() const {
+ return reinterpret_cast(get<0>(*static_cast(this)) << FlagsCount);
+}
+
+template
+PackedPointer::operator std::uintptr_t() const {
+ return get<0>(*static_cast(this)) << FlagsCount;
+}
+
+template
+PackedPointer::operator bool() const {
+ return static_cast(*this) != nullptr;
+}
+
+template
+PackedPointer& PackedPointer::operator= (const PackedPointer& other) {
+ *static_cast(this) = static_cast(other);
+ return *this;
+}
+
+template
+PackedPointer& PackedPointer::operator= (PackedPointer&& other) {
+ *static_cast(this) = std::move(static_cast(other));
+ return *this;
+}
+
+template
+PackedPointer& PackedPointer::operator= (T* ptr) {
+ assert(reinterpret_cast(ptr) % A == 0);
+ set<0>(*static_cast(this), reinterpret_cast(ptr) >> FlagsCount);
+ return *this;
+}
+
+template
+T* PackedPointer::operator+ (std::integral auto diff) {
+ return static_cast(*this) + diff;
+}
+
+template
+const T* PackedPointer::operator+ (std::integral auto diff) const {
+ return static_cast(*this) + diff;
+}
+
+template
+T* PackedPointer::operator- (std::integral auto diff) {
+ return static_cast(*this) + diff;
+}
+
+template
+const T* PackedPointer::operator- (std::integral auto diff) const {
+ return static_cast(*this) + diff;
+}
+
+template
+T* PackedPointer::operator->() {
+ return static_cast(*this);
+}
+
+template
+const T* PackedPointer::operator->() const {
+ return static_cast(*this);
+}
+
+template
+T& PackedPointer::operator*() {
+ return *static_cast(*this);
+}
+
+template
+const T& PackedPointer::operator*() const {
+ return *static_cast(*this);
+}
+
+template
+bool PackedPointer::operator== (const PackedPointer& other) const {
+ return this == &other or static_cast(*this) == static_cast(other);
+}
+
+template
+bool PackedPointer::operator== (T* other) const {
+ return static_cast(*this) == other;
+}
+
+template
+bool PackedPointer::operator!= (const PackedPointer& other) const {
+ return not this->operator==(other);
+}
+
+template
+bool PackedPointer::operator!= (T* other) const {
+ return not this->operator==(other);
+}
+} //namepace dhandy
+
+#endif
diff --git a/subprojects/memcard/meson.build b/subprojects/memcard/meson.build
index 61fc4b6..1e630b7 100644
--- a/subprojects/memcard/meson.build
+++ b/subprojects/memcard/meson.build
@@ -3,6 +3,7 @@ add_project_link_arguments(['-lstdc++fs'], language: 'cpp')
private_incl = include_directories('src')
library_incl = include_directories('include')
+duckhandy_incl = include_directories('lib/duckhandy/include')
memcard = shared_library('memcard',
'src/memorycard.cpp',
@@ -16,11 +17,12 @@ memcard = shared_library('memcard',
'src/part_iterator.cpp',
'src/frame.cpp',
'src/country_code.cpp',
+ 'src/alignment.cpp',
install: true,
- include_directories: [private_incl, library_incl],
+ include_directories: [private_incl, library_incl, duckhandy_incl],
)
memcard_dep = declare_dependency(
- include_directories: library_incl,
+ include_directories: [library_incl, duckhandy_incl],
link_with: memcard
)
diff --git a/subprojects/memcard/src/alignment.cpp b/subprojects/memcard/src/alignment.cpp
new file mode 100644
index 0000000..4686010
--- /dev/null
+++ b/subprojects/memcard/src/alignment.cpp
@@ -0,0 +1,54 @@
+/* Copyright 2020-2025, 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 .
+ */
+
+#include "memcard/alignment.hpp"
+#include "memcard/block.hpp"
+#include "duckhandy/packed_pointer.hpp"
+
+namespace mc::psx {
+std::uint8_t* to_block_aligned (std::uint8_t* ptr, std::size_t index, std::ptrdiff_t base_offs) {
+ return reinterpret_cast(
+ (reinterpret_cast(ptr) + (Block::DataAlignment-1)) & ~(Block::DataAlignment-1)
+ ) + base_offs + index * Block::BlockByteSize;
+}
+
+const std::uint8_t* to_block_aligned (const std::uint8_t* ptr, std::size_t index, std::ptrdiff_t base_offs) {
+ return const_cast(
+ to_block_aligned(const_cast(ptr), index, base_offs)
+ );
+}
+
+bool is_block_aligned (const std::uint8_t* ptr) {
+ return (0 == reinterpret_cast(ptr) % Block::DataAlignment);
+}
+
+template
+bool is_block_aligned (const dhandy::PackedPointer& ptr) {
+ return (0 == static_cast(ptr) % Block::DataAlignment);
+}
+
+template bool is_block_aligned (
+ const dhandy::PackedPointer&
+);
+template bool is_block_aligned (
+ const dhandy::PackedPointer&
+);
+
+std::unique_ptr make_unique_for_block_aligned(std::size_t size) {
+ return std::make_unique(size + Block::DataAlignment - 1);
+}
+} //namespace mc::psx
diff --git a/subprojects/memcard/src/block.cpp b/subprojects/memcard/src/block.cpp
index 27c55b4..7c43849 100644
--- a/subprojects/memcard/src/block.cpp
+++ b/subprojects/memcard/src/block.cpp
@@ -17,6 +17,7 @@
#include "memcard/block.hpp"
#include "memcard/memorycard.hpp"
+#include "memcard/alignment.hpp"
#include "shiftjis_to_utf8.hpp"
#include
#include
@@ -94,10 +95,12 @@ BasicBlock::BasicBlock (data_type* blk, std::size_t index) :
template
BasicBlock::BasicBlock (data_type* blk, BasicFrame toc_entry, std::size_t index) :
m_toc_entry(toc_entry),
- m_index(index),
m_begin(blk)
{
assert(m_begin);
+ assert(is_block_aligned(m_begin));
+ assert(index <= m_begin.max_payload);
+ dhandy::set<0>(m_begin, index);
}
template
diff --git a/subprojects/memcard/src/frame.cpp b/subprojects/memcard/src/frame.cpp
index 0d8bb3c..a707c85 100644
--- a/subprojects/memcard/src/frame.cpp
+++ b/subprojects/memcard/src/frame.cpp
@@ -16,14 +16,16 @@
*/
#include "memcard/frame.hpp"
+#include "memcard/alignment.hpp"
#include
namespace mc::psx {
template
BasicFrame::BasicFrame (data_type* data, std::size_t index) :
- m_index(index),
m_data(data)
{
+ assert(is_block_aligned(m_data));
+ dhandy::set<0>(m_data, index);
}
template
@@ -41,7 +43,6 @@ auto BasicFrame::operator[] (std::size_t index) const -> const data_type&
template
template
BasicFrame& BasicFrame::operator= (const BasicFrame& other) {
- m_index = other.m_index;
m_data = other.m_data;
return *this;
}
diff --git a/subprojects/memcard/src/memorycard.cpp b/subprojects/memcard/src/memorycard.cpp
index 9d13fb7..926edf5 100644
--- a/subprojects/memcard/src/memorycard.cpp
+++ b/subprojects/memcard/src/memorycard.cpp
@@ -18,6 +18,7 @@
#include "memcard/memorycard.hpp"
#include "memcard/block.hpp"
#include "memcard/frame.hpp"
+#include "memcard/alignment.hpp"
#include