static void ScanProjectFileAsync(AssetFileRecord fileRecord)
        {
            if (m_CancelScanProcess)
            {
                return;
            }

            // Read the asset data file
            string assetDataFile  = string.Empty;
            bool   hasFileChanged = false;

            try
            {
                assetDataFile = File.ReadAllText(m_ProjectPath + "/" + fileRecord.assetFilePath);
            }
            catch
            {
                // Continue to the next asset if we can't read the current one.
                return;
            }

            // Read the asset meta data file
            string assetMetaFile      = File.ReadAllText(m_ProjectPath + "/" + fileRecord.assetMetaFilePath);
            bool   hasMetaFileChanges = false;

            foreach (AssetConversionRecord record in m_ConversionData.assetRecords)
            {
                if (assetDataFile.Contains(record.target))
                {
                    hasFileChanged = true;

                    assetDataFile = assetDataFile.Replace(record.target, record.replacement);
                }

                //// Check meta file
                if (assetMetaFile.Contains(record.target))
                {
                    hasMetaFileChanges = true;

                    assetMetaFile = assetMetaFile.Replace(record.target, record.replacement);
                }
            }

            if (hasFileChanged)
            {
                AssetModificationRecord modifiedAsset;
                modifiedAsset.assetFilePath = fileRecord.assetFilePath;
                modifiedAsset.assetDataFile = assetDataFile;

                m_ModifiedAssetList.Add(modifiedAsset);

                m_ProjectScanResults += fileRecord.assetFilePath + "\n";
            }

            if (hasMetaFileChanges)
            {
                AssetModificationRecord modifiedAsset;
                modifiedAsset.assetFilePath = fileRecord.assetMetaFilePath;
                modifiedAsset.assetDataFile = assetMetaFile;

                m_ModifiedAssetList.Add(modifiedAsset);

                m_ProjectScanResults += fileRecord.assetMetaFilePath + "\n";
            }
        }
        static void ScanProjectFile(AssetFileRecord fileRecord)
        {
            if (m_CancelScanProcess)
            {
                return;
            }

            // Read the asset data file
            string assetDataFile;
            bool   hasDataFileChanged = false;

            try
            {
                assetDataFile = File.ReadAllText(m_ProjectPath + "/" + fileRecord.assetFilePath);
            }
            catch
            {
                // Continue to the next asset if we can't read the current one.
                return;
            }

            // Check if asset file references any text components.
            if (assetDataFile.Contains(k_TextMeshProScriptID) || assetDataFile.Contains(k_TextMeshProUGUIScriptID))
            {
                float characterSpacingValue    = 0;
                float newCharacterSpacingValue = 0;
                float wordSpacingValue         = 0;
                float newWordSpacingValue      = 0;
                float lineSpacingValue         = 0;
                float newLineSpacingValue      = 0;
                float paragraphSpacingValue    = 0;
                float newParagraphSpacingValue = 0;

                float fontSize          = 0;
                float samplingPointSize = 0;
                float faceScale         = 1;

                List <string> lines = assetDataFile.Split('\n').ToList();
                int           serializedVersionInsertionIndex = 0;

                int readingFlag = 0;

                // Read through each lines of the asset file
                for (int i = 0; i < lines.Count; i++)
                {
                    string line = lines[i];

                    // Track potential line index to insert serializedVersion property
                    if (line.Contains("MonoBehaviour:"))
                    {
                        serializedVersionInsertionIndex = i + 1;
                        continue;
                    }

                    // Read until we find the line that contains a reference to a text component
                    if (readingFlag == 0 && (line.Contains(k_TextMeshProScriptID) || line.Contains(k_TextMeshProUGUIScriptID)))
                    {
                        // Check if spacing values for this component have already been converted
                        if (lines[serializedVersionInsertionIndex].Contains("  m_SerializedVersion: 1"))
                        {
                            readingFlag = 0;
                            continue;
                        }

                        lines.Insert(serializedVersionInsertionIndex, "  m_SerializedVersion: 1");
                        readingFlag = 1;
                        continue;
                    }

                    // Keep reading until we find the font asset property field.
                    if (readingFlag == 1)
                    {
                        // Check for font asset property
                        if (line.Contains(k_FontAssetProperty))
                        {
                            int guidIndex = line.IndexOf("guid: ", StringComparison.InvariantCulture);
                            if (guidIndex != -1)
                            {
                                string        guid      = line.Substring(guidIndex + 6, 32);
                                TMP_FontAsset fontAsset = AssetDatabase.LoadAssetAtPath <TMP_FontAsset>(AssetDatabase.GUIDToAssetPath(guid));
                                if (fontAsset != null)
                                {
                                    samplingPointSize = fontAsset.faceInfo.pointSize;
                                    faceScale         = fontAsset.faceInfo.scale;
                                }
                            }

                            readingFlag = 2;
                            continue;
                        }
                    }

                    // Read font size property
                    if (readingFlag == 2)
                    {
                        if (line.Contains(k_FontSizeProperty))
                        {
                            fontSize    = float.Parse(line.Split(':')[1], NumberStyles.Float, CultureInfo.InvariantCulture);
                            readingFlag = 3;
                            continue;
                        }
                    }

                    // Check for the spacing properties that need to be converted
                    if (readingFlag == 3)
                    {
                        // Read character spacing
                        if (line.Contains(k_CharacterSpacingProperty))
                        {
                            characterSpacingValue = float.Parse(line.Split(':')[1], NumberStyles.Float, CultureInfo.InvariantCulture);
                            if (characterSpacingValue != 0)
                            {
                                // Convert character spacing value.
                                newCharacterSpacingValue = characterSpacingValue * faceScale / (samplingPointSize * 0.01f);
                                lines[i] = lines[i].Replace(k_CharacterSpacingProperty + characterSpacingValue, k_CharacterSpacingProperty + newCharacterSpacingValue);

                                hasDataFileChanged = true;
                            }
                            continue;
                        }

                        // Read word spacing
                        if (line.Contains(k_WordSpacingProperty))
                        {
                            // Get the character spacing value
                            wordSpacingValue = float.Parse(line.Split(':')[1], NumberStyles.Float, CultureInfo.InvariantCulture);
                            if (wordSpacingValue != 0)
                            {
                                // Convert character spacing value.
                                newWordSpacingValue = wordSpacingValue * faceScale / (samplingPointSize * 0.01f);
                                lines[i]            = lines[i].Replace(k_WordSpacingProperty + wordSpacingValue, k_WordSpacingProperty + newWordSpacingValue);

                                hasDataFileChanged = true;
                            }
                            continue;
                        }

                        // Read line spacing
                        if (line.Contains(k_LineSpacingProperty))
                        {
                            // Get the value of line spacing value
                            lineSpacingValue = float.Parse(line.Split(':')[1], NumberStyles.Float, CultureInfo.InvariantCulture);
                            if (lineSpacingValue != 0)
                            {
                                // Convert line spacing value.
                                newLineSpacingValue = lineSpacingValue / (fontSize * 0.01f) * fontSize / samplingPointSize * faceScale;
                                lines[i]            = lines[i].Replace(k_LineSpacingProperty + lineSpacingValue, k_LineSpacingProperty + newLineSpacingValue);

                                hasDataFileChanged = true;
                            }
                            continue;
                        }

                        // Read paragraph spacing
                        if (line.Contains(k_ParagraphSpacingProperty))
                        {
                            // Get the value of line spacing value
                            paragraphSpacingValue = float.Parse(line.Split(':')[1], NumberStyles.Float, CultureInfo.InvariantCulture);
                            if (paragraphSpacingValue != 0)
                            {
                                // Convert line spacing value.
                                newParagraphSpacingValue = paragraphSpacingValue / (fontSize * 0.01f) * fontSize / samplingPointSize * faceScale;
                                lines[i] = lines[i].Replace(k_ParagraphSpacingProperty + paragraphSpacingValue, k_ParagraphSpacingProperty + newParagraphSpacingValue);

                                hasDataFileChanged = true;
                            }

                            readingFlag = 4;
                            continue;
                        }
                    }

                    // Done reading text component serialized data.
                    if (readingFlag == 4 && line.Contains("---"))
                    {
                        readingFlag = 0;

                        string characterSpacingFormat = $"{(characterSpacingValue == 0 ? "                    " : $"{characterSpacingValue,10:F}{newCharacterSpacingValue,10:F}")}";
                        string wordSpacingFormat      = $"{(wordSpacingValue == 0 ? "                    " : $"{wordSpacingValue,10:F}{newWordSpacingValue,10:F}")}";
                        string lineSpacingFormat      = $"{(lineSpacingValue == 0 ? "                    " : $"{lineSpacingValue,10:F}{newLineSpacingValue,10:F}")}";
                        string paragraphSpacingFormat = $"{(paragraphSpacingValue == 0 ? "                    " : $"{paragraphSpacingValue,10:F}{newParagraphSpacingValue,10:F}")}";

                        if (characterSpacingValue != 0 || lineSpacingValue != 0)
                        {
                            m_ProjectScanResults += $"{fileRecord.assetFilePath,-100}" + characterSpacingFormat + wordSpacingFormat + lineSpacingFormat + paragraphSpacingFormat + "\n";
                        }

                        // Update asset data file
                        assetDataFile = string.Join("\n", lines);

                        newCharacterSpacingValue = 0;
                        newWordSpacingValue      = 0;
                        newLineSpacingValue      = 0;
                        newParagraphSpacingValue = 0;
                    }
                }
            }

            // Check if asset file is a font asset
            // if (assetDataFile.Contains(k_FontAssetScriptID))
            // {
            //     float samplingPointSize;
            //     float normalSpacing;
            //     float newNormalSpacing;
            //     float boldSpacing;
            //     float newBoldSpacing;
            // }

            if (hasDataFileChanged)
            {
                AssetModificationRecord modifiedAsset;
                modifiedAsset.assetFilePath = fileRecord.assetFilePath;
                modifiedAsset.assetDataFile = assetDataFile;

                m_ModifiedAssetList.Add(modifiedAsset);
            }
        }
        private IEnumerator ScanProjectFiles()
        {
            m_IsAlreadyScanningProject = true;
            string packageFullPath = EditorUtilities.TMP_EditorUtility.packageFullPath;

            // List containing assets that have been modified.
            m_ProjectScanResults = k_ProjectScanReportDefaultText;
            m_ModifiedAssetList.Clear();
            m_ProgressPercentage = 0;

            // Read Conversion Data from Json file.
            if (m_ConversionData == null)
            {
                m_ConversionData = JsonUtility.FromJson <AssetConversionData>(File.ReadAllText(packageFullPath + "/PackageConversionData.json"));
            }

            // Get list of GUIDs for assets that might contain references to previous GUIDs that require updating.
            string searchFolder = string.IsNullOrEmpty(m_ProjectFolderToScan) ? "Assets" : ("Assets/" + m_ProjectFolderToScan);

            string[] guids = AssetDatabase.FindAssets("t:Object", new string[] { searchFolder }).Distinct().ToArray();

            k_ProjectScanLabelPrefix   = "<b>Phase 1 - Filtering:</b> ";
            m_ScanningTotalFiles       = guids.Length;
            m_ScanningCurrentFileIndex = 0;

            List <AssetFileRecord> projectFilesToScan = new List <AssetFileRecord>();

            foreach (var guid in guids)
            {
                if (m_CancelScanProcess)
                {
                    break;
                }

                string assetFilePath = AssetDatabase.GUIDToAssetPath(guid);

                m_ScanningCurrentFileIndex += 1;
                m_ScanningCurrentFileName   = assetFilePath;
                m_ProgressPercentage        = (float)m_ScanningCurrentFileIndex / m_ScanningTotalFiles;

                // Filter out file types we have no interest in searching
                if (ShouldIgnoreFile(assetFilePath))
                {
                    continue;
                }

                string assetMetaFilePath = AssetDatabase.GetTextMetaFilePathFromAssetPath(assetFilePath);

                projectFilesToScan.Add(new AssetFileRecord(assetFilePath, assetMetaFilePath));

                yield return(null);
            }

            m_RemainingFilesToScan = m_ScanningTotalFiles = projectFilesToScan.Count;

            k_ProjectScanLabelPrefix = "<b>Phase 2 - Scanning:</b> ";

            for (int i = 0; i < m_ScanningTotalFiles; i++)
            {
                if (m_CancelScanProcess)
                {
                    break;
                }

                AssetFileRecord fileRecord = projectFilesToScan[i];

                ThreadPool.QueueUserWorkItem(Task =>
                {
                    ScanProjectFileAsync(fileRecord);

                    m_ScanningCurrentFileName = fileRecord.assetFilePath;

                    int completedScans = m_ScanningTotalFiles - Interlocked.Decrement(ref m_RemainingFilesToScan);

                    m_ScanningCurrentFileIndex = completedScans;
                    m_ProgressPercentage       = (float)completedScans / m_ScanningTotalFiles;
                });

                if (i % 64 == 0)
                {
                    yield return(new WaitForSeconds(2.0f));
                }
            }

            while (m_RemainingFilesToScan > 0 && !m_CancelScanProcess)
            {
                yield return(null);
            }

            m_IsAlreadyScanningProject = false;
            m_ScanningCurrentFileName  = string.Empty;
        }
        private IEnumerator ScanProjectFiles()
        {
            m_IsAlreadyScanningProject = true;
            string packageFullPath = EditorUtilities.TMP_EditorUtility.packageFullPath;

            // List containing assets that have been modified.
            m_ProjectScanResults = k_ProjectScanReportDefaultText;
            m_ModifiedAssetList.Clear();
            m_ProgressPercentage = 0;

            // Get list of GUIDs for assets that might contain references to previous GUIDs that require updating.
            string searchFolder = string.IsNullOrEmpty(m_ProjectFolderToScan) ? "Assets" : ("Assets/" + m_ProjectFolderToScan);

            string[] guids = AssetDatabase.FindAssets("t:Object", new string[] { searchFolder }).Distinct().ToArray();

            k_ProjectScanLabelPrefix   = "<b>Phase 1 - Filtering:</b> ";
            m_ScanningTotalFiles       = guids.Length;
            m_ScanningCurrentFileIndex = 0;

            List <AssetFileRecord> projectFilesToScan = new List <AssetFileRecord>();

            foreach (var guid in guids)
            {
                if (m_CancelScanProcess)
                {
                    break;
                }

                string assetFilePath = AssetDatabase.GUIDToAssetPath(guid);

                m_ScanningCurrentFileIndex += 1;
                m_ScanningCurrentFileName   = assetFilePath;
                m_ProgressPercentage        = (float)m_ScanningCurrentFileIndex / m_ScanningTotalFiles;

                string fileExtension = Path.GetExtension(assetFilePath);
                Type   fileType      = AssetDatabase.GetMainAssetTypeAtPath(assetFilePath);

                // Ignore all files other than Scenes and Prefabs.
                if ((fileType == typeof(SceneAsset) || (fileType == typeof(GameObject) && fileExtension.ToLower() == ".prefab")) == false)
                {
                    continue;
                }

                string assetMetaFilePath = AssetDatabase.GetTextMetaFilePathFromAssetPath(assetFilePath);

                projectFilesToScan.Add(new AssetFileRecord(assetFilePath, assetMetaFilePath));

                yield return(null);
            }

            m_ScanningTotalFiles = projectFilesToScan.Count;

            k_ProjectScanLabelPrefix   = "<b>Phase 2 - Scanning:</b> ";
            m_ScanningCurrentFileIndex = 0;

            for (int i = 0; i < m_ScanningTotalFiles; i++)
            {
                if (m_CancelScanProcess)
                {
                    break;
                }

                AssetFileRecord fileRecord = projectFilesToScan[i];

                ScanProjectFile(fileRecord);

                m_ScanningCurrentFileName = fileRecord.assetFilePath;

                m_ScanningCurrentFileIndex += 1;
                m_ProgressPercentage        = (float)m_ScanningCurrentFileIndex / m_ScanningTotalFiles;

                yield return(null);
            }

            m_IsAlreadyScanningProject = false;
            m_ScanningCurrentFileName  = string.Empty;
        }