Add PACK support

This commit is contained in:
2025-06-04 12:33:56 +08:00
parent ec9a163c39
commit 6e8d248ed2
5 changed files with 156 additions and 15 deletions

View File

@@ -63,8 +63,9 @@ pub fn struct_unpack_impl_for_num(item: TokenStream) -> TokenStream {
///
/// * `skip_pack` attribute can be used to skip fields from packing.
/// * `fstring = <len>` attribute can be used to specify a fixed string length for String fields.
/// * `fstring_pad = <u8>` attribute can be used to specify a padding byte for fixed strings. (Default is 0)
/// * `fvec = <len>` attribute can be used to specify a fixed vector length for Vec<_> fields.
#[proc_macro_derive(StructPack, attributes(skip_pack, fstring, fvec))]
#[proc_macro_derive(StructPack, attributes(skip_pack, fstring, fstring_pad, fvec))]
pub fn struct_pack_derive(input: TokenStream) -> TokenStream {
let a = syn::parse_macro_input!(input as PackStruct);
match a {
@@ -75,6 +76,7 @@ pub fn struct_pack_derive(input: TokenStream) -> TokenStream {
let mut skipped = false;
let mut fixed_string: Option<usize> = None;
let mut fixed_vec: Option<usize> = None;
let mut fstring_pad = 0u8; // Default padding byte
for attr in &field.attrs {
let path = attr.path();
if path.is_ident("skip_pack") {
@@ -95,6 +97,14 @@ pub fn struct_pack_derive(input: TokenStream) -> TokenStream {
}
}
}
} else if path.is_ident("fstring_pad") {
if let syn::Meta::NameValue(nv) = &attr.meta {
if let syn::Expr::Lit(lit) = &nv.value {
if let syn::Lit::Int(s) = &lit.lit {
fstring_pad = s.base10_parse().unwrap();
}
}
}
}
}
if skipped {
@@ -115,13 +125,17 @@ pub fn struct_pack_derive(input: TokenStream) -> TokenStream {
if let Some(fixed_string) = fixed_string {
return quote::quote! {
let s = encode_string(encoding, &self.#field_name, true)?;
let slen = s.len();
let mut slen = s.len();
if slen > #fixed_string {
return Err(anyhow::anyhow!("String length was too long for field '{}'", stringify!(#field_name)));
}
writer.write_all(&s)?;
for _ in slen..#fixed_string {
if slen < #fixed_string {
writer.write_all(&[0])?;
slen += 1;
}
for _ in slen..#fixed_string {
writer.write_all(&[#fstring_pad])?;
}
};
}
@@ -176,6 +190,7 @@ pub fn struct_pack_derive(input: TokenStream) -> TokenStream {
let mut skipped = false;
let mut fixed_string: Option<usize> = None;
let mut fixed_vec: Option<usize> = None;
let mut fstring_pad = 0u8; // Default padding byte
for attr in &field.attrs {
let path = attr.path();
if path.is_ident("skip_pack") {
@@ -196,6 +211,14 @@ pub fn struct_pack_derive(input: TokenStream) -> TokenStream {
}
}
}
} else if path.is_ident("fstring_pad") {
if let syn::Meta::NameValue(nv) = &attr.meta {
if let syn::Expr::Lit(lit) = &nv.value {
if let syn::Lit::Int(s) = &lit.lit {
fstring_pad = s.base10_parse().unwrap();
}
}
}
}
}
if skipped {
@@ -217,13 +240,17 @@ pub fn struct_pack_derive(input: TokenStream) -> TokenStream {
if let Some(fixed_string) = fixed_string {
return quote::quote! {
let s = encode_string(encoding, &#field_name, true)?;
let slen = s.len();
let mut slen = s.len();
if slen > #fixed_string {
return Err(anyhow::anyhow!("String length was too long for field '{}'", stringify!(#field_name)));
}
writer.write_all(&s)?;
for _ in slen..#fixed_string {
if slen < #fixed_string {
writer.write_all(&[0])?;
slen += 1;
}
for _ in slen..#fixed_string {
writer.write_all(&[#fstring_pad])?;
}
};
}

View File

@@ -126,6 +126,12 @@ pub enum Command {
},
/// Import to script
Import(ImportArgs),
Pack {
/// Input directory
input: String,
/// Output archive file
output: String,
},
}
pub fn parse_args() -> Arg {

View File

@@ -896,6 +896,73 @@ pub fn import_script(
Ok(types::ScriptResult::Ok)
}
pub fn pack_archive(
input: &str,
output: &str,
arg: &args::Arg,
config: &types::ExtraConfig,
) -> anyhow::Result<()> {
let typ = match &arg.script_type {
Some(t) => t,
None => {
return Err(anyhow::anyhow!("No script type specified"));
}
};
let (files, isdir) = utils::files::collect_files(input, arg.recursive, true)
.map_err(|e| anyhow::anyhow!("Error collecting files: {}", e))?;
if !isdir {
return Err(anyhow::anyhow!("Input must be a directory for packing"));
}
let re_files: Vec<String> = files
.iter()
.filter_map(|f| {
std::path::PathBuf::from(f)
.strip_prefix(input)
.ok()
.and_then(|p| {
p.to_str()
.map(|s| s.replace("\\", "/").trim_start_matches("/").to_owned())
})
})
.collect();
let reff = re_files.iter().map(|s| s.as_str()).collect::<Vec<_>>();
let mut archive = scripts::BUILDER
.iter()
.find(|b| b.script_type() == typ)
.ok_or_else(|| anyhow::anyhow!("Unsupported script type"))?
.create_archive(output, &reff, get_output_encoding(arg), config)?;
for (file, name) in files.iter().zip(reff) {
let mut f = match std::fs::File::open(file) {
Ok(f) => f,
Err(e) => {
eprintln!("Error opening file {}: {}", file, e);
COUNTER.inc_error();
continue;
}
};
let mut wf = match archive.new_file(name) {
Ok(f) => f,
Err(e) => {
eprintln!("Error creating file {} in archive: {}", name, e);
COUNTER.inc_error();
continue;
}
};
match std::io::copy(&mut f, &mut wf) {
Ok(_) => {
COUNTER.inc(types::ScriptResult::Ok);
}
Err(e) => {
eprintln!("Error writing to file {} in archive: {}", name, e);
COUNTER.inc_error();
continue;
}
}
}
archive.write_header()?;
Ok(())
}
lazy_static::lazy_static! {
static ref COUNTER: utils::counter::Counter = utils::counter::Counter::new();
}
@@ -911,7 +978,8 @@ fn main() {
};
match &arg.command {
args::Command::Export { input, output } => {
let (scripts, is_dir) = utils::files::collect_files(input, arg.recursive).unwrap();
let (scripts, is_dir) =
utils::files::collect_files(input, arg.recursive, false).unwrap();
if is_dir {
match &output {
Some(output) => {
@@ -962,7 +1030,7 @@ fn main() {
None => None,
};
let (scripts, is_dir) =
utils::files::collect_files(&args.input, arg.recursive).unwrap();
utils::files::collect_files(&args.input, arg.recursive, false).unwrap();
if is_dir {
let pb = std::path::Path::new(&args.patched);
if pb.exists() {
@@ -998,6 +1066,13 @@ fn main() {
}
}
}
args::Command::Pack { input, output } => {
let re = pack_archive(input, output, &arg, &cfg);
if let Err(e) = re {
COUNTER.inc_error();
eprintln!("Error packing archive: {}", e);
}
}
}
eprintln!("{}", std::ops::Deref::deref(&COUNTER));
}

View File

@@ -320,21 +320,25 @@ impl Script for EscudeBinList {
#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
struct ScriptT {
#[fstring = 64]
#[fstring_pad = 0x20]
/// File name
pub file: String,
pub source: u32,
#[fstring = 64]
#[fstring_pad = 0x20]
pub title: String,
}
#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
struct NameT {
#[fstring = 64]
#[fstring_pad = 0x20]
/// Name of the character
pub text: String,
/// Text color
pub color: u32,
#[fstring = 32]
#[fstring_pad = 0x20]
/// Face image file name
pub face: String,
}
@@ -343,6 +347,7 @@ struct NameT {
struct VarT {
/// Variable name
#[fstring = 32]
#[fstring_pad = 0x20]
pub name: String,
/// Variable value
pub value: u16,
@@ -356,9 +361,11 @@ struct SceneT {
pub script: u32,
/// The scene name
#[fstring = 64]
#[fstring_pad = 0x20]
pub name: String,
/// The scene thumbail image file name
#[fstring = 32]
#[fstring_pad = 0x20]
pub thumbnail: String,
/// The scene order in the scene (Extra)
pub order: i32,
@@ -377,11 +384,14 @@ enum EnumScr {
struct BgT {
/// Background image name
#[fstring = 32]
#[fstring_pad = 0x20]
name: String,
/// Background image file name
#[fstring = 64]
#[fstring_pad = 0x20]
file: String,
#[fstring = 128]
#[fstring_pad = 0x20]
option: String,
coverd: u32,
color: u32,
@@ -395,11 +405,14 @@ struct BgT {
struct EvT {
/// Event image name
#[fstring = 32]
#[fstring_pad = 0x20]
name: String,
/// Event image file name
#[fstring = 64]
#[fstring_pad = 0x20]
file: String,
#[fstring = 128]
#[fstring_pad = 0x20]
option: String,
coverd: u32,
color: u32,
@@ -412,10 +425,13 @@ struct EvT {
#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
struct StT {
#[fstring = 32]
#[fstring_pad = 0x20]
name: String,
#[fstring = 64]
#[fstring_pad = 0x20]
file: String,
#[fstring = 128]
#[fstring_pad = 0x20]
option: String,
coverd: u32,
color: u32,
@@ -429,9 +445,11 @@ struct StT {
struct EfxT {
/// Effect image name
#[fstring = 32]
#[fstring_pad = 0x20]
name: String,
/// Effect image file name
#[fstring = 64]
#[fstring_pad = 0x20]
file: String,
spot: i32,
dx: i32,
@@ -471,10 +489,13 @@ enum EnumGfx {
#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
struct BgmT {
#[fstring = 64]
#[fstring_pad = 0x20]
pub name: String,
#[fstring = 64]
#[fstring_pad = 0x20]
pub file: String,
#[fstring = 64]
#[fstring_pad = 0x20]
pub title: String,
pub order: i32,
}
@@ -482,24 +503,30 @@ struct BgmT {
#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
struct AmbT {
#[fstring = 64]
#[fstring_pad = 0x20]
pub name: String,
#[fstring = 64]
#[fstring_pad = 0x20]
pub file: String,
}
#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
struct SeT {
#[fstring = 64]
#[fstring_pad = 0x20]
pub name: String,
#[fstring = 64]
#[fstring_pad = 0x20]
pub file: String,
}
#[derive(Debug, Serialize, Deserialize, StructPack, StructUnpack)]
struct SfxT {
#[fstring = 64]
#[fstring_pad = 0x20]
pub name: String,
#[fstring = 64]
#[fstring_pad = 0x20]
pub file: String,
}

View File

@@ -5,7 +5,7 @@ use std::path::Path;
use crate::scripts::ALL_EXTS;
pub fn find_files(path: &String, recursive: bool) -> io::Result<Vec<String>> {
pub fn find_files(path: &str, recursive: bool, no_ext_filter: bool) -> io::Result<Vec<String>> {
let mut result = Vec::new();
let dir_path = Path::new(&path);
@@ -15,16 +15,18 @@ pub fn find_files(path: &String, recursive: bool) -> io::Result<Vec<String>> {
let path = entry.path();
if path.is_file()
&& path.extension().map_or(true, |ext| {
ALL_EXTS.contains(&ext.to_string_lossy().to_lowercase())
})
&& (no_ext_filter
|| path.extension().map_or(true, |ext| {
ALL_EXTS.contains(&ext.to_string_lossy().to_lowercase())
}))
{
if let Some(path_str) = path.to_str() {
result.push(path_str.to_string());
}
} else if recursive && path.is_dir() {
if let Some(path_str) = path.to_str() {
let mut sub_files = find_files(&path_str.to_string(), recursive)?;
let mut sub_files =
find_files(&path_str.to_string(), recursive, no_ext_filter)?;
result.append(&mut sub_files);
}
}
@@ -34,13 +36,17 @@ pub fn find_files(path: &String, recursive: bool) -> io::Result<Vec<String>> {
Ok(result)
}
pub fn collect_files(path: &String, recursive: bool) -> io::Result<(Vec<String>, bool)> {
pub fn collect_files(
path: &str,
recursive: bool,
no_ext_filter: bool,
) -> io::Result<(Vec<String>, bool)> {
let pa = Path::new(path);
if pa.is_dir() {
return Ok((find_files(path, recursive)?, true));
return Ok((find_files(path, recursive, no_ext_filter)?, true));
}
if pa.is_file() {
return Ok((vec![path.clone()], false));
return Ok((vec![path.to_string()], false));
}
Err(io::Error::new(
io::ErrorKind::NotFound,