An unofficial collection of APIs used in FreeJam games and mods
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

165 lines
6.5KB

  1. #[cfg(feature = "techblox")]
  2. use libfj::techblox;
  3. #[cfg(feature = "techblox")]
  4. use libfj::techblox::{SerializedEntityDescriptor, Parsable, blocks, EntityHeader};
  5. #[cfg(feature = "techblox")]
  6. use std::io::{Read, Seek};
  7. #[cfg(feature = "techblox")]
  8. use std::fs::{File, OpenOptions};
  9. #[cfg(feature = "techblox")]
  10. const GAMESAVE_PATH: &str = "tests/GameSave.Techblox";
  11. #[cfg(feature = "techblox")]
  12. const GAMESAVE_PATH2: &str = "tests/GameSave2.Techblox";
  13. #[cfg(feature = "techblox")]
  14. const HASHNAMES: &[&str] = &[
  15. "BlockGroupEntityDescriptorV0",
  16. "StandardBlockEntityDescriptorV4",
  17. "BatteryEntityDescriptorV4",
  18. "MotorEntityDescriptorV7",
  19. "LeverEntityDescriptorV7",
  20. "ButtonEntityDescriptorV6",
  21. "JointBlockEntityDescriptorV3",
  22. "ServoEntityDescriptorV7",
  23. "PistonEntityDescriptorV6",
  24. "DampedSpringEntityDescriptorV5",
  25. "DampedAngularSpringEntityDescriptorV4",
  26. "SpawnPointEntityDescriptorV6",
  27. "BuildingSpawnPointEntityDescriptorV4",
  28. "TriggerEntityDescriptorV6",
  29. "PilotSeatEntityDescriptorV4",
  30. "PilotSeatEntityDescriptorV3",
  31. "TextBlockEntityDescriptorV4",
  32. "PassengerSeatEntityDescriptorV4",
  33. "PassengerSeatEntityDescriptorV3",
  34. "LogicBlockEntityDescriptorV1",
  35. "TyreEntityDescriptorV1",
  36. "ObjectIDEntityDescriptorV1",
  37. "MoverEntityDescriptorV1",
  38. "RotatorEntityDescriptorV1",
  39. "DamperEntityDescriptorV1",
  40. "AdvancedDamperEntityDescriptorV1",
  41. "CoMEntityDescriptor",
  42. "FilterBlockEntityDescriptorV1",
  43. "ConstrainerEntityDescriptorV1",
  44. "NumberToTextBlockEntityDescriptorV1",
  45. "CentreHudBlockEntityDescriptorV1",
  46. "ObjectiveHudBlockEntityDescriptorV1",
  47. "GameStatsHudBlockEntityDescriptorV1",
  48. "GameOverHudBlockEntityDescriptorV1",
  49. "TimerBlockEntityDescriptorV1",
  50. "BitBlockEntityDescriptorV2",
  51. "ConstantBlockEntityDescriptor",
  52. "CounterBlockEntityDescriptorV1",
  53. "SimpleSfxEntityDescriptorV1",
  54. "LoopedSfxEntityDescriptorV1",
  55. "MusicBlockEntityDescriptorV1",
  56. "ProjectileBlockEntityDescriptorV1",
  57. "DamagingSurfaceEntityDescriptorV1",
  58. "DestructionManagerEntityDescriptorV1",
  59. "ChunkDestructionBlockEntityDescriptorV1",
  60. "ClusterDestructionBlockEntityDescriptorV1",
  61. "PickupBlockEntityDescriptorV1",
  62. "PointLightEntityDescriptorV1",
  63. "SpotLightEntityDescriptorV1",
  64. "SunLightEntityDescriptorV1",
  65. "AmbientLightEntityDescriptorV1",
  66. "FogEntityDescriptorV1",
  67. "SkyEntityDescriptorV1",
  68. "SynchronizedWireBlockEntityDescriptor",
  69. "WheelRigEntityDescriptor",
  70. "WheelRigSteerableEntityDescriptor",
  71. "EngineBlockEntityDescriptor",
  72. "WireEntityDescriptorMock",
  73. "GlobalWireSettingsEntityDescriptor",
  74. "FlyCamEntityDescriptorV0",
  75. "CharacterCameraEntityDescriptorV1",
  76. ];
  77. #[cfg(feature = "techblox")]
  78. #[test]
  79. fn techblox_gamesave_parse() -> Result<(), ()> {
  80. let mut f = File::open(GAMESAVE_PATH).map_err(|_| ())?;
  81. let mut buf = Vec::new();
  82. f.read_to_end(&mut buf).map_err(|_| ())?;
  83. let gs = techblox::GameSave::parse(&mut buf.as_slice()).map_err(|_| ())?;
  84. for i in 1..(gs.group_len as usize) {
  85. assert_eq!(gs.group_headers[i-1].hash, gs.group_headers[i].hash);
  86. //println!("#{} count {} vs {}", i, gs.group_headers[i-1].component_count, gs.group_headers[i].component_count);
  87. assert_eq!(gs.group_headers[i-1].component_count, gs.group_headers[i].component_count);
  88. }
  89. for i in 0..(gs.group_len as usize) {
  90. assert_eq!(gs.group_headers[i].component_count, techblox::BlockGroupEntity::serialized_components());
  91. assert_eq!(gs.group_headers[i].hash, gs.cube_groups[i].hash_name());
  92. }
  93. for i in 1..(gs.cube_len as usize) {
  94. //assert_eq!(gs.cube_headers[i-1].hash, gs.cube_headers[i].hash);
  95. //println!("#{} count {} vs {}", i, gs.cube_headers[i-1].component_count, gs.cube_headers[i].component_count);
  96. if gs.cube_headers[i-1].hash == gs.cube_headers[i].hash {
  97. assert_eq!(gs.group_headers[i-1].component_count, gs.group_headers[i].component_count);
  98. }
  99. }
  100. for i in 0..(gs.cube_len as usize) {
  101. assert!(gs.cube_headers[i].component_count >= blocks::BlockEntity::serialized_components());
  102. //println!("#{} components: {}", i, gs.cube_headers[i].component_count);
  103. assert_eq!(gs.cube_headers[i].hash, gs.cube_entities[i].hash_name());
  104. }
  105. //println!("Parsed wire settings hash: {} obsolete? {}", gs.wire_settings_header.hash, gs.wire_settings_entity.settings_component.obsolete != 0);
  106. assert_eq!(gs.wire_settings_header.hash, EntityHeader::from_name("GlobalWireSettingsEntityDescriptor", 0, 0, 0).hash);
  107. assert_eq!(gs.wire_settings_header.hash, gs.wire_settings_entity.hash_name());
  108. //println!("Parsed Flycam hash: {}", gs.flycam_header.hash);
  109. assert_eq!(gs.flycam_header.hash, EntityHeader::from_name("FlyCamEntityDescriptorV0", 0, 0, 0).hash);
  110. assert_eq!(gs.flycam_header.hash, gs.flycam_entity.hash_name());
  111. //println!("Parsed Phycam hash: {}", gs.phycam_header.hash);
  112. assert_eq!(gs.phycam_header.hash, EntityHeader::from_name("CharacterCameraEntityDescriptorV1", 0, 0, 0).hash);
  113. assert_eq!(gs.phycam_header.hash, gs.phycam_entity.hash_name());
  114. println!("{}", gs.to_string());
  115. Ok(())
  116. }
  117. #[allow(dead_code)]
  118. #[cfg(feature = "techblox")]
  119. //#[test]
  120. fn techblox_gamesave_brute_force() -> Result<(), ()> {
  121. // this is slow and not very important, so it's probably better to not test this
  122. let mut f = File::open(GAMESAVE_PATH).map_err(|_| ())?;
  123. let mut buf = Vec::new();
  124. f.read_to_end(&mut buf).map_err(|_| ())?;
  125. let gs = techblox::GameSave::parse(&mut buf.as_slice()).map_err(|_| ())?;
  126. println!("murmurhash3: {} -> {}", gs.group_headers[0].guess_name(), gs.group_headers[0].hash);
  127. Ok(())
  128. }
  129. #[cfg(feature = "techblox")]
  130. #[test]
  131. fn hash_tb_name() {
  132. for name in HASHNAMES {
  133. println!("MurmurHash3: {} -> {}", name, crate::techblox::EntityHeader::from_name(name, 0, 0, 0).hash);
  134. }
  135. }
  136. #[cfg(feature = "techblox")]
  137. #[test]
  138. fn techblox_gamesave_perfect_parse() -> Result<(), ()> {
  139. let mut in_file = File::open(GAMESAVE_PATH).map_err(|_| ())?;
  140. let mut buf = Vec::new();
  141. in_file.read_to_end(&mut buf).map_err(|_| ())?;
  142. let gs = techblox::GameSave::parse(&mut buf.as_slice()).map_err(|_| ())?;
  143. let mut out_file = OpenOptions::new()
  144. .write(true)
  145. .truncate(true)
  146. .create(true)
  147. .open(GAMESAVE_PATH2)
  148. .map_err(|_| ())?;
  149. gs.dump(&mut out_file).map_err(|_| ())?;
  150. assert_eq!(in_file.stream_position().unwrap(), out_file.stream_position().unwrap());
  151. Ok(())
  152. }