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; } }