using AssetRipper.Assets.Bundles; using AssetRipper.Import.AssetCreation; using AssetRipper.Import.Configuration; using AssetRipper.Import.Logging; using AssetRipper.Import.Structure.Assembly; using AssetRipper.Import.Structure.Assembly.Managers; using AssetRipper.Import.Structure.Platforms; using AssetRipper.IO.Files; using AssetRipper.IO.Files.ResourceFiles; namespace AssetRipper.Import.Structure; public sealed class GameStructure : IDisposable { public GameBundle FileCollection { get; private set; } public PlatformGameStructure? PlatformStructure { get; private set; } public PlatformGameStructure? MixedStructure { get; private set; } public IAssemblyManager AssemblyManager { get; set; } public FileSystem FileSystem { get; } private GameStructure(List paths, FileSystem fileSystem, CoreConfiguration configuration) { Logger.SendStatusChange("loading_step_detect_platform"); FileSystem = fileSystem; PlatformChecker.CheckPlatform(paths, fileSystem, out PlatformGameStructure? platformStructure, out MixedGameStructure? mixedStructure); PlatformStructure = platformStructure; PlatformStructure?.CollectFiles(configuration.ImportSettings.IgnoreStreamingAssets); MixedStructure = mixedStructure; //MixedStructure?.CollectFiles(configuration.IgnoreStreamingAssets); //The PlatformGameStructure constructor adds all the paths to the Assemblies and Files dictionaries //No bundles or assemblies have been loaded yet Logger.SendStatusChange("loading_step_initialize_layout"); InitializeAssemblyManager(configuration); Logger.SendStatusChange("loading_step_begin_scheme_processing"); InitializeGameCollection(configuration.ImportSettings.DefaultVersion, configuration.ImportSettings.TargetVersion); if (!FileCollection.HasAnyAssetCollections()) { Logger.Log(LogType.Warning, LogCategory.Import, "The game structure processor could not find any valid assets."); } } public bool IsValid => FileCollection.HasAnyAssetCollections(); public string? Name => PlatformStructure?.Name ?? MixedStructure?.Name; public static GameStructure Load(IEnumerable paths, FileSystem fileSystem, CoreConfiguration configuration) { List toProcess = ZipExtractor.Process(paths); if (toProcess.Count == 0) { throw new ArgumentException("Game files not found", nameof(paths)); } return new GameStructure(toProcess, fileSystem, configuration); } [MemberNotNull(nameof(FileCollection))] private void InitializeGameCollection(UnityVersion defaultVersion, UnityVersion targetVersion) { Logger.SendStatusChange("loading_step_create_file_collection"); GameAssetFactory assetFactory = new GameAssetFactory(AssemblyManager); IEnumerable filePaths; if (PlatformStructure is null || MixedStructure is null) { filePaths = (PlatformStructure ?? MixedStructure)?.Files.Values() ?? []; } else { filePaths = PlatformStructure.Files.Union(MixedStructure.Files).Select(pair => pair.Value); } FileCollection = GameBundle.FromPaths( filePaths, assetFactory, FileSystem, new GameInitializer(PlatformStructure, MixedStructure, FileSystem, defaultVersion, targetVersion)); } [MemberNotNull(nameof(AssemblyManager))] private void InitializeAssemblyManager(CoreConfiguration configuration) { ScriptingBackend scriptBackend = GetScriptingBackend(configuration.DisableScriptImport); Logger.Info(LogCategory.Import, $"Files use the '{scriptBackend}' scripting backend."); AssemblyManager = scriptBackend switch { ScriptingBackend.Mono => new MonoManager(OnRequestAssembly), ScriptingBackend.IL2Cpp => new IL2CppManager(OnRequestAssembly, configuration.ImportSettings.ScriptContentLevel), _ => new BaseManager(OnRequestAssembly), }; Logger.SendStatusChange("loading_step_load_assemblies"); try { //Loads any Mono or IL2Cpp assemblies AssemblyManager.Initialize(PlatformStructure ?? MixedStructure ?? throw new Exception("No platform structure")); } catch (Exception ex) { Logger.Error(LogCategory.Import, "Could not initialize assembly manager. Switching to the 'Unknown' scripting backend."); Logger.Error(ex); AssemblyManager = new BaseManager(OnRequestAssembly); } } private ScriptingBackend GetScriptingBackend(bool disableScriptImport) { if (disableScriptImport) { Logger.Info(LogCategory.Import, "Script import disabled by settings."); return ScriptingBackend.Unknown; } if (PlatformStructure != null) { ScriptingBackend backend = PlatformStructure.Backend; if (backend != ScriptingBackend.Unknown) { return backend; } } if (MixedStructure != null) { ScriptingBackend backend = MixedStructure.Backend; if (backend != ScriptingBackend.Unknown) { return backend; } } return ScriptingBackend.Unknown; } private void OnRequestAssembly(string assembly) { string assemblyName = $"{assembly}.dll"; ResourceFile? resFile = FileCollection.ResolveResource(assemblyName); if (resFile is not null) { resFile.Stream.Position = 0; AssemblyManager.Read(resFile.Stream.CreateReference(), assemblyName); } else { string? path = RequestAssembly(assembly); if (path is null) { Logger.Log(LogType.Warning, LogCategory.Import, $"Assembly '{assembly}' hasn't been found"); return; } AssemblyManager.Load(path, FileSystem); } Logger.Info(LogCategory.Import, $"Assembly '{assembly}' has been loaded"); } public string? RequestAssembly(string assembly) { return PlatformStructure?.RequestAssembly(assembly) ?? MixedStructure?.RequestAssembly(assembly); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool _) { AssemblyManager?.Dispose(); FileCollection?.Dispose(); } }