Added tools to extract and create rlen encoded files.

This commit is contained in:
Filipe Rodrigues 2023-10-11 11:35:26 +01:00
parent e57db69b6c
commit 311efdf4ca
9 changed files with 310 additions and 0 deletions

View File

@ -73,12 +73,14 @@
"Paildramon",
"Patamon",
"pathdiff",
"peekable",
"Phoenixmon",
"pngs",
"psexe",
"REGS",
"Renamon",
"reqs",
"RLEN",
"Rosemon",
"Sakuyamon",
"seeked",

31
tools/Cargo.lock generated
View File

@ -439,6 +439,22 @@ dependencies = [
"tracing",
]
[[package]]
name = "ddw3-mkrlen"
version = "0.1.0"
dependencies = [
"anyhow",
"byteorder",
"clap",
"ddw3-logger",
"ddw3-util",
"itertools 0.11.0",
"pathdiff",
"serde",
"serde_yaml",
"tracing",
]
[[package]]
name = "ddw3-mktim"
version = "0.1.0"
@ -562,6 +578,21 @@ dependencies = [
"tracing",
]
[[package]]
name = "ddw3-unrlen"
version = "0.1.0"
dependencies = [
"anyhow",
"byteorder",
"clap",
"ddw3-logger",
"itertools 0.11.0",
"pathdiff",
"serde",
"serde_yaml",
"tracing",
]
[[package]]
name = "ddw3-untim"
version = "0.1.0"

View File

