/// <summary> /// Ashita injection method. /// </summary> /// <param name="config"></param> /// <returns></returns> public static Boolean DoInjection(Configuration config) { // Ensure the configuration file exists.. if (config == null || !File.Exists(config.FilePath)) { Error("Invalid configuration file; could not launch."); return false; } // Obtain path to PlayOnline.. var polPath = RegisteryHelper.GetValue<String>(String.Format("HKEY_LOCAL_MACHINE\\SOFTWARE\\PlayOnline{0}\\InstallFolder", (config.PolVersion == "JP") ? "" : config.PolVersion), "1000"); if (string.IsNullOrEmpty(polPath)) { Error("Failed to read PlayOnline path from registry."); return false; } // Build path to Final Fantasy registry settings.. var ffxiPath = String.Format( "HKEY_LOCAL_MACHINE\\SOFTWARE\\PlayOnline{0}\\{1}\\FinalFantasyXI{2}", (config.PolVersion == "JP") ? "" : config.PolVersion, (config.PolVersion == "JP") ? "SQUARE" : "SquareEnix", config.TestServer ? "TestClient" : ""); // Write FFXI window resolution.. RegisteryHelper.SetValue(ffxiPath, "0001", config.ResolutionX); RegisteryHelper.SetValue(ffxiPath, "0002", config.ResolutionY); // Write FFXI background resolution.. if (config.BackgroundX != -1 && config.BackgroundY != -1) { RegisteryHelper.SetValue(ffxiPath, "0003", config.BackgroundX); RegisteryHelper.SetValue(ffxiPath, "0004", config.BackgroundY); } // Write FFXI menu resolution.. if (config.MenuX != -1 && config.MenuY != -1) { RegisteryHelper.SetValue(ffxiPath, "0037", config.MenuX); RegisteryHelper.SetValue(ffxiPath, "0038", config.MenuY); } // Write FFXI misc settings.. RegisteryHelper.SetValue(ffxiPath, "0021", 1); // Hardware Mouse RegisteryHelper.SetValue(ffxiPath, "0034", (config.ShowBorder) ? 1 : 0); // Attempt to launch the game suspended.. var bootFile = (config.BootFile.Length > 0) ? config.BootFile : polPath + "\\pol.exe"; var procId = ManagedInjector.CreateSuspended(bootFile, String.Format("{0} {1}", bootFile, config.BootCommand)); if (procId == 0) { var errorMessage = String.Format( "Ashita failed to load the boot file.\r\n" + "PlayOnline Path:\r\n{0}\r\n" + "Last Error Code: {1}", bootFile, Marshal.GetLastWin32Error().ToString() ); Error(errorMessage); return false; } // Create configuration mapping.. var fileHandle = NativeMethods.CreateFileMapping(IntPtr.Zero, IntPtr.Zero, (int)NativeMethods.MemoryProtection.ReadWrite, 0, Marshal.SizeOf(typeof(AshitaSettings)), String.Format("AshitaMMFSettings_{0}", procId)); if (fileHandle == IntPtr.Zero) { Error("Ashita failed to load configuration mapping."); return false; } // Map view of file into local space.. var fileMapping = NativeMethods.MapViewOfFile(fileHandle, 0x001F, 0, 0, 0); if (fileMapping == IntPtr.Zero) { Error("Ashita failed to map view of configuration file."); return false; } // Write settings to MMF.. var settings = new AshitaSettings { InstallPath = AppDomain.CurrentDomain.BaseDirectory, ConfigPath = config.FilePath, Language = config.Language, IsLoaded = false }; Marshal.StructureToPtr(settings, fileMapping, true); // Inject Ashita into remote target.. if (!ManagedInjector.InjectModule(procId, AppDomain.CurrentDomain.BaseDirectory + "\\Ashita Core.dll", false)) { ManagedInjector.KillProcess(procId); Error("Ashita failed to inject into PlayOnline."); return false; } // Resume the game and play! ManagedInjector.ResumeProcess(procId); // Wait for Ashita to load our configurations.. var loadedAttempts = 0; while (settings.IsLoaded == false) { if (loadedAttempts == 5) { // Cleanup.. NativeMethods.UnmapViewOfFile(fileMapping); NativeMethods.CloseHandle(fileHandle); ManagedInjector.KillProcess(procId); Error("Ashita failed to process the selected configuration."); return false; } settings = (AshitaSettings)Marshal.PtrToStructure(fileMapping, typeof(AshitaSettings)); Thread.Sleep(1000); loadedAttempts++; } // Cleanup.. NativeMethods.UnmapViewOfFile(fileMapping); NativeMethods.CloseHandle(fileHandle); return true; }
/// <summary> /// Ashita injection method. /// </summary> /// <param name="config"></param> /// <returns></returns> public static Boolean DoInjection(Configuration config) { // Ensure the configuration file exists.. if (config == null || !File.Exists(config.FilePath)) { Error("Invalid configuration file; could not launch."); return(false); } // Obtain path to PlayOnline.. var polPath = RegisteryHelper.GetValue <String>(String.Format("HKEY_LOCAL_MACHINE\\SOFTWARE\\PlayOnline{0}\\InstallFolder", (config.PolVersion == "JP") ? "" : config.PolVersion), "1000"); if (string.IsNullOrEmpty(polPath)) { Error("Failed to read PlayOnline path from registry."); return(false); } // Build path to Final Fantasy registry settings.. var ffxiPath = String.Format( "HKEY_LOCAL_MACHINE\\SOFTWARE\\PlayOnline{0}\\{1}\\FinalFantasyXI{2}", (config.PolVersion == "JP") ? "" : config.PolVersion, (config.PolVersion == "JP") ? "SQUARE" : "SquareEnix", config.TestServer ? "TestClient" : ""); // Write FFXI window resolution.. RegisteryHelper.SetValue(ffxiPath, "0001", config.ResolutionX); RegisteryHelper.SetValue(ffxiPath, "0002", config.ResolutionY); // Write FFXI background resolution.. if (config.BackgroundX != -1 && config.BackgroundY != -1) { RegisteryHelper.SetValue(ffxiPath, "0003", config.BackgroundX); RegisteryHelper.SetValue(ffxiPath, "0004", config.BackgroundY); } // Write FFXI menu resolution.. if (config.MenuX != -1 && config.MenuY != -1) { RegisteryHelper.SetValue(ffxiPath, "0037", config.MenuX); RegisteryHelper.SetValue(ffxiPath, "0038", config.MenuY); } // Write FFXI misc settings.. RegisteryHelper.SetValue(ffxiPath, "0021", 1); // Hardware Mouse RegisteryHelper.SetValue(ffxiPath, "0034", (config.ShowBorder) ? 1 : 0); // Attempt to launch the game suspended.. var bootFile = (config.BootFile.Length > 0) ? config.BootFile : polPath + "\\pol.exe"; var procId = ManagedInjector.CreateSuspended(bootFile, String.Format("{0} {1}", bootFile, config.BootCommand)); if (procId == 0) { var errorMessage = String.Format( "Ashita failed to load the boot file.\r\n" + "PlayOnline Path:\r\n{0}\r\n" + "Last Error Code: {1}", bootFile, Marshal.GetLastWin32Error().ToString() ); Error(errorMessage); return(false); } // Create configuration mapping.. var fileHandle = NativeMethods.CreateFileMapping(IntPtr.Zero, IntPtr.Zero, (int)NativeMethods.MemoryProtection.ReadWrite, 0, Marshal.SizeOf(typeof(AshitaSettings)), String.Format("AshitaMMFSettings_{0}", procId)); if (fileHandle == IntPtr.Zero) { Error("Ashita failed to load configuration mapping."); return(false); } // Map view of file into local space.. var fileMapping = NativeMethods.MapViewOfFile(fileHandle, 0x001F, 0, 0, 0); if (fileMapping == IntPtr.Zero) { Error("Ashita failed to map view of configuration file."); return(false); } // Write settings to MMF.. var settings = new AshitaSettings { InstallPath = AppDomain.CurrentDomain.BaseDirectory, ConfigPath = config.FilePath, Language = config.Language, IsLoaded = false }; Marshal.StructureToPtr(settings, fileMapping, true); // Inject Ashita into remote target.. if (!ManagedInjector.InjectModule(procId, AppDomain.CurrentDomain.BaseDirectory + "\\Ashita Core.dll", false)) { ManagedInjector.KillProcess(procId); Error("Ashita failed to inject into PlayOnline."); return(false); } // Resume the game and play! ManagedInjector.ResumeProcess(procId); // Wait for Ashita to load our configurations.. var loadedAttempts = 0; while (settings.IsLoaded == false) { if (loadedAttempts == 5) { // Cleanup.. NativeMethods.UnmapViewOfFile(fileMapping); NativeMethods.CloseHandle(fileHandle); ManagedInjector.KillProcess(procId); Error("Ashita failed to process the selected configuration."); return(false); } settings = (AshitaSettings)Marshal.PtrToStructure(fileMapping, typeof(AshitaSettings)); Thread.Sleep(1000); loadedAttempts++; } // Cleanup.. NativeMethods.UnmapViewOfFile(fileMapping); NativeMethods.CloseHandle(fileHandle); return(true); }