public IEnumerable <Tuple <string, string> > InstallNSP(ObservableCollection <AluminumFoil.NSP> NSPs) { // Installs NSP to Switch via TinFoil using (AluminumFoil.Switch NX = new AluminumFoil.Switch()) { // Send NSP List yield return(new InstallUpdate("Sending NSP names to TinFoil", "installing")); NX.Write(MAGIC); Console.WriteLine("Sending NSP names to TinFoil"); string concatenatedNames = string.Join("\n", NSPs.Select(n => n.BaseName)); byte[] namesBytes = concatenatedNames.AsBytes(); NX.Write(BitConverter.GetBytes(Convert.ToUInt32(namesBytes.Length))); NX.Write(new byte[0x8] { 0, 0, 0, 0, 0, 0, 0, 0 }); NX.Write(namesBytes); Console.WriteLine("Waiting for response from TinFoil"); yield return(new InstallUpdate("Select NSP on TinFoil", "waiting")); // Poll Commands while (true) { byte[] command = NX.Read(0x20); if (!command.SubArray(0x0, 0x4).SequenceEqual(COMMANDMAGIC)) { continue; } ; byte[] cmdType = command.SubArray(0x4, 0x1); uint cmdID = BitConverter.ToUInt32(command, 0x8); ulong payloadSize = BitConverter.ToUInt64(command, 0xC); if (cmdID == (uint)CommandIDs.Exit) { yield return(new InstallUpdate("Finished", "finished")); break; } else if (cmdID == (uint)CommandIDs.FileRange) { // File Range Command byte[] fileRangeRequest = NX.Read(0x20); ulong size = BitConverter.ToUInt64(fileRangeRequest, 0x0); ulong offset = BitConverter.ToUInt64(fileRangeRequest, 0x8); ulong nameLen = BitConverter.ToUInt64(fileRangeRequest, 0x10); string selectedBaseName = NX.Read(Convert.ToInt32(nameLen)).AsString(); AluminumFoil.NSP selectedNSP = NSPs.FirstOrDefault(n => n.BaseName == selectedBaseName); if (selectedNSP == null) { throw new Exception(string.Format("TinFoil requested {0} but this NSP is not opened for installation.", selectedBaseName)); } yield return(new InstallUpdate(string.Format("Transferring {0} requested bytes to TinFoil", size), "installing")); NX.Write(ResponseHeader((uint)CommandIDs.FileRange, size)); using (BinaryReader fileReader = new BinaryReader(new FileStream(selectedNSP.FilePath, FileMode.Open))) { fileReader.BaseStream.Seek((long)offset, SeekOrigin.Begin); ulong bytesRead = 0; while (bytesRead < size) { ulong readLen; if (bytesRead + ChunkSize >= size) { readLen = size - bytesRead; } else { readLen = ChunkSize; } NX.Write(fileReader.ReadBytes((int)readLen)); bytesRead += readLen; selectedNSP.Transferred += readLen; } } } } } }
public IEnumerable <(string, string)> InstallNSP(ObservableCollection <AluminumFoil.NSP> NSPs) { AluminumFoil.NSP nsp = NSPs.First(); Console.WriteLine(string.Format("Installing {0} via GoldLeaf", nsp.BaseName)); // Installs NSP to Switch via GoldLeaf int txLen; using (AluminumFoil.Switch NX = new AluminumFoil.Switch()) { Console.WriteLine("Sending ConnectionRequest"); NX.Write(commandHeaders["ConnectionRequest"]); bool finished = false; while (!finished) { byte[] resp = NX.Read(0x8); string CommandName = RespType(resp); Console.WriteLine("Recieved command from GoldLeaf: " + CommandName); switch (CommandName) { case "ConnectionResponse": Console.WriteLine("Sending NSP name to GoldLeaf"); yield return(new InstallUpdate("Sending NSP name to GoldLeaf", "installing")); NX.Write(commandHeaders["NSPName"]); var nameBytes = nsp.BaseName.AsBytes(); NX.Write(BitConverter.GetBytes(Convert.ToUInt32(nameBytes.Length))); NX.Write(nameBytes); yield return(new InstallUpdate("Confirm Installation Options on GoldLeaf", "waiting")); break; case "Start": Console.WriteLine("Sending NSP metadata to GoldLeaf"); yield return(new InstallUpdate("Sending NSP Metadata", "installing")); NX.Write(commandHeaders["NSPData"]); NX.Write(BitConverter.GetBytes(Convert.ToUInt32(nsp.Contents.Count))); foreach (var file in nsp.Contents) { // uint [4] Name len NX.Write(BitConverter.GetBytes(Convert.ToUInt32(file.Name.Length))); // str [*] Name NX.Write(file.Name.AsBytes()); // ulong [8] File offset NX.Write(BitConverter.GetBytes(file.Offset)); // ulong [8] File size NX.Write(BitConverter.GetBytes(file.Size)); } break; case "NSPContent": byte[] indBytes = NX.Read(0x4); var idx = (int)BitConverter.ToUInt32(indBytes, 0); Console.WriteLine(string.Format("GoldLeaf requested nca {0}: {1}", idx, nsp.Contents[idx].Name)); yield return(new InstallUpdate("Installing " + nsp.Contents[idx].Name, "installing")); foreach (var chunk in nsp.ReadFile(idx)) { txLen = NX.Write(chunk); nsp.Transferred += (ulong)chunk.Length; } nsp.Contents[idx].Finished = true; break; case "NSPTicket": Console.WriteLine("Sending NSP ticket"); yield return(new InstallUpdate("Installing Ticket", "installing")); var tikind = -1; for (var i = 0; i < nsp.Contents.Count; i++) { if (nsp.Contents[i].Name.EndsWith("tik")) { tikind = i; break; } } if (tikind == -1) { Console.WriteLine("Could not find tik in NSP"); Exception exc = new IndexOutOfRangeException("Ticket file not found in NSP header."); exc.Source = nsp.FilePath; throw exc; } ; byte[] ticketFile = new byte[nsp.Contents[tikind].Size]; using (BinaryReader reader = new BinaryReader(new FileStream(nsp.FilePath, FileMode.Open))) { var tikoffset = nsp.Contents[tikind].Offset; reader.BaseStream.Seek(Convert.ToInt64(tikoffset), SeekOrigin.Begin); reader.Read(ticketFile, 0, ticketFile.Length); } NX.Write(ticketFile); break; case "Finish": Console.WriteLine("Finished installing " + nsp.BaseName); yield return(new InstallUpdate("Finished", "finished")); finished = true; break; default: Console.WriteLine("Unhandled command from GoldLeaf: " + resp.AsString()); yield return(new InstallUpdate("Unknown request from GoldLeaf: " + resp.AsString(), "alert")); finished = true; break; } } } }