libfj/tests/techblox_parsing.rs

256 lines
11 KiB
Rust
Raw Normal View History

#[cfg(feature = "techblox")]
use libfj::techblox;
#[cfg(feature = "techblox")]
use libfj::techblox::{SerializedEntityDescriptor, Parsable, blocks, EntityHeader};
#[cfg(feature = "techblox")]
use std::io::{Read, Seek};
#[cfg(feature = "techblox")]
use std::fs::{File, OpenOptions};
#[cfg(feature = "techblox")]
use std::convert::AsRef;
#[cfg(feature = "techblox")]
const GAMESAVE_PATH: &str = "tests/GameSave.Techblox";
#[cfg(feature = "techblox")]
const GAMESAVE_PATH_OUT: &str = "tests/GameSave.out.Techblox";
#[cfg(feature = "techblox")]
const GAMESAVE_PATH_ALL: &str = "tests/All.Techblox";
#[cfg(feature = "techblox")]
const GAMESAVE_PATH_ALL_OUT: &str = "tests/All.out.Techblox";
#[cfg(feature = "techblox")]
const HASHNAMES: &[&str] = &[
2021-06-22 17:50:30 -04:00
"BlockGroupEntityDescriptorV0",
"StandardBlockEntityDescriptorV4",
2021-06-22 17:50:30 -04:00
"BatteryEntityDescriptorV4",
"MotorEntityDescriptorV7",
"LeverEntityDescriptorV7",
"ButtonEntityDescriptorV6",
"JointBlockEntityDescriptorV3",
"ServoEntityDescriptorV7",
"PistonEntityDescriptorV6",
"DampedSpringEntityDescriptorV5",
"DampedAngularSpringEntityDescriptorV4",
"SpawnPointEntityDescriptorV6",
"BuildingSpawnPointEntityDescriptorV4",
"TriggerEntityDescriptorV6",
"PilotSeatEntityDescriptorV4",
"PilotSeatEntityDescriptorV3",
"TextBlockEntityDescriptorV4",
"PassengerSeatEntityDescriptorV4",
"PassengerSeatEntityDescriptorV3",
"LogicBlockEntityDescriptorV1",
"TyreEntityDescriptorV1",
"ObjectIDEntityDescriptorV1",
"MoverEntityDescriptorV1",
"RotatorEntityDescriptorV1",
"DamperEntityDescriptorV1",
"AdvancedDamperEntityDescriptorV1",
"CoMEntityDescriptor",
"FilterBlockEntityDescriptorV1",
"ConstrainerEntityDescriptorV1",
"NumberToTextBlockEntityDescriptorV1",
"CentreHudBlockEntityDescriptorV1",
"ObjectiveHudBlockEntityDescriptorV1",
"GameStatsHudBlockEntityDescriptorV1",
"GameOverHudBlockEntityDescriptorV1",
"TimerBlockEntityDescriptorV1",
"BitBlockEntityDescriptorV2",
"ConstantBlockEntityDescriptor",
"CounterBlockEntityDescriptorV1",
"SimpleSfxEntityDescriptorV1",
"LoopedSfxEntityDescriptorV1",
"MusicBlockEntityDescriptorV1",
"ProjectileBlockEntityDescriptorV1",
"DamagingSurfaceEntityDescriptorV1",
"DestructionManagerEntityDescriptorV1",
"ChunkDestructionBlockEntityDescriptorV1",
"ClusterDestructionBlockEntityDescriptorV1",
"PickupBlockEntityDescriptorV1",
"PointLightEntityDescriptorV1",
"SpotLightEntityDescriptorV1",
"SunLightEntityDescriptorV1",
"AmbientLightEntityDescriptorV1",
"FogEntityDescriptorV1",
"SkyEntityDescriptorV1",
"SynchronizedWireBlockEntityDescriptor",
"WheelRigEntityDescriptor",
"WheelRigSteerableEntityDescriptor",
"EngineBlockEntityDescriptor",
"WireEntityDescriptorMock",
"GlobalWireSettingsEntityDescriptor",
"FlyCamEntityDescriptorV0",
"CharacterCameraEntityDescriptorV1",
];
#[cfg(feature = "techblox")]
#[test]
fn techblox_gamesave_parse() -> Result<(), ()> {
let mut f = File::open(GAMESAVE_PATH).map_err(|_| ())?;
let mut buf = Vec::new();
f.read_to_end(&mut buf).map_err(|_| ())?;
let gs = techblox::GameSave::parse(&mut buf.as_slice()).map_err(|_| ())?;
for i in 1..(gs.group_len as usize) {
assert_eq!(gs.group_headers[i-1].hash, gs.group_headers[i].hash);
//println!("#{} count {} vs {}", i, gs.group_headers[i-1].component_count, gs.group_headers[i].component_count);
assert_eq!(gs.group_headers[i-1].component_count, gs.group_headers[i].component_count);
}
for i in 0..(gs.group_len as usize) {
assert_eq!(gs.group_headers[i].component_count, techblox::BlockGroupEntity::serialized_components());
2021-06-22 17:50:30 -04:00
assert_eq!(gs.group_headers[i].hash, gs.cube_groups[i].hash_name());
}
for i in 1..(gs.cube_len as usize) {
//assert_eq!(gs.cube_headers[i-1].hash, gs.cube_headers[i].hash);
//println!("#{} count {} vs {}", i, gs.cube_headers[i-1].component_count, gs.cube_headers[i].component_count);
if gs.cube_headers[i-1].hash == gs.cube_headers[i].hash {
assert_eq!(gs.group_headers[i-1].component_count, gs.group_headers[i].component_count);
}
}
for i in 0..(gs.cube_len as usize) {
assert!(gs.cube_headers[i].component_count >= blocks::BlockEntity::serialized_components());
//println!("#{} components: {}", i, gs.cube_headers[i].component_count);
2021-06-22 17:50:30 -04:00
assert_eq!(gs.cube_headers[i].hash, gs.cube_entities[i].hash_name());
}
//println!("Parsed wire settings hash: {} obsolete? {}", gs.wire_settings_header.hash, gs.wire_settings_entity.settings_component.obsolete != 0);
assert_eq!(gs.wire_settings_header.hash, EntityHeader::from_name("GlobalWireSettingsEntityDescriptor", 0, 0, 0).hash);
2021-06-22 17:50:30 -04:00
assert_eq!(gs.wire_settings_header.hash, gs.wire_settings_entity.hash_name());
//println!("Parsed Flycam hash: {}", gs.flycam_header.hash);
assert_eq!(gs.flycam_header.hash, EntityHeader::from_name("FlyCamEntityDescriptorV0", 0, 0, 0).hash);
2021-06-22 17:50:30 -04:00
assert_eq!(gs.flycam_header.hash, gs.flycam_entity.hash_name());
//println!("Parsed Phycam hash: {}", gs.phycam_header.hash);
assert_eq!(gs.phycam_header.hash, EntityHeader::from_name("CharacterCameraEntityDescriptorV1", 0, 0, 0).hash);
2021-06-22 17:50:30 -04:00
assert_eq!(gs.phycam_header.hash, gs.phycam_entity.hash_name());
println!("{}", gs.to_string());
Ok(())
}
#[allow(dead_code)]
#[cfg(feature = "techblox")]
//#[test]
fn techblox_gamesave_brute_force() -> Result<(), ()> {
// this is slow and not very important, so it's probably better to not test this
let mut f = File::open(GAMESAVE_PATH).map_err(|_| ())?;
let mut buf = Vec::new();
f.read_to_end(&mut buf).map_err(|_| ())?;
let gs = techblox::GameSave::parse(&mut buf.as_slice()).map_err(|_| ())?;
println!("murmurhash3: {} -> {}", gs.group_headers[0].guess_name(), gs.group_headers[0].hash);
Ok(())
}
#[cfg(feature = "techblox")]
#[test]
fn hash_tb_name() {
for name in HASHNAMES {
println!("MurmurHash3: {} -> {}", name, crate::techblox::EntityHeader::from_name(name, 0, 0, 0).hash);
}
}
#[cfg(feature = "techblox")]
#[test]
fn techblox_gamesave_perfect_parse() -> Result<(), ()> {
let mut in_file = File::open(GAMESAVE_PATH_ALL).map_err(|_| ())?;
let mut buf = Vec::new();
in_file.read_to_end(&mut buf).map_err(|_| ())?;
let gs = techblox::GameSave::parse(&mut buf.as_slice()).map_err(|_| ())?;
let mut out_file = OpenOptions::new()
.write(true)
.truncate(true)
.create(true)
.open(GAMESAVE_PATH_OUT)
.map_err(|_| ())?;
gs.dump(&mut out_file).map_err(|_| ())?;
assert_eq!(in_file.stream_position().unwrap(), out_file.stream_position().unwrap());
Ok(())
}
#[cfg(feature = "techblox")]
#[test]
fn techblox_gamesave_parse_all() -> Result<(), ()> {
let mut in_file = File::open(GAMESAVE_PATH_ALL).map_err(|_| ())?;
let mut buf = Vec::new();
in_file.read_to_end(&mut buf).map_err(|_| ())?;
let gs = techblox::GameSave::parse(&mut buf.as_slice()).map_err(|_| ())?;
// verify
for i in 1..(gs.group_len as usize) {
assert_eq!(gs.group_headers[i-1].hash, gs.group_headers[i].hash);
//println!("#{} count {} vs {}", i, gs.group_headers[i-1].component_count, gs.group_headers[i].component_count);
assert_eq!(gs.group_headers[i-1].component_count, gs.group_headers[i].component_count);
}
for i in 0..(gs.group_len as usize) {
assert_eq!(gs.group_headers[i].component_count, techblox::BlockGroupEntity::serialized_components());
assert_eq!(gs.group_headers[i].hash, gs.cube_groups[i].hash_name());
/*let pos = format!("({}, {}, {})", gs.cube_groups[i].block_group_transform.block_group_grid_position.x, gs.cube_groups[i].block_group_transform.block_group_grid_position.y, gs.cube_groups[i].block_group_transform.block_group_grid_position.z);
let rot = format!("({}, {}, {}, {})", gs.cube_groups[i].block_group_transform.block_group_grid_rotation.value.x, gs.cube_groups[i].block_group_transform.block_group_grid_rotation.value.y, gs.cube_groups[i].block_group_transform.block_group_grid_rotation.value.z,
gs.cube_groups[i].block_group_transform.block_group_grid_rotation.value.w);
println!("block id: {}, position: {}, rotation: {}", gs.cube_groups[i].saved_block_group_id.saved_block_group_id, pos, rot);*/
}
for i in 1..(gs.cube_len as usize) {
//assert_eq!(gs.cube_headers[i-1].hash, gs.cube_headers[i].hash);
//println!("#{} count {} vs {}", i, gs.cube_headers[i-1].component_count, gs.cube_headers[i].component_count);
if gs.cube_headers[i-1].hash == gs.cube_headers[i].hash {
assert_eq!(gs.group_headers[i-1].component_count, gs.group_headers[i].component_count);
}
}
for i in 0..(gs.cube_len as usize) {
assert!(gs.cube_headers[i].component_count >= blocks::BlockEntity::serialized_components());
//println!("#{} components: {}", i, gs.cube_headers[i].component_count);
assert_eq!(gs.cube_headers[i].hash, gs.cube_entities[i].hash_name());
}
//println!("Parsed wire settings hash: {} obsolete? {}", gs.wire_settings_header.hash, gs.wire_settings_entity.settings_component.obsolete != 0);
assert_eq!(gs.wire_settings_header.hash, EntityHeader::from_name("GlobalWireSettingsEntityDescriptor", 0, 0, 0).hash);
assert_eq!(gs.wire_settings_header.hash, gs.wire_settings_entity.hash_name());
//println!("Parsed Flycam hash: {}", gs.flycam_header.hash);
assert_eq!(gs.flycam_header.hash, EntityHeader::from_name("FlyCamEntityDescriptorV0", 0, 0, 0).hash);
assert_eq!(gs.flycam_header.hash, gs.flycam_entity.hash_name());
//println!("Parsed Phycam hash: {}", gs.phycam_header.hash);
assert_eq!(gs.phycam_header.hash, EntityHeader::from_name("CharacterCameraEntityDescriptorV1", 0, 0, 0).hash);
assert_eq!(gs.phycam_header.hash, gs.phycam_entity.hash_name());
// write out
let mut out_file = OpenOptions::new()
.write(true)
.truncate(true)
.create(true)
.open(GAMESAVE_PATH_ALL_OUT)
.map_err(|_| ())?;
gs.dump(&mut out_file).map_err(|_| ())?;
assert_eq!(in_file.stream_position().unwrap(), out_file.stream_position().unwrap());
Ok(())
}
#[cfg(feature = "techblox")]
#[test]
fn techblox_gamesave_block_groups() -> Result<(), ()> {
let mut in_file = File::open(GAMESAVE_PATH_ALL).map_err(|_| ())?;
let mut buf = Vec::new();
in_file.read_to_end(&mut buf).map_err(|_| ())?;
let gs = techblox::GameSave::parse(&mut buf.as_slice()).map_err(|_| ())?;
for block_trait in &gs.cube_entities {
let block: &blocks::BlockEntity = block_trait.as_ref().as_ref();
//println!("Block @ ({}, {}, {})", block.pos_component.position.x, block.pos_component.position.y, block.pos_component.position.z);
assert!(is_in_block_groups(block.group_component.current_block_group, &gs.cube_groups));
}
Ok(())
}
#[cfg(feature = "techblox")]
fn is_in_block_groups(id: i32, block_groups: &Vec<techblox::BlockGroupEntity>) -> bool {
for bg in block_groups {
if bg.saved_block_group_id.saved_block_group_id == id {
return true;
}
}
false
}