2019-04-22 13:35:40 -04:00
# include " mainwindow.h "
2018-07-21 15:40:16 -07:00
# include "ui_mainwindow.h"
2018-12-18 12:14:54 -05:00
# include "defs.h"
2020-02-28 00:34:34 -07:00
# include "module.h"
2020-03-02 23:38:51 -07:00
# include "extenwindow.h"
# include "host/host.h"
2020-02-28 00:34:34 -07:00
# include "host/hookcode.h"
2019-02-18 22:39:04 -05:00
# include <shellapi.h>
2020-03-05 01:17:45 -07:00
# include <process.h>
2020-02-28 04:21:07 -07:00
# include <QRegularExpression>
2019-09-10 21:59:59 -04:00
# include <QStringListModel>
2019-09-11 14:27:57 -04:00
# include <QScrollBar>
2019-07-13 19:15:43 +05:30
# include <QMenu>
2019-06-16 15:28:59 -04:00
# include <QDialogButtonBox>
2019-01-11 17:14:49 -05:00
# include <QFileDialog>
2019-07-13 19:15:43 +05:30
# include <QFontDialog>
2018-07-23 12:25:02 -07:00
2019-02-27 11:33:17 -05:00
extern const char * ATTACH ;
extern const char * LAUNCH ;
2021-01-15 06:07:23 -07:00
extern const char * CONFIG ;
2019-02-27 11:33:17 -05:00
extern const char * DETACH ;
2019-06-27 12:39:44 +05:30
extern const char * FORGET ;
2019-02-27 11:33:17 -05:00
extern const char * ADD_HOOK ;
2019-06-10 01:49:11 -04:00
extern const char * REMOVE_HOOKS ;
2019-02-27 11:33:17 -05:00
extern const char * SAVE_HOOKS ;
2019-06-16 15:28:59 -04:00
extern const char * SEARCH_FOR_HOOKS ;
2019-02-27 11:33:17 -05:00
extern const char * SETTINGS ;
extern const char * EXTENSIONS ;
2019-07-13 19:15:43 +05:30
extern const char * FONT ;
2019-02-27 11:33:17 -05:00
extern const char * SELECT_PROCESS ;
extern const char * ATTACH_INFO ;
2019-07-02 11:26:04 +05:30
extern const char * SELECT_PROCESS_INFO ;
extern const char * FROM_COMPUTER ;
2019-02-27 11:33:17 -05:00
extern const char * PROCESSES ;
extern const char * CODE_INFODUMP ;
2020-04-25 20:07:42 -06:00
extern const char * FAILED_TO_CREATE_CONFIG_FILE ;
2019-06-09 06:19:54 -04:00
extern const char * HOOK_SEARCH_UNSTABLE_WARNING ;
2019-06-16 15:28:59 -04:00
extern const char * SEARCH_CJK ;
2019-06-02 02:09:17 -04:00
extern const char * SEARCH_PATTERN ;
extern const char * SEARCH_DURATION ;
2019-11-10 14:13:54 -05:00
extern const char * SEARCH_MODULE ;
2019-06-02 02:09:17 -04:00
extern const char * PATTERN_OFFSET ;
extern const char * MIN_ADDRESS ;
extern const char * MAX_ADDRESS ;
2019-06-09 00:48:30 -04:00
extern const char * STRING_OFFSET ;
2019-07-16 21:55:40 +05:30
extern const char * MAX_HOOK_SEARCH_RECORDS ;
2019-06-06 23:53:37 -04:00
extern const char * HOOK_SEARCH_FILTER ;
2019-08-19 16:15:08 -04:00
extern const char * SEARCH_FOR_TEXT ;
extern const char * TEXT ;
extern const char * CODEPAGE ;
2019-06-02 02:09:17 -04:00
extern const char * START_HOOK_SEARCH ;
extern const char * SAVE_SEARCH_RESULTS ;
extern const char * TEXT_FILES ;
2019-06-10 22:47:58 -04:00
extern const char * DOUBLE_CLICK_TO_REMOVE_HOOK ;
2019-02-27 11:33:17 -05:00
extern const char * SAVE_SETTINGS ;
extern const char * USE_JP_LOCALE ;
extern const char * FILTER_REPETITION ;
2019-06-27 12:39:44 +05:30
extern const char * AUTO_ATTACH ;
extern const char * ATTACH_SAVED_ONLY ;
2019-08-10 04:18:30 -04:00
extern const char * SHOW_SYSTEM_PROCESSES ;
2019-02-27 11:33:17 -05:00
extern const char * DEFAULT_CODEPAGE ;
extern const char * FLUSH_DELAY ;
extern const char * MAX_BUFFER_SIZE ;
2019-08-12 11:10:33 -04:00
extern const char * MAX_HISTORY_SIZE ;
2020-03-05 01:17:45 -07:00
extern const char * CONFIG_JP_LOCALE ;
2019-02-27 11:33:17 -05:00
extern const wchar_t * ABOUT ;
extern const wchar_t * CL_OPTIONS ;
extern const wchar_t * LAUNCH_FAILED ;
extern const wchar_t * INVALID_CODE ;
2020-03-02 23:38:51 -07:00
namespace
2018-07-21 15:40:16 -07:00
{
2020-03-02 23:38:51 -07:00
constexpr auto HOOK_SAVE_FILE = u8 " SavedHooks.txt " ;
constexpr auto GAME_SAVE_FILE = u8 " SavedGames.txt " ;
2020-03-05 01:17:45 -07:00
enum LaunchWithJapaneseLocale { PROMPT , ALWAYS , NEVER } ;
2020-03-16 02:56:04 -06:00
Ui : : MainWindow ui ;
2020-03-02 23:38:51 -07:00
std : : atomic < DWORD > selectedProcessId = 0 ;
ExtenWindow * extenWindow = nullptr ;
std : : unordered_set < DWORD > alreadyAttached ;
bool autoAttach = false , autoAttachSavedOnly = true ;
bool showSystemProcesses = false ;
uint64_t savedThreadCtx = 0 , savedThreadCtx2 = 0 ;
wchar_t savedThreadCode [ 1000 ] = { } ;
TextThread * current = nullptr ;
MainWindow * This = nullptr ;
QString TextThreadString ( TextThread & thread )
2018-12-14 22:26:49 -05:00
{
2020-03-02 23:38:51 -07:00
return QString ( " %1:%2:%3:%4:%5: %6 " ) . arg (
QString : : number ( thread . handle , 16 ) ,
QString : : number ( thread . tp . processId , 16 ) ,
QString : : number ( thread . tp . addr , 16 ) ,
QString : : number ( thread . tp . ctx , 16 ) ,
QString : : number ( thread . tp . ctx2 , 16 )
) . toUpper ( ) . arg ( S ( thread . name ) ) ;
2018-12-14 22:26:49 -05:00
}
2018-07-23 22:57:54 -07:00
2020-03-02 23:38:51 -07:00
ThreadParam ParseTextThreadString ( QString ttString )
2019-01-06 02:57:52 -05:00
{
2020-03-02 23:38:51 -07:00
QStringList threadParam = ttString . split ( " : " ) ;
return { threadParam [ 1 ] . toUInt ( nullptr , 16 ) , threadParam [ 2 ] . toULongLong ( nullptr , 16 ) , threadParam [ 3 ] . toULongLong ( nullptr , 16 ) , threadParam [ 4 ] . toULongLong ( nullptr , 16 ) } ;
}
2018-07-23 22:57:54 -07:00
2020-03-02 23:38:51 -07:00
std : : array < InfoForExtension , 10 > GetSentenceInfo ( TextThread & thread )
2020-02-28 02:34:07 -07:00
{
2020-04-25 20:34:53 -06:00
void ( * AddText ) ( int64_t , const wchar_t * ) = [ ] ( int64_t number , const wchar_t * text )
{
QMetaObject : : invokeMethod ( This , [ number , text = std : : wstring ( text ) ] { if ( TextThread * thread = Host : : GetThread ( number ) ) thread - > Push ( text . c_str ( ) ) ; } ) ;
} ;
2020-03-02 23:38:51 -07:00
void ( * AddSentence ) ( int64_t , const wchar_t * ) = [ ] ( int64_t number , const wchar_t * sentence )
{
// pointer from Host::GetThread may not stay valid unless on main thread
2020-04-25 20:07:42 -06:00
QMetaObject : : invokeMethod ( This , [ number , sentence = std : : wstring ( sentence ) ] { if ( TextThread * thread = Host : : GetThread ( number ) ) thread - > AddSentence ( sentence ) ; } ) ;
2020-03-02 23:38:51 -07:00
} ;
DWORD ( * GetSelectedProcessId ) ( ) = [ ] { return selectedProcessId . load ( ) ; } ;
return
{ {
{ " current select " , & thread = = current } ,
{ " text number " , thread . handle } ,
{ " process id " , thread . tp . processId } ,
{ " hook address " , ( int64_t ) thread . tp . addr } ,
{ " text handle " , thread . handle } ,
{ " text name " , ( int64_t ) thread . name . c_str ( ) } ,
{ " void (*AddSentence)(int64_t number, const wchar_t* sentence) " , ( int64_t ) AddSentence } ,
2020-04-25 20:34:53 -06:00
{ " void (*AddText)(int64_t number, const wchar_t* text) " , ( int64_t ) AddText } ,
2020-03-02 23:38:51 -07:00
{ " DWORD (*GetSelectedProcessId)() " , ( int64_t ) GetSelectedProcessId } ,
{ nullptr , 0 } // nullptr marks end of info array
} } ;
2020-02-28 02:34:07 -07:00
}
2018-07-24 10:39:02 -07:00
2020-03-02 23:38:51 -07:00
std : : optional < std : : wstring > UserSelectedProcess ( )
2018-07-25 10:46:59 -07:00
{
2020-03-02 23:38:51 -07:00
QStringList savedProcesses = QString : : fromUtf8 ( QTextFile ( GAME_SAVE_FILE , QIODevice : : ReadOnly ) . readAll ( ) ) . split ( " \n " , QString : : SkipEmptyParts ) ;
std : : reverse ( savedProcesses . begin ( ) , savedProcesses . end ( ) ) ;
savedProcesses . removeDuplicates ( ) ;
savedProcesses . insert ( 1 , FROM_COMPUTER ) ;
QString process = QInputDialog : : getItem ( This , SELECT_PROCESS , SELECT_PROCESS_INFO , savedProcesses , 0 , true , & ok , Qt : : WindowCloseButtonHint ) ;
if ( process = = FROM_COMPUTER ) process = QDir : : toNativeSeparators ( QFileDialog : : getOpenFileName ( This , SELECT_PROCESS , " C: \\ " , PROCESSES ) ) ;
if ( ok & & process . contains ( ' \\ ' ) ) return S ( process ) ;
return { } ;
}
2018-07-23 22:57:54 -07:00
2020-03-02 23:38:51 -07:00
void AttachProcess ( )
2018-10-08 00:26:43 -04:00
{
2020-03-02 23:38:51 -07:00
QMultiHash < QString , DWORD > allProcesses ;
for ( auto [ processId , processName ] : GetAllProcesses ( ) )
2020-12-14 06:26:01 -07:00
if ( processName & & ( showSystemProcesses | | processName - > find ( L " : \\ Windows \\ " ) = = std : : string : : npos ) )
2020-03-02 23:38:51 -07:00
allProcesses . insert ( QFileInfo ( S ( processName . value ( ) ) ) . fileName ( ) , processId ) ;
QStringList processList ( allProcesses . uniqueKeys ( ) ) ;
processList . sort ( Qt : : CaseInsensitive ) ;
if ( QString process = QInputDialog : : getItem ( This , SELECT_PROCESS , ATTACH_INFO , processList , 0 , true , & ok , Qt : : WindowCloseButtonHint ) ; ok )
if ( process . toInt ( nullptr , 0 ) ) Host : : InjectProcess ( process . toInt ( nullptr , 0 ) ) ;
else for ( auto processId : allProcesses . values ( process ) ) Host : : InjectProcess ( processId ) ;
}
2018-08-22 15:11:40 -04:00
2020-03-02 23:38:51 -07:00
void LaunchProcess ( )
2019-06-16 22:57:41 -04:00
{
2020-03-02 23:38:51 -07:00
std : : wstring process ;
if ( auto selected = UserSelectedProcess ( ) ) process = selected . value ( ) ;
else return ;
std : : wstring path = std : : wstring ( process ) . erase ( process . rfind ( L ' \\ ' ) ) ;
2019-07-02 11:26:04 +05:30
2020-03-02 23:38:51 -07:00
PROCESS_INFORMATION info = { } ;
2020-11-02 06:27:21 -07:00
auto useLocale = Settings ( ) . value ( CONFIG_JP_LOCALE , PROMPT ) . toInt ( ) ;
2020-03-05 01:17:45 -07:00
if ( ! x64 & & ( useLocale = = ALWAYS | | ( useLocale = = PROMPT & & QMessageBox : : question ( This , SELECT_PROCESS , USE_JP_LOCALE ) = = QMessageBox : : Yes ) ) )
2019-01-11 17:14:49 -05:00
{
2020-03-02 23:38:51 -07:00
if ( HMODULE localeEmulator = LoadLibraryW ( L " LoaderDll " ) )
2019-01-13 03:40:00 -05:00
{
2020-03-02 23:38:51 -07:00
// see https://github.com/xupefei/Locale-Emulator/blob/aa99dec3b25708e676c90acf5fed9beaac319160/LEProc/LoaderWrapper.cs#L252
struct
{
ULONG AnsiCodePage = SHIFT_JIS ;
ULONG OemCodePage = SHIFT_JIS ;
ULONG LocaleID = LANG_JAPANESE ;
ULONG DefaultCharset = SHIFTJIS_CHARSET ;
ULONG HookUiLanguageApi = FALSE ;
WCHAR DefaultFaceName [ LF_FACESIZE ] = { } ;
TIME_ZONE_INFORMATION Timezone ;
ULONG64 Unused = 0 ;
} LEB ;
GetTimeZoneInformation ( & LEB . Timezone ) ;
( ( LONG ( __stdcall * ) ( decltype ( & LEB ) , LPCWSTR appName , LPWSTR commandLine , LPCWSTR currentDir , void * , void * , PROCESS_INFORMATION * , void * , void * , void * , void * ) )
GetProcAddress ( localeEmulator , " LeCreateProcess " ) ) ( & LEB , process . c_str ( ) , NULL , path . c_str ( ) , NULL , NULL , & info , NULL , NULL , NULL , NULL ) ;
}
2019-01-13 03:40:00 -05:00
}
2020-03-02 23:38:51 -07:00
if ( info . hProcess = = NULL )
{
STARTUPINFOW DUMMY = { sizeof ( DUMMY ) } ;
CreateProcessW ( process . c_str ( ) , NULL , nullptr , nullptr , FALSE , 0 , nullptr , path . c_str ( ) , & DUMMY , & info ) ;
}
if ( info . hProcess = = NULL ) return Host : : AddConsoleOutput ( LAUNCH_FAILED ) ;
Host : : InjectProcess ( info . dwProcessId ) ;
CloseHandle ( info . hProcess ) ;
CloseHandle ( info . hThread ) ;
2019-06-10 01:49:11 -04:00
}
2020-03-02 23:38:51 -07:00
2021-01-15 06:07:23 -07:00
void ConfigureProcess ( )
2020-03-05 01:17:45 -07:00
{
if ( auto processName = GetModuleFilename ( selectedProcessId ) ) if ( int last = processName - > rfind ( L ' \\ ' ) + 1 )
{
2020-12-14 06:26:01 -07:00
std : : wstring configFile = std : : wstring ( processName . value ( ) ) . replace ( last , std : : string : : npos , GAME_CONFIG_FILE ) ;
2020-03-05 01:17:45 -07:00
if ( ! std : : filesystem : : exists ( configFile ) ) QTextFile ( S ( configFile ) , QFile : : WriteOnly ) . write ( " see https://github.com/Artikash/Textractor/wiki/Game-configuration-file " ) ;
2020-04-25 20:07:42 -06:00
if ( std : : filesystem : : exists ( configFile ) ) _wspawnlp ( _P_DETACH , L " notepad " , L " notepad " , configFile . c_str ( ) , NULL ) ;
2021-01-15 06:07:23 -07:00
else QMessageBox : : critical ( This , CONFIG , QString ( FAILED_TO_CREATE_CONFIG_FILE ) . arg ( S ( configFile ) ) ) ;
2020-03-05 01:17:45 -07:00
}
}
2020-03-02 23:38:51 -07:00
void DetachProcess ( )
2019-01-11 17:14:49 -05:00
{
2020-03-02 23:38:51 -07:00
try { Host : : DetachProcess ( selectedProcessId ) ; }
catch ( std : : out_of_range ) { }
2019-01-11 17:14:49 -05:00
}
2018-07-23 22:57:54 -07:00
2020-03-02 23:38:51 -07:00
void ForgetProcess ( )
2019-06-27 12:39:44 +05:30
{
2020-03-05 01:17:45 -07:00
auto processName = GetModuleFilename ( selectedProcessId ) ;
2020-03-02 23:38:51 -07:00
if ( ! processName ) processName = UserSelectedProcess ( ) ;
DetachProcess ( ) ;
if ( ! processName ) return ;
for ( auto file : { GAME_SAVE_FILE , HOOK_SAVE_FILE } )
{
QStringList lines = QString : : fromUtf8 ( QTextFile ( file , QIODevice : : ReadOnly ) . readAll ( ) ) . split ( " \n " , QString : : SkipEmptyParts ) ;
lines . erase ( std : : remove_if ( lines . begin ( ) , lines . end ( ) , [ & ] ( const QString & line ) { return line . contains ( S ( processName . value ( ) ) ) ; } ) , lines . end ( ) ) ;
QTextFile ( file , QIODevice : : WriteOnly | QIODevice : : Truncate ) . write ( lines . join ( " \n " ) . append ( " \n " ) . toUtf8 ( ) ) ;
}
2019-06-27 12:39:44 +05:30
}
2020-03-02 23:38:51 -07:00
void FindHooks ( ) ;
2019-08-06 11:51:17 -04:00
2020-03-02 23:38:51 -07:00
void AddHook ( QString hook )
{
if ( QString hookCode = QInputDialog : : getText ( This , ADD_HOOK , CODE_INFODUMP , QLineEdit : : Normal , hook , & ok , Qt : : WindowCloseButtonHint ) ; ok )
2019-08-19 16:15:08 -04:00
if ( hookCode . startsWith ( " S " ) | | hookCode . startsWith ( " /S " ) ) FindHooks ( ) ;
2020-03-02 23:38:51 -07:00
else if ( auto hp = HookCode : : Parse ( S ( hookCode ) ) ) try { Host : : InsertHook ( selectedProcessId , hp . value ( ) ) ; } catch ( std : : out_of_range ) { }
2019-01-13 03:40:00 -05:00
else Host : : AddConsoleOutput ( INVALID_CODE ) ;
2020-03-02 23:38:51 -07:00
}
2018-07-25 21:48:18 -07:00
2020-03-02 23:38:51 -07:00
void AddHook ( )
2019-06-10 01:49:11 -04:00
{
2020-03-02 23:38:51 -07:00
AddHook ( " " ) ;
2019-06-10 01:49:11 -04:00
}
2020-03-02 23:38:51 -07:00
void RemoveHooks ( )
2019-06-10 01:49:11 -04:00
{
2020-03-02 23:38:51 -07:00
DWORD processId = selectedProcessId ;
std : : unordered_map < uint64_t , HookParam > hooks ;
for ( int i = 0 ; i < ui . ttCombo - > count ( ) ; + + i )
2019-06-10 01:49:11 -04:00
{
2020-03-02 23:38:51 -07:00
ThreadParam tp = ParseTextThreadString ( ui . ttCombo - > itemText ( i ) ) ;
if ( tp . processId = = selectedProcessId ) hooks [ tp . addr ] = Host : : GetThread ( tp ) . hp ;
2019-06-10 01:49:11 -04:00
}
2020-03-02 23:38:51 -07:00
auto hookList = new QListWidget ( This ) ;
hookList - > setWindowFlags ( Qt : : Window | Qt : : WindowCloseButtonHint ) ;
hookList - > setAttribute ( Qt : : WA_DeleteOnClose ) ;
hookList - > setMinimumSize ( { 300 , 50 } ) ;
hookList - > setWindowTitle ( DOUBLE_CLICK_TO_REMOVE_HOOK ) ;
2021-01-15 06:07:23 -07:00
for ( auto [ address , hp ] : hooks ) new QListWidgetItem ( QString ( hp . name ) + " @ " + QString : : number ( address , 16 ) , hookList ) ;
2020-03-02 23:38:51 -07:00
QObject : : connect ( hookList , & QListWidget : : itemDoubleClicked , [ processId , hookList ] ( QListWidgetItem * item )
{
try
{
Host : : RemoveHook ( processId , item - > text ( ) . split ( " @ " ) [ 1 ] . toULongLong ( nullptr , 16 ) ) ;
delete item ;
}
catch ( std : : out_of_range ) { hookList - > close ( ) ; }
} ) ;
hookList - > show ( ) ;
}
2019-06-10 01:49:11 -04:00
2020-03-02 23:38:51 -07:00
void SaveHooks ( )
2018-11-27 16:57:47 -05:00
{
2020-03-02 23:38:51 -07:00
auto processName = GetModuleFilename ( selectedProcessId ) ;
if ( ! processName ) return ;
QHash < uint64_t , QString > hookCodes ;
for ( int i = 0 ; i < ui . ttCombo - > count ( ) ; + + i )
2018-12-14 23:14:30 -05:00
{
2020-03-02 23:38:51 -07:00
ThreadParam tp = ParseTextThreadString ( ui . ttCombo - > itemText ( i ) ) ;
if ( tp . processId = = selectedProcessId )
{
HookParam hp = Host : : GetThread ( tp ) . hp ;
if ( ! ( hp . type & HOOK_ENGINE ) ) hookCodes [ tp . addr ] = S ( HookCode : : Generate ( hp , tp . processId ) ) ;
}
2018-12-14 23:14:30 -05:00
}
2020-03-02 23:38:51 -07:00
auto hookInfo = QStringList ( ) < < S ( processName . value ( ) ) < < hookCodes . values ( ) ;
ThreadParam tp = current - > tp ;
if ( tp . processId = = selectedProcessId ) hookInfo < < QString ( " |%1:%2:%3 " ) . arg ( tp . ctx ) . arg ( tp . ctx2 ) . arg ( S ( HookCode : : Generate ( current - > hp , tp . processId ) ) ) ;
QTextFile ( HOOK_SAVE_FILE , QIODevice : : WriteOnly | QIODevice : : Append ) . write ( ( hookInfo . join ( " , " ) + " \n " ) . toUtf8 ( ) ) ;
2018-11-27 16:57:47 -05:00
}
2018-07-26 22:42:21 -07:00
2020-03-02 23:38:51 -07:00
void FindHooks ( )
2019-08-19 16:15:08 -04:00
{
2020-03-02 23:38:51 -07:00
QMessageBox : : information ( This , SEARCH_FOR_HOOKS , HOOK_SEARCH_UNSTABLE_WARNING ) ;
2019-06-16 15:28:59 -04:00
2020-03-02 23:38:51 -07:00
DWORD processId = selectedProcessId ;
SearchParam sp = { } ;
sp . codepage = Host : : defaultCodepage ;
bool searchForText = false , customSettings = false ;
QRegularExpression filter ( " . " , QRegularExpression : : UseUnicodePropertiesOption | QRegularExpression : : DotMatchesEverythingOption ) ;
QDialog dialog ( This , Qt : : WindowCloseButtonHint ) ;
2019-08-19 16:15:08 -04:00
QFormLayout layout ( & dialog ) ;
2021-01-30 12:07:37 -07:00
QCheckBox asianCheck ( & dialog ) ;
layout . addRow ( SEARCH_CJK , & asianCheck ) ;
2020-03-02 23:38:51 -07:00
QDialogButtonBox confirm ( QDialogButtonBox : : Ok | QDialogButtonBox : : Help | QDialogButtonBox : : Retry , & dialog ) ;
2019-08-19 16:15:08 -04:00
layout . addRow ( & confirm ) ;
2020-03-02 23:38:51 -07:00
confirm . button ( QDialogButtonBox : : Ok ) - > setText ( START_HOOK_SEARCH ) ;
confirm . button ( QDialogButtonBox : : Retry ) - > setText ( SEARCH_FOR_TEXT ) ;
confirm . button ( QDialogButtonBox : : Help ) - > setText ( SETTINGS ) ;
QObject : : connect ( & confirm , & QDialogButtonBox : : clicked , [ & ] ( QAbstractButton * button )
{
if ( button = = confirm . button ( QDialogButtonBox : : Retry ) ) searchForText = true ;
if ( button = = confirm . button ( QDialogButtonBox : : Help ) ) customSettings = true ;
dialog . accept ( ) ;
} ) ;
dialog . setWindowTitle ( SEARCH_FOR_HOOKS ) ;
2019-08-19 16:15:08 -04:00
if ( ! dialog . exec ( ) ) return ;
2020-03-02 23:38:51 -07:00
if ( searchForText )
{
QDialog dialog ( This , Qt : : WindowCloseButtonHint ) ;
QFormLayout layout ( & dialog ) ;
2021-01-18 18:58:21 -07:00
QLineEdit textEdit ( & dialog ) ;
layout . addRow ( TEXT , & textEdit ) ;
QSpinBox codepageSpin ( & dialog ) ;
codepageSpin . setMaximum ( INT_MAX ) ;
codepageSpin . setValue ( sp . codepage ) ;
layout . addRow ( CODEPAGE , & codepageSpin ) ;
2020-03-02 23:38:51 -07:00
QDialogButtonBox confirm ( QDialogButtonBox : : Ok ) ;
QObject : : connect ( & confirm , & QDialogButtonBox : : accepted , & dialog , & QDialog : : accept ) ;
layout . addRow ( & confirm ) ;
if ( ! dialog . exec ( ) ) return ;
2021-01-18 18:58:21 -07:00
wcsncpy_s ( sp . text , S ( textEdit . text ( ) ) . c_str ( ) , PATTERN_SIZE - 1 ) ;
2021-01-15 06:07:23 -07:00
try { Host : : FindHooks ( selectedProcessId , sp ) ; } catch ( std : : out_of_range ) { }
2020-03-02 23:38:51 -07:00
return ;
}
2021-01-30 12:07:37 -07:00
filter . setPattern ( asianCheck . isChecked ( ) ? " [ \\ x{3000}- \\ x{a000}]{4,} " : " [ \\ x{0020}- \\ x{1000}]{4,} " ) ;
2020-03-02 23:38:51 -07:00
if ( customSettings )
{
QDialog dialog ( This , Qt : : WindowCloseButtonHint ) ;
QFormLayout layout ( & dialog ) ;
2021-01-18 18:58:21 -07:00
QLineEdit patternEdit ( x64 ? " CC CC 48 89 " : " 55 8B EC " , & dialog ) ;
assert ( QByteArray : : fromHex ( patternEdit . text ( ) . toUtf8 ( ) ) = = QByteArray ( ( const char * ) sp . pattern , sp . length ) ) ;
layout . addRow ( SEARCH_PATTERN , & patternEdit ) ;
2020-03-02 23:38:51 -07:00
for ( auto [ value , label ] : Array < int & , const char * > {
{ sp . searchTime , SEARCH_DURATION } ,
{ sp . offset , PATTERN_OFFSET } ,
{ sp . maxRecords , MAX_HOOK_SEARCH_RECORDS } ,
{ sp . codepage , CODEPAGE } ,
} )
{
auto spinBox = new QSpinBox ( & dialog ) ;
spinBox - > setMaximum ( INT_MAX ) ;
spinBox - > setValue ( value ) ;
layout . addRow ( label , spinBox ) ;
QObject : : connect ( spinBox , qOverload < int > ( & QSpinBox : : valueChanged ) , [ & value ] ( int newValue ) { value = newValue ; } ) ;
}
2021-01-18 18:58:21 -07:00
QLineEdit boundEdit ( QFileInfo ( S ( GetModuleFilename ( selectedProcessId ) . value_or ( L " " ) ) ) . fileName ( ) , & dialog ) ;
layout . addRow ( SEARCH_MODULE , & boundEdit ) ;
2020-03-02 23:38:51 -07:00
for ( auto [ value , label ] : Array < uintptr_t & , const char * > {
{ sp . minAddress , MIN_ADDRESS } ,
{ sp . maxAddress , MAX_ADDRESS } ,
{ sp . padding , STRING_OFFSET } ,
} )
{
2021-01-18 18:58:21 -07:00
auto edit = new QLineEdit ( QString : : number ( value , 16 ) , & dialog ) ;
layout . addRow ( label , edit ) ;
QObject : : connect ( edit , & QLineEdit : : textEdited , [ & value ] ( QString text ) { if ( uintptr_t newValue = text . toULongLong ( & ok , 16 ) ; ok ) value = newValue ; } ) ;
2020-03-02 23:38:51 -07:00
}
2021-01-18 18:58:21 -07:00
QLineEdit filterEdit ( filter . pattern ( ) , & dialog ) ;
layout . addRow ( HOOK_SEARCH_FILTER , & filterEdit ) ;
2020-03-02 23:38:51 -07:00
QPushButton startButton ( START_HOOK_SEARCH , & dialog ) ;
layout . addWidget ( & startButton ) ;
QObject : : connect ( & startButton , & QPushButton : : clicked , & dialog , & QDialog : : accept ) ;
if ( ! dialog . exec ( ) ) return ;
2021-01-18 18:58:21 -07:00
if ( patternEdit . text ( ) . contains ( ' . ' ) )
2020-03-02 23:38:51 -07:00
{
2021-01-18 18:58:21 -07:00
wcsncpy_s ( sp . exportModule , S ( patternEdit . text ( ) ) . c_str ( ) , MAX_MODULE_SIZE - 1 ) ;
2020-03-02 23:38:51 -07:00
sp . length = 1 ;
}
else
{
2021-01-18 18:58:21 -07:00
QByteArray pattern = QByteArray : : fromHex ( patternEdit . text ( ) . replace ( " ?? " , QString : : number ( XX , 16 ) ) . toUtf8 ( ) ) ;
2020-03-02 23:38:51 -07:00
memcpy ( sp . pattern , pattern . data ( ) , sp . length = min ( pattern . size ( ) , PATTERN_SIZE ) ) ;
}
2021-01-18 18:58:21 -07:00
wcsncpy_s ( sp . boundaryModule , S ( boundEdit . text ( ) ) . c_str ( ) , MAX_MODULE_SIZE - 1 ) ;
filter . setPattern ( filterEdit . text ( ) ) ;
2020-03-02 23:38:51 -07:00
if ( ! filter . isValid ( ) ) filter . setPattern ( " . " ) ;
}
else
{
sp . length = 0 ; // use default
}
filter . optimize ( ) ;
auto hooks = std : : make_shared < QStringList > ( ) ;
try
{
Host : : FindHooks ( processId , sp ,
[ hooks , filter ] ( HookParam hp , std : : wstring text ) { if ( filter . match ( S ( text ) ) . hasMatch ( ) ) * hooks < < sanitize ( S ( HookCode : : Generate ( hp ) + L " => " + text ) ) ; } ) ;
}
catch ( std : : out_of_range ) { return ; }
std : : thread ( [ hooks ]
{
2021-01-15 06:07:23 -07:00
for ( int lastSize = 0 ; hooks - > size ( ) = = 0 | | hooks - > size ( ) ! = lastSize ; Sleep ( 2000 ) ) lastSize = hooks - > size ( ) ;
2020-03-02 23:38:51 -07:00
QString saveFileName ;
QMetaObject : : invokeMethod ( This , [ & ]
{
auto hookList = new QListView ( This ) ;
hookList - > setWindowFlags ( Qt : : Window | Qt : : WindowCloseButtonHint ) ;
hookList - > setAttribute ( Qt : : WA_DeleteOnClose ) ;
hookList - > resize ( { 750 , 300 } ) ;
hookList - > setWindowTitle ( SEARCH_FOR_HOOKS ) ;
if ( hooks - > size ( ) > 5'000 )
{
hookList - > setUniformItemSizes ( true ) ; // they aren't actually uniform, but this improves performance
hooks - > push_back ( QString ( 2000 , ' - ' ) ) ; // dumb hack: with uniform item sizes, the last item is assumed to be the largest
}
hookList - > setModel ( new QStringListModel ( * hooks , hookList ) ) ;
QObject : : connect ( hookList , & QListView : : clicked , [ ] ( QModelIndex i ) { AddHook ( i . data ( ) . toString ( ) . split ( " => " ) [ 0 ] ) ; } ) ;
hookList - > show ( ) ;
saveFileName = QFileDialog : : getSaveFileName ( This , SAVE_SEARCH_RESULTS , " ./results.txt " , TEXT_FILES ) ;
} , Qt : : BlockingQueuedConnection ) ;
if ( ! saveFileName . isEmpty ( ) )
{
QTextFile saveFile ( saveFileName , QIODevice : : WriteOnly | QIODevice : : Truncate ) ;
for ( auto hook = hooks - > cbegin ( ) ; hook ! = hooks - > cend ( ) ; + + hook ) saveFile . write ( hook - > toUtf8 ( ) . append ( ' \n ' ) ) ; // QStringList::begin() makes a copy
}
hooks - > clear ( ) ;
} ) . detach ( ) ;
2019-08-19 16:15:08 -04:00
}
2020-11-02 06:27:21 -07:00
void OpenSettings ( )
2019-06-02 02:09:17 -04:00
{
2020-03-02 23:38:51 -07:00
QDialog dialog ( This , Qt : : WindowCloseButtonHint ) ;
2020-11-02 06:27:21 -07:00
Settings settings ( & dialog ) ;
2019-06-16 15:28:59 -04:00
QFormLayout layout ( & dialog ) ;
2020-03-02 23:38:51 -07:00
QPushButton saveButton ( SAVE_SETTINGS , & dialog ) ;
for ( auto [ value , label ] : Array < bool & , const char * > {
{ TextThread : : filterRepetition , FILTER_REPETITION } ,
{ autoAttach , AUTO_ATTACH } ,
{ autoAttachSavedOnly , ATTACH_SAVED_ONLY } ,
{ showSystemProcesses , SHOW_SYSTEM_PROCESSES } ,
} )
{
auto checkBox = new QCheckBox ( & dialog ) ;
checkBox - > setChecked ( value ) ;
layout . addRow ( label , checkBox ) ;
QObject : : connect ( & saveButton , & QPushButton : : clicked , [ checkBox , label , & settings , & value ] { settings . setValue ( label , value = checkBox - > isChecked ( ) ) ; } ) ;
}
2020-01-18 23:25:57 -07:00
for ( auto [ value , label ] : Array < int & , const char * > {
2020-03-02 23:38:51 -07:00
{ TextThread : : maxBufferSize , MAX_BUFFER_SIZE } ,
{ TextThread : : flushDelay , FLUSH_DELAY } ,
{ TextThread : : maxHistorySize , MAX_HISTORY_SIZE } ,
{ Host : : defaultCodepage , DEFAULT_CODEPAGE } ,
2019-06-16 15:28:59 -04:00
} )
2019-06-02 02:09:17 -04:00
{
2019-06-16 15:28:59 -04:00
auto spinBox = new QSpinBox ( & dialog ) ;
spinBox - > setMaximum ( INT_MAX ) ;
spinBox - > setValue ( value ) ;
layout . addRow ( label , spinBox ) ;
2020-03-02 23:38:51 -07:00
QObject : : connect ( & saveButton , & QPushButton : : clicked , [ spinBox , label , & settings , & value ] { settings . setValue ( label , value = spinBox - > value ( ) ) ; } ) ;
2019-06-16 15:28:59 -04:00
}
2021-01-18 18:58:21 -07:00
QComboBox localeCombo ( & dialog ) ;
2020-03-05 01:17:45 -07:00
assert ( PROMPT = = 0 & & ALWAYS = = 1 & & NEVER = = 2 ) ;
2021-01-18 18:58:21 -07:00
localeCombo . addItems ( { { " Prompt " , " Always " , " Never " } } ) ;
localeCombo . setCurrentIndex ( settings . value ( CONFIG_JP_LOCALE , PROMPT ) . toInt ( ) ) ;
layout . addRow ( CONFIG_JP_LOCALE , & localeCombo ) ;
QObject : : connect ( & localeCombo , qOverload < int > ( & QComboBox : : activated ) , [ & settings ] ( int i ) { settings . setValue ( CONFIG_JP_LOCALE , i ) ; } ) ;
2020-03-02 23:38:51 -07:00
layout . addWidget ( & saveButton ) ;
QObject : : connect ( & saveButton , & QPushButton : : clicked , & dialog , & QDialog : : accept ) ;
dialog . setWindowTitle ( SETTINGS ) ;
dialog . exec ( ) ;
2019-06-16 15:28:59 -04:00
}
2020-03-02 23:38:51 -07:00
void Extensions ( )
2019-06-16 15:28:59 -04:00
{
2020-03-02 23:38:51 -07:00
extenWindow - > activateWindow ( ) ;
extenWindow - > showNormal ( ) ;
2019-06-16 15:28:59 -04:00
}
2019-06-02 02:09:17 -04:00
2020-03-02 23:38:51 -07:00
void ViewThread ( int index )
2019-06-16 15:28:59 -04:00
{
2020-03-02 23:38:51 -07:00
ui . ttCombo - > setCurrentIndex ( index ) ;
ui . textOutput - > setPlainText ( sanitize ( S ( ( current = & Host : : GetThread ( ParseTextThreadString ( ui . ttCombo - > itemText ( index ) ) ) ) - > storage - > c_str ( ) ) ) ) ;
ui . textOutput - > moveCursor ( QTextCursor : : End ) ;
2019-06-18 00:39:50 -04:00
}
2020-03-02 23:38:51 -07:00
void SetOutputFont ( QString fontString )
2019-06-16 15:28:59 -04:00
{
2020-03-02 23:38:51 -07:00
QFont font = ui . textOutput - > font ( ) ;
font . fromString ( fontString ) ;
font . setStyleStrategy ( QFont : : NoFontMerging ) ;
ui . textOutput - > setFont ( font ) ;
2020-11-02 06:27:21 -07:00
Settings ( ) . setValue ( FONT , font . toString ( ) ) ;
2020-03-02 23:38:51 -07:00
}
2019-09-10 21:59:59 -04:00
2020-03-02 23:38:51 -07:00
void ProcessConnected ( DWORD processId )
{
alreadyAttached . insert ( processId ) ;
QString process = S ( GetModuleFilename ( processId ) . value_or ( L " ??? " ) ) ;
QMetaObject : : invokeMethod ( This , [ process , processId ]
2019-07-16 21:55:40 +05:30
{
2020-03-02 23:38:51 -07:00
ui . processCombo - > addItem ( QString : : number ( processId , 16 ) . toUpper ( ) + " : " + QFileInfo ( process ) . fileName ( ) ) ;
} ) ;
if ( process = = " ??? " ) return ;
// This does add (potentially tons of) duplicates to the file, but as long as I don't perform Ω(N^2) operations it shouldn't be an issue
QTextFile ( GAME_SAVE_FILE , QIODevice : : WriteOnly | QIODevice : : Append ) . write ( ( process + " \n " ) . toUtf8 ( ) ) ;
QStringList allProcesses = QString ( QTextFile ( HOOK_SAVE_FILE , QIODevice : : ReadOnly ) . readAll ( ) ) . split ( " \n " , QString : : SkipEmptyParts ) ;
auto hookList = std : : find_if ( allProcesses . rbegin ( ) , allProcesses . rend ( ) , [ & ] ( QString hookList ) { return hookList . contains ( process ) ; } ) ;
if ( hookList ! = allProcesses . rend ( ) )
for ( auto hookInfo : hookList - > split ( " , " ) )
if ( auto hp = HookCode : : Parse ( S ( hookInfo ) ) ) Host : : InsertHook ( processId , hp . value ( ) ) ;
else swscanf_s ( S ( hookInfo ) . c_str ( ) , L " |%I64d:%I64d:%[^ \n ] " , & savedThreadCtx , & savedThreadCtx2 , savedThreadCode , ( unsigned ) std : : size ( savedThreadCode ) ) ;
}
2019-07-20 10:03:28 -04:00
2020-03-02 23:38:51 -07:00
void ProcessDisconnected ( DWORD processId )
{
QMetaObject : : invokeMethod ( This , [ processId ]
{
ui . processCombo - > removeItem ( ui . processCombo - > findText ( QString : : number ( processId , 16 ) . toUpper ( ) + " : " , Qt : : MatchStartsWith ) ) ;
2019-09-12 13:19:02 -04:00
} , Qt : : BlockingQueuedConnection ) ;
2020-03-02 23:38:51 -07:00
}
void ThreadAdded ( TextThread & thread )
{
std : : wstring threadCode = HookCode : : Generate ( thread . hp , thread . tp . processId ) ;
2020-03-30 13:19:35 -06:00
bool savedMatch = ( savedThreadCtx & 0xFFFF ) = = ( thread . tp . ctx & 0xFFFF ) & & savedThreadCtx2 = = thread . tp . ctx2 & & savedThreadCode = = threadCode ;
2020-03-02 23:38:51 -07:00
if ( savedMatch )
2019-09-12 13:19:02 -04:00
{
2020-03-02 23:38:51 -07:00
savedThreadCtx = savedThreadCtx2 = savedThreadCode [ 0 ] = 0 ;
current = & thread ;
2019-09-12 13:19:02 -04:00
}
2020-03-02 23:38:51 -07:00
QMetaObject : : invokeMethod ( This , [ savedMatch , ttString = TextThreadString ( thread ) + S ( FormatString ( L " (%s) " , threadCode ) ) ]
{
ui . ttCombo - > addItem ( ttString ) ;
if ( savedMatch ) ViewThread ( ui . ttCombo - > count ( ) - 1 ) ;
} ) ;
}
2019-06-02 02:09:17 -04:00
2020-03-02 23:38:51 -07:00
void ThreadRemoved ( TextThread & thread )
2019-06-27 12:39:44 +05:30
{
2020-03-02 23:38:51 -07:00
QMetaObject : : invokeMethod ( This , [ ttString = TextThreadString ( thread ) ]
{
int threadIndex = ui . ttCombo - > findText ( ttString , Qt : : MatchStartsWith ) ;
if ( threadIndex = = ui . ttCombo - > currentIndex ( ) ) ViewThread ( 0 ) ;
ui . ttCombo - > removeItem ( threadIndex ) ;
} , Qt : : BlockingQueuedConnection ) ;
2019-06-27 12:39:44 +05:30
}
2020-03-02 23:38:51 -07:00
bool SentenceReceived ( TextThread & thread , std : : wstring & sentence )
2018-12-21 14:18:43 -05:00
{
2021-01-15 06:07:23 -07:00
for ( int i = 0 ; i < sentence . size ( ) ; + + i ) if ( sentence [ i ] = = ' \r ' & & sentence [ i + 1 ] = = ' \n ' ) sentence [ i ] = 0x200b ; // for some reason \r appears as newline - no need to double
2020-03-02 23:38:51 -07:00
if ( ! DispatchSentenceToExtensions ( sentence , GetSentenceInfo ( thread ) . data ( ) ) ) return false ;
sentence + = L ' \n ' ;
if ( & thread = = current ) QMetaObject : : invokeMethod ( This , [ sentence = S ( sentence ) ] ( ) mutable
{
sanitize ( sentence ) ;
auto scrollbar = ui . textOutput - > verticalScrollBar ( ) ;
bool atBottom = scrollbar - > value ( ) + 3 > scrollbar - > maximum ( ) | | ( double ) scrollbar - > value ( ) / scrollbar - > maximum ( ) > 0.975 ; // arbitrary
QTextCursor cursor ( ui . textOutput - > document ( ) ) ;
cursor . movePosition ( QTextCursor : : End ) ;
cursor . insertText ( sentence ) ;
if ( atBottom ) scrollbar - > setValue ( scrollbar - > maximum ( ) ) ;
} ) ;
return true ;
}
void OutputContextMenu ( QPoint point )
{
std : : unique_ptr < QMenu > menu ( ui . textOutput - > createStandardContextMenu ( ) ) ;
menu - > addAction ( FONT , [ ] { if ( QString font = QFontDialog : : getFont ( & ok , ui . textOutput - > font ( ) , This , FONT ) . toString ( ) ; ok ) SetOutputFont ( font ) ; } ) ;
menu - > exec ( ui . textOutput - > mapToGlobal ( point ) ) ;
}
void CopyUnlessMouseDown ( )
{
if ( ! ( QApplication : : mouseButtons ( ) & Qt : : LeftButton ) ) ui . textOutput - > copy ( ) ;
2019-06-16 15:28:59 -04:00
}
2018-11-10 05:13:59 -05:00
}
2020-03-02 23:38:51 -07:00
MainWindow : : MainWindow ( QWidget * parent ) : QMainWindow ( parent )
2018-07-23 22:57:54 -07:00
{
2020-03-02 23:38:51 -07:00
This = this ;
ui . setupUi ( this ) ;
extenWindow = new ExtenWindow ( this ) ;
for ( auto [ text , slot ] : Array < const char * , void ( & ) ( ) > {
{ ATTACH , AttachProcess } ,
{ LAUNCH , LaunchProcess } ,
2021-01-15 06:07:23 -07:00
{ CONFIG , ConfigureProcess } ,
2020-03-02 23:38:51 -07:00
{ DETACH , DetachProcess } ,
{ FORGET , ForgetProcess } ,
{ ADD_HOOK , AddHook } ,
{ REMOVE_HOOKS , RemoveHooks } ,
{ SAVE_HOOKS , SaveHooks } ,
{ SEARCH_FOR_HOOKS , FindHooks } ,
2020-11-02 06:27:21 -07:00
{ SETTINGS , OpenSettings } ,
2020-03-02 23:38:51 -07:00
{ EXTENSIONS , Extensions }
} )
{
auto button = new QPushButton ( text , ui . processFrame ) ;
connect ( button , & QPushButton : : clicked , slot ) ;
ui . processLayout - > addWidget ( button ) ;
}
ui . processLayout - > addItem ( new QSpacerItem ( 0 , 0 , QSizePolicy : : Minimum , QSizePolicy : : Expanding ) ) ;
2021-02-21 14:15:59 -07:00
connect ( ui . processCombo , qOverload < int > ( & QComboBox : : currentIndexChanged ) , [ ] { selectedProcessId = ui . processCombo - > currentText ( ) . split ( " : " ) [ 0 ] . toULong ( nullptr , 16 ) ; } ) ;
2020-03-02 23:38:51 -07:00
connect ( ui . ttCombo , qOverload < int > ( & QComboBox : : activated ) , this , ViewThread ) ;
connect ( ui . textOutput , & QPlainTextEdit : : selectionChanged , this , CopyUnlessMouseDown ) ;
connect ( ui . textOutput , & QPlainTextEdit : : customContextMenuRequested , this , OutputContextMenu ) ;
2020-11-02 06:27:21 -07:00
Settings settings ;
2020-03-02 23:38:51 -07:00
if ( settings . contains ( WINDOW ) & & QApplication : : screenAt ( settings . value ( WINDOW ) . toRect ( ) . center ( ) ) ) setGeometry ( settings . value ( WINDOW ) . toRect ( ) ) ;
SetOutputFont ( settings . value ( FONT , ui . textOutput - > font ( ) . toString ( ) ) . toString ( ) ) ;
TextThread : : filterRepetition = settings . value ( FILTER_REPETITION , TextThread : : filterRepetition ) . toBool ( ) ;
autoAttach = settings . value ( AUTO_ATTACH , autoAttach ) . toBool ( ) ;
autoAttachSavedOnly = settings . value ( ATTACH_SAVED_ONLY , autoAttachSavedOnly ) . toBool ( ) ;
showSystemProcesses = settings . value ( SHOW_SYSTEM_PROCESSES , showSystemProcesses ) . toBool ( ) ;
TextThread : : flushDelay = settings . value ( FLUSH_DELAY , TextThread : : flushDelay ) . toInt ( ) ;
TextThread : : maxBufferSize = settings . value ( MAX_BUFFER_SIZE , TextThread : : maxBufferSize ) . toInt ( ) ;
TextThread : : maxHistorySize = settings . value ( MAX_HISTORY_SIZE , TextThread : : maxHistorySize ) . toInt ( ) ;
Host : : defaultCodepage = settings . value ( DEFAULT_CODEPAGE , Host : : defaultCodepage ) . toInt ( ) ;
Host : : Start ( ProcessConnected , ProcessDisconnected , ThreadAdded , ThreadRemoved , SentenceReceived ) ;
current = & Host : : GetThread ( Host : : console ) ;
Host : : AddConsoleOutput ( ABOUT ) ;
AttachConsole ( ATTACH_PARENT_PROCESS ) ;
WriteConsoleW ( GetStdHandle ( STD_OUTPUT_HANDLE ) , CL_OPTIONS , wcslen ( CL_OPTIONS ) , DUMMY , NULL ) ;
auto processes = GetAllProcesses ( ) ;
int argc ;
std : : unique_ptr < LPWSTR [ ] , Functor < LocalFree > > argv ( CommandLineToArgvW ( GetCommandLineW ( ) , & argc ) ) ;
for ( int i = 0 ; i < argc ; + + i )
if ( std : : wstring arg = argv [ i ] ; arg [ 0 ] = = L ' / ' | | arg [ 0 ] = = L ' - ' )
if ( arg [ 1 ] = = L ' p ' | | arg [ 1 ] = = L ' P ' )
2021-01-15 06:07:23 -07:00
if ( DWORD processId = wcstoul ( arg . substr ( 2 ) . c_str ( ) , nullptr , 0 ) ) Host : : InjectProcess ( processId ) ;
2020-03-02 23:38:51 -07:00
else for ( auto [ processId , processName ] : processes )
2020-12-14 06:26:01 -07:00
if ( processName . value_or ( L " " ) . find ( L " \\ " + arg . substr ( 2 ) ) ! = std : : string : : npos ) Host : : InjectProcess ( processId ) ;
2020-03-02 23:38:51 -07:00
std : : thread ( [ ]
{
for ( ; ; Sleep ( 10000 ) )
{
std : : unordered_set < std : : wstring > attachTargets ;
if ( autoAttach )
for ( auto process : QString ( QTextFile ( GAME_SAVE_FILE , QIODevice : : ReadOnly ) . readAll ( ) ) . split ( " \n " , QString : : SkipEmptyParts ) )
attachTargets . insert ( S ( process ) ) ;
if ( autoAttachSavedOnly )
for ( auto process : QString ( QTextFile ( HOOK_SAVE_FILE , QIODevice : : ReadOnly ) . readAll ( ) ) . split ( " \n " , QString : : SkipEmptyParts ) )
attachTargets . insert ( S ( process . split ( " , " ) [ 0 ] ) ) ;
if ( ! attachTargets . empty ( ) )
for ( auto [ processId , processName ] : GetAllProcesses ( ) )
if ( processName & & attachTargets . count ( processName . value ( ) ) > 0 & & alreadyAttached . count ( processId ) = = 0 ) Host : : InjectProcess ( processId ) ;
}
} ) . detach ( ) ;
2018-07-22 16:53:51 -07:00
}
2018-07-26 22:42:21 -07:00
2020-03-02 23:38:51 -07:00
MainWindow : : ~ MainWindow ( )
2018-07-26 22:42:21 -07:00
{
2020-11-02 06:27:21 -07:00
Settings ( ) . setValue ( WINDOW , geometry ( ) ) ;
2020-03-02 23:38:51 -07:00
CleanupExtensions ( ) ;
SetErrorMode ( SEM_NOGPFAULTERRORBOX ) ;
ExitProcess ( 0 ) ;
2018-07-26 22:42:21 -07:00
}
2019-07-13 19:15:43 +05:30
2020-03-02 23:38:51 -07:00
void MainWindow : : closeEvent ( QCloseEvent * )
2019-07-13 19:15:43 +05:30
{
2021-01-30 12:07:37 -07:00
QApplication : : quit ( ) ; // Need to do this to kill any windows that might've been made by extensions
2019-07-13 19:15:43 +05:30
}