use std::env;
use std::fs::File;
use std::fmt::Write as FmtWrite;
use std::io::Write;
use std::path::Path;
use serde::{Deserialize};
#[derive(PartialEq, Clone, Deserialize)]
enum ParamType
{
Number,
Float,
String,
Items,
StringList,
NumberList,
ListOfNumberList,
}
impl ParamType {
fn rust_type(&self) -> &'static str {
match self {
ParamType::Number => "usize",
ParamType::Float => "f32",
ParamType::String => "String",
ParamType::Items => "Items",
ParamType::StringList => "Vec<String>",
ParamType::NumberList => "Vec<u32>",
ParamType::ListOfNumberList => "Vec<Vec<u32>>",
}
}
fn parse_macro(&self) -> &'static str {
match self {
ParamType::Number => "decode_number!",
ParamType::Float => "decode_float!",
ParamType::String => "decode_string!",
ParamType::Items => "decode_number_vec!",
ParamType::StringList => "decode_string_vec!",
ParamType::NumberList => "decode_number_vec!",
ParamType::ListOfNumberList => "decode_vec_of_number_vec!",
}
}
}
#[derive(Clone, Deserialize)]
struct Param
{
name: String,
id: u32,
param_type: ParamType,
optional: Option<bool>
}
#[derive(Clone, Deserialize)]
struct Message
{
name: String,
code: u32,
params: Vec<Param>
}
#[derive(Clone, Deserialize)]
struct Messages
{
events: Vec<Message>,
requests: Vec<Message>,
responses: Vec<Message>,
}
fn main() {
save_file("itemdb.rs", &generate_itemdb());
save_file("messages.rs", &generate_messages());
}
fn generate_messages() -> String {
let mut out = String::new();
out.push_str(r"use log::*;
use crate::photon_decode;
use crate::photon_messages::Items;
use crate::photon_decode::Parameters;
use crate::photon_decode::Value;
");
out.push_str(include_str!("assets/decode_macros.rs"));
let messages: Messages = serde_json::from_str(include_str!("assets/messages.json")).expect("Missing assets/messages.json");
for msg in &[&messages.events[..], &messages.requests[..], &messages.responses[..]].concat() {
let mut struct_params = String::new();
for param in &msg.params {
if param.optional.is_some() {
struct_params.push_str(&format!(" pub {}: Option<{}>,\n", param.name, param.param_type.rust_type()));
} else {
struct_params.push_str(&format!(" pub {}: {},\n", param.name, param.param_type.rust_type()));
}
}
out.push_str(&format!(r###"
#[derive(Debug, Clone, PartialEq, Default)]
pub struct {} {{
{}
}}
"###, msg.name, struct_params));
let mut parse_body = String::new();
for param in &msg.params {
if param.param_type == ParamType::Items {
parse_body.push_str(&format!(" let {} = {}(val, {}, \"{}::{0}\")?;\n", "item_array", param.param_type.parse_macro(), param.id, msg.name));
parse_body.push_str(&format!(" let items = item_array.into();\n"));
} else {
if param.optional.is_some() {
parse_body.push_str(&format!(" let {} = {}(val, {}, \"{}::{0}\");\n", param.name, param.param_type.parse_macro(), param.id, msg.name));
} else {
parse_body.push_str(&format!(" let {} = {}(val, {}, \"{}::{0}\")?;\n", param.name, param.param_type.parse_macro(), param.id, msg.name));
}
}
}
let mut param_names = String::new();
for param in &msg.params {
param_names.push_str(&format!("{}, ", param.name));
}
out.push_str(&format!(r###"
impl {0} {{
pub fn parse(val: Parameters) -> Option<Message> {{
info!("{0} parameters: {{:?}}", val);
{1}
Some(Message::{0}({0} {{ {2} }}))
}}
}}
"###, msg.name, parse_body, param_names));
}
out.push_str("\n#[derive(Debug, Clone, PartialEq)]\n");
out.push_str("pub enum Message {\n");
for msg in &[&messages.events[..], &messages.requests[..], &messages.responses[..]].concat() {
out.push_str(&format!(" {0}({0}),\n", msg.name));
}
out.push_str("}");
let mut event_matches = String::new();
let mut requests_matches = String::new();
let mut responses_matches = String::new();
for msg in &messages.events {
event_matches.push_str(&format!(" Some(photon_decode::Value::Short({})) => {}::parse(parameters),\n", msg.code, msg.name));
}
for msg in &messages.requests {
requests_matches.push_str(&format!(" Some(photon_decode::Value::Short({})) => {}::parse(parameters),\n", msg.code, msg.name));
}
for msg in &messages.responses {
responses_matches.push_str(&format!(" Some(photon_decode::Value::Short({})) => {}::parse(parameters),\n", msg.code, msg.name));
}
out.push_str(&format!(r###"
pub fn into_game_message(photon_message: photon_decode::Message) -> Option<Message> {{
debug!("Raw photon : {{:?}}", photon_message);
match photon_message {{
photon_decode::Message::Event(photon_decode::EventData{{
code: 1,
parameters
}}) => {{
match parameters.get(&252u8) {{
{}
_ => None
}}
}},
photon_decode::Message::Request(photon_decode::OperationRequest{{
code: 1,
parameters
}}) => {{
match parameters.get(&253u8) {{
{}
_ => None
}}
}},
photon_decode::Message::Response(photon_decode::OperationResponse{{
code: 1,
parameters,
return_code: _,
debug_message: _
}}) => {{
match parameters.get(&253u8) {{
{}
_ => None
}}
}},
_ => None
}}
}}
"###, event_matches, requests_matches, responses_matches));
out
}
fn generate_itemdb() -> String {
let mut out = String::new();
writeln!(&mut out, "use std::collections::HashMap;").unwrap();
writeln!(&mut out, "").unwrap();
writeln!(&mut out, "lazy_static! {{").unwrap();
writeln!(&mut out, " pub static ref ITEMDB: HashMap<u32, &'static str> = {{[").unwrap();
include_str!("assets/item_ids.txt").split('\n').filter_map(|line| {
let v: Vec<&str> = line.split(',').collect();
let id : u32 = v.get(0)?.parse().ok()?;
let item = v.get(1)?.to_owned();
Some((id, item))
}).for_each(|(id, item)| {
writeln!(&mut out, " ({}, \"{}\"),", id, item.trim()).unwrap();
});
writeln!(&mut out, " ].iter().cloned().collect()}};").unwrap();
write!(&mut out, "}}").unwrap();
out
}
fn save_file(file_name: &str, content: &str) {
let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join(file_name);
let mut f = File::create(&dest_path).unwrap();
f.write_all(content.as_bytes()).unwrap();
}