diff --git a/Net/CliParser.cs b/Net/CliParser.cs index b6f7c2bc..a5e40c25 100644 --- a/Net/CliParser.cs +++ b/Net/CliParser.cs @@ -17,6 +17,7 @@ namespace Net new CliOption("did", 1, (o, a) => o.DeviceId = ParseTitleId(a[0])), new CliOption("cert", 1, (o, a) => o.CertFile = a[0]), new CliOption("commoncert", 1, (o, a) => o.CommonCertFile = a[0]), + new CliOption("token", 1, (o, a) => o.Token = a[0]), new CliOption("metadata", 0, (o, a) => o.GetMetadata = true) }; diff --git a/Net/Database.cs b/Net/Database.cs index bc7608f2..04e5a5d1 100644 --- a/Net/Database.cs +++ b/Net/Database.cs @@ -9,7 +9,7 @@ namespace Net { public class Database { - public Dictionary Titles { get; set; } = new Dictionary(); + public Dictionary Titles { get; set; } = new Dictionary(); public DateTime VersionListTime { get; set; } public string Serialize() @@ -30,36 +30,39 @@ namespace Net public void ImportVersionList(VersionList list) { - foreach (var title in list.titles) + foreach (VersionListTitle title in list.titles) { - var mainId = long.Parse(title.id, NumberStyles.HexNumber, CultureInfo.InvariantCulture); - long updateId = 0; - bool isUpdate = (mainId & 0x800) != 0; - if (isUpdate) + ulong mainId = ulong.Parse(title.id, NumberStyles.HexNumber, CultureInfo.InvariantCulture); + + AddTitle(mainId); + } + } + + public void AddTitle(ulong id, int version = -1) + { + bool isUpdate = (id & 0x800) != 0; + + if (!Titles.TryGetValue(id, out TitleMetadata titleDb)) + { + titleDb = new TitleMetadata { Id = id }; + Titles[id] = titleDb; + } + + if (version >= 0) + { + titleDb.MaxVersion = version; + + int minVersion = isUpdate ? 1 : 0; + + int maxVersionShort = titleDb.MaxVersion >> 16; + for (int i = minVersion; i <= maxVersionShort; i++) { - updateId = mainId; - mainId &= ~0x800; - } + int longVersion = i << 16; - if (!Titles.TryGetValue(mainId, out TitleMetadata titleDb)) - { - titleDb = new TitleMetadata(); - Titles[mainId] = titleDb; - } - - titleDb.Id = mainId; - titleDb.UpdateId = updateId; - titleDb.MaxVersion = title.version; - - int maxVersionShort = title.version >> 16; - for (int i = 0; i <= maxVersionShort; i++) - { - var version = i << 16; - - if (!titleDb.Versions.TryGetValue(version, out TitleVersion versionDb)) + if (!titleDb.Versions.TryGetValue(longVersion, out TitleVersion versionDb)) { - versionDb = new TitleVersion { Version = version }; - titleDb.Versions.Add(version, versionDb); + versionDb = new TitleVersion { Version = longVersion }; + titleDb.Versions.Add(longVersion, versionDb); } } } @@ -74,46 +77,21 @@ namespace Net { foreach (string id in titleIds) { - var mainId = long.Parse(id, NumberStyles.HexNumber, CultureInfo.InvariantCulture); - long updateId = 0; - bool isUpdate = (mainId & 0x800) != 0; - if (isUpdate) - { - updateId = mainId; - mainId &= ~0x800; - } - - var titleDb = new TitleMetadata(); - Titles[mainId] = titleDb; - - titleDb.Id = mainId; - titleDb.UpdateId = mainId | 0x800; - titleDb.MaxVersion = 5 << 16; - - int maxVersionShort = 5; - for (int i = 0; i <= maxVersionShort; i++) - { - var version = i << 16; - - if (!titleDb.Versions.TryGetValue(version, out TitleVersion versionDb)) - { - versionDb = new TitleVersion { Version = version }; - titleDb.Versions.Add(version, versionDb); - } - } + ulong mainId = ulong.Parse(id, NumberStyles.HexNumber, CultureInfo.InvariantCulture); + AddTitle(mainId); } } } public class TitleMetadata { - public long Id { get; set; } - public long UpdateId { get; set; } - public List AocIds { get; set; } = new List(); + public ulong Id { get; set; } public int MaxVersion { get; set; } + public List Superfly { get; set; } = new List(); + public DateTime SuperflyTime { get; set; } public Dictionary Versions { get; set; } = new Dictionary(); - + public bool IsSuperflyCurrent() => SuperflyTime.AddDays(15) > DateTime.UtcNow; } public class TitleVersion diff --git a/Net/Json.cs b/Net/Json.cs index 53287db3..e4091fb1 100644 --- a/Net/Json.cs +++ b/Net/Json.cs @@ -14,6 +14,12 @@ namespace Net var versionList = JsonConvert.DeserializeObject(text); return versionList; } + + public static List ReadSuperfly(string filename) + { + string text = File.ReadAllText(filename); + return JsonConvert.DeserializeObject>(text); + } } public class VersionList @@ -29,4 +35,11 @@ namespace Net public int version { get; set; } public int required_version { get; set; } } + + public class SuperflyInfo + { + public string title_id { get; set; } + public int version { get; set; } + public string title_type { get; set; } + } } diff --git a/Net/NetContext.cs b/Net/NetContext.cs index b7c747c8..6d9b2ebe 100644 --- a/Net/NetContext.cs +++ b/Net/NetContext.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; @@ -11,9 +12,10 @@ namespace Net { private X509Certificate2 Certificate { get; set; } private X509Certificate2 CertificateCommon { get; set; } + private string Token { get; } private string Eid { get; } = "lp1"; private ulong Did { get; } - private string Firmware { get; } = "5.1.0-3.0"; + private string Firmware { get; } = "6.0.0-5.0"; private string CachePath { get; } = "titles"; private Context ToolCtx { get; } public Database Db { get; } @@ -24,6 +26,8 @@ namespace Net { ToolCtx = ctx; Did = ctx.Options.DeviceId; + Token = ctx.Options.Token; + if (ctx.Options.CertFile != null) { SetCertificate(ctx.Options.CertFile); @@ -62,7 +66,7 @@ namespace Net if (stream == null) return null; var nca = new Nca(ToolCtx.Keyset, stream, true); - Stream sect = nca.OpenSection(0, false); + Stream sect = nca.OpenSection(0, false, true); var pfs0 = new Pfs(sect); var file = pfs0.OpenFile(pfs0.Files[0]); @@ -112,7 +116,7 @@ namespace Net if (controlNca == null) return null; var nca = new Nca(ToolCtx.Keyset, controlNca, true); - var romfs = new Romfs(nca.OpenSection(0, false)); + var romfs = new Romfs(nca.OpenSection(0, false, true)); var controlNacp = romfs.GetFile("/control.nacp"); var reader = new BinaryReader(new MemoryStream(controlNacp)); @@ -136,6 +140,27 @@ namespace Net return new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read); } + public List GetSuperfly(ulong titleId) + { + var filename = GetSuperflyFile(titleId); + return Json.ReadSuperfly(filename); + } + + public string GetSuperflyFile(ulong titleId) + { + string titleDir = GetTitleDir(titleId); + + var filePath = Path.Combine(titleDir, $"{titleId:x16}.json"); + if (!File.Exists(filePath)) + { + DownloadFile(GetSuperflyUrl(titleId), filePath); + } + + if (!File.Exists(filePath)) return null; + + return filePath; + } + private void DownloadCnmt(ulong titleId, int version) { var titleDir = GetTitleDir(titleId, version); @@ -179,9 +204,14 @@ namespace Net } } - private string GetTitleDir(ulong titleId, int version) + private string GetTitleDir(ulong titleId, int version = -1) { - return Path.Combine(CachePath, $"{titleId:x16}", $"{version}"); + if (version >= 0) + { + return Path.Combine(CachePath, $"{titleId:x16}", $"{version}"); + } + + return Path.Combine(CachePath, $"{titleId:x16}"); } public string GetMetaUrl(string ncaId) @@ -196,6 +226,12 @@ namespace Net return url; } + public string GetSuperflyUrl(ulong titleId) + { + string url = $"https://superfly.hac.{Eid}.d4c.nintendo.net/v1/a/{titleId:x16}/dv"; + return url; + } + public string GetMetadataNcaId(ulong titleId, int version) { string url = $"{GetAtumUrl()}/t/a/{titleId:x16}/{version}?deviceid={Did}"; @@ -239,7 +275,9 @@ namespace Net { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); request.ClientCertificates.Add(Certificate); + request.Accept = "*/*"; request.UserAgent = $"NintendoSDK Firmware/{Firmware} (platform:NX; did:{Did}; eid:{Eid})"; + request.Headers.Add("X-Nintendo-DenebEdgeToken", Token); request.Method = method; ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true; diff --git a/Net/Options.cs b/Net/Options.cs index 5d4e1250..abbc82aa 100644 --- a/Net/Options.cs +++ b/Net/Options.cs @@ -12,6 +12,7 @@ namespace Net public ulong DeviceId; public string CertFile; public string CommonCertFile; + public string Token; public bool GetMetadata; } diff --git a/Net/Program.cs b/Net/Program.cs index 17e70d23..b8919886 100644 --- a/Net/Program.cs +++ b/Net/Program.cs @@ -45,8 +45,14 @@ namespace Net return; } - var tid = ctx.Options.TitleId; - var ver = ctx.Options.Version; + if (string.IsNullOrWhiteSpace(ctx.Options.Token)) + { + CliParser.PrintWithUsage("A token must be set."); + return; + } + + ulong tid = ctx.Options.TitleId; + int ver = ctx.Options.Version; var net = new NetContext(ctx); var cnmt = net.GetCnmt(tid, ver); @@ -66,38 +72,79 @@ namespace Net private static void GetMetadata(NetContext net, IProgressReport logger = null) { - var versionList = net.GetVersionList(); + VersionList versionList = net.GetVersionList(); net.Db.ImportVersionList(versionList); //net.Db.ImportList("titles.txt"); net.Save(); + ReadMetaNcas(net, logger); + + net.Save(); + } - foreach (var title in net.Db.Titles.Values) + private static void ReadMetaNcas(NetContext net, IProgressReport logger = null) + { + foreach (TitleMetadata title in net.Db.Titles.Values.ToArray()) { - foreach (var version in title.Versions.Values.Where(x => x.Exists)) + if (title.Versions.Count == 0) { - var titleId = version.Version == 0 ? title.Id : title.UpdateId; + int version = 0; + if ((title.Id & 0x800) != 0) + { + version = 1 << 16; + } + + title.Versions.Add(version, new TitleVersion { Version = version }); + } + + foreach (TitleVersion version in title.Versions.Values.Where(x => x.Exists)) + { + ulong titleId = title.Id; try { - var control = net.GetControl((ulong)titleId, version.Version); - version.Control = control; - if (control == null) version.Exists = false; - - Cnmt meta = net.GetCnmt((ulong)titleId, version.Version); + Cnmt meta = net.GetCnmt(titleId, version.Version); version.ContentMetadata = meta; - if (meta == null) version.Exists = false; + if (meta == null) + { + version.Exists = false; + logger?.LogMessage($"{titleId:x16}v{version.Version} not found."); + continue; + } - logger?.LogMessage($"{titleId}v{version.Version}"); + Nacp control = net.GetControl(titleId, version.Version); + version.Control = control; + + if (!net.Db.Titles.ContainsKey(meta.ApplicationTitleId)) + { + net.Db.AddTitle(meta.ApplicationTitleId); + logger?.LogMessage($"Found title {meta.ApplicationTitleId:x16}"); + } + + if (meta.Type == TitleType.Application) + { + ReadSuperfly(title, net, logger); + } + + logger?.LogMessage($"{titleId:x16}v{version.Version}"); //Thread.Sleep(300); } catch (Exception ex) { - logger?.LogMessage($"Failed getting {titleId}v{version.Version}\n{ex.Message}"); + logger?.LogMessage($"Failed getting {titleId:x16}v{version.Version}\n{ex.Message}"); } } // net.Save(); } + } - net.Save(); + private static void ReadSuperfly(TitleMetadata titleDb, NetContext net, IProgressReport logger = null) + { + titleDb.Superfly = net.GetSuperfly(titleDb.Id); + + foreach (SuperflyInfo title in titleDb.Superfly) + { + ulong id = ulong.Parse(title.title_id, NumberStyles.HexNumber, CultureInfo.InvariantCulture); + net.Db.AddTitle(id, title.version); + } } private static void OpenKeyset(Context ctx)