// Entrypoint static void Main(string[] args) { Console.WriteLine(""); Console.WriteLine("-----------------------------------------------------"); Console.WriteLine("Hacbuild by LucaFraga. Modded by JulesOnTheRoad. v1.1"); Console.WriteLine("-----------------------------------------------------"); Console.WriteLine("github.com/julesontheroad/hacbuild"); // Configure AES AES128CBC.BlockSize = 128; AES128CBC.Mode = CipherMode.CBC; AES128CBC.Padding = PaddingMode.Zeros; if (args.Length < 2) { PrintUsage(); return; } switch (args[0]) { case "read": switch (args[1]) { case "xci": if (args.Length < 3) { PrintUsage(); return; } Console.WriteLine("Reading {0}", args[2]); XCIManager.xci_header header = XCIManager.GetXCIHeader(args[2]); XCIManager.gamecard_info gcInfo = XCIManager.DecryptGamecardInfo(header); Console.WriteLine(header.ToString()); Console.WriteLine(gcInfo.ToString()); Directory.CreateDirectory(args[3]); string folder = args[3]; string iniPath = Path.Combine(folder, Path.GetFileNameWithoutExtension(args[2])) + ".ini"; IniFile ini = new IniFile(iniPath); ini.Write("PackageID", header.PackageID.ToString(), "XCI_Header"); ini.WriteBytes("GamecardIV", header.GamecardIV, "XCI_Header"); ini.Write("KEKIndex", header.KEK.ToString(), "XCI_Header"); ini.WriteBytes("InitialDataHash", header.InitialDataHash, "XCI_Header"); ini.Write("Version", gcInfo.Version.ToString(), "GameCard_Info"); ini.Write("AccessControlFlags", gcInfo.AccessControlFlags.ToString(), "GameCard_Info"); ini.Write("ReadWaitTime", gcInfo.ReadWaitTime.ToString(), "GameCard_Info"); ini.Write("ReadWaitTime2", gcInfo.ReadWaitTime2.ToString(), "GameCard_Info"); ini.Write("WriteWriteTime", gcInfo.WriteWriteTime.ToString(), "GameCard_Info"); ini.Write("WriteWriteTime2", gcInfo.WriteWriteTime2.ToString(), "GameCard_Info"); ini.Write("FirmwareMode", gcInfo.FirmwareMode.ToString(), "GameCard_Info"); ini.Write("CUPVersion", gcInfo.CUPVersion.ToString(), "GameCard_Info"); ini.Write("CUPID", gcInfo.CUPID.ToString(), "GameCard_Info"); // end dump to ini break; default: Console.WriteLine("Usage: hacbuild.exe read xci <IN>"); break; } break; case "gameinfo_ini": switch (args[1]) { case "xci": if (args.Length < 3) { PrintUsage(); return; } Console.WriteLine("Reading {0}", args[2]); XCIManager.xci_header header = XCIManager.GetXCIHeader(args[2]); XCIManager.gamecard_info gcInfo = XCIManager.DecryptGamecardInfo(header); Console.WriteLine(header.ToString()); Console.WriteLine(gcInfo.ToString()); Directory.CreateDirectory(args[3]); string folder = args[3]; string iniPath = folder + "/game_info.ini"; IniFile ini = new IniFile(iniPath); ini.Write("PackageID", header.PackageID.ToString(), "XCI_Header"); ini.WriteBytes("GamecardIV", header.GamecardIV, "XCI_Header"); ini.Write("KEKIndex", header.KEK.ToString(), "XCI_Header"); ini.WriteBytes("InitialDataHash", header.InitialDataHash, "XCI_Header"); ini.Write("Version", gcInfo.Version.ToString(), "GameCard_Info"); ini.Write("AccessControlFlags", gcInfo.AccessControlFlags.ToString(), "GameCard_Info"); ini.Write("ReadWaitTime", gcInfo.ReadWaitTime.ToString(), "GameCard_Info"); ini.Write("ReadWaitTime2", gcInfo.ReadWaitTime2.ToString(), "GameCard_Info"); ini.Write("WriteWriteTime", gcInfo.WriteWriteTime.ToString(), "GameCard_Info"); ini.Write("WriteWriteTime2", gcInfo.WriteWriteTime2.ToString(), "GameCard_Info"); ini.Write("FirmwareMode", gcInfo.FirmwareMode.ToString(), "GameCard_Info"); ini.Write("CUPVersion", gcInfo.CUPVersion.ToString(), "GameCard_Info"); ini.Write("CUPID", gcInfo.CUPID.ToString(), "GameCard_Info"); // end dump to ini break; default: Console.WriteLine("Usage: hacbuild.exe read xci <IN>"); break; } break; case "hfs0": if (args.Length < 3) { PrintUsage(); return; } string multiplier = HFS0Manager.xci_multiplier(args[1]); HFS0Manager.BuildHFS0(args[1], args[2], multiplier); Console.WriteLine("Done"); break; case "root_hfs0": if (args.Length < 3) { PrintUsage(); return; } HFS0Manager.BuildHFS0(args[1], args[2], args[3]); break; case "xci": if (args.Length < 3) { PrintUsage(); return; } XCIManager.BuildXCI(args[1], args[2]); Console.WriteLine("Done"); break; case "xci_auto": if (args.Length < 3) { PrintUsage(); return; } string inPath = Path.Combine(Environment.CurrentDirectory, args[1]); string outPath = Path.Combine(Environment.CurrentDirectory, args[2]); string tmpPath = Path.Combine(inPath, "root_tmp"); Directory.CreateDirectory(tmpPath); Console.WriteLine("...................................."); Console.WriteLine("Preparing to build secure partition"); Console.WriteLine("...................................."); string multiplier1 = HFS0Manager.xci_multiplier(Path.Combine(inPath, "secure")); HFS0Manager.BuildHFS0(Path.Combine(inPath, "secure"), Path.Combine(tmpPath, "secure"), multiplier1); Console.WriteLine("...................................."); Console.WriteLine("Preparing to build normal partition"); Console.WriteLine("...................................."); string multiplier2 = HFS0Manager.xci_multiplier(Path.Combine(inPath, "normal")); HFS0Manager.BuildHFS0(Path.Combine(inPath, "normal"), Path.Combine(tmpPath, "normal"), multiplier2); Console.WriteLine("...................................."); Console.WriteLine("Preparing to build update partition"); Console.WriteLine("...................................."); string multiplier3 = HFS0Manager.xci_multiplier(Path.Combine(inPath, "update")); HFS0Manager.BuildHFS0(Path.Combine(inPath, "update"), Path.Combine(tmpPath, "update"), multiplier3); if (Directory.Exists(Path.Combine(inPath, "logo"))) { Console.WriteLine("...................................."); Console.WriteLine("Preparing to build logo partition"); Console.WriteLine("...................................."); string multiplier4 = HFS0Manager.xci_multiplier(Path.Combine(inPath, "logo")); HFS0Manager.BuildHFS0(Path.Combine(inPath, "logo"), Path.Combine(tmpPath, "logo"), multiplier4); } Console.WriteLine("...................................."); Console.WriteLine("Preparing to build root.hfs0"); Console.WriteLine("...................................."); HFS0Manager.BuildHFS0(tmpPath, Path.Combine(inPath, "root.hfs0"), multiplier1); Directory.Delete(tmpPath, true); Console.WriteLine("...................................."); Console.WriteLine("Building xci"); Console.WriteLine("...................................."); XCIManager.BuildXCI(inPath, outPath); Console.WriteLine("DONE"); Console.WriteLine("...................................."); Console.WriteLine("Erasing root.hfs0"); Console.WriteLine("...................................."); File.Delete(Path.Combine(inPath, "root.hfs0")); Console.WriteLine("DONE"); break; case "xci_auto_del": if (args.Length < 3) { PrintUsage(); return; } string inPath_d = Path.Combine(Environment.CurrentDirectory, args[1]); string outPath_d = Path.Combine(Environment.CurrentDirectory, args[2]); string tmpPath_d = Path.Combine(inPath_d, "root_tmp"); Directory.CreateDirectory(tmpPath_d); Console.WriteLine("...................................."); Console.WriteLine("Preparing to build secure partition"); Console.WriteLine("...................................."); string multiplier1_d = HFS0Manager.xci_multiplier(Path.Combine(inPath_d, "secure")); HFS0Manager.BuildHFS0(Path.Combine(inPath_d, "secure"), Path.Combine(tmpPath_d, "secure"), multiplier1_d); Directory.Delete(Path.Combine(inPath_d, "secure"), true); Console.WriteLine("...................................."); Console.WriteLine("Preparing to build normal partition"); Console.WriteLine("...................................."); string multiplier2_d = HFS0Manager.xci_multiplier(Path.Combine(inPath_d, "normal")); HFS0Manager.BuildHFS0(Path.Combine(inPath_d, "normal"), Path.Combine(tmpPath_d, "normal"), multiplier2_d); Console.WriteLine("...................................."); Directory.Delete(Path.Combine(inPath_d, "normal"), true); Console.WriteLine("Preparing to build update partition"); Console.WriteLine("...................................."); string multiplier3_d = HFS0Manager.xci_multiplier(Path.Combine(inPath_d, "update")); HFS0Manager.BuildHFS0(Path.Combine(inPath_d, "update"), Path.Combine(tmpPath_d, "update"), multiplier3_d); Directory.Delete(Path.Combine(inPath_d, "update"), true); if (Directory.Exists(Path.Combine(inPath_d, "logo"))) { Console.WriteLine("...................................."); Console.WriteLine("Preparing to build logo partition"); Console.WriteLine("...................................."); string multiplier4 = HFS0Manager.xci_multiplier(Path.Combine(inPath_d, "logo")); HFS0Manager.BuildHFS0(Path.Combine(inPath_d, "logo"), Path.Combine(tmpPath_d, "logo"), multiplier4); Directory.Delete(Path.Combine(inPath_d, "logo"), true); } Console.WriteLine("...................................."); Console.WriteLine("Preparing to build root.hfs0"); Console.WriteLine("...................................."); HFS0Manager.BuildHFS0(tmpPath_d, Path.Combine(inPath_d, "root.hfs0"), multiplier1_d); Directory.Delete(tmpPath_d, true); Console.WriteLine("...................................."); Console.WriteLine("Building xci"); Console.WriteLine("...................................."); XCIManager.BuildXCI(inPath_d, outPath_d); Console.WriteLine("DONE"); Console.WriteLine("...................................."); Console.WriteLine("Erasing root.hfs0"); Console.WriteLine("...................................."); File.Delete(Path.Combine(inPath_d, "root.hfs0")); Console.WriteLine("DONE"); break; case "rhfs0_auto": if (args.Length < 2) { PrintUsage(); return; } string inPath_rh = Path.Combine(Environment.CurrentDirectory, args[1]); string tmpPath_rh = Path.Combine(inPath_rh, "root_tmp"); Directory.CreateDirectory(tmpPath_rh); Console.WriteLine("...................................."); Console.WriteLine("Preparing to build secure partition"); Console.WriteLine("...................................."); string multiplier1_rh = HFS0Manager.xci_multiplier(Path.Combine(inPath_rh, "secure")); HFS0Manager.BuildHFS0(Path.Combine(inPath_rh, "secure"), Path.Combine(tmpPath_rh, "secure"), multiplier1_rh); Console.WriteLine("...................................."); Console.WriteLine("Preparing to build normal partition"); Console.WriteLine("...................................."); string multiplier2_rh = HFS0Manager.xci_multiplier(Path.Combine(inPath_rh, "normal")); HFS0Manager.BuildHFS0(Path.Combine(inPath_rh, "normal"), Path.Combine(tmpPath_rh, "normal"), multiplier2_rh); Console.WriteLine("...................................."); Console.WriteLine("Preparing to build update partition"); Console.WriteLine("...................................."); string multiplier3_rh = HFS0Manager.xci_multiplier(Path.Combine(inPath_rh, "update")); HFS0Manager.BuildHFS0(Path.Combine(inPath_rh, "update"), Path.Combine(tmpPath_rh, "update"), multiplier3_rh); if (Directory.Exists(Path.Combine(inPath_rh, "logo"))) { Console.WriteLine("...................................."); Console.WriteLine("Preparing to build logo partition"); Console.WriteLine("...................................."); string multiplier4 = HFS0Manager.xci_multiplier(Path.Combine(inPath_rh, "logo")); HFS0Manager.BuildHFS0(Path.Combine(inPath_rh, "logo"), Path.Combine(tmpPath_rh, "logo"), multiplier4); } Console.WriteLine("...................................."); Console.WriteLine("Preparing to build root.hfs0"); Console.WriteLine("...................................."); HFS0Manager.BuildHFS0(tmpPath_rh, Path.Combine(inPath_rh, "root.hfs0"), multiplier1_rh); Directory.Delete(tmpPath_rh, true); break; case "rhfs0_auto_del": if (args.Length < 2) { PrintUsage(); return; } string inPath_rd = Path.Combine(Environment.CurrentDirectory, args[1]); string tmpPath_rd = Path.Combine(inPath_rd, "root_tmp"); Directory.CreateDirectory(tmpPath_rd); Console.WriteLine("...................................."); Console.WriteLine("Preparing to build secure partition"); Console.WriteLine("...................................."); string multiplier1_rd = HFS0Manager.xci_multiplier(Path.Combine(inPath_rd, "secure")); HFS0Manager.BuildHFS0(Path.Combine(inPath_rd, "secure"), Path.Combine(tmpPath_rd, "secure"), multiplier1_rd); Directory.Delete(Path.Combine(inPath_rd, "secure"), true); Console.WriteLine("...................................."); Console.WriteLine("Preparing to build normal partition"); Console.WriteLine("...................................."); string multiplier2_rd = HFS0Manager.xci_multiplier(Path.Combine(inPath_rd, "normal")); HFS0Manager.BuildHFS0(Path.Combine(inPath_rd, "normal"), Path.Combine(tmpPath_rd, "normal"), multiplier2_rd); Console.WriteLine("...................................."); Directory.Delete(Path.Combine(inPath_rd, "normal"), true); Console.WriteLine("Preparing to build update partition"); Console.WriteLine("...................................."); string multiplier3_rd = HFS0Manager.xci_multiplier(Path.Combine(inPath_rd, "update")); HFS0Manager.BuildHFS0(Path.Combine(inPath_rd, "update"), Path.Combine(tmpPath_rd, "update"), multiplier3_rd); Directory.Delete(Path.Combine(inPath_rd, "update"), true); Console.WriteLine("...................................."); Console.WriteLine("Preparing to build logo partition"); Console.WriteLine("...................................."); if (Directory.Exists(Path.Combine(inPath_rd, "logo"))) { string multiplier4 = HFS0Manager.xci_multiplier(Path.Combine(inPath_rd, "logo")); HFS0Manager.BuildHFS0(Path.Combine(inPath_rd, "logo"), Path.Combine(tmpPath_rd, "logo"), multiplier4); Directory.Delete(Path.Combine(inPath_rd, "logo"), true); } Console.WriteLine("...................................."); Console.WriteLine("Preparing to build root.hfs0"); Console.WriteLine("...................................."); HFS0Manager.BuildHFS0(tmpPath_rd, Path.Combine(inPath_rd, "root.hfs0"), multiplier1_rd); Directory.Delete(tmpPath_rd, true); break; default: PrintUsage(); break; } }
internal static bool BuildXCI(string inDir, string outFile) { // root.hfs0 contains secure/update/normal which are hfs0 on their owno string rootPath = Path.Combine(inDir, "root.hfs0"); // gameData.ini contains vital data to build the XCI string iniPath = Path.Combine(inDir, "game_info.ini"); if (!File.Exists(rootPath)) { Console.WriteLine("[ERR] {0} does not exist.", rootPath); return(false); } xci_header header = new xci_header(); gamecard_info gcInfo = new gamecard_info(); // Static stuff header.Magic = 0x44414548; // HEAD < -- XCI Header header.BackupAreaAddress = 0xFFFFFFFF; // This is probably reserved for future use header.HeaderVersion = (byte)0x0; // This is probably reserved for future use header.Flag = 0; // This is probably reserved for future use header.SecureModeFlag = 1; // Secure mode enabled header.TitleKeyFlag = 2; header.KeyFlag = 0; // Fake RSA signature byte[] fakeSignature = new byte[XCI_SIGNATURE_SIZE]; Program.Rand.NextBytes(fakeSignature); header.Signature = fakeSignature; // Ini loaded stuff if (File.Exists(iniPath)) { IniFile iniFile = new IniFile(iniPath); // Header header.KEK = byte.Parse(iniFile.Read("KEKIndex", "XCI_Header")); header.GamecardIV = iniFile.ReadBytes("GamecardIV", "XCI_Header"); header.InitialDataHash = iniFile.ReadBytes("InitialDataHash", "XCI_Header"); header.PackageID = UInt64.Parse(iniFile.Read("PackageID", "XCI_Header")); // Gamecard Info gcInfo.Version = UInt64.Parse(iniFile.Read("Version", "GameCard_Info")); gcInfo.AccessControlFlags = UInt32.Parse(iniFile.Read("AccessControlFlags", "GameCard_Info")); gcInfo.ReadWaitTime = UInt32.Parse(iniFile.Read("ReadWaitTime", "GameCard_Info")); gcInfo.ReadWaitTime2 = UInt32.Parse(iniFile.Read("ReadWaitTime2", "GameCard_Info")); gcInfo.WriteWriteTime = UInt32.Parse(iniFile.Read("WriteWriteTime", "GameCard_Info")); gcInfo.WriteWriteTime2 = UInt32.Parse(iniFile.Read("WriteWriteTime2", "GameCard_Info")); gcInfo.FirmwareMode = UInt32.Parse(iniFile.Read("FirmwareMode", "GameCard_Info")); gcInfo.CUPVersion = UInt32.Parse(iniFile.Read("CUPVersion", "GameCard_Info")); gcInfo.CUPID = UInt64.Parse(iniFile.Read("CUPID", "GameCard_Info")); } else { Console.WriteLine("[WARN] {0} does not exist. Data will be randomized (and the XCI could not work)", iniPath); // Header header.KEK = 0; Program.Rand.NextBytes(header.GamecardIV); header.PackageID = Utils.LongRandom(0, 100000000000000000, Program.Rand); // Gamecard Info - Taken from Cave Story (and pretty much universal) gcInfo.Version = 1; gcInfo.AccessControlFlags = 10551313; gcInfo.ReadWaitTime = 5000; gcInfo.ReadWaitTime2 = 0; gcInfo.WriteWriteTime = 0; gcInfo.WriteWriteTime2 = 0; gcInfo.FirmwareMode = 790784; gcInfo.CUPVersion = 450; gcInfo.CUPID = 72057594037930006; } // Read root.hfs0 raw (for header size and hash) byte[] rootHeader = HFS0Manager.GetHFS0Header(rootPath); header.HFS0HeaderSize = Convert.ToUInt64(rootHeader.Length); // hfs0 should be 0x200 aligned in order to properly work. // TODO should it? if (header.HFS0HeaderSize % 0x200 != 0) { Console.WriteLine("[WARN] root.hfs0 is not 0x200 aligned."); } // Calculating SHA256 of root.hfs0 header header.HFS0HeaderHash = Program.SHA256.ComputeHash(rootHeader); // Read root.hfs0 (for partition details) HFS0Manager.hfs0_header rootHeaderManaged = new HFS0Manager.hfs0_header(); List <HFS0Manager.hfs0_file_entry> rootFileEntries = new List <HFS0Manager.hfs0_file_entry>(); List <string> rootStringTable = new List <string>(); HFS0Manager.GetHFS0Managed(rootPath, ref rootHeaderManaged, ref rootFileEntries, ref rootStringTable); int partitionIndex = 0; foreach (var fileName in rootStringTable) { if (fileName == "secure") { UInt64 secureOffset = Convert.ToUInt64(HFS0_START + header.HFS0HeaderSize + rootFileEntries[partitionIndex].Offset); if (secureOffset % 0x200 != 0) { Console.WriteLine("[WARN] secure.hfs0 is not 0x200 aligned."); } // This is fine since we force partition order header.SecureOffset = header.NormalAreaEndAddress = Convert.ToUInt32(secureOffset / 0x200); } partitionIndex++; } header.HFS0Offset = HFS0_START; UInt64 CardSize = Convert.ToUInt64(HFS0_START + (ulong)(new FileInfo(rootPath).Length)); if (CardSize % 0x200 != 0) { Console.WriteLine("[WARN] card size is not 0x200 aligned."); } header.CardSize = CardSize / 0x200 - 1; // Excludes signature if (header.CardSize * 0x200l > 16l * 1024l * 1024l * 1024l) { header.CartType = CartridgeType.CARTSIZE_32GB; } else if (header.CardSize * 0x200l > 8l * 1024l * 1024l * 1024l) { header.CartType = CartridgeType.CARTSIZE_16GB; } else if (header.CardSize * 0x200l > 4l * 1024l * 1024l * 1024l) { header.CartType = CartridgeType.CARTSIZE_8GB; } else if (header.CardSize * 0x200l > 2l * 1024l * 1024l * 1024l) { header.CartType = CartridgeType.CARTSIZE_4GB; } else { header.CartType = CartridgeType.CARTSIZE_2GB; } // Write encrypted gamecardinfo header byte[] rawGameCardInfo = Utils.StructureToByteArray(gcInfo); // REVERSE THA IV byte[] iv_flipped = new byte[XCIManager.XCI_IV_SIZE]; Array.Copy(header.GamecardIV, iv_flipped, XCIManager.XCI_IV_SIZE); Array.Reverse(iv_flipped); // GameCardInfo Encrypt var encrypt = Program.AES128CBC.CreateEncryptor(XCIManager.XCI_GAMECARDINFO_KEY, iv_flipped); var gcInfoStream = new MemoryStream(rawGameCardInfo); var gcCryptoStream = new CryptoStream(gcInfoStream, encrypt, CryptoStreamMode.Read); // Create the struct byte[] rawEncryptedGameCardInfo = new byte[XCI_GAMECARD_INFO_LENGTH]; gcCryptoStream.Read(rawEncryptedGameCardInfo, 0, XCI_GAMECARD_INFO_LENGTH); // Clean stuff up gcInfoStream.Close(); gcCryptoStream.Close(); encrypt.Dispose(); header.GamecardInfo = rawEncryptedGameCardInfo; FileStream fs; BinaryWriter bw; // Opening output file try { fs = new FileStream(outFile, FileMode.Create, FileAccess.Write); bw = new BinaryWriter(fs); } catch (Exception ex) { Console.WriteLine("[ERR] Cannot create {0}.\n{1}", outFile, ex.Message); return(false); } // Writing header bw.Write(Utils.StructureToByteArray(header)); // Writing padding int toWrite00 = 0x6E00; for (int i = 0; i < toWrite00; i++) { bw.Write((byte)0x0); } int toWriteFF = 0x8000; for (int i = 0; i < toWriteFF; i++) { bw.Write((byte)0xFF); } // Writing data FileStream stream = new FileStream(rootPath, FileMode.Open, FileAccess.Read); byte[] buffer = new Byte[1024 * 5]; int count = 0; while ((count = stream.Read(buffer, 0, buffer.Length)) > 0) { bw.Write(buffer, 0, count); } stream.Close(); return(true); }