Exemplo n.º 1
0
        static void Main(string[] args)
        {
            Bio.Header("godotdec", VERSION, "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.Skip(-12);
                    var offset = inputStream.ReadInt64();
                    inputStream.BaseStream.Skip(-offset - 8);

                    CheckMagic(inputStream.ReadInt32());
                }

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

                // Skip reserved bytes (16x Int32)
                inputStream.BaseStream.Skip(16 * 4);

                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.Skip(16);
                    //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");
                        }
                    }

                    inputStream.BaseStream.Position = fileEntry.offset;
                    var destination = Path.Combine(outputDirectory, fileEntry.path);

                    try {
                        Action <Stream, Stream> copyFunction = (input, output) => input.Copy(output, (int)fileEntry.size);
                        inputStream.BaseStream.WriteToFile(destination, PROMPT_ID, copyFunction);
                    }
                    catch (Exception e) {
                        Bio.Error(e);
                        failed++;
                    }
                }
            }

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