public static void LoadBBMapInBackground(string mapName, bool excludeScenery,
                                                 Action <ModelInstance> addMapModel, IProgress <double> progress)
        {
            var modelDir  = GetInterrootPath($@"map\{mapName}");
            var modelDict = new Dictionary <string, Model>();

            Model loadModel(string modelName)
            {
                if (!modelDict.ContainsKey(modelName))
                {
                    SoulsFormats.FLVER flver = null;

                    lock (_lock_IO)
                    {
                        flver = LoadMapFlver(
                            GetInterrootPath($@"map\{mapName}\{mapName}_{modelName.Substring(1)}"));
                    }

                    if (flver != null)
                    {
                        modelDict.Add(modelName, new Model(flver));
                    }
                }

                if (modelDict.ContainsKey(modelName))
                {
                    return(modelDict[modelName]);
                }
                else
                {
                    return(null);
                }
            }

            var msb = MSB64.Read(GetInterrootPath($@"map\MapStudio\{mapName}.msb.dcx"),
                                 (Type == InterrootType.InterrootBloodborne ? MSB64.MSBVersion.MSBVersionBB : MSB64.MSBVersion.MSBVersionDS3));

            void addMsbPart(MSB64.Part part)
            {
                var model = loadModel(part.ModelName);

                if (model != null)
                {
                    var partModelInstance = new ModelInstance(part.Name, model, new Transform(part.Position.X, part.Position.Y, part.Position.Z,
                                                                                              MathHelper.ToRadians(part.Rotation.X), MathHelper.ToRadians(part.Rotation.Y), MathHelper.ToRadians(part.Rotation.Z),
                                                                                              part.Scale.X, part.Scale.Y, part.Scale.Z), (int)part.DrawGroup1, (int)part.DrawGroup2, (int)part.DrawGroup3, (int)part.DrawGroup4);

                    addMapModel.Invoke(partModelInstance);
                }
            }

            // Be sure to update this count if more types of parts are loaded.
            int totalNumberOfParts =
                msb.Parts.MapPieces.Count
            ;

            int i = 0;

            foreach (var part in msb.Parts.MapPieces)
            {
                addMsbPart(part);
                progress?.Report(1.0 * (++i) / totalNumberOfParts);
            }

            modelDict = null;

            GFX.ModelDrawer.RequestTextureLoad();
        }
        public static void LoadDS1MapInBackground(string mapName, bool excludeScenery,
                                                  Action <ModelInstance> addMapModel, IProgress <double> progress)
        {
            var modelDir  = GetInterrootPath($@"map\{mapName}");
            var modelDict = new Dictionary <string, Model>();

            int area = int.Parse(mapName.Substring(1, 2));

            //foreach (var mfn in modelFileNames)
            //{
            //    if (excludeScenery && (mfn.StartsWith("m8") || mfn.StartsWith("m9")))
            //        continue;
            //    modelDict.Add(MiscUtil.GetFileNameWithoutDirectoryOrExtension(mfn), DataFile.LoadFromFile<FLVER>(mfn));
            //}

            Model loadModel(string modelName, PartsParamSubtype partType)
            {
                if (!modelDict.ContainsKey(modelName))
                {
                    SoulsFormats.FLVER flver = null;

                    lock (_lock_IO)
                    {
                        switch (partType)
                        {
                        case PartsParamSubtype.MapPieces:
                            flver = LoadMapFlver(
                                GetInterrootPath($@"map\{mapName}\{modelName}A{area:D2}"));
                            break;

                        case PartsParamSubtype.NPCs:
                        case PartsParamSubtype.DummyNPCs:
                        case PartsParamSubtype.Objects:
                        case PartsParamSubtype.DummyObjects:
                            string bndRelPath = (partType == PartsParamSubtype.Objects ||
                                                 partType == PartsParamSubtype.DummyObjects)
                                    ? $@"obj\{modelName}.objbnd" : $@"chr\{modelName}.chrbnd";

                            var bnd = LoadDecompressedBND(GetInterrootPath(bndRelPath));
                            if (bnd != null)
                            {
                                foreach (var entry in bnd)
                                {
                                    var compareName = entry.Name.ToUpper();
                                    if (flver == null && compareName.EndsWith(".FLVER"))
                                    {
                                        flver = SoulsFormats.FLVER.Read(entry.GetBytes());
                                    }
                                    else if (compareName.EndsWith(".TPF"))
                                    {
                                        TexturePool.AddTpf(SoulsFormats.TPF.Read(entry.GetBytes()));
                                    }
                                }
                            }
                            break;
                        }
                    }

                    if (flver != null)
                    {
                        modelDict.Add(modelName, new Model(flver));
                    }
                }

                if (modelDict.ContainsKey(modelName))
                {
                    return(modelDict[modelName]);
                }
                else
                {
                    return(null);
                }
            }

            var msb = DataFile.LoadFromFile <MSB>(GetInterrootPath($@"map\MapStudio\{mapName}.msb"));

            void addMsbPart(MsbPartsBase part)
            {
                var partSubtype = part.GetSubtypeValue();

                var model = loadModel(part.ModelName, partSubtype);

                if (model != null)
                {
                    var partModelInstance = new ModelInstance(part.Name, model, new Transform(part.PosX, part.PosY, part.PosZ,
                                                                                              MathHelper.ToRadians(part.RotX), MathHelper.ToRadians(part.RotY), MathHelper.ToRadians(part.RotZ),
                                                                                              part.ScaleX, part.ScaleY, part.ScaleZ), part.DrawGroup1, part.DrawGroup2, part.DrawGroup3, part.DrawGroup4);

                    if (partSubtype == PartsParamSubtype.DummyNPCs || partSubtype == PartsParamSubtype.DummyObjects)
                    {
                        partModelInstance.IsDummyMapPart = true;
                    }

                    addMapModel.Invoke(partModelInstance);
                }
            }

            // Be sure to update this count if more types of parts are loaded.
            int totalNumberOfParts =
                msb.Parts.MapPieces.Count +
                msb.Parts.NPCs.Count +
                msb.Parts.DummyNPCs.Count +
                msb.Parts.Objects.Count +
                msb.Parts.DummyObjects.Count
            ;

            int i = 0;

            foreach (var part in msb.Parts.MapPieces)
            {
                addMsbPart(part);
                progress?.Report(1.0 * (++i) / totalNumberOfParts);
            }

            foreach (var part in msb.Parts.NPCs)
            {
                addMsbPart(part);
                progress?.Report(1.0 * (++i) / totalNumberOfParts);
            }

            foreach (var part in msb.Parts.DummyNPCs)
            {
                addMsbPart(part);
                progress?.Report(1.0 * (++i) / totalNumberOfParts);
            }

            foreach (var part in msb.Parts.Objects)
            {
                addMsbPart(part);
                progress?.Report(1.0 * (++i) / totalNumberOfParts);
            }

            foreach (var part in msb.Parts.DummyObjects)
            {
                addMsbPart(part);
                progress?.Report(1.0 * (++i) / totalNumberOfParts);
            }

            modelDict = null;

            GFX.ModelDrawer.RequestTextureLoad();
        }