mirror of
https://github.com/lifegpc/msg-tool.git
synced 2026-07-08 01:31:53 +08:00
Add support to unpack Yu-RIS archive in exe file
This commit is contained in:
@@ -124,7 +124,7 @@ will-plus-img = ["will-plus", "image"]
|
|||||||
yaneurao = []
|
yaneurao = []
|
||||||
yaneurao-itufuru = ["yaneurao", "utils-xored-stream"]
|
yaneurao-itufuru = ["yaneurao", "utils-xored-stream"]
|
||||||
yuris = ["dep:chrono", "dep:hex", "utils-serde-base64bytes", "utils-xored-stream"]
|
yuris = ["dep:chrono", "dep:hex", "utils-serde-base64bytes", "utils-xored-stream"]
|
||||||
yuris-arc = ["yuris", "dep:adler", "dep:crc32fast", "flate2", "dep:int-enum", "utils-murmur2", "dep:xxhash-rust", "xxhash-rust/xxh32", "zopfli"]
|
yuris-arc = ["yuris", "dep:adler", "dep:crc32fast", "flate2", "dep:int-enum", "dep:pelite", "utils-murmur2", "dep:xxhash-rust", "xxhash-rust/xxh32", "zopfli"]
|
||||||
yuris-img = ["yuris", "image", "qoi", "webp"]
|
yuris-img = ["yuris", "image", "qoi", "webp"]
|
||||||
# basic feature
|
# basic feature
|
||||||
image = ["dep:png"]
|
image = ["dep:png"]
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
|
mod pe;
|
||||||
pub mod ypf;
|
pub mod ypf;
|
||||||
|
|||||||
39
src/scripts/yuris/arc/pe.rs
Normal file
39
src/scripts/yuris/arc/pe.rs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use pelite::{PeFile, Wrap};
|
||||||
|
|
||||||
|
const YSER_MAGIC: &[u8; 4] = b"YSER";
|
||||||
|
|
||||||
|
/// Find the YPF archive base offset inside a PE (EXE) file.
|
||||||
|
///
|
||||||
|
/// Searches the PE overlay for the "YSER" header signature at 0x10-aligned
|
||||||
|
/// boundaries, then reads the 32-bit header size field at offset+4 and returns
|
||||||
|
/// `YSER_offset + header_size` as the start of the YPF data.
|
||||||
|
pub fn get_base_offset<D: AsRef<[u8]> + ?Sized>(data: &D) -> Result<u64> {
|
||||||
|
let file = PeFile::from_bytes(data)?;
|
||||||
|
let last_section_end = file
|
||||||
|
.section_headers()
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.PointerToRawData + s.SizeOfRawData)
|
||||||
|
.max()
|
||||||
|
.unwrap_or_else(|| match file.optional_header() {
|
||||||
|
Wrap::T32(h) => h.SizeOfHeaders,
|
||||||
|
Wrap::T64(h) => h.SizeOfHeaders,
|
||||||
|
});
|
||||||
|
let aligned_offset = ((last_section_end + 0xF) & !0xF) as usize;
|
||||||
|
let data = data.as_ref();
|
||||||
|
if aligned_offset + 8 > data.len() {
|
||||||
|
anyhow::bail!("No overlay for pe image.");
|
||||||
|
}
|
||||||
|
for i in (aligned_offset..(data.len() - 8)).step_by(0x10) {
|
||||||
|
if &data[i..i + 4] == YSER_MAGIC {
|
||||||
|
let header_size = u32::from_le_bytes([
|
||||||
|
data[i + 4],
|
||||||
|
data[i + 5],
|
||||||
|
data[i + 6],
|
||||||
|
data[i + 7],
|
||||||
|
]);
|
||||||
|
return Ok(i as u64 + header_size as u64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
anyhow::bail!("Failed to find YSER header in pe file.")
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
//! Yu-Ris Archive (.ypf)
|
//! Yu-Ris Archive (.ypf)
|
||||||
|
use super::pe;
|
||||||
use crate::ext::io::*;
|
use crate::ext::io::*;
|
||||||
use crate::ext::mutex::*;
|
use crate::ext::mutex::*;
|
||||||
use crate::scripts::base::*;
|
use crate::scripts::base::*;
|
||||||
@@ -45,10 +46,15 @@ impl ScriptBuilder for YpfBuilder {
|
|||||||
config: &ExtraConfig,
|
config: &ExtraConfig,
|
||||||
_archive: Option<&Box<dyn Script>>,
|
_archive: Option<&Box<dyn Script>>,
|
||||||
) -> Result<Box<dyn Script + Send + Sync>> {
|
) -> Result<Box<dyn Script + Send + Sync>> {
|
||||||
|
let mut base_offset = 0;
|
||||||
|
if data.starts_with(b"MZ") {
|
||||||
|
base_offset = pe::get_base_offset(&data)?;
|
||||||
|
}
|
||||||
Ok(Box::new(YPF::new(
|
Ok(Box::new(YPF::new(
|
||||||
MemReader::new(data),
|
MemReader::new(data),
|
||||||
archive_encoding,
|
archive_encoding,
|
||||||
config,
|
config,
|
||||||
|
base_offset,
|
||||||
)?))
|
)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,42 +68,69 @@ impl ScriptBuilder for YpfBuilder {
|
|||||||
) -> Result<Box<dyn Script + Send + Sync>> {
|
) -> Result<Box<dyn Script + Send + Sync>> {
|
||||||
if filename == "-" {
|
if filename == "-" {
|
||||||
let data = crate::utils::files::read_file(filename)?;
|
let data = crate::utils::files::read_file(filename)?;
|
||||||
|
let mut base_offset = 0;
|
||||||
|
if data.starts_with(b"MZ") {
|
||||||
|
base_offset = pe::get_base_offset(&data)?;
|
||||||
|
}
|
||||||
Ok(Box::new(YPF::new(
|
Ok(Box::new(YPF::new(
|
||||||
MemReader::new(data),
|
MemReader::new(data),
|
||||||
archive_encoding,
|
archive_encoding,
|
||||||
config,
|
config,
|
||||||
|
base_offset,
|
||||||
)?))
|
)?))
|
||||||
} else {
|
} else {
|
||||||
let f = std::fs::File::open(filename)?;
|
let mut file = std::fs::File::open(filename)?;
|
||||||
let reader = std::io::BufReader::new(f);
|
let mut base_offset = 0;
|
||||||
Ok(Box::new(YPF::new(reader, archive_encoding, config)?))
|
if file.peek_and_equal(b"MZ").is_ok() {
|
||||||
|
let mp = pelite::FileMap::open(filename)?;
|
||||||
|
base_offset = pe::get_base_offset(&mp)?;
|
||||||
|
}
|
||||||
|
Ok(Box::new(YPF::new(file, archive_encoding, config, base_offset)?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_script_from_reader<'a>(
|
fn build_script_from_reader<'a>(
|
||||||
&self,
|
&self,
|
||||||
reader: Box<dyn ReadSeek + Send + Sync + 'a>,
|
mut reader: Box<dyn ReadSeek + Send + Sync + 'a>,
|
||||||
_filename: &str,
|
_filename: &str,
|
||||||
_encoding: Encoding,
|
_encoding: Encoding,
|
||||||
archive_encoding: Encoding,
|
archive_encoding: Encoding,
|
||||||
config: &ExtraConfig,
|
config: &ExtraConfig,
|
||||||
_archive: Option<&Box<dyn Script>>,
|
_archive: Option<&Box<dyn Script>>,
|
||||||
) -> Result<Box<dyn Script + Send + Sync + 'a>> {
|
) -> Result<Box<dyn Script + Send + Sync + 'a>> {
|
||||||
Ok(Box::new(YPF::new(reader, archive_encoding, config)?))
|
let mut base_offset = 0;
|
||||||
|
if reader.peek_and_equal(b"MZ").is_ok() {
|
||||||
|
let mut data = Vec::new();
|
||||||
|
let pos = reader.stream_position()?;
|
||||||
|
reader.read_to_end(&mut data)?;
|
||||||
|
reader.seek(SeekFrom::Start(pos))?;
|
||||||
|
base_offset = pe::get_base_offset(&data)?;
|
||||||
|
}
|
||||||
|
Ok(Box::new(YPF::new(reader, archive_encoding, config, base_offset)?))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extensions(&self) -> &'static [&'static str] {
|
fn extensions(&self) -> &'static [&'static str] {
|
||||||
&["ypf"]
|
&["ypf", "exe"]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn script_type(&self) -> &'static ScriptType {
|
fn script_type(&self) -> &'static ScriptType {
|
||||||
&ScriptType::YurisYPF
|
&ScriptType::YurisYPF
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
|
fn is_this_format(&self, filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
|
||||||
if buf_len >= 4 && buf.starts_with(b"YPF\0") {
|
if buf_len >= 4 && buf.starts_with(b"YPF\0") {
|
||||||
return Some(20);
|
return Some(20);
|
||||||
}
|
}
|
||||||
|
if buf_len >= 2 && buf.starts_with(b"MZ") {
|
||||||
|
let p = std::path::Path::new(filename);
|
||||||
|
if p.exists() {
|
||||||
|
if let Ok(file) = pelite::FileMap::open(p) {
|
||||||
|
if pe::get_base_offset(&file).is_ok() {
|
||||||
|
return Some(20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,7 +377,15 @@ fn cal_name_hash(name: &[u8], typ: NameHashType) -> u32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'b, T: Read + Seek + std::fmt::Debug + Send + Sync + 'b> YPF<'b, T> {
|
impl<'b, T: Read + Seek + std::fmt::Debug + Send + Sync + 'b> YPF<'b, T> {
|
||||||
pub fn new(mut reader: T, archive_encoding: Encoding, config: &ExtraConfig) -> Result<Self> {
|
pub fn new(
|
||||||
|
mut reader: T,
|
||||||
|
archive_encoding: Encoding,
|
||||||
|
config: &ExtraConfig,
|
||||||
|
base_offset: u64,
|
||||||
|
) -> Result<Self> {
|
||||||
|
if base_offset > 0 {
|
||||||
|
reader.seek(SeekFrom::Start(base_offset))?;
|
||||||
|
}
|
||||||
let mut header = [0u8; 4];
|
let mut header = [0u8; 4];
|
||||||
reader.read_exact(&mut header)?;
|
reader.read_exact(&mut header)?;
|
||||||
if &header != b"YPF\0" {
|
if &header != b"YPF\0" {
|
||||||
|
|||||||
Reference in New Issue
Block a user