Skip to content

Commit

Permalink
Merge pull request #2 from LemonInTheDark/restore-better
Browse files Browse the repository at this point in the history
Refactors operation return handling into its own proc so it can recurse. I don't like just packing all my returns into the same type, thought it would be nicer if returns could like "compose" each other

IDK if this breaks some optimization, if it does I can undo it

Implements StringMap for mapping strings to strings in configs

Implements ConfigWrapped, which wraps some other output with a toml config.

Implements BitmaskSliceReconstruct, a config mode that will do its damndest to construct a working png and toml file from a cut dmi.

It's not perfect, but it is pretty powerful. Knows how to extract prefixes, delays, and the icon size variables.
  • Loading branch information
LemonInTheDark authored Apr 27, 2024
2 parents a6e15f1 + 2294ac7 commit 42a5d04
Show file tree
Hide file tree
Showing 6 changed files with 436 additions and 37 deletions.
18 changes: 18 additions & 0 deletions examples/bitmask-slice-restore.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Bitmask restoration!
# Allows for easy mass extraction of template pngs and their configs from a dmi
# Use this if you have a dmi and you want a cutter config you can edit easily
# Of note, while it tries its best it is nowhere near perfect. We don't parity check against the existing dmi
# And we also do not account for overrided states very well
# Always double check (and be aware that dmi is weird so you may get diffs of 1 rgb value when doin this)
mode = "BitmaskSliceReconstruct"
# List of icon states to pull out
extract = ["0", "3", "12", "15", "255"]

# Map of name -> state that will be encoded into a positions list later
# Lets you extract particular states and use them to fill in for states later
# Useful to carry over odd snowflake states
#[bespoke]

# Map of key -> value to set on the created config
# Lets you set arbitrary values on the created config, mostly useful for batch processing
#[set]
106 changes: 70 additions & 36 deletions hypnagogic_cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ use hypnagogic_core::operations::{
InputIcon,
NamedIcon,
OperationMode,
Output,
OutputImage,
OutputText,
ProcessorPayload,
};
use rayon::prelude::*;
Expand Down Expand Up @@ -250,6 +252,57 @@ fn process_icon(
fs::create_dir_all(output_path)?;
}

let out_paths: Vec<(PathBuf, Output)> = handle_payload(out, input_icon_path, output, flatten);

for (mut path, output) in out_paths {
let parent_dir = path.parent().expect(
"Failed to get parent? (this is a program error, not a config error! Please report!)",
);

fs::create_dir_all(parent_dir).expect(
"Failed to create dirs (This is a program error, not a config error! Please report!)",
);

let mut file = File::create(path.as_path()).expect(
"Failed to create output file (This is a program error, not a config error! Please \
report!)",
);

// TODO: figure out a better thing to do than just the unwrap
match output {
Output::Image(icon) => {
match icon {
OutputImage::Png(png) => {
png.save(&mut path).unwrap();
}
OutputImage::Dmi(dmi) => {
dmi.save(&mut file).unwrap();
}
}
}
Output::Text(text) => {
match text {
OutputText::PngConfig(config) | OutputText::DmiConfig(config) => {
fs::write(path, config).expect(
"Failed to write config text, (This is a program error, not a config \
error! Please report!)",
)
}
}
}
}
}
Ok(())
}

