use crate::common::*;
use base64::{decode, encode};
use failure_derive::Fail;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Clone, Debug, Fail, PartialEq, Eq)]
pub enum SaveError {
#[fail(display = "JSON error: {}", error_string)]
JSONError { error_string: String },
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Save {
pub ascension_level: u32,
pub name: String,
pub custom_mods: Vec<String>,
pub daily_mods: Vec<String>,
pub is_endless_mode: bool,
pub is_daily: bool,
pub is_ascension_mode: bool,
pub is_trial: bool,
pub daily_date: u32,
pub act_num: u32,
pub gold: u32,
pub current_health: u32,
pub max_health: u32,
pub post_combat: bool,
pub smoked: bool,
pub chose_neow_reward: bool,
pub play_time: u32,
pub obtained_cards: HashMap<String, u32>,
pub mugged: bool,
pub monsters_killed: u32,
pub spirit_count: u32,
pub relic_counters: Vec<i32>,
pub one_time_event_list: Vec<String>,
pub max_orbs: u32,
pub level_name: String,
pub save_date: u64,
pub is_final_act_on: bool,
pub has_sapphire_key: bool,
pub has_ruby_key: bool,
pub has_emerald_key: bool,
pub hand_size: u32,
pub gold_gained: u32,
pub floor_num: f64,
pub event_list: Vec<String>,
pub event_chances: Vec<f64>,
pub cards: Vec<Card>,
#[serde(rename = "purgeCost")]
pub purge_cost: u32,
pub boss_relics: Vec<String>,
pub common_relics: Vec<String>,
pub rare_relics: Vec<String>,
pub shop_relics: Vec<String>,
pub uncommon_relics: Vec<String>,
pub relics: Vec<String>,
pub current_room: String,
pub room_x: i32,
pub room_y: i32,
pub path_x: Vec<i32>,
pub path_y: Vec<i32>,
pub monster_list: Vec<String>,
pub elite_monster_list: Vec<String>,
pub boss_list: Vec<String>,
pub boss: String,
pub potions: Vec<String>,
pub potion_slots: u32,
pub potion_seed_count: u32,
pub potion_chance: i32,
pub perfect: u32,
pub overkill: bool,
pub elites1_killed: u32,
pub elites2_killed: u32,
pub elites3_killed: u32,
pub combo: bool,
pub metric_relics_obtained: Vec<FloorKey>,
pub metric_purchased_purges: u32,
pub metric_potions_obtained: Vec<FloorKey>,
pub metric_playtime: u32,
pub metric_potions_floor_usage: Vec<u32>,
pub metric_potions_floor_spawned: Vec<u32>,
pub metric_path_taken: Vec<String>,
pub metric_path_per_floor: Vec<Option<String>>,
pub metric_max_hp_per_floor: Vec<u32>,
pub metric_items_purged_floors: Vec<u32>,
pub metric_items_purged: Vec<String>,
pub metric_item_purchase_floors: Vec<u32>,
pub metric_gold_per_floor: Vec<u32>,
pub metric_floor_reached: u32,
pub metric_event_choices: Vec<EventChoice>,
pub metric_damage_taken: Vec<DamageTaken>,
pub metric_current_hp_per_floor: Vec<u32>,
pub metric_card_choices: Vec<CardChoice>,
pub metric_campfire_upgraded: u32,
pub metric_campfire_rituals: u32,
pub metric_campfire_rested: u32,
pub metric_campfire_meditates: u32,
pub metric_campfire_choices: Vec<FloorKey>,
pub metric_build_version: String,
pub metric_boss_relics: Vec<BossRelicChoice>,
pub metric_seed_played: String,
pub seed: i64,
pub special_seed: u32,
pub treasure_seed_count: u32,
pub shuffle_seed_count: u32,
pub relic_seed_count: u32,
pub monster_seed_count: u32,
pub merchant_seed_count: u32,
pub event_seed_count: u32,
pub card_seed_count: u32,
pub card_random_seed_randomizer: i32,
pub card_random_seed_count: u32,
pub ai_seed_count: u32,
pub neow_cost: String,
pub neow_bonus: String,
pub mystery_machine: u32,
pub champions: u32,
pub blights: Vec<String>,
pub blight_counters: Vec<i32>,
pub endless_increments: Vec<i32>,
pub red: u32,
pub green: u32,
pub blue: u32,
}
fn xor_key(bytes: &[u8]) -> Vec<u8> {
let key = b"key";
bytes
.iter()
.enumerate()
.map(|(i, v)| *v ^ key[i % key.len()])
.collect()
}
impl Save {
pub fn new(contents: &str) -> Result<Save, SaveError> {
let decoded = decode(contents).map(|bytes| xor_key(&bytes));
let bytes = match decoded {
Ok(decoded) => decoded,
Err(_) => {
Vec::from(contents)
}
};
serde_json::from_slice(bytes.as_slice()).map_err(|e| SaveError::JSONError {
error_string: e.to_string(),
})
}
pub fn to_bytes(&self) -> Result<Vec<u8>, SaveError> {
serde_json::to_vec(self).map_err(|e| SaveError::JSONError {
error_string: e.to_string(),
})
}
pub fn to_string(&self) -> Result<String, SaveError> {
serde_json::to_string(self).map_err(|e| SaveError::JSONError {
error_string: e.to_string(),
})
}
pub fn to_b64_string(&self) -> Result<String, SaveError> {
let bytes = self.to_bytes().map(|b| xor_key(&b))?;
Ok(encode(std::str::from_utf8(&bytes).unwrap()))
}
}