unsafe public void EnumerateFiles(IntPtr medBuffer, ref Dictionary <ulong, FileNameAndParentFrn> files) { IntPtr pData = Marshal.AllocHGlobal(sizeof(UInt64) + 0x10000); PInvokeWin32.ZeroMemory(pData, sizeof(UInt64) + 0x10000); uint outBytesReturned = 0; while (false != PInvokeWin32.DeviceIoControl(_changeJournalRootHandle, PInvokeWin32.FSCTL_ENUM_USN_DATA, medBuffer, sizeof(PInvokeWin32.MFT_ENUM_DATA), pData, sizeof(UInt64) + 0x10000, out outBytesReturned, IntPtr.Zero)) { IntPtr pUsnRecord = new IntPtr(pData.ToInt32() + sizeof(Int64)); while (outBytesReturned > 60) { PInvokeWin32.USN_RECORD usn = new PInvokeWin32.USN_RECORD(pUsnRecord); if (0 != (usn.FileAttributes & PInvokeWin32.FILE_ATTRIBUTE_DIRECTORY)) { // // handle directories // if (!files.ContainsKey(usn.FileReferenceNumber)) { files.Add(usn.FileReferenceNumber, new FileNameAndParentFrn(usn.FileName, usn.ParentFileReferenceNumber)); } else { // this is debug code and should be removed when we are certain that // duplicate frn's don't exist on a given drive. To date, this exception has // never been thrown. Removing this code improves performance.... throw new Exception(string.Format("Duplicate FRN: {0} for {1}", usn.FileReferenceNumber, usn.FileName)); } } else { if (!files.ContainsKey(usn.FileReferenceNumber)) { files.Add(usn.FileReferenceNumber, new FileNameAndParentFrn(usn.FileName, usn.ParentFileReferenceNumber)); } else { FileNameAndParentFrn frn = files[usn.FileReferenceNumber]; if (0 != string.Compare(usn.FileName, frn.Name, true)) { // Log.InfoFormat( // "Attempt to add duplicate file reference number: {0} for file {1}, file from index {2}", // usn.FileReferenceNumber, usn.FileName, frn.Name); throw new Exception(string.Format("Duplicate FRN: {0} for {1}", usn.FileReferenceNumber, usn.FileName)); } } } pUsnRecord = new IntPtr(pUsnRecord.ToInt32() + usn.RecordLength); outBytesReturned -= usn.RecordLength; } Marshal.WriteInt64(medBuffer, Marshal.ReadInt64(pData, 0)); } Marshal.FreeHGlobal(pData); }
public static string soloMFTGetFullyQualifiedPath(UInt32 frn) { string retval = string.Empty;; FileNameAndParentFrn fnFRN = null; if (frn >= 0) { if (soloMFTDictOffsets.TryGetValue(frn, out fnFRN)) { retval = fnFRN.Name; while (fnFRN.ParentFrn != 0) { if (soloMFTDictOffsets.TryGetValue(fnFRN.ParentFrn, out fnFRN)) { string name = fnFRN.Name; retval = Path.Combine(name, retval); } else { break; } } } } else { throw new ArgumentException("Invalid argument", "frn"); } return(retval); }
private void ResolvePath(string drive, ref Dictionary <ulong, FileNameAndParentFrn> files) { foreach (KeyValuePair <ulong, FileNameAndParentFrn> entry in files) { FileNameAndParentFrn file = (FileNameAndParentFrn)entry.Value; file.Path = string.Concat(FrnToParentDirectory(drive, file.ParentFrn), Path.DirectorySeparatorChar, file.Name); } }
public void ScanExes() { DriveInfo[] drives = DriveInfo.GetDrives(); Stopwatch stop = new Stopwatch(); for (int i = 0; i < drives.Length; i++) { DriveInfo d = drives[i]; if (d.DriveFormat != "NTFS") { continue; } LogManager.Log("> Searching drive {0} for game executables", d.Name); stop.Reset(); stop.Start(); Dictionary <ulong, FileNameAndParentFrn> mDict = new Dictionary <ulong, FileNameAndParentFrn>(); MFTReader mft = new MFTReader(); mft.Drive = d.RootDirectory.FullName; mft.EnumerateVolume(out mDict, new string[] { ".exe" }); foreach (KeyValuePair <UInt64, FileNameAndParentFrn> entry in mDict) { FileNameAndParentFrn file = (FileNameAndParentFrn)entry.Value; string name = file.Name; string lower = name.ToLower(); GameInfo game; if (gameManager.GameInfos.TryGetValue(lower, out game)) { string path = mft.GetFullPath(file); LogManager.Log("Found game: {0}, full path: {1}", game.GameName, path); UserGameInfo info = new UserGameInfo(); info.InitializeDefault(game, path); gameManager.User.Games.Add(info); } } stop.Stop(); LogManager.Log("> Took {0} seconds to search drive {1}", stop.Elapsed.TotalSeconds.ToString("0.00"), d.Name); } gameManager.SaveUserProfile(); gameManager.WaitSave(); }
public FileNameAndParentFrn SearchId(ulong key, Dictionary <ulong, FileNameAndParentFrn> mDict) { FileNameAndParentFrn file = null; if (mDict.ContainsKey(key)) { file = mDict[key]; Program.nameLst.Add(file.Name); while (file != null) { file = SearchId(file.ParentFrn, mDict); } } return(file); }
private void GetRootFrnEntry(string drive) { string driveRoot = string.Concat("\\\\.\\", drive); driveRoot = string.Concat(driveRoot, Path.DirectorySeparatorChar); IntPtr hRoot = NativeWrapper.CreateFile(driveRoot, 0, NativeWrapper.FILE_SHARE_READ | NativeWrapper.FILE_SHARE_WRITE, IntPtr.Zero, NativeWrapper.OPEN_EXISTING, NativeWrapper.FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero); if (hRoot.ToInt32() != NativeWrapper.INVALID_HANDLE_VALUE) { BY_HANDLE_FILE_INFORMATION fi = new BY_HANDLE_FILE_INFORMATION(); bool bRtn = NativeWrapper.GetFileInformationByHandle(hRoot, out fi); if (bRtn) { UInt64 fileIndexHigh = (UInt64)fi.FileIndexHigh; UInt64 indexRoot = (fileIndexHigh << 32) | fi.FileIndexLow; FileNameAndParentFrn f = new FileNameAndParentFrn(driveRoot, 0); _directories.Add(indexRoot, f); } else { throw new IOException("GetFileInformationbyHandle() returned invalid handle", new Win32Exception(Marshal.GetLastWin32Error())); } NativeWrapper.CloseHandle(hRoot); } else { throw new IOException("Unable to get root frn entry", new Win32Exception(Marshal.GetLastWin32Error())); } }
private void SearchDrive(object state) { int i = (int)state; SearchDriveInfo info = toSearch[i]; if (!info.drive.IsReady) { done++; return; } float totalDiskPc = 1 / (float)toSearch.Count; float thirdDiskPc = totalDiskPc / 3.0f; // 1/3 done, we started the operation UpdateProgress(thirdDiskPc); LogManager.Log("> Searching drive {0} for game executables", info.drive.Name); Dictionary <ulong, FileNameAndParentFrn> mDict = new Dictionary <ulong, FileNameAndParentFrn>(); MFTReader mft = new MFTReader(); mft.Drive = info.drive.RootDirectory.FullName; mft.EnumerateVolume(out mDict, new string[] { ".exe" }); progress += thirdDiskPc; // 2/3 done UpdateProgress(thirdDiskPc); float increment = thirdDiskPc / (float)mDict.Count; foreach (KeyValuePair <UInt64, FileNameAndParentFrn> entry in mDict) { if (closed) { return; } UpdateProgress(increment); FileNameAndParentFrn file = (FileNameAndParentFrn)entry.Value; string name = file.Name; string lower = name.ToLower(); if (GameManager.Instance.AnyGame(lower)) { string path = mft.GetFullPath(file); if (path.Contains("$Recycle.Bin") || path.Contains(@"\Instance")) { // noope continue; } UserGameInfo uinfo = GameManager.Instance.TryAddGame(path); if (uinfo != null) { #if RELEASE if (uinfo.Game.Debug) { continue; } #endif LogManager.Log("> Found new game {0} on drive {1}", uinfo.Game.GameName, info.drive.Name); Invoke(new Action(delegate { listGames.Items.Add(uinfo.Game.GameName + " - " + path); listGames.Invalidate(); main.NewUserGame(uinfo); })); } } } if (closed) { return; } done++; if (done == toSearch.Count) { searching = false; Invoke(new Action(delegate { progress = 1; UpdateProgress(0); btnSearch.Enabled = true; main.RefreshGames(); MessageBox.Show("Finished searching!"); })); } }
private void SearchDrive(object state) { int driveIndex = (int)state; SearchStorageInfo info = drivesToSearch[driveIndex]; if (!info.Drive.IsReady) { drivesFinishedSearching++; return; } float totalDiskPc = 1 / (float)drivesToSearch.Count; float thirdDiskPc = totalDiskPc / 3.0f; // 1/3 done, we started the operation UpdateProgress(thirdDiskPc); Log.WriteLine($"> Searching drive {info.Drive.Name} for game executables"); Dictionary <ulong, FileNameAndParentFrn> allExes = new Dictionary <ulong, FileNameAndParentFrn>(); MFTReader mft = new MFTReader(); mft.Drive = info.Drive.RootDirectory.FullName; // TODO: search only for specific games? mft.EnumerateVolume(out allExes, new string[] { ".exe" }); UpdateProgress(thirdDiskPc); // 2/3 done float perFilePCIncrement = thirdDiskPc / (float)allExes.Count; bool shouldUpdate = false; foreach (KeyValuePair <UInt64, FileNameAndParentFrn> entry in allExes) { UpdateProgress(perFilePCIncrement); FileNameAndParentFrn file = (FileNameAndParentFrn)entry.Value; string name = file.Name; string lower = name.ToLower(); if (GameManager.Instance.AnyGame(lower)) { string path = mft.GetFullPath(file); if (path.Contains("$Recycle.Bin") || path.Contains(@"\Instance")) { // noope continue; } UserGameInfo uinfo = GameManager.Instance.TryAddGame(path); if (uinfo != null) { Log.WriteLine($"> Found new game ID {uinfo.GameID} on drive {info.Drive.Name}"); Invoke(new Action(delegate { list_games.Items.Add(GameManager.Instance.MetadataManager.GetGameName(uinfo.GameID) + " - " + path); list_games.Invalidate(); shouldUpdate = true; })); } } } if (shouldUpdate) { MainForm.Instance.Invoke((Action)MainForm.Instance.RefreshGames); } drivesFinishedSearching++; if (drivesFinishedSearching == drivesToSearch.Count) { searching = false; Invoke(new Action(delegate { progress = 1; UpdateProgress(0); btn_search.Enabled = true; // TODO make it better MainForm.Instance.RefreshGames(); MessageBox.Show("Finished searching!"); })); } }
unsafe public void EnumerateFiles(IntPtr medBuffer, ref Dictionary <ulong, FileNameAndParentFrn> files, string[] fileExtensions) { IntPtr pData = Marshal.AllocHGlobal(sizeof(UInt64) + 0x10000); try { NativeWrapper.ZeroMemory(pData, sizeof(UInt64) + 0x10000); uint outBytesReturned = 0; while (false != NativeWrapper.DeviceIoControl(_changeJournalRootHandle, NativeWrapper.FSCTL_ENUM_USN_DATA, medBuffer, sizeof(MFT_ENUM_DATA), pData, sizeof(UInt64) + 0x10000, out outBytesReturned, IntPtr.Zero)) { IntPtr pUsnRecord = new IntPtr(pData.ToInt32() + sizeof(Int64)); while (outBytesReturned > 60) { USN_RECORD usn = new USN_RECORD(pUsnRecord); if (0 != (usn.FileAttributes & NativeWrapper.FILE_ATTRIBUTE_DIRECTORY)) { // // handle directories // if (!_directories.ContainsKey(usn.FileReferenceNumber)) { _directories.Add(usn.FileReferenceNumber, new FileNameAndParentFrn(usn.FileName, usn.ParentFileReferenceNumber)); } else { // this is debug code and should be removed when we are certain that // duplicate frn's don't exist on a given drive. To date, this exception has // never been thrown. Removing this code improves performance.... throw new Exception(string.Format("Duplicate FRN: {0} for {1}", usn.FileReferenceNumber, usn.FileName)); } } else { // // handle files // // at this point we could get the * for the extension bool add = true; bool fullpath = false; if (fileExtensions != null && fileExtensions.Length != 0) { if (fileExtensions[0].ToString() == "*") { add = true; fullpath = true; } else { add = false; string s = Path.GetExtension(usn.FileName); foreach (string extension in fileExtensions) { if (0 == string.Compare(s, extension, true)) { add = true; break; } } } } if (add) { if (fullpath) { if (!files.ContainsKey(usn.FileReferenceNumber)) { files.Add(usn.FileReferenceNumber, new FileNameAndParentFrn(usn.FileName, usn.ParentFileReferenceNumber)); } else { FileNameAndParentFrn frn = files[usn.FileReferenceNumber]; if (0 != string.Compare(usn.FileName, frn.Name, true)) { // Log.InfoFormat( // "Attempt to add duplicate file reference number: {0} for file {1}, file from index {2}", // usn.FileReferenceNumber, usn.FileName, frn.Name); throw new Exception(string.Format("Duplicate FRN: {0} for {1}", usn.FileReferenceNumber, usn.FileName)); } } } else { if (!files.ContainsKey(usn.FileReferenceNumber)) { files.Add(usn.FileReferenceNumber, new FileNameAndParentFrn(usn.FileName, usn.ParentFileReferenceNumber)); } else { FileNameAndParentFrn frn = files[usn.FileReferenceNumber]; if (0 != string.Compare(usn.FileName, frn.Name, true)) { // Log.InfoFormat( // "Attempt to add duplicate file reference number: {0} for file {1}, file from index {2}", // usn.FileReferenceNumber, usn.FileName, frn.Name); throw new Exception(string.Format("Duplicate FRN: {0} for {1}", usn.FileReferenceNumber, usn.FileName)); } } } } } pUsnRecord = new IntPtr(pUsnRecord.ToInt32() + usn.RecordLength); outBytesReturned -= usn.RecordLength; } Marshal.WriteInt64(medBuffer, Marshal.ReadInt64(pData, 0)); } } catch (Exception e) { Console.Error.WriteLine(e.Message); Console.Error.WriteLine(e.StackTrace); throw new ApplicationException("Error in EnumerateFiles()", e); } finally { Marshal.FreeHGlobal(pData); } }
static void Main(string[] args) { try { Console.WriteLine("### " + Constants.APP_SIGNATURE + " ###"); Console.WriteLine("### " + Constants.APP_URL + " ###\n"); Console.Write("Processing...\r"); Int32 unixTimestampInit = (Int32)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds; string driveLetter = Properties.Settings.Default.search_volume; string fileNamePath = Properties.Settings.Default.report_folder; string fileExtension = Properties.Settings.Default.search_extensions; bool calcMd5 = Properties.Settings.Default.calc_md5; string fileNamePathRecommendation = "report_folder (" + fileNamePath + ") must be a valid folder, and the current user must have write access in it. The valid slash must be / and NOT \\."; if (!Directory.Exists(fileNamePath)) { Utils.Instance.ThrowErr(fileNamePathRecommendation); } else if (fileNamePath[fileNamePath.Length - 1] == '/') { fileNamePath = fileNamePath.Substring(0, fileNamePath.Length - 1); } if (driveLetter.Length > 1 || driveLetter.Contains(":") || fileNamePath.Contains("\\") || !fileExtension.Contains(".") || fileExtension.Contains("*")) { Utils.Instance.ThrowErr("\n\nCheck the config file:\n\n1. search_volume (" + driveLetter + ") must be JUST a NTFS volume/drive letter WITHOUT ':', like C, D, F, G, etc. The current user must have administration rights over this volume.\n\n" + "2. " + fileNamePathRecommendation + "\n\n" + "3. search_extensions (" + fileExtension + ") is the representation of a file extension, like .txt, .pdf, .doc, etc, WITH dot (.) WITHOUT asterisk (*)."); } nameLst = new List <string>(); Dictionary <ulong, FileNameAndParentFrn> mDict = new Dictionary <ulong, FileNameAndParentFrn>(); long totalBytes = 0l; EnumerateVolume.PInvokeWin32 mft = new EnumerateVolume.PInvokeWin32(); mft.Drive = driveLetter; mft.Drive = mft.Drive + ":"; mft.EnumerateVolume(out mDict); StringBuilder sb = new StringBuilder(); StringBuilder pathSb = null; String jsonFileNamePath = fileNamePath + "/" + driveLetter + "." + unixTimestampInit + ".json"; Console.Write("Volume: " + driveLetter + "\t\t\n"); Console.WriteLine("Report folder: " + fileNamePath); Console.WriteLine("Extension: " + fileExtension); long totalDriveSize = 0; try { totalDriveSize = Utils.Instance.GetDriveTotalSize(driveLetter); } catch (Exception e) { Utils.Instance.LogException(e); } if (File.Exists(jsonFileNamePath)) { File.Delete(jsonFileNamePath); Console.WriteLine("Old file deleted: " + jsonFileNamePath); } finalNameLst = new List <string>(); Console.WriteLine("MFT items: " + mDict.Count); foreach (KeyValuePair <UInt64, FileNameAndParentFrn> entry in mDict) { FileNameAndParentFrn file = (FileNameAndParentFrn)entry.Value; pathSb = new StringBuilder(); string extractedExtension = Utils.Instance.ExtractExtension(file.Name); if (extractedExtension != null && fileExtension.ToLower().Contains(extractedExtension.ToLower())) { pathSb.Append(driveLetter + ":\\"); Utils.Instance.SearchId(file.ParentFrn, mDict); nameLst.Reverse(); foreach (var item in nameLst) { pathSb.Append(item + "\\"); } pathSb.Append(file.Name); finalNameLst.Add(pathSb.ToString()); Console.Write("File references found: " + finalNameLst.Count + "\r"); nameLst.Clear(); } } Console.WriteLine(); sb.AppendLine("{\"initTime\": " + unixTimestampInit + ", \"search_volume\": \"" + driveLetter + "\", \"report_folder\": \"" + fileNamePath + "\", \"search_extensions\": \"" + fileExtension + "\", \"totalDriveSize\": " + totalDriveSize + ", \"objectLst\": ["); String comma = ", "; int notFoundCount = 0; int ownerExceptionCount = 0; int machineNameExceptionCount = 0; int fqdnExceptionCount = 0; int md5ExceptionCount = 0; for (int i = 0; i < finalNameLst.Count; i++) { String item = finalNameLst[i]; if (File.Exists(item)) { if (i + 1 == finalNameLst.Count) { comma = ""; } long fileSize = new FileInfo(item).Length; DateTime fileCreationDate = File.GetCreationTime(item); DateTime fileUpdateDate = File.GetLastWriteTime(item); string owner = "n/a"; string machineName = "n/a"; string fqdn = "n/a"; string md5Hash = ""; if (calcMd5) { try { byte[] byteArray = File.ReadAllBytes(item); string strMd5 = Utils.Instance.ByteArrayToMd5HashString(byteArray); md5Hash = ", \"md5\": \"" + strMd5 + "\""; } catch (Exception e) { md5ExceptionCount++; md5Hash = ", \"md5\": \"n/a\""; Utils.Instance.LogException(e); } } try { owner = Utils.Instance.GetOwnerName(item); } catch (Exception e) { ownerExceptionCount++; Utils.Instance.LogException(e); } finally { owner = Uri.EscapeDataString(owner); } try { machineName = Environment.MachineName; } catch (Exception e) { machineNameExceptionCount++; Utils.Instance.LogException(e); } finally { machineName = Uri.EscapeDataString(machineName); } try { fqdn = Utils.Instance.GetFQDN(); } catch (Exception e) { fqdnExceptionCount++; Utils.Instance.LogException(e); } finally { fqdn = Uri.EscapeDataString(fqdn); } sb.AppendLine("{ \"fileName\": \"" + Uri.EscapeDataString(item) + "\", \"fileSize\": " + fileSize + ", \"fileCreationDate\": \"" + fileCreationDate + "\", \"fileUpdateDate\": \"" + fileUpdateDate + "\", \"fileAuthor\": \"" + owner + "\", \"fqdn\": \"" + fqdn + "\" " + md5Hash + "}" + comma); totalBytes = fileSize + totalBytes; } else { notFoundCount++; } Console.Write("Inspecting file: " + (i + 1) + "/" + finalNameLst.Count + "\r"); } Int32 unixTimestampEnd = (Int32)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds; sb.AppendLine("], \"endTime\": " + unixTimestampEnd + " }"); Utils.Instance.WriteToFile(sb.ToString(), jsonFileNamePath); Console.WriteLine("\nFile references not found: " + notFoundCount); Console.WriteLine("FQDN resolution errors: " + fqdnExceptionCount); Console.WriteLine("Machine Name resolution errors: " + machineNameExceptionCount); Console.WriteLine("File ownership resolution errors: " + ownerExceptionCount); Console.WriteLine("MD5 hash errors: " + md5ExceptionCount); Console.WriteLine("\nTotal bytes: " + Utils.Instance.FormatBytesLength(totalBytes)); Console.WriteLine("Process ended. Check the results in: " + jsonFileNamePath); Console.WriteLine("\n[PRESS ENTER]"); Console.ReadLine(); } catch (Exception e) { Utils.Instance.ThrowErr(e.Message); Utils.Instance.LogException(e); } }
private static void ExecuteTask(StartGameData data) { switch (data.Task) { case GameStarterTask.StartGame: { string gamePath = data.Parameters[0]; string gameArgs = data.Parameters[1]; string gameWorkingDir = data.Parameters[2]; ConsoleU.WriteLine($"Start game: EXE: {gamePath} ARGS: {gameArgs} WORKDIR: {gameWorkingDir}", Palette.Feedback); StartGame(gamePath, gameArgs, gameWorkingDir); } break; case GameStarterTask.KillMutex: { Log.WriteLine($"Kill Mutex Task"); string procId = data.Parameters[0]; string[] mutexes = new string[data.Parameters.Length - 1]; for (int j = 1; j < data.Parameters.Length; j++) { string m = data.Parameters[j]; mutexes[j - 1] = m; } KillMutex(procId, mutexes); WriteToDataFile(Assembly.GetEntryAssembly().Location, true.ToString()); } break; case GameStarterTask.RenameMutex: { Log.WriteLine($"Rename Mutex Task"); string procId = data.Parameters[0]; string[] mutexes = new string[data.Parameters.Length - 1]; for (int j = 1; j < data.Parameters.Length; j++) { string m = data.Parameters[j]; mutexes[j - 1] = m; } KillMutex(procId, mutexes); WriteToDataFile(Assembly.GetEntryAssembly().Location, true.ToString()); } break; case GameStarterTask.ScanKillMutex: { Log.WriteLine($"Scan Kill Mutex"); List <int> processIds = new List <int>(); for (int j = 0; j < data.Parameters.Length; j++) { string scanMutexDataRaw = data.Parameters[j]; ScanMutexData scanMutex = JsonConvert.DeserializeObject <ScanMutexData>(scanMutexDataRaw); Log.WriteLine($"Kill Mutex for process {scanMutex.ProcessName}"); for (; ;) { Process[] procs = Process.GetProcessesByName(scanMutex.ProcessName); if (procs == null || procs.Length == 0) { Thread.Sleep(250); } else { // kill mutexes bool killedMutexes = false; for (int k = 0; k < procs.Length; k++) { Process p = procs[k]; if (processIds.Contains(p.Id)) { continue; } // start other process, as the mutexes are only truly killed // when the process is ended if (scanMutex.ShouldRename) { StartGameUtil.RenameMutex(p, scanMutex.Mutexes); } else { StartGameUtil.KillMutex(p, scanMutex.Mutexes); } //KillMutex(p.Id.ToString(), scanMutex.Mutexes); processIds.Add(p.Id); killedMutexes = true; break; } if (killedMutexes) { Log.WriteLine($"Killed all mutexes for process {scanMutex.ProcessName}"); WriteToDataFile(Assembly.GetEntryAssembly().Location, true.ToString()); break; } } } } } break; case GameStarterTask.MultipleTasks: { Log.WriteLine($"Multiple tasks"); for (int j = 0; j < data.Parameters.Length; j++) { string taskDataRaw = data.Parameters[j]; StartGameData taskData = JsonConvert.DeserializeObject <StartGameData>(taskDataRaw); Log.WriteLine($"Executing task {j + 1}"); ExecuteTask(taskData); } } break; case GameStarterTask.QueryMutex: { string procId = data.Parameters[0]; string[] mutexes = new string[data.Parameters.Length - 1]; for (int j = 1; j < data.Parameters.Length; j++) { string m = data.Parameters[j]; mutexes[j - 1] = m; } } break; case GameStarterTask.ListMonitors: break; case GameStarterTask.ScanGames: { // initialize game manager to read available handlers GameManager gameManager = new GameManager(); List <string> games = new List <string>(); for (int j = 0; j < data.Parameters.Length; j++) { string driveName = data.Parameters[j]; //SearchStorageInfo info = JsonConvert.DeserializeObject<SearchStorageInfo>(storageData); DriveInfo drive = new DriveInfo(driveName); if (!drive.IsReady) { continue; } Log.WriteLine($"> Searching drive {drive.Name} for game executables"); Dictionary <ulong, FileNameAndParentFrn> allExes = new Dictionary <ulong, FileNameAndParentFrn>(); MFTReader mft = new MFTReader(); mft.Drive = drive.RootDirectory.FullName; // TODO: search only for specific games? mft.EnumerateVolume(out allExes, new string[] { ".exe" }); foreach (KeyValuePair <UInt64, FileNameAndParentFrn> entry in allExes) { FileNameAndParentFrn file = (FileNameAndParentFrn)entry.Value; string name = file.Name; string lower = name.ToLower(); string path = mft.GetFullPath(file); if (path.Contains("$Recycle.Bin") || path.Contains(@"\Instance")) { // noope continue; } if (GameManager.Instance.AnyGame(lower)) { Log.WriteLine($"Found game at path: {path}"); games.Add(path); } } } WriteToDataFile(Assembly.GetEntryAssembly().Location, JsonConvert.SerializeObject(games)); } break; case GameStarterTask.SymlinkFolders: for (int j = 0; j < data.Parameters.Length; j++) { string symData = data.Parameters[j]; Log.WriteLine($"Symlink game instance {j + 1}"); SymlinkGameData gameData = JsonConvert.DeserializeObject <SymlinkGameData>(symData); int exitCode; WinDirectoryUtil.LinkDirectory(gameData.SourcePath, new DirectoryInfo(gameData.SourcePath), gameData.DestinationPath, out exitCode, gameData.DirExclusions, gameData.FileExclusions, gameData.FileCopies, true); } WriteToDataFile(Assembly.GetEntryAssembly().Location, true.ToString()); break; } }