mirror of
https://github.com/k1tbyte/Wand-Enhancer.git
synced 2026-08-29 00:01:09 +00:00
160 lines
7.5 KiB
C#
160 lines
7.5 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Runtime.InteropServices;
|
||
using AsarSharp.AsarFileSystem;
|
||
using AsarSharp.Utils;
|
||
|
||
namespace AsarSharp
|
||
{
|
||
public class AsarExtractor
|
||
{
|
||
public static void ExtractAll(string archivePath, string dest)
|
||
{
|
||
var filesystem = Disk.ReadFilesystemSync(archivePath);
|
||
var filenames = filesystem.ListFiles();
|
||
|
||
// under windows just extract links as regular files
|
||
bool followLinks = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
||
|
||
// create destination directory
|
||
Directory.CreateDirectory(dest);
|
||
|
||
var extractionErrors = new List<Exception>();
|
||
foreach (var fullPath in filenames)
|
||
{
|
||
try
|
||
{
|
||
// Remove leading slash
|
||
var filename = fullPath.Substring(1);
|
||
var destFilename = Path.Combine(dest, filename);
|
||
var file = filesystem.GetFile(filename, followLinks);
|
||
|
||
// Check that the file is not written outside the specified destination folder
|
||
string relativePath = Extensions.GetRelativePath(dest, destFilename);
|
||
if (relativePath.StartsWith(".."))
|
||
{
|
||
throw new InvalidOperationException($"{fullPath}: file \"{destFilename}\" writes out of the package");
|
||
}
|
||
|
||
if (file.IsDirectory)
|
||
{
|
||
// it's a directory, create it and continue with the next entry
|
||
Directory.CreateDirectory(destFilename);
|
||
}
|
||
// TODO (LINK NOT SUPPORTED)
|
||
else if (file.IsLink)
|
||
{
|
||
// it's a symlink, create a symlink
|
||
var linkSrcPath = Extensions.GetDirectoryName(Path.Combine(dest, file.Link));
|
||
var linkDestPath = Extensions.GetDirectoryName(destFilename);
|
||
var relativeLinkPath = Extensions.GetRelativePath(linkDestPath, linkSrcPath);
|
||
|
||
// try to delete output file, because we can't overwrite a link
|
||
try
|
||
{
|
||
File.Delete(destFilename);
|
||
}
|
||
catch {
|
||
// Ignore errors during file link deletion
|
||
}
|
||
|
||
var linkTo = Path.Combine(relativeLinkPath, Path.GetFileName(file.Link));
|
||
|
||
if (Extensions.GetRelativePath(dest, linkSrcPath).StartsWith(".."))
|
||
{
|
||
throw new InvalidOperationException(
|
||
$"{fullPath}: file \"{file.Link}\" links out of the package to \"{linkSrcPath}\"");
|
||
}
|
||
|
||
// On Windows, creating symlinks requires additional permissions or enabling Developer Mode,
|
||
// so just copy the contents of the file
|
||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||
{
|
||
var targetPath = Path.Combine(linkSrcPath, Path.GetFileName(file.Link));
|
||
if (Directory.Exists(targetPath))
|
||
{
|
||
Directory.CreateDirectory(destFilename);
|
||
Extensions.CopyDirectory(targetPath, destFilename);
|
||
}
|
||
else if (File.Exists(targetPath))
|
||
{
|
||
Directory.CreateDirectory(Extensions.GetDirectoryName(destFilename));
|
||
File.Copy(targetPath, destFilename, true);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// On Unix systems we use symlinks
|
||
Directory.CreateDirectory(Extensions.GetDirectoryName(destFilename));
|
||
Extensions.CreateSymbolicLink(linkTo, destFilename);
|
||
}
|
||
}
|
||
else if (file.IsFile)
|
||
{
|
||
// it's a file, try to extract it
|
||
try
|
||
{
|
||
// Unpacked entries already live on disk next to the archive in
|
||
// "<archive>.unpacked". When the caller extracts INTO that same
|
||
// directory (e.g. re-extracting in place to repack later) reading +
|
||
// writing the file is a self-copy that needlessly fails when the
|
||
// file is locked by another process (TrainerLib_x64.dll) or has been
|
||
// removed from disk by an installer (auxiliary/GameLauncher.exe).
|
||
if (file.Unpacked == true)
|
||
{
|
||
string unpackedSourcePath = Path.GetFullPath(
|
||
Path.Combine($"{filesystem.GetRootPath()}.unpacked", filename));
|
||
string unpackedDestPath = Path.GetFullPath(destFilename);
|
||
|
||
if (string.Equals(unpackedSourcePath, unpackedDestPath, StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
// Nothing to do – the file is already at the destination.
|
||
continue;
|
||
}
|
||
|
||
if (!File.Exists(unpackedSourcePath))
|
||
{
|
||
// The header references an unpacked file that no longer
|
||
// exists on disk; skip it instead of aborting the whole
|
||
// extraction so the rest of the asar can still be repacked.
|
||
continue;
|
||
}
|
||
|
||
Directory.CreateDirectory(Extensions.GetDirectoryName(destFilename));
|
||
File.Copy(unpackedSourcePath, destFilename, true);
|
||
}
|
||
else
|
||
{
|
||
byte[] content = Disk.ReadFileSync(filesystem, filename, file);
|
||
File.WriteAllBytes(destFilename, content);
|
||
}
|
||
|
||
if (file.Executable == true && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||
{
|
||
Extensions.SetUnixFilePermission(destFilename, "755");
|
||
}
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
extractionErrors.Add(e);
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
extractionErrors.Add(ex);
|
||
}
|
||
}
|
||
|
||
if (extractionErrors.Count > 0)
|
||
{
|
||
throw new AggregateException(
|
||
"Unable to extract some files:\n\n" +
|
||
string.Join("\n\n", extractionErrors.Select(e => e.ToString())),
|
||
extractionErrors);
|
||
}
|
||
}
|
||
}
|
||
} |