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
56 ChazLog( @
"Selected %@", sender
);
58 if ( ![self shouldConnectWithServer
:sender
] ) {
62 ChazLog( @
"resolving rendezvous service..." );
64 _resolvingService
= [[sender representedObject
] retain
];
65 [_resolvingService setDelegate
:self];
66 [_resolvingService resolveWithTimeout
:timeout
];
69 - (void)netServiceDidResolveAddress
:(NSNetService
*)sender
73 ChazLog( @
"service resolved!" );
78 if ( sender
!= _resolvingService
) {
82 [self disconnectFromCheater
];
83 [self connectWithServer
:(NSMenuItem
*)[ibServerPopup itemAtIndex
:[ibServerPopup indexOfItemWithRepresentedObject
:_resolvingService
]]];
85 addresses
= [_resolvingService addresses
];
87 _resolvingService
= nil;
89 // create new remote cheater
90 ChazLog( @
"found %i addresses", [addresses count
] );
91 _cheater
= [[RemoteCheater alloc
] initWithDelegate
:self];
92 [(RemoteCheater
*)_cheater connectToHostWithData
:[addresses objectAtIndex
:0]];
94 // send initial messages
96 [_cheater getProcessList
];
98 // send preferences to the cheater
99 [_cheater limitReturnedResults
:[[NSUserDefaults standardUserDefaults
] integerForKey
:TCHitsDisplayedPref
]];
101 [ibStatusText setDefaultStatus
:[self defaultStatusString
]];
102 [self updateInterface
];
105 - (void)netService
:(NSNetService
*)sender didNotResolve
:(NSDictionary
*)errorDict
109 if ( sender
!= _resolvingService
) {
113 _resolvingService
= nil;
115 NSBeginInformationalAlertSheet( @
"The Cheat can't find the server.", @
"OK", nil, nil, ibWindow
, self, NULL
, NULL
, NULL
,
116 @
"The Cheat can't connect to the server \"%@\" because it can't be found.", [sender name
] );
119 - (void)netServiceDidStop
:(NSNetService
*)sender
124 - (IBAction
)ibSetCustomCheater
:(id)sender
126 RemoteCheater
*cheater
;
127 ChazLog( @
"Selected %@", [sender description
] );
129 if ( ![self shouldConnectWithServer
:sender
] ) {
133 cheater
= [[RemoteCheater alloc
] initWithDelegate
:self];
134 if ( ![(RemoteCheater
*)cheater connectToHostWithData
:[sender representedObject
]] ) {
135 NSBeginInformationalAlertSheet( @
"The Cheat can't find the server.", @
"OK", nil, nil, ibWindow
, self, NULL
, NULL
, NULL
,
136 @
"The Cheat can't connect to \"%@\" because there is no server at that address.", [sender title
] );
138 [self selectConnectedCheater
];
142 [self disconnectFromCheater
];
143 [self connectWithServer
:sender
];
147 // send initial messages
149 [_cheater getProcessList
];
151 // send preferences to the cheater
152 [_cheater limitReturnedResults
:[[NSUserDefaults standardUserDefaults
] integerForKey
:TCHitsDisplayedPref
]];
154 [ibStatusText setDefaultStatus
:[self defaultStatusString
]];
155 [self updateInterface
];
158 - (IBAction
)ibSetNoCheater
:(id)sender
160 [self disconnectFromCheater
];
163 [_serverObject release
];
166 [ibStatusText setDefaultStatus
:[self defaultStatusString
]];
167 [self updateInterface
];
170 - (IBAction
)ibSetProcess
:(id)sender
172 if ( [_process isEqual
:(Process
*)[sender representedObject
]] ) {
173 // this process is already selected, do nothing
177 [_cheatData process
];
178 if ( [_searchData hasSearchedOnce
] ) {
179 NSBeginInformationalAlertSheet( @
"Confirm target change.", @
"OK", @
"Cancel", nil, ibWindow
, self, NULL
,
180 @selector(_confirmTargetChange
:returnCode
:context
:), [[sender representedObject
] retain
],
181 @
"If you change the target now, your search will be cleared. This cannot be undone. Continue?" );
184 // request the change
185 [_cheater setTarget
:(Process
*)[sender representedObject
]];
189 - (void)_confirmTargetChange
:(NSWindow
*)sheet returnCode
:(int)returnCode context
:(void *)contextInfo
191 NSMenu
*processMenu
= [ibProcessPopup menu
];
192 Process
*process
= (Process
*)contextInfo
;
194 if ( returnCode
== NSAlertDefaultReturn
) {
196 [self ibClearSearch
:nil];
197 // request the change
198 [_cheater setTarget
:process
];
201 // select the correct server menuitem
202 [ibProcessPopup selectItemAtIndex
:[processMenu indexOfItemWithRepresentedObject
:_process
]];
209 - (IBAction
)ibSetVariableType
:(id)sender
211 [_searchData setVariableType
:[sender tag
]];
212 [self updateInterface
];
215 - (IBAction
)ibSetIntegerSign
:(id)sender
217 [_searchData setIntegerSign
:[[sender selectedCell
] tag
]];
220 - (IBAction
)ibSetOperator
:(id)sender
222 [_searchData setSearchOperator
:[sender tag
]];
225 - (IBAction
)ibSetValueUsed
:(id)sender
227 [_searchData setValueUsed
:[[sender selectedCell
] tag
]];
228 [self updateInterface
];
231 - (IBAction
)ibClearSearch
:(id)sender
233 [_cheater clearSearch
];
236 - (IBAction
)ibSearch
:(id)sender
241 if ( [_searchData valueUsed
] == TCGivenValue
) {
242 variable
= [[Variable alloc
] initWithType
:[_searchData variableType
] integerSign
:[_searchData integerSign
]];
243 [variable setProcess
:_process
];
244 [variable setStringValue
:[ibSearchValueField stringValue
]];
245 if ( [variable isValueValid
] && [variable valueSize
] > 0 ) {
246 _status
= TCSearchingStatus
;
247 [ibStatusText setDefaultStatus
:[NSString stringWithFormat
:@
"Searching %@'s memory%C", [_process name
], 0x2026]];
248 [ibStatusBar setIndeterminate
:NO
];
250 [_searchData setSearchValue
:variable
];
251 [_cheater searchForVariable
:variable comparison
:[_searchData searchOperator
]];
252 //[_cheater searchForVariable:[_searchData searchValue] comparison:[_searchData searchOperator]];
256 NSBeginAlertSheet( @
"Invalid Input", @
"OK", nil, nil, ibWindow
, nil, NULL
, NULL
, NULL
,
257 @
"The search value \"%@\" cannot be used with this type of search.", [ibSearchValueField stringValue
] );
261 _status
= TCSearchingStatus
;
262 [ibStatusText setDefaultStatus
:[NSString stringWithFormat
:@
"Searching %@'s memory%C", [_process name
], 0x2026]];
263 [ibStatusBar setIndeterminate
:NO
];
265 [_cheater searchLastValuesComparison
:[_searchData searchOperator
]];
268 [self updateInterface
];
271 - (IBAction
)ibAddSearchVariable
:(id)sender
276 // don't do anything if there is nothing selected
277 if ( [ibSearchVariableTable selectedRow
] == -1 ) {
281 rows
= [ibSearchVariableTable selectedRows
];
283 for ( i
= 0; i
< top
; i
++ ) {
284 int rowIndex
= [[rows objectAtIndex
:i
] unsignedIntValue
];
285 // transfer the search variable to the cheat data
286 [_cheatData addVariable
:[_searchData variableAtIndex
:rowIndex
]];
289 // update the variable table
290 [ibCheatVariableTable reloadData
];
292 if ( [[NSUserDefaults standardUserDefaults
] boolForKey
:TCSwitchVariablesPref
] ) {
293 [self switchToCheatMode
];
295 int rowIndex
= [_cheatData variableCount
]-1;
296 [ibCheatVariableTable selectRowIndexes
:[NSIndexSet indexSetWithIndex
:rowIndex
] byExtendingSelection
:NO
];
297 // start editing the last added variable
298 if ( [[NSUserDefaults standardUserDefaults
] boolForKey
:TCAutoStartEditingVarsPref
] ) {
300 [ibCheatVariableTable selectRowIndexes
:[NSIndexSet indexSetWithIndexesInRange
:NSMakeRange(rowIndex
-top
+1,top
-1)] byExtendingSelection
:YES
];
301 [ibCheatVariableTable scrollRowToVisible
:rowIndex
];
302 [self ibRunEditVariablesSheet
:nil];
306 [ibCheatVariableTable editColumn
:[ibCheatVariableTable columnWithIdentifier
:@
"value"]
307 row
:rowIndex withEvent
:nil select
:YES
];
313 [self setDocumentChanged
];
314 [self updateInterface
];
318 - (IBAction
)ibSetCheatRepeats
:(id)sender
320 [_cheatData setRepeats
:[sender state
]];
323 [self setDocumentChanged
];
324 [self updateInterface
];
327 - (IBAction
)ibSetRepeatInterval
:(id)sender
329 [_cheatData setRepeatInterval
:[sender doubleValue
]];
332 [self setDocumentChanged
];
333 [self updateInterface
];
336 - (IBAction
)ibCheat
:(id)sender
338 _status
= TCCheatingStatus
;
339 [_cheater makeVariableChanges
:[_cheatData enabledVariables
] repeat
:[_cheatData repeats
] interval
:[_cheatData repeatInterval
]];
341 // update status description
342 if ( [_cheatData repeats
] ) {
343 [ibStatusText setDefaultStatus
:[NSString stringWithFormat
:@
"Applying cheats to %@%C", [_process name
], 0x2026]];
344 [ibStatusBar setIndeterminate
:YES
];
345 [ibStatusBar startAnimation
:self];
347 [self updateInterface
];
352 - (IBAction
)ibRunPropertiesSheet
:(id)sender
355 [ibWindowTitleField setStringValue
:[_cheatData windowTitle
]];
356 [ibCheatInfoField setStringValue
:[_cheatData cheatInfo
]];
359 [NSApp beginSheet
:ibPropertiesSheet modalForWindow
:ibWindow modalDelegate
:nil didEndSelector
:NULL contextInfo
:nil];
362 - (IBAction
)ibEndPropertiesSheet
:(id)sender
364 [ibPropertiesSheet orderOut
:sender
];
365 [NSApp endSheet
:ibPropertiesSheet returnCode
:0];
367 if ( [sender tag
] == 1 ) {
368 // do not update anything if nothing has changed
369 if ( [[ibWindowTitleField stringValue
] isEqualToString
:[_cheatData windowTitle
]] &&
370 [[ibCheatInfoField stringValue
] isEqualToString
:[_cheatData cheatInfo
]] ) {
374 [_cheatData setWindowTitle
:[ibWindowTitleField stringValue
]];
375 [_cheatData setCheatInfo
:[ibCheatInfoField stringValue
]];
377 [self setDocumentChanged
];
378 [self updateInterface
];
383 - (IBAction
)ibRunPasswordSheet
:(id)sender
388 - (IBAction
)ibEndPasswordSheet
:(id)sender
394 - (IBAction
)ibRunCustomServerSheet
:(id)sender
397 [ibServerField setStringValue
:@
""];
398 [ibPortField setStringValue
:[NSString stringWithFormat
:@
"%i", TCDefaultListenPort
]];
401 [NSApp beginSheet
:ibCustomServerSheet modalForWindow
:ibWindow modalDelegate
:nil didEndSelector
:NULL contextInfo
:nil];
404 - (IBAction
)ibEndCustomServerSheet
:(id)sender
406 NSString
*server
= [ibServerField stringValue
];
407 int port
= [[ibPortField stringValue
] intValue
];
409 ChazLog( @
"ibEndCustomServerSheet: %@:%i", server
, port
);
411 [ibCustomServerSheet orderOut
:sender
];
412 [NSApp endSheet
:ibCustomServerSheet returnCode
:0];
414 if ( [sender tag
] == 1 ) {
415 [self connectWithURL
:[NSString stringWithFormat
:@
"cheat://%@:%i", server
, port
]];
420 - (IBAction
)ibRunEditVariablesSheet
:(id)sender
422 int row
= [ibCheatVariableTable selectedRow
];
425 // must have selected items
430 var
= [_cheatData variableAtIndex
:row
];
433 [ibNewValueField setStringValue
:[var stringValue
]];
436 [NSApp beginSheet
:ibEditVariablesSheet modalForWindow
:ibWindow modalDelegate
:nil didEndSelector
:NULL contextInfo
:nil];
439 - (IBAction
)ibEndEditVariablesSheet
:(id)sender
441 NSString
*newValue
= [ibNewValueField stringValue
];
445 [ibEditVariablesSheet orderOut
:sender
];
446 [NSApp endSheet
:ibEditVariablesSheet returnCode
:0];
448 if ( [sender tag
] == 0 ) {
451 if ( [newValue isEqualToString
:@
""] ) {
455 rows
= [ibCheatVariableTable selectedRows
];
458 // change all selected variables with the new value
460 for ( i
= 0; i
< top
; i
++ ) {
461 Variable
*var
= [_cheatData variableAtIndex
:[[rows objectAtIndex
:i
] unsignedIntValue
]];
462 [var setStringValue
:newValue
];
466 [ibCheatVariableTable reloadData
];
468 [self setDocumentChanged
];
469 [self updateInterface
];
473 - (IBAction
)ibPauseTarget
:(id)sender
475 [_cheater pauseTarget
];
478 - (IBAction
)ibResumeTarget
:(id)sender
480 [_cheater resumeTarget
];
484 - (IBAction
)ibCancelSearch
:(id)sender
486 _isCancelingTask
= YES
;
487 [_cheater cancelSearch
];
489 [self updateInterface
];
492 - (IBAction
)ibStopCheat
:(id)sender
494 _isCancelingTask
= YES
;
495 [_cheater stopChangingVariables
];
497 [self updateInterface
];
501 - (IBAction
)ibDumpMemory
:(id)sender
503 _status
= TCDumpingStatus
;
504 [_cheater getMemoryDump
];
507 [ibStatusText setDefaultStatus
:[NSString stringWithFormat
:@
"Dumping %@'s memory%C", [_process name
], 0x2026]];
508 [ibStatusBar setIndeterminate
:YES
];
509 [ibStatusBar startAnimation
:self];
511 [self updateInterface
];
514 - (IBAction
)ibCancelDump
:(id)sender
516 _isCancelingTask
= YES
;
517 [_cheater cancelMemoryDump
];
519 [self updateInterface
];
523 - (IBAction
)ibAddCheatVariable
:(id)sender
525 ChazLog( @
"ibAddCheatVariable:" );
526 Variable
*var
= [[Variable alloc
] initWithType
:[sender tag
]];
527 // add the new variable to the doc data
528 [_cheatData addVariable
:var
];
530 // update the variable table
531 [ibCheatVariableTable reloadData
];
533 if ( [[NSUserDefaults standardUserDefaults
] boolForKey
:TCSwitchVariablesPref
] ) {
534 [self switchToCheatMode
];
536 int row
= [_cheatData variableCount
]-1;
537 [ibCheatVariableTable selectRowIndexes
:[NSIndexSet indexSetWithIndex
:row
] byExtendingSelection
:NO
];
538 // start editing new variable
539 if ( [[NSUserDefaults standardUserDefaults
] boolForKey
:TCAutoStartEditingVarsPref
] ) {
540 [ibCheatVariableTable editColumn
:[ibCheatVariableTable columnWithIdentifier
:@
"address"] row
:row withEvent
:nil select
:YES
];
545 [self setDocumentChanged
];
546 [self updateInterface
];
549 - (IBAction
)ibSetVariableEnabled
:(id)sender
556 ChazLog( @
"ibSetVariableEnabled: %i", [sender selectedRow
] );
558 flag
= [[_cheatData variableAtIndex
:[ibCheatVariableTable selectedRow
]] isEnabled
];
560 rows
= [ibCheatVariableTable selectedRows
];
563 for ( i
= 0; i
< top
; i
++ ) {
564 Variable
*var
= [_cheatData variableAtIndex
:[[rows objectAtIndex
:i
] unsignedIntValue
]];
565 [var setEnabled
:!flag
];
569 [ibCheatVariableTable reloadData
];
570 [self setDocumentChanged
];
571 [self updateInterface
];
575 - (IBAction
)ibToggleSearchCheat
:(id)sender
577 if ( _mode
== TCCheatMode
) {
578 [self switchToSearchMode
];
580 else if ( _mode
== TCSearchMode
) {
581 [self switchToCheatMode
];
586 - (IBAction
)ibUndo
:(id)sender
591 - (IBAction
)ibRedo
:(id)sender