#include "HighScoreManager.hxx" #include <SDL.h> #include <algorithm> #include <filesystem> #include <fstream> #include <numeric> namespace fs = std::filesystem; HighScoreManager::HighScoreManager() { data_dir_ = SDL_GetPrefPath("draconic-bytes", "snake"); fs::create_directories(data_dir_); load(); } void HighScoreManager::load() { auto const filename = fs::path{data_dir_}/"highscore.dat"; std::ifstream input{filename, std::ios::binary}; if (!input) { SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Missing high score file: %s", filename.c_str()); return; } std::size_t count; input.read(std::bit_cast<char*>(&count), sizeof(std::size_t)); high_score_.scores_.resize(count); for (std::size_t n = 0; n<count; ++n) { std::size_t len; input.read(std::bit_cast<char*>(&len), sizeof(std::size_t)); high_score_.scores_[n].player_name_.resize(len); input.read(high_score_.scores_[n].player_name_.data(), static_cast<std::streamsize>(len)); input.read(std::bit_cast<char*>(&(high_score_.scores_[n].points_)), sizeof(unsigned)); } std::ranges::stable_sort(high_score_.scores_, std::greater<>{}, &Score::points_); if (high_score_.scores_.empty()) { SDL_Log("No existing high scores."); } else { SDL_Log("Loaded high scores:"); for (auto const& score: high_score_.scores_) { SDL_Log(" - %s: %u", score.player_name_.c_str(), score.points_); } } } void HighScoreManager::save() const { auto const filename = fs::path{data_dir_}/"highscore.dat"; std::ofstream output{filename, std::ios::binary}; if (!output) { SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed saving high scores to file %s", filename.c_str()); return; } auto const count = high_score_.scores_.size(); output.write(std::bit_cast<char const*>(&count), sizeof(std::size_t)); for (auto const& score: high_score_.scores_) { auto const len = score.player_name_.length(); output.write(std::bit_cast<char const*>(&len), sizeof(std::size_t)); output.write(score.player_name_.data(), static_cast<std::streamsize>(score.player_name_.length())); output.write(std::bit_cast<char const*>(&score.points_), sizeof(unsigned)); } if (high_score_.scores_.empty()) { SDL_Log("No high scores saved."); } else { SDL_Log("Saved high scores:"); for (auto const& score: high_score_.scores_) { SDL_Log(" - %s: %u", score.player_name_.c_str(), score.points_); } } } void HighScoreManager::set_new_score(unsigned int score) { if (high_score_.scores_.size()<MAX_SCORES || high_score_.scores_.back().points_<score) { new_score_ = score; } else { new_score_.reset(); } } bool HighScoreManager::has_new_score() const { return new_score_.has_value(); } unsigned HighScoreManager::get_new_score() const { return new_score_.value_or(0u); } void HighScoreManager::provide_name_for_new_score(std::string const& name) { if (new_score_.has_value()) { high_score_.scores_.push_back({name, new_score_.value()}); new_score_.reset(); std::ranges::stable_sort(high_score_.scores_, std::greater<>{}, &Score::points_); if (high_score_.scores_.size()>MAX_SCORES) { high_score_.scores_.resize(MAX_SCORES); } save(); } } HighScoreManager& HighScoreManager::instance() { static HighScoreManager manager; return manager; } std::vector<Score> const& HighScoreManager::get_scores() const { return high_score_.scores_; }