Esempio n. 1
0
        public static void Main(string[] cmdArgs)
        {
            OptionSet optionSet = new OptionSet
            {
                { "h|help", "Show this message and exit", _ => ShowHelp = true },
                { "v|dev=", "Load production keys as development keys\n(optional)", _ => IsDev = true },
                { "p|prodkeys=", "Path to a file containing switch production keys.\n(optional)", s => ProdKeyPath = s },
                { "k|titlekeys=", "Path to a file containing switch title keys.\n(optional)", s => TitleKeyPath = s },
                { "l|level=", "zStd compression level used to compress the file.", s => CompressionLevel = byte.Parse(s) },
                { "f|framesize=", "Size of a frame used to split a file.", s => FrameSize = uint.Parse(s) },
                { "t|temp=", "The directory to use for storing temp files.\n(Defaults to OS temp)", s => TempPath = s },
                { "o|output=", "The directory to output the compressed file.\n(Defaults to the same dir as input file)", s => OutDirectoryPath = s }
            };

            List <string> args = optionSet.Parse(cmdArgs);

            if (ShowHelp)
            {
                Console.WriteLine("ZcaTool - Copyright (c) 2020 Xpl0itR");
                Console.WriteLine("Usage: ZcaTool(.exe) [options] <path>");
                Console.WriteLine("Options:");
                optionSet.WriteOptionDescriptions(Console.Out);
                return;
            }

            if (args.Count < 1 || !File.Exists(args[0]))
            {
                throw new Exception("Input file does not exist!");
            }

            if (CompressionLevel < 1 || CompressionLevel > 22)
            {
                throw new Exception("You must enter a valid compression level!");
            }

            KeySet = LoadKeySet();
            OutDirectoryPath ??= Path.GetDirectoryName(args[0]);

            using (IStorage inStorage = new LocalStorage(args[0], FileAccess.Read))
            {
                string fileName = Path.GetFileNameWithoutExtension(args[0]);
                inStorage.GetSize(out long inSize);
                IStorage outStorage = null;

                Stopwatch stopwatch = Stopwatch.StartNew();
                switch (Path.GetExtension(args[0]).ToLower())
                {
                case ".nca":
                    Console.WriteLine($"Compressing {Path.GetFileName(args[0])} [{PrettyFileSize(inSize)}] with ZStandard compression level: {CompressionLevel} and frame size: {FrameSize}");
                    fileName += ".zca";
                    break;

                case ".zca":
                    Console.WriteLine($"Decompressing {Path.GetFileName(args[0])} [{PrettyFileSize(inSize)}]");
                    outStorage = new Nca(KeySet, new ZraDecompressionStream(inStorage.AsStream()).AsStorage()).OpenEncryptedNca();
                    fileName  += ".nca";
                    break;

                case ".nsp":
                    Console.WriteLine($"Compressing {Path.GetFileName(args[0])} [{PrettyFileSize(inSize)}] with ZStandard compression level: {CompressionLevel} and frame size: {FrameSize}");
                    outStorage = ProcessPartitionFileSystem(new PartitionFileSystem(inStorage), PartitionFileSystemType.Standard, true);
                    fileName  += ".zsp";
                    break;

                case ".zsp":
                    Console.WriteLine($"Decompressing {Path.GetFileName(args[0])} [{PrettyFileSize(inSize)}]");
                    outStorage = ProcessPartitionFileSystem(new PartitionFileSystem(inStorage), PartitionFileSystemType.Standard, false);
                    fileName  += ".nsp";
                    break;

                case ".xci":
                    Console.WriteLine($"Compressing {Path.GetFileName(args[0])} [{PrettyFileSize(inSize)}] with ZStandard compression level: {CompressionLevel} and frame size: {FrameSize}");
                    outStorage = ProcessXci(inStorage, true);
                    fileName  += ".zci";
                    break;

                case ".zci":
                    Console.WriteLine($"Decompressing {Path.GetFileName(args[0])} [{PrettyFileSize(inSize)}]");
                    outStorage = ProcessXci(inStorage, false);
                    fileName  += ".xci";
                    break;

                default:
                    throw new Exception("Input file was not of a valid format!");
                }

                long   outSize;
                string filePath = Path.Join(OutDirectoryPath, fileName);
                using (FileStream outStream = File.OpenWrite(filePath))
                {
                    if (Path.GetExtension(args[0]).ToLower() == ".nca")
                    {
                        (IStorage processedNca, byte[] metaBuffer) = ProcessNca(inStorage);
                        processedNca.GetSize(out long ncaLength);

                        using (ZraCompressionStream compressionStream = new ZraCompressionStream(outStream, (ulong)ncaLength, CompressionLevel, FrameSize, metaBuffer, true))
                        {
                            processedNca.CopyToStream(compressionStream, (int)FrameSize);
                        }
                    }
                    else
                    {
                        outStorage.CopyToStream(outStream);
                        outStorage?.Dispose();
                    }

                    outSize = outStream.Length;
                }
                stopwatch.Stop();

                Console.WriteLine($"Out file: {filePath} [{PrettyFileSize(outSize)}]");
                Console.WriteLine($"Time taken: {decimal.Round((decimal)stopwatch.ElapsedMilliseconds / 1000, 2)}s ({stopwatch.ElapsedMilliseconds}ms)");
                Console.WriteLine($"Size Reduction: {decimal.Truncate(100 - (decimal)outSize / inSize * 100)}%");
            }

            Console.WriteLine("Cleaning temp files...");
            ZraCompressionStorageHack.CleanTempFiles();
            Console.WriteLine("Done!");
        }