Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
wgreenberg committed Nov 10, 2024
1 parent be7d362 commit 37bcf14
Show file tree
Hide file tree
Showing 16 changed files with 666 additions and 411 deletions.
30 changes: 20 additions & 10 deletions rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[workspace]
members = ["noclip-macros"]

[package]
name = "noclip-support"
version = "0.0.0"
Expand Down Expand Up @@ -33,4 +36,4 @@ wee_alloc = { version = "0.4.5", optional = true }
nalgebra-glm = "0.19.0"
rand = "0.8.5"
getrandom = { version = "0.2.15", features = ["js"] }

noclip-macros = { version = "*", path = "./noclip-macros" }
12 changes: 12 additions & 0 deletions rust/noclip-macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "noclip-macros"
version = "0.1.0"
edition = "2021"

[lib]
proc-macro = true

[dependencies]
proc-macro2 = "1.0.89"
quote = "1.0.37"
syn = { version = "2.0.87", features = ["extra-traits"] }
99 changes: 99 additions & 0 deletions rust/noclip-macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
use syn::{Attribute, Data, Path};
use quote::quote;

#[proc_macro_derive(FromStructPerField, attributes(from))]
pub fn derive_from_struct_per_field(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = syn::parse_macro_input!(item as syn::DeriveInput);

let mut from_structs = Vec::new();
for attr in &input.attrs {
match &attr.meta {
syn::Meta::List(meta_list) => {
let tokens: proc_macro::TokenStream = meta_list.tokens.clone().into();
from_structs.push(syn::parse_macro_input!(tokens as syn::Path));
},
_ => unimplemented!(),
};
}

let struct_identifier = &input.ident;
let mut impls = proc_macro2::TokenStream::new();

match &input.data {
Data::Struct(syn::DataStruct { fields, .. }) => {
let mut field_assignments = proc_macro2::TokenStream::new();
for field in fields {
let identifier = field.ident.as_ref().unwrap();
field_assignments.extend(quote!{
#identifier: value.#identifier.into(),
});
}

for from_struct in from_structs {
impls.extend(quote!{
impl From<#from_struct> for #struct_identifier {
fn from(value: #from_struct) -> #struct_identifier {
#struct_identifier {
#field_assignments
}
}
}
});
}
},
_ => unimplemented!(),
}

impls.into()
}

#[proc_macro_derive(FromEnumPerVariant, attributes(from))]
pub fn derive_from_enum(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = syn::parse_macro_input!(item as syn::DeriveInput);

let mut from_enums = Vec::new();
for attr in &input.attrs {
match &attr.meta {
syn::Meta::List(meta_list) => {
let tokens: proc_macro::TokenStream = meta_list.tokens.clone().into();
from_enums.push(syn::parse_macro_input!(tokens as syn::Path));
},
_ => unimplemented!(),
};
}

let enum_identifier = &input.ident;
let mut impls = proc_macro2::TokenStream::new();

match &input.data {
Data::Enum(syn::DataEnum { variants, .. }) => {
for from_enum in from_enums {
let mut variant_patterns = proc_macro2::TokenStream::new();
for variant in variants {
let identifier = &variant.ident;
variant_patterns.extend(quote!{
#from_enum::#identifier => #enum_identifier::#identifier,
});
}

impls.extend(quote!{
impl From<#from_enum> for #enum_identifier {
fn from(value: #from_enum) -> #enum_identifier {
match value {
#variant_patterns
}
}
}
});
}
},
_ => unimplemented!(),
}

impls.into()
}

#[proc_macro_attribute]
pub fn from(attr: proc_macro::TokenStream, _: proc_macro::TokenStream) -> proc_macro::TokenStream {
attr
}
12 changes: 12 additions & 0 deletions rust/src/unity/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,12 @@ pub struct Packedi32Vec {
pub data: Vec<i32>,
}

impl From<Packedi32Vec> for Vec<i32> {
fn from(value: Packedi32Vec) -> Self {
value.data
}
}

