Rework for custom pcb

This commit is contained in:
shylie 2026-02-24 16:50:15 -05:00
parent 5b985bd5ff
commit 2f8a9d7e5f

View File

@ -1,46 +1,48 @@
use image::imageops::FilterType;
use image::{EncodableLayout, ImageError};
use nusb::transfer::{Bulk, Out, TransferError};
use nusb::MaybeFuture;
use nusb::{list_devices, ErrorKind};
use nusb::transfer::{Bulk, In, Out, TransferError};
use nusb::{ErrorKind, list_devices};
use reqwest::header::ACCEPT;
use serde::Deserialize;
use std::env;
use std::fmt::{Display, Formatter};
use std::io::Write;
use std::io::{Read, Write};
use std::time::Duration;
#[derive(Debug)]
struct Error {
message: &'static str
message: String,
}
impl From<nusb::Error> for Error {
fn from(value: nusb::Error) -> Self {
Error{
Error {
message: match value.kind() {
ErrorKind::Disconnected => "Device disconnected",
ErrorKind::Busy => "Device busy",
ErrorKind::PermissionDenied => "No permission to access device",
ErrorKind::NotFound => "Device not found",
ErrorKind::Unsupported => "Device not supported",
_ => "Unknown error"
}
ErrorKind::Disconnected => "Device disconnected".to_owned(),
ErrorKind::Busy => "Device busy".to_owned(),
ErrorKind::PermissionDenied => "No permission to access device".to_owned(),
ErrorKind::NotFound => "Device not found".to_owned(),
ErrorKind::Unsupported => "Device not supported".to_owned(),
ErrorKind::Other => value.to_string(),
_ => "Unknown error".to_owned(),
},
}
}
}
impl From<TransferError> for Error {
fn from(_: TransferError) -> Self {
Error{
message: "Error during transfer"
fn from(value: TransferError) -> Self {
Error {
message: value.to_string(),
}
}
}
impl From<std::io::Error> for Error {
fn from(_: std::io::Error) -> Self {
fn from(value: std::io::Error) -> Self {
Error {
message: "Error during transfer"
message: value.to_string(),
}
}
}
@ -48,7 +50,7 @@ impl From<std::io::Error> for Error {
impl From<ImageError> for Error {
fn from(_: ImageError) -> Self {
Error {
message: "Error reading image"
message: "Error reading image".to_owned(),
}
}
}
@ -56,7 +58,7 @@ impl From<ImageError> for Error {
impl From<reqwest::Error> for Error {
fn from(_: reqwest::Error) -> Self {
Error {
message: "Error retrieving card info"
message: "Error retrieving card info".to_owned(),
}
}
}
@ -69,7 +71,7 @@ impl Display for Error {
#[derive(Deserialize)]
struct ImageUris {
pub png: String
pub png: String,
}
#[derive(Deserialize)]
@ -77,17 +79,20 @@ struct ScryfallCardInfo {
pub cmc: f32,
pub colors: Vec<String>,
pub name: String,
pub image_uris: ImageUris
pub image_uris: ImageUris,
}
struct RelevantCardInfo {
pub cmc: u8,
pub colors: u8,
pub name: String,
pub image: Vec<u8>
pub image: Vec<u8>,
}
fn get_relevant_card_info(set_code: &str, collector_number: &str) -> Result<RelevantCardInfo, Error> {
fn get_relevant_card_info(
set_code: &str,
collector_number: &str,
) -> Result<RelevantCardInfo, Error> {
let client = reqwest::blocking::ClientBuilder::new()
.user_agent("Digital MtG Token/0.1.0")
.build()?;
@ -95,23 +100,28 @@ fn get_relevant_card_info(set_code: &str, collector_number: &str) -> Result<Rele
let card_uri = format!("https://api.scryfall.com/cards/{set_code}/{collector_number}");
let response = client.get(card_uri).header(ACCEPT, "*/*").send()?;
let card_info: ScryfallCardInfo = response.json()?;
let card_image = client.get(&card_info.image_uris.png).header(ACCEPT, "*/*").send()?.bytes()?.to_vec();
let card_image = client
.get(&card_info.image_uris.png)
.header(ACCEPT, "*/*")
.send()?
.bytes()?
.to_vec();
let mut colors = 0;
for color in &card_info.colors {
match color.as_str() {
"W" => {
colors |= 0b10000;
},
}
"U" => {
colors |= 0b1000;
},
}
"B" => {
colors |= 0b100;
},
}
"R" => {
colors |= 0b10;
},
}
"G" => {
colors |= 0b1;
}
@ -123,14 +133,17 @@ fn get_relevant_card_info(set_code: &str, collector_number: &str) -> Result<Rele
cmc: card_info.cmc.trunc() as u8,
colors,
name: card_info.name.to_ascii_lowercase(),
image: card_image
image: card_image,
})
}
fn main() -> Result<(), Error> {
let device = list_devices().wait()?
let device = list_devices()
.wait()?
.find(|dev| dev.vendor_id() == 0xCAFE && dev.product_id() == 0xCA6D)
.ok_or(Error { message: "Device not found" })?;
.ok_or(Error {
message: "Device not found".to_owned(),
})?;
let device = device.open().wait()?;
let interface = device.claim_interface(1).wait()?;
@ -138,44 +151,107 @@ fn main() -> Result<(), Error> {
let mut writer = interface.endpoint::<Bulk, Out>(0x01)?.writer(4096);
let args: Vec<String> = env::args().collect();
if args.len() == 1 {
writer.write(&[0x42])?;
writer.flush()?;
if args.len() > 1 {
match args[1].as_str() {
"erase" => {
writer.write_all(&[0x42])?;
writer.flush()?;
}
"test" => {
writer.write_all(&[0x69])?;
writer.flush()?;
let mut reader = interface
.endpoint::<Bulk, In>(0x81)?
.reader(128)
.with_read_timeout(Duration::from_millis(2500));
let mut temp = [0u8; 256];
let mut buf: Vec<u8> = vec![];
loop {
match reader.read(&mut temp) {
Ok(n) => {
if n > 0 {
buf.extend_from_slice(&temp[..n]);
println!("extended: {}", n);
}
}
Err(e) if e.kind() == std::io::ErrorKind::TimedOut => break,
r => {
r?;
}
}
}
println!("buflen: {}", buf.len());
let mut card_index = 0;
for (i, val) in buf.iter().enumerate() {
print!("{:02x}", val);
if i > 0 && i % 256 == 0 {
println!();
let b0 = (buf[256 * card_index] as u16)
+ ((buf[256 * card_index + 1] as u16) << 8);
let is_used = b0 & 0x01 > 0;
let name_length = (b0 >> 1) & 0x1F;
let cmc = (b0 >> 6) & 0x1F;
let colors = (b0 >> 11) & 0x1F;
let name =
&buf[256 * card_index + 8..256 * card_index + 8 + name_length as usize];
for ch in name {
print!("{}", *ch as char);
}
println!(" ({})", name_length);
println!("Used: {}", is_used);
println!("CMC: {}", cmc);
println!("Colors: {}", colors);
println!();
card_index += 1;
}
}
println!();
}
_ => {
let slash_position = args[1].find("/").ok_or(Error {
message: "Error retrieving card info".to_owned(),
})?;
let set_code = &args[1][0..slash_position];
let collectors_number = &args[1][slash_position + 1..];
let relevant_info = get_relevant_card_info(set_code, collectors_number)?;
// converted mana cost
writer.write_all(&[relevant_info.cmc])?;
// colors
writer.write_all(&[relevant_info.colors])?;
let name = relevant_info.name.as_str();
let name_len = if name.len() >= 32 { 31 } else { name.len() };
// length of card name
writer.write_all(&[name_len as u8])?;
// name of card
writer.write_all(&name.as_bytes()[0..name_len])?;
let img = image::load_from_memory(relevant_info.image.as_bytes())?;
let img = img.crop_imm(28, 28, img.width() - 56, img.height() - 56);
let img = img.resize_exact(240, 320, FilterType::Lanczos3);
let img = img.into_rgb8();
// image of card
writer.write_all(img.as_flat_samples().samples)?;
writer.flush()?;
}
}
Ok(())
} else {
Err(Error {
message: "Incorrect usage".to_owned(),
})
}
else {
let slash_position = args[1].find("/").ok_or(Error {
message: "Error retrieving card info"
})?;
let set_code = &args[1][0..slash_position];
let collectors_number = &args[1][slash_position + 1..];
let relevant_info = get_relevant_card_info(set_code, collectors_number)?;
// converted mana cost
writer.write(&[relevant_info.cmc])?;
// colors
writer.write(&[relevant_info.colors])?;
let name = relevant_info.name.as_str();
let name_len = if name.len() >= 32 { 31 } else { name.len() };
// length of card name
writer.write(&[name_len as u8])?;
// name of card
writer.write(name[0..name_len].as_bytes())?;
let img = image::load_from_memory(relevant_info.image.as_bytes())?;
let img = img.crop_imm(28, 28, img.width() - 56, img.height() - 56);
let img = img.resize_exact(240, 320, FilterType::Lanczos3);
let img = img.into_rgb8();
// image of card
writer.write_all(img.as_flat_samples().samples)?;
writer.flush()?;
}
Ok(())
}