mirror of
https://github.com/lifegpc/msg-tool.git
synced 2026-06-06 12:58:45 +08:00
Add support to read embbed control block (tested game: https://vndb.org/v19829 )
This commit is contained in:
2
msg_tool_build/.gitignore
vendored
Normal file
2
msg_tool_build/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
Cargo.lock
|
||||
target/
|
||||
18
msg_tool_build/Cargo.toml
Normal file
18
msg_tool_build/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "msg_tool_build"
|
||||
version = "0.3.1"
|
||||
edition = "2024"
|
||||
repository = "https://github.com/lifegpc/msg-tool"
|
||||
description = "Build time library for the msg-tool project."
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
[dependencies]
|
||||
json = { version = "0.12", optional = true }
|
||||
zstd = { version = "0.13", optional = true }
|
||||
|
||||
[features]
|
||||
kirikiri-arc = ["json", "simple-pack"]
|
||||
simple-pack = ["zstd"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
38
msg_tool_build/src/kr_arc.rs
Normal file
38
msg_tool_build/src/kr_arc.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
use crate::simple_pack::SimplePack;
|
||||
use std::path::Path;
|
||||
|
||||
/// Pack all binary files in cx_cb into a single archive.
|
||||
pub fn gen_cx_cb<P: AsRef<Path> + ?Sized, D: AsRef<Path> + ?Sized>(
|
||||
json_path: &P,
|
||||
outdir: &D,
|
||||
level: i32,
|
||||
) -> std::io::Result<()> {
|
||||
let p = json_path.as_ref();
|
||||
let pb = p
|
||||
.parent()
|
||||
.unwrap_or_else(|| Path::new(""))
|
||||
.join("crypt")
|
||||
.join("cx_cb");
|
||||
let json_data = std::fs::read_to_string(p)?;
|
||||
let json = json::parse(&json_data)
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
|
||||
let mut pack = SimplePack::new(&outdir.as_ref().join("cx_cb.pck"))?;
|
||||
for (_, obj) in json.entries() {
|
||||
if let Some(name) = obj["ControlBlockName"].as_str() {
|
||||
let file_path = pb.join(name);
|
||||
if !file_path.exists() {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::NotFound,
|
||||
format!("File not found: {}", file_path.display()),
|
||||
));
|
||||
}
|
||||
let file = std::fs::File::open(file_path)?;
|
||||
let file = std::io::BufReader::new(file);
|
||||
pack.add_file(name, file)?;
|
||||
}
|
||||
}
|
||||
if level >= 0 && level <= 22 {
|
||||
pack.compress(level)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
4
msg_tool_build/src/lib.rs
Normal file
4
msg_tool_build/src/lib.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
#[cfg(feature = "kirikiri-arc")]
|
||||
pub mod kr_arc;
|
||||
#[cfg(feature = "simple-pack")]
|
||||
mod simple_pack;
|
||||
66
msg_tool_build/src/simple_pack.rs
Normal file
66
msg_tool_build/src/simple_pack.rs
Normal file
@@ -0,0 +1,66 @@
|
||||
//! A simple implementation of a pack file
|
||||
use std::fs::File;
|
||||
use std::io::{BufWriter, Read, Result, Seek, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub struct SimplePack {
|
||||
file: File,
|
||||
path: PathBuf,
|
||||
tmp_path: PathBuf,
|
||||
}
|
||||
|
||||
impl SimplePack {
|
||||
pub fn new<P: AsRef<Path> + ?Sized>(path: &P) -> Result<Self> {
|
||||
let mut file = File::create(path.as_ref())?;
|
||||
file.write_all(b"SPCK")?;
|
||||
file.write_all(&[0])?; // No compression
|
||||
Ok(Self {
|
||||
file,
|
||||
path: path.as_ref().to_path_buf(),
|
||||
tmp_path: path.as_ref().with_added_extension(".tmp"),
|
||||
})
|
||||
}
|
||||
pub fn add_file<R: Read>(&mut self, name: &str, mut data: R) -> Result<()> {
|
||||
let mut writer = BufWriter::new(&mut self.file);
|
||||
writer.write_all(name.as_bytes())?;
|
||||
writer.write_all(&[0])?; // Null terminator for the name
|
||||
let file_size_loc = writer.stream_position()?;
|
||||
writer.write_all(&0u64.to_le_bytes())?; // Placeholder for file size
|
||||
let size = std::io::copy(&mut data, &mut writer)?;
|
||||
let current_pos = writer.stream_position()?;
|
||||
writer.seek(std::io::SeekFrom::Start(file_size_loc))?;
|
||||
writer.write_all(&size.to_le_bytes())?; // Write the actual file size
|
||||
writer.seek(std::io::SeekFrom::Start(current_pos))?; // Move back to the end of the file
|
||||
writer.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
pub fn compress(mut self, level: i32) -> Result<()> {
|
||||
self.file.flush()?;
|
||||
std::mem::drop(self.file); // Close the file before renaming
|
||||
// Move the file to a temporary location
|
||||
std::fs::rename(&self.path, &self.tmp_path)?;
|
||||
{
|
||||
let tmp_file = File::open(&self.tmp_path)?;
|
||||
let mut reader = std::io::BufReader::new(tmp_file);
|
||||
reader.seek_relative(5)?; // Skip header
|
||||
let original_size = reader.get_ref().metadata()?.len() - 5;
|
||||
let outfile = File::create(&self.path)?;
|
||||
let mut writer = std::io::BufWriter::new(outfile);
|
||||
writer.write_all(b"SPCK")?;
|
||||
writer.write_all(&[1])?; // Compression flag
|
||||
let compress_size_loc = writer.stream_position()?;
|
||||
writer.write_all(&0u64.to_le_bytes())?; // Placeholder for compressed size
|
||||
writer.write_all(&original_size.to_le_bytes())?;
|
||||
let cur_loc = writer.stream_position()?;
|
||||
let mut encoder = zstd::stream::write::Encoder::new(&mut writer, level)?;
|
||||
std::io::copy(&mut reader, &mut encoder)?;
|
||||
encoder.finish()?;
|
||||
writer.flush()?;
|
||||
let compressed_size = writer.stream_position()? - cur_loc;
|
||||
writer.seek(std::io::SeekFrom::Start(compress_size_loc))?;
|
||||
writer.write_all(&compressed_size.to_le_bytes())?; // Write the actual compressed size
|
||||
}
|
||||
std::fs::remove_file(&self.tmp_path)?; // Clean up the temporary file
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user