public static void Main(string[] args)
        {
            bool unpackNestedPacks = true;
            bool verbose           = false;
            bool showHelp          = false;

            var options = new OptionSet()
            {
                { "d|dont-unpack-nested-packs", "don't unpack nested .pack files", v => unpackNestedPacks = v == null },
                { "v|verbose", "be verbose", v => verbose = v != null },
                { "h|help", "show this message and exit", v => showHelp = v != null },
            };

            List <string> extra;

            try
            {
                extra = options.Parse(args);
            }
            catch (OptionException e)
            {
                Console.Write("{0}: ", GetExecutableName());
                Console.WriteLine(e.Message);
                Console.WriteLine("Try `{0} --help' for more information.", GetExecutableName());
                return;
            }

            if (extra.Count < 1 || extra.Count > 2 || showHelp == true)
            {
                Console.WriteLine("Usage: {0} [OPTIONS]+ input_FILETABLE [output_directory]", GetExecutableName());
                Console.WriteLine("Unpack specified archive.");
                Console.WriteLine();
                Console.WriteLine("Options:");
                options.WriteOptionDescriptions(Console.Out);
                return;
            }

            string inputPath      = extra[0];
            string outputBasePath = extra.Count > 1 ? extra[1] : Path.ChangeExtension(inputPath, null) + "_unpacked";

            FileTableFile table;

            using (var input = File.OpenRead(inputPath))
            {
                table = new FileTableFile();
                table.Deserialize(input);
            }

            var inputBasePath = Path.GetDirectoryName(inputPath);

            // TODO(gibbed):
            //  - generate file index for successful repacking
            //  - better name lookup for name hashes (FNV32)
            //    (don't hardcode the list)
            var names = new string[]
            {
                "MENU_COMMON_PACK",
                "MENU_TEXTURE_MISC_PACK",
                "MN_AT_ORGANIZE",
                "MN_BIRTHDAY",
                "MN_BT_MAIN",
                "MN_BT_RESULT",
                "MN_COMMON",
                "MN_COMMONWIN",
                "MN_EVENT",
                "MN_INPUT",
                "MN_ITEMICON",
                "MN_KEY_LAYOUT",
                "MN_MOVIE",
                "MN_NETWORK",
                "MN_OPTION",
                "MN_ORGANIZE",
                "MN_SHOP2",
                "MN_STAFFROLL",
                "MN_STATUS",
                "MN_TITLE",
                "MN_WARRENREPORT",
                "MN_WORLD",
            };
            var nameHashLookup = names.ToDictionary(v => v.HashFNV32(), v => v);

            var tableManifestPath = Path.Combine(outputBasePath, "@manifest.json");
            var tableManifest     = new FileTableManifest()
            {
                Endian               = table.Endian,
                TitleId1             = table.TitleId1,
                TitleId2             = table.TitleId2,
                Unknown32            = table.Unknown32,
                ParentalLevel        = table.ParentalLevel,
                InstallDataCryptoKey = table.InstallDataCryptoKey,
            };

            foreach (var directory in table.Directories)
            {
                var tableDirectory = new TableDirectory()
                {
                    Id       = directory.Id,
                    BasePath = Path.Combine(outputBasePath, $"{directory.Id}"),
                };

                var fileContainers = new List <IFileContainer>()
                {
                    tableDirectory,
                };

                var binPath = Path.Combine(inputBasePath, $"{directory.Id:X4}.BIN");
                using (var input = File.OpenRead(binPath))
                {
                    var fileQueue = new Queue <QueuedFile>();
                    foreach (var file in directory.Files)
                    {
                        long dataOffset;
                        dataOffset  = directory.DataBaseOffset;
                        dataOffset += (file.DataBlockOffset << directory.DataBlockSize) * FileTableFile.BaseDataBlockSize;

                        fileQueue.Enqueue(new QueuedFile()
                        {
                            Id         = file.Id,
                            Parent     = tableDirectory,
                            NameHash   = file.NameHash,
                            DataOffset = dataOffset,
                            DataSize   = file.DataSize,
                        });
                    }

                    while (fileQueue.Count > 0)
                    {
                        var file   = fileQueue.Dequeue();
                        var parent = file.Parent;

                        var nameBuilder = new StringBuilder();
                        nameBuilder.Append($"{file.Id}");

                        string name = null;
                        if (file.NameHash != null)
                        {
                            if (nameHashLookup.TryGetValue(file.NameHash.Value, out name) == true)
                            {
                                nameBuilder.Append($"_{name}");
                            }
                            else
                            {
                                nameBuilder.Append($"_HASH[{file.NameHash.Value:X8}]");
                            }
                        }

                        if (parent.IdCounts != null)
                        {
                            var idCounts = parent.IdCounts;
                            int idCount;
                            idCounts.TryGetValue(file.Id, out idCount);
                            idCount++;
                            idCounts[file.Id] = idCount;

                            if (idCount > 1)
                            {
                                nameBuilder.Append($"_DUP_{idCount}");
                            }
                        }

                        if (unpackNestedPacks == true && file.DataSize >= 8)
                        {
                            input.Position = file.DataOffset;
                            var fileMagic = input.ReadValueU32(Endian.Little);
                            if (fileMagic == PackFile.Signature || fileMagic.Swap() == PackFile.Signature)
                            {
                                input.Position = file.DataOffset;
                                var nestedPack = HandleNestedPack(input, fileQueue, file.Id, nameBuilder.ToString(), parent);
                                fileContainers.Add(nestedPack);
                                parent.FileManifests.Add(new FileTableManifest.File()
                                {
                                    Id       = file.Id,
                                    NameHash = file.NameHash,
                                    Name     = name,
                                    IsPack   = true,
                                    PackId   = PackId.Create(file.PackRawId),
                                    Path     = CleanPathForManifest(PathHelper.GetRelativePath(parent.BasePath, nestedPack.ManifestPath)),
                                });
                                continue;
                            }
                        }

                        var outputPath = Path.Combine(parent.BasePath, nameBuilder.ToString());

                        var outputParentPath = Path.GetDirectoryName(outputPath);
                        if (string.IsNullOrEmpty(outputParentPath) == false)
                        {
                            Directory.CreateDirectory(outputParentPath);
                        }

                        input.Position = file.DataOffset;
                        var extension = FileDetection.Guess(input, (int)file.DataSize, file.DataSize);
                        outputPath = Path.ChangeExtension(outputPath, extension);

                        if (verbose == true)
                        {
                            Console.WriteLine(outputPath);
                        }

                        input.Position = file.DataOffset;
                        using (var output = File.Create(outputPath))
                        {
                            output.WriteFromStream(input, file.DataSize);
                        }

                        parent.FileManifests.Add(new FileTableManifest.File()
                        {
                            Id       = file.Id,
                            NameHash = file.NameHash,
                            Name     = name,
                            PackId   = PackId.Create(file.PackRawId),
                            Path     = CleanPathForManifest(PathHelper.GetRelativePath(parent.BasePath, outputPath)),
                        });
                    }
                }

                foreach (var fileContainer in fileContainers)
                {
                    WriteManifest(fileContainer.ManifestPath, fileContainer);
                }

                tableManifest.Directories.Add(new FileTableManifest.Directory()
                {
                    Id              = directory.Id,
                    DataBlockSize   = directory.DataBlockSize,
                    IsInInstallData = directory.IsInInstallData,
                    FileManifest    = CleanPathForManifest(
                        PathHelper.GetRelativePath(outputBasePath, tableDirectory.ManifestPath)),
                });
            }

            WriteManifest(tableManifestPath, tableManifest);
        }
        public static void Main(string[] args)
        {
            bool verbose  = false;
            bool showHelp = false;

            var options = new OptionSet()
            {
                { "v|verbose", "be verbose (list files)", v => verbose = v != null },
                { "h|help", "show this message and exit", v => showHelp = v != null },
            };

            List <string> extra;

            try
            {
                extra = options.Parse(args);
            }
            catch (OptionException e)
            {
                Console.Write("{0}: ", GetExecutableName());
                Console.WriteLine(e.Message);
                Console.WriteLine("Try `{0} --help' for more information.", GetExecutableName());
                return;
            }

            if (extra.Count < 1 || extra.Count > 2 || showHelp == true)
            {
                Console.WriteLine("Usage: {0} [OPTIONS]+ input_pack [output_directory]", GetExecutableName());
                Console.WriteLine("Unpack specified archive.");
                Console.WriteLine();
                Console.WriteLine("Options:");
                options.WriteOptionDescriptions(Console.Out);
                return;
            }

            string inputPath      = extra[0];
            string outputBasePath = extra.Count > 1 ? extra[1] : Path.ChangeExtension(inputPath, null) + "_unpacked";

            Directory.CreateDirectory(outputBasePath);

            using (var input = File.OpenRead(inputPath))
            {
                var header = new PackFile();
                header.Deserialize(input);

                var entryCount = header.Entries.Count;
                for (int i = 0; i < entryCount; i++)
                {
                    var outputPath = Path.Combine(outputBasePath, $"{i}");

                    uint entryOffset     = header.Entries[i].Offset;
                    uint nextEntryOffset = i + 1 >= entryCount ? header.TotalSize : header.Entries[i + 1].Offset;
                    uint entrySize       = nextEntryOffset - entryOffset;

                    input.Position = entryOffset;
                    var extension = FileDetection.Guess(input, (int)entrySize, entrySize);
                    outputPath = Path.ChangeExtension(outputPath, extension);

                    if (verbose == true)
                    {
                        Console.WriteLine(outputPath);
                    }

                    input.Position = entryOffset;
                    using (var output = File.Create(outputPath))
                    {
                        output.WriteFromStream(input, entrySize);
                    }
                }
            }
        }
