public static Conclusion BootHakchi(Tasker tasker, Object syncObject = null) { // Continue the hakchi boot process tasker.SetStatus(Resources.BootingHakchi); MemoryStream hakchiLogStream = new MemoryStream(); var splitStream = new SplitterStream(hakchiLogStream).AddStreams(Program.debugStreams); tasker.PushState(State.Waiting); try { hakchi.Shell.Execute("boot", null, splitStream, splitStream, 0, true); } catch { } tasker.PopState(); hakchiLogStream.Seek(0, SeekOrigin.Begin); string hakchiLog; using (StreamReader sr = new StreamReader(hakchiLogStream)) { hakchiLog = sr.ReadToEnd(); } foreach (string line in hakchiLog.Split(Convert.ToChar(0x0A))) { if (line.StartsWith("flash md5 mismatch! ")) { throw new Exception(line); } } 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 TaskFunc InstallHmods(string transferPath = "/tmp/hmods") { return((Tasker tasker, Object syncObject) => { tasker.SetStatus(Resources.InstallingMods); bool commandSucceeded = false; var splitStream = new SplitterStream(Program.debugStreams); commandSucceeded = hakchi.Shell.Execute($"hakchi packs_install {Shared.EscapeShellArgument(transferPath)}", null, splitStream, splitStream) == 0; return commandSucceeded ? Conclusion.Success : Conclusion.Error; }); }
public static TaskFunc FlashUboot(UbootType type) { return((Tasker tasker, Object syncObject) => { tasker.SetStatus(Resources.FlashingUboot); MemoryStream flashLog = new MemoryStream(); var splitStream = new SplitterStream(flashLog).AddStreams(Program.debugStreams); if (hakchi.Shell.Execute($"sntool sd {(type == UbootType.SD ? "enable" : "disable")}", null, splitStream, splitStream) != 0) { flashLog.Seek(0, SeekOrigin.Begin); using (var sr = new StreamReader(flashLog)) { throw new Exception(sr.ReadToEnd()); } } return Conclusion.Success; }); }
public static TaskFunc FormatDevice(string device) { Regex mke2fsHeaderRegex = new Regex(@"(Allocating group tables|Writing inode tables|Creating journal \(\d+ blocks\)|Writing superblocks and filesystem accounting information)", RegexOptions.Compiled); Regex mke2fsProgressRegex = new Regex(@"(\d+/\d+|done)", RegexOptions.Compiled); return((Tasker tasker, Object sync) => { using (EventStream formatProgress = new EventStream()) using (var splitStream = new SplitterStream(Program.debugStreams)) { splitStream.AddStreams(formatProgress); string currentHeading = null; formatProgress.OnData += (byte[] buffer) => { string data = Encoding.ASCII.GetString(buffer); MatchCollection matches = mke2fsHeaderRegex.Matches(data); if (matches.Count > 0) { currentHeading = matches[matches.Count - 1].Value; tasker?.SetStatus(currentHeading); } matches = mke2fsProgressRegex.Matches(data); if (matches.Count > 0 && currentHeading != null && currentHeading != "Writing superblocks and filesystem accounting information") { tasker?.SetStatus($"{currentHeading}: {matches[matches.Count - 1].Value}"); if (currentHeading == "Writing inode tables") { var inodes = matches[matches.Count - 1].Value.Split("/"[0]); tasker?.SetProgress(long.Parse(inodes[0]), long.Parse(inodes[1])); } } }; hakchi.Shell.Execute($"yes | mke2fs -t ext4 -L data -b 4K -m 0 -J size=4 -E stripe-width=32 -O ^huge_file,^metadata_csum {Shared.EscapeShellArgument(device)}", null, splitStream, splitStream, 0, true); splitStream.RemoveStream(formatProgress); return Conclusion.Success; } }); }
public static Conclusion CheckExternalStorage(Tasker tasker = null, Object syncObject = null) { tasker?.SetStatus("hakchi checkExtStorage"); using (var eventStream = new EventStream()) using (var splitStream = new SplitterStream()) { eventStream.OnData += (byte[] buffer) => { tasker.SetStatus(Encoding.UTF8.GetString(buffer)); }; splitStream.AddStreams(Program.debugStreams); splitStream.AddStreams(eventStream); hakchi.Shell.Execute("hakchi checkExtStorage", null, splitStream, splitStream); return(Conclusion.Success); } return(Conclusion.Error); }
public static TaskFunc ProcessNand(string nandDump, NandTasks task) { return((Tasker tasker, Object sync) => { return TempHelpers.doWithTempFolder((tempFolder) => { NandTasks[] validTasks = { NandTasks.DumpNand, NandTasks.DumpSystemPartition, NandTasks.DumpUserPartition, NandTasks.FlashSystemPartition, NandTasks.FlashUserPartition }; if (!validTasks.Contains(task)) { throw new ArgumentOutOfRangeException(Enum.GetName(typeof(NandTasks), task)); } if (task == NandTasks.FlashSystemPartition || task == NandTasks.FlashUserPartition) { var extension = new FileInfo(nandDump).Extension.ToLower(); if (extension == ".gz" || extension == ".tgz" || extension == ".xz" || extension == ".bz2") { var tempFilename = Path.Combine(tempFolder, "dump.bin"); using (var extractor = ArchiveFactory.Open(nandDump)) using (var entryStream = extractor.Entries.First().OpenEntryStream()) using (var extractedFile = File.Create(tempFilename)) { tasker.SetStatus(Resources.ExtractingToTemporaryFolder); entryStream.CopyTo(extractedFile); nandDump = tempFilename; } } } bool isHsqs = false; bool isExtFs = false; bool isTar = false; if (task == NandTasks.FlashSystemPartition && !((isHsqs = Files.CheckFileType.IsSquashFs(nandDump)) || (isExtFs = Files.CheckFileType.IsExtFs(nandDump)) || (isTar = Files.CheckFileType.IsTar(nandDump)))) { throw new Exception(Properties.Resources.InvalidHsqs); } if (task == NandTasks.FlashUserPartition) { if (!((isTar = Files.CheckFileType.IsTar(nandDump)) || (isExtFs = Files.CheckFileType.IsExtFs(nandDump)))) { throw new Exception(Properties.Resources.InvalidUserDataBackup); } } var nandInfo = Sunxi.NandInfo.GetNandInfo(); long partitionSize = 300 * 1024 * 1024; var splitStream = new SplitterStream(Program.debugStreams); string rootfsDevice = $"/dev/{nandInfo.GetRootfsPartition().Device}"; string osDecryptedDevice = rootfsDevice; string userDataDevice = $"/dev/{nandInfo.GetDataPartition().Device}"; bool hasKeyfile = hakchi.Shell.Execute("[ -f /key-file ]") == 0; if (hasKeyfile) { osDecryptedDevice = "/dev/mapper/root-crypt"; } bool systemIsHsqs = hakchi.Shell.ExecuteSimple($"dd if={osDecryptedDevice} bs=1 count=4", 0) == "HSQS"; switch (task) { case NandTasks.DumpSystemPartition: if (systemIsHsqs) { partitionSize = long.Parse(hakchi.Shell.ExecuteSimple($"echo $((($(hexdump -e '1/4 \"%u\"' -s $((0x28)) -n 4 {osDecryptedDevice})+0xfff)/0x1000))", throwOnNonZero: true).Trim()) * 4 * 1024; } else { partitionSize = long.Parse(hakchi.Shell.ExecuteSimple($"blockdev --getsize64 {osDecryptedDevice}", throwOnNonZero: true)); } break; case NandTasks.FlashSystemPartition: hakchi.Shell.Execute("hakchi umount_base", null, splitStream, splitStream); hakchi.Shell.Execute("umount /newroot"); if (hasKeyfile) { hakchi.Shell.Execute("cryptsetup close root-crypt"); hakchi.Shell.ExecuteSimple($"cryptsetup open {rootfsDevice} root-crypt --type plain --cipher aes-xts-plain --key-file /key-file", 2000, true); } partitionSize = long.Parse(hakchi.Shell.ExecuteSimple($"blockdev --getsize64 {osDecryptedDevice}", throwOnNonZero: true)); break; case NandTasks.DumpUserPartition: hakchi.Shell.Execute("hakchi mount_base", null, null, null, 0, true); partitionSize = long.Parse(hakchi.Shell.ExecuteSimple("df -B 1 | grep /newroot/var/lib | head -n 1 | awk -e '{print $3 }'", throwOnNonZero: true).Trim()); break; case NandTasks.FlashUserPartition: partitionSize = long.Parse(hakchi.Shell.ExecuteSimple($"blockdev --getsize64 {userDataDevice}", throwOnNonZero: true)); break; case NandTasks.DumpNand: partitionSize = 536870912; break; } FileMode mode = FileMode.Create; if (task == NandTasks.FlashUserPartition || task == NandTasks.FlashSystemPartition) { mode = FileMode.Open; } tasker.SetStatus(mode == FileMode.Open ? Resources.FlashingNand : Resources.DumpingNand); using (var file = new TrackableFileStream(nandDump, mode, mode == FileMode.Open ? FileAccess.Read : FileAccess.ReadWrite)) { if (mode == FileMode.Open && file.Length > partitionSize) { throw new Exception(Resources.ImageTooLarge); } if (mode == FileMode.Create && task != NandTasks.DumpUserPartition && task != NandTasks.DumpSystemPartition) { file.SetLength(partitionSize); } if (task == NandTasks.DumpUserPartition) { file.OnProgress += (long position, long length) => { tasker.OnProgress(Math.Min(position, partitionSize), partitionSize); }; } else if (task == NandTasks.DumpSystemPartition && !systemIsHsqs) { file.OnProgress += (long position, long length) => { tasker.OnProgress(Math.Min(position, partitionSize) + partitionSize, partitionSize * 2); }; } else { file.OnProgress += tasker.OnProgress; } switch (task) { case NandTasks.DumpSystemPartition: if (systemIsHsqs) { Shared.ShellPipe($"dd if={osDecryptedDevice} bs=128K count={(partitionSize / 1024) / 4 }", null, file, throwOnNonZero: true); } else { Regex mksquashfsProgress = new Regex(@"(\d+)/(\d+)", RegexOptions.Compiled); using (var mksquashfs = File.OpenRead(Path.Combine(Program.BaseDirectoryInternal, "tools", "mksquashfs"))) { hakchi.Shell.Execute("cat > /mksquashfs", mksquashfs, throwOnNonZero: true); hakchi.Shell.Execute("chmod +x /mksquashfs", throwOnNonZero: true); } hakchi.Shell.ExecuteSimple("hakchi mount_base"); hakchi.Shell.ExecuteSimple("mkdir -p /tmp/"); using (EventStream mkSquashfsProgress = new EventStream()) { splitStream.AddStreams(mkSquashfsProgress); mkSquashfsProgress.OnData += (byte[] buffer) => { string data = Encoding.ASCII.GetString(buffer); MatchCollection matches = mksquashfsProgress.Matches(data); if (matches.Count > 0) { tasker.SetProgress(long.Parse(matches[matches.Count - 1].Groups[1].Value), long.Parse(matches[matches.Count - 1].Groups[2].Value) * 2); } }; hakchi.Shell.Execute("/mksquashfs /newroot/var/squashfs /tmp/rootfs.hsqs", null, splitStream, splitStream, throwOnNonZero: true); splitStream.RemoveStream(mkSquashfsProgress); } partitionSize = long.Parse(hakchi.Shell.ExecuteSimple("ls -la /tmp/rootfs.hsqs | awk -e '{ print $5 }'")); hakchi.Shell.ExecuteSimple("hakchi umount_base"); Shared.ShellPipe("cat /tmp/rootfs.hsqs", stdout: file); hakchi.Shell.ExecuteSimple("rm /tmp/rootfs.hsqs"); } break; case NandTasks.FlashSystemPartition: if (isTar || (hakchi.IsMdPartitioning && !isExtFs)) { using (var eventStream = new EventStream()) { eventStream.OnData += (byte[] data) => { var dataString = Encoding.UTF8.GetString(data).Trim(); tasker.SetStatus(dataString); Trace.WriteLine(dataString); }; hakchi.Shell.Execute("mkdir -p /tmp/rootfs /tmp/squashfs", throwOnNonZero: true); if (isHsqs) { Shared.ShellPipe($"dd of=/tmp/squashfs/squash.hsqs bs=128K", file, throwOnNonZero: true); } ShellTasks.FormatDevice(osDecryptedDevice)(tasker); hakchi.Shell.Execute($"mount {osDecryptedDevice} /tmp/rootfs", throwOnNonZero: true); if (isTar) { hakchi.Shell.Execute($"tar -xvf - -C /tmp/rootfs", file, eventStream, null, throwOnNonZero: true); } else if (isHsqs) { tasker.SetStatus(Resources.FlashingNand); hakchi.Shell.Execute($"mount /tmp/squashfs/squash.hsqs /tmp/squashfs/", throwOnNonZero: true); hakchi.Shell.Execute("cd /tmp/squashfs; rsync -av ./ ../rootfs", null, eventStream, eventStream, throwOnNonZero: true); hakchi.Shell.Execute("umount /tmp/squashfs", throwOnNonZero: true); hakchi.Shell.Execute("rm -rf /tmp/squashfs", throwOnNonZero: true); } hakchi.Shell.Execute("umount /tmp/rootfs", throwOnNonZero: true); hakchi.Shell.Execute("rmdir /tmp/rootfs", throwOnNonZero: true); } } else { Shared.ShellPipe($"dd of={osDecryptedDevice} bs=128K", file, throwOnNonZero: true); } if (hasKeyfile) { hakchi.Shell.Execute("cryptsetup close root-crypt", throwOnNonZero: true); } break; case NandTasks.DumpUserPartition: Shared.ShellPipe($"tar -cvC /newroot/var/lib/ .", null, file, null, throwOnNonZero: true); break; case NandTasks.FlashUserPartition: if (isTar) { hakchi.Shell.Execute("hakchi mount_base", null, null, null, 0, true); hakchi.Shell.Execute("rm -rf /newroot/var/lib/*", null, null, null, 0, true); hakchi.Shell.Execute("tar -xvC /newroot/var/lib/", file, throwOnNonZero: true); } else { Shared.ShellPipe($"dd of={userDataDevice} bs=128K", file, throwOnNonZero: true); } break; case NandTasks.DumpNand: hakchi.Shell.Execute("hakchi umount_base", null, splitStream, splitStream, 0, true); Shared.ShellPipe("sntool sunxi_flash phy_read 0", null, file, throwOnNonZero: true); break; } file.Close(); } tasker.SetStatus(Resources.Done); tasker.SetProgress(1, 1); return Conclusion.Success; }); }); }
public static TaskFunc ProcessNand(string nandDump, NandTasks task) { return((Tasker tasker, Object sync) => { NandTasks[] validTasks = { NandTasks.DumpNand, NandTasks.DumpNandB, NandTasks.DumpNandC, NandTasks.FlashNandB, NandTasks.FlashNandC, NandTasks.FormatNandC }; if (!validTasks.Contains(task)) { throw new ArgumentOutOfRangeException("task"); } if (task == NandTasks.FlashNandB && !Files.CheckFileType.IsSquashFs(nandDump)) { throw new Exception(Properties.Resources.InvalidHsqs); } bool isTar = false; if (task == NandTasks.FlashNandC) { isTar = Files.CheckFileType.IsTar(nandDump); if (!(isTar || Files.CheckFileType.IsExtFs(nandDump))) { throw new Exception(Properties.Resources.InvalidUserDataBackup); } } long partitionSize = 300 * 1024 * 1024; var splitStream = new SplitterStream(Program.debugStreams); switch (task) { case NandTasks.DumpNandB: case NandTasks.FlashNandB: hakchi.Shell.Execute("umount /newroot"); hakchi.Shell.Execute("cryptsetup close root-crypt"); hakchi.Shell.ExecuteSimple("cryptsetup open /dev/nandb root-crypt --type plain --cipher aes-xts-plain --key-file /key-file", 2000, true); if (task == NandTasks.DumpNandB) { partitionSize = long.Parse(hakchi.Shell.ExecuteSimple("echo $((($(hexdump -e '1/4 \"%u\"' -s $((0x28)) -n 4 /dev/mapper/root-crypt)+0xfff)/0x1000))", throwOnNonZero: true).Trim()) * 4 * 1024; } if (task == NandTasks.FlashNandB) { partitionSize = long.Parse(hakchi.Shell.ExecuteSimple("blockdev --getsize64 /dev/mapper/root-crypt", throwOnNonZero: true)); } break; case NandTasks.DumpNandC: hakchi.Shell.Execute("hakchi mount_base", null, null, null, 0, true); partitionSize = long.Parse(hakchi.Shell.ExecuteSimple("df -B 1 | grep /newroot/var/lib | head -n 1 | awk -e '{print $3 }'", throwOnNonZero: true).Trim()); break; case NandTasks.FlashNandC: partitionSize = long.Parse(hakchi.Shell.ExecuteSimple("blockdev --getsize64 /dev/nandc", throwOnNonZero: true)); break; case NandTasks.DumpNand: partitionSize = 536870912; break; case NandTasks.FormatNandC: hakchi.Shell.Execute("cat > /bin/mke2fs; chmod +x /bin/mke2fs", File.OpenRead(Shared.PathCombine(Program.BaseDirectoryInternal, "tools", "arm", "mke2fs.static")), null, null, 0, true); hakchi.Shell.Execute("hakchi umount_base", null, splitStream, splitStream); hakchi.Shell.Execute("yes | mke2fs -t ext4 -L data -b 4K -E stripe-width=32 -O ^huge_file,^metadata_csum /dev/nandc", null, splitStream, splitStream, 0, true); hakchi.Shell.Execute("rm /bin/mke2fs"); return Conclusion.Success; } FileMode mode = FileMode.Create; if (task == NandTasks.FlashNandC || task == NandTasks.FlashNandB) { mode = FileMode.Open; } tasker.SetStatus(mode == FileMode.Open ? Resources.FlashingNand : Resources.DumpingNand); using (var file = new TrackableFileStream(nandDump, mode)) { if (mode == FileMode.Open && file.Length > partitionSize) { throw new Exception(Resources.ImageTooLarge); } if (mode == FileMode.Create && task != NandTasks.DumpNandC) { file.SetLength(partitionSize); } if (task == NandTasks.DumpNandC) { file.OnProgress += (long position, long length) => { tasker.OnProgress(Math.Min(position, partitionSize), partitionSize); }; } else { file.OnProgress += tasker.OnProgress; } switch (task) { case NandTasks.DumpNandB: Shared.ShellPipe($"dd if=/dev/mapper/root-crypt bs=128K count={(partitionSize / 1024) / 4 }", null, file, throwOnNonZero: true); break; case NandTasks.FlashNandB: Shared.ShellPipe("dd of=/dev/mapper/root-crypt bs=128K", file, throwOnNonZero: true); hakchi.Shell.Execute("cryptsetup close root-crypt", throwOnNonZero: true); break; case NandTasks.DumpNandC: Shared.ShellPipe($"tar -cvC /newroot/var/lib/ .", null, file, null, throwOnNonZero: true); break; case NandTasks.FlashNandC: if (isTar) { hakchi.Shell.Execute("hakchi mount_base", null, null, null, 0, true); hakchi.Shell.Execute("rm -rf /newroot/var/lib/*", null, null, null, 0, true); hakchi.Shell.Execute("tar -xvC /newroot/var/lib/", file, throwOnNonZero: true); } else { Shared.ShellPipe("dd of=/dev/nandc bs=128K", file, throwOnNonZero: true); } break; case NandTasks.DumpNand: Shared.ShellPipe("sntool sunxi_flash phy_read 0 1000", null, file, throwOnNonZero: true); break; } file.Close(); } tasker.SetStatus(Resources.Done); tasker.SetProgress(1, 1); return Conclusion.Success; }); }
private Tasker.Conclusion InstallSDPart2(Tasker tasker, Object sync) { var splitStream = new SplitterStream(Program.debugStreams); tasker.SetStatus(Resources.MountingSDCard); if (DialogOptions.MakeBootable) { hakchi.Shell.Execute("sh /tmp/init mount", null, splitStream, splitStream, 0, true); } else { hakchi.Shell.Execute("mkdir -p /data/", null, null, null, 0, true); hakchi.Shell.Execute("mount /dev/mmcblk0p1 /data/", null, null, null, 0, true); } hakchi.Shell.Execute("mkdir -p /data/hakchi/games/", null, null, null, 0, true); if (!DialogOptions.MakeBootable && DialogOptions.StoreSaves) { hakchi.Shell.Execute("mkdir -p /data/hakchi/saves/", null, null, null, 0, true); } if (DialogOptions.CopyType != SDFormatResult.CopyTypes.None) { using (EventStream copyDataProgress = new EventStream()) { var userDataDeviceName = Sunxi.NandInfo.GetNandInfo().GetDataPartition().Device; splitStream.AddStreams(copyDataProgress); copyDataProgress.OnData += (byte[] buffer) => { tasker.SetStatus(System.Text.Encoding.ASCII.GetString(buffer)); }; tasker.SetStatus(Resources.CopyingNandDataToSDCard); hakchi.Shell.Execute($"mkdir -p /{userDataDeviceName} && mount /dev/{userDataDeviceName} /{userDataDeviceName}", null, null, null, 0, true); if (DialogOptions.CopyType == SDFormatResult.CopyTypes.Everything) { hakchi.Shell.Execute($"rsync -avc /{userDataDeviceName}/ /data/", null, splitStream, splitStream, 0, true); } else { var nandPath = DialogOptions.MakeBootable ? $"/{userDataDeviceName}/clover" : $"/{userDataDeviceName}/clover/profiles/0"; var dataPath = DialogOptions.MakeBootable ? "/data/clover" : "/data/hakchi/saves"; if (hakchi.Shell.Execute($"[ -d {nandPath} ]") == 0) { hakchi.Shell.Execute($"rsync -avc {nandPath} {dataPath}", null, splitStream, splitStream, 0, true); } } hakchi.Shell.Execute($"umount /{userDataDeviceName}/ && rmdir /{userDataDeviceName}/", null, null, null, 0, true); splitStream.RemoveStream(copyDataProgress); copyDataProgress.Dispose(); } } if (DialogOptions.MakeBootable) { tasker.SetStatus(Resources.CopyingHakchiToSDCard); hakchi.Shell.Execute("sh /tmp/init copy", null, splitStream, splitStream, 0, true); tasker.SetStatus(Resources.UnmountingSDCard); hakchi.Shell.Execute("sh /tmp/init unmount", null, splitStream, splitStream, 0, true); } return(Tasker.Conclusion.Success); }
private Tasker.Conclusion InstallSDPart1(Tasker tasker, Object sync) { tasker.SetStatus(Resources.InstallingHakchi); if (hakchi.Shell.Execute("[ -b /dev/mmcblk0 ]") != 0) { throw new Exception(Resources.NoSDCard); } var splitStream = new SplitterStream(Program.debugStreams); hakchi.Shell.Execute("mkdir -p /squashtools /tmp", null, null, null, 0, true); hakchi.Shell.Execute("umount /newroot"); hakchi.Shell.Execute("losetup -d /dev/loop2"); hakchi.Shell.Execute("umount /firmware"); hakchi.Shell.Execute("mkdir -p /sd-temp/", null, null, null, 0, true); tasker.SetStatus(Resources.ExtractingHakchiToTemporaryFolder); hakchi.Shell.Execute("tar -xzvf - -C /sd-temp/", hakchi.Hmod.GetHmodStream(), null, null, 0, true); // 16 tasker.SetProgress(16, 161); tasker.SetStatus(Resources.ClearingTheFirst32MBOfSDCard); hakchi.Shell.Execute("dd if=/dev/zero of=/dev/mmcblk0 bs=1M count=32", null, null, null, 0, true); // 32 tasker.SetProgress(48, 161); if (DialogOptions.MakeBootable) { tasker.SetStatus(Resources.AddingHakchiMBR); hakchi.Shell.Execute("printf \"hakchi\\n%s\\n\" \"$(cat \"/sd-temp/var/version\")\" | dd \"of=/dev/mmcblk0\"", null, null, null, 0, true); tasker.SetProgress(49, 161); tasker.SetStatus(Resources.WritingFATFilesystem); hakchi.Shell.Execute("gunzip -c /sd-temp/sd/data.vfat.gz | dd of=/dev/mmcblk0 bs=1M seek=32", null, null, null, 0, true); // 96 tasker.SetProgress(145, 161); tasker.SetStatus(Resources.WritingBoot0); hakchi.Shell.Execute("dd if=/sd-temp/sd/boot0.bin of=/dev/mmcblk0 bs=1K seek=8", null, null, null, 0, true); // 1 tasker.SetProgress(146, 161); tasker.SetStatus(Resources.WritingUboot); hakchi.Shell.Execute("dd if=/sd-temp/sd/uboot.bin of=/dev/mmcblk0 bs=1K seek=19096", null, null, null, 0, true); // 1 tasker.SetProgress(147, 161); tasker.SetStatus(Resources.WritingKernel); hakchi.Shell.Execute("dd if=/sd-temp/sd/kernel.img of=/dev/mmcblk0 bs=1K seek=20480", null, null, null, 0, true); // 4 tasker.SetProgress(151, 161); tasker.SetStatus(Resources.WritingSquashFS); hakchi.Shell.Execute("dd \"if=/sd-temp/sd/squash.hsqs\" of=/dev/mmcblk0 bs=1K seek=40", null, null, null, 0, true); // 10 tasker.SetProgress(161, 161); tasker.SetStatus(Resources.MountingSquashFS); hakchi.Shell.Execute($"losetup -o {1024 * 40} /dev/loop1 /dev/mmcblk0", null, null, null, 0, true); hakchi.Shell.Execute("mount /dev/loop1 /squashtools", null, null, null, 0, true); hakchi.Shell.Execute("cp /squashtools/init /tmp/init", null, null, null, 0, true); hakchi.Shell.Execute("sh /tmp/init partition", null, splitStream, splitStream, 0, true); } else { hakchi.Shell.Execute("echo sdprep | dd \"of=/dev/mmcblk0\"", null, null, null, 0, true); hakchi.Shell.Execute("mount /sd-temp/sd/squash.hsqs /squashtools", null, null, null, 0, true); hakchi.Shell.Execute("/squashtools/sfdisk /dev/mmcblk0", new MemoryStream(Encoding.ASCII.GetBytes("128M,,L\n")), splitStream, splitStream, 0, true); } return(Tasker.Conclusion.Success); }