2 // **********************************************************************
3 // The Cheat - A universal game cheater for Mac OS X
4 // (C) 2003-2005 Chaz McGarvey (BrokenZipper)
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 1, or (at your option)
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #import "CheatDocument.h"
25 @interface CheatDocument (DocumentActionsPrivateAPI
)
27 - (void)_confirmTargetChange
:(NSWindow
*)sheet returnCode
:(int)returnCode context
:(void *)contextInfo
;
32 @implementation CheatDocument ( DocumentActions
)
35 - (IBAction
)ibSetLocalCheater
:(id)sender
37 ChazLog( @
"Selected %@", sender
);
39 // if this is the current server, don't reconnect
40 if ( ![self shouldConnectWithServer
:sender
] ) {
44 // disconnect and prepare to reconnect
45 [self disconnectFromCheater
];
46 [self connectWithServer
:sender
];
48 // create new local cheater
49 _cheater
= [[LocalCheater alloc
] initWithDelegate
:self];
50 [(LocalCheater
*)_cheater setShouldCopy
:YES
];
52 // send initial messages
54 [_cheater getProcessList
];
56 // send preferences to the cheater
57 [_cheater limitReturnedResults
:[[NSUserDefaults standardUserDefaults
] integerForKey
:TCHitsDisplayedPref
]];
59 [ibStatusText setDefaultStatus
:[self defaultStatusString
]];
62 - (IBAction
)ibSetRemoteCheater
:(id)sender
64 ChazLog( @
"Selected %@", sender
);
66 if ( ![self shouldConnectWithServer
:sender
] ) {
70 ChazLog( @
"resolving rendezvous service..." );
72 _resolvingService
= [[sender representedObject
] retain
];
73 [_resolvingService setDelegate
:self];
74 [_resolvingService resolve
];
77 - (void)netServiceDidResolveAddress
:(NSNetService
*)sender
81 ChazLog( @
"service resolved!" );
86 if ( sender
!= _resolvingService
) {
90 [self disconnectFromCheater
];
91 [self connectWithServer
:(NSMenuItem
*)[ibServerPopup itemAtIndex
:[ibServerPopup indexOfItemWithRepresentedObject
:_resolvingService
]]];
93 addresses
= [_resolvingService addresses
];
95 _resolvingService
= nil;
97 // create new remote cheater
98 ChazLog( @
"found %i addresses", [addresses count
] );
99 _cheater
= [[RemoteCheater alloc
] initWithDelegate
:self];
100 [(RemoteCheater
*)_cheater connectToHostWithData
:[addresses objectAtIndex
:0]];
102 // send initial messages
104 [_cheater getProcessList
];
106 // send preferences to the cheater
107 [_cheater limitReturnedResults
:[[NSUserDefaults standardUserDefaults
] integerForKey
:TCHitsDisplayedPref
]];
109 [ibStatusText setDefaultStatus
:[self defaultStatusString
]];
110 [self updateInterface
];
113 - (void)netService
:(NSNetService
*)sender didNotResolve
:(NSDictionary
*)errorDict
117 if ( sender
!= _resolvingService
) {
121 _resolvingService
= nil;
123 NSBeginInformationalAlertSheet( @
"The Cheat can't find the server.", @
"OK", nil, nil, ibWindow
, self, NULL
, NULL
, NULL
,
124 @
"The Cheat can't connect to the server \"%@\" because it can't be found.", [sender name
] );
127 - (void)netServiceDidStop
:(NSNetService
*)sender
132 - (IBAction
)ibSetCustomCheater
:(id)sender
134 RemoteCheater
*cheater
;
135 ChazLog( @
"Selected %@", [sender description
] );
137 if ( ![self shouldConnectWithServer
:sender
] ) {
141 cheater
= [[RemoteCheater alloc
] initWithDelegate
:self];
142 if ( ![(RemoteCheater
*)cheater connectToHostWithData
:[sender representedObject
]] ) {
143 NSBeginInformationalAlertSheet( @
"The Cheat can't find the server.", @
"OK", nil, nil, ibWindow
, self, NULL
, NULL
, NULL
,
144 @
"The Cheat can't connect to \"%@\" because there is no server at that address.", [sender title
] );
146 [self selectConnectedCheater
];
150 [self disconnectFromCheater
];
151 [self connectWithServer
:sender
];
155 // send initial messages
157 [_cheater getProcessList
];
159 // send preferences to the cheater
160 [_cheater limitReturnedResults
:[[NSUserDefaults standardUserDefaults
] integerForKey
:TCHitsDisplayedPref
]];
162 [ibStatusText setDefaultStatus
:[self defaultStatusString
]];
163 [self updateInterface
];
166 - (IBAction
)ibSetNoCheater
:(id)sender
168 [self disconnectFromCheater
];
171 [_serverObject release
];
174 [ibStatusText setDefaultStatus
:[self defaultStatusString
]];
175 [self updateInterface
];
178 - (IBAction
)ibSetProcess
:(id)sender
180 if ( [_process isEqual
:(Process
*)[sender representedObject
]] ) {
181 // this process is already selected, do nothing
185 if ( [_searchData hasSearchedOnce
] ) {
186 NSBeginInformationalAlertSheet( @
"Confirm target change.", @
"OK", @
"Cancel", nil, ibWindow
, self, NULL
,
187 @selector(_confirmTargetChange
:returnCode
:context
:), [[sender representedObject
] retain
],
188 @
"If you change the target now, your search will be cleared. This cannot be undone. Continue?" );
191 // request the change
192 [_cheater setTarget
:(Process
*)[sender representedObject
]];
196 - (void)_confirmTargetChange
:(NSWindow
*)sheet returnCode
:(int)returnCode context
:(void *)contextInfo
198 NSMenu
*processMenu
= [ibProcessPopup menu
];
199 Process
*process
= (Process
*)contextInfo
;
201 if ( returnCode
== NSAlertDefaultReturn
) {
203 [self ibClearSearch
:nil];
204 // request the change
205 [_cheater setTarget
:process
];
208 // select the correct server menuitem
209 [ibProcessPopup selectItemAtIndex
:[processMenu indexOfItemWithRepresentedObject
:_process
]];
216 - (IBAction
)ibSetVariableType
:(id)sender
218 [_searchData setVariableType
:[sender tag
]];
219 [self updateInterface
];
222 - (IBAction
)ibSetIntegerSign
:(id)sender
224 [_searchData setIntegerSign
:[[sender selectedCell
] tag
]];
227 - (IBAction
)ibSetOperator
:(id)sender
229 [_searchData setSearchOperator
:[sender tag
]];
232 - (IBAction
)ibSetValueUsed
:(id)sender
234 [_searchData setValueUsed
:[[sender selectedCell
] tag
]];
235 [self updateInterface
];
238 - (IBAction
)ibClearSearch
:(id)sender
240 [_cheater clearSearch
];
243 - (IBAction
)ibSearch
:(id)sender
248 if ( [_searchData valueUsed
] == TCGivenValue
) {
249 variable
= [[Variable alloc
] initWithType
:[_searchData variableType
] integerSign
:[_searchData integerSign
]];
250 [variable setStringValue
:[ibSearchValueField stringValue
]];
251 if ( [variable isValueValid
] && [variable valueSize
] > 0 ) {
252 _status
= TCSearchingStatus
;
253 [ibStatusText setDefaultStatus
:[NSString stringWithFormat
:@
"Searching %@'s memory%C", [_process name
], 0x2026]];
254 [ibStatusBar setIndeterminate
:NO
];
256 [_searchData setSearchValue
:variable
];
258 [_cheater searchForVariable
:[_searchData searchValue
] comparison
:[_searchData searchOperator
]];
261 NSBeginAlertSheet( @
"Invalid Input", @
"OK", nil, nil, ibWindow
, nil, NULL
, NULL
, NULL
,
262 @
"The search value \"%@\" cannot be used with this type of search.", [ibSearchValueField stringValue
] );
266 _status
= TCSearchingStatus
;
267 [ibStatusText setDefaultStatus
:[NSString stringWithFormat
:@
"Searching %@'s memory%C", [_process name
], 0x2026]];
268 [ibStatusBar setIndeterminate
:NO
];
270 [_cheater searchLastValuesComparison
:[_searchData searchOperator
]];
273 [self updateInterface
];
276 - (IBAction
)ibAddSearchVariable
:(id)sender
281 // don't do anything if there is nothing selected
282 if ( [ibSearchVariableTable selectedRow
] == -1 ) {
286 rows
= [ibSearchVariableTable selectedRows
];
288 for ( i
= 0; i
< top
; i
++ ) {
289 int rowIndex
= [[rows objectAtIndex
:i
] unsignedIntValue
];
290 // transfer the search variable to the cheat data
291 [_cheatData addVariable
:[_searchData variableAtIndex
:rowIndex
]];
294 // update the variable table
295 [ibCheatVariableTable reloadData
];
297 if ( [[NSUserDefaults standardUserDefaults
] boolForKey
:TCSwitchVariablesPref
] ) {
298 [self switchToCheatMode
];
300 int rowIndex
= [_cheatData variableCount
]-1;
301 if ( MacOSXVersion() >= 0x1030 ) {
302 [ibCheatVariableTable selectRowIndexes
:[NSIndexSet indexSetWithIndex
:rowIndex
] byExtendingSelection
:NO
];
305 [ibCheatVariableTable selectRow
:rowIndex byExtendingSelection
:NO
];
307 // start editing the last added variable
308 if ( [[NSUserDefaults standardUserDefaults
] boolForKey
:TCAutoStartEditingVarsPref
] ) {
311 if ( MacOSXVersion() >= 0x1030 ) {
312 [ibCheatVariableTable selectRowIndexes
:[NSIndexSet indexSetWithIndexesInRange
:NSMakeRange(rowIndex
-top
+1,top
-1)]
313 byExtendingSelection
:YES
];
316 for ( i
= 1; i
< top
; i
++ ) {
317 [ibCheatVariableTable selectRow
:rowIndex
-i byExtendingSelection
:YES
];
320 [ibCheatVariableTable scrollRowToVisible
:rowIndex
];
321 [self ibRunEditVariablesSheet
:nil];
325 [ibCheatVariableTable editColumn
:[ibCheatVariableTable columnWithIdentifier
:@
"value"]
326 row
:rowIndex withEvent
:nil select
:YES
];
332 [self setDocumentChanged
];
333 [self updateInterface
];
337 - (IBAction
)ibSetCheatRepeats
:(id)sender
339 [_cheatData setRepeats
:[sender state
]];
342 [self setDocumentChanged
];
343 [self updateInterface
];
346 - (IBAction
)ibSetRepeatInterval
:(id)sender
348 [_cheatData setRepeatInterval
:[sender doubleValue
]];
351 [self setDocumentChanged
];
352 [self updateInterface
];
355 - (IBAction
)ibCheat
:(id)sender
357 _status
= TCCheatingStatus
;
358 [_cheater makeVariableChanges
:[_cheatData enabledVariables
] repeat
:[_cheatData repeats
] interval
:[_cheatData repeatInterval
]];
360 // update status description
361 if ( [_cheatData repeats
] ) {
362 [ibStatusText setDefaultStatus
:[NSString stringWithFormat
:@
"Applying cheats to %@%C", [_process name
], 0x2026]];
363 [ibStatusBar setIndeterminate
:YES
];
364 [ibStatusBar startAnimation
:self];
366 [self updateInterface
];
371 - (IBAction
)ibRunPropertiesSheet
:(id)sender
374 [ibWindowTitleField setStringValue
:[_cheatData windowTitle
]];
375 [ibCheatInfoField setStringValue
:[_cheatData cheatInfo
]];
378 [NSApp beginSheet
:ibPropertiesSheet modalForWindow
:ibWindow modalDelegate
:nil didEndSelector
:NULL contextInfo
:nil];
381 - (IBAction
)ibEndPropertiesSheet
:(id)sender
383 [ibPropertiesSheet orderOut
:sender
];
384 [NSApp endSheet
:ibPropertiesSheet returnCode
:0];
386 if ( [sender tag
] == 1 ) {
387 // do not update anything if nothing has changed
388 if ( [[ibWindowTitleField stringValue
] isEqualToString
:[_cheatData windowTitle
]] &&
389 [[ibCheatInfoField stringValue
] isEqualToString
:[_cheatData cheatInfo
]] ) {
393 [_cheatData setWindowTitle
:[ibWindowTitleField stringValue
]];
394 [_cheatData setCheatInfo
:[ibCheatInfoField stringValue
]];
396 [self setDocumentChanged
];
397 [self updateInterface
];
402 - (IBAction
)ibRunPasswordSheet
:(id)sender
407 - (IBAction
)ibEndPasswordSheet
:(id)sender
413 - (IBAction
)ibRunCustomServerSheet
:(id)sender
416 [ibServerField setStringValue
:@
""];
417 [ibPortField setStringValue
:[NSString stringWithFormat
:@
"%i", TCDefaultListenPort
]];
420 [NSApp beginSheet
:ibCustomServerSheet modalForWindow
:ibWindow modalDelegate
:nil didEndSelector
:NULL contextInfo
:nil];
423 - (IBAction
)ibEndCustomServerSheet
:(id)sender
425 NSString
*server
= [ibServerField stringValue
];
426 int port
= [[ibPortField stringValue
] intValue
];
428 ChazLog( @
"ibEndCustomServerSheet: %@:%i", server
, port
);
430 [ibCustomServerSheet orderOut
:sender
];
431 [NSApp endSheet
:ibCustomServerSheet returnCode
:0];
433 if ( [sender tag
] == 1 ) {
434 [self connectWithURL
:[NSString stringWithFormat
:@
"cheat://%@:%i", server
, port
]];
439 - (IBAction
)ibRunEditVariablesSheet
:(id)sender
441 int row
= [ibCheatVariableTable selectedRow
];
444 // must have selected items
449 var
= [_cheatData variableAtIndex
:row
];
452 [ibNewValueField setStringValue
:[var stringValue
]];
455 [NSApp beginSheet
:ibEditVariablesSheet modalForWindow
:ibWindow modalDelegate
:nil didEndSelector
:NULL contextInfo
:nil];
458 - (IBAction
)ibEndEditVariablesSheet
:(id)sender
460 NSString
*newValue
= [ibNewValueField stringValue
];
464 [ibEditVariablesSheet orderOut
:sender
];
465 [NSApp endSheet
:ibEditVariablesSheet returnCode
:0];
467 if ( [sender tag
] == 0 ) {
470 if ( [newValue isEqualToString
:@
""] ) {
474 rows
= [ibCheatVariableTable selectedRows
];
477 // change all selected variables with the new value
479 for ( i
= 0; i
< top
; i
++ ) {
480 Variable
*var
= [_cheatData variableAtIndex
:[[rows objectAtIndex
:i
] unsignedIntValue
]];
481 [var setStringValue
:newValue
];
485 [ibCheatVariableTable reloadData
];
487 [self setDocumentChanged
];
488 [self updateInterface
];
492 - (IBAction
)ibPauseTarget
:(id)sender
494 [_cheater pauseTarget
];
497 - (IBAction
)ibResumeTarget
:(id)sender
499 [_cheater resumeTarget
];
503 - (IBAction
)ibCancelSearch
:(id)sender
505 _isCancelingTask
= YES
;
506 [_cheater cancelSearch
];
508 [self updateInterface
];
511 - (IBAction
)ibStopCheat
:(id)sender
513 _isCancelingTask
= YES
;
514 [_cheater stopChangingVariables
];
516 [self updateInterface
];
520 - (IBAction
)ibDumpMemory
:(id)sender
522 _status
= TCDumpingStatus
;
523 [_cheater getMemoryDump
];
526 [ibStatusText setDefaultStatus
:[NSString stringWithFormat
:@
"Dumping %@'s memory%C", [_process name
], 0x2026]];
527 [ibStatusBar setIndeterminate
:YES
];
528 [ibStatusBar startAnimation
:self];
530 [self updateInterface
];
533 - (IBAction
)ibCancelDump
:(id)sender
535 _isCancelingTask
= YES
;
536 [_cheater cancelMemoryDump
];
538 [self updateInterface
];
542 - (IBAction
)ibAddCheatVariable
:(id)sender
544 ChazLog( @
"ibAddCheatVariable:" );
546 Variable
*var
= [[Variable alloc
] initWithType
:[sender tag
]];
547 // add the new variable to the doc data
548 [_cheatData addVariable
:var
];
550 // update the variable table
551 [ibCheatVariableTable reloadData
];
553 if ( [[NSUserDefaults standardUserDefaults
] boolForKey
:TCSwitchVariablesPref
] ) {
554 [self switchToCheatMode
];
556 int row
= [_cheatData variableCount
]-1;
557 if ( MacOSXVersion() >= 0x1030 ) {
558 [ibCheatVariableTable selectRowIndexes
:[NSIndexSet indexSetWithIndex
:row
] byExtendingSelection
:NO
];
561 [ibCheatVariableTable selectRow
:row byExtendingSelection
:NO
];
563 // start editing new variable
564 if ( [[NSUserDefaults standardUserDefaults
] boolForKey
:TCAutoStartEditingVarsPref
] ) {
565 [ibCheatVariableTable editColumn
:[ibCheatVariableTable columnWithIdentifier
:@
"address"] row
:row withEvent
:nil select
:YES
];
570 [self setDocumentChanged
];
571 [self updateInterface
];
574 - (IBAction
)ibSetVariableEnabled
:(id)sender
581 ChazLog( @
"ibSetVariableEnabled: %i", [sender selectedRow
] );
583 flag
= [[_cheatData variableAtIndex
:[ibCheatVariableTable selectedRow
]] isEnabled
];
585 rows
= [ibCheatVariableTable selectedRows
];
588 for ( i
= 0; i
< top
; i
++ ) {
589 Variable
*var
= [_cheatData variableAtIndex
:[[rows objectAtIndex
:i
] unsignedIntValue
]];
590 [var setEnabled
:!flag
];
594 [ibCheatVariableTable reloadData
];
595 [self setDocumentChanged
];
596 [self updateInterface
];
600 - (IBAction
)ibToggleSearchCheat
:(id)sender
602 if ( _mode
== TCCheatMode
) {
603 [self switchToSearchMode
];
605 else if ( _mode
== TCSearchMode
) {
606 [self switchToCheatMode
];
611 - (IBAction
)ibUndo
:(id)sender
616 - (IBAction
)ibRedo
:(id)sender