Added tools to extract and create rlen encoded files.
This commit is contained in:
parent
e57db69b6c
commit
311efdf4ca
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@ -73,12 +73,14 @@
|
||||
"Paildramon",
|
||||
"Patamon",
|
||||
"pathdiff",
|
||||
"peekable",
|
||||
"Phoenixmon",
|
||||
"pngs",
|
||||
"psexe",
|
||||
"REGS",
|
||||
"Renamon",
|
||||
"reqs",
|
||||
"RLEN",
|
||||
"Rosemon",
|
||||
"Sakuyamon",
|
||||
"seeked",
|
||||
|
||||
31
tools/Cargo.lock
generated
31
tools/Cargo.lock
generated
@ -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"
|
||||
|
||||
@ -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" }
|
||||
|
||||
17
tools/ddw3-mkrlen/Cargo.toml
Normal file
17
tools/ddw3-mkrlen/Cargo.toml
Normal 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 }
|
||||
17
tools/ddw3-mkrlen/src/args.rs
Normal file
17
tools/ddw3-mkrlen/src/args.rs
Normal 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,
|
||||
}
|
||||
114
tools/ddw3-mkrlen/src/main.rs
Normal file
114
tools/ddw3-mkrlen/src/main.rs
Normal 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(())
|
||||
}
|
||||
16
tools/ddw3-unrlen/Cargo.toml
Normal file
16
tools/ddw3-unrlen/Cargo.toml
Normal 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 }
|
||||
17
tools/ddw3-unrlen/src/args.rs
Normal file
17
tools/ddw3-unrlen/src/args.rs
Normal 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,
|
||||
}
|
||||
90
tools/ddw3-unrlen/src/main.rs
Normal file
90
tools/ddw3-unrlen/src/main.rs
Normal 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,
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user