コード例 #1
0
        /// <summary>
        /// When the user presses "Start Streaming Steam", first check that they are paired
        /// </summary>
        public static async Task <StreamContext> StartStreaming(CoreDispatcher uiDispatcher, Computer computer, MoonlightStreamConfiguration streamConfig)
        {
            PairingManager p = new PairingManager(computer);

            // If we can't get the pair state, return
            bool?pairState = await p.QueryPairState();

            if (!pairState.HasValue)
            {
                DialogUtils.DisplayDialog(uiDispatcher, "Pair state query failed", "Failed to start streaming");
                return(null);
            }

            // If we're not paired, return
            if (pairState == false)
            {
                DialogUtils.DisplayDialog(uiDispatcher, "Device not paired", "Failed to start streaming");
                return(null);
            }

            // Lookup the desired app in the app list
            // NOTE: This will go away when we have a proper app list
            int appId = await LookupAppIdForApp(uiDispatcher, new NvHttp(computer.IpAddress), "Steam");

            if (appId == 0)
            {
                // LookupAppIdForApp() handles displaying a failure dialog
                return(null);
            }

            return(new StreamContext(computer, appId, streamConfig));
        }
コード例 #2
0
        /// <summary>
        /// Query the app list on the server to get the Steam App ID
        /// </summary>
        /// <returns>True if the operation succeeded, false otherwise</returns>
        private static async Task <int> LookupAppIdForApp(CoreDispatcher dispatcher, NvHttp nv, String app)
        {
            XmlQuery appList;
            string   appIdStr;

            appList = new XmlQuery(nv.BaseUrl + "/applist?uniqueid=" + nv.GetUniqueId());

            // App list query went well - try to get the app ID
            try
            {
                appIdStr = await appList.SearchElement("App", "AppTitle", app, "ID");

                Debug.WriteLine(appIdStr);
                if (appIdStr == null)
                {
                    // Not found
                    DialogUtils.DisplayDialog(dispatcher, "App Not Found", "App ID Lookup Failed");
                    return(0);
                }
            }
            // Exception connecting to the resource
            catch (Exception e)
            {
                // Steam ID lookup failed
                DialogUtils.DisplayDialog(dispatcher, "Failed to get app ID: " + e.Message, "App ID Lookup Failed");
                return(0);
            }

            // We're in the clear - save the app ID
            return(Convert.ToInt32(appIdStr));
        }
コード例 #3
0
        /// <summary>
        /// Pair with the hostname in the textbox
        /// </summary>
        public async Task Pair(CoreDispatcher uiDispatcher, Computer c)
        {
            Debug.WriteLine("Pairing...");

            // Get the pair state.
            bool?pairState = await QueryPairState();

            if (pairState == true)
            {
                DialogUtils.DisplayDialog(uiDispatcher, "This device is already paired to the host PC", "Already Paired");
                return;
            }
            // pairstate = null. We've encountered an error
            else if (!pairState.HasValue)
            {
                DialogUtils.DisplayDialog(uiDispatcher, "Failed to query pair state", "Pairing failed");
                return;
            }

            bool challenge = await PairingCryptoHelpers.PerformPairingHandshake(uiDispatcher, new WindowsCryptoProvider(), nv, nv.GetUniqueId());

            if (!challenge)
            {
                Debug.WriteLine("Challenges failed");
                return;
            }

            // Otherwise, everything was successful
            MainPage.SaveComputer(c);

            // FIXME: We can't have two dialogs open at once.
            // DialogUtils.DisplayDialog(uiDispatcher, "Pairing successful", "Success");
        }
コード例 #4
0
        public static void CreatePackFromDirectory()
        {
            string sourceFolder = DialogUtils.OpenDirectory(
                "Select folder that contains the assets you wish to pack",
                "",
                "", DialogUtils.DirectoryIsEmpty);

            if (sourceFolder == null)
            {
                return;
            }

            string[] files = Directory.GetFiles(sourceFolder);

            var destinationFile = DialogUtils.SaveFile(
                "Select destination to save created pack file",
                sourceFolder,
                "Assets_256",
                "pack");

            if (destinationFile != null)
            {
                CreatePackFromFiles(files, destinationFile);
                DialogUtils.DisplayDialog("Export Successful", "Successfully packed and saved " + files.Length + " assets to " + destinationFile);
            }
        }
コード例 #5
0
 /// <summary>
 /// Event handler for clicking the add PC manually button
 /// </summary>
 /// <param name="sender"></param>
 /// <param name="e"></param>
 private void Add_Button_Click(object sender, RoutedEventArgs e)
 {
     if (String.IsNullOrWhiteSpace(ip_textbox.Text) || String.IsNullOrWhiteSpace(nickname_textbox.Text))
     {
         DialogUtils.DisplayDialog(this.Dispatcher, "Please fill out both text boxes", "Add PC Failed");
         return;
     }
     else
     {
         Computer toAdd = new Computer(nickname_textbox.Text, ip_textbox.Text);
         this.Frame.Navigate(typeof(MainPage), toAdd);
     }
 }
