Last active
May 18, 2024 10:24
-
-
Save viruseg/7953345c633fcb0e7f8bff722a7e8775 to your computer and use it in GitHub Desktop.
Unity Package Extractor. Extract your .unitypackage with .net C#
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
Use the command line to extract the package. | |
program.exe packagePath outputPath extractMetaFiles | |
program.exe "d:\dir\package.unitypackage" "d:\outputDir" "true" |
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
using System.Diagnostics; | |
using System.IO.Compression; | |
using System.Text; | |
using ICSharpCode.SharpZipLib.Tar; //nuget package SharpZipLib | |
var packagePath = args.Length >= 1 ? args[0] : null; | |
var outputPath = args.Length >= 2 ? args[1] : null; | |
var extractMetaFiles = args.Length < 3 || Convert.ToBoolean(args[2]); | |
if (string.IsNullOrEmpty(packagePath)) | |
throw new ArgumentException("No .unitypackage path was given. \n\nUSAGE: program.exe \"XXX.unitypackage\" \"optional\\output\\path\" \"optional extractMetaFiles(true or false)\" \nprogram.exe \"d:\\dir\\package.unitypackage\" \"d:\\outputDir\" \"true\"\n"); | |
var startTime = Stopwatch.StartNew(); | |
UnityPackageExtractor.ExtractPackage(packagePath, outputPath, extractMetaFiles); | |
startTime.Stop(); | |
Console.WriteLine($"--- Finished in {startTime.Elapsed.TotalSeconds} seconds ---"); | |
internal static class UnityPackageExtractor | |
{ | |
public static void ExtractPackage(string packagePath, string? outputPath, bool extractMetaFiles) | |
{ | |
outputPath ??= Directory.GetCurrentDirectory(); | |
using var tmpDir = new TemporaryDirectory(); | |
ExtractTarGz(packagePath, tmpDir.Path); | |
Directory.EnumerateDirectories(tmpDir.Path) | |
.ToList() | |
.ForEach(dirEntry => | |
{ | |
var assetEntryDir = Path.Combine(tmpDir.Path, dirEntry); | |
if (!File.Exists(Path.Combine(assetEntryDir, "pathname")) || | |
!File.Exists(Path.Combine(assetEntryDir, "asset"))) | |
return; | |
using var reader = new StreamReader(Path.Combine(assetEntryDir, "pathname")); | |
var pathname = reader.ReadLine(); | |
if (string.IsNullOrEmpty(pathname)) return; | |
pathname = pathname.TrimEnd('\n'); | |
var name = Path.GetFileName(pathname); | |
var path = pathname[..^name.Length]; | |
name = string.Join("_", name.Split(Path.GetInvalidFileNameChars())); | |
path = string.Join("_", path.Split(Path.GetInvalidPathChars())); | |
pathname = path + name; | |
var assetOutPath = Path.Combine(outputPath, pathname); | |
if (!Path.GetFullPath(assetOutPath).StartsWith(Path.GetFullPath(outputPath))) | |
{ | |
Console.WriteLine($"WARNING: Skipping '{dirEntry}' as '{assetOutPath}' is outside of '{outputPath}'."); | |
return; | |
} | |
Console.WriteLine($"Extracting '{dirEntry}' as '{pathname}'"); | |
Directory.CreateDirectory(Path.GetDirectoryName(assetOutPath)); | |
File.Move(Path.Combine(assetEntryDir, "asset"), assetOutPath); | |
if (extractMetaFiles) File.Move(Path.Combine(assetEntryDir, "asset.meta"), assetOutPath + ".meta"); | |
}); | |
} | |
private static void ExtractTarGz(string filename, string outputDir) | |
{ | |
using var fs = new FileStream(filename, FileMode.Open, FileAccess.Read); | |
using var gzipStream = new GZipStream(fs, CompressionMode.Decompress); | |
using var tarArchive = TarArchive.CreateInputTarArchive(gzipStream, Encoding.UTF8); | |
tarArchive.ExtractContents(outputDir); | |
} | |
private class TemporaryDirectory : IDisposable | |
{ | |
public string Path { get; } | |
public TemporaryDirectory() | |
{ | |
Path = System.IO.Path.Combine(System.IO.Path.GetTempPath(), System.IO.Path.GetRandomFileName()); | |
Directory.CreateDirectory(Path); | |
} | |
public void Dispose() | |
{ | |
if (Directory.Exists(Path)) Directory.Delete(Path, true); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment