mirror of
https://github.com/Zenithsiz/dcb.git
synced 2026-02-03 16:16:33 +00:00
Added minimal deck editor.
This commit is contained in:
parent
f384b95825
commit
98e9bf8a76
@ -25,5 +25,6 @@ members = [
|
||||
"dcb-tools/dcb-unbin",
|
||||
"dcb-tools/dcb-unmsd",
|
||||
"dcb-tools/dcb-card-editor",
|
||||
"dcb-tools/dcb-deck-editor",
|
||||
"dcb-tools/dcb-file-editor",
|
||||
]
|
||||
|
||||
32
dcb-tools/dcb-deck-editor/Cargo.toml
Normal file
32
dcb-tools/dcb-deck-editor/Cargo.toml
Normal file
@ -0,0 +1,32 @@
|
||||
[package]
|
||||
name = "dcb-deck-editor"
|
||||
version = "0.1.0"
|
||||
authors = ["Filipe Rodrigues <filipejacintorodrigues1@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
# Dcb
|
||||
dcb = { path = "../../dcb" }
|
||||
dcb-bytes = { path = "../../dcb-bytes" }
|
||||
dcb-util = { path = "../../dcb-util" }
|
||||
dcb-cdrom-xa = { path = "../../dcb-cdrom-xa" }
|
||||
|
||||
# Gui
|
||||
eframe = { git = "https://github.com/emilk/egui" }
|
||||
|
||||
# Logging
|
||||
log = "0.4.14"
|
||||
simplelog = "0.10.0"
|
||||
|
||||
# Error
|
||||
anyhow = "1.0.40"
|
||||
|
||||
# Utils
|
||||
native-dialog = "0.5.5"
|
||||
either = "1.6.1"
|
||||
ref-cast = "1.0.6"
|
||||
derive_more = "0.99.13"
|
||||
strum = { version = "0.20.0", features = ["derive"] }
|
||||
|
||||
# Serde
|
||||
serde_yaml = "0.8.17"
|
||||
55
dcb-tools/dcb-deck-editor/src/ascii_text_buffer.rs
Normal file
55
dcb-tools/dcb-deck-editor/src/ascii_text_buffer.rs
Normal file
@ -0,0 +1,55 @@
|
||||
//! Ascii text buffer
|
||||
|
||||
use dcb_util::{ascii_str_arr::AsciiChar, AsciiStrArr};
|
||||
|
||||
/// An ascii text buffer
|
||||
#[derive(PartialEq, Default, Clone, Debug, derive_more::Display)]
|
||||
#[derive(ref_cast::RefCast)]
|
||||
#[repr(transparent)]
|
||||
pub struct AsciiTextBuffer<const N: usize>(pub AsciiStrArr<N>);
|
||||
|
||||
|
||||
// Truncates any extra characters and ignores non-ascii
|
||||
impl<const N: usize> From<String> for AsciiTextBuffer<N> {
|
||||
fn from(s: String) -> Self {
|
||||
let mut buffer = Self::default();
|
||||
for ch in s.chars() {
|
||||
match AsciiChar::from_ascii(ch) {
|
||||
Ok(ch) => match buffer.0.push(ch) {
|
||||
Ok(_) => continue,
|
||||
Err(_) => break,
|
||||
},
|
||||
Err(_) => continue,
|
||||
}
|
||||
}
|
||||
buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> From<AsciiTextBuffer<N>> for String {
|
||||
fn from(buffer: AsciiTextBuffer<N>) -> Self {
|
||||
buffer.as_ref().to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> AsRef<str> for AsciiTextBuffer<N> {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.0.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
// Note: In ascii, the character index is the same
|
||||
// as the byte index.
|
||||
impl<const N: usize> eframe::egui::widgets::TextBuffer for AsciiTextBuffer<N> {
|
||||
fn insert_text(&mut self, text: &str, ch_idx: usize) -> usize {
|
||||
text.chars()
|
||||
.filter_map(|ch| AsciiChar::from_ascii(ch).ok())
|
||||
.enumerate()
|
||||
.take_while(|&(idx, ch)| self.0.insert(ch_idx + idx, ch).is_ok())
|
||||
.count()
|
||||
}
|
||||
|
||||
fn delete_char_range(&mut self, ch_range: std::ops::Range<usize>) {
|
||||
self.0.drain_range(ch_range);
|
||||
}
|
||||
}
|
||||
379
dcb-tools/dcb-deck-editor/src/main.rs
Normal file
379
dcb-tools/dcb-deck-editor/src/main.rs
Normal file
@ -0,0 +1,379 @@
|
||||
//! Deck editor
|
||||
|
||||
// Features
|
||||
#![feature(array_map, with_options, format_args_capture, once_cell, never_type)]
|
||||
|
||||
// Modules
|
||||
pub mod ascii_text_buffer;
|
||||
|
||||
// Exports
|
||||
pub use ascii_text_buffer::AsciiTextBuffer;
|
||||
|
||||
// Imports
|
||||
use anyhow::Context;
|
||||
use dcb::{CardTable, Deck, DeckTable};
|
||||
use dcb_util::StrContainsCaseInsensitive;
|
||||
use eframe::{egui, epi, NativeOptions};
|
||||
use native_dialog::{FileDialog, MessageDialog, MessageType};
|
||||
use ref_cast::RefCast;
|
||||
use std::{
|
||||
fs,
|
||||
io::{self, Read, Seek},
|
||||
ops::Range,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
fn main() {
|
||||
// Initialize the logger
|
||||
simplelog::TermLogger::init(
|
||||
log::LevelFilter::Debug,
|
||||
simplelog::Config::default(),
|
||||
simplelog::TerminalMode::Stderr,
|
||||
simplelog::ColorChoice::Auto,
|
||||
)
|
||||
.expect("Unable to initialize logger");
|
||||
|
||||
// Crate the app and run it
|
||||
let app = DeckEditor::default();
|
||||
eframe::run_native(Box::new(app), NativeOptions::default());
|
||||
}
|
||||
|
||||
pub struct DeckEditor {
|
||||
/// File path
|
||||
file_path: Option<PathBuf>,
|
||||
|
||||
/// Loaded game
|
||||
loaded_game: Option<LoadedGame>,
|
||||
|
||||
/// Deck search
|
||||
deck_search: String,
|
||||
|
||||
/// All selected edit screens
|
||||
open_edit_screens: Vec<EditScreen>,
|
||||
}
|
||||
|
||||
impl DeckEditor {
|
||||
/// Card table offset
|
||||
pub const CARD_TABLE_OFFSET: u64 = 0x216d000;
|
||||
/// Card table size
|
||||
pub const CARD_TABLE_SIZE: u64 = 0x14958;
|
||||
/// Deck table offset
|
||||
pub const DECK_TABLE_OFFSET: u64 = 0x21a6800;
|
||||
/// Deck table size
|
||||
pub const DECK_TABLE_SIZE: u64 = 0x445a;
|
||||
|
||||
/// Parses the card table from file
|
||||
pub fn parse_card_table(file_path: &Path) -> Result<CardTable, anyhow::Error> {
|
||||
// Open the file
|
||||
let file = fs::File::open(file_path).context("Unable to open file")?;
|
||||
let mut file = dcb_cdrom_xa::CdRomCursor::new(file);
|
||||
|
||||
// Seek to the card file position and limit our reading to the file size
|
||||
file.seek(io::SeekFrom::Start(Self::CARD_TABLE_OFFSET))
|
||||
.context("Unable to seek to card table")?;
|
||||
let mut file = file.take(Self::CARD_TABLE_SIZE);
|
||||
|
||||
// Then parse it
|
||||
let card_table = CardTable::deserialize(&mut file).context("Unable to parse table")?;
|
||||
|
||||
Ok(card_table)
|
||||
}
|
||||
|
||||
/// Parses the deck table from file
|
||||
pub fn parse_deck_table(file_path: &Path) -> Result<DeckTable, anyhow::Error> {
|
||||
// Open the file
|
||||
let file = fs::File::open(file_path).context("Unable to open file")?;
|
||||
let mut file = dcb_cdrom_xa::CdRomCursor::new(file);
|
||||
|
||||
// Seek to the deck file position and limit our reading to the file size
|
||||
file.seek(io::SeekFrom::Start(Self::DECK_TABLE_OFFSET))
|
||||
.context("Unable to seek to deck table")?;
|
||||
let mut file = file.take(Self::DECK_TABLE_SIZE);
|
||||
|
||||
// Then parse it
|
||||
let deck_table = DeckTable::deserialize(&mut file).context("Unable to parse table")?;
|
||||
|
||||
Ok(deck_table)
|
||||
}
|
||||
|
||||
/// Saves the deck table to file
|
||||
pub fn save_deck_table(file_path: &Path, deck_table: &DeckTable) -> Result<(), anyhow::Error> {
|
||||
// Open the file
|
||||
let file = fs::File::with_options()
|
||||
.write(true)
|
||||
.open(file_path)
|
||||
.context("Unable to open file")?;
|
||||
let mut file = dcb_cdrom_xa::CdRomCursor::new(file);
|
||||
|
||||
// Seek to the deck file position and limit our writing to the file size
|
||||
file.seek(io::SeekFrom::Start(Self::DECK_TABLE_OFFSET))
|
||||
.context("Unable to seek to deck table")?;
|
||||
let mut file = dcb_util::WriteTake::new(file, Self::DECK_TABLE_SIZE);
|
||||
|
||||
// Then serialize it
|
||||
deck_table.serialize(&mut file).context("Unable to serialize table")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the digimon's indexes
|
||||
pub fn digimon_idxs(card_table: &CardTable) -> Range<usize> {
|
||||
0..card_table.digimons.len()
|
||||
}
|
||||
|
||||
/// Returns the item's indexes
|
||||
pub fn item_idxs(card_table: &CardTable) -> Range<usize> {
|
||||
card_table.digimons.len()..(card_table.digimons.len() + card_table.items.len())
|
||||
}
|
||||
|
||||
/// Returns the digivolve's indexes
|
||||
pub fn digivolve_idxs(card_table: &CardTable) -> Range<usize> {
|
||||
(card_table.digimons.len() + card_table.items.len())..
|
||||
(card_table.digimons.len() + card_table.items.len() + card_table.digivolves.len())
|
||||
}
|
||||
|
||||
/// Returns a card given it's index
|
||||
pub fn get_card_from_idx(card_table: &mut CardTable, idx: usize) -> Card {
|
||||
let digimons_len = card_table.digimons.len();
|
||||
let items_len = card_table.items.len();
|
||||
|
||||
if Self::digimon_idxs(card_table).contains(&idx) {
|
||||
Card::Digimon(&mut card_table.digimons[idx])
|
||||
} else if Self::item_idxs(card_table).contains(&idx) {
|
||||
Card::Item(&mut card_table.items[idx - digimons_len])
|
||||
} else if Self::digivolve_idxs(card_table).contains(&idx) {
|
||||
Card::Digivolve(&mut card_table.digivolves[idx - digimons_len - items_len])
|
||||
} else {
|
||||
panic!("Invalid card index");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DeckEditor {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
file_path: None,
|
||||
loaded_game: None,
|
||||
deck_search: String::new(),
|
||||
open_edit_screens: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl epi::App for DeckEditor {
|
||||
fn update(&mut self, ctx: &egui::CtxRef, frame: &mut epi::Frame<'_>) {
|
||||
let Self {
|
||||
file_path,
|
||||
loaded_game,
|
||||
deck_search,
|
||||
open_edit_screens,
|
||||
} = self;
|
||||
|
||||
// Top panel
|
||||
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
||||
egui::menu::bar(ui, |ui| {
|
||||
egui::menu::menu(ui, "File", |ui| {
|
||||
// On open, ask the user and open the file
|
||||
if ui.button("Open").clicked() {
|
||||
let cur_dir_path = std::env::current_dir().expect("Unable to get current directory path");
|
||||
*file_path = FileDialog::new()
|
||||
.set_location(&cur_dir_path)
|
||||
.add_filter("Game file", &["bin"])
|
||||
.show_open_single_file()
|
||||
.expect("Unable to ask user for file");
|
||||
|
||||
// Then load the card table if we got a file
|
||||
if let Some(file_path) = file_path {
|
||||
match (
|
||||
Self::parse_card_table(file_path).context("Unable to load card table"),
|
||||
Self::parse_deck_table(file_path).context("Unable to load deck table"),
|
||||
) {
|
||||
(Ok(card_table), Ok(deck_table)) => {
|
||||
*loaded_game = Some(LoadedGame { card_table, deck_table });
|
||||
},
|
||||
(Err(err), _) => MessageDialog::new()
|
||||
.set_text(&format!("Unable to open file: {:?}", err))
|
||||
.set_type(MessageType::Error)
|
||||
.show_alert()
|
||||
.expect("Unable to alert user"),
|
||||
(_, Err(err)) => MessageDialog::new()
|
||||
.set_text(&format!("Unable to open file: {:?}", err))
|
||||
.set_type(MessageType::Error)
|
||||
.show_alert()
|
||||
.expect("Unable to alert user"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// On save, if we have a file, save it to there, else tell error
|
||||
if ui.button("Save").clicked() {
|
||||
match (&file_path, &mut *loaded_game) {
|
||||
(Some(file_path), Some(loaded_game)) => {
|
||||
match Self::save_deck_table(file_path, &loaded_game.deck_table) {
|
||||
Ok(()) => MessageDialog::new()
|
||||
.set_text("Successfully saved!")
|
||||
.set_type(MessageType::Info)
|
||||
.show_alert()
|
||||
.expect("Unable to alert user"),
|
||||
Err(err) => MessageDialog::new()
|
||||
.set_text(&format!("Unable to save file: {:?}", err))
|
||||
.set_type(MessageType::Error)
|
||||
.show_alert()
|
||||
.expect("Unable to alert user"),
|
||||
}
|
||||
},
|
||||
_ => MessageDialog::new()
|
||||
.set_text("You must first open a file to save")
|
||||
.set_type(MessageType::Warning)
|
||||
.show_alert()
|
||||
.expect("Unable to alert user"),
|
||||
}
|
||||
}
|
||||
|
||||
if ui.button("Quit").clicked() {
|
||||
frame.quit();
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
egui::menu::menu(ui, "Edit", |ui| {
|
||||
if loaded_game.is_some() && ui.button("Swap").clicked() {
|
||||
todo!();
|
||||
}
|
||||
});
|
||||
*/
|
||||
});
|
||||
});
|
||||
|
||||
egui::SidePanel::left("side_panel").show(ctx, |ui| {
|
||||
ui.heading("Deck list");
|
||||
|
||||
ui.vertical(|ui| {
|
||||
ui.label("Search");
|
||||
ui.text_edit_singleline(deck_search);
|
||||
});
|
||||
|
||||
// If we have a loaded game, display all decks
|
||||
if let Some(loaded_game) = &loaded_game {
|
||||
let names = loaded_game
|
||||
.deck_table
|
||||
.decks
|
||||
.iter()
|
||||
.map(|deck| deck.name)
|
||||
.enumerate()
|
||||
.map(|(idx, name)| (idx, format!("{idx}. {name}")))
|
||||
.filter(|(_, name)| name.contains_case_insensitive(deck_search));
|
||||
|
||||
egui::ScrollArea::auto_sized().show(ui, |ui| {
|
||||
for (idx, name) in names {
|
||||
// If clicked, open/close a new screen
|
||||
let screen_idx = open_edit_screens.iter().position(|screen| screen.deck_idx == idx);
|
||||
if ui.selectable_label(screen_idx.is_some(), name).clicked() {
|
||||
match screen_idx {
|
||||
Some(screen_idx) => {
|
||||
open_edit_screens.remove(screen_idx);
|
||||
},
|
||||
None => open_edit_screens.push(EditScreen { deck_idx: idx }),
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// For every screen, display it
|
||||
if let Some(loaded_game) = loaded_game {
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
let screens_len = open_edit_screens.len();
|
||||
for screen in open_edit_screens {
|
||||
let deck = &mut loaded_game.deck_table.decks[screen.deck_idx];
|
||||
let card_table = &mut loaded_game.card_table;
|
||||
|
||||
let total_available_width = ui.available_width();
|
||||
let default_width = total_available_width / (screens_len as f32);
|
||||
egui::SidePanel::left((screen as *const _, "panel", default_width.to_bits()))
|
||||
.default_width(default_width)
|
||||
.show(ctx, |ui| {
|
||||
// Header for the card
|
||||
ui.vertical(|ui| {
|
||||
ui.heading(deck.name.as_str());
|
||||
ui.separator();
|
||||
});
|
||||
|
||||
egui::ScrollArea::auto_sized().show(ui, |ui| {
|
||||
self::render_deck(ui, deck, card_table);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn on_exit(&mut self) {
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
"Dcb deck editor"
|
||||
}
|
||||
}
|
||||
|
||||
/// An edit screen
|
||||
pub struct EditScreen {
|
||||
/// Currently selected deck
|
||||
deck_idx: usize,
|
||||
}
|
||||
|
||||
/// Loaded game
|
||||
pub struct LoadedGame {
|
||||
/// Card table
|
||||
card_table: CardTable,
|
||||
|
||||
/// Deck table
|
||||
deck_table: DeckTable,
|
||||
}
|
||||
|
||||
/// Digimon, Item or digivolve
|
||||
pub enum Card<'a> {
|
||||
Digimon(&'a mut dcb::Digimon),
|
||||
Item(&'a mut dcb::Item),
|
||||
Digivolve(&'a mut dcb::Digivolve),
|
||||
}
|
||||
|
||||
impl<'a> Card<'a> {
|
||||
/// Returns the name of this card
|
||||
pub fn name(&self) -> &str {
|
||||
match self {
|
||||
Card::Digimon(digimon) => digimon.name.as_str(),
|
||||
Card::Item(item) => item.name.as_str(),
|
||||
Card::Digivolve(digivolve) => digivolve.name.as_str(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Renders a deck
|
||||
fn render_deck(ui: &mut egui::Ui, deck: &mut Deck, card_table: &mut CardTable) {
|
||||
// Name
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Name");
|
||||
ui.text_edit_singleline(AsciiTextBuffer::ref_cast_mut(&mut deck.name));
|
||||
});
|
||||
|
||||
// Owner
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Owner");
|
||||
ui.text_edit_singleline(AsciiTextBuffer::ref_cast_mut(&mut deck.owner));
|
||||
});
|
||||
|
||||
ui.group(|ui| {
|
||||
ui.label("Cards");
|
||||
for card_id in &mut deck.cards {
|
||||
ui.horizontal(|ui| {
|
||||
let card = DeckEditor::get_card_from_idx(card_table, usize::from(card_id.0));
|
||||
|
||||
ui.add(egui::Slider::new(&mut card_id.0, 0..=300).clamp_to_range(true));
|
||||
ui.label(card.name());
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -8,9 +8,10 @@ pub use error::{DeserializeError, SerializeError};
|
||||
|
||||
// Imports
|
||||
use crate::Deck;
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use dcb_bytes::Bytes;
|
||||
use dcb_io::GameFile;
|
||||
use std::io;
|
||||
use dcb_util::array_split_mut;
|
||||
use std::{convert::TryInto, io};
|
||||
|
||||
/// The decks table, where all decks are stored
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
@ -18,7 +19,7 @@ use std::io;
|
||||
#[allow(clippy::unsafe_derive_deserialize)] // False positive
|
||||
pub struct Table {
|
||||
/// All decks
|
||||
decks: Vec<Deck>,
|
||||
pub decks: Vec<Deck>,
|
||||
}
|
||||
|
||||
// Constants
|
||||
@ -28,27 +29,15 @@ impl Table {
|
||||
/// The magic in the table header
|
||||
/// = "33KD"
|
||||
pub const HEADER_MAGIC: u32 = 0x444b3033;
|
||||
/*
|
||||
/// The max size of the deck table
|
||||
// TODO: Verify this
|
||||
pub const MAX_BYTE_SIZE: usize = 0x4452;
|
||||
/// The start address of the decks table
|
||||
const START_ADDRESS: Data = Data::from_u64(0x21a6800);
|
||||
*/
|
||||
}
|
||||
|
||||
impl Table {
|
||||
/// Deserializes the deck table from `file`.
|
||||
pub fn deserialize<R: io::Read>(_file: &mut GameFile<R>) -> Result<Self, DeserializeError> {
|
||||
todo!();
|
||||
/*
|
||||
// Seek to the beginning of the deck table
|
||||
file.seek(std::io::SeekFrom::Start(Self::START_ADDRESS.as_u64()))
|
||||
.map_err(DeserializeError::Seek)?;
|
||||
|
||||
pub fn deserialize<R: io::Read>(file: &mut R) -> Result<Self, DeserializeError> {
|
||||
// Read header
|
||||
let mut header_bytes = [0u8; Self::HEADER_BYTE_SIZE];
|
||||
file.read_exact(&mut header_bytes).map_err(DeserializeError::ReadHeader)?;
|
||||
file.read_exact(&mut header_bytes)
|
||||
.map_err(DeserializeError::ReadHeader)?;
|
||||
|
||||
// Check if the magic is right
|
||||
let magic = LittleEndian::read_u32(&header_bytes[0x0..0x4]);
|
||||
@ -60,17 +49,13 @@ impl Table {
|
||||
let decks_count: usize = header_bytes[0x4].into();
|
||||
log::trace!("Found {decks_count} decks");
|
||||
|
||||
// If there are too many decks, return Err
|
||||
if decks_count * std::mem::size_of::<<Deck as Bytes>::ByteArray>() > Self::MAX_BYTE_SIZE {
|
||||
return Err(DeserializeError::TooManyDecks { decks_count });
|
||||
}
|
||||
|
||||
// Then get each deck
|
||||
let mut decks = vec![];
|
||||
for id in 0..decks_count {
|
||||
// Read all bytes of the deck
|
||||
let mut bytes = [0; 0x6e];
|
||||
file.read_exact(&mut bytes).map_err(|err| DeserializeError::ReadDeck { id, err })?;
|
||||
file.read_exact(&mut bytes)
|
||||
.map_err(|err| DeserializeError::ReadDeck { id, err })?;
|
||||
|
||||
// And try to serialize the deck
|
||||
let deck = Deck::from_bytes(&bytes).map_err(|err| DeserializeError::DeserializeDeck { id, err })?;
|
||||
@ -84,25 +69,10 @@ impl Table {
|
||||
|
||||
// And return the table
|
||||
Ok(Self { decks })
|
||||
*/
|
||||
}
|
||||
|
||||
/// Serializes the deck table to `file`
|
||||
pub fn serialize<R: io::Write>(&self, _file: &mut GameFile<R>) -> Result<(), SerializeError> {
|
||||
let _ = self;
|
||||
todo!();
|
||||
/*
|
||||
// If the total table size is bigger than the max, return Err
|
||||
if self.decks.len() * std::mem::size_of::<<Deck as Bytes>::ByteArray>() > Self::MAX_BYTE_SIZE {
|
||||
return Err(SerializeError::TooManyDecks {
|
||||
decks_count: self.decks.len(),
|
||||
});
|
||||
}
|
||||
|
||||
// Seek to the beginning of the deck table
|
||||
file.seek(std::io::SeekFrom::Start(Self::START_ADDRESS.as_u64()))
|
||||
.map_err(SerializeError::Seek)?;
|
||||
|
||||
pub fn serialize<R: io::Write>(&self, file: &mut R) -> Result<(), SerializeError> {
|
||||
// Write header
|
||||
let mut header_bytes = [0u8; 0x8];
|
||||
let header = array_split_mut!(&mut header_bytes,
|
||||
@ -129,11 +99,11 @@ impl Table {
|
||||
deck.to_bytes(&mut bytes).into_ok();
|
||||
|
||||
// And write them to file
|
||||
file.write(&bytes).map_err(|err| SerializeError::WriteDeck { id, err })?;
|
||||
file.write(&bytes)
|
||||
.map_err(|err| SerializeError::WriteDeck { id, err })?;
|
||||
}
|
||||
|
||||
// And return Ok
|
||||
Ok(())
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,16 +1,12 @@
|
||||
//! Errors
|
||||
|
||||
// Imports
|
||||
use super::{Bytes, Deck, Table};
|
||||
use super::Table;
|
||||
use crate::deck::deck;
|
||||
|
||||
/// Error type for [`Table::deserialize`]
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum DeserializeError {
|
||||
/// Unable to seek game file
|
||||
#[error("Unable to seek game file to card table")]
|
||||
Seek(#[source] std::io::Error),
|
||||
|
||||
/// Unable to read table header
|
||||
#[error("Unable to read table header")]
|
||||
ReadHeader(#[source] std::io::Error),
|
||||
@ -26,16 +22,6 @@ pub enum DeserializeError {
|
||||
magic: u32,
|
||||
},
|
||||
|
||||
/// There were too many decks
|
||||
#[error(
|
||||
"Too many decks in table ({decks_count} decks, {} / 0 bytes max)",
|
||||
decks_count * std::mem::size_of::<<Deck as Bytes>::ByteArray>(),
|
||||
)]
|
||||
TooManyDecks {
|
||||
/// Number of decks
|
||||
decks_count: usize,
|
||||
},
|
||||
|
||||
/// Could not read a deck entry
|
||||
#[error("Unable to read deck entry with id {}", id)]
|
||||
ReadDeck {
|
||||
@ -62,20 +48,6 @@ pub enum DeserializeError {
|
||||
/// Error type for [`Table::serialize`]
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum SerializeError {
|
||||
/// Unable to seek game file
|
||||
#[error("Unable to seek game file to card table")]
|
||||
Seek(#[source] std::io::Error),
|
||||
|
||||
/// There were too many decks
|
||||
#[error(
|
||||
"Too many decks in table ({decks_count} decks, {} / 0 bytes max)",
|
||||
decks_count * std::mem::size_of::<<Deck as Bytes>::ByteArray>(),
|
||||
)]
|
||||
TooManyDecks {
|
||||
/// Number of decks
|
||||
decks_count: usize,
|
||||
},
|
||||
|
||||
/// Unable to write table header
|
||||
#[error("Unable to write table header")]
|
||||
WriteHeader(#[source] std::io::Error),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user