Пример #1
0
        public new MTexture GetAtlasSubtexturesAt(string key, int index)
        {
            if (orderedTexturesCache.TryGetValue(key, out List <MTexture> list))
            {
                if (index < 0 || index >= list.Count)
                {
                    Logger.Log(LogLevel.Warn, "Atlas", $"Requested atlas subtexture that falls out of range: {key} {index}");

                    return(GetFallback());
                }
                return(list[index]);
            }

            MTexture result = GetAtlasSubtextureFromAtlasAt(key, index);

            if (result == null)
            {
                MTexture fallback = GetFallback();
                if (fallback != null)
                {
                    // SpriteData and other places use GetAtlasSubtextureAt to check if textures exist.
                    // Logging this verbosely when no fallback exists doesn't make sense in those cases.
                    Logger.Log(LogLevel.Warn, "Atlas", $"Requested atlas subtexture that does not exist: {key} {index}");
                    return(fallback);
                }
            }
            return(result);
        }
Пример #2
0
        public new List <MTexture> GetAtlasSubtextures(string key)
        {
            List <MTexture> result = orig_GetAtlasSubtextures(key);

            if (result == null || result.Count == 0)
            {
                Logger.Log(LogLevel.Warn, "Atlas.GetAtlasSubtextures", $"Requested atlas subtextures but none were found: {key}");
            }
            return(result);
        }
Пример #3
0
        // log missing texture when getting one by ID (for example, tilesets)
        public new MTexture this[string id] {
            [MonoModReplace]
            get {
                if (!textures.ContainsKey(id))
                {
                    Logger.Log(LogLevel.Warn, "Atlas", $"Requested texture that does not exist: {id}");
                }
                return(textures[id]);
            }

            // we don't want to modify the setter, but want it to exist in the patch class so that we can call it from within our patches.
            [MonoModIgnore]
            set { }
        }
Пример #4
0
        // log missing texture when getting one by ID (for example, tilesets)
        public new MTexture this[string id] {
            [MonoModReplace]
            get {
                if (!textures.TryGetValue(id, out MTexture result))
                {
                    Logger.Log(LogLevel.Warn, "Atlas", $"Requested texture that does not exist: {id}");
                    return(GetFallback());
                }
                return(result);
            }

            // we don't want to modify the setter, but want it to exist in the patch class so that we can call it from within our patches.
            [MonoModIgnore]
            set { }
        }
Пример #5
0
        public new List <MTexture> GetAtlasSubtextures(string key)
        {
            PushFallback(null);
            List <MTexture> result = orig_GetAtlasSubtextures(key);

            PopFallback();
            if (result == null || result.Count == 0)
            {
                Logger.Log(LogLevel.Warn, "Atlas", $"Requested atlas subtextures but none were found: {key}");
                MTexture fallback = GetFallback();
                if (fallback != null)
                {
                    return new List <MTexture>()
                           {
                               fallback
                           }
                }
                ;
            }
            return(result);
        }
