-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This is a complete rewrite in C#. Godotdec now supports bigger archives and also fixes several extraction failures. Additionally, a new command line parameter allows to automatically convert certain engine-specific file types to standard formats, making the output more useful.
- Loading branch information
Showing
14 changed files
with
435 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,4 @@ | ||
/bin | ||
/godotdec/bin | ||
/legacy/bin | ||
/godotdec/obj | ||
/.vs |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
|
||
Microsoft Visual Studio Solution File, Format Version 12.00 | ||
# Visual Studio Version 16 | ||
VisualStudioVersion = 16.0.29123.88 | ||
MinimumVisualStudioVersion = 10.0.40219.1 | ||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "godotdec", "godotdec\godotdec.csproj", "{3C36505F-9F80-4B13-B212-446B463689A7}" | ||
EndProject | ||
Global | ||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
Debug|Any CPU = Debug|Any CPU | ||
Release|Any CPU = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
{3C36505F-9F80-4B13-B212-446B463689A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{3C36505F-9F80-4B13-B212-446B463689A7}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{3C36505F-9F80-4B13-B212-446B463689A7}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{3C36505F-9F80-4B13-B212-446B463689A7}.Release|Any CPU.Build.0 = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(SolutionProperties) = preSolution | ||
HideSolutionNode = FALSE | ||
EndGlobalSection | ||
GlobalSection(ExtensibilityGlobals) = postSolution | ||
SolutionGuid = {E6A83782-A391-42A0-85A4-5287504E8E22} | ||
EndGlobalSection | ||
EndGlobal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> | ||
<s:Int64 x:Key="/Default/CodeStyle/Naming/CSharpAutoNaming/AutoNamingCompletedVersion/@EntryValue">2</s:Int64></wpf:ResourceDictionary> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<configuration> | ||
<startup> | ||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/> | ||
</startup> | ||
</configuration> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
|
||
namespace cicdec { | ||
public static class Bio { | ||
public const string SEPERATOR = "\n---------------------------------------------------------------------"; | ||
private static readonly Dictionary<string, PROMPT_SETTING> promptSettings = new Dictionary<string, PROMPT_SETTING>(); | ||
|
||
public static void CopyStream(Stream input, Stream output, int bytes = -1, bool keepPosition = true, int bufferSize = 1024) { | ||
var buffer = new byte[bufferSize]; | ||
long initialPosition = 0; | ||
if (keepPosition) initialPosition = input.Position; | ||
int read; | ||
if (bytes < 1) bytes = (int) (input.Length - input.Position); | ||
|
||
while (bytes > 0 && (read = input.Read(buffer, 0, Math.Min(bufferSize, bytes))) > 0) { | ||
output.Write(buffer, 0, read); | ||
bytes -= read; | ||
} | ||
|
||
if (keepPosition) input.Seek(initialPosition, SeekOrigin.Begin); | ||
} | ||
|
||
public static string FileReplaceInvalidChars(string filename, string by = "_") { | ||
return string.Join(by, filename.Split(Path.GetInvalidPathChars())); | ||
} | ||
|
||
public static void Header(string name, string version, string year, string description, string usage = "") { | ||
var header = string.Format("{0} by Bioruebe (https://bioruebe.com), {1}, Version {2}, Released under a BSD 3-Clause style license\n\n{3}{4}\n{5}", name, year, version, description, (usage == null ? "" : "\n\nUsage: " + GetProgramName() + " " + usage), SEPERATOR); | ||
|
||
Console.WriteLine(header); | ||
} | ||
|
||
public static void Seperator() { | ||
Console.WriteLine(SEPERATOR + "\n"); | ||
} | ||
|
||
public static string GetProgramName() { | ||
return System.Diagnostics.Process.GetCurrentProcess().ProcessName; | ||
} | ||
|
||
public static bool Prompt(string msg, string id = "", string choices = | ||
"[Y]es | [N]o | [A]lways | n[E]ver", string chars = "ynae") { | ||
// Check setting from previous function calls | ||
promptSettings.TryGetValue(id, out var setting); | ||
if (setting == PROMPT_SETTING.ALWAYS) return true; | ||
if (setting == PROMPT_SETTING.NEVER) return false; | ||
|
||
var aChars = chars.ToCharArray(); | ||
int input; | ||
|
||
while (true) { | ||
Console.WriteLine(msg + $" {choices}"); | ||
input = Console.ReadKey().KeyChar; | ||
Console.WriteLine(); | ||
|
||
if (input == aChars[0]) return true; | ||
if (input == aChars[1]) return false; | ||
if (input == aChars[2]) { | ||
promptSettings[id] = PROMPT_SETTING.ALWAYS; | ||
return true; | ||
} | ||
|
||
if (input == aChars[3]) { | ||
promptSettings[id] = PROMPT_SETTING.NEVER; | ||
return false; | ||
} | ||
} | ||
|
||
} | ||
|
||
public static void Cout(string msg, LOG_SEVERITY logSeverity = LOG_SEVERITY.MESSAGE) { | ||
#if !DEBUG | ||
if (logSeverity == LOG_SEVERITY.DEBUG) return; | ||
#endif | ||
if (msg.StartsWith("\n")) { | ||
Console.WriteLine(); | ||
msg = msg.Substring(1); | ||
} | ||
|
||
if (logSeverity != LOG_SEVERITY.MESSAGE) msg = string.Format("[{0}] {1}", logSeverity, msg); | ||
|
||
switch (logSeverity) { | ||
case LOG_SEVERITY.ERROR: | ||
case LOG_SEVERITY.CRITICAL: | ||
Console.Error.WriteLine(); | ||
Console.WriteLine(msg); | ||
break; | ||
default: | ||
Console.WriteLine(msg); | ||
break; | ||
} | ||
} | ||
|
||
public static void Cout(object msg, LOG_SEVERITY logSeverity = LOG_SEVERITY.MESSAGE) { | ||
Cout(msg.ToString(), logSeverity); | ||
} | ||
|
||
public static void Debug(object msg) { | ||
Cout(msg, LOG_SEVERITY.DEBUG); | ||
} | ||
|
||
public static void Warn(object msg) { | ||
Cout(msg, LOG_SEVERITY.WARNING); | ||
} | ||
|
||
public static void Error(object msg, int exitCode = -1) { | ||
Cout(msg, LOG_SEVERITY.ERROR); | ||
#if DEBUG | ||
Console.ReadKey(false); | ||
#endif | ||
if (exitCode > -1) Environment.Exit(exitCode); | ||
} | ||
|
||
public static void Pause() { | ||
#if DEBUG | ||
Console.ReadKey(); | ||
#endif | ||
} | ||
|
||
public enum LOG_SEVERITY { | ||
DEBUG, | ||
INFO, | ||
WARNING, | ||
ERROR, | ||
CRITICAL, | ||
MESSAGE, | ||
UNITTEST | ||
} | ||
|
||
public enum PROMPT_SETTING { | ||
NONE, | ||
ALWAYS, | ||
NEVER | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using cicdec; | ||
|
||
namespace godotdec { | ||
class Program { | ||
static void Main(string[] aArgs) { | ||
Bio.Header("godotdec", "2.0.0", "2018-2019", "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"); | ||
|
||
var args = aArgs.ToList(); | ||
|
||
if (args.Contains("-h") || args.Contains("--help") || args.Contains("/?") || args.Contains("-?") || | ||
args.Contains("/h")) return; | ||
|
||
var convert = args.Remove("--convert") || args.Remove("-c"); | ||
if (args.Count < 1) Bio.Error("Please specify the path to a Godot .pck or .exe file.", 1); | ||
|
||
var inputFile = args[0]; | ||
if (!File.Exists(inputFile)) Bio.Error("The input file " + inputFile + " does not exist.", 1); | ||
var outdir = args.Count > 1? args[1]: Path.Combine(Path.GetDirectoryName(inputFile), Path.GetFileNameWithoutExtension(inputFile)); | ||
Bio.Debug("Input file: " + inputFile); | ||
Bio.Debug("Output directory: " + outdir); | ||
|
||
var failed = 0; | ||
using (var inputStream = new BinaryReader(File.Open(inputFile, FileMode.Open))) { | ||
if (inputStream.ReadInt32() != 0x43504447) { | ||
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", 2); | ||
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.Cout($"{i+1}/{fileIndex.Count}\t{fileEntry.path}"); | ||
//break; | ||
if (fileEntry.offset < fileIndexEnd) { | ||
Bio.Warn("Invalid file offset: " + fileEntry.offset); | ||
continue; | ||
} | ||
|
||
// TODO: Only PNG compression is supported | ||
if (convert) { | ||
// 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 | ||
} | ||
} | ||
|
||
var destination = Path.Combine(outdir, Bio.FileReplaceInvalidChars(fileEntry.path)); | ||
inputStream.BaseStream.Seek(fileEntry.offset, SeekOrigin.Begin); | ||
|
||
try { | ||
var fileMode = FileMode.CreateNew; | ||
Directory.CreateDirectory(Path.GetDirectoryName(destination)); | ||
if (File.Exists(destination)) { | ||
if (!Bio.Prompt($"The file {fileEntry.path} already exists. Overwrite?", "godotdec_overwrite")) continue; | ||
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(failed < 1? "All OK": failed + " files failed to extract"); | ||
Bio.Pause(); | ||
} | ||
|
||
static void CheckMagic(int magic) { | ||
if (magic == 0x43504447) return; | ||
Bio.Error("The input file is not a valid Godot package file.", 2); | ||
} | ||
} | ||
} | ||
|
||
class FileEntry { | ||
public string path; | ||
public long offset; | ||
public long size; | ||
//public var md5:String; | ||
|
||
public FileEntry (string path, long offset, long size) { | ||
this.path = path.Substring(6).TrimEnd('\0'); | ||
this.offset = offset; | ||
this.size = size; | ||
} | ||
|
||
public void Resize(int by, int stripAtEnd = 0) { | ||
offset += by; | ||
size -= by + stripAtEnd; | ||
} | ||
|
||
public void ChangeExtension(string from, string to) { | ||
path = path.Replace(from, to); | ||
} | ||
|
||
public override string ToString() { | ||
return $"{offset:000000} {path}, {size}"; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
using System.Reflection; | ||
using System.Runtime.CompilerServices; | ||
using System.Runtime.InteropServices; | ||
|
||
// Allgemeine Informationen über eine Assembly werden über die folgenden | ||
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, | ||
// die einer Assembly zugeordnet sind. | ||
[assembly: AssemblyTitle("godotdec")] | ||
[assembly: AssemblyDescription("")] | ||
[assembly: AssemblyConfiguration("")] | ||
[assembly: AssemblyCompany("")] | ||
[assembly: AssemblyProduct("godotdec")] | ||
[assembly: AssemblyCopyright("Copyright © 2019")] | ||
[assembly: AssemblyTrademark("")] | ||
[assembly: AssemblyCulture("")] | ||
|
||
// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly | ||
// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von | ||
// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen. | ||
[assembly: ComVisible(false)] | ||
|
||
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird | ||
[assembly: Guid("3c36505f-9f80-4b13-b212-446b463689a7")] | ||
|
||
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: | ||
// | ||
// Hauptversion | ||
// Nebenversion | ||
// Buildnummer | ||
// Revision | ||
// | ||
// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden, | ||
// indem Sie "*" wie unten gezeigt eingeben: | ||
// [assembly: AssemblyVersion("1.0.*")] | ||
[assembly: AssemblyVersion("1.0.0.0")] | ||
[assembly: AssemblyFileVersion("1.0.0.0")] |
Oops, something went wrong.