/// <summary>
        /// Checks for replacement building data within a block.
        /// </summary>
        /// <param name="blockName">Block name</param>
        /// <param name="blockIndex">Block index</param>
        /// <param name="recordIndex">Record index of building within block</param>
        /// <param name="buildingData">BuildingReplacementData output</param>
        /// <returns>True if replacement data found, false otherwise</returns>
        public static bool GetBuildingReplacementData(string blockName, int blockIndex, int recordIndex, out BuildingReplacementData buildingData)
        {
            if (DaggerfallUnity.Settings.AssetInjection)
            {
                BlockRecordKey blockRecordKey = new BlockRecordKey()
                {
                    blockIndex = blockIndex, recordIndex = recordIndex, variant = WorldDataVariants.NoVariant
                };
                string variant = WorldDataVariants.GetBuildingVariant(ref blockRecordKey, blockName);
                if (buildings.ContainsKey(blockRecordKey))
                {
                    buildingData = buildings[blockRecordKey];
                    return(buildingData.BuildingType != noReplacementIndicator);
                }
                else
                {
                    string fileName = GetBuildingReplacementFilename(blockName, blockIndex, recordIndex, variant);

                    // Seek from loose files
                    if (File.Exists(Path.Combine(worldDataPath, fileName)))
                    {
                        string buildingReplacementJson = File.ReadAllText(Path.Combine(worldDataPath, fileName));
                        buildingData = (BuildingReplacementData)SaveLoadManager.Deserialize(typeof(BuildingReplacementData), buildingReplacementJson);
#if !UNITY_EDITOR       // Cache building replacement data, unless running in editor
                        buildings.Add(blockRecordKey, buildingData);
#endif
                        return(true);
                    }
                    // Seek from mods
                    TextAsset buildingReplacementJsonAsset;
                    if (ModManager.Instance != null && ModManager.Instance.TryGetAsset(fileName, false, out buildingReplacementJsonAsset))
                    {
                        buildingData = (BuildingReplacementData)SaveLoadManager.Deserialize(typeof(BuildingReplacementData), buildingReplacementJsonAsset.text);
#if !UNITY_EDITOR       // Cache building replacement data, unless running in editor
                        buildings.Add(blockRecordKey, buildingData);
#endif
                        return(true);
                    }
#if !UNITY_EDITOR   // Only look for replacement data once, non variant. So only look for replaced buildings once (unless running in editor)
                    if (variant == WorldDataVariants.NoVariant)
                    {
                        buildings.Add(blockRecordKey, noReplacementBuilding);
                    }
#endif
                }
            }
            buildingData = noReplacementBuilding;
            return(false);
        }
        /// <summary>
        /// Checks for replacement block data. Only RDB block replacement is currently possible.
        /// </summary>
        /// Replacing entire RMB blocks is not currently possible as RmbFldGroundData contains
        /// groundData as a 2D array and FullSerializer can't do 2D arrays out of the box.
        /// <param name="block">Block index</param>
        /// <param name="blockName">Block name</param>
        /// <param name="dfBlock">DFBlock data output</param>
        /// <returns>True if replacement data found, false otherwise</returns>
        public static bool GetDFBlockReplacementData(int block, string blockName, out DFBlock dfBlock)
        {
            if (DaggerfallUnity.Settings.AssetInjection)
            {
                // Check the block cache and return if found or not (variants are not marked as not present)
                string variant  = WorldDataVariants.GetBlockVariant(blockName);
                string blockKey = blockName + variant;
                if (blocks.ContainsKey(blockKey))
                {
                    dfBlock = blocks[blockKey];
                    return(dfBlock.Index != noReplacementBlock.Index);
                }

                string    fileName = GetDFBlockReplacementFilename(blockName, variant);
                TextAsset blockReplacementJsonAsset;

                // Seek from loose files
                if (File.Exists(Path.Combine(worldDataPath, fileName)))
                {
                    string blockReplacementJson = File.ReadAllText(Path.Combine(worldDataPath, fileName));
                    dfBlock = (DFBlock)SaveLoadManager.Deserialize(typeof(DFBlock), blockReplacementJson);
                }
                // Seek from mods
                else if (ModManager.Instance != null && ModManager.Instance.TryGetAsset(fileName, false, out blockReplacementJsonAsset))
                {
                    dfBlock = (DFBlock)SaveLoadManager.Deserialize(typeof(DFBlock), blockReplacementJsonAsset.text);
                }
                else
                {
#if !UNITY_EDITOR // Cache that there's no replacement block data, non variant. So only look for replaced blocks once (unless running in editor)
                    if (variant == WorldDataVariants.NoVariant)
                    {
                        blocks.Add(blockName, noReplacementBlock);
                    }
#endif
                    dfBlock = noReplacementBlock;
                    return(false);
                }
                dfBlock.Index = block;
#if !UNITY_EDITOR   // Cache block data for added/replaced blocks (unless running in editor)
                blocks.Add(blockKey, dfBlock);
#endif
                Debug.LogFormat("Found DFBlock override: {0} (index: {1})", blockName, block);
                return(true);
            }
            dfBlock = noReplacementBlock;
            return(false);
        }
        /// <summary>
        /// Checks for replacement location data.
        /// </summary>
        /// <param name="regionIndex">Region index</param>
        /// <param name="locationIndex">Location index</param>
        /// <param name="dfLocation">DFLocation data output</param>
        /// <returns>True if replacement data found, false otherwise</returns>
        public static bool GetDFLocationReplacementData(int regionIndex, int locationIndex, out DFLocation dfLocation)
        {
            if (DaggerfallUnity.Settings.AssetInjection)
            {
                int    locationKey = MakeLocationKey(regionIndex, locationIndex);
                bool   newLocation;
                string variant            = WorldDataVariants.GetLocationVariant(locationKey, out newLocation);
                string locationVariantKey = locationKey.ToString() + variant;

                // If it's a new location variant, pre-load it into cache
                if (newLocation && !locations.ContainsKey(locationVariantKey))
                {
                    if (!LoadNewDFLocationVariant(regionIndex, locationIndex, variant))
                    {
                        locationVariantKey = locationKey.ToString();    // Fall back to non-variant if load fails
                    }
                }
                // If found, return a previously cached DFLocation
                if (locations.ContainsKey(locationVariantKey))
                {
                    dfLocation = locations[locationVariantKey];
                    return(dfLocation.LocationIndex != noReplacementLocation.LocationIndex);
                }

                string fileName = GetDFLocationReplacementFilename(regionIndex, locationIndex, variant);
                TextAsset locationReplacementJsonAsset;

                // Seek from loose files
                if (File.Exists(Path.Combine(worldDataPath, fileName)))
                {
                    string locationReplacementJson = File.ReadAllText(Path.Combine(worldDataPath, fileName));
                    dfLocation = (DFLocation)SaveLoadManager.Deserialize(typeof(DFLocation), locationReplacementJson);
                }
                // Seek from mods
                else if (ModManager.Instance != null && ModManager.Instance.TryGetAsset(fileName, false, out locationReplacementJsonAsset))
                {
                    dfLocation = (DFLocation)SaveLoadManager.Deserialize(typeof(DFLocation), locationReplacementJsonAsset.text);
                }
                else
                {
#if !UNITY_EDITOR // Cache that there's no replacement location data, for non-variant. So only look for replaced locations once (unless running in editor)
                    if (variant == WorldDataVariants.NoVariant)
                    {
                        locations.Add(locationVariantKey, noReplacementLocation);
                    }
#endif
                    dfLocation = noReplacementLocation;
                    return(false);
                }
                // Assign any new blocks in this location a block index if they haven't already been assigned
                if (AssignBlockIndices(ref dfLocation))
                {
#if !UNITY_EDITOR   // Cache location data for replaced locations if new blocks have been assigned indices (unless running in editor)
                    locations.Add(locationVariantKey, dfLocation);
#endif
                }
                Debug.LogFormat("Found DFLocation override, region:{0}, index:{1} variant:{2}", regionIndex, locationIndex, variant);
                return(true);
            }
            dfLocation = noReplacementLocation;
            return(false);
        }