Пример #6
0
        /// <summary>
        /// Feed the given ModAsset into the atlas.
        /// </summary>
        public static void Ingest(this Atlas self, ModAsset asset)
        {
            // Crawl through all child assets.
            if (asset.Type == typeof(AssetTypeDirectory))
            {
                foreach (ModAsset child in asset.Children)
                {
                    self.Ingest(child);
                }
                return;
            }

            // Forcibly add the mod content to the atlas.
            if (asset.Type == typeof(Texture2D))
            {
                Logger.Log(LogLevel.Verbose, "Atlas.Ingest", $"{self.GetDataPath()} + {asset.PathVirtual}");

                string parentPath = self.GetDataPath();
                if (parentPath.StartsWith(Everest.Content.PathContentOrig))
                {
                    parentPath = parentPath.Substring(Everest.Content.PathContentOrig.Length + 1);
                }
                parentPath = parentPath.Replace('\\', '/');

                bool   lq   = false;
                string path = asset.PathVirtual;

                if (path.StartsWith(parentPath + "LQ/"))
                {
                    lq   = true;
                    path = path.Substring(parentPath.Length + 3);
                }
                else if (path.StartsWith(parentPath + "/"))
                {
                    path = path.Substring(parentPath.Length + 1);
                }
                else
                {
                    return;
                }

                VirtualTexture vtex = VirtualContentExt.CreateTexture(asset);
                MTexture       mtex;
                MTextureMeta   meta = asset.GetMeta <MTextureMeta>();
                if (meta != null)
                {
                    if (meta.Width == 0)
                    {
                        meta.Width = vtex.Width;
                    }
                    if (meta.Height == 0)
                    {
                        meta.Height = vtex.Height;
                    }
                }

                Dictionary <string, MTexture> textures = self.GetTextures();
                MTexture existing;
                if (textures.TryGetValue(path, out existing))
                {
                    if (lq && !CoreModule.Settings.LQAtlas)
                    {
                        return;
                    }

                    if (existing.Texture.GetMetadata() == asset)
                    {
                        return; // We're the currently active overlay.
                    }
                    if (meta != null)
                    {
                        // Apply width and height from existing meta.
                        existing.AddOverride(vtex, new Vector2(meta.X, meta.Y), meta.Width, meta.Height);
                    }
                    else
                    {
                        // Keep width and height from existing instance.
                        existing.AddOverride(vtex, existing.DrawOffset, existing.Width, existing.Height);
                    }

                    mtex = existing;
                }
                else
                {
                    if (meta != null)
                    {
                        // Apply width and height from existing meta.
                        mtex = new MTexture(vtex, new Vector2(meta.X, meta.Y), meta.Width, meta.Height);
                    }
                    else
                    {
                        // Apply width and height from replacement texture.
                        mtex = new MTexture(vtex);
                    }
                    mtex.SetAtlasPath(path);
                }

                VTextureToMTextureMap[vtex.Name] = mtex;
                self[path] = mtex;
                if (!self.Sources.Contains(vtex))
                {
                    self.Sources.Add(vtex);
                }
                return;
            }
        }
Пример #7
0
        public void Ingest(ModAsset asset)
        {
            if (asset == null)
            {
                return;
            }

            // Crawl through all child assets.
            if (asset.Type == typeof(AssetTypeDirectory))
            {
                lock (asset.Children) {
                    foreach (ModAsset child in asset.Children)
                    {
                        Ingest(child);
                    }
                }
                return;
            }

            // Forcibly add the mod content to the atlas.
            if (asset.Type != typeof(Texture2D))
            {
                return;
            }

            string path = asset.PathVirtual;

            if (!path.StartsWith(RelativeDataPath))
            {
                return;
            }
            path = path.Substring(RelativeDataPath.Length);

            if (textures.TryGetValue(path, out MTexture mtex))
            {
                Logger.Log(LogLevel.Verbose, "Atlas.Ingest", $"{Path.GetFileName(DataPath)} + ({asset.Source?.Name ?? "???"}) {path}");
                mtex.SetOverride(asset);
                this[path] = mtex;
                return;
            }

            VirtualTexture vtex;

            try {
                vtex = VirtualContentExt.CreateTexture(asset);
            } catch {
                // The game is going to crash from this. Log the offending texture to make debugging easier.
                Logger.Log(LogLevel.Verbose, "Atlas.Ingest", $"Error while loading texture {path} ({asset.Source?.Name ?? "???"}) into atlas {Path.GetFileName(DataPath)}");
                throw;
            }
            MTextureMeta meta = asset.GetMeta <MTextureMeta>();

            if (meta != null)
            {
                // Apply width and height from meta.
                if (meta.Width == 0)
                {
                    meta.Width = vtex.Width;
                }
                if (meta.Height == 0)
                {
                    meta.Height = vtex.Height;
                }
                mtex = new MTexture(vtex, new Vector2(meta.X, meta.Y), meta.Width, meta.Height);
            }
            else
            {
                // Apply width and height from replacement texture.
                mtex = new MTexture(vtex);
            }
            mtex.AtlasPath = path;
            mtex.SetAtlas(this);
            mtex.SetOverride(asset);
            this[path] = mtex;
            Sources.Add(vtex);
        }