コード例 #6
0
        public static void LoadZoneFileDbg()
        {
            string path = DialogUtils.OpenFile("Load Zone File", "", "zone");

            if (path != null)
            {
                ForgelightGame game = new ForgelightGame("hehe", "", "");
                if (!game.LoadZoneFromFile(path))
                {
                    DialogUtils.DisplayDialog("Zone Import Failed", "An error occurred while loading the zone file. Please check the console window for more info.");
                }
            }
        }
コード例 #7
0
        /// <summary>
        /// Runs if starting the connection failed
        /// </summary>
        private void ConnectionFailed()
        {
            // Stop showing the wait UI
            this.Waitgrid.Visibility         = Visibility.Collapsed;
            this.currentStateText.Visibility = Visibility.Collapsed;

            // Inform the user of the failure via a message dialog
            DialogUtils.DisplayDialog(this.Dispatcher, stageFailureText, "Starting Connection Failed", x =>
            {
                Cleanup();
                this.Frame.Navigate(typeof(MainPage));
            });
        }
コード例 #8
0
        public static void LoadZoneFile()
        {
            if (ForgelightExtension.Instance.ForgelightGameFactory.ActiveForgelightGame == null)
            {
                DialogUtils.DisplayDialog("No Active Game", "There is currently no active forgelight game. Please load the correct forgelight game and try again.");
                return;
            }

            string path = EditorUtility.OpenFilePanel("Load Zone File", "", "zone");

            if (path != null)
            {
                if (!ForgelightExtension.Instance.ForgelightGameFactory.ActiveForgelightGame.LoadZoneFromFile(path))
                {
                    DialogUtils.DisplayDialog("Zone Import Failed", "An error occurred while loading the zone file. Please check the console window for more info.");
                }
            }
        }
コード例 #9
0
        /// <summary>
        /// Executed when the user presses "Pair"
        /// </summary>
        private async Task PairButton_Click_Common()
        {
            Computer selected = (Computer)computerPicker.SelectedItem;

            // User hasn't selected anything or selected the placeholder
            if (selected == null || selected.IpAddress == null)
            {
                DialogUtils.DisplayDialog(this.Dispatcher, "No machine selected", "Pairing Failed");
                return;
            }

            PairingManager p = new PairingManager(selected);

            // Stop polling timer while we're pairing
            mDnsTimer.Stop();

            await p.Pair(this.Dispatcher, selected);

            mDnsTimer.Start();
        }
コード例 #10
0
        /// <summary>
        /// Connection terminated callback
        /// </summary>
        /// <param name="errorCode">Error code for connection terminated</param>
        public void ClConnectionTerminated(int errorCode)
        {
            Debug.WriteLine("Connection terminated: " + errorCode);

            if (controllers != null)
            {
                // Stop controller code
                controllers.Stop();
            }

            var unused = Task.Run(() =>
            {
                // This needs to be done on a separate thread
                MoonlightCommonRuntimeComponent.StopConnection();
            });

            DialogUtils.DisplayDialog(this.Dispatcher, "Connection terminated unexpectedly", "Connection Terminated", (command) =>
            {
                this.Frame.Navigate(typeof(MainPage), null);
            });
        }
コード例 #11
0
        public void ExportZoneFile()
        {
            if (ForgelightExtension.Instance.ZoneManager.LoadedZone != null)
            {
                string path = DialogUtils.SaveFile(
                    "Save zone file",
                    ForgelightExtension.Instance.ForgelightGameFactory.ActiveForgelightGame.PackDirectory,
                    Path.GetFileNameWithoutExtension(ForgelightExtension.Instance.ZoneManager.LoadedZone.Name),
                    "zone");

                if (path == null)
                {
                    return;
                }

                SaveZone(path);
            }
            else
            {
                DialogUtils.DisplayDialog("Cannot save zone", "An existing zone file needs to be loaded first. Please import a zone file, then try again");
            }
        }
