Example #1
0
        internal static bool BuildHFS0(string inDir, string outFile, string multiplier)
        //internal static bool BuildHFS0(string inDir, string outFile)
        {
            Console.WriteLine("Building {0} from folder {1}...", outFile, inDir);
            int vmultiplier = Int32.Parse(multiplier);
            int secuhash    = 0x200 * vmultiplier;
            //Console.WriteLine("Round secuhash {0}", vmultiplier);
            hfs0_header header = new hfs0_header();

            header.Magic = 0x30534648; // HFS0

            List <hfs0_file_entry> fileEntries = new List <hfs0_file_entry>();
            List <string>          stringTable = new List <string>();

            char[]       objPath = new char[256];
            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);
            }

            // Opening input directory
            if (!Directory.Exists(inDir))
            {
                Console.WriteLine("[ERR] Input folder {0} does not exist.", inDir);
                return(false);
            }
            int           length_input = 0;
            int           file_number  = 0;
            List <string> inputFiles   = new List <string>();

            foreach (string file in Directory.GetFiles(inDir))
            {
                inputFiles.Add(Path.GetFileName(file));
                //length_input = length_input + 1 + Path.GetFileName(file).Length;
                //file_number = file_number + 1;
            }

            //Console.WriteLine("Length of string of files {0}", length_input);
            //Console.WriteLine("Number of files {0}", file_number);
            //float n_lines =  1+(4*file_number)+(length_input/16);
            //Console.WriteLine("Number of lines {0}", n_lines);
            //float mult = (n_lines/32);
            //int round_mult_up = (int)Math.Ceiling(mult);
            //Console.WriteLine("Multiplier {0}", mult);
            //Console.WriteLine("Round multiplier {0}", round_mult_up);
            //int secuhash2 = 0x200 * round_mult_up;



            // Handling root partitions for correct partition order
            if (inputFiles.Count >= 3 && inputFiles.Count <= 4 && inputFiles.Contains("secure") && inputFiles.Contains("normal") && inputFiles.Contains("update"))
            {
                if (inputFiles.Contains("logo"))
                {
                    Console.WriteLine("Treating {0} as CARD2 root hfs0", outFile);
                    inputFiles = new List <string>();
                    inputFiles.Add("update");
                    inputFiles.Add("normal");
                    inputFiles.Add("secure");
                    inputFiles.Add("logo");
                }
                else if (inputFiles.Count == 3)
                {
                    Console.WriteLine("Treating {0} as CARD1 root hfs0", outFile);
                    inputFiles = new List <string>();
                    inputFiles.Add("update");
                    inputFiles.Add("normal");
                    inputFiles.Add("secure");
                }
            }

            // Number of files in HFS0 archive
            header.NumberOfFiles = Convert.ToUInt32(inputFiles.Count);

            header.StringTableSize = 0;

            UInt64 fileEntry_relativeOffset = 0;

            // Building stringtable



            foreach (string file in inputFiles)
            {
                var absPath = Path.Combine(inDir, file);
                stringTable.Add(file);

                FileStream      inputFS       = new FileStream(absPath, FileMode.Open, FileAccess.Read);
                BinaryReader    inputFSReader = new BinaryReader(inputFS);
                hfs0_file_entry fileEntry     = new hfs0_file_entry();

                fileEntry.Offset = Convert.ToUInt64(fileEntry_relativeOffset);
                fileEntry.Size   = Convert.ToUInt64(inputFS.Length);

                UInt64 paddedSize = Convert.ToUInt64(Math.Ceiling((double)fileEntry.Size / (double)0x200) * 0x200);
                fileEntry_relativeOffset += paddedSize;

                if (inputFiles.Count < 3)
                {
                    fileEntry.HashedSize = 0x200;
                }
                else if (fileEntry.Size <= 0x200)
                {
                    fileEntry.HashedSize = 0x200;
                }
                else if (fileEntry.Size <= 0x128800)
                {
                    fileEntry.HashedSize = 0x200;
                }
                else if (header.NumberOfFiles < 5)
                {
                    fileEntry.HashedSize = Convert.ToUInt32(secuhash);
                }
                else if (fileEntry.Size > 0x200)
                {
                    fileEntry.HashedSize = 0x200;
                }
                else
                {
                    fileEntry.HashedSize = Convert.ToUInt32(fileEntry.Size);
                }

                byte[] dataToHash = new byte[fileEntry.HashedSize];
                inputFSReader.Read(dataToHash, 0, dataToHash.Length);

                inputFSReader.Close();
                inputFS.Close();

                fileEntry.StringTableOffset = header.StringTableSize;
                fileEntry.FileHash          = Program.SHA256.ComputeHash(dataToHash);


                header.StringTableSize += Convert.ToUInt32(file.Length + 1);

                fileEntries.Add(fileEntry);
            }

            // Calculate padding fo alignment
            ulong bytesWrittenUntilNow       = HFS0_HEADER_LENGTH + (HFS0_ENTRY_LENGTH * header.NumberOfFiles) + header.StringTableSize;
            ulong bytesWrittenUntilNowPadded = Convert.ToUInt64(Math.Ceiling((double)bytesWrittenUntilNow / (double)0x200) * 0x200);
            ulong bytesWrittenUntilNowDif    = bytesWrittenUntilNowPadded - bytesWrittenUntilNow;

            header.StringTableSize += Convert.ToUInt32(bytesWrittenUntilNowDif);

            bw.Write(Utils.StructureToByteArray(header));
            foreach (hfs0_file_entry fileEntry in fileEntries)
            {
                bw.Write(Utils.StructureToByteArray(fileEntry));
            }
            foreach (string str in stringTable)
            {
                bw.Write(str.ToCharArray());
                bw.Write((byte)0x00);
            }
            // Fill padding
            for (ulong i = 0; i < bytesWrittenUntilNowDif; i++)
            {
                bw.Write((byte)0x0);
            }
            foreach (string file in inputFiles)
            {
                var        absPath = Path.Combine(inDir, file);
                FileStream stream  = new FileStream(absPath, 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);
                }

                ulong paddedLength = Convert.ToUInt64(Math.Ceiling((double)stream.Length / (double)0x200) * 0x200);

                ulong difference = paddedLength - Convert.ToUInt64(stream.Length);

                for (ulong i = 0; i < difference; i++)
                {
                    bw.Write((byte)0x0);
                }
                stream.Close();
            }
            bw.Close();
            fs.Close();



            Console.WriteLine("Operation successful");
            return(true);
        }
Example #2
0
        public static bool BuildXCI(string inDir, string outFile, Random rand, SHA256 sha, Aes aes)
        {
            // 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];
            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;
                rand.NextBytes(header.GamecardIV);
                header.PackageID = Utils.LongRandom(0, 100000000000000000, 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 = sha.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        = aes.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);
        }