Handle failed file loading

* Closes #1208
* Related: #1403
This commit is contained in:
ds5678 2024-08-11 11:17:30 -07:00
parent 570fc318e7
commit 8429911c25
13 changed files with 177 additions and 30 deletions

View File

@ -87,6 +87,7 @@
"export_preparing": "Preparing for Export...\nThis might take a minute.",
"export_primary_content": "Export Primary Content",
"export_unity_project": "Export Unity Project",
"failed_files": "Failed Files",
"format": "Format",
"frequency": "Frequency",
"game_object": "GameObject",

View File

@ -1,5 +1,6 @@
using AssetRipper.Assets.Collections;
using AssetRipper.Assets.IO;
using AssetRipper.IO.Files;
using AssetRipper.IO.Files.ResourceFiles;
using AssetRipper.IO.Files.SerializedFiles;
using AssetRipper.IO.Files.SerializedFiles.Parser;
@ -16,21 +17,31 @@ public abstract class Bundle : IDisposable
/// The parent <see cref="Bundle"/> of this Bundle.
/// </summary>
public Bundle? Parent { get; private set; }
/// <summary>
/// The list of <see cref="ResourceFile"/>s in this Bundle.
/// </summary>
public IReadOnlyList<ResourceFile> Resources => resources;
private readonly List<ResourceFile> resources = new();
private readonly List<ResourceFile> resources = [];
/// <summary>
/// The list of <see cref="AssetCollection"/>s in this Bundle.
/// </summary>
public IReadOnlyList<AssetCollection> Collections => collections;
private readonly List<AssetCollection> collections = new();
private readonly List<AssetCollection> collections = [];
/// <summary>
/// The list of child <see cref="Bundle"/>s in this Bundle.
/// </summary>
public IReadOnlyList<Bundle> Bundles => bundles;
private readonly List<Bundle> bundles = new();
private readonly List<Bundle> bundles = [];
/// <summary>
/// The list of <see cref="FailedFile"/>s in this Bundle.
/// </summary>
public IReadOnlyList<FailedFile> FailedFiles => failedFiles;
private readonly List<FailedFile> failedFiles = [];
private bool disposedValue;
/// <summary>
@ -281,6 +292,11 @@ public abstract class Bundle : IDisposable
}
}
public void AddFailed(FailedFile file)
{
failedFiles.Add(file);
}
/// <summary>
/// Indicates if the specified <see cref="AssetCollection"/> is compatible with this Bundle.
/// </summary>

View File

@ -49,6 +49,9 @@ partial class GameBundle
case ResourceFile resourceFile:
AddResource(resourceFile);
break;
case FailedFile failedFile:
AddFailed(failedFile);
break;
}
}
}
@ -67,13 +70,26 @@ partial class GameBundle
HashSet<string> serializedFileNames = new();//Includes missing dependencies
foreach (string path in paths)
{
FileBase? file = SchemeReader.LoadFile(path);
file?.ReadContentsRecursively();
FileBase? file;
try
{
file = SchemeReader.LoadFile(path);
file.ReadContentsRecursively();
}
catch (Exception ex)
{
file = new FailedFile()
{
Name = Path.GetFileName(path),
FilePath = path,
StackTrace = ex.ToString(),
};
}
while (file is CompressedFile compressedFile)
{
file = compressedFile.UncompressedFile;
}
if (file is ResourceFile resourceFile)
if (file is ResourceFile or FailedFile)
{
files.Add(file);
}

View File

@ -32,6 +32,10 @@ public sealed class SerializedBundle : Bundle
SerializedBundle childBundle = FromFileContainer(childContainer, factory, defaultVersion);
bundle.AddBundle(childBundle);
}
foreach (FailedFile failedFile in container.FailedFiles)
{
bundle.AddFailed(failedFile);
}
return bundle;
}

View File