impl<'a> DekuRead<'a> for Packedi32Vec {
fn read(input: &'a BitSlice<u8, Msb0>, ctx: ()) -> Result<(&'a BitSlice<u8, Msb0>, Self), DekuError>
where
Expand All @@ -228,6 +234,12 @@ pub struct Packedf32Vec {
pub data: Vec<f32>,
}

impl From<Packedf32Vec> for Vec<f32> {
fn from(value: Packedf32Vec) -> Self {
value.data
}
}

impl<'a> DekuRead<'a> for Packedf32Vec {
fn read(input: &'a BitSlice<u8, Msb0>, ctx: ()) -> Result<(&'a BitSlice<u8, Msb0>, Self), DekuError>
where
Expand Down
69 changes: 58 additions & 11 deletions rust/src/unity/serialized_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,20 @@ use super::{class_id::ClassID, common::UnityArray};
#[wasm_bindgen(js_name = "UnityAssetFile")]
pub struct AssetFile {
header: SerializedFileHeader,
metadata_offset: usize,
metadata: Option<SerializedFileMetadata>,
}

#[wasm_bindgen(js_class = "UnityAssetFile")]
impl AssetFile {
pub fn initialize_with_header_chunk(data: &[u8]) -> Result<AssetFile, String> {
match SerializedFileHeader::from_bytes((data, 0)) {
Ok((_, header)) => {
Ok(((rest, _), header)) => {
let header_size = data.len() - rest.len();
assert_eq!(header.endianness, 0); // we're always expecting little endian files
Ok(Self {
header,
metadata_offset: header_size,
metadata: None,
})
},
Expand Down Expand Up @@ -73,7 +76,7 @@ impl AssetFile {
result
}

pub fn get_external_path(&self, pptr: WasmFriendlyPPtr) -> Option<String> {
pub fn get_external_path(&self, pptr: &WasmFriendlyPPtr) -> Option<String> {
let idx = pptr.file_index as usize - 1;
let metadata = self.get_metadata();
metadata.externals.values
Expand Down Expand Up @@ -236,20 +239,64 @@ struct FileIdentifier {

#[cfg(test)]
mod tests {
use std::collections::HashMap;

use crate::unity::types::object::{GameObject, MeshFilter, MeshRenderer, Transform, UnityVersion};
use crate::unity::class_id::ClassID;

use super::*;
use env_logger;
use deku::bitvec::BitVec;

#[test]
fn test() {
let data = std::fs::read("E:\\SteamLibrary\\steamapps\\common\\Outer Wilds\\OuterWilds_Data\\level1").unwrap();
let ((rest, _), header) = SerializedFileHeader::from_bytes((&data, 0)).unwrap();
let header_len = data.len() - rest.len();
dbg!(&header);
let bitvec = BitVec::from_slice(&rest[0..header.metadata_size as usize]);
let bit_len = header_len * 8 + bitvec.len();
let (slice, metadata) = SerializedFileMetadata::read(bitvec.as_bitslice(), header.version).unwrap();
let bit_diff = bit_len - slice.len();
dbg!(bit_diff / 8);
let data = std::fs::read("C:\\Users\\ifnsp\\dev\\noclip.website\\data\\AShortHike\\level2").unwrap();
let version = UnityVersion::V2021_3_27f1;
let mut asset_file = AssetFile::initialize_with_header_chunk(&data).unwrap();
dbg!(&asset_file.header);
asset_file.append_metadata_chunk(&data).unwrap();
let metadata = asset_file.metadata.as_ref().unwrap();

let mut objects = HashMap::new();
let mut object_infos = HashMap::new();
for obj in &metadata.objects {
object_infos.insert(obj.file_id, obj);
let obj_type = &metadata.type_tree[obj.serialized_type_index as usize];
if !matches!(obj_type.header.raw_type_id, ClassID::GameObject) {
continue;
}
let byte_start = asset_file.get_data_offset() as usize + match obj.small_file_byte_start {
Some(start) => start as usize,
None => obj.large_file_byte_start.unwrap() as usize,
};
let byte_size = obj.byte_size as usize;
let data = &data[byte_start..byte_start + byte_size];
let game_object = GameObject::create(version, data).unwrap();
objects.insert(obj.file_id, game_object);
}

for obj in objects.values() {
for component_ptr in &obj.components {
if component_ptr.file_index != 0 {
dbg!(component_ptr);
continue;
}
let obj = object_infos.get(&component_ptr.path_id).unwrap();
let obj_type = &metadata.type_tree[obj.serialized_type_index as usize];
let byte_start = asset_file.get_data_offset() as usize + match obj.small_file_byte_start {
Some(start) => start as usize,
None => obj.large_file_byte_start.unwrap() as usize,
};
let byte_size = obj.byte_size as usize;
let data = &data[byte_start..byte_start + byte_size];
match obj_type.header.raw_type_id {
ClassID::Transform => {Transform::create(version, data).unwrap();},
ClassID::RectTransform => {Transform::create(version, data).unwrap();},
ClassID::MeshFilter => {MeshFilter::create(version, data).unwrap();},
ClassID::MeshRenderer => {MeshRenderer::create(version, data).unwrap();},
s => println!("skipping {:?}", s),
}
}
}
}
}
2 changes: 2 additions & 0 deletions rust/src/unity/types/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
pub mod v2019_4_39f1;
pub mod v2020_3_16f1;
pub mod v2021_3_27f1;
pub mod object;
Loading

0 comments on commit 37bcf14

Please sign in to comment.