mirror of
https://github.com/Zenithsiz/dcb.git
synced 2026-02-09 11:48:16 +00:00
Moved panic.rs to be shared among all binaries.
Moved `init_logger` to `logger.rs`. Added a `card-editor` binary based on imgui (Abandoned).
This commit is contained in:
886
Cargo.lock
generated
886
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
22
Cargo.toml
22
Cargo.toml
@@ -12,10 +12,22 @@ path = "src/extractor/main.rs"
|
||||
name = "patcher"
|
||||
path = "src/patcher/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "card-editor"
|
||||
path = "src/card-editor/main.rs"
|
||||
|
||||
[dependencies]
|
||||
# Dcb
|
||||
dcb = { path = "../dcb" }
|
||||
|
||||
# Text
|
||||
ascii = "1.0"
|
||||
strsim = "0.10"
|
||||
|
||||
# Helpers
|
||||
float-ord = "0.2"
|
||||
itertools = "0.9"
|
||||
|
||||
# Cmd
|
||||
clap = "2.33"
|
||||
|
||||
@@ -33,6 +45,16 @@ derive_more = "0.99"
|
||||
# Serde
|
||||
serde_yaml = "0.8"
|
||||
|
||||
# Window / Ui
|
||||
gfx = "0.18"
|
||||
gfx_device_gl = "0.16"
|
||||
gfx_window_glutin = "0.31"
|
||||
glutin = "0.21"
|
||||
#image = "0.23"
|
||||
imgui = "0.3"
|
||||
imgui-gfx-renderer = "0.3"
|
||||
imgui-winit-support = { version = "0.3", default-features = false, features = ["winit-19"] }
|
||||
|
||||
# Build dependencies in release mode
|
||||
[profile.dev.package."*"]
|
||||
opt-level = 2
|
||||
|
||||
41
src/card-editor/cli.rs
Normal file
41
src/card-editor/cli.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
//! Cli manager for the extractor
|
||||
|
||||
// Filesystem
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
// Clap
|
||||
use clap::{App as ClapApp, Arg as ClapArg};
|
||||
|
||||
/// Data from the command line
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub struct CliData {
|
||||
/// The data directory
|
||||
pub data_dir: PathBuf,
|
||||
}
|
||||
|
||||
impl CliData {
|
||||
/// Constructs all of the cli data given and returns it
|
||||
pub fn new() -> Self {
|
||||
// Get all matches from cli
|
||||
let matches = ClapApp::new("Dcb Card Editor")
|
||||
.version("0.0")
|
||||
.author("Filipe [...] <[...]@gmail.com>")
|
||||
.about("Edits card data from Digimon Digital Card Battle extracted files")
|
||||
.arg(
|
||||
ClapArg::with_name("INPUT")
|
||||
.help("Sets the Data directory to use")
|
||||
.short("i")
|
||||
.long("input")
|
||||
.index(1)
|
||||
.takes_value(true)
|
||||
.required(true),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
// Get the data dir as either an input or the current directory
|
||||
let data_dir = matches.value_of("INPUT").map_or_else(|| Path::new("."), Path::new).to_path_buf();
|
||||
|
||||
// Return the cli data
|
||||
Self { data_dir }
|
||||
}
|
||||
}
|
||||
282
src/card-editor/main.rs
Normal file
282
src/card-editor/main.rs
Normal file
@@ -0,0 +1,282 @@
|
||||
//! Data patches
|
||||
//!
|
||||
//! # Details
|
||||
//! Patches data to the game file from several other files.
|
||||
//!
|
||||
//! # Syntax
|
||||
//! The executable may be called as `./patcher <game file> <directory>`
|
||||
//!
|
||||
//! Use the command `./patcher --help` for more information.
|
||||
//!
|
||||
//! # Data patched
|
||||
//! Currently only the following is patched:
|
||||
//! - Card table
|
||||
|
||||
// Features
|
||||
#![feature(box_syntax, backtrace, panic_info_message, bool_to_option)]
|
||||
// Lints
|
||||
#![warn(clippy::restriction, clippy::pedantic, clippy::nursery)]
|
||||
#![allow(
|
||||
clippy::implicit_return, // We prefer implicit returns where possible
|
||||
clippy::module_name_repetitions, // This happens often due to separating things into modules finely
|
||||
clippy::wildcard_enum_match_arm, // We only use wildcards when we truly only care about some variants
|
||||
clippy::result_expect_used,
|
||||
clippy::option_expect_used, // We use expect when there is no alternative.
|
||||
clippy::used_underscore_binding, // Useful for macros and such
|
||||
clippy::integer_arithmetic,
|
||||
clippy::float_arithmetic, // We need to use numbers my guy
|
||||
clippy::as_conversions,
|
||||
clippy::cast_sign_loss,
|
||||
clippy::cast_possible_wrap,
|
||||
clippy::cast_possible_truncation, // Needed for converting between stuff
|
||||
clippy::items_after_statements,
|
||||
)]
|
||||
|
||||
// Modules
|
||||
mod cli;
|
||||
#[path = "../logger.rs"]
|
||||
mod logger;
|
||||
#[path = "../panic.rs"]
|
||||
mod panic;
|
||||
|
||||
// Gfx / GLutin
|
||||
use gfx::Device;
|
||||
use glutin::{Event, WindowEvent};
|
||||
|
||||
// Imgui
|
||||
use imgui::{Context, FontConfig, FontGlyphRanges, FontSource, ImString};
|
||||
use imgui_gfx_renderer::{Renderer, Shaders};
|
||||
use imgui_winit_support::{HiDpiMode, WinitPlatform};
|
||||
|
||||
// Errors
|
||||
use err_backtrace::ErrBacktraceExt;
|
||||
use err_ext::ResultExt;
|
||||
use err_panic::ErrorExtPanic;
|
||||
|
||||
// Itertools
|
||||
use itertools::Itertools;
|
||||
|
||||
// Dcb
|
||||
use dcb::game::{card::Table as CardTable, deck::Table as DeckTable};
|
||||
|
||||
#[allow(clippy::too_many_lines)] // TODO: Fix
|
||||
fn main() {
|
||||
// Initialize the logger and set the panic handler
|
||||
logger::init();
|
||||
std::panic::set_hook(box panic::log_handler);
|
||||
|
||||
// Get all data from cli
|
||||
let cli::CliData { data_dir } = cli::CliData::new();
|
||||
|
||||
// Load all data files
|
||||
let cards_table_file = std::fs::File::open(data_dir.join("cards.yaml")).panic_err_msg("Unable to open `cards.yaml`");
|
||||
let decks_table_file = std::fs::File::open(data_dir.join("decks.yaml")).panic_err_msg("Unable to open `decks.yaml`");
|
||||
|
||||
// Parse everything from yaml
|
||||
let mut cards_table: CardTable = serde_yaml::from_reader(cards_table_file).panic_err_msg("Unable to parse `cards.yaml`");
|
||||
let decks_table: DeckTable = serde_yaml::from_reader(decks_table_file).panic_err_msg("Unable to parse `decks.yaml`");
|
||||
|
||||
// Create a new window
|
||||
let mut events_loop = glutin::EventsLoop::new();
|
||||
let builder = glutin::WindowBuilder::new()
|
||||
.with_title("Dcb Card Editor")
|
||||
.with_dimensions(glutin::dpi::LogicalSize::new(1024.0, 768.0));
|
||||
let context = glutin::ContextBuilder::new().with_vsync(true);
|
||||
let (windowed_context, mut device, mut factory, mut main_color, mut main_depth) =
|
||||
gfx_window_glutin::init::<gfx::format::Rgba8, gfx::format::DepthStencil>(builder, context, &events_loop)
|
||||
.panic_err_msg("Failed to initialize graphics");
|
||||
let mut encoder: gfx::Encoder<_, _> = factory.create_command_buffer().into();
|
||||
let shaders = {
|
||||
let version = device.get_info().shading_language;
|
||||
if version.is_embedded {
|
||||
if version.major >= 3 {
|
||||
Shaders::GlSlEs300
|
||||
} else {
|
||||
Shaders::GlSlEs100
|
||||
}
|
||||
} else if version.major >= 4 {
|
||||
Shaders::GlSl400
|
||||
} else if version.major >= 3 {
|
||||
if version.minor >= 2 {
|
||||
Shaders::GlSl150
|
||||
} else {
|
||||
Shaders::GlSl130
|
||||
}
|
||||
} else {
|
||||
Shaders::GlSl110
|
||||
}
|
||||
};
|
||||
|
||||
// Create a new context for `imgui`
|
||||
let mut imgui_context = Context::create();
|
||||
let mut platform = WinitPlatform::init(&mut imgui_context);
|
||||
|
||||
// Add our font to imgui
|
||||
let hidpi_factor = platform.hidpi_factor();
|
||||
let font_size = (13.0 * hidpi_factor) as f32;
|
||||
imgui_context.fonts().add_font(&[
|
||||
FontSource::DefaultFontData {
|
||||
config: Some(FontConfig {
|
||||
size_pixels: font_size,
|
||||
..FontConfig::default()
|
||||
}),
|
||||
},
|
||||
FontSource::TtfData {
|
||||
data: include_bytes!("../../resources/OpenSans-Regular.ttf"),
|
||||
size_pixels: font_size,
|
||||
config: Some(FontConfig {
|
||||
rasterizer_multiply: 1.75,
|
||||
glyph_ranges: FontGlyphRanges::japanese(),
|
||||
..FontConfig::default()
|
||||
}),
|
||||
},
|
||||
]);
|
||||
imgui_context.io_mut().font_global_scale = (1.0 / hidpi_factor) as f32;
|
||||
|
||||
// Fix `sRGB` on style colors
|
||||
let style = imgui_context.style_mut();
|
||||
#[allow(clippy::indexing_slicing)] // False positive, this is a `[f32; 4]`
|
||||
for color in style.colors.iter_mut() {
|
||||
color[0] = color[0].powf(2.2);
|
||||
color[1] = color[1].powf(2.2);
|
||||
color[2] = color[2].powf(2.2);
|
||||
color[3] = 1.0 - (1.0 - color[3]).powf(2.2);
|
||||
}
|
||||
|
||||
// Attach the window to the platform
|
||||
let mut renderer = Renderer::init(&mut imgui_context, &mut factory, shaders).panic_err_msg("Unable to initialize renderer");
|
||||
platform.attach_window(imgui_context.io_mut(), windowed_context.window(), HiDpiMode::Rounded);
|
||||
|
||||
let mut last_frame = std::time::Instant::now();
|
||||
let mut run = true;
|
||||
|
||||
// Get all digimon names as `ImString`s
|
||||
let mut digimon_names: Vec<_> = cards_table.digimons.iter().map(|digimon| ImString::new(digimon.name.clone())).collect();
|
||||
let mut digimon_filter_idx = 0;
|
||||
let mut digimon_filter_search = ImString::new("");
|
||||
|
||||
while run {
|
||||
// Check for events
|
||||
events_loop.poll_events(|event| {
|
||||
platform.handle_event(imgui_context.io_mut(), windowed_context.window(), &event);
|
||||
|
||||
if let Event::WindowEvent { event, .. } = event {
|
||||
match event {
|
||||
WindowEvent::Resized(_) => {
|
||||
gfx_window_glutin::update_views(&windowed_context, &mut main_color, &mut main_depth);
|
||||
},
|
||||
WindowEvent::CloseRequested => run = false,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Prepare and execute ui frame
|
||||
let io = imgui_context.io_mut();
|
||||
platform
|
||||
.prepare_frame(io, windowed_context.window())
|
||||
.ignore_with_err(|err| log::warn!("Unable to prepare frame: {}", err));
|
||||
last_frame = io.update_delta_time(last_frame);
|
||||
let ui = imgui_context.frame();
|
||||
{
|
||||
ui.text("Digimon: ");
|
||||
|
||||
ui.input_text(imgui::im_str!("Digimon Filter"), &mut digimon_filter_search)
|
||||
.resize_buffer(true)
|
||||
.build();
|
||||
|
||||
let digimon_filters: Vec<_> = digimon_names
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, string)| {
|
||||
(
|
||||
float_ord::FloatOrd(1.0 - strsim::jaro(string.to_str(), digimon_filter_search.to_str())),
|
||||
string,
|
||||
idx,
|
||||
)
|
||||
})
|
||||
.sorted()
|
||||
.collect();
|
||||
|
||||
let digimon_filter_names: Vec<_> = digimon_filters
|
||||
.iter()
|
||||
.filter_map(|(value, string, _)| (digimon_filter_search.is_empty() || value.0 < 0.5).then_some(string))
|
||||
.collect();
|
||||
|
||||
ui.list_box(
|
||||
imgui::im_str!("Digimon List"),
|
||||
&mut digimon_filter_idx,
|
||||
digimon_filter_names.as_slice(),
|
||||
4,
|
||||
);
|
||||
|
||||
ui.separator();
|
||||
|
||||
/*
|
||||
if let Some((_, _, idx)) = digimon_filters.get(digimon_filter_idx as usize) {
|
||||
let idx = *idx;
|
||||
std::mem::drop(digimon_filters);
|
||||
if let Some(digimon_name_buffer) = digimon_names.get_mut(idx) {
|
||||
if ui.input_text(imgui::im_str!("Name"), digimon_name_buffer).resize_buffer(true).build() {
|
||||
if let Some(digimon) = cards_table.digimons.get_mut(idx) {
|
||||
match ascii::AsciiString::from_ascii(digimon_name_buffer.to_string()) {
|
||||
Ok(name) => {
|
||||
digimon.name = name;
|
||||
},
|
||||
Err(err) => {
|
||||
ui.text(format!("Unable to set digimon name:\n{}", err.err_backtrace()));
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
//if let Some(digimon_name) = digimon_names.iter_mut().enumerate().find(|(_, name)| name == )
|
||||
|
||||
/*
|
||||
if let Some((idx, digimon)) = cards_table.digimons.iter_mut().enumerate().find(|(_, digimon)| {
|
||||
digimon_filter_names
|
||||
.get(digimon_filter_idx as usize)
|
||||
.map_or(false, |&name| name.to_str() == digimon.name)
|
||||
}) {
|
||||
let mut name_buffer = ImString::new(digimon.name.clone());
|
||||
if ui.input_text(imgui::im_str!("Name"), &mut name_buffer).resize_buffer(true).build() {
|
||||
match ascii::AsciiString::from_ascii(name_buffer.to_string()) {
|
||||
Ok(name) => {
|
||||
digimon.name = name;
|
||||
if let Some(name) = digimon_names.get_mut(idx) {
|
||||
*name = name_buffer.clone();
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
ui.text(format!("Unable to set digimon name:\n{}", err.err_backtrace()));
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// Clear, render everything and flush it out
|
||||
encoder.clear(&main_color, [0.2, 0.2, 0.2, 1.0]);
|
||||
platform.prepare_render(&ui, windowed_context.window());
|
||||
renderer
|
||||
.render(&mut factory, &mut encoder, &mut main_color, ui.render())
|
||||
.ignore_with_err(|err| log::warn!("Unable to render: {}", err.err_backtrace()));
|
||||
encoder.flush(&mut device);
|
||||
windowed_context
|
||||
.swap_buffers()
|
||||
.ignore_with_err(|err| log::warn!("Unable to swap buffers: {}", err.err_backtrace()));
|
||||
device.cleanup();
|
||||
}
|
||||
|
||||
// Convert everything back to yaml
|
||||
let cards_table_yaml = serde_yaml::to_string(&cards_table).panic_err_msg("Unable to serialize cards table to yaml");
|
||||
let decks_table_yaml = serde_yaml::to_string(&decks_table).panic_err_msg("Unable to serialize decks table to yaml");
|
||||
|
||||
// Ouput all data to devices
|
||||
std::fs::write(&data_dir.join("cards.yaml"), cards_table_yaml).panic_err_msg("Unable to write cards table to file");
|
||||
std::fs::write(&data_dir.join("decks.yaml"), decks_table_yaml).panic_err_msg("Unable to write decks table to file");
|
||||
}
|
||||
@@ -28,6 +28,9 @@
|
||||
|
||||
// Modules
|
||||
mod cli;
|
||||
#[path = "../logger.rs"]
|
||||
mod logger;
|
||||
#[path = "../panic.rs"]
|
||||
mod panic;
|
||||
|
||||
// Exports
|
||||
@@ -40,12 +43,11 @@ use dcb::{
|
||||
};
|
||||
|
||||
// Errors
|
||||
use err_ext::ResultExt;
|
||||
use err_panic::ErrorExtPanic;
|
||||
|
||||
fn main() {
|
||||
// Initialize the logger and set the panic handler
|
||||
init_logger();
|
||||
logger::init();
|
||||
std::panic::set_hook(box panic::log_handler);
|
||||
|
||||
// Get all data from cli
|
||||
@@ -68,25 +70,3 @@ fn main() {
|
||||
std::fs::write(&output_dir.join("cards.yaml"), cards_table_yaml).panic_err_msg("Unable to write cards table to file");
|
||||
std::fs::write(&output_dir.join("decks.yaml"), decks_table_yaml).panic_err_msg("Unable to write decks table to file");
|
||||
}
|
||||
|
||||
/// Initializes the global logger
|
||||
fn init_logger() {
|
||||
use log::LevelFilter::{Info, Trace};
|
||||
use simplelog::{CombinedLogger, Config, SharedLogger, TermLogger, TerminalMode, WriteLogger};
|
||||
use std::convert::identity;
|
||||
/// The type of logger required to pass to `CombinedLogger::init`
|
||||
type BoxedLogger = Box<dyn SharedLogger>;
|
||||
|
||||
// All loggers to try and initialize
|
||||
let loggers: Vec<Option<BoxedLogger>> = vec![
|
||||
TermLogger::new(Info, Config::default(), TerminalMode::Mixed).map(|logger| BoxedLogger::from(logger)),
|
||||
std::fs::File::create("latest.log")
|
||||
.ok()
|
||||
.map(|file| WriteLogger::new(Trace, Config::default(), file))
|
||||
.map(|logger| BoxedLogger::from(logger)),
|
||||
];
|
||||
|
||||
// Filter all logger that actually work and initialize them
|
||||
CombinedLogger::init(loggers.into_iter().filter_map(identity).collect())
|
||||
.ignore_with_err(|_| log::warn!("Logger was already initialized at the start of the program"));
|
||||
}
|
||||
|
||||
27
src/logger.rs
Normal file
27
src/logger.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
//! Logger initialization
|
||||
|
||||
// Log
|
||||
use log::LevelFilter;
|
||||
use simplelog::{CombinedLogger, Config, SharedLogger, TermLogger, TerminalMode, WriteLogger};
|
||||
|
||||
// Error
|
||||
use err_ext::ResultExt;
|
||||
|
||||
/// The type of logger required to pass to `CombinedLogger::init`
|
||||
type BoxedLogger = Box<dyn SharedLogger>;
|
||||
|
||||
/// Initializes the global logger
|
||||
pub fn init() {
|
||||
// All loggers to try and initialize
|
||||
let loggers: Vec<Option<BoxedLogger>> = vec![
|
||||
TermLogger::new(LevelFilter::Warn, Config::default(), TerminalMode::Mixed).map(|logger| BoxedLogger::from(logger)),
|
||||
std::fs::File::create("latest.log")
|
||||
.ok()
|
||||
.map(|file| WriteLogger::new(LevelFilter::Trace, Config::default(), file))
|
||||
.map(|logger| BoxedLogger::from(logger)),
|
||||
];
|
||||
|
||||
// Filter all logger that actually work and initialize them
|
||||
CombinedLogger::init(loggers.into_iter().filter_map(std::convert::identity).collect())
|
||||
.ignore_with_err(|_| log::warn!("Logger was already initialized at the start of the program"));
|
||||
}
|
||||
@@ -27,6 +27,9 @@
|
||||
|
||||
// Modules
|
||||
mod cli;
|
||||
#[path = "../logger.rs"]
|
||||
mod logger;
|
||||
#[path = "../panic.rs"]
|
||||
mod panic;
|
||||
|
||||
// Exports
|
||||
@@ -39,12 +42,11 @@ use dcb::{
|
||||
};
|
||||
|
||||
// Errors
|
||||
use err_ext::ResultExt;
|
||||
use err_panic::ErrorExtPanic;
|
||||
|
||||
fn main() {
|
||||
// Initialize the logger and set the panic handler
|
||||
init_logger();
|
||||
logger::init();
|
||||
std::panic::set_hook(box panic::log_handler);
|
||||
|
||||
// Get all data from cli
|
||||
@@ -70,25 +72,3 @@ fn main() {
|
||||
cards_table.serialize(&mut game_file).panic_err_msg("Unable to serialize cards table");
|
||||
decks_table.serialize(&mut game_file).panic_err_msg("Unable to serialize decks table");
|
||||
}
|
||||
|
||||
/// Initializes the global logger
|
||||
fn init_logger() {
|
||||
use log::LevelFilter::{Info, Trace};
|
||||
use simplelog::{CombinedLogger, Config, SharedLogger, TermLogger, TerminalMode, WriteLogger};
|
||||
use std::convert::identity;
|
||||
/// The type of logger required to pass to `CombinedLogger::init`
|
||||
type BoxedLogger = Box<dyn SharedLogger>;
|
||||
|
||||
// All loggers to try and initialize
|
||||
let loggers: Vec<Option<BoxedLogger>> = vec![
|
||||
TermLogger::new(Info, Config::default(), TerminalMode::Mixed).map(|logger| BoxedLogger::from(logger)),
|
||||
std::fs::File::create("latest.log")
|
||||
.ok()
|
||||
.map(|file| WriteLogger::new(Trace, Config::default(), file))
|
||||
.map(|logger| BoxedLogger::from(logger)),
|
||||
];
|
||||
|
||||
// Filter all logger that actually work and initialize them
|
||||
CombinedLogger::init(loggers.into_iter().filter_map(identity).collect())
|
||||
.ignore_with_err(|_| log::warn!("Logger was already initialized at the start of the program"));
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
//! Panic handlers for this application
|
||||
|
||||
// Std
|
||||
use std::{
|
||||
backtrace::{Backtrace, BacktraceStatus},
|
||||
error::Error,
|
||||
};
|
||||
// Error backtrace
|
||||
use err_backtrace::ErrBacktraceExt;
|
||||
|
||||
/// Panic handler based on logging to the current initialization
|
||||
pub fn log_handler(info: &std::panic::PanicInfo) {
|
||||
// Log that this thread has panicked
|
||||
log::error!("Thread \"{}\" panicked", std::thread::current().name().unwrap_or("[Unknown]"));
|
||||
|
||||
// Log any message that came with the panic
|
||||
log::info!("Panic message: {}", info.message().unwrap_or(&format_args!("None")));
|
||||
|
||||
// Print an error backtrace if we found any
|
||||
if let Some(err) = info.payload().downcast_ref::<Box<dyn Error + Send + Sync>>() {
|
||||
log::info!("Error backtrace:\n{}", err.err_backtrace());
|
||||
}
|
||||
|
||||
// And print a backtrace of where this panic occured.
|
||||
let backtrace = Backtrace::force_capture();
|
||||
if backtrace.status() == BacktraceStatus::Captured {
|
||||
log::info!("Backtrace:\n{}", backtrace);
|
||||
} else {
|
||||
log::info!("Unable to get backtrace: {}", backtrace);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user