3 * The Cheat - The legendary universal game trainer for Mac OS X.
4 * http://www.brokenzipper.com/trac/wiki/TheCheat
6 * Copyright (c) 2003-2011, Charles McGarvey et al.
8 * Distributable under the terms and conditions of the 2-clause BSD
9 * license; see the file COPYING for the legal text of the license.
13 #import "CheatDocument.h"
16 @interface CheatDocument (DocumentActionsPrivateAPI
)
18 - (void)_confirmTargetChange
:(NSWindow
*)sheet returnCode
:(int)returnCode context
:(void *)contextInfo
;
23 @implementation CheatDocument ( DocumentActions
)
26 - (IBAction
)ibSetLocalCheater
:(id)sender
28 ChazLog( @
"Selected %@", sender
);
30 // if this is the current server, don't reconnect
31 if ( ![self shouldConnectWithServer
:sender
] ) {
35 // disconnect and prepare to reconnect
36 [self disconnectFromCheater
];
37 [self connectWithServer
:sender
];
39 // create new local cheater
40 _cheater
= [[LocalCheater alloc
] initWithDelegate
:self];
41 [(LocalCheater
*)_cheater setShouldCopy
:YES
];
43 // send initial messages
45 [_cheater getProcessList
];
47 // send preferences to the cheater
48 [_cheater limitReturnedResults
:[[NSUserDefaults standardUserDefaults
] integerForKey
:TCHitsDisplayedPref
]];
50 [ibStatusText setDefaultStatus
:[self defaultStatusString
]];
53 - (IBAction
)ibSetRemoteCheater
:(id)sender
55 ChazLog( @
"Selected %@", sender
);
57 if ( ![self shouldConnectWithServer
:sender
] ) {
61 ChazLog( @
"resolving rendezvous service..." );
63 _resolvingService
= [[sender representedObject
] retain
];
64 [_resolvingService setDelegate
:self];
65 [_resolvingService resolve
];
68 - (void)netServiceDidResolveAddress
:(NSNetService
*)sender
72 ChazLog( @
"service resolved!" );
77 if ( sender
!= _resolvingService
) {
81 [self disconnectFromCheater
];
82 [self connectWithServer
:(NSMenuItem
*)[ibServerPopup itemAtIndex
:[ibServerPopup indexOfItemWithRepresentedObject
:_resolvingService
]]];
84 addresses
= [_resolvingService addresses
];
86 _resolvingService
= nil;
88 // create new remote cheater
89 ChazLog( @
"found %i addresses", [addresses count
] );
90 _cheater
= [[RemoteCheater alloc
] initWithDelegate
:self];
91 [(RemoteCheater
*)_cheater connectToHostWithData
:[addresses objectAtIndex
:0]];
93 // send initial messages
95 [_cheater getProcessList
];
97 // send preferences to the cheater
98 [_cheater limitReturnedResults
:[[NSUserDefaults standardUserDefaults
] integerForKey
:TCHitsDisplayedPref
]];
100 [ibStatusText setDefaultStatus
:[self defaultStatusString
]];
101 [self updateInterface
];
104 - (void)netService
:(NSNetService
*)sender didNotResolve
:(NSDictionary
*)errorDict
108 if ( sender
!= _resolvingService
) {
112 _resolvingService
= nil;
114 NSBeginInformationalAlertSheet( @
"The Cheat can't find the server.", @
"OK", nil, nil, ibWindow
, self, NULL
, NULL
, NULL
,
115 @
"The Cheat can't connect to the server \"%@\" because it can't be found.", [sender name
] );
118 - (void)netServiceDidStop
:(NSNetService
*)sender
123 - (IBAction
)ibSetCustomCheater
:(id)sender
125 RemoteCheater
*cheater
;
126 ChazLog( @
"Selected %@", [sender description
] );
128 if ( ![self shouldConnectWithServer
:sender
] ) {
132 cheater
= [[RemoteCheater alloc
] initWithDelegate
:self];
133 if ( ![(RemoteCheater
*)cheater connectToHostWithData
:[sender representedObject
]] ) {
134 NSBeginInformationalAlertSheet( @
"The Cheat can't find the server.", @
"OK", nil, nil, ibWindow
, self, NULL
, NULL
, NULL
,
135 @
"The Cheat can't connect to \"%@\" because there is no server at that address.", [sender title
] );
137 [self selectConnectedCheater
];
141 [self disconnectFromCheater
];
142 [self connectWithServer
:sender
];
146 // send initial messages
148 [_cheater getProcessList
];
150 // send preferences to the cheater
151 [_cheater limitReturnedResults
:[[NSUserDefaults standardUserDefaults
] integerForKey
:TCHitsDisplayedPref
]];
153 [ibStatusText setDefaultStatus
:[self defaultStatusString
]];
154 [self updateInterface
];
157 - (IBAction
)ibSetNoCheater
:(id)sender
159 [self disconnectFromCheater
];
162 [_serverObject release
];
165 [ibStatusText setDefaultStatus
:[self defaultStatusString
]];
166 [self updateInterface
];
169 - (IBAction
)ibSetProcess
:(id)sender
171 if ( [_process isEqual
:(Process
*)[sender representedObject
]] ) {
172 // this process is already selected, do nothing
176 [_cheatData process
];
177 if ( [_searchData hasSearchedOnce
] ) {
178 NSBeginInformationalAlertSheet( @
"Confirm target change.", @
"OK", @
"Cancel", nil, ibWindow
, self, NULL
,
179 @selector(_confirmTargetChange
:returnCode
:context
:), [[sender representedObject
] retain
],
180 @
"If you change the target now, your search will be cleared. This cannot be undone. Continue?" );
183 // request the change
184 [_cheater setTarget
:(Process
*)[sender representedObject
]];
188 - (void)_confirmTargetChange
:(NSWindow
*)sheet returnCode
:(int)returnCode context
:(void *)contextInfo
190 NSMenu
*processMenu
= [ibProcessPopup menu
];
191 Process
*process
= (Process
*)contextInfo
;
193 if ( returnCode
== NSAlertDefaultReturn
) {
195 [self ibClearSearch
:nil];
196 // request the change
197 [_cheater setTarget
:process
];
200 // select the correct server menuitem
201 [ibProcessPopup selectItemAtIndex
:[processMenu indexOfItemWithRepresentedObject
:_process
]];
208 - (IBAction
)ibSetVariableType
:(id)sender
210 [_searchData setVariableType
:[sender tag
]];
211 [self updateInterface
];
214 - (IBAction
)ibSetIntegerSign
:(id)sender
216 [_searchData setIntegerSign
:[[sender selectedCell
] tag
]];
219 - (IBAction
)ibSetOperator
:(id)sender
221 [_searchData setSearchOperator
:[sender tag
]];
224 - (IBAction
)ibSetValueUsed
:(id)sender
226 [_searchData setValueUsed
:[[sender selectedCell
] tag
]];
227 [self updateInterface
];
230 - (IBAction
)ibClearSearch
:(id)sender
232 [_cheater clearSearch
];
235 - (IBAction
)ibSearch
:(id)sender
240 if ( [_searchData valueUsed
] == TCGivenValue
) {
241 variable
= [[Variable alloc
] initWithType
:[_searchData variableType
] integerSign
:[_searchData integerSign
]];
242 [variable setProcess
:_process
];
243 [variable setStringValue
:[ibSearchValueField stringValue
]];
244 if ( [variable isValueValid
] && [variable valueSize
] > 0 ) {
245 _status
= TCSearchingStatus
;
246 [ibStatusText setDefaultStatus
:[NSString stringWithFormat
:@
"Searching %@'s memory%C", [_process name
], 0x2026]];
247 [ibStatusBar setIndeterminate
:NO
];
249 [_searchData setSearchValue
:variable
];
250 [_cheater searchForVariable
:variable comparison
:[_searchData searchOperator
]];
251 //[_cheater searchForVariable:[_searchData searchValue] comparison:[_searchData searchOperator]];
255 NSBeginAlertSheet( @
"Invalid Input", @
"OK", nil, nil, ibWindow
, nil, NULL
, NULL
, NULL
,
256 @
"The search value \"%@\" cannot be used with this type of search.", [ibSearchValueField stringValue
] );
260 _status
= TCSearchingStatus
;
261 [ibStatusText setDefaultStatus
:[NSString stringWithFormat
:@
"Searching %@'s memory%C", [_process name
], 0x2026]];
262 [ibStatusBar setIndeterminate
:NO
];
264 [_cheater searchLastValuesComparison
:[_searchData searchOperator
]];
267 [self updateInterface
];
270 - (IBAction
)ibAddSearchVariable
:(id)sender
275 // don't do anything if there is nothing selected
276 if ( [ibSearchVariableTable selectedRow
] == -1 ) {
280 rows
= [ibSearchVariableTable selectedRows
];
282 for ( i
= 0; i
< top
; i
++ ) {
283 int rowIndex
= [[rows objectAtIndex
:i
] unsignedIntValue
];
284 // transfer the search variable to the cheat data
285 [_cheatData addVariable
:[_searchData variableAtIndex
:rowIndex
]];
288 // update the variable table
289 [ibCheatVariableTable reloadData
];
291 if ( [[NSUserDefaults standardUserDefaults
] boolForKey
:TCSwitchVariablesPref
] ) {
292 [self switchToCheatMode
];
294 int rowIndex
= [_cheatData variableCount
]-1;
295 if ( MacOSXVersion() >= 0x1030 ) {
296 [ibCheatVariableTable selectRowIndexes
:[NSIndexSet indexSetWithIndex
:rowIndex
] byExtendingSelection
:NO
];
299 [ibCheatVariableTable selectRow
:rowIndex byExtendingSelection
:NO
];
301 // start editing the last added variable
302 if ( [[NSUserDefaults standardUserDefaults
] boolForKey
:TCAutoStartEditingVarsPref
] ) {
305 if ( MacOSXVersion() >= 0x1030 ) {
306 [ibCheatVariableTable selectRowIndexes
:[NSIndexSet indexSetWithIndexesInRange
:NSMakeRange(rowIndex
-top
+1,top
-1)]
307 byExtendingSelection
:YES
];
310 for ( i
= 1; i
< top
; i
++ ) {
311 [ibCheatVariableTable selectRow
:rowIndex
-i byExtendingSelection
:YES
];
314 [ibCheatVariableTable scrollRowToVisible
:rowIndex
];
315 [self ibRunEditVariablesSheet
:nil];
319 [ibCheatVariableTable editColumn
:[ibCheatVariableTable columnWithIdentifier
:@
"value"]
320 row
:rowIndex withEvent
:nil select
:YES
];
326 [self setDocumentChanged
];
327 [self updateInterface
];
331 - (IBAction
)ibSetCheatRepeats
:(id)sender
333 [_cheatData setRepeats
:[sender state
]];
336 [self setDocumentChanged
];
337 [self updateInterface
];
340 - (IBAction
)ibSetRepeatInterval
:(id)sender
342 [_cheatData setRepeatInterval
:[sender doubleValue
]];
345 [self setDocumentChanged
];
346 [self updateInterface
];
349 - (IBAction
)ibCheat
:(id)sender
351 _status
= TCCheatingStatus
;
352 [_cheater makeVariableChanges
:[_cheatData enabledVariables
] repeat
:[_cheatData repeats
] interval
:[_cheatData repeatInterval
]];
354 // update status description
355 if ( [_cheatData repeats
] ) {
356 [ibStatusText setDefaultStatus
:[NSString stringWithFormat
:@
"Applying cheats to %@%C", [_process name
], 0x2026]];
357 [ibStatusBar setIndeterminate
:YES
];
358 [ibStatusBar startAnimation
:self];
360 [self updateInterface
];
365 - (IBAction
)ibRunPropertiesSheet
:(id)sender
368 [ibWindowTitleField setStringValue
:[_cheatData windowTitle
]];
369 [ibCheatInfoField setStringValue
:[_cheatData cheatInfo
]];
372 [NSApp beginSheet
:ibPropertiesSheet modalForWindow
:ibWindow modalDelegate
:nil didEndSelector
:NULL contextInfo
:nil];
375 - (IBAction
)ibEndPropertiesSheet
:(id)sender
377 [ibPropertiesSheet orderOut
:sender
];
378 [NSApp endSheet
:ibPropertiesSheet returnCode
:0];
380 if ( [sender tag
] == 1 ) {
381 // do not update anything if nothing has changed
382 if ( [[ibWindowTitleField stringValue
] isEqualToString
:[_cheatData windowTitle
]] &&
383 [[ibCheatInfoField stringValue
] isEqualToString
:[_cheatData cheatInfo
]] ) {
387 [_cheatData setWindowTitle
:[ibWindowTitleField stringValue
]];
388 [_cheatData setCheatInfo
:[ibCheatInfoField stringValue
]];
390 [self setDocumentChanged
];
391 [self updateInterface
];
396 - (IBAction
)ibRunPasswordSheet
:(id)sender
401 - (IBAction
)ibEndPasswordSheet
:(id)sender
407 - (IBAction
)ibRunCustomServerSheet
:(id)sender
410 [ibServerField setStringValue
:@
""];
411 [ibPortField setStringValue
:[NSString stringWithFormat
:@
"%i", TCDefaultListenPort
]];
414 [NSApp beginSheet
:ibCustomServerSheet modalForWindow
:ibWindow modalDelegate
:nil didEndSelector
:NULL contextInfo
:nil];
417 - (IBAction
)ibEndCustomServerSheet
:(id)sender
419 NSString
*server
= [ibServerField stringValue
];
420 int port
= [[ibPortField stringValue
] intValue
];
422 ChazLog( @
"ibEndCustomServerSheet: %@:%i", server
, port
);
424 [ibCustomServerSheet orderOut
:sender
];
425 [NSApp endSheet
:ibCustomServerSheet returnCode
:0];
427 if ( [sender tag
] == 1 ) {
428 [self connectWithURL
:[NSString stringWithFormat
:@
"cheat://%@:%i", server
, port
]];
433 - (IBAction
)ibRunEditVariablesSheet
:(id)sender
435 int row
= [ibCheatVariableTable selectedRow
];
438 // must have selected items
443 var
= [_cheatData variableAtIndex
:row
];
446 [ibNewValueField setStringValue
:[var stringValue
]];
449 [NSApp beginSheet
:ibEditVariablesSheet modalForWindow
:ibWindow modalDelegate
:nil didEndSelector
:NULL contextInfo
:nil];
452 - (IBAction
)ibEndEditVariablesSheet
:(id)sender
454 NSString
*newValue
= [ibNewValueField stringValue
];
458 [ibEditVariablesSheet orderOut
:sender
];
459 [NSApp endSheet
:ibEditVariablesSheet returnCode
:0];
461 if ( [sender tag
] == 0 ) {
464 if ( [newValue isEqualToString
:@
""] ) {
468 rows
= [ibCheatVariableTable selectedRows
];
471 // change all selected variables with the new value
473 for ( i
= 0; i
< top
; i
++ ) {
474 Variable
*var
= [_cheatData variableAtIndex
:[[rows objectAtIndex
:i
] unsignedIntValue
]];
475 [var setStringValue
:newValue
];
479 [ibCheatVariableTable reloadData
];
481 [self setDocumentChanged
];
482 [self updateInterface
];
486 - (IBAction
)ibPauseTarget
:(id)sender
488 [_cheater pauseTarget
];
491 - (IBAction
)ibResumeTarget
:(id)sender
493 [_cheater resumeTarget
];
497 - (IBAction
)ibCancelSearch
:(id)sender
499 _isCancelingTask
= YES
;
500 [_cheater cancelSearch
];
502 [self updateInterface
];
505 - (IBAction
)ibStopCheat
:(id)sender
507 _isCancelingTask
= YES
;
508 [_cheater stopChangingVariables
];
510 [self updateInterface
];
514 - (IBAction
)ibDumpMemory
:(id)sender
516 _status
= TCDumpingStatus
;
517 [_cheater getMemoryDump
];
520 [ibStatusText setDefaultStatus
:[NSString stringWithFormat
:@
"Dumping %@'s memory%C", [_process name
], 0x2026]];
521 [ibStatusBar setIndeterminate
:YES
];
522 [ibStatusBar startAnimation
:self];
524 [self updateInterface
];
527 - (IBAction
)ibCancelDump
:(id)sender
529 _isCancelingTask
= YES
;
530 [_cheater cancelMemoryDump
];
532 [self updateInterface
];
536 - (IBAction
)ibAddCheatVariable
:(id)sender
538 ChazLog( @
"ibAddCheatVariable:" );
539 Variable
*var
= [[Variable alloc
] initWithType
:[sender tag
]];
540 // add the new variable to the doc data
541 [_cheatData addVariable
:var
];
543 // update the variable table
544 [ibCheatVariableTable reloadData
];
546 if ( [[NSUserDefaults standardUserDefaults
] boolForKey
:TCSwitchVariablesPref
] ) {
547 [self switchToCheatMode
];
549 int row
= [_cheatData variableCount
]-1;
550 if ( MacOSXVersion() >= 0x1030 ) {
551 [ibCheatVariableTable selectRowIndexes
:[NSIndexSet indexSetWithIndex
:row
] byExtendingSelection
:NO
];
554 [ibCheatVariableTable selectRow
:row byExtendingSelection
:NO
];
556 // start editing new variable
557 if ( [[NSUserDefaults standardUserDefaults
] boolForKey
:TCAutoStartEditingVarsPref
] ) {
558 [ibCheatVariableTable editColumn
:[ibCheatVariableTable columnWithIdentifier
:@
"address"] row
:row withEvent
:nil select
:YES
];
563 [self setDocumentChanged
];
564 [self updateInterface
];
567 - (IBAction
)ibSetVariableEnabled
:(id)sender
574 ChazLog( @
"ibSetVariableEnabled: %i", [sender selectedRow
] );
576 flag
= [[_cheatData variableAtIndex
:[ibCheatVariableTable selectedRow
]] isEnabled
];
578 rows
= [ibCheatVariableTable selectedRows
];
581 for ( i
= 0; i
< top
; i
++ ) {
582 Variable
*var
= [_cheatData variableAtIndex
:[[rows objectAtIndex
:i
] unsignedIntValue
]];
583 [var setEnabled
:!flag
];
587 [ibCheatVariableTable reloadData
];
588 [self setDocumentChanged
];
589 [self updateInterface
];
593 - (IBAction
)ibToggleSearchCheat
:(id)sender
595 if ( _mode
== TCCheatMode
) {
596 [self switchToSearchMode
];
598 else if ( _mode
== TCSearchMode
) {
599 [self switchToCheatMode
];
604 - (IBAction
)ibUndo
:(id)sender
609 - (IBAction
)ibRedo
:(id)sender