// 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); }
public ulong GetWritableAddress() { const ulong mediaUnitSize = 0x200; return(this.Card2 ? Ncsd.Align(this.HeaderData.OffsetSizeTable[this.NcchArray.Count - 1].Offset * Ncch.MediaUnitSize + this.HeaderData.OffsetSizeTable[this.NcchArray.Count - 1].Size * Ncch.MediaUnitSize + 0x1000, 0x10000) / mediaUnitSize : 0x00000000FFFFFFFF); }
internal static bool WriteROM(Ncsd ncsd, string savePath) { using (FileStream outFileStream = new FileStream(savePath, FileMode.Create)) { outFileStream.Write(ncsd.Data, 0, ncsd.Data.Length); outFileStream.Write(ncsd.NcchArray[0].HeaderData.Data, 0, ncsd.NcchArray[0].HeaderData.Data.Length); //Write NCCH header //AES time. byte[] key = new byte[0x10]; //Fixed-Crypto key is all zero. for (int i = 0; i < 3; i++) { AesCtr aesctr = new AesCtr(key, ncsd.NcchArray[0].HeaderData.ProgramId, (ulong)(i + 1) << 56); //CTR is ProgramID, section id<<88 switch (i) { case 0: //Exheader + AccessDesc byte[] inEncExheader = new byte[ncsd.NcchArray[0].Exheader.Data.Length + ncsd.NcchArray[0].Exheader.AccessDescriptor.Length]; byte[] outEncExheader = new byte[ncsd.NcchArray[0].Exheader.Data.Length + ncsd.NcchArray[0].Exheader.AccessDescriptor.Length]; Array.Copy(ncsd.NcchArray[0].Exheader.Data, inEncExheader, ncsd.NcchArray[0].Exheader.Data.Length); Array.Copy(ncsd.NcchArray[0].Exheader.AccessDescriptor, 0, inEncExheader, ncsd.NcchArray[0].Exheader.Data.Length, ncsd.NcchArray[0].Exheader.AccessDescriptor.Length); aesctr.TransformBlock(inEncExheader, 0, inEncExheader.Length, outEncExheader, 0); outFileStream.Write(outEncExheader, 0, outEncExheader.Length); // Write Exheader break; case 1: //Exefs outFileStream.Seek(0x4000 + ncsd.NcchArray[0].HeaderData.ExefsOffset * MediaUnitSize, SeekOrigin.Begin); byte[] outExefs = new byte[ncsd.NcchArray[0].Exefs.Data.Length]; aesctr.TransformBlock(ncsd.NcchArray[0].Exefs.Data, 0, ncsd.NcchArray[0].Exefs.Data.Length, outExefs, 0); outFileStream.Write(outExefs, 0, outExefs.Length); break; case 2: //Romfs outFileStream.Seek(0x4000 + ncsd.NcchArray[0].HeaderData.RomfsOffset * MediaUnitSize, SeekOrigin.Begin); using (FileStream inFileStream = new FileStream(ncsd.NcchArray[0].Romfs.FileName, FileMode.Open, FileAccess.Read)) { uint bufferSize; ulong romfsLen = ncsd.NcchArray[0].HeaderData.RomfsSize * MediaUnitSize; for (ulong j = 0; j < romfsLen; j += bufferSize) { bufferSize = romfsLen - j > 0x400000 ? 0x400000 : (uint)(romfsLen - j); byte[] buf = new byte[bufferSize]; byte[] outbuf = new byte[bufferSize]; inFileStream.Read(buf, 0, (int)bufferSize); aesctr.TransformBlock(buf, 0, (int)bufferSize, outbuf, 0); outFileStream.Write(outbuf, 0, (int)bufferSize); } } break; } } outFileStream.Seek(0x4000 + ncsd.NcchArray[0].HeaderData.LogoOffset * MediaUnitSize, SeekOrigin.Begin); outFileStream.Write(ncsd.NcchArray[0].Logo, 0, ncsd.NcchArray[0].Logo.Length); if (ncsd.NcchArray[0].Plainregion.Length > 0) { outFileStream.Seek(0x4000 + ncsd.NcchArray[0].HeaderData.PlainRegionOffset * MediaUnitSize, SeekOrigin.Begin); outFileStream.Write(ncsd.NcchArray[0].Plainregion, 0, ncsd.NcchArray[0].Plainregion.Length); } //NCSD Padding outFileStream.Seek(ncsd.HeaderData.OffsetSizeTable[ncsd.NcchArray.Count - 1].Offset * MediaUnitSize + ncsd.HeaderData.OffsetSizeTable[ncsd.NcchArray.Count - 1].Size * MediaUnitSize, SeekOrigin.Begin); ulong totalLen = ncsd.HeaderData.MediaSize * MediaUnitSize; byte[] buffer = Enumerable.Repeat((byte)0xFF, 0x400000).ToArray(); while ((ulong)outFileStream.Position < totalLen) { int bufferLen = totalLen - (ulong)outFileStream.Position < 0x400000 ? (int)(totalLen - (ulong)outFileStream.Position) : 0x400000; outFileStream.Write(buffer, 0, bufferLen); } } //Delete Temporary Romfs File if (ncsd.NcchArray[0].Romfs.isTempFile) { File.Delete(ncsd.NcchArray[0].Romfs.FileName); } return(true); }
internal static Ncsd SetNcsd(Ncch ncch, bool card2) { Ncsd ncsd = new Ncsd { NcchArray = new List <Ncch> { ncch }, Card2 = card2, HeaderData = new Ncsd.Header { Signature = new byte[0x100], Magic = 0x4453434E } }; ulong length = 0x80 * 0x100000; // 128 MB while (length <= ncch.HeaderData.Size * MediaUnitSize + 0x400000) //Extra 4 MB for potential save data { length *= 2; } ncsd.HeaderData.MediaSize = (uint)(length / MediaUnitSize); ncsd.HeaderData.TitleId = ncch.Exheader.TitleID; ncsd.HeaderData.OffsetSizeTable = new Ncsd.NcchMeta[8]; ulong osOfs = 0x4000; for (int i = 0; i < ncsd.HeaderData.OffsetSizeTable.Length; i++) { Ncsd.NcchMeta ncchm = new Ncsd.NcchMeta(); if (i < ncsd.NcchArray.Count) { ncchm.Offset = (uint)(osOfs / MediaUnitSize); ncchm.Size = ncsd.NcchArray[i].HeaderData.Size; } else { ncchm.Offset = 0; ncchm.Size = 0; } ncsd.HeaderData.OffsetSizeTable[i] = ncchm; osOfs += ncchm.Size * MediaUnitSize; } ncsd.HeaderData.Flags = new byte[0x8]; ncsd.HeaderData.Flags[0] = 0; // 0-255 seconds of waiting for save writing. ncsd.HeaderData.Flags[3] = (byte)(ncsd.Card2 ? 2 : 1); // Media Card Device: 1 = NOR Flash, 2 = None, 3 = BT ncsd.HeaderData.Flags[4] = 1; // Media Platform Index: 1 = CTR ncsd.HeaderData.Flags[5] = (byte)(ncsd.Card2 ? 2 : 1); // Media Type Index: 0 = Inner Device, 1 = Card1, 2 = Card2, 3 = Extended Device ncsd.HeaderData.Flags[6] = 0; // Media Unit Size. Same as NCCH. ncsd.HeaderData.Flags[7] = 0; // Old Media Card Device. ncsd.HeaderData.NcchIdTable = new ulong[8]; for (int i = 0; i < ncsd.NcchArray.Count; i++) { ncsd.HeaderData.NcchIdTable[i] = ncsd.NcchArray[i].HeaderData.TitleId; } ncsd.Cardinfoheader = new Ncsd.CardInfoHeader { WritableAddress = (uint)ncsd.GetWritableAddress(), CardInfoBitmask = 0, Cin = new Ncsd.CardInfoHeader.CardInfoNotes { Reserved0 = new byte[0xF8], MediaSizeUsed = osOfs, Reserved1 = 0, Unknown = 0, Reserved2 = new byte[0xC], CVerTitleId = 0, CVerTitleVersion = 0, Reserved3 = new byte[0xCD6] }, Ncch0TitleId = ncsd.NcchArray[0].HeaderData.TitleId, Reserved0 = 0, InitialData = new byte[0x30] }; RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); byte[] randbuffer = new byte[0x2C]; rng.GetBytes(randbuffer); Array.Copy(randbuffer, ncsd.Cardinfoheader.InitialData, randbuffer.Length); ncsd.Cardinfoheader.Reserved1 = new byte[0xC0]; ncsd.Cardinfoheader.Ncch0Header = new byte[0x100]; Array.Copy(ncsd.NcchArray[0].HeaderData.Data, 0x100, ncsd.Cardinfoheader.Ncch0Header, 0, 0x100); ncsd.BuildHeader(); //NCSD is Initialized return(ncsd); }