private IEnumerator GetBeatmapFromLocation(Uri uri) { // We will extract the contents of the zip to the temp directory, so we will save the zip in memory. DownloadHandlerBuffer downloadHandler = new DownloadHandlerBuffer(); // Create our web request, and set our handler. UnityWebRequest request = UnityWebRequest.Get(uri); request.downloadHandler = downloadHandler; // Change our User-Agent so BeatSaver can download our map request.SetRequestHeader("User-Agent", $"{Application.productName}/{Application.version}"); // Set progress bar state. PersistentUI.Instance.LevelLoadSlider.gameObject.SetActive(true); PersistentUI.Instance.LevelLoadSlider.value = 0; PersistentUI.Instance.LevelLoadSliderLabel.text = $"Downloading file... Starting download..."; var operation = request.SendWebRequest(); while (!request.isDone) { // Grab Content-Length, which is the length of the downloading file, to use for progress bar. if (int.TryParse(request.GetResponseHeader("Content-Length"), out int length)) { float progress = downloadHandler.data.Length / (float)length; PersistentUI.Instance.LevelLoadSlider.value = progress; float percent = progress * 100; PersistentUI.Instance.LevelLoadSliderLabel.text = $"Downloading file... {percent:F2}% complete."; } else { // Just gives the bar something to do until we get the content length. PersistentUI.Instance.LevelLoadSlider.value = (Mathf.Sin(Time.time) / 2) + 0.5f; } // Cancel loading if an error has occurred. if (request.isHttpError || request.isNetworkError) { CancelTempLoader(request.error); yield break; } yield return(new WaitForEndOfFrame()); } // Check one more time to be safe. if (request.isHttpError || request.isNetworkError) { CancelTempLoader(request.error); yield break; } // Wahoo! We are done. Let's grab our downloaded data. byte[] downloaded = downloadHandler.data; // If the request failed, our downloaded bytes will be null. Let's check that. if (downloaded != null) { PersistentUI.Instance.LevelLoadSlider.value = 1; PersistentUI.Instance.LevelLoadSliderLabel.text = "Extracting contents..."; yield return(new WaitForEndOfFrame()); try { // Slap our downloaded bytes into a memory stream and slap that into a ZipArchive. MemoryStream stream = new MemoryStream(downloaded); ZipArchive archive = new ZipArchive(stream, ZipArchiveMode.Read); // Create the directory for our song to go to. // Path.GetTempPath() should be compatible with Windows and UNIX. // See Microsoft docs on it. string directory = $"{Path.GetTempPath()}ChroMapper Temp Loader\\{request.GetHashCode()}"; if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } // Extract our zipped file into this directory. archive.ExtractToDirectory(directory); // Dispose our downloaded bytes, we don't need them. downloadHandler.Dispose(); // Try and get a BeatSaberSong out of what we've downloaded. BeatSaberSong song = BeatSaberSong.GetSongFromFolder(directory); if (song != null) { PersistentUI.Instance.LevelLoadSliderLabel.text = "Loading song..."; BeatSaberSongContainer.Instance.song = song; } else { CancelTempLoader("Could not obtain a valid Beatmap from the downloaded content."); } } catch (Exception e) { // Uh oh, an error occurred. // Let's see if it is due to user error, or a genuine error on ChroMapper's part. switch (e.GetType().Name) { // InvalidDataException means that the ZipArchive cannot be created. // ChroMapper tried to download something that is not a zip. case nameof(InvalidDataException): CancelTempLoader($"Downloaded content was not a valid zip."); break; // Default case is a genuine error, let's print what it has to say. default: CancelTempLoader($"An unknown error ({e.GetType().Name}) has occurred:\n\n{e.Message}"); break; } } } else { CancelTempLoader("Downloaded bytes is somehow null, yet the request was successfully completed. WTF!?"); } }