@ -449,6 +449,11 @@ partial class Localization
/// </summary>
public static string ExportUnityProject => Get("export_unity_project");
/// <summary>
/// Failed Files
/// </summary>
public static string FailedFiles => Get("failed_files");
/// <summary>
/// Format
/// </summary>

View File

@ -1,6 +1,7 @@
using AssetRipper.Assets.Bundles;
using AssetRipper.Assets.Collections;
using AssetRipper.GUI.Web.Paths;
using AssetRipper.IO.Files;
namespace AssetRipper.GUI.Web.Pages.Bundles;
@ -69,5 +70,18 @@ public sealed class ViewPage : DefaultPage
}
}
}
if (Bundle.FailedFiles.Count > 0)
{
new H2(writer).Close(Localization.FailedFiles);
using (new Ul(writer).End())
{
foreach (FailedFile failedFile in Bundle.FailedFiles)
{
// Todo: page for failed files
new Li(writer).Close(failedFile.Name);
}
}
}
}
}

View File

@ -28,10 +28,7 @@ namespace AssetRipper.IO.Files.BundleFiles.FileStream
public SmartStream ReadEntry(FileStreamNode entry)
{
if (m_isDisposed)
{
throw new ObjectDisposedException(nameof(BundleFileBlockReader));
}
ObjectDisposedException.ThrowIf(m_isDisposed, typeof(BundleFileBlockReader));
// find block offsets
int blockIndex;

View File

@ -142,8 +142,20 @@ namespace AssetRipper.IO.Files.BundleFiles.FileStream
using BundleFileBlockReader blockReader = new BundleFileBlockReader(stream, BlocksInfo);
foreach (FileStreamNode entry in DirectoryInfo.Nodes)
{
SmartStream entryStream = blockReader.ReadEntry(entry);
AddResourceFile(new ResourceFile(entryStream, FilePath, entry.Path));
try
{
SmartStream entryStream = blockReader.ReadEntry(entry);
AddResourceFile(new ResourceFile(entryStream, FilePath, entry.Path));
}
catch (Exception ex)
{
AddFailedFile(new FailedFile()
{
Name = entry.Path,
FilePath = FilePath,
StackTrace = ex.ToString(),
});
}
}
}

View File

@ -1,7 +1,6 @@
using AssetRipper.IO.Endian;
using AssetRipper.IO.Files.BundleFiles.RawWeb.Raw;
using AssetRipper.IO.Files.BundleFiles.RawWeb.Web;
using AssetRipper.IO.Files.Extensions;
using AssetRipper.IO.Files.ResourceFiles;
using AssetRipper.IO.Files.Streams.Smart;

View File

@ -10,8 +10,20 @@ namespace AssetRipper.IO.Files.CompressedFiles.Brotli
public override void Read(SmartStream stream)
{
byte[] buffer = ReadBrotli(stream);
UncompressedFile = new ResourceFile(buffer, FilePath, Name);
try
{
byte[] buffer = ReadBrotli(stream);
UncompressedFile = new ResourceFile(buffer, FilePath, Name);
}
catch (Exception ex)
{
UncompressedFile = new FailedFile()
{
Name = Name,
FilePath = FilePath,
StackTrace = ex.ToString(),
};
}
}
internal static bool IsBrotliFile(Stream stream)

View File

@ -11,13 +11,25 @@ namespace AssetRipper.IO.Files.CompressedFiles.GZip
public override void Read(SmartStream stream)
{
using SmartStream memoryStream = SmartStream.CreateMemory();
using (GZipStream gzipStream = new GZipStream(stream, CompressionMode.Decompress, true))
try
{
gzipStream.CopyTo(memoryStream);
using SmartStream memoryStream = SmartStream.CreateMemory();
using (GZipStream gzipStream = new GZipStream(stream, CompressionMode.Decompress, true))
{
gzipStream.CopyTo(memoryStream);
}
memoryStream.Position = 0;
UncompressedFile = new ResourceFile(memoryStream, FilePath, Name);
}
catch (Exception ex)
{
UncompressedFile = new FailedFile()
{
Name = Name,
FilePath = FilePath,
StackTrace = ex.ToString(),
};
}
memoryStream.Position = 0;
UncompressedFile = new ResourceFile(memoryStream, FilePath, Name);
}
public override void Write(Stream stream)