#[allow(clippy::result_large_err)]
fn handle_payload(
payload: ProcessorPayload,
input_path: PathBuf,
output_at: &Option<String>,
flatten: bool,
) -> Vec<(PathBuf, Output)> {
let mut out_paths: Vec<(PathBuf, Output)> = vec![];
let process_path = |path: PathBuf, named_img: Option<&NamedIcon>| -> PathBuf {
debug!(path = ?path, img = ?named_img, "Processing path");
let processed_path = if let Some(named_img) = named_img {
Expand All @@ -263,7 +316,7 @@ fn process_icon(

let mut path = PathBuf::new();

if let Some(output) = &output {
if let Some(output) = &output_at {
path = PathBuf::from(output).join(&path);
}

Expand All @@ -272,55 +325,36 @@ fn process_icon(
}
path.push(processed_path);
info!(path = ?path, "Processed path");

path
};

let mut out_paths: Vec<(PathBuf, OutputImage)> = vec![];

match out {
match payload {
ProcessorPayload::Single(inner) => {
let mut processed_path = process_path(input_icon_path.clone(), None);
let mut processed_path = process_path(input_path.clone(), None);
processed_path.set_extension(inner.extension());
out_paths.push((processed_path, *inner));
out_paths.push((processed_path, Output::Image(*inner)));
}
ProcessorPayload::SingleNamed(named) => {
let mut processed_path = process_path(input_icon_path.clone(), Some(&named));
let mut processed_path = process_path(input_path.clone(), Some(&named));
processed_path.set_extension(named.image.extension());
out_paths.push((processed_path, named.image))
out_paths.push((processed_path, Output::Image(named.image)))
}
ProcessorPayload::MultipleNamed(icons) => {
for icon in icons {
let mut processed_path = process_path(input_icon_path.clone(), Some(&icon));
let mut processed_path = process_path(input_path.clone(), Some(&icon));
processed_path.set_extension(icon.image.extension());
out_paths.push((processed_path, icon.image))
out_paths.push((processed_path, Output::Image(icon.image)))
}
}
}

for (mut path, icon) in out_paths {
let parent_dir = path.parent().expect(
"Failed to get parent? (this is a program error, not a config error! Please report!)",
);

fs::create_dir_all(parent_dir).expect(
"Failed to create dirs (This is a program error, not a config error! Please report!)",
);

let mut file = File::create(path.as_path()).expect(
"Failed to create output file (This is a program error, not a config error! Please \
report!)",
);

// TODO: figure out a better thing to do than just the unwrap
match icon {
OutputImage::Png(png) => {
png.save(&mut path).unwrap();
}
OutputImage::Dmi(dmi) => {
dmi.save(&mut file).unwrap();
}
ProcessorPayload::ConfigWrapped(payload, config_text) => {
// First, we'll pack in our config
let mut processed_path = process_path(input_path.clone(), None);
processed_path.set_extension(config_text.extension());
out_paths.push((processed_path, Output::Text(*config_text)));
// Then we recurse and handle the enclosed payload
let mut contained = handle_payload(*payload, input_path, output_at, flatten);
out_paths.append(&mut contained);
}
}
Ok(())
out_paths
}
48 changes: 47 additions & 1 deletion hypnagogic_core/src/config/blocks/cutters.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::BTreeMap;
use std::collections::{BTreeMap, HashMap};

use fixed_map::Map;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
Expand Down Expand Up @@ -104,6 +104,52 @@ impl Default for Positions {
}
}

#[derive(Clone, Eq, PartialEq, Debug, Default)]
pub struct StringMap(pub HashMap<String, String>);

impl StringMap {
#[must_use]
pub fn get(&self, key: &str) -> Option<&String> {
self.0.get(key)
}
}

#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
#[serde(transparent)]
struct StringMapHelper {
map: HashMap<String, String>,
}

impl Serialize for StringMap {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = HashMap::new();

for (k, v) in &self.0 {
map.insert(k.clone(), v.clone());
}

StringMapHelper { map }.serialize(serializer)
}
}

impl<'de> Deserialize<'de> for StringMap {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Deserialize::deserialize(deserializer).map(|StringMapHelper { map }| {
let mut result = HashMap::new();
for (k, v) in map {
result.insert(k, v);
}
StringMap(result)
})
}
}

#[derive(Clone, Eq, PartialEq, Debug, Default)]
pub struct Prefabs(pub BTreeMap<u8, u32>);

Expand Down
2 changes: 2 additions & 0 deletions hypnagogic_core/src/operations/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ pub enum ProcessorError {
FormatError(String),
#[error("Error processing image:\n{0}")]
ImageError(#[from] image::error::ImageError),
#[error("Error restoring dmi:\n{0}")]
DmiError(String),
#[error("Error generating icon for processor:\n{0}")]
GenerationError(#[from] crate::generation::error::GenerationError),
#[error("Error within image config:")]
Expand Down
Loading

0 comments on commit 42a5d04

Please sign in to comment.