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.

224 lines
9.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_PATH_OUT: &str = "tests/GameSave.out.Techblox";
  13. #[cfg(feature = "techblox")]
  14. const GAMESAVE_PATH_ALL: &str = "tests/All.Techblox";
  15. #[cfg(feature = "techblox")]
  16. const GAMESAVE_PATH_ALL_OUT: &str = "tests/All.out.Techblox";
  17. #[cfg(feature = "techblox")]
  18. const HASHNAMES: &[&str] = &[
  19. "BlockGroupEntityDescriptorV0",
  20. "StandardBlockEntityDescriptorV4",
  21. "BatteryEntityDescriptorV4",
  22. "MotorEntityDescriptorV7",
  23. "LeverEntityDescriptorV7",
  24. "ButtonEntityDescriptorV6",
  25. "JointBlockEntityDescriptorV3",
  26. "ServoEntityDescriptorV7",
  27. "PistonEntityDescriptorV6",
  28. "DampedSpringEntityDescriptorV5",
  29. "DampedAngularSpringEntityDescriptorV4",
  30. "SpawnPointEntityDescriptorV6",
  31. "BuildingSpawnPointEntityDescriptorV4",
  32. "TriggerEntityDescriptorV6",
  33. "PilotSeatEntityDescriptorV4",
  34. "PilotSeatEntityDescriptorV3",
  35. "TextBlockEntityDescriptorV4",
  36. "PassengerSeatEntityDescriptorV4",
  37. "PassengerSeatEntityDescriptorV3",
  38. "LogicBlockEntityDescriptorV1",
  39. "TyreEntityDescriptorV1",
  40. "ObjectIDEntityDescriptorV1",
  41. "MoverEntityDescriptorV1",
  42. "RotatorEntityDescriptorV1",
  43. "DamperEntityDescriptorV1",
  44. "AdvancedDamperEntityDescriptorV1",
  45. "CoMEntityDescriptor",
  46. "FilterBlockEntityDescriptorV1",
  47. "ConstrainerEntityDescriptorV1",
  48. "NumberToTextBlockEntityDescriptorV1",
  49. "CentreHudBlockEntityDescriptorV1",
  50. "ObjectiveHudBlockEntityDescriptorV1",
  51. "GameStatsHudBlockEntityDescriptorV1",
  52. "GameOverHudBlockEntityDescriptorV1",
  53. "TimerBlockEntityDescriptorV1",
  54. "BitBlockEntityDescriptorV2",
  55. "ConstantBlockEntityDescriptor",
  56. "CounterBlockEntityDescriptorV1",
  57. "SimpleSfxEntityDescriptorV1",
  58. "LoopedSfxEntityDescriptorV1",
  59. "MusicBlockEntityDescriptorV1",
  60. "ProjectileBlockEntityDescriptorV1",
  61. "DamagingSurfaceEntityDescriptorV1",
  62. "DestructionManagerEntityDescriptorV1",
  63. "ChunkDestructionBlockEntityDescriptorV1",
  64. "ClusterDestructionBlockEntityDescriptorV1",
  65. "PickupBlockEntityDescriptorV1",
  66. "PointLightEntityDescriptorV1",
  67. "SpotLightEntityDescriptorV1",
  68. "SunLightEntityDescriptorV1",
  69. "AmbientLightEntityDescriptorV1",
  70. "FogEntityDescriptorV1",
  71. "SkyEntityDescriptorV1",
  72. "SynchronizedWireBlockEntityDescriptor",
  73. "WheelRigEntityDescriptor",
  74. "WheelRigSteerableEntityDescriptor",
  75. "EngineBlockEntityDescriptor",
  76. "WireEntityDescriptorMock",
  77. "GlobalWireSettingsEntityDescriptor",
  78. "FlyCamEntityDescriptorV0",
  79. "CharacterCameraEntityDescriptorV1",
  80. ];
  81. #[cfg(feature = "techblox")]
  82. #[test]
  83. fn techblox_gamesave_parse() -> Result<(), ()> {
  84. let mut f = File::open(GAMESAVE_PATH).map_err(|_| ())?;
  85. let mut buf = Vec::new();
  86. f.read_to_end(&mut buf).map_err(|_| ())?;
  87. let gs = techblox::GameSave::parse(&mut buf.as_slice()).map_err(|_| ())?;
  88. for i in 1..(gs.group_len as usize) {
  89. assert_eq!(gs.group_headers[i-1].hash, gs.group_headers[i].hash);
  90. //println!("#{} count {} vs {}", i, gs.group_headers[i-1].component_count, gs.group_headers[i].component_count);
  91. assert_eq!(gs.group_headers[i-1].component_count, gs.group_headers[i].component_count);
  92. }
  93. for i in 0..(gs.group_len as usize) {
  94. assert_eq!(gs.group_headers[i].component_count, techblox::BlockGroupEntity::serialized_components());
  95. assert_eq!(gs.group_headers[i].hash, gs.cube_groups[i].hash_name());
  96. }
  97. for i in 1..(gs.cube_len as usize) {
  98. //assert_eq!(gs.cube_headers[i-1].hash, gs.cube_headers[i].hash);
  99. //println!("#{} count {} vs {}", i, gs.cube_headers[i-1].component_count, gs.cube_headers[i].component_count);
  100. if gs.cube_headers[i-1].hash == gs.cube_headers[i].hash {
  101. assert_eq!(gs.group_headers[i-1].component_count, gs.group_headers[i].component_count);
  102. }
  103. }
  104. for i in 0..(gs.cube_len as usize) {
  105. assert!(gs.cube_headers[i].component_count >= blocks::BlockEntity::serialized_components());
  106. //println!("#{} components: {}", i, gs.cube_headers[i].component_count);
  107. assert_eq!(gs.cube_headers[i].hash, gs.cube_entities[i].hash_name());
  108. }
  109. //println!("Parsed wire settings hash: {} obsolete? {}", gs.wire_settings_header.hash, gs.wire_settings_entity.settings_component.obsolete != 0);
  110. assert_eq!(gs.wire_settings_header.hash, EntityHeader::from_name("GlobalWireSettingsEntityDescriptor", 0, 0, 0).hash);
  111. assert_eq!(gs.wire_settings_header.hash, gs.wire_settings_entity.hash_name());
  112. //println!("Parsed Flycam hash: {}", gs.flycam_header.hash);
  113. assert_eq!(gs.flycam_header.hash, EntityHeader::from_name("FlyCamEntityDescriptorV0", 0, 0, 0).hash);
  114. assert_eq!(gs.flycam_header.hash, gs.flycam_entity.hash_name());
  115. //println!("Parsed Phycam hash: {}", gs.phycam_header.hash);
  116. assert_eq!(gs.phycam_header.hash, EntityHeader::from_name("CharacterCameraEntityDescriptorV1", 0, 0, 0).hash);
  117. assert_eq!(gs.phycam_header.hash, gs.phycam_entity.hash_name());
  118. println!("{}", gs.to_string());
  119. Ok(())
  120. }
  121. #[allow(dead_code)]
  122. #[cfg(feature = "techblox")]
  123. //#[test]
  124. fn techblox_gamesave_brute_force() -> Result<(), ()> {
  125. // this is slow and not very important, so it's probably better to not test this
  126. let mut f = File::open(GAMESAVE_PATH).map_err(|_| ())?;
  127. let mut buf = Vec::new();
  128. f.read_to_end(&mut buf).map_err(|_| ())?;
  129. let gs = techblox::GameSave::parse(&mut buf.as_slice()).map_err(|_| ())?;
  130. println!("murmurhash3: {} -> {}", gs.group_headers[0].guess_name(), gs.group_headers[0].hash);
  131. Ok(())
  132. }
  133. #[cfg(feature = "techblox")]
  134. #[test]
  135. fn hash_tb_name() {
  136. for name in HASHNAMES {
  137. println!("MurmurHash3: {} -> {}", name, crate::techblox::EntityHeader::from_name(name, 0, 0, 0).hash);
  138. }
  139. }
  140. #[cfg(feature = "techblox")]
  141. #[test]
  142. fn techblox_gamesave_perfect_parse() -> Result<(), ()> {
  143. let mut in_file = File::open(GAMESAVE_PATH_ALL).map_err(|_| ())?;
  144. let mut buf = Vec::new();
  145. in_file.read_to_end(&mut buf).map_err(|_| ())?;
  146. let gs = techblox::GameSave::parse(&mut buf.as_slice()).map_err(|_| ())?;
  147. let mut out_file = OpenOptions::new()
  148. .write(true)
  149. .truncate(true)
  150. .create(true)
  151. .open(GAMESAVE_PATH_OUT)
  152. .map_err(|_| ())?;
  153. gs.dump(&mut out_file).map_err(|_| ())?;
  154. assert_eq!(in_file.stream_position().unwrap(), out_file.stream_position().unwrap());
  155. Ok(())
  156. }
  157. #[cfg(feature = "techblox")]
  158. #[test]
  159. fn techblox_gamesave_parse_all() -> Result<(), ()> {
  160. let mut in_file = File::open(GAMESAVE_PATH_ALL).map_err(|_| ())?;
  161. let mut buf = Vec::new();
  162. in_file.read_to_end(&mut buf).map_err(|_| ())?;
  163. let gs = techblox::GameSave::parse(&mut buf.as_slice()).map_err(|_| ())?;
  164. // verify
  165. for i in 1..(gs.group_len as usize) {
  166. assert_eq!(gs.group_headers[i-1].hash, gs.group_headers[i].hash);
  167. //println!("#{} count {} vs {}", i, gs.group_headers[i-1].component_count, gs.group_headers[i].component_count);
  168. assert_eq!(gs.group_headers[i-1].component_count, gs.group_headers[i].component_count);
  169. }
  170. for i in 0..(gs.group_len as usize) {
  171. assert_eq!(gs.group_headers[i].component_count, techblox::BlockGroupEntity::serialized_components());
  172. assert_eq!(gs.group_headers[i].hash, gs.cube_groups[i].hash_name());
  173. }
  174. for i in 1..(gs.cube_len as usize) {
  175. //assert_eq!(gs.cube_headers[i-1].hash, gs.cube_headers[i].hash);
  176. //println!("#{} count {} vs {}", i, gs.cube_headers[i-1].component_count, gs.cube_headers[i].component_count);
  177. if gs.cube_headers[i-1].hash == gs.cube_headers[i].hash {
  178. assert_eq!(gs.group_headers[i-1].component_count, gs.group_headers[i].component_count);
  179. }
  180. }
  181. for i in 0..(gs.cube_len as usize) {
  182. assert!(gs.cube_headers[i].component_count >= blocks::BlockEntity::serialized_components());
  183. //println!("#{} components: {}", i, gs.cube_headers[i].component_count);
  184. assert_eq!(gs.cube_headers[i].hash, gs.cube_entities[i].hash_name());
  185. }
  186. //println!("Parsed wire settings hash: {} obsolete? {}", gs.wire_settings_header.hash, gs.wire_settings_entity.settings_component.obsolete != 0);
  187. assert_eq!(gs.wire_settings_header.hash, EntityHeader::from_name("GlobalWireSettingsEntityDescriptor", 0, 0, 0).hash);
  188. assert_eq!(gs.wire_settings_header.hash, gs.wire_settings_entity.hash_name());
  189. //println!("Parsed Flycam hash: {}", gs.flycam_header.hash);
  190. assert_eq!(gs.flycam_header.hash, EntityHeader::from_name("FlyCamEntityDescriptorV0", 0, 0, 0).hash);
  191. assert_eq!(gs.flycam_header.hash, gs.flycam_entity.hash_name());
  192. //println!("Parsed Phycam hash: {}", gs.phycam_header.hash);
  193. assert_eq!(gs.phycam_header.hash, EntityHeader::from_name("CharacterCameraEntityDescriptorV1", 0, 0, 0).hash);
  194. assert_eq!(gs.phycam_header.hash, gs.phycam_entity.hash_name());
  195. // write out
  196. let mut out_file = OpenOptions::new()
  197. .write(true)
  198. .truncate(true)
  199. .create(true)
  200. .open(GAMESAVE_PATH_ALL_OUT)
  201. .map_err(|_| ())?;
  202. gs.dump(&mut out_file).map_err(|_| ())?;
  203. assert_eq!(in_file.stream_position().unwrap(), out_file.stream_position().unwrap());
  204. Ok(())
  205. }