Compare commits
4 Commits
0a64215cb1
...
0bf9129986
Author | SHA1 | Date |
---|---|---|
Anna | 0bf9129986 | |
Anna | f39eab6c51 | |
Anna | 0a00b48c4d | |
Anna | f31dfd704d |
43
Cargo.toml
43
Cargo.toml
|
@ -1,23 +1,42 @@
|
|||
[package]
|
||||
name = "ttmp"
|
||||
version = "5.2.0"
|
||||
version = "6.0.0"
|
||||
edition = "2021"
|
||||
autoexamples = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
model = [
|
||||
"serde_json",
|
||||
]
|
||||
logic = [
|
||||
"model",
|
||||
"blake3",
|
||||
"crossbeam-channel",
|
||||
"crossbeam-utils",
|
||||
"flate2",
|
||||
"num_cpus",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqpack",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"zip",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
blake3 = { version = "1", features = ["traits-preview"] }
|
||||
crossbeam-channel = "0.5"
|
||||
crossbeam-utils = "0.8"
|
||||
flate2 = { version = "1", default-features = false, features = ["zlib-ng"] }
|
||||
num_cpus = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
sqpack = { git = "https://git.anna.lgbt/ascclemens/sqpack-rs", features = ["read", "write"] }
|
||||
tempfile = "3"
|
||||
thiserror = "1"
|
||||
zip = { version = "0.6", default-features = false, features = ["deflate-zlib"] }
|
||||
blake3 = { version = "1", features = ["traits-preview"], optional = true }
|
||||
crossbeam-channel = { version = "0.5", optional = true }
|
||||
crossbeam-utils = { version = "0.8", optional = true }
|
||||
flate2 = { version = "1", default-features = false, features = ["zlib-ng"], optional = true }
|
||||
num_cpus = { version = "1", optional = true }
|
||||
serde = { version = "1", features = ["derive"], optional = true }
|
||||
serde_json = { version = "1", optional = true }
|
||||
sqpack = { git = "https://git.anna.lgbt/ascclemens/sqpack-rs", features = ["read", "write"], optional = true }
|
||||
tempfile = { version = "3", optional = true }
|
||||
thiserror = { version = "1", optional = true }
|
||||
zip = { version = "0.6", default-features = false, features = ["deflate-zlib"], optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = "1"
|
||||
|
|
32
src/lib.rs
32
src/lib.rs
|
@ -1,22 +1,41 @@
|
|||
#[cfg(feature = "logic")]
|
||||
use std::io::{Read, Seek};
|
||||
|
||||
use serde::de::Error as _;
|
||||
use serde::Deserialize;
|
||||
use serde_json::StreamDeserializer;
|
||||
use serde_json::de::IoRead;
|
||||
#[cfg(feature = "logic")]
|
||||
use serde::{
|
||||
de::Error as _,
|
||||
Deserialize,
|
||||
};
|
||||
#[cfg(feature = "logic")]
|
||||
use serde_json::{
|
||||
de::IoRead,
|
||||
StreamDeserializer,
|
||||
};
|
||||
#[cfg(feature = "logic")]
|
||||
pub use zip::{read::ZipFile, ZipArchive};
|
||||
|
||||
#[cfg(feature = "logic")]
|
||||
use crate::{
|
||||
model::{ModPack, SimpleMod},
|
||||
model::manifest_kind::ManifestKind,
|
||||
};
|
||||
#[cfg(feature = "logic")]
|
||||
use crate::error::{Error, Result};
|
||||
use crate::model::{ModPack, SimpleMod};
|
||||
use crate::model::manifest_kind::ManifestKind;
|
||||
|
||||
#[cfg(feature = "model")]
|
||||
pub mod model;
|
||||
#[cfg(feature = "logic")]
|
||||
pub mod error;
|
||||
#[cfg(feature = "logic")]
|
||||
pub(crate) mod tracking_reader;
|
||||
#[cfg(feature = "logic")]
|
||||
pub mod ttmp_extractor;
|
||||
#[cfg(feature = "logic")]
|
||||
pub mod mpd_encoder;
|
||||
#[cfg(feature = "logic")]
|
||||
pub(crate) mod util;
|
||||
|
||||
#[cfg(feature = "logic")]
|
||||
pub fn from_value(value: serde_json::Value) -> Result<ManifestKind> {
|
||||
let manifest = if value.is_array() {
|
||||
ManifestKind::V1(serde_json::from_value(value).map_err(Error::InvalidManifest)?)
|
||||
|
@ -27,6 +46,7 @@ pub fn from_value(value: serde_json::Value) -> Result<ManifestKind> {
|
|||
Ok(manifest)
|
||||
}
|
||||
|
||||
#[cfg(feature = "logic")]
|
||||
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)?;
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::model::{ModPack, SimpleMod};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
|
||||
pub enum ManifestKind {
|
||||
V1(Vec<SimpleMod>),
|
||||
V2(ModPack),
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::model::{ModOption, SelectionType};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(rename_all = "PascalCase"))]
|
||||
pub struct ModGroup {
|
||||
pub group_name: String,
|
||||
pub selection_type: SelectionType,
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::model::{SelectionType, SimpleMod};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(rename_all = "PascalCase"))]
|
||||
pub struct ModOption {
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::model::{ModPackPage, SimpleMod};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(rename_all = "PascalCase"))]
|
||||
pub struct ModPack {
|
||||
pub minimum_framework_version: Option<String>,
|
||||
#[serde(rename = "TTMPVersion")]
|
||||
#[cfg_attr(feature = "serde", serde(rename = "TTMPVersion"))]
|
||||
pub ttmp_version: String,
|
||||
pub name: String,
|
||||
pub author: String,
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::model::ModGroup;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(rename_all = "PascalCase"))]
|
||||
pub struct ModPackPage {
|
||||
pub page_index: i32,
|
||||
pub mod_groups: Vec<ModGroup>,
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub enum SelectionType {
|
||||
Single,
|
||||
Multi,
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::model::ModPack;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(rename_all = "PascalCase"))]
|
||||
pub struct SimpleMod {
|
||||
pub name: String,
|
||||
pub category: String,
|
||||
pub full_path: String,
|
||||
pub dat_file: String,
|
||||
#[serde(default)]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
pub is_default: bool,
|
||||
pub mod_offset: usize,
|
||||
pub mod_size: usize,
|
||||
#[serde(default)]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
pub mod_pack_entry: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
|
|
|
@ -268,6 +268,10 @@ impl MpdEncoder {
|
|||
.map(|info| info.uncompressed_size as u32)
|
||||
.sum();
|
||||
|
||||
if compressed_size == 0 || decompressed_size == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
lod_blocks.push(LodBlock {
|
||||
compressed_offset: compressed_offset as u32,
|
||||
compressed_size,
|
||||
|
@ -281,6 +285,7 @@ impl MpdEncoder {
|
|||
sub_blocks.extend(infos);
|
||||
}
|
||||
|
||||
// FIXME: fails on minions defined
|
||||
assert_eq!(sub_blocks_len, sub_blocks.len());
|
||||
|
||||
let after_data = self.writer.stream_position().map_err(Error::Io)?;
|
||||
|
|
|
@ -52,8 +52,16 @@ impl<R: Read + Seek> TtmpExtractor<R> {
|
|||
let mut data_file = TrackingReader::new(zip.by_name("TTMPD.mpd")
|
||||
.map_err(Error::MissingDataFile)?);
|
||||
|
||||
let mut last_offset = None;
|
||||
let mut buf = [0; 4096];
|
||||
for mod_file in all_files {
|
||||
// handle deduped ttmps
|
||||
if Some(mod_file.file.mod_offset) == last_offset {
|
||||
continue;
|
||||
}
|
||||
|
||||
last_offset = Some(mod_file.file.mod_offset);
|
||||
|
||||
let file = mod_file.file;
|
||||
data_file.read = 0;
|
||||
|
||||
|
|
Loading…
Reference in New Issue