diff --git a/EasyInstallerV2-1.0/EasyInstallerV2-1.0/EasyInstallerV2.sln b/EasyInstallerV2-1.0/EasyInstallerV2-1.0/EasyInstallerV2.sln new file mode 100644 index 0000000..65b5ecb --- /dev/null +++ b/EasyInstallerV2-1.0/EasyInstallerV2-1.0/EasyInstallerV2.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.2.32616.157 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyInstallerV2", "EasyInstallerV2\EasyInstallerV2.csproj", "{0A977DC7-8EB0-4840-A1B7-791156F00F9D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0A977DC7-8EB0-4840-A1B7-791156F00F9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0A977DC7-8EB0-4840-A1B7-791156F00F9D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0A977DC7-8EB0-4840-A1B7-791156F00F9D}.Debug|x64.ActiveCfg = Debug|x64 + {0A977DC7-8EB0-4840-A1B7-791156F00F9D}.Debug|x64.Build.0 = Debug|x64 + {0A977DC7-8EB0-4840-A1B7-791156F00F9D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0A977DC7-8EB0-4840-A1B7-791156F00F9D}.Release|Any CPU.Build.0 = Release|Any CPU + {0A977DC7-8EB0-4840-A1B7-791156F00F9D}.Release|x64.ActiveCfg = Release|x64 + {0A977DC7-8EB0-4840-A1B7-791156F00F9D}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {97F708CC-110B-4CC6-BAEE-59E698ADB840} + EndGlobalSection +EndGlobal diff --git a/EasyInstallerV2-1.0/EasyInstallerV2-1.0/EasyInstallerV2/EasyInstallerV2.csproj b/EasyInstallerV2-1.0/EasyInstallerV2-1.0/EasyInstallerV2/EasyInstallerV2.csproj new file mode 100644 index 0000000..426d772 --- /dev/null +++ b/EasyInstallerV2-1.0/EasyInstallerV2-1.0/EasyInstallerV2/EasyInstallerV2.csproj @@ -0,0 +1,24 @@ + + + + Exe + net6.0-windows + enable + enable + AnyCPU;x64 + Icon1.ico + + + + + + + + + + + + + + + diff --git a/EasyInstallerV2-1.0/EasyInstallerV2-1.0/EasyInstallerV2/EasyInstallerV2.csproj.user b/EasyInstallerV2-1.0/EasyInstallerV2-1.0/EasyInstallerV2/EasyInstallerV2.csproj.user new file mode 100644 index 0000000..156d305 --- /dev/null +++ b/EasyInstallerV2-1.0/EasyInstallerV2-1.0/EasyInstallerV2/EasyInstallerV2.csproj.user @@ -0,0 +1,6 @@ + + + + <_LastSelectedProfileId>C:\Users\Admin\Downloads\EasyInstallerV2-1.0\EasyInstallerV2-1.0\EasyInstallerV2\Properties\PublishProfiles\FolderProfile.pubxml + + \ No newline at end of file diff --git a/EasyInstallerV2-1.0/EasyInstallerV2-1.0/EasyInstallerV2/Icon1.ico b/EasyInstallerV2-1.0/EasyInstallerV2-1.0/EasyInstallerV2/Icon1.ico new file mode 100644 index 0000000..12dac91 Binary files /dev/null and b/EasyInstallerV2-1.0/EasyInstallerV2-1.0/EasyInstallerV2/Icon1.ico differ diff --git a/EasyInstallerV2-1.0/EasyInstallerV2-1.0/EasyInstallerV2/Program.cs b/EasyInstallerV2-1.0/EasyInstallerV2-1.0/EasyInstallerV2/Program.cs new file mode 100644 index 0000000..1937471 --- /dev/null +++ b/EasyInstallerV2-1.0/EasyInstallerV2-1.0/EasyInstallerV2/Program.cs @@ -0,0 +1,171 @@ +using Newtonsoft.Json; +using System.IO.Compression; +using System.Net; + +namespace EasyInstallerV2 +{ + class Program + { + public const string BASE_URL = "https://manifest.simplyblk.xyz"; + private const int CHUNK_SIZE = 536870912 / 8; + + class ChunkedFile + { + public List ChunksIds = new(); + public String File = String.Empty; + public long FileSize = 0; + } + + class ManifestFile + { + public String Name = String.Empty; + public List Chunks = new(); + public long Size = 0; + } + + static string FormatBytesWithSuffix(long bytes) + { + string[] Suffix = { "B", "KB", "MB", "GB", "TB" }; + int i; + double dblSByte = bytes; + for (i = 0; i < Suffix.Length && bytes >= 1024; i++, bytes /= 1024) + { + dblSByte = bytes / 1024.0; + } + + return String.Format("{0:0.##} {1}", dblSByte, Suffix[i]); + } + + static async Task Download(ManifestFile manifest, string version, string resultPath) + { + long totalBytes = manifest.Size; + long completedBytes = 0; + int progressLength = 0; + + if (!Directory.Exists(resultPath)) + Directory.CreateDirectory(resultPath); + + SemaphoreSlim semaphore = new SemaphoreSlim(12); + + await Task.WhenAll(manifest.Chunks.Select(async chunkedFile => + { + await semaphore.WaitAsync(); + + try + { + WebClient httpClient = new WebClient(); + + string outputFilePath = Path.Combine(resultPath, chunkedFile.File); + var fileInfo = new FileInfo(outputFilePath); + + if (File.Exists(outputFilePath) && fileInfo.Length == chunkedFile.FileSize) + { + completedBytes += chunkedFile.FileSize; + semaphore.Release(); + return; + } + + Directory.CreateDirectory(Path.GetDirectoryName(outputFilePath)); + + using (FileStream outputStream = File.OpenWrite(outputFilePath)) + { + foreach (int chunkId in chunkedFile.ChunksIds) + { + retry: + + try + { + string chunkUrl = BASE_URL + $"/{version}/" + chunkId + ".chunk"; + var chunkData = await httpClient.DownloadDataTaskAsync(chunkUrl); + + byte[] chunkDecompData = new byte[CHUNK_SIZE + 1]; + int bytesRead; + long chunkCompletedBytes = 0; + + MemoryStream memoryStream = new MemoryStream(chunkData); + GZipStream decompressionStream = new GZipStream(memoryStream, CompressionMode.Decompress); + + while ((bytesRead = await decompressionStream.ReadAsync(chunkDecompData, 0, chunkDecompData.Length)) > 0) + { + await outputStream.WriteAsync(chunkDecompData, 0, bytesRead); + Interlocked.Add(ref completedBytes, bytesRead); + Interlocked.Add(ref chunkCompletedBytes, bytesRead); + + double progress = (double)completedBytes / totalBytes * 100; + string progressMessage = $"\rDownloaded: {FormatBytesWithSuffix(completedBytes)} / {FormatBytesWithSuffix(totalBytes)} ({progress:F2}%)"; + + int padding = progressLength - progressMessage.Length; + if (padding > 0) + progressMessage += new string(' ', padding); + + Console.Write(progressMessage); + progressLength = progressMessage.Length; + } + + memoryStream.Close(); + decompressionStream.Close(); + } + catch (Exception ex) + { + goto retry; + } + } + } + } + finally + { + semaphore.Release(); + } + })); + + Console.WriteLine("\n\nFinished Downloading.\nPress any key to exit!"); + Thread.Sleep(100); + Console.ReadKey(); + } + + static void Main(string[] args) + { + var httpClient = new WebClient(); + + List versions = JsonConvert.DeserializeObject>(httpClient.DownloadString(BASE_URL + "/versions.json")); + + Console.Clear(); + + Console.Title = "EasyInstaller V2 made by Ender & blk"; + Console.Write("\n\nEasyInstaller V2 made by Ender & blk\n\n"); + Console.WriteLine("\nAvailable manifests:"); + + for (int i = 0; i < versions.Count; i++) + { + Console.WriteLine($" * [{i}] {versions[i]}"); + } + + Console.WriteLine($"\nTotal: {versions.Count}"); + + Console.Write("Please enter the number before the Build Version to select it: "); + var targetVersionStr = Console.ReadLine(); + var targetVersionIndex = 0; + + try + { + targetVersionIndex = int.Parse(targetVersionStr); + } + catch (Exception ex) + { + Main(args); + } + + if (!(targetVersionIndex >= 0 && targetVersionIndex < versions.Count)) + Main(args); + + var targetVersion = versions[targetVersionIndex].Split("-")[1]; + var manifest = JsonConvert.DeserializeObject(httpClient.DownloadString(BASE_URL + $"/{targetVersion}/{targetVersion}.manifest")); + + Console.Write("Please enter a game folder location: "); + var targetPath = Console.ReadLine(); + Console.Write("\n"); + + Download(manifest, targetVersion, targetPath).GetAwaiter().GetResult(); + } + } +} \ No newline at end of file diff --git a/EasyInstallerV2-1.0/EasyInstallerV2-1.0/EasyInstallerV2/Properties/PublishProfiles/FolderProfile.pubxml b/EasyInstallerV2-1.0/EasyInstallerV2-1.0/EasyInstallerV2/Properties/PublishProfiles/FolderProfile.pubxml new file mode 100644 index 0000000..4b29c13 --- /dev/null +++ b/EasyInstallerV2-1.0/EasyInstallerV2-1.0/EasyInstallerV2/Properties/PublishProfiles/FolderProfile.pubxml @@ -0,0 +1,17 @@ + + + + + Release + x64 + bin\Release\net6.0-windows\publish\win-x64\ + FileSystem + net6.0-windows + win-x64 + false + true + false + + \ No newline at end of file diff --git a/EasyInstallerV2-1.0/EasyInstallerV2-1.0/EasyInstallerV2/Properties/PublishProfiles/FolderProfile.pubxml.user b/EasyInstallerV2-1.0/EasyInstallerV2-1.0/EasyInstallerV2/Properties/PublishProfiles/FolderProfile.pubxml.user new file mode 100644 index 0000000..5de0331 --- /dev/null +++ b/EasyInstallerV2-1.0/EasyInstallerV2-1.0/EasyInstallerV2/Properties/PublishProfiles/FolderProfile.pubxml.user @@ -0,0 +1,10 @@ + + + + + True|2024-05-20T13:42:32.9559570Z;False|2024-05-20T09:42:14.3536757-04:00;True|2024-05-20T09:41:56.8029605-04:00;True|2023-06-04T01:06:49.9843864-04:00;True|2023-06-04T01:01:13.4649588-04:00;True|2023-06-04T00:53:14.8161177-04:00;False|2023-06-04T00:49:47.5760062-04:00;False|2023-06-04T00:48:08.4918082-04:00;True|2023-06-04T00:47:35.6382492-04:00;True|2023-06-04T00:45:41.3367378-04:00;True|2023-06-04T00:43:34.9416852-04:00;True|2023-06-04T00:43:11.2497253-04:00;True|2023-06-04T00:42:40.5978756-04:00; + + + \ No newline at end of file diff --git a/EasyInstallerV2-1.0/EasyInstallerV2-1.0/README.md b/EasyInstallerV2-1.0/EasyInstallerV2-1.0/README.md new file mode 100644 index 0000000..d8bfecc --- /dev/null +++ b/EasyInstallerV2-1.0/EasyInstallerV2-1.0/README.md @@ -0,0 +1,7 @@ +# EasyInstallerV2 + +Credits to [Ender](https://github.com/Ender-0001/) for writing the code, [blk](https://github.com/simplyblk) for providing servers. + +Based off of [Kyiro's EasyInstaller](https://github.com/Kyiro/Fortnite-ManifestsArchive) + +Download [here](https://github.com/simplyblk/EasyInstallerV2/releases)