Пример #8
0
        public static void Ingest(this Atlas self, AssetMetadata asset)
        {
            Logger.Log("Atlas.Ingest", $"{self.GetDataPath()} + {asset.PathRelative}");

            // Crawl through all child assets.
            if (asset.AssetType == typeof(AssetTypeDirectory))
            {
                foreach (AssetMetadata child in asset.Children)
                {
                    self.Ingest(child);
                }
                return;
            }

            // Forcibly add the mod content to the atlas.
            if (asset.AssetType == typeof(Texture2D))
            {
                string parentPath = self.GetDataPath();
                if (parentPath.StartsWith(Everest.Content.PathContentOrig))
                {
                    parentPath = parentPath.Substring(Everest.Content.PathContentOrig.Length + 1);
                }
                parentPath = parentPath.Replace('\\', '/');

                string path = asset.PathRelative;
                if (!path.StartsWith(parentPath))
                {
                    return;
                }
                path = path.Substring(parentPath.Length + 1);

                VirtualTexture replacementV = VirtualContentExt.CreateTexture(asset);
                MTexture       replacement;
                MTextureMeta   meta = asset.GetMeta <MTextureMeta>();

                Dictionary <string, MTexture> textures = self.GetTextures();
                MTexture existing;
                if (textures.TryGetValue(path, out existing))
                {
                    // We're the currently active overlay.
                    if (existing.Texture.GetMetadata() == asset)
                    {
                        return;
                    }

                    if (meta != null)
                    {
                        // Apply width and height from existing meta.
                        existing.AddOverlay(replacementV, new Vector2(meta.X, meta.Y), meta.Width, meta.Height);
                    }
                    else
                    {
                        // Keep width and height from existing instance.
                        existing.AddOverlay(replacementV, existing.DrawOffset, existing.Width, existing.Height);
                    }

                    replacement = existing;
                }
                else
                {
                    if (meta != null)
                    {
                        // Apply width and height from existing meta.
                        replacement = new MTexture(replacementV, new Vector2(meta.X, meta.Y), meta.Width, meta.Height);
                    }
                    else
                    {
                        // Apply width and height from replacement texture.
                        replacement = new MTexture(replacementV);
                    }
                    // TODO: What's with the AtlasPath? Seems to stem from an atlas metadata property...
                }

                self[path] = replacement;
                return;
            }
        }
Пример #9
0
        /// <summary>
        /// Feed the given ModAsset into the atlas.
        /// </summary>
        public static void Ingest(this Atlas atlas, ModAsset asset)
        {
            if (asset == null)
            {
                return;
            }

            // Crawl through all child assets.
            if (asset.Type == typeof(AssetTypeDirectory))
            {
                lock (asset.Children) {
                    foreach (ModAsset child in asset.Children)
                    {
                        atlas.Ingest(child);
                    }
                }
                return;
            }

            // Forcibly add the mod content to the atlas.
            if (asset.Type != typeof(Texture2D))
            {
                return;
            }

            string parentPath = atlas.GetDataPath();

            if (parentPath.StartsWith(Everest.Content.PathContentOrig))
            {
                parentPath = parentPath.Substring(Everest.Content.PathContentOrig.Length + 1);
            }
            parentPath = parentPath.Replace('\\', '/');

            string path = asset.PathVirtual;

            if (!path.StartsWith(parentPath + "/"))
            {
                return;
            }
            path = path.Substring(parentPath.Length + 1);

            Logger.Log(LogLevel.Verbose, "Atlas.Ingest", $"{Path.GetFileName(atlas.GetDataPath())} + ({asset.Source?.Name ?? "???"}) {path}");

            MTexture mtex;

            Dictionary <string, MTexture> textures = atlas.GetTextures();

            if (textures.TryGetValue(path, out mtex))
            {
                mtex.SetOverride(asset);
            }
            else
            {
                VirtualTexture vtex = VirtualContentExt.CreateTexture(asset);
                MTextureMeta   meta = asset.GetMeta <MTextureMeta>();
                if (meta != null)
                {
                    // Apply width and height from meta.
                    if (meta.Width == 0)
                    {
                        meta.Width = vtex.Width;
                    }
                    if (meta.Height == 0)
                    {
                        meta.Height = vtex.Height;
                    }
                    mtex = new MTexture(vtex, new Vector2(meta.X, meta.Y), meta.Width, meta.Height);
                }
                else
                {
                    // Apply width and height from replacement texture.
                    mtex = new MTexture(vtex);
                }
                mtex.AtlasPath = path;
                mtex.SetAtlas(atlas);
                mtex.SetOverride(asset);
            }

            atlas.ResetCaches();
            atlas[path] = mtex;
        }
