Exemplo n.º 1
0
        static void Main(string[] args)
        {
            Bio.Header("godotdec", "2.1.0", "2018-2020", "A simple unpacker for Godot Engine package files (.pck|.exe)",
                       "[<options>] <input_file> [<output_directory>]\n\nOptions:\n-c\t--convert\tConvert textures and audio files");

            if (Bio.HasCommandlineSwitchHelp(args))
            {
                return;
            }
            ParseCommandLine(args.ToList());

            var failed = 0;

            using (var inputStream = new BinaryReader(File.Open(inputFile, FileMode.Open))) {
                if (inputStream.ReadInt32() != MAGIC)
                {
                    inputStream.BaseStream.Seek(-4, SeekOrigin.End);

                    CheckMagic(inputStream.ReadInt32());

                    inputStream.BaseStream.Seek(-12, SeekOrigin.Current);
                    var offset = inputStream.ReadInt64();
                    inputStream.BaseStream.Seek(-offset - 8, SeekOrigin.Current);

                    CheckMagic(inputStream.ReadInt32());
                }

                Bio.Cout($"Godot Engine version: {inputStream.ReadInt32()}.{inputStream.ReadInt32()}.{inputStream.ReadInt32()}.{inputStream.ReadInt32()}");

                // Skip reserved bytes (16x Int32)
                inputStream.BaseStream.Seek(16 * 4, SeekOrigin.Current);

                var fileCount = inputStream.ReadInt32();
                Bio.Cout($"Found {fileCount} files in package");
                Bio.Cout("Reading file index");

                var fileIndex = new List <FileEntry>();
                for (var i = 0; i < fileCount; i++)
                {
                    var pathLength = inputStream.ReadInt32();
                    var path       = Encoding.UTF8.GetString(inputStream.ReadBytes(pathLength));
                    var fileEntry  = new FileEntry(path.ToString(), inputStream.ReadInt64(), inputStream.ReadInt64());
                    fileIndex.Add(fileEntry);
                    Bio.Debug(fileEntry);
                    inputStream.BaseStream.Seek(16, SeekOrigin.Current);
                    //break;
                }

                if (fileIndex.Count < 1)
                {
                    Bio.Error("No files were found inside the archive", Bio.EXITCODE.RUNTIME_ERROR);
                }
                fileIndex.Sort((a, b) => (int)(a.offset - b.offset));

                var fileIndexEnd = inputStream.BaseStream.Position;
                for (var i = 0; i < fileIndex.Count; i++)
                {
                    var fileEntry = fileIndex[i];
                    Bio.Progress(fileEntry.path, i + 1, fileIndex.Count);
                    //break;
                    if (fileEntry.offset < fileIndexEnd)
                    {
                        Bio.Warn("Invalid file offset: " + fileEntry.offset);
                        continue;
                    }

                    // TODO: Only PNG compression is supported
                    if (convertAssets)
                    {
                        // https://github.com/godotengine/godot/blob/master/editor/import/resource_importer_texture.cpp#L222
                        if (fileEntry.path.EndsWith(".stex") && fileEntry.path.Contains(".png"))
                        {
                            fileEntry.Resize(32);
                            fileEntry.ChangeExtension(".stex", ".png");
                            Bio.Debug(fileEntry);
                        }
                        // https://github.com/godotengine/godot/blob/master/core/io/resource_format_binary.cpp#L836
                        else if (fileEntry.path.EndsWith(".oggstr"))
                        {
                            fileEntry.Resize(279, 4);
                            fileEntry.ChangeExtension(".oggstr", ".ogg");
                        }
                        // https://github.com/godotengine/godot/blob/master/scene/resources/audio_stream_sample.cpp#L518
                        else if (fileEntry.path.EndsWith(".sample"))
                        {
                            // TODO
                            Bio.Warn("The file type '.sample' is currently not supported");
                        }
                    }

                    var destination = Path.Combine(outputDirectory, fileEntry.path);
                    destination = Bio.EnsureFileDoesNotExist(destination, "godotdec_overwrite");
                    inputStream.BaseStream.Seek(fileEntry.offset, SeekOrigin.Begin);
                    if (destination == null)
                    {
                        continue;
                    }

                    try {
                        var fileMode = FileMode.CreateNew;
                        Directory.CreateDirectory(Path.GetDirectoryName(destination));

                        if (File.Exists(destination))
                        {
                            fileMode = FileMode.Create;
                        }
                        using (var outputStream = new FileStream(destination, fileMode)) {
                            Bio.CopyStream(inputStream.BaseStream, outputStream, (int)fileEntry.size, false);
                        }
                    }
                    catch (Exception e) {
                        Bio.Error(e);
                        failed++;
                    }
                }
            }

            Bio.Cout();
            Bio.Cout(failed < 1? "All OK": failed + " files failed to extract");
            Bio.Pause();
        }