Compare commits
2 Commits
065b6cc8e2
...
6c125bace0
Author | SHA1 | Date |
---|---|---|
Anna | 6c125bace0 | |
Anna | 1a4588bb8d |
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "ttmp"
|
name = "ttmp"
|
||||||
version = "1.0.0"
|
version = "2.0.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
autoexamples = true
|
autoexamples = true
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,6 @@ pub fn main() {
|
||||||
let files = extractor.all_files_sorted();
|
let files = extractor.all_files_sorted();
|
||||||
|
|
||||||
let mut data_file = zip.by_name("TTMPD.mpd").unwrap();
|
let mut data_file = zip.by_name("TTMPD.mpd").unwrap();
|
||||||
let version = &*extractor.manifest().version;
|
|
||||||
|
|
||||||
std::fs::create_dir_all("files").unwrap();
|
std::fs::create_dir_all("files").unwrap();
|
||||||
|
|
||||||
|
@ -69,31 +68,3 @@ pub struct SavedFile {
|
||||||
pub group: Option<String>,
|
pub group: Option<String>,
|
||||||
pub option: Option<String>,
|
pub option: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Multiplexer<W1, W2> {
|
|
||||||
one: W1,
|
|
||||||
two: W2,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<W1, W2> Multiplexer<W1, W2> {
|
|
||||||
fn new(one: W1, two: W2) -> Self {
|
|
||||||
Self {
|
|
||||||
one,
|
|
||||||
two,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<W1: Write, W2: Write> Write for Multiplexer<W1, W2> {
|
|
||||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
|
||||||
let one = self.one.write(buf);
|
|
||||||
let two = self.two.write(buf);
|
|
||||||
one.and(two)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush(&mut self) -> std::io::Result<()> {
|
|
||||||
let one = self.one.flush();
|
|
||||||
let two = self.two.flush();
|
|
||||||
one.and(two)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
41
src/lib.rs
41
src/lib.rs
|
@ -1,18 +1,51 @@
|
||||||
use std::io::{Read, Seek};
|
use std::io::{Read, Seek};
|
||||||
|
|
||||||
|
use serde::de::Error as _;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde_json::de::IoRead;
|
||||||
|
use serde_json::StreamDeserializer;
|
||||||
pub use zip::{read::ZipFile, ZipArchive};
|
pub use zip::{read::ZipFile, ZipArchive};
|
||||||
|
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::model::ModPack;
|
use crate::model::{ModPack, SimpleMod};
|
||||||
|
use crate::model::manifest_kind::ManifestKind;
|
||||||
|
|
||||||
pub mod model;
|
pub mod model;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub(crate) mod tracking_reader;
|
pub(crate) mod tracking_reader;
|
||||||
pub mod ttmp_extractor;
|
pub mod ttmp_extractor;
|
||||||
|
|
||||||
pub fn from_reader<R: Read + Seek>(reader: R) -> Result<(ModPack, ZipArchive<R>)> {
|
pub fn from_reader<R: Read + Seek>(reader: R) -> Result<(ManifestKind, ZipArchive<R>)> {
|
||||||
let mut zip = ZipArchive::new(reader).map_err(Error::Zip)?;
|
let mut zip = ZipArchive::new(reader).map_err(Error::Zip)?;
|
||||||
let manifest = zip.by_name("TTMPL.mpl").map_err(Error::Zip)?;
|
let manifest = zip.by_name("TTMPL.mpl").map_err(Error::Zip)?;
|
||||||
let manifest = serde_json::from_reader(manifest).map_err(Error::InvalidManifest)?;
|
let mut manifest = StreamDeserializer::new(IoRead::new(manifest))
|
||||||
Ok((manifest, zip))
|
.collect::<Result<Vec<DesManifest>, _>>()
|
||||||
|
.map_err(Error::InvalidManifest)?;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum DesManifest {
|
||||||
|
V1(SimpleMod),
|
||||||
|
V2(ModPack),
|
||||||
|
}
|
||||||
|
|
||||||
|
let all_v1 = manifest.iter().all(|inner| matches!(inner, DesManifest::V1(_)));
|
||||||
|
let transformed = if all_v1 {
|
||||||
|
let mods: Vec<SimpleMod> = manifest.into_iter()
|
||||||
|
.map(|inner| match inner {
|
||||||
|
DesManifest::V1(inner) => inner,
|
||||||
|
_ => unreachable!(),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
ManifestKind::V1(mods)
|
||||||
|
} else if manifest.len() == 1 && matches!(&manifest[0], DesManifest::V2(_)) {
|
||||||
|
ManifestKind::V2(match manifest.remove(0) {
|
||||||
|
DesManifest::V2(pack) => pack,
|
||||||
|
_ => unreachable!(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return Err(Error::InvalidManifest(serde_json::Error::custom("unrecognised manifest")));
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((transformed, zip))
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
use crate::model::{ModPack, SimpleMod};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ManifestKind {
|
||||||
|
V1(Vec<SimpleMod>),
|
||||||
|
V2(ModPack),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ManifestKind {
|
||||||
|
pub fn simple_mods_list(&self) -> &[SimpleMod] {
|
||||||
|
match self {
|
||||||
|
Self::V1(mods) => mods,
|
||||||
|
Self::V2(pack) => match &pack.simple_mods_list {
|
||||||
|
Some(list) => list,
|
||||||
|
None => &[],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ pub use self::{
|
||||||
simple_mod::SimpleMod,
|
simple_mod::SimpleMod,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub mod manifest_kind;
|
||||||
pub mod mod_group;
|
pub mod mod_group;
|
||||||
pub mod mod_option;
|
pub mod mod_option;
|
||||||
pub mod mod_pack;
|
pub mod mod_pack;
|
||||||
|
|
|
@ -7,8 +7,10 @@ pub struct SimpleMod {
|
||||||
pub category: String,
|
pub category: String,
|
||||||
pub full_path: String,
|
pub full_path: String,
|
||||||
pub dat_file: String,
|
pub dat_file: String,
|
||||||
|
#[serde(default)]
|
||||||
pub is_default: bool,
|
pub is_default: bool,
|
||||||
pub mod_offset: usize,
|
pub mod_offset: usize,
|
||||||
pub mod_size: usize,
|
pub mod_size: usize,
|
||||||
|
#[serde(default)]
|
||||||
pub mod_pack_entry: Option<serde_json::Value>,
|
pub mod_pack_entry: Option<serde_json::Value>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,9 @@ use sqpack::binrw::{BinRead, BinWriterExt, VecArgs};
|
||||||
use sqpack::binrw::meta::ReadEndian;
|
use sqpack::binrw::meta::ReadEndian;
|
||||||
use zip::ZipArchive;
|
use zip::ZipArchive;
|
||||||
|
|
||||||
use crate::{Error, ModPack};
|
use crate::Error;
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
|
use crate::model::manifest_kind::ManifestKind;
|
||||||
use crate::model::SimpleMod;
|
use crate::model::SimpleMod;
|
||||||
use crate::tracking_reader::TrackingReader;
|
use crate::tracking_reader::TrackingReader;
|
||||||
|
|
||||||
|
@ -20,7 +21,7 @@ impl<T> WriteSeek for T
|
||||||
where T: Write + Seek {}
|
where T: Write + Seek {}
|
||||||
|
|
||||||
pub struct TtmpExtractor<R> {
|
pub struct TtmpExtractor<R> {
|
||||||
manifest: ModPack,
|
manifest: ManifestKind,
|
||||||
zip: RefCell<ZipArchive<R>>,
|
zip: RefCell<ZipArchive<R>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,11 +35,11 @@ impl<R: Read + Seek> TtmpExtractor<R> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn manifest(&self) -> &ModPack {
|
pub fn manifest(&self) -> &ManifestKind {
|
||||||
&self.manifest
|
&self.manifest
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_manifest(self) -> ModPack {
|
pub fn into_manifest(self) -> ManifestKind {
|
||||||
self.manifest
|
self.manifest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,34 +93,34 @@ impl<R: Read + Seek> TtmpExtractor<R> {
|
||||||
let mut all_files = Vec::new();
|
let mut all_files = Vec::new();
|
||||||
|
|
||||||
let mut seen_groups: HashMap<&str, usize> = HashMap::new();
|
let mut seen_groups: HashMap<&str, usize> = HashMap::new();
|
||||||
if let Some(pages) = &self.manifest.mod_pack_pages {
|
if let ManifestKind::V2(pack) = &self.manifest {
|
||||||
for page in pages {
|
if let Some(pages) = &pack.mod_pack_pages {
|
||||||
for group in &page.mod_groups {
|
for page in pages {
|
||||||
let seen = seen_groups.entry(&group.group_name).or_default();
|
for group in &page.mod_groups {
|
||||||
*seen += 1;
|
let seen = seen_groups.entry(&group.group_name).or_default();
|
||||||
|
*seen += 1;
|
||||||
|
|
||||||
for option in &group.option_list {
|
for option in &group.option_list {
|
||||||
for file in &option.mods_jsons {
|
for file in &option.mods_jsons {
|
||||||
all_files.push(ModFile {
|
all_files.push(ModFile {
|
||||||
group: Some(&*option.group_name),
|
group: Some(&*option.group_name),
|
||||||
group_occurence: *seen,
|
group_occurence: *seen,
|
||||||
option: Some(&*option.name),
|
option: Some(&*option.name),
|
||||||
file,
|
file,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(list) = &self.manifest.simple_mods_list {
|
all_files.extend(self.manifest.simple_mods_list().iter().map(|file| ModFile {
|
||||||
all_files.extend(list.iter().map(|file| ModFile {
|
group: None,
|
||||||
group: None,
|
group_occurence: 0,
|
||||||
group_occurence: 0,
|
option: None,
|
||||||
option: None,
|
file,
|
||||||
file,
|
}));
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
all_files.sort_unstable_by_key(|file| file.file.mod_offset);
|
all_files.sort_unstable_by_key(|file| file.file.mod_offset);
|
||||||
all_files
|
all_files
|
||||||
|
@ -293,9 +294,11 @@ impl<R: Read> TtmpExtractor<R> {
|
||||||
|
|
||||||
fn extract_texture_file<T: Read, W: Write>(info: &SqPackFileInfoHeader, mut reader: T, mut writer: W, buf: &mut [u8]) -> Result<()> {
|
fn extract_texture_file<T: Read, W: Write>(info: &SqPackFileInfoHeader, mut reader: T, mut writer: W, buf: &mut [u8]) -> Result<()> {
|
||||||
let std_info: SqPackFileInfo = Self::read_struct(&mut reader, buf)?;
|
let std_info: SqPackFileInfo = Self::read_struct(&mut reader, buf)?;
|
||||||
|
println!("{:#?}", std_info);
|
||||||
let blocks: Vec<LodBlock> = (0..std_info.number_of_blocks)
|
let blocks: Vec<LodBlock> = (0..std_info.number_of_blocks)
|
||||||
.map(|_| Self::read_struct(&mut reader, buf))
|
.map(|_| Self::read_struct(&mut reader, buf))
|
||||||
.collect::<Result<_>>()?;
|
.collect::<Result<_>>()?;
|
||||||
|
println!("{:#?}", blocks);
|
||||||
|
|
||||||
let sub_block_count = blocks
|
let sub_block_count = blocks
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -341,6 +344,7 @@ impl<R: Read> TtmpExtractor<R> {
|
||||||
|
|
||||||
fn read_block_into<T: Read, W: Write>(mut reader: T, mut writer: W, buf: &mut [u8], size: usize) -> Result<u64> {
|
fn read_block_into<T: Read, W: Write>(mut reader: T, mut writer: W, buf: &mut [u8], size: usize) -> Result<u64> {
|
||||||
let header: DatBlockHeader = Self::read_struct(&mut reader, buf)?;
|
let header: DatBlockHeader = Self::read_struct(&mut reader, buf)?;
|
||||||
|
println!("{:#?}", header);
|
||||||
|
|
||||||
let (read, actual) = if header.compressed_size == 32_000 {
|
let (read, actual) = if header.compressed_size == 32_000 {
|
||||||
// uncompressed
|
// uncompressed
|
||||||
|
|
Loading…
Reference in New Issue