// Main wrapper that assembles the ROM based on the following specifications: internal static bool BuildROM(bool card2, string logoName, string exefsPath, string romfsPath, string exheaderPath, string serialText, string savePath) { // Sanity check the input files. if (! ((File.Exists(exefsPath) || Directory.Exists(exefsPath)) && (File.Exists(romfsPath) || Directory.Exists(romfsPath)) && File.Exists(exheaderPath))) { return(false); } // If ExeFS and RomFS are not built, build. if (!File.Exists(exefsPath) && Directory.Exists(exefsPath)) { ExeFS.Set(Directory.GetFiles(exefsPath), exefsPath = "exefs.bin"); } if (!File.Exists(romfsPath) && Directory.Exists(romfsPath)) { RomFS.BuildRomFS(romfsPath, romfsPath = "romfs.bin"); } Ncch ncch = Ctr.SetNcch(exefsPath, romfsPath, exheaderPath, serialText, logoName); Ncsd ncsd = Ctr.SetNcsd(ncch, card2); bool success = Ctr.WriteROM(ncsd, savePath); return(success); }
// Sub methods that drive the operation internal static Ncch SetNcch(string exefsPath, string romfsPath, string exheaderPath, string tbSerial, string logoName) { Ncch ncch = new Ncch { Exheader = new Exheader(exheaderPath), Plainregion = new byte[0] }; if (ncch.Exheader.IsPokemon()) { if (ncch.Exheader.IsXy()) { ncch.Plainregion = (byte[])Resources.ResourceManager.GetObject("XY"); } else if (ncch.Exheader.IsOras()) { ncch.Plainregion = (byte[])Resources.ResourceManager.GetObject("ORAS"); } } ncch.Exefs = new ExeFS(exefsPath); ncch.Romfs = new RomFS(romfsPath); ncch.Logo = (byte[])Resources.ResourceManager.GetObject(logoName); ulong len = 0x200; //NCCH Signature + NCCH Header ncch.HeaderData = new Ncch.Header { Signature = new byte[0x100], Magic = 0x4843434E }; ncch.HeaderData.TitleId = ncch.HeaderData.ProgramId = ncch.Exheader.TitleID; ncch.HeaderData.MakerCode = 0x3130; //01 ncch.HeaderData.FormatVersion = 0x2; //Default ncch.HeaderData.LogoHash = new SHA256Managed().ComputeHash(ncch.Logo); ncch.HeaderData.ProductCode = Encoding.ASCII.GetBytes(tbSerial); Array.Resize(ref ncch.HeaderData.ProductCode, 0x10); ncch.HeaderData.ExheaderHash = ncch.Exheader.GetSuperBlockHash(); ncch.HeaderData.ExheaderSize = (uint)ncch.Exheader.Data.Length; len += ncch.HeaderData.ExheaderSize + (uint)ncch.Exheader.AccessDescriptor.Length; ncch.HeaderData.Flags = new byte[0x8]; //FLAGS ncch.HeaderData.Flags[3] = 0; // Crypto: 0 = <7.x, 1=7.x; ncch.HeaderData.Flags[4] = 1; // Content Platform: 1 = CTR; ncch.HeaderData.Flags[5] = 0x3; // Content Type Bitflags: 1=Data, 2=Executable, 4=SysUpdate, 8=Manual, 0x10=Trial; ncch.HeaderData.Flags[6] = 0; // MEDIA_UNIT_SIZE = 0x200*Math.Pow(2, Content.header.Flags[6]); ncch.HeaderData.Flags[7] = 1; // FixedCrypto = 1, NoMountRomfs = 2; NoCrypto=4; ncch.HeaderData.LogoOffset = (uint)(len / MediaUnitSize); ncch.HeaderData.LogoSize = (uint)(ncch.Logo.Length / MediaUnitSize); len += (uint)ncch.Logo.Length; ncch.HeaderData.PlainRegionOffset = (uint)(ncch.Plainregion?.Length > 0 ? len / MediaUnitSize : 0); ncch.HeaderData.PlainRegionSize = (uint)(ncch.Plainregion?.Length ?? 0) / MediaUnitSize; len += (uint)(ncch.Plainregion?.Length ?? 0); ncch.HeaderData.ExefsOffset = (uint)(len / MediaUnitSize); ncch.HeaderData.ExefsSize = (uint)(ncch.Exefs.Data.Length / MediaUnitSize); ncch.HeaderData.ExefsSuperBlockSize = 0x200 / MediaUnitSize; //Static 0x200 for exefs superblock len += (uint)ncch.Exefs.Data.Length; len = (uint)Ctr.Align(len, 0x1000); //Romfs Start is aligned to 0x1000 ncch.HeaderData.RomfsOffset = (uint)(len / MediaUnitSize); ncch.HeaderData.RomfsSize = (uint)(new FileInfo(ncch.Romfs.FileName).Length / MediaUnitSize); ncch.HeaderData.RomfsSuperBlockSize = ncch.Romfs.SuperBlockLen / MediaUnitSize; len += ncch.HeaderData.RomfsSize * MediaUnitSize; ncch.HeaderData.ExefsHash = ncch.Exefs.SuperBlockHash; ncch.HeaderData.RomfsHash = ncch.Romfs.SuperBlockHash; ncch.HeaderData.Size = (uint)(len / MediaUnitSize); //Build the Header byte[]. ncch.HeaderData.BuildHeader(); return(ncch); }