Пример #10
0
                private Source _RequestStart()
                {
                    Entries     = null;
                    ErrorDialog = null;

                    string data;

                    try {
                        using (WebClient wc = new WebClient())
                            data = wc.DownloadString(Index);
                    } catch (Exception e) {
                        ErrorDialog = "updater_versions_err_download";
                        Logger.Log(LogLevel.Warn, "updater", "Failed requesting index: " + e.ToString());
                        return(this);
                    }

                    List <Entry> entries = new List <Entry>();

                    if (ParseData != null)
                    {
                        try {
                            entries.AddRange(ParseData(data));
                        } catch (Exception e) {
                            ErrorDialog = "updater_versions_err_format";
                            Logger.Log(LogLevel.Warn, "updater", "Failed parsing index: " + e.ToString());
                            return(this);
                        }
                    }
                    else
                    {
                        string[] lines = data.Split('\n');
                        for (int i = 0; i < lines.Length; i++)
                        {
                            string line = lines[i].Trim('\r', '\n').Trim();
                            if (line.Length == 0 || line.StartsWith("#"))
                            {
                                continue;
                            }

                            try {
                                Entry entry = ParseLine(line);
                                if (entry != null)
                                {
                                    entries.Add(entry);
                                }
                            } catch (Exception e) {
                                ErrorDialog = "updater_versions_err_format";
                                Logger.Log(LogLevel.Warn, "updater", "Failed parsing index: " + e.ToString());
                                return(this);
                            }
                        }
                    }

                    // Highly convoluted scientific method to determine the entry order:
                    // - Order by first occurence of branch
                    // - Order by version inside branch
                    Dictionary <string, int> branchFirsts = new Dictionary <string, int>();

                    // Force stable, then beta, then dev branches to appear first.
                    branchFirsts["stable"] = int.MaxValue;
                    branchFirsts["beta"]   = int.MaxValue - 3;
                    branchFirsts["dev"]    = int.MaxValue - 4;

                    // Make sure that the branch we're on appears between stable and beta.
                    // This ensures that people don't miss out on important stability updates,
                    // but don't get dragged onto another branch by accident.
                    foreach (Entry entry in entries)
                    {
                        if (entry.Build == Build)
                        {
                            CoreModule.Settings.CurrentBranch = entry.Branch;
                            break;
                        }
                    }

                    if (CoreModule.Settings.CurrentBranch != null)
                    {
                        branchFirsts[CoreModule.Settings.CurrentBranch] = int.MaxValue - 2;
                    }

                    for (int i = 0; i < entries.Count; i++)
                    {
                        Entry entry = entries[i];
                        if (!branchFirsts.ContainsKey(entry.Branch))
                        {
                            branchFirsts[entry.Branch] = i;
                        }
                    }

                    entries.Sort((a, b) => {
                        if (a.Branch != b.Branch)
                        {
                            return(-(branchFirsts[a.Branch].CompareTo(branchFirsts[b.Branch])));
                        }
                        return(-a.Build.CompareTo(b.Build));
                    });
                    Entries = new ReadOnlyCollection <Entry>(entries);
                    return(this);
                }
Пример #11
0
            /// <summary>
            /// Load a mod .dll given its metadata at runtime. Doesn't load the mod content.
            /// </summary>
            /// <param name="meta">Metadata of the mod to load.</param>
            public static void LoadMod(EverestModuleMetadata meta)
            {
                if (Flags.IsDisabled || !Flags.SupportRuntimeMods)
                {
                    Logger.Log(LogLevel.Warn, "loader", "Loader disabled!");
                    return;
                }

                if (meta == null)
                {
                    return;
                }

                // Add an AssemblyResolve handler for all bundled libraries.
                AppDomain.CurrentDomain.AssemblyResolve += GenerateModAssemblyResolver(meta);

                ApplyRelinkerHackfixes(meta);

                // Load the actual assembly.
                Assembly asm = null;

                if (!string.IsNullOrEmpty(meta.PathArchive))
                {
                    bool returnEarly = false;
                    using (ZipFile zip = new ZipFile(meta.PathArchive)) {
                        foreach (ZipEntry entry in zip.Entries)
                        {
                            string entryName = entry.FileName.Replace('\\', '/');
                            if (entryName == meta.DLL)
                            {
                                using (MemoryStream stream = entry.ExtractStream()) {
                                    if (meta.Prelinked)
                                    {
                                        asm = Assembly.Load(stream.GetBuffer());
                                    }
                                    else
                                    {
                                        asm = Relinker.GetRelinkedAssembly(meta, stream);
                                    }
                                }
                            }

                            if (entryName == "main.lua")
                            {
                                new LuaModule(meta).Register();
                                returnEarly = true;
                            }
                        }
                    }

                    if (returnEarly)
                    {
                        return;
                    }
                }
                else
                {
                    if (!string.IsNullOrEmpty(meta.DLL) && File.Exists(meta.DLL))
                    {
                        if (meta.Prelinked)
                        {
                            asm = Assembly.LoadFrom(meta.DLL);
                        }
                        else
                        {
                            using (FileStream stream = File.OpenRead(meta.DLL))
                                asm = Relinker.GetRelinkedAssembly(meta, stream);
                        }
                    }

                    if (File.Exists(Path.Combine(meta.PathDirectory, "main.lua")))
                    {
                        new LuaModule(meta).Register();
                        return;
                    }
                }

                ApplyModHackfixes(meta, asm);

                if (asm == null)
                {
                    // Register a null module for content mods.
                    new NullModule(meta).Register();
                    return;
                }

                LoadModAssembly(meta, asm);
            }
