mirror of
https://github.com/lifegpc/msg-tool.git
synced 2026-06-07 05:18:44 +08:00
Add support to unpack xp3 file in pe(exe) file
This commit is contained in:
@@ -2,6 +2,7 @@ mod archive;
|
||||
#[allow(dead_code)]
|
||||
mod consts;
|
||||
mod crypt;
|
||||
mod pe;
|
||||
mod read;
|
||||
mod reader;
|
||||
mod segmenter;
|
||||
@@ -134,10 +135,15 @@ impl ScriptBuilder for Xp3ArchiveBuilder {
|
||||
config: &ExtraConfig,
|
||||
_archive: Option<&Box<dyn Script>>,
|
||||
) -> Result<Box<dyn Script + Send + Sync>> {
|
||||
let mut base_offset = 0;
|
||||
if buf.starts_with(b"MZ") {
|
||||
base_offset = pe::get_base_offset(&buf)?;
|
||||
}
|
||||
Ok(Box::new(Xp3Archive::new(
|
||||
MemReader::new(buf),
|
||||
config,
|
||||
filename,
|
||||
base_offset,
|
||||
)?))
|
||||
}
|
||||
|
||||
@@ -149,24 +155,47 @@ impl ScriptBuilder for Xp3ArchiveBuilder {
|
||||
config: &ExtraConfig,
|
||||
_archive: Option<&Box<dyn Script>>,
|
||||
) -> Result<Box<dyn Script + Send + Sync>> {
|
||||
let file = std::fs::File::open(filename)?;
|
||||
Ok(Box::new(Xp3Archive::new(file, config, filename)?))
|
||||
let mut file = std::fs::File::open(filename)?;
|
||||
let mut base_offset = 0;
|
||||
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(Xp3Archive::new(
|
||||
file,
|
||||
config,
|
||||
filename,
|
||||
base_offset,
|
||||
)?))
|
||||
}
|
||||
|
||||
fn build_script_from_reader<'a>(
|
||||
&self,
|
||||
reader: Box<dyn ReadSeek + Send + Sync + 'a>,
|
||||
mut reader: Box<dyn ReadSeek + Send + Sync + 'a>,
|
||||
filename: &str,
|
||||
_encoding: Encoding,
|
||||
_archive_encoding: Encoding,
|
||||
config: &ExtraConfig,
|
||||
_archive: Option<&Box<dyn Script>>,
|
||||
) -> Result<Box<dyn Script + Send + Sync + 'a>> {
|
||||
Ok(Box::new(Xp3Archive::new(reader, config, filename)?))
|
||||
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(Xp3Archive::new(
|
||||
reader,
|
||||
config,
|
||||
filename,
|
||||
base_offset,
|
||||
)?))
|
||||
}
|
||||
|
||||
fn extensions(&self) -> &'static [&'static str] {
|
||||
&["xp3", "bin", "dat"]
|
||||
&["xp3", "bin", "dat", "exe"]
|
||||
}
|
||||
|
||||
fn script_type(&self) -> &'static ScriptType {
|
||||
@@ -187,10 +216,20 @@ impl ScriptBuilder for Xp3ArchiveBuilder {
|
||||
Ok(Box::new(Xp3ArchiveWriter::new(filename, files, config)?))
|
||||
}
|
||||
|
||||
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 >= 11 && buf.starts_with(consts::XP3_MAGIC) {
|
||||
return Some(100);
|
||||
}
|
||||
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(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -210,8 +249,9 @@ impl<'a> Xp3Archive<'a> {
|
||||
stream: T,
|
||||
config: &ExtraConfig,
|
||||
filename: &str,
|
||||
base_offset: u64,
|
||||
) -> Result<Self> {
|
||||
let mut archive = archive::Xp3Archive::new(stream, config, filename)?;
|
||||
let mut archive = archive::Xp3Archive::new(stream, config, filename, base_offset)?;
|
||||
if config.xp3_debug_archive {
|
||||
println!("Debug info for {}:\n{:#?}", filename, archive);
|
||||
// Try flush stdout.
|
||||
|
||||
34
src/scripts/kirikiri/archive/xp3/pe.rs
Normal file
34
src/scripts/kirikiri/archive/xp3/pe.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
use super::consts::*;
|
||||
use anyhow::Result;
|
||||
use memchr::memmem::find;
|
||||
use pelite::{PeFile, Wrap};
|
||||
|
||||
pub fn get_base_offset<D: AsRef<[u8]> + ?Sized>(data: &D) -> Result<u64> {
|
||||
let file = PeFile::from_bytes(data)?;
|
||||
if let Some(rsrc) = file.section_headers().by_name(".rsrc") {
|
||||
let bytes = file.get_section_bytes(rsrc)?;
|
||||
if let Some(pos) = find(bytes, XP3_MAGIC) {
|
||||
return Ok(rsrc.file_range().start as u64 + pos as u64);
|
||||
}
|
||||
}
|
||||
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 >= data.len() {
|
||||
anyhow::bail!("No overlay for pe image.");
|
||||
}
|
||||
for i in (aligned_offset..(data.len() - 11)).step_by(0x10) {
|
||||
if &data[i..i + 11] == XP3_MAGIC {
|
||||
return Ok(i as u64);
|
||||
}
|
||||
}
|
||||
anyhow::bail!("Failed to find xp3 file in pe file.")
|
||||
}
|
||||
@@ -12,6 +12,7 @@ impl<'a> Xp3Archive<'a> {
|
||||
stream: T,
|
||||
config: &ExtraConfig,
|
||||
filename: &str,
|
||||
base_offset: u64,
|
||||
) -> Result<Self> {
|
||||
#[allow(unused_mut)]
|
||||
let mut crypt: Box<dyn Crypt + Send + Sync> =
|
||||
@@ -25,7 +26,6 @@ impl<'a> Xp3Archive<'a> {
|
||||
Box::new(NoCrypt::new())
|
||||
};
|
||||
let mut stream = Box::new(stream);
|
||||
let base_offset = 0;
|
||||
if base_offset != 0 {
|
||||
stream.seek(SeekFrom::Start(base_offset))?;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user