@@ 1,4 1,158 @@
+use std::{env, fs::File};
+use std::io::{prelude::*, BufReader, BufWriter};
+use minicbor::bytes::ByteArray;
+use minicbor_io::Writer;
+use pokemon::pokedex::{self, Pokemon, Type, Stats};
+use serde_json::Value;
fn main() {
- println!("Hello, World!");
+ let args: Vec<String> = env::args().collect();
+
+ if args.len() != 3 {
+ println!("usage: {} <path-to-pokemon> <output>", args[0]);
+ return;
+ }
+
+ println!("you provided {}", args[1]);
+
+ let mut bmp_file = File::open(format!("{}.bmp", args[1])).unwrap();
+ let mut buffer = [0u8; 578];
+ bmp_file.read_exact(&mut buffer).unwrap();
+
+ let json_file = File::open(format!("{}.json", args[1])).unwrap();
+ let json: Value = serde_json::from_reader(BufReader::new(json_file)).unwrap();
+
+ let pokemon = parse_pokemon(json, &buffer).unwrap();
+
+ println!("created pokemon {:?}", pokemon);
+
+ let output_file = BufWriter::new(File::create(&args[2]).unwrap());
+ let mut cbor_writer = Writer::new(output_file);
+ let outsize = cbor_writer.write(pokemon).unwrap();
+
+ println!("wrote to file. final size was {} bytes", outsize);
+}
+
+fn parse_pokemon(json: Value, bitmap: &[u8; 578]) -> Result<Pokemon, &'static str> {
+ let name = match json["name"].as_str() {
+ Some(n) => {
+ let mut i = 0;
+ let mut name = ['\0'; 12];
+ for c in n.chars() {
+ name[i] = c;
+ i += 1;
+ }
+ name
+ },
+ None => return Err("name was missing")
+ };
+
+ let mut types = [Option::None, Option::None];
+ match json["types"].as_array() {
+ Some(raw_types) => {
+ for t in raw_types {
+ let slot = match t["slot"].as_i64() {
+ Some(1) => 0,
+ Some(2) => 1,
+ None => return Err("missing type slot"),
+ _ => return Err("invalid type slot")
+ };
+ types[slot] = match t["type"].as_str() {
+ Some(val) => {
+ match type_from_string(val) {
+ Ok(t) => Some(t),
+ Err(_) => return Err("failed to parse type")
+ }
+ }
+ None => None
+ }
+ }
+ }
+ None => return Err("no types found")
+ }
+
+ let stats = match json["stats"].as_array() {
+ Some(all_stats) => all_stats.iter().flat_map(|j| parse_as_stats(j)).collect(),
+ None => return Err("missing stats")
+ };
+
+ Ok(pokedex::Pokemon {
+ id: match parse_as_u8(&json["id"]) {
+ Some(i) => i,
+ None => return Err("missing id")
+ },
+ name,
+ type_primary: match types[0] {
+ Some(t) => t,
+ None => return Err("no primary type given")
+ },
+ type_secondary: types[1],
+ capture_rate: match parse_as_u8(&json["species"]["capture_rate"]) {
+ Some(i) => i,
+ None => return Err("missing capture rate")
+ },
+ base_experience: match parse_as_u8(&json["id"]) {
+ Some(i) => i,
+ None => return Err("missing base experience")
+ },
+ stats,
+ sprite: ByteArray::from(*bitmap),
+ })
+}
+
+fn parse_as_u8(json: &Value) -> Option<u8> {
+ return match json.as_u64().map(|i| u8::try_from(i)) {
+ Some(i) => {
+ match i {
+ Ok(i) => Some(i),
+ Err(_) => None
+ }
+ },
+ None => None
+ }
+}
+
+fn parse_as_stats(json: &Value) -> Option<Stats> {
+ Some(Stats {
+ stat: match json["stat"].as_str() {
+ Some(s) => match s {
+ "hp" => Stat::Hp,
+ "attack" => Stat::Attack,
+ "defense" => Stat::Defense,
+ "special-attack" => Stat::SpecialAttack,
+ "special-defense" => Stat::SpecialDefense,
+ "speed" => Stat::Speed,
+ _ => return None
+ }
+ None => return None
+ },
+ base_value: match json["base_state"] {
+ Some(i) => i,
+ None => return None
+ }
+ })
+}
+
+fn type_from_string(s: &str) -> Result<Type, &str> {
+ match s {
+ "normal" => Ok(Type::Normal),
+ "fighting" => Ok(Type::Fighting),
+ "flying" => Ok(Type::Flying),
+ "poison" => Ok(Type::Poison),
+ "ground" => Ok(Type::Ground),
+ "rock" => Ok(Type::Rock),
+ "bug" => Ok(Type::Bug),
+ "ghost" => Ok(Type::Ghost),
+ "steel" => Ok(Type::Steel),
+ "fire" => Ok(Type::Fire),
+ "water" => Ok(Type::Water),
+ "grass" => Ok(Type::Grass),
+ "electric" => Ok(Type::Electric),
+ "psychic" => Ok(Type::Psychic),
+ "ice" => Ok(Type::Ice),
+ "dragon" => Ok(Type::Dragon),
+ "dark" => Ok(Type::Dark),
+ "fairy" => Ok(Type::Fairy),
+ _ => Err("unknown type")
+ }
}=
\ No newline at end of file
@@ 0,0 1,165 @@
+#![no_std]
+
+pub mod pokedex {
+ use minicbor::{Encode, Decode, bytes::ByteArray};
+
+ #[derive(Encode, Decode, Debug)]
+ pub struct Pokemon {
+ #[n(0)] pub id: u8,
+ #[n(1)] pub name: [char; 12],
+ #[n(2)] pub type_primary: Type,
+ #[n(3)] pub type_secondary: Option<Type>,
+
+ #[n(4)] pub capture_rate: u8,
+ #[n(5)] pub base_experience: u8,
+ #[n(6)] pub stats: [Stats; 6],
+
+ #[b(7)] pub sprite: ByteArray<578>,
+ }
+
+ #[derive(Encode, Decode, Debug)]
+ #[cbor(index_only)]
+ pub enum Stat {
+ #[n(0)] Hp,
+ #[n(1)] Attack,
+ #[n(2)] Defense,
+ #[n(3)] SpecialAttack,
+ #[n(4)] SpecialDefense,
+ #[n(5)] Speed,
+ }
+
+ #[derive(Encode, Decode, Debug)]
+ pub struct Stats {
+ #[n(0)] stat: Stat,
+ #[n(1)] base_value: u16,
+ }
+
+ // These could be created dynamically from the JSON data. This isn't so bad
+ // though so /shruggie.
+ #[derive(Encode, Decode, Debug, Clone, Copy)]
+ #[cbor(index_only)]
+ pub enum Type {
+ #[n(0)] Normal,
+ #[n(1)] Fighting,
+ #[n(2)] Flying,
+ #[n(3)] Poison,
+ #[n(4)] Ground,
+ #[n(5)] Rock,
+ #[n(6)] Bug,
+ #[n(7)] Ghost,
+ #[n(8)] Steel,
+ #[n(9)] Fire,
+ #[n(10)] Water,
+ #[n(11)] Grass,
+ #[n(12)] Electric,
+ #[n(13)] Psychic,
+ #[n(14)] Ice,
+ #[n(15)] Dragon,
+ #[n(16)] Dark,
+ #[n(17)] Fairy,
+ }
+
+ #[derive(Encode, Decode, Debug)]
+ pub struct MoveListChunk {
+ #[n(0)] is_final_chunk: bool,
+ #[n(1)] moves: [Option<LearnableMove>; 16],
+ }
+
+ #[derive(Encode, Decode, Debug)]
+ pub struct LearnableMove {
+ #[n(0)] id: u16,
+ #[n(1)] condition: LearnCondition,
+ }
+
+ #[derive(Encode, Decode, Debug)]
+ pub enum LearnCondition {
+ #[n(0)] LevelUp {
+ #[n(0)] level: u8
+ },
+ #[n(1)] Machine,
+ }
+
+ #[derive(Encode, Decode, Debug)]
+ pub struct Move {
+ #[n(0)] id: u16,
+ #[n(1)] name: [char; 12],
+
+ #[n(2)] type_: Type,
+ #[n(3)] damage_class: DamageClass,
+ #[n(4)] target: Target,
+
+ #[n(5)] accuracy: u8,
+ #[n(6)] power: u8,
+ #[n(7)] pp: u8,
+ #[n(8)] priority: u8,
+
+ #[n(9)] parameters: Parameters,
+ #[n(10)] stat_changes: [Option<StatChange>; 2]
+ }
+
+ #[derive(Encode, Decode, Debug)]
+ #[cbor(map)]
+ pub struct Parameters {
+ #[n(0)] ailment: Option<AilmentParameter>,
+ #[n(1)] crit_rate: u8,
+ #[n(2)] drain: u8,
+ #[n(3)] flinch_chance: u8,
+ #[n(4)] healing: u8,
+ #[n(5)] stat_chance: u8,
+ #[n(6)] turn_range: Option<ParameterRange>,
+ #[n(7)] hit_range: Option<ParameterRange>,
+ }
+
+ #[derive(Encode, Decode, Debug)]
+ pub struct AilmentParameter {
+ #[n(0)] ailment: AilmentType,
+ #[n(1)] chance: u8,
+ }
+
+ #[derive(Encode, Decode, Debug)]
+ #[cbor(index_only)]
+ pub enum AilmentType {
+ #[n(0)] Burn,
+ #[n(1)] Confusion,
+ #[n(2)] Disable,
+ #[n(3)] Freeze,
+ #[n(4)] LeechSeed,
+ #[n(5)] Paralysis,
+ #[n(6)] Poison,
+ #[n(7)] Sleep,
+ #[n(8)] Trap,
+ }
+
+ #[derive(Encode, Decode, Debug)]
+ pub struct ParameterRange {
+ #[n(0)] min: u8,
+ #[n(1)] max: u8,
+ }
+
+ #[derive(Encode, Decode, Debug)]
+ #[cbor(index_only)]
+ pub enum DamageClass {
+ #[n(0)] Physical,
+ #[n(1)] Special,
+ #[n(2)] Status,
+ }
+
+ #[derive(Encode, Decode, Debug)]
+ #[cbor(index_only)]
+ pub enum Target {
+ #[n(0)] AllOpponents,
+ #[n(1)] AllOtherPokemon,
+ #[n(2)] EntireField,
+ #[n(3)] RandomOpponent,
+ #[n(4)] SelectedPokemon,
+ #[n(5)] SpecificMove,
+ #[n(6)] User,
+ #[n(7)] UserField,
+ }
+
+ #[derive(Encode, Decode, Debug)]
+ pub struct StatChange {
+ #[n(0)] amount: u8,
+ #[n(1)] stat: Stat,
+ }
+}