示例#3
0
        public static void Main(string[] args)
        {
            bool verbose  = false;
            bool showHelp = false;

            var options = new OptionSet()
            {
                { "v|verbose", "be verbose", v => verbose = v != null },
                { "h|help", "show this message and exit", v => showHelp = v != null },
            };

            List <string> extra;

            try
            {
                extra = options.Parse(args);
            }
            catch (OptionException e)
            {
                Console.Write("{0}: ", GetExecutableName());
                Console.WriteLine(e.Message);
                Console.WriteLine("Try `{0} --help' for more information.", GetExecutableName());
                return;
            }

            if (extra.Count < 1 || extra.Count > 2 || showHelp == true)
            {
                Console.WriteLine("Usage: {0} [OPTIONS]+ input_blob [output_directory]", GetExecutableName());
                Console.WriteLine();
                Console.WriteLine("Options:");
                options.WriteOptionDescriptions(Console.Out);
                return;
            }

            string inputPath      = extra[0];
            string baseOutputPath = extra.Count > 1 ? extra[1] : Path.ChangeExtension(inputPath, null) + "_unpacked";

            using (var input = File.OpenRead(inputPath))
            {
                const Endian endian = Endian.Little;

                var count = input.ReadValueU32(endian);

                var ids   = new uint[count];
                var sizes = new uint[count];

                for (uint i = 0; i < count; i++)
                {
                    ids[i]   = input.ReadValueU32(endian);
                    sizes[i] = input.ReadValueU32(endian);
                }
                var end = (uint)input.Length;

                for (uint i = 0; i < count; i++)
                {
                    uint id   = ids[i];
                    uint size = sizes[i];

                    long currentPosition = input.Position;
                    var  extension       = FileDetection.Guess(input, (int)size, size);
                    input.Position = currentPosition;

                    var name = string.Format(
                        "{0}_{1:X4}_{2:X2}_{3:X2}",
                        i,
                        (id & 0x0000FFFF) >> 0,
                        (id & 0x00FF0000) >> 16,
                        (id & 0xFF000000) >> 24);

                    var outputPath = Path.Combine(baseOutputPath, Path.ChangeExtension(name, extension));

                    if (verbose == true)
                    {
                        Console.WriteLine(outputPath);
                    }

                    var outputParentPath = Path.GetDirectoryName(outputPath);
                    if (string.IsNullOrEmpty(outputParentPath) == false)
                    {
                        Directory.CreateDirectory(outputParentPath);
                    }

                    using (var output = File.Create(outputPath))
                    {
                        output.WriteFromStream(input, size);
                    }
                }
            }
        }