FbxExporterRepairMissingScripts.cs 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using System.IO;
  5. namespace UnityEditor.Formats.Fbx.Exporter
  6. {
  7. internal class RepairMissingScripts
  8. {
  9. private const string ForumPackageGUID = "2d81c55c4d9d85146b1d2de96e084b63";
  10. private const string AssetStorePackageGUID = "628ffbda3fdf4df4588770785d91a698";
  11. private const string FbxPrefabDLLFileId = "69888640";
  12. private const string IdFormat = "{{fileID: {0}, guid: {1}, type:";
  13. private static List<string> s_searchIDsToReplace;
  14. private static List<string> SearchIDsToReplace
  15. {
  16. get
  17. {
  18. if (s_searchIDsToReplace == null || s_searchIDsToReplace.Count <= 0)
  19. {
  20. s_searchIDsToReplace = new List<string>() {
  21. string.Format(IdFormat, FbxPrefabDLLFileId, ForumPackageGUID),
  22. string.Format(IdFormat, FbxPrefabDLLFileId, AssetStorePackageGUID)
  23. };
  24. }
  25. return s_searchIDsToReplace;
  26. }
  27. }
  28. private string[] m_assetsToRepair;
  29. private string[] AssetsToRepair{
  30. get{
  31. if (m_assetsToRepair == null) {
  32. m_assetsToRepair = FindAssetsToRepair ();
  33. }
  34. return m_assetsToRepair;
  35. }
  36. }
  37. public static string SourceCodeSearchID
  38. {
  39. get
  40. {
  41. var fbxPrefabObj = AssetDatabase.LoadMainAssetAtPath(FindFbxPrefabAssetPath());
  42. string searchID = null;
  43. string guid;
  44. long fileId;
  45. if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier(fbxPrefabObj, out guid, out fileId))
  46. {
  47. searchID = string.Format(IdFormat, fileId, guid);
  48. }
  49. return searchID;
  50. }
  51. }
  52. #if COM_UNITY_FORMATS_FBX_AS_ASSET
  53. public const string FbxPrefabFile = "/UnityFbxPrefab.dll";
  54. #else
  55. public const string FbxPrefabFile = "Packages/com.unity.formats.fbx/Runtime/FbxPrefab.cs";
  56. #endif
  57. public static string FindFbxPrefabAssetPath()
  58. {
  59. #if COM_UNITY_FORMATS_FBX_AS_ASSET
  60. // Find guids that are scripts that look like FbxPrefab.
  61. // That catches FbxPrefabTest too, so we have to make sure.
  62. var allGuids = AssetDatabase.FindAssets("FbxPrefab t:MonoScript");
  63. string foundPath = "";
  64. foreach (var guid in allGuids) {
  65. var path = AssetDatabase.GUIDToAssetPath(guid);
  66. if (path.EndsWith(FbxPrefabFile)) {
  67. if (!string.IsNullOrEmpty(foundPath)) {
  68. // How did this happen? Anyway, just don't try.
  69. Debug.LogWarning(string.Format("{0} found in multiple places; did you forget to delete one of these?\n{1}\n{2}",
  70. FbxPrefabFile.Substring(1), foundPath, path));
  71. return "";
  72. }
  73. foundPath = path;
  74. }
  75. }
  76. if (string.IsNullOrEmpty(foundPath)) {
  77. Debug.LogWarning(string.Format("{0} not found; are you trying to uninstall {1}?", FbxPrefabFile.Substring(1), ModelExporter.PACKAGE_UI_NAME));
  78. }
  79. return foundPath;
  80. #else
  81. // In Unity 2018.1 and 2018.2.0b7, FindAssets can't find FbxPrefab.cs in a package.
  82. // So we hardcode the path.
  83. var path = FbxPrefabFile;
  84. if (System.IO.File.Exists(System.IO.Path.GetFullPath(path)))
  85. {
  86. return path;
  87. }
  88. else
  89. {
  90. Debug.LogWarningFormat("{0} not found; update FbxPrefabFile variable in FbxExporterRepairMissingScripts.cs to point to FbxPrefab.cs path.", FbxPrefabFile);
  91. return "";
  92. }
  93. #endif
  94. }
  95. public int AssetsToRepairCount
  96. {
  97. get
  98. {
  99. return AssetsToRepair.Length;
  100. }
  101. }
  102. public string[] GetAssetsToRepair(){
  103. return AssetsToRepair;
  104. }
  105. public static string[] FindAssetsToRepair()
  106. {
  107. // search project for assets containing old GUID
  108. // ignore if forced binary
  109. if (UnityEditor.EditorSettings.serializationMode == SerializationMode.ForceBinary) {
  110. return new string[]{};
  111. }
  112. // check all scenes and prefabs
  113. string[] searchFilePatterns = new string[]{ "*.prefab", "*.unity" };
  114. List<string> assetsToRepair = new List<string> ();
  115. foreach (string searchPattern in searchFilePatterns) {
  116. foreach (string file in Directory.GetFiles(Application.dataPath, searchPattern, SearchOption.AllDirectories)) {
  117. if (AssetNeedsRepair (file)) {
  118. assetsToRepair.Add (file);
  119. }
  120. }
  121. }
  122. return assetsToRepair.ToArray ();
  123. }
  124. private static bool AssetNeedsRepair(string filePath)
  125. {
  126. try{
  127. using(var sr = new StreamReader (filePath)){
  128. if(sr.Peek() > -1){
  129. var firstLine = sr.ReadLine();
  130. if(!firstLine.StartsWith("%YAML")){
  131. return false;
  132. }
  133. }
  134. var contents = sr.ReadToEnd();
  135. if (SearchIDsToReplace.Exists(searchId => contents.Contains(searchId)))
  136. {
  137. return true;
  138. }
  139. }
  140. }
  141. catch(IOException e){
  142. Debug.LogError (string.Format ("Failed to check file for component update: {0} (error={1})", filePath, e));
  143. }
  144. return false;
  145. }
  146. public bool ReplaceGUIDInTextAssets ()
  147. {
  148. string sourceCodeSearchID = SourceCodeSearchID;
  149. if(string.IsNullOrEmpty(sourceCodeSearchID))
  150. {
  151. return false;
  152. }
  153. bool replacedGUID = false;
  154. foreach (string file in AssetsToRepair) {
  155. replacedGUID |= ReplaceGUIDInFile (file, sourceCodeSearchID);
  156. }
  157. if (replacedGUID) {
  158. AssetDatabase.Refresh ();
  159. }
  160. return replacedGUID;
  161. }
  162. private static bool ReplaceID(string searchId, string replacementId, ref string line)
  163. {
  164. if (line.Contains(searchId))
  165. {
  166. line = line.Replace(searchId, replacementId);
  167. return true;
  168. }
  169. return false;
  170. }
  171. private static bool ReplaceGUIDInFile (string path, string replacementSearchID)
  172. {
  173. // try to read file, assume it's a text file for now
  174. bool modified = false;
  175. try {
  176. var tmpFile = Path.GetTempFileName();
  177. if(string.IsNullOrEmpty(tmpFile)){
  178. return false;
  179. }
  180. using(var sr = new StreamReader (path)){
  181. // verify that this is a text file
  182. var firstLine = "";
  183. if (sr.Peek () > -1) {
  184. firstLine = sr.ReadLine ();
  185. if (!firstLine.StartsWith ("%YAML")) {
  186. return false;
  187. }
  188. }
  189. using(var sw = new StreamWriter (tmpFile, false)){
  190. if (!string.IsNullOrEmpty (firstLine)) {
  191. sw.WriteLine (firstLine);
  192. }
  193. while (sr.Peek () > -1) {
  194. var line = sr.ReadLine ();
  195. SearchIDsToReplace.ForEach(searchId =>
  196. modified |= ReplaceID(searchId, replacementSearchID, ref line)
  197. );
  198. sw.WriteLine (line);
  199. }
  200. }
  201. }
  202. if (modified) {
  203. File.Delete (path);
  204. File.Move (tmpFile, path);
  205. Debug.LogFormat("Updated FbxPrefab components in file {0}", path);
  206. return true;
  207. } else {
  208. File.Delete (tmpFile);
  209. }
  210. } catch (IOException e) {
  211. Debug.LogError (string.Format ("Failed to replace GUID in file {0} (error={1})", path, e));
  212. }
  213. return false;
  214. }
  215. }
  216. }