7#include "absl/strings/str_cat.h"
8#include "absl/strings/str_format.h"
14#include "nlohmann/json.hpp"
26 for (
size_t i = 0; i < data.size(); ++i) {
27 hash = hash * 31 + data[i];
29 return absl::StrFormat(
"%08x", hash);
34 auto now = std::chrono::system_clock::now();
35 auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
36 now.time_since_epoch())
38 return absl::StrFormat(
"snap_%lld", ms);
42 return std::chrono::duration_cast<std::chrono::milliseconds>(
43 std::chrono::system_clock::now().time_since_epoch())
54 : rom_(rom), last_backup_time_(0) {}
64 auto initial_result =
CreateSnapshot(
"Initial state",
"system",
true);
66 if (!initial_result.ok()) {
67 return initial_result.status();
75 const std::string& description,
const std::string& creator,
78 return absl::FailedPreconditionError(
"ROM not loaded");
82 std::vector<uint8_t> rom_data(
rom_->
size());
89 snapshot.
timestamp = GetCurrentTimestamp();
90 snapshot.
rom_hash = ComputeHash(rom_data);
100 snapshot.
rom_data = std::move(rom_data);
105 snapshot.metadata = nlohmann::json::object();
106 snapshot.metadata[
"size"] =
rom_->
size();
107 snapshot.metadata[
"auto_backup"] = !is_checkpoint;
123 const std::string& snapshot_id) {
126 return absl::NotFoundError(
127 absl::StrCat(
"Snapshot not found: ", snapshot_id));
131 return absl::FailedPreconditionError(
"ROM not loaded");
137 std::vector<uint8_t> rom_data;
145 if (rom_data.size() !=
rom_->
size()) {
146 return absl::DataLossError(
"Snapshot size mismatch");
150 auto backup_result =
CreateSnapshot(
"Pre-restore backup",
"system",
false);
152 if (!backup_result.ok()) {
153 return absl::InternalError(
"Failed to create pre-restore backup");
161 return absl::OkStatus();
165 const std::string& snapshot_id) {
168 return absl::NotFoundError(
"Snapshot not found");
171 it->second.is_safe_point =
true;
172 return absl::OkStatus();
176 bool safe_points_only)
const {
177 std::vector<RomSnapshot> result;
179 for (
const auto& [
id, snapshot] :
snapshots_) {
180 if (!safe_points_only || snapshot.is_safe_point) {
181 result.push_back(snapshot);
186 std::sort(result.begin(), result.end(),
188 return a.timestamp > b.timestamp;
195 const std::string& snapshot_id)
const {
198 return absl::NotFoundError(
"Snapshot not found");
206 return absl::NotFoundError(
"Snapshot not found");
210 if (it->second.is_safe_point) {
211 return absl::FailedPreconditionError(
"Cannot delete safe point");
215 return absl::OkStatus();
224 return absl::FailedPreconditionError(
"ROM not loaded");
228 std::vector<uint8_t> current_data(
rom_->
size());
230 std::string current_hash = ComputeHash(current_data);
234 if (!integrity_status.ok()) {
251 if (snapshots.empty()) {
252 return absl::NotFoundError(
"No safe points available for recovery");
264 std::vector<uint8_t> data(
rom_->
size());
266 return ComputeHash(data);
273 std::vector<std::pair<int64_t, std::string>> auto_backups;
274 for (
const auto& [
id, snapshot] :
snapshots_) {
275 if (!snapshot.is_safe_point && !snapshot.is_checkpoint) {
276 auto_backups.push_back({snapshot.timestamp,
id});
281 std::sort(auto_backups.begin(), auto_backups.end());
285 snapshots_.erase(auto_backups.front().second);
286 auto_backups.erase(auto_backups.begin());
291 !auto_backups.empty()) {
292 snapshots_.erase(auto_backups.front().second);
293 auto_backups.erase(auto_backups.begin());
296 return absl::OkStatus();
303 for (
const auto& [
id, snapshot] :
snapshots_) {
304 if (snapshot.is_safe_point)
306 if (snapshot.is_checkpoint)
308 if (!snapshot.is_checkpoint)
332 std::vector<uint8_t> data(
rom_->
size());
334 return ComputeHash(data);
338 const std::vector<uint8_t>& data)
const {
345 const std::vector<uint8_t>& compressed)
const {
352 return absl::FailedPreconditionError(
"ROM not loaded");
357 return absl::DataLossError(
"ROM size is zero");
363 return absl::DataLossError(
"ROM too small to be valid");
366 return absl::OkStatus();
371 for (
const auto& [
id, snapshot] :
snapshots_) {
372 total += snapshot.compressed_size;
382 : version_mgr_(version_mgr), mode_(
ApprovalMode::kHostOnly) {}
393 const std::string& proposal_id,
const std::string& sender,
394 const std::string& description,
const nlohmann::json& proposal_data) {
397 status.
status =
"pending";
403 absl::StrCat(
"Before proposal: ", description), sender,
false);
405 if (!snapshot_result.ok()) {
406 return snapshot_result.status();
413 return absl::OkStatus();
417 const std::string& proposal_id,
const std::string& username,
421 return absl::NotFoundError(
"Proposal not found");
426 if (status.
status !=
"pending") {
427 return absl::FailedPreconditionError(
"Proposal already decided");
431 status.
votes[username] = approved;
435 status.
status =
"approved";
439 size_t rejection_count = 0;
440 for (
const auto& [user, vote] : status.
votes) {
448 status.
status =
"rejected";
453 return absl::OkStatus();
467 size_t approval_count = 0;
468 for (
const auto& [user, approved] : status.
votes) {
479 for (
const auto& [user, approved] : status.
votes) {
494 const std::string& proposal_id)
const {
499 return it->second.status ==
"approved";
502std::vector<ProposalApprovalManager::ApprovalStatus>
504 std::vector<ApprovalStatus> pending;
506 if (status.status ==
"pending") {
507 pending.push_back(status);
513absl::StatusOr<ProposalApprovalManager::ApprovalStatus>
515 const std::string& proposal_id)
const {
518 return absl::NotFoundError(
"Proposal not found");
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
std::vector< std::string > participants_
void SetHost(const std::string &host_username)
absl::StatusOr< ApprovalStatus > GetProposalStatus(const std::string &proposal_id) const
absl::Status VoteOnProposal(const std::string &proposal_id, const std::string &username, bool approved)
std::string host_username_
void SetApprovalMode(ApprovalMode mode)
bool IsProposalApproved(const std::string &proposal_id) const
RomVersionManager * version_mgr_
absl::Status SubmitProposal(const std::string &proposal_id, const std::string &sender, const std::string &description, const nlohmann::json &proposal_data)
std::vector< ApprovalStatus > GetPendingProposals() const
bool CheckApprovalThreshold(const ApprovalStatus &status) const
ProposalApprovalManager(RomVersionManager *version_mgr)
std::map< std::string, ApprovalStatus > proposals_
Manages ROM versioning, snapshots, and rollback capabilities.
std::string ComputeRomHash() const
absl::Status CleanupOldSnapshots()
std::vector< uint8_t > DecompressData(const std::vector< uint8_t > &compressed) const
size_t GetTotalStorageUsed() const
absl::StatusOr< std::string > CreateSnapshot(const std::string &description, const std::string &creator, bool is_checkpoint=false)
absl::StatusOr< RomSnapshot > GetSnapshot(const std::string &snapshot_id) const
std::string GetCurrentHash() const
std::map< std::string, RomSnapshot > snapshots_
absl::Status ValidateRomIntegrity() const
std::string last_known_hash_
RomVersionManager(Rom *rom)
absl::Status Initialize(const Config &config)
absl::Status AutoRecover()
absl::StatusOr< bool > DetectCorruption()
absl::Status RestoreSnapshot(const std::string &snapshot_id)
absl::Status MarkAsSafePoint(const std::string &snapshot_id)
std::vector< uint8_t > CompressData(const std::vector< uint8_t > &data) const
absl::Status DeleteSnapshot(const std::string &snapshot_id)
std::vector< RomSnapshot > GetSnapshots(bool safe_points_only=false) const
std::string ComputeHash(const std::vector< uint8_t > &data)
int64_t GetCurrentTimestamp()
std::string snapshot_before
std::map< std::string, bool > votes
Represents a versioned snapshot of ROM state.
std::vector< uint8_t > rom_data
bool enable_corruption_detection
size_t total_storage_bytes
size_t manual_checkpoints
int64_t oldest_snapshot_timestamp
int64_t newest_snapshot_timestamp