Compare commits

...

2 Commits

Author SHA1 Message Date
Anna 6c125bace0
chore: bump version to 2.0.0 2022-11-02 15:43:39 -04:00
Anna 1a4588bb8d
feat: add support for ttmp v1 2022-11-02 15:43:25 -04:00
7 changed files with 89 additions and 59 deletions

View File

@ -1,6 +1,6 @@
[package]
name = "ttmp"
version = "1.0.0"
version = "2.0.0"
edition = "2021"
autoexamples = true

View File

@ -16,7 +16,6 @@ pub fn main() {
let files = extractor.all_files_sorted();
let mut data_file = zip.by_name("TTMPD.mpd").unwrap();
let version = &*extractor.manifest().version;
std::fs::create_dir_all("files").unwrap();
@ -69,31 +68,3 @@ pub struct SavedFile {
pub group: 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)
}
}

View File

@ -1,18 +1,51 @@
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};
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 error;
pub(crate) mod tracking_reader;
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 manifest = zip.by_name("TTMPL.mpl").map_err(Error::Zip)?;
let manifest = serde_json::from_reader(manifest).map_err(Error::InvalidManifest)?;
Ok((manifest, zip))
let mut manifest = StreamDeserializer::new(IoRead::new(manifest))
.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))
}

View File

@ -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 => &[],
},
}
}
}

View File

@ -7,6 +7,7 @@ pub use self::{
simple_mod::SimpleMod,
};
pub mod manifest_kind;
pub mod mod_group;
pub mod mod_option;
pub mod mod_pack;

View File

@ -7,8 +7,10 @@ pub struct SimpleMod {
pub category: String,
pub full_path: String,
pub dat_file: String,
#[serde(default)]
pub is_default: bool,
pub mod_offset: usize,
pub mod_size: usize,
#[serde(default)]
pub mod_pack_entry: Option<serde_json::Value>,
}

View File

@ -8,8 +8,9 @@ use sqpack::binrw::{BinRead, BinWriterExt, VecArgs};
use sqpack::binrw::meta::ReadEndian;
use zip::ZipArchive;
use crate::{Error, ModPack};
use crate::Error;
use crate::error::Result;
use crate::model::manifest_kind::ManifestKind;
use crate::model::SimpleMod;
use crate::tracking_reader::TrackingReader;
@ -20,7 +21,7 @@ impl<T> WriteSeek for T
where T: Write + Seek {}
pub struct TtmpExtractor<R> {
manifest: ModPack,
manifest: ManifestKind,
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
}
pub fn into_manifest(self) -> ModPack {
pub fn into_manifest(self) -> ManifestKind {
self.manifest
}
@ -92,34 +93,34 @@ impl<R: Read + Seek> TtmpExtractor<R> {
let mut all_files = Vec::new();
let mut seen_groups: HashMap<&str, usize> = HashMap::new();
if let Some(pages) = &self.manifest.mod_pack_pages {
for page in pages {
for group in &page.mod_groups {
let seen = seen_groups.entry(&group.group_name).or_default();
*seen += 1;
if let ManifestKind::V2(pack) = &self.manifest {
if let Some(pages) = &pack.mod_pack_pages {
for page in pages {
for group in &page.mod_groups {
let seen = seen_groups.entry(&group.group_name).or_default();
*seen += 1;
for option in &group.option_list {
for file in &option.mods_jsons {
all_files.push(ModFile {
group: Some(&*option.group_name),
group_occurence: *seen,
option: Some(&*option.name),
file,
});
for option in &group.option_list {
for file in &option.mods_jsons {
all_files.push(ModFile {
group: Some(&*option.group_name),
group_occurence: *seen,
option: Some(&*option.name),
file,
});
}
}
}
}
}
}
if let Some(list) = &self.manifest.simple_mods_list {
all_files.extend(list.iter().map(|file| ModFile {
group: None,
group_occurence: 0,
option: None,
file,
}));
}
all_files.extend(self.manifest.simple_mods_list().iter().map(|file| ModFile {
group: None,
group_occurence: 0,
option: None,
file,
}));
all_files.sort_unstable_by_key(|file| file.file.mod_offset);
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<()> {
let std_info: SqPackFileInfo = Self::read_struct(&mut reader, buf)?;
println!("{:#?}", std_info);
let blocks: Vec<LodBlock> = (0..std_info.number_of_blocks)
.map(|_| Self::read_struct(&mut reader, buf))
.collect::<Result<_>>()?;
println!("{:#?}", blocks);
let sub_block_count = blocks
.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> {
let header: DatBlockHeader = Self::read_struct(&mut reader, buf)?;
println!("{:#?}", header);
let (read, actual) = if header.compressed_size == 32_000 {
// uncompressed