public Conclusion MembootKexec(Tasker tasker, Object syncObject = null) { tasker.SetStatus(Resources.Membooting); if (!hakchi.Shell.IsOnline) { return(Conclusion.Abort); } // load appropriate kernel byte[] kernel; if (stockKernel != null && stockKernel.Length > 0) { kernel = stockKernel.ToArray(); } else { stockKernel = null; kernel = hakchi.Hmod.GetMembootImage().ToArray(); } // memboot using kexec (no way to force clovershell or shell) try { hakchi.Shell.ExecuteSimple("uistop"); hakchi.Shell.ExecuteSimple("mkdir -p /tmp/kexec/", throwOnNonZero: true); hakchi.UploadFile( Path.Combine(Program.BaseDirectoryInternal, "tools", "arm", "kexec.static"), "/tmp/kexec/kexec"); hakchi.UploadFile( Path.Combine(Program.BaseDirectoryInternal, "tools", "arm", "unpackbootimg.static"), "/tmp/kexec/unpackbootimg"); TrackableStream kernelStream = new TrackableStream(kernel); kernelStream.OnProgress += tasker.OnProgress; hakchi.Shell.Execute( command: "cat > /tmp/kexec/boot.img; cd /tmp/kexec/; ./unpackbootimg -i boot.img", stdin: kernelStream, throwOnNonZero: true ); hakchi.Shell.ExecuteSimple("cd /tmp/kexec/ && ./kexec -l -t zImage boot.img-zImage \"--command-line=$(cat boot.img-cmdline)\" --ramdisk=boot.img-ramdisk.gz --atags", 0, true); hakchi.Shell.ExecuteSimple("cd /tmp/; umount -ar", 0); try { hakchi.Shell.ExecuteSimple("/tmp/kexec/kexec -e", 100); } catch { } // no-op } catch { try { hakchi.Shell.ExecuteSimple("uistart"); } catch { } // no-op throw; } return(Conclusion.Success); }
public static TaskFunc FlashUboot(Fel.UbootType type) { return((Tasker tasker, Object syncObject) => { tasker.SetStatus(Resources.FlashingUboot); TrackableStream uboot; if (type == Fel.UbootType.Normal) { uboot = new TrackableStream(Resources.uboot); } else { uboot = new TrackableStream(Resources.ubootSD); } if (uboot.Length > 655360) { throw new Exception(Resources.InvalidUbootSize + " " + uboot.Length); } uboot.OnProgress += tasker.OnProgress; hakchi.Shell.Execute("cat > /uboot.bin", uboot, null, null, 0, true); MemoryStream flashLog = new MemoryStream(); var splitStream = new SplitterStream(flashLog).AddStreams(Program.debugStreams); if (hakchi.Shell.Execute("hakchi flashBoot2 /uboot.bin 8 5", null, splitStream) != 0) { using (var sr = new StreamReader(flashLog)) { throw new Exception(sr.ReadToEnd()); } } return Conclusion.Success; }); }
public static Conclusion Memboot(Tasker tasker, Object syncObject = null) { tasker.SetStatus(Resources.Membooting); if (!hakchi.Shell.IsOnline) { return(Conclusion.Abort); } // get kernel image (only custom kernel with this method) byte[] kernel = hakchi.Hmod.GetMembootImage().ToArray(); // override arguments string addedArgs = ""; if (ConfigIni.Instance.ForceClovershell) { addedArgs = " hakchi-clovershell"; } else if (ConfigIni.Instance.ForceNetwork) { addedArgs = " hakchi-shell"; } // use detached-fallback script and up-to-date boot.img try { hakchi.Shell.ExecuteSimple("uistop"); hakchi.Shell.ExecuteSimple("mkdir -p /tmp/kexec/", throwOnNonZero: true); hakchi.UploadFile( Path.Combine(Program.BaseDirectoryInternal, "tools", "arm", "kexec.static"), "/tmp/kexec/kexec"); hakchi.UploadFile( Path.Combine(Program.BaseDirectoryInternal, "tools", "arm", "detached-fallback"), "/tmp/kexec/detached-fallback"); TrackableStream kernelStream = new TrackableStream(kernel); kernelStream.OnProgress += tasker.OnProgress; hakchi.UploadFile(kernelStream, "/tmp/kexec/boot.img", false); try { hakchi.Shell.ExecuteSimple("cd /tmp/kexec/; /bin/sh /tmp/kexec/detached-fallback recovery /tmp/kexec/boot.img" + addedArgs, 100); } catch { } // no-op } catch { try { hakchi.Shell.ExecuteSimple("uistart"); } catch { } // no-op throw; } return(Conclusion.Success); }
public static TaskFunc TransferBaseHmods(string transferPath = "/hakchi/transfer") { return((Tasker tasker, Object syncObject) => { tasker.SetStatus(Resources.TransferringMods); var escapedTransferPath = Shared.EscapeShellArgument(transferPath); var hmodStream = new TrackableStream(Resources.baseHmods); hmodStream.OnProgress += tasker.OnProgress; hakchi.Shell.Execute($"mkdir -p {escapedTransferPath}", null, null, null, 0, true); hakchi.Shell.Execute($"tar -xvC {escapedTransferPath}", hmodStream, null, null, 0, true); return Conclusion.Success; }); }
public static TaskFunc TransferBaseHmods(string transferPath = "/hakchi/transfer") { return((Tasker tasker, Object syncObject) => { tasker.SetStatus(Resources.TransferringMods); var escapedTransferPath = Shared.EscapeShellArgument(transferPath); using (var baseHmods = File.OpenRead(Path.Combine(Program.BaseDirectoryInternal, "basehmods.tar"))) using (var hmodStream = new TrackableStream(baseHmods)) { hmodStream.OnProgress += tasker.OnProgress; hakchi.Shell.Execute($"mkdir -p {escapedTransferPath}", null, null, null, 0, true); hakchi.Shell.Execute($"tar -xvC {escapedTransferPath} --exclude='./hakchi.hmod'", hmodStream, null, null, 0, true); } return Conclusion.Success; }); }
public static TaskFunc TransferHmod(string transferPath, string hmod) { return((Tasker tasker, Object syncObject) => { tasker.SetStatus(Resources.TransferringMods); var modName = hmod + ".hmod"; foreach (var dir in Shared.hmodDirectories) { string hmodHakchiPath = Shared.EscapeShellArgument($"{transferPath}/{modName}"); if (Directory.Exists(Path.Combine(dir, modName))) { hakchi.Shell.ExecuteSimple($"mkdir -p {hmodHakchiPath}"); using (var hmodTar = new TarStream(Path.Combine(dir, modName))) { if (hmodTar.Length > 0) { using (TrackableStream hmodStream = new TrackableStream(hmodTar)) { hmodStream.OnProgress += tasker.OnProgress; hakchi.Shell.Execute($"tar -xvC {hmodHakchiPath}", hmodStream, null, null, 0, true); } } } break; } if (File.Exists(Path.Combine(dir, modName))) { hakchi.Shell.ExecuteSimple($"mkdir -p {hmodHakchiPath}"); using (var hmodStream = new TrackableFileStream(Path.Combine(dir, modName), FileMode.Open)) { hmodStream.OnProgress += tasker.OnProgress; hakchi.Shell.Execute($"tar -xzvC {hmodHakchiPath}", hmodStream, null, null, 0, true); } break; } } return Conclusion.Success; }); }
public static TaskFunc ExtractArchive(String fileName, string destinationFolder) { return((Tasker tasker, Object syncObject) => { using (var fileStream = File.OpenRead(fileName)) using (var trackStream = new TrackableStream(fileStream)) { trackStream.OnProgress += tasker.OnProgress; using (var reader = ReaderFactory.Open(trackStream)) { reader.WriteAllToDirectory(destinationFolder, new SharpCompress.Common.ExtractionOptions() { ExtractFullPath = true, Overwrite = true, PreserveFileTime = true }); return Conclusion.Success; } } }); }
public override Stream OpenWrite() { string tempFileName = Path.GetTempFileName(); var trackableStream = new TrackableStream(new FileStream(tempFileName, FileMode.Create, FileAccess.ReadWrite, FileShare.None)); trackableStream.Closing += delegate(object source, EventArgs e) { var innerStream = ((TrackableStream)source).InnerStream; innerStream.Seek(0L, SeekOrigin.Begin); Write(innerStream); }; trackableStream.Closed += delegate { var fileInfo = new FileInfo(tempFileName); if (fileInfo.Exists) { fileInfo.Delete(); } }; return(trackableStream); }
public Conclusion Memboot(Tasker tasker, Object syncObject = null) { // load appropriate kernel byte[] kernel; if (stockKernel != null && stockKernel.Length > 0) { kernel = stockKernel.ToArray(); } else { stockKernel = null; // double-safety kernel = hakchi.GetMembootImage().ToArray(); } tasker.SetStatus(Resources.Membooting); if (hakchi.Shell.IsOnline) { // override arguments string addedArgs = ConfigIni.Instance.ForceClovershell ? " hakchi-clovershell" : ""; // skip to built-in recovery if running latest version if (stockKernel == null && hakchi.CanInteract && !hakchi.SystemEligibleForRootfsUpdate()) { if (hakchi.Shell.Execute("[ -e /bin/detached ]") == 0) // detached recovery function? { try { hakchi.Shell.ExecuteSimple("/bin/detached recovery" + addedArgs, 100); } catch { } // no-op return(Conclusion.Success); } } try { hakchi.Shell.ExecuteSimple("uistop"); hakchi.Shell.ExecuteSimple("mkdir -p /tmp/kexec/", throwOnNonZero: true); hakchi.UploadFile( Path.Combine(Program.BaseDirectoryInternal, "tools", "arm", "kexec.static"), "/tmp/kexec/kexec"); TrackableStream kernelStream = new TrackableStream(kernel); kernelStream.OnProgress += tasker.OnProgress; if (stockKernel == null) { hakchi.UploadFile( Path.Combine(Program.BaseDirectoryInternal, "tools", "arm", "detached-fallback"), "/tmp/kexec/detached-fallback"); hakchi.UploadFile(kernelStream, "/tmp/kexec/boot.img", false); try { hakchi.Shell.ExecuteSimple("cd /tmp/kexec/; /bin/sh /tmp/kexec/detached-fallback recovery /tmp/kexec/boot.img" + addedArgs, 100); } catch { } // no-op } else { hakchi.UploadFile( Path.Combine(Program.BaseDirectoryInternal, "tools", "arm", "unpackbootimg.static"), "/tmp/kexec/unpackbootimg"); hakchi.Shell.Execute( command: "cat > /tmp/kexec/boot.img; cd /tmp/kexec/; ./unpackbootimg -i boot.img", stdin: kernelStream, throwOnNonZero: true ); hakchi.Shell.ExecuteSimple("cd /tmp/kexec/ && ./kexec -l -t zImage boot.img-zImage \"--command-line=$(cat boot.img-cmdline)\" --ramdisk=boot.img-ramdisk.gz --atags", 0, true); hakchi.Shell.ExecuteSimple("cd /tmp/; umount -ar", 0); try { hakchi.Shell.ExecuteSimple("/tmp/kexec/kexec -e", 100); } catch { } // no-op } } catch { try { hakchi.Shell.ExecuteSimple("uistart"); } catch { } // no-op throw; } } else { // check and adjust kernel size var size = Shared.CalcKernelSize(kernel); if (size > kernel.Length || size > Fel.transfer_max_size) { throw new Exception(Resources.InvalidKernelSize + " " + size); } size = (size + Fel.sector_size - 1) / Fel.sector_size; size = size * Fel.sector_size; if (kernel.Length != size) { var newK = new byte[size]; Array.Copy(kernel, newK, kernel.Length); kernel = newK; } // clovershell override "hex-edit" boot.img if (stockKernel == null && ConfigIni.Instance.ForceClovershell) { kernel.InPlaceStringEdit(64, 512, 0, (string str) => { if (str.IndexOf("hakchi-shell") != -1) { return(str.Replace("hakchi-shell", "hakchi-clovershell")); } else { return(str + " hakchi-clovershell"); } }); } // upload kernel through fel int progress = 0; int maxProgress = (int)((double)kernel.Length / (double)67000 + 50); fel.WriteMemory(Fel.transfer_base_m, kernel, delegate(Fel.CurrentAction action, string command) { switch (action) { case Fel.CurrentAction.WritingMemory: tasker.SetStatus(Resources.UploadingKernel); break; } progress++; tasker.SetProgress(progress, maxProgress); } ); var bootCommand = string.Format("boota {0:x}", Fel.transfer_base_m); tasker.SetStatus(Resources.ExecutingCommand + " " + bootCommand); fel.RunUbootCmd(bootCommand, true); } return(Conclusion.Success); }
public void Load() { string[] list = new string[] { }; var repoResponse = HTTPHelpers.GetHTTPResponseStreamAsync(RepositoryPackURL); repoResponse.Wait(); if (repoResponse.Result.Status == HttpStatusCode.OK) { // Start pack processing var tempDict = new Dictionary <string, Item>(); var trackableStream = new TrackableStream(repoResponse.Result.Stream); trackableStream.OnProgress += (long current, long total) => { RepositoryProgress?.Invoke(current, repoResponse.Result.Length); }; using (var reader = ReaderFactory.Open(trackableStream)) { while (reader.MoveToNextEntry()) { if (Regex.Match(reader.Entry.Key, @"^(?:\./)?list$", RegexOptions.IgnoreCase).Success) { list = Regex.Replace(StreamToString(reader.OpenEntryStream()), @"[\r\n]+", "\n").Split("\n"[0]); } if (Regex.Match(reader.Entry.Key, @"^(?:\./)?readme.md$", RegexOptions.IgnoreCase).Success) { Readme = StreamToString(reader.OpenEntryStream()); } var match = Regex.Match(reader.Entry.Key, @"^(?:\./)?([^/]+)/(extract|link|md5|sha1|readme(?:\.(?:md|txt)?)?)$", RegexOptions.IgnoreCase); if (match.Success) { var mod = match.Groups[1].ToString(); var fileName = match.Groups[2].ToString(); Item item; if (!tempDict.TryGetValue(mod, out item)) { item = new Item(mod); tempDict.Add(mod, item); } switch (fileName.ToLower()) { case "extract": item.setExtract(true); break; case "link": item.setURL(StreamToString(reader.OpenEntryStream()).Trim()); break; case "md5": item.setMD5(StreamToString(reader.OpenEntryStream()).Trim()); break; case "sha1": item.setSHA1(StreamToString(reader.OpenEntryStream()).Trim()); break; case "readme": case "readme.txt": case "readme.md": item.setReadme(StreamToString(reader.OpenEntryStream()).Trim(), fileName.EndsWith(".md")); break; } } } } if (list.Length == 0) { list = tempDict.Keys.ToArray(); } foreach (var key in tempDict.Keys.ToArray()) { var item = tempDict[key]; if (list.Contains(key)) { Items.Add(item); } tempDict.Remove(key); } tempDict.Clear(); tempDict = null; Items.Sort((x, y) => x.Name.CompareTo(y.Name)); RepositoryLoaded?.Invoke(Items.ToArray()); return; // End pack processing } var taskList = HTTPHelpers.GetHTTPResponseStringAsync(RepositoryListURL); taskList.Wait(); list = (taskList.Result ?? "").Split("\n"[0]); for (int i = 0; i < list.Length; i++) { var mod = list[i]; Item item = new Item(mod); var taskExtract = HTTPHelpers.GetHTTPStatusCodeAsync($"{RepositoryURL}{mod}/extract"); var taskURL = HTTPHelpers.GetHTTPResponseStringAsync($"{RepositoryURL}{mod}/link"); var taskMD5 = HTTPHelpers.GetHTTPResponseStringAsync($"{RepositoryURL}{mod}/md5"); var taskSHA1 = HTTPHelpers.GetHTTPResponseStringAsync($"{RepositoryURL}{mod}/sha1"); taskExtract.Wait(); taskURL.Wait(); taskMD5.Wait(); taskSHA1.Wait(); item.setExtract(taskExtract.Result == HttpStatusCode.OK); item.setURL(taskURL.Result); item.setMD5(taskMD5.Result); item.setSHA1(taskSHA1.Result); for (var x = 0; x < HmodReadme.readmeFiles.Length; x++) { var taskReadme = HTTPHelpers.GetHTTPResponseStringAsync($"{RepositoryURL}{mod}/{HmodReadme.readmeFiles[x]}"); taskReadme.Wait(); if (taskReadme.Result != null) { item.setReadme(taskReadme.Result, HmodReadme.readmeFiles[x].EndsWith(".md")); break; } } Items.Add(item); RepositoryProgress?.Invoke(i + 1, list.Length); } RepositoryLoaded?.Invoke(Items.ToArray()); return; }
public static TaskFunc DownloadFile(string url, string fileName, bool successOnError = false, bool onlyLatest = false, DateTime?comparisonDate = null, bool gunzip = false) { return((Tasker tasker, Object sync) => { Conclusion result = Conclusion.Success; Debug.WriteLine($"Downloading: {url} to {fileName}"); if (comparisonDate == null && File.Exists(fileName)) { comparisonDate = File.GetLastWriteTime(fileName); } var wr = HttpWebRequest.Create(url) as HttpWebRequest; wr.UserAgent = HakchiWebClient.UserAgent; try { using (var response = wr.GetResponse()) { var headers = response.Headers; var contentLength = headers.AllKeys.Contains("Content-Length") ? response.ContentLength : 0; var date = DateTime.Now; if (headers.AllKeys.Contains("Last-Modified")) { date = DateTime.ParseExact(headers["Last-Modified"], "ddd, dd MMM yyyy HH:mm:ss 'GMT'", CultureInfo.InvariantCulture.DateTimeFormat, DateTimeStyles.AssumeUniversal); if (onlyLatest && comparisonDate != null && comparisonDate >= date) { response.Close(); return Conclusion.Success; } } using (var webStream = response.GetResponseStream()) using (var trackableStream = new TrackableStream(webStream)) { trackableStream.OnProgress += (progress, max) => { tasker.SetStatus($"{Shared.SizeSuffix(progress)}{(contentLength > 0 ? $" / {Shared.SizeSuffix(contentLength)}" : "")}"); tasker.SetProgress(progress, contentLength); }; using (var outputFile = File.Create(fileName)) { if (gunzip) { using (var gzipStream = new GZipStream(trackableStream, CompressionMode.Decompress)) { gzipStream.CopyTo(outputFile); } } else { trackableStream.CopyTo(outputFile); } } File.SetLastWriteTime(fileName, date); } } } catch (ThreadAbortException) { } catch (Exception e) { if (!successOnError) { throw e; } } return result; }); }
public static TaskFunc GameCopyTask(FoundGame game) { return((Tasker tasker, Object sync) => { if (game.Desktop.Code.StartsWith("CLV-")) { long dataTransferred = 0; var destinationPath = Path.Combine(NesApplication.GamesDirectory, game.Desktop.Code); tasker?.SetStatus($"{game.Desktop.Name}"); if (Directory.Exists(destinationPath)) { Directory.Delete(destinationPath, true); } Directory.CreateDirectory(destinationPath); foreach (var folder in hakchi.Shell.ExecuteSimple($"cd {Shared.EscapeShellArgument(game.RemotePath)}; find -type d").Split('\n')) { Directory.CreateDirectory(Path.Combine(destinationPath, folder)); } FtpClient ftp = null; if (hakchi.Shell is INetworkShell) { ftp = new FtpClient(new Uri($"ftp://{(hakchi.Shell as INetworkShell).IPAddress}"), new NetworkCredential("root", "root")); } foreach (Match match in new Regex(@"^(\d+)\s*\./(.*)$", RegexOptions.Multiline).Matches(hakchi.Shell.ExecuteSimple($"cd {Shared.EscapeShellArgument(game.RemotePath)}; find -type f -exec du {"{}"} \\;"))) { var size = long.Parse(match.Groups[1].Value) * 1024; var filename = match.Groups[2].Value; using (var file = File.Create(Path.Combine(destinationPath, filename))) using (var tracker = new TrackableStream(file)) { tracker.OnProgress += (long transferred, long length) => { var totalTransferred = Math.Min(dataTransferred + transferred, game.Size); tasker?.SetProgress(totalTransferred, game.Size); tasker?.SetStatus($"{game.Desktop.Name} ({Shared.SizeSuffix(totalTransferred, 2)} / {Shared.SizeSuffix(game.Size, 2)})"); }; if (hakchi.Shell is INetworkShell) { using (var ftpStream = ftp.Retr($"{game.RemotePath}/{filename}")) { ftpStream.CopyTo(tracker); } } else { hakchi.Shell.Execute($"cat {Shared.EscapeShellArgument($"{game.RemotePath}/{filename}")}", null, tracker, throwOnNonZero: true); } dataTransferred += size; } } ftp?.Dispose(); ftp = null; game.Desktop.Save(Path.Combine(destinationPath, $"{game.Desktop.Code}.desktop")); if (!ConfigIni.Instance.SelectedGames.Contains(game.Desktop.Code)) { ConfigIni.Instance.SelectedGames.Add(game.Desktop.Code); } return Conclusion.Success; } return Conclusion.Error; }); }