Пример #12
0
            /// <summary>
            /// Load a mod from a directory at runtime.
            /// </summary>
            /// <param name="dir">The path to the mod directory.</param>
            public static void LoadDir(string dir)
            {
                if (Flags.IsDisabled || !Flags.SupportRuntimeMods)
                {
                    Logger.Log(LogLevel.Warn, "loader", "Loader disabled!");
                    return;
                }

                if (!Directory.Exists(dir)) // Relative path?
                {
                    dir = Path.Combine(PathMods, dir);
                }
                if (!Directory.Exists(dir)) // It just doesn't exist.
                {
                    return;
                }

                Logger.Log(LogLevel.Verbose, "loader", $"Loading mod directory: {dir}");

                EverestModuleMetadata meta = null;

                EverestModuleMetadata[] multimetas = null;

                string metaPath = Path.Combine(dir, "metadata.yaml");

                if (File.Exists(metaPath))
                {
                    using (StreamReader reader = new StreamReader(metaPath)) {
                        try {
                            meta = YamlHelper.Deserializer.Deserialize <EverestModuleMetadata>(reader);
                            meta.PathDirectory = dir;
                            meta.PostParse();
                        } catch (Exception e) {
                            Logger.Log(LogLevel.Warn, "loader", $"Failed parsing metadata.yaml in {dir}: {e}");
                        }
                    }
                }

                metaPath = Path.Combine(dir, "multimetadata.yaml");
                if (!File.Exists(metaPath))
                {
                    metaPath = Path.Combine(dir, "everest.yaml");
                }
                if (!File.Exists(metaPath))
                {
                    metaPath = Path.Combine(dir, "everest.yml");
                }
                if (File.Exists(metaPath))
                {
                    using (StreamReader reader = new StreamReader(metaPath)) {
                        try {
                            if (!reader.EndOfStream)
                            {
                                multimetas = YamlHelper.Deserializer.Deserialize <EverestModuleMetadata[]>(reader);
                                foreach (EverestModuleMetadata multimeta in multimetas)
                                {
                                    multimeta.PathDirectory = dir;
                                    multimeta.PostParse();
                                }
                            }
                        } catch (Exception e) {
                            Logger.Log(LogLevel.Warn, "loader", $"Failed parsing everest.yaml in {dir}: {e}");
                        }
                    }
                }

                FileSystemModContent  contentMeta       = new FileSystemModContent(dir);
                EverestModuleMetadata contentMetaParent = null;

                Action contentCrawl = () => {
                    if (contentMeta == null)
                    {
                        return;
                    }
                    if (contentMetaParent != null)
                    {
                        contentMeta.Mod  = contentMetaParent;
                        contentMeta.Name = contentMetaParent.Name;
                    }
                    Content.Crawl(contentMeta);
                    contentMeta = null;
                };

                if (multimetas != null)
                {
                    foreach (EverestModuleMetadata multimeta in multimetas)
                    {
                        if (contentMetaParent == null)
                        {
                            contentMetaParent = multimeta;
                        }
                        LoadModDelayed(multimeta, contentCrawl);
                    }
                }
                else
                {
                    if (meta == null)
                    {
                        meta = new EverestModuleMetadata()
                        {
                            Name          = "_dir_" + Path.GetFileName(dir),
                            VersionString = "0.0.0-dummy",
                            PathDirectory = dir
                        };
                        meta.PostParse();
                    }
                    contentMetaParent = meta;
                    LoadModDelayed(meta, contentCrawl);
                }
            }
