using UnityEditor; using UnityEngine; using System; using System.Collections.Generic; using System.Linq; using System.Text; using Perforce.P4; using System.Runtime.InteropServices; using System.Reflection; using System.IO; using log4net; namespace P4Connect { /// <summary> /// Utility methods for P4Connect /// </summary> public class Utils { static bool caseKnown = false; static bool caseSensitive = false; private static readonly ILog log = LogManager.GetLogger(typeof(Utils)); /// <summary> /// Returns true if running on a case insensitive file system. /// </summary> public static bool IsCaseSensitive() { // Return cached results if (caseKnown) return caseSensitive; // The assets directory should always exist in a project string path = Main.DataPath; bool u = System.IO.Directory.Exists(path.ToUpper()); bool l = System.IO.Directory.Exists(path.ToLower()); if (u ^ l) return true; // default to true for a new project, but don't cache the result. if (u && l){ caseSensitive = false; } else { caseSensitive = true; } caseKnown = true; return caseSensitive; } public static bool IsRunningUnity5() { if (Application.unityVersion.Trim()[0] == '5') return true; return false; } /// <summary> /// Retrieves a displayable filename from a perforce file spec /// </summary> public static string GetFilename(FileAndMeta aFileAndMeta) { return GetFilename(aFileAndMeta, Config.ShowPaths); } /// <summary> /// Retrieves a displayable filename from a perforce file spec /// </summary> public static string GetFilename(FileAndMeta aFileAndMeta, bool aShowPath) { // Base version uses whatever prefs the user set if (aFileAndMeta.File != null) return GetFilename(aFileAndMeta.File, aShowPath); else if (aFileAndMeta.Meta != null) return GetFilename(aFileAndMeta.Meta, aShowPath); else return ""; } /// <summary> /// Retrieves a displayable filename from a perforce file spec /// </summary> public static string GetFilename(Perforce.P4.FileSpec aFile) { // Base version uses whatever prefs the user set return GetFilename(aFile, Config.ShowPaths); } /// <summary> /// Retrieves a displayable filename from a perforce file spec /// </summary> public static string GetFilename(Perforce.P4.FileSpec aFile, bool aShowPath) { string filename = ""; if (aFile.ClientPath != null) filename = aFile.ClientPath.Path; else if (aFile.DepotPath != null) filename = aFile.DepotPath.Path; else filename = aFile.LocalPath.Path; if (!filename.EndsWith("...") && !aShowPath) { // parse out the file name filename = Path.GetFileName(filename); } return UnescapeFilename(filename); } /// <summary> /// Separates the files based on their extension /// </summary> public static void FilterTextAndBinary(List<FileSpec> aFiles, List<FileSpec> aOutText, List<FileSpec> aOutBinary, List<FileSpec> aOutUnknown) { foreach (var file in aFiles) { switch (FilterTextAndBinary(file)) { case FilterTextAndBinaryResult.Binary: aOutBinary.Add(file); break; case FilterTextAndBinaryResult.Text: aOutText.Add(file); break; case FilterTextAndBinaryResult.Unknown: aOutUnknown.Add(file); break; } } } public enum FilterTextAndBinaryResult { Text, Binary, Unknown } /// <summary> /// Separates the files based on their extension /// </summary> public static FilterTextAndBinaryResult FilterTextAndBinary(FileSpec aFile) { bool defaultToBinary = false; if (EditorSettings.serializationMode == SerializationMode.ForceBinary || EditorSettings.serializationMode == SerializationMode.Mixed) { defaultToBinary = true; } string extension = System.IO.Path.GetExtension(GetFilename(aFile, false)).ToLowerInvariant(); // Certain files are forced as text! if (extension == ".meta" || extension == ".txt" || extension == ".html" || extension == ".htm" || extension == ".xml" || extension == ".bytes" || extension == ".json" || extension == ".csv" || extension == ".yaml" || extension == ".fnt" || extension == ".js" || extension == ".cs" || extension == ".sln" || extension == ".csproj" || extension == ".compute" || extension == ".unityproj") { return FilterTextAndBinaryResult.Text; } // Others depend on the serialization setting else if (extension == ".scene" || extension == ".prefab" || extension == ".mat" || extension == ".cubemap" || extension == ".flare" || extension == ".rendertexture" || extension == ".controller" || extension == ".anim" || extension == ".overridecontroller" || extension == ".mask" || extension == ".physicmaterial" || extension == ".physicmaterial2d" || extension == ".guiskin" || extension == ".fontsettings") { if (defaultToBinary) { return FilterTextAndBinaryResult.Binary; } else { return FilterTextAndBinaryResult.Text; } } // And the rest, we leave up to perforce to determine. else { return FilterTextAndBinaryResult.Unknown; } } /// <summary> /// Escape filenames for perforce /// </summary> public static string EscapeFilename(string aFilename) { return aFilename.Replace("%", "%25").Replace("@", "%40").Replace("#", "%23").Replace("*", "%2A"); } /// <summary> /// Unescape filenames for perforce /// </summary> public static string UnescapeFilename(string aFilename) { return aFilename.Replace("%25", "%").Replace("%40", "@").Replace("%23", "#").Replace("%2A", "*"); } public static void CheckForMissingFilesAndWarn(IList<FileSpec> aSubmitted, IList<FileSpec> aRetrieved, string aErrorMessage) { #if DEBUG log.Debug("CheckForMissingFilesAndWarn"); log.Debug(aSubmitted); log.Debug(aRetrieved); log.Debug("ErrorMessage: " + aErrorMessage); #endif if (aSubmitted != null && aSubmitted.Count > 0) { // Make a copy of the submitted list List<FileSpec> remainder = new List<FileSpec>(aSubmitted); // Remove special entries remainder.RemoveAll(spec => spec == null || (spec.ClientPath != null && spec.ClientPath.Path != null && spec.ClientPath.Path.EndsWith("..."))); // Remove all the files we did get back if (aRetrieved != null) { foreach (var spec in aRetrieved) { for (int i = 0; i < remainder.Count; ++i) { // Submitted is Asset Path // Retrieved is Depot Paths #if DEBUG log.Debug("\ttry this: " + LocalPathToRelativePath(remainder[i].LocalPath.Path)); #endif if (spec.LocalPath.Path.EndsWith(LocalPathToRelativePath(remainder[i].LocalPath.Path), !IsCaseSensitive(), null)) { remainder.RemoveAt(i); } } } } if (remainder.Count > 0) { StringBuilder builder = new StringBuilder(); builder.AppendLine(aErrorMessage); foreach (var spec in remainder) { if (spec != null) { builder.AppendLine("\t" + GetFilename(spec, true)); } } string errorMsg = builder.ToString(); #if DEBUG log.Debug("\tP4Connect - " + errorMsg); #endif EditorUtility.DisplayDialog("P4Connect - Error", errorMsg, "Ok"); } } } #region LogFiles /// <summary> /// Logs a message to the console, formatting the filename properly /// </summary> public static void LogFile(FileAndMeta aFileAndMeta, string aMsg) { if (aFileAndMeta.File != null) { string filename = GetFilename(aFileAndMeta.File); if (aFileAndMeta.Meta != null) filename += " (and meta)"; Debug.Log("P4Connect - " + string.Format(aMsg, filename)); } else { if (aFileAndMeta.Meta != null) { string filename = GetFilename(aFileAndMeta.Meta); Debug.Log("P4Connect - " + string.Format(aMsg, filename)); } else { Debug.LogError("null file AND meta passed in to LogFile()"); } } } /// <summary> /// Logs a message to the console, formatting the filename properly /// </summary> public static void LogFile(Perforce.P4.FileSpec aFile, string aMsg) { LogFile(new FileAndMeta(aFile, null), aMsg); } /// <summary> /// Logs a message to the console, formatting the filename properly /// </summary> public static void LogFiles(IList<Perforce.P4.FileSpec> aFiles, string aMsg) { if (aFiles != null) { List<FileAndMeta> fileAndMetas = GetFileAndMetas(aFiles); LogFiles(fileAndMetas, aMsg); } } /// <summary> /// Logs a message to the console, formatting the filename properly /// </summary> public static void LogFiles(IList<FileAndMeta> aFilesAndMetas, string aMsg) { if (aFilesAndMetas != null && aFilesAndMetas.Count > 0) { if (aFilesAndMetas.Count == 1) { LogFile(aFilesAndMetas[0], aMsg); } else { StringBuilder builder = new StringBuilder(); builder.Append(aFilesAndMetas.Count.ToString()); builder.Append(" assets.\n"); foreach (var fileAndMeta in aFilesAndMetas) { if (fileAndMeta.File != null) { string filename = GetFilename(fileAndMeta.File, true); if (fileAndMeta.Meta != null) filename += " (and meta)"; builder.Append(filename); builder.Append("\n"); } else { if (fileAndMeta.Meta != null) { string filename = GetFilename(fileAndMeta.Meta, true); builder.Append(filename); builder.Append("\n"); } else { Debug.LogError("null file AND meta passed in to LogFile()"); } } } builder.Remove(builder.Length - 1, 1); Debug.Log("P4Connect - " + string.Format(aMsg, builder.ToString())); } } } /// <summary> /// Logs a message to the console, formatting the filename properly /// </summary> public static void LogFile(FileAndMeta aFileAndMeta, FileAndMeta aMoveToFileAndMeta, string aMsg, bool aShowPath) { if (aFileAndMeta.File != null && aMoveToFileAndMeta.File != null) { string filename = GetFilename(aFileAndMeta.File, aShowPath); string moveToFilename = GetFilename(aMoveToFileAndMeta.File, aShowPath); if (aFileAndMeta.Meta != null) filename += " (and meta)"; Debug.Log("P4Connect - " + string.Format(aMsg, filename, moveToFilename)); } else { if (aFileAndMeta.Meta != null && aMoveToFileAndMeta.Meta != null) { string filename = GetFilename(aFileAndMeta.Meta, aShowPath); string moveToFilename = GetFilename(aMoveToFileAndMeta.Meta, aShowPath); Debug.Log("P4Connect - " + string.Format(aMsg, filename, moveToFilename)); } else { Debug.LogError("null file AND meta passed in to LogFile()"); } } } /// <summary> /// Logs a message to the console, formatting the filename properly /// </summary> public static void LogFiles(IList<FileAndMeta> aFilesAndMetas, IList<FileAndMeta> aMoveToFilesAndMetas, string aMsg) { if (aFilesAndMetas != null && aMoveToFilesAndMetas != null && aFilesAndMetas.Count > 0 && aMoveToFilesAndMetas.Count > 0) { if (aFilesAndMetas.Count == 1 && aMoveToFilesAndMetas.Count == 1) { LogFile(aFilesAndMetas[0], aMoveToFilesAndMetas[0], aMsg); } else { StringBuilder builderFrom = new StringBuilder(); foreach (var fileAndMeta in aFilesAndMetas) { if (fileAndMeta.File != null) { string filename = GetFilename(fileAndMeta.File, true); if (fileAndMeta.Meta != null) filename += " (and meta)"; builderFrom.Append(filename); builderFrom.Append("\n"); } else { if (fileAndMeta.Meta != null) { string filename = GetFilename(fileAndMeta.Meta, true); builderFrom.Append(filename); builderFrom.Append("\n"); } else { Debug.LogError("null file AND meta passed in to LogFile()"); } } } builderFrom.Remove(builderFrom.Length - 1, 1); Debug.Log("P4Connect - " + string.Format(aMsg, aFilesAndMetas.Count.ToString(), "a new location") + "\n" + builderFrom.ToString()); } } } /// <summary> /// Logs a message to the console, formatting the filename properly /// </summary> public static void LogFiles(IList<FileSpec> aFiles, IList<FileAndMeta> aMoveToFilesAndMetas, string aMsg) { if (aFiles != null && aMoveToFilesAndMetas != null) { List<FileAndMeta> fileAndMetas = GetFileAndMetas(aFiles); LogFiles(fileAndMetas, aMoveToFilesAndMetas, aMsg); } } /// <summary> /// Logs a message to the console, formatting the filename properly /// </summary> public static void LogFile(FileAndMeta aFileAndMeta, FileAndMeta aMoveToFileAndMeta, string aMsg) { LogFile(aFileAndMeta, aMoveToFileAndMeta, aMsg, Config.ShowPaths); } /// <summary> /// Logs a message to the console, formatting the filename properly /// </summary> public static void LogFile(Perforce.P4.FileSpec aFile, Perforce.P4.FileSpec aMoveToFile, string aMsg, bool aShowPath) { LogFile(new FileAndMeta(aFile, null), new FileAndMeta(aMoveToFile, null), aMsg, aShowPath); } /// <summary> /// Logs a message to the console, formatting the filename properly /// </summary> public static void LogFile(Perforce.P4.FileSpec aFile, Perforce.P4.FileSpec aMoveToFile, string aMsg) { LogFile(aFile, aMoveToFile, aMsg, Config.ShowPaths); } /// <summary> /// Logs a warning to the console, formatting the filename properly /// </summary> public static void LogFileWarning(Perforce.P4.FileSpec aFile, string aMsg) { Debug.LogWarning("P4Connect - " + GetFilename(aFile) + aMsg); } #endregion /// <summary> /// Checks whether the filepath can be used by perforce /// </summary> public static bool IsFilePathValid(string aFilePath) { bool ret = true; char[] forbiddenChars = new char[] { '@', '#', '*', '%' }; for (int i = 0; ret && i < forbiddenChars.Length; ++i) { if (aFilePath.Contains(forbiddenChars[i])) { ret = false; } } return ret; } public static string ClientPathToLocalPath(string aClientPath) { string ClientPrefix = "//" + Config.Workspace; return aClientPath.Substring(ClientPrefix.Length + 1).Replace('/', System.IO.Path.DirectorySeparatorChar); } public static string ClientPathToFullPath(string aClientPath) { return LocalPathToFullPath(ClientPathToLocalPath(aClientPath)); } public static string DepotPathToLocalPath(PerforceConnection aConnection, FileSpec aDepotPath) { IList<FileSpec> mapping = aConnection.P4Client.GetClientFileMappings(null, aDepotPath); if (mapping != null && mapping.Count > 0) { return ClientPathToLocalPath(mapping[0].ClientPath.Path); } else { return ""; } } public static string DepotPathToFullPath(PerforceConnection aConnection, FileSpec aDepotPath) { IList<FileSpec> mapping = aConnection.P4Client.GetClientFileMappings(null, aDepotPath); if (mapping != null && mapping.Count > 0) { return ClientPathToFullPath(mapping[0].ClientPath.Path); } else { return ""; } } public static string LocalPathToRelativePath(string aLocalPath) { string result = aLocalPath; string project = Main.RootPath; if (aLocalPath.StartsWith(project, !IsCaseSensitive(), null)) { if (aLocalPath.Length > project.Length) { result = aLocalPath.Substring(project.Length + 1); } } Debug.Log("LocalPathToRelativePath returns " + result); return result; } public static string AssetPathToLocalPath(string aAssetPath) { return System.IO.Path.Combine(Main.RootPath, aAssetPath).Replace('/', Path.DirectorySeparatorChar); } public static string LocalPathToAssetPath(string aLocalPath) { if (aLocalPath.StartsWith(Main.RootPath, !IsCaseSensitive(), null)) { var cleanedRoot = Main.RootPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); if (aLocalPath.Length > cleanedRoot.Length + 1) { return aLocalPath.Substring(cleanedRoot.Length + 1).Replace(Path.DirectorySeparatorChar, '/'); } else { return aLocalPath.Replace(Path.DirectorySeparatorChar, '/'); } } else { return aLocalPath.Replace(Path.DirectorySeparatorChar, '/'); } } public static string LocalPathToClientPath(string aLocalPath) { string ClientPrefix = "//" + Config.Workspace; return ClientPrefix + "/" + aLocalPath.Replace(Path.DirectorySeparatorChar, '/'); } public static bool IsDirectory(string aAssetPath) { if (aAssetPath.EndsWith("...")) return true; else { string fullPath = LocalPathToFullPath(aAssetPath); if (Directory.Exists(fullPath + Path.DirectorySeparatorChar)) return true; else if (Path.GetExtension(aAssetPath) == string.Empty) return true; else return false; } } public static string MetaFromAsset(string aAssetPath) { return aAssetPath + ".meta"; } public static string AssetFromMeta(string aMetaPath) { return aMetaPath.Substring(0, aMetaPath.Length - 5); } public static string AssetPathToFullPath(string aAssetPath) { return Path.Combine(Main.RootPath, aAssetPath.Replace('/', Path.DirectorySeparatorChar)); } public static string LocalPathToFullPath(string aLocalPath) { return AssetPathToFullPath(LocalPathToAssetPath(aLocalPath)); } public static string FullPathToAssetPath(string aFullPath) { return aFullPath.Substring(Main.RootPath.Length).Replace(System.IO.Path.DirectorySeparatorChar, '/'); } public static void GetFileAndMeta(string aName, out string aFileName, out string aMetaName) { // Is this a .meta file? if so we need to fetch the original file name from it if (aName.EndsWith(".meta")) { aMetaName = Utils.AssetPathToLocalPath(aName); aFileName = aMetaName.Substring(0, aMetaName.Length - 5); } // If it's not a meta file, generate the meta file name from it else { aFileName = Utils.AssetPathToLocalPath(aName); aMetaName = aFileName + ".meta"; } } public static string GetFileStateString(FileState aState) { string ret = "<unknown>"; switch (aState) { case FileState.None: ret = "<unknown>"; break; case FileState.InDepot: ret = "None"; break; case FileState.MarkedForEdit: ret = "Edit"; break; case FileState.MarkedForAdd: ret = "Add"; break; case FileState.MarkedForDelete: ret = "Delete"; break; case FileState.MarkedForAddMove: ret = "Move/Add"; break; case FileState.MarkedForDeleteMove: ret = "Move/Delete"; break; } return ret; } public static string GetStorageTypeString(StorageType aType) { string ret = "Other"; switch (aType) { case StorageType.Text: ret = "Text"; break; case StorageType.Binary: ret = "Binary"; break; } return ret; } public static List<string> GetDeepSelectedEffectivePaths() { var objects = Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.Assets); List<string> assets = new List<string>(); foreach (var obj in objects) { string assetPath = AssetDatabase.GetAssetPath(obj); assets.Add(assetPath); } return assets; } public static void LaunchDiffAgainstHaveRev(string aAsset) { if (System.IO.File.Exists(Config.DiffToolPathname)) { Engine.PerformConnectionOperation(con => { FileSpec localSpec = FileSpec.LocalSpec(Utils.AssetPathToLocalPath(aAsset)); IList<FileMetaData> metaDataList = Engine.GetFileMetaData(con, null, localSpec); if (metaDataList != null && metaDataList.Count == 1) { BaseFileType baseFileType = metaDataList[0].Type.BaseType; if ((baseFileType == BaseFileType.Text) || (baseFileType == BaseFileType.Unicode) || (baseFileType == BaseFileType.UTF16)) { if (metaDataList[0].HaveRev != -1) { FileSpec headRevSpec = FileSpec.LocalSpec(Utils.AssetPathToLocalPath(aAsset), metaDataList[0].HaveRev); IList<string> contents = con.P4Depot.GetFileContents(null, headRevSpec); if (contents != null && contents.Count == 2) { // Create a temp file ad dump the content in there string haveRevPathname = System.IO.Path.Combine(System.IO.Path.GetTempPath(), System.IO.Path.GetFileName(headRevSpec.ToString())); System.IO.StreamWriter tempFile = System.IO.File.CreateText(haveRevPathname); tempFile.Write(contents[1]); tempFile.Close(); // Fetch the full pathname of the original file string localPathname = Utils.AssetPathToFullPath(aAsset); // Now run the diff tool string arguments = haveRevPathname + " " + localPathname; System.Diagnostics.Process.Start(Config.DiffToolPathname, arguments); } } else { EditorUtility.DisplayDialog("File not Synced", "You have never synced this file before and cannot compare it against your HAVE revision", "Ok"); } } } }); } else { EditorUtility.DisplayDialog("No Diff Utility Set", "You must point P4Connect to your favorite Diff Utility in Edit->Perforce Settings to use this feature.", "Ok"); } } public static bool MakeWriteable(string filename) { FileInfo fi = new FileInfo(filename); if (fi.Exists) { if (fi.IsReadOnly) { Debug.Log("MakeWriteable: " + filename); fi.IsReadOnly = false; // Make the file writeable anyway to avoid problems. return true; } } return false; } public static string GetBridgeDirectory() { return System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + System.IO.Path.DirectorySeparatorChar + (IntPtr.Size == 4 ? "x86" : "x86_64" ) + System.IO.Path.DirectorySeparatorChar; } public static string GetEditorAssetFullDirectory() { return System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + System.IO.Path.DirectorySeparatorChar; } static string GetFileSpecPath(FileSpec aSpec) { string filePath = ""; if (aSpec.LocalPath != null) filePath = aSpec.LocalPath.Path; else if (aSpec.ClientPath != null) filePath = aSpec.ClientPath.Path; else if (aSpec.DepotPath != null) filePath = aSpec.DepotPath.Path; return filePath; } /// <summary> /// Attempts to group files with their metas from a list of file specs /// </summary> public static List<FileAndMeta> GetFileAndMetas(IList<FileSpec> aFiles) { #if DEBUG log.DebugFormat("files: {0}", Logger.FileSpecListToString(aFiles)); #endif List<Perforce.P4.FileSpec> files = new List<Perforce.P4.FileSpec>(aFiles); List<FileAndMeta> fileAndMetas = new List<FileAndMeta>(); // Parse the list, seeing if files have metas and such while (files.Count > 0) { string filePath = GetFileSpecPath(files[0]); FileAndMeta assetAndMeta = new FileAndMeta(); if (filePath.EndsWith(".meta")) { // It's a meta file, is there an asset in the list as well? assetAndMeta.Meta = files[0]; string assetPath = Utils.AssetFromMeta(filePath); int assetIndex = files.FindIndex(fs => String.Compare(assetPath, GetFileSpecPath(fs), StringComparison.InvariantCultureIgnoreCase) == 0); if (assetIndex != -1) { assetAndMeta.File = files[assetIndex]; files.RemoveAt(assetIndex); } } else { // It's not a meta file, is there a meta as well? assetAndMeta.File = files[0]; string metaPath = Utils.MetaFromAsset(filePath); int metaIndex = files.FindIndex(fs => String.Compare(metaPath, GetFileSpecPath(fs), StringComparison.InvariantCultureIgnoreCase) == 0); if (metaIndex != -1) { assetAndMeta.Meta = files[metaIndex]; files.RemoveAt(metaIndex); } } // Finally, add the asset and meta to the list fileAndMetas.Add(assetAndMeta); // No matter what, remove the file files.RemoveAt(0); } #if DEBUG log.DebugFormat("returns: {0}", Logger.FileAndMetaListToString(fileAndMetas)); #endif return fileAndMetas; } /// <summary> /// Matches up a list of meta data to the list of files it was generated from /// </summary> public static void GetMatchingMetaData(List<FileSpec> aFiles, IList<FileMetaData> aMetaData, List<FileMetaData> aOutMeta) { Dictionary<string, FileMetaData> metaDataPaths = new Dictionary<string, FileMetaData>(); if (aMetaData != null) { foreach (var meta in aMetaData) { if (meta != null) { metaDataPaths[meta.LocalPath.Path.ToLowerInvariant()] = meta; } } } foreach (var file in aFiles) { if (file != null) { string lcPath = Utils.LocalPathToFullPath(file.LocalPath.Path).ToLowerInvariant(); FileMetaData meta = null; metaDataPaths.TryGetValue(lcPath, out meta); aOutMeta.Add(meta); } else { aOutMeta.Add(null); } } } public static bool IsSolutionProjectFile(string aPath) { return aPath.EndsWith(".csproj") || aPath.EndsWith(".sln"); } public static bool IsDirOrValidSubDirectoryOf(string aSubPath, string aAncestorPath) { System.IO.DirectoryInfo subDir = new System.IO.DirectoryInfo(aSubPath.TrimEnd(System.IO.Path.DirectorySeparatorChar, '/')); System.IO.DirectoryInfo ancestorDir = new System.IO.DirectoryInfo(aAncestorPath.TrimEnd(System.IO.Path.DirectorySeparatorChar, '/')); bool isParent = false; while (subDir != null) { if (string.Compare(subDir.FullName, ancestorDir.FullName, StringComparison.InvariantCultureIgnoreCase) == 0) { isParent = true; break; } else subDir = subDir.Parent; } return isParent; } } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#21 | 16251 | Norman Morse | Update Dev branch to match reorganization in workshop | ||
#20 | 15075 | Norman Morse |
Rewrote AssetStatusCache to Cache AssetStatuses and FileMetaData Fixed Edge conditions on Engine Operations Change Debug output defaults. Will now Checkout files which request to be "added" but which already exist in perforce. Output P4Connect version to log on initialization. |
||
#19 | 14800 | Norman Morse |
Fixed Exception in debug statements Improved performance of pending assets menu on large change sets, some other minor premature optimizations Allowed saving of configuration while Perforce is disabled. Improved restoring connection after recompile. |
||
#18 | 14236 | Norman Morse | update dev branch with GA.8 release files | ||
#17 | 13872 | Norman Morse | Update dev branch from GA.5 release | ||
#16 | 12558 | Norman Morse | Integrate from main to dev | ||
#15 | 12480 | Norman Morse |
Fixed crash in logging. Worked on UI issues. Cleaned up some connection usage. Still looking for the problem with pending changes. |
||
#14 | 12473 | Norman Morse | More minor fixes to logging and config | ||
#13 | 12467 | Norman Morse |
Many minor changes to improve logging Added a filter for Console log display where you can select a level Hooked the log file location to the Configuration Log Entry. |
||
#12 | 12445 | Norman Morse |
Integrated log4net and nunit into P4Connect. Still need cleanup and debugging, good enough for dev tree Also added ChangeManager and ChangeLists Classes for future use with multiple changes. |
||
#11 | 12363 | Norman Morse |
Merging //guest/perforce_software/p4connect/src/... to //guest/norman_morse/dev/p4connect/src/... |
||
#10 | 12131 | Norman Morse |
Unity 5 fixes for icons and loading the DLL. Needed to use reflection to load the icons. |
||
#9 | 12101 | Norman Morse |
Additional movement towards Unity 5. Still gettting DllNotFound exceptions in 5 |
||
#8 | 12099 | Norman Morse |
Various changes to prepare for Beta Fixed VersionInfo to show changeID Added "additional ignore in config". Updated ReleaseNotes |
||
#7 | 12087 | Norman Morse |
Another Day, another dollar Fixed some bad __except clauses for OSX. Debugging ignore stuff passing pwd in |
||
#6 | 12064 | Norman Morse | Simplify Bridge Directory Utilities | ||
#5 | 11827 | Norman Morse | Fixes to build an DLL locating routines | ||
#4 | 11821 | Norman Morse | Move Unity 5 compatible build from internal dev branch to workshop dev branch. | ||
#3 | 11448 | Norman Morse | copied source from main to dev branch | ||
#2 | 11320 | Norman Morse | Added MakeWriteable() method to utils | ||
#1 | 10941 | Norman Morse | Create dev branch from workshop | ||
//guest/perforce_software/p4connect/src/P4Connect/P4Connect/P4Connect.Utils.cs | |||||
#1 | 10940 | Norman Morse |
Inital Workshop release of P4Connect. Released under BSD-2 license |