コード例 #12
0
        /// <summary>
        /// Executed when the user presses "Start Streaming Steam!"
        /// </summary>
        private async Task StreamButton_Click_Common()
        {
            Debug.WriteLine("Start Streaming button pressed");

            selected = (Computer)computerPicker.SelectedItem;

            // User hasn't selected a machine or selected a placeholder
            if (selected == null || String.IsNullOrWhiteSpace(selected.IpAddress))
            {
                DialogUtils.DisplayDialog(this.Dispatcher, "No machine selected", "Streaming Failed");
            }
            else
            {
                // Stop enumerating machines while we're trying to check pair state
                mDnsTimer.Stop();

                byte[] aesKey = PairingCryptoHelpers.GenerateRandomBytes(16);

                // GameStream only uses 4 bytes of a 16 byte IV. Go figure.
                byte[] aesRiIndex = PairingCryptoHelpers.GenerateRandomBytes(4);
                byte[] aesIv      = new byte[16];
                Array.ConstrainedCopy(aesRiIndex, 0, aesIv, 0, aesRiIndex.Length);
                SettingsPage s = new SettingsPage();
                MoonlightStreamConfiguration config = new MoonlightStreamConfiguration(
                    s.GetStreamWidth(),
                    s.GetStreamHeight(),
                    s.GetStreamFps(),
                    10000, // FIXME: Scale by resolution
                    1024,
                    aesKey, aesIv);

                StreamContext context = await ConnectionManager.StartStreaming(this.Dispatcher, selected, config);

                if (context != null)
                {
                    this.Frame.Navigate(typeof(StreamFrame), context);
                }
            }
        }
