summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2022-12-02 03:53:23 +0100
committerStanislaw Halik <sthalik@misaki.pl>2022-12-02 03:53:23 +0100
commit9a0ac7c7fd06a60f0e61b88828eaa59c0ee30fdd (patch)
treed8166bea565e13e6337b4bd3dc2fb8be027d69b5
parente76fae1b2f007efc58ef46ecc248d271676c6673 (diff)
serialize: work on recovering from corrupted saves
-rw-r--r--compat/exception.hpp49
-rw-r--r--serialize/CMakeLists.txt10
-rw-r--r--serialize/binary-reader.hpp14
-rw-r--r--serialize/binary-reader.inl26
-rw-r--r--serialize/binary-serializer.hpp10
-rw-r--r--serialize/world-reader.cpp43
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/exception.cpp7
8 files changed, 106 insertions, 54 deletions
diff --git a/compat/exception.hpp b/compat/exception.hpp
new file mode 100644
index 00000000..d0d2ca31
--- /dev/null
+++ b/compat/exception.hpp
@@ -0,0 +1,49 @@
+#pragma once
+#include "compat/format.hpp"
+#include <type_traits>
+#include <iterator>
+#include <exception>
+
+namespace floormat {
+
+struct base_exception {};
+
+struct exception : std::exception, base_exception
+{
+ template<typename Fmt, typename... Ts>
+ exception(const Fmt& fmt, Ts&&... args) noexcept;
+
+ const char* what() const noexcept override;
+
+private:
+ fmt::memory_buffer buf;
+};
+
+template<typename Fmt, typename... Ts>
+exception::exception(const Fmt& fmt, Ts&&... args) noexcept
+{
+ fmt::format_to(std::back_inserter(buf), fmt, std::forward<Ts>(args)...);
+ buf.push_back('\0');
+}
+
+} // namespace floormat
+
+#define fm_soft_assert(...) \
+ do { \
+ if (!(__VA_ARGS__)) \
+ { \
+ if (std::is_constant_evaluated()) \
+ throw base_exception{}; \
+ else \
+ throw exception{"assertion failed: {} in {}:{}"_cf, \
+ #__VA_ARGS__, __FILE__, __LINE__}; \
+ } \
+ } while (false)
+
+#define fm_throw(fmt, ...) \
+ do { \
+ if (std::is_constant_evaluated()) \
+ throw base_exception{}; \
+ else \
+ throw exception{fmt, __VA_ARGS__}; \
+ } while (false)
diff --git a/serialize/CMakeLists.txt b/serialize/CMakeLists.txt
index 501dc448..7b7efb76 100644
--- a/serialize/CMakeLists.txt
+++ b/serialize/CMakeLists.txt
@@ -5,14 +5,16 @@ target_link_libraries(
${self}_o PUBLIC
Magnum::Magnum
nlohmann_json::nlohmann_json
+ fmt::fmt
)
+
+if(MSVC)
+ add_compile_options(-EHsc)
+endif()
+
add_library(${self} STATIC dummy.cc)
target_link_libraries(${self} PUBLIC ${self}_o floormat-loader floormat)
if(FLOORMAT_PRECOMPILED-HEADERS)
target_precompile_headers(${self}_o PRIVATE precomp.hpp)
endif()
-
-if(MSVC)
- add_compile_options(-EHsc)
-endif()
diff --git a/serialize/binary-reader.hpp b/serialize/binary-reader.hpp
index 530d9f0c..0c597772 100644
--- a/serialize/binary-reader.hpp
+++ b/serialize/binary-reader.hpp
@@ -26,12 +26,12 @@ template<string_input_iterator It>
struct binary_reader final {
template<char_sequence Seq> explicit constexpr binary_reader(const Seq& seq) noexcept;
constexpr binary_reader(It begin, It end) noexcept;
- constexpr void assert_end() noexcept;
+ constexpr void assert_end() noexcept(false);
- template<serializable T> constexpr T read() noexcept;
- template<std::size_t N> constexpr std::array<char, N> read() noexcept;
constexpr std::size_t bytes_read() const noexcept { return num_bytes_read; }
- template<std::size_t Max> constexpr auto read_asciiz_string() noexcept;
+ template<serializable T> constexpr T read() noexcept(false);
+ template<std::size_t N> constexpr std::array<char, N> read() noexcept(false);
+ template<std::size_t Max> constexpr auto read_asciiz_string() noexcept(false);
binary_reader(binary_reader&&) noexcept = default;
binary_reader& operator=(binary_reader&&) noexcept = default;
@@ -44,14 +44,14 @@ private:
};
template<string_input_iterator It, serializable T>
-void operator<<(T& x, binary_reader<It>& reader) noexcept;
+void operator<<(T& x, binary_reader<It>& reader) noexcept(false);
template<string_input_iterator It, serializable T>
-binary_reader<It>& operator>>(binary_reader<It>& reader, T& x) noexcept;
+binary_reader<It>& operator>>(binary_reader<It>& reader, T& x) noexcept(false);
template<string_input_iterator It> binary_reader(It&& begin, It&& end) -> binary_reader<std::decay_t<It>>;
template<typename Array>
-binary_reader(Array&& array) -> binary_reader<std::decay_t<decltype(std::begin(array))>>;
+binary_reader(const Array& array) -> binary_reader<std::decay_t<decltype(std::begin(array))>>;
} // namespace floormat::Serialize
diff --git a/serialize/binary-reader.inl b/serialize/binary-reader.inl
index d1abcec9..7b831f30 100644
--- a/serialize/binary-reader.inl
+++ b/serialize/binary-reader.inl
@@ -1,26 +1,26 @@
#pragma once
#include "binary-reader.hpp"
-#include "compat/assert.hpp"
+#include "compat/exception.hpp"
namespace floormat::Serialize {
template<string_input_iterator It>
template<char_sequence Seq>
constexpr binary_reader<It>::binary_reader(const Seq& seq) noexcept
- : it{std::begin(seq)}, end{std::end(seq)}
+ : it{std::cbegin(seq)}, end{std::cend(seq)}
{}
template<string_input_iterator It>
constexpr binary_reader<It>::binary_reader(It begin, It end) noexcept :
- it{begin}, end{end}
+ it{std::move(begin)}, end{std::move(end)}
{}
template<string_input_iterator It>
template<serializable T>
-constexpr T binary_reader<It>::read() noexcept
+constexpr T binary_reader<It>::read() noexcept(false)
{
constexpr std::size_t N = sizeof(T);
- fm_assert((std::ptrdiff_t)N <= std::distance(it, end));
+ fm_soft_assert((std::ptrdiff_t)N <= std::distance(it, end));
num_bytes_read += N;
char buf[N];
for (std::size_t i = 0; i < N; i++)
@@ -30,12 +30,12 @@ constexpr T binary_reader<It>::read() noexcept
template<string_input_iterator It>
template<std::size_t N>
-constexpr std::array<char, N> binary_reader<It>::read() noexcept
+constexpr std::array<char, N> binary_reader<It>::read() noexcept(false)
{
std::array<char, N> array;
if (std::is_constant_evaluated())
array = {};
- fm_assert(N <= (std::size_t)std::distance(it, end));
+ fm_soft_assert(N <= (std::size_t)std::distance(it, end));
num_bytes_read += N;
for (std::size_t i = 0; i < N; i++)
array[i] = *it++;
@@ -43,27 +43,27 @@ constexpr std::array<char, N> binary_reader<It>::read() noexcept
}
template<string_input_iterator It>
-constexpr void binary_reader<It>::assert_end() noexcept
+constexpr void binary_reader<It>::assert_end() noexcept(false)
{
- fm_assert(it == end);
+ fm_soft_assert(it == end);
}
template<string_input_iterator It, serializable T>
-binary_reader<It>& operator>>(binary_reader<It>& reader, T& x) noexcept
+binary_reader<It>& operator>>(binary_reader<It>& reader, T& x) noexcept(false)
{
x = reader.template read<T>();
return reader;
}
template<string_input_iterator It, serializable T>
-void operator<<(T& x, binary_reader<It>& reader) noexcept
+void operator<<(T& x, binary_reader<It>& reader) noexcept(false)
{
x = reader.template read<T>();
}
template<string_input_iterator It>
template<std::size_t MAX>
-constexpr auto binary_reader<It>::read_asciiz_string() noexcept
+constexpr auto binary_reader<It>::read_asciiz_string() noexcept(false)
{
static_assert(MAX > 0);
@@ -84,7 +84,7 @@ constexpr auto binary_reader<It>::read_asciiz_string() noexcept
return ret;
}
}
- fm_abort("can't find string terminator");
+ fm_throw("can't find string terminator"_cf);
}
} // namespace floormat::Serialize
diff --git a/serialize/binary-serializer.hpp b/serialize/binary-serializer.hpp
index 057b3c4d..338173c2 100644
--- a/serialize/binary-serializer.hpp
+++ b/serialize/binary-serializer.hpp
@@ -10,12 +10,6 @@ namespace floormat::Serialize {
static_assert(std::endian::native == std::endian::big || std::endian::native == std::endian::little);
-enum class value_type : unsigned char {
- none, uc, u8, u16, u32, u64,
- f32, f64,
- COUNT
-};
-
template<std::size_t N> struct make_integer;
template<std::size_t N> using make_integer_t = typename make_integer<N>::type;
@@ -39,13 +33,13 @@ concept serializable = requires(T x) {
};
template<typename T>
-constexpr inline T maybe_byteswap(T x)
+constexpr inline T maybe_byteswap(T x) noexcept
{
return x;
}
template<integer T>
-constexpr inline T maybe_byteswap(T x)
+constexpr inline T maybe_byteswap(T x) noexcept
{
if constexpr(std::endian::native == std::endian::big)
return std::byteswap(x);
diff --git a/serialize/world-reader.cpp b/serialize/world-reader.cpp
index d95f81b5..b6cf8839 100644
--- a/serialize/world-reader.cpp
+++ b/serialize/world-reader.cpp
@@ -6,6 +6,7 @@
#include "loader/scenery.hpp"
#include "src/tile-atlas.hpp"
#include "src/anim-atlas.hpp"
+
#include <cstring>
namespace {
@@ -66,23 +67,23 @@ void reader_state::read_sceneries(reader_t& s)
std::uint16_t magic; magic << s;
if (magic != scenery_magic)
- fm_abort("bad scenery magic");
+ fm_throw("bad scenery magic"_cf);
atlasid sz; sz << s;
- fm_assert(sz < scenery_id_max);
+ fm_soft_assert(sz < scenery_id_max);
sceneries.resize(sz);
std::size_t i = 0;
while (i < sz)
{
std::uint8_t num; num << s;
- fm_assert(num > 0);
+ fm_soft_assert(num > 0);
auto str = s.read_asciiz_string<atlas_name_max>();
const auto sc_ = loader.scenery(str);
for (std::size_t n = 0; n < num; n++)
{
atlasid id; id << s;
- fm_assert(id < sz);
- fm_assert(!sceneries[id]);
+ fm_soft_assert(id < sz);
+ fm_soft_assert(!sceneries[id]);
scenery_proto sc = sc_;
bool short_frame = read_scenery_flags(s, sc.frame);
fm_debug_assert(sc.atlas != nullptr);
@@ -90,14 +91,12 @@ void reader_state::read_sceneries(reader_t& s)
sc.frame.frame = s.read<std::uint8_t>();
else
sc.frame.frame << s;
- fm_assert(sc.frame.frame < sc.atlas->info().nframes);
+ fm_soft_assert(sc.frame.frame < sc.atlas->info().nframes);
sceneries[id] = sc;
}
i += num;
}
- fm_assert(i == sz);
- for (const scenery_proto& sc : sceneries)
- fm_assert(sc);
+ fm_soft_assert(i == sz);
}
const std::shared_ptr<tile_atlas>& reader_state::lookup_atlas(atlasid id)
@@ -105,7 +104,7 @@ const std::shared_ptr<tile_atlas>& reader_state::lookup_atlas(atlasid id)
if (id < atlases.size())
return atlases[id];
else
- fm_abort("no such atlas: '%zu'", (std::size_t)id);
+ fm_throw("no such atlas: '{}'"_cf, id);
}
const scenery_proto& reader_state::lookup_scenery(atlasid id)
@@ -113,7 +112,7 @@ const scenery_proto& reader_state::lookup_scenery(atlasid id)
if (id < sceneries.size())
return sceneries[id];
else
- fm_abort("no such scenery: '%zu'", (std::size_t)id);
+ fm_throw("no such scenery: '{}'"_cf, id);
}
void reader_state::read_chunks(reader_t& s)
@@ -125,7 +124,7 @@ void reader_state::read_chunks(reader_t& s)
std::decay_t<decltype(chunk_magic)> magic;
magic << s;
if (magic != chunk_magic)
- fm_abort("bad chunk magic");
+ fm_throw("bad chunk magic"_cf);
chunk_coords coord;
coord.x << s;
coord.y << s;
@@ -145,7 +144,7 @@ void reader_state::read_chunks(reader_t& s)
? s.read<std::uint8_t>()
: std::uint8_t(s.read<std::uint16_t>());
auto atlas = lookup_atlas(id);
- fm_assert(v < atlas->num_tiles());
+ fm_soft_assert(v < atlas->num_tiles());
return { atlas, v };
};
@@ -186,7 +185,7 @@ void reader_state::read_chunks(reader_t& s)
t.pass_mode() = x;
break;
default: [[unlikely]]
- fm_abort("bad pass mode '%zu' for tile %zu", i, (std::size_t)x);
+ fm_throw("bad pass mode '{}' for tile {}"_cf, i, x);
}
}
}
@@ -196,11 +195,11 @@ void reader_state::deserialize_world(ArrayView<const char> buf)
{
auto s = binary_reader{buf};
if (!!::memcmp(s.read<std::size(file_magic)-1>().data(), file_magic, std::size(file_magic)-1))
- fm_abort("bad magic");
+ fm_throw("bad magic"_cf);
proto_t proto;
proto << s;
if (!(proto >= min_proto_version && proto <= proto_version))
- fm_abort("bad proto version '%zu' (should be between '%zu' and '%zu')",
+ fm_throw("bad proto version '{}' (should be between '{}' and '{}')"_cf,
(std::size_t)proto, (std::size_t)min_proto_version, (std::size_t)proto_version);
PROTO = proto;
read_atlases(s);
@@ -225,17 +224,17 @@ world world::deserialize(StringView filename)
(void)::strerror_s(buf, std::size(buf), errno);
#endif
};
- fm_assert(filename.flags() & StringViewFlag::NullTerminated);
+ fm_soft_assert(filename.flags() & StringViewFlag::NullTerminated);
FILE_raii f = ::fopen(filename.data(), "rb");
if (!f)
{
get_error_string(errbuf);
- fm_abort("fopen(\"%s\", \"r\"): %s", filename.data(), errbuf);
+ fm_throw("fopen(\"{}\", \"r\"): {}"_cf, filename.data(), errbuf);
}
if (int ret = ::fseek(f, 0, SEEK_END); ret != 0)
{
get_error_string(errbuf);
- fm_abort("fseek(SEEK_END): %s", errbuf);
+ fm_throw("fseek(SEEK_END): {}"_cf, errbuf);
}
std::size_t len;
if (auto len_ = ::ftell(f); len_ >= 0)
@@ -243,19 +242,19 @@ world world::deserialize(StringView filename)
else
{
get_error_string(errbuf);
- fm_abort("ftell: %s", errbuf);
+ fm_throw("ftell: {}"_cf, errbuf);
}
if (int ret = ::fseek(f, 0, SEEK_SET); ret != 0)
{
get_error_string(errbuf);
- fm_abort("fseek(SEEK_SET): %s", errbuf);
+ fm_throw("fseek(SEEK_SET): {}"_cf, errbuf);
}
auto buf_ = std::make_unique<char[]>(len);
if (auto ret = ::fread(&buf_[0], 1, len, f); ret != len)
{
get_error_string(errbuf);
- fm_abort("fread short read: %s", errbuf);
+ fm_throw("fread short read: {}"_cf, errbuf);
}
world w;
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index c994aacb..7af9439e 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -6,6 +6,7 @@ target_link_libraries(
Magnum::GL
Magnum::Magnum
Magnum::Shaders
+ fmt::fmt
)
if(FLOORMAT_PRECOMPILED-HEADERS)
target_precompile_headers(${self} PRIVATE precomp.hpp)
diff --git a/src/exception.cpp b/src/exception.cpp
new file mode 100644
index 00000000..4855e240
--- /dev/null
+++ b/src/exception.cpp
@@ -0,0 +1,7 @@
+#include "compat/exception.hpp"
+
+namespace floormat {
+
+const char* exception::what() const noexcept { return buf.data(); }
+
+} // namespace floormat