rabex 0.2.0

Tools for parsing and writing unity files, bundles and typetrees
Documentation
use anyhow::Result;
use rabex::files::bundlefile::{BundleFileBuilder, CompressionType};
use rabex::files::serializedfile::builder::SerializedFileBuilder;
use rabex::files::serializedfile::{FileIdentifier, build_common_offset_map};
use rabex::objects::{ClassId, ClassIdType, PPtr};
use rabex::tpk::TpkTypeTreeBlob;
use rabex::typetree::typetree_cache::TypeTreeCache;
use serde_derive::Serialize;
use std::collections::BTreeMap;
use std::fs::File;
use std::io::{BufWriter, Cursor};

fn main() -> Result<()> {
    let tpk_raw = TpkTypeTreeBlob::embedded();
    let tpk = TypeTreeCache::new(&tpk_raw);

    let bundle_name = "bundle";
    let unity_version = "2020.2.2f1".parse().unwrap();
    let mut out = BufWriter::new(File::create("out.unity3d")?);

    let objects = [
        (6, [199, 1210].as_slice()),
        (37, &[660]),
        (38, &[257]),
        (40, &[258]),
    ];

    let common_offset_map = build_common_offset_map(&tpk_raw, &unity_version);
    let mut builder = SerializedFileBuilder::new(&unity_version, &tpk, &common_offset_map, false);

    let mut container = BTreeMap::default();
    for (scene_index, objects) in objects {
        let new_file_idx =
            builder.add_external_uncached(FileIdentifier::new(format!("level{scene_index}")));

        for &obj in objects {
            container.insert(
                format!("test/{scene_index}-{obj}.prefab"),
                AssetInfo {
                    preloadIndex: 0,
                    preloadSize: 0,
                    asset: PPtr::new(new_file_idx, obj),
                },
            );
        }
    }

    let ab = AssetBundle {
        m_Name: bundle_name.to_owned(),
        m_Container: container,
        m_MainAsset: AssetInfo::default(),
        m_RuntimeCompatibility: 1,
        m_IsStreamedSceneAssetBundle: false,
        m_PathFlags: 7,
        ..Default::default()
    };
    builder.add_object_at(1, &ab)?;

    let mut builder_out = Vec::new();
    builder.write(Cursor::new(&mut builder_out))?;

    let mut bundle = BundleFileBuilder::unityfs(7, &unity_version);
    bundle.add_file("CAB-bundle", Cursor::new(builder_out))?;

    bundle.write(&mut out, CompressionType::None)?;

    Ok(())
}

#[derive(Debug, Serialize, Default)]
#[allow(non_snake_case)]
pub struct PreloadData {
    pub m_Name: String,
    pub m_Assets: Vec<PPtr>,
    pub m_Dependencies: Vec<String>,
    pub m_ExplicitDataLayout: bool,
}
impl ClassIdType for PreloadData {
    const CLASS_ID: ClassId = ClassId::PreloadData;
}

#[derive(Debug, Serialize, Default)]
#[allow(non_snake_case)]
pub struct AssetBundle {
    pub m_Name: String,
    pub m_PreloadTable: Vec<PPtr>,
    pub m_Container: BTreeMap<String, AssetInfo>,
    pub m_MainAsset: AssetInfo,
    pub m_RuntimeCompatibility: u32,
    pub m_AssetBundleName: String,
    pub m_Dependencies: Vec<String>,
    pub m_IsStreamedSceneAssetBundle: bool,
    pub m_ExplicitDataLayout: i32,
    pub m_PathFlags: i32,
    pub m_SceneHashes: BTreeMap<String, String>,
}
impl ClassIdType for AssetBundle {
    const CLASS_ID: ClassId = ClassId::AssetBundle;
}

#[derive(Debug, Serialize, Default)]
#[allow(non_snake_case)]
pub struct AssetInfo {
    pub preloadIndex: i32,
    pub preloadSize: i32,
    pub asset: PPtr,
}