using CodeWalker.GameFiles; using SharpDX; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using WeifenLuo.WinFormsUI.Docking; namespace CodeWalker.Project.Panels { public partial class EditYmapPanel : ProjectPanel { public ProjectForm ProjectForm; public YmapFile Ymap { get; set; } private bool populatingui = false; private bool waschanged = false; public EditYmapPanel(ProjectForm projectForm) { ProjectForm = projectForm; InitializeComponent(); } public void SetYmap(YmapFile ymap) { Ymap = ymap; Tag = ymap; UpdateFormTitle(); UpdateYmapUI(); waschanged = ymap?.HasChanged ?? false; } public void UpdateFormTitleYmapChanged() { bool changed = Ymap.HasChanged; if (!waschanged && changed) { UpdateFormTitle(); waschanged = true; } else if (waschanged && !changed) { UpdateFormTitle(); waschanged = false; } } private void UpdateFormTitle() { string fn = Ymap.RpfFileEntry?.Name ?? Ymap.Name; if (string.IsNullOrEmpty(fn)) fn = "untitled.ymap"; Text = fn + (Ymap.HasChanged ? "*" : ""); } public void UpdateYmapUI() { if (Ymap == null) { YmapNameTextBox.Text = ""; YmapNameHashLabel.Text = "Hash: 0"; YmapParentTextBox.Text = string.Empty; YmapParentHashLabel.Text = "Hash: 0"; YmapFlagsTextBox.Text = string.Empty; YmapContentFlagsTextBox.Text = string.Empty; YmapCFlagsHDCheckBox.Checked = false; YmapCFlagsLODCheckBox.Checked = false; YmapCFlagsSLOD2CheckBox.Checked = false; YmapCFlagsInteriorCheckBox.Checked = false; YmapCFlagsSLODCheckBox.Checked = false; YmapCFlagsOcclusionCheckBox.Checked = false; YmapCFlagsPhysicsCheckBox.Checked = false; YmapCFlagsLODLightsCheckBox.Checked = false; YmapCFlagsDistLightsCheckBox.Checked = false; YmapCFlagsCriticalCheckBox.Checked = false; YmapCFlagsGrassCheckBox.Checked = false; YmapFlagsScriptedCheckBox.Checked = false; YmapFlagsLODCheckBox.Checked = false; YmapPhysicsDictionariesTextBox.Text = string.Empty; YmapEntitiesExtentsMinTextBox.Text = string.Empty; YmapEntitiesExtentsMaxTextBox.Text = string.Empty; YmapStreamingExtentsMinTextBox.Text = string.Empty; YmapStreamingExtentsMaxTextBox.Text = string.Empty; YmapFileLocationTextBox.Text = string.Empty; YmapProjectPathTextBox.Text = string.Empty; } else { populatingui = true; var md = Ymap.CMapData; if (md.name.Hash == 0) { string name = Path.GetFileNameWithoutExtension(Ymap.Name); JenkIndex.Ensure(name); md.name = new MetaHash(JenkHash.GenHash(name)); } var project = ProjectForm?.CurrentProjectFile; YmapNameTextBox.Text = md.name.ToString(); YmapNameHashLabel.Text = "Hash: " + md.name.Hash.ToString(); YmapParentTextBox.Text = md.parent.ToString(); YmapParentHashLabel.Text = "Hash: " + md.parent.Hash.ToString(); YmapEntitiesExtentsMinTextBox.Text = FloatUtil.GetVector3String(md.entitiesExtentsMin); YmapEntitiesExtentsMaxTextBox.Text = FloatUtil.GetVector3String(md.entitiesExtentsMax); YmapStreamingExtentsMinTextBox.Text = FloatUtil.GetVector3String(md.streamingExtentsMin); YmapStreamingExtentsMaxTextBox.Text = FloatUtil.GetVector3String(md.streamingExtentsMax); YmapFileLocationTextBox.Text = Ymap.RpfFileEntry?.Path ?? Ymap.FilePath; YmapProjectPathTextBox.Text = (project != null) ? project.GetRelativePath(Ymap.FilePath) : Ymap.FilePath; UpdateYmapFlagsUI(true, true); UpdateYmapPhysicsDictionariesUI(); populatingui = false; ////struct CMapData: //MetaHash name { get; set; } //8 8: Hash: 0: name //MetaHash parent { get; set; } //12 12: Hash: 0: parent //uint flags { get; set; } //16 16: UnsignedInt: 0: flags //uint contentFlags { get; set; } //20 20: UnsignedInt: 0: contentFlags//1785155637 //Vector3 streamingExtentsMin { get; set; } //32 32: Float_XYZ: 0: streamingExtentsMin//3710026271 //Vector3 streamingExtentsMax { get; set; } //48 48: Float_XYZ: 0: streamingExtentsMax//2720965429 //Vector3 entitiesExtentsMin { get; set; } //64 64: Float_XYZ: 0: entitiesExtentsMin//477478129 //Vector3 entitiesExtentsMax { get; set; } //80 80: Float_XYZ: 0: entitiesExtentsMax//1829192759 //Array_StructurePointer entities { get; set; } //96 96: Array: 0: entities {0: StructurePointer: 0: 256} //Array_Structure containerLods { get; set; } //112 112: Array: 0: containerLods//2935983381 {0: Structure: 372253349: 256} //Array_Structure boxOccluders { get; set; } //128 128: Array: 0: boxOccluders//3983590932 {0: Structure: SectionUNKNOWN7: 256} //Array_Structure occludeModels { get; set; } //144 144: Array: 0: occludeModels//2132383965 {0: Structure: SectionUNKNOWN5: 256} //Array_uint physicsDictionaries { get; set; } //160 160: Array: 0: physicsDictionaries//949589348 {0: Hash: 0: 256} //rage__fwInstancedMapData instancedData { get; set; } //176 176: Structure: rage__fwInstancedMapData: instancedData//2569067561 //Array_Structure timeCycleModifiers { get; set; } //224 224: Array: 0: timeCycleModifiers {0: Structure: CTimeCycleModifier: 256} //Array_Structure carGenerators { get; set; } //240 240: Array: 0: carGenerators//3254823756 {0: Structure: CCarGen: 256} //CLODLight LODLightsSOA { get; set; } //256 256: Structure: CLODLight: LODLightsSOA//1774371066 //CDistantLODLight DistantLODLightsSOA { get; set; } //392 392: Structure: CDistantLODLight: DistantLODLightsSOA//2954466641 //CBlockDesc block { get; set; } //440 440: Structure: CBlockDesc//3072355914: block } } private void UpdateYmapFlagsUI(bool updateCheckboxes, bool updateTextboxes) { if (Ymap == null) return; var md = Ymap.CMapData; var flags = md.flags; var contentFlags = md.contentFlags; if (updateCheckboxes) { YmapCFlagsHDCheckBox.Checked = BitUtil.IsBitSet(contentFlags, 0); //1 YmapCFlagsLODCheckBox.Checked = BitUtil.IsBitSet(contentFlags, 1); //2 YmapCFlagsSLOD2CheckBox.Checked = BitUtil.IsBitSet(contentFlags, 2); //4 YmapCFlagsInteriorCheckBox.Checked = BitUtil.IsBitSet(contentFlags, 3); //8 YmapCFlagsSLODCheckBox.Checked = BitUtil.IsBitSet(contentFlags, 4); //16 YmapCFlagsOcclusionCheckBox.Checked = BitUtil.IsBitSet(contentFlags, 5); //32 YmapCFlagsPhysicsCheckBox.Checked = BitUtil.IsBitSet(contentFlags, 6); //64 YmapCFlagsLODLightsCheckBox.Checked = BitUtil.IsBitSet(contentFlags, 7); //128 YmapCFlagsDistLightsCheckBox.Checked = BitUtil.IsBitSet(contentFlags, 8); //256 YmapCFlagsCriticalCheckBox.Checked = BitUtil.IsBitSet(contentFlags, 9); //512 YmapCFlagsGrassCheckBox.Checked = BitUtil.IsBitSet(contentFlags, 10); //1024 YmapFlagsScriptedCheckBox.Checked = BitUtil.IsBitSet(flags, 0); //1 YmapFlagsLODCheckBox.Checked = BitUtil.IsBitSet(flags, 1); //2 } if (updateTextboxes) { YmapFlagsTextBox.Text = flags.ToString(); YmapContentFlagsTextBox.Text = contentFlags.ToString(); } } private void UpdateYmapPhysicsDictionariesUI() { if ((Ymap == null) || (Ymap.physicsDictionaries == null)) { YmapPhysicsDictionariesTextBox.Text = string.Empty; } else { StringBuilder sb = new StringBuilder(); foreach (var hash in Ymap.physicsDictionaries) { sb.AppendLine(hash.ToString()); } YmapPhysicsDictionariesTextBox.Text = sb.ToString(); } } private void SetYmapHasChanged(bool changed) { ProjectForm.SetYmapHasChanged(changed); UpdateFormTitleYmapChanged(); } private void SetYmapPhysicsDictionariesFromTextbox() { if (populatingui) return; if (Ymap == null) return; List hashes = new List(); var strs = YmapPhysicsDictionariesTextBox.Text.Split('\n'); foreach (var str in strs) { var tstr = str.Trim(); if (!string.IsNullOrEmpty(tstr)) { uint h = 0; if (uint.TryParse(tstr, out h)) { hashes.Add(h); } else { h = JenkHash.GenHash(tstr.ToLowerInvariant()); hashes.Add(h); } } } lock (ProjectForm.ProjectSyncRoot) { Ymap.physicsDictionaries = (hashes.Count > 0) ? hashes.ToArray() : null; SetYmapHasChanged(true); } } private void SetYmapFlagsFromCheckBoxes() { if (populatingui) return; if (Ymap == null) return; uint flags = 0; uint contentFlags = 0; contentFlags = BitUtil.UpdateBit(contentFlags, 0, YmapCFlagsHDCheckBox.Checked); contentFlags = BitUtil.UpdateBit(contentFlags, 1, YmapCFlagsLODCheckBox.Checked); contentFlags = BitUtil.UpdateBit(contentFlags, 2, YmapCFlagsSLOD2CheckBox.Checked); contentFlags = BitUtil.UpdateBit(contentFlags, 3, YmapCFlagsInteriorCheckBox.Checked); contentFlags = BitUtil.UpdateBit(contentFlags, 4, YmapCFlagsSLODCheckBox.Checked); contentFlags = BitUtil.UpdateBit(contentFlags, 5, YmapCFlagsOcclusionCheckBox.Checked); contentFlags = BitUtil.UpdateBit(contentFlags, 6, YmapCFlagsPhysicsCheckBox.Checked); contentFlags = BitUtil.UpdateBit(contentFlags, 7, YmapCFlagsLODLightsCheckBox.Checked); contentFlags = BitUtil.UpdateBit(contentFlags, 8, YmapCFlagsDistLightsCheckBox.Checked); contentFlags = BitUtil.UpdateBit(contentFlags, 9, YmapCFlagsCriticalCheckBox.Checked); contentFlags = BitUtil.UpdateBit(contentFlags, 10, YmapCFlagsGrassCheckBox.Checked); flags = BitUtil.UpdateBit(flags, 0, YmapFlagsScriptedCheckBox.Checked); flags = BitUtil.UpdateBit(flags, 1, YmapFlagsLODCheckBox.Checked); lock (ProjectForm.ProjectSyncRoot) { if (Ymap._CMapData.flags != flags) { Ymap._CMapData.flags = flags; SetYmapHasChanged(true); } if (Ymap._CMapData.contentFlags != contentFlags) { Ymap._CMapData.contentFlags = contentFlags; SetYmapHasChanged(true); } } populatingui = true; UpdateYmapFlagsUI(false, true); //update textbox populatingui = false; } private void SetYmapFlagsFromTextBoxes() { if (populatingui) return; if (Ymap == null) return; uint flags = 0; uint contentFlags = 0; uint.TryParse(YmapFlagsTextBox.Text, out flags); uint.TryParse(YmapContentFlagsTextBox.Text, out contentFlags); lock (ProjectForm.ProjectSyncRoot) { if (Ymap._CMapData.flags != flags) { Ymap._CMapData.flags = flags; SetYmapHasChanged(true); } if (Ymap._CMapData.contentFlags != contentFlags) { Ymap._CMapData.contentFlags = contentFlags; SetYmapHasChanged(true); } } populatingui = true; UpdateYmapFlagsUI(true, false); //update checkboxes populatingui = false; } private void CalcYmapFlags() { if (populatingui) return; if (Ymap == null) return; lock (ProjectForm.ProjectSyncRoot) { if (Ymap.CalcFlags()) { SetYmapHasChanged(true); } } populatingui = true; UpdateYmapFlagsUI(true, true); //update checkboxes and textboxes populatingui = false; } private void CalcYmapExtents() { if (Ymap == null) return; var allents = Ymap.AllEntities; var allbatches = Ymap.GrassInstanceBatches; if ((allents == null) && (allbatches == null)) { MessageBox.Show("No items to calculate extents from."); return; } lock (ProjectForm.ProjectSyncRoot) { if (Ymap.CalcExtents()) { SetYmapHasChanged(true); } } populatingui = true; var md = Ymap.CMapData; YmapEntitiesExtentsMinTextBox.Text = FloatUtil.GetVector3String(md.entitiesExtentsMin); YmapEntitiesExtentsMaxTextBox.Text = FloatUtil.GetVector3String(md.entitiesExtentsMax); YmapStreamingExtentsMinTextBox.Text = FloatUtil.GetVector3String(md.streamingExtentsMin); YmapStreamingExtentsMaxTextBox.Text = FloatUtil.GetVector3String(md.streamingExtentsMax); populatingui = false; } private void YmapNameTextBox_TextChanged(object sender, EventArgs e) { if (populatingui) return; uint hash = 0; string name = YmapNameTextBox.Text; if (!uint.TryParse(name, out hash))//don't re-hash hashes { hash = JenkHash.GenHash(name); JenkIndex.Ensure(name); } YmapNameHashLabel.Text = "Hash: " + hash.ToString(); if (Ymap != null) { lock (ProjectForm.ProjectSyncRoot) { string ymname = name + ".ymap"; if (Ymap.Name != ymname) { Ymap.Name = ymname; Ymap._CMapData.name = new MetaHash(hash); SetYmapHasChanged(true); UpdateFormTitle(); } } } } private void YmapParentTextBox_TextChanged(object sender, EventArgs e) { if (populatingui) return; uint hash = 0; string name = YmapParentTextBox.Text; if (!uint.TryParse(name, out hash))//don't re-hash hashes { hash = JenkHash.GenHash(name); JenkIndex.Ensure(name); } YmapParentHashLabel.Text = "Hash: " + hash.ToString(); if (hash != 0) { var entry = ProjectForm.FindParentYmapEntry(hash); if (entry == null) { YmapParentHashLabel.Text += " (not found!)"; } } if (Ymap != null) { lock (ProjectForm.ProjectSyncRoot) { if (Ymap._CMapData.parent.Hash != hash) { Ymap._CMapData.parent = new MetaHash(hash); SetYmapHasChanged(true); //TODO: confirm entity parent linkage? } } } } private void YmapFlagsTextBox_TextChanged(object sender, EventArgs e) { SetYmapFlagsFromTextBoxes(); } private void YmapContentFlagsTextBox_TextChanged(object sender, EventArgs e) { SetYmapFlagsFromTextBoxes(); } private void YmapCFlagsHDCheckBox_CheckedChanged(object sender, EventArgs e) { SetYmapFlagsFromCheckBoxes(); } private void YmapCFlagsLODCheckBox_CheckedChanged(object sender, EventArgs e) { SetYmapFlagsFromCheckBoxes(); } private void YmapCFlagsSLOD2CheckBox_CheckedChanged(object sender, EventArgs e) { SetYmapFlagsFromCheckBoxes(); } private void YmapCFlagsInteriorCheckBox_CheckedChanged(object sender, EventArgs e) { SetYmapFlagsFromCheckBoxes(); } private void YmapCFlagsSLODCheckBox_CheckedChanged(object sender, EventArgs e) { SetYmapFlagsFromCheckBoxes(); } private void YmapCFlagsOcclusionCheckBox_CheckedChanged(object sender, EventArgs e) { SetYmapFlagsFromCheckBoxes(); } private void YmapCFlagsPhysicsCheckBox_CheckedChanged(object sender, EventArgs e) { SetYmapFlagsFromCheckBoxes(); } private void YmapCFlagsLODLightsCheckBox_CheckedChanged(object sender, EventArgs e) { SetYmapFlagsFromCheckBoxes(); } private void YmapCFlagsDistLightsCheckBox_CheckedChanged(object sender, EventArgs e) { SetYmapFlagsFromCheckBoxes(); } private void YmapCFlagsCriticalCheckBox_CheckedChanged(object sender, EventArgs e) { SetYmapFlagsFromCheckBoxes(); } private void YmapCFlagsGrassCheckBox_CheckedChanged(object sender, EventArgs e) { SetYmapFlagsFromCheckBoxes(); } private void YmapFlagsScriptedCheckBox_CheckedChanged(object sender, EventArgs e) { SetYmapFlagsFromCheckBoxes(); } private void YmapFlagsLODCheckBox_CheckedChanged(object sender, EventArgs e) { SetYmapFlagsFromCheckBoxes(); } private void YmapCalculateFlagsButton_Click(object sender, EventArgs e) { CalcYmapFlags(); } private void YmapEntitiesExtentsMinTextBox_TextChanged(object sender, EventArgs e) { if (populatingui) return; if (Ymap == null) return; Vector3 v = FloatUtil.ParseVector3String(YmapEntitiesExtentsMinTextBox.Text); lock (ProjectForm.ProjectSyncRoot) { if (Ymap._CMapData.entitiesExtentsMin != v) { Ymap._CMapData.entitiesExtentsMin = v; SetYmapHasChanged(true); } } } private void YmapEntitiesExtentsMaxTextBox_TextChanged(object sender, EventArgs e) { if (populatingui) return; if (Ymap == null) return; Vector3 v = FloatUtil.ParseVector3String(YmapEntitiesExtentsMaxTextBox.Text); lock (ProjectForm.ProjectSyncRoot) { if (Ymap._CMapData.entitiesExtentsMax != v) { Ymap._CMapData.entitiesExtentsMax = v; SetYmapHasChanged(true); } } } private void YmapStreamingExtentsMinTextBox_TextChanged(object sender, EventArgs e) { if (populatingui) return; if (Ymap == null) return; Vector3 v = FloatUtil.ParseVector3String(YmapStreamingExtentsMinTextBox.Text); lock (ProjectForm.ProjectSyncRoot) { if (Ymap._CMapData.streamingExtentsMin != v) { Ymap._CMapData.streamingExtentsMin = v; SetYmapHasChanged(true); } } } private void YmapStreamingExtentsMaxTextBox_TextChanged(object sender, EventArgs e) { if (populatingui) return; if (Ymap == null) return; Vector3 v = FloatUtil.ParseVector3String(YmapStreamingExtentsMaxTextBox.Text); lock (ProjectForm.ProjectSyncRoot) { if (Ymap._CMapData.streamingExtentsMax != v) { Ymap._CMapData.streamingExtentsMax = v; SetYmapHasChanged(true); } } } private void YmapCalculateExtentsButton_Click(object sender, EventArgs e) { CalcYmapExtents(); } private void YmapPhysicsDictionariesTextBox_TextChanged(object sender, EventArgs e) { SetYmapPhysicsDictionariesFromTextbox(); } } }