using CodeWalker.GameFiles; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Xml; using System.Xml.Linq; using Xunit; using Xunit.Abstractions; namespace CodeWalker.Test { public class XmlTests { private readonly ITestOutputHelper output; public XmlTests(ITestOutputHelper output) { this.output = output; } public static string markup = @" vehshare brabusgt600brabusgt600 brabusgt600 GT 600 BRABUS null null null null null ta176m177 LAYOUT_LOW BULLET_COVER_OFFSET_INFO EXPLOSION_INFO_DEFAULT DEFAULT_FOLLOW_VEHICLE_CAMERA MID_BOX_VEHICLE_AIM_CAMERA VEHICLE_BONNET_CAMERA_NEAR_EXTRA_HIGH DEFAULT_POV_CAMERA VFXVEHICLEINFO_CAR_BULLET 60.000000 80.000000 100.000000 120.000000 500.000000 500.000000 SWANKNESS_3 FLAG_SPORTS FLAG_RICH_CAR FLAG_NO_BROKEN_DOWN_SCENARIO FLAG_RECESSED_TAILLIGHT_CORONAS FLAG_NO_HEAVY_BRAKE_ANIMATION VEHICLE_TYPE_CAR VPT_FRONT_AND_BACK_PLATES VDT_BANSHEE VC_SPORT VWT_HIEND docktrailer trailers trailers2 trailers3 trailers4 tanker trailerlogs tr2 trflat S_M_Y_Cop_01 WHEEL_FRONT_RIGHT_CAMERA WHEEL_FRONT_LEFT_CAMERA WHEEL_REAR_RIGHT_CAMERA WHEEL_REAR_LEFT_CAMERA LOW_BULLET_FRONT_LEFT LOW_BULLET_FRONT_RIGHT vehicles_banshee_interior brabusgt600 "; public static string pedsList = @" comp_peds_generic CS_LesterCrest_2 CS_LesterCrest_2_p move_m@generic null CS_LesterCrest CS_LesterCrest CIVMALE move_m@generic move_ped_strafing move_ped_to_strafe move_strafe_injured dam_ko dam_ad ANIM_GROUP_GESTURE_M_GENERIC facial_clipset_group_gen_male ANIM_GROUP_VISEMES_M_HI CLIP_SET_ID_INVALID Male Male_prone NMBS_SLOW_GETUPS 3Lateral_Facial DEFAULT STANDARD_PED STANDARD_PED STANDARD_MALE NO_IK CIVMALE STANDARD_PED DEFAULT_PERCEPTION BS_AI WEAPON_UNARMED Streamed_Male DEFAULT VFXPEDINFO_HUMAN_GENERIC FLEE RADIO_GENRE_OFF RADIO_GENRE_OFF SILENT_CUTSCENE_PVG SAT_NONE TB_WARM SLOD_HUMAN SCENARIO_POP_STREAMING_NORMAL DSP_NORMAL IG_LesterCrest_2 IG_LesterCrest_2_p move_characters@lester@STD_CaneUp null expr_set_ambient_male null null CIVMALE move_lester_CaneUp move_ped_strafing move_ped_to_strafe move_strafe_injured dam_ko dam_ad ANIM_GROUP_GESTURE_M_GENERIC facial_clipset_group_gen_male ANIM_GROUP_VISEMES_M_LO CLIP_SET_ID_INVALID Male Male_prone NMBS_SLOW_GETUPS ambientPed_upperWrinkles DEFAULT STANDARD_PED STANDARD_PED STANDARD_MALE CIVMALE STANDARD_PED DEFAULT_PERCEPTION BS_AI WEAPON_UNARMED Streamed_Male DEFAULT VFXPEDINFO_HUMAN_GENERIC FLEE RADIO_GENRE_REGGAE RADIO_GENRE_SURF LESTER_PVG SAT_NONE TB_WARM SLOD_HUMAN SCENARIO_POP_STREAMING_NORMAL DSP_NORMAL CSB_Mrs_R null move_f@generic null CSB_Denise_friend CSB_Denise_friend CIVFEMALE move_characters@patricia move_ped_strafing move_ped_to_strafe move_strafe_injured dam_ko dam_ad ANIM_GROUP_GESTURE_F_GENERIC facial_clipset_group_gen_female ANIM_GROUP_VISEMES_F_LO CLIP_SET_ID_INVALID Male Male_prone NMBS_SLOW_GETUPS null DEFAULT STANDARD_PED STANDARD_PED STANDARD_FEMALE CIVFEMALE STANDARD_PED DEFAULT_PERCEPTION BS_AI WEAPON_UNARMED Streamed_Female DEFAULT VFXPEDINFO_HUMAN_GENERIC FLEE RADIO_GENRE_POP RADIO_GENRE_DANCE SILENT_CUTSCENE_PVG SAT_NONE TB_WARM SLOD_HUMAN SCENARIO_POP_STREAMING_NORMAL DSP_NORMAL CSB_Avon CSB_Avon_p move_m@generic null CSB_Avon CSB_Avon CIVMALE move_m@generic move_ped_strafing move_ped_to_strafe move_strafe_injured dam_ko dam_ad ANIM_GROUP_GESTURE_M_GENERIC facial_clipset_group_gen_male ANIM_GROUP_VISEMES_M_HI CLIP_SET_ID_INVALID Male Male_prone NMBS_SLOW_GETUPS null DEFAULT STANDARD_PED STANDARD_PED STANDARD_MALE CIVMALE STANDARD_PED DEFAULT_PERCEPTION BS_AI WEAPON_UNARMED Streamed_Male DEFAULT VFXPEDINFO_HUMAN_GENERIC FLEE RADIO_GENRE_OFF RADIO_GENRE_OFF SILENT_CUTSCENE_PVG SAT_NONE TB_WARM SLOD_HUMAN SCENARIO_POP_STREAMING_NORMAL DSP_NORMAL IG_Avon Ig_Avon_p move_m@generic null expr_set_ambient_male_skirt null null CIVMALE move_chubby move_ped_strafing move_ped_to_strafe move_strafe_injured dam_ko dam_ad ANIM_GROUP_GESTURE_M_GENERIC facial_clipset_group_gen_male ANIM_GROUP_VISEMES_M_LO CLIP_SET_ID_INVALID Male Male_prone NMBS_SLOW_GETUPS null DEFAULT STANDARD_PED STANDARD_PED STANDARD_MALE CIVMALE STANDARD_PED DEFAULT_PERCEPTION BS_AI WEAPON_UNARMED CONSTRUCTION DEFAULT VFXPEDINFO_HUMAN_GENERIC FLEE RADIO_GENRE_MODERN_ROCK RADIO_GENRE_MOTOWN SF_JEER_AT_HOT_PED H2AVON_PVG SAT_NONE TB_WARM SLOD_HUMAN SCENARIO_POP_STREAMING_NORMAL DSP_NORMAL CSB_Bogdan CSB_Bogdan_p move_m@generic null CSB_Bogdan CSB_Bogdan CIVMALE move_m@generic move_ped_strafing move_ped_to_strafe move_strafe_injured dam_ko dam_ad ANIM_GROUP_GESTURE_M_GENERIC facial_clipset_group_gen_male ANIM_GROUP_VISEMES_M_HI CLIP_SET_ID_INVALID Male Male_prone NMBS_SLOW_GETUPS null DEFAULT STANDARD_PED STANDARD_PED STANDARD_MALE CIVMALE STANDARD_PED DEFAULT_PERCEPTION BS_AI WEAPON_UNARMED Streamed_Male DEFAULT VFXPEDINFO_HUMAN_GENERIC FLEE RADIO_GENRE_OFF RADIO_GENRE_OFF H2_BOGDAN_PVG SAT_NONE TB_WARM SLOD_HUMAN SCENARIO_POP_STREAMING_NORMAL DSP_NORMAL U_M_Y_Juggernaut_01 U_M_Y_Juggernaut_01_p move_m@multiplayer null HC_Gunman HC_Gunman CIVMALE move_m@multiplayer move_ped_strafing move_ped_to_strafe move_strafe_injured dam_ko dam_ad ANIM_GROUP_GESTURE_M_GENERIC facial_clipset_group_gen_male ANIM_GROUP_VISEMES_M_LO CLIP_SET_ID_INVALID Male Male_prone NMBS_SLOW_GETUPS fx_fire_torch DEFAULT STANDARD_PED STANDARD_PED STANDARD_MALE CIVMALE STANDARD_PED DEFAULT_PERCEPTION BS_AI WEAPON_UNARMED Streamed_Male DEFAULT VFXPEDINFO_HUMAN_GENERIC FLEE RADIO_GENRE_PUNK RADIO_GENRE_MOTOWN COLLAR SILENT_PVG SAT_NONE TB_WARM SLOD_HUMAN SCENARIO_POP_STREAMING_NORMAL DSP_NORMAL MP_M_AvonGoon MP_M_AvonGoon_p move_m@generic null expr_set_ambient_male_skirt null null CIVMALE move_chubby move_ped_strafing move_ped_to_strafe move_strafe_injured dam_ko dam_ad ANIM_GROUP_GESTURE_M_GENERIC facial_clipset_group_gen_male ANIM_GROUP_VISEMES_M_LO CLIP_SET_ID_INVALID Male Male_prone NMBS_SLOW_GETUPS null DEFAULT STANDARD_PED STANDARD_PED STANDARD_MALE CIVMALE STANDARD_PED DEFAULT_PERCEPTION BS_AI WEAPON_UNARMED CONSTRUCTION DEFAULT VFXPEDINFO_HUMAN_GENERIC FLEE RADIO_GENRE_MODERN_ROCK RADIO_GENRE_MOTOWN SF_JEER_AT_HOT_PED G_M_Y_X17_AGuard_PVG SAT_NONE TB_WARM SLOD_HUMAN SCENARIO_POP_STREAMING_NORMAL DSP_NORMAL MP_M_BogdanGoon MP_M_BogdanGoon_p move_m@generic null expr_set_ambient_male_skirt null null CIVMALE move_chubby move_ped_strafing move_ped_to_strafe move_strafe_injured dam_ko dam_ad ANIM_GROUP_GESTURE_M_GENERIC facial_clipset_group_gen_male ANIM_GROUP_VISEMES_M_LO CLIP_SET_ID_INVALID Male Male_prone NMBS_SLOW_GETUPS null DEFAULT STANDARD_PED STANDARD_PED STANDARD_MALE CIVMALE STANDARD_PED DEFAULT_PERCEPTION BS_AI WEAPON_UNARMED CONSTRUCTION DEFAULT VFXPEDINFO_HUMAN_GENERIC FLEE RADIO_GENRE_MODERN_ROCK RADIO_GENRE_MOTOWN SF_JEER_AT_HOT_PED G_M_M_X17_RSO_PVG SAT_NONE TB_WARM SLOD_HUMAN SCENARIO_POP_STREAMING_NORMAL DSP_NORMAL U_M_Y_Corpse_01 null move_m@generic null expr_set_ambient_male_skirt null null CIVMALE move_chubby move_ped_strafing move_ped_to_strafe move_strafe_injured dam_ko_@gangops@morgue@table@ dam_ad ANIM_GROUP_GESTURE_M_GENERIC facial_clipset_group_gen_male ANIM_GROUP_VISEMES_M_LO CLIP_SET_ID_INVALID Male Male_prone NMBS_SLOW_GETUPS null DEFAULT STANDARD_PED STANDARD_PED STANDARD_MALE CIVMALE STANDARD_PED DEFAULT_PERCEPTION BS_AI WEAPON_UNARMED CONSTRUCTION DEFAULT VFXPEDINFO_HUMAN_GENERIC FLEE RADIO_GENRE_MODERN_ROCK RADIO_GENRE_MOTOWN SF_JEER_AT_HOT_PED SILENT_PVG SAT_NONE TB_WARM SLOD_HUMAN SCENARIO_POP_STREAMING_NORMAL DSP_NORMAL comp_peds_helmets_moped comp_peds_helmets_motox comp_peds_helmets_sports comp_peds_helmets_shorty strm_peds_mpTattRTs strm_peds_mpShare comp_peds_marine comp_peds_marine S_M_M_Marine_01 S_M_Y_Marine_01 S_M_Y_Marine_02 S_M_Y_Marine_03 "; [Fact] public void GetChildInnerTextShouldReturnInnerText() { var xdoc = new XmlDocument(); xdoc.LoadXml(markup); XmlNodeList? items = xdoc.SelectNodes("CVehicleModelInfo__InitDataList/InitDatas/Item | CVehicleModelInfo__InitDataList/InitDatas/item"); VehicleInitData? initDataExpected = null; VehicleInitData? initData = null; string? modelName; string? gameName; for (int i = 0; i < items.Count; i++) { var node = items[i]; Assert.NotNull(node); initDataExpected = new VehicleInitData(); initDataExpected.Load(node); modelName = Xml.GetChildInnerText(node, "modelName"); Assert.Equal("brabusgt600", modelName); gameName = Xml.GetChildInnerText(node, "gameName"); Assert.Equal("GT 600", gameName); } Assert.NotNull(initDataExpected); Assert.Equal("brabusgt600", initDataExpected.modelName); Assert.Equal("GT 600", initDataExpected.gameName); Assert.Equal("brabusgt600", initDataExpected.txdName); //Assert.Null(initDataExpected.trailers); Assert.Equal(9, initDataExpected.trailers.Length); Assert.Single(initDataExpected.drivers); Assert.Equal("S_M_Y_Cop_01", initDataExpected.drivers[0].DriverName); Assert.Equal("trailers4", initDataExpected.trailers[4]); using XmlReader xmlReader = XmlReader.Create(new StringReader(markup)); gameName = null; modelName = null; while (xmlReader.Read()) { if (xmlReader.NodeType == XmlNodeType.Element) { xmlReader.ReadToFollowing("InitDatas"); if (xmlReader.Name == "InitDatas") { xmlReader.Read(); xmlReader.MoveToContent(); initData = new VehicleInitData(); initData.Load(xmlReader); } } } Assert.NotNull(initData); Assert.NotNull(initData.pOverrideRagdollThreshold); Assert.Equal(initDataExpected.pOverrideRagdollThreshold, initData.pOverrideRagdollThreshold); Assert.Equal(initDataExpected.requiredExtras, initData.requiredExtras); Assert.Equivalent(initDataExpected, initData); var bytes = Encoding.UTF8.GetBytes(markup); var vehiclesFileExpected = new VehiclesFile(); var fileEntry = RpfFile.CreateFileEntry("kaas.meta", "saak.meta", ref bytes); vehiclesFileExpected.LoadOld(bytes, fileEntry); var vehiclesFile = new VehiclesFile(); vehiclesFile.Load(bytes, fileEntry); Assert.Equivalent(vehiclesFileExpected, vehiclesFile); } [Fact] public void IterateOverItemsShouldReturnOnlyItemsAndAdvanceXmlReaderPastEndElement() { string xml = @" WHEEL_FRONT_RIGHT_CAMERA WHEEL_FRONT_LEFT_CAMERA WHEEL_REAR_RIGHT_CAMERA WHEEL_REAR_LEFT_CAMERA "; using var xmlReader = XmlReader.Create(new StringReader(xml)); foreach(var item in Xml.IterateItems(xmlReader, "cinematicPartCamera")) { Assert.NotNull(item); output.WriteLine(item.ToString()); } } [Fact] public void PedFileShouldBeSame() { var xmlText = TextUtil.GetUTF8Text(System.Text.Encoding.UTF8.GetBytes(pedsList)); using XmlReader xmlReader = XmlReader.Create(new StringReader(xmlText)); var xDocument = new XmlDocument(); xDocument.LoadXml(xmlText); var pedsFile = new CPedModelInfo__InitDataList(xmlReader); Assert.NotNull(xDocument?.DocumentElement); var pedsFileExpected = new CPedModelInfo__InitDataList(xDocument.DocumentElement); for (int i = 0; i < pedsFileExpected.InitDatas.Length; i++) { Assert.Equivalent(pedsFileExpected.InitDatas[i], pedsFile.InitDatas[i]); } for (int i = 0; i < pedsFileExpected.multiTxdRelationships.Length; i++) { Assert.Equivalent(pedsFileExpected.multiTxdRelationships[i], pedsFile.multiTxdRelationships[i]); } Assert.Equivalent(pedsFileExpected, pedsFile); } [Fact] public void ContentFileShouldBeTheSame() { var fileStream = File.OpenRead(TestFiles.GetFilePath("content.xml")); var xmlReader = XmlReader.Create(fileStream); var contentFile = new DlcContentFile(); contentFile.Load(xmlReader); var xmlDocument = new XmlDocument(); xmlDocument.LoadXml(TextUtil.GetUTF8Text(File.ReadAllBytes(TestFiles.GetFilePath("content.xml")))); var contentFileExpected = new DlcContentFile(); contentFileExpected.Load(xmlDocument); for (int i = 0; i < contentFileExpected.contentChangeSets.Count; i++) { Assert.Equivalent(contentFileExpected.contentChangeSets[i], contentFile.contentChangeSets[i]); } Assert.Equivalent(contentFileExpected, contentFile); } } }