From 5d812ed92721fbb7f22d8c470e8e7d69cbb332e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arif=20Sezer=20AKTA=C5=9E?= Date: Sun, 30 Jan 2022 17:05:46 +0300 Subject: [PATCH 1/4] Add duplicate light button to model light form --- CodeWalker/Forms/ModelLightForm.Designer.cs | 38 +++++-- CodeWalker/Forms/ModelLightForm.cs | 114 +++++++++++++++++++- 2 files changed, 145 insertions(+), 7 deletions(-) diff --git a/CodeWalker/Forms/ModelLightForm.Designer.cs b/CodeWalker/Forms/ModelLightForm.Designer.cs index 4822ec1..4562eea 100644 --- a/CodeWalker/Forms/ModelLightForm.Designer.cs +++ b/CodeWalker/Forms/ModelLightForm.Designer.cs @@ -30,6 +30,7 @@ { System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ModelLightForm)); this.MainSplitContainer = new System.Windows.Forms.SplitContainer(); + this.DuplicateLightButton = new System.Windows.Forms.Button(); this.DeleteLightButton = new System.Windows.Forms.Button(); this.NewLightButton = new System.Windows.Forms.Button(); this.LightsTreeView = new CodeWalker.WinForms.TreeViewFix(); @@ -113,6 +114,7 @@ this.EditMenu = new System.Windows.Forms.ToolStripMenuItem(); this.EditNewLightMenu = new System.Windows.Forms.ToolStripMenuItem(); this.EditDeleteLightMenu = new System.Windows.Forms.ToolStripMenuItem(); + this.EditDuplicateLightMenu = new System.Windows.Forms.ToolStripMenuItem(); this.OptionsMenu = new System.Windows.Forms.ToolStripMenuItem(); this.OptionsShowOutlinesMenu = new System.Windows.Forms.ToolStripMenuItem(); this.MoveMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -148,6 +150,7 @@ // // MainSplitContainer.Panel1 // + this.MainSplitContainer.Panel1.Controls.Add(this.DuplicateLightButton); this.MainSplitContainer.Panel1.Controls.Add(this.DeleteLightButton); this.MainSplitContainer.Panel1.Controls.Add(this.NewLightButton); this.MainSplitContainer.Panel1.Controls.Add(this.LightsTreeView); @@ -155,10 +158,22 @@ // MainSplitContainer.Panel2 // this.MainSplitContainer.Panel2.Controls.Add(this.LightPropertiesPanel); - this.MainSplitContainer.Size = new System.Drawing.Size(733, 523); + this.MainSplitContainer.Panel2.Paint += new System.Windows.Forms.PaintEventHandler(this.MainSplitContainer_Panel2_Paint); + this.MainSplitContainer.Size = new System.Drawing.Size(733, 561); this.MainSplitContainer.SplitterDistance = 184; this.MainSplitContainer.TabIndex = 0; // + // DuplicateLightButton + // + this.DuplicateLightButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.DuplicateLightButton.Location = new System.Drawing.Point(7, 526); + this.DuplicateLightButton.Name = "DuplicateLightButton"; + this.DuplicateLightButton.Size = new System.Drawing.Size(169, 23); + this.DuplicateLightButton.TabIndex = 8; + this.DuplicateLightButton.Text = "Duplicate Light"; + this.DuplicateLightButton.UseVisualStyleBackColor = true; + this.DuplicateLightButton.Click += new System.EventHandler(this.DuplicateLightButton_Click); + // // DeleteLightButton // this.DeleteLightButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); @@ -279,7 +294,7 @@ this.LightPropertiesPanel.Controls.Add(this.label31); this.LightPropertiesPanel.Location = new System.Drawing.Point(4, 3); this.LightPropertiesPanel.Name = "LightPropertiesPanel"; - this.LightPropertiesPanel.Size = new System.Drawing.Size(538, 517); + this.LightPropertiesPanel.Size = new System.Drawing.Size(529, 517); this.LightPropertiesPanel.TabIndex = 1; // // VolumetricFadeDistanceUpDown @@ -1127,7 +1142,8 @@ // this.EditMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.EditNewLightMenu, - this.EditDeleteLightMenu}); + this.EditDeleteLightMenu, + this.EditDuplicateLightMenu}); this.EditMenu.Name = "EditMenu"; this.EditMenu.Size = new System.Drawing.Size(39, 20); this.EditMenu.Text = "Edit"; @@ -1135,7 +1151,7 @@ // EditNewLightMenu // this.EditNewLightMenu.Name = "EditNewLightMenu"; - this.EditNewLightMenu.Size = new System.Drawing.Size(180, 22); + this.EditNewLightMenu.Size = new System.Drawing.Size(154, 22); this.EditNewLightMenu.Text = "New Light"; this.EditNewLightMenu.Click += new System.EventHandler(this.EditNewLightMenu_Click); // @@ -1143,10 +1159,18 @@ // this.EditDeleteLightMenu.Enabled = false; this.EditDeleteLightMenu.Name = "EditDeleteLightMenu"; - this.EditDeleteLightMenu.Size = new System.Drawing.Size(180, 22); + this.EditDeleteLightMenu.Size = new System.Drawing.Size(154, 22); this.EditDeleteLightMenu.Text = "Delete Light"; this.EditDeleteLightMenu.Click += new System.EventHandler(this.EditDeleteLightMenu_Click); // + // EditDuplicateLightMenu + // + this.EditDuplicateLightMenu.Enabled = false; + this.EditDuplicateLightMenu.Name = "EditDuplicateLightMenu"; + this.EditDuplicateLightMenu.Size = new System.Drawing.Size(154, 22); + this.EditDuplicateLightMenu.Text = "Duplicate Light"; + this.EditDuplicateLightMenu.Click += new System.EventHandler(this.EditDuplicateLightMenu_Click); + // // OptionsMenu // this.OptionsMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { @@ -1193,7 +1217,7 @@ // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(733, 547); + this.ClientSize = new System.Drawing.Size(733, 585); this.Controls.Add(this.MainSplitContainer); this.Controls.Add(this.MainMenu); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); @@ -1291,6 +1315,7 @@ private System.Windows.Forms.ToolStripMenuItem EditMenu; private System.Windows.Forms.ToolStripMenuItem EditNewLightMenu; private System.Windows.Forms.ToolStripMenuItem EditDeleteLightMenu; + private System.Windows.Forms.ToolStripMenuItem EditDuplicateLightMenu; private System.Windows.Forms.ToolStripMenuItem OptionsMenu; private System.Windows.Forms.ToolStripMenuItem OptionsShowOutlinesMenu; private System.Windows.Forms.ToolStripMenuItem MoveMenuItem; @@ -1319,5 +1344,6 @@ private System.Windows.Forms.NumericUpDown FlashinessUpDown; private System.Windows.Forms.Button DeleteLightButton; private System.Windows.Forms.Button NewLightButton; + private System.Windows.Forms.Button DuplicateLightButton; } } \ No newline at end of file diff --git a/CodeWalker/Forms/ModelLightForm.cs b/CodeWalker/Forms/ModelLightForm.cs index 2e3cf19..be0aa12 100644 --- a/CodeWalker/Forms/ModelLightForm.cs +++ b/CodeWalker/Forms/ModelLightForm.cs @@ -111,6 +111,7 @@ namespace CodeWalker.Forms { DeleteLightButton.Enabled = false; EditDeleteLightMenu.Enabled = false; + EditDuplicateLightMenu.Enabled = false; populatingui = true; PositionTextBox.Text = ""; DirectionTextBox.Text = ""; @@ -154,6 +155,7 @@ namespace CodeWalker.Forms { DeleteLightButton.Enabled = true; EditDeleteLightMenu.Enabled = true; + EditDuplicateLightMenu.Enabled = true; populatingui = true; PositionTextBox.Text = FloatUtil.GetVector3String(light.Position); DirectionTextBox.Text = FloatUtil.GetVector3String(light.Direction); @@ -222,6 +224,57 @@ namespace CodeWalker.Forms light.TimeFlags = 14680191; return light; } + private LightAttributes DuplicateLightAttribute() + { + LightAttributes light = new LightAttributes(); + light.Unknown_0h = selectedLight.Unknown_0h; + light.Unknown_4h = selectedLight.Unknown_4h; + light.Position = selectedLight.Position; + light.Unknown_14h = selectedLight.Unknown_14h; + light.ColorR = selectedLight.ColorR; + light.ColorG = selectedLight.ColorG; + light.ColorB = selectedLight.ColorB; + light.Flashiness = selectedLight.Flashiness; + light.Intensity = selectedLight.Intensity; + light.Flags = selectedLight.Flags; + light.BoneId = selectedLight.BoneId; + light.Type = selectedLight.Type; + light.GroupId = selectedLight.GroupId; + light.TimeFlags = selectedLight.TimeFlags; + light.Falloff = selectedLight.Falloff; + light.FalloffExponent = selectedLight.FalloffExponent; + light.CullingPlaneNormal = selectedLight.CullingPlaneNormal; + light.CullingPlaneOffset = selectedLight.CullingPlaneOffset; + light.ShadowBlur = selectedLight.ShadowBlur; + light.Unknown_45h = selectedLight.Unknown_45h; + light.Unknown_46h = selectedLight.Unknown_46h; + light.VolumeIntensity = selectedLight.VolumeIntensity; + light.VolumeSizeScale = selectedLight.VolumeSizeScale; + light.VolumeOuterColorR = selectedLight.VolumeOuterColorR; + light.VolumeOuterColorG = selectedLight.VolumeOuterColorG; + light.VolumeOuterColorB = selectedLight.VolumeOuterColorB; + light.LightHash = selectedLight.LightHash; + light.VolumeOuterIntensity = selectedLight.VolumeOuterIntensity; + light.CoronaSize = selectedLight.CoronaSize; + light.VolumeOuterExponent = selectedLight.VolumeOuterExponent; + light.LightFadeDistance = selectedLight.LightFadeDistance; + light.ShadowFadeDistance = selectedLight.ShadowFadeDistance; + light.SpecularFadeDistance = selectedLight.SpecularFadeDistance; + light.VolumetricFadeDistance = selectedLight.VolumetricFadeDistance; + light.ShadowNearClip = selectedLight.ShadowNearClip; + light.CoronaIntensity = selectedLight.CoronaIntensity; + light.CoronaZBias = selectedLight.CoronaZBias; + light.Direction = selectedLight.Direction; + light.Tangent = selectedLight.Tangent; + light.ConeInnerAngle = selectedLight.ConeInnerAngle; + light.ConeOuterAngle = selectedLight.ConeOuterAngle; + light.Extent = selectedLight.Extent; + light.ProjectedTextureHash = selectedLight.ProjectedTextureHash; + light.Unknown_A4h = selectedLight.Unknown_A4h; + + return light; + } + private void SelectLight(LightAttributes light) { if (light == null) @@ -318,7 +371,7 @@ namespace CodeWalker.Forms private void DeleteLight() { if (selectedLight == null) return; - if(Drawable != null) + if (Drawable != null) { List lights = Drawable.LightAttributes.data_items.ToList(); lights.Remove(selectedLight); @@ -346,6 +399,50 @@ namespace CodeWalker.Forms LoadModels(DrawableDict); } } + + } + + private void DuplicateLight() + { + if (selectedLight == null) return; + selectedLight = DuplicateLightAttribute(); + if (Drawable != null) + { + if (Drawable.LightAttributes == null) Drawable.LightAttributes = new ResourceSimpleList64(); + List lights = Drawable.LightAttributes.data_items?.ToList() ?? new List(); + lights.Add(selectedLight); + Drawable.LightAttributes.data_items = lights.ToArray(); + UpdateLightParams(); + LoadModel(Drawable); + } + else if (FragDrawable != null) + { + if (FragDrawable.OwnerFragment.LightAttributes == null) FragDrawable.OwnerFragment.LightAttributes = new ResourceSimpleList64(); + List lights = FragDrawable.OwnerFragment.LightAttributes.data_items?.ToList() ?? new List(); + lights.Add(selectedLight); + FragDrawable.OwnerFragment.LightAttributes.data_items = lights.ToArray(); + UpdateLightParams(); + LoadModel(FragDrawable); + } + else + { + var n = LightsTreeView.SelectedNode; + if (n != null) + { + var dr = n.Tag as Drawable; + if (dr == null) { dr = n.Parent.Tag as Drawable; } //try parent node tag also + if (dr != null) + { + if (dr.LightAttributes == null) dr.LightAttributes = new ResourceSimpleList64(); + List lights = dr.LightAttributes.data_items?.ToList() ?? new List(); + lights.Add(selectedLight); + dr.LightAttributes.data_items = lights.ToArray(); + UpdateLightParams(); + LoadModels(DrawableDict); + } + } + } + SelectLightTreeNode(selectedLight); } private void UpdateFlagsCheckBoxes() @@ -933,6 +1030,11 @@ namespace CodeWalker.Forms DeleteLight(); } + private void EditDuplicateLightMenu_Click(object sender, EventArgs e) + { + DuplicateLight(); + } + private void OptionsShowOutlinesMenu_Click(object sender, EventArgs e) { OptionsShowOutlinesMenu.Checked = !OptionsShowOutlinesMenu.Checked; @@ -969,5 +1071,15 @@ namespace CodeWalker.Forms { DeleteLight(); } + + private void DuplicateLightButton_Click(object sender, EventArgs e) + { + DuplicateLight(); + } + + private void MainSplitContainer_Panel2_Paint(object sender, PaintEventArgs e) + { + + } } } From 59b4850371e79cb5594e2e7d4bf38ceb847c5598 Mon Sep 17 00:00:00 2001 From: alexguirre Date: Wed, 23 Feb 2022 23:28:47 +0100 Subject: [PATCH 2/4] Support YPDB files --- .../GameFiles/FileTypes/YpdbFile.cs | 270 ++++++++++++++++++ CodeWalker.Core/GameFiles/GameFile.cs | 1 + CodeWalker.Core/GameFiles/GameFileCache.cs | 43 +++ CodeWalker/ExploreForm.cs | 12 + 4 files changed, 326 insertions(+) create mode 100644 CodeWalker.Core/GameFiles/FileTypes/YpdbFile.cs diff --git a/CodeWalker.Core/GameFiles/FileTypes/YpdbFile.cs b/CodeWalker.Core/GameFiles/FileTypes/YpdbFile.cs new file mode 100644 index 0000000..0405714 --- /dev/null +++ b/CodeWalker.Core/GameFiles/FileTypes/YpdbFile.cs @@ -0,0 +1,270 @@ +using SharpDX; +using System.IO; +using TC = System.ComponentModel.TypeConverterAttribute; +using EXP = System.ComponentModel.ExpandableObjectConverter; + +namespace CodeWalker.GameFiles +{ + public class YpdbFile : GameFile, PackedFile + { + public int SerializerVersion { get; set; } // 2 + public int PoseMatcherVersion { get; set; } // 0 + public uint Signature { get; set; } + public int SamplesCount { get; set; } + public PoseMatcherMatchSample[] Samples { get; set; } + public int BoneTagsCount { get; set; } + public ushort[] BoneTags { get; set; } + public PoseMatcherWeightSet WeightSet { get; set; } + public float Unk7 { get; set; } // 0.033333f + public int Unk8 { get; set; } // 1 + + public YpdbFile() : base(null, GameFileType.Ypdb) + { + } + public YpdbFile(RpfFileEntry entry) : base(entry, GameFileType.Ypdb) + { + } + + public void Load(byte[] data, RpfFileEntry entry) + { + if (entry != null) + { + RpfFileEntry = entry; + Name = entry.Name; + } + + using (MemoryStream ms = new MemoryStream(data)) + { + DataReader r = new DataReader(ms, Endianess.LittleEndian); + + Read(r); + } + + Loaded = true; + + } + + public byte[] Save() + { + MemoryStream s = new MemoryStream(); + DataWriter w = new DataWriter(s); + + Write(w); + + var buf = new byte[s.Length]; + s.Position = 0; + s.Read(buf, 0, buf.Length); + return buf; + } + + private void Read(DataReader r) + { + byte magic = r.ReadByte(); + if (magic != 0x1A) // 0x1A indicates to the game deserializer that it's binary instead of text + { } // no hit + + SerializerVersion = r.ReadInt32(); + PoseMatcherVersion = r.ReadInt32(); + Signature = r.ReadUInt32(); + + SamplesCount = r.ReadInt32(); + if (SamplesCount > 0) + { + Samples = new PoseMatcherMatchSample[SamplesCount]; + + for (int i = 0; i < SamplesCount; i++) + Samples[i] = new PoseMatcherMatchSample(r); + } + + BoneTagsCount = r.ReadInt32(); + if (BoneTagsCount > 0) + { + BoneTags = new ushort[BoneTagsCount]; + + for (int i = 0; i < BoneTagsCount; i++) + BoneTags[i] = r.ReadUInt16(); + } + + WeightSet = new PoseMatcherWeightSet(r); + + Unk7 = r.ReadSingle(); + Unk8 = r.ReadInt32(); + + uint signature2 = r.ReadUInt32(); + + if (SerializerVersion != 2) + { } // no hit + if (PoseMatcherVersion != 0) + { } // no hit + if (BoneTagsCount != WeightSet.WeightsCount) + { } // no hit + if (Unk7 != 0.033333f) + { } // no hit + if (Unk8 != 1) + { } // no hit + if (Signature != signature2) + { } // no hit + + if (r.Position != r.Length) + { } + } + + private void Write(DataWriter w) + { + w.Write((byte)0x1A); + w.Write(SerializerVersion); + w.Write(PoseMatcherVersion); + w.Write(Signature); + + w.Write(SamplesCount); + if (SamplesCount > 0) + { + foreach (var entry in Samples) + entry.Write(w); + } + + w.Write(BoneTagsCount); + if (BoneTagsCount > 0) + { + foreach (var boneTag in BoneTags) + w.Write(boneTag); + } + + WeightSet.Write(w); + + w.Write(Unk7); + w.Write(Unk8); + + w.Write(Signature); + } + } + + [TC(typeof(EXP))] + public class PoseMatcherMatchSample + { + // rage::crPoseMatcherData::MatchSample + public MetaHash ClipSet { get; set; } // from clip_sets.ymt/xml + public MetaHash Clip { get; set; } + public float Unk3 { get; set; } + public PoseMatcherPointCloud PointCloud { get; set; } + + public PoseMatcherMatchSample(DataReader r) + { + ClipSet = r.ReadUInt32(); + Clip = r.ReadUInt32(); + + Unk3 = r.ReadSingle(); + + PointCloud = new PoseMatcherPointCloud(r); + } + + public void Write(DataWriter w) + { + w.Write(ClipSet); + w.Write(Clip); + + w.Write(Unk3); + + PointCloud.Write(w); + } + + public override string ToString() + { + return $"{ClipSet}, {Clip}"; + } + } + + [TC(typeof(EXP))] + public class PoseMatcherPointCloud + { + // rage::crpmPointCloud + public int PointsCount { get; set; } + public Vector3[] Points { get; set; } + public int Unk2_Count { get; set; } // == PointsCount + public float[] Unk2_Items { get; set; } + public Vector3 BoundsMin { get; set; } + public Vector3 BoundsMax { get; set; } + public float Unk5 { get; set; } + + public PoseMatcherPointCloud(DataReader r) + { + PointsCount = r.ReadInt32(); + if (PointsCount > 0) + { + Points = new Vector3[PointsCount]; + + for (int i = 0; i < PointsCount; i++) + Points[i] = new Vector3(r.ReadSingle(), r.ReadSingle(), r.ReadSingle()); + } + + Unk2_Count = r.ReadInt32(); + if (Unk2_Count > 0) + { + Unk2_Items = new float[Unk2_Count]; + + for (int i = 0; i < Unk2_Count; i++) + Unk2_Items[i] = r.ReadSingle(); + } + + BoundsMin = new Vector3(r.ReadSingle(), r.ReadSingle(), r.ReadSingle()); + BoundsMax = new Vector3(r.ReadSingle(), r.ReadSingle(), r.ReadSingle()); + + Unk5 = r.ReadSingle(); + + if (PointsCount != Unk2_Count) + { } // no hit + } + + public void Write(DataWriter w) + { + w.Write(PointsCount); + if (PointsCount > 0) + { + foreach (var point in Points) + w.Write(point); + } + + w.Write(Unk2_Count); + if (Unk2_Count > 0) + { + foreach (var entry in Unk2_Items) + w.Write(entry); + } + + w.Write(BoundsMin); + w.Write(BoundsMax); + + w.Write(Unk5); + } + } + + [TC(typeof(EXP))] + public class PoseMatcherWeightSet + { + // rage::crWeightSet + public int WeightsCount { get; set; } + public float[] Weights { get; set; } + + public PoseMatcherWeightSet(DataReader r) + { + WeightsCount = r.ReadInt32(); + if (WeightsCount > 0) + { + Weights = new float[WeightsCount]; + + for (int i = 0; i < WeightsCount; i++) + Weights[i] = r.ReadSingle(); + } + } + + public void Write(DataWriter w) + { + w.Write(WeightsCount); + if (WeightsCount > 0) + { + foreach (var weight in Weights) + w.Write(weight); + } + } + } +} diff --git a/CodeWalker.Core/GameFiles/GameFile.cs b/CodeWalker.Core/GameFiles/GameFile.cs index 7e55e0c..05d2064 100644 --- a/CodeWalker.Core/GameFiles/GameFile.cs +++ b/CodeWalker.Core/GameFiles/GameFile.cs @@ -84,6 +84,7 @@ namespace CodeWalker.GameFiles Watermap = 28, Mrf = 29, DistantLights = 30, + Ypdb = 31, } diff --git a/CodeWalker.Core/GameFiles/GameFileCache.cs b/CodeWalker.Core/GameFiles/GameFileCache.cs index 874764e..1d33e4c 100644 --- a/CodeWalker.Core/GameFiles/GameFileCache.cs +++ b/CodeWalker.Core/GameFiles/GameFileCache.cs @@ -215,6 +215,7 @@ namespace CodeWalker.GameFiles //TestYvrs(); //TestYwrs(); //TestYmaps(); + //TestYpdbs(); //TestMrfs(); //TestPlacements(); //TestDrawables(); @@ -4526,6 +4527,48 @@ namespace CodeWalker.GameFiles } } } + public void TestYpdbs() + { + foreach (RpfFile file in AllRpfs) + { + foreach (RpfEntry entry in file.AllEntries) + { + var rfe = entry as RpfFileEntry; + if (rfe == null) continue; + + try + { + if (rfe.NameLower.EndsWith(".ypdb")) + { + UpdateStatus(string.Format(entry.Path)); + YpdbFile ypdb = RpfMan.GetFile(entry); + if (ypdb != null) + { + var odata = entry.File.ExtractFile(entry as RpfFileEntry); + var ndata = ypdb.Save(); + if (ndata.Length == odata.Length) + { + for (int i = 0; i < ndata.Length; i++) + { + if (ndata[i] != odata[i]) + { break; } + } + } + else + { } + } + else + { } + } + } + catch (Exception ex) + { + UpdateStatus("Error! " + ex.ToString()); + } + + } + } + } public void TestMrfs() { foreach (RpfFile file in AllRpfs) diff --git a/CodeWalker/ExploreForm.cs b/CodeWalker/ExploreForm.cs index 6209187..a78c984 100644 --- a/CodeWalker/ExploreForm.cs +++ b/CodeWalker/ExploreForm.cs @@ -309,6 +309,7 @@ namespace CodeWalker InitFileType(".awc", "Audio Wave Container", 22, FileTypeAction.ViewAwc, true); InitFileType(".rel", "Audio Data (REL)", 23, FileTypeAction.ViewRel, true); InitFileType(".nametable", "Name Table", 5, FileTypeAction.ViewNametable); + InitFileType(".ypdb", "Pose Matcher Database", 9, FileTypeAction.ViewYpdb); InitSubFileType(".dat", "cache_y.dat", "Cache File", 6, FileTypeAction.ViewCacheDat, true); InitSubFileType(".dat", "heightmap.dat", "Heightmap", 6, FileTypeAction.ViewHeightmap, true); @@ -1545,6 +1546,9 @@ namespace CodeWalker case FileTypeAction.ViewDistantLights: ViewDistantLights(name, path, data, fe); break; + case FileTypeAction.ViewYpdb: + ViewYpdb(name, path, data, fe); + break; case FileTypeAction.ViewHex: default: ViewHex(name, path, data); @@ -1806,6 +1810,13 @@ namespace CodeWalker f.Show(); f.LoadFile(dlf, dlf.RpfFileEntry); } + private void ViewYpdb(string name, string path, byte[] data, RpfFileEntry e) + { + var ypdb = RpfFile.GetFile(e, data); + GenericForm f = new GenericForm(this); + f.Show(); + f.LoadFile(ypdb, ypdb.RpfFileEntry); + } private RpfFileEntry CreateFileEntry(string name, string path, ref byte[] data) { @@ -4878,6 +4889,7 @@ namespace CodeWalker ViewMrf = 24, ViewNametable = 25, ViewDistantLights = 26, + ViewYpdb = 27, } From f82f2ea7bfa652a14ef91f7698140ffaa1ed29ea Mon Sep 17 00:00:00 2001 From: alexguirre Date: Mon, 14 Mar 2022 21:40:29 +0100 Subject: [PATCH 3/4] Fix reading/writing of Dat54WrapperSound --- CodeWalker.Core/GameFiles/FileTypes/RelFile.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/CodeWalker.Core/GameFiles/FileTypes/RelFile.cs b/CodeWalker.Core/GameFiles/FileTypes/RelFile.cs index 468d362..b2c4f8c 100644 --- a/CodeWalker.Core/GameFiles/FileTypes/RelFile.cs +++ b/CodeWalker.Core/GameFiles/FileTypes/RelFile.cs @@ -2673,11 +2673,12 @@ namespace CodeWalker.GameFiles MinRepeatTime = br.ReadInt16(); VariableCount = br.ReadByte(); VariableNames = new MetaHash[VariableCount]; + VariableValues = new byte[VariableCount]; for (int i = 0; i < VariableCount; i++) { VariableNames[i] = br.ReadUInt32(); + VariableValues[i] = br.ReadByte(); } - VariableValues = br.ReadBytes(VariableCount); ChildSoundsHashes = new[] { ChildSound, FallBackSound }; } @@ -2743,13 +2744,8 @@ namespace CodeWalker.GameFiles for (int i = 0; i < VariableCount; i++) { bw.Write(VariableNames[i]); + bw.Write(VariableValues[i]); } - if (VariableValues != null) - { - bw.Write(VariableValues); - } - else - { } } public override uint[] GetHashTableOffsets() { From 8b188a13e56a6f0feca441c4b744b82f327bfbd6 Mon Sep 17 00:00:00 2001 From: alexguirre Date: Wed, 16 Mar 2022 21:25:56 +0100 Subject: [PATCH 4/4] Synthesizer progress Implemented: - LERP_BUFFER_2 - OnePole_LPF/HPF - AWProcess - AllpassProcess --- CodeWalker/Utils/Synthesizer.cs | 290 +++++++++++++++++++++----------- 1 file changed, 195 insertions(+), 95 deletions(-) diff --git a/CodeWalker/Utils/Synthesizer.cs b/CodeWalker/Utils/Synthesizer.cs index 328a9a9..a170745 100644 --- a/CodeWalker/Utils/Synthesizer.cs +++ b/CodeWalker/Utils/Synthesizer.cs @@ -432,8 +432,25 @@ namespace CodeWalker.Utils a[i] = Lerp(a[i], min, max); } break; - //case Dat10Synth.Opcode.LERP_BUFFER_2: - // break; + case Dat10Synth.Opcode.LERP_BUFFER_2: // TODO: some better name for LERP_BUFFER_2 + { + var t = GetScalar(param[1]); + float[] min, max; + if ((param[0].Value & 0xFF) == (param[2].Value & 0xFF)) + { + min = GetBuffer(param[2]); + max = GetBuffer(param[3]); + } + else + { + min = GetBuffer(param[3]); + max = GetBuffer(param[2]); + } + + for (int i = 0; i < BufferSize; i++) + min[i] = Lerp(t, min[i], max[i]); + } + break; case Dat10Synth.Opcode.LERP_SCALAR: { var t = GetScalar(param[1]); @@ -567,18 +584,24 @@ namespace CodeWalker.Utils GetScalar(param[5]), ref StateBlocks[param[6].Value]); break; - //case Dat10Synth.Opcode.OnePole_LPF_BUFFER_BUFFER: - // break; - //case Dat10Synth.Opcode.OnePole_LPF_BUFFER_SCALAR: - // break; - //case Dat10Synth.Opcode.OnePole_LPF_SCALAR_SCALAR: - // break; - //case Dat10Synth.Opcode.OnePole_HPF_BUFFER_BUFFER: - // break; - //case Dat10Synth.Opcode.OnePole_HPF_BUFFER_SCALAR: - // break; - //case Dat10Synth.Opcode.OnePole_HPF_SCALAR_SCALAR: - // break; + case Dat10Synth.Opcode.OnePole_LPF_BUFFER_BUFFER: + OnePoleLPF(GetBuffer(param[0]), GetBuffer(param[1]), ref StateBlocks[param[2].Value]); + break; + case Dat10Synth.Opcode.OnePole_LPF_BUFFER_SCALAR: + OnePoleLPF(GetBuffer(param[0]), GetScalar(param[1]), ref StateBlocks[param[2].Value]); + break; + case Dat10Synth.Opcode.OnePole_LPF_SCALAR_SCALAR: + SetRegister(param[0], OnePoleLPF(GetScalar(param[1]), GetScalar(param[2]), ref StateBlocks[param[3].Value])); + break; + case Dat10Synth.Opcode.OnePole_HPF_BUFFER_BUFFER: + OnePoleHPF(GetBuffer(param[0]), GetBuffer(param[1]), ref StateBlocks[param[2].Value]); + break; + case Dat10Synth.Opcode.OnePole_HPF_BUFFER_SCALAR: + OnePoleHPF(GetBuffer(param[0]), GetScalar(param[1]), ref StateBlocks[param[2].Value]); + break; + case Dat10Synth.Opcode.OnePole_HPF_SCALAR_SCALAR: + SetRegister(param[0], OnePoleHPF(GetScalar(param[1]), GetScalar(param[2]), ref StateBlocks[param[3].Value])); + break; case Dat10Synth.Opcode.OSC_RAMP_BUFFER_BUFFER: OscillatorRamp(GetBuffer(param[0]), ref StateBlocks[param[1].Value]); break; @@ -816,8 +839,9 @@ namespace CodeWalker.Utils for (int i = 0; i < BufferSize; i++) a[i] = scalar; break; - //case Dat10Synth.Opcode.AWProcess: - // break; + case Dat10Synth.Opcode.AWProcess: + AWFilter(GetBuffer(param[0]), GetBuffer(param[1]), GetBuffer(param[2]), ref StateBlocks[param[3].Value]); + break; case Dat10Synth.Opcode.LERP_BUFFER_BUFFER: { var t = GetBuffer(param[0]); @@ -910,10 +934,12 @@ namespace CodeWalker.Utils SetRegister(param[0], result); } break; - //case Dat10Synth.Opcode.AllpassProcess_BUFFER_SCALAR: - // break; - //case Dat10Synth.Opcode.AllpassProcess_BUFFER_BUFFER: - // break; + case Dat10Synth.Opcode.AllpassProcess_BUFFER_SCALAR: + AllpassFilter(GetBuffer(param[0]), GetScalar(param[1]), ref StateBlocks[param[2].Value]); + break; + case Dat10Synth.Opcode.AllpassProcess_BUFFER_BUFFER: + AllpassFilter(GetBuffer(param[0]), GetBuffer(param[1]), ref StateBlocks[param[2].Value]); + break; case Dat10Synth.Opcode.FINISH: frameFinished = true; break; @@ -1007,32 +1033,19 @@ namespace CodeWalker.Utils return (max - min) * t + min; } - private float HardKnee(float y, float threshold) + private float HardKnee(float sample, float threshold) { - /* -ENVELOPE_GEN__R_LINEAR_T_ONE_SHOT 0, 0.025, 0, 1, 0, 0, 1 => B0 [0] -COPY_BUFFER B0 => B1 -HARD_KNEE_BUFFER B1, 0.25 => B1 -COPY_BUFFER B0 => B2 -HARD_KNEE_BUFFER B2, 0.85 => B2 -COPY_BUFFER B0 => B4 -HARD_KNEE_BUFFER B4, 0.75 => B4 - -FILL_BUFFER 0 => B3 -FINISH => - */ - // TODO(alexguirre): better names for HardKnee and maybe some comments float result; - if (y < 0.0f) + if (sample < 0.0f) result = 0.0f; else - result = (y / threshold) * 0.5f; + result = (sample / threshold) * 0.5f; - if ((y - threshold) >= 0.0f) - result = (((y - threshold) / (1.0f - threshold)) + 1.0f) * 0.5f; + if (sample >= threshold) + result = (((sample - threshold) / (1.0f - threshold)) + 1.0f) * 0.5f; - if (y >= 1.0f) + if (sample >= 1.0f) result = 1.0f; return result; @@ -1048,10 +1061,11 @@ FINISH => return (float)Math.Pow(Math.Max(0, a), b); } - private float NoteToFrequency(float note) + // https://newt.phys.unsw.edu.au/jw/notes.html + private float NoteToFrequency(float midiNote) { - // TODO(alexguirre): figure out the meaning of this formula and constants in NoteToFrequency - return (float)Math.Pow(2.0f, (note + 36.376301f) / 12.0f); + // A4 (note #69) = 440Hz + return 440.0f * (float)Math.Pow(2.0f, (midiNote - 69) / 12.0f); } private void Decimate(float[] buffer, float scalar1, float deltaPerSample, ref StateBlock stateBlock) @@ -1220,7 +1234,7 @@ FINISH => float w = (float)(2.0 * Math.PI * centerFrequency / SampleRate); float cosW = (float)Math.Cos(w); - float v14 = 1.0f / (float)Math.Tan((freq2 * 0.000020833333f) * Math.PI); + float v14 = 1.0f / (float)Math.Tan(Math.PI * freq2 / SampleRate); b0 = 1.0f; b1 = 0.0f; @@ -1243,7 +1257,7 @@ FINISH => float w = (float)(2.0f * Math.PI * centerFrequency / SampleRate); float cosW = (float)Math.Cos(w); - float v15 = (float)Math.Tan((freq2 * 0.000020833333f) * Math.PI); + float v15 = (float)Math.Tan(Math.PI * freq2 / SampleRate); b0 = 1.0f; b1 = -2.0f * cosW; @@ -1268,7 +1282,7 @@ FINISH => float q = centerFrequency / freq2; float alpha = (float)Math.Sin(w) / (2.0f * q); float cosW = (float)Math.Cos(w); - float A = Math.Max(0.000001f, gain * 1.4141999f); // TODO(alexguirre): this should be amp = 10.0 ** (gain_db/40.0), where does 1.41 come from? maybe gain not in dB? + float A = Math.Max(0.000001f, gain * (float)Math.Sqrt(2)); // TODO(alexguirre): this should be amp = 10.0 ** (gain_db/40.0), where does sqrt(2) come from? maybe gain not in dB? b0 = 1.0f + alpha * A; b1 = -2.0f * cosW; @@ -1338,53 +1352,96 @@ FINISH => private void BiquadFilter2Pole(float[] buffer, float b0, float b1, float b2, float a1, float a2, ref StateBlock stateBlock) { - // State block: - // X -> ... - // Y -> ... - - float stateX = stateBlock.X; - float stateY = stateBlock.Y; + float pole1 = stateBlock.X; + float pole2 = stateBlock.Y; for (int i = 0; i < BufferSize; i++) { - float x = (buffer[i] * b0) + stateX; - stateX = (buffer[i] * b1) - (x * a1) + stateY; - stateY = (buffer[i] * b2) - (x * a2); + float s = buffer[i]; + float x = (s * b0) + pole1; + pole1 = (s * b1) - (x * a1) + pole2; + pole2 = (s * b2) - (x * a2); buffer[i] = x; } - stateBlock.X = stateX; - stateBlock.Y = stateY; + stateBlock.X = pole1; + stateBlock.Y = pole2; } private void BiquadFilter4Pole(float[] buffer, float b0, float b1, float b2, float a1, float a2, ref StateBlock stateBlock) { - // State block: - // X -> ... - // Y -> ... - // Z -> ... - // W -> ... - - float stateX = stateBlock.X; - float stateY = stateBlock.Y; - float stateZ = stateBlock.Z; - float stateW = stateBlock.W; + float pole1 = stateBlock.X; + float pole2 = stateBlock.Y; + float pole3 = stateBlock.Z; + float pole4 = stateBlock.W; for (int i = 0; i < BufferSize; i++) { - float x = (buffer[i] * b0) + stateX; - float y = (x * b0) + stateZ; - stateX = (buffer[i] * b1) - (x * a1) + stateY; - stateY = (buffer[i] * b2) - (x * a2); - stateZ = (x * b1) - (y * a1) + stateW; - stateW = (x * b2) - (y * a2); + float s = buffer[i]; + float x = (s * b0) + pole1; + pole1 = (s * b1) - (x * a1) + pole2; + pole2 = (s * b2) - (x * a2); + float y = (x * b0) + pole3; + pole3 = (x * b1) - (y * a1) + pole4; + pole4 = (x * b2) - (y * a2); buffer[i] = y; } - stateBlock.X = stateX; - stateBlock.Y = stateY; - stateBlock.Z = stateZ; - stateBlock.W = stateW; + stateBlock.X = pole1; + stateBlock.Y = pole2; + stateBlock.Z = pole3; + stateBlock.W = pole4; + } + + // https://www.earlevel.com/main/2012/12/15/a-one-pole-filter/ + // TODO: verify OnePoleLPF/HPF results + private void OnePoleLPF(float[] buffer, float[] frequencies, ref StateBlock stateBlock) + { + for (int i = 0; i < BufferSize; i++) + { + buffer[i] = OnePoleLPF(buffer[i], frequencies[i], ref stateBlock); + } + } + + private void OnePoleLPF(float[] buffer, float frequency, ref StateBlock stateBlock) + { + for (int i = 0; i < BufferSize; i++) + { + buffer[i] = OnePoleLPF(buffer[i], frequency, ref stateBlock); + } + } + + private float OnePoleLPF(float sample, float frequency, ref StateBlock stateBlock) + { + float previousSample = stateBlock.X; + + float b1 = (float)Math.Exp(-2.0f * Math.PI * frequency * 256.0f / SampleRate); + float a0 = 1.0f - b1; + + float s = a0 * sample + (b1 * previousSample); + stateBlock.X = s; + return s; + } + + private void OnePoleHPF(float[] buffer, float[] frequencies, ref StateBlock stateBlock) + { + for (int i = 0; i < BufferSize; i++) + { + buffer[i] = OnePoleHPF(buffer[i], frequencies[i], ref stateBlock); + } + } + + private void OnePoleHPF(float[] buffer, float frequency, ref StateBlock stateBlock) + { + for (int i = 0; i < BufferSize; i++) + { + buffer[i] = OnePoleHPF(buffer[i], frequency, ref stateBlock); + } + } + + private float OnePoleHPF(float sample, float frequency, ref StateBlock stateBlock) + { + return sample - OnePoleLPF(sample, frequency, ref stateBlock); } private void EnvelopeFollower(float[] buffer, float a2, float a3, ref StateBlock stateBlock) @@ -1469,13 +1526,12 @@ FINISH => // State block: // X -> latch SET - // TODO(alexguirre): understand logic of TriggerLatch - float stateSet = stateBlock.X; + bool set = stateBlock.X != 0.0f; float latch = 0.0f; - if (triggered && stateSet == 0.0f) + if (triggered && !set) latch = 1.0f; - if (triggered != (stateSet != 0.0f)) + if (triggered != set) { stateBlock.X = triggered ? 1.0f : 0.0f; } @@ -1811,21 +1867,6 @@ FINISH => // TODO(alexguirre): TimedTrigger may not be equivalent, game code has like 10 states, here I'm using the same states as the envelope gen // TODO(alexguirre): verify how TimedTrigger works in-game - /* -ENVELOPE_GEN__R_LINEAR_T_ONE_SHOT 0.1, 0.2, 0.3, 0.5, 0.4, 0.35, 1 => B6, R0 [1] -TIMED_TRIGGER__T_ONE_SHOT 1, 0.1, 0.2, 0.3, 0.4, 0.35 => R1, R2, R3, R4, R5 [2] - -;FILL_BUFFER R0 => B0 ; envelope finished -FILL_BUFFER R1 => B1 ; timed trigger finished -;FILL_BUFFER R2 => B2 ; attack -;FILL_BUFFER R3 => B3 ; decay -;FILL_BUFFER R4 => B4 ; hold -;FILL_BUFFER R5 => B5 ; release - - -FILL_BUFFER 0 => B7 -FINISH => - */ private TimedTriggerResult TimedTrigger(EnvelopeTriggerMode triggerMode, ref StateBlock stateBlock, float trigger, float predelay, float attack, float decay, float hold, float release) { // State block: @@ -2070,6 +2111,65 @@ FINISH => } } + private void AWFilter(float[] buffer, float[] buffer2, float[] buffer3, ref StateBlock stateBlock) + { + var s = stateBlock.X; + for (int i = 0; i < BufferSize; i++) + { + var v10 = buffer3[i] * 64.0f; + var v12 = (float)rnd.NextDouble() * v10 + buffer2[i] * 127.0f + 1.0f; + var v13 = 1.0f / v12; + var v14 = (1.0f - (v13 * v12)) * v13 + v13; + s = s - ((s - buffer[i]) * v14); + buffer[i] = s; + } + stateBlock.X = s; + } + + // https://thewolfsound.com/allpass-filter/#first-order-iir-allpass + private void AllpassFilter(float[] buffer, float breakFrequency, ref StateBlock stateBlock) + { + float previousUnfilteredSample = stateBlock.X; + float previousSample = stateBlock.Y; + + float a1 = (float)Math.Tan(Math.PI * breakFrequency / SampleRate); + a1 = (a1 - 1.0f) / (a1 + 1.0f); + + for (int i = 0; i < BufferSize; i++) + { + float sample = a1 * buffer[i] + previousUnfilteredSample - a1 * previousSample; + sample = Math.Max(Math.Min(sample, 1.0f), -1.0f); + + previousUnfilteredSample = buffer[i]; + previousSample = sample; + buffer[i] = sample; + } + + stateBlock.X = previousUnfilteredSample; + stateBlock.Y = previousSample; + } + + private void AllpassFilter(float[] buffer, float[] breakFrequencies, ref StateBlock stateBlock) + { + float previousUnfilteredSample = stateBlock.X; + float previousSample = stateBlock.Y; + + for (int i = 0; i < BufferSize; i++) + { + float a1 = (float)Math.Tan(Math.PI * breakFrequencies[i] / SampleRate); + a1 = (a1 - 1.0f) / (a1 + 1.0f); + + float sample = a1 * buffer[i] + previousUnfilteredSample - a1 * previousSample; + sample = Math.Max(Math.Min(sample, 1.0f), -1.0f); + + previousUnfilteredSample = buffer[i]; + previousSample = sample; + buffer[i] = sample; + } + + stateBlock.X = previousUnfilteredSample; + stateBlock.Y = previousSample; + }