public void addContentPack(string folderName, string fileName, IModHelper helper = null, Dictionary <string, string> options = null)
        {
            if (helper == null)
            {
                helper = Helper;
            }

            string baseFolder = CustomFarmingReduxMod.folder;

            if (options != null && options.ContainsKey("baseFolder"))
            {
                baseFolder = options["baseFolder"];
            }

            string            path = Path.Combine(baseFolder, folderName, fileName);
            CustomFarmingPack pack = helper.ReadJsonFile <CustomFarmingPack>(path);

            pack.baseFolder = baseFolder;

            Dictionary <string, string> toCrafting = new Dictionary <string, string>();

            pack.folderName = folderName;
            pack.fileName   = fileName;

            if (pack is CustomFarmingPack)
            {
                foreach (CustomMachineBlueprint blueprint in pack.machines)
                {
                    blueprint.pack      = pack;
                    blueprint.texture2d = blueprint.getTexture(helper);

                    CustomFarmingReduxMod.machines.AddOrReplace(blueprint);

                    if (blueprint.production != null)
                    {
                        foreach (RecipeBlueprint recipe in blueprint.production)
                        {
                            if (recipe.texture != null && recipe.texture != "")
                            {
                                recipe.texture2d = recipe.getTexture(helper);
                            }

                            recipe.mBlueprint = blueprint;
                        }
                    }
                    else if (blueprint.asdisplay)
                    {
                        blueprint.pulsate    = false;
                        blueprint.production = new List <RecipeBlueprint>();
                        blueprint.production.Add(new RecipeBlueprint());
                        blueprint.production[0].index = 0;
                        blueprint.production[0].time  = 0;
                    }

                    if (blueprint.crafting != null)
                    {
                        toCrafting.AddOrReplace(blueprint.fullid, $"{blueprint.crafting}/Home/130/true/null/{blueprint.fullid}");
                        CustomFarmingReduxMod.craftingrecipes.AddOrReplace(blueprint.fullid, 0);
                    }

                    if (blueprint.forsale && (blueprint.condition == null || PyTK.PyUtils.CheckEventConditions(blueprint.condition)))
                    {
                        new InventoryItem(new CustomMachine(blueprint), blueprint.price).addToNPCShop(blueprint.shop);
                    }
                }
            }

            if (toCrafting.Count > 0)
            {
                toCrafting.injectInto($"Data/CraftingRecipes");
            }
        }
        private void loadPacks()
        {
            List <CustomFarmingPack> packs = new List <CustomFarmingPack>();

            List <CustomFarmingPack> newPacks = new List <CustomFarmingPack>();
            string machineDir = Path.Combine(Helper.DirectoryPath, folder);

            if (Directory.Exists(machineDir) && new DirectoryInfo(machineDir).GetDirectories().Length > 0)
            {
                PyUtils.loadContentPacks(out newPacks, machineDir, SearchOption.AllDirectories, Monitor);
            }
            machines = new List <CustomMachineBlueprint>();
            Dictionary <string, string> toCrafting = new Dictionary <string, string>();

            List <CustomFarmingPack> legacyPacks = new List <CustomFarmingPack>();
            string legacyDir = Path.Combine(Helper.DirectoryPath, legacyFolder);

            if (Directory.Exists(legacyDir) && new DirectoryInfo(legacyDir).GetDirectories().Length > 0)
            {
                PyUtils.loadContentPacks(out legacyPacks, legacyDir, SearchOption.AllDirectories, Monitor);
            }

            legacyPacks.useAll(l => l.baseFolder = legacyFolder);

            newPacks.AddRange(legacyPacks);

            foreach (CustomFarmingPack lPack in newPacks)
            {
                if (!lPack.legacy)
                {
                    packs.AddOrReplace(lPack);
                    continue;
                }

                string lid = lPack.folderName + "." + lPack.fileName;

                bool exists = false;
                packs.useAll(p => exists = exists || p.machines.Exists(m => m.legacy == lid));

                if (exists)
                {
                    Monitor.Log("Skipped legacy machine " + lid + " because a new version was found.", LogLevel.Trace);
                    continue;
                }

                CustomFarmingPack next = new CustomFarmingPack();
                next.legacy     = true;
                next.author     = lPack.author;
                next.fileName   = lPack.fileName;
                next.folderName = lPack.folderName;
                next.name       = lPack.name;
                next.machines   = new List <CustomMachineBlueprint>();
                CustomMachineBlueprint legacyMachine = new CustomMachineBlueprint();
                legacyMachine.id          = 0;
                legacyMachine.pack        = next;
                legacyMachine.category    = lPack.CategoryName;
                legacyMachine.description = lPack.Description;
                legacyMachine.name        = lPack.Name;
                legacyMachine.frames      = lPack.WorkAnimationFrames;
                legacyMachine.pulsate     = lPack.WorkAnimationFrames <= 0;
                legacyMachine.readyindex  = lPack.ReadyTileIndex;
                legacyMachine.tileindex   = lPack.TileIndex;
                legacyMachine.texture     = lPack.Tilesheet;
                legacyMachine.fps         = 6;
                legacyMachine.showitem    = lPack.displayItem;
                legacyMachine.itempos     = new int[] { lPack.displayItemX, lPack.displayItemY };
                legacyMachine.itemzoom    = lPack.displayItemZoom;
                legacyMachine.crafting    = lPack.Crafting;

                if (lPack.StarterMaterial > 0)
                {
                    IngredientBlueprint starter = new IngredientBlueprint();
                    starter.index         = lPack.StarterMaterial;
                    starter.stack         = lPack.StarterMaterialStack;
                    legacyMachine.starter = starter;
                }

                if (lPack.Produce != null && lPack.Produce.ProduceID <= 0)
                {
                    legacyMachine.asdisplay = true;
                }

                if (lPack.Produce != null && lPack.Produce.ProduceID > 0)
                {
                    legacyMachine.production = new List <RecipeBlueprint>();
                    legacyMachine.legacy     = lid;
                    Monitor.Log("Legacy:" + legacyMachine.legacy);
                    RecipeBlueprint baseProduce = new RecipeBlueprint();
                    baseProduce.name        = lPack.Produce.Name;
                    baseProduce.index       = lPack.Produce.ProduceID;
                    baseProduce.colored     = lPack.Produce.useColor;
                    baseProduce.prefix      = lPack.Produce.usePrefix;
                    baseProduce.suffix      = lPack.Produce.useSuffic;
                    baseProduce.texture     = lPack.Produce.Tilesheet;
                    baseProduce.tileindex   = lPack.TileIndex;
                    baseProduce.time        = lPack.Produce.ProductionTime;
                    baseProduce.stack       = lPack.Produce.Stack;
                    baseProduce.description = lPack.Produce.Description;
                    baseProduce.quality     = lPack.Produce.Quality;
                    List <int> materials = lPack.Materials.ToList();
                    baseProduce.materials = new List <IngredientBlueprint> {
                        new IngredientBlueprint()
                    };
                    baseProduce.materials[0].index        = materials[0];
                    baseProduce.materials[0].exactquality = lPack.Produce.MaterialQuality;
                    baseProduce.materials[0].stack        = lPack.RequiredStack;

                    if (lPack.SpecialProduce != null)
                    {
                        foreach (LegacySpecialProduce pnext in lPack.SpecialProduce)
                        {
                            RecipeBlueprint nextProduce = new RecipeBlueprint();
                            nextProduce.name        = pnext.Name != null ? pnext.Name : baseProduce.name;
                            nextProduce.index       = pnext.ProduceID != -1 ? pnext.ProduceID : baseProduce.index;
                            nextProduce.colored     = pnext.uc ? pnext._useColor : baseProduce.colored;
                            nextProduce.prefix      = pnext.up ? pnext._usePrefix : baseProduce.prefix;
                            nextProduce.suffix      = pnext.us ? pnext._useSuffix : baseProduce.suffix;
                            nextProduce.texture     = pnext.Tilesheet != null ? pnext.Tilesheet : baseProduce.texture;
                            nextProduce.tileindex   = pnext.TileIndex != -1 ? pnext.TileIndex : baseProduce.tileindex;
                            nextProduce.time        = pnext.ProductionTime != -1 ? pnext.ProductionTime : baseProduce.time;
                            nextProduce.stack       = pnext.Stack != -1 ? pnext.Stack : baseProduce.stack;
                            nextProduce.description = pnext.Description != null ? pnext.Description : baseProduce._description;
                            nextProduce.quality     = pnext.Quality != -9 ? pnext.Quality : baseProduce.quality;
                            nextProduce.materials   = new List <IngredientBlueprint>()
                            {
                                new IngredientBlueprint()
                            };
                            nextProduce.materials[0].index        = pnext.Material;
                            nextProduce.materials[0].exactquality = pnext.MaterialQuality;
                            nextProduce.materials[0].stack        = lPack.RequiredStack;
                            materials.Remove(pnext.Material);
                            legacyMachine.production.Add(nextProduce);
                        }
                    }

                    baseProduce.materials[0].index = materials.Count > 0 ? materials[0] : baseProduce.materials[0].index;
                    materials.Remove(baseProduce.materials[0].index);
                    baseProduce.include = materials.Count > 0 ? materials.ToArray() : null;
                    legacyMachine.production.Add(baseProduce);
                }

                next.machines.Add(legacyMachine);
                packs.Add(next);
            }

            foreach (CustomFarmingPack pack in packs)
            {
                foreach (CustomMachineBlueprint blueprint in pack.machines)
                {
                    blueprint.pack = pack;
                    machines.AddOrReplace(blueprint);

                    if (blueprint.production != null)
                    {
                        foreach (RecipeBlueprint recipe in blueprint.production)
                        {
                            recipe.mBlueprint = blueprint;
                        }
                    }
                    else if (blueprint.asdisplay)
                    {
                        blueprint.pulsate    = false;
                        blueprint.production = new List <RecipeBlueprint>();
                        blueprint.production.Add(new RecipeBlueprint());
                        blueprint.production[0].index = 0;
                        blueprint.production[0].time  = (STime.CURRENT + STime.YEAR * 1000).timestamp;
                    }

                    CustomObjectData data = new CustomObjectData(blueprint.fullid, $"{blueprint.name}/{blueprint.price}/-300/Crafting -9/{blueprint.description}/true/true/0/{blueprint.name}", blueprint.getTexture(), Color.White, blueprint.tileindex, true, typeof(CustomMachine), (blueprint.crafting == null || blueprint.crafting == "") ? null : new CraftingData(blueprint.fullid, blueprint.crafting));

                    if (blueprint.forsale && (blueprint.condition == null || PyUtils.CheckEventConditions(blueprint.condition)))
                    {
                        new InventoryItem(new CustomMachine(blueprint), blueprint.price).addToNPCShop(blueprint.shop);
                    }
                }
            }

            Monitor.Log(packs.Count + " Content Packs with " + machines.Count + " machines found.", LogLevel.Trace);
        }