diff --git a/Localizations/en_US.json b/Localizations/en_US.json
index 3e9be1a33..0fe4325e0 100644
--- a/Localizations/en_US.json
+++ b/Localizations/en_US.json
@@ -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",
diff --git a/Source/AssetRipper.Assets/Bundles/Bundle.cs b/Source/AssetRipper.Assets/Bundles/Bundle.cs
index 232161105..fd1927a5f 100644
--- a/Source/AssetRipper.Assets/Bundles/Bundle.cs
+++ b/Source/AssetRipper.Assets/Bundles/Bundle.cs
@@ -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 of this Bundle.
///
public Bundle? Parent { get; private set; }
+
///
/// The list of s in this Bundle.
///
public IReadOnlyList Resources => resources;
- private readonly List resources = new();
+ private readonly List resources = [];
+
///
/// The list of s in this Bundle.
///
public IReadOnlyList Collections => collections;
- private readonly List collections = new();
+ private readonly List collections = [];
+
///
/// The list of child s in this Bundle.
///
public IReadOnlyList Bundles => bundles;
- private readonly List bundles = new();
+ private readonly List bundles = [];
+
+ ///
+ /// The list of s in this Bundle.
+ ///
+ public IReadOnlyList FailedFiles => failedFiles;
+ private readonly List failedFiles = [];
+
private bool disposedValue;
///
@@ -281,6 +292,11 @@ public abstract class Bundle : IDisposable
}
}
+ public void AddFailed(FailedFile file)
+ {
+ failedFiles.Add(file);
+ }
+
///
/// Indicates if the specified is compatible with this Bundle.
///
diff --git a/Source/AssetRipper.Assets/Bundles/GameBundle.FromPaths.cs b/Source/AssetRipper.Assets/Bundles/GameBundle.FromPaths.cs
index 9db1dc949..2c1eef845 100644
--- a/Source/AssetRipper.Assets/Bundles/GameBundle.FromPaths.cs
+++ b/Source/AssetRipper.Assets/Bundles/GameBundle.FromPaths.cs
@@ -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 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);
}
diff --git a/Source/AssetRipper.Assets/Bundles/SerializedBundle.cs b/Source/AssetRipper.Assets/Bundles/SerializedBundle.cs
index 837ed503f..a8683a2e6 100644
--- a/Source/AssetRipper.Assets/Bundles/SerializedBundle.cs
+++ b/Source/AssetRipper.Assets/Bundles/SerializedBundle.cs
@@ -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;
}
diff --git a/Source/AssetRipper.GUI.Localizations/Localization.g.cs b/Source/AssetRipper.GUI.Localizations/Localization.g.cs
index 04e6ee9c9..4e2e8270d 100644
--- a/Source/AssetRipper.GUI.Localizations/Localization.g.cs
+++ b/Source/AssetRipper.GUI.Localizations/Localization.g.cs
@@ -449,6 +449,11 @@ partial class Localization
///
public static string ExportUnityProject => Get("export_unity_project");
+ ///
+ /// Failed Files
+ ///
+ public static string FailedFiles => Get("failed_files");
+
///
/// Format
///
diff --git a/Source/AssetRipper.GUI.Web/Pages/Bundles/ViewPage.cs b/Source/AssetRipper.GUI.Web/Pages/Bundles/ViewPage.cs
index 833e1bc56..932931b56 100644
--- a/Source/AssetRipper.GUI.Web/Pages/Bundles/ViewPage.cs
+++ b/Source/AssetRipper.GUI.Web/Pages/Bundles/ViewPage.cs
@@ -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);
+ }
+ }
+ }
}
}
diff --git a/Source/AssetRipper.IO.Files/BundleFiles/FileStream/BundleFileBlockReader.cs b/Source/AssetRipper.IO.Files/BundleFiles/FileStream/BundleFileBlockReader.cs
index 3a66d5d26..f4071f251 100644
--- a/Source/AssetRipper.IO.Files/BundleFiles/FileStream/BundleFileBlockReader.cs
+++ b/Source/AssetRipper.IO.Files/BundleFiles/FileStream/BundleFileBlockReader.cs
@@ -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;
diff --git a/Source/AssetRipper.IO.Files/BundleFiles/FileStream/FileStreamBundleFile.cs b/Source/AssetRipper.IO.Files/BundleFiles/FileStream/FileStreamBundleFile.cs
index ac0f4b425..15ccfc5e7 100644
--- a/Source/AssetRipper.IO.Files/BundleFiles/FileStream/FileStreamBundleFile.cs
+++ b/Source/AssetRipper.IO.Files/BundleFiles/FileStream/FileStreamBundleFile.cs
@@ -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(),
+ });
+ }
}
}
diff --git a/Source/AssetRipper.IO.Files/BundleFiles/RawWeb/RawWebBundleFile.cs b/Source/AssetRipper.IO.Files/BundleFiles/RawWeb/RawWebBundleFile.cs
index 4818cfb40..89e7ff4a8 100644
--- a/Source/AssetRipper.IO.Files/BundleFiles/RawWeb/RawWebBundleFile.cs
+++ b/Source/AssetRipper.IO.Files/BundleFiles/RawWeb/RawWebBundleFile.cs
@@ -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;
diff --git a/Source/AssetRipper.IO.Files/CompressedFiles/Brotli/BrotliFile.cs b/Source/AssetRipper.IO.Files/CompressedFiles/Brotli/BrotliFile.cs
index 9c13e9218..4cbbd25da 100644
--- a/Source/AssetRipper.IO.Files/CompressedFiles/Brotli/BrotliFile.cs
+++ b/Source/AssetRipper.IO.Files/CompressedFiles/Brotli/BrotliFile.cs
@@ -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)
diff --git a/Source/AssetRipper.IO.Files/CompressedFiles/GZip/GZipFile.cs b/Source/AssetRipper.IO.Files/CompressedFiles/GZip/GZipFile.cs
index 6851ab402..da7aa214f 100644
--- a/Source/AssetRipper.IO.Files/CompressedFiles/GZip/GZipFile.cs
+++ b/Source/AssetRipper.IO.Files/CompressedFiles/GZip/GZipFile.cs
@@ -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)
diff --git a/Source/AssetRipper.IO.Files/FailedFile.cs b/Source/AssetRipper.IO.Files/FailedFile.cs
new file mode 100644
index 000000000..6f423130b
--- /dev/null
+++ b/Source/AssetRipper.IO.Files/FailedFile.cs
@@ -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();
+ }
+}
diff --git a/Source/AssetRipper.IO.Files/FileContainer.cs b/Source/AssetRipper.IO.Files/FileContainer.cs
index e4c288957..c7a12ee80 100644
--- a/Source/AssetRipper.IO.Files/FileContainer.cs
+++ b/Source/AssetRipper.IO.Files/FileContainer.cs
@@ -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 SerializedFiles => m_serializedFiles;
- public IReadOnlyList FileLists => m_fileLists;
- public IReadOnlyList ResourceFiles => m_resourceFiles;
+ public IReadOnlyList SerializedFiles => m_serializedFiles ?? [];
+ public IReadOnlyList FileLists => m_fileLists ?? [];
+ public IReadOnlyList ResourceFiles => m_resourceFiles ?? [];
+ public IReadOnlyList FailedFiles => m_failedFiles ?? [];
public IEnumerable AllFiles
{
@@ -104,11 +141,16 @@ namespace AssetRipper.IO.Files
{
yield return container;
}
+ foreach (FailedFile file in FailedFiles)
+ {
+ yield return file;
+ }
}
}
- private readonly List m_serializedFiles = new List(0);
- private readonly List m_fileLists = new List(0);
- private readonly List m_resourceFiles = new List(0);
+ private List? m_serializedFiles;
+ private List? m_fileLists;
+ private List? m_resourceFiles;
+ private List? m_failedFiles;
}
}