@ -33,6 +33,10 @@ members = [
"ddw3-unmap",
"ddw3-mkmap",
# RLen
"ddw3-unrlen",
"ddw3-mkrlen",
# Util
"ddw3-bytes",
"ddw3-util",
@ -72,6 +76,7 @@ ddw3-lang-file = { path = "ddw3-lang-file" }
ddw3-logger = { path = "ddw3-logger" }
ddw3-mklang-file = { path = "ddw3-mklang-file" }
ddw3-mkpsexe = { path = "ddw3-mkpsexe" }
ddw3-mkrlen = { path = "ddw3-mkrlen" }
ddw3-mkmap = { path = "ddw3-mkmap" }
ddw3-mkpack = { path = "ddw3-mkpack" }
ddw3-mktim = { path = "ddw3-untim" }
@ -82,5 +87,6 @@ ddw3-uniso = { path = "ddw3-uniso" }
ddw3-unlang-file = { path = "ddw3-unlang-file" }
ddw3-unmap = { path = "ddw3-unmap" }
ddw3-unpack = { path = "ddw3-unpack" }
ddw3-unrlen = { path = "ddw3-unrlen" }
ddw3-untim = { path = "ddw3-untim" }
ddw3-util = { path = "ddw3-util" }

View File

@ -0,0 +1,17 @@
[package]
edition = "2021"
name = "ddw3-mkrlen"
version = "0.1.0"
[dependencies]
anyhow = { workspace = true }
byteorder = { workspace = true }
clap = { workspace = true }
ddw3-logger = { workspace = true }
ddw3-util = { workspace = true }
itertools = { workspace = true }
pathdiff = { workspace = true }
serde = { workspace = true }
serde_yaml = { workspace = true }
tracing = { workspace = true }

View File

@ -0,0 +1,17 @@
//! Cli manager
// Imports
use std::path::PathBuf;
/// Data from the command line
#[derive(PartialEq, Eq, Clone, Debug)]
#[derive(clap::Parser)]
#[clap(author, version, about)]
pub struct Args {
/// Input file
pub input: PathBuf,
/// The output file
#[clap(long = "output", short = 'o')]
pub output: PathBuf,
}

View File

@ -0,0 +1,114 @@
//! `RLEN` creator
// Features
#![feature(array_chunks, array_windows, seek_stream_len, let_chains)]
// Modules
mod args;
// Imports
use {
self::args::Args,
anyhow::Context,
byteorder::{LittleEndian, WriteBytesExt},
clap::Parser,
std::{
fs,
io::{self, BufWriter, Seek, Write},
},
};
fn main() -> Result<(), anyhow::Error> {
// Initialize the logger
ddw3_logger::init().context("Unable to initialize logger")?;
// Get all args
let args = Args::parse();
tracing::debug!(?args, "Arguments");
// Read the input file
// TODO: Stream the input instead of reading it all?
let input = fs::read(&args.input).context("Unable to read input file")?;
// Create the output file
let output = fs::File::create(&args.output).context("Unable to open output file")?;
let mut output = BufWriter::new(output);
// Skip over the header
output
.seek(io::SeekFrom::Start(8))
.context("Unable to seek past header")?;
let mut total_size = 0;
let mut input = &*input;
loop {
let Some(&cur) = input.first() else {
break;
};
match input.get(1) {
Some(&next) => match cur == next {
true => {
let mut len = match input[1..].iter().position(|&b| b != cur) {
Some(idx) => idx + 1,
None => input.len(),
};
assert!(len >= 2, "Tried to use repeat opcode on length less than 2: {len}");
for bytes in input[..len].chunks(0x7f) {
// If we'd emit less than a `0x82`, instead quit
// Note: This implies that `bytes.len()` is 1, as it will
// never be 0, and we've just checked it's `< 2`
if bytes.len() < 2 {
len -= 1;
break;
}
let opcode = u8::try_from(0x80 + bytes.len()).expect("Chunk size was greater than 0x80");
output.write_u8(opcode).context("Unable to write opcode")?;
output.write_u8(cur).context("Unable to write data")?;
}
input = &input[len..];
total_size += len;
},
false => {
let len = match input[1..].array_windows().position(|&[b0, b1]| b0 == b1) {
Some(idx) => idx + 1,
None => input.len(),
};
for bytes in input[..len].chunks(0x7f) {
let opcode = u8::try_from(bytes.len()).expect("Chunk size was greater than 0x80");
assert!(opcode < 0x80, "Chunk size was greater than 0x80");
output.write_u8(opcode).context("Unable to write opcode")?;
output.write_all(bytes).context("Unable to write data")?;
}
input = &input[len..];
total_size += len;
},
},
None => {
output.write_u8(0x01).context("Unable to write opcode")?;
output.write_u8(cur).context("Unable to write data")?;
total_size += 1;
break;
},
}
}
// Then go back and write the header
output
.seek(io::SeekFrom::Start(0))
.context("Unable to seek output to start")?;
output
.write_u32::<LittleEndian>(u32::from_le_bytes(*b"RLEN"))
.context("Unable to write magic")?;
let total_size = u32::try_from(total_size).context("Total size didn't fit into `u32`")?;
output
.write_u32::<LittleEndian>(total_size)
.context("Unable to write total size")?;
Ok(())
}

View File

@ -0,0 +1,16 @@
[package]
edition = "2021"
name = "ddw3-unrlen"
version = "0.1.0"
[dependencies]
anyhow = { workspace = true }
byteorder = { workspace = true }
clap = { workspace = true }
ddw3-logger = { workspace = true }
itertools = { workspace = true }
pathdiff = { workspace = true }
serde = { workspace = true }
serde_yaml = { workspace = true }
tracing = { workspace = true }

View File

@ -0,0 +1,17 @@
//! Cli manager
// Imports
use std::path::PathBuf;
/// Data from the command line
#[derive(PartialEq, Eq, Clone, Debug)]
#[derive(clap::Parser)]
#[clap(author, version, about)]
pub struct Args {
/// Input file
pub input: PathBuf,
/// The output path
#[clap(long = "output", short = 'o')]
pub output: PathBuf,
}

View File

@ -0,0 +1,90 @@
//! `RLEN` extractor
// Features
#![feature(array_chunks, array_windows, seek_stream_len, exclusive_range_pattern)]
// Modules
mod args;
// Imports
use {
self::args::Args,
anyhow::Context,
byteorder::{LittleEndian, ReadBytesExt},
clap::Parser,
std::{
fs,
io::{self, BufReader, BufWriter, Read, Write},
path::PathBuf,
},
};
fn main() -> Result<(), anyhow::Error> {
// Initialize the logger
ddw3_logger::init().context("Unable to initialize logger")?;
// Get all args
let args = Args::parse();
tracing::debug!(?args, "Arguments");
// Open the file
let reader = fs::File::open(&args.input).context("Unable to open input file")?;
let mut reader = BufReader::new(reader);
let magic = reader
.read_u32::<LittleEndian>()
.context("Unable to read magic")?
.to_le_bytes();
const MAGIC: [u8; 4] = *b"RLEN";
anyhow::ensure!(magic == MAGIC, "Found wrong magic: {magic:x?}, expected {MAGIC:x?}");
let total_size = reader.read_u32::<LittleEndian>().context("Unable to read total size")?;
// Open the output file
let output = fs::File::create(&args.output).context("Unable to create output file")?;
let mut output = BufWriter::new(output);
let mut cur_pos = 0;
while cur_pos < total_size {
let opcode = reader.read_u8().context("Unable to read next opcode")?;
match opcode {
0x00..0x80 => {
let len = opcode;
io::copy(&mut reader.by_ref().take(u64::from(len)), &mut output)
.context("Unable to write to output")?;
cur_pos += u32::from(opcode);
},
0x80..=0xff => {
let byte = reader.read_u8().context("Unable to read byte")?;
let len = opcode - 0x80;
let bytes = vec![byte; usize::from(len)];
output.write_all(&bytes).context("Unable to write to output")?;
cur_pos += u32::from(len);
},
}
}
Ok(())
}
/// Output
#[derive(serde::Serialize)]
pub struct Output {
/// Width
width: u32,
/// Height
height: u32,
/// Entries
entries: Vec<PathBuf>,
/// Override number of entries in header
#[serde(skip_serializing_if = "Option::is_none")]
override_entries_len: Option<u32>,
/// Extra padding
extra_padding: u64,
}