#include "save_load_service.h" #include "game/core/serialization.h" #include "game/core/world.h" #include "save_storage.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Game::Systems { SaveLoadService::SaveLoadService() { ensure_saves_directory_exists(); m_storage = std::make_unique(get_database_path()); QString init_error; if (!m_storage->initialize(&init_error)) { m_last_error = init_error; qWarning() << "SaveLoadService: failed to initialize storage" << init_error; } } SaveLoadService::~SaveLoadService() = default; auto SaveLoadService::get_saves_directory() -> QString { QString const saves_path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); return saves_path + "/saves"; } auto SaveLoadService::get_database_path() -> QString { return get_saves_directory() + QStringLiteral("/saves.sqlite"); } void SaveLoadService::ensure_saves_directory_exists() { QString const saves_dir = get_saves_directory(); QDir const dir; if (!dir.exists(saves_dir)) { dir.mkpath(saves_dir); } } auto SaveLoadService::save_game_to_slot(Engine::Core::World &world, const QString &slot_name, const QString &title, const QString &map_name, const QJsonObject &metadata, const QByteArray &screenshot) -> bool { qInfo() << "Saving game to slot:" << slot_name; try { if (!m_storage) { m_last_error = QStringLiteral("Save storage unavailable"); qWarning() << m_last_error; return false; } QJsonDocument const world_doc = Engine::Core::Serialization::serialize_world(&world); const QByteArray world_bytes = world_doc.toJson(QJsonDocument::Compact); QJsonObject combined_metadata = metadata; combined_metadata["slotName"] = slot_name; combined_metadata["title"] = title; combined_metadata["timestamp"] = QDateTime::currentDateTimeUtc().toString(Qt::ISODateWithMs); if (!combined_metadata.contains("map_name")) { combined_metadata["map_name"] = map_name.isEmpty() ? QStringLiteral("Unknown Map") : map_name; } combined_metadata["version"] = QStringLiteral("1.0"); QString storage_error; if (!m_storage->save_slot(slot_name, title, combined_metadata, world_bytes, screenshot, &storage_error)) { m_last_error = storage_error; qWarning() << "SaveLoadService: failed to persist slot" << storage_error; return false; } m_last_metadata = combined_metadata; m_last_title = title; m_last_screenshot = screenshot; m_last_error.clear(); return true; } catch (const std::exception &e) { m_last_error = QString("Exception while saving game to slot: %1").arg(e.what()); qWarning() << m_last_error; return false; } } auto SaveLoadService::load_game_from_slot(Engine::Core::World &world, const QString &slot_name) -> bool { qInfo() << "Loading game from slot:" << slot_name; try { if (!m_storage) { m_last_error = QStringLiteral("Save storage unavailable"); qWarning() << m_last_error; return false; } QByteArray world_bytes; QJsonObject metadata; QByteArray screenshot; QString title; QString load_error; if (!m_storage->load_slot(slot_name, world_bytes, metadata, screenshot, title, &load_error)) { m_last_error = load_error; qWarning() << "SaveLoadService: failed to load slot" << load_error; return false; } QJsonParseError parse_error{}; QJsonDocument const doc = QJsonDocument::fromJson(world_bytes, &parse_error); if (parse_error.error != QJsonParseError::NoError || doc.isNull()) { m_last_error = QStringLiteral("Corrupted save data for slot '%1': %2") .arg(slot_name, parse_error.errorString()); qWarning() << m_last_error; return false; } world.clear(); Engine::Core::Serialization::deserialize_world(&world, doc); m_last_metadata = metadata; m_last_title = title; m_last_screenshot = screenshot; m_last_error.clear(); return true; } catch (const std::exception &e) { m_last_error = QString("Exception while loading game from slot: %1").arg(e.what()); qWarning() << m_last_error; return false; } } auto SaveLoadService::get_save_slots() const -> QVariantList { if (!m_storage) { return {}; } QString list_error; QVariantList slot_list = m_storage->list_slots(&list_error); if (!list_error.isEmpty()) { m_last_error = list_error; qWarning() << "SaveLoadService: failed to enumerate slots" << list_error; } else { m_last_error.clear(); } return slot_list; } auto SaveLoadService::delete_save_slot(const QString &slot_name) -> bool { qInfo() << "Deleting save slot:" << slot_name; if (!m_storage) { m_last_error = QStringLiteral("Save storage unavailable"); qWarning() << m_last_error; return false; } QString delete_error; if (!m_storage->delete_slot(slot_name, &delete_error)) { m_last_error = delete_error; qWarning() << "SaveLoadService: failed to delete slot" << delete_error; return false; } m_last_error.clear(); return true; } auto SaveLoadService::list_campaigns(QString *out_error) -> QVariantList { if (!m_storage) { if (out_error != nullptr) { *out_error = "Storage not initialized"; } return {}; } return m_storage->list_campaigns(out_error); } auto SaveLoadService::get_campaign_progress( const QString &campaign_id, QString *out_error) const -> QVariantMap { if (!m_storage) { if (out_error != nullptr) { *out_error = "Storage not initialized"; } return {}; } return m_storage->get_campaign_progress(campaign_id, out_error); } auto SaveLoadService::mark_campaign_completed(const QString &campaign_id, QString *out_error) -> bool { if (!m_storage) { if (out_error != nullptr) { *out_error = "Storage not initialized"; } return false; } return m_storage->mark_campaign_completed(campaign_id, out_error); } auto SaveLoadService::save_mission_result( const QString &mission_id, const QString &mode, const QString &campaign_id, bool completed, const QString &result, const QString &difficulty, float completion_time, QString *out_error) -> bool { if (!m_storage) { if (out_error != nullptr) { *out_error = "Storage not initialized"; } return false; } return m_storage->save_mission_result(mission_id, mode, campaign_id, completed, result, difficulty, completion_time, out_error); } auto SaveLoadService::get_mission_progress( const QString &mission_id, QString *out_error) const -> QVariantMap { if (!m_storage) { if (out_error != nullptr) { *out_error = "Storage not initialized"; } return {}; } return m_storage->get_mission_progress(mission_id, out_error); } auto SaveLoadService::get_campaign_mission_progress( const QString &campaign_id, QString *out_error) const -> QVariantList { if (!m_storage) { if (out_error != nullptr) { *out_error = "Storage not initialized"; } return {}; } return m_storage->get_campaign_mission_progress(campaign_id, out_error); } auto SaveLoadService::unlock_next_campaign_mission( const QString &campaign_id, const QString &completed_mission_id, QString *out_error) -> bool { if (!m_storage) { if (out_error != nullptr) { *out_error = "Storage not initialized"; } return false; } return m_storage->unlock_next_mission(campaign_id, completed_mission_id, out_error); } SaveLoadService *SaveLoadService::instance() { static SaveLoadService instance; return &instance; } void SaveLoadService::open_settings() { qInfo() << "Open settings requested"; } void SaveLoadService::exit_game() { qInfo() << "Exit game requested"; QCoreApplication::quit(); } } // namespace Game::Systems