Add document

This commit is contained in:
2025-08-10 16:58:44 +08:00
parent f602ddb4b5
commit cfc1dbf507
43 changed files with 516 additions and 13 deletions

View File

@@ -1,14 +1,19 @@
//! Bit stream utilities.
use crate::ext::io::*;
use anyhow::Result;
use std::io::{Read, Write};
/// A most significant bit (MSB) bit stream reader.
pub struct MsbBitStream<T: Read> {
/// The input stream to read from.
pub m_input: T,
m_bits: u32,
/// The number of bits currently cached.
pub m_cached_bits: u32,
}
impl<T: Read> MsbBitStream<T> {
/// Creates a new MSB bit stream reader.
pub fn new(input: T) -> Self {
MsbBitStream {
m_input: input,
@@ -17,6 +22,7 @@ impl<T: Read> MsbBitStream<T> {
}
}
/// Reads a specified number of bits from the stream.
pub fn get_bits(&mut self, count: u32) -> Result<u32> {
while self.m_cached_bits < count {
let byte = self.m_input.read_u8()?;
@@ -29,6 +35,7 @@ impl<T: Read> MsbBitStream<T> {
Ok(result)
}
/// Reads the next bit from the stream.
pub fn get_next_bit(&mut self) -> Result<bool> {
if self.m_cached_bits == 0 {
let byte = self.m_input.read_u8()?;
@@ -41,13 +48,16 @@ impl<T: Read> MsbBitStream<T> {
}
}
/// A most significant bit (MSB) bit writer.
pub struct MsbBitWriter<'a, T: Write> {
/// The output stream to write to.
pub writer: &'a mut T,
buffer: u32,
buffer_size: u32,
}
impl<'a, T: Write> MsbBitWriter<'a, T> {
/// Creates a new MSB bit writer.
pub fn new(writer: &'a mut T) -> Self {
MsbBitWriter {
writer,
@@ -56,6 +66,8 @@ impl<'a, T: Write> MsbBitWriter<'a, T> {
}
}
/// Flushes the buffer to the output stream.
/// This writes any remaining bits in the buffer to the stream.
pub fn flush(&mut self) -> Result<()> {
if self.buffer_size > 0 {
self.writer
@@ -66,6 +78,7 @@ impl<'a, T: Write> MsbBitWriter<'a, T> {
Ok(())
}
/// Puts a byte into the bit stream with a specified token width.
pub fn put_bits(&mut self, byte: u32, token_width: u8) -> Result<()> {
for i in 0..token_width {
self.put_bit((byte & (1 << (token_width - 1 - i))) != 0)?;
@@ -73,6 +86,7 @@ impl<'a, T: Write> MsbBitWriter<'a, T> {
Ok(())
}
/// Puts a single bit into the bit stream.
pub fn put_bit(&mut self, bit: bool) -> Result<()> {
self.buffer <<= 1;
if bit {
@@ -87,13 +101,17 @@ impl<'a, T: Write> MsbBitWriter<'a, T> {
}
}
/// A least significant bit (LSB) bit stream reader.
pub struct LsbBitStream<T: Read> {
/// The input stream to read from.
pub m_input: T,
m_bits: u32,
/// The number of bits currently cached.
pub m_cached_bits: u32,
}
impl<T: Read> LsbBitStream<T> {
/// Creates a new LSB bit stream reader.
pub fn new(input: T) -> Self {
LsbBitStream {
m_input: input,
@@ -102,6 +120,7 @@ impl<T: Read> LsbBitStream<T> {
}
}
/// Reads a specified number of bits from the stream.
pub fn get_bits(&mut self, mut count: u32) -> Result<u32> {
if self.m_cached_bits >= count {
let mask = (1 << count) - 1;
@@ -130,6 +149,7 @@ impl<T: Read> LsbBitStream<T> {
}
}
/// Reads the next bit from the stream.
pub fn get_next_bit(&mut self) -> Result<bool> {
Ok(self.get_bits(1)? == 1)
}

View File

@@ -1,7 +1,9 @@
//! A simple counter for tracking script execution results.
use crate::types::*;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::SeqCst;
/// A counter for tracking script execution results.
pub struct Counter {
ok: AtomicUsize,
ignored: AtomicUsize,
@@ -10,6 +12,7 @@ pub struct Counter {
}
impl Counter {
/// Creates a new Counter instance.
pub fn new() -> Self {
Self {
ok: AtomicUsize::new(0),
@@ -19,14 +22,17 @@ impl Counter {
}
}
/// Increments the count of errors.
pub fn inc_error(&self) {
self.error.fetch_add(1, SeqCst);
}
/// Increments the count of warnings.
pub fn inc_warning(&self) {
self.warning.fetch_add(1, SeqCst);
}
/// Increments the count of script executions.
pub fn inc(&self, result: ScriptResult) {
match result {
ScriptResult::Ok => self.ok.fetch_add(1, SeqCst),

View File

@@ -1,3 +1,4 @@
//! Crc32 Utility
use lazy_static::lazy_static;
fn get_crc32normal_table() -> [u32; 256] {
@@ -17,18 +18,22 @@ fn get_crc32normal_table() -> [u32; 256] {
}
lazy_static! {
/// CRC32 Normal Table
pub static ref CRC32NORMAL_TABLE: [u32; 256] = get_crc32normal_table();
}
/// A CRC32 Normal implementation.
pub struct Crc32Normal {
crc: u32,
}
impl Crc32Normal {
/// Creates a new Crc32Normal instance with an initial CRC value.
pub fn new() -> Self {
Crc32Normal { crc: 0xFFFFFFFF }
}
/// Creates a new Crc32Normal instance with a specified initial CRC value.
pub fn update_crc(init_crc: u32, data: &[u8]) -> u32 {
let mut crc = init_crc;
for &byte in data {
@@ -38,10 +43,12 @@ impl Crc32Normal {
crc ^ 0xFFFFFFFF
}
/// Updates the CRC value with new data.
pub fn update(&mut self, data: &[u8]) {
self.crc = Self::update_crc(self.crc, data);
}
/// Returns the current CRC value.
pub fn value(&self) -> u32 {
self.crc
}

View File

@@ -1,5 +1,11 @@
//! Encoding Utilities
use crate::types::*;
/// Decodes a byte slice to a string using the specified encoding with BOM detection.
///
/// * `check` - If true, checks for decoding errors and returns an error if any.
///
/// Returns the decoded string and the detected BOM type.
pub fn decode_with_bom_detect(
encoding: Encoding,
data: &[u8],
@@ -53,6 +59,9 @@ pub fn decode_with_bom_detect(
decode_to_string(encoding, data, check).map(|s| (s, BomType::None))
}
/// Decodes a byte slice to a string using the specified encoding.
///
/// * `check` - If true, checks for decoding errors and returns an error if any.
pub fn decode_to_string(
encoding: Encoding,
data: &[u8],
@@ -98,6 +107,9 @@ pub fn decode_to_string(
}
}
/// Encodes a string to a byte vector using the specified encoding.
///
/// * `check` - If true, checks for encoding errors and returns an error if any.
pub fn encode_string(
encoding: Encoding,
data: &str,
@@ -141,6 +153,10 @@ pub fn encode_string(
}
}
/// Encodes a string to a byte vector using the specified encoding with BOM.
///
/// * `bom` - The BOM type to use.
/// * `check` - If true, checks for encoding errors and returns an error if any
pub fn encode_string_with_bom(
encoding: Encoding,
data: &str,

View File

@@ -1,5 +1,7 @@
//! Escape and Unescape Utilities
use fancy_regex::Regex;
/// Escapes special characters in XML attribute values.
pub fn escape_xml_attr_value(s: &str) -> String {
let mut escaped = String::with_capacity(s.len());
for c in s.chars() {
@@ -14,6 +16,7 @@ pub fn escape_xml_attr_value(s: &str) -> String {
escaped
}
/// Escapes special characters in XML text values.
pub fn escape_xml_text_value(s: &str) -> String {
let mut escaped = String::with_capacity(s.len());
for c in s.chars() {
@@ -37,6 +40,7 @@ lazy_static::lazy_static! {
static ref LUA_NCR_BASE16_U_REGEX: Regex = Regex::new(r"\\u([0-9a-fA-F]{4})").unwrap();
}
/// Unescapes XML character references and entities.
pub fn unescape_xml(s: &str) -> String {
let mut s = s.to_owned();
s = XML_NCR_BASE10_REGEX
@@ -58,6 +62,7 @@ pub fn unescape_xml(s: &str) -> String {
.replace("&apos;", "'")
}
/// Unescapes Lua string escape sequences.
pub fn unescape_lua_str(s: &str) -> String {
let mut s = s.to_owned();
s = s
@@ -91,6 +96,7 @@ pub fn unescape_lua_str(s: &str) -> String {
s.replace("\\\\", "\\")
}
/// Checks if a string contains characters that need to be escaped in Lua strings.
pub fn lua_str_contains_need_escape(s: &str) -> bool {
s.contains('\\')
|| s.contains('\n')
@@ -103,6 +109,7 @@ pub fn lua_str_contains_need_escape(s: &str) -> bool {
|| s.contains('"')
}
/// Checks if a string contains characters that need to be escaped in Lua keys.
pub fn lua_key_contains_need_escape(s: &str) -> bool {
s.chars().next().map_or(false, |c| c.is_ascii_digit())
}

View File

@@ -1,9 +1,11 @@
//! Utilities for File Operations
use crate::scripts::{ALL_EXTS, ARCHIVE_EXTS};
use std::fs;
use std::io;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
/// Returns the relative path from `root` to `target`.
pub fn relative_path<P: AsRef<Path>, T: AsRef<Path>>(root: P, target: T) -> PathBuf {
let root = root
.as_ref()
@@ -40,6 +42,7 @@ pub fn relative_path<P: AsRef<Path>, T: AsRef<Path>>(root: P, target: T) -> Path
result
}
/// Finds all files in the specified directory and its subdirectories.
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);
@@ -79,6 +82,7 @@ pub fn find_files(path: &str, recursive: bool, no_ext_filter: bool) -> io::Resul
Ok(result)
}
/// Finds all archive files in the specified directory and its subdirectories.
pub fn find_arc_files(path: &str, recursive: bool) -> io::Result<Vec<String>> {
let mut result = Vec::new();
let dir_path = Path::new(&path);
@@ -116,6 +120,7 @@ pub fn find_arc_files(path: &str, recursive: bool) -> io::Result<Vec<String>> {
Ok(result)
}
/// Collects files from the specified path, either as a directory or a single file.
pub fn collect_files(
path: &str,
recursive: bool,
@@ -134,6 +139,7 @@ pub fn collect_files(
))
}
/// Collects archive files from the specified path, either as a directory or a single file.
pub fn collect_arc_files(path: &str, recursive: bool) -> io::Result<(Vec<String>, bool)> {
let pa = Path::new(path);
if pa.is_dir() {
@@ -148,6 +154,7 @@ pub fn collect_arc_files(path: &str, recursive: bool) -> io::Result<(Vec<String>
))
}
/// Reads the content of a file or standard input if the path is "-".
pub fn read_file<F: AsRef<Path> + ?Sized>(f: &F) -> io::Result<Vec<u8>> {
let mut content = Vec::new();
if f.as_ref() == Path::new("-") {
@@ -158,6 +165,7 @@ pub fn read_file<F: AsRef<Path> + ?Sized>(f: &F) -> io::Result<Vec<u8>> {
Ok(content)
}
/// Writes content to a file or standard output if the path is "-".
pub fn write_file<F: AsRef<Path> + ?Sized>(f: &F) -> io::Result<Box<dyn Write>> {
Ok(if f.as_ref() == Path::new("-") {
Box::new(io::stdout())
@@ -166,6 +174,7 @@ pub fn write_file<F: AsRef<Path> + ?Sized>(f: &F) -> io::Result<Box<dyn Write>>
})
}
/// Ensures that the parent directory for the specified path exists, creating it if necessary.
pub fn make_sure_dir_exists<F: AsRef<Path> + ?Sized>(f: &F) -> io::Result<()> {
let path = f.as_ref();
if let Some(parent) = path.parent() {

View File

@@ -1,6 +1,10 @@
//! Image Utilities
use crate::types::*;
use anyhow::Result;
/// Reverses the alpha values of an image.
///
/// Only supports RGBA or BGRA images with 8-bit depth.
pub fn reverse_alpha_values(data: &mut ImageData) -> Result<()> {
if data.color_type != ImageColorType::Rgba && data.color_type != ImageColorType::Bgra {
return Err(anyhow::anyhow!("Image is not RGBA or BGRA"));
@@ -16,6 +20,9 @@ pub fn reverse_alpha_values(data: &mut ImageData) -> Result<()> {
Ok(())
}
/// Converts a BGR image to BGRA format.
///
/// Only supports BGR images with 8-bit depth.
pub fn convert_bgr_to_bgra(data: &mut ImageData) -> Result<()> {
if data.color_type != ImageColorType::Bgr {
return Err(anyhow::anyhow!("Image is not BGR"));
@@ -37,6 +44,9 @@ pub fn convert_bgr_to_bgra(data: &mut ImageData) -> Result<()> {
Ok(())
}
/// Converts a BGR image to RGB format.
///
/// Only supports BGR images with 8-bit depth.
pub fn convert_bgr_to_rgb(data: &mut ImageData) -> Result<()> {
if data.color_type != ImageColorType::Bgr {
return Err(anyhow::anyhow!("Image is not BGR"));
@@ -55,6 +65,9 @@ pub fn convert_bgr_to_rgb(data: &mut ImageData) -> Result<()> {
Ok(())
}
/// Converts a BGRA image to BGR format.
///
/// Only supports BGRA images with 8-bit depth.
pub fn convert_bgra_to_bgr(data: &mut ImageData) -> Result<()> {
if data.color_type != ImageColorType::Bgra {
return Err(anyhow::anyhow!("Image is not BGRA"));
@@ -75,6 +88,9 @@ pub fn convert_bgra_to_bgr(data: &mut ImageData) -> Result<()> {
Ok(())
}
/// Converts a BGRA image to RGBA format.
///
/// Only supports BGRA images with 8-bit depth.
pub fn convert_bgra_to_rgba(data: &mut ImageData) -> Result<()> {
if data.color_type != ImageColorType::Bgra {
return Err(anyhow::anyhow!("Image is not BGRA"));
@@ -93,6 +109,9 @@ pub fn convert_bgra_to_rgba(data: &mut ImageData) -> Result<()> {
Ok(())
}
/// Converts an RGB image to RGBA format.
///
/// Only supports RGB images with 8-bit depth.
pub fn convert_rgb_to_rgba(data: &mut ImageData) -> Result<()> {
if data.color_type != ImageColorType::Rgb {
return Err(anyhow::anyhow!("Image is not RGB"));
@@ -114,6 +133,9 @@ pub fn convert_rgb_to_rgba(data: &mut ImageData) -> Result<()> {
Ok(())
}
/// Converts an RGB image to BGR format.
///
/// Only supports RGB images with 8-bit depth.
pub fn convert_rgb_to_bgr(data: &mut ImageData) -> Result<()> {
if data.color_type != ImageColorType::Rgb {
return Err(anyhow::anyhow!("Image is not RGB"));
@@ -132,6 +154,9 @@ pub fn convert_rgb_to_bgr(data: &mut ImageData) -> Result<()> {
Ok(())
}
/// Converts an RGBA image to BGRA format.
///
/// Only supports RGBA images with 8-bit depth.
pub fn convert_rgba_to_bgra(data: &mut ImageData) -> Result<()> {
if data.color_type != ImageColorType::Rgba {
return Err(anyhow::anyhow!("Image is not RGBA"));
@@ -150,6 +175,12 @@ pub fn convert_rgba_to_bgra(data: &mut ImageData) -> Result<()> {
Ok(())
}
/// Encodes an image to the specified format and writes it to a file.
///
/// * `data` - The image data to encode.
/// * `typ` - The output image format.
/// * `filename` - The path of the file to write the encoded image to.
/// * `config` - Extra configuration.
pub fn encode_img(
mut data: ImageData,
typ: ImageOutputType,
@@ -256,6 +287,7 @@ pub fn encode_img(
}
}
/// Loads a PNG image from the given reader and returns its data.
pub fn load_png<R: std::io::Read>(data: R) -> Result<ImageData> {
let decoder = png::Decoder::new(data);
let mut reader = decoder.read_info()?;
@@ -289,6 +321,10 @@ pub fn load_png<R: std::io::Read>(data: R) -> Result<ImageData> {
})
}
/// Decodes an image from the specified file path and returns its data.
///
/// * `typ` - The type of the image to decode.
/// * `filename` - The path of the file to decode.
pub fn decode_img(typ: ImageOutputType, filename: &str) -> Result<ImageData> {
match typ {
ImageOutputType::Png => {
@@ -361,6 +397,15 @@ pub fn decode_img(typ: ImageOutputType, filename: &str) -> Result<ImageData> {
}
}
/// Draws an image on a canvas with specified offsets.
///
/// * `img` - The image data to draw.
/// * `canvas_width` - The width of the canvas.
/// * `canvas_height` - The height of the canvas.
/// * `offset_x` - The horizontal offset to start drawing the image.
/// * `offset_y` - The vertical offset to start drawing the image.
///
/// Returns the canvas image data.
pub fn draw_on_canvas(
img: ImageData,
canvas_width: u32,
@@ -397,6 +442,7 @@ pub fn draw_on_canvas(
})
}
/// Flips an image vertically.
pub fn flip_image(data: &mut ImageData) -> Result<()> {
if data.height <= 1 {
return Ok(());
@@ -420,6 +466,9 @@ pub fn flip_image(data: &mut ImageData) -> Result<()> {
Ok(())
}
/// Applies opacity to an image.
///
/// Only supports RGBA or BGRA images with 8-bit depth.
pub fn apply_opacity(img: &mut ImageData, opacity: u8) -> Result<()> {
if img.color_type != ImageColorType::Rgba && img.color_type != ImageColorType::Bgra {
return Err(anyhow::anyhow!("Image is not RGBA or BGRA"));
@@ -435,6 +484,13 @@ pub fn apply_opacity(img: &mut ImageData, opacity: u8) -> Result<()> {
Ok(())
}
/// Draws an image on another image with specified opacity.
///
/// * `base` - The base image to draw on.
/// * `diff` - The image to draw with opacity.
/// * `left` - The horizontal offset to start drawing the image.
/// * `top` - The vertical offset to start drawing the image.
/// * `opacity` - The opacity level to apply to the drawn image (0-255
pub fn draw_on_img_with_opacity(
base: &mut ImageData,
diff: &ImageData,

View File

@@ -1,4 +1,7 @@
//! Macros Utilities
#[macro_export]
/// A macro likes `try!` but returns `Option<Result<T, E>>`.
macro_rules! try_option {
($expr:expr $(,)?) => {
match $expr {

View File

@@ -1,3 +1,4 @@
//! Utility functions and modules.
#[cfg(feature = "utils-bit-stream")]
pub mod bit_stream;
pub mod counter;

View File

@@ -1,7 +1,9 @@
//! Name Replacement Utilities
use crate::types::*;
use anyhow::Result;
use std::collections::HashMap;
/// Read Name Replacement Table from CSV
pub fn read_csv(path: &str) -> Result<HashMap<String, String>> {
let mut reader = csv::ReaderBuilder::new()
.has_headers(true)
@@ -17,6 +19,7 @@ pub fn read_csv(path: &str) -> Result<HashMap<String, String>> {
Ok(map)
}
/// Replace names in the message with the given name table.
pub fn replace_message(mes: &mut Vec<Message>, name_table: &HashMap<String, String>) {
for message in mes.iter_mut() {
if let Some(name) = &message.name {

View File

@@ -1,3 +1,4 @@
//! PCM Utilities
use crate::ext::io::*;
use crate::types::*;
use crate::utils::struct_pack::*;
@@ -6,15 +7,27 @@ use msg_tool_macro::*;
use std::io::{Read, Seek, Write};
#[derive(Debug, StructPack, StructUnpack)]
/// PCM Audio Format
pub struct PcmFormat {
format_tag: u16,
channels: u16,
sample_rate: u32,
average_bytes_per_second: u32,
block_align: u16,
bits_per_sample: u16,
/// The format tag
pub format_tag: u16,
/// The number of channels
pub channels: u16,
/// The sample rate
pub sample_rate: u32,
/// The average bytes per second
pub average_bytes_per_second: u32,
/// The block alignment
pub block_align: u16,
/// The bits per sample
pub bits_per_sample: u16,
}
/// Writes PCM data to a file.
///
/// * `format` - The PCM format to write.
/// * `reader` - The reader to read PCM data from.
/// * `writer` - The writer to write PCM data to.
pub fn write_pcm<W: Write + Seek, R: Read>(
format: &PcmFormat,
mut reader: R,

View File

@@ -1,3 +1,4 @@
//! String Utilities
use crate::types::*;
use crate::utils::encoding::*;
use anyhow::Result;

View File

@@ -1,13 +1,26 @@
//! Binary Struct Packing and Unpacking Utilities
use crate::types::Encoding;
use anyhow::Result;
use msg_tool_macro::struct_unpack_impl_for_num;
use std::io::{Read, Seek, Write};
/// Trait for unpacking a struct from a binary stream.
pub trait StructUnpack: Sized {
/// Unpacks a struct from a binary stream.
///
/// * `reader` - The reader to read the binary data from.
/// * `big` - Whether the data is in big-endian format.
/// * `encoding` - The encoding to use for string fields.
fn unpack<R: Read + Seek>(reader: R, big: bool, encoding: Encoding) -> Result<Self>;
}
/// Trait for packing a struct into a binary stream.
pub trait StructPack: Sized {
/// Packs a struct into a binary stream.
///
/// * `writer` - The writer to write the binary data to.
/// * `big` - Whether to use big-endian format.
/// * `encoding` - The encoding to use for string fields.
fn pack<W: Write>(&self, writer: &mut W, big: bool, encoding: Encoding) -> Result<()>;
}