ikamaki/src/exporter.rs

261 lines
7.8 KiB
Rust

use crate::schedule::Schedule;
use roead::Endian;
use roead::byml::Byml;
/*
Fixes the formatting in the human readable YAML.
Not the prettiest, but it works.
*/
fn fix_yaml(byml: &Byml) -> Byml {
match byml {
Byml::I32(v) => Byml::I64(*v as i64),
Byml::Array(arr) => Byml::Array(arr.iter().map(fix_yaml).collect()),
Byml::Map(map) => {
let mut new_map = roead::byml::Map::default();
new_map.reserve(map.len());
for (k, v) in map.iter() {
new_map.insert(k.clone(), fix_yaml(v));
}
Byml::Map(new_map)
}
_ => byml.clone(),
}
}
const MAP_FIRST_APPEAR: [(&str, i32); 16] = [
("2015-04-17", 0),
("2015-04-17", 1),
("2015-04-17", 2),
("2015-04-17", 3),
("2015-04-17", 4),
("2015-06-02", 6),
("2015-06-11", 7),
("2015-06-20", 9),
("2015-07-11", 8),
("2015-07-25", 5),
("2015-08-21", 11),
("2015-09-18", 10),
("2015-11-14", 12),
("2015-12-04", 15),
("2015-12-29", 14),
("2016-01-22", 13),
];
const RULE_FIRST_APPEAR: [(&str, &str); 3] = [
("2015-06-02", "cVar"),
("2015-07-02", "cVlf"),
("2015-08-15", "cVgl"),
];
const WEAPON_UNLOCK: [(&str, i32); 58] = [
("2015-06-02", 1060),
("2015-06-06", 2030),
("2015-06-13", 1020),
("2015-06-17", 1160),
("2015-06-17", 4031),
("2015-06-24", 1110),
("2015-06-27", 2000),
("2015-06-27", 1091),
("2015-07-08", 1000),
("2015-07-08", 1081),
("2015-07-18", 2040),
("2015-07-18", 1061),
("2015-07-22", 4040),
("2015-07-22", 1021),
("2015-08-01", 1130),
("2015-08-01", 2031),
("2015-08-06", 3000),
("2015-08-06", 5010),
("2015-08-29", 4050),
("2015-08-29", 1161),
("2015-09-05", 5000),
("2015-09-12", 1170),
("2015-09-26", 3010),
("2015-10-03", 2001),
("2015-10-10", 1131),
("2015-10-10", 4041),
("2015-10-17", 1150),
("2015-10-30", 1111),
("2015-10-30", 1171),
("2015-11-07", 5011),
("2015-11-11", 1001),
("2015-11-18", 4051),
("2015-11-21", 5020),
("2015-11-25", 3001),
("2015-11-28", 3020),
("2015-12-12", 5001),
("2015-12-19", 3011),
("2015-12-25", 1151),
("'2016-01-01T19:00:00+09:00'", 2041),
("2016-01-09", 3021),
("2016-01-16", 5021),
("'2016-04-13T11:00:00+09:00'", 1042),
("'2016-04-13T11:00:00+09:00'", 1072),
("'2016-04-13T11:00:00+09:00'", 2022),
("'2016-04-13T11:00:00+09:00'", 2032),
("'2016-04-13T11:00:00+09:00'", 3002),
("'2016-04-13T11:00:00+09:00'", 4002),
("'2016-04-13T11:00:00+09:00'", 4052),
("'2016-04-13T11:00:00+09:00'", 5002),
("'2016-06-08T11:00:00+09:00'", 1002),
("'2016-06-08T11:00:00+09:00'", 1032),
("'2016-06-08T11:00:00+09:00'", 1062),
("'2016-06-08T11:00:00+09:00'", 1132),
("'2016-06-08T11:00:00+09:00'", 1172),
("'2016-06-08T11:00:00+09:00'", 2012),
("'2016-06-08T11:00:00+09:00'", 4012),
("'2016-06-08T11:00:00+09:00'", 4022),
("'2016-06-08T11:00:00+09:00'", 5012),
];
const HEADER: &str = "Version: 1\n\
IsBigEndian: True\n\
SupportPaths: False\n\
HasReferenceNodes: False\n\
root:\n";
fn build_map_first_appear() -> Byml {
Byml::Array(
MAP_FIRST_APPEAR
.iter()
.map(|(date, id)| {
let mut map = roead::byml::Map::default();
map.insert("Date".into(), Byml::String((*date).into()));
map.insert("MapID".into(), Byml::I32(*id));
Byml::Map(map)
})
.collect(),
)
}
fn build_rule_first_appear() -> Byml {
Byml::Array(
RULE_FIRST_APPEAR
.iter()
.map(|(date, rule)| {
let mut map = roead::byml::Map::default();
map.insert("Date".into(), Byml::String((*date).into()));
map.insert("GachiRule".into(), Byml::String((*rule).into()));
Byml::Map(map)
})
.collect(),
)
}
fn build_weapon_unlock() -> Byml {
Byml::Array(
WEAPON_UNLOCK
.iter()
.map(|(date, id)| {
let mut map = roead::byml::Map::default();
map.insert("Date".into(), Byml::String((*date).into()));
map.insert("WeaponSetID".into(), Byml::I32(*id));
Byml::Map(map)
})
.collect(),
)
}
fn build_byml(schedule: &Schedule, hash_kick: bool) -> Byml {
let mut root = roead::byml::Map::default();
root.insert("AddFirstMatchingTime".into(), Byml::I32(30));
root.insert("AddMatchingTime".into(), Byml::I32(0));
root.insert(
"AfterFesBonusStart".into(),
Byml::String("2016-07-24T19:00:00+09:00".into()),
);
root.insert("BottleneckThreasholdFrame".into(), Byml::I32(480));
let aligned = crate::default_timestamp();
let date_time = chrono::DateTime::from_timestamp(aligned, 0)
.unwrap()
.to_rfc3339_opts(chrono::SecondsFormat::Secs, true);
root.insert("DateTime".into(), Byml::String(date_time.into()));
root.insert("DisconnectByMemoryHash".into(), Byml::Bool(hash_kick));
root.insert("TimeoutAfterJoin".into(), Byml::I32(120));
root.insert("Version".into(), Byml::I32(3));
root.insert("WaitMatchingTime".into(), Byml::I32(25));
root.insert("WebPost".into(), Byml::Bool(true));
root.insert("MapFirstAppear".into(), build_map_first_appear());
root.insert("RuleFirstAppear".into(), build_rule_first_appear());
root.insert("WeaponUnlock".into(), build_weapon_unlock());
let phases_array: Vec<Byml> = schedule
.phases
.iter()
.map(|phase| {
let mut dict = roead::byml::Map::default();
let duration_hours = ((phase.end - phase.start) / 3600) as i32;
dict.insert("Time".into(), Byml::I32(duration_hours));
let regular_stages: Vec<Byml> = phase
.regular
.iter()
.map(|&m| {
let mut stage_map = roead::byml::Map::default();
stage_map.insert("MapID".into(), Byml::I32(m));
Byml::Map(stage_map)
})
.collect();
dict.insert("RegularStages".into(), Byml::Array(regular_stages));
let gachi_stages: Vec<Byml> = phase
.gachi
.iter()
.map(|&m| {
let mut stage_map = roead::byml::Map::default();
stage_map.insert("MapID".into(), Byml::I32(m));
Byml::Map(stage_map)
})
.collect();
dict.insert("GachiStages".into(), Byml::Array(gachi_stages));
dict.insert(
"RegularRule".into(),
Byml::String(phase.regular_rule.clone().into()),
);
dict.insert(
"GachiRule".into(),
Byml::String(phase.gachi_rule.clone().into()),
);
Byml::Map(dict)
})
.collect();
root.insert("Phases".into(), Byml::Array(phases_array));
Byml::Map(root)
}
// Export the BYAML file
pub fn export(
schedule: &Schedule,
path: &str,
hash_kick: bool,
) -> Result<(), Box<dyn std::error::Error>> {
let byml = build_byml(schedule, hash_kick);
// Version 1 is being used on console
let bytes = byml.to_binary_with_version(Endian::Big, 1);
std::fs::write(path, bytes)?;
Ok(())
}
// Export the human-readable YAML file
pub fn export_text(
schedule: &Schedule,
path: &str,
hash_kick: bool,
) -> Result<(), Box<dyn std::error::Error>> {
let body = fix_yaml(&build_byml(schedule, hash_kick)).to_text();
let mut out = String::from(HEADER);
for line in body.lines() {
out.push_str(" ");
out.push_str(line);
out.push('\n');
}
std::fs::write(path, out)?;
Ok(())
}