/*---------------------------------------------------------------------------* Copyright 2010-2012 Nintendo. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. *---------------------------------------------------------------------------*/ using System; using System.IO; using System.Diagnostics; using System.Xml; using System.Text; using System.Collections.Generic; namespace CafeX { /// /// This class encapsulates the environment variables required by Cafe /// internal class EnvVar { private string var_name; private string var_value; private static Dictionary dictionaryOfVariables = new Dictionary(); private static Dictionary dictionaryOfSavedValues = new Dictionary(); public static void BackUpCurrentEnvironment() { #if DEBUG Log.WriteLine("BackUpCurrentEnvironment started."); #endif dictionaryOfSavedValues.Clear(); foreach (string variableName in dictionaryOfVariables.Keys) { dictionaryOfSavedValues.Add(variableName, dictionaryOfVariables[variableName].value); } #if DEBUG Log.WriteLine("BackUpCurrentEnvironment completed."); #endif } public static void RestoreSavedEnvironment() { #if DEBUG Log.WriteLine("RestoreSavedEnvironment started."); #endif if (dictionaryOfSavedValues.Count == 0) { #if DEBUG Log.WriteLine("RestoreSavedEnvironment ignored. No backup available."); #endif // No backup had been made, so there is no way to restore. Just return. return; } foreach (string variableName in dictionaryOfVariables.Keys) { // Set the respective envVar instance in the dictionary of variables. if (dictionaryOfSavedValues.ContainsKey(variableName)) { EnvVar temp = dictionaryOfVariables[variableName]; temp.value = dictionaryOfSavedValues[variableName]; } } #if DEBUG Log.WriteLine("RestoreSavedEnvironment completed successfully."); #endif } public EnvVar(string name) { if (string.IsNullOrEmpty(name)) { throw new ArgumentNullException("name cannot be null."); } var_name = name; var_value = Environment.GetEnvironmentVariable(var_name); // Store the environment variable, so it can be backed-up and restored at will EnvVar.dictionaryOfVariables.Add(name, this); } public string value { get { var_value = Environment.GetEnvironmentVariable(var_name); return var_value; } set { var_value = value; Environment.SetEnvironmentVariable(var_name, var_value); } } public void AddToVar(string addition, char delimiter) { if (value == null) value = addition; else value = value + delimiter + addition; } public void RemoveFromVar(string removal, char delimiter) { if (string.IsNullOrEmpty(value)) { #if DEBUG Log.WriteLine("RemoveFromVar: removal is empty!"); #endif return; } if (string.IsNullOrEmpty(delimiter.ToString())) { #if DEBUG Log.WriteLine("RemoveFromVar: delimiter is null!"); #endif return; } int position = value.IndexOf(removal); if (position >= 0) { int len = removal.Length; if (position > 0) { // There is more text in value than only the one we want to remove. // ex: data1 data2 removal data3 // ex: data1 removal. // In those scenarios, we need to remove the delimiter char before, since AddToVar does value = value + delimiter + addition; --position; ++len; } value = value.Remove(position, len); } else { #if DEBUG Log.WriteLine("RemoveFromVar: \"" + removal + "\" does not exist in the arg list!"); #endif return; } } } /// /// This class provides enables CafeX to work in both Windows and Cygwin environments by converting paths to the Windows format. /// internal static class PathConverter { /// /// Ensure the provide path is rooted. /// /// Path to a file. /// Rooted path. internal static string Root(string input) { if (Path.IsPathRooted(input)) { return input; } else { return Environment.CurrentDirectory + "\\" + input; } } /// /// Convert a Cygwin path to a Windows path. /// If the input is already a valid Windows path, it is returned unchanged. /// /// Cywin path. /// Windows equivalent of the provided Cygwin path. internal static string Windowsify(string input) { #if DEBUG Log.WriteLine("Windowsify input = " + input); #endif if (string.IsNullOrEmpty(input)) { return input; } int cygdriverIndex = -1; cygdriverIndex = input.IndexOf(@"/cygdrive///"); if ((cygdriverIndex == 0 || cygdriverIndex == 1 ) && !string.IsNullOrEmpty(CafeX.Program.CYGWIN_PATH.value) ) { // Special treatment input = input.Replace( @"/cygdrive///", Path.Combine(CafeX.Program.CYGWIN_PATH.value, "cygdrive/") ); // it should continue with normal conversion to fix the \\ } // Normal conversion -> path starts with /cygdriver/ or "/cygdriver/" cygdriverIndex = input.IndexOf("/cygdrive/"); if (cygdriverIndex == 0 || cygdriverIndex == 1) { char drive_letter = input.ToCharArray()[10 + cygdriverIndex]; input = input.Remove(0, 11 + cygdriverIndex); input = input.Replace('/', '\\'); input = drive_letter + ":" + input; } else { // This should be a path that doesn't start with cygdrive, // so just make sure it has the proper \\ instead of / input = input.Replace('/', '\\'); } #if DEBUG Log.WriteLine("Windowsify return = " + input); #endif return input; } } /// /// This class enables external processes to be started and monitored. /// internal static class ProcessExecutor { /// /// Creates a process, starts it, wait for its execution and return its exit code. /// /// Path to the executable to start on a different process. /// Arguments to the executable. /// The process' exit code. internal static int DoProcess(string filename, string arguments) { #if DEBUG Log.WriteLine("ProcessorExecutor.DoProcess : " + filename + " " + arguments); #endif try { Process p = new Process(); ProcessStartInfo si = new ProcessStartInfo(filename); si.UseShellExecute = false; si.CreateNoWindow = true; si.Arguments = arguments; si.RedirectStandardError = true; si.RedirectStandardOutput = true; p.StartInfo = si; p.Start(); p.WaitForExit(); int ret = p.ExitCode; p.Close(); p.Dispose(); p = null; return ret; } catch (Exception e) { Console.WriteLine("CafeX Error: Exception in process executor"); Console.WriteLine(filename + " " + arguments); Console.WriteLine(e.Message); return -1; } } /// /// Creates and starts a new process, but return immediately the execution to the caller. /// The child's stdout and errout is printed to this process' stdout. /// /// Path to the executable to start on a different process. /// Arguments to the executable. /// 0 if the process is created, -1 otherwise. internal static int DoProcess_DontWait(string filename, string arguments) { #if DEBUG Log.WriteLine("ProcessorExecutor.DoProcess_DontWait : " + filename + " " + arguments); #endif try { Process p = new Process(); ProcessStartInfo si = new ProcessStartInfo(filename); si.UseShellExecute = false; si.CreateNoWindow = true; si.Arguments = arguments; si.RedirectStandardError = true; si.RedirectStandardOutput = true; p.StartInfo = si; // Won't wait, but need to connect stdout and stderr to this parent process. p.OutputDataReceived += (sender, args) => Console.WriteLine(args.Data); p.ErrorDataReceived += (sender, args) => Console.WriteLine(args.Data); p.Start(); p.BeginOutputReadLine(); p.BeginErrorReadLine(); return 0; } catch (Exception e) { Console.WriteLine("CafeX Error: Exception in process executor"); Console.WriteLine(filename + " " + arguments); Console.WriteLine(e.Message); return -1; } } /// /// Creates a process, starts it, wait for its execution and return its exit code. /// The child's process stdout is captured and returned to the caller in the ouput variable. /// /// Path to the executable to start on a different process. /// Arguments to the executable. /// Returns the child's process captured output the the caller. /// Chid's process exit code. internal static int DoProcess_GetOutput(string filename, string arguments, out string output) { #if DEBUG Log.WriteLine("ProcessorExecutor.DoProcess_GetOutput : " + filename + " " + arguments); #endif return DoProcess_GetOutput(filename, arguments, false, -1, out output); } /// /// Creates a process, starts it, wait for its execution and return its exit code. /// The child's process stdout is captured and returned to the caller in the ouput variable. /// /// Path to the executable to start on a different process. /// Arguments to the executable. /// How long (in ms) to wait for the process to end. Set to values less than 1 to wait indefinately. /// Returns the child's process captured output the the caller. /// Chid's process exit code. internal static int DoProcess_GetOutput(string filename, string arguments, bool waitDataReceived, int timeout, out string output) { #if DEBUG Log.WriteLine("ProcessorExecutor.DoProcess_GetOutput : " + filename + " " + arguments); #endif try { int ret; bool isDataReceived = !(waitDataReceived); // If we want to wait for data received event (waitDataReceived=true), need to set isDataReceived = false. System.Threading.ManualResetEvent dataReceivedEvent = new System.Threading.ManualResetEvent(false); TimeSpan DATA_RECEIVE_FLUSH_TIMEOUT = new TimeSpan(0, 0, 2); Process p = new Process(); ProcessStartInfo si = new ProcessStartInfo(filename); si.UseShellExecute = false; si.CreateNoWindow = true; si.Arguments = arguments; StringBuilder outputTmp = new StringBuilder(); DataReceivedEventHandler callback = (sender, e) => { if (e != null && e.Data != null) { outputTmp.AppendLine( e.Data ); if (!isDataReceived) { isDataReceived = true; dataReceivedEvent.Set(); } } }; si.RedirectStandardError = true; si.RedirectStandardOutput = true; p.StartInfo = si; p.OutputDataReceived += callback; p.ErrorDataReceived += callback; p.Start(); p.BeginOutputReadLine(); p.BeginErrorReadLine(); bool exitedProperly = true; if (timeout > 0) { exitedProperly = p.WaitForExit(timeout); if (!exitedProperly) { Console.WriteLine("ERROR: Process started for '" + filename + " " + arguments + "' did not finish in the expected timeout (" + timeout.ToString() + "ms)."); Console.WriteLine("Dumping process std/out std/err until now:\n {0} \n", outputTmp.ToString()); } } else { p.WaitForExit(); } if (exitedProperly) { ret = p.ExitCode; } else { ret = -1; } // Some output should have been received either in stdout or stderr, so wait if (isDataReceived == false) { dataReceivedEvent.WaitOne(DATA_RECEIVE_FLUSH_TIMEOUT); } p.CancelOutputRead(); p.CancelErrorRead(); p.StartInfo.RedirectStandardOutput = false; p.StartInfo.RedirectStandardError = false; p.OutputDataReceived -= callback; p.ErrorDataReceived -= callback; p.Close(); p.Dispose(); p = null; output = outputTmp.ToString(); return ret; } catch (Exception e) { output = string.Empty; Console.WriteLine("CafeX Error: Exception in process executor"); Console.WriteLine(filename + " " + arguments); Console.WriteLine(e.Message); return -1; } } /// /// Creates a process, starts it, wait for its execution and return its exit code. /// The child's process stdout and errout are set to a callback handler provided. /// /// Path to the executable to start on a different process. /// Arguments to the executable. /// An instance of DataReceivedEventHandler provided by the caller. /// Chid's process exit code. internal static int StartProcess_RedirectOutput(string filename, string arguments, DataReceivedEventHandler callback, bool waitCompletion) { #if DEBUG Log.WriteLine("ProcessorExecutor.StartProcess_RedirectOutput : " + filename + " " + arguments); #endif try { Process p = new Process(); ProcessStartInfo si = new ProcessStartInfo(filename); si.UseShellExecute = false; si.CreateNoWindow = true; si.Arguments = arguments; si.RedirectStandardError = true; si.RedirectStandardOutput = true; p.StartInfo = si; p.OutputDataReceived += callback; p.ErrorDataReceived += callback; p.Start(); p.BeginOutputReadLine(); p.BeginErrorReadLine(); if (waitCompletion) { p.WaitForExit(); int ret = p.ExitCode; p.CancelOutputRead(); p.CancelErrorRead(); p.StartInfo.RedirectStandardOutput = false; p.StartInfo.RedirectStandardError = false; p.OutputDataReceived -= callback; p.ErrorDataReceived -= callback; p.Close(); p.Dispose(); p = null; return ret; } else { return 0; } } catch (Exception e) { Console.WriteLine("CafeX Error: Exception in process executor"); Console.WriteLine(filename + " " + arguments); Console.WriteLine(e.Message); return -1; } } internal static int StartProcess_RedirectOutput(string filename, string arguments) { #if DEBUG Log.WriteLine("ProcessorExecutor.StartProcess_RedirectOutput : " + filename + " " + arguments); #endif try { Process p = new Process(); ProcessStartInfo si = new ProcessStartInfo(filename); si.UseShellExecute = false; si.CreateNoWindow = true; si.Arguments = arguments; si.RedirectStandardError = true; si.RedirectStandardOutput = true; si.RedirectStandardInput = true; p.StartInfo = si; p.Start(); p.BeginOutputReadLine(); return 0; } catch (Exception e) { Console.WriteLine("CafeX Error: Exception in process executor"); Console.WriteLine(filename + " " + arguments); Console.WriteLine(e.Message); return -1; } } } /// /// This class allows string arrays to be combined into a single string with the delimiter of choice. /// internal static class StringCombiner { internal static string MakeDelimitedString(string[] arr, char delimiter) { string ret = string.Empty; if (arr != null) { for (int i = 0; i < arr.Length; ++i) { if (i == 0) { ret += arr[i]; } else { ret += delimiter + arr[i]; } } } return ret; } } /// /// This class is used to edit xml content files. /// internal static class XmlHandler { /// /// Traverses the xml document from the provided root node until it finds the node that matches the provided name. /// /// Root node /// Name of the node being searched /// The node that matches the provided node name, null if it was not found. internal static XmlNode GetXmlNodeRecursive(XmlNode root_name, string node_name) { if (root_name.Name == node_name) return root_name; XmlNode ret = null; foreach (XmlNode child in root_name.ChildNodes) { ret = GetXmlNodeRecursive(child, node_name); if (ret != null) break; } return ret; } /// /// Check if a node by the provided name exists inside an xml document. /// /// Full path to the xml document. /// Name of the node to search. /// True if the node was found, false otherwise. internal static bool NodeExists(string file_name, string node_name) { #if DEBUG Log.WriteLine(string.Format("NodeExists: File: {0}, Node: {1}", file_name, node_name)); #endif XmlDocument document = new XmlDocument(); document.Load(file_name); XmlNode node = GetXmlNodeRecursive(document.DocumentElement, node_name); return (node == null) ? false : true; } /// /// Returns the value of a node from an existing xml document. /// /// Full path to the xml document. /// Name of the node to search. /// String representing the node's value, null if the node was not found. internal static string GetNodeValue(string file_name, string node_name) { #if DEBUG Log.WriteLine("GetNodeValue called. file_name=" + file_name + ", node_name=" + node_name); #endif XmlDocument document = new XmlDocument(); document.Load(file_name); XmlNode node = GetXmlNodeRecursive(document.DocumentElement, node_name); if (node == null) { Console.WriteLine("cafex xmlhandler getnodevalue failed: Node " + node_name + " does not exist in " + file_name); return null; } #if DEBUG Log.WriteLine("GetNodeValue return = " + node.InnerText); #endif return node.InnerText; } /// /// Updates the value of a node inside an xml document. /// /// Full path to the xml document. /// Name of the node to search. /// The new value for the node. internal static void UpdateNodeValue(string file_name, string node_name, string new_value) { #if DEBUG Log.WriteLine("UpdateNodeValue called. file_name=" + file_name + ", node_name=" + node_name + ", new_value=" + new_value); #endif XmlDocument document = new XmlDocument(); document.Load(file_name); XmlNode node = GetXmlNodeRecursive(document.DocumentElement, node_name); if (node == null) { Console.WriteLine("cafex xmlhandler updatenodevalue failed: Node " + node_name + " does not exist in " + file_name); return; } // Only update if necessary. if (node.InnerText != new_value) { if (File.Exists(file_name)) { // Strip the read-only attribute so we can overwrite it if it exists File.SetAttributes(file_name, File.GetAttributes(file_name) & ~FileAttributes.ReadOnly); } // Before the update, check if the file is not locked for 3 times uint retries = Program.RETRY_ATTEMPS; while (FileUtil.IsFileLocked(file_name) && retries > 0) { System.Threading.Thread.Sleep(Program.RETRY_TIMESPAN); --retries; } node.InnerText = new_value; #if DEBUG Log.WriteLine("updateNodeValue: file: " + file_name + ", node: " + node_name + ", newvalue: " + new_value); #endif XmlWriterSettings settings = new XmlWriterSettings(); settings.NewLineChars = "\n"; settings.Indent = true; settings.Encoding = new UTF8Encoding(false); using (XmlWriter writer = XmlWriter.Create(file_name, settings)) { document.WriteTo(writer); writer.Flush(); writer.Close(); } File.SetAttributes(file_name, FileAttributes.Normal); #if DEBUG Log.WriteLine("updateNodeValue completed on '" + file_name + "' and file in use = " + FileUtil.IsFileLocked(file_name)); #endif } } /// /// Add an xml node to an xml document. /// /// Full path to the xml document. /// Name for the new xml node. /// Node's value. /// Array of strings representing the node's attributes. internal static void AddNode(string file_name, string node_name, string value, string[] attributes) { #if DEBUG Log.WriteLine("AddNode called. file_name=" + file_name + ", node_name=" + node_name + ", value=" + value); string attrString = null; foreach (string attribute in attributes) { attrString += attribute + " "; } Log.WriteLine("Attributes = " + attrString); #endif XmlDocument document = new XmlDocument(); document.Load(file_name); XmlElement node = document.CreateElement(node_name); node.InnerText = value; for (int i = 0; i < attributes.Length; i += 2) { node.SetAttribute(attributes[i], attributes[i + 1]); } document.DocumentElement.AppendChild(node); #if DEBUG Log.WriteLine("AddNode: file: " + file_name + ", node: " + node_name + ", value: " + value); #endif XmlWriterSettings settings = new XmlWriterSettings(); settings.NewLineChars = "\n"; settings.Indent = true; settings.Encoding = new UTF8Encoding(false); // Before the update, check if the file is not locked for 3 times uint retries = Program.RETRY_ATTEMPS; while (FileUtil.IsFileLocked(file_name) && retries > 0) { System.Threading.Thread.Sleep(Program.RETRY_TIMESPAN); // 1s --retries; } using (XmlWriter writer = XmlWriter.Create(file_name, settings)) { document.WriteTo(writer); writer.Flush(); writer.Close(); } File.SetAttributes(file_name, FileAttributes.Normal); #if DEBUG Log.WriteLine("AddNode completed on '" + file_name + "' and file in use = " + FileUtil.IsFileLocked(file_name)); #endif } /// /// Remove a node from an xml document. /// /// Full path to the xml document. /// Name for the node to search. internal static void RemoveNode(string file_name, string node_name) { #if DEBUG Log.WriteLine("RemoveNode called. file_name=" + file_name + ", node_name=" + node_name); #endif XmlDocument document = new XmlDocument(); document.Load(file_name); XmlNode node = GetXmlNodeRecursive(document.DocumentElement, node_name); if (node == null) { return; } #if DEBUG Log.WriteLine("RemoveNode: file: " + file_name + ", node: " + node_name); #endif document.DocumentElement.RemoveChild(node); XmlWriterSettings settings = new XmlWriterSettings(); settings.NewLineChars = "\n"; settings.Indent = true; settings.Encoding = new UTF8Encoding(false); // Before the update, check if the file is not locked for 3 times uint retries = Program.RETRY_ATTEMPS; while (FileUtil.IsFileLocked(file_name) && retries > 0) { System.Threading.Thread.Sleep(Program.RETRY_TIMESPAN); // 1s --retries; } using (XmlWriter writer = XmlWriter.Create(file_name, settings)) { document.WriteTo(writer); writer.Flush(); writer.Close(); } File.SetAttributes(file_name, FileAttributes.Normal); #if DEBUG Log.WriteLine("RemoveNode completed on '" + file_name + "' and file in use = " + FileUtil.IsFileLocked(file_name)); #endif } internal static void UpdateNodeValueByPath(string file_name, string xpath, string new_value) { #if DEBUG Log.WriteLine("UpdateNodeValueByPath called. file_name=" + file_name + ", xpath=" + xpath + ", new_value=" + new_value); #endif XmlDocument document = new XmlDocument(); document.Load(file_name); try { XmlNode target = document.SelectSingleNode(xpath); if (target == null) { Console.WriteLine("cafex xmlhandler UpdateNodeValueByPath failed: Node specified by path " + xpath + " does not exist in " + file_name); return; } if (target.InnerText != new_value) { uint retries = Program.RETRY_ATTEMPS; while (FileUtil.IsFileLocked(file_name) && retries > 0) { System.Threading.Thread.Sleep(Program.RETRY_TIMESPAN); --retries; } target.InnerText = new_value; #if DEBUG Log.WriteLine("UpdateNodeValueByPath: file=" + file_name + ", xpath=" + xpath + ", new_value=" + new_value); #endif XmlWriterSettings settings = new XmlWriterSettings(); settings.NewLineChars = "\n"; settings.Indent = true; settings.Encoding = new UTF8Encoding(false); using (XmlWriter writer = XmlWriter.Create(file_name, settings)) { document.WriteTo(writer); writer.Flush(); writer.Close(); } File.SetAttributes(file_name, FileAttributes.Normal); #if DEBUG Log.WriteLine("UpdateNodeValueByPath completed on '" + file_name + "' and file in use = " + FileUtil.IsFileLocked(file_name)); #endif } } catch (System.Xml.XPath.XPathException) { Console.WriteLine("UpdateNodeValueByPath contained an invalid xpath parameter! xpath={0}\nNode will not be updated!",xpath); return; } } } /// /// This class is a wrapper on HEX conversion functions used by CafeX. /// internal static class HexHandler { /// /// Transform the input string into its integer representation. /// Strings that are invalid representations of integers will cause exceptions. /// /// String representing a number. /// The integer that represents the provided string. internal static uint GetNumberFromString(string input) { #if DEBUG Log.WriteLine("GetNumberFromString called. input=" + input); #endif if (input.StartsWith("0x")) { return Convert.ToUInt32(input, 16); } else { foreach (char c in input) { if ((c >= 'a' || c >= 'A') && (c <= 'f' || c <= 'F')) { return Convert.ToUInt32(input, 16); } } return Convert.ToUInt32(input); } } /// /// Transform the input string into its long representation. /// Strings that are invalid representations of longs will cause exceptions. /// /// String representing a number. /// The long that represents the provided string. internal static long GetLongNumberFromString(string input) { #if DEBUG Log.WriteLine("GetLongNumberFromString called. input=" + input); #endif if (input.StartsWith("0x")) { return Convert.ToInt64(input, 16); } else { foreach (char c in input) { if ((c >= 'a' || c >= 'A') && (c <= 'f' || c <= 'F')) { return Convert.ToInt64(input, 16); } } return Convert.ToInt64(input); } } } /// /// Utilities for file verification. /// internal static class FileUtil { private static string cafex_system_wide_mtx_name = "Global\\cafex_system_wide_mtx"; private static DateTime entryAssemblyTs; private static bool bRunningFromCygwin = false; private static string assemblyFileName; /// /// Initializes one-shot only utility variables /// internal static void GatherEnvironment() { string cygcheck; assemblyFileName = System.Reflection.Assembly.GetExecutingAssembly().Location; entryAssemblyTs = File.GetLastWriteTimeUtc(assemblyFileName); cygcheck = Environment.GetEnvironmentVariable("CYGCHECK"); if ( !(string.IsNullOrEmpty(cygcheck)) ) { if( cygcheck.StartsWith("cygwin" ) ) { bRunningFromCygwin = true; } } } /// /// Determine whether we are running from within cygwin /// internal static bool RunningFromCygwin { get { return bRunningFromCygwin; } } /// /// Check if a file is locked for R/W and/or delete. /// /// Full path to the file. /// True if the file is locked, false otherwise. internal static bool IsFileLocked(string filename) { FileStream stream = null; bool result = false; try { FileInfo file = new FileInfo(filename); stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None); result = false; } catch (IOException) { //the file is unavailable because it is: //a) still being written to //b) or being processed by another thread (maybe held by PCFS) //c) or does not exist (has already been processed) #if DEBUG // Collect debug information to help troubleshoot who has this file handle try { Process handleTool = new Process(); handleTool.StartInfo.FileName = "handle.exe"; handleTool.StartInfo.Arguments = filename + " /accepteula"; handleTool.StartInfo.UseShellExecute = false; handleTool.StartInfo.RedirectStandardOutput = true; handleTool.Start(); handleTool.WaitForExit(); string handleOutput = handleTool.StandardOutput.ReadToEnd(); Log.WriteLine("############## FILE LOCKED WARNING REPORT ##############"); Log.WriteLine("Filename = " + filename); Log.WriteLine("Handles opened:"); Log.WriteLine(handleOutput); Log.WriteLine("############## FILE LOCKED END OF REPORT ##############"); } catch (Exception) { Log.WriteLine("Unable to get handle information on file " + filename); } finally #endif { result = true; } } finally { if (stream != null) { stream.Close(); } } return result; } /// /// Expand an executable to the temp folder from a resource location /// /// Executable bytes from resource /// File to be written to disk /// Full path to temporary file written to disk. internal static string ExpandExecutable(byte[] payload, string filename) { System.Threading.Mutex mtx = null; string full_tmp_path; full_tmp_path = Environment.GetEnvironmentVariable("CAFE_TEMP"); if( string.IsNullOrEmpty(full_tmp_path) ) { full_tmp_path = Environment.GetEnvironmentVariable("TEMP"); if( string.IsNullOrEmpty(full_tmp_path) ) { full_tmp_path = Environment.GetEnvironmentVariable("TMP"); if ( string.IsNullOrEmpty(full_tmp_path) ) { full_tmp_path = Path.GetDirectoryName(assemblyFileName); } } } full_tmp_path = PathConverter.Windowsify(full_tmp_path) + "\\" + filename; // the kinds of things that can prevent this from happening are all fatal // errors, don't try to catch anything mtx = new System.Threading.Mutex(false, cafex_system_wide_mtx_name); mtx.WaitOne(); // from this point on, we must release the mutex no matter what happens! try { DateTime tempExeTs; bool doOverwrite = false; try { tempExeTs = File.GetLastWriteTimeUtc(full_tmp_path); // if the internal resource is newer than the file on disk if (entryAssemblyTs >= tempExeTs) { doOverwrite = true; } // else, we will use the file that's there, as it is newer than we are } catch (FileNotFoundException) { // file's not there - we will overwrite it doOverwrite = true; } if (doOverwrite) { try { File.WriteAllBytes(full_tmp_path, payload); } catch( System.IO.DirectoryNotFoundException ) { try { Directory.CreateDirectory( Path.GetDirectoryName(full_tmp_path) ); } catch { } File.WriteAllBytes(full_tmp_path, payload); } } } finally { mtx.ReleaseMutex(); } return full_tmp_path; } /// /// Check if the provide string is a valid file name. /// /// String to check. /// True if it is a valid file name, false otherwise. internal static bool IsValidFile(string filename, bool checkExistence) { #if DEBUG Log.WriteLine("IsValidFile called. filename=" + filename + ", checkExistence=" + checkExistence.ToString()); #endif if (string.IsNullOrEmpty(filename)) { return false; } // Convert to windows, if it was a cygwin path filename = PathConverter.Windowsify(filename); bool returnVal = false; try { FileInfo file = new FileInfo(filename); // A path without a filename resolves to a valid FileInfo instance // so we need to check if there is a file at the end of the path. if (Path.GetFileName(file.FullName).Length == 0) { // There is no filename, only a valid path! return false; } if (!checkExistence) { returnVal = true; } else { returnVal = file.Exists; } } catch (ArgumentException) { } catch (System.IO.PathTooLongException) { } catch (NotSupportedException) { } #if DEBUG Log.WriteLine("IsValidFile return " + returnVal.ToString()); #endif return returnVal; } internal static void AddQuotes(ref string fileNameOrListWithSpaces) { #if DEBUG Log.WriteLine("AddQuotes called. " + "fileNameOrListWithSpaces=" + fileNameOrListWithSpaces); #endif if (string.IsNullOrEmpty(fileNameOrListWithSpaces)) { return; } if (fileNameOrListWithSpaces.StartsWith("\"") && fileNameOrListWithSpaces.EndsWith("\"")) { return; } fileNameOrListWithSpaces = "\"" + fileNameOrListWithSpaces + "\""; } /// /// Copy a directory from one location to another with optional recursion. /// /// Source folder name. /// Destination folder name. /// Flag to copy all of the sub-folders recurively. internal static void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs) { #if DEBUG Console.WriteLine("FileUtil::DirectoryCopy: Copying '{0}' => '{1}'{2}", sourceDirName, destDirName, copySubDirs ? " recursively" : ""); #endif // Get the subdirectories for the specified directory. DirectoryInfo dir = new DirectoryInfo(sourceDirName); DirectoryInfo[] dirs = dir.GetDirectories(); if (!dir.Exists) { throw new DirectoryNotFoundException("Source directory does not exist or could not be found: " + sourceDirName); } // If the destination directory doesn't exist, create it. if (!Directory.Exists(destDirName)) { Directory.CreateDirectory(destDirName); } // Get the files in the directory and copy them to the new location. FileInfo[] files = dir.GetFiles(); foreach (FileInfo file in files) { string temppath = Path.Combine(destDirName, file.Name); file.CopyTo(temppath, false); } // If copying subdirectories, copy them and their contents to new location. if (copySubDirs) { foreach (DirectoryInfo subdir in dirs) { string temppath = Path.Combine(destDirName, subdir.Name); DirectoryCopy(subdir.FullName, temppath, copySubDirs); } } } /// /// Deletes the content of a directory with optional recursion /// /// Directory folder name. /// Flag to remove subdirectories recursively. internal static void DirectoryDelete(string dirName, bool deleteSubDirs) { #if DEBUG Console.WriteLine("FileUtil::DirectoryDelete: Deleting '{0}'{1}", dirName, deleteSubDirs ? " recursively" : ""); #endif // Delete all of the files in the directory first foreach (string srcEntry in Directory.GetFiles(dirName)) { FileDelete(srcEntry); } if (deleteSubDirs) { foreach (string srcEntry in Directory.GetDirectories(dirName)) { DirectoryDelete(srcEntry, deleteSubDirs); } #if DEBUG Console.WriteLine("FileUtil::DirectoryDelete: Deleting '{0}'", dirName); #endif Directory.Delete(dirName); } } internal static void FileDelete(string fileName) { #if DEBUG Console.WriteLine("FileUtil::FileDelete: Deleting '{0}'", fileName); #endif if (File.Exists(fileName)) { // Strip any readonly, hidden and system attributes FileInfo fileInfo = new FileInfo(fileName); fileInfo.Attributes = FileAttributes.Normal; // Now delete the file File.Delete(fileName); } } } /// /// Log to the stdout and debug. This is only used in debug builds. /// internal static class Log { internal static StreamWriter sw = null; internal static bool OpenLogFile(string logFilePath) { try { if (sw == null) { string path = Environment.ExpandEnvironmentVariables(logFilePath); path = PathConverter.Windowsify(path); string directory = path.Substring(0, path.LastIndexOf('\\')); if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } sw = new StreamWriter(path); } } catch (Exception ex) { Console.WriteLine("Failed to open the log file. InnerException: {0}", ex.Message); sw = null; } return (sw != null); } internal static void WriteLine(string message) { message = string.Format("[ {0} - DEBUG: {1} ] {2}", System.Diagnostics.Process.GetCurrentProcess().Id, DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss-ffff"), message); Console.WriteLine(message); Debug.WriteLine(message); if (sw != null) { sw.WriteLine(message); } } internal static void WriteLine(string format, object arg0) { WriteLine(string.Format(format, arg0)); } internal static void WriteLine(string format, object arg0, object arg1) { WriteLine(string.Format(format, arg0, arg1)); } internal static void WriteLine(string format, object arg0, object arg1, object arg2) { WriteLine(string.Format(format, arg0, arg1, arg2)); } internal static void WriteLineRaw(string message) { Console.WriteLine(message); Debug.WriteLine(message); if (sw != null) { sw.WriteLine(message); } } internal static void WriteLineRaw(string format, object arg0) { WriteLineRaw(string.Format(format, arg0)); } internal static void WriteLineRaw(string format, object arg0, object arg1) { WriteLineRaw(string.Format(format, arg0, arg1)); } internal static void WriteLineRaw(string format, object arg0, object arg1, object arg2) { WriteLineRaw(string.Format(format, arg0, arg1, arg2)); } internal static void CloseLogFile() { if (sw != null) { sw.Close(); } } } /// /// This class provides tools for argument validation. /// internal static class ArgumentChecks { internal static bool IsArgumentValidSetting(string[] args, int index) { #if DEBUG Log.WriteLine("IsArgumentValidSetting called. Checking index=" + index.ToString()); string argString = null; for (int counter = 0; counter < args.Length; ++counter) { argString += "{" + counter.ToString() + "}" + args[counter] + " "; } Log.WriteLine("arguments=" + argString); #endif bool returnVal = false; // If the argument doesn't exist, it is not valid. if (!ArgumentExists(args, index)) { returnVal = false; } else { // If the argument starts with '-' it is another option, not a setting. if (!args[index].StartsWith("-")) { returnVal = true; } else { returnVal = false; } } #if DEBUG Log.WriteLine("IsArgumentValidSetting return " + returnVal.ToString()); #endif return returnVal; } /// /// Check if the provided index exists in the argument list. /// /// Array of string representing an argument list. /// The index of the argument that will be checked. /// True if the argument at the provide index exists, false otherwise. internal static bool ArgumentExists(string[] args, int index) { if (args == null || args.Length == 0 || index < 0 || index > (args.Length - 1)) { return false; } else { return true; } } } internal class PerfTimer { private System.Diagnostics.Stopwatch _clock; private string _name; public string Name { get { return _name;} } public PerfTimer(string name) { _name = name; _clock = new Stopwatch(); } public void Start() { Log.WriteLine("[TIMER]: Starting PerfTimer " + _name); _clock.Start(); } public void Report() { Log.WriteLine("[TIMER]: PerfTimer " + _name + " current time is " + _clock.ElapsedMilliseconds + " ms."); } public void Stop() { _clock.Stop(); Log.WriteLine("[TIMER]: PerfTimer " + _name + " stopped."); this.Report(); } public void Reset() { _clock.Reset(); Log.WriteLine("[TIMER]: PerfTimer " + _name + " reset."); } } }