コード例 #13
0
        /// <summary>
        /// Quit Game Event Handler
        /// </summary>
        private async Task QuitGame_Common()
        {
            Computer selected = (Computer)computerPicker.SelectedItem;

            // User hasn't selected anything or selected the placeholder
            if (selected == null || selected.IpAddress == null)
            {
                DialogUtils.DisplayDialog(this.Dispatcher, "No machine selected", "Quit Failed");
                return;
            }

            PairingManager p = new PairingManager(selected);

            if (await p.QueryPairState() != true)
            {
                DialogUtils.DisplayDialog(this.Dispatcher, "Device not paired", "Quit Failed");
                return;
            }

            try
            {
                NvHttp   nv        = new NvHttp(selected.IpAddress);
                XmlQuery quit      = new XmlQuery(nv.BaseUrl + "/cancel?uniqueid=" + nv.GetUniqueId());
                string   cancelled = await quit.ReadXmlElement("cancel");

                if (cancelled == "1")
                {
                    DialogUtils.DisplayDialog(this.Dispatcher, "Successfully Quit Game", "Quit Game");
                    return;
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }

            DialogUtils.DisplayDialog(this.Dispatcher, "Unable to quit", "Quit Game");
        }
コード例 #14
0
        public static void CreatePackFromDirectory()
        {
            string sourceFolder = EditorUtility.OpenFolderPanel("Select folder that contains the assets you wish to pack", "", "");

            if (sourceFolder == null)
            {
                return;
            }

            if (DialogUtils.DirectoryIsEmpty(sourceFolder))
            {
                bool dialog = DialogUtils.DisplayCancelableDialog("Invalid Directory", "Please select a directory that contains files.");
                if (dialog)
                {
                    CreatePackFromDirectory();
                }

                return;
            }

            string[] files = Directory.GetFiles(sourceFolder);

            var destinationFile = EditorUtility.SaveFilePanel(
                "Select destination to save created pack file",
                sourceFolder,
                "Assets_256",
                "pack");

            if (destinationFile == null)
            {
                return;
            }

            CreatePackFromFiles(files, destinationFile);
            DialogUtils.DisplayDialog("Export Successful", "Successfully packed and saved " + files.Length + " assets to " + destinationFile);
        }
コード例 #15
0
        public void ShowMessageDialog(string title, string message, string okBtn, PlatformDialogServiceArguments platformArguments = null)
        {
            Activity activity = platformArguments?.Context != null && platformArguments.Context is Activity act ? act : Current.Activity;

            DialogUtils.DisplayDialog(activity, title, message, okBtn);
        }
コード例 #16
0
        public static async Task<bool> PerformPairingHandshake(CoreDispatcher uiDispatcher, WindowsCryptoProvider provider, NvHttp nv, string uniqueId)
        {
            string result;

            // Generate a salt for hashing the PIN
            byte[] salt = GenerateRandomBytes(16);

            string pin = new Random().Next(9999).ToString("D4");
            
            // Combine the salt and pin, then create an AES key from them
            byte[] saltAndPin = SaltPin(salt, pin);
            CryptographicKey aesKey = GenerateAesKey(saltAndPin);

            // Send the salt and get the server cert
            DialogUtils.DisplayDialog(uiDispatcher, "Enter the following PIN on the host PC: " + pin, "Enter PIN");

            // User will need to close dialog themselves
            XmlQuery getServerCert = new XmlQuery(nv.BaseUrl + "/pair?uniqueid=" + uniqueId +
                "&devicename=roth&updateState=1&phrase=getservercert&salt=" + BytesToHex(salt) + "&clientcert=" + BytesToHex(await provider.GetPemCertBytes()));
            result = await getServerCert.ReadXmlElement("paired");
            if (result == null || !result.Equals("1"))
            {
                await Unpair(nv);
                return false; 
            }

            X509Certificate serverCert = await ExtractPlainCert(getServerCert, "plaincert");

            // Generate a random challenge and encrypt it with our AES key
		    byte[] randomChallenge = GenerateRandomBytes(16);
            Debug.WriteLine("Client challenge: " + BytesToHex(randomChallenge));
		    byte[] encryptedChallenge = EncryptAes(randomChallenge, aesKey);

		    // Send the encrypted challenge to the server
		    XmlQuery challengeResp = new XmlQuery(nv.BaseUrl + 
				    "/pair?uniqueid="+uniqueId+"&devicename=roth&updateState=1&clientchallenge="+BytesToHex(encryptedChallenge));
            // If we're not paired, there's a problem. 
            result = await challengeResp.ReadXmlElement("paired");
		    if (result == null || !result.Equals("1")) {
                await Unpair(nv); 
			    return false;
		    }

            // Decode the server's response and subsequent challenge
            byte[] encServerChallengeResponse = HexToBytes(await challengeResp.ReadXmlElement("challengeresponse"));
            byte[] decServerChallengeResponse = DecryptAes(encServerChallengeResponse, aesKey);

            byte[] serverResponse = new byte[20], serverChallenge = new byte[16];
            Array.Copy(decServerChallengeResponse, serverResponse, serverResponse.Length);
            Array.Copy(decServerChallengeResponse, 20, serverChallenge, 0, serverChallenge.Length);
            Debug.WriteLine("serverResponse: " + BytesToHex(serverResponse));
            Debug.WriteLine("server challenge: " + BytesToHex(serverChallenge));

            // Using another 16 bytes secret, compute a challenge response hash using the secret, our cert sig, and the challenge
            byte[] clientSecret = GenerateRandomBytes(16);
            Debug.WriteLine("Client secret: " + BytesToHex(clientSecret));
            Debug.WriteLine("Client sig: " + BytesToHex((await provider.GetClientCertificate()).GetSignature()));

            byte[] challengeRespHash = ToSHA1Bytes(concatBytes(concatBytes(serverChallenge, (await provider.GetClientCertificate()).GetSignature()), clientSecret));
            Debug.WriteLine("Challenge SHA 1: " + BytesToHex(challengeRespHash));
            byte[] challengeRespEncrypted = EncryptAes(challengeRespHash, aesKey);
            XmlQuery secretResp = new XmlQuery(nv.BaseUrl +
                    "/pair?uniqueid=" + uniqueId + "&devicename=roth&updateState=1&serverchallengeresp=" + BytesToHex(challengeRespEncrypted));
            result = await secretResp.ReadXmlElement("paired");
            if (result == null || !result.Equals("1"))
            {
                await Unpair(nv); 
                return false;
            }

            // Get the server's signed secret
            byte[] serverSecretResp = HexToBytes(await secretResp.ReadXmlElement("pairingsecret"));
            byte[] serverSecret = new byte[16]; byte[] serverSignature = new byte[256]; 
            Array.Copy(serverSecretResp, 0, serverSecret, 0, 16);
            Array.Copy(serverSecretResp, 16, serverSignature, 0, 256);

            // Ensure the authenticity of the data
            if (!VerifySignature(serverSecret, serverSignature, serverCert))
            {
                // Cancel the pairing process
                await Unpair(nv); 
                // Looks like a MITM
                return false;
            }

            // Ensure the server challenge matched what we expected (aka the PIN was correct)
            byte[] serverChallengeRespHash = ToSHA1Bytes(concatBytes(concatBytes(randomChallenge, serverCert.GetSignature()), serverSecret));
            if (!serverChallengeRespHash.SequenceEqual(serverResponse))
            {
                // Cancel the pairing process
                await Unpair(nv); 
                // Probably got the wrong PIN
                return false;
            }

            // Send the server our signed secret
            byte[] clientPairingSecret = concatBytes(clientSecret, SignData(await provider.GetKeyPair(), clientSecret));
            XmlQuery clientSecretResp = new XmlQuery(nv.BaseUrl +
                    "/pair?uniqueid=" + uniqueId + "&devicename=roth&updateState=1&clientpairingsecret=" + BytesToHex(clientPairingSecret));
            result = await clientSecretResp.ReadXmlElement("paired");
            if (result == null || !result.Equals("1"))
            {
                await Unpair(nv);
                return false; 
            }

            // Do the initial challenge (seems neccessary for us to show as paired)
            XmlQuery pairChallenge = new XmlQuery(nv.BaseUrl + "/pair?uniqueid=" + uniqueId + "&devicename=roth&updateState=1&phrase=pairchallenge");

            result = await pairChallenge.ReadXmlElement("paired");
            if (result == null || !result.Equals("1"))
            {
                await Unpair(nv);
                return false; 
            }
            return true; 
        }