├── App.config ├── README.md ├── Properties ├── Settings.settings ├── Settings.Designer.cs ├── AssemblyInfo.cs ├── Resources.Designer.cs └── Resources.resx ├── Program.cs ├── Form1.resx ├── GameshowSC2.csproj ├── .gitignore ├── Form1.Designer.cs └── Form1.cs /App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GameShowSC2Switcher 2 | Port of XSplitSC2Switcher to work in the GameShow broadcasting app 3 | 4 | [Download](https://github.com/leigholiver/GameShowSC2Switcher/releases/download/0.1/GameshowSC2.exe) 5 | -------------------------------------------------------------------------------- /Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Windows.Forms; 6 | 7 | namespace GameshowSC2 8 | { 9 | static class Program 10 | { 11 | /// 12 | /// The main entry point for the application. 13 | /// 14 | [STAThread] 15 | static void Main() 16 | { 17 | Application.EnableVisualStyles(); 18 | Application.SetCompatibleTextRenderingDefault(false); 19 | Application.Run(new Form1()); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace GameshowSC2.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("GameshowSC2")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("GameshowSC2")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("7a3a7cec-6779-4438-bcf1-986f4960b5ae")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace GameshowSC2.Properties 12 | { 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources 26 | { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal Resources() 34 | { 35 | } 36 | 37 | /// 38 | /// Returns the cached ResourceManager instance used by this class. 39 | /// 40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) 46 | { 47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("GameshowSC2.Properties.Resources", typeof(Resources).Assembly); 48 | resourceMan = temp; 49 | } 50 | return resourceMan; 51 | } 52 | } 53 | 54 | /// 55 | /// Overrides the current thread's CurrentUICulture property for all 56 | /// resource lookups using this strongly typed resource class. 57 | /// 58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 59 | internal static global::System.Globalization.CultureInfo Culture 60 | { 61 | get 62 | { 63 | return resourceCulture; 64 | } 65 | set 66 | { 67 | resourceCulture = value; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /Form1.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /GameshowSC2.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {7A3A7CEC-6779-4438-BCF1-986F4960B5AE} 8 | WinExe 9 | GameshowSC2 10 | GameshowSC2 11 | v4.5.2 12 | 512 13 | true 14 | false 15 | publish\ 16 | true 17 | Disk 18 | false 19 | Foreground 20 | 7 21 | Days 22 | false 23 | false 24 | true 25 | 1 26 | 1.0.0.%2a 27 | false 28 | true 29 | true 30 | 31 | 32 | AnyCPU 33 | true 34 | full 35 | false 36 | bin\Debug\ 37 | DEBUG;TRACE 38 | prompt 39 | 4 40 | 41 | 42 | AnyCPU 43 | pdbonly 44 | true 45 | bin\Release\ 46 | TRACE 47 | prompt 48 | 4 49 | 50 | 51 | 8B2A99242B33A3DE49D19DD2DBB7694D790B6ACB 52 | 53 | 54 | GameshowSC2_TemporaryKey.pfx 55 | 56 | 57 | true 58 | 59 | 60 | true 61 | 62 | 63 | bin\Release32\ 64 | TRACE 65 | true 66 | pdbonly 67 | AnyCPU 68 | prompt 69 | MinimumRecommendedRules.ruleset 70 | true 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | Form 88 | 89 | 90 | Form1.cs 91 | 92 | 93 | 94 | 95 | Form1.cs 96 | 97 | 98 | ResXFileCodeGenerator 99 | Resources.Designer.cs 100 | Designer 101 | 102 | 103 | True 104 | Resources.resx 105 | 106 | 107 | 108 | SettingsSingleFileGenerator 109 | Settings.Designer.cs 110 | 111 | 112 | True 113 | Settings.settings 114 | True 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | False 123 | Microsoft .NET Framework 4.5.2 %28x86 and x64%29 124 | true 125 | 126 | 127 | False 128 | .NET Framework 3.5 SP1 129 | false 130 | 131 | 132 | 133 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | *.ndf 223 | 224 | # Business Intelligence projects 225 | *.rdl.data 226 | *.bim.layout 227 | *.bim_*.settings 228 | 229 | # Microsoft Fakes 230 | FakesAssemblies/ 231 | 232 | # GhostDoc plugin setting file 233 | *.GhostDoc.xml 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | node_modules/ 238 | 239 | # Typescript v1 declaration files 240 | typings/ 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 249 | *.vbw 250 | 251 | # Visual Studio LightSwitch build output 252 | **/*.HTMLClient/GeneratedArtifacts 253 | **/*.DesktopClient/GeneratedArtifacts 254 | **/*.DesktopClient/ModelManifest.xml 255 | **/*.Server/GeneratedArtifacts 256 | **/*.Server/ModelManifest.xml 257 | _Pvt_Extensions 258 | 259 | # Paket dependency manager 260 | .paket/paket.exe 261 | paket-files/ 262 | 263 | # FAKE - F# Make 264 | .fake/ 265 | 266 | # JetBrains Rider 267 | .idea/ 268 | *.sln.iml 269 | 270 | # CodeRush 271 | .cr/ 272 | 273 | # Python Tools for Visual Studio (PTVS) 274 | __pycache__/ 275 | *.pyc 276 | 277 | # Cake - Uncomment if you are using it 278 | # tools/** 279 | # !tools/packages.config 280 | 281 | # Telerik's JustMock configuration file 282 | *.jmconfig 283 | 284 | # BizTalk build output 285 | *.btp.cs 286 | *.btm.cs 287 | *.odx.cs 288 | *.xsd.cs -------------------------------------------------------------------------------- /Form1.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace GameshowSC2 2 | { 3 | partial class Form1 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.inGameList = new System.Windows.Forms.ComboBox(); 32 | this.outGameList = new System.Windows.Forms.ComboBox(); 33 | this.ipBox = new System.Windows.Forms.TextBox(); 34 | this.layerBox = new System.Windows.Forms.ComboBox(); 35 | this.label1 = new System.Windows.Forms.Label(); 36 | this.label2 = new System.Windows.Forms.Label(); 37 | this.label3 = new System.Windows.Forms.Label(); 38 | this.label4 = new System.Windows.Forms.Label(); 39 | this.button1 = new System.Windows.Forms.Button(); 40 | this.textBox1 = new System.Windows.Forms.TextBox(); 41 | this.errorLabel = new System.Windows.Forms.Label(); 42 | this.label5 = new System.Windows.Forms.Label(); 43 | this.label6 = new System.Windows.Forms.Label(); 44 | this.textBox2 = new System.Windows.Forms.TextBox(); 45 | this.button2 = new System.Windows.Forms.Button(); 46 | this.SuspendLayout(); 47 | // 48 | // inGameList 49 | // 50 | this.inGameList.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; 51 | this.inGameList.FormattingEnabled = true; 52 | this.inGameList.Location = new System.Drawing.Point(119, 87); 53 | this.inGameList.Name = "inGameList"; 54 | this.inGameList.Size = new System.Drawing.Size(152, 21); 55 | this.inGameList.TabIndex = 0; 56 | // 57 | // outGameList 58 | // 59 | this.outGameList.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; 60 | this.outGameList.FormattingEnabled = true; 61 | this.outGameList.Location = new System.Drawing.Point(119, 129); 62 | this.outGameList.Name = "outGameList"; 63 | this.outGameList.Size = new System.Drawing.Size(152, 21); 64 | this.outGameList.TabIndex = 1; 65 | // 66 | // ipBox 67 | // 68 | this.ipBox.Location = new System.Drawing.Point(119, 265); 69 | this.ipBox.Name = "ipBox"; 70 | this.ipBox.Size = new System.Drawing.Size(152, 20); 71 | this.ipBox.TabIndex = 2; 72 | // 73 | // layerBox 74 | // 75 | this.layerBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; 76 | this.layerBox.FormattingEnabled = true; 77 | this.layerBox.Items.AddRange(new object[] { 78 | "Master Layer 1", 79 | "Master Layer 2", 80 | "Master Layer 3", 81 | "Master Layer 4", 82 | "Master Layer 5"}); 83 | this.layerBox.Location = new System.Drawing.Point(119, 47); 84 | this.layerBox.Name = "layerBox"; 85 | this.layerBox.Size = new System.Drawing.Size(152, 21); 86 | this.layerBox.TabIndex = 3; 87 | this.layerBox.SelectedIndexChanged += new System.EventHandler(this.layerBox_SelectedIndexChanged); 88 | // 89 | // label1 90 | // 91 | this.label1.AutoSize = true; 92 | this.label1.Location = new System.Drawing.Point(80, 50); 93 | this.label1.Name = "label1"; 94 | this.label1.Size = new System.Drawing.Size(33, 13); 95 | this.label1.TabIndex = 4; 96 | this.label1.Text = "Layer"; 97 | this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleRight; 98 | // 99 | // label2 100 | // 101 | this.label2.AutoSize = true; 102 | this.label2.Location = new System.Drawing.Point(41, 90); 103 | this.label2.Name = "label2"; 104 | this.label2.Size = new System.Drawing.Size(72, 13); 105 | this.label2.TabIndex = 5; 106 | this.label2.Text = "In Game Shot"; 107 | this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleRight; 108 | // 109 | // label3 110 | // 111 | this.label3.AutoSize = true; 112 | this.label3.Location = new System.Drawing.Point(21, 132); 113 | this.label3.Name = "label3"; 114 | this.label3.Size = new System.Drawing.Size(92, 13); 115 | this.label3.TabIndex = 6; 116 | this.label3.Text = "Out of Game Shot"; 117 | this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleRight; 118 | // 119 | // label4 120 | // 121 | this.label4.AutoSize = true; 122 | this.label4.Location = new System.Drawing.Point(15, 268); 123 | this.label4.Name = "label4"; 124 | this.label4.Size = new System.Drawing.Size(98, 13); 125 | this.label4.TabIndex = 7; 126 | this.label4.Text = "SC2 PC IP Address"; 127 | // 128 | // button1 129 | // 130 | this.button1.Location = new System.Drawing.Point(18, 172); 131 | this.button1.Name = "button1"; 132 | this.button1.Size = new System.Drawing.Size(253, 23); 133 | this.button1.TabIndex = 8; 134 | this.button1.Text = "Start"; 135 | this.button1.UseVisualStyleBackColor = true; 136 | this.button1.Click += new System.EventHandler(this.button1_Click); 137 | // 138 | // textBox1 139 | // 140 | this.textBox1.ForeColor = System.Drawing.SystemColors.WindowFrame; 141 | this.textBox1.Location = new System.Drawing.Point(382, 12); 142 | this.textBox1.Multiline = true; 143 | this.textBox1.Name = "textBox1"; 144 | this.textBox1.Size = new System.Drawing.Size(240, 212); 145 | this.textBox1.TabIndex = 9; 146 | // 147 | // errorLabel 148 | // 149 | this.errorLabel.Location = new System.Drawing.Point(18, 198); 150 | this.errorLabel.Name = "errorLabel"; 151 | this.errorLabel.Size = new System.Drawing.Size(253, 22); 152 | this.errorLabel.TabIndex = 10; 153 | this.errorLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; 154 | // 155 | // label5 156 | // 157 | this.label5.AutoSize = true; 158 | this.label5.Location = new System.Drawing.Point(41, 297); 159 | this.label5.MaximumSize = new System.Drawing.Size(200, 0); 160 | this.label5.Name = "label5"; 161 | this.label5.Size = new System.Drawing.Size(195, 91); 162 | this.label5.TabIndex = 11; 163 | this.label5.Text = "Note: You need to go into the Battle.net launcher, click Options, Game Settings, " + 164 | "check \'Additional command line arguments\' and paste the following into the texbo" + 165 | "x:\r\n \r\n\r\n"; 166 | this.label5.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; 167 | // 168 | // label6 169 | // 170 | this.label6.AutoSize = true; 171 | this.label6.Location = new System.Drawing.Point(41, 240); 172 | this.label6.Name = "label6"; 173 | this.label6.Size = new System.Drawing.Size(195, 13); 174 | this.label6.TabIndex = 12; 175 | this.label6.Text = "If you are using a second PC to stream: "; 176 | // 177 | // textBox2 178 | // 179 | this.textBox2.Location = new System.Drawing.Point(92, 368); 180 | this.textBox2.Name = "textBox2"; 181 | this.textBox2.Size = new System.Drawing.Size(100, 20); 182 | this.textBox2.TabIndex = 13; 183 | this.textBox2.Text = "-clientapi 6119"; 184 | this.textBox2.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; 185 | // 186 | // button2 187 | // 188 | this.button2.Location = new System.Drawing.Point(190, 12); 189 | this.button2.Name = "button2"; 190 | this.button2.Size = new System.Drawing.Size(81, 23); 191 | this.button2.TabIndex = 14; 192 | this.button2.Text = "Save Settings"; 193 | this.button2.UseVisualStyleBackColor = true; 194 | this.button2.Click += new System.EventHandler(this.button2_Click); 195 | // 196 | // Form1 197 | // 198 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 199 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 200 | this.ClientSize = new System.Drawing.Size(285, 396); 201 | this.Controls.Add(this.button2); 202 | this.Controls.Add(this.textBox2); 203 | this.Controls.Add(this.label6); 204 | this.Controls.Add(this.label5); 205 | this.Controls.Add(this.errorLabel); 206 | this.Controls.Add(this.textBox1); 207 | this.Controls.Add(this.button1); 208 | this.Controls.Add(this.label4); 209 | this.Controls.Add(this.label3); 210 | this.Controls.Add(this.label2); 211 | this.Controls.Add(this.label1); 212 | this.Controls.Add(this.layerBox); 213 | this.Controls.Add(this.ipBox); 214 | this.Controls.Add(this.outGameList); 215 | this.Controls.Add(this.inGameList); 216 | this.Name = "Form1"; 217 | this.Text = "GameShow SC2 Switcher"; 218 | this.Load += new System.EventHandler(this.Form1_Load); 219 | this.ResumeLayout(false); 220 | this.PerformLayout(); 221 | 222 | } 223 | 224 | #endregion 225 | 226 | private System.Windows.Forms.ComboBox inGameList; 227 | private System.Windows.Forms.ComboBox outGameList; 228 | private System.Windows.Forms.TextBox ipBox; 229 | private System.Windows.Forms.ComboBox layerBox; 230 | private System.Windows.Forms.Label label1; 231 | private System.Windows.Forms.Label label2; 232 | private System.Windows.Forms.Label label3; 233 | private System.Windows.Forms.Label label4; 234 | private System.Windows.Forms.Button button1; 235 | private System.Windows.Forms.TextBox textBox1; 236 | private System.Windows.Forms.Label errorLabel; 237 | private System.Windows.Forms.Label label5; 238 | private System.Windows.Forms.Label label6; 239 | private System.Windows.Forms.TextBox textBox2; 240 | private System.Windows.Forms.Button button2; 241 | } 242 | } 243 | 244 | -------------------------------------------------------------------------------- /Form1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Windows.Forms; 4 | using System.Runtime.InteropServices; 5 | using System.Reflection; 6 | using System.Timers; 7 | using System.Net; 8 | using System.IO; 9 | using System.Xml; 10 | 11 | namespace GameshowSC2 12 | { 13 | public partial class Form1 : Form 14 | { 15 | private WirecastBinding _Wirecast; 16 | private BindingList inGameShotList; 17 | private BindingList outGameShotList; 18 | private System.Timers.Timer _timer; 19 | 20 | private Boolean isRunning; 21 | private Boolean inGame; 22 | 23 | public Form1() 24 | { 25 | InitializeComponent(); 26 | _Wirecast = new WirecastBinding(); 27 | _Wirecast.Initialize(); 28 | 29 | inGameShotList = new BindingList(); 30 | inGameList.DataSource = inGameShotList; 31 | inGameList.DisplayMember = "statusName"; 32 | 33 | outGameShotList = new BindingList(); 34 | outGameList.DataSource = outGameShotList; 35 | outGameList.DisplayMember = "statusName"; 36 | 37 | isRunning = false; 38 | inGame = false; 39 | 40 | _timer = new System.Timers.Timer(1000); 41 | _timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed); 42 | _timer.Start(); 43 | checkSC2State(); 44 | } 45 | 46 | private void layerBox_SelectedIndexChanged(object sender, EventArgs e) 47 | { 48 | int selIndex = layerBox.SelectedIndex + 1; 49 | _Wirecast.SwitchLayer(selIndex); 50 | LoadLayerShots(); 51 | } 52 | 53 | private void LoadLayerShots() 54 | { 55 | inGameShotList.Clear(); 56 | outGameShotList.Clear(); 57 | 58 | // Populate Listbox with shots 59 | int count = _Wirecast.GetShotCount(); 60 | for (int idx = 1; idx <= count; idx++) 61 | { 62 | string name = _Wirecast.GetShotNameWithIndex(idx); 63 | bool isInPreview = _Wirecast.IsShotNameInPreview(name); 64 | bool isLive = _Wirecast.IsShotNameLive(name); 65 | bool isPlaylist = _Wirecast.IsPlaylistByShotName(name); 66 | 67 | ShotItem sItem = new ShotItem(); 68 | sItem.name = name; 69 | sItem.id = _Wirecast.GetShotIDByName(name); 70 | sItem.UpdateStatusName(isLive, isInPreview, isPlaylist); 71 | inGameShotList.Add(sItem); 72 | outGameShotList.Add(sItem); 73 | } 74 | } 75 | 76 | private void button1_Click(object sender, EventArgs e) 77 | { 78 | toggleRunning(); 79 | } 80 | 81 | private void toggleRunning() 82 | { 83 | if (isRunning) 84 | { 85 | inGameList.Enabled = true; 86 | outGameList.Enabled = true; 87 | ipBox.Enabled = true; 88 | layerBox.Enabled = true; 89 | 90 | isRunning = false; 91 | button1.Text = "Start"; 92 | } 93 | else 94 | { 95 | inGameList.Enabled = false; 96 | outGameList.Enabled = false; 97 | ipBox.Enabled = false; 98 | layerBox.Enabled = false; 99 | 100 | isRunning = true; 101 | button1.Text = "Stop"; 102 | } 103 | } 104 | 105 | private void _timer_Elapsed(object sender, ElapsedEventArgs e) 106 | { 107 | checkSC2State(); 108 | } 109 | 110 | private void checkSC2State() { 111 | String ip = "localhost"; 112 | if (ipBox.Text != "") 113 | { 114 | ip = ipBox.Text; 115 | } 116 | 117 | String url = "http://" + ip + ":6119/ui"; 118 | WebRequest request = WebRequest.Create(url); 119 | request.Timeout = 1000; 120 | 121 | try 122 | { 123 | WebResponse response = request.GetResponse(); 124 | Stream dataStream = response.GetResponseStream(); 125 | StreamReader reader = new StreamReader(dataStream); 126 | string responseFromServer = reader.ReadToEnd(); 127 | reader.Close(); 128 | response.Close(); 129 | 130 | // this is disgusting but im just prototyping and i kinda dont really 131 | // need to go through the hassle of working out json parsing libraries 132 | // #SorryNotSorry 133 | Boolean tmpInGame = false; 134 | if (responseFromServer == "{\"activeScreens\":[\"ScreenLoading/ScreenLoading\"]}" || responseFromServer == "{\"activeScreens\":[]}") 135 | { 136 | tmpInGame = true; 137 | } 138 | Invoke(new Action(() => 139 | { 140 | errorLabel.Text = ""; 141 | textBox1.Text = responseFromServer; 142 | 143 | if (inGame && !tmpInGame) // state has changed from in game to out of game 144 | { 145 | inGame = false; 146 | errorLabel.Text = "out of game"; 147 | if (isRunning) 148 | { 149 | ShotItem sItem = (ShotItem)outGameList.SelectedItem; 150 | _Wirecast.SetActiveShot(sItem.name); 151 | _Wirecast.Go(); 152 | } 153 | } 154 | else if(!inGame && tmpInGame) // state has changed from out of game to in game 155 | { 156 | inGame = true; 157 | errorLabel.Text = "in game"; 158 | if (isRunning) 159 | { 160 | ShotItem sItem = (ShotItem)inGameList.SelectedItem; 161 | _Wirecast.SetActiveShot(sItem.name); 162 | _Wirecast.Go(); 163 | } 164 | } 165 | })); 166 | } 167 | catch (Exception) 168 | { 169 | try 170 | { 171 | Invoke(new Action(() => 172 | { 173 | errorLabel.Text = "Could not reach SC2"; 174 | })); 175 | } 176 | catch (Exception) 177 | { 178 | // do nothing 179 | } 180 | } 181 | } 182 | 183 | private void Form1_Load(object sender, EventArgs e) 184 | { 185 | try 186 | { 187 | FormSerialisor.Deserialise(this, Application.StartupPath + @"\cfg.xml"); 188 | } 189 | catch (Exception) 190 | { 191 | errorLabel.Text = "Error loading configuration"; 192 | } 193 | } 194 | 195 | private void button2_Click(object sender, EventArgs e) 196 | { 197 | FormSerialisor.Serialise(this, Application.StartupPath + @"\cfg.xml"); 198 | } 199 | } 200 | 201 | public class ShotItem 202 | { 203 | public string name { get; set; } 204 | public int id { get; set; } 205 | public string statusName { get; set; } 206 | public void UpdateStatusName(bool live, bool preview, bool playlist) 207 | { 208 | statusName = name; 209 | } 210 | } 211 | 212 | /// 213 | /// Proxy Object for the Late binding 214 | /// Provide more logical API for bindings 215 | /// 216 | public class WirecastBinding 217 | { 218 | private object _Wirecast; 219 | private object _Document; 220 | private object _SelectedLayer; 221 | private int _SelectedLayerIndex; 222 | 223 | /// 224 | /// Late binding helper class. 225 | /// static bindings to help you get/set via OLE/COM. 226 | /// 227 | class Late 228 | { 229 | public static void Set(object obj, string sProperty, object oValue) 230 | { 231 | object[] oParam = new object[1]; 232 | oParam[0] = oValue; 233 | obj.GetType().InvokeMember(sProperty, BindingFlags.SetProperty, null, obj, oParam); 234 | } 235 | public static object Get(object obj, string sProperty, object oValue) 236 | { 237 | object[] oParam = new object[1]; 238 | oParam[0] = oValue; 239 | return obj.GetType().InvokeMember(sProperty, BindingFlags.GetProperty, null, obj, oParam); 240 | } 241 | public static object Get(object obj, string sProperty, object[] oValue) 242 | { 243 | return obj.GetType().InvokeMember(sProperty, BindingFlags.GetProperty, null, obj, oValue); 244 | } 245 | public static object Get(object obj, string sProperty) 246 | { 247 | return obj.GetType().InvokeMember(sProperty, BindingFlags.GetProperty, null, obj, null); 248 | } 249 | public static object Invoke(object obj, string sProperty, object[] oParam) 250 | { 251 | return obj.GetType().InvokeMember(sProperty, BindingFlags.InvokeMethod, null, obj, oParam); 252 | } 253 | public static object Invoke(object obj, string sProperty, object oValue) 254 | { 255 | object[] oParam = new object[1]; 256 | oParam[0] = oValue; 257 | return obj.GetType().InvokeMember(sProperty, BindingFlags.InvokeMethod, null, obj, oParam); 258 | } 259 | public static object Invoke(object obj, string sProperty, object oValue, object oValue2) 260 | { 261 | object[] oParam = new object[2]; 262 | oParam[0] = oValue; 263 | oParam[1] = oValue2; 264 | return obj.GetType().InvokeMember(sProperty, BindingFlags.InvokeMethod, null, obj, oParam); 265 | } 266 | public static object Invoke(object obj, string sProperty) 267 | { 268 | return obj.GetType().InvokeMember(sProperty, BindingFlags.InvokeMethod, null, obj, null); 269 | } 270 | } 271 | 272 | /// 273 | /// Initialize the Binding object with Document Index 1 274 | /// And the Normal Layer (3) Selected 275 | /// 276 | public void Initialize() 277 | { 278 | _Wirecast = null; 279 | _Document = null; 280 | _SelectedLayer = null; 281 | _SelectedLayerIndex = -1; 282 | try 283 | { 284 | _Wirecast = Marshal.GetActiveObject("Wirecast.Application"); 285 | } 286 | catch 287 | { 288 | Type objClassType = Type.GetTypeFromProgID("Wirecast.Application"); 289 | _Wirecast = Activator.CreateInstance(objClassType); 290 | } 291 | 292 | SwitchDocument(1); 293 | } 294 | 295 | /// 296 | /// Returns a string array of the layer names 297 | /// 298 | public string[] ValidLayerNames() 299 | { 300 | return new string[] { "text", "overlay", "normal", "underlay", "audio" }; 301 | } 302 | 303 | /// 304 | /// Converts a given Layer Name to the associated Index value 305 | /// If the name is invalid, returns -1 306 | /// 307 | public int LayerNameToIndex(string layer) 308 | { 309 | string[] layerNames = ValidLayerNames(); 310 | for (int i = 0; i < 5; i++) 311 | { 312 | if (layerNames[i] == layer) 313 | { 314 | return i + 1; 315 | } 316 | } 317 | 318 | return -1; 319 | } 320 | /// 321 | /// Get the Name of the layer associated with the index value. 322 | /// If the index is out of bounds, returns an empty string 323 | /// 324 | public string LayerIndexToName(int index) 325 | { 326 | if (index >= 1 && index <= 5) 327 | { 328 | string[] layerNames = ValidLayerNames(); 329 | return layerNames[index - 1]; 330 | } 331 | 332 | return ""; 333 | } 334 | 335 | /// 336 | /// Switches the internal document of the binding object to the new one 337 | /// The seletec layer of the new document will be the same as the previous document 338 | /// So if the selected layer was "text"/1, then that is the selected layer of the new document 339 | /// All future call to the Document and Layer API will apply to the new document 340 | /// 341 | public bool SwitchDocument(int index) 342 | { 343 | bool validDoc = false; 344 | object newDocument = Late.Invoke(_Wirecast, "DocumentByIndex", index); 345 | 346 | if (newDocument != null) 347 | { 348 | _Document = newDocument; 349 | validDoc = true; 350 | 351 | // After Change the Document, we need to update the layer 352 | SwitchLayer(3); 353 | } 354 | 355 | return validDoc; 356 | } 357 | 358 | /// 359 | /// Same as SwitchDocument with an index except indead of a document index, use the document name 360 | /// 361 | public bool SwitchDocument(string docName) 362 | { 363 | bool validDoc = false; 364 | object newDocument = Late.Invoke(_Wirecast, "DocumentByName", docName); 365 | 366 | if (newDocument != null) 367 | { 368 | _Document = newDocument; 369 | validDoc = true; 370 | 371 | // After Change the Document, we need to update the layer 372 | SwitchLayer(_SelectedLayerIndex); // 3 is the "normal" layer 373 | } 374 | 375 | return validDoc; 376 | } 377 | 378 | /// 379 | /// Returns the index of the currently selected layer 380 | /// 381 | public int GetSelectedLayerIndex() 382 | { 383 | return _SelectedLayerIndex; 384 | } 385 | 386 | /// 387 | /// Returns the name of the currently selected layer 388 | /// 389 | public string GetSelectedLayerName() 390 | { 391 | return LayerIndexToName(_SelectedLayerIndex); 392 | } 393 | 394 | /// 395 | /// Returns the number of master layers in the system 396 | /// 397 | public int GetMasterLayerCount() 398 | { 399 | return 5; 400 | } 401 | 402 | /// 403 | /// Change the internal selected layer of the binding object 404 | /// All future calls to layer related APIs will be use this new layer 405 | /// 406 | public bool SwitchLayer(int index) 407 | { 408 | bool hasLayer = false; 409 | string layerName = LayerIndexToName(index); 410 | 411 | if (layerName != "") 412 | { 413 | _SelectedLayer = Late.Invoke(_Document, "LayerByName", layerName); 414 | _SelectedLayerIndex = index; 415 | hasLayer = true; 416 | } 417 | 418 | return hasLayer; 419 | } 420 | 421 | /// 422 | /// Change the internal selected layer of the binding object 423 | /// All future calls to layer related APIs will be use this new layer 424 | /// 425 | public bool SwitchLayer(string name) 426 | { 427 | bool validName = false; 428 | int index = LayerNameToIndex(name.ToLower()); 429 | 430 | if (index != -1) 431 | { 432 | SwitchLayer(index); 433 | validName = true; 434 | } 435 | 436 | return validName; 437 | } 438 | 439 | /// 440 | /// Return whether or not the current document is broadcasting a stream 441 | /// 442 | public bool IsBroadcasting() 443 | { 444 | int isBroadcasting = (int)Late.Invoke(_Document, "IsBroadcasting"); 445 | return isBroadcasting == 1; 446 | } 447 | 448 | /// 449 | /// Toggles the Broadcast state 450 | /// If it is not broadcasting, start a broadcast 451 | /// If it is, stop it 452 | /// 453 | public void ToggleBroadcast() 454 | { 455 | if (IsBroadcasting()) 456 | { 457 | StopBroadcast(); 458 | } 459 | else 460 | { 461 | StartBroadcast(); 462 | } 463 | } 464 | public void StartBroadcast() 465 | { 466 | Late.Invoke(_Document, "Broadcast", "start"); 467 | } 468 | public void StopBroadcast() 469 | { 470 | Late.Invoke(_Document, "Broadcast", "stop"); 471 | } 472 | 473 | /// 474 | /// Returns whether or not the current document is recording to disk 475 | /// 476 | public bool IsRecording() 477 | { 478 | int isRecording = (int)Late.Invoke(_Document, "IsArchivingToDisk"); 479 | return isRecording == 1; 480 | } 481 | 482 | /// 483 | /// Toggles the Record to Disk state 484 | /// If it is not recording, start the recording 485 | /// If it is, stop it 486 | /// 487 | public void ToggleRecord() 488 | { 489 | if (IsRecording()) 490 | { 491 | StopRecording(); 492 | } 493 | else 494 | { 495 | StartRecording(); 496 | } 497 | } 498 | public void StartRecording() 499 | { 500 | Late.Invoke(_Document, "ArchiveToDisk", "start"); 501 | } 502 | public void StopRecording() 503 | { 504 | Late.Invoke(_Document, "ArchiveToDisk", "stop"); 505 | } 506 | 507 | /// 508 | /// Returns the total umber of shots in the selected layer 509 | /// To change which layer to count the shots in, use switch layers 510 | /// To get the total number of shots in the document, use GetTotalShotCount 511 | /// 512 | public int GetShotCount() 513 | { 514 | int shotCount = (int)Late.Invoke(_SelectedLayer, "ShotCount"); 515 | return shotCount; 516 | } 517 | 518 | /// 519 | /// Returns the total number of shots in the document 520 | /// 521 | public int GetTotalShotCount() 522 | { 523 | int totalLayers = GetMasterLayerCount(); 524 | int totalShotCount = 0; 525 | for (int i = 0; i < totalLayers; i++) 526 | { 527 | totalShotCount += (int)Late.Invoke(i, "ShotCount"); 528 | } 529 | 530 | return totalShotCount; 531 | } 532 | 533 | /// 534 | /// Returns the ShotID for associated with the Shot name 535 | /// The name is the text under the shot in the Shot bin 536 | /// 537 | public int GetShotIDByName(string name) 538 | { 539 | int shotID = (int)Late.Invoke(_SelectedLayer, "ShotIDByName", name, 2); 540 | return shotID; 541 | } 542 | 543 | /// 544 | /// Returns the ShotID for the index within the selected layer 545 | /// The index is Zero based and from left to right in the layer 546 | /// 547 | public int GetShotIDByIndex(int index) 548 | { 549 | int shotID = (int)Late.Invoke(_SelectedLayer, "ShotIDByIndex", index); 550 | return shotID; 551 | } 552 | 553 | /// 554 | /// Returns the Shot COM object for the index 555 | /// Usually would not need to call this method directly 556 | /// 557 | /// Returns null if there are no shots assoicated witht he index 558 | /// 559 | public object GetShotWithIndex(int index) 560 | { 561 | object shot = null; 562 | int shotID = GetShotIDByIndex(index); 563 | 564 | if (shotID != 0) 565 | { 566 | shot = GetShotWithID(shotID); 567 | } 568 | 569 | return shot; 570 | } 571 | 572 | /// 573 | /// Returns the Shot COM object for the shotID 574 | /// Usually would not need to call this method directly 575 | /// 576 | /// Returns null if there are no shots assoicated witht he shotID 577 | /// 578 | public object GetShotWithID(int shotID) 579 | { 580 | return Late.Invoke(_Document, "ShotByShotID", shotID); 581 | } 582 | 583 | /// 584 | /// Returns the Shot COM object for the name 585 | /// Usually would not need to call this method directly 586 | /// 587 | /// Returns null if there are no shots assoicated witht he name 588 | /// 589 | public object GetShotWithName(string name) 590 | { 591 | int shotID = GetShotIDByName(name); 592 | return GetShotWithID(shotID); 593 | } 594 | 595 | /// 596 | /// Returns the name of the shot associated with the shotID 597 | /// If there are no shots associated with that ID, returns an empty string 598 | /// 599 | public string GetShotNameWithID(int shotID) 600 | { 601 | object shot = GetShotWithID(shotID); 602 | if (shot != null) 603 | { 604 | return (string)Late.Get(shot, "Name"); 605 | } 606 | else 607 | { 608 | return ""; 609 | } 610 | } 611 | 612 | /// 613 | /// Returns the name of the shot at "index" 614 | /// If there are no shots associated with the index, returns an empty string 615 | /// 616 | public string GetShotNameWithIndex(int index) 617 | { 618 | int shotID = GetShotIDByIndex(index); 619 | return GetShotNameWithID(shotID); 620 | } 621 | 622 | /// 623 | /// Sets the name of the shot associated with the shot ID 624 | /// Returns true if the name was set successfully 625 | /// Returns false if there is no shot associated with the shot ID 626 | /// 627 | public bool SetShotNameWithID(int shotID, string newName) 628 | { 629 | bool isShotValid = false; 630 | object shot = GetShotWithID(shotID); 631 | 632 | if (shot != null) 633 | { 634 | Late.Set(shot, "Name", newName); 635 | isShotValid = true; 636 | } 637 | 638 | return isShotValid; 639 | } 640 | 641 | /// 642 | /// Returns true if the name was set successfully 643 | /// Returns false if there is no shot called "oldName" 644 | /// 645 | public bool SetShotNameWithName(string oldName, string newName) 646 | { 647 | bool isShotValid = false; 648 | object shot = GetShotWithName(oldName); 649 | if (shot != null) 650 | { 651 | Late.Set(shot, "Name", newName); 652 | isShotValid = true; 653 | } 654 | return isShotValid; 655 | } 656 | 657 | /// 658 | /// Returns true if the speed input string is a valid transition speed name 659 | /// Returns false if it is not 660 | /// 661 | private bool isSpeedValid(string speed) 662 | { 663 | bool isValid = (speed == "slowest" || speed == "slow" || speed == "normal" || speed == "faster" || speed == "fastest"); 664 | return isValid; 665 | } 666 | 667 | /// 668 | /// Returns a string array of all the valid Transition Speeds 669 | /// 670 | public string[] GetValidTransitionSpeeds() 671 | { 672 | return new string[] { "slowest", "slow", "normal", "faster", "fastest" }; 673 | } 674 | 675 | /// 676 | /// Returns the currently select Transition speed name 677 | /// 678 | /// For a list of valid transition speeds, see GetValidTransitionSpeeds() 679 | /// 680 | public string GetTransitionSpeed() 681 | { 682 | string transSpeed = (string)Late.Get(_Document, "TransitionSpeed"); 683 | return transSpeed; 684 | } 685 | 686 | /// 687 | /// Sets the Transition speed of the durrent document 688 | /// Returns true if the speed is valie 689 | /// Returns false if the speed is invalid 690 | /// 691 | /// For a list of valid transition speeds, see GetValidTransitionSpeeds() 692 | /// 693 | public bool SetTransitionSpeed(string speed) 694 | { 695 | string lowerSpeed = speed.ToLower(); 696 | bool isValidSpeed = isSpeedValid(lowerSpeed); 697 | if (isValidSpeed) 698 | { 699 | Late.Set(_Document, "TransitionSpeed", lowerSpeed); 700 | } 701 | 702 | return isValidSpeed; 703 | } 704 | 705 | /// 706 | /// Returns the current sate of AutoLive in the selected document 707 | /// 708 | public bool IsAutoLiveOn() 709 | { 710 | int autoLiveOn = (int)Late.Get(_Document, "AutoLive"); 711 | return autoLiveOn == 1; 712 | } 713 | 714 | /// 715 | /// Toggles the AutoLive state 716 | /// If it is off, turn it on 717 | /// If it is on, turn it off 718 | /// 719 | public void ToggleAutoLive() 720 | { 721 | SetAutoLive(!IsAutoLiveOn()); 722 | } 723 | public void SetAutoLive(bool on) 724 | { 725 | Late.Set(_Document, "AutoLive", on); 726 | } 727 | 728 | /// 729 | /// Returns true if the index maps to a Transition popup in Wirecast, false otherwise 730 | /// 731 | public bool isTransitionIndexValid(int index) 732 | { 733 | return (index == 1 || index == 2); 734 | } 735 | 736 | /// 737 | /// Returns the index of the currently active Transition popup 738 | /// A value of 0 represents the left most popup in the Wirecast UI 739 | /// 740 | public int GetActiveTransitionIndex() 741 | { 742 | int activeTransIndex = (int)Late.Get(_Document, "ActiveTransitionIndex"); 743 | return activeTransIndex; 744 | } 745 | 746 | /// 747 | /// Set the Active Transition popup in Wirecast 748 | /// A value of 0 represents the left most popup in the Wirecast UI 749 | /// 750 | public bool SetActiveTransitionIndex(int index) 751 | { 752 | bool isIndexValid = isTransitionIndexValid(index); 753 | if (isIndexValid) 754 | { 755 | Late.Set(_Document, "ActiveTransitionIndex", index); 756 | } 757 | return isIndexValid; 758 | } 759 | 760 | /// 761 | /// Returns true if the Audio is muted tot he speakers 762 | /// 763 | public bool IsAudioMutedToSpeakers() 764 | { 765 | int audioMuted = (int)Late.Get(_Document, "AudioMutedToSpeaker"); 766 | return audioMuted == 1; 767 | } 768 | public void ToggleAudioMutedToSpeakers() 769 | { 770 | bool audioMuted = IsAudioMutedToSpeakers(); 771 | SetAudioMutedToSpeakers(!audioMuted); 772 | } 773 | public void SetAudioMutedToSpeakers(bool muted) 774 | { 775 | Late.Set(_Document, "AudioMutedToSpeaker", muted); 776 | } 777 | 778 | /// 779 | /// Takes a snapshot still image of the current output and saves it as a JPEG to the given path 780 | /// 781 | public void SaveSnapshot(string path) 782 | { 783 | Late.Invoke(_Document, "SaveSnapshot", path); 784 | } 785 | 786 | /// 787 | /// Remove the media asset at the given path from Wirecast 788 | /// The path is not the shot name, but the actual media location on disk 789 | /// 790 | public void RemoveMedia(string path) 791 | { 792 | Late.Invoke(_Document, "RemoveMedia", path); 793 | } 794 | 795 | /// 796 | /// Creates a new shot with the asset located in the given path and adds it to the currently selected layer 797 | /// 798 | public int AddShotWithMedia(string path) 799 | { 800 | int shotID = (int)Late.Invoke(_SelectedLayer, "AddShotWithMedia", path); 801 | return shotID; 802 | } 803 | 804 | /// 805 | /// Removes the shot with the given ID from the currently selected layer 806 | /// Does nothing if the shot ID is invalid or not associated with any shots in the currently selected layer 807 | /// 808 | public void RemoveShotWithID(int shotID) 809 | { 810 | Late.Invoke(_SelectedLayer, "RemoveShotByID", shotID); 811 | } 812 | 813 | /// 814 | /// Removes the shot with the given name from the currently selected layer 815 | /// Does nothing if the name is not associated with any shots in the currently selected layer 816 | /// 817 | public void RemoveShotWithName(string name) 818 | { 819 | int shotID = GetShotIDByName(name); 820 | RemoveShotWithID(shotID); 821 | } 822 | 823 | /// 824 | /// Makes the active shot of the selected layer go live 825 | /// 826 | public void Go() 827 | { 828 | Late.Invoke(_SelectedLayer, "Go"); 829 | } 830 | 831 | /// 832 | /// Returns true if the currently selected layer is visible, false otherwise 833 | /// 834 | public bool IsLayerVisible() 835 | { 836 | int visible = (int)Late.Get(_SelectedLayer, "Visible"); 837 | return visible == 1; 838 | } 839 | 840 | /// 841 | /// Toggles the selected layer's visibility 842 | /// 843 | public void ToggleLayerVisibility() 844 | { 845 | bool visible = IsLayerVisible(); 846 | Late.Set(_SelectedLayer, "Visible", !visible); 847 | } 848 | 849 | /// 850 | /// Returns true if the shot ID is the ID of current active shot of the currently selected layer 851 | /// Returns false if the shot ID is invalid or not in the currently selected layer 852 | /// 853 | public bool IsActiveShot(int shotID) 854 | { 855 | int activeShotID = GetActiveShotID(); 856 | return activeShotID == shotID; 857 | } 858 | 859 | /// 860 | /// Returns true if the name is the name of the current active shot of the currently selected layer 861 | /// Returns false if the name is not 862 | /// 863 | public bool IsActiveShot(string name) 864 | { 865 | string activeShotName = GetActiveShotName(); 866 | return activeShotName == name; 867 | } 868 | 869 | /// 870 | /// Returns the shot ID of the active shot, of the currently selected layer 871 | /// The Active shot is equivilent to the shot the user has clicked 872 | /// It doesn't mean the shot that is currently live or in preview, though it is possible the active shot is live or in preview 873 | /// 874 | public int GetActiveShotID() 875 | { 876 | int shotID = (int)Late.Get(_SelectedLayer, "ActiveShotID"); 877 | return shotID; 878 | } 879 | 880 | /// 881 | /// Sets the active shot of the currently selected layer 882 | /// The Active shot is equivilent to the shot the user has clicked, so this method is the same as when a user clicked the shot 883 | /// It doesn't mean the shot that is currently live or in preview, though it is possible the active shot is live or in preview 884 | /// 885 | public bool SetActiveShot(int shotID) 886 | { 887 | bool isShotValid = false; 888 | object shot = GetShotWithID(shotID); 889 | if (shot != null) 890 | { 891 | Late.Set(_SelectedLayer, "ActiveShotID", shotID); 892 | } 893 | return isShotValid; 894 | } 895 | public bool SetActiveShot(string name) 896 | { 897 | bool isShotValid = false; 898 | object shot = GetShotWithName(name); 899 | if (shot != null) 900 | { 901 | int shotID = GetShotIDByName(name); 902 | Late.Set(_SelectedLayer, "ActiveShotID", shotID); 903 | } 904 | return isShotValid; 905 | } 906 | 907 | /// 908 | /// Returns the shot info of the shot currently in preview, of the currently selected layer 909 | /// The shot in preview is equivilent to the active shot 910 | /// 911 | public int GetPreviewShotID() 912 | { 913 | int shotID = (int)Late.Invoke(_SelectedLayer, "PreviewShotID"); 914 | return shotID; 915 | } 916 | public string GetPreviewShotName() 917 | { 918 | int shotID = GetPreviewShotID(); 919 | return GetShotNameWithID(shotID); 920 | } 921 | public object GetPreviewShot() 922 | { 923 | int shotID = GetPreviewShotID(); 924 | return GetShotWithID(shotID); 925 | } 926 | 927 | /// 928 | /// Returns the shot info of the shot currently live, in the currently selected layer 929 | /// 930 | public int GetLiveShotID() 931 | { 932 | int shotID = (int)Late.Invoke(_SelectedLayer, "LiveShotID"); 933 | return shotID; 934 | } 935 | public string GetLiveShotName() 936 | { 937 | int shotID = GetLiveShotID(); 938 | return GetShotNameWithID(shotID); 939 | } 940 | public object GetLiveShot() 941 | { 942 | int shotID = GetLiveShotID(); 943 | return GetShotWithID(shotID); 944 | } 945 | 946 | /// 947 | /// Returns the name of the active shot in the currently selected layer 948 | /// 949 | public string GetActiveShotName() 950 | { 951 | int shotID = GetActiveShotID(); 952 | return GetShotNameWithID(shotID); 953 | } 954 | 955 | /// 956 | /// Returns true if the shot is currently in preview 957 | /// false otherwise 958 | /// 959 | public bool IsShotIDInPreview(int shotID) 960 | { 961 | object shot = GetShotWithID(shotID); 962 | int result = (int)Late.Invoke(shot, "Preview"); 963 | 964 | return result == 1; 965 | } 966 | public bool IsShotNameInPreview(string name) 967 | { 968 | object shot = GetShotWithName(name); 969 | int result = (int)Late.Invoke(shot, "Preview"); 970 | 971 | return result == 1; 972 | } 973 | 974 | /// 975 | /// Returns true if the shot is currently live 976 | /// false otherwise 977 | /// 978 | public bool IsShotIDLive(int shotID) 979 | { 980 | object shot = GetShotWithID(shotID); 981 | int result = (int)Late.Invoke(shot, "Live"); 982 | 983 | return result == 1; 984 | } 985 | public bool IsShotNameLive(string name) 986 | { 987 | object shot = GetShotWithName(name); 988 | int result = (int)Late.Invoke(shot, "Live"); 989 | 990 | return result == 1; 991 | } 992 | 993 | /// 994 | /// Returns true if the shot is a Playlist Shot 995 | /// false otherwise 996 | /// 997 | public bool IsPlaylistByShotID(int shotID) 998 | { 999 | object shot = GetShotWithID(shotID); 1000 | int result = (int)Late.Invoke(shot, "Playlist"); 1001 | 1002 | return result == 1; 1003 | } 1004 | public bool IsPlaylistByShotName(string name) 1005 | { 1006 | object shot = GetShotWithName(name); 1007 | int result = (int)Late.Invoke(shot, "Playlist"); 1008 | 1009 | return result == 1; 1010 | } 1011 | 1012 | /// 1013 | /// Tells the input Playlist Shot to transition to the next shot 1014 | /// 1015 | public void NextShotByShotID(int shotID) 1016 | { 1017 | object shot = GetShotWithID(shotID); 1018 | Late.Invoke(shot, "NextShot"); 1019 | } 1020 | public void NextShotByShotName(string name) 1021 | { 1022 | object shot = GetShotWithName(name); 1023 | Late.Invoke(shot, "NextShot"); 1024 | } 1025 | 1026 | /// 1027 | /// Tells the input Playlist Shot to transition to the previous shot 1028 | /// 1029 | public void PreviousShotByShotID(int shotID) 1030 | { 1031 | object shot = GetShotWithID(shotID); 1032 | Late.Invoke(shot, "PreviousShot"); 1033 | } 1034 | public void PreviousShotByShotName(string name) 1035 | { 1036 | object shot = GetShotWithName(name); 1037 | Late.Invoke(shot, "PreviousShot"); 1038 | } 1039 | } 1040 | 1041 | public static class FormSerialisor 1042 | { 1043 | /* 1044 | * Drop this class into your project, and add the following line at the top of any class/form that wishes to use it... 1045 | using FormSerialisation; 1046 | To use the code, simply call FormSerialisor.Serialise(FormOrControlToBeSerialised, FullPathToXMLFile) 1047 | * 1048 | * For more details, see http://www.codeproject.com/KB/dialog/SavingTheStateOfAForm.aspx 1049 | * 1050 | * Last updated 13th June '10 to account for the odd behaviour of the two Panel controls in a SplitContainer (see the article) 1051 | */ 1052 | public static void Serialise(Control c, string XmlFileName) 1053 | { 1054 | XmlTextWriter xmlSerialisedForm = new XmlTextWriter(XmlFileName, System.Text.Encoding.Default); 1055 | xmlSerialisedForm.Formatting = Formatting.Indented; 1056 | xmlSerialisedForm.WriteStartDocument(); 1057 | xmlSerialisedForm.WriteStartElement("ChildForm"); 1058 | // enumerate all controls on the form, and serialise them as appropriate 1059 | AddChildControls(xmlSerialisedForm, c); 1060 | xmlSerialisedForm.WriteEndElement(); // ChildForm 1061 | xmlSerialisedForm.WriteEndDocument(); 1062 | xmlSerialisedForm.Flush(); 1063 | xmlSerialisedForm.Close(); 1064 | } 1065 | 1066 | private static void AddChildControls(XmlTextWriter xmlSerialisedForm, Control c) 1067 | { 1068 | foreach (Control childCtrl in c.Controls) 1069 | { 1070 | if (!(childCtrl is Label)) 1071 | { 1072 | // serialise this control 1073 | xmlSerialisedForm.WriteStartElement("Control"); 1074 | xmlSerialisedForm.WriteAttributeString("Type", childCtrl.GetType().ToString()); 1075 | xmlSerialisedForm.WriteAttributeString("Name", childCtrl.Name); 1076 | if (childCtrl is TextBox) 1077 | { 1078 | xmlSerialisedForm.WriteElementString("Text", ((TextBox)childCtrl).Text); 1079 | } 1080 | else if (childCtrl is ComboBox) 1081 | { 1082 | xmlSerialisedForm.WriteElementString("Text", ((ComboBox)childCtrl).Text); 1083 | xmlSerialisedForm.WriteElementString("SelectedIndex", ((ComboBox)childCtrl).SelectedIndex.ToString()); 1084 | } 1085 | else if (childCtrl is ListBox) 1086 | { 1087 | // need to account for multiply selected items 1088 | ListBox lst = (ListBox)childCtrl; 1089 | if (lst.SelectedIndex == -1) 1090 | { 1091 | xmlSerialisedForm.WriteElementString("SelectedIndex", "-1"); 1092 | } 1093 | else 1094 | { 1095 | for (int i = 0; i < lst.SelectedIndices.Count; i++) 1096 | { 1097 | xmlSerialisedForm.WriteElementString("SelectedIndex", (lst.SelectedIndices[i].ToString())); 1098 | } 1099 | } 1100 | } 1101 | else if (childCtrl is CheckBox) 1102 | { 1103 | xmlSerialisedForm.WriteElementString("Checked", ((CheckBox)childCtrl).Checked.ToString()); 1104 | } 1105 | // this next line was taken from http://stackoverflow.com/questions/391888/how-to-get-the-real-value-of-the-visible-property 1106 | // which dicusses the problem of child controls claiming to have Visible=false even when they haven't, based on the parent 1107 | // having Visible=true 1108 | bool visible = (bool)typeof(Control).GetMethod("GetState", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(childCtrl, new object[] { 2 }); 1109 | xmlSerialisedForm.WriteElementString("Visible", visible.ToString()); 1110 | // see if this control has any children, and if so, serialise them 1111 | if (childCtrl.HasChildren) 1112 | { 1113 | if (childCtrl is SplitContainer) 1114 | { 1115 | // handle this one as a special case 1116 | AddChildControls(xmlSerialisedForm, ((SplitContainer)childCtrl).Panel1); 1117 | AddChildControls(xmlSerialisedForm, ((SplitContainer)childCtrl).Panel2); 1118 | } 1119 | else 1120 | { 1121 | AddChildControls(xmlSerialisedForm, childCtrl); 1122 | } 1123 | } 1124 | xmlSerialisedForm.WriteEndElement(); // Control 1125 | } 1126 | } 1127 | } 1128 | 1129 | public static void Deserialise(Control c, string XmlFileName) 1130 | { 1131 | if (File.Exists(XmlFileName)) 1132 | { 1133 | XmlDocument xmlSerialisedForm = new XmlDocument(); 1134 | xmlSerialisedForm.Load(XmlFileName); 1135 | XmlNode topLevel = xmlSerialisedForm.ChildNodes[1]; 1136 | foreach (XmlNode n in topLevel.ChildNodes) 1137 | { 1138 | SetControlProperties((Control)c, n); 1139 | } 1140 | } 1141 | } 1142 | 1143 | private static void SetControlProperties(Control currentCtrl, XmlNode n) 1144 | { 1145 | // get the control's name and type 1146 | string controlName = n.Attributes["Name"].Value; 1147 | string controlType = n.Attributes["Type"].Value; 1148 | // find the control 1149 | Control[] ctrl = currentCtrl.Controls.Find(controlName, true); 1150 | if (ctrl.Length == 0) 1151 | { 1152 | // can't find the control 1153 | } 1154 | else 1155 | { 1156 | Control ctrlToSet = GetImmediateChildControl(ctrl, currentCtrl); 1157 | if (ctrlToSet != null) 1158 | { 1159 | if (ctrlToSet.GetType().ToString() == controlType) 1160 | { 1161 | // the right type too ;-) 1162 | switch (controlType) 1163 | { 1164 | case "System.Windows.Forms.TextBox": 1165 | ((System.Windows.Forms.TextBox)ctrlToSet).Text = n["Text"].InnerText; 1166 | break; 1167 | case "System.Windows.Forms.ComboBox": 1168 | ((System.Windows.Forms.ComboBox)ctrlToSet).Text = n["Text"].InnerText; 1169 | ((System.Windows.Forms.ComboBox)ctrlToSet).SelectedIndex = Convert.ToInt32(n["SelectedIndex"].InnerText); 1170 | break; 1171 | case "System.Windows.Forms.ListBox": 1172 | // need to account for multiply selected items 1173 | ListBox lst = (ListBox)ctrlToSet; 1174 | XmlNodeList xnlSelectedIndex = n.SelectNodes("SelectedIndex"); 1175 | for (int i = 0; i < xnlSelectedIndex.Count; i++) 1176 | { 1177 | lst.SelectedIndex = Convert.ToInt32(xnlSelectedIndex[i].InnerText); 1178 | } 1179 | break; 1180 | case "System.Windows.Forms.CheckBox": 1181 | ((System.Windows.Forms.CheckBox)ctrlToSet).Checked = Convert.ToBoolean(n["Checked"].InnerText); 1182 | break; 1183 | } 1184 | ctrlToSet.Visible = Convert.ToBoolean(n["Visible"].InnerText); 1185 | // if n has any children that are controls, deserialise them as well 1186 | if (n.HasChildNodes && ctrlToSet.HasChildren) 1187 | { 1188 | XmlNodeList xnlControls = n.SelectNodes("Control"); 1189 | foreach (XmlNode n2 in xnlControls) 1190 | { 1191 | SetControlProperties(ctrlToSet, n2); 1192 | } 1193 | } 1194 | } 1195 | else 1196 | { 1197 | // not the right type 1198 | } 1199 | } 1200 | else 1201 | { 1202 | // can't find a control whose parent is the current control 1203 | } 1204 | } 1205 | } 1206 | 1207 | private static Control GetImmediateChildControl(Control[] ctrl, Control currentCtrl) 1208 | { 1209 | Control c = null; 1210 | for (int i = 0; i < ctrl.Length; i++) 1211 | { 1212 | if ((ctrl[i].Parent.Name == currentCtrl.Name) || (currentCtrl is SplitContainer && ctrl[i].Parent.Parent.Name == currentCtrl.Name)) 1213 | { 1214 | c = ctrl[i]; 1215 | break; 1216 | } 1217 | } 1218 | return c; 1219 | } 1220 | 1221 | } 1222 | } 1223 | --------------------------------------------------------------------------------