1
0
mirror of synced 2025-01-22 19:32:09 +01:00

Finally remove cereal

Introduce MappableKeys
This commit is contained in:
Stepland 2020-04-26 01:26:17 +02:00
parent 7385d1abe9
commit 9f9669e1aa
18 changed files with 23393 additions and 419 deletions

22875
include/nlohmann/json.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -31,8 +31,11 @@ sources = [
'src/Data/Chart.hpp',
'src/Data/KeyMapping.hpp',
'src/Data/KeyMapping.cpp',
'src/Data/MappableKeys.hpp',
'src/Data/MappableKeys.cpp',
'src/Data/Note.hpp',
'src/Data/Preferences.hpp',
'src/Data/Preferences.cpp',
'src/Data/Score.hpp',
'src/Data/Song.hpp',
'src/Data/Song.cpp',
@ -79,7 +82,6 @@ sources = [
'src/Toolkit/Debuggable.hpp',
'src/Toolkit/EasingFunctions.hpp',
'src/Toolkit/EasingFunctions.cpp',
'src/Toolkit/ExtraCerealTypes/GHCFilesystemPath.hpp',
'src/Toolkit/GHCFilesystemPathHash.hpp',
'src/Toolkit/HSL.hpp',
'src/Toolkit/HSL.cpp',

View File

@ -1,7 +1,5 @@
#include "KeyMapping.hpp"
#include <cereal/details/traits.hpp>
namespace Data {
KeyMapping::KeyMapping() {
m_key_to_button[sf::Keyboard::Num1] = Button::B1;
@ -26,21 +24,21 @@ namespace Data {
}
}
KeyMapping::KeyMapping(std::unordered_map<Button, sf::Keyboard::Key> button_to_key) : m_button_to_key(button_to_key) {
KeyMapping::KeyMapping(std::unordered_map<Button, MappableKey> button_to_key) : m_button_to_key(button_to_key) {
for (auto &&[button, key] : m_button_to_key) {
m_key_to_button[key] = button;
}
assert((m_button_to_key.size() == m_key_to_button.size()));
}
KeyMapping::KeyMapping(std::unordered_map<sf::Keyboard::Key, Button> key_to_button) : m_key_to_button(key_to_button) {
KeyMapping::KeyMapping(std::unordered_map<MappableKey, Button> key_to_button) : m_key_to_button(key_to_button) {
for (auto &&[key, button] : m_key_to_button) {
m_button_to_key[button] = key;
}
assert((m_button_to_key.size() == m_key_to_button.size()));
}
void KeyMapping::set_button_to_key(const Button& button, const sf::Keyboard::Key& key) {
void KeyMapping::set_button_to_key(const Button& button, const MappableKey& key) {
if (m_key_to_button.find(key) != m_key_to_button.end()) {
m_button_to_key.erase(m_key_to_button[key]);
m_key_to_button.erase(key);
@ -49,7 +47,7 @@ namespace Data {
m_key_to_button[key] = button;
}
std::optional<Button> KeyMapping::key_to_button(const sf::Keyboard::Key& key) {
std::optional<Button> KeyMapping::key_to_button(const MappableKey& key) {
if (m_key_to_button.find(key) == m_key_to_button.end()) {
return {};
} else {
@ -57,12 +55,26 @@ namespace Data {
}
}
std::optional<sf::Keyboard::Key> KeyMapping::button_to_key(const Button& button) {
std::optional<MappableKey> KeyMapping::button_to_key(const Button& button) {
if (m_button_to_key.find(button) == m_button_to_key.end()) {
return {};
} else {
return m_button_to_key.at(button);
}
}
void to_json(nlohmann::json& j, const KeyMapping& km) {
for(auto const&[button, key] : km.m_button_to_key) {
j["B"+(std::to_string(button_to_index(button)+1))] = to_string(key);
}
}
void from_json(const nlohmann::json& j, KeyMapping& km) {
std::unordered_map<Button, MappableKey> map;
for (std::size_t i = 0; i < 16; i++) {
map[*index_to_button(i)] = from_string(j.at("B"+std::to_string(i+1)));
}
km = KeyMapping{map};
}
}

View File

@ -4,77 +4,28 @@
#include <string>
#include <unordered_map>
#include <cereal/details/traits.hpp>
#include <cereal/types/string.hpp>
#include <nlohmann/json.hpp>
#include <magic_enum.hpp>
#include <SFML/Window.hpp>
#include "Buttons.hpp"
#include "MappableKeys.hpp"
namespace Data {
class KeyMapping {
public:
KeyMapping();
explicit KeyMapping(std::unordered_map<Button, sf::Keyboard::Key> button_to_key);
explicit KeyMapping(std::unordered_map<sf::Keyboard::Key, Button> button_to_key);
void set_button_to_key(const Button& button, const sf::Keyboard::Key& key);
std::optional<Button> key_to_button(const sf::Keyboard::Key& key);
std::optional<sf::Keyboard::Key> button_to_key(const Button& button);
explicit KeyMapping(std::unordered_map<Button, MappableKey> button_to_key);
explicit KeyMapping(std::unordered_map<MappableKey, Button> button_to_key);
void set_button_to_key(const Button& button, const MappableKey& key);
std::optional<Button> key_to_button(const MappableKey& key);
std::optional<MappableKey> button_to_key(const Button& button);
private:
std::unordered_map<sf::Keyboard::Key, Button> m_key_to_button;
std::unordered_map<Button, sf::Keyboard::Key> m_button_to_key;
std::unordered_map<MappableKey, Button> m_key_to_button;
std::unordered_map<Button, MappableKey> m_button_to_key;
friend struct Preferences;
friend void to_json(nlohmann::json& j, const KeyMapping& km);
friend void from_json(const nlohmann::json& j, KeyMapping& km);
};
}
namespace cereal {
// Saving for std::unordered_map<Button, sf::Keyboard::Key> for text based archives
template <
class Archive,
traits::EnableIf<traits::is_text_archive<Archive>::value> = traits::sfinae
> inline
void save(Archive& ar, const std::unordered_map<Data::Button, sf::Keyboard::Key>& map ) {
for(auto&& i : magic_enum::enum_entries<Data::Button>()) {
auto it = map.find(i.first);
if (it != map.end()) {
ar(
cereal::make_nvp(
std::string{i.second},
std::string{magic_enum::enum_name(it->second)}
)
);
}
}
}
//! Loading for std::unordered_map<Button, sf::Keyboard::Key> for text based archives
template <
class Archive,
traits::EnableIf<traits::is_text_archive<Archive>::value> = traits::sfinae
> inline
void load(Archive& ar, std::unordered_map<Data::Button, sf::Keyboard::Key>& map ) {
map.clear();
while(true) {
const auto namePtr = ar.getNodeName();
if (not namePtr) {
break;
}
std::string button_str = namePtr;
std::string keyboard_key_str;
ar(keyboard_key_str);
auto button = magic_enum::enum_cast<Data::Button>(button_str);
if (not button.has_value()) {
throw std::runtime_error("Invalid Button : "+button_str);
}
auto keyboard_key = magic_enum::enum_cast<sf::Keyboard::Key>(keyboard_key_str);
if (not keyboard_key.has_value()) {
throw std::runtime_error("Invalid Keyboard key : "+keyboard_key_str);
}
map.emplace(*button, *keyboard_key);
}
}
} // namespace cereal

43
src/Data/MappableKeys.cpp Normal file
View File

@ -0,0 +1,43 @@
#include "MappableKeys.hpp"
namespace Data {
MappableKeyToString mappable_button_to_string;
std::string to_string(const MappableKey& mk) {
return std::visit(mappable_button_to_string, mk);
}
std::regex mappable_button_regex("(.+?)::(.+)");
std::regex joystick_button_regex("(\\d+)_(\\d+)");
MappableKey from_string(const std::string& s) {
std::smatch matches;
if (not std::regex_match(s, matches, mappable_button_regex)) {
throw std::runtime_error("Unknown MappableKey : "+s);
}
auto device = matches[1].str();
auto button = matches[2].str();
if (device == "Keyboard") {
return string_to_keyboard.at(button);
} else if (device == "Joystick") {
std::smatch joystick_matches;
if (not std::regex_match(button, joystick_matches, joystick_button_regex)) {
throw std::runtime_error("Invalid Joystick Button : "+button);
}
auto joystick_id = std::stoul(matches[1].str());
auto joystick_button = std::stoul(matches[2].str());
if (joystick_id >= sf::Joystick::Count) {
throw std::runtime_error("Unsupported Joystick ID : "+matches[1].str());
}
if (joystick_button >= sf::Joystick::ButtonCount) {
throw std::runtime_error("Unsupported Joystick Button : "+matches[2].str());
}
Data::JoystickButton res;
res.joystickId = static_cast<unsigned int>(joystick_id);
res.button = static_cast<unsigned int>(joystick_button);
return res;
} else {
throw std::runtime_error("Unsupported input device : "+device);
}
}
}

264
src/Data/MappableKeys.hpp Normal file
View File

@ -0,0 +1,264 @@
#pragma once
#include <iomanip>
#include <limits>
#include <regex>
#include <string>
#include <variant>
#include <unordered_map>
#include <utility>
#include <nlohmann/json.hpp>
#include <SFML/Window/Keyboard.hpp>
#include <SFML/Window/Event.hpp>
namespace Data {
struct JoystickButton : sf::Event::JoystickButtonEvent {
friend bool operator==(const JoystickButton& rhs, const JoystickButton& lhs) {
return rhs.joystickId == lhs.joystickId and rhs.button == lhs.button;
}
};
}
namespace std {
template <> struct hash<Data::JoystickButton> {
size_t operator()(const Data::JoystickButton & jbe) const {
std::hash<unsigned int> hasher;
size_t res = 17;
res = res * 37 + hasher(jbe.joystickId);
res = res * 37 + hasher(jbe.button);
return res;
}
};
}
namespace Data {
using MappableKey = std::variant<sf::Keyboard::Key, JoystickButton>;
const std::unordered_map<sf::Keyboard::Key, std::string> keyboard_to_string{
{
{sf::Keyboard::Unknown, "Unknown"},
{sf::Keyboard::A, "A"},
{sf::Keyboard::B, "B"},
{sf::Keyboard::C, "C"},
{sf::Keyboard::D, "D"},
{sf::Keyboard::E, "E"},
{sf::Keyboard::F, "F"},
{sf::Keyboard::G, "G"},
{sf::Keyboard::H, "H"},
{sf::Keyboard::I, "I"},
{sf::Keyboard::J, "J"},
{sf::Keyboard::K, "K"},
{sf::Keyboard::L, "L"},
{sf::Keyboard::M, "M"},
{sf::Keyboard::N, "N"},
{sf::Keyboard::O, "O"},
{sf::Keyboard::P, "P"},
{sf::Keyboard::Q, "Q"},
{sf::Keyboard::R, "R"},
{sf::Keyboard::S, "S"},
{sf::Keyboard::T, "T"},
{sf::Keyboard::U, "U"},
{sf::Keyboard::V, "V"},
{sf::Keyboard::W, "W"},
{sf::Keyboard::X, "X"},
{sf::Keyboard::Y, "Y"},
{sf::Keyboard::Z, "Z"},
{sf::Keyboard::Num0, "Num0"},
{sf::Keyboard::Num1, "Num1"},
{sf::Keyboard::Num2, "Num2"},
{sf::Keyboard::Num3, "Num3"},
{sf::Keyboard::Num4, "Num4"},
{sf::Keyboard::Num5, "Num5"},
{sf::Keyboard::Num6, "Num6"},
{sf::Keyboard::Num7, "Num7"},
{sf::Keyboard::Num8, "Num8"},
{sf::Keyboard::Num9, "Num9"},
{sf::Keyboard::Escape, "Escape"},
{sf::Keyboard::LControl, "LControl"},
{sf::Keyboard::LShift, "LShift"},
{sf::Keyboard::LAlt, "LAlt"},
{sf::Keyboard::LSystem, "LSystem"},
{sf::Keyboard::RControl, "RControl"},
{sf::Keyboard::RShift, "RShift"},
{sf::Keyboard::RAlt, "RAlt"},
{sf::Keyboard::RSystem, "RSystem"},
{sf::Keyboard::Menu, "Menu"},
{sf::Keyboard::LBracket, "LBracket"},
{sf::Keyboard::RBracket, "RBracket"},
{sf::Keyboard::Semicolon, "Semicolon"},
{sf::Keyboard::Comma, "Comma"},
{sf::Keyboard::Period, "Period"},
{sf::Keyboard::Quote, "Quote"},
{sf::Keyboard::Slash, "Slash"},
{sf::Keyboard::Backslash, "Backslash"},
{sf::Keyboard::Tilde, "Tilde"},
{sf::Keyboard::Equal, "Equal"},
{sf::Keyboard::Hyphen, "Hyphen"},
{sf::Keyboard::Space, "Space"},
{sf::Keyboard::Enter, "Enter"},
{sf::Keyboard::Backspace, "Backspace"},
{sf::Keyboard::Tab, "Tab"},
{sf::Keyboard::PageUp, "PageUp"},
{sf::Keyboard::PageDown, "PageDown"},
{sf::Keyboard::End, "End"},
{sf::Keyboard::Home, "Home"},
{sf::Keyboard::Insert, "Insert"},
{sf::Keyboard::Delete, "Delete"},
{sf::Keyboard::Add, "Add"},
{sf::Keyboard::Subtract, "Subtract"},
{sf::Keyboard::Multiply, "Multiply"},
{sf::Keyboard::Divide, "Divide"},
{sf::Keyboard::Left, "Left"},
{sf::Keyboard::Right, "Right"},
{sf::Keyboard::Up, "Up"},
{sf::Keyboard::Down, "Down"},
{sf::Keyboard::Numpad0, "Numpad0"},
{sf::Keyboard::Numpad1, "Numpad1"},
{sf::Keyboard::Numpad2, "Numpad2"},
{sf::Keyboard::Numpad3, "Numpad3"},
{sf::Keyboard::Numpad4, "Numpad4"},
{sf::Keyboard::Numpad5, "Numpad5"},
{sf::Keyboard::Numpad6, "Numpad6"},
{sf::Keyboard::Numpad7, "Numpad7"},
{sf::Keyboard::Numpad8, "Numpad8"},
{sf::Keyboard::Numpad9, "Numpad9"},
{sf::Keyboard::F1, "F1"},
{sf::Keyboard::F2, "F2"},
{sf::Keyboard::F3, "F3"},
{sf::Keyboard::F4, "F4"},
{sf::Keyboard::F5, "F5"},
{sf::Keyboard::F6, "F6"},
{sf::Keyboard::F7, "F7"},
{sf::Keyboard::F8, "F8"},
{sf::Keyboard::F9, "F9"},
{sf::Keyboard::F10, "F10"},
{sf::Keyboard::F11, "F11"},
{sf::Keyboard::F12, "F12"},
{sf::Keyboard::F13, "F13"},
{sf::Keyboard::F14, "F14"},
{sf::Keyboard::F15, "F15"},
{sf::Keyboard::Pause, "Pause"}
}
};
const std::unordered_map<std::string, sf::Keyboard::Key> string_to_keyboard{
{
{"Unknown", sf::Keyboard::Unknown},
{"A", sf::Keyboard::A},
{"B", sf::Keyboard::B},
{"C", sf::Keyboard::C},
{"D", sf::Keyboard::D},
{"E", sf::Keyboard::E},
{"F", sf::Keyboard::F},
{"G", sf::Keyboard::G},
{"H", sf::Keyboard::H},
{"I", sf::Keyboard::I},
{"J", sf::Keyboard::J},
{"K", sf::Keyboard::K},
{"L", sf::Keyboard::L},
{"M", sf::Keyboard::M},
{"N", sf::Keyboard::N},
{"O", sf::Keyboard::O},
{"P", sf::Keyboard::P},
{"Q", sf::Keyboard::Q},
{"R", sf::Keyboard::R},
{"S", sf::Keyboard::S},
{"T", sf::Keyboard::T},
{"U", sf::Keyboard::U},
{"V", sf::Keyboard::V},
{"W", sf::Keyboard::W},
{"X", sf::Keyboard::X},
{"Y", sf::Keyboard::Y},
{"Z", sf::Keyboard::Z},
{"Num0", sf::Keyboard::Num0},
{"Num1", sf::Keyboard::Num1},
{"Num2", sf::Keyboard::Num2},
{"Num3", sf::Keyboard::Num3},
{"Num4", sf::Keyboard::Num4},
{"Num5", sf::Keyboard::Num5},
{"Num6", sf::Keyboard::Num6},
{"Num7", sf::Keyboard::Num7},
{"Num8", sf::Keyboard::Num8},
{"Num9", sf::Keyboard::Num9},
{"Escape", sf::Keyboard::Escape},
{"LControl", sf::Keyboard::LControl},
{"LShift", sf::Keyboard::LShift},
{"LAlt", sf::Keyboard::LAlt},
{"LSystem", sf::Keyboard::LSystem},
{"RControl", sf::Keyboard::RControl},
{"RShift", sf::Keyboard::RShift},
{"RAlt", sf::Keyboard::RAlt},
{"RSystem", sf::Keyboard::RSystem},
{"Menu", sf::Keyboard::Menu},
{"LBracket", sf::Keyboard::LBracket},
{"RBracket", sf::Keyboard::RBracket},
{"Semicolon", sf::Keyboard::Semicolon},
{"Comma", sf::Keyboard::Comma},
{"Period", sf::Keyboard::Period},
{"Quote", sf::Keyboard::Quote},
{"Slash", sf::Keyboard::Slash},
{"Backslash", sf::Keyboard::Backslash},
{"Tilde", sf::Keyboard::Tilde},
{"Equal", sf::Keyboard::Equal},
{"Hyphen", sf::Keyboard::Hyphen},
{"Space", sf::Keyboard::Space},
{"Enter", sf::Keyboard::Enter},
{"Backspace", sf::Keyboard::Backspace},
{"Tab", sf::Keyboard::Tab},
{"PageUp", sf::Keyboard::PageUp},
{"PageDown", sf::Keyboard::PageDown},
{"End", sf::Keyboard::End},
{"Home", sf::Keyboard::Home},
{"Insert", sf::Keyboard::Insert},
{"Delete", sf::Keyboard::Delete},
{"Add", sf::Keyboard::Add},
{"Subtract", sf::Keyboard::Subtract},
{"Multiply", sf::Keyboard::Multiply},
{"Divide", sf::Keyboard::Divide},
{"Left", sf::Keyboard::Left},
{"Right", sf::Keyboard::Right},
{"Up", sf::Keyboard::Up},
{"Down", sf::Keyboard::Down},
{"Numpad0", sf::Keyboard::Numpad0},
{"Numpad1", sf::Keyboard::Numpad1},
{"Numpad2", sf::Keyboard::Numpad2},
{"Numpad3", sf::Keyboard::Numpad3},
{"Numpad4", sf::Keyboard::Numpad4},
{"Numpad5", sf::Keyboard::Numpad5},
{"Numpad6", sf::Keyboard::Numpad6},
{"Numpad7", sf::Keyboard::Numpad7},
{"Numpad8", sf::Keyboard::Numpad8},
{"Numpad9", sf::Keyboard::Numpad9},
{"F1", sf::Keyboard::F1},
{"F2", sf::Keyboard::F2},
{"F3", sf::Keyboard::F3},
{"F4", sf::Keyboard::F4},
{"F5", sf::Keyboard::F5},
{"F6", sf::Keyboard::F6},
{"F7", sf::Keyboard::F7},
{"F8", sf::Keyboard::F8},
{"F9", sf::Keyboard::F9},
{"F10", sf::Keyboard::F10},
{"F11", sf::Keyboard::F11},
{"F12", sf::Keyboard::F12},
{"F13", sf::Keyboard::F13},
{"F14", sf::Keyboard::F14},
{"F15", sf::Keyboard::F15},
{"Pause", sf::Keyboard::Pause}
}
};
struct MappableKeyToString {
public:
std::string operator() (const sf::Keyboard::Key& k) {
return "Keyboard::"+keyboard_to_string.at(k);
};
std::string operator() (const Data::JoystickButton& jbe) {
return "Joystick::"+std::to_string(jbe.joystickId)+"_"+std::to_string(jbe.button);
};
};
std::string to_string(const MappableKey& mk);
MappableKey from_string(const std::string& s);
}

103
src/Data/Preferences.cpp Normal file
View File

@ -0,0 +1,103 @@
#include "Preferences.hpp"
#include <iostream>
#include <fstream>
namespace Data {
void to_json(nlohmann::json& j, const Screen& s) {
j = nlohmann::json{
{"width", s.width},
{"height", s.height},
{"fullscreen", s.fullscreen}
};
}
void from_json(const nlohmann::json& j, Screen& s) {
j.at("width").get_to(s.width);
j.at("height").get_to(s.height);
j.at("fullscreen").get_to(s.fullscreen);
}
void to_json(nlohmann::json& j, const Layout& l) {
j = nlohmann::json{
{"panel_size", l.panel_size},
{"panel_spacing", l.panel_spacing},
{"ribbon_x", l.ribbon_x},
{"ribbon_y", l.ribbon_y},
{"upper_part_height", l.upper_part_height}
};
}
void from_json(const nlohmann::json& j, Layout& l) {
j.at("panel_size").get_to(l.panel_size);
j.at("panel_spacing").get_to(l.panel_spacing);
j.at("ribbon_x").get_to(l.ribbon_x);
j.at("ribbon_y").get_to(l.ribbon_y);
j.at("upper_part_height").get_to(l.upper_part_height);
}
void to_json(nlohmann::json& j, const Options& o) {
j = nlohmann::json{{"marker", o.marker}};
}
void from_json(const nlohmann::json& j, Options& o) {
j.at("marker").get_to(o.marker);
}
// RAII style class which loads preferences from the dedicated file when constructed and saves them when destructed
Preferences::Preferences(const ghc::filesystem::path& t_jujube_path) :
screen(),
layout(),
options(),
key_mapping(),
jujube_path(t_jujube_path)
{
auto path = jujube_path/"data"/"preferences.json";
if (ghc::filesystem::exists(path)) {
std::ifstream prefs_file;
prefs_file.open(path);
nlohmann::json j;
try {
prefs_file >> j;
j.get_to(*this);
} catch (const std::exception& e) {
std::cerr << "Error while loading data/preferences.json : " << e.what() << std::endl;
std::cerr << "Using fallback preferences instead" << std::endl;
return;
}
key_mapping = KeyMapping{key_mapping.m_button_to_key};
}
}
Preferences::~Preferences() {
auto data_folder = jujube_path/"data";
if (not ghc::filesystem::exists(data_folder)) {
ghc::filesystem::create_directory(data_folder);
}
if (not ghc::filesystem::is_directory(data_folder)) {
std::cerr << "Can't create data folder to save preferences, a file named 'data' exists" << std::endl;
}
std::ofstream preferences_file;
preferences_file.open(data_folder/"preferences.json", std::ofstream::trunc | std::ofstream::out);
nlohmann::json j = *this;
preferences_file << j.dump(4);
}
void to_json(nlohmann::json& j, const Preferences& p) {
j = nlohmann::json{
{"screen", p.screen},
{"layout", p.layout},
{"options", p.options},
{"key_mapping", p.key_mapping}
};
}
void from_json(const nlohmann::json& j, Preferences& p) {
j.at("screen").get_to(p.screen);
j.at("layout").get_to(p.layout);
j.at("options").get_to(p.options);
j.at("key_mapping").get_to(p.key_mapping);
}
}

View File

@ -1,12 +1,9 @@
#pragma once
#include <cstddef>
#include <fstream>
#include <cereal/archives/json.hpp>
#include <cereal/types/string.hpp>
#include <cereal/types/unordered_map.hpp>
#include <ghc/filesystem.hpp>
#include <nlohmann/json.hpp>
#include <SFML/System.hpp>
#include "Buttons.hpp"
@ -19,17 +16,11 @@ namespace Data {
std::size_t width = 768;
std::size_t height = 1360;
bool fullscreen = false;
template<class Archive>
void serialize(Archive & archive) {
archive(
CEREAL_NVP(width),
CEREAL_NVP(height),
CEREAL_NVP(fullscreen)
);
}
};
void to_json(nlohmann::json& j, const Screen& s);
void from_json(const nlohmann::json& j, Screen& s);
struct Layout {
float panel_size = 160.f / 768.f;
float panel_spacing = (112.f / 3.f) / 768.f;
@ -42,30 +33,18 @@ namespace Data {
float big_level_x = 656.f / 768.f;
float big_level_y = 30.f / 768.f;
float upper_part_height = 464.f / 768.f;
template<class Archive>
void serialize(Archive & archive) {
archive(
CEREAL_NVP(panel_size),
CEREAL_NVP(panel_spacing),
CEREAL_NVP(ribbon_x),
CEREAL_NVP(ribbon_y),
CEREAL_NVP(upper_part_height)
);
}
};
void to_json(nlohmann::json& j, const Layout& l);
void from_json(const nlohmann::json& j, Layout& l);
struct Options {
std::string marker;
template<class Archive>
void serialize(Archive & archive) {
archive(
CEREAL_NVP(marker)
);
}
};
void to_json(nlohmann::json& j, const Options& o);
void from_json(const nlohmann::json& j, Options& o);
// RAII style class which loads preferences from the dedicated file when constructed and saves them when destructed
struct Preferences {
Screen screen;
@ -74,62 +53,13 @@ namespace Data {
KeyMapping key_mapping;
ghc::filesystem::path jujube_path;
Preferences(const ghc::filesystem::path& t_jujube_path) :
screen(),
layout(),
jujube_path(t_jujube_path)
{
load_from_file();
}
~Preferences() {
save_to_file();
}
void load_from_file() {
auto path = jujube_path/"data"/"preferences.json";
if (ghc::filesystem::exists(path)) {
std::ifstream prefs_file;
prefs_file.open(path);
try {
cereal::JSONInputArchive archive{prefs_file};
serialize(archive);
key_mapping = KeyMapping{key_mapping.m_button_to_key};
} catch (const std::exception& e) {
std::cerr << "Error while loading data/preferences.json : " << e.what() << std::endl;
std::cerr << "Using fallback preferences instead" << std::endl;
}
}
};
void save_to_file() {
auto data_folder = jujube_path/"data";
if (not ghc::filesystem::exists(data_folder)) {
ghc::filesystem::create_directory(data_folder);
}
if (not ghc::filesystem::is_directory(data_folder)) {
std::cerr << "Can't create data folder to save preferences, a file named 'data' exists" << std::endl;
}
std::ofstream preferences_file;
preferences_file.open(data_folder/"preferences.json", std::ofstream::trunc | std::ofstream::out);
{
cereal::JSONOutputArchive archive{preferences_file};
serialize(archive);
}
}
template<class Archive>
void serialize(Archive & archive) {
archive(
CEREAL_NVP(screen),
CEREAL_NVP(layout),
CEREAL_NVP(options),
cereal::make_nvp("key_mapping", key_mapping.m_button_to_key)
);
}
Preferences(const ghc::filesystem::path& t_jujube_path);
~Preferences();
};
void to_json(nlohmann::json& j, const Preferences& p);
void from_json(const nlohmann::json& j, Preferences& p);
struct HoldsPreferences {
HoldsPreferences(Preferences& t_preferences) : preferences(t_preferences) {};
float get_screen_width() const {return static_cast<float>(preferences.screen.width);};

View File

@ -1,7 +1,6 @@
#include <iostream>
#include <SFML/Graphics.hpp>
#include <cereal/archives/json.hpp>
#include <whereami/whereami++.hpp>
#include "Data/Song.hpp"

View File

@ -7,13 +7,52 @@
#include <iostream>
#include <sstream>
#include <cereal/types/string.hpp>
#include <cereal/archives/json.hpp>
namespace fs = ghc::filesystem;
namespace Resources {
void to_json(nlohmann::json& j, const MarkerAnimationMetadata& mam) {
j = nlohmann::json{
{"sprite_sheet", mam.sprite_sheet.string()},
{"count", mam.count},
{"columns", mam.columns},
{"rows", mam.rows}
};
}
void from_json(const nlohmann::json& j, MarkerAnimationMetadata& mam) {
mam.sprite_sheet = ghc::filesystem::path{j.at("sprite_sheet").get<std::string>()};
j.at("count").get_to(mam.count);
j.at("columns").get_to(mam.columns);
j.at("rows").get_to(mam.rows);
}
void to_json(nlohmann::json& j, const MarkerMetadata& mm) {
j = nlohmann::json{
{"name", mm.name},
{"size", mm.size},
{"fps", mm.fps},
{"approach", mm.approach},
{"miss", mm.miss},
{"early", mm.early},
{"good", mm.good},
{"great", mm.great},
{"perfect", mm.perfect}
};
}
void from_json(const nlohmann::json& j, MarkerMetadata& mm) {
j.at("name").get_to(mm.name);
j.at("size").get_to(mm.size);
j.at("fps").get_to(mm.fps);
j.at("approach").get_to(mm.approach);
j.at("miss").get_to(mm.miss);
j.at("early").get_to(mm.early);
j.at("good").get_to(mm.good);
j.at("great").get_to(mm.great);
j.at("perfect").get_to(mm.perfect);
}
Marker::Marker(const fs::path& marker_folder) :
m_folder(marker_folder),
m_metadata(),
@ -31,10 +70,9 @@ namespace Resources {
throw std::invalid_argument("Marker folder ( "+m_folder.string()+" ) has no marker.json file");
}
std::ifstream marker_json{m_folder/"marker.json"};
{
cereal::JSONInputArchive archive{marker_json};
m_metadata.serialize(archive);
}
nlohmann::json j;
marker_json >> j;
j.get_to(m_metadata);
load_and_check(m_approach, m_metadata.approach);
load_and_check(m_miss, m_metadata.miss);
load_and_check(m_early, m_metadata.early);

View File

@ -3,12 +3,10 @@
#include <string>
#include <map>
#include <cereal/types/string.hpp>
#include <ghc/filesystem.hpp>
#include <nlohmann/json.hpp>
#include <SFML/Graphics.hpp>
#include "../Toolkit/ExtraCerealTypes/GHCFilesystemPath.hpp"
namespace Resources {
enum class MarkerAnimation {
APPROACH,
@ -24,18 +22,11 @@ namespace Resources {
std::size_t count; // how many sprites total on the sheet
std::size_t columns; // how many horizontally
std::size_t rows; // how many vertically
template<class Archive>
void serialize(Archive& archive) {
archive(
CEREAL_NVP(sprite_sheet),
CEREAL_NVP(count),
CEREAL_NVP(columns),
CEREAL_NVP(rows)
);
}
};
void to_json(nlohmann::json& j, const MarkerAnimationMetadata& mam);
void from_json(const nlohmann::json& j, MarkerAnimationMetadata& mam);
// Represents what's held in marker.json
struct MarkerMetadata {
std::string name;
@ -47,23 +38,11 @@ namespace Resources {
MarkerAnimationMetadata good;
MarkerAnimationMetadata great;
MarkerAnimationMetadata perfect;
template<class Archive>
void serialize(Archive & archive) {
archive(
CEREAL_NVP(name),
CEREAL_NVP(size),
CEREAL_NVP(fps),
CEREAL_NVP(approach),
CEREAL_NVP(miss),
CEREAL_NVP(early),
CEREAL_NVP(good),
CEREAL_NVP(great),
CEREAL_NVP(perfect)
);
}
};
void to_json(nlohmann::json& j, const MarkerMetadata& mm);
void from_json(const nlohmann::json& j, MarkerMetadata& mm);
struct Marker {
explicit Marker(const ghc::filesystem::path& marker_folder);
std::optional<sf::Sprite> get_sprite(const MarkerAnimation& state, float seconds) const;

View File

@ -3,6 +3,7 @@
#include <cmath>
#include <magic_enum.hpp>
#include <SFML/Graphics.hpp>
#include "../../../Toolkit/HSL.hpp"
#include "../../../Toolkit/NormalizedOrigin.hpp"
@ -60,7 +61,7 @@ namespace MusicSelect {
MappingPreview::MappingPreview(
SharedResources& t_resources,
const std::unordered_map<sf::Keyboard::Key, Data::Button>& t_key_to_button
const std::unordered_map<Data::MappableKey, Data::Button>& t_key_to_button
) :
HoldsSharedResources(t_resources),
key_to_button(t_key_to_button)
@ -88,7 +89,7 @@ namespace MusicSelect {
auto text_size = square_size*0.33f;
for (auto &&[key, button] : key_to_button) {
auto coord = Data::button_to_coords(button);
std::string key_name{magic_enum::enum_name(key)};
std::string key_name = Data::to_string(key);
key_label.setString(key_name);
key_label.setCharacterSize(static_cast<unsigned int>(text_size));
Toolkit::set_local_origin_normalized(key_label, 0.5f, 0.5f);

View File

@ -6,6 +6,7 @@
#include <SFML/System.hpp>
#include <SFML/Graphics.hpp>
#include "../../../Data/MappableKeys.hpp"
#include "../../../Toolkit/AffineTransform.hpp"
#include "../SharedResources.hpp"
#include "OptionPage.hpp"
@ -36,10 +37,10 @@ namespace MusicSelect {
class MappingPreview final : public sf::Drawable, public sf::Transformable, public HoldsSharedResources {
public:
MappingPreview(SharedResources& t_resources, const std::unordered_map<sf::Keyboard::Key, Data::Button>& t_key_to_button);
MappingPreview(SharedResources& t_resources, const std::unordered_map<Data::MappableKey, Data::Button>& t_key_to_button);
private:
void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
const std::unordered_map<sf::Keyboard::Key, Data::Button>& key_to_button;
const std::unordered_map<Data::MappableKey, Data::Button>& key_to_button;
mutable sf::RectangleShape square;
mutable sf::Text key_label;
};
@ -60,7 +61,7 @@ namespace MusicSelect {
mutable sf::Text confirm_text_top;
mutable sf::Text confirm_text_bottom;
mutable sf::Text big_number;
std::unordered_map<sf::Keyboard::Key, Data::Button> m_key_to_button;
std::unordered_map<Data::MappableKey, Data::Button> m_key_to_button;
mutable PressHere press_here_panel;
mutable AlreadyMapped already_mapped_panel;
mutable MappingPreview mapping_preview;

View File

@ -1,20 +0,0 @@
#pragma once
#include <string>
#include <cereal/cereal.hpp>
#include <ghc/filesystem.hpp>
namespace ghc {
namespace filesystem {
template <class Archive>
std::string save_minimal(const Archive &, const path& p) {
return p.string();
}
template <class Archive>
void load_minimal(const Archive &, path& p, const std::string& value) {
p = path{value};
}
}
}

View File

@ -1,110 +0,0 @@
#include <cassert>
#include <cstdio>
#include <fstream>
#include <string>
#include <unordered_map>
#include <cereal/archives/json.hpp>
#include <cereal/types/string.hpp>
#include <cereal/types/unordered_map.hpp>
#include <magic_enum.hpp>
enum class Keys {
blue,
green,
yellow,
orange,
red,
};
enum Values {
YES,
NO,
MAYBE
};
namespace cereal {
template <
class Archive,
traits::EnableIf<traits::is_text_archive<Archive>::value> = traits::sfinae
> inline
void save(Archive& ar, const std::unordered_map<Keys, Values>& map ) {
for(auto&& i : magic_enum::enum_entries<Keys>()) {
auto it = map.find(i.first);
if (it != map.end()) {
ar(
cereal::make_nvp(
std::string{i.second},
std::string{magic_enum::enum_name(it->second)}
)
);
}
}
}
template <
class Archive,
traits::EnableIf<traits::is_text_archive<Archive>::value> = traits::sfinae
> inline
void load(Archive& ar, std::unordered_map<Keys, Values>& map ) {
map.clear();
while(true) {
const auto namePtr = ar.getNodeName();
if (not namePtr) {
break;
}
std::string key_str = namePtr;
std::string value_str;
ar(value_str);
auto key = magic_enum::enum_cast<Keys>(key_str);
if (not key.has_value()) {
throw std::runtime_error("Invalid Key : "+key_str);
}
auto value = magic_enum::enum_cast<Values>(value_str);
if (not value.has_value()) {
throw std::runtime_error("Invalid Value : "+value_str);
}
map.emplace(*key, *value);
}
}
} // namespace cereal
struct MyThingWithAnUnorderedMap {
std::unordered_map<Keys, Values> m_map;
};
struct Indirect {
MyThingWithAnUnorderedMap m;
template<class Archive>
void serialize(Archive & archive) {
archive(
cereal::make_nvp("map", m.m_map)
);
}
};
int main(int argc, char const *argv[]) {
Indirect ref;
ref.m.m_map = {{Keys::blue, YES}, {Keys::green, MAYBE}};
{
std::ofstream file;
file.open("cereal_test_specialize_enums.json", std::ofstream::trunc | std::ofstream::out);
cereal::JSONOutputArchive archive{file};
archive(ref);
}
Indirect test;
{
std::ifstream file;
file.open("cereal_test_specialize_enums.json", std::ifstream::in);
cereal::JSONInputArchive archive{file};
archive(test);
}
assert((ref.m.m_map == test.m.m_map));
std::remove("cereal_test_specialize_enums.json");
return 0;
}

View File

@ -1,30 +0,0 @@
#include <cassert>
#include <fstream>
#include <string>
#include <cereal/archives/json.hpp>
#include <cereal/types/string.hpp>
#include <ghc/filesystem.hpp>
#include "../src/Toolkit/ExtraCerealTypes/GHCFilesystemPath.hpp"
int main(int argc, char const *argv[]) {
ghc::filesystem::path ref{"hihi/haha/hoho.jpg"};
{
std::ofstream file;
file.open("cereal_test_specialize_path.json", std::ofstream::trunc | std::ofstream::out);
cereal::JSONOutputArchive archive{file};
archive(ref);
}
ghc::filesystem::path test;
{
std::ifstream file;
file.open("cereal_test_specialize_path.json", std::ifstream::in);
cereal::JSONInputArchive archive{file};
archive(test);
}
assert((ref == test));
//std::remove("cereal_test_specialize_path.json");
return 0;
}

View File

@ -1,60 +0,0 @@
#include <cassert>
#include <cstdio>
#include <fstream>
#include <string>
#include <unordered_map>
#include <cereal/archives/json.hpp>
#include <cereal/types/string.hpp>
#include <cereal/types/unordered_map.hpp>
enum class Keys {
blue,
green,
yellow,
orange,
red,
};
enum Values {
YES,
NO,
MAYBE
};
struct MyThingWithAnUnorderedMap {
std::unordered_map<Keys, Values> m_map;
};
struct Indirect {
MyThingWithAnUnorderedMap m;
template<class Archive>
void serialize(Archive & archive) {
archive(
cereal::make_nvp("map", m.m_map)
);
}
};
int main(int argc, char const *argv[]) {
Indirect ref;
ref.m.m_map = {{Keys::blue, YES}, {Keys::green, MAYBE}};
{
std::ofstream file;
file.open("cereal_test_unordered_map.json", std::ofstream::trunc | std::ofstream::out);
cereal::JSONOutputArchive archive{file};
archive(ref);
}
Indirect test;
{
std::ifstream file;
file.open("cereal_test_unordered_map.json", std::ifstream::in);
cereal::JSONInputArchive archive{file};
archive(test);
}
assert((ref.m.m_map == test.m.m_map));
std::remove("cereal_test_specialize_enums.json");
return 0;
}

View File

@ -1,8 +1,4 @@
test_files = [
'cereal_specialize_enums.cpp',
'cereal_unordered_map.cpp',
'cereal_specialize_path.cpp'
]
test_files = []
foreach test_file : test_files
test_executable = executable(