Пример #13
0
            /// <summary>
            /// Load a mod from a .zip archive at runtime.
            /// </summary>
            /// <param name="archive">The path to the mod .zip archive.</param>
            public static void LoadZip(string archive)
            {
                if (Flags.IsDisabled || !Flags.SupportRuntimeMods)
                {
                    Logger.Log(LogLevel.Warn, "loader", "Loader disabled!");
                    return;
                }

                if (!File.Exists(archive)) // Relative path? Let's just make it absolute.
                {
                    archive = Path.Combine(PathMods, archive);
                }
                if (!File.Exists(archive)) // It just doesn't exist.
                {
                    return;
                }

                Logger.Log(LogLevel.Verbose, "loader", $"Loading mod .zip: {archive}");

                EverestModuleMetadata meta = null;

                EverestModuleMetadata[] multimetas = null;

                // In case the icon appears before the metadata in the .zip, store it temporarily, set it later.
                Texture2D icon = null;

                using (ZipFile zip = new ZipFile(archive)) {
                    foreach (ZipEntry entry in zip.Entries)
                    {
                        if (entry.FileName == "metadata.yaml")
                        {
                            using (MemoryStream stream = entry.ExtractStream())
                                using (StreamReader reader = new StreamReader(stream)) {
                                    try {
                                        meta             = YamlHelper.Deserializer.Deserialize <EverestModuleMetadata>(reader);
                                        meta.PathArchive = archive;
                                        meta.PostParse();
                                    } catch (Exception e) {
                                        Logger.Log(LogLevel.Warn, "loader", $"Failed parsing metadata.yaml in {archive}: {e}");
                                    }
                                }
                            continue;
                        }
                        if (entry.FileName == "multimetadata.yaml" ||
                            entry.FileName == "everest.yaml" ||
                            entry.FileName == "everest.yml")
                        {
                            using (MemoryStream stream = entry.ExtractStream())
                                using (StreamReader reader = new StreamReader(stream)) {
                                    try {
                                        if (!reader.EndOfStream)
                                        {
                                            multimetas = YamlHelper.Deserializer.Deserialize <EverestModuleMetadata[]>(reader);
                                            foreach (EverestModuleMetadata multimeta in multimetas)
                                            {
                                                multimeta.PathArchive = archive;
                                                multimeta.PostParse();
                                            }
                                        }
                                    } catch (Exception e) {
                                        Logger.Log(LogLevel.Warn, "loader", $"Failed parsing multimetadata.yaml in {archive}: {e}");
                                    }
                                }
                            continue;
                        }
                        if (entry.FileName == "icon.png")
                        {
                            using (Stream stream = entry.ExtractStream())
                                icon = Texture2D.FromStream(Celeste.Instance.GraphicsDevice, stream);
                            continue;
                        }
                    }
                }

                if (meta != null)
                {
                    if (icon != null)
                    {
                        meta.Icon = icon;
                    }
                }

                ZipModContent         contentMeta       = new ZipModContent(archive);
                EverestModuleMetadata contentMetaParent = null;

                Action contentCrawl = () => {
                    if (contentMeta == null)
                    {
                        return;
                    }
                    if (contentMetaParent != null)
                    {
                        contentMeta.Mod  = contentMetaParent;
                        contentMeta.Name = contentMetaParent.Name;
                    }
                    Content.Crawl(contentMeta);
                    contentMeta = null;
                };

                if (multimetas != null)
                {
                    foreach (EverestModuleMetadata multimeta in multimetas)
                    {
                        if (contentMetaParent == null)
                        {
                            contentMetaParent = multimeta;
                        }
                        LoadModDelayed(multimeta, contentCrawl);
                    }
                }
                else
                {
                    if (meta == null)
                    {
                        meta = new EverestModuleMetadata()
                        {
                            Name          = "_zip_" + Path.GetFileNameWithoutExtension(archive),
                            VersionString = "0.0.0-dummy",
                            PathArchive   = archive
                        };
                        meta.PostParse();
                    }
                    contentMetaParent = meta;
                    LoadModDelayed(meta, contentCrawl);
                }
            }