View File

@ -0,0 +1,17 @@
using AssetRipper.IO.Files.Streams.Smart;
namespace AssetRipper.IO.Files;
public class FailedFile : FileBase
{
public string StackTrace { get; set; } = "";
public override void Read(SmartStream stream)
{
}
public override void Write(Stream stream)
{
throw new NotSupportedException();
}
}

View File

@ -33,6 +33,9 @@ namespace AssetRipper.IO.Files
case FileContainer fileList:
AddFileContainer(fileList);
return;
case FailedFile failedFile:
AddFailedFile(failedFile);
return;
default:
throw new NotSupportedException(file.GetType().ToString());
}
@ -40,22 +43,55 @@ namespace AssetRipper.IO.Files
public void AddSerializedFile(SerializedFile file)
{
m_serializedFiles.Add(file);
if (m_serializedFiles is null)
{
m_serializedFiles = [file];
}
else
{
m_serializedFiles.Add(file);
}
OnSerializedFileAdded(file);
}
public void AddFileContainer(FileContainer container)
{
m_fileLists.Add(container);
if (m_fileLists is null)
{
m_fileLists = [container];
}
else
{
m_fileLists.Add(container);
}
OnFileContainerAdded(container);
}
public void AddResourceFile(ResourceFile resource)
{
m_resourceFiles.Add(resource);
if (m_resourceFiles is null)
{
m_resourceFiles = [resource];
}
else
{
m_resourceFiles.Add(resource);
}
OnResourceFileAdded(resource);
}
public void AddFailedFile(FailedFile file)
{
if (m_failedFiles is null)
{
m_failedFiles = [file];
}
else
{
m_failedFiles.Add(file);
}
}
protected virtual void OnSerializedFileAdded(SerializedFile file) { }
protected virtual void OnFileContainerAdded(FileContainer container) { }
@ -64,7 +100,7 @@ namespace AssetRipper.IO.Files
public override void ReadContents()
{
if (m_resourceFiles.Count > 0)
if (m_resourceFiles is { Count: > 0 })
{
ResourceFile[] resourceFiles = m_resourceFiles.ToArray();
m_resourceFiles.Clear();
@ -84,9 +120,10 @@ namespace AssetRipper.IO.Files
}
}
public IReadOnlyList<SerializedFile> SerializedFiles => m_serializedFiles;
public IReadOnlyList<FileContainer> FileLists => m_fileLists;
public IReadOnlyList<ResourceFile> ResourceFiles => m_resourceFiles;
public IReadOnlyList<SerializedFile> SerializedFiles => m_serializedFiles ?? [];
public IReadOnlyList<FileContainer> FileLists => m_fileLists ?? [];
public IReadOnlyList<ResourceFile> ResourceFiles => m_resourceFiles ?? [];
public IReadOnlyList<FailedFile> FailedFiles => m_failedFiles ?? [];
public IEnumerable<FileBase> AllFiles
{
@ -104,11 +141,16 @@ namespace AssetRipper.IO.Files
{
yield return container;
}
foreach (FailedFile file in FailedFiles)
{
yield return file;
}
}
}
private readonly List<SerializedFile> m_serializedFiles = new List<SerializedFile>(0);
private readonly List<FileContainer> m_fileLists = new List<FileContainer>(0);
private readonly List<ResourceFile> m_resourceFiles = new List<ResourceFile>(0);
private List<SerializedFile>? m_serializedFiles;
private List<FileContainer>? m_fileLists;
private List<ResourceFile>? m_resourceFiles;
private List<FailedFile>? m_failedFiles;
}
}