From 1f0b17457f3e3b144a0f52be1423e84d72a868a8 Mon Sep 17 00:00:00 2001 From: xan1242 <8014093+xan1242@users.noreply.github.com> Date: Tue, 22 Nov 2022 08:13:07 +0100 Subject: [PATCH] add PathfinderPlayer to repo --- PathfinderPlayer/Form1.Designer.cs | 299 ++++++++ PathfinderPlayer/Form1.cs | 877 +++++++++++++++++++++++ PathfinderPlayer/Form1.resx | 72 ++ PathfinderPlayer/PathfinderPlayer.csproj | 15 + PathfinderPlayer/PathfinderPlayer.sln | 25 + PathfinderPlayer/Program.cs | 17 + PathfinderPlayer/README.md | 39 + 7 files changed, 1344 insertions(+) create mode 100644 PathfinderPlayer/Form1.Designer.cs create mode 100644 PathfinderPlayer/Form1.cs create mode 100644 PathfinderPlayer/Form1.resx create mode 100644 PathfinderPlayer/PathfinderPlayer.csproj create mode 100644 PathfinderPlayer/PathfinderPlayer.sln create mode 100644 PathfinderPlayer/Program.cs create mode 100644 PathfinderPlayer/README.md diff --git a/PathfinderPlayer/Form1.Designer.cs b/PathfinderPlayer/Form1.Designer.cs new file mode 100644 index 0000000..ea244c6 --- /dev/null +++ b/PathfinderPlayer/Form1.Designer.cs @@ -0,0 +1,299 @@ +namespace WinFormsApp1 +{ + partial class Form1 + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.button1 = new System.Windows.Forms.Button(); + this.button2 = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.timer1 = new System.Windows.Forms.Timer(this.components); + this.label2 = new System.Windows.Forms.Label(); + this.button3 = new System.Windows.Forms.Button(); + this.label3 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.textBox2 = new System.Windows.Forms.TextBox(); + this.label5 = new System.Windows.Forms.Label(); + this.button4 = new System.Windows.Forms.Button(); + this.button5 = new System.Windows.Forms.Button(); + this.label6 = new System.Windows.Forms.Label(); + this.menuStrip1 = new System.Windows.Forms.MenuStrip(); + this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); + this.openToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog(); + this.sampleFolderDialog = new System.Windows.Forms.OpenFileDialog(); + this.label7 = new System.Windows.Forms.Label(); + this.menuStrip1.SuspendLayout(); + this.SuspendLayout(); + // + // button1 + // + this.button1.Location = new System.Drawing.Point(12, 27); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(75, 23); + this.button1.TabIndex = 0; + this.button1.Text = "Play"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // button2 + // + this.button2.Location = new System.Drawing.Point(93, 27); + this.button2.Name = "button2"; + this.button2.Size = new System.Drawing.Size(75, 23); + this.button2.TabIndex = 1; + this.button2.Text = "Pause"; + this.button2.UseVisualStyleBackColor = true; + this.button2.Click += new System.EventHandler(this.button2_Click); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 83); + this.label1.Margin = new System.Windows.Forms.Padding(5); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(36, 15); + this.label1.TabIndex = 2; + this.label1.Text = "Time:"; + // + // timer1 + // + this.timer1.Enabled = true; + this.timer1.Interval = 1; + this.timer1.Tick += new System.EventHandler(this.timer1_Tick); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(12, 133); + this.label2.Margin = new System.Windows.Forms.Padding(5); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(87, 15); + this.label2.TabIndex = 3; + this.label2.Text = "Pushing status:"; + // + // button3 + // + this.button3.Location = new System.Drawing.Point(174, 27); + this.button3.Name = "button3"; + this.button3.Size = new System.Drawing.Size(75, 23); + this.button3.TabIndex = 4; + this.button3.Text = "Stop"; + this.button3.UseVisualStyleBackColor = true; + this.button3.Click += new System.EventHandler(this.button3_Click); + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(12, 158); + this.label3.Margin = new System.Windows.Forms.Padding(5); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(28, 15); + this.label3.TabIndex = 5; + this.label3.Text = "File:"; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(12, 108); + this.label4.Margin = new System.Windows.Forms.Padding(5); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(52, 15); + this.label4.TabIndex = 6; + this.label4.Text = "Queued:"; + // + // textBox1 + // + this.textBox1.Location = new System.Drawing.Point(110, 208); + this.textBox1.Name = "textBox1"; + this.textBox1.Size = new System.Drawing.Size(100, 23); + this.textBox1.TabIndex = 0; + this.textBox1.TextChanged += new System.EventHandler(this.textBox1_TextChanged); + // + // textBox2 + // + this.textBox2.Location = new System.Drawing.Point(110, 180); + this.textBox2.Name = "textBox2"; + this.textBox2.Size = new System.Drawing.Size(100, 23); + this.textBox2.TabIndex = 7; + this.textBox2.TextChanged += new System.EventHandler(this.textBox2_TextChanged); + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(12, 183); + this.label5.Margin = new System.Windows.Forms.Padding(5, 5, 3, 0); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(58, 15); + this.label5.TabIndex = 8; + this.label5.Text = "Intensity: "; + // + // button4 + // + this.button4.Location = new System.Drawing.Point(216, 179); + this.button4.Name = "button4"; + this.button4.Size = new System.Drawing.Size(75, 23); + this.button4.TabIndex = 9; + this.button4.Text = "Apply"; + this.button4.UseVisualStyleBackColor = true; + this.button4.Click += new System.EventHandler(this.button4_Click); + // + // button5 + // + this.button5.Location = new System.Drawing.Point(216, 207); + this.button5.Name = "button5"; + this.button5.Size = new System.Drawing.Size(75, 23); + this.button5.TabIndex = 10; + this.button5.Text = "Apply"; + this.button5.UseVisualStyleBackColor = true; + this.button5.Click += new System.EventHandler(this.button5_Click); + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Location = new System.Drawing.Point(12, 211); + this.label6.Margin = new System.Windows.Forms.Padding(5, 5, 3, 0); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(68, 15); + this.label6.TabIndex = 11; + this.label6.Text = "Next node: "; + // + // menuStrip1 + // + this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripMenuItem1}); + this.menuStrip1.Location = new System.Drawing.Point(0, 0); + this.menuStrip1.Name = "menuStrip1"; + this.menuStrip1.Size = new System.Drawing.Size(365, 24); + this.menuStrip1.TabIndex = 12; + this.menuStrip1.Text = "menuStrip1"; + // + // toolStripMenuItem1 + // + this.toolStripMenuItem1.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.openToolStripMenuItem, + this.exitToolStripMenuItem}); + this.toolStripMenuItem1.Name = "toolStripMenuItem1"; + this.toolStripMenuItem1.Size = new System.Drawing.Size(37, 20); + this.toolStripMenuItem1.Text = "File"; + // + // openToolStripMenuItem + // + this.openToolStripMenuItem.Name = "openToolStripMenuItem"; + this.openToolStripMenuItem.Size = new System.Drawing.Size(112, 22); + this.openToolStripMenuItem.Text = "Open..."; + this.openToolStripMenuItem.Click += new System.EventHandler(this.openToolStripMenuItem_Click); + // + // exitToolStripMenuItem + // + this.exitToolStripMenuItem.Name = "exitToolStripMenuItem"; + this.exitToolStripMenuItem.Size = new System.Drawing.Size(112, 22); + this.exitToolStripMenuItem.Text = "Exit"; + this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click); + // + // openFileDialog1 + // + this.openFileDialog1.DefaultExt = "txt"; + this.openFileDialog1.Filter = "Pathfinder Map File Source|*.txt|All files|*.*"; + this.openFileDialog1.Title = "Open a decompiled MPF (map file)..."; + // + // sampleFolderDialog + // + this.sampleFolderDialog.Filter = "WAV files|*.wav|OGG Vorbis files|*.ogg|MP3 files|*.mp3"; + this.sampleFolderDialog.Title = "Open the folder containing samples (select any file within folder)..."; + // + // label7 + // + this.label7.AutoSize = true; + this.label7.Location = new System.Drawing.Point(12, 58); + this.label7.Margin = new System.Windows.Forms.Padding(5); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(104, 15); + this.label7.TabIndex = 13; + this.label7.Text = "Playback: Stopped"; + // + // Form1 + // + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(365, 243); + this.Controls.Add(this.label7); + this.Controls.Add(this.label6); + this.Controls.Add(this.button5); + this.Controls.Add(this.button4); + this.Controls.Add(this.label5); + this.Controls.Add(this.textBox2); + this.Controls.Add(this.textBox1); + this.Controls.Add(this.label4); + this.Controls.Add(this.label3); + this.Controls.Add(this.button3); + this.Controls.Add(this.label2); + this.Controls.Add(this.label1); + this.Controls.Add(this.button2); + this.Controls.Add(this.button1); + this.Controls.Add(this.menuStrip1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.MainMenuStrip = this.menuStrip1; + this.MaximizeBox = false; + this.Name = "Form1"; + this.Text = "Pathfinder Player"; + this.Load += new System.EventHandler(this.Form1_Load); + this.menuStrip1.ResumeLayout(false); + this.menuStrip1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private Button button1; + private Button button2; + private Label label1; + private System.Windows.Forms.Timer timer1; + private Label label2; + private Button button3; + private Label label3; + private Label label4; + private TextBox textBox1; + private TextBox textBox2; + private Label label5; + private Button button4; + private Button button5; + private Label label6; + private MenuStrip menuStrip1; + private ToolStripMenuItem toolStripMenuItem1; + private ToolStripMenuItem openToolStripMenuItem; + private ToolStripMenuItem exitToolStripMenuItem; + private OpenFileDialog openFileDialog1; + private OpenFileDialog sampleFolderDialog; + private Label label7; + } +} \ No newline at end of file diff --git a/PathfinderPlayer/Form1.cs b/PathfinderPlayer/Form1.cs new file mode 100644 index 0000000..545c974 --- /dev/null +++ b/PathfinderPlayer/Form1.cs @@ -0,0 +1,877 @@ +using ManagedBass; +using System; +using System.Globalization; +using System.Linq; +using static System.Windows.Forms.VisualStyles.VisualStyleElement.TreeView; + +namespace WinFormsApp1 +{ + + public struct PATHFINDHEADER + { + /* 0x0000 */ int id; + /* 0x0004 */ byte majorRev; + /* 0x0005 */ byte minorRev; + /* 0x0006 */ byte release; + /* 0x0007 */ byte prerelease; + /* 0x0008 */ short saveIncrement; + /* 0x000a */ short generateID; + /* 0x000c */ byte projectID; + /* 0x000d */ byte numtracks; + /* 0x000e */ byte numsections; + /* 0x000f */ byte numevents; + /* 0x0010 */ byte numrouters; + /* 0x0011 */ byte numnamedvars; + /* 0x0012 */ short numnodes; + /* 0x0014 */ uint nodeoffsets; + /* 0x0018 */ uint nodedata; + /* 0x001c */ uint eventoffsets; + /* 0x0020 */ uint eventdata; + /* 0x0024 */ uint namedvars; + /* 0x0028 */ uint noderouters; + /* 0x002c */ uint trackoffsets; + /* 0x0030 */ uint trackinfos; + /* 0x0034 */ uint sampleoffsets; + /* 0x0038 */ uint mapfilelen; + /* 0x003c */ //uint v40reserve[]; + }; /* size: 0x0048 */ + + public enum PATHACTIONTYPE + { + PATHACTION_NONE = 0, + PATHACTION_CONDITION = 1, + PATHACTION_WAITTIME = 2, + PATHACTION_WAITBEAT = 3, + PATHACTION_BRANCHTO = 4, + PATHACTION_FADE = 5, + PATHACTION_DRYLEVELFADE = 6, + PATHACTION_SFXFADE = 7, + PATHACTION_SETVALUE = 8, + PATHACTION_EVENT = 9, + PATHACTION_FILTER_ON = 10, + PATHACTION_FILTER_OFF = 11, + PATHACTION_FILTER_CLEAR = 12, + PATHACTION_CALLBACK = 13, + PATHACTION_CALC = 14, + PATHACTION_PAUSE = 15, + PATHACTION_LOADBANK = 16, + PATHACTION_PITCHFADE = 17, + PATHACTION_STRETCHFADE = 18, + PATHACTION_MAX = 19, + }; + + public enum PATHVALUETYPE + { + PATH_VALUE_BADTYPE = 0, + PATH_VALUE_SPECIAL = 1, + PATH_VALUE_VARIABLE = 2, + PATH_VALUE_INTEGER = 3, + PATH_VALUE_MAXTYPES = 4, + }; + + public enum PATHFADEWHAT + { + PATH_FADE_INVALID = 0, + PATH_FADE_VOLUME = 1, + PATH_FADE_SFXSENDLEVEL = 2, + PATH_FADE_DRYLEVEL = 3, + PATH_FADE_PITCHMULT = 4, + PATH_FADE_STRETCHMULT = 5, + PATH_FADE_CHANNELVOL = 6, + PATH_FADE_PANANGLE = 7, + PATH_FADE_PANDISTANCE = 8, + PATH_FADE_PANSIZE = 9, + PATH_FADE_PANTWIST = 10, + PATH_FADE_MAXTYPES = 11, + }; + + public enum PATHSPECIALVALUETYPE + { + PATH_SPECIALVALUE_BAD = 0, + PATH_CONTROLLER = 1, + PATH_CURRENTNODE = 2, + PATH_CURRENTPART = 3, + PATH_CURRENTSECTION = 4, + PATH_EVENTEXPIRY = 5, + PATH_EVENTPRIORITY = 6, + PATH_FXBUS = 7, + PATH_FXDRYLEVEL = 8, + PATH_FXSENDLEVEL = 9, + PATH_MAINVOICE = 10, + PATH_NEXTNODE = 11, + PATH_NOBRANCHING = 12, + PATH_NODEDURATION = 13, + PATH_PAUSE = 14, + PATH_PITCHMULT = 15, + PATH_PLAYINGNODE = 16, + PATH_PLAYSTATUS = 17, + PATH_RANDOMSHORT = 18, + PATH_TIMENOW = 19, + PATH_TIMETONEXTBEAT = 20, + PATH_TIMETONEXTBAR = 21, + PATH_TIMETONEXTNODE = 22, + PATH_VOLUME = 23, + PATH_TIMESTRETCH = 24, + PATH_BARDURATION = 25, + PATH_BEATDURATION = 26, + PATH_CURRENTCHANNELSET = 27, + PATH_PLAYINGCHANNELSET = 28, + PATH_SPECIALVALUE_MAX = 29, + }; + + public enum PATHOPERATOR + { + PATH_OPERATOR_INVALID = 0, + PATH_OPERATOR_ADD = 1, + PATH_OPERATOR_SUB = 2, + PATH_OPERATOR_MULT = 3, + PATH_OPERATOR_DIV = 4, + PATH_OPERATOR_MOD = 5, + PATH_OPERATOR_MAX = 6, + }; + + public enum PATHCOMPARE + { + PATH_COMPARE_INVALID = 0, + PATH_COMPARE_EQUALS = 1, + PATH_COMPARE_NOT_EQUAL = 2, + PATH_COMPARE_GREATER_THAN = 3, + PATH_COMPARE_LESS_THAN = 4, + PATH_COMPARE_GREATER_OR_EQUAL = 5, + PATH_COMPARE_LESS_OR_EQUAL = 6, + PATH_COMPARE_MAX = 7, + }; + + public struct PATHNODEEVENT + { + public uint eventID; + } + + public struct PATHFINDNODE + { + + public int index; /* bit position: 16 */ + public uint trackID; /* bit position: 11 */ + public uint sectionID; /* bit position: 5 */ + public int repeat; /* bit position: 0 */ + public int routerID; /* bit position: 20 */ + public uint numbranches; /* bit position: 15 */ + public uint controller; /* bit position: 12 */ + public uint beats; /* bit position: 8 */ + public uint bars; /* bit position: 0 */ + public uint partID; /* bit position: 16 */ + public uint synchevery; /* bit position: 12 */ + public uint synchoffset; /* bit position: 8 */ + public uint notes; /* bit position: 4 */ + public uint synch; /* bit position: 2 */ + public uint channelbranching; /* bit position: 1 */ + }; + + public struct PATHNAMEDVAR + { + public string name; + public int value; + }; + + struct PATHFINDBRANCH + { + public int controlmin; + public int controlmax; + public ushort dstnode; + } + public struct PATHACTCONDITION + { + public int value; + public int comparevalue; + }; + + public struct PATHACTFADE + { + public uint tovol; + public int id; + public uint flip; + public uint ms; + }; + + public struct PATHACTWAITTIME + { + public int millisecs; + public int lowest; + }; + + public struct PATHACTBRANCHTO + { + public int node; + public int ofsection; + public int immediate; + }; + + public struct PATHACTSETVALUE + { + public int towhat; + public uint setwhat; + }; + + public struct PATHACTEVENT + { + public uint eventID; + } + + public struct PATHACTCALC + { + public uint value; + public uint op; + public int by; + } + public struct PATHACT + { + public PATHACTCONDITION only; + public PATHACTWAITTIME waittime; + //public PATHACTWAITBEAT waitbeat; + public PATHACTBRANCHTO branch; + public PATHACTFADE fade; + // public PATHACTSFXFADE sfxfade; + //public PATHACTDRYFADE dryfade; + // public PATHACTPITCHFADE pitchfade; + // public PATHACTSTRETCHFADE stretchfade; + public PATHACTSETVALUE setval; + public PATHACTEVENT evt; + // public PATHACTCALLBACK callback; + public PATHACTCALC calc; + // public PATHACTPAUSE pause; + // public PATHLOADBANK loadbank; + } + public struct PATHACTION + { + public int track; + public int sectionID; + public uint type; + public uint done; + public uint leftvaluetype; + public uint rightvaluetype; + public uint assess; + public uint comparison; + public uint indent; + public PATHACT act; + } + + struct PATHEVENT + { + /* 0x0000 */ public uint queued; + /* 0x0004 */ public uint expiry; + /* 0x0008 */ public uint lastact; + /* 0x000c */ public uint eventID; /* bit position: 8 */ + // struct /* bitfield */ + // { + /* 0x000c */ public uint numactions; /* bit position: 0 */ + /* 0x0010 */ public uint currentaction; /* bit position: 24 */ + /* 0x0010 */ public uint voices; /* bit position: 20 */ + /* 0x0010 */ public int priority; /* bit position: 16 */ + /* 0x0010 */ public uint bumplower; /* bit position: 15 */ + /* 0x0010 */ public uint beingFiltered; /* bit position: 14 */ + /* 0x0010 */ public int project; /* bit position: 11 */ + //}; /* bitfield */ + /* 0x0010 */ public int unused; /* bit position: 0 */ + }; /* size: 0x0014 */ + + struct prsNODE + { + public PATHFINDNODE node; + public List branches; + public int index; + } + + struct prsPATHEVENT + { + public PATHEVENT evt; + public List actions; + } + + enum PATHASSESS + { + PATHASSESS_NONE, + PATHASSESS_IF, + PATHASSESS_ELIF, + PATHASSESS_ELSE, + PATHASSESS_ENDIF, + PATHASSESS_MAX + }; + + + public partial class Form1 : Form + { + int music; + byte[] pushbuffer; + int counter; + + int intensity_controller = 0; + int cur_node = 0; + int cur_sample = 0; + int cur_eventid = 0; + int next_branchto = 0; + int next_sample = 0; + + int next_intensity = 0; + int initial_node = 0; + + bool bReadyToQueue = false; + string queued_file; + + string sample_folder; + string sample_ext; + + bool bPausedPlayback = false; + + //PATHNAMEDVAR[] namedvars; + //List namedvars = new List(); + List nodes = new List(); + //List prsEvents = new List(); + + string[] ActionStrings = + { + "NONE", + "Condition", + "Wait", + "WaitBeat", + "Branch", + "Fade", + "DryFade", + "SFXFade", + "Set", + "Event", + "Filter ON", + "Filter OFF", + "Filter CLEAR", + "Callback", + "Calc", + "Pause", + "LoadBank", + "PitchFade", + "StretchFade", + }; + + string[] paAssessments = + { + "None", + "If", + "Elif", + "Else", + "Endif" + }; + + PATHASSESS GetPATHASSESS(string name) + { + for (int i = 0; i < (int)PATHASSESS.PATHASSESS_MAX; i++) + { + if (name.Contains(paAssessments[i])) + { + return (PATHASSESS)i; + } + } + + return PATHASSESS.PATHASSESS_NONE; + } + + PATHACTIONTYPE GetPATHACTIONTYPE(string name) + { + for (int i = 0; i < (int)PATHACTIONTYPE.PATHACTION_MAX; i++) + { + if (name.Contains(ActionStrings[i])) + { + return (PATHACTIONTYPE)i; + } + + if (GetPATHASSESS(name) != PATHASSESS.PATHASSESS_NONE) + return PATHACTIONTYPE.PATHACTION_CONDITION; + } + + return PATHACTIONTYPE.PATHACTION_NONE; + } + + enum cmpPathNodeParser + { + cmpPNP_NULL, + cmpPNP_Wave, + cmpPNP_Track, + cmpPNP_Section, + cmpPNP_Part, + cmpPNP_Router, + cmpPNP_Controller, + cmpPNP_Beats, + cmpPNP_Bars, + cmpPNP_SynchEvery, + cmpPNP_SynchOffset, + cmpPNP_Notes, + cmpPNP_Synch, + cmpPNP_ChannelBranching, + cmpPNP_Repeat, + cmpPNP_Branches, + cmpPNP_Event, + cmpPNP_MAX + }; + + public string[] cmpPathNodeStrings = + { + "NULL", + "Wave", + "Track", + "Section", + "Part", + "Router", + "Controller", + "Beats", + "Bars", + "SynchEvery", + "SynchOffset", + "Notes", + "Synch", + "ChannelBranching", + "Repeat", + "Branches", + "Event", + }; + + cmpPathNodeParser cmpGetPathNodeParserType(string line) + { + for (int i = 0; i < (int)cmpPathNodeParser.cmpPNP_MAX; i++) + { + if (line.Contains(cmpPathNodeStrings[i])) + { + return (cmpPathNodeParser)i; + } + } + + return cmpPathNodeParser.cmpPNP_NULL; + } + + void ParseMapFile(string fname) + { + StreamReader reader = File.OpenText(fname); + string line; + + while ((line = reader.ReadLine()) != null) + { + if (!line.StartsWith('#')) + { + //if (line.Contains("Var")) + //{ + // PATHNAMEDVAR var = new PATHNAMEDVAR(); + // reader.ReadLine(); + // line = reader.ReadLine(); + // //line.Replace("\t", ""); + // line.Trim(); + // string[] items = line.Split(' '); + // var.name = items[0]; + // var.value = int.Parse(items[2]); + // namedvars.Add(var); + //} + if (line.Contains("Node")) + { + prsNODE node = new prsNODE(); + + string[] items = line.Split(' '); + node.index = int.Parse(items[1]); + + cmpPathNodeParser nodetype; + reader.ReadLine(); + do + { + line = reader.ReadLine(); + if (line.Contains("}")) + break; + + nodetype = cmpGetPathNodeParserType(line); + items = line.Split(' '); + + switch (nodetype) + { + case cmpPathNodeParser.cmpPNP_Wave: + node.node.index = int.Parse(items[1]); + break; + case cmpPathNodeParser.cmpPNP_Track: + node.node.trackID = uint.Parse(items[1]); + break; + case cmpPathNodeParser.cmpPNP_Section: + node.node.sectionID = uint.Parse(items[1]); + break; + case cmpPathNodeParser.cmpPNP_Part: + node.node.partID = uint.Parse(items[1]); + break; + case cmpPathNodeParser.cmpPNP_Router: + node.node.routerID = int.Parse(items[1]); + break; + case cmpPathNodeParser.cmpPNP_Controller: + node.node.controller = uint.Parse(items[1]); + break; + case cmpPathNodeParser.cmpPNP_Beats: + node.node.beats = uint.Parse(items[1]); + break; + case cmpPathNodeParser.cmpPNP_Bars: + node.node.bars = uint.Parse(items[1]); + break; + case cmpPathNodeParser.cmpPNP_SynchEvery: + node.node.synchevery = uint.Parse(items[1]); + break; + case cmpPathNodeParser.cmpPNP_SynchOffset: + node.node.synchoffset = uint.Parse(items[1]); + break; + case cmpPathNodeParser.cmpPNP_Notes: + node.node.notes = uint.Parse(items[1]); + break; + case cmpPathNodeParser.cmpPNP_Synch: + node.node.synch = uint.Parse(items[1]); + break; + case cmpPathNodeParser.cmpPNP_ChannelBranching: + node.node.channelbranching = uint.Parse(items[1]); + break; + case cmpPathNodeParser.cmpPNP_Repeat: + node.node.repeat = int.Parse(items[1]); + break; + case cmpPathNodeParser.cmpPNP_Branches: + // branches + node.branches = new(); + reader.ReadLine(); + node.node.numbranches = 0; + do + { + line = reader.ReadLine(); + + if (line.Contains("Control")) + { + PATHFINDBRANCH newbranch = new PATHFINDBRANCH(); + line.Trim(); + items = line.Split(' '); + string[] items2 = items[1].Split(','); + newbranch.controlmin = int.Parse(items2[0]); + newbranch.controlmax = int.Parse(items2[1]); + newbranch.dstnode = ushort.Parse(items[3]); + + node.branches.Add(newbranch); + node.node.numbranches = (uint)node.branches.Count(); + } + } while (!line.Contains("}")); + break; + + default: + break; + } + } while (!line.Contains("}")); + nodes.Add(node); + } + + //if (line.Contains("Event")) + //{ + // prsPATHEVENT evt = new prsPATHEVENT(); + // evt.actions = new List(); + // + // line.Trim(); + // string[] items = line.Split(' '); + // items[1] = items[1].Trim(); + // items[1] = items[1].Remove(0, 2); + // evt.evt.eventID = uint.Parse(items[1], NumberStyles.HexNumber); + // + // // actions + // reader.ReadLine(); + // line = reader.ReadLine(); + // if (line.Contains("Actions")) + // { + // reader.ReadLine(); + // do + // { + // + // PATHACTION action = new PATHACTION(); + // PATHACTIONTYPE type; + // + // line = reader.ReadLine(); + // if (line.Contains('}')) + // break; + // + // items = line.Split('\t'); + // string[] items2 = items[0].Split(','); + // items2[0] = items2[0].Remove(0, 1); + // items2[1] = items2[1].Trim(); + // items2[1] = items2[1].Replace(")", ""); + // + // action.track = int.Parse(items2[0]); + // action.sectionID = int.Parse(items2[1]); + // + // type = GetPATHACTIONTYPE(items[1]); + // action.type = (uint)type; + // + // switch (type) + // { + // case PATHACTIONTYPE.PATHACTION_BRANCHTO: + // //PATHACTBRANCHTO branch = new(); + // items2 = items[1].Split(' '); + // + // action.act.branch.node = int.Parse(items2[1]); + // + // string[] items3 = items2[2].Split(','); + // items3[0] = items3[0].Remove(0, 1); + // items3[1] = items3[1].Trim(); + // items3[1] = items3[1].Replace(")", ""); + // + // action.act.branch.ofsection = int.Parse(items3[0]); + // action.act.branch.immediate = int.Parse(items3[1]); + // + // break; + // + // default: + // break; + // } + // + // evt.actions.Add(action); + // } while (!line.Contains("}")); + // + // } + // + // prsEvents.Add(evt); + //} + } + } + } + + int DecodeData(string FilePath) + { + int decoder = Bass.CreateStream(FilePath, 0, 0, BassFlags.Decode); + int DecodeSize = (int)Bass.ChannelGetLength(decoder, PositionFlags.Bytes); + pushbuffer = new byte[DecodeSize]; + Bass.ChannelGetData(decoder, pushbuffer, DecodeSize); + float DecodedSampleRate = 0.0f; + Bass.ChannelGetAttribute(decoder, ChannelAttribute.Frequency, out DecodedSampleRate); + Bass.ChannelSetAttribute(music, ChannelAttribute.Frequency, DecodedSampleRate); + Bass.StreamFree(decoder); + + return DecodeSize; + } + void PushFile(string FilePath, int hHandle) + { + int DecodeSize = DecodeData(FilePath); + Bass.StreamPutData(hHandle, pushbuffer, (int)DecodeSize); + } + void CreateStreamPushHandle() + { + if (music == 0) + music = Bass.CreateStream(44100, 2, 0, StreamProcedureType.Push); + } + + + + void QueueNode(int node) + { + short shnode = (short)node; + node = shnode; + + if (node < 0) + { + node = cur_node + node; + } + + prsNODE nd = nodes.ElementAt(node); + bool bFoundBranch = false; + + //if (nd.node.index <= -1) + //{ + // return; + //} + + next_sample = -1; + next_branchto = -1; + + //if (nd.node.index > 0) + next_sample = nd.node.index; + + for (int i = 0; i < nd.node.numbranches; i++) + { + if ((intensity_controller >= nd.branches.ElementAt(i).controlmin) && (intensity_controller <= nd.branches.ElementAt(i).controlmax)) + { + next_branchto = nd.branches.ElementAt(i).dstnode; + bFoundBranch = true; + } + } + if ((!bFoundBranch) && (nd.node.numbranches > 0)) + { + next_branchto = nd.branches.ElementAt(0).dstnode; + } + cur_node = node; + } + + void HandlePushing() + { + long remaining = 0; + + remaining = Bass.StreamPutData(music, (byte[])null, 0); + double time = Bass.ChannelBytes2Seconds(music, remaining); + + label1.Text = "Time: " + time.ToString("0.00") + " (bytes: " + remaining.ToString() + ")"; + label2.Text = "Pushing status: "; + if ((time - 0.1) <= 0) + { + //string fname = String.Format("E:\\Need for Speed Most Wanted\\InteractiveMusic_ps2\\MW_Music.mus_{0}.asf.wav", counter); + + label2.Text += "Pushing"; + label3.Text = "File: " + queued_file; + bReadyToQueue = true; + + + //PushFile(fname, music); + //counter++; + } + else + { + label2.Text += "Not pushing"; + } + + + } + void HandlePathfinderPlayback() + { + HandlePushing(); + + if (next_branchto >= 0 && bReadyToQueue) + { + label4.Text = String.Format("Queued: {0}", next_branchto); + QueueNode(next_branchto); + + if (next_sample > 0) + { + if (music != 0) + { + queued_file = String.Format("{0}\\{1}{2}", sample_folder, next_sample, sample_ext); + PushFile(queued_file, music); + } + } + bReadyToQueue = false; + } + } + + + public Form1() + { + InitializeComponent(); + } + + private void Form1_Load(object sender, EventArgs e) + { + music = 0; + + // 10ms update period + Bass.UpdatePeriod = 10; + + if (!Bass.Init(-1, 44100, DeviceInitFlags.Latency)) + { + MessageBox.Show("Can't initialize device"); + } + } + + private void timer1_Tick(object sender, EventArgs e) + { + if (music != 0 && (next_branchto >= 0) && !bPausedPlayback) + HandlePathfinderPlayback(); + + if (music != 0) + { + label7.Text = "Playback: "; + switch (Bass.ChannelIsActive(music)) + { + case PlaybackState.Stalled: + label7.Text += "Stalled"; + break; + case PlaybackState.Stopped: + label7.Text += "Stopped"; + break; + case PlaybackState.Paused: + label7.Text += "Paused"; + break; + case PlaybackState.Playing: + label7.Text += "Playing"; + break; + default: + break; + } + label5.Text = "Intensity: " + intensity_controller; + label6.Text = "Next node: " + next_branchto; + } + } + + private void button3_Click(object sender, EventArgs e) + { + if (music != 0) + { + Bass.ChannelStop(music); + Bass.StreamFree(music); + music = 0; + } + } + + private void button1_Click(object sender, EventArgs e) + { + //music = Bass.CreateStream("C:\\Temp\\MW_Music.mus_0.asf.wav", 0, 0, BassFlags.Default); + //ParseMapFile("D:\\Temp\\mpfmastertest\\MW_Music_decomp.txt"); + if (nodes.Count > 0) + { + bPausedPlayback = false; + if (music == 0) + { + next_branchto = initial_node; + CreateStreamPushHandle(); + } + Bass.ChannelPlay(music, false); + } + } + + private void button2_Click(object sender, EventArgs e) + { + if (music != 0) + { + if (Bass.ChannelIsActive(music) == PlaybackState.Playing) + { + Bass.ChannelPause(music); + bPausedPlayback = true; + } + } + } + + private void textBox2_TextChanged(object sender, EventArgs e) + { + int.TryParse(textBox2.Text, out next_intensity); + } + + private void button4_Click(object sender, EventArgs e) + { + intensity_controller = next_intensity; + label5.Text = "Intensity: " + intensity_controller; + } + + private void textBox1_TextChanged(object sender, EventArgs e) + { + int.TryParse(textBox1.Text, out initial_node); + } + + private void button5_Click(object sender, EventArgs e) + { + next_branchto = initial_node; + label6.Text = "Next node: " + next_branchto; + } + + private void openToolStripMenuItem_Click(object sender, EventArgs e) + { + if (openFileDialog1.ShowDialog() == DialogResult.OK) + { + if (sampleFolderDialog.ShowDialog() == DialogResult.OK) + { + nodes.Clear(); + + sample_folder = Path.GetDirectoryName(sampleFolderDialog.FileName); + sample_ext = Path.GetExtension(sampleFolderDialog.FileName); + ParseMapFile(openFileDialog1.FileName); + } + } + } + + private void exitToolStripMenuItem_Click(object sender, EventArgs e) + { + Close(); + } + } +} \ No newline at end of file diff --git a/PathfinderPlayer/Form1.resx b/PathfinderPlayer/Form1.resx new file mode 100644 index 0000000..d0d8547 --- /dev/null +++ b/PathfinderPlayer/Form1.resx @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 104, 17 + + + 219, 17 + + + 359, 17 + + \ No newline at end of file diff --git a/PathfinderPlayer/PathfinderPlayer.csproj b/PathfinderPlayer/PathfinderPlayer.csproj new file mode 100644 index 0000000..c3f390c --- /dev/null +++ b/PathfinderPlayer/PathfinderPlayer.csproj @@ -0,0 +1,15 @@ + + + + WinExe + net6.0-windows + enable + true + enable + + + + + + + \ No newline at end of file diff --git a/PathfinderPlayer/PathfinderPlayer.sln b/PathfinderPlayer/PathfinderPlayer.sln new file mode 100644 index 0000000..db200d3 --- /dev/null +++ b/PathfinderPlayer/PathfinderPlayer.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.4.33103.184 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PathfinderPlayer", "PathfinderPlayer.csproj", "{E75A71A4-FFD3-4C8C-AC89-B549D3AFEC53}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E75A71A4-FFD3-4C8C-AC89-B549D3AFEC53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E75A71A4-FFD3-4C8C-AC89-B549D3AFEC53}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E75A71A4-FFD3-4C8C-AC89-B549D3AFEC53}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E75A71A4-FFD3-4C8C-AC89-B549D3AFEC53}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {497AABF3-D40B-493E-BE84-3257D82B294E} + EndGlobalSection +EndGlobal diff --git a/PathfinderPlayer/Program.cs b/PathfinderPlayer/Program.cs new file mode 100644 index 0000000..1e39c2a --- /dev/null +++ b/PathfinderPlayer/Program.cs @@ -0,0 +1,17 @@ +namespace WinFormsApp1 +{ + internal static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + // To customize application configuration such as set high DPI settings or default font, + // see https://aka.ms/applicationconfiguration. + ApplicationConfiguration.Initialize(); + Application.Run(new Form1()); + } + } +} \ No newline at end of file diff --git a/PathfinderPlayer/README.md b/PathfinderPlayer/README.md new file mode 100644 index 0000000..00ced5e --- /dev/null +++ b/PathfinderPlayer/README.md @@ -0,0 +1,39 @@ +# Pathfinder Player + +This is an extremely basic player. + +Currently it doesn't support any advanced actions (looping, event actions, variables, routers, etc). + +It only does basic branching with a single controller. + +You can use this tool to preview what each node will sound like without having to test in the game itself. + +## Usage + +1. Open the Pathfinder map file source (decompiled with MPFmaster) + +2. Open a folder with samples in a supported format by BASS (WAV, MP3, OGG) - convert your samples if you have to + +3. Type in your desired node in the "Next node" box and apply it (optional) + +4. Press "Play" + +5. Fiddle around with the "Intensity" controller as the player is running. You should be able to observe different behavior in real time. + +## Status + +It is currently very limited and buggy. + +It currently has a basic node parser and nothing much more. + +It does not exit out of branches and can get stuck if the branches lead nowhere. + +It's also limited to 44.1kHz output as I did not write up a configuration dialog for setting up BASS and actually having a config file. + +# Credits + +[Un4seen Developments Ltd.](http://www.un4seen.com/) (Ian Luck) - BASS library + +[ManagedBass wrapper for .NET](https://github.com/ManagedBass/ManagedBass) + +