mirror of
https://github.com/eiskasten/wc-beacon.git
synced 2026-09-08 12:25:08 -05:00
Implement gift instance
This commit is contained in:
20
src/info.rs
20
src/info.rs
@@ -6,7 +6,7 @@ use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::PathBuf;
|
||||
use crate::error::err_reason;
|
||||
use crate::pcd::{CardType, Deserialized, Game, Partitioned, PCD, PCD_LENGTH, Raw};
|
||||
use crate::pcd::{CardType, Deserialized, Game, Partitioned, PCD, PCD_LENGTH, pgt_info, Raw};
|
||||
|
||||
|
||||
pub fn info(pcd: PathBuf) -> Result<(), Box<dyn Error>> {
|
||||
@@ -17,7 +17,7 @@ pub fn info(pcd: PathBuf) -> Result<(), Box<dyn Error>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set(title: Option<String>, card_type: Option<CardType>, card_id: Option<u16>, games: Option<Vec<Game>>, comment: Option<String>, redistribution: Option<u8>, icons: Option<Vec<u16>>, pgt: Option<PathBuf>, received: Option<u16>, pcd: Option<PathBuf>, output: PathBuf) -> Result<(), Box<dyn Error>> {
|
||||
pub fn set(title: Option<String>, card_type: Option<CardType>, card_id: Option<u16>, gift_instance: Option<u16>, games: Option<Vec<Game>>, comment: Option<String>, redistribution: Option<u8>, icons: Option<Vec<u16>>, pgt: Option<PathBuf>, received: Option<u16>, pcd: Option<PathBuf>, output: PathBuf) -> Result<(), Box<dyn Error>> {
|
||||
let mut pcd = if let Some(f) = pcd {
|
||||
let data = fs::read(f).map_err(|e| err_reason("Unable to read pcd file", e))?;
|
||||
let raw: PCD<Raw> = PCD::try_from(data.as_slice())?;
|
||||
@@ -31,10 +31,6 @@ pub fn set(title: Option<String>, card_type: Option<CardType>, card_id: Option<u
|
||||
pcd.state.title = t;
|
||||
}
|
||||
|
||||
if let Some(c) = card_type {
|
||||
pcd.state.card_type = c;
|
||||
}
|
||||
|
||||
if let Some(c) = card_id {
|
||||
pcd.state.card_id = c;
|
||||
}
|
||||
@@ -66,6 +62,18 @@ pub fn set(title: Option<String>, card_type: Option<CardType>, card_id: Option<u
|
||||
Ok(len) => if len > 0 { eprintln!("warning: provided pgt file is bigger than expected and will be truncated") }
|
||||
Err(e) => eprintln!("warning: unable to check if pgt file is too long: {}", e)
|
||||
}
|
||||
|
||||
let (card_type, gift_instance) = pgt_info(&pcd.state.pgt);
|
||||
pcd.state.card_type = card_type;
|
||||
pcd.state.gift_instance = gift_instance;
|
||||
}
|
||||
|
||||
if let Some(c) = card_type {
|
||||
pcd.state.card_type = c;
|
||||
}
|
||||
|
||||
if let Some(gift_instance) = gift_instance {
|
||||
pcd.state.gift_instance = gift_instance;
|
||||
}
|
||||
|
||||
let pcd: PCD<Raw> = (&pcd.serialize()).try_into()?;
|
||||
|
||||
@@ -47,7 +47,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
distribute(pcd, region, device, address.unwrap_or([0xa4, 0xc0, 0xe1, 0x6e, 0x76, 0x80]), interval),
|
||||
Command::Decrypt { epcd, checksum, address, pcd } => decrypt(epcd, checksum, address, pcd),
|
||||
Command::Info { pcd } => info(pcd),
|
||||
Command::Set { title, kind: card_type, card_id, games, description: comment, redistribution, icons, pgt, date: received, pcd, output } => set(title, card_type, card_id, games, comment, redistribution, icons, pgt, received, pcd, output)
|
||||
Command::Set { title, kind: card_type, gift_instance, card_id, games, description: comment, redistribution, icons, pgt, date: received, pcd, output } => set(title, card_type, card_id, gift_instance, games, comment, redistribution, icons, pgt, received, pcd, output)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ enum Command {
|
||||
/// Create a new PCD file or edit an existing one
|
||||
#[command(name = "set")]
|
||||
Set {
|
||||
/// PCD file to edit, non-destructive only for input, leave empty to create from scratch
|
||||
/// PCD file to edit, non-destructive only for input, leave empty to create from scratch, kind and gift instance id have precedence over pcd data
|
||||
#[arg(short, long, value_name = "PCD_FILE")]
|
||||
pcd: Option<PathBuf>,
|
||||
/// Wonder Card title
|
||||
@@ -117,6 +117,9 @@ enum Command {
|
||||
/// Wonder Card ID
|
||||
#[arg(short, long, value_name = "ID")]
|
||||
card_id: Option<u16>,
|
||||
/// Gift instance, use id from item, pokewalker area, poketch app, rule, seal, accessory or unknown
|
||||
#[arg(long, value_name = "GIFT INSTANCE ID")]
|
||||
gift_instance: Option<u16>,
|
||||
/// Games to distribute to
|
||||
#[arg(short, long, value_name = "GAMES")]
|
||||
games: Option<Vec<Game>>,
|
||||
|
||||
26
src/pcd.rs
26
src/pcd.rs
@@ -22,6 +22,7 @@ pub const PCD_FRAGMENT_LENGTH: usize = PCD_EXTENDED_LENGTH / (PCD_FRAGMENTS - 1)
|
||||
|
||||
/// Attribute const offsets are absolute to pcd raw data
|
||||
pub const PCD_CARD_TYPE_OFFSET: usize = 0x0;
|
||||
pub const PCD_CARD_GIFT_INSTANCE_OFFSET: usize = 0x4;
|
||||
pub const PCD_TITLE_OFFSET: usize = 0x104;
|
||||
|
||||
pub const PCD_CARD_ID_OFFSET: usize = 0x150;
|
||||
@@ -137,6 +138,7 @@ fn serialize_games(games: &[Game]) -> u16 {
|
||||
pub struct Deserialized {
|
||||
pub title: String,
|
||||
pub card_type: CardType,
|
||||
pub gift_instance: u16,
|
||||
pub card_id: u16,
|
||||
pub games: Vec<Game>,
|
||||
pub comment: String,
|
||||
@@ -212,18 +214,28 @@ impl From<PCD<Raw>> for PCD<Partitioned> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get basic information from raw pcd data.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// `(CardType, gift_instance)`
|
||||
pub fn pgt_info(pgt: &[u8; PCD_PGT_LENGTH]) -> (CardType, u16) {
|
||||
(CardType::try_from(pgt[0]).unwrap_or(Unknown), u16::from_le_bytes([pgt[PCD_CARD_GIFT_INSTANCE_OFFSET], pgt[PCD_CARD_GIFT_INSTANCE_OFFSET + 1]]))
|
||||
}
|
||||
|
||||
impl PCD<Partitioned> {
|
||||
pub fn deserialize(self) -> PCD<Deserialized> {
|
||||
|
||||
// self.state.header
|
||||
let header: Vec<u16> = self.state.header.chunks_exact(2).map(|c| u16::from_le_bytes([c[0], c[1]])).collect();
|
||||
let card_data: Vec<u16> = self.state.card_data.chunks_exact(2).map(|c| u16::from_le_bytes([c[0], c[1]])).collect();
|
||||
|
||||
let pgt = pgt_info(&self.state.pgt);
|
||||
|
||||
let icons_offset_rela = (PCD_ICONS_OFFSET - PCD_COMMENT_OFFSET) / 2;
|
||||
|
||||
let des = Deserialized {
|
||||
title: (&first_str(&self.state.header, PCD_TITLE_MAX_LENGTH)).try_into().unwrap_or_else(|e: DecodeError| e.escaped),
|
||||
card_type: CardType::try_from(self.state.pgt[0]).unwrap_or(Unknown),
|
||||
card_type: pgt.0,
|
||||
gift_instance: pgt.1,
|
||||
card_id: header[(PCD_CARD_ID_OFFSET - PCD_TITLE_OFFSET) / 2],
|
||||
games: Game::parse(header[(PCD_GAMES_OFFSET - PCD_TITLE_OFFSET) / 2].rotate_left(8)),
|
||||
comment: (&first_str(&self.state.card_data, PCD_COMMENT_MAX_LENGTH)).try_into().unwrap_or_else(|e: DecodeError| e.escaped),
|
||||
@@ -259,6 +271,7 @@ impl PCD<Deserialized> {
|
||||
state: Deserialized {
|
||||
title: "".to_string(),
|
||||
card_type: CardType::None,
|
||||
gift_instance: 0,
|
||||
card_id: 0,
|
||||
games: vec![],
|
||||
comment: "".to_string(),
|
||||
@@ -277,6 +290,7 @@ impl PCD<Deserialized> {
|
||||
let mut pgt = des.pgt.clone();
|
||||
|
||||
pgt[PCD_CARD_TYPE_OFFSET] = des.card_type as u8;
|
||||
pgt[PCD_CARD_GIFT_INSTANCE_OFFSET..PCD_CARD_GIFT_INSTANCE_OFFSET + 2].copy_from_slice(&des.gift_instance.to_le_bytes());
|
||||
|
||||
put_str(&mut header, &des.title, PCD_TITLE_MAX_LENGTH);
|
||||
header[PCD_CARD_ID_OFFSET..PCD_CARD_ID_OFFSET + 2].copy_from_slice(&des.card_id.to_le_bytes());
|
||||
@@ -296,7 +310,7 @@ impl PCD<Deserialized> {
|
||||
}
|
||||
|
||||
/// Calculates the received date and returns it as a tuple.
|
||||
/// Representation: (year, month of year, day of month)
|
||||
/// Representation: (year, month of year, day of month)
|
||||
pub fn received(&self) -> (u16, u8, u8) {
|
||||
let approx_years = self.state.received / 365; // works until 2365, max year un u16 days is 2179
|
||||
let corrected_days = self.state.received - approx_years / 4 + approx_years / 100 - approx_years / 400 - 1;
|
||||
@@ -339,11 +353,11 @@ impl Display for PCD<Deserialized> {
|
||||
);
|
||||
let (year, month, day) = self.received();
|
||||
write!(f, "title: {}\ticons: {}({}),{}({}),{}({})\n\
|
||||
type: {:?}\tcard ID: {}\n\n\
|
||||
type: {:?}\tinstance: {}\tcard ID: {}\n\n\
|
||||
{}\n\n\
|
||||
games: {:?}\n\
|
||||
redistribution limit: {}{}\n\
|
||||
received: {}-{:02}-{:02}\n", self.state.title, icon_names.0, self.state.icons.0, icon_names.1, self.state.icons.1, icon_names.2, self.state.icons.2, self.state.card_type, self.state.card_id, self.state.comment, self.state.games, self.state.redistribution, if self.state.redistribution == 0xff { "(unlimited)" } else { "" }, year, month, day)
|
||||
received: {}-{:02}-{:02}\n", self.state.title, icon_names.0, self.state.icons.0, icon_names.1, self.state.icons.1, icon_names.2, self.state.icons.2, self.state.card_type, self.state.gift_instance, self.state.card_id, self.state.comment, self.state.games, self.state.redistribution, if self.state.redistribution == 0xff { "(unlimited)" } else { "" }, year, month, day)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user