diff --git a/CodeWalker.Core/GameFiles/FileTypes/RelFile.cs b/CodeWalker.Core/GameFiles/FileTypes/RelFile.cs index 7274b2b..831d64f 100644 --- a/CodeWalker.Core/GameFiles/FileTypes/RelFile.cs +++ b/CodeWalker.Core/GameFiles/FileTypes/RelFile.cs @@ -48,12 +48,9 @@ SOFTWARE. namespace CodeWalker.GameFiles { - [TC(typeof(EXP))] public class RelFile : PackedFile + [TC(typeof(EXP))] public class RelFile : GameFile, PackedFile { - public RpfFileEntry FileEntry { get; set; } - public string Name { get; set; } - - public uint Type { get; set; } + public uint RelType { get; set; } public uint DataLength { get; set; } public byte[] DataBlock { get; set; } public uint DataUnkVal { get; set; } @@ -65,12 +62,12 @@ namespace CodeWalker.GameFiles public uint IndexStringFlags { get; set; } public RelIndexHash[] IndexHashes { get; set; } public RelIndexString[] IndexStrings { get; set; } - public uint Unk05Count { get; set; } - public uint[] Unk05Arr { get; set; } - public MetaHash[] Unk05Hashes { get; set; } - public uint ContainerCount { get; set; } - public uint[] ContainerUnkArr { get; set; } - public MetaHash[] ContainerHashes { get; set; } + public uint WaveTracksCount { get; set; } + public uint[] WaveTracksOffsets { get; set; } + public MetaHash[] WaveTracksHashes { get; set; } + public uint WaveContainersCount { get; set; } + public uint[] WaveContainersOffsets { get; set; } + public MetaHash[] WaveContainersHashes { get; set; } public RelData[] RelDatas { get; set; } public RelData[] RelDatasSorted { get; set; } @@ -79,24 +76,29 @@ namespace CodeWalker.GameFiles public Dictionary RelDataDict { get; set; } = new Dictionary(); - public RelFile() + //fields used by the editor: + public bool HasChanged { get; set; } = false; + public List SaveWarnings = null; + + + public RelFile() : base(null, GameFileType.Rel) { } - public RelFile(RpfFileEntry entry) + public RelFile(RpfFileEntry entry) : base(entry, GameFileType.Rel) { - FileEntry = entry; + RpfFileEntry = entry; } public void Load(byte[] data, RpfFileEntry entry) { - FileEntry = entry; + RpfFileEntry = entry; Name = entry.Name; MemoryStream ms = new MemoryStream(data); BinaryReader br = new BinaryReader(ms); StringBuilder sb = new StringBuilder(); - Type = br.ReadUInt32(); //type/version? + RelType = br.ReadUInt32(); //type/version? DataLength = br.ReadUInt32(); //length of data block DataBlock = br.ReadBytes((int)DataLength); //main data block... @@ -105,12 +107,12 @@ namespace CodeWalker.GameFiles NameTableCount = br.ReadUInt32(); if (NameTableCount > 0) { - uint[] d02 = new uint[NameTableCount]; //string offsets + uint[] ntoffsets = new uint[NameTableCount]; //string offsets for (uint i = 0; i < NameTableCount; i++) { - d02[i] = br.ReadUInt32(); + ntoffsets[i] = br.ReadUInt32(); } - NameTableOffsets = d02; + NameTableOffsets = ntoffsets; string[] names = new string[NameTableCount]; for (uint i = 0; i < NameTableCount; i++) { @@ -133,7 +135,7 @@ namespace CodeWalker.GameFiles if (IndexCount > 0) { //checking NameTableLength here doesn't make sense! - if ((Type == 4) && (NameTableLength == 4))//audioconfig.dat4.rel + if ((RelType == 4) && (NameTableLength == 4))//audioconfig.dat4.rel { IndexStringFlags = br.ReadUInt32(); //what is this? 2524 RelIndexString[] indexstrs = new RelIndexString[IndexCount]; @@ -146,11 +148,11 @@ namespace CodeWalker.GameFiles char c = (char)br.ReadByte(); if (c != 0) sb.Append(c); } - RelIndexString cunk01 = new RelIndexString(); - cunk01.Name = sb.ToString(); - cunk01.Offset = br.ReadUInt32(); - cunk01.Length = br.ReadUInt32(); - indexstrs[i] = cunk01; + RelIndexString ristr = new RelIndexString(); + ristr.Name = sb.ToString(); + ristr.Offset = br.ReadUInt32(); + ristr.Length = br.ReadUInt32(); + indexstrs[i] = ristr; } IndexStrings = indexstrs; } @@ -159,51 +161,51 @@ namespace CodeWalker.GameFiles RelIndexHash[] indexhashes = new RelIndexHash[IndexCount]; for (uint i = 0; i < IndexCount; i++) { - RelIndexHash unk01 = new RelIndexHash(); - unk01.Name = new MetaHash(br.ReadUInt32()); - unk01.Offset = br.ReadUInt32(); - unk01.Length = br.ReadUInt32(); - indexhashes[i] = unk01; + RelIndexHash rihash = new RelIndexHash(); + rihash.Name = new MetaHash(br.ReadUInt32()); + rihash.Offset = br.ReadUInt32(); + rihash.Length = br.ReadUInt32(); + indexhashes[i] = rihash; } IndexHashes = indexhashes; } } - Unk05Count = br.ReadUInt32(); - if (Unk05Count != 0) + WaveTracksCount = br.ReadUInt32(); + if (WaveTracksCount != 0) { - uint[] d05 = new uint[Unk05Count]; - MetaHash[] d05h = new MetaHash[Unk05Count]; - for (uint i = 0; i < Unk05Count; i++) + uint[] wtoffsets = new uint[WaveTracksCount]; + MetaHash[] wthashes = new MetaHash[WaveTracksCount]; + for (uint i = 0; i < WaveTracksCount; i++) { - d05[i] = br.ReadUInt32(); + wtoffsets[i] = br.ReadUInt32(); var pos = ms.Position; - ms.Position = d05[i]; - d05h[i] = new MetaHash(br.ReadUInt32()); + ms.Position = wtoffsets[i]; + wthashes[i] = new MetaHash(br.ReadUInt32()); ms.Position = pos; } - Unk05Arr = d05; - Unk05Hashes = d05h; + WaveTracksOffsets = wtoffsets; + WaveTracksHashes = wthashes; } - ContainerCount = br.ReadUInt32(); - if (ContainerCount != 0) + WaveContainersCount = br.ReadUInt32(); + if (WaveContainersCount != 0) { - uint[] cunks = new uint[ContainerCount]; - MetaHash[] chashes = new MetaHash[ContainerCount]; - for (uint i = 0; i < ContainerCount; i++) + uint[] wcoffsets = new uint[WaveContainersCount]; + MetaHash[] wchashes = new MetaHash[WaveContainersCount]; + for (uint i = 0; i < WaveContainersCount; i++) { - cunks[i] = br.ReadUInt32(); + wcoffsets[i] = br.ReadUInt32(); var pos = ms.Position; - ms.Position = cunks[i]; - chashes[i] = new MetaHash(br.ReadUInt32()); + ms.Position = wcoffsets[i]; + wchashes[i] = new MetaHash(br.ReadUInt32()); ms.Position = pos; } - ContainerUnkArr = cunks; - ContainerHashes = chashes; + WaveContainersOffsets = wcoffsets; + WaveContainersHashes = wchashes; } if (ms.Position != ms.Length) @@ -400,7 +402,7 @@ namespace CodeWalker.GameFiles byte[] data = br.ReadBytes((int)length); - RelData d = new RelData(); //use this base object to construct the derived one... + RelData d = new RelData(this); //use this base object to construct the derived one... d.Name = name; d.NameHash = hash; d.DataOffset = offset; @@ -412,7 +414,7 @@ namespace CodeWalker.GameFiles { d.ReadType(dbr); - switch (Type) + switch (RelType) { case 4: //speech.dat4.rel, audioconfig.dat4.rel return ReadData4(d, dbr); @@ -1098,6 +1100,18 @@ namespace CodeWalker.GameFiles + + + public void AddRelData(RelData d) //TODO!!! + { + } + public bool RemoveRelData(RelData d) //TODO!!! + { + return false; + } + + + public override string ToString() { return Name; @@ -1140,7 +1154,9 @@ namespace CodeWalker.GameFiles public byte[] Data { get; set; } public byte TypeID { get; set; } - public RelData() { } + public RelFile Rel { get; set; } + + public RelData(RelFile rel) { Rel = rel; } public RelData(RelData d) { NameHash = d.NameHash; @@ -1149,6 +1165,7 @@ namespace CodeWalker.GameFiles DataLength = d.DataLength; Data = d.Data; TypeID = d.TypeID; + Rel = d.Rel; } public void ReadType(BinaryReader br) @@ -2479,7 +2496,7 @@ namespace CodeWalker.GameFiles - public Dat151RelData() { } + public Dat151RelData(RelFile rel) : base(rel) { } public Dat151RelData(RelData d, BinaryReader br) : base(d) { Type = (Dat151RelType)TypeID; diff --git a/CodeWalker.Core/GameFiles/GameFileCache.cs b/CodeWalker.Core/GameFiles/GameFileCache.cs index 4752a07..7e052ec 100644 --- a/CodeWalker.Core/GameFiles/GameFileCache.cs +++ b/CodeWalker.Core/GameFiles/GameFileCache.cs @@ -2709,10 +2709,10 @@ namespace CodeWalker.GameFiles } sbh.AppendLine(); } - if (rel.Unk05Hashes != null) + if (rel.WaveTracksHashes != null) { - sbh.AppendLine(rfe.Path + ": " + rel.Unk05Hashes.Length.ToString() + " Hashes1:"); - foreach (var unk in rel.Unk05Hashes) + sbh.AppendLine(rfe.Path + ": " + rel.WaveTracksHashes.Length.ToString() + " Hashes1:"); + foreach (var unk in rel.WaveTracksHashes) { sbh.Append(unk.Hash.ToString("X8")); string strval; @@ -2725,10 +2725,10 @@ namespace CodeWalker.GameFiles } sbh.AppendLine(); } - if (rel.ContainerHashes != null) + if (rel.WaveContainersHashes != null) { - sbh.AppendLine(rfe.Path + ": " + rel.ContainerHashes.Length.ToString() + " Hashes2:"); - foreach (var unk in rel.ContainerHashes) + sbh.AppendLine(rfe.Path + ": " + rel.WaveContainersHashes.Length.ToString() + " Hashes2:"); + foreach (var unk in rel.WaveContainersHashes) { sbh.Append(unk.Hash.ToString("X8")); string strval; diff --git a/Forms/RelForm.cs b/Forms/RelForm.cs index 15eb008..7947ba4 100644 --- a/Forms/RelForm.cs +++ b/Forms/RelForm.cs @@ -48,7 +48,7 @@ namespace CodeWalker.Forms fileName = rel?.Name; if (string.IsNullOrEmpty(fileName)) { - fileName = rel?.FileEntry?.Name; + fileName = rel?.RpfFileEntry?.Name; } UpdateFormTitle(); diff --git a/Project/Panels/ProjectExplorerPanel.cs b/Project/Panels/ProjectExplorerPanel.cs index a27344b..f83fc13 100644 --- a/Project/Panels/ProjectExplorerPanel.cs +++ b/Project/Panels/ProjectExplorerPanel.cs @@ -173,6 +173,27 @@ namespace CodeWalker.Project.Panels scenariosnode.Expand(); } + if (CurrentProjectFile.AudioRelFiles.Count > 0) + { + var audiorelsnode = projnode.Nodes.Add("Audio Rel Files"); + audiorelsnode.Name = "AudioRels"; + + foreach (var audiorelfile in CurrentProjectFile.AudioRelFiles) + { + var acstr = audiorelfile.HasChanged ? "*" : ""; + string name = audiorelfile.Name; + if (audiorelfile.RpfFileEntry != null) + { + name = audiorelfile.RpfFileEntry.Name; + } + var audiorelnode = audiorelsnode.Nodes.Add(acstr + name); + audiorelnode.Tag = audiorelfile; + + LoadAudioRelTreeNodes(audiorelfile, audiorelnode); + } + audiorelsnode.Expand(); + } + projnode.Expand(); } @@ -377,6 +398,69 @@ namespace CodeWalker.Project.Panels //} } + private void LoadAudioRelTreeNodes(RelFile rel, TreeNode node) + { + if (!string.IsNullOrEmpty(node.Name)) return; //named nodes are eg Zones, Emitters + + node.Nodes.Clear(); + + + TreeNode n; + n = node.Nodes.Add("Edit Zone"); + n.Name = "EditZone"; + n.Tag = rel; //this tag should get updated with the selected zone! + + n = node.Nodes.Add("Edit Emitter"); + n.Name = "EditEmitter"; + n.Tag = rel; //this tag should get updated with the selected emitter! + + + + var zonelists = new List(); + var emitterlists = new List(); + + foreach (var reldata in rel.RelDatas) + { + if (reldata is Dat151AmbientZoneList) + { + zonelists.Add(reldata as Dat151AmbientZoneList); + } + if (reldata is Dat151AmbientEmitterList) + { + emitterlists.Add(reldata as Dat151AmbientEmitterList); + } + } + + + + if (zonelists.Count > 0) + { + var zonelistsnode = node.Nodes.Add("Zone Lists (" + zonelists.Count.ToString() + ")"); + zonelistsnode.Name = "ZoneLists"; + zonelistsnode.Tag = rel; + for (int i = 0; i < zonelists.Count; i++) + { + var zonelist = zonelists[i]; + var tnode = zonelistsnode.Nodes.Add(zonelist.ToString()); + tnode.Tag = zonelist; + } + } + + if (emitterlists.Count > 0) + { + var emitterlistsnode = node.Nodes.Add("Emitter Lists (" + emitterlists.Count.ToString() + ")"); + emitterlistsnode.Name = "EmitterLists"; + emitterlistsnode.Tag = rel; + for (int i = 0; i < emitterlists.Count; i++) + { + var emitterlist = emitterlists[i]; + var tnode = emitterlistsnode.Nodes.Add(emitterlist.ToString()); + tnode.Tag = emitterlist; + } + } + + + } @@ -533,6 +617,30 @@ namespace CodeWalker.Project.Panels } } } + public void SetAudioRelHasChanged(RelFile rel, bool changed) + { + if (ProjectTreeView.Nodes.Count > 0) + { + var pnode = ProjectTreeView.Nodes[0]; + var acnode = GetChildTreeNode(pnode, "AudioRels"); + if (acnode == null) return; + string changestr = changed ? "*" : ""; + for (int i = 0; i < acnode.Nodes.Count; i++) + { + var anode = acnode.Nodes[i]; + if (anode.Tag == rel) + { + string name = rel.Name; + if (rel.RpfFileEntry != null) + { + name = rel.RpfFileEntry.Name; + } + anode.Text = changestr + name; + break; + } + } + } + } @@ -726,6 +834,63 @@ namespace CodeWalker.Project.Panels } return null; } + public TreeNode FindAudioRelTreeNode(RelFile rel) + { + if (ProjectTreeView.Nodes.Count <= 0) return null; + var projnode = ProjectTreeView.Nodes[0]; + var scenariosnode = GetChildTreeNode(projnode, "AudioRels"); + if (scenariosnode == null) return null; + for (int i = 0; i < scenariosnode.Nodes.Count; i++) + { + var ymtnode = scenariosnode.Nodes[i]; + if (ymtnode.Tag == rel) return ymtnode; + } + return null; + } + public TreeNode FindAudioZoneTreeNode(AudioPlacement zone) + { + if (zone == null) return null; + TreeNode relnode = FindAudioRelTreeNode(zone.RelFile); + var zonenode = GetChildTreeNode(relnode, "EditZone"); + if (zonenode == null) return null; + zonenode.Tag = zone; + return zonenode; + } + public TreeNode FindAudioEmitterTreeNode(AudioPlacement emitter) + { + if (emitter == null) return null; + TreeNode relnode = FindAudioRelTreeNode(emitter.RelFile); + var zonenode = GetChildTreeNode(relnode, "EditEmitter"); + if (zonenode == null) return null; + zonenode.Tag = emitter; + return zonenode; + } + public TreeNode FindAudioZoneListTreeNode(Dat151AmbientZoneList list) + { + if (list == null) return null; + TreeNode relnode = FindAudioRelTreeNode(list.Rel); + var zonelistsnode = GetChildTreeNode(relnode, "ZoneLists"); + if (zonelistsnode == null) return null; + for (int i = 0; i < zonelistsnode.Nodes.Count; i++) + { + TreeNode lnode = zonelistsnode.Nodes[i]; + if (lnode.Tag == list) return lnode; + } + return null; + } + public TreeNode FindAudioEmitterListTreeNode(Dat151AmbientEmitterList list) + { + if (list == null) return null; + TreeNode relnode = FindAudioRelTreeNode(list.Rel); + var emitterlistsnode = GetChildTreeNode(relnode, "EmitterLists"); + if (emitterlistsnode == null) return null; + for (int i = 0; i < emitterlistsnode.Nodes.Count; i++) + { + TreeNode enode = emitterlistsnode.Nodes[i]; + if (enode.Tag == list) return enode; + } + return null; + } @@ -894,6 +1059,97 @@ namespace CodeWalker.Project.Panels } } } + public void TrySelectAudioRelTreeNode(RelFile rel) + { + TreeNode tnode = FindAudioRelTreeNode(rel); + if (tnode != null) + { + if (ProjectTreeView.SelectedNode == tnode) + { + OnItemSelected?.Invoke(rel); + } + else + { + ProjectTreeView.SelectedNode = tnode; + } + } + } + public void TrySelectAudioZoneTreeNode(AudioPlacement zone) + { + TreeNode tnode = FindAudioZoneTreeNode(zone); + if (tnode == null) + { + tnode = FindAudioRelTreeNode(zone?.RelFile); + } + if (tnode != null) + { + if (ProjectTreeView.SelectedNode == tnode) + { + OnItemSelected?.Invoke(zone); + } + else + { + ProjectTreeView.SelectedNode = tnode; + } + } + } + public void TrySelectAudioEmitterTreeNode(AudioPlacement emitter) + { + TreeNode tnode = FindAudioEmitterTreeNode(emitter); + if (tnode == null) + { + tnode = FindAudioRelTreeNode(emitter?.RelFile); + } + if (tnode != null) + { + if (ProjectTreeView.SelectedNode == tnode) + { + OnItemSelected?.Invoke(emitter); + } + else + { + ProjectTreeView.SelectedNode = tnode; + } + } + } + public void TrySelectAudioZoneListTreeNode(Dat151AmbientZoneList list) + { + TreeNode tnode = FindAudioZoneListTreeNode(list); + if (tnode == null) + { + tnode = FindAudioRelTreeNode(list?.Rel); + } + if (tnode != null) + { + if (ProjectTreeView.SelectedNode == tnode) + { + OnItemSelected?.Invoke(list); + } + else + { + ProjectTreeView.SelectedNode = tnode; + } + } + } + public void TrySelectAudioEmitterListTreeNode(Dat151AmbientEmitterList list) + { + TreeNode tnode = FindAudioEmitterListTreeNode(list); + if (tnode == null) + { + tnode = FindAudioRelTreeNode(list?.Rel); + } + if (tnode != null) + { + if (ProjectTreeView.SelectedNode == tnode) + { + OnItemSelected?.Invoke(list); + } + else + { + ProjectTreeView.SelectedNode = tnode; + } + } + } public void UpdateCarGenTreeNode(YmapCarGen cargen) { @@ -916,7 +1172,6 @@ namespace CodeWalker.Project.Panels var tn = FindNavPolyTreeNode(poly); if (tn != null) { - tn.Text = poly._RawData.ToString(); } } public void UpdateTrainNodeTreeNode(TrainTrackNode node) @@ -935,6 +1190,37 @@ namespace CodeWalker.Project.Panels tn.Text = node.MedTypeName + ": " + node.StringText; } } + public void UpdateAudioZoneTreeNode(AudioPlacement zone) + { + var tn = FindAudioZoneTreeNode(zone); + if (tn != null) + { + } + } + public void UpdateAudioEmitterTreeNode(AudioPlacement emitter) + { + var tn = FindAudioEmitterTreeNode(emitter); + if (tn != null) + { + } + } + public void UpdateAudioZoneListTreeNode(Dat151AmbientZoneList list) + { + var tn = FindAudioZoneListTreeNode(list); + if (tn != null) + { + tn.Text = list.ToString(); + } + } + public void UpdateAudioEmitterListTreeNode(Dat151AmbientEmitterList list) + { + var tn = FindAudioEmitterListTreeNode(list); + if (tn != null) + { + tn.Text = list.ToString(); + } + } + public void RemoveEntityTreeNode(YmapEntityDef ent) { @@ -981,6 +1267,42 @@ namespace CodeWalker.Project.Panels tn.Parent.Nodes.Remove(tn); } } + public void RemoveAudioZoneListTreeNode(Dat151AmbientZoneList list) + { + var tn = FindAudioZoneListTreeNode(list); + if ((tn != null) && (tn.Parent != null)) + { + var zonelists = new List(); + foreach (var reldata in list.Rel.RelDatas) + { + if (reldata is Dat151AmbientZoneList) + { + zonelists.Add(reldata as Dat151AmbientZoneList); + } + } + + tn.Parent.Text = "Zone Lists (" + zonelists.Count.ToString() + ")"; + tn.Parent.Nodes.Remove(tn); + } + } + public void RemoveAudioEmitterListTreeNode(Dat151AmbientEmitterList list) + { + var tn = FindAudioEmitterListTreeNode(list); + if ((tn != null) && (tn.Parent != null)) + { + var emitterlists = new List(); + foreach (var reldata in list.Rel.RelDatas) + { + if (reldata is Dat151AmbientEmitterList) + { + emitterlists.Add(reldata as Dat151AmbientEmitterList); + } + } + + tn.Parent.Text = "Emitter Lists (" + emitterlists.Count.ToString() + ")"; + tn.Parent.Nodes.Remove(tn); + } + } diff --git a/Project/ProjectFile.cs b/Project/ProjectFile.cs index daf7850..af5451e 100644 --- a/Project/ProjectFile.cs +++ b/Project/ProjectFile.cs @@ -21,6 +21,7 @@ namespace CodeWalker.Project public List YnvFilenames { get; set; } = new List(); public List TrainsFilenames { get; set; } = new List(); public List ScenarioFilenames { get; set; } = new List(); + public List AudioRelFilenames { get; set; } = new List(); //fields not stored public string Filename { get; set; } //filename without path @@ -33,6 +34,7 @@ namespace CodeWalker.Project public List YnvFiles { get; set; } = new List(); public List TrainsFiles { get; set; } = new List(); public List ScenarioFiles { get; set; } = new List(); + public List AudioRelFiles { get; set; } = new List(); @@ -81,6 +83,12 @@ namespace CodeWalker.Project Xml.AddChildWithInnerText(doc, scenarioselem, "Item", scenariofilename); } + var audiorelselem = Xml.AddChild(doc, projelem, "AudioRelFilenames"); + foreach (string audiorelfilename in AudioRelFilenames) + { + Xml.AddChildWithInnerText(doc, audiorelselem, "Item", audiorelfilename); + } + doc.Save(Filepath); } @@ -196,6 +204,22 @@ namespace CodeWalker.Project } + + AudioRelFilenames.Clear(); + AudioRelFiles.Clear(); + var audiorelselem = Xml.GetChild(projelem, "AudioRelFilenames"); + if (audiorelselem != null) + { + foreach (var node in audiorelselem.SelectNodes("Item")) + { + XmlElement audiorelel = node as XmlElement; + if (audiorelel != null) + { + AddAudioRelFile(audiorelel.InnerText); + } + } + } + } @@ -225,6 +249,10 @@ namespace CodeWalker.Project { ScenarioFilenames[i] = GetUpdatedFilePath(ScenarioFilenames[i], oldprojpath); } + for (int i = 0; i < AudioRelFilenames.Count; i++) + { + AudioRelFilenames[i] = GetUpdatedFilePath(AudioRelFilenames[i], oldprojpath); + } } public string GetUpdatedFilePath(string oldpath, string oldprojpath) @@ -697,5 +725,72 @@ namespace CodeWalker.Project } + public RelFile AddAudioRelFile(string filename) + { + RelFile relfile = new RelFile(); + relfile.RpfFileEntry = new RpfResourceFileEntry(); + relfile.RpfFileEntry.Name = Path.GetFileName(filename); + relfile.FilePath = GetFullFilePath(filename); + relfile.Name = relfile.RpfFileEntry.Name; + if (!AddAudioRelFile(relfile)) return null; + return relfile; + } + public bool AddAudioRelFile(RelFile rel) + { + string relpath = GetRelativePath(rel.FilePath); + if (string.IsNullOrEmpty(relpath)) relpath = rel.Name; + if (AudioRelFilenames.Contains(relpath)) return false; + AudioRelFilenames.Add(relpath); + AudioRelFiles.Add(rel); + return true; + } + public void RemoveAudioRelFile(RelFile rel) + { + if (rel == null) return; + var relpath = GetRelativePath(rel.FilePath); + if (string.IsNullOrEmpty(relpath)) relpath = rel.Name; + AudioRelFiles.Remove(rel); + AudioRelFilenames.Remove(relpath); + HasChanged = true; + } + public bool ContainsAudioRel(string filename) + { + bool found = false; + filename = filename.ToLowerInvariant(); + foreach (var audiorelfn in AudioRelFilenames) + { + if (audiorelfn == filename) + { + found = true; + break; + } + } + return found; + } + public bool ContainsAudioRel(RelFile rel) + { + foreach (var f in AudioRelFiles) + { + if (f == rel) return true; + } + return false; + } + public bool RenameAudioRel(string oldfilename, string newfilename) + { + oldfilename = oldfilename.ToLowerInvariant(); + newfilename = newfilename.ToLowerInvariant(); + for (int i = 0; i < AudioRelFilenames.Count; i++) + { + if (AudioRelFilenames[i] == oldfilename) + { + AudioRelFilenames[i] = newfilename; + HasChanged = true; + return true; + } + } + return false; + } + + } } diff --git a/Project/ProjectForm.cs b/Project/ProjectForm.cs index 845b3dc..e88895a 100644 --- a/Project/ProjectForm.cs +++ b/Project/ProjectForm.cs @@ -60,6 +60,12 @@ namespace CodeWalker.Project private ScenarioNode CurrentScenarioNode; private MCScenarioChainingEdge CurrentScenarioChainEdge; + private RelFile CurrentAudioFile; + private AudioPlacement CurrentAudioZone; + private AudioPlacement CurrentAudioEmitter; + private Dat151AmbientZoneList CurrentAudioZoneList; + private Dat151AmbientEmitterList CurrentAudioEmitterList; + private bool renderitems = true; private bool hidegtavmap = false; @@ -417,6 +423,21 @@ namespace CodeWalker.Project (panel) => { panel.SetScenarioNode(CurrentScenarioNode); }, //updateFunc (panel) => { return panel.CurrentScenarioNode == CurrentScenarioNode; }); //findFunc } + private void ShowEditAudioFilePanel(bool promote) //TODO + { + } + private void ShowEditAudioZonePanel(bool promote) //TODO + { + } + private void ShowEditAudioEmitterPanel(bool promote) //TODO + { + } + private void ShowEditAudioZoneListPanel(bool promote) //TODO + { + } + private void ShowEditAudioEmitterListPanel(bool promote) //TODO + { + } private void ShowCurrentProjectItem(bool promote) { @@ -480,6 +501,27 @@ namespace CodeWalker.Project { ShowEditScenarioYmtPanel(promote); } + if (CurrentAudioZone != null) + { + ShowEditAudioZonePanel(promote); + } + else if (CurrentAudioEmitter != null) + { + ShowEditAudioEmitterPanel(promote); + } + else if (CurrentAudioZoneList != null) + { + ShowEditAudioZoneListPanel(promote); + } + else if (CurrentAudioEmitterList != null) + { + ShowEditAudioEmitterListPanel(promote); + } + else if (CurrentAudioFile != null) + { + ShowEditAudioFilePanel(promote); + } + } public void ShowProjectItem(object item, bool promote) { @@ -516,6 +558,11 @@ namespace CodeWalker.Project CurrentScenario = item as YmtFile; CurrentScenarioNode = item as ScenarioNode; CurrentScenarioChainEdge = item as MCScenarioChainingEdge; + CurrentAudioFile = item as RelFile; + CurrentAudioZone = item as AudioPlacement; if (CurrentAudioZone?.AudioZone == null) CurrentAudioZone = null; + CurrentAudioEmitter = item as AudioPlacement; if (CurrentAudioEmitter?.AudioEmitter == null) CurrentAudioEmitter = null; + CurrentAudioZoneList = item as Dat151AmbientZoneList; + CurrentAudioEmitterList = item as Dat151AmbientEmitterList; if (CurrentEntity != null) { @@ -561,6 +608,22 @@ namespace CodeWalker.Project { CurrentScenario = CurrentScenarioChainEdge.Region?.Ymt; } + if (CurrentAudioZone != null) + { + CurrentAudioFile = CurrentAudioZone.RelFile; + } + if (CurrentAudioEmitter != null) + { + CurrentAudioFile = CurrentAudioEmitter.RelFile; + } + if (CurrentAudioZoneList != null) + { + CurrentAudioFile = CurrentAudioZoneList.Rel; + } + if (CurrentAudioEmitterList != null) + { + CurrentAudioFile = CurrentAudioEmitterList.Rel; + } RefreshUI(); @@ -1389,7 +1452,11 @@ namespace CodeWalker.Project ccg.flags = 3680; ccg.orientX = 5.0f; ccg.perpendicularLength = 2.6f; - //TODO: set default values for cargen + ccg.bodyColorRemap1 = -1; + ccg.bodyColorRemap2 = -1; + ccg.bodyColorRemap3 = -1; + ccg.bodyColorRemap4 = -1; + ccg.livery = -1; } if (!copyPosition || (copy == null)) @@ -3621,6 +3688,113 @@ namespace CodeWalker.Project + + + + public void NewAudioFile() //TODO + { + } + public void OpenAudioFile() //TODO + { + } + public void SaveAudioFile(bool saveas = false) //TODO + { + } + public void AddAudioFileToProject(RelFile rel) + { + if (rel == null) return; + if (CurrentProjectFile == null) + { + NewProject(); + } + if (AudioFileExistsInProject(rel)) return; + if (CurrentProjectFile.AddAudioRelFile(rel)) + { + rel.HasChanged = true; + CurrentProjectFile.HasChanged = true; + LoadProjectTree(); + } + CurrentAudioFile = rel; + RefreshUI(); + if (CurrentAudioZone != null) + { + ProjectExplorer?.TrySelectAudioZoneTreeNode(CurrentAudioZone); + } + else if (CurrentAudioEmitter != null) + { + ProjectExplorer?.TrySelectAudioEmitterTreeNode(CurrentAudioEmitter); + } + } + public void RemoveAudioFileFromProject() + { + if (CurrentAudioFile == null) return; + if (CurrentProjectFile == null) return; + CurrentProjectFile.RemoveAudioRelFile(CurrentAudioFile); + CurrentAudioFile = null; + LoadProjectTree(); + RefreshUI(); + } + public bool AudioFileExistsInProject(RelFile rel) + { + if (rel == null) return false; + if (CurrentProjectFile == null) return false; + return CurrentProjectFile.ContainsAudioRel(rel); + } + + public void NewAudioZone(AudioPlacement copy = null, bool copyPosition = false) //TODO + { + } + public bool DeleteAudioZone() //TODO + { + return false; + } + public bool IsCurrentAudioZone(AudioPlacement zone) + { + return zone == CurrentAudioZone; + } + + public void NewAudioEmitter(AudioPlacement copy = null, bool copyPosition = false) //TODO + { + } + public bool DeleteAudioEmitter() //TODO + { + return false; + } + public bool IsCurrentAudioEmitter(AudioPlacement emitter) + { + return emitter == CurrentAudioEmitter; + } + + public void NewAudioZoneList() //TODO + { + } + public bool DeleteAudioZoneList() //TODO + { + return false; + } + public bool IsCurrentAudioZoneList(Dat151AmbientZoneList list) + { + return list == CurrentAudioZoneList; + } + + public void NewAudioEmitterList() //TODO + { + } + public bool DeleteAudioEmitterList() //TODO + { + return false; + } + public bool IsCurrentAudioEmitterList(Dat151AmbientEmitterList list) + { + return list == CurrentAudioEmitterList; + } + + + + + + + public void GetVisibleYmaps(Camera camera, Dictionary ymaps) { if (hidegtavmap) @@ -3822,11 +3996,13 @@ namespace CodeWalker.Project var trainnode = sel.TrainTrackNode; var scenariond = sel.ScenarioNode; var scenarioedge = sel.ScenarioEdge; + var audiopl = sel.Audio; YmapFile ymap = ent?.Ymap ?? cargen?.Ymap ?? grassbatch?.Ymap; YndFile ynd = pathnode?.Ynd; YnvFile ynv = navpoly?.Ynv ?? navpoint?.Ynv ?? navportal?.Ynv; TrainTrack traintrack = trainnode?.Track; YmtFile scenario = scenariond?.Ymt ?? scenarioedge?.Region?.Ymt; + RelFile audiofile = audiopl?.RelFile; bool showcurrent = false; if (YmapExistsInProject(ymap)) @@ -3876,6 +4052,17 @@ namespace CodeWalker.Project ProjectExplorer?.TrySelectScenarioNodeTreeNode(scenariond); } } + else if (AudioFileExistsInProject(audiofile)) + { + if ((audiopl?.AudioZone != null) && (audiopl != CurrentAudioZone)) + { + ProjectExplorer?.TrySelectAudioZoneTreeNode(audiopl); + } + if ((audiopl?.AudioEmitter != null) && (audiopl != CurrentAudioEmitter)) + { + ProjectExplorer?.TrySelectAudioEmitterTreeNode(audiopl); + } + } else { ProjectExplorer?.DeselectNode(); @@ -3900,6 +4087,11 @@ namespace CodeWalker.Project CurrentScenario = scenario; CurrentScenarioNode = scenariond; CurrentScenarioChainEdge = scenarioedge; + CurrentAudioFile = audiofile; + CurrentAudioZone = (audiopl?.AudioZone != null) ? audiopl : null; + CurrentAudioEmitter = (audiopl?.AudioEmitter != null) ? audiopl : null; + CurrentAudioZoneList = null; + CurrentAudioEmitterList = null; RefreshUI(); if (showcurrent) { @@ -3949,6 +4141,10 @@ namespace CodeWalker.Project { OnWorldScenarioNodeModified(sel.ScenarioNode); } + else if (sel.Audio != null) + { + OnWorldAudioPlacementModified(sel.Audio); + } } private void OnWorldEntityModified(YmapEntityDef ent) @@ -4321,26 +4517,76 @@ namespace CodeWalker.Project } catch { } } - - - - - - - public Vector3 GetSpawnPos(float dist) + private void OnWorldAudioPlacementModified(AudioPlacement audio) { - Vector3 pos = Vector3.Zero; - if (WorldForm != null) + try { - Vector3 campos = WorldForm.GetCameraPosition(); - Vector3 camdir = WorldForm.GetCameraViewDir(); - pos = campos + camdir * dist; + if (InvokeRequired) + { + BeginInvoke(new Action(() => { OnWorldAudioPlacementModified(audio); })); + } + else + { + if (audio?.RelFile == null) return; + + if (CurrentProjectFile == null) + { + NewProject(); + } + + if (!AudioFileExistsInProject(audio.RelFile)) + { + audio.RelFile.HasChanged = true; + AddAudioFileToProject(audio.RelFile); + if (audio.AudioZone != null) + { + ProjectExplorer?.TrySelectAudioZoneTreeNode(audio); + } + if (audio.AudioEmitter != null) + { + ProjectExplorer?.TrySelectAudioEmitterTreeNode(audio); + } + } + + if ((audio.AudioZone != null) && (audio != CurrentAudioZone)) + { + CurrentAudioZone = audio; + ProjectExplorer?.TrySelectAudioZoneTreeNode(audio); + } + if ((audio.AudioEmitter != null) && (audio != CurrentAudioEmitter)) + { + CurrentAudioEmitter = audio; + ProjectExplorer?.TrySelectAudioEmitterTreeNode(audio); + } + if (audio == CurrentAudioZone) + { + ShowEditAudioZonePanel(false); + if (audio.RelFile != null) + { + SetAudioFileHasChanged(true); + } + } + else if (audio == CurrentAudioEmitter) + { + ShowEditAudioEmitterPanel(false); + if (audio.RelFile != null) + { + SetAudioFileHasChanged(true); + } + } + + } } - return pos; + catch { } } + + + + + public void SetProjectHasChanged(bool changed) { if (CurrentProjectFile == null) return; @@ -4429,7 +4675,38 @@ namespace CodeWalker.Project PromoteIfPreviewPanelActive(); } + public void SetAudioFileHasChanged(bool changed) + { + if (CurrentAudioFile == null) return; + bool changechange = changed != CurrentAudioFile.HasChanged; + if (!changechange) return; + + CurrentAudioFile.HasChanged = changed; + + ProjectExplorer?.SetAudioRelHasChanged(CurrentAudioFile, changed); + + PromoteIfPreviewPanelActive(); + } + + + + + + + + + public Vector3 GetSpawnPos(float dist) + { + Vector3 pos = Vector3.Zero; + if (WorldForm != null) + { + Vector3 campos = WorldForm.GetCameraPosition(); + Vector3 camdir = WorldForm.GetCameraViewDir(); + pos = campos + camdir * dist; + } + return pos; + } public RpfFileEntry FindParentYmapEntry(uint hash) { @@ -4458,8 +4735,6 @@ namespace CodeWalker.Project - - //######## Private methods private void LoadYmapFromFile(YmapFile ymap, string filename) @@ -4522,6 +4797,9 @@ namespace CodeWalker.Project ymt.Load(data); } + private void LoadAudioRelFromFile(RelFile rel, string filename) //TODO + { + } @@ -4567,6 +4845,7 @@ namespace CodeWalker.Project RefreshYnvUI(); RefreshTrainTrackUI(); RefreshScenarioUI(); + RefreshAudioUI(); SetCurrentSaveItem(); //ShowEditYmapPanel(false); //ShowEditYmapEntityPanel(false); @@ -4762,6 +5041,9 @@ namespace CodeWalker.Project WorldForm.EnableScenarioUI(enable, CurrentScenario?.Name ?? ""); } } + private void RefreshAudioUI() //TODO + { + } private void SetCurrentSaveItem() @@ -4791,6 +5073,10 @@ namespace CodeWalker.Project { filename = CurrentScenario.RpfFileEntry?.Name; } + else if (CurrentAudioFile != null) + { + filename = CurrentAudioFile.RpfFileEntry?.Name; + } bool enable = !string.IsNullOrEmpty(filename); diff --git a/Todo.txt b/Todo.txt index d989c8c..20139e1 100644 --- a/Todo.txt +++ b/Todo.txt @@ -56,7 +56,6 @@ load/convert/save map editor / menyoo xml files, fiveM json files, etc... ---[Rendering stuff]--- -model view - improve .ynv rendering (portals etc) model view - YPT particles - animate particles? do more work on format? mapDataGroups - how to isolate one of the grouped ymaps? eg destruction etc. diff --git a/WorldForm.cs b/WorldForm.cs index ed39dc4..6219a66 100644 --- a/WorldForm.cs +++ b/WorldForm.cs @@ -183,6 +183,7 @@ namespace CodeWalker YnvPortal CopiedNavPortal = null; TrainTrackNode CopiedTrainNode = null; ScenarioNode CopiedScenarioNode = null; + AudioPlacement CopiedAudio = null; public bool EditEntityPivot { get; set; } = false; @@ -3611,6 +3612,7 @@ namespace CodeWalker YndFile ynd = null; TrainTrack traintr = null; YmtFile scenario = null; + RelFile audiofile = null; ToolbarCopyButton.Enabled = false; ToolbarDeleteItemButton.Enabled = false; ToolbarDeleteItemButton.Text = "Delete"; @@ -3707,6 +3709,7 @@ namespace CodeWalker { SelectionEntityTabPage.Text = item.Audio.ShortTypeName; SelEntityPropertyGrid.SelectedObject = item.Audio; + audiofile = item.Audio.RelFile; } else { @@ -3780,6 +3783,10 @@ namespace CodeWalker { EnableScenarioUI(true, scenario.Name); } + if (audiofile != null) + { + EnableAudioUI(true, audiofile.Name); + } } private void ShowSelectedExtensionTab(bool show, string text = "Ext") @@ -4877,6 +4884,10 @@ namespace CodeWalker ToolbarPasteButton.Enabled = (CopiedScenarioNode != null) && enable; } } + public void EnableAudioUI(bool enable, string filename) //TODO + { + + } private void New() @@ -4922,6 +4933,11 @@ namespace CodeWalker ShowProjectForm(); ProjectForm.NewScenario(); } + private void NewAudioRel() + { + ShowProjectForm(); + ProjectForm.NewAudioFile(); + } private void Open() { ShowProjectForm(); @@ -4965,6 +4981,11 @@ namespace CodeWalker ShowProjectForm(); ProjectForm.OpenScenario(); } + private void OpenAudioRel() + { + ShowProjectForm(); + ProjectForm.OpenAudioFile(); + } private void Save() { if (ProjectForm == null) return; @@ -4987,6 +5008,7 @@ namespace CodeWalker case MapSelectionMode.NavMesh: AddNavPoly(); break;//how to add points/portals? project window case MapSelectionMode.TrainTrack: AddTrainNode(); break; case MapSelectionMode.Scenario: AddScenarioNode(); break; //how to add different node types? project window + case MapSelectionMode.Audio: AddAudioZone(); break; //how to add emitters as well? project window } } private void DeleteItem() @@ -4999,6 +5021,8 @@ namespace CodeWalker else if (SelectedItem.NavPortal != null) DeleteNavPortal(); else if (SelectedItem.TrainTrackNode != null) DeleteTrainNode(); else if (SelectedItem.ScenarioNode != null) DeleteScenarioNode(); + else if (SelectedItem.Audio?.AudioZone != null) DeleteAudioZone(); + else if (SelectedItem.Audio?.AudioEmitter != null) DeleteAudioEmitter(); } private void CopyItem() { @@ -5010,6 +5034,8 @@ namespace CodeWalker else if (SelectedItem.NavPortal != null) CopyNavPortal(); else if (SelectedItem.TrainTrackNode != null) CopyTrainNode(); else if (SelectedItem.ScenarioNode != null) CopyScenarioNode(); + else if (SelectedItem.Audio?.AudioZone != null) CopyAudioZone(); + else if (SelectedItem.Audio?.AudioEmitter != null) CopyAudioEmitter(); } private void PasteItem() { @@ -5021,6 +5047,8 @@ namespace CodeWalker else if (CopiedNavPortal != null) PasteNavPortal(); else if (CopiedTrainNode != null) PasteTrainNode(); else if (CopiedScenarioNode != null) PasteScenarioNode(); + else if (CopiedAudio?.AudioZone != null) PasteAudioZone(); + else if (CopiedAudio?.AudioEmitter != null) PasteAudioEmitter(); } private void CloneItem() { @@ -5032,6 +5060,8 @@ namespace CodeWalker else if (SelectedItem.NavPortal != null) CloneNavPortal(); else if (SelectedItem.TrainTrackNode != null) CloneTrainNode(); else if (SelectedItem.ScenarioNode != null) CloneScenarioNode(); + else if (SelectedItem.Audio?.AudioZone != null) CloneAudioZone(); + else if (SelectedItem.Audio?.AudioEmitter != null) CloneAudioEmitter(); } private void AddEntity() @@ -5468,6 +5498,113 @@ namespace CodeWalker ProjectForm.NewScenarioNode(SelectedItem.ScenarioNode, true); } + private void AddAudioZone() + { + if (ProjectForm == null) return; + ProjectForm.NewAudioZone(); + } + private void DeleteAudioZone() + { + var audio = SelectedItem.Audio; + if (audio == null) return; + + if ((ProjectForm != null) && (ProjectForm.IsCurrentAudioZone(audio))) + { + if (!ProjectForm.DeleteAudioZone()) + { + //MessageBox.Show("Unable to delete this audio zone from the current project. Make sure the zone's .rel file exists in the current project."); + } + else + { + SelectItem(null); + } + } + else + { + //project not open, or zone not selected there, just remove the zone from the rel... + var rel = audio.RelFile; + if (!rel.RemoveRelData(audio.AudioZone)) + { + MessageBox.Show("Unable to remove audio zone. Audio zone editing TODO!"); + } + else + { + SelectItem(null); + } + } + } + private void CopyAudioZone() + { + CopiedAudio = SelectedItem.Audio; + ToolbarPasteButton.Enabled = (CopiedAudio != null) && ToolbarAddItemButton.Enabled; + } + private void PasteAudioZone() + { + if (CopiedAudio == null) return; + if (ProjectForm == null) return; + ProjectForm.NewAudioZone(CopiedAudio); + } + private void CloneAudioZone() + { + if (SelectedItem.Audio == null) return; + if (ProjectForm == null) return; + ProjectForm.NewAudioZone(SelectedItem.Audio, true); + } + + private void AddAudioEmitter() + { + if (ProjectForm == null) return; + ProjectForm.NewAudioEmitter(); + } + private void DeleteAudioEmitter() + { + var audio = SelectedItem.Audio; + if (audio == null) return; + + if ((ProjectForm != null) && (ProjectForm.IsCurrentAudioEmitter(audio))) + { + if (!ProjectForm.DeleteAudioEmitter()) + { + //MessageBox.Show("Unable to delete this audio emitter from the current project. Make sure the emitter's .rel file exists in the current project."); + } + else + { + SelectItem(null); + } + } + else + { + //project not open, or zone not selected there, just remove the zone from the rel... + var rel = audio.RelFile; + if (!rel.RemoveRelData(audio.AudioEmitter)) + { + MessageBox.Show("Unable to remove audio emitter. Audio zone editing TODO!"); + } + else + { + SelectItem(null); + } + } + } + private void CopyAudioEmitter() + { + CopiedAudio = SelectedItem.Audio; + ToolbarPasteButton.Enabled = (CopiedAudio != null) && ToolbarAddItemButton.Enabled; + } + private void PasteAudioEmitter() + { + if (CopiedAudio == null) return; + if (ProjectForm == null) return; + ProjectForm.NewAudioEmitter(CopiedAudio); + } + private void CloneAudioEmitter() + { + if (SelectedItem.Audio == null) return; + if (ProjectForm == null) return; + ProjectForm.NewAudioEmitter(SelectedItem.Audio, true); + } + + private void SetMouseSelect(bool enable) {